import { Modificators, FormBufferOptions, FormBufferInsideOptions, ValueOfPreset, TypedModificatorOptions, IModificator } from './types'; import formBufferSymbols, { optionsSymbol, skipSymbol, delSymbol } from './symbols'; interface ModifAndOptions { modificator: IModificator; options: TypedModificatorOptions; } function fixOptions(options: FormBufferOptions): FormBufferInsideOptions { if (!options.defaultFormaters) options.defaultFormaters = {}; if (!options.global) options.global = {}; if (!options.inFormaters) options.inFormaters = {}; if (!options.outFormaters) options.outFormaters = {}; return options as FormBufferInsideOptions; } const modificators: Map> = new Map(); export default class FormBuffer

{ [optionsSymbol]: FormBufferInsideOptions; constructor(options: FormBufferOptions, data?: P) { this[optionsSymbol] = fixOptions(options); this.setValues(data); } static extend

(options: FormBufferOptions, data?: P): FormBufferExtended { const buffer = new FormBuffer(options, data); return buffer as FormBufferExtended; } static get modificators() { return modificators; } static addModificator(modificator: IModificator) { modificators.set(modificator.name, modificator); } static delModificator(modificatorName: string): boolean { return modificators.delete(modificatorName); } static getModificator(modificatorName: string): IModificator | undefined { return modificators.get(modificatorName); } setValues(data: any) { if (data && typeof data === 'object' && !Array.isArray(data)) { const options = this[optionsSymbol]; for (const key in options.preset) { if (key in FormBuffer.prototype) throw new Error(`[FormBuffer] Property name cannot have "${key}" value because it reserved`); let value = data[key]; const oldValue = (this as any)[key]; if (key in data) { const formater = options.inFormaters[key]; let formaterResult: any; if (formater) { formaterResult = formater(oldValue, value, this, formBufferSymbols); if (formaterResult !== skipSymbol) value = formaterResult; } if (!formater || formaterResult === skipSymbol) { const modificatorsAndOptions = this._getModificatorsForProp(key); for (const { modificator, options } of modificatorsAndOptions.values()) { if (!modificator.inFormater) continue; const resultFromModificator = modificator.inFormater(oldValue, value, options, this, formBufferSymbols); if (resultFromModificator === skipSymbol) continue; value = resultFromModificator; break; } } } else value = this.clearProperty(key); (this as any)[key] = value; } } else this.clear(); } getFormData(): P { const options = this[optionsSymbol]; const result = {}; for (const key in options.preset) { let value = (this as any)[key]; const formater = options.outFormaters[key]; let formaterResult: any; if (formater) { formaterResult = formater(value, this, formBufferSymbols); if (formaterResult !== skipSymbol) value = formaterResult; } if (!formater || formaterResult === skipSymbol) { const modificatorsAndOptions = this._getModificatorsForProp(key); for (const { modificator, options } of modificatorsAndOptions.values()) { if (!modificator.outFormater) continue; const resultFromModificator = modificator.outFormater(value, options, this, formBufferSymbols); if (resultFromModificator === skipSymbol) continue; value = resultFromModificator; break; } } if (value !== delSymbol) (result as P)[key] = value; } return result as P; } private _getModificatorsForProp(propKey: Extract): Map> { const result = new Map>(); const options = this[optionsSymbol]; const globalModificators = Object.keys(options.global).map(name => ({ name, isGlobal: true })); const propModificators = Object.keys(options.preset[propKey]).map(name => ({ name, isGlobal: false })); [...propModificators, ...globalModificators].forEach(({ name, isGlobal }) => { if (result.has(name)) return; const modificator = FormBuffer.getModificator(name); if (!modificator) throw new Error(`[FormBuffer] Modificator with name "${name}" does not exist`); const modifOptions = isGlobal ? options.global[name] : options.preset[propKey][name]; result.set(name, { modificator, options: modifOptions as TypedModificatorOptions }); }); return result; } clear() { for (const key in this[optionsSymbol].preset) { (this as any)[key] = this.clearProperty(key); } } clearProperty(key: Extract): ValueOfPreset

{ const options = this[optionsSymbol]; let value: any = undefined; const oldValue = (this as any)[key]; const defaultFormater = options.defaultFormaters[key]; let formaterResult: any; if (defaultFormater) { formaterResult = defaultFormater(oldValue, this, formBufferSymbols); if (formaterResult !== skipSymbol) value = formaterResult; } if (!defaultFormater || formaterResult === skipSymbol) { const modificatorsAndOptions = this._getModificatorsForProp(key); for (const { modificator, options } of modificatorsAndOptions.values()) { if (!modificator.defaultFormater) continue; const resultFromModificator = modificator.defaultFormater(oldValue, options, this, formBufferSymbols); if (resultFromModificator === skipSymbol) continue; value = resultFromModificator; break; } } if (value === undefined) throw new Error(`[FormBuffer] prop "${key}" haven't default value`); return value; } } export type FormBufferExtended

= FormBuffer & P;