@zumer/snapdom 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -5
- package/dist/snapdom.js +814 -549
- package/dist/snapdom.min.js +13 -13
- package/dist/snapdom.min.mjs +13 -13
- package/dist/snapdom.mjs +261 -97
- package/package.json +3 -2
package/dist/snapdom.js
CHANGED
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.8.0
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
8
|
+
|
|
8
9
|
(() => {
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
12
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
14
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
15
|
+
var __spreadValues = (a, b) => {
|
|
16
|
+
for (var prop in b || (b = {}))
|
|
17
|
+
if (__hasOwnProp.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
if (__getOwnPropSymbols)
|
|
20
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
21
|
+
if (__propIsEnum.call(b, prop))
|
|
22
|
+
__defNormalProp(a, prop, b[prop]);
|
|
23
|
+
}
|
|
24
|
+
return a;
|
|
25
|
+
};
|
|
26
|
+
var __objRest = (source, exclude) => {
|
|
27
|
+
var target = {};
|
|
28
|
+
for (var prop in source)
|
|
29
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
30
|
+
target[prop] = source[prop];
|
|
31
|
+
if (source != null && __getOwnPropSymbols)
|
|
32
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
33
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
34
|
+
target[prop] = source[prop];
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
};
|
|
38
|
+
var __async = (__this, __arguments, generator) => {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
var fulfilled = (value) => {
|
|
41
|
+
try {
|
|
42
|
+
step(generator.next(value));
|
|
43
|
+
} catch (e) {
|
|
44
|
+
reject(e);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var rejected = (value) => {
|
|
48
|
+
try {
|
|
49
|
+
step(generator.throw(value));
|
|
50
|
+
} catch (e) {
|
|
51
|
+
reject(e);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
55
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
9
59
|
// src/core/cache.js
|
|
10
60
|
var imageCache = /* @__PURE__ */ new Map();
|
|
11
61
|
var bgCache = /* @__PURE__ */ new Map();
|
|
@@ -13,6 +63,7 @@
|
|
|
13
63
|
var defaultStylesCache = /* @__PURE__ */ new Map();
|
|
14
64
|
var baseCSSCache = /* @__PURE__ */ new Map();
|
|
15
65
|
var computedStyleCache = /* @__PURE__ */ new WeakMap();
|
|
66
|
+
var processedFontURLs = /* @__PURE__ */ new Set();
|
|
16
67
|
|
|
17
68
|
// src/utils/cssTools.js
|
|
18
69
|
var commonTags = [
|
|
@@ -152,6 +203,29 @@
|
|
|
152
203
|
}
|
|
153
204
|
|
|
154
205
|
// src/utils/helpers.js
|
|
206
|
+
function inlineSingleBackgroundEntry(_0) {
|
|
207
|
+
return __async(this, arguments, function* (entry, options = {}) {
|
|
208
|
+
const isUrl = entry.startsWith("url(");
|
|
209
|
+
const isGradient = /^((repeating-)?(linear|radial|conic)-gradient)\(/i.test(entry);
|
|
210
|
+
if (isUrl) {
|
|
211
|
+
const rawUrl = extractURL(entry);
|
|
212
|
+
if (!rawUrl) return entry;
|
|
213
|
+
const encodedUrl = safeEncodeURI(rawUrl);
|
|
214
|
+
if (bgCache.has(encodedUrl)) {
|
|
215
|
+
return options.skipInline ? void 0 : `url(${bgCache.get(encodedUrl)})`;
|
|
216
|
+
} else {
|
|
217
|
+
const crossOrigin = options.crossOrigin ? options.crossOrigin(encodedUrl) : "anonymous";
|
|
218
|
+
const dataUrl = yield fetchImage(encodedUrl, 3e3, crossOrigin);
|
|
219
|
+
bgCache.set(encodedUrl, dataUrl);
|
|
220
|
+
return options.skipInline ? void 0 : `url(${dataUrl})`;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (isGradient || entry === "none") {
|
|
224
|
+
return entry;
|
|
225
|
+
}
|
|
226
|
+
return entry;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
155
229
|
function idle(fn, { fast = false } = {}) {
|
|
156
230
|
if (fast) return fn();
|
|
157
231
|
if ("requestIdleCallback" in window) {
|
|
@@ -180,7 +254,7 @@
|
|
|
180
254
|
if (clean.startsWith("\\")) {
|
|
181
255
|
try {
|
|
182
256
|
return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
|
|
183
|
-
} catch {
|
|
257
|
+
} catch (e) {
|
|
184
258
|
return clean;
|
|
185
259
|
}
|
|
186
260
|
}
|
|
@@ -205,7 +279,8 @@
|
|
|
205
279
|
/feather/i,
|
|
206
280
|
/bootstrap\s*icons/i,
|
|
207
281
|
/remix\s*icons/i,
|
|
208
|
-
/heroicons/i
|
|
282
|
+
/heroicons/i,
|
|
283
|
+
/lucide/i
|
|
209
284
|
];
|
|
210
285
|
return iconFontPatterns.some((rx) => rx.test(familyOrUrl));
|
|
211
286
|
}
|
|
@@ -219,10 +294,10 @@
|
|
|
219
294
|
}, timeout);
|
|
220
295
|
const image = new Image();
|
|
221
296
|
image.crossOrigin = crossOrigin;
|
|
222
|
-
image.onload =
|
|
297
|
+
image.onload = () => __async(null, null, function* () {
|
|
223
298
|
clearTimeout(timeoutId);
|
|
224
299
|
try {
|
|
225
|
-
|
|
300
|
+
yield image.decode();
|
|
226
301
|
const canvas = document.createElement("canvas");
|
|
227
302
|
canvas.width = image.width;
|
|
228
303
|
canvas.height = image.height;
|
|
@@ -238,7 +313,7 @@
|
|
|
238
313
|
} catch (e) {
|
|
239
314
|
reject(e);
|
|
240
315
|
}
|
|
241
|
-
};
|
|
316
|
+
});
|
|
242
317
|
image.onerror = (e) => {
|
|
243
318
|
clearTimeout(timeoutId);
|
|
244
319
|
reject(new Error("Failed to load image: " + (e.message || "Unknown error")));
|
|
@@ -256,22 +331,67 @@
|
|
|
256
331
|
function isSafari() {
|
|
257
332
|
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
258
333
|
}
|
|
334
|
+
function stripTranslate(transform) {
|
|
335
|
+
if (!transform || transform === "none") return "";
|
|
336
|
+
let cleaned = transform.replace(/translate[XY]?\([^)]*\)/g, "");
|
|
337
|
+
cleaned = cleaned.replace(/matrix\(([^)]+)\)/g, (_, values) => {
|
|
338
|
+
const parts = values.split(",").map((s) => s.trim());
|
|
339
|
+
if (parts.length !== 6) return `matrix(${values})`;
|
|
340
|
+
parts[4] = "0";
|
|
341
|
+
parts[5] = "0";
|
|
342
|
+
return `matrix(${parts.join(", ")})`;
|
|
343
|
+
});
|
|
344
|
+
cleaned = cleaned.replace(/matrix3d\(([^)]+)\)/g, (_, values) => {
|
|
345
|
+
const parts = values.split(",").map((s) => s.trim());
|
|
346
|
+
if (parts.length !== 16) return `matrix3d(${values})`;
|
|
347
|
+
parts[12] = "0";
|
|
348
|
+
parts[13] = "0";
|
|
349
|
+
return `matrix3d(${parts.join(", ")})`;
|
|
350
|
+
});
|
|
351
|
+
return cleaned.trim().replace(/\s{2,}/g, " ");
|
|
352
|
+
}
|
|
353
|
+
function safeEncodeURI(uri) {
|
|
354
|
+
try {
|
|
355
|
+
const newURI = decodeURI(uri);
|
|
356
|
+
return encodeURI(newURI);
|
|
357
|
+
} catch (e) {
|
|
358
|
+
return uri;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function splitBackgroundImage(bg) {
|
|
362
|
+
const parts = [];
|
|
363
|
+
let depth = 0;
|
|
364
|
+
let lastIndex = 0;
|
|
365
|
+
for (let i = 0; i < bg.length; i++) {
|
|
366
|
+
const char = bg[i];
|
|
367
|
+
if (char === "(") depth++;
|
|
368
|
+
if (char === ")") depth--;
|
|
369
|
+
if (char === "," && depth === 0) {
|
|
370
|
+
parts.push(bg.slice(lastIndex, i).trim());
|
|
371
|
+
lastIndex = i + 1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
parts.push(bg.slice(lastIndex).trim());
|
|
375
|
+
return parts;
|
|
376
|
+
}
|
|
259
377
|
|
|
260
378
|
// src/modules/styles.js
|
|
261
379
|
function inlineAllStyles(source, clone, styleMap, cache, compress) {
|
|
380
|
+
var _a;
|
|
262
381
|
if (source.tagName === "STYLE") return;
|
|
263
382
|
if (!cache.has(source)) {
|
|
264
383
|
cache.set(source, getStyle(source));
|
|
265
384
|
}
|
|
266
385
|
const style = cache.get(source);
|
|
267
386
|
const snapshot = snapshotComputedStyle(style);
|
|
268
|
-
const tagName = source.tagName
|
|
387
|
+
const tagName = ((_a = source.tagName) == null ? void 0 : _a.toLowerCase()) || "div";
|
|
269
388
|
const key = getStyleKey(snapshot, tagName, compress);
|
|
270
389
|
styleMap.set(clone, key);
|
|
271
390
|
}
|
|
272
391
|
|
|
273
392
|
// src/core/clone.js
|
|
274
393
|
function deepClone(node, styleMap, styleCache, nodeMap, compress) {
|
|
394
|
+
var _a;
|
|
275
395
|
if (node.nodeType === Node.ELEMENT_NODE && node.getAttribute("data-capture") === "exclude") {
|
|
276
396
|
const spacer = document.createElement("div");
|
|
277
397
|
const rect = node.getBoundingClientRect();
|
|
@@ -281,7 +401,7 @@
|
|
|
281
401
|
if (node.tagName === "IFRAME") {
|
|
282
402
|
const fallback = document.createElement("div");
|
|
283
403
|
fallback.textContent = "";
|
|
284
|
-
fallback.style.cssText = `width: ${node.offsetWidth}px; height: ${node.offsetHeight}px; background: 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;`;
|
|
404
|
+
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;`;
|
|
285
405
|
return fallback;
|
|
286
406
|
}
|
|
287
407
|
if (node.nodeType === Node.ELEMENT_NODE && node.getAttribute("data-capture") === "placeholder") {
|
|
@@ -301,12 +421,12 @@
|
|
|
301
421
|
img.width = node.width;
|
|
302
422
|
img.height = node.height;
|
|
303
423
|
img.style.display = "inline-block";
|
|
304
|
-
img.style.width = `${node.width}px`;
|
|
305
|
-
img.style.height = `${node.height}px`;
|
|
424
|
+
img.style.width = node.style.width || `${node.width}px`;
|
|
425
|
+
img.style.height = node.style.height || `${node.height}px`;
|
|
306
426
|
return img;
|
|
307
427
|
}
|
|
308
428
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
309
|
-
if (node.parentElement
|
|
429
|
+
if ((_a = node.parentElement) == null ? void 0 : _a.shadowRoot) {
|
|
310
430
|
const tag = node.parentElement.tagName.toLowerCase();
|
|
311
431
|
if (customElements.get(tag)) return null;
|
|
312
432
|
}
|
|
@@ -352,552 +472,675 @@
|
|
|
352
472
|
}
|
|
353
473
|
|
|
354
474
|
// src/modules/fonts.js
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
475
|
+
function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
476
|
+
return __async(this, null, function* () {
|
|
477
|
+
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
478
|
+
const dpr = window.devicePixelRatio || 1;
|
|
479
|
+
const tempCanvas = document.createElement("canvas");
|
|
480
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
481
|
+
tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
482
|
+
const metrics = tempCtx.measureText(unicodeChar);
|
|
483
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
484
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
|
|
485
|
+
const height = ascent + descent;
|
|
486
|
+
const width = metrics.width;
|
|
487
|
+
const canvas = document.createElement("canvas");
|
|
488
|
+
canvas.width = Math.ceil(width * dpr);
|
|
489
|
+
canvas.height = Math.ceil(height * dpr);
|
|
490
|
+
const ctx = canvas.getContext("2d");
|
|
491
|
+
ctx.scale(dpr, dpr);
|
|
492
|
+
ctx.font = tempCtx.font;
|
|
493
|
+
ctx.textAlign = "left";
|
|
494
|
+
ctx.textBaseline = "alphabetic";
|
|
495
|
+
ctx.fillStyle = color;
|
|
496
|
+
ctx.fillText(unicodeChar, 0, ascent);
|
|
497
|
+
return canvas.toDataURL();
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
function isStylesheetLoaded(href) {
|
|
501
|
+
return Array.from(document.styleSheets).some((sheet) => sheet.href === href);
|
|
502
|
+
}
|
|
503
|
+
function injectLinkIfMissing(href) {
|
|
504
|
+
return new Promise((resolve) => {
|
|
505
|
+
if (isStylesheetLoaded(href)) return resolve(null);
|
|
506
|
+
const link = document.createElement("link");
|
|
507
|
+
link.rel = "stylesheet";
|
|
508
|
+
link.href = href;
|
|
509
|
+
link.setAttribute("data-snapdom", "injected-import");
|
|
510
|
+
link.onload = () => resolve(link);
|
|
511
|
+
link.onerror = () => resolve(null);
|
|
512
|
+
document.head.appendChild(link);
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
function embedCustomFonts() {
|
|
516
|
+
return __async(this, arguments, function* ({ ignoreIconFonts = true, preCached = false } = {}) {
|
|
517
|
+
if (resourceCache.has("fonts-embed-css")) {
|
|
518
|
+
if (preCached) {
|
|
519
|
+
const style = document.createElement("style");
|
|
520
|
+
style.setAttribute("data-snapdom", "embedFonts");
|
|
521
|
+
style.textContent = resourceCache.get("fonts-embed-css");
|
|
522
|
+
document.head.appendChild(style);
|
|
387
523
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
} catch (e) {
|
|
400
|
-
console.warn("[snapdom] Failed to encode font URL:", url);
|
|
401
|
-
}
|
|
402
|
-
if (ignoreIconFonts && isIconFont(url)) {
|
|
403
|
-
return null;
|
|
404
|
-
}
|
|
405
|
-
if (resourceCache.has(url)) {
|
|
406
|
-
return { original: match[0], inlined: `url(${resourceCache.get(url)})` };
|
|
407
|
-
}
|
|
408
|
-
try {
|
|
409
|
-
const fontRes = await fetch(url);
|
|
410
|
-
const blob = await fontRes.blob();
|
|
411
|
-
const b64 = await new Promise((resolve) => {
|
|
412
|
-
const reader = new FileReader();
|
|
413
|
-
reader.onload = () => resolve(reader.result);
|
|
414
|
-
reader.readAsDataURL(blob);
|
|
415
|
-
});
|
|
416
|
-
resourceCache.set(url, b64);
|
|
417
|
-
return { original: match[0], inlined: `url(${b64})` };
|
|
418
|
-
} catch (err) {
|
|
419
|
-
console.warn("[snapdom] Failed to fetch font:", url);
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
|
-
})
|
|
423
|
-
);
|
|
424
|
-
let cssFinal = cssText;
|
|
425
|
-
for (const r of inlinedCSS) {
|
|
426
|
-
if (r) {
|
|
427
|
-
cssFinal = cssFinal.replace(r.original, r.inlined);
|
|
524
|
+
return resourceCache.get("fonts-embed-css");
|
|
525
|
+
}
|
|
526
|
+
const importRegex = /@import\s+url\(["']?([^"')]+)["']?\)/g;
|
|
527
|
+
const styleImports = [];
|
|
528
|
+
for (const styleTag of document.querySelectorAll("style")) {
|
|
529
|
+
const cssText = styleTag.textContent || "";
|
|
530
|
+
const matches = Array.from(cssText.matchAll(importRegex));
|
|
531
|
+
for (const match of matches) {
|
|
532
|
+
const importUrl = match[1];
|
|
533
|
+
if (!isStylesheetLoaded(importUrl)) {
|
|
534
|
+
styleImports.push(importUrl);
|
|
428
535
|
}
|
|
429
536
|
}
|
|
430
|
-
finalCSS += cssFinal + "\n";
|
|
431
|
-
} catch (e) {
|
|
432
|
-
console.warn("[snapdom] Failed to fetch CSS:", link.href);
|
|
433
537
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
538
|
+
yield Promise.all(styleImports.map(injectLinkIfMissing));
|
|
539
|
+
const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter((link) => link.href);
|
|
540
|
+
let finalCSS = "";
|
|
541
|
+
for (const link of links) {
|
|
542
|
+
try {
|
|
543
|
+
const res = yield fetch(link.href);
|
|
544
|
+
const cssText = yield res.text();
|
|
545
|
+
if (ignoreIconFonts && (isIconFont(link.href) || isIconFont(cssText))) continue;
|
|
546
|
+
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
547
|
+
const inlinedCSS = yield Promise.all(
|
|
548
|
+
Array.from(cssText.matchAll(urlRegex)).map((match) => __async(null, null, function* () {
|
|
549
|
+
let rawUrl = extractURL(match[0]);
|
|
550
|
+
if (!rawUrl) return null;
|
|
551
|
+
let url = rawUrl;
|
|
552
|
+
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
553
|
+
url = new URL(url, link.href).href;
|
|
554
|
+
}
|
|
555
|
+
if (ignoreIconFonts && isIconFont(url)) return null;
|
|
556
|
+
if (resourceCache.has(url)) {
|
|
557
|
+
processedFontURLs.add(url);
|
|
558
|
+
return { original: match[0], inlined: `url(${resourceCache.get(url)})` };
|
|
559
|
+
}
|
|
560
|
+
if (processedFontURLs.has(url)) return null;
|
|
561
|
+
try {
|
|
562
|
+
const fontRes = yield fetch(url);
|
|
563
|
+
const blob = yield fontRes.blob();
|
|
564
|
+
const b64 = yield new Promise((resolve) => {
|
|
565
|
+
const reader = new FileReader();
|
|
566
|
+
reader.onload = () => resolve(reader.result);
|
|
567
|
+
reader.readAsDataURL(blob);
|
|
568
|
+
});
|
|
569
|
+
resourceCache.set(url, b64);
|
|
570
|
+
processedFontURLs.add(url);
|
|
571
|
+
return { original: match[0], inlined: `url(${b64})` };
|
|
572
|
+
} catch (e) {
|
|
573
|
+
console.warn("[snapdom] Failed to fetch font resource:", url);
|
|
574
|
+
return null;
|
|
472
575
|
}
|
|
473
|
-
|
|
576
|
+
}))
|
|
577
|
+
);
|
|
578
|
+
let cssFinal = cssText;
|
|
579
|
+
for (const r of inlinedCSS) {
|
|
580
|
+
if (r) cssFinal = cssFinal.replace(r.original, r.inlined);
|
|
581
|
+
}
|
|
582
|
+
finalCSS += cssFinal + "\n";
|
|
583
|
+
} catch (e) {
|
|
584
|
+
console.warn("[snapdom] Failed to fetch CSS:", link.href);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
for (const sheet of document.styleSheets) {
|
|
588
|
+
try {
|
|
589
|
+
if (!sheet.href || links.every((link) => link.href !== sheet.href)) {
|
|
590
|
+
for (const rule of sheet.cssRules) {
|
|
591
|
+
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
|
592
|
+
const src = rule.style.getPropertyValue("src");
|
|
593
|
+
if (!src) continue;
|
|
594
|
+
const urlRegex = /url\((["']?)([^"')]+)\1\)/g;
|
|
595
|
+
let inlinedSrc = src;
|
|
596
|
+
const matches = Array.from(src.matchAll(urlRegex));
|
|
597
|
+
for (const match of matches) {
|
|
598
|
+
let rawUrl = match[2].trim();
|
|
599
|
+
if (!rawUrl) continue;
|
|
600
|
+
let url = rawUrl;
|
|
601
|
+
if (!url.startsWith("http") && !url.startsWith("data:")) {
|
|
602
|
+
url = new URL(url, sheet.href || location.href).href;
|
|
603
|
+
}
|
|
604
|
+
if (ignoreIconFonts && isIconFont(url)) continue;
|
|
605
|
+
if (resourceCache.has(url)) {
|
|
606
|
+
processedFontURLs.add(url);
|
|
607
|
+
inlinedSrc = inlinedSrc.replace(match[0], `url(${resourceCache.get(url)})`);
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
if (processedFontURLs.has(url)) continue;
|
|
611
|
+
try {
|
|
612
|
+
const res = yield fetch(url);
|
|
613
|
+
const blob = yield res.blob();
|
|
614
|
+
const b64 = yield new Promise((resolve) => {
|
|
615
|
+
const reader = new FileReader();
|
|
616
|
+
reader.onload = () => resolve(reader.result);
|
|
617
|
+
reader.readAsDataURL(blob);
|
|
618
|
+
});
|
|
619
|
+
resourceCache.set(url, b64);
|
|
620
|
+
processedFontURLs.add(url);
|
|
621
|
+
inlinedSrc = inlinedSrc.replace(match[0], `url(${b64})`);
|
|
622
|
+
} catch (e) {
|
|
623
|
+
console.warn("[snapdom] Failed to fetch font URL:", url);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
finalCSS += `@font-face {
|
|
474
627
|
font-family: ${rule.style.getPropertyValue("font-family")};
|
|
475
628
|
src: ${inlinedSrc};
|
|
476
629
|
font-style: ${rule.style.getPropertyValue("font-style") || "normal"};
|
|
477
630
|
font-weight: ${rule.style.getPropertyValue("font-weight") || "normal"};
|
|
478
631
|
}
|
|
479
632
|
`;
|
|
633
|
+
}
|
|
480
634
|
}
|
|
481
635
|
}
|
|
636
|
+
} catch (e) {
|
|
637
|
+
console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
|
|
482
638
|
}
|
|
483
|
-
} catch (e) {
|
|
484
|
-
console.warn("[snapdom] Cannot access stylesheet", sheet.href, e);
|
|
485
639
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
640
|
+
for (const font of document.fonts) {
|
|
641
|
+
if (font.family && font.status === "loaded" && font._snapdomSrc) {
|
|
642
|
+
let b64 = font._snapdomSrc;
|
|
643
|
+
if (!b64.startsWith("data:")) {
|
|
644
|
+
if (resourceCache.has(font._snapdomSrc)) {
|
|
645
|
+
b64 = resourceCache.get(font._snapdomSrc);
|
|
646
|
+
processedFontURLs.add(font._snapdomSrc);
|
|
647
|
+
} else if (!processedFontURLs.has(font._snapdomSrc)) {
|
|
648
|
+
try {
|
|
649
|
+
const res = yield fetch(font._snapdomSrc);
|
|
650
|
+
const blob = yield res.blob();
|
|
651
|
+
b64 = yield new Promise((resolve) => {
|
|
652
|
+
const reader = new FileReader();
|
|
653
|
+
reader.onload = () => resolve(reader.result);
|
|
654
|
+
reader.readAsDataURL(blob);
|
|
655
|
+
});
|
|
656
|
+
resourceCache.set(font._snapdomSrc, b64);
|
|
657
|
+
processedFontURLs.add(font._snapdomSrc);
|
|
658
|
+
} catch (e) {
|
|
659
|
+
console.warn("[snapdom] Failed to fetch dynamic font src:", font._snapdomSrc);
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
506
662
|
}
|
|
507
663
|
}
|
|
508
|
-
|
|
509
|
-
finalCSS += `@font-face {
|
|
664
|
+
finalCSS += `@font-face {
|
|
510
665
|
font-family: '${font.family}';
|
|
511
666
|
src: url(${b64});
|
|
512
667
|
font-style: ${font.style || "normal"};
|
|
513
668
|
font-weight: ${font.weight || "normal"};
|
|
514
669
|
}
|
|
515
670
|
`;
|
|
671
|
+
}
|
|
516
672
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
673
|
+
if (finalCSS) {
|
|
674
|
+
resourceCache.set("fonts-embed-css", finalCSS);
|
|
675
|
+
if (preCached) {
|
|
676
|
+
const style = document.createElement("style");
|
|
677
|
+
style.setAttribute("data-snapdom", "embedFonts");
|
|
678
|
+
style.textContent = finalCSS;
|
|
679
|
+
document.head.appendChild(style);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return finalCSS;
|
|
683
|
+
});
|
|
525
684
|
}
|
|
526
685
|
|
|
527
686
|
// src/modules/pseudo.js
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const fontWeight = parseInt(style.getPropertyValue("font-weight")) || false;
|
|
542
|
-
const color = style.getPropertyValue("color") || "#000";
|
|
543
|
-
const pseudoEl = document.createElement("span");
|
|
544
|
-
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
545
|
-
const snapshot = snapshotComputedStyle(style);
|
|
546
|
-
const key = getStyleKey(snapshot, "span", compress);
|
|
547
|
-
styleMap.set(pseudoEl, key);
|
|
548
|
-
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(
|
|
549
|
-
fontFamily
|
|
550
|
-
);
|
|
551
|
-
let cleanContent = parseContent(content);
|
|
552
|
-
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
553
|
-
const imgEl = document.createElement("img");
|
|
554
|
-
imgEl.src = await iconToImage(
|
|
555
|
-
cleanContent,
|
|
556
|
-
fontFamily,
|
|
557
|
-
fontWeight,
|
|
558
|
-
fontSize,
|
|
559
|
-
color
|
|
687
|
+
function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
688
|
+
return __async(this, null, function* () {
|
|
689
|
+
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
690
|
+
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
691
|
+
try {
|
|
692
|
+
const style = getStyle(source, pseudo);
|
|
693
|
+
if (!style || typeof style[Symbol.iterator] !== "function") continue;
|
|
694
|
+
if (pseudo === "::first-letter") {
|
|
695
|
+
const normal = getComputedStyle(source);
|
|
696
|
+
const isMeaningful = style.color !== normal.color || style.fontSize !== normal.fontSize || style.fontWeight !== normal.fontWeight;
|
|
697
|
+
if (!isMeaningful) continue;
|
|
698
|
+
const textNode = Array.from(clone.childNodes).find(
|
|
699
|
+
(n) => n.nodeType === Node.TEXT_NODE && n.textContent && n.textContent.trim().length > 0
|
|
560
700
|
);
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
701
|
+
if (!textNode) continue;
|
|
702
|
+
const text = textNode.textContent;
|
|
703
|
+
const match = text.match(/^([^\p{L}\p{N}\s]*[\p{L}\p{N}](?:['’])?)/u);
|
|
704
|
+
const first = match == null ? void 0 : match[0];
|
|
705
|
+
const rest = text.slice((first == null ? void 0 : first.length) || 0);
|
|
706
|
+
if (!first || /[\uD800-\uDFFF]/.test(first)) continue;
|
|
707
|
+
const span = document.createElement("span");
|
|
708
|
+
span.textContent = first;
|
|
709
|
+
span.dataset.snapdomPseudo = "::first-letter";
|
|
710
|
+
const snapshot = snapshotComputedStyle(style);
|
|
711
|
+
const key = getStyleKey(snapshot, "span", compress);
|
|
712
|
+
styleMap.set(span, key);
|
|
713
|
+
const restNode = document.createTextNode(rest);
|
|
714
|
+
clone.replaceChild(restNode, textNode);
|
|
715
|
+
clone.insertBefore(span, restNode);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
const content = style.getPropertyValue("content");
|
|
719
|
+
const bg = style.getPropertyValue("background-image");
|
|
720
|
+
const bgColor = style.getPropertyValue("background-color");
|
|
721
|
+
const hasContent = content !== "none";
|
|
722
|
+
const hasBg = bg && bg !== "none";
|
|
723
|
+
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
724
|
+
if (hasContent || hasBg || hasBgColor) {
|
|
725
|
+
const fontFamily = style.getPropertyValue("font-family");
|
|
726
|
+
const fontSize = parseInt(style.getPropertyValue("font-size")) || 32;
|
|
727
|
+
const fontWeight = parseInt(style.getPropertyValue("font-weight")) || false;
|
|
728
|
+
const color = style.getPropertyValue("color") || "#000";
|
|
729
|
+
const pseudoEl = document.createElement("span");
|
|
730
|
+
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
731
|
+
const snapshot = snapshotComputedStyle(style);
|
|
732
|
+
const key = getStyleKey(snapshot, "span", compress);
|
|
733
|
+
styleMap.set(pseudoEl, key);
|
|
734
|
+
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons|lucide/i.test(
|
|
735
|
+
fontFamily
|
|
736
|
+
);
|
|
737
|
+
const cleanContent = parseContent(content);
|
|
738
|
+
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
739
|
+
const imgEl = document.createElement("img");
|
|
740
|
+
imgEl.src = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
741
|
+
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
742
|
+
pseudoEl.appendChild(imgEl);
|
|
743
|
+
} else if (cleanContent.startsWith("url(")) {
|
|
744
|
+
const rawUrl = extractURL(cleanContent);
|
|
745
|
+
if (rawUrl && rawUrl.trim() !== "") {
|
|
746
|
+
try {
|
|
747
|
+
const imgEl = document.createElement("img");
|
|
748
|
+
const dataUrl = yield fetchImage(safeEncodeURI(rawUrl));
|
|
749
|
+
imgEl.src = dataUrl;
|
|
750
|
+
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
751
|
+
pseudoEl.appendChild(imgEl);
|
|
752
|
+
} catch (e) {
|
|
753
|
+
console.error(`[snapdom] Error in pseudo ${pseudo} for`, source, e);
|
|
754
|
+
}
|
|
574
755
|
}
|
|
756
|
+
} else if (!isIconFont2 && cleanContent && cleanContent !== "none") {
|
|
757
|
+
pseudoEl.textContent = cleanContent;
|
|
575
758
|
}
|
|
576
|
-
|
|
577
|
-
pseudoEl.textContent = cleanContent;
|
|
578
|
-
}
|
|
579
|
-
if (hasBg && bg) {
|
|
580
|
-
const rawUrl = extractURL(bg);
|
|
581
|
-
if (rawUrl) {
|
|
759
|
+
if (hasBg) {
|
|
582
760
|
try {
|
|
583
|
-
const
|
|
584
|
-
|
|
761
|
+
const bgSplits = splitBackgroundImage(bg);
|
|
762
|
+
const newBgParts = yield Promise.all(
|
|
763
|
+
bgSplits.map((entry) => inlineSingleBackgroundEntry(entry))
|
|
764
|
+
);
|
|
765
|
+
pseudoEl.style.backgroundImage = newBgParts.join(", ");
|
|
585
766
|
} catch (e) {
|
|
586
767
|
console.warn(`[snapdom] Failed to inline background-image for ${pseudo}`, e);
|
|
587
768
|
}
|
|
588
769
|
}
|
|
770
|
+
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
|
|
771
|
+
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent && pseudoEl.textContent.trim() !== "";
|
|
772
|
+
const hasVisibleBox = hasContent2 || hasBg || hasBgColor;
|
|
773
|
+
if (!hasVisibleBox) continue;
|
|
774
|
+
pseudo === "::before" ? clone.insertBefore(pseudoEl, clone.firstChild) : clone.appendChild(pseudoEl);
|
|
589
775
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
if (pseudo === "::before") {
|
|
593
|
-
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
594
|
-
} else {
|
|
595
|
-
clone.appendChild(pseudoEl);
|
|
596
|
-
}
|
|
776
|
+
} catch (e) {
|
|
777
|
+
console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);
|
|
597
778
|
}
|
|
598
|
-
} catch (e) {
|
|
599
|
-
console.warn(`[snapdom] Failed to capture ${pseudo} for`, source, e);
|
|
600
779
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
780
|
+
const sChildren = Array.from(source.children);
|
|
781
|
+
const cChildren = Array.from(clone.children).filter((child) => !child.dataset.snapdomPseudo);
|
|
782
|
+
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
783
|
+
yield inlinePseudoElements(
|
|
784
|
+
sChildren[i],
|
|
785
|
+
cChildren[i],
|
|
786
|
+
styleMap,
|
|
787
|
+
styleCache,
|
|
788
|
+
compress,
|
|
789
|
+
embedFonts
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
});
|
|
614
793
|
}
|
|
615
794
|
|
|
616
795
|
// src/core/prepare.js
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
clone
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
630
|
-
} catch (e) {
|
|
631
|
-
console.warn("inlinePseudoElements failed:", e);
|
|
632
|
-
}
|
|
633
|
-
let classCSS = "";
|
|
634
|
-
if (compress) {
|
|
635
|
-
const keyToClass = generateCSSClasses(styleMap);
|
|
636
|
-
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
637
|
-
for (const [node, key] of styleMap.entries()) {
|
|
638
|
-
if (node.tagName === "STYLE") continue;
|
|
639
|
-
const className = keyToClass.get(key);
|
|
640
|
-
if (className) node.classList.add(className);
|
|
641
|
-
const bgImage = node.style?.backgroundImage;
|
|
642
|
-
node.removeAttribute("style");
|
|
643
|
-
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
796
|
+
function prepareClone(element, compress = false, embedFonts = false) {
|
|
797
|
+
return __async(this, null, function* () {
|
|
798
|
+
var _a;
|
|
799
|
+
const styleMap = /* @__PURE__ */ new Map();
|
|
800
|
+
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
801
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
802
|
+
let clone;
|
|
803
|
+
try {
|
|
804
|
+
clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
|
|
805
|
+
} catch (e) {
|
|
806
|
+
console.warn("deepClone failed:", e);
|
|
807
|
+
throw e;
|
|
644
808
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
809
|
+
try {
|
|
810
|
+
yield inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
811
|
+
} catch (e) {
|
|
812
|
+
console.warn("inlinePseudoElements failed:", e);
|
|
649
813
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
814
|
+
let classCSS = "";
|
|
815
|
+
if (compress) {
|
|
816
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
817
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
818
|
+
for (const [node, key] of styleMap.entries()) {
|
|
819
|
+
if (node.tagName === "STYLE") continue;
|
|
820
|
+
const className = keyToClass.get(key);
|
|
821
|
+
if (className) node.classList.add(className);
|
|
822
|
+
const bgImage = (_a = node.style) == null ? void 0 : _a.backgroundImage;
|
|
823
|
+
node.removeAttribute("style");
|
|
824
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
825
|
+
}
|
|
826
|
+
} else {
|
|
827
|
+
for (const [node, key] of styleMap.entries()) {
|
|
828
|
+
if (node.tagName === "STYLE") continue;
|
|
829
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
666
830
|
}
|
|
667
|
-
cloneNode.appendChild(inner);
|
|
668
831
|
}
|
|
669
|
-
|
|
670
|
-
|
|
832
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
833
|
+
const scrollX = originalNode.scrollLeft;
|
|
834
|
+
const scrollY = originalNode.scrollTop;
|
|
835
|
+
const hasScroll = scrollX || scrollY;
|
|
836
|
+
if (hasScroll && cloneNode instanceof HTMLElement) {
|
|
837
|
+
cloneNode.style.overflow = "hidden";
|
|
838
|
+
cloneNode.style.scrollbarWidth = "none";
|
|
839
|
+
cloneNode.style.msOverflowStyle = "none";
|
|
840
|
+
const inner = document.createElement("div");
|
|
841
|
+
inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
|
|
842
|
+
inner.style.willChange = "transform";
|
|
843
|
+
inner.style.display = "inline-block";
|
|
844
|
+
inner.style.width = "100%";
|
|
845
|
+
while (cloneNode.firstChild) {
|
|
846
|
+
inner.appendChild(cloneNode.firstChild);
|
|
847
|
+
}
|
|
848
|
+
cloneNode.appendChild(inner);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
if (element === nodeMap.get(clone)) {
|
|
852
|
+
const computed = styleCache.get(element) || window.getComputedStyle(element);
|
|
853
|
+
styleCache.set(element, computed);
|
|
854
|
+
const transform = stripTranslate(computed.transform);
|
|
855
|
+
clone.style.margin = "0";
|
|
856
|
+
clone.style.position = "static";
|
|
857
|
+
clone.style.top = "auto";
|
|
858
|
+
clone.style.left = "auto";
|
|
859
|
+
clone.style.right = "auto";
|
|
860
|
+
clone.style.bottom = "auto";
|
|
861
|
+
clone.style.zIndex = "auto";
|
|
862
|
+
clone.style.float = "none";
|
|
863
|
+
clone.style.clear = "none";
|
|
864
|
+
clone.style.transform = transform || "";
|
|
865
|
+
}
|
|
866
|
+
return { clone, classCSS, styleCache };
|
|
867
|
+
});
|
|
671
868
|
}
|
|
672
869
|
|
|
673
870
|
// src/modules/images.js
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
871
|
+
function inlineImages(_0) {
|
|
872
|
+
return __async(this, arguments, function* (clone, options = {}) {
|
|
873
|
+
const imgs = Array.from(clone.querySelectorAll("img"));
|
|
874
|
+
const processImg = (img) => __async(null, null, function* () {
|
|
875
|
+
const src = img.src;
|
|
876
|
+
try {
|
|
877
|
+
const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
|
|
878
|
+
const dataUrl = yield fetchImage(src, 3e3, crossOrigin);
|
|
879
|
+
img.src = dataUrl;
|
|
880
|
+
if (!img.width) img.width = img.naturalWidth || 100;
|
|
881
|
+
if (!img.height) img.height = img.naturalHeight || 100;
|
|
882
|
+
} catch (e) {
|
|
883
|
+
const fallback = document.createElement("div");
|
|
884
|
+
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;`;
|
|
885
|
+
fallback.innerText = "img";
|
|
886
|
+
img.replaceWith(fallback);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
for (let i = 0; i < imgs.length; i += 4) {
|
|
890
|
+
const group = imgs.slice(i, i + 4).map(processImg);
|
|
891
|
+
yield Promise.allSettled(group);
|
|
689
892
|
}
|
|
690
|
-
};
|
|
691
|
-
for (let i = 0; i < imgs.length; i += 4) {
|
|
692
|
-
const group = imgs.slice(i, i + 4).map(processImg);
|
|
693
|
-
await Promise.allSettled(group);
|
|
694
|
-
}
|
|
893
|
+
});
|
|
695
894
|
}
|
|
696
895
|
|
|
697
896
|
// src/modules/background.js
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
if (bgCache.has(bgUrl)) {
|
|
713
|
-
dataUrl = bgCache.get(bgUrl);
|
|
714
|
-
} else {
|
|
715
|
-
const crossOrigin = options.crossOrigin ? options.crossOrigin(bgUrl) : "anonymous";
|
|
716
|
-
dataUrl = await fetchImage(bgUrl, 3e3, crossOrigin);
|
|
717
|
-
bgCache.set(bgUrl, dataUrl);
|
|
718
|
-
}
|
|
719
|
-
bgSplits[i] = `url(${dataUrl})`;
|
|
720
|
-
} catch {
|
|
721
|
-
bgSplits[i] = "none";
|
|
897
|
+
function inlineBackgroundImages(_0, _1, _2) {
|
|
898
|
+
return __async(this, arguments, function* (source, clone, styleCache, options = {}) {
|
|
899
|
+
const queue = [[source, clone]];
|
|
900
|
+
while (queue.length) {
|
|
901
|
+
const [srcNode, cloneNode] = queue.shift();
|
|
902
|
+
const style = styleCache.get(srcNode) || getStyle(srcNode);
|
|
903
|
+
if (!styleCache.has(srcNode)) styleCache.set(srcNode, style);
|
|
904
|
+
const bg = style.getPropertyValue("background-image");
|
|
905
|
+
const bgColor = style.getPropertyValue("background-color");
|
|
906
|
+
if (!bg || bg === "none") {
|
|
907
|
+
const sChildren2 = Array.from(srcNode.children);
|
|
908
|
+
const cChildren2 = Array.from(cloneNode.children);
|
|
909
|
+
for (let i = 0; i < Math.min(sChildren2.length, cChildren2.length); i++) {
|
|
910
|
+
queue.push([sChildren2[i], cChildren2[i]]);
|
|
722
911
|
}
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
const bgSplits = splitBackgroundImage(bg);
|
|
915
|
+
const newBgParts = yield Promise.all(
|
|
916
|
+
bgSplits.map((entry) => inlineSingleBackgroundEntry(entry, options))
|
|
917
|
+
);
|
|
918
|
+
const hasRealBg = newBgParts.some(
|
|
919
|
+
(p) => p && p !== "none" && !/^url\(undefined\)/.test(p)
|
|
920
|
+
);
|
|
921
|
+
if (hasRealBg) {
|
|
922
|
+
cloneNode.style.backgroundImage = newBgParts.join(", ");
|
|
923
|
+
}
|
|
924
|
+
if (bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)") {
|
|
925
|
+
cloneNode.style.backgroundColor = bgColor;
|
|
926
|
+
}
|
|
927
|
+
const sChildren = Array.from(srcNode.children);
|
|
928
|
+
const cChildren = Array.from(cloneNode.children);
|
|
929
|
+
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
930
|
+
queue.push([sChildren[i], cChildren[i]]);
|
|
723
931
|
}
|
|
724
932
|
}
|
|
725
|
-
|
|
726
|
-
cloneNode.style.backgroundImage = bgSplits.join(",");
|
|
727
|
-
}
|
|
728
|
-
const sChildren = Array.from(srcNode.children);
|
|
729
|
-
const cChildren = Array.from(cloneNode.children);
|
|
730
|
-
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
731
|
-
queue.push([sChildren[i], cChildren[i]]);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
933
|
+
});
|
|
734
934
|
}
|
|
735
935
|
|
|
736
936
|
// src/core/capture.js
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
});
|
|
752
|
-
await new Promise((resolve) => {
|
|
753
|
-
idle(async () => {
|
|
754
|
-
await inlineBackgroundImages(element, clone, styleCache, options);
|
|
755
|
-
resolve();
|
|
756
|
-
}, { fast });
|
|
757
|
-
});
|
|
758
|
-
if (embedFonts) {
|
|
759
|
-
await new Promise((resolve) => {
|
|
760
|
-
idle(async () => {
|
|
761
|
-
fontsCSS = await embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
937
|
+
function captureDOM(_0) {
|
|
938
|
+
return __async(this, arguments, function* (element, options = {}) {
|
|
939
|
+
if (!element) throw new Error("Element cannot be null or undefined");
|
|
940
|
+
if (!(element instanceof Element)) throw new Error("captureDOM: Only Element nodes are supported");
|
|
941
|
+
const { compress = true, embedFonts = false, fast = true, scale = 1 } = options;
|
|
942
|
+
let clone, classCSS, styleCache;
|
|
943
|
+
let fontsCSS = "";
|
|
944
|
+
let baseCSS = "";
|
|
945
|
+
let dataURL;
|
|
946
|
+
let svgString;
|
|
947
|
+
({ clone, classCSS, styleCache } = yield prepareClone(element, compress, embedFonts));
|
|
948
|
+
yield new Promise((resolve) => {
|
|
949
|
+
idle(() => __async(null, null, function* () {
|
|
950
|
+
yield inlineImages(clone, options);
|
|
762
951
|
resolve();
|
|
763
|
-
}, { fast });
|
|
952
|
+
}), { fast });
|
|
764
953
|
});
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
idle(() => {
|
|
774
|
-
|
|
775
|
-
baseCSSCache.set(tagKey, baseCSS);
|
|
954
|
+
yield new Promise((resolve) => {
|
|
955
|
+
idle(() => __async(null, null, function* () {
|
|
956
|
+
yield inlineBackgroundImages(element, clone, styleCache, options);
|
|
957
|
+
resolve();
|
|
958
|
+
}), { fast });
|
|
959
|
+
});
|
|
960
|
+
if (embedFonts) {
|
|
961
|
+
yield new Promise((resolve) => {
|
|
962
|
+
idle(() => __async(null, null, function* () {
|
|
963
|
+
fontsCSS = yield embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
776
964
|
resolve();
|
|
777
|
-
}, { fast });
|
|
965
|
+
}), { fast });
|
|
778
966
|
});
|
|
779
967
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
968
|
+
if (compress) {
|
|
969
|
+
const usedTags = collectUsedTagNames(clone).sort();
|
|
970
|
+
const tagKey = usedTags.join(",");
|
|
971
|
+
if (baseCSSCache.has(tagKey)) {
|
|
972
|
+
baseCSS = baseCSSCache.get(tagKey);
|
|
973
|
+
} else {
|
|
974
|
+
yield new Promise((resolve) => {
|
|
975
|
+
idle(() => {
|
|
976
|
+
baseCSS = generateDedupedBaseCSS(usedTags);
|
|
977
|
+
baseCSSCache.set(tagKey, baseCSS);
|
|
978
|
+
resolve();
|
|
979
|
+
}, { fast });
|
|
980
|
+
});
|
|
790
981
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
982
|
+
}
|
|
983
|
+
yield new Promise((resolve) => {
|
|
984
|
+
idle(() => {
|
|
985
|
+
const rect = element.getBoundingClientRect();
|
|
986
|
+
let w = rect.width;
|
|
987
|
+
let h = rect.height;
|
|
988
|
+
const hasW = Number.isFinite(options.width);
|
|
989
|
+
const hasH = Number.isFinite(options.height);
|
|
990
|
+
const hasScale = typeof scale === "number" && scale !== 1;
|
|
991
|
+
if (!hasScale) {
|
|
992
|
+
const aspect = rect.width / rect.height;
|
|
993
|
+
if (hasW && hasH) {
|
|
994
|
+
w = options.width;
|
|
995
|
+
h = options.height;
|
|
996
|
+
} else if (hasW) {
|
|
997
|
+
w = options.width;
|
|
998
|
+
h = w / aspect;
|
|
999
|
+
} else if (hasH) {
|
|
1000
|
+
h = options.height;
|
|
1001
|
+
w = h * aspect;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
w = Math.ceil(w);
|
|
1005
|
+
h = Math.ceil(h);
|
|
1006
|
+
clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
1007
|
+
clone.style.transformOrigin = "top left";
|
|
1008
|
+
if (!hasScale && (hasW || hasH)) {
|
|
1009
|
+
const originalW = rect.width;
|
|
1010
|
+
const originalH = rect.height;
|
|
1011
|
+
const scaleX = w / originalW;
|
|
1012
|
+
const scaleY = h / originalH;
|
|
1013
|
+
const existingTransform = clone.style.transform || "";
|
|
1014
|
+
const scaleTransform = `scale(${scaleX}, ${scaleY})`;
|
|
1015
|
+
clone.style.transform = `${scaleTransform} ${existingTransform}`.trim();
|
|
1016
|
+
} else if (hasScale && isSafari()) {
|
|
1017
|
+
clone.style.scale = `${scale}`;
|
|
1018
|
+
}
|
|
1019
|
+
const svgNS = "http://www.w3.org/2000/svg";
|
|
1020
|
+
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
1021
|
+
fo.setAttribute("width", "100%");
|
|
1022
|
+
fo.setAttribute("height", "100%");
|
|
1023
|
+
const styleTag = document.createElement("style");
|
|
1024
|
+
styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;}" + classCSS;
|
|
1025
|
+
fo.appendChild(styleTag);
|
|
1026
|
+
fo.appendChild(clone);
|
|
1027
|
+
const serializer = new XMLSerializer();
|
|
1028
|
+
const foString = serializer.serializeToString(fo);
|
|
1029
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">`;
|
|
1030
|
+
const svgFooter = "</svg>";
|
|
1031
|
+
svgString = svgHeader + foString + svgFooter;
|
|
1032
|
+
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
1033
|
+
resolve();
|
|
1034
|
+
}, { fast });
|
|
1035
|
+
});
|
|
1036
|
+
const sandbox = document.getElementById("snapdom-sandbox");
|
|
1037
|
+
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
1038
|
+
return dataURL;
|
|
807
1039
|
});
|
|
808
|
-
const sandbox = document.getElementById("snapdom-sandbox");
|
|
809
|
-
if (sandbox && sandbox.style.position === "absolute") sandbox.remove();
|
|
810
|
-
return dataURL;
|
|
811
1040
|
}
|
|
812
1041
|
|
|
813
1042
|
// src/api/snapdom.js
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1043
|
+
function toImg(_0, _1) {
|
|
1044
|
+
return __async(this, arguments, function* (url, { dpr = 1, scale = 1 }) {
|
|
1045
|
+
const img = new Image();
|
|
1046
|
+
img.src = url;
|
|
1047
|
+
yield img.decode();
|
|
1048
|
+
if (isSafari) {
|
|
1049
|
+
img.width = img.width * scale;
|
|
1050
|
+
img.height = img.height * scale;
|
|
1051
|
+
} else {
|
|
1052
|
+
img.width = img.width / scale;
|
|
1053
|
+
img.height = img.height / scale;
|
|
1054
|
+
}
|
|
1055
|
+
return img;
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
function toCanvas(_0) {
|
|
1059
|
+
return __async(this, arguments, function* (url, { dpr = 1, scale = 1 } = {}) {
|
|
1060
|
+
const img = new Image();
|
|
1061
|
+
img.src = url;
|
|
1062
|
+
yield img.decode();
|
|
1063
|
+
const canvas = document.createElement("canvas");
|
|
1064
|
+
const width = img.width * scale;
|
|
1065
|
+
const height = img.height * scale;
|
|
1066
|
+
canvas.width = Math.ceil(width * dpr);
|
|
1067
|
+
canvas.height = Math.ceil(height * dpr);
|
|
1068
|
+
const ctx = canvas.getContext("2d");
|
|
1069
|
+
ctx.scale(dpr, dpr);
|
|
1070
|
+
ctx.drawImage(img, 0, 0, width, height);
|
|
1071
|
+
canvas.style.width = `${width}px`;
|
|
1072
|
+
canvas.style.height = `${height}px`;
|
|
1073
|
+
return canvas;
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
function toBlob(url) {
|
|
1077
|
+
return __async(this, null, function* () {
|
|
1078
|
+
const svgText = decodeURIComponent(url.split(",")[1]);
|
|
1079
|
+
return new Blob([svgText], { type: "image/svg+xml" });
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
function createBackground(_0, _1, _2) {
|
|
1083
|
+
return __async(this, arguments, function* (url, { dpr = 1, scale = 1 }, backgroundColor) {
|
|
1084
|
+
const baseCanvas = yield toCanvas(url, { dpr, scale });
|
|
1085
|
+
if (!backgroundColor) return baseCanvas;
|
|
1086
|
+
const temp = document.createElement("canvas");
|
|
1087
|
+
temp.width = baseCanvas.width;
|
|
1088
|
+
temp.height = baseCanvas.height;
|
|
1089
|
+
const ctx = temp.getContext("2d");
|
|
1090
|
+
ctx.fillStyle = backgroundColor;
|
|
1091
|
+
ctx.fillRect(0, 0, temp.width, temp.height);
|
|
1092
|
+
ctx.drawImage(baseCanvas, 0, 0);
|
|
1093
|
+
return temp;
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
function toRasterImg(_0, _1) {
|
|
1097
|
+
return __async(this, arguments, function* (url, { dpr = 1, scale = 1, backgroundColor = "#fff", quality }, format = "png") {
|
|
1098
|
+
const canvas = yield createBackground(url, { dpr, scale }, backgroundColor);
|
|
1099
|
+
const img = new Image();
|
|
1100
|
+
img.src = canvas.toDataURL(`image/${format}`, quality);
|
|
1101
|
+
yield img.decode();
|
|
1102
|
+
img.style.width = `${canvas.width / dpr}px`;
|
|
1103
|
+
img.style.height = `${canvas.height / dpr}px`;
|
|
1104
|
+
return img;
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
function download(_0) {
|
|
1108
|
+
return __async(this, arguments, function* (url, { dpr = 1, scale = 1, backgroundColor = "#fff", format = "png", filename = "capture" } = {}) {
|
|
1109
|
+
if (format === "svg") {
|
|
1110
|
+
const blob = yield toBlob(url);
|
|
1111
|
+
const objectURL = URL.createObjectURL(blob);
|
|
1112
|
+
const a2 = document.createElement("a");
|
|
1113
|
+
a2.href = objectURL;
|
|
1114
|
+
a2.download = `${filename}.svg`;
|
|
1115
|
+
a2.click();
|
|
1116
|
+
URL.revokeObjectURL(objectURL);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const defaultBg = ["jpg", "jpeg", "webp"].includes(format) ? "#fff" : void 0;
|
|
1120
|
+
const finalBg = backgroundColor != null ? backgroundColor : defaultBg;
|
|
1121
|
+
const canvas = yield createBackground(url, { dpr, scale }, finalBg);
|
|
1122
|
+
const mime = {
|
|
1123
|
+
jpg: "image/jpeg",
|
|
1124
|
+
jpeg: "image/jpeg",
|
|
1125
|
+
png: "image/png",
|
|
1126
|
+
webp: "image/webp"
|
|
1127
|
+
}[format] || "image/png";
|
|
1128
|
+
const dataURL = canvas.toDataURL(mime);
|
|
1129
|
+
const a = document.createElement("a");
|
|
1130
|
+
a.href = dataURL;
|
|
1131
|
+
a.download = `${filename}.${format}`;
|
|
1132
|
+
a.click();
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
function snapdom(_0) {
|
|
1136
|
+
return __async(this, arguments, function* (element, options = {}) {
|
|
1137
|
+
options = __spreadValues({ scale: 1 }, options);
|
|
1138
|
+
if (!element) throw new Error("Element cannot be null or undefined");
|
|
1139
|
+
return yield snapdom.capture(element, options);
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
snapdom.capture = (_0, ..._1) => __async(null, [_0, ..._1], function* (el, options = {}) {
|
|
1143
|
+
const url = yield captureDOM(el, options);
|
|
901
1144
|
const dpr = window.devicePixelRatio || 1;
|
|
902
1145
|
const scale = options.scale || 1;
|
|
903
1146
|
return {
|
|
@@ -907,74 +1150,96 @@
|
|
|
907
1150
|
toImg: () => toImg(url, { dpr, scale }),
|
|
908
1151
|
toCanvas: () => toCanvas(url, { dpr, scale }),
|
|
909
1152
|
toBlob: () => toBlob(url),
|
|
910
|
-
toPng: (options2) => toRasterImg(url, { dpr, scale,
|
|
911
|
-
toJpg: (options2) => toRasterImg(url, { dpr, scale,
|
|
912
|
-
toWebp: (options2) => toRasterImg(url, { dpr, scale,
|
|
1153
|
+
toPng: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "png"),
|
|
1154
|
+
toJpg: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "jpeg"),
|
|
1155
|
+
toWebp: (options2) => toRasterImg(url, __spreadValues({ dpr, scale }, options2), "webp"),
|
|
913
1156
|
download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
|
|
914
1157
|
};
|
|
915
|
-
};
|
|
916
|
-
snapdom.toRaw =
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
snapdom.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
snapdom.
|
|
923
|
-
|
|
924
|
-
|
|
1158
|
+
});
|
|
1159
|
+
snapdom.toRaw = (el, options) => __async(null, null, function* () {
|
|
1160
|
+
return (yield snapdom.capture(el, options)).toRaw();
|
|
1161
|
+
});
|
|
1162
|
+
snapdom.toImg = (el, options) => __async(null, null, function* () {
|
|
1163
|
+
return (yield snapdom.capture(el, options)).toImg();
|
|
1164
|
+
});
|
|
1165
|
+
snapdom.toCanvas = (el, options) => __async(null, null, function* () {
|
|
1166
|
+
return (yield snapdom.capture(el, options)).toCanvas();
|
|
1167
|
+
});
|
|
1168
|
+
snapdom.toBlob = (el, options) => __async(null, null, function* () {
|
|
1169
|
+
return (yield snapdom.capture(el, options)).toBlob();
|
|
1170
|
+
});
|
|
1171
|
+
snapdom.toPng = (el, options) => __async(null, null, function* () {
|
|
1172
|
+
return (yield snapdom.capture(el, options)).toPng(options);
|
|
1173
|
+
});
|
|
1174
|
+
snapdom.toJpg = (el, options) => __async(null, null, function* () {
|
|
1175
|
+
return (yield snapdom.capture(el, options)).toJpg(options);
|
|
1176
|
+
});
|
|
1177
|
+
snapdom.toWebp = (el, options) => __async(null, null, function* () {
|
|
1178
|
+
return (yield snapdom.capture(el, options)).toWebp(options);
|
|
1179
|
+
});
|
|
1180
|
+
snapdom.download = (_0, ..._1) => __async(null, [_0, ..._1], function* (el, options = {}) {
|
|
1181
|
+
const _a = options, {
|
|
925
1182
|
format = "png",
|
|
926
1183
|
filename = "capture",
|
|
927
|
-
backgroundColor
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1184
|
+
backgroundColor
|
|
1185
|
+
} = _a, rest = __objRest(_a, [
|
|
1186
|
+
"format",
|
|
1187
|
+
"filename",
|
|
1188
|
+
"backgroundColor"
|
|
1189
|
+
]);
|
|
1190
|
+
const capture = yield snapdom.capture(el, rest);
|
|
1191
|
+
return yield capture.download({ format, filename, backgroundColor });
|
|
1192
|
+
});
|
|
933
1193
|
|
|
934
1194
|
// src/api/preCache.js
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
await document.fonts.ready;
|
|
946
|
-
precacheCommonTags();
|
|
947
|
-
let imgEls = [], allEls = [];
|
|
948
|
-
if (root?.querySelectorAll) {
|
|
949
|
-
imgEls = Array.from(root.querySelectorAll("img[src]"));
|
|
950
|
-
allEls = Array.from(root.querySelectorAll("*"));
|
|
951
|
-
}
|
|
952
|
-
const promises = [];
|
|
953
|
-
for (const img of imgEls) {
|
|
954
|
-
const src = img.src;
|
|
955
|
-
if (!imageCache.has(src)) {
|
|
956
|
-
const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
|
|
957
|
-
promises.push(
|
|
958
|
-
fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
|
|
959
|
-
})
|
|
960
|
-
);
|
|
1195
|
+
function preCache() {
|
|
1196
|
+
return __async(this, arguments, function* (root = document, options = {}) {
|
|
1197
|
+
const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
|
|
1198
|
+
if (reset) {
|
|
1199
|
+
imageCache.clear();
|
|
1200
|
+
bgCache.clear();
|
|
1201
|
+
resourceCache.clear();
|
|
1202
|
+
baseCSSCache.clear();
|
|
1203
|
+
return;
|
|
961
1204
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
promises.push(
|
|
969
|
-
fetchImage(url, 3e3, crossOrigin).then((dataURL) => bgCache.set(url, dataURL)).catch(() => {
|
|
970
|
-
})
|
|
971
|
-
);
|
|
1205
|
+
yield document.fonts.ready;
|
|
1206
|
+
precacheCommonTags();
|
|
1207
|
+
let imgEls = [], allEls = [];
|
|
1208
|
+
if (root == null ? void 0 : root.querySelectorAll) {
|
|
1209
|
+
imgEls = Array.from(root.querySelectorAll("img[src]"));
|
|
1210
|
+
allEls = Array.from(root.querySelectorAll("*"));
|
|
972
1211
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1212
|
+
const promises = [];
|
|
1213
|
+
for (const img of imgEls) {
|
|
1214
|
+
const src = img.src;
|
|
1215
|
+
if (!imageCache.has(src)) {
|
|
1216
|
+
const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
|
|
1217
|
+
promises.push(
|
|
1218
|
+
fetchImage(src, 3e3, crossOrigin).then((dataURL) => imageCache.set(src, dataURL)).catch(() => {
|
|
1219
|
+
})
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
for (const el of allEls) {
|
|
1224
|
+
const bg = getStyle(el).backgroundImage;
|
|
1225
|
+
if (bg && bg !== "none") {
|
|
1226
|
+
const bgSplits = splitBackgroundImage(bg);
|
|
1227
|
+
for (const entry of bgSplits) {
|
|
1228
|
+
const isUrl = entry.startsWith("url(");
|
|
1229
|
+
if (isUrl) {
|
|
1230
|
+
promises.push(
|
|
1231
|
+
inlineSingleBackgroundEntry(entry, { crossOrigin: crossOriginFn, skipInline: true }).catch(() => {
|
|
1232
|
+
})
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
if (embedFonts) {
|
|
1239
|
+
yield embedCustomFonts({ ignoreIconFonts: !embedFonts, preCached: true });
|
|
1240
|
+
}
|
|
1241
|
+
yield Promise.all(promises);
|
|
1242
|
+
});
|
|
978
1243
|
}
|
|
979
1244
|
|
|
980
1245
|
// src/index.browser.js
|