/*!
 * Jodit Editor (https://xdsoft.net/jodit/)
 * Released under MIT see LICENSE.txt in the project root for license information.
 * Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
 */
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
        r = Reflect.decorate(decorators, target, key, desc);
    else
        for (var i = decorators.length - 1; i >= 0; i--)
            if (d = decorators[i])
                r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var Jodit_1;
import * as constants from "./core/constants.js";
import { FAT_MODE, IS_PROD, lang } from "./core/constants.js";
import { autobind, cache, cached, derive, throttle, watch } from "./core/decorators/index.js";
import { eventEmitter, instances, modules, pluginSystem } from "./core/global.js";
import { asArray, attr, callPromise, ConfigMerge, ConfigProto, css, error, isFunction, isJoditObject, isNumber, isPromise, isString, isVoid, kebabCase, markAsAtomic, normalizeKeyAliases, resolveElement, toArray, ucfirst } from "./core/helpers/index.js";
import { Ajax } from "./core/request/index.js";
import { Dlgs } from "./core/traits/dlgs.js";
import { Config } from "./config.js";
import { Create, Dom, History, Plugin, Selection, StatusBar, STATUSES, ViewWithToolbar } from "./modules/index.js";
const __defaultStyleDisplayKey = 'data-jodit-default-style-display';
const __defaultClassesKey = 'data-jodit-default-classes';
const NOEDIT = {
    contenteditable: false
};
let Jodit = Jodit_1 = class Jodit extends ViewWithToolbar {
    /** @override */
    className() {
        return 'Jodit';
    }
    /**
     * Return promise for ready actions
     * @example
     * ```js
     * const jodit = Jodit.make('#editor');
     * await jodit.waitForReady();
     * jodit.e.fire('someAsyncLoadedPluginEvent', (test) => {
     *   alert(test);
     * });
     * ```
     */
    waitForReady() {
        if (this.isReady) {
            return Promise.resolve(this);
        }
        return this.async.promise(resolve => {
            this.hookStatus('ready', () => resolve(this));
        });
    }
    /**
     * @deprecated I don't know why I wrote itp
     */
    static get ready() {
        return new Promise(resolve => {
            eventEmitter.on('joditready', resolve);
        });
    }
    /**
     * Plain text editor's value
     */
    get text() {
        if (this.editor) {
            return this.editor.innerText || '';
        }
        const div = this.createInside.div();
        div.innerHTML = this.getElementValue();
        return div.innerText || '';
    }
    /**
     * Return a default timeout period in milliseconds for some debounce or throttle functions.
     * By default, `{history.timeout}` options
     */
    get defaultTimeout() {
        return isNumber(this.o.defaultTimeout)
            ? this.o.defaultTimeout
            : Config.defaultOptions.defaultTimeout;
    }
    /**
     * Method wrap usual object in Object helper for prevent deep object merging in options*
     * ```js
     * const editor = Jodit.make('#editor', {
     * 	controls: {
     * 		fontsize: {
     * 			list: Jodit.atom([8, 9, 10])
     * 		}
     * 	}
     * });
     * ```
     * In this case, the array [8, 9, 10] will not be combined with other arrays, but will replace them
     */
    static atom(object) {
        return markAsAtomic(object);
    }
    /**
     * Factory for creating Jodit instance
     */
    static make(element, options) {
        return new this(element, options);
    }
    /**
     * Checks if the element has already been initialized when for Jodit
     */
    static isJoditAssigned(element) {
        return (element &&
            isJoditObject(element.component) &&
            !element.component.isInDestruct);
    }
    /**
     * Default settings
     */
    static get defaultOptions() {
        return Config.defaultOptions;
    }
    /**
     * Deep-merges partial options into the global defaults without replacing
     * top-level objects. This lets you patch nested settings (e.g. a single
     * button inside `controls`) without losing the rest:
     *
     * ```js
     * // Add a custom button — all existing controls remain untouched
     * Jodit.configure({
     *   controls: {
     *     myButton: {
     *       icon: 'pencil',
     *       command: 'selectall'
     *     }
     *   }
     * });
     *
     * // Override only the `group` of an existing button
     * Jodit.configure({
     *   controls: {
     *     someButton: { group: 'custom' }
     *   }
     * });
     *
     * // Works with any nested option
     * Jodit.configure({
     *   createAttributes: {
     *     div: { class: 'my-class' }
     *   }
     * });
     *
     * // Use Jodit.atom() to replace a nested value entirely instead of merging
     * Jodit.configure({
     *   controls: {
     *     fontsize: {
     *       list: Jodit.atom([8, 9, 10])
     *     }
     *   }
     * });
     * ```
     *
     * @see {@link ConfigMerge} for the merge algorithm
     * @see {@link ConfigProto} for per-instance prototype-based merge used at editor creation time
     */
    static configure(options) {
        ConfigMerge(Jodit_1.defaultOptions, options);
    }
    get createInside() {
        return new Create(() => this.ed, this.o.createAttributes);
    }
    __setPlaceField(field, value) {
        if (!this.currentPlace) {
            this.currentPlace = {};
            this.places = [this.currentPlace];
        }
        this.currentPlace[field] = value;
    }
    /**
     * element It contains source element
     */
    get element() {
        return this.currentPlace.element;
    }
    /**
     * editor It contains the root element editor
     */
    get editor() {
        return this.currentPlace.editor;
    }
    set editor(editor) {
        this.__setPlaceField('editor', editor);
    }
    /**
     * Container for all staff
     */
    get container() {
        return this.currentPlace.container;
    }
    set container(container) {
        this.__setPlaceField('container', container);
    }
    /**
     * workplace It contains source and wysiwyg editors
     */
    get workplace() {
        return this.currentPlace.workplace;
    }
    get message() {
        return this.getMessageModule(this.workplace);
    }
    /**
     * Statusbar module
     */
    get statusbar() {
        return this.currentPlace.statusbar;
    }
    /**
     * iframe Iframe for iframe mode
     */
    get iframe() {
        return this.currentPlace.iframe;
    }
    set iframe(iframe) {
        this.__setPlaceField('iframe', iframe);
    }
    get history() {
        return this.currentPlace.history;
    }
    /**
     * In iframe mode editor's window can be different by owner
     */
    get editorWindow() {
        return this.currentPlace.editorWindow;
    }
    set editorWindow(win) {
        this.__setPlaceField('editorWindow', win);
    }
    /**
     * Alias for this.ew
     */
    get ew() {
        return this.editorWindow;
    }
    /**
     * In iframe mode editor's window can be different by owner
     */
    get editorDocument() {
        return this.currentPlace.editorWindow.document;
    }
    /**
     * Alias for this.ew
     */
    get ed() {
        return this.editorDocument;
    }
    /**
     * options All Jodit settings default + second arguments of constructor
     */
    get options() {
        return this.currentPlace.options;
    }
    set options(opt) {
        this.__options = opt;
        this.__setPlaceField('options', opt);
    }
    /**
     * Alias for this.selection
     */
    get s() {
        return this.selection;
    }
    get uploader() {
        return this.getInstance('Uploader', this.o.uploader);
    }
    get filebrowser() {
        const jodit = this;
        const options = ConfigProto({
            defaultTimeout: jodit.defaultTimeout,
            uploader: jodit.o.uploader,
            language: jodit.o.language,
            license: jodit.o.license,
            theme: jodit.o.theme,
            shadowRoot: jodit.o.shadowRoot,
            defaultCallback(data) {
                if (data.files && data.files.length) {
                    data.files.forEach((file, i) => {
                        const url = data.baseurl + file;
                        const isImage = data.isImages
                            ? data.isImages[i]
                            : false;
                        if (isImage) {
                            jodit.s.insertImage(url, null, jodit.o.imageDefaultWidth);
                        }
                        else {
                            jodit.s.insertNode(jodit.createInside.fromHTML(`<a href='${url}' title='${url}'>${url}</a>`));
                        }
                    });
                }
            }
        }, this.o.filebrowser);
        return jodit.getInstance('FileBrowser', options);
    }
    /**
     * Editor's mode
     */
    get mode() {
        return this.__mode;
    }
    set mode(mode) {
        this.setMode(mode);
    }
    /**
     * Return real HTML value from WYSIWYG editor.
     * @internal
     */
    getNativeEditorValue() {
        const value = this.e.fire('beforeGetNativeEditorValue');
        if (isString(value)) {
            return value;
        }
        if (this.editor) {
            return this.editor.innerHTML;
        }
        return this.getElementValue();
    }
    /**
     * Set value to native editor
     */
    setNativeEditorValue(value) {
        const data = {
            value
        };
        if (this.e.fire('beforeSetNativeEditorValue', data)) {
            return;
        }
        if (this.editor) {
            this.editor.innerHTML = data.value;
        }
    }
    /**
     * HTML value
     */
    get value() {
        return this.getEditorValue();
    }
    set value(html) {
        this.setEditorValue(html);
        // @ts-ignore Internal method
        this.history.__processChanges();
    }
    synchronizeValues() {
        this.__imdSynchronizeValues();
    }
    /**
     * This is an internal method, do not use it in your applications.
     * @private
     * @internal
     */
    __imdSynchronizeValues() {
        this.setEditorValue();
    }
    /**
     * Return editor value
     */
    getEditorValue(removeSelectionMarkers = true, consumer) {
        /**
         * Triggered before getEditorValue executed.
         * If returned not undefined, getEditorValue will return this value
         * @example
         * ```javascript
         * var editor = Jodit.make("#redactor");
         * editor.e.on('beforeGetValueFromEditor', function () {
         *     return editor.editor.innerHTML.replace(/a/g, 'b');
         * });
         * ```
         */
        let value;
        value = this.e.fire('beforeGetValueFromEditor', consumer);
        if (value !== undefined) {
            return value;
        }
        value = this.getNativeEditorValue().replace(constants.INVISIBLE_SPACE_REG_EXP(), '');
        if (removeSelectionMarkers) {
            value = value.replace(/<span[^>]+id="jodit-selection_marker_[^>]+><\/span>/g, '');
        }
        if (value === '<br>') {
            value = '';
        }
        /**
         * Triggered after getEditorValue got value from wysiwyg.
         * It can change new_value.value
         *
         * @example
         * ```javascript
         * var editor = Jodit.make("#redactor");
         * editor.e.on('afterGetValueFromEditor', function (new_value) {
         *     new_value.value = new_value.value.replace('a', 'b');
         * });
         * ```
         */
        const new_value = { value };
        this.e.fire('afterGetValueFromEditor', new_value, consumer);
        return new_value.value;
    }
    /**
     * Set editor html value and if set sync fill source element value
     * When method was called without arguments - it is a simple way to synchronize editor to element
     */
    setEditorValue(value) {
        /**
         * Triggered before getEditorValue set value to wysiwyg.
         * @example
         * ```javascript
         * var editor = Jodit.make("#redactor");
         * editor.e.on('beforeSetValueToEditor', function (old_value) {
         *     return old_value.value.replace('a', 'b');
         * });
         * editor.e.on('beforeSetValueToEditor', function () {
         *     return false; // disable setEditorValue method
         * });
         * ```
         */
        const newValue = this.e.fire('beforeSetValueToEditor', value);
        if (newValue === false) {
            return;
        }
        if (isString(newValue)) {
            value = newValue;
        }
        if (!this.editor) {
            if (value !== undefined) {
                this.__setElementValue(value);
            }
            return; // try change value before init or after destruct
        }
        if (!isString(value) && !isVoid(value)) {
            throw error('value must be string');
        }
        if (!isVoid(value) && this.getNativeEditorValue() !== value) {
            this.setNativeEditorValue(value);
        }
        this.e.fire('postProcessSetEditorValue');
        const old_value = this.getElementValue(), new_value = this.getEditorValue();
        if (!this.__isSilentChange &&
            old_value !== new_value &&
            this.__callChangeCount < constants.SAFE_COUNT_CHANGE_CALL) {
            this.__setElementValue(new_value);
            this.__callChangeCount += 1;
            if (!IS_PROD && this.__callChangeCount > 4) {
                console.warn('Too many recursive changes', new_value, old_value);
            }
            try {
                // @ts-ignore Internal method
                this.history.__upTick();
                this.e.fire('change', new_value, old_value);
                this.e.fire(this.history, 'change', new_value, old_value);
            }
            finally {
                this.__callChangeCount = 0;
            }
        }
    }
    /**
     * If some plugin changes the DOM directly, then you need to update the content of the original element
     */
    updateElementValue() {
        this.__setElementValue(this.getEditorValue());
    }
    /**
     * Return source element value
     */
    getElementValue() {
        return this.element.value !== undefined
            ? this.element.value
            : this.element.innerHTML;
    }
    __setElementValue(value) {
        if (!isString(value)) {
            throw error('value must be string');
        }
        if (this.element !== this.container &&
            value !== this.getElementValue()) {
            const data = { value };
            const res = this.e.fire('beforeSetElementValue', data);
            callPromise(res, () => {
                if (this.element.value !== undefined) {
                    this.element.value = data.value;
                }
                else {
                    this.element.innerHTML = data.value;
                }
                this.e.fire('afterSetElementValue', data);
            });
        }
    }
    /**
     * Register custom handler for command
     *
     * @example
     * ```javascript
     * var jodit = Jodit.make('#editor);
     *
     * jodit.setEditorValue('test test test');
     *
     * jodit.registerCommand('replaceString', function (command, needle, replace) {
     *      var value = this.getEditorValue();
     *      this.setEditorValue(value.replace(needle, replace));
     *      return false; // stop execute native command
     * });
     *
     * jodit.execCommand('replaceString', 'test', 'stop');
     *
     * console.log(jodit.value); // stop test
     *
     * // and you can add hotkeys for command
     * jodit.registerCommand('replaceString', {
     *    hotkeys: 'ctrl+r',
     *    exec: function (command, needle, replace) {
     *     var value = this.getEditorValue();
     *     this.setEditorValue(value.replace(needle, replace));
     *    }
     * });
     *
     * ```
     */
    registerCommand(commandNameOriginal, command, options) {
        const commandName = commandNameOriginal.toLowerCase();
        let commands = this.commands.get(commandName);
        if (commands === undefined) {
            commands = [];
            this.commands.set(commandName, commands);
        }
        commands.push(command);
        if (!isFunction(command)) {
            const hotkeys = this.o.commandToHotkeys[commandName] ||
                this.o.commandToHotkeys[commandNameOriginal] ||
                command.hotkeys;
            if (hotkeys) {
                this.registerHotkeyToCommand(hotkeys, commandName, options === null || options === void 0 ? void 0 : options.stopPropagation);
            }
        }
        return this;
    }
    /**
     * Register hotkey for command
     */
    registerHotkeyToCommand(hotkeys, commandName, shouldStop = true) {
        const shortcuts = asArray(hotkeys)
            .map(normalizeKeyAliases)
            .map(hotkey => hotkey + '.hotkey')
            .join(' ');
        this.e
            .off(shortcuts)
            .on(shortcuts, (type, stop) => {
            if (stop) {
                stop.shouldStop = shouldStop !== null && shouldStop !== void 0 ? shouldStop : true;
            }
            return this.execCommand(commandName); // because need `beforeCommand`
        });
    }
    /**
     * Execute command editor
     *
     * @param command - command. It supports all the
     * @see https://developer.mozilla.org/ru/docs/Web/API/Document/execCommand#commands and a number of its own
     * for example applyStyleProperty. Comand fontSize receives the second parameter px,
     * formatBlock and can take several options
     * @example
     * ```javascript
     * this.execCommand('fontSize', 12); // sets the size of 12 px
     * this.execCommand('underline');
     * this.execCommand('formatBlock', 'p'); // will be inserted paragraph
     * ```
     */
    execCommand(command, showUI, value, ...args) {
        if (!this.s.isFocused()) {
            this.s.focus();
        }
        if (this.o.readonly &&
            !this.o.allowCommandsInReadOnly.includes(command)) {
            return;
        }
        let result;
        command = command.toLowerCase();
        /**
         * Called before any command
         * @param command - Command name in lowercase
         * @param second - The second parameter for the command
         * @param third - The third option is for the team
         * @example
         * ```javascript
         * parent.e.on('beforeCommand', function (command) {
         *  if (command === 'justifyCenter') {
         *      var p = parent.c.element('p')
         *      parent.s.insertNode(p)
         *      parent.s.setCursorIn(p);
         *      p.style.textAlign = 'justyfy';
         *      return false; // break executes native command
         *  }
         * })
         * ```
         */
        result = this.e.fire(`beforeCommand${ucfirst(command)}`, showUI, value, ...args);
        if (result !== false) {
            result = this.e.fire('beforeCommand', command, showUI, value, ...args);
        }
        if (result !== false) {
            result = this.__execCustomCommands(command, showUI, value, ...args);
        }
        if (result !== false) {
            this.s.focus();
            try {
                result = this.nativeExecCommand(command, showUI, value);
            }
            catch (e) {
                if (!IS_PROD) {
                    throw e;
                }
            }
        }
        /**
         * It called after any command
         * @param command - name command
         * @param second - The second parameter for the command
         * @param third - The third option is for the team
         */
        this.e.fire('afterCommand', command, showUI, value);
        this.__imdSynchronizeValues(); // synchrony
        return result;
    }
    /**
     * Exec native command
     */
    nativeExecCommand(command, showUI, value) {
        this.__isSilentChange = true;
        try {
            return this.ed.execCommand(command, showUI, value);
        }
        finally {
            this.__isSilentChange = false;
        }
    }
    __execCustomCommands(commandName, second, third, ...args) {
        commandName = commandName.toLowerCase();
        const commands = this.commands.get(commandName);
        if (commands !== undefined) {
            let result;
            commands.forEach((command) => {
                let callback;
                if (isFunction(command)) {
                    callback = command;
                }
                else {
                    callback = command.exec;
                }
                const resultCurrent = callback.call(this, commandName, second, third, ...args);
                if (resultCurrent !== undefined) {
                    result = resultCurrent;
                }
            });
            return result;
        }
    }
    /**
     * Disable selecting
     */
    lock(name = 'any') {
        if (super.lock(name)) {
            this.__selectionLocked = this.s.save();
            this.s.clear();
            this.container.classList.add('jodit_lock');
            this.e.fire('lock', true);
            return true;
        }
        return false;
    }
    /**
     * Enable selecting
     */
    unlock() {
        if (super.unlock()) {
            this.container.classList.remove('jodit_lock');
            if (this.__selectionLocked) {
                this.s.restore();
            }
            this.e.fire('lock', false);
            return true;
        }
        return false;
    }
    /**
     * Return current editor mode: Jodit.MODE_WYSIWYG, Jodit.MODE_SOURCE or Jodit.MODE_SPLIT
     */
    getMode() {
        return this.mode;
    }
    isEditorMode() {
        return this.getRealMode() === constants.MODE_WYSIWYG;
    }
    /**
     * Return current real work mode. When editor in MODE_SOURCE or MODE_WYSIWYG it will
     * return them, but then editor in MODE_SPLIT it will return MODE_SOURCE if
     * Textarea(CodeMirror) focused or MODE_WYSIWYG otherwise
     *
     * @example
     * ```javascript
     * var editor = Jodit.make('#editor');
     * console.log(editor.getRealMode());
     * ```
     */
    getRealMode() {
        if (this.getMode() !== constants.MODE_SPLIT) {
            return this.getMode();
        }
        const active = this.od.activeElement;
        if (active &&
            (active === this.iframe ||
                Dom.isOrContains(this.editor, active) ||
                Dom.isOrContains(this.toolbar.container, active))) {
            return constants.MODE_WYSIWYG;
        }
        return constants.MODE_SOURCE;
    }
    /**
     * Set current mode
     */
    setMode(mode) {
        const oldMode = this.getMode();
        const data = {
            mode: parseInt(mode.toString(), 10)
        }, modeClasses = [
            'jodit-wysiwyg_mode',
            'jodit-source__mode',
            'jodit_split_mode'
        ];
        /**
         * Triggered before setMode executed. If returned false method stopped
         * @param data - PlainObject `{mode: {string}}` In handler you can change data.mode
         * @example
         * ```javascript
         * var editor = Jodit.make("#redactor");
         * editor.e.on('beforeSetMode', function (data) {
         *     data.mode = Jodit.MODE_SOURCE; // not respond to the mode change. Always make the source code mode
         * });
         * ```
         */
        if (this.e.fire('beforeSetMode', data) === false) {
            return;
        }
        this.__mode = [
            constants.MODE_SOURCE,
            constants.MODE_WYSIWYG,
            constants.MODE_SPLIT
        ].includes(data.mode)
            ? data.mode
            : constants.MODE_WYSIWYG;
        if (this.o.saveModeInStorage) {
            this.storage.set('jodit_default_mode', this.mode);
        }
        modeClasses.forEach(className => {
            this.container.classList.remove(className);
        });
        this.container.classList.add(modeClasses[this.mode - 1]);
        /**
         * Triggered after setMode executed
         * @example
         * ```javascript
         * var editor = Jodit.make("#redactor");
         * editor.e.on('afterSetMode', function () {
         *     editor.setEditorValue(''); // clear editor's value after change mode
         * });
         * ```
         */
        if (oldMode !== this.getMode()) {
            this.e.fire('afterSetMode');
        }
    }
    /**
     * Toggle editor mode WYSIWYG to TEXTAREA(CodeMirror) to SPLIT(WYSIWYG and TEXTAREA) to again WYSIWYG
     *
     * @example
     * ```javascript
     * var editor = Jodit.make('#editor');
     * editor.toggleMode();
     * ```
     */
    toggleMode() {
        let mode = this.getMode();
        if ([
            constants.MODE_SOURCE,
            constants.MODE_WYSIWYG,
            this.o.useSplitMode ? constants.MODE_SPLIT : 9
        ].includes(mode + 1)) {
            mode += 1;
        }
        else {
            mode = constants.MODE_WYSIWYG;
        }
        this.setMode(mode);
    }
    /**
     * Switch on/off the editor into the disabled state.
     * When disabled, the user is not able to change the editor content
     * This function firing the `disabled` event.
     */
    setDisabled(isDisabled) {
        this.o.disabled = isDisabled;
        const readOnly = this.__wasReadOnly;
        this.setReadOnly(isDisabled || readOnly);
        this.__wasReadOnly = readOnly;
        if (this.editor) {
            this.editor.setAttribute('aria-disabled', isDisabled.toString());
            this.container.classList.toggle('jodit_disabled', isDisabled);
            this.e.fire('disabled', isDisabled);
        }
    }
    /**
     * Return true if editor in disabled mode
     */
    getDisabled() {
        return this.o.disabled;
    }
    /**
     * Switch on/off the editor into the read-only state.
     * When in readonly, the user is not able to change the editor content, but can still
     * use some editor functions (show source code, print content, or seach).
     * This function firing the `readonly` event.
     */
    setReadOnly(isReadOnly) {
        if (this.__wasReadOnly === isReadOnly) {
            return;
        }
        this.__wasReadOnly = isReadOnly;
        this.o.readonly = isReadOnly;
        if (isReadOnly) {
            this.editor && this.editor.removeAttribute('contenteditable');
        }
        else {
            this.editor && this.editor.setAttribute('contenteditable', 'true');
        }
        this.e && this.e.fire('readonly', isReadOnly);
    }
    /**
     * Return true if editor in read-only mode
     */
    getReadOnly() {
        return this.o.readonly;
    }
    focus() {
        if (this.getMode() !== constants.MODE_SOURCE) {
            this.s.focus();
        }
    }
    get isFocused() {
        return this.s.isFocused();
    }
    /**
     * Hook before init
     */
    beforeInitHook() {
        // do nothing
    }
    /**
     * Hook after init
     */
    afterInitHook() {
        // do nothing
    }
    /** @override **/
    initOptions(options) {
        this.options = (ConfigProto(options || {}, Config.defaultOptions));
    }
    /** @override **/
    initOwners() {
        // in iframe, it can be changed
        this.editorWindow = this.o.ownerWindow;
        this.ownerWindow = this.o.ownerWindow;
    }
    /**
     * Create instance of Jodit
     *
     * @param element - Selector or HTMLElement
     * @param options - Editor's options
     */
    constructor(element, options) {
        super(options, true);
        /**
         * Define if object is Jodit
         */
        this.isJodit = true;
        this.commands = new Map();
        this.__selectionLocked = null;
        this.__wasReadOnly = false;
        /**
         * Editor has focus in this time
         */
        this.editorIsActive = false;
        this.__mode = constants.MODE_WYSIWYG;
        this.__callChangeCount = 0;
        /**
         * Don't raise a change event
         */
        this.__isSilentChange = false;
        this.currentPlace = {
            options: this.__options,
            container: this.__container
        };
        this.places = [];
        this.__elementToPlace = new Map();
        try {
            const elementSource = resolveElement(element, this.options.shadowRoot || this.od);
            if (Jodit_1.isJoditAssigned(elementSource)) {
                // @ts-ignore
                return elementSource.component;
            }
        }
        catch (e) {
            if (!IS_PROD) {
                throw e;
            }
            this.destruct();
            throw e;
        }
        this.setStatus(STATUSES.beforeInit);
        this.id =
            attr(resolveElement(element, this.o.shadowRoot || this.od), 'id') ||
                new Date().getTime().toString();
        instances[this.id] = this;
        this.attachEvents(options);
        this.e.on(this.ow, 'resize', () => {
            if (this.e) {
                this.e.fire('resize');
            }
        });
        this.e.on('prepareWYSIWYGEditor', this.__prepareWYSIWYGEditor);
        this.selection = new Selection(this);
        const beforeInitHookResult = this.beforeInitHook();
        callPromise(beforeInitHookResult, () => {
            if (this.isInDestruct) {
                return;
            }
            this.e.fire('beforeInit', this);
            pluginSystem.__init(this);
            this.e.fire('afterPluginSystemInit', this);
            this.e.on('changePlace', () => {
                this.setReadOnly(this.o.readonly);
                this.setDisabled(this.o.disabled);
            });
            this.places.length = 0;
            const addPlaceResult = this.addPlace(element, options);
            instances[this.id] = this;
            const init = () => {
                if (this.isInDestruct) {
                    return;
                }
                if (this.e) {
                    this.e.fire('afterInit', this);
                }
                callPromise(this.afterInitHook());
                this.setStatus(STATUSES.ready);
                this.e.fire('afterConstructor', this);
            };
            callPromise(addPlaceResult, init);
        });
    }
    /**
     * Create and init current editable place
     */
    addPlace(source, options) {
        const element = resolveElement(source, this.o.shadowRoot || this.od);
        this.attachEvents(options);
        if (element.attributes) {
            toArray(element.attributes).forEach((attr) => {
                const name = attr.name;
                let value = attr.value;
                if (Config.defaultOptions[name] !== undefined &&
                    (!options || options[name] === undefined)) {
                    if (['readonly', 'disabled'].indexOf(name) !== -1) {
                        value = value === '' || value === 'true';
                    }
                    if (/^[0-9]+(\.)?([0-9]+)?$/.test(value.toString())) {
                        value = Number(value);
                    }
                    this.options[name] = value;
                }
            });
        }
        let container = this.c.div('jodit-container');
        container.classList.add('jodit');
        container.classList.add('jodit-container');
        container.classList.add(`jodit_theme_${this.o.theme || 'default'}`);
        addClassNames(this.o.className, container);
        if (this.o.containerStyle) {
            css(container, this.o.containerStyle);
        }
        const { styleValues } = this.o;
        Object.keys(styleValues).forEach(key => {
            const property = kebabCase(key);
            container.style.setProperty(`--jd-${property}`, styleValues[key]);
        });
        container.setAttribute('contenteditable', 'false');
        let buffer = null;
        if (this.o.inline) {
            if (['TEXTAREA', 'INPUT'].indexOf(element.nodeName) === -1) {
                container = element;
                element.setAttribute(__defaultClassesKey, element.className.toString());
                buffer = container.innerHTML;
                container.innerHTML = '';
            }
            container.classList.add('jodit_inline');
            container.classList.add('jodit-container');
        }
        // actual for inline mode
        if (element !== container) {
            // hide source element
            if (element.style.display) {
                element.setAttribute(__defaultStyleDisplayKey, element.style.display);
            }
            element.style.display = 'none';
        }
        const SLOT = 'workplace-slot';
        const topSlot = this.c.div(this.getFullElName(SLOT, 'top'), NOEDIT);
        container.appendChild(topSlot);
        const centerSlot = this.c.div(this.getFullElName(SLOT, 'center'), NOEDIT);
        const workplace = this.c.div('jodit-workplace', NOEDIT);
        const leftSlot = this.c.div(this.getFullElName(SLOT, 'left'), NOEDIT);
        const rightSlot = this.c.div(this.getFullElName(SLOT, 'right'), NOEDIT);
        centerSlot.appendChild(leftSlot);
        centerSlot.appendChild(workplace);
        centerSlot.appendChild(rightSlot);
        container.appendChild(centerSlot);
        const bottomPanel = this.c.div(this.getFullElName(SLOT, 'bottom'), NOEDIT);
        container.appendChild(bottomPanel);
        if (element.parentNode && element !== container) {
            element.parentNode.insertBefore(container, element);
        }
        Object.defineProperty(element, 'component', {
            enumerable: false,
            configurable: true,
            value: this
        });
        const editor = this.c.div('jodit-wysiwyg', {
            contenteditable: true,
            'aria-disabled': false,
            tabindex: this.o.tabIndex
        });
        workplace.appendChild(editor);
        const currentPlace = {
            editor,
            element,
            container,
            workplace,
            slots: {
                top: topSlot,
                bottom: bottomPanel,
                center: centerSlot,
                left: leftSlot,
                right: rightSlot
            },
            statusbar: new StatusBar(this, container),
            options: this.isReady
                ? ConfigProto(options || {}, Config.defaultOptions)
                : this.options,
            history: new History(this),
            editorWindow: this.ow
        };
        this.__elementToPlace.set(editor, currentPlace);
        this.setCurrentPlace(currentPlace);
        this.places.push(currentPlace);
        this.setNativeEditorValue(this.getElementValue()); // Init value
        const initResult = this.__initEditor(buffer);
        const opt = this.options;
        const init = () => {
            if (opt.enableDragAndDropFileToEditor &&
                opt.uploader &&
                (opt.uploader.url || opt.uploader.insertImageAsBase64URI)) {
                this.uploader.bind(this.editor);
            }
            // in initEditor - the editor could change
            if (!this.__elementToPlace.get(this.editor)) {
                this.__elementToPlace.set(this.editor, currentPlace);
            }
            this.e.fire('afterAddPlace', currentPlace);
        };
        return callPromise(initResult, init);
    }
    addDisclaimer(elm) {
        this.workplace.appendChild(elm);
    }
    /**
     * Set current place object
     */
    setCurrentPlace(place) {
        if (this.currentPlace === place) {
            return;
        }
        if (!this.isEditorMode()) {
            this.setMode(constants.MODE_WYSIWYG);
        }
        this.currentPlace = place;
        this.buildToolbar();
        if (this.isReady) {
            this.e.fire('changePlace', place);
        }
    }
    __initEditor(buffer) {
        const result = this.__createEditor();
        return callPromise(result, () => {
            if (this.isInDestruct) {
                return;
            }
            // syncro
            if (this.element !== this.container) {
                const value = this.getElementValue();
                if (value !== this.getEditorValue()) {
                    this.setEditorValue(value);
                }
            }
            else {
                buffer != null && this.setEditorValue(buffer); // inline mode
            }
            let mode = this.o.defaultMode;
            if (this.o.saveModeInStorage) {
                const localMode = this.storage.get('jodit_default_mode');
                if (typeof localMode === 'string') {
                    mode = parseInt(localMode, 10);
                }
            }
            this.setMode(mode);
            if (this.o.readonly) {
                this.__wasReadOnly = false;
                this.setReadOnly(true);
            }
            if (this.o.disabled) {
                this.setDisabled(true);
            }
            // if enter plugin isn't installed
            try {
                this.ed.execCommand('defaultParagraphSeparator', false, this.o.enter.toLowerCase());
            }
            catch (_a) { }
        });
    }
    /**
     * Create main DIV element and replace source textarea
     */
    __createEditor() {
        const defaultEditorArea = this.editor;
        const stayDefault = this.e.fire('createEditor', this);
        return callPromise(stayDefault, () => {
            if (this.isInDestruct) {
                return;
            }
            if (stayDefault === false || isPromise(stayDefault)) {
                Dom.safeRemove(defaultEditorArea);
            }
            addClassNames(this.o.editorClassName, this.editor);
            if (this.o.style) {
                css(this.editor, this.o.style);
            }
            this.e
                .on('synchro', () => {
                this.setEditorValue();
            })
                .on('focus', () => {
                this.editorIsActive = true;
            })
                .on('blur', () => (this.editorIsActive = false));
            this.__prepareWYSIWYGEditor();
            if (this.o.triggerChangeEvent) {
                this.e.on('change', this.async.debounce(() => {
                    this.e && this.e.fire(this.element, 'change');
                }, this.defaultTimeout));
            }
        });
    }
    /**
     * Attach some native event listeners
     */
    __prepareWYSIWYGEditor() {
        const { editor } = this;
        // direction
        if (this.o.direction) {
            const direction = this.o.direction.toLowerCase() === 'rtl' ? 'rtl' : 'ltr';
            this.editor.style.direction = direction;
            this.editor.setAttribute('dir', direction);
            this.container.style.direction = direction;
            this.container.setAttribute('dir', direction);
            this.toolbar.setDirection(direction);
        }
        // proxy events
        this.e
            .on(editor, 'mousedown touchstart focus', () => {
            const place = this.__elementToPlace.get(editor);
            if (place) {
                this.setCurrentPlace(place);
            }
        })
            .on(editor, 'compositionend', this.synchronizeValues)
            .on(editor, 'selectionchange selectionstart keydown keyup input keypress dblclick mousedown mouseup ' +
            'click copy cut dragstart drop dragover paste resize touchstart touchend focus blur', (event) => {
            if (this.o.readonly || this.__isSilentChange) {
                return;
            }
            const w = this.ew;
            if (event instanceof w.KeyboardEvent &&
                event.isComposing) {
                return;
            }
            if (this.e && this.e.fire) {
                if (this.e.fire(event.type, event) === false) {
                    return false;
                }
                this.synchronizeValues();
            }
        })
            .on(this.ow, 'mouseup', (event) => {
            if (this.o.readonly || this.__isSilentChange) {
                return;
            }
            // When a selection is started inside the editor and the
            // mouse button is released outside of it, the editable
            // area never receives the `mouseup` event, so the toolbar
            // state (active buttons) is not recalculated. Re-fire the
            // event manually for that case while the selection still
            // belongs to the editor. See #1251
            const target = event.target;
            const insideEditor = Boolean(target &&
                isNumber(target.nodeType) &&
                editor.contains(target));
            if (insideEditor || !this.s.isInsideArea) {
                return;
            }
            this.e.fire('changeSelection');
            this.synchronizeValues();
        });
    }
    fetch(url, options) {
        const ajax = new Ajax({
            url,
            ...options
        }, this.o.defaultAjaxOptions);
        const destroy = () => {
            this.e.off('beforeDestruct', destroy);
            this.progressbar.progress(100).hide();
            ajax.destruct();
        };
        this.e.one('beforeDestruct', destroy);
        this.progressbar.show().progress(30);
        const promise = ajax.send();
        promise.finally(destroy).catch(() => null);
        return promise;
    }
    /**
     * Jodit's Destructor. Remove editor, and return source input
     */
    destruct() {
        var _a, _b;
        if (this.isInDestruct) {
            return;
        }
        this.setStatus(STATUSES.beforeDestruct);
        this.__elementToPlace.clear();
        (_a = cached(this, 'storage')) === null || _a === void 0 ? void 0 : _a.clear();
        (_b = cached(this, 'buffer')) === null || _b === void 0 ? void 0 : _b.clear();
        this.commands.clear();
        this.__selectionLocked = null;
        this.e.off(this.ow, 'resize');
        this.e.off(this.ow);
        this.e.off(this.od);
        this.e.off(this.od.body);
        const tmpValue = this.editor ? this.getEditorValue() : '';
        this.places.forEach(({ container, workplace, statusbar, element, iframe, editor, history }) => {
            if (!element) {
                return;
            }
            if (element !== container) {
                if (element.hasAttribute(__defaultStyleDisplayKey)) {
                    const display = attr(element, __defaultStyleDisplayKey);
                    if (display) {
                        element.style.display = display;
                        element.removeAttribute(__defaultStyleDisplayKey);
                    }
                }
                else {
                    element.style.display = '';
                }
            }
            else {
                if (element.hasAttribute(__defaultClassesKey)) {
                    element.className =
                        attr(element, __defaultClassesKey) || '';
                    element.removeAttribute(__defaultClassesKey);
                }
            }
            if (element.hasAttribute('style') && !attr(element, 'style')) {
                element.removeAttribute('style');
            }
            statusbar.destruct();
            this.e.off(container);
            this.e.off(element);
            this.e.off(editor);
            Dom.safeRemove(workplace);
            Dom.safeRemove(editor);
            if (container !== element) {
                Dom.safeRemove(container);
            }
            Object.defineProperty(element, 'component', {
                enumerable: false,
                configurable: true,
                value: null
            });
            Dom.safeRemove(iframe);
            // inline mode
            if (container === element) {
                element.innerHTML = tmpValue;
            }
            history.destruct();
        });
        this.places.length = 0;
        this.currentPlace = {};
        delete instances[this.id];
        super.destruct();
    }
};
Jodit.fatMode = FAT_MODE;
Jodit.plugins = pluginSystem;
Jodit.modules = modules;
Jodit.ns = modules;
Jodit.decorators = {};
Jodit.constants = constants;
Jodit.instances = instances;
Jodit.lang = lang;
Jodit.core = {
    Plugin
};
__decorate([
    cache
], Jodit.prototype, "createInside", null);
__decorate([
    cache
], Jodit.prototype, "message", null);
__decorate([
    cache
], Jodit.prototype, "s", null);
__decorate([
    cache
], Jodit.prototype, "uploader", null);
__decorate([
    cache
], Jodit.prototype, "filebrowser", null);
__decorate([
    throttle()
], Jodit.prototype, "synchronizeValues", null);
__decorate([
    watch(':internalChange')
], Jodit.prototype, "updateElementValue", null);
__decorate([
    autobind
], Jodit.prototype, "__prepareWYSIWYGEditor", null);
Jodit = Jodit_1 = __decorate([
    derive(Dlgs)
], Jodit);
export { Jodit };
function addClassNames(className, elm) {
    if (className) {
        className.split(/\s+/).forEach(cn => elm.classList.add(cn));
    }
}
