From 120f767e7d664246d82368c4043e9fa01e448ff6 Mon Sep 17 00:00:00 2001
From: MoyuScript <i@moyu.moe>
Date: Thu, 20 Nov 2014 14:27:34 -0600
Subject: [PATCH 1/2] add html2canvas-container classname to iframe

---
 dist/html2canvas.js         |   530 +-
 dist/html2canvas.min.js     |     4 +-
 dist/html2canvas.svg.js     | 17691 ----------------------------------
 dist/html2canvas.svg.min.js |     7 +-
 src/core.js                 |     1 +
 5 files changed, 5 insertions(+), 18228 deletions(-)

diff --git a/dist/html2canvas.js b/dist/html2canvas.js
index abd33f8..4040f59 100644
--- a/dist/html2canvas.js
+++ b/dist/html2canvas.js
@@ -37,535 +37,6 @@ if (typeof(Object.create) !== "function" || typeof(document.createElement("canva
     return;
 }
 
-/*! https://mths.be/punycode v1.3.1 by @mathias */
-;(function(root) {
-
-	/** Detect free variables */
-	var freeExports = typeof exports == 'object' && exports &&
-		!exports.nodeType && exports;
-	var freeModule = typeof module == 'object' && module &&
-		!module.nodeType && module;
-	var freeGlobal = typeof global == 'object' && global;
-	if (
-		freeGlobal.global === freeGlobal ||
-		freeGlobal.window === freeGlobal ||
-		freeGlobal.self === 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 = /[^\x20-\x7E]/, // 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;
-		var result = [];
-		while (length--) {
-			result[length] = fn(array[length]);
-		}
-		return result;
-	}
-
-	/**
-	 * A simple `Array#map`-like wrapper to work with domain name strings or email
-	 * addresses.
-	 * @private
-	 * @param {String} domain The domain name or email address.
-	 * @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) {
-		var parts = string.split('@');
-		var result = '';
-		if (parts.length > 1) {
-			// In email addresses, only the domain name should be punycoded. Leave
-			// the local part (i.e. everything up to `@`) intact.
-			result = parts[0] + '@';
-			string = parts[1];
-		}
-		var labels = string.split(regexSeparators);
-		var encoded = map(labels, fn).join('.');
-		return result + encoded;
-	}
-
-	/**
-	 * 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 <https://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 (e.g. a domain name label) 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 or an email address
-	 * to Unicode. Only the Punycoded parts of the input 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} input The Punycoded domain name or email address to
-	 * convert to Unicode.
-	 * @returns {String} The Unicode representation of the given Punycode
-	 * string.
-	 */
-	function toUnicode(input) {
-		return mapDomain(input, function(string) {
-			return regexPunycode.test(string)
-				? decode(string.slice(4).toLowerCase())
-				: string;
-		});
-	}
-
-	/**
-	 * Converts a Unicode string representing a domain name or an email address 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} input The domain name or email address to convert, as a
-	 * Unicode string.
-	 * @returns {String} The Punycode representation of the given domain name or
-	 * email address.
-	 */
-	function toASCII(input) {
-		return mapDomain(input, 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.3.1',
-		/**
-		 * An object of methods to convert from JavaScript's internal character
-		 * representation (UCS-2) to Unicode code points, and back.
-		 * @see <https://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 && freeModule) {
-		if (module.exports == freeExports) { // 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));
-
 var html2canvasNodeAttribute = "data-html2canvas-node";
 var html2canvasCanvasCloneAttribute = "data-html2canvas-canvas-clone";
 var html2canvasCanvasCloneIndex = 0;
@@ -692,6 +163,7 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
     var documentElement = ownerDocument.documentElement.cloneNode(true),
         container = containerDocument.createElement("iframe");
 
+    container.className = "html2canvas-container";
     container.style.visibility = "hidden";
     container.style.position = "absolute";
     container.style.left = container.style.top = "-10000px";
diff --git a/dist/html2canvas.min.js b/dist/html2canvas.min.js
index cac6e26..e1598de 100644
--- a/dist/html2canvas.min.js
+++ b/dist/html2canvas.min.js
@@ -4,5 +4,5 @@
 
   Released under MIT License
 */
-(function(a,b,c,d,e,f,g){function h(a,b,c,d){return o(a,a,c,d,b).then(function(e){E("Document cloned");var f="["+Qb+"='true']";a.querySelector(f).removeAttribute(Qb);var g=e.contentWindow,h=g.document.querySelector(f),j=Promise.resolve("function"==typeof b.onclone?b.onclone(g.document):!0);return j.then(function(){return i(h,e,b,c,d)})})}function i(a,c,d,e,f){var g=c.contentWindow,h=new Gb(g.document),i=new C(d,h),n=M(a),o="view"===d.type?e:l(g.document),p="view"===d.type?f:m(g.document),q=new Ob(o,p,i,d,b),r=new O(a,q,h,i,d);return r.ready.then(function(){E("Finished rendering");var b;return b="view"===d.type?k(q.canvas,{width:q.canvas.width,height:q.canvas.height,top:0,left:0,x:0,y:0}):a===g.document.body||a===g.document.documentElement||null!=d.canvas?q.canvas:k(q.canvas,{width:null!=d.width?d.width:n.width,height:null!=d.height?d.height:n.height,top:n.top,left:n.left,x:g.pageXOffset,y:g.pageYOffset}),j(c,d),b})}function j(a,b){b.removeContainer&&(a.parentNode.removeChild(a),E("Cleaned up container"))}function k(a,c){var d=b.createElement("canvas"),e=Math.min(a.width-1,Math.max(0,c.left)),f=Math.min(a.width,Math.max(1,c.left+c.width)),g=Math.min(a.height-1,Math.max(0,c.top)),h=Math.min(a.height,Math.max(1,c.top+c.height));return d.width=c.width,d.height=c.height,E("Cropping canvas at:","left:",c.left,"top:",c.top,"width:",f-e,"height:",h-g),E("Resulting crop with width",c.width,"and height",c.height," with x",e,"and y",g),d.getContext("2d").drawImage(a,e,g,f-e,h-g,c.x,c.y,f-e,h-g),d}function l(a){return Math.max(Math.max(a.body.scrollWidth,a.documentElement.scrollWidth),Math.max(a.body.offsetWidth,a.documentElement.offsetWidth),Math.max(a.body.clientWidth,a.documentElement.clientWidth))}function m(a){return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))}function n(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}function o(a,b,c,d,e){r(a);var f=a.documentElement.cloneNode(!0),g=b.createElement("iframe");return g.style.visibility="hidden",g.style.position="absolute",g.style.left=g.style.top="-10000px",g.width=c,g.height=d,g.scrolling="no",b.body.appendChild(g),new Promise(function(b){var c=g.contentWindow.document;g.contentWindow.onload=g.onload=function(){var f=setInterval(function(){c.body.childNodes.length>0&&(s(a,c),clearInterval(f),"view"===e.type&&g.contentWindow.scrollTo(d,h),b(g))},50)};var d=a.defaultView.pageXOffset,h=a.defaultView.pageYOffset;c.open(),c.write("<!DOCTYPE html><html></html>"),(d!==a.defaultView.pageXOffset||h!==a.defaultView.pageYOffset)&&a.defaultView.scrollTo(d,h),c.replaceChild(e.javascriptEnabled===!0?c.adoptNode(f):t(c.adoptNode(f)),c.documentElement),c.close()})}function p(b,c,d,e,f,g){return new xb(b,c,a.document).then(q(b)).then(function(a){return o(a,d,e,f,g)})}function q(a){return function(c){var d,e=new DOMParser;try{d=e.parseFromString(c,"text/html")}catch(f){E("DOMParser not supported, falling back to createHTMLDocument"),d=b.implementation.createHTMLDocument("");try{d.open(),d.write(c),d.close()}catch(g){E("createHTMLDocument write not supported, falling back to document.body.innerHTML"),d.body.innerHTML=c}}var h=d.querySelector("base");if(!h||!h.href.host){var i=d.createElement("base");i.href=a,d.head.insertBefore(i,d.head.firstChild)}return d}}function r(a){[].slice.call(a.querySelectorAll("canvas"),0).forEach(function(a){a.setAttribute(Rb,"canvas-"+Sb++)})}function s(a,b){[].slice.call(a.querySelectorAll("["+Rb+"]"),0).forEach(function(a){try{var c=b.querySelector("["+Rb+'="'+a.getAttribute(Rb)+'"]');c&&(c.width=a.width,c.height=a.height,c.getContext("2d").putImageData(a.getContext("2d").getImageData(0,0,a.width,a.height),0,0))}catch(d){E("Unable to copy canvas content from",a,d)}a.removeAttribute(Rb)})}function t(a){return[].slice.call(a.childNodes,0).filter(u).forEach(function(b){"SCRIPT"===b.tagName?a.removeChild(b):t(b)}),a}function u(a){return a.nodeType===Node.ELEMENT_NODE}function v(a){var c=b.createElement("a");return c.href=a,c.href=c.href,c}function w(a){if(this.src=a,E("DummyImageContainer for",a),!this.promise||!this.image){E("Initiating DummyImageContainer"),w.prototype.image=new Image;var b=this.image;w.prototype.promise=new Promise(function(a,c){b.onload=a,b.onerror=c,b.src=n(),b.complete===!0&&a(b)})}}function x(a,c){var d,e,f=b.createElement("div"),g=b.createElement("img"),h=b.createElement("span"),i="Hidden Text";f.style.visibility="hidden",f.style.fontFamily=a,f.style.fontSize=c,f.style.margin=0,f.style.padding=0,b.body.appendChild(f),g.src=n(),g.width=1,g.height=1,g.style.margin=0,g.style.padding=0,g.style.verticalAlign="baseline",h.style.fontFamily=a,h.style.fontSize=c,h.style.margin=0,h.style.padding=0,h.appendChild(b.createTextNode(i)),f.appendChild(h),f.appendChild(g),d=g.offsetTop-h.offsetTop+1,f.removeChild(h),f.appendChild(b.createTextNode(i)),f.style.lineHeight="normal",g.style.verticalAlign="super",e=g.offsetTop-f.offsetTop+1,b.body.removeChild(f),this.baseline=d,this.lineWidth=1,this.middle=e}function y(){this.data={}}function z(a,b,c){this.image=null,this.src=a;var d=this,e=M(a);this.promise=(b?new Promise(function(b){"about:blank"===a.contentWindow.document.URL||null==a.contentWindow.document.documentElement?a.contentWindow.onload=a.onload=function(){b(a)}:b(a)}):this.proxyLoad(c.proxy,e,c)).then(function(a){return html2canvas(a.contentWindow.document.documentElement,{type:"view",width:a.width,height:a.height,proxy:c.proxy,javascriptEnabled:c.javascriptEnabled,removeContainer:c.removeContainer,allowTaint:c.allowTaint,imageTimeout:c.imageTimeout/2})}).then(function(a){return d.image=a})}function A(a){this.src=a.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=Promise.resolve(!0)}function B(a,b){this.src=a,this.image=new Image;var c=this;this.tainted=null,this.promise=new Promise(function(d,e){c.image.onload=d,c.image.onerror=e,b&&(c.image.crossOrigin="anonymous"),c.image.src=a,c.image.complete===!0&&d(c.image)})}function C(b,c){this.link=null,this.options=b,this.support=c,this.origin=this.getOrigin(a.location.href)}function D(a){A.apply(this,arguments),this.type=this.TYPES.LINEAR;var b=null===a.args[0].match(this.stepRegExp);b?a.args[0].split(" ").reverse().forEach(function(a){switch(a){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 b=this.y0,c=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=c,this.y1=b}},this):(this.y0=0,this.y1=1),this.colorStops=a.args.slice(b?1:0).map(function(a){var b=a.match(this.stepRegExp);return{color:b[1],stop:"%"===b[3]?b[2]/100:null}},this),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(a,b){null===a.stop&&this.colorStops.slice(b).some(function(c,d){return null!==c.stop?(a.stop=(c.stop-this.colorStops[b-1].stop)/(d+1)+this.colorStops[b-1].stop,!0):!1},this)},this)}function E(){a.html2canvas.logging&&a.console&&a.console.log&&Function.prototype.bind.call(a.console.log,a.console).apply(a.console,[Date.now()-a.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function F(a,b){this.node=a,this.parent=b,this.stack=null,this.bounds=null,this.borders=null,this.clip=[],this.backgroundClip=[],this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null,this.isPseudoElement=!1,this.opacity=null}function G(a){var b=a.options[a.selectedIndex||0];return b?b.text||"":""}function H(a){return a&&"matrix"===a[1]?a[2].split(",").map(function(a){return parseFloat(a.trim())}):void 0}function I(a){return-1!==a.toString().indexOf("%")}function J(a){var b,c,d,e,f,g,h,i=" \r\n	",j=[],k=0,l=0,m=function(){b&&('"'===c.substr(0,1)&&(c=c.substr(1,c.length-2)),c&&h.push(c),"-"===b.substr(0,1)&&(e=b.indexOf("-",1)+1)>0&&(d=b.substr(0,e),b=b.substr(e)),j.push({prefix:d,method:b.toLowerCase(),value:f,args:h,image:null})),h=[],b=d=c=f=""};return h=[],b=d=c=f="",a.split("").forEach(function(a){if(!(0===k&&i.indexOf(a)>-1)){switch(a){case'"':g?g===a&&(g=null):g=a;break;case"(":if(g)break;if(0===k)return k=1,void(f+=a);l++;break;case")":if(g)break;if(1===k){if(0===l)return k=0,f+=a,void m();l--}break;case",":if(g)break;if(0===k)return void m();if(1===k&&0===l&&!b.match(/^url$/i))return h.push(c),c="",void(f+=a)}f+=a,0===k?b+=a:c+=a}}),m(),j}function K(a){return a.replace("px","")}function L(a){return parseFloat(a)}function M(a){if(a.getBoundingClientRect){var b=a.getBoundingClientRect(),c=null==a.offsetWidth?b.width:a.offsetWidth;return{top:b.top,bottom:b.bottom||b.top+b.height,right:b.left+c,left:b.left,width:c,height:null==a.offsetHeight?b.height:a.offsetHeight}}return{}}function N(a){var b=a.offsetParent?N(a.offsetParent):{top:0,left:0};return{top:a.offsetTop+b.top,bottom:a.offsetTop+a.offsetHeight+b.top,right:a.offsetLeft+b.left+a.offsetWidth,left:a.offsetLeft+b.left,width:a.offsetWidth,height:a.offsetHeight}}function O(a,b,c,d,e){E("Starting NodeParser"),this.renderer=b,this.options=e,this.range=null,this.support=c,this.renderQueue=[],this.stack=new Fb(!0,1,a.ownerDocument,null);var f=new F(a,null);if(a===a.ownerDocument.documentElement){var g=new F(this.renderer.isTransparent(f.css("backgroundColor"))?a.ownerDocument.body:a.ownerDocument.documentElement,null);b.rectangle(0,0,b.width,b.height,g.css("backgroundColor"))}f.visibile=f.isElementVisible(),this.createPseudoHideStyles(a.ownerDocument),this.disableAnimations(a.ownerDocument),this.nodes=sb([f].concat(this.getChildren(f)).filter(function(a){return a.visible=a.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new y,E("Fetched nodes, total:",this.nodes.length),E("Calculate overflow clips"),this.calculateOverflowClips(),E("Start fetching images"),this.images=d.fetch(this.nodes.filter(jb)),this.ready=this.images.ready.then(ob(function(){return E("Images loaded, starting parsing"),E("Creating stacking contexts"),this.createStackingContexts(),E("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.parse(this.stack),E("Render queue created with "+this.renderQueue.length+" items"),new Promise(ob(function(a){e.async?"function"==typeof e.async?e.async.call(this,this.renderQueue,a):this.renderQueue.length>0?(this.renderIndex=0,this.asyncRenderer(this.renderQueue,a)):a():(this.renderQueue.forEach(this.paint,this),a())},this))},this))}function P(a){return a.parent&&a.parent.clip.length}function Q(a){return a.replace(/(\-[a-z])/g,function(a){return a.toUpperCase().replace("-","")})}function R(){}function S(a,b,c,d){var e=4*((Math.sqrt(2)-1)/3),f=c*e,g=d*e,h=a+c,i=b+d;return{topLeft:U({x:a,y:i},{x:a,y:i-g},{x:h-f,y:b},{x:h,y:b}),topRight:U({x:a,y:b},{x:a+f,y:b},{x:h,y:i-g},{x:h,y:i}),bottomRight:U({x:h,y:b},{x:h,y:b+g},{x:a+f,y:i},{x:a,y:i}),bottomLeft:U({x:h,y:i},{x:h-f,y:i},{x:a,y:b+g},{x:a,y:b})}}function T(a,b,c){var d=a.left,e=a.top,f=a.width,g=a.height,h=b[0][0],i=b[0][1],j=b[1][0],k=b[1][1],l=b[2][0],m=b[2][1],n=b[3][0],o=b[3][1],p=f-j,q=g-m,r=f-l,s=g-o;return{topLeftOuter:S(d,e,h,i).topLeft.subdivide(.5),topLeftInner:S(d+c[3].width,e+c[0].width,Math.max(0,h-c[3].width),Math.max(0,i-c[0].width)).topLeft.subdivide(.5),topRightOuter:S(d+p,e,j,k).topRight.subdivide(.5),topRightInner:S(d+Math.min(p,f+c[3].width),e+c[0].width,p>f+c[3].width?0:j-c[3].width,k-c[0].width).topRight.subdivide(.5),bottomRightOuter:S(d+r,e+q,l,m).bottomRight.subdivide(.5),bottomRightInner:S(d+Math.min(r,f-c[3].width),e+Math.min(q,g+c[0].width),Math.max(0,l-c[1].width),m-c[2].width).bottomRight.subdivide(.5),bottomLeftOuter:S(d,e+s,n,o).bottomLeft.subdivide(.5),bottomLeftInner:S(d+c[3].width,e+s,Math.max(0,n-c[3].width),o-c[2].width).bottomLeft.subdivide(.5)}}function U(a,b,c,d){var e=function(a,b,c){return{x:a.x+(b.x-a.x)*c,y:a.y+(b.y-a.y)*c}};return{start:a,startControl:b,endControl:c,end:d,subdivide:function(f){var g=e(a,b,f),h=e(b,c,f),i=e(c,d,f),j=e(g,h,f),k=e(h,i,f),l=e(j,k,f);return[U(a,g,j,l),U(l,k,i,d)]},curveTo:function(a){a.push(["bezierCurve",b.x,b.y,c.x,c.y,d.x,d.y])},curveToReversed:function(d){d.push(["bezierCurve",c.x,c.y,b.x,b.y,a.x,a.y])}}}function V(a,b,c,d,e,f,g){var h=[];return b[0]>0||b[1]>0?(h.push(["line",d[1].start.x,d[1].start.y]),d[1].curveTo(h)):h.push(["line",a.c1[0],a.c1[1]]),c[0]>0||c[1]>0?(h.push(["line",f[0].start.x,f[0].start.y]),f[0].curveTo(h),h.push(["line",g[0].end.x,g[0].end.y]),g[0].curveToReversed(h)):(h.push(["line",a.c2[0],a.c2[1]]),h.push(["line",a.c3[0],a.c3[1]])),b[0]>0||b[1]>0?(h.push(["line",e[1].end.x,e[1].end.y]),e[1].curveToReversed(h)):h.push(["line",a.c4[0],a.c4[1]]),h}function W(a,b,c,d,e,f,g){b[0]>0||b[1]>0?(a.push(["line",d[0].start.x,d[0].start.y]),d[0].curveTo(a),d[1].curveTo(a)):a.push(["line",f,g]),(c[0]>0||c[1]>0)&&a.push(["line",e[0].start.x,e[0].start.y])}function X(a){return a.cssInt("zIndex")<0}function Y(a){return a.cssInt("zIndex")>0}function Z(a){return 0===a.cssInt("zIndex")}function $(a){return-1!==["inline","inline-block","inline-table"].indexOf(a.css("display"))}function _(a){return a instanceof Fb}function ab(a){return a.node.data.trim().length>0}function bb(a){return/^(normal|none|0px)$/.test(a.parent.css("letterSpacing"))}function cb(a){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(b){var c=a.css("border"+b+"Radius"),d=c.split(" ");return d.length<=1&&(d[1]=d[0]),d.map(pb)})}function db(a){return a.nodeType===Node.TEXT_NODE||a.nodeType===Node.ELEMENT_NODE}function eb(a){var b=a.css("position"),c=-1!==["absolute","relative","fixed"].indexOf(b)?a.css("zIndex"):"auto";return"auto"!==c}function fb(a){return"static"!==a.css("position")}function gb(a){return"none"!==a.css("float")}function hb(a){return-1!==["inline-block","inline-table"].indexOf(a.css("display"))}function ib(a){var b=this;return function(){return!a.apply(b,arguments)}}function jb(a){return a.node.nodeType===Node.ELEMENT_NODE}function kb(a){return a.isPseudoElement===!0}function lb(a){return a.node.nodeType===Node.TEXT_NODE}function mb(a){return function(b,c){return b.cssInt("zIndex")+a.indexOf(b)/a.length-(c.cssInt("zIndex")+a.indexOf(c)/a.length)}}function nb(a){return a.getOpacity()<1}function ob(a,b){return function(){return a.apply(b,arguments)}}function pb(a){return parseInt(a,10)}function qb(a){return a.width}function rb(a){return a.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(a.node.nodeName)}function sb(a){return[].concat.apply([],a)}function tb(a){var b=a.substr(0,1);return b===a.substr(a.length-1)&&b.match(/'|"/)?a.substr(1,a.length-2):a}function ub(b){for(var c,d=[],e=0,f=!1;b.length;)vb(b[e])===f?(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)),f=!f,e=0):e++,e>=b.length&&(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)));return d}function vb(a){return-1!==[32,13,10,9,45].indexOf(a)}function wb(a){return/[^\u0000-\u00ff]/.test(a)}function xb(a,b,c){var d=Ab(Ub),e=Bb(b,a,d);return Ub?Nb(e):zb(c,e,d).then(function(a){return Ib(a.content)})}function yb(a,b,c){var d=Ab(Vb),e=Bb(b,a,d);return Vb?Promise.resolve(e):zb(c,e,d).then(function(a){return"data:"+a.type+";base64,"+a.content})}function zb(b,c,d){return new Promise(function(e,f){var g=b.createElement("script"),h=function(){delete a.html2canvas.proxy[d],b.body.removeChild(g)};a.html2canvas.proxy[d]=function(a){h(),e(a)},g.src=c,g.onerror=function(a){h(),f(a)},b.body.appendChild(g)})}function Ab(a){return a?"":"html2canvas_"+Date.now()+"_"+ ++Tb+"_"+Math.round(1e5*Math.random())}function Bb(a,b,c){return a+"?url="+encodeURIComponent(b)+(c.length?"&callback=html2canvas.proxy."+c:"")}function Cb(a,c){var d=(b.createElement("script"),b.createElement("a"));d.href=a,a=d.href,this.src=a,this.image=new Image;var e=this;this.promise=new Promise(function(d,f){e.image.crossOrigin="Anonymous",e.image.onload=d,e.image.onerror=f,new yb(a,c,b).then(function(a){e.image.src=a})["catch"](f)})}function Db(a,b,c){F.call(this,a,b),this.isPseudoElement=!0,this.before=":before"===c}function Eb(a,b,c,d,e){this.width=a,this.height=b,this.images=c,this.options=d,this.document=e}function Fb(a,b,c,d){F.call(this,c,d),this.ownStacking=a,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*b}function Gb(a){this.rangeBounds=this.testRangeBounds(a),this.cors=this.testCORS(),this.svg=this.testSVG()}function Hb(a){this.src=a,this.image=null;var b=this;this.promise=this.hasFabric().then(function(){return b.isInline(a)?Promise.resolve(b.inlineFormatting(a)):Nb(a)}).then(function(a){return new Promise(function(c){html2canvas.fabric.loadSVGFromString(a,b.createCanvas.call(b,c))})})}function Ib(a){var b,c,d,e,f,g,h,i,j="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",k=a.length,l="";for(b=0;k>b;b+=4)c=j.indexOf(a[b]),d=j.indexOf(a[b+1]),e=j.indexOf(a[b+2]),f=j.indexOf(a[b+3]),g=c<<2|d>>4,h=(15&d)<<4|e>>2,i=(3&e)<<6|f,l+=64===e?String.fromCharCode(g):64===f||-1===f?String.fromCharCode(g,h):String.fromCharCode(g,h,i);return l}function Jb(a,b){this.src=a,this.image=null;var c=this;this.promise=b?new Promise(function(b,d){c.image=new Image,c.image.onload=b,c.image.onerror=d,c.image.src="data:image/svg+xml,"+(new XMLSerializer).serializeToString(a),c.image.complete===!0&&b(c.image)}):this.hasFabric().then(function(){return new Promise(function(b){html2canvas.fabric.parseSVGDocument(a,c.createCanvas.call(c,b))})})}function Kb(a,b){F.call(this,a,b)}function Lb(a,b,c){return a.length>0?b+c.toUpperCase():void 0}function Mb(a){A.apply(this,arguments),this.type="linear"===a.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}function Nb(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onload=function(){200===d.status?b(d.responseText):c(new Error(d.statusText))},d.onerror=function(){c(new Error("Network Error"))},d.send()})}function Ob(a,b){Eb.apply(this,arguments),this.canvas=this.options.canvas||this.document.createElement("canvas"),this.options.canvas||(this.canvas.width=a,this.canvas.height=b),this.ctx=this.canvas.getContext("2d"),this.options.background&&this.rectangle(0,0,a,b,this.options.background),this.taintCtx=this.document.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},E("Initialized CanvasRenderer with size",a,"x",b)}function Pb(a){return a.length>0}if(!function(){var c,d,f,g;!function(){var a={},b={};c=function(b,c,d){a[b]={deps:c,callback:d}},g=f=d=function(c){function e(a){if("."!==a.charAt(0))return a;for(var b=a.split("/"),d=c.split("/").slice(0,-1),e=0,f=b.length;f>e;e++){var g=b[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(g._eak_seen=a,b[c])return b[c];if(b[c]={},!a[c])throw new Error("Could not find module "+c);for(var f,h=a[c],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)k.push("exports"===i[l]?f={}:d(e(i[l])));var n=j.apply(this,k);return b[c]=f||n}}(),c("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j<a.length;j++)g=a[j],g&&e(g.then)?g.then(d(j),c):f(j,g)})}var d=a.isArray,e=a.isFunction;b.all=c}),c("promise/asap",["exports"],function(c){"use strict";function d(){return function(){process.nextTick(h)}}function f(){var a=0,c=new l(h),d=b.createTextNode("");return c.observe(d,{characterData:!0}),function(){d.data=a=++a%2}}function g(){return function(){m.setTimeout(h,1)}}function h(){for(var a=0;a<n.length;a++){var b=n[a],c=b[0],d=b[1];c(d)}n=[]}function i(a,b){var c=n.push([a,b]);1===c&&j()}var j,k="undefined"!=typeof a?a:{},l=k.MutationObserver||k.WebKitMutationObserver,m="undefined"!=typeof e?e:this,n=[];j="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?d():l?f():g(),c.asap=i}),c("promise/cast",["exports"],function(a){"use strict";function b(a){if(a&&"object"==typeof a&&a.constructor===this)return a;var b=this;return new b(function(b){b(a)})}a.cast=b}),c("promise/config",["exports"],function(a){"use strict";function b(a,b){return 2!==arguments.length?c[a]:void(c[a]=b)}var c={instrument:!1};a.config=c,a.configure=b}),c("promise/polyfill",["./promise","./utils","exports"],function(b,c,d){"use strict";function e(){var b="Promise"in a&&"cast"in a.Promise&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var b;return new a.Promise(function(a){b=a}),g(b)}();b||(a.Promise=f)}var f=b.Promise,g=c.isFunction;d.polyfill=e}),c("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(a,b,c,d,e,f,g,h,i){"use strict";function j(a){if(!w(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof j))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],k(a,this)}function k(a,b){function c(a){p(b,a)}function d(a){r(b,a)}try{a(c,d)}catch(e){d(e)}}function l(a,b,c,d){var e,f,g,h,i=w(c);if(i)try{e=c(d),g=!0}catch(j){h=!0,f=j}else e=d,g=!0;o(b,e)||(i&&g?p(b,e):h?r(b,f):a===F?p(b,e):a===G&&r(b,e))}function m(a,b,c,d){var e=a._subscribers,f=e.length;e[f]=b,e[f+F]=c,e[f+G]=d}function n(a,b){for(var c,d,e=a._subscribers,f=a._detail,g=0;g<e.length;g+=3)c=e[g],d=e[g+b],l(b,c,d,f);a._subscribers=null}function o(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(v(b)&&(d=b.then,w(d)))return d.call(b,function(d){return c?!0:(c=!0,void(b!==d?p(a,d):q(a,d)))},function(b){return c?!0:(c=!0,void r(a,b))}),!0}catch(e){return c?!0:(r(a,e),!0)}return!1}function p(a,b){a===b?q(a,b):o(a,b)||q(a,b)}function q(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(s,a))}function r(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(t,a))}function s(a){n(a,a._state=F)}function t(a){n(a,a._state=G)}var u=a.config,v=(a.configure,b.objectOrFunction),w=b.isFunction,x=(b.now,c.cast),y=d.all,z=e.race,A=f.resolve,B=g.reject,C=h.asap;u.async=C;var D=void 0,E=0,F=1,G=2;j.prototype={constructor:j,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(a,b){var c=this,d=new this.constructor(function(){});if(this._state){var e=arguments;u.async(function(){l(c._state,d,e[c._state-1],c._detail)})}else m(this,d,a,b);return d},"catch":function(a){return this.then(null,a)}},j.all=y,j.cast=x,j.race=z,j.resolve=A,j.reject=B,i.Promise=j}),c("promise/race",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to race.");return new b(function(b,c){for(var d,e=0;e<a.length;e++)d=a[e],d&&"function"==typeof d.then?d.then(b,c):b(d)})}var d=a.isArray;b.race=c}),c("promise/reject",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b,c){c(a)})}a.reject=b}),c("promise/resolve",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b){b(a)})}a.resolve=b}),c("promise/utils",["exports"],function(a){"use strict";function b(a){return c(a)||"object"==typeof a&&null!==a}function c(a){return"function"==typeof a}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}var e=Date.now||function(){return(new Date).getTime()};a.objectOrFunction=b,a.isFunction=c,a.isArray=d,a.now=e}),d("promise/polyfill").polyfill()}(),"function"!=typeof Object.create||"function"!=typeof b.createElement("canvas").getContext)return void(a.html2canvas=function(){return Promise.reject("No canvas support")});!function(a){function b(a){throw RangeError(I[a])}function g(a,b){for(var c=a.length,d=[];c--;)d[c]=b(a[c]);return d}function h(a,b){var c=a.split("@"),d="";c.length>1&&(d=c[0]+"@",a=c[1]);var e=a.split(H),f=g(e,b).join(".");return d+f}function i(a){for(var b,c,d=[],e=0,f=a.length;f>e;)b=a.charCodeAt(e++),b>=55296&&56319>=b&&f>e?(c=a.charCodeAt(e++),56320==(64512&c)?d.push(((1023&b)<<10)+(1023&c)+65536):(d.push(b),e--)):d.push(b);return d}function j(a){return g(a,function(a){var b="";return a>65535&&(a-=65536,b+=L(a>>>10&1023|55296),a=56320|1023&a),b+=L(a)}).join("")}function k(a){return 10>a-48?a-22:26>a-65?a-65:26>a-97?a-97:x}function l(a,b){return a+22+75*(26>a)-((0!=b)<<5)}function m(a,b,c){var d=0;for(a=c?K(a/B):a>>1,a+=K(a/b);a>J*z>>1;d+=x)a=K(a/J);return K(d+(J+1)*a/(a+A))}function n(a){var c,d,e,f,g,h,i,l,n,o,p=[],q=a.length,r=0,s=D,t=C;for(d=a.lastIndexOf(E),0>d&&(d=0),e=0;d>e;++e)a.charCodeAt(e)>=128&&b("not-basic"),p.push(a.charCodeAt(e));for(f=d>0?d+1:0;q>f;){for(g=r,h=1,i=x;f>=q&&b("invalid-input"),l=k(a.charCodeAt(f++)),(l>=x||l>K((w-r)/h))&&b("overflow"),r+=l*h,n=t>=i?y:i>=t+z?z:i-t,!(n>l);i+=x)o=x-n,h>K(w/o)&&b("overflow"),h*=o;c=p.length+1,t=m(r-g,c,0==g),K(r/c)>w-s&&b("overflow"),s+=K(r/c),r%=c,p.splice(r++,0,s)}return j(p)}function o(a){var c,d,e,f,g,h,j,k,n,o,p,q,r,s,t,u=[];for(a=i(a),q=a.length,c=D,d=0,g=C,h=0;q>h;++h)p=a[h],128>p&&u.push(L(p));for(e=f=u.length,f&&u.push(E);q>e;){for(j=w,h=0;q>h;++h)p=a[h],p>=c&&j>p&&(j=p);for(r=e+1,j-c>K((w-d)/r)&&b("overflow"),d+=(j-c)*r,c=j,h=0;q>h;++h)if(p=a[h],c>p&&++d>w&&b("overflow"),p==c){for(k=d,n=x;o=g>=n?y:n>=g+z?z:n-g,!(o>k);n+=x)t=k-o,s=x-o,u.push(L(l(o+t%s,0))),k=K(t/s);u.push(L(l(k,0))),g=m(d,r,e==f),d=0,++e}++d,++c}return u.join("")}function p(a){return h(a,function(a){return F.test(a)?n(a.slice(4).toLowerCase()):a})}function q(a){return h(a,function(a){return G.test(a)?"xn--"+o(a):a})}var r="object"==typeof d&&d&&!d.nodeType&&d,s="object"==typeof c&&c&&!c.nodeType&&c,t="object"==typeof e&&e;(t.global===t||t.window===t||t.self===t)&&(a=t);var u,v,w=2147483647,x=36,y=1,z=26,A=38,B=700,C=72,D=128,E="-",F=/^xn--/,G=/[^\x20-\x7E]/,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=x-y,K=Math.floor,L=String.fromCharCode;if(u={version:"1.3.1",ucs2:{decode:i,encode:j},decode:n,encode:o,toASCII:q,toUnicode:p},"function"==typeof f&&"object"==typeof f.amd&&f.amd)f("punycode",function(){return u});else if(r&&s)if(c.exports==r)s.exports=u;else for(v in u)u.hasOwnProperty(v)&&(r[v]=u[v]);else a.punycode=u}(this);var Qb="data-html2canvas-node",Rb="data-html2canvas-canvas-clone",Sb=0;a.html2canvas=function(c,d){if(d=d||{},d.logging&&(a.html2canvas.logging=!0,a.html2canvas.start=Date.now()),d.async="undefined"==typeof d.async?!0:d.async,d.allowTaint="undefined"==typeof d.allowTaint?!1:d.allowTaint,d.removeContainer="undefined"==typeof d.removeContainer?!0:d.removeContainer,d.javascriptEnabled="undefined"==typeof d.javascriptEnabled?!1:d.javascriptEnabled,d.imageTimeout="undefined"==typeof d.imageTimeout?1e4:d.imageTimeout,"string"==typeof c)return"string"!=typeof d.proxy?Promise.reject("Proxy must be used when rendering url"):p(v(c),d.proxy,b,a.innerWidth,a.innerHeight,d).then(function(b){return i(b.contentWindow.document.documentElement,b,d,a.innerWidth,a.innerHeight)});var e=(c===g?[b.documentElement]:c.length?c:[c])[0];return e.setAttribute(Qb,"true"),h(e.ownerDocument,d,e.ownerDocument.defaultView.innerWidth,e.ownerDocument.defaultView.innerHeight).then(function(a){return"function"==typeof d.onrendered&&(E("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"),d.onrendered(a)),a})},a.html2canvas.punycode=this.punycode,a.html2canvas.proxy={},y.prototype.getMetrics=function(a,b){return this.data[a+"-"+b]===g&&(this.data[a+"-"+b]=new x(a,b)),this.data[a+"-"+b]},z.prototype.proxyLoad=function(a,b,c){var d=this.src;return p(d.src,a,d.ownerDocument,b.width,b.height,c)},A.prototype.TYPES={LINEAR:1,RADIAL:2},C.prototype.findImages=function(a){var b=[];return a.reduce(function(a,b){switch(b.node.nodeName){case"IMG":return a.concat([{args:[b.node.src],method:"url"}]);case"svg":case"IFRAME":return a.concat([{args:[b.node],method:b.node.nodeName}])}return a},[]).forEach(this.addImage(b,this.loadImage),this),b},C.prototype.findBackgroundImage=function(a,b){return b.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(a,this.loadImage),this),a},C.prototype.addImage=function(a,b){return function(c){c.args.forEach(function(d){this.imageExists(a,d)||(a.splice(0,0,b.call(this,c)),E("Added image #"+a.length,"string"==typeof d?d.substring(0,100):d))},this)}},C.prototype.hasImageBackground=function(a){return"none"!==a.method},C.prototype.loadImage=function(a){if("url"===a.method){var b=a.args[0];return!this.isSVG(b)||this.support.svg||this.options.allowTaint?b.match(/data:image\/.*;base64,/i)?new B(b.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(b)||this.options.allowTaint===!0||this.isSVG(b)?new B(b,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new B(b,!0):this.options.proxy?new Cb(b,this.options.proxy):new w(b):new Hb(b)}return"linear-gradient"===a.method?new D(a):"gradient"===a.method?new Mb(a):"svg"===a.method?new Jb(a.args[0],this.support.svg):"IFRAME"===a.method?new z(a.args[0],this.isSameOrigin(a.args[0].src),this.options):new w(a)},C.prototype.isSVG=function(a){return"svg"===a.substring(a.length-3).toLowerCase()||Hb.prototype.isInline(a)},C.prototype.imageExists=function(a,b){return a.some(function(a){return a.src===b})},C.prototype.isSameOrigin=function(a){return this.getOrigin(a)===this.origin},C.prototype.getOrigin=function(a){var c=this.link||(this.link=b.createElement("a"));return c.href=a,c.href=c.href,c.protocol+c.hostname+c.port},C.prototype.getPromise=function(a){return this.timeout(a,this.options.imageTimeout)["catch"](function(){var b=new w(a.src);return b.promise.then(function(b){a.image=b})})},C.prototype.get=function(a){var b=null;return this.images.some(function(c){return(b=c).src===a})?b:null},C.prototype.fetch=function(a){return this.images=a.reduce(ob(this.findBackgroundImage,this),this.findImages(a)),this.images.forEach(function(a,b){a.promise.then(function(){E("Succesfully loaded image #"+(b+1),a)},function(c){E("Failed loading image #"+(b+1),a,c)})}),this.ready=Promise.all(this.images.map(this.getPromise,this)),E("Finished searching images"),this},C.prototype.timeout=function(a,b){var c;return Promise.race([a.promise,new Promise(function(d,e){c=setTimeout(function(){E("Timed out loading image",a),e(a)},b)})]).then(function(a){return clearTimeout(c),a})},D.prototype=Object.create(A.prototype),D.prototype.stepRegExp=/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/,F.prototype.cloneTo=function(a){a.visible=this.visible,a.borders=this.borders,a.bounds=this.bounds,a.clip=this.clip,a.backgroundClip=this.backgroundClip,a.computedStyles=this.computedStyles,a.styles=this.styles,a.backgroundImages=this.backgroundImages,a.opacity=this.opacity},F.prototype.getOpacity=function(){return null===this.opacity?this.opacity=this.cssFloat("opacity"):this.opacity},F.prototype.assignStack=function(a){this.stack=a,a.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(a){return this.computedStyles||(this.computedStyles=this.isPseudoElement?this.parent.computedStyle(this.before?":before":":after"):this.computedStyle(null)),this.styles[a]||(this.styles[a]=this.computedStyles[a])},F.prototype.prefixedCss=function(a){var b=["webkit","moz","ms","o"],c=this.css(a);
-return c===g&&b.some(function(b){return c=this.css(b+a.substr(0,1).toUpperCase()+a.substr(1)),c!==g},this),c===g?null:c},F.prototype.computedStyle=function(a){return this.node.ownerDocument.defaultView.getComputedStyle(this.node,a)},F.prototype.cssInt=function(a){var b=parseInt(this.css(a),10);return isNaN(b)?0:b},F.prototype.cssFloat=function(a){var b=parseFloat(this.css(a));return isNaN(b)?0:b},F.prototype.fontWeight=function(){var a=this.css("fontWeight");switch(parseInt(a,10)){case 401:a="bold";break;case 400:a="normal"}return a},F.prototype.parseClip=function(){var a=this.css("clip").match(this.CLIP);return a?{top:parseInt(a[1],10),right:parseInt(a[2],10),bottom:parseInt(a[3],10),left:parseInt(a[4],10)}:null},F.prototype.parseBackgroundImages=function(){return this.backgroundImages||(this.backgroundImages=J(this.css("backgroundImage")))},F.prototype.cssList=function(a,b){var c=(this.css(a)||"").split(",");return c=c[b||0]||c[0]||"auto",c=c.trim().split(" "),1===c.length&&(c=[c[0],c[0]]),c},F.prototype.parseBackgroundSize=function(a,b,c){var d,e,f=this.cssList("backgroundSize",c);if(I(f[0]))d=a.width*parseFloat(f[0])/100;else{if(/contain|cover/.test(f[0])){var g=a.width/a.height,h=b.width/b.height;return h>g^"contain"===f[0]?{width:a.height*h,height:a.height}:{width:a.width,height:a.width/h}}d=parseInt(f[0],10)}return e="auto"===f[0]&&"auto"===f[1]?b.height:"auto"===f[1]?d/b.width*b.height:I(f[1])?a.height*parseFloat(f[1])/100:parseInt(f[1],10),"auto"===f[0]&&(d=e/b.height*b.width),{width:d,height:e}},F.prototype.parseBackgroundPosition=function(a,b,c,d){var e,f,g=this.cssList("backgroundPosition",c);return e=I(g[0])?(a.width-(d||b).width)*(parseFloat(g[0])/100):parseInt(g[0],10),f="auto"===g[1]?e/b.width*b.height:I(g[1])?(a.height-(d||b).height)*parseFloat(g[1])/100:parseInt(g[1],10),"auto"===g[0]&&(e=f/b.height*b.width),{left:e,top:f}},F.prototype.parseBackgroundRepeat=function(a){return this.cssList("backgroundRepeat",a)[0]},F.prototype.parseTextShadows=function(){var a=this.css("textShadow"),b=[];if(a&&"none"!==a)for(var c=a.match(this.TEXT_SHADOW_PROPERTY),d=0;c&&d<c.length;d++){var e=c[d].match(this.TEXT_SHADOW_VALUES);b.push({color:e[0],offsetX:e[1]?parseFloat(e[1].replace("px","")):0,offsetY:e[2]?parseFloat(e[2].replace("px","")):0,blur:e[3]?e[3].replace("px",""):0})}return b},F.prototype.parseTransform=function(){if(!this.transformData)if(this.hasTransform()){var a=this.parseBounds(),b=this.prefixedCss("transformOrigin").split(" ").map(K).map(L);b[0]+=a.left,b[1]+=a.top,this.transformData={origin:b,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 a=this.prefixedCss("transform"),b=a?H(a.match(this.MATRIX_PROPERTY)):null;this.transformMatrix=b?b:[1,0,0,1,0,0]}return this.transformMatrix},F.prototype.parseBounds=function(){return this.bounds||(this.bounds=this.hasTransform()?N(this.node):M(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 a=this.node.value||"";return a="SELECT"===this.node.tagName?G(this.node):a,0===a.length?this.node.placeholder||"":a},F.prototype.MATRIX_PROPERTY=/(matrix)\((.+)\)/,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\)$/,O.prototype.calculateOverflowClips=function(){this.nodes.forEach(function(a){if(jb(a)){kb(a)&&a.appendToDOM(),a.borders=this.parseBorders(a);var b="hidden"===a.css("overflow")?[a.borders.clip]:[],c=a.parseClip();c&&-1!==["absolute","fixed"].indexOf(a.css("position"))&&b.push([["rect",a.bounds.left+c.left,a.bounds.top+c.top,c.right-c.left,c.bottom-c.top]]),a.clip=P(a)?a.parent.clip.concat(b):b,a.backgroundClip="hidden"!==a.css("overflow")?a.clip.concat([a.borders.clip]):a.clip,kb(a)&&a.cleanDOM()}else lb(a)&&(a.clip=P(a)?a.parent.clip:[]);kb(a)||(a.bounds=null)},this)},O.prototype.asyncRenderer=function(a,b,c){c=c||Date.now(),this.paint(a[this.renderIndex++]),a.length===this.renderIndex?b():c+20>Date.now()?this.asyncRenderer(a,b,c):setTimeout(ob(function(){this.asyncRenderer(a,b)},this),0)},O.prototype.createPseudoHideStyles=function(a){this.createStyles(a,"."+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+':before { content: "" !important; display: none !important; }.'+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER+':after { content: "" !important; display: none !important; }')},O.prototype.disableAnimations=function(a){this.createStyles(a,"* { -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;}")},O.prototype.createStyles=function(a,b){var c=a.createElement("style");c.innerHTML=b,a.body.appendChild(c)},O.prototype.getPseudoElements=function(a){var b=[[a]];if(a.node.nodeType===Node.ELEMENT_NODE){var c=this.getPseudoElement(a,":before"),d=this.getPseudoElement(a,":after");c&&b.push(c),d&&b.push(d)}return sb(b)},O.prototype.getPseudoElement=function(a,c){var d=a.computedStyle(c);if(!d||!d.content||"none"===d.content||"-moz-alt-content"===d.content||"none"===d.display)return null;for(var e=tb(d.content),f="url"===e.substr(0,3),g=b.createElement(f?"img":"html2canvaspseudoelement"),h=new Db(g,a,c),i=d.length-1;i>=0;i--){var j=Q(d.item(i));g.style[j]=d[j]}if(g.className=Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+" "+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER,f)return g.src=J(e)[0].args[0],[h];var k=b.createTextNode(e);return g.appendChild(k),[h,new Kb(k,h)]},O.prototype.getChildren=function(a){return sb([].filter.call(a.node.childNodes,db).map(function(b){var c=[b.nodeType===Node.TEXT_NODE?new Kb(b,a):new F(b,a)].filter(rb);return b.nodeType===Node.ELEMENT_NODE&&c.length&&"TEXTAREA"!==b.tagName?c[0].isElementVisible()?c.concat(this.getChildren(c[0])):[]:c},this))},O.prototype.newStackingContext=function(a,b){var c=new Fb(b,a.getOpacity(),a.node,a.parent);a.cloneTo(c);var d=b?c.getParentStack(this):c.parent.stack;d.contexts.push(c),a.stack=c},O.prototype.createStackingContexts=function(){this.nodes.forEach(function(a){jb(a)&&(this.isRootElement(a)||nb(a)||eb(a)||this.isBodyWithTransparentRoot(a)||a.hasTransform())?this.newStackingContext(a,!0):jb(a)&&(fb(a)&&Z(a)||hb(a)||gb(a))?this.newStackingContext(a,!1):a.assignStack(a.parent.stack)},this)},O.prototype.isBodyWithTransparentRoot=function(a){return"BODY"===a.node.nodeName&&this.renderer.isTransparent(a.parent.css("backgroundColor"))},O.prototype.isRootElement=function(a){return null===a.parent},O.prototype.sortStackingContexts=function(a){a.contexts.sort(mb(a.contexts.slice(0))),a.contexts.forEach(this.sortStackingContexts,this)},O.prototype.parseTextBounds=function(a){return function(b,c,d){if("none"!==a.parent.css("textDecoration").substr(0,4)||0!==b.trim().length){if(this.support.rangeBounds&&!a.parent.hasTransform()){var e=d.slice(0,c).join("").length;return this.getRangeBounds(a.node,e,b.length)}if(a.node&&"string"==typeof a.node.data){var f=a.node.splitText(b.length),g=this.getWrapperBounds(a.node,a.parent.hasTransform());return a.node=f,g}}else(!this.support.rangeBounds||a.parent.hasTransform())&&(a.node=a.node.splitText(b.length));return{}}},O.prototype.getWrapperBounds=function(a,b){var c=a.ownerDocument.createElement("html2canvaswrapper"),d=a.parentNode,e=a.cloneNode(!0);c.appendChild(a.cloneNode(!0)),d.replaceChild(c,a);var f=b?N(c):M(c);return d.replaceChild(e,c),f},O.prototype.getRangeBounds=function(a,b,c){var d=this.range||(this.range=a.ownerDocument.createRange());return d.setStart(a,b),d.setEnd(a,b+c),d.getBoundingClientRect()},O.prototype.parse=function(a){var b=a.contexts.filter(X),c=a.children.filter(jb),d=c.filter(ib(gb)),e=d.filter(ib(fb)).filter(ib($)),f=c.filter(ib(fb)).filter(gb),g=d.filter(ib(fb)).filter($),h=a.contexts.concat(d.filter(fb)).filter(Z),i=a.children.filter(lb).filter(ab),j=a.contexts.filter(Y);b.concat(e).concat(f).concat(g).concat(h).concat(i).concat(j).forEach(function(a){this.renderQueue.push(a),_(a)&&(this.parse(a),this.renderQueue.push(new R))},this)},O.prototype.paint=function(a){try{a instanceof R?this.renderer.ctx.restore():lb(a)?(kb(a.parent)&&a.parent.appendToDOM(),this.paintText(a),kb(a.parent)&&a.parent.cleanDOM()):this.paintNode(a)}catch(b){E(b)}},O.prototype.paintNode=function(a){_(a)&&(this.renderer.setOpacity(a.opacity),this.renderer.ctx.save(),a.hasTransform()&&this.renderer.setTransform(a.parseTransform()));var b=a.parseBounds();this.renderer.clip(a.backgroundClip,function(){this.renderer.renderBackground(a,b,a.borders.borders.map(qb))},this),this.renderer.clip(a.clip,function(){this.renderer.renderBorders(a.borders.borders)},this),this.renderer.clip(a.backgroundClip,function(){switch(a.node.nodeName){case"svg":case"IFRAME":var c=this.images.get(a.node);c?this.renderer.renderImage(a,b,a.borders,c):E("Error loading <"+a.node.nodeName+">",a.node);break;case"IMG":var d=this.images.get(a.node.src);d?this.renderer.renderImage(a,b,a.borders,d):E("Error loading <img>",a.node.src);break;case"CANVAS":this.renderer.renderImage(a,b,a.borders,{image:a.node});break;case"SELECT":case"INPUT":case"TEXTAREA":this.paintFormValue(a)}},this)},O.prototype.paintFormValue=function(a){if(a.getValue().length>0){var b=a.node.ownerDocument,c=b.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(b){try{c.style[b]=a.css(b)}catch(d){E("html2canvas: Parse: Exception caught in renderFormValue: "+d.message)}});var e=a.parseBounds();c.style.position="fixed",c.style.left=e.left+"px",c.style.top=e.top+"px",c.textContent=a.getValue(),b.body.appendChild(c),this.paintText(new Kb(c.firstChild,a)),b.body.removeChild(c)}},O.prototype.paintText=function(b){b.applyTextTransform();var c=a.html2canvas.punycode.ucs2.decode(b.node.data),d=this.options.letterRendering&&!bb(b)||wb(b.node.data)?c.map(function(b){return a.html2canvas.punycode.ucs2.encode([b])}):ub(c),e=b.parent.fontWeight(),f=b.parent.css("fontSize"),g=b.parent.css("fontFamily"),h=b.parent.parseTextShadows();this.renderer.font(b.parent.css("color"),b.parent.css("fontStyle"),b.parent.css("fontVariant"),e,f,g),h.length?this.renderer.fontShadow(h[0].color,h[0].offsetX,h[0].offsetY,h[0].blur):this.renderer.clearShadow(),this.renderer.clip(b.parent.clip,function(){d.map(this.parseTextBounds(b),this).forEach(function(a,c){a&&(this.renderer.text(d[c],a.left,a.bottom),this.renderTextDecoration(b.parent,a,this.fontMetrics.getMetrics(g,f)))},this)},this)},O.prototype.renderTextDecoration=function(a,b,c){switch(a.css("textDecoration").split(" ")[0]){case"underline":this.renderer.rectangle(b.left,Math.round(b.top+c.baseline+c.lineWidth),b.width,1,a.css("color"));break;case"overline":this.renderer.rectangle(b.left,Math.round(b.top),b.width,1,a.css("color"));break;case"line-through":this.renderer.rectangle(b.left,Math.ceil(b.top+c.middle+c.lineWidth),b.width,1,a.css("color"))}},O.prototype.parseBorders=function(a){var b=a.parseBounds(),c=cb(a),d=["Top","Right","Bottom","Left"].map(function(b){return{width:a.cssInt("border"+b+"Width"),color:a.css("border"+b+"Color"),args:null}}),e=T(b,c,d);return{clip:this.parseBackgroundClip(a,e,d,c,b),borders:d.map(function(a,f){if(a.width>0){var g=b.left,h=b.top,i=b.width,j=b.height-d[2].width;switch(f){case 0:j=d[0].width,a.args=V({c1:[g,h],c2:[g+i,h],c3:[g+i-d[1].width,h+j],c4:[g+d[3].width,h+j]},c[0],c[1],e.topLeftOuter,e.topLeftInner,e.topRightOuter,e.topRightInner);break;case 1:g=b.left+b.width-d[1].width,i=d[1].width,a.args=V({c1:[g+i,h],c2:[g+i,h+j+d[2].width],c3:[g,h+j],c4:[g,h+d[0].width]},c[1],c[2],e.topRightOuter,e.topRightInner,e.bottomRightOuter,e.bottomRightInner);break;case 2:h=h+b.height-d[2].width,j=d[2].width,a.args=V({c1:[g+i,h+j],c2:[g,h+j],c3:[g+d[3].width,h],c4:[g+i-d[3].width,h]},c[2],c[3],e.bottomRightOuter,e.bottomRightInner,e.bottomLeftOuter,e.bottomLeftInner);break;case 3:i=d[3].width,a.args=V({c1:[g,h+j+d[2].width],c2:[g,h],c3:[g+i,h+d[0].width],c4:[g+i,h+j]},c[3],c[0],e.bottomLeftOuter,e.bottomLeftInner,e.topLeftOuter,e.topLeftInner)}}return a})}},O.prototype.parseBackgroundClip=function(a,b,c,d,e){var f=a.css("backgroundClip"),g=[];switch(f){case"content-box":case"padding-box":W(g,d[0],d[1],b.topLeftInner,b.topRightInner,e.left+c[3].width,e.top+c[0].width),W(g,d[1],d[2],b.topRightInner,b.bottomRightInner,e.left+e.width-c[1].width,e.top+c[0].width),W(g,d[2],d[3],b.bottomRightInner,b.bottomLeftInner,e.left+e.width-c[1].width,e.top+e.height-c[2].width),W(g,d[3],d[0],b.bottomLeftInner,b.topLeftInner,e.left+c[3].width,e.top+e.height-c[2].width);break;default:W(g,d[0],d[1],b.topLeftOuter,b.topRightOuter,e.left,e.top),W(g,d[1],d[2],b.topRightOuter,b.bottomRightOuter,e.left+e.width,e.top),W(g,d[2],d[3],b.bottomRightOuter,b.bottomLeftOuter,e.left+e.width,e.top+e.height),W(g,d[3],d[0],b.bottomLeftOuter,b.topLeftOuter,e.left,e.top+e.height)}return g};var Tb=0,Ub="withCredentials"in new XMLHttpRequest,Vb="crossOrigin"in new Image;Db.prototype.cloneTo=function(a){Db.prototype.cloneTo.call(this,a),a.isPseudoElement=!0,a.before=this.before},Db.prototype=Object.create(F.prototype),Db.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()},Db.prototype.cleanDOM=function(){this.node.parentNode.removeChild(this.node),this.parent.node.className=this.parent.node.className.replace(this.getHideClass(),"")},Db.prototype.getHideClass=function(){return this["PSEUDO_HIDE_ELEMENT_CLASS_"+(this.before?"BEFORE":"AFTER")]},Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE="___html2canvas___pseudoelement_before",Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER="___html2canvas___pseudoelement_after",Eb.prototype.renderImage=function(a,b,c,d){var e=a.cssInt("paddingLeft"),f=a.cssInt("paddingTop"),g=a.cssInt("paddingRight"),h=a.cssInt("paddingBottom"),i=c.borders,j=b.width-(i[1].width+i[3].width+e+g),k=b.height-(i[0].width+i[2].width+f+h);this.drawImage(d,0,0,d.image.width||j,d.image.height||k,b.left+e+i[3].width,b.top+f+i[0].width,j,k)},Eb.prototype.renderBackground=function(a,b,c){b.height>0&&b.width>0&&(this.renderBackgroundColor(a,b),this.renderBackgroundImage(a,b,c))},Eb.prototype.renderBackgroundColor=function(a,b){var c=a.css("backgroundColor");this.isTransparent(c)||this.rectangle(b.left,b.top,b.width,b.height,a.css("backgroundColor"))},Eb.prototype.renderBorders=function(a){a.forEach(this.renderBorder,this)},Eb.prototype.renderBorder=function(a){this.isTransparent(a.color)||null===a.args||this.drawShape(a.args,a.color)},Eb.prototype.renderBackgroundImage=function(a,b,c){var d=a.parseBackgroundImages();d.reverse().forEach(function(d,e,f){switch(d.method){case"url":var g=this.images.get(d.args[0]);g?this.renderBackgroundRepeating(a,b,g,f.length-(e+1),c):E("Error loading background-image",d.args[0]);break;case"linear-gradient":case"gradient":var h=this.images.get(d.value);h?this.renderBackgroundGradient(h,b,c):E("Error loading background-image",d.args[0]);break;case"none":break;default:E("Unknown background-image type",d.args[0])}},this)},Eb.prototype.renderBackgroundRepeating=function(a,b,c,d,e){var f=a.parseBackgroundSize(b,c.image,d),g=a.parseBackgroundPosition(b,c.image,d,f),h=a.parseBackgroundRepeat(d);switch(h){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+e[3],b.top+g.top+e[0],99999,f.height,e);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+e[0],f.width,99999,e);break;case"no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+g.top+e[0],f.width,f.height,e);break;default:this.renderBackgroundRepeat(c,g,f,{top:b.top,left:b.left},e[3],e[0])}},Eb.prototype.isTransparent=function(a){return!a||"transparent"===a||"rgba(0, 0, 0, 0)"===a},Fb.prototype=Object.create(F.prototype),Fb.prototype.getParentStack=function(a){var b=this.parent?this.parent.stack:null;return b?b.ownStacking?b:b.getParentStack(a):a.stack},Gb.prototype.testRangeBounds=function(a){var b,c,d,e,f=!1;return a.createRange&&(b=a.createRange(),b.getBoundingClientRect&&(c=a.createElement("boundtest"),c.style.height="123px",c.style.display="block",a.body.appendChild(c),b.selectNode(c),d=b.getBoundingClientRect(),e=d.height,123===e&&(f=!0),a.body.removeChild(c))),f},Gb.prototype.testCORS=function(){return"undefined"!=typeof(new Image).crossOrigin},Gb.prototype.testSVG=function(){var a=new Image,c=b.createElement("canvas"),d=c.getContext("2d");a.src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";try{d.drawImage(a,0,0),c.toDataURL()}catch(e){return!1}return!0},Hb.prototype.hasFabric=function(){return html2canvas.fabric?Promise.resolve():Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg"))},Hb.prototype.inlineFormatting=function(a){return/^data:image\/svg\+xml;base64,/.test(a)?this.decode64(this.removeContentType(a)):this.removeContentType(a)},Hb.prototype.removeContentType=function(a){return a.replace(/^data:image\/svg\+xml(;base64)?,/,"")},Hb.prototype.isInline=function(a){return/^data:image\/svg\+xml/i.test(a)},Hb.prototype.createCanvas=function(a){var b=this;return function(c,d){var e=new html2canvas.fabric.StaticCanvas("c");b.image=e.lowerCanvasEl,e.setWidth(d.width).setHeight(d.height).add(html2canvas.fabric.util.groupSVGElements(c,d)).renderAll(),a(e.lowerCanvasEl)}},Hb.prototype.decode64=function(b){return"function"==typeof a.atob?a.atob(b):Ib(b)},Jb.prototype=Object.create(Hb.prototype),Kb.prototype=Object.create(F.prototype),Kb.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},Kb.prototype.transform=function(a){var b=this.node.data;switch(a){case"lowercase":return b.toLowerCase();case"capitalize":return b.replace(/(^|\s|:|-|\(|\))([a-z])/g,Lb);case"uppercase":return b.toUpperCase();default:return b}},Mb.prototype=Object.create(A.prototype),Ob.prototype=Object.create(Eb.prototype),Ob.prototype.setFillStyle=function(a){return this.ctx.fillStyle=a,this.ctx},Ob.prototype.rectangle=function(a,b,c,d,e){this.setFillStyle(e).fillRect(a,b,c,d)},Ob.prototype.drawShape=function(a,b){this.shape(a),this.setFillStyle(b).fill()},Ob.prototype.taints=function(a){if(null===a.tainted){this.taintCtx.drawImage(a.image,0,0);try{this.taintCtx.getImageData(0,0,1,1),a.tainted=!1}catch(c){this.taintCtx=b.createElement("canvas").getContext("2d"),a.tainted=!0}}return a.tainted},Ob.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){(!this.taints(a)||this.options.allowTaint)&&this.ctx.drawImage(a.image,b,c,d,e,f,g,h,i)},Ob.prototype.clip=function(a,b,c){this.ctx.save(),a.filter(Pb).forEach(function(a){this.shape(a).clip()},this),b.call(c),this.ctx.restore()},Ob.prototype.shape=function(a){return this.ctx.beginPath(),a.forEach(function(a,b){"rect"===a[0]?this.ctx.rect.apply(this.ctx,a.slice(1)):this.ctx[0===b?"moveTo":a[0]+"To"].apply(this.ctx,a.slice(1))},this),this.ctx.closePath(),this.ctx},Ob.prototype.font=function(a,b,c,d,e,f){this.setFillStyle(a).font=[b,c,d,e,f].join(" ").split(",")[0]},Ob.prototype.fontShadow=function(a,b,c,d){this.setVariable("shadowColor",a).setVariable("shadowOffsetY",b).setVariable("shadowOffsetX",c).setVariable("shadowBlur",d)},Ob.prototype.clearShadow=function(){this.setVariable("shadowColor","rgba(0,0,0,0)")},Ob.prototype.setOpacity=function(a){this.ctx.globalAlpha=a},Ob.prototype.setTransform=function(a){this.ctx.translate(a.origin[0],a.origin[1]),this.ctx.transform.apply(this.ctx,a.matrix),this.ctx.translate(-a.origin[0],-a.origin[1])},Ob.prototype.setVariable=function(a,b){return this.variables[a]!==b&&(this.variables[a]=this.ctx[a]=b),this},Ob.prototype.text=function(a,b,c){this.ctx.fillText(a,b,c)},Ob.prototype.backgroundRepeatShape=function(a,b,c,d,e,f,g,h,i){var j=[["line",Math.round(e),Math.round(f)],["line",Math.round(e+g),Math.round(f)],["line",Math.round(e+g),Math.round(h+f)],["line",Math.round(e),Math.round(h+f)]];this.clip([j],function(){this.renderBackgroundRepeat(a,b,c,d,i[3],i[0])},this)},Ob.prototype.renderBackgroundRepeat=function(a,b,c,d,e,f){var g=Math.round(d.left+b.left+e),h=Math.round(d.top+b.top+f);this.setFillStyle(this.ctx.createPattern(this.resizeImage(a,c),"repeat")),this.ctx.translate(g,h),this.ctx.fill(),this.ctx.translate(-g,-h)},Ob.prototype.renderBackgroundGradient=function(a,b){if(a instanceof D){var c=this.ctx.createLinearGradient(b.left+b.width*a.x0,b.top+b.height*a.y0,b.left+b.width*a.x1,b.top+b.height*a.y1);a.colorStops.forEach(function(a){c.addColorStop(a.stop,a.color)}),this.rectangle(b.left,b.top,b.width,b.height,c)}},Ob.prototype.resizeImage=function(a,c){var d=a.image;if(d.width===c.width&&d.height===c.height)return d;var e,f=b.createElement("canvas");return f.width=c.width,f.height=c.height,e=f.getContext("2d"),e.drawImage(d,0,0,d.width,d.height,0,0,c.width,c.height),f}}).call({},window,document);
\ No newline at end of file
+(function(a,b,c,d,e,f,g){function h(a,b,c,d){return o(a,a,c,d,b).then(function(e){E("Document cloned");var f="["+Qb+"='true']";a.querySelector(f).removeAttribute(Qb);var g=e.contentWindow,h=g.document.querySelector(f),j=Promise.resolve("function"==typeof b.onclone?b.onclone(g.document):!0);return j.then(function(){return i(h,e,b,c,d)})})}function i(a,c,d,e,f){var g=c.contentWindow,h=new Gb(g.document),i=new C(d,h),n=M(a),o="view"===d.type?e:l(g.document),p="view"===d.type?f:m(g.document),q=new Ob(o,p,i,d,b),r=new O(a,q,h,i,d);return r.ready.then(function(){E("Finished rendering");var b;return b="view"===d.type?k(q.canvas,{width:q.canvas.width,height:q.canvas.height,top:0,left:0,x:0,y:0}):a===g.document.body||a===g.document.documentElement||null!=d.canvas?q.canvas:k(q.canvas,{width:null!=d.width?d.width:n.width,height:null!=d.height?d.height:n.height,top:n.top,left:n.left,x:g.pageXOffset,y:g.pageYOffset}),j(c,d),b})}function j(a,b){b.removeContainer&&(a.parentNode.removeChild(a),E("Cleaned up container"))}function k(a,c){var d=b.createElement("canvas"),e=Math.min(a.width-1,Math.max(0,c.left)),f=Math.min(a.width,Math.max(1,c.left+c.width)),g=Math.min(a.height-1,Math.max(0,c.top)),h=Math.min(a.height,Math.max(1,c.top+c.height));return d.width=c.width,d.height=c.height,E("Cropping canvas at:","left:",c.left,"top:",c.top,"width:",f-e,"height:",h-g),E("Resulting crop with width",c.width,"and height",c.height," with x",e,"and y",g),d.getContext("2d").drawImage(a,e,g,f-e,h-g,c.x,c.y,f-e,h-g),d}function l(a){return Math.max(Math.max(a.body.scrollWidth,a.documentElement.scrollWidth),Math.max(a.body.offsetWidth,a.documentElement.offsetWidth),Math.max(a.body.clientWidth,a.documentElement.clientWidth))}function m(a){return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))}function n(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}function o(a,b,c,d,e){r(a);var f=a.documentElement.cloneNode(!0),g=b.createElement("iframe");return g.className="html2canvas-container",g.style.visibility="hidden",g.style.position="absolute",g.style.left=g.style.top="-10000px",g.width=c,g.height=d,g.scrolling="no",b.body.appendChild(g),new Promise(function(b){var c=g.contentWindow.document;g.contentWindow.onload=g.onload=function(){var f=setInterval(function(){c.body.childNodes.length>0&&(s(a,c),clearInterval(f),"view"===e.type&&g.contentWindow.scrollTo(d,h),b(g))},50)};var d=a.defaultView.pageXOffset,h=a.defaultView.pageYOffset;c.open(),c.write("<!DOCTYPE html><html></html>"),(d!==a.defaultView.pageXOffset||h!==a.defaultView.pageYOffset)&&a.defaultView.scrollTo(d,h),c.replaceChild(e.javascriptEnabled===!0?c.adoptNode(f):t(c.adoptNode(f)),c.documentElement),c.close()})}function p(b,c,d,e,f,g){return new xb(b,c,a.document).then(q(b)).then(function(a){return o(a,d,e,f,g)})}function q(a){return function(c){var d,e=new DOMParser;try{d=e.parseFromString(c,"text/html")}catch(f){E("DOMParser not supported, falling back to createHTMLDocument"),d=b.implementation.createHTMLDocument("");try{d.open(),d.write(c),d.close()}catch(g){E("createHTMLDocument write not supported, falling back to document.body.innerHTML"),d.body.innerHTML=c}}var h=d.querySelector("base");if(!h||!h.href.host){var i=d.createElement("base");i.href=a,d.head.insertBefore(i,d.head.firstChild)}return d}}function r(a){[].slice.call(a.querySelectorAll("canvas"),0).forEach(function(a){a.setAttribute(Rb,"canvas-"+Sb++)})}function s(a,b){[].slice.call(a.querySelectorAll("["+Rb+"]"),0).forEach(function(a){try{var c=b.querySelector("["+Rb+'="'+a.getAttribute(Rb)+'"]');c&&(c.width=a.width,c.height=a.height,c.getContext("2d").putImageData(a.getContext("2d").getImageData(0,0,a.width,a.height),0,0))}catch(d){E("Unable to copy canvas content from",a,d)}a.removeAttribute(Rb)})}function t(a){return[].slice.call(a.childNodes,0).filter(u).forEach(function(b){"SCRIPT"===b.tagName?a.removeChild(b):t(b)}),a}function u(a){return a.nodeType===Node.ELEMENT_NODE}function v(a){var c=b.createElement("a");return c.href=a,c.href=c.href,c}function w(a){if(this.src=a,E("DummyImageContainer for",a),!this.promise||!this.image){E("Initiating DummyImageContainer"),w.prototype.image=new Image;var b=this.image;w.prototype.promise=new Promise(function(a,c){b.onload=a,b.onerror=c,b.src=n(),b.complete===!0&&a(b)})}}function x(a,c){var d,e,f=b.createElement("div"),g=b.createElement("img"),h=b.createElement("span"),i="Hidden Text";f.style.visibility="hidden",f.style.fontFamily=a,f.style.fontSize=c,f.style.margin=0,f.style.padding=0,b.body.appendChild(f),g.src=n(),g.width=1,g.height=1,g.style.margin=0,g.style.padding=0,g.style.verticalAlign="baseline",h.style.fontFamily=a,h.style.fontSize=c,h.style.margin=0,h.style.padding=0,h.appendChild(b.createTextNode(i)),f.appendChild(h),f.appendChild(g),d=g.offsetTop-h.offsetTop+1,f.removeChild(h),f.appendChild(b.createTextNode(i)),f.style.lineHeight="normal",g.style.verticalAlign="super",e=g.offsetTop-f.offsetTop+1,b.body.removeChild(f),this.baseline=d,this.lineWidth=1,this.middle=e}function y(){this.data={}}function z(a,b,c){this.image=null,this.src=a;var d=this,e=M(a);this.promise=(b?new Promise(function(b){"about:blank"===a.contentWindow.document.URL||null==a.contentWindow.document.documentElement?a.contentWindow.onload=a.onload=function(){b(a)}:b(a)}):this.proxyLoad(c.proxy,e,c)).then(function(a){return html2canvas(a.contentWindow.document.documentElement,{type:"view",width:a.width,height:a.height,proxy:c.proxy,javascriptEnabled:c.javascriptEnabled,removeContainer:c.removeContainer,allowTaint:c.allowTaint,imageTimeout:c.imageTimeout/2})}).then(function(a){return d.image=a})}function A(a){this.src=a.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=Promise.resolve(!0)}function B(a,b){this.src=a,this.image=new Image;var c=this;this.tainted=null,this.promise=new Promise(function(d,e){c.image.onload=d,c.image.onerror=e,b&&(c.image.crossOrigin="anonymous"),c.image.src=a,c.image.complete===!0&&d(c.image)})}function C(b,c){this.link=null,this.options=b,this.support=c,this.origin=this.getOrigin(a.location.href)}function D(a){A.apply(this,arguments),this.type=this.TYPES.LINEAR;var b=null===a.args[0].match(this.stepRegExp);b?a.args[0].split(" ").reverse().forEach(function(a){switch(a){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 b=this.y0,c=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=c,this.y1=b}},this):(this.y0=0,this.y1=1),this.colorStops=a.args.slice(b?1:0).map(function(a){var b=a.match(this.stepRegExp);return{color:b[1],stop:"%"===b[3]?b[2]/100:null}},this),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(a,b){null===a.stop&&this.colorStops.slice(b).some(function(c,d){return null!==c.stop?(a.stop=(c.stop-this.colorStops[b-1].stop)/(d+1)+this.colorStops[b-1].stop,!0):!1},this)},this)}function E(){a.html2canvas.logging&&a.console&&a.console.log&&Function.prototype.bind.call(a.console.log,a.console).apply(a.console,[Date.now()-a.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function F(a,b){this.node=a,this.parent=b,this.stack=null,this.bounds=null,this.borders=null,this.clip=[],this.backgroundClip=[],this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null,this.isPseudoElement=!1,this.opacity=null}function G(a){var b=a.options[a.selectedIndex||0];return b?b.text||"":""}function H(a){return a&&"matrix"===a[1]?a[2].split(",").map(function(a){return parseFloat(a.trim())}):void 0}function I(a){return-1!==a.toString().indexOf("%")}function J(a){var b,c,d,e,f,g,h,i=" \r\n	",j=[],k=0,l=0,m=function(){b&&('"'===c.substr(0,1)&&(c=c.substr(1,c.length-2)),c&&h.push(c),"-"===b.substr(0,1)&&(e=b.indexOf("-",1)+1)>0&&(d=b.substr(0,e),b=b.substr(e)),j.push({prefix:d,method:b.toLowerCase(),value:f,args:h,image:null})),h=[],b=d=c=f=""};return h=[],b=d=c=f="",a.split("").forEach(function(a){if(!(0===k&&i.indexOf(a)>-1)){switch(a){case'"':g?g===a&&(g=null):g=a;break;case"(":if(g)break;if(0===k)return k=1,void(f+=a);l++;break;case")":if(g)break;if(1===k){if(0===l)return k=0,f+=a,void m();l--}break;case",":if(g)break;if(0===k)return void m();if(1===k&&0===l&&!b.match(/^url$/i))return h.push(c),c="",void(f+=a)}f+=a,0===k?b+=a:c+=a}}),m(),j}function K(a){return a.replace("px","")}function L(a){return parseFloat(a)}function M(a){if(a.getBoundingClientRect){var b=a.getBoundingClientRect(),c=null==a.offsetWidth?b.width:a.offsetWidth;return{top:b.top,bottom:b.bottom||b.top+b.height,right:b.left+c,left:b.left,width:c,height:null==a.offsetHeight?b.height:a.offsetHeight}}return{}}function N(a){var b=a.offsetParent?N(a.offsetParent):{top:0,left:0};return{top:a.offsetTop+b.top,bottom:a.offsetTop+a.offsetHeight+b.top,right:a.offsetLeft+b.left+a.offsetWidth,left:a.offsetLeft+b.left,width:a.offsetWidth,height:a.offsetHeight}}function O(a,b,c,d,e){E("Starting NodeParser"),this.renderer=b,this.options=e,this.range=null,this.support=c,this.renderQueue=[],this.stack=new Fb(!0,1,a.ownerDocument,null);var f=new F(a,null);if(a===a.ownerDocument.documentElement){var g=new F(this.renderer.isTransparent(f.css("backgroundColor"))?a.ownerDocument.body:a.ownerDocument.documentElement,null);b.rectangle(0,0,b.width,b.height,g.css("backgroundColor"))}f.visibile=f.isElementVisible(),this.createPseudoHideStyles(a.ownerDocument),this.disableAnimations(a.ownerDocument),this.nodes=sb([f].concat(this.getChildren(f)).filter(function(a){return a.visible=a.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new y,E("Fetched nodes, total:",this.nodes.length),E("Calculate overflow clips"),this.calculateOverflowClips(),E("Start fetching images"),this.images=d.fetch(this.nodes.filter(jb)),this.ready=this.images.ready.then(ob(function(){return E("Images loaded, starting parsing"),E("Creating stacking contexts"),this.createStackingContexts(),E("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.parse(this.stack),E("Render queue created with "+this.renderQueue.length+" items"),new Promise(ob(function(a){e.async?"function"==typeof e.async?e.async.call(this,this.renderQueue,a):this.renderQueue.length>0?(this.renderIndex=0,this.asyncRenderer(this.renderQueue,a)):a():(this.renderQueue.forEach(this.paint,this),a())},this))},this))}function P(a){return a.parent&&a.parent.clip.length}function Q(a){return a.replace(/(\-[a-z])/g,function(a){return a.toUpperCase().replace("-","")})}function R(){}function S(a,b,c,d){var e=4*((Math.sqrt(2)-1)/3),f=c*e,g=d*e,h=a+c,i=b+d;return{topLeft:U({x:a,y:i},{x:a,y:i-g},{x:h-f,y:b},{x:h,y:b}),topRight:U({x:a,y:b},{x:a+f,y:b},{x:h,y:i-g},{x:h,y:i}),bottomRight:U({x:h,y:b},{x:h,y:b+g},{x:a+f,y:i},{x:a,y:i}),bottomLeft:U({x:h,y:i},{x:h-f,y:i},{x:a,y:b+g},{x:a,y:b})}}function T(a,b,c){var d=a.left,e=a.top,f=a.width,g=a.height,h=b[0][0],i=b[0][1],j=b[1][0],k=b[1][1],l=b[2][0],m=b[2][1],n=b[3][0],o=b[3][1],p=f-j,q=g-m,r=f-l,s=g-o;return{topLeftOuter:S(d,e,h,i).topLeft.subdivide(.5),topLeftInner:S(d+c[3].width,e+c[0].width,Math.max(0,h-c[3].width),Math.max(0,i-c[0].width)).topLeft.subdivide(.5),topRightOuter:S(d+p,e,j,k).topRight.subdivide(.5),topRightInner:S(d+Math.min(p,f+c[3].width),e+c[0].width,p>f+c[3].width?0:j-c[3].width,k-c[0].width).topRight.subdivide(.5),bottomRightOuter:S(d+r,e+q,l,m).bottomRight.subdivide(.5),bottomRightInner:S(d+Math.min(r,f-c[3].width),e+Math.min(q,g+c[0].width),Math.max(0,l-c[1].width),m-c[2].width).bottomRight.subdivide(.5),bottomLeftOuter:S(d,e+s,n,o).bottomLeft.subdivide(.5),bottomLeftInner:S(d+c[3].width,e+s,Math.max(0,n-c[3].width),o-c[2].width).bottomLeft.subdivide(.5)}}function U(a,b,c,d){var e=function(a,b,c){return{x:a.x+(b.x-a.x)*c,y:a.y+(b.y-a.y)*c}};return{start:a,startControl:b,endControl:c,end:d,subdivide:function(f){var g=e(a,b,f),h=e(b,c,f),i=e(c,d,f),j=e(g,h,f),k=e(h,i,f),l=e(j,k,f);return[U(a,g,j,l),U(l,k,i,d)]},curveTo:function(a){a.push(["bezierCurve",b.x,b.y,c.x,c.y,d.x,d.y])},curveToReversed:function(d){d.push(["bezierCurve",c.x,c.y,b.x,b.y,a.x,a.y])}}}function V(a,b,c,d,e,f,g){var h=[];return b[0]>0||b[1]>0?(h.push(["line",d[1].start.x,d[1].start.y]),d[1].curveTo(h)):h.push(["line",a.c1[0],a.c1[1]]),c[0]>0||c[1]>0?(h.push(["line",f[0].start.x,f[0].start.y]),f[0].curveTo(h),h.push(["line",g[0].end.x,g[0].end.y]),g[0].curveToReversed(h)):(h.push(["line",a.c2[0],a.c2[1]]),h.push(["line",a.c3[0],a.c3[1]])),b[0]>0||b[1]>0?(h.push(["line",e[1].end.x,e[1].end.y]),e[1].curveToReversed(h)):h.push(["line",a.c4[0],a.c4[1]]),h}function W(a,b,c,d,e,f,g){b[0]>0||b[1]>0?(a.push(["line",d[0].start.x,d[0].start.y]),d[0].curveTo(a),d[1].curveTo(a)):a.push(["line",f,g]),(c[0]>0||c[1]>0)&&a.push(["line",e[0].start.x,e[0].start.y])}function X(a){return a.cssInt("zIndex")<0}function Y(a){return a.cssInt("zIndex")>0}function Z(a){return 0===a.cssInt("zIndex")}function $(a){return-1!==["inline","inline-block","inline-table"].indexOf(a.css("display"))}function _(a){return a instanceof Fb}function ab(a){return a.node.data.trim().length>0}function bb(a){return/^(normal|none|0px)$/.test(a.parent.css("letterSpacing"))}function cb(a){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(b){var c=a.css("border"+b+"Radius"),d=c.split(" ");return d.length<=1&&(d[1]=d[0]),d.map(pb)})}function db(a){return a.nodeType===Node.TEXT_NODE||a.nodeType===Node.ELEMENT_NODE}function eb(a){var b=a.css("position"),c=-1!==["absolute","relative","fixed"].indexOf(b)?a.css("zIndex"):"auto";return"auto"!==c}function fb(a){return"static"!==a.css("position")}function gb(a){return"none"!==a.css("float")}function hb(a){return-1!==["inline-block","inline-table"].indexOf(a.css("display"))}function ib(a){var b=this;return function(){return!a.apply(b,arguments)}}function jb(a){return a.node.nodeType===Node.ELEMENT_NODE}function kb(a){return a.isPseudoElement===!0}function lb(a){return a.node.nodeType===Node.TEXT_NODE}function mb(a){return function(b,c){return b.cssInt("zIndex")+a.indexOf(b)/a.length-(c.cssInt("zIndex")+a.indexOf(c)/a.length)}}function nb(a){return a.getOpacity()<1}function ob(a,b){return function(){return a.apply(b,arguments)}}function pb(a){return parseInt(a,10)}function qb(a){return a.width}function rb(a){return a.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(a.node.nodeName)}function sb(a){return[].concat.apply([],a)}function tb(a){var b=a.substr(0,1);return b===a.substr(a.length-1)&&b.match(/'|"/)?a.substr(1,a.length-2):a}function ub(b){for(var c,d=[],e=0,f=!1;b.length;)vb(b[e])===f?(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)),f=!f,e=0):e++,e>=b.length&&(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)));return d}function vb(a){return-1!==[32,13,10,9,45].indexOf(a)}function wb(a){return/[^\u0000-\u00ff]/.test(a)}function xb(a,b,c){var d=Ab(Ub),e=Bb(b,a,d);return Ub?Nb(e):zb(c,e,d).then(function(a){return Ib(a.content)})}function yb(a,b,c){var d=Ab(Vb),e=Bb(b,a,d);return Vb?Promise.resolve(e):zb(c,e,d).then(function(a){return"data:"+a.type+";base64,"+a.content})}function zb(b,c,d){return new Promise(function(e,f){var g=b.createElement("script"),h=function(){delete a.html2canvas.proxy[d],b.body.removeChild(g)};a.html2canvas.proxy[d]=function(a){h(),e(a)},g.src=c,g.onerror=function(a){h(),f(a)},b.body.appendChild(g)})}function Ab(a){return a?"":"html2canvas_"+Date.now()+"_"+ ++Tb+"_"+Math.round(1e5*Math.random())}function Bb(a,b,c){return a+"?url="+encodeURIComponent(b)+(c.length?"&callback=html2canvas.proxy."+c:"")}function Cb(a,c){var d=(b.createElement("script"),b.createElement("a"));d.href=a,a=d.href,this.src=a,this.image=new Image;var e=this;this.promise=new Promise(function(d,f){e.image.crossOrigin="Anonymous",e.image.onload=d,e.image.onerror=f,new yb(a,c,b).then(function(a){e.image.src=a})["catch"](f)})}function Db(a,b,c){F.call(this,a,b),this.isPseudoElement=!0,this.before=":before"===c}function Eb(a,b,c,d,e){this.width=a,this.height=b,this.images=c,this.options=d,this.document=e}function Fb(a,b,c,d){F.call(this,c,d),this.ownStacking=a,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*b}function Gb(a){this.rangeBounds=this.testRangeBounds(a),this.cors=this.testCORS(),this.svg=this.testSVG()}function Hb(a){this.src=a,this.image=null;var b=this;this.promise=this.hasFabric().then(function(){return b.isInline(a)?Promise.resolve(b.inlineFormatting(a)):Nb(a)}).then(function(a){return new Promise(function(c){html2canvas.fabric.loadSVGFromString(a,b.createCanvas.call(b,c))})})}function Ib(a){var b,c,d,e,f,g,h,i,j="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",k=a.length,l="";for(b=0;k>b;b+=4)c=j.indexOf(a[b]),d=j.indexOf(a[b+1]),e=j.indexOf(a[b+2]),f=j.indexOf(a[b+3]),g=c<<2|d>>4,h=(15&d)<<4|e>>2,i=(3&e)<<6|f,l+=64===e?String.fromCharCode(g):64===f||-1===f?String.fromCharCode(g,h):String.fromCharCode(g,h,i);return l}function Jb(a,b){this.src=a,this.image=null;var c=this;this.promise=b?new Promise(function(b,d){c.image=new Image,c.image.onload=b,c.image.onerror=d,c.image.src="data:image/svg+xml,"+(new XMLSerializer).serializeToString(a),c.image.complete===!0&&b(c.image)}):this.hasFabric().then(function(){return new Promise(function(b){html2canvas.fabric.parseSVGDocument(a,c.createCanvas.call(c,b))})})}function Kb(a,b){F.call(this,a,b)}function Lb(a,b,c){return a.length>0?b+c.toUpperCase():void 0}function Mb(a){A.apply(this,arguments),this.type="linear"===a.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}function Nb(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onload=function(){200===d.status?b(d.responseText):c(new Error(d.statusText))},d.onerror=function(){c(new Error("Network Error"))},d.send()})}function Ob(a,b){Eb.apply(this,arguments),this.canvas=this.options.canvas||this.document.createElement("canvas"),this.options.canvas||(this.canvas.width=a,this.canvas.height=b),this.ctx=this.canvas.getContext("2d"),this.options.background&&this.rectangle(0,0,a,b,this.options.background),this.taintCtx=this.document.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},E("Initialized CanvasRenderer with size",a,"x",b)}function Pb(a){return a.length>0}if(!function(){var c,d,f,g;!function(){var a={},b={};c=function(b,c,d){a[b]={deps:c,callback:d}},g=f=d=function(c){function e(a){if("."!==a.charAt(0))return a;for(var b=a.split("/"),d=c.split("/").slice(0,-1),e=0,f=b.length;f>e;e++){var g=b[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(g._eak_seen=a,b[c])return b[c];if(b[c]={},!a[c])throw new Error("Could not find module "+c);for(var f,h=a[c],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)k.push("exports"===i[l]?f={}:d(e(i[l])));var n=j.apply(this,k);return b[c]=f||n}}(),c("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j<a.length;j++)g=a[j],g&&e(g.then)?g.then(d(j),c):f(j,g)})}var d=a.isArray,e=a.isFunction;b.all=c}),c("promise/asap",["exports"],function(c){"use strict";function d(){return function(){process.nextTick(h)}}function f(){var a=0,c=new l(h),d=b.createTextNode("");return c.observe(d,{characterData:!0}),function(){d.data=a=++a%2}}function g(){return function(){m.setTimeout(h,1)}}function h(){for(var a=0;a<n.length;a++){var b=n[a],c=b[0],d=b[1];c(d)}n=[]}function i(a,b){var c=n.push([a,b]);1===c&&j()}var j,k="undefined"!=typeof a?a:{},l=k.MutationObserver||k.WebKitMutationObserver,m="undefined"!=typeof e?e:this,n=[];j="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?d():l?f():g(),c.asap=i}),c("promise/cast",["exports"],function(a){"use strict";function b(a){if(a&&"object"==typeof a&&a.constructor===this)return a;var b=this;return new b(function(b){b(a)})}a.cast=b}),c("promise/config",["exports"],function(a){"use strict";function b(a,b){return 2!==arguments.length?c[a]:void(c[a]=b)}var c={instrument:!1};a.config=c,a.configure=b}),c("promise/polyfill",["./promise","./utils","exports"],function(b,c,d){"use strict";function e(){var b="Promise"in a&&"cast"in a.Promise&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var b;return new a.Promise(function(a){b=a}),g(b)}();b||(a.Promise=f)}var f=b.Promise,g=c.isFunction;d.polyfill=e}),c("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(a,b,c,d,e,f,g,h,i){"use strict";function j(a){if(!w(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof j))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],k(a,this)}function k(a,b){function c(a){p(b,a)}function d(a){r(b,a)}try{a(c,d)}catch(e){d(e)}}function l(a,b,c,d){var e,f,g,h,i=w(c);if(i)try{e=c(d),g=!0}catch(j){h=!0,f=j}else e=d,g=!0;o(b,e)||(i&&g?p(b,e):h?r(b,f):a===F?p(b,e):a===G&&r(b,e))}function m(a,b,c,d){var e=a._subscribers,f=e.length;e[f]=b,e[f+F]=c,e[f+G]=d}function n(a,b){for(var c,d,e=a._subscribers,f=a._detail,g=0;g<e.length;g+=3)c=e[g],d=e[g+b],l(b,c,d,f);a._subscribers=null}function o(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(v(b)&&(d=b.then,w(d)))return d.call(b,function(d){return c?!0:(c=!0,void(b!==d?p(a,d):q(a,d)))},function(b){return c?!0:(c=!0,void r(a,b))}),!0}catch(e){return c?!0:(r(a,e),!0)}return!1}function p(a,b){a===b?q(a,b):o(a,b)||q(a,b)}function q(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(s,a))}function r(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(t,a))}function s(a){n(a,a._state=F)}function t(a){n(a,a._state=G)}var u=a.config,v=(a.configure,b.objectOrFunction),w=b.isFunction,x=(b.now,c.cast),y=d.all,z=e.race,A=f.resolve,B=g.reject,C=h.asap;u.async=C;var D=void 0,E=0,F=1,G=2;j.prototype={constructor:j,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(a,b){var c=this,d=new this.constructor(function(){});if(this._state){var e=arguments;u.async(function(){l(c._state,d,e[c._state-1],c._detail)})}else m(this,d,a,b);return d},"catch":function(a){return this.then(null,a)}},j.all=y,j.cast=x,j.race=z,j.resolve=A,j.reject=B,i.Promise=j}),c("promise/race",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to race.");return new b(function(b,c){for(var d,e=0;e<a.length;e++)d=a[e],d&&"function"==typeof d.then?d.then(b,c):b(d)})}var d=a.isArray;b.race=c}),c("promise/reject",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b,c){c(a)})}a.reject=b}),c("promise/resolve",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b){b(a)})}a.resolve=b}),c("promise/utils",["exports"],function(a){"use strict";function b(a){return c(a)||"object"==typeof a&&null!==a}function c(a){return"function"==typeof a}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}var e=Date.now||function(){return(new Date).getTime()};a.objectOrFunction=b,a.isFunction=c,a.isArray=d,a.now=e}),d("promise/polyfill").polyfill()}(),"function"!=typeof Object.create||"function"!=typeof b.createElement("canvas").getContext)return void(a.html2canvas=function(){return Promise.reject("No canvas support")});var Qb="data-html2canvas-node",Rb="data-html2canvas-canvas-clone",Sb=0;a.html2canvas=function(c,d){if(d=d||{},d.logging&&(a.html2canvas.logging=!0,a.html2canvas.start=Date.now()),d.async="undefined"==typeof d.async?!0:d.async,d.allowTaint="undefined"==typeof d.allowTaint?!1:d.allowTaint,d.removeContainer="undefined"==typeof d.removeContainer?!0:d.removeContainer,d.javascriptEnabled="undefined"==typeof d.javascriptEnabled?!1:d.javascriptEnabled,d.imageTimeout="undefined"==typeof d.imageTimeout?1e4:d.imageTimeout,"string"==typeof c)return"string"!=typeof d.proxy?Promise.reject("Proxy must be used when rendering url"):p(v(c),d.proxy,b,a.innerWidth,a.innerHeight,d).then(function(b){return i(b.contentWindow.document.documentElement,b,d,a.innerWidth,a.innerHeight)});var e=(c===g?[b.documentElement]:c.length?c:[c])[0];return e.setAttribute(Qb,"true"),h(e.ownerDocument,d,e.ownerDocument.defaultView.innerWidth,e.ownerDocument.defaultView.innerHeight).then(function(a){return"function"==typeof d.onrendered&&(E("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"),d.onrendered(a)),a})},a.html2canvas.punycode=this.punycode,a.html2canvas.proxy={},y.prototype.getMetrics=function(a,b){return this.data[a+"-"+b]===g&&(this.data[a+"-"+b]=new x(a,b)),this.data[a+"-"+b]},z.prototype.proxyLoad=function(a,b,c){var d=this.src;return p(d.src,a,d.ownerDocument,b.width,b.height,c)},A.prototype.TYPES={LINEAR:1,RADIAL:2},C.prototype.findImages=function(a){var b=[];return a.reduce(function(a,b){switch(b.node.nodeName){case"IMG":return a.concat([{args:[b.node.src],method:"url"}]);case"svg":case"IFRAME":return a.concat([{args:[b.node],method:b.node.nodeName}])}return a},[]).forEach(this.addImage(b,this.loadImage),this),b},C.prototype.findBackgroundImage=function(a,b){return b.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(a,this.loadImage),this),a},C.prototype.addImage=function(a,b){return function(c){c.args.forEach(function(d){this.imageExists(a,d)||(a.splice(0,0,b.call(this,c)),E("Added image #"+a.length,"string"==typeof d?d.substring(0,100):d))},this)}},C.prototype.hasImageBackground=function(a){return"none"!==a.method},C.prototype.loadImage=function(a){if("url"===a.method){var b=a.args[0];return!this.isSVG(b)||this.support.svg||this.options.allowTaint?b.match(/data:image\/.*;base64,/i)?new B(b.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(b)||this.options.allowTaint===!0||this.isSVG(b)?new B(b,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new B(b,!0):this.options.proxy?new Cb(b,this.options.proxy):new w(b):new Hb(b)}return"linear-gradient"===a.method?new D(a):"gradient"===a.method?new Mb(a):"svg"===a.method?new Jb(a.args[0],this.support.svg):"IFRAME"===a.method?new z(a.args[0],this.isSameOrigin(a.args[0].src),this.options):new w(a)},C.prototype.isSVG=function(a){return"svg"===a.substring(a.length-3).toLowerCase()||Hb.prototype.isInline(a)},C.prototype.imageExists=function(a,b){return a.some(function(a){return a.src===b})},C.prototype.isSameOrigin=function(a){return this.getOrigin(a)===this.origin},C.prototype.getOrigin=function(a){var c=this.link||(this.link=b.createElement("a"));return c.href=a,c.href=c.href,c.protocol+c.hostname+c.port},C.prototype.getPromise=function(a){return this.timeout(a,this.options.imageTimeout)["catch"](function(){var b=new w(a.src);return b.promise.then(function(b){a.image=b})})},C.prototype.get=function(a){var b=null;return this.images.some(function(c){return(b=c).src===a})?b:null},C.prototype.fetch=function(a){return this.images=a.reduce(ob(this.findBackgroundImage,this),this.findImages(a)),this.images.forEach(function(a,b){a.promise.then(function(){E("Succesfully loaded image #"+(b+1),a)},function(c){E("Failed loading image #"+(b+1),a,c)})}),this.ready=Promise.all(this.images.map(this.getPromise,this)),E("Finished searching images"),this},C.prototype.timeout=function(a,b){var c;return Promise.race([a.promise,new Promise(function(d,e){c=setTimeout(function(){E("Timed out loading image",a),e(a)},b)})]).then(function(a){return clearTimeout(c),a})},D.prototype=Object.create(A.prototype),D.prototype.stepRegExp=/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/,F.prototype.cloneTo=function(a){a.visible=this.visible,a.borders=this.borders,a.bounds=this.bounds,a.clip=this.clip,a.backgroundClip=this.backgroundClip,a.computedStyles=this.computedStyles,a.styles=this.styles,a.backgroundImages=this.backgroundImages,a.opacity=this.opacity},F.prototype.getOpacity=function(){return null===this.opacity?this.opacity=this.cssFloat("opacity"):this.opacity},F.prototype.assignStack=function(a){this.stack=a,a.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(a){return this.computedStyles||(this.computedStyles=this.isPseudoElement?this.parent.computedStyle(this.before?":before":":after"):this.computedStyle(null)),this.styles[a]||(this.styles[a]=this.computedStyles[a])},F.prototype.prefixedCss=function(a){var b=["webkit","moz","ms","o"],c=this.css(a);return c===g&&b.some(function(b){return c=this.css(b+a.substr(0,1).toUpperCase()+a.substr(1)),c!==g},this),c===g?null:c},F.prototype.computedStyle=function(a){return this.node.ownerDocument.defaultView.getComputedStyle(this.node,a)},F.prototype.cssInt=function(a){var b=parseInt(this.css(a),10);return isNaN(b)?0:b},F.prototype.cssFloat=function(a){var b=parseFloat(this.css(a));return isNaN(b)?0:b},F.prototype.fontWeight=function(){var a=this.css("fontWeight");switch(parseInt(a,10)){case 401:a="bold";break;case 400:a="normal"}return a},F.prototype.parseClip=function(){var a=this.css("clip").match(this.CLIP);return a?{top:parseInt(a[1],10),right:parseInt(a[2],10),bottom:parseInt(a[3],10),left:parseInt(a[4],10)}:null},F.prototype.parseBackgroundImages=function(){return this.backgroundImages||(this.backgroundImages=J(this.css("backgroundImage")))},F.prototype.cssList=function(a,b){var c=(this.css(a)||"").split(",");return c=c[b||0]||c[0]||"auto",c=c.trim().split(" "),1===c.length&&(c=[c[0],c[0]]),c},F.prototype.parseBackgroundSize=function(a,b,c){var d,e,f=this.cssList("backgroundSize",c);if(I(f[0]))d=a.width*parseFloat(f[0])/100;else{if(/contain|cover/.test(f[0])){var g=a.width/a.height,h=b.width/b.height;return h>g^"contain"===f[0]?{width:a.height*h,height:a.height}:{width:a.width,height:a.width/h}}d=parseInt(f[0],10)}return e="auto"===f[0]&&"auto"===f[1]?b.height:"auto"===f[1]?d/b.width*b.height:I(f[1])?a.height*parseFloat(f[1])/100:parseInt(f[1],10),"auto"===f[0]&&(d=e/b.height*b.width),{width:d,height:e}},F.prototype.parseBackgroundPosition=function(a,b,c,d){var e,f,g=this.cssList("backgroundPosition",c);return e=I(g[0])?(a.width-(d||b).width)*(parseFloat(g[0])/100):parseInt(g[0],10),f="auto"===g[1]?e/b.width*b.height:I(g[1])?(a.height-(d||b).height)*parseFloat(g[1])/100:parseInt(g[1],10),"auto"===g[0]&&(e=f/b.height*b.width),{left:e,top:f}},F.prototype.parseBackgroundRepeat=function(a){return this.cssList("backgroundRepeat",a)[0]},F.prototype.parseTextShadows=function(){var a=this.css("textShadow"),b=[];if(a&&"none"!==a)for(var c=a.match(this.TEXT_SHADOW_PROPERTY),d=0;c&&d<c.length;d++){var e=c[d].match(this.TEXT_SHADOW_VALUES);b.push({color:e[0],offsetX:e[1]?parseFloat(e[1].replace("px","")):0,offsetY:e[2]?parseFloat(e[2].replace("px","")):0,blur:e[3]?e[3].replace("px",""):0})}return b},F.prototype.parseTransform=function(){if(!this.transformData)if(this.hasTransform()){var a=this.parseBounds(),b=this.prefixedCss("transformOrigin").split(" ").map(K).map(L);b[0]+=a.left,b[1]+=a.top,this.transformData={origin:b,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 a=this.prefixedCss("transform"),b=a?H(a.match(this.MATRIX_PROPERTY)):null;this.transformMatrix=b?b:[1,0,0,1,0,0]}return this.transformMatrix},F.prototype.parseBounds=function(){return this.bounds||(this.bounds=this.hasTransform()?N(this.node):M(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 a=this.node.value||"";return a="SELECT"===this.node.tagName?G(this.node):a,0===a.length?this.node.placeholder||"":a},F.prototype.MATRIX_PROPERTY=/(matrix)\((.+)\)/,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\)$/,O.prototype.calculateOverflowClips=function(){this.nodes.forEach(function(a){if(jb(a)){kb(a)&&a.appendToDOM(),a.borders=this.parseBorders(a);var b="hidden"===a.css("overflow")?[a.borders.clip]:[],c=a.parseClip();c&&-1!==["absolute","fixed"].indexOf(a.css("position"))&&b.push([["rect",a.bounds.left+c.left,a.bounds.top+c.top,c.right-c.left,c.bottom-c.top]]),a.clip=P(a)?a.parent.clip.concat(b):b,a.backgroundClip="hidden"!==a.css("overflow")?a.clip.concat([a.borders.clip]):a.clip,kb(a)&&a.cleanDOM()}else lb(a)&&(a.clip=P(a)?a.parent.clip:[]);kb(a)||(a.bounds=null)},this)},O.prototype.asyncRenderer=function(a,b,c){c=c||Date.now(),this.paint(a[this.renderIndex++]),a.length===this.renderIndex?b():c+20>Date.now()?this.asyncRenderer(a,b,c):setTimeout(ob(function(){this.asyncRenderer(a,b)},this),0)},O.prototype.createPseudoHideStyles=function(a){this.createStyles(a,"."+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+':before { content: "" !important; display: none !important; }.'+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER+':after { content: "" !important; display: none !important; }')},O.prototype.disableAnimations=function(a){this.createStyles(a,"* { -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;}")},O.prototype.createStyles=function(a,b){var c=a.createElement("style");c.innerHTML=b,a.body.appendChild(c)},O.prototype.getPseudoElements=function(a){var b=[[a]];if(a.node.nodeType===Node.ELEMENT_NODE){var c=this.getPseudoElement(a,":before"),d=this.getPseudoElement(a,":after");c&&b.push(c),d&&b.push(d)}return sb(b)},O.prototype.getPseudoElement=function(a,c){var d=a.computedStyle(c);if(!d||!d.content||"none"===d.content||"-moz-alt-content"===d.content||"none"===d.display)return null;for(var e=tb(d.content),f="url"===e.substr(0,3),g=b.createElement(f?"img":"html2canvaspseudoelement"),h=new Db(g,a,c),i=d.length-1;i>=0;i--){var j=Q(d.item(i));g.style[j]=d[j]}if(g.className=Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+" "+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER,f)return g.src=J(e)[0].args[0],[h];var k=b.createTextNode(e);return g.appendChild(k),[h,new Kb(k,h)]},O.prototype.getChildren=function(a){return sb([].filter.call(a.node.childNodes,db).map(function(b){var c=[b.nodeType===Node.TEXT_NODE?new Kb(b,a):new F(b,a)].filter(rb);return b.nodeType===Node.ELEMENT_NODE&&c.length&&"TEXTAREA"!==b.tagName?c[0].isElementVisible()?c.concat(this.getChildren(c[0])):[]:c},this))},O.prototype.newStackingContext=function(a,b){var c=new Fb(b,a.getOpacity(),a.node,a.parent);a.cloneTo(c);var d=b?c.getParentStack(this):c.parent.stack;d.contexts.push(c),a.stack=c},O.prototype.createStackingContexts=function(){this.nodes.forEach(function(a){jb(a)&&(this.isRootElement(a)||nb(a)||eb(a)||this.isBodyWithTransparentRoot(a)||a.hasTransform())?this.newStackingContext(a,!0):jb(a)&&(fb(a)&&Z(a)||hb(a)||gb(a))?this.newStackingContext(a,!1):a.assignStack(a.parent.stack)},this)},O.prototype.isBodyWithTransparentRoot=function(a){return"BODY"===a.node.nodeName&&this.renderer.isTransparent(a.parent.css("backgroundColor"))},O.prototype.isRootElement=function(a){return null===a.parent},O.prototype.sortStackingContexts=function(a){a.contexts.sort(mb(a.contexts.slice(0))),a.contexts.forEach(this.sortStackingContexts,this)},O.prototype.parseTextBounds=function(a){return function(b,c,d){if("none"!==a.parent.css("textDecoration").substr(0,4)||0!==b.trim().length){if(this.support.rangeBounds&&!a.parent.hasTransform()){var e=d.slice(0,c).join("").length;return this.getRangeBounds(a.node,e,b.length)}if(a.node&&"string"==typeof a.node.data){var f=a.node.splitText(b.length),g=this.getWrapperBounds(a.node,a.parent.hasTransform());return a.node=f,g}}else(!this.support.rangeBounds||a.parent.hasTransform())&&(a.node=a.node.splitText(b.length));return{}}},O.prototype.getWrapperBounds=function(a,b){var c=a.ownerDocument.createElement("html2canvaswrapper"),d=a.parentNode,e=a.cloneNode(!0);c.appendChild(a.cloneNode(!0)),d.replaceChild(c,a);var f=b?N(c):M(c);return d.replaceChild(e,c),f},O.prototype.getRangeBounds=function(a,b,c){var d=this.range||(this.range=a.ownerDocument.createRange());return d.setStart(a,b),d.setEnd(a,b+c),d.getBoundingClientRect()},O.prototype.parse=function(a){var b=a.contexts.filter(X),c=a.children.filter(jb),d=c.filter(ib(gb)),e=d.filter(ib(fb)).filter(ib($)),f=c.filter(ib(fb)).filter(gb),g=d.filter(ib(fb)).filter($),h=a.contexts.concat(d.filter(fb)).filter(Z),i=a.children.filter(lb).filter(ab),j=a.contexts.filter(Y);b.concat(e).concat(f).concat(g).concat(h).concat(i).concat(j).forEach(function(a){this.renderQueue.push(a),_(a)&&(this.parse(a),this.renderQueue.push(new R))},this)},O.prototype.paint=function(a){try{a instanceof R?this.renderer.ctx.restore():lb(a)?(kb(a.parent)&&a.parent.appendToDOM(),this.paintText(a),kb(a.parent)&&a.parent.cleanDOM()):this.paintNode(a)}catch(b){E(b)}},O.prototype.paintNode=function(a){_(a)&&(this.renderer.setOpacity(a.opacity),this.renderer.ctx.save(),a.hasTransform()&&this.renderer.setTransform(a.parseTransform()));var b=a.parseBounds();this.renderer.clip(a.backgroundClip,function(){this.renderer.renderBackground(a,b,a.borders.borders.map(qb))},this),this.renderer.clip(a.clip,function(){this.renderer.renderBorders(a.borders.borders)},this),this.renderer.clip(a.backgroundClip,function(){switch(a.node.nodeName){case"svg":case"IFRAME":var c=this.images.get(a.node);c?this.renderer.renderImage(a,b,a.borders,c):E("Error loading <"+a.node.nodeName+">",a.node);break;case"IMG":var d=this.images.get(a.node.src);d?this.renderer.renderImage(a,b,a.borders,d):E("Error loading <img>",a.node.src);break;case"CANVAS":this.renderer.renderImage(a,b,a.borders,{image:a.node});break;case"SELECT":case"INPUT":case"TEXTAREA":this.paintFormValue(a)}},this)},O.prototype.paintFormValue=function(a){if(a.getValue().length>0){var b=a.node.ownerDocument,c=b.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(b){try{c.style[b]=a.css(b)}catch(d){E("html2canvas: Parse: Exception caught in renderFormValue: "+d.message)}});var e=a.parseBounds();c.style.position="fixed",c.style.left=e.left+"px",c.style.top=e.top+"px",c.textContent=a.getValue(),b.body.appendChild(c),this.paintText(new Kb(c.firstChild,a)),b.body.removeChild(c)}},O.prototype.paintText=function(b){b.applyTextTransform();var c=a.html2canvas.punycode.ucs2.decode(b.node.data),d=this.options.letterRendering&&!bb(b)||wb(b.node.data)?c.map(function(b){return a.html2canvas.punycode.ucs2.encode([b])}):ub(c),e=b.parent.fontWeight(),f=b.parent.css("fontSize"),g=b.parent.css("fontFamily"),h=b.parent.parseTextShadows();this.renderer.font(b.parent.css("color"),b.parent.css("fontStyle"),b.parent.css("fontVariant"),e,f,g),h.length?this.renderer.fontShadow(h[0].color,h[0].offsetX,h[0].offsetY,h[0].blur):this.renderer.clearShadow(),this.renderer.clip(b.parent.clip,function(){d.map(this.parseTextBounds(b),this).forEach(function(a,c){a&&(this.renderer.text(d[c],a.left,a.bottom),this.renderTextDecoration(b.parent,a,this.fontMetrics.getMetrics(g,f)))},this)},this)},O.prototype.renderTextDecoration=function(a,b,c){switch(a.css("textDecoration").split(" ")[0]){case"underline":this.renderer.rectangle(b.left,Math.round(b.top+c.baseline+c.lineWidth),b.width,1,a.css("color"));break;case"overline":this.renderer.rectangle(b.left,Math.round(b.top),b.width,1,a.css("color"));break;case"line-through":this.renderer.rectangle(b.left,Math.ceil(b.top+c.middle+c.lineWidth),b.width,1,a.css("color"))}},O.prototype.parseBorders=function(a){var b=a.parseBounds(),c=cb(a),d=["Top","Right","Bottom","Left"].map(function(b){return{width:a.cssInt("border"+b+"Width"),color:a.css("border"+b+"Color"),args:null}}),e=T(b,c,d);return{clip:this.parseBackgroundClip(a,e,d,c,b),borders:d.map(function(a,f){if(a.width>0){var g=b.left,h=b.top,i=b.width,j=b.height-d[2].width;switch(f){case 0:j=d[0].width,a.args=V({c1:[g,h],c2:[g+i,h],c3:[g+i-d[1].width,h+j],c4:[g+d[3].width,h+j]},c[0],c[1],e.topLeftOuter,e.topLeftInner,e.topRightOuter,e.topRightInner);break;case 1:g=b.left+b.width-d[1].width,i=d[1].width,a.args=V({c1:[g+i,h],c2:[g+i,h+j+d[2].width],c3:[g,h+j],c4:[g,h+d[0].width]},c[1],c[2],e.topRightOuter,e.topRightInner,e.bottomRightOuter,e.bottomRightInner);break;case 2:h=h+b.height-d[2].width,j=d[2].width,a.args=V({c1:[g+i,h+j],c2:[g,h+j],c3:[g+d[3].width,h],c4:[g+i-d[3].width,h]},c[2],c[3],e.bottomRightOuter,e.bottomRightInner,e.bottomLeftOuter,e.bottomLeftInner);break;case 3:i=d[3].width,a.args=V({c1:[g,h+j+d[2].width],c2:[g,h],c3:[g+i,h+d[0].width],c4:[g+i,h+j]},c[3],c[0],e.bottomLeftOuter,e.bottomLeftInner,e.topLeftOuter,e.topLeftInner)}}return a})}},O.prototype.parseBackgroundClip=function(a,b,c,d,e){var f=a.css("backgroundClip"),g=[];switch(f){case"content-box":case"padding-box":W(g,d[0],d[1],b.topLeftInner,b.topRightInner,e.left+c[3].width,e.top+c[0].width),W(g,d[1],d[2],b.topRightInner,b.bottomRightInner,e.left+e.width-c[1].width,e.top+c[0].width),W(g,d[2],d[3],b.bottomRightInner,b.bottomLeftInner,e.left+e.width-c[1].width,e.top+e.height-c[2].width),W(g,d[3],d[0],b.bottomLeftInner,b.topLeftInner,e.left+c[3].width,e.top+e.height-c[2].width);break;default:W(g,d[0],d[1],b.topLeftOuter,b.topRightOuter,e.left,e.top),W(g,d[1],d[2],b.topRightOuter,b.bottomRightOuter,e.left+e.width,e.top),W(g,d[2],d[3],b.bottomRightOuter,b.bottomLeftOuter,e.left+e.width,e.top+e.height),W(g,d[3],d[0],b.bottomLeftOuter,b.topLeftOuter,e.left,e.top+e.height)}return g};var Tb=0,Ub="withCredentials"in new XMLHttpRequest,Vb="crossOrigin"in new Image;Db.prototype.cloneTo=function(a){Db.prototype.cloneTo.call(this,a),a.isPseudoElement=!0,a.before=this.before},Db.prototype=Object.create(F.prototype),Db.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()},Db.prototype.cleanDOM=function(){this.node.parentNode.removeChild(this.node),this.parent.node.className=this.parent.node.className.replace(this.getHideClass(),"")},Db.prototype.getHideClass=function(){return this["PSEUDO_HIDE_ELEMENT_CLASS_"+(this.before?"BEFORE":"AFTER")]},Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE="___html2canvas___pseudoelement_before",Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER="___html2canvas___pseudoelement_after",Eb.prototype.renderImage=function(a,b,c,d){var e=a.cssInt("paddingLeft"),f=a.cssInt("paddingTop"),g=a.cssInt("paddingRight"),h=a.cssInt("paddingBottom"),i=c.borders,j=b.width-(i[1].width+i[3].width+e+g),k=b.height-(i[0].width+i[2].width+f+h);this.drawImage(d,0,0,d.image.width||j,d.image.height||k,b.left+e+i[3].width,b.top+f+i[0].width,j,k)},Eb.prototype.renderBackground=function(a,b,c){b.height>0&&b.width>0&&(this.renderBackgroundColor(a,b),this.renderBackgroundImage(a,b,c))},Eb.prototype.renderBackgroundColor=function(a,b){var c=a.css("backgroundColor");this.isTransparent(c)||this.rectangle(b.left,b.top,b.width,b.height,a.css("backgroundColor"))},Eb.prototype.renderBorders=function(a){a.forEach(this.renderBorder,this)},Eb.prototype.renderBorder=function(a){this.isTransparent(a.color)||null===a.args||this.drawShape(a.args,a.color)},Eb.prototype.renderBackgroundImage=function(a,b,c){var d=a.parseBackgroundImages();d.reverse().forEach(function(d,e,f){switch(d.method){case"url":var g=this.images.get(d.args[0]);g?this.renderBackgroundRepeating(a,b,g,f.length-(e+1),c):E("Error loading background-image",d.args[0]);break;case"linear-gradient":case"gradient":var h=this.images.get(d.value);h?this.renderBackgroundGradient(h,b,c):E("Error loading background-image",d.args[0]);break;case"none":break;default:E("Unknown background-image type",d.args[0])}},this)},Eb.prototype.renderBackgroundRepeating=function(a,b,c,d,e){var f=a.parseBackgroundSize(b,c.image,d),g=a.parseBackgroundPosition(b,c.image,d,f),h=a.parseBackgroundRepeat(d);switch(h){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+e[3],b.top+g.top+e[0],99999,f.height,e);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+e[0],f.width,99999,e);break;case"no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+g.top+e[0],f.width,f.height,e);break;default:this.renderBackgroundRepeat(c,g,f,{top:b.top,left:b.left},e[3],e[0])}},Eb.prototype.isTransparent=function(a){return!a||"transparent"===a||"rgba(0, 0, 0, 0)"===a},Fb.prototype=Object.create(F.prototype),Fb.prototype.getParentStack=function(a){var b=this.parent?this.parent.stack:null;return b?b.ownStacking?b:b.getParentStack(a):a.stack},Gb.prototype.testRangeBounds=function(a){var b,c,d,e,f=!1;return a.createRange&&(b=a.createRange(),b.getBoundingClientRect&&(c=a.createElement("boundtest"),c.style.height="123px",c.style.display="block",a.body.appendChild(c),b.selectNode(c),d=b.getBoundingClientRect(),e=d.height,123===e&&(f=!0),a.body.removeChild(c))),f},Gb.prototype.testCORS=function(){return"undefined"!=typeof(new Image).crossOrigin},Gb.prototype.testSVG=function(){var a=new Image,c=b.createElement("canvas"),d=c.getContext("2d");a.src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";try{d.drawImage(a,0,0),c.toDataURL()}catch(e){return!1}return!0},Hb.prototype.hasFabric=function(){return html2canvas.fabric?Promise.resolve():Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg"))},Hb.prototype.inlineFormatting=function(a){return/^data:image\/svg\+xml;base64,/.test(a)?this.decode64(this.removeContentType(a)):this.removeContentType(a)},Hb.prototype.removeContentType=function(a){return a.replace(/^data:image\/svg\+xml(;base64)?,/,"")},Hb.prototype.isInline=function(a){return/^data:image\/svg\+xml/i.test(a)},Hb.prototype.createCanvas=function(a){var b=this;return function(c,d){var e=new html2canvas.fabric.StaticCanvas("c");b.image=e.lowerCanvasEl,e.setWidth(d.width).setHeight(d.height).add(html2canvas.fabric.util.groupSVGElements(c,d)).renderAll(),a(e.lowerCanvasEl)}},Hb.prototype.decode64=function(b){return"function"==typeof a.atob?a.atob(b):Ib(b)},Jb.prototype=Object.create(Hb.prototype),Kb.prototype=Object.create(F.prototype),Kb.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},Kb.prototype.transform=function(a){var b=this.node.data;switch(a){case"lowercase":return b.toLowerCase();case"capitalize":return b.replace(/(^|\s|:|-|\(|\))([a-z])/g,Lb);case"uppercase":return b.toUpperCase();default:return b}},Mb.prototype=Object.create(A.prototype),Ob.prototype=Object.create(Eb.prototype),Ob.prototype.setFillStyle=function(a){return this.ctx.fillStyle=a,this.ctx},Ob.prototype.rectangle=function(a,b,c,d,e){this.setFillStyle(e).fillRect(a,b,c,d)},Ob.prototype.drawShape=function(a,b){this.shape(a),this.setFillStyle(b).fill()},Ob.prototype.taints=function(a){if(null===a.tainted){this.taintCtx.drawImage(a.image,0,0);try{this.taintCtx.getImageData(0,0,1,1),a.tainted=!1}catch(c){this.taintCtx=b.createElement("canvas").getContext("2d"),a.tainted=!0}}return a.tainted},Ob.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){(!this.taints(a)||this.options.allowTaint)&&this.ctx.drawImage(a.image,b,c,d,e,f,g,h,i)},Ob.prototype.clip=function(a,b,c){this.ctx.save(),a.filter(Pb).forEach(function(a){this.shape(a).clip()},this),b.call(c),this.ctx.restore()},Ob.prototype.shape=function(a){return this.ctx.beginPath(),a.forEach(function(a,b){"rect"===a[0]?this.ctx.rect.apply(this.ctx,a.slice(1)):this.ctx[0===b?"moveTo":a[0]+"To"].apply(this.ctx,a.slice(1))},this),this.ctx.closePath(),this.ctx},Ob.prototype.font=function(a,b,c,d,e,f){this.setFillStyle(a).font=[b,c,d,e,f].join(" ").split(",")[0]},Ob.prototype.fontShadow=function(a,b,c,d){this.setVariable("shadowColor",a).setVariable("shadowOffsetY",b).setVariable("shadowOffsetX",c).setVariable("shadowBlur",d)},Ob.prototype.clearShadow=function(){this.setVariable("shadowColor","rgba(0,0,0,0)")},Ob.prototype.setOpacity=function(a){this.ctx.globalAlpha=a},Ob.prototype.setTransform=function(a){this.ctx.translate(a.origin[0],a.origin[1]),this.ctx.transform.apply(this.ctx,a.matrix),this.ctx.translate(-a.origin[0],-a.origin[1])},Ob.prototype.setVariable=function(a,b){return this.variables[a]!==b&&(this.variables[a]=this.ctx[a]=b),this},Ob.prototype.text=function(a,b,c){this.ctx.fillText(a,b,c)},Ob.prototype.backgroundRepeatShape=function(a,b,c,d,e,f,g,h,i){var j=[["line",Math.round(e),Math.round(f)],["line",Math.round(e+g),Math.round(f)],["line",Math.round(e+g),Math.round(h+f)],["line",Math.round(e),Math.round(h+f)]];this.clip([j],function(){this.renderBackgroundRepeat(a,b,c,d,i[3],i[0])},this)},Ob.prototype.renderBackgroundRepeat=function(a,b,c,d,e,f){var g=Math.round(d.left+b.left+e),h=Math.round(d.top+b.top+f);this.setFillStyle(this.ctx.createPattern(this.resizeImage(a,c),"repeat")),this.ctx.translate(g,h),this.ctx.fill(),this.ctx.translate(-g,-h)},Ob.prototype.renderBackgroundGradient=function(a,b){if(a instanceof D){var c=this.ctx.createLinearGradient(b.left+b.width*a.x0,b.top+b.height*a.y0,b.left+b.width*a.x1,b.top+b.height*a.y1);a.colorStops.forEach(function(a){c.addColorStop(a.stop,a.color)}),this.rectangle(b.left,b.top,b.width,b.height,c)}},Ob.prototype.resizeImage=function(a,c){var d=a.image;if(d.width===c.width&&d.height===c.height)return d;var e,f=b.createElement("canvas");return f.width=c.width,f.height=c.height,e=f.getContext("2d"),e.drawImage(d,0,0,d.width,d.height,0,0,c.width,c.height),f}}).call({},window,document);
\ No newline at end of file
diff --git a/dist/html2canvas.svg.js b/dist/html2canvas.svg.js
index f446066..75ce2e0 100644
--- a/dist/html2canvas.svg.js
+++ b/dist/html2canvas.svg.js
@@ -7,17696 +7,5 @@
 
 (function(window, document, exports, undefined){
 
-/* 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({}, window, document, html2canvas);
\ No newline at end of file
diff --git a/dist/html2canvas.svg.min.js b/dist/html2canvas.svg.min.js
index 38bd965..07c61db 100644
--- a/dist/html2canvas.svg.min.js
+++ b/dist/html2canvas.svg.min.js
@@ -4,9 +4,4 @@
 
   Released under MIT License
 */
-(function(window,document,exports,undefined){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 a(a){var b=this.face=a.face;this.glyphs=a.glyphs,this.w=a.w,this.baseSize=parseInt(b["units-per-em"],10),this.family=b["font-family"].toLowerCase(),this.weight=b["font-weight"],this.style=b["font-style"]||"normal",this.viewBox=function(){var a=b.bbox.split(/\s+/),c={minX:parseInt(a[0],10),minY:parseInt(a[1],10),maxX:parseInt(a[2],10),maxY:parseInt(a[3],10)};return c.width=c.maxX-c.minX,c.height=c.maxY-c.minY,c.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},c}(),this.ascent=-parseInt(b.ascent,10),this.descent=-parseInt(b.descent,10),this.height=-this.ascent+this.descent}function b(){var a={},b={oblique:"italic",italic:"oblique"};this.add=function(b){(a[b.style]||(a[b.style]={}))[b.weight]=b},this.get=function(c,d){var e=a[c]||a[b[c]]||a.normal||a.italic||a.oblique;if(!e)return null;if(d={normal:400,bold:700}[d]||parseInt(d,10),e[d])return e[d];var f,g,h={1:1,99:0}[d%100],i=[];h===undefined&&(h=d>400),500==d&&(d=400);for(var j in e)j=parseInt(j,10),(!f||f>j)&&(f=j),(!g||j>g)&&(g=j),i.push(j);return f>d&&(d=f),d>g&&(d=g),i.sort(function(a,b){return(h?a>d&&b>d?b>a:a>b:d>a&&d>b?a>b:b>a)?-1:1}),e[i[0]]}}function c(){function a(a,b){return a.contains?a.contains(b):16&a.compareDocumentPosition(b)}function b(b){var c=b.relatedTarget;c&&!a(this,c)&&d(this)}function c(){d(this)}function d(a){setTimeout(function(){n.replace(a,r.get(a).options,!0)},10)}this.attach=function(a){a.onmouseenter===undefined?(f(a,"mouseover",b),f(a,"mouseout",b)):(f(a,"mouseenter",c),f(a,"mouseleave",c))}}function d(){function a(a){return a.cufid||(a.cufid=++c)}var b={},c=0;this.get=function(c){var d=a(c);return b[d]||(b[d]={})}}function e(a){var b={},c={};this.get=function(c){return b[c]!=undefined?b[c]:a[c]},this.getSize=function(a,b){return c[a]||(c[a]=new p.Size(this.get(a),b))},this.extend=function(a){for(var c in a)b[c]=a[c];return this}}function f(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,function(){return c.call(a,fabric.window.event)})}function g(a,b){var c=r.get(a);return c.options?a:(b.hover&&b.hoverables[a.nodeName.toLowerCase()]&&s.attach(a),c.options=b,a)}function h(a){var b={};return function(c){return b.hasOwnProperty(c)||(b[c]=a.apply(null,arguments)),b[c]}}function i(a,b){b||(b=p.getStyle(a));for(var c,d=p.quotedList(b.get("fontFamily").toLowerCase()),e=0,f=d.length;f>e;++e)if(c=d[e],v[c])return v[c].get(b.get("fontStyle"),b.get("fontWeight"));return null}function j(a){return fabric.document.getElementsByTagName(a)}function k(){for(var a,b={},c=0,d=arguments.length;d>c;++c)for(a in arguments[c])b[a]=arguments[c][a];return b}function l(a,b,c,d,e,f){var g=d.separate;if("none"==g)return u[d.engine].apply(null,arguments);var h,i=fabric.document.createDocumentFragment(),j=b.split(x[g]),k="words"==g;k&&q&&(/^\s/.test(b)&&j.unshift(""),/\s$/.test(b)&&j.push(""));for(var l=0,m=j.length;m>l;++l)h=u[d.engine](a,k?p.textAlign(j[l],c,l,m):j[l],c,d,e,f,m-1>l),h&&i.appendChild(h);return i}function m(a,b){for(var c,d,e,f,h=g(a,b).firstChild;h;h=e){if(e=h.nextSibling,f=!1,1==h.nodeType){if(!h.firstChild)continue;if(!/cufon/.test(h.className)){arguments.callee(h,b);continue}f=!0}if(d||(d=p.getStyle(a).extend(b)),c||(c=i(a,d)),c)if(f)u[b.engine](c,null,d,b,h,a);else{var j=h.data;if("undefined"!=typeof G_vmlCanvasManager&&(j=j.replace(/\r/g,"\n")),""!==j){var k=l(c,j,d,b,h,a);k?h.parentNode.replaceChild(k,h):h.parentNode.removeChild(h)}}}}var n=function(){return n.replace.apply(null,arguments)},o=n.DOM={ready:function(){var a=!1,b={loaded:1,complete:1},c=[],d=function(){if(!a){a=!0;for(var b;b=c.shift();b());}};return fabric.document.addEventListener&&(fabric.document.addEventListener("DOMContentLoaded",d,!1),fabric.window.addEventListener("pageshow",d,!1)),!fabric.window.opera&&fabric.document.readyState&&function(){b[fabric.document.readyState]?d():setTimeout(arguments.callee,10)}(),fabric.document.readyState&&fabric.document.createStyleSheet&&function(){try{fabric.document.body.doScroll("left"),d()}catch(a){setTimeout(arguments.callee,1)}}(),f(fabric.window,"load",d),function(b){arguments.length?a?b():c.push(b):d()}}()},p=n.CSS={Size:function(a,b){this.value=parseFloat(a),this.unit=String(a).match(/[a-z%]*$/)[0]||"px",this.convert=function(a){return a/b*this.value},this.convertFrom=function(a){return a/this.value*b},this.toString=function(){return this.value+this.unit}},getStyle:function(a){return new e(a.style)},quotedList:h(function(a){for(var b,c=[],d=/\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g;b=d.exec(a);)c.push(b[3]||b[1]);return c}),ready:function(){var a=!1,b=[],c=function(){a=!0;for(var c;c=b.shift();c());},d=Object.prototype.propertyIsEnumerable?j("style"):{length:0},e=j("link");return o.ready(function(){for(var a,b=0,f=0,g=e.length;a=e[f],g>f;++f)a.disabled||"stylesheet"!=a.rel.toLowerCase()||++b;fabric.document.styleSheets.length>=d.length+b?c():setTimeout(arguments.callee,10)}),function(c){a?c():b.push(c)}}(),supports:function(a,b){var c=fabric.document.createElement("span").style;return c[a]===undefined?!1:(c[a]=b,c[a]===b)},textAlign:function(a,b,c,d){return"right"==b.get("textAlign")?c>0&&(a=" "+a):d-1>c&&(a+=" "),a},textDecoration:function(a,b){b||(b=this.getStyle(a));for(var c={underline:null,overline:null,"line-through":null},d=a;d.parentNode&&1==d.parentNode.nodeType;){var e=!0;for(var f in c)c[f]||(-1!=b.get("textDecoration").indexOf(f)&&(c[f]=b.get("color")),e=!1);if(e)break;b=this.getStyle(d=d.parentNode)}return c},textShadow:h(function(a){if("none"==a)return null;for(var b,c=[],d={},e=0,f=/(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/gi;b=f.exec(a);)","==b[0]?(c.push(d),d={},e=0):b[1]?d.color=b[1]:d[["offX","offY","blur"][e++]]=b[2];return c.push(d),c}),color:h(function(a){var b={};return b.color=a.replace(/^rgba\((.*?),\s*([\d.]+)\)/,function(a,c,d){return b.opacity=parseFloat(d),"rgb("+c+")"}),b}),textTransform:function(a,b){return a[{uppercase:"toUpperCase",lowercase:"toLowerCase"}[b.get("textTransform")]||"toString"]()}},q=0==" ".split(/\s+/).length,r=new d,s=new c,t=[],u={},v={},w={engine:null,hover:!1,hoverables:{a:!0},printable:!0,selector:fabric.window.Sizzle||fabric.window.jQuery&&function(a){return jQuery(a)}||fabric.window.dojo&&dojo.query||fabric.window.$$&&function(a){return $$(a)}||fabric.window.$&&function(a){return $(a)}||fabric.document.querySelectorAll&&function(a){return fabric.document.querySelectorAll(a)}||j,separate:"words",textShadow:"none"},x={words:/\s+/,characters:""};return n.now=function(){return o.ready(),n},n.refresh=function(){for(var a=t.splice(0,t.length),b=0,c=a.length;c>b;++b)n.replace.apply(null,a[b]);return n},n.registerEngine=function(a,b){return b?(u[a]=b,n.set("engine",a)):n},n.registerFont=function(c){var d=new a(c),e=d.family;return v[e]||(v[e]=new b),v[e].add(d),n.set("fontFamily",'"'+e+'"')},n.replace=function(a,b,c){return b=k(w,b),b.engine?("string"==typeof b.textShadow&&b.textShadow&&(b.textShadow=p.textShadow(b.textShadow)),c||t.push(arguments),(a.nodeType||"string"==typeof a)&&(a=[a]),p.ready(function(){for(var c=0,d=a.length;d>c;++c){var e=a[c];"string"==typeof e?n.replace(b.selector(e),b,!0):m(e,b)}}),n):n},n.replaceElement=function(a,b){return b=k(w,b),"string"==typeof b.textShadow&&b.textShadow&&(b.textShadow=p.textShadow(b.textShadow)),m(a,b)},n.engines=u,n.fonts=v,n.getOptions=function(){return k(w)},n.set=function(a,b){return w[a]=b,n},n}();Cufon.registerEngine("canvas",function(){function a(a,b){var c,d=0,e=0,f=[],g=/([mrvxe])([^a-z]*)/g;a:for(var h=0;c=g.exec(a);++h){var i=c[2].split(",");switch(c[1]){case"v":f[h]={m:"bezierCurveTo",a:[d+~~i[0],e+~~i[1],d+~~i[2],e+~~i[3],d+=~~i[4],e+=~~i[5]]};break;case"r":f[h]={m:"lineTo",a:[d+=~~i[0],e+=~~i[1]]};break;case"m":f[h]={m:"moveTo",a:[d=~~i[0],e=~~i[1]]};break;case"x":f[h]={m:"closePath",a:[]};break;case"e":break a}b[f[h].m].apply(b,f[h].a)}return f}function b(a,b){for(var c=0,d=a.length;d>c;++c){var e=a[c];b[e.m].apply(b,e.a)}}var c=Cufon.CSS.supports("display","inline-block"),d=!c&&("BackCompat"==fabric.document.compatMode||/frameset|transitional/i.test(fabric.document.doctype.publicId)),e=fabric.document.createElement("style");e.type="text/css";var f=fabric.document.createTextNode(".cufon-canvas{text-indent:0}@media screen,projection{.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle"+(d?"":";font-size:1px;line-height:1px")+"}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}"+(c?".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{e.appendChild(f)}catch(g){e.setAttribute("type","text/css"),e.styleSheet.cssText=f.data}return fabric.document.getElementsByTagName("head")[0].appendChild(e),function(d,e,f,g,h){function i(){T.save();var a=0,b=0,c=[{left:0}];g.backgroundColor&&(T.save(),T.fillStyle=g.backgroundColor,T.translate(0,d.ascent),T.fillRect(0,0,A+10,(-d.ascent+d.descent)*D),T.restore()),"right"===g.textAlign?(T.translate(G[b],0),c[0].left=G[b]*U):"center"===g.textAlign&&(T.translate(G[b]/2,0),c[0].left=G[b]/2*U);for(var e=0,f=z.length;f>e;++e)if("\n"!==z[e]){var h=d.glyphs[z[e]]||d.missingGlyph;if(h){var i=Number(h.w||d.w)+n;g.textBackgroundColor&&(T.save(),T.fillStyle=g.textBackgroundColor,T.translate(0,d.ascent),T.fillRect(0,0,i+10,-d.ascent+d.descent),T.restore()),T.translate(i,0),a+=i,e==f-1&&(c[c.length-1].width=a*U,c[c.length-1].height=(-d.ascent+d.descent)*U)}}else{b++;var j=-d.ascent-d.ascent/5*g.lineHeight,k=c[c.length-1],l={left:0};k.width=a*U,k.height=(-d.ascent+d.descent)*U,"right"===g.textAlign?(T.translate(-A,j),T.translate(G[b],0),l.left=G[b]*U):"center"===g.textAlign?(T.translate(-a-G[b-1]/2,j),T.translate(G[b]/2,0),l.left=G[b]/2*U):T.translate(-a,j),c.push(l),a=0}T.restore(),Cufon.textOptions.boundaries=c}function j(c){T.fillStyle=c||Cufon.textOptions.color||f.get("color");var e=0,h=0;"right"===g.textAlign?T.translate(G[h],0):"center"===g.textAlign&&T.translate(G[h]/2,0);for(var i=0,j=z.length;j>i;++i)if("\n"!==z[i]){var k=d.glyphs[z[i]]||d.missingGlyph;if(k){var l=Number(k.w||d.w)+n;W&&(T.save(),T.strokeStyle=T.fillStyle,T.lineWidth+=T.lineWidth,T.beginPath(),W.underline&&(T.moveTo(0,-d.face["underline-position"]+.5),T.lineTo(l,-d.face["underline-position"]+.5)),W.overline&&(T.moveTo(0,d.ascent+.5),T.lineTo(l,d.ascent+.5)),W["line-through"]&&(T.moveTo(0,-d.descent+.5),T.lineTo(l,-d.descent+.5)),T.stroke(),T.restore()),X&&(T.save(),T.transform(1,0,-.25,1,0,0)),T.beginPath(),k.d&&(k.code?b(k.code,T):k.code=a("m"+k.d,T)),T.fill(),g.strokeStyle&&(T.closePath(),T.save(),T.lineWidth=g.strokeWidth,T.strokeStyle=g.strokeStyle,T.stroke(),T.restore()),X&&T.restore(),T.translate(l,0),e+=l}}else{h++;var m=-d.ascent-d.ascent/5*g.lineHeight;"right"===g.textAlign?(T.translate(-A,m),T.translate(G[h],0)):"center"===g.textAlign?(T.translate(-e-G[h-1]/2,m),T.translate(G[h]/2,0)):T.translate(-e,m),e=0}}var k=null===e,l=d.viewBox,m=f.getSize("fontSize",d.baseSize),n=f.get("letterSpacing");n="normal"==n?0:m.convertFrom(parseInt(n,10));var o=0,p=0,q=0,r=0,s=g.textShadow,t=[];if(Cufon.textOptions.shadowOffsets=[],Cufon.textOptions.shadows=null,s){Cufon.textOptions.shadows=s;for(var u=0,v=s.length;v>u;++u){var w=s[u],x=m.convertFrom(parseFloat(w.offX)),y=m.convertFrom(parseFloat(w.offY));t[u]=[x,y]}}for(var z=Cufon.CSS.textTransform(k?h.alt:e,f).split(""),A=0,B=null,C=0,D=1,E=[],u=0,v=z.length;v>u;++u)if("\n"!==z[u]){var F=d.glyphs[z[u]]||d.missingGlyph;F&&(A+=B=Number(F.w||d.w)+n)}else D++,A>C&&(C=A),E.push(A),A=0;E.push(A),A=Math.max(C,A);for(var G=[],u=E.length;u--;)G[u]=A-E[u];if(null===B)return null;p+=l.width-B,r+=l.minX;var H,I;if(k)H=h,I=h.firstChild;else if(H=fabric.document.createElement("span"),H.className="cufon cufon-canvas",H.alt=e,I=fabric.document.createElement("canvas"),H.appendChild(I),g.printable){var J=fabric.document.createElement("span");J.className="cufon-alt",J.appendChild(fabric.document.createTextNode(e)),H.appendChild(J)}var K=H.style,L=I.style||{},M=m.convert(l.height-o+q),N=Math.ceil(M),O=N/M;I.width=Math.ceil(m.convert(A+p-r)*O),I.height=N,o+=l.minY,L.top=Math.round(m.convert(o-d.ascent))+"px",L.left=Math.round(m.convert(r))+"px";var P=Math.ceil(m.convert(A*O)),Q=P+"px",R=m.convert(d.height),S=(g.lineHeight-1)*m.convert(-d.ascent/5)*(D-1);Cufon.textOptions.width=P,Cufon.textOptions.height=R*D+S,Cufon.textOptions.lines=D,Cufon.textOptions.totalLineHeight=S,c?(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/l.height;Cufon.textOptions.fontAscent=d.ascent*U,Cufon.textOptions.boundaries=null;for(var V=Cufon.textOptions.shadowOffsets,u=t.length;u--;)V[u]=[t[u][0]*U,t[u][1]*U];T.save(),T.scale(U,U),T.translate(-r-1/U*I.width/2+(Cufon.fonts[d.family].offsetLeft||0),-o-Cufon.textOptions.height/U/2+(Cufon.fonts[d.family].offsetTop||0)),T.lineWidth=d.face["underline-thickness"],T.save();var W=Cufon.getTextDecoration(g),X="italic"===g.fontStyle;if(T.save(),i(),s)for(var u=0,v=s.length;v>u;++u){var w=s[u];T.save(),T.translate.apply(T,t[u]),j(w.color),T.restore()}return j(),T.restore(),T.restore(),T.restore(),H}}()),Cufon.registerEngine("vml",function(){function a(a,c){return b(a,/(?:em|ex|%)$/i.test(c)?"1em":c)}function b(a,b){if(/px$/i.test(b))return parseFloat(b);var c=a.style.left,d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left,a.style.left=b;var e=a.style.pixelLeft;return a.style.left=c,a.runtimeStyle.left=d,e}if(fabric.document.namespaces){var c=fabric.document.createElement("canvas");if(!(c&&c.getContext&&c.getContext.apply)){null==fabric.document.namespaces.cvml&&fabric.document.namespaces.add("cvml","urn:schemas-microsoft-com:vml");var d=fabric.document.createElement("cvml:shape");if(d.style.behavior="url(#default#VML)",d.coordsize)return d=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(c,d,e,f,g,h,i){var j=null===d;j&&(d=g.alt);var k=c.viewBox,l=e.computedFontSize||(e.computedFontSize=new Cufon.CSS.Size(a(h,e.get("fontSize"))+"px",c.baseSize)),m=e.computedLSpacing;m==undefined&&(m=e.get("letterSpacing"),e.computedLSpacing=m="normal"==m?0:~~l.convertFrom(b(h,m)));var n,o;if(j)n=g,o=g.firstChild;else{if(n=fabric.document.createElement("span"),n.className="cufon cufon-vml",n.alt=d,o=fabric.document.createElement("span"),o.className="cufon-vml-canvas",n.appendChild(o),f.printable){var p=fabric.document.createElement("span");p.className="cufon-alt",p.appendChild(fabric.document.createTextNode(d)),n.appendChild(p)}i||n.appendChild(fabric.document.createElement("cvml:shape"))}var q=n.style,r=o.style,s=l.convert(k.height),t=Math.ceil(s),u=t/s,v=k.minX,w=k.minY;r.height=t,r.top=Math.round(l.convert(w-c.ascent)),r.left=Math.round(l.convert(v)),q.height=l.convert(c.height)+"px";for(var x,y,z=(Cufon.getTextDecoration(f),e.get("color")),A=Cufon.CSS.textTransform(d,e).split(""),B=0,C=0,D=null,E=f.textShadow,F=0,G=0,H=A.length;H>F;++F)x=c.glyphs[A[F]]||c.missingGlyph,x&&(B+=D=~~(x.w||c.w)+m);if(null===D)return null;var I,J=-v+B+(k.width-D),K=l.convert(J*u),L=Math.round(K),M=J+","+k.height,N="r"+M+"nsnf";for(F=0;H>F;++F)if(x=c.glyphs[A[F]]||c.missingGlyph){j?(y=o.childNodes[G],y.firstChild&&y.removeChild(y.firstChild)):(y=fabric.document.createElement("cvml:shape"),o.appendChild(y)),y.stroked="f",y.coordsize=M,y.coordorigin=I=v-C+","+w,y.path=(x.d?"m"+x.d+"xe":"")+"m"+I+N,y.fillcolor=z;var O=y.style;if(O.width=L,O.height=t,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,y.appendChild(T)}C+=~~(x.w||c.w)+m,++G}return q.width=Math.max(Math.ceil(l.convert(B*u)),0),n}}}}()),Cufon.getTextDecoration=function(a){return{underline:"underline"===a.textDecoration,overline:"overline"===a.textDecoration,"line-through":"line-through"===a.textDecoration}},"undefined"!=typeof exports&&(exports.Cufon=Cufon),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"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(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"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(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.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 a(a,b){this.__eventListeners[a]&&(b?fabric.util.removeFromArray(this.__eventListeners[a],b):this.__eventListeners[a].length=0)}function b(a,b){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var c in a)this.on(c,a[c]);else this.__eventListeners[a]||(this.__eventListeners[a]=[]),this.__eventListeners[a].push(b);return this}function c(b,c){if(this.__eventListeners){if(0===arguments.length)this.__eventListeners={};else if(1===arguments.length&&"object"==typeof arguments[0])for(var d in b)a.call(this,d,b[d]);else a.call(this,b,c);return this}}function d(a,b){if(this.__eventListeners){var c=this.__eventListeners[a];if(c){for(var d=0,e=c.length;e>d;d++)c[d].call(this,b||{});return this}}}fabric.Observable={observe:b,stopObserving:c,fire:d,on:b,off:c,trigger:d}}(),fabric.Collection={add:function(){this._objects.push.apply(this._objects,arguments);for(var a=0,b=arguments.length;b>a;a++)this._onObjectAdded(arguments[a]);return this.renderOnAddRemove&&this.renderAll(),this},insertAt:function(a,b,c){var d=this.getObjects();return c?d[b]=a:d.splice(b,0,a),this._onObjectAdded(a),this.renderOnAddRemove&&this.renderAll(),this},remove:function(){for(var a,b=this.getObjects(),c=0,d=arguments.length;d>c;c++)a=b.indexOf(arguments[c]),-1!==a&&(b.splice(a,1),this._onObjectRemoved(arguments[c]));return this.renderOnAddRemove&&this.renderAll(),this},forEachObject:function(a,b){for(var c=this.getObjects(),d=c.length;d--;)a.call(b,c[d],d,c);return this},getObjects:function(a){return"undefined"==typeof a?this._objects:this._objects.filter(function(b){return b.type===a})},item:function(a){return this.getObjects()[a]},isEmpty:function(){return 0===this.getObjects().length},size:function(){return this.getObjects().length},contains:function(a){return this.getObjects().indexOf(a)>-1},complexity:function(){return this.getObjects().reduce(function(a,b){return a+=b.complexity?b.complexity():0},0)}},function(a){var b=Math.sqrt,c=Math.atan2,d=Math.PI/180;fabric.util={removeFromArray:function(a,b){var c=a.indexOf(b);return-1!==c&&a.splice(c,1),a},getRandomInt:function(a,b){return Math.floor(Math.random()*(b-a+1))+a},degreesToRadians:function(a){return a*d},radiansToDegrees:function(a){return a/d},rotatePoint:function(a,b,c){var d=Math.sin(c),e=Math.cos(c);a.subtractEquals(b);var f=a.x*e-a.y*d,g=a.x*d+a.y*e;return new fabric.Point(f,g).addEquals(b)},transformPoint:function(a,b,c){return c?new fabric.Point(b[0]*a.x+b[1]*a.y,b[2]*a.x+b[3]*a.y):new fabric.Point(b[0]*a.x+b[1]*a.y+b[4],b[2]*a.x+b[3]*a.y+b[5])},invertTransform:function(a){var b=a.slice(),c=1/(a[0]*a[3]-a[1]*a[2]);b=[c*a[3],-c*a[1],-c*a[2],c*a[0],0,0];var d=fabric.util.transformPoint({x:a[4],y:a[5]},b);return b[4]=-d.x,b[5]=-d.y,b},toFixed:function(a,b){return parseFloat(Number(a).toFixed(b))},parseUnit:function(a){var b=/\D{0,2}$/.exec(a),c=parseFloat(a);switch(b[0]){case"mm":return c*fabric.DPI/25.4;case"cm":return c*fabric.DPI/2.54;case"in":return c*fabric.DPI;case"pt":return c*fabric.DPI/72;case"pc":return c*fabric.DPI/72*12;default:return c}},falseFunction:function(){return!1},getKlass:function(a,b){return a=fabric.util.string.camelize(a.charAt(0).toUpperCase()+a.slice(1)),fabric.util.resolveNamespace(b)[a]},resolveNamespace:function(b){if(!b)return fabric;for(var c=b.split("."),d=c.length,e=a||fabric.window,f=0;d>f;++f)e=e[c[f]];return e},loadImage:function(a,b,c,d){if(!a)return void(b&&b.call(c,a));var e=fabric.util.createImage();e.onload=function(){b&&b.call(c,e),e=e.onload=e.onerror=null},e.onerror=function(){fabric.log("Error loading "+e.src),b&&b.call(c,null,!0),e=e.onload=e.onerror=null},0!==a.indexOf("data")&&"undefined"!=typeof d&&(e.crossOrigin=d),e.src=a},enlivenObjects:function(a,b,c,d){function e(){++g===h&&b&&b(f)}a=a||[];var f=[],g=0,h=a.length;return h?void a.forEach(function(a,b){if(!a||!a.type)return void e();var g=fabric.util.getKlass(a.type,c);g.async?g.fromObject(a,function(c,g){g||(f[b]=c,d&&d(a,f[b])),e()}):(f[b]=g.fromObject(a),d&&d(a,f[b]),e())}):void(b&&b(f))},groupSVGElements:function(a,b,c){var d;return d=new fabric.PathGroup(a,b),"undefined"!=typeof c&&d.setSourcePath(c),d},populateWithProperties:function(a,b,c){if(c&&"[object Array]"===Object.prototype.toString.call(c))for(var d=0,e=c.length;e>d;d++)c[d]in a&&(b[c[d]]=a[c[d]])},drawDashedLine:function(a,d,e,f,g,h){var i=f-d,j=g-e,k=b(i*i+j*j),l=c(j,i),m=h.length,n=0,o=!0;for(a.save(),a.translate(d,e),a.moveTo(0,0),a.rotate(l),d=0;k>d;)d+=h[n++%m],d>k&&(d=k),a[o?"lineTo":"moveTo"](d,0),o=!o;a.restore()},createCanvasElement:function(a){return a||(a=fabric.document.createElement("canvas")),a.getContext||"undefined"==typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(a),a},createImage:function(){return fabric.isLikelyNode?new(require("canvas").Image):fabric.document.createElement("img")},createAccessors:function(a){for(var b=a.prototype,c=b.stateProperties.length;c--;){var d=b.stateProperties[c],e=d.charAt(0).toUpperCase()+d.slice(1),f="set"+e,g="get"+e;b[g]||(b[g]=function(a){return new Function('return this.get("'+a+'")')}(d)),b[f]||(b[f]=function(a){return new Function("value",'return this.set("'+a+'", value)')}(d))}},clipContext:function(a,b){b.save(),b.beginPath(),a.clipTo(b),b.clip()},multiplyTransformMatrices:function(a,b){for(var c=[[a[0],a[2],a[4]],[a[1],a[3],a[5]],[0,0,1]],d=[[b[0],b[2],b[4]],[b[1],b[3],b[5]],[0,0,1]],e=[],f=0;3>f;f++){e[f]=[];for(var g=0;3>g;g++){for(var h=0,i=0;3>i;i++)h+=c[f][i]*d[i][g];e[f][g]=h}}return[e[0][0],e[1][0],e[0][1],e[1][1],e[0][2],e[1][2]]},getFunctionBody:function(a){return(String(a).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},isTransparent:function(a,b,c,d){d>0&&(b>d?b-=d:b=0,c>d?c-=d:c=0);for(var e=!0,f=a.getImageData(b,c,2*d||1,2*d||1),g=3,h=f.data.length;h>g;g+=4){var i=f.data[g];if(e=0>=i,e===!1)break}return f=null,e}}}("undefined"!=typeof exports?exports:this),function(){function a(a,e,g,h,i,j,k){var l=f.call(arguments);if(d[l])return d[l];var m=Math.PI,n=k*(m/180),o=Math.sin(n),p=Math.cos(n),q=0,r=0;g=Math.abs(g),h=Math.abs(h);var s=-p*a-o*e,t=-p*e+o*a,u=g*g,v=h*h,w=t*t,x=s*s,y=4*u*v-u*w-v*x,z=0;if(0>y){var A=Math.sqrt(1-.25*y/(u*v));g*=A,h*=A}else z=(i===j?-.5:.5)*Math.sqrt(y/(u*w+v*x));var B=z*g*t/h,C=-z*h*s/g,D=p*B-o*C+a/2,E=o*B+p*C+e/2,F=c(1,0,(s-B)/g,(t-C)/h),G=c((s-B)/g,(t-C)/h,(-s-B)/g,(-t-C)/h);0===j&&G>0?G-=2*m:1===j&&0>G&&(G+=2*m);for(var H=Math.ceil(Math.abs(G/(.5*m))),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]=b(F,L,p,o,g,h,D,E,K,q,r),q=I[M][4],r=I[M][5],F+=J,L+=J;return d[l]=I,I}function b(a,b,c,d,g,h,i,j,k,l,m){var n=f.call(arguments);if(e[n])return e[n];var o=Math.cos(a),p=Math.sin(a),q=Math.cos(b),r=Math.sin(b),s=c*g*q-d*h*r+i,t=d*g*q+c*h*r+j,u=l+k*(-c*g*p-d*h*o),v=m+k*(-d*g*p+c*h*o),w=s+k*(c*g*r+d*h*q),x=t+k*(d*g*r-c*h*q);return e[n]=[u,v,w,x,s,t],e[n]}function c(a,b,c,d){var e=Math.atan2(b,a),f=Math.atan2(d,c);return f>=e?f-e:2*Math.PI-(e-f)}var d={},e={},f=Array.prototype.join;fabric.util.drawArc=function(b,c,d,e){for(var f=e[0],g=e[1],h=e[2],i=e[3],j=e[4],k=e[5],l=e[6],m=[[],[],[],[]],n=a(k-c,l-d,f,g,i,j,h),o=0,p=n.length;p>o;o++)m[o][0]=n[o][0]+c,m[o][1]=n[o][1]+d,m[o][2]=n[o][2]+c,m[o][3]=n[o][3]+d,m[o][4]=n[o][4]+c,m[o][5]=n[o][5]+d,b.bezierCurveTo.apply(b,m[o])}}(),function(){function a(a,b){for(var c=e.call(arguments,2),d=[],f=0,g=a.length;g>f;f++)d[f]=c.length?a[f][b].apply(a[f],c):a[f][b].call(a[f]);return d}function b(a,b){return d(a,b,function(a,b){return a>=b})}function c(a,b){return d(a,b,function(a,b){return b>a})}function d(a,b,c){if(a&&0!==a.length){var d=a.length-1,e=b?a[d][b]:a[d];if(b)for(;d--;)c(a[d][b],e)&&(e=a[d][b]);else for(;d--;)c(a[d],e)&&(e=a[d]);return e}}var e=Array.prototype.slice;fabric.util.array={invoke:a,min:c,max:b}}(),function(){function a(a,b){for(var c in b)a[c]=b[c];return a}function b(b){return a({},b)}fabric.util.object={extend:a,clone:b}}(),function(){function a(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})}function b(a,b){return a.charAt(0).toUpperCase()+(b?a.slice(1):a.slice(1).toLowerCase())}function c(a){return a.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}fabric.util.string={camelize:a,capitalize:b,escapeXml:c}}(),function(){function a(){}function b(a){var b=this.constructor.superclass.prototype[a];return arguments.length>1?b.apply(this,d.call(arguments,1)):b.call(this)}function c(){function c(){this.initialize.apply(this,arguments)}var f=null,h=d.call(arguments,0);"function"==typeof h[0]&&(f=h.shift()),c.superclass=f,c.subclasses=[],f&&(a.prototype=f.prototype,c.prototype=new a,f.subclasses.push(c));for(var i=0,j=h.length;j>i;i++)g(c,h[i],f);return c.prototype.initialize||(c.prototype.initialize=e),c.prototype.constructor=c,c.prototype.callSuper=b,c}var d=Array.prototype.slice,e=function(){},f=function(){for(var a in{toString:1})if("toString"===a)return!1;return!0}(),g=function(a,b,c){for(var d in b)a.prototype[d]=d in a.prototype&&"function"==typeof a.prototype[d]&&(b[d]+"").indexOf("callSuper")>-1?function(a){return function(){var d=this.constructor.superclass;this.constructor.superclass=c;var e=b[a].apply(this,arguments);return this.constructor.superclass=d,"initialize"!==a?e:void 0}}(d):b[d],f&&(b.toString!==Object.prototype.toString&&(a.prototype.toString=b.toString),b.valueOf!==Object.prototype.valueOf&&(a.prototype.valueOf=b.valueOf))};fabric.util.createClass=c}(),function(){function a(a){var b,c,d=Array.prototype.slice.call(arguments,1),e=d.length;for(c=0;e>c;c++)if(b=typeof a[d[c]],!/^(?:function|object|unknown)$/.test(b))return!1;return!0}function b(a,b){return{handler:b,wrappedHandler:c(a,b)}}function c(a,b){return function(c){b.call(g(a),c||fabric.window.event)}}function d(a,b){return function(c){if(p[a]&&p[a][b])for(var d=p[a][b],e=0,f=d.length;f>e;e++)d[e].call(this,c||fabric.window.event)}}function e(a,b){a||(a=fabric.window.event);var c=a.target||(typeof a.srcElement!==i?a.srcElement:null),d=fabric.util.getScrollLeftTop(c,b);return{x:q(a)+d.left,y:r(a)+d.top}}function f(a,b,c){var d="touchend"===a.type?"changedTouches":"touches";return a[d]&&a[d][0]?a[d][0][b]-(a[d][0][b]-a[d][0][c])||a[c]:a[c]}var g,h,i="unknown",j=function(){var a=0;return function(b){return b.__uniqueID||(b.__uniqueID="uniqueID__"+a++)}}();!function(){var a={};g=function(b){return a[b]},h=function(b,c){a[b]=c}}();var k,l,m=a(fabric.document.documentElement,"addEventListener","removeEventListener")&&a(fabric.window,"addEventListener","removeEventListener"),n=a(fabric.document.documentElement,"attachEvent","detachEvent")&&a(fabric.window,"attachEvent","detachEvent"),o={},p={};m?(k=function(a,b,c){a.addEventListener(b,c,!1)},l=function(a,b,c){a.removeEventListener(b,c,!1)}):n?(k=function(a,c,d){var e=j(a);h(e,a),o[e]||(o[e]={}),o[e][c]||(o[e][c]=[]);var f=b(e,d);o[e][c].push(f),a.attachEvent("on"+c,f.wrappedHandler)},l=function(a,b,c){var d,e=j(a);if(o[e]&&o[e][b])for(var f=0,g=o[e][b].length;g>f;f++)d=o[e][b][f],d&&d.handler===c&&(a.detachEvent("on"+b,d.wrappedHandler),o[e][b][f]=null)}):(k=function(a,b,c){var e=j(a);if(p[e]||(p[e]={}),!p[e][b]){p[e][b]=[];var f=a["on"+b];f&&p[e][b].push(f),a["on"+b]=d(e,b)}p[e][b].push(c)},l=function(a,b,c){var d=j(a);if(p[d]&&p[d][b])for(var e=p[d][b],f=0,g=e.length;g>f;f++)e[f]===c&&e.splice(f,1)}),fabric.util.addListener=k,fabric.util.removeListener=l;var q=function(a){return typeof a.clientX!==i?a.clientX:0},r=function(a){return typeof a.clientY!==i?a.clientY:0};fabric.isTouchSupported&&(q=function(a){return f(a,"pageX","clientX")},r=function(a){return f(a,"pageY","clientY")
-}),fabric.util.getPointer=e,fabric.util.object.extend(fabric.util,fabric.Observable)}(),function(){function a(a,b){var c=a.style;if(!c)return a;if("string"==typeof b)return a.style.cssText+=";"+b,b.indexOf("opacity")>-1?f(a,b.match(/opacity:\s*(\d?\.?\d*)/)[1]):a;for(var d in b)if("opacity"===d)f(a,b[d]);else{var e="float"===d||"cssFloat"===d?"undefined"==typeof c.styleFloat?"cssFloat":"styleFloat":d;c[e]=b[d]}return a}var b=fabric.document.createElement("div"),c="string"==typeof b.style.opacity,d="string"==typeof b.style.filter,e=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,f=function(a){return a};c?f=function(a,b){return a.style.opacity=b,a}:d&&(f=function(a,b){var c=a.style;return a.currentStyle&&!a.currentStyle.hasLayout&&(c.zoom=1),e.test(c.filter)?(b=b>=.9999?"":"alpha(opacity="+100*b+")",c.filter=c.filter.replace(e,b)):c.filter+=" alpha(opacity="+100*b+")",a}),fabric.util.setStyle=a}(),function(){function a(a){return"string"==typeof a?fabric.document.getElementById(a):a}function b(a,b){var c=fabric.document.createElement(a);for(var d in b)"class"===d?c.className=b[d]:"for"===d?c.htmlFor=b[d]:c.setAttribute(d,b[d]);return c}function c(a,b){a&&-1===(" "+a.className+" ").indexOf(" "+b+" ")&&(a.className+=(a.className?" ":"")+b)}function d(a,c,d){return"string"==typeof c&&(c=b(c,d)),a.parentNode&&a.parentNode.replaceChild(c,a),c.appendChild(a),c}function e(a,b){var c,d,e=0,f=0,g=fabric.document.documentElement,h=fabric.document.body||{scrollLeft:0,scrollTop:0};for(d=a;a&&a.parentNode&&!c;)a=a.parentNode,a!==fabric.document&&"fixed"===fabric.util.getElementStyle(a,"position")&&(c=a),a!==fabric.document&&d!==b&&"absolute"===fabric.util.getElementStyle(a,"position")?(e=0,f=0):a===fabric.document?(e=h.scrollLeft||g.scrollLeft||0,f=h.scrollTop||g.scrollTop||0):(e+=a.scrollLeft||0,f+=a.scrollTop||0);return{left:e,top:f}}function f(a){var b,c,d=a&&a.ownerDocument,e={left:0,top:0},f={left:0,top:0},g={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!d)return{left:0,top:0};for(var h in g)f[g[h]]+=parseInt(k(a,h),10)||0;return b=d.documentElement,"undefined"!=typeof a.getBoundingClientRect&&(e=a.getBoundingClientRect()),c=fabric.util.getScrollLeftTop(a,null),{left:e.left+c.left-(b.clientLeft||0)+f.left,top:e.top+c.top-(b.clientTop||0)+f.top}}var g,h=Array.prototype.slice,i=function(a){return h.call(a,0)};try{g=i(fabric.document.childNodes)instanceof Array}catch(j){}g||(i=function(a){for(var b=new Array(a.length),c=a.length;c--;)b[c]=a[c];return b});var k;k=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(a,b){return fabric.document.defaultView.getComputedStyle(a,null)[b]}:function(a,b){var c=a.style[b];return!c&&a.currentStyle&&(c=a.currentStyle[b]),c},function(){function a(a){return"undefined"!=typeof a.onselectstart&&(a.onselectstart=fabric.util.falseFunction),d?a.style[d]="none":"string"==typeof a.unselectable&&(a.unselectable="on"),a}function b(a){return"undefined"!=typeof a.onselectstart&&(a.onselectstart=null),d?a.style[d]="":"string"==typeof a.unselectable&&(a.unselectable=""),a}var c=fabric.document.documentElement.style,d="userSelect"in c?"userSelect":"MozUserSelect"in c?"MozUserSelect":"WebkitUserSelect"in c?"WebkitUserSelect":"KhtmlUserSelect"in c?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=a,fabric.util.makeElementSelectable=b}(),function(){function a(a,b){var c=fabric.document.getElementsByTagName("head")[0],d=fabric.document.createElement("script"),e=!0;d.onload=d.onreadystatechange=function(a){if(e){if("string"==typeof this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)return;e=!1,b(a||fabric.window.event),d=d.onload=d.onreadystatechange=null}},d.src=a,c.appendChild(d)}fabric.util.getScript=a}(),fabric.util.getById=a,fabric.util.toArray=i,fabric.util.makeElement=b,fabric.util.addClass=c,fabric.util.wrapElement=d,fabric.util.getScrollLeftTop=e,fabric.util.getElementOffset=f,fabric.util.getElementStyle=k}(),function(){function a(a,b){return a+(/\?/.test(a)?"&":"?")+b}function b(){}function c(c,e){e||(e={});var f,g=e.method?e.method.toUpperCase():"GET",h=e.onComplete||function(){},i=d();return i.onreadystatechange=function(){4===i.readyState&&(h(i),i.onreadystatechange=b)},"GET"===g&&(f=null,"string"==typeof e.parameters&&(c=a(c,e.parameters))),i.open(g,c,!0),("POST"===g||"PUT"===g)&&i.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),i.send(f),i}var d=function(){for(var a=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}],b=a.length;b--;)try{var c=a[b]();if(c)return a[b]}catch(d){}}();fabric.util.request=c}(),fabric.log=function(){},fabric.warn=function(){},"undefined"!=typeof console&&["log","warn"].forEach(function(a){"undefined"!=typeof console[a]&&console[a].apply&&(fabric[a]=function(){return console[a].apply(console,arguments)})}),function(a){"use strict";function b(a){return a in w?w[a]:a}function c(a,b,c){var d,e="[object Array]"===Object.prototype.toString.call(b);return"fill"!==a&&"stroke"!==a||"none"!==b?"fillRule"===a?b="evenodd"===b?"destination-over":b:"strokeDashArray"===a?b=b.replace(/,/g," ").split(/\s+/).map(function(a){return parseInt(a)}):"transformMatrix"===a?b=c&&c.transformMatrix?v(c.transformMatrix,p.parseTransformAttribute(b)):p.parseTransformAttribute(b):"visible"===a?(b="none"===b||"hidden"===b?!1:!0,c&&c.visible===!1&&(b=!1)):"originX"===a?b="start"===b?"left":"end"===b?"right":"center":d=e?b.map(u):u(b):b="",!e&&isNaN(d)?b:d}function d(a){for(var b in x)if(a[b]&&"undefined"!=typeof a[x[b]]&&0!==a[b].indexOf("url(")){var c=new p.Color(a[b]);a[b]=c.setAlpha(t(c.getAlpha()*a[x[b]],2)).toRgba()}return a}function e(a,b){var c=a.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(c){var d=c[1],e=c[3],f=c[4],g=c[5],h=c[6];d&&(b.fontStyle=d),e&&(b.fontWeight=isNaN(parseFloat(e))?e:parseFloat(e)),f&&(b.fontSize=parseFloat(f)),h&&(b.fontFamily=h),g&&(b.lineHeight="normal"===g?1:g)}}function f(a,d){var f,g;a.replace(/;$/,"").split(";").forEach(function(a){var h=a.split(":");f=b(h[0].trim().toLowerCase()),g=c(f,h[1].trim()),"font"===f?e(g,d):d[f]=g})}function g(a,d){var f,g;for(var h in a)"undefined"!=typeof a[h]&&(f=b(h.toLowerCase()),g=c(f,a[h]),"font"===f?e(g,d):d[f]=g)}function h(a){var b={};for(var c in p.cssRules)if(i(a,c.split(" ")))for(var d in p.cssRules[c])b[d]=p.cssRules[c][d];return b}function i(a,b){var c,d=!0;return c=k(a,b.pop()),c&&b.length&&(d=j(a,b)),c&&d&&0===b.length}function j(a,b){for(var c,d=!0;a.parentNode&&1===a.parentNode.nodeType&&b.length;)d&&(c=b.pop()),a=a.parentNode,d=k(a,c);return 0===b.length}function k(a,b){var c,d=a.nodeName,e=a.getAttribute("class"),f=a.getAttribute("id");if(c=new RegExp("^"+d,"i"),b=b.replace(c,""),f&&b.length&&(c=new RegExp("#"+f+"(?![a-zA-Z\\-]+)","i"),b=b.replace(c,"")),e&&b.length){e=e.split(" ");for(var g=e.length;g--;)c=new RegExp("\\."+e[g]+"(?![a-zA-Z\\-]+)","i"),b=b.replace(c,"")}return 0===b.length}function l(a){for(var b=a.getElementsByTagName("use");b.length;){for(var c,d=b[0],e=d.getAttribute("xlink:href").substr(1),f=d.getAttribute("x")||0,g=d.getAttribute("y")||0,h=a.getElementById(e).cloneNode(!0),i=(d.getAttribute("transform")||"")+" translate("+f+", "+g+")",j=0,k=d.attributes,l=k.length;l>j;j++){var m=k.item(j);"x"!==m.nodeName&&"y"!==m.nodeName&&"xlink:href"!==m.nodeName&&("transform"===m.nodeName?i=i+" "+m.nodeValue:h.setAttribute(m.nodeName,m.nodeValue))}h.setAttribute("transform",i),h.removeAttribute("id"),c=d.parentNode,c.replaceChild(h,d)}}function m(a,b){if(b[3]=b[0]=b[0]>b[3]?b[3]:b[0],1!==b[0]||1!==b[3]||0!==b[4]||0!==b[5]){for(var c=a.ownerDocument.createElement("g");null!=a.firstChild;)c.appendChild(a.firstChild);c.setAttribute("transform","matrix("+b[0]+" "+b[1]+" "+b[2]+" "+b[3]+" "+b[4]+" "+b[5]+")"),a.appendChild(c)}}function n(a){var b=a.objects,c=a.options;return b=b.map(function(a){return p[r(a.type)].fromObject(a)}),{objects:b,options:c}}function o(a,b,c){b[c]&&b[c].toSVG&&a.push('<pattern x="0" y="0" id="',c,'Pattern" ','width="',b[c].source.width,'" height="',b[c].source.height,'" patternUnits="userSpaceOnUse">','<image x="0" y="0" ','width="',b[c].source.width,'" height="',b[c].source.height,'" xlink:href="',b[c].source.src,'"></image></pattern>')}var p=a.fabric||(a.fabric={}),q=p.util.object.extend,r=p.util.string.capitalize,s=p.util.object.clone,t=p.util.toFixed,u=p.util.parseUnit,v=p.util.multiplyTransformMatrices,w={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"},x={stroke:"strokeOpacity",fill:"fillOpacity"};p.parseTransformAttribute=function(){function a(a,b){var c=b[0];a[0]=Math.cos(c),a[1]=Math.sin(c),a[2]=-Math.sin(c),a[3]=Math.cos(c)}function b(a,b){var c=b[0],d=2===b.length?b[1]:b[0];a[0]=c,a[3]=d}function c(a,b){a[2]=b[0]}function d(a,b){a[1]=b[0]}function e(a,b){a[4]=b[0],2===b.length&&(a[5]=b[1])}var f=[1,0,0,1,0,0],g="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",h="(?:\\s+,?\\s*|,\\s*)",i="(?:(skewX)\\s*\\(\\s*("+g+")\\s*\\))",j="(?:(skewY)\\s*\\(\\s*("+g+")\\s*\\))",k="(?:(rotate)\\s*\\(\\s*("+g+")(?:"+h+"("+g+")"+h+"("+g+"))?\\s*\\))",l="(?:(scale)\\s*\\(\\s*("+g+")(?:"+h+"("+g+"))?\\s*\\))",m="(?:(translate)\\s*\\(\\s*("+g+")(?:"+h+"("+g+"))?\\s*\\))",n="(?:(matrix)\\s*\\(\\s*("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")\\s*\\))",o="(?:"+n+"|"+m+"|"+l+"|"+k+"|"+i+"|"+j+")",q="(?:"+o+"(?:"+h+o+")*)",r="^\\s*(?:"+q+"?)\\s*$",s=new RegExp(r),t=new RegExp(o,"g");return function(g){var h=f.concat(),i=[];if(!g||g&&!s.test(g))return h;g.replace(t,function(g){var j=new RegExp(o).exec(g).filter(function(a){return""!==a&&null!=a}),k=j[1],l=j.slice(2).map(parseFloat);switch(k){case"translate":e(h,l);break;case"rotate":l[0]=p.util.degreesToRadians(l[0]),a(h,l);break;case"scale":b(h,l);break;case"skewX":c(h,l);break;case"skewY":d(h,l);break;case"matrix":h=l}i.push(h.concat()),h=f.concat()});for(var j=i[0];i.length>1;)i.shift(),j=p.util.multiplyTransformMatrices(j,i[0]);return j}}(),p.parseSVGDocument=function(){function a(a,b){for(;a&&(a=a.parentNode);)if(b.test(a.nodeName))return!0;return!1}var b=/^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,c="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",d=new RegExp("^\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*$");return function(c,e,f){if(c){var g=new Date;l(c);var h,i,j=c.getAttribute("viewBox"),k=u(c.getAttribute("width")||"100%"),n=u(c.getAttribute("height")||"100%");if(j&&(j=j.match(d))){var o=parseFloat(j[1]),q=parseFloat(j[2]),r=1,t=1;h=parseFloat(j[3]),i=parseFloat(j[4]),k&&k!==h&&(r=k/h),n&&n!==i&&(t=n/i),m(c,[r,0,0,t,r*-o,t*-q])}var v=p.util.toArray(c.getElementsByTagName("*"));if(0===v.length&&p.isLikelyNode){v=c.selectNodes('//*[name(.)!="svg"]');for(var w=[],x=0,y=v.length;y>x;x++)w[x]=v[x];v=w}var z=v.filter(function(c){return b.test(c.tagName)&&!a(c,/^(?:pattern|defs)$/)});if(!z||z&&!z.length)return void(e&&e([],{}));var A={width:k?k:h,height:n?n:i,widthAttr:k,heightAttr:n};p.gradientDefs=p.getGradientDefs(c),p.cssRules=p.getCSSRules(c),p.parseElements(z,function(a){p.documentParsingTime=new Date-g,e&&e(a,A)},s(A),f)}}}();var y={has:function(a,b){b(!1)},get:function(){},set:function(){}};q(p,{getGradientDefs:function(a){var b,c,d,e,f=a.getElementsByTagName("linearGradient"),g=a.getElementsByTagName("radialGradient"),h=0,i=[],j={},k={};for(i.length=f.length+g.length,c=f.length;c--;)i[h++]=f[c];for(c=g.length;c--;)i[h++]=g[c];for(;h--;)b=i[h],e=b.getAttribute("xlink:href"),d=b.getAttribute("id"),e&&(k[d]=e.substr(1)),j[d]=b;for(d in k){var l=j[k[d]].cloneNode(!0);for(b=j[d];l.firstChild;)b.appendChild(l.firstChild)}return j},parseAttributes:function(a,e){if(a){var f,g={};a.parentNode&&/^symbol|[g|a]$/i.test(a.parentNode.nodeName)&&(g=p.parseAttributes(a.parentNode,e));var i=e.reduce(function(d,e){return f=a.getAttribute(e),f&&(e=b(e),f=c(e,f,g),d[e]=f),d},{});return i=q(i,q(h(a),p.parseStyleAttribute(a))),d(q(g,i))}},parseElements:function(a,b,c,d){new p.ElementsParser(a,b,c,d).parse()},parseStyleAttribute:function(a){var b={},c=a.getAttribute("style");return c?("string"==typeof c?f(c,b):g(c,b),b):b},parsePointsAttribute:function(a){if(!a)return null;a=a.replace(/,/g," ").trim(),a=a.split(/\s+/);var b,c,d=[];for(b=0,c=a.length;c>b;b+=2)d.push({x:parseFloat(a[b]),y:parseFloat(a[b+1])});return d},getCSSRules:function(a){for(var d,e=a.getElementsByTagName("style"),f={},g=0,h=e.length;h>g;g++){var i=e[0].textContent;i=i.replace(/\/\*[\s\S]*?\*\//g,""),d=i.match(/[^{]*\{[\s\S]*?\}/g),d=d.map(function(a){return a.trim()}),d.forEach(function(a){for(var d=a.match(/([\s\S]*?)\s*\{([^}]*)\}/),e={},g=d[2].trim(),h=g.replace(/;$/,"").split(/\s*;\s*/),i=0,j=h.length;j>i;i++){var k=h[i].split(/\s*:\s*/),l=b(k[0]),m=c(l,k[1],k[0]);e[l]=m}a=d[1],a.split(",").forEach(function(a){f[a.trim()]=p.util.object.clone(e)})})}return f},loadSVGFromURL:function(a,b,c){function d(d){var e=d.responseXML;e&&!e.documentElement&&p.window.ActiveXObject&&d.responseText&&(e=new ActiveXObject("Microsoft.XMLDOM"),e.async="false",e.loadXML(d.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,""))),e&&e.documentElement&&p.parseSVGDocument(e.documentElement,function(c,d){y.set(a,{objects:p.util.array.invoke(c,"toObject"),options:d}),b(c,d)},c)}a=a.replace(/^\n\s*/,"").trim(),y.has(a,function(c){c?y.get(a,function(a){var c=n(a);b(c.objects,c.options)}):new p.util.request(a,{method:"get",onComplete:d})})},loadSVGFromString:function(a,b,c){a=a.trim();var d;if("undefined"!=typeof DOMParser){var e=new DOMParser;e&&e.parseFromString&&(d=e.parseFromString(a,"text/xml"))}else p.window.ActiveXObject&&(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(a.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,"")));p.parseSVGDocument(d.documentElement,function(a,c){b(a,c)},c)},createSVGFontFacesMarkup:function(a){for(var b="",c=0,d=a.length;d>c;c++)"text"===a[c].type&&a[c].path&&(b+=["@font-face {","font-family: ",a[c].fontFamily,"; ","src: url('",a[c].path,"')","}"].join(""));return b&&(b=['<style type="text/css">',"<![CDATA[",b,"]]>","</style>"].join("")),b},createSVGRefElementsMarkup:function(a){var b=[];return o(b,a,"backgroundColor"),o(b,a,"overlayColor"),b.join("")}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(a,b,c,d){this.elements=a,this.callback=b,this.options=c,this.reviver=d},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 a=0,b=this.elements.length;b>a;a++)!function(a,b){setTimeout(function(){a.createObject(a.elements[b],b)},0)}(this,a)},fabric.ElementsParser.prototype.createObject=function(a,b){var c=fabric[fabric.util.string.capitalize(a.tagName)];if(c&&c.fromElement)try{this._createObject(c,a,b)}catch(d){fabric.log(d)}else this.checkIfDone()},fabric.ElementsParser.prototype._createObject=function(a,b,c){if(a.async)a.fromElement(b,this.createCallback(c,b),this.options);else{var d=a.fromElement(b,this.options);this.resolveGradient(d,"fill"),this.resolveGradient(d,"stroke"),this.reviver&&this.reviver(b,d),this.instances[c]=d,this.checkIfDone()}},fabric.ElementsParser.prototype.createCallback=function(a,b){var c=this;return function(d){c.resolveGradient(d,"fill"),c.resolveGradient(d,"stroke"),c.reviver&&c.reviver(b,d),c.instances[a]=d,c.checkIfDone()}},fabric.ElementsParser.prototype.resolveGradient=function(a,b){var c=a.get(b);if(/^url\(/.test(c)){var d=c.slice(5,c.length-1);fabric.gradientDefs[d]&&a.set(b,fabric.Gradient.fromElement(fabric.gradientDefs[d],a))}},fabric.ElementsParser.prototype.checkIfDone=function(){0===--this.numElements&&(this.instances=this.instances.filter(function(a){return null!=a}),this.callback(this.instances))},function(a){"use strict";function b(a,b){this.x=a,this.y=b}var c=a.fabric||(a.fabric={});return c.Point?void c.warn("fabric.Point is already defined"):(c.Point=b,void(b.prototype={constructor:b,add:function(a){return new b(this.x+a.x,this.y+a.y)},addEquals:function(a){return this.x+=a.x,this.y+=a.y,this},scalarAdd:function(a){return new b(this.x+a,this.y+a)},scalarAddEquals:function(a){return this.x+=a,this.y+=a,this},subtract:function(a){return new b(this.x-a.x,this.y-a.y)},subtractEquals:function(a){return this.x-=a.x,this.y-=a.y,this},scalarSubtract:function(a){return new b(this.x-a,this.y-a)},scalarSubtractEquals:function(a){return this.x-=a,this.y-=a,this},multiply:function(a){return new b(this.x*a,this.y*a)},multiplyEquals:function(a){return this.x*=a,this.y*=a,this},divide:function(a){return new b(this.x/a,this.y/a)},divideEquals:function(a){return this.x/=a,this.y/=a,this},eq:function(a){return this.x===a.x&&this.y===a.y},lt:function(a){return this.x<a.x&&this.y<a.y},lte:function(a){return this.x<=a.x&&this.y<=a.y},gt:function(a){return this.x>a.x&&this.y>a.y},gte:function(a){return this.x>=a.x&&this.y>=a.y},lerp:function(a,c){return new b(this.x+(a.x-this.x)*c,this.y+(a.y-this.y)*c)},distanceFrom:function(a){var b=this.x-a.x,c=this.y-a.y;return Math.sqrt(b*b+c*c)},midPointFrom:function(a){return new b(this.x+(a.x-this.x)/2,this.y+(a.y-this.y)/2)},min:function(a){return new b(Math.min(this.x,a.x),Math.min(this.y,a.y))},max:function(a){return new b(Math.max(this.x,a.x),Math.max(this.y,a.y))},toString:function(){return this.x+","+this.y},setXY:function(a,b){this.x=a,this.y=b},setFromPoint:function(a){this.x=a.x,this.y=a.y},swap:function(a){var b=this.x,c=this.y;this.x=a.x,this.y=a.y,a.x=b,a.y=c}}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){this.status=a,this.points=[]}var c=a.fabric||(a.fabric={});return c.Intersection?void c.warn("fabric.Intersection is already defined"):(c.Intersection=b,c.Intersection.prototype={appendPoint:function(a){this.points.push(a)},appendPoints:function(a){this.points=this.points.concat(a)}},c.Intersection.intersectLineLine=function(a,d,e,f){var g,h=(f.x-e.x)*(a.y-e.y)-(f.y-e.y)*(a.x-e.x),i=(d.x-a.x)*(a.y-e.y)-(d.y-a.y)*(a.x-e.x),j=(f.y-e.y)*(d.x-a.x)-(f.x-e.x)*(d.y-a.y);if(0!==j){var k=h/j,l=i/j;k>=0&&1>=k&&l>=0&&1>=l?(g=new b("Intersection"),g.points.push(new c.Point(a.x+k*(d.x-a.x),a.y+k*(d.y-a.y)))):g=new b}else g=new b(0===h||0===i?"Coincident":"Parallel");return g},c.Intersection.intersectLinePolygon=function(a,c,d){for(var e=new b,f=d.length,g=0;f>g;g++){var h=d[g],i=d[(g+1)%f],j=b.intersectLineLine(a,c,h,i);e.appendPoints(j.points)}return e.points.length>0&&(e.status="Intersection"),e},c.Intersection.intersectPolygonPolygon=function(a,c){for(var d=new b,e=a.length,f=0;e>f;f++){var g=a[f],h=a[(f+1)%e],i=b.intersectLinePolygon(g,h,c);d.appendPoints(i.points)}return d.points.length>0&&(d.status="Intersection"),d},void(c.Intersection.intersectPolygonRectangle=function(a,d,e){var f=d.min(e),g=d.max(e),h=new c.Point(g.x,f.y),i=new c.Point(f.x,g.y),j=b.intersectLinePolygon(f,h,a),k=b.intersectLinePolygon(h,g,a),l=b.intersectLinePolygon(g,i,a),m=b.intersectLinePolygon(i,f,a),n=new b;return n.appendPoints(j.points),n.appendPoints(k.points),n.appendPoints(l.points),n.appendPoints(m.points),n.points.length>0&&(n.status="Intersection"),n}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){a?this._tryParsingColor(a):this.setSource([0,0,0,1])}function c(a,b,c){return 0>c&&(c+=1),c>1&&(c-=1),1/6>c?a+6*(b-a)*c:.5>c?b:2/3>c?a+(b-a)*(2/3-c)*6:a}var d=a.fabric||(a.fabric={});return d.Color?void d.warn("fabric.Color is already defined."):(d.Color=b,d.Color.prototype={_tryParsingColor:function(a){var c;return a in b.colorNameMap&&(a=b.colorNameMap[a]),"transparent"===a?void this.setSource([255,255,255,0]):(c=b.sourceFromHex(a),c||(c=b.sourceFromRgb(a)),c||(c=b.sourceFromHsl(a)),void(c&&this.setSource(c)))},_rgbToHsl:function(a,b,c){a/=255,b/=255,c/=255;var e,f,g,h=d.util.array.max([a,b,c]),i=d.util.array.min([a,b,c]);if(g=(h+i)/2,h===i)e=f=0;else{var j=h-i;switch(f=g>.5?j/(2-h-i):j/(h+i),h){case a:e=(b-c)/j+(c>b?6:0);break;case b:e=(c-a)/j+2;break;case c:e=(a-b)/j+4}e/=6}return[Math.round(360*e),Math.round(100*f),Math.round(100*g)]},getSource:function(){return this._source},setSource:function(a){this._source=a},toRgb:function(){var a=this.getSource();return"rgb("+a[0]+","+a[1]+","+a[2]+")"},toRgba:function(){var a=this.getSource();return"rgba("+a[0]+","+a[1]+","+a[2]+","+a[3]+")"},toHsl:function(){var a=this.getSource(),b=this._rgbToHsl(a[0],a[1],a[2]);return"hsl("+b[0]+","+b[1]+"%,"+b[2]+"%)"},toHsla:function(){var a=this.getSource(),b=this._rgbToHsl(a[0],a[1],a[2]);return"hsla("+b[0]+","+b[1]+"%,"+b[2]+"%,"+a[3]+")"},toHex:function(){var a,b,c,d=this.getSource();return a=d[0].toString(16),a=1===a.length?"0"+a:a,b=d[1].toString(16),b=1===b.length?"0"+b:b,c=d[2].toString(16),c=1===c.length?"0"+c:c,a.toUpperCase()+b.toUpperCase()+c.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(a){var b=this.getSource();return b[3]=a,this.setSource(b),this},toGrayscale:function(){var a=this.getSource(),b=parseInt((.3*a[0]+.59*a[1]+.11*a[2]).toFixed(0),10),c=a[3];return this.setSource([b,b,b,c]),this},toBlackWhite:function(a){var b=this.getSource(),c=(.3*b[0]+.59*b[1]+.11*b[2]).toFixed(0),d=b[3];return a=a||127,c=Number(c)<Number(a)?0:255,this.setSource([c,c,c,d]),this},overlayWith:function(a){a instanceof b||(a=new b(a));for(var c=[],d=this.getAlpha(),e=.5,f=this.getSource(),g=a.getSource(),h=0;3>h;h++)c.push(Math.round(f[h]*(1-e)+g[h]*e));return c[3]=d,this.setSource(c),this}},d.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*)?\)$/,d.Color.reHSLa=/^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/,d.Color.reHex=/^#?([0-9a-f]{6}|[0-9a-f]{3})$/i,d.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"},d.Color.fromRgb=function(a){return b.fromSource(b.sourceFromRgb(a))},d.Color.sourceFromRgb=function(a){var c=a.match(b.reRGBa);if(c){var d=parseInt(c[1],10)/(/%$/.test(c[1])?100:1)*(/%$/.test(c[1])?255:1),e=parseInt(c[2],10)/(/%$/.test(c[2])?100:1)*(/%$/.test(c[2])?255:1),f=parseInt(c[3],10)/(/%$/.test(c[3])?100:1)*(/%$/.test(c[3])?255:1);return[parseInt(d,10),parseInt(e,10),parseInt(f,10),c[4]?parseFloat(c[4]):1]}},d.Color.fromRgba=b.fromRgb,d.Color.fromHsl=function(a){return b.fromSource(b.sourceFromHsl(a))},d.Color.sourceFromHsl=function(a){var d=a.match(b.reHSLa);if(d){var e,f,g,h=(parseFloat(d[1])%360+360)%360/360,i=parseFloat(d[2])/(/%$/.test(d[2])?100:1),j=parseFloat(d[3])/(/%$/.test(d[3])?100:1);if(0===i)e=f=g=j;else{var k=.5>=j?j*(i+1):j+i-j*i,l=2*j-k;e=c(l,k,h+1/3),f=c(l,k,h),g=c(l,k,h-1/3)}return[Math.round(255*e),Math.round(255*f),Math.round(255*g),d[4]?parseFloat(d[4]):1]}},d.Color.fromHsla=b.fromHsl,d.Color.fromHex=function(a){return b.fromSource(b.sourceFromHex(a))},d.Color.sourceFromHex=function(a){if(a.match(b.reHex)){var c=a.slice(a.indexOf("#")+1),d=3===c.length,e=d?c.charAt(0)+c.charAt(0):c.substring(0,2),f=d?c.charAt(1)+c.charAt(1):c.substring(2,4),g=d?c.charAt(2)+c.charAt(2):c.substring(4,6);return[parseInt(e,16),parseInt(f,16),parseInt(g,16),1]}},void(d.Color.fromSource=function(a){var c=new b;return c.setSource(a),c}))}("undefined"!=typeof exports?exports:this),function(){function a(a){var b,c,d,e=a.getAttribute("style"),f=a.getAttribute("offset");if(f=parseFloat(f)/(/%$/.test(f)?100:1),f=0>f?0:f>1?1:f,e){var g=e.split(/\s*;\s*/);""===g[g.length-1]&&g.pop();for(var h=g.length;h--;){var i=g[h].split(/\s*:\s*/),j=i[0].trim(),k=i[1].trim();"stop-color"===j?b=k:"stop-opacity"===j&&(d=k)}}return b||(b=a.getAttribute("stop-color")||"rgb(0,0,0)"),d||(d=a.getAttribute("stop-opacity")),b=new fabric.Color(b),c=b.getAlpha(),d=isNaN(parseFloat(d))?1:parseFloat(d),d*=c,{offset:f,color:b.toRgb(),opacity:d}}function b(a){return{x1:a.getAttribute("x1")||0,y1:a.getAttribute("y1")||0,x2:a.getAttribute("x2")||"100%",y2:a.getAttribute("y2")||0}}function c(a){return{x1:a.getAttribute("fx")||a.getAttribute("cx")||"50%",y1:a.getAttribute("fy")||a.getAttribute("cy")||"50%",r1:0,x2:a.getAttribute("cx")||"50%",y2:a.getAttribute("cy")||"50%",r2:a.getAttribute("r")||"50%"}}function d(a,b,c){var d,e=0,f=1,g="";for(var h in b)d=parseFloat(b[h],10),f="string"==typeof b[h]&&/^\d+%$/.test(b[h])?.01:1,"x1"===h||"x2"===h||"r2"===h?(f*="objectBoundingBox"===c?a.width:1,e="objectBoundingBox"===c?a.left||0:0):("y1"===h||"y2"===h)&&(f*="objectBoundingBox"===c?a.height:1,e="objectBoundingBox"===c?a.top||0:0),b[h]=d*f+e;if("ellipse"===a.type&&null!==b.r2&&"objectBoundingBox"===c&&a.rx!==a.ry){var i=a.ry/a.rx;g=" scale(1, "+i+")",b.y1&&(b.y1/=i),b.y2&&(b.y2/=i)}return g}fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,initialize:function(a){a||(a={});var b={};this.id=fabric.Object.__uid++,this.type=a.type||"linear",b={x1:a.coords.x1||0,y1:a.coords.y1||0,x2:a.coords.x2||0,y2:a.coords.y2||0},"radial"===this.type&&(b.r1=a.coords.r1||0,b.r2=a.coords.r2||0),this.coords=b,this.colorStops=a.colorStops.slice(),a.gradientTransform&&(this.gradientTransform=a.gradientTransform),this.offsetX=a.offsetX||this.offsetX,this.offsetY=a.offsetY||this.offsetY},addColorStop:function(a){for(var b in a){var c=new fabric.Color(a[b]);this.colorStops.push({offset:b,color:c.toRgb(),opacity:c.getAlpha()})}return this},toObject:function(){return{type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(a){var b,c,d=fabric.util.object.clone(this.coords);if(this.colorStops.sort(function(a,b){return a.offset-b.offset}),!a.group||"path-group"!==a.group.type)for(var e in d)"x1"===e||"x2"===e||"r2"===e?d[e]+=this.offsetX-a.width/2:("y1"===e||"y2"===e)&&(d[e]+=this.offsetY-a.height/2);c='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(c+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?b=["<linearGradient ",c,' x1="',d.x1,'" y1="',d.y1,'" x2="',d.x2,'" y2="',d.y2,'">\n']:"radial"===this.type&&(b=["<radialGradient ",c,' cx="',d.x2,'" cy="',d.y2,'" r="',d.r2,'" fx="',d.x1,'" fy="',d.y1,'">\n']);for(var f=0;f<this.colorStops.length;f++)b.push("<stop ",'offset="',100*this.colorStops[f].offset+"%",'" style="stop-color:',this.colorStops[f].color,null!=this.colorStops[f].opacity?";stop-opacity: "+this.colorStops[f].opacity:";",'"/>\n');return b.push("linear"===this.type?"</linearGradient>\n":"</radialGradient>\n"),b.join("")},toLive:function(a){var b;if(this.type){"linear"===this.type?b=a.createLinearGradient(this.coords.x1,this.coords.y1,this.coords.x2,this.coords.y2):"radial"===this.type&&(b=a.createRadialGradient(this.coords.x1,this.coords.y1,this.coords.r1,this.coords.x2,this.coords.y2,this.coords.r2));for(var c=0,d=this.colorStops.length;d>c;c++){var e=this.colorStops[c].color,f=this.colorStops[c].opacity,g=this.colorStops[c].offset;"undefined"!=typeof f&&(e=new fabric.Color(e).setAlpha(f).toRgba()),b.addColorStop(parseFloat(g),e)}return b}}}),fabric.util.object.extend(fabric.Gradient,{fromElement:function(e,f){var g,h=e.getElementsByTagName("stop"),i="linearGradient"===e.nodeName?"linear":"radial",j=e.getAttribute("gradientUnits")||"objectBoundingBox",k=e.getAttribute("gradientTransform"),l=[],m={};"linear"===i?m=b(e):"radial"===i&&(m=c(e));for(var n=h.length;n--;)l.push(a(h[n]));g=d(f,m,j);var o=new fabric.Gradient({type:i,coords:m,colorStops:l,offsetX:-f.left,offsetY:-f.top});return(k||""!==g)&&(o.gradientTransform=fabric.parseTransformAttribute((k||"")+g)),o},forObject:function(a,b){return b||(b={}),d(a,b.coords,"userSpaceOnUse"),new fabric.Gradient(b)}})}(),fabric.Pattern=fabric.util.createClass({repeat:"repeat",offsetX:0,offsetY:0,initialize:function(a){if(a||(a={}),this.id=fabric.Object.__uid++,a.source)if("string"==typeof a.source)if("undefined"!=typeof fabric.util.getFunctionBody(a.source))this.source=new Function(fabric.util.getFunctionBody(a.source));else{var b=this;this.source=fabric.util.createImage(),fabric.util.loadImage(a.source,function(a){b.source=a})}else this.source=a.source;a.repeat&&(this.repeat=a.repeat),a.offsetX&&(this.offsetX=a.offsetX),a.offsetY&&(this.offsetY=a.offsetY)},toObject:function(){var a;return"function"==typeof this.source?a=String(this.source):"string"==typeof this.source.src&&(a=this.source.src),{source:a,repeat:this.repeat,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(a){var b="function"==typeof this.source?this.source():this.source,c=b.width/a.getWidth(),d=b.height/a.getHeight(),e="";return b.src?e=b.src:b.toDataURL&&(e=b.toDataURL()),'<pattern id="SVGID_'+this.id+'" x="'+this.offsetX+'" y="'+this.offsetY+'" width="'+c+'" height="'+d+'"><image x="0" y="0" width="'+b.width+'" height="'+b.height+'" xlink:href="'+e+'"></image></pattern>'},toLive:function(a){var b="function"==typeof this.source?this.source():this.source;if(!b)return"";if("undefined"!=typeof b.src){if(!b.complete)return"";if(0===b.naturalWidth||0===b.naturalHeight)return""}return a.createPattern(b,this.repeat)}}),function(a){"use strict";var b=a.fabric||(a.fabric={});return b.Shadow?void b.warn("fabric.Shadow is already defined."):(b.Shadow=b.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(a){"string"==typeof a&&(a=this._parseShadow(a));for(var c in a)this[c]=a[c];this.id=b.Object.__uid++},_parseShadow:function(a){var c=a.trim(),d=b.Shadow.reOffsetsAndBlur.exec(c)||[],e=c.replace(b.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:e.trim(),offsetX:parseInt(d[1],10)||0,offsetY:parseInt(d[2],10)||0,blur:parseInt(d[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(a){var b="SourceAlpha";return!a||a.fill!==this.color&&a.stroke!==this.color||(b="SourceGraphic"),'<filter id="SVGID_'+this.id+'" y="-40%" height="180%"><feGaussianBlur in="'+b+'" 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 a={},c=b.Shadow.prototype;return this.color!==c.color&&(a.color=this.color),this.blur!==c.blur&&(a.blur=this.blur),this.offsetX!==c.offsetX&&(a.offsetX=this.offsetX),this.offsetY!==c.offsetY&&(a.offsetY=this.offsetY),a}}),void(b.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 a=fabric.util.object.extend,b=fabric.util.getElementOffset,c=fabric.util.removeFromArray,d=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(a,b){b||(b={}),this._initStatic(a,b),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(a,b){this._objects=[],this._createLowerCanvas(a),this._initOptions(b),this._setImageSmoothing(),b.overlayImage&&this.setOverlayImage(b.overlayImage,this.renderAll.bind(this)),b.backgroundImage&&this.setBackgroundImage(b.backgroundImage,this.renderAll.bind(this)),b.backgroundColor&&this.setBackgroundColor(b.backgroundColor,this.renderAll.bind(this)),b.overlayColor&&this.setOverlayColor(b.overlayColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=b(this.lowerCanvasEl),this},setOverlayImage:function(a,b,c){return this.__setBgOverlayImage("overlayImage",a,b,c)},setBackgroundImage:function(a,b,c){return this.__setBgOverlayImage("backgroundImage",a,b,c)},setOverlayColor:function(a,b){return this.__setBgOverlayColor("overlayColor",a,b)},setBackgroundColor:function(a,b){return this.__setBgOverlayColor("backgroundColor",a,b)},_setImageSmoothing:function(){var a=this.getContext();a.imageSmoothingEnabled=this.imageSmoothingEnabled,a.webkitImageSmoothingEnabled=this.imageSmoothingEnabled,a.mozImageSmoothingEnabled=this.imageSmoothingEnabled,a.msImageSmoothingEnabled=this.imageSmoothingEnabled,a.oImageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(a,b,c,d){return"string"==typeof b?fabric.util.loadImage(b,function(b){this[a]=new fabric.Image(b,d),c&&c()},this):(this[a]=b,c&&c()),this},__setBgOverlayColor:function(a,b,c){if(b&&b.source){var d=this;fabric.util.loadImage(b.source,function(e){d[a]=new fabric.Pattern({source:e,repeat:b.repeat,offsetX:b.offsetX,offsetY:b.offsetY}),c&&c()})}else this[a]=b,c&&c();return this},_createCanvasElement:function(){var a=fabric.document.createElement("canvas");if(a.style||(a.style={}),!a)throw d;return this._initCanvasElement(a),a},_initCanvasElement:function(a){if(fabric.util.createCanvasElement(a),"undefined"==typeof a.getContext)throw d},_initOptions:function(a){for(var b in a)this[b]=a[b];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(a){this.lowerCanvasEl=fabric.util.getById(a)||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(a,b){return this.setDimensions({width:a},b)},setHeight:function(a,b){return this.setDimensions({height:a},b)},setDimensions:function(a,b){var c;b=b||{};for(var d in a)c=a[d],b.cssOnly||(this._setBackstoreDimension(d,a[d]),c+="px"),b.backstoreOnly||this._setCssDimension(d,c);return b.cssOnly||this.renderAll(),this.calcOffset(),this},_setBackstoreDimension:function(a,b){return this.lowerCanvasEl[a]=b,this.upperCanvasEl&&(this.upperCanvasEl[a]=b),this.cacheCanvasEl&&(this.cacheCanvasEl[a]=b),this[a]=b,this},_setCssDimension:function(a,b){return this.lowerCanvasEl.style[a]=b,this.upperCanvasEl&&(this.upperCanvasEl.style[a]=b),this.wrapperEl&&(this.wrapperEl.style[a]=b),this},getZoom:function(){return Math.sqrt(this.viewportTransform[0]*this.viewportTransform[3])},setViewportTransform:function(a){this.viewportTransform=a,this.renderAll();for(var b=0,c=this._objects.length;c>b;b++)this._objects[b].setCoords();return this},zoomToPoint:function(a,b){var c=a;a=fabric.util.transformPoint(a,fabric.util.invertTransform(this.viewportTransform)),this.viewportTransform[0]=b,this.viewportTransform[3]=b;var d=fabric.util.transformPoint(a,this.viewportTransform);this.viewportTransform[4]+=c.x-d.x,this.viewportTransform[5]+=c.y-d.y,this.renderAll();for(var e=0,f=this._objects.length;f>e;e++)this._objects[e].setCoords();return this},setZoom:function(a){return this.zoomToPoint(new fabric.Point(0,0),a),this},absolutePan:function(a){this.viewportTransform[4]=-a.x,this.viewportTransform[5]=-a.y,this.renderAll();for(var b=0,c=this._objects.length;c>b;b++)this._objects[b].setCoords();return this},relativePan:function(a){return this.absolutePan(new fabric.Point(-a.x-this.viewportTransform[4],-a.y-this.viewportTransform[5]))},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(a,b){if(b){a.save();var c=this.viewportTransform;a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),b.render(a),a.restore(),this.controlsAboveOverlay||b._renderControls(a)}},_onObjectAdded:function(a){this.stateful&&a.setupState(),a.canvas=this,a.setCoords(),this.fire("object:added",{target:a}),a.fire("added")},_onObjectRemoved:function(a){this.getActiveObject()===a&&(this.fire("before:selection:cleared",{target:a}),this._discardActiveObject(),this.fire("selection:cleared")),this.fire("object:removed",{target:a}),a.fire("removed")},clearContext:function(a){return a.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(a){var b=this[a===!0&&this.interactive?"contextTop":"contextContainer"],c=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),a||this.clearContext(b),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,b),this._renderBackground(b),this._renderObjects(b,c),this._renderActiveGroup(b,c),this.clipTo&&b.restore(),this._renderOverlay(b),this.controlsAboveOverlay&&this.interactive&&this.drawControls(b),this.fire("after:render"),this},_renderObjects:function(a,b){var c,d;if(b)for(c=0,d=this._objects.length;d>c;++c)this._objects[c]&&!b.contains(this._objects[c])&&this._draw(a,this._objects[c]);else for(c=0,d=this._objects.length;d>c;++c)this._draw(a,this._objects[c])},_renderActiveGroup:function(a,b){if(b){var c=[];this.forEachObject(function(a){b.contains(a)&&c.push(a)}),b._set("objects",c),this._draw(a,b)}},_renderBackground:function(a){this.backgroundColor&&(a.fillStyle=this.backgroundColor.toLive?this.backgroundColor.toLive(a):this.backgroundColor,a.fillRect(this.backgroundColor.offsetX||0,this.backgroundColor.offsetY||0,this.width,this.height)),this.backgroundImage&&this._draw(a,this.backgroundImage)},_renderOverlay:function(a){this.overlayColor&&(a.fillStyle=this.overlayColor.toLive?this.overlayColor.toLive(a):this.overlayColor,a.fillRect(this.overlayColor.offsetX||0,this.overlayColor.offsetY||0,this.width,this.height)),this.overlayImage&&this._draw(a,this.overlayImage)},renderTop:function(){var a=this.contextTop||this.contextContainer;this.clearContext(a),this.selection&&this._groupSelector&&this._drawSelection();var b=this.getActiveGroup();return b&&b.render(a),this._renderOverlay(a),this.fire("after:render"),this},getCenter:function(){return{top:this.getHeight()/2,left:this.getWidth()/2}},centerObjectH:function(a){return this._centerObject(a,new fabric.Point(this.getCenter().left,a.getCenterPoint().y)),this.renderAll(),this},centerObjectV:function(a){return this._centerObject(a,new fabric.Point(a.getCenterPoint().x,this.getCenter().top)),this.renderAll(),this},centerObject:function(a){var b=this.getCenter();return this._centerObject(a,new fabric.Point(b.left,b.top)),this.renderAll(),this},_centerObject:function(a,b){return a.setPositionByOrigin(b,"center","center"),this},toDatalessJSON:function(a){return this.toDatalessObject(a)},toObject:function(a){return this._toObjectMethod("toObject",a)},toDatalessObject:function(a){return this._toObjectMethod("toDatalessObject",a)},_toObjectMethod:function(b,c){var d=this.getActiveGroup();d&&this.discardActiveGroup();var e={objects:this._toObjects(b,c)};return a(e,this.__serializeBgOverlay()),fabric.util.populateWithProperties(this,e,c),d&&(this.setActiveGroup(new fabric.Group(d.getObjects(),{originX:"center",originY:"center"})),d.forEachObject(function(a){a.set("active",!0)}),this._currentTransform&&(this._currentTransform.target=this.getActiveGroup())),e},_toObjects:function(a,b){return this.getObjects().map(function(c){return this._toObject(c,a,b)},this)},_toObject:function(a,b,c){var d;this.includeDefaultValues||(d=a.includeDefaultValues,a.includeDefaultValues=!1);var e=a[b](c);return this.includeDefaultValues||(a.includeDefaultValues=d),e},__serializeBgOverlay:function(){var a={background:this.backgroundColor&&this.backgroundColor.toObject?this.backgroundColor.toObject():this.backgroundColor};return this.overlayColor&&(a.overlay=this.overlayColor.toObject?this.overlayColor.toObject():this.overlayColor),this.backgroundImage&&(a.backgroundImage=this.backgroundImage.toObject()),this.overlayImage&&(a.overlayImage=this.overlayImage.toObject()),a},svgViewportTransformation:!0,toSVG:function(a,b){a||(a={});var c=[];return this._setSVGPreamble(c,a),this._setSVGHeader(c,a),this._setSVGBgOverlayColor(c,"backgroundColor"),this._setSVGBgOverlayImage(c,"backgroundImage"),this._setSVGObjects(c,b),this._setSVGBgOverlayColor(c,"overlayColor"),this._setSVGBgOverlayImage(c,"overlayImage"),c.push("</svg>"),c.join("")},_setSVGPreamble:function(a,b){b.suppressPreamble||a.push('<?xml version="1.0" encoding="',b.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(a,b){var c,d,e;b.viewBox?(c=b.viewBox.width,d=b.viewBox.height):(c=this.width,d=this.height,this.svgViewportTransformation||(e=this.viewportTransform,c/=e[0],d/=e[3])),a.push("<svg ",'xmlns="http://www.w3.org/2000/svg" ','xmlns:xlink="http://www.w3.org/1999/xlink" ','version="1.1" ','width="',c,'" ','height="',d,'" ',this.backgroundColor&&!this.backgroundColor.toLive?'style="background-color: '+this.backgroundColor+'" ':null,b.viewBox?'viewBox="'+b.viewBox.x+" "+b.viewBox.y+" "+b.viewBox.width+" "+b.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(a,b){var c=this.getActiveGroup();c&&this.discardActiveGroup();for(var d=0,e=this.getObjects(),f=e.length;f>d;d++)a.push(e[d].toSVG(b));c&&(this.setActiveGroup(new fabric.Group(c.getObjects())),c.forEachObject(function(a){a.set("active",!0)}))},_setSVGBgOverlayImage:function(a,b){this[b]&&this[b].toSVG&&a.push(this[b].toSVG())},_setSVGBgOverlayColor:function(a,b){this[b]&&this[b].source?a.push('<rect x="',this[b].offsetX,'" y="',this[b].offsetY,'" ','width="',"repeat-y"===this[b].repeat||"no-repeat"===this[b].repeat?this[b].source.width:this.width,'" height="',"repeat-x"===this[b].repeat||"no-repeat"===this[b].repeat?this[b].source.height:this.height,'" fill="url(#'+b+'Pattern)"',"></rect>"):this[b]&&"overlayColor"===b&&a.push('<rect x="0" y="0" ','width="',this.width,'" height="',this.height,'" fill="',this[b],'"',"></rect>")},sendToBack:function(a){return c(this._objects,a),this._objects.unshift(a),this.renderAll&&this.renderAll()},bringToFront:function(a){return c(this._objects,a),this._objects.push(a),this.renderAll&&this.renderAll()},sendBackwards:function(a,b){var d=this._objects.indexOf(a);if(0!==d){var e=this._findNewLowerIndex(a,d,b);c(this._objects,a),this._objects.splice(e,0,a),this.renderAll&&this.renderAll()}return this},_findNewLowerIndex:function(a,b,c){var d;if(c){d=b;for(var e=b-1;e>=0;--e){var f=a.intersectsWithObject(this._objects[e])||a.isContainedWithinObject(this._objects[e])||this._objects[e].isContainedWithinObject(a);if(f){d=e;break}}}else d=b-1;return d},bringForward:function(a,b){var d=this._objects.indexOf(a);if(d!==this._objects.length-1){var e=this._findNewUpperIndex(a,d,b);c(this._objects,a),this._objects.splice(e,0,a),this.renderAll&&this.renderAll()}return this},_findNewUpperIndex:function(a,b,c){var d;if(c){d=b;for(var e=b+1;e<this._objects.length;++e){var f=a.intersectsWithObject(this._objects[e])||a.isContainedWithinObject(this._objects[e])||this._objects[e].isContainedWithinObject(a);if(f){d=e;break}}}else d=b+1;return d},moveTo:function(a,b){return c(this._objects,a),this._objects.splice(b,0,a),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+" }>"}}),a(fabric.StaticCanvas.prototype,fabric.Observable),a(fabric.StaticCanvas.prototype,fabric.Collection),a(fabric.StaticCanvas.prototype,fabric.DataURLExporter),a(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(a){var b=fabric.util.createCanvasElement();if(!b||!b.getContext)return null;var c=b.getContext("2d");if(!c)return null;switch(a){case"getImageData":return"undefined"!=typeof c.getImageData;case"setLineDash":return"undefined"!=typeof c.setLineDash;case"toDataURL":return"undefined"!=typeof b.toDataURL;case"toDataURLWithQuality":try{return b.toDataURL("image/jpeg",0),!0}catch(d){}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(a){return this.shadow=new fabric.Shadow(a),this},_setBrushStyles:function(){var a=this.canvas.contextTop;a.strokeStyle=this.color,a.lineWidth=this.width,a.lineCap=this.strokeLineCap,a.lineJoin=this.strokeLineJoin},_setShadow:function(){if(this.shadow){var a=this.canvas.contextTop;a.shadowColor=this.shadow.color,a.shadowBlur=this.shadow.blur,a.shadowOffsetX=this.shadow.offsetX,a.shadowOffsetY=this.shadow.offsetY}},_resetShadow:function(){var a=this.canvas.contextTop;a.shadowColor="",a.shadowBlur=a.shadowOffsetX=a.shadowOffsetY=0}}),function(){var a=fabric.util.array.min,b=fabric.util.array.max;fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{initialize:function(a){this.canvas=a,this._points=[]},onMouseDown:function(a){this._prepareForDrawing(a),this._captureDrawingPath(a),this._render()},onMouseMove:function(a){this._captureDrawingPath(a),this.canvas.clearContext(this.canvas.contextTop),this._render()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(a){var b=new fabric.Point(a.x,a.y);this._reset(),this._addPoint(b),this.canvas.contextTop.moveTo(b.x,b.y)},_addPoint:function(a){this._points.push(a)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(a){var b=new fabric.Point(a.x,a.y);this._addPoint(b)},_render:function(){var a=this.canvas.contextTop,b=this.canvas.viewportTransform,c=this._points[0],d=this._points[1];a.save(),a.transform(b[0],b[1],b[2],b[3],b[4],b[5]),a.beginPath(),2===this._points.length&&c.x===d.x&&c.y===d.y&&(c.x-=.5,d.x+=.5),a.moveTo(c.x,c.y);for(var e=1,f=this._points.length;f>e;e++){var g=c.midPointFrom(d);a.quadraticCurveTo(c.x,c.y,g.x,g.y),c=this._points[e],d=this._points[e+1]}a.lineTo(c.x,c.y),a.stroke(),a.restore()},_getSVGPathData:function(){return this.box=this.getPathBoundingBox(this._points),this.convertPointsToSVGPath(this._points,this.box.minX,this.box.minY)},getPathBoundingBox:function(c){for(var d=[],e=[],f=c[0],g=c[1],h=f,i=1,j=c.length;j>i;i++){var k=f.midPointFrom(g);d.push(h.x),d.push(k.x),e.push(h.y),e.push(k.y),f=c[i],g=c[i+1],h=k}return d.push(f.x),e.push(f.y),{minX:a(d),minY:a(e),maxX:b(d),maxY:b(e)}},convertPointsToSVGPath:function(a,b,c){var d=[],e=new fabric.Point(a[0].x-b,a[0].y-c),f=new fabric.Point(a[1].x-b,a[1].y-c);d.push("M ",a[0].x-b," ",a[0].y-c," ");for(var g=1,h=a.length;h>g;g++){var i=e.midPointFrom(f);d.push("Q ",e.x," ",e.y," ",i.x," ",i.y," "),e=new fabric.Point(a[g].x-b,a[g].y-c),g+1<a.length&&(f=new fabric.Point(a[g+1].x-b,a[g+1].y-c))}return d.push("L ",e.x," ",e.y," "),d},createPath:function(a){var b=new fabric.Path(a);return b.fill=null,b.stroke=this.color,b.strokeWidth=this.width,b.strokeLineCap=this.strokeLineCap,b.strokeLineJoin=this.strokeLineJoin,this.shadow&&(this.shadow.affectStroke=!0,b.setShadow(this.shadow)),b},_finalizeAndAddPath:function(){var a=this.canvas.contextTop;a.closePath();var b=this._getSVGPathData().join("");if("M 0 0 Q 0 0 0 0 L 0 0"===b)return void this.canvas.renderAll();var c=this.box.minX+(this.box.maxX-this.box.minX)/2,d=this.box.minY+(this.box.maxY-this.box.minY)/2;this.canvas.contextTop.arc(c,d,3,0,2*Math.PI,!1);var e=this.createPath(b);e.set({left:c,top:d,originX:"center",originY:"center"}),this.canvas.add(e),e.setCoords(),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderAll(),this.canvas.fire("path:created",{path:e})}})}(),fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,initialize:function(a){this.canvas=a,this.points=[]},drawDot:function(a){var b=this.addPoint(a),c=this.canvas.contextTop,d=this.canvas.viewportTransform;c.save(),c.transform(d[0],d[1],d[2],d[3],d[4],d[5]),c.fillStyle=b.fill,c.beginPath(),c.arc(b.x,b.y,b.radius,0,2*Math.PI,!1),c.closePath(),c.fill(),c.restore()},onMouseDown:function(a){this.points.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.drawDot(a)},onMouseMove:function(a){this.drawDot(a)},onMouseUp:function(){var a=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var b=[],c=0,d=this.points.length;d>c;c++){var e=this.points[c],f=new fabric.Circle({radius:e.radius,left:e.x,top:e.y,originX:"center",originY:"center",fill:e.fill});this.shadow&&f.setShadow(this.shadow),b.push(f)}var g=new fabric.Group(b,{originX:"center",originY:"center"});g.canvas=this.canvas,this.canvas.add(g),this.canvas.fire("path:created",{path:g}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=a,this.canvas.renderAll()},addPoint:function(a){var b=new fabric.Point(a.x,a.y),c=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,d=new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return b.radius=c,b.fill=d,this.points.push(b),b}}),fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(a){this.canvas=a,this.sprayChunks=[]},onMouseDown:function(a){this.sprayChunks.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.addSprayChunk(a),this.render()},onMouseMove:function(a){this.addSprayChunk(a),this.render()},onMouseUp:function(){var a=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var b=[],c=0,d=this.sprayChunks.length;d>c;c++)for(var e=this.sprayChunks[c],f=0,g=e.length;g>f;f++){var h=new fabric.Rect({width:e[f].width,height:e[f].width,left:e[f].x+1,top:e[f].y+1,originX:"center",originY:"center",fill:this.color});this.shadow&&h.setShadow(this.shadow),b.push(h)}this.optimizeOverlapping&&(b=this._getOptimizedRects(b));var i=new fabric.Group(b,{originX:"center",originY:"center"});i.canvas=this.canvas,this.canvas.add(i),this.canvas.fire("path:created",{path:i}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=a,this.canvas.renderAll()},_getOptimizedRects:function(a){for(var b,c={},d=0,e=a.length;e>d;d++)b=a[d].left+""+a[d].top,c[b]||(c[b]=a[d]);var f=[];for(b in c)f.push(c[b]);return f},render:function(){var a=this.canvas.contextTop;a.fillStyle=this.color;var b=this.canvas.viewportTransform;a.save(),a.transform(b[0],b[1],b[2],b[3],b[4],b[5]);for(var c=0,d=this.sprayChunkPoints.length;d>c;c++){var e=this.sprayChunkPoints[c];"undefined"!=typeof e.opacity&&(a.globalAlpha=e.opacity),a.fillRect(e.x,e.y,e.width,e.width)}a.restore()},addSprayChunk:function(a){this.sprayChunkPoints=[];for(var b,c,d,e=this.width/2,f=0;f<this.density;f++){b=fabric.util.getRandomInt(a.x-e,a.x+e),c=fabric.util.getRandomInt(a.y-e,a.y+e),d=this.dotWidthVariance?fabric.util.getRandomInt(Math.max(1,this.dotWidth-this.dotWidthVariance),this.dotWidth+this.dotWidthVariance):this.dotWidth;var g=new fabric.Point(b,c);g.width=d,this.randomOpacity&&(g.opacity=fabric.util.getRandomInt(0,100)/100),this.sprayChunkPoints.push(g)}this.sprayChunks.push(this.sprayChunkPoints)}}),fabric.PatternBrush=fabric.util.createClass(fabric.PencilBrush,{getPatternSrc:function(){var a=20,b=5,c=fabric.document.createElement("canvas"),d=c.getContext("2d");return c.width=c.height=a+b,d.fillStyle=this.color,d.beginPath(),d.arc(a/2,a/2,a/2,0,2*Math.PI,!1),d.closePath(),d.fill(),c},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(a){var b=this.callSuper("createPath",a);return b.stroke=new fabric.Pattern({source:this.source||this.getPatternSrcFunction()}),b}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{toDataURL:function(a){a||(a={});var b=a.format||"png",c=a.quality||1,d=a.multiplier||1,e={left:a.left,top:a.top,width:a.width,height:a.height};return 1!==d?this.__toDataURLWithMultiplier(b,c,e,d):this.__toDataURL(b,c,e)},__toDataURL:function(a,b,c){this.renderAll(!0);var d=this.upperCanvasEl||this.lowerCanvasEl,e=this.__getCroppedCanvas(d,c);"jpg"===a&&(a="jpeg");var f=fabric.StaticCanvas.supports("toDataURLWithQuality")?(e||d).toDataURL("image/"+a,b):(e||d).toDataURL("image/"+a);return this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),e&&(e=null),f},__getCroppedCanvas:function(a,b){var c,d,e="left"in b||"top"in b||"width"in b||"height"in b;return e&&(c=fabric.util.createCanvasElement(),d=c.getContext("2d"),c.width=b.width||this.width,c.height=b.height||this.height,d.drawImage(a,-b.left||0,-b.top||0)),c},__toDataURLWithMultiplier:function(a,b,c,d){var e=this.getWidth(),f=this.getHeight(),g=e*d,h=f*d,i=this.getActiveObject(),j=this.getActiveGroup(),k=this.contextTop||this.contextContainer;d>1&&this.setWidth(g).setHeight(h),k.scale(d,d),c.left&&(c.left*=d),c.top&&(c.top*=d),c.width?c.width*=d:1>d&&(c.width=g),c.height?c.height*=d:1>d&&(c.height=h),j?this._tempRemoveBordersControlsFromGroup(j):i&&this.deactivateAll&&this.deactivateAll(),this.renderAll(!0);var l=this.__toDataURL(a,b,c);return this.width=e,this.height=f,k.scale(1/d,1/d),this.setWidth(e).setHeight(f),j?this._restoreBordersControlsOnGroup(j):i&&this.setActiveObject&&this.setActiveObject(i),this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),l},toDataURLWithMultiplier:function(a,b,c){return this.toDataURL({format:a,multiplier:b,quality:c})},_tempRemoveBordersControlsFromGroup:function(a){a.origHasControls=a.hasControls,a.origBorderColor=a.borderColor,a.hasControls=!0,a.borderColor="rgba(0,0,0,0)",a.forEachObject(function(a){a.origBorderColor=a.borderColor,a.borderColor="rgba(0,0,0,0)"})},_restoreBordersControlsOnGroup:function(a){a.hideControls=a.origHideControls,a.borderColor=a.origBorderColor,a.forEachObject(function(a){a.borderColor=a.origBorderColor,delete a.origBorderColor})}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{loadFromDatalessJSON:function(a,b,c){return this.loadFromJSON(a,b,c)},loadFromJSON:function(a,b,c){if(a){var d="string"==typeof a?JSON.parse(a):a;this.clear();var e=this;return this._enlivenObjects(d.objects,function(){e._setBgOverlay(d,b)},c),this}},_setBgOverlay:function(a,b){var c=this,d={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!(a.backgroundImage||a.overlayImage||a.background||a.overlay))return void(b&&b());var e=function(){d.backgroundImage&&d.overlayImage&&d.backgroundColor&&d.overlayColor&&(c.renderAll(),b&&b())};this.__setBgOverlay("backgroundImage",a.backgroundImage,d,e),this.__setBgOverlay("overlayImage",a.overlayImage,d,e),this.__setBgOverlay("backgroundColor",a.background,d,e),this.__setBgOverlay("overlayColor",a.overlay,d,e),e()},__setBgOverlay:function(a,b,c,d){var e=this;return b?void("backgroundImage"===a||"overlayImage"===a?fabric.Image.fromObject(b,function(b){e[a]=b,c[a]=!0,d&&d()}):this["set"+fabric.util.string.capitalize(a,!0)](b,function(){c[a]=!0,d&&d()})):void(c[a]=!0)},_enlivenObjects:function(a,b,c){var d=this;if(!a||0===a.length)return void(b&&b());var e=this.renderOnAddRemove;this.renderOnAddRemove=!1,fabric.util.enlivenObjects(a,function(a){a.forEach(function(a,b){d.insertAt(a,b,!0)}),d.renderOnAddRemove=e,b&&b()},null,c)},_toDataURL:function(a,b){this.clone(function(c){b(c.toDataURL(a))})},_toDataURLWithMultiplier:function(a,b,c){this.clone(function(d){c(d.toDataURLWithMultiplier(a,b))})},clone:function(a,b){var c=JSON.stringify(this.toJSON(b));this.cloneWithoutData(function(b){b.loadFromJSON(c,function(){a&&a(b)})})},cloneWithoutData:function(a){var b=fabric.document.createElement("canvas");b.width=this.getWidth(),b.height=this.getHeight();var c=new fabric.Canvas(b);c.clipTo=this.clipTo,this.backgroundImage?(c.setBackgroundImage(this.backgroundImage.src,function(){c.renderAll(),a&&a(c)}),c.backgroundImageOpacity=this.backgroundImageOpacity,c.backgroundImageStretch=this.backgroundImageStretch):a&&a(c)}}),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.toFixed,e=b.util.string.capitalize,f=b.util.degreesToRadians,g=b.StaticCanvas.supports("setLineDash");b.Object||(b.Object=b.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(a){a&&this.setOptions(a)},_initGradient:function(a){!a.fill||!a.fill.colorStops||a.fill instanceof b.Gradient||this.set("fill",new b.Gradient(a.fill))},_initPattern:function(a){!a.fill||!a.fill.source||a.fill instanceof b.Pattern||this.set("fill",new b.Pattern(a.fill)),!a.stroke||!a.stroke.source||a.stroke instanceof b.Pattern||this.set("stroke",new b.Pattern(a.stroke))},_initClipping:function(a){if(a.clipTo&&"string"==typeof a.clipTo){var c=b.util.getFunctionBody(a.clipTo);"undefined"!=typeof c&&(this.clipTo=new Function("ctx",c))}},setOptions:function(a){for(var b in a)this.set(b,a[b]);this._initGradient(a),this._initPattern(a),this._initClipping(a)},transform:function(a,b){this.group&&this.group.transform(a,b),a.globalAlpha=this.opacity;var c=b?this._getLeftTopCoords():this.getCenterPoint();a.translate(c.x,c.y),a.rotate(f(this.angle)),a.scale(this.scaleX*(this.flipX?-1:1),this.scaleY*(this.flipY?-1:1))},toObject:function(a){var c=b.Object.NUM_FRACTION_DIGITS,e={type:this.type,originX:this.originX,originY:this.originY,left:d(this.left,c),top:d(this.top,c),width:d(this.width,c),height:d(this.height,c),fill:this.fill&&this.fill.toObject?this.fill.toObject():this.fill,stroke:this.stroke&&this.stroke.toObject?this.stroke.toObject():this.stroke,strokeWidth:d(this.strokeWidth,c),strokeDashArray:this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeLineJoin:this.strokeLineJoin,strokeMiterLimit:d(this.strokeMiterLimit,c),scaleX:d(this.scaleX,c),scaleY:d(this.scaleY,c),angle:d(this.getAngle(),c),flipX:this.flipX,flipY:this.flipY,opacity:d(this.opacity,c),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||(e=this._removeDefaultValues(e)),b.util.populateWithProperties(this,e,a),e},toDatalessObject:function(a){return this.toObject(a)},_removeDefaultValues:function(a){var c=b.util.getKlass(a.type).prototype,d=c.stateProperties;return d.forEach(function(b){a[b]===c[b]&&delete a[b]}),a},toString:function(){return"#<fabric."+e(this.type)+">"},get:function(a){return this[a]},_setObject:function(a){for(var b in a)this._set(b,a[b])},set:function(a,b){return"object"==typeof a?this._setObject(a):"function"==typeof b&&"clipTo"!==a?this._set(a,b(this.get(a))):this._set(a,b),this},_set:function(a,c){var e="scaleX"===a||"scaleY"===a;return e&&(c=this._constrainScale(c)),"scaleX"===a&&0>c?(this.flipX=!this.flipX,c*=-1):"scaleY"===a&&0>c?(this.flipY=!this.flipY,c*=-1):"width"===a||"height"===a?this.minScaleLimit=d(Math.min(.1,1/Math.max(this.width,this.height)),2):"shadow"!==a||!c||c instanceof b.Shadow||(c=new b.Shadow(c)),this[a]=c,this},toggle:function(a){var b=this.get(a);return"boolean"==typeof b&&this.set(a,!b),this},setSourcePath:function(a){return this.sourcePath=a,this},getViewportTransform:function(){return this.canvas&&this.canvas.viewportTransform?this.canvas.viewportTransform:[1,0,0,1,0,0]},render:function(a,c){if(0!==this.width&&0!==this.height&&this.visible){if(a.save(),this._setupFillRule(a),this._transform(a,c),this._setStrokeStyles(a),this._setFillStyles(a),this.group&&"path-group"===this.group.type){a.translate(-this.group.width/2,-this.group.height/2);var d=this.transformMatrix;d&&a.transform.apply(a,d)}a.globalAlpha=this.group?a.globalAlpha*this.opacity:this.opacity,this._setShadow(a),this.clipTo&&b.util.clipContext(this,a),this._render(a,c),this.clipTo&&a.restore(),this._removeShadow(a),this._restoreFillRule(a),a.restore()}},_transform:function(a,b){var c=this.transformMatrix;c&&!this.group&&a.setTransform.apply(a,c),b||this.transform(a)},_setStrokeStyles:function(a){this.stroke&&(a.lineWidth=this.strokeWidth,a.lineCap=this.strokeLineCap,a.lineJoin=this.strokeLineJoin,a.miterLimit=this.strokeMiterLimit,a.strokeStyle=this.stroke.toLive?this.stroke.toLive(a):this.stroke)},_setFillStyles:function(a){this.fill&&(a.fillStyle=this.fill.toLive?this.fill.toLive(a):this.fill)},_renderControls:function(a,c){var d=this.getViewportTransform();if(a.save(),this.active&&!c){var e;this.group&&(e=b.util.transformPoint(this.group.getCenterPoint(),d),a.translate(e.x,e.y),a.rotate(f(this.group.angle))),e=b.util.transformPoint(this.getCenterPoint(),d,null!=this.group),this.group&&(e.x*=this.group.scaleX,e.y*=this.group.scaleY),a.translate(e.x,e.y),a.rotate(f(this.angle)),this.drawBorders(a),this.drawControls(a)}a.restore()},_setShadow:function(a){this.shadow&&(a.shadowColor=this.shadow.color,a.shadowBlur=this.shadow.blur,a.shadowOffsetX=this.shadow.offsetX,a.shadowOffsetY=this.shadow.offsetY)
-},_removeShadow:function(a){this.shadow&&(a.shadowColor="",a.shadowBlur=a.shadowOffsetX=a.shadowOffsetY=0)},_renderFill:function(a){if(this.fill){if(a.save(),this.fill.toLive&&a.translate(-this.width/2+this.fill.offsetX||0,-this.height/2+this.fill.offsetY||0),this.fill.gradientTransform){var b=this.fill.gradientTransform;a.transform.apply(a,b)}"destination-over"===this.fillRule?a.fill("evenodd"):a.fill(),a.restore(),this.shadow&&!this.shadow.affectStroke&&this._removeShadow(a)}},_renderStroke:function(a){if(this.stroke&&0!==this.strokeWidth){if(a.save(),this.strokeDashArray)1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),g?(a.setLineDash(this.strokeDashArray),this._stroke&&this._stroke(a)):this._renderDashedStroke&&this._renderDashedStroke(a),a.stroke();else{if(this.stroke.gradientTransform){var b=this.stroke.gradientTransform;a.transform.apply(a,b)}this._stroke?this._stroke(a):a.stroke()}this._removeShadow(a),a.restore()}},clone:function(a,c){return this.constructor.fromObject?this.constructor.fromObject(this.toObject(c),a):new b.Object(this.toObject(c))},cloneAsImage:function(a){var c=this.toDataURL();return b.util.loadImage(c,function(c){a&&a(new b.Image(c))}),this},toDataURL:function(a){a||(a={});var c=b.util.createCanvasElement(),d=this.getBoundingRect();c.width=d.width,c.height=d.height,b.util.wrapElement(c,"div");var e=new b.Canvas(c);"jpg"===a.format&&(a.format="jpeg"),"jpeg"===a.format&&(e.backgroundColor="#fff");var f={active:this.get("active"),left:this.getLeft(),top:this.getTop()};this.set("active",!1),this.setPositionByOrigin(new b.Point(c.width/2,c.height/2),"center","center");var g=this.canvas;e.add(this);var h=e.toDataURL(a);return this.set(f).setCoords(),this.canvas=g,e.dispose(),e=null,h},isType:function(a){return this.type===a},complexity:function(){return 0},toJSON:function(a){return this.toObject(a)},setGradient:function(a,c){c||(c={});var d={colorStops:[]};d.type=c.type||(c.r1||c.r2?"radial":"linear"),d.coords={x1:c.x1,y1:c.y1,x2:c.x2,y2:c.y2},(c.r1||c.r2)&&(d.coords.r1=c.r1,d.coords.r2=c.r2);for(var e in c.colorStops){var f=new b.Color(c.colorStops[e]);d.colorStops.push({offset:e,color:f.toRgb(),opacity:f.getAlpha()})}return this.set(a,b.Gradient.forObject(this,d))},setPatternFill:function(a){return this.set("fill",new b.Pattern(a))},setShadow:function(a){return this.set("shadow",a?new b.Shadow(a):null)},setColor:function(a){return this.set("fill",a),this},setAngle:function(a){var b=("center"!==this.originX||"center"!==this.originY)&&this.centeredRotation;return b&&this._setOriginToCenter(),this.set("angle",a),b&&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(a,b){b=b||this.canvas.getPointer(a);var c=this.translateToOriginPoint(this.getCenterPoint(),"left","top");return{x:b.x-c.x,y:b.y-c.y}},_setupFillRule:function(a){this.fillRule&&(this._prevFillRule=a.globalCompositeOperation,a.globalCompositeOperation=this.fillRule)},_restoreFillRule:function(a){this.fillRule&&this._prevFillRule&&(a.globalCompositeOperation=this._prevFillRule)}}),b.util.createAccessors(b.Object),b.Object.prototype.rotate=b.Object.prototype.setAngle,c(b.Object.prototype,b.Observable),b.Object.NUM_FRACTION_DIGITS=2,b.Object.__uid=0)}("undefined"!=typeof exports?exports:this),function(){var a=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{translateToCenterPoint:function(b,c,d){var e=b.x,f=b.y,g=this.stroke?this.strokeWidth:0;return"left"===c?e=b.x+(this.getWidth()+g*this.scaleX)/2:"right"===c&&(e=b.x-(this.getWidth()+g*this.scaleX)/2),"top"===d?f=b.y+(this.getHeight()+g*this.scaleY)/2:"bottom"===d&&(f=b.y-(this.getHeight()+g*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(e,f),b,a(this.angle))},translateToOriginPoint:function(b,c,d){var e=b.x,f=b.y,g=this.stroke?this.strokeWidth:0;return"left"===c?e=b.x-(this.getWidth()+g*this.scaleX)/2:"right"===c&&(e=b.x+(this.getWidth()+g*this.scaleX)/2),"top"===d?f=b.y-(this.getHeight()+g*this.scaleY)/2:"bottom"===d&&(f=b.y+(this.getHeight()+g*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(e,f),b,a(this.angle))},getCenterPoint:function(){var a=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(a,this.originX,this.originY)},getPointByOrigin:function(a,b){var c=this.getCenterPoint();return this.translateToOriginPoint(c,a,b)},toLocalPoint:function(b,c,d){var e,f,g=this.getCenterPoint(),h=this.stroke?this.strokeWidth:0;return c&&d?(e="left"===c?g.x-(this.getWidth()+h*this.scaleX)/2:"right"===c?g.x+(this.getWidth()+h*this.scaleX)/2:g.x,f="top"===d?g.y-(this.getHeight()+h*this.scaleY)/2:"bottom"===d?g.y+(this.getHeight()+h*this.scaleY)/2:g.y):(e=this.left,f=this.top),fabric.util.rotatePoint(new fabric.Point(b.x,b.y),g,-a(this.angle)).subtractEquals(new fabric.Point(e,f))},setPositionByOrigin:function(a,b,c){var d=this.translateToCenterPoint(a,b,c),e=this.translateToOriginPoint(d,this.originX,this.originY);this.set("left",e.x),this.set("top",e.y)},adjustPosition:function(b){var c=a(this.angle),d=this.getWidth()/2,e=Math.cos(c)*d,f=Math.sin(c)*d,g=this.getWidth(),h=Math.cos(c)*g,i=Math.sin(c)*g;"center"===this.originX&&"left"===b||"right"===this.originX&&"center"===b?(this.left-=e,this.top-=f):"left"===this.originX&&"center"===b||"center"===this.originX&&"right"===b?(this.left+=e,this.top+=f):"left"===this.originX&&"right"===b?(this.left+=h,this.top+=i):"right"===this.originX&&"left"===b&&(this.left-=h,this.top-=i),this.setCoords(),this.originX=b},_setOriginToCenter:function(){this._originalOriginX=this.originX,this._originalOriginY=this.originY;var a=this.getCenterPoint();this.originX="center",this.originY="center",this.left=a.x,this.top=a.y},_resetOrigin:function(){var a=this.translateToOriginPoint(this.getCenterPoint(),this._originalOriginX,this._originalOriginY);this.originX=this._originalOriginX,this.originY=this._originalOriginY,this.left=a.x,this.top=a.y,this._originalOriginX=null,this._originalOriginY=null},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","center")}})}(),function(){var a=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{oCoords:null,intersectsWithRect:function(a,b){var c=this.oCoords,d=new fabric.Point(c.tl.x,c.tl.y),e=new fabric.Point(c.tr.x,c.tr.y),f=new fabric.Point(c.bl.x,c.bl.y),g=new fabric.Point(c.br.x,c.br.y),h=fabric.Intersection.intersectPolygonRectangle([d,e,g,f],a,b);return"Intersection"===h.status},intersectsWithObject:function(a){function b(a){return{tl:new fabric.Point(a.tl.x,a.tl.y),tr:new fabric.Point(a.tr.x,a.tr.y),bl:new fabric.Point(a.bl.x,a.bl.y),br:new fabric.Point(a.br.x,a.br.y)}}var c=b(this.oCoords),d=b(a.oCoords),e=fabric.Intersection.intersectPolygonPolygon([c.tl,c.tr,c.br,c.bl],[d.tl,d.tr,d.br,d.bl]);return"Intersection"===e.status},isContainedWithinObject:function(a){var b=a.getBoundingRect(),c=new fabric.Point(b.left,b.top),d=new fabric.Point(b.left+b.width,b.top+b.height);return this.isContainedWithinRect(c,d)},isContainedWithinRect:function(a,b){var c=this.getBoundingRect();return c.left>=a.x&&c.left+c.width<=b.x&&c.top>=a.y&&c.top+c.height<=b.y},containsPoint:function(a){var b=this._getImageLines(this.oCoords),c=this._findCrossPoints(a,b);return 0!==c&&c%2===1},_getImageLines:function(a){return{topline:{o:a.tl,d:a.tr},rightline:{o:a.tr,d:a.br},bottomline:{o:a.br,d:a.bl},leftline:{o:a.bl,d:a.tl}}},_findCrossPoints:function(a,b){var c,d,e,f,g,h,i,j=0;for(var k in b)if(i=b[k],!(i.o.y<a.y&&i.d.y<a.y||i.o.y>=a.y&&i.d.y>=a.y||(i.o.x===i.d.x&&i.o.x>=a.x?(g=i.o.x,h=a.y):(c=0,d=(i.d.y-i.o.y)/(i.d.x-i.o.x),e=a.y-c*a.x,f=i.o.y-d*i.o.x,g=-(e-f)/(c-d),h=e+c*g),g>=a.x&&(j+=1),2!==j)))break;return j},getBoundingRectWidth:function(){return this.getBoundingRect().width},getBoundingRectHeight:function(){return this.getBoundingRect().height},getBoundingRect:function(){this.oCoords||this.setCoords();var a=[this.oCoords.tl.x,this.oCoords.tr.x,this.oCoords.br.x,this.oCoords.bl.x],b=fabric.util.array.min(a),c=fabric.util.array.max(a),d=Math.abs(b-c),e=[this.oCoords.tl.y,this.oCoords.tr.y,this.oCoords.br.y,this.oCoords.bl.y],f=fabric.util.array.min(e),g=fabric.util.array.max(e),h=Math.abs(f-g);return{left:b,top:f,width:d,height:h}},getWidth:function(){return this.width*this.scaleX},getHeight:function(){return this.height*this.scaleY},_constrainScale:function(a){return Math.abs(a)<this.minScaleLimit?0>a?-this.minScaleLimit:this.minScaleLimit:a},scale:function(a){return a=this._constrainScale(a),0>a&&(this.flipX=!this.flipX,this.flipY=!this.flipY,a*=-1),this.scaleX=a,this.scaleY=a,this.setCoords(),this},scaleToWidth:function(a){var b=this.getBoundingRectWidth()/this.getWidth();return this.scale(a/this.width/b)},scaleToHeight:function(a){var b=this.getBoundingRectHeight()/this.getHeight();return this.scale(a/this.height/b)},setCoords:function(){var b=this.strokeWidth>1?this.strokeWidth:0,c=a(this.angle),d=this.getViewportTransform(),e=function(a){return fabric.util.transformPoint(a,d)},f=this.width,g=this.height,h="round"===this.strokeLineCap||"square"===this.strokeLineCap,i="line"===this.type&&1===this.width,j="line"===this.type&&1===this.height,k=h&&j||"line"!==this.type,l=h&&i||"line"!==this.type;i?f=b:j&&(g=b),k&&(f+=b),l&&(g+=b),this.currentWidth=f*this.scaleX,this.currentHeight=g*this.scaleY,this.currentWidth<0&&(this.currentWidth=Math.abs(this.currentWidth));var m=Math.sqrt(Math.pow(this.currentWidth/2,2)+Math.pow(this.currentHeight/2,2)),n=Math.atan(isFinite(this.currentHeight/this.currentWidth)?this.currentHeight/this.currentWidth:0),o=Math.cos(n+c)*m,p=Math.sin(n+c)*m,q=Math.sin(c),r=Math.cos(c),s=this.getCenterPoint(),t=new fabric.Point(this.currentWidth,this.currentHeight),u=new fabric.Point(s.x-o,s.y-p),v=new fabric.Point(u.x+t.x*r,u.y+t.x*q),w=new fabric.Point(u.x-t.y*q,u.y+t.y*r),x=new fabric.Point(u.x+t.x/2*r,u.y+t.x/2*q),y=e(u),z=e(v),A=e(new fabric.Point(v.x-t.y*q,v.y+t.y*r)),B=e(w),C=e(new fabric.Point(u.x-t.y/2*q,u.y+t.y/2*r)),D=e(x),E=e(new fabric.Point(v.x-t.y/2*q,v.y+t.y/2*r)),F=e(new fabric.Point(w.x+t.x/2*r,w.y+t.x/2*q)),G=e(new fabric.Point(x.x,x.y)),H=Math.cos(n+c)*this.padding*Math.sqrt(2),I=Math.sin(n+c)*this.padding*Math.sqrt(2);return y=y.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:y,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(a){return this.group?fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,a):this.canvas.sendBackwards(this,a),this},bringForward:function(a){return this.group?fabric.StaticCanvas.prototype.bringForward.call(this.group,this,a):this.canvas.bringForward(this,a),this},moveTo:function(a){return this.group?fabric.StaticCanvas.prototype.moveTo.call(this.group,this,a):this.canvas.moveTo(this,a),this}}),fabric.util.object.extend(fabric.Object.prototype,{getSvgStyles:function(){var a=this.fill?this.fill.toLive?"url(#SVGID_"+this.fill.id+")":this.fill:"none",b="destination-over"===this.fillRule?"evenodd":this.fillRule,c=this.stroke?this.stroke.toLive?"url(#SVGID_"+this.stroke.id+")":this.stroke:"none",d=this.strokeWidth?this.strokeWidth:"0",e=this.strokeDashArray?this.strokeDashArray.join(" "):"",f=this.strokeLineCap?this.strokeLineCap:"butt",g=this.strokeLineJoin?this.strokeLineJoin:"miter",h=this.strokeMiterLimit?this.strokeMiterLimit:"4",i="undefined"!=typeof this.opacity?this.opacity:"1",j=this.visible?"":" visibility: hidden;",k=this.shadow&&"text"!==this.type?"filter: url(#SVGID_"+this.shadow.id+");":"";return["stroke: ",c,"; ","stroke-width: ",d,"; ","stroke-dasharray: ",e,"; ","stroke-linecap: ",f,"; ","stroke-linejoin: ",g,"; ","stroke-miterlimit: ",h,"; ","fill: ",a,"; ","fill-rule: ",b,"; ","opacity: ",i,";",k,j].join("")},getSvgTransform:function(){if(this.group)return"";var a=fabric.util.toFixed,b=this.getAngle(),c=!this.canvas||this.canvas.svgViewportTransformation?this.getViewportTransform():[1,0,0,1,0,0],d=fabric.util.transformPoint(this.getCenterPoint(),c),e=fabric.Object.NUM_FRACTION_DIGITS,f="path-group"===this.type?"":"translate("+a(d.x,e)+" "+a(d.y,e)+")",g=0!==b?" rotate("+a(b,e)+")":"",h=1===this.scaleX&&1===this.scaleY&&1===c[0]&&1===c[3]?"":" scale("+a(this.scaleX*c[0],e)+" "+a(this.scaleY*c[3],e)+")",i="path-group"===this.type?this.width*c[0]:0,j=this.flipX?" matrix(-1 0 0 1 "+i+" 0) ":"",k="path-group"===this.type?this.height*c[3]:0,l=this.flipY?" matrix(1 0 0 -1 0 "+k+")":"";return[f,g,h,j,l].join("")},getSvgTransformMatrix:function(){return this.transformMatrix?" matrix("+this.transformMatrix.join(" ")+")":""},_createBaseSVGMarkup:function(){var a=[];return this.fill&&this.fill.toLive&&a.push(this.fill.toSVG(this,!1)),this.stroke&&this.stroke.toLive&&a.push(this.stroke.toSVG(this,!1)),this.shadow&&a.push(this.shadow.toSVG(this)),a}}),fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(){return this.stateProperties.some(function(a){return this.get(a)!==this.originalState[a]},this)},saveState:function(a){return this.stateProperties.forEach(function(a){this.originalState[a]=this.get(a)},this),a&&a.stateProperties&&a.stateProperties.forEach(function(a){this.originalState[a]=this.get(a)},this),this},setupState:function(){return this.originalState={},this.saveState(),this}}),function(a){"use strict";function b(a,b){var c=a.origin,d=a.axis1,e=a.axis2,f=a.dimension,g=b.nearest,h=b.center,i=b.farthest;return function(){switch(this.get(c)){case g:return Math.min(this.get(d),this.get(e));case h:return Math.min(this.get(d),this.get(e))+.5*this.get(f);case i:return Math.max(this.get(d),this.get(e))}}}var c=a.fabric||(a.fabric={}),d=c.util.object.extend,e={x1:1,x2:1,y1:1,y2:1},f=c.StaticCanvas.supports("setLineDash");return c.Line?void c.warn("fabric.Line is already defined"):(c.Line=c.util.createClass(c.Object,{type:"line",x1:0,y1:0,x2:0,y2:0,initialize:function(a,b){b=b||{},a||(a=[0,0,0,0]),this.callSuper("initialize",b),this.set("x1",a[0]),this.set("y1",a[1]),this.set("x2",a[2]),this.set("y2",a[3]),this._setWidthHeight(b)},_setWidthHeight:function(a){a||(a={}),this.width=Math.abs(this.x2-this.x1)||1,this.height=Math.abs(this.y2-this.y1)||1,this.left="left"in a?a.left:this._getLeftToOriginX(),this.top="top"in a?a.top:this._getTopToOriginY()},_set:function(a,b){return this[a]=b,"undefined"!=typeof e[a]&&this._setWidthHeight(),this},_getLeftToOriginX:b({origin:"originX",axis1:"x1",axis2:"x2",dimension:"width"},{nearest:"left",center:"center",farthest:"right"}),_getTopToOriginY:b({origin:"originY",axis1:"y1",axis2:"y2",dimension:"height"},{nearest:"top",center:"center",farthest:"bottom"}),_render:function(a,b){if(a.beginPath(),b){var c=this.getCenterPoint();a.translate(c.x,c.y)}if(!this.strokeDashArray||this.strokeDashArray&&f){var d=this.x1<=this.x2?-1:1,e=this.y1<=this.y2?-1:1;a.moveTo(1===this.width?0:d*this.width/2,1===this.height?0:e*this.height/2),a.lineTo(1===this.width?0:-1*d*this.width/2,1===this.height?0:-1*e*this.height/2)}a.lineWidth=this.strokeWidth;var g=a.strokeStyle;a.strokeStyle=this.stroke||a.fillStyle,this.stroke&&this._renderStroke(a),a.strokeStyle=g},_renderDashedStroke:function(a){var b=this.x1<=this.x2?-1:1,d=this.y1<=this.y2?-1:1,e=1===this.width?0:b*this.width/2,f=1===this.height?0:d*this.height/2;a.beginPath(),c.util.drawDashedLine(a,e,f,-e,-f,this.strokeDashArray),a.closePath()},toObject:function(a){return d(this.callSuper("toObject",a),{x1:this.get("x1"),y1:this.get("y1"),x2:this.get("x2"),y2:this.get("y2")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c="";if(!this.group){var d=-this.width/2-(this.x1>this.x2?this.x2:this.x1),e=-this.height/2-(this.y1>this.y2?this.y2:this.y1);c="translate("+d+", "+e+") "}return b.push("<line ",'x1="',this.x1,'" y1="',this.y1,'" x2="',this.x2,'" y2="',this.y2,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),c,this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),c.Line.ATTRIBUTE_NAMES=c.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),c.Line.fromElement=function(a,b){var e=c.parseAttributes(a,c.Line.ATTRIBUTE_NAMES),f=[e.x1||0,e.y1||0,e.x2||0,e.y2||0];return new c.Line(f,d(e,b))},void(c.Line.fromObject=function(a){var b=[a.x1,a.y1,a.x2,a.y2];return new c.Line(b,a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){return"radius"in a&&a.radius>0}var c=a.fabric||(a.fabric={}),d=2*Math.PI,e=c.util.object.extend;return c.Circle?void c.warn("fabric.Circle is already defined."):(c.Circle=c.util.createClass(c.Object,{type:"circle",radius:0,initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("radius",a.radius||0)},_set:function(a,b){return this.callSuper("_set",a,b),"radius"===a&&this.setRadius(b),this},toObject:function(a){return e(this.callSuper("toObject",a),{radius:this.get("radius")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=0,d=0;return this.group&&(c=this.left+this.radius,d=this.top+this.radius),b.push("<circle ",'cx="'+c+'" cy="'+d+'" ','r="',this.radius,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},_render:function(a,b){a.beginPath(),a.arc(b?this.left+this.radius:0,b?this.top+this.radius:0,this.radius,0,d,!1),this._renderFill(a),this._renderStroke(a)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(a){this.radius=a,this.set("width",2*a).set("height",2*a)},complexity:function(){return 1}}),c.Circle.ATTRIBUTE_NAMES=c.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),c.Circle.fromElement=function(a,d){d||(d={});var f=c.parseAttributes(a,c.Circle.ATTRIBUTE_NAMES);if(!b(f))throw new Error("value of `r` attribute is required and can not be negative");f.left=f.left||0,f.top=f.top||0;var g=new c.Circle(e(f,d));return g.left-=g.radius,g.top-=g.radius,g},void(c.Circle.fromObject=function(a){return new c.Circle(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});return b.Triangle?void b.warn("fabric.Triangle is already defined"):(b.Triangle=b.util.createClass(b.Object,{type:"triangle",initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("width",a.width||100).set("height",a.height||100)},_render:function(a){var b=this.width/2,c=this.height/2;a.beginPath(),a.moveTo(-b,c),a.lineTo(0,-c),a.lineTo(b,c),a.closePath(),this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c=this.width/2,d=this.height/2;a.beginPath(),b.util.drawDashedLine(a,-c,d,0,-d,this.strokeDashArray),b.util.drawDashedLine(a,0,-d,c,d,this.strokeDashArray),b.util.drawDashedLine(a,c,d,-c,d,this.strokeDashArray),a.closePath()},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=this.width/2,d=this.height/2,e=[-c+" "+d,"0 "+-d,c+" "+d].join(",");return b.push("<polygon ",'points="',e,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),'"/>'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),void(b.Triangle.fromObject=function(a){return new b.Triangle(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=2*Math.PI,d=b.util.object.extend;return b.Ellipse?void b.warn("fabric.Ellipse is already defined."):(b.Ellipse=b.util.createClass(b.Object,{type:"ellipse",rx:0,ry:0,initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("rx",a.rx||0),this.set("ry",a.ry||0),this.set("width",2*this.get("rx")),this.set("height",2*this.get("ry"))},toObject:function(a){return d(this.callSuper("toObject",a),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=0,d=0;return this.group&&(c=this.left+this.rx,d=this.top+this.ry),b.push("<ellipse ",'cx="',c,'" cy="',d,'" ','rx="',this.rx,'" ry="',this.ry,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},_render:function(a,b){a.beginPath(),a.save(),a.transform(1,0,0,this.ry/this.rx,0,0),a.arc(b?this.left+this.rx:0,b?(this.top+this.ry)*this.rx/this.ry:0,this.rx,0,c,!1),a.restore(),this._renderFill(a),this._renderStroke(a)},complexity:function(){return 1}}),b.Ellipse.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),b.Ellipse.fromElement=function(a,c){c||(c={});var e=b.parseAttributes(a,b.Ellipse.ATTRIBUTE_NAMES);e.left=e.left||0,e.top=e.top||0;var f=new b.Ellipse(d(e,c));return f.top-=f.ry,f.left-=f.rx,f},void(b.Ellipse.fromObject=function(a){return new b.Ellipse(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;if(b.Rect)return void console.warn("fabric.Rect is already defined");var d=b.Object.prototype.stateProperties.concat();d.push("rx","ry","x","y"),b.Rect=b.util.createClass(b.Object,{stateProperties:d,type:"rect",rx:0,ry:0,strokeDashArray:null,initialize:function(a){a=a||{},this.callSuper("initialize",a),this._initRxRy()},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(a,b){if(1===this.width&&1===this.height)return void a.fillRect(0,0,1,1);var c=this.rx?Math.min(this.rx,this.width/2):0,d=this.ry?Math.min(this.ry,this.height/2):0,e=this.width,f=this.height,g=b?this.left:-this.width/2,h=b?this.top:-this.height/2,i=0!==c||0!==d,j=.4477152502;a.beginPath(),a.moveTo(g+c,h),a.lineTo(g+e-c,h),i&&a.bezierCurveTo(g+e-j*c,h,g+e,h+j*d,g+e,h+d),a.lineTo(g+e,h+f-d),i&&a.bezierCurveTo(g+e,h+f-j*d,g+e-j*c,h+f,g+e-c,h+f),a.lineTo(g+c,h+f),i&&a.bezierCurveTo(g+j*c,h+f,g,h+f-j*d,g,h+f-d),a.lineTo(g,h+d),i&&a.bezierCurveTo(g,h+j*d,g+j*c,h,g+c,h),a.closePath(),this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c=-this.width/2,d=-this.height/2,e=this.width,f=this.height;a.beginPath(),b.util.drawDashedLine(a,c,d,c+e,d,this.strokeDashArray),b.util.drawDashedLine(a,c+e,d,c+e,d+f,this.strokeDashArray),b.util.drawDashedLine(a,c+e,d+f,c,d+f,this.strokeDashArray),b.util.drawDashedLine(a,c,d+f,c,d,this.strokeDashArray),a.closePath()},toObject:function(a){var b=c(this.callSuper("toObject",a),{rx:this.get("rx")||0,ry:this.get("ry")||0});return this.includeDefaultValues||this._removeDefaultValues(b),b},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=this.left,d=this.top;return this.group||(c=-this.width/2,d=-this.height/2),b.push("<rect ",'x="',c,'" y="',d,'" rx="',this.get("rx"),'" ry="',this.get("ry"),'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),b.Rect.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),b.Rect.fromElement=function(a,d){if(!a)return null;d=d||{};var e=b.parseAttributes(a,b.Rect.ATTRIBUTE_NAMES);return e.left=e.left||0,e.top=e.top||0,new b.Rect(c(d?b.util.object.clone(d):{},e))},b.Rect.fromObject=function(a){return new b.Rect(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.toFixed;return b.Polyline?void b.warn("fabric.Polyline is already defined"):(b.Polyline=b.util.createClass(b.Object,{type:"polyline",points:null,initialize:function(a,b){b=b||{},this.set("points",a),this.callSuper("initialize",b),this._calcDimensions()},_calcDimensions:function(){return b.Polygon.prototype._calcDimensions.call(this)},_applyPointOffset:function(){return b.Polygon.prototype._applyPointOffset.call(this)},toObject:function(a){return b.Polygon.prototype.toObject.call(this,a)},toSVG:function(a){for(var b=[],d=this._createBaseSVGMarkup(),e=0,f=this.points.length;f>e;e++)b.push(c(this.points[e].x,2),",",c(this.points[e].y,2)," ");return d.push("<polyline ",'points="',b.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(d.join("")):d.join("")},_render:function(a){var b;a.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),a.moveTo(this.points[0].x,this.points[0].y);for(var c=0,d=this.points.length;d>c;c++)b=this.points[c],a.lineTo(b.x,b.y);this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c,d;a.beginPath();for(var e=0,f=this.points.length;f>e;e++)c=this.points[e],d=this.points[e+1]||c,b.util.drawDashedLine(a,c.x,c.y,d.x,d.y,this.strokeDashArray)},complexity:function(){return this.get("points").length}}),b.Polyline.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat(),b.Polyline.fromElement=function(a,c){if(!a)return null;c||(c={});var d=b.parsePointsAttribute(a.getAttribute("points")),e=b.parseAttributes(a,b.Polyline.ATTRIBUTE_NAMES);return null===d?null:new b.Polyline(d,b.util.object.extend(e,c))},void(b.Polyline.fromObject=function(a){var c=a.points;return new b.Polyline(c,a,!0)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.min,e=b.util.array.max,f=b.util.toFixed;return b.Polygon?void b.warn("fabric.Polygon is already defined"):(b.Polygon=b.util.createClass(b.Object,{type:"polygon",points:null,initialize:function(a,b){b=b||{},this.points=a,this.callSuper("initialize",b),this._calcDimensions()},_calcDimensions:function(){var a=this.points,b=d(a,"x"),c=d(a,"y"),f=e(a,"x"),g=e(a,"y");this.width=f-b||1,this.height=g-c||1,this.left=b,this.top=c},_applyPointOffset:function(){this.points.forEach(function(a){a.x-=this.left+this.width/2,a.y-=this.top+this.height/2},this)},toObject:function(a){return c(this.callSuper("toObject",a),{points:this.points.concat()})},toSVG:function(a){for(var b=[],c=this._createBaseSVGMarkup(),d=0,e=this.points.length;e>d;d++)b.push(f(this.points[d].x,2),",",f(this.points[d].y,2)," ");return c.push("<polygon ",'points="',b.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(c.join("")):c.join("")},_render:function(a){var b;a.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),a.moveTo(this.points[0].x,this.points[0].y);for(var c=0,d=this.points.length;d>c;c++)b=this.points[c],a.lineTo(b.x,b.y);this._renderFill(a),(this.stroke||this.strokeDashArray)&&(a.closePath(),this._renderStroke(a))},_renderDashedStroke:function(a){var c,d;a.beginPath();for(var e=0,f=this.points.length;f>e;e++)c=this.points[e],d=this.points[e+1]||this.points[0],b.util.drawDashedLine(a,c.x,c.y,d.x,d.y,this.strokeDashArray);a.closePath()},complexity:function(){return this.points.length}}),b.Polygon.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat(),b.Polygon.fromElement=function(a,d){if(!a)return null;d||(d={});var e=b.parsePointsAttribute(a.getAttribute("points")),f=b.parseAttributes(a,b.Polygon.ATTRIBUTE_NAMES);return null===e?null:new b.Polygon(e,c(f,d))},void(b.Polygon.fromObject=function(a){return new b.Polygon(a.points,a,!0)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){return"H"===a[0]?a[1]:a[a.length-2]}function c(a){return"V"===a[0]?a[1]:a[a.length-1]}var d=a.fabric||(a.fabric={}),e=d.util.array.min,f=d.util.array.max,g=d.util.object.extend,h=Object.prototype.toString,i=d.util.drawArc,j={m:2,l:2,h:1,v:1,c:6,s:4,q:4,t:2,a:7},k={m:"l",M:"L"};return d.Path?void d.warn("fabric.Path is already defined"):(d.Path=d.util.createClass(d.Object,{type:"path",path:null,initialize:function(a,b){if(b=b||{},this.setOptions(b),!a)throw new Error("`path` argument is required");var c="[object Array]"===h.call(a);this.path=c?a:a.match&&a.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi),this.path&&(c||(this.path=this._parsePath()),this._initializePath(b),b.sourcePath&&this.setSourcePath(b.sourcePath))},_initializePath:function(a){var b="width"in a&&null!=a.width,c="height"in a&&null!=a.width,d="left"in a,e="top"in a,f=d?this.left:0,h=e?this.top:0;b&&c?(e||(this.top=this.height/2),d||(this.left=this.width/2)):(g(this,this._parseDimensions()),b&&(this.width=a.width),c&&(this.height=a.height)),this.pathOffset=this.pathOffset||this._calculatePathOffset(f,h)},_calculatePathOffset:function(a,b){return{x:this.left-a-this.width/2,y:this.top-b-this.height/2}},_render:function(a,b){var c,d,e,f,g,h=null,j=0,k=0,l=0,m=0,n=0,o=0,p=-(this.width/2+this.pathOffset.x),q=-(this.height/2+this.pathOffset.y);b&&(p+=this.width/2,q+=this.height/2);for(var r=0,s=this.path.length;s>r;++r){switch(c=this.path[r],c[0]){case"l":l+=c[1],m+=c[2],a.lineTo(l+p,m+q);break;case"L":l=c[1],m=c[2],a.lineTo(l+p,m+q);break;case"h":l+=c[1],a.lineTo(l+p,m+q);break;case"H":l=c[1],a.lineTo(l+p,m+q);break;case"v":m+=c[1],a.lineTo(l+p,m+q);break;case"V":m=c[1],a.lineTo(l+p,m+q);break;case"m":l+=c[1],m+=c[2],j=l,k=m,a.moveTo(l+p,m+q);break;case"M":l=c[1],m=c[2],j=l,k=m,a.moveTo(l+p,m+q);break;case"c":d=l+c[5],e=m+c[6],n=l+c[3],o=m+c[4],a.bezierCurveTo(l+c[1]+p,m+c[2]+q,n+p,o+q,d+p,e+q),l=d,m=e;break;case"C":l=c[5],m=c[6],n=c[3],o=c[4],a.bezierCurveTo(c[1]+p,c[2]+q,n+p,o+q,l+p,m+q);break;case"s":d=l+c[3],e=m+c[4],n=n?2*l-n:l,o=o?2*m-o:m,a.bezierCurveTo(n+p,o+q,l+c[1]+p,m+c[2]+q,d+p,e+q),n=l+c[1],o=m+c[2],l=d,m=e;break;case"S":d=c[3],e=c[4],n=2*l-n,o=2*m-o,a.bezierCurveTo(n+p,o+q,c[1]+p,c[2]+q,d+p,e+q),l=d,m=e,n=c[1],o=c[2];break;case"q":d=l+c[3],e=m+c[4],n=l+c[1],o=m+c[2],a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e;break;case"Q":d=c[3],e=c[4],a.quadraticCurveTo(c[1]+p,c[2]+q,d+p,e+q),l=d,m=e,n=c[1],o=c[2];break;case"t":d=l+c[1],e=m+c[2],null===h[0].match(/[QqTt]/)?(n=l,o=m):"t"===h[0]?(n=2*l-f,o=2*m-g):"q"===h[0]&&(n=2*l-n,o=2*m-o),f=n,g=o,a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e,n=l+c[1],o=m+c[2];break;case"T":d=c[1],e=c[2],n=2*l-n,o=2*m-o,a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e;break;case"a":i(a,l+p,m+q,[c[1],c[2],c[3],c[4],c[5],c[6]+l+p,c[7]+m+q]),l+=c[6],m+=c[7];break;case"A":i(a,l+p,m+q,[c[1],c[2],c[3],c[4],c[5],c[6]+p,c[7]+q]),l=c[6],m=c[7];break;case"z":case"Z":l=j,m=k,a.closePath()}h=c}},render:function(a,b){if(this.visible){a.save(),b&&a.translate(-this.width/2,-this.height/2);var c=this.transformMatrix;c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),b||this.transform(a),this._setStrokeStyles(a),this._setFillStyles(a),this._setShadow(a),this.clipTo&&d.util.clipContext(this,a),a.beginPath(),a.globalAlpha=this.group?a.globalAlpha*this.opacity:this.opacity,this._render(a,b),this._renderFill(a),this._renderStroke(a),this.clipTo&&a.restore(),this._removeShadow(a),a.restore()}},toString:function(){return"#<fabric.Path ("+this.complexity()+'): { "top": '+this.top+', "left": '+this.left+" }>"},toObject:function(a){var b=g(this.callSuper("toObject",a),{path:this.path.map(function(a){return a.slice()}),pathOffset:this.pathOffset});return this.sourcePath&&(b.sourcePath=this.sourcePath),this.transformMatrix&&(b.transformMatrix=this.transformMatrix),b},toDatalessObject:function(a){var b=this.toObject(a);return this.sourcePath&&(b.path=this.sourcePath),delete b.sourcePath,b},toSVG:function(a){for(var b=[],c=this._createBaseSVGMarkup(),d=0,e=this.path.length;e>d;d++)b.push(this.path[d].join(" "));
-var f=b.join(" ");return c.push("<path ",'d="',f,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'" stroke-linecap="round" ',"/>\n"),a?a(c.join("")):c.join("")},complexity:function(){return this.path.length},_parsePath:function(){for(var a,b,c,d,e,f=[],g=[],h=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi,i=0,l=this.path.length;l>i;i++){for(a=this.path[i],d=a.slice(1).trim(),g.length=0;c=h.exec(d);)g.push(c[0]);e=[a.charAt(0)];for(var m=0,n=g.length;n>m;m++)b=parseFloat(g[m]),isNaN(b)||e.push(b);var o=e[0],p=j[o.toLowerCase()],q=k[o]||o;if(e.length-1>p)for(var r=1,s=e.length;s>r;r+=p)f.push([o].concat(e.slice(r,r+p))),o=q;else f.push(e)}return f},_parseDimensions:function(){var a=[],b=[],c={};this.path.forEach(function(d,e){this._getCoordsFromCommand(d,e,a,b,c)},this);var d=e(a),g=e(b),h=f(a),i=f(b),j=h-d,k=i-g,l={left:this.left+(d+j/2),top:this.top+(g+k/2),width:j,height:k};return l},_getCoordsFromCommand:function(a,d,e,f,g){var h=!1;"H"!==a[0]&&(g.x=b(0===d?a:this.path[d-1])),"V"!==a[0]&&(g.y=c(0===d?a:this.path[d-1])),a[0]===a[0].toLowerCase()&&(h=!0);var i,j=this._getXY(a,h,g);i=parseInt(j.x,10),isNaN(i)||e.push(i),i=parseInt(j.y,10),isNaN(i)||f.push(i)},_getXY:function(a,d,e){var f=d?e.x+b(a):"V"===a[0]?e.x:b(a),g=d?e.y+c(a):"H"===a[0]?e.y:c(a);return{x:f,y:g}}}),d.Path.fromObject=function(a,b){"string"==typeof a.path?d.loadSVGFromURL(a.path,function(c){var e=c[0],f=a.path;delete a.path,d.util.object.extend(e,a),e.setSourcePath(f),b(e)}):b(new d.Path(a.path,a))},d.Path.ATTRIBUTE_NAMES=d.SHARED_ATTRIBUTES.concat(["d"]),d.Path.fromElement=function(a,b,c){var e=d.parseAttributes(a,d.Path.ATTRIBUTE_NAMES);b&&b(new d.Path(e.d,g(e,c)))},void(d.Path.async=!0))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.invoke,e=b.Object.prototype.toObject;return b.PathGroup?void b.warn("fabric.PathGroup is already defined"):(b.PathGroup=b.util.createClass(b.Path,{type:"path-group",fill:"",initialize:function(a,b){b=b||{},this.paths=a||[];for(var c=this.paths.length;c--;)this.paths[c].group=this;this.setOptions(b),b.widthAttr&&(this.scaleX=b.widthAttr/b.width),b.heightAttr&&(this.scaleY=b.heightAttr/b.height),this.setCoords(),b.sourcePath&&this.setSourcePath(b.sourcePath)},render:function(a){if(this.visible){a.save();var c=this.transformMatrix;c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),this.transform(a),this._setShadow(a),this.clipTo&&b.util.clipContext(this,a);for(var d=0,e=this.paths.length;e>d;++d)this.paths[d].render(a,!0);this.clipTo&&a.restore(),this._removeShadow(a),a.restore()}},_set:function(a,b){if("fill"===a&&b&&this.isSameColor())for(var c=this.paths.length;c--;)this.paths[c]._set(a,b);return this.callSuper("_set",a,b)},toObject:function(a){var b=c(e.call(this,a),{paths:d(this.getObjects(),"toObject",a)});return this.sourcePath&&(b.sourcePath=this.sourcePath),b},toDatalessObject:function(a){var b=this.toObject(a);return this.sourcePath&&(b.paths=this.sourcePath),b},toSVG:function(a){for(var b=this.getObjects(),c="translate("+this.left+" "+this.top+")",d=["<g ",'style="',this.getSvgStyles(),'" ','transform="',c,this.getSvgTransform(),'" ',">\n"],e=0,f=b.length;f>e;e++)d.push(b[e].toSVG(a));return d.push("</g>\n"),a?a(d.join("")):d.join("")},toString:function(){return"#<fabric.PathGroup ("+this.complexity()+"): { top: "+this.top+", left: "+this.left+" }>"},isSameColor:function(){var a=(this.getObjects()[0].get("fill")||"").toLowerCase();return this.getObjects().every(function(b){return(b.get("fill")||"").toLowerCase()===a})},complexity:function(){return this.paths.reduce(function(a,b){return a+(b&&b.complexity?b.complexity():0)},0)},getObjects:function(){return this.paths}}),b.PathGroup.fromObject=function(a,c){"string"==typeof a.paths?b.loadSVGFromURL(a.paths,function(d){var e=a.paths;delete a.paths;var f=b.util.groupSVGElements(d,a,e);c(f)}):b.util.enlivenObjects(a.paths,function(d){delete a.paths,c(new b.PathGroup(d,a))})},void(b.PathGroup.async=!0))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.min,e=b.util.array.max,f=b.util.array.invoke;if(!b.Group){var g={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};b.Group=b.util.createClass(b.Object,b.Collection,{type:"group",initialize:function(a,b){b=b||{},this._objects=a||[];for(var d=this._objects.length;d--;)this._objects[d].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),b&&c(this,b),this._setOpacityIfSame(),this.setCoords(),this.saveCoords()},_updateObjectsCoords:function(){this.forEachObject(this._updateObjectCoords,this)},_updateObjectCoords:function(a){var b=a.getLeft(),c=a.getTop();a.set({originalLeft:b,originalTop:c,left:b-this.left,top:c-this.top}),a.setCoords(),a.__origHasControls=a.hasControls,a.hasControls=!1},toString:function(){return"#<fabric.Group: ("+this.complexity()+")>"},addWithUpdate:function(a){return this._restoreObjectsState(),a&&(this._objects.push(a),a.group=this),this.forEachObject(this._setObjectActive,this),this._calcBounds(),this._updateObjectsCoords(),this},_setObjectActive:function(a){a.set("active",!0),a.group=this},removeWithUpdate:function(a){return this._moveFlippedObject(a),this._restoreObjectsState(),this.forEachObject(this._setObjectActive,this),this.remove(a),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(a){a.group=this},_onObjectRemoved:function(a){delete a.group,a.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(a,b){if(a in this.delegatedProperties){var c=this._objects.length;for(this[a]=b;c--;)this._objects[c].set(a,b)}else this[a]=b},toObject:function(a){return c(this.callSuper("toObject",a),{objects:f(this._objects,"toObject",a)})},render:function(a){if(this.visible){a.save(),this.clipTo&&b.util.clipContext(this,a);for(var c=0,d=this._objects.length;d>c;c++)this._renderObject(this._objects[c],a);this.clipTo&&a.restore(),a.restore()}},_renderControls:function(a,b){this.callSuper("_renderControls",a,b);for(var c=0,d=this._objects.length;d>c;c++)this._objects[c]._renderControls(a)},_renderObject:function(a,b){var c=a.hasRotatingPoint;a.visible&&(a.hasRotatingPoint=!1,a.render(b),a.hasRotatingPoint=c)},_restoreObjectsState:function(){return this._objects.forEach(this._restoreObjectState,this),this},_moveFlippedObject:function(a){var b=a.get("originX"),c=a.get("originY"),d=a.getCenterPoint();a.set({originX:"center",originY:"center",left:d.x,top:d.y}),this._toggleFlipping(a);var e=a.getPointByOrigin(b,c);return a.set({originX:b,originY:c,left:e.x,top:e.y}),this},_toggleFlipping:function(a){this.flipX&&(a.toggle("flipX"),a.set("left",-a.get("left")),a.setAngle(-a.getAngle())),this.flipY&&(a.toggle("flipY"),a.set("top",-a.get("top")),a.setAngle(-a.getAngle()))},_restoreObjectState:function(a){return this._setObjectPosition(a),a.setCoords(),a.hasControls=a.__origHasControls,delete a.__origHasControls,a.set("active",!1),a.setCoords(),delete a.group,this},_setObjectPosition:function(a){var b=this.getLeft(),c=this.getTop(),d=this._getRotatedLeftTop(a);a.set({angle:a.getAngle()+this.getAngle(),left:b+d.left,top:c+d.top,scaleX:a.get("scaleX")*this.get("scaleX"),scaleY:a.get("scaleY")*this.get("scaleY")})},_getRotatedLeftTop:function(a){var b=this.getAngle()*(Math.PI/180);return{left:-Math.sin(b)*a.getTop()*this.get("scaleY")+Math.cos(b)*a.getLeft()*this.get("scaleX"),top:Math.cos(b)*a.getTop()*this.get("scaleY")+Math.sin(b)*a.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(a){a.setCoords()}),this},_setOpacityIfSame:function(){var a=this.getObjects(),b=a[0]?a[0].get("opacity"):1,c=a.every(function(a){return a.get("opacity")===b});c&&(this.opacity=b)},_calcBounds:function(a){for(var b,c=[],d=[],e=0,f=this._objects.length;f>e;++e){b=this._objects[e],b.setCoords();for(var g in b.oCoords)c.push(b.oCoords[g].x),d.push(b.oCoords[g].y)}this.set(this._getBounds(c,d,a))},_getBounds:function(a,c,f){var g=b.util.invertTransform(this.getViewportTransform()),h=b.util.transformPoint(new b.Point(d(a),d(c)),g),i=b.util.transformPoint(new b.Point(e(a),e(c)),g),j={width:i.x-h.x||0,height:i.y-h.y||0};return f||(j.left=(h.x+i.x)/2||0,j.top=(h.y+i.y)/2||0),j},toSVG:function(a){for(var b=["<g ",'transform="',this.getSvgTransform(),'">\n'],c=0,d=this._objects.length;d>c;c++)b.push(this._objects[c].toSVG(a));return b.push("</g>\n"),a?a(b.join("")):b.join("")},get:function(a){if(a in g){if(this[a])return this[a];for(var b=0,c=this._objects.length;c>b;b++)if(this._objects[b][a])return!0;return!1}return a in this.delegatedProperties?this._objects[0]&&this._objects[0].get(a):this[a]}}),b.Group.fromObject=function(a,c){b.util.enlivenObjects(a.objects,function(d){delete a.objects,c&&c(new b.Group(d,a))})},b.Group.async=!0}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=fabric.util.object.extend;return a.fabric||(a.fabric={}),a.fabric.Image?void fabric.warn("fabric.Image is already defined."):(fabric.Image=fabric.util.createClass(fabric.Object,{type:"image",crossOrigin:"",initialize:function(a,b){b||(b={}),this.filters=[],this.callSuper("initialize",b),this._initElement(a,b),this._initConfig(b),b.filters&&(this.filters=b.filters,this.applyFilters())},getElement:function(){return this._element},setElement:function(a,b){return this._element=a,this._originalElement=a,this._initConfig(),0!==this.filters.length&&this.applyFilters(b),this},setCrossOrigin:function(a){return this.crossOrigin=a,this._element.crossOrigin=a,this},getOriginalSize:function(){var a=this.getElement();return{width:a.width,height:a.height}},_stroke:function(a){a.save(),this._setStrokeStyles(a),a.beginPath(),a.strokeRect(-this.width/2,-this.height/2,this.width,this.height),a.closePath(),a.restore()},_renderDashedStroke:function(a){var b=-this.width/2,c=-this.height/2,d=this.width,e=this.height;a.save(),this._setStrokeStyles(a),a.beginPath(),fabric.util.drawDashedLine(a,b,c,b+d,c,this.strokeDashArray),fabric.util.drawDashedLine(a,b+d,c,b+d,c+e,this.strokeDashArray),fabric.util.drawDashedLine(a,b+d,c+e,b,c+e,this.strokeDashArray),fabric.util.drawDashedLine(a,b,c+e,b,c,this.strokeDashArray),a.closePath(),a.restore()},toObject:function(a){return b(this.callSuper("toObject",a),{src:this._originalElement.src||this._originalElement._src,filters:this.filters.map(function(a){return a&&a.toObject()}),crossOrigin:this.crossOrigin})},toSVG:function(a){var b=[],c=-this.width/2,d=-this.height/2;if(this.group&&(c=this.left,d=this.top),b.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n','<image xlink:href="',this.getSvgSrc(),'" x="',c,'" y="',d,'" style="',this.getSvgStyles(),'" width="',this.width,'" height="',this.height,'" preserveAspectRatio="none"',"></image>\n"),this.stroke||this.strokeDashArray){var e=this.fill;this.fill=null,b.push("<rect ",'x="',c,'" y="',d,'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'"/>\n'),this.fill=e}return b.push("</g>\n"),a?a(b.join("")):b.join("")},getSrc:function(){return this.getElement()?this.getElement().src||this.getElement()._src:void 0},toString:function(){return'#<fabric.Image: { src: "'+this.getSrc()+'" }>'},clone:function(a,b){this.constructor.fromObject(this.toObject(b),a)},applyFilters:function(a){if(this._originalElement){if(0===this.filters.length)return this._element=this._originalElement,void(a&&a());var b=this._originalElement,c=fabric.util.createCanvasElement(),d=fabric.util.createImage(),e=this;return c.width=b.width,c.height=b.height,c.getContext("2d").drawImage(b,0,0,b.width,b.height),this.filters.forEach(function(a){a&&a.applyTo(c)}),d.width=b.width,d.height=b.height,fabric.isLikelyNode?(d.src=c.toBuffer(undefined,fabric.Image.pngCompression),e._element=d,a&&a()):(d.onload=function(){e._element=d,a&&a(),d.onload=c=b=null},d.src=c.toDataURL("image/png")),this}},_render:function(a,b){this._element&&a.drawImage(this._element,b?this.left:-this.width/2,b?this.top:-this.height/2,this.width,this.height),this._renderStroke(a)},_resetWidthHeight:function(){var a=this.getElement();this.set("width",a.width),this.set("height",a.height)},_initElement:function(a){this.setElement(fabric.util.getById(a)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(a){a||(a={}),this.setOptions(a),this._setWidthHeight(a),this._element&&this.crossOrigin&&(this._element.crossOrigin=this.crossOrigin)},_initFilters:function(a,b){a.filters&&a.filters.length?fabric.util.enlivenObjects(a.filters,function(a){b&&b(a)},"fabric.Image.filters"):b&&b()},_setWidthHeight:function(a){this.width="width"in a?a.width:this.getElement()?this.getElement().width||0:0,this.height="height"in a?a.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(a,b){fabric.util.loadImage(a.src,function(c){fabric.Image.prototype._initFilters.call(a,a,function(d){a.filters=d||[];var e=new fabric.Image(c,a);b&&b(e)})},null,a.crossOrigin)},fabric.Image.fromURL=function(a,b,c){fabric.util.loadImage(a,function(a){b(new fabric.Image(a,c))},null,c&&c.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(a,c,d){var e=fabric.parseAttributes(a,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(e["xlink:href"],c,b(d?fabric.util.object.clone(d):{},e))},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(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Brightness=b.util.createClass(b.Image.filters.BaseFilter,{type:"Brightness",initialize:function(a){a=a||{},this.brightness=a.brightness||0},applyTo:function(a){for(var b=a.getContext("2d"),c=b.getImageData(0,0,a.width,a.height),d=c.data,e=this.brightness,f=0,g=d.length;g>f;f+=4)d[f]+=e,d[f+1]+=e,d[f+2]+=e;b.putImageData(c,0,0)},toObject:function(){return c(this.callSuper("toObject"),{brightness:this.brightness})}}),b.Image.filters.Brightness.fromObject=function(a){return new b.Image.filters.Brightness(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Convolute=b.util.createClass(b.Image.filters.BaseFilter,{type:"Convolute",initialize:function(a){a=a||{},this.opaque=a.opaque,this.matrix=a.matrix||[0,0,0,0,1,0,0,0,0];var c=b.util.createCanvasElement();this.tmpCtx=c.getContext("2d")},_createImageData:function(a,b){return this.tmpCtx.createImageData(a,b)},applyTo:function(a){for(var b=this.matrix,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=Math.round(Math.sqrt(b.length)),f=Math.floor(e/2),g=d.data,h=d.width,i=d.height,j=h,k=i,l=this._createImageData(j,k),m=l.data,n=this.opaque?1:0,o=0;k>o;o++)for(var p=0;j>p;p++){for(var q=o,r=p,s=4*(o*j+p),t=0,u=0,v=0,w=0,x=0;e>x;x++)for(var y=0;e>y;y++){var z=q+x-f,A=r+y-f;if(!(0>z||z>i||0>A||A>h)){var B=4*(z*h+A),C=b[x*e+y];t+=g[B]*C,u+=g[B+1]*C,v+=g[B+2]*C,w+=g[B+3]*C}}m[s]=t,m[s+1]=u,m[s+2]=v,m[s+3]=w+n*(255-w)}c.putImageData(l,0,0)},toObject:function(){return c(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),b.Image.filters.Convolute.fromObject=function(a){return new b.Image.filters.Convolute(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.GradientTransparency=b.util.createClass(b.Image.filters.BaseFilter,{type:"GradientTransparency",initialize:function(a){a=a||{},this.threshold=a.threshold||100},applyTo:function(a){for(var b=a.getContext("2d"),c=b.getImageData(0,0,a.width,a.height),d=c.data,e=this.threshold,f=d.length,g=0,h=d.length;h>g;g+=4)d[g+3]=e+255*(f-g)/f;b.putImageData(c,0,0)},toObject:function(){return c(this.callSuper("toObject"),{threshold:this.threshold})}}),b.Image.filters.GradientTransparency.fromObject=function(a){return new b.Image.filters.GradientTransparency(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Grayscale=b.util.createClass(b.Image.filters.BaseFilter,{type:"Grayscale",applyTo:function(a){for(var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=d.width*d.height*4,g=0;f>g;)b=(e[g]+e[g+1]+e[g+2])/3,e[g]=b,e[g+1]=b,e[g+2]=b,g+=4;c.putImageData(d,0,0)}}),b.Image.filters.Grayscale.fromObject=function(){return new b.Image.filters.Grayscale}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Invert=b.util.createClass(b.Image.filters.BaseFilter,{type:"Invert",applyTo:function(a){var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=e.length;for(b=0;f>b;b+=4)e[b]=255-e[b],e[b+1]=255-e[b+1],e[b+2]=255-e[b+2];c.putImageData(d,0,0)}}),b.Image.filters.Invert.fromObject=function(){return new b.Image.filters.Invert}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Mask=b.util.createClass(b.Image.filters.BaseFilter,{type:"Mask",initialize:function(a){a=a||{},this.mask=a.mask,this.channel=[0,1,2,3].indexOf(a.channel)>-1?a.channel:0},applyTo:function(a){if(this.mask){var c,d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=e.data,g=this.mask.getElement(),h=b.util.createCanvasElement(),i=this.channel,j=e.width*e.height*4;h.width=g.width,h.height=g.height,h.getContext("2d").drawImage(g,0,0,g.width,g.height);var k=h.getContext("2d").getImageData(0,0,g.width,g.height),l=k.data;for(c=0;j>c;c+=4)f[c+3]=l[c+i];d.putImageData(e,0,0)}},toObject:function(){return c(this.callSuper("toObject"),{mask:this.mask.toObject(),channel:this.channel})}}),b.Image.filters.Mask.fromObject=function(a,c){b.util.loadImage(a.mask.src,function(d){a.mask=new b.Image(d,a.mask),c&&c(new b.Image.filters.Mask(a))})},b.Image.filters.Mask.async=!0}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Noise=b.util.createClass(b.Image.filters.BaseFilter,{type:"Noise",initialize:function(a){a=a||{},this.noise=a.noise||0},applyTo:function(a){for(var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=this.noise,g=0,h=e.length;h>g;g+=4)b=(.5-Math.random())*f,e[g]+=b,e[g+1]+=b,e[g+2]+=b;c.putImageData(d,0,0)},toObject:function(){return c(this.callSuper("toObject"),{noise:this.noise})}}),b.Image.filters.Noise.fromObject=function(a){return new b.Image.filters.Noise(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Pixelate=b.util.createClass(b.Image.filters.BaseFilter,{type:"Pixelate",initialize:function(a){a=a||{},this.blocksize=a.blocksize||4},applyTo:function(a){var b,c,d,e,f,g,h,i=a.getContext("2d"),j=i.getImageData(0,0,a.width,a.height),k=j.data,l=j.height,m=j.width;for(c=0;l>c;c+=this.blocksize)for(d=0;m>d;d+=this.blocksize){b=4*c*m+4*d,e=k[b],f=k[b+1],g=k[b+2],h=k[b+3];for(var n=c,o=c+this.blocksize;o>n;n++)for(var p=d,q=d+this.blocksize;q>p;p++)b=4*n*m+4*p,k[b]=e,k[b+1]=f,k[b+2]=g,k[b+3]=h}i.putImageData(j,0,0)},toObject:function(){return c(this.callSuper("toObject"),{blocksize:this.blocksize})}}),b.Image.filters.Pixelate.fromObject=function(a){return new b.Image.filters.Pixelate(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.RemoveWhite=b.util.createClass(b.Image.filters.BaseFilter,{type:"RemoveWhite",initialize:function(a){a=a||{},this.threshold=a.threshold||30,this.distance=a.distance||20},applyTo:function(a){for(var b,c,d,e=a.getContext("2d"),f=e.getImageData(0,0,a.width,a.height),g=f.data,h=this.threshold,i=this.distance,j=255-h,k=Math.abs,l=0,m=g.length;m>l;l+=4)b=g[l],c=g[l+1],d=g[l+2],b>j&&c>j&&d>j&&k(b-c)<i&&k(b-d)<i&&k(c-d)<i&&(g[l+3]=1);e.putImageData(f,0,0)},toObject:function(){return c(this.callSuper("toObject"),{threshold:this.threshold,distance:this.distance})}}),b.Image.filters.RemoveWhite.fromObject=function(a){return new b.Image.filters.RemoveWhite(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Sepia=b.util.createClass(b.Image.filters.BaseFilter,{type:"Sepia",applyTo:function(a){var b,c,d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=e.data,g=f.length;for(b=0;g>b;b+=4)c=.3*f[b]+.59*f[b+1]+.11*f[b+2],f[b]=c+100,f[b+1]=c+50,f[b+2]=c+255;d.putImageData(e,0,0)}}),b.Image.filters.Sepia.fromObject=function(){return new b.Image.filters.Sepia}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Sepia2=b.util.createClass(b.Image.filters.BaseFilter,{type:"Sepia2",applyTo:function(a){var b,c,d,e,f=a.getContext("2d"),g=f.getImageData(0,0,a.width,a.height),h=g.data,i=h.length;for(b=0;i>b;b+=4)c=h[b],d=h[b+1],e=h[b+2],h[b]=(.393*c+.769*d+.189*e)/1.351,h[b+1]=(.349*c+.686*d+.168*e)/1.203,h[b+2]=(.272*c+.534*d+.131*e)/2.14;f.putImageData(g,0,0)}}),b.Image.filters.Sepia2.fromObject=function(){return new b.Image.filters.Sepia2}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Tint=b.util.createClass(b.Image.filters.BaseFilter,{type:"Tint",initialize:function(a){a=a||{},this.color=a.color||"#000000",this.opacity="undefined"!=typeof a.opacity?a.opacity:new b.Color(this.color).getAlpha()},applyTo:function(a){var c,d,e,f,g,h,i,j,k,l=a.getContext("2d"),m=l.getImageData(0,0,a.width,a.height),n=m.data,o=n.length;for(k=new b.Color(this.color).getSource(),d=k[0]*this.opacity,e=k[1]*this.opacity,f=k[2]*this.opacity,j=1-this.opacity,c=0;o>c;c+=4)g=n[c],h=n[c+1],i=n[c+2],n[c]=d+g*j,n[c+1]=e+h*j,n[c+2]=f+i*j;l.putImageData(m,0,0)},toObject:function(){return c(this.callSuper("toObject"),{color:this.color,opacity:this.opacity})}}),b.Image.filters.Tint.fromObject=function(a){return new b.Image.filters.Tint(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Multiply=b.util.createClass(b.Image.filters.BaseFilter,{type:"Multiply",initialize:function(a){a=a||{},this.color=a.color||"#000000"},applyTo:function(a){var c,d,e=a.getContext("2d"),f=e.getImageData(0,0,a.width,a.height),g=f.data,h=g.length;for(d=new b.Color(this.color).getSource(),c=0;h>c;c+=4)g[c]*=d[0]/255,g[c+1]*=d[1]/255,g[c+2]*=d[2]/255;e.putImageData(f,0,0)},toObject:function(){return c(this.callSuper("toObject"),{color:this.color})}}),b.Image.filters.Multiply.fromObject=function(a){return new b.Image.filters.Multiply(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric;b.Image.filters.Blend=b.util.createClass({type:"Blend",initialize:function(a){a=a||{},this.color=a.color||"#000",this.image=a.image||!1,this.mode=a.mode||"multiply",this.alpha=a.alpha||1},applyTo:function(a){var c,d,e,f,g,h,i,j=a.getContext("2d"),k=j.getImageData(0,0,a.width,a.height),l=k.data,m=!1;if(this.image){m=!0;var n=b.util.createCanvasElement();n.width=this.image.width,n.height=this.image.height;var o=new b.StaticCanvas(n);o.add(this.image);var p=o.getContext("2d");i=p.getImageData(0,0,o.width,o.height).data}else i=new b.Color(this.color).getSource(),c=i[0]*this.alpha,d=i[1]*this.alpha,e=i[2]*this.alpha;for(var q=0,r=l.length;r>q;q+=4)switch(f=l[q],g=l[q+1],h=l[q+2],m&&(c=i[q]*this.alpha,d=i[q+1]*this.alpha,e=i[q+2]*this.alpha),this.mode){case"multiply":l[q]=f*c/255,l[q+1]=g*d/255,l[q+2]=h*e/255;break;case"screen":l[q]=1-(1-f)*(1-c),l[q+1]=1-(1-g)*(1-d),l[q+2]=1-(1-h)*(1-e);break;case"add":l[q]=Math.min(255,f+c),l[q+1]=Math.min(255,g+d),l[q+2]=Math.min(255,h+e);break;case"diff":case"difference":l[q]=Math.abs(f-c),l[q+1]=Math.abs(g-d),l[q+2]=Math.abs(h-e);break;case"subtract":var s=f-c,t=g-d,u=h-e;l[q]=0>s?0:s,l[q+1]=0>t?0:t,l[q+2]=0>u?0:u;break;case"darken":l[q]=Math.min(f,c),l[q+1]=Math.min(g,d),l[q+2]=Math.min(h,e);break;case"lighten":l[q]=Math.max(f,c),l[q+1]=Math.max(g,d),l[q+2]=Math.max(h,e)}j.putImageData(k,0,0)}}),b.Image.filters.Blend.fromObject=function(a){return new b.Image.filters.Blend(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.object.clone,e=b.util.toFixed,f=b.StaticCanvas.supports("setLineDash");if(b.Text)return void b.warn("fabric.Text is already defined");var g=b.Object.prototype.stateProperties.concat();g.push("fontFamily","fontWeight","fontSize","text","textDecoration","textAlign","fontStyle","lineHeight","textBackgroundColor","useNative","path"),b.Text=b.util.createClass(b.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:g,stroke:null,shadow:null,initialize:function(a,b){b=b||{},this.text=a,this.__skipDimension=!0,this.setOptions(b),this.__skipDimension=!1,this._initDimensions()},_initDimensions:function(){if(!this.__skipDimension){var a=b.util.createCanvasElement();this._render(a.getContext("2d"))}},toString:function(){return"#<fabric.Text ("+this.complexity()+'): { "text": "'+this.text+'", "fontFamily": "'+this.fontFamily+'" }>'},_render:function(a){"undefined"==typeof Cufon||this.useNative===!0?this._renderViaNative(a):this._renderViaCufon(a)},_renderViaNative:function(a){var c=this.text.split(this._reNewline);this._setTextStyles(a),this.width=this._getTextWidth(a,c),this.height=this._getTextHeight(a,c),this.clipTo&&b.util.clipContext(this,a),this._renderTextBackground(a,c),this._translateForTextAlign(a),this._renderText(a,c),"left"!==this.textAlign&&"justify"!==this.textAlign&&a.restore(),this._renderTextDecoration(a,c),this.clipTo&&a.restore(),this._setBoundaries(a,c),this._totalLineHeight=0},_renderText:function(a,b){a.save(),this._setShadow(a),this._setupFillRule(a),this._renderTextFill(a,b),this._renderTextStroke(a,b),this._restoreFillRule(a),this._removeShadow(a),a.restore()},_translateForTextAlign:function(a){"left"!==this.textAlign&&"justify"!==this.textAlign&&(a.save(),a.translate("center"===this.textAlign?this.width/2:this.width,0))},_setBoundaries:function(a,b){this._boundaries=[];for(var c=0,d=b.length;d>c;c++){var e=this._getLineWidth(a,b[c]),f=this._getLineLeftOffset(e);this._boundaries.push({height:this.fontSize*this.lineHeight,width:e,left:f})}},_setTextStyles:function(a){this._setFillStyles(a),this._setStrokeStyles(a),a.textBaseline="alphabetic",this.skipTextAlign||(a.textAlign=this.textAlign),a.font=this._getFontDeclaration()},_getTextHeight:function(a,b){return this.fontSize*b.length*this.lineHeight},_getTextWidth:function(a,b){for(var c=a.measureText(b[0]||"|").width,d=1,e=b.length;e>d;d++){var f=a.measureText(b[d]).width;f>c&&(c=f)}return c},_renderChars:function(a,b,c,d,e){b[a](c,d,e)},_renderTextLine:function(a,b,c,d,e,f){if(e-=this.fontSize/4,"justify"!==this.textAlign)return void this._renderChars(a,b,c,d,e,f);var g=b.measureText(c).width,h=this.width;if(h>g)for(var i=c.split(/\s+/),j=b.measureText(c.replace(/\s+/g,"")).width,k=h-j,l=i.length-1,m=k/l,n=0,o=0,p=i.length;p>o;o++)this._renderChars(a,b,i[o],d+n,e,f),n+=b.measureText(i[o]).width+m;else this._renderChars(a,b,c,d,e,f)},_getLeftOffset:function(){return b.isLikelyNode?0:-this.width/2},_getTopOffset:function(){return-this.height/2},_renderTextFill:function(a,b){if(this.fill||this._skipFillStrokeCheck){this._boundaries=[];for(var c=0,d=0,e=b.length;e>d;d++){var f=this._getHeightOfLine(a,d,b);c+=f,this._renderTextLine("fillText",a,b[d],this._getLeftOffset(),this._getTopOffset()+c,d)}}},_renderTextStroke:function(a,b){if(this.stroke&&0!==this.strokeWidth||this._skipFillStrokeCheck){var c=0;a.save(),this.strokeDashArray&&(1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),f&&a.setLineDash(this.strokeDashArray)),a.beginPath();for(var d=0,e=b.length;e>d;d++){var g=this._getHeightOfLine(a,d,b);c+=g,this._renderTextLine("strokeText",a,b[d],this._getLeftOffset(),this._getTopOffset()+c,d)}a.closePath(),a.restore()}},_getHeightOfLine:function(){return this.fontSize*this.lineHeight},_renderTextBackground:function(a,b){this._renderTextBoxBackground(a),this._renderTextLinesBackground(a,b)},_renderTextBoxBackground:function(a){this.backgroundColor&&(a.save(),a.fillStyle=this.backgroundColor,a.fillRect(this._getLeftOffset(),this._getTopOffset(),this.width,this.height),a.restore())},_renderTextLinesBackground:function(a,b){if(this.textBackgroundColor){a.save(),a.fillStyle=this.textBackgroundColor;for(var c=0,d=b.length;d>c;c++)if(""!==b[c]){var e=this._getLineWidth(a,b[c]),f=this._getLineLeftOffset(e);a.fillRect(this._getLeftOffset()+f,this._getTopOffset()+c*this.fontSize*this.lineHeight,e,this.fontSize*this.lineHeight)}a.restore()}},_getLineLeftOffset:function(a){return"center"===this.textAlign?(this.width-a)/2:"right"===this.textAlign?this.width-a:0},_getLineWidth:function(a,b){return"justify"===this.textAlign?this.width:a.measureText(b).width},_renderTextDecoration:function(a,b){function c(c){for(var f=0,g=b.length;g>f;f++){var h=e._getLineWidth(a,b[f]),i=e._getLineLeftOffset(h);a.fillRect(e._getLeftOffset()+i,~~(c+f*e._getHeightOfLine(a,f,b)-d),h,1)}}if(this.textDecoration){var d=this._getTextHeight(a,b)/2,e=this;this.textDecoration.indexOf("underline")>-1&&c(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&c(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&c(this.fontSize*this.lineHeight-this.fontSize)}},_getFontDeclaration:function(){return[b.isLikelyNode?this.fontWeight:this.fontStyle,b.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",b.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(a,b){if(this.visible){a.save(),this._transform(a,b);var c=this.transformMatrix,d=this.group&&"path-group"===this.group.type;d&&a.translate(-this.group.width/2,-this.group.height/2),c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),d&&a.translate(this.left,this.top),this._render(a),a.restore()}},toObject:function(a){var b=c(this.callSuper("toObject",a),{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(b),b},toSVG:function(a){var b=[],c=this.text.split(this._reNewline),d=this._getSVGLeftTopOffsets(c),e=this._getSVGTextAndBg(d.lineTop,d.textLeft,c),f=this._getSVGShadows(d.lineTop,c);return d.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._wrapSVGTextAndBg(b,e,f,d),a?a(b.join("")):b.join("")
-},_getSVGLeftTopOffsets:function(a){var b=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,c=-(this.width/2),d=this.useNative?this.fontSize-1:this.height/2-a.length*this.fontSize-this._totalLineHeight;return{textLeft:c+(this.group&&"path-group"===this.group.type?this.left:0),textTop:d+(this.group&&"path-group"===this.group.type?this.top:0),lineTop:b}},_wrapSVGTextAndBg:function(a,b,c,d){a.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n',b.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(',e(d.textLeft,2)," ",e(d.textTop,2),')">',c.join(""),b.textSpans.join(""),"</text>\n","</g>\n")},_getSVGShadows:function(a,c){var d,f,g=[],h=1;if(!this.shadow||!this._boundaries)return g;for(d=0,f=c.length;f>d;d++)if(""!==c[d]){var i=this._boundaries&&this._boundaries[d]?this._boundaries[d].left:0;g.push('<tspan x="',e(i+h+this.shadow.offsetX,2),0===d||this.useNative?'" y':'" dy','="',e(this.useNative?a*d-this.height/2+this.shadow.offsetY:a+(0===d?this.shadow.offsetY:0),2),'" ',this._getFillAttributes(this.shadow.color),">",b.util.string.escapeXml(c[d]),"</tspan>"),h=1}else h++;return g},_getSVGTextAndBg:function(a,b,c){var d=[],e=[],f=1;this._setSVGBg(e);for(var g=0,h=c.length;h>g;g++)""!==c[g]?(this._setSVGTextLineText(c[g],g,d,a,f,e),f=1):f++,this.textBackgroundColor&&this._boundaries&&this._setSVGTextLineBg(e,g,b,a);return{textSpans:d,textBgRects:e}},_setSVGTextLineText:function(a,c,d,f,g){var h=this._boundaries&&this._boundaries[c]?e(this._boundaries[c].left,2):0;d.push('<tspan x="',h,'" ',0===c||this.useNative?"y":"dy",'="',e(this.useNative?f*c-this.height/2:f*g,2),'" ',this._getFillAttributes(this.fill),">",b.util.string.escapeXml(a),"</tspan>")},_setSVGTextLineBg:function(a,b,c,d){a.push("<rect ",this._getFillAttributes(this.textBackgroundColor),' x="',e(c+this._boundaries[b].left,2),'" y="',e(d*b-this.height/2,2),'" width="',e(this._boundaries[b].width,2),'" height="',e(this._boundaries[b].height,2),'"></rect>\n')},_setSVGBg:function(a){this.backgroundColor&&this._boundaries&&a.push("<rect ",this._getFillAttributes(this.backgroundColor),' x="',e(-this.width/2,2),'" y="',e(-this.height/2,2),'" width="',e(this.width,2),'" height="',e(this.height,2),'"></rect>')},_getFillAttributes:function(a){var c=a&&"string"==typeof a?new b.Color(a):"";return c&&c.getSource()&&1!==c.getAlpha()?'opacity="'+c.getAlpha()+'" fill="'+c.setAlpha(1).toRgb()+'"':'fill="'+a+'"'},_set:function(a,b){"fontFamily"===a&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+b+"$3")),this.callSuper("_set",a,b),a in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),b.Text.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("x y dx dy font-family font-style font-weight font-size text-decoration text-anchor".split(" ")),b.Text.DEFAULT_SVG_FONT_SIZE=16,b.Text.fromElement=function(a,c){if(!a)return null;var d=b.parseAttributes(a,b.Text.ATTRIBUTE_NAMES);c=b.util.object.extend(c?b.util.object.clone(c):{},d),"dx"in d&&(c.left+=d.dx),"dy"in d&&(c.top+=d.dy),"fontSize"in c||(c.fontSize=b.Text.DEFAULT_SVG_FONT_SIZE),c.originX||(c.originX="left");var e=new b.Text(a.textContent,c),f=0;return"left"===e.originX&&(f=e.getWidth()/2),"right"===e.originX&&(f=-e.getWidth()/2),e.set({left:e.getLeft()+f,top:e.getTop()-e.getHeight()/2}),e},b.Text.fromObject=function(a){return new b.Text(a.text,d(a))},b.util.createAccessors(b.Text)}("undefined"!=typeof exports?exports:this)}).call({},window,document,html2canvas);
\ No newline at end of file
+(function(){}).call({},window,document,html2canvas);
\ No newline at end of file
diff --git a/src/core.js b/src/core.js
index 0d3cee2..953c41f 100644
--- a/src/core.js
+++ b/src/core.js
@@ -124,6 +124,7 @@ function createWindowClone(ownerDocument, containerDocument, width, height, opti
     var documentElement = ownerDocument.documentElement.cloneNode(true),
         container = containerDocument.createElement("iframe");
 
+    container.className = "html2canvas-container";
     container.style.visibility = "hidden";
     container.style.position = "absolute";
     container.style.left = container.style.top = "-10000px";

From 1c62d38138e243d3de86b475f518e734828e7021 Mon Sep 17 00:00:00 2001
From: MoyuScript <i@moyu.moe>
Date: Thu, 20 Nov 2014 14:31:21 -0600
Subject: [PATCH 2/2] adding submodules

---
 dist/html2canvas.js         |   529 +
 dist/html2canvas.min.js     |     4 +-
 dist/html2canvas.svg.js     | 17691 ++++++++++++++++++++++++++++++++++
 dist/html2canvas.svg.min.js |     7 +-
 4 files changed, 18228 insertions(+), 3 deletions(-)

diff --git a/dist/html2canvas.js b/dist/html2canvas.js
index 4040f59..b241a33 100644
--- a/dist/html2canvas.js
+++ b/dist/html2canvas.js
@@ -37,6 +37,535 @@ if (typeof(Object.create) !== "function" || typeof(document.createElement("canva
     return;
 }
 
+/*! https://mths.be/punycode v1.3.1 by @mathias */
+;(function(root) {
+
+	/** Detect free variables */
+	var freeExports = typeof exports == 'object' && exports &&
+		!exports.nodeType && exports;
+	var freeModule = typeof module == 'object' && module &&
+		!module.nodeType && module;
+	var freeGlobal = typeof global == 'object' && global;
+	if (
+		freeGlobal.global === freeGlobal ||
+		freeGlobal.window === freeGlobal ||
+		freeGlobal.self === 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 = /[^\x20-\x7E]/, // 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;
+		var result = [];
+		while (length--) {
+			result[length] = fn(array[length]);
+		}
+		return result;
+	}
+
+	/**
+	 * A simple `Array#map`-like wrapper to work with domain name strings or email
+	 * addresses.
+	 * @private
+	 * @param {String} domain The domain name or email address.
+	 * @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) {
+		var parts = string.split('@');
+		var result = '';
+		if (parts.length > 1) {
+			// In email addresses, only the domain name should be punycoded. Leave
+			// the local part (i.e. everything up to `@`) intact.
+			result = parts[0] + '@';
+			string = parts[1];
+		}
+		var labels = string.split(regexSeparators);
+		var encoded = map(labels, fn).join('.');
+		return result + encoded;
+	}
+
+	/**
+	 * 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 <https://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 (e.g. a domain name label) 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 or an email address
+	 * to Unicode. Only the Punycoded parts of the input 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} input The Punycoded domain name or email address to
+	 * convert to Unicode.
+	 * @returns {String} The Unicode representation of the given Punycode
+	 * string.
+	 */
+	function toUnicode(input) {
+		return mapDomain(input, function(string) {
+			return regexPunycode.test(string)
+				? decode(string.slice(4).toLowerCase())
+				: string;
+		});
+	}
+
+	/**
+	 * Converts a Unicode string representing a domain name or an email address 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} input The domain name or email address to convert, as a
+	 * Unicode string.
+	 * @returns {String} The Punycode representation of the given domain name or
+	 * email address.
+	 */
+	function toASCII(input) {
+		return mapDomain(input, 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.3.1',
+		/**
+		 * An object of methods to convert from JavaScript's internal character
+		 * representation (UCS-2) to Unicode code points, and back.
+		 * @see <https://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 && freeModule) {
+		if (module.exports == freeExports) { // 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));
+
 var html2canvasNodeAttribute = "data-html2canvas-node";
 var html2canvasCanvasCloneAttribute = "data-html2canvas-canvas-clone";
 var html2canvasCanvasCloneIndex = 0;
diff --git a/dist/html2canvas.min.js b/dist/html2canvas.min.js
index e1598de..953373a 100644
--- a/dist/html2canvas.min.js
+++ b/dist/html2canvas.min.js
@@ -4,5 +4,5 @@
 
   Released under MIT License
 */
-(function(a,b,c,d,e,f,g){function h(a,b,c,d){return o(a,a,c,d,b).then(function(e){E("Document cloned");var f="["+Qb+"='true']";a.querySelector(f).removeAttribute(Qb);var g=e.contentWindow,h=g.document.querySelector(f),j=Promise.resolve("function"==typeof b.onclone?b.onclone(g.document):!0);return j.then(function(){return i(h,e,b,c,d)})})}function i(a,c,d,e,f){var g=c.contentWindow,h=new Gb(g.document),i=new C(d,h),n=M(a),o="view"===d.type?e:l(g.document),p="view"===d.type?f:m(g.document),q=new Ob(o,p,i,d,b),r=new O(a,q,h,i,d);return r.ready.then(function(){E("Finished rendering");var b;return b="view"===d.type?k(q.canvas,{width:q.canvas.width,height:q.canvas.height,top:0,left:0,x:0,y:0}):a===g.document.body||a===g.document.documentElement||null!=d.canvas?q.canvas:k(q.canvas,{width:null!=d.width?d.width:n.width,height:null!=d.height?d.height:n.height,top:n.top,left:n.left,x:g.pageXOffset,y:g.pageYOffset}),j(c,d),b})}function j(a,b){b.removeContainer&&(a.parentNode.removeChild(a),E("Cleaned up container"))}function k(a,c){var d=b.createElement("canvas"),e=Math.min(a.width-1,Math.max(0,c.left)),f=Math.min(a.width,Math.max(1,c.left+c.width)),g=Math.min(a.height-1,Math.max(0,c.top)),h=Math.min(a.height,Math.max(1,c.top+c.height));return d.width=c.width,d.height=c.height,E("Cropping canvas at:","left:",c.left,"top:",c.top,"width:",f-e,"height:",h-g),E("Resulting crop with width",c.width,"and height",c.height," with x",e,"and y",g),d.getContext("2d").drawImage(a,e,g,f-e,h-g,c.x,c.y,f-e,h-g),d}function l(a){return Math.max(Math.max(a.body.scrollWidth,a.documentElement.scrollWidth),Math.max(a.body.offsetWidth,a.documentElement.offsetWidth),Math.max(a.body.clientWidth,a.documentElement.clientWidth))}function m(a){return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))}function n(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}function o(a,b,c,d,e){r(a);var f=a.documentElement.cloneNode(!0),g=b.createElement("iframe");return g.className="html2canvas-container",g.style.visibility="hidden",g.style.position="absolute",g.style.left=g.style.top="-10000px",g.width=c,g.height=d,g.scrolling="no",b.body.appendChild(g),new Promise(function(b){var c=g.contentWindow.document;g.contentWindow.onload=g.onload=function(){var f=setInterval(function(){c.body.childNodes.length>0&&(s(a,c),clearInterval(f),"view"===e.type&&g.contentWindow.scrollTo(d,h),b(g))},50)};var d=a.defaultView.pageXOffset,h=a.defaultView.pageYOffset;c.open(),c.write("<!DOCTYPE html><html></html>"),(d!==a.defaultView.pageXOffset||h!==a.defaultView.pageYOffset)&&a.defaultView.scrollTo(d,h),c.replaceChild(e.javascriptEnabled===!0?c.adoptNode(f):t(c.adoptNode(f)),c.documentElement),c.close()})}function p(b,c,d,e,f,g){return new xb(b,c,a.document).then(q(b)).then(function(a){return o(a,d,e,f,g)})}function q(a){return function(c){var d,e=new DOMParser;try{d=e.parseFromString(c,"text/html")}catch(f){E("DOMParser not supported, falling back to createHTMLDocument"),d=b.implementation.createHTMLDocument("");try{d.open(),d.write(c),d.close()}catch(g){E("createHTMLDocument write not supported, falling back to document.body.innerHTML"),d.body.innerHTML=c}}var h=d.querySelector("base");if(!h||!h.href.host){var i=d.createElement("base");i.href=a,d.head.insertBefore(i,d.head.firstChild)}return d}}function r(a){[].slice.call(a.querySelectorAll("canvas"),0).forEach(function(a){a.setAttribute(Rb,"canvas-"+Sb++)})}function s(a,b){[].slice.call(a.querySelectorAll("["+Rb+"]"),0).forEach(function(a){try{var c=b.querySelector("["+Rb+'="'+a.getAttribute(Rb)+'"]');c&&(c.width=a.width,c.height=a.height,c.getContext("2d").putImageData(a.getContext("2d").getImageData(0,0,a.width,a.height),0,0))}catch(d){E("Unable to copy canvas content from",a,d)}a.removeAttribute(Rb)})}function t(a){return[].slice.call(a.childNodes,0).filter(u).forEach(function(b){"SCRIPT"===b.tagName?a.removeChild(b):t(b)}),a}function u(a){return a.nodeType===Node.ELEMENT_NODE}function v(a){var c=b.createElement("a");return c.href=a,c.href=c.href,c}function w(a){if(this.src=a,E("DummyImageContainer for",a),!this.promise||!this.image){E("Initiating DummyImageContainer"),w.prototype.image=new Image;var b=this.image;w.prototype.promise=new Promise(function(a,c){b.onload=a,b.onerror=c,b.src=n(),b.complete===!0&&a(b)})}}function x(a,c){var d,e,f=b.createElement("div"),g=b.createElement("img"),h=b.createElement("span"),i="Hidden Text";f.style.visibility="hidden",f.style.fontFamily=a,f.style.fontSize=c,f.style.margin=0,f.style.padding=0,b.body.appendChild(f),g.src=n(),g.width=1,g.height=1,g.style.margin=0,g.style.padding=0,g.style.verticalAlign="baseline",h.style.fontFamily=a,h.style.fontSize=c,h.style.margin=0,h.style.padding=0,h.appendChild(b.createTextNode(i)),f.appendChild(h),f.appendChild(g),d=g.offsetTop-h.offsetTop+1,f.removeChild(h),f.appendChild(b.createTextNode(i)),f.style.lineHeight="normal",g.style.verticalAlign="super",e=g.offsetTop-f.offsetTop+1,b.body.removeChild(f),this.baseline=d,this.lineWidth=1,this.middle=e}function y(){this.data={}}function z(a,b,c){this.image=null,this.src=a;var d=this,e=M(a);this.promise=(b?new Promise(function(b){"about:blank"===a.contentWindow.document.URL||null==a.contentWindow.document.documentElement?a.contentWindow.onload=a.onload=function(){b(a)}:b(a)}):this.proxyLoad(c.proxy,e,c)).then(function(a){return html2canvas(a.contentWindow.document.documentElement,{type:"view",width:a.width,height:a.height,proxy:c.proxy,javascriptEnabled:c.javascriptEnabled,removeContainer:c.removeContainer,allowTaint:c.allowTaint,imageTimeout:c.imageTimeout/2})}).then(function(a){return d.image=a})}function A(a){this.src=a.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=Promise.resolve(!0)}function B(a,b){this.src=a,this.image=new Image;var c=this;this.tainted=null,this.promise=new Promise(function(d,e){c.image.onload=d,c.image.onerror=e,b&&(c.image.crossOrigin="anonymous"),c.image.src=a,c.image.complete===!0&&d(c.image)})}function C(b,c){this.link=null,this.options=b,this.support=c,this.origin=this.getOrigin(a.location.href)}function D(a){A.apply(this,arguments),this.type=this.TYPES.LINEAR;var b=null===a.args[0].match(this.stepRegExp);b?a.args[0].split(" ").reverse().forEach(function(a){switch(a){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 b=this.y0,c=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=c,this.y1=b}},this):(this.y0=0,this.y1=1),this.colorStops=a.args.slice(b?1:0).map(function(a){var b=a.match(this.stepRegExp);return{color:b[1],stop:"%"===b[3]?b[2]/100:null}},this),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(a,b){null===a.stop&&this.colorStops.slice(b).some(function(c,d){return null!==c.stop?(a.stop=(c.stop-this.colorStops[b-1].stop)/(d+1)+this.colorStops[b-1].stop,!0):!1},this)},this)}function E(){a.html2canvas.logging&&a.console&&a.console.log&&Function.prototype.bind.call(a.console.log,a.console).apply(a.console,[Date.now()-a.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function F(a,b){this.node=a,this.parent=b,this.stack=null,this.bounds=null,this.borders=null,this.clip=[],this.backgroundClip=[],this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null,this.isPseudoElement=!1,this.opacity=null}function G(a){var b=a.options[a.selectedIndex||0];return b?b.text||"":""}function H(a){return a&&"matrix"===a[1]?a[2].split(",").map(function(a){return parseFloat(a.trim())}):void 0}function I(a){return-1!==a.toString().indexOf("%")}function J(a){var b,c,d,e,f,g,h,i=" \r\n	",j=[],k=0,l=0,m=function(){b&&('"'===c.substr(0,1)&&(c=c.substr(1,c.length-2)),c&&h.push(c),"-"===b.substr(0,1)&&(e=b.indexOf("-",1)+1)>0&&(d=b.substr(0,e),b=b.substr(e)),j.push({prefix:d,method:b.toLowerCase(),value:f,args:h,image:null})),h=[],b=d=c=f=""};return h=[],b=d=c=f="",a.split("").forEach(function(a){if(!(0===k&&i.indexOf(a)>-1)){switch(a){case'"':g?g===a&&(g=null):g=a;break;case"(":if(g)break;if(0===k)return k=1,void(f+=a);l++;break;case")":if(g)break;if(1===k){if(0===l)return k=0,f+=a,void m();l--}break;case",":if(g)break;if(0===k)return void m();if(1===k&&0===l&&!b.match(/^url$/i))return h.push(c),c="",void(f+=a)}f+=a,0===k?b+=a:c+=a}}),m(),j}function K(a){return a.replace("px","")}function L(a){return parseFloat(a)}function M(a){if(a.getBoundingClientRect){var b=a.getBoundingClientRect(),c=null==a.offsetWidth?b.width:a.offsetWidth;return{top:b.top,bottom:b.bottom||b.top+b.height,right:b.left+c,left:b.left,width:c,height:null==a.offsetHeight?b.height:a.offsetHeight}}return{}}function N(a){var b=a.offsetParent?N(a.offsetParent):{top:0,left:0};return{top:a.offsetTop+b.top,bottom:a.offsetTop+a.offsetHeight+b.top,right:a.offsetLeft+b.left+a.offsetWidth,left:a.offsetLeft+b.left,width:a.offsetWidth,height:a.offsetHeight}}function O(a,b,c,d,e){E("Starting NodeParser"),this.renderer=b,this.options=e,this.range=null,this.support=c,this.renderQueue=[],this.stack=new Fb(!0,1,a.ownerDocument,null);var f=new F(a,null);if(a===a.ownerDocument.documentElement){var g=new F(this.renderer.isTransparent(f.css("backgroundColor"))?a.ownerDocument.body:a.ownerDocument.documentElement,null);b.rectangle(0,0,b.width,b.height,g.css("backgroundColor"))}f.visibile=f.isElementVisible(),this.createPseudoHideStyles(a.ownerDocument),this.disableAnimations(a.ownerDocument),this.nodes=sb([f].concat(this.getChildren(f)).filter(function(a){return a.visible=a.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new y,E("Fetched nodes, total:",this.nodes.length),E("Calculate overflow clips"),this.calculateOverflowClips(),E("Start fetching images"),this.images=d.fetch(this.nodes.filter(jb)),this.ready=this.images.ready.then(ob(function(){return E("Images loaded, starting parsing"),E("Creating stacking contexts"),this.createStackingContexts(),E("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.parse(this.stack),E("Render queue created with "+this.renderQueue.length+" items"),new Promise(ob(function(a){e.async?"function"==typeof e.async?e.async.call(this,this.renderQueue,a):this.renderQueue.length>0?(this.renderIndex=0,this.asyncRenderer(this.renderQueue,a)):a():(this.renderQueue.forEach(this.paint,this),a())},this))},this))}function P(a){return a.parent&&a.parent.clip.length}function Q(a){return a.replace(/(\-[a-z])/g,function(a){return a.toUpperCase().replace("-","")})}function R(){}function S(a,b,c,d){var e=4*((Math.sqrt(2)-1)/3),f=c*e,g=d*e,h=a+c,i=b+d;return{topLeft:U({x:a,y:i},{x:a,y:i-g},{x:h-f,y:b},{x:h,y:b}),topRight:U({x:a,y:b},{x:a+f,y:b},{x:h,y:i-g},{x:h,y:i}),bottomRight:U({x:h,y:b},{x:h,y:b+g},{x:a+f,y:i},{x:a,y:i}),bottomLeft:U({x:h,y:i},{x:h-f,y:i},{x:a,y:b+g},{x:a,y:b})}}function T(a,b,c){var d=a.left,e=a.top,f=a.width,g=a.height,h=b[0][0],i=b[0][1],j=b[1][0],k=b[1][1],l=b[2][0],m=b[2][1],n=b[3][0],o=b[3][1],p=f-j,q=g-m,r=f-l,s=g-o;return{topLeftOuter:S(d,e,h,i).topLeft.subdivide(.5),topLeftInner:S(d+c[3].width,e+c[0].width,Math.max(0,h-c[3].width),Math.max(0,i-c[0].width)).topLeft.subdivide(.5),topRightOuter:S(d+p,e,j,k).topRight.subdivide(.5),topRightInner:S(d+Math.min(p,f+c[3].width),e+c[0].width,p>f+c[3].width?0:j-c[3].width,k-c[0].width).topRight.subdivide(.5),bottomRightOuter:S(d+r,e+q,l,m).bottomRight.subdivide(.5),bottomRightInner:S(d+Math.min(r,f-c[3].width),e+Math.min(q,g+c[0].width),Math.max(0,l-c[1].width),m-c[2].width).bottomRight.subdivide(.5),bottomLeftOuter:S(d,e+s,n,o).bottomLeft.subdivide(.5),bottomLeftInner:S(d+c[3].width,e+s,Math.max(0,n-c[3].width),o-c[2].width).bottomLeft.subdivide(.5)}}function U(a,b,c,d){var e=function(a,b,c){return{x:a.x+(b.x-a.x)*c,y:a.y+(b.y-a.y)*c}};return{start:a,startControl:b,endControl:c,end:d,subdivide:function(f){var g=e(a,b,f),h=e(b,c,f),i=e(c,d,f),j=e(g,h,f),k=e(h,i,f),l=e(j,k,f);return[U(a,g,j,l),U(l,k,i,d)]},curveTo:function(a){a.push(["bezierCurve",b.x,b.y,c.x,c.y,d.x,d.y])},curveToReversed:function(d){d.push(["bezierCurve",c.x,c.y,b.x,b.y,a.x,a.y])}}}function V(a,b,c,d,e,f,g){var h=[];return b[0]>0||b[1]>0?(h.push(["line",d[1].start.x,d[1].start.y]),d[1].curveTo(h)):h.push(["line",a.c1[0],a.c1[1]]),c[0]>0||c[1]>0?(h.push(["line",f[0].start.x,f[0].start.y]),f[0].curveTo(h),h.push(["line",g[0].end.x,g[0].end.y]),g[0].curveToReversed(h)):(h.push(["line",a.c2[0],a.c2[1]]),h.push(["line",a.c3[0],a.c3[1]])),b[0]>0||b[1]>0?(h.push(["line",e[1].end.x,e[1].end.y]),e[1].curveToReversed(h)):h.push(["line",a.c4[0],a.c4[1]]),h}function W(a,b,c,d,e,f,g){b[0]>0||b[1]>0?(a.push(["line",d[0].start.x,d[0].start.y]),d[0].curveTo(a),d[1].curveTo(a)):a.push(["line",f,g]),(c[0]>0||c[1]>0)&&a.push(["line",e[0].start.x,e[0].start.y])}function X(a){return a.cssInt("zIndex")<0}function Y(a){return a.cssInt("zIndex")>0}function Z(a){return 0===a.cssInt("zIndex")}function $(a){return-1!==["inline","inline-block","inline-table"].indexOf(a.css("display"))}function _(a){return a instanceof Fb}function ab(a){return a.node.data.trim().length>0}function bb(a){return/^(normal|none|0px)$/.test(a.parent.css("letterSpacing"))}function cb(a){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(b){var c=a.css("border"+b+"Radius"),d=c.split(" ");return d.length<=1&&(d[1]=d[0]),d.map(pb)})}function db(a){return a.nodeType===Node.TEXT_NODE||a.nodeType===Node.ELEMENT_NODE}function eb(a){var b=a.css("position"),c=-1!==["absolute","relative","fixed"].indexOf(b)?a.css("zIndex"):"auto";return"auto"!==c}function fb(a){return"static"!==a.css("position")}function gb(a){return"none"!==a.css("float")}function hb(a){return-1!==["inline-block","inline-table"].indexOf(a.css("display"))}function ib(a){var b=this;return function(){return!a.apply(b,arguments)}}function jb(a){return a.node.nodeType===Node.ELEMENT_NODE}function kb(a){return a.isPseudoElement===!0}function lb(a){return a.node.nodeType===Node.TEXT_NODE}function mb(a){return function(b,c){return b.cssInt("zIndex")+a.indexOf(b)/a.length-(c.cssInt("zIndex")+a.indexOf(c)/a.length)}}function nb(a){return a.getOpacity()<1}function ob(a,b){return function(){return a.apply(b,arguments)}}function pb(a){return parseInt(a,10)}function qb(a){return a.width}function rb(a){return a.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(a.node.nodeName)}function sb(a){return[].concat.apply([],a)}function tb(a){var b=a.substr(0,1);return b===a.substr(a.length-1)&&b.match(/'|"/)?a.substr(1,a.length-2):a}function ub(b){for(var c,d=[],e=0,f=!1;b.length;)vb(b[e])===f?(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)),f=!f,e=0):e++,e>=b.length&&(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)));return d}function vb(a){return-1!==[32,13,10,9,45].indexOf(a)}function wb(a){return/[^\u0000-\u00ff]/.test(a)}function xb(a,b,c){var d=Ab(Ub),e=Bb(b,a,d);return Ub?Nb(e):zb(c,e,d).then(function(a){return Ib(a.content)})}function yb(a,b,c){var d=Ab(Vb),e=Bb(b,a,d);return Vb?Promise.resolve(e):zb(c,e,d).then(function(a){return"data:"+a.type+";base64,"+a.content})}function zb(b,c,d){return new Promise(function(e,f){var g=b.createElement("script"),h=function(){delete a.html2canvas.proxy[d],b.body.removeChild(g)};a.html2canvas.proxy[d]=function(a){h(),e(a)},g.src=c,g.onerror=function(a){h(),f(a)},b.body.appendChild(g)})}function Ab(a){return a?"":"html2canvas_"+Date.now()+"_"+ ++Tb+"_"+Math.round(1e5*Math.random())}function Bb(a,b,c){return a+"?url="+encodeURIComponent(b)+(c.length?"&callback=html2canvas.proxy."+c:"")}function Cb(a,c){var d=(b.createElement("script"),b.createElement("a"));d.href=a,a=d.href,this.src=a,this.image=new Image;var e=this;this.promise=new Promise(function(d,f){e.image.crossOrigin="Anonymous",e.image.onload=d,e.image.onerror=f,new yb(a,c,b).then(function(a){e.image.src=a})["catch"](f)})}function Db(a,b,c){F.call(this,a,b),this.isPseudoElement=!0,this.before=":before"===c}function Eb(a,b,c,d,e){this.width=a,this.height=b,this.images=c,this.options=d,this.document=e}function Fb(a,b,c,d){F.call(this,c,d),this.ownStacking=a,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*b}function Gb(a){this.rangeBounds=this.testRangeBounds(a),this.cors=this.testCORS(),this.svg=this.testSVG()}function Hb(a){this.src=a,this.image=null;var b=this;this.promise=this.hasFabric().then(function(){return b.isInline(a)?Promise.resolve(b.inlineFormatting(a)):Nb(a)}).then(function(a){return new Promise(function(c){html2canvas.fabric.loadSVGFromString(a,b.createCanvas.call(b,c))})})}function Ib(a){var b,c,d,e,f,g,h,i,j="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",k=a.length,l="";for(b=0;k>b;b+=4)c=j.indexOf(a[b]),d=j.indexOf(a[b+1]),e=j.indexOf(a[b+2]),f=j.indexOf(a[b+3]),g=c<<2|d>>4,h=(15&d)<<4|e>>2,i=(3&e)<<6|f,l+=64===e?String.fromCharCode(g):64===f||-1===f?String.fromCharCode(g,h):String.fromCharCode(g,h,i);return l}function Jb(a,b){this.src=a,this.image=null;var c=this;this.promise=b?new Promise(function(b,d){c.image=new Image,c.image.onload=b,c.image.onerror=d,c.image.src="data:image/svg+xml,"+(new XMLSerializer).serializeToString(a),c.image.complete===!0&&b(c.image)}):this.hasFabric().then(function(){return new Promise(function(b){html2canvas.fabric.parseSVGDocument(a,c.createCanvas.call(c,b))})})}function Kb(a,b){F.call(this,a,b)}function Lb(a,b,c){return a.length>0?b+c.toUpperCase():void 0}function Mb(a){A.apply(this,arguments),this.type="linear"===a.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}function Nb(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onload=function(){200===d.status?b(d.responseText):c(new Error(d.statusText))},d.onerror=function(){c(new Error("Network Error"))},d.send()})}function Ob(a,b){Eb.apply(this,arguments),this.canvas=this.options.canvas||this.document.createElement("canvas"),this.options.canvas||(this.canvas.width=a,this.canvas.height=b),this.ctx=this.canvas.getContext("2d"),this.options.background&&this.rectangle(0,0,a,b,this.options.background),this.taintCtx=this.document.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},E("Initialized CanvasRenderer with size",a,"x",b)}function Pb(a){return a.length>0}if(!function(){var c,d,f,g;!function(){var a={},b={};c=function(b,c,d){a[b]={deps:c,callback:d}},g=f=d=function(c){function e(a){if("."!==a.charAt(0))return a;for(var b=a.split("/"),d=c.split("/").slice(0,-1),e=0,f=b.length;f>e;e++){var g=b[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(g._eak_seen=a,b[c])return b[c];if(b[c]={},!a[c])throw new Error("Could not find module "+c);for(var f,h=a[c],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)k.push("exports"===i[l]?f={}:d(e(i[l])));var n=j.apply(this,k);return b[c]=f||n}}(),c("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j<a.length;j++)g=a[j],g&&e(g.then)?g.then(d(j),c):f(j,g)})}var d=a.isArray,e=a.isFunction;b.all=c}),c("promise/asap",["exports"],function(c){"use strict";function d(){return function(){process.nextTick(h)}}function f(){var a=0,c=new l(h),d=b.createTextNode("");return c.observe(d,{characterData:!0}),function(){d.data=a=++a%2}}function g(){return function(){m.setTimeout(h,1)}}function h(){for(var a=0;a<n.length;a++){var b=n[a],c=b[0],d=b[1];c(d)}n=[]}function i(a,b){var c=n.push([a,b]);1===c&&j()}var j,k="undefined"!=typeof a?a:{},l=k.MutationObserver||k.WebKitMutationObserver,m="undefined"!=typeof e?e:this,n=[];j="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?d():l?f():g(),c.asap=i}),c("promise/cast",["exports"],function(a){"use strict";function b(a){if(a&&"object"==typeof a&&a.constructor===this)return a;var b=this;return new b(function(b){b(a)})}a.cast=b}),c("promise/config",["exports"],function(a){"use strict";function b(a,b){return 2!==arguments.length?c[a]:void(c[a]=b)}var c={instrument:!1};a.config=c,a.configure=b}),c("promise/polyfill",["./promise","./utils","exports"],function(b,c,d){"use strict";function e(){var b="Promise"in a&&"cast"in a.Promise&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var b;return new a.Promise(function(a){b=a}),g(b)}();b||(a.Promise=f)}var f=b.Promise,g=c.isFunction;d.polyfill=e}),c("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(a,b,c,d,e,f,g,h,i){"use strict";function j(a){if(!w(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof j))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],k(a,this)}function k(a,b){function c(a){p(b,a)}function d(a){r(b,a)}try{a(c,d)}catch(e){d(e)}}function l(a,b,c,d){var e,f,g,h,i=w(c);if(i)try{e=c(d),g=!0}catch(j){h=!0,f=j}else e=d,g=!0;o(b,e)||(i&&g?p(b,e):h?r(b,f):a===F?p(b,e):a===G&&r(b,e))}function m(a,b,c,d){var e=a._subscribers,f=e.length;e[f]=b,e[f+F]=c,e[f+G]=d}function n(a,b){for(var c,d,e=a._subscribers,f=a._detail,g=0;g<e.length;g+=3)c=e[g],d=e[g+b],l(b,c,d,f);a._subscribers=null}function o(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(v(b)&&(d=b.then,w(d)))return d.call(b,function(d){return c?!0:(c=!0,void(b!==d?p(a,d):q(a,d)))},function(b){return c?!0:(c=!0,void r(a,b))}),!0}catch(e){return c?!0:(r(a,e),!0)}return!1}function p(a,b){a===b?q(a,b):o(a,b)||q(a,b)}function q(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(s,a))}function r(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(t,a))}function s(a){n(a,a._state=F)}function t(a){n(a,a._state=G)}var u=a.config,v=(a.configure,b.objectOrFunction),w=b.isFunction,x=(b.now,c.cast),y=d.all,z=e.race,A=f.resolve,B=g.reject,C=h.asap;u.async=C;var D=void 0,E=0,F=1,G=2;j.prototype={constructor:j,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(a,b){var c=this,d=new this.constructor(function(){});if(this._state){var e=arguments;u.async(function(){l(c._state,d,e[c._state-1],c._detail)})}else m(this,d,a,b);return d},"catch":function(a){return this.then(null,a)}},j.all=y,j.cast=x,j.race=z,j.resolve=A,j.reject=B,i.Promise=j}),c("promise/race",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to race.");return new b(function(b,c){for(var d,e=0;e<a.length;e++)d=a[e],d&&"function"==typeof d.then?d.then(b,c):b(d)})}var d=a.isArray;b.race=c}),c("promise/reject",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b,c){c(a)})}a.reject=b}),c("promise/resolve",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b){b(a)})}a.resolve=b}),c("promise/utils",["exports"],function(a){"use strict";function b(a){return c(a)||"object"==typeof a&&null!==a}function c(a){return"function"==typeof a}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}var e=Date.now||function(){return(new Date).getTime()};a.objectOrFunction=b,a.isFunction=c,a.isArray=d,a.now=e}),d("promise/polyfill").polyfill()}(),"function"!=typeof Object.create||"function"!=typeof b.createElement("canvas").getContext)return void(a.html2canvas=function(){return Promise.reject("No canvas support")});var Qb="data-html2canvas-node",Rb="data-html2canvas-canvas-clone",Sb=0;a.html2canvas=function(c,d){if(d=d||{},d.logging&&(a.html2canvas.logging=!0,a.html2canvas.start=Date.now()),d.async="undefined"==typeof d.async?!0:d.async,d.allowTaint="undefined"==typeof d.allowTaint?!1:d.allowTaint,d.removeContainer="undefined"==typeof d.removeContainer?!0:d.removeContainer,d.javascriptEnabled="undefined"==typeof d.javascriptEnabled?!1:d.javascriptEnabled,d.imageTimeout="undefined"==typeof d.imageTimeout?1e4:d.imageTimeout,"string"==typeof c)return"string"!=typeof d.proxy?Promise.reject("Proxy must be used when rendering url"):p(v(c),d.proxy,b,a.innerWidth,a.innerHeight,d).then(function(b){return i(b.contentWindow.document.documentElement,b,d,a.innerWidth,a.innerHeight)});var e=(c===g?[b.documentElement]:c.length?c:[c])[0];return e.setAttribute(Qb,"true"),h(e.ownerDocument,d,e.ownerDocument.defaultView.innerWidth,e.ownerDocument.defaultView.innerHeight).then(function(a){return"function"==typeof d.onrendered&&(E("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"),d.onrendered(a)),a})},a.html2canvas.punycode=this.punycode,a.html2canvas.proxy={},y.prototype.getMetrics=function(a,b){return this.data[a+"-"+b]===g&&(this.data[a+"-"+b]=new x(a,b)),this.data[a+"-"+b]},z.prototype.proxyLoad=function(a,b,c){var d=this.src;return p(d.src,a,d.ownerDocument,b.width,b.height,c)},A.prototype.TYPES={LINEAR:1,RADIAL:2},C.prototype.findImages=function(a){var b=[];return a.reduce(function(a,b){switch(b.node.nodeName){case"IMG":return a.concat([{args:[b.node.src],method:"url"}]);case"svg":case"IFRAME":return a.concat([{args:[b.node],method:b.node.nodeName}])}return a},[]).forEach(this.addImage(b,this.loadImage),this),b},C.prototype.findBackgroundImage=function(a,b){return b.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(a,this.loadImage),this),a},C.prototype.addImage=function(a,b){return function(c){c.args.forEach(function(d){this.imageExists(a,d)||(a.splice(0,0,b.call(this,c)),E("Added image #"+a.length,"string"==typeof d?d.substring(0,100):d))},this)}},C.prototype.hasImageBackground=function(a){return"none"!==a.method},C.prototype.loadImage=function(a){if("url"===a.method){var b=a.args[0];return!this.isSVG(b)||this.support.svg||this.options.allowTaint?b.match(/data:image\/.*;base64,/i)?new B(b.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(b)||this.options.allowTaint===!0||this.isSVG(b)?new B(b,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new B(b,!0):this.options.proxy?new Cb(b,this.options.proxy):new w(b):new Hb(b)}return"linear-gradient"===a.method?new D(a):"gradient"===a.method?new Mb(a):"svg"===a.method?new Jb(a.args[0],this.support.svg):"IFRAME"===a.method?new z(a.args[0],this.isSameOrigin(a.args[0].src),this.options):new w(a)},C.prototype.isSVG=function(a){return"svg"===a.substring(a.length-3).toLowerCase()||Hb.prototype.isInline(a)},C.prototype.imageExists=function(a,b){return a.some(function(a){return a.src===b})},C.prototype.isSameOrigin=function(a){return this.getOrigin(a)===this.origin},C.prototype.getOrigin=function(a){var c=this.link||(this.link=b.createElement("a"));return c.href=a,c.href=c.href,c.protocol+c.hostname+c.port},C.prototype.getPromise=function(a){return this.timeout(a,this.options.imageTimeout)["catch"](function(){var b=new w(a.src);return b.promise.then(function(b){a.image=b})})},C.prototype.get=function(a){var b=null;return this.images.some(function(c){return(b=c).src===a})?b:null},C.prototype.fetch=function(a){return this.images=a.reduce(ob(this.findBackgroundImage,this),this.findImages(a)),this.images.forEach(function(a,b){a.promise.then(function(){E("Succesfully loaded image #"+(b+1),a)},function(c){E("Failed loading image #"+(b+1),a,c)})}),this.ready=Promise.all(this.images.map(this.getPromise,this)),E("Finished searching images"),this},C.prototype.timeout=function(a,b){var c;return Promise.race([a.promise,new Promise(function(d,e){c=setTimeout(function(){E("Timed out loading image",a),e(a)},b)})]).then(function(a){return clearTimeout(c),a})},D.prototype=Object.create(A.prototype),D.prototype.stepRegExp=/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/,F.prototype.cloneTo=function(a){a.visible=this.visible,a.borders=this.borders,a.bounds=this.bounds,a.clip=this.clip,a.backgroundClip=this.backgroundClip,a.computedStyles=this.computedStyles,a.styles=this.styles,a.backgroundImages=this.backgroundImages,a.opacity=this.opacity},F.prototype.getOpacity=function(){return null===this.opacity?this.opacity=this.cssFloat("opacity"):this.opacity},F.prototype.assignStack=function(a){this.stack=a,a.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(a){return this.computedStyles||(this.computedStyles=this.isPseudoElement?this.parent.computedStyle(this.before?":before":":after"):this.computedStyle(null)),this.styles[a]||(this.styles[a]=this.computedStyles[a])},F.prototype.prefixedCss=function(a){var b=["webkit","moz","ms","o"],c=this.css(a);return c===g&&b.some(function(b){return c=this.css(b+a.substr(0,1).toUpperCase()+a.substr(1)),c!==g},this),c===g?null:c},F.prototype.computedStyle=function(a){return this.node.ownerDocument.defaultView.getComputedStyle(this.node,a)},F.prototype.cssInt=function(a){var b=parseInt(this.css(a),10);return isNaN(b)?0:b},F.prototype.cssFloat=function(a){var b=parseFloat(this.css(a));return isNaN(b)?0:b},F.prototype.fontWeight=function(){var a=this.css("fontWeight");switch(parseInt(a,10)){case 401:a="bold";break;case 400:a="normal"}return a},F.prototype.parseClip=function(){var a=this.css("clip").match(this.CLIP);return a?{top:parseInt(a[1],10),right:parseInt(a[2],10),bottom:parseInt(a[3],10),left:parseInt(a[4],10)}:null},F.prototype.parseBackgroundImages=function(){return this.backgroundImages||(this.backgroundImages=J(this.css("backgroundImage")))},F.prototype.cssList=function(a,b){var c=(this.css(a)||"").split(",");return c=c[b||0]||c[0]||"auto",c=c.trim().split(" "),1===c.length&&(c=[c[0],c[0]]),c},F.prototype.parseBackgroundSize=function(a,b,c){var d,e,f=this.cssList("backgroundSize",c);if(I(f[0]))d=a.width*parseFloat(f[0])/100;else{if(/contain|cover/.test(f[0])){var g=a.width/a.height,h=b.width/b.height;return h>g^"contain"===f[0]?{width:a.height*h,height:a.height}:{width:a.width,height:a.width/h}}d=parseInt(f[0],10)}return e="auto"===f[0]&&"auto"===f[1]?b.height:"auto"===f[1]?d/b.width*b.height:I(f[1])?a.height*parseFloat(f[1])/100:parseInt(f[1],10),"auto"===f[0]&&(d=e/b.height*b.width),{width:d,height:e}},F.prototype.parseBackgroundPosition=function(a,b,c,d){var e,f,g=this.cssList("backgroundPosition",c);return e=I(g[0])?(a.width-(d||b).width)*(parseFloat(g[0])/100):parseInt(g[0],10),f="auto"===g[1]?e/b.width*b.height:I(g[1])?(a.height-(d||b).height)*parseFloat(g[1])/100:parseInt(g[1],10),"auto"===g[0]&&(e=f/b.height*b.width),{left:e,top:f}},F.prototype.parseBackgroundRepeat=function(a){return this.cssList("backgroundRepeat",a)[0]},F.prototype.parseTextShadows=function(){var a=this.css("textShadow"),b=[];if(a&&"none"!==a)for(var c=a.match(this.TEXT_SHADOW_PROPERTY),d=0;c&&d<c.length;d++){var e=c[d].match(this.TEXT_SHADOW_VALUES);b.push({color:e[0],offsetX:e[1]?parseFloat(e[1].replace("px","")):0,offsetY:e[2]?parseFloat(e[2].replace("px","")):0,blur:e[3]?e[3].replace("px",""):0})}return b},F.prototype.parseTransform=function(){if(!this.transformData)if(this.hasTransform()){var a=this.parseBounds(),b=this.prefixedCss("transformOrigin").split(" ").map(K).map(L);b[0]+=a.left,b[1]+=a.top,this.transformData={origin:b,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 a=this.prefixedCss("transform"),b=a?H(a.match(this.MATRIX_PROPERTY)):null;this.transformMatrix=b?b:[1,0,0,1,0,0]}return this.transformMatrix},F.prototype.parseBounds=function(){return this.bounds||(this.bounds=this.hasTransform()?N(this.node):M(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 a=this.node.value||"";return a="SELECT"===this.node.tagName?G(this.node):a,0===a.length?this.node.placeholder||"":a},F.prototype.MATRIX_PROPERTY=/(matrix)\((.+)\)/,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\)$/,O.prototype.calculateOverflowClips=function(){this.nodes.forEach(function(a){if(jb(a)){kb(a)&&a.appendToDOM(),a.borders=this.parseBorders(a);var b="hidden"===a.css("overflow")?[a.borders.clip]:[],c=a.parseClip();c&&-1!==["absolute","fixed"].indexOf(a.css("position"))&&b.push([["rect",a.bounds.left+c.left,a.bounds.top+c.top,c.right-c.left,c.bottom-c.top]]),a.clip=P(a)?a.parent.clip.concat(b):b,a.backgroundClip="hidden"!==a.css("overflow")?a.clip.concat([a.borders.clip]):a.clip,kb(a)&&a.cleanDOM()}else lb(a)&&(a.clip=P(a)?a.parent.clip:[]);kb(a)||(a.bounds=null)},this)},O.prototype.asyncRenderer=function(a,b,c){c=c||Date.now(),this.paint(a[this.renderIndex++]),a.length===this.renderIndex?b():c+20>Date.now()?this.asyncRenderer(a,b,c):setTimeout(ob(function(){this.asyncRenderer(a,b)},this),0)},O.prototype.createPseudoHideStyles=function(a){this.createStyles(a,"."+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+':before { content: "" !important; display: none !important; }.'+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER+':after { content: "" !important; display: none !important; }')},O.prototype.disableAnimations=function(a){this.createStyles(a,"* { -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;}")},O.prototype.createStyles=function(a,b){var c=a.createElement("style");c.innerHTML=b,a.body.appendChild(c)},O.prototype.getPseudoElements=function(a){var b=[[a]];if(a.node.nodeType===Node.ELEMENT_NODE){var c=this.getPseudoElement(a,":before"),d=this.getPseudoElement(a,":after");c&&b.push(c),d&&b.push(d)}return sb(b)},O.prototype.getPseudoElement=function(a,c){var d=a.computedStyle(c);if(!d||!d.content||"none"===d.content||"-moz-alt-content"===d.content||"none"===d.display)return null;for(var e=tb(d.content),f="url"===e.substr(0,3),g=b.createElement(f?"img":"html2canvaspseudoelement"),h=new Db(g,a,c),i=d.length-1;i>=0;i--){var j=Q(d.item(i));g.style[j]=d[j]}if(g.className=Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+" "+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER,f)return g.src=J(e)[0].args[0],[h];var k=b.createTextNode(e);return g.appendChild(k),[h,new Kb(k,h)]},O.prototype.getChildren=function(a){return sb([].filter.call(a.node.childNodes,db).map(function(b){var c=[b.nodeType===Node.TEXT_NODE?new Kb(b,a):new F(b,a)].filter(rb);return b.nodeType===Node.ELEMENT_NODE&&c.length&&"TEXTAREA"!==b.tagName?c[0].isElementVisible()?c.concat(this.getChildren(c[0])):[]:c},this))},O.prototype.newStackingContext=function(a,b){var c=new Fb(b,a.getOpacity(),a.node,a.parent);a.cloneTo(c);var d=b?c.getParentStack(this):c.parent.stack;d.contexts.push(c),a.stack=c},O.prototype.createStackingContexts=function(){this.nodes.forEach(function(a){jb(a)&&(this.isRootElement(a)||nb(a)||eb(a)||this.isBodyWithTransparentRoot(a)||a.hasTransform())?this.newStackingContext(a,!0):jb(a)&&(fb(a)&&Z(a)||hb(a)||gb(a))?this.newStackingContext(a,!1):a.assignStack(a.parent.stack)},this)},O.prototype.isBodyWithTransparentRoot=function(a){return"BODY"===a.node.nodeName&&this.renderer.isTransparent(a.parent.css("backgroundColor"))},O.prototype.isRootElement=function(a){return null===a.parent},O.prototype.sortStackingContexts=function(a){a.contexts.sort(mb(a.contexts.slice(0))),a.contexts.forEach(this.sortStackingContexts,this)},O.prototype.parseTextBounds=function(a){return function(b,c,d){if("none"!==a.parent.css("textDecoration").substr(0,4)||0!==b.trim().length){if(this.support.rangeBounds&&!a.parent.hasTransform()){var e=d.slice(0,c).join("").length;return this.getRangeBounds(a.node,e,b.length)}if(a.node&&"string"==typeof a.node.data){var f=a.node.splitText(b.length),g=this.getWrapperBounds(a.node,a.parent.hasTransform());return a.node=f,g}}else(!this.support.rangeBounds||a.parent.hasTransform())&&(a.node=a.node.splitText(b.length));return{}}},O.prototype.getWrapperBounds=function(a,b){var c=a.ownerDocument.createElement("html2canvaswrapper"),d=a.parentNode,e=a.cloneNode(!0);c.appendChild(a.cloneNode(!0)),d.replaceChild(c,a);var f=b?N(c):M(c);return d.replaceChild(e,c),f},O.prototype.getRangeBounds=function(a,b,c){var d=this.range||(this.range=a.ownerDocument.createRange());return d.setStart(a,b),d.setEnd(a,b+c),d.getBoundingClientRect()},O.prototype.parse=function(a){var b=a.contexts.filter(X),c=a.children.filter(jb),d=c.filter(ib(gb)),e=d.filter(ib(fb)).filter(ib($)),f=c.filter(ib(fb)).filter(gb),g=d.filter(ib(fb)).filter($),h=a.contexts.concat(d.filter(fb)).filter(Z),i=a.children.filter(lb).filter(ab),j=a.contexts.filter(Y);b.concat(e).concat(f).concat(g).concat(h).concat(i).concat(j).forEach(function(a){this.renderQueue.push(a),_(a)&&(this.parse(a),this.renderQueue.push(new R))},this)},O.prototype.paint=function(a){try{a instanceof R?this.renderer.ctx.restore():lb(a)?(kb(a.parent)&&a.parent.appendToDOM(),this.paintText(a),kb(a.parent)&&a.parent.cleanDOM()):this.paintNode(a)}catch(b){E(b)}},O.prototype.paintNode=function(a){_(a)&&(this.renderer.setOpacity(a.opacity),this.renderer.ctx.save(),a.hasTransform()&&this.renderer.setTransform(a.parseTransform()));var b=a.parseBounds();this.renderer.clip(a.backgroundClip,function(){this.renderer.renderBackground(a,b,a.borders.borders.map(qb))},this),this.renderer.clip(a.clip,function(){this.renderer.renderBorders(a.borders.borders)},this),this.renderer.clip(a.backgroundClip,function(){switch(a.node.nodeName){case"svg":case"IFRAME":var c=this.images.get(a.node);c?this.renderer.renderImage(a,b,a.borders,c):E("Error loading <"+a.node.nodeName+">",a.node);break;case"IMG":var d=this.images.get(a.node.src);d?this.renderer.renderImage(a,b,a.borders,d):E("Error loading <img>",a.node.src);break;case"CANVAS":this.renderer.renderImage(a,b,a.borders,{image:a.node});break;case"SELECT":case"INPUT":case"TEXTAREA":this.paintFormValue(a)}},this)},O.prototype.paintFormValue=function(a){if(a.getValue().length>0){var b=a.node.ownerDocument,c=b.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(b){try{c.style[b]=a.css(b)}catch(d){E("html2canvas: Parse: Exception caught in renderFormValue: "+d.message)}});var e=a.parseBounds();c.style.position="fixed",c.style.left=e.left+"px",c.style.top=e.top+"px",c.textContent=a.getValue(),b.body.appendChild(c),this.paintText(new Kb(c.firstChild,a)),b.body.removeChild(c)}},O.prototype.paintText=function(b){b.applyTextTransform();var c=a.html2canvas.punycode.ucs2.decode(b.node.data),d=this.options.letterRendering&&!bb(b)||wb(b.node.data)?c.map(function(b){return a.html2canvas.punycode.ucs2.encode([b])}):ub(c),e=b.parent.fontWeight(),f=b.parent.css("fontSize"),g=b.parent.css("fontFamily"),h=b.parent.parseTextShadows();this.renderer.font(b.parent.css("color"),b.parent.css("fontStyle"),b.parent.css("fontVariant"),e,f,g),h.length?this.renderer.fontShadow(h[0].color,h[0].offsetX,h[0].offsetY,h[0].blur):this.renderer.clearShadow(),this.renderer.clip(b.parent.clip,function(){d.map(this.parseTextBounds(b),this).forEach(function(a,c){a&&(this.renderer.text(d[c],a.left,a.bottom),this.renderTextDecoration(b.parent,a,this.fontMetrics.getMetrics(g,f)))},this)},this)},O.prototype.renderTextDecoration=function(a,b,c){switch(a.css("textDecoration").split(" ")[0]){case"underline":this.renderer.rectangle(b.left,Math.round(b.top+c.baseline+c.lineWidth),b.width,1,a.css("color"));break;case"overline":this.renderer.rectangle(b.left,Math.round(b.top),b.width,1,a.css("color"));break;case"line-through":this.renderer.rectangle(b.left,Math.ceil(b.top+c.middle+c.lineWidth),b.width,1,a.css("color"))}},O.prototype.parseBorders=function(a){var b=a.parseBounds(),c=cb(a),d=["Top","Right","Bottom","Left"].map(function(b){return{width:a.cssInt("border"+b+"Width"),color:a.css("border"+b+"Color"),args:null}}),e=T(b,c,d);return{clip:this.parseBackgroundClip(a,e,d,c,b),borders:d.map(function(a,f){if(a.width>0){var g=b.left,h=b.top,i=b.width,j=b.height-d[2].width;switch(f){case 0:j=d[0].width,a.args=V({c1:[g,h],c2:[g+i,h],c3:[g+i-d[1].width,h+j],c4:[g+d[3].width,h+j]},c[0],c[1],e.topLeftOuter,e.topLeftInner,e.topRightOuter,e.topRightInner);break;case 1:g=b.left+b.width-d[1].width,i=d[1].width,a.args=V({c1:[g+i,h],c2:[g+i,h+j+d[2].width],c3:[g,h+j],c4:[g,h+d[0].width]},c[1],c[2],e.topRightOuter,e.topRightInner,e.bottomRightOuter,e.bottomRightInner);break;case 2:h=h+b.height-d[2].width,j=d[2].width,a.args=V({c1:[g+i,h+j],c2:[g,h+j],c3:[g+d[3].width,h],c4:[g+i-d[3].width,h]},c[2],c[3],e.bottomRightOuter,e.bottomRightInner,e.bottomLeftOuter,e.bottomLeftInner);break;case 3:i=d[3].width,a.args=V({c1:[g,h+j+d[2].width],c2:[g,h],c3:[g+i,h+d[0].width],c4:[g+i,h+j]},c[3],c[0],e.bottomLeftOuter,e.bottomLeftInner,e.topLeftOuter,e.topLeftInner)}}return a})}},O.prototype.parseBackgroundClip=function(a,b,c,d,e){var f=a.css("backgroundClip"),g=[];switch(f){case"content-box":case"padding-box":W(g,d[0],d[1],b.topLeftInner,b.topRightInner,e.left+c[3].width,e.top+c[0].width),W(g,d[1],d[2],b.topRightInner,b.bottomRightInner,e.left+e.width-c[1].width,e.top+c[0].width),W(g,d[2],d[3],b.bottomRightInner,b.bottomLeftInner,e.left+e.width-c[1].width,e.top+e.height-c[2].width),W(g,d[3],d[0],b.bottomLeftInner,b.topLeftInner,e.left+c[3].width,e.top+e.height-c[2].width);break;default:W(g,d[0],d[1],b.topLeftOuter,b.topRightOuter,e.left,e.top),W(g,d[1],d[2],b.topRightOuter,b.bottomRightOuter,e.left+e.width,e.top),W(g,d[2],d[3],b.bottomRightOuter,b.bottomLeftOuter,e.left+e.width,e.top+e.height),W(g,d[3],d[0],b.bottomLeftOuter,b.topLeftOuter,e.left,e.top+e.height)}return g};var Tb=0,Ub="withCredentials"in new XMLHttpRequest,Vb="crossOrigin"in new Image;Db.prototype.cloneTo=function(a){Db.prototype.cloneTo.call(this,a),a.isPseudoElement=!0,a.before=this.before},Db.prototype=Object.create(F.prototype),Db.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()},Db.prototype.cleanDOM=function(){this.node.parentNode.removeChild(this.node),this.parent.node.className=this.parent.node.className.replace(this.getHideClass(),"")},Db.prototype.getHideClass=function(){return this["PSEUDO_HIDE_ELEMENT_CLASS_"+(this.before?"BEFORE":"AFTER")]},Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE="___html2canvas___pseudoelement_before",Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER="___html2canvas___pseudoelement_after",Eb.prototype.renderImage=function(a,b,c,d){var e=a.cssInt("paddingLeft"),f=a.cssInt("paddingTop"),g=a.cssInt("paddingRight"),h=a.cssInt("paddingBottom"),i=c.borders,j=b.width-(i[1].width+i[3].width+e+g),k=b.height-(i[0].width+i[2].width+f+h);this.drawImage(d,0,0,d.image.width||j,d.image.height||k,b.left+e+i[3].width,b.top+f+i[0].width,j,k)},Eb.prototype.renderBackground=function(a,b,c){b.height>0&&b.width>0&&(this.renderBackgroundColor(a,b),this.renderBackgroundImage(a,b,c))},Eb.prototype.renderBackgroundColor=function(a,b){var c=a.css("backgroundColor");this.isTransparent(c)||this.rectangle(b.left,b.top,b.width,b.height,a.css("backgroundColor"))},Eb.prototype.renderBorders=function(a){a.forEach(this.renderBorder,this)},Eb.prototype.renderBorder=function(a){this.isTransparent(a.color)||null===a.args||this.drawShape(a.args,a.color)},Eb.prototype.renderBackgroundImage=function(a,b,c){var d=a.parseBackgroundImages();d.reverse().forEach(function(d,e,f){switch(d.method){case"url":var g=this.images.get(d.args[0]);g?this.renderBackgroundRepeating(a,b,g,f.length-(e+1),c):E("Error loading background-image",d.args[0]);break;case"linear-gradient":case"gradient":var h=this.images.get(d.value);h?this.renderBackgroundGradient(h,b,c):E("Error loading background-image",d.args[0]);break;case"none":break;default:E("Unknown background-image type",d.args[0])}},this)},Eb.prototype.renderBackgroundRepeating=function(a,b,c,d,e){var f=a.parseBackgroundSize(b,c.image,d),g=a.parseBackgroundPosition(b,c.image,d,f),h=a.parseBackgroundRepeat(d);switch(h){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+e[3],b.top+g.top+e[0],99999,f.height,e);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+e[0],f.width,99999,e);break;case"no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+g.top+e[0],f.width,f.height,e);break;default:this.renderBackgroundRepeat(c,g,f,{top:b.top,left:b.left},e[3],e[0])}},Eb.prototype.isTransparent=function(a){return!a||"transparent"===a||"rgba(0, 0, 0, 0)"===a},Fb.prototype=Object.create(F.prototype),Fb.prototype.getParentStack=function(a){var b=this.parent?this.parent.stack:null;return b?b.ownStacking?b:b.getParentStack(a):a.stack},Gb.prototype.testRangeBounds=function(a){var b,c,d,e,f=!1;return a.createRange&&(b=a.createRange(),b.getBoundingClientRect&&(c=a.createElement("boundtest"),c.style.height="123px",c.style.display="block",a.body.appendChild(c),b.selectNode(c),d=b.getBoundingClientRect(),e=d.height,123===e&&(f=!0),a.body.removeChild(c))),f},Gb.prototype.testCORS=function(){return"undefined"!=typeof(new Image).crossOrigin},Gb.prototype.testSVG=function(){var a=new Image,c=b.createElement("canvas"),d=c.getContext("2d");a.src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";try{d.drawImage(a,0,0),c.toDataURL()}catch(e){return!1}return!0},Hb.prototype.hasFabric=function(){return html2canvas.fabric?Promise.resolve():Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg"))},Hb.prototype.inlineFormatting=function(a){return/^data:image\/svg\+xml;base64,/.test(a)?this.decode64(this.removeContentType(a)):this.removeContentType(a)},Hb.prototype.removeContentType=function(a){return a.replace(/^data:image\/svg\+xml(;base64)?,/,"")},Hb.prototype.isInline=function(a){return/^data:image\/svg\+xml/i.test(a)},Hb.prototype.createCanvas=function(a){var b=this;return function(c,d){var e=new html2canvas.fabric.StaticCanvas("c");b.image=e.lowerCanvasEl,e.setWidth(d.width).setHeight(d.height).add(html2canvas.fabric.util.groupSVGElements(c,d)).renderAll(),a(e.lowerCanvasEl)}},Hb.prototype.decode64=function(b){return"function"==typeof a.atob?a.atob(b):Ib(b)},Jb.prototype=Object.create(Hb.prototype),Kb.prototype=Object.create(F.prototype),Kb.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},Kb.prototype.transform=function(a){var b=this.node.data;switch(a){case"lowercase":return b.toLowerCase();case"capitalize":return b.replace(/(^|\s|:|-|\(|\))([a-z])/g,Lb);case"uppercase":return b.toUpperCase();default:return b}},Mb.prototype=Object.create(A.prototype),Ob.prototype=Object.create(Eb.prototype),Ob.prototype.setFillStyle=function(a){return this.ctx.fillStyle=a,this.ctx},Ob.prototype.rectangle=function(a,b,c,d,e){this.setFillStyle(e).fillRect(a,b,c,d)},Ob.prototype.drawShape=function(a,b){this.shape(a),this.setFillStyle(b).fill()},Ob.prototype.taints=function(a){if(null===a.tainted){this.taintCtx.drawImage(a.image,0,0);try{this.taintCtx.getImageData(0,0,1,1),a.tainted=!1}catch(c){this.taintCtx=b.createElement("canvas").getContext("2d"),a.tainted=!0}}return a.tainted},Ob.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){(!this.taints(a)||this.options.allowTaint)&&this.ctx.drawImage(a.image,b,c,d,e,f,g,h,i)},Ob.prototype.clip=function(a,b,c){this.ctx.save(),a.filter(Pb).forEach(function(a){this.shape(a).clip()},this),b.call(c),this.ctx.restore()},Ob.prototype.shape=function(a){return this.ctx.beginPath(),a.forEach(function(a,b){"rect"===a[0]?this.ctx.rect.apply(this.ctx,a.slice(1)):this.ctx[0===b?"moveTo":a[0]+"To"].apply(this.ctx,a.slice(1))},this),this.ctx.closePath(),this.ctx},Ob.prototype.font=function(a,b,c,d,e,f){this.setFillStyle(a).font=[b,c,d,e,f].join(" ").split(",")[0]},Ob.prototype.fontShadow=function(a,b,c,d){this.setVariable("shadowColor",a).setVariable("shadowOffsetY",b).setVariable("shadowOffsetX",c).setVariable("shadowBlur",d)},Ob.prototype.clearShadow=function(){this.setVariable("shadowColor","rgba(0,0,0,0)")},Ob.prototype.setOpacity=function(a){this.ctx.globalAlpha=a},Ob.prototype.setTransform=function(a){this.ctx.translate(a.origin[0],a.origin[1]),this.ctx.transform.apply(this.ctx,a.matrix),this.ctx.translate(-a.origin[0],-a.origin[1])},Ob.prototype.setVariable=function(a,b){return this.variables[a]!==b&&(this.variables[a]=this.ctx[a]=b),this},Ob.prototype.text=function(a,b,c){this.ctx.fillText(a,b,c)},Ob.prototype.backgroundRepeatShape=function(a,b,c,d,e,f,g,h,i){var j=[["line",Math.round(e),Math.round(f)],["line",Math.round(e+g),Math.round(f)],["line",Math.round(e+g),Math.round(h+f)],["line",Math.round(e),Math.round(h+f)]];this.clip([j],function(){this.renderBackgroundRepeat(a,b,c,d,i[3],i[0])},this)},Ob.prototype.renderBackgroundRepeat=function(a,b,c,d,e,f){var g=Math.round(d.left+b.left+e),h=Math.round(d.top+b.top+f);this.setFillStyle(this.ctx.createPattern(this.resizeImage(a,c),"repeat")),this.ctx.translate(g,h),this.ctx.fill(),this.ctx.translate(-g,-h)},Ob.prototype.renderBackgroundGradient=function(a,b){if(a instanceof D){var c=this.ctx.createLinearGradient(b.left+b.width*a.x0,b.top+b.height*a.y0,b.left+b.width*a.x1,b.top+b.height*a.y1);a.colorStops.forEach(function(a){c.addColorStop(a.stop,a.color)}),this.rectangle(b.left,b.top,b.width,b.height,c)}},Ob.prototype.resizeImage=function(a,c){var d=a.image;if(d.width===c.width&&d.height===c.height)return d;var e,f=b.createElement("canvas");return f.width=c.width,f.height=c.height,e=f.getContext("2d"),e.drawImage(d,0,0,d.width,d.height,0,0,c.width,c.height),f}}).call({},window,document);
\ No newline at end of file
+(function(a,b,c,d,e,f,g){function h(a,b,c,d){return o(a,a,c,d,b).then(function(e){E("Document cloned");var f="["+Qb+"='true']";a.querySelector(f).removeAttribute(Qb);var g=e.contentWindow,h=g.document.querySelector(f),j=Promise.resolve("function"==typeof b.onclone?b.onclone(g.document):!0);return j.then(function(){return i(h,e,b,c,d)})})}function i(a,c,d,e,f){var g=c.contentWindow,h=new Gb(g.document),i=new C(d,h),n=M(a),o="view"===d.type?e:l(g.document),p="view"===d.type?f:m(g.document),q=new Ob(o,p,i,d,b),r=new O(a,q,h,i,d);return r.ready.then(function(){E("Finished rendering");var b;return b="view"===d.type?k(q.canvas,{width:q.canvas.width,height:q.canvas.height,top:0,left:0,x:0,y:0}):a===g.document.body||a===g.document.documentElement||null!=d.canvas?q.canvas:k(q.canvas,{width:null!=d.width?d.width:n.width,height:null!=d.height?d.height:n.height,top:n.top,left:n.left,x:g.pageXOffset,y:g.pageYOffset}),j(c,d),b})}function j(a,b){b.removeContainer&&(a.parentNode.removeChild(a),E("Cleaned up container"))}function k(a,c){var d=b.createElement("canvas"),e=Math.min(a.width-1,Math.max(0,c.left)),f=Math.min(a.width,Math.max(1,c.left+c.width)),g=Math.min(a.height-1,Math.max(0,c.top)),h=Math.min(a.height,Math.max(1,c.top+c.height));return d.width=c.width,d.height=c.height,E("Cropping canvas at:","left:",c.left,"top:",c.top,"width:",f-e,"height:",h-g),E("Resulting crop with width",c.width,"and height",c.height," with x",e,"and y",g),d.getContext("2d").drawImage(a,e,g,f-e,h-g,c.x,c.y,f-e,h-g),d}function l(a){return Math.max(Math.max(a.body.scrollWidth,a.documentElement.scrollWidth),Math.max(a.body.offsetWidth,a.documentElement.offsetWidth),Math.max(a.body.clientWidth,a.documentElement.clientWidth))}function m(a){return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))}function n(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}function o(a,b,c,d,e){r(a);var f=a.documentElement.cloneNode(!0),g=b.createElement("iframe");return g.className="html2canvas-container",g.style.visibility="hidden",g.style.position="absolute",g.style.left=g.style.top="-10000px",g.width=c,g.height=d,g.scrolling="no",b.body.appendChild(g),new Promise(function(b){var c=g.contentWindow.document;g.contentWindow.onload=g.onload=function(){var f=setInterval(function(){c.body.childNodes.length>0&&(s(a,c),clearInterval(f),"view"===e.type&&g.contentWindow.scrollTo(d,h),b(g))},50)};var d=a.defaultView.pageXOffset,h=a.defaultView.pageYOffset;c.open(),c.write("<!DOCTYPE html><html></html>"),(d!==a.defaultView.pageXOffset||h!==a.defaultView.pageYOffset)&&a.defaultView.scrollTo(d,h),c.replaceChild(e.javascriptEnabled===!0?c.adoptNode(f):t(c.adoptNode(f)),c.documentElement),c.close()})}function p(b,c,d,e,f,g){return new xb(b,c,a.document).then(q(b)).then(function(a){return o(a,d,e,f,g)})}function q(a){return function(c){var d,e=new DOMParser;try{d=e.parseFromString(c,"text/html")}catch(f){E("DOMParser not supported, falling back to createHTMLDocument"),d=b.implementation.createHTMLDocument("");try{d.open(),d.write(c),d.close()}catch(g){E("createHTMLDocument write not supported, falling back to document.body.innerHTML"),d.body.innerHTML=c}}var h=d.querySelector("base");if(!h||!h.href.host){var i=d.createElement("base");i.href=a,d.head.insertBefore(i,d.head.firstChild)}return d}}function r(a){[].slice.call(a.querySelectorAll("canvas"),0).forEach(function(a){a.setAttribute(Rb,"canvas-"+Sb++)})}function s(a,b){[].slice.call(a.querySelectorAll("["+Rb+"]"),0).forEach(function(a){try{var c=b.querySelector("["+Rb+'="'+a.getAttribute(Rb)+'"]');c&&(c.width=a.width,c.height=a.height,c.getContext("2d").putImageData(a.getContext("2d").getImageData(0,0,a.width,a.height),0,0))}catch(d){E("Unable to copy canvas content from",a,d)}a.removeAttribute(Rb)})}function t(a){return[].slice.call(a.childNodes,0).filter(u).forEach(function(b){"SCRIPT"===b.tagName?a.removeChild(b):t(b)}),a}function u(a){return a.nodeType===Node.ELEMENT_NODE}function v(a){var c=b.createElement("a");return c.href=a,c.href=c.href,c}function w(a){if(this.src=a,E("DummyImageContainer for",a),!this.promise||!this.image){E("Initiating DummyImageContainer"),w.prototype.image=new Image;var b=this.image;w.prototype.promise=new Promise(function(a,c){b.onload=a,b.onerror=c,b.src=n(),b.complete===!0&&a(b)})}}function x(a,c){var d,e,f=b.createElement("div"),g=b.createElement("img"),h=b.createElement("span"),i="Hidden Text";f.style.visibility="hidden",f.style.fontFamily=a,f.style.fontSize=c,f.style.margin=0,f.style.padding=0,b.body.appendChild(f),g.src=n(),g.width=1,g.height=1,g.style.margin=0,g.style.padding=0,g.style.verticalAlign="baseline",h.style.fontFamily=a,h.style.fontSize=c,h.style.margin=0,h.style.padding=0,h.appendChild(b.createTextNode(i)),f.appendChild(h),f.appendChild(g),d=g.offsetTop-h.offsetTop+1,f.removeChild(h),f.appendChild(b.createTextNode(i)),f.style.lineHeight="normal",g.style.verticalAlign="super",e=g.offsetTop-f.offsetTop+1,b.body.removeChild(f),this.baseline=d,this.lineWidth=1,this.middle=e}function y(){this.data={}}function z(a,b,c){this.image=null,this.src=a;var d=this,e=M(a);this.promise=(b?new Promise(function(b){"about:blank"===a.contentWindow.document.URL||null==a.contentWindow.document.documentElement?a.contentWindow.onload=a.onload=function(){b(a)}:b(a)}):this.proxyLoad(c.proxy,e,c)).then(function(a){return html2canvas(a.contentWindow.document.documentElement,{type:"view",width:a.width,height:a.height,proxy:c.proxy,javascriptEnabled:c.javascriptEnabled,removeContainer:c.removeContainer,allowTaint:c.allowTaint,imageTimeout:c.imageTimeout/2})}).then(function(a){return d.image=a})}function A(a){this.src=a.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=Promise.resolve(!0)}function B(a,b){this.src=a,this.image=new Image;var c=this;this.tainted=null,this.promise=new Promise(function(d,e){c.image.onload=d,c.image.onerror=e,b&&(c.image.crossOrigin="anonymous"),c.image.src=a,c.image.complete===!0&&d(c.image)})}function C(b,c){this.link=null,this.options=b,this.support=c,this.origin=this.getOrigin(a.location.href)}function D(a){A.apply(this,arguments),this.type=this.TYPES.LINEAR;var b=null===a.args[0].match(this.stepRegExp);b?a.args[0].split(" ").reverse().forEach(function(a){switch(a){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 b=this.y0,c=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=c,this.y1=b}},this):(this.y0=0,this.y1=1),this.colorStops=a.args.slice(b?1:0).map(function(a){var b=a.match(this.stepRegExp);return{color:b[1],stop:"%"===b[3]?b[2]/100:null}},this),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(a,b){null===a.stop&&this.colorStops.slice(b).some(function(c,d){return null!==c.stop?(a.stop=(c.stop-this.colorStops[b-1].stop)/(d+1)+this.colorStops[b-1].stop,!0):!1},this)},this)}function E(){a.html2canvas.logging&&a.console&&a.console.log&&Function.prototype.bind.call(a.console.log,a.console).apply(a.console,[Date.now()-a.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function F(a,b){this.node=a,this.parent=b,this.stack=null,this.bounds=null,this.borders=null,this.clip=[],this.backgroundClip=[],this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null,this.isPseudoElement=!1,this.opacity=null}function G(a){var b=a.options[a.selectedIndex||0];return b?b.text||"":""}function H(a){return a&&"matrix"===a[1]?a[2].split(",").map(function(a){return parseFloat(a.trim())}):void 0}function I(a){return-1!==a.toString().indexOf("%")}function J(a){var b,c,d,e,f,g,h,i=" \r\n	",j=[],k=0,l=0,m=function(){b&&('"'===c.substr(0,1)&&(c=c.substr(1,c.length-2)),c&&h.push(c),"-"===b.substr(0,1)&&(e=b.indexOf("-",1)+1)>0&&(d=b.substr(0,e),b=b.substr(e)),j.push({prefix:d,method:b.toLowerCase(),value:f,args:h,image:null})),h=[],b=d=c=f=""};return h=[],b=d=c=f="",a.split("").forEach(function(a){if(!(0===k&&i.indexOf(a)>-1)){switch(a){case'"':g?g===a&&(g=null):g=a;break;case"(":if(g)break;if(0===k)return k=1,void(f+=a);l++;break;case")":if(g)break;if(1===k){if(0===l)return k=0,f+=a,void m();l--}break;case",":if(g)break;if(0===k)return void m();if(1===k&&0===l&&!b.match(/^url$/i))return h.push(c),c="",void(f+=a)}f+=a,0===k?b+=a:c+=a}}),m(),j}function K(a){return a.replace("px","")}function L(a){return parseFloat(a)}function M(a){if(a.getBoundingClientRect){var b=a.getBoundingClientRect(),c=null==a.offsetWidth?b.width:a.offsetWidth;return{top:b.top,bottom:b.bottom||b.top+b.height,right:b.left+c,left:b.left,width:c,height:null==a.offsetHeight?b.height:a.offsetHeight}}return{}}function N(a){var b=a.offsetParent?N(a.offsetParent):{top:0,left:0};return{top:a.offsetTop+b.top,bottom:a.offsetTop+a.offsetHeight+b.top,right:a.offsetLeft+b.left+a.offsetWidth,left:a.offsetLeft+b.left,width:a.offsetWidth,height:a.offsetHeight}}function O(a,b,c,d,e){E("Starting NodeParser"),this.renderer=b,this.options=e,this.range=null,this.support=c,this.renderQueue=[],this.stack=new Fb(!0,1,a.ownerDocument,null);var f=new F(a,null);if(a===a.ownerDocument.documentElement){var g=new F(this.renderer.isTransparent(f.css("backgroundColor"))?a.ownerDocument.body:a.ownerDocument.documentElement,null);b.rectangle(0,0,b.width,b.height,g.css("backgroundColor"))}f.visibile=f.isElementVisible(),this.createPseudoHideStyles(a.ownerDocument),this.disableAnimations(a.ownerDocument),this.nodes=sb([f].concat(this.getChildren(f)).filter(function(a){return a.visible=a.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new y,E("Fetched nodes, total:",this.nodes.length),E("Calculate overflow clips"),this.calculateOverflowClips(),E("Start fetching images"),this.images=d.fetch(this.nodes.filter(jb)),this.ready=this.images.ready.then(ob(function(){return E("Images loaded, starting parsing"),E("Creating stacking contexts"),this.createStackingContexts(),E("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.parse(this.stack),E("Render queue created with "+this.renderQueue.length+" items"),new Promise(ob(function(a){e.async?"function"==typeof e.async?e.async.call(this,this.renderQueue,a):this.renderQueue.length>0?(this.renderIndex=0,this.asyncRenderer(this.renderQueue,a)):a():(this.renderQueue.forEach(this.paint,this),a())},this))},this))}function P(a){return a.parent&&a.parent.clip.length}function Q(a){return a.replace(/(\-[a-z])/g,function(a){return a.toUpperCase().replace("-","")})}function R(){}function S(a,b,c,d){var e=4*((Math.sqrt(2)-1)/3),f=c*e,g=d*e,h=a+c,i=b+d;return{topLeft:U({x:a,y:i},{x:a,y:i-g},{x:h-f,y:b},{x:h,y:b}),topRight:U({x:a,y:b},{x:a+f,y:b},{x:h,y:i-g},{x:h,y:i}),bottomRight:U({x:h,y:b},{x:h,y:b+g},{x:a+f,y:i},{x:a,y:i}),bottomLeft:U({x:h,y:i},{x:h-f,y:i},{x:a,y:b+g},{x:a,y:b})}}function T(a,b,c){var d=a.left,e=a.top,f=a.width,g=a.height,h=b[0][0],i=b[0][1],j=b[1][0],k=b[1][1],l=b[2][0],m=b[2][1],n=b[3][0],o=b[3][1],p=f-j,q=g-m,r=f-l,s=g-o;return{topLeftOuter:S(d,e,h,i).topLeft.subdivide(.5),topLeftInner:S(d+c[3].width,e+c[0].width,Math.max(0,h-c[3].width),Math.max(0,i-c[0].width)).topLeft.subdivide(.5),topRightOuter:S(d+p,e,j,k).topRight.subdivide(.5),topRightInner:S(d+Math.min(p,f+c[3].width),e+c[0].width,p>f+c[3].width?0:j-c[3].width,k-c[0].width).topRight.subdivide(.5),bottomRightOuter:S(d+r,e+q,l,m).bottomRight.subdivide(.5),bottomRightInner:S(d+Math.min(r,f-c[3].width),e+Math.min(q,g+c[0].width),Math.max(0,l-c[1].width),m-c[2].width).bottomRight.subdivide(.5),bottomLeftOuter:S(d,e+s,n,o).bottomLeft.subdivide(.5),bottomLeftInner:S(d+c[3].width,e+s,Math.max(0,n-c[3].width),o-c[2].width).bottomLeft.subdivide(.5)}}function U(a,b,c,d){var e=function(a,b,c){return{x:a.x+(b.x-a.x)*c,y:a.y+(b.y-a.y)*c}};return{start:a,startControl:b,endControl:c,end:d,subdivide:function(f){var g=e(a,b,f),h=e(b,c,f),i=e(c,d,f),j=e(g,h,f),k=e(h,i,f),l=e(j,k,f);return[U(a,g,j,l),U(l,k,i,d)]},curveTo:function(a){a.push(["bezierCurve",b.x,b.y,c.x,c.y,d.x,d.y])},curveToReversed:function(d){d.push(["bezierCurve",c.x,c.y,b.x,b.y,a.x,a.y])}}}function V(a,b,c,d,e,f,g){var h=[];return b[0]>0||b[1]>0?(h.push(["line",d[1].start.x,d[1].start.y]),d[1].curveTo(h)):h.push(["line",a.c1[0],a.c1[1]]),c[0]>0||c[1]>0?(h.push(["line",f[0].start.x,f[0].start.y]),f[0].curveTo(h),h.push(["line",g[0].end.x,g[0].end.y]),g[0].curveToReversed(h)):(h.push(["line",a.c2[0],a.c2[1]]),h.push(["line",a.c3[0],a.c3[1]])),b[0]>0||b[1]>0?(h.push(["line",e[1].end.x,e[1].end.y]),e[1].curveToReversed(h)):h.push(["line",a.c4[0],a.c4[1]]),h}function W(a,b,c,d,e,f,g){b[0]>0||b[1]>0?(a.push(["line",d[0].start.x,d[0].start.y]),d[0].curveTo(a),d[1].curveTo(a)):a.push(["line",f,g]),(c[0]>0||c[1]>0)&&a.push(["line",e[0].start.x,e[0].start.y])}function X(a){return a.cssInt("zIndex")<0}function Y(a){return a.cssInt("zIndex")>0}function Z(a){return 0===a.cssInt("zIndex")}function $(a){return-1!==["inline","inline-block","inline-table"].indexOf(a.css("display"))}function _(a){return a instanceof Fb}function ab(a){return a.node.data.trim().length>0}function bb(a){return/^(normal|none|0px)$/.test(a.parent.css("letterSpacing"))}function cb(a){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(b){var c=a.css("border"+b+"Radius"),d=c.split(" ");return d.length<=1&&(d[1]=d[0]),d.map(pb)})}function db(a){return a.nodeType===Node.TEXT_NODE||a.nodeType===Node.ELEMENT_NODE}function eb(a){var b=a.css("position"),c=-1!==["absolute","relative","fixed"].indexOf(b)?a.css("zIndex"):"auto";return"auto"!==c}function fb(a){return"static"!==a.css("position")}function gb(a){return"none"!==a.css("float")}function hb(a){return-1!==["inline-block","inline-table"].indexOf(a.css("display"))}function ib(a){var b=this;return function(){return!a.apply(b,arguments)}}function jb(a){return a.node.nodeType===Node.ELEMENT_NODE}function kb(a){return a.isPseudoElement===!0}function lb(a){return a.node.nodeType===Node.TEXT_NODE}function mb(a){return function(b,c){return b.cssInt("zIndex")+a.indexOf(b)/a.length-(c.cssInt("zIndex")+a.indexOf(c)/a.length)}}function nb(a){return a.getOpacity()<1}function ob(a,b){return function(){return a.apply(b,arguments)}}function pb(a){return parseInt(a,10)}function qb(a){return a.width}function rb(a){return a.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(a.node.nodeName)}function sb(a){return[].concat.apply([],a)}function tb(a){var b=a.substr(0,1);return b===a.substr(a.length-1)&&b.match(/'|"/)?a.substr(1,a.length-2):a}function ub(b){for(var c,d=[],e=0,f=!1;b.length;)vb(b[e])===f?(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)),f=!f,e=0):e++,e>=b.length&&(c=b.splice(0,e),c.length&&d.push(a.html2canvas.punycode.ucs2.encode(c)));return d}function vb(a){return-1!==[32,13,10,9,45].indexOf(a)}function wb(a){return/[^\u0000-\u00ff]/.test(a)}function xb(a,b,c){var d=Ab(Ub),e=Bb(b,a,d);return Ub?Nb(e):zb(c,e,d).then(function(a){return Ib(a.content)})}function yb(a,b,c){var d=Ab(Vb),e=Bb(b,a,d);return Vb?Promise.resolve(e):zb(c,e,d).then(function(a){return"data:"+a.type+";base64,"+a.content})}function zb(b,c,d){return new Promise(function(e,f){var g=b.createElement("script"),h=function(){delete a.html2canvas.proxy[d],b.body.removeChild(g)};a.html2canvas.proxy[d]=function(a){h(),e(a)},g.src=c,g.onerror=function(a){h(),f(a)},b.body.appendChild(g)})}function Ab(a){return a?"":"html2canvas_"+Date.now()+"_"+ ++Tb+"_"+Math.round(1e5*Math.random())}function Bb(a,b,c){return a+"?url="+encodeURIComponent(b)+(c.length?"&callback=html2canvas.proxy."+c:"")}function Cb(a,c){var d=(b.createElement("script"),b.createElement("a"));d.href=a,a=d.href,this.src=a,this.image=new Image;var e=this;this.promise=new Promise(function(d,f){e.image.crossOrigin="Anonymous",e.image.onload=d,e.image.onerror=f,new yb(a,c,b).then(function(a){e.image.src=a})["catch"](f)})}function Db(a,b,c){F.call(this,a,b),this.isPseudoElement=!0,this.before=":before"===c}function Eb(a,b,c,d,e){this.width=a,this.height=b,this.images=c,this.options=d,this.document=e}function Fb(a,b,c,d){F.call(this,c,d),this.ownStacking=a,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*b}function Gb(a){this.rangeBounds=this.testRangeBounds(a),this.cors=this.testCORS(),this.svg=this.testSVG()}function Hb(a){this.src=a,this.image=null;var b=this;this.promise=this.hasFabric().then(function(){return b.isInline(a)?Promise.resolve(b.inlineFormatting(a)):Nb(a)}).then(function(a){return new Promise(function(c){html2canvas.fabric.loadSVGFromString(a,b.createCanvas.call(b,c))})})}function Ib(a){var b,c,d,e,f,g,h,i,j="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",k=a.length,l="";for(b=0;k>b;b+=4)c=j.indexOf(a[b]),d=j.indexOf(a[b+1]),e=j.indexOf(a[b+2]),f=j.indexOf(a[b+3]),g=c<<2|d>>4,h=(15&d)<<4|e>>2,i=(3&e)<<6|f,l+=64===e?String.fromCharCode(g):64===f||-1===f?String.fromCharCode(g,h):String.fromCharCode(g,h,i);return l}function Jb(a,b){this.src=a,this.image=null;var c=this;this.promise=b?new Promise(function(b,d){c.image=new Image,c.image.onload=b,c.image.onerror=d,c.image.src="data:image/svg+xml,"+(new XMLSerializer).serializeToString(a),c.image.complete===!0&&b(c.image)}):this.hasFabric().then(function(){return new Promise(function(b){html2canvas.fabric.parseSVGDocument(a,c.createCanvas.call(c,b))})})}function Kb(a,b){F.call(this,a,b)}function Lb(a,b,c){return a.length>0?b+c.toUpperCase():void 0}function Mb(a){A.apply(this,arguments),this.type="linear"===a.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}function Nb(a){return new Promise(function(b,c){var d=new XMLHttpRequest;d.open("GET",a),d.onload=function(){200===d.status?b(d.responseText):c(new Error(d.statusText))},d.onerror=function(){c(new Error("Network Error"))},d.send()})}function Ob(a,b){Eb.apply(this,arguments),this.canvas=this.options.canvas||this.document.createElement("canvas"),this.options.canvas||(this.canvas.width=a,this.canvas.height=b),this.ctx=this.canvas.getContext("2d"),this.options.background&&this.rectangle(0,0,a,b,this.options.background),this.taintCtx=this.document.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},E("Initialized CanvasRenderer with size",a,"x",b)}function Pb(a){return a.length>0}if(!function(){var c,d,f,g;!function(){var a={},b={};c=function(b,c,d){a[b]={deps:c,callback:d}},g=f=d=function(c){function e(a){if("."!==a.charAt(0))return a;for(var b=a.split("/"),d=c.split("/").slice(0,-1),e=0,f=b.length;f>e;e++){var g=b[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(g._eak_seen=a,b[c])return b[c];if(b[c]={},!a[c])throw new Error("Could not find module "+c);for(var f,h=a[c],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)k.push("exports"===i[l]?f={}:d(e(i[l])));var n=j.apply(this,k);return b[c]=f||n}}(),c("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j<a.length;j++)g=a[j],g&&e(g.then)?g.then(d(j),c):f(j,g)})}var d=a.isArray,e=a.isFunction;b.all=c}),c("promise/asap",["exports"],function(c){"use strict";function d(){return function(){process.nextTick(h)}}function f(){var a=0,c=new l(h),d=b.createTextNode("");return c.observe(d,{characterData:!0}),function(){d.data=a=++a%2}}function g(){return function(){m.setTimeout(h,1)}}function h(){for(var a=0;a<n.length;a++){var b=n[a],c=b[0],d=b[1];c(d)}n=[]}function i(a,b){var c=n.push([a,b]);1===c&&j()}var j,k="undefined"!=typeof a?a:{},l=k.MutationObserver||k.WebKitMutationObserver,m="undefined"!=typeof e?e:this,n=[];j="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?d():l?f():g(),c.asap=i}),c("promise/cast",["exports"],function(a){"use strict";function b(a){if(a&&"object"==typeof a&&a.constructor===this)return a;var b=this;return new b(function(b){b(a)})}a.cast=b}),c("promise/config",["exports"],function(a){"use strict";function b(a,b){return 2!==arguments.length?c[a]:void(c[a]=b)}var c={instrument:!1};a.config=c,a.configure=b}),c("promise/polyfill",["./promise","./utils","exports"],function(b,c,d){"use strict";function e(){var b="Promise"in a&&"cast"in a.Promise&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var b;return new a.Promise(function(a){b=a}),g(b)}();b||(a.Promise=f)}var f=b.Promise,g=c.isFunction;d.polyfill=e}),c("promise/promise",["./config","./utils","./cast","./all","./race","./resolve","./reject","./asap","exports"],function(a,b,c,d,e,f,g,h,i){"use strict";function j(a){if(!w(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof j))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],k(a,this)}function k(a,b){function c(a){p(b,a)}function d(a){r(b,a)}try{a(c,d)}catch(e){d(e)}}function l(a,b,c,d){var e,f,g,h,i=w(c);if(i)try{e=c(d),g=!0}catch(j){h=!0,f=j}else e=d,g=!0;o(b,e)||(i&&g?p(b,e):h?r(b,f):a===F?p(b,e):a===G&&r(b,e))}function m(a,b,c,d){var e=a._subscribers,f=e.length;e[f]=b,e[f+F]=c,e[f+G]=d}function n(a,b){for(var c,d,e=a._subscribers,f=a._detail,g=0;g<e.length;g+=3)c=e[g],d=e[g+b],l(b,c,d,f);a._subscribers=null}function o(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(v(b)&&(d=b.then,w(d)))return d.call(b,function(d){return c?!0:(c=!0,void(b!==d?p(a,d):q(a,d)))},function(b){return c?!0:(c=!0,void r(a,b))}),!0}catch(e){return c?!0:(r(a,e),!0)}return!1}function p(a,b){a===b?q(a,b):o(a,b)||q(a,b)}function q(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(s,a))}function r(a,b){a._state===D&&(a._state=E,a._detail=b,u.async(t,a))}function s(a){n(a,a._state=F)}function t(a){n(a,a._state=G)}var u=a.config,v=(a.configure,b.objectOrFunction),w=b.isFunction,x=(b.now,c.cast),y=d.all,z=e.race,A=f.resolve,B=g.reject,C=h.asap;u.async=C;var D=void 0,E=0,F=1,G=2;j.prototype={constructor:j,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(a,b){var c=this,d=new this.constructor(function(){});if(this._state){var e=arguments;u.async(function(){l(c._state,d,e[c._state-1],c._detail)})}else m(this,d,a,b);return d},"catch":function(a){return this.then(null,a)}},j.all=y,j.cast=x,j.race=z,j.resolve=A,j.reject=B,i.Promise=j}),c("promise/race",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to race.");return new b(function(b,c){for(var d,e=0;e<a.length;e++)d=a[e],d&&"function"==typeof d.then?d.then(b,c):b(d)})}var d=a.isArray;b.race=c}),c("promise/reject",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b,c){c(a)})}a.reject=b}),c("promise/resolve",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b){b(a)})}a.resolve=b}),c("promise/utils",["exports"],function(a){"use strict";function b(a){return c(a)||"object"==typeof a&&null!==a}function c(a){return"function"==typeof a}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}var e=Date.now||function(){return(new Date).getTime()};a.objectOrFunction=b,a.isFunction=c,a.isArray=d,a.now=e}),d("promise/polyfill").polyfill()}(),"function"!=typeof Object.create||"function"!=typeof b.createElement("canvas").getContext)return void(a.html2canvas=function(){return Promise.reject("No canvas support")});!function(a){function b(a){throw RangeError(I[a])}function g(a,b){for(var c=a.length,d=[];c--;)d[c]=b(a[c]);return d}function h(a,b){var c=a.split("@"),d="";c.length>1&&(d=c[0]+"@",a=c[1]);var e=a.split(H),f=g(e,b).join(".");return d+f}function i(a){for(var b,c,d=[],e=0,f=a.length;f>e;)b=a.charCodeAt(e++),b>=55296&&56319>=b&&f>e?(c=a.charCodeAt(e++),56320==(64512&c)?d.push(((1023&b)<<10)+(1023&c)+65536):(d.push(b),e--)):d.push(b);return d}function j(a){return g(a,function(a){var b="";return a>65535&&(a-=65536,b+=L(a>>>10&1023|55296),a=56320|1023&a),b+=L(a)}).join("")}function k(a){return 10>a-48?a-22:26>a-65?a-65:26>a-97?a-97:x}function l(a,b){return a+22+75*(26>a)-((0!=b)<<5)}function m(a,b,c){var d=0;for(a=c?K(a/B):a>>1,a+=K(a/b);a>J*z>>1;d+=x)a=K(a/J);return K(d+(J+1)*a/(a+A))}function n(a){var c,d,e,f,g,h,i,l,n,o,p=[],q=a.length,r=0,s=D,t=C;for(d=a.lastIndexOf(E),0>d&&(d=0),e=0;d>e;++e)a.charCodeAt(e)>=128&&b("not-basic"),p.push(a.charCodeAt(e));for(f=d>0?d+1:0;q>f;){for(g=r,h=1,i=x;f>=q&&b("invalid-input"),l=k(a.charCodeAt(f++)),(l>=x||l>K((w-r)/h))&&b("overflow"),r+=l*h,n=t>=i?y:i>=t+z?z:i-t,!(n>l);i+=x)o=x-n,h>K(w/o)&&b("overflow"),h*=o;c=p.length+1,t=m(r-g,c,0==g),K(r/c)>w-s&&b("overflow"),s+=K(r/c),r%=c,p.splice(r++,0,s)}return j(p)}function o(a){var c,d,e,f,g,h,j,k,n,o,p,q,r,s,t,u=[];for(a=i(a),q=a.length,c=D,d=0,g=C,h=0;q>h;++h)p=a[h],128>p&&u.push(L(p));for(e=f=u.length,f&&u.push(E);q>e;){for(j=w,h=0;q>h;++h)p=a[h],p>=c&&j>p&&(j=p);for(r=e+1,j-c>K((w-d)/r)&&b("overflow"),d+=(j-c)*r,c=j,h=0;q>h;++h)if(p=a[h],c>p&&++d>w&&b("overflow"),p==c){for(k=d,n=x;o=g>=n?y:n>=g+z?z:n-g,!(o>k);n+=x)t=k-o,s=x-o,u.push(L(l(o+t%s,0))),k=K(t/s);u.push(L(l(k,0))),g=m(d,r,e==f),d=0,++e}++d,++c}return u.join("")}function p(a){return h(a,function(a){return F.test(a)?n(a.slice(4).toLowerCase()):a})}function q(a){return h(a,function(a){return G.test(a)?"xn--"+o(a):a})}var r="object"==typeof d&&d&&!d.nodeType&&d,s="object"==typeof c&&c&&!c.nodeType&&c,t="object"==typeof e&&e;(t.global===t||t.window===t||t.self===t)&&(a=t);var u,v,w=2147483647,x=36,y=1,z=26,A=38,B=700,C=72,D=128,E="-",F=/^xn--/,G=/[^\x20-\x7E]/,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=x-y,K=Math.floor,L=String.fromCharCode;if(u={version:"1.3.1",ucs2:{decode:i,encode:j},decode:n,encode:o,toASCII:q,toUnicode:p},"function"==typeof f&&"object"==typeof f.amd&&f.amd)f("punycode",function(){return u});else if(r&&s)if(c.exports==r)s.exports=u;else for(v in u)u.hasOwnProperty(v)&&(r[v]=u[v]);else a.punycode=u}(this);var Qb="data-html2canvas-node",Rb="data-html2canvas-canvas-clone",Sb=0;a.html2canvas=function(c,d){if(d=d||{},d.logging&&(a.html2canvas.logging=!0,a.html2canvas.start=Date.now()),d.async="undefined"==typeof d.async?!0:d.async,d.allowTaint="undefined"==typeof d.allowTaint?!1:d.allowTaint,d.removeContainer="undefined"==typeof d.removeContainer?!0:d.removeContainer,d.javascriptEnabled="undefined"==typeof d.javascriptEnabled?!1:d.javascriptEnabled,d.imageTimeout="undefined"==typeof d.imageTimeout?1e4:d.imageTimeout,"string"==typeof c)return"string"!=typeof d.proxy?Promise.reject("Proxy must be used when rendering url"):p(v(c),d.proxy,b,a.innerWidth,a.innerHeight,d).then(function(b){return i(b.contentWindow.document.documentElement,b,d,a.innerWidth,a.innerHeight)});var e=(c===g?[b.documentElement]:c.length?c:[c])[0];return e.setAttribute(Qb,"true"),h(e.ownerDocument,d,e.ownerDocument.defaultView.innerWidth,e.ownerDocument.defaultView.innerHeight).then(function(a){return"function"==typeof d.onrendered&&(E("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"),d.onrendered(a)),a})},a.html2canvas.punycode=this.punycode,a.html2canvas.proxy={},y.prototype.getMetrics=function(a,b){return this.data[a+"-"+b]===g&&(this.data[a+"-"+b]=new x(a,b)),this.data[a+"-"+b]},z.prototype.proxyLoad=function(a,b,c){var d=this.src;return p(d.src,a,d.ownerDocument,b.width,b.height,c)},A.prototype.TYPES={LINEAR:1,RADIAL:2},C.prototype.findImages=function(a){var b=[];return a.reduce(function(a,b){switch(b.node.nodeName){case"IMG":return a.concat([{args:[b.node.src],method:"url"}]);case"svg":case"IFRAME":return a.concat([{args:[b.node],method:b.node.nodeName}])}return a},[]).forEach(this.addImage(b,this.loadImage),this),b},C.prototype.findBackgroundImage=function(a,b){return b.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(a,this.loadImage),this),a},C.prototype.addImage=function(a,b){return function(c){c.args.forEach(function(d){this.imageExists(a,d)||(a.splice(0,0,b.call(this,c)),E("Added image #"+a.length,"string"==typeof d?d.substring(0,100):d))},this)}},C.prototype.hasImageBackground=function(a){return"none"!==a.method},C.prototype.loadImage=function(a){if("url"===a.method){var b=a.args[0];return!this.isSVG(b)||this.support.svg||this.options.allowTaint?b.match(/data:image\/.*;base64,/i)?new B(b.replace(/url\(['"]{0,}|['"]{0,}\)$/gi,""),!1):this.isSameOrigin(b)||this.options.allowTaint===!0||this.isSVG(b)?new B(b,!1):this.support.cors&&!this.options.allowTaint&&this.options.useCORS?new B(b,!0):this.options.proxy?new Cb(b,this.options.proxy):new w(b):new Hb(b)}return"linear-gradient"===a.method?new D(a):"gradient"===a.method?new Mb(a):"svg"===a.method?new Jb(a.args[0],this.support.svg):"IFRAME"===a.method?new z(a.args[0],this.isSameOrigin(a.args[0].src),this.options):new w(a)},C.prototype.isSVG=function(a){return"svg"===a.substring(a.length-3).toLowerCase()||Hb.prototype.isInline(a)},C.prototype.imageExists=function(a,b){return a.some(function(a){return a.src===b})},C.prototype.isSameOrigin=function(a){return this.getOrigin(a)===this.origin},C.prototype.getOrigin=function(a){var c=this.link||(this.link=b.createElement("a"));return c.href=a,c.href=c.href,c.protocol+c.hostname+c.port},C.prototype.getPromise=function(a){return this.timeout(a,this.options.imageTimeout)["catch"](function(){var b=new w(a.src);return b.promise.then(function(b){a.image=b})})},C.prototype.get=function(a){var b=null;return this.images.some(function(c){return(b=c).src===a})?b:null},C.prototype.fetch=function(a){return this.images=a.reduce(ob(this.findBackgroundImage,this),this.findImages(a)),this.images.forEach(function(a,b){a.promise.then(function(){E("Succesfully loaded image #"+(b+1),a)},function(c){E("Failed loading image #"+(b+1),a,c)})}),this.ready=Promise.all(this.images.map(this.getPromise,this)),E("Finished searching images"),this},C.prototype.timeout=function(a,b){var c;return Promise.race([a.promise,new Promise(function(d,e){c=setTimeout(function(){E("Timed out loading image",a),e(a)},b)})]).then(function(a){return clearTimeout(c),a})},D.prototype=Object.create(A.prototype),D.prototype.stepRegExp=/((?:rgb|rgba)\(\d{1,3},\s\d{1,3},\s\d{1,3}(?:,\s[0-9\.]+)?\))\s*(\d{1,3})?(%|px)?/,F.prototype.cloneTo=function(a){a.visible=this.visible,a.borders=this.borders,a.bounds=this.bounds,a.clip=this.clip,a.backgroundClip=this.backgroundClip,a.computedStyles=this.computedStyles,a.styles=this.styles,a.backgroundImages=this.backgroundImages,a.opacity=this.opacity},F.prototype.getOpacity=function(){return null===this.opacity?this.opacity=this.cssFloat("opacity"):this.opacity},F.prototype.assignStack=function(a){this.stack=a,a.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(a){return this.computedStyles||(this.computedStyles=this.isPseudoElement?this.parent.computedStyle(this.before?":before":":after"):this.computedStyle(null)),this.styles[a]||(this.styles[a]=this.computedStyles[a])},F.prototype.prefixedCss=function(a){var b=["webkit","moz","ms","o"],c=this.css(a);
+return c===g&&b.some(function(b){return c=this.css(b+a.substr(0,1).toUpperCase()+a.substr(1)),c!==g},this),c===g?null:c},F.prototype.computedStyle=function(a){return this.node.ownerDocument.defaultView.getComputedStyle(this.node,a)},F.prototype.cssInt=function(a){var b=parseInt(this.css(a),10);return isNaN(b)?0:b},F.prototype.cssFloat=function(a){var b=parseFloat(this.css(a));return isNaN(b)?0:b},F.prototype.fontWeight=function(){var a=this.css("fontWeight");switch(parseInt(a,10)){case 401:a="bold";break;case 400:a="normal"}return a},F.prototype.parseClip=function(){var a=this.css("clip").match(this.CLIP);return a?{top:parseInt(a[1],10),right:parseInt(a[2],10),bottom:parseInt(a[3],10),left:parseInt(a[4],10)}:null},F.prototype.parseBackgroundImages=function(){return this.backgroundImages||(this.backgroundImages=J(this.css("backgroundImage")))},F.prototype.cssList=function(a,b){var c=(this.css(a)||"").split(",");return c=c[b||0]||c[0]||"auto",c=c.trim().split(" "),1===c.length&&(c=[c[0],c[0]]),c},F.prototype.parseBackgroundSize=function(a,b,c){var d,e,f=this.cssList("backgroundSize",c);if(I(f[0]))d=a.width*parseFloat(f[0])/100;else{if(/contain|cover/.test(f[0])){var g=a.width/a.height,h=b.width/b.height;return h>g^"contain"===f[0]?{width:a.height*h,height:a.height}:{width:a.width,height:a.width/h}}d=parseInt(f[0],10)}return e="auto"===f[0]&&"auto"===f[1]?b.height:"auto"===f[1]?d/b.width*b.height:I(f[1])?a.height*parseFloat(f[1])/100:parseInt(f[1],10),"auto"===f[0]&&(d=e/b.height*b.width),{width:d,height:e}},F.prototype.parseBackgroundPosition=function(a,b,c,d){var e,f,g=this.cssList("backgroundPosition",c);return e=I(g[0])?(a.width-(d||b).width)*(parseFloat(g[0])/100):parseInt(g[0],10),f="auto"===g[1]?e/b.width*b.height:I(g[1])?(a.height-(d||b).height)*parseFloat(g[1])/100:parseInt(g[1],10),"auto"===g[0]&&(e=f/b.height*b.width),{left:e,top:f}},F.prototype.parseBackgroundRepeat=function(a){return this.cssList("backgroundRepeat",a)[0]},F.prototype.parseTextShadows=function(){var a=this.css("textShadow"),b=[];if(a&&"none"!==a)for(var c=a.match(this.TEXT_SHADOW_PROPERTY),d=0;c&&d<c.length;d++){var e=c[d].match(this.TEXT_SHADOW_VALUES);b.push({color:e[0],offsetX:e[1]?parseFloat(e[1].replace("px","")):0,offsetY:e[2]?parseFloat(e[2].replace("px","")):0,blur:e[3]?e[3].replace("px",""):0})}return b},F.prototype.parseTransform=function(){if(!this.transformData)if(this.hasTransform()){var a=this.parseBounds(),b=this.prefixedCss("transformOrigin").split(" ").map(K).map(L);b[0]+=a.left,b[1]+=a.top,this.transformData={origin:b,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 a=this.prefixedCss("transform"),b=a?H(a.match(this.MATRIX_PROPERTY)):null;this.transformMatrix=b?b:[1,0,0,1,0,0]}return this.transformMatrix},F.prototype.parseBounds=function(){return this.bounds||(this.bounds=this.hasTransform()?N(this.node):M(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 a=this.node.value||"";return a="SELECT"===this.node.tagName?G(this.node):a,0===a.length?this.node.placeholder||"":a},F.prototype.MATRIX_PROPERTY=/(matrix)\((.+)\)/,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\)$/,O.prototype.calculateOverflowClips=function(){this.nodes.forEach(function(a){if(jb(a)){kb(a)&&a.appendToDOM(),a.borders=this.parseBorders(a);var b="hidden"===a.css("overflow")?[a.borders.clip]:[],c=a.parseClip();c&&-1!==["absolute","fixed"].indexOf(a.css("position"))&&b.push([["rect",a.bounds.left+c.left,a.bounds.top+c.top,c.right-c.left,c.bottom-c.top]]),a.clip=P(a)?a.parent.clip.concat(b):b,a.backgroundClip="hidden"!==a.css("overflow")?a.clip.concat([a.borders.clip]):a.clip,kb(a)&&a.cleanDOM()}else lb(a)&&(a.clip=P(a)?a.parent.clip:[]);kb(a)||(a.bounds=null)},this)},O.prototype.asyncRenderer=function(a,b,c){c=c||Date.now(),this.paint(a[this.renderIndex++]),a.length===this.renderIndex?b():c+20>Date.now()?this.asyncRenderer(a,b,c):setTimeout(ob(function(){this.asyncRenderer(a,b)},this),0)},O.prototype.createPseudoHideStyles=function(a){this.createStyles(a,"."+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+':before { content: "" !important; display: none !important; }.'+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER+':after { content: "" !important; display: none !important; }')},O.prototype.disableAnimations=function(a){this.createStyles(a,"* { -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;}")},O.prototype.createStyles=function(a,b){var c=a.createElement("style");c.innerHTML=b,a.body.appendChild(c)},O.prototype.getPseudoElements=function(a){var b=[[a]];if(a.node.nodeType===Node.ELEMENT_NODE){var c=this.getPseudoElement(a,":before"),d=this.getPseudoElement(a,":after");c&&b.push(c),d&&b.push(d)}return sb(b)},O.prototype.getPseudoElement=function(a,c){var d=a.computedStyle(c);if(!d||!d.content||"none"===d.content||"-moz-alt-content"===d.content||"none"===d.display)return null;for(var e=tb(d.content),f="url"===e.substr(0,3),g=b.createElement(f?"img":"html2canvaspseudoelement"),h=new Db(g,a,c),i=d.length-1;i>=0;i--){var j=Q(d.item(i));g.style[j]=d[j]}if(g.className=Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE+" "+Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER,f)return g.src=J(e)[0].args[0],[h];var k=b.createTextNode(e);return g.appendChild(k),[h,new Kb(k,h)]},O.prototype.getChildren=function(a){return sb([].filter.call(a.node.childNodes,db).map(function(b){var c=[b.nodeType===Node.TEXT_NODE?new Kb(b,a):new F(b,a)].filter(rb);return b.nodeType===Node.ELEMENT_NODE&&c.length&&"TEXTAREA"!==b.tagName?c[0].isElementVisible()?c.concat(this.getChildren(c[0])):[]:c},this))},O.prototype.newStackingContext=function(a,b){var c=new Fb(b,a.getOpacity(),a.node,a.parent);a.cloneTo(c);var d=b?c.getParentStack(this):c.parent.stack;d.contexts.push(c),a.stack=c},O.prototype.createStackingContexts=function(){this.nodes.forEach(function(a){jb(a)&&(this.isRootElement(a)||nb(a)||eb(a)||this.isBodyWithTransparentRoot(a)||a.hasTransform())?this.newStackingContext(a,!0):jb(a)&&(fb(a)&&Z(a)||hb(a)||gb(a))?this.newStackingContext(a,!1):a.assignStack(a.parent.stack)},this)},O.prototype.isBodyWithTransparentRoot=function(a){return"BODY"===a.node.nodeName&&this.renderer.isTransparent(a.parent.css("backgroundColor"))},O.prototype.isRootElement=function(a){return null===a.parent},O.prototype.sortStackingContexts=function(a){a.contexts.sort(mb(a.contexts.slice(0))),a.contexts.forEach(this.sortStackingContexts,this)},O.prototype.parseTextBounds=function(a){return function(b,c,d){if("none"!==a.parent.css("textDecoration").substr(0,4)||0!==b.trim().length){if(this.support.rangeBounds&&!a.parent.hasTransform()){var e=d.slice(0,c).join("").length;return this.getRangeBounds(a.node,e,b.length)}if(a.node&&"string"==typeof a.node.data){var f=a.node.splitText(b.length),g=this.getWrapperBounds(a.node,a.parent.hasTransform());return a.node=f,g}}else(!this.support.rangeBounds||a.parent.hasTransform())&&(a.node=a.node.splitText(b.length));return{}}},O.prototype.getWrapperBounds=function(a,b){var c=a.ownerDocument.createElement("html2canvaswrapper"),d=a.parentNode,e=a.cloneNode(!0);c.appendChild(a.cloneNode(!0)),d.replaceChild(c,a);var f=b?N(c):M(c);return d.replaceChild(e,c),f},O.prototype.getRangeBounds=function(a,b,c){var d=this.range||(this.range=a.ownerDocument.createRange());return d.setStart(a,b),d.setEnd(a,b+c),d.getBoundingClientRect()},O.prototype.parse=function(a){var b=a.contexts.filter(X),c=a.children.filter(jb),d=c.filter(ib(gb)),e=d.filter(ib(fb)).filter(ib($)),f=c.filter(ib(fb)).filter(gb),g=d.filter(ib(fb)).filter($),h=a.contexts.concat(d.filter(fb)).filter(Z),i=a.children.filter(lb).filter(ab),j=a.contexts.filter(Y);b.concat(e).concat(f).concat(g).concat(h).concat(i).concat(j).forEach(function(a){this.renderQueue.push(a),_(a)&&(this.parse(a),this.renderQueue.push(new R))},this)},O.prototype.paint=function(a){try{a instanceof R?this.renderer.ctx.restore():lb(a)?(kb(a.parent)&&a.parent.appendToDOM(),this.paintText(a),kb(a.parent)&&a.parent.cleanDOM()):this.paintNode(a)}catch(b){E(b)}},O.prototype.paintNode=function(a){_(a)&&(this.renderer.setOpacity(a.opacity),this.renderer.ctx.save(),a.hasTransform()&&this.renderer.setTransform(a.parseTransform()));var b=a.parseBounds();this.renderer.clip(a.backgroundClip,function(){this.renderer.renderBackground(a,b,a.borders.borders.map(qb))},this),this.renderer.clip(a.clip,function(){this.renderer.renderBorders(a.borders.borders)},this),this.renderer.clip(a.backgroundClip,function(){switch(a.node.nodeName){case"svg":case"IFRAME":var c=this.images.get(a.node);c?this.renderer.renderImage(a,b,a.borders,c):E("Error loading <"+a.node.nodeName+">",a.node);break;case"IMG":var d=this.images.get(a.node.src);d?this.renderer.renderImage(a,b,a.borders,d):E("Error loading <img>",a.node.src);break;case"CANVAS":this.renderer.renderImage(a,b,a.borders,{image:a.node});break;case"SELECT":case"INPUT":case"TEXTAREA":this.paintFormValue(a)}},this)},O.prototype.paintFormValue=function(a){if(a.getValue().length>0){var b=a.node.ownerDocument,c=b.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(b){try{c.style[b]=a.css(b)}catch(d){E("html2canvas: Parse: Exception caught in renderFormValue: "+d.message)}});var e=a.parseBounds();c.style.position="fixed",c.style.left=e.left+"px",c.style.top=e.top+"px",c.textContent=a.getValue(),b.body.appendChild(c),this.paintText(new Kb(c.firstChild,a)),b.body.removeChild(c)}},O.prototype.paintText=function(b){b.applyTextTransform();var c=a.html2canvas.punycode.ucs2.decode(b.node.data),d=this.options.letterRendering&&!bb(b)||wb(b.node.data)?c.map(function(b){return a.html2canvas.punycode.ucs2.encode([b])}):ub(c),e=b.parent.fontWeight(),f=b.parent.css("fontSize"),g=b.parent.css("fontFamily"),h=b.parent.parseTextShadows();this.renderer.font(b.parent.css("color"),b.parent.css("fontStyle"),b.parent.css("fontVariant"),e,f,g),h.length?this.renderer.fontShadow(h[0].color,h[0].offsetX,h[0].offsetY,h[0].blur):this.renderer.clearShadow(),this.renderer.clip(b.parent.clip,function(){d.map(this.parseTextBounds(b),this).forEach(function(a,c){a&&(this.renderer.text(d[c],a.left,a.bottom),this.renderTextDecoration(b.parent,a,this.fontMetrics.getMetrics(g,f)))},this)},this)},O.prototype.renderTextDecoration=function(a,b,c){switch(a.css("textDecoration").split(" ")[0]){case"underline":this.renderer.rectangle(b.left,Math.round(b.top+c.baseline+c.lineWidth),b.width,1,a.css("color"));break;case"overline":this.renderer.rectangle(b.left,Math.round(b.top),b.width,1,a.css("color"));break;case"line-through":this.renderer.rectangle(b.left,Math.ceil(b.top+c.middle+c.lineWidth),b.width,1,a.css("color"))}},O.prototype.parseBorders=function(a){var b=a.parseBounds(),c=cb(a),d=["Top","Right","Bottom","Left"].map(function(b){return{width:a.cssInt("border"+b+"Width"),color:a.css("border"+b+"Color"),args:null}}),e=T(b,c,d);return{clip:this.parseBackgroundClip(a,e,d,c,b),borders:d.map(function(a,f){if(a.width>0){var g=b.left,h=b.top,i=b.width,j=b.height-d[2].width;switch(f){case 0:j=d[0].width,a.args=V({c1:[g,h],c2:[g+i,h],c3:[g+i-d[1].width,h+j],c4:[g+d[3].width,h+j]},c[0],c[1],e.topLeftOuter,e.topLeftInner,e.topRightOuter,e.topRightInner);break;case 1:g=b.left+b.width-d[1].width,i=d[1].width,a.args=V({c1:[g+i,h],c2:[g+i,h+j+d[2].width],c3:[g,h+j],c4:[g,h+d[0].width]},c[1],c[2],e.topRightOuter,e.topRightInner,e.bottomRightOuter,e.bottomRightInner);break;case 2:h=h+b.height-d[2].width,j=d[2].width,a.args=V({c1:[g+i,h+j],c2:[g,h+j],c3:[g+d[3].width,h],c4:[g+i-d[3].width,h]},c[2],c[3],e.bottomRightOuter,e.bottomRightInner,e.bottomLeftOuter,e.bottomLeftInner);break;case 3:i=d[3].width,a.args=V({c1:[g,h+j+d[2].width],c2:[g,h],c3:[g+i,h+d[0].width],c4:[g+i,h+j]},c[3],c[0],e.bottomLeftOuter,e.bottomLeftInner,e.topLeftOuter,e.topLeftInner)}}return a})}},O.prototype.parseBackgroundClip=function(a,b,c,d,e){var f=a.css("backgroundClip"),g=[];switch(f){case"content-box":case"padding-box":W(g,d[0],d[1],b.topLeftInner,b.topRightInner,e.left+c[3].width,e.top+c[0].width),W(g,d[1],d[2],b.topRightInner,b.bottomRightInner,e.left+e.width-c[1].width,e.top+c[0].width),W(g,d[2],d[3],b.bottomRightInner,b.bottomLeftInner,e.left+e.width-c[1].width,e.top+e.height-c[2].width),W(g,d[3],d[0],b.bottomLeftInner,b.topLeftInner,e.left+c[3].width,e.top+e.height-c[2].width);break;default:W(g,d[0],d[1],b.topLeftOuter,b.topRightOuter,e.left,e.top),W(g,d[1],d[2],b.topRightOuter,b.bottomRightOuter,e.left+e.width,e.top),W(g,d[2],d[3],b.bottomRightOuter,b.bottomLeftOuter,e.left+e.width,e.top+e.height),W(g,d[3],d[0],b.bottomLeftOuter,b.topLeftOuter,e.left,e.top+e.height)}return g};var Tb=0,Ub="withCredentials"in new XMLHttpRequest,Vb="crossOrigin"in new Image;Db.prototype.cloneTo=function(a){Db.prototype.cloneTo.call(this,a),a.isPseudoElement=!0,a.before=this.before},Db.prototype=Object.create(F.prototype),Db.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()},Db.prototype.cleanDOM=function(){this.node.parentNode.removeChild(this.node),this.parent.node.className=this.parent.node.className.replace(this.getHideClass(),"")},Db.prototype.getHideClass=function(){return this["PSEUDO_HIDE_ELEMENT_CLASS_"+(this.before?"BEFORE":"AFTER")]},Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE="___html2canvas___pseudoelement_before",Db.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER="___html2canvas___pseudoelement_after",Eb.prototype.renderImage=function(a,b,c,d){var e=a.cssInt("paddingLeft"),f=a.cssInt("paddingTop"),g=a.cssInt("paddingRight"),h=a.cssInt("paddingBottom"),i=c.borders,j=b.width-(i[1].width+i[3].width+e+g),k=b.height-(i[0].width+i[2].width+f+h);this.drawImage(d,0,0,d.image.width||j,d.image.height||k,b.left+e+i[3].width,b.top+f+i[0].width,j,k)},Eb.prototype.renderBackground=function(a,b,c){b.height>0&&b.width>0&&(this.renderBackgroundColor(a,b),this.renderBackgroundImage(a,b,c))},Eb.prototype.renderBackgroundColor=function(a,b){var c=a.css("backgroundColor");this.isTransparent(c)||this.rectangle(b.left,b.top,b.width,b.height,a.css("backgroundColor"))},Eb.prototype.renderBorders=function(a){a.forEach(this.renderBorder,this)},Eb.prototype.renderBorder=function(a){this.isTransparent(a.color)||null===a.args||this.drawShape(a.args,a.color)},Eb.prototype.renderBackgroundImage=function(a,b,c){var d=a.parseBackgroundImages();d.reverse().forEach(function(d,e,f){switch(d.method){case"url":var g=this.images.get(d.args[0]);g?this.renderBackgroundRepeating(a,b,g,f.length-(e+1),c):E("Error loading background-image",d.args[0]);break;case"linear-gradient":case"gradient":var h=this.images.get(d.value);h?this.renderBackgroundGradient(h,b,c):E("Error loading background-image",d.args[0]);break;case"none":break;default:E("Unknown background-image type",d.args[0])}},this)},Eb.prototype.renderBackgroundRepeating=function(a,b,c,d,e){var f=a.parseBackgroundSize(b,c.image,d),g=a.parseBackgroundPosition(b,c.image,d,f),h=a.parseBackgroundRepeat(d);switch(h){case"repeat-x":case"repeat no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+e[3],b.top+g.top+e[0],99999,f.height,e);break;case"repeat-y":case"no-repeat repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+e[0],f.width,99999,e);break;case"no-repeat":this.backgroundRepeatShape(c,g,f,b,b.left+g.left+e[3],b.top+g.top+e[0],f.width,f.height,e);break;default:this.renderBackgroundRepeat(c,g,f,{top:b.top,left:b.left},e[3],e[0])}},Eb.prototype.isTransparent=function(a){return!a||"transparent"===a||"rgba(0, 0, 0, 0)"===a},Fb.prototype=Object.create(F.prototype),Fb.prototype.getParentStack=function(a){var b=this.parent?this.parent.stack:null;return b?b.ownStacking?b:b.getParentStack(a):a.stack},Gb.prototype.testRangeBounds=function(a){var b,c,d,e,f=!1;return a.createRange&&(b=a.createRange(),b.getBoundingClientRect&&(c=a.createElement("boundtest"),c.style.height="123px",c.style.display="block",a.body.appendChild(c),b.selectNode(c),d=b.getBoundingClientRect(),e=d.height,123===e&&(f=!0),a.body.removeChild(c))),f},Gb.prototype.testCORS=function(){return"undefined"!=typeof(new Image).crossOrigin},Gb.prototype.testSVG=function(){var a=new Image,c=b.createElement("canvas"),d=c.getContext("2d");a.src="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";try{d.drawImage(a,0,0),c.toDataURL()}catch(e){return!1}return!0},Hb.prototype.hasFabric=function(){return html2canvas.fabric?Promise.resolve():Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg"))},Hb.prototype.inlineFormatting=function(a){return/^data:image\/svg\+xml;base64,/.test(a)?this.decode64(this.removeContentType(a)):this.removeContentType(a)},Hb.prototype.removeContentType=function(a){return a.replace(/^data:image\/svg\+xml(;base64)?,/,"")},Hb.prototype.isInline=function(a){return/^data:image\/svg\+xml/i.test(a)},Hb.prototype.createCanvas=function(a){var b=this;return function(c,d){var e=new html2canvas.fabric.StaticCanvas("c");b.image=e.lowerCanvasEl,e.setWidth(d.width).setHeight(d.height).add(html2canvas.fabric.util.groupSVGElements(c,d)).renderAll(),a(e.lowerCanvasEl)}},Hb.prototype.decode64=function(b){return"function"==typeof a.atob?a.atob(b):Ib(b)},Jb.prototype=Object.create(Hb.prototype),Kb.prototype=Object.create(F.prototype),Kb.prototype.applyTextTransform=function(){this.node.data=this.transform(this.parent.css("textTransform"))},Kb.prototype.transform=function(a){var b=this.node.data;switch(a){case"lowercase":return b.toLowerCase();case"capitalize":return b.replace(/(^|\s|:|-|\(|\))([a-z])/g,Lb);case"uppercase":return b.toUpperCase();default:return b}},Mb.prototype=Object.create(A.prototype),Ob.prototype=Object.create(Eb.prototype),Ob.prototype.setFillStyle=function(a){return this.ctx.fillStyle=a,this.ctx},Ob.prototype.rectangle=function(a,b,c,d,e){this.setFillStyle(e).fillRect(a,b,c,d)},Ob.prototype.drawShape=function(a,b){this.shape(a),this.setFillStyle(b).fill()},Ob.prototype.taints=function(a){if(null===a.tainted){this.taintCtx.drawImage(a.image,0,0);try{this.taintCtx.getImageData(0,0,1,1),a.tainted=!1}catch(c){this.taintCtx=b.createElement("canvas").getContext("2d"),a.tainted=!0}}return a.tainted},Ob.prototype.drawImage=function(a,b,c,d,e,f,g,h,i){(!this.taints(a)||this.options.allowTaint)&&this.ctx.drawImage(a.image,b,c,d,e,f,g,h,i)},Ob.prototype.clip=function(a,b,c){this.ctx.save(),a.filter(Pb).forEach(function(a){this.shape(a).clip()},this),b.call(c),this.ctx.restore()},Ob.prototype.shape=function(a){return this.ctx.beginPath(),a.forEach(function(a,b){"rect"===a[0]?this.ctx.rect.apply(this.ctx,a.slice(1)):this.ctx[0===b?"moveTo":a[0]+"To"].apply(this.ctx,a.slice(1))},this),this.ctx.closePath(),this.ctx},Ob.prototype.font=function(a,b,c,d,e,f){this.setFillStyle(a).font=[b,c,d,e,f].join(" ").split(",")[0]},Ob.prototype.fontShadow=function(a,b,c,d){this.setVariable("shadowColor",a).setVariable("shadowOffsetY",b).setVariable("shadowOffsetX",c).setVariable("shadowBlur",d)},Ob.prototype.clearShadow=function(){this.setVariable("shadowColor","rgba(0,0,0,0)")},Ob.prototype.setOpacity=function(a){this.ctx.globalAlpha=a},Ob.prototype.setTransform=function(a){this.ctx.translate(a.origin[0],a.origin[1]),this.ctx.transform.apply(this.ctx,a.matrix),this.ctx.translate(-a.origin[0],-a.origin[1])},Ob.prototype.setVariable=function(a,b){return this.variables[a]!==b&&(this.variables[a]=this.ctx[a]=b),this},Ob.prototype.text=function(a,b,c){this.ctx.fillText(a,b,c)},Ob.prototype.backgroundRepeatShape=function(a,b,c,d,e,f,g,h,i){var j=[["line",Math.round(e),Math.round(f)],["line",Math.round(e+g),Math.round(f)],["line",Math.round(e+g),Math.round(h+f)],["line",Math.round(e),Math.round(h+f)]];this.clip([j],function(){this.renderBackgroundRepeat(a,b,c,d,i[3],i[0])},this)},Ob.prototype.renderBackgroundRepeat=function(a,b,c,d,e,f){var g=Math.round(d.left+b.left+e),h=Math.round(d.top+b.top+f);this.setFillStyle(this.ctx.createPattern(this.resizeImage(a,c),"repeat")),this.ctx.translate(g,h),this.ctx.fill(),this.ctx.translate(-g,-h)},Ob.prototype.renderBackgroundGradient=function(a,b){if(a instanceof D){var c=this.ctx.createLinearGradient(b.left+b.width*a.x0,b.top+b.height*a.y0,b.left+b.width*a.x1,b.top+b.height*a.y1);a.colorStops.forEach(function(a){c.addColorStop(a.stop,a.color)}),this.rectangle(b.left,b.top,b.width,b.height,c)}},Ob.prototype.resizeImage=function(a,c){var d=a.image;if(d.width===c.width&&d.height===c.height)return d;var e,f=b.createElement("canvas");return f.width=c.width,f.height=c.height,e=f.getContext("2d"),e.drawImage(d,0,0,d.width,d.height,0,0,c.width,c.height),f}}).call({},window,document);
\ No newline at end of file
diff --git a/dist/html2canvas.svg.js b/dist/html2canvas.svg.js
index 75ce2e0..f446066 100644
--- a/dist/html2canvas.svg.js
+++ b/dist/html2canvas.svg.js
@@ -7,5 +7,17696 @@
 
 (function(window, document, exports, undefined){
 
+/* 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({}, window, document, html2canvas);
\ No newline at end of file
diff --git a/dist/html2canvas.svg.min.js b/dist/html2canvas.svg.min.js
index 07c61db..38bd965 100644
--- a/dist/html2canvas.svg.min.js
+++ b/dist/html2canvas.svg.min.js
@@ -4,4 +4,9 @@
 
   Released under MIT License
 */
-(function(){}).call({},window,document,html2canvas);
\ No newline at end of file
+(function(window,document,exports,undefined){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 a(a){var b=this.face=a.face;this.glyphs=a.glyphs,this.w=a.w,this.baseSize=parseInt(b["units-per-em"],10),this.family=b["font-family"].toLowerCase(),this.weight=b["font-weight"],this.style=b["font-style"]||"normal",this.viewBox=function(){var a=b.bbox.split(/\s+/),c={minX:parseInt(a[0],10),minY:parseInt(a[1],10),maxX:parseInt(a[2],10),maxY:parseInt(a[3],10)};return c.width=c.maxX-c.minX,c.height=c.maxY-c.minY,c.toString=function(){return[this.minX,this.minY,this.width,this.height].join(" ")},c}(),this.ascent=-parseInt(b.ascent,10),this.descent=-parseInt(b.descent,10),this.height=-this.ascent+this.descent}function b(){var a={},b={oblique:"italic",italic:"oblique"};this.add=function(b){(a[b.style]||(a[b.style]={}))[b.weight]=b},this.get=function(c,d){var e=a[c]||a[b[c]]||a.normal||a.italic||a.oblique;if(!e)return null;if(d={normal:400,bold:700}[d]||parseInt(d,10),e[d])return e[d];var f,g,h={1:1,99:0}[d%100],i=[];h===undefined&&(h=d>400),500==d&&(d=400);for(var j in e)j=parseInt(j,10),(!f||f>j)&&(f=j),(!g||j>g)&&(g=j),i.push(j);return f>d&&(d=f),d>g&&(d=g),i.sort(function(a,b){return(h?a>d&&b>d?b>a:a>b:d>a&&d>b?a>b:b>a)?-1:1}),e[i[0]]}}function c(){function a(a,b){return a.contains?a.contains(b):16&a.compareDocumentPosition(b)}function b(b){var c=b.relatedTarget;c&&!a(this,c)&&d(this)}function c(){d(this)}function d(a){setTimeout(function(){n.replace(a,r.get(a).options,!0)},10)}this.attach=function(a){a.onmouseenter===undefined?(f(a,"mouseover",b),f(a,"mouseout",b)):(f(a,"mouseenter",c),f(a,"mouseleave",c))}}function d(){function a(a){return a.cufid||(a.cufid=++c)}var b={},c=0;this.get=function(c){var d=a(c);return b[d]||(b[d]={})}}function e(a){var b={},c={};this.get=function(c){return b[c]!=undefined?b[c]:a[c]},this.getSize=function(a,b){return c[a]||(c[a]=new p.Size(this.get(a),b))},this.extend=function(a){for(var c in a)b[c]=a[c];return this}}function f(a,b,c){a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent&&a.attachEvent("on"+b,function(){return c.call(a,fabric.window.event)})}function g(a,b){var c=r.get(a);return c.options?a:(b.hover&&b.hoverables[a.nodeName.toLowerCase()]&&s.attach(a),c.options=b,a)}function h(a){var b={};return function(c){return b.hasOwnProperty(c)||(b[c]=a.apply(null,arguments)),b[c]}}function i(a,b){b||(b=p.getStyle(a));for(var c,d=p.quotedList(b.get("fontFamily").toLowerCase()),e=0,f=d.length;f>e;++e)if(c=d[e],v[c])return v[c].get(b.get("fontStyle"),b.get("fontWeight"));return null}function j(a){return fabric.document.getElementsByTagName(a)}function k(){for(var a,b={},c=0,d=arguments.length;d>c;++c)for(a in arguments[c])b[a]=arguments[c][a];return b}function l(a,b,c,d,e,f){var g=d.separate;if("none"==g)return u[d.engine].apply(null,arguments);var h,i=fabric.document.createDocumentFragment(),j=b.split(x[g]),k="words"==g;k&&q&&(/^\s/.test(b)&&j.unshift(""),/\s$/.test(b)&&j.push(""));for(var l=0,m=j.length;m>l;++l)h=u[d.engine](a,k?p.textAlign(j[l],c,l,m):j[l],c,d,e,f,m-1>l),h&&i.appendChild(h);return i}function m(a,b){for(var c,d,e,f,h=g(a,b).firstChild;h;h=e){if(e=h.nextSibling,f=!1,1==h.nodeType){if(!h.firstChild)continue;if(!/cufon/.test(h.className)){arguments.callee(h,b);continue}f=!0}if(d||(d=p.getStyle(a).extend(b)),c||(c=i(a,d)),c)if(f)u[b.engine](c,null,d,b,h,a);else{var j=h.data;if("undefined"!=typeof G_vmlCanvasManager&&(j=j.replace(/\r/g,"\n")),""!==j){var k=l(c,j,d,b,h,a);k?h.parentNode.replaceChild(k,h):h.parentNode.removeChild(h)}}}}var n=function(){return n.replace.apply(null,arguments)},o=n.DOM={ready:function(){var a=!1,b={loaded:1,complete:1},c=[],d=function(){if(!a){a=!0;for(var b;b=c.shift();b());}};return fabric.document.addEventListener&&(fabric.document.addEventListener("DOMContentLoaded",d,!1),fabric.window.addEventListener("pageshow",d,!1)),!fabric.window.opera&&fabric.document.readyState&&function(){b[fabric.document.readyState]?d():setTimeout(arguments.callee,10)}(),fabric.document.readyState&&fabric.document.createStyleSheet&&function(){try{fabric.document.body.doScroll("left"),d()}catch(a){setTimeout(arguments.callee,1)}}(),f(fabric.window,"load",d),function(b){arguments.length?a?b():c.push(b):d()}}()},p=n.CSS={Size:function(a,b){this.value=parseFloat(a),this.unit=String(a).match(/[a-z%]*$/)[0]||"px",this.convert=function(a){return a/b*this.value},this.convertFrom=function(a){return a/this.value*b},this.toString=function(){return this.value+this.unit}},getStyle:function(a){return new e(a.style)},quotedList:h(function(a){for(var b,c=[],d=/\s*((["'])([\s\S]*?[^\\])\2|[^,]+)\s*/g;b=d.exec(a);)c.push(b[3]||b[1]);return c}),ready:function(){var a=!1,b=[],c=function(){a=!0;for(var c;c=b.shift();c());},d=Object.prototype.propertyIsEnumerable?j("style"):{length:0},e=j("link");return o.ready(function(){for(var a,b=0,f=0,g=e.length;a=e[f],g>f;++f)a.disabled||"stylesheet"!=a.rel.toLowerCase()||++b;fabric.document.styleSheets.length>=d.length+b?c():setTimeout(arguments.callee,10)}),function(c){a?c():b.push(c)}}(),supports:function(a,b){var c=fabric.document.createElement("span").style;return c[a]===undefined?!1:(c[a]=b,c[a]===b)},textAlign:function(a,b,c,d){return"right"==b.get("textAlign")?c>0&&(a=" "+a):d-1>c&&(a+=" "),a},textDecoration:function(a,b){b||(b=this.getStyle(a));for(var c={underline:null,overline:null,"line-through":null},d=a;d.parentNode&&1==d.parentNode.nodeType;){var e=!0;for(var f in c)c[f]||(-1!=b.get("textDecoration").indexOf(f)&&(c[f]=b.get("color")),e=!1);if(e)break;b=this.getStyle(d=d.parentNode)}return c},textShadow:h(function(a){if("none"==a)return null;for(var b,c=[],d={},e=0,f=/(#[a-f0-9]+|[a-z]+\(.*?\)|[a-z]+)|(-?[\d.]+[a-z%]*)|,/gi;b=f.exec(a);)","==b[0]?(c.push(d),d={},e=0):b[1]?d.color=b[1]:d[["offX","offY","blur"][e++]]=b[2];return c.push(d),c}),color:h(function(a){var b={};return b.color=a.replace(/^rgba\((.*?),\s*([\d.]+)\)/,function(a,c,d){return b.opacity=parseFloat(d),"rgb("+c+")"}),b}),textTransform:function(a,b){return a[{uppercase:"toUpperCase",lowercase:"toLowerCase"}[b.get("textTransform")]||"toString"]()}},q=0==" ".split(/\s+/).length,r=new d,s=new c,t=[],u={},v={},w={engine:null,hover:!1,hoverables:{a:!0},printable:!0,selector:fabric.window.Sizzle||fabric.window.jQuery&&function(a){return jQuery(a)}||fabric.window.dojo&&dojo.query||fabric.window.$$&&function(a){return $$(a)}||fabric.window.$&&function(a){return $(a)}||fabric.document.querySelectorAll&&function(a){return fabric.document.querySelectorAll(a)}||j,separate:"words",textShadow:"none"},x={words:/\s+/,characters:""};return n.now=function(){return o.ready(),n},n.refresh=function(){for(var a=t.splice(0,t.length),b=0,c=a.length;c>b;++b)n.replace.apply(null,a[b]);return n},n.registerEngine=function(a,b){return b?(u[a]=b,n.set("engine",a)):n},n.registerFont=function(c){var d=new a(c),e=d.family;return v[e]||(v[e]=new b),v[e].add(d),n.set("fontFamily",'"'+e+'"')},n.replace=function(a,b,c){return b=k(w,b),b.engine?("string"==typeof b.textShadow&&b.textShadow&&(b.textShadow=p.textShadow(b.textShadow)),c||t.push(arguments),(a.nodeType||"string"==typeof a)&&(a=[a]),p.ready(function(){for(var c=0,d=a.length;d>c;++c){var e=a[c];"string"==typeof e?n.replace(b.selector(e),b,!0):m(e,b)}}),n):n},n.replaceElement=function(a,b){return b=k(w,b),"string"==typeof b.textShadow&&b.textShadow&&(b.textShadow=p.textShadow(b.textShadow)),m(a,b)},n.engines=u,n.fonts=v,n.getOptions=function(){return k(w)},n.set=function(a,b){return w[a]=b,n},n}();Cufon.registerEngine("canvas",function(){function a(a,b){var c,d=0,e=0,f=[],g=/([mrvxe])([^a-z]*)/g;a:for(var h=0;c=g.exec(a);++h){var i=c[2].split(",");switch(c[1]){case"v":f[h]={m:"bezierCurveTo",a:[d+~~i[0],e+~~i[1],d+~~i[2],e+~~i[3],d+=~~i[4],e+=~~i[5]]};break;case"r":f[h]={m:"lineTo",a:[d+=~~i[0],e+=~~i[1]]};break;case"m":f[h]={m:"moveTo",a:[d=~~i[0],e=~~i[1]]};break;case"x":f[h]={m:"closePath",a:[]};break;case"e":break a}b[f[h].m].apply(b,f[h].a)}return f}function b(a,b){for(var c=0,d=a.length;d>c;++c){var e=a[c];b[e.m].apply(b,e.a)}}var c=Cufon.CSS.supports("display","inline-block"),d=!c&&("BackCompat"==fabric.document.compatMode||/frameset|transitional/i.test(fabric.document.doctype.publicId)),e=fabric.document.createElement("style");e.type="text/css";var f=fabric.document.createTextNode(".cufon-canvas{text-indent:0}@media screen,projection{.cufon-canvas{display:inline;display:inline-block;position:relative;vertical-align:middle"+(d?"":";font-size:1px;line-height:1px")+"}.cufon-canvas .cufon-alt{display:-moz-inline-box;display:inline-block;width:0;height:0;overflow:hidden}"+(c?".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{e.appendChild(f)}catch(g){e.setAttribute("type","text/css"),e.styleSheet.cssText=f.data}return fabric.document.getElementsByTagName("head")[0].appendChild(e),function(d,e,f,g,h){function i(){T.save();var a=0,b=0,c=[{left:0}];g.backgroundColor&&(T.save(),T.fillStyle=g.backgroundColor,T.translate(0,d.ascent),T.fillRect(0,0,A+10,(-d.ascent+d.descent)*D),T.restore()),"right"===g.textAlign?(T.translate(G[b],0),c[0].left=G[b]*U):"center"===g.textAlign&&(T.translate(G[b]/2,0),c[0].left=G[b]/2*U);for(var e=0,f=z.length;f>e;++e)if("\n"!==z[e]){var h=d.glyphs[z[e]]||d.missingGlyph;if(h){var i=Number(h.w||d.w)+n;g.textBackgroundColor&&(T.save(),T.fillStyle=g.textBackgroundColor,T.translate(0,d.ascent),T.fillRect(0,0,i+10,-d.ascent+d.descent),T.restore()),T.translate(i,0),a+=i,e==f-1&&(c[c.length-1].width=a*U,c[c.length-1].height=(-d.ascent+d.descent)*U)}}else{b++;var j=-d.ascent-d.ascent/5*g.lineHeight,k=c[c.length-1],l={left:0};k.width=a*U,k.height=(-d.ascent+d.descent)*U,"right"===g.textAlign?(T.translate(-A,j),T.translate(G[b],0),l.left=G[b]*U):"center"===g.textAlign?(T.translate(-a-G[b-1]/2,j),T.translate(G[b]/2,0),l.left=G[b]/2*U):T.translate(-a,j),c.push(l),a=0}T.restore(),Cufon.textOptions.boundaries=c}function j(c){T.fillStyle=c||Cufon.textOptions.color||f.get("color");var e=0,h=0;"right"===g.textAlign?T.translate(G[h],0):"center"===g.textAlign&&T.translate(G[h]/2,0);for(var i=0,j=z.length;j>i;++i)if("\n"!==z[i]){var k=d.glyphs[z[i]]||d.missingGlyph;if(k){var l=Number(k.w||d.w)+n;W&&(T.save(),T.strokeStyle=T.fillStyle,T.lineWidth+=T.lineWidth,T.beginPath(),W.underline&&(T.moveTo(0,-d.face["underline-position"]+.5),T.lineTo(l,-d.face["underline-position"]+.5)),W.overline&&(T.moveTo(0,d.ascent+.5),T.lineTo(l,d.ascent+.5)),W["line-through"]&&(T.moveTo(0,-d.descent+.5),T.lineTo(l,-d.descent+.5)),T.stroke(),T.restore()),X&&(T.save(),T.transform(1,0,-.25,1,0,0)),T.beginPath(),k.d&&(k.code?b(k.code,T):k.code=a("m"+k.d,T)),T.fill(),g.strokeStyle&&(T.closePath(),T.save(),T.lineWidth=g.strokeWidth,T.strokeStyle=g.strokeStyle,T.stroke(),T.restore()),X&&T.restore(),T.translate(l,0),e+=l}}else{h++;var m=-d.ascent-d.ascent/5*g.lineHeight;"right"===g.textAlign?(T.translate(-A,m),T.translate(G[h],0)):"center"===g.textAlign?(T.translate(-e-G[h-1]/2,m),T.translate(G[h]/2,0)):T.translate(-e,m),e=0}}var k=null===e,l=d.viewBox,m=f.getSize("fontSize",d.baseSize),n=f.get("letterSpacing");n="normal"==n?0:m.convertFrom(parseInt(n,10));var o=0,p=0,q=0,r=0,s=g.textShadow,t=[];if(Cufon.textOptions.shadowOffsets=[],Cufon.textOptions.shadows=null,s){Cufon.textOptions.shadows=s;for(var u=0,v=s.length;v>u;++u){var w=s[u],x=m.convertFrom(parseFloat(w.offX)),y=m.convertFrom(parseFloat(w.offY));t[u]=[x,y]}}for(var z=Cufon.CSS.textTransform(k?h.alt:e,f).split(""),A=0,B=null,C=0,D=1,E=[],u=0,v=z.length;v>u;++u)if("\n"!==z[u]){var F=d.glyphs[z[u]]||d.missingGlyph;F&&(A+=B=Number(F.w||d.w)+n)}else D++,A>C&&(C=A),E.push(A),A=0;E.push(A),A=Math.max(C,A);for(var G=[],u=E.length;u--;)G[u]=A-E[u];if(null===B)return null;p+=l.width-B,r+=l.minX;var H,I;if(k)H=h,I=h.firstChild;else if(H=fabric.document.createElement("span"),H.className="cufon cufon-canvas",H.alt=e,I=fabric.document.createElement("canvas"),H.appendChild(I),g.printable){var J=fabric.document.createElement("span");J.className="cufon-alt",J.appendChild(fabric.document.createTextNode(e)),H.appendChild(J)}var K=H.style,L=I.style||{},M=m.convert(l.height-o+q),N=Math.ceil(M),O=N/M;I.width=Math.ceil(m.convert(A+p-r)*O),I.height=N,o+=l.minY,L.top=Math.round(m.convert(o-d.ascent))+"px",L.left=Math.round(m.convert(r))+"px";var P=Math.ceil(m.convert(A*O)),Q=P+"px",R=m.convert(d.height),S=(g.lineHeight-1)*m.convert(-d.ascent/5)*(D-1);Cufon.textOptions.width=P,Cufon.textOptions.height=R*D+S,Cufon.textOptions.lines=D,Cufon.textOptions.totalLineHeight=S,c?(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/l.height;Cufon.textOptions.fontAscent=d.ascent*U,Cufon.textOptions.boundaries=null;for(var V=Cufon.textOptions.shadowOffsets,u=t.length;u--;)V[u]=[t[u][0]*U,t[u][1]*U];T.save(),T.scale(U,U),T.translate(-r-1/U*I.width/2+(Cufon.fonts[d.family].offsetLeft||0),-o-Cufon.textOptions.height/U/2+(Cufon.fonts[d.family].offsetTop||0)),T.lineWidth=d.face["underline-thickness"],T.save();var W=Cufon.getTextDecoration(g),X="italic"===g.fontStyle;if(T.save(),i(),s)for(var u=0,v=s.length;v>u;++u){var w=s[u];T.save(),T.translate.apply(T,t[u]),j(w.color),T.restore()}return j(),T.restore(),T.restore(),T.restore(),H}}()),Cufon.registerEngine("vml",function(){function a(a,c){return b(a,/(?:em|ex|%)$/i.test(c)?"1em":c)}function b(a,b){if(/px$/i.test(b))return parseFloat(b);var c=a.style.left,d=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left,a.style.left=b;var e=a.style.pixelLeft;return a.style.left=c,a.runtimeStyle.left=d,e}if(fabric.document.namespaces){var c=fabric.document.createElement("canvas");if(!(c&&c.getContext&&c.getContext.apply)){null==fabric.document.namespaces.cvml&&fabric.document.namespaces.add("cvml","urn:schemas-microsoft-com:vml");var d=fabric.document.createElement("cvml:shape");if(d.style.behavior="url(#default#VML)",d.coordsize)return d=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(c,d,e,f,g,h,i){var j=null===d;j&&(d=g.alt);var k=c.viewBox,l=e.computedFontSize||(e.computedFontSize=new Cufon.CSS.Size(a(h,e.get("fontSize"))+"px",c.baseSize)),m=e.computedLSpacing;m==undefined&&(m=e.get("letterSpacing"),e.computedLSpacing=m="normal"==m?0:~~l.convertFrom(b(h,m)));var n,o;if(j)n=g,o=g.firstChild;else{if(n=fabric.document.createElement("span"),n.className="cufon cufon-vml",n.alt=d,o=fabric.document.createElement("span"),o.className="cufon-vml-canvas",n.appendChild(o),f.printable){var p=fabric.document.createElement("span");p.className="cufon-alt",p.appendChild(fabric.document.createTextNode(d)),n.appendChild(p)}i||n.appendChild(fabric.document.createElement("cvml:shape"))}var q=n.style,r=o.style,s=l.convert(k.height),t=Math.ceil(s),u=t/s,v=k.minX,w=k.minY;r.height=t,r.top=Math.round(l.convert(w-c.ascent)),r.left=Math.round(l.convert(v)),q.height=l.convert(c.height)+"px";for(var x,y,z=(Cufon.getTextDecoration(f),e.get("color")),A=Cufon.CSS.textTransform(d,e).split(""),B=0,C=0,D=null,E=f.textShadow,F=0,G=0,H=A.length;H>F;++F)x=c.glyphs[A[F]]||c.missingGlyph,x&&(B+=D=~~(x.w||c.w)+m);if(null===D)return null;var I,J=-v+B+(k.width-D),K=l.convert(J*u),L=Math.round(K),M=J+","+k.height,N="r"+M+"nsnf";for(F=0;H>F;++F)if(x=c.glyphs[A[F]]||c.missingGlyph){j?(y=o.childNodes[G],y.firstChild&&y.removeChild(y.firstChild)):(y=fabric.document.createElement("cvml:shape"),o.appendChild(y)),y.stroked="f",y.coordsize=M,y.coordorigin=I=v-C+","+w,y.path=(x.d?"m"+x.d+"xe":"")+"m"+I+N,y.fillcolor=z;var O=y.style;if(O.width=L,O.height=t,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,y.appendChild(T)}C+=~~(x.w||c.w)+m,++G}return q.width=Math.max(Math.ceil(l.convert(B*u)),0),n}}}}()),Cufon.getTextDecoration=function(a){return{underline:"underline"===a.textDecoration,overline:"overline"===a.textDecoration,"line-through":"line-through"===a.textDecoration}},"undefined"!=typeof exports&&(exports.Cufon=Cufon),"object"!=typeof JSON&&(JSON={}),function(){"use strict";function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)"string"==typeof rep[c]&&(d=rep[c],e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.prototype.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"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(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"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(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.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 a(a,b){this.__eventListeners[a]&&(b?fabric.util.removeFromArray(this.__eventListeners[a],b):this.__eventListeners[a].length=0)}function b(a,b){if(this.__eventListeners||(this.__eventListeners={}),1===arguments.length)for(var c in a)this.on(c,a[c]);else this.__eventListeners[a]||(this.__eventListeners[a]=[]),this.__eventListeners[a].push(b);return this}function c(b,c){if(this.__eventListeners){if(0===arguments.length)this.__eventListeners={};else if(1===arguments.length&&"object"==typeof arguments[0])for(var d in b)a.call(this,d,b[d]);else a.call(this,b,c);return this}}function d(a,b){if(this.__eventListeners){var c=this.__eventListeners[a];if(c){for(var d=0,e=c.length;e>d;d++)c[d].call(this,b||{});return this}}}fabric.Observable={observe:b,stopObserving:c,fire:d,on:b,off:c,trigger:d}}(),fabric.Collection={add:function(){this._objects.push.apply(this._objects,arguments);for(var a=0,b=arguments.length;b>a;a++)this._onObjectAdded(arguments[a]);return this.renderOnAddRemove&&this.renderAll(),this},insertAt:function(a,b,c){var d=this.getObjects();return c?d[b]=a:d.splice(b,0,a),this._onObjectAdded(a),this.renderOnAddRemove&&this.renderAll(),this},remove:function(){for(var a,b=this.getObjects(),c=0,d=arguments.length;d>c;c++)a=b.indexOf(arguments[c]),-1!==a&&(b.splice(a,1),this._onObjectRemoved(arguments[c]));return this.renderOnAddRemove&&this.renderAll(),this},forEachObject:function(a,b){for(var c=this.getObjects(),d=c.length;d--;)a.call(b,c[d],d,c);return this},getObjects:function(a){return"undefined"==typeof a?this._objects:this._objects.filter(function(b){return b.type===a})},item:function(a){return this.getObjects()[a]},isEmpty:function(){return 0===this.getObjects().length},size:function(){return this.getObjects().length},contains:function(a){return this.getObjects().indexOf(a)>-1},complexity:function(){return this.getObjects().reduce(function(a,b){return a+=b.complexity?b.complexity():0},0)}},function(a){var b=Math.sqrt,c=Math.atan2,d=Math.PI/180;fabric.util={removeFromArray:function(a,b){var c=a.indexOf(b);return-1!==c&&a.splice(c,1),a},getRandomInt:function(a,b){return Math.floor(Math.random()*(b-a+1))+a},degreesToRadians:function(a){return a*d},radiansToDegrees:function(a){return a/d},rotatePoint:function(a,b,c){var d=Math.sin(c),e=Math.cos(c);a.subtractEquals(b);var f=a.x*e-a.y*d,g=a.x*d+a.y*e;return new fabric.Point(f,g).addEquals(b)},transformPoint:function(a,b,c){return c?new fabric.Point(b[0]*a.x+b[1]*a.y,b[2]*a.x+b[3]*a.y):new fabric.Point(b[0]*a.x+b[1]*a.y+b[4],b[2]*a.x+b[3]*a.y+b[5])},invertTransform:function(a){var b=a.slice(),c=1/(a[0]*a[3]-a[1]*a[2]);b=[c*a[3],-c*a[1],-c*a[2],c*a[0],0,0];var d=fabric.util.transformPoint({x:a[4],y:a[5]},b);return b[4]=-d.x,b[5]=-d.y,b},toFixed:function(a,b){return parseFloat(Number(a).toFixed(b))},parseUnit:function(a){var b=/\D{0,2}$/.exec(a),c=parseFloat(a);switch(b[0]){case"mm":return c*fabric.DPI/25.4;case"cm":return c*fabric.DPI/2.54;case"in":return c*fabric.DPI;case"pt":return c*fabric.DPI/72;case"pc":return c*fabric.DPI/72*12;default:return c}},falseFunction:function(){return!1},getKlass:function(a,b){return a=fabric.util.string.camelize(a.charAt(0).toUpperCase()+a.slice(1)),fabric.util.resolveNamespace(b)[a]},resolveNamespace:function(b){if(!b)return fabric;for(var c=b.split("."),d=c.length,e=a||fabric.window,f=0;d>f;++f)e=e[c[f]];return e},loadImage:function(a,b,c,d){if(!a)return void(b&&b.call(c,a));var e=fabric.util.createImage();e.onload=function(){b&&b.call(c,e),e=e.onload=e.onerror=null},e.onerror=function(){fabric.log("Error loading "+e.src),b&&b.call(c,null,!0),e=e.onload=e.onerror=null},0!==a.indexOf("data")&&"undefined"!=typeof d&&(e.crossOrigin=d),e.src=a},enlivenObjects:function(a,b,c,d){function e(){++g===h&&b&&b(f)}a=a||[];var f=[],g=0,h=a.length;return h?void a.forEach(function(a,b){if(!a||!a.type)return void e();var g=fabric.util.getKlass(a.type,c);g.async?g.fromObject(a,function(c,g){g||(f[b]=c,d&&d(a,f[b])),e()}):(f[b]=g.fromObject(a),d&&d(a,f[b]),e())}):void(b&&b(f))},groupSVGElements:function(a,b,c){var d;return d=new fabric.PathGroup(a,b),"undefined"!=typeof c&&d.setSourcePath(c),d},populateWithProperties:function(a,b,c){if(c&&"[object Array]"===Object.prototype.toString.call(c))for(var d=0,e=c.length;e>d;d++)c[d]in a&&(b[c[d]]=a[c[d]])},drawDashedLine:function(a,d,e,f,g,h){var i=f-d,j=g-e,k=b(i*i+j*j),l=c(j,i),m=h.length,n=0,o=!0;for(a.save(),a.translate(d,e),a.moveTo(0,0),a.rotate(l),d=0;k>d;)d+=h[n++%m],d>k&&(d=k),a[o?"lineTo":"moveTo"](d,0),o=!o;a.restore()},createCanvasElement:function(a){return a||(a=fabric.document.createElement("canvas")),a.getContext||"undefined"==typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(a),a},createImage:function(){return fabric.isLikelyNode?new(require("canvas").Image):fabric.document.createElement("img")},createAccessors:function(a){for(var b=a.prototype,c=b.stateProperties.length;c--;){var d=b.stateProperties[c],e=d.charAt(0).toUpperCase()+d.slice(1),f="set"+e,g="get"+e;b[g]||(b[g]=function(a){return new Function('return this.get("'+a+'")')}(d)),b[f]||(b[f]=function(a){return new Function("value",'return this.set("'+a+'", value)')}(d))}},clipContext:function(a,b){b.save(),b.beginPath(),a.clipTo(b),b.clip()},multiplyTransformMatrices:function(a,b){for(var c=[[a[0],a[2],a[4]],[a[1],a[3],a[5]],[0,0,1]],d=[[b[0],b[2],b[4]],[b[1],b[3],b[5]],[0,0,1]],e=[],f=0;3>f;f++){e[f]=[];for(var g=0;3>g;g++){for(var h=0,i=0;3>i;i++)h+=c[f][i]*d[i][g];e[f][g]=h}}return[e[0][0],e[1][0],e[0][1],e[1][1],e[0][2],e[1][2]]},getFunctionBody:function(a){return(String(a).match(/function[^{]*\{([\s\S]*)\}/)||{})[1]},isTransparent:function(a,b,c,d){d>0&&(b>d?b-=d:b=0,c>d?c-=d:c=0);for(var e=!0,f=a.getImageData(b,c,2*d||1,2*d||1),g=3,h=f.data.length;h>g;g+=4){var i=f.data[g];if(e=0>=i,e===!1)break}return f=null,e}}}("undefined"!=typeof exports?exports:this),function(){function a(a,e,g,h,i,j,k){var l=f.call(arguments);if(d[l])return d[l];var m=Math.PI,n=k*(m/180),o=Math.sin(n),p=Math.cos(n),q=0,r=0;g=Math.abs(g),h=Math.abs(h);var s=-p*a-o*e,t=-p*e+o*a,u=g*g,v=h*h,w=t*t,x=s*s,y=4*u*v-u*w-v*x,z=0;if(0>y){var A=Math.sqrt(1-.25*y/(u*v));g*=A,h*=A}else z=(i===j?-.5:.5)*Math.sqrt(y/(u*w+v*x));var B=z*g*t/h,C=-z*h*s/g,D=p*B-o*C+a/2,E=o*B+p*C+e/2,F=c(1,0,(s-B)/g,(t-C)/h),G=c((s-B)/g,(t-C)/h,(-s-B)/g,(-t-C)/h);0===j&&G>0?G-=2*m:1===j&&0>G&&(G+=2*m);for(var H=Math.ceil(Math.abs(G/(.5*m))),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]=b(F,L,p,o,g,h,D,E,K,q,r),q=I[M][4],r=I[M][5],F+=J,L+=J;return d[l]=I,I}function b(a,b,c,d,g,h,i,j,k,l,m){var n=f.call(arguments);if(e[n])return e[n];var o=Math.cos(a),p=Math.sin(a),q=Math.cos(b),r=Math.sin(b),s=c*g*q-d*h*r+i,t=d*g*q+c*h*r+j,u=l+k*(-c*g*p-d*h*o),v=m+k*(-d*g*p+c*h*o),w=s+k*(c*g*r+d*h*q),x=t+k*(d*g*r-c*h*q);return e[n]=[u,v,w,x,s,t],e[n]}function c(a,b,c,d){var e=Math.atan2(b,a),f=Math.atan2(d,c);return f>=e?f-e:2*Math.PI-(e-f)}var d={},e={},f=Array.prototype.join;fabric.util.drawArc=function(b,c,d,e){for(var f=e[0],g=e[1],h=e[2],i=e[3],j=e[4],k=e[5],l=e[6],m=[[],[],[],[]],n=a(k-c,l-d,f,g,i,j,h),o=0,p=n.length;p>o;o++)m[o][0]=n[o][0]+c,m[o][1]=n[o][1]+d,m[o][2]=n[o][2]+c,m[o][3]=n[o][3]+d,m[o][4]=n[o][4]+c,m[o][5]=n[o][5]+d,b.bezierCurveTo.apply(b,m[o])}}(),function(){function a(a,b){for(var c=e.call(arguments,2),d=[],f=0,g=a.length;g>f;f++)d[f]=c.length?a[f][b].apply(a[f],c):a[f][b].call(a[f]);return d}function b(a,b){return d(a,b,function(a,b){return a>=b})}function c(a,b){return d(a,b,function(a,b){return b>a})}function d(a,b,c){if(a&&0!==a.length){var d=a.length-1,e=b?a[d][b]:a[d];if(b)for(;d--;)c(a[d][b],e)&&(e=a[d][b]);else for(;d--;)c(a[d],e)&&(e=a[d]);return e}}var e=Array.prototype.slice;fabric.util.array={invoke:a,min:c,max:b}}(),function(){function a(a,b){for(var c in b)a[c]=b[c];return a}function b(b){return a({},b)}fabric.util.object={extend:a,clone:b}}(),function(){function a(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})}function b(a,b){return a.charAt(0).toUpperCase()+(b?a.slice(1):a.slice(1).toLowerCase())}function c(a){return a.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&apos;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}fabric.util.string={camelize:a,capitalize:b,escapeXml:c}}(),function(){function a(){}function b(a){var b=this.constructor.superclass.prototype[a];return arguments.length>1?b.apply(this,d.call(arguments,1)):b.call(this)}function c(){function c(){this.initialize.apply(this,arguments)}var f=null,h=d.call(arguments,0);"function"==typeof h[0]&&(f=h.shift()),c.superclass=f,c.subclasses=[],f&&(a.prototype=f.prototype,c.prototype=new a,f.subclasses.push(c));for(var i=0,j=h.length;j>i;i++)g(c,h[i],f);return c.prototype.initialize||(c.prototype.initialize=e),c.prototype.constructor=c,c.prototype.callSuper=b,c}var d=Array.prototype.slice,e=function(){},f=function(){for(var a in{toString:1})if("toString"===a)return!1;return!0}(),g=function(a,b,c){for(var d in b)a.prototype[d]=d in a.prototype&&"function"==typeof a.prototype[d]&&(b[d]+"").indexOf("callSuper")>-1?function(a){return function(){var d=this.constructor.superclass;this.constructor.superclass=c;var e=b[a].apply(this,arguments);return this.constructor.superclass=d,"initialize"!==a?e:void 0}}(d):b[d],f&&(b.toString!==Object.prototype.toString&&(a.prototype.toString=b.toString),b.valueOf!==Object.prototype.valueOf&&(a.prototype.valueOf=b.valueOf))};fabric.util.createClass=c}(),function(){function a(a){var b,c,d=Array.prototype.slice.call(arguments,1),e=d.length;for(c=0;e>c;c++)if(b=typeof a[d[c]],!/^(?:function|object|unknown)$/.test(b))return!1;return!0}function b(a,b){return{handler:b,wrappedHandler:c(a,b)}}function c(a,b){return function(c){b.call(g(a),c||fabric.window.event)}}function d(a,b){return function(c){if(p[a]&&p[a][b])for(var d=p[a][b],e=0,f=d.length;f>e;e++)d[e].call(this,c||fabric.window.event)}}function e(a,b){a||(a=fabric.window.event);var c=a.target||(typeof a.srcElement!==i?a.srcElement:null),d=fabric.util.getScrollLeftTop(c,b);return{x:q(a)+d.left,y:r(a)+d.top}}function f(a,b,c){var d="touchend"===a.type?"changedTouches":"touches";return a[d]&&a[d][0]?a[d][0][b]-(a[d][0][b]-a[d][0][c])||a[c]:a[c]}var g,h,i="unknown",j=function(){var a=0;return function(b){return b.__uniqueID||(b.__uniqueID="uniqueID__"+a++)}}();!function(){var a={};g=function(b){return a[b]},h=function(b,c){a[b]=c}}();var k,l,m=a(fabric.document.documentElement,"addEventListener","removeEventListener")&&a(fabric.window,"addEventListener","removeEventListener"),n=a(fabric.document.documentElement,"attachEvent","detachEvent")&&a(fabric.window,"attachEvent","detachEvent"),o={},p={};m?(k=function(a,b,c){a.addEventListener(b,c,!1)},l=function(a,b,c){a.removeEventListener(b,c,!1)}):n?(k=function(a,c,d){var e=j(a);h(e,a),o[e]||(o[e]={}),o[e][c]||(o[e][c]=[]);var f=b(e,d);o[e][c].push(f),a.attachEvent("on"+c,f.wrappedHandler)},l=function(a,b,c){var d,e=j(a);if(o[e]&&o[e][b])for(var f=0,g=o[e][b].length;g>f;f++)d=o[e][b][f],d&&d.handler===c&&(a.detachEvent("on"+b,d.wrappedHandler),o[e][b][f]=null)}):(k=function(a,b,c){var e=j(a);if(p[e]||(p[e]={}),!p[e][b]){p[e][b]=[];var f=a["on"+b];f&&p[e][b].push(f),a["on"+b]=d(e,b)}p[e][b].push(c)},l=function(a,b,c){var d=j(a);if(p[d]&&p[d][b])for(var e=p[d][b],f=0,g=e.length;g>f;f++)e[f]===c&&e.splice(f,1)}),fabric.util.addListener=k,fabric.util.removeListener=l;var q=function(a){return typeof a.clientX!==i?a.clientX:0},r=function(a){return typeof a.clientY!==i?a.clientY:0};fabric.isTouchSupported&&(q=function(a){return f(a,"pageX","clientX")},r=function(a){return f(a,"pageY","clientY")
+}),fabric.util.getPointer=e,fabric.util.object.extend(fabric.util,fabric.Observable)}(),function(){function a(a,b){var c=a.style;if(!c)return a;if("string"==typeof b)return a.style.cssText+=";"+b,b.indexOf("opacity")>-1?f(a,b.match(/opacity:\s*(\d?\.?\d*)/)[1]):a;for(var d in b)if("opacity"===d)f(a,b[d]);else{var e="float"===d||"cssFloat"===d?"undefined"==typeof c.styleFloat?"cssFloat":"styleFloat":d;c[e]=b[d]}return a}var b=fabric.document.createElement("div"),c="string"==typeof b.style.opacity,d="string"==typeof b.style.filter,e=/alpha\s*\(\s*opacity\s*=\s*([^\)]+)\)/,f=function(a){return a};c?f=function(a,b){return a.style.opacity=b,a}:d&&(f=function(a,b){var c=a.style;return a.currentStyle&&!a.currentStyle.hasLayout&&(c.zoom=1),e.test(c.filter)?(b=b>=.9999?"":"alpha(opacity="+100*b+")",c.filter=c.filter.replace(e,b)):c.filter+=" alpha(opacity="+100*b+")",a}),fabric.util.setStyle=a}(),function(){function a(a){return"string"==typeof a?fabric.document.getElementById(a):a}function b(a,b){var c=fabric.document.createElement(a);for(var d in b)"class"===d?c.className=b[d]:"for"===d?c.htmlFor=b[d]:c.setAttribute(d,b[d]);return c}function c(a,b){a&&-1===(" "+a.className+" ").indexOf(" "+b+" ")&&(a.className+=(a.className?" ":"")+b)}function d(a,c,d){return"string"==typeof c&&(c=b(c,d)),a.parentNode&&a.parentNode.replaceChild(c,a),c.appendChild(a),c}function e(a,b){var c,d,e=0,f=0,g=fabric.document.documentElement,h=fabric.document.body||{scrollLeft:0,scrollTop:0};for(d=a;a&&a.parentNode&&!c;)a=a.parentNode,a!==fabric.document&&"fixed"===fabric.util.getElementStyle(a,"position")&&(c=a),a!==fabric.document&&d!==b&&"absolute"===fabric.util.getElementStyle(a,"position")?(e=0,f=0):a===fabric.document?(e=h.scrollLeft||g.scrollLeft||0,f=h.scrollTop||g.scrollTop||0):(e+=a.scrollLeft||0,f+=a.scrollTop||0);return{left:e,top:f}}function f(a){var b,c,d=a&&a.ownerDocument,e={left:0,top:0},f={left:0,top:0},g={borderLeftWidth:"left",borderTopWidth:"top",paddingLeft:"left",paddingTop:"top"};if(!d)return{left:0,top:0};for(var h in g)f[g[h]]+=parseInt(k(a,h),10)||0;return b=d.documentElement,"undefined"!=typeof a.getBoundingClientRect&&(e=a.getBoundingClientRect()),c=fabric.util.getScrollLeftTop(a,null),{left:e.left+c.left-(b.clientLeft||0)+f.left,top:e.top+c.top-(b.clientTop||0)+f.top}}var g,h=Array.prototype.slice,i=function(a){return h.call(a,0)};try{g=i(fabric.document.childNodes)instanceof Array}catch(j){}g||(i=function(a){for(var b=new Array(a.length),c=a.length;c--;)b[c]=a[c];return b});var k;k=fabric.document.defaultView&&fabric.document.defaultView.getComputedStyle?function(a,b){return fabric.document.defaultView.getComputedStyle(a,null)[b]}:function(a,b){var c=a.style[b];return!c&&a.currentStyle&&(c=a.currentStyle[b]),c},function(){function a(a){return"undefined"!=typeof a.onselectstart&&(a.onselectstart=fabric.util.falseFunction),d?a.style[d]="none":"string"==typeof a.unselectable&&(a.unselectable="on"),a}function b(a){return"undefined"!=typeof a.onselectstart&&(a.onselectstart=null),d?a.style[d]="":"string"==typeof a.unselectable&&(a.unselectable=""),a}var c=fabric.document.documentElement.style,d="userSelect"in c?"userSelect":"MozUserSelect"in c?"MozUserSelect":"WebkitUserSelect"in c?"WebkitUserSelect":"KhtmlUserSelect"in c?"KhtmlUserSelect":"";fabric.util.makeElementUnselectable=a,fabric.util.makeElementSelectable=b}(),function(){function a(a,b){var c=fabric.document.getElementsByTagName("head")[0],d=fabric.document.createElement("script"),e=!0;d.onload=d.onreadystatechange=function(a){if(e){if("string"==typeof this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)return;e=!1,b(a||fabric.window.event),d=d.onload=d.onreadystatechange=null}},d.src=a,c.appendChild(d)}fabric.util.getScript=a}(),fabric.util.getById=a,fabric.util.toArray=i,fabric.util.makeElement=b,fabric.util.addClass=c,fabric.util.wrapElement=d,fabric.util.getScrollLeftTop=e,fabric.util.getElementOffset=f,fabric.util.getElementStyle=k}(),function(){function a(a,b){return a+(/\?/.test(a)?"&":"?")+b}function b(){}function c(c,e){e||(e={});var f,g=e.method?e.method.toUpperCase():"GET",h=e.onComplete||function(){},i=d();return i.onreadystatechange=function(){4===i.readyState&&(h(i),i.onreadystatechange=b)},"GET"===g&&(f=null,"string"==typeof e.parameters&&(c=a(c,e.parameters))),i.open(g,c,!0),("POST"===g||"PUT"===g)&&i.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),i.send(f),i}var d=function(){for(var a=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP.3.0")},function(){return new XMLHttpRequest}],b=a.length;b--;)try{var c=a[b]();if(c)return a[b]}catch(d){}}();fabric.util.request=c}(),fabric.log=function(){},fabric.warn=function(){},"undefined"!=typeof console&&["log","warn"].forEach(function(a){"undefined"!=typeof console[a]&&console[a].apply&&(fabric[a]=function(){return console[a].apply(console,arguments)})}),function(a){"use strict";function b(a){return a in w?w[a]:a}function c(a,b,c){var d,e="[object Array]"===Object.prototype.toString.call(b);return"fill"!==a&&"stroke"!==a||"none"!==b?"fillRule"===a?b="evenodd"===b?"destination-over":b:"strokeDashArray"===a?b=b.replace(/,/g," ").split(/\s+/).map(function(a){return parseInt(a)}):"transformMatrix"===a?b=c&&c.transformMatrix?v(c.transformMatrix,p.parseTransformAttribute(b)):p.parseTransformAttribute(b):"visible"===a?(b="none"===b||"hidden"===b?!1:!0,c&&c.visible===!1&&(b=!1)):"originX"===a?b="start"===b?"left":"end"===b?"right":"center":d=e?b.map(u):u(b):b="",!e&&isNaN(d)?b:d}function d(a){for(var b in x)if(a[b]&&"undefined"!=typeof a[x[b]]&&0!==a[b].indexOf("url(")){var c=new p.Color(a[b]);a[b]=c.setAlpha(t(c.getAlpha()*a[x[b]],2)).toRgba()}return a}function e(a,b){var c=a.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(c){var d=c[1],e=c[3],f=c[4],g=c[5],h=c[6];d&&(b.fontStyle=d),e&&(b.fontWeight=isNaN(parseFloat(e))?e:parseFloat(e)),f&&(b.fontSize=parseFloat(f)),h&&(b.fontFamily=h),g&&(b.lineHeight="normal"===g?1:g)}}function f(a,d){var f,g;a.replace(/;$/,"").split(";").forEach(function(a){var h=a.split(":");f=b(h[0].trim().toLowerCase()),g=c(f,h[1].trim()),"font"===f?e(g,d):d[f]=g})}function g(a,d){var f,g;for(var h in a)"undefined"!=typeof a[h]&&(f=b(h.toLowerCase()),g=c(f,a[h]),"font"===f?e(g,d):d[f]=g)}function h(a){var b={};for(var c in p.cssRules)if(i(a,c.split(" ")))for(var d in p.cssRules[c])b[d]=p.cssRules[c][d];return b}function i(a,b){var c,d=!0;return c=k(a,b.pop()),c&&b.length&&(d=j(a,b)),c&&d&&0===b.length}function j(a,b){for(var c,d=!0;a.parentNode&&1===a.parentNode.nodeType&&b.length;)d&&(c=b.pop()),a=a.parentNode,d=k(a,c);return 0===b.length}function k(a,b){var c,d=a.nodeName,e=a.getAttribute("class"),f=a.getAttribute("id");if(c=new RegExp("^"+d,"i"),b=b.replace(c,""),f&&b.length&&(c=new RegExp("#"+f+"(?![a-zA-Z\\-]+)","i"),b=b.replace(c,"")),e&&b.length){e=e.split(" ");for(var g=e.length;g--;)c=new RegExp("\\."+e[g]+"(?![a-zA-Z\\-]+)","i"),b=b.replace(c,"")}return 0===b.length}function l(a){for(var b=a.getElementsByTagName("use");b.length;){for(var c,d=b[0],e=d.getAttribute("xlink:href").substr(1),f=d.getAttribute("x")||0,g=d.getAttribute("y")||0,h=a.getElementById(e).cloneNode(!0),i=(d.getAttribute("transform")||"")+" translate("+f+", "+g+")",j=0,k=d.attributes,l=k.length;l>j;j++){var m=k.item(j);"x"!==m.nodeName&&"y"!==m.nodeName&&"xlink:href"!==m.nodeName&&("transform"===m.nodeName?i=i+" "+m.nodeValue:h.setAttribute(m.nodeName,m.nodeValue))}h.setAttribute("transform",i),h.removeAttribute("id"),c=d.parentNode,c.replaceChild(h,d)}}function m(a,b){if(b[3]=b[0]=b[0]>b[3]?b[3]:b[0],1!==b[0]||1!==b[3]||0!==b[4]||0!==b[5]){for(var c=a.ownerDocument.createElement("g");null!=a.firstChild;)c.appendChild(a.firstChild);c.setAttribute("transform","matrix("+b[0]+" "+b[1]+" "+b[2]+" "+b[3]+" "+b[4]+" "+b[5]+")"),a.appendChild(c)}}function n(a){var b=a.objects,c=a.options;return b=b.map(function(a){return p[r(a.type)].fromObject(a)}),{objects:b,options:c}}function o(a,b,c){b[c]&&b[c].toSVG&&a.push('<pattern x="0" y="0" id="',c,'Pattern" ','width="',b[c].source.width,'" height="',b[c].source.height,'" patternUnits="userSpaceOnUse">','<image x="0" y="0" ','width="',b[c].source.width,'" height="',b[c].source.height,'" xlink:href="',b[c].source.src,'"></image></pattern>')}var p=a.fabric||(a.fabric={}),q=p.util.object.extend,r=p.util.string.capitalize,s=p.util.object.clone,t=p.util.toFixed,u=p.util.parseUnit,v=p.util.multiplyTransformMatrices,w={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"},x={stroke:"strokeOpacity",fill:"fillOpacity"};p.parseTransformAttribute=function(){function a(a,b){var c=b[0];a[0]=Math.cos(c),a[1]=Math.sin(c),a[2]=-Math.sin(c),a[3]=Math.cos(c)}function b(a,b){var c=b[0],d=2===b.length?b[1]:b[0];a[0]=c,a[3]=d}function c(a,b){a[2]=b[0]}function d(a,b){a[1]=b[0]}function e(a,b){a[4]=b[0],2===b.length&&(a[5]=b[1])}var f=[1,0,0,1,0,0],g="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",h="(?:\\s+,?\\s*|,\\s*)",i="(?:(skewX)\\s*\\(\\s*("+g+")\\s*\\))",j="(?:(skewY)\\s*\\(\\s*("+g+")\\s*\\))",k="(?:(rotate)\\s*\\(\\s*("+g+")(?:"+h+"("+g+")"+h+"("+g+"))?\\s*\\))",l="(?:(scale)\\s*\\(\\s*("+g+")(?:"+h+"("+g+"))?\\s*\\))",m="(?:(translate)\\s*\\(\\s*("+g+")(?:"+h+"("+g+"))?\\s*\\))",n="(?:(matrix)\\s*\\(\\s*("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")"+h+"("+g+")\\s*\\))",o="(?:"+n+"|"+m+"|"+l+"|"+k+"|"+i+"|"+j+")",q="(?:"+o+"(?:"+h+o+")*)",r="^\\s*(?:"+q+"?)\\s*$",s=new RegExp(r),t=new RegExp(o,"g");return function(g){var h=f.concat(),i=[];if(!g||g&&!s.test(g))return h;g.replace(t,function(g){var j=new RegExp(o).exec(g).filter(function(a){return""!==a&&null!=a}),k=j[1],l=j.slice(2).map(parseFloat);switch(k){case"translate":e(h,l);break;case"rotate":l[0]=p.util.degreesToRadians(l[0]),a(h,l);break;case"scale":b(h,l);break;case"skewX":c(h,l);break;case"skewY":d(h,l);break;case"matrix":h=l}i.push(h.concat()),h=f.concat()});for(var j=i[0];i.length>1;)i.shift(),j=p.util.multiplyTransformMatrices(j,i[0]);return j}}(),p.parseSVGDocument=function(){function a(a,b){for(;a&&(a=a.parentNode);)if(b.test(a.nodeName))return!0;return!1}var b=/^(path|circle|polygon|polyline|ellipse|rect|line|image|text)$/,c="(?:[-+]?(?:\\d+|\\d*\\.\\d+)(?:e[-+]?\\d+)?)",d=new RegExp("^\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*,?\\s*("+c+"+)\\s*$");return function(c,e,f){if(c){var g=new Date;l(c);var h,i,j=c.getAttribute("viewBox"),k=u(c.getAttribute("width")||"100%"),n=u(c.getAttribute("height")||"100%");if(j&&(j=j.match(d))){var o=parseFloat(j[1]),q=parseFloat(j[2]),r=1,t=1;h=parseFloat(j[3]),i=parseFloat(j[4]),k&&k!==h&&(r=k/h),n&&n!==i&&(t=n/i),m(c,[r,0,0,t,r*-o,t*-q])}var v=p.util.toArray(c.getElementsByTagName("*"));if(0===v.length&&p.isLikelyNode){v=c.selectNodes('//*[name(.)!="svg"]');for(var w=[],x=0,y=v.length;y>x;x++)w[x]=v[x];v=w}var z=v.filter(function(c){return b.test(c.tagName)&&!a(c,/^(?:pattern|defs)$/)});if(!z||z&&!z.length)return void(e&&e([],{}));var A={width:k?k:h,height:n?n:i,widthAttr:k,heightAttr:n};p.gradientDefs=p.getGradientDefs(c),p.cssRules=p.getCSSRules(c),p.parseElements(z,function(a){p.documentParsingTime=new Date-g,e&&e(a,A)},s(A),f)}}}();var y={has:function(a,b){b(!1)},get:function(){},set:function(){}};q(p,{getGradientDefs:function(a){var b,c,d,e,f=a.getElementsByTagName("linearGradient"),g=a.getElementsByTagName("radialGradient"),h=0,i=[],j={},k={};for(i.length=f.length+g.length,c=f.length;c--;)i[h++]=f[c];for(c=g.length;c--;)i[h++]=g[c];for(;h--;)b=i[h],e=b.getAttribute("xlink:href"),d=b.getAttribute("id"),e&&(k[d]=e.substr(1)),j[d]=b;for(d in k){var l=j[k[d]].cloneNode(!0);for(b=j[d];l.firstChild;)b.appendChild(l.firstChild)}return j},parseAttributes:function(a,e){if(a){var f,g={};a.parentNode&&/^symbol|[g|a]$/i.test(a.parentNode.nodeName)&&(g=p.parseAttributes(a.parentNode,e));var i=e.reduce(function(d,e){return f=a.getAttribute(e),f&&(e=b(e),f=c(e,f,g),d[e]=f),d},{});return i=q(i,q(h(a),p.parseStyleAttribute(a))),d(q(g,i))}},parseElements:function(a,b,c,d){new p.ElementsParser(a,b,c,d).parse()},parseStyleAttribute:function(a){var b={},c=a.getAttribute("style");return c?("string"==typeof c?f(c,b):g(c,b),b):b},parsePointsAttribute:function(a){if(!a)return null;a=a.replace(/,/g," ").trim(),a=a.split(/\s+/);var b,c,d=[];for(b=0,c=a.length;c>b;b+=2)d.push({x:parseFloat(a[b]),y:parseFloat(a[b+1])});return d},getCSSRules:function(a){for(var d,e=a.getElementsByTagName("style"),f={},g=0,h=e.length;h>g;g++){var i=e[0].textContent;i=i.replace(/\/\*[\s\S]*?\*\//g,""),d=i.match(/[^{]*\{[\s\S]*?\}/g),d=d.map(function(a){return a.trim()}),d.forEach(function(a){for(var d=a.match(/([\s\S]*?)\s*\{([^}]*)\}/),e={},g=d[2].trim(),h=g.replace(/;$/,"").split(/\s*;\s*/),i=0,j=h.length;j>i;i++){var k=h[i].split(/\s*:\s*/),l=b(k[0]),m=c(l,k[1],k[0]);e[l]=m}a=d[1],a.split(",").forEach(function(a){f[a.trim()]=p.util.object.clone(e)})})}return f},loadSVGFromURL:function(a,b,c){function d(d){var e=d.responseXML;e&&!e.documentElement&&p.window.ActiveXObject&&d.responseText&&(e=new ActiveXObject("Microsoft.XMLDOM"),e.async="false",e.loadXML(d.responseText.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,""))),e&&e.documentElement&&p.parseSVGDocument(e.documentElement,function(c,d){y.set(a,{objects:p.util.array.invoke(c,"toObject"),options:d}),b(c,d)},c)}a=a.replace(/^\n\s*/,"").trim(),y.has(a,function(c){c?y.get(a,function(a){var c=n(a);b(c.objects,c.options)}):new p.util.request(a,{method:"get",onComplete:d})})},loadSVGFromString:function(a,b,c){a=a.trim();var d;if("undefined"!=typeof DOMParser){var e=new DOMParser;e&&e.parseFromString&&(d=e.parseFromString(a,"text/xml"))}else p.window.ActiveXObject&&(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(a.replace(/<!DOCTYPE[\s\S]*?(\[[\s\S]*\])*?>/i,"")));p.parseSVGDocument(d.documentElement,function(a,c){b(a,c)},c)},createSVGFontFacesMarkup:function(a){for(var b="",c=0,d=a.length;d>c;c++)"text"===a[c].type&&a[c].path&&(b+=["@font-face {","font-family: ",a[c].fontFamily,"; ","src: url('",a[c].path,"')","}"].join(""));return b&&(b=['<style type="text/css">',"<![CDATA[",b,"]]>","</style>"].join("")),b},createSVGRefElementsMarkup:function(a){var b=[];return o(b,a,"backgroundColor"),o(b,a,"overlayColor"),b.join("")}})}("undefined"!=typeof exports?exports:this),fabric.ElementsParser=function(a,b,c,d){this.elements=a,this.callback=b,this.options=c,this.reviver=d},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 a=0,b=this.elements.length;b>a;a++)!function(a,b){setTimeout(function(){a.createObject(a.elements[b],b)},0)}(this,a)},fabric.ElementsParser.prototype.createObject=function(a,b){var c=fabric[fabric.util.string.capitalize(a.tagName)];if(c&&c.fromElement)try{this._createObject(c,a,b)}catch(d){fabric.log(d)}else this.checkIfDone()},fabric.ElementsParser.prototype._createObject=function(a,b,c){if(a.async)a.fromElement(b,this.createCallback(c,b),this.options);else{var d=a.fromElement(b,this.options);this.resolveGradient(d,"fill"),this.resolveGradient(d,"stroke"),this.reviver&&this.reviver(b,d),this.instances[c]=d,this.checkIfDone()}},fabric.ElementsParser.prototype.createCallback=function(a,b){var c=this;return function(d){c.resolveGradient(d,"fill"),c.resolveGradient(d,"stroke"),c.reviver&&c.reviver(b,d),c.instances[a]=d,c.checkIfDone()}},fabric.ElementsParser.prototype.resolveGradient=function(a,b){var c=a.get(b);if(/^url\(/.test(c)){var d=c.slice(5,c.length-1);fabric.gradientDefs[d]&&a.set(b,fabric.Gradient.fromElement(fabric.gradientDefs[d],a))}},fabric.ElementsParser.prototype.checkIfDone=function(){0===--this.numElements&&(this.instances=this.instances.filter(function(a){return null!=a}),this.callback(this.instances))},function(a){"use strict";function b(a,b){this.x=a,this.y=b}var c=a.fabric||(a.fabric={});return c.Point?void c.warn("fabric.Point is already defined"):(c.Point=b,void(b.prototype={constructor:b,add:function(a){return new b(this.x+a.x,this.y+a.y)},addEquals:function(a){return this.x+=a.x,this.y+=a.y,this},scalarAdd:function(a){return new b(this.x+a,this.y+a)},scalarAddEquals:function(a){return this.x+=a,this.y+=a,this},subtract:function(a){return new b(this.x-a.x,this.y-a.y)},subtractEquals:function(a){return this.x-=a.x,this.y-=a.y,this},scalarSubtract:function(a){return new b(this.x-a,this.y-a)},scalarSubtractEquals:function(a){return this.x-=a,this.y-=a,this},multiply:function(a){return new b(this.x*a,this.y*a)},multiplyEquals:function(a){return this.x*=a,this.y*=a,this},divide:function(a){return new b(this.x/a,this.y/a)},divideEquals:function(a){return this.x/=a,this.y/=a,this},eq:function(a){return this.x===a.x&&this.y===a.y},lt:function(a){return this.x<a.x&&this.y<a.y},lte:function(a){return this.x<=a.x&&this.y<=a.y},gt:function(a){return this.x>a.x&&this.y>a.y},gte:function(a){return this.x>=a.x&&this.y>=a.y},lerp:function(a,c){return new b(this.x+(a.x-this.x)*c,this.y+(a.y-this.y)*c)},distanceFrom:function(a){var b=this.x-a.x,c=this.y-a.y;return Math.sqrt(b*b+c*c)},midPointFrom:function(a){return new b(this.x+(a.x-this.x)/2,this.y+(a.y-this.y)/2)},min:function(a){return new b(Math.min(this.x,a.x),Math.min(this.y,a.y))},max:function(a){return new b(Math.max(this.x,a.x),Math.max(this.y,a.y))},toString:function(){return this.x+","+this.y},setXY:function(a,b){this.x=a,this.y=b},setFromPoint:function(a){this.x=a.x,this.y=a.y},swap:function(a){var b=this.x,c=this.y;this.x=a.x,this.y=a.y,a.x=b,a.y=c}}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){this.status=a,this.points=[]}var c=a.fabric||(a.fabric={});return c.Intersection?void c.warn("fabric.Intersection is already defined"):(c.Intersection=b,c.Intersection.prototype={appendPoint:function(a){this.points.push(a)},appendPoints:function(a){this.points=this.points.concat(a)}},c.Intersection.intersectLineLine=function(a,d,e,f){var g,h=(f.x-e.x)*(a.y-e.y)-(f.y-e.y)*(a.x-e.x),i=(d.x-a.x)*(a.y-e.y)-(d.y-a.y)*(a.x-e.x),j=(f.y-e.y)*(d.x-a.x)-(f.x-e.x)*(d.y-a.y);if(0!==j){var k=h/j,l=i/j;k>=0&&1>=k&&l>=0&&1>=l?(g=new b("Intersection"),g.points.push(new c.Point(a.x+k*(d.x-a.x),a.y+k*(d.y-a.y)))):g=new b}else g=new b(0===h||0===i?"Coincident":"Parallel");return g},c.Intersection.intersectLinePolygon=function(a,c,d){for(var e=new b,f=d.length,g=0;f>g;g++){var h=d[g],i=d[(g+1)%f],j=b.intersectLineLine(a,c,h,i);e.appendPoints(j.points)}return e.points.length>0&&(e.status="Intersection"),e},c.Intersection.intersectPolygonPolygon=function(a,c){for(var d=new b,e=a.length,f=0;e>f;f++){var g=a[f],h=a[(f+1)%e],i=b.intersectLinePolygon(g,h,c);d.appendPoints(i.points)}return d.points.length>0&&(d.status="Intersection"),d},void(c.Intersection.intersectPolygonRectangle=function(a,d,e){var f=d.min(e),g=d.max(e),h=new c.Point(g.x,f.y),i=new c.Point(f.x,g.y),j=b.intersectLinePolygon(f,h,a),k=b.intersectLinePolygon(h,g,a),l=b.intersectLinePolygon(g,i,a),m=b.intersectLinePolygon(i,f,a),n=new b;return n.appendPoints(j.points),n.appendPoints(k.points),n.appendPoints(l.points),n.appendPoints(m.points),n.points.length>0&&(n.status="Intersection"),n}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){a?this._tryParsingColor(a):this.setSource([0,0,0,1])}function c(a,b,c){return 0>c&&(c+=1),c>1&&(c-=1),1/6>c?a+6*(b-a)*c:.5>c?b:2/3>c?a+(b-a)*(2/3-c)*6:a}var d=a.fabric||(a.fabric={});return d.Color?void d.warn("fabric.Color is already defined."):(d.Color=b,d.Color.prototype={_tryParsingColor:function(a){var c;return a in b.colorNameMap&&(a=b.colorNameMap[a]),"transparent"===a?void this.setSource([255,255,255,0]):(c=b.sourceFromHex(a),c||(c=b.sourceFromRgb(a)),c||(c=b.sourceFromHsl(a)),void(c&&this.setSource(c)))},_rgbToHsl:function(a,b,c){a/=255,b/=255,c/=255;var e,f,g,h=d.util.array.max([a,b,c]),i=d.util.array.min([a,b,c]);if(g=(h+i)/2,h===i)e=f=0;else{var j=h-i;switch(f=g>.5?j/(2-h-i):j/(h+i),h){case a:e=(b-c)/j+(c>b?6:0);break;case b:e=(c-a)/j+2;break;case c:e=(a-b)/j+4}e/=6}return[Math.round(360*e),Math.round(100*f),Math.round(100*g)]},getSource:function(){return this._source},setSource:function(a){this._source=a},toRgb:function(){var a=this.getSource();return"rgb("+a[0]+","+a[1]+","+a[2]+")"},toRgba:function(){var a=this.getSource();return"rgba("+a[0]+","+a[1]+","+a[2]+","+a[3]+")"},toHsl:function(){var a=this.getSource(),b=this._rgbToHsl(a[0],a[1],a[2]);return"hsl("+b[0]+","+b[1]+"%,"+b[2]+"%)"},toHsla:function(){var a=this.getSource(),b=this._rgbToHsl(a[0],a[1],a[2]);return"hsla("+b[0]+","+b[1]+"%,"+b[2]+"%,"+a[3]+")"},toHex:function(){var a,b,c,d=this.getSource();return a=d[0].toString(16),a=1===a.length?"0"+a:a,b=d[1].toString(16),b=1===b.length?"0"+b:b,c=d[2].toString(16),c=1===c.length?"0"+c:c,a.toUpperCase()+b.toUpperCase()+c.toUpperCase()},getAlpha:function(){return this.getSource()[3]},setAlpha:function(a){var b=this.getSource();return b[3]=a,this.setSource(b),this},toGrayscale:function(){var a=this.getSource(),b=parseInt((.3*a[0]+.59*a[1]+.11*a[2]).toFixed(0),10),c=a[3];return this.setSource([b,b,b,c]),this},toBlackWhite:function(a){var b=this.getSource(),c=(.3*b[0]+.59*b[1]+.11*b[2]).toFixed(0),d=b[3];return a=a||127,c=Number(c)<Number(a)?0:255,this.setSource([c,c,c,d]),this},overlayWith:function(a){a instanceof b||(a=new b(a));for(var c=[],d=this.getAlpha(),e=.5,f=this.getSource(),g=a.getSource(),h=0;3>h;h++)c.push(Math.round(f[h]*(1-e)+g[h]*e));return c[3]=d,this.setSource(c),this}},d.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*)?\)$/,d.Color.reHSLa=/^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/,d.Color.reHex=/^#?([0-9a-f]{6}|[0-9a-f]{3})$/i,d.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"},d.Color.fromRgb=function(a){return b.fromSource(b.sourceFromRgb(a))},d.Color.sourceFromRgb=function(a){var c=a.match(b.reRGBa);if(c){var d=parseInt(c[1],10)/(/%$/.test(c[1])?100:1)*(/%$/.test(c[1])?255:1),e=parseInt(c[2],10)/(/%$/.test(c[2])?100:1)*(/%$/.test(c[2])?255:1),f=parseInt(c[3],10)/(/%$/.test(c[3])?100:1)*(/%$/.test(c[3])?255:1);return[parseInt(d,10),parseInt(e,10),parseInt(f,10),c[4]?parseFloat(c[4]):1]}},d.Color.fromRgba=b.fromRgb,d.Color.fromHsl=function(a){return b.fromSource(b.sourceFromHsl(a))},d.Color.sourceFromHsl=function(a){var d=a.match(b.reHSLa);if(d){var e,f,g,h=(parseFloat(d[1])%360+360)%360/360,i=parseFloat(d[2])/(/%$/.test(d[2])?100:1),j=parseFloat(d[3])/(/%$/.test(d[3])?100:1);if(0===i)e=f=g=j;else{var k=.5>=j?j*(i+1):j+i-j*i,l=2*j-k;e=c(l,k,h+1/3),f=c(l,k,h),g=c(l,k,h-1/3)}return[Math.round(255*e),Math.round(255*f),Math.round(255*g),d[4]?parseFloat(d[4]):1]}},d.Color.fromHsla=b.fromHsl,d.Color.fromHex=function(a){return b.fromSource(b.sourceFromHex(a))},d.Color.sourceFromHex=function(a){if(a.match(b.reHex)){var c=a.slice(a.indexOf("#")+1),d=3===c.length,e=d?c.charAt(0)+c.charAt(0):c.substring(0,2),f=d?c.charAt(1)+c.charAt(1):c.substring(2,4),g=d?c.charAt(2)+c.charAt(2):c.substring(4,6);return[parseInt(e,16),parseInt(f,16),parseInt(g,16),1]}},void(d.Color.fromSource=function(a){var c=new b;return c.setSource(a),c}))}("undefined"!=typeof exports?exports:this),function(){function a(a){var b,c,d,e=a.getAttribute("style"),f=a.getAttribute("offset");if(f=parseFloat(f)/(/%$/.test(f)?100:1),f=0>f?0:f>1?1:f,e){var g=e.split(/\s*;\s*/);""===g[g.length-1]&&g.pop();for(var h=g.length;h--;){var i=g[h].split(/\s*:\s*/),j=i[0].trim(),k=i[1].trim();"stop-color"===j?b=k:"stop-opacity"===j&&(d=k)}}return b||(b=a.getAttribute("stop-color")||"rgb(0,0,0)"),d||(d=a.getAttribute("stop-opacity")),b=new fabric.Color(b),c=b.getAlpha(),d=isNaN(parseFloat(d))?1:parseFloat(d),d*=c,{offset:f,color:b.toRgb(),opacity:d}}function b(a){return{x1:a.getAttribute("x1")||0,y1:a.getAttribute("y1")||0,x2:a.getAttribute("x2")||"100%",y2:a.getAttribute("y2")||0}}function c(a){return{x1:a.getAttribute("fx")||a.getAttribute("cx")||"50%",y1:a.getAttribute("fy")||a.getAttribute("cy")||"50%",r1:0,x2:a.getAttribute("cx")||"50%",y2:a.getAttribute("cy")||"50%",r2:a.getAttribute("r")||"50%"}}function d(a,b,c){var d,e=0,f=1,g="";for(var h in b)d=parseFloat(b[h],10),f="string"==typeof b[h]&&/^\d+%$/.test(b[h])?.01:1,"x1"===h||"x2"===h||"r2"===h?(f*="objectBoundingBox"===c?a.width:1,e="objectBoundingBox"===c?a.left||0:0):("y1"===h||"y2"===h)&&(f*="objectBoundingBox"===c?a.height:1,e="objectBoundingBox"===c?a.top||0:0),b[h]=d*f+e;if("ellipse"===a.type&&null!==b.r2&&"objectBoundingBox"===c&&a.rx!==a.ry){var i=a.ry/a.rx;g=" scale(1, "+i+")",b.y1&&(b.y1/=i),b.y2&&(b.y2/=i)}return g}fabric.Gradient=fabric.util.createClass({offsetX:0,offsetY:0,initialize:function(a){a||(a={});var b={};this.id=fabric.Object.__uid++,this.type=a.type||"linear",b={x1:a.coords.x1||0,y1:a.coords.y1||0,x2:a.coords.x2||0,y2:a.coords.y2||0},"radial"===this.type&&(b.r1=a.coords.r1||0,b.r2=a.coords.r2||0),this.coords=b,this.colorStops=a.colorStops.slice(),a.gradientTransform&&(this.gradientTransform=a.gradientTransform),this.offsetX=a.offsetX||this.offsetX,this.offsetY=a.offsetY||this.offsetY},addColorStop:function(a){for(var b in a){var c=new fabric.Color(a[b]);this.colorStops.push({offset:b,color:c.toRgb(),opacity:c.getAlpha()})}return this},toObject:function(){return{type:this.type,coords:this.coords,colorStops:this.colorStops,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(a){var b,c,d=fabric.util.object.clone(this.coords);if(this.colorStops.sort(function(a,b){return a.offset-b.offset}),!a.group||"path-group"!==a.group.type)for(var e in d)"x1"===e||"x2"===e||"r2"===e?d[e]+=this.offsetX-a.width/2:("y1"===e||"y2"===e)&&(d[e]+=this.offsetY-a.height/2);c='id="SVGID_'+this.id+'" gradientUnits="userSpaceOnUse"',this.gradientTransform&&(c+=' gradientTransform="matrix('+this.gradientTransform.join(" ")+')" '),"linear"===this.type?b=["<linearGradient ",c,' x1="',d.x1,'" y1="',d.y1,'" x2="',d.x2,'" y2="',d.y2,'">\n']:"radial"===this.type&&(b=["<radialGradient ",c,' cx="',d.x2,'" cy="',d.y2,'" r="',d.r2,'" fx="',d.x1,'" fy="',d.y1,'">\n']);for(var f=0;f<this.colorStops.length;f++)b.push("<stop ",'offset="',100*this.colorStops[f].offset+"%",'" style="stop-color:',this.colorStops[f].color,null!=this.colorStops[f].opacity?";stop-opacity: "+this.colorStops[f].opacity:";",'"/>\n');return b.push("linear"===this.type?"</linearGradient>\n":"</radialGradient>\n"),b.join("")},toLive:function(a){var b;if(this.type){"linear"===this.type?b=a.createLinearGradient(this.coords.x1,this.coords.y1,this.coords.x2,this.coords.y2):"radial"===this.type&&(b=a.createRadialGradient(this.coords.x1,this.coords.y1,this.coords.r1,this.coords.x2,this.coords.y2,this.coords.r2));for(var c=0,d=this.colorStops.length;d>c;c++){var e=this.colorStops[c].color,f=this.colorStops[c].opacity,g=this.colorStops[c].offset;"undefined"!=typeof f&&(e=new fabric.Color(e).setAlpha(f).toRgba()),b.addColorStop(parseFloat(g),e)}return b}}}),fabric.util.object.extend(fabric.Gradient,{fromElement:function(e,f){var g,h=e.getElementsByTagName("stop"),i="linearGradient"===e.nodeName?"linear":"radial",j=e.getAttribute("gradientUnits")||"objectBoundingBox",k=e.getAttribute("gradientTransform"),l=[],m={};"linear"===i?m=b(e):"radial"===i&&(m=c(e));for(var n=h.length;n--;)l.push(a(h[n]));g=d(f,m,j);var o=new fabric.Gradient({type:i,coords:m,colorStops:l,offsetX:-f.left,offsetY:-f.top});return(k||""!==g)&&(o.gradientTransform=fabric.parseTransformAttribute((k||"")+g)),o},forObject:function(a,b){return b||(b={}),d(a,b.coords,"userSpaceOnUse"),new fabric.Gradient(b)}})}(),fabric.Pattern=fabric.util.createClass({repeat:"repeat",offsetX:0,offsetY:0,initialize:function(a){if(a||(a={}),this.id=fabric.Object.__uid++,a.source)if("string"==typeof a.source)if("undefined"!=typeof fabric.util.getFunctionBody(a.source))this.source=new Function(fabric.util.getFunctionBody(a.source));else{var b=this;this.source=fabric.util.createImage(),fabric.util.loadImage(a.source,function(a){b.source=a})}else this.source=a.source;a.repeat&&(this.repeat=a.repeat),a.offsetX&&(this.offsetX=a.offsetX),a.offsetY&&(this.offsetY=a.offsetY)},toObject:function(){var a;return"function"==typeof this.source?a=String(this.source):"string"==typeof this.source.src&&(a=this.source.src),{source:a,repeat:this.repeat,offsetX:this.offsetX,offsetY:this.offsetY}},toSVG:function(a){var b="function"==typeof this.source?this.source():this.source,c=b.width/a.getWidth(),d=b.height/a.getHeight(),e="";return b.src?e=b.src:b.toDataURL&&(e=b.toDataURL()),'<pattern id="SVGID_'+this.id+'" x="'+this.offsetX+'" y="'+this.offsetY+'" width="'+c+'" height="'+d+'"><image x="0" y="0" width="'+b.width+'" height="'+b.height+'" xlink:href="'+e+'"></image></pattern>'},toLive:function(a){var b="function"==typeof this.source?this.source():this.source;if(!b)return"";if("undefined"!=typeof b.src){if(!b.complete)return"";if(0===b.naturalWidth||0===b.naturalHeight)return""}return a.createPattern(b,this.repeat)}}),function(a){"use strict";var b=a.fabric||(a.fabric={});return b.Shadow?void b.warn("fabric.Shadow is already defined."):(b.Shadow=b.util.createClass({color:"rgb(0,0,0)",blur:0,offsetX:0,offsetY:0,affectStroke:!1,includeDefaultValues:!0,initialize:function(a){"string"==typeof a&&(a=this._parseShadow(a));for(var c in a)this[c]=a[c];this.id=b.Object.__uid++},_parseShadow:function(a){var c=a.trim(),d=b.Shadow.reOffsetsAndBlur.exec(c)||[],e=c.replace(b.Shadow.reOffsetsAndBlur,"")||"rgb(0,0,0)";return{color:e.trim(),offsetX:parseInt(d[1],10)||0,offsetY:parseInt(d[2],10)||0,blur:parseInt(d[3],10)||0}},toString:function(){return[this.offsetX,this.offsetY,this.blur,this.color].join("px ")},toSVG:function(a){var b="SourceAlpha";return!a||a.fill!==this.color&&a.stroke!==this.color||(b="SourceGraphic"),'<filter id="SVGID_'+this.id+'" y="-40%" height="180%"><feGaussianBlur in="'+b+'" 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 a={},c=b.Shadow.prototype;return this.color!==c.color&&(a.color=this.color),this.blur!==c.blur&&(a.blur=this.blur),this.offsetX!==c.offsetX&&(a.offsetX=this.offsetX),this.offsetY!==c.offsetY&&(a.offsetY=this.offsetY),a}}),void(b.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 a=fabric.util.object.extend,b=fabric.util.getElementOffset,c=fabric.util.removeFromArray,d=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(a,b){b||(b={}),this._initStatic(a,b),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(a,b){this._objects=[],this._createLowerCanvas(a),this._initOptions(b),this._setImageSmoothing(),b.overlayImage&&this.setOverlayImage(b.overlayImage,this.renderAll.bind(this)),b.backgroundImage&&this.setBackgroundImage(b.backgroundImage,this.renderAll.bind(this)),b.backgroundColor&&this.setBackgroundColor(b.backgroundColor,this.renderAll.bind(this)),b.overlayColor&&this.setOverlayColor(b.overlayColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=b(this.lowerCanvasEl),this},setOverlayImage:function(a,b,c){return this.__setBgOverlayImage("overlayImage",a,b,c)},setBackgroundImage:function(a,b,c){return this.__setBgOverlayImage("backgroundImage",a,b,c)},setOverlayColor:function(a,b){return this.__setBgOverlayColor("overlayColor",a,b)},setBackgroundColor:function(a,b){return this.__setBgOverlayColor("backgroundColor",a,b)},_setImageSmoothing:function(){var a=this.getContext();a.imageSmoothingEnabled=this.imageSmoothingEnabled,a.webkitImageSmoothingEnabled=this.imageSmoothingEnabled,a.mozImageSmoothingEnabled=this.imageSmoothingEnabled,a.msImageSmoothingEnabled=this.imageSmoothingEnabled,a.oImageSmoothingEnabled=this.imageSmoothingEnabled},__setBgOverlayImage:function(a,b,c,d){return"string"==typeof b?fabric.util.loadImage(b,function(b){this[a]=new fabric.Image(b,d),c&&c()},this):(this[a]=b,c&&c()),this},__setBgOverlayColor:function(a,b,c){if(b&&b.source){var d=this;fabric.util.loadImage(b.source,function(e){d[a]=new fabric.Pattern({source:e,repeat:b.repeat,offsetX:b.offsetX,offsetY:b.offsetY}),c&&c()})}else this[a]=b,c&&c();return this},_createCanvasElement:function(){var a=fabric.document.createElement("canvas");if(a.style||(a.style={}),!a)throw d;return this._initCanvasElement(a),a},_initCanvasElement:function(a){if(fabric.util.createCanvasElement(a),"undefined"==typeof a.getContext)throw d},_initOptions:function(a){for(var b in a)this[b]=a[b];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(a){this.lowerCanvasEl=fabric.util.getById(a)||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(a,b){return this.setDimensions({width:a},b)},setHeight:function(a,b){return this.setDimensions({height:a},b)},setDimensions:function(a,b){var c;b=b||{};for(var d in a)c=a[d],b.cssOnly||(this._setBackstoreDimension(d,a[d]),c+="px"),b.backstoreOnly||this._setCssDimension(d,c);return b.cssOnly||this.renderAll(),this.calcOffset(),this},_setBackstoreDimension:function(a,b){return this.lowerCanvasEl[a]=b,this.upperCanvasEl&&(this.upperCanvasEl[a]=b),this.cacheCanvasEl&&(this.cacheCanvasEl[a]=b),this[a]=b,this},_setCssDimension:function(a,b){return this.lowerCanvasEl.style[a]=b,this.upperCanvasEl&&(this.upperCanvasEl.style[a]=b),this.wrapperEl&&(this.wrapperEl.style[a]=b),this},getZoom:function(){return Math.sqrt(this.viewportTransform[0]*this.viewportTransform[3])},setViewportTransform:function(a){this.viewportTransform=a,this.renderAll();for(var b=0,c=this._objects.length;c>b;b++)this._objects[b].setCoords();return this},zoomToPoint:function(a,b){var c=a;a=fabric.util.transformPoint(a,fabric.util.invertTransform(this.viewportTransform)),this.viewportTransform[0]=b,this.viewportTransform[3]=b;var d=fabric.util.transformPoint(a,this.viewportTransform);this.viewportTransform[4]+=c.x-d.x,this.viewportTransform[5]+=c.y-d.y,this.renderAll();for(var e=0,f=this._objects.length;f>e;e++)this._objects[e].setCoords();return this},setZoom:function(a){return this.zoomToPoint(new fabric.Point(0,0),a),this},absolutePan:function(a){this.viewportTransform[4]=-a.x,this.viewportTransform[5]=-a.y,this.renderAll();for(var b=0,c=this._objects.length;c>b;b++)this._objects[b].setCoords();return this},relativePan:function(a){return this.absolutePan(new fabric.Point(-a.x-this.viewportTransform[4],-a.y-this.viewportTransform[5]))},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(a,b){if(b){a.save();var c=this.viewportTransform;a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),b.render(a),a.restore(),this.controlsAboveOverlay||b._renderControls(a)}},_onObjectAdded:function(a){this.stateful&&a.setupState(),a.canvas=this,a.setCoords(),this.fire("object:added",{target:a}),a.fire("added")},_onObjectRemoved:function(a){this.getActiveObject()===a&&(this.fire("before:selection:cleared",{target:a}),this._discardActiveObject(),this.fire("selection:cleared")),this.fire("object:removed",{target:a}),a.fire("removed")},clearContext:function(a){return a.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(a){var b=this[a===!0&&this.interactive?"contextTop":"contextContainer"],c=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),a||this.clearContext(b),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,b),this._renderBackground(b),this._renderObjects(b,c),this._renderActiveGroup(b,c),this.clipTo&&b.restore(),this._renderOverlay(b),this.controlsAboveOverlay&&this.interactive&&this.drawControls(b),this.fire("after:render"),this},_renderObjects:function(a,b){var c,d;if(b)for(c=0,d=this._objects.length;d>c;++c)this._objects[c]&&!b.contains(this._objects[c])&&this._draw(a,this._objects[c]);else for(c=0,d=this._objects.length;d>c;++c)this._draw(a,this._objects[c])},_renderActiveGroup:function(a,b){if(b){var c=[];this.forEachObject(function(a){b.contains(a)&&c.push(a)}),b._set("objects",c),this._draw(a,b)}},_renderBackground:function(a){this.backgroundColor&&(a.fillStyle=this.backgroundColor.toLive?this.backgroundColor.toLive(a):this.backgroundColor,a.fillRect(this.backgroundColor.offsetX||0,this.backgroundColor.offsetY||0,this.width,this.height)),this.backgroundImage&&this._draw(a,this.backgroundImage)},_renderOverlay:function(a){this.overlayColor&&(a.fillStyle=this.overlayColor.toLive?this.overlayColor.toLive(a):this.overlayColor,a.fillRect(this.overlayColor.offsetX||0,this.overlayColor.offsetY||0,this.width,this.height)),this.overlayImage&&this._draw(a,this.overlayImage)},renderTop:function(){var a=this.contextTop||this.contextContainer;this.clearContext(a),this.selection&&this._groupSelector&&this._drawSelection();var b=this.getActiveGroup();return b&&b.render(a),this._renderOverlay(a),this.fire("after:render"),this},getCenter:function(){return{top:this.getHeight()/2,left:this.getWidth()/2}},centerObjectH:function(a){return this._centerObject(a,new fabric.Point(this.getCenter().left,a.getCenterPoint().y)),this.renderAll(),this},centerObjectV:function(a){return this._centerObject(a,new fabric.Point(a.getCenterPoint().x,this.getCenter().top)),this.renderAll(),this},centerObject:function(a){var b=this.getCenter();return this._centerObject(a,new fabric.Point(b.left,b.top)),this.renderAll(),this},_centerObject:function(a,b){return a.setPositionByOrigin(b,"center","center"),this},toDatalessJSON:function(a){return this.toDatalessObject(a)},toObject:function(a){return this._toObjectMethod("toObject",a)},toDatalessObject:function(a){return this._toObjectMethod("toDatalessObject",a)},_toObjectMethod:function(b,c){var d=this.getActiveGroup();d&&this.discardActiveGroup();var e={objects:this._toObjects(b,c)};return a(e,this.__serializeBgOverlay()),fabric.util.populateWithProperties(this,e,c),d&&(this.setActiveGroup(new fabric.Group(d.getObjects(),{originX:"center",originY:"center"})),d.forEachObject(function(a){a.set("active",!0)}),this._currentTransform&&(this._currentTransform.target=this.getActiveGroup())),e},_toObjects:function(a,b){return this.getObjects().map(function(c){return this._toObject(c,a,b)},this)},_toObject:function(a,b,c){var d;this.includeDefaultValues||(d=a.includeDefaultValues,a.includeDefaultValues=!1);var e=a[b](c);return this.includeDefaultValues||(a.includeDefaultValues=d),e},__serializeBgOverlay:function(){var a={background:this.backgroundColor&&this.backgroundColor.toObject?this.backgroundColor.toObject():this.backgroundColor};return this.overlayColor&&(a.overlay=this.overlayColor.toObject?this.overlayColor.toObject():this.overlayColor),this.backgroundImage&&(a.backgroundImage=this.backgroundImage.toObject()),this.overlayImage&&(a.overlayImage=this.overlayImage.toObject()),a},svgViewportTransformation:!0,toSVG:function(a,b){a||(a={});var c=[];return this._setSVGPreamble(c,a),this._setSVGHeader(c,a),this._setSVGBgOverlayColor(c,"backgroundColor"),this._setSVGBgOverlayImage(c,"backgroundImage"),this._setSVGObjects(c,b),this._setSVGBgOverlayColor(c,"overlayColor"),this._setSVGBgOverlayImage(c,"overlayImage"),c.push("</svg>"),c.join("")},_setSVGPreamble:function(a,b){b.suppressPreamble||a.push('<?xml version="1.0" encoding="',b.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(a,b){var c,d,e;b.viewBox?(c=b.viewBox.width,d=b.viewBox.height):(c=this.width,d=this.height,this.svgViewportTransformation||(e=this.viewportTransform,c/=e[0],d/=e[3])),a.push("<svg ",'xmlns="http://www.w3.org/2000/svg" ','xmlns:xlink="http://www.w3.org/1999/xlink" ','version="1.1" ','width="',c,'" ','height="',d,'" ',this.backgroundColor&&!this.backgroundColor.toLive?'style="background-color: '+this.backgroundColor+'" ':null,b.viewBox?'viewBox="'+b.viewBox.x+" "+b.viewBox.y+" "+b.viewBox.width+" "+b.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(a,b){var c=this.getActiveGroup();c&&this.discardActiveGroup();for(var d=0,e=this.getObjects(),f=e.length;f>d;d++)a.push(e[d].toSVG(b));c&&(this.setActiveGroup(new fabric.Group(c.getObjects())),c.forEachObject(function(a){a.set("active",!0)}))},_setSVGBgOverlayImage:function(a,b){this[b]&&this[b].toSVG&&a.push(this[b].toSVG())},_setSVGBgOverlayColor:function(a,b){this[b]&&this[b].source?a.push('<rect x="',this[b].offsetX,'" y="',this[b].offsetY,'" ','width="',"repeat-y"===this[b].repeat||"no-repeat"===this[b].repeat?this[b].source.width:this.width,'" height="',"repeat-x"===this[b].repeat||"no-repeat"===this[b].repeat?this[b].source.height:this.height,'" fill="url(#'+b+'Pattern)"',"></rect>"):this[b]&&"overlayColor"===b&&a.push('<rect x="0" y="0" ','width="',this.width,'" height="',this.height,'" fill="',this[b],'"',"></rect>")},sendToBack:function(a){return c(this._objects,a),this._objects.unshift(a),this.renderAll&&this.renderAll()},bringToFront:function(a){return c(this._objects,a),this._objects.push(a),this.renderAll&&this.renderAll()},sendBackwards:function(a,b){var d=this._objects.indexOf(a);if(0!==d){var e=this._findNewLowerIndex(a,d,b);c(this._objects,a),this._objects.splice(e,0,a),this.renderAll&&this.renderAll()}return this},_findNewLowerIndex:function(a,b,c){var d;if(c){d=b;for(var e=b-1;e>=0;--e){var f=a.intersectsWithObject(this._objects[e])||a.isContainedWithinObject(this._objects[e])||this._objects[e].isContainedWithinObject(a);if(f){d=e;break}}}else d=b-1;return d},bringForward:function(a,b){var d=this._objects.indexOf(a);if(d!==this._objects.length-1){var e=this._findNewUpperIndex(a,d,b);c(this._objects,a),this._objects.splice(e,0,a),this.renderAll&&this.renderAll()}return this},_findNewUpperIndex:function(a,b,c){var d;if(c){d=b;for(var e=b+1;e<this._objects.length;++e){var f=a.intersectsWithObject(this._objects[e])||a.isContainedWithinObject(this._objects[e])||this._objects[e].isContainedWithinObject(a);if(f){d=e;break}}}else d=b+1;return d},moveTo:function(a,b){return c(this._objects,a),this._objects.splice(b,0,a),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+" }>"}}),a(fabric.StaticCanvas.prototype,fabric.Observable),a(fabric.StaticCanvas.prototype,fabric.Collection),a(fabric.StaticCanvas.prototype,fabric.DataURLExporter),a(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(a){var b=fabric.util.createCanvasElement();if(!b||!b.getContext)return null;var c=b.getContext("2d");if(!c)return null;switch(a){case"getImageData":return"undefined"!=typeof c.getImageData;case"setLineDash":return"undefined"!=typeof c.setLineDash;case"toDataURL":return"undefined"!=typeof b.toDataURL;case"toDataURLWithQuality":try{return b.toDataURL("image/jpeg",0),!0}catch(d){}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(a){return this.shadow=new fabric.Shadow(a),this},_setBrushStyles:function(){var a=this.canvas.contextTop;a.strokeStyle=this.color,a.lineWidth=this.width,a.lineCap=this.strokeLineCap,a.lineJoin=this.strokeLineJoin},_setShadow:function(){if(this.shadow){var a=this.canvas.contextTop;a.shadowColor=this.shadow.color,a.shadowBlur=this.shadow.blur,a.shadowOffsetX=this.shadow.offsetX,a.shadowOffsetY=this.shadow.offsetY}},_resetShadow:function(){var a=this.canvas.contextTop;a.shadowColor="",a.shadowBlur=a.shadowOffsetX=a.shadowOffsetY=0}}),function(){var a=fabric.util.array.min,b=fabric.util.array.max;fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{initialize:function(a){this.canvas=a,this._points=[]},onMouseDown:function(a){this._prepareForDrawing(a),this._captureDrawingPath(a),this._render()},onMouseMove:function(a){this._captureDrawingPath(a),this.canvas.clearContext(this.canvas.contextTop),this._render()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(a){var b=new fabric.Point(a.x,a.y);this._reset(),this._addPoint(b),this.canvas.contextTop.moveTo(b.x,b.y)},_addPoint:function(a){this._points.push(a)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(a){var b=new fabric.Point(a.x,a.y);this._addPoint(b)},_render:function(){var a=this.canvas.contextTop,b=this.canvas.viewportTransform,c=this._points[0],d=this._points[1];a.save(),a.transform(b[0],b[1],b[2],b[3],b[4],b[5]),a.beginPath(),2===this._points.length&&c.x===d.x&&c.y===d.y&&(c.x-=.5,d.x+=.5),a.moveTo(c.x,c.y);for(var e=1,f=this._points.length;f>e;e++){var g=c.midPointFrom(d);a.quadraticCurveTo(c.x,c.y,g.x,g.y),c=this._points[e],d=this._points[e+1]}a.lineTo(c.x,c.y),a.stroke(),a.restore()},_getSVGPathData:function(){return this.box=this.getPathBoundingBox(this._points),this.convertPointsToSVGPath(this._points,this.box.minX,this.box.minY)},getPathBoundingBox:function(c){for(var d=[],e=[],f=c[0],g=c[1],h=f,i=1,j=c.length;j>i;i++){var k=f.midPointFrom(g);d.push(h.x),d.push(k.x),e.push(h.y),e.push(k.y),f=c[i],g=c[i+1],h=k}return d.push(f.x),e.push(f.y),{minX:a(d),minY:a(e),maxX:b(d),maxY:b(e)}},convertPointsToSVGPath:function(a,b,c){var d=[],e=new fabric.Point(a[0].x-b,a[0].y-c),f=new fabric.Point(a[1].x-b,a[1].y-c);d.push("M ",a[0].x-b," ",a[0].y-c," ");for(var g=1,h=a.length;h>g;g++){var i=e.midPointFrom(f);d.push("Q ",e.x," ",e.y," ",i.x," ",i.y," "),e=new fabric.Point(a[g].x-b,a[g].y-c),g+1<a.length&&(f=new fabric.Point(a[g+1].x-b,a[g+1].y-c))}return d.push("L ",e.x," ",e.y," "),d},createPath:function(a){var b=new fabric.Path(a);return b.fill=null,b.stroke=this.color,b.strokeWidth=this.width,b.strokeLineCap=this.strokeLineCap,b.strokeLineJoin=this.strokeLineJoin,this.shadow&&(this.shadow.affectStroke=!0,b.setShadow(this.shadow)),b},_finalizeAndAddPath:function(){var a=this.canvas.contextTop;a.closePath();var b=this._getSVGPathData().join("");if("M 0 0 Q 0 0 0 0 L 0 0"===b)return void this.canvas.renderAll();var c=this.box.minX+(this.box.maxX-this.box.minX)/2,d=this.box.minY+(this.box.maxY-this.box.minY)/2;this.canvas.contextTop.arc(c,d,3,0,2*Math.PI,!1);var e=this.createPath(b);e.set({left:c,top:d,originX:"center",originY:"center"}),this.canvas.add(e),e.setCoords(),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderAll(),this.canvas.fire("path:created",{path:e})}})}(),fabric.CircleBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,initialize:function(a){this.canvas=a,this.points=[]},drawDot:function(a){var b=this.addPoint(a),c=this.canvas.contextTop,d=this.canvas.viewportTransform;c.save(),c.transform(d[0],d[1],d[2],d[3],d[4],d[5]),c.fillStyle=b.fill,c.beginPath(),c.arc(b.x,b.y,b.radius,0,2*Math.PI,!1),c.closePath(),c.fill(),c.restore()},onMouseDown:function(a){this.points.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.drawDot(a)},onMouseMove:function(a){this.drawDot(a)},onMouseUp:function(){var a=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var b=[],c=0,d=this.points.length;d>c;c++){var e=this.points[c],f=new fabric.Circle({radius:e.radius,left:e.x,top:e.y,originX:"center",originY:"center",fill:e.fill});this.shadow&&f.setShadow(this.shadow),b.push(f)}var g=new fabric.Group(b,{originX:"center",originY:"center"});g.canvas=this.canvas,this.canvas.add(g),this.canvas.fire("path:created",{path:g}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=a,this.canvas.renderAll()},addPoint:function(a){var b=new fabric.Point(a.x,a.y),c=fabric.util.getRandomInt(Math.max(0,this.width-20),this.width+20)/2,d=new fabric.Color(this.color).setAlpha(fabric.util.getRandomInt(0,100)/100).toRgba();return b.radius=c,b.fill=d,this.points.push(b),b}}),fabric.SprayBrush=fabric.util.createClass(fabric.BaseBrush,{width:10,density:20,dotWidth:1,dotWidthVariance:1,randomOpacity:!1,optimizeOverlapping:!0,initialize:function(a){this.canvas=a,this.sprayChunks=[]},onMouseDown:function(a){this.sprayChunks.length=0,this.canvas.clearContext(this.canvas.contextTop),this._setShadow(),this.addSprayChunk(a),this.render()},onMouseMove:function(a){this.addSprayChunk(a),this.render()},onMouseUp:function(){var a=this.canvas.renderOnAddRemove;this.canvas.renderOnAddRemove=!1;for(var b=[],c=0,d=this.sprayChunks.length;d>c;c++)for(var e=this.sprayChunks[c],f=0,g=e.length;g>f;f++){var h=new fabric.Rect({width:e[f].width,height:e[f].width,left:e[f].x+1,top:e[f].y+1,originX:"center",originY:"center",fill:this.color});this.shadow&&h.setShadow(this.shadow),b.push(h)}this.optimizeOverlapping&&(b=this._getOptimizedRects(b));var i=new fabric.Group(b,{originX:"center",originY:"center"});i.canvas=this.canvas,this.canvas.add(i),this.canvas.fire("path:created",{path:i}),this.canvas.clearContext(this.canvas.contextTop),this._resetShadow(),this.canvas.renderOnAddRemove=a,this.canvas.renderAll()},_getOptimizedRects:function(a){for(var b,c={},d=0,e=a.length;e>d;d++)b=a[d].left+""+a[d].top,c[b]||(c[b]=a[d]);var f=[];for(b in c)f.push(c[b]);return f},render:function(){var a=this.canvas.contextTop;a.fillStyle=this.color;var b=this.canvas.viewportTransform;a.save(),a.transform(b[0],b[1],b[2],b[3],b[4],b[5]);for(var c=0,d=this.sprayChunkPoints.length;d>c;c++){var e=this.sprayChunkPoints[c];"undefined"!=typeof e.opacity&&(a.globalAlpha=e.opacity),a.fillRect(e.x,e.y,e.width,e.width)}a.restore()},addSprayChunk:function(a){this.sprayChunkPoints=[];for(var b,c,d,e=this.width/2,f=0;f<this.density;f++){b=fabric.util.getRandomInt(a.x-e,a.x+e),c=fabric.util.getRandomInt(a.y-e,a.y+e),d=this.dotWidthVariance?fabric.util.getRandomInt(Math.max(1,this.dotWidth-this.dotWidthVariance),this.dotWidth+this.dotWidthVariance):this.dotWidth;var g=new fabric.Point(b,c);g.width=d,this.randomOpacity&&(g.opacity=fabric.util.getRandomInt(0,100)/100),this.sprayChunkPoints.push(g)}this.sprayChunks.push(this.sprayChunkPoints)}}),fabric.PatternBrush=fabric.util.createClass(fabric.PencilBrush,{getPatternSrc:function(){var a=20,b=5,c=fabric.document.createElement("canvas"),d=c.getContext("2d");return c.width=c.height=a+b,d.fillStyle=this.color,d.beginPath(),d.arc(a/2,a/2,a/2,0,2*Math.PI,!1),d.closePath(),d.fill(),c},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(a){var b=this.callSuper("createPath",a);return b.stroke=new fabric.Pattern({source:this.source||this.getPatternSrcFunction()}),b}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{toDataURL:function(a){a||(a={});var b=a.format||"png",c=a.quality||1,d=a.multiplier||1,e={left:a.left,top:a.top,width:a.width,height:a.height};return 1!==d?this.__toDataURLWithMultiplier(b,c,e,d):this.__toDataURL(b,c,e)},__toDataURL:function(a,b,c){this.renderAll(!0);var d=this.upperCanvasEl||this.lowerCanvasEl,e=this.__getCroppedCanvas(d,c);"jpg"===a&&(a="jpeg");var f=fabric.StaticCanvas.supports("toDataURLWithQuality")?(e||d).toDataURL("image/"+a,b):(e||d).toDataURL("image/"+a);return this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),e&&(e=null),f},__getCroppedCanvas:function(a,b){var c,d,e="left"in b||"top"in b||"width"in b||"height"in b;return e&&(c=fabric.util.createCanvasElement(),d=c.getContext("2d"),c.width=b.width||this.width,c.height=b.height||this.height,d.drawImage(a,-b.left||0,-b.top||0)),c},__toDataURLWithMultiplier:function(a,b,c,d){var e=this.getWidth(),f=this.getHeight(),g=e*d,h=f*d,i=this.getActiveObject(),j=this.getActiveGroup(),k=this.contextTop||this.contextContainer;d>1&&this.setWidth(g).setHeight(h),k.scale(d,d),c.left&&(c.left*=d),c.top&&(c.top*=d),c.width?c.width*=d:1>d&&(c.width=g),c.height?c.height*=d:1>d&&(c.height=h),j?this._tempRemoveBordersControlsFromGroup(j):i&&this.deactivateAll&&this.deactivateAll(),this.renderAll(!0);var l=this.__toDataURL(a,b,c);return this.width=e,this.height=f,k.scale(1/d,1/d),this.setWidth(e).setHeight(f),j?this._restoreBordersControlsOnGroup(j):i&&this.setActiveObject&&this.setActiveObject(i),this.contextTop&&this.clearContext(this.contextTop),this.renderAll(),l},toDataURLWithMultiplier:function(a,b,c){return this.toDataURL({format:a,multiplier:b,quality:c})},_tempRemoveBordersControlsFromGroup:function(a){a.origHasControls=a.hasControls,a.origBorderColor=a.borderColor,a.hasControls=!0,a.borderColor="rgba(0,0,0,0)",a.forEachObject(function(a){a.origBorderColor=a.borderColor,a.borderColor="rgba(0,0,0,0)"})},_restoreBordersControlsOnGroup:function(a){a.hideControls=a.origHideControls,a.borderColor=a.origBorderColor,a.forEachObject(function(a){a.borderColor=a.origBorderColor,delete a.origBorderColor})}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{loadFromDatalessJSON:function(a,b,c){return this.loadFromJSON(a,b,c)},loadFromJSON:function(a,b,c){if(a){var d="string"==typeof a?JSON.parse(a):a;this.clear();var e=this;return this._enlivenObjects(d.objects,function(){e._setBgOverlay(d,b)},c),this}},_setBgOverlay:function(a,b){var c=this,d={backgroundColor:!1,overlayColor:!1,backgroundImage:!1,overlayImage:!1};if(!(a.backgroundImage||a.overlayImage||a.background||a.overlay))return void(b&&b());var e=function(){d.backgroundImage&&d.overlayImage&&d.backgroundColor&&d.overlayColor&&(c.renderAll(),b&&b())};this.__setBgOverlay("backgroundImage",a.backgroundImage,d,e),this.__setBgOverlay("overlayImage",a.overlayImage,d,e),this.__setBgOverlay("backgroundColor",a.background,d,e),this.__setBgOverlay("overlayColor",a.overlay,d,e),e()},__setBgOverlay:function(a,b,c,d){var e=this;return b?void("backgroundImage"===a||"overlayImage"===a?fabric.Image.fromObject(b,function(b){e[a]=b,c[a]=!0,d&&d()}):this["set"+fabric.util.string.capitalize(a,!0)](b,function(){c[a]=!0,d&&d()})):void(c[a]=!0)},_enlivenObjects:function(a,b,c){var d=this;if(!a||0===a.length)return void(b&&b());var e=this.renderOnAddRemove;this.renderOnAddRemove=!1,fabric.util.enlivenObjects(a,function(a){a.forEach(function(a,b){d.insertAt(a,b,!0)}),d.renderOnAddRemove=e,b&&b()},null,c)},_toDataURL:function(a,b){this.clone(function(c){b(c.toDataURL(a))})},_toDataURLWithMultiplier:function(a,b,c){this.clone(function(d){c(d.toDataURLWithMultiplier(a,b))})},clone:function(a,b){var c=JSON.stringify(this.toJSON(b));this.cloneWithoutData(function(b){b.loadFromJSON(c,function(){a&&a(b)})})},cloneWithoutData:function(a){var b=fabric.document.createElement("canvas");b.width=this.getWidth(),b.height=this.getHeight();var c=new fabric.Canvas(b);c.clipTo=this.clipTo,this.backgroundImage?(c.setBackgroundImage(this.backgroundImage.src,function(){c.renderAll(),a&&a(c)}),c.backgroundImageOpacity=this.backgroundImageOpacity,c.backgroundImageStretch=this.backgroundImageStretch):a&&a(c)}}),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.toFixed,e=b.util.string.capitalize,f=b.util.degreesToRadians,g=b.StaticCanvas.supports("setLineDash");b.Object||(b.Object=b.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(a){a&&this.setOptions(a)},_initGradient:function(a){!a.fill||!a.fill.colorStops||a.fill instanceof b.Gradient||this.set("fill",new b.Gradient(a.fill))},_initPattern:function(a){!a.fill||!a.fill.source||a.fill instanceof b.Pattern||this.set("fill",new b.Pattern(a.fill)),!a.stroke||!a.stroke.source||a.stroke instanceof b.Pattern||this.set("stroke",new b.Pattern(a.stroke))},_initClipping:function(a){if(a.clipTo&&"string"==typeof a.clipTo){var c=b.util.getFunctionBody(a.clipTo);"undefined"!=typeof c&&(this.clipTo=new Function("ctx",c))}},setOptions:function(a){for(var b in a)this.set(b,a[b]);this._initGradient(a),this._initPattern(a),this._initClipping(a)},transform:function(a,b){this.group&&this.group.transform(a,b),a.globalAlpha=this.opacity;var c=b?this._getLeftTopCoords():this.getCenterPoint();a.translate(c.x,c.y),a.rotate(f(this.angle)),a.scale(this.scaleX*(this.flipX?-1:1),this.scaleY*(this.flipY?-1:1))},toObject:function(a){var c=b.Object.NUM_FRACTION_DIGITS,e={type:this.type,originX:this.originX,originY:this.originY,left:d(this.left,c),top:d(this.top,c),width:d(this.width,c),height:d(this.height,c),fill:this.fill&&this.fill.toObject?this.fill.toObject():this.fill,stroke:this.stroke&&this.stroke.toObject?this.stroke.toObject():this.stroke,strokeWidth:d(this.strokeWidth,c),strokeDashArray:this.strokeDashArray,strokeLineCap:this.strokeLineCap,strokeLineJoin:this.strokeLineJoin,strokeMiterLimit:d(this.strokeMiterLimit,c),scaleX:d(this.scaleX,c),scaleY:d(this.scaleY,c),angle:d(this.getAngle(),c),flipX:this.flipX,flipY:this.flipY,opacity:d(this.opacity,c),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||(e=this._removeDefaultValues(e)),b.util.populateWithProperties(this,e,a),e},toDatalessObject:function(a){return this.toObject(a)},_removeDefaultValues:function(a){var c=b.util.getKlass(a.type).prototype,d=c.stateProperties;return d.forEach(function(b){a[b]===c[b]&&delete a[b]}),a},toString:function(){return"#<fabric."+e(this.type)+">"},get:function(a){return this[a]},_setObject:function(a){for(var b in a)this._set(b,a[b])},set:function(a,b){return"object"==typeof a?this._setObject(a):"function"==typeof b&&"clipTo"!==a?this._set(a,b(this.get(a))):this._set(a,b),this},_set:function(a,c){var e="scaleX"===a||"scaleY"===a;return e&&(c=this._constrainScale(c)),"scaleX"===a&&0>c?(this.flipX=!this.flipX,c*=-1):"scaleY"===a&&0>c?(this.flipY=!this.flipY,c*=-1):"width"===a||"height"===a?this.minScaleLimit=d(Math.min(.1,1/Math.max(this.width,this.height)),2):"shadow"!==a||!c||c instanceof b.Shadow||(c=new b.Shadow(c)),this[a]=c,this},toggle:function(a){var b=this.get(a);return"boolean"==typeof b&&this.set(a,!b),this},setSourcePath:function(a){return this.sourcePath=a,this},getViewportTransform:function(){return this.canvas&&this.canvas.viewportTransform?this.canvas.viewportTransform:[1,0,0,1,0,0]},render:function(a,c){if(0!==this.width&&0!==this.height&&this.visible){if(a.save(),this._setupFillRule(a),this._transform(a,c),this._setStrokeStyles(a),this._setFillStyles(a),this.group&&"path-group"===this.group.type){a.translate(-this.group.width/2,-this.group.height/2);var d=this.transformMatrix;d&&a.transform.apply(a,d)}a.globalAlpha=this.group?a.globalAlpha*this.opacity:this.opacity,this._setShadow(a),this.clipTo&&b.util.clipContext(this,a),this._render(a,c),this.clipTo&&a.restore(),this._removeShadow(a),this._restoreFillRule(a),a.restore()}},_transform:function(a,b){var c=this.transformMatrix;c&&!this.group&&a.setTransform.apply(a,c),b||this.transform(a)},_setStrokeStyles:function(a){this.stroke&&(a.lineWidth=this.strokeWidth,a.lineCap=this.strokeLineCap,a.lineJoin=this.strokeLineJoin,a.miterLimit=this.strokeMiterLimit,a.strokeStyle=this.stroke.toLive?this.stroke.toLive(a):this.stroke)},_setFillStyles:function(a){this.fill&&(a.fillStyle=this.fill.toLive?this.fill.toLive(a):this.fill)},_renderControls:function(a,c){var d=this.getViewportTransform();if(a.save(),this.active&&!c){var e;this.group&&(e=b.util.transformPoint(this.group.getCenterPoint(),d),a.translate(e.x,e.y),a.rotate(f(this.group.angle))),e=b.util.transformPoint(this.getCenterPoint(),d,null!=this.group),this.group&&(e.x*=this.group.scaleX,e.y*=this.group.scaleY),a.translate(e.x,e.y),a.rotate(f(this.angle)),this.drawBorders(a),this.drawControls(a)}a.restore()},_setShadow:function(a){this.shadow&&(a.shadowColor=this.shadow.color,a.shadowBlur=this.shadow.blur,a.shadowOffsetX=this.shadow.offsetX,a.shadowOffsetY=this.shadow.offsetY)
+},_removeShadow:function(a){this.shadow&&(a.shadowColor="",a.shadowBlur=a.shadowOffsetX=a.shadowOffsetY=0)},_renderFill:function(a){if(this.fill){if(a.save(),this.fill.toLive&&a.translate(-this.width/2+this.fill.offsetX||0,-this.height/2+this.fill.offsetY||0),this.fill.gradientTransform){var b=this.fill.gradientTransform;a.transform.apply(a,b)}"destination-over"===this.fillRule?a.fill("evenodd"):a.fill(),a.restore(),this.shadow&&!this.shadow.affectStroke&&this._removeShadow(a)}},_renderStroke:function(a){if(this.stroke&&0!==this.strokeWidth){if(a.save(),this.strokeDashArray)1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),g?(a.setLineDash(this.strokeDashArray),this._stroke&&this._stroke(a)):this._renderDashedStroke&&this._renderDashedStroke(a),a.stroke();else{if(this.stroke.gradientTransform){var b=this.stroke.gradientTransform;a.transform.apply(a,b)}this._stroke?this._stroke(a):a.stroke()}this._removeShadow(a),a.restore()}},clone:function(a,c){return this.constructor.fromObject?this.constructor.fromObject(this.toObject(c),a):new b.Object(this.toObject(c))},cloneAsImage:function(a){var c=this.toDataURL();return b.util.loadImage(c,function(c){a&&a(new b.Image(c))}),this},toDataURL:function(a){a||(a={});var c=b.util.createCanvasElement(),d=this.getBoundingRect();c.width=d.width,c.height=d.height,b.util.wrapElement(c,"div");var e=new b.Canvas(c);"jpg"===a.format&&(a.format="jpeg"),"jpeg"===a.format&&(e.backgroundColor="#fff");var f={active:this.get("active"),left:this.getLeft(),top:this.getTop()};this.set("active",!1),this.setPositionByOrigin(new b.Point(c.width/2,c.height/2),"center","center");var g=this.canvas;e.add(this);var h=e.toDataURL(a);return this.set(f).setCoords(),this.canvas=g,e.dispose(),e=null,h},isType:function(a){return this.type===a},complexity:function(){return 0},toJSON:function(a){return this.toObject(a)},setGradient:function(a,c){c||(c={});var d={colorStops:[]};d.type=c.type||(c.r1||c.r2?"radial":"linear"),d.coords={x1:c.x1,y1:c.y1,x2:c.x2,y2:c.y2},(c.r1||c.r2)&&(d.coords.r1=c.r1,d.coords.r2=c.r2);for(var e in c.colorStops){var f=new b.Color(c.colorStops[e]);d.colorStops.push({offset:e,color:f.toRgb(),opacity:f.getAlpha()})}return this.set(a,b.Gradient.forObject(this,d))},setPatternFill:function(a){return this.set("fill",new b.Pattern(a))},setShadow:function(a){return this.set("shadow",a?new b.Shadow(a):null)},setColor:function(a){return this.set("fill",a),this},setAngle:function(a){var b=("center"!==this.originX||"center"!==this.originY)&&this.centeredRotation;return b&&this._setOriginToCenter(),this.set("angle",a),b&&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(a,b){b=b||this.canvas.getPointer(a);var c=this.translateToOriginPoint(this.getCenterPoint(),"left","top");return{x:b.x-c.x,y:b.y-c.y}},_setupFillRule:function(a){this.fillRule&&(this._prevFillRule=a.globalCompositeOperation,a.globalCompositeOperation=this.fillRule)},_restoreFillRule:function(a){this.fillRule&&this._prevFillRule&&(a.globalCompositeOperation=this._prevFillRule)}}),b.util.createAccessors(b.Object),b.Object.prototype.rotate=b.Object.prototype.setAngle,c(b.Object.prototype,b.Observable),b.Object.NUM_FRACTION_DIGITS=2,b.Object.__uid=0)}("undefined"!=typeof exports?exports:this),function(){var a=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{translateToCenterPoint:function(b,c,d){var e=b.x,f=b.y,g=this.stroke?this.strokeWidth:0;return"left"===c?e=b.x+(this.getWidth()+g*this.scaleX)/2:"right"===c&&(e=b.x-(this.getWidth()+g*this.scaleX)/2),"top"===d?f=b.y+(this.getHeight()+g*this.scaleY)/2:"bottom"===d&&(f=b.y-(this.getHeight()+g*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(e,f),b,a(this.angle))},translateToOriginPoint:function(b,c,d){var e=b.x,f=b.y,g=this.stroke?this.strokeWidth:0;return"left"===c?e=b.x-(this.getWidth()+g*this.scaleX)/2:"right"===c&&(e=b.x+(this.getWidth()+g*this.scaleX)/2),"top"===d?f=b.y-(this.getHeight()+g*this.scaleY)/2:"bottom"===d&&(f=b.y+(this.getHeight()+g*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(e,f),b,a(this.angle))},getCenterPoint:function(){var a=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(a,this.originX,this.originY)},getPointByOrigin:function(a,b){var c=this.getCenterPoint();return this.translateToOriginPoint(c,a,b)},toLocalPoint:function(b,c,d){var e,f,g=this.getCenterPoint(),h=this.stroke?this.strokeWidth:0;return c&&d?(e="left"===c?g.x-(this.getWidth()+h*this.scaleX)/2:"right"===c?g.x+(this.getWidth()+h*this.scaleX)/2:g.x,f="top"===d?g.y-(this.getHeight()+h*this.scaleY)/2:"bottom"===d?g.y+(this.getHeight()+h*this.scaleY)/2:g.y):(e=this.left,f=this.top),fabric.util.rotatePoint(new fabric.Point(b.x,b.y),g,-a(this.angle)).subtractEquals(new fabric.Point(e,f))},setPositionByOrigin:function(a,b,c){var d=this.translateToCenterPoint(a,b,c),e=this.translateToOriginPoint(d,this.originX,this.originY);this.set("left",e.x),this.set("top",e.y)},adjustPosition:function(b){var c=a(this.angle),d=this.getWidth()/2,e=Math.cos(c)*d,f=Math.sin(c)*d,g=this.getWidth(),h=Math.cos(c)*g,i=Math.sin(c)*g;"center"===this.originX&&"left"===b||"right"===this.originX&&"center"===b?(this.left-=e,this.top-=f):"left"===this.originX&&"center"===b||"center"===this.originX&&"right"===b?(this.left+=e,this.top+=f):"left"===this.originX&&"right"===b?(this.left+=h,this.top+=i):"right"===this.originX&&"left"===b&&(this.left-=h,this.top-=i),this.setCoords(),this.originX=b},_setOriginToCenter:function(){this._originalOriginX=this.originX,this._originalOriginY=this.originY;var a=this.getCenterPoint();this.originX="center",this.originY="center",this.left=a.x,this.top=a.y},_resetOrigin:function(){var a=this.translateToOriginPoint(this.getCenterPoint(),this._originalOriginX,this._originalOriginY);this.originX=this._originalOriginX,this.originY=this._originalOriginY,this.left=a.x,this.top=a.y,this._originalOriginX=null,this._originalOriginY=null},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","center")}})}(),function(){var a=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{oCoords:null,intersectsWithRect:function(a,b){var c=this.oCoords,d=new fabric.Point(c.tl.x,c.tl.y),e=new fabric.Point(c.tr.x,c.tr.y),f=new fabric.Point(c.bl.x,c.bl.y),g=new fabric.Point(c.br.x,c.br.y),h=fabric.Intersection.intersectPolygonRectangle([d,e,g,f],a,b);return"Intersection"===h.status},intersectsWithObject:function(a){function b(a){return{tl:new fabric.Point(a.tl.x,a.tl.y),tr:new fabric.Point(a.tr.x,a.tr.y),bl:new fabric.Point(a.bl.x,a.bl.y),br:new fabric.Point(a.br.x,a.br.y)}}var c=b(this.oCoords),d=b(a.oCoords),e=fabric.Intersection.intersectPolygonPolygon([c.tl,c.tr,c.br,c.bl],[d.tl,d.tr,d.br,d.bl]);return"Intersection"===e.status},isContainedWithinObject:function(a){var b=a.getBoundingRect(),c=new fabric.Point(b.left,b.top),d=new fabric.Point(b.left+b.width,b.top+b.height);return this.isContainedWithinRect(c,d)},isContainedWithinRect:function(a,b){var c=this.getBoundingRect();return c.left>=a.x&&c.left+c.width<=b.x&&c.top>=a.y&&c.top+c.height<=b.y},containsPoint:function(a){var b=this._getImageLines(this.oCoords),c=this._findCrossPoints(a,b);return 0!==c&&c%2===1},_getImageLines:function(a){return{topline:{o:a.tl,d:a.tr},rightline:{o:a.tr,d:a.br},bottomline:{o:a.br,d:a.bl},leftline:{o:a.bl,d:a.tl}}},_findCrossPoints:function(a,b){var c,d,e,f,g,h,i,j=0;for(var k in b)if(i=b[k],!(i.o.y<a.y&&i.d.y<a.y||i.o.y>=a.y&&i.d.y>=a.y||(i.o.x===i.d.x&&i.o.x>=a.x?(g=i.o.x,h=a.y):(c=0,d=(i.d.y-i.o.y)/(i.d.x-i.o.x),e=a.y-c*a.x,f=i.o.y-d*i.o.x,g=-(e-f)/(c-d),h=e+c*g),g>=a.x&&(j+=1),2!==j)))break;return j},getBoundingRectWidth:function(){return this.getBoundingRect().width},getBoundingRectHeight:function(){return this.getBoundingRect().height},getBoundingRect:function(){this.oCoords||this.setCoords();var a=[this.oCoords.tl.x,this.oCoords.tr.x,this.oCoords.br.x,this.oCoords.bl.x],b=fabric.util.array.min(a),c=fabric.util.array.max(a),d=Math.abs(b-c),e=[this.oCoords.tl.y,this.oCoords.tr.y,this.oCoords.br.y,this.oCoords.bl.y],f=fabric.util.array.min(e),g=fabric.util.array.max(e),h=Math.abs(f-g);return{left:b,top:f,width:d,height:h}},getWidth:function(){return this.width*this.scaleX},getHeight:function(){return this.height*this.scaleY},_constrainScale:function(a){return Math.abs(a)<this.minScaleLimit?0>a?-this.minScaleLimit:this.minScaleLimit:a},scale:function(a){return a=this._constrainScale(a),0>a&&(this.flipX=!this.flipX,this.flipY=!this.flipY,a*=-1),this.scaleX=a,this.scaleY=a,this.setCoords(),this},scaleToWidth:function(a){var b=this.getBoundingRectWidth()/this.getWidth();return this.scale(a/this.width/b)},scaleToHeight:function(a){var b=this.getBoundingRectHeight()/this.getHeight();return this.scale(a/this.height/b)},setCoords:function(){var b=this.strokeWidth>1?this.strokeWidth:0,c=a(this.angle),d=this.getViewportTransform(),e=function(a){return fabric.util.transformPoint(a,d)},f=this.width,g=this.height,h="round"===this.strokeLineCap||"square"===this.strokeLineCap,i="line"===this.type&&1===this.width,j="line"===this.type&&1===this.height,k=h&&j||"line"!==this.type,l=h&&i||"line"!==this.type;i?f=b:j&&(g=b),k&&(f+=b),l&&(g+=b),this.currentWidth=f*this.scaleX,this.currentHeight=g*this.scaleY,this.currentWidth<0&&(this.currentWidth=Math.abs(this.currentWidth));var m=Math.sqrt(Math.pow(this.currentWidth/2,2)+Math.pow(this.currentHeight/2,2)),n=Math.atan(isFinite(this.currentHeight/this.currentWidth)?this.currentHeight/this.currentWidth:0),o=Math.cos(n+c)*m,p=Math.sin(n+c)*m,q=Math.sin(c),r=Math.cos(c),s=this.getCenterPoint(),t=new fabric.Point(this.currentWidth,this.currentHeight),u=new fabric.Point(s.x-o,s.y-p),v=new fabric.Point(u.x+t.x*r,u.y+t.x*q),w=new fabric.Point(u.x-t.y*q,u.y+t.y*r),x=new fabric.Point(u.x+t.x/2*r,u.y+t.x/2*q),y=e(u),z=e(v),A=e(new fabric.Point(v.x-t.y*q,v.y+t.y*r)),B=e(w),C=e(new fabric.Point(u.x-t.y/2*q,u.y+t.y/2*r)),D=e(x),E=e(new fabric.Point(v.x-t.y/2*q,v.y+t.y/2*r)),F=e(new fabric.Point(w.x+t.x/2*r,w.y+t.x/2*q)),G=e(new fabric.Point(x.x,x.y)),H=Math.cos(n+c)*this.padding*Math.sqrt(2),I=Math.sin(n+c)*this.padding*Math.sqrt(2);return y=y.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:y,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(a){return this.group?fabric.StaticCanvas.prototype.sendBackwards.call(this.group,this,a):this.canvas.sendBackwards(this,a),this},bringForward:function(a){return this.group?fabric.StaticCanvas.prototype.bringForward.call(this.group,this,a):this.canvas.bringForward(this,a),this},moveTo:function(a){return this.group?fabric.StaticCanvas.prototype.moveTo.call(this.group,this,a):this.canvas.moveTo(this,a),this}}),fabric.util.object.extend(fabric.Object.prototype,{getSvgStyles:function(){var a=this.fill?this.fill.toLive?"url(#SVGID_"+this.fill.id+")":this.fill:"none",b="destination-over"===this.fillRule?"evenodd":this.fillRule,c=this.stroke?this.stroke.toLive?"url(#SVGID_"+this.stroke.id+")":this.stroke:"none",d=this.strokeWidth?this.strokeWidth:"0",e=this.strokeDashArray?this.strokeDashArray.join(" "):"",f=this.strokeLineCap?this.strokeLineCap:"butt",g=this.strokeLineJoin?this.strokeLineJoin:"miter",h=this.strokeMiterLimit?this.strokeMiterLimit:"4",i="undefined"!=typeof this.opacity?this.opacity:"1",j=this.visible?"":" visibility: hidden;",k=this.shadow&&"text"!==this.type?"filter: url(#SVGID_"+this.shadow.id+");":"";return["stroke: ",c,"; ","stroke-width: ",d,"; ","stroke-dasharray: ",e,"; ","stroke-linecap: ",f,"; ","stroke-linejoin: ",g,"; ","stroke-miterlimit: ",h,"; ","fill: ",a,"; ","fill-rule: ",b,"; ","opacity: ",i,";",k,j].join("")},getSvgTransform:function(){if(this.group)return"";var a=fabric.util.toFixed,b=this.getAngle(),c=!this.canvas||this.canvas.svgViewportTransformation?this.getViewportTransform():[1,0,0,1,0,0],d=fabric.util.transformPoint(this.getCenterPoint(),c),e=fabric.Object.NUM_FRACTION_DIGITS,f="path-group"===this.type?"":"translate("+a(d.x,e)+" "+a(d.y,e)+")",g=0!==b?" rotate("+a(b,e)+")":"",h=1===this.scaleX&&1===this.scaleY&&1===c[0]&&1===c[3]?"":" scale("+a(this.scaleX*c[0],e)+" "+a(this.scaleY*c[3],e)+")",i="path-group"===this.type?this.width*c[0]:0,j=this.flipX?" matrix(-1 0 0 1 "+i+" 0) ":"",k="path-group"===this.type?this.height*c[3]:0,l=this.flipY?" matrix(1 0 0 -1 0 "+k+")":"";return[f,g,h,j,l].join("")},getSvgTransformMatrix:function(){return this.transformMatrix?" matrix("+this.transformMatrix.join(" ")+")":""},_createBaseSVGMarkup:function(){var a=[];return this.fill&&this.fill.toLive&&a.push(this.fill.toSVG(this,!1)),this.stroke&&this.stroke.toLive&&a.push(this.stroke.toSVG(this,!1)),this.shadow&&a.push(this.shadow.toSVG(this)),a}}),fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(){return this.stateProperties.some(function(a){return this.get(a)!==this.originalState[a]},this)},saveState:function(a){return this.stateProperties.forEach(function(a){this.originalState[a]=this.get(a)},this),a&&a.stateProperties&&a.stateProperties.forEach(function(a){this.originalState[a]=this.get(a)},this),this},setupState:function(){return this.originalState={},this.saveState(),this}}),function(a){"use strict";function b(a,b){var c=a.origin,d=a.axis1,e=a.axis2,f=a.dimension,g=b.nearest,h=b.center,i=b.farthest;return function(){switch(this.get(c)){case g:return Math.min(this.get(d),this.get(e));case h:return Math.min(this.get(d),this.get(e))+.5*this.get(f);case i:return Math.max(this.get(d),this.get(e))}}}var c=a.fabric||(a.fabric={}),d=c.util.object.extend,e={x1:1,x2:1,y1:1,y2:1},f=c.StaticCanvas.supports("setLineDash");return c.Line?void c.warn("fabric.Line is already defined"):(c.Line=c.util.createClass(c.Object,{type:"line",x1:0,y1:0,x2:0,y2:0,initialize:function(a,b){b=b||{},a||(a=[0,0,0,0]),this.callSuper("initialize",b),this.set("x1",a[0]),this.set("y1",a[1]),this.set("x2",a[2]),this.set("y2",a[3]),this._setWidthHeight(b)},_setWidthHeight:function(a){a||(a={}),this.width=Math.abs(this.x2-this.x1)||1,this.height=Math.abs(this.y2-this.y1)||1,this.left="left"in a?a.left:this._getLeftToOriginX(),this.top="top"in a?a.top:this._getTopToOriginY()},_set:function(a,b){return this[a]=b,"undefined"!=typeof e[a]&&this._setWidthHeight(),this},_getLeftToOriginX:b({origin:"originX",axis1:"x1",axis2:"x2",dimension:"width"},{nearest:"left",center:"center",farthest:"right"}),_getTopToOriginY:b({origin:"originY",axis1:"y1",axis2:"y2",dimension:"height"},{nearest:"top",center:"center",farthest:"bottom"}),_render:function(a,b){if(a.beginPath(),b){var c=this.getCenterPoint();a.translate(c.x,c.y)}if(!this.strokeDashArray||this.strokeDashArray&&f){var d=this.x1<=this.x2?-1:1,e=this.y1<=this.y2?-1:1;a.moveTo(1===this.width?0:d*this.width/2,1===this.height?0:e*this.height/2),a.lineTo(1===this.width?0:-1*d*this.width/2,1===this.height?0:-1*e*this.height/2)}a.lineWidth=this.strokeWidth;var g=a.strokeStyle;a.strokeStyle=this.stroke||a.fillStyle,this.stroke&&this._renderStroke(a),a.strokeStyle=g},_renderDashedStroke:function(a){var b=this.x1<=this.x2?-1:1,d=this.y1<=this.y2?-1:1,e=1===this.width?0:b*this.width/2,f=1===this.height?0:d*this.height/2;a.beginPath(),c.util.drawDashedLine(a,e,f,-e,-f,this.strokeDashArray),a.closePath()},toObject:function(a){return d(this.callSuper("toObject",a),{x1:this.get("x1"),y1:this.get("y1"),x2:this.get("x2"),y2:this.get("y2")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c="";if(!this.group){var d=-this.width/2-(this.x1>this.x2?this.x2:this.x1),e=-this.height/2-(this.y1>this.y2?this.y2:this.y1);c="translate("+d+", "+e+") "}return b.push("<line ",'x1="',this.x1,'" y1="',this.y1,'" x2="',this.x2,'" y2="',this.y2,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),c,this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),c.Line.ATTRIBUTE_NAMES=c.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),c.Line.fromElement=function(a,b){var e=c.parseAttributes(a,c.Line.ATTRIBUTE_NAMES),f=[e.x1||0,e.y1||0,e.x2||0,e.y2||0];return new c.Line(f,d(e,b))},void(c.Line.fromObject=function(a){var b=[a.x1,a.y1,a.x2,a.y2];return new c.Line(b,a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){return"radius"in a&&a.radius>0}var c=a.fabric||(a.fabric={}),d=2*Math.PI,e=c.util.object.extend;return c.Circle?void c.warn("fabric.Circle is already defined."):(c.Circle=c.util.createClass(c.Object,{type:"circle",radius:0,initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("radius",a.radius||0)},_set:function(a,b){return this.callSuper("_set",a,b),"radius"===a&&this.setRadius(b),this},toObject:function(a){return e(this.callSuper("toObject",a),{radius:this.get("radius")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=0,d=0;return this.group&&(c=this.left+this.radius,d=this.top+this.radius),b.push("<circle ",'cx="'+c+'" cy="'+d+'" ','r="',this.radius,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},_render:function(a,b){a.beginPath(),a.arc(b?this.left+this.radius:0,b?this.top+this.radius:0,this.radius,0,d,!1),this._renderFill(a),this._renderStroke(a)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(a){this.radius=a,this.set("width",2*a).set("height",2*a)},complexity:function(){return 1}}),c.Circle.ATTRIBUTE_NAMES=c.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),c.Circle.fromElement=function(a,d){d||(d={});var f=c.parseAttributes(a,c.Circle.ATTRIBUTE_NAMES);if(!b(f))throw new Error("value of `r` attribute is required and can not be negative");f.left=f.left||0,f.top=f.top||0;var g=new c.Circle(e(f,d));return g.left-=g.radius,g.top-=g.radius,g},void(c.Circle.fromObject=function(a){return new c.Circle(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});return b.Triangle?void b.warn("fabric.Triangle is already defined"):(b.Triangle=b.util.createClass(b.Object,{type:"triangle",initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("width",a.width||100).set("height",a.height||100)},_render:function(a){var b=this.width/2,c=this.height/2;a.beginPath(),a.moveTo(-b,c),a.lineTo(0,-c),a.lineTo(b,c),a.closePath(),this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c=this.width/2,d=this.height/2;a.beginPath(),b.util.drawDashedLine(a,-c,d,0,-d,this.strokeDashArray),b.util.drawDashedLine(a,0,-d,c,d,this.strokeDashArray),b.util.drawDashedLine(a,c,d,-c,d,this.strokeDashArray),a.closePath()},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=this.width/2,d=this.height/2,e=[-c+" "+d,"0 "+-d,c+" "+d].join(",");return b.push("<polygon ",'points="',e,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),'"/>'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),void(b.Triangle.fromObject=function(a){return new b.Triangle(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=2*Math.PI,d=b.util.object.extend;return b.Ellipse?void b.warn("fabric.Ellipse is already defined."):(b.Ellipse=b.util.createClass(b.Object,{type:"ellipse",rx:0,ry:0,initialize:function(a){a=a||{},this.callSuper("initialize",a),this.set("rx",a.rx||0),this.set("ry",a.ry||0),this.set("width",2*this.get("rx")),this.set("height",2*this.get("ry"))},toObject:function(a){return d(this.callSuper("toObject",a),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=0,d=0;return this.group&&(c=this.left+this.rx,d=this.top+this.ry),b.push("<ellipse ",'cx="',c,'" cy="',d,'" ','rx="',this.rx,'" ry="',this.ry,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},_render:function(a,b){a.beginPath(),a.save(),a.transform(1,0,0,this.ry/this.rx,0,0),a.arc(b?this.left+this.rx:0,b?(this.top+this.ry)*this.rx/this.ry:0,this.rx,0,c,!1),a.restore(),this._renderFill(a),this._renderStroke(a)},complexity:function(){return 1}}),b.Ellipse.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),b.Ellipse.fromElement=function(a,c){c||(c={});var e=b.parseAttributes(a,b.Ellipse.ATTRIBUTE_NAMES);e.left=e.left||0,e.top=e.top||0;var f=new b.Ellipse(d(e,c));return f.top-=f.ry,f.left-=f.rx,f},void(b.Ellipse.fromObject=function(a){return new b.Ellipse(a)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;if(b.Rect)return void console.warn("fabric.Rect is already defined");var d=b.Object.prototype.stateProperties.concat();d.push("rx","ry","x","y"),b.Rect=b.util.createClass(b.Object,{stateProperties:d,type:"rect",rx:0,ry:0,strokeDashArray:null,initialize:function(a){a=a||{},this.callSuper("initialize",a),this._initRxRy()},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(a,b){if(1===this.width&&1===this.height)return void a.fillRect(0,0,1,1);var c=this.rx?Math.min(this.rx,this.width/2):0,d=this.ry?Math.min(this.ry,this.height/2):0,e=this.width,f=this.height,g=b?this.left:-this.width/2,h=b?this.top:-this.height/2,i=0!==c||0!==d,j=.4477152502;a.beginPath(),a.moveTo(g+c,h),a.lineTo(g+e-c,h),i&&a.bezierCurveTo(g+e-j*c,h,g+e,h+j*d,g+e,h+d),a.lineTo(g+e,h+f-d),i&&a.bezierCurveTo(g+e,h+f-j*d,g+e-j*c,h+f,g+e-c,h+f),a.lineTo(g+c,h+f),i&&a.bezierCurveTo(g+j*c,h+f,g,h+f-j*d,g,h+f-d),a.lineTo(g,h+d),i&&a.bezierCurveTo(g,h+j*d,g+j*c,h,g+c,h),a.closePath(),this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c=-this.width/2,d=-this.height/2,e=this.width,f=this.height;a.beginPath(),b.util.drawDashedLine(a,c,d,c+e,d,this.strokeDashArray),b.util.drawDashedLine(a,c+e,d,c+e,d+f,this.strokeDashArray),b.util.drawDashedLine(a,c+e,d+f,c,d+f,this.strokeDashArray),b.util.drawDashedLine(a,c,d+f,c,d,this.strokeDashArray),a.closePath()},toObject:function(a){var b=c(this.callSuper("toObject",a),{rx:this.get("rx")||0,ry:this.get("ry")||0});return this.includeDefaultValues||this._removeDefaultValues(b),b},toSVG:function(a){var b=this._createBaseSVGMarkup(),c=this.left,d=this.top;return this.group||(c=-this.width/2,d=-this.height/2),b.push("<rect ",'x="',c,'" y="',d,'" rx="',this.get("rx"),'" ry="',this.get("ry"),'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'"/>\n'),a?a(b.join("")):b.join("")},complexity:function(){return 1}}),b.Rect.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),b.Rect.fromElement=function(a,d){if(!a)return null;d=d||{};var e=b.parseAttributes(a,b.Rect.ATTRIBUTE_NAMES);return e.left=e.left||0,e.top=e.top||0,new b.Rect(c(d?b.util.object.clone(d):{},e))},b.Rect.fromObject=function(a){return new b.Rect(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.toFixed;return b.Polyline?void b.warn("fabric.Polyline is already defined"):(b.Polyline=b.util.createClass(b.Object,{type:"polyline",points:null,initialize:function(a,b){b=b||{},this.set("points",a),this.callSuper("initialize",b),this._calcDimensions()},_calcDimensions:function(){return b.Polygon.prototype._calcDimensions.call(this)},_applyPointOffset:function(){return b.Polygon.prototype._applyPointOffset.call(this)},toObject:function(a){return b.Polygon.prototype.toObject.call(this,a)},toSVG:function(a){for(var b=[],d=this._createBaseSVGMarkup(),e=0,f=this.points.length;f>e;e++)b.push(c(this.points[e].x,2),",",c(this.points[e].y,2)," ");return d.push("<polyline ",'points="',b.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(d.join("")):d.join("")},_render:function(a){var b;a.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),a.moveTo(this.points[0].x,this.points[0].y);for(var c=0,d=this.points.length;d>c;c++)b=this.points[c],a.lineTo(b.x,b.y);this._renderFill(a),this._renderStroke(a)},_renderDashedStroke:function(a){var c,d;a.beginPath();for(var e=0,f=this.points.length;f>e;e++)c=this.points[e],d=this.points[e+1]||c,b.util.drawDashedLine(a,c.x,c.y,d.x,d.y,this.strokeDashArray)},complexity:function(){return this.get("points").length}}),b.Polyline.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat(),b.Polyline.fromElement=function(a,c){if(!a)return null;c||(c={});var d=b.parsePointsAttribute(a.getAttribute("points")),e=b.parseAttributes(a,b.Polyline.ATTRIBUTE_NAMES);return null===d?null:new b.Polyline(d,b.util.object.extend(e,c))},void(b.Polyline.fromObject=function(a){var c=a.points;return new b.Polyline(c,a,!0)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.min,e=b.util.array.max,f=b.util.toFixed;return b.Polygon?void b.warn("fabric.Polygon is already defined"):(b.Polygon=b.util.createClass(b.Object,{type:"polygon",points:null,initialize:function(a,b){b=b||{},this.points=a,this.callSuper("initialize",b),this._calcDimensions()},_calcDimensions:function(){var a=this.points,b=d(a,"x"),c=d(a,"y"),f=e(a,"x"),g=e(a,"y");this.width=f-b||1,this.height=g-c||1,this.left=b,this.top=c},_applyPointOffset:function(){this.points.forEach(function(a){a.x-=this.left+this.width/2,a.y-=this.top+this.height/2},this)},toObject:function(a){return c(this.callSuper("toObject",a),{points:this.points.concat()})},toSVG:function(a){for(var b=[],c=this._createBaseSVGMarkup(),d=0,e=this.points.length;e>d;d++)b.push(f(this.points[d].x,2),",",f(this.points[d].y,2)," ");return c.push("<polygon ",'points="',b.join(""),'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform()," ",this.getSvgTransformMatrix(),'"/>\n'),a?a(c.join("")):c.join("")},_render:function(a){var b;a.beginPath(),this._applyPointOffset&&(this.group&&"path-group"===this.group.type||this._applyPointOffset(),this._applyPointOffset=null),a.moveTo(this.points[0].x,this.points[0].y);for(var c=0,d=this.points.length;d>c;c++)b=this.points[c],a.lineTo(b.x,b.y);this._renderFill(a),(this.stroke||this.strokeDashArray)&&(a.closePath(),this._renderStroke(a))},_renderDashedStroke:function(a){var c,d;a.beginPath();for(var e=0,f=this.points.length;f>e;e++)c=this.points[e],d=this.points[e+1]||this.points[0],b.util.drawDashedLine(a,c.x,c.y,d.x,d.y,this.strokeDashArray);a.closePath()},complexity:function(){return this.points.length}}),b.Polygon.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat(),b.Polygon.fromElement=function(a,d){if(!a)return null;d||(d={});var e=b.parsePointsAttribute(a.getAttribute("points")),f=b.parseAttributes(a,b.Polygon.ATTRIBUTE_NAMES);return null===e?null:new b.Polygon(e,c(f,d))},void(b.Polygon.fromObject=function(a){return new b.Polygon(a.points,a,!0)}))}("undefined"!=typeof exports?exports:this),function(a){"use strict";function b(a){return"H"===a[0]?a[1]:a[a.length-2]}function c(a){return"V"===a[0]?a[1]:a[a.length-1]}var d=a.fabric||(a.fabric={}),e=d.util.array.min,f=d.util.array.max,g=d.util.object.extend,h=Object.prototype.toString,i=d.util.drawArc,j={m:2,l:2,h:1,v:1,c:6,s:4,q:4,t:2,a:7},k={m:"l",M:"L"};return d.Path?void d.warn("fabric.Path is already defined"):(d.Path=d.util.createClass(d.Object,{type:"path",path:null,initialize:function(a,b){if(b=b||{},this.setOptions(b),!a)throw new Error("`path` argument is required");var c="[object Array]"===h.call(a);this.path=c?a:a.match&&a.match(/[mzlhvcsqta][^mzlhvcsqta]*/gi),this.path&&(c||(this.path=this._parsePath()),this._initializePath(b),b.sourcePath&&this.setSourcePath(b.sourcePath))},_initializePath:function(a){var b="width"in a&&null!=a.width,c="height"in a&&null!=a.width,d="left"in a,e="top"in a,f=d?this.left:0,h=e?this.top:0;b&&c?(e||(this.top=this.height/2),d||(this.left=this.width/2)):(g(this,this._parseDimensions()),b&&(this.width=a.width),c&&(this.height=a.height)),this.pathOffset=this.pathOffset||this._calculatePathOffset(f,h)},_calculatePathOffset:function(a,b){return{x:this.left-a-this.width/2,y:this.top-b-this.height/2}},_render:function(a,b){var c,d,e,f,g,h=null,j=0,k=0,l=0,m=0,n=0,o=0,p=-(this.width/2+this.pathOffset.x),q=-(this.height/2+this.pathOffset.y);b&&(p+=this.width/2,q+=this.height/2);for(var r=0,s=this.path.length;s>r;++r){switch(c=this.path[r],c[0]){case"l":l+=c[1],m+=c[2],a.lineTo(l+p,m+q);break;case"L":l=c[1],m=c[2],a.lineTo(l+p,m+q);break;case"h":l+=c[1],a.lineTo(l+p,m+q);break;case"H":l=c[1],a.lineTo(l+p,m+q);break;case"v":m+=c[1],a.lineTo(l+p,m+q);break;case"V":m=c[1],a.lineTo(l+p,m+q);break;case"m":l+=c[1],m+=c[2],j=l,k=m,a.moveTo(l+p,m+q);break;case"M":l=c[1],m=c[2],j=l,k=m,a.moveTo(l+p,m+q);break;case"c":d=l+c[5],e=m+c[6],n=l+c[3],o=m+c[4],a.bezierCurveTo(l+c[1]+p,m+c[2]+q,n+p,o+q,d+p,e+q),l=d,m=e;break;case"C":l=c[5],m=c[6],n=c[3],o=c[4],a.bezierCurveTo(c[1]+p,c[2]+q,n+p,o+q,l+p,m+q);break;case"s":d=l+c[3],e=m+c[4],n=n?2*l-n:l,o=o?2*m-o:m,a.bezierCurveTo(n+p,o+q,l+c[1]+p,m+c[2]+q,d+p,e+q),n=l+c[1],o=m+c[2],l=d,m=e;break;case"S":d=c[3],e=c[4],n=2*l-n,o=2*m-o,a.bezierCurveTo(n+p,o+q,c[1]+p,c[2]+q,d+p,e+q),l=d,m=e,n=c[1],o=c[2];break;case"q":d=l+c[3],e=m+c[4],n=l+c[1],o=m+c[2],a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e;break;case"Q":d=c[3],e=c[4],a.quadraticCurveTo(c[1]+p,c[2]+q,d+p,e+q),l=d,m=e,n=c[1],o=c[2];break;case"t":d=l+c[1],e=m+c[2],null===h[0].match(/[QqTt]/)?(n=l,o=m):"t"===h[0]?(n=2*l-f,o=2*m-g):"q"===h[0]&&(n=2*l-n,o=2*m-o),f=n,g=o,a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e,n=l+c[1],o=m+c[2];break;case"T":d=c[1],e=c[2],n=2*l-n,o=2*m-o,a.quadraticCurveTo(n+p,o+q,d+p,e+q),l=d,m=e;break;case"a":i(a,l+p,m+q,[c[1],c[2],c[3],c[4],c[5],c[6]+l+p,c[7]+m+q]),l+=c[6],m+=c[7];break;case"A":i(a,l+p,m+q,[c[1],c[2],c[3],c[4],c[5],c[6]+p,c[7]+q]),l=c[6],m=c[7];break;case"z":case"Z":l=j,m=k,a.closePath()}h=c}},render:function(a,b){if(this.visible){a.save(),b&&a.translate(-this.width/2,-this.height/2);var c=this.transformMatrix;c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),b||this.transform(a),this._setStrokeStyles(a),this._setFillStyles(a),this._setShadow(a),this.clipTo&&d.util.clipContext(this,a),a.beginPath(),a.globalAlpha=this.group?a.globalAlpha*this.opacity:this.opacity,this._render(a,b),this._renderFill(a),this._renderStroke(a),this.clipTo&&a.restore(),this._removeShadow(a),a.restore()}},toString:function(){return"#<fabric.Path ("+this.complexity()+'): { "top": '+this.top+', "left": '+this.left+" }>"},toObject:function(a){var b=g(this.callSuper("toObject",a),{path:this.path.map(function(a){return a.slice()}),pathOffset:this.pathOffset});return this.sourcePath&&(b.sourcePath=this.sourcePath),this.transformMatrix&&(b.transformMatrix=this.transformMatrix),b},toDatalessObject:function(a){var b=this.toObject(a);return this.sourcePath&&(b.path=this.sourcePath),delete b.sourcePath,b},toSVG:function(a){for(var b=[],c=this._createBaseSVGMarkup(),d=0,e=this.path.length;e>d;d++)b.push(this.path[d].join(" "));
+var f=b.join(" ");return c.push("<path ",'d="',f,'" style="',this.getSvgStyles(),'" transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'" stroke-linecap="round" ',"/>\n"),a?a(c.join("")):c.join("")},complexity:function(){return this.path.length},_parsePath:function(){for(var a,b,c,d,e,f=[],g=[],h=/([-+]?((\d+\.\d+)|((\d+)|(\.\d+)))(?:e[-+]?\d+)?)/gi,i=0,l=this.path.length;l>i;i++){for(a=this.path[i],d=a.slice(1).trim(),g.length=0;c=h.exec(d);)g.push(c[0]);e=[a.charAt(0)];for(var m=0,n=g.length;n>m;m++)b=parseFloat(g[m]),isNaN(b)||e.push(b);var o=e[0],p=j[o.toLowerCase()],q=k[o]||o;if(e.length-1>p)for(var r=1,s=e.length;s>r;r+=p)f.push([o].concat(e.slice(r,r+p))),o=q;else f.push(e)}return f},_parseDimensions:function(){var a=[],b=[],c={};this.path.forEach(function(d,e){this._getCoordsFromCommand(d,e,a,b,c)},this);var d=e(a),g=e(b),h=f(a),i=f(b),j=h-d,k=i-g,l={left:this.left+(d+j/2),top:this.top+(g+k/2),width:j,height:k};return l},_getCoordsFromCommand:function(a,d,e,f,g){var h=!1;"H"!==a[0]&&(g.x=b(0===d?a:this.path[d-1])),"V"!==a[0]&&(g.y=c(0===d?a:this.path[d-1])),a[0]===a[0].toLowerCase()&&(h=!0);var i,j=this._getXY(a,h,g);i=parseInt(j.x,10),isNaN(i)||e.push(i),i=parseInt(j.y,10),isNaN(i)||f.push(i)},_getXY:function(a,d,e){var f=d?e.x+b(a):"V"===a[0]?e.x:b(a),g=d?e.y+c(a):"H"===a[0]?e.y:c(a);return{x:f,y:g}}}),d.Path.fromObject=function(a,b){"string"==typeof a.path?d.loadSVGFromURL(a.path,function(c){var e=c[0],f=a.path;delete a.path,d.util.object.extend(e,a),e.setSourcePath(f),b(e)}):b(new d.Path(a.path,a))},d.Path.ATTRIBUTE_NAMES=d.SHARED_ATTRIBUTES.concat(["d"]),d.Path.fromElement=function(a,b,c){var e=d.parseAttributes(a,d.Path.ATTRIBUTE_NAMES);b&&b(new d.Path(e.d,g(e,c)))},void(d.Path.async=!0))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.invoke,e=b.Object.prototype.toObject;return b.PathGroup?void b.warn("fabric.PathGroup is already defined"):(b.PathGroup=b.util.createClass(b.Path,{type:"path-group",fill:"",initialize:function(a,b){b=b||{},this.paths=a||[];for(var c=this.paths.length;c--;)this.paths[c].group=this;this.setOptions(b),b.widthAttr&&(this.scaleX=b.widthAttr/b.width),b.heightAttr&&(this.scaleY=b.heightAttr/b.height),this.setCoords(),b.sourcePath&&this.setSourcePath(b.sourcePath)},render:function(a){if(this.visible){a.save();var c=this.transformMatrix;c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),this.transform(a),this._setShadow(a),this.clipTo&&b.util.clipContext(this,a);for(var d=0,e=this.paths.length;e>d;++d)this.paths[d].render(a,!0);this.clipTo&&a.restore(),this._removeShadow(a),a.restore()}},_set:function(a,b){if("fill"===a&&b&&this.isSameColor())for(var c=this.paths.length;c--;)this.paths[c]._set(a,b);return this.callSuper("_set",a,b)},toObject:function(a){var b=c(e.call(this,a),{paths:d(this.getObjects(),"toObject",a)});return this.sourcePath&&(b.sourcePath=this.sourcePath),b},toDatalessObject:function(a){var b=this.toObject(a);return this.sourcePath&&(b.paths=this.sourcePath),b},toSVG:function(a){for(var b=this.getObjects(),c="translate("+this.left+" "+this.top+")",d=["<g ",'style="',this.getSvgStyles(),'" ','transform="',c,this.getSvgTransform(),'" ',">\n"],e=0,f=b.length;f>e;e++)d.push(b[e].toSVG(a));return d.push("</g>\n"),a?a(d.join("")):d.join("")},toString:function(){return"#<fabric.PathGroup ("+this.complexity()+"): { top: "+this.top+", left: "+this.left+" }>"},isSameColor:function(){var a=(this.getObjects()[0].get("fill")||"").toLowerCase();return this.getObjects().every(function(b){return(b.get("fill")||"").toLowerCase()===a})},complexity:function(){return this.paths.reduce(function(a,b){return a+(b&&b.complexity?b.complexity():0)},0)},getObjects:function(){return this.paths}}),b.PathGroup.fromObject=function(a,c){"string"==typeof a.paths?b.loadSVGFromURL(a.paths,function(d){var e=a.paths;delete a.paths;var f=b.util.groupSVGElements(d,a,e);c(f)}):b.util.enlivenObjects(a.paths,function(d){delete a.paths,c(new b.PathGroup(d,a))})},void(b.PathGroup.async=!0))}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.array.min,e=b.util.array.max,f=b.util.array.invoke;if(!b.Group){var g={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};b.Group=b.util.createClass(b.Object,b.Collection,{type:"group",initialize:function(a,b){b=b||{},this._objects=a||[];for(var d=this._objects.length;d--;)this._objects[d].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),b&&c(this,b),this._setOpacityIfSame(),this.setCoords(),this.saveCoords()},_updateObjectsCoords:function(){this.forEachObject(this._updateObjectCoords,this)},_updateObjectCoords:function(a){var b=a.getLeft(),c=a.getTop();a.set({originalLeft:b,originalTop:c,left:b-this.left,top:c-this.top}),a.setCoords(),a.__origHasControls=a.hasControls,a.hasControls=!1},toString:function(){return"#<fabric.Group: ("+this.complexity()+")>"},addWithUpdate:function(a){return this._restoreObjectsState(),a&&(this._objects.push(a),a.group=this),this.forEachObject(this._setObjectActive,this),this._calcBounds(),this._updateObjectsCoords(),this},_setObjectActive:function(a){a.set("active",!0),a.group=this},removeWithUpdate:function(a){return this._moveFlippedObject(a),this._restoreObjectsState(),this.forEachObject(this._setObjectActive,this),this.remove(a),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(a){a.group=this},_onObjectRemoved:function(a){delete a.group,a.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(a,b){if(a in this.delegatedProperties){var c=this._objects.length;for(this[a]=b;c--;)this._objects[c].set(a,b)}else this[a]=b},toObject:function(a){return c(this.callSuper("toObject",a),{objects:f(this._objects,"toObject",a)})},render:function(a){if(this.visible){a.save(),this.clipTo&&b.util.clipContext(this,a);for(var c=0,d=this._objects.length;d>c;c++)this._renderObject(this._objects[c],a);this.clipTo&&a.restore(),a.restore()}},_renderControls:function(a,b){this.callSuper("_renderControls",a,b);for(var c=0,d=this._objects.length;d>c;c++)this._objects[c]._renderControls(a)},_renderObject:function(a,b){var c=a.hasRotatingPoint;a.visible&&(a.hasRotatingPoint=!1,a.render(b),a.hasRotatingPoint=c)},_restoreObjectsState:function(){return this._objects.forEach(this._restoreObjectState,this),this},_moveFlippedObject:function(a){var b=a.get("originX"),c=a.get("originY"),d=a.getCenterPoint();a.set({originX:"center",originY:"center",left:d.x,top:d.y}),this._toggleFlipping(a);var e=a.getPointByOrigin(b,c);return a.set({originX:b,originY:c,left:e.x,top:e.y}),this},_toggleFlipping:function(a){this.flipX&&(a.toggle("flipX"),a.set("left",-a.get("left")),a.setAngle(-a.getAngle())),this.flipY&&(a.toggle("flipY"),a.set("top",-a.get("top")),a.setAngle(-a.getAngle()))},_restoreObjectState:function(a){return this._setObjectPosition(a),a.setCoords(),a.hasControls=a.__origHasControls,delete a.__origHasControls,a.set("active",!1),a.setCoords(),delete a.group,this},_setObjectPosition:function(a){var b=this.getLeft(),c=this.getTop(),d=this._getRotatedLeftTop(a);a.set({angle:a.getAngle()+this.getAngle(),left:b+d.left,top:c+d.top,scaleX:a.get("scaleX")*this.get("scaleX"),scaleY:a.get("scaleY")*this.get("scaleY")})},_getRotatedLeftTop:function(a){var b=this.getAngle()*(Math.PI/180);return{left:-Math.sin(b)*a.getTop()*this.get("scaleY")+Math.cos(b)*a.getLeft()*this.get("scaleX"),top:Math.cos(b)*a.getTop()*this.get("scaleY")+Math.sin(b)*a.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(a){a.setCoords()}),this},_setOpacityIfSame:function(){var a=this.getObjects(),b=a[0]?a[0].get("opacity"):1,c=a.every(function(a){return a.get("opacity")===b});c&&(this.opacity=b)},_calcBounds:function(a){for(var b,c=[],d=[],e=0,f=this._objects.length;f>e;++e){b=this._objects[e],b.setCoords();for(var g in b.oCoords)c.push(b.oCoords[g].x),d.push(b.oCoords[g].y)}this.set(this._getBounds(c,d,a))},_getBounds:function(a,c,f){var g=b.util.invertTransform(this.getViewportTransform()),h=b.util.transformPoint(new b.Point(d(a),d(c)),g),i=b.util.transformPoint(new b.Point(e(a),e(c)),g),j={width:i.x-h.x||0,height:i.y-h.y||0};return f||(j.left=(h.x+i.x)/2||0,j.top=(h.y+i.y)/2||0),j},toSVG:function(a){for(var b=["<g ",'transform="',this.getSvgTransform(),'">\n'],c=0,d=this._objects.length;d>c;c++)b.push(this._objects[c].toSVG(a));return b.push("</g>\n"),a?a(b.join("")):b.join("")},get:function(a){if(a in g){if(this[a])return this[a];for(var b=0,c=this._objects.length;c>b;b++)if(this._objects[b][a])return!0;return!1}return a in this.delegatedProperties?this._objects[0]&&this._objects[0].get(a):this[a]}}),b.Group.fromObject=function(a,c){b.util.enlivenObjects(a.objects,function(d){delete a.objects,c&&c(new b.Group(d,a))})},b.Group.async=!0}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=fabric.util.object.extend;return a.fabric||(a.fabric={}),a.fabric.Image?void fabric.warn("fabric.Image is already defined."):(fabric.Image=fabric.util.createClass(fabric.Object,{type:"image",crossOrigin:"",initialize:function(a,b){b||(b={}),this.filters=[],this.callSuper("initialize",b),this._initElement(a,b),this._initConfig(b),b.filters&&(this.filters=b.filters,this.applyFilters())},getElement:function(){return this._element},setElement:function(a,b){return this._element=a,this._originalElement=a,this._initConfig(),0!==this.filters.length&&this.applyFilters(b),this},setCrossOrigin:function(a){return this.crossOrigin=a,this._element.crossOrigin=a,this},getOriginalSize:function(){var a=this.getElement();return{width:a.width,height:a.height}},_stroke:function(a){a.save(),this._setStrokeStyles(a),a.beginPath(),a.strokeRect(-this.width/2,-this.height/2,this.width,this.height),a.closePath(),a.restore()},_renderDashedStroke:function(a){var b=-this.width/2,c=-this.height/2,d=this.width,e=this.height;a.save(),this._setStrokeStyles(a),a.beginPath(),fabric.util.drawDashedLine(a,b,c,b+d,c,this.strokeDashArray),fabric.util.drawDashedLine(a,b+d,c,b+d,c+e,this.strokeDashArray),fabric.util.drawDashedLine(a,b+d,c+e,b,c+e,this.strokeDashArray),fabric.util.drawDashedLine(a,b,c+e,b,c,this.strokeDashArray),a.closePath(),a.restore()},toObject:function(a){return b(this.callSuper("toObject",a),{src:this._originalElement.src||this._originalElement._src,filters:this.filters.map(function(a){return a&&a.toObject()}),crossOrigin:this.crossOrigin})},toSVG:function(a){var b=[],c=-this.width/2,d=-this.height/2;if(this.group&&(c=this.left,d=this.top),b.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n','<image xlink:href="',this.getSvgSrc(),'" x="',c,'" y="',d,'" style="',this.getSvgStyles(),'" width="',this.width,'" height="',this.height,'" preserveAspectRatio="none"',"></image>\n"),this.stroke||this.strokeDashArray){var e=this.fill;this.fill=null,b.push("<rect ",'x="',c,'" y="',d,'" width="',this.width,'" height="',this.height,'" style="',this.getSvgStyles(),'"/>\n'),this.fill=e}return b.push("</g>\n"),a?a(b.join("")):b.join("")},getSrc:function(){return this.getElement()?this.getElement().src||this.getElement()._src:void 0},toString:function(){return'#<fabric.Image: { src: "'+this.getSrc()+'" }>'},clone:function(a,b){this.constructor.fromObject(this.toObject(b),a)},applyFilters:function(a){if(this._originalElement){if(0===this.filters.length)return this._element=this._originalElement,void(a&&a());var b=this._originalElement,c=fabric.util.createCanvasElement(),d=fabric.util.createImage(),e=this;return c.width=b.width,c.height=b.height,c.getContext("2d").drawImage(b,0,0,b.width,b.height),this.filters.forEach(function(a){a&&a.applyTo(c)}),d.width=b.width,d.height=b.height,fabric.isLikelyNode?(d.src=c.toBuffer(undefined,fabric.Image.pngCompression),e._element=d,a&&a()):(d.onload=function(){e._element=d,a&&a(),d.onload=c=b=null},d.src=c.toDataURL("image/png")),this}},_render:function(a,b){this._element&&a.drawImage(this._element,b?this.left:-this.width/2,b?this.top:-this.height/2,this.width,this.height),this._renderStroke(a)},_resetWidthHeight:function(){var a=this.getElement();this.set("width",a.width),this.set("height",a.height)},_initElement:function(a){this.setElement(fabric.util.getById(a)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(a){a||(a={}),this.setOptions(a),this._setWidthHeight(a),this._element&&this.crossOrigin&&(this._element.crossOrigin=this.crossOrigin)},_initFilters:function(a,b){a.filters&&a.filters.length?fabric.util.enlivenObjects(a.filters,function(a){b&&b(a)},"fabric.Image.filters"):b&&b()},_setWidthHeight:function(a){this.width="width"in a?a.width:this.getElement()?this.getElement().width||0:0,this.height="height"in a?a.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(a,b){fabric.util.loadImage(a.src,function(c){fabric.Image.prototype._initFilters.call(a,a,function(d){a.filters=d||[];var e=new fabric.Image(c,a);b&&b(e)})},null,a.crossOrigin)},fabric.Image.fromURL=function(a,b,c){fabric.util.loadImage(a,function(a){b(new fabric.Image(a,c))},null,c&&c.crossOrigin)},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(a,c,d){var e=fabric.parseAttributes(a,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(e["xlink:href"],c,b(d?fabric.util.object.clone(d):{},e))},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(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Brightness=b.util.createClass(b.Image.filters.BaseFilter,{type:"Brightness",initialize:function(a){a=a||{},this.brightness=a.brightness||0},applyTo:function(a){for(var b=a.getContext("2d"),c=b.getImageData(0,0,a.width,a.height),d=c.data,e=this.brightness,f=0,g=d.length;g>f;f+=4)d[f]+=e,d[f+1]+=e,d[f+2]+=e;b.putImageData(c,0,0)},toObject:function(){return c(this.callSuper("toObject"),{brightness:this.brightness})}}),b.Image.filters.Brightness.fromObject=function(a){return new b.Image.filters.Brightness(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Convolute=b.util.createClass(b.Image.filters.BaseFilter,{type:"Convolute",initialize:function(a){a=a||{},this.opaque=a.opaque,this.matrix=a.matrix||[0,0,0,0,1,0,0,0,0];var c=b.util.createCanvasElement();this.tmpCtx=c.getContext("2d")},_createImageData:function(a,b){return this.tmpCtx.createImageData(a,b)},applyTo:function(a){for(var b=this.matrix,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=Math.round(Math.sqrt(b.length)),f=Math.floor(e/2),g=d.data,h=d.width,i=d.height,j=h,k=i,l=this._createImageData(j,k),m=l.data,n=this.opaque?1:0,o=0;k>o;o++)for(var p=0;j>p;p++){for(var q=o,r=p,s=4*(o*j+p),t=0,u=0,v=0,w=0,x=0;e>x;x++)for(var y=0;e>y;y++){var z=q+x-f,A=r+y-f;if(!(0>z||z>i||0>A||A>h)){var B=4*(z*h+A),C=b[x*e+y];t+=g[B]*C,u+=g[B+1]*C,v+=g[B+2]*C,w+=g[B+3]*C}}m[s]=t,m[s+1]=u,m[s+2]=v,m[s+3]=w+n*(255-w)}c.putImageData(l,0,0)},toObject:function(){return c(this.callSuper("toObject"),{opaque:this.opaque,matrix:this.matrix})}}),b.Image.filters.Convolute.fromObject=function(a){return new b.Image.filters.Convolute(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.GradientTransparency=b.util.createClass(b.Image.filters.BaseFilter,{type:"GradientTransparency",initialize:function(a){a=a||{},this.threshold=a.threshold||100},applyTo:function(a){for(var b=a.getContext("2d"),c=b.getImageData(0,0,a.width,a.height),d=c.data,e=this.threshold,f=d.length,g=0,h=d.length;h>g;g+=4)d[g+3]=e+255*(f-g)/f;b.putImageData(c,0,0)},toObject:function(){return c(this.callSuper("toObject"),{threshold:this.threshold})}}),b.Image.filters.GradientTransparency.fromObject=function(a){return new b.Image.filters.GradientTransparency(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Grayscale=b.util.createClass(b.Image.filters.BaseFilter,{type:"Grayscale",applyTo:function(a){for(var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=d.width*d.height*4,g=0;f>g;)b=(e[g]+e[g+1]+e[g+2])/3,e[g]=b,e[g+1]=b,e[g+2]=b,g+=4;c.putImageData(d,0,0)}}),b.Image.filters.Grayscale.fromObject=function(){return new b.Image.filters.Grayscale}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Invert=b.util.createClass(b.Image.filters.BaseFilter,{type:"Invert",applyTo:function(a){var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=e.length;for(b=0;f>b;b+=4)e[b]=255-e[b],e[b+1]=255-e[b+1],e[b+2]=255-e[b+2];c.putImageData(d,0,0)}}),b.Image.filters.Invert.fromObject=function(){return new b.Image.filters.Invert}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Mask=b.util.createClass(b.Image.filters.BaseFilter,{type:"Mask",initialize:function(a){a=a||{},this.mask=a.mask,this.channel=[0,1,2,3].indexOf(a.channel)>-1?a.channel:0},applyTo:function(a){if(this.mask){var c,d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=e.data,g=this.mask.getElement(),h=b.util.createCanvasElement(),i=this.channel,j=e.width*e.height*4;h.width=g.width,h.height=g.height,h.getContext("2d").drawImage(g,0,0,g.width,g.height);var k=h.getContext("2d").getImageData(0,0,g.width,g.height),l=k.data;for(c=0;j>c;c+=4)f[c+3]=l[c+i];d.putImageData(e,0,0)}},toObject:function(){return c(this.callSuper("toObject"),{mask:this.mask.toObject(),channel:this.channel})}}),b.Image.filters.Mask.fromObject=function(a,c){b.util.loadImage(a.mask.src,function(d){a.mask=new b.Image(d,a.mask),c&&c(new b.Image.filters.Mask(a))})},b.Image.filters.Mask.async=!0}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Noise=b.util.createClass(b.Image.filters.BaseFilter,{type:"Noise",initialize:function(a){a=a||{},this.noise=a.noise||0},applyTo:function(a){for(var b,c=a.getContext("2d"),d=c.getImageData(0,0,a.width,a.height),e=d.data,f=this.noise,g=0,h=e.length;h>g;g+=4)b=(.5-Math.random())*f,e[g]+=b,e[g+1]+=b,e[g+2]+=b;c.putImageData(d,0,0)},toObject:function(){return c(this.callSuper("toObject"),{noise:this.noise})}}),b.Image.filters.Noise.fromObject=function(a){return new b.Image.filters.Noise(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Pixelate=b.util.createClass(b.Image.filters.BaseFilter,{type:"Pixelate",initialize:function(a){a=a||{},this.blocksize=a.blocksize||4},applyTo:function(a){var b,c,d,e,f,g,h,i=a.getContext("2d"),j=i.getImageData(0,0,a.width,a.height),k=j.data,l=j.height,m=j.width;for(c=0;l>c;c+=this.blocksize)for(d=0;m>d;d+=this.blocksize){b=4*c*m+4*d,e=k[b],f=k[b+1],g=k[b+2],h=k[b+3];for(var n=c,o=c+this.blocksize;o>n;n++)for(var p=d,q=d+this.blocksize;q>p;p++)b=4*n*m+4*p,k[b]=e,k[b+1]=f,k[b+2]=g,k[b+3]=h}i.putImageData(j,0,0)},toObject:function(){return c(this.callSuper("toObject"),{blocksize:this.blocksize})}}),b.Image.filters.Pixelate.fromObject=function(a){return new b.Image.filters.Pixelate(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.RemoveWhite=b.util.createClass(b.Image.filters.BaseFilter,{type:"RemoveWhite",initialize:function(a){a=a||{},this.threshold=a.threshold||30,this.distance=a.distance||20},applyTo:function(a){for(var b,c,d,e=a.getContext("2d"),f=e.getImageData(0,0,a.width,a.height),g=f.data,h=this.threshold,i=this.distance,j=255-h,k=Math.abs,l=0,m=g.length;m>l;l+=4)b=g[l],c=g[l+1],d=g[l+2],b>j&&c>j&&d>j&&k(b-c)<i&&k(b-d)<i&&k(c-d)<i&&(g[l+3]=1);e.putImageData(f,0,0)},toObject:function(){return c(this.callSuper("toObject"),{threshold:this.threshold,distance:this.distance})}}),b.Image.filters.RemoveWhite.fromObject=function(a){return new b.Image.filters.RemoveWhite(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Sepia=b.util.createClass(b.Image.filters.BaseFilter,{type:"Sepia",applyTo:function(a){var b,c,d=a.getContext("2d"),e=d.getImageData(0,0,a.width,a.height),f=e.data,g=f.length;for(b=0;g>b;b+=4)c=.3*f[b]+.59*f[b+1]+.11*f[b+2],f[b]=c+100,f[b+1]=c+50,f[b+2]=c+255;d.putImageData(e,0,0)}}),b.Image.filters.Sepia.fromObject=function(){return new b.Image.filters.Sepia}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={});b.Image.filters.Sepia2=b.util.createClass(b.Image.filters.BaseFilter,{type:"Sepia2",applyTo:function(a){var b,c,d,e,f=a.getContext("2d"),g=f.getImageData(0,0,a.width,a.height),h=g.data,i=h.length;for(b=0;i>b;b+=4)c=h[b],d=h[b+1],e=h[b+2],h[b]=(.393*c+.769*d+.189*e)/1.351,h[b+1]=(.349*c+.686*d+.168*e)/1.203,h[b+2]=(.272*c+.534*d+.131*e)/2.14;f.putImageData(g,0,0)}}),b.Image.filters.Sepia2.fromObject=function(){return new b.Image.filters.Sepia2}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Tint=b.util.createClass(b.Image.filters.BaseFilter,{type:"Tint",initialize:function(a){a=a||{},this.color=a.color||"#000000",this.opacity="undefined"!=typeof a.opacity?a.opacity:new b.Color(this.color).getAlpha()},applyTo:function(a){var c,d,e,f,g,h,i,j,k,l=a.getContext("2d"),m=l.getImageData(0,0,a.width,a.height),n=m.data,o=n.length;for(k=new b.Color(this.color).getSource(),d=k[0]*this.opacity,e=k[1]*this.opacity,f=k[2]*this.opacity,j=1-this.opacity,c=0;o>c;c+=4)g=n[c],h=n[c+1],i=n[c+2],n[c]=d+g*j,n[c+1]=e+h*j,n[c+2]=f+i*j;l.putImageData(m,0,0)},toObject:function(){return c(this.callSuper("toObject"),{color:this.color,opacity:this.opacity})}}),b.Image.filters.Tint.fromObject=function(a){return new b.Image.filters.Tint(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend;b.Image.filters.Multiply=b.util.createClass(b.Image.filters.BaseFilter,{type:"Multiply",initialize:function(a){a=a||{},this.color=a.color||"#000000"},applyTo:function(a){var c,d,e=a.getContext("2d"),f=e.getImageData(0,0,a.width,a.height),g=f.data,h=g.length;for(d=new b.Color(this.color).getSource(),c=0;h>c;c+=4)g[c]*=d[0]/255,g[c+1]*=d[1]/255,g[c+2]*=d[2]/255;e.putImageData(f,0,0)},toObject:function(){return c(this.callSuper("toObject"),{color:this.color})}}),b.Image.filters.Multiply.fromObject=function(a){return new b.Image.filters.Multiply(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric;b.Image.filters.Blend=b.util.createClass({type:"Blend",initialize:function(a){a=a||{},this.color=a.color||"#000",this.image=a.image||!1,this.mode=a.mode||"multiply",this.alpha=a.alpha||1},applyTo:function(a){var c,d,e,f,g,h,i,j=a.getContext("2d"),k=j.getImageData(0,0,a.width,a.height),l=k.data,m=!1;if(this.image){m=!0;var n=b.util.createCanvasElement();n.width=this.image.width,n.height=this.image.height;var o=new b.StaticCanvas(n);o.add(this.image);var p=o.getContext("2d");i=p.getImageData(0,0,o.width,o.height).data}else i=new b.Color(this.color).getSource(),c=i[0]*this.alpha,d=i[1]*this.alpha,e=i[2]*this.alpha;for(var q=0,r=l.length;r>q;q+=4)switch(f=l[q],g=l[q+1],h=l[q+2],m&&(c=i[q]*this.alpha,d=i[q+1]*this.alpha,e=i[q+2]*this.alpha),this.mode){case"multiply":l[q]=f*c/255,l[q+1]=g*d/255,l[q+2]=h*e/255;break;case"screen":l[q]=1-(1-f)*(1-c),l[q+1]=1-(1-g)*(1-d),l[q+2]=1-(1-h)*(1-e);break;case"add":l[q]=Math.min(255,f+c),l[q+1]=Math.min(255,g+d),l[q+2]=Math.min(255,h+e);break;case"diff":case"difference":l[q]=Math.abs(f-c),l[q+1]=Math.abs(g-d),l[q+2]=Math.abs(h-e);break;case"subtract":var s=f-c,t=g-d,u=h-e;l[q]=0>s?0:s,l[q+1]=0>t?0:t,l[q+2]=0>u?0:u;break;case"darken":l[q]=Math.min(f,c),l[q+1]=Math.min(g,d),l[q+2]=Math.min(h,e);break;case"lighten":l[q]=Math.max(f,c),l[q+1]=Math.max(g,d),l[q+2]=Math.max(h,e)}j.putImageData(k,0,0)}}),b.Image.filters.Blend.fromObject=function(a){return new b.Image.filters.Blend(a)}}("undefined"!=typeof exports?exports:this),function(a){"use strict";var b=a.fabric||(a.fabric={}),c=b.util.object.extend,d=b.util.object.clone,e=b.util.toFixed,f=b.StaticCanvas.supports("setLineDash");if(b.Text)return void b.warn("fabric.Text is already defined");var g=b.Object.prototype.stateProperties.concat();g.push("fontFamily","fontWeight","fontSize","text","textDecoration","textAlign","fontStyle","lineHeight","textBackgroundColor","useNative","path"),b.Text=b.util.createClass(b.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:g,stroke:null,shadow:null,initialize:function(a,b){b=b||{},this.text=a,this.__skipDimension=!0,this.setOptions(b),this.__skipDimension=!1,this._initDimensions()},_initDimensions:function(){if(!this.__skipDimension){var a=b.util.createCanvasElement();this._render(a.getContext("2d"))}},toString:function(){return"#<fabric.Text ("+this.complexity()+'): { "text": "'+this.text+'", "fontFamily": "'+this.fontFamily+'" }>'},_render:function(a){"undefined"==typeof Cufon||this.useNative===!0?this._renderViaNative(a):this._renderViaCufon(a)},_renderViaNative:function(a){var c=this.text.split(this._reNewline);this._setTextStyles(a),this.width=this._getTextWidth(a,c),this.height=this._getTextHeight(a,c),this.clipTo&&b.util.clipContext(this,a),this._renderTextBackground(a,c),this._translateForTextAlign(a),this._renderText(a,c),"left"!==this.textAlign&&"justify"!==this.textAlign&&a.restore(),this._renderTextDecoration(a,c),this.clipTo&&a.restore(),this._setBoundaries(a,c),this._totalLineHeight=0},_renderText:function(a,b){a.save(),this._setShadow(a),this._setupFillRule(a),this._renderTextFill(a,b),this._renderTextStroke(a,b),this._restoreFillRule(a),this._removeShadow(a),a.restore()},_translateForTextAlign:function(a){"left"!==this.textAlign&&"justify"!==this.textAlign&&(a.save(),a.translate("center"===this.textAlign?this.width/2:this.width,0))},_setBoundaries:function(a,b){this._boundaries=[];for(var c=0,d=b.length;d>c;c++){var e=this._getLineWidth(a,b[c]),f=this._getLineLeftOffset(e);this._boundaries.push({height:this.fontSize*this.lineHeight,width:e,left:f})}},_setTextStyles:function(a){this._setFillStyles(a),this._setStrokeStyles(a),a.textBaseline="alphabetic",this.skipTextAlign||(a.textAlign=this.textAlign),a.font=this._getFontDeclaration()},_getTextHeight:function(a,b){return this.fontSize*b.length*this.lineHeight},_getTextWidth:function(a,b){for(var c=a.measureText(b[0]||"|").width,d=1,e=b.length;e>d;d++){var f=a.measureText(b[d]).width;f>c&&(c=f)}return c},_renderChars:function(a,b,c,d,e){b[a](c,d,e)},_renderTextLine:function(a,b,c,d,e,f){if(e-=this.fontSize/4,"justify"!==this.textAlign)return void this._renderChars(a,b,c,d,e,f);var g=b.measureText(c).width,h=this.width;if(h>g)for(var i=c.split(/\s+/),j=b.measureText(c.replace(/\s+/g,"")).width,k=h-j,l=i.length-1,m=k/l,n=0,o=0,p=i.length;p>o;o++)this._renderChars(a,b,i[o],d+n,e,f),n+=b.measureText(i[o]).width+m;else this._renderChars(a,b,c,d,e,f)},_getLeftOffset:function(){return b.isLikelyNode?0:-this.width/2},_getTopOffset:function(){return-this.height/2},_renderTextFill:function(a,b){if(this.fill||this._skipFillStrokeCheck){this._boundaries=[];for(var c=0,d=0,e=b.length;e>d;d++){var f=this._getHeightOfLine(a,d,b);c+=f,this._renderTextLine("fillText",a,b[d],this._getLeftOffset(),this._getTopOffset()+c,d)}}},_renderTextStroke:function(a,b){if(this.stroke&&0!==this.strokeWidth||this._skipFillStrokeCheck){var c=0;a.save(),this.strokeDashArray&&(1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),f&&a.setLineDash(this.strokeDashArray)),a.beginPath();for(var d=0,e=b.length;e>d;d++){var g=this._getHeightOfLine(a,d,b);c+=g,this._renderTextLine("strokeText",a,b[d],this._getLeftOffset(),this._getTopOffset()+c,d)}a.closePath(),a.restore()}},_getHeightOfLine:function(){return this.fontSize*this.lineHeight},_renderTextBackground:function(a,b){this._renderTextBoxBackground(a),this._renderTextLinesBackground(a,b)},_renderTextBoxBackground:function(a){this.backgroundColor&&(a.save(),a.fillStyle=this.backgroundColor,a.fillRect(this._getLeftOffset(),this._getTopOffset(),this.width,this.height),a.restore())},_renderTextLinesBackground:function(a,b){if(this.textBackgroundColor){a.save(),a.fillStyle=this.textBackgroundColor;for(var c=0,d=b.length;d>c;c++)if(""!==b[c]){var e=this._getLineWidth(a,b[c]),f=this._getLineLeftOffset(e);a.fillRect(this._getLeftOffset()+f,this._getTopOffset()+c*this.fontSize*this.lineHeight,e,this.fontSize*this.lineHeight)}a.restore()}},_getLineLeftOffset:function(a){return"center"===this.textAlign?(this.width-a)/2:"right"===this.textAlign?this.width-a:0},_getLineWidth:function(a,b){return"justify"===this.textAlign?this.width:a.measureText(b).width},_renderTextDecoration:function(a,b){function c(c){for(var f=0,g=b.length;g>f;f++){var h=e._getLineWidth(a,b[f]),i=e._getLineLeftOffset(h);a.fillRect(e._getLeftOffset()+i,~~(c+f*e._getHeightOfLine(a,f,b)-d),h,1)}}if(this.textDecoration){var d=this._getTextHeight(a,b)/2,e=this;this.textDecoration.indexOf("underline")>-1&&c(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&c(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&c(this.fontSize*this.lineHeight-this.fontSize)}},_getFontDeclaration:function(){return[b.isLikelyNode?this.fontWeight:this.fontStyle,b.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",b.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(a,b){if(this.visible){a.save(),this._transform(a,b);var c=this.transformMatrix,d=this.group&&"path-group"===this.group.type;d&&a.translate(-this.group.width/2,-this.group.height/2),c&&a.transform(c[0],c[1],c[2],c[3],c[4],c[5]),d&&a.translate(this.left,this.top),this._render(a),a.restore()}},toObject:function(a){var b=c(this.callSuper("toObject",a),{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(b),b},toSVG:function(a){var b=[],c=this.text.split(this._reNewline),d=this._getSVGLeftTopOffsets(c),e=this._getSVGTextAndBg(d.lineTop,d.textLeft,c),f=this._getSVGShadows(d.lineTop,c);return d.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._wrapSVGTextAndBg(b,e,f,d),a?a(b.join("")):b.join("")
+},_getSVGLeftTopOffsets:function(a){var b=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,c=-(this.width/2),d=this.useNative?this.fontSize-1:this.height/2-a.length*this.fontSize-this._totalLineHeight;return{textLeft:c+(this.group&&"path-group"===this.group.type?this.left:0),textTop:d+(this.group&&"path-group"===this.group.type?this.top:0),lineTop:b}},_wrapSVGTextAndBg:function(a,b,c,d){a.push('<g transform="',this.getSvgTransform(),this.getSvgTransformMatrix(),'">\n',b.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(',e(d.textLeft,2)," ",e(d.textTop,2),')">',c.join(""),b.textSpans.join(""),"</text>\n","</g>\n")},_getSVGShadows:function(a,c){var d,f,g=[],h=1;if(!this.shadow||!this._boundaries)return g;for(d=0,f=c.length;f>d;d++)if(""!==c[d]){var i=this._boundaries&&this._boundaries[d]?this._boundaries[d].left:0;g.push('<tspan x="',e(i+h+this.shadow.offsetX,2),0===d||this.useNative?'" y':'" dy','="',e(this.useNative?a*d-this.height/2+this.shadow.offsetY:a+(0===d?this.shadow.offsetY:0),2),'" ',this._getFillAttributes(this.shadow.color),">",b.util.string.escapeXml(c[d]),"</tspan>"),h=1}else h++;return g},_getSVGTextAndBg:function(a,b,c){var d=[],e=[],f=1;this._setSVGBg(e);for(var g=0,h=c.length;h>g;g++)""!==c[g]?(this._setSVGTextLineText(c[g],g,d,a,f,e),f=1):f++,this.textBackgroundColor&&this._boundaries&&this._setSVGTextLineBg(e,g,b,a);return{textSpans:d,textBgRects:e}},_setSVGTextLineText:function(a,c,d,f,g){var h=this._boundaries&&this._boundaries[c]?e(this._boundaries[c].left,2):0;d.push('<tspan x="',h,'" ',0===c||this.useNative?"y":"dy",'="',e(this.useNative?f*c-this.height/2:f*g,2),'" ',this._getFillAttributes(this.fill),">",b.util.string.escapeXml(a),"</tspan>")},_setSVGTextLineBg:function(a,b,c,d){a.push("<rect ",this._getFillAttributes(this.textBackgroundColor),' x="',e(c+this._boundaries[b].left,2),'" y="',e(d*b-this.height/2,2),'" width="',e(this._boundaries[b].width,2),'" height="',e(this._boundaries[b].height,2),'"></rect>\n')},_setSVGBg:function(a){this.backgroundColor&&this._boundaries&&a.push("<rect ",this._getFillAttributes(this.backgroundColor),' x="',e(-this.width/2,2),'" y="',e(-this.height/2,2),'" width="',e(this.width,2),'" height="',e(this.height,2),'"></rect>')},_getFillAttributes:function(a){var c=a&&"string"==typeof a?new b.Color(a):"";return c&&c.getSource()&&1!==c.getAlpha()?'opacity="'+c.getAlpha()+'" fill="'+c.setAlpha(1).toRgb()+'"':'fill="'+a+'"'},_set:function(a,b){"fontFamily"===a&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+b+"$3")),this.callSuper("_set",a,b),a in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),b.Text.ATTRIBUTE_NAMES=b.SHARED_ATTRIBUTES.concat("x y dx dy font-family font-style font-weight font-size text-decoration text-anchor".split(" ")),b.Text.DEFAULT_SVG_FONT_SIZE=16,b.Text.fromElement=function(a,c){if(!a)return null;var d=b.parseAttributes(a,b.Text.ATTRIBUTE_NAMES);c=b.util.object.extend(c?b.util.object.clone(c):{},d),"dx"in d&&(c.left+=d.dx),"dy"in d&&(c.top+=d.dy),"fontSize"in c||(c.fontSize=b.Text.DEFAULT_SVG_FONT_SIZE),c.originX||(c.originX="left");var e=new b.Text(a.textContent,c),f=0;return"left"===e.originX&&(f=e.getWidth()/2),"right"===e.originX&&(f=-e.getWidth()/2),e.set({left:e.getLeft()+f,top:e.getTop()-e.getHeight()/2}),e},b.Text.fromObject=function(a){return new b.Text(a.text,d(a))},b.util.createAccessors(b.Text)}("undefined"!=typeof exports?exports:this)}).call({},window,document,html2canvas);
\ No newline at end of file