image-exporter 1.0.6 → 1.0.8
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 +2 -2
- package/bun.lockb +0 -0
- package/dist/image-exporter.es.js +1456 -682
- package/dist/image-exporter.umd.js +1463 -689
- package/package.json +3 -3
- package/src/capture/capture-element.ts +10 -7
- package/src/capture/handle-filenames.ts +9 -7
- package/src/types.d.ts +1 -1
|
@@ -3,810 +3,1581 @@
|
|
|
3
3
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global2 = typeof globalThis !== "undefined" ? globalThis : global2 || self, factory(global2["image-exporter"] = {}));
|
|
4
4
|
})(this, function(exports2) {
|
|
5
5
|
"use strict";
|
|
6
|
+
var _a;
|
|
7
|
+
function handleFileNames(imageOptions, filenames) {
|
|
8
|
+
let proposedFilename = imageOptions.label;
|
|
9
|
+
if (imageOptions.includeScaleInLabel) proposedFilename += `_@${imageOptions.scale}x`;
|
|
10
|
+
const extension = `.${imageOptions.format}`;
|
|
11
|
+
proposedFilename += extension;
|
|
12
|
+
if (!filenames.includes(proposedFilename)) {
|
|
13
|
+
filenames.push(proposedFilename);
|
|
14
|
+
return proposedFilename;
|
|
15
|
+
}
|
|
16
|
+
const numberPattern = /-(\d+)$/;
|
|
17
|
+
const match = proposedFilename.match(numberPattern);
|
|
18
|
+
if (match) {
|
|
19
|
+
const baseFilename = proposedFilename.replace(numberPattern, "");
|
|
20
|
+
let counter = parseInt(match[1], 10);
|
|
21
|
+
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
22
|
+
counter++;
|
|
23
|
+
}
|
|
24
|
+
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
25
|
+
filenames.push(newFilename);
|
|
26
|
+
return newFilename;
|
|
27
|
+
} else {
|
|
28
|
+
const baseFilename = proposedFilename.replace(extension, "");
|
|
29
|
+
let counter = 2;
|
|
30
|
+
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
31
|
+
counter++;
|
|
32
|
+
}
|
|
33
|
+
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
34
|
+
filenames.push(newFilename);
|
|
35
|
+
return newFilename;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function changeJpegDpi(uint8Array, dpi) {
|
|
39
|
+
uint8Array[13] = 1;
|
|
40
|
+
uint8Array[14] = dpi >> 8;
|
|
41
|
+
uint8Array[15] = dpi & 255;
|
|
42
|
+
uint8Array[16] = dpi >> 8;
|
|
43
|
+
uint8Array[17] = dpi & 255;
|
|
44
|
+
return uint8Array;
|
|
45
|
+
}
|
|
46
|
+
const _P = "p".charCodeAt(0);
|
|
47
|
+
const _H = "H".charCodeAt(0);
|
|
48
|
+
const _Y = "Y".charCodeAt(0);
|
|
49
|
+
const _S = "s".charCodeAt(0);
|
|
50
|
+
let pngDataTable;
|
|
51
|
+
function createPngDataTable() {
|
|
52
|
+
const crcTable = new Int32Array(256);
|
|
53
|
+
for (let n = 0; n < 256; n++) {
|
|
54
|
+
let c = n;
|
|
55
|
+
for (let k = 0; k < 8; k++) {
|
|
56
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
57
|
+
}
|
|
58
|
+
crcTable[n] = c;
|
|
59
|
+
}
|
|
60
|
+
return crcTable;
|
|
61
|
+
}
|
|
62
|
+
function calcCrc(uint8Array) {
|
|
63
|
+
let c = -1;
|
|
64
|
+
if (!pngDataTable)
|
|
65
|
+
pngDataTable = createPngDataTable();
|
|
66
|
+
for (let n = 0; n < uint8Array.length; n++) {
|
|
67
|
+
c = pngDataTable[(c ^ uint8Array[n]) & 255] ^ c >>> 8;
|
|
68
|
+
}
|
|
69
|
+
return c ^ -1;
|
|
70
|
+
}
|
|
71
|
+
function searchStartOfPhys(uint8Array) {
|
|
72
|
+
const length = uint8Array.length - 1;
|
|
73
|
+
for (let i = length; i >= 4; i--) {
|
|
74
|
+
if (uint8Array[i - 4] === 9 && uint8Array[i - 3] === _P && uint8Array[i - 2] === _H && uint8Array[i - 1] === _Y && uint8Array[i] === _S) {
|
|
75
|
+
return i - 3;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
function changePngDpi(uint8Array, dpi, overwritepHYs = false) {
|
|
81
|
+
const physChunk = new Uint8Array(13);
|
|
82
|
+
dpi *= 39.3701;
|
|
83
|
+
physChunk[0] = _P;
|
|
84
|
+
physChunk[1] = _H;
|
|
85
|
+
physChunk[2] = _Y;
|
|
86
|
+
physChunk[3] = _S;
|
|
87
|
+
physChunk[4] = dpi >>> 24;
|
|
88
|
+
physChunk[5] = dpi >>> 16;
|
|
89
|
+
physChunk[6] = dpi >>> 8;
|
|
90
|
+
physChunk[7] = dpi & 255;
|
|
91
|
+
physChunk[8] = physChunk[4];
|
|
92
|
+
physChunk[9] = physChunk[5];
|
|
93
|
+
physChunk[10] = physChunk[6];
|
|
94
|
+
physChunk[11] = physChunk[7];
|
|
95
|
+
physChunk[12] = 1;
|
|
96
|
+
const crc = calcCrc(physChunk);
|
|
97
|
+
const crcChunk = new Uint8Array(4);
|
|
98
|
+
crcChunk[0] = crc >>> 24;
|
|
99
|
+
crcChunk[1] = crc >>> 16;
|
|
100
|
+
crcChunk[2] = crc >>> 8;
|
|
101
|
+
crcChunk[3] = crc & 255;
|
|
102
|
+
if (overwritepHYs) {
|
|
103
|
+
const startingIndex = searchStartOfPhys(uint8Array);
|
|
104
|
+
uint8Array.set(physChunk, startingIndex);
|
|
105
|
+
uint8Array.set(crcChunk, startingIndex + 13);
|
|
106
|
+
return uint8Array;
|
|
107
|
+
} else {
|
|
108
|
+
const chunkLength = new Uint8Array(4);
|
|
109
|
+
chunkLength[0] = 0;
|
|
110
|
+
chunkLength[1] = 0;
|
|
111
|
+
chunkLength[2] = 0;
|
|
112
|
+
chunkLength[3] = 9;
|
|
113
|
+
const finalHeader = new Uint8Array(54);
|
|
114
|
+
finalHeader.set(uint8Array, 0);
|
|
115
|
+
finalHeader.set(chunkLength, 33);
|
|
116
|
+
finalHeader.set(physChunk, 37);
|
|
117
|
+
finalHeader.set(crcChunk, 50);
|
|
118
|
+
return finalHeader;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const b64PhysSignature1 = "AAlwSFlz";
|
|
122
|
+
const b64PhysSignature2 = "AAAJcEhZ";
|
|
123
|
+
const b64PhysSignature3 = "AAAACXBI";
|
|
124
|
+
function detectPhysChunkFromDataUrl(dataUrl) {
|
|
125
|
+
let b64index = dataUrl.indexOf(b64PhysSignature1);
|
|
126
|
+
if (b64index === -1) {
|
|
127
|
+
b64index = dataUrl.indexOf(b64PhysSignature2);
|
|
128
|
+
}
|
|
129
|
+
if (b64index === -1) {
|
|
130
|
+
b64index = dataUrl.indexOf(b64PhysSignature3);
|
|
131
|
+
}
|
|
132
|
+
return b64index;
|
|
133
|
+
}
|
|
134
|
+
const PREFIX = "[modern-screenshot]";
|
|
135
|
+
const IN_BROWSER = typeof window !== "undefined";
|
|
136
|
+
const SUPPORT_WEB_WORKER = IN_BROWSER && "Worker" in window;
|
|
137
|
+
const SUPPORT_ATOB = IN_BROWSER && "atob" in window;
|
|
138
|
+
const SUPPORT_BTOA = IN_BROWSER && "btoa" in window;
|
|
139
|
+
const USER_AGENT = IN_BROWSER ? (_a = window.navigator) == null ? void 0 : _a.userAgent : "";
|
|
140
|
+
const IN_CHROME = USER_AGENT.includes("Chrome");
|
|
141
|
+
const IN_SAFARI = USER_AGENT.includes("AppleWebKit") && !IN_CHROME;
|
|
142
|
+
const IN_FIREFOX = USER_AGENT.includes("Firefox");
|
|
143
|
+
const isContext = (value) => value && "__CONTEXT__" in value;
|
|
144
|
+
const isCssFontFaceRule = (rule) => rule.constructor.name === "CSSFontFaceRule";
|
|
145
|
+
const isCSSImportRule = (rule) => rule.constructor.name === "CSSImportRule";
|
|
146
|
+
const isElementNode = (node) => node.nodeType === 1;
|
|
147
|
+
const isSVGElementNode = (node) => typeof node.className === "object";
|
|
148
|
+
const isSVGImageElementNode = (node) => node.tagName === "image";
|
|
149
|
+
const isSVGUseElementNode = (node) => node.tagName === "use";
|
|
150
|
+
const isHTMLElementNode = (node) => isElementNode(node) && typeof node.style !== "undefined" && !isSVGElementNode(node);
|
|
151
|
+
const isCommentNode = (node) => node.nodeType === 8;
|
|
152
|
+
const isTextNode = (node) => node.nodeType === 3;
|
|
153
|
+
const isImageElement = (node) => node.tagName === "IMG";
|
|
154
|
+
const isVideoElement = (node) => node.tagName === "VIDEO";
|
|
155
|
+
const isCanvasElement = (node) => node.tagName === "CANVAS";
|
|
156
|
+
const isTextareaElement = (node) => node.tagName === "TEXTAREA";
|
|
157
|
+
const isInputElement = (node) => node.tagName === "INPUT";
|
|
158
|
+
const isStyleElement = (node) => node.tagName === "STYLE";
|
|
159
|
+
const isScriptElement = (node) => node.tagName === "SCRIPT";
|
|
160
|
+
const isSelectElement = (node) => node.tagName === "SELECT";
|
|
161
|
+
const isSlotElement = (node) => node.tagName === "SLOT";
|
|
162
|
+
const isIFrameElement = (node) => node.tagName === "IFRAME";
|
|
163
|
+
const consoleWarn = (...args) => console.warn(PREFIX, ...args);
|
|
164
|
+
function supportWebp(ownerDocument) {
|
|
165
|
+
var _a2;
|
|
166
|
+
const canvas = (_a2 = ownerDocument == null ? void 0 : ownerDocument.createElement) == null ? void 0 : _a2.call(ownerDocument, "canvas");
|
|
167
|
+
if (canvas) {
|
|
168
|
+
canvas.height = canvas.width = 1;
|
|
169
|
+
}
|
|
170
|
+
return Boolean(canvas) && "toDataURL" in canvas && Boolean(canvas.toDataURL("image/webp").includes("image/webp"));
|
|
171
|
+
}
|
|
172
|
+
const isDataUrl = (url) => url.startsWith("data:");
|
|
6
173
|
function resolveUrl(url, baseUrl) {
|
|
7
|
-
if (url.match(/^[a-z]+:\/\//i))
|
|
174
|
+
if (url.match(/^[a-z]+:\/\//i))
|
|
8
175
|
return url;
|
|
9
|
-
|
|
10
|
-
if (url.match(/^\/\//)) {
|
|
176
|
+
if (IN_BROWSER && url.match(/^\/\//))
|
|
11
177
|
return window.location.protocol + url;
|
|
12
|
-
|
|
13
|
-
if (url.match(/^[a-z]+:/i)) {
|
|
178
|
+
if (url.match(/^[a-z]+:/i))
|
|
14
179
|
return url;
|
|
15
|
-
|
|
16
|
-
|
|
180
|
+
if (!IN_BROWSER)
|
|
181
|
+
return url;
|
|
182
|
+
const doc = getDocument().implementation.createHTMLDocument();
|
|
17
183
|
const base = doc.createElement("base");
|
|
18
184
|
const a = doc.createElement("a");
|
|
19
185
|
doc.head.appendChild(base);
|
|
20
186
|
doc.body.appendChild(a);
|
|
21
|
-
if (baseUrl)
|
|
187
|
+
if (baseUrl)
|
|
22
188
|
base.href = baseUrl;
|
|
23
|
-
}
|
|
24
189
|
a.href = url;
|
|
25
190
|
return a.href;
|
|
26
191
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const random = () => (
|
|
30
|
-
// eslint-disable-next-line no-bitwise
|
|
31
|
-
`0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
|
|
32
|
-
);
|
|
33
|
-
return () => {
|
|
34
|
-
counter += 1;
|
|
35
|
-
return `u${random()}${counter}`;
|
|
36
|
-
};
|
|
37
|
-
})();
|
|
38
|
-
function toArray(arrayLike) {
|
|
39
|
-
const arr = [];
|
|
40
|
-
for (let i = 0, l = arrayLike.length; i < l; i++) {
|
|
41
|
-
arr.push(arrayLike[i]);
|
|
42
|
-
}
|
|
43
|
-
return arr;
|
|
44
|
-
}
|
|
45
|
-
let styleProps = null;
|
|
46
|
-
function getStyleProperties(options = {}) {
|
|
47
|
-
if (styleProps) {
|
|
48
|
-
return styleProps;
|
|
49
|
-
}
|
|
50
|
-
if (options.includeStyleProperties) {
|
|
51
|
-
styleProps = options.includeStyleProperties;
|
|
52
|
-
return styleProps;
|
|
53
|
-
}
|
|
54
|
-
styleProps = toArray(window.getComputedStyle(document.documentElement));
|
|
55
|
-
return styleProps;
|
|
56
|
-
}
|
|
57
|
-
function px(node, styleProperty) {
|
|
58
|
-
const win = node.ownerDocument.defaultView || window;
|
|
59
|
-
const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
|
|
60
|
-
return val ? parseFloat(val.replace("px", "")) : 0;
|
|
61
|
-
}
|
|
62
|
-
function getNodeWidth(node) {
|
|
63
|
-
const leftBorder = px(node, "border-left-width");
|
|
64
|
-
const rightBorder = px(node, "border-right-width");
|
|
65
|
-
return node.clientWidth + leftBorder + rightBorder;
|
|
66
|
-
}
|
|
67
|
-
function getNodeHeight(node) {
|
|
68
|
-
const topBorder = px(node, "border-top-width");
|
|
69
|
-
const bottomBorder = px(node, "border-bottom-width");
|
|
70
|
-
return node.clientHeight + topBorder + bottomBorder;
|
|
71
|
-
}
|
|
72
|
-
function getImageSize(targetNode, options = {}) {
|
|
73
|
-
const width = options.width || getNodeWidth(targetNode);
|
|
74
|
-
const height = options.height || getNodeHeight(targetNode);
|
|
75
|
-
return { width, height };
|
|
192
|
+
function getDocument(target) {
|
|
193
|
+
return (target && isElementNode(target) ? target == null ? void 0 : target.ownerDocument : target) ?? window.document;
|
|
76
194
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
|
|
85
|
-
if (val) {
|
|
86
|
-
ratio = parseInt(val, 10);
|
|
87
|
-
if (Number.isNaN(ratio)) {
|
|
88
|
-
ratio = 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
return ratio || window.devicePixelRatio || 1;
|
|
195
|
+
const XMLNS = "http://www.w3.org/2000/svg";
|
|
196
|
+
function createSvg(width, height, ownerDocument) {
|
|
197
|
+
const svg = getDocument(ownerDocument).createElementNS(XMLNS, "svg");
|
|
198
|
+
svg.setAttributeNS(null, "width", width.toString());
|
|
199
|
+
svg.setAttributeNS(null, "height", height.toString());
|
|
200
|
+
svg.setAttributeNS(null, "viewBox", `0 0 ${width} ${height}`);
|
|
201
|
+
return svg;
|
|
92
202
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
if (canvas.width > canvas.height) {
|
|
98
|
-
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
99
|
-
canvas.width = canvasDimensionLimit;
|
|
100
|
-
} else {
|
|
101
|
-
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
102
|
-
canvas.height = canvasDimensionLimit;
|
|
103
|
-
}
|
|
104
|
-
} else if (canvas.width > canvasDimensionLimit) {
|
|
105
|
-
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
106
|
-
canvas.width = canvasDimensionLimit;
|
|
107
|
-
} else {
|
|
108
|
-
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
109
|
-
canvas.height = canvasDimensionLimit;
|
|
110
|
-
}
|
|
203
|
+
function svgToDataUrl(svg, removeControlCharacter) {
|
|
204
|
+
let xhtml = new XMLSerializer().serializeToString(svg);
|
|
205
|
+
if (removeControlCharacter) {
|
|
206
|
+
xhtml = xhtml.replace(/[\u0000-\u0008\v\f\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu, "");
|
|
111
207
|
}
|
|
208
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xhtml)}`;
|
|
112
209
|
}
|
|
113
|
-
function
|
|
210
|
+
function readBlob(blob, type) {
|
|
114
211
|
return new Promise((resolve, reject) => {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
img.crossOrigin = "anonymous";
|
|
123
|
-
img.decoding = "async";
|
|
124
|
-
img.src = url;
|
|
212
|
+
const reader = new FileReader();
|
|
213
|
+
reader.onload = () => resolve(reader.result);
|
|
214
|
+
reader.onerror = () => reject(reader.error);
|
|
215
|
+
reader.onabort = () => reject(new Error(`Failed read blob to ${type}`));
|
|
216
|
+
{
|
|
217
|
+
reader.readAsDataURL(blob);
|
|
218
|
+
}
|
|
125
219
|
});
|
|
126
220
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
svg.setAttribute("width", `${width}`);
|
|
135
|
-
svg.setAttribute("height", `${height}`);
|
|
136
|
-
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
137
|
-
foreignObject.setAttribute("width", "100%");
|
|
138
|
-
foreignObject.setAttribute("height", "100%");
|
|
139
|
-
foreignObject.setAttribute("x", "0");
|
|
140
|
-
foreignObject.setAttribute("y", "0");
|
|
141
|
-
foreignObject.setAttribute("externalResourcesRequired", "true");
|
|
142
|
-
svg.appendChild(foreignObject);
|
|
143
|
-
foreignObject.appendChild(node);
|
|
144
|
-
return svgToDataURL(svg);
|
|
221
|
+
const blobToDataUrl = (blob) => readBlob(blob, "dataUrl");
|
|
222
|
+
function createImage(url, ownerDocument) {
|
|
223
|
+
const img = getDocument(ownerDocument).createElement("img");
|
|
224
|
+
img.decoding = "sync";
|
|
225
|
+
img.loading = "eager";
|
|
226
|
+
img.src = url;
|
|
227
|
+
return img;
|
|
145
228
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
229
|
+
function loadMedia(media, options) {
|
|
230
|
+
return new Promise((resolve) => {
|
|
231
|
+
const { timeout, ownerDocument, onError: userOnError, onWarn } = options ?? {};
|
|
232
|
+
const node = typeof media === "string" ? createImage(media, getDocument(ownerDocument)) : media;
|
|
233
|
+
let timer = null;
|
|
234
|
+
let removeEventListeners = null;
|
|
235
|
+
function onResolve() {
|
|
236
|
+
resolve(node);
|
|
237
|
+
timer && clearTimeout(timer);
|
|
238
|
+
removeEventListeners == null ? void 0 : removeEventListeners();
|
|
239
|
+
}
|
|
240
|
+
if (timeout) {
|
|
241
|
+
timer = setTimeout(onResolve, timeout);
|
|
242
|
+
}
|
|
243
|
+
if (isVideoElement(node)) {
|
|
244
|
+
const currentSrc = node.currentSrc || node.src;
|
|
245
|
+
if (!currentSrc) {
|
|
246
|
+
if (node.poster) {
|
|
247
|
+
return loadMedia(node.poster, options).then(resolve);
|
|
248
|
+
}
|
|
249
|
+
return onResolve();
|
|
250
|
+
}
|
|
251
|
+
if (node.readyState >= 2) {
|
|
252
|
+
return onResolve();
|
|
253
|
+
}
|
|
254
|
+
const onLoadeddata = onResolve;
|
|
255
|
+
const onError = (error) => {
|
|
256
|
+
onWarn == null ? void 0 : onWarn(
|
|
257
|
+
"Failed video load",
|
|
258
|
+
currentSrc,
|
|
259
|
+
error
|
|
260
|
+
);
|
|
261
|
+
userOnError == null ? void 0 : userOnError(error);
|
|
262
|
+
onResolve();
|
|
263
|
+
};
|
|
264
|
+
removeEventListeners = () => {
|
|
265
|
+
node.removeEventListener("loadeddata", onLoadeddata);
|
|
266
|
+
node.removeEventListener("error", onError);
|
|
267
|
+
};
|
|
268
|
+
node.addEventListener("loadeddata", onLoadeddata, { once: true });
|
|
269
|
+
node.addEventListener("error", onError, { once: true });
|
|
270
|
+
} else {
|
|
271
|
+
const currentSrc = isSVGImageElementNode(node) ? node.href.baseVal : node.currentSrc || node.src;
|
|
272
|
+
if (!currentSrc) {
|
|
273
|
+
return onResolve();
|
|
274
|
+
}
|
|
275
|
+
const onLoad = async () => {
|
|
276
|
+
if (isImageElement(node) && "decode" in node) {
|
|
277
|
+
try {
|
|
278
|
+
await node.decode();
|
|
279
|
+
} catch (error) {
|
|
280
|
+
onWarn == null ? void 0 : onWarn(
|
|
281
|
+
"Failed to decode image, trying to render anyway",
|
|
282
|
+
node.dataset.originalSrc || currentSrc,
|
|
283
|
+
error
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
onResolve();
|
|
288
|
+
};
|
|
289
|
+
const onError = (error) => {
|
|
290
|
+
onWarn == null ? void 0 : onWarn(
|
|
291
|
+
"Failed image load",
|
|
292
|
+
node.dataset.originalSrc || currentSrc,
|
|
293
|
+
error
|
|
294
|
+
);
|
|
295
|
+
onResolve();
|
|
296
|
+
};
|
|
297
|
+
if (isImageElement(node) && node.complete) {
|
|
298
|
+
return onLoad();
|
|
299
|
+
}
|
|
300
|
+
removeEventListeners = () => {
|
|
301
|
+
node.removeEventListener("load", onLoad);
|
|
302
|
+
node.removeEventListener("error", onError);
|
|
303
|
+
};
|
|
304
|
+
node.addEventListener("load", onLoad, { once: true });
|
|
305
|
+
node.addEventListener("error", onError, { once: true });
|
|
306
|
+
}
|
|
307
|
+
});
|
|
157
308
|
}
|
|
158
|
-
function
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
|
|
171
|
-
const style = window.getComputedStyle(nativeNode, pseudo);
|
|
172
|
-
const content = style.getPropertyValue("content");
|
|
173
|
-
if (content === "" || content === "none") {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
const className = uuid();
|
|
177
|
-
try {
|
|
178
|
-
clonedNode.className = `${clonedNode.className} ${className}`;
|
|
179
|
-
} catch (err) {
|
|
180
|
-
return;
|
|
309
|
+
async function waitUntilLoad(node, options) {
|
|
310
|
+
if (isHTMLElementNode(node)) {
|
|
311
|
+
if (isImageElement(node) || isVideoElement(node)) {
|
|
312
|
+
await loadMedia(node, options);
|
|
313
|
+
} else {
|
|
314
|
+
await Promise.all(
|
|
315
|
+
["img", "video"].flatMap((selectors) => {
|
|
316
|
+
return Array.from(node.querySelectorAll(selectors)).map((el) => loadMedia(el, options));
|
|
317
|
+
})
|
|
318
|
+
);
|
|
319
|
+
}
|
|
181
320
|
}
|
|
182
|
-
const styleElement = document.createElement("style");
|
|
183
|
-
styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
|
|
184
|
-
clonedNode.appendChild(styleElement);
|
|
185
|
-
}
|
|
186
|
-
function clonePseudoElements(nativeNode, clonedNode, options) {
|
|
187
|
-
clonePseudoElement(nativeNode, clonedNode, ":before", options);
|
|
188
|
-
clonePseudoElement(nativeNode, clonedNode, ":after", options);
|
|
189
|
-
}
|
|
190
|
-
const WOFF = "application/font-woff";
|
|
191
|
-
const JPEG = "image/jpeg";
|
|
192
|
-
const mimes = {
|
|
193
|
-
woff: WOFF,
|
|
194
|
-
woff2: WOFF,
|
|
195
|
-
ttf: "application/font-truetype",
|
|
196
|
-
eot: "application/vnd.ms-fontobject",
|
|
197
|
-
png: "image/png",
|
|
198
|
-
jpg: JPEG,
|
|
199
|
-
jpeg: JPEG,
|
|
200
|
-
gif: "image/gif",
|
|
201
|
-
tiff: "image/tiff",
|
|
202
|
-
svg: "image/svg+xml",
|
|
203
|
-
webp: "image/webp"
|
|
204
|
-
};
|
|
205
|
-
function getExtension(url) {
|
|
206
|
-
const match = /\.([^./]*?)$/g.exec(url);
|
|
207
|
-
return match ? match[1] : "";
|
|
208
321
|
}
|
|
209
|
-
function
|
|
210
|
-
|
|
211
|
-
|
|
322
|
+
const uuid = /* @__PURE__ */ function uuid2() {
|
|
323
|
+
let counter = 0;
|
|
324
|
+
const random = () => `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4);
|
|
325
|
+
return () => {
|
|
326
|
+
counter += 1;
|
|
327
|
+
return `u${random()}${counter}`;
|
|
328
|
+
};
|
|
329
|
+
}();
|
|
330
|
+
function splitFontFamily(fontFamily) {
|
|
331
|
+
return fontFamily == null ? void 0 : fontFamily.split(",").map((val) => val.trim().replace(/"|'/g, "").toLowerCase()).filter(Boolean);
|
|
212
332
|
}
|
|
213
|
-
|
|
214
|
-
|
|
333
|
+
let uid = 0;
|
|
334
|
+
function createLogger(debug) {
|
|
335
|
+
const prefix = `${PREFIX}[#${uid}]`;
|
|
336
|
+
uid++;
|
|
337
|
+
return {
|
|
338
|
+
// eslint-disable-next-line no-console
|
|
339
|
+
time: (label) => debug && console.time(`${prefix} ${label}`),
|
|
340
|
+
// eslint-disable-next-line no-console
|
|
341
|
+
timeEnd: (label) => debug && console.timeEnd(`${prefix} ${label}`),
|
|
342
|
+
warn: (...args) => debug && consoleWarn(...args)
|
|
343
|
+
};
|
|
215
344
|
}
|
|
216
|
-
function
|
|
217
|
-
return
|
|
345
|
+
function getDefaultRequestInit(bypassingCache) {
|
|
346
|
+
return {
|
|
347
|
+
cache: bypassingCache ? "no-cache" : "force-cache"
|
|
348
|
+
};
|
|
218
349
|
}
|
|
219
|
-
function
|
|
220
|
-
return
|
|
350
|
+
async function orCreateContext(node, options) {
|
|
351
|
+
return isContext(node) ? node : createContext(node, { ...options, autoDestruct: true });
|
|
221
352
|
}
|
|
222
|
-
async function
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
353
|
+
async function createContext(node, options) {
|
|
354
|
+
var _a2, _b;
|
|
355
|
+
const { scale = 1, workerUrl, workerNumber = 1 } = options || {};
|
|
356
|
+
const debug = Boolean(options == null ? void 0 : options.debug);
|
|
357
|
+
const features = (options == null ? void 0 : options.features) ?? true;
|
|
358
|
+
const ownerDocument = node.ownerDocument ?? (IN_BROWSER ? window.document : void 0);
|
|
359
|
+
const ownerWindow = ((_a2 = node.ownerDocument) == null ? void 0 : _a2.defaultView) ?? (IN_BROWSER ? window : void 0);
|
|
360
|
+
const requests = /* @__PURE__ */ new Map();
|
|
361
|
+
const context = {
|
|
362
|
+
// Options
|
|
363
|
+
width: 0,
|
|
364
|
+
height: 0,
|
|
365
|
+
quality: 1,
|
|
366
|
+
type: "image/png",
|
|
367
|
+
scale,
|
|
368
|
+
backgroundColor: null,
|
|
369
|
+
style: null,
|
|
370
|
+
filter: null,
|
|
371
|
+
maximumCanvasSize: 0,
|
|
372
|
+
timeout: 3e4,
|
|
373
|
+
progress: null,
|
|
374
|
+
debug,
|
|
375
|
+
fetch: {
|
|
376
|
+
requestInit: getDefaultRequestInit((_b = options == null ? void 0 : options.fetch) == null ? void 0 : _b.bypassingCache),
|
|
377
|
+
placeholderImage: "data:image/png;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
378
|
+
bypassingCache: false,
|
|
379
|
+
...options == null ? void 0 : options.fetch
|
|
380
|
+
},
|
|
381
|
+
fetchFn: null,
|
|
382
|
+
font: {},
|
|
383
|
+
drawImageInterval: 100,
|
|
384
|
+
workerUrl: null,
|
|
385
|
+
workerNumber,
|
|
386
|
+
onCloneNode: null,
|
|
387
|
+
onEmbedNode: null,
|
|
388
|
+
onCreateForeignObjectSvg: null,
|
|
389
|
+
includeStyleProperties: null,
|
|
390
|
+
autoDestruct: false,
|
|
391
|
+
...options,
|
|
392
|
+
// InternalContext
|
|
393
|
+
__CONTEXT__: true,
|
|
394
|
+
log: createLogger(debug),
|
|
395
|
+
node,
|
|
396
|
+
ownerDocument,
|
|
397
|
+
ownerWindow,
|
|
398
|
+
dpi: scale === 1 ? null : 96 * scale,
|
|
399
|
+
svgStyleElement: createStyleElement(ownerDocument),
|
|
400
|
+
svgDefsElement: ownerDocument == null ? void 0 : ownerDocument.createElementNS(XMLNS, "defs"),
|
|
401
|
+
svgStyles: /* @__PURE__ */ new Map(),
|
|
402
|
+
defaultComputedStyles: /* @__PURE__ */ new Map(),
|
|
403
|
+
workers: [
|
|
404
|
+
...Array.from({
|
|
405
|
+
length: SUPPORT_WEB_WORKER && workerUrl && workerNumber ? workerNumber : 0
|
|
406
|
+
})
|
|
407
|
+
].map(() => {
|
|
232
408
|
try {
|
|
233
|
-
|
|
409
|
+
const worker = new Worker(workerUrl);
|
|
410
|
+
worker.onmessage = async (event) => {
|
|
411
|
+
var _a3, _b2, _c, _d;
|
|
412
|
+
const { url, result } = event.data;
|
|
413
|
+
if (result) {
|
|
414
|
+
(_b2 = (_a3 = requests.get(url)) == null ? void 0 : _a3.resolve) == null ? void 0 : _b2.call(_a3, result);
|
|
415
|
+
} else {
|
|
416
|
+
(_d = (_c = requests.get(url)) == null ? void 0 : _c.reject) == null ? void 0 : _d.call(_c, new Error(`Error receiving message from worker: ${url}`));
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
worker.onmessageerror = (event) => {
|
|
420
|
+
var _a3, _b2;
|
|
421
|
+
const { url } = event.data;
|
|
422
|
+
(_b2 = (_a3 = requests.get(url)) == null ? void 0 : _a3.reject) == null ? void 0 : _b2.call(_a3, new Error(`Error receiving message from worker: ${url}`));
|
|
423
|
+
};
|
|
424
|
+
return worker;
|
|
234
425
|
} catch (error) {
|
|
235
|
-
|
|
426
|
+
context.log.warn("Failed to new Worker", error);
|
|
427
|
+
return null;
|
|
236
428
|
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
429
|
+
}).filter(Boolean),
|
|
430
|
+
fontFamilies: /* @__PURE__ */ new Map(),
|
|
431
|
+
fontCssTexts: /* @__PURE__ */ new Map(),
|
|
432
|
+
acceptOfImage: `${[
|
|
433
|
+
supportWebp(ownerDocument) && "image/webp",
|
|
434
|
+
"image/svg+xml",
|
|
435
|
+
"image/*",
|
|
436
|
+
"*/*"
|
|
437
|
+
].filter(Boolean).join(",")};q=0.8`,
|
|
438
|
+
requests,
|
|
439
|
+
drawImageCount: 0,
|
|
440
|
+
tasks: [],
|
|
441
|
+
features,
|
|
442
|
+
isEnable: (key) => {
|
|
443
|
+
if (key === "restoreScrollPosition") {
|
|
444
|
+
return typeof features === "boolean" ? false : features[key] ?? false;
|
|
445
|
+
}
|
|
446
|
+
if (typeof features === "boolean") {
|
|
447
|
+
return features;
|
|
448
|
+
}
|
|
449
|
+
return features[key] ?? true;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
context.log.time("wait until load");
|
|
453
|
+
await waitUntilLoad(node, { timeout: context.timeout, onWarn: context.log.warn });
|
|
454
|
+
context.log.timeEnd("wait until load");
|
|
455
|
+
const { width, height } = resolveBoundingBox(node, context);
|
|
456
|
+
context.width = width;
|
|
457
|
+
context.height = height;
|
|
458
|
+
return context;
|
|
459
|
+
}
|
|
460
|
+
function createStyleElement(ownerDocument) {
|
|
461
|
+
if (!ownerDocument)
|
|
462
|
+
return void 0;
|
|
463
|
+
const style = ownerDocument.createElement("style");
|
|
464
|
+
const cssText = style.ownerDocument.createTextNode(`
|
|
465
|
+
.______background-clip--text {
|
|
466
|
+
background-clip: text;
|
|
467
|
+
-webkit-background-clip: text;
|
|
468
|
+
}
|
|
469
|
+
`);
|
|
470
|
+
style.appendChild(cssText);
|
|
471
|
+
return style;
|
|
240
472
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
473
|
+
function resolveBoundingBox(node, context) {
|
|
474
|
+
let { width, height } = context;
|
|
475
|
+
if (isElementNode(node) && (!width || !height)) {
|
|
476
|
+
const box = node.getBoundingClientRect();
|
|
477
|
+
width = width || box.width || Number(node.getAttribute("width")) || 0;
|
|
478
|
+
height = height || box.height || Number(node.getAttribute("height")) || 0;
|
|
246
479
|
}
|
|
247
|
-
|
|
248
|
-
|
|
480
|
+
return { width, height };
|
|
481
|
+
}
|
|
482
|
+
async function imageToCanvas(image, context) {
|
|
483
|
+
const {
|
|
484
|
+
log: log2,
|
|
485
|
+
timeout,
|
|
486
|
+
drawImageCount,
|
|
487
|
+
drawImageInterval
|
|
488
|
+
} = context;
|
|
489
|
+
log2.time("image to canvas");
|
|
490
|
+
const loaded = await loadMedia(image, { timeout, onWarn: context.log.warn });
|
|
491
|
+
const { canvas, context2d } = createCanvas(image.ownerDocument, context);
|
|
492
|
+
const drawImage = () => {
|
|
493
|
+
try {
|
|
494
|
+
context2d == null ? void 0 : context2d.drawImage(loaded, 0, 0, canvas.width, canvas.height);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
context.log.warn("Failed to drawImage", error);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
drawImage();
|
|
500
|
+
if (context.isEnable("fixSvgXmlDecode")) {
|
|
501
|
+
for (let i = 0; i < drawImageCount; i++) {
|
|
502
|
+
await new Promise((resolve) => {
|
|
503
|
+
setTimeout(() => {
|
|
504
|
+
drawImage();
|
|
505
|
+
resolve();
|
|
506
|
+
}, i + drawImageInterval);
|
|
507
|
+
});
|
|
508
|
+
}
|
|
249
509
|
}
|
|
250
|
-
|
|
510
|
+
context.drawImageCount = 0;
|
|
511
|
+
log2.timeEnd("image to canvas");
|
|
512
|
+
return canvas;
|
|
251
513
|
}
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
514
|
+
function createCanvas(ownerDocument, context) {
|
|
515
|
+
const { width, height, scale, backgroundColor, maximumCanvasSize: max } = context;
|
|
516
|
+
const canvas = ownerDocument.createElement("canvas");
|
|
517
|
+
canvas.width = Math.floor(width * scale);
|
|
518
|
+
canvas.height = Math.floor(height * scale);
|
|
519
|
+
canvas.style.width = `${width}px`;
|
|
520
|
+
canvas.style.height = `${height}px`;
|
|
521
|
+
if (max) {
|
|
522
|
+
if (canvas.width > max || canvas.height > max) {
|
|
523
|
+
if (canvas.width > max && canvas.height > max) {
|
|
524
|
+
if (canvas.width > canvas.height) {
|
|
525
|
+
canvas.height *= max / canvas.width;
|
|
526
|
+
canvas.width = max;
|
|
527
|
+
} else {
|
|
528
|
+
canvas.width *= max / canvas.height;
|
|
529
|
+
canvas.height = max;
|
|
530
|
+
}
|
|
531
|
+
} else if (canvas.width > max) {
|
|
532
|
+
canvas.height *= max / canvas.width;
|
|
533
|
+
canvas.width = max;
|
|
534
|
+
} else {
|
|
535
|
+
canvas.width *= max / canvas.height;
|
|
536
|
+
canvas.height = max;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
256
539
|
}
|
|
257
|
-
|
|
258
|
-
|
|
540
|
+
const context2d = canvas.getContext("2d");
|
|
541
|
+
if (context2d && backgroundColor) {
|
|
542
|
+
context2d.fillStyle = backgroundColor;
|
|
543
|
+
context2d.fillRect(0, 0, canvas.width, canvas.height);
|
|
259
544
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
545
|
+
return { canvas, context2d };
|
|
546
|
+
}
|
|
547
|
+
function cloneCanvas(canvas, context) {
|
|
548
|
+
if (canvas.ownerDocument) {
|
|
549
|
+
try {
|
|
550
|
+
const dataURL = canvas.toDataURL();
|
|
551
|
+
if (dataURL !== "data:,") {
|
|
552
|
+
return createImage(dataURL, canvas.ownerDocument);
|
|
265
553
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
554
|
+
} catch (error) {
|
|
555
|
+
context.log.warn("Failed to clone canvas", error);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
const cloned = canvas.cloneNode(false);
|
|
559
|
+
const ctx = canvas.getContext("2d");
|
|
560
|
+
const clonedCtx = cloned.getContext("2d");
|
|
561
|
+
try {
|
|
562
|
+
if (ctx && clonedCtx) {
|
|
563
|
+
clonedCtx.putImageData(
|
|
564
|
+
ctx.getImageData(0, 0, canvas.width, canvas.height),
|
|
565
|
+
0,
|
|
566
|
+
0
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
return cloned;
|
|
269
570
|
} catch (error) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
console.warn(msg);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
cache[cacheKey] = dataURL;
|
|
280
|
-
return dataURL;
|
|
281
|
-
}
|
|
282
|
-
async function cloneCanvasElement(canvas) {
|
|
283
|
-
const dataURL = canvas.toDataURL();
|
|
284
|
-
if (dataURL === "data:,") {
|
|
285
|
-
return canvas.cloneNode(false);
|
|
286
|
-
}
|
|
287
|
-
return createImage(dataURL);
|
|
288
|
-
}
|
|
289
|
-
async function cloneVideoElement(video, options) {
|
|
290
|
-
if (video.currentSrc) {
|
|
291
|
-
const canvas = document.createElement("canvas");
|
|
292
|
-
const ctx = canvas.getContext("2d");
|
|
293
|
-
canvas.width = video.clientWidth;
|
|
294
|
-
canvas.height = video.clientHeight;
|
|
295
|
-
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
296
|
-
const dataURL2 = canvas.toDataURL();
|
|
297
|
-
return createImage(dataURL2);
|
|
298
|
-
}
|
|
299
|
-
const poster = video.poster;
|
|
300
|
-
const contentType = getMimeType(poster);
|
|
301
|
-
const dataURL = await resourceToDataURL(poster, contentType, options);
|
|
302
|
-
return createImage(dataURL);
|
|
303
|
-
}
|
|
304
|
-
async function cloneIFrameElement(iframe, options) {
|
|
305
|
-
var _a;
|
|
571
|
+
context.log.warn("Failed to clone canvas", error);
|
|
572
|
+
}
|
|
573
|
+
return cloned;
|
|
574
|
+
}
|
|
575
|
+
function cloneIframe(iframe, context) {
|
|
576
|
+
var _a2;
|
|
306
577
|
try {
|
|
307
|
-
if ((
|
|
308
|
-
return
|
|
578
|
+
if ((_a2 = iframe == null ? void 0 : iframe.contentDocument) == null ? void 0 : _a2.body) {
|
|
579
|
+
return cloneNode(iframe.contentDocument.body, context);
|
|
309
580
|
}
|
|
310
|
-
} catch (
|
|
581
|
+
} catch (error) {
|
|
582
|
+
context.log.warn("Failed to clone iframe", error);
|
|
311
583
|
}
|
|
312
584
|
return iframe.cloneNode(false);
|
|
313
585
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
586
|
+
function cloneImage(image) {
|
|
587
|
+
const cloned = image.cloneNode(false);
|
|
588
|
+
if (image.currentSrc && image.currentSrc !== image.src) {
|
|
589
|
+
cloned.src = image.currentSrc;
|
|
590
|
+
cloned.srcset = "";
|
|
317
591
|
}
|
|
318
|
-
if (
|
|
319
|
-
|
|
592
|
+
if (cloned.loading === "lazy") {
|
|
593
|
+
cloned.loading = "eager";
|
|
320
594
|
}
|
|
321
|
-
|
|
322
|
-
|
|
595
|
+
return cloned;
|
|
596
|
+
}
|
|
597
|
+
async function cloneVideo(video, context) {
|
|
598
|
+
if (video.ownerDocument && !video.currentSrc && video.poster) {
|
|
599
|
+
return createImage(video.poster, video.ownerDocument);
|
|
600
|
+
}
|
|
601
|
+
const cloned = video.cloneNode(false);
|
|
602
|
+
cloned.crossOrigin = "anonymous";
|
|
603
|
+
if (video.currentSrc && video.currentSrc !== video.src) {
|
|
604
|
+
cloned.src = video.currentSrc;
|
|
323
605
|
}
|
|
324
|
-
|
|
606
|
+
const ownerDocument = cloned.ownerDocument;
|
|
607
|
+
if (ownerDocument) {
|
|
608
|
+
let canPlay = true;
|
|
609
|
+
await loadMedia(cloned, { onError: () => canPlay = false, onWarn: context.log.warn });
|
|
610
|
+
if (!canPlay) {
|
|
611
|
+
if (video.poster) {
|
|
612
|
+
return createImage(video.poster, video.ownerDocument);
|
|
613
|
+
}
|
|
614
|
+
return cloned;
|
|
615
|
+
}
|
|
616
|
+
cloned.currentTime = video.currentTime;
|
|
617
|
+
await new Promise((resolve) => {
|
|
618
|
+
cloned.addEventListener("seeked", resolve, { once: true });
|
|
619
|
+
});
|
|
620
|
+
const canvas = ownerDocument.createElement("canvas");
|
|
621
|
+
canvas.width = video.offsetWidth;
|
|
622
|
+
canvas.height = video.offsetHeight;
|
|
623
|
+
try {
|
|
624
|
+
const ctx = canvas.getContext("2d");
|
|
625
|
+
if (ctx)
|
|
626
|
+
ctx.drawImage(cloned, 0, 0, canvas.width, canvas.height);
|
|
627
|
+
} catch (error) {
|
|
628
|
+
context.log.warn("Failed to clone video", error);
|
|
629
|
+
if (video.poster) {
|
|
630
|
+
return createImage(video.poster, video.ownerDocument);
|
|
631
|
+
}
|
|
632
|
+
return cloned;
|
|
633
|
+
}
|
|
634
|
+
return cloneCanvas(canvas, context);
|
|
635
|
+
}
|
|
636
|
+
return cloned;
|
|
325
637
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
var _a, _b;
|
|
330
|
-
if (isSVGElement(clonedNode)) {
|
|
331
|
-
return clonedNode;
|
|
638
|
+
function cloneElement(node, context) {
|
|
639
|
+
if (isCanvasElement(node)) {
|
|
640
|
+
return cloneCanvas(node, context);
|
|
332
641
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
children = toArray(nativeNode.assignedNodes());
|
|
336
|
-
} else if (isInstanceOfElement(nativeNode, HTMLIFrameElement) && ((_a = nativeNode.contentDocument) === null || _a === void 0 ? void 0 : _a.body)) {
|
|
337
|
-
children = toArray(nativeNode.contentDocument.body.childNodes);
|
|
338
|
-
} else {
|
|
339
|
-
children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
|
|
642
|
+
if (isIFrameElement(node)) {
|
|
643
|
+
return cloneIframe(node, context);
|
|
340
644
|
}
|
|
341
|
-
if (
|
|
342
|
-
return
|
|
645
|
+
if (isImageElement(node)) {
|
|
646
|
+
return cloneImage(node);
|
|
343
647
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
648
|
+
if (isVideoElement(node)) {
|
|
649
|
+
return cloneVideo(node, context);
|
|
650
|
+
}
|
|
651
|
+
return node.cloneNode(false);
|
|
652
|
+
}
|
|
653
|
+
function getSandBox(context) {
|
|
654
|
+
var _a2;
|
|
655
|
+
let sandbox = context.sandbox;
|
|
656
|
+
if (!sandbox) {
|
|
657
|
+
const { ownerDocument } = context;
|
|
658
|
+
try {
|
|
659
|
+
if (ownerDocument) {
|
|
660
|
+
sandbox = ownerDocument.createElement("iframe");
|
|
661
|
+
sandbox.id = `__SANDBOX__-${uuid()}`;
|
|
662
|
+
sandbox.width = "0";
|
|
663
|
+
sandbox.height = "0";
|
|
664
|
+
sandbox.style.visibility = "hidden";
|
|
665
|
+
sandbox.style.position = "fixed";
|
|
666
|
+
ownerDocument.body.appendChild(sandbox);
|
|
667
|
+
(_a2 = sandbox.contentWindow) == null ? void 0 : _a2.document.write('<!DOCTYPE html><meta charset="UTF-8"><title></title><body>');
|
|
668
|
+
context.sandbox = sandbox;
|
|
669
|
+
}
|
|
670
|
+
} catch (error) {
|
|
671
|
+
context.log.warn("Failed to getSandBox", error);
|
|
347
672
|
}
|
|
348
|
-
}
|
|
349
|
-
return
|
|
673
|
+
}
|
|
674
|
+
return sandbox;
|
|
350
675
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
676
|
+
const ignoredStyles = [
|
|
677
|
+
"width",
|
|
678
|
+
"height",
|
|
679
|
+
"-webkit-text-fill-color"
|
|
680
|
+
];
|
|
681
|
+
const includedAttributes = [
|
|
682
|
+
"stroke",
|
|
683
|
+
"fill"
|
|
684
|
+
];
|
|
685
|
+
function getDefaultStyle(node, pseudoElement, context) {
|
|
686
|
+
const { defaultComputedStyles } = context;
|
|
687
|
+
const nodeName = node.nodeName.toLowerCase();
|
|
688
|
+
const isSvgNode = isSVGElementNode(node) && nodeName !== "svg";
|
|
689
|
+
const attributes = isSvgNode ? includedAttributes.map((name) => [name, node.getAttribute(name)]).filter(([, value]) => value !== null) : [];
|
|
690
|
+
const key = [
|
|
691
|
+
isSvgNode && "svg",
|
|
692
|
+
nodeName,
|
|
693
|
+
attributes.map((name, value) => `${name}=${value}`).join(","),
|
|
694
|
+
pseudoElement
|
|
695
|
+
].filter(Boolean).join(":");
|
|
696
|
+
if (defaultComputedStyles.has(key))
|
|
697
|
+
return defaultComputedStyles.get(key);
|
|
698
|
+
const sandbox = getSandBox(context);
|
|
699
|
+
const sandboxWindow = sandbox == null ? void 0 : sandbox.contentWindow;
|
|
700
|
+
if (!sandboxWindow)
|
|
701
|
+
return /* @__PURE__ */ new Map();
|
|
702
|
+
const sandboxDocument = sandboxWindow == null ? void 0 : sandboxWindow.document;
|
|
703
|
+
let root;
|
|
704
|
+
let el;
|
|
705
|
+
if (isSvgNode) {
|
|
706
|
+
root = sandboxDocument.createElementNS(XMLNS, "svg");
|
|
707
|
+
el = root.ownerDocument.createElementNS(root.namespaceURI, nodeName);
|
|
708
|
+
attributes.forEach(([name, value]) => {
|
|
709
|
+
el.setAttributeNS(null, name, value);
|
|
710
|
+
});
|
|
711
|
+
root.appendChild(el);
|
|
712
|
+
} else {
|
|
713
|
+
root = el = sandboxDocument.createElement(nodeName);
|
|
355
714
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
715
|
+
el.textContent = " ";
|
|
716
|
+
sandboxDocument.body.appendChild(root);
|
|
717
|
+
const computedStyle = sandboxWindow.getComputedStyle(el, pseudoElement);
|
|
718
|
+
const styles = /* @__PURE__ */ new Map();
|
|
719
|
+
for (let len = computedStyle.length, i = 0; i < len; i++) {
|
|
720
|
+
const name = computedStyle.item(i);
|
|
721
|
+
if (ignoredStyles.includes(name))
|
|
722
|
+
continue;
|
|
723
|
+
styles.set(name, computedStyle.getPropertyValue(name));
|
|
724
|
+
}
|
|
725
|
+
sandboxDocument.body.removeChild(root);
|
|
726
|
+
defaultComputedStyles.set(key, styles);
|
|
727
|
+
return styles;
|
|
728
|
+
}
|
|
729
|
+
function getDiffStyle(style, defaultStyle, includeStyleProperties) {
|
|
730
|
+
var _a2;
|
|
731
|
+
const diffStyle = /* @__PURE__ */ new Map();
|
|
732
|
+
const prefixs = [];
|
|
733
|
+
const prefixTree = /* @__PURE__ */ new Map();
|
|
734
|
+
if (includeStyleProperties) {
|
|
735
|
+
for (const name of includeStyleProperties) {
|
|
736
|
+
applyTo(name);
|
|
737
|
+
}
|
|
360
738
|
} else {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
739
|
+
for (let len = style.length, i = 0; i < len; i++) {
|
|
740
|
+
const name = style.item(i);
|
|
741
|
+
applyTo(name);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
for (let len = prefixs.length, i = 0; i < len; i++) {
|
|
745
|
+
(_a2 = prefixTree.get(prefixs[i])) == null ? void 0 : _a2.forEach((value, name) => diffStyle.set(name, value));
|
|
746
|
+
}
|
|
747
|
+
function applyTo(name) {
|
|
748
|
+
const value = style.getPropertyValue(name);
|
|
749
|
+
const priority = style.getPropertyPriority(name);
|
|
750
|
+
const subIndex = name.lastIndexOf("-");
|
|
751
|
+
const prefix = subIndex > -1 ? name.substring(0, subIndex) : void 0;
|
|
752
|
+
if (prefix) {
|
|
753
|
+
let map = prefixTree.get(prefix);
|
|
754
|
+
if (!map) {
|
|
755
|
+
map = /* @__PURE__ */ new Map();
|
|
756
|
+
prefixTree.set(prefix, map);
|
|
372
757
|
}
|
|
373
|
-
|
|
374
|
-
}
|
|
758
|
+
map.set(name, [value, priority]);
|
|
759
|
+
}
|
|
760
|
+
if (defaultStyle.get(name) === value && !priority)
|
|
761
|
+
return;
|
|
762
|
+
if (prefix) {
|
|
763
|
+
prefixs.push(prefix);
|
|
764
|
+
} else {
|
|
765
|
+
diffStyle.set(name, [value, priority]);
|
|
766
|
+
}
|
|
375
767
|
}
|
|
768
|
+
return diffStyle;
|
|
376
769
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
|
|
770
|
+
function copyCssStyles(node, cloned, isRoot, context) {
|
|
771
|
+
var _a2, _b, _c, _d;
|
|
772
|
+
const { ownerWindow, includeStyleProperties, currentParentNodeStyle } = context;
|
|
773
|
+
const clonedStyle = cloned.style;
|
|
774
|
+
const computedStyle = ownerWindow.getComputedStyle(node);
|
|
775
|
+
const defaultStyle = getDefaultStyle(node, null, context);
|
|
776
|
+
currentParentNodeStyle == null ? void 0 : currentParentNodeStyle.forEach((_, key) => {
|
|
777
|
+
defaultStyle.delete(key);
|
|
778
|
+
});
|
|
779
|
+
const style = getDiffStyle(computedStyle, defaultStyle, includeStyleProperties);
|
|
780
|
+
style.delete("transition-property");
|
|
781
|
+
style.delete("all");
|
|
782
|
+
style.delete("d");
|
|
783
|
+
style.delete("content");
|
|
784
|
+
if (isRoot) {
|
|
785
|
+
style.delete("margin-top");
|
|
786
|
+
style.delete("margin-right");
|
|
787
|
+
style.delete("margin-bottom");
|
|
788
|
+
style.delete("margin-left");
|
|
789
|
+
style.delete("margin-block-start");
|
|
790
|
+
style.delete("margin-block-end");
|
|
791
|
+
style.delete("margin-inline-start");
|
|
792
|
+
style.delete("margin-inline-end");
|
|
793
|
+
style.set("box-sizing", ["border-box", ""]);
|
|
380
794
|
}
|
|
381
|
-
if (
|
|
382
|
-
|
|
795
|
+
if (((_a2 = style.get("background-clip")) == null ? void 0 : _a2[0]) === "text") {
|
|
796
|
+
cloned.classList.add("______background-clip--text");
|
|
383
797
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (selectedOption) {
|
|
390
|
-
selectedOption.setAttribute("selected", "");
|
|
798
|
+
if (IN_CHROME) {
|
|
799
|
+
if (!style.has("font-kerning"))
|
|
800
|
+
style.set("font-kerning", ["normal", ""]);
|
|
801
|
+
if ((((_b = style.get("overflow-x")) == null ? void 0 : _b[0]) === "hidden" || ((_c = style.get("overflow-y")) == null ? void 0 : _c[0]) === "hidden") && ((_d = style.get("text-overflow")) == null ? void 0 : _d[0]) === "ellipsis" && node.scrollWidth === node.clientWidth) {
|
|
802
|
+
style.set("text-overflow", ["clip", ""]);
|
|
391
803
|
}
|
|
392
804
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (isInstanceOfElement(clonedNode, Element)) {
|
|
396
|
-
cloneCSSStyle(nativeNode, clonedNode, options);
|
|
397
|
-
clonePseudoElements(nativeNode, clonedNode, options);
|
|
398
|
-
cloneInputValue(nativeNode, clonedNode);
|
|
399
|
-
cloneSelectValue(nativeNode, clonedNode);
|
|
805
|
+
for (let len = clonedStyle.length, i = 0; i < len; i++) {
|
|
806
|
+
clonedStyle.removeProperty(clonedStyle.item(i));
|
|
400
807
|
}
|
|
401
|
-
|
|
808
|
+
style.forEach(([value, priority], name) => {
|
|
809
|
+
clonedStyle.setProperty(name, value, priority);
|
|
810
|
+
});
|
|
811
|
+
return style;
|
|
402
812
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
return clone;
|
|
813
|
+
function copyInputValue(node, cloned) {
|
|
814
|
+
if (isTextareaElement(node) || isInputElement(node) || isSelectElement(node)) {
|
|
815
|
+
cloned.setAttribute("value", node.value);
|
|
407
816
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
817
|
+
}
|
|
818
|
+
const pseudoClasses = [
|
|
819
|
+
":before",
|
|
820
|
+
":after"
|
|
821
|
+
// ':placeholder', TODO
|
|
822
|
+
];
|
|
823
|
+
const scrollbarPseudoClasses = [
|
|
824
|
+
":-webkit-scrollbar",
|
|
825
|
+
":-webkit-scrollbar-button",
|
|
826
|
+
// ':-webkit-scrollbar:horizontal', TODO
|
|
827
|
+
":-webkit-scrollbar-thumb",
|
|
828
|
+
":-webkit-scrollbar-track",
|
|
829
|
+
":-webkit-scrollbar-track-piece",
|
|
830
|
+
// ':-webkit-scrollbar:vertical', TODO
|
|
831
|
+
":-webkit-scrollbar-corner",
|
|
832
|
+
":-webkit-resizer"
|
|
833
|
+
];
|
|
834
|
+
function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFamilies) {
|
|
835
|
+
const { ownerWindow, svgStyleElement, svgStyles, currentNodeStyle } = context;
|
|
836
|
+
if (!svgStyleElement || !ownerWindow)
|
|
837
|
+
return;
|
|
838
|
+
function copyBy(pseudoClass) {
|
|
839
|
+
var _a2;
|
|
840
|
+
const computedStyle = ownerWindow.getComputedStyle(node, pseudoClass);
|
|
841
|
+
let content = computedStyle.getPropertyValue("content");
|
|
842
|
+
if (!content || content === "none")
|
|
843
|
+
return;
|
|
844
|
+
addWordToFontFamilies == null ? void 0 : addWordToFontFamilies(content);
|
|
845
|
+
content = content.replace(/(')|(")|(counter\(.+\))/g, "");
|
|
846
|
+
const klasses = [uuid()];
|
|
847
|
+
const defaultStyle = getDefaultStyle(node, pseudoClass, context);
|
|
848
|
+
currentNodeStyle == null ? void 0 : currentNodeStyle.forEach((_, key) => {
|
|
849
|
+
defaultStyle.delete(key);
|
|
850
|
+
});
|
|
851
|
+
const style = getDiffStyle(computedStyle, defaultStyle, context.includeStyleProperties);
|
|
852
|
+
style.delete("content");
|
|
853
|
+
style.delete("-webkit-locale");
|
|
854
|
+
if (((_a2 = style.get("background-clip")) == null ? void 0 : _a2[0]) === "text") {
|
|
855
|
+
cloned.classList.add("______background-clip--text");
|
|
418
856
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
857
|
+
const cloneStyle = [
|
|
858
|
+
`content: '${content}';`
|
|
859
|
+
];
|
|
860
|
+
style.forEach(([value, priority], name) => {
|
|
861
|
+
cloneStyle.push(`${name}: ${value}${priority ? " !important" : ""};`);
|
|
862
|
+
});
|
|
863
|
+
if (cloneStyle.length === 1)
|
|
864
|
+
return;
|
|
865
|
+
try {
|
|
866
|
+
cloned.className = [cloned.className, ...klasses].join(" ");
|
|
867
|
+
} catch (err) {
|
|
868
|
+
context.log.warn("Failed to copyPseudoClass", err);
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const cssText = cloneStyle.join("\n ");
|
|
872
|
+
let allClasses = svgStyles.get(cssText);
|
|
873
|
+
if (!allClasses) {
|
|
874
|
+
allClasses = [];
|
|
875
|
+
svgStyles.set(cssText, allClasses);
|
|
434
876
|
}
|
|
435
|
-
|
|
877
|
+
allClasses.push(`.${klasses[0]}:${pseudoClass}`);
|
|
436
878
|
}
|
|
437
|
-
|
|
879
|
+
pseudoClasses.forEach(copyBy);
|
|
880
|
+
if (copyScrollbar)
|
|
881
|
+
scrollbarPseudoClasses.forEach(copyBy);
|
|
438
882
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
883
|
+
const excludeParentNodes = /* @__PURE__ */ new Set([
|
|
884
|
+
"symbol"
|
|
885
|
+
// test/fixtures/svg.symbol.html
|
|
886
|
+
]);
|
|
887
|
+
async function appendChildNode(node, cloned, child, context, addWordToFontFamilies) {
|
|
888
|
+
if (isElementNode(child) && (isStyleElement(child) || isScriptElement(child)))
|
|
889
|
+
return;
|
|
890
|
+
if (context.filter && !context.filter(child))
|
|
891
|
+
return;
|
|
892
|
+
if (excludeParentNodes.has(cloned.nodeName) || excludeParentNodes.has(child.nodeName)) {
|
|
893
|
+
context.currentParentNodeStyle = void 0;
|
|
894
|
+
} else {
|
|
895
|
+
context.currentParentNodeStyle = context.currentNodeStyle;
|
|
896
|
+
}
|
|
897
|
+
const childCloned = await cloneNode(child, context, false, addWordToFontFamilies);
|
|
898
|
+
if (context.isEnable("restoreScrollPosition")) {
|
|
899
|
+
restoreScrollPosition(node, childCloned);
|
|
442
900
|
}
|
|
443
|
-
|
|
901
|
+
cloned.appendChild(childCloned);
|
|
444
902
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
903
|
+
async function cloneChildNodes(node, cloned, context, addWordToFontFamilies) {
|
|
904
|
+
var _a2;
|
|
905
|
+
const firstChild = (isElementNode(node) ? (_a2 = node.shadowRoot) == null ? void 0 : _a2.firstChild : void 0) ?? node.firstChild;
|
|
906
|
+
for (let child = firstChild; child; child = child.nextSibling) {
|
|
907
|
+
if (isCommentNode(child))
|
|
908
|
+
continue;
|
|
909
|
+
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === "function") {
|
|
910
|
+
const nodes = child.assignedNodes();
|
|
911
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
912
|
+
await appendChildNode(node, cloned, nodes[i], context, addWordToFontFamilies);
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
await appendChildNode(node, cloned, child, context, addWordToFontFamilies);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
451
918
|
}
|
|
452
|
-
function
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
919
|
+
function restoreScrollPosition(node, chlidCloned) {
|
|
920
|
+
if (!isHTMLElementNode(node) || !isHTMLElementNode(chlidCloned))
|
|
921
|
+
return;
|
|
922
|
+
const { scrollTop, scrollLeft } = node;
|
|
923
|
+
if (!scrollTop && !scrollLeft) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const { transform } = chlidCloned.style;
|
|
927
|
+
const matrix = new DOMMatrix(transform);
|
|
928
|
+
const { a, b, c, d } = matrix;
|
|
929
|
+
matrix.a = 1;
|
|
930
|
+
matrix.b = 0;
|
|
931
|
+
matrix.c = 0;
|
|
932
|
+
matrix.d = 1;
|
|
933
|
+
matrix.translateSelf(-scrollLeft, -scrollTop);
|
|
934
|
+
matrix.a = a;
|
|
935
|
+
matrix.b = b;
|
|
936
|
+
matrix.c = c;
|
|
937
|
+
matrix.d = d;
|
|
938
|
+
chlidCloned.style.transform = matrix.toString();
|
|
459
939
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
940
|
+
function applyCssStyleWithOptions(cloned, context) {
|
|
941
|
+
const { backgroundColor, width, height, style: styles } = context;
|
|
942
|
+
const clonedStyle = cloned.style;
|
|
943
|
+
if (backgroundColor)
|
|
944
|
+
clonedStyle.setProperty("background-color", backgroundColor, "important");
|
|
945
|
+
if (width)
|
|
946
|
+
clonedStyle.setProperty("width", `${width}px`, "important");
|
|
947
|
+
if (height)
|
|
948
|
+
clonedStyle.setProperty("height", `${height}px`, "important");
|
|
949
|
+
if (styles) {
|
|
950
|
+
for (const name in styles) clonedStyle[name] = styles[name];
|
|
471
951
|
}
|
|
472
|
-
return cssText;
|
|
473
952
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
953
|
+
const NORMAL_ATTRIBUTE_RE = /^[\w-:]+$/;
|
|
954
|
+
async function cloneNode(node, context, isRoot = false, addWordToFontFamilies) {
|
|
955
|
+
var _a2, _b, _c, _d;
|
|
956
|
+
const { ownerDocument, ownerWindow, fontFamilies } = context;
|
|
957
|
+
if (ownerDocument && isTextNode(node)) {
|
|
958
|
+
if (addWordToFontFamilies && /\S/.test(node.data)) {
|
|
959
|
+
addWordToFontFamilies(node.data);
|
|
960
|
+
}
|
|
961
|
+
return ownerDocument.createTextNode(node.data);
|
|
962
|
+
}
|
|
963
|
+
if (ownerDocument && ownerWindow && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) {
|
|
964
|
+
const cloned2 = await cloneElement(node, context);
|
|
965
|
+
if (context.isEnable("removeAbnormalAttributes")) {
|
|
966
|
+
const names = cloned2.getAttributeNames();
|
|
967
|
+
for (let len = names.length, i = 0; i < len; i++) {
|
|
968
|
+
const name = names[i];
|
|
969
|
+
if (!NORMAL_ATTRIBUTE_RE.test(name)) {
|
|
970
|
+
cloned2.removeAttribute(name);
|
|
971
|
+
}
|
|
480
972
|
}
|
|
481
|
-
|
|
482
|
-
|
|
973
|
+
}
|
|
974
|
+
const style = context.currentNodeStyle = copyCssStyles(node, cloned2, isRoot, context);
|
|
975
|
+
if (isRoot)
|
|
976
|
+
applyCssStyleWithOptions(cloned2, context);
|
|
977
|
+
let copyScrollbar = false;
|
|
978
|
+
if (context.isEnable("copyScrollbar")) {
|
|
979
|
+
const overflow = [
|
|
980
|
+
(_a2 = style.get("overflow-x")) == null ? void 0 : _a2[0],
|
|
981
|
+
(_b = style.get("overflow-y")) == null ? void 0 : _b[0]
|
|
982
|
+
];
|
|
983
|
+
copyScrollbar = overflow.includes("scroll") || (overflow.includes("auto") || overflow.includes("overlay")) && (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth);
|
|
984
|
+
}
|
|
985
|
+
const textTransform = (_c = style.get("text-transform")) == null ? void 0 : _c[0];
|
|
986
|
+
const families = splitFontFamily((_d = style.get("font-family")) == null ? void 0 : _d[0]);
|
|
987
|
+
const addWordToFontFamilies2 = families ? (word) => {
|
|
988
|
+
if (textTransform === "uppercase") {
|
|
989
|
+
word = word.toUpperCase();
|
|
990
|
+
} else if (textTransform === "lowercase") {
|
|
991
|
+
word = word.toLowerCase();
|
|
992
|
+
} else if (textTransform === "capitalize") {
|
|
993
|
+
word = word[0].toUpperCase() + word.substring(1);
|
|
483
994
|
}
|
|
995
|
+
families.forEach((family) => {
|
|
996
|
+
let fontFamily = fontFamilies.get(family);
|
|
997
|
+
if (!fontFamily) {
|
|
998
|
+
fontFamilies.set(family, fontFamily = /* @__PURE__ */ new Set());
|
|
999
|
+
}
|
|
1000
|
+
word.split("").forEach((text) => fontFamily.add(text));
|
|
1001
|
+
});
|
|
1002
|
+
} : void 0;
|
|
1003
|
+
copyPseudoClass(
|
|
1004
|
+
node,
|
|
1005
|
+
cloned2,
|
|
1006
|
+
copyScrollbar,
|
|
1007
|
+
context,
|
|
1008
|
+
addWordToFontFamilies2
|
|
1009
|
+
);
|
|
1010
|
+
copyInputValue(node, cloned2);
|
|
1011
|
+
if (!isVideoElement(node)) {
|
|
1012
|
+
await cloneChildNodes(
|
|
1013
|
+
node,
|
|
1014
|
+
cloned2,
|
|
1015
|
+
context,
|
|
1016
|
+
addWordToFontFamilies2
|
|
1017
|
+
);
|
|
484
1018
|
}
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
function shouldEmbed(url) {
|
|
488
|
-
return url.search(URL_REGEX) !== -1;
|
|
489
|
-
}
|
|
490
|
-
async function embedResources(cssText, baseUrl, options) {
|
|
491
|
-
if (!shouldEmbed(cssText)) {
|
|
492
|
-
return cssText;
|
|
1019
|
+
return cloned2;
|
|
493
1020
|
}
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
return
|
|
1021
|
+
const cloned = node.cloneNode(false);
|
|
1022
|
+
await cloneChildNodes(node, cloned, context);
|
|
1023
|
+
return cloned;
|
|
497
1024
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1025
|
+
function destroyContext(context) {
|
|
1026
|
+
context.ownerDocument = void 0;
|
|
1027
|
+
context.ownerWindow = void 0;
|
|
1028
|
+
context.svgStyleElement = void 0;
|
|
1029
|
+
context.svgDefsElement = void 0;
|
|
1030
|
+
context.svgStyles.clear();
|
|
1031
|
+
context.defaultComputedStyles.clear();
|
|
1032
|
+
if (context.sandbox) {
|
|
1033
|
+
try {
|
|
1034
|
+
context.sandbox.remove();
|
|
1035
|
+
} catch (err) {
|
|
1036
|
+
context.log.warn("Failed to destroyContext", err);
|
|
1037
|
+
}
|
|
1038
|
+
context.sandbox = void 0;
|
|
505
1039
|
}
|
|
506
|
-
|
|
1040
|
+
context.workers = [];
|
|
1041
|
+
context.fontFamilies.clear();
|
|
1042
|
+
context.fontCssTexts.clear();
|
|
1043
|
+
context.requests.clear();
|
|
1044
|
+
context.tasks = [];
|
|
507
1045
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
1046
|
+
function baseFetch(options) {
|
|
1047
|
+
const { url, timeout, responseType, ...requestInit } = options;
|
|
1048
|
+
const controller = new AbortController();
|
|
1049
|
+
const timer = timeout ? setTimeout(() => controller.abort(), timeout) : void 0;
|
|
1050
|
+
return fetch(url, { signal: controller.signal, ...requestInit }).then((response) => {
|
|
1051
|
+
if (!response.ok) {
|
|
1052
|
+
throw new Error("Failed fetch, not 2xx response", { cause: response });
|
|
1053
|
+
}
|
|
1054
|
+
switch (responseType) {
|
|
1055
|
+
case "arrayBuffer":
|
|
1056
|
+
return response.arrayBuffer();
|
|
1057
|
+
case "dataUrl":
|
|
1058
|
+
return response.blob().then(blobToDataUrl);
|
|
1059
|
+
case "text":
|
|
1060
|
+
default:
|
|
1061
|
+
return response.text();
|
|
1062
|
+
}
|
|
1063
|
+
}).finally(() => clearTimeout(timer));
|
|
511
1064
|
}
|
|
512
|
-
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
1065
|
+
function contextFetch(context, options) {
|
|
1066
|
+
const { url: rawUrl, requestType = "text", responseType = "text", imageDom } = options;
|
|
1067
|
+
let url = rawUrl;
|
|
1068
|
+
const {
|
|
1069
|
+
timeout,
|
|
1070
|
+
acceptOfImage,
|
|
1071
|
+
requests,
|
|
1072
|
+
fetchFn,
|
|
1073
|
+
fetch: {
|
|
1074
|
+
requestInit,
|
|
1075
|
+
bypassingCache,
|
|
1076
|
+
placeholderImage
|
|
1077
|
+
},
|
|
1078
|
+
font,
|
|
1079
|
+
workers,
|
|
1080
|
+
fontFamilies
|
|
1081
|
+
} = context;
|
|
1082
|
+
if (requestType === "image" && (IN_SAFARI || IN_FIREFOX)) {
|
|
1083
|
+
context.drawImageCount++;
|
|
516
1084
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
try {
|
|
523
|
-
resolve(options.onImageErrorHandler(...attributes));
|
|
524
|
-
} catch (error) {
|
|
525
|
-
reject(error);
|
|
1085
|
+
let request = requests.get(rawUrl);
|
|
1086
|
+
if (!request) {
|
|
1087
|
+
if (bypassingCache) {
|
|
1088
|
+
if (bypassingCache instanceof RegExp && bypassingCache.test(url)) {
|
|
1089
|
+
url += (/\?/.test(url) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
|
|
526
1090
|
}
|
|
527
|
-
} : reject;
|
|
528
|
-
const image = clonedNode;
|
|
529
|
-
if (image.decode) {
|
|
530
|
-
image.decode = resolve;
|
|
531
1091
|
}
|
|
532
|
-
|
|
533
|
-
|
|
1092
|
+
const canFontMinify = requestType.startsWith("font") && font && font.minify;
|
|
1093
|
+
const fontTexts = /* @__PURE__ */ new Set();
|
|
1094
|
+
if (canFontMinify) {
|
|
1095
|
+
const families = requestType.split(";")[1].split(",");
|
|
1096
|
+
families.forEach((family) => {
|
|
1097
|
+
if (!fontFamilies.has(family))
|
|
1098
|
+
return;
|
|
1099
|
+
fontFamilies.get(family).forEach((text) => fontTexts.add(text));
|
|
1100
|
+
});
|
|
534
1101
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
1102
|
+
const needFontMinify = canFontMinify && fontTexts.size;
|
|
1103
|
+
const baseFetchOptions = {
|
|
1104
|
+
url,
|
|
1105
|
+
timeout,
|
|
1106
|
+
responseType: needFontMinify ? "arrayBuffer" : responseType,
|
|
1107
|
+
headers: requestType === "image" ? { accept: acceptOfImage } : void 0,
|
|
1108
|
+
...requestInit
|
|
1109
|
+
};
|
|
1110
|
+
request = {
|
|
1111
|
+
type: requestType,
|
|
1112
|
+
resolve: void 0,
|
|
1113
|
+
reject: void 0,
|
|
1114
|
+
response: null
|
|
1115
|
+
};
|
|
1116
|
+
request.response = (async () => {
|
|
1117
|
+
if (fetchFn && requestType === "image") {
|
|
1118
|
+
const result = await fetchFn(rawUrl);
|
|
1119
|
+
if (result)
|
|
1120
|
+
return result;
|
|
1121
|
+
}
|
|
1122
|
+
if (!IN_SAFARI && rawUrl.startsWith("http") && workers.length) {
|
|
1123
|
+
return new Promise((resolve, reject) => {
|
|
1124
|
+
const worker = workers[requests.size & workers.length - 1];
|
|
1125
|
+
worker.postMessage({ rawUrl, ...baseFetchOptions });
|
|
1126
|
+
request.resolve = resolve;
|
|
1127
|
+
request.reject = reject;
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
return baseFetch(baseFetchOptions);
|
|
1131
|
+
})().catch((error) => {
|
|
1132
|
+
requests.delete(rawUrl);
|
|
1133
|
+
if (requestType === "image" && placeholderImage) {
|
|
1134
|
+
context.log.warn("Failed to fetch image base64, trying to use placeholder image", url);
|
|
1135
|
+
return typeof placeholderImage === "string" ? placeholderImage : placeholderImage(imageDom);
|
|
1136
|
+
}
|
|
1137
|
+
throw error;
|
|
1138
|
+
});
|
|
1139
|
+
requests.set(rawUrl, request);
|
|
1140
|
+
}
|
|
1141
|
+
return request.response;
|
|
1142
|
+
}
|
|
1143
|
+
async function replaceCssUrlToDataUrl(cssText, baseUrl, context, isImage) {
|
|
1144
|
+
if (!hasCssUrl(cssText))
|
|
1145
|
+
return cssText;
|
|
1146
|
+
for (const [rawUrl, url] of parseCssUrls(cssText, baseUrl)) {
|
|
1147
|
+
try {
|
|
1148
|
+
const dataUrl = await contextFetch(
|
|
1149
|
+
context,
|
|
1150
|
+
{
|
|
1151
|
+
url,
|
|
1152
|
+
requestType: isImage ? "image" : "text",
|
|
1153
|
+
responseType: "dataUrl"
|
|
1154
|
+
}
|
|
1155
|
+
);
|
|
1156
|
+
cssText = cssText.replace(toRE(rawUrl), `$1${dataUrl}$3`);
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
context.log.warn("Failed to fetch css data url", rawUrl, error);
|
|
540
1159
|
}
|
|
1160
|
+
}
|
|
1161
|
+
return cssText;
|
|
1162
|
+
}
|
|
1163
|
+
function hasCssUrl(cssText) {
|
|
1164
|
+
return /url\((['"]?)([^'"]+?)\1\)/.test(cssText);
|
|
1165
|
+
}
|
|
1166
|
+
const URL_RE = /url\((['"]?)([^'"]+?)\1\)/g;
|
|
1167
|
+
function parseCssUrls(cssText, baseUrl) {
|
|
1168
|
+
const result = [];
|
|
1169
|
+
cssText.replace(URL_RE, (raw, quotation, url) => {
|
|
1170
|
+
result.push([url, resolveUrl(url, baseUrl)]);
|
|
1171
|
+
return raw;
|
|
541
1172
|
});
|
|
1173
|
+
return result.filter(([url]) => !isDataUrl(url));
|
|
542
1174
|
}
|
|
543
|
-
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
await Promise.all(deferreds).then(() => clonedNode);
|
|
1175
|
+
function toRE(url) {
|
|
1176
|
+
const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
|
1177
|
+
return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
|
|
547
1178
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
1179
|
+
const properties = [
|
|
1180
|
+
"background-image",
|
|
1181
|
+
"border-image-source",
|
|
1182
|
+
"-webkit-border-image",
|
|
1183
|
+
"-webkit-mask-image",
|
|
1184
|
+
"list-style-image"
|
|
1185
|
+
];
|
|
1186
|
+
function embedCssStyleImage(style, context) {
|
|
1187
|
+
return properties.map((property) => {
|
|
1188
|
+
const value = style.getPropertyValue(property);
|
|
1189
|
+
if (!value || value === "none") {
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
if (IN_SAFARI || IN_FIREFOX) {
|
|
1193
|
+
context.drawImageCount++;
|
|
1194
|
+
}
|
|
1195
|
+
return replaceCssUrlToDataUrl(value, null, context, true).then((newValue) => {
|
|
1196
|
+
if (!newValue || value === newValue)
|
|
1197
|
+
return;
|
|
1198
|
+
style.setProperty(
|
|
1199
|
+
property,
|
|
1200
|
+
newValue,
|
|
1201
|
+
style.getPropertyPriority(property)
|
|
1202
|
+
);
|
|
1203
|
+
});
|
|
1204
|
+
}).filter(Boolean);
|
|
1205
|
+
}
|
|
1206
|
+
function embedImageElement(cloned, context) {
|
|
1207
|
+
if (isImageElement(cloned)) {
|
|
1208
|
+
const originalSrc = cloned.currentSrc || cloned.src;
|
|
1209
|
+
if (!isDataUrl(originalSrc)) {
|
|
1210
|
+
return [
|
|
1211
|
+
contextFetch(context, {
|
|
1212
|
+
url: originalSrc,
|
|
1213
|
+
imageDom: cloned,
|
|
1214
|
+
requestType: "image",
|
|
1215
|
+
responseType: "dataUrl"
|
|
1216
|
+
}).then((url) => {
|
|
1217
|
+
if (!url)
|
|
1218
|
+
return;
|
|
1219
|
+
cloned.srcset = "";
|
|
1220
|
+
cloned.dataset.originalSrc = originalSrc;
|
|
1221
|
+
cloned.src = url || "";
|
|
1222
|
+
})
|
|
1223
|
+
];
|
|
1224
|
+
}
|
|
1225
|
+
if (IN_SAFARI || IN_FIREFOX) {
|
|
1226
|
+
context.drawImageCount++;
|
|
1227
|
+
}
|
|
1228
|
+
} else if (isSVGElementNode(cloned) && !isDataUrl(cloned.href.baseVal)) {
|
|
1229
|
+
const originalSrc = cloned.href.baseVal;
|
|
1230
|
+
return [
|
|
1231
|
+
contextFetch(context, {
|
|
1232
|
+
url: originalSrc,
|
|
1233
|
+
imageDom: cloned,
|
|
1234
|
+
requestType: "image",
|
|
1235
|
+
responseType: "dataUrl"
|
|
1236
|
+
}).then((url) => {
|
|
1237
|
+
if (!url)
|
|
1238
|
+
return;
|
|
1239
|
+
cloned.dataset.originalSrc = originalSrc;
|
|
1240
|
+
cloned.href.baseVal = url || "";
|
|
1241
|
+
})
|
|
1242
|
+
];
|
|
553
1243
|
}
|
|
1244
|
+
return [];
|
|
554
1245
|
}
|
|
555
|
-
function
|
|
556
|
-
const {
|
|
557
|
-
|
|
558
|
-
|
|
1246
|
+
function embedSvgUse(cloned, context) {
|
|
1247
|
+
const { ownerDocument, svgDefsElement } = context;
|
|
1248
|
+
const href = cloned.getAttribute("href") ?? cloned.getAttribute("xlink:href");
|
|
1249
|
+
if (!href)
|
|
1250
|
+
return [];
|
|
1251
|
+
const [svgUrl, id] = href.split("#");
|
|
1252
|
+
if (id) {
|
|
1253
|
+
const query = `#${id}`;
|
|
1254
|
+
const definition = ownerDocument == null ? void 0 : ownerDocument.querySelector(`svg ${query}`);
|
|
1255
|
+
if (svgUrl) {
|
|
1256
|
+
cloned.setAttribute("href", query);
|
|
1257
|
+
}
|
|
1258
|
+
if (svgDefsElement == null ? void 0 : svgDefsElement.querySelector(query))
|
|
1259
|
+
return [];
|
|
1260
|
+
if (definition) {
|
|
1261
|
+
svgDefsElement == null ? void 0 : svgDefsElement.appendChild(definition.cloneNode(true));
|
|
1262
|
+
return [];
|
|
1263
|
+
} else if (svgUrl) {
|
|
1264
|
+
return [
|
|
1265
|
+
contextFetch(context, {
|
|
1266
|
+
url: svgUrl,
|
|
1267
|
+
responseType: "text"
|
|
1268
|
+
}).then((svgData) => {
|
|
1269
|
+
svgDefsElement == null ? void 0 : svgDefsElement.insertAdjacentHTML("beforeend", svgData);
|
|
1270
|
+
})
|
|
1271
|
+
];
|
|
1272
|
+
}
|
|
559
1273
|
}
|
|
560
|
-
|
|
561
|
-
|
|
1274
|
+
return [];
|
|
1275
|
+
}
|
|
1276
|
+
function embedNode(cloned, context) {
|
|
1277
|
+
const { tasks } = context;
|
|
1278
|
+
if (isElementNode(cloned)) {
|
|
1279
|
+
if (isImageElement(cloned) || isSVGImageElementNode(cloned)) {
|
|
1280
|
+
tasks.push(...embedImageElement(cloned, context));
|
|
1281
|
+
}
|
|
1282
|
+
if (isSVGUseElementNode(cloned)) {
|
|
1283
|
+
tasks.push(...embedSvgUse(cloned, context));
|
|
1284
|
+
}
|
|
562
1285
|
}
|
|
563
|
-
if (
|
|
564
|
-
style
|
|
1286
|
+
if (isHTMLElementNode(cloned)) {
|
|
1287
|
+
tasks.push(...embedCssStyleImage(cloned.style, context));
|
|
565
1288
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
1289
|
+
cloned.childNodes.forEach((child) => {
|
|
1290
|
+
embedNode(child, context);
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
async function embedWebFont(clone, context) {
|
|
1294
|
+
const {
|
|
1295
|
+
ownerDocument,
|
|
1296
|
+
svgStyleElement,
|
|
1297
|
+
fontFamilies,
|
|
1298
|
+
fontCssTexts,
|
|
1299
|
+
tasks,
|
|
1300
|
+
font
|
|
1301
|
+
} = context;
|
|
1302
|
+
if (!ownerDocument || !svgStyleElement || !fontFamilies.size) {
|
|
1303
|
+
return;
|
|
571
1304
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
return cache2;
|
|
585
|
-
}
|
|
586
|
-
async function embedFonts(data, options) {
|
|
587
|
-
let cssText = data.cssText;
|
|
588
|
-
const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
|
|
589
|
-
const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
|
|
590
|
-
const loadFonts = fontLocs.map(async (loc) => {
|
|
591
|
-
let url = loc.replace(regexUrl, "$1");
|
|
592
|
-
if (!url.startsWith("https://")) {
|
|
593
|
-
url = new URL(url, data.url).href;
|
|
594
|
-
}
|
|
595
|
-
return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
|
|
596
|
-
cssText = cssText.replace(loc, `url(${result})`);
|
|
597
|
-
return [loc, result];
|
|
1305
|
+
if (font && font.cssText) {
|
|
1306
|
+
const cssText = filterPreferredFormat(font.cssText, context);
|
|
1307
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
|
|
1308
|
+
`));
|
|
1309
|
+
} else {
|
|
1310
|
+
const styleSheets = Array.from(ownerDocument.styleSheets).filter((styleSheet) => {
|
|
1311
|
+
try {
|
|
1312
|
+
return "cssRules" in styleSheet && Boolean(styleSheet.cssRules.length);
|
|
1313
|
+
} catch (error) {
|
|
1314
|
+
context.log.warn(`Error while reading CSS rules from ${styleSheet.href}`, error);
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
598
1317
|
});
|
|
599
|
-
|
|
600
|
-
|
|
1318
|
+
await Promise.all(
|
|
1319
|
+
styleSheets.flatMap((styleSheet) => {
|
|
1320
|
+
return Array.from(styleSheet.cssRules).map(async (cssRule, index) => {
|
|
1321
|
+
if (isCSSImportRule(cssRule)) {
|
|
1322
|
+
let importIndex = index + 1;
|
|
1323
|
+
const baseUrl = cssRule.href;
|
|
1324
|
+
let cssText = "";
|
|
1325
|
+
try {
|
|
1326
|
+
cssText = await contextFetch(context, {
|
|
1327
|
+
url: baseUrl,
|
|
1328
|
+
requestType: "text",
|
|
1329
|
+
responseType: "text"
|
|
1330
|
+
});
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
context.log.warn(`Error fetch remote css import from ${baseUrl}`, error);
|
|
1333
|
+
}
|
|
1334
|
+
const replacedCssText = cssText.replace(
|
|
1335
|
+
URL_RE,
|
|
1336
|
+
(raw, quotation, url) => raw.replace(url, resolveUrl(url, baseUrl))
|
|
1337
|
+
);
|
|
1338
|
+
for (const rule of parseCss(replacedCssText)) {
|
|
1339
|
+
try {
|
|
1340
|
+
styleSheet.insertRule(
|
|
1341
|
+
rule,
|
|
1342
|
+
rule.startsWith("@import") ? importIndex += 1 : styleSheet.cssRules.length
|
|
1343
|
+
);
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
context.log.warn("Error inserting rule from remote css import", { rule, error });
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
})
|
|
1351
|
+
);
|
|
1352
|
+
const cssRules = styleSheets.flatMap((styleSheet) => Array.from(styleSheet.cssRules));
|
|
1353
|
+
cssRules.filter((cssRule) => {
|
|
1354
|
+
var _a2;
|
|
1355
|
+
return isCssFontFaceRule(cssRule) && hasCssUrl(cssRule.style.getPropertyValue("src")) && ((_a2 = splitFontFamily(cssRule.style.getPropertyValue("font-family"))) == null ? void 0 : _a2.some((val) => fontFamilies.has(val)));
|
|
1356
|
+
}).forEach((value) => {
|
|
1357
|
+
const rule = value;
|
|
1358
|
+
const cssText = fontCssTexts.get(rule.cssText);
|
|
1359
|
+
if (cssText) {
|
|
1360
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
|
|
1361
|
+
`));
|
|
1362
|
+
} else {
|
|
1363
|
+
tasks.push(
|
|
1364
|
+
replaceCssUrlToDataUrl(
|
|
1365
|
+
rule.cssText,
|
|
1366
|
+
rule.parentStyleSheet ? rule.parentStyleSheet.href : null,
|
|
1367
|
+
context
|
|
1368
|
+
).then((cssText2) => {
|
|
1369
|
+
cssText2 = filterPreferredFormat(cssText2, context);
|
|
1370
|
+
fontCssTexts.set(rule.cssText, cssText2);
|
|
1371
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText2}
|
|
1372
|
+
`));
|
|
1373
|
+
})
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
601
1378
|
}
|
|
602
|
-
|
|
603
|
-
|
|
1379
|
+
const COMMENTS_RE = /(\/\*[\s\S]*?\*\/)/g;
|
|
1380
|
+
const KEYFRAMES_RE = /((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi;
|
|
1381
|
+
function parseCss(source) {
|
|
1382
|
+
if (source == null)
|
|
604
1383
|
return [];
|
|
605
|
-
}
|
|
606
1384
|
const result = [];
|
|
607
|
-
|
|
608
|
-
let cssText = source.replace(commentsRegex, "");
|
|
609
|
-
const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
|
|
1385
|
+
let cssText = source.replace(COMMENTS_RE, "");
|
|
610
1386
|
while (true) {
|
|
611
|
-
const matches =
|
|
612
|
-
if (matches
|
|
1387
|
+
const matches = KEYFRAMES_RE.exec(cssText);
|
|
1388
|
+
if (!matches)
|
|
613
1389
|
break;
|
|
614
|
-
}
|
|
615
1390
|
result.push(matches[0]);
|
|
616
1391
|
}
|
|
617
|
-
cssText = cssText.replace(
|
|
618
|
-
const
|
|
619
|
-
const
|
|
620
|
-
|
|
1392
|
+
cssText = cssText.replace(KEYFRAMES_RE, "");
|
|
1393
|
+
const IMPORT_RE = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
|
|
1394
|
+
const UNIFIED_RE = new RegExp(
|
|
1395
|
+
// eslint-disable-next-line
|
|
1396
|
+
"((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})",
|
|
1397
|
+
"gi"
|
|
1398
|
+
);
|
|
621
1399
|
while (true) {
|
|
622
|
-
let matches =
|
|
623
|
-
if (matches
|
|
624
|
-
matches =
|
|
625
|
-
if (matches
|
|
1400
|
+
let matches = IMPORT_RE.exec(cssText);
|
|
1401
|
+
if (!matches) {
|
|
1402
|
+
matches = UNIFIED_RE.exec(cssText);
|
|
1403
|
+
if (!matches) {
|
|
626
1404
|
break;
|
|
627
1405
|
} else {
|
|
628
|
-
|
|
1406
|
+
IMPORT_RE.lastIndex = UNIFIED_RE.lastIndex;
|
|
629
1407
|
}
|
|
630
1408
|
} else {
|
|
631
|
-
|
|
1409
|
+
UNIFIED_RE.lastIndex = IMPORT_RE.lastIndex;
|
|
632
1410
|
}
|
|
633
1411
|
result.push(matches[0]);
|
|
634
1412
|
}
|
|
635
1413
|
return result;
|
|
636
1414
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
1415
|
+
const URL_WITH_FORMAT_RE = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
|
|
1416
|
+
const FONT_SRC_RE = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
|
|
1417
|
+
function filterPreferredFormat(str, context) {
|
|
1418
|
+
const { font } = context;
|
|
1419
|
+
const preferredFormat = font ? font == null ? void 0 : font.preferredFormat : void 0;
|
|
1420
|
+
return preferredFormat ? str.replace(FONT_SRC_RE, (match) => {
|
|
1421
|
+
while (true) {
|
|
1422
|
+
const [src, , format] = URL_WITH_FORMAT_RE.exec(match) || [];
|
|
1423
|
+
if (!format)
|
|
1424
|
+
return "";
|
|
1425
|
+
if (format === preferredFormat)
|
|
1426
|
+
return `src: ${src};`;
|
|
1427
|
+
}
|
|
1428
|
+
}) : str;
|
|
1429
|
+
}
|
|
1430
|
+
async function domToForeignObjectSvg(node, options) {
|
|
1431
|
+
const context = await orCreateContext(node, options);
|
|
1432
|
+
if (isElementNode(context.node) && isSVGElementNode(context.node))
|
|
1433
|
+
return context.node;
|
|
1434
|
+
const {
|
|
1435
|
+
ownerDocument,
|
|
1436
|
+
log: log2,
|
|
1437
|
+
tasks,
|
|
1438
|
+
svgStyleElement,
|
|
1439
|
+
svgDefsElement,
|
|
1440
|
+
svgStyles,
|
|
1441
|
+
font,
|
|
1442
|
+
progress,
|
|
1443
|
+
autoDestruct,
|
|
1444
|
+
onCloneNode,
|
|
1445
|
+
onEmbedNode,
|
|
1446
|
+
onCreateForeignObjectSvg
|
|
1447
|
+
} = context;
|
|
1448
|
+
log2.time("clone node");
|
|
1449
|
+
const clone = await cloneNode(context.node, context, true);
|
|
1450
|
+
if (svgStyleElement && ownerDocument) {
|
|
1451
|
+
let allCssText = "";
|
|
1452
|
+
svgStyles.forEach((klasses, cssText) => {
|
|
1453
|
+
allCssText += `${klasses.join(",\n")} {
|
|
1454
|
+
${cssText}
|
|
1455
|
+
}
|
|
1456
|
+
`;
|
|
1457
|
+
});
|
|
1458
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(allCssText));
|
|
1459
|
+
}
|
|
1460
|
+
log2.timeEnd("clone node");
|
|
1461
|
+
await (onCloneNode == null ? void 0 : onCloneNode(clone));
|
|
1462
|
+
if (font !== false && isElementNode(clone)) {
|
|
1463
|
+
log2.time("embed web font");
|
|
1464
|
+
await embedWebFont(clone, context);
|
|
1465
|
+
log2.timeEnd("embed web font");
|
|
1466
|
+
}
|
|
1467
|
+
log2.time("embed node");
|
|
1468
|
+
embedNode(clone, context);
|
|
1469
|
+
const count = tasks.length;
|
|
1470
|
+
let current = 0;
|
|
1471
|
+
const runTask = async () => {
|
|
1472
|
+
while (true) {
|
|
1473
|
+
const task = tasks.pop();
|
|
1474
|
+
if (!task)
|
|
1475
|
+
break;
|
|
642
1476
|
try {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const url = item.href;
|
|
647
|
-
const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
648
|
-
try {
|
|
649
|
-
sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
|
|
650
|
-
} catch (error) {
|
|
651
|
-
console.error("Error inserting rule from remote css", {
|
|
652
|
-
rule,
|
|
653
|
-
error
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
|
-
})).catch((e) => {
|
|
657
|
-
console.error("Error loading remote css", e.toString());
|
|
658
|
-
});
|
|
659
|
-
deferreds.push(deferred);
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
} catch (e) {
|
|
663
|
-
const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
|
|
664
|
-
if (sheet.href != null) {
|
|
665
|
-
deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
666
|
-
inline.insertRule(rule, inline.cssRules.length);
|
|
667
|
-
})).catch((err) => {
|
|
668
|
-
console.error("Error loading remote stylesheet", err);
|
|
669
|
-
}));
|
|
670
|
-
}
|
|
671
|
-
console.error("Error inlining remote css file", e);
|
|
1477
|
+
await task;
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
context.log.warn("Failed to run task", error);
|
|
672
1480
|
}
|
|
1481
|
+
progress == null ? void 0 : progress(++current, count);
|
|
673
1482
|
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
});
|
|
687
|
-
return ret;
|
|
688
|
-
});
|
|
1483
|
+
};
|
|
1484
|
+
progress == null ? void 0 : progress(current, count);
|
|
1485
|
+
await Promise.all([...Array.from({ length: 4 })].map(runTask));
|
|
1486
|
+
log2.timeEnd("embed node");
|
|
1487
|
+
await (onEmbedNode == null ? void 0 : onEmbedNode(clone));
|
|
1488
|
+
const svg = createForeignObjectSvg(clone, context);
|
|
1489
|
+
svgDefsElement && svg.insertBefore(svgDefsElement, svg.children[0]);
|
|
1490
|
+
svgStyleElement && svg.insertBefore(svgStyleElement, svg.children[0]);
|
|
1491
|
+
autoDestruct && destroyContext(context);
|
|
1492
|
+
await (onCreateForeignObjectSvg == null ? void 0 : onCreateForeignObjectSvg(svg));
|
|
1493
|
+
return svg;
|
|
689
1494
|
}
|
|
690
|
-
function
|
|
691
|
-
|
|
1495
|
+
function createForeignObjectSvg(clone, context) {
|
|
1496
|
+
const { width, height } = context;
|
|
1497
|
+
const svg = createSvg(width, height, clone.ownerDocument);
|
|
1498
|
+
const foreignObject = svg.ownerDocument.createElementNS(svg.namespaceURI, "foreignObject");
|
|
1499
|
+
foreignObject.setAttributeNS(null, "x", "0%");
|
|
1500
|
+
foreignObject.setAttributeNS(null, "y", "0%");
|
|
1501
|
+
foreignObject.setAttributeNS(null, "width", "100%");
|
|
1502
|
+
foreignObject.setAttributeNS(null, "height", "100%");
|
|
1503
|
+
foreignObject.append(clone);
|
|
1504
|
+
svg.appendChild(foreignObject);
|
|
1505
|
+
return svg;
|
|
692
1506
|
}
|
|
693
|
-
async function
|
|
694
|
-
|
|
695
|
-
|
|
1507
|
+
async function domToCanvas(node, options) {
|
|
1508
|
+
var _a2;
|
|
1509
|
+
const context = await orCreateContext(node, options);
|
|
1510
|
+
const svg = await domToForeignObjectSvg(context);
|
|
1511
|
+
const dataUrl = svgToDataUrl(svg, context.isEnable("removeControlCharacter"));
|
|
1512
|
+
if (!context.autoDestruct) {
|
|
1513
|
+
context.svgStyleElement = createStyleElement(context.ownerDocument);
|
|
1514
|
+
context.svgDefsElement = (_a2 = context.ownerDocument) == null ? void 0 : _a2.createElementNS(XMLNS, "defs");
|
|
1515
|
+
context.svgStyles.clear();
|
|
696
1516
|
}
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
return getWebFontRules(cssRules);
|
|
700
|
-
}
|
|
701
|
-
function normalizeFontFamily(font) {
|
|
702
|
-
return font.trim().replace(/["']/g, "");
|
|
1517
|
+
const image = createImage(dataUrl, svg.ownerDocument);
|
|
1518
|
+
return await imageToCanvas(image, context);
|
|
703
1519
|
}
|
|
704
|
-
function
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1520
|
+
async function domToDataUrl(node, options) {
|
|
1521
|
+
const context = await orCreateContext(node, options);
|
|
1522
|
+
const { log: log2, quality, type, dpi } = context;
|
|
1523
|
+
const canvas = await domToCanvas(context);
|
|
1524
|
+
log2.time("canvas to data url");
|
|
1525
|
+
let dataUrl = canvas.toDataURL(type, quality);
|
|
1526
|
+
if (["image/png", "image/jpeg"].includes(type) && dpi && SUPPORT_ATOB && SUPPORT_BTOA) {
|
|
1527
|
+
const [format, body] = dataUrl.split(",");
|
|
1528
|
+
let headerLength = 0;
|
|
1529
|
+
let overwritepHYs = false;
|
|
1530
|
+
if (type === "image/png") {
|
|
1531
|
+
const b64Index = detectPhysChunkFromDataUrl(body);
|
|
1532
|
+
if (b64Index >= 0) {
|
|
1533
|
+
headerLength = Math.ceil((b64Index + 28) / 3) * 4;
|
|
1534
|
+
overwritepHYs = true;
|
|
1535
|
+
} else {
|
|
1536
|
+
headerLength = 33 / 3 * 4;
|
|
714
1537
|
}
|
|
715
|
-
})
|
|
1538
|
+
} else if (type === "image/jpeg") {
|
|
1539
|
+
headerLength = 18 / 3 * 4;
|
|
1540
|
+
}
|
|
1541
|
+
const stringHeader = body.substring(0, headerLength);
|
|
1542
|
+
const restOfData = body.substring(headerLength);
|
|
1543
|
+
const headerBytes = window.atob(stringHeader);
|
|
1544
|
+
const uint8Array = new Uint8Array(headerBytes.length);
|
|
1545
|
+
for (let i = 0; i < uint8Array.length; i++) {
|
|
1546
|
+
uint8Array[i] = headerBytes.charCodeAt(i);
|
|
1547
|
+
}
|
|
1548
|
+
const finalArray = type === "image/png" ? changePngDpi(uint8Array, dpi, overwritepHYs) : changeJpegDpi(uint8Array, dpi);
|
|
1549
|
+
const base64Header = window.btoa(String.fromCharCode(...finalArray));
|
|
1550
|
+
dataUrl = [format, ",", base64Header, restOfData].join("");
|
|
716
1551
|
}
|
|
717
|
-
|
|
718
|
-
return
|
|
719
|
-
}
|
|
720
|
-
async function getWebFontCSS(node, options) {
|
|
721
|
-
const rules = await parseWebFontRules(node, options);
|
|
722
|
-
const usedFonts = getUsedFonts(node);
|
|
723
|
-
const cssTexts = await Promise.all(rules.filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily))).map((rule) => {
|
|
724
|
-
const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
|
|
725
|
-
return embedResources(rule.cssText, baseUrl, options);
|
|
726
|
-
}));
|
|
727
|
-
return cssTexts.join("\n");
|
|
728
|
-
}
|
|
729
|
-
async function embedWebFonts(clonedNode, options) {
|
|
730
|
-
const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
|
|
731
|
-
if (cssText) {
|
|
732
|
-
const styleNode = document.createElement("style");
|
|
733
|
-
const sytleContent = document.createTextNode(cssText);
|
|
734
|
-
styleNode.appendChild(sytleContent);
|
|
735
|
-
if (clonedNode.firstChild) {
|
|
736
|
-
clonedNode.insertBefore(styleNode, clonedNode.firstChild);
|
|
737
|
-
} else {
|
|
738
|
-
clonedNode.appendChild(styleNode);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
async function toSvg(node, options = {}) {
|
|
743
|
-
const { width, height } = getImageSize(node, options);
|
|
744
|
-
const clonedNode = await cloneNode(node, options, true);
|
|
745
|
-
await embedWebFonts(clonedNode, options);
|
|
746
|
-
await embedImages(clonedNode, options);
|
|
747
|
-
applyStyle(clonedNode, options);
|
|
748
|
-
const datauri = await nodeToDataURL(clonedNode, width, height);
|
|
749
|
-
return datauri;
|
|
750
|
-
}
|
|
751
|
-
async function toCanvas(node, options = {}) {
|
|
752
|
-
const { width, height } = getImageSize(node, options);
|
|
753
|
-
const svg = await toSvg(node, options);
|
|
754
|
-
const img = await createImage(svg);
|
|
755
|
-
const canvas = document.createElement("canvas");
|
|
756
|
-
const context = canvas.getContext("2d");
|
|
757
|
-
const ratio = options.pixelRatio || getPixelRatio();
|
|
758
|
-
const canvasWidth = options.canvasWidth || width;
|
|
759
|
-
const canvasHeight = options.canvasHeight || height;
|
|
760
|
-
canvas.width = canvasWidth * ratio;
|
|
761
|
-
canvas.height = canvasHeight * ratio;
|
|
762
|
-
if (!options.skipAutoScale) {
|
|
763
|
-
checkCanvasDimensions(canvas);
|
|
764
|
-
}
|
|
765
|
-
canvas.style.width = `${canvasWidth}`;
|
|
766
|
-
canvas.style.height = `${canvasHeight}`;
|
|
767
|
-
if (options.backgroundColor) {
|
|
768
|
-
context.fillStyle = options.backgroundColor;
|
|
769
|
-
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
770
|
-
}
|
|
771
|
-
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
772
|
-
return canvas;
|
|
1552
|
+
log2.timeEnd("canvas to data url");
|
|
1553
|
+
return dataUrl;
|
|
773
1554
|
}
|
|
774
|
-
async function
|
|
775
|
-
const
|
|
776
|
-
|
|
1555
|
+
async function domToSvg(node, options) {
|
|
1556
|
+
const context = await orCreateContext(node, options);
|
|
1557
|
+
const { width, height, ownerDocument } = context;
|
|
1558
|
+
const dataUrl = await domToDataUrl(context);
|
|
1559
|
+
const svg = createSvg(width, height, ownerDocument);
|
|
1560
|
+
const svgImage = svg.ownerDocument.createElementNS(svg.namespaceURI, "image");
|
|
1561
|
+
svgImage.setAttributeNS(null, "href", dataUrl);
|
|
1562
|
+
svgImage.setAttributeNS(null, "height", "100%");
|
|
1563
|
+
svgImage.setAttributeNS(null, "width", "100%");
|
|
1564
|
+
svg.appendChild(svgImage);
|
|
1565
|
+
return svgToDataUrl(svg, context.isEnable("removeControlCharacter"));
|
|
777
1566
|
}
|
|
778
|
-
async function
|
|
779
|
-
|
|
780
|
-
|
|
1567
|
+
async function domToJpeg(node, options) {
|
|
1568
|
+
return domToDataUrl(
|
|
1569
|
+
await orCreateContext(node, { ...options, type: "image/jpeg" })
|
|
1570
|
+
);
|
|
781
1571
|
}
|
|
782
|
-
function
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
const match = proposedFilename.match(numberPattern);
|
|
792
|
-
if (match) {
|
|
793
|
-
const baseFilename = proposedFilename.replace(numberPattern, "");
|
|
794
|
-
let counter = parseInt(match[1], 10);
|
|
795
|
-
while (filenames.includes(`${baseFilename}-${counter}`)) {
|
|
796
|
-
counter++;
|
|
797
|
-
}
|
|
798
|
-
const newFilename = `${baseFilename}-${counter}`;
|
|
799
|
-
filenames.push(newFilename);
|
|
800
|
-
return newFilename;
|
|
801
|
-
} else {
|
|
802
|
-
let counter = 2;
|
|
803
|
-
while (filenames.includes(`${proposedFilename}-${counter}`)) {
|
|
804
|
-
counter++;
|
|
805
|
-
}
|
|
806
|
-
const newFilename = `${proposedFilename}-${counter}`;
|
|
807
|
-
filenames.push(newFilename);
|
|
808
|
-
return newFilename;
|
|
809
|
-
}
|
|
1572
|
+
async function domToPng(node, options) {
|
|
1573
|
+
return domToDataUrl(
|
|
1574
|
+
await orCreateContext(node, { ...options, type: "image/png" })
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
async function domToWebp(node, options) {
|
|
1578
|
+
return domToDataUrl(
|
|
1579
|
+
await orCreateContext(node, { ...options, type: "image/webp" })
|
|
1580
|
+
);
|
|
810
1581
|
}
|
|
811
1582
|
async function captureElement(element, imageOptions, filenames) {
|
|
812
1583
|
try {
|
|
@@ -815,7 +1586,7 @@
|
|
|
815
1586
|
// Ensure quality is a number
|
|
816
1587
|
quality: imageOptions.quality,
|
|
817
1588
|
// Ensure scale is a number
|
|
818
|
-
|
|
1589
|
+
scale: imageOptions.scale,
|
|
819
1590
|
// Ignores elements with data-ignore-capture attribute
|
|
820
1591
|
filter
|
|
821
1592
|
};
|
|
@@ -829,13 +1600,16 @@
|
|
|
829
1600
|
}
|
|
830
1601
|
switch (imageOptions.format) {
|
|
831
1602
|
case "jpg":
|
|
832
|
-
dataURL = await
|
|
1603
|
+
dataURL = await domToJpeg(element, htmlToImageOptions);
|
|
833
1604
|
break;
|
|
834
1605
|
case "png":
|
|
835
|
-
dataURL = await
|
|
1606
|
+
dataURL = await domToPng(element, htmlToImageOptions);
|
|
836
1607
|
break;
|
|
837
1608
|
case "svg":
|
|
838
|
-
dataURL = await
|
|
1609
|
+
dataURL = await domToSvg(element, htmlToImageOptions);
|
|
1610
|
+
break;
|
|
1611
|
+
case "webp":
|
|
1612
|
+
dataURL = await domToWebp(element, htmlToImageOptions);
|
|
839
1613
|
break;
|
|
840
1614
|
}
|
|
841
1615
|
if (cleanUpBackground) {
|
|
@@ -986,15 +1760,15 @@
|
|
|
986
1760
|
var jszip_min = { exports: {} };
|
|
987
1761
|
/*!
|
|
988
1762
|
|
|
989
|
-
|
|
990
|
-
|
|
1763
|
+
JSZip v3.10.1 - A JavaScript class for generating and reading zip files
|
|
1764
|
+
<http://stuartk.com/jszip>
|
|
991
1765
|
|
|
992
|
-
|
|
993
|
-
|
|
1766
|
+
(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
|
|
1767
|
+
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.
|
|
994
1768
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1769
|
+
JSZip uses the library pako released under the MIT license :
|
|
1770
|
+
https://github.com/nodeca/pako/blob/main/LICENSE
|
|
1771
|
+
*/
|
|
998
1772
|
(function(module2, exports3) {
|
|
999
1773
|
!function(e) {
|
|
1000
1774
|
module2.exports = e();
|