1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
\r
3 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
\r
4 * Copyright (C) 2003-2010 Frederico Caldeira Knabben
\r
6 * == BEGIN LICENSE ==
\r
8 * Licensed under the terms of any of the following licenses at your
\r
11 * - GNU General Public License Version 2 or later (the "GPL")
\r
12 * http://www.gnu.org/licenses/gpl.html
\r
14 * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
\r
15 * http://www.gnu.org/licenses/lgpl.html
\r
17 * - Mozilla Public License Version 1.1 or later (the "MPL")
\r
18 * http://www.mozilla.org/MPL/MPL-1.1.html
\r
22 * "Find" and "Replace" dialog box window.
\r
24 <html xmlns="http://www.w3.org/1999/xhtml">
\r
27 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
\r
28 <meta content="noindex, nofollow" name="robots" />
\r
29 <script src="common/fck_dialog_common.js" type="text/javascript"></script>
\r
30 <script type="text/javascript">
\r
32 var dialog = window.parent ;
\r
33 var oEditor = dialog.InnerDialogLoaded() ;
\r
34 var dialogArguments = dialog.Args() ;
\r
36 var FCKLang = oEditor.FCKLang ;
\r
37 var FCKDomTools = oEditor.FCKDomTools ;
\r
38 var FCKDomRange = oEditor.FCKDomRange ;
\r
39 var FCKListsLib = oEditor.FCKListsLib ;
\r
40 var FCKTools = oEditor.FCKTools ;
\r
41 var EditorDocument = oEditor.FCK.EditorDocument ;
\r
42 var HighlightStyle = oEditor.FCKStyles.GetStyle( '_FCK_SelectionHighlight' ) ;
\r
44 dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ;
\r
45 dialog.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ;
\r
48 function OnDialogTabChange( tabCode )
\r
50 ShowE( 'divFind', ( tabCode == 'Find' ) ) ;
\r
51 ShowE( 'divReplace', ( tabCode == 'Replace' ) ) ;
\r
52 idMap['FindText'] = 'txtFind' + tabCode ;
\r
53 idMap['CheckCase'] = 'chkCase' + tabCode ;
\r
54 idMap['CheckWord'] = 'chkWord' + tabCode ;
\r
56 if ( tabCode == 'Replace' )
\r
57 dialog.SetAutoSize( true ) ;
\r
60 GetNextNonEmptyTextNode = function( node, stopNode )
\r
62 while ( ( node = FCKDomTools.GetNextSourceNode( node, false, 3, stopNode ) ) && node && node.length < 1 )
\r
67 CharacterCursor = function( arg )
\r
69 if ( arg.nodeType && arg.nodeType == 9 )
\r
71 this._textNode = GetNextNonEmptyTextNode( arg.body, arg.documentElement ) ;
\r
77 this._textNode = arguments[0] ;
\r
78 this._offset = arguments[1] ;
\r
79 this._doc = FCKTools.GetElementDocument( arguments[0] ) ;
\r
82 CharacterCursor.prototype =
\r
84 GetCharacter : function()
\r
86 return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ;
\r
90 GetTextNode : function()
\r
92 return this._textNode ;
\r
96 GetIndex : function()
\r
98 return this._offset ;
\r
101 // Return value means whehther we've crossed a line break or a paragraph boundary.
\r
102 MoveNext : function()
\r
104 if ( this._offset < this._textNode.length - 1 )
\r
110 var crossed = false ;
\r
111 var curNode = this._textNode ;
\r
112 while ( ( curNode = FCKDomTools.GetNextSourceNode( curNode ) )
\r
113 && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
\r
115 var tag = curNode.nodeName.toLowerCase() ;
\r
116 if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
\r
120 this._textNode = curNode ;
\r
125 // Return value means whehther we've crossed a line break or a paragraph boundary.
\r
126 MoveBack : function()
\r
128 if ( this._offset > 0 && this._textNode.length > 0 )
\r
130 this._offset = Math.min( this._offset - 1, this._textNode.length - 1 ) ;
\r
134 var crossed = false ;
\r
135 var curNode = this._textNode ;
\r
136 while ( ( curNode = FCKDomTools.GetPreviousSourceNode( curNode ) )
\r
137 && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) )
\r
139 var tag = curNode.nodeName.toLowerCase() ;
\r
140 if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
\r
144 this._textNode = curNode ;
\r
145 this._offset = curNode && curNode.length - 1 ;
\r
151 return new CharacterCursor( this._textNode, this._offset ) ;
\r
155 CharacterRange = function( initCursor, maxLength )
\r
157 this._cursors = initCursor.push ? initCursor : [initCursor] ;
\r
158 this._maxLength = maxLength ;
\r
159 this._highlightRange = null ;
\r
161 CharacterRange.prototype =
\r
163 ToDomRange : function()
\r
165 var firstCursor = this._cursors[0] ;
\r
166 var lastCursor = this._cursors[ this._cursors.length - 1 ] ;
\r
167 var domRange = new FCKDomRange( FCKTools.GetElementWindow( firstCursor.GetTextNode() ) ) ;
\r
168 var w3cRange = domRange._Range = domRange.CreateRange() ;
\r
169 w3cRange.setStart( firstCursor.GetTextNode(), firstCursor.GetIndex() ) ;
\r
170 w3cRange.setEnd( lastCursor.GetTextNode(), lastCursor.GetIndex() + 1 ) ;
\r
171 domRange._UpdateElementInfo() ;
\r
175 Highlight : function()
\r
177 if ( this._cursors.length < 1 )
\r
180 var domRange = this.ToDomRange() ;
\r
181 HighlightStyle.ApplyToRange( domRange, false, true ) ;
\r
182 this._highlightRange = domRange ;
\r
184 var charRange = CharacterRange.CreateFromDomRange( domRange ) ;
\r
185 var focusNode = domRange.StartNode ;
\r
186 if ( focusNode.nodeType != 1 )
\r
187 focusNode = focusNode.parentNode ;
\r
188 FCKDomTools.ScrollIntoView( focusNode, false ) ;
\r
189 this._cursors = charRange._cursors ;
\r
192 RemoveHighlight : function()
\r
194 if ( this._highlightRange )
\r
196 HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ;
\r
197 var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ;
\r
198 this._cursors = charRange._cursors ;
\r
199 this._highlightRange = null ;
\r
203 GetHighlightDomRange : function()
\r
205 return this._highlightRange;
\r
208 MoveNext : function()
\r
210 var next = this._cursors[ this._cursors.length - 1 ].Clone() ;
\r
211 var retval = next.MoveNext() ;
\r
213 this._cursors = [] ;
\r
214 this._cursors.push( next ) ;
\r
215 if ( this._cursors.length > this._maxLength )
\r
216 this._cursors.shift() ;
\r
220 MoveBack : function()
\r
222 var prev = this._cursors[0].Clone() ;
\r
223 var retval = prev.MoveBack() ;
\r
225 this._cursors = [] ;
\r
226 this._cursors.unshift( prev ) ;
\r
227 if ( this._cursors.length > this._maxLength )
\r
228 this._cursors.pop() ;
\r
232 GetEndCharacter : function()
\r
234 if ( this._cursors.length < 1 )
\r
236 var retval = this._cursors[ this._cursors.length - 1 ].GetCharacter() ;
\r
240 GetNextRange : function( len )
\r
242 if ( this._cursors.length == 0 )
\r
244 var cur = this._cursors[ this._cursors.length - 1 ].Clone() ;
\r
246 return new CharacterRange( cur, len ) ;
\r
249 GetCursors : function()
\r
251 return this._cursors ;
\r
255 CharacterRange.CreateFromDomRange = function( domRange )
\r
257 var w3cRange = domRange._Range ;
\r
258 var startContainer = w3cRange.startContainer ;
\r
259 var endContainer = w3cRange.endContainer ;
\r
260 var startTextNode, startIndex, endTextNode, endIndex ;
\r
262 if ( startContainer.nodeType == 3 )
\r
264 startTextNode = startContainer ;
\r
265 startIndex = w3cRange.startOffset ;
\r
267 else if ( domRange.StartNode.nodeType == 3 )
\r
269 startTextNode = domRange.StartNode ;
\r
274 startTextNode = GetNextNonEmptyTextNode( domRange.StartNode, domRange.StartNode.parentNode ) ;
\r
275 if ( !startTextNode )
\r
280 if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 )
\r
282 endTextNode = endContainer ;
\r
283 endIndex = w3cRange.endOffset - 1 ;
\r
287 endTextNode = domRange.EndNode ;
\r
288 while ( endTextNode.nodeType != 3 )
\r
289 endTextNode = endTextNode.lastChild ;
\r
290 endIndex = endTextNode.length - 1 ;
\r
294 var current = new CharacterCursor( startTextNode, startIndex ) ;
\r
295 cursors.push( current ) ;
\r
296 if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() )
\r
300 current = current.Clone() ;
\r
301 current.MoveNext() ;
\r
302 cursors.push( current ) ;
\r
304 while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ;
\r
307 return new CharacterRange( cursors, cursors.length ) ;
\r
310 // Knuth-Morris-Pratt Algorithm for stream input
\r
314 KmpMatch = function( pattern, ignoreCase )
\r
316 var overlap = [ -1 ] ;
\r
317 for ( var i = 0 ; i < pattern.length ; i++ )
\r
319 overlap.push( overlap[i] + 1 ) ;
\r
320 while ( overlap[ i + 1 ] > 0 && pattern.charAt( i ) != pattern.charAt( overlap[ i + 1 ] - 1 ) )
\r
321 overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1 ;
\r
323 this._Overlap = overlap ;
\r
325 this._IgnoreCase = ( ignoreCase === true ) ;
\r
327 this.Pattern = pattern.toLowerCase();
\r
329 this.Pattern = pattern ;
\r
331 KmpMatch.prototype = {
\r
332 FeedCharacter : function( c )
\r
334 if ( this._IgnoreCase )
\r
335 c = c.toLowerCase();
\r
339 if ( c == this.Pattern.charAt( this._State ) )
\r
342 if ( this._State == this.Pattern.length )
\r
344 // found a match, start over, don't care about partial matches involving the current match
\r
346 return KMP_MATCHED;
\r
348 return KMP_ADVANCED ;
\r
350 else if ( this._State == 0 )
\r
351 return KMP_NOMATCH;
\r
353 this._State = this._Overlap[ this._State ];
\r
365 // Place a range at the start of document.
\r
368 // First of all, translate the dialog box texts.
\r
369 oEditor.FCKLanguageManager.TranslatePage( document ) ;
\r
371 // Show the appropriate tab at startup.
\r
372 if ( dialogArguments.CustomValue == 'Find' )
\r
374 dialog.SetSelectedTab( 'Find' ) ;
\r
375 dialog.SetAutoSize( true ) ;
\r
378 dialog.SetSelectedTab( 'Replace' ) ;
\r
380 SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
\r
385 GetE('btnReplace').disabled =
\r
386 GetE('btnReplaceAll').disabled =
\r
387 GetE('btnFind').disabled =
\r
388 ( GetE(idMap["FindText"]).value.length == 0 ) ;
\r
391 function btnStatDelayed()
\r
393 setTimeout( btnStat, 1 ) ;
\r
396 function GetSearchString()
\r
398 return GetE(idMap['FindText']).value ;
\r
401 function GetReplaceString()
\r
403 return GetE("txtReplace").value ;
\r
406 function GetCheckCase()
\r
408 return !! ( GetE(idMap['CheckCase']).checked ) ;
\r
411 function GetMatchWord()
\r
413 return !! ( GetE(idMap['CheckWord']).checked ) ;
\r
416 /* Is this character a unicode whitespace or a punctuation mark?
\r
418 * http://unicode.org/Public/UNIDATA/PropList.txt (whitespaces)
\r
419 * http://php.chinaunix.net/manual/tw/ref.regex.php (punctuation marks)
\r
421 function CheckIsWordSeparator( c )
\r
425 var code = c.charCodeAt( 0 );
\r
426 if ( code >= 9 && code <= 0xd )
\r
428 if ( code >= 0x2000 && code <= 0x200a )
\r
445 return /[.,"'?!;:]/.test( c ) ;
\r
451 var searchString = GetSearchString() ;
\r
453 FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ;
\r
456 FindRange.RemoveHighlight() ;
\r
457 FindRange = FindRange.GetNextRange( searchString.length ) ;
\r
459 var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ;
\r
460 var matchState = KMP_NOMATCH ;
\r
461 var character = '%' ;
\r
463 while ( character != null )
\r
465 while ( ( character = FindRange.GetEndCharacter() ) )
\r
467 matchState = matcher.FeedCharacter( character ) ;
\r
468 if ( matchState == KMP_MATCHED )
\r
470 if ( FindRange.MoveNext() )
\r
474 if ( matchState == KMP_MATCHED )
\r
476 if ( GetMatchWord() )
\r
478 var cursors = FindRange.GetCursors() ;
\r
479 var head = cursors[ cursors.length - 1 ].Clone() ;
\r
480 var tail = cursors[0].Clone() ;
\r
481 if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) )
\r
483 if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) )
\r
487 FindRange.Highlight() ;
\r
499 alert( FCKLang.DlgFindNotFoundMsg ) ;
\r
504 var saveUndoStep = function( selectRange )
\r
507 if ( oEditor.FCKBrowserInfo.IsIE )
\r
508 ieRange = document.selection.createRange() ;
\r
510 selectRange.Select() ;
\r
511 oEditor.FCKUndo.SaveUndoStep() ;
\r
512 var cloneRange = selectRange.Clone() ;
\r
513 cloneRange.Collapse( false ) ;
\r
514 cloneRange.Select() ;
\r
517 setTimeout( function(){ ieRange.select() ; }, 1 ) ;
\r
520 if ( FindRange && FindRange.GetHighlightDomRange() )
\r
522 var range = FindRange.GetHighlightDomRange() ;
\r
523 var bookmark = range.CreateBookmark() ;
\r
524 FindRange.RemoveHighlight() ;
\r
525 range.MoveToBookmark( bookmark ) ;
\r
527 saveUndoStep( range ) ;
\r
528 range.DeleteContents() ;
\r
529 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
\r
530 range._UpdateElementInfo() ;
\r
532 FindRange = CharacterRange.CreateFromDomRange( range ) ;
\r
538 FindRange && FindRange.RemoveHighlight() ;
\r
539 alert( FCKLang.DlgFindNotFoundMsg ) ;
\r
544 function ReplaceAll()
\r
546 oEditor.FCKUndo.SaveUndoStep() ;
\r
547 var replaceCount = 0 ;
\r
551 var range = FindRange.GetHighlightDomRange() ;
\r
552 var bookmark = range.CreateBookmark() ;
\r
553 FindRange.RemoveHighlight() ;
\r
554 range.MoveToBookmark( bookmark) ;
\r
556 range.DeleteContents() ;
\r
557 range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
\r
558 range._UpdateElementInfo() ;
\r
560 FindRange = CharacterRange.CreateFromDomRange( range ) ;
\r
563 if ( replaceCount == 0 )
\r
565 FindRange && FindRange.RemoveHighlight() ;
\r
566 alert( FCKLang.DlgFindNotFoundMsg ) ;
\r
571 window.onunload = function()
\r
575 FindRange.RemoveHighlight() ;
\r
576 FindRange.ToDomRange().Select() ;
\r
581 <body onload="OnLoad()" style="overflow: hidden">
\r
582 <div id="divFind" style="display: none">
\r
583 <table cellspacing="3" cellpadding="2" width="100%" border="0">
\r
585 <td nowrap="nowrap">
\r
586 <label for="txtFindFind" fcklang="DlgReplaceFindLbl">
\r
590 <input id="txtFindFind" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1"
\r
594 <input id="btnFind" style="width: 80px" disabled="disabled" onclick="Find();"
\r
595 type="button" value="Find" fcklang="DlgFindFindBtn" />
\r
599 <td valign="bottom" colspan="3">
\r
600 <input id="chkCaseFind" tabindex="3" type="checkbox" /><label for="chkCaseFind" fcklang="DlgReplaceCaseChk">Match
\r
603 <input id="chkWordFind" tabindex="4" type="checkbox" /><label for="chkWordFind" fcklang="DlgReplaceWordChk">Match
\r
609 <div id="divReplace" style="display:none">
\r
610 <table cellspacing="3" cellpadding="2" width="100%" border="0">
\r
612 <td nowrap="nowrap">
\r
613 <label for="txtFindReplace" fcklang="DlgReplaceFindLbl">
\r
617 <input id="txtFindReplace" onkeyup="btnStat()" oninput="btnStat()" onpaste="btnStatDelayed()" style="width: 100%" tabindex="1"
\r
621 <input id="btnReplace" style="width: 80px" disabled="disabled" onclick="Replace();"
\r
622 type="button" value="Replace" fcklang="DlgReplaceReplaceBtn" />
\r
626 <td valign="top" nowrap="nowrap">
\r
627 <label for="txtReplace" fcklang="DlgReplaceReplaceLbl">
\r
628 Replace with:</label>
\r
631 <input id="txtReplace" style="width: 100%" tabindex="2" type="text" />
\r
634 <input id="btnReplaceAll" style="width: 80px" disabled="disabled" onclick="ReplaceAll()" type="button"
\r
635 value="Replace All" fcklang="DlgReplaceReplAllBtn" />
\r
639 <td valign="bottom" colspan="3">
\r
640 <input id="chkCaseReplace" tabindex="3" type="checkbox" /><label for="chkCaseReplace" fcklang="DlgReplaceCaseChk">Match
\r
643 <input id="chkWordReplace" tabindex="4" type="checkbox" /><label for="chkWordReplace" fcklang="DlgReplaceWordChk">Match
\r