Index: FCKeditor/trunk/_whatsnew.html
===================================================================
--- FCKeditor/trunk/_whatsnew.html	(revision 1971)
+++ FCKeditor/trunk/_whatsnew.html	(revision 1973)
@@ -40,4 +40,6 @@
 		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2142">#2142</a>] HTML samples will
 		now use sampleposteddata.php in action parameter inside a form.</li>
+		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2150">#2150</a>] The searching
+			speed of the Find/Replace dialog has been vastly improved.</li>
 	</ul>
 	<p>
@@ -76,4 +78,8 @@
 		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2126">#2126</a>] Opening and closing floating
 			dialogs will no longer cause toolbar button states to become frozen.</li>
+		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2159">#2159</a>] Selection are now
+			correctly restored when undoing changes made by the Replace dialog.</li>
+		<li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2160">#2160</a>] "Match whole word" in the
+			Find and Replace dialog will now find words next to punctuation marks as well.</li>
 	</ul>
 	<h3>
Index: FCKeditor/trunk/editor/dialog/fck_replace.html
===================================================================
--- FCKeditor/trunk/editor/dialog/fck_replace.html	(revision 1971)
+++ FCKeditor/trunk/editor/dialog/fck_replace.html	(revision 1973)
@@ -35,4 +35,10 @@
 
 var FCKLang = oEditor.FCKLang ;
+var FCKDomTools = oEditor.FCKDomTools ;
+var FCKDomRange = oEditor.FCKDomRange ;
+var FCKListsLib = oEditor.FCKListsLib ;
+var FCKTools = oEditor.FCKTools ;
+var EditorDocument = oEditor.FCK.EditorDocument ;
+var HighlightStyle = oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' )  ;
 
 dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ;
@@ -52,180 +58,252 @@
 }
 
-// Place a range at the start of document.
-// This will be the starting point of our search.
-var GlobalRange = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ;
-
-function ResetGlobalRange()
-{
-	GlobalRange.SetStart( oEditor.FCK.EditorDocument.body, 1 ) ;
-	GlobalRange.SetEnd( oEditor.FCK.EditorDocument.body, 1 ) ;
-	GlobalRange.Collapse( true ) ;
-}
-ResetGlobalRange() ;
-
-var HighlightRange = null ;
-function Highlight()
-{
-	if ( HighlightRange )
-		ClearHighlight() ;
-	var cloneRange = GlobalRange.Clone() ;
-	oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ).ApplyToRange( cloneRange, false, true ) ;
-	HighlightRange = cloneRange ;
-	GlobalRange = HighlightRange.Clone() ;
-}
-
-function ClearHighlight()
-{
-	if ( HighlightRange )
-	{
-		oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ).RemoveFromRange( HighlightRange, false, true ) ;
-		HighlightRange = null ;
-	}
-}
-
-function OnLoad()
-{
-	// First of all, translate the dialog box texts.
-	oEditor.FCKLanguageManager.TranslatePage( document ) ;
-
-	// Show the appropriate tab at startup.
-	if ( dialogArguments.CustomValue == 'Find' )
-	{
-		dialog.SetSelectedTab( 'Find' ) ;
-		dialog.SetAutoSize( true ) ;
+GetNextNonEmptyTextNode = function( node, stopNode )
+{
+	var node ;
+	while ( ( node = FCKDomTools.GetNextSourceNode( node, false, 3, stopNode ) ) && node && node.length < 1 ) ;
+	return node ;
+}
+
+CharacterCursor = function( arg )
+{
+	if ( arg.nodeType && arg.nodeType == 9 )
+	{
+		this._textNode = GetNextNonEmptyTextNode( arg.body, arg.documentElement ) ;
+		this._offset = 0 ;
+		this._doc = arg ;
 	}
 	else
-		dialog.SetSelectedTab( 'Replace' ) ;
-
-	SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
-}
-
-function btnStat()
-{
-	GetE('btnReplace').disabled =
-		GetE('btnReplaceAll').disabled =
-			GetE('btnFind').disabled =
-				( GetE(idMap["FindText"]).value.length == 0 ) ;
-}
-
-function btnStatDelayed()
-{
-	setTimeout( btnStat, 1 ) ;
-}
-
-function GetSearchString()
-{
-	return GetE(idMap['FindText']).value ;
-}
-
-function GetReplaceString()
-{
-	return GetE("txtReplace").value ;
-}
-
-function GetCheckCase()
-{
-	return !! ( GetE(idMap['CheckCase']).checked ) ;
-}
-
-function GetMatchWord()
-{
-	return !! ( GetE(idMap['CheckWord']).checked ) ;
-}
-
-// Get the data pointed to by a bookmark.
-function GetData( bookmark )
-{
-	var cursor = oEditor.FCK.EditorDocument.documentElement ;
-	for ( var i = 0 ; i < bookmark.length ; i++ )
-	{
-		var target = bookmark[i] ;
-		var currentIndex = -1 ;
-		if ( cursor.nodeType != 3 )
-		{
-			for (var j = 0 ; j < cursor.childNodes.length ; j++ )
-			{
-				var candidate = cursor.childNodes[j] ;
-				if ( candidate.nodeType == 3 &&
-						candidate.previousSibling &&
-						candidate.previousSibling.nodeType == 3 )
-					continue ;
-				currentIndex++ ;
-				if ( currentIndex == target )
-				{
-					cursor = candidate ;
-					break ;
-				}
-			}
-			if ( currentIndex < target )
-				return null ;
-		}
-		else
-		{
-			if ( i != bookmark.length - 1 )
-				return null ;
-			while ( target >= cursor.length && cursor.nextSibling && cursor.nextSibling.nodeType == 3 )
-			{
-				target -= cursor.length ;
-				cursor = cursor.nextSibling ;
-			}
-			cursor = cursor.nodeValue.charAt( target ) ;
-			if ( cursor == "" )
-				cursor = null ;
-		}
-	}
-	return cursor ;
-}
-
-// With this function, we can treat the bookmark as an iterator for DFS.
-function NextPosition( bookmark )
-{
-	// See if there's anything further down the tree.
-	var next = bookmark.concat( [0] ) ;
-	if ( GetData( next ) != null )
-		return next ;
-
-	// Nothing down there? See if there's anything next to me.
-	var next = bookmark.slice( 0, bookmark.length - 1 ).concat( [ bookmark[ bookmark.length - 1 ] + 1 ] ) ;
-	if ( GetData( next ) != null )
-		return next ;
-
-	// Nothing even next to me? See if there's anything next to my ancestors.
-	for ( var i = bookmark.length - 1 ; i > 0 ; i-- )
-	{
-		var next = bookmark.slice( 0, i - 1 ).concat( [ bookmark[ i - 1 ] + 1 ] ) ;
-		if ( GetData( next ) != null )
-			return next ;
-	}
-
-	// There's absolutely nothing left to walk, return null.
-	return null ;
-}
-
-// Is this character a unicode whitespace?
-// Reference: http://unicode.org/Public/UNIDATA/PropList.txt
-function CheckIsWhitespace( c )
-{
-	var code = c.charCodeAt( 0 );
-	if ( code >= 9 && code <= 0xd )
-		return true;
-	if ( code >= 0x2000 && code <= 0x200a )
-		return true;
-	switch ( code )
-	{
-		case 0x20:
-		case 0x85:
-		case 0xa0:
-		case 0x1680:
-		case 0x180e:
-		case 0x2028:
-		case 0x2029:
-		case 0x202f:
-		case 0x205f:
-		case 0x3000:
-			return true;
-		default:
-			return false;
-	}
+	{
+		this._textNode = arguments[0] ;
+		this._offset = arguments[1] ;
+		this._doc = FCKTools.GetElementDocument( arguments[0] ) ;
+	}
+}
+CharacterCursor.prototype =
+{
+	GetCharacter : function()
+	{
+		return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ;
+	},
+
+	// Non-normalized.
+	GetTextNode : function()
+	{
+		return this._textNode ;
+	},
+
+	// Non-normalized.
+	GetIndex : function()
+	{
+		return this._offset ;
+	},
+
+	// Return value means whehther we've crossed a line break or a paragraph boundary.
+	MoveNext : function()
+	{
+		if ( this._offset < this._textNode.length - 1 )
+		{
+			this._offset++ ;
+			return false ;
+		}
+
+		var crossed = false ;
+		var curNode = this._textNode ;
+		while ( ( curNode = FCKDomTools.GetNextSourceNode( curNode ) )
+				&& curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
+		{
+			var tag = curNode.nodeName.toLowerCase() ;
+			if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
+				crossed = true ;
+		}
+
+		this._textNode = curNode ;
+		this._offset = 0 ;
+		return crossed ;
+	},
+
+	// Return value means whehther we've crossed a line break or a paragraph boundary.
+	MoveBack : function()
+	{
+		if ( this._offset > 0 && this._textNode.length > 0 )
+		{
+			this._offset = Math.min( this._offset - 1, this._textNode.length - 1 ) ;
+			return false ;
+		}
+
+		var crossed = false ;
+		var curNode = this._textNode ;
+		while ( ( curNode = FCKDomTools.GetPreviousSourceNode( curNode ) )
+				&& curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
+		{
+			var tag = curNode.nodeName.toLowerCase() ;
+			if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
+				crossed = true ;
+		}
+
+		this._textNode = curNode ;
+		this._offset = curNode.length - 1 ;
+		return crossed ;
+	},
+
+	Clone : function()
+	{
+		return new CharacterCursor( this._textNode, this._offset ) ;
+	}
+} ;
+
+CharacterRange = function( initCursor, maxLength )
+{
+	this._cursors = initCursor.push ? initCursor : [initCursor] ;
+	this._maxLength = maxLength ;
+	this._highlightRange = null ;
+}
+CharacterRange.prototype =
+{
+	ToDomRange : function()
+	{
+		var firstCursor = this._cursors[0] ;
+		var lastCursor = this._cursors[ this._cursors.length - 1 ] ;
+		var domRange = new FCKDomRange( FCKTools.GetElementWindow( firstCursor.GetTextNode() ) ) ;
+		var w3cRange = domRange._Range = domRange.CreateRange() ;
+		w3cRange.setStart( firstCursor.GetTextNode(), firstCursor.GetIndex() ) ;
+		w3cRange.setEnd( lastCursor.GetTextNode(), lastCursor.GetIndex() + 1 ) ;
+		domRange._UpdateElementInfo() ;
+		return domRange ;
+	},
+
+	Highlight : function()
+	{
+		if ( this._cursors.length < 1 )
+			return ;
+
+		var domRange = this.ToDomRange() ;
+		HighlightStyle.ApplyToRange( domRange, false, true ) ;
+		this._highlightRange = domRange ;
+
+		var charRange = CharacterRange.CreateFromDomRange( domRange ) ;
+		var focusNode = domRange.StartNode ;
+		if ( focusNode.nodeType != 1 )
+			focusNode = focusNode.parentNode ;
+		focusNode.scrollIntoView( false ) ;
+		this._cursors = charRange._cursors ;
+	},
+
+	RemoveHighlight : function()
+	{
+		if ( this._highlightRange )
+		{
+			HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ;
+			var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ;
+			this._cursors = charRange._cursors ;
+			this._highlightRange = null ;
+		}
+	},
+
+	GetHighlightDomRange : function()
+	{
+		return this._highlightRange;
+	},
+
+	MoveNext : function()
+	{
+		var next = this._cursors[ this._cursors.length - 1 ].Clone() ;
+		var retval = next.MoveNext() ;
+		if ( retval )
+			this._cursors = [] ;
+		this._cursors.push( next ) ;
+		if ( this._cursors.length > this._maxLength )
+			this._cursors.shift() ;
+		return retval ;
+	},
+
+	MoveBack : function()
+	{
+		var prev = this._cursors[0].Clone() ;
+		var retval = prev.MoveBack() ;
+		if ( retval )
+			this._cursors = [] ;
+		this._cursors.unshift( prev ) ;
+		if ( this._cursors.length > this._maxLength )
+			this._cursors.pop() ;
+		return retval ;
+	},
+
+	GetEndCharacter : function()
+	{
+		if ( this._cursors.length < 1 )
+			return null ;
+		var retval = this._cursors[ this._cursors.length - 1 ].GetCharacter() ;
+		return retval ;
+	},
+
+	GetNextRange : function( len )
+	{		
+		if ( this._cursors.length == 0 )
+			return null ;
+		var cur = this._cursors[ this._cursors.length - 1 ].Clone() ;
+		cur.MoveNext() ;
+		return new CharacterRange( cur, len ) ;
+	},
+
+	GetCursors : function()
+	{
+		return this._cursors ;
+	}
+} ;
+
+CharacterRange.CreateFromDomRange = function( domRange )
+{
+	var w3cRange = domRange._Range ;
+	var startContainer = w3cRange.startContainer ;
+	var endContainer = w3cRange.endContainer ;
+	var startTextNode, startIndex, endTextNode, endIndex ;
+
+	if ( startContainer.nodeType == 3 )
+	{
+		startTextNode = startContainer ;
+		startIndex = w3cRange.startOffset ;
+	}
+	else if ( domRange.StartNode.nodeType == 3 )
+	{
+		startTextNode = domRange.StartNode ;
+		startIndex = 0 ;
+	}
+	else
+	{
+		startTextNode = GetNextNonEmptyTextNode( domRange.StartNode, domRange.StartNode.parentNode ) ;
+		if ( !startTextNode )
+			return null ;
+		startIndex = 0 ;
+	}
+
+	if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 )
+	{
+		endTextNode = endContainer ;
+		endIndex = w3cRange.endOffset - 1 ;
+	}
+	else
+	{
+		endTextNode = domRange.EndNode ;
+		while ( endTextNode.nodeType != 3 )
+			endTextNode = endTextNode.lastChild ;
+		endIndex = endTextNode.length - 1 ;
+	}
+
+	var cursors = [] ;
+	var current = new CharacterCursor( startTextNode, startIndex ) ;
+	cursors.push( current ) ;
+	if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() )
+	{
+		do
+		{
+			current = current.Clone() ;
+			current.MoveNext() ;
+			cursors.push( current ) ;
+		}
+		while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ;
+	}
+
+	return new CharacterRange( cursors, cursors.length ) ;
 }
 
@@ -234,5 +312,5 @@
 KMP_ADVANCED = 1 ;
 KMP_MATCHED = 2 ;
-function KmpMatch( pattern, ignoreCase )
+KmpMatch = function( pattern, ignoreCase )
 {
 	var overlap = [ -1 ] ;
@@ -252,5 +330,5 @@
 }
 KmpMatch.prototype = {
-	"FeedCharacter" : function( c )
+	FeedCharacter : function( c )
 	{
 		if ( this._IgnoreCase )
@@ -278,5 +356,6 @@
 		return null ;
 	},
-	"Reset" : function()
+
+	Reset : function()
 	{
 		this._State = 0 ;
@@ -284,152 +363,178 @@
 };
 
+// Place a range at the start of document.
+function OnLoad()
+{
+	// First of all, translate the dialog box texts.
+	oEditor.FCKLanguageManager.TranslatePage( document ) ;
+
+	// Show the appropriate tab at startup.
+	if ( dialogArguments.CustomValue == 'Find' )
+	{
+		dialog.SetSelectedTab( 'Find' ) ;
+		dialog.SetAutoSize( true ) ;
+	}
+	else
+		dialog.SetSelectedTab( 'Replace' ) ;
+
+	SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
+}
+
+function btnStat()
+{
+	GetE('btnReplace').disabled =
+		GetE('btnReplaceAll').disabled =
+			GetE('btnFind').disabled =
+				( GetE(idMap["FindText"]).value.length == 0 ) ;
+}
+
+function btnStatDelayed()
+{
+	setTimeout( btnStat, 1 ) ;
+}
+
+function GetSearchString()
+{
+	return GetE(idMap['FindText']).value ;
+}
+
+function GetReplaceString()
+{
+	return GetE("txtReplace").value ;
+}
+
+function GetCheckCase()
+{
+	return !! ( GetE(idMap['CheckCase']).checked ) ;
+}
+
+function GetMatchWord()
+{
+	return !! ( GetE(idMap['CheckWord']).checked ) ;
+}
+
+/* Is this character a unicode whitespace or a punctuation mark?
+ * References:
+ * http://unicode.org/Public/UNIDATA/PropList.txt (whitespaces)
+ * http://php.chinaunix.net/manual/tw/ref.regex.php (punctuation marks)
+ */
+function CheckIsWordSeparator( c )
+{
+	var code = c.charCodeAt( 0 );
+	if ( code >= 9 && code <= 0xd )
+		return true;
+	if ( code >= 0x2000 && code <= 0x200a )
+		return true;
+	switch ( code )
+	{
+		case 0x20:
+		case 0x85:
+		case 0xa0:
+		case 0x1680:
+		case 0x180e:
+		case 0x2028:
+		case 0x2029:
+		case 0x202f:
+		case 0x205f:
+		case 0x3000:
+			return true;
+		default:
+	}
+	return /[.,"'?!;:]/.test( c ) ;
+}
+
+FindRange = null ;
 function _Find()
 {
-	// Start from the end of the current selection.
-	var matcher = new KmpMatch( GetSearchString(), ! GetCheckCase() ) ;
-	var cursor = GlobalRange.CreateBookmark2().End ;
+	var searchString = GetSearchString() ;
+	if ( !FindRange )
+		FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ;
+	else
+	{
+		FindRange.RemoveHighlight() ;
+		FindRange = FindRange.GetNextRange( searchString.length ) ;
+	}
+	var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ;
 	var matchState = KMP_NOMATCH ;
-	var matchBookmark = null ;
-	var matchBookmarkStart = [] ;
-
-	// Match finding.
-	while ( true )
-	{
-		// Perform KMP stream matching.
-		//	- Reset KMP matcher if we encountered a block element.
-		var data = GetData( cursor ) ;
-		if ( data )
-		{
-			if ( data.tagName )
+	var character = '%' ;
+
+	while ( character != null )
+	{
+		while ( ( character = FindRange.GetEndCharacter() ) )
+		{
+			matchState = matcher.FeedCharacter( character ) ;
+			if ( matchState == KMP_MATCHED )
+				break ;
+			if ( FindRange.MoveNext() )
+				matcher.Reset() ;
+		}
+
+		if ( matchState == KMP_MATCHED )
+		{
+			if ( GetMatchWord() )
 			{
-				if ( oEditor.FCKListsLib.BlockElements[ data.tagName.toLowerCase() ] )
-				{
-					matcher.Reset();
-					matchBookmarkStart = [] ;
-				}
+				var cursors = FindRange.GetCursors() ;
+				var head = cursors[ cursors.length - 1 ].Clone() ;
+				var tail = cursors[0].Clone() ;
+				if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) )
+					continue ;
+				if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) )
+					continue ;
 			}
-			else if ( data.charAt != undefined )
-			{
-				matchState = matcher.FeedCharacter(data) ;
-
-				// No possible match of any useful substring in the pattern for the currently scanned character.
-				// So delete any positional information.
-				if ( matchState == KMP_NOMATCH )
-					matchBookmarkStart = [] ;
-				// We've matched something, but it's not a complete match, so let's just mark down the position for backtracking later.
-				else if ( matchState == KMP_ADVANCED )
-				{
-					matchBookmarkStart.push( cursor.concat( [] ) ) ;
-					if ( matchBookmarkStart.length > matcher._State )
-						matchBookmarkStart.shift() ;
-				}
-				// Found a complete match! Mark down the ending position as well.
-				else if ( matchState == KMP_MATCHED )
-				{
-					// It is possible to get a KMP_MATCHED without KMP_ADVANCED when the match pattern is only 1 character.
-					// So need to check and mark down the starting position as well.
-					if ( matchBookmarkStart.length == 0 )
-						matchBookmarkStart = [cursor.concat( [] )] ;
-
-					matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ;
-					matchBookmark.End[ matchBookmark.End.length - 1 ]++;
-
-					// Wait, do we have to match a whole word?
-					// If yes, carry out additional checks on what we've got.
-					if ( GetMatchWord() )
-					{
-						var startOk = false ;
-						var endOk = false ;
-						var start = matchBookmark.Start ;
-						var end = matchBookmark.End ;
-						if ( start[ start.length - 1 ] == 0 )
-							startOk = true ;
-						else
-						{
-							var cursorBeforeStart = start.slice( 0, start.length - 1 ) ;
-							cursorBeforeStart.push( start[ start.length - 1 ] - 1 ) ;
-							var dataBeforeStart = GetData( cursorBeforeStart ) ;
-							if ( dataBeforeStart == null || dataBeforeStart.charAt == undefined )
-								startOk = true ;
-							else if ( CheckIsWhitespace( dataBeforeStart ) )
-								startOk = true ;
-						}
-
-						// this is already one character beyond the last char, no need to move
-						var cursorAfterEnd = end ;
-						var dataAfterEnd = GetData( cursorAfterEnd );
-						if ( dataAfterEnd == null || dataAfterEnd.charAt == undefined )
-							endOk = true ;
-						else if ( CheckIsWhitespace( dataAfterEnd ) )
-							endOk = true ;
-
-						if ( startOk && endOk )
-							break ;
-						else
-							matcher.Reset() ;
-					}
-					else
-						break ;
-				}
-			}
-		}
-
-		// Perform DFS across the document, until we've reached the end.
-		cursor = NextPosition( cursor ) ;
-		if ( cursor == null )
-			break;
-	}
-
-	// If we've found a match, highlight the match.
-	if ( matchState == KMP_MATCHED )
-	{
-		GlobalRange.MoveToBookmark2( matchBookmark ) ;
-		Highlight() ;
-		var focus = GlobalRange._Range.endContainer ;
-		while ( focus && focus.nodeType != 1 )
-			focus = focus.parentNode ;
-
-		if ( focus )
-		{
-			if ( oEditor.FCKBrowserInfo.IsSafari )
-				oEditor.FCKDomTools.ScrollIntoView( focus, false ) ;
-			else
-				focus.scrollIntoView( false ) ;
-		}
-
-		return true ;
+
+			FindRange.Highlight() ;
+			return true ;
+		}
+	}
+
+	FindRange = null ;
+	return false ;
+}
+
+function Find()
+{
+	if ( ! _Find() )
+		alert( FCKLang.DlgFindNotFoundMsg ) ;
+}
+
+function Replace()
+{
+	var saveUndoStep = function( selectRange )
+	{
+		var ieRange ;
+		if ( oEditor.FCKBrowserInfo.IsIE )
+			ieRange = document.selection.createRange() ;
+
+		selectRange.Select() ;
+		oEditor.FCKUndo.SaveUndoStep() ;
+		var cloneRange = selectRange.Clone() ;
+		cloneRange.Collapse( false ) ;
+		cloneRange.Select() ;
+
+		if ( ieRange )
+			setTimeout( function(){ ieRange.select() ; }, 1 ) ;
+	}
+
+	if ( FindRange && FindRange.GetHighlightDomRange() )
+	{
+		var range = FindRange.GetHighlightDomRange() ;
+		var bookmark = range.CreateBookmark() ;
+		FindRange.RemoveHighlight() ;
+		range.MoveToBookmark( bookmark ) ;
+
+		saveUndoStep( range ) ;
+		range.DeleteContents() ;
+		range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
+		range._UpdateElementInfo() ;
+
+		FindRange = CharacterRange.CreateFromDomRange( range ) ;
 	}
 	else
 	{
-		ResetGlobalRange() ;
-		return false ;
-	}
-}
-
-function Find()
-{
-	if ( ! _Find() )
-	{
-		ClearHighlight() ;
-		alert( FCKLang.DlgFindNotFoundMsg ) ;
-	}
-}
-
-function Replace()
-{
-	if ( GlobalRange.CheckIsCollapsed() )
-	{
-		if (! _Find() )
-		{
-			ClearHighlight() ;
+		if ( ! _Find() )
+		{
+			FindRange && FindRange.RemoveHighlight() ;
 			alert( FCKLang.DlgFindNotFoundMsg ) ;
 		}
-	}
-	else
-	{
-		oEditor.FCKUndo.SaveUndoStep() ;
-		GlobalRange.DeleteContents() ;
-		GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;
-		GlobalRange.Collapse( false ) ;
 	}
 }
@@ -442,13 +547,19 @@
 	while ( _Find() )
 	{
-		dialog.Selection.EnsureSelection() ;
-		GlobalRange.DeleteContents() ;
-		GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;
-		GlobalRange.Collapse( false ) ;
+		var range = FindRange.GetHighlightDomRange() ;
+		var bookmark = range.CreateBookmark() ;
+		FindRange.RemoveHighlight() ;
+		range.MoveToBookmark( bookmark) ;
+
+		range.DeleteContents() ;
+		range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
+		range._UpdateElementInfo() ;
+
+		FindRange = CharacterRange.CreateFromDomRange( range ) ;
 		replaceCount++ ;
 	}
 	if ( replaceCount == 0 )
 	{
-		ClearHighlight() ;
+		FindRange && FindRange.RemoveHighlight() ;
 		alert( FCKLang.DlgFindNotFoundMsg ) ;
 	}
@@ -456,5 +567,12 @@
 }
 
-window.onunload = function(){ ClearHighlight() ; }
+window.onunload = function()
+{
+	if ( FindRange )
+	{
+		FindRange.RemoveHighlight() ;
+		FindRange.ToDomRange().Select() ;
+	}
+}
 	</script>
 </head>
