Regular Expressions 101

Save & Share

Flavor

  • PCRE2 (PHP >=7.3)
  • PCRE (PHP <7.3)
  • ECMAScript (JavaScript)
  • Python
  • Golang
  • Java 8
  • .NET 7.0 (C#)
  • Rust
  • Regex Flavor Guide

Function

  • Match
  • Substitution
  • List
  • Unit Tests

Tools

Sponsors
There are currently no sponsors. Become a sponsor today!
An explanation of your regex will be automatically generated as you type.
Detailed match information will be displayed here automatically.
  • All Tokens
  • Common Tokens
  • General Tokens
  • Anchors
  • Meta Sequences
  • Quantifiers
  • Group Constructs
  • Character Classes
  • Flags/Modifiers
  • Substitution
  • A single character of: a, b or c
    [abc]
  • A character except: a, b or c
    [^abc]
  • A character in the range: a-z
    [a-z]
  • A character not in the range: a-z
    [^a-z]
  • A character in the range: a-z or A-Z
    [a-zA-Z]
  • Any single character
    .
  • Alternate - match either a or b
    a|b
  • Any whitespace character
    \s
  • Any non-whitespace character
    \S
  • Any digit
    \d
  • Any non-digit
    \D
  • Any word character
    \w
  • Any non-word character
    \W
  • Non-capturing group
    (?:...)
  • Capturing group
    (...)
  • Zero or one of a
    a?
  • Zero or more of a
    a*
  • One or more of a
    a+
  • Exactly 3 of a
    a{3}
  • 3 or more of a
    a{3,}
  • Between 3 and 6 of a
    a{3,6}
  • Start of string
    ^
  • End of string
    $
  • A word boundary
    \b
  • Non-word boundary
    \B

Regular Expression

/
/
g

Test String

Code Generator

Generated Code

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