--- /dev/null
+/*\r
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+For licensing, see LICENSE.html or http://ckeditor.com/license\r
+*/\r
+\r
+(function()\r
+{\r
+ var isReplace;\r
+\r
+ function findEvaluator( node )\r
+ {\r
+ return node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 && ( !isReplace || !node.isReadOnly() );\r
+ }\r
+\r
+ /**\r
+ * Elements which break characters been considered as sequence.\r
+ */\r
+ function nonCharactersBoundary( node )\r
+ {\r
+ return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary(\r
+ CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$empty, CKEDITOR.dtd.$nonEditable ) ) );\r
+ }\r
+\r
+ /**\r
+ * Get the cursor object which represent both current character and it's dom\r
+ * position thing.\r
+ */\r
+ var cursorStep = function()\r
+ {\r
+ return {\r
+ textNode : this.textNode,\r
+ offset : this.offset,\r
+ character : this.textNode ?\r
+ this.textNode.getText().charAt( this.offset ) : null,\r
+ hitMatchBoundary : this._.matchBoundary\r
+ };\r
+ };\r
+\r
+ var pages = [ 'find', 'replace' ],\r
+ fieldsMapping = [\r
+ [ 'txtFindFind', 'txtFindReplace' ],\r
+ [ 'txtFindCaseChk', 'txtReplaceCaseChk' ],\r
+ [ 'txtFindWordChk', 'txtReplaceWordChk' ],\r
+ [ 'txtFindCyclic', 'txtReplaceCyclic' ] ];\r
+\r
+ /**\r
+ * Synchronize corresponding filed values between 'replace' and 'find' pages.\r
+ * @param {String} currentPageId The page id which receive values.\r
+ */\r
+ function syncFieldsBetweenTabs( currentPageId )\r
+ {\r
+ var sourceIndex, targetIndex,\r
+ sourceField, targetField;\r
+\r
+ sourceIndex = currentPageId === 'find' ? 1 : 0;\r
+ targetIndex = 1 - sourceIndex;\r
+ var i, l = fieldsMapping.length;\r
+ for ( i = 0 ; i < l ; i++ )\r
+ {\r
+ sourceField = this.getContentElement( pages[ sourceIndex ],\r
+ fieldsMapping[ i ][ sourceIndex ] );\r
+ targetField = this.getContentElement( pages[ targetIndex ],\r
+ fieldsMapping[ i ][ targetIndex ] );\r
+\r
+ targetField.setValue( sourceField.getValue() );\r
+ }\r
+ }\r
+\r
+ var findDialog = function( editor, startupPage )\r
+ {\r
+ // Style object for highlights: (#5018)\r
+ // 1. Defined as full match style to avoid compromising ordinary text color styles.\r
+ // 2. Must be apply onto inner-most text to avoid conflicting with ordinary text color styles visually.\r
+ var highlightStyle = new CKEDITOR.style( CKEDITOR.tools.extend( { fullMatch : true, childRule : function(){ return 0; } },\r
+ editor.config.find_highlight ) );\r
+\r
+ /**\r
+ * Iterator which walk through the specified range char by char. By\r
+ * default the walking will not stop at the character boundaries, until\r
+ * the end of the range is encountered.\r
+ * @param { CKEDITOR.dom.range } range\r
+ * @param {Boolean} matchWord Whether the walking will stop at character boundary.\r
+ */\r
+ var characterWalker = function( range , matchWord )\r
+ {\r
+ var self = this;\r
+ var walker =\r
+ new CKEDITOR.dom.walker( range );\r
+ walker.guard = matchWord ? nonCharactersBoundary : function( node )\r
+ {\r
+ !nonCharactersBoundary( node ) && ( self._.matchBoundary = true );\r
+ };\r
+ walker[ 'evaluator' ] = findEvaluator;\r
+ walker.breakOnFalse = 1;\r
+\r
+ if ( range.startContainer.type == CKEDITOR.NODE_TEXT )\r
+ {\r
+ this.textNode = range.startContainer;\r
+ this.offset = range.startOffset - 1;\r
+ }\r
+\r
+ this._ = {\r
+ matchWord : matchWord,\r
+ walker : walker,\r
+ matchBoundary : false\r
+ };\r
+ };\r
+\r
+ characterWalker.prototype = {\r
+ next : function()\r
+ {\r
+ return this.move();\r
+ },\r
+\r
+ back : function()\r
+ {\r
+ return this.move( true );\r
+ },\r
+\r
+ move : function( rtl )\r
+ {\r
+ var currentTextNode = this.textNode;\r
+ // Already at the end of document, no more character available.\r
+ if ( currentTextNode === null )\r
+ return cursorStep.call( this );\r
+\r
+ this._.matchBoundary = false;\r
+\r
+ // There are more characters in the text node, step forward.\r
+ if ( currentTextNode\r
+ && rtl\r
+ && this.offset > 0 )\r
+ {\r
+ this.offset--;\r
+ return cursorStep.call( this );\r
+ }\r
+ else if ( currentTextNode\r
+ && this.offset < currentTextNode.getLength() - 1 )\r
+ {\r
+ this.offset++;\r
+ return cursorStep.call( this );\r
+ }\r
+ else\r
+ {\r
+ currentTextNode = null;\r
+ // At the end of the text node, walking foward for the next.\r
+ while ( !currentTextNode )\r
+ {\r
+ currentTextNode =\r
+ this._.walker[ rtl ? 'previous' : 'next' ].call( this._.walker );\r
+\r
+ // Stop searching if we're need full word match OR\r
+ // already reach document end.\r
+ if ( this._.matchWord && !currentTextNode\r
+ || this._.walker._.end )\r
+ break;\r
+ }\r
+ // Found a fresh text node.\r
+ this.textNode = currentTextNode;\r
+ if ( currentTextNode )\r
+ this.offset = rtl ? currentTextNode.getLength() - 1 : 0;\r
+ else\r
+ this.offset = 0;\r
+ }\r
+\r
+ return cursorStep.call( this );\r
+ }\r
+\r
+ };\r
+\r
+ /**\r
+ * A range of cursors which represent a trunk of characters which try to\r
+ * match, it has the same length as the pattern string.\r
+ */\r
+ var characterRange = function( characterWalker, rangeLength )\r
+ {\r
+ this._ = {\r
+ walker : characterWalker,\r
+ cursors : [],\r
+ rangeLength : rangeLength,\r
+ highlightRange : null,\r
+ isMatched : 0\r
+ };\r
+ };\r
+\r
+ characterRange.prototype = {\r
+ /**\r
+ * Translate this range to {@link CKEDITOR.dom.range}\r
+ */\r
+ toDomRange : function()\r
+ {\r
+ var range = new CKEDITOR.dom.range( editor.document );\r
+ var cursors = this._.cursors;\r
+ if ( cursors.length < 1 )\r
+ {\r
+ var textNode = this._.walker.textNode;\r
+ if ( textNode )\r
+ range.setStartAfter( textNode );\r
+ else\r
+ return null;\r
+ }\r
+ else\r
+ {\r
+ var first = cursors[0],\r
+ last = cursors[ cursors.length - 1 ];\r
+\r
+ range.setStart( first.textNode, first.offset );\r
+ range.setEnd( last.textNode, last.offset + 1 );\r
+ }\r
+\r
+ return range;\r
+ },\r
+ /**\r
+ * Reflect the latest changes from dom range.\r
+ */\r
+ updateFromDomRange : function( domRange )\r
+ {\r
+ var cursor,\r
+ walker = new characterWalker( domRange );\r
+ this._.cursors = [];\r
+ do\r
+ {\r
+ cursor = walker.next();\r
+ if ( cursor.character )\r
+ this._.cursors.push( cursor );\r
+ }\r
+ while ( cursor.character );\r
+ this._.rangeLength = this._.cursors.length;\r
+ },\r
+\r
+ setMatched : function()\r
+ {\r
+ this._.isMatched = true;\r
+ },\r
+\r
+ clearMatched : function()\r
+ {\r
+ this._.isMatched = false;\r
+ },\r
+\r
+ isMatched : function()\r
+ {\r
+ return this._.isMatched;\r
+ },\r
+\r
+ /**\r
+ * Hightlight the current matched chunk of text.\r
+ */\r
+ highlight : function()\r
+ {\r
+ // Do not apply if nothing is found.\r
+ if ( this._.cursors.length < 1 )\r
+ return;\r
+\r
+ // Remove the previous highlight if there's one.\r
+ if ( this._.highlightRange )\r
+ this.removeHighlight();\r
+\r
+ // Apply the highlight.\r
+ var range = this.toDomRange(),\r
+ bookmark = range.createBookmark();\r
+ highlightStyle.applyToRange( range );\r
+ range.moveToBookmark( bookmark );\r
+ this._.highlightRange = range;\r
+\r
+ // Scroll the editor to the highlighted area.\r
+ var element = range.startContainer;\r
+ if ( element.type != CKEDITOR.NODE_ELEMENT )\r
+ element = element.getParent();\r
+ element.scrollIntoView();\r
+\r
+ // Update the character cursors.\r
+ this.updateFromDomRange( range );\r
+ },\r
+\r
+ /**\r
+ * Remove highlighted find result.\r
+ */\r
+ removeHighlight : function()\r
+ {\r
+ if ( !this._.highlightRange )\r
+ return;\r
+\r
+ var bookmark = this._.highlightRange.createBookmark();\r
+ highlightStyle.removeFromRange( this._.highlightRange );\r
+ this._.highlightRange.moveToBookmark( bookmark );\r
+ this.updateFromDomRange( this._.highlightRange );\r
+ this._.highlightRange = null;\r
+ },\r
+\r
+ isReadOnly : function()\r
+ {\r
+ if ( !this._.highlightRange )\r
+ return 0;\r
+\r
+ return this._.highlightRange.startContainer.isReadOnly();\r
+ },\r
+\r
+ moveBack : function()\r
+ {\r
+ var retval = this._.walker.back(),\r
+ cursors = this._.cursors;\r
+\r
+ if ( retval.hitMatchBoundary )\r
+ this._.cursors = cursors = [];\r
+\r
+ cursors.unshift( retval );\r
+ if ( cursors.length > this._.rangeLength )\r
+ cursors.pop();\r
+\r
+ return retval;\r
+ },\r
+\r
+ moveNext : function()\r
+ {\r
+ var retval = this._.walker.next(),\r
+ cursors = this._.cursors;\r
+\r
+ // Clear the cursors queue if we've crossed a match boundary.\r
+ if ( retval.hitMatchBoundary )\r
+ this._.cursors = cursors = [];\r
+\r
+ cursors.push( retval );\r
+ if ( cursors.length > this._.rangeLength )\r
+ cursors.shift();\r
+\r
+ return retval;\r
+ },\r
+\r
+ getEndCharacter : function()\r
+ {\r
+ var cursors = this._.cursors;\r
+ if ( cursors.length < 1 )\r
+ return null;\r
+\r
+ return cursors[ cursors.length - 1 ].character;\r
+ },\r
+\r
+ getNextCharacterRange : function( maxLength )\r
+ {\r
+ var lastCursor,\r
+ nextRangeWalker,\r
+ cursors = this._.cursors;\r
+\r
+ if ( ( lastCursor = cursors[ cursors.length - 1 ] ) && lastCursor.textNode )\r
+ nextRangeWalker = new characterWalker( getRangeAfterCursor( lastCursor ) );\r
+ // In case it's an empty range (no cursors), figure out next range from walker (#4951).\r
+ else\r
+ nextRangeWalker = this._.walker;\r
+\r
+ return new characterRange( nextRangeWalker, maxLength );\r
+ },\r
+\r
+ getCursors : function()\r
+ {\r
+ return this._.cursors;\r
+ }\r
+ };\r
+\r
+\r
+ // The remaining document range after the character cursor.\r
+ function getRangeAfterCursor( cursor , inclusive )\r
+ {\r
+ var range = new CKEDITOR.dom.range();\r
+ range.setStart( cursor.textNode,\r
+ ( inclusive ? cursor.offset : cursor.offset + 1 ) );\r
+ range.setEndAt( editor.document.getBody(),\r
+ CKEDITOR.POSITION_BEFORE_END );\r
+ return range;\r
+ }\r
+\r
+ // The document range before the character cursor.\r
+ function getRangeBeforeCursor( cursor )\r
+ {\r
+ var range = new CKEDITOR.dom.range();\r
+ range.setStartAt( editor.document.getBody(),\r
+ CKEDITOR.POSITION_AFTER_START );\r
+ range.setEnd( cursor.textNode, cursor.offset );\r
+ return range;\r
+ }\r
+\r
+ var KMP_NOMATCH = 0,\r
+ KMP_ADVANCED = 1,\r
+ KMP_MATCHED = 2;\r
+ /**\r
+ * Examination the occurrence of a word which implement KMP algorithm.\r
+ */\r
+ var kmpMatcher = function( pattern, ignoreCase )\r
+ {\r
+ var overlap = [ -1 ];\r
+ if ( ignoreCase )\r
+ pattern = pattern.toLowerCase();\r
+ for ( var i = 0 ; i < pattern.length ; i++ )\r
+ {\r
+ overlap.push( overlap[i] + 1 );\r
+ while ( overlap[ i + 1 ] > 0\r
+ && pattern.charAt( i ) != pattern\r
+ .charAt( overlap[ i + 1 ] - 1 ) )\r
+ overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1;\r
+ }\r
+\r
+ this._ = {\r
+ overlap : overlap,\r
+ state : 0,\r
+ ignoreCase : !!ignoreCase,\r
+ pattern : pattern\r
+ };\r
+ };\r
+\r
+ kmpMatcher.prototype =\r
+ {\r
+ feedCharacter : function( c )\r
+ {\r
+ if ( this._.ignoreCase )\r
+ c = c.toLowerCase();\r
+\r
+ while ( true )\r
+ {\r
+ if ( c == this._.pattern.charAt( this._.state ) )\r
+ {\r
+ this._.state++;\r
+ if ( this._.state == this._.pattern.length )\r
+ {\r
+ this._.state = 0;\r
+ return KMP_MATCHED;\r
+ }\r
+ return KMP_ADVANCED;\r
+ }\r
+ else if ( !this._.state )\r
+ return KMP_NOMATCH;\r
+ else\r
+ this._.state = this._.overlap[ this._.state ];\r
+ }\r
+\r
+ return null;\r
+ },\r
+\r
+ reset : function()\r
+ {\r
+ this._.state = 0;\r
+ }\r
+ };\r
+\r
+ var wordSeparatorRegex =\r
+ /[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/;\r
+\r
+ var isWordSeparator = function( c )\r
+ {\r
+ if ( !c )\r
+ return true;\r
+ var code = c.charCodeAt( 0 );\r
+ return ( code >= 9 && code <= 0xd )\r
+ || ( code >= 0x2000 && code <= 0x200a )\r
+ || wordSeparatorRegex.test( c );\r
+ };\r
+\r
+ var finder = {\r
+ searchRange : null,\r
+ matchRange : null,\r
+ find : function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun )\r
+ {\r
+ if ( !this.matchRange )\r
+ this.matchRange =\r
+ new characterRange(\r
+ new characterWalker( this.searchRange ),\r
+ pattern.length );\r
+ else\r
+ {\r
+ this.matchRange.removeHighlight();\r
+ this.matchRange = this.matchRange.getNextCharacterRange( pattern.length );\r
+ }\r
+\r
+ var matcher = new kmpMatcher( pattern, !matchCase ),\r
+ matchState = KMP_NOMATCH,\r
+ character = '%';\r
+\r
+ while ( character !== null )\r
+ {\r
+ this.matchRange.moveNext();\r
+ while ( ( character = this.matchRange.getEndCharacter() ) )\r
+ {\r
+ matchState = matcher.feedCharacter( character );\r
+ if ( matchState == KMP_MATCHED )\r
+ break;\r
+ if ( this.matchRange.moveNext().hitMatchBoundary )\r
+ matcher.reset();\r
+ }\r
+\r
+ if ( matchState == KMP_MATCHED )\r
+ {\r
+ if ( matchWord )\r
+ {\r
+ var cursors = this.matchRange.getCursors(),\r
+ tail = cursors[ cursors.length - 1 ],\r
+ head = cursors[ 0 ];\r
+\r
+ var headWalker = new characterWalker( getRangeBeforeCursor( head ), true ),\r
+ tailWalker = new characterWalker( getRangeAfterCursor( tail ), true );\r
+\r
+ if ( ! ( isWordSeparator( headWalker.back().character )\r
+ && isWordSeparator( tailWalker.next().character ) ) )\r
+ continue;\r
+ }\r
+ this.matchRange.setMatched();\r
+ if ( highlightMatched !== false )\r
+ this.matchRange.highlight();\r
+ return true;\r
+ }\r
+ }\r
+\r
+ this.matchRange.clearMatched();\r
+ this.matchRange.removeHighlight();\r
+ // Clear current session and restart with the default search\r
+ // range.\r
+ // Re-run the finding once for cyclic.(#3517)\r
+ if ( matchCyclic && !cyclicRerun )\r
+ {\r
+ this.searchRange = getSearchRange( 1 );\r
+ this.matchRange = null;\r
+ return arguments.callee.apply( this,\r
+ Array.prototype.slice.call( arguments ).concat( [ true ] ) );\r
+ }\r
+\r
+ return false;\r
+ },\r
+\r
+ /**\r
+ * Record how much replacement occurred toward one replacing.\r
+ */\r
+ replaceCounter : 0,\r
+\r
+ replace : function( dialog, pattern, newString, matchCase, matchWord,\r
+ matchCyclic , isReplaceAll )\r
+ {\r
+ isReplace = 1;\r
+\r
+ // Successiveness of current replace/find.\r
+ var result = 0;\r
+\r
+ // 1. Perform the replace when there's already a match here.\r
+ // 2. Otherwise perform the find but don't replace it immediately.\r
+ if ( this.matchRange && this.matchRange.isMatched()\r
+ && !this.matchRange._.isReplaced && !this.matchRange.isReadOnly() )\r
+ {\r
+ // Turn off highlight for a while when saving snapshots.\r
+ this.matchRange.removeHighlight();\r
+ var domRange = this.matchRange.toDomRange();\r
+ var text = editor.document.createText( newString );\r
+ if ( !isReplaceAll )\r
+ {\r
+ // Save undo snaps before and after the replacement.\r
+ var selection = editor.getSelection();\r
+ selection.selectRanges( [ domRange ] );\r
+ editor.fire( 'saveSnapshot' );\r
+ }\r
+ domRange.deleteContents();\r
+ domRange.insertNode( text );\r
+ if ( !isReplaceAll )\r
+ {\r
+ selection.selectRanges( [ domRange ] );\r
+ editor.fire( 'saveSnapshot' );\r
+ }\r
+ this.matchRange.updateFromDomRange( domRange );\r
+ if ( !isReplaceAll )\r
+ this.matchRange.highlight();\r
+ this.matchRange._.isReplaced = true;\r
+ this.replaceCounter++;\r
+ result = 1;\r
+ }\r
+ else\r
+ result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );\r
+\r
+ isReplace = 0;\r
+\r
+ return result;\r
+ }\r
+ };\r
+\r
+ /**\r
+ * The range in which find/replace happened, receive from user\r
+ * selection prior.\r
+ */\r
+ function getSearchRange( isDefault )\r
+ {\r
+ var searchRange,\r
+ sel = editor.getSelection(),\r
+ body = editor.document.getBody();\r
+ if ( sel && !isDefault )\r
+ {\r
+ searchRange = sel.getRanges()[ 0 ].clone();\r
+ searchRange.collapse( true );\r
+ }\r
+ else\r
+ {\r
+ searchRange = new CKEDITOR.dom.range();\r
+ searchRange.setStartAt( body, CKEDITOR.POSITION_AFTER_START );\r
+ }\r
+ searchRange.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );\r
+ return searchRange;\r
+ }\r
+\r
+ var lang = editor.lang.findAndReplace;\r
+ return {\r
+ title : lang.title,\r
+ resizable : CKEDITOR.DIALOG_RESIZE_NONE,\r
+ minWidth : 350,\r
+ minHeight : 170,\r
+ buttons : [ CKEDITOR.dialog.cancelButton ], // Cancel button only.\r
+ contents : [\r
+ {\r
+ id : 'find',\r
+ label : lang.find,\r
+ title : lang.find,\r
+ accessKey : '',\r
+ elements : [\r
+ {\r
+ type : 'hbox',\r
+ widths : [ '230px', '90px' ],\r
+ children :\r
+ [\r
+ {\r
+ type : 'text',\r
+ id : 'txtFindFind',\r
+ label : lang.findWhat,\r
+ isChanged : false,\r
+ labelLayout : 'horizontal',\r
+ accessKey : 'F'\r
+ },\r
+ {\r
+ type : 'button',\r
+ align : 'left',\r
+ style : 'width:100%',\r
+ label : lang.find,\r
+ onClick : function()\r
+ {\r
+ var dialog = this.getDialog();\r
+ if ( !finder.find( dialog.getValueOf( 'find', 'txtFindFind' ),\r
+ dialog.getValueOf( 'find', 'txtFindCaseChk' ),\r
+ dialog.getValueOf( 'find', 'txtFindWordChk' ),\r
+ dialog.getValueOf( 'find', 'txtFindCyclic' ) ) )\r
+ alert( lang\r
+ .notFoundMsg );\r
+ }\r
+ }\r
+ ]\r
+ },\r
+ {\r
+ type : 'vbox',\r
+ padding : 0,\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtFindCaseChk',\r
+ isChanged : false,\r
+ style : 'margin-top:28px',\r
+ label : lang.matchCase\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtFindWordChk',\r
+ isChanged : false,\r
+ label : lang.matchWord\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtFindCyclic',\r
+ isChanged : false,\r
+ 'default' : true,\r
+ label : lang.matchCyclic\r
+ }\r
+ ]\r
+ }\r
+ ]\r
+ },\r
+ {\r
+ id : 'replace',\r
+ label : lang.replace,\r
+ accessKey : 'M',\r
+ elements : [\r
+ {\r
+ type : 'hbox',\r
+ widths : [ '230px', '90px' ],\r
+ children :\r
+ [\r
+ {\r
+ type : 'text',\r
+ id : 'txtFindReplace',\r
+ label : lang.findWhat,\r
+ isChanged : false,\r
+ labelLayout : 'horizontal',\r
+ accessKey : 'F'\r
+ },\r
+ {\r
+ type : 'button',\r
+ align : 'left',\r
+ style : 'width:100%',\r
+ label : lang.replace,\r
+ onClick : function()\r
+ {\r
+ var dialog = this.getDialog();\r
+ if ( !finder.replace( dialog,\r
+ dialog.getValueOf( 'replace', 'txtFindReplace' ),\r
+ dialog.getValueOf( 'replace', 'txtReplace' ),\r
+ dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),\r
+ dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),\r
+ dialog.getValueOf( 'replace', 'txtReplaceCyclic' ) ) )\r
+ alert( lang\r
+ .notFoundMsg );\r
+ }\r
+ }\r
+ ]\r
+ },\r
+ {\r
+ type : 'hbox',\r
+ widths : [ '230px', '90px' ],\r
+ children :\r
+ [\r
+ {\r
+ type : 'text',\r
+ id : 'txtReplace',\r
+ label : lang.replaceWith,\r
+ isChanged : false,\r
+ labelLayout : 'horizontal',\r
+ accessKey : 'R'\r
+ },\r
+ {\r
+ type : 'button',\r
+ align : 'left',\r
+ style : 'width:100%',\r
+ label : lang.replaceAll,\r
+ isChanged : false,\r
+ onClick : function()\r
+ {\r
+ var dialog = this.getDialog();\r
+ var replaceNums;\r
+\r
+ finder.replaceCounter = 0;\r
+\r
+ // Scope to full document.\r
+ finder.searchRange = getSearchRange( 1 );\r
+ if ( finder.matchRange )\r
+ {\r
+ finder.matchRange.removeHighlight();\r
+ finder.matchRange = null;\r
+ }\r
+ editor.fire( 'saveSnapshot' );\r
+ while ( finder.replace( dialog,\r
+ dialog.getValueOf( 'replace', 'txtFindReplace' ),\r
+ dialog.getValueOf( 'replace', 'txtReplace' ),\r
+ dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),\r
+ dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),\r
+ false, true ) )\r
+ { /*jsl:pass*/ }\r
+\r
+ if ( finder.replaceCounter )\r
+ {\r
+ alert( lang.replaceSuccessMsg.replace( /%1/, finder.replaceCounter ) );\r
+ editor.fire( 'saveSnapshot' );\r
+ }\r
+ else\r
+ alert( lang.notFoundMsg );\r
+ }\r
+ }\r
+ ]\r
+ },\r
+ {\r
+ type : 'vbox',\r
+ padding : 0,\r
+ children :\r
+ [\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtReplaceCaseChk',\r
+ isChanged : false,\r
+ label : lang\r
+ .matchCase\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtReplaceWordChk',\r
+ isChanged : false,\r
+ label : lang\r
+ .matchWord\r
+ },\r
+ {\r
+ type : 'checkbox',\r
+ id : 'txtReplaceCyclic',\r
+ isChanged : false,\r
+ 'default' : true,\r
+ label : lang\r
+ .matchCyclic\r
+ }\r
+ ]\r
+ }\r
+ ]\r
+ }\r
+ ],\r
+ onLoad : function()\r
+ {\r
+ var dialog = this;\r
+\r
+ // Keep track of the current pattern field in use.\r
+ var patternField, wholeWordChkField;\r
+\r
+ // Ignore initial page select on dialog show\r
+ var isUserSelect = 0;\r
+ this.on( 'hide', function()\r
+ {\r
+ isUserSelect = 0;\r
+ });\r
+ this.on( 'show', function()\r
+ {\r
+ isUserSelect = 1;\r
+ });\r
+\r
+ this.selectPage = CKEDITOR.tools.override( this.selectPage, function( originalFunc )\r
+ {\r
+ return function( pageId )\r
+ {\r
+ originalFunc.call( dialog, pageId );\r
+\r
+ var currPage = dialog._.tabs[ pageId ];\r
+ var patternFieldInput, patternFieldId, wholeWordChkFieldId;\r
+ patternFieldId = pageId === 'find' ? 'txtFindFind' : 'txtFindReplace';\r
+ wholeWordChkFieldId = pageId === 'find' ? 'txtFindWordChk' : 'txtReplaceWordChk';\r
+\r
+ patternField = dialog.getContentElement( pageId,\r
+ patternFieldId );\r
+ wholeWordChkField = dialog.getContentElement( pageId,\r
+ wholeWordChkFieldId );\r
+\r
+ // Prepare for check pattern text filed 'keyup' event\r
+ if ( !currPage.initialized )\r
+ {\r
+ patternFieldInput = CKEDITOR.document\r
+ .getById( patternField._.inputId );\r
+ currPage.initialized = true;\r
+ }\r
+\r
+ // Synchronize fields on tab switch.\r
+ if ( isUserSelect )\r
+ syncFieldsBetweenTabs.call( this, pageId );\r
+ };\r
+ } );\r
+\r
+ },\r
+ onShow : function()\r
+ {\r
+ // Establish initial searching start position.\r
+ finder.searchRange = getSearchRange();\r
+\r
+ this.selectPage( startupPage );\r
+ },\r
+ onHide : function()\r
+ {\r
+ var range;\r
+ if ( finder.matchRange && finder.matchRange.isMatched() )\r
+ {\r
+ finder.matchRange.removeHighlight();\r
+ editor.focus();\r
+\r
+ range = finder.matchRange.toDomRange();\r
+ if ( range )\r
+ editor.getSelection().selectRanges( [ range ] );\r
+ }\r
+\r
+ // Clear current session before dialog close\r
+ delete finder.matchRange;\r
+ },\r
+ onFocus : function()\r
+ {\r
+ if ( startupPage == 'replace' )\r
+ return this.getContentElement( 'replace', 'txtFindReplace' );\r
+ else\r
+ return this.getContentElement( 'find', 'txtFindFind' );\r
+ }\r
+ };\r
+ };\r
+\r
+ CKEDITOR.dialog.add( 'find', function( editor )\r
+ {\r
+ return findDialog( editor, 'find' );\r
+ });\r
+\r
+ CKEDITOR.dialog.add( 'replace', function( editor )\r
+ {\r
+ return findDialog( editor, 'replace' );\r
+ });\r
+})();\r