Skip to content

Commit 49f8ac6

Browse files
author
Elliot Shepherd
committed
add options.crossOrigin
1 parent 9a7be15 commit 49f8ac6

7 files changed

Lines changed: 50 additions & 20 deletions

File tree

README.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,34 @@ Returns an object with reusable export methods:
122122
123123
All capture methods accept an `options` object:
124124
125-
| Option | Type | Default | Description |
126-
| ----------------- | ------- | -------- | ------------------------------------------ |
127-
| `compress` | boolean | `true` | Removes redundant styles |
128-
| `fast` | boolean | `true` | Skips idle delay for faster results |
129-
| `embedFonts` | boolean | `false` | Inlines fonts (icon fonts always embedded) |
130-
| `scale` | number | `1` | Output scale multiplier |
131-
| `backgroundColor` | string | `"#fff"` | Fallback color for JPG/WebP |
132-
| `quality` | number | `1` | Quality for JPG/WebP (0 to 1) |
125+
| Option | Type | Default | Description |
126+
| ----------------- | -------- | -------- | ------------------------------------------ |
127+
| `compress` | boolean | `true` | Removes redundant styles |
128+
| `fast` | boolean | `true` | Skips idle delay for faster results |
129+
| `embedFonts` | boolean | `false` | Inlines fonts (icon fonts always embedded) |
130+
| `scale` | number | `1` | Output scale multiplier |
131+
| `backgroundColor` | string | `"#fff"` | Fallback color for JPG/WebP |
132+
| `quality` | number | `1` | Quality for JPG/WebP (0 to 1) |
133+
| `crossOrigin` | function | - | Function to determine CORS mode per image URL |
134+
135+
### Cross-Origin Images
136+
137+
By default, snapDOM loads images with `crossOrigin="anonymous"`. You can customize this behavior using the `crossOrigin` option:
138+
139+
```js
140+
const result = await snapdom(element, {
141+
crossOrigin: (url) => {
142+
// Use credentials for same-origin images
143+
if (url.startsWith(window.location.origin)) {
144+
return "use-credentials";
145+
}
146+
// Use anonymous for cross-origin images
147+
return "anonymous";
148+
}
149+
});
150+
```
151+
152+
This is useful when your images require authentication or when dealing with credentialed requests.
133153
134154
### Download options
135155
@@ -164,6 +184,7 @@ import { snapdom, preCache } from './snapdom.mjs';
164184
165185
* `embedFonts` *(boolean, default: true)* — Inlines non-icon fonts during preload.
166186
* `reset` *(boolean, default: false)* — Clears all existing internal caches.
187+
* `crossOrigin` *(function)* — Function to determine CORS mode per image URL during preload.
167188
168189
169190
## Features
@@ -177,7 +198,7 @@ import { snapdom, preCache } from './snapdom.mjs';
177198
178199
## Limitations
179200
180-
* External images must be CORS-accessible
201+
* External images must be CORS-accessible (use `crossOrigin` option for credentialed requests)
181202
* Iframes are not supported
182203
* When WebP format is used on Safari, it will fallback to PNG rendering.
183204

src/api/preCache.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ import { imageCache, bgCache, resourceCache, baseCSSCache, computedStyleCache }
1818
* @param {boolean} [options.embedFonts=true] - Whether to embed custom fonts
1919
* @param {boolean} [options.reset=false] - Whether to clear all caches before pre-caching
2020
* @param {boolean} [options.preWarm=true] - Whether to pre-cache common tag styles
21+
* @param {Function} [options.crossOrigin] - Function that returns CORS mode for each image URL
2122
* @returns {Promise<void>} Resolves when all resources are pre-cached
2223
*/
2324

2425
export async function preCache(root = document, options = {}) {
25-
const { embedFonts = true, reset = false, preWarm = true } = options;
26+
const { embedFonts = true, reset = false, crossOrigin: crossOriginFn } = options;
2627
if (reset) {
2728
imageCache.clear();
2829
bgCache.clear();
@@ -45,8 +46,9 @@ export async function preCache(root = document, options = {}) {
4546
for (const img of imgEls) {
4647
const src = img.src;
4748
if (!imageCache.has(src)) {
49+
const crossOrigin = crossOriginFn ? crossOriginFn(src) : "anonymous";
4850
promises.push(
49-
fetchImage(src)
51+
fetchImage(src, 3000, crossOrigin)
5052
.then(dataURL => imageCache.set(src, dataURL))
5153
.catch(() => {})
5254
);
@@ -56,8 +58,9 @@ export async function preCache(root = document, options = {}) {
5658
const bg = getComputedStyle(el).backgroundImage;
5759
const url = extractURL(bg);
5860
if (url && !bgCache.has(url)) {
61+
const crossOrigin = crossOriginFn ? crossOriginFn(url) : "anonymous";
5962
promises.push(
60-
fetchImage(url)
63+
fetchImage(url, 3000, crossOrigin)
6164
.then(dataURL => bgCache.set(url, dataURL))
6265
.catch(() => {})
6366
);

src/core/capture.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ export async function captureDOM(element, options = {}) {
3434
({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
3535
await new Promise((resolve) => {
3636
idle(async () => {
37-
await inlineImages(clone);
37+
await inlineImages(clone, options);
3838
resolve();
3939
}, { fast });
4040
});
4141
await new Promise((resolve) => {
4242
idle(async () => {
43-
await inlineBackgroundImages(element, clone, styleCache);
43+
await inlineBackgroundImages(element, clone, styleCache, options);
4444
resolve();
4545
}, { fast });
4646
});

src/modules/background.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { bgCache } from '../core/cache.js'
1212
* @param {Element} source - Original element
1313
* @param {Element} clone - Cloned element
1414
* @param {WeakMap} styleCache - Cache of computed styles
15+
* @param {Object} [options={}] - Options for image processing
1516
* @returns {Promise<void>} Promise that resolves when all background images are processed
1617
*/
17-
export async function inlineBackgroundImages(source, clone, styleCache) {
18+
export async function inlineBackgroundImages(source, clone, styleCache, options = {}) {
1819
const queue = [[source, clone]];
1920
while (queue.length) {
2021
const [srcNode, cloneNode] = queue.shift();
@@ -29,7 +30,8 @@ export async function inlineBackgroundImages(source, clone, styleCache) {
2930
if (bgCache.has(bgUrl)) {
3031
dataUrl = bgCache.get(bgUrl);
3132
} else {
32-
dataUrl = await fetchImage(bgUrl);
33+
const crossOrigin = options.crossOrigin ? options.crossOrigin(bgUrl) : "anonymous";
34+
dataUrl = await fetchImage(bgUrl, 3000, crossOrigin);
3335
bgCache.set(bgUrl, dataUrl);
3436
}
3537
cloneNode.style.backgroundImage = `url(${dataUrl})`;

src/modules/images.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import { fetchImage } from '../utils/helpers.js';
99
* Converts all <img> elements in the clone to data URLs or replaces them with placeholders if loading fails.
1010
*
1111
* @param {Element} clone - Clone of the original element
12+
* @param {Object} [options={}] - Options for image processing
1213
* @returns {Promise<void>} Promise that resolves when all images are processed
1314
*/
14-
export async function inlineImages(clone) {
15+
export async function inlineImages(clone, options = {}) {
1516
const imgs = Array.from(clone.querySelectorAll("img"));
1617
const processImg = async (img) => {
1718
const src = img.src;
1819
try {
19-
const dataUrl = await fetchImage(src);
20+
const crossOrigin = options.crossOrigin ? options.crossOrigin(src) : "anonymous";
21+
const dataUrl = await fetchImage(src, 3000, crossOrigin);
2022
img.src = dataUrl;
2123
if (!img.width) img.width = img.naturalWidth || 100;
2224
if (!img.height) img.height = img.naturalHeight || 100;

src/utils/helpers.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export function isIconFont(familyOrUrl) {
106106
* @param {number} [timeout=3000]
107107
* @return {*}
108108
*/
109-
export function fetchImage(src, timeout = 3000) {
109+
export function fetchImage(src, timeout = 3000, crossOrigin = "anonymous") {
110110

111111
if (imageCache.has(src)) {
112112
return Promise.resolve(imageCache.get(src));
@@ -118,7 +118,7 @@ export function fetchImage(src, timeout = 3000) {
118118
}, timeout);
119119

120120
const image = new Image();
121-
image.crossOrigin = "anonymous";
121+
image.crossOrigin = crossOrigin;
122122

123123
image.onload = async () => {
124124
clearTimeout(timeoutId);

types/snapdom.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ declare module "@zumer/snapdom" {
99
filename?: string;
1010
dpr?: number;
1111
quality?: number;
12+
crossOrigin?: (url: string) => "anonymous" | "use-credentials";
1213
}
1314

1415
export interface SnapResult {
@@ -51,6 +52,7 @@ declare module "@zumer/snapdom" {
5152
root?: Document | HTMLElement,
5253
options?: {
5354
embedFonts?: boolean;
55+
crossOrigin?: (url: string) => "anonymous" | "use-credentials";
5456
}
5557
): Promise<void>;
5658
}

0 commit comments

Comments
 (0)