Ticket #2150: 2150_3.patch
File 2150_3.patch, 22.3 KB (added by , 16 years ago) |
---|
-
_whatsnew.html
37 37 <p> 38 38 New Features and Improvements:</p> 39 39 <ul> 40 <li></li> 40 <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2150">#2150</a>] The searching 41 speed of the Find/Replace dialog has been vastly improved.</li> 41 42 </ul> 42 43 <p> 43 44 Fixed Bugs:</p> … … 55 56 issue with resolving paths on Windows servers with PHP 5.2.4/5.2.5.</li> 56 57 <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2124">#2059</a>] Fixed the error in the 57 58 toolbar name in fckeditor.py.</li> 59 <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2159">#2159</a>] Selection are now 60 correctly restored when undoing changes made by the Replace dialog.</li> 61 <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2160">#2160</a>] "Match whole word" in the 62 Find and Replace dialog will now find words next to punctuation marks as well.</li> 58 63 </ul> 59 64 <h3> 60 65 Version 2.6</h3> -
editor/dialog/fck_replace.html
34 34 var dialogArguments = dialog.Args() ; 35 35 36 36 var FCKLang = oEditor.FCKLang ; 37 var FCKDomTools = oEditor.FCKDomTools ; 38 var FCKDomRange = oEditor.FCKDomRange ; 39 var FCKListsLib = oEditor.FCKListsLib ; 40 var FCKTools = oEditor.FCKTools ; 41 var EditorDocument = oEditor.FCK.EditorDocument ; 42 var HighlightStyle = oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ) ; 37 43 38 44 dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ; 39 45 dialog.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ; … … 51 57 dialog.SetAutoSize( true ) ; 52 58 } 53 59 54 // Place a range at the start of document. 55 // This will be the starting point of our search. 56 var GlobalRange = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 57 58 function ResetGlobalRange() 60 GetNextNonEmptyTextNode = function( node, stopNode ) 59 61 { 60 GlobalRange.SetStart( oEditor.FCK.EditorDocument.body, 1 );61 GlobalRange.SetEnd( oEditor.FCK.EditorDocument.body,1 ) ;62 GlobalRange.Collapse( true );62 var node ; 63 while ( ( node = FCKDomTools.GetNextSourceNode( node, false, 3, stopNode ) ) && node && node.length < 1 ) ; 64 return node ; 63 65 } 64 ResetGlobalRange() ;65 66 66 var HighlightRange = null ; 67 function Highlight() 67 CharacterCursor = function( arg ) 68 68 { 69 if ( HighlightRange ) 70 ClearHighlight() ; 71 var cloneRange = GlobalRange.Clone() ; 72 oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ).ApplyToRange( cloneRange, false, true ) ; 73 HighlightRange = cloneRange ; 74 GlobalRange = HighlightRange.Clone() ; 75 } 76 77 function ClearHighlight() 78 { 79 if ( HighlightRange ) 69 if ( arg.nodeType && arg.nodeType == 9 ) 80 70 { 81 oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ).RemoveFromRange( HighlightRange, false, true ) ; 82 HighlightRange = null ; 71 this._textNode = GetNextNonEmptyTextNode( arg.body, arg.documentElement ) ; 72 this._offset = 0 ; 73 this._doc = arg ; 83 74 } 75 else 76 { 77 this._textNode = arguments[0] ; 78 this._offset = arguments[1] ; 79 this._doc = FCKTools.GetElementDocument( arguments[0] ) ; 80 } 84 81 } 85 86 function OnLoad() 82 CharacterCursor.prototype = 87 83 { 88 // First of all, translate the dialog box texts. 89 oEditor.FCKLanguageManager.TranslatePage( document ) ; 84 GetCharacter : function() 85 { 86 return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ; 87 }, 90 88 91 // Show the appropriate tab at startup.92 if ( dialogArguments.CustomValue == 'Find')89 // Non-normalized. 90 GetTextNode : function() 93 91 { 94 dialog.SetSelectedTab( 'Find' ) ; 95 dialog.SetAutoSize( true ) ; 96 } 97 else 98 dialog.SetSelectedTab( 'Replace' ) ; 92 return this._textNode ; 93 }, 99 94 100 SelectField( 'txtFind' + dialogArguments.CustomValue ) ; 101 } 95 // Non-normalized. 96 GetIndex : function() 97 { 98 return this._offset ; 99 }, 102 100 103 function btnStat() 104 { 105 GetE('btnReplace').disabled = 106 GetE('btnReplaceAll').disabled = 107 GetE('btnFind').disabled = 108 ( GetE(idMap["FindText"]).value.length == 0 ) ; 109 } 101 // Return value means whehther we've crossed a line break or a paragraph boundary. 102 MoveNext : function() 103 { 104 if ( this._offset < this._textNode.length - 1 ) 105 { 106 this._offset++ ; 107 return false ; 108 } 110 109 111 function btnStatDelayed() 112 { 113 setTimeout( btnStat, 1 ) ; 114 } 110 var crossed = false ; 111 var curNode = this._textNode ; 112 while ( ( curNode = FCKDomTools.GetNextSourceNode( curNode ) ) 113 && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) ) 114 { 115 var tag = curNode.nodeName.toLowerCase() ; 116 if ( FCKListsLib.BlockElements[tag] || tag == 'br' ) 117 crossed = true ; 118 } 115 119 116 function GetSearchString() 117 { 118 return GetE(idMap['FindText']).value;119 } 120 this._textNode = curNode ; 121 this._offset = 0 ; 122 return crossed ; 123 }, 120 124 121 function GetReplaceString() 122 { 123 return GetE("txtReplace").value ; 124 } 125 // Return value means whehther we've crossed a line break or a paragraph boundary. 126 MoveBack : function() 127 { 128 if ( this._offset > 0 && this._textNode.length > 0 ) 129 { 130 this._offset = Math.min( this._offset - 1, this._textNode.length - 1 ) ; 131 return false ; 132 } 125 133 126 function GetCheckCase() 127 { 128 return !! ( GetE(idMap['CheckCase']).checked ) ; 129 } 134 var crossed = false ; 135 var curNode = this._textNode ; 136 while ( ( curNode = FCKDomTools.GetPreviousSourceNode( curNode ) ) 137 && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) ) 138 { 139 var tag = curNode.nodeName.toLowerCase() ; 140 if ( FCKListsLib.BlockElements[tag] || tag == 'br' ) 141 crossed = true ; 142 } 130 143 131 function GetMatchWord() 144 this._textNode = curNode ; 145 this._offset = curNode.length - 1 ; 146 return crossed ; 147 }, 148 149 Clone : function() 150 { 151 return new CharacterCursor( this._textNode, this._offset ) ; 152 } 153 } ; 154 155 CharacterRange = function( initCursor, maxLength ) 132 156 { 133 return !! ( GetE(idMap['CheckWord']).checked ) ; 157 this._cursors = initCursor.push ? initCursor : [initCursor] ; 158 this._maxLength = maxLength ; 159 this._highlightRange = null ; 134 160 } 135 136 // Get the data pointed to by a bookmark. 137 function GetData( bookmark ) 161 CharacterRange.prototype = 138 162 { 139 var cursor = oEditor.FCK.EditorDocument.documentElement ; 140 for ( var i = 0 ; i < bookmark.length ; i++ ) 163 ToDomRange : function() 141 164 { 142 var target = bookmark[i] ; 143 var currentIndex = -1 ; 144 if ( cursor.nodeType != 3 ) 165 var firstCursor = this._cursors[0] ; 166 var lastCursor = this._cursors[ this._cursors.length - 1 ] ; 167 var domRange = new FCKDomRange( FCKTools.GetElementWindow( firstCursor.GetTextNode() ) ) ; 168 var w3cRange = domRange._Range = domRange.CreateRange() ; 169 w3cRange.setStart( firstCursor.GetTextNode(), firstCursor.GetIndex() ) ; 170 w3cRange.setEnd( lastCursor.GetTextNode(), lastCursor.GetIndex() + 1 ) ; 171 domRange._UpdateElementInfo() ; 172 return domRange ; 173 }, 174 175 Highlight : function() 176 { 177 if ( this._cursors.length < 1 ) 178 return ; 179 180 var domRange = this.ToDomRange() ; 181 HighlightStyle.ApplyToRange( domRange, false, true ) ; 182 this._highlightRange = domRange ; 183 184 var charRange = CharacterRange.CreateFromDomRange( domRange ) ; 185 var focusNode = domRange.StartNode ; 186 if ( focusNode.nodeType != 1 ) 187 focusNode = focusNode.parentNode ; 188 focusNode.scrollIntoView( false ) ; 189 this._cursors = charRange._cursors ; 190 }, 191 192 RemoveHighlight : function() 193 { 194 if ( this._highlightRange ) 145 195 { 146 for (var j = 0 ; j < cursor.childNodes.length ; j++ ) 147 { 148 var candidate = cursor.childNodes[j] ; 149 if ( candidate.nodeType == 3 && 150 candidate.previousSibling && 151 candidate.previousSibling.nodeType == 3 ) 152 continue ; 153 currentIndex++ ; 154 if ( currentIndex == target ) 155 { 156 cursor = candidate ; 157 break ; 158 } 159 } 160 if ( currentIndex < target ) 161 return null ; 196 HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ; 197 var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ; 198 this._cursors = charRange._cursors ; 199 this._highlightRange = null ; 162 200 } 163 else 164 { 165 if ( i != bookmark.length - 1 ) 166 return null ; 167 while ( target >= cursor.length && cursor.nextSibling && cursor.nextSibling.nodeType == 3 ) 168 { 169 target -= cursor.length ; 170 cursor = cursor.nextSibling ; 171 } 172 cursor = cursor.nodeValue.charAt( target ) ; 173 if ( cursor == "" ) 174 cursor = null ; 175 } 201 }, 202 203 GetHighlightDomRange : function() 204 { 205 return this._highlightRange; 206 }, 207 208 MoveNext : function() 209 { 210 var next = this._cursors[ this._cursors.length - 1 ].Clone() ; 211 var retval = next.MoveNext() ; 212 if ( retval ) 213 this._cursors = [] ; 214 this._cursors.push( next ) ; 215 if ( this._cursors.length > this._maxLength ) 216 this._cursors.shift() ; 217 return retval ; 218 }, 219 220 MoveBack : function() 221 { 222 var prev = this._cursors[0].Clone() ; 223 var retval = prev.MoveBack() ; 224 if ( retval ) 225 this._cursors = [] ; 226 this._cursors.unshift( prev ) ; 227 if ( this._cursors.length > this._maxLength ) 228 this._cursors.pop() ; 229 return retval ; 230 }, 231 232 GetEndCharacter : function() 233 { 234 if ( this._cursors.length < 1 ) 235 return null ; 236 var retval = this._cursors[ this._cursors.length - 1 ].GetCharacter() ; 237 return retval ; 238 }, 239 240 GetNextRange : function( len ) 241 { 242 if ( this._cursors.length == 0 ) 243 return null ; 244 var cur = this._cursors[ this._cursors.length - 1 ].Clone() ; 245 cur.MoveNext() ; 246 return new CharacterRange( cur, len ) ; 247 }, 248 249 GetCursors : function() 250 { 251 return this._cursors ; 176 252 } 177 return cursor ; 178 } 253 } ; 179 254 180 // With this function, we can treat the bookmark as an iterator for DFS. 181 function NextPosition( bookmark ) 255 CharacterRange.CreateFromDomRange = function( domRange ) 182 256 { 183 // See if there's anything further down the tree.184 var next = bookmark.concat( [0] );185 if ( GetData( next ) != null )186 return next;257 var w3cRange = domRange._Range ; 258 var startContainer = w3cRange.startContainer ; 259 var endContainer = w3cRange.endContainer ; 260 var startTextNode, startIndex, endTextNode, endIndex ; 187 261 188 // Nothing down there? See if there's anything next to me. 189 var next = bookmark.slice( 0, bookmark.length - 1 ).concat( [ bookmark[ bookmark.length - 1 ] + 1 ] ) ; 190 if ( GetData( next ) != null ) 191 return next ; 262 if ( startContainer.nodeType == 3 ) 263 { 264 startTextNode = startContainer ; 265 startIndex = w3cRange.startOffset ; 266 } 267 else if ( domRange.StartNode.nodeType == 3 ) 268 { 269 startTextNode = domRange.StartNode ; 270 startIndex = 0 ; 271 } 272 else 273 { 274 startTextNode = GetNextNonEmptyTextNode( domRange.StartNode, domRange.StartNode.parentNode ) ; 275 if ( !startTextNode ) 276 return null ; 277 startIndex = 0 ; 278 } 192 279 193 // Nothing even next to me? See if there's anything next to my ancestors. 194 for ( var i = bookmark.length - 1 ; i > 0 ; i-- ) 280 if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 ) 195 281 { 196 var next = bookmark.slice( 0, i - 1 ).concat( [ bookmark[ i - 1 ] + 1 ] ) ; 197 if ( GetData( next ) != null ) 198 return next ; 282 endTextNode = endContainer ; 283 endIndex = w3cRange.endOffset - 1 ; 199 284 } 285 else 286 { 287 endTextNode = domRange.EndNode ; 288 while ( endTextNode.nodeType != 3 ) 289 endTextNode = endTextNode.lastChild ; 290 endIndex = endTextNode.length - 1 ; 291 } 200 292 201 // There's absolutely nothing left to walk, return null. 202 return null ; 203 } 204 205 // Is this character a unicode whitespace? 206 // Reference: http://unicode.org/Public/UNIDATA/PropList.txt 207 function CheckIsWhitespace( c ) 208 { 209 var code = c.charCodeAt( 0 ); 210 if ( code >= 9 && code <= 0xd ) 211 return true; 212 if ( code >= 0x2000 && code <= 0x200a ) 213 return true; 214 switch ( code ) 293 var cursors = [] ; 294 var current = new CharacterCursor( startTextNode, startIndex ) ; 295 cursors.push( current ) ; 296 do 215 297 { 216 case 0x20: 217 case 0x85: 218 case 0xa0: 219 case 0x1680: 220 case 0x180e: 221 case 0x2028: 222 case 0x2029: 223 case 0x202f: 224 case 0x205f: 225 case 0x3000: 226 return true; 227 default: 228 return false; 298 current = current.Clone() ; 299 current.MoveNext() ; 300 cursors.push( current ) ; 229 301 } 302 while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ; 303 304 return new CharacterRange( cursors, cursors.length ) ; 230 305 } 231 306 232 307 // Knuth-Morris-Pratt Algorithm for stream input 233 308 KMP_NOMATCH = 0 ; 234 309 KMP_ADVANCED = 1 ; 235 310 KMP_MATCHED = 2 ; 236 function KmpMatch( pattern, ignoreCase )311 KmpMatch = function( pattern, ignoreCase ) 237 312 { 238 313 var overlap = [ -1 ] ; 239 314 for ( var i = 0 ; i < pattern.length ; i++ ) … … 251 326 this.Pattern = pattern ; 252 327 } 253 328 KmpMatch.prototype = { 254 "FeedCharacter": function( c )329 FeedCharacter : function( c ) 255 330 { 256 331 if ( this._IgnoreCase ) 257 332 c = c.toLowerCase(); … … 277 352 278 353 return null ; 279 354 }, 280 "Reset" : function() 355 356 Reset : function() 281 357 { 282 358 this._State = 0 ; 283 359 } 284 360 }; 285 361 286 function _Find() 362 // Place a range at the start of document. 363 function OnLoad() 287 364 { 288 // Start from the end of the current selection. 289 var matcher = new KmpMatch( GetSearchString(), ! GetCheckCase() ) ; 290 var cursor = GlobalRange.CreateBookmark2().End ; 291 var matchState = KMP_NOMATCH ; 292 var matchBookmark = null ; 293 var matchBookmarkStart = [] ; 365 // First of all, translate the dialog box texts. 366 oEditor.FCKLanguageManager.TranslatePage( document ) ; 294 367 295 // Match finding.296 while ( true)368 // Show the appropriate tab at startup. 369 if ( dialogArguments.CustomValue == 'Find' ) 297 370 { 298 // Perform KMP stream matching. 299 // - Reset KMP matcher if we encountered a block element. 300 var data = GetData( cursor ) ; 301 if ( data ) 302 { 303 if ( data.tagName ) 304 { 305 if ( oEditor.FCKListsLib.BlockElements[ data.tagName.toLowerCase() ] ) 306 { 307 matcher.Reset(); 308 matchBookmarkStart = [] ; 309 } 310 } 311 else if ( data.charAt != undefined ) 312 { 313 matchState = matcher.FeedCharacter(data) ; 371 dialog.SetSelectedTab( 'Find' ) ; 372 dialog.SetAutoSize( true ) ; 373 } 374 else 375 dialog.SetSelectedTab( 'Replace' ) ; 314 376 315 // No possible match of any useful substring in the pattern for the currently scanned character. 316 // So delete any positional information. 317 if ( matchState == KMP_NOMATCH ) 318 matchBookmarkStart = [] ; 319 // We've matched something, but it's not a complete match, so let's just mark down the position for backtracking later. 320 else if ( matchState == KMP_ADVANCED ) 321 { 322 matchBookmarkStart.push( cursor.concat( [] ) ) ; 323 if ( matchBookmarkStart.length > matcher._State ) 324 matchBookmarkStart.shift() ; 325 } 326 // Found a complete match! Mark down the ending position as well. 327 else if ( matchState == KMP_MATCHED ) 328 { 329 // It is possible to get a KMP_MATCHED without KMP_ADVANCED when the match pattern is only 1 character. 330 // So need to check and mark down the starting position as well. 331 if ( matchBookmarkStart.length == 0 ) 332 matchBookmarkStart = [cursor.concat( [] )] ; 377 SelectField( 'txtFind' + dialogArguments.CustomValue ) ; 378 } 333 379 334 matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ; 335 matchBookmark.End[ matchBookmark.End.length - 1 ]++; 380 function btnStat() 381 { 382 GetE('btnReplace').disabled = 383 GetE('btnReplaceAll').disabled = 384 GetE('btnFind').disabled = 385 ( GetE(idMap["FindText"]).value.length == 0 ) ; 386 } 336 387 337 // Wait, do we have to match a whole word? 338 // If yes, carry out additional checks on what we've got. 339 if ( GetMatchWord() ) 340 { 341 var startOk = false ; 342 var endOk = false ; 343 var start = matchBookmark.Start ; 344 var end = matchBookmark.End ; 345 if ( start[ start.length - 1 ] == 0 ) 346 startOk = true ; 347 else 348 { 349 var cursorBeforeStart = start.slice( 0, start.length - 1 ) ; 350 cursorBeforeStart.push( start[ start.length - 1 ] - 1 ) ; 351 var dataBeforeStart = GetData( cursorBeforeStart ) ; 352 if ( dataBeforeStart == null || dataBeforeStart.charAt == undefined ) 353 startOk = true ; 354 else if ( CheckIsWhitespace( dataBeforeStart ) ) 355 startOk = true ; 356 } 388 function btnStatDelayed() 389 { 390 setTimeout( btnStat, 1 ) ; 391 } 357 392 358 // this is already one character beyond the last char, no need to move 359 var cursorAfterEnd = end ; 360 var dataAfterEnd = GetData( cursorAfterEnd ); 361 if ( dataAfterEnd == null || dataAfterEnd.charAt == undefined ) 362 endOk = true ; 363 else if ( CheckIsWhitespace( dataAfterEnd ) ) 364 endOk = true ; 393 function GetSearchString() 394 { 395 return GetE(idMap['FindText']).value ; 396 } 365 397 366 if ( startOk && endOk ) 367 break ; 368 else 369 matcher.Reset() ; 370 } 371 else 372 break ; 373 } 374 } 375 } 398 function GetReplaceString() 399 { 400 return GetE("txtReplace").value ; 401 } 376 402 377 // Perform DFS across the document, until we've reached the end. 378 cursor = NextPosition( cursor ) ; 379 if ( cursor == null ) 380 break; 403 function GetCheckCase() 404 { 405 return !! ( GetE(idMap['CheckCase']).checked ) ; 406 } 407 408 function GetMatchWord() 409 { 410 return !! ( GetE(idMap['CheckWord']).checked ) ; 411 } 412 413 /* Is this character a unicode whitespace or a punctuation mark? 414 * References: 415 * http://unicode.org/Public/UNIDATA/PropList.txt (whitespaces) 416 * http://php.chinaunix.net/manual/tw/ref.regex.php (punctuation marks) 417 */ 418 function CheckIsWordSeparator( c ) 419 { 420 var code = c.charCodeAt( 0 ); 421 if ( code >= 9 && code <= 0xd ) 422 return true; 423 if ( code >= 0x2000 && code <= 0x200a ) 424 return true; 425 switch ( code ) 426 { 427 case 0x20: 428 case 0x85: 429 case 0xa0: 430 case 0x1680: 431 case 0x180e: 432 case 0x2028: 433 case 0x2029: 434 case 0x202f: 435 case 0x205f: 436 case 0x3000: 437 return true; 438 default: 381 439 } 440 return /[.,"'?!;:]/.test( c ) ; 441 } 382 442 383 // If we've found a match, highlight the match. 384 if ( matchState == KMP_MATCHED ) 443 FindRange = null ; 444 function _Find() 445 { 446 var searchString = GetSearchString() ; 447 if ( !FindRange ) 448 FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ; 449 else 385 450 { 386 GlobalRange.MoveToBookmark2( matchBookmark ) ; 387 Highlight() ; 388 var focus = GlobalRange._Range.endContainer ; 389 while ( focus && focus.nodeType != 1 ) 390 focus = focus.parentNode ; 451 FindRange.RemoveHighlight() ; 452 FindRange = FindRange.GetNextRange( searchString.length ) ; 453 } 454 var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ; 455 var matchState = KMP_NOMATCH ; 456 var character = '%' ; 391 457 392 if ( focus ) 458 while ( character != null ) 459 { 460 while ( ( character = FindRange.GetEndCharacter() ) ) 393 461 { 394 if ( oEditor.FCKBrowserInfo.IsSafari ) 395 oEditor.FCKDomTools.ScrollIntoView( focus, false ) ; 396 else 397 focus.scrollIntoView( false ) ; 462 matchState = matcher.FeedCharacter( character ) ; 463 if ( matchState == KMP_MATCHED ) 464 break ; 465 if ( FindRange.MoveNext() ) 466 matcher.Reset() ; 398 467 } 399 468 400 return true ; 469 if ( matchState == KMP_MATCHED ) 470 { 471 if ( GetMatchWord() ) 472 { 473 var cursors = FindRange.GetCursors() ; 474 var head = cursors[ cursors.length - 1 ].Clone() ; 475 var tail = cursors[0].Clone() ; 476 if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) ) 477 continue ; 478 if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) ) 479 continue ; 480 } 481 482 FindRange.Highlight() ; 483 return true ; 484 } 401 485 } 402 else 403 { 404 ResetGlobalRange() ; 405 return false ; 406 } 486 487 FindRange = null ; 488 return false ; 407 489 } 408 490 409 491 function Find() 410 492 { 411 493 if ( ! _Find() ) 412 {413 ClearHighlight() ;414 494 alert( FCKLang.DlgFindNotFoundMsg ) ; 415 }416 495 } 417 496 418 497 function Replace() 419 498 { 420 if ( GlobalRange.CheckIsCollapsed())499 var saveUndoStep = function( selectRange ) 421 500 { 422 if (! _Find() ) 501 var ieRange ; 502 if ( oEditor.FCKBrowserInfo.IsIE ) 503 ieRange = document.selection.createRange() ; 504 505 selectRange.Select() ; 506 oEditor.FCKUndo.SaveUndoStep() ; 507 var cloneRange = selectRange.Clone() ; 508 cloneRange.Collapse( false ) ; 509 cloneRange.Select() ; 510 511 if ( ieRange ) 512 setTimeout( function(){ ieRange.select() ; }, 1 ) ; 513 } 514 515 if ( FindRange && FindRange.GetHighlightDomRange() ) 516 { 517 var range = FindRange.GetHighlightDomRange() ; 518 var bookmark = range.CreateBookmark() ; 519 FindRange.RemoveHighlight() ; 520 range.MoveToBookmark( bookmark ) ; 521 522 saveUndoStep( range ) ; 523 range.DeleteContents() ; 524 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ; 525 range._UpdateElementInfo() ; 526 527 FindRange = CharacterRange.CreateFromDomRange( range ) ; 528 } 529 else 530 { 531 if ( ! _Find() ) 423 532 { 424 ClearHighlight() ;533 FindRange && FindRange.RemoveHighlight() ; 425 534 alert( FCKLang.DlgFindNotFoundMsg ) ; 426 535 } 427 536 } 428 else429 {430 oEditor.FCKUndo.SaveUndoStep() ;431 GlobalRange.DeleteContents() ;432 GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;433 GlobalRange.Collapse( false ) ;434 }435 537 } 436 538 437 539 function ReplaceAll() … … 441 543 442 544 while ( _Find() ) 443 545 { 444 dialog.Selection.EnsureSelection() ; 445 GlobalRange.DeleteContents() ; 446 GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ; 447 GlobalRange.Collapse( false ) ; 546 var range = FindRange.GetHighlightDomRange() ; 547 var bookmark = range.CreateBookmark() ; 548 FindRange.RemoveHighlight() ; 549 range.MoveToBookmark( bookmark) ; 550 551 range.DeleteContents() ; 552 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ; 553 range._UpdateElementInfo() ; 554 555 FindRange = CharacterRange.CreateFromDomRange( range ) ; 448 556 replaceCount++ ; 449 557 } 450 558 if ( replaceCount == 0 ) 451 559 { 452 ClearHighlight() ;560 FindRange && FindRange.RemoveHighlight() ; 453 561 alert( FCKLang.DlgFindNotFoundMsg ) ; 454 562 } 455 563 dialog.Cancel() ; 456 564 } 457 565 458 window.onunload = function(){ ClearHighlight() ; }566 window.onunload = function(){ if ( FindRange ) FindRange.RemoveHighlight() ; } 459 567 </script> 460 568 </head> 461 569 <body onload="OnLoad()" style="overflow: hidden">