(function () {
'use strict';
var apiUrl = 'https://auth.xiot.pro/api/v2/entry';
var tokenKey = 'xiot_calculator_integrator_jwt';
var activeProjectIdKey = 'xiot_calculator_active_project_id';
var activeProjectNameKey = 'xiot_calculator_active_project_name';
var authToken = localStorage.getItem(tokenKey) || '';
var challengeId = '';
var pendingEmail = '';
function byId(id) {
return document.getElementById(id);
}
function api(func, data) {
data = data || {};
data.func = func;
if (authToken) {
data.jwt = authToken;
}
var options = {
method: 'POST',
credentials: 'omit',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
return fetch(apiUrl, options).then(function (response) {
return response.json().catch(function () {
throw new Error('Сервер вернул некорректный ответ.');
}).then(function (payload) {
if (!response.ok || payload.ok === false) {
throw new Error(payload.detail || payload.message || 'Не удалось выполнить запрос.');
}
return payload;
});
});
}
function setMessage(element, text, isError) {
element.textContent = text || '';
element.classList.toggle('is-error', Boolean(isError));
}
function setAuthenticated(email) {
window.XiotCalculatorIntegratorAuthenticated = Boolean(email);
byId('login-open-btn').hidden = Boolean(email);
byId('logout-btn').hidden = !email;
byId('feedback-open-btn').hidden = !email;
byId('project-save-btn').hidden = !email;
byId('projects-open-btn').hidden = !email;
byId('account-user').hidden = !email;
byId('account-user').textContent = email ? 'Интегратор: ' + email : '';
window.dispatchEvent(new CustomEvent('xiot-calculator-auth-changed', {
detail: { authenticated: Boolean(email), email: email || '' }
}));
}
function escapeHtml(value) {
return String(value || '')
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function formatProjectDate(timestamp) {
if (!timestamp) {
return '';
}
return new Date(timestamp * 1000).toLocaleString('ru-RU');
}
function openModal(modal) {
modal.hidden = false;
document.body.style.overflow = 'hidden';
var focusable = modal.querySelector('input, select, textarea, button');
if (focusable) {
window.setTimeout(function () { focusable.focus(); }, 0);
}
}
function closeModal(modal) {
modal.hidden = true;
document.body.style.overflow = '';
}
function currentCalculatorState() {
try {
return JSON.parse(localStorage.getItem('xiot_work_calc_state_v1') || '{}');
} catch (error) {
return {};
}
}
function setActiveProject(project) {
if (!project || !project.id) {
localStorage.removeItem(activeProjectIdKey);
localStorage.removeItem(activeProjectNameKey);
return;
}
localStorage.setItem(activeProjectIdKey, project.id);
localStorage.setItem(activeProjectNameKey, project.name || 'Проект');
}
function renderProjects(projects) {
var target = byId('projects-list');
if (!projects.length) {
target.innerHTML = '
Сохранённых проектов пока нет.
';
return;
}
target.innerHTML = projects.map(function (project) {
return '' +
'
' + escapeHtml(project.name) + '' +
'Изменён: ' + escapeHtml(formatProjectDate(project.updated_at)) + '
' +
'
' +
'' +
'' +
'' +
'' +
'
';
}).join('');
}
function refreshProjects() {
setMessage(byId('projects-message'), 'Загружаем проекты…');
return api('calculator_project_list').then(function (result) {
renderProjects(result.projects || []);
setMessage(byId('projects-message'), '');
return result.projects || [];
}).catch(function (error) {
setMessage(byId('projects-message'), error.message, true);
throw error;
});
}
function saveProject(projectId, name) {
return api('calculator_project_save', {
project_id: projectId || '',
name: name,
state: currentCalculatorState()
}).then(function (result) {
setActiveProject(result.project);
return result;
});
}
function initialise() {
var loginModal = byId('login-modal');
var feedbackModal = byId('feedback-modal');
var projectsModal = byId('projects-modal');
var emailForm = byId('email-form');
var codeForm = byId('code-form');
var loginMessage = byId('login-message');
var feedbackResult = byId('feedback-message-result');
var projectsMessage = byId('projects-message');
document.querySelectorAll('[data-account-close]').forEach(function (element) {
element.addEventListener('click', function () {
closeModal(element.closest('.account-modal'));
});
});
byId('login-open-btn').addEventListener('click', function () {
setMessage(loginMessage, '');
openModal(loginModal);
});
byId('feedback-open-btn').addEventListener('click', function () {
setMessage(feedbackResult, '');
openModal(feedbackModal);
});
byId('projects-open-btn').addEventListener('click', function () {
var state = currentCalculatorState();
byId('project-name').value = state.objectName || 'Новый проект';
openModal(projectsModal);
refreshProjects().catch(function () {});
});
byId('project-save-btn').addEventListener('click', function () {
var button = byId('project-save-btn');
var projectId = localStorage.getItem(activeProjectIdKey) || '';
var projectName = localStorage.getItem(activeProjectNameKey) || '';
if (!projectId || !projectName) {
var state = currentCalculatorState();
byId('project-name').value = state.objectName || 'Новый проект';
openModal(projectsModal);
refreshProjects().catch(function () {});
return;
}
button.disabled = true;
saveProject(projectId, projectName).then(function () {
button.textContent = 'Сохранено';
window.setTimeout(function () { button.textContent = 'Сохранить проект'; }, 1500);
}).catch(function (error) {
window.alert(error.message);
}).then(function () {
button.disabled = false;
});
});
byId('project-create-form').addEventListener('submit', function (event) {
event.preventDefault();
var submit = event.currentTarget.querySelector('[type="submit"]');
var name = byId('project-name').value.trim();
submit.disabled = true;
setMessage(projectsMessage, 'Сохраняем проект…');
saveProject('', name).then(function (result) {
setMessage(projectsMessage, result.message || 'Проект сохранён');
return refreshProjects();
}).catch(function (error) {
setMessage(projectsMessage, error.message, true);
}).then(function () {
submit.disabled = false;
});
});
byId('projects-list').addEventListener('click', function (event) {
var row = event.target.closest('[data-project-id]');
if (!row) {
return;
}
var projectId = row.dataset.projectId;
var projectName = row.dataset.projectName;
if (event.target.closest('[data-project-open]')) {
setMessage(projectsMessage, 'Открываем проект…');
api('calculator_project_get', { project_id: projectId }).then(function (result) {
localStorage.setItem('xiot_work_calc_state_v1', JSON.stringify(result.project.state || {}));
localStorage.removeItem('CalcInputData');
setActiveProject(result.project);
window.location.reload();
}).catch(function (error) {
setMessage(projectsMessage, error.message, true);
});
return;
}
if (event.target.closest('[data-project-copy]')) {
var copyName = window.prompt('Введите название копии проекта:', projectName + ' — копия');
if (copyName === null) {
return;
}
copyName = copyName.trim();
if (!copyName) {
window.alert('Название проекта не может быть пустым.');
return;
}
if (copyName.length > 160) {
window.alert('Название проекта должно быть не длиннее 160 символов.');
return;
}
setMessage(projectsMessage, 'Создаём копию…');
api('calculator_project_get', { project_id: projectId }).then(function (result) {
return api('calculator_project_save', {
project_id: '',
name: copyName,
state: result.project.state || {}
});
}).then(function () {
setMessage(projectsMessage, 'Копия создана');
return refreshProjects();
}).catch(function (error) {
setMessage(projectsMessage, error.message, true);
});
return;
}
if (event.target.closest('[data-project-rename]')) {
var newName = window.prompt('Введите новое название проекта:', projectName);
if (newName === null) {
return;
}
newName = newName.trim();
if (!newName) {
window.alert('Название проекта не может быть пустым.');
return;
}
if (newName.length > 160) {
window.alert('Название проекта должно быть не длиннее 160 символов.');
return;
}
if (newName === projectName) {
return;
}
setMessage(projectsMessage, 'Переименовываем проект…');
api('calculator_project_get', { project_id: projectId }).then(function (result) {
return api('calculator_project_save', {
project_id: projectId,
name: newName,
state: result.project.state || {}
});
}).then(function (result) {
if (localStorage.getItem(activeProjectIdKey) === projectId) {
setActiveProject(result.project);
}
setMessage(projectsMessage, 'Проект переименован');
return refreshProjects();
}).catch(function (error) {
setMessage(projectsMessage, error.message, true);
});
return;
}
if (event.target.closest('[data-project-delete]')) {
if (!window.confirm('Удалить проект «' + projectName + '»?')) {
return;
}
setMessage(projectsMessage, 'Удаляем проект…');
api('calculator_project_delete', { project_id: projectId }).then(function () {
if (localStorage.getItem(activeProjectIdKey) === projectId) {
setActiveProject(null);
}
setMessage(projectsMessage, 'Проект удалён');
return refreshProjects();
}).catch(function (error) {
setMessage(projectsMessage, error.message, true);
});
}
});
byId('change-email-btn').addEventListener('click', function () {
emailForm.hidden = false;
codeForm.hidden = true;
byId('login-email').focus();
setMessage(loginMessage, '');
});
emailForm.addEventListener('submit', function (event) {
event.preventDefault();
var submit = emailForm.querySelector('[type="submit"]');
pendingEmail = byId('login-email').value.trim().toLowerCase();
submit.disabled = true;
setMessage(loginMessage, 'Отправляем код…');
api('calculator_auth_start', { email: pendingEmail }).then(function (result) {
challengeId = result.challenge_id || '';
emailForm.hidden = true;
codeForm.hidden = false;
byId('login-code').value = '';
byId('login-code').focus();
setMessage(loginMessage, result.message || 'Если адрес разрешён, код отправлен.');
}).catch(function (error) {
setMessage(loginMessage, error.message, true);
}).then(function () {
submit.disabled = false;
});
});
codeForm.addEventListener('submit', function (event) {
event.preventDefault();
var submit = codeForm.querySelector('[type="submit"]');
submit.disabled = true;
setMessage(loginMessage, 'Проверяем код…');
api('calculator_auth_verify', {
challenge_id: challengeId,
code: byId('login-code').value.trim()
}).then(function (result) {
authToken = result.jwt || '';
localStorage.setItem(tokenKey, authToken);
setAuthenticated(result.email);
closeModal(loginModal);
}).catch(function (error) {
setMessage(loginMessage, error.message, true);
}).then(function () {
submit.disabled = false;
});
});
byId('logout-btn').addEventListener('click', function () {
authToken = '';
localStorage.removeItem(tokenKey);
setActiveProject(null);
setAuthenticated('');
});
byId('feedback-form').addEventListener('submit', function (event) {
event.preventDefault();
var form = event.currentTarget;
var submit = form.querySelector('[type="submit"]');
var includeContext = byId('feedback-context').checked;
submit.disabled = true;
setMessage(feedbackResult, 'Отправляем обращение…');
api('calculator_feedback', {
category: byId('feedback-category').value,
subject: byId('feedback-subject').value.trim(),
message: byId('feedback-message').value.trim(),
context: includeContext ? {
calculator: currentCalculatorState(),
page: window.location.href,
userAgent: navigator.userAgent
} : null
}).then(function (result) {
form.reset();
byId('feedback-context').checked = true;
setMessage(feedbackResult, result.message || 'Обращение отправлено.');
}).catch(function (error) {
setMessage(feedbackResult, error.message, true);
}).then(function () {
submit.disabled = false;
});
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
document.querySelectorAll('.account-modal:not([hidden])').forEach(closeModal);
}
});
if (!authToken) {
setAuthenticated('');
return;
}
api('calculator_auth_status').then(function (result) {
setAuthenticated(result.authenticated ? result.email : '');
}).catch(function () {
authToken = '';
localStorage.removeItem(tokenKey);
setActiveProject(null);
setAuthenticated('');
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initialise);
} else {
initialise();
}
}());