105 lines
2.8 KiB
TypeScript
105 lines
2.8 KiB
TypeScript
|
|
import { Options } from './types'
|
||
|
|
import { embedResources } from './embed-resources'
|
||
|
|
import { toArray, isInstanceOfElement } from './util'
|
||
|
|
import { isDataUrl, resourceToDataURL } from './dataurl'
|
||
|
|
import { getMimeType } from './mimes'
|
||
|
|
|
||
|
|
async function embedProp(
|
||
|
|
propName: string,
|
||
|
|
node: HTMLElement,
|
||
|
|
options: Options,
|
||
|
|
) {
|
||
|
|
const propValue = node.style?.getPropertyValue(propName)
|
||
|
|
if (propValue) {
|
||
|
|
const cssString = await embedResources(propValue, null, options)
|
||
|
|
node.style.setProperty(
|
||
|
|
propName,
|
||
|
|
cssString,
|
||
|
|
node.style.getPropertyPriority(propName),
|
||
|
|
)
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
async function embedBackground<T extends HTMLElement>(
|
||
|
|
clonedNode: T,
|
||
|
|
options: Options,
|
||
|
|
) {
|
||
|
|
;(await embedProp('background', clonedNode, options)) ||
|
||
|
|
(await embedProp('background-image', clonedNode, options))
|
||
|
|
;(await embedProp('mask', clonedNode, options)) ||
|
||
|
|
(await embedProp('-webkit-mask', clonedNode, options)) ||
|
||
|
|
(await embedProp('mask-image', clonedNode, options)) ||
|
||
|
|
(await embedProp('-webkit-mask-image', clonedNode, options))
|
||
|
|
}
|
||
|
|
|
||
|
|
async function embedImageNode<T extends HTMLElement | SVGImageElement>(
|
||
|
|
clonedNode: T,
|
||
|
|
options: Options,
|
||
|
|
) {
|
||
|
|
const isImageElement = isInstanceOfElement(clonedNode, HTMLImageElement)
|
||
|
|
|
||
|
|
if (
|
||
|
|
!(isImageElement && !isDataUrl(clonedNode.src)) &&
|
||
|
|
!(
|
||
|
|
isInstanceOfElement(clonedNode, SVGImageElement) &&
|
||
|
|
!isDataUrl(clonedNode.href.baseVal)
|
||
|
|
)
|
||
|
|
) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const url = isImageElement ? clonedNode.src : clonedNode.href.baseVal
|
||
|
|
|
||
|
|
const dataURL = await resourceToDataURL(url, getMimeType(url), options)
|
||
|
|
await new Promise((resolve, reject) => {
|
||
|
|
clonedNode.onload = resolve
|
||
|
|
clonedNode.onerror = options.onImageErrorHandler
|
||
|
|
? (...attributes) => {
|
||
|
|
try {
|
||
|
|
resolve(options.onImageErrorHandler!(...attributes))
|
||
|
|
} catch (error) {
|
||
|
|
reject(error)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
: reject
|
||
|
|
|
||
|
|
const image = clonedNode as HTMLImageElement
|
||
|
|
if (image.decode) {
|
||
|
|
image.decode = resolve as any
|
||
|
|
}
|
||
|
|
|
||
|
|
if (image.loading === 'lazy') {
|
||
|
|
image.loading = 'eager'
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isImageElement) {
|
||
|
|
clonedNode.srcset = ''
|
||
|
|
clonedNode.src = dataURL
|
||
|
|
} else {
|
||
|
|
clonedNode.href.baseVal = dataURL
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
async function embedChildren<T extends HTMLElement>(
|
||
|
|
clonedNode: T,
|
||
|
|
options: Options,
|
||
|
|
) {
|
||
|
|
const children = toArray<HTMLElement>(clonedNode.childNodes)
|
||
|
|
const deferreds = children.map((child) => embedImages(child, options))
|
||
|
|
await Promise.all(deferreds).then(() => clonedNode)
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function embedImages<T extends HTMLElement>(
|
||
|
|
clonedNode: T,
|
||
|
|
options: Options,
|
||
|
|
) {
|
||
|
|
if (isInstanceOfElement(clonedNode, Element)) {
|
||
|
|
await embedBackground(clonedNode, options)
|
||
|
|
await embedImageNode(clonedNode, options)
|
||
|
|
await embedChildren(clonedNode, options)
|
||
|
|
}
|
||
|
|
}
|