This commit is contained in:
allllen4a 2024-01-09 14:01:48 +03:00
parent 0823ed978b
commit 83e35316e3
24 changed files with 818 additions and 1055 deletions

File diff suppressed because one or more lines are too long

@ -1,455 +1,67 @@
/* eslint-disable linebreak-style */
// модуль с логикой
// Модуль для работы с модальным окном
// eslint-disable-next-line linebreak-style
// eslint-disable-next-line import/no-self-import
import {
// eslint-disable-next-line import/named
hideUpdateModal,
// eslint-disable-next-line import/named
showUpdateModal,
} from "./lines-modal";
import {
createLine,
deleteLine,
getAllGengeTypes,
getAllAuthorTypes,
getAllLines,
getLine,
updateLine,
} from "./lines-rest-api";
import {
cntrls,
createTableRow,
imagePlaceholder,
createGenresOption,
createAuthorsOption,
createTableRowOnIndex,
} from "./lines-ui";
// импорт компонента Modal из bootstrap
import { Modal } from "bootstrap";
import { cntrls, imagePlaceholder } from "./lines-ui";
async function drawGenreSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllGengeTypes();
const data2 = await getAllAuthorTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.genresType.innerHTML = "";
cntrls.authorsType.innerHTML = "";
// пустое значение
cntrls.genresType.appendChild(createGenresOption("Выберите значение", "", true));
cntrls.authorsType.appendChild(createGenresOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((genre) => {
cntrls.genresType.appendChild(createGenresOption(genre.name, genre.id));
});
data2.forEach((author) => {
cntrls.authorsType.appendChild(createAuthorsOption(author.name, author.id));
});
// поиск модального окна на странице
const modal = document.getElementById("items-update");
// если он найден, то создается экземпляр компонента Modal
// для программного управления модальным окном
const myModal = modal ? new Modal(modal, {}) : null;
// поиск тега с заголовком модального кона для его смены
const modalTitle = document.getElementById("items-update-title");
// обнуление значений модального окна, т. к.
// используется одно окно для всех операций
function resetValues() {
cntrls.lineId.value = "";
cntrls.nameBook.value = "";
cntrls.authorsType.value = "";
cntrls.genresType.value = "";
cntrls.year.value = "";
cntrls.description.value = "";
cntrls.count.value = "";
cntrls.image.value = "";
cntrls.imagePreview.src = imagePlaceholder;
}
async function drawLinesTable() {
console.info("Try to load data");
if (!cntrls.table) {
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
// очистка содержимого table
// удаляется все, что находится между тегами <table></table>
// но не атрибуты
cntrls.table.innerHTML = "";
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.table.appendChild(
createTableRow(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => removeLine(item.id),
),
);
});
// функция для показа модального окна
// перед показом происходит заполнение формы для редактирования
// если объект item не пуст
export function showUpdateModal(item) {
modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
console.info(item);
if (item) {
cntrls.lineId.value = item.id;
cntrls.nameBook.value = item.nameBook;
cntrls.authorsType.value = item.authorsId;
cntrls.genresType.value = item.genresId;
cntrls.year.value = item.year;
cntrls.description.value = item.description;
cntrls.count.value = item.count;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
} else {
resetValues();
}
myModal.show();
}
async function drawLinesTableOnIndex() {
console.info("Try to load data On Index");
if (!cntrls.container) {
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
// очистка содержимого table
// удаляется все, что находится между тегами <table></table>
// но не атрибуты
cntrls.container.innerHTML = "";
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.container.appendChild(
createTableRowOnIndex(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => location.assign(`page3.html?id=${item.id}`),
() => removeLine(item.id),
),
);
});
}
async function addLine(nameBook, authorsType, genresType, year, description, count, date, image) {
console.info("Try to add item");
// вызов метода REST API для добавления записи
// eslint-disable-next-line max-len
const data = await createLine(nameBook, authorsType, genresType, year, description, count, date, image);
console.info("Added");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
// eslint-disable-next-line max-len
async function editLine(id, nameBook, authorsType, genresType, year, description, count, date, image) {
console.info("Try to update item");
console.log(id, nameBook, authorsType, genresType, description, count, date, image);
// вызов метода REST API для обновления записи
// eslint-disable-next-line max-len
const data = await updateLine(id, nameBook, authorsType, genresType, year, description, count, date, image);
console.info("Updated");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function removeLine(id) {
if (!confirm("Вы точно хотите удалить этот товар?")) {
console.info("Canceled");
return;
}
console.info("Try to remove item");
// вызов метода REST API для удаления записи
const data = await deleteLine(id);
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
// функция для получения содержимого файла в виде base64 строки
// https://ru.wikipedia.org/wiki/Base64
async function readFile(file) {
const reader = new FileReader();
// создание Promise-объекта для использования функции
// с помощью await (асинхронно) без коллбэков (callback)
// https://learn.javascript.ru/promise
return new Promise((resolve, reject) => {
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
});
}
// функция для обновления блока с превью выбранного изображения
async function updateImagePreview() {
// получение выбранного файла
// возможен выбор нескольких файлов, поэтому необходимо получить только первый
const file = cntrls.image.files[0];
// чтение содержимого файла в виде base64 строки
const fileContent = await readFile(file);
console.info("base64 ", fileContent);
// обновление атрибута src для тега img с id image-preview
cntrls.imagePreview.src = fileContent;
}
// Функция для обработки создания и редактирования элементов таблицы через модальное окно
// Если хотите делать через страницу, то удалите эту функцию
export function linesForm() {
console.info("linesForm");
// загрузка и заполнение select со списком товаров
drawGenreSelect();
// drawAuthorSelect();
// загрузка и заполнение table
drawLinesTable();
// Вызов функции обновления превью изображения при возникновении
// события oncahnge в тэге input с id image
cntrls.image.addEventListener("change", () => updateImagePreview());
// обработчик события нажатия на кнопку для показа модального окна
cntrls.button.addEventListener("click", () => showUpdateModal(null));
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}
export async function linesFormOnIndex() {
console.info("linesFormOnIndex");
// await drawGenreSelect();
await drawLinesTableOnIndex();
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}
// Функция для обработки создания и редактирования элементов таблицы через страницу page-admin.html
// Если хотите делать через модальное окно, то удалите эту функцию
export async function linesPageForm() {
console.info("linesPageForm");
// загрузка и заполнение select со списком товаров
// drawGenreSelect();
// func1 = (id) => {} аналогично function func1(id) {}
const goBack = () => location.assign("/page3.html");
// Вызов функции обновления превью изображения при возникновении
// события onchange в тэге input с id image
cntrls.image.addEventListener("change", () => updateImagePreview());
// получение параметров GET-запроса из URL
// параметры перечислены после символа ? (?id=1&color=black&...)
const urlParams = new URLSearchParams(location.search);
// получение значения конкретного параметра (id)
// указан только при редактировании
const currentId = urlParams.get("id");
// если id задан
if (currentId) {
try {
// вызов метода REST API для получения записи по первичному ключу(id)
const line = await getLine(currentId);
// заполнение формы для редактирования
cntrls.nameBook.value = line.nameBook;
cntrls.authorsType.value = line.authorsType;
cntrls.genresType.value = line.genresType;
cntrls.year.value = line.year;
cntrls.description.value = line.description;
cntrls.count.value = line.count;
cntrls.date.value = line.date;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
} catch {
// в случае ошибки происходит возврат к index
goBack();
}
}
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// если значение параметра запроса не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
}
// возврат к странице index
goBack();
});
// функция для скрытия модального окна
export function hideUpdateModal() {
resetValues();
// удаление класса was-validated для скрытия результатов валидации
cntrls.form.classList.remove("was-validated");
myModal.hide();
}

@ -1,12 +1,20 @@
/* eslint-disable linebreak-style */
// eslint-disable-next-line linebreak-style
// eslint-disable-next-line linebreak-style
// eslint-disable-next-line linebreak-style
/* eslint-disable eol-last */
/* eslint-disable linebreak-style */
/* eslint-disable max-len */
/* eslint-disable linebreak-style */
/* eslint-disable comma-dangle */
/* eslint-disable linebreak-style */
// модуль для работы с REST API сервера
// адрес сервера
const serverUrl = "http://localhost:8081";
// функция возвращает объект нужной структуры для отправки на сервер
// eslint-disable-next-line max-len
function createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
function createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image) {
return {
nameBook: _name,
authorsId: _authorsType,
@ -14,8 +22,7 @@ function createLineObject(_name, _authorsType, _genresType, _year, _description,
year: _year,
description: _description,
count: _count,
date: _date,
image: _image,
image: _image
};
}
@ -56,10 +63,8 @@ export async function getLine(id) {
// обращение к серверу для создания записи (post)
// объект отправляется в теле запроса (body)
// eslint-disable-next-line max-len
export async function createLine(_name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
// eslint-disable-next-line max-len
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image);
export async function createLine(_name, _authorsType, _genresType, _year, _description, _count, _image) {
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image);
const options = {
method: "POST",
@ -80,10 +85,8 @@ export async function createLine(_name, _authorsType, _genresType, _year, _descr
// обращение к серверу для обновления записи по id (put)
// объект отправляется в теле запроса (body)
// id передается в качестве части пути URL get-запроса
// eslint-disable-next-line max-len
export async function updateLine(id, _name, _authorsType, _genresType, _year, _description, _count, _date, _image) {
// eslint-disable-next-line max-len
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _date, _image);
export async function updateLine(id, _name, _authorsType, _genresType, _year, _description, _count, _image) {
const itemObject = createLineObject(_name, _authorsType, _genresType, _year, _description, _count, _image);
const options = {
method: "PUT",
@ -114,4 +117,4 @@ export async function deleteLine(id) {
}
await response.json();
location.reload();
}
}

@ -1,7 +1,10 @@
/* eslint-disable linebreak-style */
/* eslint-disable no-unused-vars */
/* eslint-disable linebreak-style */
/* eslint-disable eol-last */
/* eslint-disable linebreak-style */
// модуль для работы с элементами управления
// eslint-disable-next-line linebreak-style
// объект для удобного получения элементов
// при обращении к атрибуту объекта вызывается
// нужная функция для поиска элемента
@ -17,7 +20,6 @@ export const cntrls = {
year: document.getElementById("yearPicker"),
description: document.getElementById("description"),
count: document.getElementById("count"),
date: document.getElementById("date"),
image: document.getElementById("image"),
imagePreview: document.getElementById("image-preview"),
};
@ -101,7 +103,6 @@ export function createTableRow(item, index, editCallback, deleteCallback) {
row.appendChild(createTableColumn(item.year));
row.appendChild(createTableColumn(item.description));
row.appendChild(createTableColumn(item.count));
row.appendChild(createTableColumn(item.date));
// редактировать в модальном окне
row.appendChild(createTableAnchor("fa-pencil", editCallback));
@ -110,13 +111,15 @@ export function createTableRow(item, index, editCallback, deleteCallback) {
return row;
}
export function createTableRowOnIndex(item) {
export function createTableRowOnIndex(item, index, editCallback, editPageCallback, deleteCallback) {
console.log(item);
const bookCard = createBookCard(item);
return bookCard;
}
// eslint-disable-next-line no-multiple-empty-lines
function createBookCard(item) {
const card = document.createElement("div");
card.classList.add("book");
@ -128,14 +131,29 @@ function createBookCard(item) {
const title = document.createElement("h3");
title.textContent = `${item.nameBook}, ${item.authors.name}`;
const detailsButton = document.createElement("a");
detailsButton.href = "#link";
detailsButton.classList.add("btn", "btn-custom");
detailsButton.role = "button";
detailsButton.textContent = "Подробнее";
const favoriteButton = document.createElement("a");
favoriteButton.href = "#link";
favoriteButton.classList.add("btn", "btn-custom");
favoriteButton.role = "button";
favoriteButton.textContent = "В избранное";
card.appendChild(image);
card.appendChild(title);
card.appendChild(detailsButton);
card.appendChild(favoriteButton);
const div = document.createElement("div");
div.classList.add("col-12");
div.classList.add("col-md-4");
div.classList.add("col-md-6");
div.classList.add("col-lg-4");
div.classList.add("col-xl-3");
div.appendChild(card);
return div;
}
}

@ -1,66 +1,62 @@
/* eslint-disable linebreak-style */
/* eslint-disable import/named */
// eslint-disable-next-line linebreak-style
/* eslint-disable spaced-comment */
/* eslint-disable linebreak-style */
/* eslint-disable comma-dangle */
/* eslint-disable linebreak-style */
/* eslint-disable no-trailing-spaces */
/* eslint-disable linebreak-style */
// модуль с логикой
// eslint-disable-next-line import/named
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
hideUpdateModal,
showUpdateModal
} from "./lines-modal";
import {
createLine,
deleteLine,
getAllItemTypes,
getAllItemAuthors,
getAllGengeTypes,
getAllAuthorTypes,
getAllLines,
getLine,
updateLine,
} from "./lines-rest-api";
import {
cntrls,
createItemsOption,
createTableRow,
imagePlaceholder,
cntrls,
createTableRow,
imagePlaceholder,
createGenresOption,
createAuthorsOption,
createTableRowOnIndex
} from "./lines-ui";
async function drawItemsSelect() {
async function drawGenreSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllItemTypes();
const data = await getAllGengeTypes();
const data2 = await getAllAuthorTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.itemsType.innerHTML = "";
cntrls.genresType.innerHTML = "";
cntrls.authorsType.innerHTML = "";
// пустое значение
cntrls.itemsType.appendChild(
createItemsOption("Выберите значение", "", true),
);
cntrls.genresType.appendChild(createGenresOption("Выберите значение", "", true));
cntrls.authorsType.appendChild(createGenresOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.itemsType.appendChild(createItemsOption(item.name, item.id));
data.forEach((genre) => {
cntrls.genresType.appendChild(createGenresOption(genre.name, genre.id));
});
}
async function drawAuthorsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllItemAuthors();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.author.innerHTML = "";
// пустое значение
cntrls.author.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.author.appendChild(createItemsOption(item.name, item.id));
data2.forEach((author) => {
cntrls.authorsType.appendChild(createAuthorsOption(author.name, author.id));
});
}
async function drawLinesTable() {
console.info("Try to load data");
if (!cntrls.table) {
return;
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
@ -72,44 +68,66 @@ async function drawLinesTable() {
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.table.appendChild(
createTableRow(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => location.assign(`page-edit.html?id=${item.id}`),
() => removeLine(item.id),
),
);
cntrls.table.appendChild(
createTableRow(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => removeLine(item.id),
),
);
});
}
async function addLine(item, author, name, desc, count, date, image) {
async function drawLinesTableOnIndex() {
console.info("Try to load data On Index");
if (!cntrls.container) {
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
// очистка содержимого table
// удаляется все, что находится между тегами <table></table>
// но не атрибуты
cntrls.container.innerHTML = "";
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.container.appendChild(
createTableRowOnIndex(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => location.assign(`page3.html?id=${item.id}`),
() => removeLine(item.id),
),
);
});
}
async function addLine(nameBook, authorsType, genresType, year, description, count, image) {
console.info("Try to add item");
// вызов метода REST API для добавления записи
const data = await createLine(item, author, name, desc, count, date, image);
const data = await createLine(nameBook, authorsType, genresType, year, description, count, image);
console.info("Added");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function editLine(id, item, author, name, desc, count, date, image) {
async function editLine(id, nameBook, authorsType, genresType, year, description, count, image) {
console.info("Try to update item");
console.log(id, nameBook, authorsType, genresType, description, count, image);
// вызов метода REST API для обновления записи
const data = await updateLine(
id,
item,
author,
name,
desc,
count,
date,
image,
);
// eslint-disable-next-line max-len
const data = await updateLine(id, nameBook, authorsType, genresType, year, description, count, image);
console.info("Updated");
console.info(data);
// загрузка и заполнение table
@ -117,9 +135,9 @@ async function editLine(id, item, author, name, desc, count, date, image) {
}
async function removeLine(id) {
if (!confirm("Do you really want to remove this item?")) {
console.info("Canceled");
return;
if (!confirm("Вы точно хотите удалить этот товар?")) {
console.info("Canceled");
return;
}
console.info("Try to remove item");
// вызов метода REST API для удаления записи
@ -138,25 +156,25 @@ async function readFile(file) {
// с помощью await (асинхронно) без коллбэков (callback)
// https://learn.javascript.ru/promise
return new Promise((resolve, reject) => {
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
});
}
@ -178,8 +196,8 @@ export function linesForm() {
console.info("linesForm");
// загрузка и заполнение select со списком товаров
drawItemsSelect();
drawAuthorsSelect();
drawGenreSelect();
//drawAuthorSelect();
// загрузка и заполнение table
drawLinesTable();
@ -194,78 +212,147 @@ export function linesForm() {
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.itemsType.value,
cntrls.author.value,
cntrls.name.value,
cntrls.desc.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.itemsType.value,
cntrls.author.value,
cntrls.name.value,
cntrls.desc.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}
// Функция для обработки создания и редактирования элементов таблицы через страницу page-edit.html
export async function linesFormOnIndex() {
console.info("linesFormOnIndex");
// await drawGenreSelect();
await drawLinesTableOnIndex();
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}
// Функция для обработки создания и редактирования элементов таблицы через страницу page-admin.html
// Если хотите делать через модальное окно, то удалите эту функцию
export async function linesPageForm() {
console.info("linesPageForm");
// загрузка и заполнение select со списком товаров
drawItemsSelect();
drawAuthorsSelect();
// drawGenreSelect();
// func1 = (id) => {} аналогично function func1(id) {}
const goBack = () => location.assign("/page4.html");
const goBack = () => location.assign("/Catalog.html");
// Вызов функции обновления превью изображения при возникновении
// события onchange в тэге input с id image
@ -280,84 +367,84 @@ export async function linesPageForm() {
const currentId = urlParams.get("id");
// если id задан
if (currentId) {
try {
// вызов метода REST API для получения записи по первичному ключу(id)
const line = await getLine(currentId);
// заполнение формы для редактирования
cntrls.itemsType.value = line.itemsId;
cntrls.author.value = line.authorsId;
cntrls.name.value = line.name;
cntrls.desc.value = line.desc;
cntrls.count.value = line.count;
cntrls.date.value = line.date;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
} catch {
// в случае ошибки происходит возврат к page4
goBack();
}
try {
// вызов метода REST API для получения записи по первичному ключу(id)
const line = await getLine(currentId);
// заполнение формы для редактирования
cntrls.nameBook.value = line.nameBook;
cntrls.authorsType.value = line.authorsType;
cntrls.genresType.value = line.genresType;
cntrls.year.value = line.year;
cntrls.description.value = line.description;
cntrls.count.value = line.count;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = line.image ? line.image : imagePlaceholder;
} catch {
// в случае ошибки происходит возврат к index
goBack();
}
}
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// если значение параметра запроса не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.itemsType.value,
cntrls.author.value,
cntrls.name.value,
cntrls.desc.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.itemsType.value,
cntrls.author.value,
cntrls.name.value,
cntrls.desc.value,
cntrls.count.value,
cntrls.date.value,
imageBase64,
);
}
// возврат к странице page4
goBack();
// если значение параметра запроса не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
if (!currentId) {
await addLine(
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.nameBook.value,
cntrls.authorsType.value,
cntrls.genresType.value,
cntrls.year.value,
cntrls.description.value,
cntrls.count.value,
imageBase64,
);
}
// возврат к странице index
goBack();
});
}

@ -1,3 +1,4 @@
/* eslint-disable linebreak-style */
// модуль используется для валидации форма на странице
function validation() {

@ -1678,9 +1678,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@ -2756,9 +2756,9 @@
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"funding": [
{
@ -3229,9 +3229,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"dev": true,
"funding": [
{
@ -3248,7 +3248,7 @@
}
],
"dependencies": {
"nanoid": "^3.3.6",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},

@ -1,11 +1,11 @@
{
"hash": "2b23bead",
"browserHash": "d63b02ce",
"hash": "fa1fc037",
"browserHash": "c20cb0a6",
"optimized": {
"bootstrap": {
"src": "../../bootstrap/dist/js/bootstrap.esm.js",
"file": "bootstrap.js",
"fileHash": "c09afe4f",
"fileHash": "fba3a4d5",
"needsInterop": false
}
},

File diff suppressed because one or more lines are too long

@ -6,6 +6,29 @@ var Writable = require("stream").Writable;
var assert = require("assert");
var debug = require("./debug");
// Whether to use the native URL object or the legacy url module
var useNativeURL = false;
try {
assert(new URL());
}
catch (error) {
useNativeURL = error.code === "ERR_INVALID_URL";
}
// URL fields to preserve in copy operations
var preservedUrlFields = [
"auth",
"host",
"hostname",
"href",
"path",
"pathname",
"port",
"protocol",
"query",
"search",
];
// Create handlers that pass events from native requests
var events = ["abort", "aborted", "connect", "error", "socket", "timeout"];
var eventHandlers = Object.create(null);
@ -15,19 +38,20 @@ events.forEach(function (event) {
};
});
// Error types with codes
var InvalidUrlError = createErrorType(
"ERR_INVALID_URL",
"Invalid URL",
TypeError
);
// Error types with codes
var RedirectionError = createErrorType(
"ERR_FR_REDIRECTION_FAILURE",
"Redirected request failed"
);
var TooManyRedirectsError = createErrorType(
"ERR_FR_TOO_MANY_REDIRECTS",
"Maximum number of redirects exceeded"
"Maximum number of redirects exceeded",
RedirectionError
);
var MaxBodyLengthExceededError = createErrorType(
"ERR_FR_MAX_BODY_LENGTH_EXCEEDED",
@ -38,6 +62,9 @@ var WriteAfterEndError = createErrorType(
"write after end"
);
// istanbul ignore next
var destroy = Writable.prototype.destroy || noop;
// An HTTP(S) request that can be redirected
function RedirectableRequest(options, responseCallback) {
// Initialize the request
@ -59,7 +86,13 @@ function RedirectableRequest(options, responseCallback) {
// React to responses of native requests
var self = this;
this._onNativeResponse = function (response) {
self._processResponse(response);
try {
self._processResponse(response);
}
catch (cause) {
self.emit("error", cause instanceof RedirectionError ?
cause : new RedirectionError({ cause: cause }));
}
};
// Perform the first request
@ -68,10 +101,17 @@ function RedirectableRequest(options, responseCallback) {
RedirectableRequest.prototype = Object.create(Writable.prototype);
RedirectableRequest.prototype.abort = function () {
abortRequest(this._currentRequest);
destroyRequest(this._currentRequest);
this._currentRequest.abort();
this.emit("abort");
};
RedirectableRequest.prototype.destroy = function (error) {
destroyRequest(this._currentRequest, error);
destroy.call(this, error);
return this;
};
// Writes buffered data to the current native request
RedirectableRequest.prototype.write = function (data, encoding, callback) {
// Writing is not allowed if end has been called
@ -184,6 +224,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
self.removeListener("abort", clearTimer);
self.removeListener("error", clearTimer);
self.removeListener("response", clearTimer);
self.removeListener("close", clearTimer);
if (callback) {
self.removeListener("timeout", callback);
}
@ -210,6 +251,7 @@ RedirectableRequest.prototype.setTimeout = function (msecs, callback) {
this.on("abort", clearTimer);
this.on("error", clearTimer);
this.on("response", clearTimer);
this.on("close", clearTimer);
return this;
};
@ -268,8 +310,7 @@ RedirectableRequest.prototype._performRequest = function () {
var protocol = this._options.protocol;
var nativeProtocol = this._options.nativeProtocols[protocol];
if (!nativeProtocol) {
this.emit("error", new TypeError("Unsupported protocol " + protocol));
return;
throw new TypeError("Unsupported protocol " + protocol);
}
// If specified, use the agent corresponding to the protocol
@ -361,15 +402,14 @@ RedirectableRequest.prototype._processResponse = function (response) {
}
// The response is a redirect, so abort the current request
abortRequest(this._currentRequest);
destroyRequest(this._currentRequest);
// Discard the remainder of the response to avoid waiting for data
response.destroy();
// RFC7231§6.4: A client SHOULD detect and intervene
// in cyclical redirections (i.e., "infinite" redirection loops).
if (++this._redirectCount > this._options.maxRedirects) {
this.emit("error", new TooManyRedirectsError());
return;
throw new TooManyRedirectsError();
}
// Store the request headers if applicable
@ -403,33 +443,23 @@ RedirectableRequest.prototype._processResponse = function (response) {
var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers);
// If the redirect is relative, carry over the host of the last request
var currentUrlParts = url.parse(this._currentUrl);
var currentUrlParts = parseUrl(this._currentUrl);
var currentHost = currentHostHeader || currentUrlParts.host;
var currentUrl = /^\w+:/.test(location) ? this._currentUrl :
url.format(Object.assign(currentUrlParts, { host: currentHost }));
// Determine the URL of the redirection
var redirectUrl;
try {
redirectUrl = url.resolve(currentUrl, location);
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
return;
}
// Create the redirected request
debug("redirecting to", redirectUrl);
var redirectUrl = resolveUrl(location, currentUrl);
debug("redirecting to", redirectUrl.href);
this._isRedirect = true;
var redirectUrlParts = url.parse(redirectUrl);
Object.assign(this._options, redirectUrlParts);
spreadUrlObject(redirectUrl, this._options);
// Drop confidential headers when redirecting to a less secure protocol
// or to a different domain that is not a superdomain
if (redirectUrlParts.protocol !== currentUrlParts.protocol &&
redirectUrlParts.protocol !== "https:" ||
redirectUrlParts.host !== currentHost &&
!isSubdomain(redirectUrlParts.host, currentHost)) {
if (redirectUrl.protocol !== currentUrlParts.protocol &&
redirectUrl.protocol !== "https:" ||
redirectUrl.host !== currentHost &&
!isSubdomain(redirectUrl.host, currentHost)) {
removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers);
}
@ -444,23 +474,12 @@ RedirectableRequest.prototype._processResponse = function (response) {
method: method,
headers: requestHeaders,
};
try {
beforeRedirect(this._options, responseDetails, requestDetails);
}
catch (err) {
this.emit("error", err);
return;
}
beforeRedirect(this._options, responseDetails, requestDetails);
this._sanitizeOptions(this._options);
}
// Perform the redirected request
try {
this._performRequest();
}
catch (cause) {
this.emit("error", new RedirectionError({ cause: cause }));
}
this._performRequest();
};
// Wraps the key/value object of protocols with redirect functionality
@ -480,27 +499,16 @@ function wrap(protocols) {
// Executes a request, following redirects
function request(input, options, callback) {
// Parse parameters
if (isString(input)) {
var parsed;
try {
parsed = urlToOptions(new URL(input));
}
catch (err) {
/* istanbul ignore next */
parsed = url.parse(input);
}
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
input = parsed;
// Parse parameters, ensuring that input is an object
if (isURL(input)) {
input = spreadUrlObject(input);
}
else if (URL && (input instanceof URL)) {
input = urlToOptions(input);
else if (isString(input)) {
input = spreadUrlObject(parseUrl(input));
}
else {
callback = options;
options = input;
options = validateUrl(input);
input = { protocol: protocol };
}
if (isFunction(options)) {
@ -539,27 +547,57 @@ function wrap(protocols) {
return exports;
}
/* istanbul ignore next */
function noop() { /* empty */ }
// from https://github.com/nodejs/node/blob/master/lib/internal/url.js
function urlToOptions(urlObject) {
var options = {
protocol: urlObject.protocol,
hostname: urlObject.hostname.startsWith("[") ?
/* istanbul ignore next */
urlObject.hostname.slice(1, -1) :
urlObject.hostname,
hash: urlObject.hash,
search: urlObject.search,
pathname: urlObject.pathname,
path: urlObject.pathname + urlObject.search,
href: urlObject.href,
};
if (urlObject.port !== "") {
options.port = Number(urlObject.port);
function parseUrl(input) {
var parsed;
/* istanbul ignore else */
if (useNativeURL) {
parsed = new URL(input);
}
return options;
else {
// Ensure the URL is valid and absolute
parsed = validateUrl(url.parse(input));
if (!isString(parsed.protocol)) {
throw new InvalidUrlError({ input });
}
}
return parsed;
}
function resolveUrl(relative, base) {
/* istanbul ignore next */
return useNativeURL ? new URL(relative, base) : parseUrl(url.resolve(base, relative));
}
function validateUrl(input) {
if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
throw new InvalidUrlError({ input: input.href || input });
}
if (/^\[/.test(input.host) && !/^\[[:0-9a-f]+\](:\d+)?$/i.test(input.host)) {
throw new InvalidUrlError({ input: input.href || input });
}
return input;
}
function spreadUrlObject(urlObject, target) {
var spread = target || {};
for (var key of preservedUrlFields) {
spread[key] = urlObject[key];
}
// Fix IPv6 hostname
if (spread.hostname.startsWith("[")) {
spread.hostname = spread.hostname.slice(1, -1);
}
// Ensure port is a number
if (spread.port !== "") {
spread.port = Number(spread.port);
}
// Concatenate path
spread.path = spread.search ? spread.pathname + spread.search : spread.pathname;
return spread;
}
function removeMatchingHeaders(regex, headers) {
@ -585,17 +623,25 @@ function createErrorType(code, message, baseClass) {
// Attach constructor and set default properties
CustomError.prototype = new (baseClass || Error)();
CustomError.prototype.constructor = CustomError;
CustomError.prototype.name = "Error [" + code + "]";
Object.defineProperties(CustomError.prototype, {
constructor: {
value: CustomError,
enumerable: false,
},
name: {
value: "Error [" + code + "]",
enumerable: false,
},
});
return CustomError;
}
function abortRequest(request) {
function destroyRequest(request, error) {
for (var event of events) {
request.removeListener(event, eventHandlers[event]);
}
request.on("error", noop);
request.abort();
request.destroy(error);
}
function isSubdomain(subdomain, domain) {
@ -616,6 +662,10 @@ function isBuffer(value) {
return typeof value === "object" && ("length" in value);
}
function isURL(value) {
return URL && value instanceof URL;
}
// Exports
module.exports = wrap({ http: http, https: https });
module.exports.wrap = wrap;

@ -1,6 +1,6 @@
{
"name": "follow-redirects",
"version": "1.15.2",
"version": "1.15.4",
"description": "HTTP and HTTPS modules that follow redirects.",
"license": "MIT",
"main": "index.js",
@ -11,9 +11,8 @@
"node": ">=4.0"
},
"scripts": {
"test": "npm run lint && npm run mocha",
"lint": "eslint *.js test",
"mocha": "nyc mocha"
"test": "nyc mocha"
},
"repository": {
"type": "git",

@ -36,4 +36,4 @@ Supports modern browsers, IE [with Babel], Node.js and React Native.
</a>
## Docs
Read **[full docs](https://github.com/ai/nanoid#readme)** on GitHub.
Read full docs **[here](https://github.com/ai/nanoid#readme)**.

91
3 proba/node_modules/nanoid/index.d.cts generated vendored Normal file

@ -0,0 +1,91 @@
/**
* Generate secure URL-friendly unique ID.
*
* By default, the ID will have 21 symbols to have a collision probability
* similar to UUID v4.
*
* ```js
* import { nanoid } from 'nanoid'
* model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"
* ```
*
* @param size Size of the ID. The default size is 21.
* @returns A random string.
*/
export function nanoid(size?: number): string
/**
* Generate secure unique ID with custom alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* @param alphabet Alphabet used to generate the ID.
* @param defaultSize Size of the ID. The default size is 21.
* @returns A random string generator.
*
* ```js
* const { customAlphabet } = require('nanoid')
* const nanoid = customAlphabet('0123456789абвгдеё', 5)
* nanoid() //=> "8ё56а"
* ```
*/
export function customAlphabet(
alphabet: string,
defaultSize?: number
): (size?: number) => string
/**
* Generate unique ID with custom random generator and alphabet.
*
* Alphabet must contain 256 symbols or less. Otherwise, the generator
* will not be secure.
*
* ```js
* import { customRandom } from 'nanoid/format'
*
* const nanoid = customRandom('abcdef', 5, size => {
* const random = []
* for (let i = 0; i < size; i++) {
* random.push(randomByte())
* }
* return random
* })
*
* nanoid() //=> "fbaef"
* ```
*
* @param alphabet Alphabet used to generate a random string.
* @param size Size of the random string.
* @param random A random bytes generator.
* @returns A random string generator.
*/
export function customRandom(
alphabet: string,
size: number,
random: (bytes: number) => Uint8Array
): () => string
/**
* URL safe symbols.
*
* ```js
* import { urlAlphabet } from 'nanoid'
* const nanoid = customAlphabet(urlAlphabet, 10)
* nanoid() //=> "Uakgb_J5m9"
* ```
*/
export const urlAlphabet: string
/**
* Generate an array of random bytes collected from hardware noise.
*
* ```js
* import { customRandom, random } from 'nanoid'
* const nanoid = customRandom("abcdef", 5, random)
* ```
*
* @param bytes Size of the array.
* @returns An array of random bytes.
*/
export function random(bytes: number): Uint8Array

@ -1,6 +1,6 @@
{
"name": "nanoid",
"version": "3.3.6",
"version": "3.3.7",
"description": "A tiny (116 bytes), secure URL-friendly unique string ID generator",
"keywords": [
"uuid",
@ -35,31 +35,53 @@
"module": "index.js",
"exports": {
".": {
"types": "./index.d.ts",
"browser": "./index.browser.js",
"require": "./index.cjs",
"import": "./index.js",
"require": {
"types": "./index.d.cts",
"default": "./index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./index.js"
},
"default": "./index.js"
},
"./index.d.ts": "./index.d.ts",
"./package.json": "./package.json",
"./async/package.json": "./async/package.json",
"./async": {
"browser": "./async/index.browser.js",
"require": "./async/index.cjs",
"import": "./async/index.js",
"require": {
"types": "./index.d.cts",
"default": "./async/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./async/index.js"
},
"default": "./async/index.js"
},
"./non-secure/package.json": "./non-secure/package.json",
"./non-secure": {
"require": "./non-secure/index.cjs",
"import": "./non-secure/index.js",
"require": {
"types": "./index.d.cts",
"default": "./non-secure/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./non-secure/index.js"
},
"default": "./non-secure/index.js"
},
"./url-alphabet/package.json": "./url-alphabet/package.json",
"./url-alphabet": {
"require": "./url-alphabet/index.cjs",
"import": "./url-alphabet/index.js",
"require": {
"types": "./index.d.cts",
"default": "./url-alphabet/index.cjs"
},
"import": {
"types": "./index.d.ts",
"default": "./url-alphabet/index.js"
},
"default": "./url-alphabet/index.js"
}
}

@ -13,7 +13,7 @@ and JetBrains. The [Autoprefixer] and [Stylelint] PostCSS plugins is one of
---
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" />  Made in <b><a href="https://evilmartians.com/?utm_source=postcss&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.
<img src="https://cdn.evilmartians.com/badges/logo-no-label.svg" alt="" width="22" height="16" />  Made in <b><a href="https://evilmartians.com/devtools?utm_source=postcss&utm_campaign=devtools-button&utm_medium=github">Evil Martians</a></b>, product consulting for <b>developer tools</b>.
---

@ -16,6 +16,7 @@ class MapGenerator {
this.root = root
this.opts = opts
this.css = cssString
this.originalCSS = cssString
this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute
this.memoizedFileURLs = new Map()
@ -74,7 +75,7 @@ class MapGenerator {
}
}
} else if (this.css) {
this.css = this.css.replace(/(\n)?\/\*#[\S\s]*?\*\/$/gm, '')
this.css = this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm, '')
}
}
@ -276,7 +277,7 @@ class MapGenerator {
}
})
} else {
let input = new Input(this.css, this.opts)
let input = new Input(this.originalCSS, this.opts)
if (input.map) this.previousMaps.push(input.map)
}
}

@ -37,6 +37,9 @@ class NoWorkResult {
if (generatedMap) {
this.result.map = generatedMap
}
} else {
map.clearAnnotation();
this.result.css = map.css;
}
}

@ -309,7 +309,7 @@ declare namespace postcss {
* The path of the CSS source file. You should always set `from`,
* because it is used in source map generation and syntax error messages.
*/
from?: string
from?: string | undefined
/**
* Source map options

@ -7,7 +7,7 @@ let Root = require('./root')
class Processor {
constructor(plugins = []) {
this.version = '8.4.30'
this.version = '8.4.33'
this.plugins = this.normalize(plugins)
}
@ -43,10 +43,10 @@ class Processor {
process(css, opts = {}) {
if (
this.plugins.length === 0 &&
typeof opts.parser === 'undefined' &&
typeof opts.stringifier === 'undefined' &&
typeof opts.syntax === 'undefined'
!this.plugins.length &&
!opts.parser &&
!opts.stringifier &&
!opts.syntax
) {
return new NoWorkResult(this, css, opts)
} else {

@ -22,7 +22,7 @@ const AT = '@'.charCodeAt(0)
const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g
const RE_WORD_END = /[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g
const RE_BAD_BRACKET = /.[\n"'(/\\]/
const RE_BAD_BRACKET = /.[\r\n"'(/\\]/
const RE_HEX_ESCAPE = /[\da-f]/i
module.exports = function tokenizer(input, options = {}) {

@ -1,6 +1,6 @@
{
"name": "postcss",
"version": "8.4.30",
"version": "8.4.33",
"description": "Tool for transforming styles with JS plugins",
"engines": {
"node": "^10 || ^12 || >=14"
@ -74,7 +74,7 @@
"url": "https://github.com/postcss/postcss/issues"
},
"dependencies": {
"nanoid": "^3.3.6",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},

@ -2031,9 +2031,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@ -3123,9 +3123,9 @@
"dev": true
},
"node_modules/nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true,
"funding": [
{
@ -3596,9 +3596,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"dev": true,
"funding": [
{
@ -3615,7 +3615,7 @@
}
],
"dependencies": {
"nanoid": "^3.3.6",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
@ -6058,9 +6058,9 @@
"dev": true
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true
},
"for-each": {
@ -6866,9 +6866,9 @@
"dev": true
},
"nanoid": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"dev": true
},
"natural-compare": {
@ -7216,12 +7216,12 @@
}
},
"postcss": {
"version": "8.4.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"dev": true,
"requires": {
"nanoid": "^3.3.6",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}

@ -50,13 +50,13 @@
</div>
</div>
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
Жирнова Алена ПИбд-21
</footer>
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
Жирнова Алена ПИбд-21
</footer>
<script type="module">
import validation from "./scripts/validation";
import { linesForm } from "./scripts/lines";
import {linesFormOnIndex} from "./scripts/lines";
import validation from "./js/validation";
import { linesForm } from "./js/lines";
import {linesFormOnIndex} from "./js/lines";
document.addEventListener('DOMContentLoaded', () => {
validation();
linesFormOnIndex();

@ -37,146 +37,94 @@
<button id="items-add" type="submit" class="btn btn-success">
Добавить книгу (диалог)
</button>
<a class="btn btn-secondary" href="/page-edit.html"
>Добавить книгу (страница)</a
>
<div>
<table id="items-table" class="table table-light table-striped">
<table id="items-table" class="table table-hover">
<thead>
<th scope="col"></th>
<th scope="col" class="w-25">Жанр</th>
<th scope="col" class="w-25">Автор</th>
<th scope="col" class="w-25">Название</th>
<th scope="col" class="w-25">Описание</th>
<th scope="col" class="w-25">Количество</th>
<th scope="col" class="w-25">Дата</th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col"></th>
<th scope="col" class="w-25">Название</th>
<th scope="col" class="w-25">Автор</th>
<th scope="col" class="w-25">Жанр</th>
<th scope="col" class="w-15">Год выпуска</th>
<th scope="col" class="w-25">Описание</th>
<th scope="col" class="w-25">Кол-во</th>
<th scope="col"></th>
<th scope="col"></th>
</thead>
<tbody></tbody>
</table>
</div>
</main>
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
Жирнова Алена ПИбд-21
</footer>
<div
id="items-update"
class="modal fade"
tabindex="-1"
data-bs-backdrop="static"
data-bs-keyboard="false"
>
<div class="modal-dialog">
<tbody></tbody>
</table>
</div>
</div>
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
Жирнова Алена ПИбд-21
</footer>
<div id="items-update" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<form id="items-form" class="needs-validation" novalidate>
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="items-update-title"></h1>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="items-update-title"></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<img id="image-preview" src="https://via.placeholder.com/200" class="rounded"
alt="placeholder">
</div>
<input id="items-line-id" type="number" hidden>
<div class="mb-2">
<label for="nameBook" class="form-label">Название</label>
<input id="nameBook" class="form-control" name="nameBook" type="text" required="">
</div>
<div class="mb-2">
<label for="author" class="form-label">Автор</label>
<select id="author" class="form-select" name="author" required>
</div>
<input id="genres-line-id" type="number" hidden>
<div class="mb-2">
<label for="genre" class="form-label">Жанр</label>
<select id="genre" class="form-select" name="genre" required>
</select>
</div>
<div class="mb-2">
<label for="description" class="form-label">Описание</label>
<input id="description" class="form-control" name="description" type="text" required="">
</div>
<div class="mb-2">
<label for="yearPicker" class="form-label">Год выпуска</label>
<input id="yearPicker" class="form-control" name="yearPicker" type="text" required="">
</div>
<div class="mb-2">
<label for="count" class="form-label">Количество книг</label>
<input
id="count"
name="count"
class="form-control"
type="number"
value="0"
min="1"
step="1"
required
/>
</div>
<div class="mb-2">
<label for="image" class="form-label">Изображение</label>
<input id="image" type="file" name="image" class="form-control" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button>
</div>
</div>
<div class="modal-body">
<div class="text-center">
<img
id="image-preview"
src="https://via.placeholder.com/200"
class="rounded rounded-circle"
alt="placeholder"
/>
</div>
<input id="items-line-id" type="number" hidden />
<div class="mb-2">
<label for="item" class="form-label">Жанр</label>
<select
id="item"
class="form-select"
name="selected"
required
></select>
<div class="mb-2">
<label for="author" class="form-label">Автор</label>
<select
id="author"
class="form-select"
name="selected"
required>
</select>
</div>
<div class="mb-2">
<label for="name" class="form-label">Название</label>
<input
id="name"
name="name"
class="form-control"
type="text"
maxlength="100"
required
/>
</div>
<div class="mb-2">
<label for="desc" class="form-label">Описание</label>
<input
id="desc"
name="desc"
class="form-control"
type="text"
maxlength="100"
required
/>
</div>
<div class="mb-2">
<label for="count" class="form-label">Количество книг</label>
<input
id="count"
name="count"
class="form-control"
type="number"
value="0"
min="1"
step="1"
required
/>
</div>
<div class="mb-2">
<label for="date" class="form-label">Дата</label>
<input
id="date"
name="date"
class="form-control"
type="date"
value="2022-01-01"
required
/>
</div>
<div class="mb-2">
<label for="image" class="form-label">Изображение</label>
<input
id="image"
type="file"
name="image"
class="form-control"
accept="image/*"
/>
</div></div>
<div class="modal-footer">
<button
type="submit-grey"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Закрыть
</button>
<button type="submit" class="btn btn-success">Сохранить</button>
</div>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
<script type="module">
import validation from "./js/validation";
import { linesForm } from "./js/lines";