import HTMLElement from '../html-element/HTMLElement.js'; import * as PropertySymbol from '../../PropertySymbol.js'; import IHTMLScriptElement from './IHTMLScriptElement.js'; import Event from '../../event/Event.js'; import ErrorEvent from '../../event/events/ErrorEvent.js'; import INode from '../../nodes/node/INode.js'; import INamedNodeMap from '../../named-node-map/INamedNodeMap.js'; import HTMLScriptElementNamedNodeMap from './HTMLScriptElementNamedNodeMap.js'; import WindowErrorUtility from '../../window/WindowErrorUtility.js'; import WindowBrowserSettingsReader from '../../window/WindowBrowserSettingsReader.js'; import HTMLScriptElementScriptLoader from './HTMLScriptElementScriptLoader.js'; import IBrowserFrame from '../../browser/types/IBrowserFrame.js'; import BrowserErrorCaptureEnum from '../../browser/enums/BrowserErrorCaptureEnum.js'; /** * HTML Script Element. * * Reference: * https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement. */ export default class HTMLScriptElement extends HTMLElement implements IHTMLScriptElement { // Events public onerror: (event: ErrorEvent) => void = null; public onload: (event: Event) => void = null; // Internal properties public override [PropertySymbol.attributes]: INamedNodeMap; public [PropertySymbol.evaluateScript] = true; // Private properties #scriptLoader: HTMLScriptElementScriptLoader; /** * Constructor. * * @param browserFrame Browser frame. */ constructor(browserFrame: IBrowserFrame) { super(); this.#scriptLoader = new HTMLScriptElementScriptLoader({ element: this, browserFrame }); this[PropertySymbol.attributes] = new HTMLScriptElementNamedNodeMap(this, this.#scriptLoader); } /** * Returns type. * * @returns Type. */ public get type(): string { return this.getAttribute('type') || ''; } /** * Sets type. * * @param type Type. */ public set type(type: string) { this.setAttribute('type', type); } /** * Returns source. * * @returns Source. */ public get src(): string { if (!this.hasAttribute('src')) { return ''; } try { return new URL(this.getAttribute('src'), this[PropertySymbol.ownerDocument].location.href) .href; } catch (e) { return this.getAttribute('src'); } } /** * Sets source. * * @param src Source. */ public set src(src: string) { this.setAttribute('src', src); } /** * Returns charset. * * @returns Charset. */ public get charset(): string { return this.getAttribute('charset') || ''; } /** * Sets charset. * * @param charset Charset. */ public set charset(charset: string) { this.setAttribute('charset', charset); } /** * Returns lang. * * @returns Lang. */ public get lang(): string { return this.getAttribute('lang') || ''; } /** * Sets lang. * * @param lang Lang. */ public set lang(lang: string) { this.setAttribute('lang', lang); } /** * Returns async. * * @returns Async. */ public get async(): boolean { return this.getAttribute('async') !== null; } /** * Sets async. * * @param async Async. */ public set async(async: boolean) { if (!async) { this.removeAttribute('async'); } else { this.setAttribute('async', ''); } } /** * Returns defer. * * @returns Defer. */ public get defer(): boolean { return this.getAttribute('defer') !== null; } /** * Sets defer. * * @param defer Defer. */ public set defer(defer: boolean) { if (!defer) { this.removeAttribute('defer'); } else { this.setAttribute('defer', ''); } } /** * Returns text. * * @returns Text. */ public get text(): string { return this.textContent; } /** * Sets text. * * @param text Text. */ public set text(text: string) { this.textContent = text; } /** * Clones a node. * * @override * @param [deep=false] "true" to clone deep. * @returns Cloned node. */ public cloneNode(deep = false): IHTMLScriptElement { return super.cloneNode(deep); } /** * @override */ public override [PropertySymbol.connectToNode](parentNode: INode = null): void { const isConnected = this[PropertySymbol.isConnected]; const isParentConnected = parentNode ? parentNode[PropertySymbol.isConnected] : false; const browserSettings = WindowBrowserSettingsReader.getSettings( this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow] ); super[PropertySymbol.connectToNode](parentNode); if ( isParentConnected && isConnected !== isParentConnected && this[PropertySymbol.evaluateScript] ) { const src = this.getAttribute('src'); if (src !== null) { this.#scriptLoader.loadScript(src); } else if (!browserSettings.disableJavaScriptEvaluation) { const textContent = this.textContent; const type = this.getAttribute('type'); if ( textContent && (type === null || type === 'application/x-ecmascript' || type === 'application/x-javascript' || type.startsWith('text/javascript')) ) { this[PropertySymbol.ownerDocument][PropertySymbol.currentScript] = this; const code = `//# sourceURL=${ this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].location.href }\n` + textContent; if ( browserSettings.disableErrorCapturing || browserSettings.errorCapture !== BrowserErrorCaptureEnum.tryAndCatch ) { this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].eval(code); } else { WindowErrorUtility.captureError( this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow], () => this[PropertySymbol.ownerDocument][PropertySymbol.ownerWindow].eval(code) ); } this[PropertySymbol.ownerDocument][PropertySymbol.currentScript] = null; } } } } }