html2canvas - custom build of 0.5.0-beta2

I modified some stuff to fit in to my script, use official version instead.

بۇ قوليازمىنى بىۋاسىتە قاچىلاشقا بولمايدۇ. بۇ باشقا قوليازمىلارنىڭ ئىشلىتىشى ئۈچۈن تەمىنلەنگەن ئامبار بولۇپ، ئىشلىتىش ئۈچۈن مېتا كۆرسەتمىسىگە قىستۇرىدىغان كود: // @require https://update.greatest.deepsurf.us/scripts/13980/87728/html2canvas%20-%20custom%20build%20of%20050-beta2.js

  1. /*
  2. html2canvas 0.5.0-beta2 <http://html2canvas.hertzen.com>
  3. Copyright (c) 2015 Niklas von Hertzen
  4.  
  5. Released under MIT License
  6.  
  7. Custom build by Jixun.Moe; for code changes please see:
  8. https://github.com/JixunMoe/html2canvas
  9. */
  10.  
  11. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.html2canvas = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  12. (function (global){
  13. /*! https://mths.be/punycode v1.3.2 by @mathias */
  14. ;(function(root) {
  15.  
  16. /** Detect free variables */
  17. var freeExports = typeof exports == 'object' && exports &&
  18. !exports.nodeType && exports;
  19. var freeModule = typeof module == 'object' && module &&
  20. !module.nodeType && module;
  21. var freeGlobal = typeof global == 'object' && global;
  22. if (
  23. freeGlobal.global === freeGlobal ||
  24. freeGlobal.window === freeGlobal ||
  25. freeGlobal.self === freeGlobal
  26. ) {
  27. root = freeGlobal;
  28. }
  29.  
  30. /**
  31. * The `punycode` object.
  32. * @name punycode
  33. * @type Object
  34. */
  35. var punycode,
  36.  
  37. /** Highest positive signed 32-bit float value */
  38. maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
  39.  
  40. /** Bootstring parameters */
  41. base = 36,
  42. tMin = 1,
  43. tMax = 26,
  44. skew = 38,
  45. damp = 700,
  46. initialBias = 72,
  47. initialN = 128, // 0x80
  48. delimiter = '-', // '\x2D'
  49.  
  50. /** Regular expressions */
  51. regexPunycode = /^xn--/,
  52. regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
  53. regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
  54.  
  55. /** Error messages */
  56. errors = {
  57. 'overflow': 'Overflow: input needs wider integers to process',
  58. 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
  59. 'invalid-input': 'Invalid input'
  60. },
  61.  
  62. /** Convenience shortcuts */
  63. baseMinusTMin = base - tMin,
  64. floor = Math.floor,
  65. stringFromCharCode = String.fromCharCode,
  66.  
  67. /** Temporary variable */
  68. key;
  69.  
  70. /*--------------------------------------------------------------------------*/
  71.  
  72. /**
  73. * A generic error utility function.
  74. * @private
  75. * @param {String} type The error type.
  76. * @returns {Error} Throws a `RangeError` with the applicable error message.
  77. */
  78. function error(type) {
  79. throw RangeError(errors[type]);
  80. }
  81.  
  82. /**
  83. * A generic `Array#map` utility function.
  84. * @private
  85. * @param {Array} array The array to iterate over.
  86. * @param {Function} callback The function that gets called for every array
  87. * item.
  88. * @returns {Array} A new array of values returned by the callback function.
  89. */
  90. function map(array, fn) {
  91. var length = array.length;
  92. var result = [];
  93. while (length--) {
  94. result[length] = fn(array[length]);
  95. }
  96. return result;
  97. }
  98.  
  99. /**
  100. * A simple `Array#map`-like wrapper to work with domain name strings or email
  101. * addresses.
  102. * @private
  103. * @param {String} domain The domain name or email address.
  104. * @param {Function} callback The function that gets called for every
  105. * character.
  106. * @returns {Array} A new string of characters returned by the callback
  107. * function.
  108. */
  109. function mapDomain(string, fn) {
  110. var parts = string.split('@');
  111. var result = '';
  112. if (parts.length > 1) {
  113. // In email addresses, only the domain name should be punycoded. Leave
  114. // the local part (i.e. everything up to `@`) intact.
  115. result = parts[0] + '@';
  116. string = parts[1];
  117. }
  118. // Avoid `split(regex)` for IE8 compatibility. See #17.
  119. string = string.replace(regexSeparators, '\x2E');
  120. var labels = string.split('.');
  121. var encoded = map(labels, fn).join('.');
  122. return result + encoded;
  123. }
  124.  
  125. /**
  126. * Creates an array containing the numeric code points of each Unicode
  127. * character in the string. While JavaScript uses UCS-2 internally,
  128. * this function will convert a pair of surrogate halves (each of which
  129. * UCS-2 exposes as separate characters) into a single code point,
  130. * matching UTF-16.
  131. * @see `punycode.ucs2.encode`
  132. * @see <https://mathiasbynens.be/notes/javascript-encoding>
  133. * @memberOf punycode.ucs2
  134. * @name decode
  135. * @param {String} string The Unicode input string (UCS-2).
  136. * @returns {Array} The new array of code points.
  137. */
  138. function ucs2decode(string) {
  139. var output = [],
  140. counter = 0,
  141. length = string.length,
  142. value,
  143. extra;
  144. while (counter < length) {
  145. value = string.charCodeAt(counter++);
  146. if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
  147. // high surrogate, and there is a next character
  148. extra = string.charCodeAt(counter++);
  149. if ((extra & 0xFC00) == 0xDC00) { // low surrogate
  150. output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
  151. } else {
  152. // unmatched surrogate; only append this code unit, in case the next
  153. // code unit is the high surrogate of a surrogate pair
  154. output.push(value);
  155. counter--;
  156. }
  157. } else {
  158. output.push(value);
  159. }
  160. }
  161. return output;
  162. }
  163.  
  164. /**
  165. * Creates a string based on an array of numeric code points.
  166. * @see `punycode.ucs2.decode`
  167. * @memberOf punycode.ucs2
  168. * @name encode
  169. * @param {Array} codePoints The array of numeric code points.
  170. * @returns {String} The new Unicode string (UCS-2).
  171. */
  172. function ucs2encode(array) {
  173. return map(array, function(value) {
  174. var output = '';
  175. if (value > 0xFFFF) {
  176. value -= 0x10000;
  177. output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
  178. value = 0xDC00 | value & 0x3FF;
  179. }
  180. output += stringFromCharCode(value);
  181. return output;
  182. }).join('');
  183. }
  184.  
  185. /**
  186. * Converts a basic code point into a digit/integer.
  187. * @see `digitToBasic()`
  188. * @private
  189. * @param {Number} codePoint The basic numeric code point value.
  190. * @returns {Number} The numeric value of a basic code point (for use in
  191. * representing integers) in the range `0` to `base - 1`, or `base` if
  192. * the code point does not represent a value.
  193. */
  194. function basicToDigit(codePoint) {
  195. if (codePoint - 48 < 10) {
  196. return codePoint - 22;
  197. }
  198. if (codePoint - 65 < 26) {
  199. return codePoint - 65;
  200. }
  201. if (codePoint - 97 < 26) {
  202. return codePoint - 97;
  203. }
  204. return base;
  205. }
  206.  
  207. /**
  208. * Converts a digit/integer into a basic code point.
  209. * @see `basicToDigit()`
  210. * @private
  211. * @param {Number} digit The numeric value of a basic code point.
  212. * @returns {Number} The basic code point whose value (when used for
  213. * representing integers) is `digit`, which needs to be in the range
  214. * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
  215. * used; else, the lowercase form is used. The behavior is undefined
  216. * if `flag` is non-zero and `digit` has no uppercase form.
  217. */
  218. function digitToBasic(digit, flag) {
  219. // 0..25 map to ASCII a..z or A..Z
  220. // 26..35 map to ASCII 0..9
  221. return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
  222. }
  223.  
  224. /**
  225. * Bias adaptation function as per section 3.4 of RFC 3492.
  226. * http://tools.ietf.org/html/rfc3492#section-3.4
  227. * @private
  228. */
  229. function adapt(delta, numPoints, firstTime) {
  230. var k = 0;
  231. delta = firstTime ? floor(delta / damp) : delta >> 1;
  232. delta += floor(delta / numPoints);
  233. for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
  234. delta = floor(delta / baseMinusTMin);
  235. }
  236. return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
  237. }
  238.  
  239. /**
  240. * Converts a Punycode string of ASCII-only symbols to a string of Unicode
  241. * symbols.
  242. * @memberOf punycode
  243. * @param {String} input The Punycode string of ASCII-only symbols.
  244. * @returns {String} The resulting string of Unicode symbols.
  245. */
  246. function decode(input) {
  247. // Don't use UCS-2
  248. var output = [],
  249. inputLength = input.length,
  250. out,
  251. i = 0,
  252. n = initialN,
  253. bias = initialBias,
  254. basic,
  255. j,
  256. index,
  257. oldi,
  258. w,
  259. k,
  260. digit,
  261. t,
  262. /** Cached calculation results */
  263. baseMinusT;
  264.  
  265. // Handle the basic code points: let `basic` be the number of input code
  266. // points before the last delimiter, or `0` if there is none, then copy
  267. // the first basic code points to the output.
  268.  
  269. basic = input.lastIndexOf(delimiter);
  270. if (basic < 0) {
  271. basic = 0;
  272. }
  273.  
  274. for (j = 0; j < basic; ++j) {
  275. // if it's not a basic code point
  276. if (input.charCodeAt(j) >= 0x80) {
  277. error('not-basic');
  278. }
  279. output.push(input.charCodeAt(j));
  280. }
  281.  
  282. // Main decoding loop: start just after the last delimiter if any basic code
  283. // points were copied; start at the beginning otherwise.
  284.  
  285. for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
  286.  
  287. // `index` is the index of the next character to be consumed.
  288. // Decode a generalized variable-length integer into `delta`,
  289. // which gets added to `i`. The overflow checking is easier
  290. // if we increase `i` as we go, then subtract off its starting
  291. // value at the end to obtain `delta`.
  292. for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
  293.  
  294. if (index >= inputLength) {
  295. error('invalid-input');
  296. }
  297.  
  298. digit = basicToDigit(input.charCodeAt(index++));
  299.  
  300. if (digit >= base || digit > floor((maxInt - i) / w)) {
  301. error('overflow');
  302. }
  303.  
  304. i += digit * w;
  305. t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
  306.  
  307. if (digit < t) {
  308. break;
  309. }
  310.  
  311. baseMinusT = base - t;
  312. if (w > floor(maxInt / baseMinusT)) {
  313. error('overflow');
  314. }
  315.  
  316. w *= baseMinusT;
  317.  
  318. }
  319.  
  320. out = output.length + 1;
  321. bias = adapt(i - oldi, out, oldi == 0);
  322.  
  323. // `i` was supposed to wrap around from `out` to `0`,
  324. // incrementing `n` each time, so we'll fix that now:
  325. if (floor(i / out) > maxInt - n) {
  326. error('overflow');
  327. }
  328.  
  329. n += floor(i / out);
  330. i %= out;
  331.  
  332. // Insert `n` at position `i` of the output
  333. output.splice(i++, 0, n);
  334.  
  335. }
  336.  
  337. return ucs2encode(output);
  338. }
  339.  
  340. /**
  341. * Converts a string of Unicode symbols (e.g. a domain name label) to a
  342. * Punycode string of ASCII-only symbols.
  343. * @memberOf punycode
  344. * @param {String} input The string of Unicode symbols.
  345. * @returns {String} The resulting Punycode string of ASCII-only symbols.
  346. */
  347. function encode(input) {
  348. var n,
  349. delta,
  350. handledCPCount,
  351. basicLength,
  352. bias,
  353. j,
  354. m,
  355. q,
  356. k,
  357. t,
  358. currentValue,
  359. output = [],
  360. /** `inputLength` will hold the number of code points in `input`. */
  361. inputLength,
  362. /** Cached calculation results */
  363. handledCPCountPlusOne,
  364. baseMinusT,
  365. qMinusT;
  366.  
  367. // Convert the input in UCS-2 to Unicode
  368. input = ucs2decode(input);
  369.  
  370. // Cache the length
  371. inputLength = input.length;
  372.  
  373. // Initialize the state
  374. n = initialN;
  375. delta = 0;
  376. bias = initialBias;
  377.  
  378. // Handle the basic code points
  379. for (j = 0; j < inputLength; ++j) {
  380. currentValue = input[j];
  381. if (currentValue < 0x80) {
  382. output.push(stringFromCharCode(currentValue));
  383. }
  384. }
  385.  
  386. handledCPCount = basicLength = output.length;
  387.  
  388. // `handledCPCount` is the number of code points that have been handled;
  389. // `basicLength` is the number of basic code points.
  390.  
  391. // Finish the basic string - if it is not empty - with a delimiter
  392. if (basicLength) {
  393. output.push(delimiter);
  394. }
  395.  
  396. // Main encoding loop:
  397. while (handledCPCount < inputLength) {
  398.  
  399. // All non-basic code points < n have been handled already. Find the next
  400. // larger one:
  401. for (m = maxInt, j = 0; j < inputLength; ++j) {
  402. currentValue = input[j];
  403. if (currentValue >= n && currentValue < m) {
  404. m = currentValue;
  405. }
  406. }
  407.  
  408. // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
  409. // but guard against overflow
  410. handledCPCountPlusOne = handledCPCount + 1;
  411. if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
  412. error('overflow');
  413. }
  414.  
  415. delta += (m - n) * handledCPCountPlusOne;
  416. n = m;
  417.  
  418. for (j = 0; j < inputLength; ++j) {
  419. currentValue = input[j];
  420.  
  421. if (currentValue < n && ++delta > maxInt) {
  422. error('overflow');
  423. }
  424.  
  425. if (currentValue == n) {
  426. // Represent delta as a generalized variable-length integer
  427. for (q = delta, k = base; /* no condition */; k += base) {
  428. t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
  429. if (q < t) {
  430. break;
  431. }
  432. qMinusT = q - t;
  433. baseMinusT = base - t;
  434. output.push(
  435. stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
  436. );
  437. q = floor(qMinusT / baseMinusT);
  438. }
  439.  
  440. output.push(stringFromCharCode(digitToBasic(q, 0)));
  441. bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
  442. delta = 0;
  443. ++handledCPCount;
  444. }
  445. }
  446.  
  447. ++delta;
  448. ++n;
  449.  
  450. }
  451. return output.join('');
  452. }
  453.  
  454. /**
  455. * Converts a Punycode string representing a domain name or an email address
  456. * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
  457. * it doesn't matter if you call it on a string that has already been
  458. * converted to Unicode.
  459. * @memberOf punycode
  460. * @param {String} input The Punycoded domain name or email address to
  461. * convert to Unicode.
  462. * @returns {String} The Unicode representation of the given Punycode
  463. * string.
  464. */
  465. function toUnicode(input) {
  466. return mapDomain(input, function(string) {
  467. return regexPunycode.test(string)
  468. ? decode(string.slice(4).toLowerCase())
  469. : string;
  470. });
  471. }
  472.  
  473. /**
  474. * Converts a Unicode string representing a domain name or an email address to
  475. * Punycode. Only the non-ASCII parts of the domain name will be converted,
  476. * i.e. it doesn't matter if you call it with a domain that's already in
  477. * ASCII.
  478. * @memberOf punycode
  479. * @param {String} input The domain name or email address to convert, as a
  480. * Unicode string.
  481. * @returns {String} The Punycode representation of the given domain name or
  482. * email address.
  483. */
  484. function toASCII(input) {
  485. return mapDomain(input, function(string) {
  486. return regexNonASCII.test(string)
  487. ? 'xn--' + encode(string)
  488. : string;
  489. });
  490. }
  491.  
  492. /*--------------------------------------------------------------------------*/
  493.  
  494. /** Define the public API */
  495. punycode = {
  496. /**
  497. * A string representing the current Punycode.js version number.
  498. * @memberOf punycode
  499. * @type String
  500. */
  501. 'version': '1.3.2',
  502. /**
  503. * An object of methods to convert from JavaScript's internal character
  504. * representation (UCS-2) to Unicode code points, and back.
  505. * @see <https://mathiasbynens.be/notes/javascript-encoding>
  506. * @memberOf punycode
  507. * @type Object
  508. */
  509. 'ucs2': {
  510. 'decode': ucs2decode,
  511. 'encode': ucs2encode
  512. },
  513. 'decode': decode,
  514. 'encode': encode,
  515. 'toASCII': toASCII,
  516. 'toUnicode': toUnicode
  517. };
  518.  
  519. /** Expose `punycode` */
  520. // Some AMD build optimizers, like r.js, check for specific condition patterns
  521. // like the following:
  522. if (
  523. typeof define == 'function' &&
  524. typeof define.amd == 'object' &&
  525. define.amd
  526. ) {
  527. define('punycode', function() {
  528. return punycode;
  529. });
  530. } else if (freeExports && freeModule) {
  531. if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
  532. freeModule.exports = punycode;
  533. } else { // in Narwhal or RingoJS v0.7.0-
  534. for (key in punycode) {
  535. punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
  536. }
  537. }
  538. } else { // in Rhino or a web browser
  539. root.punycode = punycode;
  540. }
  541.  
  542. }(this));
  543.  
  544. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  545. },{}],2:[function(require,module,exports){
  546. var log = require('./log');
  547.  
  548. function restoreOwnerScroll(ownerDocument, x, y) {
  549. if (ownerDocument.defaultView && (x !== ownerDocument.defaultView.pageXOffset || y !== ownerDocument.defaultView.pageYOffset)) {
  550. ownerDocument.defaultView.scrollTo(x, y);
  551. }
  552. }
  553.  
  554. function cloneCanvasContents(canvas, clonedCanvas) {
  555. try {
  556. if (clonedCanvas) {
  557. clonedCanvas.width = canvas.width;
  558. clonedCanvas.height = canvas.height;
  559. clonedCanvas.getContext("2d").putImageData(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height), 0, 0);
  560. }
  561. } catch(e) {
  562. log("Unable to copy canvas content from", canvas, e);
  563. }
  564. }
  565.  
  566. function cloneNode(node, javascriptEnabled) {
  567. var clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
  568.  
  569. var child = node.firstChild;
  570. while(child) {
  571. if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
  572. clone.appendChild(cloneNode(child, javascriptEnabled));
  573. }
  574. child = child.nextSibling;
  575. }
  576.  
  577. if (node.nodeType === 1) {
  578. clone._scrollTop = node.scrollTop;
  579. clone._scrollLeft = node.scrollLeft;
  580. if (node.nodeName === "CANVAS") {
  581. cloneCanvasContents(node, clone);
  582. } else if (node.nodeName === "TEXTAREA" || node.nodeName === "SELECT") {
  583. clone.value = node.value;
  584. }
  585. }
  586.  
  587. return clone;
  588. }
  589.  
  590. function initNode(node) {
  591. if (node.nodeType === 1) {
  592. node.scrollTop = node._scrollTop;
  593. node.scrollLeft = node._scrollLeft;
  594.  
  595. var child = node.firstChild;
  596. while(child) {
  597. initNode(child);
  598. child = child.nextSibling;
  599. }
  600. }
  601. }
  602.  
  603. module.exports = function(ownerDocument, containerDocument, width, height, options, x ,y) {
  604. var documentElement = cloneNode(ownerDocument.documentElement, options.javascriptEnabled);
  605. var container = containerDocument.createElement("iframe");
  606.  
  607. container.className = "html2canvas-container";
  608. container.style.visibility = "hidden";
  609. container.style.position = "fixed";
  610. container.style.left = "-10000px";
  611. container.style.top = "0px";
  612. container.style.border = "0";
  613. container.width = width;
  614. container.height = height;
  615. container.scrolling = "no"; // ios won't scroll without it
  616. containerDocument.body.appendChild(container);
  617.  
  618. return new Promise(function(resolve) {
  619. var documentClone = container.contentWindow.document;
  620.  
  621. /* Chrome doesn't detect relative background-images assigned in inline <style> sheets when fetched through getComputedStyle
  622. if window url is about:blank, we can assign the url to current by writing onto the document
  623. */
  624. container.contentWindow.onload = container.onload = function() {
  625. var interval = setInterval(function() {
  626. if (documentClone.body.childNodes.length > 0) {
  627. initNode(documentClone.documentElement);
  628. clearInterval(interval);
  629. if (options.type === "view") {
  630. container.contentWindow.scrollTo(x, y);
  631. if ((/(iPad|iPhone|iPod)/g).test(navigator.userAgent) && (container.contentWindow.scrollY !== y || container.contentWindow.scrollX !== x)) {
  632. documentClone.documentElement.style.top = (-y) + "px";
  633. documentClone.documentElement.style.left = (-x) + "px";
  634. documentClone.documentElement.style.position = 'absolute';
  635. }
  636. }
  637. resolve(container);
  638. }
  639. }, 50);
  640. };
  641.  
  642. documentClone.open();
  643. documentClone.write("<!DOCTYPE html><html></html>");
  644. // Chrome scrolls the parent document for some reason after the write to the cloned window???
  645. restoreOwnerScroll(ownerDocument, x, y);
  646. documentClone.replaceChild(documentClone.adoptNode(documentElement), documentClone.documentElement);
  647. documentClone.close();
  648. });
  649. };
  650.  
  651. },{"./log":13}],3:[function(require,module,exports){
  652. // http://dev.w3.org/csswg/css-color/
  653.  
  654. function Color(value) {
  655. this.r = 0;
  656. this.g = 0;
  657. this.b = 0;
  658. this.a = null;
  659. var result = this.fromArray(value) ||
  660. this.namedColor(value) ||
  661. this.rgb(value) ||
  662. this.rgba(value) ||
  663. this.hex6(value) ||
  664. this.hex3(value);
  665. }
  666.  
  667. Color.prototype.darken = function(amount) {
  668. var a = 1 - amount;
  669. return new Color([
  670. Math.round(this.r * a),
  671. Math.round(this.g * a),
  672. Math.round(this.b * a),
  673. this.a
  674. ]);
  675. };
  676.  
  677. Color.prototype.isTransparent = function() {
  678. return this.a === 0;
  679. };
  680.  
  681. Color.prototype.isBlack = function() {
  682. return this.r === 0 && this.g === 0 && this.b === 0;
  683. };
  684.  
  685. Color.prototype.fromArray = function(array) {
  686. if (Array.isArray(array)) {
  687. this.r = Math.min(array[0], 255);
  688. this.g = Math.min(array[1], 255);
  689. this.b = Math.min(array[2], 255);
  690. if (array.length > 3) {
  691. this.a = array[3];
  692. }
  693. }
  694.  
  695. return (Array.isArray(array));
  696. };
  697.  
  698. var _hex3 = /^#([a-f0-9]{3})$/i;
  699.  
  700. Color.prototype.hex3 = function(value) {
  701. var match = null;
  702. if ((match = value.match(_hex3)) !== null) {
  703. this.r = parseInt(match[1][0] + match[1][0], 16);
  704. this.g = parseInt(match[1][1] + match[1][1], 16);
  705. this.b = parseInt(match[1][2] + match[1][2], 16);
  706. }
  707. return match !== null;
  708. };
  709.  
  710. var _hex6 = /^#([a-f0-9]{6})$/i;
  711.  
  712. Color.prototype.hex6 = function(value) {
  713. var match = null;
  714. if ((match = value.match(_hex6)) !== null) {
  715. this.r = parseInt(match[1].substring(0, 2), 16);
  716. this.g = parseInt(match[1].substring(2, 4), 16);
  717. this.b = parseInt(match[1].substring(4, 6), 16);
  718. }
  719. return match !== null;
  720. };
  721.  
  722.  
  723. var _rgb = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/;
  724.  
  725. Color.prototype.rgb = function(value) {
  726. var match = null;
  727. if ((match = value.match(_rgb)) !== null) {
  728. this.r = Number(match[1]);
  729. this.g = Number(match[2]);
  730. this.b = Number(match[3]);
  731. }
  732. return match !== null;
  733. };
  734.  
  735. var _rgba = /^rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d?\.?\d+)\s*\)$/;
  736.  
  737. Color.prototype.rgba = function(value) {
  738. var match = null;
  739. if ((match = value.match(_rgba)) !== null) {
  740. this.r = Number(match[1]);
  741. this.g = Number(match[2]);
  742. this.b = Number(match[3]);
  743. this.a = Number(match[4]);
  744. }
  745. return match !== null;
  746. };
  747.  
  748. Color.prototype.toString = function() {
  749. return this.a !== null && this.a !== 1 ?
  750. "rgba(" + [this.r, this.g, this.b, this.a].join(",") + ")" :
  751. "rgb(" + [this.r, this.g, this.b].join(",") + ")";
  752. };
  753.  
  754. Color.prototype.namedColor = function(value) {
  755. value = value.toLowerCase();
  756. var color = colors[value];
  757. if (color) {
  758. this.r = color[0];
  759. this.g = color[1];
  760. this.b = color[2];
  761. } else if (value === "transparent") {
  762. this.r = this.g = this.b = this.a = 0;
  763. return true;
  764. }
  765.  
  766. return !!color;
  767. };
  768.  
  769. Color.prototype.isColor = true;
  770.  
  771. // 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}, {}))
  772. var colors = {
  773. "aliceblue": [240, 248, 255],
  774. "antiquewhite": [250, 235, 215],
  775. "aqua": [0, 255, 255],
  776. "aquamarine": [127, 255, 212],
  777. "azure": [240, 255, 255],
  778. "beige": [245, 245, 220],
  779. "bisque": [255, 228, 196],
  780. "black": [0, 0, 0],
  781. "blanchedalmond": [255, 235, 205],
  782. "blue": [0, 0, 255],
  783. "blueviolet": [138, 43, 226],
  784. "brown": [165, 42, 42],
  785. "burlywood": [222, 184, 135],
  786. "cadetblue": [95, 158, 160],
  787. "chartreuse": [127, 255, 0],
  788. "chocolate": [210, 105, 30],
  789. "coral": [255, 127, 80],
  790. "cornflowerblue": [100, 149, 237],
  791. "cornsilk": [255, 248, 220],
  792. "crimson": [220, 20, 60],
  793. "cyan": [0, 255, 255],
  794. "darkblue": [0, 0, 139],
  795. "darkcyan": [0, 139, 139],
  796. "darkgoldenrod": [184, 134, 11],
  797. "darkgray": [169, 169, 169],
  798. "darkgreen": [0, 100, 0],
  799. "darkgrey": [169, 169, 169],
  800. "darkkhaki": [189, 183, 107],
  801. "darkmagenta": [139, 0, 139],
  802. "darkolivegreen": [85, 107, 47],
  803. "darkorange": [255, 140, 0],
  804. "darkorchid": [153, 50, 204],
  805. "darkred": [139, 0, 0],
  806. "darksalmon": [233, 150, 122],
  807. "darkseagreen": [143, 188, 143],
  808. "darkslateblue": [72, 61, 139],
  809. "darkslategray": [47, 79, 79],
  810. "darkslategrey": [47, 79, 79],
  811. "darkturquoise": [0, 206, 209],
  812. "darkviolet": [148, 0, 211],
  813. "deeppink": [255, 20, 147],
  814. "deepskyblue": [0, 191, 255],
  815. "dimgray": [105, 105, 105],
  816. "dimgrey": [105, 105, 105],
  817. "dodgerblue": [30, 144, 255],
  818. "firebrick": [178, 34, 34],
  819. "floralwhite": [255, 250, 240],
  820. "forestgreen": [34, 139, 34],
  821. "fuchsia": [255, 0, 255],
  822. "gainsboro": [220, 220, 220],
  823. "ghostwhite": [248, 248, 255],
  824. "gold": [255, 215, 0],
  825. "goldenrod": [218, 165, 32],
  826. "gray": [128, 128, 128],
  827. "green": [0, 128, 0],
  828. "greenyellow": [173, 255, 47],
  829. "grey": [128, 128, 128],
  830. "honeydew": [240, 255, 240],
  831. "hotpink": [255, 105, 180],
  832. "indianred": [205, 92, 92],
  833. "indigo": [75, 0, 130],
  834. "ivory": [255, 255, 240],
  835. "khaki": [240, 230, 140],
  836. "lavender": [230, 230, 250],
  837. "lavenderblush": [255, 240, 245],
  838. "lawngreen": [124, 252, 0],
  839. "lemonchiffon": [255, 250, 205],
  840. "lightblue": [173, 216, 230],
  841. "lightcoral": [240, 128, 128],
  842. "lightcyan": [224, 255, 255],
  843. "lightgoldenrodyellow": [250, 250, 210],
  844. "lightgray": [211, 211, 211],
  845. "lightgreen": [144, 238, 144],
  846. "lightgrey": [211, 211, 211],
  847. "lightpink": [255, 182, 193],
  848. "lightsalmon": [255, 160, 122],
  849. "lightseagreen": [32, 178, 170],
  850. "lightskyblue": [135, 206, 250],
  851. "lightslategray": [119, 136, 153],
  852. "lightslategrey": [119, 136, 153],
  853. "lightsteelblue": [176, 196, 222],
  854. "lightyellow": [255, 255, 224],
  855. "lime": [0, 255, 0],
  856. "limegreen": [50, 205, 50],
  857. "linen": [250, 240, 230],
  858. "magenta": [255, 0, 255],
  859. "maroon": [128, 0, 0],
  860. "mediumaquamarine": [102, 205, 170],
  861. "mediumblue": [0, 0, 205],
  862. "mediumorchid": [186, 85, 211],
  863. "mediumpurple": [147, 112, 219],
  864. "mediumseagreen": [60, 179, 113],
  865. "mediumslateblue": [123, 104, 238],
  866. "mediumspringgreen": [0, 250, 154],
  867. "mediumturquoise": [72, 209, 204],
  868. "mediumvioletred": [199, 21, 133],
  869. "midnightblue": [25, 25, 112],
  870. "mintcream": [245, 255, 250],
  871. "mistyrose": [255, 228, 225],
  872. "moccasin": [255, 228, 181],
  873. "navajowhite": [255, 222, 173],
  874. "navy": [0, 0, 128],
  875. "oldlace": [253, 245, 230],
  876. "olive": [128, 128, 0],
  877. "olivedrab": [107, 142, 35],
  878. "orange": [255, 165, 0],
  879. "orangered": [255, 69, 0],
  880. "orchid": [218, 112, 214],
  881. "palegoldenrod": [238, 232, 170],
  882. "palegreen": [152, 251, 152],
  883. "paleturquoise": [175, 238, 238],
  884. "palevioletred": [219, 112, 147],
  885. "papayawhip": [255, 239, 213],
  886. "peachpuff": [255, 218, 185],
  887. "peru": [205, 133, 63],
  888. "pink": [255, 192, 203],
  889. "plum": [221, 160, 221],
  890. "powderblue": [176, 224, 230],
  891. "purple": [128, 0, 128],
  892. "rebeccapurple": [102, 51, 153],
  893. "red": [255, 0, 0],
  894. "rosybrown": [188, 143, 143],
  895. "royalblue": [65, 105, 225],
  896. "saddlebrown": [139, 69, 19],
  897. "salmon": [250, 128, 114],
  898. "sandybrown": [244, 164, 96],
  899. "seagreen": [46, 139, 87],
  900. "seashell": [255, 245, 238],
  901. "sienna": [160, 82, 45],
  902. "silver": [192, 192, 192],
  903. "skyblue": [135, 206, 235],
  904. "slateblue": [106, 90, 205],
  905. "slategray": [112, 128, 144],
  906. "slategrey": [112, 128, 144],
  907. "snow": [255, 250, 250],
  908. "springgreen": [0, 255, 127],
  909. "steelblue": [70, 130, 180],
  910. "tan": [210, 180, 140],
  911. "teal": [0, 128, 128],
  912. "thistle": [216, 191, 216],
  913. "tomato": [255, 99, 71],
  914. "turquoise": [64, 224, 208],
  915. "violet": [238, 130, 238],
  916. "wheat": [245, 222, 179],
  917. "white": [255, 255, 255],
  918. "whitesmoke": [245, 245, 245],
  919. "yellow": [255, 255, 0],
  920. "yellowgreen": [154, 205, 50]
  921. };
  922.  
  923. module.exports = Color;
  924.  
  925. },{}],4:[function(require,module,exports){
  926. var Support = require('./support');
  927. var CanvasRenderer = require('./renderers/canvas');
  928. var ImageLoader = require('./imageloader');
  929. var NodeParser = require('./nodeparser');
  930. var NodeContainer = require('./nodecontainer');
  931. var log = require('./log');
  932. var utils = require('./utils');
  933. var createWindowClone = require('./clone');
  934. var loadUrlDocument = require('./proxy').loadUrlDocument;
  935. var getBounds = utils.getBounds;
  936.  
  937. var html2canvasNodeAttribute = "data-html2canvas-node";
  938. var html2canvasCloneIndex = 0;
  939.  
  940. function html2canvas(nodeList, options) {
  941. var index = html2canvasCloneIndex++;
  942. options = options || {};
  943. if (options.logging) {
  944. window.html2canvas.logging = true;
  945. window.html2canvas.start = Date.now();
  946. }
  947.  
  948. options.async = typeof(options.async) === "undefined" ? true : options.async;
  949. options.allowTaint = typeof(options.allowTaint) === "undefined" ? false : options.allowTaint;
  950. options.removeContainer = typeof(options.removeContainer) === "undefined" ? true : options.removeContainer;
  951. options.javascriptEnabled = typeof(options.javascriptEnabled) === "undefined" ? false : options.javascriptEnabled;
  952. options.imageTimeout = typeof(options.imageTimeout) === "undefined" ? 10000 : options.imageTimeout;
  953. options.renderer = typeof(options.renderer) === "function" ? options.renderer : CanvasRenderer;
  954. options.strict = !!options.strict;
  955.  
  956. if (typeof(nodeList) === "string") {
  957. if (typeof(options.proxy) !== "string") {
  958. return Promise.reject("Proxy must be used when rendering url");
  959. }
  960. var width = options.width != null ? options.width : window.innerWidth;
  961. var height = options.height != null ? options.height : window.innerHeight;
  962. return loadUrlDocument(absoluteUrl(nodeList), options.proxy, document, width, height, options).then(function(container) {
  963. return renderWindow(container.contentWindow.document.documentElement, container, options, width, height);
  964. });
  965. }
  966.  
  967. var node = ((nodeList === undefined) ? [document.documentElement] : ((nodeList.length) ? nodeList : [nodeList]))[0];
  968. node.setAttribute(html2canvasNodeAttribute + index, index);
  969. return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {
  970. if (typeof(options.onrendered) === "function") {
  971. log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas");
  972. options.onrendered(canvas);
  973. }
  974. return canvas;
  975. });
  976. }
  977.  
  978. html2canvas.CanvasRenderer = CanvasRenderer;
  979. html2canvas.NodeContainer = NodeContainer;
  980. html2canvas.log = log;
  981. html2canvas.utils = utils;
  982.  
  983. var html2canvasExport = (typeof(document) === "undefined" || typeof(Object.create) !== "function" || typeof(document.createElement("canvas").getContext) !== "function") ? function() {
  984. return Promise.reject("No canvas support");
  985. } : html2canvas;
  986.  
  987. module.exports = html2canvasExport;
  988.  
  989. if (typeof(define) === 'function' && define.amd) {
  990. define('html2canvas', [], function() {
  991. return html2canvasExport;
  992. });
  993. }
  994.  
  995. function renderDocument(document, options, windowWidth, windowHeight, html2canvasIndex) {
  996. return createWindowClone(document, document, windowWidth, windowHeight, options, document.defaultView.pageXOffset, document.defaultView.pageYOffset).then(function(container) {
  997. log("Document cloned");
  998. var attributeName = html2canvasNodeAttribute + html2canvasIndex;
  999. var selector = "[" + attributeName + "='" + html2canvasIndex + "']";
  1000. document.querySelector(selector).removeAttribute(attributeName);
  1001. var clonedWindow = container.contentWindow;
  1002. var node = clonedWindow.document.querySelector(selector);
  1003. var oncloneHandler = (typeof(options.onclone) === "function") ? Promise.resolve(options.onclone(clonedWindow.document)) : Promise.resolve(true);
  1004. return oncloneHandler.then(function() {
  1005. return renderWindow(node, container, options, windowWidth, windowHeight);
  1006. });
  1007. });
  1008. }
  1009.  
  1010. function renderWindow(node, container, options, windowWidth, windowHeight) {
  1011. var clonedWindow = container.contentWindow;
  1012. var support = new Support(clonedWindow.document);
  1013. var imageLoader = new ImageLoader(options, support);
  1014. var bounds = getBounds(node);
  1015. var width = options.type === "view" ? windowWidth : documentWidth(clonedWindow.document);
  1016. var height = options.type === "view" ? windowHeight : documentHeight(clonedWindow.document);
  1017. var renderer = new options.renderer(width, height, imageLoader, options, document);
  1018. var parser = new NodeParser(node, renderer, support, imageLoader, options);
  1019. return parser.ready.then(function() {
  1020. log("Finished rendering");
  1021. var canvas;
  1022.  
  1023. if (options.type === "view") {
  1024. canvas = crop(renderer.canvas, {width: renderer.canvas.width, height: renderer.canvas.height, top: 0, left: 0, x: 0, y: 0});
  1025. } else if (node === clonedWindow.document.body || node === clonedWindow.document.documentElement || options.canvas != null) {
  1026. canvas = renderer.canvas;
  1027. } else {
  1028. 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});
  1029. }
  1030.  
  1031. cleanupContainer(container, options);
  1032. return canvas;
  1033. });
  1034. }
  1035.  
  1036. function cleanupContainer(container, options) {
  1037. if (options.removeContainer) {
  1038. container.parentNode.removeChild(container);
  1039. log("Cleaned up container");
  1040. }
  1041. }
  1042.  
  1043. function crop(canvas, bounds) {
  1044. var croppedCanvas = document.createElement("canvas");
  1045. var x1 = Math.min(canvas.width - 1, Math.max(0, bounds.left)) - 1;
  1046. var x2 = Math.min(canvas.width, Math.max(1, bounds.left + bounds.width)) + 1;
  1047. var y1 = Math.min(canvas.height - 1, Math.max(0, bounds.top)) - 1;
  1048. var y2 = Math.min(canvas.height, Math.max(1, bounds.top + bounds.height)) + 1;
  1049.  
  1050. var w = x2 - x1;
  1051. var h = y2 - y1;
  1052.  
  1053. croppedCanvas.width = w;
  1054. croppedCanvas.height = h;
  1055. log("Cropping canvas at:", "left:", bounds.left, "top:", bounds.top, "width:", w, "height:", h);
  1056. log("Resulting crop with width", bounds.width, "and height", bounds.height, " with x", x1, "and y", y1);
  1057. var cropCtx = croppedCanvas.getContext("2d");
  1058. cropCtx.drawImage(canvas, x1, y1, w, h, 0, 0, w, h);
  1059. // Text got painted to screen offset?
  1060. // cropCtx.drawImage(canvas, x1 - window.scrollX, y1 - window.scrollY, w, h, 0, 0, w, h);
  1061. return croppedCanvas;
  1062. }
  1063.  
  1064. function documentWidth (doc) {
  1065. return Math.max(
  1066. Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth),
  1067. Math.max(doc.body.offsetWidth, doc.documentElement.offsetWidth),
  1068. Math.max(doc.body.clientWidth, doc.documentElement.clientWidth)
  1069. );
  1070. }
  1071.  
  1072. function documentHeight (doc) {
  1073. return Math.max(
  1074. Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight),
  1075. Math.max(doc.body.offsetHeight, doc.documentElement.offsetHeight),
  1076. Math.max(doc.body.clientHeight, doc.documentElement.clientHeight)
  1077. );
  1078. }
  1079.  
  1080. function absoluteUrl(url) {
  1081. var link = document.createElement("a");
  1082. link.href = url;
  1083. link.href = link.href;
  1084. return link;
  1085. }
  1086.  
  1087. },{"./clone":2,"./imageloader":11,"./log":13,"./nodecontainer":14,"./nodeparser":15,"./proxy":16,"./renderers/canvas":20,"./support":22,"./utils":26}],5:[function(require,module,exports){
  1088. var log = require('./log');
  1089. var smallImage = require('./utils').smallImage;
  1090.  
  1091. function DummyImageContainer(src) {
  1092. this.src = src;
  1093. log("DummyImageContainer for", src);
  1094. if (!this.promise || !this.image) {
  1095. log("Initiating DummyImageContainer");
  1096. DummyImageContainer.prototype.image = new Image();
  1097. var image = this.image;
  1098. DummyImageContainer.prototype.promise = new Promise(function(resolve, reject) {
  1099. image.onload = resolve;
  1100. image.onerror = reject;
  1101. image.src = smallImage();
  1102. if (image.complete === true) {
  1103. resolve(image);
  1104. }
  1105. });
  1106. }
  1107. }
  1108.  
  1109. module.exports = DummyImageContainer;
  1110.  
  1111. },{"./log":13,"./utils":26}],6:[function(require,module,exports){
  1112. var smallImage = require('./utils').smallImage;
  1113.  
  1114. function Font(family, size) {
  1115. var container = document.createElement('div'),
  1116. img = document.createElement('img'),
  1117. span = document.createElement('span'),
  1118. sampleText = 'Hidden Text',
  1119. baseline,
  1120. middle;
  1121.  
  1122. container.style.visibility = "hidden";
  1123. container.style.fontFamily = family;
  1124. container.style.fontSize = size;
  1125. container.style.margin = 0;
  1126. container.style.padding = 0;
  1127.  
  1128. document.body.appendChild(container);
  1129.  
  1130. img.src = smallImage();
  1131. img.width = 1;
  1132. img.height = 1;
  1133.  
  1134. img.style.margin = 0;
  1135. img.style.padding = 0;
  1136. img.style.verticalAlign = "baseline";
  1137.  
  1138. span.style.fontFamily = family;
  1139. span.style.fontSize = size;
  1140. span.style.margin = 0;
  1141. span.style.padding = 0;
  1142.  
  1143. span.appendChild(document.createTextNode(sampleText));
  1144. container.appendChild(span);
  1145. container.appendChild(img);
  1146. baseline = (img.offsetTop - span.offsetTop) + 1;
  1147.  
  1148. container.removeChild(span);
  1149. container.appendChild(document.createTextNode(sampleText));
  1150.  
  1151. container.style.lineHeight = "normal";
  1152. img.style.verticalAlign = "super";
  1153.  
  1154. middle = (img.offsetTop-container.offsetTop) + 1;
  1155.  
  1156. document.body.removeChild(container);
  1157.  
  1158. this.baseline = baseline;
  1159. this.lineWidth = 1;
  1160. this.middle = middle;
  1161. }
  1162.  
  1163. module.exports = Font;
  1164.  
  1165. },{"./utils":26}],7:[function(require,module,exports){
  1166. var Font = require('./font');
  1167.  
  1168. function FontMetrics() {
  1169. this.data = {};
  1170. }
  1171.  
  1172. FontMetrics.prototype.getMetrics = function(family, size) {
  1173. if (this.data[family + "-" + size] === undefined) {
  1174. this.data[family + "-" + size] = new Font(family, size);
  1175. }
  1176. return this.data[family + "-" + size];
  1177. };
  1178.  
  1179. module.exports = FontMetrics;
  1180.  
  1181. },{"./font":6}],8:[function(require,module,exports){
  1182. var utils = require('./utils');
  1183. var getBounds = utils.getBounds;
  1184. var loadUrlDocument = require('./proxy').loadUrlDocument;
  1185.  
  1186. function FrameContainer(container, sameOrigin, options) {
  1187. this.image = null;
  1188. this.src = container;
  1189. var self = this;
  1190. var bounds = getBounds(container);
  1191. this.promise = (!sameOrigin ? this.proxyLoad(options.proxy, bounds, options) : new Promise(function(resolve) {
  1192. if (container.contentWindow.document.URL === "about:blank" || container.contentWindow.document.documentElement == null) {
  1193. container.contentWindow.onload = container.onload = function() {
  1194. resolve(container);
  1195. };
  1196. } else {
  1197. resolve(container);
  1198. }
  1199. })).then(function(container) {
  1200. var html2canvas = require('./core');
  1201. 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});
  1202. }).then(function(canvas) {
  1203. return self.image = canvas;
  1204. });
  1205. }
  1206.  
  1207. FrameContainer.prototype.proxyLoad = function(proxy, bounds, options) {
  1208. var container = this.src;
  1209. return loadUrlDocument(container.src, proxy, container.ownerDocument, bounds.width, bounds.height, options);
  1210. };
  1211.  
  1212. module.exports = FrameContainer;
  1213.  
  1214. },{"./core":4,"./proxy":16,"./utils":26}],9:[function(require,module,exports){
  1215. function GradientContainer(imageData) {
  1216. this.src = imageData.value;
  1217. this.colorStops = [];
  1218. this.type = null;
  1219. this.x0 = 0.5;
  1220. this.y0 = 0.5;
  1221. this.x1 = 0.5;
  1222. this.y1 = 0.5;
  1223. this.promise = Promise.resolve(true);
  1224. }
  1225.  
  1226. GradientContainer.TYPES = {
  1227. LINEAR: 1,
  1228. RADIAL: 2
  1229. };
  1230.  
  1231. // TODO: support hsl[a], negative %/length values
  1232. // TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
  1233. GradientContainer.REGEXP_COLORSTOP = /^\s*(rgba?\(\s*\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*[0-9\.]+)?\s*\)|[a-z]{3,20}|#[a-f0-9]{3,6})(?:\s+(\d{1,3}(?:\.\d+)?)(%|px)?)?(?:\s|$)/i;
  1234.  
  1235. module.exports = GradientContainer;
  1236.  
  1237. },{}],10:[function(require,module,exports){
  1238. function ImageContainer(src, cors) {
  1239. this.src = src;
  1240. this.image = new Image();
  1241. var self = this;
  1242. this.tainted = null;
  1243. this.promise = new Promise(function(resolve, reject) {
  1244. self.image.onload = resolve;
  1245. self.image.onerror = reject;
  1246. if (cors) {
  1247. self.image.crossOrigin = "anonymous";
  1248. }
  1249. self.image.src = src;
  1250. if (self.image.complete === true) {
  1251. resolve(self.image);
  1252. }
  1253. });
  1254. }
  1255.  
  1256. module.exports = ImageContainer;
  1257.  
  1258. },{}],11:[function(require,module,exports){
  1259. var log = require('./log');
  1260. var ImageContainer = require('./imagecontainer');
  1261. var DummyImageContainer = require('./dummyimagecontainer');
  1262. var ProxyImageContainer = require('./proxyimagecontainer');
  1263. var FrameContainer = require('./framecontainer');
  1264. var SVGContainer = require('./svgcontainer');
  1265. var SVGNodeContainer = require('./svgnodecontainer');
  1266. var LinearGradientContainer = require('./lineargradientcontainer');
  1267. var WebkitGradientContainer = require('./webkitgradientcontainer');
  1268. var bind = require('./utils').bind;
  1269.  
  1270. function ImageLoader(options, support) {
  1271. this.link = null;
  1272. this.options = options;
  1273. this.support = support;
  1274. this.origin = this.getOrigin(window.location.href);
  1275. }
  1276.  
  1277. ImageLoader.prototype.findImages = function(nodes) {
  1278. var images = [];
  1279. nodes.reduce(function(imageNodes, container) {
  1280. switch(container.node.nodeName) {
  1281. case "IMG":
  1282. return imageNodes.concat([{
  1283. args: [container.node.src],
  1284. method: "url"
  1285. }]);
  1286. case "svg":
  1287. case "IFRAME":
  1288. return imageNodes.concat([{
  1289. args: [container.node],
  1290. method: container.node.nodeName
  1291. }]);
  1292. }
  1293. return imageNodes;
  1294. }, []).forEach(this.addImage(images, this.loadImage), this);
  1295. return images;
  1296. };
  1297.  
  1298. ImageLoader.prototype.findBackgroundImage = function(images, container) {
  1299. container.parseBackgroundImages().filter(this.hasImageBackground).forEach(this.addImage(images, this.loadImage), this);
  1300. return images;
  1301. };
  1302.  
  1303. ImageLoader.prototype.addImage = function(images, callback) {
  1304. return function(newImage) {
  1305. newImage.args.forEach(function(image) {
  1306. if (!this.imageExists(images, image)) {
  1307. images.splice(0, 0, callback.call(this, newImage));
  1308. log('Added image #' + (images.length), typeof(image) === "string" ? image.substring(0, 100) : image);
  1309. }
  1310. }, this);
  1311. };
  1312. };
  1313.  
  1314. ImageLoader.prototype.hasImageBackground = function(imageData) {
  1315. return imageData.method !== "none";
  1316. };
  1317.  
  1318. ImageLoader.prototype.loadImage = function(imageData) {
  1319. if (imageData.method === "url") {
  1320. var src = imageData.args[0];
  1321. if (this.isSVG(src) && !this.support.svg && !this.options.allowTaint) {
  1322. return new SVGContainer(src);
  1323. } else if (src.match(/data:image\/.*;base64,/i)) {
  1324. return new ImageContainer(src.replace(/url\(['"]{0,}|['"]{0,}\)$/ig, ''), false);
  1325. } else if (this.isSameOrigin(src) || this.options.allowTaint === true || this.isSVG(src)) {
  1326. return new ImageContainer(src, false);
  1327. } else if (this.support.cors && !this.options.allowTaint && this.options.useCORS) {
  1328. return new ImageContainer(src, true);
  1329. } else if (this.options.proxy) {
  1330. return new ProxyImageContainer(src, this.options.proxy);
  1331. } else {
  1332. return new DummyImageContainer(src);
  1333. }
  1334. } else if (imageData.method === "linear-gradient") {
  1335. return new LinearGradientContainer(imageData);
  1336. } else if (imageData.method === "gradient") {
  1337. return new WebkitGradientContainer(imageData);
  1338. } else if (imageData.method === "svg") {
  1339. return new SVGNodeContainer(imageData.args[0], this.support.svg);
  1340. } else if (imageData.method === "IFRAME") {
  1341. return new FrameContainer(imageData.args[0], this.isSameOrigin(imageData.args[0].src), this.options);
  1342. } else {
  1343. return new DummyImageContainer(imageData);
  1344. }
  1345. };
  1346.  
  1347. ImageLoader.prototype.isSVG = function(src) {
  1348. return src.substring(src.length - 3).toLowerCase() === "svg" || SVGContainer.prototype.isInline(src);
  1349. };
  1350.  
  1351. ImageLoader.prototype.imageExists = function(images, src) {
  1352. return images.some(function(image) {
  1353. return image.src === src;
  1354. });
  1355. };
  1356.  
  1357. ImageLoader.prototype.isSameOrigin = function(url) {
  1358. return (this.getOrigin(url) === this.origin);
  1359. };
  1360.  
  1361. ImageLoader.prototype.getOrigin = function(url) {
  1362. var link = this.link || (this.link = document.createElement("a"));
  1363. link.href = url;
  1364. link.href = link.href; // IE9, LOL! - http://jsfiddle.net/niklasvh/2e48b/
  1365. return link.protocol + link.hostname + link.port;
  1366. };
  1367.  
  1368. ImageLoader.prototype.getPromise = function(container) {
  1369. return this.timeout(container, this.options.imageTimeout)['catch'](function() {
  1370. var dummy = new DummyImageContainer(container.src);
  1371. return dummy.promise.then(function(image) {
  1372. container.image = image;
  1373. });
  1374. });
  1375. };
  1376.  
  1377. ImageLoader.prototype.get = function(src) {
  1378. var found = null;
  1379. return this.images.some(function(img) {
  1380. return (found = img).src === src;
  1381. }) ? found : null;
  1382. };
  1383.  
  1384. ImageLoader.prototype.fetch = function(nodes) {
  1385. this.images = nodes.reduce(bind(this.findBackgroundImage, this), this.findImages(nodes));
  1386. this.images.forEach(function(image, index) {
  1387. image.promise.then(function() {
  1388. log("Succesfully loaded image #"+ (index+1), image);
  1389. }, function(e) {
  1390. log("Failed loading image #"+ (index+1), image, e);
  1391. });
  1392. });
  1393. this.ready = Promise.all(this.images.map(this.getPromise, this));
  1394. log("Finished searching images");
  1395. return this;
  1396. };
  1397.  
  1398. ImageLoader.prototype.timeout = function(container, timeout) {
  1399. var timer;
  1400. var promise = Promise.race([container.promise, new Promise(function(res, reject) {
  1401. timer = setTimeout(function() {
  1402. log("Timed out loading image", container);
  1403. reject(container);
  1404. }, timeout);
  1405. })]).then(function(container) {
  1406. clearTimeout(timer);
  1407. return container;
  1408. });
  1409. promise['catch'](function() {
  1410. clearTimeout(timer);
  1411. });
  1412. return promise;
  1413. };
  1414.  
  1415. module.exports = ImageLoader;
  1416.  
  1417. },{"./dummyimagecontainer":5,"./framecontainer":8,"./imagecontainer":10,"./lineargradientcontainer":12,"./log":13,"./proxyimagecontainer":17,"./svgcontainer":23,"./svgnodecontainer":24,"./utils":26,"./webkitgradientcontainer":27}],12:[function(require,module,exports){
  1418. var GradientContainer = require('./gradientcontainer');
  1419. var Color = require('./color');
  1420.  
  1421. function LinearGradientContainer(imageData) {
  1422. GradientContainer.apply(this, arguments);
  1423. this.type = GradientContainer.TYPES.LINEAR;
  1424.  
  1425. var hasDirection = LinearGradientContainer.REGEXP_DIRECTION.test( imageData.args[0] ) ||
  1426. !GradientContainer.REGEXP_COLORSTOP.test( imageData.args[0] );
  1427.  
  1428. if (hasDirection) {
  1429. imageData.args[0].split(/\s+/).reverse().forEach(function(position, index) {
  1430. switch(position) {
  1431. case "left":
  1432. this.x0 = 0;
  1433. this.x1 = 1;
  1434. break;
  1435. case "top":
  1436. this.y0 = 0;
  1437. this.y1 = 1;
  1438. break;
  1439. case "right":
  1440. this.x0 = 1;
  1441. this.x1 = 0;
  1442. break;
  1443. case "bottom":
  1444. this.y0 = 1;
  1445. this.y1 = 0;
  1446. break;
  1447. case "to":
  1448. var y0 = this.y0;
  1449. var x0 = this.x0;
  1450. this.y0 = this.y1;
  1451. this.x0 = this.x1;
  1452. this.x1 = x0;
  1453. this.y1 = y0;
  1454. break;
  1455. case "center":
  1456. break; // centered by default
  1457. // Firefox internally converts position keywords to percentages:
  1458. // http://www.w3.org/TR/2010/WD-CSS2-20101207/colors.html#propdef-background-position
  1459. default: // percentage or absolute length
  1460. // TODO: support absolute start point positions (e.g., use bounds to convert px to a ratio)
  1461. var ratio = parseFloat(position, 10) * 1e-2;
  1462. if (isNaN(ratio)) { // invalid or unhandled value
  1463. break;
  1464. }
  1465. if (index === 0) {
  1466. this.y0 = ratio;
  1467. this.y1 = 1 - this.y0;
  1468. } else {
  1469. this.x0 = ratio;
  1470. this.x1 = 1 - this.x0;
  1471. }
  1472. break;
  1473. }
  1474. }, this);
  1475. } else {
  1476. this.y0 = 0;
  1477. this.y1 = 1;
  1478. }
  1479.  
  1480. this.colorStops = imageData.args.slice(hasDirection ? 1 : 0).map(function(colorStop) {
  1481. var colorStopMatch = colorStop.match(GradientContainer.REGEXP_COLORSTOP);
  1482. var value = +colorStopMatch[2];
  1483. var unit = value === 0 ? "%" : colorStopMatch[3]; // treat "0" as "0%"
  1484. return {
  1485. color: new Color(colorStopMatch[1]),
  1486. // TODO: support absolute stop positions (e.g., compute gradient line length & convert px to ratio)
  1487. stop: unit === "%" ? value / 100 : null
  1488. };
  1489. });
  1490.  
  1491. if (this.colorStops[0].stop === null) {
  1492. this.colorStops[0].stop = 0;
  1493. }
  1494.  
  1495. if (this.colorStops[this.colorStops.length - 1].stop === null) {
  1496. this.colorStops[this.colorStops.length - 1].stop = 1;
  1497. }
  1498.  
  1499. // calculates and fills-in explicit stop positions when omitted from rule
  1500. this.colorStops.forEach(function(colorStop, index) {
  1501. if (colorStop.stop === null) {
  1502. this.colorStops.slice(index).some(function(find, count) {
  1503. if (find.stop !== null) {
  1504. colorStop.stop = ((find.stop - this.colorStops[index - 1].stop) / (count + 1)) + this.colorStops[index - 1].stop;
  1505. return true;
  1506. } else {
  1507. return false;
  1508. }
  1509. }, this);
  1510. }
  1511. }, this);
  1512. }
  1513.  
  1514. LinearGradientContainer.prototype = Object.create(GradientContainer.prototype);
  1515.  
  1516. // TODO: support <angle> (e.g. -?\d{1,3}(?:\.\d+)deg, etc. : https://developer.mozilla.org/docs/Web/CSS/angle )
  1517. LinearGradientContainer.REGEXP_DIRECTION = /^\s*(?:to|left|right|top|bottom|center|\d{1,3}(?:\.\d+)?%?)(?:\s|$)/i;
  1518.  
  1519. module.exports = LinearGradientContainer;
  1520.  
  1521. },{"./color":3,"./gradientcontainer":9}],13:[function(require,module,exports){
  1522. module.exports = function() {
  1523. if (window.html2canvas.logging && window.console && window.console.log) {
  1524. Function.prototype.bind.call(window.console.log, (window.console)).apply(window.console, [(Date.now() - window.html2canvas.start) + "ms", "html2canvas:"].concat([].slice.call(arguments, 0)));
  1525. }
  1526. };
  1527.  
  1528. },{}],14:[function(require,module,exports){
  1529. var Color = require('./color');
  1530. var utils = require('./utils');
  1531. var getBounds = utils.getBounds;
  1532. var parseBackgrounds = utils.parseBackgrounds;
  1533. var offsetBounds = utils.offsetBounds;
  1534.  
  1535. function NodeContainer(node, parent) {
  1536. this.node = node;
  1537. this.parent = parent;
  1538. this.stack = null;
  1539. this.bounds = null;
  1540. this.borders = null;
  1541. this.clip = [];
  1542. this.backgroundClip = [];
  1543. this.offsetBounds = null;
  1544. this.visible = null;
  1545. this.computedStyles = null;
  1546. this.colors = {};
  1547. this.styles = {};
  1548. this.backgroundImages = null;
  1549. this.transformData = null;
  1550. this.transformMatrix = null;
  1551. this.isPseudoElement = false;
  1552. this.opacity = null;
  1553. }
  1554.  
  1555. NodeContainer.prototype.cloneTo = function(stack) {
  1556. stack.visible = this.visible;
  1557. stack.borders = this.borders;
  1558. stack.bounds = this.bounds;
  1559. stack.clip = this.clip;
  1560. stack.backgroundClip = this.backgroundClip;
  1561. stack.computedStyles = this.computedStyles;
  1562. stack.styles = this.styles;
  1563. stack.backgroundImages = this.backgroundImages;
  1564. stack.opacity = this.opacity;
  1565. };
  1566.  
  1567. NodeContainer.prototype.getOpacity = function() {
  1568. return this.opacity === null ? (this.opacity = this.cssFloat('opacity')) : this.opacity;
  1569. };
  1570.  
  1571. NodeContainer.prototype.assignStack = function(stack) {
  1572. this.stack = stack;
  1573. stack.children.push(this);
  1574. };
  1575.  
  1576. NodeContainer.prototype.isElementVisible = function() {
  1577. return this.node.nodeType === Node.TEXT_NODE ? this.parent.visible : (
  1578. this.css('display') !== "none" &&
  1579. this.css('visibility') !== "hidden" &&
  1580. !this.node.hasAttribute("data-html2canvas-ignore") &&
  1581. (this.node.nodeName !== "INPUT" || this.node.getAttribute("type") !== "hidden")
  1582. );
  1583. };
  1584.  
  1585. NodeContainer.prototype.css = function(attribute) {
  1586. if (!this.computedStyles) {
  1587. this.computedStyles = this.isPseudoElement ? this.parent.computedStyle(this.before ? ":before" : ":after") : this.computedStyle(null);
  1588. }
  1589.  
  1590. return this.styles[attribute] || (this.styles[attribute] = this.computedStyles[attribute]);
  1591. };
  1592.  
  1593. NodeContainer.prototype.prefixedCss = function(attribute) {
  1594. var prefixes = ["webkit", "moz", "ms", "o"];
  1595. var value = this.css(attribute);
  1596. if (value === undefined) {
  1597. prefixes.some(function(prefix) {
  1598. value = this.css(prefix + attribute.substr(0, 1).toUpperCase() + attribute.substr(1));
  1599. return value !== undefined;
  1600. }, this);
  1601. }
  1602. return value === undefined ? null : value;
  1603. };
  1604.  
  1605. NodeContainer.prototype.computedStyle = function(type) {
  1606. return this.node.ownerDocument.defaultView.getComputedStyle(this.node, type);
  1607. };
  1608.  
  1609. NodeContainer.prototype.cssInt = function(attribute) {
  1610. var value = parseInt(this.css(attribute), 10);
  1611. return (isNaN(value)) ? 0 : value; // borders in old IE are throwing 'medium' for demo.html
  1612. };
  1613.  
  1614. NodeContainer.prototype.color = function(attribute) {
  1615. return this.colors[attribute] || (this.colors[attribute] = new Color(this.css(attribute)));
  1616. };
  1617.  
  1618. NodeContainer.prototype.cssFloat = function(attribute) {
  1619. var value = parseFloat(this.css(attribute));
  1620. return (isNaN(value)) ? 0 : value;
  1621. };
  1622.  
  1623. NodeContainer.prototype.fontWeight = function() {
  1624. var weight = this.css("fontWeight");
  1625. switch(parseInt(weight, 10)){
  1626. case 401:
  1627. weight = "bold";
  1628. break;
  1629. case 400:
  1630. weight = "normal";
  1631. break;
  1632. }
  1633. return weight;
  1634. };
  1635.  
  1636. NodeContainer.prototype.parseClip = function() {
  1637. var matches = this.css('clip').match(this.CLIP);
  1638. if (matches) {
  1639. return {
  1640. top: parseInt(matches[1], 10),
  1641. right: parseInt(matches[2], 10),
  1642. bottom: parseInt(matches[3], 10),
  1643. left: parseInt(matches[4], 10)
  1644. };
  1645. }
  1646. return null;
  1647. };
  1648.  
  1649. NodeContainer.prototype.parseBackgroundImages = function() {
  1650. return this.backgroundImages || (this.backgroundImages = parseBackgrounds(this.css("backgroundImage")));
  1651. };
  1652.  
  1653. NodeContainer.prototype.cssList = function(property, index) {
  1654. var value = (this.css(property) || '').split(',');
  1655. value = value[index || 0] || value[0] || 'auto';
  1656. value = value.trim().split(' ');
  1657. if (value.length === 1) {
  1658. value = [value[0], isPercentage(value[0]) ? 'auto' : value[0]];
  1659. }
  1660. return value;
  1661. };
  1662.  
  1663. NodeContainer.prototype.parseBackgroundSize = function(bounds, image, index) {
  1664. var size = this.cssList("backgroundSize", index);
  1665. var width, height;
  1666.  
  1667. if (isPercentage(size[0])) {
  1668. width = bounds.width * parseFloat(size[0]) / 100;
  1669. } else if (/contain|cover/.test(size[0])) {
  1670. var targetRatio = bounds.width / bounds.height, currentRatio = image.width / image.height;
  1671. return (targetRatio < currentRatio ^ size[0] === 'contain') ? {width: bounds.height * currentRatio, height: bounds.height} : {width: bounds.width, height: bounds.width / currentRatio};
  1672. } else {
  1673. width = parseInt(size[0], 10);
  1674. }
  1675.  
  1676. if (size[0] === 'auto' && size[1] === 'auto') {
  1677. height = image.height;
  1678. } else if (size[1] === 'auto') {
  1679. height = width / image.width * image.height;
  1680. } else if (isPercentage(size[1])) {
  1681. height = bounds.height * parseFloat(size[1]) / 100;
  1682. } else {
  1683. height = parseInt(size[1], 10);
  1684. }
  1685.  
  1686. if (size[0] === 'auto') {
  1687. width = height / image.height * image.width;
  1688. }
  1689.  
  1690. return {width: width, height: height};
  1691. };
  1692.  
  1693. NodeContainer.prototype.parseBackgroundPosition = function(bounds, image, index, backgroundSize) {
  1694. var position = this.cssList('backgroundPosition', index);
  1695. var left, top;
  1696.  
  1697. if (isPercentage(position[0])){
  1698. left = (bounds.width - (backgroundSize || image).width) * (parseFloat(position[0]) / 100);
  1699. } else {
  1700. left = parseInt(position[0], 10);
  1701. }
  1702.  
  1703. if (position[1] === 'auto') {
  1704. top = left / image.width * image.height;
  1705. } else if (isPercentage(position[1])){
  1706. top = (bounds.height - (backgroundSize || image).height) * parseFloat(position[1]) / 100;
  1707. } else {
  1708. top = parseInt(position[1], 10);
  1709. }
  1710.  
  1711. if (position[0] === 'auto') {
  1712. left = top / image.height * image.width;
  1713. }
  1714.  
  1715. return {left: left, top: top};
  1716. };
  1717.  
  1718. NodeContainer.prototype.parseBackgroundRepeat = function(index) {
  1719. return this.cssList("backgroundRepeat", index)[0];
  1720. };
  1721.  
  1722. NodeContainer.prototype.parseTextShadows = function() {
  1723. var textShadow = this.css("textShadow");
  1724. var results = [];
  1725.  
  1726. if (textShadow && textShadow !== 'none') {
  1727. var shadows = textShadow.match(this.TEXT_SHADOW_PROPERTY);
  1728. for (var i = 0; shadows && (i < shadows.length); i++) {
  1729. var s = shadows[i].match(this.TEXT_SHADOW_VALUES);
  1730. results.push({
  1731. color: new Color(s[0]),
  1732. offsetX: s[1] ? parseFloat(s[1].replace('px', '')) : 0,
  1733. offsetY: s[2] ? parseFloat(s[2].replace('px', '')) : 0,
  1734. blur: s[3] ? s[3].replace('px', '') : 0
  1735. });
  1736. }
  1737. }
  1738. return results;
  1739. };
  1740.  
  1741. NodeContainer.prototype.parseTransform = function() {
  1742. if (!this.transformData) {
  1743. if (this.hasTransform()) {
  1744. var offset = this.parseBounds();
  1745. var origin = this.prefixedCss("transformOrigin").split(" ").map(removePx).map(asFloat);
  1746. origin[0] += offset.left;
  1747. origin[1] += offset.top;
  1748. this.transformData = {
  1749. origin: origin,
  1750. matrix: this.parseTransformMatrix()
  1751. };
  1752. } else {
  1753. this.transformData = {
  1754. origin: [0, 0],
  1755. matrix: [1, 0, 0, 1, 0, 0]
  1756. };
  1757. }
  1758. }
  1759. return this.transformData;
  1760. };
  1761.  
  1762. NodeContainer.prototype.parseTransformMatrix = function() {
  1763. if (!this.transformMatrix) {
  1764. var transform = this.prefixedCss("transform");
  1765. var matrix = transform ? parseMatrix(transform.match(this.MATRIX_PROPERTY)) : null;
  1766. this.transformMatrix = matrix ? matrix : [1, 0, 0, 1, 0, 0];
  1767. }
  1768. return this.transformMatrix;
  1769. };
  1770.  
  1771. NodeContainer.prototype.parseBounds = function() {
  1772. return this.bounds || (this.bounds = this.hasTransform() ? offsetBounds(this.node) : getBounds(this.node));
  1773. };
  1774.  
  1775. NodeContainer.prototype.hasTransform = function() {
  1776. return this.parseTransformMatrix().join(",") !== "1,0,0,1,0,0" || (this.parent && this.parent.hasTransform());
  1777. };
  1778.  
  1779. NodeContainer.prototype.getValue = function() {
  1780. var value = this.node.value || "";
  1781. if (this.node.tagName === "SELECT") {
  1782. value = selectionValue(this.node);
  1783. } else if (this.node.type === "password") {
  1784. value = Array(value.length + 1).join('\u2022'); // jshint ignore:line
  1785. }
  1786. return value.length === 0 ? (this.node.placeholder || "") : value;
  1787. };
  1788.  
  1789. NodeContainer.prototype.MATRIX_PROPERTY = /(matrix|matrix3d)\((.+)\)/;
  1790. NodeContainer.prototype.TEXT_SHADOW_PROPERTY = /((rgba|rgb)\([^\)]+\)(\s-?\d+px){0,})/g;
  1791. NodeContainer.prototype.TEXT_SHADOW_VALUES = /(-?\d+px)|(#.+)|(rgb\(.+\))|(rgba\(.+\))/g;
  1792. NodeContainer.prototype.CLIP = /^rect\((\d+)px,? (\d+)px,? (\d+)px,? (\d+)px\)$/;
  1793.  
  1794. function selectionValue(node) {
  1795. var option = node.options[node.selectedIndex || 0];
  1796. return option ? (option.text || "") : "";
  1797. }
  1798.  
  1799. function parseMatrix(match) {
  1800. if (match && match[1] === "matrix") {
  1801. return match[2].split(",").map(function(s) {
  1802. return parseFloat(s.trim());
  1803. });
  1804. } else if (match && match[1] === "matrix3d") {
  1805. var matrix3d = match[2].split(",").map(function(s) {
  1806. return parseFloat(s.trim());
  1807. });
  1808. return [matrix3d[0], matrix3d[1], matrix3d[4], matrix3d[5], matrix3d[12], matrix3d[13]];
  1809. }
  1810. }
  1811.  
  1812. function isPercentage(value) {
  1813. return value.toString().indexOf("%") !== -1;
  1814. }
  1815.  
  1816. function removePx(str) {
  1817. return str.replace("px", "");
  1818. }
  1819.  
  1820. function asFloat(str) {
  1821. return parseFloat(str);
  1822. }
  1823.  
  1824. module.exports = NodeContainer;
  1825.  
  1826. },{"./color":3,"./utils":26}],15:[function(require,module,exports){
  1827. var log = require('./log');
  1828. var punycode = require('punycode');
  1829. var NodeContainer = require('./nodecontainer');
  1830. var TextContainer = require('./textcontainer');
  1831. var PseudoElementContainer = require('./pseudoelementcontainer');
  1832. var FontMetrics = require('./fontmetrics');
  1833. var Color = require('./color');
  1834. var StackingContext = require('./stackingcontext');
  1835. var utils = require('./utils');
  1836. var bind = utils.bind;
  1837. var getBounds = utils.getBounds;
  1838. var parseBackgrounds = utils.parseBackgrounds;
  1839. var offsetBounds = utils.offsetBounds;
  1840.  
  1841. function NodeParser(element, renderer, support, imageLoader, options) {
  1842. log("Starting NodeParser");
  1843. this.renderer = renderer;
  1844. this.options = options;
  1845. this.range = null;
  1846. this.support = support;
  1847. this.renderQueue = [];
  1848. this.stack = new StackingContext(true, 1, element.ownerDocument, null);
  1849. var parent = new NodeContainer(element, null);
  1850. if (options.background) {
  1851. renderer.rectangle(0, 0, renderer.width, renderer.height, new Color(options.background));
  1852. }
  1853. if (element === element.ownerDocument.documentElement) {
  1854. // http://www.w3.org/TR/css3-background/#special-backgrounds
  1855. var canvasBackground = new NodeContainer(parent.color('backgroundColor').isTransparent() ? element.ownerDocument.body : element.ownerDocument.documentElement, null);
  1856. renderer.rectangle(0, 0, renderer.width, renderer.height, canvasBackground.color('backgroundColor'));
  1857. }
  1858. parent.visibile = parent.isElementVisible();
  1859. this.createPseudoHideStyles(element.ownerDocument);
  1860. this.disableAnimations(element.ownerDocument);
  1861. this.nodes = flatten([parent].concat(this.getChildren(parent)).filter(function(container) {
  1862. return container.visible = container.isElementVisible();
  1863. }).map(this.getPseudoElements, this));
  1864. this.fontMetrics = new FontMetrics();
  1865. log("Fetched nodes, total:", this.nodes.length);
  1866. log("Calculate overflow clips");
  1867. this.calculateOverflowClips();
  1868. log("Start fetching images");
  1869. this.images = imageLoader.fetch(this.nodes.filter(isElement));
  1870. this.ready = this.images.ready.then(bind(function() {
  1871. log("Images loaded, starting parsing");
  1872. log("Creating stacking contexts");
  1873. this.createStackingContexts();
  1874. log("Sorting stacking contexts");
  1875. this.sortStackingContexts(this.stack);
  1876. this.parse(this.stack);
  1877. log("Render queue created with " + this.renderQueue.length + " items");
  1878. return new Promise(bind(function(resolve) {
  1879. if (!options.async) {
  1880. this.renderQueue.forEach(this.paint, this);
  1881. resolve();
  1882. } else if (typeof(options.async) === "function") {
  1883. options.async.call(this, this.renderQueue, resolve);
  1884. } else if (this.renderQueue.length > 0){
  1885. this.renderIndex = 0;
  1886. this.asyncRenderer(this.renderQueue, resolve);
  1887. } else {
  1888. resolve();
  1889. }
  1890. }, this));
  1891. }, this));
  1892. }
  1893.  
  1894. NodeParser.prototype.calculateOverflowClips = function() {
  1895. this.nodes.forEach(function(container) {
  1896. if (isElement(container)) {
  1897. if (isPseudoElement(container)) {
  1898. container.appendToDOM();
  1899. }
  1900. container.borders = this.parseBorders(container);
  1901. var clip = (container.css('overflow') === "hidden") ? [container.borders.clip] : [];
  1902. var cssClip = container.parseClip();
  1903. if (cssClip && ["absolute", "fixed"].indexOf(container.css('position')) !== -1) {
  1904. clip.push([["rect",
  1905. container.bounds.left + cssClip.left,
  1906. container.bounds.top + cssClip.top,
  1907. cssClip.right - cssClip.left,
  1908. cssClip.bottom - cssClip.top
  1909. ]]);
  1910. }
  1911. container.clip = hasParentClip(container) ? container.parent.clip.concat(clip) : clip;
  1912. container.backgroundClip = (container.css('overflow') !== "hidden") ? container.clip.concat([container.borders.clip]) : container.clip;
  1913. if (isPseudoElement(container)) {
  1914. container.cleanDOM();
  1915. }
  1916. } else if (isTextNode(container)) {
  1917. container.clip = hasParentClip(container) ? container.parent.clip : [];
  1918. }
  1919. if (!isPseudoElement(container)) {
  1920. container.bounds = null;
  1921. }
  1922. }, this);
  1923. };
  1924.  
  1925. function hasParentClip(container) {
  1926. return container.parent && container.parent.clip.length;
  1927. }
  1928.  
  1929. NodeParser.prototype.asyncRenderer = function(queue, resolve, asyncTimer) {
  1930. asyncTimer = asyncTimer || Date.now();
  1931. this.paint(queue[this.renderIndex++]);
  1932. if (queue.length === this.renderIndex) {
  1933. resolve();
  1934. } else if (asyncTimer + 20 > Date.now()) {
  1935. this.asyncRenderer(queue, resolve, asyncTimer);
  1936. } else {
  1937. setTimeout(bind(function() {
  1938. this.asyncRenderer(queue, resolve);
  1939. }, this), 0);
  1940. }
  1941. };
  1942.  
  1943. NodeParser.prototype.createPseudoHideStyles = function(document) {
  1944. this.createStyles(document, '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + ':before { content: "" !important; display: none !important; }' +
  1945. '.' + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER + ':after { content: "" !important; display: none !important; }');
  1946. };
  1947.  
  1948. NodeParser.prototype.disableAnimations = function(document) {
  1949. this.createStyles(document, '* { -webkit-animation: none !important; -moz-animation: none !important; -o-animation: none !important; animation: none !important; ' +
  1950. '-webkit-transition: none !important; -moz-transition: none !important; -o-transition: none !important; transition: none !important;}');
  1951. };
  1952.  
  1953. NodeParser.prototype.createStyles = function(document, styles) {
  1954. var hidePseudoElements = document.createElement('style');
  1955. hidePseudoElements.innerHTML = styles;
  1956. document.body.appendChild(hidePseudoElements);
  1957. };
  1958.  
  1959. NodeParser.prototype.getPseudoElements = function(container) {
  1960. var nodes = [[container]];
  1961. if (container.node.nodeType === Node.ELEMENT_NODE) {
  1962. var before = this.getPseudoElement(container, ":before");
  1963. var after = this.getPseudoElement(container, ":after");
  1964.  
  1965. if (before) {
  1966. nodes.push(before);
  1967. }
  1968.  
  1969. if (after) {
  1970. nodes.push(after);
  1971. }
  1972. }
  1973. return flatten(nodes);
  1974. };
  1975.  
  1976. function toCamelCase(str) {
  1977. return str.replace(/(\-[a-z])/g, function(match){
  1978. return match.toUpperCase().replace('-','');
  1979. });
  1980. }
  1981.  
  1982. NodeParser.prototype.getPseudoElement = function(container, type) {
  1983. var style = container.computedStyle(type);
  1984. if(!style || !style.content || style.content === "none" || style.content === "-moz-alt-content" || style.display === "none") {
  1985. return null;
  1986. }
  1987.  
  1988. var content = stripQuotes(style.content);
  1989. var isImage = content.substr(0, 3) === 'url';
  1990. var pseudoNode = document.createElement(isImage ? 'img' : 'html2canvaspseudoelement');
  1991. var pseudoContainer = new PseudoElementContainer(pseudoNode, container, type);
  1992.  
  1993. for (var i = style.length-1; i >= 0; i--) {
  1994. var property = toCamelCase(style.item(i));
  1995. pseudoNode.style[property] = style[property];
  1996. }
  1997.  
  1998. pseudoNode.className = PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE + " " + PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER;
  1999.  
  2000. if (isImage) {
  2001. pseudoNode.src = parseBackgrounds(content)[0].args[0];
  2002. return [pseudoContainer];
  2003. } else {
  2004. var text = document.createTextNode(content);
  2005. pseudoNode.appendChild(text);
  2006. return [pseudoContainer, new TextContainer(text, pseudoContainer)];
  2007. }
  2008. };
  2009.  
  2010.  
  2011. NodeParser.prototype.getChildren = function(parentContainer) {
  2012. return flatten([].filter.call(parentContainer.node.childNodes, renderableNode).map(function(node) {
  2013. var container = [node.nodeType === Node.TEXT_NODE ? new TextContainer(node, parentContainer) : new NodeContainer(node, parentContainer)].filter(nonIgnoredElement);
  2014. return node.nodeType === Node.ELEMENT_NODE && container.length && node.tagName !== "TEXTAREA" ? (container[0].isElementVisible() ? container.concat(this.getChildren(container[0])) : []) : container;
  2015. }, this));
  2016. };
  2017.  
  2018. NodeParser.prototype.newStackingContext = function(container, hasOwnStacking) {
  2019. var stack = new StackingContext(hasOwnStacking, container.getOpacity(), container.node, container.parent);
  2020. container.cloneTo(stack);
  2021. var parentStack = hasOwnStacking ? stack.getParentStack(this) : stack.parent.stack;
  2022. parentStack.contexts.push(stack);
  2023. container.stack = stack;
  2024. };
  2025.  
  2026. NodeParser.prototype.createStackingContexts = function() {
  2027. this.nodes.forEach(function(container) {
  2028. if (isElement(container) && (this.isRootElement(container) || hasOpacity(container) || isPositionedForStacking(container) || this.isBodyWithTransparentRoot(container) || container.hasTransform())) {
  2029. this.newStackingContext(container, true);
  2030. } else if (isElement(container) && ((isPositioned(container) && zIndex0(container)) || isInlineBlock(container) || isFloating(container))) {
  2031. this.newStackingContext(container, false);
  2032. } else {
  2033. container.assignStack(container.parent.stack);
  2034. }
  2035. }, this);
  2036. };
  2037.  
  2038. NodeParser.prototype.isBodyWithTransparentRoot = function(container) {
  2039. return container.node.nodeName === "BODY" && container.parent.color('backgroundColor').isTransparent();
  2040. };
  2041.  
  2042. NodeParser.prototype.isRootElement = function(container) {
  2043. return container.parent === null;
  2044. };
  2045.  
  2046. NodeParser.prototype.sortStackingContexts = function(stack) {
  2047. stack.contexts.sort(zIndexSort(stack.contexts.slice(0)));
  2048. stack.contexts.forEach(this.sortStackingContexts, this);
  2049. };
  2050.  
  2051. NodeParser.prototype.parseTextBounds = function(container) {
  2052. return function(text, index, textList) {
  2053. if (container.parent.css("textDecoration").substr(0, 4) !== "none" || text.trim().length !== 0) {
  2054. if (this.support.rangeBounds && !container.parent.hasTransform()) {
  2055. var offset = textList.slice(0, index).join("").length;
  2056. return this.getRangeBounds(container.node, offset, text.length);
  2057. } else if (container.node && typeof(container.node.data) === "string") {
  2058. var replacementNode = container.node.splitText(text.length);
  2059. var bounds = this.getWrapperBounds(container.node, container.parent.hasTransform());
  2060. container.node = replacementNode;
  2061. return bounds;
  2062. }
  2063. } else if(!this.support.rangeBounds || container.parent.hasTransform()){
  2064. container.node = container.node.splitText(text.length);
  2065. }
  2066. return {};
  2067. };
  2068. };
  2069.  
  2070. NodeParser.prototype.getWrapperBounds = function(node, transform) {
  2071. var wrapper = node.ownerDocument.createElement('html2canvaswrapper');
  2072. var parent = node.parentNode,
  2073. backupText = node.cloneNode(true);
  2074.  
  2075. wrapper.appendChild(node.cloneNode(true));
  2076. parent.replaceChild(wrapper, node);
  2077. var bounds = transform ? offsetBounds(wrapper) : getBounds(wrapper);
  2078. parent.replaceChild(backupText, wrapper);
  2079. return bounds;
  2080. };
  2081.  
  2082. NodeParser.prototype.getRangeBounds = function(node, offset, length) {
  2083. var range = this.range || (this.range = node.ownerDocument.createRange());
  2084. range.setStart(node, offset);
  2085. range.setEnd(node, offset + length);
  2086. return range.getBoundingClientRect();
  2087. };
  2088.  
  2089. function ClearTransform() {}
  2090.  
  2091. NodeParser.prototype.parse = function(stack) {
  2092. // http://www.w3.org/TR/CSS21/visuren.html#z-index
  2093. var negativeZindex = stack.contexts.filter(negativeZIndex); // 2. the child stacking contexts with negative stack levels (most negative first).
  2094. var descendantElements = stack.children.filter(isElement);
  2095. var descendantNonFloats = descendantElements.filter(not(isFloating));
  2096. var nonInlineNonPositionedDescendants = descendantNonFloats.filter(not(isPositioned)).filter(not(inlineLevel)); // 3 the in-flow, non-inline-level, non-positioned descendants.
  2097. var nonPositionedFloats = descendantElements.filter(not(isPositioned)).filter(isFloating); // 4. the non-positioned floats.
  2098. var inFlow = descendantNonFloats.filter(not(isPositioned)).filter(inlineLevel); // 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
  2099. 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.
  2100. var text = stack.children.filter(isTextNode).filter(hasText);
  2101. var positiveZindex = stack.contexts.filter(positiveZIndex); // 7. the child stacking contexts with positive stack levels (least positive first).
  2102. negativeZindex.concat(nonInlineNonPositionedDescendants).concat(nonPositionedFloats)
  2103. .concat(inFlow).concat(stackLevel0).concat(text).concat(positiveZindex).forEach(function(container) {
  2104. this.renderQueue.push(container);
  2105. if (isStackingContext(container)) {
  2106. this.parse(container);
  2107. this.renderQueue.push(new ClearTransform());
  2108. }
  2109. }, this);
  2110. };
  2111.  
  2112. NodeParser.prototype.paint = function(container) {
  2113. try {
  2114. if (container instanceof ClearTransform) {
  2115. this.renderer.ctx.restore();
  2116. } else if (isTextNode(container)) {
  2117. if (isPseudoElement(container.parent)) {
  2118. container.parent.appendToDOM();
  2119. }
  2120. this.paintText(container);
  2121. if (isPseudoElement(container.parent)) {
  2122. container.parent.cleanDOM();
  2123. }
  2124. } else {
  2125. this.paintNode(container);
  2126. }
  2127. } catch(e) {
  2128. log(e);
  2129. if (this.options.strict) {
  2130. throw e;
  2131. }
  2132. }
  2133. };
  2134.  
  2135. NodeParser.prototype.paintNode = function(container) {
  2136. if (isStackingContext(container)) {
  2137. this.renderer.setOpacity(container.opacity);
  2138. this.renderer.ctx.save();
  2139. if (container.hasTransform()) {
  2140. this.renderer.setTransform(container.parseTransform());
  2141. }
  2142. }
  2143.  
  2144. if (container.node.nodeName === "INPUT" && container.node.type === "checkbox") {
  2145. this.paintCheckbox(container);
  2146. } else if (container.node.nodeName === "INPUT" && container.node.type === "radio") {
  2147. this.paintRadio(container);
  2148. } else {
  2149. this.paintElement(container);
  2150. }
  2151. };
  2152.  
  2153. NodeParser.prototype.paintElement = function(container) {
  2154. var bounds = container.parseBounds();
  2155. this.renderer.clip(container.backgroundClip, function() {
  2156. this.renderer.renderBackground(container, bounds, container.borders.borders.map(getWidth));
  2157. }, this);
  2158.  
  2159. this.renderer.clip(container.clip, function() {
  2160. this.renderer.renderBorders(container.borders.borders);
  2161. }, this);
  2162.  
  2163. this.renderer.clip(container.backgroundClip, function() {
  2164. switch (container.node.nodeName) {
  2165. case "svg":
  2166. case "IFRAME":
  2167. var imgContainer = this.images.get(container.node);
  2168. if (imgContainer) {
  2169. this.renderer.renderImage(container, bounds, container.borders, imgContainer);
  2170. } else {
  2171. log("Error loading <" + container.node.nodeName + ">", container.node);
  2172. }
  2173. break;
  2174. case "IMG":
  2175. var imageContainer = this.images.get(container.node.src);
  2176. if (imageContainer) {
  2177. this.renderer.renderImage(container, bounds, container.borders, imageContainer);
  2178. } else {
  2179. log("Error loading <img>", container.node.src);
  2180. }
  2181. break;
  2182. case "CANVAS":
  2183. this.renderer.renderImage(container, bounds, container.borders, {image: container.node});
  2184. break;
  2185. case "SELECT":
  2186. case "INPUT":
  2187. case "TEXTAREA":
  2188. this.paintFormValue(container);
  2189. break;
  2190. }
  2191. }, this);
  2192. };
  2193.  
  2194. NodeParser.prototype.paintCheckbox = function(container) {
  2195. var b = container.parseBounds();
  2196.  
  2197. var size = Math.min(b.width, b.height);
  2198. var bounds = {width: size - 1, height: size - 1, top: b.top, left: b.left};
  2199. var r = [3, 3];
  2200. var radius = [r, r, r, r];
  2201. var borders = [1,1,1,1].map(function(w) {
  2202. return {color: new Color('#A5A5A5'), width: w};
  2203. });
  2204.  
  2205. var borderPoints = calculateCurvePoints(bounds, radius, borders);
  2206.  
  2207. this.renderer.clip(container.backgroundClip, function() {
  2208. this.renderer.rectangle(bounds.left + 1, bounds.top + 1, bounds.width - 2, bounds.height - 2, new Color("#DEDEDE"));
  2209. this.renderer.renderBorders(calculateBorders(borders, bounds, borderPoints, radius));
  2210. if (container.node.checked) {
  2211. this.renderer.font(new Color('#424242'), 'normal', 'normal', 'bold', (size - 3) + "px", 'arial');
  2212. this.renderer.text("\u2714", bounds.left + size / 6, bounds.top + size - 1);
  2213. }
  2214. }, this);
  2215. };
  2216.  
  2217. NodeParser.prototype.paintRadio = function(container) {
  2218. var bounds = container.parseBounds();
  2219.  
  2220. var size = Math.min(bounds.width, bounds.height) - 2;
  2221.  
  2222. this.renderer.clip(container.backgroundClip, function() {
  2223. this.renderer.circleStroke(bounds.left + 1, bounds.top + 1, size, new Color('#DEDEDE'), 1, new Color('#A5A5A5'));
  2224. if (container.node.checked) {
  2225. this.renderer.circle(Math.ceil(bounds.left + size / 4) + 1, Math.ceil(bounds.top + size / 4) + 1, Math.floor(size / 2), new Color('#424242'));
  2226. }
  2227. }, this);
  2228. };
  2229.  
  2230. NodeParser.prototype.paintFormValue = function(container) {
  2231. var value = container.getValue();
  2232. if (value.length > 0) {
  2233. var document = container.node.ownerDocument;
  2234. var wrapper = document.createElement('html2canvaswrapper');
  2235. var properties = ['lineHeight', 'textAlign', 'fontFamily', 'fontWeight', 'fontSize', 'color',
  2236. 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom',
  2237. 'width', 'height', 'borderLeftStyle', 'borderTopStyle', 'borderLeftWidth', 'borderTopWidth',
  2238. 'boxSizing', 'whiteSpace', 'wordWrap'];
  2239.  
  2240. properties.forEach(function(property) {
  2241. try {
  2242. wrapper.style[property] = container.css(property);
  2243. } catch(e) {
  2244. // Older IE has issues with "border"
  2245. log("html2canvas: Parse: Exception caught in renderFormValue: " + e.message);
  2246. }
  2247. });
  2248. var bounds = container.parseBounds();
  2249. wrapper.style.position = "fixed";
  2250. wrapper.style.left = bounds.left + "px";
  2251. wrapper.style.top = bounds.top + "px";
  2252. wrapper.textContent = value;
  2253. document.body.appendChild(wrapper);
  2254. this.paintText(new TextContainer(wrapper.firstChild, container));
  2255. document.body.removeChild(wrapper);
  2256. }
  2257. };
  2258.  
  2259. NodeParser.prototype.paintText = function(container) {
  2260. container.applyTextTransform();
  2261. var characters = punycode.ucs2.decode(container.node.data);
  2262. var textList = (!this.options.letterRendering || noLetterSpacing(container)) && !hasUnicode(container.node.data) ? getWords(characters) : characters.map(function(character) {
  2263. return punycode.ucs2.encode([character]);
  2264. });
  2265.  
  2266. var weight = container.parent.fontWeight();
  2267. var size = container.parent.css('fontSize');
  2268. var family = container.parent.css('fontFamily');
  2269. var shadows = container.parent.parseTextShadows();
  2270.  
  2271. this.renderer.font(container.parent.color('color'), container.parent.css('fontStyle'), container.parent.css('fontVariant'), weight, size, family);
  2272. if (shadows.length) {
  2273. // TODO: support multiple text shadows
  2274. this.renderer.fontShadow(shadows[0].color, shadows[0].offsetX, shadows[0].offsetY, shadows[0].blur);
  2275. } else {
  2276. this.renderer.clearShadow();
  2277. }
  2278.  
  2279. this.renderer.clip(container.parent.clip, function() {
  2280. textList.map(this.parseTextBounds(container), this).forEach(function(bounds, index) {
  2281. if (bounds) {
  2282. this.renderer.text(textList[index], bounds.left, bounds.bottom);
  2283. this.renderTextDecoration(container.parent, bounds, this.fontMetrics.getMetrics(family, size));
  2284. }
  2285. }, this);
  2286. }, this);
  2287. };
  2288.  
  2289. NodeParser.prototype.renderTextDecoration = function(container, bounds, metrics) {
  2290. switch(container.css("textDecoration").split(" ")[0]) {
  2291. case "underline":
  2292. // Draws a line at the baseline of the font
  2293. // 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
  2294. this.renderer.rectangle(bounds.left, Math.round(bounds.top + metrics.baseline + metrics.lineWidth), bounds.width, 1, container.color("color"));
  2295. break;
  2296. case "overline":
  2297. this.renderer.rectangle(bounds.left, Math.round(bounds.top), bounds.width, 1, container.color("color"));
  2298. break;
  2299. case "line-through":
  2300. // TODO try and find exact position for line-through
  2301. this.renderer.rectangle(bounds.left, Math.ceil(bounds.top + metrics.middle + metrics.lineWidth), bounds.width, 1, container.color("color"));
  2302. break;
  2303. }
  2304. };
  2305.  
  2306. var borderColorTransforms = {
  2307. inset: [
  2308. ["darken", 0.60],
  2309. ["darken", 0.10],
  2310. ["darken", 0.10],
  2311. ["darken", 0.60]
  2312. ]
  2313. };
  2314.  
  2315. NodeParser.prototype.parseBorders = function(container) {
  2316. var nodeBounds = container.parseBounds();
  2317. var radius = getBorderRadiusData(container);
  2318. var borders = ["Top", "Right", "Bottom", "Left"].map(function(side, index) {
  2319. var style = container.css('border' + side + 'Style');
  2320. var color = container.color('border' + side + 'Color');
  2321. if (style === "inset" && color.isBlack()) {
  2322. color = new Color([255, 255, 255, color.a]); // this is wrong, but
  2323. }
  2324. var colorTransform = borderColorTransforms[style] ? borderColorTransforms[style][index] : null;
  2325. return {
  2326. width: container.cssInt('border' + side + 'Width'),
  2327. color: colorTransform ? color[colorTransform[0]](colorTransform[1]) : color,
  2328. args: null
  2329. };
  2330. });
  2331. var borderPoints = calculateCurvePoints(nodeBounds, radius, borders);
  2332.  
  2333. return {
  2334. clip: this.parseBackgroundClip(container, borderPoints, borders, radius, nodeBounds),
  2335. borders: calculateBorders(borders, nodeBounds, borderPoints, radius)
  2336. };
  2337. };
  2338.  
  2339. function calculateBorders(borders, nodeBounds, borderPoints, radius) {
  2340. return borders.map(function(border, borderSide) {
  2341. if (border.width > 0) {
  2342. var bx = nodeBounds.left;
  2343. var by = nodeBounds.top;
  2344. var bw = nodeBounds.width;
  2345. var bh = nodeBounds.height - (borders[2].width);
  2346.  
  2347. switch(borderSide) {
  2348. case 0:
  2349. // top border
  2350. bh = borders[0].width;
  2351. border.args = drawSide({
  2352. c1: [bx, by],
  2353. c2: [bx + bw, by],
  2354. c3: [bx + bw - borders[1].width, by + bh],
  2355. c4: [bx + borders[3].width, by + bh]
  2356. }, radius[0], radius[1],
  2357. borderPoints.topLeftOuter, borderPoints.topLeftInner, borderPoints.topRightOuter, borderPoints.topRightInner);
  2358. break;
  2359. case 1:
  2360. // right border
  2361. bx = nodeBounds.left + nodeBounds.width - (borders[1].width);
  2362. bw = borders[1].width;
  2363.  
  2364. border.args = drawSide({
  2365. c1: [bx + bw, by],
  2366. c2: [bx + bw, by + bh + borders[2].width],
  2367. c3: [bx, by + bh],
  2368. c4: [bx, by + borders[0].width]
  2369. }, radius[1], radius[2],
  2370. borderPoints.topRightOuter, borderPoints.topRightInner, borderPoints.bottomRightOuter, borderPoints.bottomRightInner);
  2371. break;
  2372. case 2:
  2373. // bottom border
  2374. by = (by + nodeBounds.height) - (borders[2].width);
  2375. bh = borders[2].width;
  2376. border.args = drawSide({
  2377. c1: [bx + bw, by + bh],
  2378. c2: [bx, by + bh],
  2379. c3: [bx + borders[3].width, by],
  2380. c4: [bx + bw - borders[3].width, by]
  2381. }, radius[2], radius[3],
  2382. borderPoints.bottomRightOuter, borderPoints.bottomRightInner, borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner);
  2383. break;
  2384. case 3:
  2385. // left border
  2386. bw = borders[3].width;
  2387. border.args = drawSide({
  2388. c1: [bx, by + bh + borders[2].width],
  2389. c2: [bx, by],
  2390. c3: [bx + bw, by + borders[0].width],
  2391. c4: [bx + bw, by + bh]
  2392. }, radius[3], radius[0],
  2393. borderPoints.bottomLeftOuter, borderPoints.bottomLeftInner, borderPoints.topLeftOuter, borderPoints.topLeftInner);
  2394. break;
  2395. }
  2396. }
  2397. return border;
  2398. });
  2399. }
  2400.  
  2401. NodeParser.prototype.parseBackgroundClip = function(container, borderPoints, borders, radius, bounds) {
  2402. var backgroundClip = container.css('backgroundClip'),
  2403. borderArgs = [];
  2404.  
  2405. switch(backgroundClip) {
  2406. case "content-box":
  2407. case "padding-box":
  2408. parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftInner, borderPoints.topRightInner, bounds.left + borders[3].width, bounds.top + borders[0].width);
  2409. parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightInner, borderPoints.bottomRightInner, bounds.left + bounds.width - borders[1].width, bounds.top + borders[0].width);
  2410. parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightInner, borderPoints.bottomLeftInner, bounds.left + bounds.width - borders[1].width, bounds.top + bounds.height - borders[2].width);
  2411. parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftInner, borderPoints.topLeftInner, bounds.left + borders[3].width, bounds.top + bounds.height - borders[2].width);
  2412. break;
  2413.  
  2414. default:
  2415. parseCorner(borderArgs, radius[0], radius[1], borderPoints.topLeftOuter, borderPoints.topRightOuter, bounds.left, bounds.top);
  2416. parseCorner(borderArgs, radius[1], radius[2], borderPoints.topRightOuter, borderPoints.bottomRightOuter, bounds.left + bounds.width, bounds.top);
  2417. parseCorner(borderArgs, radius[2], radius[3], borderPoints.bottomRightOuter, borderPoints.bottomLeftOuter, bounds.left + bounds.width, bounds.top + bounds.height);
  2418. parseCorner(borderArgs, radius[3], radius[0], borderPoints.bottomLeftOuter, borderPoints.topLeftOuter, bounds.left, bounds.top + bounds.height);
  2419. break;
  2420. }
  2421.  
  2422. return borderArgs;
  2423. };
  2424.  
  2425. function getCurvePoints(x, y, r1, r2) {
  2426. var kappa = 4 * ((Math.sqrt(2) - 1) / 3);
  2427. var ox = (r1) * kappa, // control point offset horizontal
  2428. oy = (r2) * kappa, // control point offset vertical
  2429. xm = x + r1, // x-middle
  2430. ym = y + r2; // y-middle
  2431. return {
  2432. topLeft: bezierCurve({x: x, y: ym}, {x: x, y: ym - oy}, {x: xm - ox, y: y}, {x: xm, y: y}),
  2433. topRight: bezierCurve({x: x, y: y}, {x: x + ox,y: y}, {x: xm, y: ym - oy}, {x: xm, y: ym}),
  2434. bottomRight: bezierCurve({x: xm, y: y}, {x: xm, y: y + oy}, {x: x + ox, y: ym}, {x: x, y: ym}),
  2435. bottomLeft: bezierCurve({x: xm, y: ym}, {x: xm - ox, y: ym}, {x: x, y: y + oy}, {x: x, y:y})
  2436. };
  2437. }
  2438.  
  2439. function calculateCurvePoints(bounds, borderRadius, borders) {
  2440. var x = bounds.left,
  2441. y = bounds.top,
  2442. width = bounds.width,
  2443. height = bounds.height,
  2444.  
  2445. tlh = borderRadius[0][0] < width / 2 ? borderRadius[0][0] : width / 2,
  2446. tlv = borderRadius[0][1] < height / 2 ? borderRadius[0][1] : height / 2,
  2447. trh = borderRadius[1][0] < width / 2 ? borderRadius[1][0] : width / 2,
  2448. trv = borderRadius[1][1] < height / 2 ? borderRadius[1][1] : height / 2,
  2449. brh = borderRadius[2][0] < width / 2 ? borderRadius[2][0] : width / 2,
  2450. brv = borderRadius[2][1] < height / 2 ? borderRadius[2][1] : height / 2,
  2451. blh = borderRadius[3][0] < width / 2 ? borderRadius[3][0] : width / 2,
  2452. blv = borderRadius[3][1] < height / 2 ? borderRadius[3][1] : height / 2;
  2453.  
  2454. var topWidth = width - trh,
  2455. rightHeight = height - brv,
  2456. bottomWidth = width - brh,
  2457. leftHeight = height - blv;
  2458.  
  2459. return {
  2460. topLeftOuter: getCurvePoints(x, y, tlh, tlv).topLeft.subdivide(0.5),
  2461. 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),
  2462. topRightOuter: getCurvePoints(x + topWidth, y, trh, trv).topRight.subdivide(0.5),
  2463. 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),
  2464. bottomRightOuter: getCurvePoints(x + bottomWidth, y + rightHeight, brh, brv).bottomRight.subdivide(0.5),
  2465. 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),
  2466. bottomLeftOuter: getCurvePoints(x, y + leftHeight, blh, blv).bottomLeft.subdivide(0.5),
  2467. bottomLeftInner: getCurvePoints(x + borders[3].width, y + leftHeight, Math.max(0, blh - borders[3].width), blv - borders[2].width).bottomLeft.subdivide(0.5)
  2468. };
  2469. }
  2470.  
  2471. function bezierCurve(start, startControl, endControl, end) {
  2472. var lerp = function (a, b, t) {
  2473. return {
  2474. x: a.x + (b.x - a.x) * t,
  2475. y: a.y + (b.y - a.y) * t
  2476. };
  2477. };
  2478.  
  2479. return {
  2480. start: start,
  2481. startControl: startControl,
  2482. endControl: endControl,
  2483. end: end,
  2484. subdivide: function(t) {
  2485. var ab = lerp(start, startControl, t),
  2486. bc = lerp(startControl, endControl, t),
  2487. cd = lerp(endControl, end, t),
  2488. abbc = lerp(ab, bc, t),
  2489. bccd = lerp(bc, cd, t),
  2490. dest = lerp(abbc, bccd, t);
  2491. return [bezierCurve(start, ab, abbc, dest), bezierCurve(dest, bccd, cd, end)];
  2492. },
  2493. curveTo: function(borderArgs) {
  2494. borderArgs.push(["bezierCurve", startControl.x, startControl.y, endControl.x, endControl.y, end.x, end.y]);
  2495. },
  2496. curveToReversed: function(borderArgs) {
  2497. borderArgs.push(["bezierCurve", endControl.x, endControl.y, startControl.x, startControl.y, start.x, start.y]);
  2498. }
  2499. };
  2500. }
  2501.  
  2502. function drawSide(borderData, radius1, radius2, outer1, inner1, outer2, inner2) {
  2503. var borderArgs = [];
  2504.  
  2505. if (radius1[0] > 0 || radius1[1] > 0) {
  2506. borderArgs.push(["line", outer1[1].start.x, outer1[1].start.y]);
  2507. outer1[1].curveTo(borderArgs);
  2508. } else {
  2509. borderArgs.push([ "line", borderData.c1[0], borderData.c1[1]]);
  2510. }
  2511.  
  2512. if (radius2[0] > 0 || radius2[1] > 0) {
  2513. borderArgs.push(["line", outer2[0].start.x, outer2[0].start.y]);
  2514. outer2[0].curveTo(borderArgs);
  2515. borderArgs.push(["line", inner2[0].end.x, inner2[0].end.y]);
  2516. inner2[0].curveToReversed(borderArgs);
  2517. } else {
  2518. borderArgs.push(["line", borderData.c2[0], borderData.c2[1]]);
  2519. borderArgs.push(["line", borderData.c3[0], borderData.c3[1]]);
  2520. }
  2521.  
  2522. if (radius1[0] > 0 || radius1[1] > 0) {
  2523. borderArgs.push(["line", inner1[1].end.x, inner1[1].end.y]);
  2524. inner1[1].curveToReversed(borderArgs);
  2525. } else {
  2526. borderArgs.push(["line", borderData.c4[0], borderData.c4[1]]);
  2527. }
  2528.  
  2529. return borderArgs;
  2530. }
  2531.  
  2532. function parseCorner(borderArgs, radius1, radius2, corner1, corner2, x, y) {
  2533. if (radius1[0] > 0 || radius1[1] > 0) {
  2534. borderArgs.push(["line", corner1[0].start.x, corner1[0].start.y]);
  2535. corner1[0].curveTo(borderArgs);
  2536. corner1[1].curveTo(borderArgs);
  2537. } else {
  2538. borderArgs.push(["line", x, y]);
  2539. }
  2540.  
  2541. if (radius2[0] > 0 || radius2[1] > 0) {
  2542. borderArgs.push(["line", corner2[0].start.x, corner2[0].start.y]);
  2543. }
  2544. }
  2545.  
  2546. function negativeZIndex(container) {
  2547. return container.cssInt("zIndex") < 0;
  2548. }
  2549.  
  2550. function positiveZIndex(container) {
  2551. return container.cssInt("zIndex") > 0;
  2552. }
  2553.  
  2554. function zIndex0(container) {
  2555. return container.cssInt("zIndex") === 0;
  2556. }
  2557.  
  2558. function inlineLevel(container) {
  2559. return ["inline", "inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
  2560. }
  2561.  
  2562. function isStackingContext(container) {
  2563. return (container instanceof StackingContext);
  2564. }
  2565.  
  2566. function hasText(container) {
  2567. return container.node.data.trim().length > 0;
  2568. }
  2569.  
  2570. function noLetterSpacing(container) {
  2571. return (/^(normal|none|0px)$/.test(container.parent.css("letterSpacing")));
  2572. }
  2573.  
  2574. function getBorderRadiusData(container) {
  2575. return ["TopLeft", "TopRight", "BottomRight", "BottomLeft"].map(function(side) {
  2576. var value = container.css('border' + side + 'Radius');
  2577. var arr = value.split(" ");
  2578. if (arr.length <= 1) {
  2579. arr[1] = arr[0];
  2580. }
  2581. return arr.map(asInt);
  2582. });
  2583. }
  2584.  
  2585. function renderableNode(node) {
  2586. return (node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ELEMENT_NODE);
  2587. }
  2588.  
  2589. function isPositionedForStacking(container) {
  2590. var position = container.css("position");
  2591. var zIndex = (["absolute", "relative", "fixed"].indexOf(position) !== -1) ? container.css("zIndex") : "auto";
  2592. return zIndex !== "auto";
  2593. }
  2594.  
  2595. function isPositioned(container) {
  2596. return container.css("position") !== "static";
  2597. }
  2598.  
  2599. function isFloating(container) {
  2600. return container.css("float") !== "none";
  2601. }
  2602.  
  2603. function isInlineBlock(container) {
  2604. return ["inline-block", "inline-table"].indexOf(container.css("display")) !== -1;
  2605. }
  2606.  
  2607. function not(callback) {
  2608. var context = this;
  2609. return function() {
  2610. return !callback.apply(context, arguments);
  2611. };
  2612. }
  2613.  
  2614. function isElement(container) {
  2615. return container.node.nodeType === Node.ELEMENT_NODE;
  2616. }
  2617.  
  2618. function isPseudoElement(container) {
  2619. return container.isPseudoElement === true;
  2620. }
  2621.  
  2622. function isTextNode(container) {
  2623. return container.node.nodeType === Node.TEXT_NODE;
  2624. }
  2625.  
  2626. function zIndexSort(contexts) {
  2627. return function(a, b) {
  2628. return (a.cssInt("zIndex") + (contexts.indexOf(a) / contexts.length)) - (b.cssInt("zIndex") + (contexts.indexOf(b) / contexts.length));
  2629. };
  2630. }
  2631.  
  2632. function hasOpacity(container) {
  2633. return container.getOpacity() < 1;
  2634. }
  2635.  
  2636. function asInt(value) {
  2637. return parseInt(value, 10);
  2638. }
  2639.  
  2640. function getWidth(border) {
  2641. return border.width;
  2642. }
  2643.  
  2644. function nonIgnoredElement(nodeContainer) {
  2645. return (nodeContainer.node.nodeType !== Node.ELEMENT_NODE || ["SCRIPT", "HEAD", "TITLE", "OBJECT", "BR", "OPTION"].indexOf(nodeContainer.node.nodeName) === -1);
  2646. }
  2647.  
  2648. function flatten(arrays) {
  2649. return [].concat.apply([], arrays);
  2650. }
  2651.  
  2652. function stripQuotes(content) {
  2653. var first = content.substr(0, 1);
  2654. return (first === content.substr(content.length - 1) && first.match(/'|"/)) ? content.substr(1, content.length - 2) : content;
  2655. }
  2656.  
  2657. function getWords(characters) {
  2658. var words = [], i = 0, onWordBoundary = false, word;
  2659. while(characters.length) {
  2660. if (isWordBoundary(characters[i]) === onWordBoundary) {
  2661. word = characters.splice(0, i);
  2662. if (word.length) {
  2663. words.push(punycode.ucs2.encode(word));
  2664. }
  2665. onWordBoundary =! onWordBoundary;
  2666. i = 0;
  2667. } else {
  2668. i++;
  2669. }
  2670.  
  2671. if (i >= characters.length) {
  2672. word = characters.splice(0, i);
  2673. if (word.length) {
  2674. words.push(punycode.ucs2.encode(word));
  2675. }
  2676. }
  2677. }
  2678. return words;
  2679. }
  2680.  
  2681. function isWordBoundary(characterCode) {
  2682. return [
  2683. 32, // <space>
  2684. 13, // \r
  2685. 10, // \n
  2686. 9, // \t
  2687. 45 // -
  2688. ].indexOf(characterCode) !== -1;
  2689. }
  2690.  
  2691. function hasUnicode(string) {
  2692. return (/[^\u0000-\u00ff]/).test(string);
  2693. }
  2694.  
  2695. module.exports = NodeParser;
  2696.  
  2697. },{"./color":3,"./fontmetrics":7,"./log":13,"./nodecontainer":14,"./pseudoelementcontainer":18,"./stackingcontext":21,"./textcontainer":25,"./utils":26,"punycode":1}],16:[function(require,module,exports){
  2698. var XHR = require('./xhr');
  2699. var utils = require('./utils');
  2700. var log = require('./log');
  2701. var createWindowClone = require('./clone');
  2702. var decode64 = utils.decode64;
  2703.  
  2704. function Proxy(src, proxyUrl, document) {
  2705. var supportsCORS = ('withCredentials' in new XMLHttpRequest());
  2706. if (!proxyUrl) {
  2707. return Promise.reject("No proxy configured");
  2708. }
  2709. var callback = createCallback(supportsCORS);
  2710. var url = createProxyUrl(proxyUrl, src, callback);
  2711.  
  2712. return supportsCORS ? XHR(url) : (jsonp(document, url, callback).then(function(response) {
  2713. return decode64(response.content);
  2714. }));
  2715. }
  2716. var proxyCount = 0;
  2717.  
  2718. function ProxyURL(src, proxyUrl, document) {
  2719. var supportsCORSImage = ('crossOrigin' in new Image()) && !window.GM_PROXY;
  2720. var callback = createCallback(supportsCORSImage);
  2721. var url = createProxyUrl(proxyUrl, src, callback);
  2722. return (supportsCORSImage ? Promise.resolve(url) : jsonp(document, url, callback).then(function(response) {
  2723. return "data:" + response.type + ";base64," + response.content;
  2724. }));
  2725. }
  2726.  
  2727. function jsonp(document, url, callback) {
  2728. if (!window.html2canvas.proxy) {
  2729. window.html2canvas.proxy = {};
  2730. }
  2731.  
  2732. return new Promise(function(resolve, reject) {
  2733. var s = document.createElement("script");
  2734. var cleanup = function() {
  2735. delete window.html2canvas.proxy[callback];
  2736. document.body.removeChild(s);
  2737. };
  2738. window.html2canvas.proxy[callback] = function(response) {
  2739. cleanup();
  2740. resolve(response);
  2741. };
  2742. s.src = url;
  2743. s.onerror = function(e) {
  2744. cleanup();
  2745. reject(e);
  2746. };
  2747. document.body.appendChild(s);
  2748. });
  2749. }
  2750.  
  2751. function createCallback(useCORS) {
  2752. return !useCORS ? "html2canvas_" + Date.now() + "_" + (++proxyCount) + "_" + Math.round(Math.random() * 100000) : "";
  2753. }
  2754.  
  2755. function createProxyUrl(proxyUrl, src, callback) {
  2756. return proxyUrl + "?url=" + encodeURIComponent(src) + (callback.length ? "&callback=html2canvas.proxy." + callback : "");
  2757. }
  2758.  
  2759. function documentFromHTML(src) {
  2760. return function(html) {
  2761. var parser = new DOMParser(), doc;
  2762. try {
  2763. doc = parser.parseFromString(html, "text/html");
  2764. } catch(e) {
  2765. log("DOMParser not supported, falling back to createHTMLDocument");
  2766. doc = document.implementation.createHTMLDocument("");
  2767. try {
  2768. doc.open();
  2769. doc.write(html);
  2770. doc.close();
  2771. } catch(ee) {
  2772. log("createHTMLDocument write not supported, falling back to document.body.innerHTML");
  2773. doc.body.innerHTML = html; // ie9 doesnt support writing to documentElement
  2774. }
  2775. }
  2776.  
  2777. var b = doc.querySelector("base");
  2778. if (!b || !b.href.host) {
  2779. var base = doc.createElement("base");
  2780. base.href = src;
  2781. doc.head.insertBefore(base, doc.head.firstChild);
  2782. }
  2783.  
  2784. return doc;
  2785. };
  2786. }
  2787.  
  2788. function loadUrlDocument(src, proxy, document, width, height, options) {
  2789. return new Proxy(src, proxy, window.document).then(documentFromHTML(src)).then(function(doc) {
  2790. return createWindowClone(doc, document, width, height, options, 0, 0);
  2791. });
  2792. }
  2793.  
  2794. exports.Proxy = Proxy;
  2795. exports.ProxyURL = ProxyURL;
  2796. exports.loadUrlDocument = loadUrlDocument;
  2797.  
  2798. },{"./clone":2,"./log":13,"./utils":26,"./xhr":28}],17:[function(require,module,exports){
  2799. var ProxyURL = require('./proxy').ProxyURL;
  2800.  
  2801. function ProxyImageContainer(src, proxy) {
  2802. var link = document.createElement("a");
  2803. link.href = src;
  2804. src = link.href;
  2805. this.src = src;
  2806. this.image = new Image();
  2807. var self = this;
  2808. this.promise = new Promise(function(resolve, reject) {
  2809. self.image.crossOrigin = "Anonymous";
  2810. self.image.onload = resolve;
  2811. self.image.onerror = reject;
  2812.  
  2813. new ProxyURL(src, proxy, document).then(function(url) {
  2814. self.image.src = url;
  2815. })['catch'](reject);
  2816. });
  2817. }
  2818.  
  2819. module.exports = ProxyImageContainer;
  2820.  
  2821. },{"./proxy":16}],18:[function(require,module,exports){
  2822. var NodeContainer = require('./nodecontainer');
  2823.  
  2824. function PseudoElementContainer(node, parent, type) {
  2825. NodeContainer.call(this, node, parent);
  2826. this.isPseudoElement = true;
  2827. this.before = type === ":before";
  2828. }
  2829.  
  2830. PseudoElementContainer.prototype.cloneTo = function(stack) {
  2831. PseudoElementContainer.prototype.cloneTo.call(this, stack);
  2832. stack.isPseudoElement = true;
  2833. stack.before = this.before;
  2834. };
  2835.  
  2836. PseudoElementContainer.prototype = Object.create(NodeContainer.prototype);
  2837.  
  2838. PseudoElementContainer.prototype.appendToDOM = function() {
  2839. if (this.before) {
  2840. this.parent.node.insertBefore(this.node, this.parent.node.firstChild);
  2841. } else {
  2842. this.parent.node.appendChild(this.node);
  2843. }
  2844. this.parent.node.className += " " + this.getHideClass();
  2845. };
  2846.  
  2847. PseudoElementContainer.prototype.cleanDOM = function() {
  2848. this.node.parentNode.removeChild(this.node);
  2849. this.parent.node.className = this.parent.node.className.replace(this.getHideClass(), "");
  2850. };
  2851.  
  2852. PseudoElementContainer.prototype.getHideClass = function() {
  2853. return this["PSEUDO_HIDE_ELEMENT_CLASS_" + (this.before ? "BEFORE" : "AFTER")];
  2854. };
  2855.  
  2856. PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_BEFORE = "___html2canvas___pseudoelement_before";
  2857. PseudoElementContainer.prototype.PSEUDO_HIDE_ELEMENT_CLASS_AFTER = "___html2canvas___pseudoelement_after";
  2858.  
  2859. module.exports = PseudoElementContainer;
  2860.  
  2861. },{"./nodecontainer":14}],19:[function(require,module,exports){
  2862. var log = require('./log');
  2863.  
  2864. function Renderer(width, height, images, options, document) {
  2865. this.width = width;
  2866. this.height = height;
  2867. this.images = images;
  2868. this.options = options;
  2869. this.document = document;
  2870. }
  2871.  
  2872. Renderer.prototype.renderImage = function(container, bounds, borderData, imageContainer) {
  2873. var paddingLeft = container.cssInt('paddingLeft'),
  2874. paddingTop = container.cssInt('paddingTop'),
  2875. paddingRight = container.cssInt('paddingRight'),
  2876. paddingBottom = container.cssInt('paddingBottom'),
  2877. borders = borderData.borders;
  2878.  
  2879. var width = bounds.width - (borders[1].width + borders[3].width + paddingLeft + paddingRight);
  2880. var height = bounds.height - (borders[0].width + borders[2].width + paddingTop + paddingBottom);
  2881. this.drawImage(
  2882. imageContainer,
  2883. 0,
  2884. 0,
  2885. imageContainer.image.width || width,
  2886. imageContainer.image.height || height,
  2887. bounds.left + paddingLeft + borders[3].width,
  2888. bounds.top + paddingTop + borders[0].width,
  2889. width,
  2890. height
  2891. );
  2892. };
  2893.  
  2894. Renderer.prototype.renderBackground = function(container, bounds, borderData) {
  2895. if (bounds.height > 0 && bounds.width > 0) {
  2896. this.renderBackgroundColor(container, bounds);
  2897. this.renderBackgroundImage(container, bounds, borderData);
  2898. }
  2899. };
  2900.  
  2901. Renderer.prototype.renderBackgroundColor = function(container, bounds) {
  2902. var color = container.color("backgroundColor");
  2903. if (!color.isTransparent()) {
  2904. this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, color);
  2905. }
  2906. };
  2907.  
  2908. Renderer.prototype.renderBorders = function(borders) {
  2909. borders.forEach(this.renderBorder, this);
  2910. };
  2911.  
  2912. Renderer.prototype.renderBorder = function(data) {
  2913. if (!data.color.isTransparent() && data.args !== null) {
  2914. this.drawShape(data.args, data.color);
  2915. }
  2916. };
  2917.  
  2918. Renderer.prototype.renderBackgroundImage = function(container, bounds, borderData) {
  2919. var backgroundImages = container.parseBackgroundImages();
  2920. backgroundImages.reverse().forEach(function(backgroundImage, index, arr) {
  2921. switch(backgroundImage.method) {
  2922. case "url":
  2923. var image = this.images.get(backgroundImage.args[0]);
  2924. if (image) {
  2925. this.renderBackgroundRepeating(container, bounds, image, arr.length - (index+1), borderData);
  2926. } else {
  2927. log("Error loading background-image", backgroundImage.args[0]);
  2928. }
  2929. break;
  2930. case "linear-gradient":
  2931. case "gradient":
  2932. var gradientImage = this.images.get(backgroundImage.value);
  2933. if (gradientImage) {
  2934. this.renderBackgroundGradient(gradientImage, bounds, borderData);
  2935. } else {
  2936. log("Error loading background-image", backgroundImage.args[0]);
  2937. }
  2938. break;
  2939. case "none":
  2940. break;
  2941. default:
  2942. log("Unknown background-image type", backgroundImage.args[0]);
  2943. }
  2944. }, this);
  2945. };
  2946.  
  2947. Renderer.prototype.renderBackgroundRepeating = function(container, bounds, imageContainer, index, borderData) {
  2948. var size = container.parseBackgroundSize(bounds, imageContainer.image, index);
  2949. var position = container.parseBackgroundPosition(bounds, imageContainer.image, index, size);
  2950. var repeat = container.parseBackgroundRepeat(index);
  2951. switch (repeat) {
  2952. case "repeat-x":
  2953. case "repeat no-repeat":
  2954. this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + borderData[3], bounds.top + position.top + borderData[0], 99999, size.height, borderData);
  2955. break;
  2956. case "repeat-y":
  2957. case "no-repeat repeat":
  2958. this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + borderData[0], size.width, 99999, borderData);
  2959. break;
  2960. case "no-repeat":
  2961. this.backgroundRepeatShape(imageContainer, position, size, bounds, bounds.left + position.left + borderData[3], bounds.top + position.top + borderData[0], size.width, size.height, borderData);
  2962. break;
  2963. default:
  2964. this.renderBackgroundRepeat(imageContainer, position, size, {top: bounds.top, left: bounds.left}, borderData[3], borderData[0]);
  2965. break;
  2966. }
  2967. };
  2968.  
  2969. module.exports = Renderer;
  2970.  
  2971. },{"./log":13}],20:[function(require,module,exports){
  2972. var Renderer = require('../renderer');
  2973. var LinearGradientContainer = require('../lineargradientcontainer');
  2974. var log = require('../log');
  2975.  
  2976. function CanvasRenderer(width, height) {
  2977. Renderer.apply(this, arguments);
  2978. this.canvas = this.options.canvas || this.document.createElement("canvas");
  2979. if (!this.options.canvas) {
  2980. this.canvas.width = width;
  2981. this.canvas.height = height;
  2982. }
  2983. this.ctx = this.canvas.getContext("2d");
  2984. this.taintCtx = this.document.createElement("canvas").getContext("2d");
  2985. this.ctx.textBaseline = "bottom";
  2986. this.variables = {};
  2987. log("Initialized CanvasRenderer with size", width, "x", height);
  2988. }
  2989.  
  2990. CanvasRenderer.prototype = Object.create(Renderer.prototype);
  2991.  
  2992. CanvasRenderer.prototype.setFillStyle = function(fillStyle) {
  2993. this.ctx.fillStyle = typeof(fillStyle) === "object" && !!fillStyle.isColor ? fillStyle.toString() : fillStyle;
  2994. return this.ctx;
  2995. };
  2996.  
  2997. CanvasRenderer.prototype.rectangle = function(left, top, width, height, color) {
  2998. this.setFillStyle(color).fillRect(left, top, width, height);
  2999. };
  3000.  
  3001. CanvasRenderer.prototype.circle = function(left, top, size, color) {
  3002. this.setFillStyle(color);
  3003. this.ctx.beginPath();
  3004. this.ctx.arc(left + size / 2, top + size / 2, size / 2, 0, Math.PI*2, true);
  3005. this.ctx.closePath();
  3006. this.ctx.fill();
  3007. };
  3008.  
  3009. CanvasRenderer.prototype.circleStroke = function(left, top, size, color, stroke, strokeColor) {
  3010. this.circle(left, top, size, color);
  3011. this.ctx.strokeStyle = strokeColor.toString();
  3012. this.ctx.stroke();
  3013. };
  3014.  
  3015. CanvasRenderer.prototype.drawShape = function(shape, color) {
  3016. this.shape(shape);
  3017. this.setFillStyle(color).fill();
  3018. };
  3019.  
  3020. CanvasRenderer.prototype.taints = function(imageContainer) {
  3021. if (imageContainer.tainted === null) {
  3022. this.taintCtx.drawImage(imageContainer.image, 0, 0);
  3023. try {
  3024. this.taintCtx.getImageData(0, 0, 1, 1);
  3025. imageContainer.tainted = false;
  3026. } catch(e) {
  3027. this.taintCtx = document.createElement("canvas").getContext("2d");
  3028. imageContainer.tainted = true;
  3029. }
  3030. }
  3031.  
  3032. return imageContainer.tainted;
  3033. };
  3034.  
  3035. CanvasRenderer.prototype.drawImage = function(imageContainer, sx, sy, sw, sh, dx, dy, dw, dh) {
  3036. if (!this.taints(imageContainer) || this.options.allowTaint) {
  3037. this.ctx.drawImage(imageContainer.image, sx, sy, sw, sh, dx, dy, dw, dh);
  3038. }
  3039. };
  3040.  
  3041. CanvasRenderer.prototype.clip = function(shapes, callback, context) {
  3042. this.ctx.save();
  3043. shapes.filter(hasEntries).forEach(function(shape) {
  3044. this.shape(shape).clip();
  3045. }, this);
  3046. callback.call(context);
  3047. this.ctx.restore();
  3048. };
  3049.  
  3050. CanvasRenderer.prototype.shape = function(shape) {
  3051. this.ctx.beginPath();
  3052. shape.forEach(function(point, index) {
  3053. if (point[0] === "rect") {
  3054. this.ctx.rect.apply(this.ctx, point.slice(1));
  3055. } else {
  3056. this.ctx[(index === 0) ? "moveTo" : point[0] + "To" ].apply(this.ctx, point.slice(1));
  3057. }
  3058. }, this);
  3059. this.ctx.closePath();
  3060. return this.ctx;
  3061. };
  3062.  
  3063. CanvasRenderer.prototype.font = function(color, style, variant, weight, size, family) {
  3064. this.setFillStyle(color).font = [style, variant, weight, size, family].join(" ").split(",")[0];
  3065. };
  3066.  
  3067. CanvasRenderer.prototype.fontShadow = function(color, offsetX, offsetY, blur) {
  3068. this.setVariable("shadowColor", color.toString())
  3069. .setVariable("shadowOffsetY", offsetX)
  3070. .setVariable("shadowOffsetX", offsetY)
  3071. .setVariable("shadowBlur", blur);
  3072. };
  3073.  
  3074. CanvasRenderer.prototype.clearShadow = function() {
  3075. this.setVariable("shadowColor", "rgba(0,0,0,0)");
  3076. };
  3077.  
  3078. CanvasRenderer.prototype.setOpacity = function(opacity) {
  3079. this.ctx.globalAlpha = opacity;
  3080. };
  3081.  
  3082. CanvasRenderer.prototype.setTransform = function(transform) {
  3083. this.ctx.translate(transform.origin[0], transform.origin[1]);
  3084. this.ctx.transform.apply(this.ctx, transform.matrix);
  3085. this.ctx.translate(-transform.origin[0], -transform.origin[1]);
  3086. };
  3087.  
  3088. CanvasRenderer.prototype.setVariable = function(property, value) {
  3089. if (this.variables[property] !== value) {
  3090. this.variables[property] = this.ctx[property] = value;
  3091. }
  3092.  
  3093. return this;
  3094. };
  3095.  
  3096. CanvasRenderer.prototype.text = function(text, left, bottom) {
  3097. this.ctx.fillText(text, left, bottom);
  3098. };
  3099.  
  3100. CanvasRenderer.prototype.backgroundRepeatShape = function(imageContainer, backgroundPosition, size, bounds, left, top, width, height, borderData) {
  3101. var shape = [
  3102. ["line", Math.round(left), Math.round(top)],
  3103. ["line", Math.round(left + width), Math.round(top)],
  3104. ["line", Math.round(left + width), Math.round(height + top)],
  3105. ["line", Math.round(left), Math.round(height + top)]
  3106. ];
  3107. this.clip([shape], function() {
  3108. this.renderBackgroundRepeat(imageContainer, backgroundPosition, size, bounds, borderData[3], borderData[0]);
  3109. }, this);
  3110. };
  3111.  
  3112. CanvasRenderer.prototype.renderBackgroundRepeat = function(imageContainer, backgroundPosition, size, bounds, borderLeft, borderTop) {
  3113. var offsetX = Math.round(bounds.left + backgroundPosition.left + borderLeft), offsetY = Math.round(bounds.top + backgroundPosition.top + borderTop);
  3114. this.setFillStyle(this.ctx.createPattern(this.resizeImage(imageContainer, size), "repeat"));
  3115. this.ctx.translate(offsetX, offsetY);
  3116. this.ctx.fill();
  3117. this.ctx.translate(-offsetX, -offsetY);
  3118. };
  3119.  
  3120. CanvasRenderer.prototype.renderBackgroundGradient = function(gradientImage, bounds) {
  3121. if (gradientImage instanceof LinearGradientContainer) {
  3122. var gradient = this.ctx.createLinearGradient(
  3123. bounds.left + bounds.width * gradientImage.x0,
  3124. bounds.top + bounds.height * gradientImage.y0,
  3125. bounds.left + bounds.width * gradientImage.x1,
  3126. bounds.top + bounds.height * gradientImage.y1);
  3127. gradientImage.colorStops.forEach(function(colorStop) {
  3128. gradient.addColorStop(colorStop.stop, colorStop.color.toString());
  3129. });
  3130. this.rectangle(bounds.left, bounds.top, bounds.width, bounds.height, gradient);
  3131. }
  3132. };
  3133.  
  3134. CanvasRenderer.prototype.resizeImage = function(imageContainer, size) {
  3135. var image = imageContainer.image;
  3136. if(image.width === size.width && image.height === size.height) {
  3137. return image;
  3138. }
  3139.  
  3140. var ctx, canvas = document.createElement('canvas');
  3141. canvas.width = size.width;
  3142. canvas.height = size.height;
  3143. ctx = canvas.getContext("2d");
  3144. ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, size.width, size.height );
  3145. return canvas;
  3146. };
  3147.  
  3148. function hasEntries(array) {
  3149. return array.length > 0;
  3150. }
  3151.  
  3152. module.exports = CanvasRenderer;
  3153.  
  3154. },{"../lineargradientcontainer":12,"../log":13,"../renderer":19}],21:[function(require,module,exports){
  3155. var NodeContainer = require('./nodecontainer');
  3156.  
  3157. function StackingContext(hasOwnStacking, opacity, element, parent) {
  3158. NodeContainer.call(this, element, parent);
  3159. this.ownStacking = hasOwnStacking;
  3160. this.contexts = [];
  3161. this.children = [];
  3162. this.opacity = (this.parent ? this.parent.stack.opacity : 1) * opacity;
  3163. }
  3164.  
  3165. StackingContext.prototype = Object.create(NodeContainer.prototype);
  3166.  
  3167. StackingContext.prototype.getParentStack = function(context) {
  3168. var parentStack = (this.parent) ? this.parent.stack : null;
  3169. return parentStack ? (parentStack.ownStacking ? parentStack : parentStack.getParentStack(context)) : context.stack;
  3170. };
  3171.  
  3172. module.exports = StackingContext;
  3173.  
  3174. },{"./nodecontainer":14}],22:[function(require,module,exports){
  3175. function Support(document) {
  3176. this.rangeBounds = this.testRangeBounds(document);
  3177. this.cors = this.testCORS();
  3178. this.svg = this.testSVG();
  3179. }
  3180.  
  3181. Support.prototype.testRangeBounds = function(document) {
  3182. var range, testElement, rangeBounds, rangeHeight, support = false;
  3183.  
  3184. if (document.createRange) {
  3185. range = document.createRange();
  3186. if (range.getBoundingClientRect) {
  3187. testElement = document.createElement('boundtest');
  3188. testElement.style.height = "123px";
  3189. testElement.style.display = "block";
  3190. document.body.appendChild(testElement);
  3191.  
  3192. range.selectNode(testElement);
  3193. rangeBounds = range.getBoundingClientRect();
  3194. rangeHeight = rangeBounds.height;
  3195.  
  3196. if (rangeHeight === 123) {
  3197. support = true;
  3198. }
  3199. document.body.removeChild(testElement);
  3200. }
  3201. }
  3202.  
  3203. return support;
  3204. };
  3205.  
  3206. Support.prototype.testCORS = function() {
  3207. return typeof((new Image()).crossOrigin) !== "undefined";
  3208. };
  3209.  
  3210. Support.prototype.testSVG = function() {
  3211. var img = new Image();
  3212. var canvas = document.createElement("canvas");
  3213. var ctx = canvas.getContext("2d");
  3214. img.src = "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'></svg>";
  3215.  
  3216. try {
  3217. ctx.drawImage(img, 0, 0);
  3218. canvas.toDataURL();
  3219. } catch(e) {
  3220. return false;
  3221. }
  3222. return true;
  3223. };
  3224.  
  3225. module.exports = Support;
  3226.  
  3227. },{}],23:[function(require,module,exports){
  3228. var XHR = require('./xhr');
  3229. var decode64 = require('./utils').decode64;
  3230.  
  3231. function SVGContainer(src) {
  3232. this.src = src;
  3233. this.image = null;
  3234. var self = this;
  3235.  
  3236. this.promise = this.hasFabric().then(function() {
  3237. return (self.isInline(src) ? Promise.resolve(self.inlineFormatting(src)) : XHR(src));
  3238. }).then(function(svg) {
  3239. return new Promise(function(resolve) {
  3240. window.html2canvas.svg.fabric.loadSVGFromString(svg, self.createCanvas.call(self, resolve));
  3241. });
  3242. });
  3243. }
  3244.  
  3245. SVGContainer.prototype.hasFabric = function() {
  3246. return !window.html2canvas.svg || !window.html2canvas.svg.fabric ? Promise.reject(new Error("html2canvas.svg.js is not loaded, cannot render svg")) : Promise.resolve();
  3247. };
  3248.  
  3249. SVGContainer.prototype.inlineFormatting = function(src) {
  3250. return (/^data:image\/svg\+xml;base64,/.test(src)) ? this.decode64(this.removeContentType(src)) : this.removeContentType(src);
  3251. };
  3252.  
  3253. SVGContainer.prototype.removeContentType = function(src) {
  3254. return src.replace(/^data:image\/svg\+xml(;base64)?,/,'');
  3255. };
  3256.  
  3257. SVGContainer.prototype.isInline = function(src) {
  3258. return (/^data:image\/svg\+xml/i.test(src));
  3259. };
  3260.  
  3261. SVGContainer.prototype.createCanvas = function(resolve) {
  3262. var self = this;
  3263. return function (objects, options) {
  3264. var canvas = new window.html2canvas.svg.fabric.StaticCanvas('c');
  3265. self.image = canvas.lowerCanvasEl;
  3266. canvas
  3267. .setWidth(options.width)
  3268. .setHeight(options.height)
  3269. .add(window.html2canvas.svg.fabric.util.groupSVGElements(objects, options))
  3270. .renderAll();
  3271. resolve(canvas.lowerCanvasEl);
  3272. };
  3273. };
  3274.  
  3275. SVGContainer.prototype.decode64 = function(str) {
  3276. return (typeof(window.atob) === "function") ? window.atob(str) : decode64(str);
  3277. };
  3278.  
  3279. module.exports = SVGContainer;
  3280.  
  3281. },{"./utils":26,"./xhr":28}],24:[function(require,module,exports){
  3282. var SVGContainer = require('./svgcontainer');
  3283.  
  3284. function SVGNodeContainer(node, _native) {
  3285. this.src = node;
  3286. this.image = null;
  3287. var self = this;
  3288.  
  3289. this.promise = _native ? new Promise(function(resolve, reject) {
  3290. self.image = new Image();
  3291. self.image.onload = resolve;
  3292. self.image.onerror = reject;
  3293. self.image.src = "data:image/svg+xml," + (new XMLSerializer()).serializeToString(node);
  3294. if (self.image.complete === true) {
  3295. resolve(self.image);
  3296. }
  3297. }) : this.hasFabric().then(function() {
  3298. return new Promise(function(resolve) {
  3299. window.html2canvas.svg.fabric.parseSVGDocument(node, self.createCanvas.call(self, resolve));
  3300. });
  3301. });
  3302. }
  3303.  
  3304. SVGNodeContainer.prototype = Object.create(SVGContainer.prototype);
  3305.  
  3306. module.exports = SVGNodeContainer;
  3307.  
  3308. },{"./svgcontainer":23}],25:[function(require,module,exports){
  3309. var NodeContainer = require('./nodecontainer');
  3310.  
  3311. function TextContainer(node, parent) {
  3312. NodeContainer.call(this, node, parent);
  3313. }
  3314.  
  3315. TextContainer.prototype = Object.create(NodeContainer.prototype);
  3316.  
  3317. TextContainer.prototype.applyTextTransform = function() {
  3318. this.node.data = this.transform(this.parent.css("textTransform"));
  3319. };
  3320.  
  3321. TextContainer.prototype.transform = function(transform) {
  3322. var text = this.node.data;
  3323. switch(transform){
  3324. case "lowercase":
  3325. return text.toLowerCase();
  3326. case "capitalize":
  3327. return text.replace(/(^|\s|:|-|\(|\))([a-z])/g, capitalize);
  3328. case "uppercase":
  3329. return text.toUpperCase();
  3330. default:
  3331. return text;
  3332. }
  3333. };
  3334.  
  3335. function capitalize(m, p1, p2) {
  3336. if (m.length > 0) {
  3337. return p1 + p2.toUpperCase();
  3338. }
  3339. }
  3340.  
  3341. module.exports = TextContainer;
  3342.  
  3343. },{"./nodecontainer":14}],26:[function(require,module,exports){
  3344. exports.smallImage = function smallImage() {
  3345. return "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
  3346. };
  3347.  
  3348. exports.bind = function(callback, context) {
  3349. return function() {
  3350. return callback.apply(context, arguments);
  3351. };
  3352. };
  3353.  
  3354. /*
  3355. * base64-arraybuffer
  3356. * https://github.com/niklasvh/base64-arraybuffer
  3357. *
  3358. * Copyright (c) 2012 Niklas von Hertzen
  3359. * Licensed under the MIT license.
  3360. */
  3361.  
  3362. exports.decode64 = function(base64) {
  3363. var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  3364. var len = base64.length, i, encoded1, encoded2, encoded3, encoded4, byte1, byte2, byte3;
  3365.  
  3366. var output = "";
  3367.  
  3368. for (i = 0; i < len; i+=4) {
  3369. encoded1 = chars.indexOf(base64[i]);
  3370. encoded2 = chars.indexOf(base64[i+1]);
  3371. encoded3 = chars.indexOf(base64[i+2]);
  3372. encoded4 = chars.indexOf(base64[i+3]);
  3373.  
  3374. byte1 = (encoded1 << 2) | (encoded2 >> 4);
  3375. byte2 = ((encoded2 & 15) << 4) | (encoded3 >> 2);
  3376. byte3 = ((encoded3 & 3) << 6) | encoded4;
  3377. if (encoded3 === 64) {
  3378. output += String.fromCharCode(byte1);
  3379. } else if (encoded4 === 64 || encoded4 === -1) {
  3380. output += String.fromCharCode(byte1, byte2);
  3381. } else{
  3382. output += String.fromCharCode(byte1, byte2, byte3);
  3383. }
  3384. }
  3385.  
  3386. return output;
  3387. };
  3388.  
  3389. exports.getBounds = function(node) {
  3390. if (node.getBoundingClientRect) {
  3391. var clientRect = node.getBoundingClientRect();
  3392. var width = node.offsetWidth == null ? clientRect.width : node.offsetWidth;
  3393. return {
  3394. top: clientRect.top,
  3395. bottom: clientRect.bottom || (clientRect.top + clientRect.height),
  3396. right: clientRect.left + width,
  3397. left: clientRect.left,
  3398. width: width,
  3399. height: node.offsetHeight == null ? clientRect.height : node.offsetHeight
  3400. };
  3401. }
  3402. return {};
  3403. };
  3404.  
  3405. exports.offsetBounds = function(node) {
  3406. var parent = node.offsetParent ? exports.offsetBounds(node.offsetParent) : {top: 0, left: 0};
  3407.  
  3408. return {
  3409. top: node.offsetTop + parent.top,
  3410. bottom: node.offsetTop + node.offsetHeight + parent.top,
  3411. right: node.offsetLeft + parent.left + node.offsetWidth,
  3412. left: node.offsetLeft + parent.left,
  3413. width: node.offsetWidth,
  3414. height: node.offsetHeight
  3415. };
  3416. };
  3417.  
  3418. exports.parseBackgrounds = function(backgroundImage) {
  3419. var whitespace = ' \r\n\t',
  3420. method, definition, prefix, prefix_i, block, results = [],
  3421. mode = 0, numParen = 0, quote, args;
  3422. var appendResult = function() {
  3423. if(method) {
  3424. if (definition.substr(0, 1) === '"') {
  3425. definition = definition.substr(1, definition.length - 2);
  3426. }
  3427. if (definition) {
  3428. args.push(definition);
  3429. }
  3430. if (method.substr(0, 1) === '-' && (prefix_i = method.indexOf('-', 1 ) + 1) > 0) {
  3431. prefix = method.substr(0, prefix_i);
  3432. method = method.substr(prefix_i);
  3433. }
  3434. results.push({
  3435. prefix: prefix,
  3436. method: method.toLowerCase(),
  3437. value: block,
  3438. args: args,
  3439. image: null
  3440. });
  3441. }
  3442. args = [];
  3443. method = prefix = definition = block = '';
  3444. };
  3445. args = [];
  3446. method = prefix = definition = block = '';
  3447. backgroundImage.split("").forEach(function(c) {
  3448. if (mode === 0 && whitespace.indexOf(c) > -1) {
  3449. return;
  3450. }
  3451. switch(c) {
  3452. case '"':
  3453. if(!quote) {
  3454. quote = c;
  3455. } else if(quote === c) {
  3456. quote = null;
  3457. }
  3458. break;
  3459. case '(':
  3460. if(quote) {
  3461. break;
  3462. } else if(mode === 0) {
  3463. mode = 1;
  3464. block += c;
  3465. return;
  3466. } else {
  3467. numParen++;
  3468. }
  3469. break;
  3470. case ')':
  3471. if (quote) {
  3472. break;
  3473. } else if(mode === 1) {
  3474. if(numParen === 0) {
  3475. mode = 0;
  3476. block += c;
  3477. appendResult();
  3478. return;
  3479. } else {
  3480. numParen--;
  3481. }
  3482. }
  3483. break;
  3484.  
  3485. case ',':
  3486. if (quote) {
  3487. break;
  3488. } else if(mode === 0) {
  3489. appendResult();
  3490. return;
  3491. } else if (mode === 1) {
  3492. if (numParen === 0 && !method.match(/^url$/i)) {
  3493. args.push(definition);
  3494. definition = '';
  3495. block += c;
  3496. return;
  3497. }
  3498. }
  3499. break;
  3500. }
  3501.  
  3502. block += c;
  3503. if (mode === 0) {
  3504. method += c;
  3505. } else {
  3506. definition += c;
  3507. }
  3508. });
  3509.  
  3510. appendResult();
  3511. return results;
  3512. };
  3513.  
  3514. },{}],27:[function(require,module,exports){
  3515. var GradientContainer = require('./gradientcontainer');
  3516.  
  3517. function WebkitGradientContainer(imageData) {
  3518. GradientContainer.apply(this, arguments);
  3519. this.type = imageData.args[0] === "linear" ? GradientContainer.TYPES.LINEAR : GradientContainer.TYPES.RADIAL;
  3520. }
  3521.  
  3522. WebkitGradientContainer.prototype = Object.create(GradientContainer.prototype);
  3523.  
  3524. module.exports = WebkitGradientContainer;
  3525.  
  3526. },{"./gradientcontainer":9}],28:[function(require,module,exports){
  3527. function XHR(url) {
  3528. return new Promise(function(resolve, reject) {
  3529. var xhr = new XMLHttpRequest();
  3530. xhr.open('GET', url);
  3531.  
  3532. xhr.onload = function() {
  3533. if (xhr.status === 200) {
  3534. resolve(xhr.responseText);
  3535. } else {
  3536. reject(new Error(xhr.statusText));
  3537. }
  3538. };
  3539.  
  3540. xhr.onerror = function() {
  3541. reject(new Error("Network Error"));
  3542. };
  3543.  
  3544. xhr.send();
  3545. });
  3546. }
  3547.  
  3548. module.exports = XHR;
  3549.  
  3550. },{}]},{},[4])(4)
  3551. });