Ticket #2150: 2150_2.patch

File 2150_2.patch, 22.3 KB (added by Martin Kou, 16 years ago)
  • _whatsnew.html

     
    3737        <p>
    3838                New Features and Improvements:</p>
    3939        <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>
    4142        </ul>
    4243        <p>
    4344                Fixed Bugs:</p>
     
    5556                        issue with resolving paths on Windows servers with PHP 5.2.4/5.2.5.</li>
    5657                <li>[<a target="_blank" href="http://dev.fckeditor.net/ticket/2124">#2059</a>] Fixed the error in the
    5758                        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>
    5863        </ul>
    5964        <h3>
    6065                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.max( 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.tagName.toLowerCase() ;
     140                        if ( FCKListsLib.BlockElements[tag] || tag == 'br' )
     141                                crossed = true ;
     142                }
    130143
    131 function GetMatchWord()
     144                this._textNode = curNode ;
     145                this._offset = 0 ;
     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        do
    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                current = current.Clone() ;
     299                current.MoveNext() ;
     300                cursors.push( current ) ;
    229301        }
     302        while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ;
     303
     304        return new CharacterRange( cursors, cursors.length ) ;
    230305}
    231306
    232307// Knuth-Morris-Pratt Algorithm for stream input
    233308KMP_NOMATCH = 0 ;
    234309KMP_ADVANCED = 1 ;
    235310KMP_MATCHED = 2 ;
    236 function KmpMatch( pattern, ignoreCase )
     311KmpMatch = function( pattern, ignoreCase )
    237312{
    238313        var overlap = [ -1 ] ;
    239314        for ( var i = 0 ; i < pattern.length ; i++ )
     
    251326                this.Pattern = pattern ;
    252327}
    253328KmpMatch.prototype = {
    254         "FeedCharacter" : function( c )
     329        FeedCharacter : function( c )
    255330        {
    256331                if ( this._IgnoreCase )
    257332                        c = c.toLowerCase();
     
    277352
    278353                return null ;
    279354        },
    280         "Reset" : function()
     355
     356        Reset : function()
    281357        {
    282358                this._State = 0 ;
    283359        }
    284360};
    285361
    286 function _Find()
     362// Place a range at the start of document.
     363function OnLoad()
    287364{
    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 ) ;
    294367
    295         // Match finding.
    296         while ( true )
     368        // Show the appropriate tab at startup.
     369        if ( dialogArguments.CustomValue == 'Find' )
    297370        {
    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' ) ;
    314376
    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}
    333379
    334                                         matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ;
    335                                         matchBookmark.End[ matchBookmark.End.length - 1 ]++;
     380function btnStat()
     381{
     382        GetE('btnReplace').disabled =
     383                GetE('btnReplaceAll').disabled =
     384                        GetE('btnFind').disabled =
     385                                ( GetE(idMap["FindText"]).value.length == 0 ) ;
     386}
    336387
    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                                                 }
     388function btnStatDelayed()
     389{
     390        setTimeout( btnStat, 1 ) ;
     391}
    357392
    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 ;
     393function GetSearchString()
     394{
     395        return GetE(idMap['FindText']).value ;
     396}
    365397
    366                                                 if ( startOk && endOk )
    367                                                         break ;
    368                                                 else
    369                                                         matcher.Reset() ;
    370                                         }
    371                                         else
    372                                                 break ;
    373                                 }
    374                         }
    375                 }
     398function GetReplaceString()
     399{
     400        return GetE("txtReplace").value ;
     401}
    376402
    377                 // Perform DFS across the document, until we've reached the end.
    378                 cursor = NextPosition( cursor ) ;
    379                 if ( cursor == null )
    380                         break;
     403function GetCheckCase()
     404{
     405        return !! ( GetE(idMap['CheckCase']).checked ) ;
     406}
     407
     408function 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 */
     418function 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:
    381439        }
     440        return /[.,"'?!;:]/.test( c ) ;
     441}
    382442
    383         // If we've found a match, highlight the match.
    384         if ( matchState == KMP_MATCHED )
     443FindRange = null ;
     444function _Find()
     445{
     446        var searchString = GetSearchString() ;
     447        if ( !FindRange )
     448                FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ;
     449        else
    385450        {
    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 = '%' ;
    391457
    392                 if ( focus )
     458        while ( character != null )
     459        {
     460                while ( ( character = FindRange.GetEndCharacter() ) )
    393461                {
    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() ;
    398467                }
    399468
    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                }
    401485        }
    402         else
    403         {
    404                 ResetGlobalRange() ;
    405                 return false ;
    406         }
     486
     487        FindRange = null ;
     488        return false ;
    407489}
    408490
    409491function Find()
    410492{
    411493        if ( ! _Find() )
    412         {
    413                 ClearHighlight() ;
    414494                alert( FCKLang.DlgFindNotFoundMsg ) ;
    415         }
    416495}
    417496
    418497function Replace()
    419498{
    420         if ( GlobalRange.CheckIsCollapsed() )
     499        var saveUndoStep = function( selectRange )
    421500        {
    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() )
    423532                {
    424                         ClearHighlight() ;
     533                        FindRange && FindRange.RemoveHighlight() ;
    425534                        alert( FCKLang.DlgFindNotFoundMsg ) ;
    426535                }
    427536        }
    428         else
    429         {
    430                 oEditor.FCKUndo.SaveUndoStep() ;
    431                 GlobalRange.DeleteContents() ;
    432                 GlobalRange.InsertNode( oEditor.FCK.EditorDocument.createTextNode( GetReplaceString() ) ) ;
    433                 GlobalRange.Collapse( false ) ;
    434         }
    435537}
    436538
    437539function ReplaceAll()
     
    441543
    442544        while ( _Find() )
    443545        {
    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 ) ;
    448556                replaceCount++ ;
    449557        }
    450558        if ( replaceCount == 0 )
    451559        {
    452                 ClearHighlight() ;
     560                FindRange && FindRange.RemoveHighlight() ;
    453561                alert( FCKLang.DlgFindNotFoundMsg ) ;
    454562        }
    455563        dialog.Cancel() ;
    456564}
    457565
    458 window.onunload = function(){ ClearHighlight() ; }
     566window.onunload = function(){ if ( FindRange ) FindRange.RemoveHighlight() ; }
    459567        </script>
    460568</head>
    461569<body onload="OnLoad()" style="overflow: hidden">
© 2003 – 2022, CKSource sp. z o.o. sp.k. All rights reserved. | Terms of use | Privacy policy