text: let _cloneNode;
  let _createElement;
  let _isCanvasBlank;
  let downloadPdf;
  
  const domToImage = require('dom-to-image');
  const jsPDF = require('jspdf');
  
  _cloneNode = (node, javascriptEnabled) => {
    let child;
    let clone;
    clone = node.nodeType === 3 ? document.createTextNode(node.nodeValue) : node.cloneNode(false);
    child = node.firstChild;
    while (child) {
      if (javascriptEnabled === true || child.nodeType !== 1 || child.nodeName !== 'SCRIPT') {
        clone.appendChild(_cloneNode(child, javascriptEnabled));
      }
      child = child.nextSibling;
    }
    if (node.nodeType === 1) {
      if (node.nodeName === 'CANVAS') {
        clone.width = node.width;
        clone.height = node.height;
        clone.getContext('2d').drawImage(node, 0, 0);
      } else if (node.nodeName === 'TEXTAREA' || node.nodeName === 'SELECT') {
        clone.value = node.value;
      }
      clone.addEventListener('load', (() => {
        clone.scrollTop = node.scrollTop;
        clone.scrollLeft = node.scrollLeft;
      }), true);
    }
    return clone;
  };
  
  _createElement = (tagName, {className, innerHTML, style}) => {
    let el;
    let i;
    let key;
    let scripts;
    el = document.createElement(tagName);
    if (className) {
      el.className = className;
    }
    if (innerHTML) {
      el.innerHTML = innerHTML;
      scripts = el.getElementsByTagName('script');
      i = scripts.length;
      while (i-- > 0) {
        scripts[i].parentNode.removeChild(scripts[i]);
      }
    }
    for (key in style) {
      el.style[key] = style[key];
    }
    return el;
  };
  
  _isCanvasBlank = canvas => {
    let blank;
    let ctx;
    blank = document.createElement('canvas');
    blank.width = canvas.width;
    blank.height = canvas.height;
    ctx = blank.getContext('2d');
    ctx.fillStyle = '#FFFFFF';
    ctx.fillRect(0, 0, blank.width, blank.height);
    return canvas.toDataURL() === blank.toDataURL();
  };
  
  downloadPdf = (dom, options, cb) => {
    const a4Height = 595.28;
    const a4Width = 841.89;
    let overrideWidth;
    let container;
    let containerCSS;
    let containerWidth;
    let elements;
    let excludeClassNames;
    let filename;
    let filterFn;
    let innerRatio;
    let overlay;
    let overlayCSS;
    let pageHeightPx;
    let proxyUrl;
    let compression = 'NONE';
  
    ({filename, excludeClassNames = [], overrideWidth, proxyUrl, compression} = options);
  
    overlayCSS = {
      position: 'fixed',
      zIndex: 1000,
      opacity: 0,
      left: 0,
      right: 0,
      bottom: 0,
      top: 0,
      backgroundColor: 'rgba(0,0,0,0.8)'
    };
    if (overrideWidth) {
      overlayCSS.width = `${overrideWidth}px`;
    }
    containerCSS = {
      position: 'absolute',
      left: 0,
      right: 0,
      top: 0,
      height: 'auto',
      margin: 'auto',
      overflow: 'auto',
      backgroundColor: 'white'
    };
    overlay = _createElement('div', {
      style: overlayCSS
    });
    container = _createElement('div', {
      style: containerCSS
    });
    container.appendChild(_cloneNode(dom));
    overlay.appendChild(container);
    document.body.appendChild(overlay);
    innerRatio = a4Height / a4Width;
    containerWidth = overrideWidth || container.getBoundingClientRect().width;
    pageHeightPx = Math.floor(containerWidth * innerRatio);
    elements = container.querySelectorAll('*');
    Array.prototype.forEach.call(elements, el => {
      let clientRect;
      let endPage;
      let nPages;
      let pad;
      let rules;
      let startPage;
      rules = {
        before: false,
        after: false,
        avoid: true
      };
      clientRect = el.getBoundingClientRect();
      if (rules.avoid && !rules.before) {
        startPage = Math.floor(clientRect.top / pageHeightPx);
        endPage = Math.floor(clientRect.bottom / pageHeightPx);
        nPages = Math.abs(clientRect.bottom - clientRect.top) / pageHeightPx;
        // Turn on rules.before if the el is broken and is at most one page long.
        if (endPage !== startPage && nPages <= 1) {
          rules.before = true;
        }
        // Before: Create a padding div to push the element to the next page.
        if (rules.before) {
          pad = _createElement('div', {
            style: {
              display: 'block',
              height: `${pageHeightPx - clientRect.top % pageHeightPx}px`
            }
          });
          return el.parentNode.insertBefore(pad, el);
        }
      }
    });
  
    // Remove unnecessary elements from result pdf
    filterFn = ({classList, tagName}) => {
      let cName;
      let j;
      let len;
      let ref;
      let ref1;
      if (classList) {
        for (j = 0, len = excludeClassNames.length; j < len; j++) {
          cName = excludeClassNames[j];
          if (Array.prototype.indexOf.call(classList, cName) >= 0) {
            return false;
          }
        }
      }
      return (ref = (ref1 = tagName) != null ? ref1.toLowerCase() : void 0) !== 'button' && ref !== 'input' && ref !== 'select';
    };
  
    return domToImage.toCanvas(container, {
      filter: filterFn,
      proxy: proxyUrl
    }).then(canvas => {
      let h;
      let imgData;
      let nPages;
      let page;
      let pageCanvas;
      let pageCtx;
      let pageHeight;
      let pdf;
      let pxFullHeight;
      let w;
      let imgProps;
      let pdfW;
      let pdfH;
      // Remove overlay
      document.body.removeChild(overlay);
      // Initialize the PDF.
      pdf = new jsPDF('p', 'pt', 'a4');
      // Calculate the number of pages.
      pxFullHeight = canvas.height;
      nPages = Math.ceil(pxFullHeight / pageHeightPx);
      // Define pageHeight separately so it can be trimmed on the final page.
      pageHeight = pdfH;
      pageCanvas = document.createElement('canvas');
      pageCtx = pageCanvas.getContext('2d');
      pageCanvas.width = canvas.width;
      pageCanvas.height = pageHeightPx;
      page = 0;
      while (page < nPages) {
        if (page === nPages - 1 && pxFullHeight % pageHeightPx !== 0) {
          pageCanvas.height = pxFullHeight % pageHeightPx;
          pageHeight = pageCanvas.height * pdfW / pageCanvas.width;
        }
        w = pageCanvas.width;
        h = pageCanvas.height;
        pageCtx.fillStyle = 'white';
        pageCtx.fillRect(0, 0, w, h);
        pageCtx.drawImage(canvas, 0, page * pageHeightPx, w, h, 0, 0, w, h);
        // Don't create blank pages
        if (_isCanvasBlank(pageCanvas)) {
          ++page;
          continue;
        }
        imgData = pageCanvas.toDataURL('image/PNG');
        imgProps = pdf.getImageProperties(imgData);
        pdfW = pdf.internal.pageSize.getWidth();
        pdfH = (imgProps.height * pdfW) / imgProps.width;
        // Add the page to the PDF.
        if (page) {
          pdf.addPage([pdfW, pdfH]);
        }
        pdf.addImage(imgData, 'PNG', 0, 0, pdfW, pdfH, undefined, compression);
        ++page;
      }
      if (typeof cb === "function") {
        cb();
      }
      return pdf.save(filename);
    }).catch(error => {
      // Remove overlay
      document.body.removeChild(overlay);
      if (typeof cb === "function") {
        cb();
      }
      return console.error(error);
    });
  };
  
  module.exports = downloadPdf;
  