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