Source: xxe/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 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.executeImpl: 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();
        }
    }
}