Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/bbcode/model/src/nodes/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/bbcode/model/src/nodes/element-node.js |
import { Type, type JsonObject, type JsonValue } from 'main.core'; import { BBCodeNode, type BBCodeNodeOptions, type BBCodeContentNode, type SerializedBBCodeNode, privateMap } from './node'; import { typeof BBCodeFragmentNode } from './fragment-node'; import { type BBCodeNodeStringifier } from '../scheme/node-schemes/node-scheme'; import { typeof BBCodeTagScheme } from '../scheme/node-schemes/tag-scheme'; import { typeof BBCodeScheme } from '../scheme/bbcode-scheme'; export type BBCodeElementNodeValue = string | number | boolean; export type BBCodeElementNodeOptions = BBCodeNodeOptions & { attributes?: JsonObject, value?: BBCodeElementNodeValue, }; export type SerializedBBCodeElementNode = SerializedBBCodeNode & { attributes: JsonObject, value: BBCodeElementNodeValue, }; export type FilteredChildren = { resolved: Array<BBCodeContentNode>, unresolved: Array<BBCodeContentNode>, }; const voidSymbol: Symbol = Symbol('void'); export class BBCodeElementNode extends BBCodeNode { attributes: JsonObject = {}; value: JsonValue = ''; [voidSymbol]: boolean = false; constructor(options: BBCodeElementNodeOptions = {}) { super(options); privateMap.get(this).type = BBCodeNode.ELEMENT_NODE; const tagScheme: BBCodeTagScheme = this.getTagScheme(); this[voidSymbol] = tagScheme.isVoid(); this.setValue(options.value); this.setAttributes(options.attributes); } setScheme(scheme: BBCodeScheme, onUnknown: (node: BBCodeContentNode) => any) { this.getChildren().forEach((node: BBCodeContentNode) => { node.setScheme(scheme, onUnknown); }); if (scheme.isAllowedTag(this.getName())) { super.setScheme(scheme); const tagScheme: BBCodeTagScheme = this.getTagScheme(); this[voidSymbol] = tagScheme.isVoid(); } else { super.setScheme(scheme); onUnknown(this, scheme); } } filterChildren(children: Array<BBCodeContentNode>): FilteredChildren { const filteredChildren: FilteredChildren = { resolved: [], unresolved: [], }; const scheme: BBCodeScheme = this.getScheme(); children.forEach((child: BBCodeContentNode) => { if (scheme.isChildAllowed(this, child)) { filteredChildren.resolved.push(child); } else { filteredChildren.unresolved.push(child); } }); return filteredChildren; } convertChildren(children: Array<BBCodeContentNode>): Array<BBCodeContentNode> { const tagScheme: BBCodeTagScheme = this.getTagScheme(); const childConverter = tagScheme.getChildConverter(); if (childConverter) { const scheme: BBCodeScheme = this.getScheme(); return children.map((child: BBCodeNode) => { return childConverter(child, scheme); }); } return children; } setValue(value: BBCodeElementNodeValue) { if (Type.isString(value) || Type.isNumber(value) || Type.isBoolean(value)) { this.value = value; } } getValue(): BBCodeElementNodeValue { return this.value; } isVoid(): boolean { return this[voidSymbol]; } canBeEmpty(): boolean { return this.getTagScheme().canBeEmpty(); } hasGroup(groupName: string): boolean { return this.getTagScheme().hasGroup(groupName); } setAttributes(attributes: JsonObject) { if (Type.isPlainObject(attributes)) { const entries = Object.entries(attributes).map(([key, value]) => { return [key.toLowerCase(), value]; }); this.attributes = Object.fromEntries(entries); } } setAttribute(name: string, value: any) { if (Type.isStringFilled(name)) { const preparedName: string = name.toLowerCase(); if (Type.isNil(value)) { delete this.attributes[preparedName]; } else { this.attributes[preparedName] = value; } } } getAttribute(name: string): string | number | null { if (Type.isString(name)) { return this.attributes[name.toLowerCase()]; } return null; } getAttributes(): JsonObject { return { ...this.attributes }; } appendChild(...children: Array<BBCodeContentNode | BBCodeFragmentNode>) { const flattenedChildren: Array<BBCodeContentNode> = BBCodeNode.flattenChildren(children); const filteredChildren: FilteredChildren = this.filterChildren(flattenedChildren); const convertedChildren: Array<BBCodeContentNode> = this.convertChildren(filteredChildren.resolved); convertedChildren.forEach((node: BBCodeContentNode) => { node.remove(); node.setParent(this); this.children.push(node); }); if (Type.isArrayFilled(filteredChildren.unresolved)) { if (this.getScheme().isAllowedUnresolvedNodesHoisting()) { this.propagateChild(...filteredChildren.unresolved); } else { filteredChildren.unresolved.forEach((node: BBCodeContentNode) => { node.remove(); }); } } } prependChild(...children: Array<BBCodeContentNode | BBCodeFragmentNode>) { const flattenedChildren: Array<BBCodeContentNode> = BBCodeNode.flattenChildren(children); const filteredChildren: FilteredChildren = this.filterChildren(flattenedChildren); const convertedChildren: Array<BBCodeContentNode> = this.convertChildren(filteredChildren.resolved); convertedChildren.forEach((node: BBCodeContentNode) => { node.remove(); node.setParent(this); this.children.unshift(node); }); if (Type.isArrayFilled(filteredChildren.unresolved)) { if (this.getScheme().isAllowedUnresolvedNodesHoisting()) { this.propagateChild(...filteredChildren.unresolved); } else { filteredChildren.unresolved.forEach((node: BBCodeContentNode) => { node.remove(); }); } } } replaceChild(targetNode: BBCodeContentNode, ...children: Array<BBCodeContentNode | BBCodeFragmentNode>) { this.children = this.children.flatMap((node: BBCodeContentNode) => { if (node === targetNode) { node.setParent(null); const flattenedChildren: Array<BBCodeContentNode> = BBCodeNode.flattenChildren(children); const filteredChildren: FilteredChildren = this.filterChildren(flattenedChildren); const convertedChildren: Array<BBCodeContentNode> = this.convertChildren(filteredChildren.resolved); return convertedChildren.map((child: BBCodeContentNode) => { child.remove(); child.setParent(this); return child; }); } return node; }); } toStringValue(): string { const value: BBCodeElementNodeValue = this.getValue(); const encodedValue: string = this.getEncoder().encodeAttribute(value); return value ? `=${encodedValue}` : ''; } toStringAttributes(): string { return Object .entries(this.getAttributes()) .map(([key: string, attrValue: string]) => { const preparedKey: string = this.prepareCase(key); const encodedValue: string = this.getEncoder().encodeAttribute(attrValue); return attrValue ? `${preparedKey}=${encodedValue}` : preparedKey; }) .join(' '); } getContent(): string { return this.getChildren() .map((child: BBCodeContentNode) => { return child.toString(); }) .join(''); } getOpeningTag(): string { const displayedName: string = this.getDisplayedName(); const tagValue: BBCodeElementNodeValue = this.toStringValue(); const attributes: JsonObject = this.toStringAttributes(); const formattedAttributes: string = Type.isStringFilled(attributes) ? ` ${attributes}` : ''; return `[${displayedName}${tagValue}${formattedAttributes}]`; } getClosingTag(): string { return `[/${this.getDisplayedName()}]`; } clone(options: { deep: boolean } = {}): BBCodeElementNode { const children = (() => { if (options.deep) { return this.getChildren().map((child) => { return child.clone(options); }); } return []; })(); return this.getScheme().createElement({ name: this.getName(), void: this.isVoid(), value: this.getValue(), attributes: { ...this.getAttributes() }, children, }); } splitByChildIndex(index: number): Array<BBCodeElementNode | null> { if (!Type.isNumber(index)) { throw new TypeError('index is not a number'); } const childrenCount = this.getChildrenCount(); if (index < 0 || index > childrenCount) { throw new TypeError(`index '${index}' is out of range ${0}-${childrenCount}`); } const leftNode = (() => { if (index === childrenCount) { return this; } if (index === 0) { return null; } const leftChildren = this.getChildren().filter((child, childIndex) => { return childIndex < index; }); const node = this.clone(); node.setChildren(leftChildren); return node; })(); const rightNode = (() => { if (index === 0) { return this; } if (index === childrenCount) { return null; } const rightChildren = this.getChildren(); const node = this.clone(); node.setChildren(rightChildren); return node; })(); if (leftNode && rightNode) { this.replace(leftNode, rightNode); } return [leftNode, rightNode]; } getTagScheme(): BBCodeTagScheme { return super.getTagScheme(); } toString(): string { const tagScheme: BBCodeTagScheme = this.getTagScheme(); const stringifier: BBCodeNodeStringifier = tagScheme.getStringifier(); if (Type.isFunction(stringifier)) { const scheme: BBCodeScheme = this.getScheme(); return stringifier(this, scheme); } const openingTag: string = this.getOpeningTag(); const content: string = this.getContent(); if (this.isVoid()) { return `${openingTag}${content}`; } const closingTag: string = this.getClosingTag(); return `${openingTag}${content}${closingTag}`; } toJSON(): SerializedBBCodeElementNode { return { ...super.toJSON(), value: this.getValue(), attributes: this.getAttributes(), void: this.isVoid(), }; } }