Files
ArtPlayer/packages/artplayer-plugin-document-pip/src/projection.ts
T

62 lines
2.1 KiB
TypeScript

import { releaseAll } from './resources'
import { copyStyles } from './styles'
export function createProjection(player: HTMLElement, target: Document, text: string | undefined) {
const source = player.ownerDocument
const parent = player.parentNode
const next = player.nextSibling
if (!parent)
throw new Error('Document Picture-in-Picture requires a mounted player')
const placeholder = source.createElement('div')
placeholder.className = 'artplayer-document-pip-placeholder'
placeholder.style.cssText = 'display:flex;justify-content:center;align-items:center;width:100%;height:100%;'
placeholder.textContent = text ?? ''
const root = target.createElement('div')
root.id = '__art_dpip_root'
const nodes: HTMLElement[] = []
let closed = false
const restore = () => {
// Insertion adopts atomically; never detach the player before validating the anchor.
const anchor = placeholder.parentNode === parent ? placeholder : next?.parentNode === parent ? next : null
if (player.parentNode !== parent || player.nextSibling !== anchor) {
try {
parent.insertBefore(player, anchor)
}
catch (error) {
// Keep a recoverable node in the original document if its parent rejects insertion.
source.body.appendChild(player)
throw error
}
}
}
const cleanup = () => {
const errors = releaseAll([restore, () => placeholder.remove(), ...nodes.splice(0).map(node => () => node.remove()), () => root.remove()])
if (errors.length)
throw errors[0]
}
return {
mount() {
if (closed)
return
parent.insertBefore(placeholder, next?.parentNode === parent ? next : null)
if (!closed)
target.body.appendChild(root)
if (!closed)
copyStyles(source, target, node => nodes.push(node), () => !closed)
if (!closed) {
const adopted = target.adoptNode(player)
if (!closed)
root.appendChild(adopted)
}
if (closed)
cleanup()
},
destroy() {
if (closed)
return
closed = true
cleanup()
},
}
}