/**
* {@link XMLEditor} part used to search/replace text.
*/
class SearchReplace extends HTMLElement {
constructor() {
super();
this._options = "i"; // Default options. "i" = ignore case.
this._xmlEditor = null;
}
// -----------------------------------------------------------------------
// Custom element
// -----------------------------------------------------------------------
connectedCallback() {
if (this.firstChild === null) {
this.appendChild(SearchReplace.TEMPLATE.content.cloneNode(true));
this._searchedText = this.firstElementChild;
this._searchedText.onkeydown = this.onKeydownSearched.bind(this);
this._previousButton = this._searchedText.nextElementSibling;
this._previousButton.textContent = XUI.StockIcon["up-open"];
this._previousButton.onclick = this.findPreviousAction.bind(this);
this._nextButton = this._previousButton.nextElementSibling;
this._nextButton.textContent = XUI.StockIcon["down-open"];
this._nextButton.onclick = this.findNextAction.bind(this);
this._replacementText = this._nextButton.nextElementSibling;
this._replacementText.onkeydown =
this.onKeydownReplacement.bind(this);
this._replaceButton = this._replacementText.nextElementSibling;
this._replaceButton.textContent = XUI.StockIcon["replace"];
this._replaceButton.onclick = this.replaceNextAction.bind(this);
this._replaceAllButton = this._replaceButton.nextElementSibling;
this._replaceAllButton.textContent = XUI.StockIcon["replace-all"];
this._replaceAllButton.onclick = this.replaceAllAction.bind(this);
this._settingsButton = this._replaceAllButton.nextElementSibling;
this._settingsButton.textContent = XUI.StockIcon["settings"];
this._settingsButton.onclick = this._settingsButton.oncontextmenu =
this.setOptionsAction.bind(this);
this._settingsMenu = null;
let id = XUI.Util.uid();
this._searchHistory = XUI.Util.appendDatalist(id, [], this);
this._searchedText.setAttribute("list", id);
id = XUI.Util.uid();
this._replaceHistory = XUI.Util.appendDatalist(id, [], this);
this._replacementText.setAttribute("list", id);
}
// Otherwise, already connected.
}
// -----------------------------------------------------------------------
// Event handlers
// -----------------------------------------------------------------------
onKeydownSearched(event) {
if (event.key === "Enter") {
XUI.Util.consumeEvent(event);
this.findAction(/*previous*/ false);
}
}
findPreviousAction(event) {
XUI.Util.consumeEvent(event);
if (!event.target.classList.contains("xui-control-disabled")) {
this.findAction(true);
}
}
findNextAction(event) {
XUI.Util.consumeEvent(event);
if (!event.target.classList.contains("xui-control-disabled")) {
this.findAction(false);
}
}
findAction(previous) {
const searched = this._searchedText.value; // Do not trim.
if (searched.length === 0) {
return;
}
if (searched.trim().length > 0) {
XUI.Util.prependDatalistItem(this._searchHistory, searched);
}
const op = previous? "p" : "n";
this._xmlEditor.documentView.executeCommand(
EXECUTE_NORMAL, "textSearchReplace",
`${op}[${this._options}]${searched}`);
}
onKeydownReplacement(event) {
if (event.key === "Enter") {
XUI.Util.consumeEvent(event);
this.replaceAction(/*all*/ false);
}
}
replaceNextAction(event) {
XUI.Util.consumeEvent(event);
if (!event.target.classList.contains("xui-control-disabled")) {
this.replaceAction(false);
}
}
replaceAllAction(event) {
XUI.Util.consumeEvent(event);
if (!event.target.classList.contains("xui-control-disabled")) {
this.replaceAction(true);
}
}
replaceAction(all) {
const searched = this._searchedText.value; // Do not trim.
if (searched.length === 0) {
return;
}
if (searched.trim().length > 0) {
XUI.Util.prependDatalistItem(this._searchHistory, searched);
}
const replacement = this._replacementText.value; // May be empty.
if (replacement.trim().length > 0) {
XUI.Util.prependDatalistItem(this._replaceHistory, replacement);
}
const op = all? "a" : "r";
this._xmlEditor.documentView.executeCommand(
EXECUTE_NORMAL, "textSearchReplace",
`${op}[${this._options}]${searched}\n${replacement}`);
}
setOptionsAction(event) {
XUI.Util.consumeEvent(event);
if (this._settingsMenu === null) {
this._settingsMenu = XUI.Menu.create([
{ type: "checkbox",
text: "Ignore Case", name: "ignoreCase" },
{ type: "checkbox",
text: "Whole Word", name: "wholeWord" },
{ type: "checkbox",
text: "Regular Expression", name: "regularExpression" },
]);
this._settingsMenu.addEventListener("menuitemselected", (event) => {
this.toggleOption(event.xuiMenuItem.name);
});
}
let item = this._settingsMenu.getItem("ignoreCase");
item.setSelected(this._options.indexOf('i') >= 0);
item = this._settingsMenu.getItem("wholeWord");
item.setSelected(this._options.indexOf('w') >= 0);
item = this._settingsMenu.getItem("regularExpression");
item.setSelected(this._options.indexOf('r') >= 0);
this._settingsMenu.open("menu", this._settingsButton);
}
toggleOption(optionName) {
let option;
switch (optionName) {
case "ignoreCase":
case "wholeWord":
case "regularExpression":
option = optionName.charAt(0);
break;
default:
// Should not happen.
return;
}
let optionList = this._options.split('');
let index = optionList.indexOf(option);
if (index >= 0) {
optionList.splice(index, 1);
} else {
optionList.push(option);
}
this._options = optionList.join('');
}
// -----------------------------------------------------------------------
// Used by XMLEditor
// -----------------------------------------------------------------------
set xmlEditor(editor) {
this._xmlEditor = editor;
}
visiblityChanged(visible) {
this._searchedText.value = this._replacementText.value = "";
}
documentEdited(docUID, readOnlyDoc) {
let disable = (docUID === null);
for (let element of [ this._searchedText,
this._previousButton, this._nextButton ]) {
SearchReplace.setDisabled(element, disable);
}
disable = (disable || readOnlyDoc);
for (let element of [ this._replacementText,
this._replaceButton, this._replaceAllButton ]) {
SearchReplace.setDisabled(element, disable);
}
}
static setDisabled(element, disable) {
if (element.localName === "input") {
if (disable) {
element.setAttribute("disabled", "disabled");
} else {
element.removeAttribute("disabled");
}
} else {
if (disable) {
element.classList.add("xui-control-disabled");
} else {
element.classList.remove("xui-control-disabled");
}
}
}
}
SearchReplace.TEMPLATE = document.createElement("template");
SearchReplace.TEMPLATE.innerHTML = `
<input type="text" size="20" class="xui-control xxe-srt-searched"
spellcheck="false" autocomplete="off"
placeholder="Find what" />
<span class="xxe-tool-button xxe-srt-previous"
title="Find previous occurrence"></span>
<span class="xxe-tool-button xxe-srt-next"
title="Find next occurrence"></span>
<input type="text" size="20" class="xui-control xxe-srt-replacement"
spellcheck="false" autocomplete="off"
placeholder="Replace with" />
<span class="xxe-tool-button xxe-srt-replace"
title="Replace & find"></span>
<span class="xxe-tool-button xxe-srt-replace-all"
title="Replace all"></span>
<span class="xxe-tool-button xxe-srt-settings"
title="Search options"></span>
`;
window.customElements.define("xxe-search-replace", SearchReplace);