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-2007 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 oEditor = window.parent.InnerDialogLoaded() ; 33var FCKLang = oEditor.FCKLang ; 34 35window.parent.AddTab( 'Find', FCKLang.DlgFindTitle ) ; 36window.parent.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ; 37var idMap = {} ; 38 39function OnDialogTabChange( tabCode ) 40{ 41 ShowE( 'divFind', ( tabCode == 'Find' ) ) ; 42 ShowE( 'divReplace', ( tabCode == 'Replace' ) ) ; 43 idMap['FindText'] = 'txtFind' + tabCode ; 44 idMap['CheckCase'] = 'chkCase' + tabCode ; 45 idMap['CheckWord'] = 'chkWord' + tabCode ; 46} 47 48function OnLoad() 49{ 50 // First of all, translate the dialog box texts. 51 oEditor.FCKLanguageManager.TranslatePage( document ) ; 52 53 // Place the cursor at the start of document. 54 // This will be the starting point of our search. 55 var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 56 range.SetStart( oEditor.FCK.EditorDocument.body, 1 ) ; 57 range.SetEnd( oEditor.FCK.EditorDocument.body, 1 ) ; 58 range.Collapse( true ) ; 59 range.Select() ; 60 61 // Show the appropriate tab at startup. 62 if ( window.parent.name.search( 'Replace' ) == -1 ) 63 window.parent.SetSelectedTab( 'Find' ) ; 64 else 65 window.parent.SetSelectedTab( 'Replace' ) ; 66} 67 68function btnStat(frm) 69{ 70 document.getElementById('btnReplace').disabled = 71 document.getElementById('btnReplaceAll').disabled = 72 document.getElementById('btnFind').disabled = 73 ( document.getElementById(idMap["FindText"]).value.length == 0 ) ; 74} 75 76function GetSelection() 77{ 78 var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 79 range.MoveToSelection() ; 80 return range.CreateBookmark2() ; 81} 82 83function GetSearchString() 84{ 85 return document.getElementById(idMap['FindText']).value ; 86} 87 88function GetReplaceString() 89{ 90 return document.getElementById("txtReplace").value ; 91} 92 93function GetCheckCase() 94{ 95 return !! ( document.getElementById(idMap['CheckCase']).checked ) ; 96} 97 98function GetMatchWord() 99{ 100 return !! ( document.getElementById(idMap['CheckWord']).checked ) ; 101} 102 103// Get the data pointed to by a bookmark. 104function GetData( bookmark ) 105{ 106 var currentNode = oEditor.FCK.EditorDocument.documentElement; 107 for( var i = 0 ; i < bookmark.length ; i++ ) 108 { 109 if ( currentNode.childNodes.length > bookmark[i] ) 110 currentNode = currentNode.childNodes.item( bookmark[i] ) ; 111 else if ( currentNode.nodeType == 3 ) // text node 112 { 113 var c = currentNode.nodeValue.charAt( bookmark[i] ) ; 114 if ( i == bookmark.length - 1 ) 115 return c != "" ? c : null ; 116 else 117 return null ; 118 } 119 else 120 return null; 121 } 122 return currentNode ; 123} 124 125// With this function, we can treat the bookmark as an iterator for DFS. 126function NextPosition( bookmark ) 127{ 128 // See if there's anything further down the tree. 129 var next = bookmark.concat( [0] ) ; 130 131 if ( GetData( next ) != null ) 132 return next ; 133 134 // Nothing down there? See if there's anything next to me. 135 var next = bookmark.slice( 0, bookmark.length - 1 ).concat( [ bookmark[ bookmark.length - 1 ] + 1 ] ) ; 136 if ( GetData( next ) != null ) 137 return next ; 138 139 // Nothing even next to me? See if there's anything next to my ancestors. 140 for ( var i = bookmark.length - 1 ; i > 0 ; i-- ) 141 { 142 var next = bookmark.slice( 0, i - 1 ).concat( [ bookmark[ i - 1 ] + 1 ] ) ; 143 if ( GetData( next ) != null ) 144 return next ; 145 } 146 147 // There's absolutely nothing left to walk, return null. 148 return null ; 149} 150 151// Is this character a unicode whitespace? 152// Reference: http://unicode.org/Public/UNIDATA/PropList.txt 153function CheckIsWhitespace( c ) 154{ 155 var code = c.charCodeAt( 0 ); 156 if ( code >= 9 && code <= 0xd ) 157 return true; 158 if ( code >= 0x2000 && code <= 0x200a ) 159 return true; 160 switch ( code ) 161 { 162 case 0x20: 163 case 0x85: 164 case 0xa0: 165 case 0x1680: 166 case 0x180e: 167 case 0x2028: 168 case 0x2029: 169 case 0x202f: 170 case 0x205f: 171 case 0x3000: 172 return true; 173 default: 174 return false; 175 } 176} 177 178// Knuth-Morris-Pratt Algorithm for stream input 179KMP_NOMATCH = 0 ; 180KMP_ADVANCED = 1 ; 181KMP_MATCHED = 2 ; 182function KmpMatch( pattern, ignoreCase ) 183{ 184 var overlap = [ -1 ] ; 185 for ( var i = 0 ; i < pattern.length ; i++ ) 186 { 187 overlap.push( overlap[i] + 1 ) ; 188 while ( overlap[ i + 1 ] > 0 && pattern.charAt( i ) != pattern.charAt( overlap[ i + 1 ] - 1 ) ) 189 overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1 ; 190 } 191 this._Overlap = overlap ; 192 this._State = 0 ; 193 this._IgnoreCase = ( ignoreCase === true ) ; 194 if ( ignoreCase ) 195 this.Pattern = pattern.toLowerCase(); 196 else 197 this.Pattern = pattern ; 198} 199KmpMatch.prototype = { 200 "FeedCharacter" : function( c ) 201 { 202 if ( this._IgnoreCase ) 203 c = c.toLowerCase(); 204 205 while ( true ) 206 { 207 if ( c == this.Pattern.charAt( this._State ) ) 208 { 209 this._State++ ; 210 if ( this._State == this.Pattern.length ) 211 { 212 // found a match, start over, don't care about partial matches involving the current match 213 this._State = 0; 214 return KMP_MATCHED; 215 } 216 return KMP_ADVANCED; 217 } 218 else if ( this._State == 0 ) 219 return KMP_NOMATCH; 220 else 221 this._State = this._Overlap[ this._State ]; 222 } 223 224 return null ; 225 }, 226 "Reset" : function() 227 { 228 this._State = 0 ; 229 } 230}; 231 232function _Find() 233{ 234 // Start from the end of the current selection. 235 var matcher = new KmpMatch( GetSearchString(), ! GetCheckCase() ) ; 236 var cursor = GetSelection().End ; 237 var matchState = KMP_NOMATCH ; 238 var matchBookmark = null ; 239 240 // Match finding. 241 while ( true ) 242 { 243 // Perform KMP stream matching. 244 // - Reset KMP matcher if we encountered a block element. 245 var data = GetData( cursor ) ; 246 if ( data ) 247 { 248 if ( data.tagName ) 249 { 250 if ( oEditor.FCKListsLib.BlockElements[ data.tagName.toLowerCase() ] ) 251 matcher.Reset(); 252 } 253 else if ( data.charAt != undefined ) 254 { 255 matchState = matcher.FeedCharacter(data) ; 256 257 if ( matchState == KMP_NOMATCH ) 258 matchBookmark = null ; 259 else if ( matchState == KMP_ADVANCED && matchBookmark == null ) 260 matchBookmark = { Start : cursor.concat( [] ) } ; 261 else if ( matchState == KMP_MATCHED ) 262 { 263 if ( matchBookmark == null ) 264 matchBookmark = { Start : cursor.concat( [] ) } ; 265 matchBookmark.End = cursor.concat( [] ) ; 266 matchBookmark.End[ matchBookmark.End.length - 1 ]++; 267 268 // Wait, do we have to match a whole word? 269 if ( GetMatchWord() ) 270 { 271 var startOk = false ; 272 var endOk = false ; 273 var start = matchBookmark.Start ; 274 var end = matchBookmark.End ; 275 if ( start[ start.length - 1 ] == 0 ) 276 startOk = true ; 277 else 278 { 279 var cursorBeforeStart = start.slice( 0, start.length - 1 ) ; 280 cursorBeforeStart.push( start[ start.length - 1 ] - 1 ) ; 281 var dataBeforeStart = GetData( cursorBeforeStart ) ; 282 if ( dataBeforeStart == null || dataBeforeStart.charAt == undefined ) 283 startOk = true ; 284 else if ( CheckIsWhitespace( dataBeforeStart ) ) 285 startOk = true ; 286 } 287 288 // this is already one character beyond the last char, no need to move 289 var cursorAfterEnd = end ; 290 var dataAfterEnd = GetData( cursorAfterEnd ); 291 if ( dataAfterEnd == null || dataAfterEnd.charAt == undefined ) 292 endOk = true ; 293 else if ( CheckIsWhitespace( dataAfterEnd ) ) 294 endOk = true ; 295 296 if ( startOk && endOk ) 297 break ; 298 else 299 matcher.Reset() ; 300 } 301 else 302 break ; 303 } 304 } 305 } 306 307 // Perform DFS across the document, until we've reached the end. 308 cursor = NextPosition( cursor ) ; 309 if ( cursor == null ) 310 break; 311 } 312 313 // If we've found a match, select the match. 314 if ( matchState == KMP_MATCHED ) 315 { 316 var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 317 range.MoveToBookmark2( matchBookmark ) ; 318 range.Select() ; 319 var focus = range._Range.endContainer ; 320 while ( focus && focus.nodeType != 1 ) 321 focus = focus.parentNode ; 322 if ( focus ) 323 focus.scrollIntoView( false ) ; 324 return true; 325 } 326 else 327 return false; 328} 329 330function Find() 331{ 332 var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 333 range.MoveToSelection() ; 334 range.Collapse( false ) ; 335 range.Select() ; 336 337 if ( ! _Find() ) 338 alert( FCKLang.DlgFindNotFoundMsg ) ; 339} 340 341function Replace() 342{ 343 var selection = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 344 selection.MoveToSelection() ; 345 346 if ( selection.CheckIsCollapsed() ) 347 { 348 if (! _Find() ) 349 alert( FCKLang.DlgFindNotFoundMsg ) ; 350 } 351 else 352 { 353 oEditor.FCKUndo.SaveUndoStep() ; 354 selection.DeleteContents() ; 355 selection.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ; 356 selection.Collapse( false ) ; 357 selection.Select() ; 358 } 359} 360 361function ReplaceAll() 362{ 363 oEditor.FCKUndo.SaveUndoStep() ; 364 var range = new oEditor.FCKDomRange( oEditor.FCK.EditorWindow ) ; 365 366 var replaceCount = 0 ; 367 368 while ( _Find() ) 369 { 370 range.MoveToSelection() ; 371 range.DeleteContents() ; 372 range.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ; 373 range.Collapse( false ) ; 374 range.Select() ; 375 replaceCount++ ; 376 } 377 if ( replaceCount == 0 ) 378 alert( FCKLang.DlgFindNotFoundMsg ) ; 379 window.parent.Cancel() ; 380} 381 </script> 382</head> 383<body onload="OnLoad()" style="overflow: hidden"> 384 <div id="divFind" style="display: none"> 385 <table cellspacing="3" cellpadding="2" width="100%" border="0"> 386 <tr> 387 <td nowrap="nowrap"> 388 <label for="txtFindFind" fcklang="DlgReplaceFindLbl"> 389 Find what:</label> 390 </td> 391 <td width="100%"> 392 <input id="txtFindFind" onkeyup="btnStat(this.form)" style="width: 100%" tabindex="1" 393 type="text" /> 394 </td> 395 <td> 396 <input id="btnFind" style="width: 80px" disabled="disabled" onclick="Find();" 397 type="button" value="Find" fcklang="DlgFindFindBtn" /> 398 </td> 399 </tr> 400 <tr> 401 <td valign="bottom" colspan="3"> 402 <input id="chkCaseFind" tabindex="3" type="checkbox" /><label for="chkCaseFind" fcklang="DlgReplaceCaseChk">Match 403 case</label> 404 <br /> 405 <input id="chkWordFind" tabindex="4" type="checkbox" /><label for="chkWordFind" fcklang="DlgReplaceWordChk">Match 406 whole word</label> 407 </td> 408 </tr> 409 </table> 410 </div> 411 <div id="divReplace" style="display:none"> 412 <table cellspacing="3" cellpadding="2" width="100%" border="0"> 413 <tr> 414 <td nowrap="nowrap"> 415 <label for="txtFindReplace" fcklang="DlgReplaceFindLbl"> 416 Find what:</label> 417 </td> 418 <td width="100%"> 419 <input id="txtFindReplace" onkeyup="btnStat(this.form)" style="width: 100%" tabindex="1" 420 type="text" /> 421 </td> 422 <td> 423 <input id="btnReplace" style="width: 80px" disabled="disabled" onclick="Replace();" 424 type="button" value="Replace" fcklang="DlgReplaceReplaceBtn" /> 425 </td> 426 </tr> 427 <tr> 428 <td valign="top" nowrap="nowrap"> 429 <label for="txtReplace" fcklang="DlgReplaceReplaceLbl"> 430 Replace with:</label> 431 </td> 432 <td valign="top"> 433 <input id="txtReplace" style="width: 100%" tabindex="2" type="text" /> 434 </td> 435 <td> 436 <input id="btnReplaceAll" style="width: 80px" disabled="disabled" onclick="ReplaceAll()" type="button" 437 value="Replace All" fcklang="DlgReplaceReplAllBtn" /> 438 </td> 439 </tr> 440 <tr> 441 <td valign="bottom" colspan="3"> 442 <input id="chkCaseReplace" tabindex="3" type="checkbox" /><label for="chkCaseReplace" fcklang="DlgReplaceCaseChk">Match 443 case</label> 444 <br /> 445 <input id="chkWordReplace" tabindex="4" type="checkbox" /><label for="chkWordReplace" fcklang="DlgReplaceWordChk">Match 446 whole word</label> 447 </td> 448 </tr> 449 </table> 450 </div> 451</body> 452</html> 453