This commit is contained in:
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

View File

@@ -1,455 +1,67 @@
/* eslint-disable linebreak-style */ /* eslint-disable linebreak-style */
// модуль с логикой // Модуль для работы с модальным окном
// eslint-disable-next-line linebreak-style
// eslint-disable-next-line import/no-self-import // импорт компонента Modal из bootstrap
import { import { Modal } from "bootstrap";
// eslint-disable-next-line import/named import { cntrls, imagePlaceholder } from "./lines-ui";
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";
async function drawGenreSelect() { // поиск модального окна на странице
// вызов метода REST API для получения списка типов товаров const modal = document.getElementById("items-update");
const data = await getAllGengeTypes(); // если он найден, то создается экземпляр компонента Modal
const data2 = await getAllAuthorTypes(); // для программного управления модальным окном
// очистка содержимого select const myModal = modal ? new Modal(modal, {}) : null;
// удаляется все, что находится между тегами <select></select>
// но не атрибуты // поиск тега с заголовком модального кона для его смены
cntrls.genresType.innerHTML = ""; const modalTitle = document.getElementById("items-update-title");
cntrls.authorsType.innerHTML = "";
// пустое значение // обнуление значений модального окна, т. к.
cntrls.genresType.appendChild(createGenresOption("Выберите значение", "", true)); // используется одно окно для всех операций
cntrls.authorsType.appendChild(createGenresOption("Выберите значение", "", true)); function resetValues() {
// цикл по результату ответа от сервера cntrls.lineId.value = "";
// используется лямбда-выражение cntrls.nameBook.value = "";
// (item) => {} аналогично function(item) {} cntrls.authorsType.value = "";
data.forEach((genre) => { cntrls.genresType.value = "";
cntrls.genresType.appendChild(createGenresOption(genre.name, genre.id)); cntrls.year.value = "";
}); cntrls.description.value = "";
data2.forEach((author) => { cntrls.count.value = "";
cntrls.authorsType.appendChild(createAuthorsOption(author.name, author.id)); cntrls.image.value = "";
}); cntrls.imagePreview.src = imagePlaceholder;
} }
async function drawLinesTable() { // функция для показа модального окна
console.info("Try to load data"); // перед показом происходит заполнение формы для редактирования
if (!cntrls.table) { // если объект item не пуст
return; export function showUpdateModal(item) {
} modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
// вызов метода REST API для получения всех записей console.info(item);
const data = await getAllLines();
// очистка содержимого table if (item) {
// удаляется все, что находится между тегами <table></table> cntrls.lineId.value = item.id;
// но не атрибуты cntrls.nameBook.value = item.nameBook;
cntrls.table.innerHTML = ""; cntrls.authorsType.value = item.authorsId;
// цикл по результату ответа от сервера cntrls.genresType.value = item.genresId;
// используется лямбда-выражение cntrls.year.value = item.year;
// (item, index) => {} аналогично function(item, index) {} cntrls.description.value = item.description;
data.forEach((item, index) => { cntrls.count.value = item.count;
cntrls.table.appendChild( // заполнение превью
createTableRow( // Если пользователь выбрал изображение, то оно загружается
item, // в тэг image с id image - preview
index, // иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
// функции передаются в качестве параметра cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
// это очень удобно, так как аргументы функций доступны только } else {
// в данном месте кода и не передаются в сервисные модули resetValues();
() => showUpdateModal(item), }
() => removeLine(item.id),
), myModal.show();
);
});
} }
async function drawLinesTableOnIndex() { // функция для скрытия модального окна
console.info("Try to load data On Index"); export function hideUpdateModal() {
if (!cntrls.container) { resetValues();
return;
} // удаление класса was-validated для скрытия результатов валидации
// вызов метода REST API для получения всех записей cntrls.form.classList.remove("was-validated");
const data = await getAllLines();
// очистка содержимого table myModal.hide();
// удаляется все, что находится между тегами <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();
});
} }

View File

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

View File

@@ -1,7 +1,10 @@
/* eslint-disable linebreak-style */ /* 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"), year: document.getElementById("yearPicker"),
description: document.getElementById("description"), description: document.getElementById("description"),
count: document.getElementById("count"), count: document.getElementById("count"),
date: document.getElementById("date"),
image: document.getElementById("image"), image: document.getElementById("image"),
imagePreview: document.getElementById("image-preview"), 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.year));
row.appendChild(createTableColumn(item.description)); row.appendChild(createTableColumn(item.description));
row.appendChild(createTableColumn(item.count)); row.appendChild(createTableColumn(item.count));
row.appendChild(createTableColumn(item.date));
// редактировать в модальном окне // редактировать в модальном окне
row.appendChild(createTableAnchor("fa-pencil", editCallback)); row.appendChild(createTableAnchor("fa-pencil", editCallback));
@@ -110,13 +111,15 @@ export function createTableRow(item, index, editCallback, deleteCallback) {
return row; return row;
} }
export function createTableRowOnIndex(item) { export function createTableRowOnIndex(item, index, editCallback, editPageCallback, deleteCallback) {
console.log(item); console.log(item);
const bookCard = createBookCard(item); const bookCard = createBookCard(item);
return bookCard; return bookCard;
} }
// eslint-disable-next-line no-multiple-empty-lines
function createBookCard(item) { function createBookCard(item) {
const card = document.createElement("div"); const card = document.createElement("div");
card.classList.add("book"); card.classList.add("book");
@@ -128,14 +131,29 @@ function createBookCard(item) {
const title = document.createElement("h3"); const title = document.createElement("h3");
title.textContent = `${item.nameBook}, ${item.authors.name}`; 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(image);
card.appendChild(title); card.appendChild(title);
card.appendChild(detailsButton);
card.appendChild(favoriteButton);
const div = document.createElement("div"); const div = document.createElement("div");
div.classList.add("col-12"); 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-lg-4");
div.classList.add("col-xl-3"); div.classList.add("col-xl-3");
div.appendChild(card); div.appendChild(card);
return div; return div;
} }

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -36,4 +36,4 @@ Supports modern browsers, IE [with Babel], Node.js and React Native.
</a> </a>
## Docs ## 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
View 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

View File

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

View File

@@ -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>.
--- ---

View File

@@ -16,6 +16,7 @@ class MapGenerator {
this.root = root this.root = root
this.opts = opts this.opts = opts
this.css = cssString this.css = cssString
this.originalCSS = cssString
this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute this.usesFileUrls = !this.mapOpts.from && this.mapOpts.absolute
this.memoizedFileURLs = new Map() this.memoizedFileURLs = new Map()
@@ -74,7 +75,7 @@ class MapGenerator {
} }
} }
} else if (this.css) { } 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 { } 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) if (input.map) this.previousMaps.push(input.map)
} }
} }

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ const AT = '@'.charCodeAt(0)
const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g const RE_AT_END = /[\t\n\f\r "#'()/;[\\\]{}]/g
const RE_WORD_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 const RE_HEX_ESCAPE = /[\da-f]/i
module.exports = function tokenizer(input, options = {}) { module.exports = function tokenizer(input, options = {}) {

View File

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

View File

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

View File

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

View File

@@ -37,146 +37,94 @@
<button id="items-add" type="submit" class="btn btn-success"> <button id="items-add" type="submit" class="btn btn-success">
Добавить книгу (диалог) Добавить книгу (диалог)
</button> </button>
<a class="btn btn-secondary" href="/page-edit.html"
>Добавить книгу (страница)</a
>
<div> <div>
<table id="items-table" class="table table-light table-striped"> <table id="items-table" class="table table-hover">
<thead> <thead>
<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-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-15">Год выпуска</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"></th>
</thead> </thead>
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
</main> </div>
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
Жирнова Алена ПИбд-21 <footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center fixed-bottom">
</footer> Жирнова Алена ПИбд-21
<div </footer>
id="items-update"
class="modal fade"
tabindex="-1" <div id="items-update" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
data-bs-backdrop="static" <div class="modal-dialog">
data-bs-keyboard="false"
>
<div class="modal-dialog">
<form id="items-form" class="needs-validation" novalidate> <form id="items-form" class="needs-validation" novalidate>
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h1 class="modal-title fs-5" id="items-update-title"></h1> <h1 class="modal-title fs-5" id="items-update-title"></h1>
<button <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
type="button" </div>
class="btn-close" <div class="modal-body">
data-bs-dismiss="modal" <div class="text-center">
aria-label="Close" <img id="image-preview" src="https://via.placeholder.com/200" class="rounded"
></button> 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>
<div class="modal-body"> </form>
<div class="text-center"> </div>
<img </div>
id="image-preview" </form>
src="https://via.placeholder.com/200" </div>
class="rounded rounded-circle" </div>
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>
<script type="module"> <script type="module">
import validation from "./js/validation"; import validation from "./js/validation";
import { linesForm } from "./js/lines"; import { linesForm } from "./js/lines";