Merge pull request #1744 from msaby/replaceGUI
Add menu for searching and replacing
This commit is contained in:
commit
67a5ce15aa
@ -635,6 +635,7 @@
|
||||
"to-text": "To text",
|
||||
"blank-out": "To null",
|
||||
"blank-out-empty": "To empty string",
|
||||
"replace": "Replace",
|
||||
"fill-down": "Fill down",
|
||||
"blank-down": "Blank down",
|
||||
"split-cells": "Split multi-valued cells",
|
||||
@ -677,6 +678,15 @@
|
||||
"records": "records",
|
||||
"show": "Show",
|
||||
"hide": "Hide",
|
||||
"text-to-find": "Find:",
|
||||
"replacement-text": "Replace with:",
|
||||
"case-insensitive": "case insensitive",
|
||||
"whole-word": "whole word",
|
||||
"finding-info1": "Leave blank to add the remplacement string after each character.",
|
||||
"finding-info2": "Check \"regular expression\" to find special characters (new lines, tabulations...) or complex patterns.",
|
||||
"replacement-info": "If \"regular expression\" option is checked and finding pattern contains groups delimited with parentheses, $0 will return the complete string matching the pattern, and $1, $2... the 1st, 2d... group.",
|
||||
"replace-dont-escape": "use \\n for new lines, \\t for tabulation, \\\\n for \\n, \\\\t for \\t.",
|
||||
"warning-regex": "Invalid regular expression.",
|
||||
"use-values-as-identifiers": "Use values as identifiers",
|
||||
"use-values-as-identifiers2": "Mark cells as reconciled with their values as identifiers",
|
||||
"choose-reconciliation-service": "Choose a reconciliation service",
|
||||
|
@ -132,6 +132,154 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) {
|
||||
}
|
||||
};
|
||||
|
||||
var doReplace = function() {
|
||||
function isValidRegexPattern(p) {
|
||||
// check if a string can be used as a regexp pattern
|
||||
// parameters :
|
||||
// p : a string without beginning and trailing "/"
|
||||
|
||||
// we need a manual check for unescaped /
|
||||
// the GREL replace function cannot contain a pattern with unescaped /
|
||||
// but javascript Regexp accepts it and auto escape it
|
||||
var pos = p.replace(/\\\//g,'').indexOf("/");
|
||||
if (pos != -1) {
|
||||
alert($.i18n._('core-views')["warning-regex"] + " : " + p);
|
||||
return 0;}
|
||||
try {
|
||||
var pattern = new RegExp(p);
|
||||
return 1;
|
||||
} catch (e) {
|
||||
alert($.i18n._('core-views')["warning-regex"] + " : " + p);
|
||||
return 0;}
|
||||
}
|
||||
function escapeInputString(s) {
|
||||
// if the input is used as a plain string
|
||||
// user input | result after escaping
|
||||
// 4 characters : A\tA | 5 characters : A\\tA
|
||||
// 4 characters : A\nA | 5 characters : A\\nA
|
||||
// 1 characters : \ | 2 characters : \\
|
||||
// 1 character : ' | 2 characters : \'
|
||||
// 1 character : " | 2 characters : \"
|
||||
// new line | 2 characters : \n
|
||||
// tab | 2 characters : \t
|
||||
// parameters :
|
||||
// s : a string from an HTML input
|
||||
// Note: it is not possible to insert new lines in HTML input but it is safer to search for them, in case the input field is replaced with a textarea
|
||||
if (typeof s != 'string') {
|
||||
return "";
|
||||
}
|
||||
// convert new lines and tabs manually typed in the HTML input into \n and \t, delete other non printable characters, escape \ ' and "
|
||||
return s.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/[\x00-\x1F\x80-\x9F]/g, '').replace(/'/g, "\\'").replace(/"/g, '\\"')
|
||||
}
|
||||
function escapeInputRegex(s) {
|
||||
// if the HTML input is used as a regex pattern
|
||||
// suppress new lines and tabs manually typed in the HTML input, and other non printable characters
|
||||
// no need to escape \ or /
|
||||
// parameters :
|
||||
// s : a string from a HTML input
|
||||
return (typeof s == 'string') ? s.replace(/[\x00-\x1F\x80-\x9F]/g, '') : ""
|
||||
}
|
||||
function stringToRegex(s) {
|
||||
// converts a plain string to regex, in order to use the i flag
|
||||
// escaping is needed to force the regex processor to interpret every character litteraly
|
||||
// parameters :
|
||||
// s : a string from a HTML input, preprocessed with escapeInputString
|
||||
if (typeof s != 'string') {
|
||||
return "";
|
||||
}
|
||||
// no need to escape \ : already escaped by escapeInputString
|
||||
var EscapeCharsRegex = /[-|{}()[\]^$+*?.]/g;
|
||||
// FIXME escaping of / directly by adding / or \/ or // in EscapeCharsRegex don't work...
|
||||
return s.replace(EscapeCharsRegex, '\\$&').replace(/\//g, '\\/');
|
||||
}
|
||||
function escapeReplacementString(dont_escape,s) {
|
||||
// in replacement string, the GREL replace function handle in a specific way the Java special escape sequences
|
||||
// cf https://docs.oracle.com/javase/tutorial/java/data/characters.html
|
||||
// Escape Sequence | Java documentation | GREL replace function
|
||||
// \t | tab | tabs
|
||||
// \n | new line | new line
|
||||
// \r | carriage return | <blank>
|
||||
// \b | backspace | b
|
||||
// \f | formfeed | f
|
||||
// \' | ' | ''
|
||||
// \" | " | ""
|
||||
// \\ | \ | \
|
||||
// GREL replace function returns an error if a \ is not followed by an other character
|
||||
// it could be unexpected for the user, so the replace menu escape the replacement string, but gives the possibility to choose the default behavior
|
||||
// if dont_escape = 0, every \ are escaped as \\
|
||||
// if dont_escape = 1, \t and \n are not escaped, but \r, \b, \f are escaped as \\r, \\b, \\f
|
||||
// in both case, new lines and tabs manually typed in the input are converted into \n and \t, and non printable characters are deleted
|
||||
// parameters :
|
||||
// replace_dont_escape : 0 or 1
|
||||
// s : a string from a HTML input
|
||||
if (typeof s != 'string') {
|
||||
return "";
|
||||
}
|
||||
var temp = s;
|
||||
if (dont_escape == 0) {
|
||||
temp = temp.replace(/\\/g, '\\\\');
|
||||
}
|
||||
else {
|
||||
// replace "\n" with newline and "\t" with tab
|
||||
temp = temp.replace(/\\n/g, '\n').replace(/\\t/g, '\t');
|
||||
// replace "\" with "\\"
|
||||
temp = temp.replace(/\\/g, '\\\\');
|
||||
// replace "\newline" with "\n" and "\tab" with "\t"
|
||||
temp = temp.replace(/\\\n/g, '\\n').replace(/\\\t/g, '\\t');
|
||||
}
|
||||
// replace newline with "\n" and tab with "\t"
|
||||
return temp.replace(/\n/g, '\\n').replace(/\t/g, '\\t').replace(/[\x00-\x1F\x80-\x9F]/g,'');
|
||||
}
|
||||
var frame = $(DOM.loadHTML("core", "scripts/views/data-table/replace-dialog.html"));
|
||||
var elmts = DOM.bind(frame);
|
||||
elmts.dialogHeader.text($.i18n._('core-views')["replace"]);
|
||||
elmts.or_views_text_to_find.text($.i18n._('core-views')["text-to-find"]);
|
||||
elmts.or_views_replacement.text($.i18n._('core-views')["replacement-text"]);
|
||||
elmts.or_views_finding_info1.text($.i18n._('core-views')["finding-info1"]);
|
||||
elmts.or_views_finding_info2.text($.i18n._('core-views')["finding-info2"]);
|
||||
elmts.or_views_replacement_info.text($.i18n._('core-views')["replacement-info"]);
|
||||
elmts.or_views_find_regExp.text($.i18n._('core-views')["reg-exp"]);
|
||||
elmts.or_views_find_case_insensitive.text($.i18n._('core-views')["case-insensitive"]);
|
||||
elmts.or_views_find_whole_word.text($.i18n._('core-views')["whole-word"]);
|
||||
elmts.or_views_replace_dont_escape.text($.i18n._('core-views')["replace-dont-escape"]);
|
||||
elmts.okButton.html($.i18n._('core-buttons')["ok"]);
|
||||
elmts.cancelButton.text($.i18n._('core-buttons')["cancel"]);
|
||||
var level = DialogSystem.showDialog(frame);
|
||||
var dismiss = function() { DialogSystem.dismissUntil(level - 1); };
|
||||
elmts.cancelButton.click(dismiss);
|
||||
elmts.okButton.click(function() {
|
||||
var text_to_find = elmts.text_to_findInput[0].value;
|
||||
var replacement_text = elmts.replacement_textInput[0].value;
|
||||
var replace_dont_escape = elmts.replace_dont_escapeInput[0].checked;
|
||||
var find_regex = elmts.find_regexInput[0].checked;
|
||||
var find_case_insensitive = elmts.find_case_insensitiveInput[0].checked;
|
||||
var find_whole_word = elmts.find_whole_wordInput[0].checked;
|
||||
replacement_text = escapeReplacementString(replace_dont_escape, replacement_text)
|
||||
text_to_find = escapeInputString(text_to_find);
|
||||
if (!find_regex && (find_case_insensitive || find_whole_word)) {
|
||||
text_to_find = stringToRegex(text_to_find);
|
||||
}
|
||||
if (find_regex || find_case_insensitive || find_whole_word ) {
|
||||
if (!isValidRegexPattern (text_to_find)) {
|
||||
return;
|
||||
}
|
||||
if (find_whole_word) {
|
||||
text_to_find = "\\b"+text_to_find+"\\b";
|
||||
}
|
||||
text_to_find = "/"+text_to_find+"/";
|
||||
if (find_case_insensitive) {
|
||||
text_to_find = text_to_find+"i";
|
||||
}
|
||||
}
|
||||
else {
|
||||
text_to_find = '"'+text_to_find+'"';
|
||||
}
|
||||
expression = 'value.replace('+text_to_find+',"'+replacement_text+'")';
|
||||
doTextTransform(expression, "keep-original", false, "");
|
||||
dismiss();
|
||||
});
|
||||
};
|
||||
|
||||
var doSplitMultiValueCells = function() {
|
||||
|
||||
var frame = $(DOM.loadHTML("core", "scripts/views/data-table/split-multi-valued-cells-dialog.html"));
|
||||
@ -303,6 +451,12 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) {
|
||||
id: "core/cluster",
|
||||
label: $.i18n._('core-views')["cluster-edit"]+"...",
|
||||
click: function() { new ClusteringDialog(column.name, "value"); }
|
||||
},
|
||||
{},
|
||||
{
|
||||
id: "core/replace",
|
||||
label: $.i18n._('core-views')["replace"],
|
||||
click: doReplace
|
||||
}
|
||||
]);
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
<div class="dialog-frame" style="width: 600px;">
|
||||
<div class="dialog-border">
|
||||
<div class="dialog-header" bind="dialogHeader"></div>
|
||||
<div class="dialog-body" bind="dialogBody">
|
||||
<div class="grid-layout layout-looser layout-full">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="grid-layout layout-tighter">
|
||||
<table>
|
||||
<tr>
|
||||
<td width="20%">
|
||||
<label for="$text-to-find"><h3 bind="or_views_text_to_find"></h3></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="40" bind="text_to_findInput" id="$text-to-find"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="checkbox" bind="find_case_insensitiveInput" id="$find-case" />
|
||||
<label for="$find-case" bind="or_views_find_case_insensitive"></label>
|
||||
<input type="checkbox" bind="find_whole_wordInput" id="$find-whole-word" />
|
||||
<label for="$find-whole-word" bind="or_views_find_whole_word"></label>
|
||||
<input type="checkbox" bind="find_regexInput" id="$find-regex" />
|
||||
<label for="$find-regex" bind="or_views_find_regExp"></label>
|
||||
<p bind="or_views_finding_info1"></p>
|
||||
<p bind="or_views_finding_info2"></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2"> </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">
|
||||
<label for="$replacement-text"><h3 bind="or_views_replacement"></h3></label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" size="40" bind="replacement_textInput" id="$replacement-text"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="checkbox" bind="replace_dont_escapeInput" id="$replace-escape" />
|
||||
<label for="$replace-escape" bind="or_views_replace_dont_escape"></label>
|
||||
<p bind="or_views_replacement_info"></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog-footer" bind="dialogFooter">
|
||||
<button class="button" bind="okButton"></button>
|
||||
<button class="button" bind="cancelButton"></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user