[239] | 1 | /*
|
---|
| 2 | Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
|
---|
| 3 | For licensing, see LICENSE.html or http://ckeditor.com/license
|
---|
| 4 | */
|
---|
| 5 |
|
---|
| 6 | /**
|
---|
| 7 | * @fileOverview Undo/Redo system for saving shapshot for document modification
|
---|
| 8 | * and other recordable changes.
|
---|
| 9 | */
|
---|
| 10 |
|
---|
| 11 | (function()
|
---|
| 12 | {
|
---|
| 13 | CKEDITOR.plugins.add( 'undo',
|
---|
| 14 | {
|
---|
| 15 | requires : [ 'selection', 'wysiwygarea' ],
|
---|
| 16 |
|
---|
| 17 | init : function( editor )
|
---|
| 18 | {
|
---|
| 19 | var undoManager = new UndoManager( editor );
|
---|
| 20 |
|
---|
| 21 | var undoCommand = editor.addCommand( 'undo',
|
---|
| 22 | {
|
---|
| 23 | exec : function()
|
---|
| 24 | {
|
---|
| 25 | if ( undoManager.undo() )
|
---|
| 26 | {
|
---|
| 27 | editor.selectionChange();
|
---|
| 28 | this.fire( 'afterUndo' );
|
---|
| 29 | }
|
---|
| 30 | },
|
---|
| 31 | state : CKEDITOR.TRISTATE_DISABLED,
|
---|
| 32 | canUndo : false
|
---|
| 33 | });
|
---|
| 34 |
|
---|
| 35 | var redoCommand = editor.addCommand( 'redo',
|
---|
| 36 | {
|
---|
| 37 | exec : function()
|
---|
| 38 | {
|
---|
| 39 | if ( undoManager.redo() )
|
---|
| 40 | {
|
---|
| 41 | editor.selectionChange();
|
---|
| 42 | this.fire( 'afterRedo' );
|
---|
| 43 | }
|
---|
| 44 | },
|
---|
| 45 | state : CKEDITOR.TRISTATE_DISABLED,
|
---|
| 46 | canUndo : false
|
---|
| 47 | });
|
---|
| 48 |
|
---|
| 49 | undoManager.onChange = function()
|
---|
| 50 | {
|
---|
| 51 | undoCommand.setState( undoManager.undoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
|
---|
| 52 | redoCommand.setState( undoManager.redoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
|
---|
| 53 | };
|
---|
| 54 |
|
---|
| 55 | function recordCommand( event )
|
---|
| 56 | {
|
---|
| 57 | // If the command hasn't been marked to not support undo.
|
---|
| 58 | if ( undoManager.enabled && event.data.command.canUndo !== false )
|
---|
| 59 | undoManager.save();
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | // We'll save snapshots before and after executing a command.
|
---|
| 63 | editor.on( 'beforeCommandExec', recordCommand );
|
---|
| 64 | editor.on( 'afterCommandExec', recordCommand );
|
---|
| 65 |
|
---|
| 66 | // Save snapshots before doing custom changes.
|
---|
| 67 | editor.on( 'saveSnapshot', function( evt )
|
---|
| 68 | {
|
---|
| 69 | undoManager.save( evt.data && evt.data.contentOnly );
|
---|
| 70 | });
|
---|
| 71 |
|
---|
| 72 | // Registering keydown on every document recreation.(#3844)
|
---|
| 73 | editor.on( 'contentDom', function()
|
---|
| 74 | {
|
---|
| 75 | editor.document.on( 'keydown', function( event )
|
---|
| 76 | {
|
---|
| 77 | // Do not capture CTRL hotkeys.
|
---|
| 78 | if ( !event.data.$.ctrlKey && !event.data.$.metaKey )
|
---|
| 79 | undoManager.type( event );
|
---|
| 80 | });
|
---|
| 81 | });
|
---|
| 82 |
|
---|
| 83 | // Always save an undo snapshot - the previous mode might have
|
---|
| 84 | // changed editor contents.
|
---|
| 85 | editor.on( 'beforeModeUnload', function()
|
---|
| 86 | {
|
---|
| 87 | editor.mode == 'wysiwyg' && undoManager.save( true );
|
---|
| 88 | });
|
---|
| 89 |
|
---|
| 90 | // Make the undo manager available only in wysiwyg mode.
|
---|
| 91 | editor.on( 'mode', function()
|
---|
| 92 | {
|
---|
| 93 | undoManager.enabled = editor.readOnly ? false : editor.mode == 'wysiwyg';
|
---|
| 94 | undoManager.onChange();
|
---|
| 95 | });
|
---|
| 96 |
|
---|
| 97 | editor.ui.addButton( 'Undo',
|
---|
| 98 | {
|
---|
| 99 | label : editor.lang.undo,
|
---|
| 100 | command : 'undo'
|
---|
| 101 | });
|
---|
| 102 |
|
---|
| 103 | editor.ui.addButton( 'Redo',
|
---|
| 104 | {
|
---|
| 105 | label : editor.lang.redo,
|
---|
| 106 | command : 'redo'
|
---|
| 107 | });
|
---|
| 108 |
|
---|
| 109 | editor.resetUndo = function()
|
---|
| 110 | {
|
---|
| 111 | // Reset the undo stack.
|
---|
| 112 | undoManager.reset();
|
---|
| 113 |
|
---|
| 114 | // Create the first image.
|
---|
| 115 | editor.fire( 'saveSnapshot' );
|
---|
| 116 | };
|
---|
| 117 |
|
---|
| 118 | /**
|
---|
| 119 | * Amend the top of undo stack (last undo image) with the current DOM changes.
|
---|
| 120 | * @name CKEDITOR.editor#updateUndo
|
---|
| 121 | * @example
|
---|
| 122 | * function()
|
---|
| 123 | * {
|
---|
| 124 | * editor.fire( 'saveSnapshot' );
|
---|
| 125 | * editor.document.body.append(...);
|
---|
| 126 | * // Make new changes following the last undo snapshot part of it.
|
---|
| 127 | * editor.fire( 'updateSnapshot' );
|
---|
| 128 | * ...
|
---|
| 129 | * }
|
---|
| 130 | */
|
---|
| 131 | editor.on( 'updateSnapshot', function()
|
---|
| 132 | {
|
---|
| 133 | if ( undoManager.currentImage )
|
---|
| 134 | undoManager.update();
|
---|
| 135 | });
|
---|
| 136 | }
|
---|
| 137 | });
|
---|
| 138 |
|
---|
| 139 | CKEDITOR.plugins.undo = {};
|
---|
| 140 |
|
---|
| 141 | /**
|
---|
| 142 | * Undo snapshot which represents the current document status.
|
---|
| 143 | * @name CKEDITOR.plugins.undo.Image
|
---|
| 144 | * @param editor The editor instance on which the image is created.
|
---|
| 145 | */
|
---|
| 146 | var Image = CKEDITOR.plugins.undo.Image = function( editor )
|
---|
| 147 | {
|
---|
| 148 | this.editor = editor;
|
---|
| 149 |
|
---|
| 150 | editor.fire( 'beforeUndoImage' );
|
---|
| 151 |
|
---|
| 152 | var contents = editor.getSnapshot(),
|
---|
| 153 | selection = contents && editor.getSelection();
|
---|
| 154 |
|
---|
| 155 | // In IE, we need to remove the expando attributes.
|
---|
| 156 | CKEDITOR.env.ie && contents && ( contents = contents.replace( /\s+data-cke-expando=".*?"/g, '' ) );
|
---|
| 157 |
|
---|
| 158 | this.contents = contents;
|
---|
| 159 | this.bookmarks = selection && selection.createBookmarks2( true );
|
---|
| 160 |
|
---|
| 161 | editor.fire( 'afterUndoImage' );
|
---|
| 162 | };
|
---|
| 163 |
|
---|
| 164 | // Attributes that browser may changing them when setting via innerHTML.
|
---|
| 165 | var protectedAttrs = /\b(?:href|src|name)="[^"]*?"/gi;
|
---|
| 166 |
|
---|
| 167 | Image.prototype =
|
---|
| 168 | {
|
---|
| 169 | equals : function( otherImage, contentOnly )
|
---|
| 170 | {
|
---|
| 171 |
|
---|
| 172 | var thisContents = this.contents,
|
---|
| 173 | otherContents = otherImage.contents;
|
---|
| 174 |
|
---|
| 175 | // For IE6/7 : Comparing only the protected attribute values but not the original ones.(#4522)
|
---|
| 176 | if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) )
|
---|
| 177 | {
|
---|
| 178 | thisContents = thisContents.replace( protectedAttrs, '' );
|
---|
| 179 | otherContents = otherContents.replace( protectedAttrs, '' );
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | if ( thisContents != otherContents )
|
---|
| 183 | return false;
|
---|
| 184 |
|
---|
| 185 | if ( contentOnly )
|
---|
| 186 | return true;
|
---|
| 187 |
|
---|
| 188 | var bookmarksA = this.bookmarks,
|
---|
| 189 | bookmarksB = otherImage.bookmarks;
|
---|
| 190 |
|
---|
| 191 | if ( bookmarksA || bookmarksB )
|
---|
| 192 | {
|
---|
| 193 | if ( !bookmarksA || !bookmarksB || bookmarksA.length != bookmarksB.length )
|
---|
| 194 | return false;
|
---|
| 195 |
|
---|
| 196 | for ( var i = 0 ; i < bookmarksA.length ; i++ )
|
---|
| 197 | {
|
---|
| 198 | var bookmarkA = bookmarksA[ i ],
|
---|
| 199 | bookmarkB = bookmarksB[ i ];
|
---|
| 200 |
|
---|
| 201 | if (
|
---|
| 202 | bookmarkA.startOffset != bookmarkB.startOffset ||
|
---|
| 203 | bookmarkA.endOffset != bookmarkB.endOffset ||
|
---|
| 204 | !CKEDITOR.tools.arrayCompare( bookmarkA.start, bookmarkB.start ) ||
|
---|
| 205 | !CKEDITOR.tools.arrayCompare( bookmarkA.end, bookmarkB.end ) )
|
---|
| 206 | {
|
---|
| 207 | return false;
|
---|
| 208 | }
|
---|
| 209 | }
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | return true;
|
---|
| 213 | }
|
---|
| 214 | };
|
---|
| 215 |
|
---|
| 216 | /**
|
---|
| 217 | * @constructor Main logic for Redo/Undo feature.
|
---|
| 218 | */
|
---|
| 219 | function UndoManager( editor )
|
---|
| 220 | {
|
---|
| 221 | this.editor = editor;
|
---|
| 222 |
|
---|
| 223 | // Reset the undo stack.
|
---|
| 224 | this.reset();
|
---|
| 225 | }
|
---|
| 226 |
|
---|
| 227 |
|
---|
| 228 | var editingKeyCodes = { /*Backspace*/ 8:1, /*Delete*/ 46:1 },
|
---|
| 229 | modifierKeyCodes = { /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1 },
|
---|
| 230 | navigationKeyCodes = { 37:1, 38:1, 39:1, 40:1 }; // Arrows: L, T, R, B
|
---|
| 231 |
|
---|
| 232 | UndoManager.prototype =
|
---|
| 233 | {
|
---|
| 234 | /**
|
---|
| 235 | * Process undo system regard keystrikes.
|
---|
| 236 | * @param {CKEDITOR.dom.event} event
|
---|
| 237 | */
|
---|
| 238 | type : function( event )
|
---|
| 239 | {
|
---|
| 240 | var keystroke = event && event.data.getKey(),
|
---|
| 241 | isModifierKey = keystroke in modifierKeyCodes,
|
---|
| 242 | isEditingKey = keystroke in editingKeyCodes,
|
---|
| 243 | wasEditingKey = this.lastKeystroke in editingKeyCodes,
|
---|
| 244 | sameAsLastEditingKey = isEditingKey && keystroke == this.lastKeystroke,
|
---|
| 245 | // Keystrokes which navigation through contents.
|
---|
| 246 | isReset = keystroke in navigationKeyCodes,
|
---|
| 247 | wasReset = this.lastKeystroke in navigationKeyCodes,
|
---|
| 248 |
|
---|
| 249 | // Keystrokes which just introduce new contents.
|
---|
| 250 | isContent = ( !isEditingKey && !isReset ),
|
---|
| 251 |
|
---|
| 252 | // Create undo snap for every different modifier key.
|
---|
| 253 | modifierSnapshot = ( isEditingKey && !sameAsLastEditingKey ),
|
---|
| 254 | // Create undo snap on the following cases:
|
---|
| 255 | // 1. Just start to type .
|
---|
| 256 | // 2. Typing some content after a modifier.
|
---|
| 257 | // 3. Typing some content after make a visible selection.
|
---|
| 258 | startedTyping = !( isModifierKey || this.typing )
|
---|
| 259 | || ( isContent && ( wasEditingKey || wasReset ) );
|
---|
| 260 |
|
---|
| 261 | if ( startedTyping || modifierSnapshot )
|
---|
| 262 | {
|
---|
| 263 | var beforeTypeImage = new Image( this.editor );
|
---|
| 264 |
|
---|
| 265 | // Use setTimeout, so we give the necessary time to the
|
---|
| 266 | // browser to insert the character into the DOM.
|
---|
| 267 | CKEDITOR.tools.setTimeout( function()
|
---|
| 268 | {
|
---|
| 269 | var currentSnapshot = this.editor.getSnapshot();
|
---|
| 270 |
|
---|
| 271 | // In IE, we need to remove the expando attributes.
|
---|
| 272 | if ( CKEDITOR.env.ie )
|
---|
| 273 | currentSnapshot = currentSnapshot.replace( /\s+data-cke-expando=".*?"/g, '' );
|
---|
| 274 |
|
---|
| 275 | if ( beforeTypeImage.contents != currentSnapshot )
|
---|
| 276 | {
|
---|
| 277 | // It's safe to now indicate typing state.
|
---|
| 278 | this.typing = true;
|
---|
| 279 |
|
---|
| 280 | // This's a special save, with specified snapshot
|
---|
| 281 | // and without auto 'fireChange'.
|
---|
| 282 | if ( !this.save( false, beforeTypeImage, false ) )
|
---|
| 283 | // Drop future snapshots.
|
---|
| 284 | this.snapshots.splice( this.index + 1, this.snapshots.length - this.index - 1 );
|
---|
| 285 |
|
---|
| 286 | this.hasUndo = true;
|
---|
| 287 | this.hasRedo = false;
|
---|
| 288 |
|
---|
| 289 | this.typesCount = 1;
|
---|
| 290 | this.modifiersCount = 1;
|
---|
| 291 |
|
---|
| 292 | this.onChange();
|
---|
| 293 | }
|
---|
| 294 | },
|
---|
| 295 | 0, this
|
---|
| 296 | );
|
---|
| 297 | }
|
---|
| 298 |
|
---|
| 299 | this.lastKeystroke = keystroke;
|
---|
| 300 |
|
---|
| 301 | // Create undo snap after typed too much (over 25 times).
|
---|
| 302 | if ( isEditingKey )
|
---|
| 303 | {
|
---|
| 304 | this.typesCount = 0;
|
---|
| 305 | this.modifiersCount++;
|
---|
| 306 |
|
---|
| 307 | if ( this.modifiersCount > 25 )
|
---|
| 308 | {
|
---|
| 309 | this.save( false, null, false );
|
---|
| 310 | this.modifiersCount = 1;
|
---|
| 311 | }
|
---|
| 312 | }
|
---|
| 313 | else if ( !isReset )
|
---|
| 314 | {
|
---|
| 315 | this.modifiersCount = 0;
|
---|
| 316 | this.typesCount++;
|
---|
| 317 |
|
---|
| 318 | if ( this.typesCount > 25 )
|
---|
| 319 | {
|
---|
| 320 | this.save( false, null, false );
|
---|
| 321 | this.typesCount = 1;
|
---|
| 322 | }
|
---|
| 323 | }
|
---|
| 324 |
|
---|
| 325 | },
|
---|
| 326 |
|
---|
| 327 | reset : function() // Reset the undo stack.
|
---|
| 328 | {
|
---|
| 329 | /**
|
---|
| 330 | * Remember last pressed key.
|
---|
| 331 | */
|
---|
| 332 | this.lastKeystroke = 0;
|
---|
| 333 |
|
---|
| 334 | /**
|
---|
| 335 | * Stack for all the undo and redo snapshots, they're always created/removed
|
---|
| 336 | * in consistency.
|
---|
| 337 | */
|
---|
| 338 | this.snapshots = [];
|
---|
| 339 |
|
---|
| 340 | /**
|
---|
| 341 | * Current snapshot history index.
|
---|
| 342 | */
|
---|
| 343 | this.index = -1;
|
---|
| 344 |
|
---|
| 345 | this.limit = this.editor.config.undoStackSize || 20;
|
---|
| 346 |
|
---|
| 347 | this.currentImage = null;
|
---|
| 348 |
|
---|
| 349 | this.hasUndo = false;
|
---|
| 350 | this.hasRedo = false;
|
---|
| 351 |
|
---|
| 352 | this.resetType();
|
---|
| 353 | },
|
---|
| 354 |
|
---|
| 355 | /**
|
---|
| 356 | * Reset all states about typing.
|
---|
| 357 | * @see UndoManager.type
|
---|
| 358 | */
|
---|
| 359 | resetType : function()
|
---|
| 360 | {
|
---|
| 361 | this.typing = false;
|
---|
| 362 | delete this.lastKeystroke;
|
---|
| 363 | this.typesCount = 0;
|
---|
| 364 | this.modifiersCount = 0;
|
---|
| 365 | },
|
---|
| 366 | fireChange : function()
|
---|
| 367 | {
|
---|
| 368 | this.hasUndo = !!this.getNextImage( true );
|
---|
| 369 | this.hasRedo = !!this.getNextImage( false );
|
---|
| 370 | // Reset typing
|
---|
| 371 | this.resetType();
|
---|
| 372 | this.onChange();
|
---|
| 373 | },
|
---|
| 374 |
|
---|
| 375 | /**
|
---|
| 376 | * Save a snapshot of document image for later retrieve.
|
---|
| 377 | */
|
---|
| 378 | save : function( onContentOnly, image, autoFireChange )
|
---|
| 379 | {
|
---|
| 380 | var snapshots = this.snapshots;
|
---|
| 381 |
|
---|
| 382 | // Get a content image.
|
---|
| 383 | if ( !image )
|
---|
| 384 | image = new Image( this.editor );
|
---|
| 385 |
|
---|
| 386 | // Do nothing if it was not possible to retrieve an image.
|
---|
| 387 | if ( image.contents === false )
|
---|
| 388 | return false;
|
---|
| 389 |
|
---|
| 390 | // Check if this is a duplicate. In such case, do nothing.
|
---|
| 391 | if ( this.currentImage && image.equals( this.currentImage, onContentOnly ) )
|
---|
| 392 | return false;
|
---|
| 393 |
|
---|
| 394 | // Drop future snapshots.
|
---|
| 395 | snapshots.splice( this.index + 1, snapshots.length - this.index - 1 );
|
---|
| 396 |
|
---|
| 397 | // If we have reached the limit, remove the oldest one.
|
---|
| 398 | if ( snapshots.length == this.limit )
|
---|
| 399 | snapshots.shift();
|
---|
| 400 |
|
---|
| 401 | // Add the new image, updating the current index.
|
---|
| 402 | this.index = snapshots.push( image ) - 1;
|
---|
| 403 |
|
---|
| 404 | this.currentImage = image;
|
---|
| 405 |
|
---|
| 406 | if ( autoFireChange !== false )
|
---|
| 407 | this.fireChange();
|
---|
| 408 | return true;
|
---|
| 409 | },
|
---|
| 410 |
|
---|
| 411 | restoreImage : function( image )
|
---|
| 412 | {
|
---|
| 413 | this.editor.loadSnapshot( image.contents );
|
---|
| 414 |
|
---|
| 415 | if ( image.bookmarks )
|
---|
| 416 | this.editor.getSelection().selectBookmarks( image.bookmarks );
|
---|
| 417 | else if ( CKEDITOR.env.ie )
|
---|
| 418 | {
|
---|
| 419 | // IE BUG: If I don't set the selection to *somewhere* after setting
|
---|
| 420 | // document contents, then IE would create an empty paragraph at the bottom
|
---|
| 421 | // the next time the document is modified.
|
---|
| 422 | var $range = this.editor.document.getBody().$.createTextRange();
|
---|
| 423 | $range.collapse( true );
|
---|
| 424 | $range.select();
|
---|
| 425 | }
|
---|
| 426 |
|
---|
| 427 | this.index = image.index;
|
---|
| 428 |
|
---|
| 429 | // Update current image with the actual editor
|
---|
| 430 | // content, since actualy content may differ from
|
---|
| 431 | // the original snapshot due to dom change. (#4622)
|
---|
| 432 | this.update();
|
---|
| 433 | this.fireChange();
|
---|
| 434 | },
|
---|
| 435 |
|
---|
| 436 | // Get the closest available image.
|
---|
| 437 | getNextImage : function( isUndo )
|
---|
| 438 | {
|
---|
| 439 | var snapshots = this.snapshots,
|
---|
| 440 | currentImage = this.currentImage,
|
---|
| 441 | image, i;
|
---|
| 442 |
|
---|
| 443 | if ( currentImage )
|
---|
| 444 | {
|
---|
| 445 | if ( isUndo )
|
---|
| 446 | {
|
---|
| 447 | for ( i = this.index - 1 ; i >= 0 ; i-- )
|
---|
| 448 | {
|
---|
| 449 | image = snapshots[ i ];
|
---|
| 450 | if ( !currentImage.equals( image, true ) )
|
---|
| 451 | {
|
---|
| 452 | image.index = i;
|
---|
| 453 | return image;
|
---|
| 454 | }
|
---|
| 455 | }
|
---|
| 456 | }
|
---|
| 457 | else
|
---|
| 458 | {
|
---|
| 459 | for ( i = this.index + 1 ; i < snapshots.length ; i++ )
|
---|
| 460 | {
|
---|
| 461 | image = snapshots[ i ];
|
---|
| 462 | if ( !currentImage.equals( image, true ) )
|
---|
| 463 | {
|
---|
| 464 | image.index = i;
|
---|
| 465 | return image;
|
---|
| 466 | }
|
---|
| 467 | }
|
---|
| 468 | }
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 | return null;
|
---|
| 472 | },
|
---|
| 473 |
|
---|
| 474 | /**
|
---|
| 475 | * Check the current redo state.
|
---|
| 476 | * @return {Boolean} Whether the document has previous state to
|
---|
| 477 | * retrieve.
|
---|
| 478 | */
|
---|
| 479 | redoable : function()
|
---|
| 480 | {
|
---|
| 481 | return this.enabled && this.hasRedo;
|
---|
| 482 | },
|
---|
| 483 |
|
---|
| 484 | /**
|
---|
| 485 | * Check the current undo state.
|
---|
| 486 | * @return {Boolean} Whether the document has future state to restore.
|
---|
| 487 | */
|
---|
| 488 | undoable : function()
|
---|
| 489 | {
|
---|
| 490 | return this.enabled && this.hasUndo;
|
---|
| 491 | },
|
---|
| 492 |
|
---|
| 493 | /**
|
---|
| 494 | * Perform undo on current index.
|
---|
| 495 | */
|
---|
| 496 | undo : function()
|
---|
| 497 | {
|
---|
| 498 | if ( this.undoable() )
|
---|
| 499 | {
|
---|
| 500 | this.save( true );
|
---|
| 501 |
|
---|
| 502 | var image = this.getNextImage( true );
|
---|
| 503 | if ( image )
|
---|
| 504 | return this.restoreImage( image ), true;
|
---|
| 505 | }
|
---|
| 506 |
|
---|
| 507 | return false;
|
---|
| 508 | },
|
---|
| 509 |
|
---|
| 510 | /**
|
---|
| 511 | * Perform redo on current index.
|
---|
| 512 | */
|
---|
| 513 | redo : function()
|
---|
| 514 | {
|
---|
| 515 | if ( this.redoable() )
|
---|
| 516 | {
|
---|
| 517 | // Try to save. If no changes have been made, the redo stack
|
---|
| 518 | // will not change, so it will still be redoable.
|
---|
| 519 | this.save( true );
|
---|
| 520 |
|
---|
| 521 | // If instead we had changes, we can't redo anymore.
|
---|
| 522 | if ( this.redoable() )
|
---|
| 523 | {
|
---|
| 524 | var image = this.getNextImage( false );
|
---|
| 525 | if ( image )
|
---|
| 526 | return this.restoreImage( image ), true;
|
---|
| 527 | }
|
---|
| 528 | }
|
---|
| 529 |
|
---|
| 530 | return false;
|
---|
| 531 | },
|
---|
| 532 |
|
---|
| 533 | /**
|
---|
| 534 | * Update the last snapshot of the undo stack with the current editor content.
|
---|
| 535 | */
|
---|
| 536 | update : function()
|
---|
| 537 | {
|
---|
| 538 | this.snapshots.splice( this.index, 1, ( this.currentImage = new Image( this.editor ) ) );
|
---|
| 539 | }
|
---|
| 540 | };
|
---|
| 541 | })();
|
---|
| 542 |
|
---|
| 543 | /**
|
---|
| 544 | * The number of undo steps to be saved. The higher this setting value the more
|
---|
| 545 | * memory is used for it.
|
---|
| 546 | * @name CKEDITOR.config.undoStackSize
|
---|
| 547 | * @type Number
|
---|
| 548 | * @default 20
|
---|
| 549 | * @example
|
---|
| 550 | * config.undoStackSize = 50;
|
---|
| 551 | */
|
---|
| 552 |
|
---|
| 553 | /**
|
---|
| 554 | * Fired when the editor is about to save an undo snapshot. This event can be
|
---|
| 555 | * fired by plugins and customizations to make the editor saving undo snapshots.
|
---|
| 556 | * @name CKEDITOR.editor#saveSnapshot
|
---|
| 557 | * @event
|
---|
| 558 | */
|
---|
| 559 |
|
---|
| 560 | /**
|
---|
| 561 | * Fired before an undo image is to be taken. An undo image represents the
|
---|
| 562 | * editor state at some point. It's saved into an undo store, so the editor is
|
---|
| 563 | * able to recover the editor state on undo and redo operations.
|
---|
| 564 | * @name CKEDITOR.editor#beforeUndoImage
|
---|
| 565 | * @since 3.5.3
|
---|
| 566 | * @see CKEDITOR.editor#afterUndoImage
|
---|
| 567 | * @event
|
---|
| 568 | */
|
---|
| 569 |
|
---|
| 570 | /**
|
---|
| 571 | * Fired after an undo image is taken. An undo image represents the
|
---|
| 572 | * editor state at some point. It's saved into an undo store, so the editor is
|
---|
| 573 | * able to recover the editor state on undo and redo operations.
|
---|
| 574 | * @name CKEDITOR.editor#afterUndoImage
|
---|
| 575 | * @since 3.5.3
|
---|
| 576 | * @see CKEDITOR.editor#beforeUndoImage
|
---|
| 577 | * @event
|
---|
| 578 | */
|
---|