1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 2<!-- 3 * FCKeditor - The text editor for Internet - http://www.fckeditor.net 4 * Copyright (C) 2003-2009 Frederico Caldeira Knabben 5 * 6 * == BEGIN LICENSE == 7 * 8 * Licensed under the terms of any of the following licenses at your 9 * choice: 10 * 11 * - GNU General Public License Version 2 or later (the "GPL") 12 * http://www.gnu.org/licenses/gpl.html 13 * 14 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL") 15 * http://www.gnu.org/licenses/lgpl.html 16 * 17 * - Mozilla Public License Version 1.1 or later (the "MPL") 18 * http://www.mozilla.org/MPL/MPL-1.1.html 19 * 20 * == END LICENSE == 21 * 22 * "Find" and "Replace" dialog box window. 23--> 24<html xmlns="http://www.w3.org/1999/xhtml"> 25<head> 26 <title></title> 27 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 28 <meta content="noindex, nofollow" name="robots" /> 29 <script src="common/fck_dialog_common.js" type="text/javascript"></script> 30 <script type="text/javascript"> 31 32var dialog = window.parent ; 33var oEditor = dialog.InnerDialogLoaded() ; 34var dialogArguments = dialog.Args() ; 35 36var FCKLang = oEditor.FCKLang ; 37var FCKDomTools = oEditor.FCKDomTools ; 38var FCKDomRange = oEditor.FCKDomRange ; 39var FCKListsLib = oEditor.FCKListsLib ; 40var FCKTools = oEditor.FCKTools ; 41var EditorDocument = oEditor.FCK.EditorDocument ; 42var HighlightStyle = oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ) ; 43 44dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ; 45dialog.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ; 46var idMap = {} ; 47 48function OnDialogTabChange( tabCode ) 49{ 50 ShowE( 'divFind', ( tabCode == 'Find' ) ) ; 51 ShowE( 'divReplace', ( tabCode == 'Replace' ) ) ; 52 idMap['FindText'] = 'txtFind' + tabCode ; 53 idMap['CheckCase'] = 'chkCase' + tabCode ; 54 idMap['CheckWord'] = 'chkWord' + tabCode ; 55 56 if ( tabCode == 'Replace' ) 57 dialog.SetAutoSize( true ) ; 58} 59 60GetNextNonEmptyTextNode = function( node, stopNode ) 61{ 62 while ( ( node = FCKDomTools.GetNextSourceNode( node, false, 3, stopNode ) ) && node && node.length < 1 ) 63 1 ; 64 return node ; 65} 66 67CharacterCursor = function( arg ) 68{ 69 if ( arg.nodeType && arg.nodeType == 9 ) 70 { 71 this._textNode = GetNextNonEmptyTextNode( arg.body, arg.documentElement ) ; 72 this._offset = 0 ; 73 this._doc = arg ; 74 } 75 else 76 { 77 this._textNode = arguments[0] ; 78 this._offset = arguments[1] ; 79 this._doc = FCKTools.GetElementDocument( arguments[0] ) ; 80 } 81} 82CharacterCursor.prototype = 83{ 84 GetCharacter : function() 85 { 86 return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ; 87 }, 88 89 // Non-normalized. 90 GetTextNode : function() 91 { 92 return this._textNode ; 93 }, 94 95 // Non-normalized. 96 GetIndex : function() 97 { 98 return this._offset ; 99 }, 100 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 } 109 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 } 119 120 this._textNode = curNode ; 121 this._offset = 0 ; 122 return crossed ; 123 }, 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 } 133 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 } 143 144 this._textNode = curNode ; 145 this._offset = curNode && curNode.length - 1 ; 146 return crossed ; 147 }, 148 149 Clone : function() 150 { 151 return new CharacterCursor( this._textNode, this._offset ) ; 152 } 153} ; 154 155CharacterRange = function( initCursor, maxLength ) 156{ 157 this._cursors = initCursor.push ? initCursor : [initCursor] ; 158 this._maxLength = maxLength ; 159 this._highlightRange = null ; 160} 161CharacterRange.prototype = 162{ 163 ToDomRange : function() 164 { 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 FCKDomTools.ScrollIntoView( focusNode, false ) ; 189 this._cursors = charRange._cursors ; 190 }, 191 192 RemoveHighlight : function() 193 { 194 if ( this._highlightRange ) 195 { 196 HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ; 197 var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ; 198 this._cursors = charRange._cursors ; 199 this._highlightRange = null ; 200 } 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 ; 252 } 253} ; 254 255CharacterRange.CreateFromDomRange = function( domRange ) 256{ 257 var w3cRange = domRange._Range ; 258 var startContainer = w3cRange.startContainer ; 259 var endContainer = w3cRange.endContainer ; 260 var startTextNode, startIndex, endTextNode, endIndex ; 261 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 } 279 280 if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 ) 281 { 282 endTextNode = endContainer ; 283 endIndex = w3cRange.endOffset - 1 ; 284 } 285 else 286 { 287 endTextNode = domRange.EndNode ; 288 while ( endTextNode.nodeType != 3 ) 289 endTextNode = endTextNode.lastChild ; 290 endIndex = endTextNode.length - 1 ; 291 } 292 293 var cursors = [] ; 294 var current = new CharacterCursor( startTextNode, startIndex ) ; 295 cursors.push( current ) ; 296 if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() ) 297 { 298 do 299 { 300 current = current.Clone() ; 301 current.MoveNext() ; 302 cursors.push( current ) ; 303 } 304 while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ; 305 } 306 307 return new CharacterRange( cursors, cursors.length ) ; 308} 309 310// Knuth-Morris-Pratt Algorithm for stream input 311KMP_NOMATCH = 0 ; 312KMP_ADVANCED = 1 ; 313KMP_MATCHED = 2 ; 314KmpMatch = function( pattern, ignoreCase ) 315{ 316 var overlap = [ -1 ] ; 317 for ( var i = 0 ; i < pattern.length ; i++ ) 318 { 319 overlap.push( overlap[i] + 1 ) ; 320 while ( overlap[ i + 1 ] > 0 && pattern.charAt( i ) != pattern.charAt( overlap[ i + 1 ] - 1 ) ) 321 overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1 ; 322 } 323 this._Overlap = overlap ; 324 this._State = 0 ; 325 this._IgnoreCase = ( ignoreCase === true ) ; 326 if ( ignoreCase ) 327 this.Pattern = pattern.toLowerCase(); 328 else 329 this.Pattern = pattern ; 330} 331KmpMatch.prototype = { 332 FeedCharacter : function( c ) 333 { 334 if ( this._IgnoreCase ) 335 c = c.toLowerCase(); 336 337 while ( true ) 338 { 339 if ( c == this.Pattern.charAt( this._State ) ) 340 { 341 this._State++ ; 342 if ( this._State == this.Pattern.length ) 343 { 344 // found a match, start over, don't care about partial matches involving the current match 345 this._State = 0; 346 return KMP_MATCHED; 347 } 348 return KMP_ADVANCED ; 349 } 350 else if ( this._State == 0 ) 351 return KMP_NOMATCH; 352 else 353 this._State = this._Overlap[ this._State ]; 354 } 355 356 return null ; 357 }, 358 359 Reset : function() 360 { 361 this._State = 0 ; 362 } 363}; 364 365// Place a range at the start of document. 366function OnLoad() 367{ 368 // First of all, translate the dialog box texts. 369 oEditor.FCKLanguageManager.TranslatePage( document ) ; 370 371 // Show the appropriate tab at startup. 372 if ( dialogArguments.CustomValue == 'Find' ) 373 { 374 dialog.SetSelectedTab( 'Find' ) ; 375 dialog.SetAutoSize( true ) ; 376 } 377 else 378 dialog.SetSelectedTab( 'Replace' ) ; 379 380 SelectField( 'txtFind' + dialogArguments.CustomValue ) ; 381} 382 383function btnStat() 384{ 385 GetE('btnReplace').disabled = 386 GetE('btnReplaceAll').disabled = 387 GetE('btnFind').disabled = 388 ( GetE(idMap["FindText"]).value.length == 0 ) ; 389} 390 391function btnStatDelayed() 392{ 393 setTimeout( btnStat, 1 ) ; 394} 395 396function GetSearchString() 397{ 398 return GetE(idMap['FindText']).value ; 399} 400 401function GetReplaceString() 402{ 403 return GetE("txtReplace").value ; 404} 405 406function GetCheckCase() 407{ 408 return !! ( GetE(idMap['CheckCase']).checked ) ; 409} 410 411function GetMatchWord() 412{ 413 return !! ( GetE(idMap['CheckWord']).checked ) ; 414} 415 416/* Is this character a unicode whitespace or a punctuation mark? 417 * References: 418 * http://unicode.org/Public/UNIDATA/PropList.txt (whitespaces) 419 * http://php.chinaunix.net/manual/tw/ref.regex.php (punctuation marks) 420 */ 421function CheckIsWordSeparator( c ) 422{ 423 if ( !c ) 424 return true; 425 var code = c.charCodeAt( 0 ); 426 if ( code >= 9 && code <= 0xd ) 427 return true; 428 if ( code >= 0x2000 && code <= 0x200a ) 429 return true; 430 switch ( code ) 431 { 432 case 0x20: 433 case 0x85: 434 case 0xa0: 435 case 0x1680: 436 case 0x180e: 437 case 0x2028: 438 case 0x2029: 439 case 0x202f: 440 case 0x205f: 441 case 0x3000: 442 return true; 443 default: 444 } 445 return /[.,"'?!;:]/.test( c ) ; 446} 447 448FindRange = null ; 449function _Find() 450{ 451 var searchString = GetSearchString() ; 452 if ( !FindRange ) 453 FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ; 454 else 455 { 456 FindRange.RemoveHighlight() ; 457 FindRange = FindRange.GetNextRange( searchString.length ) ; 458 } 459 var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ; 460 var matchState = KMP_NOMATCH ; 461 var character = '%' ; 462 463 while ( character != null ) 464 { 465 while ( ( character = FindRange.GetEndCharacter() ) ) 466 { 467 matchState = matcher.FeedCharacter( character ) ; 468 if ( matchState == KMP_MATCHED ) 469 break ; 470 if ( FindRange.MoveNext() ) 471 matcher.Reset() ; 472 } 473 474 if ( matchState == KMP_MATCHED ) 475 { 476 if ( GetMatchWord() ) 477 { 478 var cursors = FindRange.GetCursors() ; 479 var head = cursors[ cursors.length - 1 ].Clone() ; 480 var tail = cursors[0].Clone() ; 481 if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) ) 482 continue ; 483 if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) ) 484 continue ; 485 } 486 487 FindRange.Highlight() ; 488 return true ; 489 } 490 } 491 492 FindRange = null ; 493 return false ; 494} 495 496function Find() 497{ 498 if ( ! _Find() ) 499 alert( FCKLang.DlgFindNotFoundMsg ) ; 500} 501 502function Replace() 503{ 504 var saveUndoStep = function( selectRange ) 505 { 506 var ieRange ; 507 if ( oEditor.FCKBrowserInfo.IsIE ) 508 ieRange = document.selection.createRange() ; 509 510 selectRange.Select() ; 511 oEditor.FCKUndo.SaveUndoStep() ; 512 var cloneRange = selectRange.Clone() ; 513 cloneRange.Collapse( false ) ; 514 cloneRange.Select() ; 515 516 if ( ieRange ) 517 setTimeout( function(){ ieRange.select() ; }, 1 ) ; 518 } 519 520 if ( FindRange && FindRange.GetHighlightDomRange() ) 521 { 522 var range = FindRange.GetHighlightDomRange() ; 523 var bookmark = range.CreateBookmark() ; 524 FindRange.RemoveHighlight() ; 525 range.MoveToBookmark( bookmark ) ; 526 527 saveUndoStep( range ) ; 528 range.DeleteContents() ; 529 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ; 530 range._UpdateElementInfo() ; 531 532 FindRange = CharacterRange.CreateFromDomRange( range ) ; 533 } 534 else 535 { 536 if ( ! _Find() ) 537 { 538 FindRange && FindRange.RemoveHighlight() ; 539 alert( FCKLang.DlgFindNotFoundMsg ) ; 540 } 541 } 542} 543 544function ReplaceAll() 545{ 546 oEditor.FCKUndo.SaveUndoStep() ; 547 var replaceCount = 0 ; 548 549 while ( _Find() ) 550 { 551 var range = FindRange.GetHighlightDomRange() ; 552 var bookmark = range.CreateBookmark() ; 553 FindRange.RemoveHighlight() ; 554 range.MoveToBookmark( bookmark) ; 555 556 range.DeleteContents() ; 557 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ; 558 range._UpdateElementInfo() ; 559 560 FindRange = CharacterRange.CreateFromDomRange( range ) ; 561 replaceCount++ ; 562 } 563 if ( replaceCount == 0 ) 564 { 565 FindRange && FindRange.RemoveHighlight() ; 566 alert( FCKLang.DlgFindNotFoundMsg ) ; 567 } 568 dialog.Cancel() ; 569} 570 571window.onunload = function() 572{ 573 if ( FindRange ) 574 { 575 FindRange.RemoveHighlight() ; 576 FindRange.ToDomRange().Select() ; 577 } 578} 579 </script> 580</head> 581<body onload="OnLoad()" style="overflow: hidden"> 582 <div id="divFind" style="display: none"> 583 <table cellspacing="3" cellpadding="2" width="100%" border="0"> 584 <tr> 585 <td nowrap="nowrap"> 586 <label for="txtFindFind" fcklang="DlgReplaceFindLbl"> 587 Find what:</label> 588 </td> 589 <td width="100%"> 590 <input id="txtFindFind" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1" 591 type="text" /> 592 </td> 593 <td> 594 <input id="btnFind" style="width: 80px" disabled="disabled" onclick="Find();" 595 type="button" value="Find" fcklang="DlgFindFindBtn" /> 596 </td> 597 </tr> 598 <tr> 599 <td valign="bottom" colspan="3"> 600 <input id="chkCaseFind" tabindex="3" type="checkbox" /><label for="chkCaseFind" fcklang="DlgReplaceCaseChk">Match 601 case</label> 602 <br /> 603 <input id="chkWordFind" tabindex="4" type="checkbox" /><label for="chkWordFind" fcklang="DlgReplaceWordChk">Match 604 whole word</label> 605 </td> 606 </tr> 607 </table> 608 </div> 609 <div id="divReplace" style="display:none"> 610 <table cellspacing="3" cellpadding="2" width="100%" border="0"> 611 <tr> 612 <td nowrap="nowrap"> 613 <label for="txtFindReplace" fcklang="DlgReplaceFindLbl"> 614 Find what:</label> 615 </td> 616 <td width="100%"> 617 <input id="txtFindReplace" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1" 618 type="text" /> 619 </td> 620 <td> 621 <input id="btnReplace" style="width: 80px" disabled="disabled" onclick="Replace();" 622 type="button" value="Replace" fcklang="DlgReplaceReplaceBtn" /> 623 </td> 624 </tr> 625 <tr> 626 <td valign="top" nowrap="nowrap"> 627 <label for="txtReplace" fcklang="DlgReplaceReplaceLbl"> 628 Replace with:</label> 629 </td> 630 <td valign="top"> 631 <input id="txtReplace" style="width: 100%" tabindex="2" type="text" /> 632 </td> 633 <td> 634 <input id="btnReplaceAll" style="width: 80px" disabled="disabled" onclick="ReplaceAll()" type="button" 635 value="Replace All" fcklang="DlgReplaceReplAllBtn" /> 636 </td> 637 </tr> 638 <tr> 639 <td valign="bottom" colspan="3"> 640 <input id="chkCaseReplace" tabindex="3" type="checkbox" /><label for="chkCaseReplace" fcklang="DlgReplaceCaseChk">Match 641 case</label> 642 <br /> 643 <input id="chkWordReplace" tabindex="4" type="checkbox" /><label for="chkWordReplace" fcklang="DlgReplaceWordChk">Match 644 whole word</label> 645 </td> 646 </tr> 647 </table> 648 </div> 649</body> 650</html> 651