import ParchmentError from '../../error.js'; import Registry from '../../registry.js'; import Scope from '../../scope.js'; import type { Blot, BlotConstructor, Formattable, Parent, Root, } from './blot.js'; class ShadowBlot implements Blot { public static blotName = 'abstract'; public static className: string; public static requiredContainer: BlotConstructor; public static scope: Scope; public static tagName: string | string[]; public static create(rawValue?: unknown): Node { if (this.tagName == null) { throw new ParchmentError('Blot definition missing tagName'); } let node: HTMLElement; let value: string | number | undefined; if (Array.isArray(this.tagName)) { if (typeof rawValue === 'string') { value = rawValue.toUpperCase(); if (parseInt(value, 10).toString() === value) { value = parseInt(value, 10); } } else if (typeof rawValue === 'number') { value = rawValue; } if (typeof value === 'number') { node = document.createElement(this.tagName[value - 1]); } else if (value && this.tagName.indexOf(value) > -1) { node = document.createElement(value); } else { node = document.createElement(this.tagName[0]); } } else { node = document.createElement(this.tagName); } if (this.className) { node.classList.add(this.className); } return node; } public prev: Blot | null; public next: Blot | null; // @ts-expect-error Fix me later public parent: Parent; // Hack for accessing inherited static methods get statics(): any { return this.constructor; } constructor( public scroll: Root, public domNode: Node, ) { Registry.blots.set(domNode, this); this.prev = null; this.next = null; } public attach(): void { // Nothing to do } public clone(): Blot { const domNode = this.domNode.cloneNode(false); return this.scroll.create(domNode); } public detach(): void { if (this.parent != null) { this.parent.removeChild(this); } Registry.blots.delete(this.domNode); } public deleteAt(index: number, length: number): void { const blot = this.isolate(index, length); blot.remove(); } public formatAt( index: number, length: number, name: string, value: any, ): void { const blot = this.isolate(index, length); if (this.scroll.query(name, Scope.BLOT) != null && value) { blot.wrap(name, value); } else if (this.scroll.query(name, Scope.ATTRIBUTE) != null) { const parent = this.scroll.create(this.statics.scope) as Parent & Formattable; blot.wrap(parent); parent.format(name, value); } } public insertAt(index: number, value: string, def?: any): void { const blot = def == null ? this.scroll.create('text', value) : this.scroll.create(value, def); const ref = this.split(index); this.parent.insertBefore(blot, ref || undefined); } public isolate(index: number, length: number): Blot { const target = this.split(index); if (target == null) { throw new Error('Attempt to isolate at end'); } target.split(length); return target; } public length(): number { return 1; } public offset(root: Blot = this.parent): number { if (this.parent == null || this === root) { return 0; } return this.parent.children.offset(this) + this.parent.offset(root); } public optimize(_context?: { [key: string]: any }): void { if ( this.statics.requiredContainer && !(this.parent instanceof this.statics.requiredContainer) ) { this.wrap(this.statics.requiredContainer.blotName); } } public remove(): void { if (this.domNode.parentNode != null) { this.domNode.parentNode.removeChild(this.domNode); } this.detach(); } public replaceWith(name: string | Blot, value?: any): Blot { const replacement = typeof name === 'string' ? this.scroll.create(name, value) : name; if (this.parent != null) { this.parent.insertBefore(replacement, this.next || undefined); this.remove(); } return replacement; } public split(index: number, _force?: boolean): Blot | null { return index === 0 ? this : this.next; } public update( _mutations: MutationRecord[], _context: { [key: string]: any }, ): void { // Nothing to do by default } public wrap(name: string | Parent, value?: any): Parent { const wrapper = typeof name === 'string' ? (this.scroll.create(name, value) as Parent) : name; if (this.parent != null) { this.parent.insertBefore(wrapper, this.next || undefined); } if (typeof wrapper.appendChild !== 'function') { throw new ParchmentError(`Cannot wrap ${name}`); } wrapper.appendChild(this); return wrapper; } } export default ShadowBlot;