[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |