refactor(core): CORE-12 type template resources and verify bundled notices

This commit is contained in:
Harvey Zhao committed 2026-09-11 03:50:33 +08:00
1 parent b8039ad1f1
commit 9eb7c945ca
101 files changed
+7363 -664

No files matched your search

@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const ar = {
'Video Info': 'معلومات الفيديو',
'Close': 'إغلاق',
@@ -35,10 +38,8 @@ const ar = {
'Jump Play': 'التشغيل القافز',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay غير متاح',
}
} satisfies Dictionary
export default ar
if (typeof window !== 'undefined') {
window['artplayer-i18n-ar'] = ar
}
publishLanguage('artplayer-i18n-ar', ar)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const cs = {
'Video Info': 'Info o videu',
'Close': 'Zavřít',
@@ -35,10 +38,8 @@ const cs = {
'Jump Play': 'Hra na skok',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay není k dispozici',
}
} satisfies Dictionary
export default cs
if (typeof window !== 'undefined') {
window['artplayer-i18n-cs'] = cs
}
publishLanguage('artplayer-i18n-cs', cs)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const es = {
'Video Info': 'Información del video',
'Close': 'Cerrar',
@@ -35,10 +38,8 @@ const es = {
'Jump Play': 'Saltar',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay no disponible',
}
} satisfies Dictionary
export default es
if (typeof window !== 'undefined') {
window['artplayer-i18n-es'] = es
}
publishLanguage('artplayer-i18n-es', es)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const fa = {
'Video Info': 'اطلاعات ویدیو',
'Close': 'بستن',
@@ -35,10 +38,8 @@ const fa = {
'Jump Play': 'جامپ پلی',
'AirPlay': 'ایر پلی',
'AirPlay Not Available': 'عدم پشتیبانی از ایرپلی',
}
} satisfies Dictionary
export default fa
if (typeof window !== 'undefined') {
window['artplayer-i18n-fa'] = fa
}
publishLanguage('artplayer-i18n-fa', fa)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const fr = {
'Video Info': 'Informations de la vidéo',
'Close': 'Fermer',
@@ -35,10 +38,8 @@ const fr = {
'Jump Play': 'Continuer',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay non disponible',
}
} satisfies Dictionary
export default fr
if (typeof window !== 'undefined') {
window['artplayer-i18n-fr'] = fr
}
publishLanguage('artplayer-i18n-fr', fr)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const id = {
'Video Info': 'Informasi Video',
'Close': 'Tutup',
@@ -35,10 +38,8 @@ const id = {
'Jump Play': 'Lompat Putar',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay Tidak Tersedia',
}
} satisfies Dictionary
export default id
if (typeof window !== 'undefined') {
window['artplayer-i18n-id'] = id
}
publishLanguage('artplayer-i18n-id', id)
-30
View File
@@ -1,30 +0,0 @@
import { mergeDeep } from '../utils'
import zhCn from './zh-cn'
export default class I18n {
constructor(art) {
this.art = art
this.languages = {
'zh-cn': zhCn,
}
this.language = {}
this.update(art.option.i18n)
}
init() {
const lang = this.art.option.lang.toLowerCase()
this.language = this.languages[lang] || {}
}
get(key) {
return this.language[key] || key
}
update(value) {
this.languages = mergeDeep(this.languages, value)
this.init()
}
}
+34
View File
@@ -0,0 +1,34 @@
import type { Dictionary, Languages } from './publish'
import { has, mergeDeep } from '../utils/property'
import zhCn from './zh-cn'
export interface I18nHost {
option: { lang: string, i18n: Languages }
}
export default class I18n {
declare art: I18nHost
declare languages: Languages
declare language: Dictionary
constructor(art: I18nHost) {
this.art = art
this.languages = { 'zh-cn': zhCn }
this.language = {}
this.update(art.option.i18n)
}
init(): void {
const lang = this.art.option.lang.toLowerCase()
this.language = (has(this.languages, lang) && this.languages[lang]) || {}
}
get(key: string): string {
return (has(this.language, key) && this.language[key]) || key
}
update(value: Languages): void {
this.languages = mergeDeep(this.languages, value)
this.init()
}
}
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const pl = {
'Video Info': 'Informacje o wideo',
'Close': 'Zamknij',
@@ -35,10 +38,8 @@ const pl = {
'Jump Play': 'Skocz do gry',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay nie jest dostępny',
}
} satisfies Dictionary
export default pl
if (typeof window !== 'undefined') {
window['artplayer-i18n-pl'] = pl
}
publishLanguage('artplayer-i18n-pl', pl)
+12
View File
@@ -0,0 +1,12 @@
export type Dictionary = Record<string, string | undefined>
export type Languages = Record<string, Dictionary | undefined>
type LanguageGlobal = 'artplayer-i18n-ar' | 'artplayer-i18n-cs' | 'artplayer-i18n-es' | 'artplayer-i18n-fa' | 'artplayer-i18n-fr' | 'artplayer-i18n-id' | 'artplayer-i18n-pl' | 'artplayer-i18n-ru' | 'artplayer-i18n-tr' | 'artplayer-i18n-vi' | 'artplayer-i18n-zh-cn' | 'artplayer-i18n-zh-tw'
// Each standalone language keeps its historical browser alias, including ESM.
export function publishLanguage(name: LanguageGlobal, value: Dictionary): void {
if (typeof window !== 'undefined') {
const globals = window as Window & Partial<Record<LanguageGlobal, Dictionary>>
globals[name] = value
}
}
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const ru = {
'Video Info': 'Информация',
'Close': 'Закрыть',
@@ -35,10 +38,8 @@ const ru = {
'Jump Play': 'Перейти',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay недоступен',
}
} satisfies Dictionary
export default ru
if (typeof window !== 'undefined') {
window['artplayer-i18n-ru'] = ru
}
publishLanguage('artplayer-i18n-ru', ru)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const tr = {
'Video Info': 'Video Bilgisi',
'Close': 'Kapat',
@@ -35,10 +38,8 @@ const tr = {
'Jump Play': 'Atla ve Oynat',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay Kullanılamıyor',
}
} satisfies Dictionary
export default tr
if (typeof window !== 'undefined') {
window['artplayer-i18n-tr'] = tr
}
publishLanguage('artplayer-i18n-tr', tr)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const vi = {
'Video Info': 'Thông tin video',
'Close': 'Đóng',
@@ -35,10 +38,8 @@ const vi = {
'Jump Play': 'Nhảy đến đoạn phát',
'AirPlay': 'AirPlay',
'AirPlay Not Available': 'AirPlay không khả dụng',
}
} satisfies Dictionary
export default vi
if (typeof window !== 'undefined') {
window['artplayer-i18n-vi'] = vi
}
publishLanguage('artplayer-i18n-vi', vi)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const zhCn = {
'Video Info': '统计信息',
'Close': '关闭',
@@ -35,10 +38,8 @@ const zhCn = {
'Jump Play': '跳转播放',
'AirPlay': '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
}
} satisfies Dictionary
export default zhCn
if (typeof window !== 'undefined') {
window['artplayer-i18n-zh-cn'] = zhCn
}
publishLanguage('artplayer-i18n-zh-cn', zhCn)
@@ -1,3 +1,6 @@
import type { Dictionary } from './publish'
import { publishLanguage } from './publish'
const zhTw = {
'Video Info': '統計訊息',
'Close': '關閉',
@@ -35,10 +38,8 @@ const zhTw = {
'Jump Play': '跳轉播放',
'AirPlay': '隔空播放',
'AirPlay Not Available': '隔空播放不可用',
}
} satisfies Dictionary
export default zhTw
if (typeof window !== 'undefined') {
window['artplayer-i18n-zh-tw'] = zhTw
}
publishLanguage('artplayer-i18n-zh-tw', zhTw)
@@ -1,4 +1,3 @@
import { def, getIcon } from '../utils'
import airplay from './airplay.svg?raw'
import arrowLeft from './arrow-left.svg?raw'
import arrowRight from './arrow-right.svg?raw'
@@ -27,43 +26,32 @@ import unlock from './unlock.svg?raw'
import volumeClose from './volume-close.svg?raw'
import volume from './volume.svg?raw'
export default class Icons {
constructor(art) {
const icons = {
loading,
state,
play,
pause,
check,
volume,
volumeClose,
screenshot,
setting,
pip,
arrowLeft,
arrowRight,
playbackRate,
aspectRatio,
config,
lock,
flip,
unlock,
fullscreenOff,
fullscreenOn,
fullscreenWebOff,
fullscreenWebOn,
switchOn,
switchOff,
error,
close,
airplay,
...art.option.icons,
}
for (const key in icons) {
def(this, key, {
get: () => getIcon(key, icons[key]),
})
}
}
export default {
loading,
state,
play,
pause,
check,
volume,
volumeClose,
screenshot,
setting,
pip,
arrowLeft,
arrowRight,
playbackRate,
aspectRatio,
config,
lock,
flip,
unlock,
fullscreenOff,
fullscreenOn,
fullscreenWebOff,
fullscreenWebOn,
switchOn,
switchOff,
error,
close,
airplay,
}
+15
View File
@@ -0,0 +1,15 @@
import { def, getIcon } from '../utils'
import defaults from './defaults'
export default class Icons {
readonly [key: string]: HTMLElement
constructor(art: { option: { icons: Record<string, string | HTMLElement> } }) {
const icons: Record<string, string | HTMLElement | undefined> = { ...defaults, ...art.option.icons }
for (const key in icons) {
def(this, key, {
get: (): HTMLElement => getIcon(key, icons[key]),
})
}
}
}
+1 -1
View File
@@ -20,7 +20,7 @@ import Plugins from './plugins'
import scheme from './scheme'
import Setting from './setting'
import Storage from './storage'
import style from './style/index.less?inline'
import style from './style'
import Subtitle from './subtitle'
import Template from './template'
import * as utils from './utils'
+3
View File
@@ -0,0 +1,3 @@
import css from './index.less?inline'
export default css
+16
View File
@@ -0,0 +1,16 @@
export function setStyleText(id: string, style: string): void {
let $style = document.getElementById(id)
if (!$style) {
$style = document.createElement('style')
$style.id = id
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
document.head.appendChild($style!)
})
}
else {
(document.head || document.documentElement).appendChild($style)
}
}
$style.textContent = style
}
-160
View File
@@ -1,160 +0,0 @@
import { version } from '../package.json'
import { ownContainer } from './lifecycle/instance'
import captureTemplate from './lifecycle/template-rollback'
import { addClass, errorHandle, isMobile, query, replaceElement, supportsFlex } from './utils'
export default class Template {
constructor(art) {
this.art = art
const { option, constructor } = art
if (option.container instanceof Element) {
this.$container = option.container
}
else {
this.$container = query(option.container)
errorHandle(this.$container, `No container element found by ${option.container}`)
}
errorHandle(supportsFlex(), 'The current browser does not support flex layout')
const type = this.$container.tagName.toLowerCase()
errorHandle(type === 'div', `Unsupported container element type, only support 'div' but got '${type}'`)
errorHandle(
constructor.instances.every(ins => ins.template.$container !== this.$container),
'Cannot mount multiple instances on the same dom element',
)
this.query = this.query.bind(this)
ownContainer(art, this.$container, captureTemplate(this.$container))
this.$container.dataset.artId = art.id
this.init()
}
static get html() {
return `
<div class="art-video-player art-subtitle-show art-layer-show art-control-show art-mask-show">
<video class="art-video">
<track default kind="metadata" src=""></track>
</video>
<div class="art-poster"></div>
<div class="art-subtitle"></div>
<div class="art-danmuku"></div>
<div class="art-layers"></div>
<div class="art-mask">
<div class="art-state"></div>
</div>
<div class="art-bottom">
<div class="art-progress"></div>
<div class="art-controls">
<div class="art-controls-left"></div>
<div class="art-controls-center"></div>
<div class="art-controls-right"></div>
</div>
</div>
<div class="art-loading"></div>
<div class="art-notice">
<div class="art-notice-inner"></div>
</div>
<div class="art-settings"></div>
<div class="art-info">
<div class="art-info-panel">
<div class="art-info-item">
<div class="art-info-title">Player version:</div>
<div class="art-info-content">${version}</div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video url:</div>
<div class="art-info-content" data-video="currentSrc"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video volume:</div>
<div class="art-info-content" data-video="volume"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video time:</div>
<div class="art-info-content" data-video="currentTime"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video duration:</div>
<div class="art-info-content" data-video="duration"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video resolution:</div>
<div class="art-info-content">
<span data-video="videoWidth"></span> x <span data-video="videoHeight"></span>
</div>
</div>
</div>
<div class="art-info-close">[x]</div>
</div>
<div class="art-contextmenus"></div>
</div>
`
}
query(className) {
return query(className, this.$container)
}
init() {
const { option } = this.art
if (!option.useSSR) {
this.$container.innerHTML = Template.html
}
this.$player = this.query('.art-video-player')
this.$video = this.query('.art-video')
this.$track = this.query('track')
this.$poster = this.query('.art-poster')
this.$subtitle = this.query('.art-subtitle')
this.$danmuku = this.query('.art-danmuku')
this.$bottom = this.query('.art-bottom')
this.$progress = this.query('.art-progress')
this.$controls = this.query('.art-controls')
this.$controlsLeft = this.query('.art-controls-left')
this.$controlsCenter = this.query('.art-controls-center')
this.$controlsRight = this.query('.art-controls-right')
this.$layer = this.query('.art-layers')
this.$loading = this.query('.art-loading')
this.$notice = this.query('.art-notice')
this.$noticeInner = this.query('.art-notice-inner')
this.$mask = this.query('.art-mask')
this.$state = this.query('.art-state')
this.$setting = this.query('.art-settings')
this.$info = this.query('.art-info')
this.$infoPanel = this.query('.art-info-panel')
this.$infoClose = this.query('.art-info-close')
this.$contextmenu = this.query('.art-contextmenus')
if (option.proxy) {
const video = option.proxy.call(this.art, this.art)
errorHandle(
video instanceof HTMLVideoElement || video instanceof HTMLCanvasElement,
`Function 'option.proxy' needs to return 'HTMLVideoElement' or 'HTMLCanvasElement'`,
)
replaceElement(video, this.$video)
video.className = 'art-video'
this.$video = video
}
if (option.backdrop) {
addClass(this.$player, 'art-backdrop')
}
if (isMobile) {
addClass(this.$player, 'art-mobile')
}
}
destroy(removeHtml) {
if (removeHtml) {
this.$container.innerHTML = ''
}
else {
addClass(this.$player, 'art-destroy')
}
}
}
+112
View File
@@ -0,0 +1,112 @@
import type { TemplateHost, TemplateNodes } from './template/types'
import { ownContainer } from './lifecycle/instance'
import captureTemplate from './lifecycle/template-rollback'
import html from './template/html'
import bindNodes from './template/nodes'
import { addClass, errorHandle, isMobile, query, replaceElement, supportsFlex } from './utils'
export default class Template<Host extends TemplateHost<Host>> implements TemplateNodes {
declare art: Host
declare $container: HTMLDivElement
declare $player: HTMLDivElement | null
declare $video: HTMLVideoElement | HTMLCanvasElement | null
declare $track: HTMLTrackElement | null
declare $poster: HTMLDivElement | null
declare $subtitle: HTMLDivElement | null
declare $danmuku: HTMLDivElement | null
declare $bottom: HTMLDivElement | null
declare $progress: HTMLDivElement | null
declare $controls: HTMLDivElement | null
declare $controlsLeft: HTMLDivElement | null
declare $controlsCenter: HTMLDivElement | null
declare $controlsRight: HTMLDivElement | null
declare $layer: HTMLDivElement | null
declare $loading: HTMLDivElement | null
declare $notice: HTMLDivElement | null
declare $noticeInner: HTMLDivElement | null
declare $mask: HTMLDivElement | null
declare $state: HTMLDivElement | null
declare $setting: HTMLDivElement | null
declare $info: HTMLDivElement | null
declare $infoPanel: HTMLDivElement | null
declare $infoClose: HTMLDivElement | null
declare $contextmenu: HTMLDivElement | null
constructor(art: Host) {
this.art = art
const { option, constructor } = art
if (option.container instanceof Element) {
this.$container = option.container as HTMLDivElement
}
else {
this.$container = query(option.container) as HTMLDivElement
errorHandle(this.$container, `No container element found by ${option.container}`)
}
errorHandle(supportsFlex(), 'The current browser does not support flex layout')
const type = this.$container.tagName.toLowerCase()
errorHandle(type === 'div', `Unsupported container element type, only support 'div' but got '${type}'`)
errorHandle(
constructor.instances.every(ins => ins.template.$container !== this.$container),
'Cannot mount multiple instances on the same dom element',
)
this.query = this.query.bind(this)
ownContainer(art, this.$container, captureTemplate(this.$container))
this.$container.dataset.artId = String(art.id)
this.init()
}
static get html(): string {
return html
}
query<T extends Element = Element>(className: string): T | null {
return query(className, this.$container) as T | null
}
init(): void {
const { option } = this.art
if (!option.useSSR) {
this.$container.innerHTML = Template.html
}
bindNodes(this)
if (option.proxy) {
const video = option.proxy.call(this.art, this.art)
assertProxy(video)
replaceElement(video, this.$video)
video.className = 'art-video'
this.$video = video
}
if (option.backdrop) {
addClass(this.$player, 'art-backdrop')
}
if (isMobile) {
addClass(this.$player, 'art-mobile')
}
}
destroy(removeHtml: boolean): void {
if (removeHtml) {
this.$container.innerHTML = ''
}
else {
addClass(this.$player, 'art-destroy')
}
}
}
function assertProxy(video: unknown): asserts video is HTMLVideoElement | HTMLCanvasElement {
errorHandle(
video instanceof HTMLVideoElement || video instanceof HTMLCanvasElement,
'Function \'option.proxy\' needs to return \'HTMLVideoElement\' or \'HTMLCanvasElement\'',
)
}
+61
View File
@@ -0,0 +1,61 @@
import { version } from '../../package.json'
export default `
<div class="art-video-player art-subtitle-show art-layer-show art-control-show art-mask-show">
<video class="art-video">
<track default kind="metadata" src=""></track>
</video>
<div class="art-poster"></div>
<div class="art-subtitle"></div>
<div class="art-danmuku"></div>
<div class="art-layers"></div>
<div class="art-mask">
<div class="art-state"></div>
</div>
<div class="art-bottom">
<div class="art-progress"></div>
<div class="art-controls">
<div class="art-controls-left"></div>
<div class="art-controls-center"></div>
<div class="art-controls-right"></div>
</div>
</div>
<div class="art-loading"></div>
<div class="art-notice">
<div class="art-notice-inner"></div>
</div>
<div class="art-settings"></div>
<div class="art-info">
<div class="art-info-panel">
<div class="art-info-item">
<div class="art-info-title">Player version:</div>
<div class="art-info-content">${version}</div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video url:</div>
<div class="art-info-content" data-video="currentSrc"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video volume:</div>
<div class="art-info-content" data-video="volume"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video time:</div>
<div class="art-info-content" data-video="currentTime"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video duration:</div>
<div class="art-info-content" data-video="duration"></div>
</div>
<div class="art-info-item">
<div class="art-info-title">Video resolution:</div>
<div class="art-info-content">
<span data-video="videoWidth"></span> x <span data-video="videoHeight"></span>
</div>
</div>
</div>
<div class="art-info-close">[x]</div>
</div>
<div class="art-contextmenus"></div>
</div>
`
+27
View File
@@ -0,0 +1,27 @@
import type { TemplateNodes } from './types'
export default function bindNodes(target: TemplateNodes & { query: <T extends Element = Element>(selector: string) => T | null }): void {
target.$player = target.query<HTMLDivElement>('.art-video-player')
target.$video = target.query<HTMLVideoElement>('.art-video')
target.$track = target.query<HTMLTrackElement>('track')
target.$poster = target.query<HTMLDivElement>('.art-poster')
target.$subtitle = target.query<HTMLDivElement>('.art-subtitle')
target.$danmuku = target.query<HTMLDivElement>('.art-danmuku')
target.$bottom = target.query<HTMLDivElement>('.art-bottom')
target.$progress = target.query<HTMLDivElement>('.art-progress')
target.$controls = target.query<HTMLDivElement>('.art-controls')
target.$controlsLeft = target.query<HTMLDivElement>('.art-controls-left')
target.$controlsCenter = target.query<HTMLDivElement>('.art-controls-center')
target.$controlsRight = target.query<HTMLDivElement>('.art-controls-right')
target.$layer = target.query<HTMLDivElement>('.art-layers')
target.$loading = target.query<HTMLDivElement>('.art-loading')
target.$notice = target.query<HTMLDivElement>('.art-notice')
target.$noticeInner = target.query<HTMLDivElement>('.art-notice-inner')
target.$mask = target.query<HTMLDivElement>('.art-mask')
target.$state = target.query<HTMLDivElement>('.art-state')
target.$setting = target.query<HTMLDivElement>('.art-settings')
target.$info = target.query<HTMLDivElement>('.art-info')
target.$infoPanel = target.query<HTMLDivElement>('.art-info-panel')
target.$infoClose = target.query<HTMLDivElement>('.art-info-close')
target.$contextmenu = target.query<HTMLDivElement>('.art-contextmenus')
}
+37
View File
@@ -0,0 +1,37 @@
export interface TemplateHost<Host> {
id: number
option: {
container: string | HTMLElement
useSSR: boolean
proxy?: (this: Host, art: Host) => unknown
backdrop: boolean
}
constructor: { instances: readonly { template: { $container: Element } }[] }
}
// Missing SSR nodes remain nullable; mounting does not synthesize replacements.
export interface TemplateNodes {
$player: HTMLDivElement | null
$video: HTMLVideoElement | HTMLCanvasElement | null
$track: HTMLTrackElement | null
$poster: HTMLDivElement | null
$subtitle: HTMLDivElement | null
$danmuku: HTMLDivElement | null
$bottom: HTMLDivElement | null
$progress: HTMLDivElement | null
$controls: HTMLDivElement | null
$controlsLeft: HTMLDivElement | null
$controlsCenter: HTMLDivElement | null
$controlsRight: HTMLDivElement | null
$layer: HTMLDivElement | null
$loading: HTMLDivElement | null
$notice: HTMLDivElement | null
$noticeInner: HTMLDivElement | null
$mask: HTMLDivElement | null
$state: HTMLDivElement | null
$setting: HTMLDivElement | null
$info: HTMLDivElement | null
$infoPanel: HTMLDivElement | null
$infoClose: HTMLDivElement | null
$contextmenu: HTMLDivElement | null
}
+4 -17
View File
@@ -1,5 +1,8 @@
import { isMobile } from './compatibility'
export { setStyleText } from '../style/inject'
/** @param {string} selector @param {ParentNode} parent */
export function query(selector, parent = document) {
return parent.querySelector(selector)
}
@@ -90,6 +93,7 @@ export function createElement(tag) {
return document.createElement(tag)
}
/** @param {string} key @param {string | HTMLElement} html */
export function getIcon(key = '', html = '') {
const icon = createElement('i')
addClass(icon, 'art-icon')
@@ -98,23 +102,6 @@ export function getIcon(key = '', html = '') {
return icon
}
export function setStyleText(id, style) {
let $style = document.getElementById(id)
if (!$style) {
$style = document.createElement('style')
$style.id = id
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
document.head.appendChild($style)
})
}
else {
(document.head || document.documentElement).appendChild($style)
}
}
$style.textContent = style
}
export function supportsFlex() {
const div = document.createElement('div')
div.style.display = 'flex'