/**
* 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();
}
}
}