ts-dom-to-image
Version:
7 lines (6 loc) • 8.79 kB
JavaScript
/*!
* ts-dom-to-image v1.0.28
* (c) 2022-2022 hank.online@foxmail.com
* Released under the MIT License.
*/
;function t(t){const{proxy:e=null,httpTimeout:o=3e4,cacheBust:n=!1}=this?.options||{proxy:null,httpTimeout:3e4,cacheBust:!1};return n&&(t+=(/\?/.test(t)?"&":"?")+(new Date).getTime()),new Promise(((n,s)=>{const r=i()?"blob":"text",a=new XMLHttpRequest;if(a.onload=()=>{if(200===a.status)if("text"===r)n(a.response);else{const t=new FileReader;t.onload=()=>n(t.result),t.onerror=t=>s(t),t.readAsDataURL(a.response)}else s(`Failed to proxy resource ${t} with status code ${a.status}`)},a.onerror=s,e){const o=e.indexOf("?")>-1?"&":"?";a.open("GET",`${e}${o}url=${encodeURIComponent(t)}&responseType=${r}`)}else a.open("GET",t);"text"!==r&&a instanceof XMLHttpRequest&&(a.responseType=r),o&&(a.timeout=o,a.ontimeout=()=>s(`Timed out (${o}ms) proxying ${t}`)),a.send()}))}var e;!function(t){t.WOFF="application/font-woff",t.TTF="application/font-truetype",t.EOT="image/eotapplication/vnd.ms-fontobject",t.GIF="image/gif",t.TIFF="image/tiff",t.SVG="image/svg+xml",t.JPEG="image/jpeg",t.JPG="image/jpeg",t.PNG="image/png",t.WEBP="image/webp"}(e||(e={}));const o=/^data:(image|font)\/.*/i,n=/^data:image\/.*/i,s=/^data:image\/.*;base64,/i,i=()=>"string"==typeof(new XMLHttpRequest).responseType,r=/url\(['"]?([^'"]+?)['"]?\)/g,a=(t,e)=>{const o=window.getComputedStyle(t).getPropertyValue(e);return parseFloat(o.replace("px",""))},c=t=>{const e=a(t,"border-left-width"),o=a(t,"border-right-width");return t.scrollWidth+e+o},l=t=>{const e=a(t,"border-top-width"),o=a(t,"border-bottom-width");return t.scrollHeight+e+o},h=t=>-1!==t.search(r),p=t=>{const e=[];let n;for(;null!==(n=r.exec(t));)e.push(n[1]);return e.filter((t=>{return e=t,!o.test(e);var e}))};function u(e,o){if(!h(e))return Promise.resolve(e);const n=p(e);let s=Promise.resolve(e);return n.forEach((e=>{s=s.then((async n=>{let s;return e=o?((t,e)=>{const o=document.implementation.createHTMLDocument(),n=o.createElement("base");o.head.appendChild(n);const s=o.createElement("a");return o.body.appendChild(s),n.href=e,s.href=t,s.href})(e,o):e,this._cache.has(e)?s=this._cache.get(e):(s=await t.call(this,e),this._cache.set(e,s)),n.replace((t=>new RegExp("(url\\(['\"]?)("+t.replace(/([.*+?^${}()|\[\]\/\\])/g,"\\$1")+")(['\"]?\\))","g"))(e),"$1"+s+"$3")}))})),s}class d{_cache={};set(t,e){this.has(t)||(this._cache[t]=e)}get(t){return this._cache[t]}has(t){return void 0!==this._cache[t]}keys(){return Promise.resolve(Object.keys(this._cache))}}const g=(t,e)=>{if(t.cssText)e.cssText=t.cssText,"100%"===t.getPropertyValue("-webkit-background-size")&&(()=>{const t=navigator.userAgent;return{isSafari:/Safari/.test(navigator.userAgent)&&!/Chrome/.test(t),isIos:/(iPhone|iPad|iPod|iOS)/i.test(t)}})().isSafari&&e.removeProperty("-webkit-background-size");else for(const o of t)t.getPropertyValue(o)&&"-webkit-background-size"!==o&&e.setProperty(o,t.getPropertyValue(o),t.getPropertyPriority(o))},m=(t,e)=>{if(!(e instanceof Element))return e;return Promise.resolve().then((()=>g(window.getComputedStyle(t),e.style))).then((()=>[":before",":after"].forEach((o=>function(o){const n=window.getComputedStyle(t,o),s=n.getPropertyValue("content");if(""===s||"none"===s)return;const i=(()=>{let t=0;return`u0000${`0000${(Math.random()*Math.pow(36,4)<<0).toString(36)}`.slice(-4)}${t++}`})();e.className=e.className+" "+i;const r=document.createElement("style");r.appendChild(function(t,e,o){const n=`.${t}:${e}`,s=o.getPropertyValue("content");let i="";if(o.cssText)i=`${o.cssText} content: ${s};`;else for(const t of o)i+=`${t}:${o.getPropertyValue(t)}${o.getPropertyPriority(t)?" !important":""};`;return document.createTextNode(`${n}{${i}}`)}(i,o,n)),e.appendChild(r)}(o))))).then((()=>v(e,t))).then((()=>T(e))).then((()=>e))};async function f(t){if(t.style){const e=t.style.getPropertyValue("background");if(!e)return t;const o=await u.call(this,e);o&&t.style.setProperty("background",o,t.style.getPropertyPriority("background"))}}function y(t){return new Promise(((e,o)=>{const n=new Image;var i;i=t,(s.test(i)||this.options.useCORS)&&(n.crossOrigin="anonymous"),n.onload=()=>e(n),n.onerror=o,n.src=t,n.alt="",!0===n.complete&&setTimeout((()=>e(n)),500),this.options.httpTimeout&&setTimeout((()=>o(`Timed out (${this.options.httpTimeout}ms) loading image`)),this.options.httpTimeout)}))}async function w(t){const e=this;if(t instanceof HTMLImageElement)return await b.call(e,t);await f.call(this,t);const o=Array.prototype.slice.call(t.childNodes).filter((t=>1===t.nodeType));return await Promise.all(o.map((t=>w.call(e,t)))),t}async function b(e){if(o=e.src,!n.test(o)){const o=await t.call(this,e.src);return this._cache.set(e.src,o),new Promise((function(t,n){e.onload=()=>t(e),e.onerror=n,e.src=o}))}var o;return e}async function P(t,e=!1){const{filter:o}=this.options;if(!e&&o&&!o(t))return;if(8===t.nodeType)return;const n=t.childNodes,s=t instanceof HTMLCanvasElement?await y.call(this,t.toDataURL()):t.cloneNode(!1);if(m(t,s),0===n.length)return s;for(const t of n)P.call(this,t,o).then((t=>{t&&s.appendChild(t)}));return s}const v=(t,e)=>{e instanceof HTMLTextAreaElement&&(t.innerHTML=e.value),e instanceof HTMLInputElement&&t.setAttribute("value",e.value)},T=t=>{t instanceof SVGElement&&(t.setAttribute("xmlns","http://www.w3.org/2000/svg"),t instanceof SVGRectElement&&["width","height"].forEach((e=>{const o=t.getAttribute(e);o&&t.style.setProperty(e,o)})))};function x(t){const e=C.call(this,document.styleSheets);return Promise.all(e.map((t=>t.resolve()))).then((t=>t.join("\n"))).then((e=>{const o=document.createElement("style");return t.appendChild(o),o.appendChild(document.createTextNode(e)),t}))}function C(t){const e=[],o=[];for(const e of t)for(const t of e.cssRules)5===t.type&&o.push(t);const n=t=>({resolve:()=>u.call(this,t.cssText,t.parentStyleSheet?.href||void 0),src:()=>t.style.getPropertyValue("src")});try{for(const t of o)(t.type===CSSRule.FONT_FACE_RULE||h(t.style.getPropertyValue("src")))&&e.push(n(t))}catch(t){}return e}module.exports=class{options;_cache;constructor(t){const e={quality:1,cacheBust:!1,proxy:null,useCORS:!1,httpTimeout:3e4,scale:window.devicePixelRatio,iosFix:!0};this.options={...e,...t},this._cache=new d}toSvg(){return this.inlineBase64Svg(!0)}toPng(){return this.drawCanvas().then((t=>t.toDataURL(e.PNG,this.options.quality)))}toJpg(){return this.drawCanvas().then((t=>t.toDataURL(e.JPG,this.options.quality)))}toCanvas(){return this.drawCanvas().then((t=>t))}toBlob(){return this.drawCanvas().then((t=>t.toBlob?new Promise((function(e){t.toBlob(e)})):(t=>new Promise((e=>{const o=window.atob(t?.toDataURL().split(",")[1]),n=o.length,s=new Uint8Array(n);for(let t=0;t<n;t++)s[t]=o.charCodeAt(t);e(new Blob([s],{type:"image/png"}))})))(t)))}toPixelData(){return this.drawCanvas().then((t=>t.getContext("2d")?.getImageData(0,0,c(this.options.targetNode),l(this.options.targetNode)).data))}async drawCanvas(){const t=await this.inlineBase64Svg(),e=await y.call(this,t),o=/(iPhone|iPad|iPod|iOS|AppleWebKit)/i.test(navigator.userAgent);var n;this.options.beforeDrawCanvasCallback instanceof Function&&this.options.beforeDrawCanvasCallback({svg:t,imageEle:e,isApple:o}),this.options.iosFix&&o&&(e.style.cssText="position:absolute;top:0;left:0;opacity: 0.01;z-index: -1;",document.body.appendChild(e),await(n=2e3,new Promise((t=>setTimeout(t,n)))));const s=document.createElement("canvas");if(s.width=(this.options.width||c(this.options.targetNode))*this.options.scale,s.height=(this.options.height||l(this.options.targetNode))*this.options.scale,this.options.bgColor){const t=s.getContext("2d");t&&(t.fillStyle=this.options.bgColor,t.fillRect(0,0,s.width,s.height),t.drawImage(e,0,0,s.width,s.height))}return this._cache,Promise.resolve(s)}inlineBase64Svg(t=!1){return Promise.resolve().then((()=>P.call(this,this.options.targetNode,!0))).then(x.bind(this)).then(w.bind(this)).then(this.applyOptions.bind(this)).then((e=>(e.setAttribute("style",""),((t,e,o,n)=>{const s=(new XMLSerializer).serializeToString(t),i=`<svg xmlns="http://www.w3.org/2000/svg" width="${e}" height="${o}">\n <foreignObject x="0" y="0" width="100%" height="100%">\n ${n?s:(r=s,r.replace(/#/g,"%23").replace(/\n/g,"%0A"))}\n </foreignObject>\n </svg>`;var r;if(n){return document.createRange().createContextualFragment(i).childNodes[0]}return`data:image/svg+xml;charset=utf-8,${i}`})(e,this.options.width||c(this.options.targetNode),this.options.height||l(this.options.targetNode),t))))}applyOptions(t){this.options.bgColor&&(t.style.backgroundColor=this.options.bgColor),this.options.width&&(t.style.width=this.options.width+"px"),this.options.height&&(t.style.height=this.options.height+"px");const e=this.options.style;for(const o in e)t.style[o]=e[o];return t}};