Ticket #2150: 2150_pre.patch

File 2150_pre.patch, 20.7 kB (added by martinkou, 2 months ago)
  • 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        // Normalized address ready to be put into a bookmark. 
     102        GetAddress : function() 
     103        { 
     104                if ( !this._textNode ) 
     105                        return null ; 
     106                var nodeAddress = FCKDomTools.GetNodeAddress( this._textNode, true ) ; 
     107                var textAddress = this._offset ; 
     108                var curTextNode = this._textNode ; 
     109                while ( ( curTextNode = curTextNode.previousSibling ) && ( curTextNode.nodeType == 3 ) ) 
     110                        textAddress += curTextNode.length ; 
     111                return nodeAddress.concat( [textAddress] ) ; 
     112        }, 
    110113 
    111 function btnStatDelayed() 
    112 { 
    113         setTimeout( btnStat, 1 ) ; 
    114 } 
     114        // Return value means whehther we've crossed a line break or a paragraph boundary. 
     115        MoveNext : function() 
     116        { 
     117                if ( this._offset < this._textNode.length - 1 ) 
     118                { 
     119                        this._offset++ ; 
     120                        return false ; 
     121                } 
    115122 
    116 function GetSearchString() 
    117 { 
    118         return GetE(idMap['FindText']).value ; 
    119 } 
     123                var crossed = false ; 
     124                var curNode = this._textNode ; 
     125                while ( ( curNode = FCKDomTools.GetNextSourceNode( curNode ) ) 
     126                                && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) ) 
     127                { 
     128                        var tag = curNode.nodeName.toLowerCase() ; 
     129                        if ( FCKListsLib.BlockElements[tag] || tag == 'br' ) 
     130                                crossed = true ; 
     131                } 
    120132 
    121 function GetReplaceString() 
    122 { 
    123         return GetE("txtReplace").value ; 
    124 } 
     133                this._textNode = curNode ; 
     134                this._offset = 0 ; 
     135                return crossed ; 
     136        }, 
    125137 
    126 function GetCheckCase() 
    127 { 
    128         return !! ( GetE(idMap['CheckCase']).checked ) ; 
    129 } 
     138        // Return value means whehther we've crossed a line break or a paragraph boundary. 
     139        MoveBack : function() 
     140        { 
     141                if ( this._offset > 0 && this._textNode.length > 0 ) 
     142                { 
     143                        this._offset = Math.max( this._offset - 1, this._textNode.length - 1 ) ; 
     144                        return false ; 
     145                } 
    130146 
    131 function GetMatchWord() 
     147                var crossed = false ; 
     148                var curNode = this._textNode ; 
     149                while ( ( curNode = FCKDomTools.GetPreviousSourceNode( curNode ) ) 
     150                                && curNode && ( curNode.nodeType != 3 || curNode.length < 1 ) ) 
     151                { 
     152                        var tag = curNode.tagName.toLowerCase() ; 
     153                        if ( FCKListsLib.BlockElements[tag] || tag == 'br' ) 
     154                                crossed = true ; 
     155                } 
     156 
     157                this._textNode = curNode ; 
     158                this._offset = 0 ; 
     159                return crossed ; 
     160        }, 
     161 
     162        Clone : function() 
     163        { 
     164                return new CharacterCursor( this._textNode, this._offset ) ; 
     165        } 
     166} ; 
     167 
     168CharacterRange = function( initCursor, maxLength ) 
    132169{ 
    133         return !! ( GetE(idMap['CheckWord']).checked ) ; 
     170        this._cursors = initCursor.push ? initCursor : [initCursor] ; 
     171        this._maxLength = maxLength ; 
     172        this._highlightRange = null ; 
    134173} 
    135  
    136 // Get the data pointed to by a bookmark. 
    137 function GetData( bookmark ) 
     174CharacterRange.prototype = 
    138175{ 
    139         var cursor = oEditor.FCK.EditorDocument.documentElement ; 
    140         for ( var i = 0 ; i < bookmark.length ; i++ ) 
     176        ToDomRange : function() 
    141177        { 
    142                 var target = bookmark[i] ; 
    143                 var currentIndex = -1 ; 
    144                 if ( cursor.nodeType != 3 ) 
    145                 { 
    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 ; 
    162                 } 
     178                var firstCursor = this._cursors[0] ; 
     179                var lastCursor = this._cursors[ this._cursors.length - 1 ] ; 
     180                var domRange = new FCKDomRange( FCKTools.GetElementWindow( firstCursor.GetTextNode() ) ) ; 
     181                var w3cRange = domRange._Range = domRange.CreateRange() ; 
     182                w3cRange.setStart( firstCursor.GetTextNode(), firstCursor.GetIndex() ) ; 
     183                w3cRange.setEnd( lastCursor.GetTextNode(), lastCursor.GetIndex() + 1 ) ; 
     184                domRange._UpdateElementInfo() ; 
     185                return domRange ; 
     186        }, 
     187 
     188        Highlight : function() 
     189        { 
     190                if ( this._cursors.length < 1 ) 
     191                        return ; 
     192 
     193                var domRange = this.ToDomRange() ; 
     194                HighlightStyle.ApplyToRange( domRange, false, true ) ; 
     195                this._highlightRange = domRange ; 
     196 
     197                var charRange = CharacterRange.CreateFromDomRange( domRange ) ; 
     198                var focusNode = domRange.StartNode ; 
     199                if ( focusNode.nodeType != 1 ) 
     200                        focusNode = focusNode.parentNode ; 
     201                if ( oEditor.FCKBrowserInfo.IsSafari ) 
     202                        FCKDomTools.ScrollIntoView( focusNode ) ; 
    163203                else 
     204                        focusNode.scrollIntoView( false ) ; 
     205                this._cursors = charRange._cursors ; 
     206        }, 
     207 
     208        RemoveHighlight : function() 
     209        { 
     210                if ( this._highlightRange ) 
    164211                { 
    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 ; 
     212                        HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ; 
     213                        var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ; 
     214                        this._cursors = charRange._cursors ; 
     215                        this._highlightRange = null ; 
    175216                } 
     217        }, 
     218 
     219        GetHighlightDomRange : function() 
     220        { 
     221                return this._highlightRange; 
     222        }, 
     223 
     224        MoveNext : function() 
     225        { 
     226                var next = this._cursors[ this._cursors.length - 1 ].Clone() ; 
     227                var retval = next.MoveNext() ; 
     228                if ( retval ) 
     229                        this._cursors = [] ; 
     230                this._cursors.push( next ) ; 
     231                if ( this._cursors.length > this._maxLength ) 
     232                        this._cursors.shift() ; 
     233                return retval ; 
     234        }, 
     235 
     236        MoveBack : function() 
     237        { 
     238                var prev = this._cursors[0].Clone() ; 
     239                var retval = prev.MoveBack() ; 
     240                if ( retval ) 
     241                        this._cursors = [] ; 
     242                this._cursors.unshift( prev ) ; 
     243                if ( this._cursors.length > this._maxLength ) 
     244                        this._cursors.pop() ; 
     245                return retval ; 
     246        }, 
     247 
     248        GetEndCharacter : function() 
     249        { 
     250                if ( this._cursors.length < 1 ) 
     251                        return null ; 
     252                var retval = this._cursors[ this._cursors.length - 1 ].GetCharacter() ; 
     253                return retval ; 
     254        }, 
     255 
     256        GetNextRange : function( len ) 
     257        {                
     258                if ( this._cursors.length == 0 ) 
     259                        return null ; 
     260                var cur = this._cursors[ this._cursors.length - 1 ].Clone() ; 
     261                cur.MoveNext() ; 
     262                return new CharacterRange( cur, len ) ; 
    176263        } 
    177         return cursor ; 
    178 } 
     264} ; 
    179265 
    180 // With this function, we can treat the bookmark as an iterator for DFS. 
    181 function NextPosition( bookmark ) 
     266CharacterRange.CreateFromDomRange = function( domRange ) 
    182267{ 
    183         // See if there's anything further down the tree. 
    184         var next = bookmark.concat( [0] ) ; 
    185         if ( GetData( next ) != null ) 
    186                 return next ; 
     268        var w3cRange = domRange._Range ; 
     269        var startContainer = w3cRange.startContainer ; 
     270        var endContainer = w3cRange.endContainer ; 
     271        var startTextNode, startIndex, endTextNode, endIndex ; 
    187272 
    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 ; 
     273        if ( startContainer.nodeType == 3 ) 
     274        { 
     275                startTextNode = startContainer ; 
     276                startIndex = w3cRange.startOffset ; 
     277        } 
     278        else if ( domRange.StartNode.nodeType == 3 ) 
     279        { 
     280                startTextNode = domRange.StartNode ; 
     281                startIndex = 0 ; 
     282        } 
     283        else 
     284        { 
     285                startTextNode = GetNextNonEmptyTextNode( domRange.StartNode, domRange.StartNode.parentNode ) ; 
     286                if ( !startTextNode ) 
     287                        return null ; 
     288                startIndex = 0 ; 
     289        } 
    192290 
    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-- ) 
     291        if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 ) 
    195292        { 
    196                 var next = bookmark.slice( 0, i - 1 ).concat( [ bookmark[ i - 1 ] + 1 ] ) ; 
    197                 if ( GetData( next ) != null ) 
    198                         return next ; 
     293                endTextNode = endContainer ; 
     294                endIndex = w3cRange.endOffset - 1 ; 
    199295        } 
     296        else 
     297        { 
     298                endTextNode = domRange.EndNode ; 
     299                while ( endTextNode.nodeType != 3 ) 
     300                        endTextNode = endTextNode.lastChild ; 
     301                endIndex = endTextNode.length - 1 ; 
     302        } 
    200303 
    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 ) 
     304        var cursors = [] ; 
     305        var current = new CharacterCursor( startTextNode, startIndex ) ; 
     306        cursors.push( current ) ; 
     307        do 
    215308        { 
    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; 
     309                current = current.Clone() ; 
     310                current.MoveNext() ; 
     311                cursors.push( current ) ; 
    229312        } 
     313        while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ; 
     314 
     315        return new CharacterRange( cursors, cursors.length ) ; 
    230316} 
    231317 
    232318// Knuth-Morris-Pratt Algorithm for stream input 
    233319KMP_NOMATCH = 0 ; 
    234320KMP_ADVANCED = 1 ; 
    235321KMP_MATCHED = 2 ; 
    236 function KmpMatch( pattern, ignoreCase ) 
     322KmpMatch = function( pattern, ignoreCase ) 
    237323{ 
    238324        var overlap = [ -1 ] ; 
    239325        for ( var i = 0 ; i < pattern.length ; i++ ) 
     
    251337                this.Pattern = pattern ; 
    252338} 
    253339KmpMatch.prototype = { 
    254         "FeedCharacter" : function( c ) 
     340        FeedCharacter : function( c ) 
    255341        { 
    256342                if ( this._IgnoreCase ) 
    257343                        c = c.toLowerCase(); 
     
    277363 
    278364                return null ; 
    279365        }, 
    280         "Reset" : function() 
     366 
     367        Reset : function() 
    281368        { 
    282369                this._State = 0 ; 
    283370        } 
    284371}; 
    285372 
    286 function _Find() 
     373// Place a range at the start of document. 
     374function OnLoad() 
    287375{ 
    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 = [] ; 
     376        // First of all, translate the dialog box texts. 
     377        oEditor.FCKLanguageManager.TranslatePage( document ) ; 
    294378 
    295         // Match finding. 
    296         while ( true ) 
     379        // Show the appropriate tab at startup. 
     380        if ( dialogArguments.CustomValue == 'Find' ) 
    297381        { 
    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) ; 
     382                dialog.SetSelectedTab( 'Find' ) ; 
     383                dialog.SetAutoSize( true ) ; 
     384        } 
     385        else 
     386                dialog.SetSelectedTab( 'Replace' ) ; 
    314387 
    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( [] )] ; 
     388        SelectField( 'txtFind' + dialogArguments.CustomValue ) ; 
     389} 
    333390 
    334                                         matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ; 
    335                                         matchBookmark.End[ matchBookmark.End.length - 1 ]++; 
     391function btnStat() 
     392{ 
     393        GetE('btnReplace').disabled = 
     394                GetE('btnReplaceAll').disabled = 
     395                        GetE('btnFind').disabled = 
     396                                ( GetE(idMap["FindText"]).value.length == 0 ) ; 
     397} 
    336398 
    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                                                 } 
     399function btnStatDelayed() 
     400{ 
     401        setTimeout( btnStat, 1 ) ; 
     402} 
    357403 
    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 ; 
     404function GetSearchString() 
     405{ 
     406        return GetE(idMap['FindText']).value ; 
     407} 
    365408 
    366                                                 if ( startOk && endOk ) 
    367                                                         break ; 
    368                                                 else 
    369                                                         matcher.Reset() ; 
    370                                         } 
    371                                         else 
    372                                                 break ; 
    373                                 } 
    374                         } 
    375                 } 
     409function GetReplaceString() 
     410{ 
     411        return GetE("txtReplace").value ; 
     412} 
    376413 
    377                 // Perform DFS across the document, until we've reached the end. 
    378                 cursor = NextPosition( cursor ) ; 
    379                 if ( cursor == null ) 
    380                         break; 
     414function GetCheckCase() 
     415{ 
     416        return !! ( GetE(idMap['CheckCase']).checked ) ; 
     417} 
     418 
     419function GetMatchWord() 
     420{ 
     421        return !! ( GetE(idMap['CheckWord']).checked ) ; 
     422} 
     423 
     424// Is this character a unicode whitespace? 
     425// Reference: http://unicode.org/Public/UNIDATA/PropList.txt 
     426function CheckIsWhitespace( c ) 
     427{ 
     428        var code = c.charCodeAt( 0 ); 
     429        if ( code >= 9 && code <= 0xd ) 
     430                return true; 
     431        if ( code >= 0x2000 && code <= 0x200a ) 
     432                return true; 
     433        switch ( code ) 
     434        { 
     435                case 0x20: 
     436                case 0x85: 
     437                case 0xa0: 
     438                case 0x1680: 
     439                case 0x180e: 
     440                case 0x2028: 
     441                case 0x2029: 
     442                case 0x202f: 
     443                case 0x205f: 
     444                case 0x3000: 
     445                        return true; 
     446                default: 
     447                        return false; 
    381448        } 
     449} 
    382450 
    383         // If we've found a match, highlight the match. 
     451FindRange = null ; 
     452function _Find() 
     453{ 
     454        var searchString = GetSearchString() ; 
     455        if ( !FindRange ) 
     456                FindRange = new CharacterRange( new CharacterCursor( EditorDocument ), searchString.length ) ; 
     457        else 
     458        { 
     459                FindRange.RemoveHighlight() ; 
     460                FindRange = FindRange.GetNextRange( searchString.length ) ; 
     461        } 
     462        var matcher = new KmpMatch( searchString, ! GetCheckCase() ) ; 
     463        var matchState = KMP_NOMATCH ; 
     464        var character = null ; 
     465 
     466        while ( ( character = FindRange.GetEndCharacter() ) ) 
     467        { 
     468                matchState = matcher.FeedCharacter( character ) ;