/
vladlevin790
/
cpp-lab-2
Обзор
Документация
Войти
/
vladlevin790
/
cpp-lab-2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
web/js/app.js
376 строк
12 KB
vladlevin790
init
05 май 2026, 21:32
05 май 2026, 21:32
3310cbd
Код
Авторство
О чём код?
import { animateChangedSteps, renderStructure, replaySteps, resetCanvasView, setItemWindowProvider, zoomCanvas } from "./canvasRenderer.js"; import { buildAsciiPreviewFromState, canRunMean, canRunShift, formatMeanResult, getInsertLabel, getRemoveLabel, getValuePlaceholder } from "./dataTypes.js"; import { getElements } from "./dom.js"; import { initMagneticButtons, initParticles, initRevealAnimations, pulseElement, setCinemaMode } from "./effects.js"; import { initHeroDna } from "./heroDna.js"; import { getLanguage, initI18n, t, translateOperationMessage } from "./i18n.js"; import { renderAsciiPreview, renderOperationLog } from "./logView.js"; import { playAsciiSweep, playCanvasPing, playControlBurst, playHeroIntro, playResultPop } from "./moEffects.js"; import { createRecorderController } from "./recording.js"; import { loadTheme, refreshThemeLabel, toggleTheme } from "./theme.js"; import { showToast } from "./toast.js"; import { getItemsWindow, getLastOperation, getState, loadWasmModule } from "./wasmAdapter.js"; import { getIntegerFromInput, getTextFromInput, parseJson, shouldUseFlyAnimation } from "./utils.js"; const elements = getElements(); let Module = null; let lastOperation = null; let lastState = null; let cinemaEnabled = false; let recorder = null; function clearResult() { elements.resultValue.textContent = "—"; } function updateInfo(state) { if (!state) { return; } elements.structureName.textContent = state.structure; elements.dataTypeName.textContent = state.dataTypeLabel ?? state.dataType ?? "int"; elements.structureSize.textContent = String(state.size); elements.structureCapacity.textContent = String(state.capacity); } function updateActionLabels(state) { const structure = state?.structure ?? elements.structureSelect.value; elements.insertBtn.textContent = getInsertLabel(structure, getLanguage()); elements.removeBtn.textContent = getRemoveLabel(structure, getLanguage()); } function updateTypeControls(state) { const type = state?.dataType ?? elements.dataTypeSelect.value; const shiftAllowed = canRunShift(state); const meanAllowed = canRunMean(state); const shiftButtons = document.querySelectorAll('[data-action="shift"]'); const meanButtons = document.querySelectorAll('[data-action="mean"]'); elements.valueInput.placeholder = getValuePlaceholder(type, getLanguage()); elements.replaceValueInput.placeholder = getValuePlaceholder(type, getLanguage()); shiftButtons.forEach((button) => { button.disabled = !shiftAllowed; button.title = shiftAllowed ? "" : "Bit shift is available only for int or long"; }); meanButtons.forEach((button) => { button.disabled = !meanAllowed; button.title = meanAllowed ? "" : "Expected value is available only for int, long and double"; }); } function renderState(state, operation = null) { if (!state) { return; } lastState = state; if (operation) { lastOperation = operation; } updateInfo(state); updateActionLabels(state); updateTypeControls(state); renderStructure(elements.structureView, state, { useFlyAnimation: shouldUseFlyAnimation(operation, state.items?.length ?? 0) }); renderOperationLog(elements.operationLog, operation); renderAsciiPreview(elements.asciiOutput, buildAsciiPreviewFromState(state)); if (operation?.steps?.length) { animateChangedSteps(elements.structureView, operation.steps); } if (operation?.success) { playCanvasPing(elements.structureScene); } pulseElement(elements.visualDeck); } function refreshFromCpp() { renderState(getState(Module), getLastOperation(Module)); } function callStateOperation(callback) { if (!Module) { showToast(t("toast.wasmLoading"), false); return; } const state = parseJson(callback()); const operation = getLastOperation(Module); renderState(state, operation); if (operation?.message) { showToast(translateOperationMessage(operation), operation.success); } } function enableCinemaMode() { cinemaEnabled = true; setCinemaMode(true); elements.cinemaBtn.textContent = t("visual.cinemaOff"); } function disableCinemaMode() { cinemaEnabled = false; setCinemaMode(false); elements.cinemaBtn.textContent = t("visual.cinema"); } function toggleCinemaMode() { if (cinemaEnabled) { disableCinemaMode(); return; } enableCinemaMode(); elements.structureScene.scrollIntoView({ behavior: "smooth", block: "center" }); } function runShiftOperation() { clearResult(); callStateOperation(() => { return Module.applyShiftLeftEvenPositions(); }); } function runMeanOperation() { if (!Module) { showToast(t("toast.wasmLoading"), false); return; } const operation = parseJson(Module.calculateMeanJson()); elements.resultValue.textContent = formatMeanResult(operation?.result); playResultPop(elements.resultValue); renderState(getState(Module), operation); if (operation?.message) { showToast(translateOperationMessage(operation), operation.success); } } function bindEvents() { elements.themeToggleBtn.addEventListener("click", () => toggleTheme(elements)); elements.downloadVideoBtn.addEventListener("click", () => recorder.download()); elements.recordVideoBtn.addEventListener("click", () => recorder.toggle()); elements.cinemaBtn.addEventListener("click", toggleCinemaMode); elements.zoomOutBtn.addEventListener("click", () => zoomCanvas(elements.structureView, 0.82)); elements.zoomResetBtn.addEventListener("click", () => resetCanvasView(elements.structureView)); elements.zoomInBtn.addEventListener("click", () => zoomCanvas(elements.structureView, 1.18)); document.querySelectorAll('[data-action="shift"]').forEach((button) => { button.addEventListener("click", () => { playControlBurst(button, "#67e8f9"); runShiftOperation(); }); }); document.querySelectorAll('[data-action="mean"]').forEach((button) => { button.addEventListener("click", () => { playControlBurst(button, "#34d399"); runMeanOperation(); }); }); elements.structureSelect.addEventListener("change", () => { clearResult(); playControlBurst(elements.structureSelect, "#67e8f9"); callStateOperation(() => { return Module.setStructure(elements.structureSelect.value); }); }); elements.dataTypeSelect.addEventListener("change", () => { clearResult(); playControlBurst(elements.dataTypeSelect, "#facc15"); callStateOperation(() => { return Module.setDataType(elements.dataTypeSelect.value); }); }); elements.insertBtn.addEventListener("click", () => { clearResult(); const value = getTextFromInput(elements.valueInput, ""); playControlBurst(elements.insertBtn, "#8b5cf6"); callStateOperation(() => { return Module.insertValue(value); }); elements.valueInput.value = ""; elements.valueInput.focus(); }); elements.removeBtn.addEventListener("click", () => { clearResult(); playControlBurst(elements.removeBtn, "#fb7185"); callStateOperation(() => { return Module.removeLast(); }); }); elements.replaceBtn.addEventListener("click", () => { clearResult(); const index = getIntegerFromInput(elements.indexInput, 0); const value = getTextFromInput(elements.replaceValueInput, ""); playControlBurst(elements.replaceBtn, "#67e8f9"); callStateOperation(() => { return Module.replaceAt(index, value); }); }); elements.demoBtn.addEventListener("click", () => { clearResult(); const count = getIntegerFromInput(elements.demoCountInput, 12); playControlBurst(elements.demoBtn, "#facc15"); callStateOperation(() => { return Module.fillDemoData(count); }); }); elements.replayBtn.addEventListener("click", async () => { if (!lastOperation?.steps?.length) { showToast(t("toast.noReplay"), false); return; } playControlBurst(elements.replayBtn, "#67e8f9"); await replaySteps(elements.structureView, lastOperation.steps); showToast(t("toast.replayDone"), true); }); elements.asciiBtn.addEventListener("click", () => { if (!Module) { showToast(t("toast.wasmLoading"), false); return; } const preview = buildAsciiPreviewFromState(getState(Module)); const operation = { success: true, action: "ascii-preview", message: getLanguage() === "en" ? "ASCII preview has been rebuilt from the current structure." : "ASCII-превью обновлено по текущей структуре.", elapsedMicroseconds: 0, steps: [] }; renderAsciiPreview(elements.asciiOutput, preview); renderOperationLog(elements.operationLog, operation); playAsciiSweep(elements.asciiOutput); playResultPop(elements.asciiOutput); showToast(t("toast.asciiUpdated"), true); }); elements.clearBtn.addEventListener("click", () => { clearResult(); playControlBurst(elements.clearBtn, "#94a3b8"); callStateOperation(() => { return Module.clearCurrent(); }); }); elements.valueInput.addEventListener("keydown", (event) => { if (event.key === "Enter") { elements.insertBtn.click(); } }); window.addEventListener("beforeunload", () => { recorder.destroy(); }); } async function initWasm() { try { Module = await loadWasmModule(); elements.runtimePill.classList.add("active"); if (Module.__fallback) { elements.runtimePill.querySelector("span:last-child").textContent = "JS fallback"; } setItemWindowProvider(elements.structureView, (start, count) => getItemsWindow(Module, start, count)); bindEvents(); refreshFromCpp(); updateActionLabels(lastState); updateTypeControls(lastState); elements.valueInput.removeAttribute("readonly"); elements.valueInput.focus(); recorder.updateStatus(t("recording.idle")); showToast(t("toast.wasmReady"), true); } catch (error) { console.error(error); elements.runtimePill.classList.add("error"); elements.runtimePill.querySelector("span:last-child").textContent = "WASM error"; recorder.updateStatus(t("recording.wasmError"), "error"); showToast(t("toast.wasmFailed"), false); } } function initUi() { initI18n(elements, { onChange: () => { refreshThemeLabel(elements); updateActionLabels(lastState); updateTypeControls(lastState); renderOperationLog(elements.operationLog, lastOperation); renderAsciiPreview(elements.asciiOutput, buildAsciiPreviewFromState(lastState)); recorder?.updateStatus(t("recording.idle")); elements.cinemaBtn.textContent = cinemaEnabled ? t("visual.cinemaOff") : t("visual.cinema"); if (!document.body.classList.contains("recording-active")) { elements.recordVideoBtn.textContent = t("visual.record"); } if (elements.downloadVideoBtn.hidden) { elements.downloadVideoBtn.textContent = t("visual.download"); } } }); loadTheme(elements); initParticles(); initHeroDna(elements.heroDnaCanvas); initRevealAnimations(); initMagneticButtons(); playHeroIntro(); recorder = createRecorderController(elements, { showToast, enableCinemaMode, disableCinemaMode }); recorder.resetDownloadVideo(); } export function initApp() { initUi(); initWasm(); }