import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Example {
public static void main(String[] args) {
final String regex = "function\\s+(?<functionName>\\w+)\\s*\\((?<functionArguments>(?:[^()]+)*)?\\s*\\)\\s*(?<functionBody>\\{(?:[^\\{\\}]+|(?-1))*+\\})";
final String string = "/*\n"
+ " html2canvas 0.5.0-alpha1 <http://html2canvas.hertzen.com>\n"
+ " Copyright (c) 2015 Niklas von Hertzen\n\n"
+ " Released under MIT License\n"
+ "*/\n\n"
+ "(function(window, document, exports, global, define, undefined){\n\n"
+ "/*!\n"
+ " * @overview es6-promise - a tiny implementation of Promises/A+.\n"
+ " * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)\n"
+ " * @license Licensed under MIT license\n"
+ " * See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE\n"
+ " * @version 2.0.1\n"
+ " */\n\n"
+ "(function(){function r(a,b){n[l]=a;n[l+1]=b;l+=2;2===l&&A()}function s(a){return\"function\"===typeof a}function F(){return function(){process.nextTick(t)}}function G(){var a=0,b=new B(t),c=document.createTextNode(\"\");b.observe(c,{characterData:!0});return function(){c.data=a=++a%2}}function H(){var a=new MessageChannel;a.port1.onmessage=t;return function(){a.port2.postMessage(0)}}function I(){return function(){setTimeout(t,1)}}function t(){for(var a=0;a<l;a+=2)(0,n[a])(n[a+1]),n[a]=void 0,n[a+1]=void 0;\n"
+ "l=0}function p(){}function J(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function K(a,b,c){r(function(a){var e=!1,f=J(c,b,function(c){e||(e=!0,b!==c?q(a,c):m(a,c))},function(b){e||(e=!0,g(a,b))});!e&&f&&(e=!0,g(a,f))},a)}function L(a,b){1===b.a?m(a,b.b):2===a.a?g(a,b.b):u(b,void 0,function(b){q(a,b)},function(b){g(a,b)})}function q(a,b){if(a===b)g(a,new TypeError(\"You cannot resolve a promise with itself\"));else if(\"function\"===typeof b||\"object\"===typeof b&&null!==b)if(b.constructor===a.constructor)L(a,\n"
+ "b);else{var c;try{c=b.then}catch(d){v.error=d,c=v}c===v?g(a,v.error):void 0===c?m(a,b):s(c)?K(a,b,c):m(a,b)}else m(a,b)}function M(a){a.f&&a.f(a.b);x(a)}function m(a,b){void 0===a.a&&(a.b=b,a.a=1,0!==a.e.length&&r(x,a))}function g(a,b){void 0===a.a&&(a.a=2,a.b=b,r(M,a))}function u(a,b,c,d){var e=a.e,f=e.length;a.f=null;e[f]=b;e[f+1]=c;e[f+2]=d;0===f&&a.a&&r(x,a)}function x(a){var b=a.e,c=a.a;if(0!==b.length){for(var d,e,f=a.b,g=0;g<b.length;g+=3)d=b[g],e=b[g+c],d?C(c,d,e,f):e(f);a.e.length=0}}function D(){this.error=\n"
+ "null}function C(a,b,c,d){var e=s(c),f,k,h,l;if(e){try{f=c(d)}catch(n){y.error=n,f=y}f===y?(l=!0,k=f.error,f=null):h=!0;if(b===f){g(b,new TypeError(\"A promises callback cannot return that same promise.\"));return}}else f=d,h=!0;void 0===b.a&&(e&&h?q(b,f):l?g(b,k):1===a?m(b,f):2===a&&g(b,f))}function N(a,b){try{b(function(b){q(a,b)},function(b){g(a,b)})}catch(c){g(a,c)}}function k(a,b,c,d){this.n=a;this.c=new a(p,d);this.i=c;this.o(b)?(this.m=b,this.d=this.length=b.length,this.l(),0===this.length?m(this.c,\n"
+ "this.b):(this.length=this.length||0,this.k(),0===this.d&&m(this.c,this.b))):g(this.c,this.p())}function h(a){O++;this.b=this.a=void 0;this.e=[];if(p!==a){if(!s(a))throw new TypeError(\"You must pass a resolver function as the first argument to the promise constructor\");if(!(this instanceof h))throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\");N(this,a)}}var E=Array.isArray?Array.isArray:function(a){return\"[object Array]\"===\n"
+ "Object.prototype.toString.call(a)},l=0,w=\"undefined\"!==typeof window?window:{},B=w.MutationObserver||w.WebKitMutationObserver,w=\"undefined\"!==typeof Uint8ClampedArray&&\"undefined\"!==typeof importScripts&&\"undefined\"!==typeof MessageChannel,n=Array(1E3),A;A=\"undefined\"!==typeof process&&\"[object process]\"==={}.toString.call(process)?F():B?G():w?H():I();var v=new D,y=new D;k.prototype.o=function(a){return E(a)};k.prototype.p=function(){return Error(\"Array Methods must be provided an Array\")};k.prototype.l=\n"
+ "function(){this.b=Array(this.length)};k.prototype.k=function(){for(var a=this.length,b=this.c,c=this.m,d=0;void 0===b.a&&d<a;d++)this.j(c[d],d)};k.prototype.j=function(a,b){var c=this.n;\"object\"===typeof a&&null!==a?a.constructor===c&&void 0!==a.a?(a.f=null,this.g(a.a,b,a.b)):this.q(c.resolve(a),b):(this.d--,this.b[b]=this.h(a))};k.prototype.g=function(a,b,c){var d=this.c;void 0===d.a&&(this.d--,this.i&&2===a?g(d,c):this.b[b]=this.h(c));0===this.d&&m(d,this.b)};k.prototype.h=function(a){return a};\n"
+ "k.prototype.q=function(a,b){var c=this;u(a,void 0,function(a){c.g(1,b,a)},function(a){c.g(2,b,a)})};var O=0;h.all=function(a,b){return(new k(this,a,!0,b)).c};h.race=function(a,b){function c(a){q(e,a)}function d(a){g(e,a)}var e=new this(p,b);if(!E(a))return (g(e,new TypeError(\"You must pass an array to race.\")), e);for(var f=a.length,h=0;void 0===e.a&&h<f;h++)u(this.resolve(a[h]),void 0,c,d);return e};h.resolve=function(a,b){if(a&&\"object\"===typeof a&&a.constructor===this)return a;var c=new this(p,b);\n"
+ "q(c,a);return c};h.reject=function(a,b){var c=new this(p,b);g(c,a);return c};h.prototype={constructor:h,then:function(a,b){var c=this.a;if(1===c&&!a||2===c&&!b)return this;var d=new this.constructor(p),e=this.b;if(c){var f=arguments[c-1];r(function(){C(c,d,f,e)})}else u(this,d,a,b);return d},\"catch\":function(a){return this.then(null,a)}};var z={Promise:h,polyfill:function(){var a;a=\"undefined\"!==typeof global?global:\"undefined\"!==typeof window&&window.document?window:self;\"Promise\"in a&&\"resolve\"in\n"
+ "a.Promise&&\"reject\"in a.Promise&&\"all\"in a.Promise&&\"race\"in a.Promise&&function(){var b;new a.Promise(function(a){b=a});return s(b)}()||(a.Promise=h)}};\"function\"===typeof define&&define.amd?define(function(){return z}):\"undefined\"!==typeof module&&module.exports?module.exports=z:\"undefined\"!==typeof this&&(this.ES6Promise=z);}).call(window);\n"
+ "if (window) {\n"
+ " window.ES6Promise.polyfill();\n"
+ "}\n\n\n"
+ "if (typeof(document) === \"undefined\" || typeof(Object.create) !== \"function\" || typeof(document.createElement(\"canvas\").getContext) !== \"function\") {\n"
+ " (window || module.exports).html2canvas = function() {\n"
+ " return Promise.reject(\"No canvas support\");\n"
+ " };\n"
+ " return;\n"
+ "}\n\n"
+ "/*! https://mths.be/punycode v1.3.1 by @mathias */\n"
+ ";(function(root) {\n\n"
+ " /** Detect free variables */\n"
+ " var freeExports = typeof exports == 'object' && exports &&\n"
+ " !exports.nodeType && exports;\n"
+ " var freeModule = typeof module == 'object' && module &&\n"
+ " !module.nodeType && module;\n"
+ " var freeGlobal = typeof global == 'object' && global;\n"
+ " if (\n"
+ " freeGlobal.global === freeGlobal ||\n"
+ " freeGlobal.window === freeGlobal ||\n"
+ " freeGlobal.self === freeGlobal\n"
+ " ) {\n"
+ " root = freeGlobal;\n"
+ " }\n\n"
+ " /**\n"
+ " * The `punycode` object.\n"
+ " * @name punycode\n"
+ " * @type Object\n"
+ " */\n"
+ " var punycode,\n\n"
+ " /** Highest positive signed 32-bit float value */\n"
+ " maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1\n\n"
+ " /** Bootstring parameters */\n"
+ " base = 36,\n"
+ " tMin = 1,\n"
+ " tMax = 26,\n"
+ " skew = 38,\n"
+ " damp = 700,\n"
+ " initialBias = 72,\n"
+ " initialN = 128, // 0x80\n"
+ " delimiter = '-', // '\\x2D'\n\n"
+ " /** Regular expressions */\n"
+ " regexPunycode = /^xn--/,\n"
+ " regexNonASCII = /[^\\x20-\\x7E]/, // unprintable ASCII chars + non-ASCII chars\n"
+ " regexSeparators = /[\\x2E\\u3002\\uFF0E\\uFF61]/g, // RFC 3490 separators\n\n"
+ " /** Error messages */\n"
+ " errors = {\n"
+ " 'overflow': 'Overflow: input needs wider integers to process',\n"
+ " 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',\n"
+ " 'invalid-input': 'Invalid input'\n"
+ " },\n\n"
+ " /** Convenience shortcuts */\n"
+ " baseMinusTMin = base - tMin,\n"
+ " floor = Math.floor,\n"
+ " stringFromCharCode = String.fromCharCode,\n\n"
+ " /** Temporary variable */\n"
+ " key;\n\n"
+ " /*--------------------------------------------------------------------------*/\n\n"
+ " /**\n"
+ " * A generic error utility function.\n"
+ " * @private\n"
+ " * @param {String} type The error type.\n"
+ " * @returns {Error} Throws a `RangeError` with the applicable error message.\n"
+ " */\n"
+ " function error(type) {\n"
+ " throw RangeError(errors[type]);\n"
+ " }\n\n"
+ " /**\n"
+ " * A generic `Array#map` utility function.\n"
+ " * @private\n"
+ " * @param {Array} array The array to iterate over.\n"
+ " * @param {Function} callback The function that gets called for every array\n"
+ " * item.\n"
+ " * @returns {Array} A new array of values returned by the callback function.\n"
+ " */\n"
+ " function map(array, fn) {\n"
+ " var length = array.length;\n"
+ " var result = [];\n"
+ " while (length--) {\n"
+ " result[length] = fn(array[length]);\n"
+ " }\n"
+ " return result;\n"
+ " }\n\n"
+ " /**\n"
+ " * A simple `Array#map`-like wrapper to work with domain name strings or email\n"
+ " * addresses.\n"
+ " * @private\n"
+ " * @param {String} domain The domain name or email address.\n"
+ " * @param {Function} callback The function that gets called for every\n"
+ " * character.\n"
+ " * @returns {Array} A new string of characters returned by the callback\n"
+ " * function.\n"
+ " */\n"
+ " function mapDomain(string, fn) {\n"
+ " var parts = string.split('@');\n"
+ " var result = '';\n"
+ " if (parts.length > 1) {\n"
+ " // In email addresses, only the domain name should be punycoded. Leave\n"
+ " // the local part (i.e. everything up to `@`) intact.\n"
+ " result = parts[0] + '@';\n"
+ " string = parts[1];\n"
+ " }\n"
+ " var labels = string.split(regexSeparators);\n"
+ " var encoded = map(labels, fn).join('.');\n"
+ " return result + encoded;\n"
+ " }\n\n"
+ " /**\n"
+ " * Creates an array containing the numeric code points of each Unicode\n"
+ " * character in the string. While JavaScript uses UCS-2 internally,\n"
+ " * this function will convert a pair of surrogate halves (each of which\n"
+ " * UCS-2 exposes as separate characters) into a single code point,\n"
+ " * matching UTF-16.\n"
+ " * @see `punycode.ucs2.encode`\n"
+ " * @see <https://mathiasbynens.be/notes/javascript-encoding>\n"
+ " * @memberOf punycode.ucs2\n"
+ " * @name decode\n"
+ " * @param {String} string The Unicode input string (UCS-2).\n"
+ " * @returns {Array} The new array of code points.\n"
+ " */\n"
+ " function ucs2decode(string) {\n"
+ " var output = [],\n"
+ " counter = 0,\n"
+ " length = string.length,\n"
+ " value,\n"
+ " extra;\n"
+ " while (counter < length) {\n"
+ " value = string.charCodeAt(counter++);\n"
+ " if (value >= 0xD800 && value <= 0xDBFF && counter < length) {\n"
+ " // high surrogate, and there is a next character\n"
+ " extra = string.charCodeAt(counter++);\n"
+ " if ((extra & 0xFC00) == 0xDC00) { // low surrogate\n"
+ " output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);\n"
+ " } else {\n"
+ " // unmatched surrogate; only append this code unit, in case the next\n"
+ " // code unit is the high surrogate of a surrogate pair\n"
+ " output.push(value);\n"
+ " counter--;\n"
+ " }\n"
+ " } else {\n"
+ " output.push(value);\n"
+ " }\n"
+ " }\n"
+ " return output;\n"
+ " }\n\n"
+ " /**\n"
+ " * Creates a string based on an array of numeric code points.\n"
+ " * @see `punycode.ucs2.decode`\n"
+ " * @memberOf punycode.ucs2\n"
+ " * @name encode\n"
+ " * @param {Array} codePoints The array of numeric code points.\n"
+ " * @returns {String} The new Unicode string (UCS-2).\n"
+ " */\n"
+ " function ucs2encode(array) {\n"
+ " return map(array, function(value) {\n"
+ " var output = '';\n"
+ " if (value > 0xFFFF) {\n"
+ " value -= 0x10000;\n"
+ " output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);\n"
+ " value = 0xDC00 | value & 0x3FF;\n"
+ " }\n"
+ " output += stringFromCharCode(value);\n"
+ " return output;\n"
+ " }).join('');\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a basic code point into a digit/integer.\n"
+ " * @see `digitToBasic()`\n"
+ " * @private\n"
+ " * @param {Number} codePoint The basic numeric code point value.\n"
+ " * @returns {Number} The numeric value of a basic code point (for use in\n"
+ " * representing integers) in the range `0` to `base - 1`, or `base` if\n"
+ " * the code point does not represent a value.\n"
+ " */\n"
+ " function basicToDigit(codePoint) {\n"
+ " if (codePoint - 48 < 10) {\n"
+ " return codePoint - 22;\n"
+ " }\n"
+ " if (codePoint - 65 < 26) {\n"
+ " return codePoint - 65;\n"
+ " }\n"
+ " if (codePoint - 97 < 26) {\n"
+ " return codePoint - 97;\n"
+ " }\n"
+ " return base;\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a digit/integer into a basic code point.\n"
+ " * @see `basicToDigit()`\n"
+ " * @private\n"
+ " * @param {Number} digit The numeric value of a basic code point.\n"
+ " * @returns {Number} The basic code point whose value (when used for\n"
+ " * representing integers) is `digit`, which needs to be in the range\n"
+ " * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is\n"
+ " * used; else, the lowercase form is used. The behavior is undefined\n"
+ " * if `flag` is non-zero and `digit` has no uppercase form.\n"
+ " */\n"
+ " function digitToBasic(digit, flag) {\n"
+ " // 0..25 map to ASCII a..z or A..Z\n"
+ " // 26..35 map to ASCII 0..9\n"
+ " return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);\n"
+ " }\n\n"
+ " /**\n"
+ " * Bias adaptation function as per section 3.4 of RFC 3492.\n"
+ " * http://tools.ietf.org/html/rfc3492#section-3.4\n"
+ " * @private\n"
+ " */\n"
+ " function adapt(delta, numPoints, firstTime) {\n"
+ " var k = 0;\n"
+ " delta = firstTime ? floor(delta / damp) : delta >> 1;\n"
+ " delta += floor(delta / numPoints);\n"
+ " for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {\n"
+ " delta = floor(delta / baseMinusTMin);\n"
+ " }\n"
+ " return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a Punycode string of ASCII-only symbols to a string of Unicode\n"
+ " * symbols.\n"
+ " * @memberOf punycode\n"
+ " * @param {String} input The Punycode string of ASCII-only symbols.\n"
+ " * @returns {String} The resulting string of Unicode symbols.\n"
+ " */\n"
+ " function decode(input) {\n"
+ " // Don't use UCS-2\n"
+ " var output = [],\n"
+ " inputLength = input.length,\n"
+ " out,\n"
+ " i = 0,\n"
+ " n = initialN,\n"
+ " bias = initialBias,\n"
+ " basic,\n"
+ " j,\n"
+ " index,\n"
+ " oldi,\n"
+ " w,\n"
+ " k,\n"
+ " digit,\n"
+ " t,\n"
+ " /** Cached calculation results */\n"
+ " baseMinusT;\n\n"
+ " // Handle the basic code points: let `basic` be the number of input code\n"
+ " // points before the last delimiter, or `0` if there is none, then copy\n"
+ " // the first basic code points to the output.\n\n"
+ " basic = input.lastIndexOf(delimiter);\n"
+ " if (basic < 0) {\n"
+ " basic = 0;\n"
+ " }\n\n"
+ " for (j = 0; j < basic; ++j) {\n"
+ " // if it's not a basic code point\n"
+ " if (input.charCodeAt(j) >= 0x80) {\n"
+ " error('not-basic');\n"
+ " }\n"
+ " output.push(input.charCodeAt(j));\n"
+ " }\n\n"
+ " // Main decoding loop: start just after the last delimiter if any basic code\n"
+ " // points were copied; start at the beginning otherwise.\n\n"
+ " for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {\n\n"
+ " // `index` is the index of the next character to be consumed.\n"
+ " // Decode a generalized variable-length integer into `delta`,\n"
+ " // which gets added to `i`. The overflow checking is easier\n"
+ " // if we increase `i` as we go, then subtract off its starting\n"
+ " // value at the end to obtain `delta`.\n"
+ " for (oldi = i, w = 1, k = base; /* no condition */; k += base) {\n\n"
+ " if (index >= inputLength) {\n"
+ " error('invalid-input');\n"
+ " }\n\n"
+ " digit = basicToDigit(input.charCodeAt(index++));\n\n"
+ " if (digit >= base || digit > floor((maxInt - i) / w)) {\n"
+ " error('overflow');\n"
+ " }\n\n"
+ " i += digit * w;\n"
+ " t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);\n\n"
+ " if (digit < t) {\n"
+ " break;\n"
+ " }\n\n"
+ " baseMinusT = base - t;\n"
+ " if (w > floor(maxInt / baseMinusT)) {\n"
+ " error('overflow');\n"
+ " }\n\n"
+ " w *= baseMinusT;\n\n"
+ " }\n\n"
+ " out = output.length + 1;\n"
+ " bias = adapt(i - oldi, out, oldi == 0);\n\n"
+ " // `i` was supposed to wrap around from `out` to `0`,\n"
+ " // incrementing `n` each time, so we'll fix that now:\n"
+ " if (floor(i / out) > maxInt - n) {\n"
+ " error('overflow');\n"
+ " }\n\n"
+ " n += floor(i / out);\n"
+ " i %= out;\n\n"
+ " // Insert `n` at position `i` of the output\n"
+ " output.splice(i++, 0, n);\n\n"
+ " }\n\n"
+ " return ucs2encode(output);\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a string of Unicode symbols (e.g. a domain name label) to a\n"
+ " * Punycode string of ASCII-only symbols.\n"
+ " * @memberOf punycode\n"
+ " * @param {String} input The string of Unicode symbols.\n"
+ " * @returns {String} The resulting Punycode string of ASCII-only symbols.\n"
+ " */\n"
+ " function encode(input) {\n"
+ " var n,\n"
+ " delta,\n"
+ " handledCPCount,\n"
+ " basicLength,\n"
+ " bias,\n"
+ " j,\n"
+ " m,\n"
+ " q,\n"
+ " k,\n"
+ " t,\n"
+ " currentValue,\n"
+ " output = [],\n"
+ " /** `inputLength` will hold the number of code points in `input`. */\n"
+ " inputLength,\n"
+ " /** Cached calculation results */\n"
+ " handledCPCountPlusOne,\n"
+ " baseMinusT,\n"
+ " qMinusT;\n\n"
+ " // Convert the input in UCS-2 to Unicode\n"
+ " input = ucs2decode(input);\n\n"
+ " // Cache the length\n"
+ " inputLength = input.length;\n\n"
+ " // Initialize the state\n"
+ " n = initialN;\n"
+ " delta = 0;\n"
+ " bias = initialBias;\n\n"
+ " // Handle the basic code points\n"
+ " for (j = 0; j < inputLength; ++j) {\n"
+ " currentValue = input[j];\n"
+ " if (currentValue < 0x80) {\n"
+ " output.push(stringFromCharCode(currentValue));\n"
+ " }\n"
+ " }\n\n"
+ " handledCPCount = basicLength = output.length;\n\n"
+ " // `handledCPCount` is the number of code points that have been handled;\n"
+ " // `basicLength` is the number of basic code points.\n\n"
+ " // Finish the basic string - if it is not empty - with a delimiter\n"
+ " if (basicLength) {\n"
+ " output.push(delimiter);\n"
+ " }\n\n"
+ " // Main encoding loop:\n"
+ " while (handledCPCount < inputLength) {\n\n"
+ " // All non-basic code points < n have been handled already. Find the next\n"
+ " // larger one:\n"
+ " for (m = maxInt, j = 0; j < inputLength; ++j) {\n"
+ " currentValue = input[j];\n"
+ " if (currentValue >= n && currentValue < m) {\n"
+ " m = currentValue;\n"
+ " }\n"
+ " }\n\n"
+ " // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,\n"
+ " // but guard against overflow\n"
+ " handledCPCountPlusOne = handledCPCount + 1;\n"
+ " if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {\n"
+ " error('overflow');\n"
+ " }\n\n"
+ " delta += (m - n) * handledCPCountPlusOne;\n"
+ " n = m;\n\n"
+ " for (j = 0; j < inputLength; ++j) {\n"
+ " currentValue = input[j];\n\n"
+ " if (currentValue < n && ++delta > maxInt) {\n"
+ " error('overflow');\n"
+ " }\n\n"
+ " if (currentValue == n) {\n"
+ " // Represent delta as a generalized variable-length integer\n"
+ " for (q = delta, k = base; /* no condition */; k += base) {\n"
+ " t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);\n"
+ " if (q < t) {\n"
+ " break;\n"
+ " }\n"
+ " qMinusT = q - t;\n"
+ " baseMinusT = base - t;\n"
+ " output.push(\n"
+ " stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))\n"
+ " );\n"
+ " q = floor(qMinusT / baseMinusT);\n"
+ " }\n\n"
+ " output.push(stringFromCharCode(digitToBasic(q, 0)));\n"
+ " bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);\n"
+ " delta = 0;\n"
+ " ++handledCPCount;\n"
+ " }\n"
+ " }\n\n"
+ " ++delta;\n"
+ " ++n;\n\n"
+ " }\n"
+ " return output.join('');\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a Punycode string representing a domain name or an email address\n"
+ " * to Unicode. Only the Punycoded parts of the input will be converted, i.e.\n"
+ " * it doesn't matter if you call it on a string that has already been\n"
+ " * converted to Unicode.\n"
+ " * @memberOf punycode\n"
+ " * @param {String} input The Punycoded domain name or email address to\n"
+ " * convert to Unicode.\n"
+ " * @returns {String} The Unicode representation of the given Punycode\n"
+ " * string.\n"
+ " */\n"
+ " function toUnicode(input) {\n"
+ " return mapDomain(input, function(string) {\n"
+ " return regexPunycode.test(string)\n"
+ " ? decode(string.slice(4).toLowerCase())\n"
+ " : string;\n"
+ " });\n"
+ " }\n\n"
+ " /**\n"
+ " * Converts a Unicode string representing a domain name or an email address to\n"
+ " * Punycode. Only the non-ASCII parts of the domain name will be converted,\n"
+ " * i.e. it doesn't matter if you call it with a domain that's already in\n"
+ " * ASCII.\n"
+ " * @memberOf punycode\n"
+ " * @param {String} input The domain name or email address to convert, as a\n"
+ " * Unicode string.\n"
+ " * @returns {String} The Punycode representation of the given domain name or\n"
+ " * email address.\n"
+ " */\n"
+ " function toASCII(input) {\n"
+ " return mapDomain(input, function(string) {\n"
+ " return regexNonASCII.test(string)\n"
+ " ? 'xn--' + encode(string)\n"
+ " : string;\n"
+ " });\n"
+ " }\n\n"
+ " /*--------------------------------------------------------------------------*/\n\n"
+ " /** Define the public API */\n"
+ " punycode = {\n"
+ " /**\n"
+ " * A string representing the current Punycode.js version number.\n"
+ " * @memberOf punycode\n"
+ " * @type String\n"
+ " */\n"
+ " 'version': '1.3.1',\n"
+ " /**\n"
+ " * An object of methods to convert from JavaScript's internal character\n"
+ " * representation (UCS-2) to Unicode code points, and back.\n"
+ " * @see <https://mathiasbynens.be/notes/javascript-encoding>\n"
+ " * @memberOf punycode\n"
+ " * @type Object\n"
+ " */\n"
+ " 'ucs2': {\n"
+ " 'decode': ucs2decode,\n"
+ " 'encode': ucs2encode\n"
+ " },\n"
+ " 'decode': decode,\n"
+ " 'encode': encode,\n"
+ " 'toASCII': toASCII,\n"
+ " 'toUnicode': toUnicode\n"
+ " };\n\n"
+ " /** Expose `punycode` */\n"
+ " // Some AMD build optimizers, like r.js, check for specific condition patterns\n"
+ " // like the following:\n"
+ " if (\n"
+ " typeof define == 'function' &&\n"
+ " typeof define.amd == 'object' &&\n"
+ " define.amd\n"
+ " ) {\n"
+ " define('punycode', function() {\n"
+ " return punycode;\n"
+ " });\n"
+ " } else if (freeExports && freeModule) {\n"
+ " if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+\n"
+ " freeModule.exports = punycode;\n"
+ " } else { // in Narwhal or RingoJS v0.7.0-\n"
+ " for (key in punycode) {\n"
+ " punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);\n"
+ " }\n"
+ " }\n"
+ " } else { // in Rhino or a web browser\n"
+ " root.punycode = punycode;\n"
+ " }\n\n"
+ "}(this));\n\n"
+ "var html2canvasNodeAttribute = \"data-html2canvas-node\";\n"
+ "var html2canvasCanvasCloneAttribute = \"data-html2canvas-canvas-clone\";\n"
+ "var html2canvasCanvasCloneIndex = 0;\n"
+ "var html2canvasCloneIndex = 0;\n\n"
+ "window.html2canvas = function(nodeList, options) {\n"
+ " var index = html2canvasCloneIndex++;\n"
+ " options = options || {};\n"
+ " if (options.logging) {\n"
+ " window.html2canvas.logging = true;\n"
+ " window.html2canvas.start = Date.now();\n"
+ " }\n\n"
+ " options.async = typeof(options.async) === \"undefined\" ? true : options.async;\n"
+ " options.allowTaint = typeof(options.allowTaint) === \"undefined\" ? false : options.allowTaint;\n"
+ " options.removeContainer = typeof(options.removeContainer) === \"undefined\" ? true : options.removeContainer;\n"
+ " options.javascriptEnabled = typeof(options.javascriptEnabled) === \"undefined\" ? false : options.javascriptEnabled;\n"
+ " options.imageTimeout = typeof(options.imageTimeout) === \"undefined\" ? 10000 : options.imageTimeout;\n"
+ " options.renderer = typeof(options.renderer) === \"function\" ? options.renderer : CanvasRenderer;\n"
+ " options.strict = !!options.strict;\n\n"
+ " if (typeof(nodeList) === \"string\") {\n"
+ " if (typeof(options.proxy) !== \"string\") {\n"
+ " return Promise.reject(\"Proxy must be used when rendering url\");\n"
+ " }\n"
+ " var width = options.width != null ? options.width : window.innerWidth;\n"
+ " var height = options.height != null ? options.height : window.innerHeight;\n"
+ " return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) {\n"
+ " return renderWindow(container.contentWindow.document.documentElement, container, options, width, height);\n"
+ " });\n"
+ " }\n\n"
+ " var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0];\n"
+ " node.setAttribute(html2canvasNodeAttribute + index, index);\n"
+ " return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {\n"
+ " if (typeof(options.onrendered) === \"function\") {\n"
+ " log(\"options.onrendered is deprecated, html2canvas returns a Promise containing the canvas\");\n"
+ " options.onrendered(canvas);\n"
+ " }\n"
+ " return canvas;\n"
+ " });\n"
+ "};\n\n"
+ "window.html2canvas.punycode = this.punycode;\n"
+ "window.html2canvas.proxy = {};\n\n"
+ "function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) {\n"
+ " return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) {\n"
+ " log(\"Document cloned\");\n"
+ " var attributeName = html2canvasNodeAttribute + html2canvasIndex;\n"
+ " var selector = \"[\" + attributeName + \"='\" + html2canvasIndex + \"']\";\n"
+ " document.querySelector(selector).removeAttribute(attributeName);\n"
+ " var clonedWindow = container.contentWindow;\n"
+ " var node = clonedWindow.document.querySelector(selector);\n"
+ " var oncloneHandler = (typeof(options.onclone) === \"function\") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true);\n"
+ " return oncloneHandler.then(function() {\n"
+ " return renderWindow(node, container, options, windowWidth, windowHeight);\n"
+ " });\n"
+ " });\n"
+ "}\n\n"
+ "function renderWindow(node, container, options, windowWidth, windowHeight) {\n"
+ " var clonedWindow = container.contentWindow;\n"
+ " var support = new Support(clonedWindow.document);\n"
+ " var imageLoader = new ImageLoader(options, support);\n"
+ " var bounds = getBounds(node);\n"
+ " var width = options.type === \"view\" ? windowWidth : documentWidth(clonedWindow.document);\n"
+ " var height = options.type === \"view\" ? windowHeight : documentHeight(clonedWindow.document);\n"
+ " var renderer = new options.renderer(width, height, imageLoader, options, document);\n"
+ " var parser = new NodeParser(node, renderer, support, imageLoader, options);\n"
+ " return parser.ready.then(function() {\n"
+ " log(\"Finished rendering\");\n"
+ " var canvas;\n\n"
+ " if (options.type === \"view\") {\n"
+ " canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});\n"
+ " } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) {\n"
+ " canvas = renderer.canvas;\n"
+ " } else {\n"
+ " canvas = crop(renderer.canvas, {width: options.width != null ? options.width : bounds.width, height: options.height != null ? options.height : bounds.height, top: bounds.top, left: bounds.left, x: clonedWindow.pageXOffset, y: clonedWindow.pageYOffset});\n"
+ " }\n\n"
+ " cleanupContainer(container, options);\n"
+ " return canvas;\n"
+ " });\n"
+ "}\n\n"
+ "function cleanupContainer(container, options) {\n"
+ " if (options.removeContainer) {\n"
+ " container.parentNode.removeChild(container);\n"
+ " log(\"Cleaned up container\");\n"
+ " }\n"
+ "}\n\n"
+ "function crop(canvas, bounds) {\n"
+ " var croppedCanvas = document.createElement(\"canvas\");\n"
+ " var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left));\n"
+ " var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width));\n"
+ " var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top));\n"
+ " var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height));\n"
+ " croppedCanvas.width = bounds.width;\n"
+ " croppedCanvas.height = bounds.height;\n"
+ " log(\"Cropping canvas at:\", \"left:\", bounds.left, \"top:\", bounds.top, \"width:\", (x2-x1), \"height:\", (y2-y1));\n"
+ " log(\"Resulting crop with width\", bounds.width, \"and height\", bounds.height, \" with x\", x1, \"and y\", y1);\n"
+ " croppedCanvas.getContext(\"2d\").drawImage(canvas, x1, y1, x2-x1, y2-y1, bounds.x, bounds.y, x2-x1, y2-y1);\n"
+ " return croppedCanvas;\n"
+ "}\n\n"
+ "function documentWidth (doc) {\n"
+ " return Math.max(\n"
+ " Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),\n"
+ " Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),\n"
+ " Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)\n"
+ " );\n"
+ "}\n\n"
+ "function documentHeight (doc) {\n"
+ " return Math.max(\n"
+ " Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),\n"
+ " Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),\n"
+ " Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)\n"
+ " );\n"
+ "}\n\n"
+ "function smallImage() {\n"
+ " return \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\";\n"
+ "}\n\n"
+ "function isIE9() {\n"
+ " return document.documentMode && document.documentMode <= 9;\n"
+ "}\n\n"
+ "// https://github.com/niklasvh/html2canvas/issues/503\n"
+ "function cloneNodeIE9(node, javascriptEnabled) {\n"
+ " var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);\n\n"
+ " var child = node.firstChild;\n"
+ " while(child) {\n"
+ " if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {\n"
+ " clone.appendChild(cloneNodeIE9(child, javascriptEnabled));\n"
+ " }\n"
+ " child = child.nextSibling;\n"
+ " }\n\n"
+ " return clone;\n"
+ "}\n\n"
+ "function createWindowClone(ownerDocument, containerDocument, width, height, options, x ,y) {\n"
+ " labelCanvasElements(ownerDocument);\n"
+ " var documentElement = isIE9() ? cloneNodeIE9(ownerDocument.documentElement, options.javascriptEnabled) : ownerDocument.documentElement.cloneNode(true);\n"
+ " var container = containerDocument.createElement(\"iframe\");\n\n"
+ " container.className = \"html2canvas-container\";\n"
+ " container.style.visibility = \"hidden\";\n"
+ " container.style.position = \"fixed\";\n"
+ " container.style.left = \"-10000px\";\n"
+ " container.style.top = \"0px\";\n"
+ " container.style.border = \"0\";\n"
+ " container.width = width;\n"
+ " container.height = height;\n"
+ " container.scrolling = \"no\"; // ios won't scroll without it\n"
+ " containerDocument.body.appendChild(container);\n\n"
+ " return new Promise(function(resolve) {\n"
+ " var documentClone = container.contentWindow.document;\n\n"
+ " cloneNodeValues(ownerDocument.documentElement, documentElement, \"textarea\");\n"
+ " cloneNodeValues(ownerDocument.documentElement, documentElement, \"select\");\n\n"
+ " /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle\n"
+ " if window url is about:blank, we can assign the url to current by writing onto the document\n"
+ " */\n"
+ " container.contentWindow.onload = container.onload = function() {\n"
+ " var interval = setInterval(function() {\n"
+ " if (documentClone.body.childNodes.length > 0) {\n"
+ " cloneCanvasContents(ownerDocument, documentClone);\n"
+ " clearInterval(interval);\n"
+ " if (options.type === \"view\") {\n"
+ " container.contentWindow.scrollTo(x, y);\n"
+ " }\n"
+ " resolve(container);\n"
+ " }\n"
+ " }, 50);\n"
+ " };\n\n"
+ " documentClone.open();\n"
+ " documentClone.write(\"<!DOCTYPE html><html></html>\");\n"
+ " // Chrome scrolls the parent document for some reason after the write to the cloned window???\n"
+ " restoreOwnerScroll(ownerDocument, x, y);\n"
+ " documentClone.replaceChild(options.javascriptEnabled === true ? documentClone.adoptNode(documentElement) : removeScriptNodes(documentClone.adoptNode(documentElement)), documentClone.documentElement);\n"
+ " documentClone.close();\n"
+ " });\n"
+ "}\n\n"
+ "function cloneNodeValues(document, clone, nodeName) {\n"
+ " var originalNodes = document.getElementsByTagName(nodeName);\n"
+ " var clonedNodes = clone.getElementsByTagName(nodeName);\n"
+ " var count = originalNodes.length;\n"
+ " for (var i = 0; i < count; i++) {\n"
+ " clonedNodes[i].value = originalNodes[i].value;\n"
+ " }\n"
+ "}\n\n"
+ "function restoreOwnerScroll(ownerDocument, x, y) {\n"
+ " if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {\n"
+ " ownerDocument.defaultView.scrollTo(x, y);\n"
+ " }\n"
+ "}\n\n"
+ "function loadUrlDocument(src, proxy, document, width, height, options) {\n"
+ " return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) {\n"
+ " return createWindowClone(doc, document, width, height, options, 0, 0);\n"
+ " });\n"
+ "}\n\n"
+ "function documentFromHTML(src) {\n"
+ " return function(html) {\n"
+ " var parser = new DOMParser(), doc;\n"
+ " try {\n"
+ " doc = parser.parseFromString(html, \"text/html\");\n"
+ " } catch(e) {\n"
+ " log(\"DOMParser not supported, falling back to createHTMLDocument\");\n"
+ " doc = document.implementation.createHTMLDocument(\"\");\n"
+ " try {\n"
+ " doc.open();\n"
+ " doc.write(html);\n"
+ " doc.close();\n"
+ " } catch(ee) {\n"
+ " log(\"createHTMLDocument write not supported, falling back to document.body.innerHTML\");\n"
+ " doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement\n"
+ " }\n"
+ " }\n\n"
+ " var b = doc.querySelector(\"base\");\n"
+ " if (!b || !b.href.host) {\n"
+ " var base = doc.createElement(\"base\");\n"
+ " base.href = src;\n"
+ " doc.head.insertBefore(base, doc.head.firstChild);\n"
+ " }\n\n"
+ " return doc;\n"
+ " };\n"
+ "}\n\n\n"
+ "function labelCanvasElements(ownerDocument) {\n"
+ " [].slice.call(ownerDocument.querySelectorAll(\"canvas\"), 0).forEach(function(canvas) {\n"
+ " canvas.setAttribute(html2canvasCanvasCloneAttribute, \"canvas-\" + html2canvasCanvasCloneIndex++);\n"
+ " });\n"
+ "}\n\n"
+ "function cloneCanvasContents(ownerDocument, documentClone) {\n"
+ " [].slice.call(ownerDocument.querySelectorAll(\"[\" + html2canvasCanvasCloneAttribute + \"]\"), 0).forEach(function(canvas) {\n"
+ " try {\n"
+ " var clonedCanvas = documentClone.querySelector('[' + html2canvasCanvasCloneAttribute + '=\"' + canvas.getAttribute(html2canvasCanvasCloneAttribute) + '\"]');\n"
+ " if (clonedCanvas) {\n"
+ " clonedCanvas.width = canvas.width;\n"
+ " clonedCanvas.height = canvas.height;\n"
+ " clonedCanvas.getContext(\"2d\").putImageData(canvas.getContext(\"2d\").getImageData(0, 0, canvas.width, canvas.height), 0, 0);\n"
+ " }\n"
+ " } catch(e) {\n"
+ " log(\"Unable to copy canvas content from\", canvas, e);\n"
+ " }\n"
+ " canvas.removeAttribute(html2canvasCanvasCloneAttribute);\n"
+ " });\n"
+ "}\n\n"
+ "function removeScriptNodes(parent) {\n"
+ " [].slice.call(parent.childNodes, 0).filter(isElementNode).forEach(function(node) {\n"
+ " if (node.tagName === \"SCRIPT\") {\n"
+ " parent.removeChild(node);\n"
+ " } else {\n"
+ " removeScriptNodes(node);\n"
+ " }\n"
+ " });\n"
+ " return parent;\n"
+ "}\n\n"
+ "function isElementNode(node) {\n"
+ " return node.nodeType === Node.ELEMENT_NODE;\n"
+ "}\n\n"
+ "function absoluteUrl(url) {\n"
+ " var link = document.createElement(\"a\");\n"
+ " link.href = url;\n"
+ " link.href = link.href;\n"
+ " return link;\n"
+ "}\n\n"
+ "// http://dev.w3.org/csswg/css-color/\n\n"
+ "function Color(value) {\n"
+ " this.r = 0;\n"
+ " this.g = 0;\n"
+ " this.b = 0;\n"
+ " this.a = null;\n"
+ " var result = this.fromArray(value) ||\n"
+ " this.namedColor(value) ||\n"
+ " this.rgb(value) ||\n"
+ " this.rgba(value) ||\n"
+ " this.hex6(value) ||\n"
+ " this.hex3(value);\n"
+ "}\n\n"
+ "Color.prototype.darken = function(amount) {\n"
+ " var a = 1 - amount;\n"
+ " return new Color([\n"
+ " Math.round(this.r * a),\n"
+ " Math.round(this.g * a),\n"
+ " Math.round(this.b * a),\n"
+ " this.a\n"
+ " ]);\n"
+ "};\n\n"
+ "Color.prototype.isTransparent = function() {\n"
+ " return this.a === 0;\n"
+ "};\n\n"
+ "Color.prototype.isBlack = function() {\n"
+ " return this.r === 0 && this.g === 0 && this.b === 0;\n"
+ "};\n\n"
+ "Color.prototype.fromArray = function(array) {\n"
+ " if (Array.isArray(array)) {\n"
+ " this.r = Math.min(array[0], 255);\n"
+ " this.g = Math.min(array[1], 255);\n"
+ " this.b = Math.min(array[2], 255);\n"
+ " if (array.length > 3) {\n"
+ " this.a = array[3];\n"
+ " }\n"
+ " }\n\n"
+ " return (Array.isArray(array));\n"
+ "};\n\n"
+ "var _hex3 = /^#([a-f0-9]{3})$/i;\n\n"
+ "Color.prototype.hex3 = function(value) {\n"
+ " var match = null;\n"
+ " if ((match = value.match(_hex3)) !== null) {\n"
+ " this.r = parseInt(match[1][0] + match[1][0], 16);\n"
+ " this.g = parseInt(match[1][1] + match[1][1], 16);\n"
+ " this.b = parseInt(match[1][2] + match[1][2], 16);\n"
+ " }\n"
+ " return match !== null;\n"
+ "};\n\n"
+ "var _hex6 = /^#([a-f0-9]{6})$/i;\n\n"
+ "Color.prototype.hex6 = function(value) {\n"
+ " var match = null;\n"
+ " if ((match = value.match(_hex6)) !== null) {\n"
+ " this.r = parseInt(match[1].substring(0, 2), 16);\n"
+ " this.g = parseInt(match[1].substring(2, 4), 16);\n"
+ " this.b = parseInt(match[1].substring(4, 6), 16);\n"
+ " }\n"
+ " return match !== null;\n"
+ "};\n\n\n"
+ "var _rgb = /^rgb\\((\\d{1,3}) *, *(\\d{1,3}) *, *(\\d{1,3})\\)$/;\n\n"
+ "Color.prototype.rgb = function(value) {\n"
+ " var match = null;\n"
+ " if ((match = value.match(_rgb)) !== null) {\n"
+ " this.r = Number(match[1]);\n"
+ " this.g = Number(match[2]);\n"
+ " this.b = Number(match[3]);\n"
+ " }\n"
+ " return match !== null;\n"
+ "};\n\n"
+ "var _rgba = /^rgba\\((\\d{1,3}) *, *(\\d{1,3}) *, *(\\d{1,3}) *, *(\\d+\\.?\\d*)\\)$/;\n\n"
+ "Color.prototype.rgba = function(value) {\n"
+ " var match = null;\n"
+ " if ((match = value.match(_rgba)) !== null) {\n"
+ " this.r = Number(match[1]);\n"
+ " this.g = Number(match[2]);\n"
+ " this.b = Number(match[3]);\n"
+ " this.a = Number(match[4]);\n"
+ " }\n"
+ " return match !== null;\n"
+ "};\n\n"
+ "Color.prototype.toString = function() {\n"
+ " return this.a !== null && this.a !== 1 ?\n"
+ " \"rgba(\" + [this.r, this.g, this.b, this.a].join(\",\") + \")\" :\n"
+ " \"rgb(\" + [this.r, this.g, this.b].join(\",\") + \")\";\n"
+ "};\n\n"
+ "Color.prototype.namedColor = function(value) {\n"
+ " var color = colors[value.toLowerCase()];\n"
+ " if (color) {\n"
+ " this.r = color[0];\n"
+ " this.g = color[1];\n"
+ " this.b = color[2];\n"
+ " } else if (value.toLowerCase() === \"transparent\") {\n"
+ " this.r = this.g = this.b = this.a = 0;\n"
+ " return true;\n"
+ " }\n\n"
+ " return !!color;\n"
+ "};\n\n"
+ "Color.prototype.isColor = true;\n\n"
+ "// JSON.stringify([].slice.call($$('.named-color-table tr'), 1).map(function(row) { return [row.childNodes[3].textContent, row.childNodes[5].textContent.trim().split(\",\").map(Number)] }).reduce(function(data, row) {data[row[0]] = row[1]; return data}, {}))\n"
+ "var colors = {\n"
+ " \"aliceblue\": [240, 248, 255],\n"
+ " \"antiquewhite\": [250, 235, 215],\n"
+ " \"aqua\": [0, 255, 255],\n"
+ " \"aquamarine\": [127, 255, 212],\n"
+ " \"azure\": [240, 255, 255],\n"
+ " \"beige\": [245, 245, 220],\n"
+ " \"bisque\": [255, 228, 196],\n"
+ " \"black\": [0, 0, 0],\n"
+ " \"blanchedalmond\": [255, 235, 205],\n"
+ " \"blue\": [0, 0, 255],\n"
+ " \"blueviolet\": [138, 43, 226],\n"
+ " \"brown\": [165, 42, 42],\n"
+ " \"burlywood\": [222, 184, 135],\n"
+ " \"cadetblue\": [95, 158, 160],\n"
+ " \"chartreuse\": [127, 255, 0],\n"
+ " \"chocolate\": [210, 105, 30],\n"
+ " \"coral\": [255, 127, 80],\n"
+ " \"cornflowerblue\": [100, 149, 237],\n"
+ " \"cornsilk\": [255, 248, 220],\n"
+ " \"crimson\": [220, 20, 60],\n"
+ " \"cyan\": [0, 255, 255],\n"
+ " \"darkblue\": [0, 0, 139],\n"
+ " \"darkcyan\": [0, 139, 139],\n"
+ " \"darkgoldenrod\": [184, 134, 11],\n"
+ " \"darkgray\": [169, 169, 169],\n"
+ " \"darkgreen\": [0, 100, 0],\n"
+ " \"darkgrey\": [169, 169, 169],\n"
+ " \"darkkhaki\": [189, 183, 107],\n"
+ " \"darkmagenta\": [139, 0, 139],\n"
+ " \"darkolivegreen\": [85, 107, 47],\n"
+ " \"darkorange\": [255, 140, 0],\n"
+ " \"darkorchid\": [153, 50, 204],\n"
+ " \"darkred\": [139, 0, 0],\n"
+ " \"darksalmon\": [233, 150, 122],\n"
+ " \"darkseagreen\": [143, 188, 143],\n"
+ " \"darkslateblue\": [72, 61, 139],\n"
+ " \"darkslategray\": [47, 79, 79],\n"
+ " \"darkslategrey\": [47, 79, 79],\n"
+ " \"darkturquoise\": [0, 206, 209],\n"
+ " \"darkviolet\": [148, 0, 211],\n"
+ " \"deeppink\": [255, 20, 147],\n"
+ " \"deepskyblue\": [0, 191, 255],\n"
+ " \"dimgray\": [105, 105, 105],\n"
+ " \"dimgrey\": [105, 105, 105],\n"
+ " \"dodgerblue\": [30, 144, 255],\n"
+ " \"firebrick\": [178, 34, 34],\n"
+ " \"floralwhite\": [255, 250, 240],\n"
+ " \"forestgreen\": [34, 139, 34],\n"
+ " \"fuchsia\": [255, 0, 255],\n"
+ " \"gainsboro\": [220, 220, 220],\n"
+ " \"ghostwhite\": [248, 248, 255],\n"
+ " \"gold\": [255, 215, 0],\n"
+ " \"goldenrod\": [218, 165, 32],\n"
+ " \"gray\": [128, 128, 128],\n"
+ " \"green\": [0, 128, 0],\n"
+ " \"greenyellow\": [173, 255, 47],\n"
+ " \"grey\": [128, 128, 128],\n"
+ " \"honeydew\": [240, 255, 240],\n"
+ " \"hotpink\": [255, 105, 180],\n"
+ " \"indianred\": [205, 92, 92],\n"
+ " \"indigo\": [75, 0, 130],\n"
+ " \"ivory\": [255, 255, 240],\n"
+ " \"khaki\": [240, 230, 140],\n"
+ " \"lavender\": [230, 230, 250],\n"
+ " \"lavenderblush\": [255, 240, 245],\n"
+ " \"lawngreen\": [124, 252, 0],\n"
+ " \"lemonchiffon\": [255, 250, 205],\n"
+ " \"lightblue\": [173, 216, 230],\n"
+ " \"lightcoral\": [240, 128, 128],\n"
+ " \"lightcyan\": [224, 255, 255],\n"
+ " \"lightgoldenrodyellow\": [250, 250, 210],\n"
+ " \"lightgray\": [211, 211, 211],\n"
+ " \"lightgreen\": [144, 238, 144],\n"
+ " \"lightgrey\": [211, 211, 211],\n"
+ " \"lightpink\": [255, 182, 193],\n"
+ " \"lightsalmon\": [255, 160, 122],\n"
+ " \"lightseagreen\": [32, 178, 170],\n"
+ " \"lightskyblue\": [135, 206, 250],\n"
+ " \"lightslategray\": [119, 136, 153],\n"
+ " \"lightslategrey\": [119, 136, 153],\n"
+ " \"lightsteelblue\": [176, 196, 222],\n"
+ " \"lightyellow\": [255, 255, 224],\n"
+ " \"lime\": [0, 255, 0],\n"
+ " \"limegreen\": [50, 205, 50],\n"
+ " \"linen\": [250, 240, 230],\n"
+ " \"magenta\": [255, 0, 255],\n"
+ " \"maroon\": [128, 0, 0],\n"
+ " \"mediumaquamarine\": [102, 205, 170],\n"
+ " \"mediumblue\": [0, 0, 205],\n"
+ " \"mediumorchid\": [186, 85, 211],\n"
+ " \"mediumpurple\": [147, 112, 219],\n"
+ " \"mediumseagreen\": [60, 179, 113],\n"
+ " \"mediumslateblue\": [123, 104, 238],\n"
+ " \"mediumspringgreen\": [0, 250, 154],\n"
+ " \"mediumturquoise\": [72, 209, 204],\n"
+ " \"mediumvioletred\": [199, 21, 133],\n"
+ " \"midnightblue\": [25, 25, 112],\n"
+ " \"mintcream\": [245, 255, 250],\n"
+ " \"mistyrose\": [255, 228, 225],\n"
+ " \"moccasin\": [255, 228, 181],\n"
+ " \"navajowhite\": [255, 222, 173],\n"
+ " \"navy\": [0, 0, 128],\n"
+ " \"oldlace\": [253, 245, 230],\n"
+ " \"olive\": [128, 128, 0],\n"
+ " \"olivedrab\": [107, 142, 35],\n"
+ " \"orange\": [255, 165, 0],\n"
+ " \"orangered\": [255, 69, 0],\n"
+ " \"orchid\": [218, 112, 214],\n"
+ " \"palegoldenrod\": [238, 232, 170],\n"
+ " \"palegreen\": [152, 251, 152],\n"
+ " \"paleturquoise\": [175, 238, 238],\n"
+ " \"palevioletred\": [219, 112, 147],\n"
+ " \"papayawhip\": [255, 239, 213],\n"
+ " \"peachpuff\": [255, 218, 185],\n"
+ " \"peru\": [205, 133, 63],\n"
+ " \"pink\": [255, 192, 203],\n"
+ " \"plum\": [221, 160, 221],\n"
+ " \"powderblue\": [176, 224, 230],\n"
+ " \"purple\": [128, 0, 128],\n"
+ " \"rebeccapurple\": [102, 51, 153],\n"
+ " \"red\": [255, 0, 0],\n"
+ " \"rosybrown\": [188, 143, 143],\n"
+ " \"royalblue\": [65, 105, 225],\n"
+ " \"saddlebrown\": [139, 69, 19],\n"
+ " \"salmon\": [250, 128, 114],\n"
+ " \"sandybrown\": [244, 164, 96],\n"
+ " \"seagreen\": [46, 139, 87],\n"
+ " \"seashell\": [255, 245, 238],\n"
+ " \"sienna\": [160, 82, 45],\n"
+ " \"silver\": [192, 192, 192],\n"
+ " \"skyblue\": [135, 206, 235],\n"
+ " \"slateblue\": [106, 90, 205],\n"
+ " \"slategray\": [112, 128, 144],\n"
+ " \"slategrey\": [112, 128, 144],\n"
+ " \"snow\": [255, 250, 250],\n"
+ " \"springgreen\": [0, 255, 127],\n"
+ " \"steelblue\": [70, 130, 180],\n"
+ " \"tan\": [210, 180, 140],\n"
+ " \"teal\": [0, 128, 128],\n"
+ " \"thistle\": [216, 191, 216],\n"
+ " \"tomato\": [255, 99, 71],\n"
+ " \"turquoise\": [64, 224, 208],\n"
+ " \"violet\": [238, 130, 238],\n"
+ " \"wheat\": [245, 222, 179],\n"
+ " \"white\": [255, 255, 255],\n"
+ " \"whitesmoke\": [245, 245, 245],\n"
+ " \"yellow\": [255, 255, 0],\n"
+ " \"yellowgreen\": [154, 205, 50]\n"
+ "};\n\n\n"
+ "function DummyImageContainer(src) {\n"
+ " this.src = src;\n"
+ " log(\"DummyImageContainer for\", src);\n"
+ " if (!this.promise || !this.image) {\n"
+ " log(\"Initiating DummyImageContainer\");\n"
+ " DummyImageContainer.prototype.image = new Image();\n"
+ " var image = this.image;\n"
+ " DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) {\n"
+ " image.onload = resolve;\n"
+ " image.onerror = reject;\n"
+ " image.src = smallImage();\n"
+ " if (image.complete === true) {\n"
+ " resolve(image);\n"
+ " }\n"
+ " });\n"
+ " }\n"
+ "}\n\n"
+ "function Font(family, size) {\n"
+ " var container = document.createElement('div'),\n"
+ " img = document.createElement('img'),\n"
+ " span = document.createElement('span'),\n"
+ " sampleText = 'Hidden Text',\n"
+ " baseline,\n"
+ " middle;\n\n"
+ " container.style.visibility = \"hidden\";\n"
+ " container.style.fontFamily = family;\n"
+ " container.style.fontSize = size;\n"
+ " container.style.margin = 0;\n"
+ " container.style.padding = 0;\n\n"
+ " document.body.appendChild(container);\n\n"
+ " img.src = smallImage();\n"
+ " img.width = 1;\n"
+ " img.height = 1;\n\n"
+ " img.style.margin = 0;\n"
+ " img.style.padding = 0;\n"
+ " img.style.verticalAlign = \"baseline\";\n\n"
+ " span.style.fontFamily = family;\n"
+ " span.style.fontSize = size;\n"
+ " span.style.margin = 0;\n"
+ " span.style.padding = 0;\n\n"
+ " span.appendChild(document.createTextNode(sampleText));\n"
+ " container.appendChild(span);\n"
+ " container.appendChild(img);\n"
+ " baseline = (img.offsetTop - span.offsetTop) + 1;\n\n"
+ " container.removeChild(span);\n"
+ " container.appendChild(document.createTextNode(sampleText));\n\n"
+ " container.style.lineHeight = \"normal\";\n"
+ " img.style.verticalAlign = \"super\";\n\n"
+ " middle = (img.offsetTop-container.offsetTop) + 1;\n\n"
+ " document.body.removeChild(container);\n\n"
+ " this.baseline = baseline;\n"
+ " this.lineWidth = 1;\n"
+ " this.middle = middle;\n"
+ "}\n\n"
+ "function FontMetrics() {\n"
+ " this.data = {};\n"
+ "}\n\n"
+ "FontMetrics.prototype.getMetrics = function(family, size) {\n"
+ " if (this.data[family + \"-\" + size] === undefined) {\n"
+ " this.data[family + \"-\" + size] = new Font(family, size);\n"
+ " }\n"
+ " return this.data[family + \"-\" + size];\n"
+ "};\n\n"
+ "function FrameContainer(container, sameOrigin, options) {\n"
+ " this.image = null;\n"
+ " this.src = container;\n"
+ " var self = this;\n"
+ " var bounds = getBounds(container);\n"
+ " this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) {\n"
+ " if (container.contentWindow.document.URL === \"about:blank\" || container.contentWindow.document.documentElement == null) {\n"
+ " container.contentWindow.onload = container.onload = function() {\n"
+ " resolve(container);\n"
+ " };\n"
+ " } else {\n"
+ " resolve(container);\n"
+ " }\n"
+ " })).then(function(container) {\n"
+ " return html2canvas(container.contentWindow.document.documentElement, {type: 'view', width: container.width, height: container.height, proxy: options.proxy, javascriptEnabled: options.javascriptEnabled, removeContainer: options.removeContainer, allowTaint: options.allowTaint, imageTimeout: options.imageTimeout / 2});\n"
+ " }).then(function(canvas) {\n"
+ " return self.image = canvas;\n"
+ " });\n"
+ "}\n\n"
+ "FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) {\n"
+ " var container = this.src;\n"
+ " return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options);\n"
+ "};\n\n"
+ "function GradientContainer(imageData) {\n"
+ " this.src = imageData.value;\n"
+ " this.colorStops = [];\n"
+ " this.type = null;\n"
+ " this.x0 = 0.5;\n"
+ " this.y0 = 0.5;\n"
+ " this.x1 = 0.5;\n"
+ " this.y1 = 0.5;\n"
+ " this.promise = Promise.resolve(true);\n"
+ "}\n\n"
+ "GradientContainer.prototype.TYPES = {\n"
+ " LINEAR: 1,\n"
+ " RADIAL: 2\n"
+ "};\n\n"
+ "function ImageContainer(src, cors) {\n"
+ " this.src = src;\n"
+ " this.image = new Image();\n"
+ " var self = this;\n"
+ " this.tainted = null;\n"
+ " this.promise = new Promise(function(resolve, reject) {\n"
+ " self.image.onload = resolve;\n"
+ " self.image.onerror = reject;\n"
+ " if (cors) {\n"
+ " self.image.crossOrigin = \"anonymous\";\n"
+ " }\n"
+ " self.image.src = src;\n"
+ " if (self.image.complete === true) {\n"
+ " resolve(self.image);\n"
+ " }\n"
+ " });\n"
+ "}\n\n"
+ "function ImageLoader(options, support) {\n"
+ " this.link = null;\n"
+ " this.options = options;\n"
+ " this.support = support;\n"
+ " this.origin = this.getOrigin(window.location.href);\n"
+ "}\n\n"
+ "ImageLoader.prototype.findImages = function(nodes) {\n"
+ " var images = [];\n"
+ " nodes.reduce(function(imageNodes, container) {\n"
+ " switch(container.node.nodeName) {\n"
+ " case \"IMG\":\n"
+ " return imageNodes.concat([{\n"
+ " args: [container.node.src],\n"
+ " method: \"url\"\n"
+ " }]);\n"
+ " case \"svg\":\n"
+ " case \"IFRAME\":\n"
+ " return imageNodes.concat([{\n"
+ " args: [container.node],\n"
+ " method: container.node.nodeName\n"
+ " }]);\n"
+ " }\n"
+ " return imageNodes;\n"
+ " }, []).forEach(this.addImage(images, this.loadImage), this);\n"
+ " return images;\n"
+ "};\n\n"
+ "ImageLoader.prototype.findBackgroundImage = function(images, container) {\n"
+ " container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this);\n"
+ " return images;\n"
+ "};\n\n"
+ "ImageLoader.prototype.addImage = function(images, callback) {\n"
+ " return function(newImage) {\n"
+ " newImage.args.forEach(function(image) {\n"
+ " if (!this.imageExists(images, image)) {\n"
+ " images.splice(0, 0, callback.call(this, newImage));\n"
+ " log('Added image #' + (images.length), typeof(image) === \"string\" ? image.substring(0, 100) : image);\n"
+ " }\n"
+ " }, this);\n"
+ " };\n"
+ "};\n\n"
+ "ImageLoader.prototype.hasImageBackground = function(imageData) {\n"
+ " return imageData.method !== \"none\";\n"
+ "};\n\n"
+ "ImageLoader.prototype.loadImage = function(imageData) {\n"
+ " if (imageData.method === \"url\") {\n"
+ " var src = imageData.args[0];\n"
+ " if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) {\n"
+ " return new SVGContainer(src);\n"
+ " } else if (src.match(/data:image\\/.*;base64,/i)) {\n"
+ " return new ImageContainer(src.replace(/url\\(['\"]{0,}|['\"]{0,}\\)$/ig, ''), false);\n"
+ " } else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) {\n"
+ " return new ImageContainer(src, false);\n"
+ " } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {\n"
+ " return new ImageContainer(src, true);\n"
+ " } else if (this.options.proxy) {\n"
+ " return new ProxyImageContainer(src, this.options.proxy);\n"
+ " } else {\n"
+ " return new DummyImageContainer(src);\n"
+ " }\n"
+ " } else if (imageData.method === \"linear-gradient\") {\n"
+ " return new LinearGradientContainer(imageData);\n"
+ " } else if (imageData.method === \"gradient\") {\n"
+ " return new WebkitGradientContainer(imageData);\n"
+ " } else if (imageData.method === \"svg\") {\n"
+ " return new SVGNodeContainer(imageData.args[0], this.support.svg);\n"
+ " } else if (imageData.method === \"IFRAME\") {\n"
+ " return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options);\n"
+ " } else {\n"
+ " return new DummyImageContainer(imageData);\n"
+ " }\n"
+ "};\n\n"
+ "ImageLoader.prototype.isSVG = function(src) {\n"
+ " return src.substring(src.length - 3).toLowerCase() === \"svg\" || SVGContainer.prototype.isInline(src);\n"
+ "};\n\n"
+ "ImageLoader.prototype.imageExists = function(images, src) {\n"
+ " return images.some(function(image) {\n"
+ " return image.src === src;\n"
+ " });\n"
+ "};\n\n"
+ "ImageLoader.prototype.isSameOrigin = function(url) {\n"
+ " return (this.getOrigin(url) === this.origin);\n"
+ "};\n\n"
+ "ImageLoader.prototype.getOrigin = function(url) {\n"
+ " var link = this.link || (this.link = document.createElement(\"a\"));\n"
+ " link.href = url;\n"
+ " link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/\n"
+ " return link.protocol + link.hostname + link.port;\n"
+ "};\n\n"
+ "ImageLoader.prototype.getPromise = function(container) {\n"
+ " return this.timeout(container, this.options.imageTimeout)['catch'](function() {\n"
+ " var dummy = new DummyImageContainer(container.src);\n"
+ " return dummy.promise.then(function(image) {\n"
+ " container.image = image;\n"
+ " });\n"
+ " });\n"
+ "};\n\n"
+ "ImageLoader.prototype.get = function(src) {\n"
+ " var found = null;\n"
+ " return this.images.some(function(img) {\n"
+ " return (found = img).src === src;\n"
+ " }) ? found : null;\n"
+ "};\n\n"
+ "ImageLoader.prototype.fetch = function(nodes) {\n"
+ " this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes));\n"
+ " this.images.forEach(function(image, index) {\n"
+ " image.promise.then(function() {\n"
+ " log(\"Succesfully loaded image #\"+ (index+1), image);\n"
+ " }, function(e) {\n"
+ " log(\"Failed loading image #\"+ (index+1), image, e);\n"
+ " });\n"
+ " });\n"
+ " this.ready = Promise.all(this.images.map(this.getPromise, this));\n"
+ " log(\"Finished searching images\");\n"
+ " return this;\n"
+ "};\n\n"
+ "ImageLoader.prototype.timeout = function(container, timeout) {\n"
+ " var timer;\n"
+ " var promise = Promise.race([container.promise, new Promise(function(res, reject) {\n"
+ " timer = setTimeout(function() {\n"
+ " log(\"Timed out loading image\", container);\n"
+ " reject(container);\n"
+ " }, timeout);\n"
+ " })]).then(function(container) {\n"
+ " clearTimeout(timer);\n"
+ " return container;\n"
+ " });\n"
+ " promise['catch'](function() {\n"
+ " clearTimeout(timer);\n"
+ " });\n"
+ " return promise;\n"
+ "};\n\n"
+ "function LinearGradientContainer(imageData) {\n"
+ " GradientContainer.apply(this, arguments);\n"
+ " this.type = this.TYPES.LINEAR;\n\n"
+ " var hasDirection = imageData.args[0].match(this.stepRegExp) === null;\n\n"
+ " if (hasDirection) {\n"
+ " imageData.args[0].split(\" \").reverse().forEach(function(position) {\n"
+ " switch(position) {\n"
+ " case \"left\":\n"
+ " this.x0 = 0;\n"
+ " this.x1 = 1;\n"
+ " break;\n"
+ " case \"top\":\n"
+ " this.y0 = 0;\n"
+ " this.y1 = 1;\n"
+ " break;\n"
+ " case \"right\":\n"
+ " this.x0 = 1;\n"
+ " this.x1 = 0;\n"
+ " break;\n"
+ " case \"bottom\":\n"
+ " this.y0 = 1;\n"
+ " this.y1 = 0;\n"
+ " break;\n"
+ " case \"to\":\n"
+ " var y0 = this.y0;\n"
+ " var x0 = this.x0;\n"
+ " this.y0 = this.y1;\n"
+ " this.x0 = this.x1;\n"
+ " this.x1 = x0;\n"
+ " this.y1 = y0;\n"
+ " break;\n"
+ " }\n"
+ " }, this);\n"
+ " } else {\n"
+ " this.y0 = 0;\n"
+ " this.y1 = 1;\n"
+ " }\n\n"
+ " this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) {\n"
+ " var colorStopMatch = colorStop.match(this.stepRegExp);\n"
+ " return {\n"
+ " color: new Color(colorStopMatch[1]),\n"
+ " stop: colorStopMatch[3] === \"%\" ? colorStopMatch[2] / 100 : null\n"
+ " };\n"
+ " }, this);\n\n"
+ " if (this.colorStops[0].stop === null) {\n"
+ " this.colorStops[0].stop = 0;\n"
+ " }\n\n"
+ " if (this.colorStops[this.colorStops.length - 1].stop === null) {\n"
+ " this.colorStops[this.colorStops.length - 1].stop = 1;\n"
+ " }\n\n"
+ " this.colorStops.forEach(function(colorStop, index) {\n"
+ " if (colorStop.stop === null) {\n"
+ " this.colorStops.slice(index).some(function(find, count) {\n"
+ " if (find.stop !== null) {\n"
+ " colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop;\n"
+ " return true;\n"
+ " } else {\n"
+ " return false;\n"
+ " }\n"
+ " }, this);\n"
+ " }\n"
+ " }, this);\n"
+ "}\n\n"
+ "LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);\n\n"
+ "LinearGradientContainer.prototype.stepRegExp = /((?:rgb|rgba)\\(\\d{1,3},\\s\\d{1,3},\\s\\d{1,3}(?:,\\s[0-9\\.]+)?\\))\\s*(\\d{1,3})?(%|px)?/;\n\n"
+ "function log() {\n"
+ " if (window.html2canvas.logging && window.console && window.console.log) {\n"
+ " Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - window.html2canvas.start) + \"ms\", \"html2canvas:\"].concat([].slice.call(arguments, 0)));\n"
+ " }\n"
+ "}\n\n"
+ "function NodeContainer(node, parent) {\n"
+ " this.node = node;\n"
+ " this.parent = parent;\n"
+ " this.stack = null;\n"
+ " this.bounds = null;\n"
+ " this.borders = null;\n"
+ " this.clip = [];\n"
+ " this.backgroundClip = [];\n"
+ " this.offsetBounds = null;\n"
+ " this.visible = null;\n"
+ " this.computedStyles = null;\n"
+ " this.colors = {};\n"
+ " this.styles = {};\n"
+ " this.backgroundImages = null;\n"
+ " this.transformData = null;\n"
+ " this.transformMatrix = null;\n"
+ " this.isPseudoElement = false;\n"
+ " this.opacity = null;\n"
+ "}\n\n"
+ "NodeContainer.prototype.cloneTo = function(stack) {\n"
+ " stack.visible = this.visible;\n"
+ " stack.borders = this.borders;\n"
+ " stack.bounds = this.bounds;\n"
+ " stack.clip = this.clip;\n"
+ " stack.backgroundClip = this.backgroundClip;\n"
+ " stack.computedStyles = this.computedStyles;\n"
+ " stack.styles = this.styles;\n"
+ " stack.backgroundImages = this.backgroundImages;\n"
+ " stack.opacity = this.opacity;\n"
+ "};\n\n"
+ "NodeContainer.prototype.getOpacity = function() {\n"
+ " return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity;\n"
+ "};\n\n"
+ "NodeContainer.prototype.assignStack = function(stack) {\n"
+ " this.stack = stack;\n"
+ " stack.children.push(this);\n"
+ "};\n\n"
+ "NodeContainer.prototype.isElementVisible = function() {\n"
+ " return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : (\n"
+ " this.css('display') !== \"none\" &&\n"
+ " this.css('visibility') !== \"hidden\" &&\n"
+ " !this.node.hasAttribute(\"data-html2canvas-ignore\") &&\n"
+ " (this.node.nodeName !== \"INPUT\" || this.node.getAttribute(\"type\") !== \"hidden\")\n"
+ " );\n"
+ "};\n\n"
+ "NodeContainer.prototype.css = function(attribute) {\n"
+ " if (!this.computedStyles) {\n"
+ " this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? \":before\" : \":after\") : this.computedStyle(null);\n"
+ " }\n\n"
+ " return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]);\n"
+ "};\n\n"
+ "NodeContainer.prototype.prefixedCss = function(attribute) {\n"
+ " var prefixes = [\"webkit\", \"moz\", \"ms\", \"o\"];\n"
+ " var value = this.css(attribute);\n"
+ " if (value === undefined) {\n"
+ " prefixes.some(function(prefix) {\n"
+ " value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1));\n"
+ " return value !== undefined;\n"
+ " }, this);\n"
+ " }\n"
+ " return value === undefined ? null : value;\n"
+ "};\n\n"
+ "NodeContainer.prototype.computedStyle = function(type) {\n"
+ " return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type);\n"
+ "};\n\n"
+ "NodeContainer.prototype.cssInt = function(attribute) {\n"
+ " var value = parseInt(this.css(attribute), 10);\n"
+ " return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html\n"
+ "};\n\n"
+ "NodeContainer.prototype.color = function(attribute) {\n"
+ " return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute)));\n"
+ "};\n\n"
+ "NodeContainer.prototype.cssFloat = function(attribute) {\n"
+ " var value = parseFloat(this.css(attribute));\n"
+ " return (isNaN(value)) ? 0 : value;\n"
+ "};\n\n"
+ "NodeContainer.prototype.fontWeight = function() {\n"
+ " var weight = this.css(\"fontWeight\");\n"
+ " switch(parseInt(weight, 10)){\n"
+ " case 401:\n"
+ " weight = \"bold\";\n"
+ " break;\n"
+ " case 400:\n"
+ " weight = \"normal\";\n"
+ " break;\n"
+ " }\n"
+ " return weight;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseClip = function() {\n"
+ " var matches = this.css('clip').match(this.CLIP);\n"
+ " if (matches) {\n"
+ " return {\n"
+ " top: parseInt(matches[1], 10),\n"
+ " right: parseInt(matches[2], 10),\n"
+ " bottom: parseInt(matches[3], 10),\n"
+ " left: parseInt(matches[4], 10)\n"
+ " };\n"
+ " }\n"
+ " return null;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseBackgroundImages = function() {\n"
+ " return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css(\"backgroundImage\")));\n"
+ "};\n\n"
+ "NodeContainer.prototype.cssList = function(property, index) {\n"
+ " var value = (this.css(property) || '').split(',');\n"
+ " value = value[index || 0] || value[0] || 'auto';\n"
+ " value = value.trim().split(' ');\n"
+ " if (value.length === 1) {\n"
+ " value = [value[0], value[0]];\n"
+ " }\n"
+ " return value;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) {\n"
+ " var size = this.cssList(\"backgroundSize\", index);\n"
+ " var width, height;\n\n"
+ " if (isPercentage(size[0])) {\n"
+ " width = bounds.width * parseFloat(size[0]) / 100;\n"
+ " } else if (/contain|cover/.test(size[0])) {\n"
+ " var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height;\n"
+ " return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio};\n"
+ " } else {\n"
+ " width = parseInt(size[0], 10);\n"
+ " }\n\n"
+ " if (size[0] === 'auto' && size[1] === 'auto') {\n"
+ " height = image.height;\n"
+ " } else if (size[1] === 'auto') {\n"
+ " height = width / image.width * image.height;\n"
+ " } else if (isPercentage(size[1])) {\n"
+ " height = bounds.height * parseFloat(size[1]) / 100;\n"
+ " } else {\n"
+ " height = parseInt(size[1], 10);\n"
+ " }\n\n"
+ " if (size[0] === 'auto') {\n"
+ " width = height / image.height * image.width;\n"
+ " }\n\n"
+ " return {width: width, height: height};\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) {\n"
+ " var position = this.cssList('backgroundPosition', index);\n"
+ " var left, top;\n\n"
+ " if (isPercentage(position[0])){\n"
+ " left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100);\n"
+ " } else {\n"
+ " left = parseInt(position[0], 10);\n"
+ " }\n\n"
+ " if (position[1] === 'auto') {\n"
+ " top = left / image.width * image.height;\n"
+ " } else if (isPercentage(position[1])){\n"
+ " top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100;\n"
+ " } else {\n"
+ " top = parseInt(position[1], 10);\n"
+ " }\n\n"
+ " if (position[0] === 'auto') {\n"
+ " left = top / image.height * image.width;\n"
+ " }\n\n"
+ " return {left: left, top: top};\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseBackgroundRepeat = function(index) {\n"
+ " return this.cssList(\"backgroundRepeat\", index)[0];\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseTextShadows = function() {\n"
+ " var textShadow = this.css(\"textShadow\");\n"
+ " var results = [];\n\n"
+ " if (textShadow && textShadow !== 'none') {\n"
+ " var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY);\n"
+ " for (var i = 0; shadows && (i < shadows.length); i++) {\n"
+ " var s = shadows[i].match(this.TEXT_SHADOW_VALUES);\n"
+ " results.push({\n"
+ " color: new Color(s[0]),\n"
+ " offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0,\n"
+ " offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0,\n"
+ " blur: s[3] ? s[3].replace('px', '') : 0\n"
+ " });\n"
+ " }\n"
+ " }\n"
+ " return results;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseTransform = function() {\n"
+ " if (!this.transformData) {\n"
+ " if (this.hasTransform()) {\n"
+ " var offset = this.parseBounds();\n"
+ " var origin = this.prefixedCss(\"transformOrigin\").split(\" \").map(removePx).map(asFloat);\n"
+ " origin[0] += offset.left;\n"
+ " origin[1] += offset.top;\n"
+ " this.transformData = {\n"
+ " origin: origin,\n"
+ " matrix: this.parseTransformMatrix()\n"
+ " };\n"
+ " } else {\n"
+ " this.transformData = {\n"
+ " origin: [0, 0],\n"
+ " matrix: [1, 0, 0, 1, 0, 0]\n"
+ " };\n"
+ " }\n"
+ " }\n"
+ " return this.transformData;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseTransformMatrix = function() {\n"
+ " if (!this.transformMatrix) {\n"
+ " var transform = this.prefixedCss(\"transform\");\n"
+ " var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null;\n"
+ " this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0];\n"
+ " }\n"
+ " return this.transformMatrix;\n"
+ "};\n\n"
+ "NodeContainer.prototype.parseBounds = function() {\n"
+ " return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node));\n"
+ "};\n\n"
+ "NodeContainer.prototype.hasTransform = function() {\n"
+ " return this.parseTransformMatrix().join(\",\") !== \"1,0,0,1,0,0\" || (this.parent && this.parent.hasTransform());\n"
+ "};\n\n"
+ "NodeContainer.prototype.getValue = function() {\n"
+ " var value = this.node.value || \"\";\n"
+ " if (this.node.tagName === \"SELECT\") {\n"
+ " value = selectionValue(this.node);\n"
+ " } else if (this.node.type === \"password\") {\n"
+ " value = Array(value.length + 1).join('\\u2022'); // jshint ignore:line\n"
+ " }\n"
+ " return value.length === 0 ? (this.node.placeholder || \"\") : value;\n"
+ "};\n\n"
+ "NodeContainer.prototype.MATRIX_PROPERTY = /(matrix)\\((.+)\\)/;\n"
+ "NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\\([^\\)]+\\)(\\s-?\\d+px){0,})/g;\n"
+ "NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\\d+px)|(#.+)|(rgb\\(.+\\))|(rgba\\(.+\\))/g;\n"
+ "NodeContainer.prototype.CLIP = /^rect\\((\\d+)px,? (\\d+)px,? (\\d+)px,? (\\d+)px\\)$/;\n\n"
+ "function selectionValue(node) {\n"
+ " var option = node.options[node.selectedIndex || 0];\n"
+ " return option ? (option.text || \"\") : \"\";\n"
+ "}\n\n"
+ "function parseMatrix(match) {\n"
+ " if (match && match[1] === \"matrix\") {\n"
+ " return match[2].split(\",\").map(function(s) {\n"
+ " return parseFloat(s.trim());\n"
+ " });\n"
+ " }\n"
+ "}\n\n"
+ "function isPercentage(value) {\n"
+ " return value.toString().indexOf(\"%\") !== -1;\n"
+ "}\n\n"
+ "function parseBackgrounds(backgroundImage) {\n"
+ " var whitespace = ' \\r\\n\\t',\n"
+ " method, definition, prefix, prefix_i, block, results = [],\n"
+ " mode = 0, numParen = 0, quote, args;\n"
+ " var appendResult = function() {\n"
+ " if(method) {\n"
+ " if (definition.substr(0, 1) === '\"') {\n"
+ " definition = definition.substr(1, definition.length - 2);\n"
+ " }\n"
+ " if (definition) {\n"
+ " args.push(definition);\n"
+ " }\n"
+ " if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) {\n"
+ " prefix = method.substr(0, prefix_i);\n"
+ " method = method.substr(prefix_i);\n"
+ " }\n"
+ " results.push({\n"
+ " prefix: prefix,\n"
+ " method: method.toLowerCase(),\n"
+ " value: block,\n"
+ " args: args,\n"
+ " image: null\n"
+ " });\n"
+ " }\n"
+ " args = [];\n"
+ " method = prefix = definition = block = '';\n"
+ " };\n"
+ " args = [];\n"
+ " method = prefix = definition = block = '';\n"
+ " backgroundImage.split(\"\").forEach(function(c) {\n"
+ " if (mode === 0 && whitespace.indexOf(c) > -1) {\n"
+ " return;\n"
+ " }\n"
+ " switch(c) {\n"
+ " case '\"':\n"
+ " if(!quote) {\n"
+ " quote = c;\n"
+ " } else if(quote === c) {\n"
+ " quote = null;\n"
+ " }\n"
+ " break;\n"
+ " case '(':\n"
+ " if(quote) {\n"
+ " break;\n"
+ " } else if(mode === 0) {\n"
+ " mode = 1;\n"
+ " block += c;\n"
+ " return;\n"
+ " } else {\n"
+ " numParen++;\n"
+ " }\n"
+ " break;\n"
+ " case ')':\n"
+ " if (quote) {\n"
+ " break;\n"
+ " } else if(mode === 1) {\n"
+ " if(numParen === 0) {\n"
+ " mode = 0;\n"
+ " block += c;\n"
+ " appendResult();\n"
+ " return;\n"
+ " } else {\n"
+ " numParen--;\n"
+ " }\n"
+ " }\n"
+ " break;\n\n"
+ " case ',':\n"
+ " if (quote) {\n"
+ " break;\n"
+ " } else if(mode === 0) {\n"
+ " appendResult();\n"
+ " return;\n"
+ " } else if (mode === 1) {\n"
+ " if (numParen === 0 && !method.match(/^url$/i)) {\n"
+ " args.push(definition);\n"
+ " definition = '';\n"
+ " block += c;\n"
+ " return;\n"
+ " }\n"
+ " }\n"
+ " break;\n"
+ " }\n\n"
+ " block += c;\n"
+ " if (mode === 0) {\n"
+ " method += c;\n"
+ " } else {\n"
+ " definition += c;\n"
+ " }\n"
+ " });\n\n"
+ " appendResult();\n"
+ " return results;\n"
+ "}\n\n"
+ "function removePx(str) {\n"
+ " return str.replace(\"px\", \"\");\n"
+ "}\n\n"
+ "function asFloat(str) {\n"
+ " return parseFloat(str);\n"
+ "}\n\n"
+ "function getBounds(node) {\n"
+ " if (node.getBoundingClientRect) {\n"
+ " var clientRect = node.getBoundingClientRect();\n"
+ " var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth;\n"
+ " return {\n"
+ " top: clientRect.top,\n"
+ " bottom: clientRect.bottom || (clientRect.top + clientRect.height),\n"
+ " right: clientRect.left + width,\n"
+ " left: clientRect.left,\n"
+ " width: width,\n"
+ " height: node.offsetHeight == null ? clientRect.height : node.offsetHeight\n"
+ " };\n"
+ " }\n"
+ " return {};\n"
+ "}\n\n"
+ "function offsetBounds(node) {\n"
+ " var parent = node.offsetParent ? offsetBounds(node.offsetParent) : {top: 0, left: 0};\n\n"
+ " return {\n"
+ " top: node.offsetTop + parent.top,\n"
+ " bottom: node.offsetTop + node.offsetHeight + parent.top,\n"
+ " right: node.offsetLeft + parent.left + node.offsetWidth,\n"
+ " left: node.offsetLeft + parent.left,\n"
+ " width: node.offsetWidth,\n"
+ " height: node.offsetHeight\n"
+ " };\n"
+ "}\n\n"
+ "function NodeParser(element, renderer, support, imageLoader, options) {\n"
+ " log(\"Starting NodeParser\");\n"
+ " this.renderer = renderer;\n"
+ " this.options = options;\n"
+ " this.range = null;\n"
+ " this.support = support;\n"
+ " this.renderQueue = [];\n"
+ " this.stack = new StackingContext(true, 1, element.ownerDocument, null);\n"
+ " var parent = new NodeContainer(element, null);\n"
+ " if (options.background) {\n"
+ " renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background));\n"
+ " }\n"
+ " if (element === element.ownerDocument.documentElement) {\n"
+ " // http://www.w3.org/TR/css3-background/#special-backgrounds\n"
+ " var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null);\n"
+ " renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor'));\n"
+ " }\n"
+ " parent.visibile = parent.isElementVisible();\n"
+ " this.createPseudoHideStyles(element.ownerDocument);\n"
+ " this.disableAnimations(element.ownerDocument);\n"
+ " this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) {\n"
+ " return container.visible = container.isElementVisible();\n"
+ " }).map(this.getPseudoElements, this));\n"
+ " this.fontMetrics = new FontMetrics();\n"
+ " log(\"Fetched nodes, total:\", this.nodes.length);\n"
+ " log(\"Calculate overflow clips\");\n"
+ " this.calculateOverflowClips();\n"
+ " log(\"Start fetching images\");\n"
+ " this.images = imageLoader.fetch(this.nodes.filter(isElement));\n"
+ " this.ready = this.images.ready.then(bind(function() {\n"
+ " log(\"Images loaded, starting parsing\");\n"
+ " log(\"Creating stacking contexts\");\n"
+ " this.createStackingContexts();\n"
+ " log(\"Sorting stacking contexts\");\n"
+ " this.sortStackingContexts(this.stack);\n"
+ " this.parse(this.stack);\n"
+ " log(\"Render queue created with \" + this.renderQueue.length + \" items\");\n"
+ " return new Promise(bind(function(resolve) {\n"
+ " if (!options.async) {\n"
+ " this.renderQueue.forEach(this.paint, this);\n"
+ " resolve();\n"
+ " } else if (typeof(options.async) === \"function\") {\n"
+ " options.async.call(this, this.renderQueue, resolve);\n"
+ " } else if (this.renderQueue.length > 0){\n"
+ " this.renderIndex = 0;\n"
+ " this.asyncRenderer(this.renderQueue, resolve);\n"
+ " } else {\n"
+ " resolve();\n"
+ " }\n"
+ " }, this));\n"
+ " }, this));\n"
+ "}\n\n"
+ "NodeParser.prototype.calculateOverflowClips = function() {\n"
+ " this.nodes.forEach(function(container) {\n"
+ " if (isElement(container)) {\n"
+ " if (isPseudoElement(container)) {\n"
+ " container.appendToDOM();\n"
+ " }\n"
+ " container.borders = this.parseBorders(container);\n"
+ " var clip = (container.css('overflow') === \"hidden\") ? [container.borders.clip] : [];\n"
+ " var cssClip = container.parseClip();\n"
+ " if (cssClip && [\"absolute\", \"fixed\"].indexOf(container.css('position')) !== -1) {\n"
+ " clip.push([[\"rect\",\n"
+ " container.bounds.left + cssClip.left,\n"
+ " container.bounds.top + cssClip.top,\n"
+ " cssClip.right - cssClip.left,\n"
+ " cssClip.bottom - cssClip.top\n"
+ " ]]);\n"
+ " }\n"
+ " container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;\n"
+ " container.backgroundClip = (container.css('overflow') !== \"hidden\") ? container.clip.concat([container.borders.clip]) : container.clip;\n"
+ " if (isPseudoElement(container)) {\n"
+ " container.cleanDOM();\n"
+ " }\n"
+ " } else if (isTextNode(container)) {\n"
+ " container.clip = hasParentClip(container) ? container.parent.clip : [];\n"
+ " }\n"
+ " if (!isPseudoElement(container)) {\n"
+ " container.bounds = null;\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "function hasParentClip(container) {\n"
+ " return container.parent && container.parent.clip.length;\n"
+ "}\n\n"
+ "NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {\n"
+ " asyncTimer = asyncTimer || Date.now();\n"
+ " this.paint(queue[this.renderIndex++]);\n"
+ " if (queue.length === this.renderIndex) {\n"
+ " resolve();\n"
+ " } else if (asyncTimer + 20 > Date.now()) {\n"
+ " this.asyncRenderer(queue, resolve, asyncTimer);\n"
+ " } else {\n"
+ " setTimeout(bind(function() {\n"
+ " this.asyncRenderer(queue, resolve);\n"
+ " }, this), 0);\n"
+ " }\n"
+ "};\n\n"
+ "NodeParser.prototype.createPseudoHideStyles = function(document) {\n"
+ " this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: \"\" !important; display: none !important; }' +\n"
+ " '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: \"\" !important; display: none !important; }');\n"
+ "};\n\n"
+ "NodeParser.prototype.disableAnimations = function(document) {\n"
+ " this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +\n"
+ " '-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}');\n"
+ "};\n\n"
+ "NodeParser.prototype.createStyles = function(document, styles) {\n"
+ " var hidePseudoElements = document.createElement('style');\n"
+ " hidePseudoElements.innerHTML = styles;\n"
+ " document.body.appendChild(hidePseudoElements);\n"
+ "};\n\n"
+ "NodeParser.prototype.getPseudoElements = function(container) {\n"
+ " var nodes = [[container]];\n"
+ " if (container.node.nodeType === Node.ELEMENT_NODE) {\n"
+ " var before = this.getPseudoElement(container, \":before\");\n"
+ " var after = this.getPseudoElement(container, \":after\");\n\n"
+ " if (before) {\n"
+ " nodes.push(before);\n"
+ " }\n\n"
+ " if (after) {\n"
+ " nodes.push(after);\n"
+ " }\n"
+ " }\n"
+ " return flatten(nodes);\n"
+ "};\n\n"
+ "function toCamelCase(str) {\n"
+ " return str.replace(/(\\-[a-z])/g, function(match){\n"
+ " return match.toUpperCase().replace('-','');\n"
+ " });\n"
+ "}\n\n"
+ "NodeParser.prototype.getPseudoElement = function(container, type) {\n"
+ " var style = container.computedStyle(type);\n"
+ " if(!style || !style.content || style.content === \"none\" || style.content === \"-moz-alt-content\" || style.display === \"none\") {\n"
+ " return null;\n"
+ " }\n\n"
+ " var content = stripQuotes(style.content);\n"
+ " var isImage = content.substr(0, 3) === 'url';\n"
+ " var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement');\n"
+ " var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type);\n\n"
+ " for (var i = style.length-1; i >= 0; i--) {\n"
+ " var property = toCamelCase(style.item(i));\n"
+ " pseudoNode.style[property] = style[property];\n"
+ " }\n\n"
+ " pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + \" \" + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER;\n\n"
+ " if (isImage) {\n"
+ " pseudoNode.src = parseBackgrounds(content)[0].args[0];\n"
+ " return [pseudoContainer];\n"
+ " } else {\n"
+ " var text = document.createTextNode(content);\n"
+ " pseudoNode.appendChild(text);\n"
+ " return [pseudoContainer, new TextContainer(text, pseudoContainer)];\n"
+ " }\n"
+ "};\n\n\n"
+ "NodeParser.prototype.getChildren = function(parentContainer) {\n"
+ " return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {\n"
+ " var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);\n"
+ " return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== \"TEXTAREA\" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;\n"
+ " }, this));\n"
+ "};\n\n"
+ "NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {\n"
+ " var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent);\n"
+ " container.cloneTo(stack);\n"
+ " var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;\n"
+ " parentStack.contexts.push(stack);\n"
+ " container.stack = stack;\n"
+ "};\n\n"
+ "NodeParser.prototype.createStackingContexts = function() {\n"
+ " this.nodes.forEach(function(container) {\n"
+ " if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) {\n"
+ " this.newStackingContext(container, true);\n"
+ " } else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) {\n"
+ " this.newStackingContext(container, false);\n"
+ " } else {\n"
+ " container.assignStack(container.parent.stack);\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.isBodyWithTransparentRoot = function(container) {\n"
+ " return container.node.nodeName === \"BODY\" && container.parent.color('backgroundColor').isTransparent();\n"
+ "};\n\n"
+ "NodeParser.prototype.isRootElement = function(container) {\n"
+ " return container.parent === null;\n"
+ "};\n\n"
+ "NodeParser.prototype.sortStackingContexts = function(stack) {\n"
+ " stack.contexts.sort(zIndexSort(stack.contexts.slice(0)));\n"
+ " stack.contexts.forEach(this.sortStackingContexts, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.parseTextBounds = function(container) {\n"
+ " return function(text, index, textList) {\n"
+ " if (container.parent.css(\"textDecoration\").substr(0, 4) !== \"none\" || text.trim().length !== 0) {\n"
+ " if (this.support.rangeBounds && !container.parent.hasTransform()) {\n"
+ " var offset = textList.slice(0, index).join(\"\").length;\n"
+ " return this.getRangeBounds(container.node, offset, text.length);\n"
+ " } else if (container.node && typeof(container.node.data) === \"string\") {\n"
+ " var replacementNode = container.node.splitText(text.length);\n"
+ " var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform());\n"
+ " container.node = replacementNode;\n"
+ " return bounds;\n"
+ " }\n"
+ " } else if(!this.support.rangeBounds || container.parent.hasTransform()){\n"
+ " container.node = container.node.splitText(text.length);\n"
+ " }\n"
+ " return {};\n"
+ " };\n"
+ "};\n\n"
+ "NodeParser.prototype.getWrapperBounds = function(node, transform) {\n"
+ " var wrapper = node.ownerDocument.createElement('html2canvaswrapper');\n"
+ " var parent = node.parentNode,\n"
+ " backupText = node.cloneNode(true);\n\n"
+ " wrapper.appendChild(node.cloneNode(true));\n"
+ " parent.replaceChild(wrapper, node);\n"
+ " var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper);\n"
+ " parent.replaceChild(backupText, wrapper);\n"
+ " return bounds;\n"
+ "};\n\n"
+ "NodeParser.prototype.getRangeBounds = function(node, offset, length) {\n"
+ " var range = this.range || (this.range = node.ownerDocument.createRange());\n"
+ " range.setStart(node, offset);\n"
+ " range.setEnd(node, offset + length);\n"
+ " return range.getBoundingClientRect();\n"
+ "};\n\n"
+ "function ClearTransform() {}\n\n"
+ "NodeParser.prototype.parse = function(stack) {\n"
+ " // http://www.w3.org/TR/CSS21/visuren.html#z-index\n"
+ " var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).\n"
+ " var descendantElements = stack.children.filter(isElement);\n"
+ " var descendantNonFloats = descendantElements.filter(not(isFloating));\n"
+ " var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.\n"
+ " var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.\n"
+ " var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.\n"
+ " var stackLevel0 = stack.contexts.concat(descendantNonFloats.filter(isPositioned)).filter(zIndex0); // 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.\n"
+ " var text = stack.children.filter(isTextNode).filter(hasText);\n"
+ " var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).\n"
+ " negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)\n"
+ " .concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {\n"
+ " this.renderQueue.push(container);\n"
+ " if (isStackingContext(container)) {\n"
+ " this.parse(container);\n"
+ " this.renderQueue.push(new ClearTransform());\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.paint = function(container) {\n"
+ " try {\n"
+ " if (container instanceof ClearTransform) {\n"
+ " this.renderer.ctx.restore();\n"
+ " } else if (isTextNode(container)) {\n"
+ " if (isPseudoElement(container.parent)) {\n"
+ " container.parent.appendToDOM();\n"
+ " }\n"
+ " this.paintText(container);\n"
+ " if (isPseudoElement(container.parent)) {\n"
+ " container.parent.cleanDOM();\n"
+ " }\n"
+ " } else {\n"
+ " this.paintNode(container);\n"
+ " }\n"
+ " } catch(e) {\n"
+ " log(e);\n"
+ " if (this.options.strict) {\n"
+ " throw e;\n"
+ " }\n"
+ " }\n"
+ "};\n\n"
+ "NodeParser.prototype.paintNode = function(container) {\n"
+ " if (isStackingContext(container)) {\n"
+ " this.renderer.setOpacity(container.opacity);\n"
+ " this.renderer.ctx.save();\n"
+ " if (container.hasTransform()) {\n"
+ " this.renderer.setTransform(container.parseTransform());\n"
+ " }\n"
+ " }\n\n"
+ " if (container.node.nodeName === \"INPUT\" && container.node.type === \"checkbox\") {\n"
+ " this.paintCheckbox(container);\n"
+ " } else if (container.node.nodeName === \"INPUT\" && container.node.type === \"radio\") {\n"
+ " this.paintRadio(container);\n"
+ " } else {\n"
+ " this.paintElement(container);\n"
+ " }\n"
+ "};\n\n"
+ "NodeParser.prototype.paintElement = function(container) {\n"
+ " var bounds = container.parseBounds();\n"
+ " this.renderer.clip(container.backgroundClip, function() {\n"
+ " this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));\n"
+ " }, this);\n\n"
+ " this.renderer.clip(container.clip, function() {\n"
+ " this.renderer.renderBorders(container.borders.borders);\n"
+ " }, this);\n\n"
+ " this.renderer.clip(container.backgroundClip, function() {\n"
+ " switch (container.node.nodeName) {\n"
+ " case \"svg\":\n"
+ " case \"IFRAME\":\n"
+ " var imgContainer = this.images.get(container.node);\n"
+ " if (imgContainer) {\n"
+ " this.renderer.renderImage(container, bounds, container.borders, imgContainer);\n"
+ " } else {\n"
+ " log(\"Error loading <\" + container.node.nodeName + \">\", container.node);\n"
+ " }\n"
+ " break;\n"
+ " case \"IMG\":\n"
+ " var imageContainer = this.images.get(container.node.src);\n"
+ " if (imageContainer) {\n"
+ " this.renderer.renderImage(container, bounds, container.borders, imageContainer);\n"
+ " } else {\n"
+ " log(\"Error loading <img>\", container.node.src);\n"
+ " }\n"
+ " break;\n"
+ " case \"CANVAS\":\n"
+ " this.renderer.renderImage(container, bounds, container.borders, {image: container.node});\n"
+ " break;\n"
+ " case \"SELECT\":\n"
+ " case \"INPUT\":\n"
+ " case \"TEXTAREA\":\n"
+ " this.paintFormValue(container);\n"
+ " break;\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.paintCheckbox = function(container) {\n"
+ " var b = container.parseBounds();\n\n"
+ " var size = Math.min(b.width, b.height);\n"
+ " var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left};\n"
+ " var r = [3, 3];\n"
+ " var radius = [r, r, r, r];\n"
+ " var borders = [1,1,1,1].map(function(w) {\n"
+ " return {color: new Color('#A5A5A5'), width: w};\n"
+ " });\n\n"
+ " var borderPoints = calculateCurvePoints(bounds, radius, borders);\n\n"
+ " this.renderer.clip(container.backgroundClip, function() {\n"
+ " this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color(\"#DEDEDE\"));\n"
+ " this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius));\n"
+ " if (container.node.checked) {\n"
+ " this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + \"px\", 'arial');\n"
+ " this.renderer.text(\"\\u2714\", bounds.left + size / 6, bounds.top + size - 1);\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.paintRadio = function(container) {\n"
+ " var bounds = container.parseBounds();\n\n"
+ " var size = Math.min(bounds.width, bounds.height) - 2;\n\n"
+ " this.renderer.clip(container.backgroundClip, function() {\n"
+ " this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5'));\n"
+ " if (container.node.checked) {\n"
+ " this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242'));\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.paintFormValue = function(container) {\n"
+ " var value = container.getValue();\n"
+ " if (value.length > 0) {\n"
+ " var document = container.node.ownerDocument;\n"
+ " var wrapper = document.createElement('html2canvaswrapper');\n"
+ " var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',\n"
+ " 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',\n"
+ " 'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth',\n"
+ " 'boxSizing', 'whiteSpace', 'wordWrap'];\n\n"
+ " properties.forEach(function(property) {\n"
+ " try {\n"
+ " wrapper.style[property] = container.css(property);\n"
+ " } catch(e) {\n"
+ " // Older IE has issues with \"border\"\n"
+ " log(\"html2canvas: Parse: Exception caught in renderFormValue: \" + e.message);\n"
+ " }\n"
+ " });\n"
+ " var bounds = container.parseBounds();\n"
+ " wrapper.style.position = \"fixed\";\n"
+ " wrapper.style.left = bounds.left + \"px\";\n"
+ " wrapper.style.top = bounds.top + \"px\";\n"
+ " wrapper.textContent = value;\n"
+ " document.body.appendChild(wrapper);\n"
+ " this.paintText(new TextContainer(wrapper.firstChild, container));\n"
+ " document.body.removeChild(wrapper);\n"
+ " }\n"
+ "};\n\n"
+ "NodeParser.prototype.paintText = function(container) {\n"
+ " container.applyTextTransform();\n"
+ " var characters = window.html2canvas.punycode.ucs2.decode(container.node.data);\n"
+ " var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(character) {\n"
+ " return window.html2canvas.punycode.ucs2.encode([character]);\n"
+ " });\n\n"
+ " var weight = container.parent.fontWeight();\n"
+ " var size = container.parent.css('fontSize');\n"
+ " var family = container.parent.css('fontFamily');\n"
+ " var shadows = container.parent.parseTextShadows();\n\n"
+ " this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);\n"
+ " if (shadows.length) {\n"
+ " // TODO: support multiple text shadows\n"
+ " this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur);\n"
+ " } else {\n"
+ " this.renderer.clearShadow();\n"
+ " }\n\n"
+ " this.renderer.clip(container.parent.clip, function() {\n"
+ " textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {\n"
+ " if (bounds) {\n"
+ " this.renderer.text(textList[index], bounds.left, bounds.bottom);\n"
+ " this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));\n"
+ " }\n"
+ " }, this);\n"
+ " }, this);\n"
+ "};\n\n"
+ "NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) {\n"
+ " switch(container.css(\"textDecoration\").split(\" \")[0]) {\n"
+ " case \"underline\":\n"
+ " // Draws a line at the baseline of the font\n"
+ " // TODO As some browsers display the line as more than 1px if the font-size is big, need to take that into account both in position and size\n"
+ " this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color(\"color\"));\n"
+ " break;\n"
+ " case \"overline\":\n"
+ " this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color(\"color\"));\n"
+ " break;\n"
+ " case \"line-through\":\n"
+ " // TODO try and find exact position for line-through\n"
+ " this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color(\"color\"));\n"
+ " break;\n"
+ " }\n"
+ "};\n\n"
+ "var borderColorTransforms = {\n"
+ " inset: [\n"
+ " [\"darken\", 0.60],\n"
+ " [\"darken\", 0.10],\n"
+ " [\"darken\", 0.10],\n"
+ " [\"darken\", 0.60]\n"
+ " ]\n"
+ "};\n\n"
+ "NodeParser.prototype.parseBorders = function(container) {\n"
+ " var nodeBounds = container.parseBounds();\n"
+ " var radius = getBorderRadiusData(container);\n"
+ " var borders = [\"Top\", \"Right\", \"Bottom\", \"Left\"].map(function(side, index) {\n"
+ " var style = container.css('border' + side + 'Style');\n"
+ " var color = container.color('border' + side + 'Color');\n"
+ " if (style === \"inset\" && color.isBlack()) {\n"
+ " color = new Color([255, 255, 255, color.a]); // this is wrong, but\n"
+ " }\n"
+ " var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null;\n"
+ " return {\n"
+ " width: container.cssInt('border' + side + 'Width'),\n"
+ " color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color,\n"
+ " args: null\n"
+ " };\n"
+ " });\n"
+ " var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);\n\n"
+ " return {\n"
+ " clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),\n"
+ " borders: calculateBorders(borders, nodeBounds, borderPoints, radius)\n"
+ " };\n"
+ "};\n\n"
+ "function calculateBorders(borders, nodeBounds, borderPoints, radius) {\n"
+ " return borders.map(function(border, borderSide) {\n"
+ " if (border.width > 0) {\n"
+ " var bx = nodeBounds.left;\n"
+ " var by = nodeBounds.top;\n"
+ " var bw = nodeBounds.width;\n"
+ " var bh = nodeBounds.height - (borders[2].width);\n\n"
+ " switch(borderSide) {\n"
+ " case 0:\n"
+ " // top border\n"
+ " bh = borders[0].width;\n"
+ " border.args = drawSide({\n"
+ " c1: [bx, by],\n"
+ " c2: [bx + bw, by],\n"
+ " c3: [bx + bw - borders[1].width, by + bh],\n"
+ " c4: [bx + borders[3].width, by + bh]\n"
+ " }, radius[0], radius[1],\n"
+ " borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);\n"
+ " break;\n"
+ " case 1:\n"
+ " // right border\n"
+ " bx = nodeBounds.left + nodeBounds.width - (borders[1].width);\n"
+ " bw = borders[1].width;\n\n"
+ " border.args = drawSide({\n"
+ " c1: [bx + bw, by],\n"
+ " c2: [bx + bw, by + bh + borders[2].width],\n"
+ " c3: [bx, by + bh],\n"
+ " c4: [bx, by + borders[0].width]\n"
+ " }, radius[1], radius[2],\n"
+ " borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);\n"
+ " break;\n"
+ " case 2:\n"
+ " // bottom border\n"
+ " by = (by + nodeBounds.height) - (borders[2].width);\n"
+ " bh = borders[2].width;\n"
+ " border.args = drawSide({\n"
+ " c1: [bx + bw, by + bh],\n"
+ " c2: [bx, by + bh],\n"
+ " c3: [bx + borders[3].width, by],\n"
+ " c4: [bx + bw - borders[3].width, by]\n"
+ " }, radius[2], radius[3],\n"
+ " borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);\n"
+ " break;\n"
+ " case 3:\n"
+ " // left border\n"
+ " bw = borders[3].width;\n"
+ " border.args = drawSide({\n"
+ " c1: [bx, by + bh + borders[2].width],\n"
+ " c2: [bx, by],\n"
+ " c3: [bx + bw, by + borders[0].width],\n"
+ " c4: [bx + bw, by + bh]\n"
+ " }, radius[3], radius[0],\n"
+ " borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " return border;\n"
+ " });\n"
+ "}\n\n"
+ "NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {\n"
+ " var backgroundClip = container.css('backgroundClip'),\n"
+ " borderArgs = [];\n\n"
+ " switch(backgroundClip) {\n"
+ " case \"content-box\":\n"
+ " case \"padding-box\":\n"
+ " parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);\n"
+ " parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);\n"
+ " parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);\n"
+ " parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);\n"
+ " break;\n\n"
+ " default:\n"
+ " parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);\n"
+ " parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);\n"
+ " parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);\n"
+ " parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);\n"
+ " break;\n"
+ " }\n\n"
+ " return borderArgs;\n"
+ "};\n\n"
+ "function getCurvePoints(x, y, r1, r2) {\n"
+ " var kappa = 4 * ((Math.sqrt(2) - 1) / 3);\n"
+ " var ox = (r1) * kappa, // control point offset horizontal\n"
+ " oy = (r2) * kappa, // control point offset vertical\n"
+ " xm = x + r1, // x-middle\n"
+ " ym = y + r2; // y-middle\n"
+ " return {\n"
+ " topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),\n"
+ " topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),\n"
+ " bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),\n"
+ " bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})\n"
+ " };\n"
+ "}\n\n"
+ "function calculateCurvePoints(bounds, borderRadius, borders) {\n"
+ " var x = bounds.left,\n"
+ " y = bounds.top,\n"
+ " width = bounds.width,\n"
+ " height = bounds.height,\n\n"
+ " tlh = borderRadius[0][0],\n"
+ " tlv = borderRadius[0][1],\n"
+ " trh = borderRadius[1][0],\n"
+ " trv = borderRadius[1][1],\n"
+ " brh = borderRadius[2][0],\n"
+ " brv = borderRadius[2][1],\n"
+ " blh = borderRadius[3][0],\n"
+ " blv = borderRadius[3][1];\n\n"
+ " var topWidth = width - trh,\n"
+ " rightHeight = height - brv,\n"
+ " bottomWidth = width - brh,\n"
+ " leftHeight = height - blv;\n\n"
+ " return {\n"
+ " topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),\n"
+ " topLeftInner: getCurvePoints(x + borders[3].width, y + borders[0].width, Math.max(0, tlh - borders[3].width), Math.max(0, tlv - borders[0].width)).topLeft.subdivide(0.5),\n"
+ " topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),\n"
+ " topRightInner: getCurvePoints(x + Math.min(topWidth, width + borders[3].width), y + borders[0].width, (topWidth > width + borders[3].width) ? 0 :trh - borders[3].width, trv - borders[0].width).topRight.subdivide(0.5),\n"
+ " bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),\n"
+ " bottomRightInner: getCurvePoints(x + Math.min(bottomWidth, width - borders[3].width), y + Math.min(rightHeight, height + borders[0].width), Math.max(0, brh - borders[1].width), brv - borders[2].width).bottomRight.subdivide(0.5),\n"
+ " bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),\n"
+ " bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5)\n"
+ " };\n"
+ "}\n\n"
+ "function bezierCurve(start, startControl, endControl, end) {\n"
+ " var lerp = function (a, b, t) {\n"
+ " return {\n"
+ " x: a.x + (b.x - a.x) * t,\n"
+ " y: a.y + (b.y - a.y) * t\n"
+ " };\n"
+ " };\n\n"
+ " return {\n"
+ " start: start,\n"
+ " startControl: startControl,\n"
+ " endControl: endControl,\n"
+ " end: end,\n"
+ " subdivide: function(t) {\n"
+ " var ab = lerp(start, startControl, t),\n"
+ " bc = lerp(startControl, endControl, t),\n"
+ " cd = lerp(endControl, end, t),\n"
+ " abbc = lerp(ab, bc, t),\n"
+ " bccd = lerp(bc, cd, t),\n"
+ " dest = lerp(abbc, bccd, t);\n"
+ " return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];\n"
+ " },\n"
+ " curveTo: function(borderArgs) {\n"
+ " borderArgs.push([\"bezierCurve\", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);\n"
+ " },\n"
+ " curveToReversed: function(borderArgs) {\n"
+ " borderArgs.push([\"bezierCurve\", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);\n"
+ " }\n"
+ " };\n"
+ "}\n\n"
+ "function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {\n"
+ " var borderArgs = [];\n\n"
+ " if (radius1[0] > 0 || radius1[1] > 0) {\n"
+ " borderArgs.push([\"line\", outer1[1].start.x, outer1[1].start.y]);\n"
+ " outer1[1].curveTo(borderArgs);\n"
+ " } else {\n"
+ " borderArgs.push([ \"line\", borderData.c1[0], borderData.c1[1]]);\n"
+ " }\n\n"
+ " if (radius2[0] > 0 || radius2[1] > 0) {\n"
+ " borderArgs.push([\"line\", outer2[0].start.x, outer2[0].start.y]);\n"
+ " outer2[0].curveTo(borderArgs);\n"
+ " borderArgs.push([\"line\", inner2[0].end.x, inner2[0].end.y]);\n"
+ " inner2[0].curveToReversed(borderArgs);\n"
+ " } else {\n"
+ " borderArgs.push([\"line\", borderData.c2[0], borderData.c2[1]]);\n"
+ " borderArgs.push([\"line\", borderData.c3[0], borderData.c3[1]]);\n"
+ " }\n\n"
+ " if (radius1[0] > 0 || radius1[1] > 0) {\n"
+ " borderArgs.push([\"line\", inner1[1].end.x, inner1[1].end.y]);\n"
+ " inner1[1].curveToReversed(borderArgs);\n"
+ " } else {\n"
+ " borderArgs.push([\"line\", borderData.c4[0], borderData.c4[1]]);\n"
+ " }\n\n"
+ " return borderArgs;\n"
+ "}\n\n"
+ "function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {\n"
+ " if (radius1[0] > 0 || radius1[1] > 0) {\n"
+ " borderArgs.push([\"line\", corner1[0].start.x, corner1[0].start.y]);\n"
+ " corner1[0].curveTo(borderArgs);\n"
+ " corner1[1].curveTo(borderArgs);\n"
+ " } else {\n"
+ " borderArgs.push([\"line\", x, y]);\n"
+ " }\n\n"
+ " if (radius2[0] > 0 || radius2[1] > 0) {\n"
+ " borderArgs.push([\"line\", corner2[0].start.x, corner2[0].start.y]);\n"
+ " }\n"
+ "}\n\n"
+ "function negativeZIndex(container) {\n"
+ " return container.cssInt(\"zIndex\") < 0;\n"
+ "}\n\n"
+ "function positiveZIndex(container) {\n"
+ " return container.cssInt(\"zIndex\") > 0;\n"
+ "}\n\n"
+ "function zIndex0(container) {\n"
+ " return container.cssInt(\"zIndex\") === 0;\n"
+ "}\n\n"
+ "function inlineLevel(container) {\n"
+ " return [\"inline\", \"inline-block\", \"inline-table\"].indexOf(container.css(\"display\")) !== -1;\n"
+ "}\n\n"
+ "function isStackingContext(container) {\n"
+ " return (container instanceof StackingContext);\n"
+ "}\n\n"
+ "function hasText(container) {\n"
+ " return container.node.data.trim().length > 0;\n"
+ "}\n\n"
+ "function noLetterSpacing(container) {\n"
+ " return (/^(normal|none|0px)$/.test(container.parent.css(\"letterSpacing\")));\n"
+ "}\n\n"
+ "function getBorderRadiusData(container) {\n"
+ " return [\"TopLeft\", \"TopRight\", \"BottomRight\", \"BottomLeft\"].map(function(side) {\n"
+ " var value = container.css('border' + side + 'Radius');\n"
+ " var arr = value.split(\" \");\n"
+ " if (arr.length <= 1) {\n"
+ " arr[1] = arr[0];\n"
+ " }\n"
+ " return arr.map(asInt);\n"
+ " });\n"
+ "}\n\n"
+ "function renderableNode(node) {\n"
+ " return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);\n"
+ "}\n\n"
+ "function isPositionedForStacking(container) {\n"
+ " var position = container.css(\"position\");\n"
+ " var zIndex = ([\"absolute\", \"relative\", \"fixed\"].indexOf(position) !== -1) ? container.css(\"zIndex\") : \"auto\";\n"
+ " return zIndex !== \"auto\";\n"
+ "}\n\n"
+ "function isPositioned(container) {\n"
+ " return container.css(\"position\") !== \"static\";\n"
+ "}\n\n"
+ "function isFloating(container) {\n"
+ " return container.css(\"float\") !== \"none\";\n"
+ "}\n\n"
+ "function isInlineBlock(container) {\n"
+ " return [\"inline-block\", \"inline-table\"].indexOf(container.css(\"display\")) !== -1;\n"
+ "}\n\n"
+ "function not(callback) {\n"
+ " var context = this;\n"
+ " return function() {\n"
+ " return !callback.apply(context, arguments);\n"
+ " };\n"
+ "}\n\n"
+ "function isElement(container) {\n"
+ " return container.node.nodeType === Node.ELEMENT_NODE;\n"
+ "}\n\n"
+ "function isPseudoElement(container) {\n"
+ " return container.isPseudoElement === true;\n"
+ "}\n\n"
+ "function isTextNode(container) {\n"
+ " return container.node.nodeType === Node.TEXT_NODE;\n"
+ "}\n\n"
+ "function zIndexSort(contexts) {\n"
+ " return function(a, b) {\n"
+ " return (a.cssInt(\"zIndex\") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt(\"zIndex\") + (contexts.indexOf(b) / contexts.length));\n"
+ " };\n"
+ "}\n\n"
+ "function hasOpacity(container) {\n"
+ " return container.getOpacity() < 1;\n"
+ "}\n\n"
+ "function bind(callback, context) {\n"
+ " return function() {\n"
+ " return callback.apply(context, arguments);\n"
+ " };\n"
+ "}\n\n"
+ "function asInt(value) {\n"
+ " return parseInt(value, 10);\n"
+ "}\n\n"
+ "function getWidth(border) {\n"
+ " return border.width;\n"
+ "}\n\n"
+ "function nonIgnoredElement(nodeContainer) {\n"
+ " return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || [\"SCRIPT\", \"HEAD\", \"TITLE\", \"OBJECT\", \"BR\", \"OPTION\"].indexOf(nodeContainer.node.nodeName) === -1);\n"
+ "}\n\n"
+ "function flatten(arrays) {\n"
+ " return [].concat.apply([], arrays);\n"
+ "}\n\n"
+ "function stripQuotes(content) {\n"
+ " var first = content.substr(0, 1);\n"
+ " return (first === content.substr(content.length - 1) && first.match(/'|\"/)) ? content.substr(1, content.length - 2) : content;\n"
+ "}\n\n"
+ "function getWords(characters) {\n"
+ " var words = [], i = 0, onWordBoundary = false, word;\n"
+ " while(characters.length) {\n"
+ " if (isWordBoundary(characters[i]) === onWordBoundary) {\n"
+ " word = characters.splice(0, i);\n"
+ " if (word.length) {\n"
+ " words.push(window.html2canvas.punycode.ucs2.encode(word));\n"
+ " }\n"
+ " onWordBoundary =! onWordBoundary;\n"
+ " i = 0;\n"
+ " } else {\n"
+ " i++;\n"
+ " }\n\n"
+ " if (i >= characters.length) {\n"
+ " word = characters.splice(0, i);\n"
+ " if (word.length) {\n"
+ " words.push(window.html2canvas.punycode.ucs2.encode(word));\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " return words;\n"
+ "}\n\n"
+ "function isWordBoundary(characterCode) {\n"
+ " return [\n"
+ " 32, // <space>\n"
+ " 13, // \\r\n"
+ " 10, // \\n\n"
+ " 9, // \\t\n"
+ " 45 // -\n"
+ " ].indexOf(characterCode) !== -1;\n"
+ "}\n\n"
+ "function hasUnicode(string) {\n"
+ " return (/[^\\u0000-\\u00ff]/).test(string);\n"
+ "}\n\n"
+ "function Proxy(src, proxyUrl, document) {\n"
+ " if (!proxyUrl) {\n"
+ " return Promise.reject(\"No proxy configured\");\n"
+ " }\n"
+ " var callback = createCallback(supportsCORS);\n"
+ " var url = createProxyUrl(proxyUrl, src, callback);\n\n"
+ " return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) {\n"
+ " return decode64(response.content);\n"
+ " }));\n"
+ "}\n"
+ "var proxyCount = 0;\n\n"
+ "var supportsCORS = ('withCredentials' in new XMLHttpRequest());\n"
+ "var supportsCORSImage = ('crossOrigin' in new Image());\n\n"
+ "function ProxyURL(src, proxyUrl, document) {\n"
+ " var callback = createCallback(supportsCORSImage);\n"
+ " var url = createProxyUrl(proxyUrl, src, callback);\n"
+ " return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) {\n"
+ " return \"data:\" + response.type + \";base64,\" + response.content;\n"
+ " }));\n"
+ "}\n\n"
+ "function jsonp(document, url, callback) {\n"
+ " return new Promise(function(resolve, reject) {\n"
+ " var s = document.createElement(\"script\");\n"
+ " var cleanup = function() {\n"
+ " delete window.html2canvas.proxy[callback];\n"
+ " document.body.removeChild(s);\n"
+ " };\n"
+ " window.html2canvas.proxy[callback] = function(response) {\n"
+ " cleanup();\n"
+ " resolve(response);\n"
+ " };\n"
+ " s.src = url;\n"
+ " s.onerror = function(e) {\n"
+ " cleanup();\n"
+ " reject(e);\n"
+ " };\n"
+ " document.body.appendChild(s);\n"
+ " });\n"
+ "}\n\n"
+ "function createCallback(useCORS) {\n"
+ " return !useCORS ? \"html2canvas_\" + Date.now() + \"_\" + (++proxyCount) + \"_\" + Math.round(Math.random() * 100000) : \"\";\n"
+ "}\n\n"
+ "function createProxyUrl(proxyUrl, src, callback) {\n"
+ " return proxyUrl + \"?url=\" + encodeURIComponent(src) + (callback.length ? \"&callback=html2canvas.proxy.\" + callback : \"\");\n"
+ "}\n\n"
+ "function ProxyImageContainer(src, proxy) {\n"
+ " var script = document.createElement(\"script\");\n"
+ " var link = document.createElement(\"a\");\n"
+ " link.href = src;\n"
+ " src = link.href;\n"
+ " this.src = src;\n"
+ " this.image = new Image();\n"
+ " var self = this;\n"
+ " this.promise = new Promise(function(resolve, reject) {\n"
+ " self.image.crossOrigin = \"Anonymous\";\n"
+ " self.image.onload = resolve;\n"
+ " self.image.onerror = reject;\n\n"
+ " new ProxyURL(src, proxy, document).then(function(url) {\n"
+ " self.image.src = url;\n"
+ " })['catch'](reject);\n"
+ " });\n"
+ "}\n\n"
+ "function PseudoElementContainer(node, parent, type) {\n"
+ " NodeContainer.call(this, node, parent);\n"
+ " this.isPseudoElement = true;\n"
+ " this.before = type === \":before\";\n"
+ "}\n\n"
+ "PseudoElementContainer.prototype.cloneTo = function(stack) {\n"
+ " PseudoElementContainer.prototype.cloneTo.call(this, stack);\n"
+ " stack.isPseudoElement = true;\n"
+ " stack.before = this.before;\n"
+ "};\n\n"
+ "PseudoElementContainer.prototype = Object.create(NodeContainer.prototype);\n\n"
+ "PseudoElementContainer.prototype.appendToDOM = function() {\n"
+ " if (this.before) {\n"
+ " this.parent.node.insertBefore(this.node, this.parent.node.firstChild);\n"
+ " } else {\n"
+ " this.parent.node.appendChild(this.node);\n"
+ " }\n"
+ " this.parent.node.className += \" \" + this.getHideClass();\n"
+ "};\n\n"
+ "PseudoElementContainer.prototype.cleanDOM = function() {\n"
+ " this.node.parentNode.removeChild(this.node);\n"
+ " this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), \"\");\n"
+ "};\n\n"
+ "PseudoElementContainer.prototype.getHideClass = function() {\n"
+ " return this[\"PSEUDO_HIDE_ELEMENT_CLASS_\" + (this.before ? \"BEFORE\" : \"AFTER\")];\n"
+ "};\n\n"
+ "PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = \"___html2canvas___pseudoelement_before\";\n"
+ "PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = \"___html2canvas___pseudoelement_after\";\n\n"
+ "function Renderer(width, height, images, options, document) {\n"
+ " this.width = width;\n"
+ " this.height = height;\n"
+ " this.images = images;\n"
+ " this.options = options;\n"
+ " this.document = document;\n"
+ "}\n\n"
+ "Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) {\n"
+ " var paddingLeft = container.cssInt('paddingLeft'),\n"
+ " paddingTop = container.cssInt('paddingTop'),\n"
+ " paddingRight = container.cssInt('paddingRight'),\n"
+ " paddingBottom = container.cssInt('paddingBottom'),\n"
+ " borders = borderData.borders;\n\n"
+ " var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight);\n"
+ " var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom);\n"
+ " this.drawImage(\n"
+ " imageContainer,\n"
+ " 0,\n"
+ " 0,\n"
+ " imageContainer.image.width || width,\n"
+ " imageContainer.image.height || height,\n"
+ " bounds.left + paddingLeft + borders[3].width,\n"
+ " bounds.top + paddingTop + borders[0].width,\n"
+ " width,\n"
+ " height\n"
+ " );\n"
+ "};\n\n"
+ "Renderer.prototype.renderBackground = function(container, bounds, borderData) {\n"
+ " if (bounds.height > 0 && bounds.width > 0) {\n"
+ " this.renderBackgroundColor(container, bounds);\n"
+ " this.renderBackgroundImage(container, bounds, borderData);\n"
+ " }\n"
+ "};\n\n"
+ "Renderer.prototype.renderBackgroundColor = function(container, bounds) {\n"
+ " var color = container.color(\"backgroundColor\");\n"
+ " if (!color.isTransparent()) {\n"
+ " this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color);\n"
+ " }\n"
+ "};\n\n"
+ "Renderer.prototype.renderBorders = function(borders) {\n"
+ " borders.forEach(this.renderBorder, this);\n"
+ "};\n\n"
+ "Renderer.prototype.renderBorder = function(data) {\n"
+ " if (!data.color.isTransparent() && data.args !== null) {\n"
+ " this.drawShape(data.args, data.color);\n"
+ " }\n"
+ "};\n\n"
+ "Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) {\n"
+ " var backgroundImages = container.parseBackgroundImages();\n"
+ " backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {\n"
+ " switch(backgroundImage.method) {\n"
+ " case \"url\":\n"
+ " var image = this.images.get(backgroundImage.args[0]);\n"
+ " if (image) {\n"
+ " this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData);\n"
+ " } else {\n"
+ " log(\"Error loading background-image\", backgroundImage.args[0]);\n"
+ " }\n"
+ " break;\n"
+ " case \"linear-gradient\":\n"
+ " case \"gradient\":\n"
+ " var gradientImage = this.images.get(backgroundImage.value);\n"
+ " if (gradientImage) {\n"
+ " this.renderBackgroundGradient(gradientImage, bounds, borderData);\n"
+ " } else {\n"
+ " log(\"Error loading background-image\", backgroundImage.args[0]);\n"
+ " }\n"
+ " break;\n"
+ " case \"none\":\n"
+ " break;\n"
+ " default:\n"
+ " log(\"Unknown background-image type\", backgroundImage.args[0]);\n"
+ " }\n"
+ " }, this);\n"
+ "};\n\n"
+ "Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) {\n"
+ " var size = container.parseBackgroundSize(bounds, imageContainer.image, index);\n"
+ " var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size);\n"
+ " var repeat = container.parseBackgroundRepeat(index);\n"
+ " switch (repeat) {\n"
+ " case \"repeat-x\":\n"
+ " case \"repeat no-repeat\":\n"
+ " this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData);\n"
+ " break;\n"
+ " case \"repeat-y\":\n"
+ " case \"no-repeat repeat\":\n"
+ " this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData);\n"
+ " break;\n"
+ " case \"no-repeat\":\n"
+ " this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData);\n"
+ " break;\n"
+ " default:\n"
+ " this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]);\n"
+ " break;\n"
+ " }\n"
+ "};\n\n"
+ "function StackingContext(hasOwnStacking, opacity, element, parent) {\n"
+ " NodeContainer.call(this, element, parent);\n"
+ " this.ownStacking = hasOwnStacking;\n"
+ " this.contexts = [];\n"
+ " this.children = [];\n"
+ " this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;\n"
+ "}\n\n"
+ "StackingContext.prototype = Object.create(NodeContainer.prototype);\n\n"
+ "StackingContext.prototype.getParentStack = function(context) {\n"
+ " var parentStack = (this.parent) ? this.parent.stack : null;\n"
+ " return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;\n"
+ "};\n\n"
+ "function Support(document) {\n"
+ " this.rangeBounds = this.testRangeBounds(document);\n"
+ " this.cors = this.testCORS();\n"
+ " this.svg = this.testSVG();\n"
+ "}\n\n"
+ "Support.prototype.testRangeBounds = function(document) {\n"
+ " var range, testElement, rangeBounds, rangeHeight, support = false;\n\n"
+ " if (document.createRange) {\n"
+ " range = document.createRange();\n"
+ " if (range.getBoundingClientRect) {\n"
+ " testElement = document.createElement('boundtest');\n"
+ " testElement.style.height = \"123px\";\n"
+ " testElement.style.display = \"block\";\n"
+ " document.body.appendChild(testElement);\n\n"
+ " range.selectNode(testElement);\n"
+ " rangeBounds = range.getBoundingClientRect();\n"
+ " rangeHeight = rangeBounds.height;\n\n"
+ " if (rangeHeight === 123) {\n"
+ " support = true;\n"
+ " }\n"
+ " document.body.removeChild(testElement);\n"
+ " }\n"
+ " }\n\n"
+ " return support;\n"
+ "};\n\n"
+ "Support.prototype.testCORS = function() {\n"
+ " return typeof((new Image()).crossOrigin) !== \"undefined\";\n"
+ "};\n\n"
+ "Support.prototype.testSVG = function() {\n"
+ " var img = new Image();\n"
+ " var canvas = document.createElement(\"canvas\");\n"
+ " var ctx = canvas.getContext(\"2d\");\n"
+ " img.src = \"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>\";\n\n"
+ " try {\n"
+ " ctx.drawImage(img, 0, 0);\n"
+ " canvas.toDataURL();\n"
+ " } catch(e) {\n"
+ " return false;\n"
+ " }\n"
+ " return true;\n"
+ "};\n\n"
+ "function SVGContainer(src) {\n"
+ " this.src = src;\n"
+ " this.image = null;\n"
+ " var self = this;\n\n"
+ " this.promise = this.hasFabric().then(function() {\n"
+ " return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src));\n"
+ " }).then(function(svg) {\n"
+ " return new Promise(function(resolve) {\n"
+ " html2canvas.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve));\n"
+ " });\n"
+ " });\n"
+ "}\n\n"
+ "SVGContainer.prototype.hasFabric = function() {\n"
+ " return !html2canvas.fabric ? Promise.reject(new Error(\"html2canvas.svg.js is not loaded, cannot render svg\")) : Promise.resolve();\n"
+ "};\n\n"
+ "SVGContainer.prototype.inlineFormatting = function(src) {\n"
+ " return (/^data:image\\/svg\\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src);\n"
+ "};\n\n"
+ "SVGContainer.prototype.removeContentType = function(src) {\n"
+ " return src.replace(/^data:image\\/svg\\+xml(;base64)?,/,'');\n"
+ "};\n\n"
+ "SVGContainer.prototype.isInline = function(src) {\n"
+ " return (/^data:image\\/svg\\+xml/i.test(src));\n"
+ "};\n\n"
+ "SVGContainer.prototype.createCanvas = function(resolve) {\n"
+ " var self = this;\n"
+ " return function (objects, options) {\n"
+ " var canvas = new html2canvas.fabric.StaticCanvas('c');\n"
+ " self.image = canvas.lowerCanvasEl;\n"
+ " canvas\n"
+ " .setWidth(options.width)\n"
+ " .setHeight(options.height)\n"
+ " .add(html2canvas.fabric.util.groupSVGElements(objects, options))\n"
+ " .renderAll();\n"
+ " resolve(canvas.lowerCanvasEl);\n"
+ " };\n"
+ "};\n\n"
+ "SVGContainer.prototype.decode64 = function(str) {\n"
+ " return (typeof(window.atob) === \"function\") ? window.atob(str) : decode64(str);\n"
+ "};\n\n"
+ "/*\n"
+ " * base64-arraybuffer\n"
+ " * https://github.com/niklasvh/base64-arraybuffer\n"
+ " *\n"
+ " * Copyright (c) 2012 Niklas von Hertzen\n"
+ " * Licensed under the MIT license.\n"
+ " */\n\n"
+ "function decode64(base64) {\n"
+ " var chars = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n"
+ " var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3;\n\n"
+ " var output = \"\";\n\n"
+ " for (i = 0; i < len; i+=4) {\n"
+ " encoded1 = chars.indexOf(base64[i]);\n"
+ " encoded2 = chars.indexOf(base64[i+1]);\n"
+ " encoded3 = chars.indexOf(base64[i+2]);\n"
+ " encoded4 = chars.indexOf(base64[i+3]);\n\n"
+ " byte1 = (encoded1 << 2) | (encoded2 >> 4);\n"
+ " byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);\n"
+ " byte3 = ((encoded3 & 3) << 6) | encoded4;\n"
+ " if (encoded3 === 64) {\n"
+ " output += String.fromCharCode(byte1);\n"
+ " } else if (encoded4 === 64 || encoded4 === -1) {\n"
+ " output += String.fromCharCode(byte1, byte2);\n"
+ " } else{\n"
+ " output += String.fromCharCode(byte1, byte2, byte3);\n"
+ " }\n"
+ " }\n\n"
+ " return output;\n"
+ "}\n\n"
+ "function SVGNodeContainer(node, native) {\n"
+ " this.src = node;\n"
+ " this.image = null;\n"
+ " var self = this;\n\n"
+ " this.promise = native ? new Promise(function(resolve, reject) {\n"
+ " self.image = new Image();\n"
+ " self.image.onload = resolve;\n"
+ " self.image.onerror = reject;\n"
+ " self.image.src = \"data:image/svg+xml,\" + (new XMLSerializer()).serializeToString(node);\n"
+ " if (self.image.complete === true) {\n"
+ " resolve(self.image);\n"
+ " }\n"
+ " }) : this.hasFabric().then(function() {\n"
+ " return new Promise(function(resolve) {\n"
+ " html2canvas.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve));\n"
+ " });\n"
+ " });\n"
+ "}\n\n"
+ "SVGNodeContainer.prototype = Object.create(SVGContainer.prototype);\n\n"
+ "function TextContainer(node, parent) {\n"
+ " NodeContainer.call(this, node, parent);\n"
+ "}\n\n"
+ "TextContainer.prototype = Object.create(NodeContainer.prototype);\n\n"
+ "TextContainer.prototype.applyTextTransform = function() {\n"
+ " this.node.data = this.transform(this.parent.css(\"textTransform\"));\n"
+ "};\n\n"
+ "TextContainer.prototype.transform = function(transform) {\n"
+ " var text = this.node.data;\n"
+ " switch(transform){\n"
+ " case \"lowercase\":\n"
+ " return text.toLowerCase();\n"
+ " case \"capitalize\":\n"
+ " return text.replace(/(^|\\s|:|-|\\(|\\))([a-z])/g, capitalize);\n"
+ " case \"uppercase\":\n"
+ " return text.toUpperCase();\n"
+ " default:\n"
+ " return text;\n"
+ " }\n"
+ "};\n\n"
+ "function capitalize(m, p1, p2) {\n"
+ " if (m.length > 0) {\n"
+ " return p1 + p2.toUpperCase();\n"
+ " }\n"
+ "}\n\n"
+ "function WebkitGradientContainer(imageData) {\n"
+ " GradientContainer.apply(this, arguments);\n"
+ " this.type = (imageData.args[0] === \"linear\") ? this.TYPES.LINEAR : this.TYPES.RADIAL;\n"
+ "}\n\n"
+ "WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);\n\n"
+ "function XHR(url) {\n"
+ " return new Promise(function(resolve, reject) {\n"
+ " var xhr = new XMLHttpRequest();\n"
+ " xhr.open('GET', url);\n\n"
+ " xhr.onload = function() {\n"
+ " if (xhr.status === 200) {\n"
+ " resolve(xhr.responseText);\n"
+ " } else {\n"
+ " reject(new Error(xhr.statusText));\n"
+ " }\n"
+ " };\n\n"
+ " xhr.onerror = function() {\n"
+ " reject(new Error(\"Network Error\"));\n"
+ " };\n\n"
+ " xhr.send();\n"
+ " });\n"
+ "}\n\n"
+ "function CanvasRenderer(width, height) {\n"
+ " Renderer.apply(this, arguments);\n"
+ " this.canvas = this.options.canvas || this.document.createElement(\"canvas\");\n"
+ " if (!this.options.canvas) {\n"
+ " this.canvas.width = width;\n"
+ " this.canvas.height = height;\n"
+ " }\n"
+ " this.ctx = this.canvas.getContext(\"2d\");\n"
+ " this.taintCtx = this.document.createElement(\"canvas\").getContext(\"2d\");\n"
+ " this.ctx.textBaseline = \"bottom\";\n"
+ " this.variables = {};\n"
+ " log(\"Initialized CanvasRenderer with size\", width, \"x\", height);\n"
+ "}\n\n"
+ "CanvasRenderer.prototype = Object.create(Renderer.prototype);\n\n"
+ "CanvasRenderer.prototype.setFillStyle = function(fillStyle) {\n"
+ " this.ctx.fillStyle = typeof(fillStyle) === \"object\" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle;\n"
+ " return this.ctx;\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) {\n"
+ " this.setFillStyle(color).fillRect(left, top, width, height);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.circle = function(left, top, size, color) {\n"
+ " this.setFillStyle(color);\n"
+ " this.ctx.beginPath();\n"
+ " this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true);\n"
+ " this.ctx.closePath();\n"
+ " this.ctx.fill();\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) {\n"
+ " this.circle(left, top, size, color);\n"
+ " this.ctx.strokeStyle = strokeColor.toString();\n"
+ " this.ctx.stroke();\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.drawShape = function(shape, color) {\n"
+ " this.shape(shape);\n"
+ " this.setFillStyle(color).fill();\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.taints = function(imageContainer) {\n"
+ " if (imageContainer.tainted === null) {\n"
+ " this.taintCtx.drawImage(imageContainer.image, 0, 0);\n"
+ " try {\n"
+ " this.taintCtx.getImageData(0, 0, 1, 1);\n"
+ " imageContainer.tainted = false;\n"
+ " } catch(e) {\n"
+ " this.taintCtx = document.createElement(\"canvas\").getContext(\"2d\");\n"
+ " imageContainer.tainted = true;\n"
+ " }\n"
+ " }\n\n"
+ " return imageContainer.tainted;\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) {\n"
+ " if (!this.taints(imageContainer) || this.options.allowTaint) {\n"
+ " this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh);\n"
+ " }\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.clip = function(shapes, callback, context) {\n"
+ " this.ctx.save();\n"
+ " shapes.filter(hasEntries).forEach(function(shape) {\n"
+ " this.shape(shape).clip();\n"
+ " }, this);\n"
+ " callback.call(context);\n"
+ " this.ctx.restore();\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.shape = function(shape) {\n"
+ " this.ctx.beginPath();\n"
+ " shape.forEach(function(point, index) {\n"
+ " if (point[0] === \"rect\") {\n"
+ " this.ctx.rect.apply(this.ctx, point.slice(1));\n"
+ " } else {\n"
+ " this.ctx[(index === 0) ? \"moveTo\" : point[0] + \"To\" ].apply(this.ctx, point.slice(1));\n"
+ " }\n"
+ " }, this);\n"
+ " this.ctx.closePath();\n"
+ " return this.ctx;\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) {\n"
+ " this.setFillStyle(color).font = [style, variant, weight, size, family].join(\" \").split(\",\")[0];\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) {\n"
+ " this.setVariable(\"shadowColor\", color.toString())\n"
+ " .setVariable(\"shadowOffsetY\", offsetX)\n"
+ " .setVariable(\"shadowOffsetX\", offsetY)\n"
+ " .setVariable(\"shadowBlur\", blur);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.clearShadow = function() {\n"
+ " this.setVariable(\"shadowColor\", \"rgba(0,0,0,0)\");\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.setOpacity = function(opacity) {\n"
+ " this.ctx.globalAlpha = opacity;\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.setTransform = function(transform) {\n"
+ " this.ctx.translate(transform.origin[0], transform.origin[1]);\n"
+ " this.ctx.transform.apply(this.ctx, transform.matrix);\n"
+ " this.ctx.translate(-transform.origin[0], -transform.origin[1]);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.setVariable = function(property, value) {\n"
+ " if (this.variables[property] !== value) {\n"
+ " this.variables[property] = this.ctx[property] = value;\n"
+ " }\n\n"
+ " return this;\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.text = function(text, left, bottom) {\n"
+ " this.ctx.fillText(text, left, bottom);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) {\n"
+ " var shape = [\n"
+ " [\"line\", Math.round(left), Math.round(top)],\n"
+ " [\"line\", Math.round(left + width), Math.round(top)],\n"
+ " [\"line\", Math.round(left + width), Math.round(height + top)],\n"
+ " [\"line\", Math.round(left), Math.round(height + top)]\n"
+ " ];\n"
+ " this.clip([shape], function() {\n"
+ " this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);\n"
+ " }, this);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) {\n"
+ " var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop);\n"
+ " this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), \"repeat\"));\n"
+ " this.ctx.translate(offsetX, offsetY);\n"
+ " this.ctx.fill();\n"
+ " this.ctx.translate(-offsetX, -offsetY);\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) {\n"
+ " if (gradientImage instanceof LinearGradientContainer) {\n"
+ " var gradient = this.ctx.createLinearGradient(\n"
+ " bounds.left + bounds.width * gradientImage.x0,\n"
+ " bounds.top + bounds.height * gradientImage.y0,\n"
+ " bounds.left + bounds.width * gradientImage.x1,\n"
+ " bounds.top + bounds.height * gradientImage.y1);\n"
+ " gradientImage.colorStops.forEach(function(colorStop) {\n"
+ " gradient.addColorStop(colorStop.stop, colorStop.color.toString());\n"
+ " });\n"
+ " this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient);\n"
+ " }\n"
+ "};\n\n"
+ "CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {\n"
+ " var image = imageContainer.image;\n"
+ " if(image.width === size.width && image.height === size.height) {\n"
+ " return image;\n"
+ " }\n\n"
+ " var ctx, canvas = document.createElement('canvas');\n"
+ " canvas.width = size.width;\n"
+ " canvas.height = size.height;\n"
+ " ctx = canvas.getContext(\"2d\");\n"
+ " ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height );\n"
+ " return canvas;\n"
+ "};\n\n"
+ "function hasEntries(array) {\n"
+ " return array.length > 0;\n"
+ "}\n\n"
+ "}).call({}, typeof(window) !== \"undefined\" ? window : undefined, typeof(document) !== \"undefined\" ? document : undefined);";
final Pattern pattern = Pattern.compile(regex);
final Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
System.out.println("Full match: " + matcher.group(0));
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println("Group " + i + ": " + matcher.group(i));
}
}
}
}
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Java, please visit: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html