# Source: control/Command.js

    /**
     * Used to test whether a command can be executed.
     * @type int
     * @see Command#execute
     */
    export const EXECUTE_TEST = 0;

    /**
     * Used by a command to execute a sub-command (said to be a "helper" command).
     * @type int
     * @see Command#execute
     */
    export const EXECUTE_HELPER = 1;

    /**
     * Used to execute a command "normally".
     * @type int
     * @see Command#execute
     */
    export const EXECUTE_NORMAL = 2;

    /**
     * The base class of all commands.
     */
    export class Command {
        constructor() {}

        /**
         * Get the <code>commandName</code> property of this command:
         * the name of this command.
         *
         * @type {string}
         */
        get commandName() {
            let name = this.constructor.name;
            if (name.endsWith("Cmd") && name.length > 3) {
                name = name.charAt(0).toLowerCase() +
                    name.substring(1, name.length-3);
            }

            return name;
        }

        /**
         * Returns a string representation of the invocation of this command
         * with specified parameters.
         */
        getCommandString(params) {
            return Command.joinCmdString(this.commandName, params, ' ');
        }

        /**
         * Returns a string representation of the invocation of the command
         * having specified name, specified parameters (may be <code>null</code>)
         * and using specified name/parameters separator.
         *
         * @param {string} cmdName - command name.
         * @param {string} cmdParam - command parameters.
         * @param {string}  - name/parameters separator char.
         * @returns {string} a command string or <code>null</code>
         * for an unusable command name.
         * @see Command.splitCmdString
         */
        static joinCmdString(cmdName, cmdParam, separ) {
            let cmdString = null;
            if (cmdName) {
                cmdString = cmdName;
                if (cmdParam !== null) {
                    cmdString += separ + cmdParam;
                }
            }

            return cmdString;
        }

        /**
         * Inverse of {@link Command.joinCmdString}.
         *
         * @param {string} cmdString - command string.
         * @param {string} separ - name/parameters separator char.
         * @returns {array} array <code>[command_name, command_parameters]</code>;
         * both array elements are <code>null</code> if command string is unusable.
         */
        static splitCmdString(cmdString, separ) {
            let cmd;
            if (!cmdString) {
                cmd = [null, null];
            } else {
                let cmdName = null;
                let cmdParam = null;

                let pos = cmdString.indexOf(separ);
                if (pos > 0 && pos+1 < cmdString.length) {
                    cmdName = cmdString.substring(0, pos);
                    cmdParam = cmdString.substring(pos+1);
                } else {
                    cmdName = cmdString;
                }

                cmd = [cmdName, cmdParam];
            }

            return cmd;
        }

        /**
         * Tests the execution or actually executes this command
         * with specified parameter in current editing context
         * after being trigerred by specified event.
         *
         * @param {number} mode - specifies how this command is to be executed.
         * @param {DocumentView} docView - the target of this command.
         * @param {string} params - parameterizes the command (that is, modifies
         * the behavior of the command in a command specific way).
         * May be <code>null</code>.
         * @param {UIEvent} [event=null] - the keyboard or mouse event
         * which triggered this command. May be <code>null</code>.
         * @returns {Promise} A Promise containing:
         * <ul>
         * <li>If <code>mode</code> is <code>EXECUTE_TEST</code>,
         * <code>true</code> if the command can be executed,
         * <code>false</code> otherwise.
         *
         * <p>Default implementation does not consume specified event (if any) and
         * returns <code>false</code>.
         * </li>
         *
         * <li>If <code>mode</code> is <code>EXECUTE_HELPER</code> or
         * <code>EXECUTE_NORMAL</code>,
         * <code>null</code> if, for any reason (e.g. unknown remote command),
         * this command has not been executed; a {@link CommandResult} otherwise.
         *
         * <p>Default implementation consumes specified event (if any) and
         * returns <code>CommandResult.FAILED</code>.
         * </li>
         * </ul>
         */
        execute(mode, docView, params, event=null) {
            if (mode === EXECUTE_TEST) {
                return Promise.resolve(false);
            }

            // ---

            Command.consumeEvent(event);

            console.error(`Command.execute: NOT IMPLEMENTED: \
    command "${this.commandName}" \
    (mode=${mode}, params="${params}", event=${event})`);

            return Promise.resolve(CommandResult.FAILED);
        }

        /**
         * Consume specified event (if not <code>null</code>).
         * <p>Helper used to implement commands.</p>
         */
        static consumeEvent(event) {
            if (event !== null) {
                event.preventDefault();
                // Just in case (contenteditable own bindings?).
                event.stopImmediatePropagation();
            }
        }
    }