Ticket #2150: 2150_6.patch

File 2150_6.patch, 22.6 KB (added by Martin Kou, 12 years ago)
  • _whatsnew.html

     
    3939        <ul>
    4040                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2142">#2142</a>] HTML samples will
    4141                now use sampleposteddata.php in action parameter inside a form.</li>
     42                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2150">#2150</a>] The searching
     43                        speed of the Find/Replace dialog has been vastly improved.</li>
    4244        </ul>
    4345        <p>
    4446                Fixed Bugs:</p>
     
    7577                        catch focus in IE on load when StartupFocus is false and the initial content is empty.</li>
    7678                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2126">#2126</a>] Opening and closing floating
    7779                        dialogs will no longer cause toolbar button states to become frozen.</li>
     80                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2159">#2159</a>] Selection are now
     81                        correctly restored when undoing changes made by the Replace dialog.</li>
     82                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2160">#2160</a>] "Match whole word" in the
     83                        Find and Replace dialog will now find words next to punctuation marks as well.</li>
    7884        </ul>
    7985        <h3>
    8086                Version 2.6</h3>
  • editor/dialog/fck_replace.html

     
    3434var dialogArguments = dialog.Args() ;
    3535
    3636var 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' )  ;
    3743
    3844dialog.AddTab( 'Find', FCKLang.DlgFindTitle ) ;
    3945dialog.AddTab( 'Replace', FCKLang.DlgReplaceTitle ) ;
     
    5157                dialog.SetAutoSize( true ) ;
    5258}
    5359
    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()
     60GetNextNonEmptyTextNode = function( node, stopNode )
    5961{
    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 ;
    6365}
    64 ResetGlobalRange() ;
    6566
    66 var HighlightRange = null ;
    67 function Highlight()
     67CharacterCursor = function( arg )
    6868{
    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 )
    8070        {
    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 ;
    8374        }
     75        else
     76        {
     77                this._textNode = arguments[0] ;
     78                this._offset = arguments[1] ;
     79                this._doc = FCKTools.GetElementDocument( arguments[0] ) ;
     80        }
    8481}
    85 
    86 function OnLoad()
     82CharacterCursor.prototype =
    8783{
    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        },
    9088
    91         // Show the appropriate tab at startup.
    92         if ( dialogArguments.CustomValue == 'Find' )
     89        // Non-normalized.
     90        GetTextNode : function()
    9391        {
    94                 dialog.SetSelectedTab( 'Find' ) ;
    95                 dialog.SetAutoSize( true ) ;
    96         }
    97         else
    98                 dialog.SetSelectedTab( 'Replace' ) ;
     92                return this._textNode ;
     93        },
    9994
    100         SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
    101 }
     95        // Non-normalized.
     96        GetIndex : function()
     97        {
     98                return this._offset ;
     99        },
    102100
    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                }
    110109
    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                }
    115119
    116 function GetSearchString()
    117 {
    118         return GetE(idMap['FindText']).value ;
    119 }
     120                this._textNode = curNode ;
     121                this._offset = 0 ;
     122                return crossed ;
     123        },
    120124
    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                }
    125133
    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                }
    130143
    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
     155CharacterRange = function( initCursor, maxLength )
    132156{
    133         return !! ( GetE(idMap['CheckWord']).checked ) ;
     157        this._cursors = initCursor.push ? initCursor : [initCursor] ;
     158        this._maxLength = maxLength ;
     159        this._highlightRange = null ;
    134160}
    135 
    136 // Get the data pointed to by a bookmark.
    137 function GetData( bookmark )
     161CharacterRange.prototype =
    138162{
    139         var cursor = oEditor.FCK.EditorDocument.documentElement ;
    140         for ( var i = 0 ; i < bookmark.length ; i++ )
     163        ToDomRange : function()
    141164        {
    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 )
    145195                {
    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 ;
    162200                }
    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 ;
    176252        }
    177         return cursor ;
    178 }
     253} ;
    179254
    180 // With this function, we can treat the bookmark as an iterator for DFS.
    181 function NextPosition( bookmark )
     255CharacterRange.CreateFromDomRange = function( domRange )
    182256{
    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 ;
    187261
    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        }
    192279
    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 )
    195281        {
    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 ;
    199284        }
     285        else
     286        {
     287                endTextNode = domRange.EndNode ;
     288                while ( endTextNode.nodeType != 3 )
     289                        endTextNode = endTextNode.lastChild ;
     290                endIndex = endTextNode.length - 1 ;
     291        }
    200292
    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        if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() )
    215297        {
    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                do
     299                {
     300                        current = current.Clone() ;
     301                        current.MoveNext() ;
     302                        cursors.push( current ) ;
     303                }
     304                while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ;
    229305        }
     306
     307        return new CharacterRange( cursors, cursors.length ) ;
    230308}
    231309
    232310// Knuth-Morris-Pratt Algorithm for stream input
    233311KMP_NOMATCH = 0 ;
    234312KMP_ADVANCED = 1 ;
    235313KMP_MATCHED = 2 ;
    236 function KmpMatch( pattern, ignoreCase )
     314KmpMatch = function( pattern, ignoreCase )
    237315{
    238316        var overlap = [ -1 ] ;
    239317        for ( var i = 0 ; i < pattern.length ; i++ )
     
    251329                this.Pattern = pattern ;
    252330}
    253331KmpMatch.prototype = {
    254         "FeedCharacter" : function( c )
     332        FeedCharacter : function( c )
    255333        {
    256334                if ( this._IgnoreCase )
    257335                        c = c.toLowerCase();
     
    277355
    278356                return null ;
    279357        },
    280         "Reset" : function()
     358
     359        Reset : function()
    281360        {
    282361                this._State = 0 ;
    283362        }
    284363};
    285364
    286 function _Find()
     365// Place a range at the start of document.
     366function OnLoad()
    287367{
    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 = [] ;
     368        // First of all, translate the dialog box texts.
     369        oEditor.FCKLanguageManager.TranslatePage( document ) ;
    294370
    295         // Match finding.
    296         while ( true )
     371        // Show the appropriate tab at startup.
     372        if ( dialogArguments.CustomValue == 'Find' )
    297373        {
    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) ;
     374                dialog.SetSelectedTab( 'Find' ) ;
     375                dialog.SetAutoSize( true ) ;
     376        }
     377        else
     378                dialog.SetSelectedTab( 'Replace' ) ;
    314379
    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( [] )] ;
     380        SelectField( 'txtFind' + dialogArguments.CustomValue ) ;
     381}
    333382
    334                                         matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ;
    335                                         matchBookmark.End[ matchBookmark.End.length - 1 ]++;
     383function btnStat()
     384{
     385        GetE('btnReplace').disabled =
     386                GetE('btnReplaceAll').disabled =
     387                        GetE('btnFind').disabled =
     388                                ( GetE(idMap["FindText"]).value.length == 0 ) ;
     389}
    336390
    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                                                 }
     391function btnStatDelayed()
     392{
     393        setTimeout( btnStat, 1 ) ;
     394}
    357395
    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 ;
     396function GetSearchString()
     397{
     398        return GetE(idMap['FindText']).value ;
     399}
    365400
    366                                                 if ( startOk && endOk )
    367                                                         break ;
    368                                                 else
    369                                                         matcher.Reset() ;
    370                                         }
    371                                         else
    372                                                 break ;
    373                                 }
    374                         }
    375                 }
     401function GetReplaceString()
     402{
     403        return GetE("txtReplace").value ;
     404}
    376405
    377                 // Perform DFS across the document, until we've reached the end.
    378                 cursor = NextPosition( cursor ) ;
    379                 if ( cursor == null )
    380                         break;
     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        var code = c.charCodeAt( 0 );
     424        if ( code >= 9 && code <= 0xd )
     425                return true;
     426        if ( code >= 0x2000 && code <= 0x200a )
     427                return true;
     428        switch ( code )
     429        {
     430                case 0x20:
     431                case 0x85:
     432                case 0xa0:
     433                case 0x1680:
     434                case 0x180e:
     435                case 0x2028:
     436                case 0x2029:
     437                case 0x202f:
     438                case 0x205f:
     439                case 0x3000:
     440                        return true;
     441                default:
    381442        }
     443        return /[.,"'?!;:]/.test( c ) ;
     444}
    382445
    383         // If we've found a match, highlight the match.
    384         if ( matchState == KMP_MATCHED )
     446FindRange = null ;
     447function _Find()
     448{
     449        var searchString = GetSearchString() ;
     450        if ( !FindRange )
     451                FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ;
     452        else
    385453        {
    386                 GlobalRange.MoveToBookmark2( matchBookmark ) ;
    387                 Highlight() ;
    388                 var focus = GlobalRange._Range.endContainer ;
    389                 while ( focus && focus.nodeType != 1 )
    390                         focus = focus.parentNode ;
     454                FindRange.RemoveHighlight() ;
     455                FindRange = FindRange.GetNextRange( searchString.length ) ;
     456        }
     457        var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ;
     458        var matchState = KMP_NOMATCH ;
     459        var character = '%' ;
    391460
    392                 if ( focus )
     461        while ( character != null )
     462        {
     463                while ( ( character = FindRange.GetEndCharacter() ) )
    393464                {
    394                         if ( oEditor.FCKBrowserInfo.IsSafari )
    395                                 oEditor.FCKDomTools.ScrollIntoView( focus, false ) ;
    396                         else
    397                                 focus.scrollIntoView( false ) ;
     465                        matchState = matcher.FeedCharacter( character ) ;
     466                        if ( matchState == KMP_MATCHED )
     467                                break ;
     468                        if ( FindRange.MoveNext() )
     469                                matcher.Reset() ;
    398470                }
    399471
    400                 return true ;
     472                if ( matchState == KMP_MATCHED )
     473                {
     474                        if ( GetMatchWord() )
     475                        {
     476                                var cursors = FindRange.GetCursors() ;
     477                                var head = cursors[ cursors.length - 1 ].Clone() ;
     478                                var tail = cursors[0].Clone() ;
     479                                if ( !head.MoveNext() && !CheckIsWordSeparator( head.GetCharacter() ) )
     480                                        continue ;
     481                                if ( !tail.MoveBack() && !CheckIsWordSeparator( tail.GetCharacter() ) )
     482                                        continue ;
     483                        }
     484
     485                        FindRange.Highlight() ;
     486                        return true ;
     487                }
    401488        }
    402         else
    403         {
    404                 ResetGlobalRange() ;
    405                 return false ;
    406         }
     489
     490        FindRange = null ;
     491        return false ;
    407492}
    408493
    409494function Find()
    410495{
    411496        if ( ! _Find() )
    412         {
    413                 ClearHighlight() ;
    414497                alert( FCKLang.DlgFindNotFoundMsg ) ;
    415         }
    416498}
    417499
    418500function Replace()
    419501{
    420         if ( GlobalRange.CheckIsCollapsed() )
     502        var saveUndoStep = function( selectRange )
    421503        {
    422                 if (! _Find() )
     504                var ieRange ;
     505                if ( oEditor.FCKBrowserInfo.IsIE )
     506                        ieRange = document.selection.createRange() ;
     507
     508                selectRange.Select() ;
     509                oEditor.FCKUndo.SaveUndoStep() ;
     510                var cloneRange = selectRange.Clone() ;
     511                cloneRange.Collapse( false ) ;
     512                cloneRange.Select() ;
     513
     514                if ( ieRange )
     515                        setTimeout( function(){ ieRange.select() ; }, 1 ) ;
     516        }
     517
     518        if ( FindRange && FindRange.GetHighlightDomRange() )
     519        {
     520                var range = FindRange.GetHighlightDomRange() ;
     521                var bookmark = range.CreateBookmark() ;
     522                FindRange.RemoveHighlight() ;
     523                range.MoveToBookmark( bookmark ) ;
     524
     525                saveUndoStep( range ) ;
     526                range.DeleteContents() ;
     527                range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
     528                range._UpdateElementInfo() ;
     529
     530                FindRange = CharacterRange.CreateFromDomRange( range ) ;
     531        }
     532        else
     533        {
     534                if ( ! _Find() )
    423535                {
    424                         ClearHighlight() ;
     536                        FindRange && FindRange.RemoveHighlight() ;
    425537                        alert( FCKLang.DlgFindNotFoundMsg ) ;
    426538                }
    427539        }
    428         else
    429         {
    430                 oEditor.FCKUndo.SaveUndoStep() ;
    431                 GlobalRange.DeleteContents() ;
    432                 GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;
    433                 GlobalRange.Collapse( false ) ;
    434         }
    435540}
    436541
    437542function ReplaceAll()
     
    441546
    442547        while ( _Find() )
    443548        {
    444                 dialog.Selection.EnsureSelection() ;
    445                 GlobalRange.DeleteContents() ;
    446                 GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;
    447                 GlobalRange.Collapse( false ) ;
     549                var range = FindRange.GetHighlightDomRange() ;
     550                var bookmark = range.CreateBookmark() ;
     551                FindRange.RemoveHighlight() ;
     552                range.MoveToBookmark( bookmark) ;
     553
     554                range.DeleteContents() ;
     555                range.InsertNode( EditorDocument.createTextNode( GetReplaceString() ) ) ;
     556                range._UpdateElementInfo() ;
     557
     558                FindRange = CharacterRange.CreateFromDomRange( range ) ;
    448559                replaceCount++ ;
    449560        }
    450561        if ( replaceCount == 0 )
    451562        {
    452                 ClearHighlight() ;
     563                FindRange && FindRange.RemoveHighlight() ;
    453564                alert( FCKLang.DlgFindNotFoundMsg ) ;
    454565        }
    455566        dialog.Cancel() ;
    456567}
    457568
    458 window.onunload = function(){ ClearHighlight() ; }
     569window.onunload = function()
     570{
     571        if ( FindRange )
     572        {
     573                FindRange.RemoveHighlight() ;
     574                FindRange.ToDomRange().Select() ;
     575        }
     576}
    459577        </script>
    460578</head>
    461579<body onload="OnLoad()" style="overflow: hidden">
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy