export class UndoManager { constructor () { this._stack = [] this._limit = Infinity // index 表示下一个可以 redo 的项目 this._index = 0 } get hasRedo () { return this._stack.length > 0 && this._index < this._stack.length } get hasUndo () { return this._stack.length > 0 && this._index > 0 } get size () { return this._stack.length } get limit () { return this._limit } set limit (limit) { this.setLimit(limit) } get index () { return this._index } get isEmpty () { return this._stack.length === 0 } get isFull () { return this._stack.length === this._limit } getSize () { return this.size } getIndex () { return this.index } setLimit (limit) { this._limit = limit >= 0 ? parseInt(limit) : 0 this._clipStack() } _clipStack () { if (this._limit >= this._stack.length) return if (this._limit < this._stack.length) { const step = this._stack.length - this._limit while (this._index < step) { this.redo() } this._index = this._index - step || 0 } this._stack = this._stack.slice(-this._limit) } clear () { this._stack = [] this._index = 0 } record ({ redo, undo }) { if (!redo || !undo) { throw (new TypeError('"redo" & "undo" are both expected to be type of "Function".')) } this._stack.push({ redo, undo }) this._clipStack() } execute ({ redo, undo }) { if (!redo || !undo) { throw (new TypeError('"redo" & "undo" are both expected to be type of "Function".')) } if (!this.hasRedo) { this._stack.push({ redo, undo }) this.redo() this._clipStack() } else { throw (new Error('UndoManager has redo, can not execute.')) } } redo () { if (this.hasRedo) { this._stack[this._index].redo() this._index = this._index + 1 } } undo () { if (this.hasUndo) { this._stack[this._index - 1].undo() this._index = this._index - 1 } } bindHotKeys () { // TODO: bindHotKeys } }