Files

229 lines
7.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
(() => {
if (typeof document === "undefined") return;
const $ = (s) => document.querySelector(s);
const addr = $("#addr");
const display = $("#display");
const exprEcho = $("#exprEcho");
const errorline = $("#errorline");
const suggestions = $("#suggestions");
const historyList = $("#historyList");
const statusVars = $("#statusVars");
const statusText = $("#statusText");
const library = $("#library");
const helpVeil = $("#helpVeil");
let ans = 0;
const history = [];
let navIdx = -1;
/* ---------- рендер ---------- */
function renderHistory() {
historyList.innerHTML = "";
if (!history.length) {
const li = document.createElement("li");
li.className = "history-empty";
li.textContent = "История пуста";
historyList.appendChild(li);
return;
}
for (let i = history.length - 1; i >= 0; i--) {
const h = history[i];
const li = document.createElement("li");
li.className = "history-item" + (h.ok ? "" : " bad");
const main = document.createElement("span");
main.className = "h-main";
main.textContent = h.expr;
const res = document.createElement("span");
res.className = "h-res";
res.textContent = h.ok ? "= " + h.result : "⚠";
li.append(main, res);
if (h.ok) li.addEventListener("click", () => { addr.value = h.expr; addr.focus(); });
historyList.appendChild(li);
}
}
function okResult(expr, value) {
exprEcho.textContent = expr + " =";
display.textContent = value;
display.classList.remove("is-err");
errorline.hidden = true;
statusVars.textContent = "ans = " + value;
statusText.textContent = "Готово";
}
function errResult(expr, msg) {
exprEcho.textContent = expr;
display.classList.add("is-err");
errorline.textContent = "Ошибка: " + msg;
errorline.hidden = false;
statusText.textContent = "Ошибка вычисления";
}
function resetSession() {
ans = 0;
history.length = 0;
navIdx = -1;
renderHistory();
exprEcho.innerHTML = " ";
display.textContent = "0";
display.classList.remove("is-err");
errorline.hidden = true;
statusVars.textContent = "ans = 0";
statusText.textContent = "Сессия сброшена (ans = 0, история пуста)";
}
/* ---------- вычисление ---------- */
function evaluateNow() {
const raw = addr.value.trim();
if (!raw) { addr.focus(); return; }
const low = raw.toLowerCase();
if (low === "exit" || low === "выход") {
statusText.textContent = "«Выход» — просто закройте вкладку 🙂";
addr.value = "";
return;
}
if (low === "reset") { resetSession(); addr.value = ""; return; }
try {
const v = Calc.evaluate(raw, { ans });
ans = v;
const text = Calc.format(v);
history.push({ expr: raw, result: text, ok: true });
okResult(raw, text);
} catch (e) {
history.push({ expr: raw, result: null, ok: false });
errResult(raw, e.message || String(e));
}
renderHistory();
addr.value = "";
navIdx = -1;
hideSuggestions();
addr.focus();
}
/* ---------- awesome bar ---------- */
function showSuggestions() {
const q = addr.value.trim().toLowerCase();
if (!q) { hideSuggestions(); return; }
const items = history
.filter((h) => h.expr.toLowerCase().includes(q))
.slice(-7)
.reverse();
if (!items.length) { hideSuggestions(); return; }
suggestions.innerHTML = "";
items.forEach((h) => {
const row = document.createElement("div");
row.className = "sugg";
const icon = document.createElement("span");
icon.className = "sugg-icon";
icon.textContent = h.ok ? "=" : "⚠";
const expr = document.createElement("span");
expr.className = "sugg-expr";
expr.textContent = h.expr;
const res = document.createElement("span");
res.className = "sugg-res";
res.textContent = h.ok ? "= " + h.result : "ошибка";
row.append(icon, expr, res);
row.addEventListener("mousedown", (ev) => {
ev.preventDefault();
addr.value = h.expr;
hideSuggestions();
addr.focus();
});
suggestions.appendChild(row);
});
suggestions.hidden = false;
}
function hideSuggestions() { suggestions.hidden = true; }
/* ---------- вставка с клавиатуры ---------- */
function insert(text) {
if (text === "back") {
const s = addr.selectionStart, e = addr.selectionEnd;
if (s !== e) {
addr.value = addr.value.slice(0, s) + addr.value.slice(e);
addr.selectionStart = addr.selectionEnd = s;
} else if (s > 0) {
addr.value = addr.value.slice(0, s - 1) + addr.value.slice(s);
addr.selectionStart = addr.selectionEnd = s - 1;
}
addr.focus();
showSuggestions();
return;
}
if (text === "C") { addr.value = ""; addr.focus(); hideSuggestions(); return; }
if (text === "CE") { resetSession(); return; }
const s = addr.selectionStart == null ? addr.value.length : addr.selectionStart;
const e = addr.selectionEnd == null ? s : addr.selectionEnd;
addr.value = addr.value.slice(0, s) + text + addr.value.slice(e);
addr.selectionStart = addr.selectionEnd = s + text.length;
addr.focus();
}
/* ---------- привязки ---------- */
document.querySelectorAll("[data-insert]").forEach((b) =>
b.addEventListener("click", () => insert(b.dataset.insert)));
document.querySelectorAll("[data-eval]").forEach((b) =>
b.addEventListener("click", evaluateNow));
$("#evalForm").addEventListener("submit", (ev) => { ev.preventDefault(); evaluateNow(); });
addr.addEventListener("input", showSuggestions);
addr.addEventListener("blur", () => setTimeout(hideSuggestions, 120));
addr.addEventListener("keydown", (ev) => {
if (ev.key === "Escape") { hideSuggestions(); return; }
const okList = history.filter((h) => h.ok);
if (ev.key === "ArrowUp" && okList.length) {
ev.preventDefault();
navIdx = Math.min(navIdx + 1, okList.length - 1);
addr.value = okList[okList.length - 1 - navIdx].expr;
return;
}
if (ev.key === "ArrowDown" && okList.length && navIdx >= 0) {
ev.preventDefault();
navIdx = Math.max(navIdx - 1, -1);
addr.value = navIdx < 0 ? "" : okList[okList.length - 1 - navIdx].expr;
return;
}
});
$("#btnReset").addEventListener("click", resetSession);
$("#btnHome").addEventListener("click", () => {
addr.value = "sqrt(3**2 + 4**2)";
evaluateNow();
});
/* ---------- библиотека (сайдбар) ---------- */
$("#btnLibrary").addEventListener("click", () => {
library.classList.toggle("hidden");
$("#btnLibrary").classList.toggle("active");
});
document.querySelectorAll(".lib-tab").forEach((tab) =>
tab.addEventListener("click", () => {
document.querySelectorAll(".lib-tab").forEach((t) => t.classList.remove("active"));
tab.classList.add("active");
$("#libHistory").hidden = tab.dataset.libtab !== "history";
$("#libFuncs").hidden = tab.dataset.libtab !== "funcs";
}));
/* ---------- справка ---------- */
$("#btnHelp").addEventListener("click", () => { helpVeil.hidden = false; });
$("#helpClose").addEventListener("click", () => { helpVeil.hidden = true; });
helpVeil.addEventListener("click", (ev) => { if (ev.target === helpVeil) helpVeil.hidden = true; });
document.addEventListener("keydown", (ev) => {
if (ev.key === "Escape") helpVeil.hidden = true;
});
renderHistory();
addr.focus();
})();