(bug 20919) Search & Replace: Change "Replace Next" functionality to "Replace" functionality. Patch by Amir E. Aharoni.

Submitter's comment: "Replace next" now replaces the currently selected text instead of finding the next occurrence and replacing it. To keep the location of the current occurrence, i added matchIndex to $(this).data.

I added some comments renamed a couple of variable for readability:
* s to textRemainder
* replace to actualReplacement

This is my first significant jQuery-style patch so it may have embarrassing mistakes - constructive criticism is welcome.
This commit is contained in:
Siebrand Mazeland 2011-09-13 08:53:12 +00:00
parent 7dc0dec3c3
commit 868eec7504
Notes: Siebrand Mazeland 2011-09-13 08:53:12 +00:00

View file

@ -963,24 +963,31 @@ getDefaultConfig: function () {
// TODO: Find a cleaner way to share this function // TODO: Find a cleaner way to share this function
$(this).data( 'replaceCallback', function( mode ) { $(this).data( 'replaceCallback', function( mode ) {
$( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide(); $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
// Search string cannot be empty
var searchStr = $( '#wikieditor-toolbar-replace-search' ).val(); var searchStr = $( '#wikieditor-toolbar-replace-search' ).val();
if ( searchStr == '' ) { if ( searchStr == '' ) {
$( '#wikieditor-toolbar-replace-emptysearch' ).show(); $( '#wikieditor-toolbar-replace-emptysearch' ).show();
return; return;
} }
// Replace string can be empty
var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val(); var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val();
// Prepare the regular expression flags
var flags = 'm'; var flags = 'm';
var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' ); var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
if ( !matchCase ) { if ( !matchCase ) {
flags += 'i'; flags += 'i';
} }
var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
if ( !isRegex ) {
searchStr = $.escapeRE( searchStr );
}
if ( mode == 'replaceAll' ) { if ( mode == 'replaceAll' ) {
flags += 'g'; flags += 'g';
} }
if ( !isRegex ) {
searchStr = $.escapeRE( searchStr );
}
try { try {
var regex = new RegExp( searchStr, flags ); var regex = new RegExp( searchStr, flags );
} catch( e ) { } catch( e ) {
@ -990,20 +997,26 @@ getDefaultConfig: function () {
.show(); .show();
return; return;
} }
var $textarea = $(this).data( 'context' ).$textarea; var $textarea = $(this).data( 'context' ).$textarea;
var text = $textarea.textSelection( 'getContents' ); var text = $textarea.textSelection( 'getContents' );
var match = false; var match = false;
var offset, s; var offset, textRemainder;
if ( mode != 'replaceAll' ) { if ( mode != 'replaceAll' ) {
offset = $(this).data( 'offset' ); if (mode == 'replace') {
s = text.substr( offset ); offset = $(this).data( 'matchIndex' );
match = s.match( regex ); } else {
offset = $(this).data( 'offset' );
}
textRemainder = text.substr( offset );
match = textRemainder.match( regex );
} }
if ( !match ) { if ( !match ) {
// Search hit BOTTOM, continuing at TOP // Search hit BOTTOM, continuing at TOP
// TODO: Add a "Wrap around" option.
offset = 0; offset = 0;
s = text; textRemainder = text;
match = s.match( regex ); match = textRemainder.match( regex );
} }
if ( !match ) { if ( !match ) {
@ -1017,13 +1030,13 @@ getDefaultConfig: function () {
// in Firefox/Webkit, but in IE replacing the entire content once is better. // in Firefox/Webkit, but in IE replacing the entire content once is better.
var index; var index;
for ( var i = 0; i < match.length; i++ ) { for ( var i = 0; i < match.length; i++ ) {
index = s.indexOf( match[i] ); index = textRemainder.indexOf( match[i] );
if ( index == -1 ) { if ( index == -1 ) {
// This shouldn't happen // This shouldn't happen
break; break;
} }
var matchedText = s.substr( index, match[i].length ); var matchedText = textRemainder.substr( index, match[i].length );
s = s.substr( index + match[i].length ); textRemainder = textRemainder.substr( index + match[i].length );
var start = index + offset; var start = index + offset;
var end = start + match[i].length; var end = start + match[i].length;
@ -1043,27 +1056,63 @@ getDefaultConfig: function () {
.show(); .show();
$(this).data( 'offset', 0 ); $(this).data( 'offset', 0 );
} else { } else {
// Make regex placeholder substitution ($1) work var start, end;
var replace = isRegex ? match[0].replace( regex, replaceStr ): replaceStr;
var start = match.index + offset;
var end = start + match[0].length;
var newEnd = start + replace.length;
var context = $( this ).data( 'context' );
$textarea.textSelection( 'setSelection', { 'start': start,
'end': end } );
if ( mode == 'replace' ) { if ( mode == 'replace' ) {
$textarea var actualReplacement;
.textSelection( 'encapsulateSelection', {
'peri': replace, if (isRegex) {
'replace': true } ) // If backreferences (like $1) are used, the actual actual replacement string will be different
.textSelection( 'setSelection', { actualReplacement = match[0].replace( regex, replaceStr );
'start': start, } else {
'end': newEnd } ); actualReplacement = replaceStr;
}
if (match) {
// Do the replacement
$textarea.textSelection( 'encapsulateSelection', {
'peri': actualReplacement,
'replace': true } );
// Reload the text after replacement
text = $textarea.textSelection( 'getContents' );
}
// Find the next instance
offset = offset + match[0].length + actualReplacement.length;
textRemainder = text.substr( offset );
match = textRemainder.match( regex );
if (match) {
start = offset + match.index;
end = start + match[0].length;
} else {
// If no new string was found, try searching from the beginning.
// TODO: Add a "Wrap around" option.
textRemainder = text;
match = textRemainder.match( regex );
if (match) {
start = match.index;
end = start + match[0].length;
} else {
// Give up
start = 0;
end = 0;
}
}
} else {
start = offset + match.index;
end = start + match[0].length;
} }
$( this ).data( 'matchIndex', start);
$textarea.textSelection( 'setSelection', {
'start': start,
'end': end
} );
$textarea.textSelection( 'scrollToCaretPosition' ); $textarea.textSelection( 'scrollToCaretPosition' );
$textarea.textSelection( 'setSelection', { 'start': start, $( this ).data( 'offset', end );
'end': mode == 'replace' ? newEnd : end } ); var context = $( this ).data( 'context' );
$( this ).data( 'offset', mode == 'replace' ? newEnd : end );
var textbox = typeof context.$iframe != 'undefined' ? var textbox = typeof context.$iframe != 'undefined' ?
context.$iframe[0].contentWindow : $textarea[0]; context.$iframe[0].contentWindow : $textarea[0];
textbox.focus(); textbox.focus();
@ -1092,6 +1141,8 @@ getDefaultConfig: function () {
}, },
open: function() { open: function() {
$(this).data( 'offset', 0 ); $(this).data( 'offset', 0 );
$(this).data( 'matchIndex', 0 );
$( '#wikieditor-toolbar-replace-search' ).focus(); $( '#wikieditor-toolbar-replace-search' ).focus();
$( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide(); $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
if ( !( $(this).data( 'onetimeonlystuff' ) ) ) { if ( !( $(this).data( 'onetimeonlystuff' ) ) ) {