| Server IP : 13.235.167.51 / Your IP : 216.73.217.116 Web Server : Apache System : Linux machinox-server 6.17.0-1013-aws #13~24.04.1-Ubuntu SMP Fri Apr 24 21:36:58 UTC 2026 aarch64 User : root ( 0) PHP Version : 8.2.29 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/control.machinox.in/node_modules/parchment/src/blot/abstract/ |
Upload File : |
import LinkedList from '../../collection/linked-list.js';
import ParchmentError from '../../error.js';
import Scope from '../../scope.js';
import type { Blot, BlotConstructor, Parent, Root } from './blot.js';
import ShadowBlot from './shadow.js';
function makeAttachedBlot(node: Node, scroll: Root): Blot {
const found = scroll.find(node);
if (found) return found;
try {
return scroll.create(node);
} catch (e) {
const blot = scroll.create(Scope.INLINE);
Array.from(node.childNodes).forEach((child: Node) => {
blot.domNode.appendChild(child);
});
if (node.parentNode) {
node.parentNode.replaceChild(blot.domNode, node);
}
blot.attach();
return blot;
}
}
class ParentBlot extends ShadowBlot implements Parent {
/**
* Whitelist array of Blots that can be direct children.
*/
public static allowedChildren?: BlotConstructor[];
/**
* Default child blot to be inserted if this blot becomes empty.
*/
public static defaultChild?: BlotConstructor;
public static uiClass = '';
public children!: LinkedList<Blot>;
public domNode!: HTMLElement;
public uiNode: HTMLElement | null = null;
constructor(scroll: Root, domNode: Node) {
super(scroll, domNode);
this.build();
}
public appendChild(other: Blot): void {
this.insertBefore(other);
}
public attach(): void {
super.attach();
this.children.forEach((child) => {
child.attach();
});
}
public attachUI(node: HTMLElement): void {
if (this.uiNode != null) {
this.uiNode.remove();
}
this.uiNode = node;
if (ParentBlot.uiClass) {
this.uiNode.classList.add(ParentBlot.uiClass);
}
this.uiNode.setAttribute('contenteditable', 'false');
this.domNode.insertBefore(this.uiNode, this.domNode.firstChild);
}
/**
* Called during construction, should fill its own children LinkedList.
*/
public build(): void {
this.children = new LinkedList<Blot>();
// Need to be reversed for if DOM nodes already in order
Array.from(this.domNode.childNodes)
.filter((node: Node) => node !== this.uiNode)
.reverse()
.forEach((node: Node) => {
try {
const child = makeAttachedBlot(node, this.scroll);
this.insertBefore(child, this.children.head || undefined);
} catch (err) {
if (err instanceof ParchmentError) {
return;
} else {
throw err;
}
}
});
}
public deleteAt(index: number, length: number): void {
if (index === 0 && length === this.length()) {
return this.remove();
}
this.children.forEachAt(index, length, (child, offset, childLength) => {
child.deleteAt(offset, childLength);
});
}
public descendant<T extends Blot>(
criteria: new (...args: any[]) => T,
index: number,
): [T | null, number];
public descendant(
criteria: (blot: Blot) => boolean,
index: number,
): [Blot | null, number];
public descendant(criteria: any, index = 0): [Blot | null, number] {
const [child, offset] = this.children.find(index);
if (
(criteria.blotName == null && criteria(child)) ||
(criteria.blotName != null && child instanceof criteria)
) {
return [child as any, offset];
} else if (child instanceof ParentBlot) {
return child.descendant(criteria, offset);
} else {
return [null, -1];
}
}
public descendants<T extends Blot>(
criteria: new (...args: any[]) => T,
index?: number,
length?: number,
): T[];
public descendants(
criteria: (blot: Blot) => boolean,
index?: number,
length?: number,
): Blot[];
public descendants(
criteria: any,
index = 0,
length: number = Number.MAX_VALUE,
): Blot[] {
let descendants: Blot[] = [];
let lengthLeft = length;
this.children.forEachAt(
index,
length,
(child: Blot, childIndex: number, childLength: number) => {
if (
(criteria.blotName == null && criteria(child)) ||
(criteria.blotName != null && child instanceof criteria)
) {
descendants.push(child);
}
if (child instanceof ParentBlot) {
descendants = descendants.concat(
child.descendants(criteria, childIndex, lengthLeft),
);
}
lengthLeft -= childLength;
},
);
return descendants;
}
public detach(): void {
this.children.forEach((child) => {
child.detach();
});
super.detach();
}
public enforceAllowedChildren(): void {
let done = false;
this.children.forEach((child: Blot) => {
if (done) {
return;
}
const allowed = this.statics.allowedChildren.some(
(def: BlotConstructor) => child instanceof def,
);
if (allowed) {
return;
}
if (child.statics.scope === Scope.BLOCK_BLOT) {
if (child.next != null) {
this.splitAfter(child);
}
if (child.prev != null) {
this.splitAfter(child.prev);
}
child.parent.unwrap();
done = true;
} else if (child instanceof ParentBlot) {
child.unwrap();
} else {
child.remove();
}
});
}
public formatAt(
index: number,
length: number,
name: string,
value: any,
): void {
this.children.forEachAt(index, length, (child, offset, childLength) => {
child.formatAt(offset, childLength, name, value);
});
}
public insertAt(index: number, value: string, def?: any): void {
const [child, offset] = this.children.find(index);
if (child) {
child.insertAt(offset, value, def);
} else {
const blot =
def == null
? this.scroll.create('text', value)
: this.scroll.create(value, def);
this.appendChild(blot);
}
}
public insertBefore(childBlot: Blot, refBlot?: Blot | null): void {
if (childBlot.parent != null) {
childBlot.parent.children.remove(childBlot);
}
let refDomNode: Node | null = null;
this.children.insertBefore(childBlot, refBlot || null);
childBlot.parent = this;
if (refBlot != null) {
refDomNode = refBlot.domNode;
}
if (
this.domNode.parentNode !== childBlot.domNode ||
this.domNode.nextSibling !== refDomNode
) {
this.domNode.insertBefore(childBlot.domNode, refDomNode);
}
childBlot.attach();
}
public length(): number {
return this.children.reduce((memo, child) => {
return memo + child.length();
}, 0);
}
public moveChildren(targetParent: Parent, refNode?: Blot | null): void {
this.children.forEach((child) => {
targetParent.insertBefore(child, refNode);
});
}
public optimize(context?: { [key: string]: any }): void {
super.optimize(context);
this.enforceAllowedChildren();
if (this.uiNode != null && this.uiNode !== this.domNode.firstChild) {
this.domNode.insertBefore(this.uiNode, this.domNode.firstChild);
}
if (this.children.length === 0) {
if (this.statics.defaultChild != null) {
const child = this.scroll.create(this.statics.defaultChild.blotName);
this.appendChild(child);
// TODO double check if necessary
// child.optimize(context);
} else {
this.remove();
}
}
}
public path(index: number, inclusive = false): [Blot, number][] {
const [child, offset] = this.children.find(index, inclusive);
const position: [Blot, number][] = [[this, index]];
if (child instanceof ParentBlot) {
return position.concat(child.path(offset, inclusive));
} else if (child != null) {
position.push([child, offset]);
}
return position;
}
public removeChild(child: Blot): void {
this.children.remove(child);
}
public replaceWith(name: string | Blot, value?: any): Blot {
const replacement =
typeof name === 'string' ? this.scroll.create(name, value) : name;
if (replacement instanceof ParentBlot) {
this.moveChildren(replacement);
}
return super.replaceWith(replacement);
}
public split(index: number, force = false): Blot | null {
if (!force) {
if (index === 0) {
return this;
}
if (index === this.length()) {
return this.next;
}
}
const after = this.clone() as ParentBlot;
if (this.parent) {
this.parent.insertBefore(after, this.next || undefined);
}
this.children.forEachAt(index, this.length(), (child, offset, _length) => {
const split = child.split(offset, force);
if (split != null) {
after.appendChild(split);
}
});
return after;
}
public splitAfter(child: Blot): Parent {
const after = this.clone() as ParentBlot;
while (child.next != null) {
after.appendChild(child.next);
}
if (this.parent) {
this.parent.insertBefore(after, this.next || undefined);
}
return after;
}
public unwrap(): void {
if (this.parent) {
this.moveChildren(this.parent, this.next || undefined);
}
this.remove();
}
public update(
mutations: MutationRecord[],
_context: { [key: string]: any },
): void {
const addedNodes: Node[] = [];
const removedNodes: Node[] = [];
mutations.forEach((mutation) => {
if (mutation.target === this.domNode && mutation.type === 'childList') {
addedNodes.push(...mutation.addedNodes);
removedNodes.push(...mutation.removedNodes);
}
});
removedNodes.forEach((node: Node) => {
// Check node has actually been removed
// One exception is Chrome does not immediately remove IFRAMEs
// from DOM but MutationRecord is correct in its reported removal
if (
node.parentNode != null &&
// @ts-expect-error Fix me later
node.tagName !== 'IFRAME' &&
document.body.compareDocumentPosition(node) &
Node.DOCUMENT_POSITION_CONTAINED_BY
) {
return;
}
const blot = this.scroll.find(node);
if (blot == null) {
return;
}
if (
blot.domNode.parentNode == null ||
blot.domNode.parentNode === this.domNode
) {
blot.detach();
}
});
addedNodes
.filter((node) => {
return node.parentNode === this.domNode && node !== this.uiNode;
})
.sort((a, b) => {
if (a === b) {
return 0;
}
if (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) {
return 1;
}
return -1;
})
.forEach((node) => {
let refBlot: Blot | null = null;
if (node.nextSibling != null) {
refBlot = this.scroll.find(node.nextSibling);
}
const blot = makeAttachedBlot(node, this.scroll);
if (blot.next !== refBlot || blot.next == null) {
if (blot.parent != null) {
blot.parent.removeChild(this);
}
this.insertBefore(blot, refBlot || undefined);
}
});
this.enforceAllowedChildren();
}
}
export default ParentBlot;