Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 93 additions & 163 deletions httpd/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web lab1</title>
<script src="./js/ajax.js"></script>
<link rel="icon" href="assets/img/logo.ico" type="image/x-icon"/>
<title>Web lab1</title>

<script src="./js/net/ajax.js"></script>
<script src="./js/plot.js"></script>
<script src="./js/historyTable.js"></script>
<script src="./js/error.js"></script>

<style>
:root {
/* Цветовая палитра в OKLCH */
Expand Down Expand Up @@ -520,26 +525,26 @@ <h2>Вариант: 467233</h2>
</g>

<style>
.svg-cord-line {
stroke: var(--svg-axis-primary);
}
.svg-cord-arrow {
fill: var(--svg-axis-primary);
}

.svg-caption {
fill: var(--text-secondary);
font-size: 12px;
}

.svg-fill-area {
fill: var(--svg-fill-area);
fill-opacity: 0.7;
}

.point-on-graph {
fill: var(--accent-primary);
}
.svg-cord-line {
stroke: var(--svg-axis-primary);
}
.svg-cord-arrow {
fill: var(--svg-axis-primary);
}

.svg-caption {
fill: var(--text-secondary);
font-size: 12px;
}

.svg-fill-area {
fill: var(--svg-fill-area);
fill-opacity: 0.7;
}

.point-on-graph {
fill: var(--accent-primary);
}
</style>
</svg>
</div>
Expand Down Expand Up @@ -671,167 +676,92 @@ <h3 class="history-title">История введенных данных</h3>
</div>

<script>
// Хранение истории данных
let formHistory = JSON.parse(sessionStorage.getItem('formHistory')) || [];

const showError = (message) => {
// Убираем существующие сообщения
const existingErrors = document.querySelectorAll('.error-message');
existingErrors.forEach(error => error.remove());

// Слздаём элемент
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;

// Вставка
document.body.appendChild(errorDiv);

// Убираем по прошествии 5-ти секунд
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, 5000);
};

const FFCGIResponceHandler = (responce, X, Y, R) => {
const JSONResponce = JSON.parse(responce);

if (JSONResponce.error) {
showError(`Ошибка: ${JSONResponce.error}`);
return;
}

// Сохраняем данные в историю
const formData = {
timestamp: new Date().toISOString(),
X: X,
Y: Y,
R: R,
elapsedTimeNs: JSONResponce.elapsedTimeNs,
isHitted: JSONResponce.result
};

formHistory.push(formData);
sessionStorage.setItem('formHistory', JSON.stringify(formHistory));

// Обновляем таблицу
refreshHistoryTable();

// Обновляем график
refreshGraph();
}
const plot = new Plot("graph-svg");
const history = new HistoryTable("historyTableBody");
const error = new ErrorFactory("error-message")

// Обработчик отправки формы
document.getElementById('graph-test-form').addEventListener('submit', function (e) {
e.preventDefault(); // Отключаем дефолтное поведение

// Получаем все значения
const X = document.querySelector('input[name="X"]:checked').value;
const R = document.querySelector('input[name="R"]:checked').value;
const Y = document.getElementById('Y').value;


if (!new RegExp("^(-?[1-9]\d*(\.\d+)?)|(^-?0(\.\d*[1-9]))$").test(Y) && Y !== '') {
showError(`Ошибка: число Y не соответствует здравому смыслу`);
return;
}

if (X && R && Y) {
sendAJAXGETRequest("/fcgi/", {"x": X, "y": Y, "r": R}, (data) => {FFCGIResponceHandler(data, X,Y,R)});

// Очищаем форму
this.reset();

} else {
alert('Пожалуйста, заполните все поля формы')
}
const X = Number(document.querySelector('input[name="X"]:checked').value);
const R = Number(document.querySelector('input[name="R"]:checked').value);
const Y = Number(document.getElementById('Y').value);

sendAJAXRequest("GET", "/fcgi/test", {"x": X, "y": Y, "r": R})
.then(
result => {testHandler(result, X, Y, R)},
result => {errorHandler(result)}
);

this.reset();
});


// Функция для обновления таблицы истории
// TODO: переделать говнокод!
const refreshHistoryTable = () => {
const tableBody = document.getElementById('historyTableBody');
const emptyHistory = document.getElementById('emptyHistory');

tableBody.innerHTML = '';

if (formHistory.length === 0) {
emptyHistory.style.display = 'block';
return;
}

emptyHistory.style.display = 'none';

// Сортируем историю по дате (новые сверху)
const sortedHistory = [...formHistory].reverse();

sortedHistory.forEach((elem, index) => {
const row = document.createElement('tr');

row.innerHTML = `
<td>${new Date(elem.timestamp).toLocaleString('ru-RU')}</td>
<td>${elem.elapsedTimeNs} ns</td>
<td>${elem.X}</td>
<td>${elem.Y}</td>
<td>${elem.R}</td>
<td>${elem.isHitted ? 'HIT' : 'MISS'}</td>
`;

tableBody.appendChild(row);
});
const errorHandler = (result) => {
const JSONResponce = JSON.parse(result.response);
error.showError(`Ошибка: ${JSONResponce.error} (${result.status})`)
}

// TODO: переделать говнокод!
const refreshGraph = () => {
const svgns = "http://www.w3.org/2000/svg";
const container = document.getElementById('graph-svg');

formHistory.forEach((elem, index) => {
const X = parseFloat(elem.X);
const Y = parseFloat(elem.Y);
const R = parseFloat(elem.R);

const cx = 20 + (1 + X/R)*80;
const cy = 20 + (1 - Y/R)*80;

if (!container.querySelector(`circle[cx="${cx}"][cy="${cy}"]`) && 0 <= cx <= 200 && 0 <= cy <= 200) {
const point = document.createElementNS(svgns, 'circle');

point.setAttributeNS(null, 'cx', cx);
point.setAttributeNS(null, 'cy', cy);
point.setAttributeNS(null, 'r', 3);
point.setAttributeNS(null, 'class', 'point-on-graph' );

container.appendChild(point);
}
});
}
const testHandler = (result, X, Y, R) => {
const JSONResponce = JSON.parse(result.response);

// Обновляем таблицу
history.addHistoryItem(
new Date().toISOString(),
JSONResponce.elapsedTimeNs,
X,
Y,
R,
JSONResponce.result
)

const clearGraph = () => {
const container = document.getElementById('graph-svg');
container.querySelectorAll('circle').forEach((elem) => {
elem.remove();
})
// Обновляем график
plot.addPoint(
...convertInputCordsToSvgCords(X, Y, R),
3,
"point-on-graph"
);
}

// Функция для очистки истории
const clearHistory = () => {
if (confirm('Вы уверены, что хотите очистить всю историю?')) {
formHistory = [];
localStorage.setItem('formHistory', JSON.stringify(formHistory));

refreshHistoryTable();

clearGraph();
history.clear();
plot.clear();
sendAJAXRequest("DELETE", "/fcgi/journal")
.then(
result => {},
result => {errorHandler(result)}
);
}
}

// Инициализация при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
refreshHistoryTable();
refreshGraph();
// Получаем историю с сервера
sendAJAXRequest("GET", "/fcgi/journal")
.then(
result => {
const JSONResponce = JSON.parse(result.response);
JSONResponce.result.forEach((elem) => {
plot.addPoint(
...convertInputCordsToSvgCords(elem.x, elem.y, elem.r),
3,
"point-on-graph"
);
});
history.addHistoryItem(
new Date(elem.timestamp).toISOString(),
elem.elapsedTimeNs,
elem.x,
elem.y,
elem.r,
elem.isInsideArea
)
},
result => {errorHandler(result)}
);
});
</script>
</body>
Expand Down
22 changes: 0 additions & 22 deletions httpd/static/js/ajax.js

This file was deleted.

31 changes: 31 additions & 0 deletions httpd/static/js/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class ErrorFactory {
#errorBlockClassName

constructor(errorBlockClassName){
this.#errorBlockClassName = errorBlockClassName;
}

#clearErrors(){
let existingErrors = document.querySelectorAll(this.#errorBlockClassName);
existingErrors.forEach(error => error.remove());
}

showError(message, showTimeMs = 5000){
this.#clearErrors();

// Слздаём элемент
const errorDiv = document.createElement('div');
errorDiv.className = this.#errorBlockClassName;
errorDiv.textContent = message;

// Вставка
document.body.appendChild(errorDiv);

// Убираем по прошествии времени
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, showTimeMs);
}
}
Loading