mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
Readded search bar and enhanced it.
- also added android hinting support
This commit is contained in:
+256
-198
@@ -635,18 +635,29 @@ var isSame;
|
||||
|
||||
|
||||
// This is from the file search.js-------------------------------------------------------------------------
|
||||
(function (search) {
|
||||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
// Define search commands. Depends on dialog.js or another
|
||||
// implementation of the openDialog method.
|
||||
|
||||
// Replace works a little oddly -- it will do the replace on the next
|
||||
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
||||
// replace by making sure the match is no longer selected when hitting
|
||||
// Ctrl-G.
|
||||
|
||||
(function(search) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
search(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
|
||||
define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], search);
|
||||
else // Plain browser env
|
||||
search(CodeMirror);
|
||||
})(function (CodeMirror) {
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
// default search panel location
|
||||
CodeMirror.defineOption("search", { bottom: false });
|
||||
CodeMirror.defineOption("search", {bottom: false});
|
||||
|
||||
function searchOverlay(query, caseInsensitive) {
|
||||
if (typeof query == "string")
|
||||
@@ -654,20 +665,18 @@ var isSame;
|
||||
else if (!query.global)
|
||||
query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
|
||||
|
||||
return {
|
||||
token: function (stream) {
|
||||
query.lastIndex = stream.pos;
|
||||
var match = query.exec(stream.string);
|
||||
if (match && match.index == stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return "searching";
|
||||
} else if (match) {
|
||||
stream.pos = match.index;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
return {token: function(stream) {
|
||||
query.lastIndex = stream.pos;
|
||||
var match = query.exec(stream.string);
|
||||
if (match && match.index == stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return "searching";
|
||||
} else if (match) {
|
||||
stream.pos = match.index;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
};
|
||||
}};
|
||||
}
|
||||
|
||||
function SearchState() {
|
||||
@@ -685,7 +694,7 @@ var isSame;
|
||||
|
||||
function getSearchCursor(cm, query, pos) {
|
||||
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
||||
return cm.getSearchCursor(query, pos, { caseFold: queryCaseInsensitive(query), multiline: true });
|
||||
return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
|
||||
}
|
||||
|
||||
function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
|
||||
@@ -693,14 +702,14 @@ var isSame;
|
||||
value: deflt,
|
||||
selectValueOnOpen: true,
|
||||
closeOnEnter: false,
|
||||
onClose: function () { clearSearch(cm); },
|
||||
onClose: function() { clearSearch(cm); },
|
||||
onKeyDown: onKeyDown,
|
||||
bottom: cm.options.search.bottom
|
||||
});
|
||||
}
|
||||
|
||||
function dialog(cm, text, shortText, deflt, f) {
|
||||
if (cm.openDialog) cm.openDialog(text, f, { value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom });
|
||||
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});
|
||||
else f(prompt(shortText, deflt));
|
||||
}
|
||||
|
||||
@@ -710,7 +719,7 @@ var isSame;
|
||||
}
|
||||
|
||||
function parseString(string) {
|
||||
return string.replace(/\\([nrt\\])/g, function (match, ch) {
|
||||
return string.replace(/\\([nrt\\])/g, function(match, ch) {
|
||||
if (ch == "n") return "\n"
|
||||
if (ch == "r") return "\r"
|
||||
if (ch == "t") return "\t"
|
||||
@@ -723,7 +732,7 @@ var isSame;
|
||||
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
||||
if (isRE) {
|
||||
try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
|
||||
catch (e) { } // Not a regular expression after all, do a string search
|
||||
catch(e) {} // Not a regular expression after all, do a string search
|
||||
} else {
|
||||
query = parseString(query)
|
||||
}
|
||||
@@ -751,7 +760,7 @@ var isSame;
|
||||
if (q instanceof RegExp && q.source == "x^") q = null
|
||||
if (persistent && cm.openDialog) {
|
||||
var hiding = null
|
||||
var searchNext = function (query, event) {
|
||||
var searchNext = function(query, event) {
|
||||
CodeMirror.e_stop(event);
|
||||
if (!query) return;
|
||||
if (query != state.queryText) {
|
||||
@@ -759,15 +768,15 @@ var isSame;
|
||||
state.posFrom = state.posTo = cm.getCursor();
|
||||
}
|
||||
if (hiding) hiding.style.opacity = 1
|
||||
findNext(cm, event.shiftKey, function (_, to) {
|
||||
findNext(cm, event.shiftKey, function(_, to) {
|
||||
var dialog
|
||||
if (to.line < 3 && document.querySelector &&
|
||||
(dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
|
||||
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
|
||||
(dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
|
||||
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
|
||||
(hiding = dialog).style.opacity = .4
|
||||
})
|
||||
};
|
||||
persistentDialog(cm, getQueryDialog(cm), q, searchNext, function (event, query) {
|
||||
persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
|
||||
var keyName = CodeMirror.keyName(event)
|
||||
var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
|
||||
if (cmd == "findNext" || cmd == "findPrev" ||
|
||||
@@ -785,8 +794,8 @@ var isSame;
|
||||
findNext(cm, rev);
|
||||
}
|
||||
} else {
|
||||
dialog(cm, getQueryDialog(cm), "Search for:", q, function (query) {
|
||||
if (query && !state.query) cm.operation(function () {
|
||||
dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
|
||||
if (query && !state.query) cm.operation(function() {
|
||||
startSearch(cm, state, query);
|
||||
state.posFrom = state.posTo = cm.getCursor();
|
||||
findNext(cm, rev);
|
||||
@@ -795,31 +804,44 @@ var isSame;
|
||||
}
|
||||
}
|
||||
|
||||
function findNext(cm, rev, callback) {
|
||||
cm.operation(function () {
|
||||
var state = getSearchState(cm);
|
||||
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
||||
if (!cursor.find(rev)) {
|
||||
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
||||
if (!cursor.find(rev)) return;
|
||||
}
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({ from: cursor.from(), to: cursor.to() }, 20);
|
||||
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
||||
if (callback) callback(cursor.from(), cursor.to())
|
||||
});
|
||||
}
|
||||
function findNext(cm, rev, callback) {
|
||||
cm.operation(function () {
|
||||
const state = getSearchState(cm);
|
||||
|
||||
function clearSearch(cm) {
|
||||
cm.operation(function () {
|
||||
var state = getSearchState(cm);
|
||||
state.lastQuery = state.query;
|
||||
if (!state.query) return;
|
||||
state.query = state.queryText = null;
|
||||
cm.removeOverlay(state.overlay);
|
||||
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||
// 🔸 Clear previous highlight
|
||||
if (state.lastHighlight) {
|
||||
state.lastHighlight.clear();
|
||||
state.lastHighlight = null;
|
||||
}
|
||||
|
||||
let cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
||||
if (!cursor.find(rev)) {
|
||||
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
||||
if (!cursor.find(rev)) return;
|
||||
}
|
||||
|
||||
// 🔸 Highlight current match
|
||||
const mark = cm.markText(cursor.from(), cursor.to(), {
|
||||
className: 'search-next-highlight'
|
||||
});
|
||||
}
|
||||
state.lastHighlight = mark;
|
||||
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({ from: cursor.from(), to: cursor.to() }, 20);
|
||||
state.posFrom = cursor.from();
|
||||
state.posTo = cursor.to();
|
||||
if (callback) callback(cursor.from(), cursor.to());
|
||||
});
|
||||
}
|
||||
|
||||
function clearSearch(cm) {cm.operation(function() {
|
||||
var state = getSearchState(cm);
|
||||
state.lastQuery = state.query;
|
||||
if (!state.query) return;
|
||||
state.query = state.queryText = null;
|
||||
cm.removeOverlay(state.overlay);
|
||||
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||
});}
|
||||
|
||||
function el(tag, attrs) {
|
||||
var element = tag ? document.createElement(tag) : document.createDocumentFragment();
|
||||
@@ -833,113 +855,159 @@ var isSame;
|
||||
return element;
|
||||
}
|
||||
|
||||
function getQueryDialog(cm) {
|
||||
var label = el("label", { className: "CodeMirror-search-label" },
|
||||
cm.phrase("Search:"),
|
||||
el("input", {
|
||||
type: "text", "style": "width: 10em", className: "CodeMirror-search-field",
|
||||
id: "CodeMirror-search-field"
|
||||
}));
|
||||
label.setAttribute("for", "CodeMirror-search-field");
|
||||
function getQueryDialog(cm) {
|
||||
var label = el("label", {className: "CodeMirror-search-label"},
|
||||
cm.phrase("Search:"),
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field",
|
||||
id: "CodeMirror-search-field"}));
|
||||
label.setAttribute("for","CodeMirror-search-field");
|
||||
return el("", null, label, " ",
|
||||
el("span", { style: "color: #666", className: "CodeMirror-search-hint" },
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
el("span", {style: "color: #666", className: "CodeMirror-search-hint"},
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
}
|
||||
function getReplaceQueryDialog(cm) {
|
||||
return el("", null, " ",
|
||||
el("input", { type: "text", "style": "width: 10em", className: "CodeMirror-search-field" }), " ",
|
||||
el("span", { style: "color: #666", className: "CodeMirror-search-hint" },
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ",
|
||||
el("span", {style: "color: #666", className: "CodeMirror-search-hint"},
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
}
|
||||
function getReplacementQueryDialog(cm) {
|
||||
return el("", null,
|
||||
el("span", { className: "CodeMirror-search-label" }, cm.phrase("With:")), " ",
|
||||
el("input", { type: "text", "style": "width: 10em", className: "CodeMirror-search-field" }));
|
||||
el("span", {className: "CodeMirror-search-label"}, cm.phrase("With:")), " ",
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}));
|
||||
}
|
||||
function getDoReplaceConfirm(cm) {
|
||||
return el("", null,
|
||||
el("span", { className: "CodeMirror-search-label" }, cm.phrase("Replace?")), " ",
|
||||
el("button", {}, cm.phrase("Yes")), " ",
|
||||
el("button", {}, cm.phrase("No")), " ",
|
||||
el("button", {}, cm.phrase("All")), " ",
|
||||
el("button", {}, cm.phrase("Stop")));
|
||||
el("span", {className: "CodeMirror-search-label"}, cm.phrase("Replace?")), " ",
|
||||
el("button", {}, cm.phrase("Yes")), " ",
|
||||
el("button", {}, cm.phrase("No")), " ",
|
||||
el("button", {}, cm.phrase("All")), " ",
|
||||
el("button", {}, cm.phrase("Stop")));
|
||||
}
|
||||
|
||||
function replaceAll(cm, query, text) {
|
||||
cm.operation(function () {
|
||||
cm.operation(function() {
|
||||
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
||||
if (typeof query != "string") {
|
||||
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
||||
cursor.replace(text.replace(/\$(\d)/g, function (_, i) { return match[i]; }));
|
||||
cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
|
||||
} else cursor.replace(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function replace(cm, all) {
|
||||
if (cm.getOption("readOnly")) return;
|
||||
var query = cm.getSelection() || getSearchState(cm).lastQuery;
|
||||
var dialogText = all ? cm.phrase("Replace all:") : cm.phrase("Replace:")
|
||||
var fragment = el("", null,
|
||||
el("span", { className: "CodeMirror-search-label" }, dialogText),
|
||||
getReplaceQueryDialog(cm))
|
||||
dialog(cm, fragment, dialogText, query, function (query) {
|
||||
if (!query) return;
|
||||
query = parseQuery(query);
|
||||
dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function (text) {
|
||||
text = parseString(text)
|
||||
if (all) {
|
||||
replaceAll(cm, query, text)
|
||||
} else {
|
||||
clearSearch(cm);
|
||||
var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
|
||||
var advance = function () {
|
||||
var start = cursor.from(), match;
|
||||
if (!(match = cursor.findNext())) {
|
||||
cursor = getSearchCursor(cm, query);
|
||||
if (!(match = cursor.findNext()) ||
|
||||
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
||||
}
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
|
||||
confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
|
||||
[function () { doReplace(match); }, advance,
|
||||
function () { replaceAll(cm, query, text) }]);
|
||||
};
|
||||
var doReplace = function (match) {
|
||||
cursor.replace(typeof query == "string" ? text :
|
||||
text.replace(/\$(\d)/g, function (_, i) { return match[i]; }));
|
||||
advance();
|
||||
};
|
||||
advance();
|
||||
}
|
||||
});
|
||||
if (cm.getOption("readOnly")) return;
|
||||
|
||||
let activeHighlight = null; // Will store the current highlight
|
||||
|
||||
function highlightCurrentMatch(from, to) {
|
||||
if (activeHighlight) {
|
||||
activeHighlight.clear();
|
||||
}
|
||||
activeHighlight = cm.markText(from, to, {
|
||||
className: 'search-next-highlight'
|
||||
});
|
||||
}
|
||||
|
||||
CodeMirror.commands.find = function (cm) { clearSearch(cm); doSearch(cm); };
|
||||
CodeMirror.commands.findPersistent = function (cm) { clearSearch(cm); doSearch(cm, false, true); };
|
||||
CodeMirror.commands.findPersistentNext = function (cm) { doSearch(cm, false, true, true); };
|
||||
CodeMirror.commands.findPersistentPrev = function (cm) { doSearch(cm, true, true, true); };
|
||||
function clearHighlight() {
|
||||
if (activeHighlight) {
|
||||
activeHighlight.clear();
|
||||
activeHighlight = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear highlight when dialog is removed (e.g., user presses Esc or clicks ❌)
|
||||
const observer = new MutationObserver(() => {
|
||||
if (!document.querySelector('.CodeMirror-dialog')) {
|
||||
clearHighlight();
|
||||
observer.disconnect();
|
||||
}
|
||||
});
|
||||
observer.observe(document.querySelector('.CodeMirror'), {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
var query = cm.getSelection() || getSearchState(cm).lastQuery;
|
||||
var dialogText = all ? cm.phrase("Replace all:") : cm.phrase("Replace:");
|
||||
var fragment = el("", null,
|
||||
el("span", { className: "CodeMirror-search-label" }, dialogText),
|
||||
getReplaceQueryDialog(cm));
|
||||
|
||||
dialog(cm, fragment, dialogText, query, function (query) {
|
||||
if (!query) return;
|
||||
query = parseQuery(query);
|
||||
|
||||
dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function (text) {
|
||||
text = parseString(text);
|
||||
|
||||
if (all) {
|
||||
clearHighlight();
|
||||
replaceAll(cm, query, text);
|
||||
} else {
|
||||
clearSearch(cm);
|
||||
var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
|
||||
|
||||
var advance = function () {
|
||||
var start = cursor.from(), match;
|
||||
|
||||
if (!(match = cursor.findNext())) {
|
||||
cursor = getSearchCursor(cm, query);
|
||||
if (!(match = cursor.findNext()) ||
|
||||
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
||||
}
|
||||
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
|
||||
highlightCurrentMatch(cursor.from(), cursor.to());
|
||||
|
||||
confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"), [
|
||||
function () { doReplace(match); },
|
||||
advance,
|
||||
function () {
|
||||
clearHighlight();
|
||||
replaceAll(cm, query, text);
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
var doReplace = function (match) {
|
||||
cursor.replace(typeof query == "string" ? text :
|
||||
text.replace(/\$(\d)/g, function (_, i) { return match[i]; }));
|
||||
advance();
|
||||
};
|
||||
|
||||
advance();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
||||
CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
|
||||
CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
|
||||
CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
|
||||
CodeMirror.commands.findNext = doSearch;
|
||||
CodeMirror.commands.findPrev = function (cm) { doSearch(cm, true); };
|
||||
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
||||
CodeMirror.commands.clearSearch = clearSearch;
|
||||
CodeMirror.commands.replace = replace;
|
||||
CodeMirror.commands.replaceAll = function (cm) { replace(cm, true); };
|
||||
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
||||
});
|
||||
|
||||
|
||||
|
||||
// This is from the file searchcursor.js-------------------------------------------------------------------------
|
||||
|
||||
(function (searchcursor) {
|
||||
|
||||
(function(searchcursor) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
searchcursor(require("../../lib/codemirror"))
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod)
|
||||
define(["../../lib/codemirror"], searchcursor)
|
||||
else // Plain browser env
|
||||
searchcursor(CodeMirror)
|
||||
})(function (CodeMirror) {
|
||||
})(function(CodeMirror) {
|
||||
"use strict"
|
||||
var Pos = CodeMirror.Pos
|
||||
|
||||
@@ -967,11 +1035,9 @@ var isSame;
|
||||
regexp.lastIndex = ch
|
||||
var string = doc.getLine(line), match = regexp.exec(string)
|
||||
if (match)
|
||||
return {
|
||||
from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match
|
||||
}
|
||||
return {from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -997,12 +1063,10 @@ var isSame;
|
||||
if (match) {
|
||||
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||
var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
|
||||
return {
|
||||
from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match
|
||||
}
|
||||
return {from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1028,11 +1092,9 @@ var isSame;
|
||||
var string = doc.getLine(line)
|
||||
var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
|
||||
if (match)
|
||||
return {
|
||||
from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match
|
||||
}
|
||||
return {from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1051,30 +1113,28 @@ var isSame;
|
||||
if (match) {
|
||||
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||
var startLine = line + before.length, startCh = before[before.length - 1].length
|
||||
return {
|
||||
from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match
|
||||
}
|
||||
return {from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var doFold, noFold
|
||||
if (String.prototype.normalize) {
|
||||
doFold = function (str) { return str.normalize("NFD").toLowerCase() }
|
||||
noFold = function (str) { return str.normalize("NFD") }
|
||||
doFold = function(str) { return str.normalize("NFD").toLowerCase() }
|
||||
noFold = function(str) { return str.normalize("NFD") }
|
||||
} else {
|
||||
doFold = function (str) { return str.toLowerCase() }
|
||||
noFold = function (str) { return str }
|
||||
doFold = function(str) { return str.toLowerCase() }
|
||||
noFold = function(str) { return str }
|
||||
}
|
||||
|
||||
// Maps a position in a case-folded line back to a position in the original line
|
||||
// (compensating for codepoints increasing in number during folding)
|
||||
function adjustPos(orig, folded, pos, foldFunc) {
|
||||
if (orig.length == folded.length) return pos
|
||||
for (var min = 0, max = pos + Math.max(0, orig.length - folded.length); ;) {
|
||||
for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
|
||||
if (min == max) return min
|
||||
var mid = (min + max) >> 1
|
||||
var len = foldFunc(orig.slice(0, mid)).length
|
||||
@@ -1097,10 +1157,8 @@ var isSame;
|
||||
var found = string.indexOf(lines[0])
|
||||
if (found == -1) continue search
|
||||
var start = adjustPos(orig, string, found, fold) + ch
|
||||
return {
|
||||
from: Pos(line, adjustPos(orig, string, found, fold) + ch),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)
|
||||
}
|
||||
return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
|
||||
} else {
|
||||
var cutFrom = string.length - lines[0].length
|
||||
if (string.slice(cutFrom) != lines[0]) continue search
|
||||
@@ -1108,10 +1166,8 @@ var isSame;
|
||||
if (fold(doc.getLine(line + i)) != lines[i]) continue search
|
||||
var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
|
||||
if (endString.slice(0, lastLine.length) != lastLine) continue search
|
||||
return {
|
||||
from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
|
||||
to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))
|
||||
}
|
||||
return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
|
||||
to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1128,10 +1184,8 @@ var isSame;
|
||||
if (lines.length == 1) {
|
||||
var found = string.lastIndexOf(lines[0])
|
||||
if (found == -1) continue search
|
||||
return {
|
||||
from: Pos(line, adjustPos(orig, string, found, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))
|
||||
}
|
||||
return {from: Pos(line, adjustPos(orig, string, found, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
|
||||
} else {
|
||||
var lastLine = lines[lines.length - 1]
|
||||
if (string.slice(0, lastLine.length) != lastLine) continue search
|
||||
@@ -1139,10 +1193,8 @@ var isSame;
|
||||
if (fold(doc.getLine(start + i)) != lines[i]) continue search
|
||||
var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
|
||||
if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
|
||||
return {
|
||||
from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, lastLine.length, fold))
|
||||
}
|
||||
return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1152,7 +1204,7 @@ var isSame;
|
||||
this.afterEmptyMatch = false
|
||||
this.doc = doc
|
||||
pos = pos ? doc.clipPos(pos) : Pos(0, 0)
|
||||
this.pos = { from: pos, to: pos }
|
||||
this.pos = {from: pos, to: pos}
|
||||
|
||||
var caseFold
|
||||
if (typeof options == "object") {
|
||||
@@ -1164,27 +1216,27 @@ var isSame;
|
||||
|
||||
if (typeof query == "string") {
|
||||
if (caseFold == null) caseFold = false
|
||||
this.matches = function (reverse, pos) {
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
|
||||
}
|
||||
} else {
|
||||
query = ensureFlags(query, "gm")
|
||||
if (!options || options.multiline !== false)
|
||||
this.matches = function (reverse, pos) {
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
|
||||
}
|
||||
else
|
||||
this.matches = function (reverse, pos) {
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchCursor.prototype = {
|
||||
findNext: function () { return this.find(false) },
|
||||
findPrevious: function () { return this.find(true) },
|
||||
findNext: function() {return this.find(false)},
|
||||
findPrevious: function() {return this.find(true)},
|
||||
|
||||
find: function (reverse) {
|
||||
find: function(reverse) {
|
||||
var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
|
||||
if (this.afterEmptyMatch && this.atOccurrence) {
|
||||
// do not return the same 0 width match twice
|
||||
@@ -1203,7 +1255,7 @@ var isSame;
|
||||
}
|
||||
}
|
||||
if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) {
|
||||
return this.atOccurrence = false
|
||||
return this.atOccurrence = false
|
||||
}
|
||||
}
|
||||
var result = this.matches(reverse, head)
|
||||
@@ -1215,36 +1267,36 @@ var isSame;
|
||||
return this.pos.match || true
|
||||
} else {
|
||||
var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
|
||||
this.pos = { from: end, to: end }
|
||||
this.pos = {from: end, to: end}
|
||||
return this.atOccurrence = false
|
||||
}
|
||||
},
|
||||
|
||||
from: function () { if (this.atOccurrence) return this.pos.from },
|
||||
to: function () { if (this.atOccurrence) return this.pos.to },
|
||||
from: function() {if (this.atOccurrence) return this.pos.from},
|
||||
to: function() {if (this.atOccurrence) return this.pos.to},
|
||||
|
||||
replace: function (newText, origin) {
|
||||
replace: function(newText, origin) {
|
||||
if (!this.atOccurrence) return
|
||||
var lines = CodeMirror.splitLines(newText)
|
||||
this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
|
||||
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
|
||||
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
|
||||
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("getSearchCursor", function (query, pos, caseFold) {
|
||||
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||
return new SearchCursor(this.doc, query, pos, caseFold)
|
||||
})
|
||||
CodeMirror.defineDocExtension("getSearchCursor", function (query, pos, caseFold) {
|
||||
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||
return new SearchCursor(this, query, pos, caseFold)
|
||||
})
|
||||
|
||||
CodeMirror.defineExtension("selectMatches", function (query, caseFold) {
|
||||
CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
|
||||
var ranges = []
|
||||
var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
|
||||
while (cur.findNext()) {
|
||||
if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
|
||||
ranges.push({ anchor: cur.from(), head: cur.to() })
|
||||
ranges.push({anchor: cur.from(), head: cur.to()})
|
||||
}
|
||||
if (ranges.length)
|
||||
this.setSelections(ranges, 0)
|
||||
@@ -1255,16 +1307,20 @@ var isSame;
|
||||
|
||||
// This is from the file dialog.js-------------------------------------------------------------------------
|
||||
|
||||
(function (dialog) {
|
||||
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
||||
|
||||
(function(dialog) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
dialog(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
define(["../../lib/codemirror"], dialog);
|
||||
else // Plain browser env
|
||||
dialog(CodeMirror);
|
||||
})(function (CodeMirror) {
|
||||
|
||||
})(function(CodeMirror) {
|
||||
function dialogDiv(cm, template, bottom) {
|
||||
var wrap = cm.getWrapperElement();
|
||||
//var wrap = cm.getWrapperElement(); changed by cxd to get searchfield out of div
|
||||
var wrap = document.body;
|
||||
var dialog;
|
||||
dialog = wrap.appendChild(document.createElement("div"));
|
||||
if (bottom)
|
||||
@@ -1277,7 +1333,9 @@ var isSame;
|
||||
} else { // Assuming it's a detached DOM element.
|
||||
dialog.appendChild(template);
|
||||
}
|
||||
// Append the dialog to <body> instead of inside the clipped container
|
||||
CodeMirror.addClass(wrap, 'dialog-opened');
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@@ -1287,7 +1345,7 @@ var isSame;
|
||||
cm.state.currentNotificationClose = newVal;
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("openDialog", function (template, callback, options) {
|
||||
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
||||
if (!options) options = {};
|
||||
|
||||
closeNotification(this, null);
|
||||
@@ -1320,11 +1378,11 @@ var isSame;
|
||||
}
|
||||
|
||||
if (options.onInput)
|
||||
CodeMirror.on(inp, "input", function (e) { options.onInput(e, inp.value, close); });
|
||||
CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
|
||||
if (options.onKeyUp)
|
||||
CodeMirror.on(inp, "keyup", function (e) { options.onKeyUp(e, inp.value, close); });
|
||||
CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
|
||||
|
||||
CodeMirror.on(inp, "keydown", function (e) {
|
||||
CodeMirror.on(inp, "keydown", function(e) {
|
||||
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
|
||||
if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
|
||||
inp.blur();
|
||||
@@ -1338,7 +1396,7 @@ var isSame;
|
||||
if (evt.relatedTarget !== null) close();
|
||||
});
|
||||
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
||||
CodeMirror.on(button, "click", function () {
|
||||
CodeMirror.on(button, "click", function() {
|
||||
close();
|
||||
me.focus();
|
||||
});
|
||||
@@ -1350,7 +1408,7 @@ var isSame;
|
||||
return close;
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("openConfirm", function (template, callbacks, options) {
|
||||
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
||||
closeNotification(this, null);
|
||||
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||
var buttons = dialog.getElementsByTagName("button");
|
||||
@@ -1365,18 +1423,18 @@ var isSame;
|
||||
buttons[0].focus();
|
||||
for (var i = 0; i < buttons.length; ++i) {
|
||||
var b = buttons[i];
|
||||
(function (callback) {
|
||||
CodeMirror.on(b, "click", function (e) {
|
||||
(function(callback) {
|
||||
CodeMirror.on(b, "click", function(e) {
|
||||
CodeMirror.e_preventDefault(e);
|
||||
close();
|
||||
if (callback) callback(me);
|
||||
});
|
||||
})(callbacks[i]);
|
||||
CodeMirror.on(b, "blur", function () {
|
||||
CodeMirror.on(b, "blur", function() {
|
||||
--blurring;
|
||||
setTimeout(function () { if (blurring <= 0) close(); }, 200);
|
||||
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
||||
});
|
||||
CodeMirror.on(b, "focus", function () { ++blurring; });
|
||||
CodeMirror.on(b, "focus", function() { ++blurring; });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1388,7 +1446,7 @@ var isSame;
|
||||
* If a notification is opened while another is opened, it will close the
|
||||
* currently opened one and open the new one immediately.
|
||||
*/
|
||||
CodeMirror.defineExtension("openNotification", function (template, options) {
|
||||
CodeMirror.defineExtension("openNotification", function(template, options) {
|
||||
closeNotification(this, close);
|
||||
var dialog = dialogDiv(this, template, options && options.bottom);
|
||||
var closed = false, doneTimer;
|
||||
@@ -1402,7 +1460,7 @@ var isSame;
|
||||
dialog.parentNode.removeChild(dialog);
|
||||
}
|
||||
|
||||
CodeMirror.on(dialog, 'click', function (e) {
|
||||
CodeMirror.on(dialog, 'click', function(e) {
|
||||
CodeMirror.e_preventDefault(e);
|
||||
close();
|
||||
});
|
||||
@@ -1412,4 +1470,4 @@ var isSame;
|
||||
|
||||
return close;
|
||||
});
|
||||
});
|
||||
});
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+16
-11
@@ -3,7 +3,7 @@
|
||||
--cmc1: black;
|
||||
--cmbg2: #eee;
|
||||
--cmc2: #999;
|
||||
--cmbd: 1px solid black;
|
||||
--cmbd: 1px solid black;
|
||||
--cmbg3: #d7d4f0;
|
||||
--cmbg4: #e8f2ff;
|
||||
--cmc3: #708;
|
||||
@@ -31,11 +31,11 @@
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--cmbg1: black;
|
||||
--cmc1: #e8d1a7;
|
||||
--cmbg1: black;
|
||||
--cmc1: #e8d1a7;
|
||||
--cmbg2: #212121;
|
||||
--cmc2: #e8d2a7;
|
||||
--cmbd: 1px solid #FFCC00;
|
||||
--cmbd: 1px solid #FFCC00;
|
||||
--cmbg3: #80cbc433;
|
||||
--cmbg4: #00000080;
|
||||
--cmc3: #ea6962;
|
||||
@@ -63,7 +63,7 @@
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme:dark) {
|
||||
[data-theme="auto"] {
|
||||
[data-theme="auto"] {
|
||||
--cmbg1: black;
|
||||
--cmc1: #e8d1a7;
|
||||
--cmbg2: #212121;
|
||||
@@ -93,7 +93,7 @@
|
||||
--cmbg8: #5f5f5f;
|
||||
--cmbg9: #5f5f5f;
|
||||
--cmc19: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* BASICS */
|
||||
@@ -637,7 +637,12 @@ div.CodeMirror-dragcursors {
|
||||
}
|
||||
|
||||
.cm-searching {
|
||||
background-color: rgba(162, 162, 162, 0.396);
|
||||
background-color: yellow;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.search-next-highlight {
|
||||
background-color: orange !important;
|
||||
}
|
||||
|
||||
/* Used to force a border model for a node */
|
||||
@@ -719,16 +724,16 @@ li.CodeMirror-hint-active {
|
||||
|
||||
.CodeMirror-dialog {
|
||||
position: fixed;
|
||||
height: 42px;
|
||||
/* height: 42px; */
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
background: inherit;
|
||||
z-index: 15;
|
||||
padding: .1em .8em;
|
||||
overflow: hidden;
|
||||
padding: .4em .8em;
|
||||
/* overflow: hidden;
|
||||
color: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: center; */
|
||||
}
|
||||
|
||||
.CodeMirror-dialog input::selection {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+304
-25
@@ -200,9 +200,9 @@ var EXTRAWORDS = commonAtoms.concat(commonPlugins, commonKeywords, commonCommand
|
||||
|
||||
var rEdit;
|
||||
var confirmR = true;
|
||||
var android = /Android/.test(navigator.userAgent);
|
||||
|
||||
function initCM() {
|
||||
var android = /Android/.test(navigator.userAgent);
|
||||
if (android) {
|
||||
if (confirm("Do you want to enable colored rules? (There are some issues with the standard Android Keyboard causing it to fail!)")) {
|
||||
confirmR = true
|
||||
@@ -230,25 +230,133 @@ function initCM() {
|
||||
'Shift-Tab': (cm) => cm.execCommand('indentLess'),
|
||||
}
|
||||
});
|
||||
|
||||
rEdit.on('change', function () { rEdit.save() });
|
||||
//hinting on input
|
||||
rEdit.on("inputRead", function (cm, event) {
|
||||
var letters = /[\w%,.]/; //characters for activation
|
||||
var cur = cm.getCursor();
|
||||
var token = cm.getTokenAt(cur);
|
||||
if (letters.test(event.text) && token.type != "comment") {
|
||||
cm.showHint({ completeSingle: false });
|
||||
};
|
||||
});
|
||||
|
||||
if (!android) {
|
||||
rEdit.on("inputRead", function (cm, event) {
|
||||
var letters = /[\w%,.]/; //characters for activation
|
||||
var cur = cm.getCursor();
|
||||
var token = cm.getTokenAt(cur);
|
||||
if (letters.test(event.text) && token.type != "comment") {
|
||||
cm.showHint({ completeSingle: false });
|
||||
};
|
||||
});
|
||||
}
|
||||
CodeMirror.keyMap.default["Ctrl-F"] = function (cm) {
|
||||
openFind(); // Inject your custom buttons
|
||||
};
|
||||
|
||||
CodeMirror.keyMap.default["Cmd-F"] = function (cm) {
|
||||
openFind();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------- add formatting option
|
||||
//----------------------------------------------------------------------- add search and formatting options
|
||||
|
||||
// Add Format button inside the form
|
||||
function closeSearchDialog() {
|
||||
const dlg = document.querySelectorAll('.CodeMirror-dialog');
|
||||
if (dlg.length > 0) {
|
||||
rEdit.execCommand('clearSearch');
|
||||
|
||||
// Remove the highlight classes from marked text spans
|
||||
const highlighted = document.querySelectorAll('.CodeMirror .search-next-highlight');
|
||||
highlighted.forEach(el => el.classList.remove('search-next-highlight'));
|
||||
|
||||
dlg.forEach(d => d.remove());
|
||||
|
||||
document.body.classList.remove('dialog-opened');
|
||||
}
|
||||
}
|
||||
|
||||
function openFind() {
|
||||
closeSearchDialog(); // Close any existing search dialog
|
||||
rEdit.execCommand('findPersistent'); // Show search dialog
|
||||
|
||||
|
||||
const element = document.querySelector('.CodeMirror-selected');
|
||||
const dialog = document.querySelector('.CodeMirror-dialog');
|
||||
if (!dialog || dialog.querySelector('.search-button-group')) return;
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
title: 'Find Previous',
|
||||
symbol: '▲',
|
||||
action: () => rEdit.execCommand('findPersistentPrev')
|
||||
},
|
||||
{
|
||||
title: 'Find Next',
|
||||
symbol: '▼',
|
||||
action: () => rEdit.execCommand('findPersistentNext')
|
||||
},
|
||||
{
|
||||
title: 'Replace',
|
||||
symbol: 'Replace',
|
||||
action: () => {
|
||||
closeSearchDialog();
|
||||
rEdit.execCommand('replace');
|
||||
openFind();
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Close',
|
||||
symbol: '❌',
|
||||
action: closeSearchDialog
|
||||
},
|
||||
{
|
||||
title: 'Help',
|
||||
symbol: '?',
|
||||
action: () => {
|
||||
alert(`Available shortcuts:
|
||||
• Ctrl+F / Cmd+F: Open search
|
||||
• Ctrl+G / Cmd+G: Find next
|
||||
• Shift+Ctrl+G / Shift+Cmd+G: Find previous
|
||||
• Shift+Ctrl+F / Cmd+Option+F: Replace
|
||||
• Shift+Ctrl+R / Shift+Cmd+Option+F: Replace all
|
||||
• Use /re/ syntax for regexp search`);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
buttons.forEach(({ title, symbol, action }) => {
|
||||
const btn = document.createElement('span');
|
||||
btn.title = title;
|
||||
btn.className = title.toLowerCase() === 'help' ? 'button help' : 'button';
|
||||
btn.innerHTML = symbol;
|
||||
btn.style.cssText = `
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
`;
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
action();
|
||||
});
|
||||
dialog.appendChild(btn);
|
||||
});
|
||||
}
|
||||
|
||||
// Add Searchfield button
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const form = document.getElementById('rulesselect');
|
||||
if (form) {
|
||||
// Add search help button to CodeMirror editor
|
||||
const btn3 = document.createElement('button');
|
||||
btn3.type = 'button'; // prevent form submission
|
||||
btn3.id = 'searchBtn';
|
||||
btn3.innerHTML = "🔎︎"; // magnifier
|
||||
btn3.style.padding = "2px 5px";
|
||||
btn3.className = 'button help'; // just the class, no inline style
|
||||
form.appendChild(btn3);
|
||||
|
||||
|
||||
btn3.addEventListener('click', () => {
|
||||
if (typeof rEdit !== 'undefined') {
|
||||
openFind();
|
||||
}
|
||||
});
|
||||
|
||||
// Add format document button
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button'; // prevent form submission
|
||||
btn.id = 'formatBtn';
|
||||
@@ -262,30 +370,157 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Add hint button to CodeMirror editor
|
||||
// const form2 = document.getElementsByClassName('CodeMirror')[0];
|
||||
// if (form2) {
|
||||
// console.log('Form2 found', form2);
|
||||
// const btn2 = document.createElement('button');
|
||||
// btn2.type = 'button'; // prevent form submission
|
||||
// btn2.id = 'hintBtn';
|
||||
// btn2.innerHTML = "☰︎"; // ☰︎
|
||||
// btn2.className = 'button help';
|
||||
// btn2.style.position = 'absolute';
|
||||
// btn2.style.right = '0';
|
||||
// btn2.style.top = '0';
|
||||
|
||||
// form2.appendChild(btn2);
|
||||
|
||||
// btn2.addEventListener('click', () => {
|
||||
// if (typeof rEdit !== 'undefined') {
|
||||
// rEdit.focus(); // Optional: ensure focus
|
||||
// rEdit.execCommand('autocomplete'); // Show hint menu
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
let charBuffer = "";
|
||||
|
||||
// Global keydown handler
|
||||
document.addEventListener('keydown', function (e) {
|
||||
// Ctrl + Shift + F triggers formatting
|
||||
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === 'f') {
|
||||
e.preventDefault();
|
||||
console.log('Keyboard shortcut detected: Formatting...');
|
||||
triggerFormatting();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear buffer on navigation/editing/meta keys
|
||||
const ignoreKeys = [
|
||||
"Backspace", "Delete", "ArrowLeft", "ArrowRight",
|
||||
"ArrowUp", "ArrowDown", "Enter", "Tab", "Escape",
|
||||
"Shift", "Control", "Alt", "Meta"
|
||||
];
|
||||
|
||||
if (ignoreKeys.includes(e.key) || e.key.length !== 1) {
|
||||
charBuffer = "";
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// Workaround of showing hints for Android devices
|
||||
if (android) {
|
||||
document.addEventListener("input", (e) => {
|
||||
if (!rEdit || !e.data || e.data.length !== 1) {
|
||||
charBuffer = "";
|
||||
return;
|
||||
}
|
||||
|
||||
const data = e.data;
|
||||
const doc = rEdit.getDoc();
|
||||
let cursor = doc.getCursor(); // Cursor is AFTER inserted char
|
||||
|
||||
const letters = /[\w%,.]/;
|
||||
const token = rEdit.getTokenAt(cursor);
|
||||
if (letters.test(data) && token.type !== "comment") {
|
||||
charBuffer += data;
|
||||
// Calculate starting point for re-inserting
|
||||
const insertPos = {
|
||||
line: cursor.line,
|
||||
ch: cursor.ch - charBuffer.length + 1
|
||||
};
|
||||
|
||||
// Replace from where the buffer began to current position
|
||||
doc.replaceRange(charBuffer, insertPos, cursor);
|
||||
|
||||
// Set cursor at end of buffer
|
||||
rEdit.setCursor({
|
||||
line: insertPos.line,
|
||||
ch: insertPos.ch + charBuffer.length
|
||||
});
|
||||
|
||||
// Show hints
|
||||
rEdit.showHint({ completeSingle: false });
|
||||
} else if (!letters.test(data)) {
|
||||
// Reset buffer on non-matching input (e.g., space, enter, etc.)
|
||||
charBuffer = "";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
rEdit.on('endCompletion', function () {
|
||||
setTimeout(() => {
|
||||
rEdit.focus();
|
||||
}, 10); // small delay may help
|
||||
});
|
||||
|
||||
function forceKeyboardOpen() {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.style.position = "absolute";
|
||||
input.style.opacity = "0";
|
||||
input.style.height = "0";
|
||||
input.style.width = "0";
|
||||
input.style.border = "none";
|
||||
input.style.top = "0";
|
||||
input.style.left = "-9999";
|
||||
input.style.padding = "0";
|
||||
input.style.zIndex = "-1";
|
||||
input.style.fontSize = "16px"; // prevents zoom on iOS
|
||||
|
||||
document.body.appendChild(input);
|
||||
input.focus();
|
||||
|
||||
setTimeout(() => {
|
||||
input.remove();
|
||||
rEdit.focus(); // bring focus back to CodeMirror
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Function to trigger formatting
|
||||
function triggerFormatting() {
|
||||
let textarea;
|
||||
if (confirmR) {
|
||||
textarea = rEdit.getValue();
|
||||
} else {
|
||||
textarea = document.getElementById('rules').value;
|
||||
}
|
||||
const doc = rEdit.getDoc();
|
||||
const scrollInfo = rEdit.getScrollInfo();
|
||||
const cursor = doc.getCursor();
|
||||
|
||||
const currentLine = cursor.ch === 0 ? cursor.line - 1 : cursor.line;
|
||||
const lineText = rEdit.getLine(currentLine) || "";
|
||||
|
||||
// Store initial text
|
||||
let textarea = confirmR
|
||||
? rEdit.getValue()
|
||||
: document.getElementById('rules').value;
|
||||
|
||||
// Apply transformations
|
||||
textarea = initalAutocorrection(textarea);
|
||||
textarea = formatLogic(textarea);
|
||||
|
||||
if (confirmR) {
|
||||
rEdit.setValue(textarea);
|
||||
|
||||
// Compute new cursor position
|
||||
const newLine = currentLine;
|
||||
const newCh = cursor.ch === 0 && lineText.length > 0
|
||||
? lineText.length
|
||||
: cursor.ch - 1;
|
||||
|
||||
rEdit.setCursor({ line: newLine, ch: newCh });
|
||||
rEdit.scrollTo(scrollInfo.left, scrollInfo.top);
|
||||
rEdit.focus();
|
||||
rEdit.save();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
document.getElementById('rules').value = textarea;
|
||||
}
|
||||
}
|
||||
@@ -350,6 +585,10 @@ function formatLogic(text) {
|
||||
return line.trim().toLowerCase() === 'else';
|
||||
}
|
||||
|
||||
function isElseif(line) {
|
||||
return line.trim().toLowerCase().startsWith('elseif');
|
||||
}
|
||||
|
||||
function isEndif(line) {
|
||||
return line.trim().toLowerCase() === 'endif';
|
||||
}
|
||||
@@ -426,6 +665,7 @@ function formatLogic(text) {
|
||||
}
|
||||
|
||||
if (isElse(trimmed)) {
|
||||
console.log("Else found:", trimmed);
|
||||
if (currentIfStack.length === 0) {
|
||||
errors.push(`• Line ${i + 1}: "Else" without matching "If"`);
|
||||
} else {
|
||||
@@ -437,6 +677,19 @@ function formatLogic(text) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isElseif(trimmed)) {
|
||||
console.log("Elseif found:", trimmed);
|
||||
if (currentIfStack.length === 0) {
|
||||
errors.push(`• Line ${i + 1}: "Elseif" without matching "If"`);
|
||||
} else {
|
||||
indentLevel = Math.max(indentLevel - 1, 0);
|
||||
}
|
||||
|
||||
result.push(INDENT.repeat(indentLevel) + trimmed);
|
||||
indentLevel++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isEndif(trimmed)) {
|
||||
if (currentIfStack.length === 0) {
|
||||
errors.push(`• Line ${i + 1}: "Endif" without matching "If"`);
|
||||
@@ -468,10 +721,17 @@ function formatLogic(text) {
|
||||
if (errors.length > 0) {
|
||||
const firstErrorLine = extractFirstErrorLine(errors);
|
||||
alert("Errors found:\n" + errors.join('\n'));
|
||||
if (!isNaN(firstErrorLine) && confirmR) {
|
||||
setTimeout(() => {
|
||||
jumpToLine(firstErrorLine);
|
||||
}, 50);
|
||||
if (!isNaN(firstErrorLine)) {
|
||||
if (confirmR) {
|
||||
setTimeout(() => {
|
||||
jumpToLine(firstErrorLine);
|
||||
}, 50);
|
||||
} else {
|
||||
const textareaR = document.getElementById('rules');
|
||||
setTimeout(() => {
|
||||
jumpToLineInTextarea(textareaR, firstErrorLine);
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +761,25 @@ function extractFirstErrorLine(errors) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function jumpToLineInTextarea(textarea, lineNumber) {
|
||||
const lines = textarea.value.split('\n');
|
||||
const clampedLine = Math.max(1, Math.min(lineNumber, lines.length)); // Clamp to valid range
|
||||
|
||||
// Calculate the character offset to the start of the target line
|
||||
let offset = 0;
|
||||
for (let i = 0; i < clampedLine - 1; i++) {
|
||||
offset += lines[i].length + 1; // +1 for the newline character
|
||||
}
|
||||
|
||||
// Move cursor and scroll into view
|
||||
textarea.focus();
|
||||
textarea.selectionStart = textarea.selectionEnd = offset;
|
||||
|
||||
// Scroll to the selection
|
||||
textarea.scrollTop = textarea.scrollHeight; // Jump to bottom
|
||||
textarea.scrollTop = textarea.scrollTop - textarea.clientHeight / 2; // Center around selection
|
||||
}
|
||||
//--------------------------------------------------------------------------------- end of formatting option
|
||||
|
||||
(function (mod) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user