739 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
		
		
			
		
	
	
			739 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
							 | 
						|||
| 
								 | 
							
								 * For licensing, see LICENSE.md or http://ckeditor.com/license
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * @fileOverview Undo/Redo system for saving a shapshot for document modification
							 | 
						|||
| 
								 | 
							
								 *		and other recordable changes.
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								( function() {
							 | 
						|||
| 
								 | 
							
									CKEDITOR.plugins.add( 'undo', {
							 | 
						|||
| 
								 | 
							
										lang: 'af,ar,bg,bn,bs,ca,cs,cy,da,de,el,en,en-au,en-ca,en-gb,eo,es,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
							 | 
						|||
| 
								 | 
							
										icons: 'redo,redo-rtl,undo,undo-rtl', // %REMOVE_LINE_CORE%
							 | 
						|||
| 
								 | 
							
										hidpi: true, // %REMOVE_LINE_CORE%
							 | 
						|||
| 
								 | 
							
										init: function( editor ) {
							 | 
						|||
| 
								 | 
							
											var undoManager = editor.undoManager = new UndoManager( editor );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var undoCommand = editor.addCommand( 'undo', {
							 | 
						|||
| 
								 | 
							
												exec: function() {
							 | 
						|||
| 
								 | 
							
													if ( undoManager.undo() ) {
							 | 
						|||
| 
								 | 
							
														editor.selectionChange();
							 | 
						|||
| 
								 | 
							
														this.fire( 'afterUndo' );
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												},
							 | 
						|||
| 
								 | 
							
												startDisabled: true,
							 | 
						|||
| 
								 | 
							
												canUndo: false
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var redoCommand = editor.addCommand( 'redo', {
							 | 
						|||
| 
								 | 
							
												exec: function() {
							 | 
						|||
| 
								 | 
							
													if ( undoManager.redo() ) {
							 | 
						|||
| 
								 | 
							
														editor.selectionChange();
							 | 
						|||
| 
								 | 
							
														this.fire( 'afterRedo' );
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												},
							 | 
						|||
| 
								 | 
							
												startDisabled: true,
							 | 
						|||
| 
								 | 
							
												canUndo: false
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											editor.setKeystroke( [
							 | 
						|||
| 
								 | 
							
												[ CKEDITOR.CTRL + 90 /*Z*/, 'undo' ],
							 | 
						|||
| 
								 | 
							
												[ CKEDITOR.CTRL + 89 /*Y*/, 'redo' ],
							 | 
						|||
| 
								 | 
							
												[ CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 /*Z*/, 'redo' ]
							 | 
						|||
| 
								 | 
							
											] );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											undoManager.onChange = function() {
							 | 
						|||
| 
								 | 
							
												undoCommand.setState( undoManager.undoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
							 | 
						|||
| 
								 | 
							
												redoCommand.setState( undoManager.redoable() ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
							 | 
						|||
| 
								 | 
							
											};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											function recordCommand( event ) {
							 | 
						|||
| 
								 | 
							
												// If the command hasn't been marked to not support undo.
							 | 
						|||
| 
								 | 
							
												if ( undoManager.enabled && event.data.command.canUndo !== false )
							 | 
						|||
| 
								 | 
							
													undoManager.save();
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// We'll save snapshots before and after executing a command.
							 | 
						|||
| 
								 | 
							
											editor.on( 'beforeCommandExec', recordCommand );
							 | 
						|||
| 
								 | 
							
											editor.on( 'afterCommandExec', recordCommand );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Save snapshots before doing custom changes.
							 | 
						|||
| 
								 | 
							
											editor.on( 'saveSnapshot', function( evt ) {
							 | 
						|||
| 
								 | 
							
												undoManager.save( evt.data && evt.data.contentOnly );
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Registering keydown on every document recreation.(#3844)
							 | 
						|||
| 
								 | 
							
											editor.on( 'contentDom', function() {
							 | 
						|||
| 
								 | 
							
												editor.editable().on( 'keydown', function( event ) {
							 | 
						|||
| 
								 | 
							
													var keystroke = event.data.getKey();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													if ( keystroke == 8 /*Backspace*/ || keystroke == 46 /*Delete*/ )
							 | 
						|||
| 
								 | 
							
														undoManager.type( keystroke, 0 );
							 | 
						|||
| 
								 | 
							
												} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												editor.editable().on( 'keypress', function( event ) {
							 | 
						|||
| 
								 | 
							
													undoManager.type( event.data.getKey(), 1 );
							 | 
						|||
| 
								 | 
							
												} );
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Always save an undo snapshot - the previous mode might have
							 | 
						|||
| 
								 | 
							
											// changed editor contents.
							 | 
						|||
| 
								 | 
							
											editor.on( 'beforeModeUnload', function() {
							 | 
						|||
| 
								 | 
							
												editor.mode == 'wysiwyg' && undoManager.save( true );
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											function toggleUndoManager() {
							 | 
						|||
| 
								 | 
							
												undoManager.enabled = editor.readOnly ? false : editor.mode == 'wysiwyg';
							 | 
						|||
| 
								 | 
							
												undoManager.onChange();
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Make the undo manager available only in wysiwyg mode.
							 | 
						|||
| 
								 | 
							
											editor.on( 'mode', toggleUndoManager );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Disable undo manager when in read-only mode.
							 | 
						|||
| 
								 | 
							
											editor.on( 'readOnly', toggleUndoManager );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( editor.ui.addButton ) {
							 | 
						|||
| 
								 | 
							
												editor.ui.addButton( 'Undo', {
							 | 
						|||
| 
								 | 
							
													label: editor.lang.undo.undo,
							 | 
						|||
| 
								 | 
							
													command: 'undo',
							 | 
						|||
| 
								 | 
							
													toolbar: 'undo,10'
							 | 
						|||
| 
								 | 
							
												} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												editor.ui.addButton( 'Redo', {
							 | 
						|||
| 
								 | 
							
													label: editor.lang.undo.redo,
							 | 
						|||
| 
								 | 
							
													command: 'redo',
							 | 
						|||
| 
								 | 
							
													toolbar: 'undo,20'
							 | 
						|||
| 
								 | 
							
												} );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											/**
							 | 
						|||
| 
								 | 
							
											 * Resets the undo stack.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
											 */
							 | 
						|||
| 
								 | 
							
											editor.resetUndo = function() {
							 | 
						|||
| 
								 | 
							
												// Reset the undo stack.
							 | 
						|||
| 
								 | 
							
												undoManager.reset();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												// Create the first image.
							 | 
						|||
| 
								 | 
							
												editor.fire( 'saveSnapshot' );
							 | 
						|||
| 
								 | 
							
											};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											/**
							 | 
						|||
| 
								 | 
							
											 * Amends the top of the undo stack (last undo image) with the current DOM changes.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 *		function() {
							 | 
						|||
| 
								 | 
							
											 *			editor.fire( 'saveSnapshot' );
							 | 
						|||
| 
								 | 
							
											 *			editor.document.body.append(...);
							 | 
						|||
| 
								 | 
							
											 *			// Makes new changes following the last undo snapshot a part of it.
							 | 
						|||
| 
								 | 
							
											 *			editor.fire( 'updateSnapshot' );
							 | 
						|||
| 
								 | 
							
											 *			..
							 | 
						|||
| 
								 | 
							
											 *		}
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * @event updateSnapshot
							 | 
						|||
| 
								 | 
							
											 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 			 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
											 */
							 | 
						|||
| 
								 | 
							
											editor.on( 'updateSnapshot', function() {
							 | 
						|||
| 
								 | 
							
												if ( undoManager.currentImage )
							 | 
						|||
| 
								 | 
							
													undoManager.update();
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											/**
							 | 
						|||
| 
								 | 
							
											 * Locks the undo manager to prevent any save/update operations.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * It is convenient to lock the undo manager before performing DOM operations
							 | 
						|||
| 
								 | 
							
											 * that should not be recored (e.g. auto paragraphing).
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * See {@link CKEDITOR.plugins.undo.UndoManager#lock} for more details.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * **Note:** In order to unlock the undo manager, {@link #unlockSnapshot} has to be fired
							 | 
						|||
| 
								 | 
							
											 * the same number of times that `lockSnapshot` has been fired.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * @since 4.0
							 | 
						|||
| 
								 | 
							
											 * @event lockSnapshot
							 | 
						|||
| 
								 | 
							
											 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 			 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
											 * @param data
							 | 
						|||
| 
								 | 
							
											 * @param {Boolean} [data.dontUpdate] When set to `true` the last snapshot will not be updated
							 | 
						|||
| 
								 | 
							
											 * with the current contents and selection. Read more in the {@link CKEDITOR.plugins.undo.UndoManager#lock} method.
							 | 
						|||
| 
								 | 
							
											 */
							 | 
						|||
| 
								 | 
							
											editor.on( 'lockSnapshot', function( evt ) {
							 | 
						|||
| 
								 | 
							
												undoManager.lock( evt.data && evt.data.dontUpdate );
							 | 
						|||
| 
								 | 
							
											} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											/**
							 | 
						|||
| 
								 | 
							
											 * Unlocks the undo manager and updates the latest snapshot.
							 | 
						|||
| 
								 | 
							
											 *
							 | 
						|||
| 
								 | 
							
											 * @since 4.0
							 | 
						|||
| 
								 | 
							
											 * @event unlockSnapshot
							 | 
						|||
| 
								 | 
							
											 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 			 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
											 */
							 | 
						|||
| 
								 | 
							
											editor.on( 'unlockSnapshot', undoManager.unlock, undoManager );
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									} );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									CKEDITOR.plugins.undo = {};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * Undoes the snapshot which represents the current document status.
							 | 
						|||
| 
								 | 
							
									 *
							 | 
						|||
| 
								 | 
							
									 * @private
							 | 
						|||
| 
								 | 
							
									 * @class CKEDITOR.plugins.undo.Image
							 | 
						|||
| 
								 | 
							
									 * @constructor Creates an Image class instance.
							 | 
						|||
| 
								 | 
							
									 * @param {CKEDITOR.editor} editor The editor instance on which the image is created.
							 | 
						|||
| 
								 | 
							
									 * @param {Boolean} [contentsOnly] If set to `true` image will contain only contents, without selection.
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									var Image = CKEDITOR.plugins.undo.Image = function( editor, contentsOnly ) {
							 | 
						|||
| 
								 | 
							
											this.editor = editor;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											editor.fire( 'beforeUndoImage' );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var contents = editor.getSnapshot();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// In IE, we need to remove the expando attributes.
							 | 
						|||
| 
								 | 
							
											if ( CKEDITOR.env.ie && contents )
							 | 
						|||
| 
								 | 
							
												contents = contents.replace( /\s+data-cke-expando=".*?"/g, '' );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.contents = contents;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( !contentsOnly ) {
							 | 
						|||
| 
								 | 
							
												var selection = contents && editor.getSelection();
							 | 
						|||
| 
								 | 
							
												this.bookmarks = selection && selection.createBookmarks2( true );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											editor.fire( 'afterUndoImage' );
							 | 
						|||
| 
								 | 
							
										};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									// Attributes that browser may changing them when setting via innerHTML.
							 | 
						|||
| 
								 | 
							
									var protectedAttrs = /\b(?:href|src|name)="[^"]*?"/gi;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									Image.prototype = {
							 | 
						|||
| 
								 | 
							
										equalsContent: function( otherImage ) {
							 | 
						|||
| 
								 | 
							
											var thisContents = this.contents,
							 | 
						|||
| 
								 | 
							
												otherContents = otherImage.contents;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// For IE6/7 : Comparing only the protected attribute values but not the original ones.(#4522)
							 | 
						|||
| 
								 | 
							
											if ( CKEDITOR.env.ie && ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) ) {
							 | 
						|||
| 
								 | 
							
												thisContents = thisContents.replace( protectedAttrs, '' );
							 | 
						|||
| 
								 | 
							
												otherContents = otherContents.replace( protectedAttrs, '' );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( thisContents != otherContents )
							 | 
						|||
| 
								 | 
							
												return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return true;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										equalsSelection: function( otherImage ) {
							 | 
						|||
| 
								 | 
							
											var bookmarksA = this.bookmarks,
							 | 
						|||
| 
								 | 
							
												bookmarksB = otherImage.bookmarks;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( bookmarksA || bookmarksB ) {
							 | 
						|||
| 
								 | 
							
												if ( !bookmarksA || !bookmarksB || bookmarksA.length != bookmarksB.length )
							 | 
						|||
| 
								 | 
							
													return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												for ( var i = 0; i < bookmarksA.length; i++ ) {
							 | 
						|||
| 
								 | 
							
													var bookmarkA = bookmarksA[ i ],
							 | 
						|||
| 
								 | 
							
														bookmarkB = bookmarksB[ i ];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													if ( bookmarkA.startOffset != bookmarkB.startOffset || bookmarkA.endOffset != bookmarkB.endOffset || !CKEDITOR.tools.arrayCompare( bookmarkA.start, bookmarkB.start ) || !CKEDITOR.tools.arrayCompare( bookmarkA.end, bookmarkB.end ) )
							 | 
						|||
| 
								 | 
							
														return false;
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return true;
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									};
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									/**
							 | 
						|||
| 
								 | 
							
									 * Main logic for the Redo/Undo feature.
							 | 
						|||
| 
								 | 
							
									 *
							 | 
						|||
| 
								 | 
							
									 * **Note:** This class is not accessible from the global scope.
							 | 
						|||
| 
								 | 
							
									 *
							 | 
						|||
| 
								 | 
							
									 * @private
							 | 
						|||
| 
								 | 
							
									 * @class CKEDITOR.plugins.undo.UndoManager
							 | 
						|||
| 
								 | 
							
									 * @constructor Creates an UndoManager class instance.
							 | 
						|||
| 
								 | 
							
									 * @param {CKEDITOR.editor} editor
							 | 
						|||
| 
								 | 
							
									 */
							 | 
						|||
| 
								 | 
							
									function UndoManager( editor ) {
							 | 
						|||
| 
								 | 
							
										this.editor = editor;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Reset the undo stack.
							 | 
						|||
| 
								 | 
							
										this.reset();
							 | 
						|||
| 
								 | 
							
									}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
									UndoManager.prototype = {
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * When `locked` property is not `null`, the undo manager is locked, so
							 | 
						|||
| 
								 | 
							
										 * operations like `save` or `update` are forbidden.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * The manager can be locked/unlocked by the {@link #lock} and {@link #unlock} methods.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @private
							 | 
						|||
| 
								 | 
							
										 * @property {Object} [locked=null]
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Handles keystroke support for the undo manager. It is called whenever a keystroke that
							 | 
						|||
| 
								 | 
							
										 * can change the editor contents is pressed.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @param {Number} keystroke The key code.
							 | 
						|||
| 
								 | 
							
										 * @param {Boolean} isCharacter If `true`, it is a character ('a', '1', '&', ...). Otherwise it is the remove key (*Delete* or *Backspace*).
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										type: function( keystroke, isCharacter ) {
							 | 
						|||
| 
								 | 
							
											// Create undo snap for every different modifier key.
							 | 
						|||
| 
								 | 
							
											var modifierSnapshot = ( !isCharacter && keystroke != this.lastKeystroke );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Create undo snap on the following cases:
							 | 
						|||
| 
								 | 
							
											// 1. Just start to type .
							 | 
						|||
| 
								 | 
							
											// 2. Typing some content after a modifier.
							 | 
						|||
| 
								 | 
							
											// 3. Typing some content after make a visible selection.
							 | 
						|||
| 
								 | 
							
											var startedTyping = !this.typing || ( isCharacter && !this.wasCharacter );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var editor = this.editor;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( startedTyping || modifierSnapshot ) {
							 | 
						|||
| 
								 | 
							
												var beforeTypeImage = new Image( editor ),
							 | 
						|||
| 
								 | 
							
													beforeTypeCount = this.snapshots.length;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												// Use setTimeout, so we give the necessary time to the
							 | 
						|||
| 
								 | 
							
												// browser to insert the character into the DOM.
							 | 
						|||
| 
								 | 
							
												CKEDITOR.tools.setTimeout( function() {
							 | 
						|||
| 
								 | 
							
													var currentSnapshot = editor.getSnapshot();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													// In IE, we need to remove the expando attributes.
							 | 
						|||
| 
								 | 
							
													if ( CKEDITOR.env.ie )
							 | 
						|||
| 
								 | 
							
														currentSnapshot = currentSnapshot.replace( /\s+data-cke-expando=".*?"/g, '' );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													// If changes have taken place, while not been captured yet (#8459),
							 | 
						|||
| 
								 | 
							
													// compensate the snapshot.
							 | 
						|||
| 
								 | 
							
													if ( beforeTypeImage.contents != currentSnapshot && beforeTypeCount == this.snapshots.length ) {
							 | 
						|||
| 
								 | 
							
														// It's safe to now indicate typing state.
							 | 
						|||
| 
								 | 
							
														this.typing = true;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
														// This's a special save, with specified snapshot
							 | 
						|||
| 
								 | 
							
														// and without auto 'fireChange'.
							 | 
						|||
| 
								 | 
							
														if ( !this.save( false, beforeTypeImage, false ) )
							 | 
						|||
| 
								 | 
							
															// Drop future snapshots.
							 | 
						|||
| 
								 | 
							
															this.snapshots.splice( this.index + 1, this.snapshots.length - this.index - 1 );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
														this.hasUndo = true;
							 | 
						|||
| 
								 | 
							
														this.hasRedo = false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
														this.typesCount = 1;
							 | 
						|||
| 
								 | 
							
														this.modifiersCount = 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
														this.onChange();
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												}, 0, this );
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.lastKeystroke = keystroke;
							 | 
						|||
| 
								 | 
							
											this.wasCharacter = isCharacter;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Create undo snap after typed too much (over 25 times).
							 | 
						|||
| 
								 | 
							
											if ( !isCharacter ) {
							 | 
						|||
| 
								 | 
							
												this.typesCount = 0;
							 | 
						|||
| 
								 | 
							
												this.modifiersCount++;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												if ( this.modifiersCount > 25 ) {
							 | 
						|||
| 
								 | 
							
													this.save( false, null, false );
							 | 
						|||
| 
								 | 
							
													this.modifiersCount = 1;
							 | 
						|||
| 
								 | 
							
												} else {
							 | 
						|||
| 
								 | 
							
													setTimeout( function() {
							 | 
						|||
| 
								 | 
							
														editor.fire( 'change' );
							 | 
						|||
| 
								 | 
							
													}, 0 );
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											} else {
							 | 
						|||
| 
								 | 
							
												this.modifiersCount = 0;
							 | 
						|||
| 
								 | 
							
												this.typesCount++;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												if ( this.typesCount > 25 ) {
							 | 
						|||
| 
								 | 
							
													this.save( false, null, false );
							 | 
						|||
| 
								 | 
							
													this.typesCount = 1;
							 | 
						|||
| 
								 | 
							
												} else {
							 | 
						|||
| 
								 | 
							
													setTimeout( function() {
							 | 
						|||
| 
								 | 
							
														editor.fire( 'change' );
							 | 
						|||
| 
								 | 
							
													}, 0 );
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Resets the undo stack.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										reset: function() {
							 | 
						|||
| 
								 | 
							
											// Remember last pressed key.
							 | 
						|||
| 
								 | 
							
											this.lastKeystroke = 0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Stack for all the undo and redo snapshots, they're always created/removed
							 | 
						|||
| 
								 | 
							
											// in consistency.
							 | 
						|||
| 
								 | 
							
											this.snapshots = [];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Current snapshot history index.
							 | 
						|||
| 
								 | 
							
											this.index = -1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.limit = this.editor.config.undoStackSize || 20;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.currentImage = null;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.hasUndo = false;
							 | 
						|||
| 
								 | 
							
											this.hasRedo = false;
							 | 
						|||
| 
								 | 
							
											this.locked = null;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.resetType();
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Resets all typing variables.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @see #type
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										resetType: function() {
							 | 
						|||
| 
								 | 
							
											this.typing = false;
							 | 
						|||
| 
								 | 
							
											delete this.lastKeystroke;
							 | 
						|||
| 
								 | 
							
											this.typesCount = 0;
							 | 
						|||
| 
								 | 
							
											this.modifiersCount = 0;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										fireChange: function() {
							 | 
						|||
| 
								 | 
							
											this.hasUndo = !!this.getNextImage( true );
							 | 
						|||
| 
								 | 
							
											this.hasRedo = !!this.getNextImage( false );
							 | 
						|||
| 
								 | 
							
											// Reset typing
							 | 
						|||
| 
								 | 
							
											this.resetType();
							 | 
						|||
| 
								 | 
							
											this.onChange();
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Saves a snapshot of the document image for later retrieval.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										save: function( onContentOnly, image, autoFireChange ) {
							 | 
						|||
| 
								 | 
							
											// Do not change snapshots stack when locked.
							 | 
						|||
| 
								 | 
							
											if ( this.locked )
							 | 
						|||
| 
								 | 
							
												return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var snapshots = this.snapshots;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Get a content image.
							 | 
						|||
| 
								 | 
							
											if ( !image )
							 | 
						|||
| 
								 | 
							
												image = new Image( this.editor );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Do nothing if it was not possible to retrieve an image.
							 | 
						|||
| 
								 | 
							
											if ( image.contents === false )
							 | 
						|||
| 
								 | 
							
												return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Check if this is a duplicate. In such case, do nothing.
							 | 
						|||
| 
								 | 
							
											if ( this.currentImage ) {
							 | 
						|||
| 
								 | 
							
												if ( image.equalsContent( this.currentImage ) ) {
							 | 
						|||
| 
								 | 
							
													if ( onContentOnly )
							 | 
						|||
| 
								 | 
							
														return false;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													if ( image.equalsSelection( this.currentImage ) )
							 | 
						|||
| 
								 | 
							
														return false;
							 | 
						|||
| 
								 | 
							
												} else
							 | 
						|||
| 
								 | 
							
													this.editor.fire( 'change' );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Drop future snapshots.
							 | 
						|||
| 
								 | 
							
											snapshots.splice( this.index + 1, snapshots.length - this.index - 1 );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// If we have reached the limit, remove the oldest one.
							 | 
						|||
| 
								 | 
							
											if ( snapshots.length == this.limit )
							 | 
						|||
| 
								 | 
							
												snapshots.shift();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Add the new image, updating the current index.
							 | 
						|||
| 
								 | 
							
											this.index = snapshots.push( image ) - 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.currentImage = image;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( autoFireChange !== false )
							 | 
						|||
| 
								 | 
							
												this.fireChange();
							 | 
						|||
| 
								 | 
							
											return true;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										restoreImage: function( image ) {
							 | 
						|||
| 
								 | 
							
											// Bring editor focused to restore selection.
							 | 
						|||
| 
								 | 
							
											var editor = this.editor,
							 | 
						|||
| 
								 | 
							
												sel;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( image.bookmarks ) {
							 | 
						|||
| 
								 | 
							
												editor.focus();
							 | 
						|||
| 
								 | 
							
												// Retrieve the selection beforehand. (#8324)
							 | 
						|||
| 
								 | 
							
												sel = editor.getSelection();
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Start transaction - do not allow any mutations to the
							 | 
						|||
| 
								 | 
							
											// snapshots stack done when selecting bookmarks (much probably
							 | 
						|||
| 
								 | 
							
											// by selectionChange listener).
							 | 
						|||
| 
								 | 
							
											this.locked = 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.editor.loadSnapshot( image.contents );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( image.bookmarks )
							 | 
						|||
| 
								 | 
							
												sel.selectBookmarks( image.bookmarks );
							 | 
						|||
| 
								 | 
							
											else if ( CKEDITOR.env.ie ) {
							 | 
						|||
| 
								 | 
							
												// IE BUG: If I don't set the selection to *somewhere* after setting
							 | 
						|||
| 
								 | 
							
												// document contents, then IE would create an empty paragraph at the bottom
							 | 
						|||
| 
								 | 
							
												// the next time the document is modified.
							 | 
						|||
| 
								 | 
							
												var $range = this.editor.document.getBody().$.createTextRange();
							 | 
						|||
| 
								 | 
							
												$range.collapse( true );
							 | 
						|||
| 
								 | 
							
												$range.select();
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.locked = 0;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											this.index = image.index;
							 | 
						|||
| 
								 | 
							
											this.currentImage = this.snapshots[ this.index ];
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Update current image with the actual editor
							 | 
						|||
| 
								 | 
							
											// content, since actualy content may differ from
							 | 
						|||
| 
								 | 
							
											// the original snapshot due to dom change. (#4622)
							 | 
						|||
| 
								 | 
							
											this.update();
							 | 
						|||
| 
								 | 
							
											this.fireChange();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											editor.fire( 'change' );
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										// Get the closest available image.
							 | 
						|||
| 
								 | 
							
										getNextImage: function( isUndo ) {
							 | 
						|||
| 
								 | 
							
											var snapshots = this.snapshots,
							 | 
						|||
| 
								 | 
							
												currentImage = this.currentImage,
							 | 
						|||
| 
								 | 
							
												image, i;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( currentImage ) {
							 | 
						|||
| 
								 | 
							
												if ( isUndo ) {
							 | 
						|||
| 
								 | 
							
													for ( i = this.index - 1; i >= 0; i-- ) {
							 | 
						|||
| 
								 | 
							
														image = snapshots[ i ];
							 | 
						|||
| 
								 | 
							
														if ( !currentImage.equalsContent( image ) ) {
							 | 
						|||
| 
								 | 
							
															image.index = i;
							 | 
						|||
| 
								 | 
							
															return image;
							 | 
						|||
| 
								 | 
							
														}
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												} else {
							 | 
						|||
| 
								 | 
							
													for ( i = this.index + 1; i < snapshots.length; i++ ) {
							 | 
						|||
| 
								 | 
							
														image = snapshots[ i ];
							 | 
						|||
| 
								 | 
							
														if ( !currentImage.equalsContent( image ) ) {
							 | 
						|||
| 
								 | 
							
															image.index = i;
							 | 
						|||
| 
								 | 
							
															return image;
							 | 
						|||
| 
								 | 
							
														}
							 | 
						|||
| 
								 | 
							
													}
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return null;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Checks the current redo state.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @returns {Boolean} Whether the document has a previous state to retrieve.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										redoable: function() {
							 | 
						|||
| 
								 | 
							
											return this.enabled && this.hasRedo;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Checks the current undo state.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @returns {Boolean} Whether the document has a future state to restore.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										undoable: function() {
							 | 
						|||
| 
								 | 
							
											return this.enabled && this.hasUndo;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Performs undo on current index.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										undo: function() {
							 | 
						|||
| 
								 | 
							
											if ( this.undoable() ) {
							 | 
						|||
| 
								 | 
							
												this.save( true );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												var image = this.getNextImage( true );
							 | 
						|||
| 
								 | 
							
												if ( image )
							 | 
						|||
| 
								 | 
							
													return this.restoreImage( image ), true;
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return false;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Performs redo on current index.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										redo: function() {
							 | 
						|||
| 
								 | 
							
											if ( this.redoable() ) {
							 | 
						|||
| 
								 | 
							
												// Try to save. If no changes have been made, the redo stack
							 | 
						|||
| 
								 | 
							
												// will not change, so it will still be redoable.
							 | 
						|||
| 
								 | 
							
												this.save( true );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
												// If instead we had changes, we can't redo anymore.
							 | 
						|||
| 
								 | 
							
												if ( this.redoable() ) {
							 | 
						|||
| 
								 | 
							
													var image = this.getNextImage( false );
							 | 
						|||
| 
								 | 
							
													if ( image )
							 | 
						|||
| 
								 | 
							
														return this.restoreImage( image ), true;
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											return false;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Updates the last snapshot of the undo stack with the current editor content.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @param {CKEDITOR.plugins.undo.Image} [newImage] The image which will replace the current one.
							 | 
						|||
| 
								 | 
							
										 * If not set defaults to image taken from editor.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										update: function( newImage ) {
							 | 
						|||
| 
								 | 
							
											// Do not change snapshots stack is locked.
							 | 
						|||
| 
								 | 
							
											if ( this.locked )
							 | 
						|||
| 
								 | 
							
												return;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											if ( !newImage )
							 | 
						|||
| 
								 | 
							
												newImage = new Image( this.editor );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											var i = this.index,
							 | 
						|||
| 
								 | 
							
												snapshots = this.snapshots;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											// Find all previous snapshots made for the same content (which differ
							 | 
						|||
| 
								 | 
							
											// only by selection) and replace all of them with the current image.
							 | 
						|||
| 
								 | 
							
											while ( i > 0 && this.currentImage.equalsContent( snapshots[ i - 1 ] ) )
							 | 
						|||
| 
								 | 
							
												i -= 1;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
											snapshots.splice( i, this.index - i + 1, newImage );
							 | 
						|||
| 
								 | 
							
											this.index = i;
							 | 
						|||
| 
								 | 
							
											this.currentImage = newImage;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Locks the snapshot stack to prevent any save/update operations and when necessary,
							 | 
						|||
| 
								 | 
							
										 * updates the tip of the snapshot stack with the DOM changes introduced during the
							 | 
						|||
| 
								 | 
							
										 * locked period, after the {@link #unlock} method is called.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * It is mainly used to ensure any DOM operations that should not be recorded
							 | 
						|||
| 
								 | 
							
										 * (e.g. auto paragraphing) are not added to the stack.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * **Note:** For every `lock` call you must call {@link #unlock} once to unlock the undo manager.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @since 4.0
							 | 
						|||
| 
								 | 
							
										 * @param {Boolean} [dontUpdate] When set to `true`, the last snapshot will not be updated
							 | 
						|||
| 
								 | 
							
										 * with current contents and selection. By default, if undo manager was up to date when the lock started,
							 | 
						|||
| 
								 | 
							
										 * the last snapshot will be updated to the current state when unlocking. This means that all changes
							 | 
						|||
| 
								 | 
							
										 * done during the lock will be merged into the previous snapshot or the next one. Use this option to gain
							 | 
						|||
| 
								 | 
							
										 * more control over this behavior. For example, it is possible to group changes done during the lock into
							 | 
						|||
| 
								 | 
							
										 * a separate snapshot.
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										lock: function( dontUpdate ) {
							 | 
						|||
| 
								 | 
							
											if ( !this.locked ) {
							 | 
						|||
| 
								 | 
							
												if ( dontUpdate )
							 | 
						|||
| 
								 | 
							
													this.locked = { level: 1 };
							 | 
						|||
| 
								 | 
							
												else {
							 | 
						|||
| 
								 | 
							
													// Make a contents image. Don't include bookmarks, because:
							 | 
						|||
| 
								 | 
							
													// * we don't compare them,
							 | 
						|||
| 
								 | 
							
													// * there's a chance that DOM has been changed since
							 | 
						|||
| 
								 | 
							
													// locked (e.g. fake) selection was made, so createBookmark2 could fail.
							 | 
						|||
| 
								 | 
							
													// http://dev.ckeditor.com/ticket/11027#comment:3
							 | 
						|||
| 
								 | 
							
													var imageBefore = new Image( this.editor, true );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													// If current editor content matches the tip of snapshot stack,
							 | 
						|||
| 
								 | 
							
													// the stack tip must be updated by unlock, to include any changes made
							 | 
						|||
| 
								 | 
							
													// during this period.
							 | 
						|||
| 
								 | 
							
													var matchedTip = this.currentImage && this.currentImage.equalsContent( imageBefore );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													this.locked = { update: matchedTip ? imageBefore : null, level: 1 };
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
											// Increase the level of lock.
							 | 
						|||
| 
								 | 
							
											else
							 | 
						|||
| 
								 | 
							
												this.locked.level++;
							 | 
						|||
| 
								 | 
							
										},
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
										/**
							 | 
						|||
| 
								 | 
							
										 * Unlocks the snapshot stack and checks to amend the last snapshot.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * See {@link #lock} for more details.
							 | 
						|||
| 
								 | 
							
										 *
							 | 
						|||
| 
								 | 
							
										 * @since 4.0
							 | 
						|||
| 
								 | 
							
										 */
							 | 
						|||
| 
								 | 
							
										unlock: function() {
							 | 
						|||
| 
								 | 
							
											if ( this.locked ) {
							 | 
						|||
| 
								 | 
							
												// Decrease level of lock and check if equals 0, what means that undoM is completely unlocked.
							 | 
						|||
| 
								 | 
							
												if ( !--this.locked.level ) {
							 | 
						|||
| 
								 | 
							
													var updateImage = this.locked.update,
							 | 
						|||
| 
								 | 
							
														newImage = updateImage && new Image( this.editor, true );
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													this.locked = null;
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
													if ( updateImage && !updateImage.equalsContent( newImage ) )
							 | 
						|||
| 
								 | 
							
														this.update();
							 | 
						|||
| 
								 | 
							
												}
							 | 
						|||
| 
								 | 
							
											}
							 | 
						|||
| 
								 | 
							
										}
							 | 
						|||
| 
								 | 
							
									};
							 | 
						|||
| 
								 | 
							
								} )();
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * The number of undo steps to be saved. The higher value is set, the more
							 | 
						|||
| 
								 | 
							
								 * memory is used for it.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 *		config.undoStackSize = 50;
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * @cfg {Number} [undoStackSize=20]
							 | 
						|||
| 
								 | 
							
								 * @member CKEDITOR.config
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * Fired when the editor is about to save an undo snapshot. This event can be
							 | 
						|||
| 
								 | 
							
								 * fired by plugins and customizations to make the editor save undo snapshots.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * @event saveSnapshot
							 | 
						|||
| 
								 | 
							
								 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * Fired before an undo image is to be taken. An undo image represents the
							 | 
						|||
| 
								 | 
							
								 * editor state at some point. It is saved into the undo store, so the editor is
							 | 
						|||
| 
								 | 
							
								 * able to recover the editor state on undo and redo operations.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * @since 3.5.3
							 | 
						|||
| 
								 | 
							
								 * @event beforeUndoImage
							 | 
						|||
| 
								 | 
							
								 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
								 * @see CKEDITOR.editor#afterUndoImage
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * Fired after an undo image is taken. An undo image represents the
							 | 
						|||
| 
								 | 
							
								 * editor state at some point. It is saved into the undo store, so the editor is
							 | 
						|||
| 
								 | 
							
								 * able to recover the editor state on undo and redo operations.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * @since 3.5.3
							 | 
						|||
| 
								 | 
							
								 * @event afterUndoImage
							 | 
						|||
| 
								 | 
							
								 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
								 * @see CKEDITOR.editor#beforeUndoImage
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * Fired when the content of the editor is changed.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * Due to performance reasons, it is not verified if the content really changed.
							 | 
						|||
| 
								 | 
							
								 * The editor instead watches several editing actions that usually result in
							 | 
						|||
| 
								 | 
							
								 * changes. This event may thus in some cases be fired when no changes happen
							 | 
						|||
| 
								 | 
							
								 * or may even get fired twice.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * If it is important not to get the change event too often, you should compare the
							 | 
						|||
| 
								 | 
							
								 * previous and the current editor content inside the event listener.
							 | 
						|||
| 
								 | 
							
								 *
							 | 
						|||
| 
								 | 
							
								 * @since 4.2
							 | 
						|||
| 
								 | 
							
								 * @event change
							 | 
						|||
| 
								 | 
							
								 * @member CKEDITOR.editor
							 | 
						|||
| 
								 | 
							
								 * @param {CKEDITOR.editor} editor This editor instance.
							 | 
						|||
| 
								 | 
							
								 */
							 |