mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			190 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
/**
 | 
						|
 * A simple undo stack manager.
 | 
						|
 *
 | 
						|
 * Actions are added along with the necessary code to
 | 
						|
 * reverse the action.
 | 
						|
 *
 | 
						|
 * @param integer maxUndo Maximum number of undo steps.
 | 
						|
 *   defaults to 50.
 | 
						|
 */
 | 
						|
function UndoStack(maxUndo) {
 | 
						|
  this.maxUndo = maxUndo || 50;
 | 
						|
  this._stack = [];
 | 
						|
}
 | 
						|
 | 
						|
exports.UndoStack = UndoStack;
 | 
						|
 | 
						|
UndoStack.prototype = {
 | 
						|
  // Current index into the undo stack.  Is positioned after the last
 | 
						|
  // currently-applied change.
 | 
						|
  _index: 0,
 | 
						|
 | 
						|
  // The current batch depth (see startBatch() for details)
 | 
						|
  _batchDepth: 0,
 | 
						|
 | 
						|
  destroy() {
 | 
						|
    this.uninstallController();
 | 
						|
    delete this._stack;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Start a collection of related changes.  Changes will be batched
 | 
						|
   * together into one undo/redo item until endBatch() is called.
 | 
						|
   *
 | 
						|
   * Batches can be nested, in which case the outer batch will contain
 | 
						|
   * all items from the inner batches.  This allows larger user
 | 
						|
   * actions made up of a collection of smaller actions to be
 | 
						|
   * undone as a single action.
 | 
						|
   */
 | 
						|
  startBatch() {
 | 
						|
    if (this._batchDepth++ === 0) {
 | 
						|
      this._batch = [];
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * End a batch of related changes, performing its action and adding
 | 
						|
   * it to the undo stack.
 | 
						|
   */
 | 
						|
  endBatch() {
 | 
						|
    if (--this._batchDepth > 0) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Cut off the end of the undo stack at the current index,
 | 
						|
    // and the beginning to prevent a stack larger than maxUndo.
 | 
						|
    const start = Math.max(this._index + 1 - this.maxUndo, 0);
 | 
						|
    this._stack = this._stack.slice(start, this._index);
 | 
						|
 | 
						|
    const batch = this._batch;
 | 
						|
    delete this._batch;
 | 
						|
    const entry = {
 | 
						|
      do() {
 | 
						|
        for (const item of batch) {
 | 
						|
          item.do();
 | 
						|
        }
 | 
						|
      },
 | 
						|
      undo() {
 | 
						|
        for (let i = batch.length - 1; i >= 0; i--) {
 | 
						|
          batch[i].undo();
 | 
						|
        }
 | 
						|
      },
 | 
						|
    };
 | 
						|
    this._stack.push(entry);
 | 
						|
    this._index = this._stack.length;
 | 
						|
    entry.do();
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Perform an action, adding it to the undo stack.
 | 
						|
   *
 | 
						|
   * @param function toDo Called to perform the action.
 | 
						|
   * @param function undo Called to reverse the action.
 | 
						|
   */
 | 
						|
  do(toDo, undo) {
 | 
						|
    this.startBatch();
 | 
						|
    this._batch.push({ do: toDo, undo });
 | 
						|
    this.endBatch();
 | 
						|
  },
 | 
						|
 | 
						|
  /*
 | 
						|
   * Returns true if undo() will do anything.
 | 
						|
   */
 | 
						|
  canUndo() {
 | 
						|
    return this._index > 0;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Undo the top of the undo stack.
 | 
						|
   *
 | 
						|
   * @return true if an action was undone.
 | 
						|
   */
 | 
						|
  undo() {
 | 
						|
    if (!this.canUndo()) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    this._stack[--this._index].undo();
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns true if redo() will do anything.
 | 
						|
   */
 | 
						|
  canRedo() {
 | 
						|
    return this._stack.length > this._index;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Redo the most recently undone action.
 | 
						|
   *
 | 
						|
   * @return true if an action was redone.
 | 
						|
   */
 | 
						|
  redo() {
 | 
						|
    if (!this.canRedo()) {
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
    this._stack[this._index++].do();
 | 
						|
    return true;
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * ViewController implementation for undo/redo.
 | 
						|
   */
 | 
						|
 | 
						|
  /**
 | 
						|
   * Install this object as a command controller.
 | 
						|
   */
 | 
						|
  installController(controllerWindow) {
 | 
						|
    const controllers = controllerWindow.controllers;
 | 
						|
    // Only available when running in a Firefox panel.
 | 
						|
    if (!controllers || !controllers.appendController) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    this._controllerWindow = controllerWindow;
 | 
						|
    controllers.appendController(this);
 | 
						|
  },
 | 
						|
 | 
						|
  /**
 | 
						|
   * Uninstall this object from the command controller.
 | 
						|
   */
 | 
						|
  uninstallController() {
 | 
						|
    if (!this._controllerWindow) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    this._controllerWindow.controllers.removeController(this);
 | 
						|
  },
 | 
						|
 | 
						|
  supportsCommand(command) {
 | 
						|
    return command == "cmd_undo" || command == "cmd_redo";
 | 
						|
  },
 | 
						|
 | 
						|
  isCommandEnabled(command) {
 | 
						|
    switch (command) {
 | 
						|
      case "cmd_undo":
 | 
						|
        return this.canUndo();
 | 
						|
      case "cmd_redo":
 | 
						|
        return this.canRedo();
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  },
 | 
						|
 | 
						|
  doCommand(command) {
 | 
						|
    switch (command) {
 | 
						|
      case "cmd_undo":
 | 
						|
        return this.undo();
 | 
						|
      case "cmd_redo":
 | 
						|
        return this.redo();
 | 
						|
      default:
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  onEvent(event) {},
 | 
						|
};
 |