[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/editor/atto/plugins/undo/yui/src/button/js/ -> button.js (source)

   1  // This file is part of Moodle - http://moodle.org/
   2  //
   3  // Moodle is free software: you can redistribute it and/or modify
   4  // it under the terms of the GNU General Public License as published by
   5  // the Free Software Foundation, either version 3 of the License, or
   6  // (at your option) any later version.
   7  //
   8  // Moodle is distributed in the hope that it will be useful,
   9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  // GNU General Public License for more details.
  12  //
  13  // You should have received a copy of the GNU General Public License
  14  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  15  
  16  /**
  17   * @component  atto_undo
  18   * @copyright  2014 Jerome Mouneyrac
  19   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  20   */
  21  
  22  /**
  23   * @module moodle-atto_undo-button
  24   */
  25  
  26  /**
  27   * Atto text editor undo plugin.
  28   *
  29   * @namespace M.atto_undo
  30   * @class button
  31   * @extends M.editor_atto.EditorPlugin
  32   */
  33  
  34  Y.namespace('M.atto_undo').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
  35      /**
  36       * The maximum saved number of undo steps.
  37       *
  38       * @property _maxUndos
  39       * @type {Number} The maximum number of saved undos.
  40       * @default 40
  41       * @private
  42       */
  43      _maxUndos: 40,
  44  
  45      /**
  46       * History of edits.
  47       *
  48       * @property _undoStack
  49       * @type {Array} The elements of the array are the html strings that make a snapshot
  50       * @private
  51       */
  52      _undoStack: null,
  53  
  54      /**
  55       * History of edits.
  56       *
  57       * @property _redoStack
  58       * @type {Array} The elements of the array are the html strings that make a snapshot
  59       * @private
  60       */
  61      _redoStack: null,
  62  
  63      /**
  64       * Add the buttons to the toolbar
  65       *
  66       * @method initializer
  67       */
  68      initializer: function() {
  69          // Initialise the undo and redo stacks.
  70          this._undoStack = [];
  71          this._redoStack = [];
  72  
  73          this.addButton({
  74              title: 'undo',
  75              icon: 'e/undo',
  76              callback: this._undoHandler,
  77              buttonName: 'undo',
  78              keys: 90
  79          });
  80  
  81          this.addButton({
  82              title: 'redo',
  83              icon: 'e/redo',
  84              callback: this._redoHandler,
  85              buttonName: 'redo',
  86              keys: 89
  87          });
  88  
  89          // Enable the undo once everything has loaded.
  90          this.get('host').on('pluginsloaded', function() {
  91              // Adds the current value to the stack.
  92              this._addToUndo(this._getHTML());
  93              this.get('host').on('atto:selectionchanged', this._changeListener, this);
  94          }, this);
  95  
  96          this._updateButtonsStates();
  97      },
  98  
  99      /**
 100       * Adds an element to the redo stack.
 101       *
 102       * @method _addToRedo
 103       * @private
 104       * @param {String} html The HTML content to save.
 105       */
 106      _addToRedo: function(html) {
 107          this._redoStack.push(html);
 108      },
 109  
 110      /**
 111       * Adds an element to the undo stack.
 112       *
 113       * @method _addToUndo
 114       * @private
 115       * @param {String} html The HTML content to save.
 116       * @param {Boolean} [clearRedo=false] Whether or not we should clear the redo stack.
 117       */
 118      _addToUndo: function(html, clearRedo) {
 119          var last = this._undoStack[this._undoStack.length - 1];
 120  
 121          if (typeof clearRedo === 'undefined') {
 122              clearRedo = false;
 123          }
 124  
 125          if (last !== html) {
 126              this._undoStack.push(html);
 127              if (clearRedo) {
 128                  this._redoStack = [];
 129              }
 130          }
 131  
 132          while (this._undoStack.length > this._maxUndos) {
 133              this._undoStack.shift();
 134          }
 135      },
 136  
 137      /**
 138       * Get the editor HTML.
 139       *
 140       * @method _getHTML
 141       * @private
 142       * @return {String} The HTML.
 143       */
 144      _getHTML: function() {
 145          return this.get('host').getCleanHTML();
 146      },
 147  
 148      /**
 149       * Get an element on the redo stack.
 150       *
 151       * @method _getRedo
 152       * @private
 153       * @return {String} The HTML to restore, or undefined.
 154       */
 155      _getRedo: function() {
 156          return this._redoStack.pop();
 157      },
 158  
 159      /**
 160       * Get an element on the undo stack.
 161       *
 162       * @method _getUndo
 163       * @private
 164       * @param {String} current The current HTML.
 165       * @return {String} The HTML to restore.
 166       */
 167      _getUndo: function(current) {
 168          if (this._undoStack.length === 1) {
 169              return this._undoStack[0];
 170          }
 171  
 172          var last = this._undoStack.pop();
 173          if (last === current) {
 174              // Oops, the latest undo step is the current content, we should unstack once more.
 175              // There is no need to do that in a loop as the same stack should never contain duplicates.
 176              last = this._undoStack.pop();
 177          }
 178  
 179          // We always need to keep the first element of the stack.
 180          if (this._undoStack.length === 0) {
 181              this._addToUndo(last);
 182          }
 183  
 184          return last;
 185      },
 186  
 187      /**
 188       * Restore a value from a stack.
 189       *
 190       * @method _restoreValue
 191       * @private
 192       * @param {String} html The HTML to restore in the editor.
 193       */
 194      _restoreValue: function(html) {
 195          this.editor.setHTML(html);
 196          // We always add the restored value to the stack, otherwise an event could think that
 197          // the content has changed and clear the redo stack.
 198          this._addToUndo(html);
 199      },
 200  
 201      /**
 202       * Update the states of the buttons.
 203       *
 204       * @method _updateButtonsStates
 205       * @private
 206       */
 207      _updateButtonsStates: function() {
 208          if (this._undoStack.length > 1) {
 209              this.enableButtons('undo');
 210          } else {
 211              this.disableButtons('undo');
 212          }
 213  
 214          if (this._redoStack.length > 0) {
 215              this.enableButtons('redo');
 216          } else {
 217              this.disableButtons('redo');
 218          }
 219      },
 220  
 221      /**
 222       * Handle a click on undo
 223       *
 224       * @method _undoHandler
 225       * @param {Event} The click event
 226       * @private
 227       */
 228      _undoHandler: function(e) {
 229          e.preventDefault();
 230          var html = this._getHTML(),
 231              undo = this._getUndo(html);
 232  
 233          // Edge case, but that could happen. We do nothing when the content equals the undo step.
 234          if (html === undo) {
 235              this._updateButtonsStates();
 236              return;
 237          }
 238  
 239          // Restore the value.
 240          this._restoreValue(undo);
 241  
 242          // Add to the redo stack.
 243          this._addToRedo(html);
 244  
 245          // Update the button states.
 246          this._updateButtonsStates();
 247      },
 248  
 249      /**
 250       * Handle a click on redo
 251       *
 252       * @method _redoHandler
 253       * @param {Event} The click event
 254       * @private
 255       */
 256      _redoHandler: function(e) {
 257          e.preventDefault();
 258          var html = this._getHTML(),
 259              redo = this._getRedo();
 260  
 261          // Edge case, but that could happen. We do nothing when the content equals the redo step.
 262          if (html === redo) {
 263              this._updateButtonsStates();
 264              return;
 265          }
 266          // Restore the value.
 267          this._restoreValue(redo);
 268  
 269          // Update the button states.
 270          this._updateButtonsStates();
 271      },
 272  
 273      /**
 274       * If we are significantly different from the last saved version, save a new version.
 275       *
 276       * @method _changeListener
 277       * @param {EventFacade} The click event
 278       * @private
 279       */
 280      _changeListener: function(e) {
 281          if (e.event && e.event.type.indexOf('key') !== -1) {
 282              // These are the 4 arrow keys.
 283              if ((e.event.keyCode !== 39) &&
 284                      (e.event.keyCode !== 37) &&
 285                      (e.event.keyCode !== 40) &&
 286                      (e.event.keyCode !== 38)) {
 287                  // Skip this event type. We only want focus/mouse/arrow events.
 288                  return;
 289              }
 290          }
 291  
 292          this._addToUndo(this._getHTML(), true);
 293          this._updateButtonsStates();
 294      }
 295  });


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1