Index: /FCKeditor/trunk/_whatsnew.html
===================================================================
--- /FCKeditor/trunk/_whatsnew.html	(revision 399)
+++ /FCKeditor/trunk/_whatsnew.html	(revision 400)
@@ -35,4 +35,7 @@
 		New Features and Improvements:</p>
 	<ul>
+		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/915">#915</a>] The undo/redo system has been
+			revamped to work the same across Internet Explorer and Gecko-based browsers (e.g. Firefox). A number
+			of critical bugs in the undo/redo system are also fixed.</li>
 		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/194">#194</a>] The editor
 			now uses the <strong>Data Processor</strong> technology, which makes it possible
Index: /FCKeditor/trunk/editor/_source/classes/fckdomrange_ie.js
===================================================================
--- /FCKeditor/trunk/editor/_source/classes/fckdomrange_ie.js	(revision 399)
+++ /FCKeditor/trunk/editor/_source/classes/fckdomrange_ie.js	(revision 400)
Index: /FCKeditor/trunk/editor/_source/internals/fck.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fck.js	(revision 399)
+++ /FCKeditor/trunk/editor/_source/internals/fck.js	(revision 400)
@@ -625,4 +625,52 @@
 	_InsertBlockElement : function( blockElement )
 	{
+	},
+
+	_IsFunctionKey : function(keyCode)
+	{
+		// keys that are captured but do not change editor contents
+		if (keyCode >= 16 && keyCode <= 20)
+			// shift, ctrl, alt, pause, capslock
+			return true;
+		if (keyCode == 27 || (keyCode >= 33 && keyCode <= 40))
+			// esc, page up, page down, end, home, left, up, right, down
+			return true;
+		if (keyCode == 45)
+			// insert, no effect on FCKeditor, yet
+			return true;
+		return false;
+	},
+
+	_KeyDownListener : function( evt )
+	{
+		if (! evt)
+			evt = FCK.EditorWindow.event;
+		if ( FCK.EditorWindow )
+		{
+			if ( !FCK._IsFunctionKey(evt.keyCode) // do not capture function key presses, like arrow keys or shift/alt/ctrl
+					&& !(evt.ctrlKey || evt.metaKey) // do not capture Ctrl hotkeys, as they have their snapshot capture logic
+					&& !(evt.keyCode == 46) ) // do not capture Del, it has its own capture logic in fckenterkey.js
+				FCK._KeyDownUndo();
+		}
+		return true;
+	},
+
+	_KeyDownUndo : function()
+	{
+		if ( !FCKUndo.Typing )
+		{
+			FCKUndo.SaveUndoStep() ;
+			FCKUndo.Typing = true ;
+			FCK.Events.FireEvent( "OnSelectionChange" ) ;
+		}
+
+		FCKUndo.TypesCount++ ;
+		FCKUndo.Changed = 1;
+
+		if ( FCKUndo.TypesCount > FCKUndo.MaxTypes )
+		{
+			FCKUndo.TypesCount = 0 ;
+			FCKUndo.SaveUndoStep() ;
+		}
 	}
 } ;
Index: /FCKeditor/trunk/editor/_source/internals/fck_gecko.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fck_gecko.js	(revision 399)
+++ /FCKeditor/trunk/editor/_source/internals/fck_gecko.js	(revision 400)
@@ -25,50 +25,4 @@
 
 FCK.Description = "FCKeditor for Gecko Browsers" ;
-
-FCK._KeyDownUndo = function()
-{
-	if ( !FCKUndo.Typing )
-	{
-		FCKUndo.SaveUndoStep() ;
-		FCKUndo.Typing = true ;
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-
-	FCKUndo.TypesCount++ ;
-	FCKUndo.Changed = 1;
-
-	if ( FCKUndo.TypesCount > FCKUndo.MaxTypes )
-	{
-		FCKUndo.TypesCount = 0 ;
-		FCKUndo.SaveUndoStep() ;
-	}
-}
-
-FCK._IsFunctionKey = function(keyCode)
-{
-	// keys that are captured but do not change editor contents
-	if (keyCode >= 16 && keyCode <= 20)
-		// shift, ctrl, alt, pause, capslock
-		return true;
-	if (keyCode == 27 || (keyCode >= 33 && keyCode <= 40))
-		// esc, page up, page down, end, home, left, up, right, down
-		return true;
-	if (keyCode == 45)
-		// insert, no effect on FCKeditor, yet
-		return true;
-	return false;
-}
-
-FCK._KeyDownListener = function( evt )
-{
-	if ( FCK.EditorWindow )
-	{
-		if ( !FCK._IsFunctionKey(evt.keyCode) // do not capture function key presses, like arrow keys or shift/alt/ctrl
-			&& !(evt.ctrlKey || evt.metaKey) // do not capture Ctrl hotkeys, as they have their snapshot capture logic
-			&& !(evt.keyCode == 46) ) // do not capture Del, it has its own capture logic in fckenterkey.js
-			FCK._KeyDownUndo();
-	}
-	return true;
-}
 
 FCK.InitializeBehaviors = function()
Index: /FCKeditor/trunk/editor/_source/internals/fck_ie.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fck_ie.js	(revision 399)
+++ /FCKeditor/trunk/editor/_source/internals/fck_ie.js	(revision 400)
@@ -77,34 +77,4 @@
 }
 
-function Doc_OnKeyDown()
-{
-	if ( FCK.EditorWindow )
-	{
-		var e = FCK.EditorWindow.event ;
-
-		if ( !( e.keyCode >=16 && e.keyCode <= 18 ) )
-			Doc_OnKeyDownUndo() ;
-	}
-	return true ;
-}
-
-function Doc_OnKeyDownUndo()
-{
-	if ( !FCKUndo.Typing )
-	{
-		FCKUndo.SaveUndoStep() ;
-		FCKUndo.Typing = true ;
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-
-	FCKUndo.TypesCount++ ;
-
-	if ( FCKUndo.TypesCount > FCKUndo.MaxTypes )
-	{
-		FCKUndo.TypesCount = 0 ;
-		FCKUndo.SaveUndoStep() ;
-	}
-}
-
 function Doc_OnDblClick()
 {
@@ -137,5 +107,5 @@
 			window.FCKTabHTML += "&nbsp;" ;
 	}
-	this.EditorDocument.attachEvent("onkeydown", Doc_OnKeyDown ) ;
+	this.EditorDocument.attachEvent("onkeydown", FCK._KeyDownListener ) ;
 
 	this.EditorDocument.attachEvent("ondblclick", Doc_OnDblClick ) ;
Index: /FCKeditor/trunk/editor/_source/internals/fckundo.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fckundo.js	(revision 400)
+++ /FCKeditor/trunk/editor/_source/internals/fckundo.js	(revision 400)
@@ -0,0 +1,180 @@
+﻿/*
+ * FCKeditor - The text editor for Internet - http://www.fckeditor.net
+ * Copyright (C) 2003-2007 Frederico Caldeira Knabben
+ *
+ * == BEGIN LICENSE ==
+ *
+ * Licensed under the terms of any of the following licenses at your
+ * choice:
+ *
+ *  - GNU General Public License Version 2 or later (the "GPL")
+ *    http://www.gnu.org/licenses/gpl.html
+ *
+ *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
+ *    http://www.gnu.org/licenses/lgpl.html
+ *
+ *  - Mozilla Public License Version 1.1 or later (the "MPL")
+ *    http://www.mozilla.org/MPL/MPL-1.1.html
+ *
+ * == END LICENSE ==
+ *
+ * Gecko specific implementation for the Undo/Redo system.
+ */
+
+var FCKUndo = new Object() ;
+
+FCKUndo.SavedData = new Array() ;
+FCKUndo.CurrentIndex = -1 ;
+FCKUndo.TypesCount = 0 ;
+FCKUndo.Changed = false ;	// Is the document changed in respect to its initial image?
+FCKUndo.MaxTypes = 25 ;
+FCKUndo.Typing = false;
+
+FCKUndo._GetBookmark = function()
+{
+	if (FCKBrowserInfo.IsIE)
+	{
+		var selection = FCK.EditorDocument.selection;
+		if (selection.type == 'Text')
+			return selection.createRange().getBookmark();
+		else
+			return null;
+	}
+	else
+	{
+		var range = new FCKDomRange(FCK.EditorWindow);
+		range.MoveToSelection();
+		return range.CreateBookmark2();
+	}
+}
+
+FCKUndo._SelectBookmark = function(bookmark)
+{
+	if (! bookmark)
+		return;
+	if (FCKBrowserInfo.IsIE)
+	{
+		var range = FCK.EditorDocument.selection.createRange();
+		range.moveToBookmark(bookmark);
+		range.select();
+	}
+	else
+	{
+		var range = new FCKDomRange(FCK.EditorWindow);
+		if (bookmark instanceof Object)
+		{
+			range.MoveToBookmark2(bookmark);
+			try
+			{
+				// this does not always succeed, there are still some tricky cases where it fails
+				// e.g. add a special character at end of document, undo, redo -> error
+				range.Select();
+			}
+			catch (e)
+			{
+				// if select restore fails, put the caret at the end of the document
+				range.MoveToPosition(FCK.EditorDocument.body, 4);
+				range.Select();
+			}
+		}
+	}
+}
+
+FCKUndo.SaveUndoStep = function()
+{
+	if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
+		return ;
+
+	// Assume the editor content is changed when SaveUndoStep() is called after the first time.
+	// This also enables the undo button in toolbar.
+	if (FCKUndo.SavedData.length)
+		FCKUndo.Changed = true;
+
+	// Get the HTML content.
+	var sHtml = FCK.EditorDocument.body.innerHTML ;
+
+	// Shrink the array to the current level.
+	FCKUndo.SavedData = FCKUndo.SavedData.slice( 0, FCKUndo.CurrentIndex + 1 ) ;
+
+	// Cancel operation if the new step is identical to the previous one.
+	if ( FCKUndo.CurrentIndex > 0 && sHtml == FCKUndo.SavedData[ FCKUndo.CurrentIndex ][0] )
+		return ;
+	// Save the selection and caret position in the first undo level for the first change.
+	else if (FCKUndo.CurrentIndex == 0 && sHtml == FCKUndo.SavedData[0][0])
+	{
+		FCKUndo.SavedData[0][1] = FCKUndo._GetBookmark();
+		return;
+	}
+
+	// If we reach the Maximun number of undo levels, we must remove the first
+	// entry of the list shifting all elements.
+	if ( FCKUndo.CurrentIndex + 1 >= FCKConfig.MaxUndoLevels )
+		FCKUndo.SavedData.shift() ;
+	else
+		FCKUndo.CurrentIndex++ ;
+
+	// Save the new level in front of the actual position.
+	FCKUndo.SavedData[ FCKUndo.CurrentIndex ] = [ sHtml, FCKUndo._GetBookmark() ] ;
+
+	FCK.Events.FireEvent( "OnSelectionChange" ) ;
+}
+
+FCKUndo.CheckUndoState = function()
+{
+	return ( FCKUndo.Changed || FCKUndo.CurrentIndex > 0 ) ;
+}
+
+FCKUndo.CheckRedoState = function()
+{
+	return ( FCKUndo.CurrentIndex < ( FCKUndo.SavedData.length - 1 ) ) ;
+}
+
+FCKUndo.Undo = function()
+{
+	if ( FCKUndo.CheckUndoState() )
+	{
+		// If it is the first step.
+		if ( FCKUndo.CurrentIndex == ( FCKUndo.SavedData.length - 1 ) )
+		{
+			// Save the actual state for a possible "Redo" call.
+			FCKUndo.SaveUndoStep() ;
+		}
+
+		// Go a step back.
+		FCKUndo._ApplyUndoLevel( --FCKUndo.CurrentIndex ) ;
+
+		FCK.Events.FireEvent( "OnSelectionChange" ) ;
+	}
+}
+
+FCKUndo.Redo = function()
+{
+	if ( FCKUndo.CheckRedoState() )
+	{
+		// Go a step forward.
+		FCKUndo._ApplyUndoLevel( ++FCKUndo.CurrentIndex ) ;
+
+		FCK.Events.FireEvent( "OnSelectionChange" ) ;
+	}
+}
+
+FCKUndo._ApplyUndoLevel = function(level)
+{
+	var oData = FCKUndo.SavedData[ level ] ;
+
+	if ( !oData )
+		return ;
+
+	// Update the editor contents with that step data.
+	if (FCKBrowserInfo.IsIE)
+		FCK.SetInnerHtml(oData[0]);
+	else
+		FCK.EditorDocument.body.innerHTML = oData[0] ;
+
+	// Restore the selection
+	FCKUndo._SelectBookmark(oData[1]);
+
+	FCKUndo.TypesCount = 0 ;
+	FCKUndo.Changed = false;
+	FCKUndo.Typing = false ;
+}
Index: Keditor/trunk/editor/_source/internals/fckundo_gecko.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fckundo_gecko.js	(revision 399)
+++ 	(revision )
@@ -1,152 +1,0 @@
-﻿/*
- * FCKeditor - The text editor for Internet - http://www.fckeditor.net
- * Copyright (C) 2003-2007 Frederico Caldeira Knabben
- *
- * == BEGIN LICENSE ==
- *
- * Licensed under the terms of any of the following licenses at your
- * choice:
- *
- *  - GNU General Public License Version 2 or later (the "GPL")
- *    http://www.gnu.org/licenses/gpl.html
- *
- *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
- *    http://www.gnu.org/licenses/lgpl.html
- *
- *  - Mozilla Public License Version 1.1 or later (the "MPL")
- *    http://www.mozilla.org/MPL/MPL-1.1.html
- *
- * == END LICENSE ==
- *
- * Gecko specific implementation for the Undo/Redo system.
- */
-
-var FCKUndo = new Object() ;
-
-FCKUndo.SavedData = new Array() ;
-FCKUndo.CurrentIndex = -1 ;
-FCKUndo.TypesCount = 0 ;
-FCKUndo.Changed = false ;	// Is the document changed in respect to its initial image?
-FCKUndo.MaxTypes = 25 ;
-FCKUndo.Typing = false;
-
-FCKUndo.SaveUndoStep = function()
-{
-	if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
-		return ;
-
-	// Assume the editor content is changed when SaveUndoStep() is called after the first time.
-	// This also enables the undo button in toolbar.
-	if (FCKUndo.SavedData.length)
-		FCKUndo.Changed = true;
-
-	// Get the HTML content.
-	var sHtml = FCK.EditorDocument.body.innerHTML ;
-
-	// Shrink the array to the current level.
-	FCKUndo.SavedData = FCKUndo.SavedData.slice( 0, FCKUndo.CurrentIndex + 1 ) ;
-
-	// Cancel operation if the new step is identical to the previous one.
-	if ( FCKUndo.CurrentIndex > 0 && sHtml == FCKUndo.SavedData[ FCKUndo.CurrentIndex ][0] )
-		return ;
-	// Save the selection and caret position in the first undo level for the first change.
-	else if (FCKUndo.CurrentIndex == 0 && sHtml == FCKUndo.SavedData[0][0])
-	{
-		var range = new FCKDomRange(FCK.EditorWindow);
-		range.MoveToSelection();
-		var bookmark = range.CreateBookmark2();
-		FCKUndo.SavedData[0][1] = bookmark;
-		return;
-	}
-
-	// If we reach the Maximun number of undo levels, we must remove the first
-	// entry of the list shifting all elements.
-	if ( FCKUndo.CurrentIndex + 1 >= FCKConfig.MaxUndoLevels )
-		FCKUndo.SavedData.shift() ;
-	else
-		FCKUndo.CurrentIndex++ ;
-
-	// Get the actual selection.
-	var range = new FCKDomRange(FCK.EditorWindow);
-	range.MoveToSelection();
-	var bookmark = range.CreateBookmark2();
-
-	// Save the new level in front of the actual position.
-	FCKUndo.SavedData[ FCKUndo.CurrentIndex ] = [ sHtml, bookmark ] ;
-
-	FCK.Events.FireEvent( "OnSelectionChange" ) ;
-}
-
-FCKUndo.CheckUndoState = function()
-{
-	return ( FCKUndo.Changed || FCKUndo.CurrentIndex > 0 ) ;
-}
-
-FCKUndo.CheckRedoState = function()
-{
-	return ( FCKUndo.CurrentIndex < ( FCKUndo.SavedData.length - 1 ) ) ;
-}
-
-FCKUndo.Undo = function()
-{
-	if ( FCKUndo.CheckUndoState() )
-	{
-		// If it is the first step.
-		if ( FCKUndo.CurrentIndex == ( FCKUndo.SavedData.length - 1 ) )
-		{
-			// Save the actual state for a possible "Redo" call.
-			FCKUndo.SaveUndoStep() ;
-		}
-
-		// Go a step back.
-		FCKUndo._ApplyUndoLevel( --FCKUndo.CurrentIndex ) ;
-
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-}
-
-FCKUndo.Redo = function()
-{
-	if ( FCKUndo.CheckRedoState() )
-	{
-		// Go a step forward.
-		FCKUndo._ApplyUndoLevel( ++FCKUndo.CurrentIndex ) ;
-
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-}
-
-FCKUndo._ApplyUndoLevel = function(level)
-{
-	var oData = FCKUndo.SavedData[ level ] ;
-
-	if ( !oData )
-		return ;
-
-	// Update the editor contents with that step data.
-	FCK.EditorDocument.body.innerHTML = oData[0] ;
-
-	// Restore the selection
-	var range = new FCKDomRange(FCK.EditorWindow);
-	var bookmark = oData[1];
-	if (bookmark instanceof Object)
-	{
-		range.MoveToBookmark2(bookmark);
-		try
-		{
-			// this does not always succeed, there are still some tricky cases where it fails
-			// e.g. add a special character at end of document, undo, redo -> error
-			range.Select();
-		}
-		catch (e)
-		{
-			// if select restore fails, put the caret at the end of the document
-			range.MoveToPosition(FCK.EditorDocument.body, 4);
-			range.Select();
-		}
-	}
-
-	FCKUndo.TypesCount = 0 ;
-	FCKUndo.Changed = false;
-	FCKUndo.Typing = false ;
-}
Index: Keditor/trunk/editor/_source/internals/fckundo_ie.js
===================================================================
--- /FCKeditor/trunk/editor/_source/internals/fckundo_ie.js	(revision 399)
+++ 	(revision )
@@ -1,123 +1,0 @@
-﻿/*
- * FCKeditor - The text editor for Internet - http://www.fckeditor.net
- * Copyright (C) 2003-2007 Frederico Caldeira Knabben
- *
- * == BEGIN LICENSE ==
- *
- * Licensed under the terms of any of the following licenses at your
- * choice:
- *
- *  - GNU General Public License Version 2 or later (the "GPL")
- *    http://www.gnu.org/licenses/gpl.html
- *
- *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
- *    http://www.gnu.org/licenses/lgpl.html
- *
- *  - Mozilla Public License Version 1.1 or later (the "MPL")
- *    http://www.mozilla.org/MPL/MPL-1.1.html
- *
- * == END LICENSE ==
- *
- * IE specific implementation for the Undo/Redo system.
- */
-
-var FCKUndo = new Object() ;
-
-FCKUndo.SavedData = new Array() ;
-FCKUndo.CurrentIndex = -1 ;
-FCKUndo.TypesCount = FCKUndo.MaxTypes = 25 ;
-FCKUndo.Typing = false ;
-
-FCKUndo.SaveUndoStep = function()
-{
-	if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG )
-		return ;
-
-	// Shrink the array to the current level.
-	FCKUndo.SavedData = FCKUndo.SavedData.slice( 0, FCKUndo.CurrentIndex + 1 ) ;
-
-	// Get the Actual HTML.
-	var sHtml = FCK.EditorDocument.body.innerHTML ;
-
-	// Cancel operation if the new step is identical to the previous one.
-	if ( FCKUndo.CurrentIndex >= 0 && sHtml == FCKUndo.SavedData[ FCKUndo.CurrentIndex ][0] )
-		return ;
-
-	// If we reach the Maximun number of undo levels, we must remove the first
-	// entry of the list shifting all elements.
-	if ( FCKUndo.CurrentIndex + 1 >= FCKConfig.MaxUndoLevels )
-		FCKUndo.SavedData.shift() ;
-	else
-		FCKUndo.CurrentIndex++ ;
-
-	// Get the actual selection.
-	var sBookmark ;
-	if ( FCK.EditorDocument.selection.type == 'Text' )
-		sBookmark = FCK.EditorDocument.selection.createRange().getBookmark() ;
-
-	// Save the new level in front of the actual position.
-	FCKUndo.SavedData[ FCKUndo.CurrentIndex ] = [ sHtml, sBookmark ] ;
-
-	FCK.Events.FireEvent( "OnSelectionChange" ) ;
-}
-
-FCKUndo.CheckUndoState = function()
-{
-	return ( FCKUndo.Typing || FCKUndo.CurrentIndex > 0 ) ;
-}
-
-FCKUndo.CheckRedoState = function()
-{
-	return ( !FCKUndo.Typing && FCKUndo.CurrentIndex < ( FCKUndo.SavedData.length - 1 ) ) ;
-}
-
-FCKUndo.Undo = function()
-{
-	if ( FCKUndo.CheckUndoState() )
-	{
-		// If it is the first step.
-		if ( FCKUndo.CurrentIndex == ( FCKUndo.SavedData.length - 1 ) )
-		{
-			// Save the actual state for a possible "Redo" call.
-			FCKUndo.SaveUndoStep() ;
-		}
-
-		// Go a step back.
-		FCKUndo._ApplyUndoLevel( --FCKUndo.CurrentIndex ) ;
-
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-}
-
-FCKUndo.Redo = function()
-{
-	if ( FCKUndo.CheckRedoState() )
-	{
-		// Go a step forward.
-		FCKUndo._ApplyUndoLevel( ++FCKUndo.CurrentIndex ) ;
-
-		FCK.Events.FireEvent( "OnSelectionChange" ) ;
-	}
-}
-
-FCKUndo._ApplyUndoLevel = function(level)
-{
-	var oData = FCKUndo.SavedData[ level ] ;
-
-	if ( !oData )
-		return ;
-
-	// Update the editor contents with that step data.
-	FCK.SetInnerHtml( oData[0] ) ;
-//	FCK.EditorDocument.body.innerHTML = oData[0] ;
-
-	if ( oData[1] )
-	{
-		var oRange = FCK.EditorDocument.selection.createRange() ;
-		oRange.moveToBookmark( oData[1] ) ;
-		oRange.select() ;
-	}
-
-	FCKUndo.TypesCount = 0 ;
-	FCKUndo.Typing = false ;
-}
Index: /FCKeditor/trunk/editor/fckeditor.html
===================================================================
--- /FCKeditor/trunk/editor/fckeditor.html	(revision 399)
+++ /FCKeditor/trunk/editor/fckeditor.html	(revision 400)
@@ -82,5 +82,5 @@
 LoadScript( '_source/internals/fckxhtml_' + sSuffix + '.js' ) ;
 LoadScript( '_source/internals/fckcodeformatter.js' ) ;
-LoadScript( '_source/internals/fckundo_' + sSuffix + '.js' ) ;
+LoadScript( '_source/internals/fckundo.js' ) ;
 LoadScript( '_source/classes/fckeditingarea.js' ) ;
 LoadScript( '_source/classes/fckkeystrokehandler.js' ) ;
