import {ListenerHandler} from './listen';
/**
* UndoStack class.
*/
export class UndoStack {
/**
* Array of commands.
*
* @type {Array}
*/
#stack = [];
/**
* Current command index.
*
* @type {number}
*/
#curCmdIndex = 0;
/**
* Listener handler.
*
* @type {ListenerHandler}
*/
#listenerHandler = new ListenerHandler();
/**
* Get the stack size.
*
* @returns {number} The size of the stack.
*/
getStackSize() {
return this.#stack.length;
}
/**
* Get the current stack index.
*
* @returns {number} The stack index.
*/
getCurrentStackIndex() {
return this.#curCmdIndex;
}
/**
* Add a command to the stack.
*
* @param {object} cmd The command to add.
* @fires UndoStack#undoadd
*/
add(cmd) {
// clear commands after current index
this.#stack = this.#stack.slice(0, this.#curCmdIndex);
// store command
this.#stack.push(cmd);
// increment index
++this.#curCmdIndex;
/**
* Command add to undo stack event.
*
* @event UndoStack#undoadd
* @type {object}
* @property {string} type The event type.
* @property {string} command The name of the command added to the
* undo stack.
*/
this.#fireEvent({
type: 'undoadd',
command: cmd.getName()
});
}
/**
* Remove a command to the stack.
*
* @param {string} name The name of the command to remove.
* @returns {boolean} True if the command was found and removed.
* @fires UndoStack#undoremove
*/
remove(name) {
let res = false;
const hasInputName = function (element) {
return element.getName() === name;
};
const index = this.#stack.findIndex(hasInputName);
if (index !== -1) {
// remove command
this.#stack.splice(index, 1);
// decrement index
--this.#curCmdIndex;
// result
res = true;
/**
* Command remove from undo stack event.
*
* @event UndoStack#undoremove
* @type {object}
* @property {string} type The event type.
* @property {string} command The name of the command added to the
* undo stack.
*/
this.#fireEvent({
type: 'undoremove',
command: name
});
}
return res;
}
/**
* Undo the last command.
*
* @fires UndoStack#undo
*/
undo() {
// a bit inefficient...
if (this.#curCmdIndex > 0) {
// decrement command index
--this.#curCmdIndex;
// undo last command
this.#stack[this.#curCmdIndex].undo();
/**
* Command undo event.
*
* @event UndoStack#undo
* @type {object}
* @property {string} type The event type.
* @property {string} command The name of the undone command.
*/
this.#fireEvent({
type: 'undo',
command: this.#stack[this.#curCmdIndex].getName()
});
}
}
/**
* Redo the last command.
*
* @fires UndoStack#redo
*/
redo() {
if (this.#curCmdIndex < this.#stack.length) {
// run last command
this.#stack[this.#curCmdIndex].execute();
/**
* Command redo event.
*
* @event UndoStack#redo
* @type {object}
* @property {string} type The event type.
* @property {string} command The name of the redone command.
*/
this.#fireEvent({
type: 'redo',
command: this.#stack[this.#curCmdIndex].getName()
});
// increment command index
++this.#curCmdIndex;
}
}
/**
* Add an event listener to this class.
*
* @param {string} type The event type.
* @param {Function} callback The function associated with the provided
* event type, will be called with the fired event.
*/
addEventListener(type, callback) {
this.#listenerHandler.add(type, callback);
}
/**
* Remove an event listener from this class.
*
* @param {string} type The event type.
* @param {Function} callback The function associated with the provided
* event type.
*/
removeEventListener(type, callback) {
this.#listenerHandler.remove(type, callback);
}
/**
* Fire an event: call all associated listeners with the input event object.
*
* @param {object} event The event to fire.
*/
#fireEvent = (event) => {
this.#listenerHandler.fireEvent(event);
};
} // UndoStack class