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
+ * @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 state to ,
+ // 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
+ * @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(""),(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;jg^"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&&dDate.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 ",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,";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(""),(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;j1&&(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&&dDate.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 ",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,";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("");
+
+ 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 ');
+
+ 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 ' '),
+ 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 dotted stroke in canvas
+ *
+ * @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, '&')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''')
+ .replace(//g, '>');
+ }
+
+ /**
+ * 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 <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 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(
+ '',
+ ''
+ );
+ }
+ }
+
+ 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(//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(//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
+ ''
+ //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 https://github.com/mjijackson
+ * @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 https://github.com/mjijackson
+ * @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
+ '\n'
+ //jscs:enable validateIndentation
+ ];
+ }
+ else if (this.type === 'radial') {
+ markup = [
+ //jscs:disable validateIndentation
+ '\n'
+ //jscs:enable validateIndentation
+ ];
+ }
+
+ for (var i = 0; i < this.colorStops.length; i++) {
+ markup.push(
+ //jscs:disable validateIndentation
+ '\n'
+ //jscs:enable validateIndentation
+ );
+ }
+
+ markup.push((this.type === 'linear' ? '\n' : '\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:
+ *
+ *
+ *
+ *
+ *
+ *
+ * OR
+ *
+ *
+ *
+ *
+ *
+ *
+ * OR
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * OR
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+
+ 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);
+ }
+ // 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 '' +
+ '' +
+ '';
+ },
+ /* _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 (
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '');
+ },
+ /* _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 <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}.
+ * Backwards incompatibility note: 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}.
+ * Backwards incompatibility note: 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 <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 Normal overlayImage with left/top = 0
+ * 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 overlayImage with different properties
+ * 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 Stretched overlayImage #1 - width/height correspond to canvas width/height
+ * 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 Stretched overlayImage #2 - width/height correspond to canvas width/height
+ * 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 Normal backgroundImage with left/top = 0
+ * 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 backgroundImage with different properties
+ * 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 Stretched backgroundImage #1 - width/height correspond to canvas width/height
+ * 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 Stretched backgroundImage #2 - width/height correspond to canvas width/height
+ * 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 Normal overlayColor - color value
+ * canvas.setOverlayColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
+ * @example fabric.Pattern used as overlayColor
+ * canvas.setOverlayColor({
+ * source: 'http://fabricjs.com/assets/escheresque_ste.png'
+ * }, canvas.renderAll.bind(canvas));
+ * @example fabric.Pattern used as overlayColor with repeat and offset
+ * 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 Normal backgroundColor - color value
+ * canvas.setBackgroundColor('rgba(255, 73, 64, 0.6)', canvas.renderAll.bind(canvas));
+ * @example fabric.Pattern used as backgroundColor
+ * canvas.setBackgroundColor({
+ * source: 'http://fabricjs.com/assets/escheresque_ste.png'
+ * }, canvas.renderAll.bind(canvas));
+ * @example fabric.Pattern used as backgroundColor with repeat and offset
+ * 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 <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 Normal SVG output
+ * var svg = canvas.toSVG();
+ * @example SVG output without preamble (without <?xml ../>)
+ * var svg = canvas.toSVG({suppressPreamble: true});
+ * @example SVG output with viewBox attribute
+ * var svg = canvas.toSVG({
+ * viewBox: {
+ * x: 100,
+ * y: 100,
+ * width: 200,
+ * height: 300
+ * }
+ * });
+ * @example SVG output with different encoding (default: UTF-8)
+ * var svg = canvas.toSVG({encoding: 'ISO-8859-1'});
+ * @example Modify SVG output with reviver function
+ * 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('');
+
+ return markup.join('');
+ },
+
+ /**
+ * @private
+ */
+ _setSVGPreamble: function(markup, options) {
+ if (!options.suppressPreamble) {
+ markup.push(
+ '',
+ '\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(
+ '/g,">")}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('','')}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(//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(//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=['"].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.xa.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)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=["\n']:"radial"===this.type&&(b=["\n']);for(var f=0;f\n');return b.push("linear"===this.type?"\n":"\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()),''},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"),''},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(""),c.join("")},_setSVGPreamble:function(a,b){b.suppressPreamble||a.push('','\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("