From bd42c1ce3fcde3d276616057d20befbc32ffab9c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 19 Aug 2025 15:51:21 +0800 Subject: [PATCH 01/10] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20dom2Image?= =?UTF-8?q?=20=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BootstrapBlazor.Extensions.sln | 7 +++++++ .../BootstrapBlazor.Dom2Image.csproj | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj diff --git a/BootstrapBlazor.Extensions.sln b/BootstrapBlazor.Extensions.sln index 7dbcbca8..b0581429 100644 --- a/BootstrapBlazor.Extensions.sln +++ b/BootstrapBlazor.Extensions.sln @@ -202,6 +202,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestOpcDa", "test\UnitT EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Tasks.Dashboard", "src\components\BootstrapBlazor.Tasks.Dashboard\BootstrapBlazor.Tasks.Dashboard.csproj", "{30C57119-C564-401C-AE3A-6203E2733E1A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Dom2Image", "src\components\BootstrapBlazor.Dom2Image\BootstrapBlazor.Dom2Image.csproj", "{B7923D29-96EC-4C9D-8180-8B2F45E1D2F9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -552,6 +554,10 @@ Global {30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU {30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU {30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.Build.0 = Release|Any CPU + {B7923D29-96EC-4C9D-8180-8B2F45E1D2F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7923D29-96EC-4C9D-8180-8B2F45E1D2F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7923D29-96EC-4C9D-8180-8B2F45E1D2F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7923D29-96EC-4C9D-8180-8B2F45E1D2F9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -647,6 +653,7 @@ Global {01007B10-7C3C-4136-83FF-981CA39AD3D4} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2} {835C8BA9-A9CC-4EA0-9002-34A20F8B2E86} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE} {30C57119-C564-401C-AE3A-6203E2733E1A} = {FF1089BE-C704-4374-B629-C57C08E1798F} + {B7923D29-96EC-4C9D-8180-8B2F45E1D2F9} = {FF1089BE-C704-4374-B629-C57C08E1798F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF} diff --git a/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj new file mode 100644 index 00000000..1f1faa6f --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj @@ -0,0 +1,21 @@ + + + + 9.0.0-beta01 + + + + Bootstrap Blazor WebAssembly wasm UI Components Dom Image + Bootstrap UI components extensions of DomToImage use snapDom lib + + + + + + + + + + + + From af388c508695ed0abd4d6c7f0bec545e9a875af5 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 19 Aug 2025 15:54:36 +0800 Subject: [PATCH 02/10] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wwwroot/dom2image.js | 23 + .../wwwroot/lib/snapdom.min.mjs | 3 + .../wwwroot/lib/snapdom.mjs | 1781 +++++++++++++++++ 3 files changed, 1807 insertions(+) create mode 100644 src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js create mode 100644 src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs create mode 100644 src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js new file mode 100644 index 00000000..f357fc08 --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js @@ -0,0 +1,23 @@ +import { snapdom } from './lib/snapdom.min.mjs' + +export async function getUrl(selector, options = {}) { + let data = null; + const el = document.querySelector(selector); + if (el) { + options.embedFonts = true; + const result = await snapdom(el, options); + data = result.url; + } + return data; +} + +export async function getStream(selector, options = {}) { + let data = null; + const el = document.querySelector(selector); + if (el) { + options.embedFonts = true; + const result = await snapdom(el, options); + data = result.toBlob(); + } + return data; +} diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs new file mode 100644 index 00000000..2415087e --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs @@ -0,0 +1,3 @@ +var h={image:new Map,background:new Map,resource:new Map,defaultStyle:new Map,baseStyle:new Map,computedStyle:new WeakMap,font:new Set,snapshot:new WeakMap,snapshotKey:new Map,reset:Wt};function Wt(){h.computedStyle=new WeakMap}var _t=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function yt(){for(let t of _t)wt(t)}function wt(t){if(h.defaultStyle.has(t))return h.defaultStyle.get(t);if(new Set(["script","style","meta","link","noscript","template","defs","symbol","title","metadata","desc"]).has(t)){let a={};return h.defaultStyle.set(t,a),a}let n=document.getElementById("snapdom-sandbox");n||(n=document.createElement("div"),n.id="snapdom-sandbox",n.style.position="absolute",n.style.left="-9999px",n.style.top="-9999px",n.style.width="0",n.style.height="0",n.style.overflow="hidden",document.body.appendChild(n));let i=document.createElement(t);i.style.all="initial",n.appendChild(i);let r=getComputedStyle(i),c={};for(let a of r)c[a]=r.getPropertyValue(a);return n.removeChild(i),h.defaultStyle.set(t,c),c}var jt=new Set(["-webkit-locale"]);function K(t,e,n=!1){let i=[],r=wt(e);for(let[c,a]of Object.entries(t))if(!jt.has(c))if(!n)a&&i.push(`${c}:${a}`);else{let f=r[c];a&&a!==f&&i.push(`${c}:${a}`)}return i.sort().join(";")}function bt(t){let e=new Set;return t.nodeType!==Node.ELEMENT_NODE&&t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(t.tagName&&e.add(t.tagName.toLowerCase()),typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(n=>e.add(n.tagName.toLowerCase())),Array.from(e))}function St(t){let e=new Map;for(let i of t){let r=h.defaultStyle.get(i);if(!r)continue;let c=Object.entries(r).map(([a,f])=>`${a}:${f};`).sort().join("");e.has(c)||e.set(c,[]),e.get(c).push(i)}let n="";for(let[i,r]of e.entries())n+=`${r.join(",")} { ${i} } +`;return n}function xt(t){let e=new Set(t.values()),n=new Map,i=1;for(let r of e)r.trim()&&n.set(r,`c${i++}`);return n}async function O(t,e={}){let n=X(t),i=/^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(t);if(n){let r=G(n);if(h.background.has(r))return e.skipInline?void 0:`url(${h.background.get(r)})`;{let c=await W(r,{useProxy:e.useProxy});return h.background.set(r,c),e.skipInline?void 0:`url("${c}")`}}return t}function q(t,{fast:e=!1}={}){if(e)return t();"requestIdleCallback"in window?requestIdleCallback(t,{timeout:50}):setTimeout(t,1)}function U(t,e=null){if(!(t instanceof Element))return window.getComputedStyle(t,e);let n=h.computedStyle.get(t);if(n||(n=new Map,h.computedStyle.set(t,n)),!n.has(e)){let i=window.getComputedStyle(t,e);n.set(e,i)}return n.get(e)}function Ct(t){let e=t.replace(/^['"]|['"]$/g,"");if(e.startsWith("\\"))try{return String.fromCharCode(parseInt(e.replace("\\",""),16))}catch{return e}return e}function X(t){let e=t.match(/url\((['"]?)(.*?)(\1)\)/);if(!e)return null;let n=e[2].trim();return n.startsWith("#")?null:n}async function V(t,{useProxy:e=""}={}){async function n(i){let r=await fetch(i);if(!r.ok)throw new Error(`[snapdom] Failed to fetch resource: ${i}`);return r}try{return await n(t)}catch(i){if(e&&typeof e=="string"){let r=e.replace(/\/$/,"")+G(t);return n(r)}throw i}}var j=new Map,at=new Map;function W(t,{timeout:e=3e3,useProxy:n="",errorTTL:i=8e3}={}){function r(u){try{return new URL(u,window.location.href).origin===window.location.origin?"use-credentials":"anonymous"}catch{return"anonymous"}}let c=u=>({ok:!0,data:u}),a=u=>({ok:!1,error:u instanceof Error?u:new Error(String(u))});function f(u){try{return fetch(u,{mode:"cors",credentials:r(u)==="use-credentials"?"include":"omit"}).then(g=>g.ok?g.blob().then(b=>new Promise(p=>{let w=new FileReader;w.onloadend=()=>{let S=w.result;typeof S!="string"||!S.startsWith("data:image/")?p(a(new Error("Invalid image data URL"))):p(c(S))},w.onerror=()=>p(a(new Error("FileReader error"))),w.readAsDataURL(b)})):a(new Error("HTTP "+g.status))).catch(g=>a(g))}catch(g){return Promise.resolve(a(g))}}function o(u){return f(u).then(g=>{if(g.ok)return g;if(n&&typeof n=="string"){let b=n.replace(/\/$/,"")+G(u);return f(b).then(p=>p.ok?p:a(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")))}return a(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"))})}let l=Date.now(),s=at.get(t);if(s&&s>l){let u=Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown)."));return u.catch(()=>{}),u}if(j.has(t))return j.get(t);let d=r(t);if(h.image.has(t))return Promise.resolve(h.image.get(t));if(t.startsWith("data:image/"))return h.image.set(t,t),Promise.resolve(t);if(/\.svg(\?.*)?$/i.test(t)){let u=(async()=>{let g=await(async()=>{try{let p=await fetch(t,{mode:"cors",credentials:d==="use-credentials"?"include":"omit"});if(!p.ok)return a(new Error("HTTP "+p.status));let w=await p.text();return c(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(w)}`)}catch(p){return a(p)}})();if(g.ok)return h.image.set(t,g.data),g.data;let b=await o(t);return b.ok?(h.image.set(t,b.data),b.data):(at.set(t,l+i),Promise.reject(b.error))})();return j.set(t,u),u.finally(()=>j.delete(t)),u.catch(()=>{}),u}let m=new Promise((u,g)=>{let b=!1,p=new Image,w=y=>k=>{b||(b=!0,clearTimeout(R),p.onload=p.onerror=null,y(k))},S=y=>{h.image.set(t,y),u(y)},x=y=>{at.set(t,Date.now()+i),g(y)},R=setTimeout(w(()=>{o(t).then(y=>{y.ok?S(y.data):x(new Error("Image load timed out"))})}),e);p.crossOrigin=d,p.onload=w(()=>{Promise.resolve(p.decode()).then(()=>{try{let y=document.createElement("canvas");y.width=p.naturalWidth||p.width,y.height=p.naturalHeight||p.height,y.getContext("2d").drawImage(p,0,0,y.width,y.height),S(y.toDataURL("image/png"))}catch{o(t).then(y=>{y.ok?S(y.data):x(y.error)})}}).catch(()=>{o(t).then(y=>{y.ok?S(y.data):x(y.error)})})}),p.onerror=w(()=>{o(t).then(y=>{y.ok?S(y.data):x(y.error)})}),p.src=t});return j.set(t,m),m.finally(()=>j.delete(t)),m.catch(()=>{}),m}function st(t){let e={};for(let n of t)e[n]=t.getPropertyValue(n);return e}function vt(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function Et(t){if(!t||t==="none")return"";let e=t.replace(/translate[XY]?\([^)]*\)/g,"");return e=e.replace(/matrix\(([^)]+)\)/g,(n,i)=>{let r=i.split(",").map(c=>c.trim());return r.length!==6?`matrix(${i})`:(r[4]="0",r[5]="0",`matrix(${r.join(", ")})`)}),e=e.replace(/matrix3d\(([^)]+)\)/g,(n,i)=>{let r=i.split(",").map(c=>c.trim());return r.length!==16?`matrix3d(${i})`:(r[12]="0",r[13]="0",`matrix3d(${r.join(", ")})`)}),e.trim().replace(/\s{2,}/g," ")}function G(t){if(/%[0-9A-Fa-f]{2}/.test(t))return t;try{return encodeURI(t)}catch{return t}}function H(t){let e=[],n=0,i=0;for(let r=0;rs.localeCompare(d)).map(([s,d])=>`${s}:${d}`).join(";");if(ct.has(f)){n.set(e,ct.get(f));return}let o=t.tagName?.toLowerCase()||"div",l=K(a,o,r);ct.set(f,l),n.set(e,l)}function qt(t,e){try{let n=t.currentSrc||t.src||"";if(!n)return;e.setAttribute("src",n),e.removeAttribute("srcset"),e.removeAttribute("sizes"),e.loading="eager",e.decoding="sync"}catch{}}function J(t,e,n,i,r,c={},a){if(!t)throw new Error("Invalid node");let f=new Set,o=null;if(t.nodeType===Node.TEXT_NODE||t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);if(t.getAttribute("data-capture")==="exclude"){let s=document.createElement("div"),d=t.getBoundingClientRect();return s.style.cssText=`display:inline-block;width:${d.width}px;height:${d.height}px;visibility:hidden;`,s}if(c.exclude&&Array.isArray(c.exclude))for(let s of c.exclude)try{if(t.matches?.(s)){let d=document.createElement("div"),m=t.getBoundingClientRect();return d.style.cssText=`display:inline-block;width:${m.width}px;height:${m.height}px;visibility:hidden;`,d}}catch(d){console.warn(`Invalid selector in exclude option: ${s}`,d)}if(typeof c.filter=="function")try{if(!c.filter(t,a||t)){let s=document.createElement("div"),d=t.getBoundingClientRect();return s.style.cssText=`display:inline-block;width:${d.width}px;height:${d.height}px;visibility:hidden;`,s}}catch(s){console.warn("Error in filter function:",s)}if(t.tagName==="IFRAME"){let s=document.createElement("div");return s.style.cssText=`width:${t.offsetWidth}px;height:${t.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`,s}if(t.getAttribute("data-capture")==="placeholder"){let s=t.cloneNode(!1);i.set(s,t),et(t,s,e,n,r);let d=document.createElement("div");return d.textContent=t.getAttribute("data-placeholder-text")||"",d.style.cssText="color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;",s.appendChild(d),s}if(t.tagName==="CANVAS"){let s=t.toDataURL(),d=document.createElement("img");return d.src=s,d.width=t.width,d.height=t.height,i.set(d,t),et(t,d,e,n,r),d}let l;try{l=t.cloneNode(!1),i.set(l,t),t.tagName==="IMG"&&qt(t,l)}catch(s){throw console.error("[Snapdom] Failed to clone node:",t,s),s}if(t instanceof HTMLTextAreaElement){l.textContent=t.value,l.value=t.value;let s=t.getBoundingClientRect();return l.style.width=`${s.width}px`,l.style.height=`${s.height}px`,l}if(t instanceof HTMLInputElement&&(l.value=t.value,l.setAttribute("value",t.value),t.checked!==void 0&&(l.checked=t.checked,t.checked&&l.setAttribute("checked",""),t.indeterminate&&(l.indeterminate=t.indeterminate))),t instanceof HTMLSelectElement&&(o=t.value),et(t,l,e,n,r),t.shadowRoot)if(Array.from(t.shadowRoot.querySelectorAll("slot")).length>0){for(let d of t.shadowRoot.childNodes)if(d.nodeType===Node.ELEMENT_NODE&&d.tagName==="STYLE"){let m=d.textContent||"";m.trim()&&r&&n.set(d,m)}}else{let d=document.createDocumentFragment();for(let m of t.shadowRoot.childNodes){if(m.nodeType===Node.ELEMENT_NODE&&m.tagName==="STYLE"){let g=m.textContent||"";g.trim()&&r&&n.set(m,g);continue}let u=J(m,e,n,i,r,c,a||t);u&&d.appendChild(u)}l.appendChild(d)}if(t.tagName==="SLOT"){let s=t.assignedNodes?.({flatten:!0})||[],d=s.length>0?s:Array.from(t.childNodes),m=document.createDocumentFragment();for(let u of d){let g=J(u,e,n,i,r,c,a||t);g&&m.appendChild(g)}return m}for(let s of t.childNodes){if(f.has(s))continue;let d=J(s,e,n,i,r,c,a||t);d&&l.appendChild(d)}if(o!==null&&l instanceof HTMLSelectElement){l.value=o;for(let s of l.options)s.value===o?s.setAttribute("selected",""):s.removeAttribute("selected")}return l}var Vt=[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i,/layui/i,/lucide/i],lt=[];function At(t){let e=Array.isArray(t)?t:[t];for(let n of e)n instanceof RegExp?lt.push(n):typeof n=="string"?lt.push(new RegExp(n,"i")):console.warn("[snapdom] Ignored invalid iconFont value:",n)}function L(t){let e=typeof t=="string"?t:"",n=[...Vt,...lt];for(let i of n)if(i instanceof RegExp&&i.test(e))return!0;return!!(/icon/i.test(e)||/glyph/i.test(e)||/symbols/i.test(e)||/feather/i.test(e)||/fontawesome/i.test(e))}async function $t(t,e,n,i=32,r="#000"){e=e.replace(/^['"]+|['"]+$/g,"");let c=window.devicePixelRatio||1;await document.fonts.ready;let a=document.createElement("span");a.textContent=t,a.style.position="absolute",a.style.visibility="hidden",a.style.fontFamily=`"${e}"`,a.style.fontWeight=n||"normal",a.style.fontSize=`${i}px`,a.style.lineHeight="1",a.style.whiteSpace="nowrap",a.style.padding="0",a.style.margin="0",document.body.appendChild(a);let f=a.getBoundingClientRect(),o=Math.ceil(f.width),l=Math.ceil(f.height);document.body.removeChild(a);let s=document.createElement("canvas");s.width=o*c,s.height=l*c;let d=s.getContext("2d");return d.scale(c,c),d.font=n?`${n} ${i}px "${e}"`:`${i}px "${e}"`,d.textAlign="left",d.textBaseline="top",d.fillStyle=r,d.fillText(t,0,0),{dataUrl:s.toDataURL(),width:o,height:l}}function kt(t){return Array.from(document.styleSheets).some(e=>e.href===t)}function Ht(t){return new Promise(e=>{if(kt(t))return e(null);let n=document.createElement("link");n.rel="stylesheet",n.href=t,n.setAttribute("data-snapdom","injected-import"),n.onload=()=>e(n),n.onerror=()=>e(null),document.head.appendChild(n)})}async function nt({preCached:t=!1,localFonts:e=[],useProxy:n=""}={}){if(h.resource.has("fonts-embed-css")){if(t){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=h.resource.get("fonts-embed-css"),document.head.appendChild(o)}return h.resource.get("fonts-embed-css")}let i=new Set;try{for(let o of document.fonts)o.status==="loaded"&&i.add(`${o.family}__${o.weight||"normal"}__${o.style||"normal"}`)}catch{}let r=/@import\s+url\(["']?([^"')]+)["']?\)/g,c=[];for(let o of document.querySelectorAll("style")){let l=o.textContent||"",s=Array.from(l.matchAll(r));for(let d of s){let m=d[1];L(m)||kt(m)||c.push(m)}}await Promise.all(c.map(Ht));let a=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(o=>o.href),f="";for(let o of a)try{let s=await(await V(o.href,{useProxy:n})).text();if(L(o.href)||L(s))continue;let d=/@font-face[^{}]*{[^}]*}/g,m=s;for(let u of s.match(d)||[]){let g=u.match(/font-family:\s*([^;]+);/i);if(!g)continue;let b=g[1].replace(/['"]/g,"").trim(),p=u.match(/font-weight:\s*([^;]+);/i),w=u.match(/font-style:\s*([^;]+);/i),S=p?p[1].trim():"normal",x=w?w[1].trim():"normal",R=`${b}__${S}__${x}`,y=/url\((["']?)([^"')]+)\1\)/g,k=/url\(/i.test(u),C=/local\(/i.test(u);if(!k&&C)continue;if(!i.has(R)){m=m.replace(u,"");continue}let A=u,P=Array.from(u.matchAll(y));for(let N of P){let T=X(N[0]);if(!T)continue;let v=T;if(!v.startsWith("http")&&!v.startsWith("data:")&&(v=new URL(v,o.href).href),!L(v)){if(h.resource.has(v)){h.font.add(v),A=A.replace(N[0],`url(${h.resource.get(v)})`);continue}if(!h.font.has(v))try{let B=await(await V(v,{useProxy:n})).blob(),Y=await new Promise($=>{let M=new FileReader;M.onload=()=>$(M.result),M.readAsDataURL(B)});h.resource.set(v,Y),h.font.add(v),A=A.replace(N[0],`url(${Y})`)}catch{console.warn("[snapdom] Failed to fetch font resource:",v)}}}m=m.replace(u,A)}f+=m+` +`}catch{console.warn("[snapdom] Failed to fetch CSS:",o.href)}for(let o of document.styleSheets)try{if(!o.href||a.every(l=>l.href!==o.href)){for(let l of o.cssRules)if(l.type===CSSRule.FONT_FACE_RULE){let s=l.style.getPropertyValue("src"),d=l.style.getPropertyValue("font-family");if(!s||L(d))continue;let m=l.style.getPropertyValue("font-weight")||"normal",u=l.style.getPropertyValue("font-style")||"normal",g=`${d}__${m}__${u}`,b=/url\((["']?)([^"')]+)\1\)/g,p=/local\((["']?)[^)]+?\1\)/g,w=!!s.match(b),S=!!s.match(p);if(!w&&S){f+=`@font-face{font-family:${d};src:${s};font-style:${u};font-weight:${m};}`;continue}if(!i.has(g))continue;let x=s,R=Array.from(s.matchAll(b));for(let y of R){let k=y[2].trim();if(!k)continue;let C=k;if(!C.startsWith("http")&&!C.startsWith("data:")&&(C=new URL(C,o.href||location.href).href),!L(C)){if(h.resource.has(C)){h.font.add(C),x=x.replace(y[0],`url(${h.resource.get(C)})`);continue}if(!h.font.has(C))try{let P=await(await V(C,{useProxy:n})).blob(),N=await new Promise(T=>{let v=new FileReader;v.onload=()=>T(v.result),v.readAsDataURL(P)});h.resource.set(C,N),h.font.add(C),x=x.replace(y[0],`url(${N})`)}catch{console.warn("[snapdom] Failed to fetch font URL:",C)}}}f+=`@font-face{font-family:${d};src:${x};font-style:${u};font-weight:${m};}`}}}catch(l){console.warn("[snapdom] Cannot access stylesheet",o.href,l)}for(let o of document.fonts)if(o.family&&o.status==="loaded"&&o._snapdomSrc){if(L(o.family))continue;let l=o._snapdomSrc;if(!l.startsWith("data:")){if(h.resource.has(o._snapdomSrc))l=h.resource.get(o._snapdomSrc),h.font.add(o._snapdomSrc);else if(!h.font.has(o._snapdomSrc))try{let d=await(await V(o._snapdomSrc,{useProxy:n})).blob();l=await new Promise(m=>{let u=new FileReader;u.onload=()=>m(u.result),u.readAsDataURL(d)}),h.resource.set(o._snapdomSrc,l),h.font.add(o._snapdomSrc)}catch{console.warn("[snapdom] Failed to fetch dynamic font src:",o._snapdomSrc);continue}}f+=`@font-face{font-family:'${o.family}';src:url(${l});font-style:${o.style||"normal"};font-weight:${o.weight||"normal"};}`}for(let o of e){if(!o||typeof o!="object")continue;let{family:l,src:s,weight:d="normal",style:m="normal"}=o;if(!l||!s)continue;let u=s;if(u.startsWith("data:"))h.resource.set(s,u),h.font.add(s);else try{let b=await(await V(s,{useProxy:n})).blob();u=await new Promise(p=>{let w=new FileReader;w.onload=()=>p(w.result),w.readAsDataURL(b)}),h.resource.set(s,u),h.font.add(s)}catch{console.warn("[snapdom] Failed to load local font:",s);continue}f+=`@font-face{font-family:'${l}';src:url(${u});font-style:${m};font-weight:${d};}`}if(f&&(h.resource.set("fonts-embed-css",f),t)){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=f,document.head.appendChild(o)}return f}async function ft(t,e,n,i,r){if(!(t instanceof Element)||!(e instanceof Element))return;for(let f of["::before","::after","::first-letter"])try{let o=U(t,f);if(!o||typeof o[Symbol.iterator]!="function"||o.content==="none"&&o.backgroundImage==="none"&&o.backgroundColor==="transparent"&&(o.borderStyle==="none"||parseFloat(o.borderWidth)===0)&&(!o.transform||o.transform==="none")&&o.display==="inline")continue;if(f==="::first-letter"){let I=getComputedStyle(t);if(!(o.color!==I.color||o.fontSize!==I.fontSize||o.fontWeight!==I.fontWeight))continue;let D=Array.from(e.childNodes).find(pt=>pt.nodeType===Node.TEXT_NODE&&pt.textContent?.trim().length>0);if(!D)continue;let _=D.textContent,Z=_.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u)?.[0],Ut=_.slice(Z?.length||0);if(!Z||/[\uD800-\uDFFF]/.test(Z))continue;let tt=document.createElement("span");tt.textContent=Z,tt.dataset.snapdomPseudo="::first-letter";let Mt=st(o),Dt=K(Mt,"span",r);n.set(tt,Dt);let gt=document.createTextNode(Ut);e.replaceChild(gt,D),e.insertBefore(tt,gt);continue}let s=o.content,d=/counter\s*\(|counters\s*\(/.test(s)?"- ":Ct(s),m=o.backgroundImage,u=o.backgroundColor,g=o.fontFamily,b=parseInt(o.fontSize)||32,p=parseInt(o.fontWeight)||!1,w=o.color||"#000",S=o.display,x=parseFloat(o.width),R=parseFloat(o.height),y=o.borderStyle,k=parseFloat(o.borderWidth),C=o.transform,A=L(g),P=s!=="none"&&d!=="",N=m&&m!=="none",T=u&&u!=="transparent"&&u!=="rgba(0, 0, 0, 0)",v=S!=="inline"&&(x>0||R>0),z=y&&y!=="none"&&k>0,B=C&&C!=="none";if(!(P||N||T||v||z||B))continue;let $=document.createElement("span");$.dataset.snapdomPseudo=f,$.style.verticalAlign="middle";let M=st(o),rt=K(M,"span",r);if(n.set($,rt),A&&d.length===1){let{dataUrl:I,width:F,height:D}=await $t(d,g,p,b,w),_=document.createElement("img");_.src=I,_.style=`height:${b}px;width:${F/D*b}px;object-fit:contain;`,$.appendChild(_),e.dataset.snapdomHasIcon="true"}else if(d.startsWith("url(")){let I=X(d);if(I?.trim())try{let F=document.createElement("img"),D=await W(G(I),r);F.src=D,F.style=`width:${b}px;height:auto;object-fit:contain;`,$.appendChild(F)}catch(F){console.error(`[snapdom] Error in pseudo ${f} for`,t,F)}}else!A&&P&&($.textContent=d);if(N)try{let I=H(m),F=await Promise.all(I.map(O));$.style.backgroundImage=F.join(", ")}catch(I){console.warn(`[snapdom] Failed to inline background-image for ${f}`,I)}if(T&&($.style.backgroundColor=u),!($.childNodes.length>0||$.textContent?.trim()!==""||N||T||v||z||B))continue;f==="::before"?e.insertBefore($,e.firstChild):e.appendChild($)}catch(o){console.warn(`[snapdom] Failed to capture ${f} for`,t,o)}let c=Array.from(t.children),a=Array.from(e.children).filter(f=>!f.dataset.snapdomPseudo);for(let f=0;f{let o=f.getAttribute("xlink:href")||f.getAttribute("href");o&&o.startsWith("#")&&e.add(o.slice(1))}),!e.size)return;let n=Array.from(document.querySelectorAll("svg > symbol, svg > defs")),i=n.filter(f=>f.tagName.toLowerCase()==="symbol"),r=n.filter(f=>f.tagName.toLowerCase()==="defs"),c=t.querySelector("svg.inline-defs-container");c||(c=document.createElementNS("http://www.w3.org/2000/svg","svg"),c.setAttribute("aria-hidden","true"),c.setAttribute("style","position: absolute; width: 0; height: 0; overflow: hidden;"),c.classList.add("inline-defs-container"),t.insertBefore(c,t.firstChild));let a=new Set;t.querySelectorAll("symbol[id], defs > *[id]").forEach(f=>{a.add(f.id)}),e.forEach(f=>{if(a.has(f))return;let o=i.find(l=>l.id===f);if(o){c.appendChild(o.cloneNode(!0)),a.add(f);return}for(let l of r){let s=l.querySelector(`#${CSS.escape(f)}`);if(s){let d=c.querySelector("defs");d||(d=document.createElementNS("http://www.w3.org/2000/svg","defs"),c.appendChild(d)),d.appendChild(s.cloneNode(!0)),a.add(f);break}}})}async function Nt(t,e=!1,n=!1,i={}){let r=new Map,c=new WeakMap,a=new Map,f,o="";zt(t);try{It(t)}catch(l){console.warn("inlineExternal defs or symbol failed:",l)}try{f=J(t,r,c,a,e,i,t)}catch(l){throw console.warn("deepClone failed:",l),l}try{await ft(t,f,r,c,e,n,i.useProxy)}catch(l){console.warn("inlinePseudoElements failed:",l)}if(await Gt(f),e){let l=xt(r);o=Array.from(l.entries()).map(([s,d])=>`.${d}{${s}}`).join("");for(let[s,d]of r.entries()){if(s.tagName==="STYLE")continue;if(s.getRootNode&&s.getRootNode()instanceof ShadowRoot){s.setAttribute("style",d.replace(/;/g,"; "));continue}let m=l.get(d);m&&s.classList.add(m);let u=s.style?.backgroundImage,g=s.dataset?.snapdomHasIcon;u&&u!=="none"&&(s.style.backgroundImage=u),g&&(s.style.verticalAlign="middle",s.style.display="inline")}}else for(let[l,s]of r.entries())l.tagName!=="STYLE"&&l.setAttribute("style",s.replace(/;/g,"; "));for(let[l,s]of a.entries()){let d=s.scrollLeft,m=s.scrollTop;if((d||m)&&l instanceof HTMLElement){l.style.overflow="hidden",l.style.scrollbarWidth="none",l.style.msOverflowStyle="none";let g=document.createElement("div");for(g.style.transform=`translate(${-d}px, ${-m}px)`,g.style.willChange="transform",g.style.display="inline-block",g.style.width="100%";l.firstChild;)g.appendChild(l.firstChild);l.appendChild(g)}}if(t===a.get(f)){let l=c.get(t)||window.getComputedStyle(t);c.set(t,l);let s=Et(l.transform);f.style.margin="0",f.style.position="static",f.style.top="auto",f.style.left="auto",f.style.right="auto",f.style.bottom="auto",f.style.zIndex="auto",f.style.float="none",f.style.clear="none",f.style.transform=s||""}for(let[l,s]of a.entries())s.tagName==="PRE"&&(l.style.marginTop="0",l.style.marginBlockStart="0");return{clone:f,classCSS:o,styleCache:c}}function zt(t){let e=getComputedStyle(t),n=e.outlineStyle,i=e.outlineWidth,r=e.borderStyle,c=e.borderWidth,a=n!=="none"&&parseFloat(i)>0,f=r==="none"||parseFloat(c)===0;a&&f&&(t.style.border=`${i} solid transparent`)}var dt=new Map;async function Q(t){if(dt.has(t))return dt.get(t);let e=await fetch(t);if(!e.ok)throw new Error(`[SnapDOM] HTTP ${e.status} on blob fetch (${t})`);let n=await e.blob(),i=await new Promise((r,c)=>{let a=new FileReader;a.onloadend=()=>{let f=a.result;typeof f=="string"&&f.startsWith("data:")?r(f):c(new Error("[SnapDOM] Invalid data URL from blob"))},a.onerror=()=>c(new Error("[SnapDOM] FileReader error")),a.readAsDataURL(n)});return dt.set(t,i),i}var Yt=/\bblob:[^)"'\s]+/g;async function Rt(t){if(!t||t.indexOf("blob:")===-1)return t;let e=Array.from(new Set(t.match(Yt)||[]));if(e.length===0)return t;let n=t;for(let i of e)try{let r=await Q(i);n=n.split(i).join(r)}catch{}return n}function ot(t){return typeof t=="string"&&t.startsWith("blob:")}function Kt(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(e=>{let n=e.match(/^(\S+)(\s+.+)?$/);return n?{url:n[1],desc:n[2]||""}:null}).filter(Boolean)}function Xt(t){return t.map(e=>e.desc?`${e.url} ${e.desc.trim()}`:e.url).join(", ")}async function Gt(t){if(!t)return;let e=t.querySelectorAll?t.querySelectorAll("img"):[];for(let a of e)try{let o=a.getAttribute("src")||a.currentSrc||"";if(ot(o)){let s=await Q(o);a.setAttribute("src",s)}let l=a.getAttribute("srcset");if(l&&l.includes("blob:")){let s=Kt(l),d=!1;for(let m of s)if(ot(m.url))try{m.url=await Q(m.url),d=!0}catch{}d&&a.setAttribute("srcset",Xt(s))}}catch{}let n=t.querySelectorAll?t.querySelectorAll("image"):[];for(let a of n)try{let f="http://www.w3.org/1999/xlink",o=a.getAttribute("href")||a.getAttributeNS?.(f,"href");if(ot(o)){let l=await Q(o);a.setAttribute("href",l),a.removeAttributeNS?.(f,"href")}}catch{}let i=t.querySelectorAll?t.querySelectorAll("[style*='blob:']"):[];for(let a of i)try{let f=a.getAttribute("style");if(f&&f.includes("blob:")){let o=await Rt(f);a.setAttribute("style",o)}}catch{}let r=t.querySelectorAll?t.querySelectorAll("style"):[];for(let a of r)try{let f=a.textContent||"";f.includes("blob:")&&(a.textContent=await Rt(f))}catch{}let c=["poster"];for(let a of c){let f=t.querySelectorAll?t.querySelectorAll(`[${a}^='blob:']`):[];for(let o of f)try{let l=o.getAttribute(a);ot(l)&&o.setAttribute(a,await Q(l))}catch{}}}async function Tt(t,e={}){let n=Array.from(t.querySelectorAll("img")),i=async r=>{if(!r.getAttribute("src")){let a=r.currentSrc||r.src||"";a&&r.setAttribute("src",a)}r.removeAttribute("srcset"),r.removeAttribute("sizes");let c=r.src;try{let a=await W(c,{useProxy:e.useProxy});r.src=a,r.width||(r.width=r.naturalWidth||100),r.height||(r.height=r.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${r.width||100}px; height: ${r.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${r.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",r.replaceWith(a)}};for(let r=0;r{let u=o.getPropertyValue("border-image"),g=o.getPropertyValue("border-image-source");return u&&u!=="none"||g&&g!=="none"})();for(let u of c){if(["border-image-slice","border-image-width","border-image-outset","border-image-repeat"].includes(u)&&!l)continue;let g=o.getPropertyValue(u);if(!g||g==="none")continue;let b=H(g),p=await Promise.all(b.map(w=>O(w,i)));p.some(w=>w&&w!=="none"&&!/^url\(undefined/.test(w))&&f.style.setProperty(u,p.join(", "))}let s=o.getPropertyValue("background-color");s&&s!=="transparent"&&s!=="rgba(0, 0, 0, 0)"&&(f.style.backgroundColor=s);let d=Array.from(a.children),m=Array.from(f.children);for(let u=0;u{q(async()=>{await Tt(o,e),p()},{fast:r})}),await new Promise(p=>{q(async()=>{await Ft(t,o,s,e),p()},{fast:r})}),i&&await new Promise(p=>{q(async()=>{d=await nt({localFonts:f,useProxy:a}),p()},{fast:r})}),n){let p=bt(o).sort(),w=p.join(",");h.baseStyle.has(w)?m=h.baseStyle.get(w):await new Promise(S=>{q(()=>{m=St(p),h.baseStyle.set(w,m),S()},{fast:r})})}await new Promise(p=>{q(()=>{let w=t.getBoundingClientRect(),S=w.width,x=w.height,R=Number.isFinite(e.width),y=Number.isFinite(e.height),k=typeof c=="number"&&c!==1;if(!k){let B=w.width/w.height;R&&y?(S=e.width,x=e.height):R?(S=e.width,x=S/B):y&&(x=e.height,S=x*B)}if(S=Math.ceil(S),x=Math.ceil(x),o.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),o.style.transformOrigin="top left",!k&&(R||y)){let B=w.width,Y=w.height,$=S/B,M=x/Y,rt=o.style.transform||"",ht=`scale(${$}, ${M})`;o.style.transform=`${ht} ${rt}`.trim()}let C="http://www.w3.org/2000/svg",A=document.createElementNS(C,"foreignObject");A.setAttribute("width","100%"),A.setAttribute("height","100%");let P=document.createElement("style");P.textContent=m+d+"svg{overflow:visible;}"+l,A.appendChild(P),A.appendChild(o);let T=new XMLSerializer().serializeToString(A);g=``+T+"",u=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:r})});let b=document.getElementById("snapdom-sandbox");return b&&b.style.position==="absolute"&&b.remove(),u}async function Jt(t,{scale:e=1}={}){let n=new Image;return n.src=t,await n.decode(),e!==1&&(n.style.width=`${n.naturalWidth*e}px`,n.style.height=`${n.naturalHeight*e}px`),n}async function Pt(t,{dpr:e=1,scale:n=1}={}){let i=new Image;i.src=t,i.crossOrigin="anonymous",i.loading="eager",i.decoding="sync";let r=vt(),c=!1;if(r&&(document.body.appendChild(i),c=!0),await i.decode(),r&&await new Promise(s=>setTimeout(s,100)),i.width===0||i.height===0)throw c&&i.remove(),new Error("Image failed to load or has no dimensions");let a=i.naturalWidth*n,f=i.naturalHeight*n,o=document.createElement("canvas");o.width=Math.ceil(a*e),o.height=Math.ceil(f*e),o.style.width=`${a}px`,o.style.height=`${f}px`;let l=o.getContext("2d");return l.scale(e,e),l.drawImage(i,0,0,a,f),c&&i.remove(),o}async function Bt(t,{type:e="svg",scale:n=1,backgroundColor:i="#fff",quality:r}={}){let c={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[e]||"image/png";if(e==="svg"){let f=decodeURIComponent(t.split(",")[1]);return new Blob([f],{type:"image/svg+xml"})}let a=await mt(t,{dpr:1,scale:n},i);return new Promise(f=>{a.toBlob(o=>f(o),`${c}`,r)})}async function mt(t,{dpr:e=1,scale:n=1},i){let r=await Pt(t,{dpr:e,scale:n});if(!i)return r;let c=document.createElement("canvas");c.width=r.width,c.height=r.height;let a=c.getContext("2d");return a.fillStyle=i,a.fillRect(0,0,c.width,c.height),a.drawImage(r,0,0),c}async function ut(t,{dpr:e=1,scale:n=1,backgroundColor:i,quality:r},c="png"){let a=["jpg","jpeg","webp"].includes(c)?"#fff":void 0,o=await mt(t,{dpr:e,scale:n},i??a),l=new Image;return l.src=o.toDataURL(`image/${c}`,r),await l.decode(),l.style.width=`${o.width/e}px`,l.style.height=`${o.height/e}px`,l}async function Qt(t,{dpr:e=1,scale:n=1,backgroundColor:i,format:r="png",filename:c="snapDOM"}={}){if(r==="svg"){let m=await Bt(t),u=URL.createObjectURL(m),g=document.createElement("a");g.href=u,g.download=`${c}.svg`,g.click(),URL.revokeObjectURL(u);return}let a=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,o=await mt(t,{dpr:e,scale:n},i??a),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",s=o.toDataURL(l),d=document.createElement("a");d.href=s,d.download=`${c}.${r}`,d.click()}async function E(t,e={}){if(e={scale:1,...e},!t)throw new Error("Element cannot be null or undefined");return e.iconFonts&&At(e.iconFonts),await E.capture(t,e)}E.capture=async(t,e={})=>{let n=await Lt(t,e),i=e.dpr??(window.devicePixelRatio||1),r=e.scale||1;return{url:n,options:e,toRaw:()=>n,toImg:(c={})=>Jt(n,{dpr:i,scale:r,...c}),toCanvas:(c={})=>Pt(n,{dpr:i,scale:r,...c}),toBlob:(c={})=>Bt(n,{dpr:i,scale:r,...c}),toPng:(c={})=>ut(n,{dpr:i,scale:r,...c},"png"),toJpg:(c={})=>ut(n,{dpr:i,scale:r,...c},"jpeg"),toWebp:(c={})=>ut(n,{dpr:i,scale:r,...c},"webp"),download:({format:c="png",filename:a="snapDOM",backgroundColor:f,...o}={})=>Qt(n,{dpr:i,scale:r,format:c,filename:a,backgroundColor:f,...o})}};E.toRaw=async(t,e)=>(await E.capture(t,e)).toRaw();E.toImg=async(t,e)=>(await E.capture(t,e)).toImg();E.toCanvas=async(t,e)=>(await E.capture(t,e)).toCanvas();E.toBlob=async(t,e)=>(await E.capture(t,e)).toBlob(e);E.toPng=async(t,e)=>(await E.capture(t,e)).toPng(e);E.toJpg=async(t,e)=>(await E.capture(t,e)).toJpg(e);E.toWebp=async(t,e)=>(await E.capture(t,e)).toWebp(e);E.download=async(t,e={})=>{let{format:n="png",filename:i="capture",backgroundColor:r,...c}=e;return await(await E.capture(t,c)).download({format:n,filename:i,backgroundColor:r})};async function Zt(t=document,e={}){let{embedFonts:n=!0,reset:i=!1,useProxy:r}=e;if(i){h.image.clear(),h.background.clear(),h.resource.clear(),h.defaultStyle.clear(),h.baseStyle.clear(),h.font.clear(),h.computedStyle=new WeakMap;return}try{await document.fonts.ready}catch{}yt();let c=[],a=[];t?.querySelectorAll&&(c=Array.from(t.querySelectorAll("img[src]")),a=Array.from(t.querySelectorAll("*")));let f=[];for(let o of c){let l=o?.src;if(l&&!h.image.has(l)){let s=Promise.resolve().then(()=>W(l,{useProxy:r})).then(d=>{h.image.set(l,d)}).catch(()=>{});f.push(s)}}for(let o of a){let l="";try{l=U(o).backgroundImage}catch{}if(l&&l!=="none"){let s=H(l);for(let d of s)if(d.startsWith("url(")){let m=Promise.resolve().then(()=>O(d,{...e,useProxy:r})).catch(()=>{});f.push(m)}}}if(n)try{await nt({preCached:!0,localFonts:e.localFonts,useProxy:e.useProxy})}catch{}await Promise.allSettled(f)}export{Zt as preCache,E as snapdom}; diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs new file mode 100644 index 00000000..8fa72522 --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs @@ -0,0 +1,1781 @@ + +/* +* snapdom +* v.1.9.9 +* Author Juan Martin Muda +* License MIT +**/ + + +// src/core/cache.js +var cache = { + image: /* @__PURE__ */ new Map(), + background: /* @__PURE__ */ new Map(), + resource: /* @__PURE__ */ new Map(), + defaultStyle: /* @__PURE__ */ new Map(), + baseStyle: /* @__PURE__ */ new Map(), + computedStyle: /* @__PURE__ */ new WeakMap(), + font: /* @__PURE__ */ new Set(), + snapshot: /* @__PURE__ */ new WeakMap(), + snapshotKey: /* @__PURE__ */ new Map(), + reset: resetCache +}; +function resetCache() { + cache.computedStyle = /* @__PURE__ */ new WeakMap(); +} + +// src/utils/cssTools.js +var commonTags = [ + "div", + "span", + "p", + "a", + "img", + "ul", + "li", + "button", + "input", + "select", + "textarea", + "label", + "section", + "article", + "header", + "footer", + "nav", + "main", + "aside", + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "svg", + "path", + "circle", + "rect", + "line", + "g", + "table", + "thead", + "tbody", + "tr", + "td", + "th" +]; +function precacheCommonTags() { + for (let tag of commonTags) { + getDefaultStyleForTag(tag); + } +} +function getDefaultStyleForTag(tagName) { + if (cache.defaultStyle.has(tagName)) { + return cache.defaultStyle.get(tagName); + } + const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]); + if (skipTags.has(tagName)) { + const empty = {}; + cache.defaultStyle.set(tagName, empty); + return empty; + } + let sandbox = document.getElementById("snapdom-sandbox"); + if (!sandbox) { + sandbox = document.createElement("div"); + sandbox.id = "snapdom-sandbox"; + sandbox.style.position = "absolute"; + sandbox.style.left = "-9999px"; + sandbox.style.top = "-9999px"; + sandbox.style.width = "0"; + sandbox.style.height = "0"; + sandbox.style.overflow = "hidden"; + document.body.appendChild(sandbox); + } + const el = document.createElement(tagName); + el.style.all = "initial"; + sandbox.appendChild(el); + const styles = getComputedStyle(el); + const defaults = {}; + for (let prop of styles) { + defaults[prop] = styles.getPropertyValue(prop); + } + sandbox.removeChild(el); + cache.defaultStyle.set(tagName, defaults); + return defaults; +} +var IGNORED_PROPS = /* @__PURE__ */ new Set([ + "-webkit-locale" +]); +function getStyleKey(snapshot, tagName, compress = false) { + const entries = []; + const defaultStyles = getDefaultStyleForTag(tagName); + for (let [prop, value] of Object.entries(snapshot)) { + if (IGNORED_PROPS.has(prop)) continue; + if (!compress) { + if (value) { + entries.push(`${prop}:${value}`); + } + } else { + const defaultValue = defaultStyles[prop]; + if (value && value !== defaultValue) { + entries.push(`${prop}:${value}`); + } + } + } + return entries.sort().join(";"); +} +function collectUsedTagNames(root) { + const tagSet = /* @__PURE__ */ new Set(); + if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { + return []; + } + if (root.tagName) { + tagSet.add(root.tagName.toLowerCase()); + } + if (typeof root.querySelectorAll === "function") { + root.querySelectorAll("*").forEach((el) => tagSet.add(el.tagName.toLowerCase())); + } + return Array.from(tagSet); +} +function generateDedupedBaseCSS(usedTagNames) { + const groups = /* @__PURE__ */ new Map(); + for (let tagName of usedTagNames) { + const styles = cache.defaultStyle.get(tagName); + if (!styles) continue; + const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join(""); + if (!groups.has(key)) { + groups.set(key, []); + } + groups.get(key).push(tagName); + } + let css = ""; + for (let [styleBlock, tagList] of groups.entries()) { + css += `${tagList.join(",")} { ${styleBlock} } +`; + } + return css; +} +function generateCSSClasses(styleMap) { + const keySet = new Set(styleMap.values()); + const classMap = /* @__PURE__ */ new Map(); + let counter = 1; + for (const key of keySet) { + if (!key.trim()) continue; + classMap.set(key, `c${counter++}`); + } + return classMap; +} + +// src/utils/helpers.js +async function inlineSingleBackgroundEntry(entry, options = {}) { + const rawUrl = extractURL(entry); + const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry); + if (rawUrl) { + const encodedUrl = safeEncodeURI(rawUrl); + if (cache.background.has(encodedUrl)) { + return options.skipInline ? void 0 : `url(${cache.background.get(encodedUrl)})`; + } else { + const dataUrl = await fetchImage(encodedUrl, { useProxy: options.useProxy }); + cache.background.set(encodedUrl, dataUrl); + return options.skipInline ? void 0 : `url("${dataUrl}")`; + } + } + if (isGradient || entry === "none") { + return entry; + } + return entry; +} +function idle(fn, { fast = false } = {}) { + if (fast) return fn(); + if ("requestIdleCallback" in window) { + requestIdleCallback(fn, { timeout: 50 }); + } else { + setTimeout(fn, 1); + } +} +function getStyle(el, pseudo = null) { + if (!(el instanceof Element)) { + return window.getComputedStyle(el, pseudo); + } + let map = cache.computedStyle.get(el); + if (!map) { + map = /* @__PURE__ */ new Map(); + cache.computedStyle.set(el, map); + } + if (!map.has(pseudo)) { + const st = window.getComputedStyle(el, pseudo); + map.set(pseudo, st); + } + return map.get(pseudo); +} +function parseContent(content) { + let clean = content.replace(/^['"]|['"]$/g, ""); + if (clean.startsWith("\\")) { + try { + return String.fromCharCode(parseInt(clean.replace("\\", ""), 16)); + } catch { + return clean; + } + } + return clean; +} +function extractURL(value) { + const match = value.match(/url\((['"]?)(.*?)(\1)\)/); + if (!match) return null; + const url = match[2].trim(); + if (url.startsWith("#")) return null; + return url; +} +async function fetchResource(url, { useProxy = "" } = {}) { + async function doFetch(u) { + const res = await fetch(u); + if (!res.ok) throw new Error(`[snapdom] Failed to fetch resource: ${u}`); + return res; + } + try { + return await doFetch(url); + } catch (e) { + if (useProxy && typeof useProxy === "string") { + const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url); + return doFetch(proxied); + } + throw e; + } +} +var _inflight = /* @__PURE__ */ new Map(); +var _errorCache = /* @__PURE__ */ new Map(); +function fetchImage(src, { timeout = 3e3, useProxy = "", errorTTL = 8e3 } = {}) { + function getCrossOriginMode(url) { + try { + const parsed = new URL(url, window.location.href); + return parsed.origin === window.location.origin ? "use-credentials" : "anonymous"; + } catch { + return "anonymous"; + } + } + const ok = (data) => ({ ok: true, data }); + const fail = (e) => ({ ok: false, error: e instanceof Error ? e : new Error(String(e)) }); + function fetchBlobAsDataURLSafe(fetchUrl) { + try { + return fetch(fetchUrl, { + mode: "cors", + credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit" + }).then((r) => { + if (!r.ok) return fail(new Error("HTTP " + r.status)); + return r.blob().then((blob) => new Promise((resolve) => { + const reader = new FileReader(); + reader.onloadend = () => { + const base64 = reader.result; + if (typeof base64 !== "string" || !base64.startsWith("data:image/")) { + resolve(fail(new Error("Invalid image data URL"))); + } else { + resolve(ok(base64)); + } + }; + reader.onerror = () => resolve(fail(new Error("FileReader error"))); + reader.readAsDataURL(blob); + })); + }).catch((e) => fail(e)); + } catch (e) { + return Promise.resolve(fail(e)); + } + } + function fetchWithFallbackOnceSafe(url) { + return fetchBlobAsDataURLSafe(url).then((r) => { + if (r.ok) return r; + if (useProxy && typeof useProxy === "string") { + const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url); + return fetchBlobAsDataURLSafe(proxied).then((r2) => { + if (r2.ok) return r2; + return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")); + }); + } + return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")); + }); + } + const now = Date.now(); + const until = _errorCache.get(src); + if (until && until > now) { + const pr = Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown).")); + pr.catch(() => { + }); + return pr; + } + if (_inflight.has(src)) return _inflight.get(src); + const crossOriginValue = getCrossOriginMode(src); + if (cache.image.has(src)) return Promise.resolve(cache.image.get(src)); + if (src.startsWith("data:image/")) { + cache.image.set(src, src); + return Promise.resolve(src); + } + if (/\.svg(\?.*)?$/i.test(src)) { + const p2 = (async () => { + const direct = await (async () => { + try { + const res = await fetch(src, { + mode: "cors", + credentials: crossOriginValue === "use-credentials" ? "include" : "omit" + }); + if (!res.ok) return fail(new Error("HTTP " + res.status)); + const svgText = await res.text(); + return ok(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`); + } catch (e) { + return fail(e); + } + })(); + if (direct.ok) { + cache.image.set(src, direct.data); + return direct.data; + } + const via = await fetchWithFallbackOnceSafe(src); + if (via.ok) { + cache.image.set(src, via.data); + return via.data; + } + _errorCache.set(src, now + errorTTL); + return Promise.reject(via.error); + })(); + _inflight.set(src, p2); + p2.finally(() => _inflight.delete(src)); + p2.catch(() => { + }); + return p2; + } + const p = new Promise((resolve, reject) => { + let finished = false; + const img = new Image(); + const finish = (fn) => (arg) => { + if (finished) return; + finished = true; + clearTimeout(timeoutId); + img.onload = img.onerror = null; + fn(arg); + }; + const onSuccess = (d) => { + cache.image.set(src, d); + resolve(d); + }; + const onFinalError = (e) => { + _errorCache.set(src, Date.now() + errorTTL); + reject(e); + }; + const timeoutId = setTimeout( + finish(() => { + fetchWithFallbackOnceSafe(src).then((r) => { + if (r.ok) onSuccess(r.data); + else onFinalError(new Error("Image load timed out")); + }); + }), + timeout + ); + img.crossOrigin = crossOriginValue; + img.onload = finish(() => { + Promise.resolve(img.decode()).then(() => { + try { + const canvas = document.createElement("canvas"); + canvas.width = img.naturalWidth || img.width; + canvas.height = img.naturalHeight || img.height; + const ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, canvas.width, canvas.height); + onSuccess(canvas.toDataURL("image/png")); + } catch { + fetchWithFallbackOnceSafe(src).then((r) => { + if (r.ok) onSuccess(r.data); + else onFinalError(r.error); + }); + } + }).catch(() => { + fetchWithFallbackOnceSafe(src).then((r) => { + if (r.ok) onSuccess(r.data); + else onFinalError(r.error); + }); + }); + }); + img.onerror = finish(() => { + fetchWithFallbackOnceSafe(src).then((r) => { + if (r.ok) onSuccess(r.data); + else onFinalError(r.error); + }); + }); + img.src = src; + }); + _inflight.set(src, p); + p.finally(() => _inflight.delete(src)); + p.catch(() => { + }); + return p; +} +function snapshotComputedStyle(style) { + const snap = {}; + for (let prop of style) { + snap[prop] = style.getPropertyValue(prop); + } + return snap; +} +function isSafari() { + return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); +} +function stripTranslate(transform) { + if (!transform || transform === "none") return ""; + let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, ""); + cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => { + const parts = values.split(",").map((s) => s.trim()); + if (parts.length !== 6) return `matrix(${values})`; + parts[4] = "0"; + parts[5] = "0"; + return `matrix(${parts.join(", ")})`; + }); + cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => { + const parts = values.split(",").map((s) => s.trim()); + if (parts.length !== 16) return `matrix3d(${values})`; + parts[12] = "0"; + parts[13] = "0"; + return `matrix3d(${parts.join(", ")})`; + }); + return cleaned.trim().replace(/\s{2,}/g, " "); +} +function safeEncodeURI(uri) { + if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri; + try { + return encodeURI(uri); + } catch { + return uri; + } +} +function splitBackgroundImage(bg) { + const parts = []; + let depth = 0; + let lastIndex = 0; + for (let i = 0; i < bg.length; i++) { + const char = bg[i]; + if (char === "(") depth++; + if (char === ")") depth--; + if (char === "," && depth === 0) { + parts.push(bg.slice(lastIndex, i).trim()); + lastIndex = i + 1; + } + } + parts.push(bg.slice(lastIndex).trim()); + return parts; +} + +// src/modules/styles.js +var snapshotCache = /* @__PURE__ */ new WeakMap(); +var snapshotKeyCache = /* @__PURE__ */ new Map(); +function snapshotComputedStyleFull(style) { + const result = {}; + const computedVisibility = style.getPropertyValue("visibility"); + for (let i = 0; i < style.length; i++) { + const prop = style[i]; + let val = style.getPropertyValue(prop); + if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) { + val = "none"; + } + result[prop] = val; + } + if (computedVisibility === "hidden") { + result.opacity = "0"; + } + return result; +} +function inlineAllStyles(source, clone, styleMap, cache2, compress) { + if (source.tagName === "STYLE") return; + if (!cache2.has(source)) { + cache2.set(source, getStyle(source)); + } + const style = cache2.get(source); + if (!snapshotCache.has(source)) { + const snapshot2 = snapshotComputedStyleFull(style); + snapshotCache.set(source, snapshot2); + } + const snapshot = snapshotCache.get(source); + const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";"); + if (snapshotKeyCache.has(hash)) { + styleMap.set(clone, snapshotKeyCache.get(hash)); + return; + } + const tagName = source.tagName?.toLowerCase() || "div"; + const key = getStyleKey(snapshot, tagName, compress); + snapshotKeyCache.set(hash, key); + styleMap.set(clone, key); +} + +// src/core/clone.js +function freezeImgSrcset(original, cloned) { + try { + const chosen = original.currentSrc || original.src || ""; + if (!chosen) return; + cloned.setAttribute("src", chosen); + cloned.removeAttribute("srcset"); + cloned.removeAttribute("sizes"); + cloned.loading = "eager"; + cloned.decoding = "sync"; + } catch { + } +} +function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {}, originalRoot) { + if (!node) throw new Error("Invalid node"); + const clonedAssignedNodes = /* @__PURE__ */ new Set(); + let pendingSelectValue = null; + if (node.nodeType === Node.TEXT_NODE) { + return node.cloneNode(true); + } + if (node.nodeType !== Node.ELEMENT_NODE) { + return node.cloneNode(true); + } + if (node.getAttribute("data-capture") === "exclude") { + const spacer = document.createElement("div"); + const rect = node.getBoundingClientRect(); + spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; + return spacer; + } + if (options.exclude && Array.isArray(options.exclude)) { + for (const selector of options.exclude) { + try { + if (node.matches?.(selector)) { + const spacer = document.createElement("div"); + const rect = node.getBoundingClientRect(); + spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; + return spacer; + } + } catch (err) { + console.warn(`Invalid selector in exclude option: ${selector}`, err); + } + } + } + if (typeof options.filter === "function") { + try { + if (!options.filter(node, originalRoot || node)) { + const spacer = document.createElement("div"); + const rect = node.getBoundingClientRect(); + spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; + return spacer; + } + } catch (err) { + console.warn("Error in filter function:", err); + } + } + if (node.tagName === "IFRAME") { + const fallback = document.createElement("div"); + fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`; + return fallback; + } + if (node.getAttribute("data-capture") === "placeholder") { + const clone2 = node.cloneNode(false); + nodeMap.set(clone2, node); + inlineAllStyles(node, clone2, styleMap, styleCache, compress); + const placeholder = document.createElement("div"); + placeholder.textContent = node.getAttribute("data-placeholder-text") || ""; + placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`; + clone2.appendChild(placeholder); + return clone2; + } + if (node.tagName === "CANVAS") { + const dataURL = node.toDataURL(); + const img = document.createElement("img"); + img.src = dataURL; + img.width = node.width; + img.height = node.height; + nodeMap.set(img, node); + inlineAllStyles(node, img, styleMap, styleCache, compress); + return img; + } + let clone; + try { + clone = node.cloneNode(false); + nodeMap.set(clone, node); + if (node.tagName === "IMG") { + freezeImgSrcset(node, clone); + } + } catch (err) { + console.error("[Snapdom] Failed to clone node:", node, err); + throw err; + } + if (node instanceof HTMLTextAreaElement) { + clone.textContent = node.value; + clone.value = node.value; + const rect = node.getBoundingClientRect(); + clone.style.width = `${rect.width}px`; + clone.style.height = `${rect.height}px`; + return clone; + } + if (node instanceof HTMLInputElement) { + clone.value = node.value; + clone.setAttribute("value", node.value); + if (node.checked !== void 0) { + clone.checked = node.checked; + if (node.checked) clone.setAttribute("checked", ""); + if (node.indeterminate) clone.indeterminate = node.indeterminate; + } + } + if (node instanceof HTMLSelectElement) { + pendingSelectValue = node.value; + } + inlineAllStyles(node, clone, styleMap, styleCache, compress); + if (node.shadowRoot) { + const hasSlot = Array.from(node.shadowRoot.querySelectorAll("slot")).length > 0; + if (hasSlot) { + for (const child of node.shadowRoot.childNodes) { + if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") { + const cssText = child.textContent || ""; + if (cssText.trim() && compress) { + styleCache.set(child, cssText); + } + } + } + } else { + const shadowFrag = document.createDocumentFragment(); + for (const child of node.shadowRoot.childNodes) { + if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") { + const cssText = child.textContent || ""; + if (cssText.trim() && compress) { + styleCache.set(child, cssText); + } + continue; + } + const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); + if (clonedChild) shadowFrag.appendChild(clonedChild); + } + clone.appendChild(shadowFrag); + } + } + if (node.tagName === "SLOT") { + const assigned = node.assignedNodes?.({ flatten: true }) || []; + const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes); + const fragment = document.createDocumentFragment(); + for (const child of nodesToClone) { + const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); + if (clonedChild) fragment.appendChild(clonedChild); + } + return fragment; + } + for (const child of node.childNodes) { + if (clonedAssignedNodes.has(child)) continue; + const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); + if (clonedChild) clone.appendChild(clonedChild); + } + if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) { + clone.value = pendingSelectValue; + for (const opt of clone.options) { + if (opt.value === pendingSelectValue) { + opt.setAttribute("selected", ""); + } else { + opt.removeAttribute("selected"); + } + } + } + return clone; +} + +// src/modules/iconFonts.js +var defaultIconFonts = [ + // /uicons/i, + /font\s*awesome/i, + /material\s*icons/i, + /ionicons/i, + /glyphicons/i, + /feather/i, + /bootstrap\s*icons/i, + /remix\s*icons/i, + /heroicons/i, + /layui/i, + /lucide/i +]; +var userIconFonts = []; +function extendIconFonts(fonts) { + const list = Array.isArray(fonts) ? fonts : [fonts]; + for (const f of list) { + if (f instanceof RegExp) { + userIconFonts.push(f); + } else if (typeof f === "string") { + userIconFonts.push(new RegExp(f, "i")); + } else { + console.warn("[snapdom] Ignored invalid iconFont value:", f); + } + } +} +function isIconFont(input) { + const text = typeof input === "string" ? input : ""; + const candidates = [...defaultIconFonts, ...userIconFonts]; + for (const rx of candidates) { + if (rx instanceof RegExp && rx.test(text)) return true; + } + if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true; + return false; +} + +// src/modules/fonts.js +async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") { + fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, ""); + const dpr = window.devicePixelRatio || 1; + await document.fonts.ready; + const span = document.createElement("span"); + span.textContent = unicodeChar; + span.style.position = "absolute"; + span.style.visibility = "hidden"; + span.style.fontFamily = `"${fontFamily}"`; + span.style.fontWeight = fontWeight || "normal"; + span.style.fontSize = `${fontSize}px`; + span.style.lineHeight = "1"; + span.style.whiteSpace = "nowrap"; + span.style.padding = "0"; + span.style.margin = "0"; + document.body.appendChild(span); + const rect = span.getBoundingClientRect(); + const width = Math.ceil(rect.width); + const height = Math.ceil(rect.height); + document.body.removeChild(span); + const canvas = document.createElement("canvas"); + canvas.width = width * dpr; + canvas.height = height * dpr; + const ctx = canvas.getContext("2d"); + ctx.scale(dpr, dpr); + ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`; + ctx.textAlign = "left"; + ctx.textBaseline = "top"; + ctx.fillStyle = color; + ctx.fillText(unicodeChar, 0, 0); + return { + dataUrl: canvas.toDataURL(), + width, + height + }; +} +function isStylesheetLoaded(href) { + return Array.from(document.styleSheets).some((sheet) => sheet.href === href); +} +function injectLinkIfMissing(href) { + return new Promise((resolve) => { + if (isStylesheetLoaded(href)) return resolve(null); + const link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = href; + link.setAttribute("data-snapdom", "injected-import"); + link.onload = () => resolve(link); + link.onerror = () => resolve(null); + document.head.appendChild(link); + }); +} +async function embedCustomFonts({ preCached = false, localFonts = [], useProxy = "" } = {}) { + if (cache.resource.has("fonts-embed-css")) { + if (preCached) { + const style = document.createElement("style"); + style.setAttribute("data-snapdom", "embedFonts"); + style.textContent = cache.resource.get("fonts-embed-css"); + document.head.appendChild(style); + } + return cache.resource.get("fonts-embed-css"); + } + const loadedFonts = /* @__PURE__ */ new Set(); + try { + for (const f of document.fonts) { + if (f.status === "loaded") { + loadedFonts.add(`${f.family}__${f.weight || "normal"}__${f.style || "normal"}`); + } + } + } catch { + } + const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g; + const styleImports = []; + for (const styleTag of document.querySelectorAll("style")) { + const cssText = styleTag.textContent || ""; + const matches = Array.from(cssText.matchAll(importRegex)); + for (const match of matches) { + const importUrl = match[1]; + if (isIconFont(importUrl)) continue; + if (!isStylesheetLoaded(importUrl)) { + styleImports.push(importUrl); + } + } + } + await Promise.all(styleImports.map(injectLinkIfMissing)); + const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href); + let finalCSS = ""; + for (const link of links) { + try { + const res = await fetchResource(link.href, { useProxy }); + const cssText = await res.text(); + if (isIconFont(link.href) || isIconFont(cssText)) continue; + const faceRegex = /@font-face[^{}]*{[^}]*}/g; + let cssFinal = cssText; + for (const face of cssText.match(faceRegex) || []) { + const famMatch = face.match(/font-family:\s*([^;]+);/i); + if (!famMatch) continue; + const family = famMatch[1].replace(/['"]/g, "").trim(); + const weightMatch = face.match(/font-weight:\s*([^;]+);/i); + const styleMatch = face.match(/font-style:\s*([^;]+);/i); + const weight = weightMatch ? weightMatch[1].trim() : "normal"; + const style = styleMatch ? styleMatch[1].trim() : "normal"; + const key = `${family}__${weight}__${style}`; + const urlRegex = /url\((["']?)([^"')]+)\1\)/g; + const hasURL = /url\(/i.test(face); + const hasLocal = /local\(/i.test(face); + if (!hasURL && hasLocal) { + continue; + } + if (!loadedFonts.has(key)) { + cssFinal = cssFinal.replace(face, ""); + continue; + } + let inlined = face; + const matches = Array.from(face.matchAll(urlRegex)); + for (const match of matches) { + let rawUrl = extractURL(match[0]); + if (!rawUrl) continue; + let url = rawUrl; + if (!url.startsWith("http") && !url.startsWith("data:")) { + url = new URL(url, link.href).href; + } + if (isIconFont(url)) continue; + if (cache.resource.has(url)) { + cache.font.add(url); + inlined = inlined.replace(match[0], `url(${cache.resource.get(url)})`); + continue; + } + if (cache.font.has(url)) continue; + try { + const fontRes = await fetchResource(url, { useProxy }); + const blob = await fontRes.blob(); + const b64 = await new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.readAsDataURL(blob); + }); + cache.resource.set(url, b64); + cache.font.add(url); + inlined = inlined.replace(match[0], `url(${b64})`); + } catch (e) { + console.warn("[snapdom] Failed to fetch font resource:", url); + } + } + cssFinal = cssFinal.replace(face, inlined); + } + finalCSS += cssFinal + "\n"; + } catch (e) { + console.warn("[snapdom] Failed to fetch CSS:", link.href); + } + } + for (const sheet of document.styleSheets) { + try { + if (!sheet.href || links.every((link) => link.href !== sheet.href)) { + for (const rule of sheet.cssRules) { + if (rule.type === CSSRule.FONT_FACE_RULE) { + const src = rule.style.getPropertyValue("src"); + const family = rule.style.getPropertyValue("font-family"); + if (!src || isIconFont(family)) continue; + const weightVal = rule.style.getPropertyValue("font-weight") || "normal"; + const styleVal = rule.style.getPropertyValue("font-style") || "normal"; + const key = `${family}__${weightVal}__${styleVal}`; + const urlRegex = /url\((["']?)([^"')]+)\1\)/g; + const localRegex = /local\((["']?)[^)]+?\1\)/g; + const hasURL = !!src.match(urlRegex); + const hasLocal = !!src.match(localRegex); + if (!hasURL && hasLocal) { + finalCSS += `@font-face{font-family:${family};src:${src};font-style:${styleVal};font-weight:${weightVal};}`; + continue; + } + if (!loadedFonts.has(key)) continue; + let inlinedSrc = src; + const matches = Array.from(src.matchAll(urlRegex)); + for (const match of matches) { + let rawUrl = match[2].trim(); + if (!rawUrl) continue; + let url = rawUrl; + if (!url.startsWith("http") && !url.startsWith("data:")) { + url = new URL(url, sheet.href || location.href).href; + } + if (isIconFont(url)) continue; + if (cache.resource.has(url)) { + cache.font.add(url); + inlinedSrc = inlinedSrc.replace(match[0], `url(${cache.resource.get(url)})`); + continue; + } + if (cache.font.has(url)) continue; + try { + const res = await fetchResource(url, { useProxy }); + const blob = await res.blob(); + const b64 = await new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.readAsDataURL(blob); + }); + cache.resource.set(url, b64); + cache.font.add(url); + inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`); + } catch (e) { + console.warn("[snapdom] Failed to fetch font URL:", url); + } + } + finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleVal};font-weight:${weightVal};}`; + } + } + } + } catch (e) { + console.warn("[snapdom] Cannot access stylesheet", sheet.href, e); + } + } + for (const font of document.fonts) { + if (font.family && font.status === "loaded" && font._snapdomSrc) { + if (isIconFont(font.family)) continue; + let b64 = font._snapdomSrc; + if (!b64.startsWith("data:")) { + if (cache.resource.has(font._snapdomSrc)) { + b64 = cache.resource.get(font._snapdomSrc); + cache.font.add(font._snapdomSrc); + } else if (!cache.font.has(font._snapdomSrc)) { + try { + const res = await fetchResource(font._snapdomSrc, { useProxy }); + const blob = await res.blob(); + b64 = await new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.readAsDataURL(blob); + }); + cache.resource.set(font._snapdomSrc, b64); + cache.font.add(font._snapdomSrc); + } catch (e) { + console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc); + continue; + } + } + } + finalCSS += `@font-face{font-family:'${font.family}';src:url(${b64});font-style:${font.style || "normal"};font-weight:${font.weight || "normal"};}`; + } + } + for (const font of localFonts) { + if (!font || typeof font !== "object") continue; + const { family, src, weight = "normal", style = "normal" } = font; + if (!family || !src) continue; + let b64 = src; + if (!b64.startsWith("data:")) { + try { + const res = await fetchResource(src, { useProxy }); + const blob = await res.blob(); + b64 = await new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.readAsDataURL(blob); + }); + cache.resource.set(src, b64); + cache.font.add(src); + } catch (e) { + console.warn("[snapdom] Failed to load local font:", src); + continue; + } + } else { + cache.resource.set(src, b64); + cache.font.add(src); + } + finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`; + } + if (finalCSS) { + cache.resource.set("fonts-embed-css", finalCSS); + if (preCached) { + const style = document.createElement("style"); + style.setAttribute("data-snapdom", "embedFonts"); + style.textContent = finalCSS; + document.head.appendChild(style); + } + } + return finalCSS; +} + +// src/modules/pseudo.js +async function inlinePseudoElements(source, clone, styleMap, styleCache, options) { + if (!(source instanceof Element) || !(clone instanceof Element)) return; + for (const pseudo of ["::before", "::after", "::first-letter"]) { + try { + const style = getStyle(source, pseudo); + if (!style || typeof style[Symbol.iterator] !== "function") continue; + const isEmptyPseudo = style.content === "none" && style.backgroundImage === "none" && style.backgroundColor === "transparent" && (style.borderStyle === "none" || parseFloat(style.borderWidth) === 0) && (!style.transform || style.transform === "none") && style.display === "inline"; + if (isEmptyPseudo) continue; + if (pseudo === "::first-letter") { + const normal = getComputedStyle(source); + const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight; + if (!isMeaningful) continue; + const textNode = Array.from(clone.childNodes).find( + (n) => n.nodeType === Node.TEXT_NODE && n.textContent?.trim().length > 0 + ); + if (!textNode) continue; + const text = textNode.textContent; + const match = text.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u); + const first = match?.[0]; + const rest = text.slice(first?.length || 0); + if (!first || /[\uD800-\uDFFF]/.test(first)) continue; + const span = document.createElement("span"); + span.textContent = first; + span.dataset.snapdomPseudo = "::first-letter"; + const snapshot2 = snapshotComputedStyle(style); + const key2 = getStyleKey(snapshot2, "span", options); + styleMap.set(span, key2); + const restNode = document.createTextNode(rest); + clone.replaceChild(restNode, textNode); + clone.insertBefore(span, restNode); + continue; + } + const content = style.content; + const cleanContent = /counter\s*\(|counters\s*\(/.test(content) ? "- " : parseContent(content); + const bg = style.backgroundImage; + const bgColor = style.backgroundColor; + const fontFamily = style.fontFamily; + const fontSize = parseInt(style.fontSize) || 32; + const fontWeight = parseInt(style.fontWeight) || false; + const color = style.color || "#000"; + const display = style.display; + const width = parseFloat(style.width); + const height = parseFloat(style.height); + const borderStyle = style.borderStyle; + const borderWidth = parseFloat(style.borderWidth); + const transform = style.transform; + const isIconFont2 = isIconFont(fontFamily); + const hasExplicitContent = content !== "none" && cleanContent !== ""; + const hasBg = bg && bg !== "none"; + const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)"; + const hasBox = display !== "inline" && (width > 0 || height > 0); + const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0; + const hasTransform = transform && transform !== "none"; + const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform; + if (!shouldRender) continue; + const pseudoEl = document.createElement("span"); + pseudoEl.dataset.snapdomPseudo = pseudo; + pseudoEl.style.verticalAlign = "middle"; + const snapshot = snapshotComputedStyle(style); + const key = getStyleKey(snapshot, "span", options); + styleMap.set(pseudoEl, key); + if (isIconFont2 && cleanContent.length === 1) { + const { dataUrl, width: width2, height: height2 } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color); + const imgEl = document.createElement("img"); + imgEl.src = dataUrl; + imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`; + pseudoEl.appendChild(imgEl); + clone.dataset.snapdomHasIcon = "true"; + } else if (cleanContent.startsWith("url(")) { + const rawUrl = extractURL(cleanContent); + if (rawUrl?.trim()) { + try { + const imgEl = document.createElement("img"); + const dataUrl = await fetchImage(safeEncodeURI(rawUrl), options); + imgEl.src = dataUrl; + imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`; + pseudoEl.appendChild(imgEl); + } catch (e) { + console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e); + } + } + } else if (!isIconFont2 && hasExplicitContent) { + pseudoEl.textContent = cleanContent; + } + if (hasBg) { + try { + const bgSplits = splitBackgroundImage(bg); + const newBgParts = await Promise.all(bgSplits.map(inlineSingleBackgroundEntry)); + pseudoEl.style.backgroundImage = newBgParts.join(", "); + } catch (e) { + console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e); + } + } + if (hasBgColor) pseudoEl.style.backgroundColor = bgColor; + const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== ""; + const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform; + if (!hasVisibleBox) continue; + if (pseudo === "::before") { + clone.insertBefore(pseudoEl, clone.firstChild); + } else { + clone.appendChild(pseudoEl); + } + } catch (e) { + console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e); + } + } + const sChildren = Array.from(source.children); + const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo); + for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { + await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, options); + } +} + +// src/modules/svgDefs.js +function inlineExternalDefsAndSymbols(rootElement) { + if (!rootElement) return; + const usedIds = /* @__PURE__ */ new Set(); + rootElement.querySelectorAll("use").forEach((use) => { + const href = use.getAttribute("xlink:href") || use.getAttribute("href"); + if (href && href.startsWith("#")) { + usedIds.add(href.slice(1)); + } + }); + if (!usedIds.size) return; + const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs")); + const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol"); + const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs"); + let container = rootElement.querySelector("svg.inline-defs-container"); + if (!container) { + container = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + container.setAttribute("aria-hidden", "true"); + container.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;"); + container.classList.add("inline-defs-container"); + rootElement.insertBefore(container, rootElement.firstChild); + } + const existingIds = /* @__PURE__ */ new Set(); + rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => { + existingIds.add(el.id); + }); + usedIds.forEach((id) => { + if (existingIds.has(id)) return; + const symbol = globalSymbols.find((sym) => sym.id === id); + if (symbol) { + container.appendChild(symbol.cloneNode(true)); + existingIds.add(id); + return; + } + for (const defs of globalDefs) { + const defEl = defs.querySelector(`#${CSS.escape(id)}`); + if (defEl) { + let defsContainer = container.querySelector("defs"); + if (!defsContainer) { + defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs"); + container.appendChild(defsContainer); + } + defsContainer.appendChild(defEl.cloneNode(true)); + existingIds.add(id); + break; + } + } + }); +} + +// src/core/prepare.js +async function prepareClone(element, compress = false, embedFonts = false, options = {}) { + const styleMap = /* @__PURE__ */ new Map(); + const styleCache = /* @__PURE__ */ new WeakMap(); + const nodeMap = /* @__PURE__ */ new Map(); + let clone; + let classCSS = ""; + stabilizeLayout(element); + try { + inlineExternalDefsAndSymbols(element); + } catch (e) { + console.warn("inlineExternal defs or symbol failed:", e); + } + try { + clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element); + } catch (e) { + console.warn("deepClone failed:", e); + throw e; + } + try { + await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy); + } catch (e) { + console.warn("inlinePseudoElements failed:", e); + } + await resolveBlobUrlsInTree(clone); + if (compress) { + const keyToClass = generateCSSClasses(styleMap); + classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join(""); + for (const [node, key] of styleMap.entries()) { + if (node.tagName === "STYLE") continue; + if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) { + node.setAttribute("style", key.replace(/;/g, "; ")); + continue; + } + const className = keyToClass.get(key); + if (className) node.classList.add(className); + const bgImage = node.style?.backgroundImage; + const hasIcon = node.dataset?.snapdomHasIcon; + if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage; + if (hasIcon) { + node.style.verticalAlign = "middle"; + node.style.display = "inline"; + } + } + } else { + for (const [node, key] of styleMap.entries()) { + if (node.tagName === "STYLE") continue; + node.setAttribute("style", key.replace(/;/g, "; ")); + } + } + for (const [cloneNode, originalNode] of nodeMap.entries()) { + const scrollX = originalNode.scrollLeft; + const scrollY = originalNode.scrollTop; + const hasScroll = scrollX || scrollY; + if (hasScroll && cloneNode instanceof HTMLElement) { + cloneNode.style.overflow = "hidden"; + cloneNode.style.scrollbarWidth = "none"; + cloneNode.style.msOverflowStyle = "none"; + const inner = document.createElement("div"); + inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`; + inner.style.willChange = "transform"; + inner.style.display = "inline-block"; + inner.style.width = "100%"; + while (cloneNode.firstChild) { + inner.appendChild(cloneNode.firstChild); + } + cloneNode.appendChild(inner); + } + } + if (element === nodeMap.get(clone)) { + const computed = styleCache.get(element) || window.getComputedStyle(element); + styleCache.set(element, computed); + const transform = stripTranslate(computed.transform); + clone.style.margin = "0"; + clone.style.position = "static"; + clone.style.top = "auto"; + clone.style.left = "auto"; + clone.style.right = "auto"; + clone.style.bottom = "auto"; + clone.style.zIndex = "auto"; + clone.style.float = "none"; + clone.style.clear = "none"; + clone.style.transform = transform || ""; + } + for (const [cloneNode, originalNode] of nodeMap.entries()) { + if (originalNode.tagName === "PRE") { + cloneNode.style.marginTop = "0"; + cloneNode.style.marginBlockStart = "0"; + } + } + return { clone, classCSS, styleCache }; +} +function stabilizeLayout(element) { + const style = getComputedStyle(element); + const outlineStyle = style.outlineStyle; + const outlineWidth = style.outlineWidth; + const borderStyle = style.borderStyle; + const borderWidth = style.borderWidth; + const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0; + const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0; + if (outlineVisible && borderAbsent) { + element.style.border = `${outlineWidth} solid transparent`; + } +} +var _blobToDataUrlCache = /* @__PURE__ */ new Map(); +async function blobUrlToDataUrl(blobUrl) { + if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl); + const res = await fetch(blobUrl); + if (!res.ok) throw new Error(`[SnapDOM] HTTP ${res.status} on blob fetch (${blobUrl})`); + const blob = await res.blob(); + const dataUrl = await new Promise((resolve, reject) => { + const fr = new FileReader(); + fr.onloadend = () => { + const v = fr.result; + if (typeof v === "string" && v.startsWith("data:")) resolve(v); + else reject(new Error("[SnapDOM] Invalid data URL from blob")); + }; + fr.onerror = () => reject(new Error("[SnapDOM] FileReader error")); + fr.readAsDataURL(blob); + }); + _blobToDataUrlCache.set(blobUrl, dataUrl); + return dataUrl; +} +var BLOB_URL_RE = /\bblob:[^)"'\s]+/g; +async function replaceBlobUrlsInCssText(cssText) { + if (!cssText || cssText.indexOf("blob:") === -1) return cssText; + const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || [])); + if (uniques.length === 0) return cssText; + let out = cssText; + for (const u of uniques) { + try { + const d = await blobUrlToDataUrl(u); + out = out.split(u).join(d); + } catch { + } + } + return out; +} +function isBlobUrl(u) { + return typeof u === "string" && u.startsWith("blob:"); +} +function parseSrcset(srcset) { + return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => { + const m = item.match(/^(\S+)(\s+.+)?$/); + return m ? { url: m[1], desc: m[2] || "" } : null; + }).filter(Boolean); +} +function stringifySrcset(parts) { + return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", "); +} +async function resolveBlobUrlsInTree(root) { + if (!root) return; + const imgs = root.querySelectorAll ? root.querySelectorAll("img") : []; + for (const img of imgs) { + try { + const srcAttr = img.getAttribute("src"); + const effective = srcAttr || img.currentSrc || ""; + if (isBlobUrl(effective)) { + const data = await blobUrlToDataUrl(effective); + img.setAttribute("src", data); + } + const srcset = img.getAttribute("srcset"); + if (srcset && srcset.includes("blob:")) { + const parts = parseSrcset(srcset); + let changed = false; + for (const p of parts) { + if (isBlobUrl(p.url)) { + try { + p.url = await blobUrlToDataUrl(p.url); + changed = true; + } catch { + } + } + } + if (changed) img.setAttribute("srcset", stringifySrcset(parts)); + } + } catch { + } + } + const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : []; + for (const node of svgImages) { + try { + const XLINK_NS = "http://www.w3.org/1999/xlink"; + const href = node.getAttribute("href") || node.getAttributeNS?.(XLINK_NS, "href"); + if (isBlobUrl(href)) { + const d = await blobUrlToDataUrl(href); + node.setAttribute("href", d); + node.removeAttributeNS?.(XLINK_NS, "href"); + } + } catch { + } + } + const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : []; + for (const el of styled) { + try { + const styleText = el.getAttribute("style"); + if (styleText && styleText.includes("blob:")) { + const replaced = await replaceBlobUrlsInCssText(styleText); + el.setAttribute("style", replaced); + } + } catch { + } + } + const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : []; + for (const s of styleTags) { + try { + const css = s.textContent || ""; + if (css.includes("blob:")) { + s.textContent = await replaceBlobUrlsInCssText(css); + } + } catch { + } + } + const urlAttrs = ["poster"]; + for (const attr of urlAttrs) { + const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : []; + for (const n of nodes) { + try { + const u = n.getAttribute(attr); + if (isBlobUrl(u)) { + n.setAttribute(attr, await blobUrlToDataUrl(u)); + } + } catch { + } + } + } +} + +// src/modules/images.js +async function inlineImages(clone, options = {}) { + const imgs = Array.from(clone.querySelectorAll("img")); + const processImg = async (img) => { + if (!img.getAttribute("src")) { + const eff = img.currentSrc || img.src || ""; + if (eff) img.setAttribute("src", eff); + } + img.removeAttribute("srcset"); + img.removeAttribute("sizes"); + const src = img.src; + try { + const dataUrl = await fetchImage(src, { useProxy: options.useProxy }); + img.src = dataUrl; + if (!img.width) img.width = img.naturalWidth || 100; + if (!img.height) img.height = img.naturalHeight || 100; + } catch { + const fallback = document.createElement("div"); + fallback.style = `width: ${img.width || 100}px; height: ${img.height || 100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${img.height || 100}px; color: #666; font-size: 12px;`; + fallback.innerText = "img"; + img.replaceWith(fallback); + } + }; + for (let i = 0; i < imgs.length; i += 4) { + const group = imgs.slice(i, i + 4).map(processImg); + await Promise.allSettled(group); + } +} + +// src/modules/background.js +async function inlineBackgroundImages(source, clone, styleCache, options = {}) { + const queue = [[source, clone]]; + const imageProps = [ + "background-image", + "mask", + "mask-image", + "-webkit-mask-image", + "mask-source", + "mask-box-image-source", + "mask-border-source", + "-webkit-mask-box-image-source", + "border-image", + "border-image-source", + "border-image-slice", + "border-image-width", + "border-image-outset", + "border-image-repeat" + ]; + while (queue.length) { + const [srcNode, cloneNode] = queue.shift(); + const style = styleCache.get(srcNode) || getStyle(srcNode); + if (!styleCache.has(srcNode)) styleCache.set(srcNode, style); + const hasBorderImage = (() => { + const bi = style.getPropertyValue("border-image"); + const bis = style.getPropertyValue("border-image-source"); + return bi && bi !== "none" || bis && bis !== "none"; + })(); + for (const prop of imageProps) { + if (["border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat"].includes(prop) && !hasBorderImage) { + continue; + } + const val = style.getPropertyValue(prop); + if (!val || val === "none") continue; + const splits = splitBackgroundImage(val); + const inlined = await Promise.all( + splits.map((entry) => inlineSingleBackgroundEntry(entry, options)) + ); + if (inlined.some((p) => p && p !== "none" && !/^url\(undefined/.test(p))) { + cloneNode.style.setProperty(prop, inlined.join(", ")); + } + } + const bgColor = style.getPropertyValue("background-color"); + if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") { + cloneNode.style.backgroundColor = bgColor; + } + const sChildren = Array.from(srcNode.children); + const cChildren = Array.from(cloneNode.children); + for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { + queue.push([sChildren[i], cChildren[i]]); + } + } +} + +// src/core/capture.js +async function captureDOM(element, options = {}) { + if (!element) throw new Error("Element cannot be null or undefined"); + cache.reset(); + const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "", localFonts = [] } = options; + let clone, classCSS, styleCache; + let fontsCSS = ""; + let baseCSS = ""; + let dataURL; + let svgString; + ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts, options)); + await new Promise((resolve) => { + idle(async () => { + await inlineImages(clone, options); + resolve(); + }, { fast }); + }); + await new Promise((resolve) => { + idle(async () => { + await inlineBackgroundImages(element, clone, styleCache, options); + resolve(); + }, { fast }); + }); + if (embedFonts) { + await new Promise((resolve) => { + idle(async () => { + fontsCSS = await embedCustomFonts({ localFonts, useProxy }); + resolve(); + }, { fast }); + }); + } + if (compress) { + const usedTags = collectUsedTagNames(clone).sort(); + const tagKey = usedTags.join(","); + if (cache.baseStyle.has(tagKey)) { + baseCSS = cache.baseStyle.get(tagKey); + } else { + await new Promise((resolve) => { + idle(() => { + baseCSS = generateDedupedBaseCSS(usedTags); + cache.baseStyle.set(tagKey, baseCSS); + resolve(); + }, { fast }); + }); + } + } + await new Promise((resolve) => { + idle(() => { + const rect = element.getBoundingClientRect(); + let w = rect.width; + let h = rect.height; + const hasW = Number.isFinite(options.width); + const hasH = Number.isFinite(options.height); + const hasScale = typeof scale === "number" && scale !== 1; + if (!hasScale) { + const aspect = rect.width / rect.height; + if (hasW && hasH) { + w = options.width; + h = options.height; + } else if (hasW) { + w = options.width; + h = w / aspect; + } else if (hasH) { + h = options.height; + w = h * aspect; + } + } + w = Math.ceil(w); + h = Math.ceil(h); + clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); + clone.style.transformOrigin = "top left"; + if (!hasScale && (hasW || hasH)) { + const originalW = rect.width; + const originalH = rect.height; + const scaleX = w / originalW; + const scaleY = h / originalH; + const existingTransform = clone.style.transform || ""; + const scaleTransform = `scale(${scaleX}, ${scaleY})`; + clone.style.transform = `${scaleTransform} ${existingTransform}`.trim(); + } + const svgNS = "http://www.w3.org/2000/svg"; + const fo = document.createElementNS(svgNS, "foreignObject"); + fo.setAttribute("width", "100%"); + fo.setAttribute("height", "100%"); + const styleTag = document.createElement("style"); + styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS; + fo.appendChild(styleTag); + fo.appendChild(clone); + const serializer = new XMLSerializer(); + const foString = serializer.serializeToString(fo); + const svgHeader = ``; + const svgFooter = ""; + svgString = svgHeader + foString + svgFooter; + dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`; + resolve(); + }, { fast }); + }); + const sandbox = document.getElementById("snapdom-sandbox"); + if (sandbox && sandbox.style.position === "absolute") sandbox.remove(); + return dataURL; +} + +// src/api/snapdom.js +async function toImg(url, { scale = 1 } = {}) { + const img = new Image(); + img.src = url; + await img.decode(); + if (scale !== 1) { + img.style.width = `${img.naturalWidth * scale}px`; + img.style.height = `${img.naturalHeight * scale}px`; + } + return img; +} +async function toCanvas(url, { dpr = 1, scale = 1 } = {}) { + const img = new Image(); + img.src = url; + img.crossOrigin = "anonymous"; + img.loading = "eager"; + img.decoding = "sync"; + const isSafariBrowser = isSafari(); + let appended = false; + if (isSafariBrowser) { + document.body.appendChild(img); + appended = true; + } + await img.decode(); + if (isSafariBrowser) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + if (img.width === 0 || img.height === 0) { + if (appended) img.remove(); + throw new Error("Image failed to load or has no dimensions"); + } + const width = img.naturalWidth * scale; + const height = img.naturalHeight * scale; + const canvas = document.createElement("canvas"); + canvas.width = Math.ceil(width * dpr); + canvas.height = Math.ceil(height * dpr); + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + const ctx = canvas.getContext("2d"); + ctx.scale(dpr, dpr); + ctx.drawImage(img, 0, 0, width, height); + if (appended) img.remove(); + return canvas; +} +async function toBlob(url, { + type = "svg", + scale = 1, + backgroundColor = "#fff", + quality +} = {}) { + const mime = { + jpg: "image/jpeg", + jpeg: "image/jpeg", + png: "image/png", + webp: "image/webp" + }[type] || "image/png"; + if (type === "svg") { + const svgText = decodeURIComponent(url.split(",")[1]); + return new Blob([svgText], { type: "image/svg+xml" }); + } + const canvas = await createBackground(url, { dpr: 1, scale }, backgroundColor); + return new Promise((resolve) => { + canvas.toBlob((blob) => resolve(blob), `${mime}`, quality); + }); +} +async function createBackground(url, { dpr = 1, scale = 1 }, backgroundColor) { + const baseCanvas = await toCanvas(url, { dpr, scale }); + if (!backgroundColor) return baseCanvas; + const temp = document.createElement("canvas"); + temp.width = baseCanvas.width; + temp.height = baseCanvas.height; + const ctx = temp.getContext("2d"); + ctx.fillStyle = backgroundColor; + ctx.fillRect(0, 0, temp.width, temp.height); + ctx.drawImage(baseCanvas, 0, 0); + return temp; +} +async function toRasterImg(url, { dpr = 1, scale = 1, backgroundColor, quality }, format = "png") { + const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0; + const finalBg = backgroundColor ?? defaultBg; + const canvas = await createBackground(url, { dpr, scale }, finalBg); + const img = new Image(); + img.src = canvas.toDataURL(`image/${format}`, quality); + await img.decode(); + img.style.width = `${canvas.width / dpr}px`; + img.style.height = `${canvas.height / dpr}px`; + return img; +} +async function download(url, { dpr = 1, scale = 1, backgroundColor, format = "png", filename = "snapDOM" } = {}) { + if (format === "svg") { + const blob = await toBlob(url); + const objectURL = URL.createObjectURL(blob); + const a2 = document.createElement("a"); + a2.href = objectURL; + a2.download = `${filename}.svg`; + a2.click(); + URL.revokeObjectURL(objectURL); + return; + } + const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0; + const finalBg = backgroundColor ?? defaultBg; + const canvas = await createBackground(url, { dpr, scale }, finalBg); + const mime = { + jpg: "image/jpeg", + jpeg: "image/jpeg", + png: "image/png", + webp: "image/webp" + }[format] || "image/png"; + const dataURL = canvas.toDataURL(mime); + const a = document.createElement("a"); + a.href = dataURL; + a.download = `${filename}.${format}`; + a.click(); +} +async function snapdom(element, options = {}) { + options = { scale: 1, ...options }; + if (!element) throw new Error("Element cannot be null or undefined"); + if (options.iconFonts) { + extendIconFonts(options.iconFonts); + } + return await snapdom.capture(element, options); +} +snapdom.capture = async (el, options = {}) => { + const url = await captureDOM(el, options); + const dpr = options.dpr ?? (window.devicePixelRatio || 1); + const scale = options.scale || 1; + return { + url, + options, + toRaw: () => url, + toImg: (opts = {}) => toImg(url, { dpr, scale, ...opts }), + toCanvas: (opts = {}) => toCanvas(url, { dpr, scale, ...opts }), + toBlob: (opts = {}) => toBlob(url, { dpr, scale, ...opts }), + toPng: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "png"), + toJpg: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "jpeg"), + toWebp: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "webp"), + download: ({ format = "png", filename = "snapDOM", backgroundColor, ...opts } = {}) => download(url, { dpr, scale, format, filename, backgroundColor, ...opts }) + }; +}; +snapdom.toRaw = async (el, options) => (await snapdom.capture(el, options)).toRaw(); +snapdom.toImg = async (el, options) => (await snapdom.capture(el, options)).toImg(); +snapdom.toCanvas = async (el, options) => (await snapdom.capture(el, options)).toCanvas(); +snapdom.toBlob = async (el, options) => (await snapdom.capture(el, options)).toBlob(options); +snapdom.toPng = async (el, options) => (await snapdom.capture(el, options)).toPng(options); +snapdom.toJpg = async (el, options) => (await snapdom.capture(el, options)).toJpg(options); +snapdom.toWebp = async (el, options) => (await snapdom.capture(el, options)).toWebp(options); +snapdom.download = async (el, options = {}) => { + const { + format = "png", + filename = "capture", + backgroundColor, + ...rest + } = options; + const capture = await snapdom.capture(el, rest); + return await capture.download({ format, filename, backgroundColor }); +}; + +// src/api/preCache.js +async function preCache(root = document, options = {}) { + const { embedFonts = true, reset = false, useProxy } = options; + if (reset) { + cache.image.clear(); + cache.background.clear(); + cache.resource.clear(); + cache.defaultStyle.clear(); + cache.baseStyle.clear(); + cache.font.clear(); + cache.computedStyle = /* @__PURE__ */ new WeakMap(); + return; + } + try { + await document.fonts.ready; + } catch { + } + precacheCommonTags(); + let imgEls = [], allEls = []; + if (root?.querySelectorAll) { + imgEls = Array.from(root.querySelectorAll("img[src]")); + allEls = Array.from(root.querySelectorAll("*")); + } + const promises = []; + for (const img of imgEls) { + const src = img?.src; + if (!src) continue; + if (!cache.image.has(src)) { + const p = Promise.resolve().then(() => fetchImage(src, { useProxy })).then((dataURL) => { + cache.image.set(src, dataURL); + }).catch(() => { + }); + promises.push(p); + } + } + for (const el of allEls) { + let bg = ""; + try { + bg = getStyle(el).backgroundImage; + } catch { + } + if (bg && bg !== "none") { + const bgSplits = splitBackgroundImage(bg); + for (const entry of bgSplits) { + if (entry.startsWith("url(")) { + const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, { ...options, useProxy })).catch(() => { + }); + promises.push(p); + } + } + } + } + if (embedFonts) { + try { + await embedCustomFonts({ preCached: true, localFonts: options.localFonts, useProxy: options.useProxy }); + } catch { + } + ; + } + await Promise.allSettled(promises); +} +export { + preCache, + snapdom +}; From d8a7a7574f56d2719672bea57c607e9a464df10c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 19 Aug 2025 15:54:48 +0800 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20AddBootstrap?= =?UTF-8?q?BlazorDom2ImageService=20=E6=9C=8D=E5=8A=A1=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ServiceCollectionExtensions.cs | 26 ++++++ .../Services/DefaultDom2ImageService.cs | 62 +++++++++++++ .../Services/Dom2ImageOptions.cs | 86 +++++++++++++++++++ .../Services/IDom2ImageService.cs | 29 +++++++ 4 files changed, 203 insertions(+) create mode 100644 src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs create mode 100644 src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs create mode 100644 src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs create mode 100644 src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs diff --git a/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs b/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..18757adb --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using BootstrapBlazor.Components; + +namespace Microsoft.Extensions.DependencyInjection; + +/// +/// BootstrapBlazor 服务扩展类 +/// +public static class BootstrapBlazorHtml2PdfServiceExtensions +{ + /// + /// 添加 AzureOpenAIService 服务 + /// + /// + public static IServiceCollection AddBootstrapBlazorDom2ImageService(this IServiceCollection services) + { + services.AddScoped(); +#if NET8_0_OR_GREATER + services.AddKeyedScoped("BootstrapBlazor.Dom2Image"); +#endif + return services; + } +} diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs new file mode 100644 index 00000000..8ffe6842 --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs @@ -0,0 +1,62 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using Microsoft.Extensions.Logging; +using Microsoft.JSInterop; + +namespace BootstrapBlazor.Components; + +/// +/// 默认 Html to Image 实现 +/// +/// +/// +class DefaultDom2ImageService(IJSRuntime runtime, ILogger logger) : IDom2ImageService +{ + private JSModule? _jsModule; + + /// + /// + /// + public async Task GetUrlAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default) + { + string? data = null; + try + { + _jsModule ??= await LoadModule(); + data = await _jsModule.InvokeAsync("getUrl", token, selector, options); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + logger.LogError(ex, "{GetUrlAsync} throw exception", nameof(GetUrlAsync)); + } + return data; + } + + /// + /// + /// + public async Task GetStreamAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default) + { + Stream? data = null; + try + { + _jsModule ??= await LoadModule(); + var streamReference = await _jsModule.InvokeAsync("getStream", selector, options); + if (streamReference != null) + { + data = await streamReference.OpenReadStreamAsync(streamReference.Length, token); + } + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + logger.LogError(ex, "{GetUrlAsync} throw exception", nameof(GetUrlAsync)); + } + return data; + } + + private Task LoadModule() => runtime.LoadModule("./_content/BootstrapBlazor.Dom2Image/dom2image.js"); +} diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs b/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs new file mode 100644 index 00000000..088256f6 --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +using System.Text.Json.Serialization; + +namespace BootstrapBlazor.Components; + +/// +/// Dom2ImageOptions 选项类 +/// +public class Dom2ImageOptions +{ + /// + /// Removes redundant styles. Default value is true + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Compress { get; set; } + + /// + /// Skips idle delay for faster results. Default value is true + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Fast { get; set; } + + /// + /// Inlines fonts (icon fonts always embedded). Default value is false + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? EmbedFonts { get; set; } + + /// + /// Output scale multiplier. Default value is 1 + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Scale { get; set; } + + /// + /// Device pixel ratio + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Dpr { get; set; } + + /// + /// Output specific width size + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Width { get; set; } + + /// + /// Output specific height size + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Height { get; set; } + + /// + /// Fallback color for JPG/WebP. Default value is #fff + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? BackgroundColor { get; set; } + + /// + /// Quality for JPG/WebP (0 to 1) + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? Quality { get; set; } + + /// + /// Select png, jpg, webp Blob type. Default value is svg + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Type { get; set; } + + /// + /// CSS selectors for elements to exclude + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[]? Exclude { get; set; } + + /// + /// + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[]? LocalFonts { get; set; } +} diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs new file mode 100644 index 00000000..2c3b3da8 --- /dev/null +++ b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs @@ -0,0 +1,29 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +namespace BootstrapBlazor.Components; + +/// +/// IDom2ImageService 接口定义 +/// +public interface IDom2ImageService +{ + /// + /// 通过指定选择器获得 Html 元素返回图片数据 + /// + /// + /// + /// + /// + Task GetUrlAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default); + + /// + /// 通过指定选择器获得 Html 元素返回图片数据流 + /// + /// + /// + /// + /// + Task GetStreamAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default); +} From 80a50e2925c0d690cc9e22fb6e10def322534ac9 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 19 Aug 2025 19:27:02 +0800 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20DownloadAsyn?= =?UTF-8?q?c=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/DefaultDom2ImageService.cs | 27 +++++++++++++++++-- .../Services/IDom2ImageService.cs | 11 ++++++++ .../wwwroot/dom2image.js | 14 ++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs index 8ffe6842..164946f0 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs @@ -30,7 +30,7 @@ class DefaultDom2ImageService(IJSRuntime runtime, ILogger + /// + /// + /// + /// + /// + /// + /// + /// + public async Task DownloadAsync(string selector, string fileName = "capture", string? format = "png", string? backgroundColor = null, Dom2ImageOptions? options = null) + { + try + { + _jsModule ??= await LoadModule(); + await _jsModule.InvokeAsync("downloadAsync", selector, fileName, format, backgroundColor, options); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + logger.LogError(ex, "{DownloadAsync} throw exception: {ex}", nameof(DownloadAsync), ex.Format()); + } + } + private Task LoadModule() => runtime.LoadModule("./_content/BootstrapBlazor.Dom2Image/dom2image.js"); } diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs index 2c3b3da8..fb0b8fb2 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs @@ -26,4 +26,15 @@ public interface IDom2ImageService /// /// Task GetStreamAsync(string selector, Dom2ImageOptions? options = null, CancellationToken token = default); + + /// + /// 通过指定选择器下载 Html 元素图片 + /// + /// + /// + /// + /// + /// + /// + Task DownloadAsync(string selector, string fileName = "capture", string? format = "png", string? backgroundColor = null, Dom2ImageOptions? options = null); } diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js index f357fc08..82ccc34f 100644 --- a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js @@ -4,7 +4,6 @@ export async function getUrl(selector, options = {}) { let data = null; const el = document.querySelector(selector); if (el) { - options.embedFonts = true; const result = await snapdom(el, options); data = result.url; } @@ -15,9 +14,20 @@ export async function getStream(selector, options = {}) { let data = null; const el = document.querySelector(selector); if (el) { - options.embedFonts = true; const result = await snapdom(el, options); data = result.toBlob(); } return data; } + +export async function downloadAsync(selector, filename, format, backgroundColor, options) { + const el = document.querySelector(selector); + if (el) { + const result = await snapdom(el, options); + data = result.download({ + format, + filename, + backgroundColor + }); + } +} From 380333e38c6d185c08cd59c64fb96c22eb9dae52 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Tue, 19 Aug 2025 20:40:01 +0800 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=20await=20?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js index 82ccc34f..0ece208f 100644 --- a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js @@ -24,7 +24,7 @@ export async function downloadAsync(selector, filename, format, backgroundColor, const el = document.querySelector(selector); if (el) { const result = await snapdom(el, options); - data = result.download({ + await result.download({ format, filename, backgroundColor From 1d5018150580d6d52c9a1297c10f6ecaf1d7582e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 20 Aug 2025 14:11:50 +0800 Subject: [PATCH 06/10] chore: bump version 9.0.0 --- .../BootstrapBlazor.Dom2Image.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj index 1f1faa6f..5ebbb5d0 100644 --- a/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj +++ b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj @@ -1,7 +1,7 @@  - 9.0.0-beta01 + 9.0.0 @@ -10,12 +10,12 @@ - - + + - + From 02a1cbcd0dec50e5ca90789bcd563c68ad0117c4 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 12 Nov 2025 11:53:18 +0800 Subject: [PATCH 07/10] =?UTF-8?q?chore:=20=E5=A2=9E=E5=8A=A0=20Dom2Image?= =?UTF-8?q?=20=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BootstrapBlazor.Extensions.slnx | 1 + 1 file changed, 1 insertion(+) diff --git a/BootstrapBlazor.Extensions.slnx b/BootstrapBlazor.Extensions.slnx index 12ec224b..375498b5 100644 --- a/BootstrapBlazor.Extensions.slnx +++ b/BootstrapBlazor.Extensions.slnx @@ -29,6 +29,7 @@ + From 0d5295b52d5f90e8e3f973ccbb9afb262b14a115 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 12 Nov 2025 12:47:30 +0800 Subject: [PATCH 08/10] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wwwroot/dom2image.js | 12 +- .../wwwroot/lib/snapdom.min.mjs | 11 +- .../wwwroot/lib/snapdom.mjs | 1781 ----------------- 3 files changed, 14 insertions(+), 1790 deletions(-) delete mode 100644 src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js index 0ece208f..e11bf5ef 100644 --- a/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/dom2image.js @@ -1,20 +1,20 @@ -import { snapdom } from './lib/snapdom.min.mjs' +import { snapdom } from './lib/snapdom.min.mjs' -export async function getUrl(selector, options = {}) { +export async function getUrl(selector, options) { let data = null; const el = document.querySelector(selector); if (el) { - const result = await snapdom(el, options); + const result = await snapdom(el, options || {}); data = result.url; } return data; } -export async function getStream(selector, options = {}) { +export async function getStream(selector, options) { let data = null; const el = document.querySelector(selector); if (el) { - const result = await snapdom(el, options); + const result = await snapdom(el, options || {}); data = result.toBlob(); } return data; @@ -23,7 +23,7 @@ export async function getStream(selector, options = {}) { export async function downloadAsync(selector, filename, format, backgroundColor, options) { const el = document.querySelector(selector); if (el) { - const result = await snapdom(el, options); + const result = await snapdom(el, options || {}); await result.download({ format, filename, diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs index 2415087e..61929a31 100644 --- a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs +++ b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.min.mjs @@ -1,3 +1,8 @@ -var h={image:new Map,background:new Map,resource:new Map,defaultStyle:new Map,baseStyle:new Map,computedStyle:new WeakMap,font:new Set,snapshot:new WeakMap,snapshotKey:new Map,reset:Wt};function Wt(){h.computedStyle=new WeakMap}var _t=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function yt(){for(let t of _t)wt(t)}function wt(t){if(h.defaultStyle.has(t))return h.defaultStyle.get(t);if(new Set(["script","style","meta","link","noscript","template","defs","symbol","title","metadata","desc"]).has(t)){let a={};return h.defaultStyle.set(t,a),a}let n=document.getElementById("snapdom-sandbox");n||(n=document.createElement("div"),n.id="snapdom-sandbox",n.style.position="absolute",n.style.left="-9999px",n.style.top="-9999px",n.style.width="0",n.style.height="0",n.style.overflow="hidden",document.body.appendChild(n));let i=document.createElement(t);i.style.all="initial",n.appendChild(i);let r=getComputedStyle(i),c={};for(let a of r)c[a]=r.getPropertyValue(a);return n.removeChild(i),h.defaultStyle.set(t,c),c}var jt=new Set(["-webkit-locale"]);function K(t,e,n=!1){let i=[],r=wt(e);for(let[c,a]of Object.entries(t))if(!jt.has(c))if(!n)a&&i.push(`${c}:${a}`);else{let f=r[c];a&&a!==f&&i.push(`${c}:${a}`)}return i.sort().join(";")}function bt(t){let e=new Set;return t.nodeType!==Node.ELEMENT_NODE&&t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(t.tagName&&e.add(t.tagName.toLowerCase()),typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(n=>e.add(n.tagName.toLowerCase())),Array.from(e))}function St(t){let e=new Map;for(let i of t){let r=h.defaultStyle.get(i);if(!r)continue;let c=Object.entries(r).map(([a,f])=>`${a}:${f};`).sort().join("");e.has(c)||e.set(c,[]),e.get(c).push(i)}let n="";for(let[i,r]of e.entries())n+=`${r.join(",")} { ${i} } -`;return n}function xt(t){let e=new Set(t.values()),n=new Map,i=1;for(let r of e)r.trim()&&n.set(r,`c${i++}`);return n}async function O(t,e={}){let n=X(t),i=/^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(t);if(n){let r=G(n);if(h.background.has(r))return e.skipInline?void 0:`url(${h.background.get(r)})`;{let c=await W(r,{useProxy:e.useProxy});return h.background.set(r,c),e.skipInline?void 0:`url("${c}")`}}return t}function q(t,{fast:e=!1}={}){if(e)return t();"requestIdleCallback"in window?requestIdleCallback(t,{timeout:50}):setTimeout(t,1)}function U(t,e=null){if(!(t instanceof Element))return window.getComputedStyle(t,e);let n=h.computedStyle.get(t);if(n||(n=new Map,h.computedStyle.set(t,n)),!n.has(e)){let i=window.getComputedStyle(t,e);n.set(e,i)}return n.get(e)}function Ct(t){let e=t.replace(/^['"]|['"]$/g,"");if(e.startsWith("\\"))try{return String.fromCharCode(parseInt(e.replace("\\",""),16))}catch{return e}return e}function X(t){let e=t.match(/url\((['"]?)(.*?)(\1)\)/);if(!e)return null;let n=e[2].trim();return n.startsWith("#")?null:n}async function V(t,{useProxy:e=""}={}){async function n(i){let r=await fetch(i);if(!r.ok)throw new Error(`[snapdom] Failed to fetch resource: ${i}`);return r}try{return await n(t)}catch(i){if(e&&typeof e=="string"){let r=e.replace(/\/$/,"")+G(t);return n(r)}throw i}}var j=new Map,at=new Map;function W(t,{timeout:e=3e3,useProxy:n="",errorTTL:i=8e3}={}){function r(u){try{return new URL(u,window.location.href).origin===window.location.origin?"use-credentials":"anonymous"}catch{return"anonymous"}}let c=u=>({ok:!0,data:u}),a=u=>({ok:!1,error:u instanceof Error?u:new Error(String(u))});function f(u){try{return fetch(u,{mode:"cors",credentials:r(u)==="use-credentials"?"include":"omit"}).then(g=>g.ok?g.blob().then(b=>new Promise(p=>{let w=new FileReader;w.onloadend=()=>{let S=w.result;typeof S!="string"||!S.startsWith("data:image/")?p(a(new Error("Invalid image data URL"))):p(c(S))},w.onerror=()=>p(a(new Error("FileReader error"))),w.readAsDataURL(b)})):a(new Error("HTTP "+g.status))).catch(g=>a(g))}catch(g){return Promise.resolve(a(g))}}function o(u){return f(u).then(g=>{if(g.ok)return g;if(n&&typeof n=="string"){let b=n.replace(/\/$/,"")+G(u);return f(b).then(p=>p.ok?p:a(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")))}return a(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided"))})}let l=Date.now(),s=at.get(t);if(s&&s>l){let u=Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown)."));return u.catch(()=>{}),u}if(j.has(t))return j.get(t);let d=r(t);if(h.image.has(t))return Promise.resolve(h.image.get(t));if(t.startsWith("data:image/"))return h.image.set(t,t),Promise.resolve(t);if(/\.svg(\?.*)?$/i.test(t)){let u=(async()=>{let g=await(async()=>{try{let p=await fetch(t,{mode:"cors",credentials:d==="use-credentials"?"include":"omit"});if(!p.ok)return a(new Error("HTTP "+p.status));let w=await p.text();return c(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(w)}`)}catch(p){return a(p)}})();if(g.ok)return h.image.set(t,g.data),g.data;let b=await o(t);return b.ok?(h.image.set(t,b.data),b.data):(at.set(t,l+i),Promise.reject(b.error))})();return j.set(t,u),u.finally(()=>j.delete(t)),u.catch(()=>{}),u}let m=new Promise((u,g)=>{let b=!1,p=new Image,w=y=>k=>{b||(b=!0,clearTimeout(R),p.onload=p.onerror=null,y(k))},S=y=>{h.image.set(t,y),u(y)},x=y=>{at.set(t,Date.now()+i),g(y)},R=setTimeout(w(()=>{o(t).then(y=>{y.ok?S(y.data):x(new Error("Image load timed out"))})}),e);p.crossOrigin=d,p.onload=w(()=>{Promise.resolve(p.decode()).then(()=>{try{let y=document.createElement("canvas");y.width=p.naturalWidth||p.width,y.height=p.naturalHeight||p.height,y.getContext("2d").drawImage(p,0,0,y.width,y.height),S(y.toDataURL("image/png"))}catch{o(t).then(y=>{y.ok?S(y.data):x(y.error)})}}).catch(()=>{o(t).then(y=>{y.ok?S(y.data):x(y.error)})})}),p.onerror=w(()=>{o(t).then(y=>{y.ok?S(y.data):x(y.error)})}),p.src=t});return j.set(t,m),m.finally(()=>j.delete(t)),m.catch(()=>{}),m}function st(t){let e={};for(let n of t)e[n]=t.getPropertyValue(n);return e}function vt(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function Et(t){if(!t||t==="none")return"";let e=t.replace(/translate[XY]?\([^)]*\)/g,"");return e=e.replace(/matrix\(([^)]+)\)/g,(n,i)=>{let r=i.split(",").map(c=>c.trim());return r.length!==6?`matrix(${i})`:(r[4]="0",r[5]="0",`matrix(${r.join(", ")})`)}),e=e.replace(/matrix3d\(([^)]+)\)/g,(n,i)=>{let r=i.split(",").map(c=>c.trim());return r.length!==16?`matrix3d(${i})`:(r[12]="0",r[13]="0",`matrix3d(${r.join(", ")})`)}),e.trim().replace(/\s{2,}/g," ")}function G(t){if(/%[0-9A-Fa-f]{2}/.test(t))return t;try{return encodeURI(t)}catch{return t}}function H(t){let e=[],n=0,i=0;for(let r=0;rs.localeCompare(d)).map(([s,d])=>`${s}:${d}`).join(";");if(ct.has(f)){n.set(e,ct.get(f));return}let o=t.tagName?.toLowerCase()||"div",l=K(a,o,r);ct.set(f,l),n.set(e,l)}function qt(t,e){try{let n=t.currentSrc||t.src||"";if(!n)return;e.setAttribute("src",n),e.removeAttribute("srcset"),e.removeAttribute("sizes"),e.loading="eager",e.decoding="sync"}catch{}}function J(t,e,n,i,r,c={},a){if(!t)throw new Error("Invalid node");let f=new Set,o=null;if(t.nodeType===Node.TEXT_NODE||t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);if(t.getAttribute("data-capture")==="exclude"){let s=document.createElement("div"),d=t.getBoundingClientRect();return s.style.cssText=`display:inline-block;width:${d.width}px;height:${d.height}px;visibility:hidden;`,s}if(c.exclude&&Array.isArray(c.exclude))for(let s of c.exclude)try{if(t.matches?.(s)){let d=document.createElement("div"),m=t.getBoundingClientRect();return d.style.cssText=`display:inline-block;width:${m.width}px;height:${m.height}px;visibility:hidden;`,d}}catch(d){console.warn(`Invalid selector in exclude option: ${s}`,d)}if(typeof c.filter=="function")try{if(!c.filter(t,a||t)){let s=document.createElement("div"),d=t.getBoundingClientRect();return s.style.cssText=`display:inline-block;width:${d.width}px;height:${d.height}px;visibility:hidden;`,s}}catch(s){console.warn("Error in filter function:",s)}if(t.tagName==="IFRAME"){let s=document.createElement("div");return s.style.cssText=`width:${t.offsetWidth}px;height:${t.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`,s}if(t.getAttribute("data-capture")==="placeholder"){let s=t.cloneNode(!1);i.set(s,t),et(t,s,e,n,r);let d=document.createElement("div");return d.textContent=t.getAttribute("data-placeholder-text")||"",d.style.cssText="color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;",s.appendChild(d),s}if(t.tagName==="CANVAS"){let s=t.toDataURL(),d=document.createElement("img");return d.src=s,d.width=t.width,d.height=t.height,i.set(d,t),et(t,d,e,n,r),d}let l;try{l=t.cloneNode(!1),i.set(l,t),t.tagName==="IMG"&&qt(t,l)}catch(s){throw console.error("[Snapdom] Failed to clone node:",t,s),s}if(t instanceof HTMLTextAreaElement){l.textContent=t.value,l.value=t.value;let s=t.getBoundingClientRect();return l.style.width=`${s.width}px`,l.style.height=`${s.height}px`,l}if(t instanceof HTMLInputElement&&(l.value=t.value,l.setAttribute("value",t.value),t.checked!==void 0&&(l.checked=t.checked,t.checked&&l.setAttribute("checked",""),t.indeterminate&&(l.indeterminate=t.indeterminate))),t instanceof HTMLSelectElement&&(o=t.value),et(t,l,e,n,r),t.shadowRoot)if(Array.from(t.shadowRoot.querySelectorAll("slot")).length>0){for(let d of t.shadowRoot.childNodes)if(d.nodeType===Node.ELEMENT_NODE&&d.tagName==="STYLE"){let m=d.textContent||"";m.trim()&&r&&n.set(d,m)}}else{let d=document.createDocumentFragment();for(let m of t.shadowRoot.childNodes){if(m.nodeType===Node.ELEMENT_NODE&&m.tagName==="STYLE"){let g=m.textContent||"";g.trim()&&r&&n.set(m,g);continue}let u=J(m,e,n,i,r,c,a||t);u&&d.appendChild(u)}l.appendChild(d)}if(t.tagName==="SLOT"){let s=t.assignedNodes?.({flatten:!0})||[],d=s.length>0?s:Array.from(t.childNodes),m=document.createDocumentFragment();for(let u of d){let g=J(u,e,n,i,r,c,a||t);g&&m.appendChild(g)}return m}for(let s of t.childNodes){if(f.has(s))continue;let d=J(s,e,n,i,r,c,a||t);d&&l.appendChild(d)}if(o!==null&&l instanceof HTMLSelectElement){l.value=o;for(let s of l.options)s.value===o?s.setAttribute("selected",""):s.removeAttribute("selected")}return l}var Vt=[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i,/layui/i,/lucide/i],lt=[];function At(t){let e=Array.isArray(t)?t:[t];for(let n of e)n instanceof RegExp?lt.push(n):typeof n=="string"?lt.push(new RegExp(n,"i")):console.warn("[snapdom] Ignored invalid iconFont value:",n)}function L(t){let e=typeof t=="string"?t:"",n=[...Vt,...lt];for(let i of n)if(i instanceof RegExp&&i.test(e))return!0;return!!(/icon/i.test(e)||/glyph/i.test(e)||/symbols/i.test(e)||/feather/i.test(e)||/fontawesome/i.test(e))}async function $t(t,e,n,i=32,r="#000"){e=e.replace(/^['"]+|['"]+$/g,"");let c=window.devicePixelRatio||1;await document.fonts.ready;let a=document.createElement("span");a.textContent=t,a.style.position="absolute",a.style.visibility="hidden",a.style.fontFamily=`"${e}"`,a.style.fontWeight=n||"normal",a.style.fontSize=`${i}px`,a.style.lineHeight="1",a.style.whiteSpace="nowrap",a.style.padding="0",a.style.margin="0",document.body.appendChild(a);let f=a.getBoundingClientRect(),o=Math.ceil(f.width),l=Math.ceil(f.height);document.body.removeChild(a);let s=document.createElement("canvas");s.width=o*c,s.height=l*c;let d=s.getContext("2d");return d.scale(c,c),d.font=n?`${n} ${i}px "${e}"`:`${i}px "${e}"`,d.textAlign="left",d.textBaseline="top",d.fillStyle=r,d.fillText(t,0,0),{dataUrl:s.toDataURL(),width:o,height:l}}function kt(t){return Array.from(document.styleSheets).some(e=>e.href===t)}function Ht(t){return new Promise(e=>{if(kt(t))return e(null);let n=document.createElement("link");n.rel="stylesheet",n.href=t,n.setAttribute("data-snapdom","injected-import"),n.onload=()=>e(n),n.onerror=()=>e(null),document.head.appendChild(n)})}async function nt({preCached:t=!1,localFonts:e=[],useProxy:n=""}={}){if(h.resource.has("fonts-embed-css")){if(t){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=h.resource.get("fonts-embed-css"),document.head.appendChild(o)}return h.resource.get("fonts-embed-css")}let i=new Set;try{for(let o of document.fonts)o.status==="loaded"&&i.add(`${o.family}__${o.weight||"normal"}__${o.style||"normal"}`)}catch{}let r=/@import\s+url\(["']?([^"')]+)["']?\)/g,c=[];for(let o of document.querySelectorAll("style")){let l=o.textContent||"",s=Array.from(l.matchAll(r));for(let d of s){let m=d[1];L(m)||kt(m)||c.push(m)}}await Promise.all(c.map(Ht));let a=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(o=>o.href),f="";for(let o of a)try{let s=await(await V(o.href,{useProxy:n})).text();if(L(o.href)||L(s))continue;let d=/@font-face[^{}]*{[^}]*}/g,m=s;for(let u of s.match(d)||[]){let g=u.match(/font-family:\s*([^;]+);/i);if(!g)continue;let b=g[1].replace(/['"]/g,"").trim(),p=u.match(/font-weight:\s*([^;]+);/i),w=u.match(/font-style:\s*([^;]+);/i),S=p?p[1].trim():"normal",x=w?w[1].trim():"normal",R=`${b}__${S}__${x}`,y=/url\((["']?)([^"')]+)\1\)/g,k=/url\(/i.test(u),C=/local\(/i.test(u);if(!k&&C)continue;if(!i.has(R)){m=m.replace(u,"");continue}let A=u,P=Array.from(u.matchAll(y));for(let N of P){let T=X(N[0]);if(!T)continue;let v=T;if(!v.startsWith("http")&&!v.startsWith("data:")&&(v=new URL(v,o.href).href),!L(v)){if(h.resource.has(v)){h.font.add(v),A=A.replace(N[0],`url(${h.resource.get(v)})`);continue}if(!h.font.has(v))try{let B=await(await V(v,{useProxy:n})).blob(),Y=await new Promise($=>{let M=new FileReader;M.onload=()=>$(M.result),M.readAsDataURL(B)});h.resource.set(v,Y),h.font.add(v),A=A.replace(N[0],`url(${Y})`)}catch{console.warn("[snapdom] Failed to fetch font resource:",v)}}}m=m.replace(u,A)}f+=m+` -`}catch{console.warn("[snapdom] Failed to fetch CSS:",o.href)}for(let o of document.styleSheets)try{if(!o.href||a.every(l=>l.href!==o.href)){for(let l of o.cssRules)if(l.type===CSSRule.FONT_FACE_RULE){let s=l.style.getPropertyValue("src"),d=l.style.getPropertyValue("font-family");if(!s||L(d))continue;let m=l.style.getPropertyValue("font-weight")||"normal",u=l.style.getPropertyValue("font-style")||"normal",g=`${d}__${m}__${u}`,b=/url\((["']?)([^"')]+)\1\)/g,p=/local\((["']?)[^)]+?\1\)/g,w=!!s.match(b),S=!!s.match(p);if(!w&&S){f+=`@font-face{font-family:${d};src:${s};font-style:${u};font-weight:${m};}`;continue}if(!i.has(g))continue;let x=s,R=Array.from(s.matchAll(b));for(let y of R){let k=y[2].trim();if(!k)continue;let C=k;if(!C.startsWith("http")&&!C.startsWith("data:")&&(C=new URL(C,o.href||location.href).href),!L(C)){if(h.resource.has(C)){h.font.add(C),x=x.replace(y[0],`url(${h.resource.get(C)})`);continue}if(!h.font.has(C))try{let P=await(await V(C,{useProxy:n})).blob(),N=await new Promise(T=>{let v=new FileReader;v.onload=()=>T(v.result),v.readAsDataURL(P)});h.resource.set(C,N),h.font.add(C),x=x.replace(y[0],`url(${N})`)}catch{console.warn("[snapdom] Failed to fetch font URL:",C)}}}f+=`@font-face{font-family:${d};src:${x};font-style:${u};font-weight:${m};}`}}}catch(l){console.warn("[snapdom] Cannot access stylesheet",o.href,l)}for(let o of document.fonts)if(o.family&&o.status==="loaded"&&o._snapdomSrc){if(L(o.family))continue;let l=o._snapdomSrc;if(!l.startsWith("data:")){if(h.resource.has(o._snapdomSrc))l=h.resource.get(o._snapdomSrc),h.font.add(o._snapdomSrc);else if(!h.font.has(o._snapdomSrc))try{let d=await(await V(o._snapdomSrc,{useProxy:n})).blob();l=await new Promise(m=>{let u=new FileReader;u.onload=()=>m(u.result),u.readAsDataURL(d)}),h.resource.set(o._snapdomSrc,l),h.font.add(o._snapdomSrc)}catch{console.warn("[snapdom] Failed to fetch dynamic font src:",o._snapdomSrc);continue}}f+=`@font-face{font-family:'${o.family}';src:url(${l});font-style:${o.style||"normal"};font-weight:${o.weight||"normal"};}`}for(let o of e){if(!o||typeof o!="object")continue;let{family:l,src:s,weight:d="normal",style:m="normal"}=o;if(!l||!s)continue;let u=s;if(u.startsWith("data:"))h.resource.set(s,u),h.font.add(s);else try{let b=await(await V(s,{useProxy:n})).blob();u=await new Promise(p=>{let w=new FileReader;w.onload=()=>p(w.result),w.readAsDataURL(b)}),h.resource.set(s,u),h.font.add(s)}catch{console.warn("[snapdom] Failed to load local font:",s);continue}f+=`@font-face{font-family:'${l}';src:url(${u});font-style:${m};font-weight:${d};}`}if(f&&(h.resource.set("fonts-embed-css",f),t)){let o=document.createElement("style");o.setAttribute("data-snapdom","embedFonts"),o.textContent=f,document.head.appendChild(o)}return f}async function ft(t,e,n,i,r){if(!(t instanceof Element)||!(e instanceof Element))return;for(let f of["::before","::after","::first-letter"])try{let o=U(t,f);if(!o||typeof o[Symbol.iterator]!="function"||o.content==="none"&&o.backgroundImage==="none"&&o.backgroundColor==="transparent"&&(o.borderStyle==="none"||parseFloat(o.borderWidth)===0)&&(!o.transform||o.transform==="none")&&o.display==="inline")continue;if(f==="::first-letter"){let I=getComputedStyle(t);if(!(o.color!==I.color||o.fontSize!==I.fontSize||o.fontWeight!==I.fontWeight))continue;let D=Array.from(e.childNodes).find(pt=>pt.nodeType===Node.TEXT_NODE&&pt.textContent?.trim().length>0);if(!D)continue;let _=D.textContent,Z=_.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u)?.[0],Ut=_.slice(Z?.length||0);if(!Z||/[\uD800-\uDFFF]/.test(Z))continue;let tt=document.createElement("span");tt.textContent=Z,tt.dataset.snapdomPseudo="::first-letter";let Mt=st(o),Dt=K(Mt,"span",r);n.set(tt,Dt);let gt=document.createTextNode(Ut);e.replaceChild(gt,D),e.insertBefore(tt,gt);continue}let s=o.content,d=/counter\s*\(|counters\s*\(/.test(s)?"- ":Ct(s),m=o.backgroundImage,u=o.backgroundColor,g=o.fontFamily,b=parseInt(o.fontSize)||32,p=parseInt(o.fontWeight)||!1,w=o.color||"#000",S=o.display,x=parseFloat(o.width),R=parseFloat(o.height),y=o.borderStyle,k=parseFloat(o.borderWidth),C=o.transform,A=L(g),P=s!=="none"&&d!=="",N=m&&m!=="none",T=u&&u!=="transparent"&&u!=="rgba(0, 0, 0, 0)",v=S!=="inline"&&(x>0||R>0),z=y&&y!=="none"&&k>0,B=C&&C!=="none";if(!(P||N||T||v||z||B))continue;let $=document.createElement("span");$.dataset.snapdomPseudo=f,$.style.verticalAlign="middle";let M=st(o),rt=K(M,"span",r);if(n.set($,rt),A&&d.length===1){let{dataUrl:I,width:F,height:D}=await $t(d,g,p,b,w),_=document.createElement("img");_.src=I,_.style=`height:${b}px;width:${F/D*b}px;object-fit:contain;`,$.appendChild(_),e.dataset.snapdomHasIcon="true"}else if(d.startsWith("url(")){let I=X(d);if(I?.trim())try{let F=document.createElement("img"),D=await W(G(I),r);F.src=D,F.style=`width:${b}px;height:auto;object-fit:contain;`,$.appendChild(F)}catch(F){console.error(`[snapdom] Error in pseudo ${f} for`,t,F)}}else!A&&P&&($.textContent=d);if(N)try{let I=H(m),F=await Promise.all(I.map(O));$.style.backgroundImage=F.join(", ")}catch(I){console.warn(`[snapdom] Failed to inline background-image for ${f}`,I)}if(T&&($.style.backgroundColor=u),!($.childNodes.length>0||$.textContent?.trim()!==""||N||T||v||z||B))continue;f==="::before"?e.insertBefore($,e.firstChild):e.appendChild($)}catch(o){console.warn(`[snapdom] Failed to capture ${f} for`,t,o)}let c=Array.from(t.children),a=Array.from(e.children).filter(f=>!f.dataset.snapdomPseudo);for(let f=0;f{let o=f.getAttribute("xlink:href")||f.getAttribute("href");o&&o.startsWith("#")&&e.add(o.slice(1))}),!e.size)return;let n=Array.from(document.querySelectorAll("svg > symbol, svg > defs")),i=n.filter(f=>f.tagName.toLowerCase()==="symbol"),r=n.filter(f=>f.tagName.toLowerCase()==="defs"),c=t.querySelector("svg.inline-defs-container");c||(c=document.createElementNS("http://www.w3.org/2000/svg","svg"),c.setAttribute("aria-hidden","true"),c.setAttribute("style","position: absolute; width: 0; height: 0; overflow: hidden;"),c.classList.add("inline-defs-container"),t.insertBefore(c,t.firstChild));let a=new Set;t.querySelectorAll("symbol[id], defs > *[id]").forEach(f=>{a.add(f.id)}),e.forEach(f=>{if(a.has(f))return;let o=i.find(l=>l.id===f);if(o){c.appendChild(o.cloneNode(!0)),a.add(f);return}for(let l of r){let s=l.querySelector(`#${CSS.escape(f)}`);if(s){let d=c.querySelector("defs");d||(d=document.createElementNS("http://www.w3.org/2000/svg","defs"),c.appendChild(d)),d.appendChild(s.cloneNode(!0)),a.add(f);break}}})}async function Nt(t,e=!1,n=!1,i={}){let r=new Map,c=new WeakMap,a=new Map,f,o="";zt(t);try{It(t)}catch(l){console.warn("inlineExternal defs or symbol failed:",l)}try{f=J(t,r,c,a,e,i,t)}catch(l){throw console.warn("deepClone failed:",l),l}try{await ft(t,f,r,c,e,n,i.useProxy)}catch(l){console.warn("inlinePseudoElements failed:",l)}if(await Gt(f),e){let l=xt(r);o=Array.from(l.entries()).map(([s,d])=>`.${d}{${s}}`).join("");for(let[s,d]of r.entries()){if(s.tagName==="STYLE")continue;if(s.getRootNode&&s.getRootNode()instanceof ShadowRoot){s.setAttribute("style",d.replace(/;/g,"; "));continue}let m=l.get(d);m&&s.classList.add(m);let u=s.style?.backgroundImage,g=s.dataset?.snapdomHasIcon;u&&u!=="none"&&(s.style.backgroundImage=u),g&&(s.style.verticalAlign="middle",s.style.display="inline")}}else for(let[l,s]of r.entries())l.tagName!=="STYLE"&&l.setAttribute("style",s.replace(/;/g,"; "));for(let[l,s]of a.entries()){let d=s.scrollLeft,m=s.scrollTop;if((d||m)&&l instanceof HTMLElement){l.style.overflow="hidden",l.style.scrollbarWidth="none",l.style.msOverflowStyle="none";let g=document.createElement("div");for(g.style.transform=`translate(${-d}px, ${-m}px)`,g.style.willChange="transform",g.style.display="inline-block",g.style.width="100%";l.firstChild;)g.appendChild(l.firstChild);l.appendChild(g)}}if(t===a.get(f)){let l=c.get(t)||window.getComputedStyle(t);c.set(t,l);let s=Et(l.transform);f.style.margin="0",f.style.position="static",f.style.top="auto",f.style.left="auto",f.style.right="auto",f.style.bottom="auto",f.style.zIndex="auto",f.style.float="none",f.style.clear="none",f.style.transform=s||""}for(let[l,s]of a.entries())s.tagName==="PRE"&&(l.style.marginTop="0",l.style.marginBlockStart="0");return{clone:f,classCSS:o,styleCache:c}}function zt(t){let e=getComputedStyle(t),n=e.outlineStyle,i=e.outlineWidth,r=e.borderStyle,c=e.borderWidth,a=n!=="none"&&parseFloat(i)>0,f=r==="none"||parseFloat(c)===0;a&&f&&(t.style.border=`${i} solid transparent`)}var dt=new Map;async function Q(t){if(dt.has(t))return dt.get(t);let e=await fetch(t);if(!e.ok)throw new Error(`[SnapDOM] HTTP ${e.status} on blob fetch (${t})`);let n=await e.blob(),i=await new Promise((r,c)=>{let a=new FileReader;a.onloadend=()=>{let f=a.result;typeof f=="string"&&f.startsWith("data:")?r(f):c(new Error("[SnapDOM] Invalid data URL from blob"))},a.onerror=()=>c(new Error("[SnapDOM] FileReader error")),a.readAsDataURL(n)});return dt.set(t,i),i}var Yt=/\bblob:[^)"'\s]+/g;async function Rt(t){if(!t||t.indexOf("blob:")===-1)return t;let e=Array.from(new Set(t.match(Yt)||[]));if(e.length===0)return t;let n=t;for(let i of e)try{let r=await Q(i);n=n.split(i).join(r)}catch{}return n}function ot(t){return typeof t=="string"&&t.startsWith("blob:")}function Kt(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(e=>{let n=e.match(/^(\S+)(\s+.+)?$/);return n?{url:n[1],desc:n[2]||""}:null}).filter(Boolean)}function Xt(t){return t.map(e=>e.desc?`${e.url} ${e.desc.trim()}`:e.url).join(", ")}async function Gt(t){if(!t)return;let e=t.querySelectorAll?t.querySelectorAll("img"):[];for(let a of e)try{let o=a.getAttribute("src")||a.currentSrc||"";if(ot(o)){let s=await Q(o);a.setAttribute("src",s)}let l=a.getAttribute("srcset");if(l&&l.includes("blob:")){let s=Kt(l),d=!1;for(let m of s)if(ot(m.url))try{m.url=await Q(m.url),d=!0}catch{}d&&a.setAttribute("srcset",Xt(s))}}catch{}let n=t.querySelectorAll?t.querySelectorAll("image"):[];for(let a of n)try{let f="http://www.w3.org/1999/xlink",o=a.getAttribute("href")||a.getAttributeNS?.(f,"href");if(ot(o)){let l=await Q(o);a.setAttribute("href",l),a.removeAttributeNS?.(f,"href")}}catch{}let i=t.querySelectorAll?t.querySelectorAll("[style*='blob:']"):[];for(let a of i)try{let f=a.getAttribute("style");if(f&&f.includes("blob:")){let o=await Rt(f);a.setAttribute("style",o)}}catch{}let r=t.querySelectorAll?t.querySelectorAll("style"):[];for(let a of r)try{let f=a.textContent||"";f.includes("blob:")&&(a.textContent=await Rt(f))}catch{}let c=["poster"];for(let a of c){let f=t.querySelectorAll?t.querySelectorAll(`[${a}^='blob:']`):[];for(let o of f)try{let l=o.getAttribute(a);ot(l)&&o.setAttribute(a,await Q(l))}catch{}}}async function Tt(t,e={}){let n=Array.from(t.querySelectorAll("img")),i=async r=>{if(!r.getAttribute("src")){let a=r.currentSrc||r.src||"";a&&r.setAttribute("src",a)}r.removeAttribute("srcset"),r.removeAttribute("sizes");let c=r.src;try{let a=await W(c,{useProxy:e.useProxy});r.src=a,r.width||(r.width=r.naturalWidth||100),r.height||(r.height=r.naturalHeight||100)}catch{let a=document.createElement("div");a.style=`width: ${r.width||100}px; height: ${r.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${r.height||100}px; color: #666; font-size: 12px;`,a.innerText="img",r.replaceWith(a)}};for(let r=0;r{let u=o.getPropertyValue("border-image"),g=o.getPropertyValue("border-image-source");return u&&u!=="none"||g&&g!=="none"})();for(let u of c){if(["border-image-slice","border-image-width","border-image-outset","border-image-repeat"].includes(u)&&!l)continue;let g=o.getPropertyValue(u);if(!g||g==="none")continue;let b=H(g),p=await Promise.all(b.map(w=>O(w,i)));p.some(w=>w&&w!=="none"&&!/^url\(undefined/.test(w))&&f.style.setProperty(u,p.join(", "))}let s=o.getPropertyValue("background-color");s&&s!=="transparent"&&s!=="rgba(0, 0, 0, 0)"&&(f.style.backgroundColor=s);let d=Array.from(a.children),m=Array.from(f.children);for(let u=0;u{q(async()=>{await Tt(o,e),p()},{fast:r})}),await new Promise(p=>{q(async()=>{await Ft(t,o,s,e),p()},{fast:r})}),i&&await new Promise(p=>{q(async()=>{d=await nt({localFonts:f,useProxy:a}),p()},{fast:r})}),n){let p=bt(o).sort(),w=p.join(",");h.baseStyle.has(w)?m=h.baseStyle.get(w):await new Promise(S=>{q(()=>{m=St(p),h.baseStyle.set(w,m),S()},{fast:r})})}await new Promise(p=>{q(()=>{let w=t.getBoundingClientRect(),S=w.width,x=w.height,R=Number.isFinite(e.width),y=Number.isFinite(e.height),k=typeof c=="number"&&c!==1;if(!k){let B=w.width/w.height;R&&y?(S=e.width,x=e.height):R?(S=e.width,x=S/B):y&&(x=e.height,S=x*B)}if(S=Math.ceil(S),x=Math.ceil(x),o.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),o.style.transformOrigin="top left",!k&&(R||y)){let B=w.width,Y=w.height,$=S/B,M=x/Y,rt=o.style.transform||"",ht=`scale(${$}, ${M})`;o.style.transform=`${ht} ${rt}`.trim()}let C="http://www.w3.org/2000/svg",A=document.createElementNS(C,"foreignObject");A.setAttribute("width","100%"),A.setAttribute("height","100%");let P=document.createElement("style");P.textContent=m+d+"svg{overflow:visible;}"+l,A.appendChild(P),A.appendChild(o);let T=new XMLSerializer().serializeToString(A);g=``+T+"",u=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(g)}`,p()},{fast:r})});let b=document.getElementById("snapdom-sandbox");return b&&b.style.position==="absolute"&&b.remove(),u}async function Jt(t,{scale:e=1}={}){let n=new Image;return n.src=t,await n.decode(),e!==1&&(n.style.width=`${n.naturalWidth*e}px`,n.style.height=`${n.naturalHeight*e}px`),n}async function Pt(t,{dpr:e=1,scale:n=1}={}){let i=new Image;i.src=t,i.crossOrigin="anonymous",i.loading="eager",i.decoding="sync";let r=vt(),c=!1;if(r&&(document.body.appendChild(i),c=!0),await i.decode(),r&&await new Promise(s=>setTimeout(s,100)),i.width===0||i.height===0)throw c&&i.remove(),new Error("Image failed to load or has no dimensions");let a=i.naturalWidth*n,f=i.naturalHeight*n,o=document.createElement("canvas");o.width=Math.ceil(a*e),o.height=Math.ceil(f*e),o.style.width=`${a}px`,o.style.height=`${f}px`;let l=o.getContext("2d");return l.scale(e,e),l.drawImage(i,0,0,a,f),c&&i.remove(),o}async function Bt(t,{type:e="svg",scale:n=1,backgroundColor:i="#fff",quality:r}={}){let c={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[e]||"image/png";if(e==="svg"){let f=decodeURIComponent(t.split(",")[1]);return new Blob([f],{type:"image/svg+xml"})}let a=await mt(t,{dpr:1,scale:n},i);return new Promise(f=>{a.toBlob(o=>f(o),`${c}`,r)})}async function mt(t,{dpr:e=1,scale:n=1},i){let r=await Pt(t,{dpr:e,scale:n});if(!i)return r;let c=document.createElement("canvas");c.width=r.width,c.height=r.height;let a=c.getContext("2d");return a.fillStyle=i,a.fillRect(0,0,c.width,c.height),a.drawImage(r,0,0),c}async function ut(t,{dpr:e=1,scale:n=1,backgroundColor:i,quality:r},c="png"){let a=["jpg","jpeg","webp"].includes(c)?"#fff":void 0,o=await mt(t,{dpr:e,scale:n},i??a),l=new Image;return l.src=o.toDataURL(`image/${c}`,r),await l.decode(),l.style.width=`${o.width/e}px`,l.style.height=`${o.height/e}px`,l}async function Qt(t,{dpr:e=1,scale:n=1,backgroundColor:i,format:r="png",filename:c="snapDOM"}={}){if(r==="svg"){let m=await Bt(t),u=URL.createObjectURL(m),g=document.createElement("a");g.href=u,g.download=`${c}.svg`,g.click(),URL.revokeObjectURL(u);return}let a=["jpg","jpeg","webp"].includes(r)?"#fff":void 0,o=await mt(t,{dpr:e,scale:n},i??a),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[r]||"image/png",s=o.toDataURL(l),d=document.createElement("a");d.href=s,d.download=`${c}.${r}`,d.click()}async function E(t,e={}){if(e={scale:1,...e},!t)throw new Error("Element cannot be null or undefined");return e.iconFonts&&At(e.iconFonts),await E.capture(t,e)}E.capture=async(t,e={})=>{let n=await Lt(t,e),i=e.dpr??(window.devicePixelRatio||1),r=e.scale||1;return{url:n,options:e,toRaw:()=>n,toImg:(c={})=>Jt(n,{dpr:i,scale:r,...c}),toCanvas:(c={})=>Pt(n,{dpr:i,scale:r,...c}),toBlob:(c={})=>Bt(n,{dpr:i,scale:r,...c}),toPng:(c={})=>ut(n,{dpr:i,scale:r,...c},"png"),toJpg:(c={})=>ut(n,{dpr:i,scale:r,...c},"jpeg"),toWebp:(c={})=>ut(n,{dpr:i,scale:r,...c},"webp"),download:({format:c="png",filename:a="snapDOM",backgroundColor:f,...o}={})=>Qt(n,{dpr:i,scale:r,format:c,filename:a,backgroundColor:f,...o})}};E.toRaw=async(t,e)=>(await E.capture(t,e)).toRaw();E.toImg=async(t,e)=>(await E.capture(t,e)).toImg();E.toCanvas=async(t,e)=>(await E.capture(t,e)).toCanvas();E.toBlob=async(t,e)=>(await E.capture(t,e)).toBlob(e);E.toPng=async(t,e)=>(await E.capture(t,e)).toPng(e);E.toJpg=async(t,e)=>(await E.capture(t,e)).toJpg(e);E.toWebp=async(t,e)=>(await E.capture(t,e)).toWebp(e);E.download=async(t,e={})=>{let{format:n="png",filename:i="capture",backgroundColor:r,...c}=e;return await(await E.capture(t,c)).download({format:n,filename:i,backgroundColor:r})};async function Zt(t=document,e={}){let{embedFonts:n=!0,reset:i=!1,useProxy:r}=e;if(i){h.image.clear(),h.background.clear(),h.resource.clear(),h.defaultStyle.clear(),h.baseStyle.clear(),h.font.clear(),h.computedStyle=new WeakMap;return}try{await document.fonts.ready}catch{}yt();let c=[],a=[];t?.querySelectorAll&&(c=Array.from(t.querySelectorAll("img[src]")),a=Array.from(t.querySelectorAll("*")));let f=[];for(let o of c){let l=o?.src;if(l&&!h.image.has(l)){let s=Promise.resolve().then(()=>W(l,{useProxy:r})).then(d=>{h.image.set(l,d)}).catch(()=>{});f.push(s)}}for(let o of a){let l="";try{l=U(o).backgroundImage}catch{}if(l&&l!=="none"){let s=H(l);for(let d of s)if(d.startsWith("url(")){let m=Promise.resolve().then(()=>O(d,{...e,useProxy:r})).catch(()=>{});f.push(m)}}}if(n)try{await nt({preCached:!0,localFonts:e.localFonts,useProxy:e.useProxy})}catch{}await Promise.allSettled(f)}export{Zt as preCache,E as snapdom}; +var w={image:new Map,background:new Map,resource:new Map,defaultStyle:new Map,baseStyle:new Map,computedStyle:new WeakMap,font:new Set,session:{styleMap:new Map,styleCache:new WeakMap,nodeMap:new Map}};function Mt(t="soft"){switch(t){case"auto":{w.session.styleMap=new Map,w.session.nodeMap=new Map;return}case"soft":{w.session.styleMap=new Map,w.session.nodeMap=new Map,w.session.styleCache=new WeakMap;return}case"full":return;case"disabled":{w.session.styleMap=new Map,w.session.nodeMap=new Map,w.session.styleCache=new WeakMap,w.computedStyle=new WeakMap,w.baseStyle=new Map,w.defaultStyle=new Map,w.image=new Map,w.background=new Map,w.resource=new Map,w.font=new Set;return}default:{w.session.styleMap=new Map,w.session.nodeMap=new Map,w.session.styleCache=new WeakMap;return}}}function tt(t){let e=t.match(/url\((['"]?)(.*?)(\1)\)/);if(!e)return null;let n=e[2].trim();return n.startsWith("#")?null:n}function jt(t){if(!t||t==="none")return"";let e=t.replace(/translate[XY]?\([^)]*\)/g,"");return e=e.replace(/matrix\(([^)]+)\)/g,(n,r)=>{let o=r.split(",").map(a=>a.trim());return o.length!==6?`matrix(${r})`:(o[4]="0",o[5]="0",`matrix(${o.join(", ")})`)}),e=e.replace(/matrix3d\(([^)]+)\)/g,(n,r)=>{let o=r.split(",").map(a=>a.trim());return o.length!==16?`matrix3d(${r})`:(o[12]="0",o[13]="0",`matrix3d(${o.join(", ")})`)}),e.trim().replace(/\s{2,}/g," ")}function Y(t){if(/%[0-9A-Fa-f]{2}/.test(t))return t;try{return encodeURI(t)}catch{return t}}function on(t="[snapDOM]",{ttlMs:e=5*6e4,maxEntries:n=12}={}){let r=new Map,o=0;function a(s,l,i){if(o>=n)return;let c=Date.now();(r.get(l)||0)>c||(r.set(l,c+e),o++,s==="warn"&&console&&console.warn?console.warn(`${t} ${i}`):console&&console.error&&console.error(`${t} ${i}`))}return{warnOnce(s,l){a("warn",s,l)},errorOnce(s,l){a("error",s,l)},reset(){r.clear(),o=0}}}var pe=on("[snapDOM]",{ttlMs:3*6e4,maxEntries:10}),Vt=new Map,kt=new Map;function sn(t){return/^data:|^blob:|^about:blank$/i.test(t)}function an(t,e){try{let n=typeof location<"u"&&location.href?location.href:"http://localhost/",r=e.includes("{url}")?e.split("{url}")[0]:e,o=new URL(r||".",n),a=new URL(t,n);if(a.origin===o.origin)return!0;let s=a.searchParams;if(s&&(s.has("url")||s.has("target")))return!0}catch{}return!1}function cn(t,e){if(!e||sn(t)||an(t,e))return!1;try{let n=typeof location<"u"&&location.href?location.href:"http://localhost/",r=new URL(t,n);return typeof location<"u"?r.origin!==location.origin:!0}catch{return!!e}}function ln(t,e){if(!e)return t;if(e.includes("{url}"))return e.replace("{urlRaw}",Y(t)).replace("{url}",encodeURIComponent(t));if(/[?&]url=?$/.test(e))return`${e}${encodeURIComponent(t)}`;if(e.endsWith("?"))return`${e}url=${encodeURIComponent(t)}`;if(e.endsWith("/"))return`${e}${Y(t)}`;let n=e.includes("?")?"&":"?";return`${e}${n}url=${encodeURIComponent(t)}`}function ge(t){return new Promise((e,n)=>{let r=new FileReader;r.onload=()=>e(String(r.result||"")),r.onerror=()=>n(new Error("read_failed")),r.readAsDataURL(t)})}function un(t,e){return[e.as||"blob",e.timeout??3e3,e.useProxy||"",e.errorTTL??8e3,t].join("|")}async function L(t,e={}){let n=e.as??"blob",r=e.timeout??3e3,o=e.useProxy||"",a=e.errorTTL??8e3,s=e.headers||{},l=!!e.silent;if(/^data:/i.test(t))try{if(n==="text")return{ok:!0,data:String(t),status:200,url:t,fromCache:!1};if(n==="dataURL")return{ok:!0,data:String(t),status:200,url:t,fromCache:!1,mime:String(t).slice(5).split(";")[0]||""};let[,h="",p=""]=String(t).match(/^data:([^,]*),(.*)$/)||[],S=/;base64/i.test(h)?atob(p):decodeURIComponent(p),x=new Uint8Array([...S].map(M=>M.charCodeAt(0))),C=new Blob([x],{type:(h||"").split(";")[0]||""});return{ok:!0,data:C,status:200,url:t,fromCache:!1,mime:C.type||""}}catch{return{ok:!1,data:null,status:0,url:t,fromCache:!1,reason:"special_url_error"}}if(/^blob:/i.test(t))try{let h=await fetch(t);if(!h.ok)return{ok:!1,data:null,status:h.status,url:t,fromCache:!1,reason:"http_error"};let p=await h.blob(),g=p.type||h.headers.get("content-type")||"";return n==="dataURL"?{ok:!0,data:await ge(p),status:h.status,url:t,fromCache:!1,mime:g}:n==="text"?{ok:!0,data:await p.text(),status:h.status,url:t,fromCache:!1,mime:g}:{ok:!0,data:p,status:h.status,url:t,fromCache:!1,mime:g}}catch{return{ok:!1,data:null,status:0,url:t,fromCache:!1,reason:"network"}}if(/^about:blank$/i.test(t))return n==="dataURL"?{ok:!0,data:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==",status:200,url:t,fromCache:!1,mime:"image/png"}:{ok:!0,data:n==="text"?"":new Blob([]),status:200,url:t,fromCache:!1};let i=un(t,{as:n,timeout:r,useProxy:o,errorTTL:a}),c=kt.get(i);if(c&&c.until>Date.now())return{...c.result,fromCache:!0};c&&kt.delete(i);let u=Vt.get(i);if(u)return u;let f=cn(t,o)?ln(t,o):t,m=e.credentials;if(!m)try{let h=typeof location<"u"&&location.href?location.href:"http://localhost/",p=new URL(t,h);m=typeof location<"u"&&p.origin===location.origin?"include":"omit"}catch{m="omit"}let y=new AbortController,b=setTimeout(()=>y.abort("timeout"),r),d=(async()=>{try{let h=await fetch(f,{signal:y.signal,credentials:m,headers:s});if(!h.ok){let S={ok:!1,data:null,status:h.status,url:f,fromCache:!1,reason:"http_error"};if(a>0&&kt.set(i,{until:Date.now()+a,result:S}),!l){let x=`${h.status} ${h.statusText||""}`.trim();pe.warnOnce(`http:${h.status}:${n}:${new URL(t,location?.href??"http://localhost/").origin}`,`HTTP error ${x} while fetching ${n} ${t}`)}return e.onError&&e.onError(S),S}if(n==="text")return{ok:!0,data:await h.text(),status:h.status,url:f,fromCache:!1};let p=await h.blob(),g=p.type||h.headers.get("content-type")||"";return n==="dataURL"?{ok:!0,data:await ge(p),status:h.status,url:f,fromCache:!1,mime:g}:{ok:!0,data:p,status:h.status,url:f,fromCache:!1,mime:g}}catch(h){let p=h&&typeof h=="object"&&"name"in h&&h.name==="AbortError"?String(h.message||"").includes("timeout")?"timeout":"abort":"network",g={ok:!1,data:null,status:0,url:f,fromCache:!1,reason:p};if(!/^blob:/i.test(t)&&a>0&&kt.set(i,{until:Date.now()+a,result:g}),!l){let S=`${p}:${n}:${new URL(t,location?.href??"http://localhost/").origin}`,x=p==="timeout"?`Timeout after ${r}ms. Consider increasing timeout or using a proxy for ${t}`:p==="abort"?`Request aborted while fetching ${n} ${t}`:`Network/CORS issue while fetching ${n} ${t}. A proxy may be required`;pe.errorOnce(S,x)}return e.onError&&e.onError(g),g}finally{clearTimeout(b),Vt.delete(i)}})();return Vt.set(i,d),d}function et(t,e){if(!e||!t.width||!t.height)return t;let n=document.createElement("canvas");n.width=t.width,n.height=t.height;let r=n.getContext("2d");return r.fillStyle=e,r.fillRect(0,0,n.width,n.height),r.drawImage(t,0,0),n}async function nt(t,e={}){if(/^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(t)||t.trim()==="none")return t;let r=tt(t);if(!r)return t;let o=Y(r);if(w.background.has(o)){let a=w.background.get(o);return a?`url("${a}")`:"none"}try{let a=await L(o,{as:"dataURL",useProxy:e.useProxy});return a.ok?(w.background.set(o,a.data),`url("${a.data}")`):(w.background.set(o,null),"none")}catch{return w.background.set(o,null),"none"}}var At=new Set(["meta","script","noscript","title","link","template"]),lt=new Set(["meta","link","style","title","noscript","script","template","g","defs","use","marker","mask","clipPath","pattern","path","polygon","polyline","line","circle","ellipse","rect","filter","lineargradient","radialgradient","stop"]),fn=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","table","thead","tbody","tr","td","th"];function zt(){for(let t of fn){let e=String(t).toLowerCase();At.has(e)||lt.has(e)||Xt(e)}}function Xt(t){if(t=String(t).toLowerCase(),lt.has(t)){let a={};return w.defaultStyle.set(t,a),a}if(w.defaultStyle.has(t))return w.defaultStyle.get(t);let e=document.getElementById("snapdom-sandbox");e||(e=document.createElement("div"),e.id="snapdom-sandbox",e.setAttribute("data-snapdom-sandbox","true"),e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.left="-9999px",e.style.top="-9999px",e.style.width="0px",e.style.height="0px",e.style.overflow="hidden",document.body.appendChild(e));let n=document.createElement(t);n.style.all="initial",e.appendChild(n);let r=getComputedStyle(n),o={};for(let a of r){if(ye(a))continue;let s=r.getPropertyValue(a);o[a]=s}return e.removeChild(n),w.defaultStyle.set(t,o),o}var dn=/(?:^|-)(animation|transition)(?:-|$)/i,mn=/^(--|view-timeline|scroll-timeline|animation-trigger|offset-|position-try|app-region|interactivity|overlay|view-transition|-webkit-locale|-webkit-user-(?:drag|modify)|-webkit-tap-highlight-color|-webkit-text-security)$/i,hn=new Set(["cursor","pointer-events","touch-action","user-select","print-color-adjust","speak","reading-flow","reading-order","anchor-name","anchor-scope","container-name","container-type","timeline-scope"]);function ye(t){let e=String(t).toLowerCase();return!!(hn.has(e)||mn.test(e)||dn.test(e))}function ut(t,e){if(e=String(e||"").toLowerCase(),lt.has(e))return"";let n=[],r=Xt(e);for(let[o,a]of Object.entries(t)){if(ye(o))continue;let s=r[o];a&&a!==s&&n.push(`${o}:${a}`)}return n.sort(),n.join(";")}function Yt(t){let e=new Set;return t.nodeType!==Node.ELEMENT_NODE&&t.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(t.tagName&&e.add(t.tagName.toLowerCase()),typeof t.querySelectorAll=="function"&&t.querySelectorAll("*").forEach(n=>e.add(n.tagName.toLowerCase())),Array.from(e))}function Gt(t){let e=new Map;for(let r of t){let o=w.defaultStyle.get(r);if(!o)continue;let a=Object.entries(o).map(([s,l])=>`${s}:${l};`).sort().join("");a&&(e.has(a)||e.set(a,[]),e.get(a).push(r))}let n="";for(let[r,o]of e.entries())n+=`${o.join(",")} { ${r} } +`;return n}function Kt(t){let e=Array.from(new Set(t.values())).filter(Boolean).sort(),n=new Map,r=1;for(let o of e)n.set(o,`c${r++}`);return n}function rt(t,e=null){if(!(t instanceof Element))return window.getComputedStyle(t,e);let n=w.computedStyle.get(t);if(n||(n=new Map,w.computedStyle.set(t,n)),!n.has(e)){let r=window.getComputedStyle(t,e);n.set(e,r)}return n.get(e)}function $t(t){let e={};for(let n of t)e[n]=t.getPropertyValue(n);return e}function ot(t){let e=[],n=0,r=0;for(let o=0;opt()).observe(t,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}catch{}try{new MutationObserver(()=>pt()).observe(document.head,{subtree:!0,childList:!0,characterData:!0,attributes:!0})}catch{}try{let e=document.fonts;e&&(e.addEventListener?.("loadingdone",pt),e.ready?.then(()=>pt()).catch(()=>{}))}catch{}}}function gn(t,e={}){let n={},r=t.getPropertyValue("visibility");for(let o=0;or[0]o[0]?1:0).map(([r,o])=>`${r}:${o}`).join(";"),Se.set(t,e),e)}function wn(t,e=null,n={}){let r=we.get(t);if(r&&r.epoch===Qt)return r.snapshot;let o=e||getComputedStyle(t),a=gn(o,n);return kn(t,o,a),we.set(t,{epoch:Qt,snapshot:a}),a}function bn(t,e){return t&&t.session&&t.persist?t:t&&(t.styleMap||t.styleCache||t.nodeMap)?{session:t,persist:{snapshotKeyCache:Jt,defaultStyle:w.defaultStyle,baseStyle:w.baseStyle,image:w.image,resource:w.resource,background:w.background,font:w.font},options:e||{}}:{session:w.session,persist:{snapshotKeyCache:Jt,defaultStyle:w.defaultStyle,baseStyle:w.baseStyle,image:w.image,resource:w.resource,background:w.background,font:w.font},options:t||e||{}}}async function st(t,e,n,r){if(t.tagName==="STYLE")return;let o=bn(n,r),a=o.options&&o.options.cache||"auto";if(a!=="disabled"&&pn(document.documentElement),a==="disabled"&&!o.session.__bumpedForDisabled&&(pt(),Jt.clear(),o.session.__bumpedForDisabled=!0),lt.has(t.tagName?.toLowerCase())){let m=t.getAttribute?.("style");m&&e.setAttribute("style",m)}let{session:s,persist:l}=o;s.styleCache.has(t)||s.styleCache.set(t,getComputedStyle(t));let i=s.styleCache.get(t),c=wn(t,i,o.options),u=yn(c),f=l.snapshotKeyCache.get(u);if(!f){let m=t.tagName?.toLowerCase()||"div";f=ut(c,m),l.snapshotKeyCache.set(u,f)}s.styleMap.set(e,f)}function Sn(t){return t instanceof HTMLImageElement||t instanceof HTMLCanvasElement||t instanceof HTMLVideoElement||t instanceof HTMLIFrameElement||t instanceof SVGElement||t instanceof HTMLObjectElement||t instanceof HTMLEmbedElement}function xn(t){return t.backgroundImage&&t.backgroundImage!=="none"||t.backgroundColor&&t.backgroundColor!=="rgba(0, 0, 0, 0)"&&t.backgroundColor!=="transparent"||(parseFloat(t.borderTopWidth)||0)>0||(parseFloat(t.borderBottomWidth)||0)>0||(parseFloat(t.paddingTop)||0)>0||(parseFloat(t.paddingBottom)||0)>0?!0:(t.overflowBlock||t.overflowY||"visible")!=="visible"}function Cn(t){let e=t.parentElement;if(!e)return!1;let n=getComputedStyle(e).display||"";return n.includes("flex")||n.includes("grid")}function Mn(t,e){if(t.textContent&&/\S/.test(t.textContent))return!0;let n=t.firstElementChild,r=t.lastElementChild;if(n&&n.tagName==="BR"||r&&r.tagName==="BR")return!0;let o=t.scrollHeight;if(o===0)return!1;let a=parseFloat(e.paddingTop)||0,s=parseFloat(e.paddingBottom)||0;return o>a+s}function kn(t,e,n){if(t instanceof HTMLElement&&t.style&&t.style.height)return;let r=e.display||"";if(r.includes("flex")||r.includes("grid")||Sn(t))return;let o=e.position;o==="absolute"||o==="fixed"||o==="sticky"||e.transform==="none"&&(xn(e)||Cn(t)||Mn(t,e)&&(delete n.height,delete n["block-size"]))}function xe(t,e){if(!(t instanceof Element)||!(e instanceof Element))return;let n=t.getAttribute?.("style"),r=!!(n&&n.includes("var("));if(!r&&t.attributes?.length){let s=t.attributes;for(let l=0;lnew Promise(o=>{function a(){j(s=>{(s&&typeof s.timeRemaining=="function"?s.timeRemaining()>0:!0)?e(r,o):a()},{fast:n})}a()})))}function An(t){return t=t.trim(),!t||/:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(t)?t:`${t}:not([data-sd-slotted])`}function $n(t,e,n=!0){return t.split(",").map(r=>r.trim()).filter(Boolean).map(r=>{if(r.startsWith(":where(")||r.startsWith("@"))return r;let o=n?An(r):r;return`:where(${e} ${o})`}).join(", ")}function vn(t,e){return t?(t=t.replace(/:host\(([^)]+)\)/g,(n,r)=>`:where(${e}:is(${r.trim()}))`),t=t.replace(/:host\b/g,`:where(${e})`),t=t.replace(/:host-context\(([^)]+)\)/g,(n,r)=>`:where(:where(${r.trim()}) ${e})`),t=t.replace(/::slotted\(([^)]+)\)/g,(n,r)=>`:where(${e} ${r.trim()})`),t=t.replace(/(^|})(\s*)([^@}{]+){/g,(n,r,o,a)=>{let s=$n(a,e,!0);return`${r}${o}${s}{`}),t):""}function En(t){return t.shadowScopeSeq=(t.shadowScopeSeq||0)+1,`s${t.shadowScopeSeq}`}function Fn(t){let e="";try{t.querySelectorAll("style").forEach(r=>{e+=(r.textContent||"")+` +`});let n=t.adoptedStyleSheets||[];for(let r of n)try{if(r&&r.cssRules)for(let o of r.cssRules)e+=o.cssText+` +`}catch{}}catch{}return e}function Nn(t,e,n){if(!e)return;let r=document.createElement("style");r.setAttribute("data-sd",n),r.textContent=e,t.insertBefore(r,t.firstChild||null)}function Ln(t,e){try{let n=t.currentSrc||t.src||"";if(!n)return;e.setAttribute("src",n),e.removeAttribute("srcset"),e.removeAttribute("sizes"),e.loading="eager",e.decoding="sync"}catch{}}function Rn(t){let e=new Set;if(!t)return e;let n=/var\(\s*(--[A-Za-z0-9_-]+)\b/g,r;for(;r=n.exec(t);)e.add(r[1]);return e}function In(t,e){try{let r=getComputedStyle(t).getPropertyValue(e).trim();if(r)return r}catch{}try{let r=getComputedStyle(document.documentElement).getPropertyValue(e).trim();if(r)return r}catch{}return""}function Tn(t,e,n){let r=[];for(let o of e){let a=In(t,o);a&&r.push(`${o}: ${a};`)}return r.length?`${n}{${r.join("")}} +`:""}function Pn(t){t&&(t.nodeType===Node.ELEMENT_NODE&&t.setAttribute("data-sd-slotted",""),t.querySelectorAll&&t.querySelectorAll("*").forEach(e=>e.setAttribute("data-sd-slotted","")))}async function _n(t,e=3){let n=()=>{try{return t.contentDocument||t.contentWindow?.document||null}catch{return null}},r=n(),o=0;for(;osetTimeout(a,0)),r=n(),o++;return r&&(r.body||r.documentElement)?r:null}function Wn(t){let e=t.getBoundingClientRect(),n=0,r=0,o=0,a=0;try{let i=getComputedStyle(t);n=parseFloat(i.borderLeftWidth)||0,r=parseFloat(i.borderRightWidth)||0,o=parseFloat(i.borderTopWidth)||0,a=parseFloat(i.borderBottomWidth)||0}catch{}let s=Math.max(0,Math.round(e.width-(n+r))),l=Math.max(0,Math.round(e.height-(o+a)));return{contentWidth:s,contentHeight:l,rect:e}}function Un(t,e,n){let r=t.createElement("style");return r.setAttribute("data-sd-iframe-pin",""),r.textContent=`html, body {margin: 0 !important;padding: 0 !important;width: ${e}px !important;height: ${n}px !important;min-width: ${e}px !important;min-height: ${n}px !important;box-sizing: border-box !important;overflow: hidden !important;background-clip: border-box !important;}`,(t.head||t.documentElement).appendChild(r),()=>{try{r.remove()}catch{}}}async function Bn(t,e,n){let r=await _n(t,3);if(!r)throw new Error("iframe document not accessible/ready");let{contentWidth:o,contentHeight:a,rect:s}=Wn(t),l=n?.snap;if(!l||typeof l.toPng!="function")throw new Error("snapdom.toPng not available in iframe or window");let i={...n,scale:1},c=Un(r,o,a),u;try{u=await l.toPng(r.documentElement,i)}finally{c()}u.style.display="block",u.style.width=`${o}px`,u.style.height=`${a}px`;let f=document.createElement("div");return e.nodeMap.set(f,t),st(t,f,e,n),f.style.overflow="hidden",f.style.display="block",f.style.width||(f.style.width=`${Math.round(s.width)}px`),f.style.height||(f.style.height=`${Math.round(s.height)}px`),f.appendChild(u),f}async function gt(t,e,n){if(!t)throw new Error("Invalid node");let r=new Set,o=null,a=null;if(t.nodeType===Node.ELEMENT_NODE){let c=(t.localName||t.tagName||"").toLowerCase();if(t.id==="snapdom-sandbox"||t.hasAttribute("data-snapdom-sandbox")||At.has(c))return null}if(t.nodeType===Node.TEXT_NODE||t.nodeType!==Node.ELEMENT_NODE)return t.cloneNode(!0);if(t.getAttribute("data-capture")==="exclude"){if(n.excludeMode==="hide"){let c=document.createElement("div"),u=t.getBoundingClientRect();return c.style.cssText=`display:inline-block;width:${u.width}px;height:${u.height}px;visibility:hidden;`,c}else if(n.excludeMode==="remove")return null}if(n.exclude&&Array.isArray(n.exclude))for(let c of n.exclude)try{if(t.matches?.(c)){if(n.excludeMode==="hide"){let u=document.createElement("div"),f=t.getBoundingClientRect();return u.style.cssText=`display:inline-block;width:${f.width}px;height:${f.height}px;visibility:hidden;`,u}else if(n.excludeMode==="remove")return null}}catch(u){console.warn(`Invalid selector in exclude option: ${c}`,u)}if(typeof n.filter=="function")try{if(!n.filter(t)){if(n.filterMode==="hide"){let c=document.createElement("div"),u=t.getBoundingClientRect();return c.style.cssText=`display:inline-block;width:${u.width}px;height:${u.height}px;visibility:hidden;`,c}else if(n.filterMode==="remove")return null}}catch(c){console.warn("Error in filter function:",c)}if(t.tagName==="IFRAME"){let c=!1;try{c=!!(t.contentDocument||t.contentWindow?.document)}catch{c=!1}if(c)try{return await Bn(t,e,n)}catch(u){console.warn("[SnapDOM] iframe rasterization failed, fallback:",u)}if(n.placeholders){let u=document.createElement("div");return u.style.cssText=`width:${t.offsetWidth}px;height:${t.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`,st(t,u,e,n),u}else{let u=t.getBoundingClientRect(),f=document.createElement("div");return f.style.cssText=`display:inline-block;width:${u.width}px;height:${u.height}px;visibility:hidden;`,st(t,f,e,n),f}}if(t.getAttribute("data-capture")==="placeholder"){let c=t.cloneNode(!1);e.nodeMap.set(c,t),st(t,c,e,n);let u=document.createElement("div");return u.textContent=t.getAttribute("data-placeholder-text")||"",u.style.cssText="color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;",c.appendChild(u),c}if(t.tagName==="CANVAS"){let c=t.toDataURL(),u=document.createElement("img");return u.src=c,u.width=t.width,u.height=t.height,e.nodeMap.set(u,t),st(t,u,e,n),u}let s;try{if(s=t.cloneNode(!1),xe(t,s),e.nodeMap.set(s,t),t.tagName==="IMG"){Ln(t,s);try{let c=t.getBoundingClientRect(),u=Math.round(c.width||0),f=Math.round(c.height||0);if(!u||!f){let m=window.getComputedStyle(t),y=parseFloat(m.width)||0,b=parseFloat(m.height)||0,d=parseInt(t.getAttribute("width")||"",10)||0,h=parseInt(t.getAttribute("height")||"",10)||0,p=t.width||t.naturalWidth||0,g=t.height||t.naturalHeight||0;u=Math.round(u||y||d||p||0),f=Math.round(f||b||h||g||0)}u&&(s.dataset.snapdomWidth=String(u)),f&&(s.dataset.snapdomHeight=String(f))}catch{}}}catch(c){throw console.error("[Snapdom] Failed to clone node:",t,c),c}if(t instanceof HTMLTextAreaElement){let c=t.getBoundingClientRect();s.style.width=`${c.width}px`,s.style.height=`${c.height}px`}if(t instanceof HTMLInputElement&&(s.value=t.value,s.setAttribute("value",t.value),t.checked!==void 0&&(s.checked=t.checked,t.checked&&s.setAttribute("checked",""),t.indeterminate&&(s.indeterminate=t.indeterminate))),t instanceof HTMLSelectElement&&(o=t.value),t instanceof HTMLTextAreaElement&&(a=t.value),st(t,s,e,n),t.shadowRoot){let h=function(g,S){if(g.nodeType===Node.ELEMENT_NODE&&g.tagName==="STYLE")return S(null);gt(g,e,n).then(x=>{S(x||null)}).catch(()=>{S(null)})};try{let g=t.shadowRoot.querySelectorAll("slot");for(let S of g){let x=[];try{x=S.assignedNodes?.({flatten:!0})||S.assignedNodes?.()||[]}catch{x=S.assignedNodes?.()||[]}for(let C of x)r.add(C)}}catch{}let c=En(e),u=`[data-sd="${c}"]`;try{s.setAttribute("data-sd",c)}catch{}let f=Fn(t.shadowRoot),m=vn(f,u),y=Rn(f),b=Tn(t,y,u);Nn(s,b+m,c);let d=document.createDocumentFragment(),p=await Zt(Array.from(t.shadowRoot.childNodes),h,n.fast);d.append(...p.filter(g=>!!g)),s.appendChild(d)}if(t.tagName==="SLOT"){let m=function(b,d){gt(b,e,n).then(h=>{h&&Pn(h),d(h||null)}).catch(()=>{d(null)})},c=t.assignedNodes?.({flatten:!0})||[],u=c.length>0?c:Array.from(t.childNodes),f=document.createDocumentFragment(),y=await Zt(Array.from(u),m,n.fast);return f.append(...y.filter(b=>!!b)),f}function l(c,u){if(r.has(c))return u(null);gt(c,e,n).then(f=>{u(f||null)}).catch(()=>{u(null)})}let i=await Zt(Array.from(t.childNodes),l,n.fast);if(s.append(...i.filter(c=>!!c)),o!==null&&s instanceof HTMLSelectElement){s.value=o;for(let c of s.options)c.value===o?c.setAttribute("selected",""):c.removeAttribute("selected")}return a!==null&&s instanceof HTMLTextAreaElement&&(s.textContent=a),s}var On=[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i,/layui/i,/lucide/i],te=[];function Ce(t){let e=Array.isArray(t)?t:[t];for(let n of e)n instanceof RegExp?te.push(n):typeof n=="string"?te.push(new RegExp(n,"i")):console.warn("[snapdom] Ignored invalid iconFont value:",n)}function B(t){let e=typeof t=="string"?t:"",n=[...On,...te];for(let r of n)if(r instanceof RegExp&&r.test(e))return!0;return!!(/icon/i.test(e)||/glyph/i.test(e)||/symbols/i.test(e)||/feather/i.test(e)||/fontawesome/i.test(e))}async function Ae(t,e,n,r=32,o="#000"){e=e.replace(/^['"]+|['"]+$/g,"");let a=window.devicePixelRatio||1;try{await document.fonts.ready}catch{}let s=document.createElement("span");s.textContent=t,s.style.position="absolute",s.style.visibility="hidden",s.style.fontFamily=`"${e}"`,s.style.fontWeight=n||"normal",s.style.fontSize=`${r}px`,s.style.lineHeight="1",s.style.whiteSpace="nowrap",s.style.padding="0",s.style.margin="0",document.body.appendChild(s);let l=s.getBoundingClientRect(),i=Math.ceil(l.width),c=Math.ceil(l.height);document.body.removeChild(s);let u=document.createElement("canvas");u.width=Math.max(1,i*a),u.height=Math.max(1,c*a);let f=u.getContext("2d");return f.scale(a,a),f.font=n?`${n} ${r}px "${e}"`:`${r}px "${e}"`,f.textAlign="left",f.textBaseline="top",f.fillStyle=o,f.fillText(t,0,0),{dataUrl:u.toDataURL(),width:i,height:c}}var Dn=new Set(["serif","sans-serif","monospace","cursive","fantasy","system-ui","emoji","math","fangsong","ui-serif","ui-sans-serif","ui-monospace","ui-rounded"]);function Ft(t){if(!t)return"";for(let e of t.split(",")){let n=e.trim().replace(/^['"]+|['"]+$/g,"");if(n&&!Dn.has(n.toLowerCase()))return n}return""}function vt(t){let e=String(t??"400").trim().toLowerCase();if(e==="normal")return 400;if(e==="bold")return 700;let n=parseInt(e,10);return Number.isFinite(n)?Math.min(900,Math.max(100,n)):400}function ee(t){let e=String(t??"normal").trim().toLowerCase();return e.startsWith("italic")?"italic":e.startsWith("oblique")?"oblique":"normal"}function Hn(t){let e=String(t??"100%").match(/(\d+(?:\.\d+)?)\s*%/);return e?Math.max(50,Math.min(200,parseFloat(e[1]))):100}function qn(t){let e=String(t||"400").trim(),n=e.match(/^(\d{2,3})\s+(\d{2,3})$/);if(n){let o=vt(n[1]),a=vt(n[2]);return{min:Math.min(o,a),max:Math.max(o,a)}}let r=vt(e);return{min:r,max:r}}function jn(t){let e=String(t||"normal").trim().toLowerCase();return e==="italic"?{kind:"italic"}:e.startsWith("oblique")?{kind:"oblique"}:{kind:"normal"}}function Vn(t){let e=String(t||"100%").trim(),n=e.match(/(\d+(?:\.\d+)?)\s*%\s+(\d+(?:\.\d+)?)\s*%/);if(n){let a=parseFloat(n[1]),s=parseFloat(n[2]);return{min:Math.min(a,s),max:Math.max(a,s)}}let r=e.match(/(\d+(?:\.\d+)?)\s*%/),o=r?parseFloat(r[1]):100;return{min:o,max:o}}function zn(t,e){if(!t)return!1;try{let n=new URL(t,location.href);if(n.origin===location.origin)return!0;let o=n.host.toLowerCase();if(["fonts.googleapis.com","fonts.gstatic.com","use.typekit.net","p.typekit.net","kit.fontawesome.com","use.fontawesome.com"].some(l=>o.endsWith(l)))return!0;let s=(n.pathname+n.search).toLowerCase();if(/\bfont(s)?\b/.test(s)||/\.woff2?(\b|$)/.test(s))return!0;for(let l of e){let i=l.toLowerCase().replace(/\s+/g,"+"),c=l.toLowerCase().replace(/\s+/g,"-");if(s.includes(i)||s.includes(c))return!0}return!1}catch{return!1}}function Xn(t){let e=new Set;for(let n of t||[]){let r=String(n).split("__")[0]?.trim();r&&e.add(r)}return e}function Me(t,e){return t&&t.replace(/url\(\s*(['"]?)([^)'"]+)\1\s*\)/g,(n,r,o)=>{let a=(o||"").trim();if(!a||/^data:|^blob:|^https?:|^file:|^about:/i.test(a))return n;let s=a;try{s=new URL(a,e||location.href).href}catch{}return`url("${s}")`})}var ne=/@import\s+(?:url\(\s*(['"]?)([^)"']+)\1\s*\)|(['"])([^"']+)\3)([^;]*);/g,Et=4;async function Yn(t,e,n){if(!t)return t;let r=new Set;function o(l,i){try{return new URL(l,i||location.href).href}catch{return l}}async function a(l,i,c=0){if(c>Et)return console.warn(`[snapDOM] @import depth exceeded (${Et}) at ${i}`),l;let u="",f=0,m;for(;m=ne.exec(l);){u+=l.slice(f,m.index),f=ne.lastIndex;let y=(m[2]||m[4]||"").trim(),b=o(y,i);if(r.has(b)){console.warn(`[snapDOM] Skipping circular @import: ${b}`);continue}r.add(b);let d="";try{let h=await L(b,{as:"text",useProxy:n,silent:!0});h.ok&&typeof h.data=="string"&&(d=h.data)}catch{}d?(d=Me(d,b),d=await a(d,b,c+1),u+=` +/* inlined: ${b} */ +${d} +`):u+=m[0]}return u+=l.slice(f),u}let s=Me(t,e||location.href);return s=await a(s,e||location.href,0),s}var $e=/url\((["']?)([^"')]+)\1\)/g,Gn=/@font-face[^{}]*\{[^}]*\}/g;function ve(t){if(!t)return[];let e=[],n=t.split(",").map(r=>r.trim()).filter(Boolean);for(let r of n){let o=r.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/);if(!o)continue;let a=o[1],s=o[2],l=i=>{if(!i.includes("?"))return parseInt(i,16);let c=parseInt(i.replace(/\?/g,"0"),16),u=parseInt(i.replace(/\?/g,"F"),16);return[c,u]};if(s){let i=l(a),c=l(s),u=Array.isArray(i)?i[0]:i,f=Array.isArray(c)?c[1]:c;e.push([Math.min(u,f),Math.max(u,f)])}else{let i=l(a);Array.isArray(i)?e.push([i[0],i[1]]):e.push([i,i])}}return e}function Ee(t,e){if(!e.length||!t||t.size===0)return!0;for(let n of t)for(let[r,o]of e)if(n>=r&&n<=o)return!0;return!1}function re(t,e){let n=[];if(!t)return n;for(let r of t.matchAll($e)){let o=(r[2]||"").trim();if(!(!o||o.startsWith("data:"))){if(!/^https?:/i.test(o))try{o=new URL(o,e||location.href).href}catch{}n.push(o)}}return n}async function Fe(t,e,n=""){let r=t;for(let o of t.matchAll($e)){let a=tt(o[0]);if(!a)continue;let s=a;if(!s.startsWith("http")&&!s.startsWith("data:"))try{s=new URL(s,e||location.href).href}catch{}if(!B(s)){if(w.resource?.has(s)){w.font?.add(s),r=r.replace(o[0],`url(${w.resource.get(s)})`);continue}if(!w.font?.has(s))try{let l=await L(s,{as:"dataURL",useProxy:n,silent:!0});if(l.ok&&typeof l.data=="string"){let i=l.data;w.resource?.set(s,i),w.font?.add(s),r=r.replace(o[0],`url(${i})`)}}catch{console.warn("[snapDOM] Failed to fetch font resource:",s)}}}return r}function Kn(t){if(!t.length)return null;let e=(l,i)=>t.some(([c,u])=>!(ui)),n=e(0,255)||e(305,305),r=e(256,591)||e(7680,7935),o=e(880,1023),a=e(1024,1279);return e(7840,7929)||e(258,259)||e(416,417)||e(431,432)?"vietnamese":a?"cyrillic":o?"greek":r?"latin-ext":n?"latin":null}function ke(t={}){let e=new Set((t.families||[]).map(o=>String(o).toLowerCase())),n=new Set((t.domains||[]).map(o=>String(o).toLowerCase())),r=new Set((t.subsets||[]).map(o=>String(o).toLowerCase()));return(o,a)=>{if(e.size&&e.has(o.family.toLowerCase()))return!0;if(n.size)for(let s of o.srcUrls)try{if(n.has(new URL(s).host.toLowerCase()))return!0}catch{}if(r.size){let s=Kn(a);if(s&&r.has(s))return!0}return!1}}function Jn(t){if(!t)return t;let e=/@font-face[^{}]*\{[^}]*\}/gi,n=new Set,r=[];for(let a of t.match(e)||[]){let s=a.match(/font-family:\s*([^;]+);/i)?.[1]||"",l=Ft(s),i=(a.match(/font-weight:\s*([^;]+);/i)?.[1]||"400").trim(),c=(a.match(/font-style:\s*([^;]+);/i)?.[1]||"normal").trim(),u=(a.match(/font-stretch:\s*([^;]+);/i)?.[1]||"100%").trim(),f=(a.match(/unicode-range:\s*([^;]+);/i)?.[1]||"").trim(),m=(a.match(/src\s*:\s*([^;]+);/i)?.[1]||"").trim(),y=re(m,location.href),b=y.length?y.map(h=>String(h).toLowerCase()).sort().join("|"):m.toLowerCase(),d=[String(l||"").toLowerCase(),i,c,u,f.toLowerCase(),b].join("|");n.has(d)||(n.add(d),r.push(a))}if(r.length===0)return t;let o=0;return t.replace(e,()=>r[o++]||"")}function Qn(t,e,n,r){let o=Array.from(t||[]).sort().join("|"),a=e?JSON.stringify({families:(e.families||[]).map(i=>String(i).toLowerCase()).sort(),domains:(e.domains||[]).map(i=>String(i).toLowerCase()).sort(),subsets:(e.subsets||[]).map(i=>String(i).toLowerCase()).sort()}):"",s=(n||[]).map(i=>`${(i.family||"").toLowerCase()}::${i.weight||"normal"}::${i.style||"normal"}::${i.src||""}`).sort().join("|");return`fonts-embed-css::req=${o}::ex=${a}::lf=${s}::px=${r||""}`}async function Ne(t,e,n,r){let o;try{o=t.cssRules||[]}catch{return}let a=(s,l)=>{try{return new URL(s,l||location.href).href}catch{return s}};for(let s of o){if(s.type===CSSRule.IMPORT_RULE&&s.styleSheet){let l=s.href?a(s.href,e):e;if(r.depth>=Et){console.warn(`[snapDOM] CSSOM import depth exceeded (${Et}) at ${l}`);continue}if(l&&r.visitedSheets.has(l)){console.warn(`[snapDOM] Skipping circular CSSOM import: ${l}`);continue}l&&r.visitedSheets.add(l);let i={...r,depth:(r.depth||0)+1};await Ne(s.styleSheet,l,n,i);continue}if(s.type===CSSRule.FONT_FACE_RULE){let l=(s.style.getPropertyValue("font-family")||"").trim(),i=Ft(l);if(!i||B(i))continue;let c=(s.style.getPropertyValue("font-weight")||"400").trim(),u=(s.style.getPropertyValue("font-style")||"normal").trim(),f=(s.style.getPropertyValue("font-stretch")||"100%").trim(),m=(s.style.getPropertyValue("src")||"").trim(),y=(s.style.getPropertyValue("unicode-range")||"").trim();if(!r.faceMatchesRequired(i,u,c,f))continue;let b=ve(y);if(!Ee(r.usedCodepoints,b))continue;let d={family:i,weightSpec:c,styleSpec:u,stretchSpec:f,unicodeRange:y,srcRaw:m,srcUrls:re(m,e||location.href),href:e||location.href};if(r.simpleExcluder&&r.simpleExcluder(d,b))continue;if(/url\(/i.test(m)){let h=await Fe(m,e||location.href,r.useProxy);await n(`@font-face{font-family:${i};src:${h};font-style:${u};font-weight:${c};font-stretch:${f};${y?`unicode-range:${y};`:""}}`)}else await n(`@font-face{font-family:${i};src:${m};font-style:${u};font-weight:${c};font-stretch:${f};${y?`unicode-range:${y};`:""}}`)}}}async function Nt({required:t,usedCodepoints:e,exclude:n=void 0,localFonts:r=[],useProxy:o=""}={}){t instanceof Set||(t=new Set),e instanceof Set||(e=new Set);let a=new Map;for(let d of t){let[h,p,g,S]=String(d).split("__");if(!h)continue;let x=a.get(h)||[];x.push({w:parseInt(p,10),s:g,st:parseInt(S,10)}),a.set(h,x)}function s(d,h,p,g){if(!a.has(d))return!1;let S=a.get(d),x=qn(p),C=jn(h),M=Vn(g),A=x.min!==x.max,P=x.min,W=$=>C.kind==="normal"&&$==="normal"||C.kind!=="normal"&&($==="italic"||$==="oblique"),N=!1;for(let $ of S){let k=A?$.w>=x.min&&$.w<=x.max:$.w===P,_=W(ee($.s)),O=$.st>=M.min&&$.st<=M.max;if(k&&_&&O){N=!0;break}}if(N)return!0;if(!A)for(let $ of S){let k=W(ee($.s)),_=$.st>=M.min&&$.st<=M.max;if(Math.abs(P-$.w)<=300&&k&&_)return!0}return!1}let l=ke(n),i=Qn(t,n,r,o);if(w.resource?.has(i))return w.resource.get(i);let c=Xn(t),u=[],f=ne;for(let d of document.querySelectorAll("style")){let h=d.textContent||"";for(let p of h.matchAll(f)){let g=(p[2]||p[4]||"").trim();if(!g||B(g))continue;!!document.querySelector(`link[rel="stylesheet"][href="${g}"]`)||u.push(g)}}u.length&&await Promise.all(u.map(d=>new Promise(h=>{if(document.querySelector(`link[rel="stylesheet"][href="${d}"]`))return h(null);let p=document.createElement("link");p.rel="stylesheet",p.href=d,p.setAttribute("data-snapdom","injected-import"),p.onload=()=>h(p),p.onerror=()=>h(null),document.head.appendChild(p)})));let m="",y=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(d=>!!d.href);for(let d of y)try{if(B(d.href))continue;let h="",p=!1;try{p=new URL(d.href,location.href).origin===location.origin}catch{}if(!p&&!zn(d.href,c))continue;if(p){let S=Array.from(document.styleSheets).find(x=>x.href===d.href);if(S)try{let x=S.cssRules||[];h=Array.from(x).map(C=>C.cssText).join("")}catch{}}if(!h&&(h=(await L(d.href,{as:"text",useProxy:o})).data,B(d.href)))continue;h=await Yn(h,d.href,o);let g="";for(let S of h.match(Gn)||[]){let x=(S.match(/font-family:\s*([^;]+);/i)?.[1]||"").trim(),C=Ft(x);if(!C||B(C))continue;let M=(S.match(/font-weight:\s*([^;]+);/i)?.[1]||"400").trim(),A=(S.match(/font-style:\s*([^;]+);/i)?.[1]||"normal").trim(),P=(S.match(/font-stretch:\s*([^;]+);/i)?.[1]||"100%").trim(),W=(S.match(/unicode-range:\s*([^;]+);/i)?.[1]||"").trim(),N=(S.match(/src\s*:\s*([^;]+);/i)?.[1]||"").trim(),$=re(N,d.href);if(!s(C,A,M,P))continue;let k=ve(W);if(!Ee(e,k))continue;let _={family:C,weightSpec:M,styleSpec:A,stretchSpec:P,unicodeRange:W,srcRaw:N,srcUrls:$,href:d.href};if(n&&l(_,k))continue;let O=/url\(/i.test(N)?await Fe(S,d.href,o):S;g+=O}g.trim()&&(m+=g)}catch{console.warn("[snapDOM] Failed to process stylesheet:",d.href)}let b={requiredIndex:a,usedCodepoints:e,faceMatchesRequired:s,simpleExcluder:n?ke(n):null,useProxy:o,visitedSheets:new Set,depth:0};for(let d of document.styleSheets)if(!(d.href&&y.some(h=>h.href===d.href)))try{let h=d.href||location.href;h&&b.visitedSheets.add(h),await Ne(d,h,async p=>{m+=p},b)}catch{}try{for(let d of document.fonts||[]){if(!d||!d.family||d.status!=="loaded"||!d._snapdomSrc)continue;let h=String(d.family).replace(/^['"]+|['"]+$/g,"");if(B(h)||!a.has(h)||n?.families&&n.families.some(g=>String(g).toLowerCase()===h.toLowerCase()))continue;let p=d._snapdomSrc;if(!String(p).startsWith("data:")){if(w.resource?.has(d._snapdomSrc))p=w.resource.get(d._snapdomSrc),w.font?.add(d._snapdomSrc);else if(!w.font?.has(d._snapdomSrc))try{let g=await L(d._snapdomSrc,{as:"dataURL",useProxy:o,silent:!0});if(g.ok&&typeof g.data=="string")p=g.data,w.resource?.set(d._snapdomSrc,p),w.font?.add(d._snapdomSrc);else continue}catch{console.warn("[snapDOM] Failed to fetch dynamic font src:",d._snapdomSrc);continue}}m+=`@font-face{font-family:'${h}';src:url(${p});font-style:${d.style||"normal"};font-weight:${d.weight||"normal"};}`}}catch{}for(let d of r){if(!d||typeof d!="object")continue;let h=String(d.family||"").replace(/^['"]+|['"]+$/g,"");if(!h||B(h)||!a.has(h)||n?.families&&n.families.some(M=>String(M).toLowerCase()===h.toLowerCase()))continue;let p=d.weight!=null?String(d.weight):"normal",g=d.style!=null?String(d.style):"normal",S=d.stretchPct!=null?`${d.stretchPct}%`:"100%",x=String(d.src||""),C=x;if(!C.startsWith("data:")){if(w.resource?.has(x))C=w.resource.get(x),w.font?.add(x);else if(!w.font?.has(x))try{let M=await L(x,{as:"dataURL",useProxy:o,silent:!0});if(M.ok&&typeof M.data=="string")C=M.data,w.resource?.set(x,C),w.font?.add(x);else continue}catch{console.warn("[snapDOM] Failed to fetch localFonts src:",x);continue}}m+=`@font-face{font-family:'${h}';src:url(${C});font-style:${g};font-weight:${p};font-stretch:${S};}`}return m&&(m=Jn(m),w.resource?.set(i,m)),m}function Lt(t){let e=new Set;if(!t)return e;let n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,null),r=s=>{let l=Ft(s.fontFamily);if(!l)return;let i=(c,u,f)=>`${l}__${vt(c)}__${ee(u)}__${Hn(f)}`;e.add(i(s.fontWeight,s.fontStyle,s.fontStretch))};r(getComputedStyle(t));let o=getComputedStyle(t,"::before");o&&o.content&&o.content!=="none"&&r(o);let a=getComputedStyle(t,"::after");for(a&&a.content&&a.content!=="none"&&r(a);n.nextNode();){let s=n.currentNode,l=getComputedStyle(s);r(l);let i=getComputedStyle(s,"::before");i&&i.content&&i.content!=="none"&&r(i);let c=getComputedStyle(s,"::after");c&&c.content&&c.content!=="none"&&r(c)}return e}function Rt(t){let e=new Set,n=o=>{if(o)for(let a of o)e.add(a.codePointAt(0))},r=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,null);for(;r.nextNode();){let o=r.currentNode;if(o.nodeType===Node.TEXT_NODE)n(o.nodeValue||"");else if(o.nodeType===Node.ELEMENT_NODE){let a=o;for(let s of["::before","::after"]){let i=getComputedStyle(a,s)?.getPropertyValue("content");if(!(!i||i==="none"))if(/^"/.test(i)||/^'/.test(i))n(i.slice(1,-1));else{let c=i.match(/\\[0-9A-Fa-f]{1,6}/g);if(c)for(let u of c)try{e.add(parseInt(u.slice(1),16))}catch{}}}}}return e}async function It(t,e=2){try{await document.fonts.ready}catch{}let n=Array.from(t||[]).filter(Boolean);if(n.length===0)return;let r=()=>{let o=document.createElement("div");o.style.cssText="position:absolute!important;left:-9999px!important;top:0!important;opacity:0!important;pointer-events:none!important;contain:layout size style;";for(let a of n){let s=document.createElement("span");s.textContent="AaBbGg1234\xC1\xC9\xCD\xD3\xDA\xE7\xF1\u2014\u221E",s.style.fontFamily=`"${a}"`,s.style.fontWeight="700",s.style.fontStyle="italic",s.style.fontSize="32px",s.style.lineHeight="1",s.style.whiteSpace="nowrap",s.style.margin="0",s.style.padding="0",o.appendChild(s)}document.body.appendChild(o),o.offsetWidth,document.body.removeChild(o)};for(let o=0;orequestAnimationFrame(()=>requestAnimationFrame(a)))}function Te(t){return/\bcounter\s*\(|\bcounters\s*\(/.test(t||"")}function Zn(t){return(t||"").replace(/"([^"]*)"/g,"$1")}function Le(t,e=!1){let n="",r=Math.max(1,t);for(;r>0;)r--,n=String.fromCharCode(97+r%26)+n,r=Math.floor(r/26);return e?n.toUpperCase():n}function Re(t,e=!0){let n=[[1e3,"M"],[900,"CM"],[500,"D"],[400,"CD"],[100,"C"],[90,"XC"],[50,"L"],[40,"XL"],[10,"X"],[9,"IX"],[5,"V"],[4,"IV"],[1,"I"]],r=Math.max(1,Math.min(3999,t)),o="";for(let[a,s]of n)for(;r>=a;)o+=s,r-=a;return e?o:o.toLowerCase()}function Ie(t,e){switch((e||"decimal").toLowerCase()){case"decimal":return String(Math.max(0,t));case"decimal-leading-zero":return(t<10?"0":"")+String(Math.max(0,t));case"lower-alpha":return Le(t,!1);case"upper-alpha":return Le(t,!0);case"lower-roman":return Re(t,!1);case"upper-roman":return Re(t,!0);default:return String(Math.max(0,t))}}function Pe(t){let e=new WeakMap,n=t instanceof Document?t.documentElement:t,r=c=>c&&c.tagName==="LI",o=c=>{let u=0,f=c?.parentElement;if(!f)return 0;for(let m of f.children){if(m===c)break;m.tagName==="LI"&&u++}return u},a=c=>{let u=new Map;for(let[f,m]of c)u.set(f,m.slice());return u},s=(c,u,f)=>{let m=a(c),y;try{y=f.style?.counterReset||getComputedStyle(f).counterReset}catch{}if(y&&y!=="none")for(let d of y.split(",")){let h=d.trim().split(/\s+/),p=h[0],g=Number.isFinite(Number(h[1]))?Number(h[1]):0;if(!p)continue;let S=u.get(p);if(S&&S.length){let x=S.slice();x.push(g),m.set(p,x)}else m.set(p,[g])}let b;try{b=f.style?.counterIncrement||getComputedStyle(f).counterIncrement}catch{}if(b&&b!=="none")for(let d of b.split(",")){let h=d.trim().split(/\s+/),p=h[0],g=Number.isFinite(Number(h[1]))?Number(h[1]):1;if(!p)continue;let S=m.get(p)||[];S.length===0&&S.push(0),S[S.length-1]+=g,m.set(p,S)}try{if(getComputedStyle(f).display==="list-item"&&r(f)){let h=f.parentElement,p=1;if(h&&h.tagName==="OL"){let S=h.getAttribute("start"),x=Number.isFinite(Number(S))?Number(S):1,C=o(f),M=f.getAttribute("value");p=Number.isFinite(Number(M))?Number(M):x+C}else p=1+o(f);let g=m.get("list-item")||[];g.length===0&&g.push(0),g[g.length-1]=p,m.set("list-item",g)}}catch{}return m},l=(c,u,f)=>{let m=s(f,u,c);e.set(c,m);let y=m;for(let b of c.children)y=l(b,m,y);return m},i=new Map;return l(n,i,i),{get(c,u){let f=e.get(c)?.get(u);return f&&f.length?f[f.length-1]:0},getStack(c,u){let f=e.get(c)?.get(u);return f?f.slice():[]}}}function _e(t,e,n){if(!t||t==="none")return t;try{let r=/\b(counter|counters)\s*\(([^)]+)\)/g,o=t.replace(r,(a,s,l)=>{let i=String(l).split(",").map(c=>c.trim());if(s==="counter"){let c=i[0]?.replace(/^["']|["']$/g,""),u=(i[1]||"decimal").toLowerCase(),f=n.get(e,c);return Ie(f,u)}else{let c=i[0]?.replace(/^["']|["']$/g,""),u=i[1]?.replace(/^["']|["']$/g,"")??"",f=(i[2]||"decimal").toLowerCase(),m=n.getStack(e,c);return m.length?m.map(b=>Ie(b,f)).join(u):""}});return Zn(o)}catch{return"- "}}var yt=null,wt=new WeakMap;function tr(t){return(t||"").replace(/"([^"]*)"/g,"$1")}function er(t){if(!t)return"";let e=[],n=/"([^"]*)"/g,r;for(;r=n.exec(t);)e.push(r[1]);return e.length?e.join(""):tr(t)}function oe(t,e){let n=t.parentElement,r=n?wt.get(n):null;return r?{get(o,a){let s=e.get(o,a),l=r.get(a);return typeof l=="number"?Math.max(s,l):s},getStack(o,a){let s=e.getStack(o,a);if(!s.length)return s;let l=r.get(a);if(typeof l=="number"){let i=s.slice();return i[i.length-1]=Math.max(i[i.length-1],l),i}return s}}:e}function se(t,e,n){let r=new Map;function o(i){let c=[];if(!i||i==="none")return c;for(let u of String(i).split(",")){let f=u.trim().split(/\s+/),m=f[0],y=Number.isFinite(Number(f[1]))?Number(f[1]):void 0;m&&c.push({name:m,num:y})}return c}let a=o(e?.counterReset),s=o(e?.counterIncrement);function l(i){if(r.has(i))return r.get(i).slice();let c=n.getStack(t,i);c=c.length?c.slice():[];let u=a.find(m=>m.name===i);if(u){let m=Number.isFinite(u.num)?u.num:0;c=c.length?[...c,m]:[m]}let f=s.find(m=>m.name===i);if(f){let m=Number.isFinite(f.num)?f.num:1;c.length===0&&(c=[0]),c[c.length-1]+=m}return r.set(i,c.slice()),c}return{get(i,c){let u=l(c);return u.length?u[u.length-1]:0},getStack(i,c){return l(c)},__incs:s}}function nr(t,e,n){let r;try{r=getComputedStyle(t,e)}catch{}let o=r?.content;if(!o||o==="none"||o==="normal")return{text:"",incs:[]};let a=oe(t,n),s=se(t,r,a),l=Te(o)?_e(o,t,s):o;return{text:er(l),incs:s.__incs||[]}}async function ae(t,e,n,r){if(!(t instanceof Element)||!(e instanceof Element))return;if(!yt)try{yt=Pe(t.ownerDocument||document)}catch{}for(let s of["::before","::after","::first-letter"])try{let l=rt(t,s);if(!l||typeof l[Symbol.iterator]!="function"||l.content==="none"&&l.backgroundImage==="none"&&l.backgroundColor==="transparent"&&(l.borderStyle==="none"||parseFloat(l.borderWidth)===0)&&(!l.transform||l.transform==="none")&&l.display==="inline")continue;if(s==="::first-letter"){let v=getComputedStyle(t);if(!(l.color!==v.color||l.fontSize!==v.fontSize||l.fontWeight!==v.fontWeight))continue;let R=Array.from(e.childNodes).find(ft=>ft.nodeType===Node.TEXT_NODE&&ft.textContent?.trim().length>0);if(!R)continue;let I=R.textContent,H=I.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u)?.[0],it=I.slice(H?.length||0);if(!H||/[\uD800-\uDFFF]/.test(H))continue;let J=document.createElement("span");J.textContent=H,J.dataset.snapdomPseudo="::first-letter";let xt=$t(l),Bt=ut(xt,"span");n.styleMap.set(J,Bt);let Ct=document.createTextNode(it);e.replaceChild(Ct,R),e.insertBefore(J,Ct);continue}let c=l.content,{text:u,incs:f}=nr(t,s,yt),m=l.backgroundImage,y=l.backgroundColor,b=l.fontFamily,d=parseInt(l.fontSize)||32,h=parseInt(l.fontWeight)||!1,p=l.color||"#000",g=l.borderStyle,S=parseFloat(l.borderWidth),x=l.transform,C=B(b),M=c!=="none"&&u!=="",A=m&&m!=="none",P=y&&y!=="transparent"&&y!=="rgba(0, 0, 0, 0)",W=g&&g!=="none"&&S>0,N=x&&x!=="none";if(!(M||A||P||W||N)){if(f&&f.length&&t.parentElement){let v=wt.get(t.parentElement)||new Map;for(let{name:F}of f){if(!F)continue;let R=oe(t,yt),D=se(t,getComputedStyle(t,s),R).get(t,F);v.set(F,D)}wt.set(t.parentElement,v)}continue}let k=document.createElement("span");k.dataset.snapdomPseudo=s,k.style.verticalAlign="middle",k.style.pointerEvents="none";let _=$t(l),O=ut(_,"span");if(n.styleMap.set(k,O),C&&u&&u.length===1){let{dataUrl:v,width:F,height:R}=await Ae(u,b,h,d,p),I=document.createElement("img");I.src=v,I.style=`height:${d}px;width:${F/R*d}px;object-fit:contain;`,k.appendChild(I),e.dataset.snapdomHasIcon="true"}else if(u&&u.startsWith("url(")){let v=tt(u);if(v?.trim())try{let F=document.createElement("img"),R=await L(Y(v),{as:"dataURL",useProxy:r.useProxy});F.src=R.data,F.style=`width:${d}px;height:auto;object-fit:contain;`,k.appendChild(F)}catch(F){console.error(`[snapdom] Error in pseudo ${s} for`,t,F)}}else!C&&M&&(k.textContent=u);if(k.style.background="none","mask"in k.style&&(k.style.mask="none"),A)try{let v=ot(m),F=await Promise.all(v.map(nt));k.style.backgroundImage=F.join(", ")}catch(v){console.warn(`[snapdom] Failed to inline background-image for ${s}`,v)}P&&(k.style.backgroundColor=y);let K=k.childNodes.length>0||k.textContent?.trim()!==""||A||P||W||N;if(f&&f.length&&t.parentElement){let v=wt.get(t.parentElement)||new Map,F=oe(t,yt),R=se(t,getComputedStyle(t,s),F);for(let{name:I}of f){if(!I)continue;let D=R.get(t,I);v.set(I,D)}wt.set(t.parentElement,v)}if(!K)continue;s==="::before"?e.insertBefore(k,e.firstChild):e.appendChild(k)}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,t,l)}let o=Array.from(t.children),a=Array.from(e.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s{let i=l.getAttribute("xlink:href")||l.getAttribute("href");i&&i.startsWith("#")&&e.add(i.slice(1))}),!e.size)return;let n=Array.from(document.querySelectorAll("svg > symbol, svg > defs")),r=n.filter(l=>l.tagName.toLowerCase()==="symbol"),o=n.filter(l=>l.tagName.toLowerCase()==="defs"),a=t.querySelector("svg.inline-defs-container");a||(a=document.createElementNS("http://www.w3.org/2000/svg","svg"),a.setAttribute("aria-hidden","true"),a.setAttribute("style","position: absolute; width: 0; height: 0; overflow: hidden;"),a.classList.add("inline-defs-container"),t.insertBefore(a,t.firstChild));let s=new Set;t.querySelectorAll("symbol[id], defs > *[id]").forEach(l=>{s.add(l.id)}),e.forEach(l=>{if(s.has(l))return;let i=r.find(c=>c.id===l);if(i){a.appendChild(i.cloneNode(!0)),s.add(l);return}for(let c of o){let u=c.querySelector(`#${CSS.escape(l)}`);if(u){let f=a.querySelector("defs");f||(f=document.createElementNS("http://www.w3.org/2000/svg","defs"),a.appendChild(f)),f.appendChild(u.cloneNode(!0)),s.add(l);break}}})}function Be(t,e){if(!t||!e)return;let n=t.scrollTop||0;if(!n)return;getComputedStyle(e).position==="static"&&(e.style.position="relative");let r=t.getBoundingClientRect(),o=t.clientHeight,a="data-snap-ph",s=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT);for(;s.nextNode();){let l=s.currentNode,i=getComputedStyle(l),c=i.position;if(c!=="sticky"&&c!=="-webkit-sticky")continue;let u=Ue(i.top),f=Ue(i.bottom);if(u==null&&f==null)continue;let m=rr(l,t),y=or(e,m,a);if(!y)continue;let b=l.getBoundingClientRect(),d=b.width,h=b.height,p=b.left-r.left;if(!(d>0&&h>0)||!Number.isFinite(p))continue;let g=u!=null?u+n:n+(o-h-f);if(!Number.isFinite(g))continue;let S=Number.parseInt(i.zIndex,10),x=Number.isFinite(S),C=x?Math.max(S,1)+1:2,M=x?S-1:0,A=y.cloneNode(!1);A.setAttribute(a,"1"),A.style.position="sticky",A.style.left=`${p}px`,A.style.top=`${g}px`,A.style.width=`${d}px`,A.style.height=`${h}px`,A.style.visibility="hidden",A.style.zIndex=String(M),A.style.overflow="hidden",A.style.background="transparent",A.style.boxShadow="none",A.style.filter="none",y.parentElement?.insertBefore(A,y),y.style.position="absolute",y.style.left=`${p}px`,y.style.top=`${g}px`,y.style.bottom="auto",y.style.zIndex=String(C),y.style.pointerEvents="none"}}function Ue(t){if(!t||t==="auto")return null;let e=Number.parseFloat(t);return Number.isFinite(e)?e:null}function rr(t,e){let n=[];for(let r=t;r&&r!==e;){let o=r.parentElement;if(!o)break;n.push(Array.prototype.indexOf.call(o.children,r)),r=o}return n.reverse()}function or(t,e,n){let r=t;for(let o=0;o`.${c}{${i}}`).join(""),o=a+o;for(let[i,c]of n.styleMap.entries()){if(i.tagName==="STYLE")continue;if(i.getRootNode&&i.getRootNode()instanceof ShadowRoot){i.setAttribute("style",c.replace(/;/g,"; "));continue}let u=s.get(c);u&&i.classList.add(u);let f=i.style?.backgroundImage,m=i.dataset?.snapdomHasIcon;f&&f!=="none"&&(i.style.backgroundImage=f),m&&(i.style.verticalAlign="middle",i.style.display="inline")}for(let[i,c]of n.nodeMap.entries()){let u=c.scrollLeft,f=c.scrollTop;if((u||f)&&i instanceof HTMLElement){i.style.overflow="hidden",i.style.scrollbarWidth="none",i.style.msOverflowStyle="none";let y=document.createElement("div");for(y.style.transform=`translate(${-u}px, ${-f}px)`,y.style.willChange="transform",y.style.display="inline-block",y.style.width="100%";i.firstChild;)y.appendChild(i.firstChild);i.appendChild(y)}}let l=r instanceof HTMLElement&&r.firstElementChild instanceof HTMLElement?r.firstElementChild:r;if(Be(t,l),t===n.nodeMap.get(r)){let i=n.styleCache.get(t)||window.getComputedStyle(t);n.styleCache.set(t,i);let c=jt(i.transform);r.style.margin="0",r.style.top="auto",r.style.left="auto",r.style.right="auto",r.style.bottom="auto",r.style.animation="none",r.style.transition="none",r.style.willChange="auto",r.style.float="none",r.style.clear="none",r.style.transform=c||""}for(let[i,c]of n.nodeMap.entries())c.tagName==="PRE"&&(i.style.marginTop="0",i.style.marginBlockStart="0");return{clone:r,classCSS:o,styleCache:n.styleCache}}function ar(t){let e=getComputedStyle(t),n=e.outlineStyle,r=e.outlineWidth,o=e.borderStyle,a=e.borderWidth,s=n!=="none"&&parseFloat(r)>0,l=o==="none"||parseFloat(a)===0;s&&l&&(t.style.border=`${r} solid transparent`)}var bt=new Map;async function St(t){if(w.resource?.has(t))return w.resource.get(t);if(bt.has(t))return bt.get(t);let e=(async()=>{let n=await L(t,{as:"dataURL",silent:!0});if(!n.ok||typeof n.data!="string")throw new Error(`[snapDOM] Failed to read blob URL: ${t}`);return w.resource?.set(t,n.data),n.data})();bt.set(t,e);try{let n=await e;return bt.set(t,n),n}catch(n){throw bt.delete(t),n}}var ir=/\bblob:[^)"'\s]+/g;async function Oe(t){if(!t||t.indexOf("blob:")===-1)return t;let e=Array.from(new Set(t.match(ir)||[]));if(e.length===0)return t;let n=t;for(let r of e)try{let o=await St(r);n=n.split(r).join(o)}catch{}return n}function Tt(t){return typeof t=="string"&&t.startsWith("blob:")}function cr(t){return(t||"").split(",").map(e=>e.trim()).filter(Boolean).map(e=>{let n=e.match(/^(\S+)(\s+.+)?$/);return n?{url:n[1],desc:n[2]||""}:null}).filter(Boolean)}function lr(t){return t.map(e=>e.desc?`${e.url} ${e.desc.trim()}`:e.url).join(", ")}async function ur(t){if(!t)return;let e=t.querySelectorAll?t.querySelectorAll("img"):[];for(let s of e)try{let i=s.getAttribute("src")||s.currentSrc||"";if(Tt(i)){let u=await St(i);s.setAttribute("src",u)}let c=s.getAttribute("srcset");if(c&&c.includes("blob:")){let u=cr(c),f=!1;for(let m of u)if(Tt(m.url))try{m.url=await St(m.url),f=!0}catch{}f&&s.setAttribute("srcset",lr(u))}}catch{}let n=t.querySelectorAll?t.querySelectorAll("image"):[];for(let s of n)try{let l="http://www.w3.org/1999/xlink",i=s.getAttribute("href")||s.getAttributeNS?.(l,"href");if(Tt(i)){let c=await St(i);s.setAttribute("href",c),s.removeAttributeNS?.(l,"href")}}catch{}let r=t.querySelectorAll?t.querySelectorAll("[style*='blob:']"):[];for(let s of r)try{let l=s.getAttribute("style");if(l&&l.includes("blob:")){let i=await Oe(l);s.setAttribute("style",i)}}catch{}let o=t.querySelectorAll?t.querySelectorAll("style"):[];for(let s of o)try{let l=s.textContent||"";l.includes("blob:")&&(s.textContent=await Oe(l))}catch{}let a=["poster"];for(let s of a){let l=t.querySelectorAll?t.querySelectorAll(`[${s}^='blob:']`):[];for(let i of l)try{let c=i.getAttribute(s);Tt(c)&&i.setAttribute(s,await St(c))}catch{}}}async function He(t,e={}){let n=Array.from(t.querySelectorAll("img")),r=async o=>{if(!o.getAttribute("src")){let u=o.currentSrc||o.src||"";u&&o.setAttribute("src",u)}o.removeAttribute("srcset"),o.removeAttribute("sizes");let a=o.src||"";if(!a)return;let s=await L(a,{as:"dataURL",useProxy:e.useProxy});if(s.ok&&typeof s.data=="string"&&s.data.startsWith("data:")){o.src=s.data,o.width||(o.width=o.naturalWidth||100),o.height||(o.height=o.naturalHeight||100);return}let{fallbackURL:l}=e||{};if(l)try{let u=parseInt(o.dataset?.snapdomWidth||"",10)||0,f=parseInt(o.dataset?.snapdomHeight||"",10)||0,m=parseInt(o.getAttribute("width")||"",10)||0,y=parseInt(o.getAttribute("height")||"",10)||0,b=parseFloat(o.style?.width||"")||0,d=parseFloat(o.style?.height||"")||0,h=u||b||m||o.width||void 0,p=f||d||y||o.height||void 0,g=typeof l=="function"?await l({width:h,height:p,src:a,element:o}):l;if(g){let S=await L(g,{as:"dataURL",useProxy:e.useProxy});o.src=S.data,!o.width&&h&&(o.width=h),!o.height&&p&&(o.height=p),o.width||(o.width=o.naturalWidth||100),o.height||(o.height=o.naturalHeight||100);return}}catch{}let i=o.width||o.naturalWidth||100,c=o.height||o.naturalHeight||100;if(e.placeholders!==!1){let u=document.createElement("div");u.style.cssText=[`width:${i}px`,`height:${c}px`,"background:#ccc","display:inline-block","text-align:center",`line-height:${c}px`,"color:#666","font-size:12px","overflow:hidden"].join(";"),u.textContent="img",o.replaceWith(u)}else{let u=document.createElement("div");u.style.cssText=`display:inline-block;width:${i}px;height:${c}px;visibility:hidden;`,o.replaceWith(u)}};for(let o=0;o{let b=u.getPropertyValue("border-image"),d=u.getPropertyValue("border-image-source");return b&&b!=="none"||d&&d!=="none"})();for(let b of a){let d=u.getPropertyValue(b);if(!d||d==="none")continue;let h=ot(d),p=await Promise.all(h.map(g=>nt(g,r)));p.some(g=>g&&g!=="none"&&!/^url\(undefined/.test(g))&&c.style.setProperty(b,p.join(", "))}for(let b of s){let d=u.getPropertyValue(b);!d||d==="initial"||c.style.setProperty(b,d)}if(f)for(let b of l){let d=u.getPropertyValue(b);!d||d==="initial"||c.style.setProperty(b,d)}let m=Array.from(i.children),y=Array.from(c.children);for(let b=0;b{};let e=fr(t);if(e<=0)return()=>{};if(!hr(t))return()=>{};let n=getComputedStyle(t),r=Math.round(dr(n)*e+mr(n)),o=t.textContent??"",a=o;if(t.scrollHeight<=r+.5)return()=>{};let s=0,l=o.length,i=-1;for(;s<=l;){let c=s+l>>1;t.textContent=o.slice(0,c)+"\u2026",t.scrollHeight<=r+.5?(i=c,s=c+1):l=c-1}return t.textContent=(i>=0?o.slice(0,i):"")+"\u2026",()=>{t.textContent=a}}function fr(t){let e=getComputedStyle(t),n=e.getPropertyValue("-webkit-line-clamp")||e.getPropertyValue("line-clamp");n=(n||"").trim();let r=parseInt(n,10);return Number.isFinite(r)&&r>0?r:0}function dr(t){let e=(t.lineHeight||"").trim(),n=parseFloat(t.fontSize)||16;return!e||e==="normal"?Math.round(n*1.2):e.endsWith("px")?parseFloat(e):/^\d+(\.\d+)?$/.test(e)?Math.round(parseFloat(e)*n):e.endsWith("%")?Math.round(parseFloat(e)/100*n):Math.round(n*1.2)}function mr(t){return(parseFloat(t.paddingTop)||0)+(parseFloat(t.paddingBottom)||0)}function hr(t){return t.childElementCount>0?!1:Array.from(t.childNodes).some(e=>e.nodeType===Node.TEXT_NODE)}function pr(t,e){if(!t||!e||!e.style)return;let n=getComputedStyle(t);try{e.style.boxShadow="none"}catch{}try{e.style.textShadow="none"}catch{}try{e.style.outline="none"}catch{}let o=(n.filter||"").replace(/\bblur\([^()]*\)\s*/gi,"").replace(/\bdrop-shadow\([^()]*\)\s*/gi,"").trim().replace(/\s+/g," ");try{e.style.filter=o.length?o:"none"}catch{}}async function ce(t,e){if(!t)throw new Error("Element cannot be null or undefined");Mt(e.cache);let n=e.fast,r=!!e.straighten,o=!!e.noShadows,a,s,l,i="",c="",u,f,m=null,y=qe(t);try{({clone:a,classCSS:s,styleCache:l}=await De(t,e)),r&&a&&(m=gr(t,a)),o&&a&&pr(t,a)}finally{y()}await new Promise(p=>{j(async()=>{await He(a,e),p()},{fast:n})}),await new Promise(p=>{j(async()=>{await Pt(t,a,l,e),p()},{fast:n})}),e.embedFonts&&await new Promise(p=>{j(async()=>{let g=Lt(t),S=Rt(t);if(T()){let x=new Set(Array.from(g).map(C=>String(C).split("__")[0]).filter(Boolean));await It(x,1)}i=await Nt({required:g,usedCodepoints:S,preCached:!1,exclude:e.excludeFonts,useProxy:e.useProxy}),p()},{fast:n})});let b=Yt(a).sort(),d=b.join(",");w.baseStyle.has(d)?c=w.baseStyle.get(d):await new Promise(p=>{j(()=>{c=Gt(b),w.baseStyle.set(d,c),p()},{fast:n})}),await new Promise(p=>{j(()=>{let g=getComputedStyle(t);function S(q){let U=`${q.filter||""} ${q.webkitFilter||""}`.trim();if(!U||U==="none")return{bleed:{top:0,right:0,bottom:0,left:0},has:!1};let X=U.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi)||[],Q=0,dt=0,mt=0,ht=0,Z=!1;for(let en of X){Z=!0;let nn=en.match(/-?\d+(?:\.\d+)?px/gi)?.map(rn=>parseFloat(rn))||[],[Ht=0,qt=0,de=0]=nn,me=Math.abs(Ht)+de,he=Math.abs(qt)+de;dt=Math.max(dt,me+Math.max(Ht,0)),ht=Math.max(ht,me+Math.max(-Ht,0)),mt=Math.max(mt,he+Math.max(qt,0)),Q=Math.max(Q,he+Math.max(-qt,0))}return{bleed:{top:Math.ceil(Q),right:Math.ceil(dt),bottom:Math.ceil(mt),left:Math.ceil(ht)},has:Z}}let x=t.getBoundingClientRect(),C=Math.max(1,Math.ceil(t.offsetWidth||parseFloat(g.width)||x.width||1)),M=Math.max(1,Math.ceil(t.offsetHeight||parseFloat(g.height)||x.height||1)),A=(q,U=NaN)=>{let X=typeof q=="string"?parseFloat(q):q;return Number.isFinite(X)?X:U},P=A(e.width),W=A(e.height),N=C,$=M,k=Number.isFinite(P),_=Number.isFinite(W),O=M>0?C/M:1;k&&_?(N=Math.max(1,Math.ceil(P)),$=Math.max(1,Math.ceil(W))):k?(N=Math.max(1,Math.ceil(P)),$=Math.max(1,Math.ceil(N/(O||1)))):_?($=Math.max(1,Math.ceil(W)),N=Math.max(1,Math.ceil($*(O||1)))):(N=C,$=M);let at=0,K=0,v=C,F=M;if(r&&m&&Number.isFinite(m.a)){let q={a:m.a,b:m.b||0,c:m.c||0,d:m.d||1,e:0,f:0},U=je(C,M,q,0,0);at=U.minX,K=U.minY,v=U.maxX,F=U.maxY}else if(!r&&Sr(t)){let U=g.transform&&g.transform!=="none"?g.transform:"",X=xr(t),Q=Ar({baseTransform:U,rotate:X.rotate||"0deg",scale:X.scale,translate:X.translate}),{ox:dt,oy:mt}=Mr(g,C,M),ht=Q.is2D?Q:new DOMMatrix(Q.toString()),Z=je(C,M,ht,dt,mt);at=Z.minX,K=Z.minY,v=Z.maxX,F=Z.maxY}let R=yr(g),I=wr(g),D=br(g),H=S(g),it=o?{top:0,right:0,bottom:0,left:0}:{top:R.top+I.top+D.top+H.bleed.top,right:R.right+I.right+D.right+H.bleed.right,bottom:R.bottom+I.bottom+D.bottom+H.bleed.bottom,left:R.left+I.left+D.left+H.bleed.left};at-=it.left,K-=it.top,v+=it.right,F+=it.bottom;let J=Math.max(1,Math.ceil(v-at)),xt=Math.max(1,Math.ceil(F-K)),Bt=Math.max(1,Math.round(J*(k||_?N/C:1))),Ct=Math.max(1,Math.round(xt*(_||k?$/M:1))),ft="http://www.w3.org/2000/svg",V=(T()?1:0)+(r?1:0),z=document.createElementNS(ft,"foreignObject"),Ke=Math.floor(at),Je=Math.floor(K);z.setAttribute("x",String(-(Ke-V))),z.setAttribute("y",String(-(Je-V))),z.setAttribute("width",String(Math.ceil(C+V*2))),z.setAttribute("height",String(Math.ceil(M+V*2))),z.style.overflow="visible";let ue=document.createElement("style");ue.textContent=c+i+"svg{overflow:visible;} foreignObject{overflow:visible;}"+s,z.appendChild(ue);let ct=document.createElement("div");ct.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),ct.style.width=`${C}px`,ct.style.height=`${M}px`,ct.style.overflow="visible",a.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),ct.appendChild(a),z.appendChild(ct);let Qe=new XMLSerializer().serializeToString(z),Ot=J+V*2,Dt=xt+V*2,fe=k||_;e.meta={w0:C,h0:M,vbW:Ot,vbH:Dt,targetW:N,targetH:$};let Ze=T()&&fe?Ot:Bt+V*2,tn=T()&&fe?Dt:Ct+V*2;f=``+Qe+"",u=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(f)}`,p()},{fast:n})});let h=document.getElementById("snapdom-sandbox");return h&&h.style.position==="absolute"&&h.remove(),u}function gr(t,e){if(!t||!e||!e.style)return null;let n=getComputedStyle(t);try{e.style.transformOrigin="0 0"}catch{}try{"translate"in e.style&&(e.style.translate="none"),"rotate"in e.style&&(e.style.rotate="none")}catch{}let r=n.transform||"none";if(!r||r==="none")try{let a=Ve(t);if(a.a===1&&a.b===0&&a.c===0&&a.d===1)return e.style.transform="none",{a:1,b:0,c:0,d:1}}catch{}let o=r.match(/^matrix\(\s*([^)]+)\)$/i);if(o){let a=o[1].split(",").map(s=>parseFloat(s.trim()));if(a.length===6&&a.every(Number.isFinite)){let[s,l,i,c]=a,u=Math.sqrt(s*s+l*l)||0,f=0,m=0,y=0,b=0,d=0,h=0;u>0&&(f=s/u,m=l/u,y=f*i+m*c,b=i-f*y,d=c-m*y,h=Math.sqrt(b*b+d*d)||0,h>0?y=y/h:y=0);let p=u,g=0,S=y*h,x=h;try{e.style.transform=`matrix(${p}, ${g}, ${S}, ${x}, 0, 0)`}catch{}return{a:p,b:g,c:S,d:x}}}try{let a=String(r).trim();return e.style.transform=a+" translate(0px, 0px) rotate(0deg)",null}catch{return null}}function yr(t){let e=t.boxShadow||"";if(!e||e==="none")return{top:0,right:0,bottom:0,left:0};let n=e.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map(l=>l.trim()),r=0,o=0,a=0,s=0;for(let l of n){let i=l.match(/-?\d+(\.\d+)?px/g)?.map(d=>parseFloat(d))||[];if(i.length<2)continue;let[c,u,f=0,m=0]=i,y=Math.abs(c)+f+m,b=Math.abs(u)+f+m;o=Math.max(o,y+Math.max(c,0)),s=Math.max(s,y+Math.max(-c,0)),a=Math.max(a,b+Math.max(u,0)),r=Math.max(r,b+Math.max(-u,0))}return{top:Math.ceil(r),right:Math.ceil(o),bottom:Math.ceil(a),left:Math.ceil(s)}}function wr(t){let e=(t.filter||"").match(/blur\(\s*([0-9.]+)px\s*\)/),n=e?Math.ceil(parseFloat(e[1])||0):0;return{top:n,right:n,bottom:n,left:n}}function br(t){if((t.outlineStyle||"none")==="none")return{top:0,right:0,bottom:0,left:0};let e=Math.ceil(parseFloat(t.outlineWidth||"0")||0);return{top:e,right:e,bottom:e,left:e}}function je(t,e,n,r,o){let a=n.a,s=n.b,l=n.c,i=n.d,c=n.e||0,u=n.f||0;function f(p,g){let S=p-r,x=g-o,C=a*S+l*x,M=s*S+i*x;return C+=r+c,M+=o+u,[C,M]}let m=[f(0,0),f(t,0),f(0,e),f(t,e)],y=1/0,b=1/0,d=-1/0,h=-1/0;for(let[p,g]of m)pd&&(d=p),g>h&&(h=g);return{minX:y,minY:b,maxX:d,maxY:h,width:d-y,height:h-b}}function Sr(t){return Cr(t)}function Ve(t){let e=getComputedStyle(t).transform;if(!e||e==="none")return new DOMMatrix;try{return new DOMMatrix(e)}catch{return new WebKitCSSMatrix(e)}}function xr(t){let e={rotate:"0deg",scale:null,translate:null},n=typeof t.computedStyleMap=="function"?t.computedStyleMap():null;if(n){let o=i=>{try{return typeof n.has=="function"&&!n.has(i)||typeof n.get!="function"?null:n.get(i)}catch{return null}},a=o("rotate");if(a)if(a.angle){let i=a.angle;e.rotate=i.unit==="rad"?i.value*180/Math.PI+"deg":i.value+i.unit}else a.unit?e.rotate=a.unit==="rad"?a.value*180/Math.PI+"deg":a.value+a.unit:e.rotate=String(a);else{let i=getComputedStyle(t);e.rotate=i.rotate&&i.rotate!=="none"?i.rotate:"0deg"}let s=o("scale");if(s){let i="x"in s&&s.x?.value!=null?s.x.value:Array.isArray(s)?s[0]?.value:Number(s)||1,c="y"in s&&s.y?.value!=null?s.y.value:Array.isArray(s)?s[1]?.value:i;e.scale=`${i} ${c}`}else{let i=getComputedStyle(t);e.scale=i.scale&&i.scale!=="none"?i.scale:null}let l=o("translate");if(l){let i="x"in l&&"value"in l.x?l.x.value:Array.isArray(l)?l[0]?.value:0,c="y"in l&&"value"in l.y?l.y.value:Array.isArray(l)?l[1]?.value:0,u="x"in l&&l.x?.unit?l.x.unit:"px",f="y"in l&&l.y?.unit?l.y.unit:"px";e.translate=`${i}${u} ${c}${f}`}else{let i=getComputedStyle(t);e.translate=i.translate&&i.translate!=="none"?i.translate:null}return e}let r=getComputedStyle(t);return e.rotate=r.rotate&&r.rotate!=="none"?r.rotate:"0deg",e.scale=r.scale&&r.scale!=="none"?r.scale:null,e.translate=r.translate&&r.translate!=="none"?r.translate:null,e}function Cr(t){let e=getComputedStyle(t),n=e.transform||"none";if(n!=="none"&&!/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(n))return!0;let o=e.rotate&&e.rotate!=="none"&&e.rotate!=="0deg",a=e.scale&&e.scale!=="none"&&e.scale!=="1",s=e.translate&&e.translate!=="none"&&e.translate!=="0px 0px";return!!(o||a||s)}function Mr(t,e,n){let r=(t.transformOrigin||"0 0").trim().split(/\s+/),[o,a]=[r[0]||"0",r[1]||"0"],s=(l,i)=>{let c=l.toLowerCase();return c==="left"||c==="top"?0:c==="center"?i/2:c==="right"||c==="bottom"?i:c.endsWith("px")?parseFloat(c)||0:c.endsWith("%")?(parseFloat(c)||0)*i/100:/^-?\d+(\.\d+)?$/.test(c)&&parseFloat(c)||0};return{ox:s(o,e),oy:s(a,n)}}var ie=null;function kr(){if(ie)return ie;let t=document.createElement("div");return t.id="snapdom-measure-slot",t.setAttribute("aria-hidden","true"),Object.assign(t.style,{position:"absolute",left:"-99999px",top:"0px",width:"0px",height:"0px",overflow:"hidden",opacity:"0",pointerEvents:"none",contain:"size layout style"}),document.documentElement.appendChild(t),ie=t,t}function Ar(t){let e=kr(),n=document.createElement("div");n.style.transformOrigin="0 0",t.baseTransform&&(n.style.transform=t.baseTransform),t.rotate&&(n.style.rotate=t.rotate),t.scale&&(n.style.scale=t.scale),t.translate&&(n.style.translate=t.translate),e.appendChild(n);let r=Ve(n);return e.removeChild(n),r}function $r(t){if(typeof t=="string"){let e=t.toLowerCase().trim();if(e==="disabled"||e==="full"||e==="auto"||e==="soft")return e}return"soft"}function ze(t={}){let e=t.format??"png",n=$r(t.cache);return{debug:t.debug??!1,fast:t.fast??!0,scale:t.scale??1,exclude:t.exclude??[],excludeMode:t.excludeMode??"hide",filter:t.filter??null,filterMode:t.filterMode??"hide",placeholders:t.placeholders!==!1,embedFonts:t.embedFonts??!1,iconFonts:Array.isArray(t.iconFonts)?t.iconFonts:t.iconFonts?[t.iconFonts]:[],localFonts:Array.isArray(t.localFonts)?t.localFonts:[],excludeFonts:t.excludeFonts??void 0,fallbackURL:t.fallbackURL??void 0,cache:n,useProxy:typeof t.useProxy=="string"?t.useProxy:"",width:t.width??null,height:t.height??null,format:e,type:t.type??"svg",quality:t.quality??.92,dpr:t.dpr??(window.devicePixelRatio||1),backgroundColor:t.backgroundColor??(["jpg","jpeg","webp"].includes(e)?"#ffffff":null),filename:t.filename??"snapDOM",straighten:t.straighten??!1,noShadows:t.noShadows??!1}}function vr(t){return typeof t=="string"&&/^data:image\/svg\+xml/i.test(t)}function Er(t){let e=t.indexOf(",");return e>=0?decodeURIComponent(t.slice(e+1)):""}function Fr(t){return`data:image/svg+xml;charset=utf-8,${encodeURIComponent(t)}`}function Nr(t){let e=[],n="",r=0;for(let o=0;oo.trim()).filter(Boolean)}function Lr(t){let e=[],n="",r=0;for(let a=0;a`${l}:${i}`).join(";")}function Rr(t){return t.replace(/([^{}]+)\{([^}]*)\}/g,(e,n,r)=>`${n}{${Xe(r)}}`)}function Ir(t){return t=t.replace(/]*>([\s\S]*?)<\/style>/gi,(e,n)=>e.replace(n,Rr(n))),t=t.replace(/style=(['"])([\s\S]*?)\1/gi,(e,n,r)=>`style=${n}${Xe(r)}${n}`),t}function Tr(t){if(!T()||!vr(t))return t;try{let e=Er(t),n=Ir(e);return Fr(n)}catch{return t}}async function G(t,e){let{width:n,height:r,scale:o=1,dpr:a=1,meta:s={}}=e;t=Tr(t);let l=new Image;l.loading="eager",l.decoding="sync",l.crossOrigin="anonymous",l.src=t,await l.decode();let i=l.naturalWidth,c=l.naturalHeight,u=Number.isFinite(s.w0)?s.w0:i,f=Number.isFinite(s.h0)?s.h0:c,m,y,b=Number.isFinite(n),d=Number.isFinite(r);if(b&&d)m=Math.max(1,n),y=Math.max(1,r);else if(b){let g=n/Math.max(1,u);m=n,y=Math.round(f*g)}else if(d){let g=r/Math.max(1,f);y=r,m=Math.round(u*g)}else m=i,y=c;m=Math.round(m*o),y=Math.round(y*o);let h=document.createElement("canvas");h.width=Math.ceil(m*a),h.height=Math.ceil(y*a),h.style.width=`${m}px`,h.style.height=`${y}px`;let p=h.getContext("2d");return a!==1&&p.scale(a,a),p.drawImage(l,0,0,m,y),h}async function _t(t,e){let n=await G(t,e),r=e.backgroundColor?et(n,e.backgroundColor):n,o=new Image;return o.src=r.toDataURL(`image/${e.format}`,e.quality),await o.decode(),o.style.width=`${r.width/e.dpr}px`,o.style.height=`${r.height/e.dpr}px`,o}async function Wt(t,e){let{scale:n=1,width:r,height:o,meta:a={}}=e,s=Number.isFinite(r),l=Number.isFinite(o),i=Number.isFinite(n)&&n!==1||s||l;if(T()&&i)return await _t(t,{...e,format:"png",quality:1,meta:a});let c=new Image;if(c.decoding="sync",c.loading="eager",c.src=t,await c.decode(),s&&l)c.style.width=`${r}px`,c.style.height=`${o}px`;else if(s){let u=Number.isFinite(a.w0)?a.w0:c.naturalWidth,f=Number.isFinite(a.h0)?a.h0:c.naturalHeight,m=r/Math.max(1,u);c.style.width=`${r}px`,c.style.height=`${Math.round(f*m)}px`}else if(l){let u=Number.isFinite(a.w0)?a.w0:c.naturalWidth,f=Number.isFinite(a.h0)?a.h0:c.naturalHeight,m=o/Math.max(1,f);c.style.height=`${o}px`,c.style.width=`${Math.round(u*m)}px`}else{let u=Math.round(c.naturalWidth*n),f=Math.round(c.naturalHeight*n);if(c.style.width=`${u}px`,c.style.height=`${f}px`,typeof t=="string"&&t.startsWith("data:image/svg+xml"))try{let y=decodeURIComponent(t.split(",")[1]).replace(/width="[^"]*"/,`width="${u}"`).replace(/height="[^"]*"/,`height="${f}"`);t=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(y)}`,c.src=t}catch{}}return c}async function Ut(t,e){let n=e.type;if(n==="svg"){let a=decodeURIComponent(t.split(",")[1]);return new Blob([a],{type:"image/svg+xml"})}let r=await G(t,e),o=e.backgroundColor?et(r,e.backgroundColor):r;return new Promise(a=>o.toBlob(s=>a(s),`image/${n}`,e.quality))}async function Ye(t,e){if(e.dpr=1,e.format==="svg"){let a=await Ut(t,{...e,type:"svg"}),s=URL.createObjectURL(a),l=document.createElement("a");l.href=s,l.download=e.filename,l.click(),URL.revokeObjectURL(s);return}let n=await G(t,e),r=e.backgroundColor?et(n,e.backgroundColor):n,o=document.createElement("a");o.href=r.toDataURL(`image/${e.format}`,e.quality),o.download=e.filename,o.click()}var Ge=Symbol("snapdom.internal"),le=!1;async function E(t,e){if(!t)throw new Error("Element cannot be null or undefined");let n=ze(e);if(T()&&(n.embedFonts===!0||_r(t)))for(let r=0;r<3;r++)try{await Pr(t,e),console.log("Iteraci\xF3n n\xFAmero:",r),le=!1}catch{}return n.iconFonts&&n.iconFonts.length>0&&Ce(n.iconFonts),n.snap||(n.snap={toPng:(r,o)=>E.toPng(r,o),toSvg:(r,o)=>E.toSvg(r,o)}),E.capture(t,n,Ge)}E.capture=async(t,e,n)=>{if(n!==Ge)throw new Error("[snapdom.capture] is internal. Use snapdom(...) instead.");let r=await ce(t,e),o=s=>({...e,...s||{}}),a=s=>l=>{let i=o({...l||{},format:s}),c=s==="jpeg"||s==="jpg",u=i.backgroundColor==null||i.backgroundColor==="transparent";return c&&u&&(i.backgroundColor="#ffffff"),_t(r,i)};return{url:r,toRaw:()=>r,toImg:s=>Wt(r,o(s)),toSvg:s=>Wt(r,o(s)),toCanvas:s=>G(r,o(s)),toBlob:s=>Ut(r,o(s)),toPng:a("png"),toJpg:a("jpeg"),toWebp:a("webp"),download:s=>Ye(r,o(s))}};E.toRaw=(t,e)=>E(t,e).then(n=>n.toRaw());E.toImg=(t,e)=>E(t,e).then(n=>n.toImg());E.toSvg=(t,e)=>E(t,e).then(n=>n.toSvg());E.toCanvas=(t,e)=>E(t,e).then(n=>n.toCanvas());E.toBlob=(t,e)=>E(t,e).then(n=>n.toBlob());E.toPng=(t,e)=>E(t,{...e,format:"png"}).then(n=>n.toPng());E.toJpg=(t,e)=>E(t,{...e,format:"jpeg"}).then(n=>n.toJpg());E.toWebp=(t,e)=>E(t,{...e,format:"webp"}).then(n=>n.toWebp());E.download=(t,e)=>E(t,e).then(n=>n.download());async function Pr(t,e){if(le)return;let n={...e,fast:!0,embedFonts:!0,scale:.2},r;try{r=await ce(t,n)}catch{return}await new Promise(o=>{let a=new Image;a.decoding="sync",a.loading="eager",a.style.position="fixed",a.style.left=0,a.style.top=0,a.style.width="10px",a.style.height="10px",a.style.opacity="0.01",a.style.transform="translateZ(10px)",a.style.willChange="transform,opacity;",a.src=r;let s=async()=>{await new Promise(l=>setTimeout(l,100)),a&&a.parentNode&&a.parentNode.removeChild(a),le=!0,o()};document.body.appendChild(a),s()})}function _r(t){let e=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT);for(;e.nextNode();){let n=e.currentNode,r=getComputedStyle(n),o=r.backgroundImage&&r.backgroundImage!=="none",a=r.maskImage&&r.maskImage!=="none"||r.webkitMaskImage&&r.webkitMaskImage!=="none";if(o||a)return!0}return!1}async function Wr(t=document,e={}){let{embedFonts:n=!0,useProxy:r=""}=e,o=e.cache??e.cacheOpt??"full";Mt(o);try{await document.fonts?.ready}catch{}try{zt()}catch{}w.session=w.session||{},w.session.styleCache||(w.session.styleCache=new WeakMap),w.image=w.image||new Map;try{await Pt(t,void 0,w.session.styleCache,{useProxy:r})}catch{}let a=[],s=[];try{t?.querySelectorAll&&(a=Array.from(t.querySelectorAll("img[src]")),s=Array.from(t.querySelectorAll("*")))}catch{}let l=[];for(let i of a){let c=i?.currentSrc||i?.src;if(c&&!w.image.has(c)){let u=Promise.resolve().then(async()=>{let f=await L(c,{as:"dataURL",useProxy:r});f?.ok&&typeof f.data=="string"&&w.image.set(c,f.data)}).catch(()=>{});l.push(u)}}for(let i of s){let c="";try{c=rt(i).backgroundImage}catch{}if(c&&c!=="none"){let u=ot(c);for(let f of u)if(f.startsWith("url(")){let m=Promise.resolve().then(()=>nt(f,{...e,useProxy:r})).catch(()=>{});l.push(m)}}}if(n)try{let i=Lt(t),c=Rt(t);if(typeof T=="function"?T():!!T){let f=new Set(Array.from(i).map(m=>String(m).split("__")[0]).filter(Boolean));await It(f,3)}await Nt({required:i,usedCodepoints:c,exclude:e.excludeFonts,localFonts:e.localFonts,useProxy:e.useProxy??r})}catch{}await Promise.allSettled(l)}export{Wr as preCache,E as snapdom}; diff --git a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs b/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs deleted file mode 100644 index 8fa72522..00000000 --- a/src/components/BootstrapBlazor.Dom2Image/wwwroot/lib/snapdom.mjs +++ /dev/null @@ -1,1781 +0,0 @@ - -/* -* snapdom -* v.1.9.9 -* Author Juan Martin Muda -* License MIT -**/ - - -// src/core/cache.js -var cache = { - image: /* @__PURE__ */ new Map(), - background: /* @__PURE__ */ new Map(), - resource: /* @__PURE__ */ new Map(), - defaultStyle: /* @__PURE__ */ new Map(), - baseStyle: /* @__PURE__ */ new Map(), - computedStyle: /* @__PURE__ */ new WeakMap(), - font: /* @__PURE__ */ new Set(), - snapshot: /* @__PURE__ */ new WeakMap(), - snapshotKey: /* @__PURE__ */ new Map(), - reset: resetCache -}; -function resetCache() { - cache.computedStyle = /* @__PURE__ */ new WeakMap(); -} - -// src/utils/cssTools.js -var commonTags = [ - "div", - "span", - "p", - "a", - "img", - "ul", - "li", - "button", - "input", - "select", - "textarea", - "label", - "section", - "article", - "header", - "footer", - "nav", - "main", - "aside", - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "svg", - "path", - "circle", - "rect", - "line", - "g", - "table", - "thead", - "tbody", - "tr", - "td", - "th" -]; -function precacheCommonTags() { - for (let tag of commonTags) { - getDefaultStyleForTag(tag); - } -} -function getDefaultStyleForTag(tagName) { - if (cache.defaultStyle.has(tagName)) { - return cache.defaultStyle.get(tagName); - } - const skipTags = /* @__PURE__ */ new Set(["script", "style", "meta", "link", "noscript", "template", "defs", "symbol", "title", "metadata", "desc"]); - if (skipTags.has(tagName)) { - const empty = {}; - cache.defaultStyle.set(tagName, empty); - return empty; - } - let sandbox = document.getElementById("snapdom-sandbox"); - if (!sandbox) { - sandbox = document.createElement("div"); - sandbox.id = "snapdom-sandbox"; - sandbox.style.position = "absolute"; - sandbox.style.left = "-9999px"; - sandbox.style.top = "-9999px"; - sandbox.style.width = "0"; - sandbox.style.height = "0"; - sandbox.style.overflow = "hidden"; - document.body.appendChild(sandbox); - } - const el = document.createElement(tagName); - el.style.all = "initial"; - sandbox.appendChild(el); - const styles = getComputedStyle(el); - const defaults = {}; - for (let prop of styles) { - defaults[prop] = styles.getPropertyValue(prop); - } - sandbox.removeChild(el); - cache.defaultStyle.set(tagName, defaults); - return defaults; -} -var IGNORED_PROPS = /* @__PURE__ */ new Set([ - "-webkit-locale" -]); -function getStyleKey(snapshot, tagName, compress = false) { - const entries = []; - const defaultStyles = getDefaultStyleForTag(tagName); - for (let [prop, value] of Object.entries(snapshot)) { - if (IGNORED_PROPS.has(prop)) continue; - if (!compress) { - if (value) { - entries.push(`${prop}:${value}`); - } - } else { - const defaultValue = defaultStyles[prop]; - if (value && value !== defaultValue) { - entries.push(`${prop}:${value}`); - } - } - } - return entries.sort().join(";"); -} -function collectUsedTagNames(root) { - const tagSet = /* @__PURE__ */ new Set(); - if (root.nodeType !== Node.ELEMENT_NODE && root.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) { - return []; - } - if (root.tagName) { - tagSet.add(root.tagName.toLowerCase()); - } - if (typeof root.querySelectorAll === "function") { - root.querySelectorAll("*").forEach((el) => tagSet.add(el.tagName.toLowerCase())); - } - return Array.from(tagSet); -} -function generateDedupedBaseCSS(usedTagNames) { - const groups = /* @__PURE__ */ new Map(); - for (let tagName of usedTagNames) { - const styles = cache.defaultStyle.get(tagName); - if (!styles) continue; - const key = Object.entries(styles).map(([k, v]) => `${k}:${v};`).sort().join(""); - if (!groups.has(key)) { - groups.set(key, []); - } - groups.get(key).push(tagName); - } - let css = ""; - for (let [styleBlock, tagList] of groups.entries()) { - css += `${tagList.join(",")} { ${styleBlock} } -`; - } - return css; -} -function generateCSSClasses(styleMap) { - const keySet = new Set(styleMap.values()); - const classMap = /* @__PURE__ */ new Map(); - let counter = 1; - for (const key of keySet) { - if (!key.trim()) continue; - classMap.set(key, `c${counter++}`); - } - return classMap; -} - -// src/utils/helpers.js -async function inlineSingleBackgroundEntry(entry, options = {}) { - const rawUrl = extractURL(entry); - const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry); - if (rawUrl) { - const encodedUrl = safeEncodeURI(rawUrl); - if (cache.background.has(encodedUrl)) { - return options.skipInline ? void 0 : `url(${cache.background.get(encodedUrl)})`; - } else { - const dataUrl = await fetchImage(encodedUrl, { useProxy: options.useProxy }); - cache.background.set(encodedUrl, dataUrl); - return options.skipInline ? void 0 : `url("${dataUrl}")`; - } - } - if (isGradient || entry === "none") { - return entry; - } - return entry; -} -function idle(fn, { fast = false } = {}) { - if (fast) return fn(); - if ("requestIdleCallback" in window) { - requestIdleCallback(fn, { timeout: 50 }); - } else { - setTimeout(fn, 1); - } -} -function getStyle(el, pseudo = null) { - if (!(el instanceof Element)) { - return window.getComputedStyle(el, pseudo); - } - let map = cache.computedStyle.get(el); - if (!map) { - map = /* @__PURE__ */ new Map(); - cache.computedStyle.set(el, map); - } - if (!map.has(pseudo)) { - const st = window.getComputedStyle(el, pseudo); - map.set(pseudo, st); - } - return map.get(pseudo); -} -function parseContent(content) { - let clean = content.replace(/^['"]|['"]$/g, ""); - if (clean.startsWith("\\")) { - try { - return String.fromCharCode(parseInt(clean.replace("\\", ""), 16)); - } catch { - return clean; - } - } - return clean; -} -function extractURL(value) { - const match = value.match(/url\((['"]?)(.*?)(\1)\)/); - if (!match) return null; - const url = match[2].trim(); - if (url.startsWith("#")) return null; - return url; -} -async function fetchResource(url, { useProxy = "" } = {}) { - async function doFetch(u) { - const res = await fetch(u); - if (!res.ok) throw new Error(`[snapdom] Failed to fetch resource: ${u}`); - return res; - } - try { - return await doFetch(url); - } catch (e) { - if (useProxy && typeof useProxy === "string") { - const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url); - return doFetch(proxied); - } - throw e; - } -} -var _inflight = /* @__PURE__ */ new Map(); -var _errorCache = /* @__PURE__ */ new Map(); -function fetchImage(src, { timeout = 3e3, useProxy = "", errorTTL = 8e3 } = {}) { - function getCrossOriginMode(url) { - try { - const parsed = new URL(url, window.location.href); - return parsed.origin === window.location.origin ? "use-credentials" : "anonymous"; - } catch { - return "anonymous"; - } - } - const ok = (data) => ({ ok: true, data }); - const fail = (e) => ({ ok: false, error: e instanceof Error ? e : new Error(String(e)) }); - function fetchBlobAsDataURLSafe(fetchUrl) { - try { - return fetch(fetchUrl, { - mode: "cors", - credentials: getCrossOriginMode(fetchUrl) === "use-credentials" ? "include" : "omit" - }).then((r) => { - if (!r.ok) return fail(new Error("HTTP " + r.status)); - return r.blob().then((blob) => new Promise((resolve) => { - const reader = new FileReader(); - reader.onloadend = () => { - const base64 = reader.result; - if (typeof base64 !== "string" || !base64.startsWith("data:image/")) { - resolve(fail(new Error("Invalid image data URL"))); - } else { - resolve(ok(base64)); - } - }; - reader.onerror = () => resolve(fail(new Error("FileReader error"))); - reader.readAsDataURL(blob); - })); - }).catch((e) => fail(e)); - } catch (e) { - return Promise.resolve(fail(e)); - } - } - function fetchWithFallbackOnceSafe(url) { - return fetchBlobAsDataURLSafe(url).then((r) => { - if (r.ok) return r; - if (useProxy && typeof useProxy === "string") { - const proxied = useProxy.replace(/\/$/, "") + safeEncodeURI(url); - return fetchBlobAsDataURLSafe(proxied).then((r2) => { - if (r2.ok) return r2; - return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")); - }); - } - return fail(new Error("[SnapDOM - fetchImage] Fetch failed and no proxy provided")); - }); - } - const now = Date.now(); - const until = _errorCache.get(src); - if (until && until > now) { - const pr = Promise.reject(new Error("[SnapDOM - fetchImage] Recently failed (cooldown).")); - pr.catch(() => { - }); - return pr; - } - if (_inflight.has(src)) return _inflight.get(src); - const crossOriginValue = getCrossOriginMode(src); - if (cache.image.has(src)) return Promise.resolve(cache.image.get(src)); - if (src.startsWith("data:image/")) { - cache.image.set(src, src); - return Promise.resolve(src); - } - if (/\.svg(\?.*)?$/i.test(src)) { - const p2 = (async () => { - const direct = await (async () => { - try { - const res = await fetch(src, { - mode: "cors", - credentials: crossOriginValue === "use-credentials" ? "include" : "omit" - }); - if (!res.ok) return fail(new Error("HTTP " + res.status)); - const svgText = await res.text(); - return ok(`data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`); - } catch (e) { - return fail(e); - } - })(); - if (direct.ok) { - cache.image.set(src, direct.data); - return direct.data; - } - const via = await fetchWithFallbackOnceSafe(src); - if (via.ok) { - cache.image.set(src, via.data); - return via.data; - } - _errorCache.set(src, now + errorTTL); - return Promise.reject(via.error); - })(); - _inflight.set(src, p2); - p2.finally(() => _inflight.delete(src)); - p2.catch(() => { - }); - return p2; - } - const p = new Promise((resolve, reject) => { - let finished = false; - const img = new Image(); - const finish = (fn) => (arg) => { - if (finished) return; - finished = true; - clearTimeout(timeoutId); - img.onload = img.onerror = null; - fn(arg); - }; - const onSuccess = (d) => { - cache.image.set(src, d); - resolve(d); - }; - const onFinalError = (e) => { - _errorCache.set(src, Date.now() + errorTTL); - reject(e); - }; - const timeoutId = setTimeout( - finish(() => { - fetchWithFallbackOnceSafe(src).then((r) => { - if (r.ok) onSuccess(r.data); - else onFinalError(new Error("Image load timed out")); - }); - }), - timeout - ); - img.crossOrigin = crossOriginValue; - img.onload = finish(() => { - Promise.resolve(img.decode()).then(() => { - try { - const canvas = document.createElement("canvas"); - canvas.width = img.naturalWidth || img.width; - canvas.height = img.naturalHeight || img.height; - const ctx = canvas.getContext("2d"); - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - onSuccess(canvas.toDataURL("image/png")); - } catch { - fetchWithFallbackOnceSafe(src).then((r) => { - if (r.ok) onSuccess(r.data); - else onFinalError(r.error); - }); - } - }).catch(() => { - fetchWithFallbackOnceSafe(src).then((r) => { - if (r.ok) onSuccess(r.data); - else onFinalError(r.error); - }); - }); - }); - img.onerror = finish(() => { - fetchWithFallbackOnceSafe(src).then((r) => { - if (r.ok) onSuccess(r.data); - else onFinalError(r.error); - }); - }); - img.src = src; - }); - _inflight.set(src, p); - p.finally(() => _inflight.delete(src)); - p.catch(() => { - }); - return p; -} -function snapshotComputedStyle(style) { - const snap = {}; - for (let prop of style) { - snap[prop] = style.getPropertyValue(prop); - } - return snap; -} -function isSafari() { - return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); -} -function stripTranslate(transform) { - if (!transform || transform === "none") return ""; - let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, ""); - cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => { - const parts = values.split(",").map((s) => s.trim()); - if (parts.length !== 6) return `matrix(${values})`; - parts[4] = "0"; - parts[5] = "0"; - return `matrix(${parts.join(", ")})`; - }); - cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => { - const parts = values.split(",").map((s) => s.trim()); - if (parts.length !== 16) return `matrix3d(${values})`; - parts[12] = "0"; - parts[13] = "0"; - return `matrix3d(${parts.join(", ")})`; - }); - return cleaned.trim().replace(/\s{2,}/g, " "); -} -function safeEncodeURI(uri) { - if (/%[0-9A-Fa-f]{2}/.test(uri)) return uri; - try { - return encodeURI(uri); - } catch { - return uri; - } -} -function splitBackgroundImage(bg) { - const parts = []; - let depth = 0; - let lastIndex = 0; - for (let i = 0; i < bg.length; i++) { - const char = bg[i]; - if (char === "(") depth++; - if (char === ")") depth--; - if (char === "," && depth === 0) { - parts.push(bg.slice(lastIndex, i).trim()); - lastIndex = i + 1; - } - } - parts.push(bg.slice(lastIndex).trim()); - return parts; -} - -// src/modules/styles.js -var snapshotCache = /* @__PURE__ */ new WeakMap(); -var snapshotKeyCache = /* @__PURE__ */ new Map(); -function snapshotComputedStyleFull(style) { - const result = {}; - const computedVisibility = style.getPropertyValue("visibility"); - for (let i = 0; i < style.length; i++) { - const prop = style[i]; - let val = style.getPropertyValue(prop); - if ((prop === "background-image" || prop === "content") && val.includes("url(") && !val.includes("data:")) { - val = "none"; - } - result[prop] = val; - } - if (computedVisibility === "hidden") { - result.opacity = "0"; - } - return result; -} -function inlineAllStyles(source, clone, styleMap, cache2, compress) { - if (source.tagName === "STYLE") return; - if (!cache2.has(source)) { - cache2.set(source, getStyle(source)); - } - const style = cache2.get(source); - if (!snapshotCache.has(source)) { - const snapshot2 = snapshotComputedStyleFull(style); - snapshotCache.set(source, snapshot2); - } - const snapshot = snapshotCache.get(source); - const hash = Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)).map(([prop, val]) => `${prop}:${val}`).join(";"); - if (snapshotKeyCache.has(hash)) { - styleMap.set(clone, snapshotKeyCache.get(hash)); - return; - } - const tagName = source.tagName?.toLowerCase() || "div"; - const key = getStyleKey(snapshot, tagName, compress); - snapshotKeyCache.set(hash, key); - styleMap.set(clone, key); -} - -// src/core/clone.js -function freezeImgSrcset(original, cloned) { - try { - const chosen = original.currentSrc || original.src || ""; - if (!chosen) return; - cloned.setAttribute("src", chosen); - cloned.removeAttribute("srcset"); - cloned.removeAttribute("sizes"); - cloned.loading = "eager"; - cloned.decoding = "sync"; - } catch { - } -} -function deepClone(node, styleMap, styleCache, nodeMap, compress, options = {}, originalRoot) { - if (!node) throw new Error("Invalid node"); - const clonedAssignedNodes = /* @__PURE__ */ new Set(); - let pendingSelectValue = null; - if (node.nodeType === Node.TEXT_NODE) { - return node.cloneNode(true); - } - if (node.nodeType !== Node.ELEMENT_NODE) { - return node.cloneNode(true); - } - if (node.getAttribute("data-capture") === "exclude") { - const spacer = document.createElement("div"); - const rect = node.getBoundingClientRect(); - spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; - return spacer; - } - if (options.exclude && Array.isArray(options.exclude)) { - for (const selector of options.exclude) { - try { - if (node.matches?.(selector)) { - const spacer = document.createElement("div"); - const rect = node.getBoundingClientRect(); - spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; - return spacer; - } - } catch (err) { - console.warn(`Invalid selector in exclude option: ${selector}`, err); - } - } - } - if (typeof options.filter === "function") { - try { - if (!options.filter(node, originalRoot || node)) { - const spacer = document.createElement("div"); - const rect = node.getBoundingClientRect(); - spacer.style.cssText = `display:inline-block;width:${rect.width}px;height:${rect.height}px;visibility:hidden;`; - return spacer; - } - } catch (err) { - console.warn("Error in filter function:", err); - } - } - if (node.tagName === "IFRAME") { - const fallback = document.createElement("div"); - fallback.style.cssText = `width:${node.offsetWidth}px;height:${node.offsetHeight}px;background-image:repeating-linear-gradient(45deg,#ddd,#ddd 5px,#f9f9f9 5px,#f9f9f9 10px);display:flex;align-items:center;justify-content:center;font-size:12px;color:#555;border:1px solid #aaa;`; - return fallback; - } - if (node.getAttribute("data-capture") === "placeholder") { - const clone2 = node.cloneNode(false); - nodeMap.set(clone2, node); - inlineAllStyles(node, clone2, styleMap, styleCache, compress); - const placeholder = document.createElement("div"); - placeholder.textContent = node.getAttribute("data-placeholder-text") || ""; - placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`; - clone2.appendChild(placeholder); - return clone2; - } - if (node.tagName === "CANVAS") { - const dataURL = node.toDataURL(); - const img = document.createElement("img"); - img.src = dataURL; - img.width = node.width; - img.height = node.height; - nodeMap.set(img, node); - inlineAllStyles(node, img, styleMap, styleCache, compress); - return img; - } - let clone; - try { - clone = node.cloneNode(false); - nodeMap.set(clone, node); - if (node.tagName === "IMG") { - freezeImgSrcset(node, clone); - } - } catch (err) { - console.error("[Snapdom] Failed to clone node:", node, err); - throw err; - } - if (node instanceof HTMLTextAreaElement) { - clone.textContent = node.value; - clone.value = node.value; - const rect = node.getBoundingClientRect(); - clone.style.width = `${rect.width}px`; - clone.style.height = `${rect.height}px`; - return clone; - } - if (node instanceof HTMLInputElement) { - clone.value = node.value; - clone.setAttribute("value", node.value); - if (node.checked !== void 0) { - clone.checked = node.checked; - if (node.checked) clone.setAttribute("checked", ""); - if (node.indeterminate) clone.indeterminate = node.indeterminate; - } - } - if (node instanceof HTMLSelectElement) { - pendingSelectValue = node.value; - } - inlineAllStyles(node, clone, styleMap, styleCache, compress); - if (node.shadowRoot) { - const hasSlot = Array.from(node.shadowRoot.querySelectorAll("slot")).length > 0; - if (hasSlot) { - for (const child of node.shadowRoot.childNodes) { - if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") { - const cssText = child.textContent || ""; - if (cssText.trim() && compress) { - styleCache.set(child, cssText); - } - } - } - } else { - const shadowFrag = document.createDocumentFragment(); - for (const child of node.shadowRoot.childNodes) { - if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "STYLE") { - const cssText = child.textContent || ""; - if (cssText.trim() && compress) { - styleCache.set(child, cssText); - } - continue; - } - const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); - if (clonedChild) shadowFrag.appendChild(clonedChild); - } - clone.appendChild(shadowFrag); - } - } - if (node.tagName === "SLOT") { - const assigned = node.assignedNodes?.({ flatten: true }) || []; - const nodesToClone = assigned.length > 0 ? assigned : Array.from(node.childNodes); - const fragment = document.createDocumentFragment(); - for (const child of nodesToClone) { - const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); - if (clonedChild) fragment.appendChild(clonedChild); - } - return fragment; - } - for (const child of node.childNodes) { - if (clonedAssignedNodes.has(child)) continue; - const clonedChild = deepClone(child, styleMap, styleCache, nodeMap, compress, options, originalRoot || node); - if (clonedChild) clone.appendChild(clonedChild); - } - if (pendingSelectValue !== null && clone instanceof HTMLSelectElement) { - clone.value = pendingSelectValue; - for (const opt of clone.options) { - if (opt.value === pendingSelectValue) { - opt.setAttribute("selected", ""); - } else { - opt.removeAttribute("selected"); - } - } - } - return clone; -} - -// src/modules/iconFonts.js -var defaultIconFonts = [ - // /uicons/i, - /font\s*awesome/i, - /material\s*icons/i, - /ionicons/i, - /glyphicons/i, - /feather/i, - /bootstrap\s*icons/i, - /remix\s*icons/i, - /heroicons/i, - /layui/i, - /lucide/i -]; -var userIconFonts = []; -function extendIconFonts(fonts) { - const list = Array.isArray(fonts) ? fonts : [fonts]; - for (const f of list) { - if (f instanceof RegExp) { - userIconFonts.push(f); - } else if (typeof f === "string") { - userIconFonts.push(new RegExp(f, "i")); - } else { - console.warn("[snapdom] Ignored invalid iconFont value:", f); - } - } -} -function isIconFont(input) { - const text = typeof input === "string" ? input : ""; - const candidates = [...defaultIconFonts, ...userIconFonts]; - for (const rx of candidates) { - if (rx instanceof RegExp && rx.test(text)) return true; - } - if (/icon/i.test(text) || /glyph/i.test(text) || /symbols/i.test(text) || /feather/i.test(text) || /fontawesome/i.test(text)) return true; - return false; -} - -// src/modules/fonts.js -async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") { - fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, ""); - const dpr = window.devicePixelRatio || 1; - await document.fonts.ready; - const span = document.createElement("span"); - span.textContent = unicodeChar; - span.style.position = "absolute"; - span.style.visibility = "hidden"; - span.style.fontFamily = `"${fontFamily}"`; - span.style.fontWeight = fontWeight || "normal"; - span.style.fontSize = `${fontSize}px`; - span.style.lineHeight = "1"; - span.style.whiteSpace = "nowrap"; - span.style.padding = "0"; - span.style.margin = "0"; - document.body.appendChild(span); - const rect = span.getBoundingClientRect(); - const width = Math.ceil(rect.width); - const height = Math.ceil(rect.height); - document.body.removeChild(span); - const canvas = document.createElement("canvas"); - canvas.width = width * dpr; - canvas.height = height * dpr; - const ctx = canvas.getContext("2d"); - ctx.scale(dpr, dpr); - ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`; - ctx.textAlign = "left"; - ctx.textBaseline = "top"; - ctx.fillStyle = color; - ctx.fillText(unicodeChar, 0, 0); - return { - dataUrl: canvas.toDataURL(), - width, - height - }; -} -function isStylesheetLoaded(href) { - return Array.from(document.styleSheets).some((sheet) => sheet.href === href); -} -function injectLinkIfMissing(href) { - return new Promise((resolve) => { - if (isStylesheetLoaded(href)) return resolve(null); - const link = document.createElement("link"); - link.rel = "stylesheet"; - link.href = href; - link.setAttribute("data-snapdom", "injected-import"); - link.onload = () => resolve(link); - link.onerror = () => resolve(null); - document.head.appendChild(link); - }); -} -async function embedCustomFonts({ preCached = false, localFonts = [], useProxy = "" } = {}) { - if (cache.resource.has("fonts-embed-css")) { - if (preCached) { - const style = document.createElement("style"); - style.setAttribute("data-snapdom", "embedFonts"); - style.textContent = cache.resource.get("fonts-embed-css"); - document.head.appendChild(style); - } - return cache.resource.get("fonts-embed-css"); - } - const loadedFonts = /* @__PURE__ */ new Set(); - try { - for (const f of document.fonts) { - if (f.status === "loaded") { - loadedFonts.add(`${f.family}__${f.weight || "normal"}__${f.style || "normal"}`); - } - } - } catch { - } - const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g; - const styleImports = []; - for (const styleTag of document.querySelectorAll("style")) { - const cssText = styleTag.textContent || ""; - const matches = Array.from(cssText.matchAll(importRegex)); - for (const match of matches) { - const importUrl = match[1]; - if (isIconFont(importUrl)) continue; - if (!isStylesheetLoaded(importUrl)) { - styleImports.push(importUrl); - } - } - } - await Promise.all(styleImports.map(injectLinkIfMissing)); - const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href); - let finalCSS = ""; - for (const link of links) { - try { - const res = await fetchResource(link.href, { useProxy }); - const cssText = await res.text(); - if (isIconFont(link.href) || isIconFont(cssText)) continue; - const faceRegex = /@font-face[^{}]*{[^}]*}/g; - let cssFinal = cssText; - for (const face of cssText.match(faceRegex) || []) { - const famMatch = face.match(/font-family:\s*([^;]+);/i); - if (!famMatch) continue; - const family = famMatch[1].replace(/['"]/g, "").trim(); - const weightMatch = face.match(/font-weight:\s*([^;]+);/i); - const styleMatch = face.match(/font-style:\s*([^;]+);/i); - const weight = weightMatch ? weightMatch[1].trim() : "normal"; - const style = styleMatch ? styleMatch[1].trim() : "normal"; - const key = `${family}__${weight}__${style}`; - const urlRegex = /url\((["']?)([^"')]+)\1\)/g; - const hasURL = /url\(/i.test(face); - const hasLocal = /local\(/i.test(face); - if (!hasURL && hasLocal) { - continue; - } - if (!loadedFonts.has(key)) { - cssFinal = cssFinal.replace(face, ""); - continue; - } - let inlined = face; - const matches = Array.from(face.matchAll(urlRegex)); - for (const match of matches) { - let rawUrl = extractURL(match[0]); - if (!rawUrl) continue; - let url = rawUrl; - if (!url.startsWith("http") && !url.startsWith("data:")) { - url = new URL(url, link.href).href; - } - if (isIconFont(url)) continue; - if (cache.resource.has(url)) { - cache.font.add(url); - inlined = inlined.replace(match[0], `url(${cache.resource.get(url)})`); - continue; - } - if (cache.font.has(url)) continue; - try { - const fontRes = await fetchResource(url, { useProxy }); - const blob = await fontRes.blob(); - const b64 = await new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.readAsDataURL(blob); - }); - cache.resource.set(url, b64); - cache.font.add(url); - inlined = inlined.replace(match[0], `url(${b64})`); - } catch (e) { - console.warn("[snapdom] Failed to fetch font resource:", url); - } - } - cssFinal = cssFinal.replace(face, inlined); - } - finalCSS += cssFinal + "\n"; - } catch (e) { - console.warn("[snapdom] Failed to fetch CSS:", link.href); - } - } - for (const sheet of document.styleSheets) { - try { - if (!sheet.href || links.every((link) => link.href !== sheet.href)) { - for (const rule of sheet.cssRules) { - if (rule.type === CSSRule.FONT_FACE_RULE) { - const src = rule.style.getPropertyValue("src"); - const family = rule.style.getPropertyValue("font-family"); - if (!src || isIconFont(family)) continue; - const weightVal = rule.style.getPropertyValue("font-weight") || "normal"; - const styleVal = rule.style.getPropertyValue("font-style") || "normal"; - const key = `${family}__${weightVal}__${styleVal}`; - const urlRegex = /url\((["']?)([^"')]+)\1\)/g; - const localRegex = /local\((["']?)[^)]+?\1\)/g; - const hasURL = !!src.match(urlRegex); - const hasLocal = !!src.match(localRegex); - if (!hasURL && hasLocal) { - finalCSS += `@font-face{font-family:${family};src:${src};font-style:${styleVal};font-weight:${weightVal};}`; - continue; - } - if (!loadedFonts.has(key)) continue; - let inlinedSrc = src; - const matches = Array.from(src.matchAll(urlRegex)); - for (const match of matches) { - let rawUrl = match[2].trim(); - if (!rawUrl) continue; - let url = rawUrl; - if (!url.startsWith("http") && !url.startsWith("data:")) { - url = new URL(url, sheet.href || location.href).href; - } - if (isIconFont(url)) continue; - if (cache.resource.has(url)) { - cache.font.add(url); - inlinedSrc = inlinedSrc.replace(match[0], `url(${cache.resource.get(url)})`); - continue; - } - if (cache.font.has(url)) continue; - try { - const res = await fetchResource(url, { useProxy }); - const blob = await res.blob(); - const b64 = await new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.readAsDataURL(blob); - }); - cache.resource.set(url, b64); - cache.font.add(url); - inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`); - } catch (e) { - console.warn("[snapdom] Failed to fetch font URL:", url); - } - } - finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleVal};font-weight:${weightVal};}`; - } - } - } - } catch (e) { - console.warn("[snapdom] Cannot access stylesheet", sheet.href, e); - } - } - for (const font of document.fonts) { - if (font.family && font.status === "loaded" && font._snapdomSrc) { - if (isIconFont(font.family)) continue; - let b64 = font._snapdomSrc; - if (!b64.startsWith("data:")) { - if (cache.resource.has(font._snapdomSrc)) { - b64 = cache.resource.get(font._snapdomSrc); - cache.font.add(font._snapdomSrc); - } else if (!cache.font.has(font._snapdomSrc)) { - try { - const res = await fetchResource(font._snapdomSrc, { useProxy }); - const blob = await res.blob(); - b64 = await new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.readAsDataURL(blob); - }); - cache.resource.set(font._snapdomSrc, b64); - cache.font.add(font._snapdomSrc); - } catch (e) { - console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc); - continue; - } - } - } - finalCSS += `@font-face{font-family:'${font.family}';src:url(${b64});font-style:${font.style || "normal"};font-weight:${font.weight || "normal"};}`; - } - } - for (const font of localFonts) { - if (!font || typeof font !== "object") continue; - const { family, src, weight = "normal", style = "normal" } = font; - if (!family || !src) continue; - let b64 = src; - if (!b64.startsWith("data:")) { - try { - const res = await fetchResource(src, { useProxy }); - const blob = await res.blob(); - b64 = await new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.readAsDataURL(blob); - }); - cache.resource.set(src, b64); - cache.font.add(src); - } catch (e) { - console.warn("[snapdom] Failed to load local font:", src); - continue; - } - } else { - cache.resource.set(src, b64); - cache.font.add(src); - } - finalCSS += `@font-face{font-family:'${family}';src:url(${b64});font-style:${style};font-weight:${weight};}`; - } - if (finalCSS) { - cache.resource.set("fonts-embed-css", finalCSS); - if (preCached) { - const style = document.createElement("style"); - style.setAttribute("data-snapdom", "embedFonts"); - style.textContent = finalCSS; - document.head.appendChild(style); - } - } - return finalCSS; -} - -// src/modules/pseudo.js -async function inlinePseudoElements(source, clone, styleMap, styleCache, options) { - if (!(source instanceof Element) || !(clone instanceof Element)) return; - for (const pseudo of ["::before", "::after", "::first-letter"]) { - try { - const style = getStyle(source, pseudo); - if (!style || typeof style[Symbol.iterator] !== "function") continue; - const isEmptyPseudo = style.content === "none" && style.backgroundImage === "none" && style.backgroundColor === "transparent" && (style.borderStyle === "none" || parseFloat(style.borderWidth) === 0) && (!style.transform || style.transform === "none") && style.display === "inline"; - if (isEmptyPseudo) continue; - if (pseudo === "::first-letter") { - const normal = getComputedStyle(source); - const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight; - if (!isMeaningful) continue; - const textNode = Array.from(clone.childNodes).find( - (n) => n.nodeType === Node.TEXT_NODE && n.textContent?.trim().length > 0 - ); - if (!textNode) continue; - const text = textNode.textContent; - const match = text.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u); - const first = match?.[0]; - const rest = text.slice(first?.length || 0); - if (!first || /[\uD800-\uDFFF]/.test(first)) continue; - const span = document.createElement("span"); - span.textContent = first; - span.dataset.snapdomPseudo = "::first-letter"; - const snapshot2 = snapshotComputedStyle(style); - const key2 = getStyleKey(snapshot2, "span", options); - styleMap.set(span, key2); - const restNode = document.createTextNode(rest); - clone.replaceChild(restNode, textNode); - clone.insertBefore(span, restNode); - continue; - } - const content = style.content; - const cleanContent = /counter\s*\(|counters\s*\(/.test(content) ? "- " : parseContent(content); - const bg = style.backgroundImage; - const bgColor = style.backgroundColor; - const fontFamily = style.fontFamily; - const fontSize = parseInt(style.fontSize) || 32; - const fontWeight = parseInt(style.fontWeight) || false; - const color = style.color || "#000"; - const display = style.display; - const width = parseFloat(style.width); - const height = parseFloat(style.height); - const borderStyle = style.borderStyle; - const borderWidth = parseFloat(style.borderWidth); - const transform = style.transform; - const isIconFont2 = isIconFont(fontFamily); - const hasExplicitContent = content !== "none" && cleanContent !== ""; - const hasBg = bg && bg !== "none"; - const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)"; - const hasBox = display !== "inline" && (width > 0 || height > 0); - const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0; - const hasTransform = transform && transform !== "none"; - const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform; - if (!shouldRender) continue; - const pseudoEl = document.createElement("span"); - pseudoEl.dataset.snapdomPseudo = pseudo; - pseudoEl.style.verticalAlign = "middle"; - const snapshot = snapshotComputedStyle(style); - const key = getStyleKey(snapshot, "span", options); - styleMap.set(pseudoEl, key); - if (isIconFont2 && cleanContent.length === 1) { - const { dataUrl, width: width2, height: height2 } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color); - const imgEl = document.createElement("img"); - imgEl.src = dataUrl; - imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`; - pseudoEl.appendChild(imgEl); - clone.dataset.snapdomHasIcon = "true"; - } else if (cleanContent.startsWith("url(")) { - const rawUrl = extractURL(cleanContent); - if (rawUrl?.trim()) { - try { - const imgEl = document.createElement("img"); - const dataUrl = await fetchImage(safeEncodeURI(rawUrl), options); - imgEl.src = dataUrl; - imgEl.style = `width:${fontSize}px;height:auto;object-fit:contain;`; - pseudoEl.appendChild(imgEl); - } catch (e) { - console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e); - } - } - } else if (!isIconFont2 && hasExplicitContent) { - pseudoEl.textContent = cleanContent; - } - if (hasBg) { - try { - const bgSplits = splitBackgroundImage(bg); - const newBgParts = await Promise.all(bgSplits.map(inlineSingleBackgroundEntry)); - pseudoEl.style.backgroundImage = newBgParts.join(", "); - } catch (e) { - console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e); - } - } - if (hasBgColor) pseudoEl.style.backgroundColor = bgColor; - const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== ""; - const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform; - if (!hasVisibleBox) continue; - if (pseudo === "::before") { - clone.insertBefore(pseudoEl, clone.firstChild); - } else { - clone.appendChild(pseudoEl); - } - } catch (e) { - console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e); - } - } - const sChildren = Array.from(source.children); - const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo); - for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { - await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, options); - } -} - -// src/modules/svgDefs.js -function inlineExternalDefsAndSymbols(rootElement) { - if (!rootElement) return; - const usedIds = /* @__PURE__ */ new Set(); - rootElement.querySelectorAll("use").forEach((use) => { - const href = use.getAttribute("xlink:href") || use.getAttribute("href"); - if (href && href.startsWith("#")) { - usedIds.add(href.slice(1)); - } - }); - if (!usedIds.size) return; - const allGlobal = Array.from(document.querySelectorAll("svg > symbol, svg > defs")); - const globalSymbols = allGlobal.filter((el) => el.tagName.toLowerCase() === "symbol"); - const globalDefs = allGlobal.filter((el) => el.tagName.toLowerCase() === "defs"); - let container = rootElement.querySelector("svg.inline-defs-container"); - if (!container) { - container = document.createElementNS("http://www.w3.org/2000/svg", "svg"); - container.setAttribute("aria-hidden", "true"); - container.setAttribute("style", "position: absolute; width: 0; height: 0; overflow: hidden;"); - container.classList.add("inline-defs-container"); - rootElement.insertBefore(container, rootElement.firstChild); - } - const existingIds = /* @__PURE__ */ new Set(); - rootElement.querySelectorAll("symbol[id], defs > *[id]").forEach((el) => { - existingIds.add(el.id); - }); - usedIds.forEach((id) => { - if (existingIds.has(id)) return; - const symbol = globalSymbols.find((sym) => sym.id === id); - if (symbol) { - container.appendChild(symbol.cloneNode(true)); - existingIds.add(id); - return; - } - for (const defs of globalDefs) { - const defEl = defs.querySelector(`#${CSS.escape(id)}`); - if (defEl) { - let defsContainer = container.querySelector("defs"); - if (!defsContainer) { - defsContainer = document.createElementNS("http://www.w3.org/2000/svg", "defs"); - container.appendChild(defsContainer); - } - defsContainer.appendChild(defEl.cloneNode(true)); - existingIds.add(id); - break; - } - } - }); -} - -// src/core/prepare.js -async function prepareClone(element, compress = false, embedFonts = false, options = {}) { - const styleMap = /* @__PURE__ */ new Map(); - const styleCache = /* @__PURE__ */ new WeakMap(); - const nodeMap = /* @__PURE__ */ new Map(); - let clone; - let classCSS = ""; - stabilizeLayout(element); - try { - inlineExternalDefsAndSymbols(element); - } catch (e) { - console.warn("inlineExternal defs or symbol failed:", e); - } - try { - clone = deepClone(element, styleMap, styleCache, nodeMap, compress, options, element); - } catch (e) { - console.warn("deepClone failed:", e); - throw e; - } - try { - await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts, options.useProxy); - } catch (e) { - console.warn("inlinePseudoElements failed:", e); - } - await resolveBlobUrlsInTree(clone); - if (compress) { - const keyToClass = generateCSSClasses(styleMap); - classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join(""); - for (const [node, key] of styleMap.entries()) { - if (node.tagName === "STYLE") continue; - if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) { - node.setAttribute("style", key.replace(/;/g, "; ")); - continue; - } - const className = keyToClass.get(key); - if (className) node.classList.add(className); - const bgImage = node.style?.backgroundImage; - const hasIcon = node.dataset?.snapdomHasIcon; - if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage; - if (hasIcon) { - node.style.verticalAlign = "middle"; - node.style.display = "inline"; - } - } - } else { - for (const [node, key] of styleMap.entries()) { - if (node.tagName === "STYLE") continue; - node.setAttribute("style", key.replace(/;/g, "; ")); - } - } - for (const [cloneNode, originalNode] of nodeMap.entries()) { - const scrollX = originalNode.scrollLeft; - const scrollY = originalNode.scrollTop; - const hasScroll = scrollX || scrollY; - if (hasScroll && cloneNode instanceof HTMLElement) { - cloneNode.style.overflow = "hidden"; - cloneNode.style.scrollbarWidth = "none"; - cloneNode.style.msOverflowStyle = "none"; - const inner = document.createElement("div"); - inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`; - inner.style.willChange = "transform"; - inner.style.display = "inline-block"; - inner.style.width = "100%"; - while (cloneNode.firstChild) { - inner.appendChild(cloneNode.firstChild); - } - cloneNode.appendChild(inner); - } - } - if (element === nodeMap.get(clone)) { - const computed = styleCache.get(element) || window.getComputedStyle(element); - styleCache.set(element, computed); - const transform = stripTranslate(computed.transform); - clone.style.margin = "0"; - clone.style.position = "static"; - clone.style.top = "auto"; - clone.style.left = "auto"; - clone.style.right = "auto"; - clone.style.bottom = "auto"; - clone.style.zIndex = "auto"; - clone.style.float = "none"; - clone.style.clear = "none"; - clone.style.transform = transform || ""; - } - for (const [cloneNode, originalNode] of nodeMap.entries()) { - if (originalNode.tagName === "PRE") { - cloneNode.style.marginTop = "0"; - cloneNode.style.marginBlockStart = "0"; - } - } - return { clone, classCSS, styleCache }; -} -function stabilizeLayout(element) { - const style = getComputedStyle(element); - const outlineStyle = style.outlineStyle; - const outlineWidth = style.outlineWidth; - const borderStyle = style.borderStyle; - const borderWidth = style.borderWidth; - const outlineVisible = outlineStyle !== "none" && parseFloat(outlineWidth) > 0; - const borderAbsent = borderStyle === "none" || parseFloat(borderWidth) === 0; - if (outlineVisible && borderAbsent) { - element.style.border = `${outlineWidth} solid transparent`; - } -} -var _blobToDataUrlCache = /* @__PURE__ */ new Map(); -async function blobUrlToDataUrl(blobUrl) { - if (_blobToDataUrlCache.has(blobUrl)) return _blobToDataUrlCache.get(blobUrl); - const res = await fetch(blobUrl); - if (!res.ok) throw new Error(`[SnapDOM] HTTP ${res.status} on blob fetch (${blobUrl})`); - const blob = await res.blob(); - const dataUrl = await new Promise((resolve, reject) => { - const fr = new FileReader(); - fr.onloadend = () => { - const v = fr.result; - if (typeof v === "string" && v.startsWith("data:")) resolve(v); - else reject(new Error("[SnapDOM] Invalid data URL from blob")); - }; - fr.onerror = () => reject(new Error("[SnapDOM] FileReader error")); - fr.readAsDataURL(blob); - }); - _blobToDataUrlCache.set(blobUrl, dataUrl); - return dataUrl; -} -var BLOB_URL_RE = /\bblob:[^)"'\s]+/g; -async function replaceBlobUrlsInCssText(cssText) { - if (!cssText || cssText.indexOf("blob:") === -1) return cssText; - const uniques = Array.from(new Set(cssText.match(BLOB_URL_RE) || [])); - if (uniques.length === 0) return cssText; - let out = cssText; - for (const u of uniques) { - try { - const d = await blobUrlToDataUrl(u); - out = out.split(u).join(d); - } catch { - } - } - return out; -} -function isBlobUrl(u) { - return typeof u === "string" && u.startsWith("blob:"); -} -function parseSrcset(srcset) { - return (srcset || "").split(",").map((s) => s.trim()).filter(Boolean).map((item) => { - const m = item.match(/^(\S+)(\s+.+)?$/); - return m ? { url: m[1], desc: m[2] || "" } : null; - }).filter(Boolean); -} -function stringifySrcset(parts) { - return parts.map((p) => p.desc ? `${p.url} ${p.desc.trim()}` : p.url).join(", "); -} -async function resolveBlobUrlsInTree(root) { - if (!root) return; - const imgs = root.querySelectorAll ? root.querySelectorAll("img") : []; - for (const img of imgs) { - try { - const srcAttr = img.getAttribute("src"); - const effective = srcAttr || img.currentSrc || ""; - if (isBlobUrl(effective)) { - const data = await blobUrlToDataUrl(effective); - img.setAttribute("src", data); - } - const srcset = img.getAttribute("srcset"); - if (srcset && srcset.includes("blob:")) { - const parts = parseSrcset(srcset); - let changed = false; - for (const p of parts) { - if (isBlobUrl(p.url)) { - try { - p.url = await blobUrlToDataUrl(p.url); - changed = true; - } catch { - } - } - } - if (changed) img.setAttribute("srcset", stringifySrcset(parts)); - } - } catch { - } - } - const svgImages = root.querySelectorAll ? root.querySelectorAll("image") : []; - for (const node of svgImages) { - try { - const XLINK_NS = "http://www.w3.org/1999/xlink"; - const href = node.getAttribute("href") || node.getAttributeNS?.(XLINK_NS, "href"); - if (isBlobUrl(href)) { - const d = await blobUrlToDataUrl(href); - node.setAttribute("href", d); - node.removeAttributeNS?.(XLINK_NS, "href"); - } - } catch { - } - } - const styled = root.querySelectorAll ? root.querySelectorAll("[style*='blob:']") : []; - for (const el of styled) { - try { - const styleText = el.getAttribute("style"); - if (styleText && styleText.includes("blob:")) { - const replaced = await replaceBlobUrlsInCssText(styleText); - el.setAttribute("style", replaced); - } - } catch { - } - } - const styleTags = root.querySelectorAll ? root.querySelectorAll("style") : []; - for (const s of styleTags) { - try { - const css = s.textContent || ""; - if (css.includes("blob:")) { - s.textContent = await replaceBlobUrlsInCssText(css); - } - } catch { - } - } - const urlAttrs = ["poster"]; - for (const attr of urlAttrs) { - const nodes = root.querySelectorAll ? root.querySelectorAll(`[${attr}^='blob:']`) : []; - for (const n of nodes) { - try { - const u = n.getAttribute(attr); - if (isBlobUrl(u)) { - n.setAttribute(attr, await blobUrlToDataUrl(u)); - } - } catch { - } - } - } -} - -// src/modules/images.js -async function inlineImages(clone, options = {}) { - const imgs = Array.from(clone.querySelectorAll("img")); - const processImg = async (img) => { - if (!img.getAttribute("src")) { - const eff = img.currentSrc || img.src || ""; - if (eff) img.setAttribute("src", eff); - } - img.removeAttribute("srcset"); - img.removeAttribute("sizes"); - const src = img.src; - try { - const dataUrl = await fetchImage(src, { useProxy: options.useProxy }); - img.src = dataUrl; - if (!img.width) img.width = img.naturalWidth || 100; - if (!img.height) img.height = img.naturalHeight || 100; - } catch { - const fallback = document.createElement("div"); - fallback.style = `width: ${img.width || 100}px; height: ${img.height || 100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${img.height || 100}px; color: #666; font-size: 12px;`; - fallback.innerText = "img"; - img.replaceWith(fallback); - } - }; - for (let i = 0; i < imgs.length; i += 4) { - const group = imgs.slice(i, i + 4).map(processImg); - await Promise.allSettled(group); - } -} - -// src/modules/background.js -async function inlineBackgroundImages(source, clone, styleCache, options = {}) { - const queue = [[source, clone]]; - const imageProps = [ - "background-image", - "mask", - "mask-image", - "-webkit-mask-image", - "mask-source", - "mask-box-image-source", - "mask-border-source", - "-webkit-mask-box-image-source", - "border-image", - "border-image-source", - "border-image-slice", - "border-image-width", - "border-image-outset", - "border-image-repeat" - ]; - while (queue.length) { - const [srcNode, cloneNode] = queue.shift(); - const style = styleCache.get(srcNode) || getStyle(srcNode); - if (!styleCache.has(srcNode)) styleCache.set(srcNode, style); - const hasBorderImage = (() => { - const bi = style.getPropertyValue("border-image"); - const bis = style.getPropertyValue("border-image-source"); - return bi && bi !== "none" || bis && bis !== "none"; - })(); - for (const prop of imageProps) { - if (["border-image-slice", "border-image-width", "border-image-outset", "border-image-repeat"].includes(prop) && !hasBorderImage) { - continue; - } - const val = style.getPropertyValue(prop); - if (!val || val === "none") continue; - const splits = splitBackgroundImage(val); - const inlined = await Promise.all( - splits.map((entry) => inlineSingleBackgroundEntry(entry, options)) - ); - if (inlined.some((p) => p && p !== "none" && !/^url\(undefined/.test(p))) { - cloneNode.style.setProperty(prop, inlined.join(", ")); - } - } - const bgColor = style.getPropertyValue("background-color"); - if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") { - cloneNode.style.backgroundColor = bgColor; - } - const sChildren = Array.from(srcNode.children); - const cChildren = Array.from(cloneNode.children); - for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) { - queue.push([sChildren[i], cChildren[i]]); - } - } -} - -// src/core/capture.js -async function captureDOM(element, options = {}) { - if (!element) throw new Error("Element cannot be null or undefined"); - cache.reset(); - const { compress = true, embedFonts = false, fast = true, scale = 1, useProxy = "", localFonts = [] } = options; - let clone, classCSS, styleCache; - let fontsCSS = ""; - let baseCSS = ""; - let dataURL; - let svgString; - ({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts, options)); - await new Promise((resolve) => { - idle(async () => { - await inlineImages(clone, options); - resolve(); - }, { fast }); - }); - await new Promise((resolve) => { - idle(async () => { - await inlineBackgroundImages(element, clone, styleCache, options); - resolve(); - }, { fast }); - }); - if (embedFonts) { - await new Promise((resolve) => { - idle(async () => { - fontsCSS = await embedCustomFonts({ localFonts, useProxy }); - resolve(); - }, { fast }); - }); - } - if (compress) { - const usedTags = collectUsedTagNames(clone).sort(); - const tagKey = usedTags.join(","); - if (cache.baseStyle.has(tagKey)) { - baseCSS = cache.baseStyle.get(tagKey); - } else { - await new Promise((resolve) => { - idle(() => { - baseCSS = generateDedupedBaseCSS(usedTags); - cache.baseStyle.set(tagKey, baseCSS); - resolve(); - }, { fast }); - }); - } - } - await new Promise((resolve) => { - idle(() => { - const rect = element.getBoundingClientRect(); - let w = rect.width; - let h = rect.height; - const hasW = Number.isFinite(options.width); - const hasH = Number.isFinite(options.height); - const hasScale = typeof scale === "number" && scale !== 1; - if (!hasScale) { - const aspect = rect.width / rect.height; - if (hasW && hasH) { - w = options.width; - h = options.height; - } else if (hasW) { - w = options.width; - h = w / aspect; - } else if (hasH) { - h = options.height; - w = h * aspect; - } - } - w = Math.ceil(w); - h = Math.ceil(h); - clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); - clone.style.transformOrigin = "top left"; - if (!hasScale && (hasW || hasH)) { - const originalW = rect.width; - const originalH = rect.height; - const scaleX = w / originalW; - const scaleY = h / originalH; - const existingTransform = clone.style.transform || ""; - const scaleTransform = `scale(${scaleX}, ${scaleY})`; - clone.style.transform = `${scaleTransform} ${existingTransform}`.trim(); - } - const svgNS = "http://www.w3.org/2000/svg"; - const fo = document.createElementNS(svgNS, "foreignObject"); - fo.setAttribute("width", "100%"); - fo.setAttribute("height", "100%"); - const styleTag = document.createElement("style"); - styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS; - fo.appendChild(styleTag); - fo.appendChild(clone); - const serializer = new XMLSerializer(); - const foString = serializer.serializeToString(fo); - const svgHeader = ``; - const svgFooter = ""; - svgString = svgHeader + foString + svgFooter; - dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`; - resolve(); - }, { fast }); - }); - const sandbox = document.getElementById("snapdom-sandbox"); - if (sandbox && sandbox.style.position === "absolute") sandbox.remove(); - return dataURL; -} - -// src/api/snapdom.js -async function toImg(url, { scale = 1 } = {}) { - const img = new Image(); - img.src = url; - await img.decode(); - if (scale !== 1) { - img.style.width = `${img.naturalWidth * scale}px`; - img.style.height = `${img.naturalHeight * scale}px`; - } - return img; -} -async function toCanvas(url, { dpr = 1, scale = 1 } = {}) { - const img = new Image(); - img.src = url; - img.crossOrigin = "anonymous"; - img.loading = "eager"; - img.decoding = "sync"; - const isSafariBrowser = isSafari(); - let appended = false; - if (isSafariBrowser) { - document.body.appendChild(img); - appended = true; - } - await img.decode(); - if (isSafariBrowser) { - await new Promise((resolve) => setTimeout(resolve, 100)); - } - if (img.width === 0 || img.height === 0) { - if (appended) img.remove(); - throw new Error("Image failed to load or has no dimensions"); - } - const width = img.naturalWidth * scale; - const height = img.naturalHeight * scale; - const canvas = document.createElement("canvas"); - canvas.width = Math.ceil(width * dpr); - canvas.height = Math.ceil(height * dpr); - canvas.style.width = `${width}px`; - canvas.style.height = `${height}px`; - const ctx = canvas.getContext("2d"); - ctx.scale(dpr, dpr); - ctx.drawImage(img, 0, 0, width, height); - if (appended) img.remove(); - return canvas; -} -async function toBlob(url, { - type = "svg", - scale = 1, - backgroundColor = "#fff", - quality -} = {}) { - const mime = { - jpg: "image/jpeg", - jpeg: "image/jpeg", - png: "image/png", - webp: "image/webp" - }[type] || "image/png"; - if (type === "svg") { - const svgText = decodeURIComponent(url.split(",")[1]); - return new Blob([svgText], { type: "image/svg+xml" }); - } - const canvas = await createBackground(url, { dpr: 1, scale }, backgroundColor); - return new Promise((resolve) => { - canvas.toBlob((blob) => resolve(blob), `${mime}`, quality); - }); -} -async function createBackground(url, { dpr = 1, scale = 1 }, backgroundColor) { - const baseCanvas = await toCanvas(url, { dpr, scale }); - if (!backgroundColor) return baseCanvas; - const temp = document.createElement("canvas"); - temp.width = baseCanvas.width; - temp.height = baseCanvas.height; - const ctx = temp.getContext("2d"); - ctx.fillStyle = backgroundColor; - ctx.fillRect(0, 0, temp.width, temp.height); - ctx.drawImage(baseCanvas, 0, 0); - return temp; -} -async function toRasterImg(url, { dpr = 1, scale = 1, backgroundColor, quality }, format = "png") { - const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0; - const finalBg = backgroundColor ?? defaultBg; - const canvas = await createBackground(url, { dpr, scale }, finalBg); - const img = new Image(); - img.src = canvas.toDataURL(`image/${format}`, quality); - await img.decode(); - img.style.width = `${canvas.width / dpr}px`; - img.style.height = `${canvas.height / dpr}px`; - return img; -} -async function download(url, { dpr = 1, scale = 1, backgroundColor, format = "png", filename = "snapDOM" } = {}) { - if (format === "svg") { - const blob = await toBlob(url); - const objectURL = URL.createObjectURL(blob); - const a2 = document.createElement("a"); - a2.href = objectURL; - a2.download = `${filename}.svg`; - a2.click(); - URL.revokeObjectURL(objectURL); - return; - } - const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0; - const finalBg = backgroundColor ?? defaultBg; - const canvas = await createBackground(url, { dpr, scale }, finalBg); - const mime = { - jpg: "image/jpeg", - jpeg: "image/jpeg", - png: "image/png", - webp: "image/webp" - }[format] || "image/png"; - const dataURL = canvas.toDataURL(mime); - const a = document.createElement("a"); - a.href = dataURL; - a.download = `${filename}.${format}`; - a.click(); -} -async function snapdom(element, options = {}) { - options = { scale: 1, ...options }; - if (!element) throw new Error("Element cannot be null or undefined"); - if (options.iconFonts) { - extendIconFonts(options.iconFonts); - } - return await snapdom.capture(element, options); -} -snapdom.capture = async (el, options = {}) => { - const url = await captureDOM(el, options); - const dpr = options.dpr ?? (window.devicePixelRatio || 1); - const scale = options.scale || 1; - return { - url, - options, - toRaw: () => url, - toImg: (opts = {}) => toImg(url, { dpr, scale, ...opts }), - toCanvas: (opts = {}) => toCanvas(url, { dpr, scale, ...opts }), - toBlob: (opts = {}) => toBlob(url, { dpr, scale, ...opts }), - toPng: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "png"), - toJpg: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "jpeg"), - toWebp: (opts = {}) => toRasterImg(url, { dpr, scale, ...opts }, "webp"), - download: ({ format = "png", filename = "snapDOM", backgroundColor, ...opts } = {}) => download(url, { dpr, scale, format, filename, backgroundColor, ...opts }) - }; -}; -snapdom.toRaw = async (el, options) => (await snapdom.capture(el, options)).toRaw(); -snapdom.toImg = async (el, options) => (await snapdom.capture(el, options)).toImg(); -snapdom.toCanvas = async (el, options) => (await snapdom.capture(el, options)).toCanvas(); -snapdom.toBlob = async (el, options) => (await snapdom.capture(el, options)).toBlob(options); -snapdom.toPng = async (el, options) => (await snapdom.capture(el, options)).toPng(options); -snapdom.toJpg = async (el, options) => (await snapdom.capture(el, options)).toJpg(options); -snapdom.toWebp = async (el, options) => (await snapdom.capture(el, options)).toWebp(options); -snapdom.download = async (el, options = {}) => { - const { - format = "png", - filename = "capture", - backgroundColor, - ...rest - } = options; - const capture = await snapdom.capture(el, rest); - return await capture.download({ format, filename, backgroundColor }); -}; - -// src/api/preCache.js -async function preCache(root = document, options = {}) { - const { embedFonts = true, reset = false, useProxy } = options; - if (reset) { - cache.image.clear(); - cache.background.clear(); - cache.resource.clear(); - cache.defaultStyle.clear(); - cache.baseStyle.clear(); - cache.font.clear(); - cache.computedStyle = /* @__PURE__ */ new WeakMap(); - return; - } - try { - await document.fonts.ready; - } catch { - } - precacheCommonTags(); - let imgEls = [], allEls = []; - if (root?.querySelectorAll) { - imgEls = Array.from(root.querySelectorAll("img[src]")); - allEls = Array.from(root.querySelectorAll("*")); - } - const promises = []; - for (const img of imgEls) { - const src = img?.src; - if (!src) continue; - if (!cache.image.has(src)) { - const p = Promise.resolve().then(() => fetchImage(src, { useProxy })).then((dataURL) => { - cache.image.set(src, dataURL); - }).catch(() => { - }); - promises.push(p); - } - } - for (const el of allEls) { - let bg = ""; - try { - bg = getStyle(el).backgroundImage; - } catch { - } - if (bg && bg !== "none") { - const bgSplits = splitBackgroundImage(bg); - for (const entry of bgSplits) { - if (entry.startsWith("url(")) { - const p = Promise.resolve().then(() => inlineSingleBackgroundEntry(entry, { ...options, useProxy })).catch(() => { - }); - promises.push(p); - } - } - } - } - if (embedFonts) { - try { - await embedCustomFonts({ preCached: true, localFonts: options.localFonts, useProxy: options.useProxy }); - } catch { - } - ; - } - await Promise.allSettled(promises); -} -export { - preCache, - snapdom -}; From 1bd10c626d0a19adaaa710407695594d2da79c04 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 12 Nov 2025 12:47:49 +0800 Subject: [PATCH 09/10] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extensions/ServiceCollectionExtensions.cs | 4 ++-- .../Services/DefaultDom2ImageService.cs | 2 +- .../BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs | 2 +- .../BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs b/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs index 18757adb..9d6092bb 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection; /// /// BootstrapBlazor 服务扩展类 /// -public static class BootstrapBlazorHtml2PdfServiceExtensions +public static class BootstrapBlazorDom2ImageServiceExtensions { /// /// 添加 AzureOpenAIService 服务 diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs index 164946f0..86d241cf 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Services/DefaultDom2ImageService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs b/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs index 088256f6..aae31d4b 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Services/Dom2ImageOptions.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone diff --git a/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs index fb0b8fb2..c6aec702 100644 --- a/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs +++ b/src/components/BootstrapBlazor.Dom2Image/Services/IDom2ImageService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ From fe08efd2570ca4801e5f93cc5597d26b85c0fd47 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 12 Nov 2025 12:47:57 +0800 Subject: [PATCH 10/10] chore: bump version 10.0.0 --- .../BootstrapBlazor.Dom2Image.csproj | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj index 5ebbb5d0..e5e5e629 100644 --- a/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj +++ b/src/components/BootstrapBlazor.Dom2Image/BootstrapBlazor.Dom2Image.csproj @@ -1,7 +1,7 @@  - 9.0.0 + 10.0.0 @@ -10,12 +10,7 @@ - - - - - - +