diff --git a/.jshintrc b/.jshintrc
index 813ab4c..77ec1af 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -15,5 +15,5 @@
     "globals": {
       "jQuery": true
     },
-    "predef": ["-Promise", "define"]
+    "predef": ["Promise", "define"]
 }
diff --git a/dist/html2canvas.js b/dist/html2canvas.js
deleted file mode 100644
index 1b8a9d5..0000000
--- a/dist/html2canvas.js
+++ /dev/null
@@ -1,4531 +0,0 @@
-/*
-  html2canvas 0.5.0-alpha2 <http://html2canvas.hertzen.com>
-  Copyright (c) 2015 Niklas von Hertzen
-
-  Released under MIT License
-*/
-
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.html2canvas=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-(function (process,global){
-/*!
- * @overview es6-promise - a tiny implementation of Promises/A+.
- * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
- * @license   Licensed under MIT license
- *            See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
- * @version   2.0.1
- */
-
-(function() {
-    "use strict";
-
-    function $$utils$$objectOrFunction(x) {
-      return typeof x === 'function' || (typeof x === 'object' && x !== null);
-    }
-
-    function $$utils$$isFunction(x) {
-      return typeof x === 'function';
-    }
-
-    function $$utils$$isMaybeThenable(x) {
-      return typeof x === 'object' && x !== null;
-    }
-
-    var $$utils$$_isArray;
-
-    if (!Array.isArray) {
-      $$utils$$_isArray = function (x) {
-        return Object.prototype.toString.call(x) === '[object Array]';
-      };
-    } else {
-      $$utils$$_isArray = Array.isArray;
-    }
-
-    var $$utils$$isArray = $$utils$$_isArray;
-    var $$utils$$now = Date.now || function() { return new Date().getTime(); };
-    function $$utils$$F() { }
-
-    var $$utils$$o_create = (Object.create || function (o) {
-      if (arguments.length > 1) {
-        throw new Error('Second argument not supported');
-      }
-      if (typeof o !== 'object') {
-        throw new TypeError('Argument must be an object');
-      }
-      $$utils$$F.prototype = o;
-      return new $$utils$$F();
-    });
-
-    var $$asap$$len = 0;
-
-    var $$asap$$default = function asap(callback, arg) {
-      $$asap$$queue[$$asap$$len] = callback;
-      $$asap$$queue[$$asap$$len + 1] = arg;
-      $$asap$$len += 2;
-      if ($$asap$$len === 2) {
-        // If len is 1, that means that we need to schedule an async flush.
-        // If additional callbacks are queued before the queue is flushed, they
-        // will be processed by this flush that we are scheduling.
-        $$asap$$scheduleFlush();
-      }
-    };
-
-    var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {};
-    var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver;
-
-    // test for web worker but not in IE10
-    var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
-      typeof importScripts !== 'undefined' &&
-      typeof MessageChannel !== 'undefined';
-
-    // node
-    function $$asap$$useNextTick() {
-      return function() {
-        process.nextTick($$asap$$flush);
-      };
-    }
-
-    function $$asap$$useMutationObserver() {
-      var iterations = 0;
-      var observer = new $$asap$$BrowserMutationObserver($$asap$$flush);
-      var node = document.createTextNode('');
-      observer.observe(node, { characterData: true });
-
-      return function() {
-        node.data = (iterations = ++iterations % 2);
-      };
-    }
-
-    // web worker
-    function $$asap$$useMessageChannel() {
-      var channel = new MessageChannel();
-      channel.port1.onmessage = $$asap$$flush;
-      return function () {
-        channel.port2.postMessage(0);
-      };
-    }
-
-    function $$asap$$useSetTimeout() {
-      return function() {
-        setTimeout($$asap$$flush, 1);
-      };
-    }
-
-    var $$asap$$queue = new Array(1000);
-
-    function $$asap$$flush() {
-      for (var i = 0; i < $$asap$$len; i+=2) {
-        var callback = $$asap$$queue[i];
-        var arg = $$asap$$queue[i+1];
-
-        callback(arg);
-
-        $$asap$$queue[i] = undefined;
-        $$asap$$queue[i+1] = undefined;
-      }
-
-      $$asap$$len = 0;
-    }
-
-    var $$asap$$scheduleFlush;
-
-    // Decide what async method to use to triggering processing of queued callbacks:
-    if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
-      $$asap$$scheduleFlush = $$asap$$useNextTick();
-    } else if ($$asap$$BrowserMutationObserver) {
-      $$asap$$scheduleFlush = $$asap$$useMutationObserver();
-    } else if ($$asap$$isWorker) {
-      $$asap$$scheduleFlush = $$asap$$useMessageChannel();
-    } else {
-      $$asap$$scheduleFlush = $$asap$$useSetTimeout();
-    }
-
-    function $$$internal$$noop() {}
-    var $$$internal$$PENDING   = void 0;
-    var $$$internal$$FULFILLED = 1;
-    var $$$internal$$REJECTED  = 2;
-    var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject();
-
-    function $$$internal$$selfFullfillment() {
-      return new TypeError("You cannot resolve a promise with itself");
-    }
-
-    function $$$internal$$cannotReturnOwn() {
-      return new TypeError('A promises callback cannot return that same promise.')
-    }
-
-    function $$$internal$$getThen(promise) {
-      try {
-        return promise.then;
-      } catch(error) {
-        $$$internal$$GET_THEN_ERROR.error = error;
-        return $$$internal$$GET_THEN_ERROR;
-      }
-    }
-
-    function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
-      try {
-        then.call(value, fulfillmentHandler, rejectionHandler);
-      } catch(e) {
-        return e;
-      }
-    }
-
-    function $$$internal$$handleForeignThenable(promise, thenable, then) {
-       $$asap$$default(function(promise) {
-        var sealed = false;
-        var error = $$$internal$$tryThen(then, thenable, function(value) {
-          if (sealed) { return; }
-          sealed = true;
-          if (thenable !== value) {
-            $$$internal$$resolve(promise, value);
-          } else {
-            $$$internal$$fulfill(promise, value);
-          }
-        }, function(reason) {
-          if (sealed) { return; }
-          sealed = true;
-
-          $$$internal$$reject(promise, reason);
-        }, 'Settle: ' + (promise._label || ' unknown promise'));
-
-        if (!sealed && error) {
-          sealed = true;
-          $$$internal$$reject(promise, error);
-        }
-      }, promise);
-    }
-
-    function $$$internal$$handleOwnThenable(promise, thenable) {
-      if (thenable._state === $$$internal$$FULFILLED) {
-        $$$internal$$fulfill(promise, thenable._result);
-      } else if (promise._state === $$$internal$$REJECTED) {
-        $$$internal$$reject(promise, thenable._result);
-      } else {
-        $$$internal$$subscribe(thenable, undefined, function(value) {
-          $$$internal$$resolve(promise, value);
-        }, function(reason) {
-          $$$internal$$reject(promise, reason);
-        });
-      }
-    }
-
-    function $$$internal$$handleMaybeThenable(promise, maybeThenable) {
-      if (maybeThenable.constructor === promise.constructor) {
-        $$$internal$$handleOwnThenable(promise, maybeThenable);
-      } else {
-        var then = $$$internal$$getThen(maybeThenable);
-
-        if (then === $$$internal$$GET_THEN_ERROR) {
-          $$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error);
-        } else if (then === undefined) {
-          $$$internal$$fulfill(promise, maybeThenable);
-        } else if ($$utils$$isFunction(then)) {
-          $$$internal$$handleForeignThenable(promise, maybeThenable, then);
-        } else {
-          $$$internal$$fulfill(promise, maybeThenable);
-        }
-      }
-    }
-
-    function $$$internal$$resolve(promise, value) {
-      if (promise === value) {
-        $$$internal$$reject(promise, $$$internal$$selfFullfillment());
-      } else if ($$utils$$objectOrFunction(value)) {
-        $$$internal$$handleMaybeThenable(promise, value);
-      } else {
-        $$$internal$$fulfill(promise, value);
-      }
-    }
-
-    function $$$internal$$publishRejection(promise) {
-      if (promise._onerror) {
-        promise._onerror(promise._result);
-      }
-
-      $$$internal$$publish(promise);
-    }
-
-    function $$$internal$$fulfill(promise, value) {
-      if (promise._state !== $$$internal$$PENDING) { return; }
-
-      promise._result = value;
-      promise._state = $$$internal$$FULFILLED;
-
-      if (promise._subscribers.length === 0) {
-      } else {
-        $$asap$$default($$$internal$$publish, promise);
-      }
-    }
-
-    function $$$internal$$reject(promise, reason) {
-      if (promise._state !== $$$internal$$PENDING) { return; }
-      promise._state = $$$internal$$REJECTED;
-      promise._result = reason;
-
-      $$asap$$default($$$internal$$publishRejection, promise);
-    }
-
-    function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
-      var subscribers = parent._subscribers;
-      var length = subscribers.length;
-
-      parent._onerror = null;
-
-      subscribers[length] = child;
-      subscribers[length + $$$internal$$FULFILLED] = onFulfillment;
-      subscribers[length + $$$internal$$REJECTED]  = onRejection;
-
-      if (length === 0 && parent._state) {
-        $$asap$$default($$$internal$$publish, parent);
-      }
-    }
-
-    function $$$internal$$publish(promise) {
-      var subscribers = promise._subscribers;
-      var settled = promise._state;
-
-      if (subscribers.length === 0) { return; }
-
-      var child, callback, detail = promise._result;
-
-      for (var i = 0; i < subscribers.length; i += 3) {
-        child = subscribers[i];
-        callback = subscribers[i + settled];
-
-        if (child) {
-          $$$internal$$invokeCallback(settled, child, callback, detail);
-        } else {
-          callback(detail);
-        }
-      }
-
-      promise._subscribers.length = 0;
-    }
-
-    function $$$internal$$ErrorObject() {
-      this.error = null;
-    }
-
-    var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject();
-
-    function $$$internal$$tryCatch(callback, detail) {
-      try {
-        return callback(detail);
-      } catch(e) {
-        $$$internal$$TRY_CATCH_ERROR.error = e;
-        return $$$internal$$TRY_CATCH_ERROR;
-      }
-    }
-
-    function $$$internal$$invokeCallback(settled, promise, callback, detail) {
-      var hasCallback = $$utils$$isFunction(callback),
-          value, error, succeeded, failed;
-
-      if (hasCallback) {
-        value = $$$internal$$tryCatch(callback, detail);
-
-        if (value === $$$internal$$TRY_CATCH_ERROR) {
-          failed = true;
-          error = value.error;
-          value = null;
-        } else {
-          succeeded = true;
-        }
-
-        if (promise === value) {
-          $$$internal$$reject(promise, $$$internal$$cannotReturnOwn());
-          return;
-        }
-
-      } else {
-        value = detail;
-        succeeded = true;
-      }
-
-      if (promise._state !== $$$internal$$PENDING) {
-        // noop
-      } else if (hasCallback && succeeded) {
-        $$$internal$$resolve(promise, value);
-      } else if (failed) {
-        $$$internal$$reject(promise, error);
-      } else if (settled === $$$internal$$FULFILLED) {
-        $$$internal$$fulfill(promise, value);
-      } else if (settled === $$$internal$$REJECTED) {
-        $$$internal$$reject(promise, value);
-      }
-    }
-
-    function $$$internal$$initializePromise(promise, resolver) {
-      try {
-        resolver(function resolvePromise(value){
-          $$$internal$$resolve(promise, value);
-        }, function rejectPromise(reason) {
-          $$$internal$$reject(promise, reason);
-        });
-      } catch(e) {
-        $$$internal$$reject(promise, e);
-      }
-    }
-
-    function $$$enumerator$$makeSettledResult(state, position, value) {
-      if (state === $$$internal$$FULFILLED) {
-        return {
-          state: 'fulfilled',
-          value: value
-        };
-      } else {
-        return {
-          state: 'rejected',
-          reason: value
-        };
-      }
-    }
-
-    function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) {
-      this._instanceConstructor = Constructor;
-      this.promise = new Constructor($$$internal$$noop, label);
-      this._abortOnReject = abortOnReject;
-
-      if (this._validateInput(input)) {
-        this._input     = input;
-        this.length     = input.length;
-        this._remaining = input.length;
-
-        this._init();
-
-        if (this.length === 0) {
-          $$$internal$$fulfill(this.promise, this._result);
-        } else {
-          this.length = this.length || 0;
-          this._enumerate();
-          if (this._remaining === 0) {
-            $$$internal$$fulfill(this.promise, this._result);
-          }
-        }
-      } else {
-        $$$internal$$reject(this.promise, this._validationError());
-      }
-    }
-
-    $$$enumerator$$Enumerator.prototype._validateInput = function(input) {
-      return $$utils$$isArray(input);
-    };
-
-    $$$enumerator$$Enumerator.prototype._validationError = function() {
-      return new Error('Array Methods must be provided an Array');
-    };
-
-    $$$enumerator$$Enumerator.prototype._init = function() {
-      this._result = new Array(this.length);
-    };
-
-    var $$$enumerator$$default = $$$enumerator$$Enumerator;
-
-    $$$enumerator$$Enumerator.prototype._enumerate = function() {
-      var length  = this.length;
-      var promise = this.promise;
-      var input   = this._input;
-
-      for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
-        this._eachEntry(input[i], i);
-      }
-    };
-
-    $$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
-      var c = this._instanceConstructor;
-      if ($$utils$$isMaybeThenable(entry)) {
-        if (entry.constructor === c && entry._state !== $$$internal$$PENDING) {
-          entry._onerror = null;
-          this._settledAt(entry._state, i, entry._result);
-        } else {
-          this._willSettleAt(c.resolve(entry), i);
-        }
-      } else {
-        this._remaining--;
-        this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry);
-      }
-    };
-
-    $$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
-      var promise = this.promise;
-
-      if (promise._state === $$$internal$$PENDING) {
-        this._remaining--;
-
-        if (this._abortOnReject && state === $$$internal$$REJECTED) {
-          $$$internal$$reject(promise, value);
-        } else {
-          this._result[i] = this._makeResult(state, i, value);
-        }
-      }
-
-      if (this._remaining === 0) {
-        $$$internal$$fulfill(promise, this._result);
-      }
-    };
-
-    $$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) {
-      return value;
-    };
-
-    $$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
-      var enumerator = this;
-
-      $$$internal$$subscribe(promise, undefined, function(value) {
-        enumerator._settledAt($$$internal$$FULFILLED, i, value);
-      }, function(reason) {
-        enumerator._settledAt($$$internal$$REJECTED, i, reason);
-      });
-    };
-
-    var $$promise$all$$default = function all(entries, label) {
-      return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise;
-    };
-
-    var $$promise$race$$default = function race(entries, label) {
-      /*jshint validthis:true */
-      var Constructor = this;
-
-      var promise = new Constructor($$$internal$$noop, label);
-
-      if (!$$utils$$isArray(entries)) {
-        $$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
-        return promise;
-      }
-
-      var length = entries.length;
-
-      function onFulfillment(value) {
-        $$$internal$$resolve(promise, value);
-      }
-
-      function onRejection(reason) {
-        $$$internal$$reject(promise, reason);
-      }
-
-      for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
-        $$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
-      }
-
-      return promise;
-    };
-
-    var $$promise$resolve$$default = function resolve(object, label) {
-      /*jshint validthis:true */
-      var Constructor = this;
-
-      if (object && typeof object === 'object' && object.constructor === Constructor) {
-        return object;
-      }
-
-      var promise = new Constructor($$$internal$$noop, label);
-      $$$internal$$resolve(promise, object);
-      return promise;
-    };
-
-    var $$promise$reject$$default = function reject(reason, label) {
-      /*jshint validthis:true */
-      var Constructor = this;
-      var promise = new Constructor($$$internal$$noop, label);
-      $$$internal$$reject(promise, reason);
-      return promise;
-    };
-
-    var $$es6$promise$promise$$counter = 0;
-
-    function $$es6$promise$promise$$needsResolver() {
-      throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
-    }
-
-    function $$es6$promise$promise$$needsNew() {
-      throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
-    }
-
-    var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise;
-
-    /**
-      Promise objects represent the eventual result of an asynchronous operation. The
-      primary way of interacting with a promise is through its `then` method, which
-      registers callbacks to receive either a promise’s eventual value or the reason
-      why the promise cannot be fulfilled.
-
-      Terminology
-      -----------
-
-      - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
-      - `thenable` is an object or function that defines a `then` method.
-      - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
-      - `exception` is a value that is thrown using the throw statement.
-      - `reason` is a value that indicates why a promise was rejected.
-      - `settled` the final resting state of a promise, fulfilled or rejected.
-
-      A promise can be in one of three states: pending, fulfilled, or rejected.
-
-      Promises that are fulfilled have a fulfillment value and are in the fulfilled
-      state.  Promises that are rejected have a rejection reason and are in the
-      rejected state.  A fulfillment value is never a thenable.
-
-      Promises can also be said to *resolve* a value.  If this value is also a
-      promise, then the original promise's settled state will match the value's
-      settled state.  So a promise that *resolves* a promise that rejects will
-      itself reject, and a promise that *resolves* a promise that fulfills will
-      itself fulfill.
-
-
-      Basic Usage:
-      ------------
-
-      ```js
-      var promise = new Promise(function(resolve, reject) {
-        // on success
-        resolve(value);
-
-        // on failure
-        reject(reason);
-      });
-
-      promise.then(function(value) {
-        // on fulfillment
-      }, function(reason) {
-        // on rejection
-      });
-      ```
-
-      Advanced Usage:
-      ---------------
-
-      Promises shine when abstracting away asynchronous interactions such as
-      `XMLHttpRequest`s.
-
-      ```js
-      function getJSON(url) {
-        return new Promise(function(resolve, reject){
-          var xhr = new XMLHttpRequest();
-
-          xhr.open('GET', url);
-          xhr.onreadystatechange = handler;
-          xhr.responseType = 'json';
-          xhr.setRequestHeader('Accept', 'application/json');
-          xhr.send();
-
-          function handler() {
-            if (this.readyState === this.DONE) {
-              if (this.status === 200) {
-                resolve(this.response);
-              } else {
-                reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
-              }
-            }
-          };
-        });
-      }
-
-      getJSON('/posts.json').then(function(json) {
-        // on fulfillment
-      }, function(reason) {
-        // on rejection
-      });
-      ```
-
-      Unlike callbacks, promises are great composable primitives.
-
-      ```js
-      Promise.all([
-        getJSON('/posts'),
-        getJSON('/comments')
-      ]).then(function(values){
-        values[0] // => postsJSON
-        values[1] // => commentsJSON
-
-        return values;
-      });
-      ```
-
-      @class Promise
-      @param {function} resolver
-      Useful for tooling.
-      @constructor
-    */
-    function $$es6$promise$promise$$Promise(resolver) {
-      this._id = $$es6$promise$promise$$counter++;
-      this._state = undefined;
-      this._result = undefined;
-      this._subscribers = [];
-
-      if ($$$internal$$noop !== resolver) {
-        if (!$$utils$$isFunction(resolver)) {
-          $$es6$promise$promise$$needsResolver();
-        }
-
-        if (!(this instanceof $$es6$promise$promise$$Promise)) {
-          $$es6$promise$promise$$needsNew();
-        }
-
-        $$$internal$$initializePromise(this, resolver);
-      }
-    }
-
-    $$es6$promise$promise$$Promise.all = $$promise$all$$default;
-    $$es6$promise$promise$$Promise.race = $$promise$race$$default;
-    $$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default;
-    $$es6$promise$promise$$Promise.reject = $$promise$reject$$default;
-
-    $$es6$promise$promise$$Promise.prototype = {
-      constructor: $$es6$promise$promise$$Promise,
-
-    /**
-      The primary way of interacting with a promise is through its `then` method,
-      which registers callbacks to receive either a promise's eventual value or the
-      reason why the promise cannot be fulfilled.
-
-      ```js
-      findUser().then(function(user){
-        // user is available
-      }, function(reason){
-        // user is unavailable, and you are given the reason why
-      });
-      ```
-
-      Chaining
-      --------
-
-      The return value of `then` is itself a promise.  This second, 'downstream'
-      promise is resolved with the return value of the first promise's fulfillment
-      or rejection handler, or rejected if the handler throws an exception.
-
-      ```js
-      findUser().then(function (user) {
-        return user.name;
-      }, function (reason) {
-        return 'default name';
-      }).then(function (userName) {
-        // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
-        // will be `'default name'`
-      });
-
-      findUser().then(function (user) {
-        throw new Error('Found user, but still unhappy');
-      }, function (reason) {
-        throw new Error('`findUser` rejected and we're unhappy');
-      }).then(function (value) {
-        // never reached
-      }, function (reason) {
-        // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
-        // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
-      });
-      ```
-      If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
-
-      ```js
-      findUser().then(function (user) {
-        throw new PedagogicalException('Upstream error');
-      }).then(function (value) {
-        // never reached
-      }).then(function (value) {
-        // never reached
-      }, function (reason) {
-        // The `PedgagocialException` is propagated all the way down to here
-      });
-      ```
-
-      Assimilation
-      ------------
-
-      Sometimes the value you want to propagate to a downstream promise can only be
-      retrieved asynchronously. This can be achieved by returning a promise in the
-      fulfillment or rejection handler. The downstream promise will then be pending
-      until the returned promise is settled. This is called *assimilation*.
-
-      ```js
-      findUser().then(function (user) {
-        return findCommentsByAuthor(user);
-      }).then(function (comments) {
-        // The user's comments are now available
-      });
-      ```
-
-      If the assimliated promise rejects, then the downstream promise will also reject.
-
-      ```js
-      findUser().then(function (user) {
-        return findCommentsByAuthor(user);
-      }).then(function (comments) {
-        // If `findCommentsByAuthor` fulfills, we'll have the value here
-      }, function (reason) {
-        // If `findCommentsByAuthor` rejects, we'll have the reason here
-      });
-      ```
-
-      Simple Example
-      --------------
-
-      Synchronous Example
-
-      ```javascript
-      var result;
-
-      try {
-        result = findResult();
-        // success
-      } catch(reason) {
-        // failure
-      }
-      ```
-
-      Errback Example
-
-      ```js
-      findResult(function(result, err){
-        if (err) {
-          // failure
-        } else {
-          // success
-        }
-      });
-      ```
-
-      Promise Example;
-
-      ```javascript
-      findResult().then(function(result){
-        // success
-      }, function(reason){
-        // failure
-      });
-      ```
-
-      Advanced Example
-      --------------
-
-      Synchronous Example
-
-      ```javascript
-      var author, books;
-
-      try {
-        author = findAuthor();
-        books  = findBooksByAuthor(author);
-        // success
-      } catch(reason) {
-        // failure
-      }
-      ```
-
-      Errback Example
-
-      ```js
-
-      function foundBooks(books) {
-
-      }
-
-      function failure(reason) {
-
-      }
-
-      findAuthor(function(author, err){
-        if (err) {
-          failure(err);
-          // failure
-        } else {
-          try {
-            findBoooksByAuthor(author, function(books, err) {
-              if (err) {
-                failure(err);
-              } else {
-                try {
-                  foundBooks(books);
-                } catch(reason) {
-                  failure(reason);
-                }
-              }
-            });
-          } catch(error) {
-            failure(err);
-          }
-          // success
-        }
-      });
-      ```
-
-      Promise Example;
-
-      ```javascript
-      findAuthor().
-        then(findBooksByAuthor).
-        then(function(books){
-          // found books
-      }).catch(function(reason){
-        // something went wrong
-      });
-      ```
-
-      @method then
-      @param {Function} onFulfilled
-      @param {Function} onRejected
-      Useful for tooling.
-      @return {Promise}
-    */
-      then: function(onFulfillment, onRejection) {
-        var parent = this;
-        var state = parent._state;
-
-        if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) {
-          return this;
-        }
-
-        var child = new this.constructor($$$internal$$noop);
-        var result = parent._result;
-
-        if (state) {
-          var callback = arguments[state - 1];
-          $$asap$$default(function(){
-            $$$internal$$invokeCallback(state, child, callback, result);
-          });
-        } else {
-          $$$internal$$subscribe(parent, child, onFulfillment, onRejection);
-        }
-
-        return child;
-      },
-
-    /**
-      `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
-      as the catch block of a try/catch statement.
-
-      ```js
-      function findAuthor(){
-        throw new Error('couldn't find that author');
-      }
-
-      // synchronous
-      try {
-        findAuthor();
-      } catch(reason) {
-        // something went wrong
-      }
-
-      // async with promises
-      findAuthor().catch(function(reason){
-        // something went wrong
-      });
-      ```
-
-      @method catch
-      @param {Function} onRejection
-      Useful for tooling.
-      @return {Promise}
-    */
-      'catch': function(onRejection) {
-        return this.then(null, onRejection);
-      }
-    };
-
-    var $$es6$promise$polyfill$$default = function polyfill() {
-      var local;
-
-      if (typeof global !== 'undefined') {
-        local = global;
-      } else if (typeof window !== 'undefined' && window.document) {
-        local = window;
-      } else {
-        local = self;
-      }
-
-      var es6PromiseSupport =
-        "Promise" in local &&
-        // Some of these methods are missing from
-        // Firefox/Chrome experimental implementations
-        "resolve" in local.Promise &&
-        "reject" in local.Promise &&
-        "all" in local.Promise &&
-        "race" in local.Promise &&
-        // Older version of the spec had a resolver object
-        // as the arg rather than a function
-        (function() {
-          var resolve;
-          new local.Promise(function(r) { resolve = r; });
-          return $$utils$$isFunction(resolve);
-        }());
-
-      if (!es6PromiseSupport) {
-        local.Promise = $$es6$promise$promise$$default;
-      }
-    };
-
-    var es6$promise$umd$$ES6Promise = {
-      'Promise': $$es6$promise$promise$$default,
-      'polyfill': $$es6$promise$polyfill$$default
-    };
-
-    /* global define:true module:true window: true */
-    if (typeof define === 'function' && define['amd']) {
-      define(function() { return es6$promise$umd$$ES6Promise; });
-    } else if (typeof module !== 'undefined' && module['exports']) {
-      module['exports'] = es6$promise$umd$$ES6Promise;
-    } else if (typeof this !== 'undefined') {
-      this['ES6Promise'] = es6$promise$umd$$ES6Promise;
-    }
-}).call(this);
-}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{"_process":2}],2:[function(require,module,exports){
-// shim for using process in browser
-
-var process = module.exports = {};
-var queue = [];
-var draining = false;
-
-function drainQueue() {
-    if (draining) {
-        return;
-    }
-    draining = true;
-    var currentQueue;
-    var len = queue.length;
-    while(len) {
-        currentQueue = queue;
-        queue = [];
-        var i = -1;
-        while (++i < len) {
-            currentQueue[i]();
-        }
-        len = queue.length;
-    }
-    draining = false;
-}
-process.nextTick = function (fun) {
-    queue.push(fun);
-    if (!draining) {
-        setTimeout(drainQueue, 0);
-    }
-};
-
-process.title = 'browser';
-process.browser = true;
-process.env = {};
-process.argv = [];
-process.version = ''; // empty string to avoid regexp issues
-
-function noop() {}
-
-process.on = noop;
-process.addListener = noop;
-process.once = noop;
-process.off = noop;
-process.removeListener = noop;
-process.removeAllListeners = noop;
-process.emit = noop;
-
-process.binding = function (name) {
-    throw new Error('process.binding is not supported');
-};
-
-// TODO(shtylman)
-process.cwd = function () { return '/' };
-process.chdir = function (dir) {
-    throw new Error('process.chdir is not supported');
-};
-process.umask = function() { return 0; };
-
-},{}],3:[function(require,module,exports){
-(function (global){
-/*! http://mths.be/punycode v1.2.4 by @mathias */
-;(function(root) {
-
-	/** Detect free variables */
-	var freeExports = typeof exports == 'object' && exports;
-	var freeModule = typeof module == 'object' && module &&
-		module.exports == freeExports && module;
-	var freeGlobal = typeof global == 'object' && global;
-	if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
-		root = freeGlobal;
-	}
-
-	/**
-	 * The `punycode` object.
-	 * @name punycode
-	 * @type Object
-	 */
-	var punycode,
-
-	/** Highest positive signed 32-bit float value */
-	maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
-
-	/** Bootstring parameters */
-	base = 36,
-	tMin = 1,
-	tMax = 26,
-	skew = 38,
-	damp = 700,
-	initialBias = 72,
-	initialN = 128, // 0x80
-	delimiter = '-', // '\x2D'
-
-	/** Regular expressions */
-	regexPunycode = /^xn--/,
-	regexNonASCII = /[^ -~]/, // unprintable ASCII chars + non-ASCII chars
-	regexSeparators = /\x2E|\u3002|\uFF0E|\uFF61/g, // RFC 3490 separators
-
-	/** Error messages */
-	errors = {
-		'overflow': 'Overflow: input needs wider integers to process',
-		'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
-		'invalid-input': 'Invalid input'
-	},
-
-	/** Convenience shortcuts */
-	baseMinusTMin = base - tMin,
-	floor = Math.floor,
-	stringFromCharCode = String.fromCharCode,
-
-	/** Temporary variable */
-	key;
-
-	/*--------------------------------------------------------------------------*/
-
-	/**
-	 * A generic error utility function.
-	 * @private
-	 * @param {String} type The error type.
-	 * @returns {Error} Throws a `RangeError` with the applicable error message.
-	 */
-	function error(type) {
-		throw RangeError(errors[type]);
-	}
-
-	/**
-	 * A generic `Array#map` utility function.
-	 * @private
-	 * @param {Array} array The array to iterate over.
-	 * @param {Function} callback The function that gets called for every array
-	 * item.
-	 * @returns {Array} A new array of values returned by the callback function.
-	 */
-	function map(array, fn) {
-		var length = array.length;
-		while (length--) {
-			array[length] = fn(array[length]);
-		}
-		return array;
-	}
-
-	/**
-	 * A simple `Array#map`-like wrapper to work with domain name strings.
-	 * @private
-	 * @param {String} domain The domain name.
-	 * @param {Function} callback The function that gets called for every
-	 * character.
-	 * @returns {Array} A new string of characters returned by the callback
-	 * function.
-	 */
-	function mapDomain(string, fn) {
-		return map(string.split(regexSeparators), fn).join('.');
-	}
-
-	/**
-	 * Creates an array containing the numeric code points of each Unicode
-	 * character in the string. While JavaScript uses UCS-2 internally,
-	 * this function will convert a pair of surrogate halves (each of which
-	 * UCS-2 exposes as separate characters) into a single code point,
-	 * matching UTF-16.
-	 * @see `punycode.ucs2.encode`
-	 * @see <http://mathiasbynens.be/notes/javascript-encoding>
-	 * @memberOf punycode.ucs2
-	 * @name decode
-	 * @param {String} string The Unicode input string (UCS-2).
-	 * @returns {Array} The new array of code points.
-	 */
-	function ucs2decode(string) {
-		var output = [],
-		    counter = 0,
-		    length = string.length,
-		    value,
-		    extra;
-		while (counter < length) {
-			value = string.charCodeAt(counter++);
-			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
-				// high surrogate, and there is a next character
-				extra = string.charCodeAt(counter++);
-				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
-					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
-				} else {
-					// unmatched surrogate; only append this code unit, in case the next
-					// code unit is the high surrogate of a surrogate pair
-					output.push(value);
-					counter--;
-				}
-			} else {
-				output.push(value);
-			}
-		}
-		return output;
-	}
-
-	/**
-	 * Creates a string based on an array of numeric code points.
-	 * @see `punycode.ucs2.decode`
-	 * @memberOf punycode.ucs2
-	 * @name encode
-	 * @param {Array} codePoints The array of numeric code points.
-	 * @returns {String} The new Unicode string (UCS-2).
-	 */
-	function ucs2encode(array) {
-		return map(array, function(value) {
-			var output = '';
-			if (value > 0xFFFF) {
-				value -= 0x10000;
-				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
-				value = 0xDC00 | value & 0x3FF;
-			}
-			output += stringFromCharCode(value);
-			return output;
-		}).join('');
-	}
-
-	/**
-	 * Converts a basic code point into a digit/integer.
-	 * @see `digitToBasic()`
-	 * @private
-	 * @param {Number} codePoint The basic numeric code point value.
-	 * @returns {Number} The numeric value of a basic code point (for use in
-	 * representing integers) in the range `0` to `base - 1`, or `base` if
-	 * the code point does not represent a value.
-	 */
-	function basicToDigit(codePoint) {
-		if (codePoint - 48 < 10) {
-			return codePoint - 22;
-		}
-		if (codePoint - 65 < 26) {
-			return codePoint - 65;
-		}
-		if (codePoint - 97 < 26) {
-			return codePoint - 97;
-		}
-		return base;
-	}
-
-	/**
-	 * Converts a digit/integer into a basic code point.
-	 * @see `basicToDigit()`
-	 * @private
-	 * @param {Number} digit The numeric value of a basic code point.
-	 * @returns {Number} The basic code point whose value (when used for
-	 * representing integers) is `digit`, which needs to be in the range
-	 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
-	 * used; else, the lowercase form is used. The behavior is undefined
-	 * if `flag` is non-zero and `digit` has no uppercase form.
-	 */
-	function digitToBasic(digit, flag) {
-		//  0..25 map to ASCII a..z or A..Z
-		// 26..35 map to ASCII 0..9
-		return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
-	}
-
-	/**
-	 * Bias adaptation function as per section 3.4 of RFC 3492.
-	 * http://tools.ietf.org/html/rfc3492#section-3.4
-	 * @private
-	 */
-	function adapt(delta, numPoints, firstTime) {
-		var k = 0;
-		delta = firstTime ? floor(delta / damp) : delta >> 1;
-		delta += floor(delta / numPoints);
-		for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
-			delta = floor(delta / baseMinusTMin);
-		}
-		return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
-	}
-
-	/**
-	 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
-	 * symbols.
-	 * @memberOf punycode
-	 * @param {String} input The Punycode string of ASCII-only symbols.
-	 * @returns {String} The resulting string of Unicode symbols.
-	 */
-	function decode(input) {
-		// Don't use UCS-2
-		var output = [],
-		    inputLength = input.length,
-		    out,
-		    i = 0,
-		    n = initialN,
-		    bias = initialBias,
-		    basic,
-		    j,
-		    index,
-		    oldi,
-		    w,
-		    k,
-		    digit,
-		    t,
-		    /** Cached calculation results */
-		    baseMinusT;
-
-		// Handle the basic code points: let `basic` be the number of input code
-		// points before the last delimiter, or `0` if there is none, then copy
-		// the first basic code points to the output.
-
-		basic = input.lastIndexOf(delimiter);
-		if (basic < 0) {
-			basic = 0;
-		}
-
-		for (j = 0; j < basic; ++j) {
-			// if it's not a basic code point
-			if (input.charCodeAt(j) >= 0x80) {
-				error('not-basic');
-			}
-			output.push(input.charCodeAt(j));
-		}
-
-		// Main decoding loop: start just after the last delimiter if any basic code
-		// points were copied; start at the beginning otherwise.
-
-		for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
-
-			// `index` is the index of the next character to be consumed.
-			// Decode a generalized variable-length integer into `delta`,
-			// which gets added to `i`. The overflow checking is easier
-			// if we increase `i` as we go, then subtract off its starting
-			// value at the end to obtain `delta`.
-			for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
-
-				if (index >= inputLength) {
-					error('invalid-input');
-				}
-
-				digit = basicToDigit(input.charCodeAt(index++));
-
-				if (digit >= base || digit > floor((maxInt - i) / w)) {
-					error('overflow');
-				}
-
-				i += digit * w;
-				t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-
-				if (digit < t) {
-					break;
-				}
-
-				baseMinusT = base - t;
-				if (w > floor(maxInt / baseMinusT)) {
-					error('overflow');
-				}
-
-				w *= baseMinusT;
-
-			}
-
-			out = output.length + 1;
-			bias = adapt(i - oldi, out, oldi == 0);
-
-			// `i` was supposed to wrap around from `out` to `0`,
-			// incrementing `n` each time, so we'll fix that now:
-			if (floor(i / out) > maxInt - n) {
-				error('overflow');
-			}
-
-			n += floor(i / out);
-			i %= out;
-
-			// Insert `n` at position `i` of the output
-			output.splice(i++, 0, n);
-
-		}
-
-		return ucs2encode(output);
-	}
-
-	/**
-	 * Converts a string of Unicode symbols to a Punycode string of ASCII-only
-	 * symbols.
-	 * @memberOf punycode
-	 * @param {String} input The string of Unicode symbols.
-	 * @returns {String} The resulting Punycode string of ASCII-only symbols.
-	 */
-	function encode(input) {
-		var n,
-		    delta,
-		    handledCPCount,
-		    basicLength,
-		    bias,
-		    j,
-		    m,
-		    q,
-		    k,
-		    t,
-		    currentValue,
-		    output = [],
-		    /** `inputLength` will hold the number of code points in `input`. */
-		    inputLength,
-		    /** Cached calculation results */
-		    handledCPCountPlusOne,
-		    baseMinusT,
-		    qMinusT;
-
-		// Convert the input in UCS-2 to Unicode
-		input = ucs2decode(input);
-
-		// Cache the length
-		inputLength = input.length;
-
-		// Initialize the state
-		n = initialN;
-		delta = 0;
-		bias = initialBias;
-
-		// Handle the basic code points
-		for (j = 0; j < inputLength; ++j) {
-			currentValue = input[j];
-			if (currentValue < 0x80) {
-				output.push(stringFromCharCode(currentValue));
-			}
-		}
-
-		handledCPCount = basicLength = output.length;
-
-		// `handledCPCount` is the number of code points that have been handled;
-		// `basicLength` is the number of basic code points.
-
-		// Finish the basic string - if it is not empty - with a delimiter
-		if (basicLength) {
-			output.push(delimiter);
-		}
-
-		// Main encoding loop:
-		while (handledCPCount < inputLength) {
-
-			// All non-basic code points < n have been handled already. Find the next
-			// larger one:
-			for (m = maxInt, j = 0; j < inputLength; ++j) {
-				currentValue = input[j];
-				if (currentValue >= n && currentValue < m) {
-					m = currentValue;
-				}
-			}
-
-			// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
-			// but guard against overflow
-			handledCPCountPlusOne = handledCPCount + 1;
-			if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
-				error('overflow');
-			}
-
-			delta += (m - n) * handledCPCountPlusOne;
-			n = m;
-
-			for (j = 0; j < inputLength; ++j) {
-				currentValue = input[j];
-
-				if (currentValue < n && ++delta > maxInt) {
-					error('overflow');
-				}
-
-				if (currentValue == n) {
-					// Represent delta as a generalized variable-length integer
-					for (q = delta, k = base; /* no condition */; k += base) {
-						t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
-						if (q < t) {
-							break;
-						}
-						qMinusT = q - t;
-						baseMinusT = base - t;
-						output.push(
-							stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
-						);
-						q = floor(qMinusT / baseMinusT);
-					}
-
-					output.push(stringFromCharCode(digitToBasic(q, 0)));
-					bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
-					delta = 0;
-					++handledCPCount;
-				}
-			}
-
-			++delta;
-			++n;
-
-		}
-		return output.join('');
-	}
-
-	/**
-	 * Converts a Punycode string representing a domain name to Unicode. Only the
-	 * Punycoded parts of the domain name will be converted, i.e. it doesn't
-	 * matter if you call it on a string that has already been converted to
-	 * Unicode.
-	 * @memberOf punycode
-	 * @param {String} domain The Punycode domain name to convert to Unicode.
-	 * @returns {String} The Unicode representation of the given Punycode
-	 * string.
-	 */
-	function toUnicode(domain) {
-		return mapDomain(domain, function(string) {
-			return regexPunycode.test(string)
-				? decode(string.slice(4).toLowerCase())
-				: string;
-		});
-	}
-
-	/**
-	 * Converts a Unicode string representing a domain name to Punycode. Only the
-	 * non-ASCII parts of the domain name will be converted, i.e. it doesn't
-	 * matter if you call it with a domain that's already in ASCII.
-	 * @memberOf punycode
-	 * @param {String} domain The domain name to convert, as a Unicode string.
-	 * @returns {String} The Punycode representation of the given domain name.
-	 */
-	function toASCII(domain) {
-		return mapDomain(domain, function(string) {
-			return regexNonASCII.test(string)
-				? 'xn--' + encode(string)
-				: string;
-		});
-	}
-
-	/*--------------------------------------------------------------------------*/
-
-	/** Define the public API */
-	punycode = {
-		/**
-		 * A string representing the current Punycode.js version number.
-		 * @memberOf punycode
-		 * @type String
-		 */
-		'version': '1.2.4',
-		/**
-		 * An object of methods to convert from JavaScript's internal character
-		 * representation (UCS-2) to Unicode code points, and back.
-		 * @see <http://mathiasbynens.be/notes/javascript-encoding>
-		 * @memberOf punycode
-		 * @type Object
-		 */
-		'ucs2': {
-			'decode': ucs2decode,
-			'encode': ucs2encode
-		},
-		'decode': decode,
-		'encode': encode,
-		'toASCII': toASCII,
-		'toUnicode': toUnicode
-	};
-
-	/** Expose `punycode` */
-	// Some AMD build optimizers, like r.js, check for specific condition patterns
-	// like the following:
-	if (
-		typeof define == 'function' &&
-		typeof define.amd == 'object' &&
-		define.amd
-	) {
-		define('punycode', function() {
-			return punycode;
-		});
-	} else if (freeExports && !freeExports.nodeType) {
-		if (freeModule) { // in Node.js or RingoJS v0.8.0+
-			freeModule.exports = punycode;
-		} else { // in Narwhal or RingoJS v0.7.0-
-			for (key in punycode) {
-				punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
-			}
-		}
-	} else { // in Rhino or a web browser
-		root.punycode = punycode;
-	}
-
-}(this));
-
-}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
-},{}],4:[function(require,module,exports){
-var log = require('./log');
-var Promise = require('./promise');
-
-function restoreOwnerScroll(ownerDocument, x, y) {
-    if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
-        ownerDocument.defaultView.scrollTo(x, y);
-    }
-}
-
-function cloneCanvasContents(canvas, clonedCanvas) {
-    try {
-        if (clonedCanvas) {
-            clonedCanvas.width = canvas.width;
-            clonedCanvas.height = canvas.height;
-            clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0);
-        }
-    } catch(e) {
-        log("Unable to copy canvas content from", canvas, e);
-    }
-}
-
-function cloneNode(node, javascriptEnabled) {
-    var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
-
-    var child = node.firstChild;
-    while(child) {
-        if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
-            clone.appendChild(cloneNode(child, javascriptEnabled));
-        }
-        child = child.nextSibling;
-    }
-
-    if (node.nodeType === 1) {
-        clone._scrollTop = node.scrollTop;
-        clone._scrollLeft = node.scrollLeft;
-        if (node.nodeName === "CANVAS") {
-            cloneCanvasContents(node, clone);
-        } else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") {
-            clone.value = node.value;
-        }
-    }
-
-    return clone;
-}
-
-function initNode(node) {
-    if (node.nodeType === 1) {
-        node.scrollTop = node._scrollTop;
-        node.scrollLeft = node._scrollLeft;
-
-        var child = node.firstChild;
-        while(child) {
-            initNode(child);
-            child = child.nextSibling;
-        }
-    }
-}
-
-module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) {
-    var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled);
-    var container = containerDocument.createElement("iframe");
-
-    container.className = "html2canvas-container";
-    container.style.visibility = "hidden";
-    container.style.position = "fixed";
-    container.style.left = "-10000px";
-    container.style.top = "0px";
-    container.style.border = "0";
-    container.width = width;
-    container.height = height;
-    container.scrolling = "no"; // ios won't scroll without it
-    containerDocument.body.appendChild(container);
-
-    return new Promise(function(resolve) {
-        var documentClone = container.contentWindow.document;
-
-        /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
-         if window url is about:blank, we can assign the url to current by writing onto the document
-         */
-        container.contentWindow.onload = container.onload = function() {
-            var interval = setInterval(function() {
-                if (documentClone.body.childNodes.length > 0) {
-                    initNode(documentClone.documentElement);
-                    clearInterval(interval);
-                    if (options.type === "view") {
-                        container.contentWindow.scrollTo(x, y);
-                        if ((/(iPad|iPhone|iPod)/g).test(navigator.userAgent) && (container.contentWindow.scrollY !== y || container.contentWindow.scrollX !== x)) {
-                            documentClone.documentElement.style.top = (-y) + "px";
-                            documentClone.documentElement.style.left = (-x) + "px";
-                            documentClone.documentElement.style.position = 'absolute';
-                        }
-                    }
-                    resolve(container);
-                }
-            }, 50);
-        };
-
-        documentClone.open();
-        documentClone.write("<!DOCTYPE html><html></html>");
-        // Chrome scrolls the parent document for some reason after the write to the cloned window???
-        restoreOwnerScroll(ownerDocument, x, y);
-        documentClone.replaceChild(documentClone.adoptNode(documentElement), documentClone.documentElement);
-        documentClone.close();
-    });
-};
-
-},{"./log":15,"./promise":18}],5:[function(require,module,exports){
-// http://dev.w3.org/csswg/css-color/
-
-function Color(value) {
-    this.r = 0;
-    this.g = 0;
-    this.b = 0;
-    this.a = null;
-    var result = this.fromArray(value) ||
-        this.namedColor(value) ||
-        this.rgb(value) ||
-        this.rgba(value) ||
-        this.hex6(value) ||
-        this.hex3(value);
-}
-
-Color.prototype.darken = function(amount) {
-    var a = 1 - amount;
-    return  new Color([
-        Math.round(this.r * a),
-        Math.round(this.g * a),
-        Math.round(this.b * a),
-        this.a
-    ]);
-};
-
-Color.prototype.isTransparent = function() {
-    return this.a === 0;
-};
-
-Color.prototype.isBlack = function() {
-    return this.r === 0 && this.g === 0 && this.b === 0;
-};
-
-Color.prototype.fromArray = function(array) {
-    if (Array.isArray(array)) {
-        this.r = Math.min(array[0], 255);
-        this.g = Math.min(array[1], 255);
-        this.b = Math.min(array[2], 255);
-        if (array.length > 3) {
-            this.a = array[3];
-        }
-    }
-
-    return (Array.isArray(array));
-};
-
-var _hex3 = /^#([a-f0-9]{3})$/i;
-
-Color.prototype.hex3 = function(value) {
-    var match = null;
-    if ((match = value.match(_hex3)) !== null) {
-        this.r = parseInt(match[1][0] + match[1][0], 16);
-        this.g = parseInt(match[1][1] + match[1][1], 16);
-        this.b = parseInt(match[1][2] + match[1][2], 16);
-    }
-    return match !== null;
-};
-
-var _hex6 = /^#([a-f0-9]{6})$/i;
-
-Color.prototype.hex6 = function(value) {
-    var match = null;
-    if ((match = value.match(_hex6)) !== null) {
-        this.r = parseInt(match[1].substring(0, 2), 16);
-        this.g = parseInt(match[1].substring(2, 4), 16);
-        this.b = parseInt(match[1].substring(4, 6), 16);
-    }
-    return match !== null;
-};
-
-
-var _rgb = /^rgb\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3})\)$/;
-
-Color.prototype.rgb = function(value) {
-    var match = null;
-    if ((match = value.match(_rgb)) !== null) {
-        this.r = Number(match[1]);
-        this.g = Number(match[2]);
-        this.b = Number(match[3]);
-    }
-    return match !== null;
-};
-
-var _rgba = /^rgba\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *, *(\d+\.?\d*)\)$/;
-
-Color.prototype.rgba = function(value) {
-    var match = null;
-    if ((match = value.match(_rgba)) !== null) {
-        this.r = Number(match[1]);
-        this.g = Number(match[2]);
-        this.b = Number(match[3]);
-        this.a = Number(match[4]);
-    }
-    return match !== null;
-};
-
-Color.prototype.toString = function() {
-    return this.a !== null && this.a !== 1 ?
-    "rgba(" + [this.r, this.g, this.b, this.a].join(",") + ")" :
-    "rgb(" + [this.r, this.g, this.b].join(",") + ")";
-};
-
-Color.prototype.namedColor = function(value) {
-    var color = colors[value.toLowerCase()];
-    if (color) {
-        this.r = color[0];
-        this.g = color[1];
-        this.b = color[2];
-    } else if (value.toLowerCase() === "transparent") {
-        this.r = this.g = this.b = this.a = 0;
-        return true;
-    }
-
-    return !!color;
-};
-
-Color.prototype.isColor = true;
-
-// JSON.stringify([].slice.call($$('.named-color-table tr'), 1).map(function(row) { return [row.childNodes[3].textContent, row.childNodes[5].textContent.trim().split(",").map(Number)] }).reduce(function(data, row) {data[row[0]] = row[1]; return data}, {}))
-var colors = {
-    "aliceblue": [240, 248, 255],
-    "antiquewhite": [250, 235, 215],
-    "aqua": [0, 255, 255],
-    "aquamarine": [127, 255, 212],
-    "azure": [240, 255, 255],
-    "beige": [245, 245, 220],
-    "bisque": [255, 228, 196],
-    "black": [0, 0, 0],
-    "blanchedalmond": [255, 235, 205],
-    "blue": [0, 0, 255],
-    "blueviolet": [138, 43, 226],
-    "brown": [165, 42, 42],
-    "burlywood": [222, 184, 135],
-    "cadetblue": [95, 158, 160],
-    "chartreuse": [127, 255, 0],
-    "chocolate": [210, 105, 30],
-    "coral": [255, 127, 80],
-    "cornflowerblue": [100, 149, 237],
-    "cornsilk": [255, 248, 220],
-    "crimson": [220, 20, 60],
-    "cyan": [0, 255, 255],
-    "darkblue": [0, 0, 139],
-    "darkcyan": [0, 139, 139],
-    "darkgoldenrod": [184, 134, 11],
-    "darkgray": [169, 169, 169],
-    "darkgreen": [0, 100, 0],
-    "darkgrey": [169, 169, 169],
-    "darkkhaki": [189, 183, 107],
-    "darkmagenta": [139, 0, 139],
-    "darkolivegreen": [85, 107, 47],
-    "darkorange": [255, 140, 0],
-    "darkorchid": [153, 50, 204],
-    "darkred": [139, 0, 0],
-    "darksalmon": [233, 150, 122],
-    "darkseagreen": [143, 188, 143],
-    "darkslateblue": [72, 61, 139],
-    "darkslategray": [47, 79, 79],
-    "darkslategrey": [47, 79, 79],
-    "darkturquoise": [0, 206, 209],
-    "darkviolet": [148, 0, 211],
-    "deeppink": [255, 20, 147],
-    "deepskyblue": [0, 191, 255],
-    "dimgray": [105, 105, 105],
-    "dimgrey": [105, 105, 105],
-    "dodgerblue": [30, 144, 255],
-    "firebrick": [178, 34, 34],
-    "floralwhite": [255, 250, 240],
-    "forestgreen": [34, 139, 34],
-    "fuchsia": [255, 0, 255],
-    "gainsboro": [220, 220, 220],
-    "ghostwhite": [248, 248, 255],
-    "gold": [255, 215, 0],
-    "goldenrod": [218, 165, 32],
-    "gray": [128, 128, 128],
-    "green": [0, 128, 0],
-    "greenyellow": [173, 255, 47],
-    "grey": [128, 128, 128],
-    "honeydew": [240, 255, 240],
-    "hotpink": [255, 105, 180],
-    "indianred": [205, 92, 92],
-    "indigo": [75, 0, 130],
-    "ivory": [255, 255, 240],
-    "khaki": [240, 230, 140],
-    "lavender": [230, 230, 250],
-    "lavenderblush": [255, 240, 245],
-    "lawngreen": [124, 252, 0],
-    "lemonchiffon": [255, 250, 205],
-    "lightblue": [173, 216, 230],
-    "lightcoral": [240, 128, 128],
-    "lightcyan": [224, 255, 255],
-    "lightgoldenrodyellow": [250, 250, 210],
-    "lightgray": [211, 211, 211],
-    "lightgreen": [144, 238, 144],
-    "lightgrey": [211, 211, 211],
-    "lightpink": [255, 182, 193],
-    "lightsalmon": [255, 160, 122],
-    "lightseagreen": [32, 178, 170],
-    "lightskyblue": [135, 206, 250],
-    "lightslategray": [119, 136, 153],
-    "lightslategrey": [119, 136, 153],
-    "lightsteelblue": [176, 196, 222],
-    "lightyellow": [255, 255, 224],
-    "lime": [0, 255, 0],
-    "limegreen": [50, 205, 50],
-    "linen": [250, 240, 230],
-    "magenta": [255, 0, 255],
-    "maroon": [128, 0, 0],
-    "mediumaquamarine": [102, 205, 170],
-    "mediumblue": [0, 0, 205],
-    "mediumorchid": [186, 85, 211],
-    "mediumpurple": [147, 112, 219],
-    "mediumseagreen": [60, 179, 113],
-    "mediumslateblue": [123, 104, 238],
-    "mediumspringgreen": [0, 250, 154],
-    "mediumturquoise": [72, 209, 204],
-    "mediumvioletred": [199, 21, 133],
-    "midnightblue": [25, 25, 112],
-    "mintcream": [245, 255, 250],
-    "mistyrose": [255, 228, 225],
-    "moccasin": [255, 228, 181],
-    "navajowhite": [255, 222, 173],
-    "navy": [0, 0, 128],
-    "oldlace": [253, 245, 230],
-    "olive": [128, 128, 0],
-    "olivedrab": [107, 142, 35],
-    "orange": [255, 165, 0],
-    "orangered": [255, 69, 0],
-    "orchid": [218, 112, 214],
-    "palegoldenrod": [238, 232, 170],
-    "palegreen": [152, 251, 152],
-    "paleturquoise": [175, 238, 238],
-    "palevioletred": [219, 112, 147],
-    "papayawhip": [255, 239, 213],
-    "peachpuff": [255, 218, 185],
-    "peru": [205, 133, 63],
-    "pink": [255, 192, 203],
-    "plum": [221, 160, 221],
-    "powderblue": [176, 224, 230],
-    "purple": [128, 0, 128],
-    "rebeccapurple": [102, 51, 153],
-    "red": [255, 0, 0],
-    "rosybrown": [188, 143, 143],
-    "royalblue": [65, 105, 225],
-    "saddlebrown": [139, 69, 19],
-    "salmon": [250, 128, 114],
-    "sandybrown": [244, 164, 96],
-    "seagreen": [46, 139, 87],
-    "seashell": [255, 245, 238],
-    "sienna": [160, 82, 45],
-    "silver": [192, 192, 192],
-    "skyblue": [135, 206, 235],
-    "slateblue": [106, 90, 205],
-    "slategray": [112, 128, 144],
-    "slategrey": [112, 128, 144],
-    "snow": [255, 250, 250],
-    "springgreen": [0, 255, 127],
-    "steelblue": [70, 130, 180],
-    "tan": [210, 180, 140],
-    "teal": [0, 128, 128],
-    "thistle": [216, 191, 216],
-    "tomato": [255, 99, 71],
-    "turquoise": [64, 224, 208],
-    "violet": [238, 130, 238],
-    "wheat": [245, 222, 179],
-    "white": [255, 255, 255],
-    "whitesmoke": [245, 245, 245],
-    "yellow": [255, 255, 0],
-    "yellowgreen": [154, 205, 50]
-};
-
-module.exports = Color;
-
-},{}],6:[function(require,module,exports){
-var Promise = require('./promise');
-var Support = require('./support');
-var CanvasRenderer = require('./renderers/canvas');
-var ImageLoader = require('./imageloader');
-var NodeParser = require('./nodeparser');
-var NodeContainer = require('./nodecontainer');
-var log = require('./log');
-var utils = require('./utils');
-var createWindowClone = require('./clone');
-var loadUrlDocument = require('./proxy').loadUrlDocument;
-var getBounds = utils.getBounds;
-
-var html2canvasNodeAttribute = "data-html2canvas-node";
-var html2canvasCloneIndex = 0;
-
-function html2canvas(nodeList, options) {
-    var index = html2canvasCloneIndex++;
-    options = options || {};
-    if (options.logging) {
-        window.html2canvas.logging = true;
-        window.html2canvas.start = Date.now();
-    }
-
-    options.async = typeof(options.async) === "undefined" ? true : options.async;
-    options.allowTaint = typeof(options.allowTaint) === "undefined" ? false : options.allowTaint;
-    options.removeContainer = typeof(options.removeContainer) === "undefined" ? true : options.removeContainer;
-    options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled;
-    options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout;
-    options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer;
-    options.strict = !!options.strict;
-
-    if (typeof(nodeList) === "string") {
-        if (typeof(options.proxy) !== "string") {
-            return Promise.reject("Proxy must be used when rendering url");
-        }
-        var width = options.width != null ? options.width : window.innerWidth;
-        var height = options.height != null ? options.height : window.innerHeight;
-        return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) {
-            return renderWindow(container.contentWindow.document.documentElement, container, options, width, height);
-        });
-    }
-
-    var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0];
-    node.setAttribute(html2canvasNodeAttribute + index, index);
-    return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {
-        if (typeof(options.onrendered) === "function") {
-            log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
-            options.onrendered(canvas);
-        }
-        return canvas;
-    });
-}
-
-html2canvas.Promise = Promise;
-html2canvas.CanvasRenderer = CanvasRenderer;
-html2canvas.NodeContainer = NodeContainer;
-html2canvas.log = log;
-html2canvas.utils = utils;
-
-var html2canvasExport = (typeof(document) === "undefined" || typeof(Object.create) !== "function" || typeof(document.createElement("canvas").getContext) !== "function") ? function() {
-    return Promise.reject("No canvas support");
-} : html2canvas;
-
-module.exports = html2canvasExport;
-
-if (typeof(define) === 'function' && define.amd) {
-    define('html2canvas', [], function() {
-        return html2canvasExport;
-    });
-}
-
-function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) {
-    return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) {
-        log("Document cloned");
-        var attributeName = html2canvasNodeAttribute + html2canvasIndex;
-        var selector = "[" + attributeName + "='" + html2canvasIndex + "']";
-        document.querySelector(selector).removeAttribute(attributeName);
-        var clonedWindow = container.contentWindow;
-        var node = clonedWindow.document.querySelector(selector);
-        var oncloneHandler = (typeof(options.onclone) === "function") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true);
-        return oncloneHandler.then(function() {
-            return renderWindow(node, container, options, windowWidth, windowHeight);
-        });
-    });
-}
-
-function renderWindow(node, container, options, windowWidth, windowHeight) {
-    var clonedWindow = container.contentWindow;
-    var support = new Support(clonedWindow.document);
-    var imageLoader = new ImageLoader(options, support);
-    var bounds = getBounds(node);
-    var width = options.type === "view" ? windowWidth : documentWidth(clonedWindow.document);
-    var height = options.type === "view" ? windowHeight : documentHeight(clonedWindow.document);
-    var renderer = new options.renderer(width, height, imageLoader, options, document);
-    var parser = new NodeParser(node, renderer, support, imageLoader, options);
-    return parser.ready.then(function() {
-        log("Finished rendering");
-        var canvas;
-
-        if (options.type === "view") {
-            canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});
-        } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) {
-            canvas = renderer.canvas;
-        } else {
-            canvas = crop(renderer.canvas, {width:  options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: clonedWindow.pageXOffset, y: clonedWindow.pageYOffset});
-        }
-
-        cleanupContainer(container, options);
-        return canvas;
-    });
-}
-
-function cleanupContainer(container, options) {
-    if (options.removeContainer) {
-        container.parentNode.removeChild(container);
-        log("Cleaned up container");
-    }
-}
-
-function crop(canvas, bounds) {
-    var croppedCanvas = document.createElement("canvas");
-    var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left));
-    var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width));
-    var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top));
-    var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height));
-    croppedCanvas.width = bounds.width;
-    croppedCanvas.height =  bounds.height;
-    log("Cropping canvas at:", "left:", bounds.left, "top:", bounds.top, "width:", (x2-x1), "height:", (y2-y1));
-    log("Resulting crop with width", bounds.width, "and height", bounds.height, " with x", x1, "and y", y1);
-    croppedCanvas.getContext("2d").drawImage(canvas, x1, y1, x2-x1, y2-y1, bounds.x, bounds.y, x2-x1, y2-y1);
-    return croppedCanvas;
-}
-
-function documentWidth (doc) {
-    return Math.max(
-        Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
-        Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
-        Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
-    );
-}
-
-function documentHeight (doc) {
-    return Math.max(
-        Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
-        Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
-        Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
-    );
-}
-
-function absoluteUrl(url) {
-    var link = document.createElement("a");
-    link.href = url;
-    link.href = link.href;
-    return link;
-}
-
-},{"./clone":4,"./imageloader":13,"./log":15,"./nodecontainer":16,"./nodeparser":17,"./promise":18,"./proxy":19,"./renderers/canvas":23,"./support":25,"./utils":29}],7:[function(require,module,exports){
-var Promise = require('./promise');
-var log = require('./log');
-var smallImage = require('./utils').smallImage;
-
-function DummyImageContainer(src) {
-    this.src = src;
-    log("DummyImageContainer for", src);
-    if (!this.promise || !this.image) {
-        log("Initiating DummyImageContainer");
-        DummyImageContainer.prototype.image = new Image();
-        var image = this.image;
-        DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) {
-            image.onload = resolve;
-            image.onerror = reject;
-            image.src = smallImage();
-            if (image.complete === true) {
-                resolve(image);
-            }
-        });
-    }
-}
-
-module.exports = DummyImageContainer;
-
-},{"./log":15,"./promise":18,"./utils":29}],8:[function(require,module,exports){
-var smallImage = require('./utils').smallImage;
-
-function Font(family, size) {
-    var container = document.createElement('div'),
-        img = document.createElement('img'),
-        span = document.createElement('span'),
-        sampleText = 'Hidden Text',
-        baseline,
-        middle;
-
-    container.style.visibility = "hidden";
-    container.style.fontFamily = family;
-    container.style.fontSize = size;
-    container.style.margin = 0;
-    container.style.padding = 0;
-
-    document.body.appendChild(container);
-
-    img.src = smallImage();
-    img.width = 1;
-    img.height = 1;
-
-    img.style.margin = 0;
-    img.style.padding = 0;
-    img.style.verticalAlign = "baseline";
-
-    span.style.fontFamily = family;
-    span.style.fontSize = size;
-    span.style.margin = 0;
-    span.style.padding = 0;
-
-    span.appendChild(document.createTextNode(sampleText));
-    container.appendChild(span);
-    container.appendChild(img);
-    baseline = (img.offsetTop - span.offsetTop) + 1;
-
-    container.removeChild(span);
-    container.appendChild(document.createTextNode(sampleText));
-
-    container.style.lineHeight = "normal";
-    img.style.verticalAlign = "super";
-
-    middle = (img.offsetTop-container.offsetTop) + 1;
-
-    document.body.removeChild(container);
-
-    this.baseline = baseline;
-    this.lineWidth = 1;
-    this.middle = middle;
-}
-
-module.exports = Font;
-
-},{"./utils":29}],9:[function(require,module,exports){
-var Font = require('./font');
-
-function FontMetrics() {
-    this.data = {};
-}
-
-FontMetrics.prototype.getMetrics = function(family, size) {
-    if (this.data[family + "-" + size] === undefined) {
-        this.data[family + "-" + size] = new Font(family, size);
-    }
-    return this.data[family + "-" + size];
-};
-
-module.exports = FontMetrics;
-
-},{"./font":8}],10:[function(require,module,exports){
-var utils = require('./utils');
-var Promise = require('./promise');
-var getBounds = utils.getBounds;
-var loadUrlDocument = require('./proxy').loadUrlDocument;
-
-function FrameContainer(container, sameOrigin, options) {
-    this.image = null;
-    this.src = container;
-    var self = this;
-    var bounds = getBounds(container);
-    this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) {
-        if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
-            container.contentWindow.onload = container.onload = function() {
-                resolve(container);
-            };
-        } else {
-            resolve(container);
-        }
-    })).then(function(container) {
-        var html2canvas = require('./core');
-        return html2canvas(container.contentWindow.document.documentElement, {type: 'view', width: container.width, height: container.height, proxy: options.proxy, javascriptEnabled: options.javascriptEnabled, removeContainer: options.removeContainer, allowTaint: options.allowTaint, imageTimeout: options.imageTimeout / 2});
-    }).then(function(canvas) {
-        return self.image = canvas;
-    });
-}
-
-FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) {
-    var container = this.src;
-    return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options);
-};
-
-module.exports = FrameContainer;
-
-},{"./core":6,"./promise":18,"./proxy":19,"./utils":29}],11:[function(require,module,exports){
-var Promise = require('./promise');
-
-function GradientContainer(imageData) {
-    this.src = imageData.value;
-    this.colorStops = [];
-    this.type = null;
-    this.x0 = 0.5;
-    this.y0 = 0.5;
-    this.x1 = 0.5;
-    this.y1 = 0.5;
-    this.promise = Promise.resolve(true);
-}
-
-GradientContainer.prototype.TYPES = {
-    LINEAR: 1,
-    RADIAL: 2
-};
-
-module.exports = GradientContainer;
-
-},{"./promise":18}],12:[function(require,module,exports){
-var Promise = require('./promise');
-
-function ImageContainer(src, cors) {
-    this.src = src;
-    this.image = new Image();
-    var self = this;
-    this.tainted = null;
-    this.promise = new Promise(function(resolve, reject) {
-        self.image.onload = resolve;
-        self.image.onerror = reject;
-        if (cors) {
-            self.image.crossOrigin = "anonymous";
-        }
-        self.image.src = src;
-        if (self.image.complete === true) {
-            resolve(self.image);
-        }
-    });
-}
-
-module.exports = ImageContainer;
-
-},{"./promise":18}],13:[function(require,module,exports){
-var Promise = require('./promise');
-var log = require('./log');
-var ImageContainer = require('./imagecontainer');
-var DummyImageContainer = require('./dummyimagecontainer');
-var ProxyImageContainer = require('./proxyimagecontainer');
-var FrameContainer = require('./framecontainer');
-var SVGContainer = require('./svgcontainer');
-var SVGNodeContainer = require('./svgnodecontainer');
-var LinearGradientContainer = require('./lineargradientcontainer');
-var WebkitGradientContainer = require('./webkitgradientcontainer');
-var bind = require('./utils').bind;
-
-function ImageLoader(options, support) {
-    this.link = null;
-    this.options = options;
-    this.support = support;
-    this.origin = this.getOrigin(window.location.href);
-}
-
-ImageLoader.prototype.findImages = function(nodes) {
-    var images = [];
-    nodes.reduce(function(imageNodes, container) {
-        switch(container.node.nodeName) {
-        case "IMG":
-            return imageNodes.concat([{
-                args: [container.node.src],
-                method: "url"
-            }]);
-        case "svg":
-        case "IFRAME":
-            return imageNodes.concat([{
-                args: [container.node],
-                method: container.node.nodeName
-            }]);
-        }
-        return imageNodes;
-    }, []).forEach(this.addImage(images, this.loadImage), this);
-    return images;
-};
-
-ImageLoader.prototype.findBackgroundImage = function(images, container) {
-    container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this);
-    return images;
-};
-
-ImageLoader.prototype.addImage = function(images, callback) {
-    return function(newImage) {
-        newImage.args.forEach(function(image) {
-            if (!this.imageExists(images, image)) {
-                images.splice(0, 0, callback.call(this, newImage));
-                log('Added image #' + (images.length), typeof(image) === "string" ? image.substring(0, 100) : image);
-            }
-        }, this);
-    };
-};
-
-ImageLoader.prototype.hasImageBackground = function(imageData) {
-    return imageData.method !== "none";
-};
-
-ImageLoader.prototype.loadImage = function(imageData) {
-    if (imageData.method === "url") {
-        var src = imageData.args[0];
-        if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) {
-            return new SVGContainer(src);
-        } else if (src.match(/data:image\/.*;base64,/i)) {
-            return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
-        } else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) {
-            return new ImageContainer(src, false);
-        } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
-            return new ImageContainer(src, true);
-        } else if (this.options.proxy) {
-            return new ProxyImageContainer(src, this.options.proxy);
-        } else {
-            return new DummyImageContainer(src);
-        }
-    } else if (imageData.method === "linear-gradient") {
-        return new LinearGradientContainer(imageData);
-    } else if (imageData.method === "gradient") {
-        return new WebkitGradientContainer(imageData);
-    } else if (imageData.method === "svg") {
-        return new SVGNodeContainer(imageData.args[0], this.support.svg);
-    } else if (imageData.method === "IFRAME") {
-        return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options);
-    } else {
-        return new DummyImageContainer(imageData);
-    }
-};
-
-ImageLoader.prototype.isSVG = function(src) {
-    return src.substring(src.length - 3).toLowerCase() === "svg" || SVGContainer.prototype.isInline(src);
-};
-
-ImageLoader.prototype.imageExists = function(images, src) {
-    return images.some(function(image) {
-        return image.src === src;
-    });
-};
-
-ImageLoader.prototype.isSameOrigin = function(url) {
-    return (this.getOrigin(url) === this.origin);
-};
-
-ImageLoader.prototype.getOrigin = function(url) {
-    var link = this.link || (this.link = document.createElement("a"));
-    link.href = url;
-    link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
-    return link.protocol + link.hostname + link.port;
-};
-
-ImageLoader.prototype.getPromise = function(container) {
-    return this.timeout(container, this.options.imageTimeout)['catch'](function() {
-        var dummy = new DummyImageContainer(container.src);
-        return dummy.promise.then(function(image) {
-            container.image = image;
-        });
-    });
-};
-
-ImageLoader.prototype.get = function(src) {
-    var found = null;
-    return this.images.some(function(img) {
-        return (found = img).src === src;
-    }) ? found : null;
-};
-
-ImageLoader.prototype.fetch = function(nodes) {
-    this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes));
-    this.images.forEach(function(image, index) {
-        image.promise.then(function() {
-            log("Succesfully loaded image #"+ (index+1), image);
-        }, function(e) {
-            log("Failed loading image #"+ (index+1), image, e);
-        });
-    });
-    this.ready = Promise.all(this.images.map(this.getPromise, this));
-    log("Finished searching images");
-    return this;
-};
-
-ImageLoader.prototype.timeout = function(container, timeout) {
-    var timer;
-    var promise = Promise.race([container.promise, new Promise(function(res, reject) {
-        timer = setTimeout(function() {
-            log("Timed out loading image", container);
-            reject(container);
-        }, timeout);
-    })]).then(function(container) {
-        clearTimeout(timer);
-        return container;
-    });
-    promise['catch'](function() {
-        clearTimeout(timer);
-    });
-    return promise;
-};
-
-module.exports = ImageLoader;
-
-},{"./dummyimagecontainer":7,"./framecontainer":10,"./imagecontainer":12,"./lineargradientcontainer":14,"./log":15,"./promise":18,"./proxyimagecontainer":20,"./svgcontainer":26,"./svgnodecontainer":27,"./utils":29,"./webkitgradientcontainer":30}],14:[function(require,module,exports){
-var GradientContainer = require('./gradientcontainer');
-var Color = require('./color');
-
-var COLOR_STOP_REGEXP = /^\s*(.*)\s*(\d{1,3})?(%|px)?$/;
-
-function LinearGradientContainer(imageData) {
-    GradientContainer.apply(this, arguments);
-    this.type = this.TYPES.LINEAR;
-
-    var hasDirection = imageData.args[0].match(this.stepRegExp) === null;
-
-    if (hasDirection) {
-        imageData.args[0].split(" ").reverse().forEach(function(position) {
-            switch(position) {
-            case "left":
-                this.x0 = 0;
-                this.x1 = 1;
-                break;
-            case "top":
-                this.y0 = 0;
-                this.y1 = 1;
-                break;
-            case "right":
-                this.x0 = 1;
-                this.x1 = 0;
-                break;
-            case "bottom":
-                this.y0 = 1;
-                this.y1 = 0;
-                break;
-            case "to":
-                var y0 = this.y0;
-                var x0 = this.x0;
-                this.y0 = this.y1;
-                this.x0 = this.x1;
-                this.x1 = x0;
-                this.y1 = y0;
-                break;
-            }
-        }, this);
-    } else {
-        this.y0 = 0;
-        this.y1 = 1;
-    }
-
-    this.colorStops = imageData.args.slice(hasDirection ? 1 : 0)
-        .map(function(colorStop) { return colorStop.match(COLOR_STOP_REGEXP);})
-        .filter(function(colorStopMatch) { return !!colorStopMatch;})
-        .map(function(colorStopMatch) {
-            return {
-                color: new Color(colorStopMatch[1]),
-                stop: colorStopMatch[3] === "%" ? colorStopMatch[2] / 100 : null
-            };
-        });
-
-    if (this.colorStops[0].stop === null) {
-        this.colorStops[0].stop = 0;
-    }
-
-    if (this.colorStops[this.colorStops.length - 1].stop === null) {
-        this.colorStops[this.colorStops.length - 1].stop = 1;
-    }
-
-    this.colorStops.forEach(function(colorStop, index) {
-        if (colorStop.stop === null) {
-            this.colorStops.slice(index).some(function(find, count) {
-                if (find.stop !== null) {
-                    colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop;
-                    return true;
-                } else {
-                    return false;
-                }
-            }, this);
-        }
-    }, this);
-}
-
-LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);
-
-LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/;
-
-module.exports = LinearGradientContainer;
-
-},{"./color":5,"./gradientcontainer":11}],15:[function(require,module,exports){
-module.exports = function() {
-    if (window.html2canvas.logging && window.console && window.console.log) {
-        Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
-    }
-};
-
-},{}],16:[function(require,module,exports){
-var Color = require('./color');
-var utils = require('./utils');
-var getBounds = utils.getBounds;
-var parseBackgrounds = utils.parseBackgrounds;
-var offsetBounds = utils.offsetBounds;
-
-function NodeContainer(node, parent) {
-    this.node = node;
-    this.parent = parent;
-    this.stack = null;
-    this.bounds = null;
-    this.borders = null;
-    this.clip = [];
-    this.backgroundClip = [];
-    this.offsetBounds = null;
-    this.visible = null;
-    this.computedStyles = null;
-    this.colors = {};
-    this.styles = {};
-    this.backgroundImages = null;
-    this.transformData = null;
-    this.transformMatrix = null;
-    this.isPseudoElement = false;
-    this.opacity = null;
-}
-
-NodeContainer.prototype.cloneTo = function(stack) {
-    stack.visible = this.visible;
-    stack.borders = this.borders;
-    stack.bounds = this.bounds;
-    stack.clip = this.clip;
-    stack.backgroundClip = this.backgroundClip;
-    stack.computedStyles = this.computedStyles;
-    stack.styles = this.styles;
-    stack.backgroundImages = this.backgroundImages;
-    stack.opacity = this.opacity;
-};
-
-NodeContainer.prototype.getOpacity = function() {
-    return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity;
-};
-
-NodeContainer.prototype.assignStack = function(stack) {
-    this.stack = stack;
-    stack.children.push(this);
-};
-
-NodeContainer.prototype.isElementVisible = function() {
-    return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : (
-        this.css('display') !== "none" &&
-        this.css('visibility') !== "hidden" &&
-        !this.node.hasAttribute("data-html2canvas-ignore") &&
-        (this.node.nodeName !== "INPUT" || this.node.getAttribute("type") !== "hidden")
-    );
-};
-
-NodeContainer.prototype.css = function(attribute) {
-    if (!this.computedStyles) {
-        this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? ":before" : ":after") : this.computedStyle(null);
-    }
-
-    return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]);
-};
-
-NodeContainer.prototype.prefixedCss = function(attribute) {
-    var prefixes = ["webkit", "moz", "ms", "o"];
-    var value = this.css(attribute);
-    if (value === undefined) {
-        prefixes.some(function(prefix) {
-            value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1));
-            return value !== undefined;
-        }, this);
-    }
-    return value === undefined ? null : value;
-};
-
-NodeContainer.prototype.computedStyle = function(type) {
-    return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type);
-};
-
-NodeContainer.prototype.cssInt = function(attribute) {
-    var value = parseInt(this.css(attribute), 10);
-    return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html
-};
-
-NodeContainer.prototype.color = function(attribute) {
-    return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute)));
-};
-
-NodeContainer.prototype.cssFloat = function(attribute) {
-    var value = parseFloat(this.css(attribute));
-    return (isNaN(value)) ? 0 : value;
-};
-
-NodeContainer.prototype.fontWeight = function() {
-    var weight = this.css("fontWeight");
-    switch(parseInt(weight, 10)){
-    case 401:
-        weight = "bold";
-        break;
-    case 400:
-        weight = "normal";
-        break;
-    }
-    return weight;
-};
-
-NodeContainer.prototype.parseClip = function() {
-    var matches = this.css('clip').match(this.CLIP);
-    if (matches) {
-        return {
-            top: parseInt(matches[1], 10),
-            right: parseInt(matches[2], 10),
-            bottom: parseInt(matches[3], 10),
-            left: parseInt(matches[4], 10)
-        };
-    }
-    return null;
-};
-
-NodeContainer.prototype.parseBackgroundImages = function() {
-    return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css("backgroundImage")));
-};
-
-NodeContainer.prototype.cssList = function(property, index) {
-    var value = (this.css(property) || '').split(',');
-    value = value[index || 0] || value[0] || 'auto';
-    value = value.trim().split(' ');
-    if (value.length === 1) {
-        value = [value[0], isPercentage(value[0]) ? 'auto' : value[0]];
-    }
-    return value;
-};
-
-NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) {
-    var size = this.cssList("backgroundSize", index);
-    var width, height;
-
-    if (isPercentage(size[0])) {
-        width = bounds.width * parseFloat(size[0]) / 100;
-    } else if (/contain|cover/.test(size[0])) {
-        var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height;
-        return (targetRatio < currentRatio ^ size[0] === 'contain') ?  {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio};
-    } else {
-        width = parseInt(size[0], 10);
-    }
-
-    if (size[0] === 'auto' && size[1] === 'auto') {
-        height = image.height;
-    } else if (size[1] === 'auto') {
-        height = width / image.width * image.height;
-    } else if (isPercentage(size[1])) {
-        height =  bounds.height * parseFloat(size[1]) / 100;
-    } else {
-        height = parseInt(size[1], 10);
-    }
-
-    if (size[0] === 'auto') {
-        width = height / image.height * image.width;
-    }
-
-    return {width: width, height: height};
-};
-
-NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) {
-    var position = this.cssList('backgroundPosition', index);
-    var left, top;
-
-    if (isPercentage(position[0])){
-        left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100);
-    } else {
-        left = parseInt(position[0], 10);
-    }
-
-    if (position[1] === 'auto') {
-        top = left / image.width * image.height;
-    } else if (isPercentage(position[1])){
-        top =  (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100;
-    } else {
-        top = parseInt(position[1], 10);
-    }
-
-    if (position[0] === 'auto') {
-        left = top / image.height * image.width;
-    }
-
-    return {left: left, top: top};
-};
-
-NodeContainer.prototype.parseBackgroundRepeat = function(index) {
-    return this.cssList("backgroundRepeat", index)[0];
-};
-
-NodeContainer.prototype.parseTextShadows = function() {
-    var textShadow = this.css("textShadow");
-    var results = [];
-
-    if (textShadow && textShadow !== 'none') {
-        var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY);
-        for (var i = 0; shadows && (i < shadows.length); i++) {
-            var s = shadows[i].match(this.TEXT_SHADOW_VALUES);
-            results.push({
-                color: new Color(s[0]),
-                offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0,
-                offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0,
-                blur: s[3] ? s[3].replace('px', '') : 0
-            });
-        }
-    }
-    return results;
-};
-
-NodeContainer.prototype.parseTransform = function() {
-    if (!this.transformData) {
-        if (this.hasTransform()) {
-            var offset = this.parseBounds();
-            var origin = this.prefixedCss("transformOrigin").split(" ").map(removePx).map(asFloat);
-            origin[0] += offset.left;
-            origin[1] += offset.top;
-            this.transformData = {
-                origin: origin,
-                matrix: this.parseTransformMatrix()
-            };
-        } else {
-            this.transformData = {
-                origin: [0, 0],
-                matrix: [1, 0, 0, 1, 0, 0]
-            };
-        }
-    }
-    return this.transformData;
-};
-
-NodeContainer.prototype.parseTransformMatrix = function() {
-    if (!this.transformMatrix) {
-        var transform = this.prefixedCss("transform");
-        var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null;
-        this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0];
-    }
-    return this.transformMatrix;
-};
-
-NodeContainer.prototype.parseBounds = function() {
-    return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node));
-};
-
-NodeContainer.prototype.hasTransform = function() {
-    return this.parseTransformMatrix().join(",") !== "1,0,0,1,0,0" || (this.parent && this.parent.hasTransform());
-};
-
-NodeContainer.prototype.getValue = function() {
-    var value = this.node.value || "";
-    if (this.node.tagName === "SELECT") {
-        value = selectionValue(this.node);
-    } else if (this.node.type === "password") {
-        value = Array(value.length + 1).join('\u2022'); // jshint ignore:line
-    }
-    return value.length === 0 ? (this.node.placeholder || "") : value;
-};
-
-NodeContainer.prototype.MATRIX_PROPERTY = /(matrix|matrix3d)\((.+)\)/;
-NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g;
-NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;
-NodeContainer.prototype.CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/;
-
-function selectionValue(node) {
-    var option = node.options[node.selectedIndex || 0];
-    return option ? (option.text || "") : "";
-}
-
-function parseMatrix(match) {
-    if (match && match[1] === "matrix") {
-        return match[2].split(",").map(function(s) {
-            return parseFloat(s.trim());
-        });
-    } else if (match && match[1] === "matrix3d") {
-        var matrix3d = match[2].split(",").map(function(s) {
-          return parseFloat(s.trim());
-        });
-        return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]];
-    }
-}
-
-function isPercentage(value) {
-    return value.toString().indexOf("%") !== -1;
-}
-
-function removePx(str) {
-    return str.replace("px", "");
-}
-
-function asFloat(str) {
-    return parseFloat(str);
-}
-
-module.exports = NodeContainer;
-
-},{"./color":5,"./utils":29}],17:[function(require,module,exports){
-var log = require('./log');
-var punycode = require('punycode');
-var NodeContainer = require('./nodecontainer');
-var TextContainer = require('./textcontainer');
-var PseudoElementContainer = require('./pseudoelementcontainer');
-var FontMetrics = require('./fontmetrics');
-var Color = require('./color');
-var Promise = require('./promise');
-var StackingContext = require('./stackingcontext');
-var utils = require('./utils');
-var bind = utils.bind;
-var getBounds = utils.getBounds;
-var parseBackgrounds = utils.parseBackgrounds;
-var offsetBounds = utils.offsetBounds;
-
-function NodeParser(element, renderer, support, imageLoader, options) {
-    log("Starting NodeParser");
-    this.renderer = renderer;
-    this.options = options;
-    this.range = null;
-    this.support = support;
-    this.renderQueue = [];
-    this.stack = new StackingContext(true, 1, element.ownerDocument, null);
-    var parent = new NodeContainer(element, null);
-    if (options.background) {
-        renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background));
-    }
-    if (element === element.ownerDocument.documentElement) {
-        // http://www.w3.org/TR/css3-background/#special-backgrounds
-        var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null);
-        renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor'));
-    }
-    parent.visibile = parent.isElementVisible();
-    this.createPseudoHideStyles(element.ownerDocument);
-    this.disableAnimations(element.ownerDocument);
-    this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) {
-        return container.visible = container.isElementVisible();
-    }).map(this.getPseudoElements, this));
-    this.fontMetrics = new FontMetrics();
-    log("Fetched nodes, total:", this.nodes.length);
-    log("Calculate overflow clips");
-    this.calculateOverflowClips();
-    log("Start fetching images");
-    this.images = imageLoader.fetch(this.nodes.filter(isElement));
-    this.ready = this.images.ready.then(bind(function() {
-        log("Images loaded, starting parsing");
-        log("Creating stacking contexts");
-        this.createStackingContexts();
-        log("Sorting stacking contexts");
-        this.sortStackingContexts(this.stack);
-        this.parse(this.stack);
-        log("Render queue created with " + this.renderQueue.length + " items");
-        return new Promise(bind(function(resolve) {
-            if (!options.async) {
-                this.renderQueue.forEach(this.paint, this);
-                resolve();
-            } else if (typeof(options.async) === "function") {
-                options.async.call(this, this.renderQueue, resolve);
-            } else if (this.renderQueue.length > 0){
-                this.renderIndex = 0;
-                this.asyncRenderer(this.renderQueue, resolve);
-            } else {
-                resolve();
-            }
-        }, this));
-    }, this));
-}
-
-NodeParser.prototype.calculateOverflowClips = function() {
-    this.nodes.forEach(function(container) {
-        if (isElement(container)) {
-            if (isPseudoElement(container)) {
-                container.appendToDOM();
-            }
-            container.borders = this.parseBorders(container);
-            var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : [];
-            var cssClip = container.parseClip();
-            if (cssClip && ["absolute", "fixed"].indexOf(container.css('position')) !== -1) {
-                clip.push([["rect",
-                        container.bounds.left + cssClip.left,
-                        container.bounds.top + cssClip.top,
-                        cssClip.right - cssClip.left,
-                        cssClip.bottom - cssClip.top
-                ]]);
-            }
-            container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;
-            container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip;
-            if (isPseudoElement(container)) {
-                container.cleanDOM();
-            }
-        } else if (isTextNode(container)) {
-            container.clip = hasParentClip(container) ? container.parent.clip : [];
-        }
-        if (!isPseudoElement(container)) {
-            container.bounds = null;
-        }
-    }, this);
-};
-
-function hasParentClip(container) {
-    return container.parent && container.parent.clip.length;
-}
-
-NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {
-    asyncTimer = asyncTimer || Date.now();
-    this.paint(queue[this.renderIndex++]);
-    if (queue.length === this.renderIndex) {
-        resolve();
-    } else if (asyncTimer + 20 > Date.now()) {
-        this.asyncRenderer(queue, resolve, asyncTimer);
-    } else {
-        setTimeout(bind(function() {
-            this.asyncRenderer(queue, resolve);
-        }, this), 0);
-    }
-};
-
-NodeParser.prototype.createPseudoHideStyles = function(document) {
-    this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: "" !important; display: none !important; }' +
-        '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: "" !important; display: none !important; }');
-};
-
-NodeParser.prototype.disableAnimations = function(document) {
-    this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +
-        '-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}');
-};
-
-NodeParser.prototype.createStyles = function(document, styles) {
-    var hidePseudoElements = document.createElement('style');
-    hidePseudoElements.innerHTML = styles;
-    document.body.appendChild(hidePseudoElements);
-};
-
-NodeParser.prototype.getPseudoElements = function(container) {
-    var nodes = [[container]];
-    if (container.node.nodeType === Node.ELEMENT_NODE) {
-        var before = this.getPseudoElement(container, ":before");
-        var after = this.getPseudoElement(container, ":after");
-
-        if (before) {
-            nodes.push(before);
-        }
-
-        if (after) {
-            nodes.push(after);
-        }
-    }
-    return flatten(nodes);
-};
-
-function toCamelCase(str) {
-    return str.replace(/(\-[a-z])/g, function(match){
-        return match.toUpperCase().replace('-','');
-    });
-}
-
-NodeParser.prototype.getPseudoElement = function(container, type) {
-    var style = container.computedStyle(type);
-    if(!style || !style.content || style.content === "none" || style.content === "-moz-alt-content" || style.display === "none") {
-        return null;
-    }
-
-    var content = stripQuotes(style.content);
-    var isImage = content.substr(0, 3) === 'url';
-    var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement');
-    var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type);
-
-    for (var i = style.length-1; i >= 0; i--) {
-        var property = toCamelCase(style.item(i));
-        pseudoNode.style[property] = style[property];
-    }
-
-    pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + " " + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER;
-
-    if (isImage) {
-        pseudoNode.src = parseBackgrounds(content)[0].args[0];
-        return [pseudoContainer];
-    } else {
-        var text = document.createTextNode(content);
-        pseudoNode.appendChild(text);
-        return [pseudoContainer, new TextContainer(text, pseudoContainer)];
-    }
-};
-
-
-NodeParser.prototype.getChildren = function(parentContainer) {
-    return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
-        var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
-        return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
-    }, this));
-};
-
-NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
-    var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent);
-    container.cloneTo(stack);
-    var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;
-    parentStack.contexts.push(stack);
-    container.stack = stack;
-};
-
-NodeParser.prototype.createStackingContexts = function() {
-    this.nodes.forEach(function(container) {
-        if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) {
-            this.newStackingContext(container, true);
-        } else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) {
-            this.newStackingContext(container, false);
-        } else {
-            container.assignStack(container.parent.stack);
-        }
-    }, this);
-};
-
-NodeParser.prototype.isBodyWithTransparentRoot = function(container) {
-    return container.node.nodeName === "BODY" && container.parent.color('backgroundColor').isTransparent();
-};
-
-NodeParser.prototype.isRootElement = function(container) {
-    return container.parent === null;
-};
-
-NodeParser.prototype.sortStackingContexts = function(stack) {
-    stack.contexts.sort(zIndexSort(stack.contexts.slice(0)));
-    stack.contexts.forEach(this.sortStackingContexts, this);
-};
-
-NodeParser.prototype.parseTextBounds = function(container) {
-    return function(text, index, textList) {
-        if (container.parent.css("textDecoration").substr(0, 4) !== "none" || text.trim().length !== 0) {
-            if (this.support.rangeBounds && !container.parent.hasTransform()) {
-                var offset = textList.slice(0, index).join("").length;
-                return this.getRangeBounds(container.node, offset, text.length);
-            } else if (container.node && typeof(container.node.data) === "string") {
-                var replacementNode = container.node.splitText(text.length);
-                var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform());
-                container.node = replacementNode;
-                return bounds;
-            }
-        } else if(!this.support.rangeBounds || container.parent.hasTransform()){
-            container.node = container.node.splitText(text.length);
-        }
-        return {};
-    };
-};
-
-NodeParser.prototype.getWrapperBounds = function(node, transform) {
-    var wrapper = node.ownerDocument.createElement('html2canvaswrapper');
-    var parent = node.parentNode,
-        backupText = node.cloneNode(true);
-
-    wrapper.appendChild(node.cloneNode(true));
-    parent.replaceChild(wrapper, node);
-    var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper);
-    parent.replaceChild(backupText, wrapper);
-    return bounds;
-};
-
-NodeParser.prototype.getRangeBounds = function(node, offset, length) {
-    var range = this.range || (this.range = node.ownerDocument.createRange());
-    range.setStart(node, offset);
-    range.setEnd(node, offset + length);
-    return range.getBoundingClientRect();
-};
-
-function ClearTransform() {}
-
-NodeParser.prototype.parse = function(stack) {
-    // http://www.w3.org/TR/CSS21/visuren.html#z-index
-    var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).
-    var descendantElements = stack.children.filter(isElement);
-    var descendantNonFloats = descendantElements.filter(not(isFloating));
-    var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.
-    var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.
-    var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
-    var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
-    var text = stack.children.filter(isTextNode).filter(hasText);
-    var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).
-    negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)
-        .concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {
-            this.renderQueue.push(container);
-            if (isStackingContext(container)) {
-                this.parse(container);
-                this.renderQueue.push(new ClearTransform());
-            }
-        }, this);
-};
-
-NodeParser.prototype.paint = function(container) {
-    try {
-        if (container instanceof ClearTransform) {
-            this.renderer.ctx.restore();
-        } else if (isTextNode(container)) {
-            if (isPseudoElement(container.parent)) {
-                container.parent.appendToDOM();
-            }
-            this.paintText(container);
-            if (isPseudoElement(container.parent)) {
-                container.parent.cleanDOM();
-            }
-        } else {
-            this.paintNode(container);
-        }
-    } catch(e) {
-        log(e);
-        if (this.options.strict) {
-            throw e;
-        }
-    }
-};
-
-NodeParser.prototype.paintNode = function(container) {
-    if (isStackingContext(container)) {
-        this.renderer.setOpacity(container.opacity);
-        this.renderer.ctx.save();
-        if (container.hasTransform()) {
-            this.renderer.setTransform(container.parseTransform());
-        }
-    }
-
-    if (container.node.nodeName === "INPUT" && container.node.type === "checkbox") {
-        this.paintCheckbox(container);
-    } else if (container.node.nodeName === "INPUT" && container.node.type === "radio") {
-        this.paintRadio(container);
-    } else {
-        this.paintElement(container);
-    }
-};
-
-NodeParser.prototype.paintElement = function(container) {
-    var bounds = container.parseBounds();
-    this.renderer.clip(container.backgroundClip, function() {
-        this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));
-    }, this);
-
-    this.renderer.clip(container.clip, function() {
-        this.renderer.renderBorders(container.borders.borders);
-    }, this);
-
-    this.renderer.clip(container.backgroundClip, function() {
-        switch (container.node.nodeName) {
-        case "svg":
-        case "IFRAME":
-            var imgContainer = this.images.get(container.node);
-            if (imgContainer) {
-                this.renderer.renderImage(container, bounds, container.borders, imgContainer);
-            } else {
-                log("Error loading <" + container.node.nodeName + ">", container.node);
-            }
-            break;
-        case "IMG":
-            var imageContainer = this.images.get(container.node.src);
-            if (imageContainer) {
-                this.renderer.renderImage(container, bounds, container.borders, imageContainer);
-            } else {
-                log("Error loading <img>", container.node.src);
-            }
-            break;
-        case "CANVAS":
-            this.renderer.renderImage(container, bounds, container.borders, {image: container.node});
-            break;
-        case "SELECT":
-        case "INPUT":
-        case "TEXTAREA":
-            this.paintFormValue(container);
-            break;
-        }
-    }, this);
-};
-
-NodeParser.prototype.paintCheckbox = function(container) {
-    var b = container.parseBounds();
-
-    var size = Math.min(b.width, b.height);
-    var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left};
-    var r = [3, 3];
-    var radius = [r, r, r, r];
-    var borders = [1,1,1,1].map(function(w) {
-        return {color: new Color('#A5A5A5'), width: w};
-    });
-
-    var borderPoints = calculateCurvePoints(bounds, radius, borders);
-
-    this.renderer.clip(container.backgroundClip, function() {
-        this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color("#DEDEDE"));
-        this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius));
-        if (container.node.checked) {
-            this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + "px", 'arial');
-            this.renderer.text("\u2714", bounds.left + size / 6, bounds.top + size - 1);
-        }
-    }, this);
-};
-
-NodeParser.prototype.paintRadio = function(container) {
-    var bounds = container.parseBounds();
-
-    var size = Math.min(bounds.width, bounds.height) - 2;
-
-    this.renderer.clip(container.backgroundClip, function() {
-        this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5'));
-        if (container.node.checked) {
-            this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242'));
-        }
-    }, this);
-};
-
-NodeParser.prototype.paintFormValue = function(container) {
-    var value = container.getValue();
-    if (value.length > 0) {
-        var document = container.node.ownerDocument;
-        var wrapper = document.createElement('html2canvaswrapper');
-        var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',
-            'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',
-            'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth',
-            'boxSizing', 'whiteSpace', 'wordWrap'];
-
-        properties.forEach(function(property) {
-            try {
-                wrapper.style[property] = container.css(property);
-            } catch(e) {
-                // Older IE has issues with "border"
-                log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
-            }
-        });
-        var bounds = container.parseBounds();
-        wrapper.style.position = "fixed";
-        wrapper.style.left = bounds.left + "px";
-        wrapper.style.top = bounds.top + "px";
-        wrapper.textContent = value;
-        document.body.appendChild(wrapper);
-        this.paintText(new TextContainer(wrapper.firstChild, container));
-        document.body.removeChild(wrapper);
-    }
-};
-
-NodeParser.prototype.paintText = function(container) {
-    container.applyTextTransform();
-    var characters = punycode.ucs2.decode(container.node.data);
-    var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(character) {
-        return punycode.ucs2.encode([character]);
-    });
-
-    var weight = container.parent.fontWeight();
-    var size = container.parent.css('fontSize');
-    var family = container.parent.css('fontFamily');
-    var shadows = container.parent.parseTextShadows();
-
-    this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
-    if (shadows.length) {
-        // TODO: support multiple text shadows
-        this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur);
-    } else {
-        this.renderer.clearShadow();
-    }
-
-    this.renderer.clip(container.parent.clip, function() {
-        textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
-            if (bounds) {
-                this.renderer.text(textList[index], bounds.left, bounds.bottom);
-                this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
-            }
-        }, this);
-    }, this);
-};
-
-NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) {
-    switch(container.css("textDecoration").split(" ")[0]) {
-    case "underline":
-        // Draws a line at the baseline of the font
-        // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size
-        this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color("color"));
-        break;
-    case "overline":
-        this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color("color"));
-        break;
-    case "line-through":
-        // TODO try and find exact position for line-through
-        this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color("color"));
-        break;
-    }
-};
-
-var borderColorTransforms = {
-    inset: [
-        ["darken", 0.60],
-        ["darken", 0.10],
-        ["darken", 0.10],
-        ["darken", 0.60]
-    ]
-};
-
-NodeParser.prototype.parseBorders = function(container) {
-    var nodeBounds = container.parseBounds();
-    var radius = getBorderRadiusData(container);
-    var borders = ["Top", "Right", "Bottom", "Left"].map(function(side, index) {
-        var style = container.css('border' + side + 'Style');
-        var color = container.color('border' + side + 'Color');
-        if (style === "inset" && color.isBlack()) {
-            color = new Color([255, 255, 255, color.a]); // this is wrong, but
-        }
-        var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null;
-        return {
-            width: container.cssInt('border' + side + 'Width'),
-            color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color,
-            args: null
-        };
-    });
-    var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);
-
-    return {
-        clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),
-        borders: calculateBorders(borders, nodeBounds, borderPoints, radius)
-    };
-};
-
-function calculateBorders(borders, nodeBounds, borderPoints, radius) {
-    return borders.map(function(border, borderSide) {
-        if (border.width > 0) {
-            var bx = nodeBounds.left;
-            var by = nodeBounds.top;
-            var bw = nodeBounds.width;
-            var bh = nodeBounds.height - (borders[2].width);
-
-            switch(borderSide) {
-            case 0:
-                // top border
-                bh = borders[0].width;
-                border.args = drawSide({
-                        c1: [bx, by],
-                        c2: [bx + bw, by],
-                        c3: [bx + bw - borders[1].width, by + bh],
-                        c4: [bx + borders[3].width, by + bh]
-                    }, radius[0], radius[1],
-                    borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
-                break;
-            case 1:
-                // right border
-                bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
-                bw = borders[1].width;
-
-                border.args = drawSide({
-                        c1: [bx + bw, by],
-                        c2: [bx + bw, by + bh + borders[2].width],
-                        c3: [bx, by + bh],
-                        c4: [bx, by + borders[0].width]
-                    }, radius[1], radius[2],
-                    borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
-                break;
-            case 2:
-                // bottom border
-                by = (by + nodeBounds.height) - (borders[2].width);
-                bh = borders[2].width;
-                border.args = drawSide({
-                        c1: [bx + bw, by + bh],
-                        c2: [bx, by + bh],
-                        c3: [bx + borders[3].width, by],
-                        c4: [bx + bw - borders[3].width, by]
-                    }, radius[2], radius[3],
-                    borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
-                break;
-            case 3:
-                // left border
-                bw = borders[3].width;
-                border.args = drawSide({
-                        c1: [bx, by + bh + borders[2].width],
-                        c2: [bx, by],
-                        c3: [bx + bw, by + borders[0].width],
-                        c4: [bx + bw, by + bh]
-                    }, radius[3], radius[0],
-                    borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
-                break;
-            }
-        }
-        return border;
-    });
-}
-
-NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {
-    var backgroundClip = container.css('backgroundClip'),
-        borderArgs = [];
-
-    switch(backgroundClip) {
-    case "content-box":
-    case "padding-box":
-        parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
-        parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
-        parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
-        parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
-        break;
-
-    default:
-        parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
-        parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
-        parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
-        parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
-        break;
-    }
-
-    return borderArgs;
-};
-
-function getCurvePoints(x, y, r1, r2) {
-    var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
-    var ox = (r1) * kappa, // control point offset horizontal
-        oy = (r2) * kappa, // control point offset vertical
-        xm = x + r1, // x-middle
-        ym = y + r2; // y-middle
-    return {
-        topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),
-        topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),
-        bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),
-        bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})
-    };
-}
-
-function calculateCurvePoints(bounds, borderRadius, borders) {
-    var x = bounds.left,
-        y = bounds.top,
-        width = bounds.width,
-        height = bounds.height,
-
-        tlh = borderRadius[0][0] < width / 2 ? borderRadius[0][0] : width / 2,
-        tlv = borderRadius[0][1] < height / 2 ? borderRadius[0][1] : height / 2,
-        trh = borderRadius[1][0] < width / 2 ? borderRadius[1][0] : width / 2,
-        trv = borderRadius[1][1] < height / 2 ? borderRadius[1][1] : height / 2,
-        brh = borderRadius[2][0] < width / 2 ? borderRadius[2][0] : width / 2,
-        brv = borderRadius[2][1] < height / 2 ? borderRadius[2][1] : height / 2,
-        blh = borderRadius[3][0] < width / 2 ? borderRadius[3][0] : width / 2,
-        blv = borderRadius[3][1] < height / 2 ? borderRadius[3][1] : height / 2;
-
-    var topWidth = width - trh,
-        rightHeight = height - brv,
-        bottomWidth = width - brh,
-        leftHeight = height - blv;
-
-    return {
-        topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),
-        topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5),
-        topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),
-        topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5),
-        bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),
-        bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width - borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width),  brv - borders[2].width).bottomRight.subdivide(0.5),
-        bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),
-        bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5)
-    };
-}
-
-function bezierCurve(start, startControl, endControl, end) {
-    var lerp = function (a, b, t) {
-        return {
-            x: a.x + (b.x - a.x) * t,
-            y: a.y + (b.y - a.y) * t
-        };
-    };
-
-    return {
-        start: start,
-        startControl: startControl,
-        endControl: endControl,
-        end: end,
-        subdivide: function(t) {
-            var ab = lerp(start, startControl, t),
-                bc = lerp(startControl, endControl, t),
-                cd = lerp(endControl, end, t),
-                abbc = lerp(ab, bc, t),
-                bccd = lerp(bc, cd, t),
-                dest = lerp(abbc, bccd, t);
-            return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
-        },
-        curveTo: function(borderArgs) {
-            borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
-        },
-        curveToReversed: function(borderArgs) {
-            borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
-        }
-    };
-}
-
-function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
-    var borderArgs = [];
-
-    if (radius1[0] > 0 || radius1[1] > 0) {
-        borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
-        outer1[1].curveTo(borderArgs);
-    } else {
-        borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
-    }
-
-    if (radius2[0] > 0 || radius2[1] > 0) {
-        borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
-        outer2[0].curveTo(borderArgs);
-        borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
-        inner2[0].curveToReversed(borderArgs);
-    } else {
-        borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]);
-        borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]);
-    }
-
-    if (radius1[0] > 0 || radius1[1] > 0) {
-        borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
-        inner1[1].curveToReversed(borderArgs);
-    } else {
-        borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]);
-    }
-
-    return borderArgs;
-}
-
-function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
-    if (radius1[0] > 0 || radius1[1] > 0) {
-        borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
-        corner1[0].curveTo(borderArgs);
-        corner1[1].curveTo(borderArgs);
-    } else {
-        borderArgs.push(["line", x, y]);
-    }
-
-    if (radius2[0] > 0 || radius2[1] > 0) {
-        borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
-    }
-}
-
-function negativeZIndex(container) {
-    return container.cssInt("zIndex") < 0;
-}
-
-function positiveZIndex(container) {
-    return container.cssInt("zIndex") > 0;
-}
-
-function zIndex0(container) {
-    return container.cssInt("zIndex") === 0;
-}
-
-function inlineLevel(container) {
-    return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
-}
-
-function isStackingContext(container) {
-    return (container instanceof StackingContext);
-}
-
-function hasText(container) {
-    return container.node.data.trim().length > 0;
-}
-
-function noLetterSpacing(container) {
-    return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing")));
-}
-
-function getBorderRadiusData(container) {
-    return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
-        var value = container.css('border' + side + 'Radius');
-        var arr = value.split(" ");
-        if (arr.length <= 1) {
-            arr[1] = arr[0];
-        }
-        return arr.map(asInt);
-    });
-}
-
-function renderableNode(node) {
-    return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);
-}
-
-function isPositionedForStacking(container) {
-    var position = container.css("position");
-    var zIndex = (["absolute", "relative", "fixed"].indexOf(position) !== -1) ? container.css("zIndex") : "auto";
-    return zIndex !== "auto";
-}
-
-function isPositioned(container) {
-    return container.css("position") !== "static";
-}
-
-function isFloating(container) {
-    return container.css("float") !== "none";
-}
-
-function isInlineBlock(container) {
-    return ["inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
-}
-
-function not(callback) {
-    var context = this;
-    return function() {
-        return !callback.apply(context, arguments);
-    };
-}
-
-function isElement(container) {
-    return container.node.nodeType === Node.ELEMENT_NODE;
-}
-
-function isPseudoElement(container) {
-    return container.isPseudoElement === true;
-}
-
-function isTextNode(container) {
-    return container.node.nodeType === Node.TEXT_NODE;
-}
-
-function zIndexSort(contexts) {
-    return function(a, b) {
-        return (a.cssInt("zIndex") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt("zIndex") + (contexts.indexOf(b) / contexts.length));
-    };
-}
-
-function hasOpacity(container) {
-    return container.getOpacity() < 1;
-}
-
-function asInt(value) {
-    return parseInt(value, 10);
-}
-
-function getWidth(border) {
-    return border.width;
-}
-
-function nonIgnoredElement(nodeContainer) {
-    return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR", "OPTION"].indexOf(nodeContainer.node.nodeName) === -1);
-}
-
-function flatten(arrays) {
-    return [].concat.apply([], arrays);
-}
-
-function stripQuotes(content) {
-    var first = content.substr(0, 1);
-    return (first === content.substr(content.length - 1) && first.match(/'|"/)) ? content.substr(1, content.length - 2) : content;
-}
-
-function getWords(characters) {
-    var words = [], i = 0, onWordBoundary = false, word;
-    while(characters.length) {
-        if (isWordBoundary(characters[i]) === onWordBoundary) {
-            word = characters.splice(0, i);
-            if (word.length) {
-                words.push(punycode.ucs2.encode(word));
-            }
-            onWordBoundary =! onWordBoundary;
-            i = 0;
-        } else {
-            i++;
-        }
-
-        if (i >= characters.length) {
-            word = characters.splice(0, i);
-            if (word.length) {
-                words.push(punycode.ucs2.encode(word));
-            }
-        }
-    }
-    return words;
-}
-
-function isWordBoundary(characterCode) {
-    return [
-        32, // <space>
-        13, // \r
-        10, // \n
-        9, // \t
-        45 // -
-    ].indexOf(characterCode) !== -1;
-}
-
-function hasUnicode(string) {
-    return (/[^\u0000-\u00ff]/).test(string);
-}
-
-module.exports = NodeParser;
-
-},{"./color":5,"./fontmetrics":9,"./log":15,"./nodecontainer":16,"./promise":18,"./pseudoelementcontainer":21,"./stackingcontext":24,"./textcontainer":28,"./utils":29,"punycode":3}],18:[function(require,module,exports){
-module.exports = require('es6-promise').Promise;
-
-},{"es6-promise":1}],19:[function(require,module,exports){
-var Promise = require('./promise');
-var XHR = require('./xhr');
-var utils = require('./utils');
-var log = require('./log');
-var createWindowClone = require('./clone');
-var decode64 = utils.decode64;
-
-function Proxy(src, proxyUrl, document) {
-    var supportsCORS = ('withCredentials' in new XMLHttpRequest());
-    if (!proxyUrl) {
-        return Promise.reject("No proxy configured");
-    }
-    var callback = createCallback(supportsCORS);
-    var url = createProxyUrl(proxyUrl, src, callback);
-
-    return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) {
-        return decode64(response.content);
-    }));
-}
-var proxyCount = 0;
-
-function ProxyURL(src, proxyUrl, document) {
-    var supportsCORSImage = ('crossOrigin' in new Image());
-    var callback = createCallback(supportsCORSImage);
-    var url = createProxyUrl(proxyUrl, src, callback);
-    return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) {
-        return "data:" + response.type + ";base64," + response.content;
-    }));
-}
-
-function jsonp(document, url, callback) {
-    return new Promise(function(resolve, reject) {
-        var s = document.createElement("script");
-        var cleanup = function() {
-            delete window.html2canvas.proxy[callback];
-            document.body.removeChild(s);
-        };
-        window.html2canvas.proxy[callback] = function(response) {
-            cleanup();
-            resolve(response);
-        };
-        s.src = url;
-        s.onerror = function(e) {
-            cleanup();
-            reject(e);
-        };
-        document.body.appendChild(s);
-    });
-}
-
-function createCallback(useCORS) {
-    return !useCORS ? "html2canvas_" + Date.now() + "_" + (++proxyCount) + "_" + Math.round(Math.random() * 100000) : "";
-}
-
-function createProxyUrl(proxyUrl, src, callback) {
-    return proxyUrl + "?url=" + encodeURIComponent(src) + (callback.length ? "&callback=html2canvas.proxy." + callback : "");
-}
-
-function documentFromHTML(src) {
-    return function(html) {
-        var parser = new DOMParser(), doc;
-        try {
-            doc = parser.parseFromString(html, "text/html");
-        } catch(e) {
-            log("DOMParser not supported, falling back to createHTMLDocument");
-            doc = document.implementation.createHTMLDocument("");
-            try {
-                doc.open();
-                doc.write(html);
-                doc.close();
-            } catch(ee) {
-                log("createHTMLDocument write not supported, falling back to document.body.innerHTML");
-                doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement
-            }
-        }
-
-        var b = doc.querySelector("base");
-        if (!b || !b.href.host) {
-            var base = doc.createElement("base");
-            base.href = src;
-            doc.head.insertBefore(base, doc.head.firstChild);
-        }
-
-        return doc;
-    };
-}
-
-function loadUrlDocument(src, proxy, document, width, height, options) {
-    return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) {
-        return createWindowClone(doc, document, width, height, options, 0, 0);
-    });
-}
-
-exports.Proxy = Proxy;
-exports.ProxyURL = ProxyURL;
-exports.loadUrlDocument = loadUrlDocument;
-
-},{"./clone":4,"./log":15,"./promise":18,"./utils":29,"./xhr":31}],20:[function(require,module,exports){
-var ProxyURL = require('./proxy').ProxyURL;
-var Promise = require('./promise');
-
-function ProxyImageContainer(src, proxy) {
-    var link = document.createElement("a");
-    link.href = src;
-    src = link.href;
-    this.src = src;
-    this.image = new Image();
-    var self = this;
-    this.promise = new Promise(function(resolve, reject) {
-        self.image.crossOrigin = "Anonymous";
-        self.image.onload = resolve;
-        self.image.onerror = reject;
-
-        new ProxyURL(src, proxy, document).then(function(url) {
-            self.image.src = url;
-        })['catch'](reject);
-    });
-}
-
-module.exports = ProxyImageContainer;
-
-},{"./promise":18,"./proxy":19}],21:[function(require,module,exports){
-var NodeContainer = require('./nodecontainer');
-
-function PseudoElementContainer(node, parent, type) {
-    NodeContainer.call(this, node, parent);
-    this.isPseudoElement = true;
-    this.before = type === ":before";
-}
-
-PseudoElementContainer.prototype.cloneTo = function(stack) {
-    PseudoElementContainer.prototype.cloneTo.call(this, stack);
-    stack.isPseudoElement = true;
-    stack.before = this.before;
-};
-
-PseudoElementContainer.prototype = Object.create(NodeContainer.prototype);
-
-PseudoElementContainer.prototype.appendToDOM = function() {
-    if (this.before) {
-        this.parent.node.insertBefore(this.node, this.parent.node.firstChild);
-    } else {
-        this.parent.node.appendChild(this.node);
-    }
-    this.parent.node.className += " " + this.getHideClass();
-};
-
-PseudoElementContainer.prototype.cleanDOM = function() {
-    this.node.parentNode.removeChild(this.node);
-    this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), "");
-};
-
-PseudoElementContainer.prototype.getHideClass = function() {
-    return this["PSEUDO_HIDE_ELEMENT_CLASS_" + (this.before ? "BEFORE" : "AFTER")];
-};
-
-PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = "___html2canvas___pseudoelement_before";
-PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = "___html2canvas___pseudoelement_after";
-
-module.exports = PseudoElementContainer;
-
-},{"./nodecontainer":16}],22:[function(require,module,exports){
-var log = require('./log');
-
-function Renderer(width, height, images, options, document) {
-    this.width = width;
-    this.height = height;
-    this.images = images;
-    this.options = options;
-    this.document = document;
-}
-
-Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) {
-    var paddingLeft = container.cssInt('paddingLeft'),
-        paddingTop = container.cssInt('paddingTop'),
-        paddingRight = container.cssInt('paddingRight'),
-        paddingBottom = container.cssInt('paddingBottom'),
-        borders = borderData.borders;
-
-    var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight);
-    var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom);
-    this.drawImage(
-        imageContainer,
-        0,
-        0,
-        imageContainer.image.width || width,
-        imageContainer.image.height || height,
-        bounds.left + paddingLeft + borders[3].width,
-        bounds.top + paddingTop + borders[0].width,
-        width,
-        height
-    );
-};
-
-Renderer.prototype.renderBackground = function(container, bounds, borderData) {
-    if (bounds.height > 0 && bounds.width > 0) {
-        this.renderBackgroundColor(container, bounds);
-        this.renderBackgroundImage(container, bounds, borderData);
-    }
-};
-
-Renderer.prototype.renderBackgroundColor = function(container, bounds) {
-    var color = container.color("backgroundColor");
-    if (!color.isTransparent()) {
-        this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color);
-    }
-};
-
-Renderer.prototype.renderBorders = function(borders) {
-    borders.forEach(this.renderBorder, this);
-};
-
-Renderer.prototype.renderBorder = function(data) {
-    if (!data.color.isTransparent() && data.args !== null) {
-        this.drawShape(data.args, data.color);
-    }
-};
-
-Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) {
-    var backgroundImages = container.parseBackgroundImages();
-    backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {
-        switch(backgroundImage.method) {
-        case "url":
-            var image = this.images.get(backgroundImage.args[0]);
-            if (image) {
-                this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData);
-            } else {
-                log("Error loading background-image", backgroundImage.args[0]);
-            }
-            break;
-        case "linear-gradient":
-        case "gradient":
-            var gradientImage = this.images.get(backgroundImage.value);
-            if (gradientImage) {
-                this.renderBackgroundGradient(gradientImage, bounds, borderData);
-            } else {
-                log("Error loading background-image", backgroundImage.args[0]);
-            }
-            break;
-        case "none":
-            break;
-        default:
-            log("Unknown background-image type", backgroundImage.args[0]);
-        }
-    }, this);
-};
-
-Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) {
-    var size = container.parseBackgroundSize(bounds, imageContainer.image, index);
-    var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size);
-    var repeat = container.parseBackgroundRepeat(index);
-    switch (repeat) {
-    case "repeat-x":
-    case "repeat no-repeat":
-        this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData);
-        break;
-    case "repeat-y":
-    case "no-repeat repeat":
-        this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData);
-        break;
-    case "no-repeat":
-        this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData);
-        break;
-    default:
-        this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]);
-        break;
-    }
-};
-
-module.exports = Renderer;
-
-},{"./log":15}],23:[function(require,module,exports){
-var Renderer = require('../renderer');
-var LinearGradientContainer = require('../lineargradientcontainer');
-var log = require('../log');
-
-function CanvasRenderer(width, height) {
-    Renderer.apply(this, arguments);
-    this.canvas = this.options.canvas || this.document.createElement("canvas");
-    if (!this.options.canvas) {
-        this.canvas.width = width;
-        this.canvas.height = height;
-    }
-    this.ctx = this.canvas.getContext("2d");
-    this.taintCtx = this.document.createElement("canvas").getContext("2d");
-    this.ctx.textBaseline = "bottom";
-    this.variables = {};
-    log("Initialized CanvasRenderer with size", width, "x", height);
-}
-
-CanvasRenderer.prototype = Object.create(Renderer.prototype);
-
-CanvasRenderer.prototype.setFillStyle = function(fillStyle) {
-    this.ctx.fillStyle = typeof(fillStyle) === "object" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle;
-    return this.ctx;
-};
-
-CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) {
-    this.setFillStyle(color).fillRect(left, top, width, height);
-};
-
-CanvasRenderer.prototype.circle = function(left, top, size, color) {
-    this.setFillStyle(color);
-    this.ctx.beginPath();
-    this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true);
-    this.ctx.closePath();
-    this.ctx.fill();
-};
-
-CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) {
-    this.circle(left, top, size, color);
-    this.ctx.strokeStyle = strokeColor.toString();
-    this.ctx.stroke();
-};
-
-CanvasRenderer.prototype.drawShape = function(shape, color) {
-    this.shape(shape);
-    this.setFillStyle(color).fill();
-};
-
-CanvasRenderer.prototype.taints = function(imageContainer) {
-    if (imageContainer.tainted === null) {
-        this.taintCtx.drawImage(imageContainer.image, 0, 0);
-        try {
-            this.taintCtx.getImageData(0, 0, 1, 1);
-            imageContainer.tainted = false;
-        } catch(e) {
-            this.taintCtx = document.createElement("canvas").getContext("2d");
-            imageContainer.tainted = true;
-        }
-    }
-
-    return imageContainer.tainted;
-};
-
-CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) {
-    if (!this.taints(imageContainer) || this.options.allowTaint) {
-        this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh);
-    }
-};
-
-CanvasRenderer.prototype.clip = function(shapes, callback, context) {
-    this.ctx.save();
-    shapes.filter(hasEntries).forEach(function(shape) {
-        this.shape(shape).clip();
-    }, this);
-    callback.call(context);
-    this.ctx.restore();
-};
-
-CanvasRenderer.prototype.shape = function(shape) {
-    this.ctx.beginPath();
-    shape.forEach(function(point, index) {
-        if (point[0] === "rect") {
-            this.ctx.rect.apply(this.ctx, point.slice(1));
-        } else {
-            this.ctx[(index === 0) ? "moveTo" : point[0] + "To" ].apply(this.ctx, point.slice(1));
-        }
-    }, this);
-    this.ctx.closePath();
-    return this.ctx;
-};
-
-CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) {
-    this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ").split(",")[0];
-};
-
-CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) {
-    this.setVariable("shadowColor", color.toString())
-        .setVariable("shadowOffsetY", offsetX)
-        .setVariable("shadowOffsetX", offsetY)
-        .setVariable("shadowBlur", blur);
-};
-
-CanvasRenderer.prototype.clearShadow = function() {
-    this.setVariable("shadowColor", "rgba(0,0,0,0)");
-};
-
-CanvasRenderer.prototype.setOpacity = function(opacity) {
-    this.ctx.globalAlpha = opacity;
-};
-
-CanvasRenderer.prototype.setTransform = function(transform) {
-    this.ctx.translate(transform.origin[0], transform.origin[1]);
-    this.ctx.transform.apply(this.ctx, transform.matrix);
-    this.ctx.translate(-transform.origin[0], -transform.origin[1]);
-};
-
-CanvasRenderer.prototype.setVariable = function(property, value) {
-    if (this.variables[property] !== value) {
-        this.variables[property] = this.ctx[property] = value;
-    }
-
-    return this;
-};
-
-CanvasRenderer.prototype.text = function(text, left, bottom) {
-    this.ctx.fillText(text, left, bottom);
-};
-
-CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) {
-    var shape = [
-        ["line", Math.round(left), Math.round(top)],
-        ["line", Math.round(left + width), Math.round(top)],
-        ["line", Math.round(left + width), Math.round(height + top)],
-        ["line", Math.round(left), Math.round(height + top)]
-    ];
-    this.clip([shape], function() {
-        this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);
-    }, this);
-};
-
-CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) {
-    var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop);
-    this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), "repeat"));
-    this.ctx.translate(offsetX, offsetY);
-    this.ctx.fill();
-    this.ctx.translate(-offsetX, -offsetY);
-};
-
-CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) {
-    if (gradientImage instanceof LinearGradientContainer) {
-        var gradient = this.ctx.createLinearGradient(
-            bounds.left + bounds.width * gradientImage.x0,
-            bounds.top + bounds.height * gradientImage.y0,
-            bounds.left +  bounds.width * gradientImage.x1,
-            bounds.top +  bounds.height * gradientImage.y1);
-        gradientImage.colorStops.forEach(function(colorStop) {
-            gradient.addColorStop(colorStop.stop, colorStop.color.toString());
-        });
-        this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient);
-    }
-};
-
-CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
-    var image = imageContainer.image;
-    if(image.width === size.width && image.height === size.height) {
-        return image;
-    }
-
-    var ctx, canvas = document.createElement('canvas');
-    canvas.width = size.width;
-    canvas.height = size.height;
-    ctx = canvas.getContext("2d");
-    ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height );
-    return canvas;
-};
-
-function hasEntries(array) {
-    return array.length > 0;
-}
-
-module.exports = CanvasRenderer;
-
-},{"../lineargradientcontainer":14,"../log":15,"../renderer":22}],24:[function(require,module,exports){
-var NodeContainer = require('./nodecontainer');
-
-function StackingContext(hasOwnStacking, opacity, element, parent) {
-    NodeContainer.call(this, element, parent);
-    this.ownStacking = hasOwnStacking;
-    this.contexts = [];
-    this.children = [];
-    this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;
-}
-
-StackingContext.prototype = Object.create(NodeContainer.prototype);
-
-StackingContext.prototype.getParentStack = function(context) {
-    var parentStack = (this.parent) ? this.parent.stack : null;
-    return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;
-};
-
-module.exports = StackingContext;
-
-},{"./nodecontainer":16}],25:[function(require,module,exports){
-function Support(document) {
-    this.rangeBounds = this.testRangeBounds(document);
-    this.cors = this.testCORS();
-    this.svg = this.testSVG();
-}
-
-Support.prototype.testRangeBounds = function(document) {
-    var range, testElement, rangeBounds, rangeHeight, support = false;
-
-    if (document.createRange) {
-        range = document.createRange();
-        if (range.getBoundingClientRect) {
-            testElement = document.createElement('boundtest');
-            testElement.style.height = "123px";
-            testElement.style.display = "block";
-            document.body.appendChild(testElement);
-
-            range.selectNode(testElement);
-            rangeBounds = range.getBoundingClientRect();
-            rangeHeight = rangeBounds.height;
-
-            if (rangeHeight === 123) {
-                support = true;
-            }
-            document.body.removeChild(testElement);
-        }
-    }
-
-    return support;
-};
-
-Support.prototype.testCORS = function() {
-    return typeof((new Image()).crossOrigin) !== "undefined";
-};
-
-Support.prototype.testSVG = function() {
-    var img = new Image();
-    var canvas = document.createElement("canvas");
-    var ctx =  canvas.getContext("2d");
-    img.src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";
-
-    try {
-        ctx.drawImage(img, 0, 0);
-        canvas.toDataURL();
-    } catch(e) {
-        return false;
-    }
-    return true;
-};
-
-module.exports = Support;
-
-},{}],26:[function(require,module,exports){
-var Promise = require('./promise');
-var XHR = require('./xhr');
-var decode64 = require('./utils').decode64;
-
-function SVGContainer(src) {
-    this.src = src;
-    this.image = null;
-    var self = this;
-
-    this.promise = this.hasFabric().then(function() {
-        return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src));
-    }).then(function(svg) {
-        return new Promise(function(resolve) {
-            window.html2canvas.svg.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve));
-        });
-    });
-}
-
-SVGContainer.prototype.hasFabric = function() {
-    return !window.html2canvas.svg || !window.html2canvas.svg.fabric ? Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg")) : Promise.resolve();
-};
-
-SVGContainer.prototype.inlineFormatting = function(src) {
-    return (/^data:image\/svg\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src);
-};
-
-SVGContainer.prototype.removeContentType = function(src) {
-    return src.replace(/^data:image\/svg\+xml(;base64)?,/,'');
-};
-
-SVGContainer.prototype.isInline = function(src) {
-    return (/^data:image\/svg\+xml/i.test(src));
-};
-
-SVGContainer.prototype.createCanvas = function(resolve) {
-    var self = this;
-    return function (objects, options) {
-        var canvas = new window.html2canvas.svg.fabric.StaticCanvas('c');
-        self.image = canvas.lowerCanvasEl;
-        canvas
-            .setWidth(options.width)
-            .setHeight(options.height)
-            .add(window.html2canvas.svg.fabric.util.groupSVGElements(objects, options))
-            .renderAll();
-        resolve(canvas.lowerCanvasEl);
-    };
-};
-
-SVGContainer.prototype.decode64 = function(str) {
-    return (typeof(window.atob) === "function") ? window.atob(str) : decode64(str);
-};
-
-module.exports = SVGContainer;
-
-},{"./promise":18,"./utils":29,"./xhr":31}],27:[function(require,module,exports){
-var SVGContainer = require('./svgcontainer');
-var Promise = require('./promise');
-
-function SVGNodeContainer(node, _native) {
-    this.src = node;
-    this.image = null;
-    var self = this;
-
-    this.promise = _native ? new Promise(function(resolve, reject) {
-        self.image = new Image();
-        self.image.onload = resolve;
-        self.image.onerror = reject;
-        self.image.src = "data:image/svg+xml," + (new XMLSerializer()).serializeToString(node);
-        if (self.image.complete === true) {
-            resolve(self.image);
-        }
-    }) : this.hasFabric().then(function() {
-        return new Promise(function(resolve) {
-            window.html2canvas.svg.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve));
-        });
-    });
-}
-
-SVGNodeContainer.prototype = Object.create(SVGContainer.prototype);
-
-module.exports = SVGNodeContainer;
-
-},{"./promise":18,"./svgcontainer":26}],28:[function(require,module,exports){
-var NodeContainer = require('./nodecontainer');
-
-function TextContainer(node, parent) {
-    NodeContainer.call(this, node, parent);
-}
-
-TextContainer.prototype = Object.create(NodeContainer.prototype);
-
-TextContainer.prototype.applyTextTransform = function() {
-    this.node.data = this.transform(this.parent.css("textTransform"));
-};
-
-TextContainer.prototype.transform = function(transform) {
-    var text = this.node.data;
-    switch(transform){
-        case "lowercase":
-            return text.toLowerCase();
-        case "capitalize":
-            return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize);
-        case "uppercase":
-            return text.toUpperCase();
-        default:
-            return text;
-    }
-};
-
-function capitalize(m, p1, p2) {
-    if (m.length > 0) {
-        return p1 + p2.toUpperCase();
-    }
-}
-
-module.exports = TextContainer;
-
-},{"./nodecontainer":16}],29:[function(require,module,exports){
-exports.smallImage = function smallImage() {
-    return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
-};
-
-exports.bind = function(callback, context) {
-    return function() {
-        return callback.apply(context, arguments);
-    };
-};
-
-/*
- * base64-arraybuffer
- * https://github.com/niklasvh/base64-arraybuffer
- *
- * Copyright (c) 2012 Niklas von Hertzen
- * Licensed under the MIT license.
- */
-
-exports.decode64 = function(base64) {
-    var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-    var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3;
-
-    var output = "";
-
-    for (i = 0; i < len; i+=4) {
-        encoded1 = chars.indexOf(base64[i]);
-        encoded2 = chars.indexOf(base64[i+1]);
-        encoded3 = chars.indexOf(base64[i+2]);
-        encoded4 = chars.indexOf(base64[i+3]);
-
-        byte1 = (encoded1 << 2) | (encoded2 >> 4);
-        byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);
-        byte3 = ((encoded3 & 3) << 6) | encoded4;
-        if (encoded3 === 64) {
-            output += String.fromCharCode(byte1);
-        } else if (encoded4 === 64 || encoded4 === -1) {
-            output += String.fromCharCode(byte1, byte2);
-        } else{
-            output += String.fromCharCode(byte1, byte2, byte3);
-        }
-    }
-
-    return output;
-};
-
-exports.getBounds = function(node) {
-    if (node.getBoundingClientRect) {
-        var clientRect = node.getBoundingClientRect();
-        var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth;
-        return {
-            top: clientRect.top,
-            bottom: clientRect.bottom || (clientRect.top + clientRect.height),
-            right: clientRect.left + width,
-            left: clientRect.left,
-            width:  width,
-            height: node.offsetHeight == null ? clientRect.height : node.offsetHeight
-        };
-    }
-    return {};
-};
-
-exports.offsetBounds = function(node) {
-    var parent = node.offsetParent ? exports.offsetBounds(node.offsetParent) : {top: 0, left: 0};
-
-    return {
-        top: node.offsetTop + parent.top,
-        bottom: node.offsetTop + node.offsetHeight + parent.top,
-        right: node.offsetLeft + parent.left + node.offsetWidth,
-        left: node.offsetLeft + parent.left,
-        width: node.offsetWidth,
-        height: node.offsetHeight
-    };
-};
-
-exports.parseBackgrounds = function(backgroundImage) {
-    var whitespace = ' \r\n\t',
-        method, definition, prefix, prefix_i, block, results = [],
-        mode = 0, numParen = 0, quote, args;
-    var appendResult = function() {
-        if(method) {
-            if (definition.substr(0, 1) === '"') {
-                definition = definition.substr(1, definition.length - 2);
-            }
-            if (definition) {
-                args.push(definition);
-            }
-            if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) {
-                prefix = method.substr(0, prefix_i);
-                method = method.substr(prefix_i);
-            }
-            results.push({
-                prefix: prefix,
-                method: method.toLowerCase(),
-                value: block,
-                args: args,
-                image: null
-            });
-        }
-        args = [];
-        method = prefix = definition = block = '';
-    };
-    args = [];
-    method = prefix = definition = block = '';
-    backgroundImage.split("").forEach(function(c) {
-        if (mode === 0 && whitespace.indexOf(c) > -1) {
-            return;
-        }
-        switch(c) {
-        case '"':
-            if(!quote) {
-                quote = c;
-            } else if(quote === c) {
-                quote = null;
-            }
-            break;
-        case '(':
-            if(quote) {
-                break;
-            } else if(mode === 0) {
-                mode = 1;
-                block += c;
-                return;
-            } else {
-                numParen++;
-            }
-            break;
-        case ')':
-            if (quote) {
-                break;
-            } else if(mode === 1) {
-                if(numParen === 0) {
-                    mode = 0;
-                    block += c;
-                    appendResult();
-                    return;
-                } else {
-                    numParen--;
-                }
-            }
-            break;
-
-        case ',':
-            if (quote) {
-                break;
-            } else if(mode === 0) {
-                appendResult();
-                return;
-            } else if (mode === 1) {
-                if (numParen === 0 && !method.match(/^url$/i)) {
-                    args.push(definition);
-                    definition = '';
-                    block += c;
-                    return;
-                }
-            }
-            break;
-        }
-
-        block += c;
-        if (mode === 0) {
-            method += c;
-        } else {
-            definition += c;
-        }
-    });
-
-    appendResult();
-    return results;
-};
-
-},{}],30:[function(require,module,exports){
-var GradientContainer = require('./gradientcontainer');
-
-function WebkitGradientContainer(imageData) {
-    GradientContainer.apply(this, arguments);
-    this.type = (imageData.args[0] === "linear") ? this.TYPES.LINEAR : this.TYPES.RADIAL;
-}
-
-WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
-
-module.exports = WebkitGradientContainer;
-
-},{"./gradientcontainer":11}],31:[function(require,module,exports){
-var Promise = require('./promise');
-
-function XHR(url) {
-    return new Promise(function(resolve, reject) {
-        var xhr = new XMLHttpRequest();
-        xhr.open('GET', url);
-
-        xhr.onload = function() {
-            if (xhr.status === 200) {
-                resolve(xhr.responseText);
-            } else {
-                reject(new Error(xhr.statusText));
-            }
-        };
-
-        xhr.onerror = function() {
-            reject(new Error("Network Error"));
-        };
-
-        xhr.send();
-    });
-}
-
-module.exports = XHR;
-
-},{"./promise":18}]},{},[6])(6)
-});
\ No newline at end of file
diff --git a/dist/html2canvas.min.js b/dist/html2canvas.min.js
deleted file mode 100644
index 215b546..0000000
--- a/dist/html2canvas.min.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
-  html2canvas 0.5.0-alpha2 <http://html2canvas.hertzen.com>
-  Copyright (c) 2015 Niklas von Hertzen
-
-  Released under MIT License
-*/
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),n.html2canvas=e()}}(function(){var e;return function n(e,f,o){function d(t,l){if(!f[t]){if(!e[t]){var s="function"==typeof require&&require;if(!l&&s)return s(t,!0);if(i)return i(t,!0);var u=new Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}var a=f[t]={exports:{}};e[t][0].call(a.exports,function(n){var f=e[t][1][n];return d(f?f:n)},a,a.exports,n,e,f,o)}return f[t].exports}for(var i="function"==typeof require&&require,t=0;t<o.length;t++)d(o[t]);return d}({1:[function(n,f){(function(n,o){(function(){"use strict";function d(e){return"function"==typeof e||"object"==typeof e&&null!==e}function i(e){return"function"==typeof e}function t(e){return"object"==typeof e&&null!==e}function l(){}function s(){return function(){n.nextTick(c)}}function u(){var e=0,n=new P(c),f=document.createTextNode("");return n.observe(f,{characterData:!0}),function(){f.data=e=++e%2}}function a(){var e=new MessageChannel;return e.port1.onmessage=c,function(){e.port2.postMessage(0)}}function p(){return function(){setTimeout(c,1)}}function c(){for(var e=0;M>e;e+=2){var n=R[e],f=R[e+1];n(f),R[e]=void 0,R[e+1]=void 0}M=0}function y(){}function m(){return new TypeError("You cannot resolve a promise with itself")}function r(){return new TypeError("A promises callback cannot return that same promise.")}function v(e){try{return e.then}catch(n){return V.error=n,V}}function w(e,n,f,o){try{e.call(n,f,o)}catch(d){return d}}function b(e,n,f){N(function(e){var o=!1,d=w(f,n,function(f){o||(o=!0,n!==f?x(e,f):k(e,f))},function(n){o||(o=!0,q(e,n))},"Settle: "+(e._label||" unknown promise"));!o&&d&&(o=!0,q(e,d))},e)}function g(e,n){n._state===T?k(e,n._result):e._state===U?q(e,n._result):z(n,void 0,function(n){x(e,n)},function(n){q(e,n)})}function h(e,n){if(n.constructor===e.constructor)g(e,n);else{var f=v(n);f===V?q(e,V.error):void 0===f?k(e,n):i(f)?b(e,n,f):k(e,n)}}function x(e,n){e===n?q(e,m()):d(n)?h(e,n):k(e,n)}function j(e){e._onerror&&e._onerror(e._result),A(e)}function k(e,n){e._state===S&&(e._result=n,e._state=T,0===e._subscribers.length||N(A,e))}function q(e,n){e._state===S&&(e._state=U,e._result=n,N(j,e))}function z(e,n,f,o){var d=e._subscribers,i=d.length;e._onerror=null,d[i]=n,d[i+T]=f,d[i+U]=o,0===i&&e._state&&N(A,e)}function A(e){var n=e._subscribers,f=e._state;if(0!==n.length){for(var o,d,i=e._result,t=0;t<n.length;t+=3)o=n[t],d=n[t+f],o?D(f,o,d,i):d(i);e._subscribers.length=0}}function B(){this.error=null}function C(e,n){try{return e(n)}catch(f){return W.error=f,W}}function D(e,n,f,o){var d,t,l,s,u=i(f);if(u){if(d=C(f,o),d===W?(s=!0,t=d.error,d=null):l=!0,n===d)return void q(n,r())}else d=o,l=!0;n._state!==S||(u&&l?x(n,d):s?q(n,t):e===T?k(n,d):e===U&&q(n,d))}function E(e,n){try{n(function(n){x(e,n)},function(n){q(e,n)})}catch(f){q(e,f)}}function F(e,n,f,o){this._instanceConstructor=e,this.promise=new e(y,o),this._abortOnReject=f,this._validateInput(n)?(this._input=n,this.length=n.length,this._remaining=n.length,this._init(),0===this.length?k(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&k(this.promise,this._result))):q(this.promise,this._validationError())}function G(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function H(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function I(e){this._id=en++,this._state=void 0,this._result=void 0,this._subscribers=[],y!==e&&(i(e)||G(),this instanceof I||H(),E(this,e))}var J;J=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var K,L=J,M=(Date.now||function(){return(new Date).getTime()},Object.create||function(e){if(arguments.length>1)throw new Error("Second argument not supported");if("object"!=typeof e)throw new TypeError("Argument must be an object");return l.prototype=e,new l},0),N=function(e,n){R[M]=e,R[M+1]=n,M+=2,2===M&&K()},O="undefined"!=typeof window?window:{},P=O.MutationObserver||O.WebKitMutationObserver,Q="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,R=new Array(1e3);K="undefined"!=typeof n&&"[object process]"==={}.toString.call(n)?s():P?u():Q?a():p();var S=void 0,T=1,U=2,V=new B,W=new B;F.prototype._validateInput=function(e){return L(e)},F.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},F.prototype._init=function(){this._result=new Array(this.length)};var X=F;F.prototype._enumerate=function(){for(var e=this.length,n=this.promise,f=this._input,o=0;n._state===S&&e>o;o++)this._eachEntry(f[o],o)},F.prototype._eachEntry=function(e,n){var f=this._instanceConstructor;t(e)?e.constructor===f&&e._state!==S?(e._onerror=null,this._settledAt(e._state,n,e._result)):this._willSettleAt(f.resolve(e),n):(this._remaining--,this._result[n]=this._makeResult(T,n,e))},F.prototype._settledAt=function(e,n,f){var o=this.promise;o._state===S&&(this._remaining--,this._abortOnReject&&e===U?q(o,f):this._result[n]=this._makeResult(e,n,f)),0===this._remaining&&k(o,this._result)},F.prototype._makeResult=function(e,n,f){return f},F.prototype._willSettleAt=function(e,n){var f=this;z(e,void 0,function(e){f._settledAt(T,n,e)},function(e){f._settledAt(U,n,e)})};var Y=function(e,n){return new X(this,e,!0,n).promise},Z=function(e,n){function f(e){x(i,e)}function o(e){q(i,e)}var d=this,i=new d(y,n);if(!L(e))return q(i,new TypeError("You must pass an array to race.")),i;for(var t=e.length,l=0;i._state===S&&t>l;l++)z(d.resolve(e[l]),void 0,f,o);return i},$=function(e,n){var f=this;if(e&&"object"==typeof e&&e.constructor===f)return e;var o=new f(y,n);return x(o,e),o},_=function(e,n){var f=this,o=new f(y,n);return q(o,e),o},en=0,nn=I;I.all=Y,I.race=Z,I.resolve=$,I.reject=_,I.prototype={constructor:I,then:function(e,n){var f=this,o=f._state;if(o===T&&!e||o===U&&!n)return this;var d=new this.constructor(y),i=f._result;if(o){var t=arguments[o-1];N(function(){D(o,d,t,i)})}else z(f,d,e,n);return d},"catch":function(e){return this.then(null,e)}};var fn=function(){var e;e="undefined"!=typeof o?o:"undefined"!=typeof window&&window.document?window:self;var n="Promise"in e&&"resolve"in e.Promise&&"reject"in e.Promise&&"all"in e.Promise&&"race"in e.Promise&&function(){var n;return new e.Promise(function(e){n=e}),i(n)}();n||(e.Promise=nn)},on={Promise:nn,polyfill:fn};"function"==typeof e&&e.amd?e(function(){return on}):"undefined"!=typeof f&&f.exports?f.exports=on:"undefined"!=typeof this&&(this.ES6Promise=on)}).call(this)}).call(this,n("_process"),"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{_process:2}],2:[function(e,n){function f(){if(!t){t=!0;for(var e,n=i.length;n;){e=i,i=[];for(var f=-1;++f<n;)e[f]();n=i.length}t=!1}}function o(){}var d=n.exports={},i=[],t=!1;d.nextTick=function(e){i.push(e),t||setTimeout(f,0)},d.title="browser",d.browser=!0,d.env={},d.argv=[],d.version="",d.on=o,d.addListener=o,d.once=o,d.off=o,d.removeListener=o,d.removeAllListeners=o,d.emit=o,d.binding=function(){throw new Error("process.binding is not supported")},d.cwd=function(){return"/"},d.chdir=function(){throw new Error("process.chdir is not supported")},d.umask=function(){return 0}},{}],3:[function(n,f,o){(function(n){!function(d){function i(e){throw RangeError(I[e])}function t(e,n){for(var f=e.length;f--;)e[f]=n(e[f]);return e}function l(e,n){return t(e.split(H),n).join(".")}function s(e){for(var n,f,o=[],d=0,i=e.length;i>d;)n=e.charCodeAt(d++),n>=55296&&56319>=n&&i>d?(f=e.charCodeAt(d++),56320==(64512&f)?o.push(((1023&n)<<10)+(1023&f)+65536):(o.push(n),d--)):o.push(n);return o}function u(e){return t(e,function(e){var n="";return e>65535&&(e-=65536,n+=L(e>>>10&1023|55296),e=56320|1023&e),n+=L(e)}).join("")}function a(e){return 10>e-48?e-22:26>e-65?e-65:26>e-97?e-97:k}function p(e,n){return e+22+75*(26>e)-((0!=n)<<5)}function c(e,n,f){var o=0;for(e=f?K(e/B):e>>1,e+=K(e/n);e>J*z>>1;o+=k)e=K(e/J);return K(o+(J+1)*e/(e+A))}function y(e){var n,f,o,d,t,l,s,p,y,m,r=[],v=e.length,w=0,b=D,g=C;for(f=e.lastIndexOf(E),0>f&&(f=0),o=0;f>o;++o)e.charCodeAt(o)>=128&&i("not-basic"),r.push(e.charCodeAt(o));for(d=f>0?f+1:0;v>d;){for(t=w,l=1,s=k;d>=v&&i("invalid-input"),p=a(e.charCodeAt(d++)),(p>=k||p>K((j-w)/l))&&i("overflow"),w+=p*l,y=g>=s?q:s>=g+z?z:s-g,!(y>p);s+=k)m=k-y,l>K(j/m)&&i("overflow"),l*=m;n=r.length+1,g=c(w-t,n,0==t),K(w/n)>j-b&&i("overflow"),b+=K(w/n),w%=n,r.splice(w++,0,b)}return u(r)}function m(e){var n,f,o,d,t,l,u,a,y,m,r,v,w,b,g,h=[];for(e=s(e),v=e.length,n=D,f=0,t=C,l=0;v>l;++l)r=e[l],128>r&&h.push(L(r));for(o=d=h.length,d&&h.push(E);v>o;){for(u=j,l=0;v>l;++l)r=e[l],r>=n&&u>r&&(u=r);for(w=o+1,u-n>K((j-f)/w)&&i("overflow"),f+=(u-n)*w,n=u,l=0;v>l;++l)if(r=e[l],n>r&&++f>j&&i("overflow"),r==n){for(a=f,y=k;m=t>=y?q:y>=t+z?z:y-t,!(m>a);y+=k)g=a-m,b=k-m,h.push(L(p(m+g%b,0))),a=K(g/b);h.push(L(p(a,0))),t=c(f,w,o==d),f=0,++o}++f,++n}return h.join("")}function r(e){return l(e,function(e){return F.test(e)?y(e.slice(4).toLowerCase()):e})}function v(e){return l(e,function(e){return G.test(e)?"xn--"+m(e):e})}var w="object"==typeof o&&o,b="object"==typeof f&&f&&f.exports==w&&f,g="object"==typeof n&&n;(g.global===g||g.window===g)&&(d=g);var h,x,j=2147483647,k=36,q=1,z=26,A=38,B=700,C=72,D=128,E="-",F=/^xn--/,G=/[^ -~]/,H=/\x2E|\u3002|\uFF0E|\uFF61/g,I={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},J=k-q,K=Math.floor,L=String.fromCharCode;if(h={version:"1.2.4",ucs2:{decode:s,encode:u},decode:y,encode:m,toASCII:v,toUnicode:r},"function"==typeof e&&"object"==typeof e.amd&&e.amd)e("punycode",function(){return h});else if(w&&!w.nodeType)if(b)b.exports=h;else for(x in h)h.hasOwnProperty(x)&&(w[x]=h[x]);else d.punycode=h}(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],4:[function(e,n){function f(e,n,f){!e.defaultView||n===e.defaultView.pageXOffset&&f===e.defaultView.pageYOffset||e.defaultView.scrollTo(n,f)}function o(e,n){try{n&&(n.width=e.width,n.height=e.height,n.getContext("2d").putImageData(e.getContext("2d").getImageData(0,0,e.width,e.height),0,0))}catch(f){t("Unable to copy canvas content from",e,f)}}function d(e,n){for(var f=3===e.nodeType?document.createTextNode(e.nodeValue):e.cloneNode(!1),i=e.firstChild;i;)(n===!0||1!==i.nodeType||"SCRIPT"!==i.nodeName)&&f.appendChild(d(i,n)),i=i.nextSibling;return 1===e.nodeType&&(f._scrollTop=e.scrollTop,f._scrollLeft=e.scrollLeft,"CANVAS"===e.nodeName?o(e,f):("TEXTAREA"===e.nodeName||"SELECT"===e.nodeName)&&(f.value=e.value)),f}function i(e){if(1===e.nodeType){e.scrollTop=e._scrollTop,e.scrollLeft=e._scrollLeft;for(var n=e.firstChild;n;)i(n),n=n.nextSibling}}var t=e("./log"),l=e("./promise");n.exports=function(e,n,o,t,s,u,a){var p=d(e.documentElement,s.javascriptEnabled),c=n.createElement("iframe");return c.className="html2canvas-container",c.style.visibility="hidden",c.style.position="fixed",c.style.left="-10000px",c.style.top="0px",c.style.border="0",c.width=o,c.height=t,c.scrolling="no",n.body.appendChild(c),new l(function(n){var o=c.contentWindow.document;c.contentWindow.onload=c.onload=function(){var e=setInterval(function(){o.body.childNodes.length>0&&(i(o.documentElement),clearInterval(e),"view"===s.type&&(c.contentWindow.scrollTo(u,a),!/(iPad|iPhone|iPod)/g.test(navigator.userAgent)||c.contentWindow.scrollY===a&&c.contentWindow.scrollX===u||(o.documentElement.style.top=-a+"px",o.documentElement.style.left=-u+"px",o.documentElement.style.position="absolute")),n(c))},50)},o.open(),o.write("<!DOCTYPE html><html></html>"),f(e,u,a),o.replaceChild(o.adoptNode(p),o.documentElement),o.close()})}},{"./log":15,"./promise":18}],5:[function(e,n){function f(e){this.r=0,this.g=0,this.b=0,this.a=null;this.fromArray(e)||this.namedColor(e)||this.rgb(e)||this.rgba(e)||this.hex6(e)||this.hex3(e)}f.prototype.darken=function(e){var n=1-e;return new f([Math.round(this.r*n),Math.round(this.g*n),Math.round(this.b*n),this.a])},f.prototype.isTransparent=function(){return 0===this.a},f.prototype.isBlack=function(){return 0===this.r&&0===this.g&&0===this.b},f.prototype.fromArray=function(e){return Array.isArray(e)&&(this.r=Math.min(e[0],255),this.g=Math.min(e[1],255),this.b=Math.min(e[2],255),e.length>3&&(this.a=e[3])),Array.isArray(e)};var o=/^#([a-f0-9]{3})$/i;f.prototype.hex3=function(e){var n=null;return null!==(n=e.match(o))&&(this.r=parseInt(n[1][0]+n[1][0],16),this.g=parseInt(n[1][1]+n[1][1],16),this.b=parseInt(n[1][2]+n[1][2],16)),null!==n};var d=/^#([a-f0-9]{6})$/i;f.prototype.hex6=function(e){var n=null;return null!==(n=e.match(d))&&(this.r=parseInt(n[1].substring(0,2),16),this.g=parseInt(n[1].substring(2,4),16),this.b=parseInt(n[1].substring(4,6),16)),null!==n};var i=/^rgb\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3})\)$/;f.prototype.rgb=function(e){var n=null;return null!==(n=e.match(i))&&(this.r=Number(n[1]),this.g=Number(n[2]),this.b=Number(n[3])),null!==n};var t=/^rgba\((\d{1,3}) *, *(\d{1,3}) *, *(\d{1,3}) *, *(\d+\.?\d*)\)$/;f.prototype.rgba=function(e){var n=null;return null!==(n=e.match(t))&&(this.r=Number(n[1]),this.g=Number(n[2]),this.b=Number(n[3]),this.a=Number(n[4])),null!==n},f.prototype.toString=function(){return null!==this.a&&1!==this.a?"rgba("+[this.r,this.g,this.b,this.a].join(",")+")":"rgb("+[this.r,this.g,this.b].join(",")+")"},f.prototype.namedColor=function(e){var n=l[e.toLowerCase()];if(n)this.r=n[0],this.g=n[1],this.b=n[2];else if("transparent"===e.toLowerCase())return this.r=this.g=this.b=this.a=0,!0;return!!n},f.prototype.isColor=!0;var l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]};n.exports=f},{}],6:[function(n,f){function o(e,n){var f=k++;if(n=n||{},n.logging&&(window.html2canvas.logging=!0,window.html2canvas.start=Date.now()),n.async="undefined"==typeof n.async?!0:n.async,n.allowTaint="undefined"==typeof n.allowTaint?!1:n.allowTaint,n.removeContainer="undefined"==typeof n.removeContainer?!0:n.removeContainer,n.javascriptEnabled="undefined"==typeof n.javascriptEnabled?!1:n.javascriptEnabled,n.imageTimeout="undefined"==typeof n.imageTimeout?1e4:n.imageTimeout,n.renderer="function"==typeof n.renderer?n.renderer:y,n.strict=!!n.strict,"string"==typeof e){if("string"!=typeof n.proxy)return p.reject("Proxy must be used when rendering url");var o=null!=n.width?n.width:window.innerWidth,t=null!=n.height?n.height:window.innerHeight;return h(a(e),n.proxy,document,o,t,n).then(function(e){return i(e.contentWindow.document.documentElement,e,n,o,t)})}var l=(void 0===e?[document.documentElement]:e.length?e:[e])[0];return l.setAttribute(j+f,f),d(l.ownerDocument,n,l.ownerDocument.defaultView.innerWidth,l.ownerDocument.defaultView.innerHeight,f).then(function(e){return"function"==typeof n.onrendered&&(w("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"),n.onrendered(e)),e})}function d(e,n,f,o,d){return g(e,e,f,o,n,e.defaultView.pageXOffset,e.defaultView.pageYOffset).then(function(t){w("Document cloned");var l=j+d,s="["+l+"='"+d+"']";e.querySelector(s).removeAttribute(l);var u=t.contentWindow,a=u.document.querySelector(s),c=p.resolve("function"==typeof n.onclone?n.onclone(u.document):!0);return c.then(function(){return i(a,t,n,f,o)})})}function i(e,n,f,o,d){var i=n.contentWindow,a=new c(i.document),p=new m(f,a),y=x(e),v="view"===f.type?o:s(i.document),b="view"===f.type?d:u(i.document),g=new f.renderer(v,b,p,f,document),h=new r(e,g,a,p,f);return h.ready.then(function(){w("Finished rendering");var o;return o="view"===f.type?l(g.canvas,{width:g.canvas.width,height:g.canvas.height,top:0,left:0,x:0,y:0}):e===i.document.body||e===i.document.documentElement||null!=f.canvas?g.canvas:l(g.canvas,{width:null!=f.width?f.width:y.width,height:null!=f.height?f.height:y.height,top:y.top,left:y.left,x:i.pageXOffset,y:i.pageYOffset}),t(n,f),o})}function t(e,n){n.removeContainer&&(e.parentNode.removeChild(e),w("Cleaned up container"))}function l(e,n){var f=document.createElement("canvas"),o=Math.min(e.width-1,Math.max(0,n.left)),d=Math.min(e.width,Math.max(1,n.left+n.width)),i=Math.min(e.height-1,Math.max(0,n.top)),t=Math.min(e.height,Math.max(1,n.top+n.height));return f.width=n.width,f.height=n.height,w("Cropping canvas at:","left:",n.left,"top:",n.top,"width:",d-o,"height:",t-i),w("Resulting crop with width",n.width,"and height",n.height," with x",o,"and y",i),f.getContext("2d").drawImage(e,o,i,d-o,t-i,n.x,n.y,d-o,t-i),f}function s(e){return Math.max(Math.max(e.body.scrollWidth,e.documentElement.scrollWidth),Math.max(e.body.offsetWidth,e.documentElement.offsetWidth),Math.max(e.body.clientWidth,e.documentElement.clientWidth))}function u(e){return Math.max(Math.max(e.body.scrollHeight,e.documentElement.scrollHeight),Math.max(e.body.offsetHeight,e.documentElement.offsetHeight),Math.max(e.body.clientHeight,e.documentElement.clientHeight))}function a(e){var n=document.createElement("a");return n.href=e,n.href=n.href,n}var p=n("./promise"),c=n("./support"),y=n("./renderers/canvas"),m=n("./imageloader"),r=n("./nodeparser"),v=n("./nodecontainer"),w=n("./log"),b=n("./utils"),g=n("./clone"),h=n("./proxy").loadUrlDocument,x=b.getBounds,j="data-html2canvas-node",k=0;o.Promise=p,o.CanvasRenderer=y,o.NodeContainer=v,o.log=w,o.utils=b;var q="undefined"==typeof document||"function"!=typeof Object.create||"function"!=typeof document.createElement("canvas").getContext?function(){return p.reject("No canvas support")}:o;f.exports=q,"function"==typeof e&&e.amd&&e("html2canvas",[],function(){return q})},{"./clone":4,"./imageloader":13,"./log":15,"./nodecontainer":16,"./nodeparser":17,"./promise":18,"./proxy":19,"./renderers/canvas":23,"./support":25,"./utils":29}],7:[function(e,n){function f(e){if(this.src=e,d("DummyImageContainer for",e),!this.promise||!this.image){d("Initiating DummyImageContainer"),f.prototype.image=new Image;var n=this.image;f.prototype.promise=new o(function(e,f){n.onload=e,n.onerror=f,n.src=i(),n.complete===!0&&e(n)})}}var o=e("./promise"),d=e("./log"),i=e("./utils").smallImage;n.exports=f},{"./log":15,"./promise":18,"./utils":29}],8:[function(e,n){function f(e,n){var f,d,i=document.createElement("div"),t=document.createElement("img"),l=document.createElement("span"),s="Hidden Text";i.style.visibility="hidden",i.style.fontFamily=e,i.style.fontSize=n,i.style.margin=0,i.style.padding=0,document.body.appendChild(i),t.src=o(),t.width=1,t.height=1,t.style.margin=0,t.style.padding=0,t.style.verticalAlign="baseline",l.style.fontFamily=e,l.style.fontSize=n,l.style.margin=0,l.style.padding=0,l.appendChild(document.createTextNode(s)),i.appendChild(l),i.appendChild(t),f=t.offsetTop-l.offsetTop+1,i.removeChild(l),i.appendChild(document.createTextNode(s)),i.style.lineHeight="normal",t.style.verticalAlign="super",d=t.offsetTop-i.offsetTop+1,document.body.removeChild(i),this.baseline=f,this.lineWidth=1,this.middle=d}var o=e("./utils").smallImage;n.exports=f},{"./utils":29}],9:[function(e,n){function f(){this.data={}}var o=e("./font");f.prototype.getMetrics=function(e,n){return void 0===this.data[e+"-"+n]&&(this.data[e+"-"+n]=new o(e,n)),this.data[e+"-"+n]},n.exports=f},{"./font":8}],10:[function(e,n){function f(n,f,o){this.image=null,this.src=n;var t=this,l=i(n);this.promise=(f?new d(function(e){"about:blank"===n.contentWindow.document.URL||null==n.contentWindow.document.documentElement?n.contentWindow.onload=n.onload=function(){e(n)}:e(n)}):this.proxyLoad(o.proxy,l,o)).then(function(n){var f=e("./core");return f(n.contentWindow.document.documentElement,{type:"view",width:n.width,height:n.height,proxy:o.proxy,javascriptEnabled:o.javascriptEnabled,removeContainer:o.removeContainer,allowTaint:o.allowTaint,imageTimeout:o.imageTimeout/2})}).then(function(e){return t.image=e})}var o=e("./utils"),d=e("./promise"),i=o.getBounds,t=e("./proxy").loadUrlDocument;f.prototype.proxyLoad=function(e,n,f){var o=this.src;return t(o.src,e,o.ownerDocument,n.width,n.height,f)},n.exports=f},{"./core":6,"./promise":18,"./proxy":19,"./utils":29}],11:[function(e,n){function f(e){this.src=e.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=o.resolve(!0)}var o=e("./promise");f.prototype.TYPES={LINEAR:1,RADIAL:2},n.exports=f},{"./promise":18}],12:[function(e,n){function f(e,n){this.src=e,this.image=new Image;var f=this;this.tainted=null,this.promise=new o(function(o,d){f.image.onload=o,f.image.onerror=d,n&&(f.image.crossOrigin="anonymous"),f.image.src=e,f.image.complete===!0&&o(f.image)})}var o=e("./promise");n.exports=f},{"./promise":18}],13:[function(e,n){function f(e,n){this.link=null,this.options=e,this.support=n,this.origin=this.getOrigin(window.location.href)}var o=e("./promise"),d=e("./log"),i=e("./imagecontainer"),t=e("./dummyimagecontainer"),l=e("./proxyimagecontainer"),s=e("./framecontainer"),u=e("./svgcontainer"),a=e("./svgnodecontainer"),p=e("./lineargradientcontainer"),c=e("./webkitgradientcontainer"),y=e("./utils").bind;f.prototype.findImages=function(e){var n=[];return e.reduce(function(e,n){switch(n.node.nodeName){case"IMG":return e.concat([{args:[n.node.src],method:"url"}]);case"svg":case"IFRAME":return e.concat([{args:[n.node],method:n.node.nodeName}])}return e},[]).forEach(this.addImage(n,this.loadImage),this),n},f.prototype.findBackgroundImage=function(e,n){return n.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(e,this.loadImage),this),e},f.prototype.addImage=function(e,n){return function(f){f.args.forEach(function(o){this.imageExists(e,o)||(e.splice(0,0,n.call(this,f)),d("Added image #"+e.length,"string"==typeof o?o.substring(0,100):o))},this)}},f.prototype.hasImageBackground=function(e){return"none"!==e.method},f.prototype.loadImage=function(e){if("url"===e.method){var n=e.args[0];return!this.isSVG(n)||this.support.svg||this.options.allowTaint?n.match(/data:image\/.*;base64,/i)?new i(n.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(n)||this.options.allowTaint===!0||this.isSVG(n)?new i(n,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new i(n,!0):this.options.proxy?new l(n,this.options.proxy):new t(n):new u(n)}return"linear-gradient"===e.method?new p(e):"gradient"===e.method?new c(e):"svg"===e.method?new a(e.args[0],this.support.svg):"IFRAME"===e.method?new s(e.args[0],this.isSameOrigin(e.args[0].src),this.options):new t(e)},f.prototype.isSVG=function(e){return"svg"===e.substring(e.length-3).toLowerCase()||u.prototype.isInline(e)},f.prototype.imageExists=function(e,n){return e.some(function(e){return e.src===n})},f.prototype.isSameOrigin=function(e){return this.getOrigin(e)===this.origin},f.prototype.getOrigin=function(e){var n=this.link||(this.link=document.createElement("a"));return n.href=e,n.href=n.href,n.protocol+n.hostname+n.port},f.prototype.getPromise=function(e){return this.timeout(e,this.options.imageTimeout)["catch"](function(){var n=new t(e.src);return n.promise.then(function(n){e.image=n})})},f.prototype.get=function(e){var n=null;return this.images.some(function(f){return(n=f).src===e})?n:null},f.prototype.fetch=function(e){return this.images=e.reduce(y(this.findBackgroundImage,this),this.findImages(e)),this.images.forEach(function(e,n){e.promise.then(function(){d("Succesfully loaded image #"+(n+1),e)},function(f){d("Failed loading image #"+(n+1),e,f)})}),this.ready=o.all(this.images.map(this.getPromise,this)),d("Finished searching images"),this},f.prototype.timeout=function(e,n){var f,i=o.race([e.promise,new o(function(o,i){f=setTimeout(function(){d("Timed out loading image",e),i(e)},n)})]).then(function(e){return clearTimeout(f),e});return i["catch"](function(){clearTimeout(f)}),i},n.exports=f},{"./dummyimagecontainer":7,"./framecontainer":10,"./imagecontainer":12,"./lineargradientcontainer":14,"./log":15,"./promise":18,"./proxyimagecontainer":20,"./svgcontainer":26,"./svgnodecontainer":27,"./utils":29,"./webkitgradientcontainer":30}],14:[function(e,n){function f(e){o.apply(this,arguments),this.type=this.TYPES.LINEAR;var n=null===e.args[0].match(this.stepRegExp);n?e.args[0].split(" ").reverse().forEach(function(e){switch(e){case"left":this.x0=0,this.x1=1;break;case"top":this.y0=0,this.y1=1;break;case"right":this.x0=1,this.x1=0;break;case"bottom":this.y0=1,this.y1=0;break;case"to":var n=this.y0,f=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=f,this.y1=n}},this):(this.y0=0,this.y1=1),this.colorStops=e.args.slice(n?1:0).map(function(e){return e.match(i)}).filter(function(e){return!!e}).map(function(e){return{color:new d(e[1]),stop:"%"===e[3]?e[2]/100:null}}),null===this.colorStops[0].stop&&(this.colorStops[0].stop=0),null===this.colorStops[this.colorStops.length-1].stop&&(this.colorStops[this.colorStops.length-1].stop=1),this.colorStops.forEach(function(e,n){null===e.stop&&this.colorStops.slice(n).some(function(f,o){return null!==f.stop?(e.stop=(f.stop-this.colorStops[n-1].stop)/(o+1)+this.colorStops[n-1].stop,!0):!1},this)},this)}var o=e("./gradientcontainer"),d=e("./color"),i=/^\s*(.*)\s*(\d{1,3})?(%|px)?$/;f.prototype=Object.create(o.prototype),f.prototype.stepRegExp=/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/,n.exports=f},{"./color":5,"./gradientcontainer":11}],15:[function(e,n){n.exports=function(){window.html2canvas.logging&&window.console&&window.console.log&&Function.prototype.bind.call(window.console.log,window.console).apply(window.console,[Date.now()-window.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}},{}],16:[function(e,n){function f(e,n){this.node=e,this.parent=n,this.stack=null,this.bounds=null,this.borders=null,this.clip=[],this.backgroundClip=[],this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.colors={},this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null,this.isPseudoElement=!1,this.opacity=null}function o(e){var n=e.options[e.selectedIndex||0];return n?n.text||"":""}function d(e){if(e&&"matrix"===e[1])return e[2].split(",").map(function(e){return parseFloat(e.trim())});if(e&&"matrix3d"===e[1]){var n=e[2].split(",").map(function(e){return parseFloat(e.trim())});return[n[0],n[1],n[4],n[5],n[12],n[13]]}}function i(e){return-1!==e.toString().indexOf("%")}function t(e){return e.replace("px","")}function l(e){return parseFloat(e)}var s=e("./color"),u=e("./utils"),a=u.getBounds,p=u.parseBackgrounds,c=u.offsetBounds;f.prototype.cloneTo=function(e){e.visible=this.visible,e.borders=this.borders,e.bounds=this.bounds,e.clip=this.clip,e.backgroundClip=this.backgroundClip,e.computedStyles=this.computedStyles,e.styles=this.styles,e.backgroundImages=this.backgroundImages,e.opacity=this.opacity},f.prototype.getOpacity=function(){return null===this.opacity?this.opacity=this.cssFloat("opacity"):this.opacity},f.prototype.assignStack=function(e){this.stack=e,e.children.push(this)},f.prototype.isElementVisible=function(){return this.node.nodeType===Node.TEXT_NODE?this.parent.visible:"none"!==this.css("display")&&"hidden"!==this.css("visibility")&&!this.node.hasAttribute("data-html2canvas-ignore")&&("INPUT"!==this.node.nodeName||"hidden"!==this.node.getAttribute("type"))},f.prototype.css=function(e){return this.computedStyles||(this.computedStyles=this.isPseudoElement?this.parent.computedStyle(this.before?":before":":after"):this.computedStyle(null)),this.styles[e]||(this.styles[e]=this.computedStyles[e])},f.prototype.prefixedCss=function(e){var n=["webkit","moz","ms","o"],f=this.css(e);return void 0===f&&n.some(function(n){return f=this.css(n+e.substr(0,1).toUpperCase()+e.substr(1)),void 0!==f},this),void 0===f?null:f},f.prototype.computedStyle=function(e){return this.node.ownerDocument.defaultView.getComputedStyle(this.node,e)},f.prototype.cssInt=function(e){var n=parseInt(this.css(e),10);return isNaN(n)?0:n},f.prototype.color=function(e){return this.colors[e]||(this.colors[e]=new s(this.css(e)))},f.prototype.cssFloat=function(e){var n=parseFloat(this.css(e));return isNaN(n)?0:n},f.prototype.fontWeight=function(){var e=this.css("fontWeight");switch(parseInt(e,10)){case 401:e="bold";break;case 400:e="normal"}return e},f.prototype.parseClip=function(){var e=this.css("clip").match(this.CLIP);return e?{top:parseInt(e[1],10),right:parseInt(e[2],10),bottom:parseInt(e[3],10),left:parseInt(e[4],10)}:null
-},f.prototype.parseBackgroundImages=function(){return this.backgroundImages||(this.backgroundImages=p(this.css("backgroundImage")))},f.prototype.cssList=function(e,n){var f=(this.css(e)||"").split(",");return f=f[n||0]||f[0]||"auto",f=f.trim().split(" "),1===f.length&&(f=[f[0],i(f[0])?"auto":f[0]]),f},f.prototype.parseBackgroundSize=function(e,n,f){var o,d,t=this.cssList("backgroundSize",f);if(i(t[0]))o=e.width*parseFloat(t[0])/100;else{if(/contain|cover/.test(t[0])){var l=e.width/e.height,s=n.width/n.height;return s>l^"contain"===t[0]?{width:e.height*s,height:e.height}:{width:e.width,height:e.width/s}}o=parseInt(t[0],10)}return d="auto"===t[0]&&"auto"===t[1]?n.height:"auto"===t[1]?o/n.width*n.height:i(t[1])?e.height*parseFloat(t[1])/100:parseInt(t[1],10),"auto"===t[0]&&(o=d/n.height*n.width),{width:o,height:d}},f.prototype.parseBackgroundPosition=function(e,n,f,o){var d,t,l=this.cssList("backgroundPosition",f);return d=i(l[0])?(e.width-(o||n).width)*(parseFloat(l[0])/100):parseInt(l[0],10),t="auto"===l[1]?d/n.width*n.height:i(l[1])?(e.height-(o||n).height)*parseFloat(l[1])/100:parseInt(l[1],10),"auto"===l[0]&&(d=t/n.height*n.width),{left:d,top:t}},f.prototype.parseBackgroundRepeat=function(e){return this.cssList("backgroundRepeat",e)[0]},f.prototype.parseTextShadows=function(){var e=this.css("textShadow"),n=[];if(e&&"none"!==e)for(var f=e.match(this.TEXT_SHADOW_PROPERTY),o=0;f&&o<f.length;o++){var d=f[o].match(this.TEXT_SHADOW_VALUES);n.push({color:new s(d[0]),offsetX:d[1]?parseFloat(d[1].replace("px","")):0,offsetY:d[2]?parseFloat(d[2].replace("px","")):0,blur:d[3]?d[3].replace("px",""):0})}return n},f.prototype.parseTransform=function(){if(!this.transformData)if(this.hasTransform()){var e=this.parseBounds(),n=this.prefixedCss("transformOrigin").split(" ").map(t).map(l);n[0]+=e.left,n[1]+=e.top,this.transformData={origin:n,matrix:this.parseTransformMatrix()}}else this.transformData={origin:[0,0],matrix:[1,0,0,1,0,0]};return this.transformData},f.prototype.parseTransformMatrix=function(){if(!this.transformMatrix){var e=this.prefixedCss("transform"),n=e?d(e.match(this.MATRIX_PROPERTY)):null;this.transformMatrix=n?n:[1,0,0,1,0,0]}return this.transformMatrix},f.prototype.parseBounds=function(){return this.bounds||(this.bounds=this.hasTransform()?c(this.node):a(this.node))},f.prototype.hasTransform=function(){return"1,0,0,1,0,0"!==this.parseTransformMatrix().join(",")||this.parent&&this.parent.hasTransform()},f.prototype.getValue=function(){var e=this.node.value||"";return"SELECT"===this.node.tagName?e=o(this.node):"password"===this.node.type&&(e=Array(e.length+1).join("•")),0===e.length?this.node.placeholder||"":e},f.prototype.MATRIX_PROPERTY=/(matrix|matrix3d)\((.+)\)/,f.prototype.TEXT_SHADOW_PROPERTY=/((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g,f.prototype.TEXT_SHADOW_VALUES=/(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g,f.prototype.CLIP=/^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/,n.exports=f},{"./color":5,"./utils":29}],17:[function(e,n){function f(e,n,f,o,d){N("Starting NodeParser"),this.renderer=n,this.options=d,this.range=null,this.support=f,this.renderQueue=[],this.stack=new V(!0,1,e.ownerDocument,null);var i=new P(e,null);if(d.background&&n.rectangle(0,0,n.width,n.height,new T(d.background)),e===e.ownerDocument.documentElement){var t=new P(i.color("backgroundColor").isTransparent()?e.ownerDocument.body:e.ownerDocument.documentElement,null);n.rectangle(0,0,n.width,n.height,t.color("backgroundColor"))}i.visibile=i.isElementVisible(),this.createPseudoHideStyles(e.ownerDocument),this.disableAnimations(e.ownerDocument),this.nodes=I([i].concat(this.getChildren(i)).filter(function(e){return e.visible=e.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new S,N("Fetched nodes, total:",this.nodes.length),N("Calculate overflow clips"),this.calculateOverflowClips(),N("Start fetching images"),this.images=o.fetch(this.nodes.filter(A)),this.ready=this.images.ready.then(X(function(){return N("Images loaded, starting parsing"),N("Creating stacking contexts"),this.createStackingContexts(),N("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.parse(this.stack),N("Render queue created with "+this.renderQueue.length+" items"),new U(X(function(e){d.async?"function"==typeof d.async?d.async.call(this,this.renderQueue,e):this.renderQueue.length>0?(this.renderIndex=0,this.asyncRenderer(this.renderQueue,e)):e():(this.renderQueue.forEach(this.paint,this),e())},this))},this))}function o(e){return e.parent&&e.parent.clip.length}function d(e){return e.replace(/(\-[a-z])/g,function(e){return e.toUpperCase().replace("-","")})}function i(){}function t(e,n,f,o){return e.map(function(d,i){if(d.width>0){var t=n.left,l=n.top,s=n.width,u=n.height-e[2].width;switch(i){case 0:u=e[0].width,d.args=a({c1:[t,l],c2:[t+s,l],c3:[t+s-e[1].width,l+u],c4:[t+e[3].width,l+u]},o[0],o[1],f.topLeftOuter,f.topLeftInner,f.topRightOuter,f.topRightInner);break;case 1:t=n.left+n.width-e[1].width,s=e[1].width,d.args=a({c1:[t+s,l],c2:[t+s,l+u+e[2].width],c3:[t,l+u],c4:[t,l+e[0].width]},o[1],o[2],f.topRightOuter,f.topRightInner,f.bottomRightOuter,f.bottomRightInner);break;case 2:l=l+n.height-e[2].width,u=e[2].width,d.args=a({c1:[t+s,l+u],c2:[t,l+u],c3:[t+e[3].width,l],c4:[t+s-e[3].width,l]},o[2],o[3],f.bottomRightOuter,f.bottomRightInner,f.bottomLeftOuter,f.bottomLeftInner);break;case 3:s=e[3].width,d.args=a({c1:[t,l+u+e[2].width],c2:[t,l],c3:[t+s,l+e[0].width],c4:[t+s,l+u]},o[3],o[0],f.bottomLeftOuter,f.bottomLeftInner,f.topLeftOuter,f.topLeftInner)}}return d})}function l(e,n,f,o){var d=4*((Math.sqrt(2)-1)/3),i=f*d,t=o*d,l=e+f,s=n+o;return{topLeft:u({x:e,y:s},{x:e,y:s-t},{x:l-i,y:n},{x:l,y:n}),topRight:u({x:e,y:n},{x:e+i,y:n},{x:l,y:s-t},{x:l,y:s}),bottomRight:u({x:l,y:n},{x:l,y:n+t},{x:e+i,y:s},{x:e,y:s}),bottomLeft:u({x:l,y:s},{x:l-i,y:s},{x:e,y:n+t},{x:e,y:n})}}function s(e,n,f){var o=e.left,d=e.top,i=e.width,t=e.height,s=n[0][0]<i/2?n[0][0]:i/2,u=n[0][1]<t/2?n[0][1]:t/2,a=n[1][0]<i/2?n[1][0]:i/2,p=n[1][1]<t/2?n[1][1]:t/2,c=n[2][0]<i/2?n[2][0]:i/2,y=n[2][1]<t/2?n[2][1]:t/2,m=n[3][0]<i/2?n[3][0]:i/2,r=n[3][1]<t/2?n[3][1]:t/2,v=i-a,w=t-y,b=i-c,g=t-r;return{topLeftOuter:l(o,d,s,u).topLeft.subdivide(.5),topLeftInner:l(o+f[3].width,d+f[0].width,Math.max(0,s-f[3].width),Math.max(0,u-f[0].width)).topLeft.subdivide(.5),topRightOuter:l(o+v,d,a,p).topRight.subdivide(.5),topRightInner:l(o+Math.min(v,i+f[3].width),d+f[0].width,v>i+f[3].width?0:a-f[3].width,p-f[0].width).topRight.subdivide(.5),bottomRightOuter:l(o+b,d+w,c,y).bottomRight.subdivide(.5),bottomRightInner:l(o+Math.min(b,i-f[3].width),d+Math.min(w,t+f[0].width),Math.max(0,c-f[1].width),y-f[2].width).bottomRight.subdivide(.5),bottomLeftOuter:l(o,d+g,m,r).bottomLeft.subdivide(.5),bottomLeftInner:l(o+f[3].width,d+g,Math.max(0,m-f[3].width),r-f[2].width).bottomLeft.subdivide(.5)}}function u(e,n,f,o){var d=function(e,n,f){return{x:e.x+(n.x-e.x)*f,y:e.y+(n.y-e.y)*f}};return{start:e,startControl:n,endControl:f,end:o,subdivide:function(i){var t=d(e,n,i),l=d(n,f,i),s=d(f,o,i),a=d(t,l,i),p=d(l,s,i),c=d(a,p,i);return[u(e,t,a,c),u(c,p,s,o)]},curveTo:function(e){e.push(["bezierCurve",n.x,n.y,f.x,f.y,o.x,o.y])},curveToReversed:function(o){o.push(["bezierCurve",f.x,f.y,n.x,n.y,e.x,e.y])}}}function a(e,n,f,o,d,i,t){var l=[];return n[0]>0||n[1]>0?(l.push(["line",o[1].start.x,o[1].start.y]),o[1].curveTo(l)):l.push(["line",e.c1[0],e.c1[1]]),f[0]>0||f[1]>0?(l.push(["line",i[0].start.x,i[0].start.y]),i[0].curveTo(l),l.push(["line",t[0].end.x,t[0].end.y]),t[0].curveToReversed(l)):(l.push(["line",e.c2[0],e.c2[1]]),l.push(["line",e.c3[0],e.c3[1]])),n[0]>0||n[1]>0?(l.push(["line",d[1].end.x,d[1].end.y]),d[1].curveToReversed(l)):l.push(["line",e.c4[0],e.c4[1]]),l}function p(e,n,f,o,d,i,t){n[0]>0||n[1]>0?(e.push(["line",o[0].start.x,o[0].start.y]),o[0].curveTo(e),o[1].curveTo(e)):e.push(["line",i,t]),(f[0]>0||f[1]>0)&&e.push(["line",d[0].start.x,d[0].start.y])}function c(e){return e.cssInt("zIndex")<0}function y(e){return e.cssInt("zIndex")>0}function m(e){return 0===e.cssInt("zIndex")}function r(e){return-1!==["inline","inline-block","inline-table"].indexOf(e.css("display"))}function v(e){return e instanceof V}function w(e){return e.node.data.trim().length>0}function b(e){return/^(normal|none|0px)$/.test(e.parent.css("letterSpacing"))}function g(e){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(n){var f=e.css("border"+n+"Radius"),o=f.split(" ");return o.length<=1&&(o[1]=o[0]),o.map(F)})}function h(e){return e.nodeType===Node.TEXT_NODE||e.nodeType===Node.ELEMENT_NODE}function x(e){var n=e.css("position"),f=-1!==["absolute","relative","fixed"].indexOf(n)?e.css("zIndex"):"auto";return"auto"!==f}function j(e){return"static"!==e.css("position")}function k(e){return"none"!==e.css("float")}function q(e){return-1!==["inline-block","inline-table"].indexOf(e.css("display"))}function z(e){var n=this;return function(){return!e.apply(n,arguments)}}function A(e){return e.node.nodeType===Node.ELEMENT_NODE}function B(e){return e.isPseudoElement===!0}function C(e){return e.node.nodeType===Node.TEXT_NODE}function D(e){return function(n,f){return n.cssInt("zIndex")+e.indexOf(n)/e.length-(f.cssInt("zIndex")+e.indexOf(f)/e.length)}}function E(e){return e.getOpacity()<1}function F(e){return parseInt(e,10)}function G(e){return e.width}function H(e){return e.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(e.node.nodeName)}function I(e){return[].concat.apply([],e)}function J(e){var n=e.substr(0,1);return n===e.substr(e.length-1)&&n.match(/'|"/)?e.substr(1,e.length-2):e}function K(e){for(var n,f=[],o=0,d=!1;e.length;)L(e[o])===d?(n=e.splice(0,o),n.length&&f.push(O.ucs2.encode(n)),d=!d,o=0):o++,o>=e.length&&(n=e.splice(0,o),n.length&&f.push(O.ucs2.encode(n)));return f}function L(e){return-1!==[32,13,10,9,45].indexOf(e)}function M(e){return/[^\u0000-\u00ff]/.test(e)}var N=e("./log"),O=e("punycode"),P=e("./nodecontainer"),Q=e("./textcontainer"),R=e("./pseudoelementcontainer"),S=e("./fontmetrics"),T=e("./color"),U=e("./promise"),V=e("./stackingcontext"),W=e("./utils"),X=W.bind,Y=W.getBounds,Z=W.parseBackgrounds,$=W.offsetBounds;f.prototype.calculateOverflowClips=function(){this.nodes.forEach(function(e){if(A(e)){B(e)&&e.appendToDOM(),e.borders=this.parseBorders(e);var n="hidden"===e.css("overflow")?[e.borders.clip]:[],f=e.parseClip();f&&-1!==["absolute","fixed"].indexOf(e.css("position"))&&n.push([["rect",e.bounds.left+f.left,e.bounds.top+f.top,f.right-f.left,f.bottom-f.top]]),e.clip=o(e)?e.parent.clip.concat(n):n,e.backgroundClip="hidden"!==e.css("overflow")?e.clip.concat([e.borders.clip]):e.clip,B(e)&&e.cleanDOM()}else C(e)&&(e.clip=o(e)?e.parent.clip:[]);B(e)||(e.bounds=null)},this)},f.prototype.asyncRenderer=function(e,n,f){f=f||Date.now(),this.paint(e[this.renderIndex++]),e.length===this.renderIndex?n():f+20>Date.now()?this.asyncRenderer(e,n,f):setTimeout(X(function(){this.asyncRenderer(e,n)},this),0)},f.prototype.createPseudoHideStyles=function(e){this.createStyles(e,"."+R.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+':before { content: "" !important; display: none !important; }.'+R.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER+':after { content: "" !important; display: none !important; }')},f.prototype.disableAnimations=function(e){this.createStyles(e,"* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; -webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}")},f.prototype.createStyles=function(e,n){var f=e.createElement("style");f.innerHTML=n,e.body.appendChild(f)},f.prototype.getPseudoElements=function(e){var n=[[e]];if(e.node.nodeType===Node.ELEMENT_NODE){var f=this.getPseudoElement(e,":before"),o=this.getPseudoElement(e,":after");f&&n.push(f),o&&n.push(o)}return I(n)},f.prototype.getPseudoElement=function(e,n){var f=e.computedStyle(n);if(!f||!f.content||"none"===f.content||"-moz-alt-content"===f.content||"none"===f.display)return null;for(var o=J(f.content),i="url"===o.substr(0,3),t=document.createElement(i?"img":"html2canvaspseudoelement"),l=new R(t,e,n),s=f.length-1;s>=0;s--){var u=d(f.item(s));t.style[u]=f[u]}if(t.className=R.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+" "+R.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER,i)return t.src=Z(o)[0].args[0],[l];var a=document.createTextNode(o);return t.appendChild(a),[l,new Q(a,l)]},f.prototype.getChildren=function(e){return I([].filter.call(e.node.childNodes,h).map(function(n){var f=[n.nodeType===Node.TEXT_NODE?new Q(n,e):new P(n,e)].filter(H);return n.nodeType===Node.ELEMENT_NODE&&f.length&&"TEXTAREA"!==n.tagName?f[0].isElementVisible()?f.concat(this.getChildren(f[0])):[]:f},this))},f.prototype.newStackingContext=function(e,n){var f=new V(n,e.getOpacity(),e.node,e.parent);e.cloneTo(f);var o=n?f.getParentStack(this):f.parent.stack;o.contexts.push(f),e.stack=f},f.prototype.createStackingContexts=function(){this.nodes.forEach(function(e){A(e)&&(this.isRootElement(e)||E(e)||x(e)||this.isBodyWithTransparentRoot(e)||e.hasTransform())?this.newStackingContext(e,!0):A(e)&&(j(e)&&m(e)||q(e)||k(e))?this.newStackingContext(e,!1):e.assignStack(e.parent.stack)},this)},f.prototype.isBodyWithTransparentRoot=function(e){return"BODY"===e.node.nodeName&&e.parent.color("backgroundColor").isTransparent()},f.prototype.isRootElement=function(e){return null===e.parent},f.prototype.sortStackingContexts=function(e){e.contexts.sort(D(e.contexts.slice(0))),e.contexts.forEach(this.sortStackingContexts,this)},f.prototype.parseTextBounds=function(e){return function(n,f,o){if("none"!==e.parent.css("textDecoration").substr(0,4)||0!==n.trim().length){if(this.support.rangeBounds&&!e.parent.hasTransform()){var d=o.slice(0,f).join("").length;return this.getRangeBounds(e.node,d,n.length)}if(e.node&&"string"==typeof e.node.data){var i=e.node.splitText(n.length),t=this.getWrapperBounds(e.node,e.parent.hasTransform());return e.node=i,t}}else(!this.support.rangeBounds||e.parent.hasTransform())&&(e.node=e.node.splitText(n.length));return{}}},f.prototype.getWrapperBounds=function(e,n){var f=e.ownerDocument.createElement("html2canvaswrapper"),o=e.parentNode,d=e.cloneNode(!0);f.appendChild(e.cloneNode(!0)),o.replaceChild(f,e);var i=n?$(f):Y(f);return o.replaceChild(d,f),i},f.prototype.getRangeBounds=function(e,n,f){var o=this.range||(this.range=e.ownerDocument.createRange());return o.setStart(e,n),o.setEnd(e,n+f),o.getBoundingClientRect()},f.prototype.parse=function(e){var n=e.contexts.filter(c),f=e.children.filter(A),o=f.filter(z(k)),d=o.filter(z(j)).filter(z(r)),t=f.filter(z(j)).filter(k),l=o.filter(z(j)).filter(r),s=e.contexts.concat(o.filter(j)).filter(m),u=e.children.filter(C).filter(w),a=e.contexts.filter(y);n.concat(d).concat(t).concat(l).concat(s).concat(u).concat(a).forEach(function(e){this.renderQueue.push(e),v(e)&&(this.parse(e),this.renderQueue.push(new i))},this)},f.prototype.paint=function(e){try{e instanceof i?this.renderer.ctx.restore():C(e)?(B(e.parent)&&e.parent.appendToDOM(),this.paintText(e),B(e.parent)&&e.parent.cleanDOM()):this.paintNode(e)}catch(n){if(N(n),this.options.strict)throw n}},f.prototype.paintNode=function(e){v(e)&&(this.renderer.setOpacity(e.opacity),this.renderer.ctx.save(),e.hasTransform()&&this.renderer.setTransform(e.parseTransform())),"INPUT"===e.node.nodeName&&"checkbox"===e.node.type?this.paintCheckbox(e):"INPUT"===e.node.nodeName&&"radio"===e.node.type?this.paintRadio(e):this.paintElement(e)},f.prototype.paintElement=function(e){var n=e.parseBounds();this.renderer.clip(e.backgroundClip,function(){this.renderer.renderBackground(e,n,e.borders.borders.map(G))},this),this.renderer.clip(e.clip,function(){this.renderer.renderBorders(e.borders.borders)},this),this.renderer.clip(e.backgroundClip,function(){switch(e.node.nodeName){case"svg":case"IFRAME":var f=this.images.get(e.node);f?this.renderer.renderImage(e,n,e.borders,f):N("Error loading <"+e.node.nodeName+">",e.node);break;case"IMG":var o=this.images.get(e.node.src);o?this.renderer.renderImage(e,n,e.borders,o):N("Error loading <img>",e.node.src);break;case"CANVAS":this.renderer.renderImage(e,n,e.borders,{image:e.node});break;case"SELECT":case"INPUT":case"TEXTAREA":this.paintFormValue(e)}},this)},f.prototype.paintCheckbox=function(e){var n=e.parseBounds(),f=Math.min(n.width,n.height),o={width:f-1,height:f-1,top:n.top,left:n.left},d=[3,3],i=[d,d,d,d],l=[1,1,1,1].map(function(e){return{color:new T("#A5A5A5"),width:e}}),u=s(o,i,l);this.renderer.clip(e.backgroundClip,function(){this.renderer.rectangle(o.left+1,o.top+1,o.width-2,o.height-2,new T("#DEDEDE")),this.renderer.renderBorders(t(l,o,u,i)),e.node.checked&&(this.renderer.font(new T("#424242"),"normal","normal","bold",f-3+"px","arial"),this.renderer.text("✔",o.left+f/6,o.top+f-1))},this)},f.prototype.paintRadio=function(e){var n=e.parseBounds(),f=Math.min(n.width,n.height)-2;this.renderer.clip(e.backgroundClip,function(){this.renderer.circleStroke(n.left+1,n.top+1,f,new T("#DEDEDE"),1,new T("#A5A5A5")),e.node.checked&&this.renderer.circle(Math.ceil(n.left+f/4)+1,Math.ceil(n.top+f/4)+1,Math.floor(f/2),new T("#424242"))},this)},f.prototype.paintFormValue=function(e){var n=e.getValue();if(n.length>0){var f=e.node.ownerDocument,o=f.createElement("html2canvaswrapper"),d=["lineHeight","textAlign","fontFamily","fontWeight","fontSize","color","paddingLeft","paddingTop","paddingRight","paddingBottom","width","height","borderLeftStyle","borderTopStyle","borderLeftWidth","borderTopWidth","boxSizing","whiteSpace","wordWrap"];d.forEach(function(n){try{o.style[n]=e.css(n)}catch(f){N("html2canvas: Parse: Exception caught in renderFormValue: "+f.message)}});var i=e.parseBounds();o.style.position="fixed",o.style.left=i.left+"px",o.style.top=i.top+"px",o.textContent=n,f.body.appendChild(o),this.paintText(new Q(o.firstChild,e)),f.body.removeChild(o)}},f.prototype.paintText=function(e){e.applyTextTransform();var n=O.ucs2.decode(e.node.data),f=this.options.letterRendering&&!b(e)||M(e.node.data)?n.map(function(e){return O.ucs2.encode([e])}):K(n),o=e.parent.fontWeight(),d=e.parent.css("fontSize"),i=e.parent.css("fontFamily"),t=e.parent.parseTextShadows();this.renderer.font(e.parent.color("color"),e.parent.css("fontStyle"),e.parent.css("fontVariant"),o,d,i),t.length?this.renderer.fontShadow(t[0].color,t[0].offsetX,t[0].offsetY,t[0].blur):this.renderer.clearShadow(),this.renderer.clip(e.parent.clip,function(){f.map(this.parseTextBounds(e),this).forEach(function(n,o){n&&(this.renderer.text(f[o],n.left,n.bottom),this.renderTextDecoration(e.parent,n,this.fontMetrics.getMetrics(i,d)))},this)},this)},f.prototype.renderTextDecoration=function(e,n,f){switch(e.css("textDecoration").split(" ")[0]){case"underline":this.renderer.rectangle(n.left,Math.round(n.top+f.baseline+f.lineWidth),n.width,1,e.color("color"));break;case"overline":this.renderer.rectangle(n.left,Math.round(n.top),n.width,1,e.color("color"));break;case"line-through":this.renderer.rectangle(n.left,Math.ceil(n.top+f.middle+f.lineWidth),n.width,1,e.color("color"))}};var _={inset:[["darken",.6],["darken",.1],["darken",.1],["darken",.6]]};f.prototype.parseBorders=function(e){var n=e.parseBounds(),f=g(e),o=["Top","Right","Bottom","Left"].map(function(n,f){var o=e.css("border"+n+"Style"),d=e.color("border"+n+"Color");"inset"===o&&d.isBlack()&&(d=new T([255,255,255,d.a]));var i=_[o]?_[o][f]:null;return{width:e.cssInt("border"+n+"Width"),color:i?d[i[0]](i[1]):d,args:null}}),d=s(n,f,o);return{clip:this.parseBackgroundClip(e,d,o,f,n),borders:t(o,n,d,f)}},f.prototype.parseBackgroundClip=function(e,n,f,o,d){var i=e.css("backgroundClip"),t=[];switch(i){case"content-box":case"padding-box":p(t,o[0],o[1],n.topLeftInner,n.topRightInner,d.left+f[3].width,d.top+f[0].width),p(t,o[1],o[2],n.topRightInner,n.bottomRightInner,d.left+d.width-f[1].width,d.top+f[0].width),p(t,o[2],o[3],n.bottomRightInner,n.bottomLeftInner,d.left+d.width-f[1].width,d.top+d.height-f[2].width),p(t,o[3],o[0],n.bottomLeftInner,n.topLeftInner,d.left+f[3].width,d.top+d.height-f[2].width);break;default:p(t,o[0],o[1],n.topLeftOuter,n.topRightOuter,d.left,d.top),p(t,o[1],o[2],n.topRightOuter,n.bottomRightOuter,d.left+d.width,d.top),p(t,o[2],o[3],n.bottomRightOuter,n.bottomLeftOuter,d.left+d.width,d.top+d.height),p(t,o[3],o[0],n.bottomLeftOuter,n.topLeftOuter,d.left,d.top+d.height)}return t},n.exports=f},{"./color":5,"./fontmetrics":9,"./log":15,"./nodecontainer":16,"./promise":18,"./pseudoelementcontainer":21,"./stackingcontext":24,"./textcontainer":28,"./utils":29,punycode:3}],18:[function(e,n){n.exports=e("es6-promise").Promise},{"es6-promise":1}],19:[function(e,n,f){function o(e,n,f){var o="withCredentials"in new XMLHttpRequest;if(!n)return a.reject("No proxy configured");var d=t(o),s=l(n,e,d);return o?p(s):i(f,s,d).then(function(e){return r(e.content)})}function d(e,n,f){var o="crossOrigin"in new Image,d=t(o),s=l(n,e,d);return o?a.resolve(s):i(f,s,d).then(function(e){return"data:"+e.type+";base64,"+e.content})}function i(e,n,f){return new a(function(o,d){var i=e.createElement("script"),t=function(){delete window.html2canvas.proxy[f],e.body.removeChild(i)};window.html2canvas.proxy[f]=function(e){t(),o(e)},i.src=n,i.onerror=function(e){t(),d(e)},e.body.appendChild(i)})}function t(e){return e?"":"html2canvas_"+Date.now()+"_"+ ++v+"_"+Math.round(1e5*Math.random())}function l(e,n,f){return e+"?url="+encodeURIComponent(n)+(f.length?"&callback=html2canvas.proxy."+f:"")}function s(e){return function(n){var f,o=new DOMParser;try{f=o.parseFromString(n,"text/html")}catch(d){y("DOMParser not supported, falling back to createHTMLDocument"),f=document.implementation.createHTMLDocument("");try{f.open(),f.write(n),f.close()}catch(i){y("createHTMLDocument write not supported, falling back to document.body.innerHTML"),f.body.innerHTML=n}}var t=f.querySelector("base");if(!t||!t.href.host){var l=f.createElement("base");l.href=e,f.head.insertBefore(l,f.head.firstChild)}return f}}function u(e,n,f,d,i,t){return new o(e,n,window.document).then(s(e)).then(function(e){return m(e,f,d,i,t,0,0)})}var a=e("./promise"),p=e("./xhr"),c=e("./utils"),y=e("./log"),m=e("./clone"),r=c.decode64,v=0;f.Proxy=o,f.ProxyURL=d,f.loadUrlDocument=u},{"./clone":4,"./log":15,"./promise":18,"./utils":29,"./xhr":31}],20:[function(e,n){function f(e,n){var f=document.createElement("a");f.href=e,e=f.href,this.src=e,this.image=new Image;var i=this;this.promise=new d(function(f,d){i.image.crossOrigin="Anonymous",i.image.onload=f,i.image.onerror=d,new o(e,n,document).then(function(e){i.image.src=e})["catch"](d)})}var o=e("./proxy").ProxyURL,d=e("./promise");n.exports=f},{"./promise":18,"./proxy":19}],21:[function(e,n){function f(e,n,f){o.call(this,e,n),this.isPseudoElement=!0,this.before=":before"===f}var o=e("./nodecontainer");f.prototype.cloneTo=function(e){f.prototype.cloneTo.call(this,e),e.isPseudoElement=!0,e.before=this.before},f.prototype=Object.create(o.prototype),f.prototype.appendToDOM=function(){this.before?this.parent.node.insertBefore(this.node,this.parent.node.firstChild):this.parent.node.appendChild(this.node),this.parent.node.className+=" "+this.getHideClass()},f.prototype.cleanDOM=function(){this.node.parentNode.removeChild(this.node),this.parent.node.className=this.parent.node.className.replace(this.getHideClass(),"")},f.prototype.getHideClass=function(){return this["PSEUDO_HIDE_ELEMENT_CLASS_"+(this.before?"BEFORE":"AFTER")]},f.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE="___html2canvas___pseudoelement_before",f.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER="___html2canvas___pseudoelement_after",n.exports=f},{"./nodecontainer":16}],22:[function(e,n){function f(e,n,f,o,d){this.width=e,this.height=n,this.images=f,this.options=o,this.document=d}var o=e("./log");f.prototype.renderImage=function(e,n,f,o){var d=e.cssInt("paddingLeft"),i=e.cssInt("paddingTop"),t=e.cssInt("paddingRight"),l=e.cssInt("paddingBottom"),s=f.borders,u=n.width-(s[1].width+s[3].width+d+t),a=n.height-(s[0].width+s[2].width+i+l);this.drawImage(o,0,0,o.image.width||u,o.image.height||a,n.left+d+s[3].width,n.top+i+s[0].width,u,a)},f.prototype.renderBackground=function(e,n,f){n.height>0&&n.width>0&&(this.renderBackgroundColor(e,n),this.renderBackgroundImage(e,n,f))},f.prototype.renderBackgroundColor=function(e,n){var f=e.color("backgroundColor");f.isTransparent()||this.rectangle(n.left,n.top,n.width,n.height,f)},f.prototype.renderBorders=function(e){e.forEach(this.renderBorder,this)},f.prototype.renderBorder=function(e){e.color.isTransparent()||null===e.args||this.drawShape(e.args,e.color)},f.prototype.renderBackgroundImage=function(e,n,f){var d=e.parseBackgroundImages();d.reverse().forEach(function(d,i,t){switch(d.method){case"url":var l=this.images.get(d.args[0]);l?this.renderBackgroundRepeating(e,n,l,t.length-(i+1),f):o("Error loading background-image",d.args[0]);break;case"linear-gradient":case"gradient":var s=this.images.get(d.value);s?this.renderBackgroundGradient(s,n,f):o("Error loading background-image",d.args[0]);break;case"none":break;default:o("Unknown background-image type",d.args[0])}},this)},f.prototype.renderBackgroundRepeating=function(e,n,f,o,d){var i=e.parseBackgroundSize(n,f.image,o),t=e.parseBackgroundPosition(n,f.image,o,i),l=e.parseBackgroundRepeat(o);switch(l){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(f,t,i,n,n.left+d[3],n.top+t.top+d[0],99999,i.height,d);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(f,t,i,n,n.left+t.left+d[3],n.top+d[0],i.width,99999,d);break;case"no-repeat":this.backgroundRepeatShape(f,t,i,n,n.left+t.left+d[3],n.top+t.top+d[0],i.width,i.height,d);break;default:this.renderBackgroundRepeat(f,t,i,{top:n.top,left:n.left},d[3],d[0])}},n.exports=f},{"./log":15}],23:[function(e,n){function f(e,n){d.apply(this,arguments),this.canvas=this.options.canvas||this.document.createElement("canvas"),this.options.canvas||(this.canvas.width=e,this.canvas.height=n),this.ctx=this.canvas.getContext("2d"),this.taintCtx=this.document.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},t("Initialized CanvasRenderer with size",e,"x",n)}function o(e){return e.length>0}var d=e("../renderer"),i=e("../lineargradientcontainer"),t=e("../log");f.prototype=Object.create(d.prototype),f.prototype.setFillStyle=function(e){return this.ctx.fillStyle="object"==typeof e&&e.isColor?e.toString():e,this.ctx},f.prototype.rectangle=function(e,n,f,o,d){this.setFillStyle(d).fillRect(e,n,f,o)},f.prototype.circle=function(e,n,f,o){this.setFillStyle(o),this.ctx.beginPath(),this.ctx.arc(e+f/2,n+f/2,f/2,0,2*Math.PI,!0),this.ctx.closePath(),this.ctx.fill()},f.prototype.circleStroke=function(e,n,f,o,d,i){this.circle(e,n,f,o),this.ctx.strokeStyle=i.toString(),this.ctx.stroke()},f.prototype.drawShape=function(e,n){this.shape(e),this.setFillStyle(n).fill()},f.prototype.taints=function(e){if(null===e.tainted){this.taintCtx.drawImage(e.image,0,0);try{this.taintCtx.getImageData(0,0,1,1),e.tainted=!1}catch(n){this.taintCtx=document.createElement("canvas").getContext("2d"),e.tainted=!0}}return e.tainted},f.prototype.drawImage=function(e,n,f,o,d,i,t,l,s){(!this.taints(e)||this.options.allowTaint)&&this.ctx.drawImage(e.image,n,f,o,d,i,t,l,s)},f.prototype.clip=function(e,n,f){this.ctx.save(),e.filter(o).forEach(function(e){this.shape(e).clip()},this),n.call(f),this.ctx.restore()},f.prototype.shape=function(e){return this.ctx.beginPath(),e.forEach(function(e,n){"rect"===e[0]?this.ctx.rect.apply(this.ctx,e.slice(1)):this.ctx[0===n?"moveTo":e[0]+"To"].apply(this.ctx,e.slice(1))},this),this.ctx.closePath(),this.ctx},f.prototype.font=function(e,n,f,o,d,i){this.setFillStyle(e).font=[n,f,o,d,i].join(" ").split(",")[0]},f.prototype.fontShadow=function(e,n,f,o){this.setVariable("shadowColor",e.toString()).setVariable("shadowOffsetY",n).setVariable("shadowOffsetX",f).setVariable("shadowBlur",o)},f.prototype.clearShadow=function(){this.setVariable("shadowColor","rgba(0,0,0,0)")},f.prototype.setOpacity=function(e){this.ctx.globalAlpha=e},f.prototype.setTransform=function(e){this.ctx.translate(e.origin[0],e.origin[1]),this.ctx.transform.apply(this.ctx,e.matrix),this.ctx.translate(-e.origin[0],-e.origin[1])},f.prototype.setVariable=function(e,n){return this.variables[e]!==n&&(this.variables[e]=this.ctx[e]=n),this},f.prototype.text=function(e,n,f){this.ctx.fillText(e,n,f)},f.prototype.backgroundRepeatShape=function(e,n,f,o,d,i,t,l,s){var u=[["line",Math.round(d),Math.round(i)],["line",Math.round(d+t),Math.round(i)],["line",Math.round(d+t),Math.round(l+i)],["line",Math.round(d),Math.round(l+i)]];this.clip([u],function(){this.renderBackgroundRepeat(e,n,f,o,s[3],s[0])},this)},f.prototype.renderBackgroundRepeat=function(e,n,f,o,d,i){var t=Math.round(o.left+n.left+d),l=Math.round(o.top+n.top+i);this.setFillStyle(this.ctx.createPattern(this.resizeImage(e,f),"repeat")),this.ctx.translate(t,l),this.ctx.fill(),this.ctx.translate(-t,-l)},f.prototype.renderBackgroundGradient=function(e,n){if(e instanceof i){var f=this.ctx.createLinearGradient(n.left+n.width*e.x0,n.top+n.height*e.y0,n.left+n.width*e.x1,n.top+n.height*e.y1);e.colorStops.forEach(function(e){f.addColorStop(e.stop,e.color.toString())}),this.rectangle(n.left,n.top,n.width,n.height,f)}},f.prototype.resizeImage=function(e,n){var f=e.image;if(f.width===n.width&&f.height===n.height)return f;var o,d=document.createElement("canvas");return d.width=n.width,d.height=n.height,o=d.getContext("2d"),o.drawImage(f,0,0,f.width,f.height,0,0,n.width,n.height),d},n.exports=f},{"../lineargradientcontainer":14,"../log":15,"../renderer":22}],24:[function(e,n){function f(e,n,f,d){o.call(this,f,d),this.ownStacking=e,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*n}var o=e("./nodecontainer");f.prototype=Object.create(o.prototype),f.prototype.getParentStack=function(e){var n=this.parent?this.parent.stack:null;return n?n.ownStacking?n:n.getParentStack(e):e.stack},n.exports=f},{"./nodecontainer":16}],25:[function(e,n){function f(e){this.rangeBounds=this.testRangeBounds(e),this.cors=this.testCORS(),this.svg=this.testSVG()}f.prototype.testRangeBounds=function(e){var n,f,o,d,i=!1;return e.createRange&&(n=e.createRange(),n.getBoundingClientRect&&(f=e.createElement("boundtest"),f.style.height="123px",f.style.display="block",e.body.appendChild(f),n.selectNode(f),o=n.getBoundingClientRect(),d=o.height,123===d&&(i=!0),e.body.removeChild(f))),i},f.prototype.testCORS=function(){return"undefined"!=typeof(new Image).crossOrigin},f.prototype.testSVG=function(){var e=new Image,n=document.createElement("canvas"),f=n.getContext("2d");e.src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";try{f.drawImage(e,0,0),n.toDataURL()}catch(o){return!1}return!0},n.exports=f},{}],26:[function(e,n){function f(e){this.src=e,this.image=null;var n=this;this.promise=this.hasFabric().then(function(){return n.isInline(e)?o.resolve(n.inlineFormatting(e)):d(e)}).then(function(e){return new o(function(f){window.html2canvas.svg.fabric.loadSVGFromString(e,n.createCanvas.call(n,f))})})}var o=e("./promise"),d=e("./xhr"),i=e("./utils").decode64;f.prototype.hasFabric=function(){return window.html2canvas.svg&&window.html2canvas.svg.fabric?o.resolve():o.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg"))},f.prototype.inlineFormatting=function(e){return/^data:image\/svg\+xml;base64,/.test(e)?this.decode64(this.removeContentType(e)):this.removeContentType(e)},f.prototype.removeContentType=function(e){return e.replace(/^data:image\/svg\+xml(;base64)?,/,"")},f.prototype.isInline=function(e){return/^data:image\/svg\+xml/i.test(e)},f.prototype.createCanvas=function(e){var n=this;return function(f,o){var d=new window.html2canvas.svg.fabric.StaticCanvas("c");n.image=d.lowerCanvasEl,d.setWidth(o.width).setHeight(o.height).add(window.html2canvas.svg.fabric.util.groupSVGElements(f,o)).renderAll(),e(d.lowerCanvasEl)}},f.prototype.decode64=function(e){return"function"==typeof window.atob?window.atob(e):i(e)
-},n.exports=f},{"./promise":18,"./utils":29,"./xhr":31}],27:[function(e,n){function f(e,n){this.src=e,this.image=null;var f=this;this.promise=n?new d(function(n,o){f.image=new Image,f.image.onload=n,f.image.onerror=o,f.image.src="data:image/svg+xml,"+(new XMLSerializer).serializeToString(e),f.image.complete===!0&&n(f.image)}):this.hasFabric().then(function(){return new d(function(n){window.html2canvas.svg.fabric.parseSVGDocument(e,f.createCanvas.call(f,n))})})}var o=e("./svgcontainer"),d=e("./promise");f.prototype=Object.create(o.prototype),n.exports=f},{"./promise":18,"./svgcontainer":26}],28:[function(e,n){function f(e,n){d.call(this,e,n)}function o(e,n,f){return e.length>0?n+f.toUpperCase():void 0}var d=e("./nodecontainer");f.prototype=Object.create(d.prototype),f.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},f.prototype.transform=function(e){var n=this.node.data;switch(e){case"lowercase":return n.toLowerCase();case"capitalize":return n.replace(/(^|\s|:|-|\(|\))([a-z])/g,o);case"uppercase":return n.toUpperCase();default:return n}},n.exports=f},{"./nodecontainer":16}],29:[function(e,n,f){f.smallImage=function(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"},f.bind=function(e,n){return function(){return e.apply(n,arguments)}},f.decode64=function(e){var n,f,o,d,i,t,l,s,u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=e.length,p="";for(n=0;a>n;n+=4)f=u.indexOf(e[n]),o=u.indexOf(e[n+1]),d=u.indexOf(e[n+2]),i=u.indexOf(e[n+3]),t=f<<2|o>>4,l=(15&o)<<4|d>>2,s=(3&d)<<6|i,p+=64===d?String.fromCharCode(t):64===i||-1===i?String.fromCharCode(t,l):String.fromCharCode(t,l,s);return p},f.getBounds=function(e){if(e.getBoundingClientRect){var n=e.getBoundingClientRect(),f=null==e.offsetWidth?n.width:e.offsetWidth;return{top:n.top,bottom:n.bottom||n.top+n.height,right:n.left+f,left:n.left,width:f,height:null==e.offsetHeight?n.height:e.offsetHeight}}return{}},f.offsetBounds=function(e){var n=e.offsetParent?f.offsetBounds(e.offsetParent):{top:0,left:0};return{top:e.offsetTop+n.top,bottom:e.offsetTop+e.offsetHeight+n.top,right:e.offsetLeft+n.left+e.offsetWidth,left:e.offsetLeft+n.left,width:e.offsetWidth,height:e.offsetHeight}},f.parseBackgrounds=function(e){var n,f,o,d,i,t,l,s=" \r\n	",u=[],a=0,p=0,c=function(){n&&('"'===f.substr(0,1)&&(f=f.substr(1,f.length-2)),f&&l.push(f),"-"===n.substr(0,1)&&(d=n.indexOf("-",1)+1)>0&&(o=n.substr(0,d),n=n.substr(d)),u.push({prefix:o,method:n.toLowerCase(),value:i,args:l,image:null})),l=[],n=o=f=i=""};return l=[],n=o=f=i="",e.split("").forEach(function(e){if(!(0===a&&s.indexOf(e)>-1)){switch(e){case'"':t?t===e&&(t=null):t=e;break;case"(":if(t)break;if(0===a)return a=1,void(i+=e);p++;break;case")":if(t)break;if(1===a){if(0===p)return a=0,i+=e,void c();p--}break;case",":if(t)break;if(0===a)return void c();if(1===a&&0===p&&!n.match(/^url$/i))return l.push(f),f="",void(i+=e)}i+=e,0===a?n+=e:f+=e}}),c(),u}},{}],30:[function(e,n){function f(e){o.apply(this,arguments),this.type="linear"===e.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}var o=e("./gradientcontainer");f.prototype=Object.create(o.prototype),n.exports=f},{"./gradientcontainer":11}],31:[function(e,n){function f(e){return new o(function(n,f){var o=new XMLHttpRequest;o.open("GET",e),o.onload=function(){200===o.status?n(o.responseText):f(new Error(o.statusText))},o.onerror=function(){f(new Error("Network Error"))},o.send()})}var o=e("./promise");n.exports=f},{"./promise":18}]},{},[6])(6)});
\ No newline at end of file
diff --git a/dist/html2canvas.svg.js b/dist/html2canvas.svg.js
deleted file mode 100644
index 53b1ad7..0000000
--- a/dist/html2canvas.svg.js
+++ /dev/null
@@ -1,19271 +0,0 @@
-/*
-  html2canvas 0.5.0-alpha2 <http://html2canvas.hertzen.com>
-  Copyright (c) 2015 Niklas von Hertzen
-
-  Released under MIT License
-*/
-
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),(n.html2canvas||(n.html2canvas={})).svg=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-
-},{}],2:[function(require,module,exports){
-/*!
- * The buffer module from node.js, for the browser.
- *
- * @author   Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
- * @license  MIT
- */
-
-var base64 = require('base64-js')
-var ieee754 = require('ieee754')
-var isArray = require('is-array')
-
-exports.Buffer = Buffer
-exports.SlowBuffer = SlowBuffer
-exports.INSPECT_MAX_BYTES = 50
-Buffer.poolSize = 8192 // not used by this implementation
-
-var kMaxLength = 0x3fffffff
-var rootParent = {}
-
-/**
- * If `Buffer.TYPED_ARRAY_SUPPORT`:
- *   === true    Use Uint8Array implementation (fastest)
- *   === false   Use Object implementation (most compatible, even IE6)
- *
- * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
- * Opera 11.6+, iOS 4.2+.
- *
- * Note:
- *
- * - Implementation must support adding new properties to `Uint8Array` instances.
- *   Firefox 4-29 lacked support, fixed in Firefox 30+.
- *   See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
- *
- *  - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
- *
- *  - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
- *    incorrect length in some situations.
- *
- * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will
- * get the Object implementation, which is slower but will work correctly.
- */
-Buffer.TYPED_ARRAY_SUPPORT = (function () {
-  try {
-    var buf = new ArrayBuffer(0)
-    var arr = new Uint8Array(buf)
-    arr.foo = function () { return 42 }
-    return 42 === arr.foo() && // typed array instances can be augmented
-        typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
-        new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
-  } catch (e) {
-    return false
-  }
-})()
-
-/**
- * Class: Buffer
- * =============
- *
- * The Buffer constructor returns instances of `Uint8Array` that are augmented
- * with function properties for all the node `Buffer` API functions. We use
- * `Uint8Array` so that square bracket notation works as expected -- it returns
- * a single octet.
- *
- * By augmenting the instances, we can avoid modifying the `Uint8Array`
- * prototype.
- */
-function Buffer (subject, encoding, noZero) {
-  if (!(this instanceof Buffer))
-    return new Buffer(subject, encoding, noZero)
-
-  var type = typeof subject
-
-  // Find the length
-  var length
-  if (type === 'number')
-    length = subject > 0 ? subject >>> 0 : 0
-  else if (type === 'string') {
-    length = Buffer.byteLength(subject, encoding)
-  } else if (type === 'object' && subject !== null) { // assume object is array-like
-    if (subject.type === 'Buffer' && isArray(subject.data))
-      subject = subject.data
-    length = +subject.length > 0 ? Math.floor(+subject.length) : 0
-  } else
-    throw new TypeError('must start with number, buffer, array or string')
-
-  if (length > kMaxLength)
-    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
-      'size: 0x' + kMaxLength.toString(16) + ' bytes')
-
-  var buf
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    // Preferred: Return an augmented `Uint8Array` instance for best performance
-    buf = Buffer._augment(new Uint8Array(length))
-  } else {
-    // Fallback: Return THIS instance of Buffer (created by `new`)
-    buf = this
-    buf.length = length
-    buf._isBuffer = true
-  }
-
-  var i
-  if (Buffer.TYPED_ARRAY_SUPPORT && typeof subject.byteLength === 'number') {
-    // Speed optimization -- use set if we're copying from a typed array
-    buf._set(subject)
-  } else if (isArrayish(subject)) {
-    // Treat array-ish objects as a byte array
-    if (Buffer.isBuffer(subject)) {
-      for (i = 0; i < length; i++)
-        buf[i] = subject.readUInt8(i)
-    } else {
-      for (i = 0; i < length; i++)
-        buf[i] = ((subject[i] % 256) + 256) % 256
-    }
-  } else if (type === 'string') {
-    buf.write(subject, 0, encoding)
-  } else if (type === 'number' && !Buffer.TYPED_ARRAY_SUPPORT && !noZero) {
-    for (i = 0; i < length; i++) {
-      buf[i] = 0
-    }
-  }
-
-  if (length > 0 && length <= Buffer.poolSize)
-    buf.parent = rootParent
-
-  return buf
-}
-
-function SlowBuffer(subject, encoding, noZero) {
-  if (!(this instanceof SlowBuffer))
-    return new SlowBuffer(subject, encoding, noZero)
-
-  var buf = new Buffer(subject, encoding, noZero)
-  delete buf.parent
-  return buf
-}
-
-Buffer.isBuffer = function (b) {
-  return !!(b != null && b._isBuffer)
-}
-
-Buffer.compare = function (a, b) {
-  if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b))
-    throw new TypeError('Arguments must be Buffers')
-
-  var x = a.length
-  var y = b.length
-  for (var i = 0, len = Math.min(x, y); i < len && a[i] === b[i]; i++) {}
-  if (i !== len) {
-    x = a[i]
-    y = b[i]
-  }
-  if (x < y) return -1
-  if (y < x) return 1
-  return 0
-}
-
-Buffer.isEncoding = function (encoding) {
-  switch (String(encoding).toLowerCase()) {
-    case 'hex':
-    case 'utf8':
-    case 'utf-8':
-    case 'ascii':
-    case 'binary':
-    case 'base64':
-    case 'raw':
-    case 'ucs2':
-    case 'ucs-2':
-    case 'utf16le':
-    case 'utf-16le':
-      return true
-    default:
-      return false
-  }
-}
-
-Buffer.concat = function (list, totalLength) {
-  if (!isArray(list)) throw new TypeError('Usage: Buffer.concat(list[, length])')
-
-  if (list.length === 0) {
-    return new Buffer(0)
-  } else if (list.length === 1) {
-    return list[0]
-  }
-
-  var i
-  if (totalLength === undefined) {
-    totalLength = 0
-    for (i = 0; i < list.length; i++) {
-      totalLength += list[i].length
-    }
-  }
-
-  var buf = new Buffer(totalLength)
-  var pos = 0
-  for (i = 0; i < list.length; i++) {
-    var item = list[i]
-    item.copy(buf, pos)
-    pos += item.length
-  }
-  return buf
-}
-
-Buffer.byteLength = function (str, encoding) {
-  var ret
-  str = str + ''
-  switch (encoding || 'utf8') {
-    case 'ascii':
-    case 'binary':
-    case 'raw':
-      ret = str.length
-      break
-    case 'ucs2':
-    case 'ucs-2':
-    case 'utf16le':
-    case 'utf-16le':
-      ret = str.length * 2
-      break
-    case 'hex':
-      ret = str.length >>> 1
-      break
-    case 'utf8':
-    case 'utf-8':
-      ret = utf8ToBytes(str).length
-      break
-    case 'base64':
-      ret = base64ToBytes(str).length
-      break
-    default:
-      ret = str.length
-  }
-  return ret
-}
-
-// pre-set for values that may exist in the future
-Buffer.prototype.length = undefined
-Buffer.prototype.parent = undefined
-
-// toString(encoding, start=0, end=buffer.length)
-Buffer.prototype.toString = function (encoding, start, end) {
-  var loweredCase = false
-
-  start = start >>> 0
-  end = end === undefined || end === Infinity ? this.length : end >>> 0
-
-  if (!encoding) encoding = 'utf8'
-  if (start < 0) start = 0
-  if (end > this.length) end = this.length
-  if (end <= start) return ''
-
-  while (true) {
-    switch (encoding) {
-      case 'hex':
-        return hexSlice(this, start, end)
-
-      case 'utf8':
-      case 'utf-8':
-        return utf8Slice(this, start, end)
-
-      case 'ascii':
-        return asciiSlice(this, start, end)
-
-      case 'binary':
-        return binarySlice(this, start, end)
-
-      case 'base64':
-        return base64Slice(this, start, end)
-
-      case 'ucs2':
-      case 'ucs-2':
-      case 'utf16le':
-      case 'utf-16le':
-        return utf16leSlice(this, start, end)
-
-      default:
-        if (loweredCase)
-          throw new TypeError('Unknown encoding: ' + encoding)
-        encoding = (encoding + '').toLowerCase()
-        loweredCase = true
-    }
-  }
-}
-
-Buffer.prototype.equals = function (b) {
-  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
-  return Buffer.compare(this, b) === 0
-}
-
-Buffer.prototype.inspect = function () {
-  var str = ''
-  var max = exports.INSPECT_MAX_BYTES
-  if (this.length > 0) {
-    str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
-    if (this.length > max)
-      str += ' ... '
-  }
-  return '<Buffer ' + str + '>'
-}
-
-Buffer.prototype.compare = function (b) {
-  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
-  return Buffer.compare(this, b)
-}
-
-// `get` will be removed in Node 0.13+
-Buffer.prototype.get = function (offset) {
-  console.log('.get() is deprecated. Access using array indexes instead.')
-  return this.readUInt8(offset)
-}
-
-// `set` will be removed in Node 0.13+
-Buffer.prototype.set = function (v, offset) {
-  console.log('.set() is deprecated. Access using array indexes instead.')
-  return this.writeUInt8(v, offset)
-}
-
-function hexWrite (buf, string, offset, length) {
-  offset = Number(offset) || 0
-  var remaining = buf.length - offset
-  if (!length) {
-    length = remaining
-  } else {
-    length = Number(length)
-    if (length > remaining) {
-      length = remaining
-    }
-  }
-
-  // must be an even number of digits
-  var strLen = string.length
-  if (strLen % 2 !== 0) throw new Error('Invalid hex string')
-
-  if (length > strLen / 2) {
-    length = strLen / 2
-  }
-  for (var i = 0; i < length; i++) {
-    var byte = parseInt(string.substr(i * 2, 2), 16)
-    if (isNaN(byte)) throw new Error('Invalid hex string')
-    buf[offset + i] = byte
-  }
-  return i
-}
-
-function utf8Write (buf, string, offset, length) {
-  var charsWritten = blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
-  return charsWritten
-}
-
-function asciiWrite (buf, string, offset, length) {
-  var charsWritten = blitBuffer(asciiToBytes(string), buf, offset, length)
-  return charsWritten
-}
-
-function binaryWrite (buf, string, offset, length) {
-  return asciiWrite(buf, string, offset, length)
-}
-
-function base64Write (buf, string, offset, length) {
-  var charsWritten = blitBuffer(base64ToBytes(string), buf, offset, length)
-  return charsWritten
-}
-
-function utf16leWrite (buf, string, offset, length) {
-  var charsWritten = blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length, 2)
-  return charsWritten
-}
-
-Buffer.prototype.write = function (string, offset, length, encoding) {
-  // Support both (string, offset, length, encoding)
-  // and the legacy (string, encoding, offset, length)
-  if (isFinite(offset)) {
-    if (!isFinite(length)) {
-      encoding = length
-      length = undefined
-    }
-  } else {  // legacy
-    var swap = encoding
-    encoding = offset
-    offset = length
-    length = swap
-  }
-
-  offset = Number(offset) || 0
-
-  if (length < 0 || offset < 0 || offset > this.length)
-    throw new RangeError('attempt to write outside buffer bounds');
-
-  var remaining = this.length - offset
-  if (!length) {
-    length = remaining
-  } else {
-    length = Number(length)
-    if (length > remaining) {
-      length = remaining
-    }
-  }
-  encoding = String(encoding || 'utf8').toLowerCase()
-
-  var ret
-  switch (encoding) {
-    case 'hex':
-      ret = hexWrite(this, string, offset, length)
-      break
-    case 'utf8':
-    case 'utf-8':
-      ret = utf8Write(this, string, offset, length)
-      break
-    case 'ascii':
-      ret = asciiWrite(this, string, offset, length)
-      break
-    case 'binary':
-      ret = binaryWrite(this, string, offset, length)
-      break
-    case 'base64':
-      ret = base64Write(this, string, offset, length)
-      break
-    case 'ucs2':
-    case 'ucs-2':
-    case 'utf16le':
-    case 'utf-16le':
-      ret = utf16leWrite(this, string, offset, length)
-      break
-    default:
-      throw new TypeError('Unknown encoding: ' + encoding)
-  }
-  return ret
-}
-
-Buffer.prototype.toJSON = function () {
-  return {
-    type: 'Buffer',
-    data: Array.prototype.slice.call(this._arr || this, 0)
-  }
-}
-
-function base64Slice (buf, start, end) {
-  if (start === 0 && end === buf.length) {
-    return base64.fromByteArray(buf)
-  } else {
-    return base64.fromByteArray(buf.slice(start, end))
-  }
-}
-
-function utf8Slice (buf, start, end) {
-  var res = ''
-  var tmp = ''
-  end = Math.min(buf.length, end)
-
-  for (var i = start; i < end; i++) {
-    if (buf[i] <= 0x7F) {
-      res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
-      tmp = ''
-    } else {
-      tmp += '%' + buf[i].toString(16)
-    }
-  }
-
-  return res + decodeUtf8Char(tmp)
-}
-
-function asciiSlice (buf, start, end) {
-  var ret = ''
-  end = Math.min(buf.length, end)
-
-  for (var i = start; i < end; i++) {
-    ret += String.fromCharCode(buf[i] & 0x7F)
-  }
-  return ret
-}
-
-function binarySlice (buf, start, end) {
-  var ret = ''
-  end = Math.min(buf.length, end)
-
-  for (var i = start; i < end; i++) {
-    ret += String.fromCharCode(buf[i])
-  }
-  return ret
-}
-
-function hexSlice (buf, start, end) {
-  var len = buf.length
-
-  if (!start || start < 0) start = 0
-  if (!end || end < 0 || end > len) end = len
-
-  var out = ''
-  for (var i = start; i < end; i++) {
-    out += toHex(buf[i])
-  }
-  return out
-}
-
-function utf16leSlice (buf, start, end) {
-  var bytes = buf.slice(start, end)
-  var res = ''
-  for (var i = 0; i < bytes.length; i += 2) {
-    res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
-  }
-  return res
-}
-
-Buffer.prototype.slice = function (start, end) {
-  var len = this.length
-  start = ~~start
-  end = end === undefined ? len : ~~end
-
-  if (start < 0) {
-    start += len;
-    if (start < 0)
-      start = 0
-  } else if (start > len) {
-    start = len
-  }
-
-  if (end < 0) {
-    end += len
-    if (end < 0)
-      end = 0
-  } else if (end > len) {
-    end = len
-  }
-
-  if (end < start)
-    end = start
-
-  var newBuf
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    newBuf = Buffer._augment(this.subarray(start, end))
-  } else {
-    var sliceLen = end - start
-    newBuf = new Buffer(sliceLen, undefined, true)
-    for (var i = 0; i < sliceLen; i++) {
-      newBuf[i] = this[i + start]
-    }
-  }
-
-  if (newBuf.length)
-    newBuf.parent = this.parent || this
-
-  return newBuf
-}
-
-/*
- * Need to make sure that buffer isn't trying to write out of bounds.
- */
-function checkOffset (offset, ext, length) {
-  if ((offset % 1) !== 0 || offset < 0)
-    throw new RangeError('offset is not uint')
-  if (offset + ext > length)
-    throw new RangeError('Trying to access beyond buffer length')
-}
-
-Buffer.prototype.readUIntLE = function (offset, byteLength, noAssert) {
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkOffset(offset, byteLength, this.length)
-
-  var val = this[offset]
-  var mul = 1
-  var i = 0
-  while (++i < byteLength && (mul *= 0x100))
-    val += this[offset + i] * mul
-
-  return val
-}
-
-Buffer.prototype.readUIntBE = function (offset, byteLength, noAssert) {
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkOffset(offset, byteLength, this.length)
-
-  var val = this[offset + --byteLength]
-  var mul = 1
-  while (byteLength > 0 && (mul *= 0x100))
-    val += this[offset + --byteLength] * mul;
-
-  return val
-}
-
-Buffer.prototype.readUInt8 = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 1, this.length)
-  return this[offset]
-}
-
-Buffer.prototype.readUInt16LE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 2, this.length)
-  return this[offset] | (this[offset + 1] << 8)
-}
-
-Buffer.prototype.readUInt16BE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 2, this.length)
-  return (this[offset] << 8) | this[offset + 1]
-}
-
-Buffer.prototype.readUInt32LE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-
-  return ((this[offset]) |
-      (this[offset + 1] << 8) |
-      (this[offset + 2] << 16)) +
-      (this[offset + 3] * 0x1000000)
-}
-
-Buffer.prototype.readUInt32BE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-
-  return (this[offset] * 0x1000000) +
-      ((this[offset + 1] << 16) |
-      (this[offset + 2] << 8) |
-      this[offset + 3])
-}
-
-Buffer.prototype.readIntLE = function (offset, byteLength, noAssert) {
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkOffset(offset, byteLength, this.length)
-
-  var val = this[offset]
-  var mul = 1
-  var i = 0
-  while (++i < byteLength && (mul *= 0x100))
-    val += this[offset + i] * mul
-  mul *= 0x80
-
-  if (val >= mul)
-    val -= Math.pow(2, 8 * byteLength)
-
-  return val
-}
-
-Buffer.prototype.readIntBE = function (offset, byteLength, noAssert) {
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkOffset(offset, byteLength, this.length)
-
-  var i = byteLength
-  var mul = 1
-  var val = this[offset + --i]
-  while (i > 0 && (mul *= 0x100))
-    val += this[offset + --i] * mul
-  mul *= 0x80
-
-  if (val >= mul)
-    val -= Math.pow(2, 8 * byteLength)
-
-  return val
-}
-
-Buffer.prototype.readInt8 = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 1, this.length)
-  if (!(this[offset] & 0x80))
-    return (this[offset])
-  return ((0xff - this[offset] + 1) * -1)
-}
-
-Buffer.prototype.readInt16LE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 2, this.length)
-  var val = this[offset] | (this[offset + 1] << 8)
-  return (val & 0x8000) ? val | 0xFFFF0000 : val
-}
-
-Buffer.prototype.readInt16BE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 2, this.length)
-  var val = this[offset + 1] | (this[offset] << 8)
-  return (val & 0x8000) ? val | 0xFFFF0000 : val
-}
-
-Buffer.prototype.readInt32LE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-
-  return (this[offset]) |
-      (this[offset + 1] << 8) |
-      (this[offset + 2] << 16) |
-      (this[offset + 3] << 24)
-}
-
-Buffer.prototype.readInt32BE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-
-  return (this[offset] << 24) |
-      (this[offset + 1] << 16) |
-      (this[offset + 2] << 8) |
-      (this[offset + 3])
-}
-
-Buffer.prototype.readFloatLE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-  return ieee754.read(this, offset, true, 23, 4)
-}
-
-Buffer.prototype.readFloatBE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 4, this.length)
-  return ieee754.read(this, offset, false, 23, 4)
-}
-
-Buffer.prototype.readDoubleLE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 8, this.length)
-  return ieee754.read(this, offset, true, 52, 8)
-}
-
-Buffer.prototype.readDoubleBE = function (offset, noAssert) {
-  if (!noAssert)
-    checkOffset(offset, 8, this.length)
-  return ieee754.read(this, offset, false, 52, 8)
-}
-
-function checkInt (buf, value, offset, ext, max, min) {
-  if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance')
-  if (value > max || value < min) throw new RangeError('value is out of bounds')
-  if (offset + ext > buf.length) throw new RangeError('index out of range')
-}
-
-Buffer.prototype.writeUIntLE = function (value, offset, byteLength, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
-
-  var mul = 1
-  var i = 0
-  this[offset] = value & 0xFF
-  while (++i < byteLength && (mul *= 0x100))
-    this[offset + i] = (value / mul) >>> 0 & 0xFF
-
-  return offset + byteLength
-}
-
-Buffer.prototype.writeUIntBE = function (value, offset, byteLength, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  byteLength = byteLength >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0)
-
-  var i = byteLength - 1
-  var mul = 1
-  this[offset + i] = value & 0xFF
-  while (--i >= 0 && (mul *= 0x100))
-    this[offset + i] = (value / mul) >>> 0 & 0xFF
-
-  return offset + byteLength
-}
-
-Buffer.prototype.writeUInt8 = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 1, 0xff, 0)
-  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
-  this[offset] = value
-  return offset + 1
-}
-
-function objectWriteUInt16 (buf, value, offset, littleEndian) {
-  if (value < 0) value = 0xffff + value + 1
-  for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) {
-    buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
-      (littleEndian ? i : 1 - i) * 8
-  }
-}
-
-Buffer.prototype.writeUInt16LE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 2, 0xffff, 0)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = value
-    this[offset + 1] = (value >>> 8)
-  } else objectWriteUInt16(this, value, offset, true)
-  return offset + 2
-}
-
-Buffer.prototype.writeUInt16BE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 2, 0xffff, 0)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = (value >>> 8)
-    this[offset + 1] = value
-  } else objectWriteUInt16(this, value, offset, false)
-  return offset + 2
-}
-
-function objectWriteUInt32 (buf, value, offset, littleEndian) {
-  if (value < 0) value = 0xffffffff + value + 1
-  for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) {
-    buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
-  }
-}
-
-Buffer.prototype.writeUInt32LE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 4, 0xffffffff, 0)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset + 3] = (value >>> 24)
-    this[offset + 2] = (value >>> 16)
-    this[offset + 1] = (value >>> 8)
-    this[offset] = value
-  } else objectWriteUInt32(this, value, offset, true)
-  return offset + 4
-}
-
-Buffer.prototype.writeUInt32BE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 4, 0xffffffff, 0)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = (value >>> 24)
-    this[offset + 1] = (value >>> 16)
-    this[offset + 2] = (value >>> 8)
-    this[offset + 3] = value
-  } else objectWriteUInt32(this, value, offset, false)
-  return offset + 4
-}
-
-Buffer.prototype.writeIntLE = function (value, offset, byteLength, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert) {
-    checkInt(this,
-             value,
-             offset,
-             byteLength,
-             Math.pow(2, 8 * byteLength - 1) - 1,
-             -Math.pow(2, 8 * byteLength - 1))
-  }
-
-  var i = 0
-  var mul = 1
-  var sub = value < 0 ? 1 : 0
-  this[offset] = value & 0xFF
-  while (++i < byteLength && (mul *= 0x100))
-    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
-
-  return offset + byteLength
-}
-
-Buffer.prototype.writeIntBE = function (value, offset, byteLength, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert) {
-    checkInt(this,
-             value,
-             offset,
-             byteLength,
-             Math.pow(2, 8 * byteLength - 1) - 1,
-             -Math.pow(2, 8 * byteLength - 1))
-  }
-
-  var i = byteLength - 1
-  var mul = 1
-  var sub = value < 0 ? 1 : 0
-  this[offset + i] = value & 0xFF
-  while (--i >= 0 && (mul *= 0x100))
-    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
-
-  return offset + byteLength
-}
-
-Buffer.prototype.writeInt8 = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 1, 0x7f, -0x80)
-  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
-  if (value < 0) value = 0xff + value + 1
-  this[offset] = value
-  return offset + 1
-}
-
-Buffer.prototype.writeInt16LE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 2, 0x7fff, -0x8000)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = value
-    this[offset + 1] = (value >>> 8)
-  } else objectWriteUInt16(this, value, offset, true)
-  return offset + 2
-}
-
-Buffer.prototype.writeInt16BE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 2, 0x7fff, -0x8000)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = (value >>> 8)
-    this[offset + 1] = value
-  } else objectWriteUInt16(this, value, offset, false)
-  return offset + 2
-}
-
-Buffer.prototype.writeInt32LE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = value
-    this[offset + 1] = (value >>> 8)
-    this[offset + 2] = (value >>> 16)
-    this[offset + 3] = (value >>> 24)
-  } else objectWriteUInt32(this, value, offset, true)
-  return offset + 4
-}
-
-Buffer.prototype.writeInt32BE = function (value, offset, noAssert) {
-  value = +value
-  offset = offset >>> 0
-  if (!noAssert)
-    checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
-  if (value < 0) value = 0xffffffff + value + 1
-  if (Buffer.TYPED_ARRAY_SUPPORT) {
-    this[offset] = (value >>> 24)
-    this[offset + 1] = (value >>> 16)
-    this[offset + 2] = (value >>> 8)
-    this[offset + 3] = value
-  } else objectWriteUInt32(this, value, offset, false)
-  return offset + 4
-}
-
-function checkIEEE754 (buf, value, offset, ext, max, min) {
-  if (value > max || value < min) throw new RangeError('value is out of bounds')
-  if (offset + ext > buf.length) throw new RangeError('index out of range')
-  if (offset < 0) throw new RangeError('index out of range')
-}
-
-function writeFloat (buf, value, offset, littleEndian, noAssert) {
-  if (!noAssert)
-    checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
-  ieee754.write(buf, value, offset, littleEndian, 23, 4)
-  return offset + 4
-}
-
-Buffer.prototype.writeFloatLE = function (value, offset, noAssert) {
-  return writeFloat(this, value, offset, true, noAssert)
-}
-
-Buffer.prototype.writeFloatBE = function (value, offset, noAssert) {
-  return writeFloat(this, value, offset, false, noAssert)
-}
-
-function writeDouble (buf, value, offset, littleEndian, noAssert) {
-  if (!noAssert)
-    checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
-  ieee754.write(buf, value, offset, littleEndian, 52, 8)
-  return offset + 8
-}
-
-Buffer.prototype.writeDoubleLE = function (value, offset, noAssert) {
-  return writeDouble(this, value, offset, true, noAssert)
-}
-
-Buffer.prototype.writeDoubleBE = function (value, offset, noAssert) {
-  return writeDouble(this, value, offset, false, noAssert)
-}
-
-// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
-Buffer.prototype.copy = function (target, target_start, start, end) {
-  var source = this
-
-  if (!start) start = 0
-  if (!end && end !== 0) end = this.length
-  if (target_start >= target.length) target_start = target.length
-  if (!target_start) target_start = 0
-  if (end > 0 && end < start) end = start
-
-  // Copy 0 bytes; we're done
-  if (end === start) return 0
-  if (target.length === 0 || source.length === 0) return 0
-
-  // Fatal error conditions
-  if (target_start < 0)
-    throw new RangeError('targetStart out of bounds')
-  if (start < 0 || start >= source.length) throw new RangeError('sourceStart out of bounds')
-  if (end < 0) throw new RangeError('sourceEnd out of bounds')
-
-  // Are we oob?
-  if (end > this.length)
-    end = this.length
-  if (target.length - target_start < end - start)
-    end = target.length - target_start + start
-
-  var len = end - start
-
-  if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
-    for (var i = 0; i < len; i++) {
-      target[i + target_start] = this[i + start]
-    }
-  } else {
-    target._set(this.subarray(start, start + len), target_start)
-  }
-
-  return len
-}
-
-// fill(value, start=0, end=buffer.length)
-Buffer.prototype.fill = function (value, start, end) {
-  if (!value) value = 0
-  if (!start) start = 0
-  if (!end) end = this.length
-
-  if (end < start) throw new RangeError('end < start')
-
-  // Fill 0 bytes; we're done
-  if (end === start) return
-  if (this.length === 0) return
-
-  if (start < 0 || start >= this.length) throw new RangeError('start out of bounds')
-  if (end < 0 || end > this.length) throw new RangeError('end out of bounds')
-
-  var i
-  if (typeof value === 'number') {
-    for (i = start; i < end; i++) {
-      this[i] = value
-    }
-  } else {
-    var bytes = utf8ToBytes(value.toString())
-    var len = bytes.length
-    for (i = start; i < end; i++) {
-      this[i] = bytes[i % len]
-    }
-  }
-
-  return this
-}
-
-/**
- * Creates a new `ArrayBuffer` with the *copied* memory of the buffer instance.
- * Added in Node 0.12. Only available in browsers that support ArrayBuffer.
- */
-Buffer.prototype.toArrayBuffer = function () {
-  if (typeof Uint8Array !== 'undefined') {
-    if (Buffer.TYPED_ARRAY_SUPPORT) {
-      return (new Buffer(this)).buffer
-    } else {
-      var buf = new Uint8Array(this.length)
-      for (var i = 0, len = buf.length; i < len; i += 1) {
-        buf[i] = this[i]
-      }
-      return buf.buffer
-    }
-  } else {
-    throw new TypeError('Buffer.toArrayBuffer not supported in this browser')
-  }
-}
-
-// HELPER FUNCTIONS
-// ================
-
-var BP = Buffer.prototype
-
-/**
- * Augment a Uint8Array *instance* (not the Uint8Array class!) with Buffer methods
- */
-Buffer._augment = function (arr) {
-  arr.constructor = Buffer
-  arr._isBuffer = true
-
-  // save reference to original Uint8Array get/set methods before overwriting
-  arr._get = arr.get
-  arr._set = arr.set
-
-  // deprecated, will be removed in node 0.13+
-  arr.get = BP.get
-  arr.set = BP.set
-
-  arr.write = BP.write
-  arr.toString = BP.toString
-  arr.toLocaleString = BP.toString
-  arr.toJSON = BP.toJSON
-  arr.equals = BP.equals
-  arr.compare = BP.compare
-  arr.copy = BP.copy
-  arr.slice = BP.slice
-  arr.readUIntLE = BP.readUIntLE
-  arr.readUIntBE = BP.readUIntBE
-  arr.readUInt8 = BP.readUInt8
-  arr.readUInt16LE = BP.readUInt16LE
-  arr.readUInt16BE = BP.readUInt16BE
-  arr.readUInt32LE = BP.readUInt32LE
-  arr.readUInt32BE = BP.readUInt32BE
-  arr.readIntLE = BP.readIntLE
-  arr.readIntBE = BP.readIntBE
-  arr.readInt8 = BP.readInt8
-  arr.readInt16LE = BP.readInt16LE
-  arr.readInt16BE = BP.readInt16BE
-  arr.readInt32LE = BP.readInt32LE
-  arr.readInt32BE = BP.readInt32BE
-  arr.readFloatLE = BP.readFloatLE
-  arr.readFloatBE = BP.readFloatBE
-  arr.readDoubleLE = BP.readDoubleLE
-  arr.readDoubleBE = BP.readDoubleBE
-  arr.writeUInt8 = BP.writeUInt8
-  arr.writeUIntLE = BP.writeUIntLE
-  arr.writeUIntBE = BP.writeUIntBE
-  arr.writeUInt16LE = BP.writeUInt16LE
-  arr.writeUInt16BE = BP.writeUInt16BE
-  arr.writeUInt32LE = BP.writeUInt32LE
-  arr.writeUInt32BE = BP.writeUInt32BE
-  arr.writeIntLE = BP.writeIntLE
-  arr.writeIntBE = BP.writeIntBE
-  arr.writeInt8 = BP.writeInt8
-  arr.writeInt16LE = BP.writeInt16LE
-  arr.writeInt16BE = BP.writeInt16BE
-  arr.writeInt32LE = BP.writeInt32LE
-  arr.writeInt32BE = BP.writeInt32BE
-  arr.writeFloatLE = BP.writeFloatLE
-  arr.writeFloatBE = BP.writeFloatBE
-  arr.writeDoubleLE = BP.writeDoubleLE
-  arr.writeDoubleBE = BP.writeDoubleBE
-  arr.fill = BP.fill
-  arr.inspect = BP.inspect
-  arr.toArrayBuffer = BP.toArrayBuffer
-
-  return arr
-}
-
-var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g
-
-function base64clean (str) {
-  // Node strips out invalid characters like \n and \t from the string, base64-js does not
-  str = stringtrim(str).replace(INVALID_BASE64_RE, '')
-  // Node converts strings with length < 2 to ''
-  if (str.length < 2) return ''
-  // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
-  while (str.length % 4 !== 0) {
-    str = str + '='
-  }
-  return str
-}
-
-function stringtrim (str) {
-  if (str.trim) return str.trim()
-  return str.replace(/^\s+|\s+$/g, '')
-}
-
-function isArrayish (subject) {
-  return isArray(subject) || Buffer.isBuffer(subject) ||
-      subject && typeof subject === 'object' &&
-      typeof subject.length === 'number'
-}
-
-function toHex (n) {
-  if (n < 16) return '0' + n.toString(16)
-  return n.toString(16)
-}
-
-function utf8ToBytes(string, units) {
-  var codePoint, length = string.length
-  var leadSurrogate = null
-  units = units || Infinity
-  var bytes = []
-  var i = 0
-
-  for (; i<length; i++) {
-    codePoint = string.charCodeAt(i)
-
-    // is surrogate component
-    if (codePoint > 0xD7FF && codePoint < 0xE000) {
-
-      // last char was a lead
-      if (leadSurrogate) {
-
-        // 2 leads in a row
-        if (codePoint < 0xDC00) {
-          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
-          leadSurrogate = codePoint
-          continue
-        }
-
-        // valid surrogate pair
-        else {
-          codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
-          leadSurrogate = null
-        }
-      }
-
-      // no lead yet
-      else {
-
-        // unexpected trail
-        if (codePoint > 0xDBFF) {
-          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
-          continue
-        }
-
-        // unpaired lead
-        else if (i + 1 === length) {
-          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
-          continue
-        }
-
-        // valid lead
-        else {
-          leadSurrogate = codePoint
-          continue
-        }
-      }
-    }
-
-    // valid bmp char, but last char was a lead
-    else if (leadSurrogate) {
-      if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
-      leadSurrogate = null
-    }
-
-    // encode utf8
-    if (codePoint < 0x80) {
-      if ((units -= 1) < 0) break
-      bytes.push(codePoint)
-    }
-    else if (codePoint < 0x800) {
-      if ((units -= 2) < 0) break
-      bytes.push(
-        codePoint >> 0x6 | 0xC0,
-        codePoint & 0x3F | 0x80
-      );
-    }
-    else if (codePoint < 0x10000) {
-      if ((units -= 3) < 0) break
-      bytes.push(
-        codePoint >> 0xC | 0xE0,
-        codePoint >> 0x6 & 0x3F | 0x80,
-        codePoint & 0x3F | 0x80
-      );
-    }
-    else if (codePoint < 0x200000) {
-      if ((units -= 4) < 0) break
-      bytes.push(
-        codePoint >> 0x12 | 0xF0,
-        codePoint >> 0xC & 0x3F | 0x80,
-        codePoint >> 0x6 & 0x3F | 0x80,
-        codePoint & 0x3F | 0x80
-      );
-    }
-    else {
-      throw new Error('Invalid code point')
-    }
-  }
-
-  return bytes
-}
-
-function asciiToBytes (str) {
-  var byteArray = []
-  for (var i = 0; i < str.length; i++) {
-    // Node's code seems to be doing this and not & 0x7F..
-    byteArray.push(str.charCodeAt(i) & 0xFF)
-  }
-  return byteArray
-}
-
-function utf16leToBytes (str, units) {
-  var c, hi, lo
-  var byteArray = []
-  for (var i = 0; i < str.length; i++) {
-
-    if ((units -= 2) < 0) break
-
-    c = str.charCodeAt(i)
-    hi = c >> 8
-    lo = c % 256
-    byteArray.push(lo)
-    byteArray.push(hi)
-  }
-
-  return byteArray
-}
-
-function base64ToBytes (str) {
-  return base64.toByteArray(base64clean(str))
-}
-
-function blitBuffer (src, dst, offset, length, unitSize) {
-  if (unitSize) length -= length % unitSize;
-  for (var i = 0; i < length; i++) {
-    if ((i + offset >= dst.length) || (i >= src.length))
-      break
-    dst[i + offset] = src[i]
-  }
-  return i
-}
-
-function decodeUtf8Char (str) {
-  try {
-    return decodeURIComponent(str)
-  } catch (err) {
-    return String.fromCharCode(0xFFFD) // UTF 8 invalid char
-  }
-}
-
-},{"base64-js":3,"ieee754":4,"is-array":5}],3:[function(require,module,exports){
-var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
-
-;(function (exports) {
-	'use strict';
-
-  var Arr = (typeof Uint8Array !== 'undefined')
-    ? Uint8Array
-    : Array
-
-	var PLUS   = '+'.charCodeAt(0)
-	var SLASH  = '/'.charCodeAt(0)
-	var NUMBER = '0'.charCodeAt(0)
-	var LOWER  = 'a'.charCodeAt(0)
-	var UPPER  = 'A'.charCodeAt(0)
-	var PLUS_URL_SAFE = '-'.charCodeAt(0)
-	var SLASH_URL_SAFE = '_'.charCodeAt(0)
-
-	function decode (elt) {
-		var code = elt.charCodeAt(0)
-		if (code === PLUS ||
-		    code === PLUS_URL_SAFE)
-			return 62 // '+'
-		if (code === SLASH ||
-		    code === SLASH_URL_SAFE)
-			return 63 // '/'
-		if (code < NUMBER)
-			return -1 //no match
-		if (code < NUMBER + 10)
-			return code - NUMBER + 26 + 26
-		if (code < UPPER + 26)
-			return code - UPPER
-		if (code < LOWER + 26)
-			return code - LOWER + 26
-	}
-
-	function b64ToByteArray (b64) {
-		var i, j, l, tmp, placeHolders, arr
-
-		if (b64.length % 4 > 0) {
-			throw new Error('Invalid string. Length must be a multiple of 4')
-		}
-
-		// the number of equal signs (place holders)
-		// if there are two placeholders, than the two characters before it
-		// represent one byte
-		// if there is only one, then the three characters before it represent 2 bytes
-		// this is just a cheap hack to not do indexOf twice
-		var len = b64.length
-		placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
-
-		// base64 is 4/3 + up to two characters of the original data
-		arr = new Arr(b64.length * 3 / 4 - placeHolders)
-
-		// if there are placeholders, only get up to the last complete 4 chars
-		l = placeHolders > 0 ? b64.length - 4 : b64.length
-
-		var L = 0
-
-		function push (v) {
-			arr[L++] = v
-		}
-
-		for (i = 0, j = 0; i < l; i += 4, j += 3) {
-			tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
-			push((tmp & 0xFF0000) >> 16)
-			push((tmp & 0xFF00) >> 8)
-			push(tmp & 0xFF)
-		}
-
-		if (placeHolders === 2) {
-			tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
-			push(tmp & 0xFF)
-		} else if (placeHolders === 1) {
-			tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
-			push((tmp >> 8) & 0xFF)
-			push(tmp & 0xFF)
-		}
-
-		return arr
-	}
-
-	function uint8ToBase64 (uint8) {
-		var i,
-			extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
-			output = "",
-			temp, length
-
-		function encode (num) {
-			return lookup.charAt(num)
-		}
-
-		function tripletToBase64 (num) {
-			return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
-		}
-
-		// go through the array every three bytes, we'll deal with trailing stuff later
-		for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
-			temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
-			output += tripletToBase64(temp)
-		}
-
-		// pad the end with zeros, but make sure to not forget the extra bytes
-		switch (extraBytes) {
-			case 1:
-				temp = uint8[uint8.length - 1]
-				output += encode(temp >> 2)
-				output += encode((temp << 4) & 0x3F)
-				output += '=='
-				break
-			case 2:
-				temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
-				output += encode(temp >> 10)
-				output += encode((temp >> 4) & 0x3F)
-				output += encode((temp << 2) & 0x3F)
-				output += '='
-				break
-		}
-
-		return output
-	}
-
-	exports.toByteArray = b64ToByteArray
-	exports.fromByteArray = uint8ToBase64
-}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
-
-},{}],4:[function(require,module,exports){
-exports.read = function(buffer, offset, isLE, mLen, nBytes) {
-  var e, m,
-      eLen = nBytes * 8 - mLen - 1,
-      eMax = (1 << eLen) - 1,
-      eBias = eMax >> 1,
-      nBits = -7,
-      i = isLE ? (nBytes - 1) : 0,
-      d = isLE ? -1 : 1,
-      s = buffer[offset + i];
-
-  i += d;
-
-  e = s & ((1 << (-nBits)) - 1);
-  s >>= (-nBits);
-  nBits += eLen;
-  for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8);
-
-  m = e & ((1 << (-nBits)) - 1);
-  e >>= (-nBits);
-  nBits += mLen;
-  for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8);
-
-  if (e === 0) {
-    e = 1 - eBias;
-  } else if (e === eMax) {
-    return m ? NaN : ((s ? -1 : 1) * Infinity);
-  } else {
-    m = m + Math.pow(2, mLen);
-    e = e - eBias;
-  }
-  return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
-};
-
-exports.write = function(buffer, value, offset, isLE, mLen, nBytes) {
-  var e, m, c,
-      eLen = nBytes * 8 - mLen - 1,
-      eMax = (1 << eLen) - 1,
-      eBias = eMax >> 1,
-      rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0),
-      i = isLE ? 0 : (nBytes - 1),
-      d = isLE ? 1 : -1,
-      s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
-
-  value = Math.abs(value);
-
-  if (isNaN(value) || value === Infinity) {
-    m = isNaN(value) ? 1 : 0;
-    e = eMax;
-  } else {
-    e = Math.floor(Math.log(value) / Math.LN2);
-    if (value * (c = Math.pow(2, -e)) < 1) {
-      e--;
-      c *= 2;
-    }
-    if (e + eBias >= 1) {
-      value += rt / c;
-    } else {
-      value += rt * Math.pow(2, 1 - eBias);
-    }
-    if (value * c >= 2) {
-      e++;
-      c /= 2;
-    }
-
-    if (e + eBias >= eMax) {
-      m = 0;
-      e = eMax;
-    } else if (e + eBias >= 1) {
-      m = (value * c - 1) * Math.pow(2, mLen);
-      e = e + eBias;
-    } else {
-      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
-      e = 0;
-    }
-  }
-
-  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8);
-
-  e = (e << mLen) | m;
-  eLen += mLen;
-  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8);
-
-  buffer[offset + i - d] |= s * 128;
-};
-
-},{}],5:[function(require,module,exports){
-
-/**
- * isArray
- */
-
-var isArray = Array.isArray;
-
-/**
- * toString
- */
-
-var str = Object.prototype.toString;
-
-/**
- * Whether or not the given `val`
- * is an array.
- *
- * example:
- *
- *        isArray([]);
- *        // > true
- *        isArray(arguments);
- *        // > false
- *        isArray('');
- *        // > false
- *
- * @param {mixed} val
- * @return {bool}
- */
-
-module.exports = isArray || function (val) {
-  return !! val && '[object Array]' == str.call(val);
-};
-
-},{}],6:[function(require,module,exports){
-(function (Buffer){
-/* build: `node build.js modules=text,serialization,parser,gradient,pattern,shadow,freedrawing,image_filters,serialization no-es5-compat minifier=uglifyjs` */
-/*! Fabric.js Copyright 2008-2014, Printio (Juriy Zaytsev, Maxim Chernyak) */
-
-var fabric = fabric || { version: "1.4.11" };
-if (typeof exports !== 'undefined') {
-  exports.fabric = fabric;
-}
-
-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("<!DOCTYPE html><html><head></head><body></body></html>");
-
-  fabric.window = fabric.document.createWindow();
-}
-
-/**
- * True when in environment that supports touch events
- * @type boolean
- */
-fabric.isTouchSupported = "ontouchstart" in fabric.document.documentElement;
-
-/**
- * True when in environment that's probably Node.js
- * @type boolean
- */
-fabric.isLikelyNode = typeof Buffer !== 'undefined' &&
-                      typeof window === 'undefined';
-
-
-/**
- * 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"
-];
-
-/**
- * Pixel per Inch as a default value set to 96. Can be changed for more realistic conversion.
- */
-fabric.DPI = 96;
-
-
-/*!
- * Copyright (c) 2009 Simo Kinnunen.
- * Licensed under the MIT license.
- */
-
-var Cufon = (function() {
-
-  /** @ignore */
-  var api = function() {
-    return api.replace.apply(null, arguments);
-  };
-
-  /** @ignore */
-  var DOM = api.DOM = {
-
-    ready: (function() {
-
-      var complete = false, readyStatus = { loaded: 1, complete: 1 };
-
-      var queue = [], /** @ignore */ perform = function() {
-        if (complete) return;
-        complete = true;
-        for (var fn; fn = queue.shift(); fn());
-      };
-
-      // Gecko, Opera, WebKit r26101+
-
-      if (fabric.document.addEventListener) {
-        fabric.document.addEventListener('DOMContentLoaded', perform, false);
-        fabric.window.addEventListener('pageshow', perform, false); // For cached Gecko pages
-      }
-
-      // Old WebKit, Internet Explorer
-
-      if (!fabric.window.opera && fabric.document.readyState) (function() {
-        readyStatus[fabric.document.readyState] ? perform() : setTimeout(arguments.callee, 10);
-      })();
-
-      // Internet Explorer
-
-      if (fabric.document.readyState && fabric.document.createStyleSheet) (function() {
-        try {
-          fabric.document.body.doScroll('left');
-          perform();
-        }
-        catch (e) {
-          setTimeout(arguments.callee, 1);
-        }
-      })();
-
-      addEvent(fabric.window, 'load', perform); // Fallback
-
-      return function(listener) {
-        if (!arguments.length) perform();
-        else complete ? listener() : queue.push(listener);
-      };
-
-    })()
-
-  };
-
-  /** @ignore */
-  var CSS = api.CSS = /** @ignore */ {
-
-    /** @ignore */
-    Size: function(value, base) {
-
-      this.value = parseFloat(value);
-      this.unit = String(value).match(/[a-z%]*$/)[0] || 'px';
-
-      /** @ignore */
-      this.convert = function(value) {
-        return value / base * this.value;
-      };
-
-      /** @ignore */
-      this.convertFrom = function(value) {
-        return value / this.value * base;
-      };
-
-      /** @ignore */
-      this.toString = function() {
-        return this.value + this.unit;
-      };
-
-    },
-
-    /** @ignore */
-    getStyle: function(el) {
-      return new Style(el.style);
-      /*
-      var view = document.defaultView;
-      if (view && view.getComputedStyle) return new Style(view.getComputedStyle(el, null));
-      if (el.currentStyle) return new Style(el.currentStyle);
-      return new Style(el.style);
-      */
-    },
-
-    quotedList: cached(function(value) {
-      // doesn't work properly with empty quoted strings (""), but
-      // it's not worth the extra code.
-      var list = [], re = /\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g, match;
-      while (match = re.exec(value)) list.push(match[3] || match[1]);
-      return list;
-    }),
-
-    ready: (function() {
-
-      var complete = false;
-
-      var queue = [], perform = function() {
-        complete = true;
-        for (var fn; fn = queue.shift(); fn());
-      };
-
-      // Safari 2 does not include <style> elements in document.styleSheets.
-      // Safari 2 also does not support Object.prototype.propertyIsEnumerable.
-
-      var styleElements = Object.prototype.propertyIsEnumerable ? elementsByTagName('style') : { length: 0 };
-      var linkElements = elementsByTagName('link');
-
-      DOM.ready(function() {
-        // These checks are actually only needed for WebKit-based browsers, but don't really hurt other browsers.
-        var linkStyles = 0, link;
-        for (var i = 0, l = linkElements.length; link = linkElements[i], i < l; ++i) {
-          // WebKit does not load alternate stylesheets.
-          if (!link.disabled && link.rel.toLowerCase() == 'stylesheet') ++linkStyles;
-        }
-        if (fabric.document.styleSheets.length >= styleElements.length + linkStyles) perform();
-        else setTimeout(arguments.callee, 10);
-      });
-
-      return function(listener) {
-        if (complete) listener();
-        else queue.push(listener);
-      };
-
-    })(),
-
-    /** @ignore */
-    supports: function(property, value) {
-      var checker = fabric.document.createElement('span').style;
-      if (checker[property] === undefined) return false;
-      checker[property] = value;
-      return checker[property] === value;
-    },
-
-    /** @ignore */
-    textAlign: function(word, style, position, wordCount) {
-      if (style.get('textAlign') == 'right') {
-        if (position > 0) word = ' ' + word;
-      }
-      else if (position < wordCount - 1) word += ' ';
-      return word;
-    },
-
-    /** @ignore */
-    textDecoration: function(el, style) {
-      if (!style) style = this.getStyle(el);
-      var types = {
-        underline: null,
-        overline: null,
-        'line-through': null
-      };
-      for (var search = el; search.parentNode && search.parentNode.nodeType == 1; ) {
-        var foundAll = true;
-        for (var type in types) {
-          if (types[type]) continue;
-          if (style.get('textDecoration').indexOf(type) != -1) types[type] = style.get('color');
-          foundAll = false;
-        }
-        if (foundAll) break; // this is rather unlikely to happen
-        style = this.getStyle(search = search.parentNode);
-      }
-      return types;
-    },
-
-    textShadow: cached(function(value) {
-      if (value == 'none') return null;
-      var shadows = [], currentShadow = {}, result, offCount = 0;
-      var re = /(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/ig;
-      while (result = re.exec(value)) {
-        if (result[0] == ',') {
-          shadows.push(currentShadow);
-          currentShadow = {}, offCount = 0;
-        }
-        else if (result[1]) {
-          currentShadow.color = result[1];
-        }
-        else {
-          currentShadow[[ 'offX', 'offY', 'blur' ][offCount++]] = result[2];
-        }
-      }
-      shadows.push(currentShadow);
-      return shadows;
-    }),
-
-    color: cached(function(value) {
-      var parsed = {};
-      parsed.color = value.replace(/^rgba\((.*?),\s*([\d.]+)\)/, function($0, $1, $2) {
-        parsed.opacity = parseFloat($2);
-        return 'rgb(' + $1 + ')';
-      });
-      return parsed;
-    }),
-
-    /** @ignore */
-    textTransform: function(text, style) {
-      return text[{
-        uppercase: 'toUpperCase',
-        lowercase: 'toLowerCase'
-      }[style.get('textTransform')] || 'toString']();
-    }
-
-  };
-
-  function Font(data) {
-
-    var face = this.face = data.face;
-    this.glyphs = data.glyphs;
-    this.w = data.w;
-    this.baseSize = parseInt(face['units-per-em'], 10);
-
-    this.family = face['font-family'].toLowerCase();
-    this.weight = face['font-weight'];
-    this.style = face['font-style'] || 'normal';
-
-    this.viewBox = (function () {
-      var parts = face.bbox.split(/\s+/);
-      var box = {
-        minX: parseInt(parts[0], 10),
-        minY: parseInt(parts[1], 10),
-        maxX: parseInt(parts[2], 10),
-        maxY: parseInt(parts[3], 10)
-      };
-      box.width = box.maxX - box.minX,
-      box.height = box.maxY - box.minY;
-      /** @ignore */
-      box.toString = function() {
-        return [ this.minX, this.minY, this.width, this.height ].join(' ');
-      };
-      return box;
-    })();
-
-    this.ascent = -parseInt(face.ascent, 10);
-    this.descent = -parseInt(face.descent, 10);
-
-    this.height = -this.ascent + this.descent;
-
-  }
-
-  function FontFamily() {
-
-    var styles = {}, mapping = {
-      oblique: 'italic',
-      italic: 'oblique'
-    };
-
-    this.add = function(font) {
-      (styles[font.style] || (styles[font.style] = {}))[font.weight] = font;
-    };
-
-    /** @ignore */
-    this.get = function(style, weight) {
-      var weights = styles[style] || styles[mapping[style]]
-        || styles.normal || styles.italic || styles.oblique;
-      if (!weights) return null;
-      // we don't have to worry about "bolder" and "lighter"
-      // because IE's currentStyle returns a numeric value for it,
-      // and other browsers use the computed value anyway
-      weight = {
-        normal: 400,
-        bold: 700
-      }[weight] || parseInt(weight, 10);
-      if (weights[weight]) return weights[weight];
-      // http://www.w3.org/TR/CSS21/fonts.html#propdef-font-weight
-      // Gecko uses x99/x01 for lighter/bolder
-      var up = {
-        1: 1,
-        99: 0
-      }[weight % 100], alts = [], min, max;
-      if (up === undefined) up = weight > 400;
-      if (weight == 500) weight = 400;
-      for (var alt in weights) {
-        alt = parseInt(alt, 10);
-        if (!min || alt < min) min = alt;
-        if (!max || alt > max) max = alt;
-        alts.push(alt);
-      }
-      if (weight < min) weight = min;
-      if (weight > max) weight = max;
-      alts.sort(function(a, b) {
-        return (up
-          ? (a > weight && b > weight) ? a < b : a > b
-          : (a < weight && b < weight) ? a > b : a < b) ? -1 : 1;
-      });
-      return weights[alts[0]];
-    };
-
-  }
-
-  function HoverHandler() {
-
-    function contains(node, anotherNode) {
-      if (node.contains) return node.contains(anotherNode);
-      return node.compareDocumentPosition(anotherNode) & 16;
-    }
-
-    function onOverOut(e) {
-      var related = e.relatedTarget;
-      if (!related || contains(this, related)) return;
-      trigger(this);
-    }
-
-    function onEnterLeave(e) {
-      trigger(this);
-    }
-
-    function trigger(el) {
-      // A timeout is needed so that the event can actually "happen"
-      // before replace is triggered. This ensures that styles are up
-      // to date.
-      setTimeout(function() {
-        api.replace(el, sharedStorage.get(el).options, true);
-      }, 10);
-    }
-
-    this.attach = function(el) {
-      if (el.onmouseenter === undefined) {
-        addEvent(el, 'mouseover', onOverOut);
-        addEvent(el, 'mouseout', onOverOut);
-      }
-      else {
-        addEvent(el, 'mouseenter', onEnterLeave);
-        addEvent(el, 'mouseleave', onEnterLeave);
-      }
-    };
-
-  }
-
-  function Storage() {
-
-    var map = {}, at = 0;
-
-    function identify(el) {
-      return el.cufid || (el.cufid = ++at);
-    }
-
-    /** @ignore */
-    this.get = function(el) {
-      var id = identify(el);
-      return map[id] || (map[id] = {});
-    };
-
-  }
-
-  function Style(style) {
-
-    var custom = {}, sizes = {};
-
-    this.get = function(property) {
-      return custom[property] != undefined ? custom[property] : style[property];
-    };
-
-    this.getSize = function(property, base) {
-      return sizes[property] || (sizes[property] = new CSS.Size(this.get(property), base));
-    };
-
-    this.extend = function(styles) {
-      for (var property in styles) custom[property] = styles[property];
-      return this;
-    };
-
-  }
-
-  function addEvent(el, type, listener) {
-    if (el.addEventListener) {
-      el.addEventListener(type, listener, false);
-    }
-    else if (el.attachEvent) {
-      el.attachEvent('on' + type, function() {
-        return listener.call(el, fabric.window.event);
-      });
-    }
-  }
-
-  function attach(el, options) {
-    var storage = sharedStorage.get(el);
-    if (storage.options) return el;
-    if (options.hover && options.hoverables[el.nodeName.toLowerCase()]) {
-      hoverHandler.attach(el);
-    }
-    storage.options = options;
-    return el;
-  }
-
-  function cached(fun) {
-    var cache = {};
-    return function(key) {
-      if (!cache.hasOwnProperty(key)) cache[key] = fun.apply(null, arguments);
-      return cache[key];
-    };
-  }
-
-  function getFont(el, style) {
-    if (!style) style = CSS.getStyle(el);
-    var families = CSS.quotedList(style.get('fontFamily').toLowerCase()), family;
-    for (var i = 0, l = families.length; i < l; ++i) {
-      family = families[i];
-      if (fonts[family]) return fonts[family].get(style.get('fontStyle'), style.get('fontWeight'));
-    }
-    return null;
-  }
-
-  function elementsByTagName(query) {
-    return fabric.document.getElementsByTagName(query);
-  }
-
-  function merge() {
-    var merged = {}, key;
-    for (var i = 0, l = arguments.length; i < l; ++i) {
-      for (key in arguments[i]) merged[key] = arguments[i][key];
-    }
-    return merged;
-  }
-
-  function process(font, text, style, options, node, el) {
-
-    var separate = options.separate;
-    if (separate == 'none') return engines[options.engine].apply(null, arguments);
-    var fragment = fabric.document.createDocumentFragment(), processed;
-    var parts = text.split(separators[separate]), needsAligning = (separate == 'words');
-    if (needsAligning && HAS_BROKEN_REGEXP) {
-      // @todo figure out a better way to do this
-      if (/^\s/.test(text)) parts.unshift('');
-      if (/\s$/.test(text)) parts.push('');
-    }
-    for (var i = 0, l = parts.length; i < l; ++i) {
-      processed = engines[options.engine](font,
-        needsAligning ? CSS.textAlign(parts[i], style, i, l) : parts[i],
-        style, options, node, el, i < l - 1);
-      if (processed) fragment.appendChild(processed);
-    }
-    return fragment;
-  }
-
-  /** @ignore */
-  function replaceElement(el, options) {
-    var font, style, nextNode, redraw;
-    for (var node = attach(el, options).firstChild; node; node = nextNode) {
-      nextNode = node.nextSibling;
-      redraw = false;
-      if (node.nodeType == 1) {
-        if (!node.firstChild) continue;
-        if (!/cufon/.test(node.className)) {
-          arguments.callee(node, options);
-          continue;
-        }
-        else redraw = true;
-      }
-      if (!style) style = CSS.getStyle(el).extend(options);
-      if (!font) font = getFont(el, style);
-
-      if (!font) continue;
-      if (redraw) {
-        engines[options.engine](font, null, style, options, node, el);
-        continue;
-      }
-      var text = node.data;
-      //for some reason, the carriage return is not stripped by IE but "\n" is, so let's keep \r as a new line marker...
-      if (typeof G_vmlCanvasManager != 'undefined') {
-          text = text.replace(/\r/g, "\n");
-      }
-      if (text === '') continue;
-      var processed = process(font, text, style, options, node, el);
-      if (processed) node.parentNode.replaceChild(processed, node);
-      else node.parentNode.removeChild(node);
-    }
-  }
-
-  var HAS_BROKEN_REGEXP = ' '.split(/\s+/).length == 0;
-
-  var sharedStorage = new Storage();
-  var hoverHandler = new HoverHandler();
-  var replaceHistory = [];
-
-  var engines = {}, fonts = {}, defaultOptions = {
-    engine: null,
-    //fontScale: 1,
-    //fontScaling: false,
-    hover: false,
-    hoverables: {
-      a: true
-    },
-    printable: true,
-    //rotation: 0,
-    //selectable: false,
-    selector: (
-        fabric.window.Sizzle
-      ||  (fabric.window.jQuery && function(query) { return jQuery(query); }) // avoid noConflict issues
-      ||  (fabric.window.dojo && dojo.query)
-      ||  (fabric.window.$$ && function(query) { return $$(query); })
-      ||  (fabric.window.$ && function(query) { return $(query); })
-      ||  (fabric.document.querySelectorAll && function(query) { return fabric.document.querySelectorAll(query); })
-      ||  elementsByTagName
-    ),
-    separate: 'words', // 'none' and 'characters' are also accepted
-    textShadow: 'none'
-  };
-
-  var separators = {
-    words: /\s+/,
-    characters: ''
-  };
-
-  /** @ignore */
-  api.now = function() {
-    DOM.ready();
-    return api;
-  };
-
-  /** @ignore */
-  api.refresh = function() {
-    var currentHistory = replaceHistory.splice(0, replaceHistory.length);
-    for (var i = 0, l = currentHistory.length; i < l; ++i) {
-      api.replace.apply(null, currentHistory[i]);
-    }
-    return api;
-  };
-
-  /** @ignore */
-  api.registerEngine = function(id, engine) {
-    if (!engine) return api;
-    engines[id] = engine;
-    return api.set('engine', id);
-  };
-
-  /** @ignore */
-  api.registerFont = function(data) {
-    var font = new Font(data), family = font.family;
-    if (!fonts[family]) fonts[family] = new FontFamily();
-    fonts[family].add(font);
-    return api.set('fontFamily', '"' + family + '"');
-  };
-
-  /** @ignore */
-  api.replace = function(elements, options, ignoreHistory) {
-    options = merge(defaultOptions, options);
-    if (!options.engine) return api; // there's no browser support so we'll just stop here
-    if (typeof options.textShadow == 'string' && options.textShadow)
-      options.textShadow = CSS.textShadow(options.textShadow);
-    if (!ignoreHistory) replaceHistory.push(arguments);
-    if (elements.nodeType || typeof elements == 'string') elements = [ elements ];
-    CSS.ready(function() {
-      for (var i = 0, l = elements.length; i < l; ++i) {
-        var el = elements[i];
-        if (typeof el == 'string') api.replace(options.selector(el), options, true);
-        else replaceElement(el, options);
-      }
-    });
-    return api;
-  };
-
-  /** @ignore */
-  api.replaceElement = function(el, options) {
-    options = merge(defaultOptions, options);
-    if (typeof options.textShadow == 'string' && options.textShadow)
-      options.textShadow = CSS.textShadow(options.textShadow);
-    return replaceElement(el, options);
-  };
-
-  api.engines = engines;
-  api.fonts = fonts;
-  /** @ignore */
-  api.getOptions = function() {
-    return merge(defaultOptions);
-  };
-
-  /** @ignore */
-  api.set = function(option, value) {
-    defaultOptions[option] = value;
-    return api;
-  };
-
-  return api;
-
-})();
-
-Cufon.registerEngine('canvas', (function() {
-
-  // Safari 2 doesn't support .apply() on native methods
-  var HAS_INLINE_BLOCK = Cufon.CSS.supports('display', 'inline-block');
-
-  // Firefox 2 w/ non-strict doctype (almost standards mode)
-  var HAS_BROKEN_LINEHEIGHT = !HAS_INLINE_BLOCK && (fabric.document.compatMode == 'BackCompat' || /frameset|transitional/i.test(fabric.document.doctype.publicId));
-
-  var styleSheet = fabric.document.createElement('style');
-  styleSheet.type = 'text/css';
-
-    var textNode = fabric.document.createTextNode(
-        '.cufon-canvas{text-indent:0}' +
-        '@media screen,projection{' +
-          '.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle' +
-          (HAS_BROKEN_LINEHEIGHT
-            ? ''
-            : ';font-size:1px;line-height:1px') +
-          '}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}' +
-          (HAS_INLINE_BLOCK
-            ? '.cufon-canvas canvas{position:relative}'
-            : '.cufon-canvas canvas{position:absolute}') +
-        '}' +
-        '@media print{' +
-          '.cufon-canvas{padding:0 !important}' +
-          '.cufon-canvas canvas{display:none}' +
-          '.cufon-canvas .cufon-alt{display:inline}' +
-        '}'
-      )
-
-  try {
-      styleSheet.appendChild(textNode);
-  } catch(e) {
-      //IE8- can't do this...
-      styleSheet.setAttribute("type", "text/css");
-      styleSheet.styleSheet.cssText = textNode.data;
-  }
-  fabric.document.getElementsByTagName('head')[0].appendChild(styleSheet);
-
-  function generateFromVML(path, context) {
-    var atX = 0, atY = 0;
-    var code = [], re = /([mrvxe])([^a-z]*)/g, match;
-    generate: for (var i = 0; match = re.exec(path); ++i) {
-      var c = match[2].split(',');
-      switch (match[1]) {
-        case 'v':
-          code[i] = { m: 'bezierCurveTo', a: [ atX + ~~c[0], atY + ~~c[1], atX + ~~c[2], atY + ~~c[3], atX += ~~c[4], atY += ~~c[5] ] };
-          break;
-        case 'r':
-          code[i] = { m: 'lineTo', a: [ atX += ~~c[0], atY += ~~c[1] ] };
-          break;
-        case 'm':
-          code[i] = { m: 'moveTo', a: [ atX = ~~c[0], atY = ~~c[1] ] };
-          break;
-        case 'x':
-          code[i] = { m: 'closePath', a: [] };
-          break;
-        case 'e':
-          break generate;
-      }
-      context[code[i].m].apply(context, code[i].a);
-    }
-    return code;
-  }
-
-  function interpret(code, context) {
-    for (var i = 0, l = code.length; i < l; ++i) {
-      var line = code[i];
-      context[line.m].apply(context, line.a);
-    }
-  }
-
-  return function(font, text, style, options, node, el) {
-
-    var redraw = (text === null);
-
-    var viewBox = font.viewBox;
-
-    var size = style.getSize('fontSize', font.baseSize);
-
-    var letterSpacing = style.get('letterSpacing');
-    letterSpacing = (letterSpacing == 'normal') ? 0 : size.convertFrom(parseInt(letterSpacing, 10));
-
-    var expandTop = 0, expandRight = 0, expandBottom = 0, expandLeft = 0;
-    var shadows = options.textShadow, shadowOffsets = [];
-
-    Cufon.textOptions.shadowOffsets = [ ];
-    Cufon.textOptions.shadows = null;
-
-    if (shadows) {
-      Cufon.textOptions.shadows = shadows;
-      for (var i = 0, l = shadows.length; i < l; ++i) {
-        var shadow = shadows[i];
-        var x = size.convertFrom(parseFloat(shadow.offX));
-        var y = size.convertFrom(parseFloat(shadow.offY));
-        shadowOffsets[i] = [ x, y ];
-        //if (y < expandTop) expandTop = y;
-        //if (x > expandRight) expandRight = x;
-        //if (y > expandBottom) expandBottom = y;
-        //if (x < expandLeft) expandLeft = x;
-      }
-    }
-
-    var chars = Cufon.CSS.textTransform(redraw ? node.alt : text, style).split('');
-
-    var width = 0, lastWidth = null;
-
-    var maxWidth = 0, lines = 1, lineWidths = [ ];
-    for (var i = 0, l = chars.length; i < l; ++i) {
-      if (chars[i] === '\n') {
-        lines++;
-        if (width > maxWidth) {
-          maxWidth = width;
-        }
-        lineWidths.push(width);
-        width = 0;
-        continue;
-      }
-      var glyph = font.glyphs[chars[i]] || font.missingGlyph;
-      if (!glyph) continue;
-      width += lastWidth = Number(glyph.w || font.w) + letterSpacing;
-    }
-    lineWidths.push(width);
-
-    width = Math.max(maxWidth, width);
-
-    var lineOffsets = [ ];
-    for (var i = lineWidths.length; i--; ) {
-      lineOffsets[i] = width - lineWidths[i];
-    }
-
-    if (lastWidth === null) return null; // there's nothing to render
-
-    expandRight += (viewBox.width - lastWidth);
-    expandLeft += viewBox.minX;
-
-    var wrapper, canvas;
-
-    if (redraw) {
-      wrapper = node;
-      canvas = node.firstChild;
-    }
-    else {
-      wrapper = fabric.document.createElement('span');
-      wrapper.className = 'cufon cufon-canvas';
-      wrapper.alt = text;
-
-      canvas = fabric.document.createElement('canvas');
-      wrapper.appendChild(canvas);
-
-      if (options.printable) {
-        var print = fabric.document.createElement('span');
-        print.className = 'cufon-alt';
-        print.appendChild(fabric.document.createTextNode(text));
-        wrapper.appendChild(print);
-      }
-    }
-
-    var wStyle = wrapper.style;
-    var cStyle = canvas.style || { };
-
-    var height = size.convert(viewBox.height - expandTop + expandBottom);
-    var roundedHeight = Math.ceil(height);
-    var roundingFactor = roundedHeight / height;
-
-    canvas.width = Math.ceil(size.convert(width + expandRight - expandLeft) * roundingFactor);
-    canvas.height = roundedHeight;
-
-    expandTop += viewBox.minY;
-
-    cStyle.top = Math.round(size.convert(expandTop - font.ascent)) + 'px';
-    cStyle.left = Math.round(size.convert(expandLeft)) + 'px';
-
-    var _width = Math.ceil(size.convert(width * roundingFactor));
-    var wrapperWidth = _width + 'px';
-    var _height = size.convert(font.height);
-    var totalLineHeight = (options.lineHeight - 1) * size.convert(-font.ascent / 5) * (lines - 1);
-
-    Cufon.textOptions.width = _width;
-    Cufon.textOptions.height = (_height * lines) + totalLineHeight;
-    Cufon.textOptions.lines = lines;
-    Cufon.textOptions.totalLineHeight = totalLineHeight;
-
-    if (HAS_INLINE_BLOCK) {
-      wStyle.width = wrapperWidth;
-      wStyle.height = _height + 'px';
-    }
-    else {
-      wStyle.paddingLeft = wrapperWidth;
-      wStyle.paddingBottom = (_height - 1) + 'px';
-    }
-
-    var g = Cufon.textOptions.context || canvas.getContext('2d'),
-        scale = roundedHeight / viewBox.height;
-
-    Cufon.textOptions.fontAscent = font.ascent * scale;
-    Cufon.textOptions.boundaries = null;
-
-    for (var offsets = Cufon.textOptions.shadowOffsets, i = shadowOffsets.length; i--; ) {
-      offsets[i] = [ shadowOffsets[i][0] * scale, shadowOffsets[i][1] * scale ];
-    }
-
-    g.save();
-    g.scale(scale, scale);
-
-    g.translate(
-      // we're at the center of an object and need to jump to the top left corner
-      // where first character is to be drawn
-      -expandLeft - ((1/scale * canvas.width) / 2) + (Cufon.fonts[font.family].offsetLeft || 0),
-      -expandTop - ((Cufon.textOptions.height / scale) / 2) + (Cufon.fonts[font.family].offsetTop || 0)
-    );
-
-    g.lineWidth = font.face['underline-thickness'];
-
-    g.save();
-
-    function line(y, color) {
-      g.strokeStyle = color;
-
-      g.beginPath();
-
-      g.moveTo(0, y);
-      g.lineTo(width, y);
-
-      g.stroke();
-    }
-
-    var textDecoration = Cufon.getTextDecoration(options),
-        isItalic = options.fontStyle === 'italic';
-
-    function renderBackground() {
-      g.save();
-
-      var left = 0, lineNum = 0, boundaries = [{ left: 0 }];
-
-      if (options.backgroundColor) {
-        g.save();
-        g.fillStyle = options.backgroundColor;
-        g.translate(0, font.ascent);
-        g.fillRect(0, 0, width + 10, (-font.ascent + font.descent) * lines);
-        g.restore();
-      }
-
-      if (options.textAlign === 'right') {
-        g.translate(lineOffsets[lineNum], 0);
-        boundaries[0].left = lineOffsets[lineNum] * scale;
-      }
-      else if (options.textAlign === 'center') {
-        g.translate(lineOffsets[lineNum] / 2, 0);
-        boundaries[0].left = lineOffsets[lineNum] / 2 * scale;
-      }
-
-      for (var i = 0, l = chars.length; i < l; ++i) {
-        if (chars[i] === '\n') {
-
-          lineNum++;
-
-          var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
-          var boundary = boundaries[boundaries.length - 1];
-          var nextBoundary = { left: 0 };
-
-          boundary.width = left * scale;
-          boundary.height = (-font.ascent + font.descent) * scale;
-
-          if (options.textAlign === 'right') {
-            g.translate(-width, topOffset);
-            g.translate(lineOffsets[lineNum], 0);
-            nextBoundary.left = lineOffsets[lineNum] * scale;
-          }
-          else if (options.textAlign === 'center') {
-            // offset to the start of text in previous line AND half of its offset
-            // (essentially moving caret to the left edge of bounding box)
-            g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
-            g.translate(lineOffsets[lineNum] / 2, 0);
-            nextBoundary.left = lineOffsets[lineNum] / 2 * scale;
-          }
-          else {
-            g.translate(-left, topOffset);
-          }
-
-          /* push next boundary (for the next line) */
-          boundaries.push(nextBoundary);
-
-          left = 0;
-
-          continue;
-        }
-        var glyph = font.glyphs[chars[i]] || font.missingGlyph;
-        if (!glyph) continue;
-
-        var charWidth = Number(glyph.w || font.w) + letterSpacing;
-
-        // only draw text-background when there's some kind of value
-        if (options.textBackgroundColor) {
-          g.save();
-          g.fillStyle = options.textBackgroundColor;
-          g.translate(0, font.ascent);
-          g.fillRect(0, 0, charWidth + 10, -font.ascent + font.descent);
-          g.restore();
-        }
-
-        g.translate(charWidth, 0);
-        left += charWidth;
-
-        if (i == l-1) {
-          boundaries[boundaries.length - 1].width = left * scale;
-          boundaries[boundaries.length - 1].height = (-font.ascent + font.descent) * scale;
-        }
-      }
-      g.restore();
-
-      Cufon.textOptions.boundaries = boundaries;
-    }
-
-    function renderText(color) {
-      g.fillStyle = color || Cufon.textOptions.color || style.get('color');
-
-      var left = 0, lineNum = 0;
-
-      if (options.textAlign === 'right') {
-        g.translate(lineOffsets[lineNum], 0);
-      }
-      else if (options.textAlign === 'center') {
-        g.translate(lineOffsets[lineNum] / 2, 0);
-      }
-
-      for (var i = 0, l = chars.length; i < l; ++i) {
-        if (chars[i] === '\n') {
-
-          lineNum++;
-
-          var topOffset = -font.ascent - ((font.ascent / 5) * options.lineHeight);
-
-          if (options.textAlign === 'right') {
-            g.translate(-width, topOffset);
-            g.translate(lineOffsets[lineNum], 0);
-          }
-          else if (options.textAlign === 'center') {
-            // offset to the start of text in previous line AND half of its offset
-            // (essentially moving caret to the left edge of bounding box)
-            g.translate(-left - (lineOffsets[lineNum - 1] / 2), topOffset);
-            g.translate(lineOffsets[lineNum] / 2, 0);
-          }
-          else {
-            g.translate(-left, topOffset);
-          }
-
-          left = 0;
-
-          continue;
-        }
-        var glyph = font.glyphs[chars[i]] || font.missingGlyph;
-        if (!glyph) continue;
-
-        var charWidth = Number(glyph.w || font.w) + letterSpacing;
-
-        if (textDecoration) {
-          g.save();
-          g.strokeStyle = g.fillStyle;
-
-          // add 2x more thickness — closer to SVG rendering
-          g.lineWidth += g.lineWidth;
-
-          g.beginPath();
-          if (textDecoration.underline) {
-            g.moveTo(0, -font.face['underline-position'] + 0.5);
-            g.lineTo(charWidth, -font.face['underline-position'] + 0.5);
-          }
-          if (textDecoration.overline) {
-            g.moveTo(0, font.ascent + 0.5);
-            g.lineTo(charWidth, font.ascent + 0.5);
-          }
-          if (textDecoration['line-through']) {
-            g.moveTo(0, -font.descent + 0.5);
-            g.lineTo(charWidth, -font.descent + 0.5);
-          }
-          g.stroke();
-          g.restore();
-        }
-
-        if (isItalic) {
-          g.save();
-          g.transform(1, 0, -0.25, 1, 0, 0);
-        }
-
-        g.beginPath();
-        if (glyph.d) {
-          if (glyph.code) interpret(glyph.code, g);
-          else glyph.code = generateFromVML('m' + glyph.d, g);
-        }
-
-        g.fill();
-
-        if (options.strokeStyle) {
-          g.closePath();
-          g.save();
-          g.lineWidth = options.strokeWidth;
-          g.strokeStyle = options.strokeStyle;
-          g.stroke();
-          g.restore();
-        }
-
-        if (isItalic) {
-          g.restore();
-        }
-
-        g.translate(charWidth, 0);
-        left += charWidth;
-      }
-    }
-
-    g.save();
-    renderBackground();
-    if (shadows) {
-      for (var i = 0, l = shadows.length; i < l; ++i) {
-        var shadow = shadows[i];
-        g.save();
-        g.translate.apply(g, shadowOffsets[i]);
-        renderText(shadow.color);
-        g.restore();
-      }
-    }
-    renderText();
-    g.restore();
-    g.restore();
-    g.restore();
-
-    return wrapper;
-
-  };
-
-})());
-
-Cufon.registerEngine('vml', (function() {
-
-  if (!fabric.document.namespaces) return;
-
-  var canvasEl = fabric.document.createElement('canvas');
-  if (canvasEl && canvasEl.getContext && canvasEl.getContext.apply) return;
-
-  if (fabric.document.namespaces.cvml == null) {
-    fabric.document.namespaces.add('cvml', 'urn:schemas-microsoft-com:vml');
-  }
-
-  var check = fabric.document.createElement('cvml:shape');
-  check.style.behavior = 'url(#default#VML)';
-  if (!check.coordsize) return; // VML isn't supported
-  check = null;
-
-  fabric.document.write('<style type="text/css">' +
-    '.cufon-vml-canvas{text-indent:0}' +
-    '@media screen{' +
-      'cvml\\:shape,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute}' +
-      '.cufon-vml-canvas{position:absolute;text-align:left}' +
-      '.cufon-vml{display:inline-block;position:relative;vertical-align:middle}' +
-      '.cufon-vml .cufon-alt{position:absolute;left:-10000in;font-size:1px}' +
-      'a .cufon-vml{cursor:pointer}' +
-    '}' +
-    '@media print{' +
-      '.cufon-vml *{display:none}' +
-      '.cufon-vml .cufon-alt{display:inline}' +
-    '}' +
-  '</style>');
-
-  function getFontSizeInPixels(el, value) {
-    return getSizeInPixels(el, /(?:em|ex|%)$/i.test(value) ? '1em' : value);
-  }
-
-  // Original by Dead Edwards.
-  // Combined with getFontSizeInPixels it also works with relative units.
-  function getSizeInPixels(el, value) {
-    if (/px$/i.test(value)) return parseFloat(value);
-    var style = el.style.left, runtimeStyle = el.runtimeStyle.left;
-    el.runtimeStyle.left = el.currentStyle.left;
-    el.style.left = value;
-    var result = el.style.pixelLeft;
-    el.style.left = style;
-    el.runtimeStyle.left = runtimeStyle;
-    return result;
-  }
-
-  return function(font, text, style, options, node, el, hasNext) {
-    var redraw = (text === null);
-
-    if (redraw) text = node.alt;
-
-    // @todo word-spacing, text-decoration
-
-    var viewBox = font.viewBox;
-
-    var size = style.computedFontSize ||
-      (style.computedFontSize = new Cufon.CSS.Size(getFontSizeInPixels(el, style.get('fontSize')) + 'px', font.baseSize));
-
-    var letterSpacing = style.computedLSpacing;
-
-    if (letterSpacing == undefined) {
-      letterSpacing = style.get('letterSpacing');
-      style.computedLSpacing = letterSpacing =
-        (letterSpacing == 'normal') ? 0 : ~~size.convertFrom(getSizeInPixels(el, letterSpacing));
-    }
-
-    var wrapper, canvas;
-
-    if (redraw) {
-      wrapper = node;
-      canvas = node.firstChild;
-    }
-    else {
-      wrapper = fabric.document.createElement('span');
-      wrapper.className = 'cufon cufon-vml';
-      wrapper.alt = text;
-
-      canvas = fabric.document.createElement('span');
-      canvas.className = 'cufon-vml-canvas';
-      wrapper.appendChild(canvas);
-
-      if (options.printable) {
-        var print = fabric.document.createElement('span');
-        print.className = 'cufon-alt';
-        print.appendChild(fabric.document.createTextNode(text));
-        wrapper.appendChild(print);
-      }
-
-      // ie6, for some reason, has trouble rendering the last VML element in the document.
-      // we can work around this by injecting a dummy element where needed.
-      // @todo find a better solution
-      if (!hasNext) wrapper.appendChild(fabric.document.createElement('cvml:shape'));
-    }
-
-    var wStyle = wrapper.style;
-    var cStyle = canvas.style;
-
-    var height = size.convert(viewBox.height), roundedHeight = Math.ceil(height);
-    var roundingFactor = roundedHeight / height;
-    var minX = viewBox.minX, minY = viewBox.minY;
-
-    cStyle.height = roundedHeight;
-    cStyle.top = Math.round(size.convert(minY - font.ascent));
-    cStyle.left = Math.round(size.convert(minX));
-
-    wStyle.height = size.convert(font.height) + 'px';
-
-    var textDecoration = Cufon.getTextDecoration(options);
-
-    var color = style.get('color');
-
-    var chars = Cufon.CSS.textTransform(text, style).split('');
-
-    var width = 0, offsetX = 0, advance = null;
-
-    var glyph, shape, shadows = options.textShadow;
-
-    // pre-calculate width
-    for (var i = 0, k = 0, l = chars.length; i < l; ++i) {
-      glyph = font.glyphs[chars[i]] || font.missingGlyph;
-      if (glyph) width += advance = ~~(glyph.w || font.w) + letterSpacing;
-    }
-
-    if (advance === null) return null;
-
-    var fullWidth = -minX + width + (viewBox.width - advance);
-
-    var shapeWidth = size.convert(fullWidth * roundingFactor), roundedShapeWidth = Math.round(shapeWidth);
-
-    var coordSize = fullWidth + ',' + viewBox.height, coordOrigin;
-    var stretch = 'r' + coordSize + 'nsnf';
-
-    for (i = 0; i < l; ++i) {
-
-      glyph = font.glyphs[chars[i]] || font.missingGlyph;
-      if (!glyph) continue;
-
-      if (redraw) {
-        // some glyphs may be missing so we can't use i
-        shape = canvas.childNodes[k];
-        if (shape.firstChild) shape.removeChild(shape.firstChild); // shadow
-      }
-      else {
-        shape = fabric.document.createElement('cvml:shape');
-        canvas.appendChild(shape);
-      }
-
-      shape.stroked = 'f';
-      shape.coordsize = coordSize;
-      shape.coordorigin = coordOrigin = (minX - offsetX) + ',' + minY;
-      shape.path = (glyph.d ? 'm' + glyph.d + 'xe' : '') + 'm' + coordOrigin + stretch;
-      shape.fillcolor = color;
-
-      // it's important to not set top/left or IE8 will grind to a halt
-      var sStyle = shape.style;
-      sStyle.width = roundedShapeWidth;
-      sStyle.height = roundedHeight;
-
-      if (shadows) {
-        // due to the limitations of the VML shadow element there
-        // can only be two visible shadows. opacity is shared
-        // for all shadows.
-        var shadow1 = shadows[0], shadow2 = shadows[1];
-        var color1 = Cufon.CSS.color(shadow1.color), color2;
-        var shadow = fabric.document.createElement('cvml:shadow');
-        shadow.on = 't';
-        shadow.color = color1.color;
-        shadow.offset = shadow1.offX + ',' + shadow1.offY;
-        if (shadow2) {
-          color2 = Cufon.CSS.color(shadow2.color);
-          shadow.type = 'double';
-          shadow.color2 = color2.color;
-          shadow.offset2 = shadow2.offX + ',' + shadow2.offY;
-        }
-        shadow.opacity = color1.opacity || (color2 && color2.opacity) || 1;
-        shape.appendChild(shadow);
-      }
-
-      offsetX += ~~(glyph.w || font.w) + letterSpacing;
-
-      ++k;
-
-    }
-
-    wStyle.width = Math.max(Math.ceil(size.convert(width * roundingFactor)), 0);
-
-    return wrapper;
-
-  };
-
-})());
-
-Cufon.getTextDecoration = function(options) {
-  return {
-    underline: options.textDecoration === 'underline',
-    overline: options.textDecoration === 'overline',
-    'line-through': options.textDecoration === 'line-through'
-  };
-};
-
-if (typeof exports != 'undefined') {
-  exports.Cufon = Cufon;
-}
-
-
-/*
-    json2.js
-    2014-02-04
-
-    Public Domain.
-
-    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
-
-    See http://www.JSON.org/js.html
-
-
-    This code should be minified before deployment.
-    See http://javascript.crockford.com/jsmin.html
-
-    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
-    NOT CONTROL.
-
-
-    This file creates a global JSON object containing two methods: stringify
-    and parse.
-
-        JSON.stringify(value, replacer, space)
-            value       any JavaScript value, usually an object or array.
-
-            replacer    an optional parameter that determines how object
-                        values are stringified for objects. It can be a
-                        function or an array of strings.
-
-            space       an optional parameter that specifies the indentation
-                        of nested structures. If it is omitted, the text will
-                        be packed without extra whitespace. If it is a number,
-                        it will specify the number of spaces to indent at each
-                        level. If it is a string (such as '\t' or '&nbsp;'),
-                        it contains the characters used to indent at each level.
-
-            This method produces a JSON text from a JavaScript value.
-
-            When an object value is found, if the object contains a toJSON
-            method, its toJSON method will be called and the result will be
-            stringified. A toJSON method does not serialize: it returns the
-            value represented by the name/value pair that should be serialized,
-            or undefined if nothing should be serialized. The toJSON method
-            will be passed the key associated with the value, and this will be
-            bound to the value
-
-            For example, this would serialize Dates as ISO strings.
-
-                Date.prototype.toJSON = function (key) {
-                    function f(n) {
-                        // Format integers to have at least two digits.
-                        return n < 10 ? '0' + n : n;
-                    }
-
-                    return this.getUTCFullYear()   + '-' +
-                         f(this.getUTCMonth() + 1) + '-' +
-                         f(this.getUTCDate())      + 'T' +
-                         f(this.getUTCHours())     + ':' +
-                         f(this.getUTCMinutes())   + ':' +
-                         f(this.getUTCSeconds())   + 'Z';
-                };
-
-            You can provide an optional replacer method. It will be passed the
-            key and value of each member, with this bound to the containing
-            object. The value that is returned from your method will be
-            serialized. If your method returns undefined, then the member will
-            be excluded from the serialization.
-
-            If the replacer parameter is an array of strings, then it will be
-            used to select the members to be serialized. It filters the results
-            such that only members with keys listed in the replacer array are
-            stringified.
-
-            Values that do not have JSON representations, such as undefined or
-            functions, will not be serialized. Such values in objects will be
-            dropped; in arrays they will be replaced with null. You can use
-            a replacer function to replace those with JSON values.
-            JSON.stringify(undefined) returns undefined.
-
-            The optional space parameter produces a stringification of the
-            value that is filled with line breaks and indentation to make it
-            easier to read.
-
-            If the space parameter is a non-empty string, then that string will
-            be used for indentation. If the space parameter is a number, then
-            the indentation will be that many spaces.
-
-            Example:
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}]);
-            // text is '["e",{"pluribus":"unum"}]'
-
-
-            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
-            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
-
-            text = JSON.stringify([new Date()], function (key, value) {
-                return this[key] instanceof Date ?
-                    'Date(' + this[key] + ')' : value;
-            });
-            // text is '["Date(---current time---)"]'
-
-
-        JSON.parse(text, reviver)
-            This method parses a JSON text to produce an object or array.
-            It can throw a SyntaxError exception.
-
-            The optional reviver parameter is a function that can filter and
-            transform the results. It receives each of the keys and values,
-            and its return value is used instead of the original value.
-            If it returns what it received, then the structure is not modified.
-            If it returns undefined then the member is deleted.
-
-            Example:
-
-            // Parse the text. Values that look like ISO date strings will
-            // be converted to Date objects.
-
-            myData = JSON.parse(text, function (key, value) {
-                var a;
-                if (typeof value === 'string') {
-                    a =
-/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
-                    if (a) {
-                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
-                            +a[5], +a[6]));
-                    }
-                }
-                return value;
-            });
-
-            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
-                var d;
-                if (typeof value === 'string' &&
-                        value.slice(0, 5) === 'Date(' &&
-                        value.slice(-1) === ')') {
-                    d = new Date(value.slice(5, -1));
-                    if (d) {
-                        return d;
-                    }
-                }
-                return value;
-            });
-
-
-    This is a reference implementation. You are free to copy, modify, or
-    redistribute.
-*/
-
-/*jslint evil: true, regexp: true */
-
-/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
-    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
-    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
-    lastIndex, length, parse, prototype, push, replace, slice, stringify,
-    test, toJSON, toString, valueOf
-*/
-
-
-// Create a JSON object only if one does not already exist. We create the
-// methods in a closure to avoid creating global variables.
-
-if (typeof JSON !== 'object') {
-    JSON = {};
-}
-
-(function () {
-    'use strict';
-
-    function f(n) {
-        // Format integers to have at least two digits.
-        return n < 10 ? '0' + n : n;
-    }
-
-    if (typeof Date.prototype.toJSON !== 'function') {
-
-        Date.prototype.toJSON = function () {
-
-            return isFinite(this.valueOf())
-                ? this.getUTCFullYear()     + '-' +
-                    f(this.getUTCMonth() + 1) + '-' +
-                    f(this.getUTCDate())      + 'T' +
-                    f(this.getUTCHours())     + ':' +
-                    f(this.getUTCMinutes())   + ':' +
-                    f(this.getUTCSeconds())   + 'Z'
-                : null;
-        };
-
-        String.prototype.toJSON      =
-            Number.prototype.toJSON  =
-            Boolean.prototype.toJSON = function () {
-                return this.valueOf();
-            };
-    }
-
-    var cx,
-        escapable,
-        gap,
-        indent,
-        meta,
-        rep;
-
-
-    function quote(string) {
-
-// If the string contains no control characters, no quote characters, and no
-// backslash characters, then we can safely slap some quotes around it.
-// Otherwise we must also replace the offending characters with safe escape
-// sequences.
-
-        escapable.lastIndex = 0;
-        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
-            var c = meta[a];
-            return typeof c === 'string'
-                ? c
-                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-        }) + '"' : '"' + string + '"';
-    }
-
-
-    function str(key, holder) {
-
-// Produce a string from holder[key].
-
-        var i,          // The loop counter.
-            k,          // The member key.
-            v,          // The member value.
-            length,
-            mind = gap,
-            partial,
-            value = holder[key];
-
-// If the value has a toJSON method, call it to obtain a replacement value.
-
-        if (value && typeof value === 'object' &&
-                typeof value.toJSON === 'function') {
-            value = value.toJSON(key);
-        }
-
-// If we were called with a replacer function, then call the replacer to
-// obtain a replacement value.
-
-        if (typeof rep === 'function') {
-            value = rep.call(holder, key, value);
-        }
-
-// What happens next depends on the value's type.
-
-        switch (typeof value) {
-        case 'string':
-            return quote(value);
-
-        case 'number':
-
-// JSON numbers must be finite. Encode non-finite numbers as null.
-
-            return isFinite(value) ? String(value) : 'null';
-
-        case 'boolean':
-        case 'null':
-
-// If the value is a boolean or null, convert it to a string. Note:
-// typeof null does not produce 'null'. The case is included here in
-// the remote chance that this gets fixed someday.
-
-            return String(value);
-
-// If the type is 'object', we might be dealing with an object or an array or
-// null.
-
-        case 'object':
-
-// Due to a specification blunder in ECMAScript, typeof null is 'object',
-// so watch out for that case.
-
-            if (!value) {
-                return 'null';
-            }
-
-// Make an array to hold the partial results of stringifying this object value.
-
-            gap += indent;
-            partial = [];
-
-// Is the value an array?
-
-            if (Object.prototype.toString.apply(value) === '[object Array]') {
-
-// The value is an array. Stringify every element. Use null as a placeholder
-// for non-JSON values.
-
-                length = value.length;
-                for (i = 0; i < length; i += 1) {
-                    partial[i] = str(i, value) || 'null';
-                }
-
-// Join all of the elements together, separated with commas, and wrap them in
-// brackets.
-
-                v = partial.length === 0
-                    ? '[]'
-                    : gap
-                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
-                    : '[' + partial.join(',') + ']';
-                gap = mind;
-                return v;
-            }
-
-// If the replacer is an array, use it to select the members to be stringified.
-
-            if (rep && typeof rep === 'object') {
-                length = rep.length;
-                for (i = 0; i < length; i += 1) {
-                    if (typeof rep[i] === 'string') {
-                        k = rep[i];
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            } else {
-
-// Otherwise, iterate through all of the keys in the object.
-
-                for (k in value) {
-                    if (Object.prototype.hasOwnProperty.call(value, k)) {
-                        v = str(k, value);
-                        if (v) {
-                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
-                        }
-                    }
-                }
-            }
-
-// Join all of the member texts together, separated with commas,
-// and wrap them in braces.
-
-            v = partial.length === 0
-                ? '{}'
-                : gap
-                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
-                : '{' + partial.join(',') + '}';
-            gap = mind;
-            return v;
-        }
-    }
-
-// If the JSON object does not yet have a stringify method, give it one.
-
-    if (typeof JSON.stringify !== 'function') {
-        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
-        meta = {    // table of character substitutions
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        };
-        JSON.stringify = function (value, replacer, space) {
-
-// The stringify method takes a value and an optional replacer, and an optional
-// space parameter, and returns a JSON text. The replacer can be a function
-// that can replace values, or an array of strings that will select the keys.
-// A default replacer method can be provided. Use of the space parameter can
-// produce text that is more easily readable.
-
-            var i;
-            gap = '';
-            indent = '';
-
-// If the space parameter is a number, make an indent string containing that
-// many spaces.
-
-            if (typeof space === 'number') {
-                for (i = 0; i < space; i += 1) {
-                    indent += ' ';
-                }
-
-// If the space parameter is a string, it will be used as the indent string.
-
-            } else if (typeof space === 'string') {
-                indent = space;
-            }
-
-// If there is a replacer, it must be a function or an array.
-// Otherwise, throw an error.
-
-            rep = replacer;
-            if (replacer && typeof replacer !== 'function' &&
-                    (typeof replacer !== 'object' ||
-                    typeof replacer.length !== 'number')) {
-                throw new Error('JSON.stringify');
-            }
-
-// Make a fake root object containing our value under the key of ''.
-// Return the result of stringifying the value.
-
-            return str('', {'': value});
-        };
-    }
-
-
-// If the JSON object does not yet have a parse method, give it one.
-
-    if (typeof JSON.parse !== 'function') {
-        cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
-        JSON.parse = function (text, reviver) {
-
-// The parse method takes a text and an optional reviver function, and returns
-// a JavaScript value if the text is a valid JSON text.
-
-            var j;
-
-            function walk(holder, key) {
-
-// The walk method is used to recursively walk the resulting structure so
-// that modifications can be made.
-
-                var k, v, value = holder[key];
-                if (value && typeof value === 'object') {
-                    for (k in value) {
-                        if (Object.prototype.hasOwnProperty.call(value, k)) {
-                            v = walk(value, k);
-                            if (v !== undefined) {
-                                value[k] = v;
-                            } else {
-                                delete value[k];
-                            }
-                        }
-                    }
-                }
-                return reviver.call(holder, key, value);
-            }
-
-
-// Parsing happens in four stages. In the first stage, we replace certain
-// Unicode characters with escape sequences. JavaScript handles many characters
-// incorrectly, either silently deleting them, or treating them as line endings.
-
-            text = String(text);
-            cx.lastIndex = 0;
-            if (cx.test(text)) {
-                text = text.replace(cx, function (a) {
-                    return '\\u' +
-                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
-                });
-            }
-
-// In the second stage, we run the text against regular expressions that look
-// for non-JSON patterns. We are especially concerned with '()' and 'new'
-// because they can cause invocation, and '=' because it can cause mutation.
-// But just to be safe, we want to reject all unexpected forms.
-
-// We split the second stage into 4 regexp operations in order to work around
-// crippling inefficiencies in IE's and Safari's regexp engines. First we
-// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
-// replace all simple value tokens with ']' characters. Third, we delete all
-// open brackets that follow a colon or comma or that begin the text. Finally,
-// we look to see that the remaining characters are only whitespace or ']' or
-// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
-
-            if (/^[\],:{}\s]*$/
-                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
-                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
-                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-// In the third stage we use the eval function to compile the text into a
-// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
-// in JavaScript: it can begin a block or an object literal. We wrap the text
-// in parens to eliminate the ambiguity.
-
-                j = eval('(' + text + ')');
-
-// In the optional fourth stage, we recursively walk the new structure, passing
-// each name/value pair to a reviver function for possible transformation.
-
-                return typeof reviver === 'function'
-                    ? walk({'': j}, '')
-                    : j;
-            }
-
-// If the text is not JSON parseable, then a SyntaxError is thrown.
-
-            throw new SyntaxError('JSON.parse');
-        };
-    }
-}());
-
-
-(function(){
-
-  /**
-   * @private
-   * @param {String} eventName
-   * @param {Function} handler
-   */
-  function _removeEventListener(eventName, handler) {
-    if (!this.__eventListeners[eventName]) {
-      return;
-    }
-
-    if (handler) {
-      fabric.util.removeFromArray(this.__eventListeners[eventName], handler);
-    }
-    else {
-      this.__eventListeners[eventName].length = 0;
-    }
-  }
-
-  /**
-   * Observes specified event
-   * @deprecated `observe` deprecated since 0.8.34 (use `on` instead)
-   * @memberOf fabric.Observable
-   * @alias on
-   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
-   * @param {Function} handler Function that receives a notification when an event of the specified type occurs
-   * @return {Self} thisArg
-   * @chainable
-   */
-  function observe(eventName, handler) {
-    if (!this.__eventListeners) {
-      this.__eventListeners = { };
-    }
-    // one object with key/value pairs was passed
-    if (arguments.length === 1) {
-      for (var prop in eventName) {
-        this.on(prop, eventName[prop]);
-      }
-    }
-    else {
-      if (!this.__eventListeners[eventName]) {
-        this.__eventListeners[eventName] = [ ];
-      }
-      this.__eventListeners[eventName].push(handler);
-    }
-    return this;
-  }
-
-  /**
-   * Stops event observing for a particular event handler. Calling this method
-   * without arguments removes all handlers for all events
-   * @deprecated `stopObserving` deprecated since 0.8.34 (use `off` instead)
-   * @memberOf fabric.Observable
-   * @alias off
-   * @param {String|Object} eventName Event name (eg. 'after:render') or object with key/value pairs (eg. {'after:render': handler, 'selection:cleared': handler})
-   * @param {Function} handler Function to be deleted from EventListeners
-   * @return {Self} thisArg
-   * @chainable
-   */
-  function stopObserving(eventName, handler) {
-    if (!this.__eventListeners) {
-      return;
-    }
-
-    // remove all key/value pairs (event name -> event handler)
-    if (arguments.length === 0) {
-      this.__eventListeners = { };
-    }
-    // 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++) {
-      // avoiding try/catch for perf. reasons
-      listenersForEvent[i].call(this, options || { });
-    }
-    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 = {
-
-  /**
-   * Adds objects to collection, then renders canvas (if `renderOnAddRemove` is not `false`)
-   * Objects should be instances of (or inherit from) fabric.Object
-   * @param {...fabric.Object} object Zero or more fabric instances
-   * @return {Self} thisArg
-   */
-  add: function () {
-    this._objects.push.apply(this._objects, arguments);
-    for (var i = 0, length = arguments.length; i < length; i++) {
-      this._onObjectAdded(arguments[i]);
-    }
-    this.renderOnAddRemove && this.renderAll();
-    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
-   * @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(object);
-    this.renderOnAddRemove && this.renderAll();
-    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;
-
-    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) {
-        objects.splice(index, 1);
-        this._onObjectRemoved(arguments[i]);
-      }
-    }
-
-    this.renderOnAddRemove && this.renderAll();
-    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.
-   *                   Iteration happens in reverse order (for performance reasons).
-   *                   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
-   */
-  forEachObject: function(callback, context) {
-    var objects = this.getObjects(),
-        i = objects.length;
-    while (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);
-  }
-};
-
-
-(function(global) {
-
-  var sqrt = Math.sqrt,
-      atan2 = Math.atan2,
-      PiBy180 = Math.PI / 180;
-
-  /**
-   * @namespace fabric.util
-   */
-  fabric.util = {
-
-    /**
-     * Removes value from an array.
-     * Presence of value (and its position in an array) is determined via `Array.prototype.indexOf`
-     * @static
-     * @memberOf fabric.util
-     * @param {Array} array
-     * @param {Any} value
-     * @return {Array} original array
-     */
-    removeFromArray: function(array, value) {
-      var idx = array.indexOf(value);
-      if (idx !== -1) {
-        array.splice(idx, 1);
-      }
-      return array;
-    },
-
-    /**
-     * Returns random number between 2 specified ones.
-     * @static
-     * @memberOf fabric.util
-     * @param {Number} min lower limit
-     * @param {Number} max upper limit
-     * @return {Number} random value (between min and max)
-     */
-    getRandomInt: function(min, max) {
-      return Math.floor(Math.random() * (max - min + 1)) + min;
-    },
-
-    /**
-     * Transforms degrees to radians.
-     * @static
-     * @memberOf fabric.util
-     * @param {Number} degrees value in degrees
-     * @return {Number} value in radians
-     */
-    degreesToRadians: function(degrees) {
-      return degrees * PiBy180;
-    },
-
-    /**
-     * Transforms radians to degrees.
-     * @static
-     * @memberOf fabric.util
-     * @param {Number} radians value in radians
-     * @return {Number} value in degrees
-     */
-    radiansToDegrees: function(radians) {
-      return radians / PiBy180;
-    },
-
-    /**
-     * Rotates `point` around `origin` with `radians`
-     * @static
-     * @memberOf fabric.util
-     * @param {fabric.Point} point The point to rotate
-     * @param {fabric.Point} origin The origin of the rotation
-     * @param {Number} radians The radians of the angle for the rotation
-     * @return {fabric.Point} The new rotated point
-     */
-    rotatePoint: function(point, origin, radians) {
-      var sin = Math.sin(radians),
-          cos = Math.cos(radians);
-
-      point.subtractEquals(origin);
-
-      var rx = point.x * cos - point.y * sin,
-          ry = point.x * sin + point.y * cos;
-
-      return new fabric.Point(rx, ry).addEquals(origin);
-    },
-
-    /**
-     * 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[1] * p.y,
-          t[2] * p.x + t[3] * p.y
-        );
-      }
-      return new fabric.Point(
-        t[0] * p.x + t[1] * p.y + t[4],
-        t[2] * p.x + t[3] * p.y + t[5]
-      );
-    },
-
-    /**
-     * Invert transformation t
-     * @static
-     * @memberOf fabric.util
-     * @param {Array} t The transform
-     * @return {Array} The inverted transform
-     */
-    invertTransform: function(t) {
-      var r = t.slice(),
-          a = 1 / (t[0] * t[3] - t[1] * t[2]);
-      r = [a * t[3], -a * t[1], -a * t[2], a * t[0], 0, 0];
-      var o = fabric.util.transformPoint({ x: t[4], y: t[5] }, r);
-      r[4] = -o.x;
-      r[5] = -o.y;
-      return r;
-    },
-
-    /**
-     * A wrapper around Number#toFixed, which contrary to native method returns number, not string.
-     * @static
-     * @memberOf fabric.util
-     * @param {Number|String} number number to operate on
-     * @param {Number} fractionDigits number of fraction digits to "leave"
-     * @return {Number}
-     */
-    toFixed: function(number, fractionDigits) {
-      return parseFloat(Number(number).toFixed(fractionDigits));
-    },
-
-    /**
-     * Converts from attribute value to pixel value if applicable.
-     * Returns converted pixels or original value not converted.
-     * @param {Number|String} value number to operate on
-     * @return {Number|String}
-     */
-    parseUnit: function(value) {
-      var unit = /\D{0,2}$/.exec(value),
-          number = parseFloat(value);
-
-      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
-
-        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 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,
-          obj = global || fabric.window;
-
-      for (var 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 {Any} [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 */
-      img.onload = function () {
-        callback && callback.call(context, img);
-        img = img.onload = img.onerror = null;
-      };
-
-      /** @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 && typeof crossOrigin !== 'undefined') {
-        img.crossOrigin = crossOrigin;
-      }
-
-      img.src = url;
-    },
-
-    /**
-     * 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);
-        if (klass.async) {
-          klass.fromObject(o, function (obj, error) {
-            if (!error) {
-              enlivenedObjects[index] = obj;
-              reviver && reviver(o, enlivenedObjects[index]);
-            }
-            onLoaded();
-          });
-        }
-        else {
-          enlivenedObjects[index] = klass.fromObject(o);
-          reviver && reviver(o, enlivenedObjects[index]);
-          onLoaded();
-        }
-      });
-    },
-
-    /**
-     * Groups SVG elements (usually those retrieved from SVG document)
-     * @static
-     * @memberOf fabric.util
-     * @param {Array} elements SVG elements to group
-     * @param {Object} [options] Options object
-     * @return {fabric.Object|fabric.PathGroup}
-     */
-    groupSVGElements: function(elements, options, path) {
-      var object;
-
-      object = new fabric.PathGroup(elements, options);
-
-      if (typeof path !== 'undefined') {
-        object.setSourcePath(path);
-      }
-      return object;
-    },
-
-    /**
-     * Populates an object with properties of another object
-     * @static
-     * @memberOf fabric.util
-     * @param {Object} source Source object
-     * @param {Object} destination Destination object
-     * @return {Array} properties Propertie names to include
-     */
-    populateWithProperties: function(source, destination, properties) {
-      if (properties && Object.prototype.toString.call(properties) === '[object Array]') {
-        for (var i = 0, len = properties.length; i < len; i++) {
-          if (properties[i] in source) {
-            destination[properties[i]] = source[properties[i]];
-          }
-        }
-      }
-    },
-
-    /**
-     * Draws a dashed line between two points
-     *
-     * This method is used to draw dashed line around selection area.
-     * See <a href="http://stackoverflow.com/questions/4576724/dotted-stroke-in-canvas">dotted stroke in canvas</a>
-     *
-     * @param {CanvasRenderingContext2D} ctx context
-     * @param {Number} x  start x coordinate
-     * @param {Number} y start y coordinate
-     * @param {Number} x2 end x coordinate
-     * @param {Number} y2 end y coordinate
-     * @param {Array} da dash array pattern
-     */
-    drawDashedLine: function(ctx, x, y, x2, y2, da) {
-      var dx = x2 - x,
-          dy = y2 - y,
-          len = sqrt(dx * dx + dy * dy),
-          rot = atan2(dy, dx),
-          dc = da.length,
-          di = 0,
-          draw = true;
-
-      ctx.save();
-      ctx.translate(x, y);
-      ctx.moveTo(0, 0);
-      ctx.rotate(rot);
-
-      x = 0;
-      while (len > x) {
-        x += da[di++ % dc];
-        if (x > len) {
-          x = len;
-        }
-        ctx[draw ? 'lineTo' : 'moveTo'](x, 0);
-        draw = !draw;
-      }
-
-      ctx.restore();
-    },
-
-    /**
-     * Creates canvas element and initializes it via excanvas if necessary
-     * @static
-     * @memberOf fabric.util
-     * @param {CanvasElement} [canvasEl] optional canvas element to initialize;
-     * when not given, element is created implicitly
-     * @return {CanvasElement} initialized canvas element
-     */
-    createCanvasElement: function(canvasEl) {
-      canvasEl || (canvasEl = fabric.document.createElement('canvas'));
-      //jscs:disable requireCamelCaseOrUpperCaseIdentifiers
-      if (!canvasEl.getContext && typeof G_vmlCanvasManager !== 'undefined') {
-        G_vmlCanvasManager.initElement(canvasEl);
-      }
-      //jscs:enable requireCamelCaseOrUpperCaseIdentifiers
-      return canvasEl;
-    },
-
-    /**
-     * Creates image element (works on client and node)
-     * @static
-     * @memberOf fabric.util
-     * @return {HTMLImageElement} HTML image element
-     */
-    createImage: function() {
-      return fabric.isLikelyNode
-        ? new (require('canvas').Image)()
-        : fabric.document.createElement('img');
-    },
-
-    /**
-     * Creates accessors (getXXX, setXXX) for a "class", based on "stateProperties" array
-     * @static
-     * @memberOf fabric.util
-     * @param {Object} klass "Class" to create accessors for
-     */
-    createAccessors: function(klass) {
-      var proto = klass.prototype;
-
-      for (var i = proto.stateProperties.length; i--; ) {
-
-        var propName = proto.stateProperties[i],
-            capitalizedPropName = propName.charAt(0).toUpperCase() + propName.slice(1),
-            setterName = 'set' + capitalizedPropName,
-            getterName = 'get' + capitalizedPropName;
-
-        // using `new Function` for better introspection
-        if (!proto[getterName]) {
-          proto[getterName] = (function(property) {
-            return new Function('return this.get("' + property + '")');
-          })(propName);
-        }
-        if (!proto[setterName]) {
-          proto[setterName] = (function(property) {
-            return new Function('value', 'return this.set("' + property + '", value)');
-          })(propName);
-        }
-      }
-    },
-
-    /**
-     * @static
-     * @memberOf fabric.util
-     * @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();
-    },
-
-    /**
-     * Multiply matrix A by matrix B to nest transformations
-     * @static
-     * @memberOf fabric.util
-     * @param  {Array} matrixA First transformMatrix
-     * @param  {Array} matrixB Second transformMatrix
-     * @return {Array} The product of the two transform matrices
-     */
-    multiplyTransformMatrices: function(matrixA, matrixB) {
-      // Matrix multiply matrixA * matrixB
-      var a = [
-        [matrixA[0], matrixA[2], matrixA[4]],
-        [matrixA[1], matrixA[3], matrixA[5]],
-        [0,          0,          1         ]
-      ],
-
-      b = [
-        [matrixB[0], matrixB[2], matrixB[4]],
-        [matrixB[1], matrixB[3], matrixB[5]],
-        [0,          0,          1         ]
-      ],
-
-      result = [];
-
-      for (var r = 0; r < 3; r++) {
-        result[r] = [];
-        for (var c = 0; c < 3; c++) {
-          var sum = 0;
-          for (var k = 0; k < 3; k++) {
-            sum += a[r][k] * b[k][c];
-          }
-
-          result[r][c] = sum;
-        }
-      }
-
-      return [
-        result[0][0],
-        result[1][0],
-        result[0][1],
-        result[1][1],
-        result[0][2],
-        result[1][2]
-      ];
-    },
-
-    /**
-     * 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;
-        }
-        else {
-          x = 0;
-        }
-        if (y > tolerance) {
-          y -= tolerance;
-        }
-        else {
-          y = 0;
-        }
-      }
-
-      var _isTransparent = true,
-          imageData = ctx.getImageData(x, y, (tolerance * 2) || 1, (tolerance * 2) || 1);
-
-      // Split image data - for tolerance > 1, pixelDataSize = 4;
-      for (var i = 3, l = imageData.data.length; i < l; i += 4) {
-        var temp = imageData.data[i];
-        _isTransparent = temp <= 0;
-        if (_isTransparent === false) {
-          break; // Stop if colour found
-        }
-      }
-
-      imageData = null;
-
-      return _isTransparent;
-    }
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
-  var arcToSegmentsCache = { },
-      segmentToBezierCache = { },
-      _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 (arcToSegmentsCache[argsString]) {
-      return arcToSegmentsCache[argsString];
-    }
-
-    var PI = Math.PI, th = rotateX * (PI / 180),
-        sinTh = Math.sin(th),
-        cosTh = Math.cos(th),
-        fromX = 0, fromY = 0;
-
-    rx = Math.abs(rx);
-    ry = Math.abs(ry);
-
-    var px = -cosTh * toX - sinTh * toY,
-        py = -cosTh * toY + sinTh * toX,
-        rx2 = rx * rx, ry2 = ry * ry, py2 = py * py, px2 = px * px,
-        pl = 4 * rx2 * ry2 - rx2 * py2 - ry2 * px2,
-        root = 0;
-
-    if (pl < 0) {
-      var s = Math.sqrt(1 - 0.25 * pl/(rx2 * ry2));
-      rx *= s;
-      ry *= s;
-    }
-    else {
-      root = (large === sweep ? -0.5 : 0.5) *
-              Math.sqrt( pl /(rx2 * py2 + ry2 * px2));
-    }
-
-    var cx = root * rx * py / ry,
-        cy = -root * ry * px / rx,
-        cx1 = cosTh * cx - sinTh * cy + toX / 2,
-        cy1 = sinTh * cx + cosTh * cy + toY / 2,
-        mTheta = calcVectorAngle(1, 0, (px - cx) / rx, (py - cy) / ry),
-        dtheta = calcVectorAngle((px - cx) / rx, (py - cy) / ry, (-px - cx) / rx, (-py - cy) / ry);
-
-    if (sweep === 0 && dtheta > 0) {
-      dtheta -= 2 * PI;
-    }
-    else if (sweep === 1 && dtheta < 0) {
-      dtheta += 2 * PI;
-    }
-
-    // Convert into cubic bezier segments <= 90deg
-    var segments = Math.ceil(Math.abs(dtheta / (PI * 0.5))),
-        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 += mDelta;
-      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 = Math.cos(th2),
-        sinth2 = Math.sin(th2),
-        costh3 = Math.cos(th3),
-        sinth3 = Math.sin(th3),
-        toX = cosTh * rx * costh3 - sinTh * ry * sinth3 + cx1,
-        toY = sinTh * rx * costh3 + cosTh * ry * sinth3 + cy1,
-        cp1X = fromX + mT * ( - cosTh * rx * sinth2 - sinTh * ry * costh2),
-        cp1Y = fromY + mT * ( - sinTh * rx * sinth2 + cosTh * ry * costh2),
-        cp2X = toX + mT * ( cosTh * rx * sinth3 + sinTh * ry * costh3),
-        cp2Y = toY + mT * ( sinTh * rx * sinth3 - cosTh * ry * costh3);
-
-    segmentToBezierCache[argsString2] = [
-      cp1X, cp1Y,
-      cp2X, cp2Y,
-      toX, toY
-    ];
-    return segmentToBezierCache[argsString2];
-  }
-
-  /*
-  * Private
-  */
-  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);
-    }
-  }
-
-  /**
-   * Draws arc
-   * @param {CanvasRenderingContext2D} ctx
-   * @param {Number} fx
-   * @param {Number} fy
-   * @param {Array} coords
-   */
-  fabric.util.drawArc = function(ctx, fx, fy, coords) {
-    var rx = coords[0],
-        ry = coords[1],
-        rot = coords[2],
-        large = coords[3],
-        sweep = coords[4],
-        tx = coords[5],
-        ty = coords[6],
-        segs = [[ ], [ ], [ ], [ ]],
-        segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);
-
-    for (var i = 0, len = segsNorm.length; i < len; i++) {
-      segs[i][0] = segsNorm[i][0] + fx;
-      segs[i][1] = segsNorm[i][1] + fy;
-      segs[i][2] = segsNorm[i][2] + fx;
-      segs[i][3] = segsNorm[i][3] + fy;
-      segs[i][4] = segsNorm[i][4] + fx;
-      segs[i][5] = segsNorm[i][5] + fy;
-      ctx.bezierCurveTo.apply(ctx, segs[i]);
-    }
-  };
-})();
-
-
-(function() {
-
-  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]);
-    }
-    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 {Any}
-   */
-  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 {Any}
-   */
-  function min(array, byProperty) {
-    return find(array, byProperty, function(value1, value2) {
-      return value1 < value2;
-    });
-  }
-
-  /**
-   * @private
-   */
-  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;
-  }
-
-  /**
-   * @namespace fabric.util.array
-   */
-  fabric.util.array = {
-    invoke: invoke,
-    min: min,
-    max: max
-  };
-
-})();
-
-
-(function(){
-
-  /**
-   * Copies all enumerable properties of one object to another
-   * @memberOf fabric.util.object
-   * @param {Object} destination Where to copy to
-   * @param {Object} source Where to copy from
-   * @return {Object}
-   */
-  function extend(destination, source) {
-    // JScript DontEnum bug is not taken care of
-    for (var property in source) {
-      destination[property] = source[property];
-    }
-    return destination;
-  }
-
-  /**
-   * Creates an empty object and copies all enumerable properties of another object to it
-   * @memberOf fabric.util.object
-   * @param {Object} object Object to clone
-   * @return {Object}
-   */
-  function clone(object) {
-    return extend({ }, object);
-  }
-
-  /** @namespace fabric.util.object */
-  fabric.util.object = {
-    extend: extend,
-    clone: clone
-  };
-
-})();
-
-
-(function() {
-
-  
-
-  /**
-   * Camelizes a string
-   * @memberOf fabric.util.string
-   * @param {String} string String to camelize
-   * @return {String} Camelized version of a string
-   */
-  function camelize(string) {
-    return string.replace(/-+(.)?/g, function(match, character) {
-      return character ? character.toUpperCase() : '';
-    });
-  }
-
-  /**
-   * Capitalizes a string
-   * @memberOf fabric.util.string
-   * @param {String} string String to capitalize
-   * @param {Boolean} [firstLetterOnly] If true only first letter is capitalized
-   * and other letters stay untouched, if false first letter is capitalized
-   * and other letters are converted to lowercase.
-   * @return {String} Capitalized version of a string
-   */
-  function capitalize(string, firstLetterOnly) {
-    return string.charAt(0).toUpperCase() +
-      (firstLetterOnly ? string.slice(1) : string.slice(1).toLowerCase());
-  }
-
-  /**
-   * Escapes XML in a string
-   * @memberOf fabric.util.string
-   * @param {String} string String to escape
-   * @return {String} Escaped version of a string
-   */
-  function escapeXml(string) {
-    return string.replace(/&/g, '&amp;')
-       .replace(/"/g, '&quot;')
-       .replace(/'/g, '&apos;')
-       .replace(/</g, '&lt;')
-       .replace(/>/g, '&gt;');
-  }
-
-  /**
-   * String utilities
-   * @namespace fabric.util.string
-   */
-  fabric.util.string = {
-    camelize: camelize,
-    capitalize: capitalize,
-    escapeXml: escapeXml
-  };
-}());
-
-
-
-
-
-(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;
-      })(),
-
-      /** @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 (source.valueOf !== Object.prototype.valueOf) {
-              klass.prototype.valueOf = source.valueOf;
-            }
-          }
-        }
-      };
-
-  function Subclass() { }
-
-  function callSuper(methodName) {
-    var fn = this.constructor.superclass.prototype[methodName];
-    return (arguments.length > 1)
-      ? fn.apply(this, slice.call(arguments, 1))
-      : fn.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 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;
-  }
-
-  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;
-      }
-    }
-    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) {
-      element.addEventListener(eventName, handler, false);
-    };
-    /** @ignore */
-    removeListener = function (element, eventName, handler) {
-      element.removeEventListener(eventName, handler, false);
-    };
-  }
-
-  else if (shouldUseAttachEventDetachEvent) {
-    /** @ignore */
-    addListener = function (element, eventName, handler) {
-      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) {
-      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) {
-      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) {
-      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
-   * @param {HTMLCanvasElement} upperCanvasEl &lt;canvas> element on which object selection is drawn
-   */
-  function getPointer(event, upperCanvasEl) {
-    event || (event = fabric.window.event);
-
-    var element = event.target ||
-                  (typeof event.srcElement !== unknown ? event.srcElement : null),
-
-        scroll = fabric.util.getScrollLeftTop(element, upperCanvasEl);
-
-    return {
-      x: pointerX(event) + scroll.left,
-      y: pointerY(event) + scroll.top
-    };
-  }
-
-  var pointerX = function(event) {
-    // looks like in IE (<9) clientX at certain point (apparently when mouseup fires on VML element)
-    // is represented as COM object, with all the consequences, like "unknown" type and error on [[Get]]
-    // need to investigate later
-    return (typeof event.clientX !== unknown ? event.clientX : 0);
-  },
-
-  pointerY = function(event) {
-    return (typeof event.clientY !== unknown ? event.clientY : 0);
-  };
-
-  function _getPointer(event, pageProp, clientProp) {
-    var touchProp = event.type === 'touchend' ? 'changedTouches' : 'touches';
-
-    return (event[touchProp] && event[touchProp][0]
-      ? (event[touchProp][0][pageProp] - (event[touchProp][0][pageProp] - event[touchProp][0][clientProp]))
-        || event[clientProp]
-      : event[clientProp]);
-  }
-
-  if (fabric.isTouchSupported) {
-    pointerX = function(event) {
-      return _getPointer(event, 'pageX', 'clientX');
-    };
-    pointerY = function(event) {
-      return _getPointer(event, 'pageY', 'clientY');
-    };
-  }
-
-  fabric.util.getPointer = getPointer;
-
-  fabric.util.object.extend(fabric.util, fabric.Observable);
-
-})();
-
-
-(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() {
-
-  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
-   * @param {HTMLElement} upperCanvasEl Upper canvas element
-   * @return {Object} Object with left/top values
-   */
-  function getScrollLeftTop(element, upperCanvasEl) {
-
-    var firstFixedAncestor,
-        origElement,
-        left = 0,
-        top = 0,
-        docElement = fabric.document.documentElement,
-        body = fabric.document.body || {
-          scrollLeft: 0, scrollTop: 0
-        };
-
-    origElement = element;
-
-    while (element && element.parentNode && !firstFixedAncestor) {
-
-      element = element.parentNode;
-
-      if (element !== fabric.document &&
-          fabric.util.getElementStyle(element, 'position') === 'fixed') {
-        firstFixedAncestor = element;
-      }
-
-      if (element !== fabric.document &&
-          origElement !== upperCanvasEl &&
-          fabric.util.getElementStyle(element, 'position') === 'absolute') {
-        left = 0;
-        top = 0;
-      }
-      else 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;
-      }
-    }
-
-    return { left: left, top: top };
-  }
-
-  /**
-   * 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 (!doc) {
-      return { left: 0, top: 0 };
-    }
-
-    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 = fabric.util.getScrollLeftTop(element, null);
-
-    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) {
-      return fabric.document.defaultView.getComputedStyle(element, null)[attr];
-    };
-  }
-  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;
-  })();
-
-  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;
-
-})();
-
-
-(function(){
-
-  function addParamToUrl(url, param) {
-    return url + (/\?/.test(url) ? '&' : '?') + param;
-  }
-
-  var makeXHR = (function() {
-    var factories = [
-      function() { return new ActiveXObject('Microsoft.XMLHTTP'); },
-      function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
-      function() { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); },
-      function() { return new XMLHttpRequest(); }
-    ];
-    for (var i = factories.length; i--; ) {
-      try {
-        var req = factories[i]();
-        if (req) {
-          return factories[i];
-        }
-      }
-      catch (err) { }
-    }
-  })();
-
-  function emptyFn() { }
-
-  /**
-   * Cross-browser abstraction for sending XMLHttpRequest
-   * @memberOf fabric.util
-   * @param {String} url URL to send XMLHttpRequest to
-   * @param {Object} [options] Options object
-   * @param {String} [options.method="GET"]
-   * @param {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;
-
-    /** @ignore */
-    xhr.onreadystatechange = function() {
-      if (xhr.readyState === 4) {
-        onComplete(xhr);
-        xhr.onreadystatechange = emptyFn;
-      }
-    };
-
-    if (method === 'GET') {
-      body = null;
-      if (typeof options.parameters === 'string') {
-        url = addParamToUrl(url, options.parameters);
-      }
-    }
-
-    xhr.open(method, url, true);
-
-    if (method === 'POST' || method === 'PUT') {
-      xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
-    }
-
-    xhr.send(body);
-    return xhr;
-  }
-
-  fabric.util.request = request;
-})();
-
-
-/**
- * Wrapper around `console.log` (when available)
- * @param {Any} [values] Values to log
- */
-fabric.log = function() { };
-
-/**
- * Wrapper around `console.warn` (when available)
- * @param {Any} [values] Values to log as a warning
- */
-fabric.warn = function() { };
-
-if (typeof console !== 'undefined') {
-  ['log', 'warn'].forEach(function(methodName) {
-    if (typeof console[methodName] !== 'undefined' && console[methodName].apply) {
-      fabric[methodName] = function() {
-        return console[methodName].apply(console, arguments);
-      };
-    }
-  });
-}
-
-
-(function(global) {
-
-  'use strict';
-
-  /**
-   * @name fabric
-   * @namespace
-   */
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      capitalize = fabric.util.string.capitalize,
-      clone = fabric.util.object.clone,
-      toFixed = fabric.util.toFixed,
-      parseUnit = fabric.util.parseUnit,
-      multiplyTransformMatrices = fabric.util.multiplyTransformMatrices,
-
-      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',
-        'stroke-dasharray':   'strokeDashArray',
-        'stroke-linecap':     'strokeLineCap',
-        'stroke-linejoin':    'strokeLineJoin',
-        'stroke-miterlimit':  'strokeMiterLimit',
-        'stroke-opacity':     'strokeOpacity',
-        'stroke-width':       'strokeWidth',
-        'text-decoration':    'textDecoration',
-        'text-anchor':        'originX'
-      },
-
-      colorAttributes = {
-        stroke: 'strokeOpacity',
-        fill:   'fillOpacity'
-      };
-
-  function normalizeAttr(attr) {
-    // transform attribute names
-    if (attr in attributesMap) {
-      return attributesMap[attr];
-    }
-    return attr;
-  }
-
-  function normalizeValue(attr, value, parentAttributes) {
-    var isArray = Object.prototype.toString.call(value) === '[object Array]',
-        parsed;
-
-    if ((attr === 'fill' || attr === 'stroke') && value === 'none') {
-      value = '';
-    }
-    else if (attr === 'fillRule') {
-      value = (value === 'evenodd') ? 'destination-over' : value;
-    }
-    else if (attr === 'strokeDashArray') {
-      value = value.replace(/,/g, ' ').split(/\s+/).map(function(n) {
-        return parseInt(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') ? false : true;
-      // display=none on parent element always takes precedence over child element
-      if (parentAttributes && parentAttributes.visible === false) {
-        value = false;
-      }
-    }
-    else if (attr === 'originX' /* text-anchor */) {
-      value = value === 'start' ? 'left' : value === 'end' ? 'right' : 'center';
-    }
-    else {
-      parsed = isArray ? value.map(parseUnit) : parseUnit(value);
-    }
-
-    return (!isArray && isNaN(parsed) ? value : parsed);
-  }
-
-  /**
-   * @private
-   * @param {Object} attributes Array of attributes to parse
-   */
-  function _setStrokeFillOpacity(attributes) {
-    for (var attr in colorAttributes) {
-
-      if (!attributes[attr] || typeof attributes[colorAttributes[attr]] === 'undefined') {
-        continue;
-      }
-
-      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;
-  }
-
-  /**
-   * 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 angle = args[0];
-
-      matrix[0] = Math.cos(angle);
-      matrix[1] = Math.sin(angle);
-      matrix[2] = -Math.sin(angle);
-      matrix[3] = Math.cos(angle);
-    }
-
-    function scaleMatrix(matrix, args) {
-      var multiplierX = args[0],
-          multiplierY = (args.length === 2) ? args[1] : args[0];
-
-      matrix[0] = multiplierX;
-      matrix[3] = multiplierY;
-    }
-
-    function skewXMatrix(matrix, args) {
-      matrix[2] = args[0];
-    }
-
-    function skewYMatrix(matrix, args) {
-      matrix[1] = 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 = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
-
-        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) {
-              return (match !== '' && match != null);
-            }),
-            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':
-            skewXMatrix(matrix, args);
-            break;
-          case 'skewY':
-            skewYMatrix(matrix, args);
-            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;
-    };
-  })();
-
-  function parseFontDeclaration(value, oStyle) {
-
-    // TODO: support non-px font size
-    var match = value.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);
-
-    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 = parseFloat(fontSize);
-    }
-    if (fontFamily) {
-      oStyle.fontFamily = fontFamily;
-    }
-    if (lineHeight) {
-      oStyle.lineHeight = lineHeight === 'normal' ? 1 : lineHeight;
-    }
-  }
-
-  /**
-   * @private
-   */
-  function parseStyleString(style, oStyle) {
-    var attr, value;
-    style.replace(/;$/, '').split(';').forEach(function (chunk) {
-      var pair = chunk.split(':');
-
-      attr = normalizeAttr(pair[0].trim().toLowerCase());
-      value = normalizeValue(attr, pair[1].trim());
-
-      if (attr === 'font') {
-        parseFontDeclaration(value, oStyle);
-      }
-      else {
-        oStyle[attr] = value;
-      }
-    });
-  }
-
-  /**
-   * @private
-   */
-  function parseStyleObject(style, oStyle) {
-    var attr, value;
-    for (var prop in style) {
-      if (typeof style[prop] === 'undefined') {
-        continue;
-      }
-
-      attr = normalizeAttr(prop.toLowerCase());
-      value = normalizeValue(attr, style[prop]);
-
-      if (attr === 'font') {
-        parseFontDeclaration(value, oStyle);
-      }
-      else {
-        oStyle[attr] = value;
-      }
-    }
-  }
-
-  /**
-   * @private
-   */
-  function getGlobalStylesForElement(element) {
-    var styles = { };
-
-    for (var rule in fabric.cssRules) {
-      if (elementMatchesRule(element, rule.split(' '))) {
-        for (var property in fabric.cssRules[rule]) {
-          styles[property] = fabric.cssRules[rule][property];
-        }
-      }
-    }
-    return styles;
-  }
-
-  /**
-   * @private
-   */
-  function elementMatchesRule(element, selectors) {
-    var firstMatching, parentMatching = true;
-    //start from rightmost selector.
-    firstMatching = selectorMatches(element, selectors.pop());
-    if (firstMatching && selectors.length) {
-      parentMatching = doesSomeParentMatch(element, selectors);
-    }
-    return firstMatching && parentMatching && (selectors.length === 0);
-  }
-
-  function doesSomeParentMatch(element, selectors) {
-    var selector, parentMatching = true;
-    while (element.parentNode && element.parentNode.nodeType === 1 && selectors.length) {
-      if (parentMatching) {
-        selector = selectors.pop();
-      }
-      element = element.parentNode;
-      parentMatching = selectorMatches(element, selector);
-    }
-    return selectors.length === 0;
-  }
-  /**
-   * @private
-   */
-  function selectorMatches(element, selector) {
-    var nodeName = element.nodeName,
-        classNames = element.getAttribute('class'),
-        id = element.getAttribute('id'), matcher;
-    // 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, '');
-    }
-    if (classNames && selector.length) {
-      classNames = classNames.split(' ');
-      for (var i = classNames.length; i--;) {
-        matcher = new RegExp('\\.' + classNames[i] + '(?![a-zA-Z\\-]+)', 'i');
-        selector = selector.replace(matcher, '');
-      }
-    }
-    return selector.length === 0;
-  }
-
-  /**
-   * @private
-   */
-  function parseUseDirectives(doc) {
-    var nodelist = doc.getElementsByTagName('use');
-    while (nodelist.length) {
-      var el = nodelist[0],
-          xlink = el.getAttribute('xlink:href').substr(1),
-          x = el.getAttribute('x') || 0,
-          y = el.getAttribute('y') || 0,
-          el2 = doc.getElementById(xlink).cloneNode(true),
-          currentTrans = (el.getAttribute('transform') || '') + ' translate(' + x + ', ' + y + ')',
-          parentNode;
-
-      for (var j = 0, attrs = el.attributes, l = attrs.length; j < l; j++) {
-        var attr = attrs.item(j);
-        if (attr.nodeName === 'x' || attr.nodeName === 'y' || attr.nodeName === 'xlink:href') {
-          continue;
-        }
-
-        if (attr.nodeName === 'transform') {
-          currentTrans = currentTrans + ' ' + attr.nodeValue;
-        }
-        else {
-          el2.setAttribute(attr.nodeName, attr.nodeValue);
-        }
-      }
-
-      el2.setAttribute('transform', currentTrans);
-      el2.removeAttribute('id');
-      parentNode = el.parentNode;
-      parentNode.replaceChild(el2, el);
-    }
-  }
-
-  /**
-   * Add a <g> element that envelop all SCG elements and makes the viewbox transformMatrix descend on all elements
-   */
-  function addSvgTransform(doc, matrix) {
-    matrix[3] = matrix[0] = (matrix[0] > matrix[3] ? matrix[3] : matrix[0]);
-    if (!(matrix[0] !== 1 || matrix[3] !== 1 || matrix[4] !== 0 || matrix[5] !== 0)) {
-      return;
-    }
-    // default is to preserve aspect ratio
-    // preserveAspectRatio attribute to be implemented
-    var el = doc.ownerDocument.createElement('g');
-    while (doc.firstChild != null) {
-      el.appendChild(doc.firstChild);
-    }
-    el.setAttribute('transform','matrix(' + matrix[0] + ' ' + matrix[1] + ' ' + matrix[2] + ' ' + matrix[3] + ' ' + matrix[4] + ' ' + matrix[5] + ')');
-    doc.appendChild(el);
-  }
-
-  /**
-   * 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.
-   */
-  fabric.parseSVGDocument = (function() {
-
-    var reAllowedSVGTagNames = /^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,
-
-        // http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
-        // \d doesn't quite cut it (as we need to match an actual float number)
-
-        // matches, e.g.: +14.56e-12, etc.
-        reNum = '(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)',
-
-        reViewBoxAttrValue = new RegExp(
-          '^' +
-          '\\s*(' + reNum + '+)\\s*,?' +
-          '\\s*(' + reNum + '+)\\s*,?' +
-          '\\s*(' + reNum + '+)\\s*,?' +
-          '\\s*(' + reNum + '+)\\s*' +
-          '$'
-        );
-
-    function hasAncestorWithNodeName(element, nodeName) {
-      while (element && (element = element.parentNode)) {
-        if (nodeName.test(element.nodeName)) {
-          return true;
-        }
-      }
-      return false;
-    }
-
-    return function(doc, callback, reviver) {
-      if (!doc) {
-        return;
-      }
-      var startTime = new Date();
-
-      parseUseDirectives(doc);
-      /* http://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
-      *  as per spec, width and height attributes are to be considered
-      *  100% if no value is specified.
-      */
-      var viewBoxAttr = doc.getAttribute('viewBox'),
-          widthAttr = parseUnit(doc.getAttribute('width') || '100%'),
-          heightAttr = parseUnit(doc.getAttribute('height') || '100%'),
-          viewBoxWidth,
-          viewBoxHeight;
-
-      if (viewBoxAttr && (viewBoxAttr = viewBoxAttr.match(reViewBoxAttrValue))) {
-        var minX = parseFloat(viewBoxAttr[1]),
-            minY = parseFloat(viewBoxAttr[2]),
-            scaleX = 1, scaleY = 1;
-        viewBoxWidth = parseFloat(viewBoxAttr[3]);
-        viewBoxHeight = parseFloat(viewBoxAttr[4]);
-        if (widthAttr && widthAttr !== viewBoxWidth ) {
-          scaleX = widthAttr / viewBoxWidth;
-        }
-        if (heightAttr && heightAttr !== viewBoxHeight) {
-          scaleY = heightAttr / viewBoxHeight;
-        }
-        addSvgTransform(doc, [scaleX, 0, 0, scaleY, scaleX * -minX, scaleY * -minY]);
-      }
-
-      var descendants = fabric.util.toArray(doc.getElementsByTagName('*'));
-
-      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 (var i = 0, len = descendants.length; i < len; i++) {
-          arr[i] = descendants[i];
-        }
-        descendants = arr;
-      }
-
-      var elements = descendants.filter(function(el) {
-        return reAllowedSVGTagNames.test(el.tagName) &&
-              !hasAncestorWithNodeName(el, /^(?:pattern|defs)$/); // http://www.w3.org/TR/SVG/struct.html#DefsElement
-      });
-
-      if (!elements || (elements && !elements.length)) {
-        callback && callback([], {});
-        return;
-      }
-
-      var options = {
-        width: widthAttr ? widthAttr : viewBoxWidth,
-        height: heightAttr ? heightAttr : viewBoxHeight,
-        widthAttr: widthAttr,
-        heightAttr: heightAttr
-      };
-
-      fabric.gradientDefs = fabric.getGradientDefs(doc);
-      fabric.cssRules = fabric.getCSSRules(doc);
-      // Precedence of rules:   style > class > attribute
-
-      fabric.parseElements(elements, function(instances) {
-        fabric.documentParsingTime = new Date() - startTime;
-        if (callback) {
-          callback(instances, options);
-        }
-      }, clone(options), reviver);
-    };
-  })();
-
-   /**
-    * Used for caching SVG documents (loaded via `fabric.Canvas#loadSVGFromURL`)
-    * @namespace
-    */
-  var svgCache = {
-
-    /**
-    * @param {String} name
-    * @param {Function} callback
-    */
-    has: function (name, callback) {
-      callback(false);
-    },
-
-    get: function () {
-      /* NOOP */
-    },
-
-    set: function () {
-      /* NOOP */
-    }
-  };
-
-  /**
-   * @private
-   */
-  function _enlivenCachedObject(cachedObject) {
-
-    var objects = cachedObject.objects,
-        options = cachedObject.options;
-
-    objects = objects.map(function (o) {
-      return fabric[capitalize(o.type)].fromObject(o);
-    });
-
-    return ({ objects: objects, options: options });
-  }
-
-  /**
-   * @private
-   */
-  function _createSVGPattern(markup, canvas, property) {
-    if (canvas[property] && canvas[property].toSVG) {
-      markup.push(
-        '<pattern x="0" y="0" id="', property, 'Pattern" ',
-          'width="', canvas[property].source.width,
-          '" height="', canvas[property].source.height,
-          '" patternUnits="userSpaceOnUse">',
-        '<image x="0" y="0" ',
-        'width="', canvas[property].source.width,
-        '" height="', canvas[property].source.height,
-        '" xlink:href="', canvas[property].source.src,
-        '"></image></pattern>'
-      );
-    }
-  }
-
-  extend(fabric, {
-
-    /**
-     * 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 linearGradientEls = doc.getElementsByTagName('linearGradient'),
-          radialGradientEls = doc.getElementsByTagName('radialGradient'),
-          el, i, j = 0, id, xlink, elList = [ ],
-          gradientDefs = { }, idsToXlinkMap = { };
-
-      elList.length = linearGradientEls.length + radialGradientEls.length;
-      i = linearGradientEls.length;
-      while (i--) {
-        elList[j++] = linearGradientEls[i];
-      }
-      i = radialGradientEls.length;
-      while (i--) {
-        elList[j++] = radialGradientEls[i];
-      }
-
-      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) {
-
-      if (!element) {
-        return;
-      }
-
-      var value,
-          parentAttributes = { };
-
-      // if there's a parent container (`g` or `a` or `symbol` node), parse its attributes recursively upwards
-      if (element.parentNode && /^symbol|[g|a]$/i.test(element.parentNode.nodeName)) {
-        parentAttributes = fabric.parseAttributes(element.parentNode, attributes);
-      }
-
-      var ownAttributes = attributes.reduce(function(memo, attr) {
-        value = element.getAttribute(attr);
-        if (value) {
-          attr = normalizeAttr(attr);
-          value = normalizeValue(attr, value, parentAttributes);
-
-          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), fabric.parseStyleAttribute(element)));
-
-      return _setStrokeFillOpacity(extend(parentAttributes, ownAttributes));
-    },
-
-    /**
-     * 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) {
-      new fabric.ElementsParser(elements, callback, options, reviver).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;
-
-      i = 0;
-      len = points.length;
-      for (; 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'),
-          allRules = { }, rules;
-
-      // very crude parsing of style contents
-      for (var i = 0, len = styles.length; i < len; i++) {
-        var styleContents = styles[0].textContent;
-
-        // remove comments
-        styleContents = styleContents.replace(/\/\*[\s\S]*?\*\//g, '');
-
-        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 (var i = 0, len = propertyValuePairs.length; i < len; i++) {
-            var pair = propertyValuePairs[i].split(/\s*:\s*/),
-                property = normalizeAttr(pair[0]),
-                value = normalizeValue(property,pair[1],pair[0]);
-            ruleObj[property] = value;
-          }
-          rule = match[1];
-          rule.split(',').forEach(function(_rule) {
-            allRules[_rule.trim()] = 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.
-     */
-    loadSVGFromURL: function(url, callback, reviver) {
-
-      url = url.replace(/^\n\s*/, '').trim();
-      svgCache.has(url, function (hasUrl) {
-        if (hasUrl) {
-          svgCache.get(url, function (value) {
-            var enlivedRecord = _enlivenCachedObject(value);
-            callback(enlivedRecord.objects, enlivedRecord.options);
-          });
-        }
-        else {
-          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 (!xml || !xml.documentElement) {
-          return;
-        }
-
-        fabric.parseSVGDocument(xml.documentElement, function (results, options) {
-          svgCache.set(url, {
-            objects: fabric.util.array.invoke(results, 'toObject'),
-            options: options
-          });
-          callback(results, options);
-        }, reviver);
-      }
-    },
-
-    /**
-     * 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.
-     */
-    loadSVGFromString: function(string, callback, reviver) {
-      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) {
-        callback(results, options);
-      }, reviver);
-    },
-
-    /**
-     * Creates markup containing SVG font faces
-     * @param {Array} objects Array of fabric objects
-     * @return {String}
-     */
-    createSVGFontFacesMarkup: function(objects) {
-      var markup = '';
-
-      for (var i = 0, len = objects.length; i < len; i++) {
-        if (objects[i].type !== 'text' || !objects[i].path) {
-          continue;
-        }
-
-        markup += [
-          //jscs:disable validateIndentation
-          '@font-face {',
-            'font-family: ', objects[i].fontFamily, '; ',
-            'src: url(\'', objects[i].path, '\')',
-          '}'
-          //jscs:enable validateIndentation
-        ].join('');
-      }
-
-      if (markup) {
-        markup = [
-          //jscs:disable validateIndentation
-          '<style type="text/css">',
-            '<![CDATA[',
-              markup,
-            ']]>',
-          '</style>'
-          //jscs:enable validateIndentation
-        ].join('');
-      }
-
-      return markup;
-    },
-
-    /**
-     * Creates markup containing SVG referenced elements like patterns, gradients etc.
-     * @param {fabric.Canvas} canvas instance of fabric.Canvas
-     * @return {String}
-     */
-    createSVGRefElementsMarkup: function(canvas) {
-      var markup = [ ];
-
-      _createSVGPattern(markup, canvas, 'backgroundColor');
-      _createSVGPattern(markup, canvas, 'overlayColor');
-
-      return markup.join('');
-    }
-  });
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-fabric.ElementsParser = function(elements, callback, options, reviver) {
-  this.elements = elements;
-  this.callback = callback;
-  this.options = options;
-  this.reviver = reviver;
-};
-
-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++) {
-    (function(_this, i) {
-      setTimeout(function() {
-        _this.createObject(_this.elements[i], i);
-      }, 0);
-    })(this, i);
-  }
-};
-
-fabric.ElementsParser.prototype.createObject = function(el, index) {
-  var klass = fabric[fabric.util.string.capitalize(el.tagName)];
-  if (klass && klass.fromElement) {
-    try {
-      this._createObject(klass, el, index);
-    }
-    catch (err) {
-      fabric.log(err);
-    }
-  }
-  else {
-    this.checkIfDone();
-  }
-};
-
-fabric.ElementsParser.prototype._createObject = function(klass, el, index) {
-  if (klass.async) {
-    klass.fromElement(el, this.createCallback(index, el), this.options);
-  }
-  else {
-    var obj = klass.fromElement(el, this.options);
-    this.resolveGradient(obj, 'fill');
-    this.resolveGradient(obj, 'stroke');
-    this.reviver && this.reviver(el, obj);
-    this.instances[index] = obj;
-    this.checkIfDone();
-  }
-};
-
-fabric.ElementsParser.prototype.createCallback = function(index, el) {
-  var _this = this;
-  return function(obj) {
-    _this.resolveGradient(obj, 'fill');
-    _this.resolveGradient(obj, 'stroke');
-    _this.reviver && _this.reviver(el, obj);
-    _this.instances[index] = obj;
-    _this.checkIfDone();
-  };
-};
-
-fabric.ElementsParser.prototype.resolveGradient = function(obj, property) {
-
-  var instanceFillValue = obj.get(property);
-  if (!(/^url\(/).test(instanceFillValue)) {
-    return;
-  }
-  var gradientId = instanceFillValue.slice(5, instanceFillValue.length - 1);
-  if (fabric.gradientDefs[gradientId]) {
-    obj.set(property,
-      fabric.Gradient.fromElement(fabric.gradientDefs[gradientId], obj));
-  }
-};
-
-fabric.ElementsParser.prototype.checkIfDone = function() {
-  if (--this.numElements === 0) {
-    this.instances = this.instances.filter(function(el) {
-      return el != null;
-    });
-    this.callback(this.instances);
-  }
-};
-
-
-(function(global) {
-
-  'use strict';
-
-  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-
-  var fabric = global.fabric || (global.fabric = { });
-
-  if (fabric.Point) {
-    fabric.warn('fabric.Point is already defined');
-    return;
-  }
-
-  fabric.Point = Point;
-
-  /**
-   * Point class
-   * @class fabric.Point
-   * @memberOf fabric
-   * @constructor
-   * @param {Number} x
-   * @param {Number} y
-   * @return {fabric.Point} thisArg
-   */
-  function Point(x, y) {
-    this.x = x;
-    this.y = y;
-  }
-
-  Point.prototype = /** @lends fabric.Point.prototype */ {
-
-    constructor: Point,
-
-    /**
-     * Adds another point to this one and returns another one
-     * @param {fabric.Point} that
-     * @return {fabric.Point} new Point instance with added values
-     */
-    add: function (that) {
-      return new Point(this.x + that.x, this.y + that.y);
-    },
-
-    /**
-     * Adds another point to this one
-     * @param {fabric.Point} that
-     * @return {fabric.Point} thisArg
-     */
-    addEquals: function (that) {
-      this.x += that.x;
-      this.y += that.y;
-      return this;
-    },
-
-    /**
-     * Adds value to this point and returns a new one
-     * @param {Number} scalar
-     * @return {fabric.Point} new Point with added value
-     */
-    scalarAdd: function (scalar) {
-      return new Point(this.x + scalar, this.y + scalar);
-    },
-
-    /**
-     * Adds value to this point
-     * @param {Number} scalar
-     * @return {fabric.Point} thisArg
-     */
-    scalarAddEquals: function (scalar) {
-      this.x += scalar;
-      this.y += scalar;
-      return this;
-    },
-
-    /**
-     * Subtracts another point from this point and returns a new one
-     * @param {fabric.Point} that
-     * @return {fabric.Point} new Point object with subtracted values
-     */
-    subtract: function (that) {
-      return new Point(this.x - that.x, this.y - that.y);
-    },
-
-    /**
-     * Subtracts another point from this point
-     * @param {fabric.Point} that
-     * @return {fabric.Point} thisArg
-     */
-    subtractEquals: function (that) {
-      this.x -= that.x;
-      this.y -= that.y;
-      return this;
-    },
-
-    /**
-     * Subtracts value from this point and returns a new one
-     * @param {Number} scalar
-     * @return {fabric.Point}
-     */
-    scalarSubtract: function (scalar) {
-      return new Point(this.x - scalar, this.y - scalar);
-    },
-
-    /**
-     * Subtracts value from this point
-     * @param {Number} scalar
-     * @return {fabric.Point} thisArg
-     */
-    scalarSubtractEquals: function (scalar) {
-      this.x -= scalar;
-      this.y -= scalar;
-      return this;
-    },
-
-    /**
-     * Miltiplies this point by a value and returns a new one
-     * @param {Number} scalar
-     * @return {fabric.Point}
-     */
-    multiply: function (scalar) {
-      return new Point(this.x * scalar, this.y * scalar);
-    },
-
-    /**
-     * Miltiplies this point by a value
-     * @param {Number} scalar
-     * @return {fabric.Point} thisArg
-     */
-    multiplyEquals: function (scalar) {
-      this.x *= scalar;
-      this.y *= scalar;
-      return this;
-    },
-
-    /**
-     * Divides this point by a value and returns a new one
-     * @param {Number} scalar
-     * @return {fabric.Point}
-     */
-    divide: function (scalar) {
-      return new Point(this.x / scalar, this.y / scalar);
-    },
-
-    /**
-     * Divides this point by a value
-     * @param {Number} scalar
-     * @return {fabric.Point} thisArg
-     */
-    divideEquals: function (scalar) {
-      this.x /= scalar;
-      this.y /= scalar;
-      return this;
-    },
-
-    /**
-     * Returns true if this point is equal to another one
-     * @param {fabric.Point} that
-     * @return {Boolean}
-     */
-    eq: function (that) {
-      return (this.x === that.x && this.y === that.y);
-    },
-
-    /**
-     * Returns true if this point is less than another one
-     * @param {fabric.Point} that
-     * @return {Boolean}
-     */
-    lt: function (that) {
-      return (this.x < that.x && this.y < that.y);
-    },
-
-    /**
-     * Returns true if this point is less than or equal to another one
-     * @param {fabric.Point} that
-     * @return {Boolean}
-     */
-    lte: function (that) {
-      return (this.x <= that.x && this.y <= that.y);
-    },
-
-    /**
-
-     * Returns true if this point is greater another one
-     * @param {fabric.Point} that
-     * @return {Boolean}
-     */
-    gt: function (that) {
-      return (this.x > that.x && this.y > that.y);
-    },
-
-    /**
-     * 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);
-    },
-
-    /**
-     * Returns new point which is the result of linear interpolation with this one and another one
-     * @param {fabric.Point} that
-     * @param {Number} t
-     * @return {fabric.Point}
-     */
-    lerp: function (that, t) {
-      return new Point(this.x + (that.x - this.x) * t, this.y + (that.y - this.y) * t);
-    },
-
-    /**
-     * 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);
-    },
-
-    /**
-     * Returns the point between this point and another one
-     * @param {fabric.Point} that
-     * @return {fabric.Point}
-     */
-    midPointFrom: function (that) {
-      return new Point(this.x + (that.x - this.x)/2, this.y + (that.y - this.y)/2);
-    },
-
-    /**
-     * 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));
-    },
-
-    /**
-     * 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));
-    },
-
-    /**
-     * Returns string representation of this point
-     * @return {String}
-     */
-    toString: function () {
-      return this.x + ',' + this.y;
-    },
-
-    /**
-     * Sets x/y of this point
-     * @param {Number} x
-     * @return {Number} y
-     */
-    setXY: function (x, y) {
-      this.x = x;
-      this.y = y;
-    },
-
-    /**
-     * Sets x/y of this point from another point
-     * @param {fabric.Point} that
-     */
-    setFromPoint: function (that) {
-      this.x = that.x;
-      this.y = that.y;
-    },
-
-    /**
-     * Swaps x/y of this point and another point
-     * @param {fabric.Point} that
-     */
-    swap: function (that) {
-      var x = this.x,
-          y = this.y;
-      this.x = that.x;
-      this.y = that.y;
-      that.x = x;
-      that.y = y;
-    }
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  /* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */
-  var fabric = global.fabric || (global.fabric = { });
-
-  if (fabric.Intersection) {
-    fabric.warn('fabric.Intersection is already defined');
-    return;
-  }
-
-  /**
-   * Intersection class
-   * @class fabric.Intersection
-   * @memberOf fabric
-   * @constructor
-   */
-  function Intersection(status) {
-    this.status = status;
-    this.points = [];
-  }
-
-  fabric.Intersection = Intersection;
-
-  fabric.Intersection.prototype = /** @lends fabric.Intersection.prototype */ {
-
-    /**
-     * Appends a point to intersection
-     * @param {fabric.Point} point
-     */
-    appendPoint: function (point) {
-      this.points.push(point);
-    },
-
-    /**
-     * Appends points to intersection
-     * @param {Array} points
-     */
-    appendPoints: function (points) {
-      this.points = this.points.concat(points);
-    }
-  };
-
-  /**
-   * Checks if one line intersects another
-   * @static
-   * @param {fabric.Point} a1
-   * @param {fabric.Point} a2
-   * @param {fabric.Point} b1
-   * @param {fabric.Point} b2
-   * @return {fabric.Intersection}
-   */
-  fabric.Intersection.intersectLineLine = function (a1, a2, b1, b2) {
-    var result,
-        uaT = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
-        ubT = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
-        uB = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
-    if (uB !== 0) {
-      var ua = uaT / uB,
-          ub = ubT / uB;
-      if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
-        result = new Intersection('Intersection');
-        result.points.push(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');
-      }
-    }
-    return result;
-  };
-
-  /**
-   * Checks if line intersects polygon
-   * @static
-   * @param {fabric.Point} a1
-   * @param {fabric.Point} a2
-   * @param {Array} points
-   * @return {fabric.Intersection}
-   */
-  fabric.Intersection.intersectLinePolygon = function(a1,a2,points){
-    var result = new Intersection(),
-        length = points.length;
-
-    for (var i = 0; i < length; i++) {
-      var b1 = points[i],
-          b2 = points[(i + 1) % length],
-          inter = Intersection.intersectLineLine(a1, a2, b1, b2);
-
-      result.appendPoints(inter.points);
-    }
-    if (result.points.length > 0) {
-      result.status = 'Intersection';
-    }
-    return result;
-  };
-
-  /**
-   * Checks if polygon intersects another polygon
-   * @static
-   * @param {Array} points1
-   * @param {Array} points2
-   * @return {fabric.Intersection}
-   */
-  fabric.Intersection.intersectPolygonPolygon = function (points1, points2) {
-    var result = new Intersection(),
-        length = points1.length;
-
-    for (var i = 0; i < length; i++) {
-      var a1 = points1[i],
-          a2 = points1[(i + 1) % length],
-          inter = Intersection.intersectLinePolygon(a1, a2, points2);
-
-      result.appendPoints(inter.points);
-    }
-    if (result.points.length > 0) {
-      result.status = 'Intersection';
-    }
-    return result;
-  };
-
-  /**
-   * Checks if polygon intersects rectangle
-   * @static
-   * @param {Array} points
-   * @param {Number} r1
-   * @param {Number} r2
-   * @return {fabric.Intersection}
-   */
-  fabric.Intersection.intersectPolygonRectangle = function (points, r1, r2) {
-    var min = r1.min(r2),
-        max = r1.max(r2),
-        topRight = new fabric.Point(max.x, min.y),
-        bottomLeft = new fabric.Point(min.x, max.y),
-        inter1 = Intersection.intersectLinePolygon(min, topRight, points),
-        inter2 = Intersection.intersectLinePolygon(topRight, max, points),
-        inter3 = Intersection.intersectLinePolygon(max, bottomLeft, points),
-        inter4 = Intersection.intersectLinePolygon(bottomLeft, min, points),
-        result = new Intersection();
-
-    result.appendPoints(inter1.points);
-    result.appendPoints(inter2.points);
-    result.appendPoints(inter3.points);
-    result.appendPoints(inter4.points);
-
-    if (result.points.length > 0) {
-      result.status = 'Intersection';
-    }
-    return result;
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { });
-
-  if (fabric.Color) {
-    fabric.warn('fabric.Color is already defined.');
-    return;
-  }
-
-  /**
-   * Color class
-   * The purpose of {@link fabric.Color} is to abstract and encapsulate common color operations;
-   * {@link fabric.Color} is a constructor and creates instances of {@link fabric.Color} objects.
-   *
-   * @class fabric.Color
-   * @param {String} color optional in hex or rgb(a) format
-   * @return {fabric.Color} thisArg
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#colors}
-   */
-  function Color(color) {
-    if (!color) {
-      this.setSource([0, 0, 0, 1]);
-    }
-    else {
-      this._tryParsingColor(color);
-    }
-  }
-
-  fabric.Color = Color;
-
-  fabric.Color.prototype = /** @lends fabric.Color.prototype */ {
-
-    /**
-     * @private
-     * @param {String|Array} color Color value to parse
-     */
-    _tryParsingColor: function(color) {
-      var source;
-
-      if (color in Color.colorNameMap) {
-        color = Color.colorNameMap[color];
-      }
-
-      if (color === 'transparent') {
-        this.setSource([255,255,255,0]);
-        return;
-      }
-
-      source = Color.sourceFromHex(color);
-
-      if (!source) {
-        source = Color.sourceFromRgb(color);
-      }
-      if (!source) {
-        source = Color.sourceFromHsl(color);
-      }
-      if (source) {
-        this.setSource(source);
-      }
-    },
-
-    /**
-     * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
-     * @private
-     * @param {Number} r Red color value
-     * @param {Number} g Green color value
-     * @param {Number} b Blue color value
-     * @return {Array} Hsl color
-     */
-    _rgbToHsl: function(r, g, b) {
-      r /= 255, g /= 255, b /= 255;
-
-      var h, s, l,
-          max = fabric.util.array.max([r, g, b]),
-          min = fabric.util.array.min([r, g, b]);
-
-      l = (max + min) / 2;
-
-      if (max === min) {
-        h = s = 0; // achromatic
-      }
-      else {
-        var d = max - min;
-        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
-        switch (max) {
-          case r:
-            h = (g - b) / d + (g < b ? 6 : 0);
-            break;
-          case g:
-            h = (b - r) / d + 2;
-            break;
-          case b:
-            h = (r - g) / d + 4;
-            break;
-        }
-        h /= 6;
-      }
-
-      return [
-        Math.round(h * 360),
-        Math.round(s * 100),
-        Math.round(l * 100)
-      ];
-    },
-
-    /**
-     * Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
-     * @return {Array}
-     */
-    getSource: function() {
-      return this._source;
-    },
-
-    /**
-     * Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
-     * @param {Array} source
-     */
-    setSource: function(source) {
-      this._source = source;
-    },
-
-    /**
-     * Returns color represenation in RGB format
-     * @return {String} ex: rgb(0-255,0-255,0-255)
-     */
-    toRgb: function() {
-      var source = this.getSource();
-      return 'rgb(' + source[0] + ',' + source[1] + ',' + source[2] + ')';
-    },
-
-    /**
-     * Returns color represenation in RGBA format
-     * @return {String} ex: rgba(0-255,0-255,0-255,0-1)
-     */
-    toRgba: function() {
-      var source = this.getSource();
-      return 'rgba(' + source[0] + ',' + source[1] + ',' + source[2] + ',' + source[3] + ')';
-    },
-
-    /**
-     * Returns color represenation in HSL format
-     * @return {String} ex: hsl(0-360,0%-100%,0%-100%)
-     */
-    toHsl: function() {
-      var source = this.getSource(),
-          hsl = this._rgbToHsl(source[0], source[1], source[2]);
-
-      return 'hsl(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%)';
-    },
-
-    /**
-     * Returns color represenation in HSLA format
-     * @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
-     */
-    toHsla: function() {
-      var source = this.getSource(),
-          hsl = this._rgbToHsl(source[0], source[1], source[2]);
-
-      return 'hsla(' + hsl[0] + ',' + hsl[1] + '%,' + hsl[2] + '%,' + source[3] + ')';
-    },
-
-    /**
-     * Returns color represenation in HEX format
-     * @return {String} ex: FF5555
-     */
-    toHex: function() {
-      var source = this.getSource(), r, g, b;
-
-      r = source[0].toString(16);
-      r = (r.length === 1) ? ('0' + r) : r;
-
-      g = source[1].toString(16);
-      g = (g.length === 1) ? ('0' + g) : g;
-
-      b = source[2].toString(16);
-      b = (b.length === 1) ? ('0' + b) : b;
-
-      return r.toUpperCase() + g.toUpperCase() + b.toUpperCase();
-    },
-
-    /**
-     * Gets value of alpha channel for this color
-     * @return {Number} 0-1
-     */
-    getAlpha: function() {
-      return this.getSource()[3];
-    },
-
-    /**
-     * Sets value of alpha channel for this color
-     * @param {Number} alpha Alpha value 0-1
-     * @return {fabric.Color} thisArg
-     */
-    setAlpha: function(alpha) {
-      var source = this.getSource();
-      source[3] = alpha;
-      this.setSource(source);
-      return this;
-    },
-
-    /**
-     * Transforms color to its grayscale representation
-     * @return {fabric.Color} thisArg
-     */
-    toGrayscale: function() {
-      var source = this.getSource(),
-          average = parseInt((source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0), 10),
-          currentAlpha = source[3];
-      this.setSource([average, average, average, currentAlpha]);
-      return this;
-    },
-
-    /**
-     * Transforms color to its black and white representation
-     * @param {Number} threshold
-     * @return {fabric.Color} thisArg
-     */
-    toBlackWhite: function(threshold) {
-      var source = this.getSource(),
-          average = (source[0] * 0.3 + source[1] * 0.59 + source[2] * 0.11).toFixed(0),
-          currentAlpha = source[3];
-
-      threshold = threshold || 127;
-
-      average = (Number(average) < Number(threshold)) ? 0 : 255;
-      this.setSource([average, average, average, currentAlpha]);
-      return this;
-    },
-
-    /**
-     * Overlays color with another color
-     * @param {String|fabric.Color} otherColor
-     * @return {fabric.Color} thisArg
-     */
-    overlayWith: function(otherColor) {
-      if (!(otherColor instanceof Color)) {
-        otherColor = new Color(otherColor);
-      }
-
-      var result = [],
-          alpha = this.getAlpha(),
-          otherAlpha = 0.5,
-          source = this.getSource(),
-          otherSource = otherColor.getSource();
-
-      for (var i = 0; i < 3; i++) {
-        result.push(Math.round((source[i] * (1 - otherAlpha)) + (otherSource[i] * otherAlpha)));
-      }
-
-      result[3] = alpha;
-      this.setSource(result);
-      return this;
-    }
-  };
-
-  /**
-   * Regex matching color in RGB or RGBA formats (ex: rgb(0, 0, 0), rgba(255, 100, 10, 0.5), rgba( 255 , 100 , 10 , 0.5 ), rgb(1,1,1), rgba(100%, 60%, 10%, 0.5))
-   * @static
-   * @field
-   * @memberOf fabric.Color
-   */
-  fabric.Color.reRGBa = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
-
-  /**
-   * Regex matching color in HSL or HSLA formats (ex: hsl(200, 80%, 10%), hsla(300, 50%, 80%, 0.5), hsla( 300 , 50% , 80% , 0.5 ))
-   * @static
-   * @field
-   * @memberOf fabric.Color
-   */
-  fabric.Color.reHSLa = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/;
-
-  /**
-   * Regex matching color in HEX format (ex: #FF5555, 010155, aff)
-   * @static
-   * @field
-   * @memberOf fabric.Color
-   */
-  fabric.Color.reHex = /^#?([0-9a-f]{6}|[0-9a-f]{3})$/i;
-
-  /**
-   * Map of the 17 basic color names with HEX code
-   * @static
-   * @field
-   * @memberOf fabric.Color
-   * @see: http://www.w3.org/TR/CSS2/syndata.html#color-units
-   */
-  fabric.Color.colorNameMap = {
-    aqua:    '#00FFFF',
-    black:   '#000000',
-    blue:    '#0000FF',
-    fuchsia: '#FF00FF',
-    gray:    '#808080',
-    green:   '#008000',
-    lime:    '#00FF00',
-    maroon:  '#800000',
-    navy:    '#000080',
-    olive:   '#808000',
-    orange:  '#FFA500',
-    purple:  '#800080',
-    red:     '#FF0000',
-    silver:  '#C0C0C0',
-    teal:    '#008080',
-    white:   '#FFFFFF',
-    yellow:  '#FFFF00'
-  };
-
-  /**
-   * @private
-   * @param {Number} p
-   * @param {Number} q
-   * @param {Number} t
-   * @return {Number}
-   */
-  function hue2rgb(p, q, t){
-    if (t < 0) {
-      t += 1;
-    }
-    if (t > 1) {
-      t -= 1;
-    }
-    if (t < 1/6) {
-      return p + (q - p) * 6 * t;
-    }
-    if (t < 1/2) {
-      return q;
-    }
-    if (t < 2/3) {
-      return p + (q - p) * (2/3 - t) * 6;
-    }
-    return p;
-  }
-
-  /**
-   * Returns new color object, when given a color in RGB format
-   * @memberOf fabric.Color
-   * @param {String} color Color value ex: rgb(0-255,0-255,0-255)
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromRgb = function(color) {
-    return Color.fromSource(Color.sourceFromRgb(color));
-  };
-
-  /**
-   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
-   * @memberOf fabric.Color
-   * @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
-   * @return {Array} source
-   */
-  fabric.Color.sourceFromRgb = function(color) {
-    var match = color.match(Color.reRGBa);
-    if (match) {
-      var r = parseInt(match[1], 10) / (/%$/.test(match[1]) ? 100 : 1) * (/%$/.test(match[1]) ? 255 : 1),
-          g = parseInt(match[2], 10) / (/%$/.test(match[2]) ? 100 : 1) * (/%$/.test(match[2]) ? 255 : 1),
-          b = parseInt(match[3], 10) / (/%$/.test(match[3]) ? 100 : 1) * (/%$/.test(match[3]) ? 255 : 1);
-
-      return [
-        parseInt(r, 10),
-        parseInt(g, 10),
-        parseInt(b, 10),
-        match[4] ? parseFloat(match[4]) : 1
-      ];
-    }
-  };
-
-  /**
-   * Returns new color object, when given a color in RGBA format
-   * @static
-   * @function
-   * @memberOf fabric.Color
-   * @param {String} color
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromRgba = Color.fromRgb;
-
-  /**
-   * Returns new color object, when given a color in HSL format
-   * @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
-   * @memberOf fabric.Color
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromHsl = function(color) {
-    return Color.fromSource(Color.sourceFromHsl(color));
-  };
-
-  /**
-   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
-   * Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
-   * @memberOf fabric.Color
-   * @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
-   * @return {Array} source
-   * @see http://http://www.w3.org/TR/css3-color/#hsl-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 <= 0.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
-    ];
-  };
-
-  /**
-   * Returns new color object, when given a color in HSLA format
-   * @static
-   * @function
-   * @memberOf fabric.Color
-   * @param {String} color
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromHsla = Color.fromHsl;
-
-  /**
-   * Returns new color object, when given a color in HEX format
-   * @static
-   * @memberOf fabric.Color
-   * @param {String} color Color value ex: FF5555
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromHex = function(color) {
-    return Color.fromSource(Color.sourceFromHex(color));
-  };
-
-  /**
-   * Returns array represenatation (ex: [100, 100, 200, 1]) of a color that's in HEX format
-   * @static
-   * @memberOf fabric.Color
-   * @param {String} color ex: FF5555
-   * @return {Array} source
-   */
-  fabric.Color.sourceFromHex = function(color) {
-    if (color.match(Color.reHex)) {
-      var value = color.slice(color.indexOf('#') + 1),
-          isShortNotation = (value.length === 3),
-          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);
-
-      return [
-        parseInt(r, 16),
-        parseInt(g, 16),
-        parseInt(b, 16),
-        1
-      ];
-    }
-  };
-
-  /**
-   * Returns new color object, when given color in array representation (ex: [200, 100, 100, 0.5])
-   * @static
-   * @memberOf fabric.Color
-   * @param {Array} source
-   * @return {fabric.Color}
-   */
-  fabric.Color.fromSource = function(source) {
-    var oColor = new Color();
-    oColor.setSource(source);
-    return oColor;
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
-  /* _FROM_SVG_START_ */
-  function getColorStop(el) {
-    var style = el.getAttribute('style'),
-        offset = el.getAttribute('offset'),
-        color, colorAlpha, opacity;
-
-    // convert percents to absolute values
-    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 (var 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
-    };
-  }
-
-  function getLinearCoords(el) {
-    return {
-      x1: el.getAttribute('x1') || 0,
-      y1: el.getAttribute('y1') || 0,
-      x2: el.getAttribute('x2') || '100%',
-      y2: el.getAttribute('y2') || 0
-    };
-  }
-
-  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%'
-    };
-  }
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Gradient class
-   * @class fabric.Gradient
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#gradients}
-   * @see {@link fabric.Gradient#initialize} for constructor definition
-   */
-  fabric.Gradient = fabric.util.createClass(/** @lends fabric.Gradient.prototype */ {
-
-    /**
-     * Horizontal offset for aligning gradients coming from SVG when outside pathgroups
-     * @type Number
-     * @default 0
-     */
-    offsetX: 0,
-
-    /**
-     * Vertical offset for aligning gradients coming from SVG when outside pathgroups
-     * @type Number
-     * @default 0
-     */
-    offsetY: 0,
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object with type, coords, gradientUnits and colorStops
-     * @return {fabric.Gradient} thisArg
-     */
-    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;
-    },
-
-    /**
-     * Adds another colorStop
-     * @param {Object} colorStop Object with offset and color
-     * @return {fabric.Gradient} thisArg
-     */
-    addColorStop: function(colorStop) {
-      for (var position in colorStop) {
-        var color = new fabric.Color(colorStop[position]);
-        this.colorStops.push({
-          offset: position,
-          color: color.toRgb(),
-          opacity: color.getAlpha()
-        });
-      }
-      return this;
-    },
-
-    /**
-     * Returns object representation of a gradient
-     * @return {Object}
-     */
-    toObject: function() {
-      return {
-        type: this.type,
-        coords: this.coords,
-        colorStops: this.colorStops,
-        offsetX: this.offsetX,
-        offsetY: this.offsetY
-      };
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an gradient
-     * @param {Object} object Object to create a gradient for
-     * @param {Boolean} normalize Whether coords should be normalized
-     * @return {String} SVG representation of an gradient (linear/radial)
-     */
-    toSVG: function(object) {
-      var coords = fabric.util.object.clone(this.coords),
-          markup, commonAttributes;
-
-      // colorStops must be sorted ascending
-      this.colorStops.sort(function(a, b) {
-        return a.offset - b.offset;
-      });
-
-      if (!(object.group && object.group.type === 'path-group')) {
-        for (var prop in coords) {
-          if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
-            coords[prop] += this.offsetX - object.width / 2;
-          }
-          else if (prop === 'y1' || prop === 'y2') {
-            coords[prop] += this.offsetY - object.height / 2;
-          }
-        }
-      }
-
-      commonAttributes = 'id="SVGID_' + this.id +
-                     '" gradientUnits="userSpaceOnUse"';
-      if (this.gradientTransform) {
-        commonAttributes += ' gradientTransform="matrix(' + this.gradientTransform.join(' ') + ')" ';
-      }
-      if (this.type === 'linear') {
-        markup = [
-          //jscs:disable validateIndentation
-          '<linearGradient ',
-            commonAttributes,
-            ' x1="', coords.x1,
-            '" y1="', coords.y1,
-            '" x2="', coords.x2,
-            '" y2="', coords.y2,
-          '">\n'
-          //jscs:enable validateIndentation
-        ];
-      }
-      else if (this.type === 'radial') {
-        markup = [
-          //jscs:disable validateIndentation
-          '<radialGradient ',
-            commonAttributes,
-            ' cx="', coords.x2,
-            '" cy="', coords.y2,
-            '" r="', coords.r2,
-            '" fx="', coords.x1,
-            '" fy="', coords.y1,
-          '">\n'
-          //jscs:enable validateIndentation
-        ];
-      }
-
-      for (var i = 0; i < this.colorStops.length; i++) {
-        markup.push(
-          //jscs:disable validateIndentation
-          '<stop ',
-            'offset="', (this.colorStops[i].offset * 100) + '%',
-            '" style="stop-color:', this.colorStops[i].color,
-            (this.colorStops[i].opacity != null ? ';stop-opacity: ' + this.colorStops[i].opacity : ';'),
-          '"/>\n'
-          //jscs:enable validateIndentation
-        );
-      }
-
-      markup.push((this.type === 'linear' ? '</linearGradient>\n' : '</radialGradient>\n'));
-
-      return markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns an instance of CanvasGradient
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @return {CanvasGradient}
-     */
-    toLive: function(ctx) {
-      var gradient;
-
-      if (!this.type) {
-        return;
-      }
-
-      if (this.type === 'linear') {
-        gradient = ctx.createLinearGradient(
-          this.coords.x1, this.coords.y1, this.coords.x2, this.coords.y2);
-      }
-      else if (this.type === 'radial') {
-        gradient = ctx.createRadialGradient(
-          this.coords.x1, this.coords.y1, this.coords.r1, this.coords.x2, this.coords.y2, this.coords.r2);
-      }
-
-      for (var 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(parseFloat(offset), color);
-      }
-
-      return gradient;
-    }
-  });
-
-  fabric.util.object.extend(fabric.Gradient, {
-
-    /* _FROM_SVG_START_ */
-    /**
-     * Returns {@link fabric.Gradient} instance from an SVG element
-     * @static
-     * @memberof fabric.Gradient
-     * @param {SVGGradientElement} el SVG gradient element
-     * @param {fabric.Object} instance
-     * @return {fabric.Gradient} Gradient instance
-     * @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement
-     * @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
-     */
-    fromElement: function(el, instance) {
-
-      /**
-       *  @example:
-       *
-       *  <linearGradient id="linearGrad1">
-       *    <stop offset="0%" stop-color="white"/>
-       *    <stop offset="100%" stop-color="black"/>
-       *  </linearGradient>
-       *
-       *  OR
-       *
-       *  <linearGradient id="linearGrad2">
-       *    <stop offset="0" style="stop-color:rgb(255,255,255)"/>
-       *    <stop offset="1" style="stop-color:rgb(0,0,0)"/>
-       *  </linearGradient>
-       *
-       *  OR
-       *
-       *  <radialGradient id="radialGrad1">
-       *    <stop offset="0%" stop-color="white" stop-opacity="1" />
-       *    <stop offset="50%" stop-color="black" stop-opacity="0.5" />
-       *    <stop offset="100%" stop-color="white" stop-opacity="1" />
-       *  </radialGradient>
-       *
-       *  OR
-       *
-       *  <radialGradient id="radialGrad2">
-       *    <stop offset="0" stop-color="rgb(255,255,255)" />
-       *    <stop offset="0.5" stop-color="rgb(0,0,0)" />
-       *    <stop offset="1" stop-color="rgb(255,255,255)" />
-       *  </radialGradient>
-       *
-       */
-
-      var colorStopEls = el.getElementsByTagName('stop'),
-          type = (el.nodeName === 'linearGradient' ? 'linear' : 'radial'),
-          gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox',
-          gradientTransform = el.getAttribute('gradientTransform'),
-          colorStops = [],
-          coords = { }, ellipseMatrix;
-
-      if (type === 'linear') {
-        coords = getLinearCoords(el);
-      }
-      else if (type === 'radial') {
-        coords = getRadialCoords(el);
-      }
-
-      for (var 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;
-    },
-    /* _FROM_SVG_END_ */
-
-    /**
-     * Returns {@link fabric.Gradient} instance from its object representation
-     * @static
-     * @memberof fabric.Gradient
-     * @param {Object} obj
-     * @param {Object} [options] Options object
-     */
-    forObject: function(obj, options) {
-      options || (options = { });
-      _convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse');
-      return new fabric.Gradient(options);
-    }
-  });
-
-  /**
-   * @private
-   */
-  function _convertPercentUnitsToValues(object, options, gradientUnits) {
-    var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = '';
-    for (var prop in options) {
-      propValue = parseFloat(options[prop], 10);
-      if (typeof options[prop] === 'string' && /^\d+%$/.test(options[prop])) {
-        multFactor = 0.01;
-      }
-      else {
-        multFactor = 1;
-      }
-      if (prop === 'x1' || prop === 'x2' || prop === 'r2') {
-        multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1;
-        addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0;
-      }
-      else if (prop === 'y1' || prop === 'y2') {
-        multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1;
-        addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0;
-      }
-      options[prop] = propValue * multFactor + addFactor;
-    }
-    if (object.type === 'ellipse' && options.r2 !== null && gradientUnits === 'objectBoundingBox' && object.rx !== object.ry) {
-      var scaleFactor = object.ry/object.rx;
-      ellipseMatrix = ' scale(1, ' + scaleFactor + ')';
-      if (options.y1) {
-        options.y1 /= scaleFactor;
-      }
-      if (options.y2) {
-        options.y2 /= scaleFactor;
-      }
-    }
-    return ellipseMatrix;
-  }
-})();
-
-
-/**
- * Pattern class
- * @class fabric.Pattern
- * @see {@link http://fabricjs.com/patterns/|Pattern demo}
- * @see {@link http://fabricjs.com/dynamic-patterns/|DynamicPattern demo}
- * @see {@link fabric.Pattern#initialize} for constructor definition
- */
-fabric.Pattern = fabric.util.createClass(/** @lends fabric.Pattern.prototype */ {
-
-  /**
-   * Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
-   * @type String
-   * @default
-   */
-  repeat: 'repeat',
-
-  /**
-   * Pattern horizontal offset from object's left/top corner
-   * @type Number
-   * @default
-   */
-  offsetX: 0,
-
-  /**
-   * Pattern vertical offset from object's left/top corner
-   * @type Number
-   * @default
-   */
-  offsetY: 0,
-
-  /**
-   * Constructor
-   * @param {Object} [options] Options object
-   * @return {fabric.Pattern} thisArg
-   */
-  initialize: function(options) {
-    options || (options = { });
-
-    this.id = fabric.Object.__uid++;
-
-    if (options.source) {
-      if (typeof options.source === 'string') {
-        // function string
-        if (typeof fabric.util.getFunctionBody(options.source) !== 'undefined') {
-          this.source = new Function(fabric.util.getFunctionBody(options.source));
-        }
-        else {
-          // img src string
-          var _this = this;
-          this.source = fabric.util.createImage();
-          fabric.util.loadImage(options.source, function(img) {
-            _this.source = img;
-          });
-        }
-      }
-      else {
-        // img element
-        this.source = options.source;
-      }
-    }
-    if (options.repeat) {
-      this.repeat = options.repeat;
-    }
-    if (options.offsetX) {
-      this.offsetX = options.offsetX;
-    }
-    if (options.offsetY) {
-      this.offsetY = options.offsetY;
-    }
-  },
-
-  /**
-   * Returns object representation of a pattern
-   * @return {Object} Object representation of a pattern instance
-   */
-  toObject: function() {
-
-    var source;
-
-    // callback
-    if (typeof this.source === 'function') {
-      source = String(this.source);
-    }
-    // <img> element
-    else if (typeof this.source.src === 'string') {
-      source = this.source.src;
-    }
-
-    return {
-      source: source,
-      repeat: this.repeat,
-      offsetX: this.offsetX,
-      offsetY: this.offsetY
-    };
-  },
-
-  /* _TO_SVG_START_ */
-  /**
-   * Returns SVG representation of a pattern
-   * @param {fabric.Object} object
-   * @return {String} SVG representation of a pattern
-   */
-  toSVG: function(object) {
-    var patternSource = typeof this.source === 'function' ? this.source() : this.source,
-        patternWidth = patternSource.width / object.getWidth(),
-        patternHeight = patternSource.height / object.getHeight(),
-        patternImgSrc = '';
-
-    if (patternSource.src) {
-      patternImgSrc = patternSource.src;
-    }
-    else if (patternSource.toDataURL) {
-      patternImgSrc = patternSource.toDataURL();
-    }
-
-    return '<pattern id="SVGID_' + this.id +
-                  '" x="' + this.offsetX +
-                  '" y="' + this.offsetY +
-                  '" width="' + patternWidth +
-                  '" height="' + patternHeight + '">' +
-             '<image x="0" y="0"' +
-                    ' width="' + patternSource.width +
-                    '" height="' + patternSource.height +
-                    '" xlink:href="' + patternImgSrc +
-             '"></image>' +
-           '</pattern>';
-  },
-  /* _TO_SVG_END_ */
-
-  /**
-   * Returns an instance of CanvasPattern
-   * @param {CanvasRenderingContext2D} ctx Context to create pattern
-   * @return {CanvasPattern}
-   */
-  toLive: function(ctx) {
-    var source = typeof this.source === 'function'
-      ? this.source()
-      : this.source;
-
-    // if the image failed to load, return, and allow rest to continue loading
-    if (!source) {
-      return '';
-    }
-
-    // if an image
-    if (typeof source.src !== 'undefined') {
-      if (!source.complete) {
-        return '';
-      }
-      if (source.naturalWidth === 0 || source.naturalHeight === 0) {
-        return '';
-      }
-    }
-    return ctx.createPattern(source, this.repeat);
-  }
-});
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { });
-
-  if (fabric.Shadow) {
-    fabric.warn('fabric.Shadow is already defined.');
-    return;
-  }
-
-  /**
-   * Shadow class
-   * @class fabric.Shadow
-   * @see {@link http://fabricjs.com/shadows/|Shadow demo}
-   * @see {@link fabric.Shadow#initialize} for constructor definition
-   */
-  fabric.Shadow = fabric.util.createClass(/** @lends fabric.Shadow.prototype */ {
-
-    /**
-     * Shadow color
-     * @type String
-     * @default
-     */
-    color: 'rgb(0,0,0)',
-
-    /**
-     * Shadow blur
-     * @type Number
-     */
-    blur: 0,
-
-    /**
-     * Shadow horizontal offset
-     * @type Number
-     * @default
-     */
-    offsetX: 0,
-
-    /**
-     * Shadow vertical offset
-     * @type Number
-     * @default
-     */
-    offsetY: 0,
-
-    /**
-     * Whether the shadow should affect stroke operations
-     * @type Boolean
-     * @default
-     */
-    affectStroke: false,
-
-    /**
-     * Indicates whether toObject should include default values
-     * @type Boolean
-     * @default
-     */
-    includeDefaultValues: true,
-
-    /**
-     * Constructor
-     * @param {Object|String} [options] Options object with any of color, blur, offsetX, offsetX properties or string (e.g. "rgba(0,0,0,0.2) 2px 2px 10px, "2px 2px 10px rgba(0,0,0,0.2)")
-     * @return {fabric.Shadow} thisArg
-     */
-    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++;
-    },
-
-    /**
-     * @private
-     * @param {String} shadow Shadow value to parse
-     * @return {Object} Shadow object with color, offsetX, offsetY and blur
-     */
-    _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
-      };
-    },
-
-    /**
-     * Returns a string representation of an instance
-     * @see http://www.w3.org/TR/css-text-decor-3/#text-shadow
-     * @return {String} Returns CSS3 text-shadow declaration
-     */
-    toString: function() {
-      return [this.offsetX, this.offsetY, this.blur, this.color].join('px ');
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of a shadow
-     * @param {fabric.Object} object
-     * @return {String} SVG representation of a shadow
-     */
-    toSVG: function(object) {
-      var mode = 'SourceAlpha';
-
-      if (object && (object.fill === this.color || object.stroke === this.color)) {
-        mode = 'SourceGraphic';
-      }
-
-      return (
-        '<filter id="SVGID_' + this.id + '" y="-40%" height="180%">' +
-          '<feGaussianBlur in="' + mode + '" stdDeviation="' +
-            (this.blur ? this.blur / 3 : 0) +
-          '"></feGaussianBlur>' +
-          '<feOffset dx="' + this.offsetX + '" dy="' + this.offsetY + '"></feOffset>' +
-          '<feMerge>' +
-            '<feMergeNode></feMergeNode>' +
-            '<feMergeNode in="SourceGraphic"></feMergeNode>' +
-          '</feMerge>' +
-        '</filter>');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns object representation of a shadow
-     * @return {Object} Object representation of a shadow instance
-     */
-    toObject: function() {
-      if (this.includeDefaultValues) {
-        return {
-          color: this.color,
-          blur: this.blur,
-          offsetX: this.offsetX,
-          offsetY: this.offsetY
-        };
-      }
-      var obj = { }, proto = fabric.Shadow.prototype;
-      if (this.color !== proto.color) {
-        obj.color = this.color;
-      }
-      if (this.blur !== proto.blur) {
-        obj.blur = this.blur;
-      }
-      if (this.offsetX !== proto.offsetX) {
-        obj.offsetX = this.offsetX;
-      }
-      if (this.offsetY !== proto.offsetY) {
-        obj.offsetY = this.offsetY;
-      }
-      return obj;
-    }
-  });
-
-  /**
-   * Regex matching shadow offsetX, offsetY and blur (ex: "2px 2px 10px rgba(0,0,0,0.2)", "rgb(0,255,0) 2px 2px")
-   * @static
-   * @field
-   * @memberOf fabric.Shadow
-   */
-  fabric.Shadow.reOffsetsAndBlur = /(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function () {
-
-  'use strict';
-
-  if (fabric.StaticCanvas) {
-    fabric.warn('fabric.StaticCanvas is already defined.');
-    return;
-  }
-
-  // aliases for faster resolution
-  var extend = fabric.util.object.extend,
-      getElementOffset = fabric.util.getElementOffset,
-      removeFromArray = fabric.util.removeFromArray,
-
-      CANVAS_INIT_ERROR = new Error('Could not initialize `canvas` element');
-
-  /**
-   * Static canvas class
-   * @class fabric.StaticCanvas
-   * @mixes fabric.Collection
-   * @mixes fabric.Observable
-   * @see {@link http://fabricjs.com/static_canvas/|StaticCanvas demo}
-   * @see {@link fabric.StaticCanvas#initialize} for constructor definition
-   * @fires before:render
-   * @fires after:render
-   * @fires canvas:cleared
-   * @fires object:added
-   * @fires object:removed
-   */
-  fabric.StaticCanvas = fabric.util.createClass(/** @lends fabric.StaticCanvas.prototype */ {
-
-    /**
-     * Constructor
-     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
-     * @param {Object} [options] Options object
-     * @return {Object} thisArg
-     */
-    initialize: function(el, options) {
-      options || (options = { });
-
-      this._initStatic(el, options);
-      fabric.StaticCanvas.activeInstance = this;
-    },
-
-    /**
-     * Background color of canvas instance.
-     * Should be set via {@link fabric.StaticCanvas#setBackgroundColor}.
-     * @type {(String|fabric.Pattern)}
-     * @default
-     */
-    backgroundColor: '',
-
-    /**
-     * Background image of canvas instance.
-     * Should be set via {@link fabric.StaticCanvas#setBackgroundImage}.
-     * <b>Backwards incompatibility note:</b> The "backgroundImageOpacity"
-     * and "backgroundImageStretch" properties are deprecated since 1.3.9.
-     * Use {@link fabric.Image#opacity}, {@link fabric.Image#width} and {@link fabric.Image#height}.
-     * @type fabric.Image
-     * @default
-     */
-    backgroundImage: null,
-
-    /**
-     * Overlay color of canvas instance.
-     * Should be set via {@link fabric.StaticCanvas#setOverlayColor}
-     * @since 1.3.9
-     * @type {(String|fabric.Pattern)}
-     * @default
-     */
-    overlayColor: '',
-
-    /**
-     * Overlay image of canvas instance.
-     * Should be set via {@link fabric.StaticCanvas#setOverlayImage}.
-     * <b>Backwards incompatibility note:</b> The "overlayImageLeft"
-     * and "overlayImageTop" properties are deprecated since 1.3.9.
-     * Use {@link fabric.Image#left} and {@link fabric.Image#top}.
-     * @type fabric.Image
-     * @default
-     */
-    overlayImage: null,
-
-    /**
-     * Indicates whether toObject/toDatalessObject should include default values
-     * @type Boolean
-     * @default
-     */
-    includeDefaultValues: true,
-
-    /**
-     * Indicates whether objects' state should be saved
-     * @type Boolean
-     * @default
-     */
-    stateful: true,
-
-    /**
-     * Indicates whether {@link fabric.Collection.add}, {@link fabric.Collection.insertAt} and {@link fabric.Collection.remove} should also re-render canvas.
-     * Disabling this option could give a great performance boost when adding/removing a lot of objects to/from canvas at once
-     * (followed by a manual rendering after addition/deletion)
-     * @type Boolean
-     * @default
-     */
-    renderOnAddRemove: true,
-
-    /**
-     * Function that determines clipping of entire canvas area
-     * Being passed context as first argument. See clipping canvas area in {@link https://github.com/kangax/fabric.js/wiki/FAQ}
-     * @type Function
-     * @default
-     */
-    clipTo: null,
-
-    /**
-     * Indicates whether object controls (borders/controls) are rendered above overlay image
-     * @type Boolean
-     * @default
-     */
-    controlsAboveOverlay: false,
-
-    /**
-     * Indicates whether the browser can be scrolled when using a touchscreen and dragging on the canvas
-     * @type Boolean
-     * @default
-     */
-    allowTouchScrolling: false,
-
-    /**
-     * Indicates whether this canvas will use image smoothing, this is on by default in browsers
-     * @type Boolean
-     * @default
-     */
-    imageSmoothingEnabled: true,
-
-    /**
-     * The transformation (in the format of Canvas transform) which focuses the viewport
-     * @type Array
-     * @default
-     */
-    viewportTransform: [1, 0, 0, 1, 0, 0],
-
-    /**
-     * Callback; invoked right before object is about to be scaled/rotated
-     */
-    onBeforeScaleRotate: function () {
-      /* NOOP */
-    },
-
-    /**
-     * @private
-     * @param {HTMLElement | String} el &lt;canvas> element to initialize instance on
-     * @param {Object} [options] Options object
-     */
-    _initStatic: function(el, options) {
-      this._objects = [];
-
-      this._createLowerCanvas(el);
-      this._initOptions(options);
-      this._setImageSmoothing();
-
-      if (options.overlayImage) {
-        this.setOverlayImage(options.overlayImage, this.renderAll.bind(this));
-      }
-      if (options.backgroundImage) {
-        this.setBackgroundImage(options.backgroundImage, this.renderAll.bind(this));
-      }
-      if (options.backgroundColor) {
-        this.setBackgroundColor(options.backgroundColor, this.renderAll.bind(this));
-      }
-      if (options.overlayColor) {
-        this.setOverlayColor(options.overlayColor, this.renderAll.bind(this));
-      }
-      this.calcOffset();
-    },
-
-    /**
-     * Calculates canvas element offset relative to the document
-     * This method is also attached as "resize" event handler of window
-     * @return {fabric.Canvas} instance
-     * @chainable
-     */
-    calcOffset: function () {
-      this._offset = getElementOffset(this.lowerCanvasEl);
-      return this;
-    },
-
-    /**
-     * Sets {@link fabric.StaticCanvas#overlayImage|overlay image} for this canvas
-     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set overlay to
-     * @param {Function} callback callback to invoke when image is loaded and set as an overlay
-     * @param {Object} [options] Optional options to set for the {@link fabric.Image|overlay image}.
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/MnzHT/|jsFiddle demo}
-     * @example <caption>Normal overlayImage with left/top = 0</caption>
-     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
-     *   // Needed to position overlayImage at 0/0
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     * @example <caption>overlayImage with different properties</caption>
-     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
-     *   opacity: 0.5,
-     *   angle: 45,
-     *   left: 400,
-     *   top: 400,
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     * @example <caption>Stretched overlayImage #1 - width/height correspond to canvas width/height</caption>
-     * fabric.Image.fromURL('http://fabricjs.com/assets/jail_cell_bars.png', function(img) {
-     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
-     *    canvas.setOverlayImage(img, canvas.renderAll.bind(canvas));
-     * });
-     * @example <caption>Stretched overlayImage #2 - width/height correspond to canvas width/height</caption>
-     * canvas.setOverlayImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
-     *   width: canvas.width,
-     *   height: canvas.height,
-     *   // Needed to position overlayImage at 0/0
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     */
-    setOverlayImage: function (image, callback, options) {
-      return this.__setBgOverlayImage('overlayImage', image, callback, options);
-    },
-
-    /**
-     * Sets {@link fabric.StaticCanvas#backgroundImage|background image} for this canvas
-     * @param {(fabric.Image|String)} image fabric.Image instance or URL of an image to set background to
-     * @param {Function} callback Callback to invoke when image is loaded and set as background
-     * @param {Object} [options] Optional options to set for the {@link fabric.Image|background image}.
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/YH9yD/|jsFiddle demo}
-     * @example <caption>Normal backgroundImage with left/top = 0</caption>
-     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
-     *   // Needed to position backgroundImage at 0/0
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     * @example <caption>backgroundImage with different properties</caption>
-     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
-     *   opacity: 0.5,
-     *   angle: 45,
-     *   left: 400,
-     *   top: 400,
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     * @example <caption>Stretched backgroundImage #1 - width/height correspond to canvas width/height</caption>
-     * fabric.Image.fromURL('http://fabricjs.com/assets/honey_im_subtle.png', function(img) {
-     *    img.set({width: canvas.width, height: canvas.height, originX: 'left', originY: 'top'});
-     *    canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
-     * });
-     * @example <caption>Stretched backgroundImage #2 - width/height correspond to canvas width/height</caption>
-     * canvas.setBackgroundImage('http://fabricjs.com/assets/honey_im_subtle.png', canvas.renderAll.bind(canvas), {
-     *   width: canvas.width,
-     *   height: canvas.height,
-     *   // Needed to position backgroundImage at 0/0
-     *   originX: 'left',
-     *   originY: 'top'
-     * });
-     */
-    setBackgroundImage: function (image, callback, options) {
-      return this.__setBgOverlayImage('backgroundImage', image, callback, options);
-    },
-
-    /**
-     * Sets {@link fabric.StaticCanvas#overlayColor|background color} for this canvas
-     * @param {(String|fabric.Pattern)} overlayColor Color or pattern to set background color to
-     * @param {Function} callback Callback to invoke when background color is set
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/pB55h/|jsFiddle demo}
-     * @example <caption>Normal overlayColor - color value</caption>
-     * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
-     * @example <caption>fabric.Pattern used as overlayColor</caption>
-     * canvas.setOverlayColor({
-     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
-     * }, canvas.renderAll.bind(canvas));
-     * @example <caption>fabric.Pattern used as overlayColor with repeat and offset</caption>
-     * canvas.setOverlayColor({
-     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
-     *   repeat: 'repeat',
-     *   offsetX: 200,
-     *   offsetY: 100
-     * }, canvas.renderAll.bind(canvas));
-     */
-    setOverlayColor: function(overlayColor, callback) {
-      return this.__setBgOverlayColor('overlayColor', overlayColor, callback);
-    },
-
-    /**
-     * Sets {@link fabric.StaticCanvas#backgroundColor|background color} for this canvas
-     * @param {(String|fabric.Pattern)} backgroundColor Color or pattern to set background color to
-     * @param {Function} callback Callback to invoke when background color is set
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/hXzvk/|jsFiddle demo}
-     * @example <caption>Normal backgroundColor - color value</caption>
-     * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
-     * @example <caption>fabric.Pattern used as backgroundColor</caption>
-     * canvas.setBackgroundColor({
-     *   source: 'http://fabricjs.com/assets/escheresque_ste.png'
-     * }, canvas.renderAll.bind(canvas));
-     * @example <caption>fabric.Pattern used as backgroundColor with repeat and offset</caption>
-     * canvas.setBackgroundColor({
-     *   source: 'http://fabricjs.com/assets/escheresque_ste.png',
-     *   repeat: 'repeat',
-     *   offsetX: 200,
-     *   offsetY: 100
-     * }, canvas.renderAll.bind(canvas));
-     */
-    setBackgroundColor: function(backgroundColor, callback) {
-      return this.__setBgOverlayColor('backgroundColor', backgroundColor, callback);
-    },
-
-    /**
-     * @private
-     * @see {@link http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-imagesmoothingenabled|WhatWG Canvas Standard}
-     */
-    _setImageSmoothing: function(){
-      var ctx = this.getContext();
-
-      ctx.imageSmoothingEnabled       = this.imageSmoothingEnabled;
-      ctx.webkitImageSmoothingEnabled = this.imageSmoothingEnabled;
-      ctx.mozImageSmoothingEnabled    = this.imageSmoothingEnabled;
-      ctx.msImageSmoothingEnabled     = this.imageSmoothingEnabled;
-      ctx.oImageSmoothingEnabled      = this.imageSmoothingEnabled;
-    },
-
-    /**
-     * @private
-     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundImage|backgroundImage}
-     * or {@link fabric.StaticCanvas#overlayImage|overlayImage})
-     * @param {(fabric.Image|String|null)} image fabric.Image instance, URL of an image or null to set background or overlay to
-     * @param {Function} callback Callback to invoke when image is loaded and set as background or overlay
-     * @param {Object} [options] Optional options to set for the {@link fabric.Image|image}.
-     */
-    __setBgOverlayImage: function(property, image, callback, options) {
-      if (typeof image === 'string') {
-        fabric.util.loadImage(image, function(img) {
-          this[property] = new fabric.Image(img, options);
-          callback && callback();
-        }, this);
-      }
-      else {
-        this[property] = image;
-        callback && callback();
-      }
-
-      return this;
-    },
-
-    /**
-     * @private
-     * @param {String} property Property to set ({@link fabric.StaticCanvas#backgroundColor|backgroundColor}
-     * or {@link fabric.StaticCanvas#overlayColor|overlayColor})
-     * @param {(Object|String|null)} color Object with pattern information, color value or null
-     * @param {Function} [callback] Callback is invoked when color is set
-     */
-    __setBgOverlayColor: function(property, color, callback) {
-      if (color && color.source) {
-        var _this = this;
-        fabric.util.loadImage(color.source, function(img) {
-          _this[property] = new fabric.Pattern({
-            source: img,
-            repeat: color.repeat,
-            offsetX: color.offsetX,
-            offsetY: color.offsetY
-          });
-          callback && callback();
-        });
-      }
-      else {
-        this[property] = color;
-        callback && callback();
-      }
-
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _createCanvasElement: function() {
-      var element = fabric.document.createElement('canvas');
-      if (!element.style) {
-        element.style = { };
-      }
-      if (!element) {
-        throw CANVAS_INIT_ERROR;
-      }
-      this._initCanvasElement(element);
-      return element;
-    },
-
-    /**
-     * @private
-     * @param {HTMLElement} element
-     */
-    _initCanvasElement: function(element) {
-      fabric.util.createCanvasElement(element);
-
-      if (typeof element.getContext === 'undefined') {
-        throw CANVAS_INIT_ERROR;
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options object
-     */
-    _initOptions: function (options) {
-      for (var prop in options) {
-        this[prop] = options[prop];
-      }
-
-      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();
-    },
-
-    /**
-     * Creates a bottom canvas
-     * @private
-     * @param {HTMLElement} [canvasEl]
-     */
-    _createLowerCanvas: function (canvasEl) {
-      this.lowerCanvasEl = fabric.util.getById(canvasEl) || this._createCanvasElement();
-      this._initCanvasElement(this.lowerCanvasEl);
-
-      fabric.util.addClass(this.lowerCanvasEl, 'lower-canvas');
-
-      if (this.interactive) {
-        this._applyCanvasStyle(this.lowerCanvasEl);
-      }
-
-      this.contextContainer = this.lowerCanvasEl.getContext('2d');
-    },
-
-    /**
-     * Returns canvas width (in px)
-     * @return {Number}
-     */
-    getWidth: function () {
-      return this.width;
-    },
-
-    /**
-     * Returns canvas height (in px)
-     * @return {Number}
-     */
-    getHeight: function () {
-      return this.height;
-    },
-
-    /**
-     * Sets width of this canvas instance
-     * @param {Number|String} value                         Value to set width to
-     * @param {Object}        [options]                     Options object
-     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
-     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    setWidth: function (value, options) {
-      return this.setDimensions({ width: value }, options);
-    },
-
-    /**
-     * Sets height of this canvas instance
-     * @param {Number|String} value                         Value to set height to
-     * @param {Object}        [options]                     Options object
-     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
-     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    setHeight: function (value, options) {
-      return this.setDimensions({ height: value }, options);
-    },
-
-    /**
-     * Sets dimensions (width, height) of this canvas instance. when options.cssOnly flag active you should also supply the unit of measure (px/%/em)
-     * @param {Object}        dimensions                    Object with width/height properties
-     * @param {Number|String} [dimensions.width]            Width of canvas element
-     * @param {Number|String} [dimensions.height]           Height of canvas element
-     * @param {Object}        [options]                     Options object
-     * @param {Boolean}       [options.backstoreOnly=false] Set the given dimensions only as canvas backstore dimensions
-     * @param {Boolean}       [options.cssOnly=false]       Set the given dimensions only as css dimensions
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    setDimensions: function (dimensions, options) {
-      var cssValue;
-
-      options = options || {};
-
-      for (var prop in dimensions) {
-        cssValue = dimensions[prop];
-
-        if (!options.cssOnly) {
-          this._setBackstoreDimension(prop, dimensions[prop]);
-          cssValue += 'px';
-        }
-
-        if (!options.backstoreOnly) {
-          this._setCssDimension(prop, cssValue);
-        }
-      }
-
-      if (!options.cssOnly) {
-        this.renderAll();
-      }
-
-      this.calcOffset();
-
-      return this;
-    },
-
-    /**
-     * Helper for setting width/height
-     * @private
-     * @param {String} prop property (width|height)
-     * @param {Number} value value to set property to
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    _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;
-    },
-
-    /**
-     * Helper for setting css width/height
-     * @private
-     * @param {String} prop property (width|height)
-     * @param {String} value value to set property to
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    _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;
-    },
-
-    /**
-     * Returns canvas zoom level
-     * @return {Number}
-     */
-    getZoom: function () {
-      return Math.sqrt(this.viewportTransform[0] * this.viewportTransform[3]);
-    },
-
-    /**
-     * Sets viewport transform of this canvas instance
-     * @param {Array} vpt the transform in the form of context.transform
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    setViewportTransform: function (vpt) {
-      this.viewportTransform = vpt;
-      this.renderAll();
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        this._objects[i].setCoords();
-      }
-      return this;
-    },
-
-    /**
-     * Sets zoom level of this canvas instance, zoom centered around point
-     * @param {fabric.Point} point to zoom with respect to
-     * @param {Number} value to set zoom to, less than 1 zooms out
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    zoomToPoint: function (point, value) {
-      // TODO: just change the scale, preserve other transformations
-      var before = point;
-      point = fabric.util.transformPoint(point, fabric.util.invertTransform(this.viewportTransform));
-      this.viewportTransform[0] = value;
-      this.viewportTransform[3] = value;
-      var after = fabric.util.transformPoint(point, this.viewportTransform);
-      this.viewportTransform[4] += before.x - after.x;
-      this.viewportTransform[5] += before.y - after.y;
-      this.renderAll();
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        this._objects[i].setCoords();
-      }
-      return this;
-    },
-
-    /**
-     * Sets zoom level of this canvas instance
-     * @param {Number} value to set zoom to, less than 1 zooms out
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    setZoom: function (value) {
-      this.zoomToPoint(new fabric.Point(0, 0), value);
-      return this;
-    },
-
-    /**
-     * Pan viewport so as to place point at top left corner of canvas
-     * @param {fabric.Point} point to move to
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    absolutePan: function (point) {
-      this.viewportTransform[4] = -point.x;
-      this.viewportTransform[5] = -point.y;
-      this.renderAll();
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        this._objects[i].setCoords();
-      }
-      return this;
-    },
-
-    /**
-     * Pans viewpoint relatively
-     * @param {fabric.Point} point (position vector) to move by
-     * @return {fabric.Canvas} instance
-     * @chainable true
-     */
-    relativePan: function (point) {
-      return this.absolutePan(new fabric.Point(
-        -point.x - this.viewportTransform[4],
-        -point.y - this.viewportTransform[5]
-      ));
-    },
-
-    /**
-     * Returns &lt;canvas> element corresponding to this instance
-     * @return {HTMLCanvasElement}
-     */
-    getElement: function () {
-      return this.lowerCanvasEl;
-    },
-
-    /**
-     * Returns currently selected object, if any
-     * @return {fabric.Object}
-     */
-    getActiveObject: function() {
-      return null;
-    },
-
-    /**
-     * Returns currently selected group of object, if any
-     * @return {fabric.Group}
-     */
-    getActiveGroup: function() {
-      return null;
-    },
-
-    /**
-     * Given a context, renders an object on that context
-     * @param {CanvasRenderingContext2D} ctx Context to render object on
-     * @param {fabric.Object} object Object to render
-     * @private
-     */
-    _draw: function (ctx, object) {
-      if (!object) {
-        return;
-      }
-
-      ctx.save();
-      var v = this.viewportTransform;
-      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-      object.render(ctx);
-      ctx.restore();
-      if (!this.controlsAboveOverlay) {
-        object._renderControls(ctx);
-      }
-    },
-
-    /**
-     * @private
-     * @param {fabric.Object} obj Object that was added
-     */
-    _onObjectAdded: function(obj) {
-      this.stateful && obj.setupState();
-      obj.canvas = this;
-      obj.setCoords();
-      this.fire('object:added', { target: obj });
-      obj.fire('added');
-    },
-
-    /**
-     * @private
-     * @param {fabric.Object} obj Object that was removed
-     */
-    _onObjectRemoved: function(obj) {
-      // removing active object should fire "selection:cleared" events
-      if (this.getActiveObject() === obj) {
-        this.fire('before:selection:cleared', { target: obj });
-        this._discardActiveObject();
-        this.fire('selection:cleared');
-      }
-
-      this.fire('object:removed', { target: obj });
-      obj.fire('removed');
-    },
-
-    /**
-     * Clears specified context of canvas element
-     * @param {CanvasRenderingContext2D} ctx Context to clear
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    clearContext: function(ctx) {
-      ctx.clearRect(0, 0, this.width, this.height);
-      return this;
-    },
-
-    /**
-     * Returns context of canvas where objects are drawn
-     * @return {CanvasRenderingContext2D}
-     */
-    getContext: function () {
-      return this.contextContainer;
-    },
-
-    /**
-     * Clears all contexts (background, main, top) of an instance
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    clear: function () {
-      this._objects.length = 0;
-      if (this.discardActiveGroup) {
-        this.discardActiveGroup();
-      }
-      if (this.discardActiveObject) {
-        this.discardActiveObject();
-      }
-      this.clearContext(this.contextContainer);
-      if (this.contextTop) {
-        this.clearContext(this.contextTop);
-      }
-      this.fire('canvas:cleared');
-      this.renderAll();
-      return this;
-    },
-
-    /**
-     * Renders both the top canvas and the secondary container canvas.
-     * @param {Boolean} [allOnTop] Whether we want to force all images to be rendered on the top canvas
-     * @return {fabric.Canvas} instance
-     * @chainable
-     */
-    renderAll: function (allOnTop) {
-      var canvasToDrawOn = this[(allOnTop === true && this.interactive) ? 'contextTop' : 'contextContainer'],
-          activeGroup = this.getActiveGroup();
-
-      if (this.contextTop && this.selection && !this._groupSelector) {
-        this.clearContext(this.contextTop);
-      }
-
-      if (!allOnTop) {
-        this.clearContext(canvasToDrawOn);
-      }
-
-      this.fire('before:render');
-
-      if (this.clipTo) {
-        fabric.util.clipContext(this, canvasToDrawOn);
-      }
-
-      this._renderBackground(canvasToDrawOn);
-      this._renderObjects(canvasToDrawOn, activeGroup);
-      this._renderActiveGroup(canvasToDrawOn, activeGroup);
-
-      if (this.clipTo) {
-        canvasToDrawOn.restore();
-      }
-
-      this._renderOverlay(canvasToDrawOn);
-
-      if (this.controlsAboveOverlay && this.interactive) {
-        this.drawControls(canvasToDrawOn);
-      }
-
-      this.fire('after:render');
-
-      return this;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {fabric.Group} activeGroup
-     */
-    _renderObjects: function(ctx, activeGroup) {
-      var i, length;
-
-      // fast path
-      if (!activeGroup) {
-        for (i = 0, length = this._objects.length; i < length; ++i) {
-          this._draw(ctx, this._objects[i]);
-        }
-      }
-      else {
-        for (i = 0, length = this._objects.length; i < length; ++i) {
-          if (this._objects[i] && !activeGroup.contains(this._objects[i])) {
-            this._draw(ctx, this._objects[i]);
-          }
-        }
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {fabric.Group} activeGroup
-     */
-    _renderActiveGroup: function(ctx, activeGroup) {
-
-      // delegate rendering to group selection (if one exists)
-      if (activeGroup) {
-
-        //Store objects in group preserving order, then replace
-        var sortedObjects = [];
-        this.forEachObject(function (object) {
-          if (activeGroup.contains(object)) {
-            sortedObjects.push(object);
-          }
-        });
-        activeGroup._set('objects', sortedObjects);
-        this._draw(ctx, activeGroup);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderBackground: function(ctx) {
-      if (this.backgroundColor) {
-        ctx.fillStyle = this.backgroundColor.toLive
-          ? this.backgroundColor.toLive(ctx)
-          : this.backgroundColor;
-
-        ctx.fillRect(
-          this.backgroundColor.offsetX || 0,
-          this.backgroundColor.offsetY || 0,
-          this.width,
-          this.height);
-      }
-      if (this.backgroundImage) {
-        this._draw(ctx, this.backgroundImage);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderOverlay: function(ctx) {
-      if (this.overlayColor) {
-        ctx.fillStyle = this.overlayColor.toLive
-          ? this.overlayColor.toLive(ctx)
-          : this.overlayColor;
-
-        ctx.fillRect(
-          this.overlayColor.offsetX || 0,
-          this.overlayColor.offsetY || 0,
-          this.width,
-          this.height);
-      }
-      if (this.overlayImage) {
-        this._draw(ctx, this.overlayImage);
-      }
-    },
-
-    /**
-     * Method to render only the top canvas.
-     * Also used to render the group selection box.
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    renderTop: function () {
-      var ctx = this.contextTop || this.contextContainer;
-      this.clearContext(ctx);
-
-      // we render the top context - last object
-      if (this.selection && this._groupSelector) {
-        this._drawSelection();
-      }
-
-      // delegate rendering to group selection if one exists
-      // used for drawing selection borders/controls
-      var activeGroup = this.getActiveGroup();
-      if (activeGroup) {
-        activeGroup.render(ctx);
-      }
-
-      this._renderOverlay(ctx);
-
-      this.fire('after:render');
-
-      return this;
-    },
-
-    /**
-     * Returns coordinates of a center of canvas.
-     * Returned value is an object with top and left properties
-     * @return {Object} object with "top" and "left" number values
-     */
-    getCenter: function () {
-      return {
-        top: this.getHeight() / 2,
-        left: this.getWidth() / 2
-      };
-    },
-
-    /**
-     * Centers object horizontally.
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @param {fabric.Object} object Object to center horizontally
-     * @return {fabric.Canvas} thisArg
-     */
-    centerObjectH: function (object) {
-      this._centerObject(object, new fabric.Point(this.getCenter().left, object.getCenterPoint().y));
-      this.renderAll();
-      return this;
-    },
-
-    /**
-     * Centers object vertically.
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @param {fabric.Object} object Object to center vertically
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    centerObjectV: function (object) {
-      this._centerObject(object, new fabric.Point(object.getCenterPoint().x, this.getCenter().top));
-      this.renderAll();
-      return this;
-    },
-
-    /**
-     * Centers object vertically and horizontally.
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @param {fabric.Object} object Object to center vertically and horizontally
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    centerObject: function(object) {
-      var center = this.getCenter();
-
-      this._centerObject(object, new fabric.Point(center.left, center.top));
-      this.renderAll();
-      return this;
-    },
-
-    /**
-     * @private
-     * @param {fabric.Object} object Object to center
-     * @param {fabric.Point} center Center point
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    _centerObject: function(object, center) {
-      object.setPositionByOrigin(center, 'center', 'center');
-      return this;
-    },
-
-    /**
-     * Returs dataless JSON representation of canvas
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {String} json string
-     */
-    toDatalessJSON: function (propertiesToInclude) {
-      return this.toDatalessObject(propertiesToInclude);
-    },
-
-    /**
-     * Returns object representation of canvas
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function (propertiesToInclude) {
-      return this._toObjectMethod('toObject', propertiesToInclude);
-    },
-
-    /**
-     * Returns dataless object representation of canvas
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toDatalessObject: function (propertiesToInclude) {
-      return this._toObjectMethod('toDatalessObject', propertiesToInclude);
-    },
-
-    /**
-     * @private
-     */
-    _toObjectMethod: function (methodName, propertiesToInclude) {
-
-      var activeGroup = this.getActiveGroup();
-      if (activeGroup) {
-        this.discardActiveGroup();
-      }
-
-      var data = {
-        objects: this._toObjects(methodName, propertiesToInclude)
-      };
-
-      extend(data, this.__serializeBgOverlay());
-
-      fabric.util.populateWithProperties(this, data, propertiesToInclude);
-
-      if (activeGroup) {
-        this.setActiveGroup(new fabric.Group(activeGroup.getObjects(), {
-          originX: 'center',
-          originY: 'center'
-        }));
-        activeGroup.forEachObject(function(o) {
-          o.set('active', true);
-        });
-
-        if (this._currentTransform) {
-          this._currentTransform.target = this.getActiveGroup();
-        }
-      }
-
-      return data;
-    },
-
-    /**
-     * @private
-     */
-    _toObjects: function(methodName, propertiesToInclude) {
-      return this.getObjects().map(function(instance) {
-        return this._toObject(instance, methodName, propertiesToInclude);
-      }, this);
-    },
-
-    /**
-     * @private
-     */
-    _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;
-    },
-
-    /**
-     * @private
-     */
-    __serializeBgOverlay: function() {
-      var data = {
-        background: (this.backgroundColor && this.backgroundColor.toObject)
-          ? this.backgroundColor.toObject()
-          : this.backgroundColor
-      };
-
-      if (this.overlayColor) {
-        data.overlay = this.overlayColor.toObject
-          ? this.overlayColor.toObject()
-          : this.overlayColor;
-      }
-      if (this.backgroundImage) {
-        data.backgroundImage = this.backgroundImage.toObject();
-      }
-      if (this.overlayImage) {
-        data.overlayImage = this.overlayImage.toObject();
-      }
-
-      return data;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * When true, getSvgTransform() will apply the StaticCanvas.viewportTransform to the SVG transformation. When true,
-     * a zoomed canvas will then produce zoomed SVG output.
-     * @type Boolean
-     * @default
-     */
-    svgViewportTransformation: true,
-
-    /**
-     * Returns SVG representation of canvas
-     * @function
-     * @param {Object} [options] Options object for SVG output
-     * @param {Boolean} [options.suppressPreamble=false] If true xml tag is not included
-     * @param {Object} [options.viewBox] SVG viewbox object
-     * @param {Number} [options.viewBox.x] x-cooridnate of viewbox
-     * @param {Number} [options.viewBox.y] y-coordinate of viewbox
-     * @param {Number} [options.viewBox.width] Width of viewbox
-     * @param {Number} [options.viewBox.height] Height of viewbox
-     * @param {String} [options.encoding=UTF-8] Encoding of SVG output
-     * @param {Function} [reviver] Method for further parsing of svg elements, called after each fabric object converted into svg representation.
-     * @return {String} SVG string
-     * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
-     * @see {@link http://jsfiddle.net/fabricjs/jQ3ZZ/|jsFiddle demo}
-     * @example <caption>Normal SVG output</caption>
-     * var svg = canvas.toSVG();
-     * @example <caption>SVG output without preamble (without &lt;?xml ../>)</caption>
-     * var svg = canvas.toSVG({suppressPreamble: true});
-     * @example <caption>SVG output with viewBox attribute</caption>
-     * var svg = canvas.toSVG({
-     *   viewBox: {
-     *     x: 100,
-     *     y: 100,
-     *     width: 200,
-     *     height: 300
-     *   }
-     * });
-     * @example <caption>SVG output with different encoding (default: UTF-8)</caption>
-     * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
-     * @example <caption>Modify SVG output with reviver function</caption>
-     * var svg = canvas.toSVG(null, function(svg) {
-     *   return svg.replace('stroke-dasharray: ; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; ', '');
-     * });
-     */
-    toSVG: function(options, reviver) {
-      options || (options = { });
-
-      var markup = [];
-
-      this._setSVGPreamble(markup, options);
-      this._setSVGHeader(markup, options);
-
-      this._setSVGBgOverlayColor(markup, 'backgroundColor');
-      this._setSVGBgOverlayImage(markup, 'backgroundImage');
-
-      this._setSVGObjects(markup, reviver);
-
-      this._setSVGBgOverlayColor(markup, 'overlayColor');
-      this._setSVGBgOverlayImage(markup, 'overlayImage');
-
-      markup.push('</svg>');
-
-      return markup.join('');
-    },
-
-    /**
-     * @private
-     */
-    _setSVGPreamble: function(markup, options) {
-      if (!options.suppressPreamble) {
-        markup.push(
-          '<?xml version="1.0" encoding="', (options.encoding || 'UTF-8'), '" standalone="no" ?>',
-            '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ',
-              '"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n'
-        );
-      }
-    },
-
-    /**
-     * @private
-     */
-    _setSVGHeader: function(markup, options) {
-      var width, height, vpt;
-
-      if (options.viewBox) {
-        width = options.viewBox.width;
-        height = options.viewBox.height;
-      }
-      else {
-        width = this.width;
-        height = this.height;
-        if (!this.svgViewportTransformation) {
-          vpt = this.viewportTransform;
-          width /= vpt[0];
-          height /= vpt[3];
-        }
-      }
-
-      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, '" ',
-          (this.backgroundColor && !this.backgroundColor.toLive
-            ? 'style="background-color: ' + this.backgroundColor + '" '
-            : null),
-          (options.viewBox
-              ? 'viewBox="' +
-                options.viewBox.x + ' ' +
-                options.viewBox.y + ' ' +
-                options.viewBox.width + ' ' +
-                options.viewBox.height + '" '
-              : null),
-          'xml:space="preserve">',
-        '<desc>Created with Fabric.js ', fabric.version, '</desc>',
-        '<defs>',
-          fabric.createSVGFontFacesMarkup(this.getObjects()),
-          fabric.createSVGRefElementsMarkup(this),
-        '</defs>'
-      );
-    },
-
-    /**
-     * @private
-     */
-    _setSVGObjects: function(markup, reviver) {
-      var activeGroup = this.getActiveGroup();
-      if (activeGroup) {
-        this.discardActiveGroup();
-      }
-      for (var i = 0, objects = this.getObjects(), len = objects.length; i < len; i++) {
-        markup.push(objects[i].toSVG(reviver));
-      }
-      if (activeGroup) {
-        this.setActiveGroup(new fabric.Group(activeGroup.getObjects()));
-        activeGroup.forEachObject(function(o) {
-          o.set('active', true);
-        });
-      }
-    },
-
-    /**
-     * @private
-     */
-    _setSVGBgOverlayImage: function(markup, property) {
-      if (this[property] && this[property].toSVG) {
-        markup.push(this[property].toSVG());
-      }
-    },
-
-    /**
-     * @private
-     */
-    _setSVGBgOverlayColor: function(markup, property) {
-      if (this[property] && this[property].source) {
-        markup.push(
-          '<rect x="', this[property].offsetX, '" y="', this[property].offsetY, '" ',
-            'width="',
-              (this[property].repeat === 'repeat-y' || this[property].repeat === 'no-repeat'
-                ? this[property].source.width
-                : this.width),
-            '" height="',
-              (this[property].repeat === 'repeat-x' || this[property].repeat === 'no-repeat'
-                ? this[property].source.height
-                : this.height),
-            '" fill="url(#' + property + 'Pattern)"',
-          '></rect>'
-        );
-      }
-      else if (this[property] && property === 'overlayColor') {
-        markup.push(
-          '<rect x="0" y="0" ',
-            'width="', this.width,
-            '" height="', this.height,
-            '" fill="', this[property], '"',
-          '></rect>'
-        );
-      }
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Moves an object to the bottom of the stack of drawn objects
-     * @param {fabric.Object} object Object to send to back
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    sendToBack: function (object) {
-      removeFromArray(this._objects, object);
-      this._objects.unshift(object);
-      return this.renderAll && this.renderAll();
-    },
-
-    /**
-     * Moves an object to the top of the stack of drawn objects
-     * @param {fabric.Object} object Object to send
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    bringToFront: function (object) {
-      removeFromArray(this._objects, object);
-      this._objects.push(object);
-      return this.renderAll && this.renderAll();
-    },
-
-    /**
-     * Moves an object down in stack of drawn objects
-     * @param {fabric.Object} object Object to send
-     * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    sendBackwards: function (object, intersecting) {
-      var idx = this._objects.indexOf(object);
-
-      // if object is not on the bottom of stack
-      if (idx !== 0) {
-        var newIdx = this._findNewLowerIndex(object, idx, intersecting);
-
-        removeFromArray(this._objects, object);
-        this._objects.splice(newIdx, 0, object);
-        this.renderAll && this.renderAll();
-      }
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _findNewLowerIndex: function(object, idx, intersecting) {
-      var newIdx;
-
-      if (intersecting) {
-        newIdx = idx;
-
-        // traverse down the stack looking for the nearest intersecting object
-        for (var 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;
-    },
-
-    /**
-     * Moves an object up in stack of drawn objects
-     * @param {fabric.Object} object Object to send
-     * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    bringForward: function (object, intersecting) {
-      var idx = this._objects.indexOf(object);
-
-      // if object is not on top of stack (last item in an array)
-      if (idx !== this._objects.length - 1) {
-        var newIdx = this._findNewUpperIndex(object, idx, intersecting);
-
-        removeFromArray(this._objects, object);
-        this._objects.splice(newIdx, 0, object);
-        this.renderAll && this.renderAll();
-      }
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _findNewUpperIndex: function(object, idx, intersecting) {
-      var newIdx;
-
-      if (intersecting) {
-        newIdx = idx;
-
-        // traverse up the stack looking for the nearest intersecting object
-        for (var i = idx + 1; i < this._objects.length; ++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;
-    },
-
-    /**
-     * Moves an object to specified level in stack of drawn objects
-     * @param {fabric.Object} object Object to send
-     * @param {Number} index Position to move to
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    moveTo: function (object, index) {
-      removeFromArray(this._objects, object);
-      this._objects.splice(index, 0, object);
-      return this.renderAll && this.renderAll();
-    },
-
-    /**
-     * Clears a canvas element and removes all event listeners
-     * @return {fabric.Canvas} thisArg
-     * @chainable
-     */
-    dispose: function () {
-      this.clear();
-      this.interactive && this.removeListeners();
-      return this;
-    },
-
-    /**
-     * Returns a string representation of an instance
-     * @return {String} string representation of an instance
-     */
-    toString: function () {
-      return '#<fabric.Canvas (' + this.complexity() + '): ' +
-               '{ objects: ' + this.getObjects().length + ' }>';
-    }
-  });
-
-  extend(fabric.StaticCanvas.prototype, fabric.Observable);
-  extend(fabric.StaticCanvas.prototype, fabric.Collection);
-  extend(fabric.StaticCanvas.prototype, fabric.DataURLExporter);
-
-  extend(fabric.StaticCanvas, /** @lends fabric.StaticCanvas */ {
-
-    /**
-     * @static
-     * @type String
-     * @default
-     */
-    EMPTY_JSON: '{"objects": [], "background": "white"}',
-
-    /**
-     * Provides a way to check support of some of the canvas methods
-     * (either those of HTMLCanvasElement itself, or rendering context)
-     *
-     * @param {String} methodName Method to check support for;
-     *                            Could be one of "getImageData", "toDataURL", "toDataURLWithQuality" or "setLineDash"
-     * @return {Boolean | null} `true` if method is supported (or at least exists),
-     *                          `null` if canvas element or context can not be initialized
-     */
-    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';
-
-        case 'setLineDash':
-          return typeof ctx.setLineDash !== 'undefined';
-
-        case 'toDataURL':
-          return typeof el.toDataURL !== 'undefined';
-
-        case 'toDataURLWithQuality':
-          try {
-            el.toDataURL('image/jpeg', 0);
-            return true;
-          }
-          catch (e) { }
-          return false;
-
-        default:
-          return null;
-      }
-    }
-  });
-
-  /**
-   * Returns JSON representation of canvas
-   * @function
-   * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-   * @return {String} JSON string
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#serialization}
-   * @see {@link http://jsfiddle.net/fabricjs/pec86/|jsFiddle demo}
-   * @example <caption>JSON without additional properties</caption>
-   * var json = canvas.toJSON();
-   * @example <caption>JSON with additional properties included</caption>
-   * var json = canvas.toJSON(['lockMovementX', 'lockMovementY', 'lockRotation', 'lockScalingX', 'lockScalingY', 'lockUniScaling']);
-   * @example <caption>JSON without default values</caption>
-   * canvas.includeDefaultValues = false;
-   * var json = canvas.toJSON();
-   */
-  fabric.StaticCanvas.prototype.toJSON = fabric.StaticCanvas.prototype.toObject;
-
-})();
-
-
-/**
- * BaseBrush class
- * @class fabric.BaseBrush
- * @see {@link http://fabricjs.com/freedrawing/|Freedrawing demo}
- */
-fabric.BaseBrush = fabric.util.createClass(/** @lends fabric.BaseBrush.prototype */ {
-
-  /**
-   * Color of a brush
-   * @type String
-   * @default
-   */
-  color:            'rgb(0, 0, 0)',
-
-  /**
-   * Width of a brush
-   * @type Number
-   * @default
-   */
-  width:            1,
-
-  /**
-   * Shadow object representing shadow of this shape.
-   * <b>Backwards incompatibility note:</b> This property replaces "shadowColor" (String), "shadowOffsetX" (Number),
-   * "shadowOffsetY" (Number) and "shadowBlur" (Number) since v1.2.12
-   * @type fabric.Shadow
-   * @default
-   */
-  shadow:          null,
-
-  /**
-   * Line endings style of a brush (one of "butt", "round", "square")
-   * @type String
-   * @default
-   */
-  strokeLineCap:    'round',
-
-  /**
-   * Corner style of a brush (one of "bevil", "round", "miter")
-   * @type String
-   * @default
-   */
-  strokeLineJoin:   'round',
-
-  /**
-   * Sets shadow of an object
-   * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  setShadow: function(options) {
-    this.shadow = new fabric.Shadow(options);
-    return this;
-  },
-
-  /**
-   * Sets brush styles
-   * @private
-   */
-  _setBrushStyles: function() {
-    var ctx = this.canvas.contextTop;
-
-    ctx.strokeStyle = this.color;
-    ctx.lineWidth = this.width;
-    ctx.lineCap = this.strokeLineCap;
-    ctx.lineJoin = this.strokeLineJoin;
-  },
-
-  /**
-   * Sets brush shadow styles
-   * @private
-   */
-  _setShadow: function() {
-    if (!this.shadow) {
-      return;
-    }
-
-    var ctx = this.canvas.contextTop;
-
-    ctx.shadowColor = this.shadow.color;
-    ctx.shadowBlur = this.shadow.blur;
-    ctx.shadowOffsetX = this.shadow.offsetX;
-    ctx.shadowOffsetY = this.shadow.offsetY;
-  },
-
-  /**
-   * Removes brush shadow styles
-   * @private
-   */
-  _resetShadow: function() {
-    var ctx = this.canvas.contextTop;
-
-    ctx.shadowColor = '';
-    ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
-  }
-});
-
-
-(function() {
-
-  var utilMin = fabric.util.array.min,
-      utilMax = fabric.util.array.max;
-
-  /**
-   * PencilBrush class
-   * @class fabric.PencilBrush
-   * @extends fabric.BaseBrush
-   */
-  fabric.PencilBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.PencilBrush.prototype */ {
-
-    /**
-     * Constructor
-     * @param {fabric.Canvas} canvas
-     * @return {fabric.PencilBrush} Instance of a pencil brush
-     */
-    initialize: function(canvas) {
-      this.canvas = canvas;
-      this._points = [ ];
-    },
-
-    /**
-     * Inovoked on mouse down
-     * @param {Object} pointer
-     */
-    onMouseDown: function(pointer) {
-      this._prepareForDrawing(pointer);
-      // capture coordinates immediately
-      // this allows to draw dots (when movement never occurs)
-      this._captureDrawingPath(pointer);
-      this._render();
-    },
-
-    /**
-     * Inovoked on mouse move
-     * @param {Object} pointer
-     */
-    onMouseMove: function(pointer) {
-      this._captureDrawingPath(pointer);
-      // redraw curve
-      // clear top canvas
-      this.canvas.clearContext(this.canvas.contextTop);
-      this._render();
-    },
-
-    /**
-     * Invoked on mouse up
-     */
-    onMouseUp: function() {
-      this._finalizeAndAddPath();
-    },
-
-    /**
-     * @private
-     * @param {Object} pointer Actual mouse position related to the canvas.
-     */
-    _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);
-    },
-
-    /**
-     * @private
-     * @param {fabric.Point} point Point to be added to points array
-     */
-    _addPoint: function(point) {
-      this._points.push(point);
-    },
-
-    /**
-     * Clear points array and set contextTop canvas style.
-     * @private
-     */
-    _reset: function() {
-      this._points.length = 0;
-
-      this._setBrushStyles();
-      this._setShadow();
-    },
-
-    /**
-     * @private
-     * @param {Object} pointer Actual mouse position related to the canvas.
-     */
-    _captureDrawingPath: function(pointer) {
-      var pointerPoint = new fabric.Point(pointer.x, pointer.y);
-      this._addPoint(pointerPoint);
-    },
-
-    /**
-     * Draw a smooth path on the topCanvas using quadraticCurveTo
-     * @private
-     */
-    _render: function() {
-      var ctx  = this.canvas.contextTop,
-          v = this.canvas.viewportTransform,
-          p1 = this._points[0],
-          p2 = this._points[1];
-
-      ctx.save();
-      ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-      ctx.beginPath();
-
-      //if we only have 2 points in the path and they are the same
-      //it means that the user only clicked the canvas without moving the mouse
-      //then we should be drawing a dot. A path isn't drawn between two identical dots
-      //that's why we set them apart a bit
-      if (this._points.length === 2 && p1.x === p2.x && p1.y === p2.y) {
-        p1.x -= 0.5;
-        p2.x += 0.5;
-      }
-      ctx.moveTo(p1.x, p1.y);
-
-      for (var i = 1, len = this._points.length; i < len; i++) {
-        // we pick the point between pi + 1 & pi + 2 as the
-        // end point and p1 as our control point.
-        var midPoint = p1.midPointFrom(p2);
-        ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y);
-
-        p1 = this._points[i];
-        p2 = this._points[i + 1];
-      }
-      // Draw last line as a straight line while
-      // we wait for the next point to be able to calculate
-      // the bezier control point
-      ctx.lineTo(p1.x, p1.y);
-      ctx.stroke();
-      ctx.restore();
-    },
-
-    /**
-     * Return an SVG path based on our captured points and their bounding box
-     * @private
-     */
-    _getSVGPathData: function() {
-      this.box = this.getPathBoundingBox(this._points);
-      return this.convertPointsToSVGPath(
-        this._points, this.box.minX, this.box.minY);
-    },
-
-     /**
-      * Returns bounding box of a path based on given points
-      * @param {Array} points Array of points
-      * @return {Object} Object with minX, minY, maxX, maxY
-      */
-    getPathBoundingBox: function(points) {
-      var xBounds = [],
-          yBounds = [],
-          p1 = points[0],
-          p2 = points[1],
-          startPoint = p1;
-
-      for (var i = 1, len = points.length; i < len; i++) {
-        var midPoint = p1.midPointFrom(p2);
-        // with startPoint, p1 as control point, midpoint as end point
-        xBounds.push(startPoint.x);
-        xBounds.push(midPoint.x);
-        yBounds.push(startPoint.y);
-        yBounds.push(midPoint.y);
-
-        p1 = points[i];
-        p2 = points[i + 1];
-        startPoint = midPoint;
-      }
-
-      xBounds.push(p1.x);
-      yBounds.push(p1.y);
-
-      return {
-        minX: utilMin(xBounds),
-        minY: utilMin(yBounds),
-        maxX: utilMax(xBounds),
-        maxY: utilMax(yBounds)
-      };
-    },
-
-    /**
-     * Converts points to SVG path
-     * @param {Array} points Array of points
-     * @param {Number} minX
-     * @param {Number} minY
-     * @return {String} SVG path
-     */
-    convertPointsToSVGPath: function(points, minX, minY) {
-      var path = [],
-          p1 = new fabric.Point(points[0].x - minX, points[0].y - minY),
-          p2 = new fabric.Point(points[1].x - minX, points[1].y - minY);
-
-      path.push('M ', points[0].x - minX, ' ', points[0].y - minY, ' ');
-      for (var i = 1, len = points.length; i < len; i++) {
-        var midPoint = p1.midPointFrom(p2);
-        // p1 is our bezier control point
-        // midpoint is our endpoint
-        // start point is p(i-1) value.
-        path.push('Q ', p1.x, ' ', p1.y, ' ', midPoint.x, ' ', midPoint.y, ' ');
-        p1 = new fabric.Point(points[i].x - minX, points[i].y - minY);
-        if ((i + 1) < points.length) {
-          p2 = new fabric.Point(points[i + 1].x - minX, points[i + 1].y - minY);
-        }
-      }
-      path.push('L ', p1.x, ' ', p1.y, ' ');
-      return path;
-    },
-
-    /**
-     * Creates fabric.Path object to add on canvas
-     * @param {String} pathData Path data
-     * @return {fabric.Path} Path to add on canvas
-     */
-    createPath: function(pathData) {
-      var path = new fabric.Path(pathData);
-      path.fill = null;
-      path.stroke = this.color;
-      path.strokeWidth = this.width;
-      path.strokeLineCap = this.strokeLineCap;
-      path.strokeLineJoin = this.strokeLineJoin;
-
-      if (this.shadow) {
-        this.shadow.affectStroke = true;
-        path.setShadow(this.shadow);
-      }
-
-      return path;
-    },
-
-    /**
-     * On mouseup after drawing the path on contextTop canvas
-     * we use the points captured to create an new fabric path object
-     * and add it to the fabric canvas.
-     */
-    _finalizeAndAddPath: function() {
-      var ctx = this.canvas.contextTop;
-      ctx.closePath();
-
-      var pathData = this._getSVGPathData().join('');
-      if (pathData === 'M 0 0 Q 0 0 0 0 L 0 0') {
-        // do not create 0 width/height paths, as they are
-        // rendered inconsistently across browsers
-        // Firefox 4, for example, renders a dot,
-        // whereas Chrome 10 renders nothing
-        this.canvas.renderAll();
-        return;
-      }
-
-      // set path origin coordinates based on our bounding box
-      var originLeft = this.box.minX  + (this.box.maxX - this.box.minX) / 2,
-          originTop = this.box.minY  + (this.box.maxY - this.box.minY) / 2;
-
-      this.canvas.contextTop.arc(originLeft, originTop, 3, 0, Math.PI * 2, false);
-
-      var path = this.createPath(pathData);
-      path.set({
-        left: originLeft,
-        top: originTop,
-        originX: 'center',
-        originY: 'center'
-      });
-
-      this.canvas.add(path);
-      path.setCoords();
-
-      this.canvas.clearContext(this.canvas.contextTop);
-      this._resetShadow();
-      this.canvas.renderAll();
-
-      // fire event 'path' created
-      this.canvas.fire('path:created', { path: path });
-    }
-  });
-})();
-
-
-/**
- * CircleBrush class
- * @class fabric.CircleBrush
- */
-fabric.CircleBrush = fabric.util.createClass(fabric.BaseBrush, /** @lends fabric.CircleBrush.prototype */ {
-
-  /**
-   * Width of a brush
-   * @type Number
-   * @default
-   */
-  width: 10,
-
-  /**
-   * Constructor
-   * @param {fabric.Canvas} canvas
-   * @return {fabric.CircleBrush} Instance of a circle brush
-   */
-  initialize: function(canvas) {
-    this.canvas = canvas;
-    this.points = [ ];
-  },
-  /**
-  * Invoked inside on mouse down and mouse move
-  * @param {Object} pointer
-  */
-  drawDot: function(pointer) {
-    var point = this.addPoint(pointer),
-        ctx = this.canvas.contextTop,
-        v = this.canvas.viewportTransform;
-    ctx.save();
-    ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-
-    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();
-  },
-
-  /**
-   * Invoked on mouse down
-   */
-  onMouseDown: function(pointer) {
-    this.points.length = 0;
-    this.canvas.clearContext(this.canvas.contextTop);
-    this._setShadow();
-    this.drawDot(pointer);
-  },
-
-  /**
-   * Invoked on mouse move
-   * @param {Object} pointer
-   */
-  onMouseMove: function(pointer) {
-    this.drawDot(pointer);
-  },
-
-  /**
-   * Invoked on mouse up
-   */
-  onMouseUp: function() {
-    var originalRenderOnAddRemove = this.canvas.renderOnAddRemove;
-    this.canvas.renderOnAddRemove = false;
-
-    var circles = [ ];
-
-    for (var 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.renderAll();
-  },
-
-  /**
-   * @param {Object} pointer
-   * @return {fabric.Point} Just added pointer point
-   */
-  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;
-  }
-});
-
-
-/**
- * SprayBrush class
- * @class fabric.SprayBrush
- */
-fabric.SprayBrush = fabric.util.createClass( fabric.BaseBrush, /** @lends fabric.SprayBrush.prototype */ {
-
-  /**
-   * Width of a spray
-   * @type Number
-   * @default
-   */
-  width:              10,
-
-  /**
-   * Density of a spray (number of dots per chunk)
-   * @type Number
-   * @default
-   */
-  density:            20,
-
-  /**
-   * Width of spray dots
-   * @type Number
-   * @default
-   */
-  dotWidth:           1,
-
-  /**
-   * Width variance of spray dots
-   * @type Number
-   * @default
-   */
-  dotWidthVariance:   1,
-
-  /**
-   * Whether opacity of a dot should be random
-   * @type Boolean
-   * @default
-   */
-  randomOpacity:        false,
-
-  /**
-   * Whether overlapping dots (rectangles) should be removed (for performance reasons)
-   * @type Boolean
-   * @default
-   */
-  optimizeOverlapping:  true,
-
-  /**
-   * Constructor
-   * @param {fabric.Canvas} canvas
-   * @return {fabric.SprayBrush} Instance of a spray brush
-   */
-  initialize: function(canvas) {
-    this.canvas = canvas;
-    this.sprayChunks = [ ];
-  },
-
-  /**
-   * Invoked on mouse down
-   * @param {Object} pointer
-   */
-  onMouseDown: function(pointer) {
-    this.sprayChunks.length = 0;
-    this.canvas.clearContext(this.canvas.contextTop);
-    this._setShadow();
-
-    this.addSprayChunk(pointer);
-    this.render();
-  },
-
-  /**
-   * Invoked on mouse move
-   * @param {Object} pointer
-   */
-  onMouseMove: function(pointer) {
-    this.addSprayChunk(pointer);
-    this.render();
-  },
-
-  /**
-   * Invoked on mouse up
-   */
-  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
-        });
-
-        this.shadow && rect.setShadow(this.shadow);
-        rects.push(rect);
-      }
-    }
-
-    if (this.optimizeOverlapping) {
-      rects = this._getOptimizedRects(rects);
-    }
-
-    var group = new fabric.Group(rects, { 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.renderAll();
-  },
-
-    /**
-     * @private
-     * @param {Array} rects
-     */
-  _getOptimizedRects: function(rects) {
-
-    // avoid creating duplicate rects at the same coordinates
-    var uniqueRects = { }, key;
-
-    for (var 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;
-  },
-
-  /**
-   * Renders brush
-   */
-  render: function() {
-    var ctx = this.canvas.contextTop;
-    ctx.fillStyle = this.color;
-
-    var v = this.canvas.viewportTransform;
-    ctx.save();
-    ctx.transform(v[0], v[1], v[2], v[3], v[4], v[5]);
-
-    for (var i = 0, len = this.sprayChunkPoints.length; i < len; i++) {
-      var point = this.sprayChunkPoints[i];
-      if (typeof point.opacity !== 'undefined') {
-        ctx.globalAlpha = point.opacity;
-      }
-      ctx.fillRect(point.x, point.y, point.width, point.width);
-    }
-    ctx.restore();
-  },
-
-  /**
-   * @param {Object} pointer
-   */
-  addSprayChunk: function(pointer) {
-    this.sprayChunkPoints = [ ];
-
-    var x, y, width, radius = this.width / 2;
-
-    for (var 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(
-          // bottom clamp width to 1
-          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);
-  }
-});
-
-
-/**
- * PatternBrush class
- * @class fabric.PatternBrush
- * @extends fabric.BaseBrush
- */
-fabric.PatternBrush = fabric.util.createClass(fabric.PencilBrush, /** @lends fabric.PatternBrush.prototype */ {
-
-  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;
-  },
-
-  getPatternSrcFunction: function() {
-    return String(this.getPatternSrc).replace('this.color', '"' + this.color + '"');
-  },
-
-  /**
-   * Creates "pattern" instance property
-   */
-  getPattern: function() {
-    return this.canvas.contextTop.createPattern(this.source || this.getPatternSrc(), 'repeat');
-  },
-
-  /**
-   * Sets brush styles
-   */
-  _setBrushStyles: function() {
-    this.callSuper('_setBrushStyles');
-    this.canvas.contextTop.strokeStyle = this.getPattern();
-  },
-
-  /**
-   * Creates path
-   */
-  createPath: function(pathData) {
-    var path = this.callSuper('createPath', pathData);
-    path.stroke = new fabric.Pattern({
-      source: this.source || this.getPatternSrcFunction()
-    });
-    return path;
-  }
-});
-
-
-fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
-
-  /**
-   * Exports canvas element to a dataurl image. Note that when multiplier is used, cropping is scaled appropriately
-   * @param {Object} [options] Options object
-   * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
-   * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
-   * @param {Number} [options.multiplier=1] Multiplier to scale by
-   * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
-   * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
-   * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
-   * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
-   * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
-   * @see {@link http://jsfiddle.net/fabricjs/NfZVb/|jsFiddle demo}
-   * @example <caption>Generate jpeg dataURL with lower quality</caption>
-   * var dataURL = canvas.toDataURL({
-   *   format: 'jpeg',
-   *   quality: 0.8
-   * });
-   * @example <caption>Generate cropped png dataURL (clipping of canvas)</caption>
-   * var dataURL = canvas.toDataURL({
-   *   format: 'png',
-   *   left: 100,
-   *   top: 100,
-   *   width: 200,
-   *   height: 200
-   * });
-   * @example <caption>Generate double scaled png dataURL</caption>
-   * var dataURL = canvas.toDataURL({
-   *   format: 'png',
-   *   multiplier: 2
-   * });
-   */
-  toDataURL: function (options) {
-    options || (options = { });
-
-    var format = options.format || 'png',
-        quality = options.quality || 1,
-        multiplier = options.multiplier || 1,
-        cropping = {
-          left: options.left,
-          top: options.top,
-          width: options.width,
-          height: options.height
-        };
-
-    if (multiplier !== 1) {
-      return this.__toDataURLWithMultiplier(format, quality, cropping, multiplier);
-    }
-    else {
-      return this.__toDataURL(format, quality, cropping);
-    }
-  },
-
-  /**
-   * @private
-   */
-  __toDataURL: function(format, quality, cropping) {
-
-    this.renderAll(true);
-
-    var canvasEl = this.upperCanvasEl || this.lowerCanvasEl,
-        croppedCanvasEl = this.__getCroppedCanvas(canvasEl, cropping);
-
-    // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
-    if (format === 'jpg') {
-      format = 'jpeg';
-    }
-
-    var data = (fabric.StaticCanvas.supports('toDataURLWithQuality'))
-              ? (croppedCanvasEl || canvasEl).toDataURL('image/' + format, quality)
-              : (croppedCanvasEl || canvasEl).toDataURL('image/' + format);
-
-    this.contextTop && this.clearContext(this.contextTop);
-    this.renderAll();
-
-    if (croppedCanvasEl) {
-      croppedCanvasEl = null;
-    }
-
-    return data;
-  },
-
-  /**
-   * @private
-   */
-  __getCroppedCanvas: function(canvasEl, cropping) {
-
-    var croppedCanvasEl,
-        croppedCtx,
-        shouldCrop = 'left' in cropping ||
-                     'top' in cropping ||
-                     'width' in cropping ||
-                     'height' in cropping;
-
-    if (shouldCrop) {
-
-      croppedCanvasEl = fabric.util.createCanvasElement();
-      croppedCtx = croppedCanvasEl.getContext('2d');
-
-      croppedCanvasEl.width = cropping.width || this.width;
-      croppedCanvasEl.height = cropping.height || this.height;
-
-      croppedCtx.drawImage(canvasEl, -cropping.left || 0, -cropping.top || 0);
-    }
-
-    return croppedCanvasEl;
-  },
-
-  /**
-   * @private
-   */
-  __toDataURLWithMultiplier: function(format, quality, cropping, multiplier) {
-
-    var origWidth = this.getWidth(),
-        origHeight = this.getHeight(),
-        scaledWidth = origWidth * multiplier,
-        scaledHeight = origHeight * multiplier,
-        activeObject = this.getActiveObject(),
-        activeGroup = this.getActiveGroup(),
-
-        ctx = this.contextTop || this.contextContainer;
-
-    if (multiplier > 1) {
-      this.setWidth(scaledWidth).setHeight(scaledHeight);
-    }
-    ctx.scale(multiplier, multiplier);
-
-    if (cropping.left) {
-      cropping.left *= multiplier;
-    }
-    if (cropping.top) {
-      cropping.top *= multiplier;
-    }
-    if (cropping.width) {
-      cropping.width *= multiplier;
-    }
-    else if (multiplier < 1) {
-      cropping.width = scaledWidth;
-    }
-    if (cropping.height) {
-      cropping.height *= multiplier;
-    }
-    else if (multiplier < 1) {
-      cropping.height = scaledHeight;
-    }
-
-    if (activeGroup) {
-      // not removing group due to complications with restoring it with correct state afterwords
-      this._tempRemoveBordersControlsFromGroup(activeGroup);
-    }
-    else if (activeObject && this.deactivateAll) {
-      this.deactivateAll();
-    }
-
-    this.renderAll(true);
-
-    var data = this.__toDataURL(format, quality, cropping);
-
-    // restoring width, height for `renderAll` to draw
-    // background properly (while context is scaled)
-    this.width = origWidth;
-    this.height = origHeight;
-
-    ctx.scale(1 / multiplier,  1 / multiplier);
-    this.setWidth(origWidth).setHeight(origHeight);
-
-    if (activeGroup) {
-      this._restoreBordersControlsOnGroup(activeGroup);
-    }
-    else if (activeObject && this.setActiveObject) {
-      this.setActiveObject(activeObject);
-    }
-
-    this.contextTop && this.clearContext(this.contextTop);
-    this.renderAll();
-
-    return data;
-  },
-
-  /**
-   * Exports canvas element to a dataurl image (allowing to change image size via multiplier).
-   * @deprecated since 1.0.13
-   * @param {String} format (png|jpeg)
-   * @param {Number} multiplier
-   * @param {Number} quality (0..1)
-   * @return {String}
-   */
-  toDataURLWithMultiplier: function (format, multiplier, quality) {
-    return this.toDataURL({
-      format: format,
-      multiplier: multiplier,
-      quality: quality
-    });
-  },
-
-  /**
-   * @private
-   */
-  _tempRemoveBordersControlsFromGroup: function(group) {
-    group.origHasControls = group.hasControls;
-    group.origBorderColor = group.borderColor;
-
-    group.hasControls = true;
-    group.borderColor = 'rgba(0,0,0,0)';
-
-    group.forEachObject(function(o) {
-      o.origBorderColor = o.borderColor;
-      o.borderColor = 'rgba(0,0,0,0)';
-    });
-  },
-
-  /**
-   * @private
-   */
-  _restoreBordersControlsOnGroup: function(group) {
-    group.hideControls = group.origHideControls;
-    group.borderColor = group.origBorderColor;
-
-    group.forEachObject(function(o) {
-      o.borderColor = o.origBorderColor;
-      delete o.origBorderColor;
-    });
-  }
-});
-
-
-fabric.util.object.extend(fabric.StaticCanvas.prototype, /** @lends fabric.StaticCanvas.prototype */ {
-
-  /**
-   * Populates canvas with data from the specified dataless JSON.
-   * JSON format must conform to the one of {@link fabric.Canvas#toDatalessJSON}
-   * @deprecated since 1.2.2
-   * @param {String|Object} json JSON string or object
-   * @param {Function} callback Callback, invoked when json is parsed
-   *                            and corresponding objects (e.g: {@link fabric.Image})
-   *                            are initialized
-   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
-   * @return {fabric.Canvas} instance
-   * @chainable
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
-   */
-  loadFromDatalessJSON: function (json, callback, reviver) {
-    return this.loadFromJSON(json, callback, reviver);
-  },
-
-  /**
-   * Populates canvas with data from the specified JSON.
-   * JSON format must conform to the one of {@link fabric.Canvas#toJSON}
-   * @param {String|Object} json JSON string or object
-   * @param {Function} callback Callback, invoked when json is parsed
-   *                            and corresponding objects (e.g: {@link fabric.Image})
-   *                            are initialized
-   * @param {Function} [reviver] Method for further parsing of JSON elements, called after each fabric object created.
-   * @return {fabric.Canvas} instance
-   * @chainable
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#deserialization}
-   * @see {@link http://jsfiddle.net/fabricjs/fmgXt/|jsFiddle demo}
-   * @example <caption>loadFromJSON</caption>
-   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas));
-   * @example <caption>loadFromJSON with reviver</caption>
-   * canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(o, object) {
-   *   // `o` = json object
-   *   // `object` = fabric.Object instance
-   *   // ... do some stuff ...
-   * });
-   */
-  loadFromJSON: function (json, callback, reviver) {
-    if (!json) {
-      return;
-    }
-
-    // serialize if it wasn't already
-    var serialized = (typeof json === 'string')
-      ? JSON.parse(json)
-      : json;
-
-    this.clear();
-
-    var _this = this;
-    this._enlivenObjects(serialized.objects, function () {
-      _this._setBgOverlay(serialized, callback);
-    }, reviver);
-
-    return this;
-  },
-
-  /**
-   * @private
-   * @param {Object} serialized Object with background and overlay information
-   * @param {Function} callback Invoked after all background and overlay images/patterns loaded
-   */
-  _setBgOverlay: function(serialized, callback) {
-    var _this = this,
-        loaded = {
-          backgroundColor: false,
-          overlayColor: false,
-          backgroundImage: false,
-          overlayImage: false
-        };
-
-    if (!serialized.backgroundImage && !serialized.overlayImage && !serialized.background && !serialized.overlay) {
-      callback && callback();
-      return;
-    }
-
-    var cbIfLoaded = function () {
-      if (loaded.backgroundImage && loaded.overlayImage && loaded.backgroundColor && loaded.overlayColor) {
-        _this.renderAll();
-        callback && callback();
-      }
-    };
-
-    this.__setBgOverlay('backgroundImage', serialized.backgroundImage, loaded, cbIfLoaded);
-    this.__setBgOverlay('overlayImage', serialized.overlayImage, loaded, cbIfLoaded);
-    this.__setBgOverlay('backgroundColor', serialized.background, loaded, cbIfLoaded);
-    this.__setBgOverlay('overlayColor', serialized.overlay, loaded, cbIfLoaded);
-
-    cbIfLoaded();
-  },
-
-  /**
-   * @private
-   * @param {String} property Property to set (backgroundImage, overlayImage, backgroundColor, overlayColor)
-   * @param {(Object|String)} value Value to set
-   * @param {Object} loaded Set loaded property to true if property is set
-   * @param {Object} callback Callback function to invoke after property is set
-   */
-  __setBgOverlay: function(property, value, loaded, callback) {
-    var _this = this;
-
-    if (!value) {
-      loaded[property] = true;
-      return;
-    }
-
-    if (property === 'backgroundImage' || property === 'overlayImage') {
-      fabric.Image.fromObject(value, function(img) {
-        _this[property] = img;
-        loaded[property] = true;
-        callback && callback();
-      });
-    }
-    else {
-      this['set' + fabric.util.string.capitalize(property, true)](value, function() {
-        loaded[property] = true;
-        callback && callback();
-      });
-    }
-  },
-
-  /**
-   * @private
-   * @param {Array} objects
-   * @param {Function} callback
-   * @param {Function} [reviver]
-   */
-  _enlivenObjects: function (objects, callback, reviver) {
-    var _this = this;
-
-    if (!objects || objects.length === 0) {
-      callback && callback();
-      return;
-    }
-
-    var renderOnAddRemove = this.renderOnAddRemove;
-    this.renderOnAddRemove = false;
-
-    fabric.util.enlivenObjects(objects, function(enlivenedObjects) {
-      enlivenedObjects.forEach(function(obj, index) {
-        _this.insertAt(obj, index, true);
-      });
-
-      _this.renderOnAddRemove = renderOnAddRemove;
-      callback && callback();
-    }, null, reviver);
-  },
-
-  /**
-   * @private
-   * @param {String} format
-   * @param {Function} callback
-   */
-  _toDataURL: function (format, callback) {
-    this.clone(function (clone) {
-      callback(clone.toDataURL(format));
-    });
-  },
-
-  /**
-   * @private
-   * @param {String} format
-   * @param {Number} multiplier
-   * @param {Function} callback
-   */
-  _toDataURLWithMultiplier: function (format, multiplier, callback) {
-    this.clone(function (clone) {
-      callback(clone.toDataURLWithMultiplier(format, multiplier));
-    });
-  },
-
-  /**
-   * Clones canvas instance
-   * @param {Object} [callback] Receives cloned instance as a first argument
-   * @param {Array} [properties] Array of properties to include in the cloned canvas and children
-   */
-  clone: function (callback, properties) {
-    var data = JSON.stringify(this.toJSON(properties));
-    this.cloneWithoutData(function(clone) {
-      clone.loadFromJSON(data, function() {
-        callback && callback(clone);
-      });
-    });
-  },
-
-  /**
-   * Clones canvas instance without cloning existing data.
-   * This essentially copies canvas dimensions, clipping properties, etc.
-   * but leaves data empty (so that you can populate it with your own)
-   * @param {Object} [callback] Receives cloned instance as a first argument
-   */
-  cloneWithoutData: function(callback) {
-    var el = fabric.document.createElement('canvas');
-
-    el.width = this.getWidth();
-    el.height = this.getHeight();
-
-    var clone = new fabric.Canvas(el);
-    clone.clipTo = this.clipTo;
-    if (this.backgroundImage) {
-      clone.setBackgroundImage(this.backgroundImage.src, function() {
-        clone.renderAll();
-        callback && callback(clone);
-      });
-      clone.backgroundImageOpacity = this.backgroundImageOpacity;
-      clone.backgroundImageStretch = this.backgroundImageStretch;
-    }
-    else {
-      callback && callback(clone);
-    }
-  }
-});
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      toFixed = fabric.util.toFixed,
-      capitalize = fabric.util.string.capitalize,
-      degreesToRadians = fabric.util.degreesToRadians,
-      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
-
-  if (fabric.Object) {
-    return;
-  }
-
-  /**
-   * Root object class from which all 2d shape classes inherit from
-   * @class fabric.Object
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#objects}
-   * @see {@link fabric.Object#initialize} for constructor definition
-   *
-   * @fires added
-   * @fires removed
-   *
-   * @fires selected
-   * @fires modified
-   * @fires rotating
-   * @fires scaling
-   * @fires moving
-   *
-   * @fires mousedown
-   * @fires mouseup
-   */
-  fabric.Object = fabric.util.createClass(/** @lends fabric.Object.prototype */ {
-
-    /**
-     * Retrieves object's {@link fabric.Object#clipTo|clipping function}
-     * @method getClipTo
-     * @memberOf fabric.Object.prototype
-     * @return {Function}
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#clipTo|clipping function}
-     * @method setClipTo
-     * @memberOf fabric.Object.prototype
-     * @param {Function} clipTo Clipping function
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#transformMatrix|transformMatrix}
-     * @method getTransformMatrix
-     * @memberOf fabric.Object.prototype
-     * @return {Array} transformMatrix
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#transformMatrix|transformMatrix}
-     * @method setTransformMatrix
-     * @memberOf fabric.Object.prototype
-     * @param {Array} transformMatrix
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#visible|visible} state
-     * @method getVisible
-     * @memberOf fabric.Object.prototype
-     * @return {Boolean} True if visible
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#visible|visible} state
-     * @method setVisible
-     * @memberOf fabric.Object.prototype
-     * @param {Boolean} value visible value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#shadow|shadow}
-     * @method getShadow
-     * @memberOf fabric.Object.prototype
-     * @return {Object} Shadow instance
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#stroke|stroke}
-     * @method getStroke
-     * @memberOf fabric.Object.prototype
-     * @return {String} stroke value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#stroke|stroke}
-     * @method setStroke
-     * @memberOf fabric.Object.prototype
-     * @param {String} value stroke value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#strokeWidth|strokeWidth}
-     * @method getStrokeWidth
-     * @memberOf fabric.Object.prototype
-     * @return {Number} strokeWidth value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#strokeWidth|strokeWidth}
-     * @method setStrokeWidth
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value strokeWidth value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#originX|originX}
-     * @method getOriginX
-     * @memberOf fabric.Object.prototype
-     * @return {String} originX value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#originX|originX}
-     * @method setOriginX
-     * @memberOf fabric.Object.prototype
-     * @param {String} value originX value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#originY|originY}
-     * @method getOriginY
-     * @memberOf fabric.Object.prototype
-     * @return {String} originY value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#originY|originY}
-     * @method setOriginY
-     * @memberOf fabric.Object.prototype
-     * @param {String} value originY value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#fill|fill}
-     * @method getFill
-     * @memberOf fabric.Object.prototype
-     * @return {String} Fill value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#fill|fill}
-     * @method setFill
-     * @memberOf fabric.Object.prototype
-     * @param {String} value Fill value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#opacity|opacity}
-     * @method getOpacity
-     * @memberOf fabric.Object.prototype
-     * @return {Number} Opacity value (0-1)
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#opacity|opacity}
-     * @method setOpacity
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value Opacity value (0-1)
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#angle|angle} (in degrees)
-     * @method getAngle
-     * @memberOf fabric.Object.prototype
-     * @return {Number}
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#angle|angle}
-     * @method setAngle
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value Angle value (in degrees)
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#top|top position}
-     * @method getTop
-     * @memberOf fabric.Object.prototype
-     * @return {Number} Top value (in pixels)
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#top|top position}
-     * @method setTop
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value Top value (in pixels)
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#left|left position}
-     * @method getLeft
-     * @memberOf fabric.Object.prototype
-     * @return {Number} Left value (in pixels)
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#left|left position}
-     * @method setLeft
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value Left value (in pixels)
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#scaleX|scaleX} value
-     * @method getScaleX
-     * @memberOf fabric.Object.prototype
-     * @return {Number} scaleX value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#scaleX|scaleX} value
-     * @method setScaleX
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value scaleX value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#scaleY|scaleY} value
-     * @method getScaleY
-     * @memberOf fabric.Object.prototype
-     * @return {Number} scaleY value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#scaleY|scaleY} value
-     * @method setScaleY
-     * @memberOf fabric.Object.prototype
-     * @param {Number} value scaleY value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#flipX|flipX} value
-     * @method getFlipX
-     * @memberOf fabric.Object.prototype
-     * @return {Boolean} flipX value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#flipX|flipX} value
-     * @method setFlipX
-     * @memberOf fabric.Object.prototype
-     * @param {Boolean} value flipX value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's {@link fabric.Object#flipY|flipY} value
-     * @method getFlipY
-     * @memberOf fabric.Object.prototype
-     * @return {Boolean} flipY value
-     */
-
-    /**
-     * Sets object's {@link fabric.Object#flipY|flipY} value
-     * @method setFlipY
-     * @memberOf fabric.Object.prototype
-     * @param {Boolean} value flipY value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-
-    /**
-     * Type of an object (rect, circle, path, etc.)
-     * @type String
-     * @default
-     */
-    type:                     'object',
-
-    /**
-     * Horizontal origin of transformation of an object (one of "left", "right", "center")
-     * @type String
-     * @default
-     */
-    originX:                  'left',
-
-    /**
-     * Vertical origin of transformation of an object (one of "top", "bottom", "center")
-     * @type String
-     * @default
-     */
-    originY:                  'top',
-
-    /**
-     * Top position of an object. Note that by default it's relative to object center. You can change this by setting originY={top/center/bottom}
-     * @type Number
-     * @default
-     */
-    top:                      0,
-
-    /**
-     * Left position of an object. Note that by default it's relative to object center. You can change this by setting originX={left/center/right}
-     * @type Number
-     * @default
-     */
-    left:                     0,
-
-    /**
-     * Object width
-     * @type Number
-     * @default
-     */
-    width:                    0,
-
-    /**
-     * Object height
-     * @type Number
-     * @default
-     */
-    height:                   0,
-
-    /**
-     * Object scale factor (horizontal)
-     * @type Number
-     * @default
-     */
-    scaleX:                   1,
-
-    /**
-     * Object scale factor (vertical)
-     * @type Number
-     * @default
-     */
-    scaleY:                   1,
-
-    /**
-     * When true, an object is rendered as flipped horizontally
-     * @type Boolean
-     * @default
-     */
-    flipX:                    false,
-
-    /**
-     * When true, an object is rendered as flipped vertically
-     * @type Boolean
-     * @default
-     */
-    flipY:                    false,
-
-    /**
-     * Opacity of an object
-     * @type Number
-     * @default
-     */
-    opacity:                  1,
-
-    /**
-     * Angle of rotation of an object (in degrees)
-     * @type Number
-     * @default
-     */
-    angle:                    0,
-
-    /**
-     * Size of object's controlling corners (in pixels)
-     * @type Number
-     * @default
-     */
-    cornerSize:               12,
-
-    /**
-     * When true, object's controlling corners are rendered as transparent inside (i.e. stroke instead of fill)
-     * @type Boolean
-     * @default
-     */
-    transparentCorners:       true,
-
-    /**
-     * Default cursor value used when hovering over this object on canvas
-     * @type String
-     * @default
-     */
-    hoverCursor:              null,
-
-    /**
-     * Padding between object and its controlling borders (in pixels)
-     * @type Number
-     * @default
-     */
-    padding:                  0,
-
-    /**
-     * Color of controlling borders of an object (when it's active)
-     * @type String
-     * @default
-     */
-    borderColor:              'rgba(102,153,255,0.75)',
-
-    /**
-     * Color of controlling corners of an object (when it's active)
-     * @type String
-     * @default
-     */
-    cornerColor:              'rgba(102,153,255,0.5)',
-
-    /**
-     * When true, this object will use center point as the origin of transformation
-     * when being scaled via the controls.
-     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
-     * @since 1.3.4
-     * @type Boolean
-     * @default
-     */
-    centeredScaling:          false,
-
-    /**
-     * When true, this object will use center point as the origin of transformation
-     * when being rotated via the controls.
-     * <b>Backwards incompatibility note:</b> This property replaces "centerTransform" (Boolean).
-     * @since 1.3.4
-     * @type Boolean
-     * @default
-     */
-    centeredRotation:         true,
-
-    /**
-     * Color of object's fill
-     * @type String
-     * @default
-     */
-    fill:                     'rgb(0,0,0)',
-
-    /**
-     * Fill rule used to fill an object
-     * @type String
-     * @default
-     */
-    fillRule:                 'source-over',
-
-    /**
-     * Background color of an object. Only works with text objects at the moment.
-     * @type String
-     * @default
-     */
-    backgroundColor:          '',
-
-    /**
-     * When defined, an object is rendered via stroke and this property specifies its color
-     * @type String
-     * @default
-     */
-    stroke:                   null,
-
-    /**
-     * Width of a stroke used to render this object
-     * @type Number
-     * @default
-     */
-    strokeWidth:              1,
-
-    /**
-     * Array specifying dash pattern of an object's stroke (stroke must be defined)
-     * @type Array
-     */
-    strokeDashArray:          null,
-
-    /**
-     * Line endings style of an object's stroke (one of "butt", "round", "square")
-     * @type String
-     * @default
-     */
-    strokeLineCap:            'butt',
-
-    /**
-     * Corner style of an object's stroke (one of "bevil", "round", "miter")
-     * @type String
-     * @default
-     */
-    strokeLineJoin:           'miter',
-
-    /**
-     * Maximum miter length (used for strokeLineJoin = "miter") of an object's stroke
-     * @type Number
-     * @default
-     */
-    strokeMiterLimit:         10,
-
-    /**
-     * Shadow object representing shadow of this shape
-     * @type fabric.Shadow
-     * @default
-     */
-    shadow:                   null,
-
-    /**
-     * Opacity of object's controlling borders when object is active and moving
-     * @type Number
-     * @default
-     */
-    borderOpacityWhenMoving:  0.4,
-
-    /**
-     * Scale factor of object's controlling borders
-     * @type Number
-     * @default
-     */
-    borderScaleFactor:        1,
-
-    /**
-     * Transform matrix (similar to SVG's transform matrix)
-     * @type Array
-     */
-    transformMatrix:          null,
-
-    /**
-     * Minimum allowed scale value of an object
-     * @type Number
-     * @default
-     */
-    minScaleLimit:            0.01,
-
-    /**
-     * When set to `false`, an object can not be selected for modification (using either point-click-based or group-based selection).
-     * But events still fire on it.
-     * @type Boolean
-     * @default
-     */
-    selectable:               true,
-
-    /**
-     * When set to `false`, an object can not be a target of events. All events propagate through it. Introduced in v1.3.4
-     * @type Boolean
-     * @default
-     */
-    evented:                  true,
-
-    /**
-     * When set to `false`, an object is not rendered on canvas
-     * @type Boolean
-     * @default
-     */
-    visible:                  true,
-
-    /**
-     * When set to `false`, object's controls are not displayed and can not be used to manipulate object
-     * @type Boolean
-     * @default
-     */
-    hasControls:              true,
-
-    /**
-     * When set to `false`, object's controlling borders are not rendered
-     * @type Boolean
-     * @default
-     */
-    hasBorders:               true,
-
-    /**
-     * When set to `false`, object's controlling rotating point will not be visible or selectable
-     * @type Boolean
-     * @default
-     */
-    hasRotatingPoint:         true,
-
-    /**
-     * Offset for object's controlling rotating point (when enabled via `hasRotatingPoint`)
-     * @type Number
-     * @default
-     */
-    rotatingPointOffset:      40,
-
-    /**
-     * When set to `true`, objects are "found" on canvas on per-pixel basis rather than according to bounding box
-     * @type Boolean
-     * @default
-     */
-    perPixelTargetFind:       false,
-
-    /**
-     * When `false`, default object's values are not included in its serialization
-     * @type Boolean
-     * @default
-     */
-    includeDefaultValues:     true,
-
-    /**
-     * Function that determines clipping of an object (context is passed as a first argument)
-     * Note that context origin is at the object's center point (not left/top corner)
-     * @type Function
-     */
-    clipTo:                   null,
-
-    /**
-     * When `true`, object horizontal movement is locked
-     * @type Boolean
-     * @default
-     */
-    lockMovementX:            false,
-
-    /**
-     * When `true`, object vertical movement is locked
-     * @type Boolean
-     * @default
-     */
-    lockMovementY:            false,
-
-    /**
-     * When `true`, object rotation is locked
-     * @type Boolean
-     * @default
-     */
-    lockRotation:             false,
-
-    /**
-     * When `true`, object horizontal scaling is locked
-     * @type Boolean
-     * @default
-     */
-    lockScalingX:             false,
-
-    /**
-     * When `true`, object vertical scaling is locked
-     * @type Boolean
-     * @default
-     */
-    lockScalingY:             false,
-
-    /**
-     * When `true`, object non-uniform scaling is locked
-     * @type Boolean
-     * @default
-     */
-    lockUniScaling:           false,
-
-    /**
-     * When `true`, object cannot be flipped by scaling into negative values
-     * @type Boolean
-     * @default
-     */
-
-    lockScalingFlip:          false,
-    /**
-     * List of properties to consider when checking if state
-     * of an object is changed (fabric.Object#hasStateChanged)
-     * as well as for history (undo/redo) purposes
-     * @type Array
-     */
-    stateProperties:  (
-      'top left width height scaleX scaleY flipX flipY originX originY transformMatrix ' +
-      'stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit ' +
-      'angle opacity fill fillRule shadow clipTo visible backgroundColor'
-    ).split(' '),
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object
-     */
-    initialize: function(options) {
-      if (options) {
-        this.setOptions(options);
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options object
-     */
-    _initGradient: function(options) {
-      if (options.fill && options.fill.colorStops && !(options.fill instanceof fabric.Gradient)) {
-        this.set('fill', new fabric.Gradient(options.fill));
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options object
-     */
-    _initPattern: function(options) {
-      if (options.fill && options.fill.source && !(options.fill instanceof fabric.Pattern)) {
-        this.set('fill', new fabric.Pattern(options.fill));
-      }
-      if (options.stroke && options.stroke.source && !(options.stroke instanceof fabric.Pattern)) {
-        this.set('stroke', new fabric.Pattern(options.stroke));
-      }
-    },
-
-    /**
-     * @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);
-      }
-    },
-
-    /**
-     * Sets object's properties from options
-     * @param {Object} [options] Options object
-     */
-    setOptions: function(options) {
-      for (var prop in options) {
-        this.set(prop, options[prop]);
-      }
-      this._initGradient(options);
-      this._initPattern(options);
-      this._initClipping(options);
-    },
-
-    /**
-     * Transforms context when rendering an object
-     * @param {CanvasRenderingContext2D} ctx Context
-     * @param {Boolean} fromLeft When true, context is transformed to object's top/left corner. This is used when rendering text on Node
-     */
-    transform: function(ctx, fromLeft) {
-      if (this.group) {
-        this.group.transform(ctx, fromLeft);
-      }
-      ctx.globalAlpha = this.opacity;
-
-      var center = fromLeft ? this._getLeftTopCoords() : this.getCenterPoint();
-      ctx.translate(center.x, center.y);
-      ctx.rotate(degreesToRadians(this.angle));
-      ctx.scale(
-        this.scaleX * (this.flipX ? -1 : 1),
-        this.scaleY * (this.flipY ? -1 : 1)
-      );
-    },
-
-    /**
-     * Returns an object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      var NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
-
-          object = {
-            type:               this.type,
-            originX:            this.originX,
-            originY:            this.originY,
-            left:               toFixed(this.left, NUM_FRACTION_DIGITS),
-            top:                toFixed(this.top, NUM_FRACTION_DIGITS),
-            width:              toFixed(this.width, NUM_FRACTION_DIGITS),
-            height:             toFixed(this.height, NUM_FRACTION_DIGITS),
-            fill:               (this.fill && this.fill.toObject) ? this.fill.toObject() : this.fill,
-            stroke:             (this.stroke && this.stroke.toObject) ? this.stroke.toObject() : this.stroke,
-            strokeWidth:        toFixed(this.strokeWidth, NUM_FRACTION_DIGITS),
-            strokeDashArray:    this.strokeDashArray,
-            strokeLineCap:      this.strokeLineCap,
-            strokeLineJoin:     this.strokeLineJoin,
-            strokeMiterLimit:   toFixed(this.strokeMiterLimit, NUM_FRACTION_DIGITS),
-            scaleX:             toFixed(this.scaleX, NUM_FRACTION_DIGITS),
-            scaleY:             toFixed(this.scaleY, NUM_FRACTION_DIGITS),
-            angle:              toFixed(this.getAngle(), NUM_FRACTION_DIGITS),
-            flipX:              this.flipX,
-            flipY:              this.flipY,
-            opacity:            toFixed(this.opacity, NUM_FRACTION_DIGITS),
-            shadow:             (this.shadow && this.shadow.toObject) ? this.shadow.toObject() : this.shadow,
-            visible:            this.visible,
-            clipTo:             this.clipTo && String(this.clipTo),
-            backgroundColor:    this.backgroundColor
-          };
-
-      if (!this.includeDefaultValues) {
-        object = this._removeDefaultValues(object);
-      }
-
-      fabric.util.populateWithProperties(this, object, propertiesToInclude);
-
-      return object;
-    },
-
-    /**
-     * Returns (dataless) object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toDatalessObject: function(propertiesToInclude) {
-      // will be overwritten by subclasses
-      return this.toObject(propertiesToInclude);
-    },
-
-    /**
-     * @private
-     * @param {Object} object
-     */
-    _removeDefaultValues: function(object) {
-      var prototype = fabric.util.getKlass(object.type).prototype,
-          stateProperties = prototype.stateProperties;
-
-      stateProperties.forEach(function(prop) {
-        if (object[prop] === prototype[prop]) {
-          delete object[prop];
-        }
-      });
-
-      return object;
-    },
-
-    /**
-     * Returns a string representation of an instance
-     * @return {String}
-     */
-    toString: function() {
-      return '#<fabric.' + capitalize(this.type) + '>';
-    },
-
-    /**
-     * Basic getter
-     * @param {String} property Property name
-     * @return {Any} value of a property
-     */
-    get: function(property) {
-      return this[property];
-    },
-
-    /**
-     * @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;
-    },
-
-    /**
-     * @private
-     * @param {String} key
-     * @param {Any} value
-     * @return {fabric.Object} thisArg
-     */
-    _set: function(key, value) {
-      var shouldConstrainValue = (key === 'scaleX' || key === 'scaleY');
-
-      if (shouldConstrainValue) {
-        value = this._constrainScale(value);
-      }
-      if (key === 'scaleX' && value < 0) {
-        this.flipX = !this.flipX;
-        value *= -1;
-      }
-      else if (key === 'scaleY' && value < 0) {
-        this.flipY = !this.flipY;
-        value *= -1;
-      }
-      else if (key === 'width' || key === 'height') {
-        this.minScaleLimit = toFixed(Math.min(0.1, 1/Math.max(this.width, this.height)), 2);
-      }
-      else if (key === 'shadow' && value && !(value instanceof fabric.Shadow)) {
-        value = new fabric.Shadow(value);
-      }
-
-      this[key] = value;
-
-      return this;
-    },
-
-    /**
-     * 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;
-    },
-
-    /**
-     * Sets sourcePath of an object
-     * @param {String} value Value to set sourcePath to
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    setSourcePath: function(value) {
-      this.sourcePath = value;
-      return this;
-    },
-
-    /**
-     * Retrieves viewportTransform from Object's canvas if possible
-     * @method getViewportTransform
-     * @memberOf fabric.Object.prototype
-     * @return {Boolean} flipY value // TODO
-     */
-    getViewportTransform: function() {
-      if (this.canvas && this.canvas.viewportTransform) {
-        return this.canvas.viewportTransform;
-      }
-      return [1, 0, 0, 1, 0, 0];
-    },
-
-    /**
-     * Renders an object on a specified context
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    render: function(ctx, noTransform) {
-      // do not render if width/height are zeros or object is not visible
-      if (this.width === 0 || this.height === 0 || !this.visible) {
-        return;
-      }
-
-      ctx.save();
-
-      //setup fill rule for current object
-      this._setupFillRule(ctx);
-
-      this._transform(ctx, noTransform);
-      this._setStrokeStyles(ctx);
-      this._setFillStyles(ctx);
-
-      if (this.group && this.group.type === 'path-group') {
-        ctx.translate(-this.group.width/2, -this.group.height/2);
-        var m = this.transformMatrix;
-        if (m) {
-          ctx.transform.apply(ctx, m);
-        }
-      }
-      ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
-      this._setShadow(ctx);
-      this.clipTo && fabric.util.clipContext(this, ctx);
-      this._render(ctx, noTransform);
-      this.clipTo && ctx.restore();
-      this._removeShadow(ctx);
-      this._restoreFillRule(ctx);
-
-      ctx.restore();
-    },
-
-    _transform: function(ctx, noTransform) {
-      var m = this.transformMatrix;
-
-      if (m && !this.group) {
-        ctx.setTransform.apply(ctx, m);
-      }
-      if (!noTransform) {
-        this.transform(ctx);
-      }
-    },
-
-    _setStrokeStyles: function(ctx) {
-      if (this.stroke) {
-        ctx.lineWidth = this.strokeWidth;
-        ctx.lineCap = this.strokeLineCap;
-        ctx.lineJoin = this.strokeLineJoin;
-        ctx.miterLimit = this.strokeMiterLimit;
-        ctx.strokeStyle = this.stroke.toLive
-          ? this.stroke.toLive(ctx)
-          : this.stroke;
-      }
-    },
-
-    _setFillStyles: function(ctx) {
-      if (this.fill) {
-        ctx.fillStyle = this.fill.toLive
-          ? this.fill.toLive(ctx)
-          : this.fill;
-      }
-    },
-
-    /**
-     * Renders controls and borders for the object
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    _renderControls: function(ctx, noTransform) {
-      var vpt = this.getViewportTransform();
-
-      ctx.save();
-      if (this.active && !noTransform) {
-        var center;
-        if (this.group) {
-          center = fabric.util.transformPoint(this.group.getCenterPoint(), vpt);
-          ctx.translate(center.x, center.y);
-          ctx.rotate(degreesToRadians(this.group.angle));
-        }
-        center = fabric.util.transformPoint(this.getCenterPoint(), vpt, null != this.group);
-        if (this.group) {
-          center.x *= this.group.scaleX;
-          center.y *= this.group.scaleY;
-        }
-        ctx.translate(center.x, center.y);
-        ctx.rotate(degreesToRadians(this.angle));
-        this.drawBorders(ctx);
-        this.drawControls(ctx);
-      }
-      ctx.restore();
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _setShadow: function(ctx) {
-      if (!this.shadow) {
-        return;
-      }
-
-      ctx.shadowColor = this.shadow.color;
-      ctx.shadowBlur = this.shadow.blur;
-      ctx.shadowOffsetX = this.shadow.offsetX;
-      ctx.shadowOffsetY = this.shadow.offsetY;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _removeShadow: function(ctx) {
-      if (!this.shadow) {
-        return;
-      }
-
-      ctx.shadowColor = '';
-      ctx.shadowBlur = ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderFill: function(ctx) {
-      if (!this.fill) {
-        return;
-      }
-
-      ctx.save();
-      if (this.fill.toLive) {
-        ctx.translate(
-          -this.width / 2 + this.fill.offsetX || 0,
-          -this.height / 2 + this.fill.offsetY || 0);
-      }
-      if (this.fill.gradientTransform) {
-        var g = this.fill.gradientTransform;
-        ctx.transform.apply(ctx, g);
-      }
-      if (this.fillRule === 'destination-over') {
-        ctx.fill('evenodd');
-      }
-      else {
-        ctx.fill();
-      }
-      ctx.restore();
-      if (this.shadow && !this.shadow.affectStroke) {
-        this._removeShadow(ctx);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderStroke: function(ctx) {
-      if (!this.stroke || this.strokeWidth === 0) {
-        return;
-      }
-
-      ctx.save();
-      if (this.strokeDashArray) {
-        // Spec requires the concatenation of two copies the dash list when the number of elements is odd
-        if (1 & this.strokeDashArray.length) {
-          this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
-        }
-
-        if (supportsLineDash) {
-          ctx.setLineDash(this.strokeDashArray);
-          this._stroke && this._stroke(ctx);
-        }
-        else {
-          this._renderDashedStroke && this._renderDashedStroke(ctx);
-        }
-        ctx.stroke();
-      }
-      else {
-        if (this.stroke.gradientTransform) {
-          var g = this.stroke.gradientTransform;
-          ctx.transform.apply(ctx, g);
-        }
-        this._stroke ? this._stroke(ctx) : ctx.stroke();
-      }
-      this._removeShadow(ctx);
-      ctx.restore();
-    },
-
-    /**
-     * Clones an instance
-     * @param {Function} callback Callback is invoked with a clone as a first argument
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {fabric.Object} clone of an instance
-     */
-    clone: function(callback, propertiesToInclude) {
-      if (this.constructor.fromObject) {
-        return this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
-      }
-      return new fabric.Object(this.toObject(propertiesToInclude));
-    },
-
-    /**
-     * Creates an instance of fabric.Image out of an object
-     * @param {Function} callback callback, invoked with an instance as a first argument
-     * @return {fabric.Object} thisArg
-     */
-    cloneAsImage: function(callback) {
-      var dataUrl = this.toDataURL();
-      fabric.util.loadImage(dataUrl, function(img) {
-        if (callback) {
-          callback(new fabric.Image(img));
-        }
-      });
-      return this;
-    },
-
-    /**
-     * Converts an object into a data-url-like string
-     * @param {Object} options Options object
-     * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png"
-     * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg.
-     * @param {Number} [options.multiplier=1] Multiplier to scale by
-     * @param {Number} [options.left] Cropping left offset. Introduced in v1.2.14
-     * @param {Number} [options.top] Cropping top offset. Introduced in v1.2.14
-     * @param {Number} [options.width] Cropping width. Introduced in v1.2.14
-     * @param {Number} [options.height] Cropping height. Introduced in v1.2.14
-     * @return {String} Returns a data: URL containing a representation of the object in the format specified by options.format
-     */
-    toDataURL: function(options) {
-      options || (options = { });
-
-      var el = fabric.util.createCanvasElement(),
-          boundingRect = this.getBoundingRect();
-
-      el.width = boundingRect.width;
-      el.height = boundingRect.height;
-
-      fabric.util.wrapElement(el, 'div');
-      var canvas = new fabric.Canvas(el);
-
-      // to avoid common confusion https://github.com/kangax/fabric.js/issues/806
-      if (options.format === 'jpg') {
-        options.format = 'jpeg';
-      }
-
-      if (options.format === 'jpeg') {
-        canvas.backgroundColor = '#fff';
-      }
-
-      var origParams = {
-        active: this.get('active'),
-        left: this.getLeft(),
-        top: this.getTop()
-      };
-
-      this.set('active', false);
-      this.setPositionByOrigin(new fabric.Point(el.width / 2, el.height / 2), 'center', 'center');
-
-      var originalCanvas = this.canvas;
-      canvas.add(this);
-      var data = canvas.toDataURL(options);
-
-      this.set(origParams).setCoords();
-      this.canvas = originalCanvas;
-
-      canvas.dispose();
-      canvas = null;
-
-      return data;
-    },
-
-    /**
-     * Returns true if specified type is identical to the type of an instance
-     * @param {String} type Type to check against
-     * @return {Boolean}
-     */
-    isType: function(type) {
-      return this.type === type;
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return 0;
-    },
-
-    /**
-     * Returns a JSON representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} JSON
-     */
-    toJSON: function(propertiesToInclude) {
-      // delegate, not alias
-      return this.toObject(propertiesToInclude);
-    },
-
-    /**
-     * Sets gradient (fill or stroke) of an object
-     * <b>Backwards incompatibility note:</b> This method was named "setGradientFill" until v1.1.0
-     * @param {String} property Property name 'stroke' or 'fill'
-     * @param {Object} [options] Options object
-     * @param {String} [options.type] Type of gradient 'radial' or 'linear'
-     * @param {Number} [options.x1=0] x-coordinate of start point
-     * @param {Number} [options.y1=0] y-coordinate of start point
-     * @param {Number} [options.x2=0] x-coordinate of end point
-     * @param {Number} [options.y2=0] y-coordinate of end point
-     * @param {Number} [options.r1=0] Radius of start point (only for radial gradients)
-     * @param {Number} [options.r2=0] Radius of end point (only for radial gradients)
-     * @param {Object} [options.colorStops] Color stops object eg. {0: 'ff0000', 1: '000000'}
-     * @return {fabric.Object} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/58y8b/|jsFiddle demo}
-     * @example <caption>Set linear gradient</caption>
-     * object.setGradient('fill', {
-     *   type: 'linear',
-     *   x1: -object.width / 2,
-     *   y1: 0,
-     *   x2: object.width / 2,
-     *   y2: 0,
-     *   colorStops: {
-     *     0: 'red',
-     *     0.5: '#005555',
-     *     1: 'rgba(0,0,255,0.5)'
-     *   }
-     * });
-     * canvas.renderAll();
-     * @example <caption>Set radial gradient</caption>
-     * object.setGradient('fill', {
-     *   type: 'radial',
-     *   x1: 0,
-     *   y1: 0,
-     *   x2: 0,
-     *   y2: 0,
-     *   r1: object.width / 2,
-     *   r2: 10,
-     *   colorStops: {
-     *     0: 'red',
-     *     0.5: '#005555',
-     *     1: 'rgba(0,0,255,0.5)'
-     *   }
-     * });
-     * canvas.renderAll();
-     */
-    setGradient: function(property, options) {
-      options || (options = { });
-
-      var gradient = { colorStops: [] };
-
-      gradient.type = options.type || (options.r1 || options.r2 ? 'radial' : 'linear');
-      gradient.coords = {
-        x1: options.x1,
-        y1: options.y1,
-        x2: options.x2,
-        y2: options.y2
-      };
-
-      if (options.r1 || options.r2) {
-        gradient.coords.r1 = options.r1;
-        gradient.coords.r2 = options.r2;
-      }
-
-      for (var position in options.colorStops) {
-        var color = new fabric.Color(options.colorStops[position]);
-        gradient.colorStops.push({
-          offset: position,
-          color: color.toRgb(),
-          opacity: color.getAlpha()
-        });
-      }
-
-      return this.set(property, fabric.Gradient.forObject(this, gradient));
-    },
-
-    /**
-     * Sets pattern fill of an object
-     * @param {Object} options Options object
-     * @param {(String|HTMLImageElement)} options.source Pattern source
-     * @param {String} [options.repeat=repeat] Repeat property of a pattern (one of repeat, repeat-x, repeat-y or no-repeat)
-     * @param {Number} [options.offsetX=0] Pattern horizontal offset from object's left/top corner
-     * @param {Number} [options.offsetY=0] Pattern vertical offset from object's left/top corner
-     * @return {fabric.Object} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/QT3pa/|jsFiddle demo}
-     * @example <caption>Set pattern</caption>
-     * fabric.util.loadImage('http://fabricjs.com/assets/escheresque_ste.png', function(img) {
-     *   object.setPatternFill({
-     *     source: img,
-     *     repeat: 'repeat'
-     *   });
-     *   canvas.renderAll();
-     * });
-     */
-    setPatternFill: function(options) {
-      return this.set('fill', new fabric.Pattern(options));
-    },
-
-    /**
-     * Sets {@link fabric.Object#shadow|shadow} of an object
-     * @param {Object|String} [options] Options object or string (e.g. "2px 2px 10px rgba(0,0,0,0.2)")
-     * @param {String} [options.color=rgb(0,0,0)] Shadow color
-     * @param {Number} [options.blur=0] Shadow blur
-     * @param {Number} [options.offsetX=0] Shadow horizontal offset
-     * @param {Number} [options.offsetY=0] Shadow vertical offset
-     * @return {fabric.Object} thisArg
-     * @chainable
-     * @see {@link http://jsfiddle.net/fabricjs/7gvJG/|jsFiddle demo}
-     * @example <caption>Set shadow with string notation</caption>
-     * object.setShadow('2px 2px 10px rgba(0,0,0,0.2)');
-     * canvas.renderAll();
-     * @example <caption>Set shadow with object notation</caption>
-     * object.setShadow({
-     *   color: 'red',
-     *   blur: 10,
-     *   offsetX: 20,
-     *   offsetY: 20
-     * });
-     * canvas.renderAll();
-     */
-    setShadow: function(options) {
-      return this.set('shadow', options ? new fabric.Shadow(options) : null);
-    },
-
-    /**
-     * Sets "color" of an instance (alias of `set('fill', &hellip;)`)
-     * @param {String} color Color value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    setColor: function(color) {
-      this.set('fill', color);
-      return this;
-    },
-
-    /**
-     * Sets "angle" of an instance
-     * @param {Number} angle Angle value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    setAngle: function(angle) {
-      var shouldCenterOrigin = (this.originX !== 'center' || this.originY !== 'center') && this.centeredRotation;
-
-      if (shouldCenterOrigin) {
-        this._setOriginToCenter();
-      }
-
-      this.set('angle', angle);
-
-      if (shouldCenterOrigin) {
-        this._resetOrigin();
-      }
-
-      return this;
-    },
-
-    /**
-     * Centers object horizontally on canvas to which it was added last.
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    centerH: function () {
-      this.canvas.centerObjectH(this);
-      return this;
-    },
-
-    /**
-     * Centers object vertically on canvas to which it was added last.
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    centerV: function () {
-      this.canvas.centerObjectV(this);
-      return this;
-    },
-
-    /**
-     * Centers object vertically and horizontally on canvas to which is was added last
-     * You might need to call `setCoords` on an object after centering, to update controls area.
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    center: function () {
-      this.canvas.centerObject(this);
-      return this;
-    },
-
-    /**
-     * Removes object from canvas to which it was added last
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    remove: function() {
-      this.canvas.remove(this);
-      return this;
-    },
-
-    /**
-     * Returns coordinates of a pointer relative to an object
-     * @param {Event} e Event to operate upon
-     * @param {Object} [pointer] Pointer to operate upon (instead of event)
-     * @return {Object} Coordinates of a pointer (x, y)
-     */
-    getLocalPointer: function(e, pointer) {
-      pointer = pointer || this.canvas.getPointer(e);
-      var objectLeftTop = this.translateToOriginPoint(this.getCenterPoint(), 'left', 'top');
-      return {
-        x: pointer.x - objectLeftTop.x,
-        y: pointer.y - objectLeftTop.y
-      };
-    },
-
-    /**
-     * Sets canvas globalCompositeOperation for specific object
-     * custom composition operation for the particular object can be specifed using fillRule property
-     * @param {CanvasRenderingContext2D} ctx Rendering canvas context
-     */
-    _setupFillRule: function (ctx) {
-      if (this.fillRule) {
-        this._prevFillRule = ctx.globalCompositeOperation;
-        ctx.globalCompositeOperation = this.fillRule;
-      }
-    },
-
-    /**
-     * Restores previously saved canvas globalCompositeOperation after obeject rendering
-     * @param {CanvasRenderingContext2D} ctx Rendering canvas context
-     */
-    _restoreFillRule: function (ctx) {
-      if (this.fillRule && this._prevFillRule) {
-        ctx.globalCompositeOperation = this._prevFillRule;
-      }
-    }
-  });
-
-  fabric.util.createAccessors(fabric.Object);
-
-  /**
-   * Alias for {@link fabric.Object.prototype.setAngle}
-   * @alias rotate -> setAngle
-   * @memberof fabric.Object
-   */
-  fabric.Object.prototype.rotate = fabric.Object.prototype.setAngle;
-
-  extend(fabric.Object.prototype, fabric.Observable);
-
-  /**
-   * Defines the number of fraction digits to use when serializing object values.
-   * You can use it to increase/decrease precision of such values like left, top, scaleX, scaleY, etc.
-   * @static
-   * @memberof fabric.Object
-   * @constant
-   * @type Number
-   */
-  fabric.Object.NUM_FRACTION_DIGITS = 2;
-
-  /**
-   * Unique id used internally when creating SVG elements
-   * @static
-   * @memberof fabric.Object
-   * @type Number
-   */
-  fabric.Object.__uid = 0;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function() {
-
-  var degreesToRadians = fabric.util.degreesToRadians;
-
-  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
-
-    /**
-     * Translates the coordinates from origin to center coordinates (based on the object's dimensions)
-     * @param {fabric.Point} point The point which corresponds to the originX and originY params
-     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
-     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
-     * @return {fabric.Point}
-     */
-    translateToCenterPoint: function(point, originX, originY) {
-      var cx = point.x,
-          cy = point.y,
-          strokeWidth = this.stroke ? this.strokeWidth : 0;
-
-      if (originX === 'left') {
-        cx = point.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
-      }
-      else if (originX === 'right') {
-        cx = point.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
-      }
-
-      if (originY === 'top') {
-        cy = point.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
-      }
-      else if (originY === 'bottom') {
-        cy = point.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
-      }
-
-      // Apply the reverse rotation to the point (it's already scaled properly)
-      return fabric.util.rotatePoint(new fabric.Point(cx, cy), point, degreesToRadians(this.angle));
-    },
-
-    /**
-     * Translates the coordinates from center to origin coordinates (based on the object's dimensions)
-     * @param {fabric.Point} center The point which corresponds to center of the object
-     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
-     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
-     * @return {fabric.Point}
-     */
-    translateToOriginPoint: function(center, originX, originY) {
-      var x = center.x,
-          y = center.y,
-          strokeWidth = this.stroke ? this.strokeWidth : 0;
-
-      // Get the point coordinates
-      if (originX === 'left') {
-        x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
-      }
-      else if (originX === 'right') {
-        x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
-      }
-      if (originY === 'top') {
-        y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
-      }
-      else if (originY === 'bottom') {
-        y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
-      }
-
-      // Apply the rotation to the point (it's already scaled properly)
-      return fabric.util.rotatePoint(new fabric.Point(x, y), center, degreesToRadians(this.angle));
-    },
-
-    /**
-     * Returns the real center coordinates of the object
-     * @return {fabric.Point}
-     */
-    getCenterPoint: function() {
-      var leftTop = new fabric.Point(this.left, this.top);
-      return this.translateToCenterPoint(leftTop, this.originX, this.originY);
-    },
-
-    /**
-     * Returns the coordinates of the object based on center coordinates
-     * @param {fabric.Point} point The point which corresponds to the originX and originY params
-     * @return {fabric.Point}
-     */
-    // getOriginPoint: function(center) {
-    //   return this.translateToOriginPoint(center, this.originX, this.originY);
-    // },
-
-    /**
-     * Returns the coordinates of the object as if it has a different origin
-     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
-     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
-     * @return {fabric.Point}
-     */
-    getPointByOrigin: function(originX, originY) {
-      var center = this.getCenterPoint();
-      return this.translateToOriginPoint(center, originX, originY);
-    },
-
-    /**
-     * Returns the point in local coordinates
-     * @param {fabric.Point} point The point relative to the global coordinate system
-     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
-     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
-     * @return {fabric.Point}
-     */
-    toLocalPoint: function(point, originX, originY) {
-      var center = this.getCenterPoint(),
-          strokeWidth = this.stroke ? this.strokeWidth : 0,
-          x, y;
-
-      if (originX && originY) {
-        if (originX === 'left') {
-          x = center.x - (this.getWidth() + strokeWidth * this.scaleX) / 2;
-        }
-        else if (originX === 'right') {
-          x = center.x + (this.getWidth() + strokeWidth * this.scaleX) / 2;
-        }
-        else {
-          x = center.x;
-        }
-
-        if (originY === 'top') {
-          y = center.y - (this.getHeight() + strokeWidth * this.scaleY) / 2;
-        }
-        else if (originY === 'bottom') {
-          y = center.y + (this.getHeight() + strokeWidth * this.scaleY) / 2;
-        }
-        else {
-          y = center.y;
-        }
-      }
-      else {
-        x = this.left;
-        y = this.top;
-      }
-
-      return fabric.util.rotatePoint(new fabric.Point(point.x, point.y), center, -degreesToRadians(this.angle))
-        .subtractEquals(new fabric.Point(x, y));
-    },
-
-    /**
-     * Returns the point in global coordinates
-     * @param {fabric.Point} The point relative to the local coordinate system
-     * @return {fabric.Point}
-     */
-    // toGlobalPoint: function(point) {
-    //   return fabric.util.rotatePoint(point, this.getCenterPoint(), degreesToRadians(this.angle)).addEquals(new fabric.Point(this.left, this.top));
-    // },
-
-    /**
-     * Sets the position of the object taking into consideration the object's origin
-     * @param {fabric.Point} pos The new position of the object
-     * @param {String} originX Horizontal origin: 'left', 'center' or 'right'
-     * @param {String} originY Vertical origin: 'top', 'center' or 'bottom'
-     * @return {void}
-     */
-    setPositionByOrigin: function(pos, originX, originY) {
-      var center = this.translateToCenterPoint(pos, originX, originY),
-          position = this.translateToOriginPoint(center, this.originX, this.originY);
-
-      this.set('left', position.x);
-      this.set('top', position.y);
-    },
-
-    /**
-     * @param {String} to One of 'left', 'center', 'right'
-     */
-    adjustPosition: function(to) {
-      var angle = degreesToRadians(this.angle),
-          hypotHalf = this.getWidth() / 2,
-          xHalf = Math.cos(angle) * hypotHalf,
-          yHalf = Math.sin(angle) * hypotHalf,
-          hypotFull = this.getWidth(),
-          xFull = Math.cos(angle) * hypotFull,
-          yFull = Math.sin(angle) * hypotFull;
-
-      if (this.originX === 'center' && to === 'left' ||
-          this.originX === 'right' && to === 'center') {
-        // move half left
-        this.left -= xHalf;
-        this.top -= yHalf;
-      }
-      else if (this.originX === 'left' && to === 'center' ||
-               this.originX === 'center' && to === 'right') {
-        // move half right
-        this.left += xHalf;
-        this.top += yHalf;
-      }
-      else if (this.originX === 'left' && to === 'right') {
-        // move full right
-        this.left += xFull;
-        this.top += yFull;
-      }
-      else if (this.originX === 'right' && to === 'left') {
-        // move full left
-        this.left -= xFull;
-        this.top -= yFull;
-      }
-
-      this.setCoords();
-      this.originX = to;
-    },
-
-    /**
-     * Sets the origin/position of the object to it's center point
-     * @private
-     * @return {void}
-     */
-    _setOriginToCenter: function() {
-      this._originalOriginX = this.originX;
-      this._originalOriginY = this.originY;
-
-      var center = this.getCenterPoint();
-
-      this.originX = 'center';
-      this.originY = 'center';
-
-      this.left = center.x;
-      this.top = center.y;
-    },
-
-    /**
-     * Resets the origin/position of the object to it's original origin
-     * @private
-     * @return {void}
-     */
-    _resetOrigin: function() {
-      var originPoint = this.translateToOriginPoint(
-        this.getCenterPoint(),
-        this._originalOriginX,
-        this._originalOriginY);
-
-      this.originX = this._originalOriginX;
-      this.originY = this._originalOriginY;
-
-      this.left = originPoint.x;
-      this.top = originPoint.y;
-
-      this._originalOriginX = null;
-      this._originalOriginY = null;
-    },
-
-    /**
-     * @private
-     */
-    _getLeftTopCoords: function() {
-      return this.translateToOriginPoint(this.getCenterPoint(), 'left', 'center');
-    }
-  });
-
-})();
-
-
-(function() {
-
-  var degreesToRadians = fabric.util.degreesToRadians;
-
-  fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
-
-    /**
-     * Object containing coordinates of object's controls
-     * @type Object
-     * @default
-     */
-    oCoords: null,
-
-    /**
-     * Checks if object intersects with an area formed by 2 points
-     * @param {Object} pointTL top-left point of area
-     * @param {Object} pointBR bottom-right point of area
-     * @return {Boolean} true if object intersects with an area formed by 2 points
-     */
-    intersectsWithRect: function(pointTL, pointBR) {
-      var oCoords = this.oCoords,
-          tl = new fabric.Point(oCoords.tl.x, oCoords.tl.y),
-          tr = new fabric.Point(oCoords.tr.x, oCoords.tr.y),
-          bl = new fabric.Point(oCoords.bl.x, oCoords.bl.y),
-          br = new fabric.Point(oCoords.br.x, oCoords.br.y),
-          intersection = fabric.Intersection.intersectPolygonRectangle(
-            [tl, tr, br, bl],
-            pointTL,
-            pointBR
-          );
-      return intersection.status === 'Intersection';
-    },
-
-    /**
-     * Checks if object intersects with another object
-     * @param {Object} other Object to test
-     * @return {Boolean} true if object intersects with another object
-     */
-    intersectsWithObject: function(other) {
-      // extracts coords
-      function getCoords(oCoords) {
-        return {
-          tl: new fabric.Point(oCoords.tl.x, oCoords.tl.y),
-          tr: new fabric.Point(oCoords.tr.x, oCoords.tr.y),
-          bl: new fabric.Point(oCoords.bl.x, oCoords.bl.y),
-          br: new fabric.Point(oCoords.br.x, oCoords.br.y)
-        };
-      }
-      var thisCoords = getCoords(this.oCoords),
-          otherCoords = getCoords(other.oCoords),
-          intersection = fabric.Intersection.intersectPolygonPolygon(
-            [thisCoords.tl, thisCoords.tr, thisCoords.br, thisCoords.bl],
-            [otherCoords.tl, otherCoords.tr, otherCoords.br, otherCoords.bl]
-          );
-
-      return intersection.status === 'Intersection';
-    },
-
-    /**
-     * Checks if object is fully contained within area of another object
-     * @param {Object} other Object to test
-     * @return {Boolean} true if object is fully contained within area of another object
-     */
-    isContainedWithinObject: function(other) {
-      var boundingRect = other.getBoundingRect(),
-          point1 = new fabric.Point(boundingRect.left, boundingRect.top),
-          point2 = new fabric.Point(boundingRect.left + boundingRect.width, boundingRect.top + boundingRect.height);
-
-      return this.isContainedWithinRect(point1, point2);
-    },
-
-    /**
-     * Checks if object is fully contained within area formed by 2 points
-     * @param {Object} pointTL top-left point of area
-     * @param {Object} pointBR bottom-right point of area
-     * @return {Boolean} true if object is fully contained within area formed by 2 points
-     */
-    isContainedWithinRect: function(pointTL, pointBR) {
-      var boundingRect = this.getBoundingRect();
-
-      return (
-        boundingRect.left >= pointTL.x &&
-        boundingRect.left + boundingRect.width <= pointBR.x &&
-        boundingRect.top >= pointTL.y &&
-        boundingRect.top + boundingRect.height <= pointBR.y
-      );
-    },
-
-    /**
-     * Checks if point is inside the object
-     * @param {fabric.Point} point Point to check against
-     * @return {Boolean} true if point is inside the object
-     */
-    containsPoint: function(point) {
-      var lines = this._getImageLines(this.oCoords),
-          xPoints = this._findCrossPoints(point, lines);
-
-      // if xPoints is odd then point is inside the object
-      return (xPoints !== 0 && xPoints % 2 === 1);
-    },
-
-    /**
-     * Method that returns an object with the object edges in it, given the coordinates of the corners
-     * @private
-     * @param {Object} oCoords Coordinates of the object corners
-     */
-    _getImageLines: function(oCoords) {
-      return {
-        topline: {
-          o: oCoords.tl,
-          d: oCoords.tr
-        },
-        rightline: {
-          o: oCoords.tr,
-          d: oCoords.br
-        },
-        bottomline: {
-          o: oCoords.br,
-          d: oCoords.bl
-        },
-        leftline: {
-          o: oCoords.bl,
-          d: oCoords.tl
-        }
-      };
-    },
-
-    /**
-     * Helper method to determine how many cross points are between the 4 object edges
-     * and the horizontal line determined by a point on canvas
-     * @private
-     * @param {fabric.Point} point Point to check
-     * @param {Object} oCoords Coordinates of the object being evaluated
-     */
-    _findCrossPoints: function(point, oCoords) {
-      var b1, b2, a1, a2, xi, yi,
-          xcount = 0,
-          iLine;
-
-      for (var lineKey in oCoords) {
-        iLine = oCoords[lineKey];
-        // optimisation 1: line below point. no cross
-        if ((iLine.o.y < point.y) && (iLine.d.y < point.y)) {
-          continue;
-        }
-        // optimisation 2: line above point. no cross
-        if ((iLine.o.y >= point.y) && (iLine.d.y >= point.y)) {
-          continue;
-        }
-        // optimisation 3: vertical line case
-        if ((iLine.o.x === iLine.d.x) && (iLine.o.x >= point.x)) {
-          xi = iLine.o.x;
-          yi = point.y;
-        }
-        // calculate the intersection point
-        else {
-          b1 = 0;
-          b2 = (iLine.d.y - iLine.o.y) / (iLine.d.x - iLine.o.x);
-          a1 = point.y - b1 * point.x;
-          a2 = iLine.o.y - b2 * iLine.o.x;
-
-          xi = - (a1 - a2) / (b1 - b2);
-          yi = a1 + b1 * xi;
-        }
-        // dont count xi < point.x cases
-        if (xi >= point.x) {
-          xcount += 1;
-        }
-        // optimisation 4: specific for square images
-        if (xcount === 2) {
-          break;
-        }
-      }
-      return xcount;
-    },
-
-    /**
-     * Returns width of an object's bounding rectangle
-     * @deprecated since 1.0.4
-     * @return {Number} width value
-     */
-    getBoundingRectWidth: function() {
-      return this.getBoundingRect().width;
-    },
-
-    /**
-     * Returns height of an object's bounding rectangle
-     * @deprecated since 1.0.4
-     * @return {Number} height value
-     */
-    getBoundingRectHeight: function() {
-      return this.getBoundingRect().height;
-    },
-
-    /**
-     * Returns coordinates of object's bounding rectangle (left, top, width, height)
-     * @return {Object} Object with left, top, width, height properties
-     */
-    getBoundingRect: function() {
-      this.oCoords || this.setCoords();
-
-      var xCoords = [this.oCoords.tl.x, this.oCoords.tr.x, this.oCoords.br.x, this.oCoords.bl.x],
-          minX = fabric.util.array.min(xCoords),
-          maxX = fabric.util.array.max(xCoords),
-          width = Math.abs(minX - maxX),
-
-          yCoords = [this.oCoords.tl.y, this.oCoords.tr.y, this.oCoords.br.y, this.oCoords.bl.y],
-          minY = fabric.util.array.min(yCoords),
-          maxY = fabric.util.array.max(yCoords),
-          height = Math.abs(minY - maxY);
-
-      return {
-        left: minX,
-        top: minY,
-        width: width,
-        height: height
-      };
-    },
-
-    /**
-     * Returns width of an object
-     * @return {Number} width value
-     */
-    getWidth: function() {
-      return this.width * this.scaleX;
-    },
-
-    /**
-     * Returns height of an object
-     * @return {Number} height value
-     */
-    getHeight: function() {
-      return this.height * this.scaleY;
-    },
-
-    /**
-     * Makes sure the scale is valid and modifies it if necessary
-     * @private
-     * @param {Number} value
-     * @return {Number}
-     */
-    _constrainScale: function(value) {
-      if (Math.abs(value) < this.minScaleLimit) {
-        if (value < 0) {
-          return -this.minScaleLimit;
-        }
-        else {
-          return this.minScaleLimit;
-        }
-      }
-      return value;
-    },
-
-    /**
-     * Scales an object (equally by x and y)
-     * @param {Number} value Scale factor
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    scale: function(value) {
-      value = this._constrainScale(value);
-
-      if (value < 0) {
-        this.flipX = !this.flipX;
-        this.flipY = !this.flipY;
-        value *= -1;
-      }
-
-      this.scaleX = value;
-      this.scaleY = value;
-      this.setCoords();
-      return this;
-    },
-
-    /**
-     * Scales an object to a given width, with respect to bounding box (scaling by x/y equally)
-     * @param {Number} value New width value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    scaleToWidth: function(value) {
-      // adjust to bounding rect factor so that rotated shapes would fit as well
-      var boundingRectFactor = this.getBoundingRectWidth() / this.getWidth();
-      return this.scale(value / this.width / boundingRectFactor);
-    },
-
-    /**
-     * Scales an object to a given height, with respect to bounding box (scaling by x/y equally)
-     * @param {Number} value New height value
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    scaleToHeight: function(value) {
-      // adjust to bounding rect factor so that rotated shapes would fit as well
-      var boundingRectFactor = this.getBoundingRectHeight() / this.getHeight();
-      return this.scale(value / this.height / boundingRectFactor);
-    },
-
-    /**
-     * Sets corner position coordinates based on current angle, width and height
-     * @return {fabric.Object} thisArg
-     * @chainable
-     */
-    setCoords: function() {
-      var strokeWidth = this.strokeWidth > 1 ? this.strokeWidth : 0,
-          theta = degreesToRadians(this.angle),
-          vpt = this.getViewportTransform(),
-          f = function (p) {
-            return fabric.util.transformPoint(p, vpt);
-          },
-          w = this.width,
-          h = this.height,
-          capped = this.strokeLineCap === 'round' || this.strokeLineCap === 'square',
-          vLine = this.type === 'line' && this.width === 1,
-          hLine = this.type === 'line' && this.height === 1,
-          strokeW = (capped && hLine) || this.type !== 'line',
-          strokeH = (capped && vLine) || this.type !== 'line';
-
-      if (vLine) {
-        w = strokeWidth;
-      }
-      else if (hLine) {
-        h = strokeWidth;
-      }
-      if (strokeW) {
-        w += strokeWidth;
-      }
-      if (strokeH) {
-        h += strokeWidth;
-      }
-      this.currentWidth = w * this.scaleX;
-      this.currentHeight = h * this.scaleY;
-
-      // If width is negative, make postive. Fixes path selection issue
-      if (this.currentWidth < 0) {
-        this.currentWidth = Math.abs(this.currentWidth);
-      }
-
-      var _hypotenuse = Math.sqrt(
-            Math.pow(this.currentWidth / 2, 2) +
-            Math.pow(this.currentHeight / 2, 2)),
-
-          _angle = Math.atan(isFinite(this.currentHeight / this.currentWidth) ? this.currentHeight / this.currentWidth : 0),
-
-          // offset added for rotate and scale actions
-          offsetX = Math.cos(_angle + theta) * _hypotenuse,
-          offsetY = Math.sin(_angle + theta) * _hypotenuse,
-          sinTh = Math.sin(theta),
-          cosTh = Math.cos(theta),
-          coords = this.getCenterPoint(),
-          wh = new fabric.Point(this.currentWidth, this.currentHeight),
-          _tl =   new fabric.Point(coords.x - offsetX, coords.y - offsetY),
-          _tr =   new fabric.Point(_tl.x + (wh.x * cosTh),   _tl.y + (wh.x * sinTh)),
-          _bl =   new fabric.Point(_tl.x - (wh.y * sinTh),   _tl.y + (wh.y * cosTh)),
-          _mt =   new fabric.Point(_tl.x + (wh.x/2 * cosTh), _tl.y + (wh.x/2 * sinTh)),
-          tl  = f(_tl),
-          tr  = f(_tr),
-          br  = f(new fabric.Point(_tr.x - (wh.y * sinTh),   _tr.y + (wh.y * cosTh))),
-          bl  = f(_bl),
-          ml  = f(new fabric.Point(_tl.x - (wh.y/2 * sinTh), _tl.y + (wh.y/2 * cosTh))),
-          mt  = f(_mt),
-          mr  = f(new fabric.Point(_tr.x - (wh.y/2 * sinTh), _tr.y + (wh.y/2 * cosTh))),
-          mb  = f(new fabric.Point(_bl.x + (wh.x/2 * cosTh), _bl.y + (wh.x/2 * sinTh))),
-          mtr = f(new fabric.Point(_mt.x, _mt.y)),
-
-          // padding
-          padX = Math.cos(_angle + theta) * this.padding * Math.sqrt(2),
-          padY = Math.sin(_angle + theta) * this.padding * Math.sqrt(2);
-
-      tl = tl.add(new fabric.Point(-padX, -padY));
-      tr = tr.add(new fabric.Point(padY, -padX));
-      br = br.add(new fabric.Point(padX, padY));
-      bl = bl.add(new fabric.Point(-padY, padX));
-      ml = ml.add(new fabric.Point((-padX - padY) / 2, (-padY + padX) / 2));
-      mt = mt.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
-      mr = mr.add(new fabric.Point((padY + padX) / 2, (padY - padX) / 2));
-      mb = mb.add(new fabric.Point((padX - padY) / 2, (padX + padY) / 2));
-      mtr = mtr.add(new fabric.Point((padY - padX) / 2, -(padY + padX) / 2));
-
-      // debugging
-
-      // setTimeout(function() {
-      //   canvas.contextTop.fillStyle = 'green';
-      //   canvas.contextTop.fillRect(mb.x, mb.y, 3, 3);
-      //   canvas.contextTop.fillRect(bl.x, bl.y, 3, 3);
-      //   canvas.contextTop.fillRect(br.x, br.y, 3, 3);
-      //   canvas.contextTop.fillRect(tl.x, tl.y, 3, 3);
-      //   canvas.contextTop.fillRect(tr.x, tr.y, 3, 3);
-      //   canvas.contextTop.fillRect(ml.x, ml.y, 3, 3);
-      //   canvas.contextTop.fillRect(mr.x, mr.y, 3, 3);
-      //   canvas.contextTop.fillRect(mt.x, mt.y, 3, 3);
-      // }, 50);
-
-      this.oCoords = {
-        // corners
-        tl: tl, tr: tr, br: br, bl: bl,
-        // middle
-        ml: ml, mt: mt, mr: mr, mb: mb,
-        // rotating point
-        mtr: mtr
-      };
-
-      // set coordinates of the draggable boxes in the corners used to scale/rotate the image
-      this._setCornerCoords && this._setCornerCoords();
-
-      return this;
-    }
-  });
-})();
-
-
-fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
-
-  /**
-   * Moves an object to the bottom of the stack of drawn objects
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  sendToBack: function() {
-    if (this.group) {
-      fabric.StaticCanvas.prototype.sendToBack.call(this.group, this);
-    }
-    else {
-      this.canvas.sendToBack(this);
-    }
-    return this;
-  },
-
-  /**
-   * Moves an object to the top of the stack of drawn objects
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  bringToFront: function() {
-    if (this.group) {
-      fabric.StaticCanvas.prototype.bringToFront.call(this.group, this);
-    }
-    else {
-      this.canvas.bringToFront(this);
-    }
-    return this;
-  },
-
-  /**
-   * Moves an object down in stack of drawn objects
-   * @param {Boolean} [intersecting] If `true`, send object behind next lower intersecting object
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  sendBackwards: function(intersecting) {
-    if (this.group) {
-      fabric.StaticCanvas.prototype.sendBackwards.call(this.group, this, intersecting);
-    }
-    else {
-      this.canvas.sendBackwards(this, intersecting);
-    }
-    return this;
-  },
-
-  /**
-   * Moves an object up in stack of drawn objects
-   * @param {Boolean} [intersecting] If `true`, send object in front of next upper intersecting object
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  bringForward: function(intersecting) {
-    if (this.group) {
-      fabric.StaticCanvas.prototype.bringForward.call(this.group, this, intersecting);
-    }
-    else {
-      this.canvas.bringForward(this, intersecting);
-    }
-    return this;
-  },
-
-  /**
-   * Moves an object to specified level in stack of drawn objects
-   * @param {Number} index New position of object
-   * @return {fabric.Object} thisArg
-   * @chainable
-   */
-  moveTo: function(index) {
-    if (this.group) {
-      fabric.StaticCanvas.prototype.moveTo.call(this.group, this, index);
-    }
-    else {
-      this.canvas.moveTo(this, index);
-    }
-    return this;
-  }
-});
-
-
-/* _TO_SVG_START_ */
-fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
-
-  /**
-   * Returns styles-string for svg-export
-   * @return {String}
-   */
-  getSvgStyles: function() {
-
-    var fill = this.fill
-          ? (this.fill.toLive ? 'url(#SVGID_' + this.fill.id + ')' : this.fill)
-          : 'none',
-        fillRule = (this.fillRule === 'destination-over' ? 'evenodd' : this.fillRule),
-        stroke = this.stroke
-          ? (this.stroke.toLive ? 'url(#SVGID_' + this.stroke.id + ')' : this.stroke)
-          : 'none',
-
-        strokeWidth = this.strokeWidth ? this.strokeWidth : '0',
-        strokeDashArray = this.strokeDashArray ? this.strokeDashArray.join(' ') : '',
-        strokeLineCap = this.strokeLineCap ? this.strokeLineCap : 'butt',
-        strokeLineJoin = this.strokeLineJoin ? this.strokeLineJoin : 'miter',
-        strokeMiterLimit = this.strokeMiterLimit ? this.strokeMiterLimit : '4',
-        opacity = typeof this.opacity !== 'undefined' ? this.opacity : '1',
-
-        visibility = this.visible ? '' : ' visibility: hidden;',
-        filter = this.shadow && this.type !== 'text' ? 'filter: url(#SVGID_' + this.shadow.id + ');' : '';
-
-    return [
-      'stroke: ', stroke, '; ',
-      'stroke-width: ', strokeWidth, '; ',
-      'stroke-dasharray: ', strokeDashArray, '; ',
-      'stroke-linecap: ', strokeLineCap, '; ',
-      'stroke-linejoin: ', strokeLineJoin, '; ',
-      'stroke-miterlimit: ', strokeMiterLimit, '; ',
-      'fill: ', fill, '; ',
-      'fill-rule: ', fillRule, '; ',
-      'opacity: ', opacity, ';',
-      filter,
-      visibility
-    ].join('');
-  },
-
-  /**
-   * Returns transform-string for svg-export
-   * @return {String}
-   */
-  getSvgTransform: function() {
-    if (this.group) {
-      return '';
-    }
-    var toFixed = fabric.util.toFixed,
-        angle = this.getAngle(),
-        vpt = !this.canvas || this.canvas.svgViewportTransformation ? this.getViewportTransform() : [1, 0, 0, 1, 0, 0],
-        center = fabric.util.transformPoint(this.getCenterPoint(), vpt),
-
-        NUM_FRACTION_DIGITS = fabric.Object.NUM_FRACTION_DIGITS,
-
-        translatePart = this.type === 'path-group' ? '' : 'translate(' +
-                          toFixed(center.x, NUM_FRACTION_DIGITS) +
-                          ' ' +
-                          toFixed(center.y, NUM_FRACTION_DIGITS) +
-                        ')',
-
-        anglePart = angle !== 0
-          ? (' rotate(' + toFixed(angle, NUM_FRACTION_DIGITS) + ')')
-          : '',
-
-        scalePart = (this.scaleX === 1 && this.scaleY === 1 && vpt[0] === 1 && vpt[3] === 1)
-          ? '' :
-          (' scale(' +
-            toFixed(this.scaleX * vpt[0], NUM_FRACTION_DIGITS) +
-            ' ' +
-            toFixed(this.scaleY * vpt[3], NUM_FRACTION_DIGITS) +
-          ')'),
-
-        addTranslateX = this.type === 'path-group' ? this.width * vpt[0] : 0,
-
-        flipXPart = this.flipX ? ' matrix(-1 0 0 1 ' + addTranslateX + ' 0) ' : '',
-
-        addTranslateY = this.type === 'path-group' ? this.height * vpt[3] : 0,
-
-        flipYPart = this.flipY ? ' matrix(1 0 0 -1 0 ' + addTranslateY + ')' : '';
-
-    return [
-      translatePart, anglePart, scalePart, flipXPart, flipYPart
-    ].join('');
-  },
-
-  /**
-   * Returns transform-string for svg-export from the transform matrix of single elements
-   * @return {String}
-   */
-  getSvgTransformMatrix: function() {
-    return this.transformMatrix ? ' matrix(' + this.transformMatrix.join(' ') + ')' : '';
-  },
-
-  /**
-   * @private
-   */
-  _createBaseSVGMarkup: function() {
-    var markup = [ ];
-
-    if (this.fill && this.fill.toLive) {
-      markup.push(this.fill.toSVG(this, false));
-    }
-    if (this.stroke && this.stroke.toLive) {
-      markup.push(this.stroke.toSVG(this, false));
-    }
-    if (this.shadow) {
-      markup.push(this.shadow.toSVG(this));
-    }
-    return markup;
-  }
-});
-/* _TO_SVG_END_ */
-
-
-/*
-  Depends on `stateProperties`
-*/
-fabric.util.object.extend(fabric.Object.prototype, /** @lends fabric.Object.prototype */ {
-
-  /**
-   * Returns true if object state (one of its state properties) was changed
-   * @return {Boolean} true if instance' state has changed since `{@link fabric.Object#saveState}` was called
-   */
-  hasStateChanged: function() {
-    return this.stateProperties.some(function(prop) {
-      return this.get(prop) !== this.originalState[prop];
-    }, this);
-  },
-
-  /**
-   * Saves state of an object
-   * @param {Object} [options] Object with additional `stateProperties` array to include when saving state
-   * @return {fabric.Object} thisArg
-   */
-  saveState: function(options) {
-    this.stateProperties.forEach(function(prop) {
-      this.originalState[prop] = this.get(prop);
-    }, this);
-
-    if (options && options.stateProperties) {
-      options.stateProperties.forEach(function(prop) {
-        this.originalState[prop] = this.get(prop);
-      }, this);
-    }
-
-    return this;
-  },
-
-  /**
-   * Setups state of an object
-   * @return {fabric.Object} thisArg
-   */
-  setupState: function() {
-    this.originalState = { };
-    this.saveState();
-
-    return this;
-  }
-});
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      coordProps = { x1: 1, x2: 1, y1: 1, y2: 1 },
-      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
-
-  if (fabric.Line) {
-    fabric.warn('fabric.Line is already defined');
-    return;
-  }
-
-  /**
-   * Line class
-   * @class fabric.Line
-   * @extends fabric.Object
-   * @see {@link fabric.Line#initialize} for constructor definition
-   */
-  fabric.Line = fabric.util.createClass(fabric.Object, /** @lends fabric.Line.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'line',
-
-    /**
-     * x value or first line edge
-     * @type Number
-     * @default
-     */
-    x1: 0,
-
-    /**
-     * y value or first line edge
-     * @type Number
-     * @default
-     */
-    y1: 0,
-
-    /**
-     * x value or second line edge
-     * @type Number
-     * @default
-     */
-    x2: 0,
-
-    /**
-     * y value or second line edge
-     * @type Number
-     * @default
-     */
-    y2: 0,
-
-    /**
-     * Constructor
-     * @param {Array} [points] Array of points
-     * @param {Object} [options] Options object
-     * @return {fabric.Line} thisArg
-     */
-    initialize: function(points, options) {
-      options = options || { };
-
-      if (!points) {
-        points = [0, 0, 0, 0];
-      }
-
-      this.callSuper('initialize', options);
-
-      this.set('x1', points[0]);
-      this.set('y1', points[1]);
-      this.set('x2', points[2]);
-      this.set('y2', points[3]);
-
-      this._setWidthHeight(options);
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options
-     */
-    _setWidthHeight: function(options) {
-      options || (options = { });
-
-      this.width = Math.abs(this.x2 - this.x1) || 1;
-      this.height = Math.abs(this.y2 - this.y1) || 1;
-
-      this.left = 'left' in options
-        ? options.left
-        : this._getLeftToOriginX();
-
-      this.top = 'top' in options
-        ? options.top
-        : this._getTopToOriginY();
-    },
-
-    /**
-     * @private
-     * @param {String} key
-     * @param {Any} value
-     */
-    _set: function(key, value) {
-      this[key] = value;
-      if (typeof coordProps[key] !== 'undefined') {
-        this._setWidthHeight();
-      }
-      return this;
-    },
-
-    /**
-     * @private
-     * @return {Number} leftToOriginX Distance from left edge of canvas to originX of Line.
-     */
-    _getLeftToOriginX: makeEdgeToOriginGetter(
-      { // property names
-        origin: 'originX',
-        axis1: 'x1',
-        axis2: 'x2',
-        dimension: 'width'
-      },
-      { // possible values of origin
-        nearest: 'left',
-        center: 'center',
-        farthest: 'right'
-      }
-    ),
-
-    /**
-     * @private
-     * @return {Number} topToOriginY Distance from top edge of canvas to originY of Line.
-     */
-    _getTopToOriginY: makeEdgeToOriginGetter(
-      { // property names
-        origin: 'originY',
-        axis1: 'y1',
-        axis2: 'y2',
-        dimension: 'height'
-      },
-      { // possible values of origin
-        nearest: 'top',
-        center: 'center',
-        farthest: 'bottom'
-      }
-    ),
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx, noTransform) {
-      ctx.beginPath();
-
-      if (noTransform) {
-        //  Line coords are distances from left-top of canvas to origin of line.
-        //
-        //  To render line in a path-group, we need to translate them to
-        //  distances from center of path-group to center of line.
-        var cp = this.getCenterPoint();
-        ctx.translate(
-          cp.x,
-          cp.y
-        );
-      }
-
-      if (!this.strokeDashArray || this.strokeDashArray && supportsLineDash) {
-
-        // move from center (of virtual box) to its left/top corner
-        // we can't assume x1, y1 is top left and x2, y2 is bottom right
-        var xMult = this.x1 <= this.x2 ? -1 : 1,
-            yMult = this.y1 <= this.y2 ? -1 : 1;
-
-        ctx.moveTo(
-          this.width === 1 ? 0 : (xMult * this.width / 2),
-          this.height === 1 ? 0 : (yMult * this.height / 2));
-
-        ctx.lineTo(
-          this.width === 1 ? 0 : (xMult * -1 * this.width / 2),
-          this.height === 1 ? 0 : (yMult * -1 * this.height / 2));
-      }
-
-      ctx.lineWidth = this.strokeWidth;
-
-      // TODO: test this
-      // make sure setting "fill" changes color of a line
-      // (by copying fillStyle to strokeStyle, since line is stroked, not filled)
-      var origStrokeStyle = ctx.strokeStyle;
-      ctx.strokeStyle = this.stroke || ctx.fillStyle;
-      this.stroke && this._renderStroke(ctx);
-      ctx.strokeStyle = origStrokeStyle;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var
-        xMult = this.x1 <= this.x2 ? -1 : 1,
-        yMult = this.y1 <= this.y2 ? -1 : 1,
-        x = this.width === 1 ? 0 : xMult * this.width / 2,
-        y = this.height === 1 ? 0 : yMult * this.height / 2;
-
-      ctx.beginPath();
-      fabric.util.drawDashedLine(ctx, x, y, -x, -y, this.strokeDashArray);
-      ctx.closePath();
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @methd toObject
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        x1: this.get('x1'),
-        y1: this.get('y1'),
-        x2: this.get('x2'),
-        y2: this.get('y2')
-      });
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = this._createBaseSVGMarkup(), addTranslate = '';
-      if (!this.group) {
-        var x = - this.width / 2 - (this.x1 > this.x2 ? this.x2 : this.x1),
-            y = - this.height / 2 - (this.y1 > this.y2 ? this.y2 : this.y1);
-        addTranslate = 'translate(' + x + ', ' + y + ') ';
-      }
-      markup.push(
-        '<line ',
-          'x1="', this.x1,
-          '" y1="', this.y1,
-          '" x2="', this.x2,
-          '" y2="', this.y2,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(), addTranslate,
-          this.getSvgTransformMatrix(),
-        '"/>\n'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Line.fromElement})
-   * @static
-   * @memberOf fabric.Line
-   * @see http://www.w3.org/TR/SVG/shapes.html#LineElement
-   */
-  fabric.Line.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x1 y1 x2 y2'.split(' '));
-
-  /**
-   * Returns fabric.Line instance from an SVG element
-   * @static
-   * @memberOf fabric.Line
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Line} instance of fabric.Line
-   */
-  fabric.Line.fromElement = function(element, options) {
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Line.ATTRIBUTE_NAMES),
-        points = [
-          parsedAttributes.x1 || 0,
-          parsedAttributes.y1 || 0,
-          parsedAttributes.x2 || 0,
-          parsedAttributes.y2 || 0
-        ];
-    return new fabric.Line(points, extend(parsedAttributes, options));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns fabric.Line instance from an object representation
-   * @static
-   * @memberOf fabric.Line
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Line} instance of fabric.Line
-   */
-  fabric.Line.fromObject = function(object) {
-    var points = [object.x1, object.y1, object.x2, object.y2];
-    return new fabric.Line(points, object);
-  };
-
-  /**
-   * Produces a function that calculates distance from canvas edge to Line origin.
-   */
-  function makeEdgeToOriginGetter(propertyNames, originValues) {
-    var origin = propertyNames.origin,
-        axis1 = propertyNames.axis1,
-        axis2 = propertyNames.axis2,
-        dimension = propertyNames.dimension,
-        nearest = originValues.nearest,
-        center = originValues.center,
-        farthest = originValues.farthest;
-
-    return function() {
-      switch (this.get(origin)) {
-      case nearest:
-        return Math.min(this.get(axis1), this.get(axis2));
-      case center:
-        return Math.min(this.get(axis1), this.get(axis2)) + (0.5 * this.get(dimension));
-      case farthest:
-        return Math.max(this.get(axis1), this.get(axis2));
-      }
-    };
-
-  }
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      piBy2   = Math.PI * 2,
-      extend = fabric.util.object.extend;
-
-  if (fabric.Circle) {
-    fabric.warn('fabric.Circle is already defined.');
-    return;
-  }
-
-  /**
-   * Circle class
-   * @class fabric.Circle
-   * @extends fabric.Object
-   * @see {@link fabric.Circle#initialize} for constructor definition
-   */
-  fabric.Circle = fabric.util.createClass(fabric.Object, /** @lends fabric.Circle.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'circle',
-
-    /**
-     * Radius of this circle
-     * @type Number
-     * @default
-     */
-    radius: 0,
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object
-     * @return {fabric.Circle} thisArg
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.callSuper('initialize', options);
-      this.set('radius', options.radius || 0);
-    },
-
-    /**
-     * @private
-     * @param {String} key
-     * @param {Any} value
-     * @return {fabric.Circle} thisArg
-     */
-    _set: function(key, value) {
-      this.callSuper('_set', key, value);
-
-      if (key === 'radius') {
-        this.setRadius(value);
-      }
-
-      return this;
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        radius: this.get('radius')
-      });
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
-      if (this.group) {
-        x = this.left + this.radius;
-        y = this.top + this.radius;
-      }
-      markup.push(
-        '<circle ',
-          'cx="' + x + '" cy="' + y + '" ',
-          'r="', this.radius,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          ' ', this.getSvgTransformMatrix(),
-        '"/>\n'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx context to render on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    _render: function(ctx, noTransform) {
-      ctx.beginPath();
-      ctx.arc(noTransform ? this.left + this.radius : 0, noTransform ? this.top + this.radius : 0, this.radius, 0, piBy2, false);
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * Returns horizontal radius of an object (according to how an object is scaled)
-     * @return {Number}
-     */
-    getRadiusX: function() {
-      return this.get('radius') * this.get('scaleX');
-    },
-
-    /**
-     * Returns vertical radius of an object (according to how an object is scaled)
-     * @return {Number}
-     */
-    getRadiusY: function() {
-      return this.get('radius') * this.get('scaleY');
-    },
-
-    /**
-     * Sets radius of an object (and updates width accordingly)
-     * @return {Number}
-     */
-    setRadius: function(value) {
-      this.radius = value;
-      this.set('width', value * 2).set('height', value * 2);
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Circle.fromElement})
-   * @static
-   * @memberOf fabric.Circle
-   * @see: http://www.w3.org/TR/SVG/shapes.html#CircleElement
-   */
-  fabric.Circle.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy r'.split(' '));
-
-  /**
-   * Returns {@link fabric.Circle} instance from an SVG element
-   * @static
-   * @memberOf fabric.Circle
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @throws {Error} If value of `r` attribute is missing or invalid
-   * @return {fabric.Circle} Instance of fabric.Circle
-   */
-  fabric.Circle.fromElement = function(element, options) {
-    options || (options = { });
-
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Circle.ATTRIBUTE_NAMES);
-
-    if (!isValidRadius(parsedAttributes)) {
-      throw new Error('value of `r` attribute is required and can not be negative');
-    }
-
-    parsedAttributes.left = parsedAttributes.left || 0;
-    parsedAttributes.top = parsedAttributes.top || 0;
-
-    var obj = new fabric.Circle(extend(parsedAttributes, options));
-
-    obj.left -= obj.radius;
-    obj.top -= obj.radius;
-    return obj;
-  };
-
-  /**
-   * @private
-   */
-  function isValidRadius(attributes) {
-    return (('radius' in attributes) && (attributes.radius > 0));
-  }
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns {@link fabric.Circle} instance from an object representation
-   * @static
-   * @memberOf fabric.Circle
-   * @param {Object} object Object to create an instance from
-   * @return {Object} Instance of fabric.Circle
-   */
-  fabric.Circle.fromObject = function(object) {
-    return new fabric.Circle(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { });
-
-  if (fabric.Triangle) {
-    fabric.warn('fabric.Triangle is already defined');
-    return;
-  }
-
-  /**
-   * Triangle class
-   * @class fabric.Triangle
-   * @extends fabric.Object
-   * @return {fabric.Triangle} thisArg
-   * @see {@link fabric.Triangle#initialize} for constructor definition
-   */
-  fabric.Triangle = fabric.util.createClass(fabric.Object, /** @lends fabric.Triangle.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'triangle',
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object
-     * @return {Object} thisArg
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.callSuper('initialize', options);
-
-      this.set('width', options.width || 100)
-          .set('height', options.height || 100);
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx) {
-      var widthBy2 = this.width / 2,
-          heightBy2 = this.height / 2;
-
-      ctx.beginPath();
-      ctx.moveTo(-widthBy2, heightBy2);
-      ctx.lineTo(0, -heightBy2);
-      ctx.lineTo(widthBy2, heightBy2);
-      ctx.closePath();
-
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var widthBy2 = this.width / 2,
-          heightBy2 = this.height / 2;
-
-      ctx.beginPath();
-      fabric.util.drawDashedLine(ctx, -widthBy2, heightBy2, 0, -heightBy2, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, 0, -heightBy2, widthBy2, heightBy2, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, widthBy2, heightBy2, -widthBy2, heightBy2, this.strokeDashArray);
-      ctx.closePath();
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = this._createBaseSVGMarkup(),
-          widthBy2 = this.width / 2,
-          heightBy2 = this.height / 2,
-          points = [
-            -widthBy2 + ' ' + heightBy2,
-            '0 ' + -heightBy2,
-            widthBy2 + ' ' + heightBy2
-          ]
-          .join(',');
-
-      markup.push(
-        '<polygon ',
-          'points="', points,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-        '"/>'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /**
-   * Returns fabric.Triangle instance from an object representation
-   * @static
-   * @memberOf fabric.Triangle
-   * @param {Object} object Object to create an instance from
-   * @return {Object} instance of Canvas.Triangle
-   */
-  fabric.Triangle.fromObject = function(object) {
-    return new fabric.Triangle(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global){
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      piBy2   = Math.PI * 2,
-      extend = fabric.util.object.extend;
-
-  if (fabric.Ellipse) {
-    fabric.warn('fabric.Ellipse is already defined.');
-    return;
-  }
-
-  /**
-   * Ellipse class
-   * @class fabric.Ellipse
-   * @extends fabric.Object
-   * @return {fabric.Ellipse} thisArg
-   * @see {@link fabric.Ellipse#initialize} for constructor definition
-   */
-  fabric.Ellipse = fabric.util.createClass(fabric.Object, /** @lends fabric.Ellipse.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'ellipse',
-
-    /**
-     * Horizontal radius
-     * @type Number
-     * @default
-     */
-    rx:   0,
-
-    /**
-     * Vertical radius
-     * @type Number
-     * @default
-     */
-    ry:   0,
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object
-     * @return {fabric.Ellipse} thisArg
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.callSuper('initialize', options);
-
-      this.set('rx', options.rx || 0);
-      this.set('ry', options.ry || 0);
-
-      this.set('width', this.get('rx') * 2);
-      this.set('height', this.get('ry') * 2);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        rx: this.get('rx'),
-        ry: this.get('ry')
-      });
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = this._createBaseSVGMarkup(), x = 0, y = 0;
-      if (this.group) {
-        x = this.left + this.rx;
-        y = this.top + this.ry;
-      }
-      markup.push(
-        '<ellipse ',
-          'cx="', x, '" cy="', y, '" ',
-          'rx="', this.rx,
-          '" ry="', this.ry,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          this.getSvgTransformMatrix(),
-        '"/>\n'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx context to render on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    _render: function(ctx, noTransform) {
-      ctx.beginPath();
-      ctx.save();
-      ctx.transform(1, 0, 0, this.ry/this.rx, 0, 0);
-      ctx.arc(noTransform ? this.left + this.rx : 0, noTransform ? (this.top + this.ry) * this.rx/this.ry : 0, this.rx, 0, piBy2, false);
-      ctx.restore();
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Ellipse.fromElement})
-   * @static
-   * @memberOf fabric.Ellipse
-   * @see http://www.w3.org/TR/SVG/shapes.html#EllipseElement
-   */
-  fabric.Ellipse.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('cx cy rx ry'.split(' '));
-
-  /**
-   * Returns {@link fabric.Ellipse} instance from an SVG element
-   * @static
-   * @memberOf fabric.Ellipse
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Ellipse}
-   */
-  fabric.Ellipse.fromElement = function(element, options) {
-    options || (options = { });
-
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Ellipse.ATTRIBUTE_NAMES);
-
-    parsedAttributes.left = parsedAttributes.left || 0;
-    parsedAttributes.top = parsedAttributes.top || 0;
-
-    var ellipse = new fabric.Ellipse(extend(parsedAttributes, options));
-
-    ellipse.top -= ellipse.ry;
-    ellipse.left -= ellipse.rx;
-    return ellipse;
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns {@link fabric.Ellipse} instance from an object representation
-   * @static
-   * @memberOf fabric.Ellipse
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Ellipse}
-   */
-  fabric.Ellipse.fromObject = function(object) {
-    return new fabric.Ellipse(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  if (fabric.Rect) {
-    console.warn('fabric.Rect is already defined');
-    return;
-  }
-
-  var stateProperties = fabric.Object.prototype.stateProperties.concat();
-  stateProperties.push('rx', 'ry', 'x', 'y');
-
-  /**
-   * Rectangle class
-   * @class fabric.Rect
-   * @extends fabric.Object
-   * @return {fabric.Rect} thisArg
-   * @see {@link fabric.Rect#initialize} for constructor definition
-   */
-  fabric.Rect = fabric.util.createClass(fabric.Object, /** @lends fabric.Rect.prototype */ {
-
-    /**
-     * List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
-     * as well as for history (undo/redo) purposes
-     * @type Array
-     */
-    stateProperties: stateProperties,
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'rect',
-
-    /**
-     * Horizontal border radius
-     * @type Number
-     * @default
-     */
-    rx:   0,
-
-    /**
-     * Vertical border radius
-     * @type Number
-     * @default
-     */
-    ry:   0,
-
-    /**
-     * Used to specify dash pattern for stroke on this object
-     * @type Array
-     */
-    strokeDashArray: null,
-
-    /**
-     * Constructor
-     * @param {Object} [options] Options object
-     * @return {Object} thisArg
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.callSuper('initialize', options);
-      this._initRxRy();
-
-    },
-
-    /**
-     * Initializes rx/ry attributes
-     * @private
-     */
-    _initRxRy: function() {
-      if (this.rx && !this.ry) {
-        this.ry = this.rx;
-      }
-      else if (this.ry && !this.rx) {
-        this.rx = this.ry;
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx, noTransform) {
-
-      // optimize 1x1 case (used in spray brush)
-      if (this.width === 1 && this.height === 1) {
-        ctx.fillRect(0, 0, 1, 1);
-        return;
-      }
-
-      var rx = this.rx ? Math.min(this.rx, this.width / 2) : 0,
-          ry = this.ry ? Math.min(this.ry, this.height / 2) : 0,
-          w = this.width,
-          h = this.height,
-          x = noTransform ? this.left : -this.width / 2,
-          y = noTransform ? this.top : -this.height / 2,
-          isRounded = rx !== 0 || ry !== 0,
-          k = 1 - 0.5522847498 /* "magic number" for bezier approximations of arcs (http://itc.ktu.lt/itc354/Riskus354.pdf) */;
-
-      ctx.beginPath();
-
-      ctx.moveTo(x + rx, y);
-
-      ctx.lineTo(x + w - rx, y);
-      isRounded && ctx.bezierCurveTo(x + w - k * rx, y, x + w, y + k * ry, x + w, y + ry);
-
-      ctx.lineTo(x + w, y + h - ry);
-      isRounded && ctx.bezierCurveTo(x + w, y + h - k * ry, x + w - k * rx, y + h, x + w - rx, y + h);
-
-      ctx.lineTo(x + rx, y + h);
-      isRounded && ctx.bezierCurveTo(x + k * rx, y + h, x, y + h - k * ry, x, y + h - ry);
-
-      ctx.lineTo(x, y + ry);
-      isRounded && ctx.bezierCurveTo(x, y + k * ry, x + k * rx, y, x + rx, y);
-
-      ctx.closePath();
-
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var x = -this.width / 2,
-          y = -this.height / 2,
-          w = this.width,
-          h = this.height;
-
-      ctx.beginPath();
-      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
-      ctx.closePath();
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      var object = extend(this.callSuper('toObject', propertiesToInclude), {
-        rx: this.get('rx') || 0,
-        ry: this.get('ry') || 0
-      });
-      if (!this.includeDefaultValues) {
-        this._removeDefaultValues(object);
-      }
-      return object;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = this._createBaseSVGMarkup(), x = this.left, y = this.top;
-      if (!this.group) {
-        x = -this.width / 2;
-        y = -this.height / 2;
-      }
-      markup.push(
-        '<rect ',
-          'x="', x, '" y="', y,
-          '" rx="', this.get('rx'), '" ry="', this.get('ry'),
-          '" width="', this.width, '" height="', this.height,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          this.getSvgTransformMatrix(),
-        '"/>\n');
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by `fabric.Rect.fromElement`)
-   * @static
-   * @memberOf fabric.Rect
-   * @see: http://www.w3.org/TR/SVG/shapes.html#RectElement
-   */
-  fabric.Rect.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y rx ry width height'.split(' '));
-
-  /**
-   * Returns {@link fabric.Rect} instance from an SVG element
-   * @static
-   * @memberOf fabric.Rect
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Rect} Instance of fabric.Rect
-   */
-  fabric.Rect.fromElement = function(element, options) {
-    if (!element) {
-      return null;
-    }
-    options = options || { };
-
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Rect.ATTRIBUTE_NAMES);
-
-    parsedAttributes.left = parsedAttributes.left || 0;
-    parsedAttributes.top  = parsedAttributes.top  || 0;
-
-    return new fabric.Rect(extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns {@link fabric.Rect} instance from an object representation
-   * @static
-   * @memberOf fabric.Rect
-   * @param {Object} object Object to create an instance from
-   * @return {Object} instance of fabric.Rect
-   */
-  fabric.Rect.fromObject = function(object) {
-    return new fabric.Rect(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      toFixed = fabric.util.toFixed;
-
-  if (fabric.Polyline) {
-    fabric.warn('fabric.Polyline is already defined');
-    return;
-  }
-
-  /**
-   * Polyline class
-   * @class fabric.Polyline
-   * @extends fabric.Object
-   * @see {@link fabric.Polyline#initialize} for constructor definition
-   */
-  fabric.Polyline = fabric.util.createClass(fabric.Object, /** @lends fabric.Polyline.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'polyline',
-
-    /**
-     * Points array
-     * @type Array
-     * @default
-     */
-    points: null,
-
-    /**
-     * Constructor
-     * @param {Array} points Array of points (where each point is an object with x and y)
-     * @param {Object} [options] Options object
-     * @param {Boolean} [skipOffset] Whether points offsetting should be skipped
-     * @return {fabric.Polyline} thisArg
-     * @example
-     * var poly = new fabric.Polyline([
-     *     { x: 10, y: 10 },
-     *     { x: 50, y: 30 },
-     *     { x: 40, y: 70 },
-     *     { x: 60, y: 50 },
-     *     { x: 100, y: 150 },
-     *     { x: 40, y: 100 }
-     *   ], {
-     *   stroke: 'red',
-     *   left: 100,
-     *   top: 100
-     * });
-     */
-    initialize: function(points, options) {
-      options = options || { };
-      this.set('points', points);
-      this.callSuper('initialize', options);
-      this._calcDimensions();
-    },
-
-    /**
-     * @private
-     */
-    _calcDimensions: function() {
-      return fabric.Polygon.prototype._calcDimensions.call(this);
-    },
-
-    /**
-     * @private
-     */
-    _applyPointOffset: function() {
-      return fabric.Polygon.prototype._applyPointOffset.call(this);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return fabric.Polygon.prototype.toObject.call(this, propertiesToInclude);
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var points = [],
-          markup = this._createBaseSVGMarkup();
-
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
-      }
-
-      markup.push(
-        '<polyline ',
-          'points="', points.join(''),
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          ' ', this.getSvgTransformMatrix(),
-        '"/>\n'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx) {
-      var point;
-      ctx.beginPath();
-
-      if (this._applyPointOffset) {
-        if (!(this.group && this.group.type === 'path-group')) {
-          this._applyPointOffset();
-        }
-        this._applyPointOffset = null;
-      }
-
-      ctx.moveTo(this.points[0].x, this.points[0].y);
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        point = this.points[i];
-        ctx.lineTo(point.x, point.y);
-      }
-
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var p1, p2;
-
-      ctx.beginPath();
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        p1 = this.points[i];
-        p2 = this.points[i + 1] || p1;
-        fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
-      }
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return this.get('points').length;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Polyline.fromElement})
-   * @static
-   * @memberOf fabric.Polyline
-   * @see: http://www.w3.org/TR/SVG/shapes.html#PolylineElement
-   */
-  fabric.Polyline.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
-
-  /**
-   * Returns fabric.Polyline instance from an SVG element
-   * @static
-   * @memberOf fabric.Polyline
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Polyline} Instance of fabric.Polyline
-   */
-  fabric.Polyline.fromElement = function(element, options) {
-    if (!element) {
-      return null;
-    }
-    options || (options = { });
-
-    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
-        parsedAttributes = fabric.parseAttributes(element, fabric.Polyline.ATTRIBUTE_NAMES);
-
-    if (points === null) {
-      return null;
-    }
-
-    return new fabric.Polyline(points, fabric.util.object.extend(parsedAttributes, options));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns fabric.Polyline instance from an object representation
-   * @static
-   * @memberOf fabric.Polyline
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Polyline} Instance of fabric.Polyline
-   */
-  fabric.Polyline.fromObject = function(object) {
-    var points = object.points;
-    return new fabric.Polyline(points, object, true);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      min = fabric.util.array.min,
-      max = fabric.util.array.max,
-      toFixed = fabric.util.toFixed;
-
-  if (fabric.Polygon) {
-    fabric.warn('fabric.Polygon is already defined');
-    return;
-  }
-
-  /**
-   * Polygon class
-   * @class fabric.Polygon
-   * @extends fabric.Object
-   * @see {@link fabric.Polygon#initialize} for constructor definition
-   */
-  fabric.Polygon = fabric.util.createClass(fabric.Object, /** @lends fabric.Polygon.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'polygon',
-
-    /**
-     * Points array
-     * @type Array
-     * @default
-     */
-    points: null,
-
-    /**
-     * Constructor
-     * @param {Array} points Array of points
-     * @param {Object} [options] Options object
-     * @return {fabric.Polygon} thisArg
-     */
-    initialize: function(points, options) {
-      options = options || { };
-      this.points = points;
-      this.callSuper('initialize', options);
-      this._calcDimensions();
-    },
-
-    /**
-     * @private
-     */
-    _calcDimensions: function() {
-
-      var points = this.points,
-          minX = min(points, 'x'),
-          minY = min(points, 'y'),
-          maxX = max(points, 'x'),
-          maxY = max(points, 'y');
-
-      this.width = (maxX - minX) || 1;
-      this.height = (maxY - minY) || 1;
-
-      this.left = minX,
-      this.top = minY;
-    },
-
-    /**
-     * @private
-     */
-    _applyPointOffset: function() {
-      // change points to offset polygon into a bounding box
-      // executed one time
-      this.points.forEach(function(p) {
-        p.x -= (this.left + this.width / 2);
-        p.y -= (this.top + this.height / 2);
-      }, this);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        points: this.points.concat()
-      });
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var points = [],
-          markup = this._createBaseSVGMarkup();
-
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        points.push(toFixed(this.points[i].x, 2), ',', toFixed(this.points[i].y, 2), ' ');
-      }
-
-      markup.push(
-        '<polygon ',
-          'points="', points.join(''),
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          ' ', this.getSvgTransformMatrix(),
-        '"/>\n'
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx) {
-      var point;
-      ctx.beginPath();
-
-      if (this._applyPointOffset) {
-        if (!(this.group && this.group.type === 'path-group')) {
-          this._applyPointOffset();
-        }
-        this._applyPointOffset = null;
-      }
-
-      ctx.moveTo(this.points[0].x, this.points[0].y);
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        point = this.points[i];
-        ctx.lineTo(point.x, point.y);
-      }
-      this._renderFill(ctx);
-      if (this.stroke || this.strokeDashArray) {
-        ctx.closePath();
-        this._renderStroke(ctx);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var p1, p2;
-
-      ctx.beginPath();
-      for (var i = 0, len = this.points.length; i < len; i++) {
-        p1 = this.points[i];
-        p2 = this.points[i + 1] || this.points[0];
-        fabric.util.drawDashedLine(ctx, p1.x, p1.y, p2.x, p2.y, this.strokeDashArray);
-      }
-      ctx.closePath();
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return this.points.length;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by `fabric.Polygon.fromElement`)
-   * @static
-   * @memberOf fabric.Polygon
-   * @see: http://www.w3.org/TR/SVG/shapes.html#PolygonElement
-   */
-  fabric.Polygon.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat();
-
-  /**
-   * Returns {@link fabric.Polygon} instance from an SVG element
-   * @static
-   * @memberOf fabric.Polygon
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Polygon} Instance of fabric.Polygon
-   */
-  fabric.Polygon.fromElement = function(element, options) {
-    if (!element) {
-      return null;
-    }
-
-    options || (options = { });
-
-    var points = fabric.parsePointsAttribute(element.getAttribute('points')),
-        parsedAttributes = fabric.parseAttributes(element, fabric.Polygon.ATTRIBUTE_NAMES);
-
-    if (points === null) {
-      return null;
-    }
-
-    return new fabric.Polygon(points, extend(parsedAttributes, options));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns fabric.Polygon instance from an object representation
-   * @static
-   * @memberOf fabric.Polygon
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Polygon} Instance of fabric.Polygon
-   */
-  fabric.Polygon.fromObject = function(object) {
-    return new fabric.Polygon(object.points, object, true);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      min = fabric.util.array.min,
-      max = fabric.util.array.max,
-      extend = fabric.util.object.extend,
-      _toString = Object.prototype.toString,
-      drawArc = fabric.util.drawArc,
-      commandLengths = {
-        m: 2,
-        l: 2,
-        h: 1,
-        v: 1,
-        c: 6,
-        s: 4,
-        q: 4,
-        t: 2,
-        a: 7
-      },
-      repeatedCommands = {
-        m: 'l',
-        M: 'L'
-      };
-
-  if (fabric.Path) {
-    fabric.warn('fabric.Path is already defined');
-    return;
-  }
-
-  /**
-   * @private
-   */
-  function getX(item) {
-    if (item[0] === 'H') {
-      return item[1];
-    }
-    return item[item.length - 2];
-  }
-
-  /**
-   * @private
-   */
-  function getY(item) {
-    if (item[0] === 'V') {
-      return item[1];
-    }
-    return item[item.length - 1];
-  }
-
-  /**
-   * Path class
-   * @class fabric.Path
-   * @extends fabric.Object
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
-   * @see {@link fabric.Path#initialize} for constructor definition
-   */
-  fabric.Path = fabric.util.createClass(fabric.Object, /** @lends fabric.Path.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'path',
-
-    /**
-     * Array of path points
-     * @type Array
-     * @default
-     */
-    path: null,
-
-    /**
-     * Constructor
-     * @param {Array|String} path Path data (sequence of coordinates and corresponding "command" tokens)
-     * @param {Object} [options] Options object
-     * @return {fabric.Path} thisArg
-     */
-    initialize: function(path, options) {
-      options = options || { };
-
-      this.setOptions(options);
-
-      if (!path) {
-        throw new Error('`path` argument is required');
-      }
-
-      var fromArray = _toString.call(path) === '[object Array]';
-
-      this.path = fromArray
-        ? path
-        // one of commands (m,M,l,L,q,Q,c,C,etc.) followed by non-command characters (i.e. command values)
-        : path.match && path.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi);
-
-      if (!this.path) {
-        return;
-      }
-
-      if (!fromArray) {
-        this.path = this._parsePath();
-      }
-      this._initializePath(options);
-
-      if (options.sourcePath) {
-        this.setSourcePath(options.sourcePath);
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options object
-     */
-    _initializePath: function (options) {
-      var isWidthSet = 'width' in options && options.width != null,
-          isHeightSet = 'height' in options && options.width != null,
-          isLeftSet = 'left' in options,
-          isTopSet = 'top' in options,
-          origLeft = isLeftSet ? this.left : 0,
-          origTop = isTopSet ? this.top : 0;
-
-      if (!isWidthSet || !isHeightSet) {
-        extend(this, this._parseDimensions());
-        if (isWidthSet) {
-          this.width = options.width;
-        }
-        if (isHeightSet) {
-          this.height = options.height;
-        }
-      }
-      else { //Set center location relative to given height/width if not specified
-        if (!isTopSet) {
-          this.top = this.height / 2;
-        }
-        if (!isLeftSet) {
-          this.left = this.width / 2;
-        }
-      }
-      this.pathOffset = this.pathOffset ||
-                        // Save top-left coords as offset
-                        this._calculatePathOffset(origLeft, origTop);
-    },
-
-    /**
-     * @private
-     * @param {Number} origLeft Original left position
-     * @param {Number} origTop  Original top position
-     */
-    _calculatePathOffset: function (origLeft, origTop) {
-      return {
-        x: this.left - origLeft - (this.width / 2),
-        y: this.top - origTop - (this.height / 2)
-      };
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx context to render path on
-     */
-    _render: function(ctx, noTransform) {
-      var current, // current instruction
-          previous = null,
-          subpathStartX = 0,
-          subpathStartY = 0,
-          x = 0, // current x
-          y = 0, // current y
-          controlX = 0, // current control point x
-          controlY = 0, // current control point y
-          tempX,
-          tempY,
-          tempControlX,
-          tempControlY,
-          l = -((this.width / 2) + this.pathOffset.x),
-          t = -((this.height / 2) + this.pathOffset.y);
-
-      if (noTransform) {
-        l += this.width / 2;
-        t += this.height / 2;
-      }
-
-      for (var i = 0, len = this.path.length; i < len; ++i) {
-
-        current = this.path[i];
-
-        switch (current[0]) { // first letter
-
-          case 'l': // lineto, relative
-            x += current[1];
-            y += current[2];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'L': // lineto, absolute
-            x = current[1];
-            y = current[2];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'h': // horizontal lineto, relative
-            x += current[1];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'H': // horizontal lineto, absolute
-            x = current[1];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'v': // vertical lineto, relative
-            y += current[1];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'V': // verical lineto, absolute
-            y = current[1];
-            ctx.lineTo(x + l, y + t);
-            break;
-
-          case 'm': // moveTo, relative
-            x += current[1];
-            y += current[2];
-            subpathStartX = x;
-            subpathStartY = y;
-            ctx.moveTo(x + l, y + t);
-            break;
-
-          case 'M': // moveTo, absolute
-            x = current[1];
-            y = current[2];
-            subpathStartX = x;
-            subpathStartY = y;
-            ctx.moveTo(x + l, y + t);
-            break;
-
-          case 'c': // bezierCurveTo, relative
-            tempX = x + current[5];
-            tempY = y + current[6];
-            controlX = x + current[3];
-            controlY = y + current[4];
-            ctx.bezierCurveTo(
-              x + current[1] + l, // x1
-              y + current[2] + t, // y1
-              controlX + l, // x2
-              controlY + t, // y2
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-            break;
-
-          case 'C': // bezierCurveTo, absolute
-            x = current[5];
-            y = current[6];
-            controlX = current[3];
-            controlY = current[4];
-            ctx.bezierCurveTo(
-              current[1] + l,
-              current[2] + t,
-              controlX + l,
-              controlY + t,
-              x + l,
-              y + t
-            );
-            break;
-
-          case 's': // shorthand cubic bezierCurveTo, relative
-
-            // transform to absolute x,y
-            tempX = x + current[3];
-            tempY = y + current[4];
-
-            // calculate reflection of previous control points
-            controlX = controlX ? (2 * x - controlX) : x;
-            controlY = controlY ? (2 * y - controlY) : y;
-
-            ctx.bezierCurveTo(
-              controlX + l,
-              controlY + t,
-              x + current[1] + l,
-              y + current[2] + t,
-              tempX + l,
-              tempY + t
-            );
-            // set control point to 2nd one of this command
-            // "... the first control point is assumed to be
-            // the reflection of the second control point on
-            // the previous command relative to the current point."
-            controlX = x + current[1];
-            controlY = y + current[2];
-
-            x = tempX;
-            y = tempY;
-            break;
-
-          case 'S': // shorthand cubic bezierCurveTo, absolute
-            tempX = current[3];
-            tempY = current[4];
-            // calculate reflection of previous control points
-            controlX = 2 * x - controlX;
-            controlY = 2 * y - controlY;
-            ctx.bezierCurveTo(
-              controlX + l,
-              controlY + t,
-              current[1] + l,
-              current[2] + t,
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-
-            // set control point to 2nd one of this command
-            // "... the first control point is assumed to be
-            // the reflection of the second control point on
-            // the previous command relative to the current point."
-            controlX = current[1];
-            controlY = current[2];
-
-            break;
-
-          case 'q': // quadraticCurveTo, relative
-            // transform to absolute x,y
-            tempX = x + current[3];
-            tempY = y + current[4];
-
-            controlX = x + current[1];
-            controlY = y + current[2];
-
-            ctx.quadraticCurveTo(
-              controlX + l,
-              controlY + t,
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-            break;
-
-          case 'Q': // quadraticCurveTo, absolute
-            tempX = current[3];
-            tempY = current[4];
-
-            ctx.quadraticCurveTo(
-              current[1] + l,
-              current[2] + t,
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-            controlX = current[1];
-            controlY = current[2];
-            break;
-
-          case 't': // shorthand quadraticCurveTo, relative
-
-            // transform to absolute x,y
-            tempX = x + current[1];
-            tempY = y + current[2];
-
-            if (previous[0].match(/[QqTt]/) === null) {
-              // If there is no previous command or if the previous command was not a Q, q, T or t,
-              // assume the control point is coincident with the current point
-              controlX = x;
-              controlY = y;
-            }
-            else if (previous[0] === 't') {
-              // calculate reflection of previous control points for t
-              controlX = 2 * x - tempControlX;
-              controlY = 2 * y - tempControlY;
-            }
-            else if (previous[0] === 'q') {
-              // calculate reflection of previous control points for q
-              controlX = 2 * x - controlX;
-              controlY = 2 * y - controlY;
-            }
-
-            tempControlX = controlX;
-            tempControlY = controlY;
-
-            ctx.quadraticCurveTo(
-              controlX + l,
-              controlY + t,
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-            controlX = x + current[1];
-            controlY = y + current[2];
-            break;
-
-          case 'T':
-            tempX = current[1];
-            tempY = current[2];
-
-            // calculate reflection of previous control points
-            controlX = 2 * x - controlX;
-            controlY = 2 * y - controlY;
-            ctx.quadraticCurveTo(
-              controlX + l,
-              controlY + t,
-              tempX + l,
-              tempY + t
-            );
-            x = tempX;
-            y = tempY;
-            break;
-
-          case 'a':
-            // TODO: optimize this
-            drawArc(ctx, x + l, y + t, [
-              current[1],
-              current[2],
-              current[3],
-              current[4],
-              current[5],
-              current[6] + x + l,
-              current[7] + y + t
-            ]);
-            x += current[6];
-            y += current[7];
-            break;
-
-          case 'A':
-            // TODO: optimize this
-            drawArc(ctx, x + l, y + t, [
-              current[1],
-              current[2],
-              current[3],
-              current[4],
-              current[5],
-              current[6] + l,
-              current[7] + t
-            ]);
-            x = current[6];
-            y = current[7];
-            break;
-
-          case 'z':
-          case 'Z':
-            x = subpathStartX;
-            y = subpathStartY;
-            ctx.closePath();
-            break;
-        }
-        previous = current;
-      }
-    },
-
-    /**
-     * Renders path on a specified context
-     * @param {CanvasRenderingContext2D} ctx context to render path on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    render: function(ctx, noTransform) {
-      // do not render if object is not visible
-      if (!this.visible) {
-        return;
-      }
-
-      ctx.save();
-      if (noTransform) {
-        ctx.translate(-this.width/2, -this.height/2);
-      }
-      var m = this.transformMatrix;
-
-      if (m) {
-        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
-      }
-      if (!noTransform) {
-        this.transform(ctx);
-      }
-      this._setStrokeStyles(ctx);
-      this._setFillStyles(ctx);
-      this._setShadow(ctx);
-      this.clipTo && fabric.util.clipContext(this, ctx);
-      ctx.beginPath();
-      ctx.globalAlpha = this.group ? (ctx.globalAlpha * this.opacity) : this.opacity;
-      this._render(ctx, noTransform);
-      this._renderFill(ctx);
-      this._renderStroke(ctx);
-      this.clipTo && ctx.restore();
-      this._removeShadow(ctx);
-      ctx.restore();
-    },
-
-    /**
-     * Returns string representation of an instance
-     * @return {String} string representation of an instance
-     */
-    toString: function() {
-      return '#<fabric.Path (' + this.complexity() +
-        '): { "top": ' + this.top + ', "left": ' + this.left + ' }>';
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      var o = extend(this.callSuper('toObject', propertiesToInclude), {
-        path: this.path.map(function(item) { return item.slice() }),
-        pathOffset: this.pathOffset
-      });
-      if (this.sourcePath) {
-        o.sourcePath = this.sourcePath;
-      }
-      if (this.transformMatrix) {
-        o.transformMatrix = this.transformMatrix;
-      }
-      return o;
-    },
-
-    /**
-     * Returns dataless object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toDatalessObject: function(propertiesToInclude) {
-      var o = this.toObject(propertiesToInclude);
-      if (this.sourcePath) {
-        o.path = this.sourcePath;
-      }
-      delete o.sourcePath;
-      return o;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var chunks = [],
-          markup = this._createBaseSVGMarkup();
-
-      for (var i = 0, len = this.path.length; i < len; i++) {
-        chunks.push(this.path[i].join(' '));
-      }
-      var path = chunks.join(' ');
-
-      markup.push(
-        //jscs:disable validateIndentation
-        '<path ',
-          'd="', path,
-          '" style="', this.getSvgStyles(),
-          '" transform="', this.getSvgTransform(),
-          this.getSvgTransformMatrix(), '" stroke-linecap="round" ',
-        '/>\n'
-        //jscs:enable validateIndentation
-      );
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns number representation of an instance complexity
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return this.path.length;
-    },
-
-    /**
-     * @private
-     */
-    _parsePath: function() {
-      var result = [ ],
-          coords = [ ],
-          currentPath,
-          parsed,
-          re = /([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/ig,
-          match,
-          coordsStr;
-
-      for (var i = 0, coordsParsed, len = this.path.length; i < len; i++) {
-        currentPath = this.path[i];
-
-        coordsStr = currentPath.slice(1).trim();
-        coords.length = 0;
-
-        while ((match = re.exec(coordsStr))) {
-          coords.push(match[0]);
-        }
-
-        coordsParsed = [ currentPath.charAt(0) ];
-
-        for (var j = 0, jlen = coords.length; j < jlen; j++) {
-          parsed = parseFloat(coords[j]);
-          if (!isNaN(parsed)) {
-            coordsParsed.push(parsed);
-          }
-        }
-
-        var command = coordsParsed[0],
-            commandLength = commandLengths[command.toLowerCase()],
-            repeatedCommand = repeatedCommands[command] || command;
-
-        if (coordsParsed.length - 1 > commandLength) {
-          for (var k = 1, klen = coordsParsed.length; k < klen; k += commandLength) {
-            result.push([ command ].concat(coordsParsed.slice(k, k + commandLength)));
-            command = repeatedCommand;
-          }
-        }
-        else {
-          result.push(coordsParsed);
-        }
-      }
-
-      return result;
-    },
-
-    /**
-     * @private
-     */
-    _parseDimensions: function() {
-      var aX = [],
-          aY = [],
-          previous = { };
-
-      this.path.forEach(function(item, i) {
-        this._getCoordsFromCommand(item, i, aX, aY, previous);
-      }, this);
-
-      var minX = min(aX),
-          minY = min(aY),
-          maxX = max(aX),
-          maxY = max(aY),
-          deltaX = maxX - minX,
-          deltaY = maxY - minY,
-
-          o = {
-            left: this.left + (minX + deltaX / 2),
-            top: this.top + (minY + deltaY / 2),
-            width: deltaX,
-            height: deltaY
-          };
-
-      return o;
-    },
-
-    _getCoordsFromCommand: function(item, i, aX, aY, previous) {
-      var isLowerCase = false;
-
-      if (item[0] !== 'H') {
-        previous.x = (i === 0) ? getX(item) : getX(this.path[i - 1]);
-      }
-      if (item[0] !== 'V') {
-        previous.y = (i === 0) ? getY(item) : getY(this.path[i - 1]);
-      }
-
-      // lowercased letter denotes relative position;
-      // transform to absolute
-      if (item[0] === item[0].toLowerCase()) {
-        isLowerCase = true;
-      }
-
-      var xy = this._getXY(item, isLowerCase, previous),
-          val;
-
-      val = parseInt(xy.x, 10);
-      if (!isNaN(val)) {
-        aX.push(val);
-      }
-
-      val = parseInt(xy.y, 10);
-      if (!isNaN(val)) {
-        aY.push(val);
-      }
-    },
-
-    _getXY: function(item, isLowerCase, previous) {
-
-      // last 2 items in an array of coordinates are the actualy x/y (except H/V), collect them
-      // TODO (kangax): support relative h/v commands
-
-      var x = isLowerCase
-        ? previous.x + getX(item)
-        : item[0] === 'V'
-          ? previous.x
-          : getX(item),
-
-          y = isLowerCase
-            ? previous.y + getY(item)
-            : item[0] === 'H'
-              ? previous.y
-              : getY(item);
-
-      return { x: x, y: y };
-    }
-  });
-
-  /**
-   * Creates an instance of fabric.Path from an object
-   * @static
-   * @memberOf fabric.Path
-   * @param {Object} object
-   * @param {Function} callback Callback to invoke when an fabric.Path instance is created
-   */
-  fabric.Path.fromObject = function(object, callback) {
-    if (typeof object.path === 'string') {
-      fabric.loadSVGFromURL(object.path, function (elements) {
-        var path = elements[0],
-            pathUrl = object.path;
-
-        delete object.path;
-
-        fabric.util.object.extend(path, object);
-        path.setSourcePath(pathUrl);
-
-        callback(path);
-      });
-    }
-    else {
-      callback(new fabric.Path(object.path, object));
-    }
-  };
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by `fabric.Path.fromElement`)
-   * @static
-   * @memberOf fabric.Path
-   * @see http://www.w3.org/TR/SVG/paths.html#PathElement
-   */
-  fabric.Path.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(['d']);
-
-  /**
-   * Creates an instance of fabric.Path from an SVG <path> element
-   * @static
-   * @memberOf fabric.Path
-   * @param {SVGElement} element to parse
-   * @param {Function} callback Callback to invoke when an fabric.Path instance is created
-   * @param {Object} [options] Options object
-   */
-  fabric.Path.fromElement = function(element, callback, options) {
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Path.ATTRIBUTE_NAMES);
-    callback && callback(new fabric.Path(parsedAttributes.d, extend(parsedAttributes, options)));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Indicates that instances of this type are async
-   * @static
-   * @memberOf fabric.Path
-   * @type Boolean
-   * @default
-   */
-  fabric.Path.async = true;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      invoke = fabric.util.array.invoke,
-      parentToObject = fabric.Object.prototype.toObject;
-
-  if (fabric.PathGroup) {
-    fabric.warn('fabric.PathGroup is already defined');
-    return;
-  }
-
-  /**
-   * Path group class
-   * @class fabric.PathGroup
-   * @extends fabric.Path
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#path_and_pathgroup}
-   * @see {@link fabric.PathGroup#initialize} for constructor definition
-   */
-  fabric.PathGroup = fabric.util.createClass(fabric.Path, /** @lends fabric.PathGroup.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'path-group',
-
-    /**
-     * Fill value
-     * @type String
-     * @default
-     */
-    fill: '',
-
-    /**
-     * Constructor
-     * @param {Array} paths
-     * @param {Object} [options] Options object
-     * @return {fabric.PathGroup} thisArg
-     */
-    initialize: function(paths, options) {
-
-      options = options || { };
-      this.paths = paths || [ ];
-
-      for (var i = this.paths.length; i--; ) {
-        this.paths[i].group = this;
-      }
-
-      this.setOptions(options);
-
-      if (options.widthAttr) {
-        this.scaleX = options.widthAttr / options.width;
-      }
-      if (options.heightAttr) {
-        this.scaleY = options.heightAttr / options.height;
-      }
-
-      this.setCoords();
-
-      if (options.sourcePath) {
-        this.setSourcePath(options.sourcePath);
-      }
-    },
-
-    /**
-     * Renders this group on a specified context
-     * @param {CanvasRenderingContext2D} ctx Context to render this instance on
-     */
-    render: function(ctx) {
-      // do not render if object is not visible
-      if (!this.visible) {
-        return;
-      }
-
-      ctx.save();
-
-      var m = this.transformMatrix;
-
-      if (m) {
-        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
-      }
-      this.transform(ctx);
-
-      this._setShadow(ctx);
-      this.clipTo && fabric.util.clipContext(this, ctx);
-      for (var i = 0, l = this.paths.length; i < l; ++i) {
-        this.paths[i].render(ctx, true);
-      }
-      this.clipTo && ctx.restore();
-      this._removeShadow(ctx);
-      ctx.restore();
-    },
-
-    /**
-     * Sets certain property to a certain value
-     * @param {String} prop
-     * @param {Any} value
-     * @return {fabric.PathGroup} thisArg
-     */
-    _set: function(prop, value) {
-
-      if (prop === 'fill' && value && this.isSameColor()) {
-        var i = this.paths.length;
-        while (i--) {
-          this.paths[i]._set(prop, value);
-        }
-      }
-
-      return this.callSuper('_set', prop, value);
-    },
-
-    /**
-     * Returns object representation of this path group
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      var o = extend(parentToObject.call(this, propertiesToInclude), {
-        paths: invoke(this.getObjects(), 'toObject', propertiesToInclude)
-      });
-      if (this.sourcePath) {
-        o.sourcePath = this.sourcePath;
-      }
-      return o;
-    },
-
-    /**
-     * Returns dataless object representation of this path group
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} dataless object representation of an instance
-     */
-    toDatalessObject: function(propertiesToInclude) {
-      var o = this.toObject(propertiesToInclude);
-      if (this.sourcePath) {
-        o.paths = this.sourcePath;
-      }
-      return o;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var objects = this.getObjects(),
-          translatePart = 'translate(' + this.left + ' ' + this.top + ')',
-          markup = [
-            //jscs:disable validateIndentation
-            '<g ',
-              'style="', this.getSvgStyles(), '" ',
-              'transform="', translatePart, this.getSvgTransform(), '" ',
-            '>\n'
-            //jscs:enable validateIndentation
-          ];
-
-      for (var i = 0, len = objects.length; i < len; i++) {
-        markup.push(objects[i].toSVG(reviver));
-      }
-      markup.push('</g>\n');
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns a string representation of this path group
-     * @return {String} string representation of an object
-     */
-    toString: function() {
-      return '#<fabric.PathGroup (' + this.complexity() +
-        '): { top: ' + this.top + ', left: ' + this.left + ' }>';
-    },
-
-    /**
-     * Returns true if all paths in this group are of same color
-     * @return {Boolean} true if all paths are of the same color (`fill`)
-     */
-    isSameColor: function() {
-      var firstPathFill = (this.getObjects()[0].get('fill') || '').toLowerCase();
-      return this.getObjects().every(function(path) {
-        return (path.get('fill') || '').toLowerCase() === firstPathFill;
-      });
-    },
-
-    /**
-     * Returns number representation of object's complexity
-     * @return {Number} complexity
-     */
-    complexity: function() {
-      return this.paths.reduce(function(total, path) {
-        return total + ((path && path.complexity) ? path.complexity() : 0);
-      }, 0);
-    },
-
-    /**
-     * Returns all paths in this path group
-     * @return {Array} array of path objects included in this path group
-     */
-    getObjects: function() {
-      return this.paths;
-    }
-  });
-
-  /**
-   * Creates fabric.PathGroup instance from an object representation
-   * @static
-   * @memberOf fabric.PathGroup
-   * @param {Object} object Object to create an instance from
-   * @param {Function} callback Callback to invoke when an fabric.PathGroup instance is created
-   */
-  fabric.PathGroup.fromObject = function(object, callback) {
-    if (typeof object.paths === 'string') {
-      fabric.loadSVGFromURL(object.paths, function (elements) {
-
-        var pathUrl = object.paths;
-        delete object.paths;
-
-        var pathGroup = fabric.util.groupSVGElements(elements, object, pathUrl);
-
-        callback(pathGroup);
-      });
-    }
-    else {
-      fabric.util.enlivenObjects(object.paths, function(enlivenedObjects) {
-        delete object.paths;
-        callback(new fabric.PathGroup(enlivenedObjects, object));
-      });
-    }
-  };
-
-  /**
-   * Indicates that instances of this type are async
-   * @static
-   * @memberOf fabric.PathGroup
-   * @type Boolean
-   * @default
-   */
-  fabric.PathGroup.async = true;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global){
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      min = fabric.util.array.min,
-      max = fabric.util.array.max,
-      invoke = fabric.util.array.invoke;
-
-  if (fabric.Group) {
-    return;
-  }
-
-  // lock-related properties, for use in fabric.Group#get
-  // to enable locking behavior on group
-  // when one of its objects has lock-related properties set
-  var _lockProperties = {
-    lockMovementX:  true,
-    lockMovementY:  true,
-    lockRotation:   true,
-    lockScalingX:   true,
-    lockScalingY:   true,
-    lockUniScaling: true
-  };
-
-  /**
-   * Group class
-   * @class fabric.Group
-   * @extends fabric.Object
-   * @mixes fabric.Collection
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-3/#groups}
-   * @see {@link fabric.Group#initialize} for constructor definition
-   */
-  fabric.Group = fabric.util.createClass(fabric.Object, fabric.Collection, /** @lends fabric.Group.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'group',
-
-    /**
-     * Constructor
-     * @param {Object} objects Group objects
-     * @param {Object} [options] Options object
-     * @return {Object} thisArg
-     */
-    initialize: function(objects, options) {
-      options = options || { };
-
-      this._objects = objects || [];
-      for (var i = this._objects.length; i--; ) {
-        this._objects[i].group = this;
-      }
-
-      this.originalState = { };
-      this.callSuper('initialize');
-
-      this._calcBounds();
-      this._updateObjectsCoords();
-
-      if (options) {
-        extend(this, options);
-      }
-      this._setOpacityIfSame();
-
-      this.setCoords();
-      this.saveCoords();
-    },
-
-    /**
-     * @private
-     */
-    _updateObjectsCoords: function() {
-      this.forEachObject(this._updateObjectCoords, this);
-    },
-
-    /**
-     * @private
-     */
-    _updateObjectCoords: function(object) {
-      var objectLeft = object.getLeft(),
-          objectTop = object.getTop();
-
-      object.set({
-        originalLeft: objectLeft,
-        originalTop: objectTop,
-        left: objectLeft - this.left,
-        top: objectTop - this.top
-      });
-
-      object.setCoords();
-
-      // do not display corners of objects enclosed in a group
-      object.__origHasControls = object.hasControls;
-      object.hasControls = false;
-    },
-
-    /**
-     * Returns string represenation of a group
-     * @return {String}
-     */
-    toString: function() {
-      return '#<fabric.Group: (' + this.complexity() + ')>';
-    },
-
-    /**
-     * Adds an object to a group; Then recalculates group's dimension, position.
-     * @param {Object} object
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    addWithUpdate: function(object) {
-      this._restoreObjectsState();
-      if (object) {
-        this._objects.push(object);
-        object.group = this;
-      }
-      // since _restoreObjectsState set objects inactive
-      this.forEachObject(this._setObjectActive, this);
-      this._calcBounds();
-      this._updateObjectsCoords();
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _setObjectActive: function(object) {
-      object.set('active', true);
-      object.group = this;
-    },
-
-    /**
-     * Removes an object from a group; Then recalculates group's dimension, position.
-     * @param {Object} object
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    removeWithUpdate: function(object) {
-      this._moveFlippedObject(object);
-      this._restoreObjectsState();
-
-      // since _restoreObjectsState set objects inactive
-      this.forEachObject(this._setObjectActive, this);
-
-      this.remove(object);
-      this._calcBounds();
-      this._updateObjectsCoords();
-
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _onObjectAdded: function(object) {
-      object.group = this;
-    },
-
-    /**
-     * @private
-     */
-    _onObjectRemoved: function(object) {
-      delete object.group;
-      object.set('active', false);
-    },
-
-    /**
-     * Properties that are delegated to group objects when reading/writing
-     * @param {Object} delegatedProperties
-     */
-    delegatedProperties: {
-      fill:             true,
-      opacity:          true,
-      fontFamily:       true,
-      fontWeight:       true,
-      fontSize:         true,
-      fontStyle:        true,
-      lineHeight:       true,
-      textDecoration:   true,
-      textAlign:        true,
-      backgroundColor:  true
-    },
-
-    /**
-     * @private
-     */
-    _set: function(key, value) {
-      if (key in this.delegatedProperties) {
-        var i = this._objects.length;
-        this[key] = value;
-        while (i--) {
-          this._objects[i].set(key, value);
-        }
-      }
-      else {
-        this[key] = value;
-      }
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        objects: invoke(this._objects, 'toObject', propertiesToInclude)
-      });
-    },
-
-    /**
-     * Renders instance on a given context
-     * @param {CanvasRenderingContext2D} ctx context to render instance on
-     */
-    render: function(ctx) {
-      // do not render if object is not visible
-      if (!this.visible) {
-        return;
-      }
-
-      ctx.save();
-      this.clipTo && fabric.util.clipContext(this, ctx);
-
-      // the array is now sorted in order of highest first, so start from end
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        this._renderObject(this._objects[i], ctx);
-      }
-
-      this.clipTo && ctx.restore();
-
-      ctx.restore();
-    },
-
-    /**
-     * Renders controls and borders for the object
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Boolean} [noTransform] When true, context is not transformed
-     */
-    _renderControls: function(ctx, noTransform) {
-      this.callSuper('_renderControls', ctx, noTransform);
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        this._objects[i]._renderControls(ctx);
-      }
-    },
-
-    /**
-     * @private
-     */
-    _renderObject: function(object, ctx) {
-      var originalHasRotatingPoint = object.hasRotatingPoint;
-
-      // do not render if object is not visible
-      if (!object.visible) {
-        return;
-      }
-
-      object.hasRotatingPoint = false;
-
-      object.render(ctx);
-
-      object.hasRotatingPoint = originalHasRotatingPoint;
-    },
-
-    /**
-     * Retores original state of each of group objects (original state is that which was before group was created).
-     * @private
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    _restoreObjectsState: function() {
-      this._objects.forEach(this._restoreObjectState, this);
-      return this;
-    },
-
-    /**
-     * Moves a flipped object to the position where it's displayed
-     * @private
-     * @param {fabric.Object} object
-     * @return {fabric.Group} thisArg
-     */
-    _moveFlippedObject: function(object) {
-      var oldOriginX = object.get('originX'),
-          oldOriginY = object.get('originY'),
-          center = object.getCenterPoint();
-
-      object.set({
-        originX: 'center',
-        originY: 'center',
-        left: center.x,
-        top: center.y
-      });
-
-      this._toggleFlipping(object);
-
-      var newOrigin = object.getPointByOrigin(oldOriginX, oldOriginY);
-
-      object.set({
-        originX: oldOriginX,
-        originY: oldOriginY,
-        left: newOrigin.x,
-        top: newOrigin.y
-      });
-
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _toggleFlipping: function(object) {
-      if (this.flipX) {
-        object.toggle('flipX');
-        object.set('left', -object.get('left'));
-        object.setAngle(-object.getAngle());
-      }
-      if (this.flipY) {
-        object.toggle('flipY');
-        object.set('top', -object.get('top'));
-        object.setAngle(-object.getAngle());
-      }
-    },
-
-    /**
-     * Restores original state of a specified object in group
-     * @private
-     * @param {fabric.Object} object
-     * @return {fabric.Group} thisArg
-     */
-    _restoreObjectState: function(object) {
-      this._setObjectPosition(object);
-
-      object.setCoords();
-      object.hasControls = object.__origHasControls;
-      delete object.__origHasControls;
-      object.set('active', false);
-      object.setCoords();
-      delete object.group;
-
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _setObjectPosition: function(object) {
-      var groupLeft = this.getLeft(),
-          groupTop = this.getTop(),
-          rotated = this._getRotatedLeftTop(object);
-
-      object.set({
-        angle: object.getAngle() + this.getAngle(),
-        left: groupLeft + rotated.left,
-        top: groupTop + rotated.top,
-        scaleX: object.get('scaleX') * this.get('scaleX'),
-        scaleY: object.get('scaleY') * this.get('scaleY')
-      });
-    },
-
-    /**
-     * @private
-     */
-    _getRotatedLeftTop: function(object) {
-      var groupAngle = this.getAngle() * (Math.PI / 180);
-      return {
-        left: (-Math.sin(groupAngle) * object.getTop() * this.get('scaleY') +
-                Math.cos(groupAngle) * object.getLeft() * this.get('scaleX')),
-
-        top:  (Math.cos(groupAngle) * object.getTop() * this.get('scaleY') +
-               Math.sin(groupAngle) * object.getLeft() * this.get('scaleX'))
-      };
-    },
-
-    /**
-     * Destroys a group (restoring state of its objects)
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    destroy: function() {
-      this._objects.forEach(this._moveFlippedObject, this);
-      return this._restoreObjectsState();
-    },
-
-    /**
-     * Saves coordinates of this instance (to be used together with `hasMoved`)
-     * @saveCoords
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    saveCoords: function() {
-      this._originalLeft = this.get('left');
-      this._originalTop = this.get('top');
-      return this;
-    },
-
-    /**
-     * Checks whether this group was moved (since `saveCoords` was called last)
-     * @return {Boolean} true if an object was moved (since fabric.Group#saveCoords was called)
-     */
-    hasMoved: function() {
-      return this._originalLeft !== this.get('left') ||
-             this._originalTop !== this.get('top');
-    },
-
-    /**
-     * Sets coordinates of all group objects
-     * @return {fabric.Group} thisArg
-     * @chainable
-     */
-    setObjectsCoords: function() {
-      this.forEachObject(function(object) {
-        object.setCoords();
-      });
-      return this;
-    },
-
-    /**
-     * @private
-     */
-    _setOpacityIfSame: function() {
-      var objects = this.getObjects(),
-          firstValue = objects[0] ? objects[0].get('opacity') : 1,
-          isSameOpacity = objects.every(function(o) {
-            return o.get('opacity') === firstValue;
-          });
-
-      if (isSameOpacity) {
-        this.opacity = firstValue;
-      }
-    },
-
-    /**
-     * @private
-     */
-    _calcBounds: function(onlyWidthHeight) {
-      var aX = [],
-          aY = [],
-          o;
-
-      for (var i = 0, len = this._objects.length; i < len; ++i) {
-        o = this._objects[i];
-        o.setCoords();
-        for (var prop in o.oCoords) {
-          aX.push(o.oCoords[prop].x);
-          aY.push(o.oCoords[prop].y);
-        }
-      }
-
-      this.set(this._getBounds(aX, aY, onlyWidthHeight));
-    },
-
-    /**
-     * @private
-     */
-    _getBounds: function(aX, aY, onlyWidthHeight) {
-      var ivt = fabric.util.invertTransform(this.getViewportTransform()),
-          minXY = fabric.util.transformPoint(new fabric.Point(min(aX), min(aY)), ivt),
-          maxXY = fabric.util.transformPoint(new fabric.Point(max(aX), max(aY)), ivt),
-          obj = {
-            width: (maxXY.x - minXY.x) || 0,
-            height: (maxXY.y - minXY.y) || 0
-          };
-
-      if (!onlyWidthHeight) {
-        obj.left = (minXY.x + maxXY.x) / 2 || 0;
-        obj.top = (minXY.y + maxXY.y) / 2 || 0;
-      }
-      return obj;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns svg representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = [
-        //jscs:disable validateIndentation
-        '<g ',
-          'transform="', this.getSvgTransform(),
-        '">\n'
-        //jscs:enable validateIndentation
-      ];
-
-      for (var i = 0, len = this._objects.length; i < len; i++) {
-        markup.push(this._objects[i].toSVG(reviver));
-      }
-
-      markup.push('</g>\n');
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns requested property
-     * @param {String} prop Property to get
-     * @return {Any}
-     */
-    get: function(prop) {
-      if (prop in _lockProperties) {
-        if (this[prop]) {
-          return this[prop];
-        }
-        else {
-          for (var i = 0, len = this._objects.length; i < len; i++) {
-            if (this._objects[i][prop]) {
-              return true;
-            }
-          }
-          return false;
-        }
-      }
-      else {
-        if (prop in this.delegatedProperties) {
-          return this._objects[0] && this._objects[0].get(prop);
-        }
-        return this[prop];
-      }
-    }
-  });
-
-  /**
-   * Returns {@link fabric.Group} instance from an object representation
-   * @static
-   * @memberOf fabric.Group
-   * @param {Object} object Object to create a group from
-   * @param {Function} [callback] Callback to invoke when an group instance is created
-   * @return {fabric.Group} An instance of fabric.Group
-   */
-  fabric.Group.fromObject = function(object, callback) {
-    fabric.util.enlivenObjects(object.objects, function(enlivenedObjects) {
-      delete object.objects;
-      callback && callback(new fabric.Group(enlivenedObjects, object));
-    });
-  };
-
-  /**
-   * Indicates that instances of this type are async
-   * @static
-   * @memberOf fabric.Group
-   * @type Boolean
-   * @default
-   */
-  fabric.Group.async = true;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var extend = fabric.util.object.extend;
-
-  if (!global.fabric) {
-    global.fabric = { };
-  }
-
-  if (global.fabric.Image) {
-    fabric.warn('fabric.Image is already defined.');
-    return;
-  }
-
-  /**
-   * Image class
-   * @class fabric.Image
-   * @extends fabric.Object
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-1/#images}
-   * @see {@link fabric.Image#initialize} for constructor definition
-   */
-  fabric.Image = fabric.util.createClass(fabric.Object, /** @lends fabric.Image.prototype */ {
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type: 'image',
-
-    /**
-     * crossOrigin value (one of "", "anonymous", "allow-credentials")
-     * @see https://developer.mozilla.org/en-US/docs/HTML/CORS_settings_attributes
-     * @type String
-     * @default
-     */
-    crossOrigin: '',
-
-    /**
-     * Constructor
-     * @param {HTMLImageElement | String} element Image element
-     * @param {Object} [options] Options object
-     * @return {fabric.Image} thisArg
-     */
-    initialize: function(element, options) {
-      options || (options = { });
-
-      this.filters = [ ];
-
-      this.callSuper('initialize', options);
-
-      this._initElement(element, options);
-      this._initConfig(options);
-
-      if (options.filters) {
-        this.filters = options.filters;
-        this.applyFilters();
-      }
-    },
-
-    /**
-     * Returns image element which this instance if based on
-     * @return {HTMLImageElement} Image element
-     */
-    getElement: function() {
-      return this._element;
-    },
-
-    /**
-     * Sets image element for this instance to a specified one.
-     * If filters defined they are applied to new image.
-     * You might need to call `canvas.renderAll` and `object.setCoords` after replacing, to render new image and update controls area.
-     * @param {HTMLImageElement} element
-     * @param {Function} [callback] Callback is invoked when all filters have been applied and new image is generated
-     * @return {fabric.Image} thisArg
-     * @chainable
-     */
-    setElement: function(element, callback) {
-      this._element = element;
-      this._originalElement = element;
-      this._initConfig();
-
-      if (this.filters.length !== 0) {
-        this.applyFilters(callback);
-      }
-
-      return this;
-    },
-
-    /**
-     * Sets crossOrigin value (on an instance and corresponding image element)
-     * @return {fabric.Image} thisArg
-     * @chainable
-     */
-    setCrossOrigin: function(value) {
-      this.crossOrigin = value;
-      this._element.crossOrigin = value;
-
-      return this;
-    },
-
-    /**
-     * Returns original size of an image
-     * @return {Object} Object with "width" and "height" properties
-     */
-    getOriginalSize: function() {
-      var element = this.getElement();
-      return {
-        width: element.width,
-        height: element.height
-      };
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _stroke: function(ctx) {
-      ctx.save();
-      this._setStrokeStyles(ctx);
-      ctx.beginPath();
-      ctx.strokeRect(-this.width / 2, -this.height / 2, this.width, this.height);
-      ctx.closePath();
-      ctx.restore();
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderDashedStroke: function(ctx) {
-      var x = -this.width / 2,
-          y = -this.height / 2,
-          w = this.width,
-          h = this.height;
-
-      ctx.save();
-      this._setStrokeStyles(ctx);
-
-      ctx.beginPath();
-      fabric.util.drawDashedLine(ctx, x, y, x + w, y, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x + w, y, x + w, y + h, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x + w, y + h, x, y + h, this.strokeDashArray);
-      fabric.util.drawDashedLine(ctx, x, y + h, x, y, this.strokeDashArray);
-      ctx.closePath();
-      ctx.restore();
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      return extend(this.callSuper('toObject', propertiesToInclude), {
-        src: this._originalElement.src || this._originalElement._src,
-        filters: this.filters.map(function(filterObj) {
-          return filterObj && filterObj.toObject();
-        }),
-        crossOrigin: this.crossOrigin
-      });
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = [], x = -this.width / 2, y = -this.height / 2;
-      if (this.group) {
-        x = this.left;
-        y = this.top;
-      }
-      markup.push(
-        '<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
-          '<image xlink:href="', this.getSvgSrc(),
-            '" x="', x, '" y="', y,
-            '" style="', this.getSvgStyles(),
-            // we're essentially moving origin of transformation from top/left corner to the center of the shape
-            // by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
-            // so that object's center aligns with container's left/top
-            '" width="', this.width,
-            '" height="', this.height,
-            '" preserveAspectRatio="none"',
-          '></image>\n'
-      );
-
-      if (this.stroke || this.strokeDashArray) {
-        var origFill = this.fill;
-        this.fill = null;
-        markup.push(
-          '<rect ',
-            'x="', x, '" y="', y,
-            '" width="', this.width, '" height="', this.height,
-            '" style="', this.getSvgStyles(),
-          '"/>\n'
-        );
-        this.fill = origFill;
-      }
-
-      markup.push('</g>\n');
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Returns source of an image
-     * @return {String} Source of an image
-     */
-    getSrc: function() {
-      if (this.getElement()) {
-        return this.getElement().src || this.getElement()._src;
-      }
-    },
-
-    /**
-     * Returns string representation of an instance
-     * @return {String} String representation of an instance
-     */
-    toString: function() {
-      return '#<fabric.Image: { src: "' + this.getSrc() + '" }>';
-    },
-
-    /**
-     * Returns a clone of an instance
-     * @param {Function} callback Callback is invoked with a clone as a first argument
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     */
-    clone: function(callback, propertiesToInclude) {
-      this.constructor.fromObject(this.toObject(propertiesToInclude), callback);
-    },
-
-    /**
-     * Applies filters assigned to this image (from "filters" array)
-     * @mthod applyFilters
-     * @param {Function} callback Callback is invoked when all filters have been applied and new image is generated
-     * @return {fabric.Image} thisArg
-     * @chainable
-     */
-    applyFilters: function(callback) {
-
-      if (!this._originalElement) {
-        return;
-      }
-
-      if (this.filters.length === 0) {
-        this._element = this._originalElement;
-        callback && callback();
-        return;
-      }
-
-      var imgEl = this._originalElement,
-          canvasEl = fabric.util.createCanvasElement(),
-          replacement = fabric.util.createImage(),
-          _this = this;
-
-      canvasEl.width = imgEl.width;
-      canvasEl.height = imgEl.height;
-
-      canvasEl.getContext('2d').drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
-
-      this.filters.forEach(function(filter) {
-        filter && filter.applyTo(canvasEl);
-      });
-
-       /** @ignore */
-
-      replacement.width = imgEl.width;
-      replacement.height = imgEl.height;
-
-      if (fabric.isLikelyNode) {
-        replacement.src = canvasEl.toBuffer(undefined, fabric.Image.pngCompression);
-
-        // onload doesn't fire in some node versions, so we invoke callback manually
-        _this._element = replacement;
-        callback && callback();
-      }
-      else {
-        replacement.onload = function() {
-          _this._element = replacement;
-          callback && callback();
-          replacement.onload = canvasEl = imgEl = null;
-        };
-        replacement.src = canvasEl.toDataURL('image/png');
-      }
-
-      return this;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx, noTransform) {
-      this._element &&
-      ctx.drawImage(
-        this._element,
-        noTransform ? this.left : -this.width/2,
-        noTransform ? this.top : -this.height/2,
-        this.width,
-        this.height
-      );
-      this._renderStroke(ctx);
-    },
-
-    /**
-     * @private
-     */
-    _resetWidthHeight: function() {
-      var element = this.getElement();
-
-      this.set('width', element.width);
-      this.set('height', element.height);
-    },
-
-    /**
-     * The Image class's initialization method. This method is automatically
-     * called by the constructor.
-     * @private
-     * @param {HTMLImageElement|String} element The element representing the image
-     */
-    _initElement: function(element) {
-      this.setElement(fabric.util.getById(element));
-      fabric.util.addClass(this.getElement(), fabric.Image.CSS_CANVAS);
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Options object
-     */
-    _initConfig: function(options) {
-      options || (options = { });
-      this.setOptions(options);
-      this._setWidthHeight(options);
-      if (this._element && this.crossOrigin) {
-        this._element.crossOrigin = this.crossOrigin;
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} object Object with filters property
-     * @param {Function} callback Callback to invoke when all fabric.Image.filters instances are created
-     */
-    _initFilters: function(object, callback) {
-      if (object.filters && object.filters.length) {
-        fabric.util.enlivenObjects(object.filters, function(enlivenedObjects) {
-          callback && callback(enlivenedObjects);
-        }, 'fabric.Image.filters');
-      }
-      else {
-        callback && callback();
-      }
-    },
-
-    /**
-     * @private
-     * @param {Object} [options] Object with width/height properties
-     */
-    _setWidthHeight: function(options) {
-      this.width = 'width' in options
-        ? options.width
-        : (this.getElement()
-            ? this.getElement().width || 0
-            : 0);
-
-      this.height = 'height' in options
-        ? options.height
-        : (this.getElement()
-            ? this.getElement().height || 0
-            : 0);
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity of this instance
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /**
-   * Default CSS class name for canvas
-   * @static
-   * @type String
-   * @default
-   */
-  fabric.Image.CSS_CANVAS = 'canvas-img';
-
-  /**
-   * Alias for getSrc
-   * @static
-   */
-  fabric.Image.prototype.getSvgSrc = fabric.Image.prototype.getSrc;
-
-  /**
-   * Creates an instance of fabric.Image from its object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @param {Function} [callback] Callback to invoke when an image instance is created
-   */
-  fabric.Image.fromObject = function(object, callback) {
-    fabric.util.loadImage(object.src, function(img) {
-      fabric.Image.prototype._initFilters.call(object, object, function(filters) {
-        object.filters = filters || [ ];
-        var instance = new fabric.Image(img, object);
-        callback && callback(instance);
-      });
-    }, null, object.crossOrigin);
-  };
-
-  /**
-   * Creates an instance of fabric.Image from an URL string
-   * @static
-   * @param {String} url URL to create an image from
-   * @param {Function} [callback] Callback to invoke when image is created (newly created image is passed as a first argument)
-   * @param {Object} [imgOptions] Options object
-   */
-  fabric.Image.fromURL = function(url, callback, imgOptions) {
-    fabric.util.loadImage(url, function(img) {
-      callback(new fabric.Image(img, imgOptions));
-    }, null, imgOptions && imgOptions.crossOrigin);
-  };
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Image.fromElement})
-   * @static
-   * @see {@link http://www.w3.org/TR/SVG/struct.html#ImageElement}
-   */
-  fabric.Image.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat('x y width height xlink:href'.split(' '));
-
-  /**
-   * Returns {@link fabric.Image} instance from an SVG element
-   * @static
-   * @param {SVGElement} element Element to parse
-   * @param {Function} callback Callback to execute when fabric.Image object is created
-   * @param {Object} [options] Options object
-   * @return {fabric.Image} Instance of fabric.Image
-   */
-  fabric.Image.fromElement = function(element, callback, options) {
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Image.ATTRIBUTE_NAMES);
-
-    fabric.Image.fromURL(parsedAttributes['xlink:href'], callback,
-      extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes));
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Indicates that instances of this type are async
-   * @static
-   * @type Boolean
-   * @default
-   */
-  fabric.Image.async = true;
-
-  /**
-   * Indicates compression level used when generating PNG under Node (in applyFilters). Any of 0-9
-   * @static
-   * @type Number
-   * @default
-   */
-  fabric.Image.pngCompression = 1;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-/**
- * @namespace fabric.Image.filters
- * @memberOf fabric.Image
- * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#image_filters}
- * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
- */
-fabric.Image.filters = fabric.Image.filters || { };
-
-/**
- * Root filter class from which all filter classes inherit from
- * @class fabric.Image.filters.BaseFilter
- * @memberOf fabric.Image.filters
- */
-fabric.Image.filters.BaseFilter = fabric.util.createClass(/** @lends fabric.Image.filters.BaseFilter.prototype */ {
-
-  /**
-   * Filter type
-   * @param {String} type
-   * @default
-   */
-  type: 'BaseFilter',
-
-  /**
-   * Returns object representation of an instance
-   * @return {Object} Object representation of an instance
-   */
-  toObject: function() {
-    return { type: this.type };
-  },
-
-  /**
-   * Returns a JSON representation of an instance
-   * @return {Object} JSON
-   */
-  toJSON: function() {
-    // delegate, not alias
-    return this.toObject();
-  }
-});
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Brightness filter class
-   * @class fabric.Image.filters.Brightness
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Brightness#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Brightness({
-   *   brightness: 200
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Brightness = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Brightness.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Brightness',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Brightness.prototype
-     * @param {Object} [options] Options object
-     * @param {Number} [options.brightness=0] Value to brighten the image up (0..255)
-     */
-    initialize: function(options) {
-      options = options || { };
-      this.brightness = options.brightness || 0;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          brightness = this.brightness;
-
-      for (var i = 0, len = data.length; i < len; i += 4) {
-        data[i] += brightness;
-        data[i + 1] += brightness;
-        data[i + 2] += brightness;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        brightness: this.brightness
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Brightness} Instance of fabric.Image.filters.Brightness
-   */
-  fabric.Image.filters.Brightness.fromObject = function(object) {
-    return new fabric.Image.filters.Brightness(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Adapted from <a href="http://www.html5rocks.com/en/tutorials/canvas/imagefilters/">html5rocks article</a>
-   * @class fabric.Image.filters.Convolute
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Convolute#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example <caption>Sharpen filter</caption>
-   * var filter = new fabric.Image.filters.Convolute({
-   *   matrix: [ 0, -1,  0,
-   *            -1,  5, -1,
-   *             0, -1,  0 ]
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   * @example <caption>Blur filter</caption>
-   * var filter = new fabric.Image.filters.Convolute({
-   *   matrix: [ 1/9, 1/9, 1/9,
-   *             1/9, 1/9, 1/9,
-   *             1/9, 1/9, 1/9 ]
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   * @example <caption>Emboss filter</caption>
-   * var filter = new fabric.Image.filters.Convolute({
-   *   matrix: [ 1,   1,  1,
-   *             1, 0.7, -1,
-   *            -1,  -1, -1 ]
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   * @example <caption>Emboss filter with opaqueness</caption>
-   * var filter = new fabric.Image.filters.Convolute({
-   *   opaque: true,
-   *   matrix: [ 1,   1,  1,
-   *             1, 0.7, -1,
-   *            -1,  -1, -1 ]
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Convolute = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Convolute.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Convolute',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Convolute.prototype
-     * @param {Object} [options] Options object
-     * @param {Boolean} [options.opaque=false] Opaque value (true/false)
-     * @param {Array} [options.matrix] Filter matrix
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.opaque = options.opaque;
-      this.matrix = options.matrix || [
-        0, 0, 0,
-        0, 1, 0,
-        0, 0, 0
-      ];
-
-      var canvasEl = fabric.util.createCanvasElement();
-      this.tmpCtx = canvasEl.getContext('2d');
-    },
-
-    /**
-     * @private
-     */
-    _createImageData: function(w, h) {
-      return this.tmpCtx.createImageData(w, h);
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-
-      var weights = this.matrix,
-          context = canvasEl.getContext('2d'),
-          pixels = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-
-          side = Math.round(Math.sqrt(weights.length)),
-          halfSide = Math.floor(side/2),
-          src = pixels.data,
-          sw = pixels.width,
-          sh = pixels.height,
-
-          // pad output by the convolution matrix
-          w = sw,
-          h = sh,
-          output = this._createImageData(w, h),
-
-          dst = output.data,
-
-          // go through the destination image pixels
-          alphaFac = this.opaque ? 1 : 0;
-
-      for (var y = 0; y < h; y++) {
-        for (var x = 0; x < w; x++) {
-          var sy = y,
-              sx = x,
-              dstOff = (y * w + x) * 4,
-              // calculate the weighed sum of the source image pixels that
-              // fall under the convolution matrix
-              r = 0, g = 0, b = 0, a = 0;
-
-          for (var cy = 0; cy < side; cy++) {
-            for (var cx = 0; cx < side; cx++) {
-
-              var scy = sy + cy - halfSide,
-                  scx = sx + cx - halfSide;
-
-              /* jshint maxdepth:5 */
-              if (scy < 0 || scy > sh || scx < 0 || scx > sw) {
-                continue;
-              }
-
-              var srcOff = (scy * sw + scx) * 4,
-                  wt = weights[cy * side + cx];
-
-              r += src[srcOff] * wt;
-              g += src[srcOff + 1] * wt;
-              b += src[srcOff + 2] * wt;
-              a += src[srcOff + 3] * wt;
-            }
-          }
-          dst[dstOff] = r;
-          dst[dstOff + 1] = g;
-          dst[dstOff + 2] = b;
-          dst[dstOff + 3] = a + alphaFac * (255 - a);
-        }
-      }
-
-      context.putImageData(output, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        opaque: this.opaque,
-        matrix: this.matrix
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Convolute} Instance of fabric.Image.filters.Convolute
-   */
-  fabric.Image.filters.Convolute.fromObject = function(object) {
-    return new fabric.Image.filters.Convolute(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * GradientTransparency filter class
-   * @class fabric.Image.filters.GradientTransparency
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.GradientTransparency#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.GradientTransparency({
-   *   threshold: 200
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.GradientTransparency = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.GradientTransparency.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'GradientTransparency',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.GradientTransparency.prototype
-     * @param {Object} [options] Options object
-     * @param {Number} [options.threshold=100] Threshold value
-     */
-    initialize: function(options) {
-      options = options || { };
-      this.threshold = options.threshold || 100;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          threshold = this.threshold,
-          total = data.length;
-
-      for (var i = 0, len = data.length; i < len; i += 4) {
-        data[i + 3] = threshold + 255 * (total - i) / total;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        threshold: this.threshold
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.GradientTransparency} Instance of fabric.Image.filters.GradientTransparency
-   */
-  fabric.Image.filters.GradientTransparency.fromObject = function(object) {
-    return new fabric.Image.filters.GradientTransparency(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { });
-
-  /**
-   * Grayscale image filter class
-   * @class fabric.Image.filters.Grayscale
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Grayscale();
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Grayscale = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Grayscale.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Grayscale',
-
-    /**
-     * Applies filter to canvas element
-     * @memberOf fabric.Image.filters.Grayscale.prototype
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          len = imageData.width * imageData.height * 4,
-          index = 0,
-          average;
-
-      while (index < len) {
-        average = (data[index] + data[index + 1] + data[index + 2]) / 3;
-        data[index]     = average;
-        data[index + 1] = average;
-        data[index + 2] = average;
-        index += 4;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @return {fabric.Image.filters.Grayscale} Instance of fabric.Image.filters.Grayscale
-   */
-  fabric.Image.filters.Grayscale.fromObject = function() {
-    return new fabric.Image.filters.Grayscale();
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { });
-
-  /**
-   * Invert filter class
-   * @class fabric.Image.filters.Invert
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Invert();
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Invert = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Invert.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Invert',
-
-    /**
-     * Applies filter to canvas element
-     * @memberOf fabric.Image.filters.Invert.prototype
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = data.length, i;
-
-      for (i = 0; i < iLen; i+=4) {
-        data[i] = 255 - data[i];
-        data[i + 1] = 255 - data[i + 1];
-        data[i + 2] = 255 - data[i + 2];
-      }
-
-      context.putImageData(imageData, 0, 0);
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @return {fabric.Image.filters.Invert} Instance of fabric.Image.filters.Invert
-   */
-  fabric.Image.filters.Invert.fromObject = function() {
-    return new fabric.Image.filters.Invert();
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Mask filter class
-   * See http://resources.aleph-1.com/mask/
-   * @class fabric.Image.filters.Mask
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Mask#initialize} for constructor definition
-   */
-  fabric.Image.filters.Mask = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Mask.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Mask',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Mask.prototype
-     * @param {Object} [options] Options object
-     * @param {fabric.Image} [options.mask] Mask image object
-     * @param {Number} [options.channel=0] Rgb channel (0, 1, 2 or 3)
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.mask = options.mask;
-      this.channel = [ 0, 1, 2, 3 ].indexOf(options.channel) > -1 ? options.channel : 0;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      if (!this.mask) {
-        return;
-      }
-
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          maskEl = this.mask.getElement(),
-          maskCanvasEl = fabric.util.createCanvasElement(),
-          channel = this.channel,
-          i,
-          iLen = imageData.width * imageData.height * 4;
-
-      maskCanvasEl.width = maskEl.width;
-      maskCanvasEl.height = maskEl.height;
-
-      maskCanvasEl.getContext('2d').drawImage(maskEl, 0, 0, maskEl.width, maskEl.height);
-
-      var maskImageData = maskCanvasEl.getContext('2d').getImageData(0, 0, maskEl.width, maskEl.height),
-          maskData = maskImageData.data;
-
-      for (i = 0; i < iLen; i += 4) {
-        data[i + 3] = maskData[i + channel];
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        mask: this.mask.toObject(),
-        channel: this.channel
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @param {Function} [callback] Callback to invoke when a mask filter instance is created
-   */
-  fabric.Image.filters.Mask.fromObject = function(object, callback) {
-    fabric.util.loadImage(object.mask.src, function(img) {
-      object.mask = new fabric.Image(img, object.mask);
-      callback && callback(new fabric.Image.filters.Mask(object));
-    });
-  };
-
-  /**
-   * Indicates that instances of this type are async
-   * @static
-   * @type Boolean
-   * @default
-   */
-  fabric.Image.filters.Mask.async = true;
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Noise filter class
-   * @class fabric.Image.filters.Noise
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Noise#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Noise({
-   *   noise: 700
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Noise = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Noise.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Noise',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Noise.prototype
-     * @param {Object} [options] Options object
-     * @param {Number} [options.noise=0] Noise value
-     */
-    initialize: function(options) {
-      options = options || { };
-      this.noise = options.noise || 0;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          noise = this.noise, rand;
-
-      for (var i = 0, len = data.length; i < len; i += 4) {
-
-        rand = (0.5 - Math.random()) * noise;
-
-        data[i] += rand;
-        data[i + 1] += rand;
-        data[i + 2] += rand;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        noise: this.noise
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Noise} Instance of fabric.Image.filters.Noise
-   */
-  fabric.Image.filters.Noise.fromObject = function(object) {
-    return new fabric.Image.filters.Noise(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Pixelate filter class
-   * @class fabric.Image.filters.Pixelate
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Pixelate#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Pixelate({
-   *   blocksize: 8
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Pixelate = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Pixelate.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Pixelate',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Pixelate.prototype
-     * @param {Object} [options] Options object
-     * @param {Number} [options.blocksize=4] Blocksize for pixelate
-     */
-    initialize: function(options) {
-      options = options || { };
-      this.blocksize = options.blocksize || 4;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = imageData.height,
-          jLen = imageData.width,
-          index, i, j, r, g, b, a;
-
-      for (i = 0; i < iLen; i += this.blocksize) {
-        for (j = 0; j < jLen; j += this.blocksize) {
-
-          index = (i * 4) * jLen + (j * 4);
-
-          r = data[index];
-          g = data[index + 1];
-          b = data[index + 2];
-          a = data[index + 3];
-
-          /*
-           blocksize: 4
-
-           [1,x,x,x,1]
-           [x,x,x,x,1]
-           [x,x,x,x,1]
-           [x,x,x,x,1]
-           [1,1,1,1,1]
-           */
-
-          for (var _i = i, _ilen = i + this.blocksize; _i < _ilen; _i++) {
-            for (var _j = j, _jlen = j + this.blocksize; _j < _jlen; _j++) {
-              index = (_i * 4) * jLen + (_j * 4);
-              data[index] = r;
-              data[index + 1] = g;
-              data[index + 2] = b;
-              data[index + 3] = a;
-            }
-          }
-        }
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        blocksize: this.blocksize
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Pixelate} Instance of fabric.Image.filters.Pixelate
-   */
-  fabric.Image.filters.Pixelate.fromObject = function(object) {
-    return new fabric.Image.filters.Pixelate(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Remove white filter class
-   * @class fabric.Image.filters.RemoveWhite
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.RemoveWhite#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.RemoveWhite({
-   *   threshold: 40,
-   *   distance: 140
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.RemoveWhite = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.RemoveWhite.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'RemoveWhite',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.RemoveWhite.prototype
-     * @param {Object} [options] Options object
-     * @param {Number} [options.threshold=30] Threshold value
-     * @param {Number} [options.distance=20] Distance value
-     */
-    initialize: function(options) {
-      options = options || { };
-      this.threshold = options.threshold || 30;
-      this.distance = options.distance || 20;
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          threshold = this.threshold,
-          distance = this.distance,
-          limit = 255 - threshold,
-          abs = Math.abs,
-          r, g, b;
-
-      for (var i = 0, len = data.length; i < len; i += 4) {
-        r = data[i];
-        g = data[i + 1];
-        b = data[i + 2];
-
-        if (r > limit &&
-            g > limit &&
-            b > limit &&
-            abs(r - g) < distance &&
-            abs(r - b) < distance &&
-            abs(g - b) < distance
-        ) {
-          data[i + 3] = 1;
-        }
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        threshold: this.threshold,
-        distance: this.distance
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.RemoveWhite} Instance of fabric.Image.filters.RemoveWhite
-   */
-  fabric.Image.filters.RemoveWhite.fromObject = function(object) {
-    return new fabric.Image.filters.RemoveWhite(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { });
-
-  /**
-   * Sepia filter class
-   * @class fabric.Image.filters.Sepia
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Sepia();
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Sepia = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Sepia',
-
-    /**
-     * Applies filter to canvas element
-     * @memberOf fabric.Image.filters.Sepia.prototype
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = data.length, i, avg;
-
-      for (i = 0; i < iLen; i+=4) {
-        avg = 0.3  * data[i] + 0.59 * data[i + 1] + 0.11 * data[i + 2];
-        data[i] = avg + 100;
-        data[i + 1] = avg + 50;
-        data[i + 2] = avg + 255;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @return {fabric.Image.filters.Sepia} Instance of fabric.Image.filters.Sepia
-   */
-  fabric.Image.filters.Sepia.fromObject = function() {
-    return new fabric.Image.filters.Sepia();
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { });
-
-  /**
-   * Sepia2 filter class
-   * @class fabric.Image.filters.Sepia2
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example
-   * var filter = new fabric.Image.filters.Sepia2();
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Sepia2 = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Sepia2.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Sepia2',
-
-    /**
-     * Applies filter to canvas element
-     * @memberOf fabric.Image.filters.Sepia.prototype
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = data.length, i, r, g, b;
-
-      for (i = 0; i < iLen; i+=4) {
-        r = data[i];
-        g = data[i + 1];
-        b = data[i + 2];
-
-        data[i] = (r * 0.393 + g * 0.769 + b * 0.189 ) / 1.351;
-        data[i + 1] = (r * 0.349 + g * 0.686 + b * 0.168 ) / 1.203;
-        data[i + 2] = (r * 0.272 + g * 0.534 + b * 0.131 ) / 2.140;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @return {fabric.Image.filters.Sepia2} Instance of fabric.Image.filters.Sepia2
-   */
-  fabric.Image.filters.Sepia2.fromObject = function() {
-    return new fabric.Image.filters.Sepia2();
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Tint filter class
-   * Adapted from <a href="https://github.com/mezzoblue/PaintbrushJS">https://github.com/mezzoblue/PaintbrushJS</a>
-   * @class fabric.Image.filters.Tint
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @see {@link fabric.Image.filters.Tint#initialize} for constructor definition
-   * @see {@link http://fabricjs.com/image-filters/|ImageFilters demo}
-   * @example <caption>Tint filter with hex color and opacity</caption>
-   * var filter = new fabric.Image.filters.Tint({
-   *   color: '#3513B0',
-   *   opacity: 0.5
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   * @example <caption>Tint filter with rgba color</caption>
-   * var filter = new fabric.Image.filters.Tint({
-   *   color: 'rgba(53, 21, 176, 0.5)'
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Tint = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Tint.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Tint',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Tint.prototype
-     * @param {Object} [options] Options object
-     * @param {String} [options.color=#000000] Color to tint the image with
-     * @param {Number} [options.opacity] Opacity value that controls the tint effect's transparency (0..1)
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.color = options.color || '#000000';
-      this.opacity = typeof options.opacity !== 'undefined'
-                      ? options.opacity
-                      : new fabric.Color(this.color).getAlpha();
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = data.length, i,
-          tintR, tintG, tintB,
-          r, g, b, alpha1,
-          source;
-
-      source = new fabric.Color(this.color).getSource();
-
-      tintR = source[0] * this.opacity;
-      tintG = source[1] * this.opacity;
-      tintB = source[2] * this.opacity;
-
-      alpha1 = 1 - this.opacity;
-
-      for (i = 0; i < iLen; i+=4) {
-        r = data[i];
-        g = data[i + 1];
-        b = data[i + 2];
-
-        // alpha compositing
-        data[i] = tintR + r * alpha1;
-        data[i + 1] = tintG + g * alpha1;
-        data[i + 2] = tintB + b * alpha1;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        color: this.color,
-        opacity: this.opacity
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Tint} Instance of fabric.Image.filters.Tint
-   */
-  fabric.Image.filters.Tint.fromObject = function(object) {
-    return new fabric.Image.filters.Tint(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric  = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend;
-
-  /**
-   * Multiply filter class
-   * Adapted from <a href="http://www.laurenscorijn.com/articles/colormath-basics">http://www.laurenscorijn.com/articles/colormath-basics</a>
-   * @class fabric.Image.filters.Multiply
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @example <caption>Multiply filter with hex color</caption>
-   * var filter = new fabric.Image.filters.Multiply({
-   *   color: '#F0F'
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   * @example <caption>Multiply filter with rgb color</caption>
-   * var filter = new fabric.Image.filters.Multiply({
-   *   color: 'rgb(53, 21, 176)'
-   * });
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Multiply = fabric.util.createClass(fabric.Image.filters.BaseFilter, /** @lends fabric.Image.filters.Multiply.prototype */ {
-
-    /**
-     * Filter type
-     * @param {String} type
-     * @default
-     */
-    type: 'Multiply',
-
-    /**
-     * Constructor
-     * @memberOf fabric.Image.filters.Multiply.prototype
-     * @param {Object} [options] Options object
-     * @param {String} [options.color=#000000] Color to multiply the image pixels with
-     */
-    initialize: function(options) {
-      options = options || { };
-
-      this.color = options.color || '#000000';
-    },
-
-    /**
-     * Applies filter to canvas element
-     * @param {Object} canvasEl Canvas element to apply filter to
-     */
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          iLen = data.length, i,
-          source;
-
-      source = new fabric.Color(this.color).getSource();
-
-      for (i = 0; i < iLen; i+=4) {
-        data[i] *= source[0] / 255;
-        data[i + 1] *= source[1] / 255;
-        data[i + 2] *= source[2] / 255;
-      }
-
-      context.putImageData(imageData, 0, 0);
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function() {
-      return extend(this.callSuper('toObject'), {
-        color: this.color
-      });
-    }
-  });
-
-  /**
-   * Returns filter instance from an object representation
-   * @static
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Image.filters.Multiply} Instance of fabric.Image.filters.Multiply
-   */
-  fabric.Image.filters.Multiply.fromObject = function(object) {
-    return new fabric.Image.filters.Multiply(object);
-  };
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global){
-  'use strict';
-
-  var fabric = global.fabric;
-
-  /**
-   * Color Blend filter class
-   * @class fabric.Image.filter.Blend
-   * @memberOf fabric.Image.filters
-   * @extends fabric.Image.filters.BaseFilter
-   * @example
-   * var filter = new fabric.Image.filters.Blend({
-   *  color: '#000',
-   *  mode: 'multiply'
-   * });
-   *
-   * var filter = new fabric.Image.filters.Blend({
-   *  image: fabricImageObject,
-   *  mode: 'multiply',
-   *  alpha: 0.5
-   * });
-
-   * object.filters.push(filter);
-   * object.applyFilters(canvas.renderAll.bind(canvas));
-   */
-  fabric.Image.filters.Blend = fabric.util.createClass({
-    type: 'Blend',
-
-    initialize: function(options){
-      options = options || {};
-      this.color = options.color || '#000';
-      this.image = options.image || false;
-      this.mode = options.mode || 'multiply';
-      this.alpha = options.alpha || 1;
-    },
-
-    applyTo: function(canvasEl) {
-      var context = canvasEl.getContext('2d'),
-          imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
-          data = imageData.data,
-          tr, tg, tb,
-          r, g, b,
-          source,
-          isImage = false;
-
-      if (this.image) {
-        // Blend images
-        isImage = true;
-
-        var _el = fabric.util.createCanvasElement();
-        _el.width = this.image.width;
-        _el.height = this.image.height;
-
-        var tmpCanvas = new fabric.StaticCanvas(_el);
-        tmpCanvas.add(this.image);
-        var context2 =  tmpCanvas.getContext('2d');
-        source = context2.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height).data;
-      }
-      else {
-        // Blend color
-        source = new fabric.Color(this.color).getSource();
-
-        tr = source[0] * this.alpha;
-        tg = source[1] * this.alpha;
-        tb = source[2] * this.alpha;
-      }
-
-      for (var i = 0, len = data.length; i < len; i += 4) {
-
-        r = data[i];
-        g = data[i + 1];
-        b = data[i + 2];
-
-        if (isImage) {
-          tr = source[i] * this.alpha;
-          tg = source[i + 1] * this.alpha;
-          tb = source[i + 2] * this.alpha;
-        }
-
-        switch (this.mode) {
-          case 'multiply':
-            data[i] = r * tr / 255;
-            data[i + 1] = g * tg / 255;
-            data[i + 2] = b * tb / 255;
-            break;
-          case 'screen':
-            data[i] = 1 - (1 - r) * (1 - tr);
-            data[i + 1] = 1 - (1 - g) * (1 - tg);
-            data[i + 2] = 1 - (1 - b) * (1 - tb);
-            break;
-          case 'add':
-            data[i] = Math.min(255, r + tr);
-            data[i + 1] = Math.min(255, g + tg);
-            data[i + 2] = Math.min(255, b + tb);
-            break;
-          case 'diff':
-          case 'difference':
-            data[i] = Math.abs(r - tr);
-            data[i + 1] = Math.abs(g - tg);
-            data[i + 2] = Math.abs(b - tb);
-            break;
-          case 'subtract':
-            var _r = r - tr,
-                _g = g - tg,
-                _b = b - tb;
-
-            data[i] = (_r < 0) ? 0 : _r;
-            data[i + 1] = (_g < 0) ? 0 : _g;
-            data[i + 2] = (_b < 0) ? 0 : _b;
-            break;
-          case 'darken':
-            data[i] = Math.min(r, tr);
-            data[i + 1] = Math.min(g, tg);
-            data[i + 2] = Math.min(b, tb);
-            break;
-          case 'lighten':
-            data[i] = Math.max(r, tr);
-            data[i + 1] = Math.max(g, tg);
-            data[i + 2] = Math.max(b, tb);
-            break;
-        }
-      }
-
-      context.putImageData(imageData, 0, 0);
-    }
-  });
-
-  fabric.Image.filters.Blend.fromObject = function(object) {
-    return new fabric.Image.filters.Blend(object);
-  };
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-(function(global) {
-
-  'use strict';
-
-  var fabric = global.fabric || (global.fabric = { }),
-      extend = fabric.util.object.extend,
-      clone = fabric.util.object.clone,
-      toFixed = fabric.util.toFixed,
-      supportsLineDash = fabric.StaticCanvas.supports('setLineDash');
-
-  if (fabric.Text) {
-    fabric.warn('fabric.Text is already defined');
-    return;
-  }
-
-  var stateProperties = fabric.Object.prototype.stateProperties.concat();
-  stateProperties.push(
-    'fontFamily',
-    'fontWeight',
-    'fontSize',
-    'text',
-    'textDecoration',
-    'textAlign',
-    'fontStyle',
-    'lineHeight',
-    'textBackgroundColor',
-    'useNative',
-    'path'
-  );
-
-  /**
-   * Text class
-   * @class fabric.Text
-   * @extends fabric.Object
-   * @return {fabric.Text} thisArg
-   * @tutorial {@link http://fabricjs.com/fabric-intro-part-2/#text}
-   * @see {@link fabric.Text#initialize} for constructor definition
-   */
-  fabric.Text = fabric.util.createClass(fabric.Object, /** @lends fabric.Text.prototype */ {
-
-    /**
-     * Properties which when set cause object to change dimensions
-     * @type Object
-     * @private
-     */
-    _dimensionAffectingProps: {
-      fontSize: true,
-      fontWeight: true,
-      fontFamily: true,
-      textDecoration: true,
-      fontStyle: true,
-      lineHeight: true,
-      stroke: true,
-      strokeWidth: true,
-      text: true
-    },
-
-    /**
-     * @private
-     */
-    _reNewline: /\r?\n/,
-
-    /**
-     * Retrieves object's fontSize
-     * @method getFontSize
-     * @memberOf fabric.Text.prototype
-     * @return {String} Font size (in pixels)
-     */
-
-    /**
-     * Sets object's fontSize
-     * @method setFontSize
-     * @memberOf fabric.Text.prototype
-     * @param {Number} fontSize Font size (in pixels)
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's fontWeight
-     * @method getFontWeight
-     * @memberOf fabric.Text.prototype
-     * @return {(String|Number)} Font weight
-     */
-
-    /**
-     * Sets object's fontWeight
-     * @method setFontWeight
-     * @memberOf fabric.Text.prototype
-     * @param {(Number|String)} fontWeight Font weight
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's fontFamily
-     * @method getFontFamily
-     * @memberOf fabric.Text.prototype
-     * @return {String} Font family
-     */
-
-    /**
-     * Sets object's fontFamily
-     * @method setFontFamily
-     * @memberOf fabric.Text.prototype
-     * @param {String} fontFamily Font family
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's text
-     * @method getText
-     * @memberOf fabric.Text.prototype
-     * @return {String} text
-     */
-
-    /**
-     * Sets object's text
-     * @method setText
-     * @memberOf fabric.Text.prototype
-     * @param {String} text Text
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's textDecoration
-     * @method getTextDecoration
-     * @memberOf fabric.Text.prototype
-     * @return {String} Text decoration
-     */
-
-    /**
-     * Sets object's textDecoration
-     * @method setTextDecoration
-     * @memberOf fabric.Text.prototype
-     * @param {String} textDecoration Text decoration
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's fontStyle
-     * @method getFontStyle
-     * @memberOf fabric.Text.prototype
-     * @return {String} Font style
-     */
-
-    /**
-     * Sets object's fontStyle
-     * @method setFontStyle
-     * @memberOf fabric.Text.prototype
-     * @param {String} fontStyle Font style
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's lineHeight
-     * @method getLineHeight
-     * @memberOf fabric.Text.prototype
-     * @return {Number} Line height
-     */
-
-    /**
-     * Sets object's lineHeight
-     * @method setLineHeight
-     * @memberOf fabric.Text.prototype
-     * @param {Number} lineHeight Line height
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's textAlign
-     * @method getTextAlign
-     * @memberOf fabric.Text.prototype
-     * @return {String} Text alignment
-     */
-
-    /**
-     * Sets object's textAlign
-     * @method setTextAlign
-     * @memberOf fabric.Text.prototype
-     * @param {String} textAlign Text alignment
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Retrieves object's textBackgroundColor
-     * @method getTextBackgroundColor
-     * @memberOf fabric.Text.prototype
-     * @return {String} Text background color
-     */
-
-    /**
-     * Sets object's textBackgroundColor
-     * @method setTextBackgroundColor
-     * @memberOf fabric.Text.prototype
-     * @param {String} textBackgroundColor Text background color
-     * @return {fabric.Text}
-     * @chainable
-     */
-
-    /**
-     * Type of an object
-     * @type String
-     * @default
-     */
-    type:                 'text',
-
-    /**
-     * Font size (in pixels)
-     * @type Number
-     * @default
-     */
-    fontSize:             40,
-
-    /**
-     * Font weight (e.g. bold, normal, 400, 600, 800)
-     * @type {(Number|String)}
-     * @default
-     */
-    fontWeight:           'normal',
-
-    /**
-     * Font family
-     * @type String
-     * @default
-     */
-    fontFamily:           'Times New Roman',
-
-    /**
-     * Text decoration Possible values: "", "underline", "overline" or "line-through".
-     * @type String
-     * @default
-     */
-    textDecoration:       '',
-
-    /**
-     * Text alignment. Possible values: "left", "center", or "right".
-     * @type String
-     * @default
-     */
-    textAlign:            'left',
-
-    /**
-     * Font style . Possible values: "", "normal", "italic" or "oblique".
-     * @type String
-     * @default
-     */
-    fontStyle:            '',
-
-    /**
-     * Line height
-     * @type Number
-     * @default
-     */
-    lineHeight:           1.3,
-
-    /**
-     * Background color of text lines
-     * @type String
-     * @default
-     */
-    textBackgroundColor:  '',
-
-    /**
-     * URL of a font file, when using Cufon
-     * @type String | null
-     * @default
-     */
-    path:                 null,
-
-    /**
-     * Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used)
-     * @type Boolean
-     * @default
-     */
-    useNative:            true,
-
-    /**
-     * List of properties to consider when checking if
-     * state of an object is changed ({@link fabric.Object#hasStateChanged})
-     * as well as for history (undo/redo) purposes
-     * @type Array
-     */
-    stateProperties:      stateProperties,
-
-    /**
-     * When defined, an object is rendered via stroke and this property specifies its color.
-     * <b>Backwards incompatibility note:</b> This property was named "strokeStyle" until v1.1.6
-     * @type String
-     * @default
-     */
-    stroke:               null,
-
-    /**
-     * Shadow object representing shadow of this shape.
-     * <b>Backwards incompatibility note:</b> This property was named "textShadow" (String) until v1.2.11
-     * @type fabric.Shadow
-     * @default
-     */
-    shadow:               null,
-
-    /**
-     * Constructor
-     * @param {String} text Text string
-     * @param {Object} [options] Options object
-     * @return {fabric.Text} thisArg
-     */
-    initialize: function(text, options) {
-      options = options || { };
-
-      this.text = text;
-      this.__skipDimension = true;
-      this.setOptions(options);
-      this.__skipDimension = false;
-      this._initDimensions();
-    },
-
-    /**
-     * Renders text object on offscreen canvas, so that it would get dimensions
-     * @private
-     */
-    _initDimensions: function() {
-      if (this.__skipDimension) {
-        return;
-      }
-      var canvasEl = fabric.util.createCanvasElement();
-      this._render(canvasEl.getContext('2d'));
-    },
-
-    /**
-     * Returns string representation of an instance
-     * @return {String} String representation of text object
-     */
-    toString: function() {
-      return '#<fabric.Text (' + this.complexity() +
-        '): { "text": "' + this.text + '", "fontFamily": "' + this.fontFamily + '" }>';
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _render: function(ctx) {
-
-      if (typeof Cufon === 'undefined' || this.useNative === true) {
-        this._renderViaNative(ctx);
-      }
-      else {
-        this._renderViaCufon(ctx);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderViaNative: function(ctx) {
-      var textLines = this.text.split(this._reNewline);
-
-      this._setTextStyles(ctx);
-
-      this.width = this._getTextWidth(ctx, textLines);
-      this.height = this._getTextHeight(ctx, textLines);
-
-      this.clipTo && fabric.util.clipContext(this, ctx);
-
-      this._renderTextBackground(ctx, textLines);
-      this._translateForTextAlign(ctx);
-      this._renderText(ctx, textLines);
-
-      if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
-        ctx.restore();
-      }
-
-      this._renderTextDecoration(ctx, textLines);
-      this.clipTo && ctx.restore();
-
-      this._setBoundaries(ctx, textLines);
-      this._totalLineHeight = 0;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderText: function(ctx, textLines) {
-      ctx.save();
-      this._setShadow(ctx);
-      this._setupFillRule(ctx);
-      this._renderTextFill(ctx, textLines);
-      this._renderTextStroke(ctx, textLines);
-      this._restoreFillRule(ctx);
-      this._removeShadow(ctx);
-      ctx.restore();
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _translateForTextAlign: function(ctx) {
-      if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
-        ctx.save();
-        ctx.translate(this.textAlign === 'center' ? (this.width / 2) : this.width, 0);
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _setBoundaries: function(ctx, textLines) {
-      this._boundaries = [ ];
-
-      for (var i = 0, len = textLines.length; i < len; i++) {
-
-        var lineWidth = this._getLineWidth(ctx, textLines[i]),
-            lineLeftOffset = this._getLineLeftOffset(lineWidth);
-
-        this._boundaries.push({
-          height: this.fontSize * this.lineHeight,
-          width: lineWidth,
-          left: lineLeftOffset
-        });
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _setTextStyles: function(ctx) {
-      this._setFillStyles(ctx);
-      this._setStrokeStyles(ctx);
-      ctx.textBaseline = 'alphabetic';
-      if (!this.skipTextAlign) {
-        ctx.textAlign = this.textAlign;
-      }
-      ctx.font = this._getFontDeclaration();
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     * @return {Number} Height of fabric.Text object
-     */
-    _getTextHeight: function(ctx, textLines) {
-      return this.fontSize * textLines.length * this.lineHeight;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     * @return {Number} Maximum width of fabric.Text object
-     */
-    _getTextWidth: function(ctx, textLines) {
-      var maxWidth = ctx.measureText(textLines[0] || '|').width;
-
-      for (var i = 1, len = textLines.length; i < len; i++) {
-        var currentLineWidth = ctx.measureText(textLines[i]).width;
-        if (currentLineWidth > maxWidth) {
-          maxWidth = currentLineWidth;
-        }
-      }
-      return maxWidth;
-    },
-
-    /**
-     * @private
-     * @param {String} method Method name ("fillText" or "strokeText")
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {String} chars Chars to render
-     * @param {Number} left Left position of text
-     * @param {Number} top Top position of text
-     */
-    _renderChars: function(method, ctx, chars, left, top) {
-      ctx[method](chars, left, top);
-    },
-
-    /**
-     * @private
-     * @param {String} method Method name ("fillText" or "strokeText")
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {String} line Text to render
-     * @param {Number} left Left position of text
-     * @param {Number} top Top position of text
-     * @param {Number} lineIndex Index of a line in a text
-     */
-    _renderTextLine: function(method, ctx, line, left, top, lineIndex) {
-      // lift the line by quarter of fontSize
-      top -= this.fontSize / 4;
-
-      // short-circuit
-      if (this.textAlign !== 'justify') {
-        this._renderChars(method, ctx, line, left, top, lineIndex);
-        return;
-      }
-
-      var lineWidth = ctx.measureText(line).width,
-          totalWidth = this.width;
-
-      if (totalWidth > lineWidth) {
-        // stretch the line
-        var words = line.split(/\s+/),
-            wordsWidth = ctx.measureText(line.replace(/\s+/g, '')).width,
-            widthDiff = totalWidth - wordsWidth,
-            numSpaces = words.length - 1,
-            spaceWidth = widthDiff / numSpaces,
-            leftOffset = 0;
-
-        for (var i = 0, len = words.length; i < len; i++) {
-          this._renderChars(method, ctx, words[i], left + leftOffset, top, lineIndex);
-          leftOffset += ctx.measureText(words[i]).width + spaceWidth;
-        }
-      }
-      else {
-        this._renderChars(method, ctx, line, left, top, lineIndex);
-      }
-    },
-
-    /**
-     * @private
-     * @return {Number} Left offset
-     */
-    _getLeftOffset: function() {
-      if (fabric.isLikelyNode) {
-        return 0;
-      }
-      return -this.width / 2;
-    },
-
-    /**
-     * @private
-     * @return {Number} Top offset
-     */
-    _getTopOffset: function() {
-      return -this.height / 2;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _renderTextFill: function(ctx, textLines) {
-      if (!this.fill && !this._skipFillStrokeCheck) {
-        return;
-      }
-
-      this._boundaries = [ ];
-      var lineHeights = 0;
-
-      for (var i = 0, len = textLines.length; i < len; i++) {
-        var heightOfLine = this._getHeightOfLine(ctx, i, textLines);
-        lineHeights += heightOfLine;
-
-        this._renderTextLine(
-          'fillText',
-          ctx,
-          textLines[i],
-          this._getLeftOffset(),
-          this._getTopOffset() + lineHeights,
-          i
-        );
-      }
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _renderTextStroke: function(ctx, textLines) {
-      if ((!this.stroke || this.strokeWidth === 0) && !this._skipFillStrokeCheck) {
-        return;
-      }
-
-      var lineHeights = 0;
-
-      ctx.save();
-      if (this.strokeDashArray) {
-        // Spec requires the concatenation of two copies the dash list when the number of elements is odd
-        if (1 & this.strokeDashArray.length) {
-          this.strokeDashArray.push.apply(this.strokeDashArray, this.strokeDashArray);
-        }
-        supportsLineDash && ctx.setLineDash(this.strokeDashArray);
-      }
-
-      ctx.beginPath();
-      for (var i = 0, len = textLines.length; i < len; i++) {
-        var heightOfLine = this._getHeightOfLine(ctx, i, textLines);
-        lineHeights += heightOfLine;
-
-        this._renderTextLine(
-          'strokeText',
-          ctx,
-          textLines[i],
-          this._getLeftOffset(),
-          this._getTopOffset() + lineHeights,
-          i
-        );
-      }
-      ctx.closePath();
-      ctx.restore();
-    },
-
-    _getHeightOfLine: function() {
-      return this.fontSize * this.lineHeight;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _renderTextBackground: function(ctx, textLines) {
-      this._renderTextBoxBackground(ctx);
-      this._renderTextLinesBackground(ctx, textLines);
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    _renderTextBoxBackground: function(ctx) {
-      if (!this.backgroundColor) {
-        return;
-      }
-
-      ctx.save();
-      ctx.fillStyle = this.backgroundColor;
-
-      ctx.fillRect(
-        this._getLeftOffset(),
-        this._getTopOffset(),
-        this.width,
-        this.height
-      );
-
-      ctx.restore();
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _renderTextLinesBackground: function(ctx, textLines) {
-      if (!this.textBackgroundColor) {
-        return;
-      }
-
-      ctx.save();
-      ctx.fillStyle = this.textBackgroundColor;
-
-      for (var i = 0, len = textLines.length; i < len; i++) {
-
-        if (textLines[i] !== '') {
-
-          var lineWidth = this._getLineWidth(ctx, textLines[i]),
-              lineLeftOffset = this._getLineLeftOffset(lineWidth);
-
-          ctx.fillRect(
-            this._getLeftOffset() + lineLeftOffset,
-            this._getTopOffset() + (i * this.fontSize * this.lineHeight),
-            lineWidth,
-            this.fontSize * this.lineHeight
-          );
-        }
-      }
-      ctx.restore();
-    },
-
-    /**
-     * @private
-     * @param {Number} lineWidth Width of text line
-     * @return {Number} Line left offset
-     */
-    _getLineLeftOffset: function(lineWidth) {
-      if (this.textAlign === 'center') {
-        return (this.width - lineWidth) / 2;
-      }
-      if (this.textAlign === 'right') {
-        return this.width - lineWidth;
-      }
-      return 0;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {String} line Text line
-     * @return {Number} Line width
-     */
-    _getLineWidth: function(ctx, line) {
-      return this.textAlign === 'justify'
-        ? this.width
-        : ctx.measureText(line).width;
-    },
-
-    /**
-     * @private
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     * @param {Array} textLines Array of all text lines
-     */
-    _renderTextDecoration: function(ctx, textLines) {
-      if (!this.textDecoration) {
-        return;
-      }
-
-      // var halfOfVerticalBox = this.originY === 'top' ? 0 : this._getTextHeight(ctx, textLines) / 2;
-      var halfOfVerticalBox = this._getTextHeight(ctx, textLines) / 2,
-          _this = this;
-
-      /** @ignore */
-      function renderLinesAtOffset(offset) {
-        for (var i = 0, len = textLines.length; i < len; i++) {
-
-          var lineWidth = _this._getLineWidth(ctx, textLines[i]),
-              lineLeftOffset = _this._getLineLeftOffset(lineWidth);
-
-          ctx.fillRect(
-            _this._getLeftOffset() + lineLeftOffset,
-            ~~((offset + (i * _this._getHeightOfLine(ctx, i, textLines))) - halfOfVerticalBox),
-            lineWidth,
-            1);
-        }
-      }
-
-      if (this.textDecoration.indexOf('underline') > -1) {
-        renderLinesAtOffset(this.fontSize * this.lineHeight);
-      }
-      if (this.textDecoration.indexOf('line-through') > -1) {
-        renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize / 2);
-      }
-      if (this.textDecoration.indexOf('overline') > -1) {
-        renderLinesAtOffset(this.fontSize * this.lineHeight - this.fontSize);
-      }
-    },
-
-    /**
-     * @private
-     */
-    _getFontDeclaration: function() {
-      return [
-        // node-canvas needs "weight style", while browsers need "style weight"
-        (fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
-        (fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
-        this.fontSize + 'px',
-        (fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
-      ].join(' ');
-    },
-
-    /**
-     * Renders text instance on a specified context
-     * @param {CanvasRenderingContext2D} ctx Context to render on
-     */
-    render: function(ctx, noTransform) {
-      // do not render if object is not visible
-      if (!this.visible) {
-        return;
-      }
-
-      ctx.save();
-      this._transform(ctx, noTransform);
-
-      var m = this.transformMatrix,
-          isInPathGroup = this.group && this.group.type === 'path-group';
-
-      if (isInPathGroup) {
-        ctx.translate(-this.group.width/2, -this.group.height/2);
-      }
-      if (m) {
-        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
-      }
-      if (isInPathGroup) {
-        ctx.translate(this.left, this.top);
-      }
-      this._render(ctx);
-      ctx.restore();
-    },
-
-    /**
-     * Returns object representation of an instance
-     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
-     * @return {Object} Object representation of an instance
-     */
-    toObject: function(propertiesToInclude) {
-      var object = extend(this.callSuper('toObject', propertiesToInclude), {
-        text:                 this.text,
-        fontSize:             this.fontSize,
-        fontWeight:           this.fontWeight,
-        fontFamily:           this.fontFamily,
-        fontStyle:            this.fontStyle,
-        lineHeight:           this.lineHeight,
-        textDecoration:       this.textDecoration,
-        textAlign:            this.textAlign,
-        path:                 this.path,
-        textBackgroundColor:  this.textBackgroundColor,
-        useNative:            this.useNative
-      });
-      if (!this.includeDefaultValues) {
-        this._removeDefaultValues(object);
-      }
-      return object;
-    },
-
-    /* _TO_SVG_START_ */
-    /**
-     * Returns SVG representation of an instance
-     * @param {Function} [reviver] Method for further parsing of svg representation.
-     * @return {String} svg representation of an instance
-     */
-    toSVG: function(reviver) {
-      var markup = [ ],
-          textLines = this.text.split(this._reNewline),
-          offsets = this._getSVGLeftTopOffsets(textLines),
-          textAndBg = this._getSVGTextAndBg(offsets.lineTop, offsets.textLeft, textLines),
-          shadowSpans = this._getSVGShadows(offsets.lineTop, textLines);
-
-      // move top offset by an ascent
-      offsets.textTop += (this._fontAscent ? ((this._fontAscent / 5) * this.lineHeight) : 0);
-
-      this._wrapSVGTextAndBg(markup, textAndBg, shadowSpans, offsets);
-
-      return reviver ? reviver(markup.join('')) : markup.join('');
-    },
-
-    /**
-     * @private
-     */
-    _getSVGLeftTopOffsets: function(textLines) {
-      var lineTop = this.useNative
-            ? this.fontSize * this.lineHeight
-            : (-this._fontAscent - ((this._fontAscent / 5) * this.lineHeight)),
-
-          textLeft = -(this.width/2),
-          textTop = this.useNative
-            ? this.fontSize - 1
-            : (this.height/2) - (textLines.length * this.fontSize) - this._totalLineHeight;
-
-      return {
-        textLeft: textLeft + (this.group && this.group.type === 'path-group' ? this.left : 0),
-        textTop: textTop + (this.group && this.group.type === 'path-group' ? this.top : 0),
-        lineTop: lineTop
-      };
-    },
-
-    /**
-     * @private
-     */
-    _wrapSVGTextAndBg: function(markup, textAndBg, shadowSpans, offsets) {
-      markup.push(
-        '<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '">\n',
-          textAndBg.textBgRects.join(''),
-          '<text ',
-            (this.fontFamily ? 'font-family="' + this.fontFamily.replace(/"/g,'\'') + '" ': ''),
-            (this.fontSize ? 'font-size="' + this.fontSize + '" ': ''),
-            (this.fontStyle ? 'font-style="' + this.fontStyle + '" ': ''),
-            (this.fontWeight ? 'font-weight="' + this.fontWeight + '" ': ''),
-            (this.textDecoration ? 'text-decoration="' + this.textDecoration + '" ': ''),
-            'style="', this.getSvgStyles(), '" ',
-            /* svg starts from left/bottom corner so we normalize height */
-            'transform="translate(', toFixed(offsets.textLeft, 2), ' ', toFixed(offsets.textTop, 2), ')">',
-            shadowSpans.join(''),
-            textAndBg.textSpans.join(''),
-          '</text>\n',
-        '</g>\n'
-      );
-    },
-
-    /**
-     * @private
-     * @param {Number} lineHeight
-     * @param {Array} textLines Array of all text lines
-     * @return {Array}
-     */
-    _getSVGShadows: function(lineHeight, textLines) {
-      var shadowSpans = [],
-          i, len,
-          lineTopOffsetMultiplier = 1;
-
-      if (!this.shadow || !this._boundaries) {
-        return shadowSpans;
-      }
-
-      for (i = 0, len = textLines.length; i < len; i++) {
-        if (textLines[i] !== '') {
-          var lineLeftOffset = (this._boundaries && this._boundaries[i]) ? this._boundaries[i].left : 0;
-          shadowSpans.push(
-            '<tspan x="',
-            toFixed((lineLeftOffset + lineTopOffsetMultiplier) + this.shadow.offsetX, 2),
-            ((i === 0 || this.useNative) ? '" y' : '" dy'), '="',
-            toFixed(this.useNative
-              ? ((lineHeight * i) - this.height / 2 + this.shadow.offsetY)
-              : (lineHeight + (i === 0 ? this.shadow.offsetY : 0)), 2),
-            '" ',
-            this._getFillAttributes(this.shadow.color), '>',
-            fabric.util.string.escapeXml(textLines[i]),
-          '</tspan>');
-          lineTopOffsetMultiplier = 1;
-        }
-        else {
-          // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
-          // prevents empty tspans
-          lineTopOffsetMultiplier++;
-        }
-      }
-
-      return shadowSpans;
-    },
-
-    /**
-     * @private
-     * @param {Number} lineHeight
-     * @param {Number} textLeftOffset Text left offset
-     * @param {Array} textLines Array of all text lines
-     * @return {Object}
-     */
-    _getSVGTextAndBg: function(lineHeight, textLeftOffset, textLines) {
-      var textSpans = [ ],
-          textBgRects = [ ],
-          lineTopOffsetMultiplier = 1;
-
-      // bounding-box background
-      this._setSVGBg(textBgRects);
-
-      // text and text-background
-      for (var i = 0, len = textLines.length; i < len; i++) {
-        if (textLines[i] !== '') {
-          this._setSVGTextLineText(textLines[i], i, textSpans, lineHeight, lineTopOffsetMultiplier, textBgRects);
-          lineTopOffsetMultiplier = 1;
-        }
-        else {
-          // in some environments (e.g. IE 7 & 8) empty tspans are completely ignored, using a lineTopOffsetMultiplier
-          // prevents empty tspans
-          lineTopOffsetMultiplier++;
-        }
-
-        if (!this.textBackgroundColor || !this._boundaries) {
-          continue;
-        }
-
-        this._setSVGTextLineBg(textBgRects, i, textLeftOffset, lineHeight);
-      }
-
-      return {
-        textSpans: textSpans,
-        textBgRects: textBgRects
-      };
-    },
-
-    _setSVGTextLineText: function(textLine, i, textSpans, lineHeight, lineTopOffsetMultiplier) {
-      var lineLeftOffset = (this._boundaries && this._boundaries[i])
-        ? toFixed(this._boundaries[i].left, 2)
-        : 0;
-
-      textSpans.push(
-        '<tspan x="',
-          lineLeftOffset, '" ',
-          (i === 0 || this.useNative ? 'y' : 'dy'), '="',
-          toFixed(this.useNative
-            ? ((lineHeight * i) - this.height / 2)
-            : (lineHeight * lineTopOffsetMultiplier), 2), '" ',
-          // doing this on <tspan> elements since setting opacity
-          // on containing <text> one doesn't work in Illustrator
-          this._getFillAttributes(this.fill), '>',
-          fabric.util.string.escapeXml(textLine),
-        '</tspan>'
-      );
-    },
-
-    _setSVGTextLineBg: function(textBgRects, i, textLeftOffset, lineHeight) {
-      textBgRects.push(
-        '<rect ',
-          this._getFillAttributes(this.textBackgroundColor),
-          ' x="',
-          toFixed(textLeftOffset + this._boundaries[i].left, 2),
-          '" y="',
-          /* an offset that seems to straighten things out */
-          toFixed((lineHeight * i) - this.height / 2, 2),
-          '" width="',
-          toFixed(this._boundaries[i].width, 2),
-          '" height="',
-          toFixed(this._boundaries[i].height, 2),
-        '"></rect>\n');
-    },
-
-    _setSVGBg: function(textBgRects) {
-      if (this.backgroundColor && this._boundaries) {
-        textBgRects.push(
-          '<rect ',
-            this._getFillAttributes(this.backgroundColor),
-            ' x="',
-            toFixed(-this.width / 2, 2),
-            '" y="',
-            toFixed(-this.height / 2, 2),
-            '" width="',
-            toFixed(this.width, 2),
-            '" height="',
-            toFixed(this.height, 2),
-          '"></rect>');
-      }
-    },
-
-    /**
-     * Adobe Illustrator (at least CS5) is unable to render rgba()-based fill values
-     * we work around it by "moving" alpha channel into opacity attribute and setting fill's alpha to 1
-     *
-     * @private
-     * @param {Any} value
-     * @return {String}
-     */
-    _getFillAttributes: function(value) {
-      var fillColor = (value && typeof value === 'string') ? new fabric.Color(value) : '';
-      if (!fillColor || !fillColor.getSource() || fillColor.getAlpha() === 1) {
-        return 'fill="' + value + '"';
-      }
-      return 'opacity="' + fillColor.getAlpha() + '" fill="' + fillColor.setAlpha(1).toRgb() + '"';
-    },
-    /* _TO_SVG_END_ */
-
-    /**
-     * Sets specified property to a specified value
-     * @param {String} key
-     * @param {Any} value
-     * @return {fabric.Text} thisArg
-     * @chainable
-     */
-    _set: function(key, value) {
-      if (key === 'fontFamily' && this.path) {
-        this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3');
-      }
-      this.callSuper('_set', key, value);
-
-      if (key in this._dimensionAffectingProps) {
-        this._initDimensions();
-        this.setCoords();
-      }
-    },
-
-    /**
-     * Returns complexity of an instance
-     * @return {Number} complexity
-     */
-    complexity: function() {
-      return 1;
-    }
-  });
-
-  /* _FROM_SVG_START_ */
-  /**
-   * List of attribute names to account for when parsing SVG element (used by {@link fabric.Text.fromElement})
-   * @static
-   * @memberOf fabric.Text
-   * @see: http://www.w3.org/TR/SVG/text.html#TextElement
-   */
-  fabric.Text.ATTRIBUTE_NAMES = fabric.SHARED_ATTRIBUTES.concat(
-    'x y dx dy font-family font-style font-weight font-size text-decoration text-anchor'.split(' '));
-
-  /**
-   * Default SVG font size
-   * @static
-   * @memberOf fabric.Text
-   */
-  fabric.Text.DEFAULT_SVG_FONT_SIZE = 16;
-
-  /**
-   * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>)
-   * @static
-   * @memberOf fabric.Text
-   * @param {SVGElement} element Element to parse
-   * @param {Object} [options] Options object
-   * @return {fabric.Text} Instance of fabric.Text
-   */
-  fabric.Text.fromElement = function(element, options) {
-    if (!element) {
-      return null;
-    }
-
-    var parsedAttributes = fabric.parseAttributes(element, fabric.Text.ATTRIBUTE_NAMES);
-    options = fabric.util.object.extend((options ? fabric.util.object.clone(options) : { }), parsedAttributes);
-
-    if ('dx' in parsedAttributes) {
-      options.left += parsedAttributes.dx;
-    }
-    if ('dy' in parsedAttributes) {
-      options.top += parsedAttributes.dy;
-    }
-    if (!('fontSize' in options)) {
-      options.fontSize = fabric.Text.DEFAULT_SVG_FONT_SIZE;
-    }
-
-    if (!options.originX) {
-      options.originX = 'left';
-    }
-
-    var text = new fabric.Text(element.textContent, options),
-        /*
-          Adjust positioning:
-            x/y attributes in SVG correspond to the bottom-left corner of text bounding box
-            top/left properties in Fabric correspond to center point of text bounding box
-        */
-        offX = 0;
-
-    if (text.originX === 'left') {
-      offX = text.getWidth() / 2;
-    }
-    if (text.originX === 'right') {
-      offX = -text.getWidth() / 2;
-    }
-    text.set({
-      left: text.getLeft() + offX,
-      top: text.getTop() - text.getHeight() / 2
-    });
-
-    return text;
-  };
-  /* _FROM_SVG_END_ */
-
-  /**
-   * Returns fabric.Text instance from an object representation
-   * @static
-   * @memberOf fabric.Text
-   * @param {Object} object Object to create an instance from
-   * @return {fabric.Text} Instance of fabric.Text
-   */
-  fabric.Text.fromObject = function(object) {
-    return new fabric.Text(object.text, clone(object));
-  };
-
-  fabric.util.createAccessors(fabric.Text);
-
-})(typeof exports !== 'undefined' ? exports : this);
-
-
-}).call(this,require("buffer").Buffer)
-},{"buffer":2,"canvas":1,"jsdom":1}]},{},[6])(6)
-});
\ No newline at end of file
diff --git a/dist/html2canvas.svg.min.js b/dist/html2canvas.svg.min.js
deleted file mode 100644
index 5fb1c1c..0000000
--- a/dist/html2canvas.svg.min.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
-  html2canvas 0.5.0-alpha2 <http://html2canvas.hertzen.com>
-  Copyright (c) 2015 Niklas von Hertzen
-
-  Released under MIT License
-*/
-!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var n;"undefined"!=typeof window?n=window:"undefined"!=typeof global?n=global:"undefined"!=typeof self&&(n=self),(n.html2canvas||(n.html2canvas={})).svg=e()}}(function(){var define,module,exports;return function e(n,f,o){function d(t,l){if(!f[t]){if(!n[t]){var s="function"==typeof require&&require;if(!l&&s)return s(t,!0);if(i)return i(t,!0);var u=new Error("Cannot find module '"+t+"'");throw u.code="MODULE_NOT_FOUND",u}var a=f[t]={exports:{}};n[t][0].call(a.exports,function(e){var f=n[t][1][e];return d(f?f:e)},a,a.exports,e,n,f,o)}return f[t].exports}for(var i="function"==typeof require&&require,t=0;t<o.length;t++)d(o[t]);return d}({1:[function(){},{}],2:[function(e,n,f){function o(e,n,f){if(!(this instanceof o))return new o(e,n,f);var d,i=typeof e;if("number"===i)d=e>0?e>>>0:0;else if("string"===i)d=o.byteLength(e,n);else{if("object"!==i||null===e)throw new TypeError("must start with number, buffer, array or string");"Buffer"===e.type&&K(e.data)&&(e=e.data),d=+e.length>0?Math.floor(+e.length):0}if(d>L)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+L.toString(16)+" bytes");var t;o.TYPED_ARRAY_SUPPORT?t=o._augment(new Uint8Array(d)):(t=this,t.length=d,t._isBuffer=!0);var l;if(o.TYPED_ARRAY_SUPPORT&&"number"==typeof e.byteLength)t._set(e);else if(A(e))if(o.isBuffer(e))for(l=0;d>l;l++)t[l]=e.readUInt8(l);else for(l=0;d>l;l++)t[l]=(e[l]%256+256)%256;else if("string"===i)t.write(e,0,n);else if("number"===i&&!o.TYPED_ARRAY_SUPPORT&&!f)for(l=0;d>l;l++)t[l]=0;return d>0&&d<=o.poolSize&&(t.parent=M),t}function d(e,n,f){if(!(this instanceof d))return new d(e,n,f);var i=new o(e,n,f);return delete i.parent,i}function i(e,n,f,o){f=Number(f)||0;var d=e.length-f;o?(o=Number(o),o>d&&(o=d)):o=d;var i=n.length;if(i%2!==0)throw new Error("Invalid hex string");o>i/2&&(o=i/2);for(var t=0;o>t;t++){var l=parseInt(n.substr(2*t,2),16);if(isNaN(l))throw new Error("Invalid hex string");e[f+t]=l}return t}function t(e,n,f,o){var d=G(C(n,e.length-f),e,f,o);return d}function l(e,n,f,o){var d=G(D(n),e,f,o);return d}function s(e,n,f,o){return l(e,n,f,o)}function u(e,n,f,o){var d=G(F(n),e,f,o);return d}function a(e,n,f,o){var d=G(E(n,e.length-f),e,f,o,2);return d}function p(e,n,f){return I.fromByteArray(0===n&&f===e.length?e:e.slice(n,f))}function c(e,n,f){var o="",d="";f=Math.min(e.length,f);for(var i=n;f>i;i++)e[i]<=127?(o+=H(d)+String.fromCharCode(e[i]),d=""):d+="%"+e[i].toString(16);return o+H(d)}function y(e,n,f){var o="";f=Math.min(e.length,f);for(var d=n;f>d;d++)o+=String.fromCharCode(127&e[d]);return o}function m(e,n,f){var o="";f=Math.min(e.length,f);for(var d=n;f>d;d++)o+=String.fromCharCode(e[d]);return o}function r(e,n,f){var o=e.length;(!n||0>n)&&(n=0),(!f||0>f||f>o)&&(f=o);for(var d="",i=n;f>i;i++)d+=B(e[i]);return d}function v(e,n,f){for(var o=e.slice(n,f),d="",i=0;i<o.length;i+=2)d+=String.fromCharCode(o[i]+256*o[i+1]);return d}function w(e,n,f){if(e%1!==0||0>e)throw new RangeError("offset is not uint");if(e+n>f)throw new RangeError("Trying to access beyond buffer length")}function b(e,n,f,d,i,t){if(!o.isBuffer(e))throw new TypeError("buffer must be a Buffer instance");if(n>i||t>n)throw new RangeError("value is out of bounds");if(f+d>e.length)throw new RangeError("index out of range")}function g(e,n,f,o){0>n&&(n=65535+n+1);for(var d=0,i=Math.min(e.length-f,2);i>d;d++)e[f+d]=(n&255<<8*(o?d:1-d))>>>8*(o?d:1-d)}function h(e,n,f,o){0>n&&(n=4294967295+n+1);for(var d=0,i=Math.min(e.length-f,4);i>d;d++)e[f+d]=n>>>8*(o?d:3-d)&255}function x(e,n,f,o,d,i){if(n>d||i>n)throw new RangeError("value is out of bounds");if(f+o>e.length)throw new RangeError("index out of range");if(0>f)throw new RangeError("index out of range")}function j(e,n,f,o,d){return d||x(e,n,f,4,3.4028234663852886e38,-3.4028234663852886e38),J.write(e,n,f,o,23,4),f+4}function k(e,n,f,o,d){return d||x(e,n,f,8,1.7976931348623157e308,-1.7976931348623157e308),J.write(e,n,f,o,52,8),f+8}function q(e){if(e=z(e).replace(O,""),e.length<2)return"";for(;e.length%4!==0;)e+="=";return e}function z(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}function A(e){return K(e)||o.isBuffer(e)||e&&"object"==typeof e&&"number"==typeof e.length}function B(e){return 16>e?"0"+e.toString(16):e.toString(16)}function C(e,n){var f,o=e.length,d=null;n=n||1/0;for(var i=[],t=0;o>t;t++){if(f=e.charCodeAt(t),f>55295&&57344>f){if(!d){if(f>56319){(n-=3)>-1&&i.push(239,191,189);continue}if(t+1===o){(n-=3)>-1&&i.push(239,191,189);continue}d=f;continue}if(56320>f){(n-=3)>-1&&i.push(239,191,189),d=f;continue}f=d-55296<<10|f-56320|65536,d=null}else d&&((n-=3)>-1&&i.push(239,191,189),d=null);if(128>f){if((n-=1)<0)break;i.push(f)}else if(2048>f){if((n-=2)<0)break;i.push(f>>6|192,63&f|128)}else if(65536>f){if((n-=3)<0)break;i.push(f>>12|224,f>>6&63|128,63&f|128)}else{if(!(2097152>f))throw new Error("Invalid code point");if((n-=4)<0)break;i.push(f>>18|240,f>>12&63|128,f>>6&63|128,63&f|128)}}return i}function D(e){for(var n=[],f=0;f<e.length;f++)n.push(255&e.charCodeAt(f));return n}function E(e,n){for(var f,o,d,i=[],t=0;t<e.length&&!((n-=2)<0);t++)f=e.charCodeAt(t),o=f>>8,d=f%256,i.push(d),i.push(o);return i}function F(e){return I.toByteArray(q(e))}function G(e,n,f,o,d){d&&(o-=o%d);for(var i=0;o>i&&!(i+f>=n.length||i>=e.length);i++)n[i+f]=e[i];return i}function H(e){try{return decodeURIComponent(e)}catch(n){return String.fromCharCode(65533)}}var I=e("base64-js"),J=e("ieee754"),K=e("is-array");f.Buffer=o,f.SlowBuffer=d,f.INSPECT_MAX_BYTES=50,o.poolSize=8192;var L=1073741823,M={};o.TYPED_ARRAY_SUPPORT=function(){try{var e=new ArrayBuffer(0),n=new Uint8Array(e);return n.foo=function(){return 42},42===n.foo()&&"function"==typeof n.subarray&&0===new Uint8Array(1).subarray(1,1).byteLength}catch(f){return!1}}(),o.isBuffer=function(e){return!(null==e||!e._isBuffer)},o.compare=function(e,n){if(!o.isBuffer(e)||!o.isBuffer(n))throw new TypeError("Arguments must be Buffers");for(var f=e.length,d=n.length,i=0,t=Math.min(f,d);t>i&&e[i]===n[i];i++);return i!==t&&(f=e[i],d=n[i]),d>f?-1:f>d?1:0},o.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"raw":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},o.concat=function(e,n){if(!K(e))throw new TypeError("Usage: Buffer.concat(list[, length])");if(0===e.length)return new o(0);if(1===e.length)return e[0];var f;if(void 0===n)for(n=0,f=0;f<e.length;f++)n+=e[f].length;var d=new o(n),i=0;for(f=0;f<e.length;f++){var t=e[f];t.copy(d,i),i+=t.length}return d},o.byteLength=function(e,n){var f;switch(e+="",n||"utf8"){case"ascii":case"binary":case"raw":f=e.length;break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":f=2*e.length;break;case"hex":f=e.length>>>1;break;case"utf8":case"utf-8":f=C(e).length;break;case"base64":f=F(e).length;break;default:f=e.length}return f},o.prototype.length=void 0,o.prototype.parent=void 0,o.prototype.toString=function(e,n,f){var o=!1;if(n>>>=0,f=void 0===f||1/0===f?this.length:f>>>0,e||(e="utf8"),0>n&&(n=0),f>this.length&&(f=this.length),n>=f)return"";for(;;)switch(e){case"hex":return r(this,n,f);case"utf8":case"utf-8":return c(this,n,f);case"ascii":return y(this,n,f);case"binary":return m(this,n,f);case"base64":return p(this,n,f);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return v(this,n,f);default:if(o)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),o=!0}},o.prototype.equals=function(e){if(!o.isBuffer(e))throw new TypeError("Argument must be a Buffer");return 0===o.compare(this,e)},o.prototype.inspect=function(){var e="",n=f.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),"<Buffer "+e+">"},o.prototype.compare=function(e){if(!o.isBuffer(e))throw new TypeError("Argument must be a Buffer");return o.compare(this,e)},o.prototype.get=function(e){return console.log(".get() is deprecated. Access using array indexes instead."),this.readUInt8(e)},o.prototype.set=function(e,n){return console.log(".set() is deprecated. Access using array indexes instead."),this.writeUInt8(e,n)},o.prototype.write=function(e,n,f,o){if(isFinite(n))isFinite(f)||(o=f,f=void 0);else{var d=o;o=n,n=f,f=d}if(n=Number(n)||0,0>f||0>n||n>this.length)throw new RangeError("attempt to write outside buffer bounds");var p=this.length-n;f?(f=Number(f),f>p&&(f=p)):f=p,o=String(o||"utf8").toLowerCase();var c;switch(o){case"hex":c=i(this,e,n,f);break;case"utf8":case"utf-8":c=t(this,e,n,f);break;case"ascii":c=l(this,e,n,f);break;case"binary":c=s(this,e,n,f);break;case"base64":c=u(this,e,n,f);break;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":c=a(this,e,n,f);break;default:throw new TypeError("Unknown encoding: "+o)}return c},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},o.prototype.slice=function(e,n){var f=this.length;e=~~e,n=void 0===n?f:~~n,0>e?(e+=f,0>e&&(e=0)):e>f&&(e=f),0>n?(n+=f,0>n&&(n=0)):n>f&&(n=f),e>n&&(n=e);var d;if(o.TYPED_ARRAY_SUPPORT)d=o._augment(this.subarray(e,n));else{var i=n-e;d=new o(i,void 0,!0);for(var t=0;i>t;t++)d[t]=this[t+e]}return d.length&&(d.parent=this.parent||this),d},o.prototype.readUIntLE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e],d=1,i=0;++i<n&&(d*=256);)o+=this[e+i]*d;return o},o.prototype.readUIntBE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e+--n],d=1;n>0&&(d*=256);)o+=this[e+--n]*d;return o},o.prototype.readUInt8=function(e,n){return n||w(e,1,this.length),this[e]},o.prototype.readUInt16LE=function(e,n){return n||w(e,2,this.length),this[e]|this[e+1]<<8},o.prototype.readUInt16BE=function(e,n){return n||w(e,2,this.length),this[e]<<8|this[e+1]},o.prototype.readUInt32LE=function(e,n){return n||w(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},o.prototype.readUInt32BE=function(e,n){return n||w(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},o.prototype.readIntLE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=this[e],d=1,i=0;++i<n&&(d*=256);)o+=this[e+i]*d;return d*=128,o>=d&&(o-=Math.pow(2,8*n)),o},o.prototype.readIntBE=function(e,n,f){e>>>=0,n>>>=0,f||w(e,n,this.length);for(var o=n,d=1,i=this[e+--o];o>0&&(d*=256);)i+=this[e+--o]*d;return d*=128,i>=d&&(i-=Math.pow(2,8*n)),i},o.prototype.readInt8=function(e,n){return n||w(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},o.prototype.readInt16LE=function(e,n){n||w(e,2,this.length);var f=this[e]|this[e+1]<<8;return 32768&f?4294901760|f:f},o.prototype.readInt16BE=function(e,n){n||w(e,2,this.length);var f=this[e+1]|this[e]<<8;return 32768&f?4294901760|f:f},o.prototype.readInt32LE=function(e,n){return n||w(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},o.prototype.readInt32BE=function(e,n){return n||w(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},o.prototype.readFloatLE=function(e,n){return n||w(e,4,this.length),J.read(this,e,!0,23,4)},o.prototype.readFloatBE=function(e,n){return n||w(e,4,this.length),J.read(this,e,!1,23,4)},o.prototype.readDoubleLE=function(e,n){return n||w(e,8,this.length),J.read(this,e,!0,52,8)},o.prototype.readDoubleBE=function(e,n){return n||w(e,8,this.length),J.read(this,e,!1,52,8)},o.prototype.writeUIntLE=function(e,n,f,o){e=+e,n>>>=0,f>>>=0,o||b(this,e,n,f,Math.pow(2,8*f),0);var d=1,i=0;for(this[n]=255&e;++i<f&&(d*=256);)this[n+i]=e/d>>>0&255;return n+f},o.prototype.writeUIntBE=function(e,n,f,o){e=+e,n>>>=0,f>>>=0,o||b(this,e,n,f,Math.pow(2,8*f),0);var d=f-1,i=1;for(this[n+d]=255&e;--d>=0&&(i*=256);)this[n+d]=e/i>>>0&255;return n+f},o.prototype.writeUInt8=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,1,255,0),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[n]=e,n+1},o.prototype.writeUInt16LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8):g(this,e,n,!0),n+2},o.prototype.writeUInt16BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,65535,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>8,this[n+1]=e):g(this,e,n,!1),n+2},o.prototype.writeUInt32LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[n+3]=e>>>24,this[n+2]=e>>>16,this[n+1]=e>>>8,this[n]=e):h(this,e,n,!0),n+4},o.prototype.writeUInt32BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,4294967295,0),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>24,this[n+1]=e>>>16,this[n+2]=e>>>8,this[n+3]=e):h(this,e,n,!1),n+4},o.prototype.writeIntLE=function(e,n,f,o){e=+e,n>>>=0,o||b(this,e,n,f,Math.pow(2,8*f-1)-1,-Math.pow(2,8*f-1));var d=0,i=1,t=0>e?1:0;for(this[n]=255&e;++d<f&&(i*=256);)this[n+d]=(e/i>>0)-t&255;return n+f},o.prototype.writeIntBE=function(e,n,f,o){e=+e,n>>>=0,o||b(this,e,n,f,Math.pow(2,8*f-1)-1,-Math.pow(2,8*f-1));var d=f-1,i=1,t=0>e?1:0;for(this[n+d]=255&e;--d>=0&&(i*=256);)this[n+d]=(e/i>>0)-t&255;return n+f},o.prototype.writeInt8=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,1,127,-128),o.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),0>e&&(e=255+e+1),this[n]=e,n+1},o.prototype.writeInt16LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8):g(this,e,n,!0),n+2},o.prototype.writeInt16BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,2,32767,-32768),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>8,this[n+1]=e):g(this,e,n,!1),n+2},o.prototype.writeInt32LE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,2147483647,-2147483648),o.TYPED_ARRAY_SUPPORT?(this[n]=e,this[n+1]=e>>>8,this[n+2]=e>>>16,this[n+3]=e>>>24):h(this,e,n,!0),n+4},o.prototype.writeInt32BE=function(e,n,f){return e=+e,n>>>=0,f||b(this,e,n,4,2147483647,-2147483648),0>e&&(e=4294967295+e+1),o.TYPED_ARRAY_SUPPORT?(this[n]=e>>>24,this[n+1]=e>>>16,this[n+2]=e>>>8,this[n+3]=e):h(this,e,n,!1),n+4},o.prototype.writeFloatLE=function(e,n,f){return j(this,e,n,!0,f)},o.prototype.writeFloatBE=function(e,n,f){return j(this,e,n,!1,f)},o.prototype.writeDoubleLE=function(e,n,f){return k(this,e,n,!0,f)},o.prototype.writeDoubleBE=function(e,n,f){return k(this,e,n,!1,f)},o.prototype.copy=function(e,n,f,d){var i=this;if(f||(f=0),d||0===d||(d=this.length),n>=e.length&&(n=e.length),n||(n=0),d>0&&f>d&&(d=f),d===f)return 0;if(0===e.length||0===i.length)return 0;if(0>n)throw new RangeError("targetStart out of bounds");if(0>f||f>=i.length)throw new RangeError("sourceStart out of bounds");if(0>d)throw new RangeError("sourceEnd out of bounds");d>this.length&&(d=this.length),e.length-n<d-f&&(d=e.length-n+f);var t=d-f;if(1e3>t||!o.TYPED_ARRAY_SUPPORT)for(var l=0;t>l;l++)e[l+n]=this[l+f];else e._set(this.subarray(f,f+t),n);return t},o.prototype.fill=function(e,n,f){if(e||(e=0),n||(n=0),f||(f=this.length),n>f)throw new RangeError("end < start");if(f!==n&&0!==this.length){if(0>n||n>=this.length)throw new RangeError("start out of bounds");if(0>f||f>this.length)throw new RangeError("end out of bounds");var o;if("number"==typeof e)for(o=n;f>o;o++)this[o]=e;else{var d=C(e.toString()),i=d.length;for(o=n;f>o;o++)this[o]=d[o%i]}return this}},o.prototype.toArrayBuffer=function(){if("undefined"!=typeof Uint8Array){if(o.TYPED_ARRAY_SUPPORT)return new o(this).buffer;for(var e=new Uint8Array(this.length),n=0,f=e.length;f>n;n+=1)e[n]=this[n];return e.buffer}throw new TypeError("Buffer.toArrayBuffer not supported in this browser")};var N=o.prototype;o._augment=function(e){return e.constructor=o,e._isBuffer=!0,e._get=e.get,e._set=e.set,e.get=N.get,e.set=N.set,e.write=N.write,e.toString=N.toString,e.toLocaleString=N.toString,e.toJSON=N.toJSON,e.equals=N.equals,e.compare=N.compare,e.copy=N.copy,e.slice=N.slice,e.readUIntLE=N.readUIntLE,e.readUIntBE=N.readUIntBE,e.readUInt8=N.readUInt8,e.readUInt16LE=N.readUInt16LE,e.readUInt16BE=N.readUInt16BE,e.readUInt32LE=N.readUInt32LE,e.readUInt32BE=N.readUInt32BE,e.readIntLE=N.readIntLE,e.readIntBE=N.readIntBE,e.readInt8=N.readInt8,e.readInt16LE=N.readInt16LE,e.readInt16BE=N.readInt16BE,e.readInt32LE=N.readInt32LE,e.readInt32BE=N.readInt32BE,e.readFloatLE=N.readFloatLE,e.readFloatBE=N.readFloatBE,e.readDoubleLE=N.readDoubleLE,e.readDoubleBE=N.readDoubleBE,e.writeUInt8=N.writeUInt8,e.writeUIntLE=N.writeUIntLE,e.writeUIntBE=N.writeUIntBE,e.writeUInt16LE=N.writeUInt16LE,e.writeUInt16BE=N.writeUInt16BE,e.writeUInt32LE=N.writeUInt32LE,e.writeUInt32BE=N.writeUInt32BE,e.writeIntLE=N.writeIntLE,e.writeIntBE=N.writeIntBE,e.writeInt8=N.writeInt8,e.writeInt16LE=N.writeInt16LE,e.writeInt16BE=N.writeInt16BE,e.writeInt32LE=N.writeInt32LE,e.writeInt32BE=N.writeInt32BE,e.writeFloatLE=N.writeFloatLE,e.writeFloatBE=N.writeFloatBE,e.writeDoubleLE=N.writeDoubleLE,e.writeDoubleBE=N.writeDoubleBE,e.fill=N.fill,e.inspect=N.inspect,e.toArrayBuffer=N.toArrayBuffer,e};var O=/[^+\/0-9A-z\-]/g},{"base64-js":3,ieee754:4,"is-array":5}],3:[function(e,n,f){var o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";!function(e){"use strict";function n(e){var n=e.charCodeAt(0);return n===t||n===p?62:n===l||n===c?63:s>n?-1:s+10>n?n-s+26+26:a+26>n?n-a:u+26>n?n-u+26:void 0}function f(e){function f(e){u[p++]=e}var o,d,t,l,s,u;if(e.length%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var a=e.length;s="="===e.charAt(a-2)?2:"="===e.charAt(a-1)?1:0,u=new i(3*e.length/4-s),t=s>0?e.length-4:e.length;var p=0;for(o=0,d=0;t>o;o+=4,d+=3)l=n(e.charAt(o))<<18|n(e.charAt(o+1))<<12|n(e.charAt(o+2))<<6|n(e.charAt(o+3)),f((16711680&l)>>16),f((65280&l)>>8),f(255&l);return 2===s?(l=n(e.charAt(o))<<2|n(e.charAt(o+1))>>4,f(255&l)):1===s&&(l=n(e.charAt(o))<<10|n(e.charAt(o+1))<<4|n(e.charAt(o+2))>>2,f(l>>8&255),f(255&l)),u}function d(e){function n(e){return o.charAt(e)}function f(e){return n(e>>18&63)+n(e>>12&63)+n(e>>6&63)+n(63&e)}var d,i,t,l=e.length%3,s="";for(d=0,t=e.length-l;t>d;d+=3)i=(e[d]<<16)+(e[d+1]<<8)+e[d+2],s+=f(i);switch(l){case 1:i=e[e.length-1],s+=n(i>>2),s+=n(i<<4&63),s+="==";break;case 2:i=(e[e.length-2]<<8)+e[e.length-1],s+=n(i>>10),s+=n(i>>4&63),s+=n(i<<2&63),s+="="}return s}var i="undefined"!=typeof Uint8Array?Uint8Array:Array,t="+".charCodeAt(0),l="/".charCodeAt(0),s="0".charCodeAt(0),u="a".charCodeAt(0),a="A".charCodeAt(0),p="-".charCodeAt(0),c="_".charCodeAt(0);e.toByteArray=f,e.fromByteArray=d}("undefined"==typeof f?this.base64js={}:f)},{}],4:[function(e,n,f){f.read=function(e,n,f,o,d){var i,t,l=8*d-o-1,s=(1<<l)-1,u=s>>1,a=-7,p=f?d-1:0,c=f?-1:1,y=e[n+p];for(p+=c,i=y&(1<<-a)-1,y>>=-a,a+=l;a>0;i=256*i+e[n+p],p+=c,a-=8);for(t=i&(1<<-a)-1,i>>=-a,a+=o;a>0;t=256*t+e[n+p],p+=c,a-=8);if(0===i)i=1-u;else{if(i===s)return t?0/0:1/0*(y?-1:1);t+=Math.pow(2,o),i-=u}return(y?-1:1)*t*Math.pow(2,i-o)},f.write=function(e,n,f,o,d,i){var t,l,s,u=8*i-d-1,a=(1<<u)-1,p=a>>1,c=23===d?Math.pow(2,-24)-Math.pow(2,-77):0,y=o?0:i-1,m=o?1:-1,r=0>n||0===n&&0>1/n?1:0;for(n=Math.abs(n),isNaN(n)||1/0===n?(l=isNaN(n)?1:0,t=a):(t=Math.floor(Math.log(n)/Math.LN2),n*(s=Math.pow(2,-t))<1&&(t--,s*=2),n+=t+p>=1?c/s:c*Math.pow(2,1-p),n*s>=2&&(t++,s/=2),t+p>=a?(l=0,t=a):t+p>=1?(l=(n*s-1)*Math.pow(2,d),t+=p):(l=n*Math.pow(2,p-1)*Math.pow(2,d),t=0));d>=8;e[f+y]=255&l,y+=m,l/=256,d-=8);for(t=t<<d|l,u+=d;u>0;e[f+y]=255&t,y+=m,t/=256,u-=8);e[f+y-m]|=128*r}},{}],5:[function(e,n){var f=Array.isArray,o=Object.prototype.toString;n.exports=f||function(e){return!!e&&"[object Array]"==o.call(e)}},{}],6:[function(require,module,exports){(function(Buffer){var fabric=fabric||{version:"1.4.11"};"undefined"!=typeof exports&&(exports.fabric=fabric),"undefined"!=typeof document&&"undefined"!=typeof window?(fabric.document=document,fabric.window=window):(fabric.document=require("jsdom").jsdom("<!DOCTYPE html><html><head></head><body></body></html>"),fabric.window=fabric.document.createWindow()),fabric.isTouchSupported="ontouchstart"in fabric.document.documentElement,fabric.isLikelyNode="undefined"!=typeof Buffer&&"undefined"==typeof window,fabric.SHARED_ATTRIBUTES=["display","transform","fill","fill-opacity","fill-rule","opacity","stroke","stroke-dasharray","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width"],fabric.DPI=96;var Cufon=function(){function e(e){var n=this.face=e.face;this.glyphs=e.glyphs,this.w=e.w,this.baseSize=parseInt(n["units-per-em"],10),this.family=n["font-family"].toLowerCase(),this.weight=n["font-weight"],this.style=n["font-style"]||"normal",this.viewBox=function(){var e=n.bbox.split(/\s+/),f={minX:parseInt(e[0],10),minY:parseInt(e[1],10),maxX:parseInt(e[2],10),maxY:parseInt(e[3],10)};return f.width=f.maxX-f.minX,f.height=f.maxY-f.minY,f.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},f}(),this.ascent=-parseInt(n.ascent,10),this.descent=-parseInt(n.descent,10),this.height=-this.ascent+this.descent}function n(){var e={},n={oblique:"italic",italic:"oblique"};this.add=function(n){(e[n.style]||(e[n.style]={}))[n.weight]=n},this.get=function(f,o){var d=e[f]||e[n[f]]||e.normal||e.italic||e.oblique;if(!d)return null;if(o={normal:400,bold:700}[o]||parseInt(o,10),d[o])return d[o];var i,t,l={1:1,99:0}[o%100],s=[];void 0===l&&(l=o>400),500==o&&(o=400);for(var u in d)u=parseInt(u,10),(!i||i>u)&&(i=u),(!t||u>t)&&(t=u),s.push(u);return i>o&&(o=i),o>t&&(o=t),s.sort(function(e,n){return(l?e>o&&n>o?n>e:e>n:o>e&&o>n?e>n:n>e)?-1:1}),d[s[0]]}}function f(){function e(e,n){return e.contains?e.contains(n):16&e.compareDocumentPosition(n)}function n(n){var f=n.relatedTarget;f&&!e(this,f)&&o(this)}function f(){o(this)}function o(e){setTimeout(function(){y.replace(e,w.get(e).options,!0)},10)}this.attach=function(e){void 0===e.onmouseenter?(i(e,"mouseover",n),i(e,"mouseout",n)):(i(e,"mouseenter",f),i(e,"mouseleave",f))}}function o(){function e(e){return e.cufid||(e.cufid=++f)}var n={},f=0;this.get=function(f){var o=e(f);return n[o]||(n[o]={})}}function d(e){var n={},f={};this.get=function(f){return void 0!=n[f]?n[f]:e[f]},this.getSize=function(e,n){return f[e]||(f[e]=new r.Size(this.get(e),n))},this.extend=function(e){for(var f in e)n[f]=e[f];return this}}function i(e,n,f){e.addEventListener?e.addEventListener(n,f,!1):e.attachEvent&&e.attachEvent("on"+n,function(){return f.call(e,fabric.window.event)})}function t(e,n){var f=w.get(e);return f.options?e:(n.hover&&n.hoverables[e.nodeName.toLowerCase()]&&b.attach(e),f.options=n,e)}function l(e){var n={};return function(f){return n.hasOwnProperty(f)||(n[f]=e.apply(null,arguments)),n[f]}}function s(e,n){n||(n=r.getStyle(e));for(var f,o=r.quotedList(n.get("fontFamily").toLowerCase()),d=0,i=o.length;i>d;++d)if(f=o[d],x[f])return x[f].get(n.get("fontStyle"),n.get("fontWeight"));return null}function u(e){return fabric.document.getElementsByTagName(e)}function a(){for(var e,n={},f=0,o=arguments.length;o>f;++f)for(e in arguments[f])n[e]=arguments[f][e];return n}function p(e,n,f,o,d,i){var t=o.separate;if("none"==t)return h[o.engine].apply(null,arguments);var l,s=fabric.document.createDocumentFragment(),u=n.split(k[t]),a="words"==t;a&&v&&(/^\s/.test(n)&&u.unshift(""),/\s$/.test(n)&&u.push(""));for(var p=0,c=u.length;c>p;++p)l=h[o.engine](e,a?r.textAlign(u[p],f,p,c):u[p],f,o,d,i,c-1>p),l&&s.appendChild(l);return s}function c(e,n){for(var f,o,d,i,l=t(e,n).firstChild;l;l=d){if(d=l.nextSibling,i=!1,1==l.nodeType){if(!l.firstChild)continue;if(!/cufon/.test(l.className)){arguments.callee(l,n);continue}i=!0}if(o||(o=r.getStyle(e).extend(n)),f||(f=s(e,o)),f)if(i)h[n.engine](f,null,o,n,l,e);else{var u=l.data;if("undefined"!=typeof G_vmlCanvasManager&&(u=u.replace(/\r/g,"\n")),""!==u){var a=p(f,u,o,n,l,e);a?l.parentNode.replaceChild(a,l):l.parentNode.removeChild(l)}}}}var y=function(){return y.replace.apply(null,arguments)},m=y.DOM={ready:function(){var e=!1,n={loaded:1,complete:1},f=[],o=function(){if(!e){e=!0;for(var n;n=f.shift();n());}};return fabric.document.addEventListener&&(fabric.document.addEventListener("DOMContentLoaded",o,!1),fabric.window.addEventListener("pageshow",o,!1)),!fabric.window.opera&&fabric.document.readyState&&function(){n[fabric.document.readyState]?o():setTimeout(arguments.callee,10)}(),fabric.document.readyState&&fabric.document.createStyleSheet&&function(){try{fabric.document.body.doScroll("left"),o()}catch(e){setTimeout(arguments.callee,1)}}(),i(fabric.window,"load",o),function(n){arguments.length?e?n():f.push(n):o()}}()},r=y.CSS={Size:function(e,n){this.value=parseFloat(e),this.unit=String(e).match(/[a-z%]*$/)[0]||"px",this.convert=function(e){return e/n*this.value},this.convertFrom=function(e){return e/this.value*n},this.toString=function(){return this.value+this.unit}},getStyle:function(e){return new d(e.style)},quotedList:l(function(e){for(var n,f=[],o=/\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g;n=o.exec(e);)f.push(n[3]||n[1]);return f}),ready:function(){var e=!1,n=[],f=function(){e=!0;for(var f;f=n.shift();f());},o=Object.prototype.propertyIsEnumerable?u("style"):{length:0},d=u("link");return m.ready(function(){for(var e,n=0,i=0,t=d.length;e=d[i],t>i;++i)e.disabled||"stylesheet"!=e.rel.toLowerCase()||++n;fabric.document.styleSheets.length>=o.length+n?f():setTimeout(arguments.callee,10)}),function(f){e?f():n.push(f)}}(),supports:function(e,n){var f=fabric.document.createElement("span").style;return void 0===f[e]?!1:(f[e]=n,f[e]===n)},textAlign:function(e,n,f,o){return"right"==n.get("textAlign")?f>0&&(e=" "+e):o-1>f&&(e+=" "),e},textDecoration:function(e,n){n||(n=this.getStyle(e));for(var f={underline:null,overline:null,"line-through":null},o=e;o.parentNode&&1==o.parentNode.nodeType;){var d=!0;for(var i in f)f[i]||(-1!=n.get("textDecoration").indexOf(i)&&(f[i]=n.get("color")),d=!1);if(d)break;n=this.getStyle(o=o.parentNode)}return f},textShadow:l(function(e){if("none"==e)return null;for(var n,f=[],o={},d=0,i=/(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/gi;n=i.exec(e);)","==n[0]?(f.push(o),o={},d=0):n[1]?o.color=n[1]:o[["offX","offY","blur"][d++]]=n[2];return f.push(o),f}),color:l(function(e){var n={};return n.color=e.replace(/^rgba\((.*?),\s*([\d.]+)\)/,function(e,f,o){return n.opacity=parseFloat(o),"rgb("+f+")"}),n}),textTransform:function(e,n){return e[{uppercase:"toUpperCase",lowercase:"toLowerCase"}[n.get("textTransform")]||"toString"]()}},v=0==" ".split(/\s+/).length,w=new o,b=new f,g=[],h={},x={},j={engine:null,hover:!1,hoverables:{a:!0},printable:!0,selector:fabric.window.Sizzle||fabric.window.jQuery&&function(e){return jQuery(e)}||fabric.window.dojo&&dojo.query||fabric.window.$$&&function(e){return $$(e)}||fabric.window.$&&function(e){return $(e)}||fabric.document.querySelectorAll&&function(e){return fabric.document.querySelectorAll(e)}||u,separate:"words",textShadow:"none"},k={words:/\s+/,characters:""};return y.now=function(){return m.ready(),y},y.refresh=function(){for(var e=g.splice(0,g.length),n=0,f=e.length;f>n;++n)y.replace.apply(null,e[n]);return y},y.registerEngine=function(e,n){return n?(h[e]=n,y.set("engine",e)):y},y.registerFont=function(f){var o=new e(f),d=o.family;return x[d]||(x[d]=new n),x[d].add(o),y.set("fontFamily",'"'+d+'"')},y.replace=function(e,n,f){return n=a(j,n),n.engine?("string"==typeof n.textShadow&&n.textShadow&&(n.textShadow=r.textShadow(n.textShadow)),f||g.push(arguments),(e.nodeType||"string"==typeof e)&&(e=[e]),r.ready(function(){for(var f=0,o=e.length;o>f;++f){var d=e[f];"string"==typeof d?y.replace(n.selector(d),n,!0):c(d,n)}}),y):y},y.replaceElement=function(e,n){return n=a(j,n),"string"==typeof n.textShadow&&n.textShadow&&(n.textShadow=r.textShadow(n.textShadow)),c(e,n)},y.engines=h,y.fonts=x,y.getOptions=function(){return a(j)},y.set=function(e,n){return j[e]=n,y},y}();Cufon.registerEngine("canvas",function(){function e(e,n){var f,o=0,d=0,i=[],t=/([mrvxe])([^a-z]*)/g;e:for(var l=0;f=t.exec(e);++l){var s=f[2].split(",");switch(f[1]){case"v":i[l]={m:"bezierCurveTo",a:[o+~~s[0],d+~~s[1],o+~~s[2],d+~~s[3],o+=~~s[4],d+=~~s[5]]};break;case"r":i[l]={m:"lineTo",a:[o+=~~s[0],d+=~~s[1]]};break;case"m":i[l]={m:"moveTo",a:[o=~~s[0],d=~~s[1]]};break;case"x":i[l]={m:"closePath",a:[]};break;case"e":break e}n[i[l].m].apply(n,i[l].a)}return i}function n(e,n){for(var f=0,o=e.length;o>f;++f){var d=e[f];n[d.m].apply(n,d.a)}}var f=Cufon.CSS.supports("display","inline-block"),o=!f&&("BackCompat"==fabric.document.compatMode||/frameset|transitional/i.test(fabric.document.doctype.publicId)),d=fabric.document.createElement("style");d.type="text/css";var i=fabric.document.createTextNode(".cufon-canvas{text-indent:0}@media screen,projection{.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle"+(o?"":";font-size:1px;line-height:1px")+"}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}"+(f?".cufon-canvas canvas{position:relative}":".cufon-canvas canvas{position:absolute}")+"}@media print{.cufon-canvas{padding:0 !important}.cufon-canvas canvas{display:none}.cufon-canvas .cufon-alt{display:inline}}");try{d.appendChild(i)}catch(t){d.setAttribute("type","text/css"),d.styleSheet.cssText=i.data}return fabric.document.getElementsByTagName("head")[0].appendChild(d),function(o,d,i,t,l){function s(){T.save();var e=0,n=0,f=[{left:0}];t.backgroundColor&&(T.save(),T.fillStyle=t.backgroundColor,T.translate(0,o.ascent),T.fillRect(0,0,A+10,(-o.ascent+o.descent)*D),T.restore()),"right"===t.textAlign?(T.translate(G[n],0),f[0].left=G[n]*U):"center"===t.textAlign&&(T.translate(G[n]/2,0),f[0].left=G[n]/2*U);for(var d=0,i=z.length;i>d;++d)if("\n"!==z[d]){var l=o.glyphs[z[d]]||o.missingGlyph;if(l){var s=Number(l.w||o.w)+y;t.textBackgroundColor&&(T.save(),T.fillStyle=t.textBackgroundColor,T.translate(0,o.ascent),T.fillRect(0,0,s+10,-o.ascent+o.descent),T.restore()),T.translate(s,0),e+=s,d==i-1&&(f[f.length-1].width=e*U,f[f.length-1].height=(-o.ascent+o.descent)*U)}}else{n++;var u=-o.ascent-o.ascent/5*t.lineHeight,a=f[f.length-1],p={left:0};a.width=e*U,a.height=(-o.ascent+o.descent)*U,"right"===t.textAlign?(T.translate(-A,u),T.translate(G[n],0),p.left=G[n]*U):"center"===t.textAlign?(T.translate(-e-G[n-1]/2,u),T.translate(G[n]/2,0),p.left=G[n]/2*U):T.translate(-e,u),f.push(p),e=0}T.restore(),Cufon.textOptions.boundaries=f}function u(f){T.fillStyle=f||Cufon.textOptions.color||i.get("color");var d=0,l=0;"right"===t.textAlign?T.translate(G[l],0):"center"===t.textAlign&&T.translate(G[l]/2,0);for(var s=0,u=z.length;u>s;++s)if("\n"!==z[s]){var a=o.glyphs[z[s]]||o.missingGlyph;if(a){var p=Number(a.w||o.w)+y;W&&(T.save(),T.strokeStyle=T.fillStyle,T.lineWidth+=T.lineWidth,T.beginPath(),W.underline&&(T.moveTo(0,-o.face["underline-position"]+.5),T.lineTo(p,-o.face["underline-position"]+.5)),W.overline&&(T.moveTo(0,o.ascent+.5),T.lineTo(p,o.ascent+.5)),W["line-through"]&&(T.moveTo(0,-o.descent+.5),T.lineTo(p,-o.descent+.5)),T.stroke(),T.restore()),X&&(T.save(),T.transform(1,0,-.25,1,0,0)),T.beginPath(),a.d&&(a.code?n(a.code,T):a.code=e("m"+a.d,T)),T.fill(),t.strokeStyle&&(T.closePath(),T.save(),T.lineWidth=t.strokeWidth,T.strokeStyle=t.strokeStyle,T.stroke(),T.restore()),X&&T.restore(),T.translate(p,0),d+=p}}else{l++;var c=-o.ascent-o.ascent/5*t.lineHeight;"right"===t.textAlign?(T.translate(-A,c),T.translate(G[l],0)):"center"===t.textAlign?(T.translate(-d-G[l-1]/2,c),T.translate(G[l]/2,0)):T.translate(-d,c),d=0}}var a=null===d,p=o.viewBox,c=i.getSize("fontSize",o.baseSize),y=i.get("letterSpacing");y="normal"==y?0:c.convertFrom(parseInt(y,10));var m=0,r=0,v=0,w=0,b=t.textShadow,g=[];if(Cufon.textOptions.shadowOffsets=[],Cufon.textOptions.shadows=null,b){Cufon.textOptions.shadows=b;for(var h=0,x=b.length;x>h;++h){var j=b[h],k=c.convertFrom(parseFloat(j.offX)),q=c.convertFrom(parseFloat(j.offY));g[h]=[k,q]}}for(var z=Cufon.CSS.textTransform(a?l.alt:d,i).split(""),A=0,B=null,C=0,D=1,E=[],h=0,x=z.length;x>h;++h)if("\n"!==z[h]){var F=o.glyphs[z[h]]||o.missingGlyph;F&&(A+=B=Number(F.w||o.w)+y)}else D++,A>C&&(C=A),E.push(A),A=0;E.push(A),A=Math.max(C,A);for(var G=[],h=E.length;h--;)G[h]=A-E[h];if(null===B)return null;r+=p.width-B,w+=p.minX;var H,I;if(a)H=l,I=l.firstChild;else if(H=fabric.document.createElement("span"),H.className="cufon cufon-canvas",H.alt=d,I=fabric.document.createElement("canvas"),H.appendChild(I),t.printable){var J=fabric.document.createElement("span");
-J.className="cufon-alt",J.appendChild(fabric.document.createTextNode(d)),H.appendChild(J)}var K=H.style,L=I.style||{},M=c.convert(p.height-m+v),N=Math.ceil(M),O=N/M;I.width=Math.ceil(c.convert(A+r-w)*O),I.height=N,m+=p.minY,L.top=Math.round(c.convert(m-o.ascent))+"px",L.left=Math.round(c.convert(w))+"px";var P=Math.ceil(c.convert(A*O)),Q=P+"px",R=c.convert(o.height),S=(t.lineHeight-1)*c.convert(-o.ascent/5)*(D-1);Cufon.textOptions.width=P,Cufon.textOptions.height=R*D+S,Cufon.textOptions.lines=D,Cufon.textOptions.totalLineHeight=S,f?(K.width=Q,K.height=R+"px"):(K.paddingLeft=Q,K.paddingBottom=R-1+"px");var T=Cufon.textOptions.context||I.getContext("2d"),U=N/p.height;Cufon.textOptions.fontAscent=o.ascent*U,Cufon.textOptions.boundaries=null;for(var V=Cufon.textOptions.shadowOffsets,h=g.length;h--;)V[h]=[g[h][0]*U,g[h][1]*U];T.save(),T.scale(U,U),T.translate(-w-1/U*I.width/2+(Cufon.fonts[o.family].offsetLeft||0),-m-Cufon.textOptions.height/U/2+(Cufon.fonts[o.family].offsetTop||0)),T.lineWidth=o.face["underline-thickness"],T.save();var W=Cufon.getTextDecoration(t),X="italic"===t.fontStyle;if(T.save(),s(),b)for(var h=0,x=b.length;x>h;++h){var j=b[h];T.save(),T.translate.apply(T,g[h]),u(j.color),T.restore()}return u(),T.restore(),T.restore(),T.restore(),H}}()),Cufon.registerEngine("vml",function(){function e(e,f){return n(e,/(?:em|ex|%)$/i.test(f)?"1em":f)}function n(e,n){if(/px$/i.test(n))return parseFloat(n);var f=e.style.left,o=e.runtimeStyle.left;e.runtimeStyle.left=e.currentStyle.left,e.style.left=n;var d=e.style.pixelLeft;return e.style.left=f,e.runtimeStyle.left=o,d}if(fabric.document.namespaces){var f=fabric.document.createElement("canvas");if(!(f&&f.getContext&&f.getContext.apply)){null==fabric.document.namespaces.cvml&&fabric.document.namespaces.add("cvml","urn:schemas-microsoft-com:vml");var o=fabric.document.createElement("cvml:shape");if(o.style.behavior="url(#default#VML)",o.coordsize)return o=null,fabric.document.write('<style type="text/css">.cufon-vml-canvas{text-indent:0}@media screen{cvml\\:shape,cvml\\:shadow{behavior:url(#default#VML);display:block;antialias:true;position:absolute}.cufon-vml-canvas{position:absolute;text-align:left}.cufon-vml{display:inline-block;position:relative;vertical-align:middle}.cufon-vml .cufon-alt{position:absolute;left:-10000in;font-size:1px}a .cufon-vml{cursor:pointer}}@media print{.cufon-vml *{display:none}.cufon-vml .cufon-alt{display:inline}}</style>'),function(f,o,d,i,t,l,s){var u=null===o;u&&(o=t.alt);var a=f.viewBox,p=d.computedFontSize||(d.computedFontSize=new Cufon.CSS.Size(e(l,d.get("fontSize"))+"px",f.baseSize)),c=d.computedLSpacing;void 0==c&&(c=d.get("letterSpacing"),d.computedLSpacing=c="normal"==c?0:~~p.convertFrom(n(l,c)));var y,m;if(u)y=t,m=t.firstChild;else{if(y=fabric.document.createElement("span"),y.className="cufon cufon-vml",y.alt=o,m=fabric.document.createElement("span"),m.className="cufon-vml-canvas",y.appendChild(m),i.printable){var r=fabric.document.createElement("span");r.className="cufon-alt",r.appendChild(fabric.document.createTextNode(o)),y.appendChild(r)}s||y.appendChild(fabric.document.createElement("cvml:shape"))}var v=y.style,w=m.style,b=p.convert(a.height),g=Math.ceil(b),h=g/b,x=a.minX,j=a.minY;w.height=g,w.top=Math.round(p.convert(j-f.ascent)),w.left=Math.round(p.convert(x)),v.height=p.convert(f.height)+"px";for(var k,q,z=(Cufon.getTextDecoration(i),d.get("color")),A=Cufon.CSS.textTransform(o,d).split(""),B=0,C=0,D=null,E=i.textShadow,F=0,G=0,H=A.length;H>F;++F)k=f.glyphs[A[F]]||f.missingGlyph,k&&(B+=D=~~(k.w||f.w)+c);if(null===D)return null;var I,J=-x+B+(a.width-D),K=p.convert(J*h),L=Math.round(K),M=J+","+a.height,N="r"+M+"nsnf";for(F=0;H>F;++F)if(k=f.glyphs[A[F]]||f.missingGlyph){u?(q=m.childNodes[G],q.firstChild&&q.removeChild(q.firstChild)):(q=fabric.document.createElement("cvml:shape"),m.appendChild(q)),q.stroked="f",q.coordsize=M,q.coordorigin=I=x-C+","+j,q.path=(k.d?"m"+k.d+"xe":"")+"m"+I+N,q.fillcolor=z;var O=q.style;if(O.width=L,O.height=g,E){var P,Q=E[0],R=E[1],S=Cufon.CSS.color(Q.color),T=fabric.document.createElement("cvml:shadow");T.on="t",T.color=S.color,T.offset=Q.offX+","+Q.offY,R&&(P=Cufon.CSS.color(R.color),T.type="double",T.color2=P.color,T.offset2=R.offX+","+R.offY),T.opacity=S.opacity||P&&P.opacity||1,q.appendChild(T)}C+=~~(k.w||f.w)+c,++G}return v.width=Math.max(Math.ceil(p.convert(B*h)),0),y}}}}()),Cufon.getTextDecoration=function(e){return{underline:"underline"===e.textDecoration,overline:"overline"===e.textDecoration,"line-through":"line-through"===e.textDecoration}},"undefined"!=typeof exports&&(exports.Cufon=Cufon),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(e){return 10>e?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var n=meta[e];return"string"==typeof n?n:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,n){var f,o,d,i,t,l=gap,s=n[e];switch(s&&"object"==typeof s&&"function"==typeof s.toJSON&&(s=s.toJSON(e)),"function"==typeof rep&&(s=rep.call(n,e,s)),typeof s){case"string":return quote(s);case"number":return isFinite(s)?String(s):"null";case"boolean":case"null":return String(s);case"object":if(!s)return"null";if(gap+=indent,t=[],"[object Array]"===Object.prototype.toString.apply(s)){for(i=s.length,f=0;i>f;f+=1)t[f]=str(f,s)||"null";return d=0===t.length?"[]":gap?"[\n"+gap+t.join(",\n"+gap)+"\n"+l+"]":"["+t.join(",")+"]",gap=l,d}if(rep&&"object"==typeof rep)for(i=rep.length,f=0;i>f;f+=1)"string"==typeof rep[f]&&(o=rep[f],d=str(o,s),d&&t.push(quote(o)+(gap?": ":":")+d));else for(o in s)Object.prototype.hasOwnProperty.call(s,o)&&(d=str(o,s),d&&t.push(quote(o)+(gap?": ":":")+d));return d=0===t.length?"{}":gap?"{\n"+gap+t.join(",\n"+gap)+"\n"+l+"}":"{"+t.join(",")+"}",gap=l,d}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx,escapable,gap,indent,meta,rep;"function"!=typeof JSON.stringify&&(escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,meta={"\b":"\\b","	":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},JSON.stringify=function(e,n,f){var o;if(gap="",indent="","number"==typeof f)for(o=0;f>o;o+=1)indent+=" ";else"string"==typeof f&&(indent=f);if(rep=n,n&&"function"!=typeof n&&("object"!=typeof n||"number"!=typeof n.length))throw new Error("JSON.stringify");return str("",{"":e})}),"function"!=typeof JSON.parse&&(cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,JSON.parse=function(text,reviver){function walk(e,n){var f,o,d=e[n];if(d&&"object"==typeof d)for(f in d)Object.prototype.hasOwnProperty.call(d,f)&&(o=walk(d,f),void 0!==o?d[f]=o:delete d[f]);return reviver.call(e,n,d)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),function(){function e(e,n){this.__eventListeners[e]&&(n?fabric.util.removeFromArray(this.__eventListeners[e],n):this.__eventListeners[e].length=0)}function n(e,n){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var f in e)this.on(f,e[f]);else this.__eventListeners[e]||(this.__eventListeners[e]=[]),this.__eventListeners[e].push(n);return this}function f(n,f){if(this.__eventListeners){if(0===arguments.length)this.__eventListeners={};else if(1===arguments.length&&"object"==typeof arguments[0])for(var o in n)e.call(this,o,n[o]);else e.call(this,n,f);return this}}function o(e,n){if(this.__eventListeners){var f=this.__eventListeners[e];if(f){for(var o=0,d=f.length;d>o;o++)f[o].call(this,n||{});return this}}}fabric.Observable={observe:n,stopObserving:f,fire:o,on:n,off:f,trigger:o}}(),fabric.Collection={add:function(){this._objects.push.apply(this._objects,arguments);for(var e=0,n=arguments.length;n>e;e++)this._onObjectAdded(arguments[e]);return this.renderOnAddRemove&&this.renderAll(),this},insertAt:function(e,n,f){var o=this.getObjects();return f?o[n]=e:o.splice(n,0,e),this._onObjectAdded(e),this.renderOnAddRemove&&this.renderAll(),this},remove:function(){for(var e,n=this.getObjects(),f=0,o=arguments.length;o>f;f++)e=n.indexOf(arguments[f]),-1!==e&&(n.splice(e,1),this._onObjectRemoved(arguments[f]));return this.renderOnAddRemove&&this.renderAll(),this},forEachObject:function(e,n){for(var f=this.getObjects(),o=f.length;o--;)e.call(n,f[o],o,f);return this},getObjects:function(e){return"undefined"==typeof e?this._objects:this._objects.filter(function(n){return n.type===e})},item:function(e){return this.getObjects()[e]},isEmpty:function(){return 0===this.getObjects().length},size:function(){return this.getObjects().length},contains:function(e){return this.getObjects().indexOf(e)>-1},complexity:function(){return this.getObjects().reduce(function(e,n){return e+=n.complexity?n.complexity():0},0)}},function(e){var n=Math.sqrt,f=Math.atan2,o=Math.PI/180;fabric.util={removeFromArray:function(e,n){var f=e.indexOf(n);return-1!==f&&e.splice(f,1),e},getRandomInt:function(e,n){return Math.floor(Math.random()*(n-e+1))+e},degreesToRadians:function(e){return e*o},radiansToDegrees:function(e){return e/o},rotatePoint:function(e,n,f){var o=Math.sin(f),d=Math.cos(f);e.subtractEquals(n);var i=e.x*d-e.y*o,t=e.x*o+e.y*d;return new fabric.Point(i,t).addEquals(n)},transformPoint:function(e,n,f){return f?new fabric.Point(n[0]*e.x+n[1]*e.y,n[2]*e.x+n[3]*e.y):new fabric.Point(n[0]*e.x+n[1]*e.y+n[4],n[2]*e.x+n[3]*e.y+n[5])},invertTransform:function(e){var n=e.slice(),f=1/(e[0]*e[3]-e[1]*e[2]);n=[f*e[3],-f*e[1],-f*e[2],f*e[0],0,0];var o=fabric.util.transformPoint({x:e[4],y:e[5]},n);return n[4]=-o.x,n[5]=-o.y,n},toFixed:function(e,n){return parseFloat(Number(e).toFixed(n))},parseUnit:function(e){var n=/\D{0,2}$/.exec(e),f=parseFloat(e);switch(n[0]){case"mm":return f*fabric.DPI/25.4;case"cm":return f*fabric.DPI/2.54;case"in":return f*fabric.DPI;case"pt":return f*fabric.DPI/72;case"pc":return f*fabric.DPI/72*12;default:return f}},falseFunction:function(){return!1},getKlass:function(e,n){return e=fabric.util.string.camelize(e.charAt(0).toUpperCase()+e.slice(1)),fabric.util.resolveNamespace(n)[e]},resolveNamespace:function(n){if(!n)return fabric;for(var f=n.split("."),o=f.length,d=e||fabric.window,i=0;o>i;++i)d=d[f[i]];return d},loadImage:function(e,n,f,o){if(!e)return void(n&&n.call(f,e));var d=fabric.util.createImage();d.onload=function(){n&&n.call(f,d),d=d.onload=d.onerror=null},d.onerror=function(){fabric.log("Error loading "+d.src),n&&n.call(f,null,!0),d=d.onload=d.onerror=null},0!==e.indexOf("data")&&"undefined"!=typeof o&&(d.crossOrigin=o),d.src=e},enlivenObjects:function(e,n,f,o){function d(){++t===l&&n&&n(i)}e=e||[];var i=[],t=0,l=e.length;return l?void e.forEach(function(e,n){if(!e||!e.type)return void d();var t=fabric.util.getKlass(e.type,f);t.async?t.fromObject(e,function(f,t){t||(i[n]=f,o&&o(e,i[n])),d()}):(i[n]=t.fromObject(e),o&&o(e,i[n]),d())}):void(n&&n(i))},groupSVGElements:function(e,n,f){var o;return o=new fabric.PathGroup(e,n),"undefined"!=typeof f&&o.setSourcePath(f),o},populateWithProperties:function(e,n,f){if(f&&"[object Array]"===Object.prototype.toString.call(f))for(var o=0,d=f.length;d>o;o++)f[o]in e&&(n[f[o]]=e[f[o]])},drawDashedLine:function(e,o,d,i,t,l){var s=i-o,u=t-d,a=n(s*s+u*u),p=f(u,s),c=l.length,y=0,m=!0;for(e.save(),e.translate(o,d),e.moveTo(0,0),e.rotate(p),o=0;a>o;)o+=l[y++%c],o>a&&(o=a),e[m?"lineTo":"moveTo"](o,0),m=!m;e.restore()},createCanvasElement:function(e){return e||(e=fabric.document.createElement("canvas")),e.getContext||"undefined"==typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(e),e},createImage:function(){return fabric.isLikelyNode?new(require("canvas").Image):fabric.document.createElement("img")},createAccessors:function(e){for(var n=e.prototype,f=n.stateProperties.length;f--;){var o=n.stateProperties[f],d=o.charAt(0).toUpperCase()+o.slice(1),i="set"+d,t="get"+d;n[t]||(n[t]=function(e){return new Function('return this.get("'+e+'")')}(o)),n[i]||(n[i]=function(e){return new Function("value",'return this.set("'+e+'", value)')}(o))}},clipContext:function(e,n){n.save(),n.beginPath(),e.clipTo(n),n.clip()},multiplyTransformMatrices:function(e,n){for(var f=[[e[0],e[2],e[4]],[e[1],e[3],e[5]],[0,0,1]],o=[[n[0],n[2],n[4]],[n[1],n[3],n[5]],[0,0,1]],d=[],i=0;3>i;i++){d[i]=[];for(var t=0;3>t;t++){for(var l=0,s=0;3>s;s++)l+=f[i][s]*o[s][t];d[i][t]=l}}return[d[0][0],d[1][0],d[0][1],d[1][1],d[0][2],d[1][2]]},getFunctionBody:function(e){return(String(e).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},isTransparent:function(e,n,f,o){o>0&&(n>o?n-=o:n=0,f>o?f-=o:f=0);for(var d=!0,i=e.getImageData(n,f,2*o||1,2*o||1),t=3,l=i.data.length;l>t;t+=4){var s=i.data[t];if(d=0>=s,d===!1)break}return i=null,d}}}("undefined"!=typeof exports?exports:this),function(){function e(e,d,t,l,s,u,a){var p=i.call(arguments);if(o[p])return o[p];var c=Math.PI,y=a*(c/180),m=Math.sin(y),r=Math.cos(y),v=0,w=0;t=Math.abs(t),l=Math.abs(l);var b=-r*e-m*d,g=-r*d+m*e,h=t*t,x=l*l,j=g*g,k=b*b,q=4*h*x-h*j-x*k,z=0;if(0>q){var A=Math.sqrt(1-.25*q/(h*x));t*=A,l*=A}else z=(s===u?-.5:.5)*Math.sqrt(q/(h*j+x*k));var B=z*t*g/l,C=-z*l*b/t,D=r*B-m*C+e/2,E=m*B+r*C+d/2,F=f(1,0,(b-B)/t,(g-C)/l),G=f((b-B)/t,(g-C)/l,(-b-B)/t,(-g-C)/l);0===u&&G>0?G-=2*c:1===u&&0>G&&(G+=2*c);for(var H=Math.ceil(Math.abs(G/(.5*c))),I=[],J=G/H,K=8/3*Math.sin(J/4)*Math.sin(J/4)/Math.sin(J/2),L=F+J,M=0;H>M;M++)I[M]=n(F,L,r,m,t,l,D,E,K,v,w),v=I[M][4],w=I[M][5],F+=J,L+=J;return o[p]=I,I}function n(e,n,f,o,t,l,s,u,a,p,c){var y=i.call(arguments);if(d[y])return d[y];var m=Math.cos(e),r=Math.sin(e),v=Math.cos(n),w=Math.sin(n),b=f*t*v-o*l*w+s,g=o*t*v+f*l*w+u,h=p+a*(-f*t*r-o*l*m),x=c+a*(-o*t*r+f*l*m),j=b+a*(f*t*w+o*l*v),k=g+a*(o*t*w-f*l*v);return d[y]=[h,x,j,k,b,g],d[y]}function f(e,n,f,o){var d=Math.atan2(n,e),i=Math.atan2(o,f);return i>=d?i-d:2*Math.PI-(d-i)}var o={},d={},i=Array.prototype.join;fabric.util.drawArc=function(n,f,o,d){for(var i=d[0],t=d[1],l=d[2],s=d[3],u=d[4],a=d[5],p=d[6],c=[[],[],[],[]],y=e(a-f,p-o,i,t,s,u,l),m=0,r=y.length;r>m;m++)c[m][0]=y[m][0]+f,c[m][1]=y[m][1]+o,c[m][2]=y[m][2]+f,c[m][3]=y[m][3]+o,c[m][4]=y[m][4]+f,c[m][5]=y[m][5]+o,n.bezierCurveTo.apply(n,c[m])}}(),function(){function e(e,n){for(var f=d.call(arguments,2),o=[],i=0,t=e.length;t>i;i++)o[i]=f.length?e[i][n].apply(e[i],f):e[i][n].call(e[i]);return o}function n(e,n){return o(e,n,function(e,n){return e>=n})}function f(e,n){return o(e,n,function(e,n){return n>e})}function o(e,n,f){if(e&&0!==e.length){var o=e.length-1,d=n?e[o][n]:e[o];if(n)for(;o--;)f(e[o][n],d)&&(d=e[o][n]);else for(;o--;)f(e[o],d)&&(d=e[o]);return d}}var d=Array.prototype.slice;fabric.util.array={invoke:e,min:f,max:n}}(),function(){function e(e,n){for(var f in n)e[f]=n[f];return e}function n(n){return e({},n)}fabric.util.object={extend:e,clone:n}}(),function(){function e(e){return e.replace(/-+(.)?/g,function(e,n){return n?n.toUpperCase():""})}function n(e,n){return e.charAt(0).toUpperCase()+(n?e.slice(1):e.slice(1).toLowerCase())}function f(e){return e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}fabric.util.string={camelize:e,capitalize:n,escapeXml:f}}(),function(){function e(){}function n(e){var n=this.constructor.superclass.prototype[e];return arguments.length>1?n.apply(this,o.call(arguments,1)):n.call(this)}function f(){function f(){this.initialize.apply(this,arguments)}var i=null,l=o.call(arguments,0);"function"==typeof l[0]&&(i=l.shift()),f.superclass=i,f.subclasses=[],i&&(e.prototype=i.prototype,f.prototype=new e,i.subclasses.push(f));for(var s=0,u=l.length;u>s;s++)t(f,l[s],i);return f.prototype.initialize||(f.prototype.initialize=d),f.prototype.constructor=f,f.prototype.callSuper=n,f}var o=Array.prototype.slice,d=function(){},i=function(){for(var e in{toString:1})if("toString"===e)return!1;return!0}(),t=function(e,n,f){for(var o in n)e.prototype[o]=o in e.prototype&&"function"==typeof e.prototype[o]&&(n[o]+"").indexOf("callSuper")>-1?function(e){return function(){var o=this.constructor.superclass;this.constructor.superclass=f;var d=n[e].apply(this,arguments);return this.constructor.superclass=o,"initialize"!==e?d:void 0}}(o):n[o],i&&(n.toString!==Object.prototype.toString&&(e.prototype.toString=n.toString),n.valueOf!==Object.prototype.valueOf&&(e.prototype.valueOf=n.valueOf))};fabric.util.createClass=f}(),function(){function e(e){var n,f,o=Array.prototype.slice.call(arguments,1),d=o.length;for(f=0;d>f;f++)if(n=typeof e[o[f]],!/^(?:function|object|unknown)$/.test(n))return!1;return!0}function n(e,n){return{handler:n,wrappedHandler:f(e,n)}}function f(e,n){return function(f){n.call(t(e),f||fabric.window.event)}}function o(e,n){return function(f){if(r[e]&&r[e][n])for(var o=r[e][n],d=0,i=o.length;i>d;d++)o[d].call(this,f||fabric.window.event)}}function d(e,n){e||(e=fabric.window.event);var f=e.target||(typeof e.srcElement!==s?e.srcElement:null),o=fabric.util.getScrollLeftTop(f,n);return{x:v(e)+o.left,y:w(e)+o.top}}function i(e,n,f){var o="touchend"===e.type?"changedTouches":"touches";return e[o]&&e[o][0]?e[o][0][n]-(e[o][0][n]-e[o][0][f])||e[f]:e[f]}var t,l,s="unknown",u=function(){var e=0;return function(n){return n.__uniqueID||(n.__uniqueID="uniqueID__"+e++)}}();!function(){var e={};t=function(n){return e[n]},l=function(n,f){e[n]=f}}();var a,p,c=e(fabric.document.documentElement,"addEventListener","removeEventListener")&&e(fabric.window,"addEventListener","removeEventListener"),y=e(fabric.document.documentElement,"attachEvent","detachEvent")&&e(fabric.window,"attachEvent","detachEvent"),m={},r={};c?(a=function(e,n,f){e.addEventListener(n,f,!1)},p=function(e,n,f){e.removeEventListener(n,f,!1)}):y?(a=function(e,f,o){var d=u(e);l(d,e),m[d]||(m[d]={}),m[d][f]||(m[d][f]=[]);var i=n(d,o);m[d][f].push(i),e.attachEvent("on"+f,i.wrappedHandler)},p=function(e,n,f){var o,d=u(e);if(m[d]&&m[d][n])for(var i=0,t=m[d][n].length;t>i;i++)o=m[d][n][i],o&&o.handler===f&&(e.detachEvent("on"+n,o.wrappedHandler),m[d][n][i]=null)}):(a=function(e,n,f){var d=u(e);if(r[d]||(r[d]={}),!r[d][n]){r[d][n]=[];var i=e["on"+n];i&&r[d][n].push(i),e["on"+n]=o(d,n)}r[d][n].push(f)},p=function(e,n,f){var o=u(e);if(r[o]&&r[o][n])for(var d=r[o][n],i=0,t=d.length;t>i;i++)d[i]===f&&d.splice(i,1)}),fabric.util.addListener=a,fabric.util.removeListener=p;var v=function(e){return typeof e.clientX!==s?e.clientX:0},w=function(e){return typeof e.clientY!==s?e.clientY:0};fabric.isTouchSupported&&(v=function(e){return i(e,"pageX","clientX")},w=function(e){return i(e,"pageY","clientY")}),fabric.util.getPointer=d,fabric.util.object.extend(fabric.util,fabric.Observable)}(),function(){function e(e,n){var f=e.style;if(!f)return e;if("string"==typeof n)return e.style.cssText+=";"+n,n.indexOf("opacity")>-1?i(e,n.match(/opacity:\s*(\d?\.?\d*)/)[1]):e;for(var o in n)if("opacity"===o)i(e,n[o]);else{var d="float"===o||"cssFloat"===o?"undefined"==typeof f.styleFloat?"cssFloat":"styleFloat":o;f[d]=n[o]}return e}var n=fabric.document.createElement("div"),f="string"==typeof n.style.opacity,o="string"==typeof n.style.filter,d=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,i=function(e){return e};f?i=function(e,n){return e.style.opacity=n,e}:o&&(i=function(e,n){var f=e.style;return e.currentStyle&&!e.currentStyle.hasLayout&&(f.zoom=1),d.test(f.filter)?(n=n>=.9999?"":"alpha(opacity="+100*n+")",f.filter=f.filter.replace(d,n)):f.filter+=" alpha(opacity="+100*n+")",e}),fabric.util.setStyle=e}(),function(){function e(e){return"string"==typeof e?fabric.document.getElementById(e):e}function n(e,n){var f=fabric.document.createElement(e);for(var o in n)"class"===o?f.className=n[o]:"for"===o?f.htmlFor=n[o]:f.setAttribute(o,n[o]);return f}function f(e,n){e&&-1===(" "+e.className+" ").indexOf(" "+n+" ")&&(e.className+=(e.className?" ":"")+n)}function o(e,f,o){return"string"==typeof f&&(f=n(f,o)),e.parentNode&&e.parentNode.replaceChild(f,e),f.appendChild(e),f}function d(e,n){var f,o,d=0,i=0,t=fabric.document.documentElement,l=fabric.document.body||{scrollLeft:0,scrollTop:0};for(o=e;e&&e.parentNode&&!f;)e=e.parentNode,e!==fabric.document&&"fixed"===fabric.util.getElementStyle(e,"position")&&(f=e),e!==fabric.document&&o!==n&&"absolute"===fabric.util.getElementStyle(e,"position")?(d=0,i=0):e===fabric.document?(d=l.scrollLeft||t.scrollLeft||0,i=l.scrollTop||t.scrollTop||0):(d+=e.scrollLeft||0,i+=e.scrollTop||0);return{left:d,top:i}}function i(e){var n,f,o=e&&e.ownerDocument,d={left:0,top:0},i={left:0,top:0},t={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!o)return{left:0,top:0};for(var l in t)i[t[l]]+=parseInt(a(e,l),10)||0;return n=o.documentElement,"undefined"!=typeof e.getBoundingClientRect&&(d=e.getBoundingClientRect()),f=fabric.util.getScrollLeftTop(e,null),{left:d.left+f.left-(n.clientLeft||0)+i.left,top:d.top+f.top-(n.clientTop||0)+i.top}}var t,l=Array.prototype.slice,s=function(e){return l.call(e,0)};try{t=s(fabric.document.childNodes)instanceof Array}catch(u){}t||(s=function(e){for(var n=new Array(e.length),f=e.length;f--;)n[f]=e[f];return n});var a;a=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(e,n){return fabric.document.defaultView.getComputedStyle(e,null)[n]}:function(e,n){var f=e.style[n];return!f&&e.currentStyle&&(f=e.currentStyle[n]),f},function(){function e(e){return"undefined"!=typeof e.onselectstart&&(e.onselectstart=fabric.util.falseFunction),o?e.style[o]="none":"string"==typeof e.unselectable&&(e.unselectable="on"),e}function n(e){return"undefined"!=typeof e.onselectstart&&(e.onselectstart=null),o?e.style[o]="":"string"==typeof e.unselectable&&(e.unselectable=""),e}var f=fabric.document.documentElement.style,o="userSelect"in f?"userSelect":"MozUserSelect"in f?"MozUserSelect":"WebkitUserSelect"in f?"WebkitUserSelect":"KhtmlUserSelect"in f?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=e,fabric.util.makeElementSelectable=n}(),function(){function e(e,n){var f=fabric.document.getElementsByTagName("head")[0],o=fabric.document.createElement("script"),d=!0;o.onload=o.onreadystatechange=function(e){if(d){if("string"==typeof this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)return;d=!1,n(e||fabric.window.event),o=o.onload=o.onreadystatechange=null}},o.src=e,f.appendChild(o)}fabric.util.getScript=e}(),fabric.util.getById=e,fabric.util.toArray=s,fabric.util.makeElement=n,fabric.util.addClass=f,fabric.util.wrapElement=o,fabric.util.getScrollLeftTop=d,fabric.util.getElementOffset=i,fabric.util.getElementStyle=a}(),function(){function e(e,n){return e+(/\?/.test(e)?"&":"?")+n}function n(){}function f(f,d){d||(d={});var i,t=d.method?d.method.toUpperCase():"GET",l=d.onComplete||function(){},s=o();return s.onreadystatechange=function(){4===s.readyState&&(l(s),s.onreadystatechange=n)},"GET"===t&&(i=null,"string"==typeof d.parameters&&(f=e(f,d.parameters))),s.open(t,f,!0),("POST"===t||"PUT"===t)&&s.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),s.send(i),s}var o=function(){for(var e=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}],n=e.length;n--;)try{var f=e[n]();if(f)return e[n]}catch(o){}}();fabric.util.request=f}(),fabric.log=function(){},fabric.warn=function(){},"undefined"!=typeof console&&["log","warn"].forEach(function(e){"undefined"!=typeof console[e]&&console[e].apply&&(fabric[e]=function(){return console[e].apply(console,arguments)})}),function(e){"use strict";function n(e){return e in j?j[e]:e}function f(e,n,f){var o,d="[object Array]"===Object.prototype.toString.call(n);return"fill"!==e&&"stroke"!==e||"none"!==n?"fillRule"===e?n="evenodd"===n?"destination-over":n:"strokeDashArray"===e?n=n.replace(/,/g," ").split(/\s+/).map(function(e){return parseInt(e)}):"transformMatrix"===e?n=f&&f.transformMatrix?x(f.transformMatrix,r.parseTransformAttribute(n)):r.parseTransformAttribute(n):"visible"===e?(n="none"===n||"hidden"===n?!1:!0,f&&f.visible===!1&&(n=!1)):"originX"===e?n="start"===n?"left":"end"===n?"right":"center":o=d?n.map(h):h(n):n="",!d&&isNaN(o)?n:o}function o(e){for(var n in k)if(e[n]&&"undefined"!=typeof e[k[n]]&&0!==e[n].indexOf("url(")){var f=new r.Color(e[n]);e[n]=f.setAlpha(g(f.getAlpha()*e[k[n]],2)).toRgba()}return e}function d(e,n){var f=e.match(/(normal|italic)?\s*(normal|small-caps)?\s*(normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900)?\s*(\d+)px(?:\/(normal|[\d\.]+))?\s+(.*)/);if(f){var o=f[1],d=f[3],i=f[4],t=f[5],l=f[6];o&&(n.fontStyle=o),d&&(n.fontWeight=isNaN(parseFloat(d))?d:parseFloat(d)),i&&(n.fontSize=parseFloat(i)),l&&(n.fontFamily=l),t&&(n.lineHeight="normal"===t?1:t)}}function i(e,o){var i,t;e.replace(/;$/,"").split(";").forEach(function(e){var l=e.split(":");i=n(l[0].trim().toLowerCase()),t=f(i,l[1].trim()),"font"===i?d(t,o):o[i]=t})}function t(e,o){var i,t;for(var l in e)"undefined"!=typeof e[l]&&(i=n(l.toLowerCase()),t=f(i,e[l]),"font"===i?d(t,o):o[i]=t)}function l(e){var n={};for(var f in r.cssRules)if(s(e,f.split(" ")))for(var o in r.cssRules[f])n[o]=r.cssRules[f][o];return n}function s(e,n){var f,o=!0;return f=a(e,n.pop()),f&&n.length&&(o=u(e,n)),f&&o&&0===n.length}function u(e,n){for(var f,o=!0;e.parentNode&&1===e.parentNode.nodeType&&n.length;)o&&(f=n.pop()),e=e.parentNode,o=a(e,f);return 0===n.length}function a(e,n){var f,o=e.nodeName,d=e.getAttribute("class"),i=e.getAttribute("id");if(f=new RegExp("^"+o,"i"),n=n.replace(f,""),i&&n.length&&(f=new RegExp("#"+i+"(?![a-zA-Z\\-]+)","i"),n=n.replace(f,"")),d&&n.length){d=d.split(" ");for(var t=d.length;t--;)f=new RegExp("\\."+d[t]+"(?![a-zA-Z\\-]+)","i"),n=n.replace(f,"")}return 0===n.length}function p(e){for(var n=e.getElementsByTagName("use");n.length;){for(var f,o=n[0],d=o.getAttribute("xlink:href").substr(1),i=o.getAttribute("x")||0,t=o.getAttribute("y")||0,l=e.getElementById(d).cloneNode(!0),s=(o.getAttribute("transform")||"")+" translate("+i+", "+t+")",u=0,a=o.attributes,p=a.length;p>u;u++){var c=a.item(u);"x"!==c.nodeName&&"y"!==c.nodeName&&"xlink:href"!==c.nodeName&&("transform"===c.nodeName?s=s+" "+c.nodeValue:l.setAttribute(c.nodeName,c.nodeValue))}l.setAttribute("transform",s),l.removeAttribute("id"),f=o.parentNode,f.replaceChild(l,o)}}function c(e,n){if(n[3]=n[0]=n[0]>n[3]?n[3]:n[0],1!==n[0]||1!==n[3]||0!==n[4]||0!==n[5]){for(var f=e.ownerDocument.createElement("g");null!=e.firstChild;)f.appendChild(e.firstChild);f.setAttribute("transform","matrix("+n[0]+" "+n[1]+" "+n[2]+" "+n[3]+" "+n[4]+" "+n[5]+")"),e.appendChild(f)}}function y(e){var n=e.objects,f=e.options;return n=n.map(function(e){return r[w(e.type)].fromObject(e)}),{objects:n,options:f}}function m(e,n,f){n[f]&&n[f].toSVG&&e.push('<pattern x="0" y="0" id="',f,'Pattern" ','width="',n[f].source.width,'" height="',n[f].source.height,'" patternUnits="userSpaceOnUse">','<image x="0" y="0" ','width="',n[f].source.width,'" height="',n[f].source.height,'" xlink:href="',n[f].source.src,'"></image></pattern>')}var r=e.fabric||(e.fabric={}),v=r.util.object.extend,w=r.util.string.capitalize,b=r.util.object.clone,g=r.util.toFixed,h=r.util.parseUnit,x=r.util.multiplyTransformMatrices,j={cx:"left",x:"left",r:"radius",cy:"top",y:"top",display:"visible",visibility:"visible",transform:"transformMatrix","fill-opacity":"fillOpacity","fill-rule":"fillRule","font-family":"fontFamily","font-size":"fontSize","font-style":"fontStyle","font-weight":"fontWeight","stroke-dasharray":"strokeDashArray","stroke-linecap":"strokeLineCap","stroke-linejoin":"strokeLineJoin","stroke-miterlimit":"strokeMiterLimit","stroke-opacity":"strokeOpacity","stroke-width":"strokeWidth","text-decoration":"textDecoration","text-anchor":"originX"},k={stroke:"strokeOpacity",fill:"fillOpacity"};r.parseTransformAttribute=function(){function e(e,n){var f=n[0];e[0]=Math.cos(f),e[1]=Math.sin(f),e[2]=-Math.sin(f),e[3]=Math.cos(f)}function n(e,n){var f=n[0],o=2===n.length?n[1]:n[0];e[0]=f,e[3]=o}function f(e,n){e[2]=n[0]}function o(e,n){e[1]=n[0]}function d(e,n){e[4]=n[0],2===n.length&&(e[5]=n[1])}var i=[1,0,0,1,0,0],t="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",l="(?:\\s+,?\\s*|,\\s*)",s="(?:(skewX)\\s*\\(\\s*("+t+")\\s*\\))",u="(?:(skewY)\\s*\\(\\s*("+t+")\\s*\\))",a="(?:(rotate)\\s*\\(\\s*("+t+")(?:"+l+"("+t+")"+l+"("+t+"))?\\s*\\))",p="(?:(scale)\\s*\\(\\s*("+t+")(?:"+l+"("+t+"))?\\s*\\))",c="(?:(translate)\\s*\\(\\s*("+t+")(?:"+l+"("+t+"))?\\s*\\))",y="(?:(matrix)\\s*\\(\\s*("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")"+l+"("+t+")\\s*\\))",m="(?:"+y+"|"+c+"|"+p+"|"+a+"|"+s+"|"+u+")",v="(?:"+m+"(?:"+l+m+")*)",w="^\\s*(?:"+v+"?)\\s*$",b=new RegExp(w),g=new RegExp(m,"g");return function(t){var l=i.concat(),s=[];if(!t||t&&!b.test(t))return l;t.replace(g,function(t){var u=new RegExp(m).exec(t).filter(function(e){return""!==e&&null!=e}),a=u[1],p=u.slice(2).map(parseFloat);switch(a){case"translate":d(l,p);break;case"rotate":p[0]=r.util.degreesToRadians(p[0]),e(l,p);break;case"scale":n(l,p);break;case"skewX":f(l,p);break;case"skewY":o(l,p);break;case"matrix":l=p}s.push(l.concat()),l=i.concat()});for(var u=s[0];s.length>1;)s.shift(),u=r.util.multiplyTransformMatrices(u,s[0]);return u}}(),r.parseSVGDocument=function(){function e(e,n){for(;e&&(e=e.parentNode);)if(n.test(e.nodeName))return!0;return!1}var n=/^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,f="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",o=new RegExp("^\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*,?\\s*("+f+"+)\\s*$");return function(f,d,i){if(f){var t=new Date;p(f);var l,s,u=f.getAttribute("viewBox"),a=h(f.getAttribute("width")||"100%"),y=h(f.getAttribute("height")||"100%");if(u&&(u=u.match(o))){var m=parseFloat(u[1]),v=parseFloat(u[2]),w=1,g=1;l=parseFloat(u[3]),s=parseFloat(u[4]),a&&a!==l&&(w=a/l),y&&y!==s&&(g=y/s),c(f,[w,0,0,g,w*-m,g*-v])}var x=r.util.toArray(f.getElementsByTagName("*"));if(0===x.length&&r.isLikelyNode){x=f.selectNodes('//*[name(.)!="svg"]');for(var j=[],k=0,q=x.length;q>k;k++)j[k]=x[k];x=j}var z=x.filter(function(f){return n.test(f.tagName)&&!e(f,/^(?:pattern|defs)$/)});if(!z||z&&!z.length)return void(d&&d([],{}));var A={width:a?a:l,height:y?y:s,widthAttr:a,heightAttr:y};r.gradientDefs=r.getGradientDefs(f),r.cssRules=r.getCSSRules(f),r.parseElements(z,function(e){r.documentParsingTime=new Date-t,d&&d(e,A)},b(A),i)}}}();var q={has:function(e,n){n(!1)},get:function(){},set:function(){}};v(r,{getGradientDefs:function(e){var n,f,o,d,i=e.getElementsByTagName("linearGradient"),t=e.getElementsByTagName("radialGradient"),l=0,s=[],u={},a={};for(s.length=i.length+t.length,f=i.length;f--;)s[l++]=i[f];for(f=t.length;f--;)s[l++]=t[f];for(;l--;)n=s[l],d=n.getAttribute("xlink:href"),o=n.getAttribute("id"),d&&(a[o]=d.substr(1)),u[o]=n;for(o in a){var p=u[a[o]].cloneNode(!0);for(n=u[o];p.firstChild;)n.appendChild(p.firstChild)}return u},parseAttributes:function(e,d){if(e){var i,t={};e.parentNode&&/^symbol|[g|a]$/i.test(e.parentNode.nodeName)&&(t=r.parseAttributes(e.parentNode,d));var s=d.reduce(function(o,d){return i=e.getAttribute(d),i&&(d=n(d),i=f(d,i,t),o[d]=i),o},{});return s=v(s,v(l(e),r.parseStyleAttribute(e))),o(v(t,s))}},parseElements:function(e,n,f,o){new r.ElementsParser(e,n,f,o).parse()},parseStyleAttribute:function(e){var n={},f=e.getAttribute("style");return f?("string"==typeof f?i(f,n):t(f,n),n):n
-},parsePointsAttribute:function(e){if(!e)return null;e=e.replace(/,/g," ").trim(),e=e.split(/\s+/);var n,f,o=[];for(n=0,f=e.length;f>n;n+=2)o.push({x:parseFloat(e[n]),y:parseFloat(e[n+1])});return o},getCSSRules:function(e){for(var o,d=e.getElementsByTagName("style"),i={},t=0,l=d.length;l>t;t++){var s=d[0].textContent;s=s.replace(/\/\*[\s\S]*?\*\//g,""),o=s.match(/[^{]*\{[\s\S]*?\}/g),o=o.map(function(e){return e.trim()}),o.forEach(function(e){for(var o=e.match(/([\s\S]*?)\s*\{([^}]*)\}/),d={},t=o[2].trim(),l=t.replace(/;$/,"").split(/\s*;\s*/),s=0,u=l.length;u>s;s++){var a=l[s].split(/\s*:\s*/),p=n(a[0]),c=f(p,a[1],a[0]);d[p]=c}e=o[1],e.split(",").forEach(function(e){i[e.trim()]=r.util.object.clone(d)})})}return i},loadSVGFromURL:function(e,n,f){function o(o){var d=o.responseXML;d&&!d.documentElement&&r.window.ActiveXObject&&o.responseText&&(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(o.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,""))),d&&d.documentElement&&r.parseSVGDocument(d.documentElement,function(f,o){q.set(e,{objects:r.util.array.invoke(f,"toObject"),options:o}),n(f,o)},f)}e=e.replace(/^\n\s*/,"").trim(),q.has(e,function(f){f?q.get(e,function(e){var f=y(e);n(f.objects,f.options)}):new r.util.request(e,{method:"get",onComplete:o})})},loadSVGFromString:function(e,n,f){e=e.trim();var o;if("undefined"!=typeof DOMParser){var d=new DOMParser;d&&d.parseFromString&&(o=d.parseFromString(e,"text/xml"))}else r.window.ActiveXObject&&(o=new ActiveXObject("Microsoft.XMLDOM"),o.async="false",o.loadXML(e.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,"")));r.parseSVGDocument(o.documentElement,function(e,f){n(e,f)},f)},createSVGFontFacesMarkup:function(e){for(var n="",f=0,o=e.length;o>f;f++)"text"===e[f].type&&e[f].path&&(n+=["@font-face {","font-family: ",e[f].fontFamily,"; ","src: url('",e[f].path,"')","}"].join(""));return n&&(n=['<style type="text/css">',"<![CDATA[",n,"]]>","</style>"].join("")),n},createSVGRefElementsMarkup:function(e){var n=[];return m(n,e,"backgroundColor"),m(n,e,"overlayColor"),n.join("")}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(e,n,f,o){this.elements=e,this.callback=n,this.options=f,this.reviver=o},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 e=0,n=this.elements.length;n>e;e++)!function(e,n){setTimeout(function(){e.createObject(e.elements[n],n)},0)}(this,e)},fabric.ElementsParser.prototype.createObject=function(e,n){var f=fabric[fabric.util.string.capitalize(e.tagName)];if(f&&f.fromElement)try{this._createObject(f,e,n)}catch(o){fabric.log(o)}else this.checkIfDone()},fabric.ElementsParser.prototype._createObject=function(e,n,f){if(e.async)e.fromElement(n,this.createCallback(f,n),this.options);else{var o=e.fromElement(n,this.options);this.resolveGradient(o,"fill"),this.resolveGradient(o,"stroke"),this.reviver&&this.reviver(n,o),this.instances[f]=o,this.checkIfDone()}},fabric.ElementsParser.prototype.createCallback=function(e,n){var f=this;return function(o){f.resolveGradient(o,"fill"),f.resolveGradient(o,"stroke"),f.reviver&&f.reviver(n,o),f.instances[e]=o,f.checkIfDone()}},fabric.ElementsParser.prototype.resolveGradient=function(e,n){var f=e.get(n);if(/^url\(/.test(f)){var o=f.slice(5,f.length-1);fabric.gradientDefs[o]&&e.set(n,fabric.Gradient.fromElement(fabric.gradientDefs[o],e))}},fabric.ElementsParser.prototype.checkIfDone=function(){0===--this.numElements&&(this.instances=this.instances.filter(function(e){return null!=e}),this.callback(this.instances))},function(e){"use strict";function n(e,n){this.x=e,this.y=n}var f=e.fabric||(e.fabric={});return f.Point?void f.warn("fabric.Point is already defined"):(f.Point=n,void(n.prototype={constructor:n,add:function(e){return new n(this.x+e.x,this.y+e.y)},addEquals:function(e){return this.x+=e.x,this.y+=e.y,this},scalarAdd:function(e){return new n(this.x+e,this.y+e)},scalarAddEquals:function(e){return this.x+=e,this.y+=e,this},subtract:function(e){return new n(this.x-e.x,this.y-e.y)},subtractEquals:function(e){return this.x-=e.x,this.y-=e.y,this},scalarSubtract:function(e){return new n(this.x-e,this.y-e)},scalarSubtractEquals:function(e){return this.x-=e,this.y-=e,this},multiply:function(e){return new n(this.x*e,this.y*e)},multiplyEquals:function(e){return this.x*=e,this.y*=e,this},divide:function(e){return new n(this.x/e,this.y/e)},divideEquals:function(e){return this.x/=e,this.y/=e,this},eq:function(e){return this.x===e.x&&this.y===e.y},lt:function(e){return this.x<e.x&&this.y<e.y},lte:function(e){return this.x<=e.x&&this.y<=e.y},gt:function(e){return this.x>e.x&&this.y>e.y},gte:function(e){return this.x>=e.x&&this.y>=e.y},lerp:function(e,f){return new n(this.x+(e.x-this.x)*f,this.y+(e.y-this.y)*f)},distanceFrom:function(e){var n=this.x-e.x,f=this.y-e.y;return Math.sqrt(n*n+f*f)},midPointFrom:function(e){return new n(this.x+(e.x-this.x)/2,this.y+(e.y-this.y)/2)},min:function(e){return new n(Math.min(this.x,e.x),Math.min(this.y,e.y))},max:function(e){return new n(Math.max(this.x,e.x),Math.max(this.y,e.y))},toString:function(){return this.x+","+this.y},setXY:function(e,n){this.x=e,this.y=n},setFromPoint:function(e){this.x=e.x,this.y=e.y},swap:function(e){var n=this.x,f=this.y;this.x=e.x,this.y=e.y,e.x=n,e.y=f}}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){this.status=e,this.points=[]}var f=e.fabric||(e.fabric={});return f.Intersection?void f.warn("fabric.Intersection is already defined"):(f.Intersection=n,f.Intersection.prototype={appendPoint:function(e){this.points.push(e)},appendPoints:function(e){this.points=this.points.concat(e)}},f.Intersection.intersectLineLine=function(e,o,d,i){var t,l=(i.x-d.x)*(e.y-d.y)-(i.y-d.y)*(e.x-d.x),s=(o.x-e.x)*(e.y-d.y)-(o.y-e.y)*(e.x-d.x),u=(i.y-d.y)*(o.x-e.x)-(i.x-d.x)*(o.y-e.y);if(0!==u){var a=l/u,p=s/u;a>=0&&1>=a&&p>=0&&1>=p?(t=new n("Intersection"),t.points.push(new f.Point(e.x+a*(o.x-e.x),e.y+a*(o.y-e.y)))):t=new n}else t=new n(0===l||0===s?"Coincident":"Parallel");return t},f.Intersection.intersectLinePolygon=function(e,f,o){for(var d=new n,i=o.length,t=0;i>t;t++){var l=o[t],s=o[(t+1)%i],u=n.intersectLineLine(e,f,l,s);d.appendPoints(u.points)}return d.points.length>0&&(d.status="Intersection"),d},f.Intersection.intersectPolygonPolygon=function(e,f){for(var o=new n,d=e.length,i=0;d>i;i++){var t=e[i],l=e[(i+1)%d],s=n.intersectLinePolygon(t,l,f);o.appendPoints(s.points)}return o.points.length>0&&(o.status="Intersection"),o},void(f.Intersection.intersectPolygonRectangle=function(e,o,d){var i=o.min(d),t=o.max(d),l=new f.Point(t.x,i.y),s=new f.Point(i.x,t.y),u=n.intersectLinePolygon(i,l,e),a=n.intersectLinePolygon(l,t,e),p=n.intersectLinePolygon(t,s,e),c=n.intersectLinePolygon(s,i,e),y=new n;return y.appendPoints(u.points),y.appendPoints(a.points),y.appendPoints(p.points),y.appendPoints(c.points),y.points.length>0&&(y.status="Intersection"),y}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){e?this._tryParsingColor(e):this.setSource([0,0,0,1])}function f(e,n,f){return 0>f&&(f+=1),f>1&&(f-=1),1/6>f?e+6*(n-e)*f:.5>f?n:2/3>f?e+(n-e)*(2/3-f)*6:e}var o=e.fabric||(e.fabric={});return o.Color?void o.warn("fabric.Color is already defined."):(o.Color=n,o.Color.prototype={_tryParsingColor:function(e){var f;return e in n.colorNameMap&&(e=n.colorNameMap[e]),"transparent"===e?void this.setSource([255,255,255,0]):(f=n.sourceFromHex(e),f||(f=n.sourceFromRgb(e)),f||(f=n.sourceFromHsl(e)),void(f&&this.setSource(f)))},_rgbToHsl:function(e,n,f){e/=255,n/=255,f/=255;var d,i,t,l=o.util.array.max([e,n,f]),s=o.util.array.min([e,n,f]);if(t=(l+s)/2,l===s)d=i=0;else{var u=l-s;switch(i=t>.5?u/(2-l-s):u/(l+s),l){case e:d=(n-f)/u+(f>n?6:0);break;case n:d=(f-e)/u+2;break;case f:d=(e-n)/u+4}d/=6}return[Math.round(360*d),Math.round(100*i),Math.round(100*t)]},getSource:function(){return this._source},setSource:function(e){this._source=e},toRgb:function(){var e=this.getSource();return"rgb("+e[0]+","+e[1]+","+e[2]+")"},toRgba:function(){var e=this.getSource();return"rgba("+e[0]+","+e[1]+","+e[2]+","+e[3]+")"},toHsl:function(){var e=this.getSource(),n=this._rgbToHsl(e[0],e[1],e[2]);return"hsl("+n[0]+","+n[1]+"%,"+n[2]+"%)"},toHsla:function(){var e=this.getSource(),n=this._rgbToHsl(e[0],e[1],e[2]);return"hsla("+n[0]+","+n[1]+"%,"+n[2]+"%,"+e[3]+")"},toHex:function(){var e,n,f,o=this.getSource();return e=o[0].toString(16),e=1===e.length?"0"+e:e,n=o[1].toString(16),n=1===n.length?"0"+n:n,f=o[2].toString(16),f=1===f.length?"0"+f:f,e.toUpperCase()+n.toUpperCase()+f.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(e){var n=this.getSource();return n[3]=e,this.setSource(n),this},toGrayscale:function(){var e=this.getSource(),n=parseInt((.3*e[0]+.59*e[1]+.11*e[2]).toFixed(0),10),f=e[3];return this.setSource([n,n,n,f]),this},toBlackWhite:function(e){var n=this.getSource(),f=(.3*n[0]+.59*n[1]+.11*n[2]).toFixed(0),o=n[3];return e=e||127,f=Number(f)<Number(e)?0:255,this.setSource([f,f,f,o]),this},overlayWith:function(e){e instanceof n||(e=new n(e));for(var f=[],o=this.getAlpha(),d=.5,i=this.getSource(),t=e.getSource(),l=0;3>l;l++)f.push(Math.round(i[l]*(1-d)+t[l]*d));return f[3]=o,this.setSource(f),this}},o.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*)?\)$/,o.Color.reHSLa=/^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/,o.Color.reHex=/^#?([0-9a-f]{6}|[0-9a-f]{3})$/i,o.Color.colorNameMap={aqua:"#00FFFF",black:"#000000",blue:"#0000FF",fuchsia:"#FF00FF",gray:"#808080",green:"#008000",lime:"#00FF00",maroon:"#800000",navy:"#000080",olive:"#808000",orange:"#FFA500",purple:"#800080",red:"#FF0000",silver:"#C0C0C0",teal:"#008080",white:"#FFFFFF",yellow:"#FFFF00"},o.Color.fromRgb=function(e){return n.fromSource(n.sourceFromRgb(e))},o.Color.sourceFromRgb=function(e){var f=e.match(n.reRGBa);if(f){var o=parseInt(f[1],10)/(/%$/.test(f[1])?100:1)*(/%$/.test(f[1])?255:1),d=parseInt(f[2],10)/(/%$/.test(f[2])?100:1)*(/%$/.test(f[2])?255:1),i=parseInt(f[3],10)/(/%$/.test(f[3])?100:1)*(/%$/.test(f[3])?255:1);return[parseInt(o,10),parseInt(d,10),parseInt(i,10),f[4]?parseFloat(f[4]):1]}},o.Color.fromRgba=n.fromRgb,o.Color.fromHsl=function(e){return n.fromSource(n.sourceFromHsl(e))},o.Color.sourceFromHsl=function(e){var o=e.match(n.reHSLa);if(o){var d,i,t,l=(parseFloat(o[1])%360+360)%360/360,s=parseFloat(o[2])/(/%$/.test(o[2])?100:1),u=parseFloat(o[3])/(/%$/.test(o[3])?100:1);if(0===s)d=i=t=u;else{var a=.5>=u?u*(s+1):u+s-u*s,p=2*u-a;d=f(p,a,l+1/3),i=f(p,a,l),t=f(p,a,l-1/3)}return[Math.round(255*d),Math.round(255*i),Math.round(255*t),o[4]?parseFloat(o[4]):1]}},o.Color.fromHsla=n.fromHsl,o.Color.fromHex=function(e){return n.fromSource(n.sourceFromHex(e))},o.Color.sourceFromHex=function(e){if(e.match(n.reHex)){var f=e.slice(e.indexOf("#")+1),o=3===f.length,d=o?f.charAt(0)+f.charAt(0):f.substring(0,2),i=o?f.charAt(1)+f.charAt(1):f.substring(2,4),t=o?f.charAt(2)+f.charAt(2):f.substring(4,6);return[parseInt(d,16),parseInt(i,16),parseInt(t,16),1]}},void(o.Color.fromSource=function(e){var f=new n;return f.setSource(e),f}))}("undefined"!=typeof exports?exports:this),function(){function e(e){var n,f,o,d=e.getAttribute("style"),i=e.getAttribute("offset");if(i=parseFloat(i)/(/%$/.test(i)?100:1),i=0>i?0:i>1?1:i,d){var t=d.split(/\s*;\s*/);""===t[t.length-1]&&t.pop();for(var l=t.length;l--;){var s=t[l].split(/\s*:\s*/),u=s[0].trim(),a=s[1].trim();"stop-color"===u?n=a:"stop-opacity"===u&&(o=a)}}return n||(n=e.getAttribute("stop-color")||"rgb(0,0,0)"),o||(o=e.getAttribute("stop-opacity")),n=new fabric.Color(n),f=n.getAlpha(),o=isNaN(parseFloat(o))?1:parseFloat(o),o*=f,{offset:i,color:n.toRgb(),opacity:o}}function n(e){return{x1:e.getAttribute("x1")||0,y1:e.getAttribute("y1")||0,x2:e.getAttribute("x2")||"100%",y2:e.getAttribute("y2")||0}}function f(e){return{x1:e.getAttribute("fx")||e.getAttribute("cx")||"50%",y1:e.getAttribute("fy")||e.getAttribute("cy")||"50%",r1:0,x2:e.getAttribute("cx")||"50%",y2:e.getAttribute("cy")||"50%",r2:e.getAttribute("r")||"50%"}}function o(e,n,f){var o,d=0,i=1,t="";for(var l in n)o=parseFloat(n[l],10),i="string"==typeof n[l]&&/^\d+%$/.test(n[l])?.01:1,"x1"===l||"x2"===l||"r2"===l?(i*="objectBoundingBox"===f?e.width:1,d="objectBoundingBox"===f?e.left||0:0):("y1"===l||"y2"===l)&&(i*="objectBoundingBox"===f?e.height:1,d="objectBoundingBox"===f?e.top||0:0),n[l]=o*i+d;if("ellipse"===e.type&&null!==n.r2&&"objectBoundingBox"===f&&e.rx!==e.ry){var s=e.ry/e.rx;t=" scale(1, "+s+")",n.y1&&(n.y1/=s),n.y2&&(n.y2/=s)}return t}fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,initialize:function(e){e||(e={});var n={};this.id=fabric.Object.__uid++,this.type=e.type||"linear",n={x1:e.coords.x1||0,y1:e.coords.y1||0,x2:e.coords.x2||0,y2:e.coords.y2||0},"radial"===this.type&&(n.r1=e.coords.r1||0,n.r2=e.coords.r2||0),this.coords=n,this.colorStops=e.colorStops.slice(),e.gradientTransform&&(this.gradientTransform=e.gradientTransform),this.offsetX=e.offsetX||this.offsetX,this.offsetY=e.offsetY||this.offsetY},addColorStop:function(e){for(var n in e){var f=new fabric.Color(e[n]);this.colorStops.push({offset:n,color:f.toRgb(),opacity:f.getAlpha()})}return this},toObject:function(){return{type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(e){var n,f,o=fabric.util.object.clone(this.coords);if(this.colorStops.sort(function(e,n){return e.offset-n.offset}),!e.group||"path-group"!==e.group.type)for(var d in o)"x1"===d||"x2"===d||"r2"===d?o[d]+=this.offsetX-e.width/2:("y1"===d||"y2"===d)&&(o[d]+=this.offsetY-e.height/2);f='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(f+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?n=["<linearGradient ",f,' x1="',o.x1,'" y1="',o.y1,'" x2="',o.x2,'" y2="',o.y2,'">\n']:"radial"===this.type&&(n=["<radialGradient ",f,' cx="',o.x2,'" cy="',o.y2,'" r="',o.r2,'" fx="',o.x1,'" fy="',o.y1,'">\n']);for(var i=0;i<this.colorStops.length;i++)n.push("<stop ",'offset="',100*this.colorStops[i].offset+"%",'" style="stop-color:',this.colorStops[i].color,null!=this.colorStops[i].opacity?";stop-opacity: "+this.colorStops[i].opacity:";",'"/>\n');return n.push("linear"===this.type?"</linearGradient>\n":"</radialGradient>\n"),n.join("")},toLive:function(e){var n;if(this.type){"linear"===this.type?n=e.createLinearGradient(this.coords.x1,this.coords.y1,this.coords.x2,this.coords.y2):"radial"===this.type&&(n=e.createRadialGradient(this.coords.x1,this.coords.y1,this.coords.r1,this.coords.x2,this.coords.y2,this.coords.r2));for(var f=0,o=this.colorStops.length;o>f;f++){var d=this.colorStops[f].color,i=this.colorStops[f].opacity,t=this.colorStops[f].offset;"undefined"!=typeof i&&(d=new fabric.Color(d).setAlpha(i).toRgba()),n.addColorStop(parseFloat(t),d)}return n}}}),fabric.util.object.extend(fabric.Gradient,{fromElement:function(d,i){var t,l=d.getElementsByTagName("stop"),s="linearGradient"===d.nodeName?"linear":"radial",u=d.getAttribute("gradientUnits")||"objectBoundingBox",a=d.getAttribute("gradientTransform"),p=[],c={};"linear"===s?c=n(d):"radial"===s&&(c=f(d));for(var y=l.length;y--;)p.push(e(l[y]));t=o(i,c,u);var m=new fabric.Gradient({type:s,coords:c,colorStops:p,offsetX:-i.left,offsetY:-i.top});return(a||""!==t)&&(m.gradientTransform=fabric.parseTransformAttribute((a||"")+t)),m},forObject:function(e,n){return n||(n={}),o(e,n.coords,"userSpaceOnUse"),new fabric.Gradient(n)}})}(),fabric.Pattern=fabric.util.createClass({repeat:"repeat",offsetX:0,offsetY:0,initialize:function(e){if(e||(e={}),this.id=fabric.Object.__uid++,e.source)if("string"==typeof e.source)if("undefined"!=typeof fabric.util.getFunctionBody(e.source))this.source=new Function(fabric.util.getFunctionBody(e.source));else{var n=this;this.source=fabric.util.createImage(),fabric.util.loadImage(e.source,function(e){n.source=e})}else this.source=e.source;e.repeat&&(this.repeat=e.repeat),e.offsetX&&(this.offsetX=e.offsetX),e.offsetY&&(this.offsetY=e.offsetY)},toObject:function(){var e;return"function"==typeof this.source?e=String(this.source):"string"==typeof this.source.src&&(e=this.source.src),{source:e,repeat:this.repeat,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(e){var n="function"==typeof this.source?this.source():this.source,f=n.width/e.getWidth(),o=n.height/e.getHeight(),d="";return n.src?d=n.src:n.toDataURL&&(d=n.toDataURL()),'<pattern id="SVGID_'+this.id+'" x="'+this.offsetX+'" y="'+this.offsetY+'" width="'+f+'" height="'+o+'"><image x="0" y="0" width="'+n.width+'" height="'+n.height+'" xlink:href="'+d+'"></image></pattern>'},toLive:function(e){var n="function"==typeof this.source?this.source():this.source;if(!n)return"";if("undefined"!=typeof n.src){if(!n.complete)return"";if(0===n.naturalWidth||0===n.naturalHeight)return""}return e.createPattern(n,this.repeat)}}),function(e){"use strict";var n=e.fabric||(e.fabric={});return n.Shadow?void n.warn("fabric.Shadow is already defined."):(n.Shadow=n.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(e){"string"==typeof e&&(e=this._parseShadow(e));for(var f in e)this[f]=e[f];this.id=n.Object.__uid++},_parseShadow:function(e){var f=e.trim(),o=n.Shadow.reOffsetsAndBlur.exec(f)||[],d=f.replace(n.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:d.trim(),offsetX:parseInt(o[1],10)||0,offsetY:parseInt(o[2],10)||0,blur:parseInt(o[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(e){var n="SourceAlpha";return!e||e.fill!==this.color&&e.stroke!==this.color||(n="SourceGraphic"),'<filter id="SVGID_'+this.id+'" y="-40%" height="180%"><feGaussianBlur in="'+n+'" stdDeviation="'+(this.blur?this.blur/3:0)+'"></feGaussianBlur><feOffset dx="'+this.offsetX+'" dy="'+this.offsetY+'"></feOffset><feMerge><feMergeNode></feMergeNode><feMergeNode in="SourceGraphic"></feMergeNode></feMerge></filter>'},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY};var e={},f=n.Shadow.prototype;return this.color!==f.color&&(e.color=this.color),this.blur!==f.blur&&(e.blur=this.blur),this.offsetX!==f.offsetX&&(e.offsetX=this.offsetX),this.offsetY!==f.offsetY&&(e.offsetY=this.offsetY),e}}),void(n.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/))}("undefined"!=typeof exports?exports:this),function(){"use strict";if(fabric.StaticCanvas)return void fabric.warn("fabric.StaticCanvas is already defined.");var e=fabric.util.object.extend,n=fabric.util.getElementOffset,f=fabric.util.removeFromArray,o=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(e,n){n||(n={}),this._initStatic(e,n),fabric.StaticCanvas.activeInstance=this},backgroundColor:"",backgroundImage:null,overlayColor:"",overlayImage:null,includeDefaultValues:!0,stateful:!0,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,imageSmoothingEnabled:!0,viewportTransform:[1,0,0,1,0,0],onBeforeScaleRotate:function(){},_initStatic:function(e,n){this._objects=[],this._createLowerCanvas(e),this._initOptions(n),this._setImageSmoothing(),n.overlayImage&&this.setOverlayImage(n.overlayImage,this.renderAll.bind(this)),n.backgroundImage&&this.setBackgroundImage(n.backgroundImage,this.renderAll.bind(this)),n.backgroundColor&&this.setBackgroundColor(n.backgroundColor,this.renderAll.bind(this)),n.overlayColor&&this.setOverlayColor(n.overlayColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=n(this.lowerCanvasEl),this},setOverlayImage:function(e,n,f){return this.__setBgOverlayImage("overlayImage",e,n,f)},setBackgroundImage:function(e,n,f){return this.__setBgOverlayImage("backgroundImage",e,n,f)},setOverlayColor:function(e,n){return this.__setBgOverlayColor("overlayColor",e,n)},setBackgroundColor:function(e,n){return this.__setBgOverlayColor("backgroundColor",e,n)},_setImageSmoothing:function(){var e=this.getContext();e.imageSmoothingEnabled=this.imageSmoothingEnabled,e.webkitImageSmoothingEnabled=this.imageSmoothingEnabled,e.mozImageSmoothingEnabled=this.imageSmoothingEnabled,e.msImageSmoothingEnabled=this.imageSmoothingEnabled,e.oImageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(e,n,f,o){return"string"==typeof n?fabric.util.loadImage(n,function(n){this[e]=new fabric.Image(n,o),f&&f()},this):(this[e]=n,f&&f()),this},__setBgOverlayColor:function(e,n,f){if(n&&n.source){var o=this;fabric.util.loadImage(n.source,function(d){o[e]=new fabric.Pattern({source:d,repeat:n.repeat,offsetX:n.offsetX,offsetY:n.offsetY}),f&&f()})}else this[e]=n,f&&f();return this},_createCanvasElement:function(){var e=fabric.document.createElement("canvas");if(e.style||(e.style={}),!e)throw o;return this._initCanvasElement(e),e},_initCanvasElement:function(e){if(fabric.util.createCanvasElement(e),"undefined"==typeof e.getContext)throw o},_initOptions:function(e){for(var n in e)this[n]=e[n];this.width=this.width||parseInt(this.lowerCanvasEl.width,10)||0,this.height=this.height||parseInt(this.lowerCanvasEl.height,10)||0,this.lowerCanvasEl.style&&(this.lowerCanvasEl.width=this.width,this.lowerCanvasEl.height=this.height,this.lowerCanvasEl.style.width=this.width+"px",this.lowerCanvasEl.style.height=this.height+"px",this.viewportTransform=this.viewportTransform.slice())},_createLowerCanvas:function(e){this.lowerCanvasEl=fabric.util.getById(e)||this._createCanvasElement(),this._initCanvasElement(this.lowerCanvasEl),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(e,n){return this.setDimensions({width:e},n)},setHeight:function(e,n){return this.setDimensions({height:e},n)},setDimensions:function(e,n){var f;n=n||{};for(var o in e)f=e[o],n.cssOnly||(this._setBackstoreDimension(o,e[o]),f+="px"),n.backstoreOnly||this._setCssDimension(o,f);return n.cssOnly||this.renderAll(),this.calcOffset(),this},_setBackstoreDimension:function(e,n){return this.lowerCanvasEl[e]=n,this.upperCanvasEl&&(this.upperCanvasEl[e]=n),this.cacheCanvasEl&&(this.cacheCanvasEl[e]=n),this[e]=n,this},_setCssDimension:function(e,n){return this.lowerCanvasEl.style[e]=n,this.upperCanvasEl&&(this.upperCanvasEl.style[e]=n),this.wrapperEl&&(this.wrapperEl.style[e]=n),this},getZoom:function(){return Math.sqrt(this.viewportTransform[0]*this.viewportTransform[3])},setViewportTransform:function(e){this.viewportTransform=e,this.renderAll();for(var n=0,f=this._objects.length;f>n;n++)this._objects[n].setCoords();return this},zoomToPoint:function(e,n){var f=e;e=fabric.util.transformPoint(e,fabric.util.invertTransform(this.viewportTransform)),this.viewportTransform[0]=n,this.viewportTransform[3]=n;var o=fabric.util.transformPoint(e,this.viewportTransform);this.viewportTransform[4]+=f.x-o.x,this.viewportTransform[5]+=f.y-o.y,this.renderAll();for(var d=0,i=this._objects.length;i>d;d++)this._objects[d].setCoords();return this},setZoom:function(e){return this.zoomToPoint(new fabric.Point(0,0),e),this},absolutePan:function(e){this.viewportTransform[4]=-e.x,this.viewportTransform[5]=-e.y,this.renderAll();for(var n=0,f=this._objects.length;f>n;n++)this._objects[n].setCoords();return this},relativePan:function(e){return this.absolutePan(new fabric.Point(-e.x-this.viewportTransform[4],-e.y-this.viewportTransform[5]))},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(e,n){if(n){e.save();var f=this.viewportTransform;e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),n.render(e),e.restore(),this.controlsAboveOverlay||n._renderControls(e)}},_onObjectAdded:function(e){this.stateful&&e.setupState(),e.canvas=this,e.setCoords(),this.fire("object:added",{target:e}),e.fire("added")},_onObjectRemoved:function(e){this.getActiveObject()===e&&(this.fire("before:selection:cleared",{target:e}),this._discardActiveObject(),this.fire("selection:cleared")),this.fire("object:removed",{target:e}),e.fire("removed")},clearContext:function(e){return e.clearRect(0,0,this.width,this.height),this},getContext:function(){return this.contextContainer},clear:function(){return this._objects.length=0,this.discardActiveGroup&&this.discardActiveGroup(),this.discardActiveObject&&this.discardActiveObject(),this.clearContext(this.contextContainer),this.contextTop&&this.clearContext(this.contextTop),this.fire("canvas:cleared"),this.renderAll(),this},renderAll:function(e){var n=this[e===!0&&this.interactive?"contextTop":"contextContainer"],f=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),e||this.clearContext(n),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,n),this._renderBackground(n),this._renderObjects(n,f),this._renderActiveGroup(n,f),this.clipTo&&n.restore(),this._renderOverlay(n),this.controlsAboveOverlay&&this.interactive&&this.drawControls(n),this.fire("after:render"),this},_renderObjects:function(e,n){var f,o;if(n)for(f=0,o=this._objects.length;o>f;++f)this._objects[f]&&!n.contains(this._objects[f])&&this._draw(e,this._objects[f]);else for(f=0,o=this._objects.length;o>f;++f)this._draw(e,this._objects[f])},_renderActiveGroup:function(e,n){if(n){var f=[];this.forEachObject(function(e){n.contains(e)&&f.push(e)}),n._set("objects",f),this._draw(e,n)}},_renderBackground:function(e){this.backgroundColor&&(e.fillStyle=this.backgroundColor.toLive?this.backgroundColor.toLive(e):this.backgroundColor,e.fillRect(this.backgroundColor.offsetX||0,this.backgroundColor.offsetY||0,this.width,this.height)),this.backgroundImage&&this._draw(e,this.backgroundImage)},_renderOverlay:function(e){this.overlayColor&&(e.fillStyle=this.overlayColor.toLive?this.overlayColor.toLive(e):this.overlayColor,e.fillRect(this.overlayColor.offsetX||0,this.overlayColor.offsetY||0,this.width,this.height)),this.overlayImage&&this._draw(e,this.overlayImage)},renderTop:function(){var e=this.contextTop||this.contextContainer;this.clearContext(e),this.selection&&this._groupSelector&&this._drawSelection();var n=this.getActiveGroup();return n&&n.render(e),this._renderOverlay(e),this.fire("after:render"),this},getCenter:function(){return{top:this.getHeight()/2,left:this.getWidth()/2}},centerObjectH:function(e){return this._centerObject(e,new fabric.Point(this.getCenter().left,e.getCenterPoint().y)),this.renderAll(),this},centerObjectV:function(e){return this._centerObject(e,new fabric.Point(e.getCenterPoint().x,this.getCenter().top)),this.renderAll(),this},centerObject:function(e){var n=this.getCenter();return this._centerObject(e,new fabric.Point(n.left,n.top)),this.renderAll(),this},_centerObject:function(e,n){return e.setPositionByOrigin(n,"center","center"),this},toDatalessJSON:function(e){return this.toDatalessObject(e)},toObject:function(e){return this._toObjectMethod("toObject",e)},toDatalessObject:function(e){return this._toObjectMethod("toDatalessObject",e)},_toObjectMethod:function(n,f){var o=this.getActiveGroup();o&&this.discardActiveGroup();var d={objects:this._toObjects(n,f)};return e(d,this.__serializeBgOverlay()),fabric.util.populateWithProperties(this,d,f),o&&(this.setActiveGroup(new fabric.Group(o.getObjects(),{originX:"center",originY:"center"})),o.forEachObject(function(e){e.set("active",!0)}),this._currentTransform&&(this._currentTransform.target=this.getActiveGroup())),d},_toObjects:function(e,n){return this.getObjects().map(function(f){return this._toObject(f,e,n)},this)},_toObject:function(e,n,f){var o;this.includeDefaultValues||(o=e.includeDefaultValues,e.includeDefaultValues=!1);var d=e[n](f);return this.includeDefaultValues||(e.includeDefaultValues=o),d},__serializeBgOverlay:function(){var e={background:this.backgroundColor&&this.backgroundColor.toObject?this.backgroundColor.toObject():this.backgroundColor};return this.overlayColor&&(e.overlay=this.overlayColor.toObject?this.overlayColor.toObject():this.overlayColor),this.backgroundImage&&(e.backgroundImage=this.backgroundImage.toObject()),this.overlayImage&&(e.overlayImage=this.overlayImage.toObject()),e},svgViewportTransformation:!0,toSVG:function(e,n){e||(e={});var f=[];return this._setSVGPreamble(f,e),this._setSVGHeader(f,e),this._setSVGBgOverlayColor(f,"backgroundColor"),this._setSVGBgOverlayImage(f,"backgroundImage"),this._setSVGObjects(f,n),this._setSVGBgOverlayColor(f,"overlayColor"),this._setSVGBgOverlayImage(f,"overlayImage"),f.push("</svg>"),f.join("")},_setSVGPreamble:function(e,n){n.suppressPreamble||e.push('<?xml version="1.0" encoding="',n.encoding||"UTF-8",'" standalone="no" ?>','<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ','"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')},_setSVGHeader:function(e,n){var f,o,d;n.viewBox?(f=n.viewBox.width,o=n.viewBox.height):(f=this.width,o=this.height,this.svgViewportTransformation||(d=this.viewportTransform,f/=d[0],o/=d[3])),e.push("<svg ",'xmlns="http://www.w3.org/2000/svg" ','xmlns:xlink="http://www.w3.org/1999/xlink" ','version="1.1" ','width="',f,'" ','height="',o,'" ',this.backgroundColor&&!this.backgroundColor.toLive?'style="background-color: '+this.backgroundColor+'" ':null,n.viewBox?'viewBox="'+n.viewBox.x+" "+n.viewBox.y+" "+n.viewBox.width+" "+n.viewBox.height+'" ':null,'xml:space="preserve">',"<desc>Created with Fabric.js ",fabric.version,"</desc>","<defs>",fabric.createSVGFontFacesMarkup(this.getObjects()),fabric.createSVGRefElementsMarkup(this),"</defs>")},_setSVGObjects:function(e,n){var f=this.getActiveGroup();f&&this.discardActiveGroup();for(var o=0,d=this.getObjects(),i=d.length;i>o;o++)e.push(d[o].toSVG(n));f&&(this.setActiveGroup(new fabric.Group(f.getObjects())),f.forEachObject(function(e){e.set("active",!0)}))},_setSVGBgOverlayImage:function(e,n){this[n]&&this[n].toSVG&&e.push(this[n].toSVG())},_setSVGBgOverlayColor:function(e,n){this[n]&&this[n].source?e.push('<rect x="',this[n].offsetX,'" y="',this[n].offsetY,'" ','width="',"repeat-y"===this[n].repeat||"no-repeat"===this[n].repeat?this[n].source.width:this.width,'" height="',"repeat-x"===this[n].repeat||"no-repeat"===this[n].repeat?this[n].source.height:this.height,'" fill="url(#'+n+'Pattern)"',"></rect>"):this[n]&&"overlayColor"===n&&e.push('<rect x="0" y="0" ','width="',this.width,'" height="',this.height,'" fill="',this[n],'"',"></rect>")},sendToBack:function(e){return f(this._objects,e),this._objects.unshift(e),this.renderAll&&this.renderAll()},bringToFront:function(e){return f(this._objects,e),this._objects.push(e),this.renderAll&&this.renderAll()},sendBackwards:function(e,n){var o=this._objects.indexOf(e);if(0!==o){var d=this._findNewLowerIndex(e,o,n);f(this._objects,e),this._objects.splice(d,0,e),this.renderAll&&this.renderAll()}return this},_findNewLowerIndex:function(e,n,f){var o;if(f){o=n;for(var d=n-1;d>=0;--d){var i=e.intersectsWithObject(this._objects[d])||e.isContainedWithinObject(this._objects[d])||this._objects[d].isContainedWithinObject(e);if(i){o=d;break}}}else o=n-1;return o},bringForward:function(e,n){var o=this._objects.indexOf(e);if(o!==this._objects.length-1){var d=this._findNewUpperIndex(e,o,n);f(this._objects,e),this._objects.splice(d,0,e),this.renderAll&&this.renderAll()}return this},_findNewUpperIndex:function(e,n,f){var o;if(f){o=n;for(var d=n+1;d<this._objects.length;++d){var i=e.intersectsWithObject(this._objects[d])||e.isContainedWithinObject(this._objects[d])||this._objects[d].isContainedWithinObject(e);if(i){o=d;
-break}}}else o=n+1;return o},moveTo:function(e,n){return f(this._objects,e),this._objects.splice(n,0,e),this.renderAll&&this.renderAll()},dispose:function(){return this.clear(),this.interactive&&this.removeListeners(),this},toString:function(){return"#<fabric.Canvas ("+this.complexity()+"): { objects: "+this.getObjects().length+" }>"}}),e(fabric.StaticCanvas.prototype,fabric.Observable),e(fabric.StaticCanvas.prototype,fabric.Collection),e(fabric.StaticCanvas.prototype,fabric.DataURLExporter),e(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(e){var n=fabric.util.createCanvasElement();if(!n||!n.getContext)return null;var f=n.getContext("2d");if(!f)return null;switch(e){case"getImageData":return"undefined"!=typeof f.getImageData;case"setLineDash":return"undefined"!=typeof f.setLineDash;case"toDataURL":return"undefined"!=typeof n.toDataURL;case"toDataURLWithQuality":try{return n.toDataURL("image/jpeg",0),!0}catch(o){}return!1;default:return null}}}),fabric.StaticCanvas.prototype.toJSON=fabric.StaticCanvas.prototype.toObject}(),fabric.BaseBrush=fabric.util.createClass({color:"rgb(0, 0, 0)",width:1,shadow:null,strokeLineCap:"round",strokeLineJoin:"round",setShadow:function(e){return this.shadow=new fabric.Shadow(e),this},_setBrushStyles:function(){var e=this.canvas.contextTop;e.strokeStyle=this.color,e.lineWidth=this.width,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin},_setShadow:function(){if(this.shadow){var e=this.canvas.contextTop;e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY}},_resetShadow:function(){var e=this.canvas.contextTop;e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0}}),function(){var e=fabric.util.array.min,n=fabric.util.array.max;fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{initialize:function(e){this.canvas=e,this._points=[]},onMouseDown:function(e){this._prepareForDrawing(e),this._captureDrawingPath(e),this._render()},onMouseMove:function(e){this._captureDrawingPath(e),this.canvas.clearContext(this.canvas.contextTop),this._render()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(e){var n=new fabric.Point(e.x,e.y);this._reset(),this._addPoint(n),this.canvas.contextTop.moveTo(n.x,n.y)},_addPoint:function(e){this._points.push(e)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(e){var n=new fabric.Point(e.x,e.y);this._addPoint(n)},_render:function(){var e=this.canvas.contextTop,n=this.canvas.viewportTransform,f=this._points[0],o=this._points[1];e.save(),e.transform(n[0],n[1],n[2],n[3],n[4],n[5]),e.beginPath(),2===this._points.length&&f.x===o.x&&f.y===o.y&&(f.x-=.5,o.x+=.5),e.moveTo(f.x,f.y);for(var d=1,i=this._points.length;i>d;d++){var t=f.midPointFrom(o);e.quadraticCurveTo(f.x,f.y,t.x,t.y),f=this._points[d],o=this._points[d+1]}e.lineTo(f.x,f.y),e.stroke(),e.restore()},_getSVGPathData:function(){return this.box=this.getPathBoundingBox(this._points),this.convertPointsToSVGPath(this._points,this.box.minX,this.box.minY)},getPathBoundingBox:function(f){for(var o=[],d=[],i=f[0],t=f[1],l=i,s=1,u=f.length;u>s;s++){var a=i.midPointFrom(t);o.push(l.x),o.push(a.x),d.push(l.y),d.push(a.y),i=f[s],t=f[s+1],l=a}return o.push(i.x),d.push(i.y),{minX:e(o),minY:e(d),maxX:n(o),maxY:n(d)}},convertPointsToSVGPath:function(e,n,f){var o=[],d=new fabric.Point(e[0].x-n,e[0].y-f),i=new fabric.Point(e[1].x-n,e[1].y-f);o.push("M ",e[0].x-n," ",e[0].y-f," ");for(var t=1,l=e.length;l>t;t++){var s=d.midPointFrom(i);o.push("Q ",d.x," ",d.y," ",s.x," ",s.y," "),d=new fabric.Point(e[t].x-n,e[t].y-f),t+1<e.length&&(i=new fabric.Point(e[t+1].x-n,e[t+1].y-f))}return o.push("L ",d.x," ",d.y," "),o},createPath:function(e){var n=new fabric.Path(e);return n.fill=null,n.stroke=this.color,n.strokeWidth=this.width,n.strokeLineCap=this.strokeLineCap,n.strokeLineJoin=this.strokeLineJoin,this.shadow&&(this.shadow.affectStroke=!0,n.setShadow(this.shadow)),n},_finalizeAndAddPath:function(){var e=this.canvas.contextTop;e.closePath();var n=this._getSVGPathData().join("");if("M 0 0 Q 0 0 0 0 L 0 0"===n)return void this.canvas.renderAll();var f=this.box.minX+(this.box.maxX-this.box.minX)/2,o=this.box.minY+(this.box.maxY-this.box.minY)/2;this.canvas.contextTop.arc(f,o,3,0,2*Math.PI,!1);var d=this.createPath(n);d.set({left:f,top:o,originX:"center",originY:"center"}),this.canvas.add(d),d.setCoords(),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderAll(),this.canvas.fire("path:created",{path:d})}})}(),fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,initialize:function(e){this.canvas=e,this.points=[]},drawDot:function(e){var n=this.addPoint(e),f=this.canvas.contextTop,o=this.canvas.viewportTransform;f.save(),f.transform(o[0],o[1],o[2],o[3],o[4],o[5]),f.fillStyle=n.fill,f.beginPath(),f.arc(n.x,n.y,n.radius,0,2*Math.PI,!1),f.closePath(),f.fill(),f.restore()},onMouseDown:function(e){this.points.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.drawDot(e)},onMouseMove:function(e){this.drawDot(e)},onMouseUp:function(){var e=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var n=[],f=0,o=this.points.length;o>f;f++){var d=this.points[f],i=new fabric.Circle({radius:d.radius,left:d.x,top:d.y,originX:"center",originY:"center",fill:d.fill});this.shadow&&i.setShadow(this.shadow),n.push(i)}var t=new fabric.Group(n,{originX:"center",originY:"center"});t.canvas=this.canvas,this.canvas.add(t),this.canvas.fire("path:created",{path:t}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=e,this.canvas.renderAll()},addPoint:function(e){var n=new fabric.Point(e.x,e.y),f=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,o=new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return n.radius=f,n.fill=o,this.points.push(n),n}}),fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(e){this.canvas=e,this.sprayChunks=[]},onMouseDown:function(e){this.sprayChunks.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.addSprayChunk(e),this.render()},onMouseMove:function(e){this.addSprayChunk(e),this.render()},onMouseUp:function(){var e=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var n=[],f=0,o=this.sprayChunks.length;o>f;f++)for(var d=this.sprayChunks[f],i=0,t=d.length;t>i;i++){var l=new fabric.Rect({width:d[i].width,height:d[i].width,left:d[i].x+1,top:d[i].y+1,originX:"center",originY:"center",fill:this.color});this.shadow&&l.setShadow(this.shadow),n.push(l)}this.optimizeOverlapping&&(n=this._getOptimizedRects(n));var s=new fabric.Group(n,{originX:"center",originY:"center"});s.canvas=this.canvas,this.canvas.add(s),this.canvas.fire("path:created",{path:s}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=e,this.canvas.renderAll()},_getOptimizedRects:function(e){for(var n,f={},o=0,d=e.length;d>o;o++)n=e[o].left+""+e[o].top,f[n]||(f[n]=e[o]);var i=[];for(n in f)i.push(f[n]);return i},render:function(){var e=this.canvas.contextTop;e.fillStyle=this.color;var n=this.canvas.viewportTransform;e.save(),e.transform(n[0],n[1],n[2],n[3],n[4],n[5]);for(var f=0,o=this.sprayChunkPoints.length;o>f;f++){var d=this.sprayChunkPoints[f];"undefined"!=typeof d.opacity&&(e.globalAlpha=d.opacity),e.fillRect(d.x,d.y,d.width,d.width)}e.restore()},addSprayChunk:function(e){this.sprayChunkPoints=[];for(var n,f,o,d=this.width/2,i=0;i<this.density;i++){n=fabric.util.getRandomInt(e.x-d,e.x+d),f=fabric.util.getRandomInt(e.y-d,e.y+d),o=this.dotWidthVariance?fabric.util.getRandomInt(Math.max(1,this.dotWidth-this.dotWidthVariance),this.dotWidth+this.dotWidthVariance):this.dotWidth;var t=new fabric.Point(n,f);t.width=o,this.randomOpacity&&(t.opacity=fabric.util.getRandomInt(0,100)/100),this.sprayChunkPoints.push(t)}this.sprayChunks.push(this.sprayChunkPoints)}}),fabric.PatternBrush=fabric.util.createClass(fabric.PencilBrush,{getPatternSrc:function(){var e=20,n=5,f=fabric.document.createElement("canvas"),o=f.getContext("2d");return f.width=f.height=e+n,o.fillStyle=this.color,o.beginPath(),o.arc(e/2,e/2,e/2,0,2*Math.PI,!1),o.closePath(),o.fill(),f},getPatternSrcFunction:function(){return String(this.getPatternSrc).replace("this.color",'"'+this.color+'"')},getPattern:function(){return this.canvas.contextTop.createPattern(this.source||this.getPatternSrc(),"repeat")},_setBrushStyles:function(){this.callSuper("_setBrushStyles"),this.canvas.contextTop.strokeStyle=this.getPattern()},createPath:function(e){var n=this.callSuper("createPath",e);return n.stroke=new fabric.Pattern({source:this.source||this.getPatternSrcFunction()}),n}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{toDataURL:function(e){e||(e={});var n=e.format||"png",f=e.quality||1,o=e.multiplier||1,d={left:e.left,top:e.top,width:e.width,height:e.height};return 1!==o?this.__toDataURLWithMultiplier(n,f,d,o):this.__toDataURL(n,f,d)},__toDataURL:function(e,n,f){this.renderAll(!0);var o=this.upperCanvasEl||this.lowerCanvasEl,d=this.__getCroppedCanvas(o,f);"jpg"===e&&(e="jpeg");var i=fabric.StaticCanvas.supports("toDataURLWithQuality")?(d||o).toDataURL("image/"+e,n):(d||o).toDataURL("image/"+e);return this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),d&&(d=null),i},__getCroppedCanvas:function(e,n){var f,o,d="left"in n||"top"in n||"width"in n||"height"in n;return d&&(f=fabric.util.createCanvasElement(),o=f.getContext("2d"),f.width=n.width||this.width,f.height=n.height||this.height,o.drawImage(e,-n.left||0,-n.top||0)),f},__toDataURLWithMultiplier:function(e,n,f,o){var d=this.getWidth(),i=this.getHeight(),t=d*o,l=i*o,s=this.getActiveObject(),u=this.getActiveGroup(),a=this.contextTop||this.contextContainer;o>1&&this.setWidth(t).setHeight(l),a.scale(o,o),f.left&&(f.left*=o),f.top&&(f.top*=o),f.width?f.width*=o:1>o&&(f.width=t),f.height?f.height*=o:1>o&&(f.height=l),u?this._tempRemoveBordersControlsFromGroup(u):s&&this.deactivateAll&&this.deactivateAll(),this.renderAll(!0);var p=this.__toDataURL(e,n,f);return this.width=d,this.height=i,a.scale(1/o,1/o),this.setWidth(d).setHeight(i),u?this._restoreBordersControlsOnGroup(u):s&&this.setActiveObject&&this.setActiveObject(s),this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),p},toDataURLWithMultiplier:function(e,n,f){return this.toDataURL({format:e,multiplier:n,quality:f})},_tempRemoveBordersControlsFromGroup:function(e){e.origHasControls=e.hasControls,e.origBorderColor=e.borderColor,e.hasControls=!0,e.borderColor="rgba(0,0,0,0)",e.forEachObject(function(e){e.origBorderColor=e.borderColor,e.borderColor="rgba(0,0,0,0)"})},_restoreBordersControlsOnGroup:function(e){e.hideControls=e.origHideControls,e.borderColor=e.origBorderColor,e.forEachObject(function(e){e.borderColor=e.origBorderColor,delete e.origBorderColor})}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{loadFromDatalessJSON:function(e,n,f){return this.loadFromJSON(e,n,f)},loadFromJSON:function(e,n,f){if(e){var o="string"==typeof e?JSON.parse(e):e;this.clear();var d=this;return this._enlivenObjects(o.objects,function(){d._setBgOverlay(o,n)},f),this}},_setBgOverlay:function(e,n){var f=this,o={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!(e.backgroundImage||e.overlayImage||e.background||e.overlay))return void(n&&n());var d=function(){o.backgroundImage&&o.overlayImage&&o.backgroundColor&&o.overlayColor&&(f.renderAll(),n&&n())};this.__setBgOverlay("backgroundImage",e.backgroundImage,o,d),this.__setBgOverlay("overlayImage",e.overlayImage,o,d),this.__setBgOverlay("backgroundColor",e.background,o,d),this.__setBgOverlay("overlayColor",e.overlay,o,d),d()},__setBgOverlay:function(e,n,f,o){var d=this;return n?void("backgroundImage"===e||"overlayImage"===e?fabric.Image.fromObject(n,function(n){d[e]=n,f[e]=!0,o&&o()}):this["set"+fabric.util.string.capitalize(e,!0)](n,function(){f[e]=!0,o&&o()})):void(f[e]=!0)},_enlivenObjects:function(e,n,f){var o=this;if(!e||0===e.length)return void(n&&n());var d=this.renderOnAddRemove;this.renderOnAddRemove=!1,fabric.util.enlivenObjects(e,function(e){e.forEach(function(e,n){o.insertAt(e,n,!0)}),o.renderOnAddRemove=d,n&&n()},null,f)},_toDataURL:function(e,n){this.clone(function(f){n(f.toDataURL(e))})},_toDataURLWithMultiplier:function(e,n,f){this.clone(function(o){f(o.toDataURLWithMultiplier(e,n))})},clone:function(e,n){var f=JSON.stringify(this.toJSON(n));this.cloneWithoutData(function(n){n.loadFromJSON(f,function(){e&&e(n)})})},cloneWithoutData:function(e){var n=fabric.document.createElement("canvas");n.width=this.getWidth(),n.height=this.getHeight();var f=new fabric.Canvas(n);f.clipTo=this.clipTo,this.backgroundImage?(f.setBackgroundImage(this.backgroundImage.src,function(){f.renderAll(),e&&e(f)}),f.backgroundImageOpacity=this.backgroundImageOpacity,f.backgroundImageStretch=this.backgroundImageStretch):e&&e(f)}}),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.toFixed,d=n.util.string.capitalize,i=n.util.degreesToRadians,t=n.StaticCanvas.supports("setLineDash");n.Object||(n.Object=n.util.createClass({type:"object",originX:"left",originY:"top",top:0,left:0,width:0,height:0,scaleX:1,scaleY:1,flipX:!1,flipY:!1,opacity:1,angle:0,cornerSize:12,transparentCorners:!0,hoverCursor:null,padding:0,borderColor:"rgba(102,153,255,0.75)",cornerColor:"rgba(102,153,255,0.5)",centeredScaling:!1,centeredRotation:!0,fill:"rgb(0,0,0)",fillRule:"source-over",backgroundColor:"",stroke:null,strokeWidth:1,strokeDashArray:null,strokeLineCap:"butt",strokeLineJoin:"miter",strokeMiterLimit:10,shadow:null,borderOpacityWhenMoving:.4,borderScaleFactor:1,transformMatrix:null,minScaleLimit:.01,selectable:!0,evented:!0,visible:!0,hasControls:!0,hasBorders:!0,hasRotatingPoint:!0,rotatingPointOffset:40,perPixelTargetFind:!1,includeDefaultValues:!0,clipTo:null,lockMovementX:!1,lockMovementY:!1,lockRotation:!1,lockScalingX:!1,lockScalingY:!1,lockUniScaling:!1,lockScalingFlip:!1,stateProperties:"top left width height scaleX scaleY flipX flipY originX originY transformMatrix stroke strokeWidth strokeDashArray strokeLineCap strokeLineJoin strokeMiterLimit angle opacity fill fillRule shadow clipTo visible backgroundColor".split(" "),initialize:function(e){e&&this.setOptions(e)},_initGradient:function(e){!e.fill||!e.fill.colorStops||e.fill instanceof n.Gradient||this.set("fill",new n.Gradient(e.fill))},_initPattern:function(e){!e.fill||!e.fill.source||e.fill instanceof n.Pattern||this.set("fill",new n.Pattern(e.fill)),!e.stroke||!e.stroke.source||e.stroke instanceof n.Pattern||this.set("stroke",new n.Pattern(e.stroke))},_initClipping:function(e){if(e.clipTo&&"string"==typeof e.clipTo){var f=n.util.getFunctionBody(e.clipTo);"undefined"!=typeof f&&(this.clipTo=new Function("ctx",f))}},setOptions:function(e){for(var n in e)this.set(n,e[n]);this._initGradient(e),this._initPattern(e),this._initClipping(e)},transform:function(e,n){this.group&&this.group.transform(e,n),e.globalAlpha=this.opacity;var f=n?this._getLeftTopCoords():this.getCenterPoint();e.translate(f.x,f.y),e.rotate(i(this.angle)),e.scale(this.scaleX*(this.flipX?-1:1),this.scaleY*(this.flipY?-1:1))},toObject:function(e){var f=n.Object.NUM_FRACTION_DIGITS,d={type:this.type,originX:this.originX,originY:this.originY,left:o(this.left,f),top:o(this.top,f),width:o(this.width,f),height:o(this.height,f),fill:this.fill&&this.fill.toObject?this.fill.toObject():this.fill,stroke:this.stroke&&this.stroke.toObject?this.stroke.toObject():this.stroke,strokeWidth:o(this.strokeWidth,f),strokeDashArray:this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeLineJoin:this.strokeLineJoin,strokeMiterLimit:o(this.strokeMiterLimit,f),scaleX:o(this.scaleX,f),scaleY:o(this.scaleY,f),angle:o(this.getAngle(),f),flipX:this.flipX,flipY:this.flipY,opacity:o(this.opacity,f),shadow:this.shadow&&this.shadow.toObject?this.shadow.toObject():this.shadow,visible:this.visible,clipTo:this.clipTo&&String(this.clipTo),backgroundColor:this.backgroundColor};return this.includeDefaultValues||(d=this._removeDefaultValues(d)),n.util.populateWithProperties(this,d,e),d},toDatalessObject:function(e){return this.toObject(e)},_removeDefaultValues:function(e){var f=n.util.getKlass(e.type).prototype,o=f.stateProperties;return o.forEach(function(n){e[n]===f[n]&&delete e[n]}),e},toString:function(){return"#<fabric."+d(this.type)+">"},get:function(e){return this[e]},_setObject:function(e){for(var n in e)this._set(n,e[n])},set:function(e,n){return"object"==typeof e?this._setObject(e):"function"==typeof n&&"clipTo"!==e?this._set(e,n(this.get(e))):this._set(e,n),this},_set:function(e,f){var d="scaleX"===e||"scaleY"===e;return d&&(f=this._constrainScale(f)),"scaleX"===e&&0>f?(this.flipX=!this.flipX,f*=-1):"scaleY"===e&&0>f?(this.flipY=!this.flipY,f*=-1):"width"===e||"height"===e?this.minScaleLimit=o(Math.min(.1,1/Math.max(this.width,this.height)),2):"shadow"!==e||!f||f instanceof n.Shadow||(f=new n.Shadow(f)),this[e]=f,this},toggle:function(e){var n=this.get(e);return"boolean"==typeof n&&this.set(e,!n),this},setSourcePath:function(e){return this.sourcePath=e,this},getViewportTransform:function(){return this.canvas&&this.canvas.viewportTransform?this.canvas.viewportTransform:[1,0,0,1,0,0]},render:function(e,f){if(0!==this.width&&0!==this.height&&this.visible){if(e.save(),this._setupFillRule(e),this._transform(e,f),this._setStrokeStyles(e),this._setFillStyles(e),this.group&&"path-group"===this.group.type){e.translate(-this.group.width/2,-this.group.height/2);var o=this.transformMatrix;o&&e.transform.apply(e,o)}e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,this._setShadow(e),this.clipTo&&n.util.clipContext(this,e),this._render(e,f),this.clipTo&&e.restore(),this._removeShadow(e),this._restoreFillRule(e),e.restore()}},_transform:function(e,n){var f=this.transformMatrix;f&&!this.group&&e.setTransform.apply(e,f),n||this.transform(e)},_setStrokeStyles:function(e){this.stroke&&(e.lineWidth=this.strokeWidth,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin,e.miterLimit=this.strokeMiterLimit,e.strokeStyle=this.stroke.toLive?this.stroke.toLive(e):this.stroke)},_setFillStyles:function(e){this.fill&&(e.fillStyle=this.fill.toLive?this.fill.toLive(e):this.fill)},_renderControls:function(e,f){var o=this.getViewportTransform();if(e.save(),this.active&&!f){var d;this.group&&(d=n.util.transformPoint(this.group.getCenterPoint(),o),e.translate(d.x,d.y),e.rotate(i(this.group.angle))),d=n.util.transformPoint(this.getCenterPoint(),o,null!=this.group),this.group&&(d.x*=this.group.scaleX,d.y*=this.group.scaleY),e.translate(d.x,d.y),e.rotate(i(this.angle)),this.drawBorders(e),this.drawControls(e)}e.restore()},_setShadow:function(e){this.shadow&&(e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY)},_removeShadow:function(e){this.shadow&&(e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0)},_renderFill:function(e){if(this.fill){if(e.save(),this.fill.toLive&&e.translate(-this.width/2+this.fill.offsetX||0,-this.height/2+this.fill.offsetY||0),this.fill.gradientTransform){var n=this.fill.gradientTransform;e.transform.apply(e,n)}"destination-over"===this.fillRule?e.fill("evenodd"):e.fill(),e.restore(),this.shadow&&!this.shadow.affectStroke&&this._removeShadow(e)}},_renderStroke:function(e){if(this.stroke&&0!==this.strokeWidth){if(e.save(),this.strokeDashArray)1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),t?(e.setLineDash(this.strokeDashArray),this._stroke&&this._stroke(e)):this._renderDashedStroke&&this._renderDashedStroke(e),e.stroke();else{if(this.stroke.gradientTransform){var n=this.stroke.gradientTransform;e.transform.apply(e,n)}this._stroke?this._stroke(e):e.stroke()}this._removeShadow(e),e.restore()}},clone:function(e,f){return this.constructor.fromObject?this.constructor.fromObject(this.toObject(f),e):new n.Object(this.toObject(f))},cloneAsImage:function(e){var f=this.toDataURL();return n.util.loadImage(f,function(f){e&&e(new n.Image(f))}),this},toDataURL:function(e){e||(e={});var f=n.util.createCanvasElement(),o=this.getBoundingRect();f.width=o.width,f.height=o.height,n.util.wrapElement(f,"div");var d=new n.Canvas(f);"jpg"===e.format&&(e.format="jpeg"),"jpeg"===e.format&&(d.backgroundColor="#fff");var i={active:this.get("active"),left:this.getLeft(),top:this.getTop()};this.set("active",!1),this.setPositionByOrigin(new n.Point(f.width/2,f.height/2),"center","center");var t=this.canvas;d.add(this);var l=d.toDataURL(e);return this.set(i).setCoords(),this.canvas=t,d.dispose(),d=null,l},isType:function(e){return this.type===e},complexity:function(){return 0},toJSON:function(e){return this.toObject(e)},setGradient:function(e,f){f||(f={});var o={colorStops:[]};o.type=f.type||(f.r1||f.r2?"radial":"linear"),o.coords={x1:f.x1,y1:f.y1,x2:f.x2,y2:f.y2},(f.r1||f.r2)&&(o.coords.r1=f.r1,o.coords.r2=f.r2);for(var d in f.colorStops){var i=new n.Color(f.colorStops[d]);o.colorStops.push({offset:d,color:i.toRgb(),opacity:i.getAlpha()})}return this.set(e,n.Gradient.forObject(this,o))},setPatternFill:function(e){return this.set("fill",new n.Pattern(e))},setShadow:function(e){return this.set("shadow",e?new n.Shadow(e):null)},setColor:function(e){return this.set("fill",e),this},setAngle:function(e){var n=("center"!==this.originX||"center"!==this.originY)&&this.centeredRotation;return n&&this._setOriginToCenter(),this.set("angle",e),n&&this._resetOrigin(),this},centerH:function(){return this.canvas.centerObjectH(this),this},centerV:function(){return this.canvas.centerObjectV(this),this},center:function(){return this.canvas.centerObject(this),this},remove:function(){return this.canvas.remove(this),this},getLocalPointer:function(e,n){n=n||this.canvas.getPointer(e);var f=this.translateToOriginPoint(this.getCenterPoint(),"left","top");return{x:n.x-f.x,y:n.y-f.y}},_setupFillRule:function(e){this.fillRule&&(this._prevFillRule=e.globalCompositeOperation,e.globalCompositeOperation=this.fillRule)},_restoreFillRule:function(e){this.fillRule&&this._prevFillRule&&(e.globalCompositeOperation=this._prevFillRule)}}),n.util.createAccessors(n.Object),n.Object.prototype.rotate=n.Object.prototype.setAngle,f(n.Object.prototype,n.Observable),n.Object.NUM_FRACTION_DIGITS=2,n.Object.__uid=0)}("undefined"!=typeof exports?exports:this),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{translateToCenterPoint:function(n,f,o){var d=n.x,i=n.y,t=this.stroke?this.strokeWidth:0;return"left"===f?d=n.x+(this.getWidth()+t*this.scaleX)/2:"right"===f&&(d=n.x-(this.getWidth()+t*this.scaleX)/2),"top"===o?i=n.y+(this.getHeight()+t*this.scaleY)/2:"bottom"===o&&(i=n.y-(this.getHeight()+t*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(d,i),n,e(this.angle))},translateToOriginPoint:function(n,f,o){var d=n.x,i=n.y,t=this.stroke?this.strokeWidth:0;return"left"===f?d=n.x-(this.getWidth()+t*this.scaleX)/2:"right"===f&&(d=n.x+(this.getWidth()+t*this.scaleX)/2),"top"===o?i=n.y-(this.getHeight()+t*this.scaleY)/2:"bottom"===o&&(i=n.y+(this.getHeight()+t*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(d,i),n,e(this.angle))},getCenterPoint:function(){var e=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(e,this.originX,this.originY)},getPointByOrigin:function(e,n){var f=this.getCenterPoint();return this.translateToOriginPoint(f,e,n)},toLocalPoint:function(n,f,o){var d,i,t=this.getCenterPoint(),l=this.stroke?this.strokeWidth:0;return f&&o?(d="left"===f?t.x-(this.getWidth()+l*this.scaleX)/2:"right"===f?t.x+(this.getWidth()+l*this.scaleX)/2:t.x,i="top"===o?t.y-(this.getHeight()+l*this.scaleY)/2:"bottom"===o?t.y+(this.getHeight()+l*this.scaleY)/2:t.y):(d=this.left,i=this.top),fabric.util.rotatePoint(new fabric.Point(n.x,n.y),t,-e(this.angle)).subtractEquals(new fabric.Point(d,i))},setPositionByOrigin:function(e,n,f){var o=this.translateToCenterPoint(e,n,f),d=this.translateToOriginPoint(o,this.originX,this.originY);this.set("left",d.x),this.set("top",d.y)},adjustPosition:function(n){var f=e(this.angle),o=this.getWidth()/2,d=Math.cos(f)*o,i=Math.sin(f)*o,t=this.getWidth(),l=Math.cos(f)*t,s=Math.sin(f)*t;"center"===this.originX&&"left"===n||"right"===this.originX&&"center"===n?(this.left-=d,this.top-=i):"left"===this.originX&&"center"===n||"center"===this.originX&&"right"===n?(this.left+=d,this.top+=i):"left"===this.originX&&"right"===n?(this.left+=l,this.top+=s):"right"===this.originX&&"left"===n&&(this.left-=l,this.top-=s),this.setCoords(),this.originX=n},_setOriginToCenter:function(){this._originalOriginX=this.originX,this._originalOriginY=this.originY;var e=this.getCenterPoint();this.originX="center",this.originY="center",this.left=e.x,this.top=e.y},_resetOrigin:function(){var e=this.translateToOriginPoint(this.getCenterPoint(),this._originalOriginX,this._originalOriginY);this.originX=this._originalOriginX,this.originY=this._originalOriginY,this.left=e.x,this.top=e.y,this._originalOriginX=null,this._originalOriginY=null},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","center")}})}(),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{oCoords:null,intersectsWithRect:function(e,n){var f=this.oCoords,o=new fabric.Point(f.tl.x,f.tl.y),d=new fabric.Point(f.tr.x,f.tr.y),i=new fabric.Point(f.bl.x,f.bl.y),t=new fabric.Point(f.br.x,f.br.y),l=fabric.Intersection.intersectPolygonRectangle([o,d,t,i],e,n);return"Intersection"===l.status},intersectsWithObject:function(e){function n(e){return{tl:new fabric.Point(e.tl.x,e.tl.y),tr:new fabric.Point(e.tr.x,e.tr.y),bl:new fabric.Point(e.bl.x,e.bl.y),br:new fabric.Point(e.br.x,e.br.y)}}var f=n(this.oCoords),o=n(e.oCoords),d=fabric.Intersection.intersectPolygonPolygon([f.tl,f.tr,f.br,f.bl],[o.tl,o.tr,o.br,o.bl]);return"Intersection"===d.status},isContainedWithinObject:function(e){var n=e.getBoundingRect(),f=new fabric.Point(n.left,n.top),o=new fabric.Point(n.left+n.width,n.top+n.height);return this.isContainedWithinRect(f,o)},isContainedWithinRect:function(e,n){var f=this.getBoundingRect();return f.left>=e.x&&f.left+f.width<=n.x&&f.top>=e.y&&f.top+f.height<=n.y},containsPoint:function(e){var n=this._getImageLines(this.oCoords),f=this._findCrossPoints(e,n);return 0!==f&&f%2===1},_getImageLines:function(e){return{topline:{o:e.tl,d:e.tr},rightline:{o:e.tr,d:e.br},bottomline:{o:e.br,d:e.bl},leftline:{o:e.bl,d:e.tl}}},_findCrossPoints:function(e,n){var f,o,d,i,t,l,s,u=0;for(var a in n)if(s=n[a],!(s.o.y<e.y&&s.d.y<e.y||s.o.y>=e.y&&s.d.y>=e.y||(s.o.x===s.d.x&&s.o.x>=e.x?(t=s.o.x,l=e.y):(f=0,o=(s.d.y-s.o.y)/(s.d.x-s.o.x),d=e.y-f*e.x,i=s.o.y-o*s.o.x,t=-(d-i)/(f-o),l=d+f*t),t>=e.x&&(u+=1),2!==u)))break;return u},getBoundingRectWidth:function(){return this.getBoundingRect().width},getBoundingRectHeight:function(){return this.getBoundingRect().height},getBoundingRect:function(){this.oCoords||this.setCoords();var e=[this.oCoords.tl.x,this.oCoords.tr.x,this.oCoords.br.x,this.oCoords.bl.x],n=fabric.util.array.min(e),f=fabric.util.array.max(e),o=Math.abs(n-f),d=[this.oCoords.tl.y,this.oCoords.tr.y,this.oCoords.br.y,this.oCoords.bl.y],i=fabric.util.array.min(d),t=fabric.util.array.max(d),l=Math.abs(i-t);return{left:n,top:i,width:o,height:l}},getWidth:function(){return this.width*this.scaleX},getHeight:function(){return this.height*this.scaleY},_constrainScale:function(e){return Math.abs(e)<this.minScaleLimit?0>e?-this.minScaleLimit:this.minScaleLimit:e},scale:function(e){return e=this._constrainScale(e),0>e&&(this.flipX=!this.flipX,this.flipY=!this.flipY,e*=-1),this.scaleX=e,this.scaleY=e,this.setCoords(),this},scaleToWidth:function(e){var n=this.getBoundingRectWidth()/this.getWidth();return this.scale(e/this.width/n)},scaleToHeight:function(e){var n=this.getBoundingRectHeight()/this.getHeight();return this.scale(e/this.height/n)},setCoords:function(){var n=this.strokeWidth>1?this.strokeWidth:0,f=e(this.angle),o=this.getViewportTransform(),d=function(e){return fabric.util.transformPoint(e,o)},i=this.width,t=this.height,l="round"===this.strokeLineCap||"square"===this.strokeLineCap,s="line"===this.type&&1===this.width,u="line"===this.type&&1===this.height,a=l&&u||"line"!==this.type,p=l&&s||"line"!==this.type;s?i=n:u&&(t=n),a&&(i+=n),p&&(t+=n),this.currentWidth=i*this.scaleX,this.currentHeight=t*this.scaleY,this.currentWidth<0&&(this.currentWidth=Math.abs(this.currentWidth));var c=Math.sqrt(Math.pow(this.currentWidth/2,2)+Math.pow(this.currentHeight/2,2)),y=Math.atan(isFinite(this.currentHeight/this.currentWidth)?this.currentHeight/this.currentWidth:0),m=Math.cos(y+f)*c,r=Math.sin(y+f)*c,v=Math.sin(f),w=Math.cos(f),b=this.getCenterPoint(),g=new fabric.Point(this.currentWidth,this.currentHeight),h=new fabric.Point(b.x-m,b.y-r),x=new fabric.Point(h.x+g.x*w,h.y+g.x*v),j=new fabric.Point(h.x-g.y*v,h.y+g.y*w),k=new fabric.Point(h.x+g.x/2*w,h.y+g.x/2*v),q=d(h),z=d(x),A=d(new fabric.Point(x.x-g.y*v,x.y+g.y*w)),B=d(j),C=d(new fabric.Point(h.x-g.y/2*v,h.y+g.y/2*w)),D=d(k),E=d(new fabric.Point(x.x-g.y/2*v,x.y+g.y/2*w)),F=d(new fabric.Point(j.x+g.x/2*w,j.y+g.x/2*v)),G=d(new fabric.Point(k.x,k.y)),H=Math.cos(y+f)*this.padding*Math.sqrt(2),I=Math.sin(y+f)*this.padding*Math.sqrt(2);return q=q.add(new fabric.Point(-H,-I)),z=z.add(new fabric.Point(I,-H)),A=A.add(new fabric.Point(H,I)),B=B.add(new fabric.Point(-I,H)),C=C.add(new fabric.Point((-H-I)/2,(-I+H)/2)),D=D.add(new fabric.Point((I-H)/2,-(I+H)/2)),E=E.add(new fabric.Point((I+H)/2,(I-H)/2)),F=F.add(new fabric.Point((H-I)/2,(H+I)/2)),G=G.add(new fabric.Point((I-H)/2,-(I+H)/2)),this.oCoords={tl:q,tr:z,br:A,bl:B,ml:C,mt:D,mr:E,mb:F,mtr:G},this._setCornerCoords&&this._setCornerCoords(),this}})}(),fabric.util.object.extend(fabric.Object.prototype,{sendToBack:function(){return this.group?fabric.StaticCanvas.prototype.sendToBack.call(this.group,this):this.canvas.sendToBack(this),this},bringToFront:function(){return this.group?fabric.StaticCanvas.prototype.bringToFront.call(this.group,this):this.canvas.bringToFront(this),this},sendBackwards:function(e){return this.group?fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,e):this.canvas.sendBackwards(this,e),this},bringForward:function(e){return this.group?fabric.StaticCanvas.prototype.bringForward.call(this.group,this,e):this.canvas.bringForward(this,e),this},moveTo:function(e){return this.group?fabric.StaticCanvas.prototype.moveTo.call(this.group,this,e):this.canvas.moveTo(this,e),this}}),fabric.util.object.extend(fabric.Object.prototype,{getSvgStyles:function(){var e=this.fill?this.fill.toLive?"url(#SVGID_"+this.fill.id+")":this.fill:"none",n="destination-over"===this.fillRule?"evenodd":this.fillRule,f=this.stroke?this.stroke.toLive?"url(#SVGID_"+this.stroke.id+")":this.stroke:"none",o=this.strokeWidth?this.strokeWidth:"0",d=this.strokeDashArray?this.strokeDashArray.join(" "):"",i=this.strokeLineCap?this.strokeLineCap:"butt",t=this.strokeLineJoin?this.strokeLineJoin:"miter",l=this.strokeMiterLimit?this.strokeMiterLimit:"4",s="undefined"!=typeof this.opacity?this.opacity:"1",u=this.visible?"":" visibility: hidden;",a=this.shadow&&"text"!==this.type?"filter: url(#SVGID_"+this.shadow.id+");":"";return["stroke: ",f,"; ","stroke-width: ",o,"; ","stroke-dasharray: ",d,"; ","stroke-linecap: ",i,"; ","stroke-linejoin: ",t,"; ","stroke-miterlimit: ",l,"; ","fill: ",e,"; ","fill-rule: ",n,"; ","opacity: ",s,";",a,u].join("")},getSvgTransform:function(){if(this.group)return"";var e=fabric.util.toFixed,n=this.getAngle(),f=!this.canvas||this.canvas.svgViewportTransformation?this.getViewportTransform():[1,0,0,1,0,0],o=fabric.util.transformPoint(this.getCenterPoint(),f),d=fabric.Object.NUM_FRACTION_DIGITS,i="path-group"===this.type?"":"translate("+e(o.x,d)+" "+e(o.y,d)+")",t=0!==n?" rotate("+e(n,d)+")":"",l=1===this.scaleX&&1===this.scaleY&&1===f[0]&&1===f[3]?"":" scale("+e(this.scaleX*f[0],d)+" "+e(this.scaleY*f[3],d)+")",s="path-group"===this.type?this.width*f[0]:0,u=this.flipX?" matrix(-1 0 0 1 "+s+" 0) ":"",a="path-group"===this.type?this.height*f[3]:0,p=this.flipY?" matrix(1 0 0 -1 0 "+a+")":"";
-return[i,t,l,u,p].join("")},getSvgTransformMatrix:function(){return this.transformMatrix?" matrix("+this.transformMatrix.join(" ")+")":""},_createBaseSVGMarkup:function(){var e=[];return this.fill&&this.fill.toLive&&e.push(this.fill.toSVG(this,!1)),this.stroke&&this.stroke.toLive&&e.push(this.stroke.toSVG(this,!1)),this.shadow&&e.push(this.shadow.toSVG(this)),e}}),fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(){return this.stateProperties.some(function(e){return this.get(e)!==this.originalState[e]},this)},saveState:function(e){return this.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),e&&e.stateProperties&&e.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),this},setupState:function(){return this.originalState={},this.saveState(),this}}),function(e){"use strict";function n(e,n){var f=e.origin,o=e.axis1,d=e.axis2,i=e.dimension,t=n.nearest,l=n.center,s=n.farthest;return function(){switch(this.get(f)){case t:return Math.min(this.get(o),this.get(d));case l:return Math.min(this.get(o),this.get(d))+.5*this.get(i);case s:return Math.max(this.get(o),this.get(d))}}}var f=e.fabric||(e.fabric={}),o=f.util.object.extend,d={x1:1,x2:1,y1:1,y2:1},i=f.StaticCanvas.supports("setLineDash");return f.Line?void f.warn("fabric.Line is already defined"):(f.Line=f.util.createClass(f.Object,{type:"line",x1:0,y1:0,x2:0,y2:0,initialize:function(e,n){n=n||{},e||(e=[0,0,0,0]),this.callSuper("initialize",n),this.set("x1",e[0]),this.set("y1",e[1]),this.set("x2",e[2]),this.set("y2",e[3]),this._setWidthHeight(n)},_setWidthHeight:function(e){e||(e={}),this.width=Math.abs(this.x2-this.x1)||1,this.height=Math.abs(this.y2-this.y1)||1,this.left="left"in e?e.left:this._getLeftToOriginX(),this.top="top"in e?e.top:this._getTopToOriginY()},_set:function(e,n){return this[e]=n,"undefined"!=typeof d[e]&&this._setWidthHeight(),this},_getLeftToOriginX:n({origin:"originX",axis1:"x1",axis2:"x2",dimension:"width"},{nearest:"left",center:"center",farthest:"right"}),_getTopToOriginY:n({origin:"originY",axis1:"y1",axis2:"y2",dimension:"height"},{nearest:"top",center:"center",farthest:"bottom"}),_render:function(e,n){if(e.beginPath(),n){var f=this.getCenterPoint();e.translate(f.x,f.y)}if(!this.strokeDashArray||this.strokeDashArray&&i){var o=this.x1<=this.x2?-1:1,d=this.y1<=this.y2?-1:1;e.moveTo(1===this.width?0:o*this.width/2,1===this.height?0:d*this.height/2),e.lineTo(1===this.width?0:-1*o*this.width/2,1===this.height?0:-1*d*this.height/2)}e.lineWidth=this.strokeWidth;var t=e.strokeStyle;e.strokeStyle=this.stroke||e.fillStyle,this.stroke&&this._renderStroke(e),e.strokeStyle=t},_renderDashedStroke:function(e){var n=this.x1<=this.x2?-1:1,o=this.y1<=this.y2?-1:1,d=1===this.width?0:n*this.width/2,i=1===this.height?0:o*this.height/2;e.beginPath(),f.util.drawDashedLine(e,d,i,-d,-i,this.strokeDashArray),e.closePath()},toObject:function(e){return o(this.callSuper("toObject",e),{x1:this.get("x1"),y1:this.get("y1"),x2:this.get("x2"),y2:this.get("y2")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f="";if(!this.group){var o=-this.width/2-(this.x1>this.x2?this.x2:this.x1),d=-this.height/2-(this.y1>this.y2?this.y2:this.y1);f="translate("+o+", "+d+") "}return n.push("<line ",'x1="',this.x1,'" y1="',this.y1,'" x2="',this.x2,'" y2="',this.y2,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),f,this.getSvgTransformMatrix(),'"/>\n'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),f.Line.ATTRIBUTE_NAMES=f.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),f.Line.fromElement=function(e,n){var d=f.parseAttributes(e,f.Line.ATTRIBUTE_NAMES),i=[d.x1||0,d.y1||0,d.x2||0,d.y2||0];return new f.Line(i,o(d,n))},void(f.Line.fromObject=function(e){var n=[e.x1,e.y1,e.x2,e.y2];return new f.Line(n,e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){return"radius"in e&&e.radius>0}var f=e.fabric||(e.fabric={}),o=2*Math.PI,d=f.util.object.extend;return f.Circle?void f.warn("fabric.Circle is already defined."):(f.Circle=f.util.createClass(f.Object,{type:"circle",radius:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("radius",e.radius||0)},_set:function(e,n){return this.callSuper("_set",e,n),"radius"===e&&this.setRadius(n),this},toObject:function(e){return d(this.callSuper("toObject",e),{radius:this.get("radius")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=0,o=0;return this.group&&(f=this.left+this.radius,o=this.top+this.radius),n.push("<circle ",'cx="'+f+'" cy="'+o+'" ','r="',this.radius,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),e?e(n.join("")):n.join("")},_render:function(e,n){e.beginPath(),e.arc(n?this.left+this.radius:0,n?this.top+this.radius:0,this.radius,0,o,!1),this._renderFill(e),this._renderStroke(e)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(e){this.radius=e,this.set("width",2*e).set("height",2*e)},complexity:function(){return 1}}),f.Circle.ATTRIBUTE_NAMES=f.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),f.Circle.fromElement=function(e,o){o||(o={});var i=f.parseAttributes(e,f.Circle.ATTRIBUTE_NAMES);if(!n(i))throw new Error("value of `r` attribute is required and can not be negative");i.left=i.left||0,i.top=i.top||0;var t=new f.Circle(d(i,o));return t.left-=t.radius,t.top-=t.radius,t},void(f.Circle.fromObject=function(e){return new f.Circle(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});return n.Triangle?void n.warn("fabric.Triangle is already defined"):(n.Triangle=n.util.createClass(n.Object,{type:"triangle",initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("width",e.width||100).set("height",e.height||100)},_render:function(e){var n=this.width/2,f=this.height/2;e.beginPath(),e.moveTo(-n,f),e.lineTo(0,-f),e.lineTo(n,f),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f=this.width/2,o=this.height/2;e.beginPath(),n.util.drawDashedLine(e,-f,o,0,-o,this.strokeDashArray),n.util.drawDashedLine(e,0,-o,f,o,this.strokeDashArray),n.util.drawDashedLine(e,f,o,-f,o,this.strokeDashArray),e.closePath()},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=this.width/2,o=this.height/2,d=[-f+" "+o,"0 "+-o,f+" "+o].join(",");return n.push("<polygon ",'points="',d,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),'"/>'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),void(n.Triangle.fromObject=function(e){return new n.Triangle(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=2*Math.PI,o=n.util.object.extend;return n.Ellipse?void n.warn("fabric.Ellipse is already defined."):(n.Ellipse=n.util.createClass(n.Object,{type:"ellipse",rx:0,ry:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("rx",e.rx||0),this.set("ry",e.ry||0),this.set("width",2*this.get("rx")),this.set("height",2*this.get("ry"))},toObject:function(e){return o(this.callSuper("toObject",e),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=0,o=0;return this.group&&(f=this.left+this.rx,o=this.top+this.ry),n.push("<ellipse ",'cx="',f,'" cy="',o,'" ','rx="',this.rx,'" ry="',this.ry,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),e?e(n.join("")):n.join("")},_render:function(e,n){e.beginPath(),e.save(),e.transform(1,0,0,this.ry/this.rx,0,0),e.arc(n?this.left+this.rx:0,n?(this.top+this.ry)*this.rx/this.ry:0,this.rx,0,f,!1),e.restore(),this._renderFill(e),this._renderStroke(e)},complexity:function(){return 1}}),n.Ellipse.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),n.Ellipse.fromElement=function(e,f){f||(f={});var d=n.parseAttributes(e,n.Ellipse.ATTRIBUTE_NAMES);d.left=d.left||0,d.top=d.top||0;var i=new n.Ellipse(o(d,f));return i.top-=i.ry,i.left-=i.rx,i},void(n.Ellipse.fromObject=function(e){return new n.Ellipse(e)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;if(n.Rect)return void console.warn("fabric.Rect is already defined");var o=n.Object.prototype.stateProperties.concat();o.push("rx","ry","x","y"),n.Rect=n.util.createClass(n.Object,{stateProperties:o,type:"rect",rx:0,ry:0,strokeDashArray:null,initialize:function(e){e=e||{},this.callSuper("initialize",e),this._initRxRy()},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(e,n){if(1===this.width&&1===this.height)return void e.fillRect(0,0,1,1);var f=this.rx?Math.min(this.rx,this.width/2):0,o=this.ry?Math.min(this.ry,this.height/2):0,d=this.width,i=this.height,t=n?this.left:-this.width/2,l=n?this.top:-this.height/2,s=0!==f||0!==o,u=.4477152502;e.beginPath(),e.moveTo(t+f,l),e.lineTo(t+d-f,l),s&&e.bezierCurveTo(t+d-u*f,l,t+d,l+u*o,t+d,l+o),e.lineTo(t+d,l+i-o),s&&e.bezierCurveTo(t+d,l+i-u*o,t+d-u*f,l+i,t+d-f,l+i),e.lineTo(t+f,l+i),s&&e.bezierCurveTo(t+u*f,l+i,t,l+i-u*o,t,l+i-o),e.lineTo(t,l+o),s&&e.bezierCurveTo(t,l+u*o,t+u*f,l,t+f,l),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f=-this.width/2,o=-this.height/2,d=this.width,i=this.height;e.beginPath(),n.util.drawDashedLine(e,f,o,f+d,o,this.strokeDashArray),n.util.drawDashedLine(e,f+d,o,f+d,o+i,this.strokeDashArray),n.util.drawDashedLine(e,f+d,o+i,f,o+i,this.strokeDashArray),n.util.drawDashedLine(e,f,o+i,f,o,this.strokeDashArray),e.closePath()},toObject:function(e){var n=f(this.callSuper("toObject",e),{rx:this.get("rx")||0,ry:this.get("ry")||0});return this.includeDefaultValues||this._removeDefaultValues(n),n},toSVG:function(e){var n=this._createBaseSVGMarkup(),f=this.left,o=this.top;return this.group||(f=-this.width/2,o=-this.height/2),n.push("<rect ",'x="',f,'" y="',o,'" rx="',this.get("rx"),'" ry="',this.get("ry"),'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),e?e(n.join("")):n.join("")},complexity:function(){return 1}}),n.Rect.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),n.Rect.fromElement=function(e,o){if(!e)return null;o=o||{};var d=n.parseAttributes(e,n.Rect.ATTRIBUTE_NAMES);return d.left=d.left||0,d.top=d.top||0,new n.Rect(f(o?n.util.object.clone(o):{},d))},n.Rect.fromObject=function(e){return new n.Rect(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.toFixed;return n.Polyline?void n.warn("fabric.Polyline is already defined"):(n.Polyline=n.util.createClass(n.Object,{type:"polyline",points:null,initialize:function(e,n){n=n||{},this.set("points",e),this.callSuper("initialize",n),this._calcDimensions()},_calcDimensions:function(){return n.Polygon.prototype._calcDimensions.call(this)},_applyPointOffset:function(){return n.Polygon.prototype._applyPointOffset.call(this)},toObject:function(e){return n.Polygon.prototype.toObject.call(this,e)},toSVG:function(e){for(var n=[],o=this._createBaseSVGMarkup(),d=0,i=this.points.length;i>d;d++)n.push(f(this.points[d].x,2),",",f(this.points[d].y,2)," ");return o.push("<polyline ",'points="',n.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),e?e(o.join("")):o.join("")},_render:function(e){var n;e.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),e.moveTo(this.points[0].x,this.points[0].y);for(var f=0,o=this.points.length;o>f;f++)n=this.points[f],e.lineTo(n.x,n.y);this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var f,o;e.beginPath();for(var d=0,i=this.points.length;i>d;d++)f=this.points[d],o=this.points[d+1]||f,n.util.drawDashedLine(e,f.x,f.y,o.x,o.y,this.strokeDashArray)},complexity:function(){return this.get("points").length}}),n.Polyline.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat(),n.Polyline.fromElement=function(e,f){if(!e)return null;f||(f={});var o=n.parsePointsAttribute(e.getAttribute("points")),d=n.parseAttributes(e,n.Polyline.ATTRIBUTE_NAMES);return null===o?null:new n.Polyline(o,n.util.object.extend(d,f))},void(n.Polyline.fromObject=function(e){var f=e.points;return new n.Polyline(f,e,!0)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.min,d=n.util.array.max,i=n.util.toFixed;return n.Polygon?void n.warn("fabric.Polygon is already defined"):(n.Polygon=n.util.createClass(n.Object,{type:"polygon",points:null,initialize:function(e,n){n=n||{},this.points=e,this.callSuper("initialize",n),this._calcDimensions()},_calcDimensions:function(){var e=this.points,n=o(e,"x"),f=o(e,"y"),i=d(e,"x"),t=d(e,"y");this.width=i-n||1,this.height=t-f||1,this.left=n,this.top=f},_applyPointOffset:function(){this.points.forEach(function(e){e.x-=this.left+this.width/2,e.y-=this.top+this.height/2},this)},toObject:function(e){return f(this.callSuper("toObject",e),{points:this.points.concat()})},toSVG:function(e){for(var n=[],f=this._createBaseSVGMarkup(),o=0,d=this.points.length;d>o;o++)n.push(i(this.points[o].x,2),",",i(this.points[o].y,2)," ");return f.push("<polygon ",'points="',n.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),e?e(f.join("")):f.join("")},_render:function(e){var n;e.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),e.moveTo(this.points[0].x,this.points[0].y);for(var f=0,o=this.points.length;o>f;f++)n=this.points[f],e.lineTo(n.x,n.y);this._renderFill(e),(this.stroke||this.strokeDashArray)&&(e.closePath(),this._renderStroke(e))},_renderDashedStroke:function(e){var f,o;e.beginPath();for(var d=0,i=this.points.length;i>d;d++)f=this.points[d],o=this.points[d+1]||this.points[0],n.util.drawDashedLine(e,f.x,f.y,o.x,o.y,this.strokeDashArray);e.closePath()},complexity:function(){return this.points.length}}),n.Polygon.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat(),n.Polygon.fromElement=function(e,o){if(!e)return null;o||(o={});var d=n.parsePointsAttribute(e.getAttribute("points")),i=n.parseAttributes(e,n.Polygon.ATTRIBUTE_NAMES);return null===d?null:new n.Polygon(d,f(i,o))},void(n.Polygon.fromObject=function(e){return new n.Polygon(e.points,e,!0)}))}("undefined"!=typeof exports?exports:this),function(e){"use strict";function n(e){return"H"===e[0]?e[1]:e[e.length-2]}function f(e){return"V"===e[0]?e[1]:e[e.length-1]}var o=e.fabric||(e.fabric={}),d=o.util.array.min,i=o.util.array.max,t=o.util.object.extend,l=Object.prototype.toString,s=o.util.drawArc,u={m:2,l:2,h:1,v:1,c:6,s:4,q:4,t:2,a:7},a={m:"l",M:"L"};return o.Path?void o.warn("fabric.Path is already defined"):(o.Path=o.util.createClass(o.Object,{type:"path",path:null,initialize:function(e,n){if(n=n||{},this.setOptions(n),!e)throw new Error("`path` argument is required");var f="[object Array]"===l.call(e);this.path=f?e:e.match&&e.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi),this.path&&(f||(this.path=this._parsePath()),this._initializePath(n),n.sourcePath&&this.setSourcePath(n.sourcePath))},_initializePath:function(e){var n="width"in e&&null!=e.width,f="height"in e&&null!=e.width,o="left"in e,d="top"in e,i=o?this.left:0,l=d?this.top:0;n&&f?(d||(this.top=this.height/2),o||(this.left=this.width/2)):(t(this,this._parseDimensions()),n&&(this.width=e.width),f&&(this.height=e.height)),this.pathOffset=this.pathOffset||this._calculatePathOffset(i,l)},_calculatePathOffset:function(e,n){return{x:this.left-e-this.width/2,y:this.top-n-this.height/2}},_render:function(e,n){var f,o,d,i,t,l=null,u=0,a=0,p=0,c=0,y=0,m=0,r=-(this.width/2+this.pathOffset.x),v=-(this.height/2+this.pathOffset.y);n&&(r+=this.width/2,v+=this.height/2);for(var w=0,b=this.path.length;b>w;++w){switch(f=this.path[w],f[0]){case"l":p+=f[1],c+=f[2],e.lineTo(p+r,c+v);break;case"L":p=f[1],c=f[2],e.lineTo(p+r,c+v);break;case"h":p+=f[1],e.lineTo(p+r,c+v);break;case"H":p=f[1],e.lineTo(p+r,c+v);break;case"v":c+=f[1],e.lineTo(p+r,c+v);break;case"V":c=f[1],e.lineTo(p+r,c+v);break;case"m":p+=f[1],c+=f[2],u=p,a=c,e.moveTo(p+r,c+v);break;case"M":p=f[1],c=f[2],u=p,a=c,e.moveTo(p+r,c+v);break;case"c":o=p+f[5],d=c+f[6],y=p+f[3],m=c+f[4],e.bezierCurveTo(p+f[1]+r,c+f[2]+v,y+r,m+v,o+r,d+v),p=o,c=d;break;case"C":p=f[5],c=f[6],y=f[3],m=f[4],e.bezierCurveTo(f[1]+r,f[2]+v,y+r,m+v,p+r,c+v);break;case"s":o=p+f[3],d=c+f[4],y=y?2*p-y:p,m=m?2*c-m:c,e.bezierCurveTo(y+r,m+v,p+f[1]+r,c+f[2]+v,o+r,d+v),y=p+f[1],m=c+f[2],p=o,c=d;break;case"S":o=f[3],d=f[4],y=2*p-y,m=2*c-m,e.bezierCurveTo(y+r,m+v,f[1]+r,f[2]+v,o+r,d+v),p=o,c=d,y=f[1],m=f[2];break;case"q":o=p+f[3],d=c+f[4],y=p+f[1],m=c+f[2],e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d;break;case"Q":o=f[3],d=f[4],e.quadraticCurveTo(f[1]+r,f[2]+v,o+r,d+v),p=o,c=d,y=f[1],m=f[2];break;case"t":o=p+f[1],d=c+f[2],null===l[0].match(/[QqTt]/)?(y=p,m=c):"t"===l[0]?(y=2*p-i,m=2*c-t):"q"===l[0]&&(y=2*p-y,m=2*c-m),i=y,t=m,e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d,y=p+f[1],m=c+f[2];break;case"T":o=f[1],d=f[2],y=2*p-y,m=2*c-m,e.quadraticCurveTo(y+r,m+v,o+r,d+v),p=o,c=d;break;case"a":s(e,p+r,c+v,[f[1],f[2],f[3],f[4],f[5],f[6]+p+r,f[7]+c+v]),p+=f[6],c+=f[7];break;case"A":s(e,p+r,c+v,[f[1],f[2],f[3],f[4],f[5],f[6]+r,f[7]+v]),p=f[6],c=f[7];break;case"z":case"Z":p=u,c=a,e.closePath()}l=f}},render:function(e,n){if(this.visible){e.save(),n&&e.translate(-this.width/2,-this.height/2);var f=this.transformMatrix;f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),n||this.transform(e),this._setStrokeStyles(e),this._setFillStyles(e),this._setShadow(e),this.clipTo&&o.util.clipContext(this,e),e.beginPath(),e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,this._render(e,n),this._renderFill(e),this._renderStroke(e),this.clipTo&&e.restore(),this._removeShadow(e),e.restore()}},toString:function(){return"#<fabric.Path ("+this.complexity()+'): { "top": '+this.top+', "left": '+this.left+" }>"},toObject:function(e){var n=t(this.callSuper("toObject",e),{path:this.path.map(function(e){return e.slice()}),pathOffset:this.pathOffset});return this.sourcePath&&(n.sourcePath=this.sourcePath),this.transformMatrix&&(n.transformMatrix=this.transformMatrix),n},toDatalessObject:function(e){var n=this.toObject(e);return this.sourcePath&&(n.path=this.sourcePath),delete n.sourcePath,n},toSVG:function(e){for(var n=[],f=this._createBaseSVGMarkup(),o=0,d=this.path.length;d>o;o++)n.push(this.path[o].join(" "));var i=n.join(" ");return f.push("<path ",'d="',i,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'" stroke-linecap="round" ',"/>\n"),e?e(f.join("")):f.join("")},complexity:function(){return this.path.length},_parsePath:function(){for(var e,n,f,o,d,i=[],t=[],l=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi,s=0,p=this.path.length;p>s;s++){for(e=this.path[s],o=e.slice(1).trim(),t.length=0;f=l.exec(o);)t.push(f[0]);d=[e.charAt(0)];for(var c=0,y=t.length;y>c;c++)n=parseFloat(t[c]),isNaN(n)||d.push(n);var m=d[0],r=u[m.toLowerCase()],v=a[m]||m;if(d.length-1>r)for(var w=1,b=d.length;b>w;w+=r)i.push([m].concat(d.slice(w,w+r))),m=v;else i.push(d)}return i},_parseDimensions:function(){var e=[],n=[],f={};this.path.forEach(function(o,d){this._getCoordsFromCommand(o,d,e,n,f)},this);var o=d(e),t=d(n),l=i(e),s=i(n),u=l-o,a=s-t,p={left:this.left+(o+u/2),top:this.top+(t+a/2),width:u,height:a};return p},_getCoordsFromCommand:function(e,o,d,i,t){var l=!1;"H"!==e[0]&&(t.x=n(0===o?e:this.path[o-1])),"V"!==e[0]&&(t.y=f(0===o?e:this.path[o-1])),e[0]===e[0].toLowerCase()&&(l=!0);var s,u=this._getXY(e,l,t);s=parseInt(u.x,10),isNaN(s)||d.push(s),s=parseInt(u.y,10),isNaN(s)||i.push(s)},_getXY:function(e,o,d){var i=o?d.x+n(e):"V"===e[0]?d.x:n(e),t=o?d.y+f(e):"H"===e[0]?d.y:f(e);return{x:i,y:t}}}),o.Path.fromObject=function(e,n){"string"==typeof e.path?o.loadSVGFromURL(e.path,function(f){var d=f[0],i=e.path;delete e.path,o.util.object.extend(d,e),d.setSourcePath(i),n(d)}):n(new o.Path(e.path,e))},o.Path.ATTRIBUTE_NAMES=o.SHARED_ATTRIBUTES.concat(["d"]),o.Path.fromElement=function(e,n,f){var d=o.parseAttributes(e,o.Path.ATTRIBUTE_NAMES);n&&n(new o.Path(d.d,t(d,f)))},void(o.Path.async=!0))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.invoke,d=n.Object.prototype.toObject;return n.PathGroup?void n.warn("fabric.PathGroup is already defined"):(n.PathGroup=n.util.createClass(n.Path,{type:"path-group",fill:"",initialize:function(e,n){n=n||{},this.paths=e||[];for(var f=this.paths.length;f--;)this.paths[f].group=this;this.setOptions(n),n.widthAttr&&(this.scaleX=n.widthAttr/n.width),n.heightAttr&&(this.scaleY=n.heightAttr/n.height),this.setCoords(),n.sourcePath&&this.setSourcePath(n.sourcePath)},render:function(e){if(this.visible){e.save();var f=this.transformMatrix;f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),this.transform(e),this._setShadow(e),this.clipTo&&n.util.clipContext(this,e);for(var o=0,d=this.paths.length;d>o;++o)this.paths[o].render(e,!0);this.clipTo&&e.restore(),this._removeShadow(e),e.restore()}},_set:function(e,n){if("fill"===e&&n&&this.isSameColor())for(var f=this.paths.length;f--;)this.paths[f]._set(e,n);return this.callSuper("_set",e,n)},toObject:function(e){var n=f(d.call(this,e),{paths:o(this.getObjects(),"toObject",e)});return this.sourcePath&&(n.sourcePath=this.sourcePath),n},toDatalessObject:function(e){var n=this.toObject(e);return this.sourcePath&&(n.paths=this.sourcePath),n},toSVG:function(e){for(var n=this.getObjects(),f="translate("+this.left+" "+this.top+")",o=["<g ",'style="',this.getSvgStyles(),'" ','transform="',f,this.getSvgTransform(),'" ',">\n"],d=0,i=n.length;i>d;d++)o.push(n[d].toSVG(e));return o.push("</g>\n"),e?e(o.join("")):o.join("")},toString:function(){return"#<fabric.PathGroup ("+this.complexity()+"): { top: "+this.top+", left: "+this.left+" }>"},isSameColor:function(){var e=(this.getObjects()[0].get("fill")||"").toLowerCase();return this.getObjects().every(function(n){return(n.get("fill")||"").toLowerCase()===e})},complexity:function(){return this.paths.reduce(function(e,n){return e+(n&&n.complexity?n.complexity():0)},0)},getObjects:function(){return this.paths}}),n.PathGroup.fromObject=function(e,f){"string"==typeof e.paths?n.loadSVGFromURL(e.paths,function(o){var d=e.paths;delete e.paths;var i=n.util.groupSVGElements(o,e,d);f(i)}):n.util.enlivenObjects(e.paths,function(o){delete e.paths,f(new n.PathGroup(o,e))})},void(n.PathGroup.async=!0))}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.array.min,d=n.util.array.max,i=n.util.array.invoke;if(!n.Group){var t={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};n.Group=n.util.createClass(n.Object,n.Collection,{type:"group",initialize:function(e,n){n=n||{},this._objects=e||[];for(var o=this._objects.length;o--;)this._objects[o].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),n&&f(this,n),this._setOpacityIfSame(),this.setCoords(),this.saveCoords()},_updateObjectsCoords:function(){this.forEachObject(this._updateObjectCoords,this)},_updateObjectCoords:function(e){var n=e.getLeft(),f=e.getTop();e.set({originalLeft:n,originalTop:f,left:n-this.left,top:f-this.top}),e.setCoords(),e.__origHasControls=e.hasControls,e.hasControls=!1},toString:function(){return"#<fabric.Group: ("+this.complexity()+")>"},addWithUpdate:function(e){return this._restoreObjectsState(),e&&(this._objects.push(e),e.group=this),this.forEachObject(this._setObjectActive,this),this._calcBounds(),this._updateObjectsCoords(),this},_setObjectActive:function(e){e.set("active",!0),e.group=this},removeWithUpdate:function(e){return this._moveFlippedObject(e),this._restoreObjectsState(),this.forEachObject(this._setObjectActive,this),this.remove(e),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(e){e.group=this},_onObjectRemoved:function(e){delete e.group,e.set("active",!1)},delegatedProperties:{fill:!0,opacity:!0,fontFamily:!0,fontWeight:!0,fontSize:!0,fontStyle:!0,lineHeight:!0,textDecoration:!0,textAlign:!0,backgroundColor:!0},_set:function(e,n){if(e in this.delegatedProperties){var f=this._objects.length;for(this[e]=n;f--;)this._objects[f].set(e,n)}else this[e]=n},toObject:function(e){return f(this.callSuper("toObject",e),{objects:i(this._objects,"toObject",e)})},render:function(e){if(this.visible){e.save(),this.clipTo&&n.util.clipContext(this,e);for(var f=0,o=this._objects.length;o>f;f++)this._renderObject(this._objects[f],e);this.clipTo&&e.restore(),e.restore()}},_renderControls:function(e,n){this.callSuper("_renderControls",e,n);for(var f=0,o=this._objects.length;o>f;f++)this._objects[f]._renderControls(e)},_renderObject:function(e,n){var f=e.hasRotatingPoint;e.visible&&(e.hasRotatingPoint=!1,e.render(n),e.hasRotatingPoint=f)},_restoreObjectsState:function(){return this._objects.forEach(this._restoreObjectState,this),this},_moveFlippedObject:function(e){var n=e.get("originX"),f=e.get("originY"),o=e.getCenterPoint();e.set({originX:"center",originY:"center",left:o.x,top:o.y}),this._toggleFlipping(e);var d=e.getPointByOrigin(n,f);return e.set({originX:n,originY:f,left:d.x,top:d.y}),this},_toggleFlipping:function(e){this.flipX&&(e.toggle("flipX"),e.set("left",-e.get("left")),e.setAngle(-e.getAngle())),this.flipY&&(e.toggle("flipY"),e.set("top",-e.get("top")),e.setAngle(-e.getAngle()))},_restoreObjectState:function(e){return this._setObjectPosition(e),e.setCoords(),e.hasControls=e.__origHasControls,delete e.__origHasControls,e.set("active",!1),e.setCoords(),delete e.group,this},_setObjectPosition:function(e){var n=this.getLeft(),f=this.getTop(),o=this._getRotatedLeftTop(e);e.set({angle:e.getAngle()+this.getAngle(),left:n+o.left,top:f+o.top,scaleX:e.get("scaleX")*this.get("scaleX"),scaleY:e.get("scaleY")*this.get("scaleY")})},_getRotatedLeftTop:function(e){var n=this.getAngle()*(Math.PI/180);return{left:-Math.sin(n)*e.getTop()*this.get("scaleY")+Math.cos(n)*e.getLeft()*this.get("scaleX"),top:Math.cos(n)*e.getTop()*this.get("scaleY")+Math.sin(n)*e.getLeft()*this.get("scaleX")}},destroy:function(){return this._objects.forEach(this._moveFlippedObject,this),this._restoreObjectsState()},saveCoords:function(){return this._originalLeft=this.get("left"),this._originalTop=this.get("top"),this},hasMoved:function(){return this._originalLeft!==this.get("left")||this._originalTop!==this.get("top")},setObjectsCoords:function(){return this.forEachObject(function(e){e.setCoords()}),this},_setOpacityIfSame:function(){var e=this.getObjects(),n=e[0]?e[0].get("opacity"):1,f=e.every(function(e){return e.get("opacity")===n});f&&(this.opacity=n)},_calcBounds:function(e){for(var n,f=[],o=[],d=0,i=this._objects.length;i>d;++d){n=this._objects[d],n.setCoords();for(var t in n.oCoords)f.push(n.oCoords[t].x),o.push(n.oCoords[t].y)}this.set(this._getBounds(f,o,e))},_getBounds:function(e,f,i){var t=n.util.invertTransform(this.getViewportTransform()),l=n.util.transformPoint(new n.Point(o(e),o(f)),t),s=n.util.transformPoint(new n.Point(d(e),d(f)),t),u={width:s.x-l.x||0,height:s.y-l.y||0};return i||(u.left=(l.x+s.x)/2||0,u.top=(l.y+s.y)/2||0),u},toSVG:function(e){for(var n=["<g ",'transform="',this.getSvgTransform(),'">\n'],f=0,o=this._objects.length;o>f;f++)n.push(this._objects[f].toSVG(e));return n.push("</g>\n"),e?e(n.join("")):n.join("")},get:function(e){if(e in t){if(this[e])return this[e];for(var n=0,f=this._objects.length;f>n;n++)if(this._objects[n][e])return!0;return!1}return e in this.delegatedProperties?this._objects[0]&&this._objects[0].get(e):this[e]}}),n.Group.fromObject=function(e,f){n.util.enlivenObjects(e.objects,function(o){delete e.objects,f&&f(new n.Group(o,e))})},n.Group.async=!0}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=fabric.util.object.extend;return e.fabric||(e.fabric={}),e.fabric.Image?void fabric.warn("fabric.Image is already defined."):(fabric.Image=fabric.util.createClass(fabric.Object,{type:"image",crossOrigin:"",initialize:function(e,n){n||(n={}),this.filters=[],this.callSuper("initialize",n),this._initElement(e,n),this._initConfig(n),n.filters&&(this.filters=n.filters,this.applyFilters())},getElement:function(){return this._element},setElement:function(e,n){return this._element=e,this._originalElement=e,this._initConfig(),0!==this.filters.length&&this.applyFilters(n),this},setCrossOrigin:function(e){return this.crossOrigin=e,this._element.crossOrigin=e,this},getOriginalSize:function(){var e=this.getElement();return{width:e.width,height:e.height}},_stroke:function(e){e.save(),this._setStrokeStyles(e),e.beginPath(),e.strokeRect(-this.width/2,-this.height/2,this.width,this.height),e.closePath(),e.restore()},_renderDashedStroke:function(e){var n=-this.width/2,f=-this.height/2,o=this.width,d=this.height;e.save(),this._setStrokeStyles(e),e.beginPath(),fabric.util.drawDashedLine(e,n,f,n+o,f,this.strokeDashArray),fabric.util.drawDashedLine(e,n+o,f,n+o,f+d,this.strokeDashArray),fabric.util.drawDashedLine(e,n+o,f+d,n,f+d,this.strokeDashArray),fabric.util.drawDashedLine(e,n,f+d,n,f,this.strokeDashArray),e.closePath(),e.restore()},toObject:function(e){return n(this.callSuper("toObject",e),{src:this._originalElement.src||this._originalElement._src,filters:this.filters.map(function(e){return e&&e.toObject()}),crossOrigin:this.crossOrigin})},toSVG:function(e){var n=[],f=-this.width/2,o=-this.height/2;if(this.group&&(f=this.left,o=this.top),n.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n','<image xlink:href="',this.getSvgSrc(),'" x="',f,'" y="',o,'" style="',this.getSvgStyles(),'" width="',this.width,'" height="',this.height,'" preserveAspectRatio="none"',"></image>\n"),this.stroke||this.strokeDashArray){var d=this.fill;this.fill=null,n.push("<rect ",'x="',f,'" y="',o,'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'"/>\n'),this.fill=d}return n.push("</g>\n"),e?e(n.join("")):n.join("")},getSrc:function(){return this.getElement()?this.getElement().src||this.getElement()._src:void 0},toString:function(){return'#<fabric.Image: { src: "'+this.getSrc()+'" }>'},clone:function(e,n){this.constructor.fromObject(this.toObject(n),e)},applyFilters:function(e){if(this._originalElement){if(0===this.filters.length)return this._element=this._originalElement,void(e&&e());var n=this._originalElement,f=fabric.util.createCanvasElement(),o=fabric.util.createImage(),d=this;return f.width=n.width,f.height=n.height,f.getContext("2d").drawImage(n,0,0,n.width,n.height),this.filters.forEach(function(e){e&&e.applyTo(f)}),o.width=n.width,o.height=n.height,fabric.isLikelyNode?(o.src=f.toBuffer(void 0,fabric.Image.pngCompression),d._element=o,e&&e()):(o.onload=function(){d._element=o,e&&e(),o.onload=f=n=null},o.src=f.toDataURL("image/png")),this}},_render:function(e,n){this._element&&e.drawImage(this._element,n?this.left:-this.width/2,n?this.top:-this.height/2,this.width,this.height),this._renderStroke(e)},_resetWidthHeight:function(){var e=this.getElement();this.set("width",e.width),this.set("height",e.height)},_initElement:function(e){this.setElement(fabric.util.getById(e)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(e){e||(e={}),this.setOptions(e),this._setWidthHeight(e),this._element&&this.crossOrigin&&(this._element.crossOrigin=this.crossOrigin)},_initFilters:function(e,n){e.filters&&e.filters.length?fabric.util.enlivenObjects(e.filters,function(e){n&&n(e)},"fabric.Image.filters"):n&&n()},_setWidthHeight:function(e){this.width="width"in e?e.width:this.getElement()?this.getElement().width||0:0,this.height="height"in e?e.height:this.getElement()?this.getElement().height||0:0
-},complexity:function(){return 1}}),fabric.Image.CSS_CANVAS="canvas-img",fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc,fabric.Image.fromObject=function(e,n){fabric.util.loadImage(e.src,function(f){fabric.Image.prototype._initFilters.call(e,e,function(o){e.filters=o||[];var d=new fabric.Image(f,e);n&&n(d)})},null,e.crossOrigin)},fabric.Image.fromURL=function(e,n,f){fabric.util.loadImage(e,function(e){n(new fabric.Image(e,f))},null,f&&f.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(e,f,o){var d=fabric.parseAttributes(e,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(d["xlink:href"],f,n(o?fabric.util.object.clone(o):{},d))},fabric.Image.async=!0,void(fabric.Image.pngCompression=1))}("undefined"!=typeof exports?exports:this),fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",toObject:function(){return{type:this.type}},toJSON:function(){return this.toObject()}}),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Brightness=n.util.createClass(n.Image.filters.BaseFilter,{type:"Brightness",initialize:function(e){e=e||{},this.brightness=e.brightness||0},applyTo:function(e){for(var n=e.getContext("2d"),f=n.getImageData(0,0,e.width,e.height),o=f.data,d=this.brightness,i=0,t=o.length;t>i;i+=4)o[i]+=d,o[i+1]+=d,o[i+2]+=d;n.putImageData(f,0,0)},toObject:function(){return f(this.callSuper("toObject"),{brightness:this.brightness})}}),n.Image.filters.Brightness.fromObject=function(e){return new n.Image.filters.Brightness(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Convolute=n.util.createClass(n.Image.filters.BaseFilter,{type:"Convolute",initialize:function(e){e=e||{},this.opaque=e.opaque,this.matrix=e.matrix||[0,0,0,0,1,0,0,0,0];var f=n.util.createCanvasElement();this.tmpCtx=f.getContext("2d")},_createImageData:function(e,n){return this.tmpCtx.createImageData(e,n)},applyTo:function(e){for(var n=this.matrix,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=Math.round(Math.sqrt(n.length)),i=Math.floor(d/2),t=o.data,l=o.width,s=o.height,u=l,a=s,p=this._createImageData(u,a),c=p.data,y=this.opaque?1:0,m=0;a>m;m++)for(var r=0;u>r;r++){for(var v=m,w=r,b=4*(m*u+r),g=0,h=0,x=0,j=0,k=0;d>k;k++)for(var q=0;d>q;q++){var z=v+k-i,A=w+q-i;if(!(0>z||z>s||0>A||A>l)){var B=4*(z*l+A),C=n[k*d+q];g+=t[B]*C,h+=t[B+1]*C,x+=t[B+2]*C,j+=t[B+3]*C}}c[b]=g,c[b+1]=h,c[b+2]=x,c[b+3]=j+y*(255-j)}f.putImageData(p,0,0)},toObject:function(){return f(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),n.Image.filters.Convolute.fromObject=function(e){return new n.Image.filters.Convolute(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.GradientTransparency=n.util.createClass(n.Image.filters.BaseFilter,{type:"GradientTransparency",initialize:function(e){e=e||{},this.threshold=e.threshold||100},applyTo:function(e){for(var n=e.getContext("2d"),f=n.getImageData(0,0,e.width,e.height),o=f.data,d=this.threshold,i=o.length,t=0,l=o.length;l>t;t+=4)o[t+3]=d+255*(i-t)/i;n.putImageData(f,0,0)},toObject:function(){return f(this.callSuper("toObject"),{threshold:this.threshold})}}),n.Image.filters.GradientTransparency.fromObject=function(e){return new n.Image.filters.GradientTransparency(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Grayscale=n.util.createClass(n.Image.filters.BaseFilter,{type:"Grayscale",applyTo:function(e){for(var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=o.width*o.height*4,t=0;i>t;)n=(d[t]+d[t+1]+d[t+2])/3,d[t]=n,d[t+1]=n,d[t+2]=n,t+=4;f.putImageData(o,0,0)}}),n.Image.filters.Grayscale.fromObject=function(){return new n.Image.filters.Grayscale}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Invert=n.util.createClass(n.Image.filters.BaseFilter,{type:"Invert",applyTo:function(e){var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=d.length;for(n=0;i>n;n+=4)d[n]=255-d[n],d[n+1]=255-d[n+1],d[n+2]=255-d[n+2];f.putImageData(o,0,0)}}),n.Image.filters.Invert.fromObject=function(){return new n.Image.filters.Invert}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Mask=n.util.createClass(n.Image.filters.BaseFilter,{type:"Mask",initialize:function(e){e=e||{},this.mask=e.mask,this.channel=[0,1,2,3].indexOf(e.channel)>-1?e.channel:0},applyTo:function(e){if(this.mask){var f,o=e.getContext("2d"),d=o.getImageData(0,0,e.width,e.height),i=d.data,t=this.mask.getElement(),l=n.util.createCanvasElement(),s=this.channel,u=d.width*d.height*4;l.width=t.width,l.height=t.height,l.getContext("2d").drawImage(t,0,0,t.width,t.height);var a=l.getContext("2d").getImageData(0,0,t.width,t.height),p=a.data;for(f=0;u>f;f+=4)i[f+3]=p[f+s];o.putImageData(d,0,0)}},toObject:function(){return f(this.callSuper("toObject"),{mask:this.mask.toObject(),channel:this.channel})}}),n.Image.filters.Mask.fromObject=function(e,f){n.util.loadImage(e.mask.src,function(o){e.mask=new n.Image(o,e.mask),f&&f(new n.Image.filters.Mask(e))})},n.Image.filters.Mask.async=!0}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Noise=n.util.createClass(n.Image.filters.BaseFilter,{type:"Noise",initialize:function(e){e=e||{},this.noise=e.noise||0},applyTo:function(e){for(var n,f=e.getContext("2d"),o=f.getImageData(0,0,e.width,e.height),d=o.data,i=this.noise,t=0,l=d.length;l>t;t+=4)n=(.5-Math.random())*i,d[t]+=n,d[t+1]+=n,d[t+2]+=n;f.putImageData(o,0,0)},toObject:function(){return f(this.callSuper("toObject"),{noise:this.noise})}}),n.Image.filters.Noise.fromObject=function(e){return new n.Image.filters.Noise(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Pixelate=n.util.createClass(n.Image.filters.BaseFilter,{type:"Pixelate",initialize:function(e){e=e||{},this.blocksize=e.blocksize||4},applyTo:function(e){var n,f,o,d,i,t,l,s=e.getContext("2d"),u=s.getImageData(0,0,e.width,e.height),a=u.data,p=u.height,c=u.width;for(f=0;p>f;f+=this.blocksize)for(o=0;c>o;o+=this.blocksize){n=4*f*c+4*o,d=a[n],i=a[n+1],t=a[n+2],l=a[n+3];for(var y=f,m=f+this.blocksize;m>y;y++)for(var r=o,v=o+this.blocksize;v>r;r++)n=4*y*c+4*r,a[n]=d,a[n+1]=i,a[n+2]=t,a[n+3]=l}s.putImageData(u,0,0)},toObject:function(){return f(this.callSuper("toObject"),{blocksize:this.blocksize})}}),n.Image.filters.Pixelate.fromObject=function(e){return new n.Image.filters.Pixelate(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.RemoveWhite=n.util.createClass(n.Image.filters.BaseFilter,{type:"RemoveWhite",initialize:function(e){e=e||{},this.threshold=e.threshold||30,this.distance=e.distance||20},applyTo:function(e){for(var n,f,o,d=e.getContext("2d"),i=d.getImageData(0,0,e.width,e.height),t=i.data,l=this.threshold,s=this.distance,u=255-l,a=Math.abs,p=0,c=t.length;c>p;p+=4)n=t[p],f=t[p+1],o=t[p+2],n>u&&f>u&&o>u&&a(n-f)<s&&a(n-o)<s&&a(f-o)<s&&(t[p+3]=1);d.putImageData(i,0,0)},toObject:function(){return f(this.callSuper("toObject"),{threshold:this.threshold,distance:this.distance})}}),n.Image.filters.RemoveWhite.fromObject=function(e){return new n.Image.filters.RemoveWhite(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Sepia=n.util.createClass(n.Image.filters.BaseFilter,{type:"Sepia",applyTo:function(e){var n,f,o=e.getContext("2d"),d=o.getImageData(0,0,e.width,e.height),i=d.data,t=i.length;for(n=0;t>n;n+=4)f=.3*i[n]+.59*i[n+1]+.11*i[n+2],i[n]=f+100,i[n+1]=f+50,i[n+2]=f+255;o.putImageData(d,0,0)}}),n.Image.filters.Sepia.fromObject=function(){return new n.Image.filters.Sepia}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={});n.Image.filters.Sepia2=n.util.createClass(n.Image.filters.BaseFilter,{type:"Sepia2",applyTo:function(e){var n,f,o,d,i=e.getContext("2d"),t=i.getImageData(0,0,e.width,e.height),l=t.data,s=l.length;for(n=0;s>n;n+=4)f=l[n],o=l[n+1],d=l[n+2],l[n]=(.393*f+.769*o+.189*d)/1.351,l[n+1]=(.349*f+.686*o+.168*d)/1.203,l[n+2]=(.272*f+.534*o+.131*d)/2.14;i.putImageData(t,0,0)}}),n.Image.filters.Sepia2.fromObject=function(){return new n.Image.filters.Sepia2}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Tint=n.util.createClass(n.Image.filters.BaseFilter,{type:"Tint",initialize:function(e){e=e||{},this.color=e.color||"#000000",this.opacity="undefined"!=typeof e.opacity?e.opacity:new n.Color(this.color).getAlpha()},applyTo:function(e){var f,o,d,i,t,l,s,u,a,p=e.getContext("2d"),c=p.getImageData(0,0,e.width,e.height),y=c.data,m=y.length;for(a=new n.Color(this.color).getSource(),o=a[0]*this.opacity,d=a[1]*this.opacity,i=a[2]*this.opacity,u=1-this.opacity,f=0;m>f;f+=4)t=y[f],l=y[f+1],s=y[f+2],y[f]=o+t*u,y[f+1]=d+l*u,y[f+2]=i+s*u;p.putImageData(c,0,0)},toObject:function(){return f(this.callSuper("toObject"),{color:this.color,opacity:this.opacity})}}),n.Image.filters.Tint.fromObject=function(e){return new n.Image.filters.Tint(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend;n.Image.filters.Multiply=n.util.createClass(n.Image.filters.BaseFilter,{type:"Multiply",initialize:function(e){e=e||{},this.color=e.color||"#000000"},applyTo:function(e){var f,o,d=e.getContext("2d"),i=d.getImageData(0,0,e.width,e.height),t=i.data,l=t.length;for(o=new n.Color(this.color).getSource(),f=0;l>f;f+=4)t[f]*=o[0]/255,t[f+1]*=o[1]/255,t[f+2]*=o[2]/255;d.putImageData(i,0,0)},toObject:function(){return f(this.callSuper("toObject"),{color:this.color})}}),n.Image.filters.Multiply.fromObject=function(e){return new n.Image.filters.Multiply(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric;n.Image.filters.Blend=n.util.createClass({type:"Blend",initialize:function(e){e=e||{},this.color=e.color||"#000",this.image=e.image||!1,this.mode=e.mode||"multiply",this.alpha=e.alpha||1},applyTo:function(e){var f,o,d,i,t,l,s,u=e.getContext("2d"),a=u.getImageData(0,0,e.width,e.height),p=a.data,c=!1;if(this.image){c=!0;var y=n.util.createCanvasElement();y.width=this.image.width,y.height=this.image.height;var m=new n.StaticCanvas(y);m.add(this.image);var r=m.getContext("2d");s=r.getImageData(0,0,m.width,m.height).data}else s=new n.Color(this.color).getSource(),f=s[0]*this.alpha,o=s[1]*this.alpha,d=s[2]*this.alpha;for(var v=0,w=p.length;w>v;v+=4)switch(i=p[v],t=p[v+1],l=p[v+2],c&&(f=s[v]*this.alpha,o=s[v+1]*this.alpha,d=s[v+2]*this.alpha),this.mode){case"multiply":p[v]=i*f/255,p[v+1]=t*o/255,p[v+2]=l*d/255;break;case"screen":p[v]=1-(1-i)*(1-f),p[v+1]=1-(1-t)*(1-o),p[v+2]=1-(1-l)*(1-d);break;case"add":p[v]=Math.min(255,i+f),p[v+1]=Math.min(255,t+o),p[v+2]=Math.min(255,l+d);break;case"diff":case"difference":p[v]=Math.abs(i-f),p[v+1]=Math.abs(t-o),p[v+2]=Math.abs(l-d);break;case"subtract":var b=i-f,g=t-o,h=l-d;p[v]=0>b?0:b,p[v+1]=0>g?0:g,p[v+2]=0>h?0:h;break;case"darken":p[v]=Math.min(i,f),p[v+1]=Math.min(t,o),p[v+2]=Math.min(l,d);break;case"lighten":p[v]=Math.max(i,f),p[v+1]=Math.max(t,o),p[v+2]=Math.max(l,d)}u.putImageData(a,0,0)}}),n.Image.filters.Blend.fromObject=function(e){return new n.Image.filters.Blend(e)}}("undefined"!=typeof exports?exports:this),function(e){"use strict";var n=e.fabric||(e.fabric={}),f=n.util.object.extend,o=n.util.object.clone,d=n.util.toFixed,i=n.StaticCanvas.supports("setLineDash");if(n.Text)return void n.warn("fabric.Text is already defined");var t=n.Object.prototype.stateProperties.concat();t.push("fontFamily","fontWeight","fontSize","text","textDecoration","textAlign","fontStyle","lineHeight","textBackgroundColor","useNative","path"),n.Text=n.util.createClass(n.Object,{_dimensionAffectingProps:{fontSize:!0,fontWeight:!0,fontFamily:!0,textDecoration:!0,fontStyle:!0,lineHeight:!0,stroke:!0,strokeWidth:!0,text:!0},_reNewline:/\r?\n/,type:"text",fontSize:40,fontWeight:"normal",fontFamily:"Times New Roman",textDecoration:"",textAlign:"left",fontStyle:"",lineHeight:1.3,textBackgroundColor:"",path:null,useNative:!0,stateProperties:t,stroke:null,shadow:null,initialize:function(e,n){n=n||{},this.text=e,this.__skipDimension=!0,this.setOptions(n),this.__skipDimension=!1,this._initDimensions()},_initDimensions:function(){if(!this.__skipDimension){var e=n.util.createCanvasElement();this._render(e.getContext("2d"))}},toString:function(){return"#<fabric.Text ("+this.complexity()+'): { "text": "'+this.text+'", "fontFamily": "'+this.fontFamily+'" }>'},_render:function(e){"undefined"==typeof Cufon||this.useNative===!0?this._renderViaNative(e):this._renderViaCufon(e)},_renderViaNative:function(e){var f=this.text.split(this._reNewline);this._setTextStyles(e),this.width=this._getTextWidth(e,f),this.height=this._getTextHeight(e,f),this.clipTo&&n.util.clipContext(this,e),this._renderTextBackground(e,f),this._translateForTextAlign(e),this._renderText(e,f),"left"!==this.textAlign&&"justify"!==this.textAlign&&e.restore(),this._renderTextDecoration(e,f),this.clipTo&&e.restore(),this._setBoundaries(e,f),this._totalLineHeight=0},_renderText:function(e,n){e.save(),this._setShadow(e),this._setupFillRule(e),this._renderTextFill(e,n),this._renderTextStroke(e,n),this._restoreFillRule(e),this._removeShadow(e),e.restore()},_translateForTextAlign:function(e){"left"!==this.textAlign&&"justify"!==this.textAlign&&(e.save(),e.translate("center"===this.textAlign?this.width/2:this.width,0))},_setBoundaries:function(e,n){this._boundaries=[];for(var f=0,o=n.length;o>f;f++){var d=this._getLineWidth(e,n[f]),i=this._getLineLeftOffset(d);this._boundaries.push({height:this.fontSize*this.lineHeight,width:d,left:i})}},_setTextStyles:function(e){this._setFillStyles(e),this._setStrokeStyles(e),e.textBaseline="alphabetic",this.skipTextAlign||(e.textAlign=this.textAlign),e.font=this._getFontDeclaration()},_getTextHeight:function(e,n){return this.fontSize*n.length*this.lineHeight},_getTextWidth:function(e,n){for(var f=e.measureText(n[0]||"|").width,o=1,d=n.length;d>o;o++){var i=e.measureText(n[o]).width;i>f&&(f=i)}return f},_renderChars:function(e,n,f,o,d){n[e](f,o,d)},_renderTextLine:function(e,n,f,o,d,i){if(d-=this.fontSize/4,"justify"!==this.textAlign)return void this._renderChars(e,n,f,o,d,i);var t=n.measureText(f).width,l=this.width;if(l>t)for(var s=f.split(/\s+/),u=n.measureText(f.replace(/\s+/g,"")).width,a=l-u,p=s.length-1,c=a/p,y=0,m=0,r=s.length;r>m;m++)this._renderChars(e,n,s[m],o+y,d,i),y+=n.measureText(s[m]).width+c;else this._renderChars(e,n,f,o,d,i)},_getLeftOffset:function(){return n.isLikelyNode?0:-this.width/2},_getTopOffset:function(){return-this.height/2},_renderTextFill:function(e,n){if(this.fill||this._skipFillStrokeCheck){this._boundaries=[];for(var f=0,o=0,d=n.length;d>o;o++){var i=this._getHeightOfLine(e,o,n);f+=i,this._renderTextLine("fillText",e,n[o],this._getLeftOffset(),this._getTopOffset()+f,o)}}},_renderTextStroke:function(e,n){if(this.stroke&&0!==this.strokeWidth||this._skipFillStrokeCheck){var f=0;e.save(),this.strokeDashArray&&(1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),i&&e.setLineDash(this.strokeDashArray)),e.beginPath();for(var o=0,d=n.length;d>o;o++){var t=this._getHeightOfLine(e,o,n);f+=t,this._renderTextLine("strokeText",e,n[o],this._getLeftOffset(),this._getTopOffset()+f,o)}e.closePath(),e.restore()}},_getHeightOfLine:function(){return this.fontSize*this.lineHeight},_renderTextBackground:function(e,n){this._renderTextBoxBackground(e),this._renderTextLinesBackground(e,n)},_renderTextBoxBackground:function(e){this.backgroundColor&&(e.save(),e.fillStyle=this.backgroundColor,e.fillRect(this._getLeftOffset(),this._getTopOffset(),this.width,this.height),e.restore())},_renderTextLinesBackground:function(e,n){if(this.textBackgroundColor){e.save(),e.fillStyle=this.textBackgroundColor;for(var f=0,o=n.length;o>f;f++)if(""!==n[f]){var d=this._getLineWidth(e,n[f]),i=this._getLineLeftOffset(d);e.fillRect(this._getLeftOffset()+i,this._getTopOffset()+f*this.fontSize*this.lineHeight,d,this.fontSize*this.lineHeight)}e.restore()}},_getLineLeftOffset:function(e){return"center"===this.textAlign?(this.width-e)/2:"right"===this.textAlign?this.width-e:0},_getLineWidth:function(e,n){return"justify"===this.textAlign?this.width:e.measureText(n).width},_renderTextDecoration:function(e,n){function f(f){for(var i=0,t=n.length;t>i;i++){var l=d._getLineWidth(e,n[i]),s=d._getLineLeftOffset(l);e.fillRect(d._getLeftOffset()+s,~~(f+i*d._getHeightOfLine(e,i,n)-o),l,1)}}if(this.textDecoration){var o=this._getTextHeight(e,n)/2,d=this;this.textDecoration.indexOf("underline")>-1&&f(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&f(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&f(this.fontSize*this.lineHeight-this.fontSize)}},_getFontDeclaration:function(){return[n.isLikelyNode?this.fontWeight:this.fontStyle,n.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",n.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(e,n){if(this.visible){e.save(),this._transform(e,n);var f=this.transformMatrix,o=this.group&&"path-group"===this.group.type;o&&e.translate(-this.group.width/2,-this.group.height/2),f&&e.transform(f[0],f[1],f[2],f[3],f[4],f[5]),o&&e.translate(this.left,this.top),this._render(e),e.restore()}},toObject:function(e){var n=f(this.callSuper("toObject",e),{text:this.text,fontSize:this.fontSize,fontWeight:this.fontWeight,fontFamily:this.fontFamily,fontStyle:this.fontStyle,lineHeight:this.lineHeight,textDecoration:this.textDecoration,textAlign:this.textAlign,path:this.path,textBackgroundColor:this.textBackgroundColor,useNative:this.useNative});return this.includeDefaultValues||this._removeDefaultValues(n),n},toSVG:function(e){var n=[],f=this.text.split(this._reNewline),o=this._getSVGLeftTopOffsets(f),d=this._getSVGTextAndBg(o.lineTop,o.textLeft,f),i=this._getSVGShadows(o.lineTop,f);return o.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._wrapSVGTextAndBg(n,d,i,o),e?e(n.join("")):n.join("")},_getSVGLeftTopOffsets:function(e){var n=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,f=-(this.width/2),o=this.useNative?this.fontSize-1:this.height/2-e.length*this.fontSize-this._totalLineHeight;return{textLeft:f+(this.group&&"path-group"===this.group.type?this.left:0),textTop:o+(this.group&&"path-group"===this.group.type?this.top:0),lineTop:n}},_wrapSVGTextAndBg:function(e,n,f,o){e.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n',n.textBgRects.join(""),"<text ",this.fontFamily?'font-family="'+this.fontFamily.replace(/"/g,"'")+'" ':"",this.fontSize?'font-size="'+this.fontSize+'" ':"",this.fontStyle?'font-style="'+this.fontStyle+'" ':"",this.fontWeight?'font-weight="'+this.fontWeight+'" ':"",this.textDecoration?'text-decoration="'+this.textDecoration+'" ':"",'style="',this.getSvgStyles(),'" ','transform="translate(',d(o.textLeft,2)," ",d(o.textTop,2),')">',f.join(""),n.textSpans.join(""),"</text>\n","</g>\n")},_getSVGShadows:function(e,f){var o,i,t=[],l=1;if(!this.shadow||!this._boundaries)return t;for(o=0,i=f.length;i>o;o++)if(""!==f[o]){var s=this._boundaries&&this._boundaries[o]?this._boundaries[o].left:0;t.push('<tspan x="',d(s+l+this.shadow.offsetX,2),0===o||this.useNative?'" y':'" dy','="',d(this.useNative?e*o-this.height/2+this.shadow.offsetY:e+(0===o?this.shadow.offsetY:0),2),'" ',this._getFillAttributes(this.shadow.color),">",n.util.string.escapeXml(f[o]),"</tspan>"),l=1}else l++;return t},_getSVGTextAndBg:function(e,n,f){var o=[],d=[],i=1;this._setSVGBg(d);for(var t=0,l=f.length;l>t;t++)""!==f[t]?(this._setSVGTextLineText(f[t],t,o,e,i,d),i=1):i++,this.textBackgroundColor&&this._boundaries&&this._setSVGTextLineBg(d,t,n,e);return{textSpans:o,textBgRects:d}},_setSVGTextLineText:function(e,f,o,i,t){var l=this._boundaries&&this._boundaries[f]?d(this._boundaries[f].left,2):0;o.push('<tspan x="',l,'" ',0===f||this.useNative?"y":"dy",'="',d(this.useNative?i*f-this.height/2:i*t,2),'" ',this._getFillAttributes(this.fill),">",n.util.string.escapeXml(e),"</tspan>")},_setSVGTextLineBg:function(e,n,f,o){e.push("<rect ",this._getFillAttributes(this.textBackgroundColor),' x="',d(f+this._boundaries[n].left,2),'" y="',d(o*n-this.height/2,2),'" width="',d(this._boundaries[n].width,2),'" height="',d(this._boundaries[n].height,2),'"></rect>\n')},_setSVGBg:function(e){this.backgroundColor&&this._boundaries&&e.push("<rect ",this._getFillAttributes(this.backgroundColor),' x="',d(-this.width/2,2),'" y="',d(-this.height/2,2),'" width="',d(this.width,2),'" height="',d(this.height,2),'"></rect>')},_getFillAttributes:function(e){var f=e&&"string"==typeof e?new n.Color(e):"";return f&&f.getSource()&&1!==f.getAlpha()?'opacity="'+f.getAlpha()+'" fill="'+f.setAlpha(1).toRgb()+'"':'fill="'+e+'"'},_set:function(e,n){"fontFamily"===e&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+n+"$3")),this.callSuper("_set",e,n),e in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),n.Text.ATTRIBUTE_NAMES=n.SHARED_ATTRIBUTES.concat("x y dx dy font-family font-style font-weight font-size text-decoration text-anchor".split(" ")),n.Text.DEFAULT_SVG_FONT_SIZE=16,n.Text.fromElement=function(e,f){if(!e)return null;var o=n.parseAttributes(e,n.Text.ATTRIBUTE_NAMES);f=n.util.object.extend(f?n.util.object.clone(f):{},o),"dx"in o&&(f.left+=o.dx),"dy"in o&&(f.top+=o.dy),"fontSize"in f||(f.fontSize=n.Text.DEFAULT_SVG_FONT_SIZE),f.originX||(f.originX="left");var d=new n.Text(e.textContent,f),i=0;return"left"===d.originX&&(i=d.getWidth()/2),"right"===d.originX&&(i=-d.getWidth()/2),d.set({left:d.getLeft()+i,top:d.getTop()-d.getHeight()/2}),d},n.Text.fromObject=function(e){return new n.Text(e.text,o(e))},n.util.createAccessors(n.Text)}("undefined"!=typeof exports?exports:this)}).call(this,require("buffer").Buffer)},{buffer:2,canvas:1,jsdom:1}]},{},[6])(6)});
\ No newline at end of file
diff --git a/package.json b/package.json
index e424d55..7fde346 100644
--- a/package.json
+++ b/package.json
@@ -12,9 +12,6 @@
   "engines": {
     "node": ">=0.10.0"
   },
-  "dependencies": {
-    "es6-promise": "^2.0.1"
-  },
   "repository": {
     "type": "git",
     "url": "git@github.com:niklasvh/html2canvas.git"
diff --git a/readme.md b/readme.md
index a1709ba..972637f 100644
--- a/readme.md
+++ b/readme.md
@@ -20,7 +20,7 @@ The script is still in a **very experimental state**, so I don't recommend using
 
 ###Browser compatibility###
 
-The script should work fine on the following browsers:
+The library should work fine on the following browsers (with `Promise` polyfill):
 
 * Firefox 3.5+
 * Google Chrome
@@ -32,6 +32,10 @@ As each CSS property needs to be manually built to be supported, there are a num
 
 ### Usage ###
 
+The html2canvas library utilizes `Promise`s and expects them to be available in the global context. If you wish to
+support [older browsers](http://caniuse.com/#search=promise) that do not natively support `Promise`s, please include a polyfill such as
+[es6-promise](https://github.com/jakearchibald/es6-promise) before including `html2canvas`.
+
 **Note!** These instructions are for using the current dev version of 0.5, for the latest release version (0.4.1), checkout the [old readme](https://github.com/niklasvh/html2canvas/blob/v0.4/readme.md).
 
 To render an `element` with html2canvas, simply call:
diff --git a/src/clone.js b/src/clone.js
index 826d45b..bafaa09 100644
--- a/src/clone.js
+++ b/src/clone.js
@@ -1,5 +1,4 @@
 var log = require('./log');
-var Promise = require('./promise');
 
 function restoreOwnerScroll(ownerDocument, x, y) {
     if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
diff --git a/src/core.js b/src/core.js
index b2b81e9..ce6a2de 100644
--- a/src/core.js
+++ b/src/core.js
@@ -1,4 +1,3 @@
-var Promise = require('./promise');
 var Support = require('./support');
 var CanvasRenderer = require('./renderers/canvas');
 var ImageLoader = require('./imageloader');
@@ -51,7 +50,6 @@ function html2canvas(nodeList, options) {
     });
 }
 
-html2canvas.Promise = Promise;
 html2canvas.CanvasRenderer = CanvasRenderer;
 html2canvas.NodeContainer = NodeContainer;
 html2canvas.log = log;
diff --git a/src/dummyimagecontainer.js b/src/dummyimagecontainer.js
index 5cb2700..4ee7401 100644
--- a/src/dummyimagecontainer.js
+++ b/src/dummyimagecontainer.js
@@ -1,4 +1,3 @@
-var Promise = require('./promise');
 var log = require('./log');
 var smallImage = require('./utils').smallImage;
 
diff --git a/src/framecontainer.js b/src/framecontainer.js
index 6f6efe8..475b9fa 100644
--- a/src/framecontainer.js
+++ b/src/framecontainer.js
@@ -1,5 +1,4 @@
 var utils = require('./utils');
-var Promise = require('./promise');
 var getBounds = utils.getBounds;
 var loadUrlDocument = require('./proxy').loadUrlDocument;
 
diff --git a/src/gradientcontainer.js b/src/gradientcontainer.js
index 4b91354..8520f34 100644
--- a/src/gradientcontainer.js
+++ b/src/gradientcontainer.js
@@ -1,5 +1,3 @@
-var Promise = require('./promise');
-
 function GradientContainer(imageData) {
     this.src = imageData.value;
     this.colorStops = [];
diff --git a/src/imagecontainer.js b/src/imagecontainer.js
index 4f46dd2..8710b56 100644
--- a/src/imagecontainer.js
+++ b/src/imagecontainer.js
@@ -1,5 +1,3 @@
-var Promise = require('./promise');
-
 function ImageContainer(src, cors) {
     this.src = src;
     this.image = new Image();
diff --git a/src/imageloader.js b/src/imageloader.js
index 176db56..3a504e1 100644
--- a/src/imageloader.js
+++ b/src/imageloader.js
@@ -1,4 +1,3 @@
-var Promise = require('./promise');
 var log = require('./log');
 var ImageContainer = require('./imagecontainer');
 var DummyImageContainer = require('./dummyimagecontainer');
diff --git a/src/nodeparser.js b/src/nodeparser.js
index aa5c25c..683f9bd 100644
--- a/src/nodeparser.js
+++ b/src/nodeparser.js
@@ -5,7 +5,6 @@ var TextContainer = require('./textcontainer');
 var PseudoElementContainer = require('./pseudoelementcontainer');
 var FontMetrics = require('./fontmetrics');
 var Color = require('./color');
-var Promise = require('./promise');
 var StackingContext = require('./stackingcontext');
 var utils = require('./utils');
 var bind = utils.bind;
diff --git a/src/promise.js b/src/promise.js
deleted file mode 100644
index 1d7217a..0000000
--- a/src/promise.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('es6-promise').Promise;
diff --git a/src/proxy.js b/src/proxy.js
index 51bb7b0..8a41ac6 100644
--- a/src/proxy.js
+++ b/src/proxy.js
@@ -1,4 +1,3 @@
-var Promise = require('./promise');
 var XHR = require('./xhr');
 var utils = require('./utils');
 var log = require('./log');
diff --git a/src/proxyimagecontainer.js b/src/proxyimagecontainer.js
index ea1b4c7..1cac032 100644
--- a/src/proxyimagecontainer.js
+++ b/src/proxyimagecontainer.js
@@ -1,5 +1,4 @@
 var ProxyURL = require('./proxy').ProxyURL;
-var Promise = require('./promise');
 
 function ProxyImageContainer(src, proxy) {
     var link = document.createElement("a");
diff --git a/src/svgcontainer.js b/src/svgcontainer.js
index 0a3ed68..67280cd 100644
--- a/src/svgcontainer.js
+++ b/src/svgcontainer.js
@@ -1,4 +1,3 @@
-var Promise = require('./promise');
 var XHR = require('./xhr');
 var decode64 = require('./utils').decode64;
 
diff --git a/src/svgnodecontainer.js b/src/svgnodecontainer.js
index 2001662..9d5f1c3 100644
--- a/src/svgnodecontainer.js
+++ b/src/svgnodecontainer.js
@@ -1,5 +1,4 @@
 var SVGContainer = require('./svgcontainer');
-var Promise = require('./promise');
 
 function SVGNodeContainer(node, _native) {
     this.src = node;
diff --git a/src/xhr.js b/src/xhr.js
index 534f849..e7ae375 100644
--- a/src/xhr.js
+++ b/src/xhr.js
@@ -1,5 +1,3 @@
-var Promise = require('./promise');
-
 function XHR(url) {
     return new Promise(function(resolve, reject) {
         var xhr = new XMLHttpRequest();
diff --git a/tests/mocha/background.html b/tests/mocha/background.html
index 7ecb5d9..626a557 100644
--- a/tests/mocha/background.html
+++ b/tests/mocha/background.html
@@ -3,6 +3,7 @@
     <meta charset="utf-8">
     <title>Mocha Tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
diff --git a/tests/mocha/cropping.html b/tests/mocha/cropping.html
index fbb1444..2a6d4fc 100644
--- a/tests/mocha/cropping.html
+++ b/tests/mocha/cropping.html
@@ -3,6 +3,7 @@
     <meta charset="utf-8">
     <title>Mocha Tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
diff --git a/tests/mocha/form-rendering.html b/tests/mocha/form-rendering.html
index 1f2dff1..363b836 100644
--- a/tests/mocha/form-rendering.html
+++ b/tests/mocha/form-rendering.html
@@ -3,6 +3,7 @@
     <meta charset="utf-8">
     <title>Mocha Tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
diff --git a/tests/mocha/ie9-clonenode-bug.html b/tests/mocha/ie9-clonenode-bug.html
index 78b246c..7949018 100644
--- a/tests/mocha/ie9-clonenode-bug.html
+++ b/tests/mocha/ie9-clonenode-bug.html
@@ -4,6 +4,7 @@
     <meta charset="UTF-8">
     <title>Proxy tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
     <script src="lib/mocha.js"></script>
diff --git a/tests/mocha/multiple-renders.html b/tests/mocha/multiple-renders.html
index 8307638..ff30a22 100644
--- a/tests/mocha/multiple-renders.html
+++ b/tests/mocha/multiple-renders.html
@@ -3,6 +3,7 @@
     <meta charset="utf-8">
     <title>Mocha Tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
@@ -25,7 +26,6 @@
 <div id="green-block"></div>
 <script>mocha.setup('bdd')</script>
 <script>
-    var Promise = html2canvas.Promise;
     describe("Multiple renders", function() {
         it("render correctly", function(done) {
             this.timeout(10000);
diff --git a/tests/mocha/options.onclone.html b/tests/mocha/options.onclone.html
index 3f0385d..da1f578 100644
--- a/tests/mocha/options.onclone.html
+++ b/tests/mocha/options.onclone.html
@@ -3,6 +3,7 @@
     <meta charset="utf-8">
     <title>Mocha Tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
@@ -23,7 +24,6 @@
 <div style="background: red; width: 200px; height:200px;" id="block"></div>
 <div style="width: 200px; height:200px;" id="block2"></div>
 <script>
-    var Promise = html2canvas.Promise;
     describe("options.onclone", function() {
         it("with a function", function(done) {
             html2canvas(document.querySelector("#block"), {onclone: function(document) {
diff --git a/tests/mocha/scrolling.html b/tests/mocha/scrolling.html
index 10a6fab..769753f 100644
--- a/tests/mocha/scrolling.html
+++ b/tests/mocha/scrolling.html
@@ -4,6 +4,7 @@
     <meta charset="UTF-8">
     <title>Scrolling tests</title>
     <link rel="stylesheet" href="lib/mocha.css" />
+    <script src="../../node_modules/bluebird/js/browser/bluebird.js"></script>
     <script src="../../dist/html2canvas.js"></script>
     <script src="../assets/jquery-1.6.2.js"></script>
     <script src="lib/expect.js"></script>
diff --git a/tests/test.js b/tests/test.js
index 61d3888..2312b32 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -4,10 +4,7 @@ var h2cSelector, h2cOptions;
         document.write('<script type="text/javascript" src="' + src + '.js?' + Math.random() + '"></script>');
     }
 
-    var sources = ['log', 'punycode/punycode', 'core', 'nodecontainer', 'pseudoelementcontainer', 'stackingcontext', 'textcontainer', 'support', 'imagecontainer', 'dummyimagecontainer', 'proxyimagecontainer', 'gradientcontainer',
-        'lineargradientcontainer', 'webkitgradientcontainer', 'svgcontainer', 'svgnodecontainer', 'imageloader', 'nodeparser', 'font', 'fontmetrics', 'renderer', 'promise', 'xhr', 'framecontainer', 'proxy', 'color', 'renderers/canvas'];
-
-    ['/tests/assets/jquery-1.6.2', '/dist/html2canvas'].forEach(appendScript);
+    ['/node_modules/bluebird/js/browser/bluebird', '/tests/assets/jquery-1.6.2', '/dist/html2canvas'].forEach(appendScript);
 
     if (typeof(noFabric) === "undefined") {
         appendScript('/dist/html2canvas.svg');