]> git.immae.eu Git - perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git/blob - sources/plugins/find/dialogs/find.js
Upgrade to 4.5.7 and add some plugin
[perso/Immae/Projets/packagist/connexionswing-ckeditor-component.git] / sources / plugins / find / dialogs / find.js
1 /**
2 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6 ( function() {
7 var isReplace;
8
9 function findEvaluator( node ) {
10 return node.type == CKEDITOR.NODE_TEXT && node.getLength() > 0 && ( !isReplace || !node.isReadOnly() );
11 }
12
13 // Elements which break characters been considered as sequence.
14 function nonCharactersBoundary( node ) {
15 return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary( CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$empty, CKEDITOR.dtd.$nonEditable ) ) );
16 }
17
18 // Get the cursor object which represent both current character and it's dom
19 // position thing.
20 var cursorStep = function() {
21 return {
22 textNode: this.textNode,
23 offset: this.offset,
24 character: this.textNode ? this.textNode.getText().charAt( this.offset ) : null,
25 hitMatchBoundary: this._.matchBoundary
26 };
27 };
28
29 var pages = [ 'find', 'replace' ],
30 fieldsMapping = [
31 [ 'txtFindFind', 'txtFindReplace' ],
32 [ 'txtFindCaseChk', 'txtReplaceCaseChk' ],
33 [ 'txtFindWordChk', 'txtReplaceWordChk' ],
34 [ 'txtFindCyclic', 'txtReplaceCyclic' ]
35 ];
36
37 // Synchronize corresponding filed values between 'replace' and 'find' pages.
38 // @param {String} currentPageId The page id which receive values.
39 function syncFieldsBetweenTabs( currentPageId ) {
40 var sourceIndex, targetIndex, sourceField, targetField;
41
42 sourceIndex = currentPageId === 'find' ? 1 : 0;
43 targetIndex = 1 - sourceIndex;
44 var i,
45 l = fieldsMapping.length;
46 for ( i = 0; i < l; i++ ) {
47 sourceField = this.getContentElement( pages[ sourceIndex ], fieldsMapping[ i ][ sourceIndex ] );
48 targetField = this.getContentElement( pages[ targetIndex ], fieldsMapping[ i ][ targetIndex ] );
49
50 targetField.setValue( sourceField.getValue() );
51 }
52 }
53
54 function findDialog( editor, startupPage ) {
55 // Style object for highlights: (#5018)
56 // 1. Defined as full match style to avoid compromising ordinary text color styles.
57 // 2. Must be apply onto inner-most text to avoid conflicting with ordinary text color styles visually.
58 var highlightConfig = {
59 attributes: {
60 'data-cke-highlight': 1
61 },
62 fullMatch: 1,
63 ignoreReadonly: 1,
64 childRule: function() {
65 return 0;
66 }
67 };
68 var highlightStyle = new CKEDITOR.style( CKEDITOR.tools.extend( highlightConfig, editor.config.find_highlight, true ) );
69
70 // Iterator which walk through the specified range char by char. By
71 // default the walking will not stop at the character boundaries, until
72 // the end of the range is encountered.
73 // @param { CKEDITOR.dom.range } range
74 // @param {Boolean} matchWord Whether the walking will stop at character boundary.
75 function characterWalker( range, matchWord ) {
76 var self = this;
77 var walker = new CKEDITOR.dom.walker( range );
78 walker.guard = matchWord ? nonCharactersBoundary : function( node ) {
79 !nonCharactersBoundary( node ) && ( self._.matchBoundary = true );
80 };
81 walker.evaluator = findEvaluator;
82 walker.breakOnFalse = 1;
83
84 if ( range.startContainer.type == CKEDITOR.NODE_TEXT ) {
85 this.textNode = range.startContainer;
86 this.offset = range.startOffset - 1;
87 }
88
89 this._ = {
90 matchWord: matchWord,
91 walker: walker,
92 matchBoundary: false
93 };
94 }
95
96 characterWalker.prototype = {
97 next: function() {
98 return this.move();
99 },
100
101 back: function() {
102 return this.move( true );
103 },
104
105 move: function( rtl ) {
106 var currentTextNode = this.textNode;
107 // Already at the end of document, no more character available.
108 if ( currentTextNode === null )
109 return cursorStep.call( this );
110
111 this._.matchBoundary = false;
112
113 // There are more characters in the text node, step forward.
114 if ( currentTextNode && rtl && this.offset > 0 ) {
115 this.offset--;
116 return cursorStep.call( this );
117 } else if ( currentTextNode && this.offset < currentTextNode.getLength() - 1 ) {
118 this.offset++;
119 return cursorStep.call( this );
120 } else {
121 currentTextNode = null;
122 // At the end of the text node, walking foward for the next.
123 while ( !currentTextNode ) {
124 currentTextNode = this._.walker[ rtl ? 'previous' : 'next' ].call( this._.walker );
125
126 // Stop searching if we're need full word match OR
127 // already reach document end.
128 if ( this._.matchWord && !currentTextNode || this._.walker._.end )
129 break;
130 }
131 // Found a fresh text node.
132 this.textNode = currentTextNode;
133 if ( currentTextNode )
134 this.offset = rtl ? currentTextNode.getLength() - 1 : 0;
135 else
136 this.offset = 0;
137 }
138
139 return cursorStep.call( this );
140 }
141
142 };
143
144 /**
145 * A range of cursors which represent a trunk of characters which try to
146 * match, it has the same length as the pattern string.
147 *
148 * **Note:** This class isn't accessible from global scope.
149 *
150 * @private
151 * @class CKEDITOR.plugins.find.characterRange
152 * @constructor Creates a characterRange class instance.
153 */
154 var characterRange = function( characterWalker, rangeLength ) {
155 this._ = {
156 walker: characterWalker,
157 cursors: [],
158 rangeLength: rangeLength,
159 highlightRange: null,
160 isMatched: 0
161 };
162 };
163
164 characterRange.prototype = {
165 /**
166 * Translate this range to {@link CKEDITOR.dom.range}.
167 */
168 toDomRange: function() {
169 var range = editor.createRange();
170 var cursors = this._.cursors;
171 if ( cursors.length < 1 ) {
172 var textNode = this._.walker.textNode;
173 if ( textNode )
174 range.setStartAfter( textNode );
175 else
176 return null;
177 } else {
178 var first = cursors[ 0 ],
179 last = cursors[ cursors.length - 1 ];
180
181 range.setStart( first.textNode, first.offset );
182 range.setEnd( last.textNode, last.offset + 1 );
183 }
184
185 return range;
186 },
187
188 /**
189 * Reflect the latest changes from dom range.
190 */
191 updateFromDomRange: function( domRange ) {
192 var cursor,
193 walker = new characterWalker( domRange );
194 this._.cursors = [];
195 do {
196 cursor = walker.next();
197 if ( cursor.character ) this._.cursors.push( cursor );
198 }
199 while ( cursor.character );
200 this._.rangeLength = this._.cursors.length;
201 },
202
203 setMatched: function() {
204 this._.isMatched = true;
205 },
206
207 clearMatched: function() {
208 this._.isMatched = false;
209 },
210
211 isMatched: function() {
212 return this._.isMatched;
213 },
214
215 /**
216 * Hightlight the current matched chunk of text.
217 */
218 highlight: function() {
219 // Do not apply if nothing is found.
220 if ( this._.cursors.length < 1 )
221 return;
222
223 // Remove the previous highlight if there's one.
224 if ( this._.highlightRange )
225 this.removeHighlight();
226
227 // Apply the highlight.
228 var range = this.toDomRange(),
229 bookmark = range.createBookmark();
230 highlightStyle.applyToRange( range, editor );
231 range.moveToBookmark( bookmark );
232 this._.highlightRange = range;
233
234 // Scroll the editor to the highlighted area.
235 var element = range.startContainer;
236 if ( element.type != CKEDITOR.NODE_ELEMENT )
237 element = element.getParent();
238 element.scrollIntoView();
239
240 // Update the character cursors.
241 this.updateFromDomRange( range );
242 },
243
244 /**
245 * Remove highlighted find result.
246 */
247 removeHighlight: function() {
248 if ( !this._.highlightRange )
249 return;
250
251 var bookmark = this._.highlightRange.createBookmark();
252 highlightStyle.removeFromRange( this._.highlightRange, editor );
253 this._.highlightRange.moveToBookmark( bookmark );
254 this.updateFromDomRange( this._.highlightRange );
255 this._.highlightRange = null;
256 },
257
258 isReadOnly: function() {
259 if ( !this._.highlightRange )
260 return 0;
261
262 return this._.highlightRange.startContainer.isReadOnly();
263 },
264
265 moveBack: function() {
266 var retval = this._.walker.back(),
267 cursors = this._.cursors;
268
269 if ( retval.hitMatchBoundary )
270 this._.cursors = cursors = [];
271
272 cursors.unshift( retval );
273 if ( cursors.length > this._.rangeLength )
274 cursors.pop();
275
276 return retval;
277 },
278
279 moveNext: function() {
280 var retval = this._.walker.next(),
281 cursors = this._.cursors;
282
283 // Clear the cursors queue if we've crossed a match boundary.
284 if ( retval.hitMatchBoundary )
285 this._.cursors = cursors = [];
286
287 cursors.push( retval );
288 if ( cursors.length > this._.rangeLength )
289 cursors.shift();
290
291 return retval;
292 },
293
294 getEndCharacter: function() {
295 var cursors = this._.cursors;
296 if ( cursors.length < 1 )
297 return null;
298
299 return cursors[ cursors.length - 1 ].character;
300 },
301
302 getNextCharacterRange: function( maxLength ) {
303 var lastCursor, nextRangeWalker,
304 cursors = this._.cursors;
305
306 if ( ( lastCursor = cursors[ cursors.length - 1 ] ) && lastCursor.textNode )
307 nextRangeWalker = new characterWalker( getRangeAfterCursor( lastCursor ) );
308 // In case it's an empty range (no cursors), figure out next range from walker (#4951).
309 else
310 nextRangeWalker = this._.walker;
311
312 return new characterRange( nextRangeWalker, maxLength );
313 },
314
315 getCursors: function() {
316 return this._.cursors;
317 }
318 };
319
320
321 // The remaining document range after the character cursor.
322 function getRangeAfterCursor( cursor, inclusive ) {
323 var range = editor.createRange();
324 range.setStart( cursor.textNode, ( inclusive ? cursor.offset : cursor.offset + 1 ) );
325 range.setEndAt( editor.editable(), CKEDITOR.POSITION_BEFORE_END );
326 return range;
327 }
328
329 // The document range before the character cursor.
330 function getRangeBeforeCursor( cursor ) {
331 var range = editor.createRange();
332 range.setStartAt( editor.editable(), CKEDITOR.POSITION_AFTER_START );
333 range.setEnd( cursor.textNode, cursor.offset );
334 return range;
335 }
336
337 var KMP_NOMATCH = 0,
338 KMP_ADVANCED = 1,
339 KMP_MATCHED = 2;
340
341 // Examination the occurrence of a word which implement KMP algorithm.
342 var kmpMatcher = function( pattern, ignoreCase ) {
343 var overlap = [ -1 ];
344 if ( ignoreCase )
345 pattern = pattern.toLowerCase();
346 for ( var i = 0; i < pattern.length; i++ ) {
347 overlap.push( overlap[ i ] + 1 );
348 while ( overlap[ i + 1 ] > 0 && pattern.charAt( i ) != pattern.charAt( overlap[ i + 1 ] - 1 ) )
349 overlap[ i + 1 ] = overlap[ overlap[ i + 1 ] - 1 ] + 1;
350 }
351
352 this._ = {
353 overlap: overlap,
354 state: 0,
355 ignoreCase: !!ignoreCase,
356 pattern: pattern
357 };
358 };
359
360 kmpMatcher.prototype = {
361 feedCharacter: function( c ) {
362 if ( this._.ignoreCase )
363 c = c.toLowerCase();
364
365 while ( true ) {
366 if ( c == this._.pattern.charAt( this._.state ) ) {
367 this._.state++;
368 if ( this._.state == this._.pattern.length ) {
369 this._.state = 0;
370 return KMP_MATCHED;
371 }
372 return KMP_ADVANCED;
373 } else if ( !this._.state ) {
374 return KMP_NOMATCH;
375 } else {
376 this._.state = this._.overlap[this._.state];
377 }
378 }
379 },
380
381 reset: function() {
382 this._.state = 0;
383 }
384 };
385
386 var wordSeparatorRegex = /[.,"'?!;: \u0085\u00a0\u1680\u280e\u2028\u2029\u202f\u205f\u3000]/;
387
388 var isWordSeparator = function( c ) {
389 if ( !c )
390 return true;
391 var code = c.charCodeAt( 0 );
392 return ( code >= 9 && code <= 0xd ) || ( code >= 0x2000 && code <= 0x200a ) || wordSeparatorRegex.test( c );
393 };
394
395 var finder = {
396 searchRange: null,
397 matchRange: null,
398 find: function( pattern, matchCase, matchWord, matchCyclic, highlightMatched, cyclicRerun ) {
399 if ( !this.matchRange )
400 this.matchRange = new characterRange( new characterWalker( this.searchRange ), pattern.length );
401 else {
402 this.matchRange.removeHighlight();
403 this.matchRange = this.matchRange.getNextCharacterRange( pattern.length );
404 }
405
406 var matcher = new kmpMatcher( pattern, !matchCase ),
407 matchState = KMP_NOMATCH,
408 character = '%';
409
410 while ( character !== null ) {
411 this.matchRange.moveNext();
412 while ( ( character = this.matchRange.getEndCharacter() ) ) {
413 matchState = matcher.feedCharacter( character );
414 if ( matchState == KMP_MATCHED )
415 break;
416 if ( this.matchRange.moveNext().hitMatchBoundary )
417 matcher.reset();
418 }
419
420 if ( matchState == KMP_MATCHED ) {
421 if ( matchWord ) {
422 var cursors = this.matchRange.getCursors(),
423 tail = cursors[ cursors.length - 1 ],
424 head = cursors[ 0 ];
425
426 var rangeBefore = getRangeBeforeCursor( head ),
427 rangeAfter = getRangeAfterCursor( tail );
428
429 // The word boundary checks requires to trim the text nodes. (#9036)
430 rangeBefore.trim();
431 rangeAfter.trim();
432
433 var headWalker = new characterWalker( rangeBefore, true ),
434 tailWalker = new characterWalker( rangeAfter, true );
435
436 if ( !( isWordSeparator( headWalker.back().character ) && isWordSeparator( tailWalker.next().character ) ) )
437 continue;
438 }
439 this.matchRange.setMatched();
440 if ( highlightMatched !== false )
441 this.matchRange.highlight();
442 return true;
443 }
444 }
445
446 this.matchRange.clearMatched();
447 this.matchRange.removeHighlight();
448 // Clear current session and restart with the default search
449 // range.
450 // Re-run the finding once for cyclic.(#3517)
451 if ( matchCyclic && !cyclicRerun ) {
452 this.searchRange = getSearchRange( 1 );
453 this.matchRange = null;
454 return arguments.callee.apply( this, Array.prototype.slice.call( arguments ).concat( [ true ] ) );
455 }
456
457 return false;
458 },
459
460 // Record how much replacement occurred toward one replacing.
461 replaceCounter: 0,
462
463 replace: function( dialog, pattern, newString, matchCase, matchWord, matchCyclic, isReplaceAll ) {
464 isReplace = 1;
465
466 // Successiveness of current replace/find.
467 var result = 0;
468
469 // 1. Perform the replace when there's already a match here.
470 // 2. Otherwise perform the find but don't replace it immediately.
471 if ( this.matchRange && this.matchRange.isMatched() && !this.matchRange._.isReplaced && !this.matchRange.isReadOnly() ) {
472 // Turn off highlight for a while when saving snapshots.
473 this.matchRange.removeHighlight();
474 var domRange = this.matchRange.toDomRange();
475 var text = editor.document.createText( newString );
476 if ( !isReplaceAll ) {
477 // Save undo snaps before and after the replacement.
478 var selection = editor.getSelection();
479 selection.selectRanges( [ domRange ] );
480 editor.fire( 'saveSnapshot' );
481 }
482 domRange.deleteContents();
483 domRange.insertNode( text );
484 if ( !isReplaceAll ) {
485 selection.selectRanges( [ domRange ] );
486 editor.fire( 'saveSnapshot' );
487 }
488 this.matchRange.updateFromDomRange( domRange );
489 if ( !isReplaceAll )
490 this.matchRange.highlight();
491 this.matchRange._.isReplaced = true;
492 this.replaceCounter++;
493 result = 1;
494 } else {
495 result = this.find( pattern, matchCase, matchWord, matchCyclic, !isReplaceAll );
496 }
497
498 isReplace = 0;
499
500 return result;
501 }
502 };
503
504 // The range in which find/replace happened, receive from user
505 // selection prior.
506 function getSearchRange( isDefault ) {
507 var searchRange,
508 sel = editor.getSelection(),
509 range = sel.getRanges()[ 0 ],
510 editable = editor.editable();
511
512 // Blink browsers return empty array of ranges when editor is in read-only mode
513 // and it hasn't got focus, so instead of selection, we check for range itself. (#12848)
514 if ( range && !isDefault ) {
515 searchRange = range.clone();
516 searchRange.collapse( true );
517 } else {
518 searchRange = editor.createRange();
519 searchRange.setStartAt( editable, CKEDITOR.POSITION_AFTER_START );
520 }
521 searchRange.setEndAt( editable, CKEDITOR.POSITION_BEFORE_END );
522 return searchRange;
523 }
524
525 var lang = editor.lang.find;
526 return {
527 title: lang.title,
528 resizable: CKEDITOR.DIALOG_RESIZE_NONE,
529 minWidth: 350,
530 minHeight: 170,
531 buttons: [
532 // Close button only.
533 CKEDITOR.dialog.cancelButton( editor, {
534 label: editor.lang.common.close
535 } )
536 ],
537 contents: [ {
538 id: 'find',
539 label: lang.find,
540 title: lang.find,
541 accessKey: '',
542 elements: [ {
543 type: 'hbox',
544 widths: [ '230px', '90px' ],
545 children: [ {
546 type: 'text',
547 id: 'txtFindFind',
548 label: lang.findWhat,
549 isChanged: false,
550 labelLayout: 'horizontal',
551 accessKey: 'F'
552 },
553 {
554 type: 'button',
555 id: 'btnFind',
556 align: 'left',
557 style: 'width:100%',
558 label: lang.find,
559 onClick: function() {
560 var dialog = this.getDialog();
561 if ( !finder.find(
562 dialog.getValueOf( 'find', 'txtFindFind' ),
563 dialog.getValueOf( 'find', 'txtFindCaseChk' ),
564 dialog.getValueOf( 'find', 'txtFindWordChk' ),
565 dialog.getValueOf( 'find', 'txtFindCyclic' )
566 ) ) {
567 alert( lang.notFoundMsg ); // jshint ignore:line
568 }
569 }
570 } ]
571 },
572 {
573 type: 'fieldset',
574 label: CKEDITOR.tools.htmlEncode( lang.findOptions ),
575 style: 'margin-top:29px',
576 children: [ {
577 type: 'vbox',
578 padding: 0,
579 children: [ {
580 type: 'checkbox',
581 id: 'txtFindCaseChk',
582 isChanged: false,
583 label: lang.matchCase
584 },
585 {
586 type: 'checkbox',
587 id: 'txtFindWordChk',
588 isChanged: false,
589 label: lang.matchWord
590 },
591 {
592 type: 'checkbox',
593 id: 'txtFindCyclic',
594 isChanged: false,
595 'default': true,
596 label: lang.matchCyclic
597 } ]
598 } ]
599 } ]
600 },
601 {
602 id: 'replace',
603 label: lang.replace,
604 accessKey: 'M',
605 elements: [ {
606 type: 'hbox',
607 widths: [ '230px', '90px' ],
608 children: [ {
609 type: 'text',
610 id: 'txtFindReplace',
611 label: lang.findWhat,
612 isChanged: false,
613 labelLayout: 'horizontal',
614 accessKey: 'F'
615 },
616 {
617 type: 'button',
618 id: 'btnFindReplace',
619 align: 'left',
620 style: 'width:100%',
621 label: lang.replace,
622 onClick: function() {
623 var dialog = this.getDialog();
624 if ( !finder.replace(
625 dialog,
626 dialog.getValueOf( 'replace', 'txtFindReplace' ),
627 dialog.getValueOf( 'replace', 'txtReplace' ),
628 dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
629 dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
630 dialog.getValueOf( 'replace', 'txtReplaceCyclic' )
631 ) ) {
632 alert( lang.notFoundMsg ); // jshint ignore:line
633 }
634 }
635 } ]
636 },
637 {
638 type: 'hbox',
639 widths: [ '230px', '90px' ],
640 children: [ {
641 type: 'text',
642 id: 'txtReplace',
643 label: lang.replaceWith,
644 isChanged: false,
645 labelLayout: 'horizontal',
646 accessKey: 'R'
647 },
648 {
649 type: 'button',
650 id: 'btnReplaceAll',
651 align: 'left',
652 style: 'width:100%',
653 label: lang.replaceAll,
654 isChanged: false,
655 onClick: function() {
656 var dialog = this.getDialog();
657
658 finder.replaceCounter = 0;
659
660 // Scope to full document.
661 finder.searchRange = getSearchRange( 1 );
662 if ( finder.matchRange ) {
663 finder.matchRange.removeHighlight();
664 finder.matchRange = null;
665 }
666 editor.fire( 'saveSnapshot' );
667 while ( finder.replace(
668 dialog,
669 dialog.getValueOf( 'replace', 'txtFindReplace' ),
670 dialog.getValueOf( 'replace', 'txtReplace' ),
671 dialog.getValueOf( 'replace', 'txtReplaceCaseChk' ),
672 dialog.getValueOf( 'replace', 'txtReplaceWordChk' ),
673 false,
674 true
675 ) ) {
676
677 }
678
679 if ( finder.replaceCounter ) {
680 alert( lang.replaceSuccessMsg.replace( /%1/, finder.replaceCounter ) ); // jshint ignore:line
681 editor.fire( 'saveSnapshot' );
682 } else {
683 alert( lang.notFoundMsg ); // jshint ignore:line
684 }
685 }
686 } ]
687 },
688 {
689 type: 'fieldset',
690 label: CKEDITOR.tools.htmlEncode( lang.findOptions ),
691 children: [ {
692 type: 'vbox',
693 padding: 0,
694 children: [ {
695 type: 'checkbox',
696 id: 'txtReplaceCaseChk',
697 isChanged: false,
698 label: lang.matchCase
699 },
700 {
701 type: 'checkbox',
702 id: 'txtReplaceWordChk',
703 isChanged: false,
704 label: lang.matchWord
705 },
706 {
707 type: 'checkbox',
708 id: 'txtReplaceCyclic',
709 isChanged: false,
710 'default': true,
711 label: lang.matchCyclic
712 } ]
713 } ]
714 } ]
715 } ],
716 onLoad: function() {
717 var dialog = this;
718
719 // Keep track of the current pattern field in use.
720 var patternField, wholeWordChkField;
721
722 // Ignore initial page select on dialog show
723 var isUserSelect = 0;
724 this.on( 'hide', function() {
725 isUserSelect = 0;
726 } );
727 this.on( 'show', function() {
728 isUserSelect = 1;
729 } );
730
731 this.selectPage = CKEDITOR.tools.override( this.selectPage, function( originalFunc ) {
732 return function( pageId ) {
733 originalFunc.call( dialog, pageId );
734
735 var currPage = dialog._.tabs[ pageId ];
736 var patternFieldInput, patternFieldId, wholeWordChkFieldId;
737 patternFieldId = pageId === 'find' ? 'txtFindFind' : 'txtFindReplace';
738 wholeWordChkFieldId = pageId === 'find' ? 'txtFindWordChk' : 'txtReplaceWordChk';
739
740 patternField = dialog.getContentElement( pageId, patternFieldId );
741 wholeWordChkField = dialog.getContentElement( pageId, wholeWordChkFieldId );
742
743 // Prepare for check pattern text filed 'keyup' event
744 if ( !currPage.initialized ) {
745 patternFieldInput = CKEDITOR.document.getById( patternField._.inputId );
746 currPage.initialized = true;
747 }
748
749 // Synchronize fields on tab switch.
750 if ( isUserSelect )
751 syncFieldsBetweenTabs.call( this, pageId );
752 };
753 } );
754
755 },
756 onShow: function() {
757 // Establish initial searching start position.
758 finder.searchRange = getSearchRange();
759
760 // Fill in the find field with selected text.
761 var selectedText = this.getParentEditor().getSelection().getSelectedText(),
762 patternFieldId = ( startupPage == 'find' ? 'txtFindFind' : 'txtFindReplace' );
763
764 var field = this.getContentElement( startupPage, patternFieldId );
765 field.setValue( selectedText );
766 field.select();
767
768 this.selectPage( startupPage );
769
770 this[ ( startupPage == 'find' && this._.editor.readOnly ? 'hide' : 'show' ) + 'Page' ]( 'replace' );
771 },
772 onHide: function() {
773 var range;
774 if ( finder.matchRange && finder.matchRange.isMatched() ) {
775 finder.matchRange.removeHighlight();
776 editor.focus();
777
778 range = finder.matchRange.toDomRange();
779 if ( range )
780 editor.getSelection().selectRanges( [ range ] );
781 }
782
783 // Clear current session before dialog close
784 delete finder.matchRange;
785 },
786 onFocus: function() {
787 if ( startupPage == 'replace' )
788 return this.getContentElement( 'replace', 'txtFindReplace' );
789 else
790 return this.getContentElement( 'find', 'txtFindFind' );
791 }
792 };
793 }
794
795 CKEDITOR.dialog.add( 'find', function( editor ) {
796 return findDialog( editor, 'find' );
797 } );
798
799 CKEDITOR.dialog.add( 'replace', function( editor ) {
800 return findDialog( editor, 'replace' );
801 } );
802 } )();