new ckeditor
New ckeditor
This commit is contained in:
824
ckeditor/plugins/find/dialogs/find.js
Normal file
824
ckeditor/plugins/find/dialogs/find.js
Normal file
@@ -0,0 +1,824 @@
|
||||
/**
|
||||
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/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: (#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 (#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. (#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.(#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). (#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. (#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. (#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 (#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' );
|
||||
} );
|
||||
} )();
|
||||
Reference in New Issue
Block a user