321 lines
13 KiB
HTML
Executable File
321 lines
13 KiB
HTML
Executable File
{% block css %}
|
||
<link type="text/css" rel="stylesheet" href="/css/account.css">
|
||
<link type="text/css" rel="stylesheet" href="/css/icons/arrow_right.css">
|
||
<link type="text/css" rel="stylesheet" href="/css/icons/nametag.css">
|
||
<link type="text/css" rel="stylesheet" href="/css/icons/keyhole.css">
|
||
<link type="text/css" rel="stylesheet" href="/css/icons/user_add.css">
|
||
{% endblock %}
|
||
|
||
{% block body %}
|
||
<section id="entry" class="panel medium snow">
|
||
<section class="header unselectable">
|
||
<h1>Идентификация</h1>
|
||
</section>
|
||
<section class="body">
|
||
<iframe name="void" style="display:none"></iframe>
|
||
<form id="identification" method="POST" action="/entry" target="void" autocomplete="on" novalidate="true">
|
||
<label id="administrator" form="identification" for="_administrator">
|
||
<i class="icon nametag"></i>
|
||
<input id="_administrator" class="stretched merged right snow borderless" name="administrator" type="text"
|
||
placeholder="Идентификатор администратора" list="administrators"
|
||
value="{{ session.buffer.entry.administrator._key ?? cookie.buffer_entry_administrator__key }}"
|
||
onkeypress="if (event.keyCode === 13) this.nextElementSibling.click()" autocomplete="username"
|
||
autofocus="true">
|
||
<button type="submit" class="clay merged left" onclick="__administrator(); return false"><i
|
||
class="icon arrow right"></i></button>
|
||
<datalist id="administrators">
|
||
{% for account in accounts %}
|
||
<option value="{{ account.getKey() }}">{{ account.getKey() }} {{ account.name.first }} {{ account.name.second
|
||
}}</option>
|
||
{% endfor %}
|
||
</datalist>
|
||
</label>
|
||
<label id="password" class="hidden" form="identification" for="_password">
|
||
<i class="icon keyhole"></i>
|
||
<input id="_password" class="stretched merged right snow borderless" name="password" type="password"
|
||
placeholder="Пароль" onkeypress="if (event.keyCode === 13) this.nextElementSibling.click()"
|
||
autocomplete="current-password">
|
||
<button type="submit" class="clay merged left" onclick="__password()"><i class="icon arrow right"></i></button>
|
||
</label>
|
||
</form>
|
||
|
||
</section>
|
||
</section>
|
||
<section id="errors" class="panel medium animation window hidden snow" style="--height: 300px">
|
||
<section class="body">
|
||
<dl></dl>
|
||
</section>
|
||
</section>
|
||
<script>
|
||
// Инициализация функций в глобальной области видимости
|
||
let _administrator, __administrator, _password, __password, _errors, balls, trash;
|
||
|
||
document.addEventListener('damper.initialized', function (e) {
|
||
// Инициализирован демпфер
|
||
|
||
// Защита от вызова на других страницах
|
||
if (trash) return;
|
||
|
||
// Инициализация HTML-элемента с блоком входа в аккаунт
|
||
const entry = {wrap: document.getElementById('entry')};
|
||
entry.title = entry.wrap.getElementsByTagName('h1')[0];
|
||
|
||
// Инициализация HTML-элемента с блоком ошибок
|
||
const errors = {wrap: document.getElementById('errors')};
|
||
errors.list = errors.wrap.getElementsByTagName('dl')[0];
|
||
|
||
// Инициализация HTML-элементов-оболочек полей ввода
|
||
const fields = {
|
||
administrator: {label: document.getElementById('administrator')},
|
||
password: {label: document.getElementById('password')},
|
||
};
|
||
fields.administrator.input = fields.administrator.label.getElementsByTagName('input')[0];
|
||
fields.password.input = fields.password.label.getElementsByTagName('input')[0];
|
||
fields.administrator.button = fields.administrator.label.getElementsByTagName('button')[0];
|
||
fields.password.button = fields.password.label.getElementsByTagName('button')[0];
|
||
|
||
// Инициализация маски идентификатора администратора
|
||
fields.administrator.input.mask = IMask(fields.administrator.input, {mask: '000000000000'});
|
||
|
||
/**
|
||
* Отправить входной псевдоним на сервер
|
||
*
|
||
* @return {void}
|
||
*/
|
||
_administrator = async () => {
|
||
// Деинициализация индикатора и анимации об ошибке
|
||
fields.administrator.input.classList.remove('error');
|
||
fields.administrator.input.previousElementSibling.classList.remove('error');
|
||
|
||
// Инициализация функции разблокировки
|
||
function unblock() {
|
||
// Разблокировка поля ввода
|
||
fields.administrator.input.removeAttribute('readonly');
|
||
fields.administrator.button.removeAttribute('disabled');
|
||
|
||
// Инициализация отображения ошибки
|
||
fields.administrator.input.classList.add('error');
|
||
fields.administrator.input.previousElementSibling.classList.add('error');
|
||
|
||
// Фокусировка на поле ввода
|
||
fields.administrator.input.focus();
|
||
}
|
||
|
||
// Запуск отсрочки разблокировки на случай, если сервер не отвечает
|
||
const timeout = setTimeout(() => {_errors(['Сервер не отвечает']); unblock()}, 5000);
|
||
|
||
// Запрос к серверу
|
||
const response = await session.administrator(fields.administrator.input.value);
|
||
|
||
// Удаление отсрочки разблокировки
|
||
clearTimeout(timeout);
|
||
|
||
if (_errors(response.errors)) {
|
||
// Сгенерированы ошибки
|
||
|
||
// Разблокировка
|
||
unblock();
|
||
} else {
|
||
// Не сгенерированы ошибки (подразумевается их отсутствие)
|
||
|
||
// Деинициализация интерфейса идентификации
|
||
fields.administrator.label.classList.add('hidden');
|
||
|
||
// Инициализация интерфейса аутентификации
|
||
entry.title.innerText = 'Аутентификация';
|
||
fields.password.label.classList.remove('hidden');
|
||
fields.password.input.focus();
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Отправить входной псевдоним на сервер с дополнительной подготовкой
|
||
*
|
||
* @return {void}
|
||
*/
|
||
__administrator = e.detail.damper(async () => {
|
||
// Блокировка поля ввода
|
||
fields.administrator.input.setAttribute('readonly', true);
|
||
fields.administrator.button.setAttribute('disabled', true);
|
||
|
||
// Реинициализация блока ошибок
|
||
_errors();
|
||
|
||
// Запуск процесса отправки входного псевдонима
|
||
_administrator();
|
||
}, 1000);
|
||
|
||
/**
|
||
* Отправить пароль на сервер
|
||
*
|
||
* @return {void}
|
||
*/
|
||
_password = async () => {
|
||
// Деинициализация индикатора и анимации об ошибке
|
||
fields.password.input.classList.remove('error');
|
||
fields.password.input.previousElementSibling.classList.remove('error');
|
||
|
||
// Инициализация функции разблокировки
|
||
function unblock() {
|
||
// Разблокировка поля ввода
|
||
fields.password.input.removeAttribute('readonly');
|
||
fields.password.button.removeAttribute('disabled');
|
||
|
||
// Инициализация отображения ошибки
|
||
fields.password.input.classList.add('error');
|
||
fields.password.input.previousElementSibling.classList.add('error');
|
||
|
||
// Фокусировка на поле ввода
|
||
fields.password.input.focus();
|
||
}
|
||
|
||
// Запуск отсрочки разблокировки на случай, если сервер не отвечает
|
||
const timeout = setTimeout(() => {_errors(['Сервер не отвечает']); unblock()}, 5000);
|
||
|
||
// Запрос к серверу
|
||
const response = await session.password(fields.password.input.value);
|
||
|
||
// Удаление отсрочки разблокировки
|
||
clearTimeout(timeout);
|
||
|
||
if (_errors(response.errors) || response.verify !== true) {
|
||
// Сгенерированы ошибки
|
||
|
||
// Разблокировка
|
||
unblock();
|
||
} else {
|
||
// Не сгенерированы ошибки (подразумевается их отсутствие)
|
||
|
||
if (response.account) {
|
||
// Инициализирован аккаунт
|
||
|
||
if (typeof loader === 'function') {
|
||
// Initialized the loader class
|
||
|
||
// Отметить данный HTML-элемент с JS-кодом в очередь на очистку?
|
||
trash = true;
|
||
|
||
// Деинициализация неактуального CSS-документа
|
||
document.head.querySelector('link[href="/css/account.css"]').remove();
|
||
|
||
// Инициализация главной страницы
|
||
loader.index();
|
||
loader.menu();
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Отправить пароль на сервер с дополнительной подготовкой
|
||
*
|
||
* @return {void}
|
||
*/
|
||
__password = e.detail.damper(async () => {
|
||
// Блокировка поля ввода
|
||
fields.password.input.setAttribute('readonly', true);
|
||
fields.password.button.setAttribute('disabled', true);
|
||
|
||
// Реинициализация блока ошибок
|
||
_errors();
|
||
|
||
// Запуск процесса отправки входного псевдонима
|
||
_password();
|
||
}, 100)
|
||
|
||
/**
|
||
* Сгенерировать HTML-элемент с блоком ошибок
|
||
*
|
||
* @param {object} registry Реестр ошибок
|
||
* @param {bool} reinitialization Реинициализировать?
|
||
*
|
||
* @return {bool} Сгенерированы ошибки?
|
||
*/
|
||
_errors = (registry, reinitialization = true) => {
|
||
function height() {
|
||
// Скрытие HTML-элемента
|
||
errors.wrap.style.zIndex = '-99999';
|
||
errors.wrap.style.left = '-99999px';
|
||
errors.wrap.style.top = '-99999px';
|
||
errors.wrap.style.position = 'absolute';
|
||
errors.wrap.style.display = 'var(--display, unset)';
|
||
errors.wrap.style.opacity = '0';
|
||
errors.wrap.style.animationName = 'unset';
|
||
|
||
// Реинициализация переменной с данными о высоте HTML-элемента
|
||
errors.wrap.style.setProperty('--height', errors.wrap.offsetHeight + 'px');
|
||
|
||
// Отмена скрытия HTML-элемента
|
||
errors.wrap.style.zIndex =
|
||
errors.wrap.style.left =
|
||
errors.wrap.style.top =
|
||
errors.wrap.style.position =
|
||
errors.wrap.style.display =
|
||
errors.wrap.style.opacity =
|
||
errors.wrap.style.animationName = null;
|
||
}
|
||
|
||
// Удаление ошибок из прошлой генерации
|
||
if (reinitialization) errors.list.innerHTML = null;
|
||
|
||
for (error in registry) {
|
||
// Генерация HTML-элементов с текстами ошибок
|
||
|
||
// Инициализация HTML-элемента текста ошибки
|
||
const samp = document.createElement('samp');
|
||
|
||
if (typeof registry[error] === 'object') {
|
||
// Категория ошибок
|
||
|
||
// Проверка наличия ошибок
|
||
if (registry[error].length === 0) continue;
|
||
|
||
// Инициализация HTML-элемента-оболочки
|
||
const wrap = document.createElement('dt');
|
||
|
||
// Запись текста категории
|
||
samp.innerText = error;
|
||
|
||
// Запись HTML-элементов в список
|
||
wrap.appendChild(samp);
|
||
errors.list.appendChild(wrap);
|
||
|
||
// Реинициализация высоты
|
||
height();
|
||
|
||
// Обработка вложенных ошибок (вход в рекурсию)
|
||
_errors(registry[error], false);
|
||
} else {
|
||
// Текст ошибки (подразумевается)
|
||
|
||
// Инициализация HTML-элемента
|
||
const wrap = document.createElement('dd');
|
||
|
||
// Запись текста ошибки
|
||
samp.innerText = registry[error];
|
||
|
||
// Запись HTML-элемента в список
|
||
wrap.appendChild(samp);
|
||
errors.list.appendChild(wrap);
|
||
|
||
// Реинициализация высоты
|
||
height();
|
||
}
|
||
}
|
||
|
||
// Реинициализация HTML-элемента с текстом ошибок
|
||
if (reinitialization && errors.list.childElementCount === 0) errors.wrap.classList.add('hidden');
|
||
else errors.wrap.classList.remove('hidden');
|
||
|
||
return errors.list.childElementCount === 0 ? false : true;
|
||
};
|
||
});
|
||
</script>
|
||
{% endblock %}
|
||
|
||
{% block js %}
|
||
<script type="text/javascript" src="/js/session.js"></script>
|
||
<script type="text/javascript" src="/js/loader.js"></script>
|
||
<script type="text/javascript" src="/js/imask-7.1.0-alpha.js"></script>
|
||
{% endblock %}
|