Your IP : 52.15.110.198


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/bbcode/formatter/src/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/js/ui/bbcode/formatter/src/node-formatter.js

import { Type } from 'main.core';
import {
	BBCodeNode,
	typeof BBCodeElementNode,
	typeof BBCodeRootNode,
	typeof BBCodeTextNode,
} from 'ui.bbcode.model';
import { type FormatterData, typeof Formatter } from './formatter';

export type ConvertCallbackOptions = {
	node: BBCodeElementNode | BBCodeRootNode | BBCodeTextNode,
	formatter: Formatter,
	data: FormatterData,
};

export type ValidateCallbackOptions = ConvertCallbackOptions & {};
export type BeforeConvertCallbackOptions = ConvertCallbackOptions & {};
export type ForChildCallbackOptions = ConvertCallbackOptions & {
	element: HTMLElement,
};
export type AfterCallbackOptions = ForChildCallbackOptions & {};
export type FormatterCallbackResult = HTMLElement | Text | null;

export type NodeFormatterOptions = {
	name: string | Array<string>,
	convert: (ConvertCallbackOptions) => FormatterCallbackResult,
	validate?: (ValidateCallbackOptions) => boolean,
	before?: (BeforeConvertCallbackOptions) => BBCodeNode | null,
	after?: (AfterCallbackOptions) => FormatterCallbackResult,
	forChild?: (ForChildCallbackOptions) => FormatterCallbackResult,
};

type DefaultNodeConverterOptions = ConvertCallbackOptions | BeforeConvertCallbackOptions;
type DefaultElementConverterOptions = ForChildCallbackOptions | AfterCallbackOptions;

const nameSymbol: Symbol = Symbol('name');
const groupSymbol: Symbol = Symbol('group');
const validateSymbol: Symbol = Symbol('validate');
const beforeSymbol: Symbol = Symbol('before');
const convertSymbol: Symbol = Symbol('convert');
const forChildSymbol: Symbol = Symbol('forChild');
const afterSymbol: Symbol = Symbol('after');

const defaultValidator = () => true;
const defaultNodeConverter = ({ node }: DefaultNodeConverterOptions) => node;
const defaultElementConverter = ({ element }: DefaultElementConverterOptions) => element;

export class NodeFormatter
{
	[nameSymbol]: string = 'unknown';
	[groupSymbol]: ?Array<string> = null;
	[validateSymbol]: (ValidateCallbackOptions) => boolean;
	[beforeSymbol]: (BeforeConvertCallbackOptions) => FormatterCallbackResult = null;
	[convertSymbol]: (ConvertCallbackOptions) => FormatterCallbackResult = null;
	[forChildSymbol]: (ForChildCallbackOptions) => FormatterCallbackResult = null;
	[afterSymbol]: (AfterCallbackOptions) => FormatterCallbackResult = null;

	constructor(options: NodeFormatterOptions = {})
	{
		if (Type.isArray(options.name))
		{
			this[groupSymbol] = [...options.name];
		}
		else
		{
			this.setName(options.name);
		}

		this.setValidate(options.validate);
		this.setBefore(options.before);
		this.setConvert(options.convert);
		this.setForChild(options.forChild);
		this.setAfter(options.after);
	}

	setName(name: string)
	{
		if (!Type.isStringFilled(name))
		{
			throw new TypeError('Name is not a string');
		}

		this[nameSymbol] = name;
	}

	getName(): string
	{
		return this[nameSymbol];
	}

	setValidate(callback: (ValidateCallbackOptions) => boolean)
	{
		if (Type.isFunction(callback))
		{
			this[validateSymbol] = callback;
		}
		else
		{
			this[validateSymbol] = defaultValidator;
		}
	}

	validate(options: ValidateCallbackOptions): boolean
	{
		const result: ?boolean = this[validateSymbol](options);
		if (Type.isBoolean(result))
		{
			return result;
		}

		throw new TypeError(`Validate callback for "${this.getName()}" returned not boolean`);
	}

	setBefore(callback: (BeforeConvertCallbackOptions) => FormatterCallbackResult)
	{
		if (Type.isFunction(callback))
		{
			this[beforeSymbol] = callback;
		}
		else
		{
			this[beforeSymbol] = defaultNodeConverter;
		}
	}

	runBefore(options: BeforeConvertCallbackOptions): FormatterCallbackResult
	{
		const result: ?BBCodeNode = this[beforeSymbol](options);
		if (result instanceof BBCodeNode || Type.isNull(result))
		{
			return result;
		}

		throw new TypeError(`Before callback for "${this.getName()}" returned not null or BBCodeNode`);
	}

	setConvert(callback: (ConvertCallbackOptions) => FormatterCallbackResult)
	{
		if (!Type.isFunction(callback))
		{
			throw new TypeError('Convert is not a function');
		}

		this[convertSymbol] = callback;
	}

	runConvert(options: ConvertCallbackOptions): FormatterCallbackResult
	{
		const result: ?HTMLElement | Text = this[convertSymbol](options);
		if (Type.isDomNode(result) || Type.isNull(result))
		{
			return result;
		}

		throw new TypeError(`Convert callback for "${this.getName()}" returned not HTMLElement, Text or null`);
	}

	setForChild(callback: (ForChildCallbackOptions) => FormatterCallbackResult)
	{
		if (Type.isFunction(callback))
		{
			this[forChildSymbol] = callback;
		}
		else
		{
			this[forChildSymbol] = defaultElementConverter;
		}
	}

	runForChild(options: ForChildCallbackOptions): FormatterCallbackResult
	{
		const result: ?HTMLElement | Text = this[forChildSymbol](options);
		if (Type.isDomNode(result) || Type.isNull(result))
		{
			return result;
		}

		throw new TypeError(`ForChild callback for "${this.getName()}" returned not HTMLElement, Text or null`);
	}

	setAfter(callback: (AfterCallbackOptions) => FormatterCallbackResult)
	{
		if (Type.isFunction(callback))
		{
			this[afterSymbol] = callback;
		}
		else
		{
			this[afterSymbol] = defaultElementConverter;
		}
	}

	runAfter(options: AfterCallbackOptions): FormatterCallbackResult
	{
		const result: ?HTMLElement | Text = this[afterSymbol](options);
		if (Type.isDomNode(result) || Type.isNull(result))
		{
			return result;
		}

		throw new TypeError(`After callback for "${this.getName()}" returned not HTMLElement, Text or null`);
	}
}