| 98 | | dialog.SetSelectedTab( 'Replace' ) ; |
| 99 | | |
| 100 | | SelectField( 'txtFind' + dialogArguments.CustomValue ) ; |
| 101 | | } |
| 102 | | |
| 103 | | function btnStat() |
| 104 | | { |
| 105 | | GetE('btnReplace').disabled = |
| 106 | | GetE('btnReplaceAll').disabled = |
| 107 | | GetE('btnFind').disabled = |
| 108 | | ( GetE(idMap["FindText"]).value.length == 0 ) ; |
| 109 | | } |
| 110 | | |
| 111 | | function btnStatDelayed() |
| 112 | | { |
| 113 | | setTimeout( btnStat, 1 ) ; |
| 114 | | } |
| 115 | | |
| 116 | | function GetSearchString() |
| 117 | | { |
| 118 | | return GetE(idMap['FindText']).value ; |
| 119 | | } |
| 120 | | |
| 121 | | function GetReplaceString() |
| 122 | | { |
| 123 | | return GetE("txtReplace").value ; |
| 124 | | } |
| 125 | | |
| 126 | | function GetCheckCase() |
| 127 | | { |
| 128 | | return !! ( GetE(idMap['CheckCase']).checked ) ; |
| 129 | | } |
| 130 | | |
| 131 | | function GetMatchWord() |
| 132 | | { |
| 133 | | return !! ( GetE(idMap['CheckWord']).checked ) ; |
| 134 | | } |
| 135 | | |
| 136 | | // Get the data pointed to by a bookmark. |
| 137 | | function GetData( bookmark ) |
| 138 | | { |
| 139 | | var cursor = oEditor.FCK.EditorDocument.documentElement ; |
| 140 | | for ( var i = 0 ; i < bookmark.length ; i++ ) |
| 141 | | { |
| 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 | | } |
| 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 | | } |
| 176 | | } |
| 177 | | return cursor ; |
| 178 | | } |
| 179 | | |
| 180 | | // With this function, we can treat the bookmark as an iterator for DFS. |
| 181 | | function NextPosition( bookmark ) |
| 182 | | { |
| 183 | | // See if there's anything further down the tree. |
| 184 | | var next = bookmark.concat( [0] ) ; |
| 185 | | if ( GetData( next ) != null ) |
| 186 | | return next ; |
| 187 | | |
| 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 ; |
| 192 | | |
| 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-- ) |
| 195 | | { |
| 196 | | var next = bookmark.slice( 0, i - 1 ).concat( [ bookmark[ i - 1 ] + 1 ] ) ; |
| 197 | | if ( GetData( next ) != null ) |
| 198 | | return next ; |
| 199 | | } |
| 200 | | |
| 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 ) |
| 215 | | { |
| 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; |
| 229 | | } |
| | 76 | { |
| | 77 | this._textNode = arguments[0] ; |
| | 78 | this._offset = arguments[1] ; |
| | 79 | this._doc = FCKTools.GetElementDocument( arguments[0] ) ; |
| | 80 | } |
| | 81 | } |
| | 82 | CharacterCursor.prototype = |
| | 83 | { |
| | 84 | GetCharacter : function() |
| | 85 | { |
| | 86 | return ( this._textNode && this._textNode.nodeValue.charAt( this._offset ) ) || null ; |
| | 87 | }, |
| | 88 | |
| | 89 | // Non-normalized. |
| | 90 | GetTextNode : function() |
| | 91 | { |
| | 92 | return this._textNode ; |
| | 93 | }, |
| | 94 | |
| | 95 | // Non-normalized. |
| | 96 | GetIndex : function() |
| | 97 | { |
| | 98 | return this._offset ; |
| | 99 | }, |
| | 100 | |
| | 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 | } |
| | 109 | |
| | 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 | } |
| | 119 | |
| | 120 | this._textNode = curNode ; |
| | 121 | this._offset = 0 ; |
| | 122 | return crossed ; |
| | 123 | }, |
| | 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 | } |
| | 133 | |
| | 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 | } |
| | 143 | |
| | 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 | |
| | 155 | CharacterRange = function( initCursor, maxLength ) |
| | 156 | { |
| | 157 | this._cursors = initCursor.push ? initCursor : [initCursor] ; |
| | 158 | this._maxLength = maxLength ; |
| | 159 | this._highlightRange = null ; |
| | 160 | } |
| | 161 | CharacterRange.prototype = |
| | 162 | { |
| | 163 | ToDomRange : function() |
| | 164 | { |
| | 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 ) |
| | 195 | { |
| | 196 | HighlightStyle.RemoveFromRange( this._highlightRange, false, true ) ; |
| | 197 | var charRange = CharacterRange.CreateFromDomRange( this._highlightRange ) ; |
| | 198 | this._cursors = charRange._cursors ; |
| | 199 | this._highlightRange = null ; |
| | 200 | } |
| | 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 ; |
| | 252 | } |
| | 253 | } ; |
| | 254 | |
| | 255 | CharacterRange.CreateFromDomRange = function( domRange ) |
| | 256 | { |
| | 257 | var w3cRange = domRange._Range ; |
| | 258 | var startContainer = w3cRange.startContainer ; |
| | 259 | var endContainer = w3cRange.endContainer ; |
| | 260 | var startTextNode, startIndex, endTextNode, endIndex ; |
| | 261 | |
| | 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 | } |
| | 279 | |
| | 280 | if ( endContainer.nodeType == 3 && w3cRange.endOffset > 0 ) |
| | 281 | { |
| | 282 | endTextNode = endContainer ; |
| | 283 | endIndex = w3cRange.endOffset - 1 ; |
| | 284 | } |
| | 285 | else |
| | 286 | { |
| | 287 | endTextNode = domRange.EndNode ; |
| | 288 | while ( endTextNode.nodeType != 3 ) |
| | 289 | endTextNode = endTextNode.lastChild ; |
| | 290 | endIndex = endTextNode.length - 1 ; |
| | 291 | } |
| | 292 | |
| | 293 | var cursors = [] ; |
| | 294 | var current = new CharacterCursor( startTextNode, startIndex ) ; |
| | 295 | cursors.push( current ) ; |
| | 296 | if ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) && !domRange.CheckIsEmpty() ) |
| | 297 | { |
| | 298 | do |
| | 299 | { |
| | 300 | current = current.Clone() ; |
| | 301 | current.MoveNext() ; |
| | 302 | cursors.push( current ) ; |
| | 303 | } |
| | 304 | while ( !( current.GetTextNode() == endTextNode && current.GetIndex() == endIndex ) ) ; |
| | 305 | } |
| | 306 | |
| | 307 | return new CharacterRange( cursors, cursors.length ) ; |
| 311 | | else if ( data.charAt != undefined ) |
| 312 | | { |
| 313 | | matchState = matcher.FeedCharacter(data) ; |
| 314 | | |
| 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( [] )] ; |
| 333 | | |
| 334 | | matchBookmark = { 'Start' : matchBookmarkStart.shift(), 'End' : cursor.concat( [] ) } ; |
| 335 | | matchBookmark.End[ matchBookmark.End.length - 1 ]++; |
| 336 | | |
| 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 | | } |
| 357 | | |
| 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 ; |
| 365 | | |
| 366 | | if ( startOk && endOk ) |
| 367 | | break ; |
| 368 | | else |
| 369 | | matcher.Reset() ; |
| 370 | | } |
| 371 | | else |
| 372 | | break ; |
| 373 | | } |
| 374 | | } |
| 375 | | } |
| 376 | | |
| 377 | | // Perform DFS across the document, until we've reached the end. |
| 378 | | cursor = NextPosition( cursor ) ; |
| 379 | | if ( cursor == null ) |
| 380 | | break; |
| 381 | | } |
| 382 | | |
| 383 | | // If we've found a match, highlight the match. |
| 384 | | if ( matchState == KMP_MATCHED ) |
| 385 | | { |
| 386 | | GlobalRange.MoveToBookmark2( matchBookmark ) ; |
| 387 | | Highlight() ; |
| 388 | | var focus = GlobalRange._Range.endContainer ; |
| 389 | | while ( focus && focus.nodeType != 1 ) |
| 390 | | focus = focus.parentNode ; |
| 391 | | |
| 392 | | if ( focus ) |
| 393 | | { |
| 394 | | if ( oEditor.FCKBrowserInfo.IsSafari ) |
| 395 | | oEditor.FCKDomTools.ScrollIntoView( focus, false ) ; |
| 396 | | else |
| 397 | | focus.scrollIntoView( false ) ; |
| 398 | | } |
| 399 | | |
| 400 | | return true ; |
| | 484 | |
| | 485 | FindRange.Highlight() ; |
| | 486 | return true ; |
| | 487 | } |
| | 488 | } |
| | 489 | |
| | 490 | FindRange = null ; |
| | 491 | return false ; |
| | 492 | } |
| | 493 | |
| | 494 | function Find() |
| | 495 | { |
| | 496 | if ( ! _Find() ) |
| | 497 | alert( FCKLang.DlgFindNotFoundMsg ) ; |
| | 498 | } |
| | 499 | |
| | 500 | function Replace() |
| | 501 | { |
| | 502 | var saveUndoStep = function( selectRange ) |
| | 503 | { |
| | 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 ) ; |