Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/itchief/feedbackForm.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoritchief <alex.malcev1980@gmail.com>2022-03-09 07:56:11 +0300
committeritchief <alex.malcev1980@gmail.com>2022-03-09 07:56:11 +0300
commit42bfc7e4b18127401d74d7d77d9193331e62a4bf (patch)
treebd88cc22f8a87c8968db781632f8f65f4773d87e
parent593a44cf1dc1bb70c8489729b23cf1616e3bc31c (diff)
Updatev4.0.0
-rw-r--r--feedback/css/style.css160
-rw-r--r--feedback/index.html72
-rw-r--r--feedback/js/process-forms-without-jquery.js474
-rw-r--r--feedback/js/process-forms.back.js381
-rw-r--r--feedback/js/process-forms.dev.js420
-rw-r--r--feedback/js/process-forms.js529
-rw-r--r--feedback/process/process.php24
7 files changed, 370 insertions, 1690 deletions
diff --git a/feedback/css/style.css b/feedback/css/style.css
index c46eb52..6488c18 100644
--- a/feedback/css/style.css
+++ b/feedback/css/style.css
@@ -198,22 +198,22 @@ textarea.form-control {
}
/* Стили для секции, с помощью которой можно добавить к форме файлы */
-.form__attach-label {
+.form-attach__label {
margin-bottom: 0.5rem;
}
-.form-attachments__wrapper {
+.form-attach__wrapper {
position: relative;
border: 2px dashed #e0e0e0;
border-radius: 0.375rem;
min-height: 50px;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 4px;
}
-.form-attachments__wrapper input {
+.is-invalid .form-attach__wrapper {
+ border-color: #dc3545;
+}
+
+.form-attach__wrapper input {
position: absolute;
top: 0;
left: 0;
@@ -226,30 +226,23 @@ textarea.form-control {
display: block;
}
-.form-attachments__description {
- width: 100%;
+.form-attach__description {
text-align: center;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 15px 10px;
+ padding: 1rem 0.5rem;
color: #757575;
}
-.form-attachments__items {
+.form-attach__items {
display: flex;
flex-wrap: wrap;
- flex: 0 0 100%;
}
-.form-attachments__item {
+.form-attach__item {
flex: 0 0 25%;
overflow: hidden;
- padding: 4px;
+ flex: 0 1 calc((100% / 4) - 0.5rem);
+ margin: 0.25rem;
font-size: 0.75rem;
-}
-
-.form-attachments__item-wrapper {
border: 1px solid #eee;
padding: 1.625rem 0.25rem;
border-radius: 0.25rem;
@@ -261,7 +254,7 @@ textarea.form-control {
justify-content: center;
}
-.form-attachments__item-image {
+.form-attach__image {
display: block;
max-width: 100%;
height: auto;
@@ -271,7 +264,7 @@ textarea.form-control {
border: 1px solid #eee;
}
-.form-attachments__item-name {
+.form-attach__name {
margin-top: auto;
max-width: 100%;
overflow: hidden;
@@ -280,7 +273,7 @@ textarea.form-control {
text-align: center;
}
-.form-attachments__item-size {
+.form-attach__size {
position: absolute;
bottom: 0;
left: 0;
@@ -290,7 +283,7 @@ textarea.form-control {
text-align: right;
}
-.form-attachments__item-link {
+.form-attach__link {
position: absolute;
top: 0;
right: 0;
@@ -306,14 +299,12 @@ textarea.form-control {
opacity: .5;
}
-.form-attachments__item.is-valid .form-attachments__item-wrapper {
+.form-attach__item.is-valid {
border-color: #28a745;
- background-color: #f8fcf9;
}
-.form-attachments__item.is-invalid .form-attachments__item-wrapper {
+.form-attach__item.is-invalid {
border-color: #dc3545;
- background-color: #fefbfb;
}
.text-sm {
@@ -368,6 +359,76 @@ textarea.form-control {
}
/* CSS для секции "Пользовтельское соглашение" */
+.form-check {
+ display: block;
+ min-height: 1.5rem;
+ padding-left: 1.5em;
+}
+
+.form-check-input.is-invalid,
+.was-validated .form-check-input:invalid {
+ border-color: #dc3545;
+}
+
+.form-check-input {
+ width: 1em;
+ height: 1em;
+ margin-top: 0.25em;
+ vertical-align: top;
+ background-color: #fff;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ border: 1px solid rgba(0, 0, 0, .25);
+ border-radius: 0.25em;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ -webkit-print-color-adjust: exact;
+ color-adjust: exact;
+}
+
+.form-check .form-check-input {
+ float: left;
+ margin-left: -1.5em;
+}
+
+.form-check-input.is-invalid~.form-check-label,
+.was-validated .form-check-input:invalid~.form-check-label {
+ color: #dc3545;
+}
+
+.form-check-input:checked[type=checkbox] {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
+}
+
+.form-check-input:checked {
+ border-color: #9e9e9e;
+ background-color: #9e9e9e;
+}
+
+.form-check-label {
+ margin-bottom: 0;
+}
+
+.is-invalid~.invalid-feedback,
+.is-invalid~.invalid-tooltip,
+.was-validated :invalid~.invalid-feedback,
+.was-validated :invalid~.invalid-tooltip {
+ display: block;
+}
+
+.invalid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: .875em;
+ color: #dc3545;
+}
+
+
+
+
.custom-control {
position: relative;
display: block;
@@ -499,7 +560,8 @@ textarea.form-control.is-invalid {
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, .25);
}
-.form-control.is-invalid~.invalid-feedback {
+.form-control.is-invalid~.invalid-feedback,
+.is-invalid .invalid-feedback {
display: block;
}
@@ -594,8 +656,13 @@ button[type="submit"]:disabled {
border: 1px solid #dc3545;
}
-/* Стили для сообщения об успешной отправки */
-.form-result-success {
+.form-error_hide {
+ display: none;
+}
+
+
+/* сообщение об успешной отправки формы */
+.form-success {
position: absolute;
top: 0;
left: 0;
@@ -604,27 +671,28 @@ button[type="submit"]:disabled {
z-index: 1000;
display: flex;
text-align: center;
- justify-content: center;
align-items: center;
- color: #fff;
- background: rgba(0, 0, 0, .6);
+ background: rgba(0, 0, 0, .7);
font-size: 1.25rem;
- border-radius: 4px;
+ border-radius: 0.25rem;
+}
+
+.form-success_hide {
+ display: none;
}
-.form-result-success>div {
+.form-success__message {
position: relative;
- padding: .75rem 1.25rem;
- margin-bottom: 1rem;
- border: 1px solid #eff4f1;
- z-index: 1001;
- border-radius: 0;
- color: #28a745;
- background-color: #eff4f1;
+ padding: 1rem;
+ background-color: #fafafa;
}
-.form-result-success a {
- color: #28a745;
+.form-success__btn {
+ font-weight: 400;
+ color: #0d6efd;
+ text-decoration: underline;
+ border: none;
background-color: transparent;
- font-weight: 700;
+ display: inline;
+ padding: 0;
}
diff --git a/feedback/index.html b/feedback/index.html
index bb49b39..63255b3 100644
--- a/feedback/index.html
+++ b/feedback/index.html
@@ -12,7 +12,7 @@
<div class="container">
<h1>Форма обратной связи</h1>
- <div class="form__wrapper">
+ <div class="form-container form__wrapper">
<!-- Форма обратной связи -->
<form id="feedback-form" action="/feedback/process/process.php" enctype="multipart/form-data" novalidate>
<div class="form-row">
@@ -41,17 +41,17 @@
</div>
<!-- Файлы, для прикрепления к форме -->
- <div class="form-group form-attachments" data-count="5">
- <div class="form__attach-label">Файлы (не более 5)</div>
- <div class="form-attachments__wrapper">
- <input type="file" name="attachment[]" multiple>
- <div class="form-attachments__items">
- <div class="form-attachments__description">
- <div>Нажмите для загрузки файлов или перетащите их</div>
- <div class="text-sm">PNG, JPG, GIF (до 512 Кбайт)</div>
- </div>
+ <div class="form-group form-attach" data-count="5">
+ <div class="form-attach__label">Файлы (не более <span class="form-attach__count">5</span>)</div>
+ <div class="form-attach__wrapper">
+ <input type="file" name="attach[]" multiple required>
+ <div class="form-attach__description">
+ <div>Нажмите для загрузки файлов или перетащите их</div>
+ <div class="text-sm">PNG, JPG, GIF (до 512 Кбайт)</div>
</div>
+ <div class="form-attach__items"></div>
</div>
+ <div class="invalid-feedback"></div>
</div>
<!-- Капча -->
@@ -74,37 +74,33 @@
</div>
<!-- Пользовательское солашение -->
- <div class="form-group form-agreement">
- <div class="custom-control custom-checkbox">
- <input type="checkbox" name="agree" class="custom-control-input" id="customCheck">
- <label class="custom-control-label" for="customCheck">Нажимая кнопку, я принимаю условия <a
- href="#">Пользовательского соглашения</a> и даю своё согласие на обработку моих персональных данных, в
- соответствии с Федеральным законом от 27.07.2006 года №152-ФЗ «О персональных данных».</label>
- </div>
+ <div class="form-group form-agree form-check">
+ <input class="form-check-input" type="checkbox" name="agree" id="agree" required>
+ <label class="form-check-label" for="agree">Нажимая кнопку, я принимаю условия <a href="#">Пользовательского
+ соглашения</a> и даю своё согласие на обработку моих персональных данных</label>
+ <div class="invalid-feedback"></div>
</div>
- <!-- Сообщение при ошибке -->
- <div class="form-error d-none">
- Исправьте данные и отправьте форму ещё раз.
- </div>
+ <!-- Сообщение об ошибке -->
+ <div class="form-error form-error_hide">Исправьте данные и отправьте форму ещё раз.</div>
<!-- Индикация отправки данных формы на сервер -->
- <div class="progress d-none">
+ <div class="progress form-progress d-none">
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
style="width: 0"></div>
</div>
<!-- Кнопка для отправки формы на сервер -->
<div class="form-submit">
- <button type="submit" disabled>Отправить сообщение</button>
+ <button type="submit">Отправить сообщение</button>
</div>
</form>
<!-- Сообщение об успешной отправки формы -->
- <div class="form-result-success d-none">
- <div>Форма успешно отправлена. Нажмите на <a href="#" data-target="#feedback-form">ссылку</a>, чтобы отправить
- ещё одно сообщение.</div>
+ <div class="form-success form-success_hide">
+ <div class="form-success__message">Форма успешно отправлена. Нажмите <button type="button"
+ class="form-success__btn">здесь</button>, если нужно отправить ещё одну форму.</div>
</div>
</div>
@@ -121,12 +117,26 @@
...
}
Основные параметры
- selector - селектор формы (по умолчанию '#feedback-form')
- attachmentsMaxFileSize - максимальный размер файла в Кбайтах (по умолчанию 512)
- attachmentsFileExt - допустимые расширения файлов для загрузки (по умолчанию 'jpg','jpeg','bmp','gif','png')
- isUseDefaultSuccessMessage - отображать дефолтное сообщение после отправки
+ attachMaxItems: 3,
+ attachMaxFileSize: 128,
+ attachExt: ['png', 'jpg']
*/
- new ProcessForm();
+
+ const form = new ItcSubmitForm('form', {
+ isCheckValidationOnClient: true
+ });
+
+ // при получении ответа result="success" от сервера
+ document.querySelector('form').addEventListener('success', (e) => {
+ const el = e.target.closest('.form-container').querySelector('.form-success');
+ el.classList.remove('form-success_hide');
+ });
+
+ // при клике на .form-success__btn
+ document.querySelector('.form-success__btn').addEventListener('click', (e) => {
+ form.reset();
+ e.target.closest('.form-container').querySelector('.form-success').classList.add('form-success_hide');
+ })
</script>
</body>
diff --git a/feedback/js/process-forms-without-jquery.js b/feedback/js/process-forms-without-jquery.js
deleted file mode 100644
index 853c541..0000000
--- a/feedback/js/process-forms-without-jquery.js
+++ /dev/null
@@ -1,474 +0,0 @@
-/*!
- * Форма обратной связи (https://github.com/itchief/feedback-form)
- * Страница с описанием: https://itchief.ru/lessons/php/feedback-form-for-website
- * Copyright 2016-2020 Alexander Maltsev
- * Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE)
- */
-
-'use strict';
-
-var ProcessForm = function (settings) {
- this._settings = {
- selector: '#feedback-form', // дефолтный селектор
- attachmentsMaxFileSize: 512, // дефолтный максимальный размер файла в Кб
- attachmentsFileExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов
- isUseDefaultSuccessMessage: true, // отображать дефолтное сообщение об успешной отправки формы
- };
- this._isCaptchaSection = false; // имеется ли в форме блок с капчей
- this._isAgreementSection = false; // имеется ли в форме блок с пользовательским соглашением
- this._isAttachmentsSection = false; // имеется ли в форме блок для добавления к ней файлов
- this._attachmentsIdCounter = 0; // счетчик, хранящий количество добавленных к форме файлов
- this._attachmentsMaxItems = 5; // переменная, определяющее максимальное количество файлов, которые можно прикрепить к форме
- this._attachmentsItems = []; // переменная, хранящая массив файлов, которые нужно прекрепить к форме
-
- for (var propName in settings) {
- if (settings.hasOwnProperty(propName)) {
- this._settings[propName] = settings[propName];
- }
- }
- this._$form = document.querySelector(this._settings.selector);
- // инициализация формы
- this._init();
-};
-
-// функция для проверки расширения файла
-ProcessForm.validateFileExtension = function (filename, validFileExtensions) {
- // получаем расширение файла
- var fileExtension = filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
- // если есть расширение, то проверяем соответствует ли оно допустимому
- if (fileExtension) {
- for (var i = 0; i <= validFileExtensions.length; i++) {
- if (validFileExtensions[i] === fileExtension.toLowerCase()) {
- return true;
- }
- }
- }
- return false;
-};
-
-// переключение состояния disabled у кнопки submit
-ProcessForm.prototype._changeStateSubmit = function (state) {
- this._$form.querySelector('[type="submit"]').disabled = state;
-};
-
-// обновление капчи
-ProcessForm.prototype._refreshCaptcha = function () {
- var captchaImg = this._$form.querySelector('.form-captcha__image');
- var captchaSrc = captchaImg.getAttribute('data-src');
- var captchaPrefix = captchaSrc.indexOf('?id') !== -1 ? '&rnd=' : '?rnd=';
- var captchaNewSrc = captchaSrc + captchaPrefix + new Date().getTime();
- captchaImg.setAttribute('src', captchaNewSrc);
-};
-
-// изменение состояния элемента формы (success, error, clear)
-ProcessForm.prototype._setStateValidaion = function (input, state, message) {
- if (state === 'error') {
- input.classList.remove('is-valid');
- input.classList.add('is-invalid');
- input.parentElement.querySelector('.invalid-feedback').textContent = message;
- } else if (state === 'success') {
- input.classList.remove('is-invalid');
- input.classList.add('is-valid');
- } else {
- input.classList.remove('is-valid');
- input.classList.remove('is-invalid');
- }
-};
-
-// валидация формы
-ProcessForm.prototype._validateForm = function () {
- var valid = true;
- var $elements = this._$form.querySelectorAll('input, textarea');
- for (var i = 0; i < $elements.length; i++) {
- if ($elements[i].type === 'file' || $elements[i].name === 'agree') {
- continue;
- }
- if ($elements[i].checkValidity()) {
- this._setStateValidaion($elements[i], 'success');
- } else {
- this._setStateValidaion($elements[i], 'error', $elements[i].validationMessage);
- valid = false;
- }
- }
-
- // для теста >
- if (this._attachmentsItems.length === 0) {
- var files = this._attachmentsItems;
- for (var i = 0, length = files.length; i < length; i++) {
- // проверим размер и расширение файла
- if (files[i].file.size > this._settings.attachmentsMaxFileSize * 1024) {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- attach.setAttribute(
- 'title',
- 'Размер файла больше ' + this._settings.attachmentsMaxFileSize + 'Кб'
- );
- attach.classList.add('is-invalid');
- valid = false;
- } else if (
- !ProcessForm.validateFileExtension(files[i].file.name, this._settings.attachmentsFileExt)
- ) {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- attach.setAttribute('title', 'Тип не соответствует разрешённым');
- attach.classList.add('is-invalid');
- valid = false;
- } else {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- if (attach) {
- attach.setAttribute('title', '');
- attach.classList.add('is-valid');
- }
- }
- }
- }
- return valid;
-};
-
-ProcessForm.prototype._showForm = function () {
- this._$form.querySelector('.form-error').classList.add('d-none');
- var $resultSuccess = this._$form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.add('d-none');
- $resultSuccess.classList.remove('d-flex');
- this._$form.reset();
- var $elements = this._$form.querySelectorAll('input, textarea');
- for (var i = 0, length = $elements.length; i < length; i++) {
- this._setStateValidaion($elements[i], 'clear');
- }
- if (this._isCaptchaSection) {
- this._refreshCaptcha();
- }
- if (this._isAgreementSection) {
- this._changeStateSubmit(true);
- } else {
- this._changeStateSubmit(false);
- }
-
- // удаление attachment items
- var $attachs = this._$form.querySelectorAll('.form-attachments__item');
- if ($attachs.length > 0) {
- for (var i = 0, length = $attachs.length; i < length; i++) {
- $attachs[i].parentElement.removeChild($attachs[i]);
- }
- }
-
- if (this._$form.querySelector('.progress-bar').length) {
- var $progressBar = this._$form.querySelector('.progress-bar');
- $progressBar.setAttribute('aria-valuenow', '0');
- $progressBar.style.width = 0;
- }
-};
-
-// переключение disabled для name="attachment[]"
-ProcessForm.prototype._disabledAttach = function (state) {
- var $attach = this._$form.querySelector('[name="attachment[]"]');
- if ($attach) {
- $attach.disabled = state;
- }
-};
-
-// собираем данные для отправки на сервер
-ProcessForm.prototype._collectData = function () {
- var data;
- var $attachs = this._attachmentsItems;
- // отключаем добавление данных из name="attachment[]" в FormData
- this._disabledAttach(true);
- data = new FormData(this._$form);
- // включаем доступность name="attachment[]"
- this._disabledAttach(false);
- // добавляем данные из name="attachment[]" в FormData
- for (var i = 0, length = $attachs.length; i < length; i++) {
- data.append('attachment[]', $attachs[i].file);
- }
- return data;
-};
-
-// отправка формы
-ProcessForm.prototype._sendForm = function () {
- this._$form.dispatchEvent(new Event('beforeSubmit'));
- if (!this._validateForm()) {
- if (this._$form.querySelectorAll('.is-invalid').length > 0) {
- if (this._$form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- this._$form.querySelector('input[type="file"]').focus();
- } else {
- this._$form.querySelectorAll('.is-invalid')[0].focus();
- }
- }
- return;
- }
-
- if (!this._$form.querySelector('.form-error').classList.contains('d-none')) {
- this._$form.querySelector('.form-error').classList.add('d-none');
- }
-
- this._changeStateSubmit(true);
-
- var _this = this;
-
- var request = new XMLHttpRequest();
- request.onreadystatechange = function () {
- if (request.readyState === 0 || request.readyState === 4) {
- if (request.status == 200) {
- _success(JSON.parse(request.responseText), _this);
- } else {
- _error(_this);
- }
- //done();
- }
- };
- if (this._$form.querySelector('.progress').classList.contains('d-none')) {
- this._$form.querySelector('.progress').classList.remove('d-none');
- }
- request.upload.onprogress = function (e) {
- // если известно количество байт для пересылки
- if (e.lengthComputable) {
- // получаем общее количество байт для пересылки
- var total = e.total;
- // получаем какое количество байт уже отправлено
- var loaded = e.loaded;
- // определяем процент отправленных данных на сервер
- var progress = ((loaded * 100) / total).toFixed(1);
- // обновляем состояние прогресс бара Bootstrap
- var progressBar = this._$form.querySelector('.progress-bar');
- progressBar.setAttribute('aria-valuenow', progress);
- progressBar.style.width = progress + '%';
- //progressBar.querySelector('.sr-only').textContent = progress + '%';
- }
- }.bind(this);
-
- request.open('POST', this._$form.getAttribute('action'), true);
- request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- //request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
- request.send(this._collectData());
-
- // при получении успешного ответа от сервера
- function _success(data, _this) {
- var _this = _this;
-
- var $progress = _this._$form.querySelector('.progress');
- if ($progress) {
- $progress.classList.add('d-none');
- var progressBar = $progress.querySelector('.progress-bar');
- progressBar.setAttribute('aria-valuenow', '0');
- progressBar.style.width = '0';
- //progressBar.querySelector('.sr-only').textContent = '0%';
- }
-
- // при успешной отправки формы
- if (data.result === 'success') {
- _this._$form.dispatchEvent(new Event('pf_success'));
- if (_this._settings.isUseDefaultSuccessMessage) {
- var $resultSuccess = _this._$form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.remove('d-none');
- $resultSuccess.classList.add('d-flex');
- }
- return;
- }
- // если произошли ошибки при отправке
- _this._$form.querySelector('.form-error').classList.remove('d-none');
- _this._changeStateSubmit(false);
-
- var attach = _this._$form.querySelector('.form-attachments__item');
- if (attach) {
- attach.setAttribute('title', '');
- attach.classList.remove('is-valid');
- attach.classList.remove('is-invalid');
- }
-
- // выводим ошибки которые прислал сервер
- for (var error in data) {
- if (!data.hasOwnProperty(error)) {
- continue;
- }
- switch (error) {
- case 'captcha':
- _this._refreshCaptcha(_this);
- var $element = _this._$form.querySelector('[name="' + error + '"]');
- _this._setStateValidaion($element, 'error', data[error]);
- break;
- case 'attachment':
- var attachs = data[error];
- for (let key in attachs) {
- var id = _this._attachmentsItems[key].id;
- var selector = '.form-attachments__item[data-id="' + id + '"]';
- var $attach = _this._$form.querySelector(selector);
- $attach.setAttribute('title', attachs[key]);
- $attach.classList.add('is-invalid');
- }
- break;
- case 'log':
- var logs = data[error];
- for (var i = 0, length = logs.length; i < length; i++) {
- console.log(logs[i]);
- }
- break;
- default:
- var $element = _this._$form.querySelector('[name="' + error + '"]');
- if ($element) {
- _this._setStateValidaion($element, 'error', data[error]);
- }
- }
- }
- // устанавливаем фокус на не валидный элемент
- if (_this._$form.querySelectorAll('.is-invalid').length > 0) {
- if (_this._$form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- _this._$form.querySelector('input[type="file"]').focus();
- } else {
- _this._$form.querySelectorAll('.is-invalid')[0].focus();
- }
- }
- var its = _this._$form.querySelectorAll('.form-attachments__item :not(.is-invalid)');
- for (var i = 0, length = its.lengt; i < length; i++) {
- its[i].classList.add('is-valid');
- }
- }
-
- // если не получили успешный ответ от сервера
- function _error(_this) {
- _this._$form.querySelector('.form-error').classList.remove('d-none');
- }
-};
-
-// функция для инициализации
-ProcessForm.prototype._init = function () {
- // устанавливаем значение свойства _isCaptchaSection в завимости от того имеется ли у формы секция с капчей или нет
- this._isCaptchaSection = this._$form.querySelectorAll('.form-captcha').length > 0;
- // устанавливаем значение свойства _isAgreementSection в завимости от того имеется ли у формы секция с пользовательским соглашением или нет
- this._isAgreementSection = this._$form.querySelectorAll('.form-agreement').length > 0;
- // устанавливаем значения свойств _isAttachmentsSection и _attachmentsMaxItems в завимости от того имеется ли у формы секция с секцией для добавления к ней файлов
- var formAttachments = this._$form.querySelectorAll('.form-attachments');
- if (formAttachments.length) {
- this._isAttachmentsSection = true;
- if (formAttachments[0].getAttribute('data-count')) {
- this._attachmentsMaxItems = +formAttachments[0].getAttribute('data-count');
- }
- }
- this._setupListener.call(this);
-};
-
-ProcessForm.prototype._reset = function () {
- this._showForm();
-};
-
-// устанавливаем обработчики событий
-ProcessForm.prototype._setupListener = function () {
- var $form = this._$form;
- $form.onchange = function (e) {
- var $element = e.target;
- if ($element.name === 'agree') {
- this._changeStateSubmit(!$element.checked);
- }
- }.bind(this);
- $form.onsubmit = function (e) {
- var $form = e.target;
- if ($form === this._$form) {
- e.preventDefault();
- this._sendForm(this);
- }
- }.bind(this);
- // обработка события click
- $form.onclick = function (e) {
- var $element = e.target;
- if ($element.classList.contains('form-captcha__refresh')) {
- // при нажатии click на form-captcha__refresh
- e.preventDefault();
- this._refreshCaptcha();
- } else if ($element.classList.contains('form-attachments__item-link')) {
- // при нажатии click на form-attachments__item-link
- var id = +$element.dataset.id;
- var $item = $form.querySelector('.form-attachments__item[data-id="' + id + '"]');
- var $attachs = this._attachmentsItems;
- for (var i = 0, length = $attachs.length; i < length; i++) {
- if ($attachs[i].id === id) {
- $attachs.splice(i, 1);
- break;
- }
- }
- $item.parentElement.removeChild($item);
- }
- }.bind(this);
-
- $form.parentElement.onclick = function (e) {
- var $element = e.target;
- if ($element.dataset.target === this._settings.selector) {
- e.preventDefault();
- this._showForm();
- }
- }.bind(this);
-
- var _this = this;
-
- // если у формы имеется .form-attachment
- if (this._isAttachmentsSection) {
- // событие при изменении элемента input с type="file" (name="attachment[])
- _this._$form.addEventListener('change', function (e) {
- if (e.target.name !== 'attachment[]') {
- return;
- }
-
- var file, fileId, removeLink;
-
- for (var i = 0, length = e.target.files.length; i < length; i++) {
- if (_this._attachmentsItems.length === _this._attachmentsMaxItems) {
- e.target.value = '';
- break;
- }
- fileId = _this._attachmentsIdCounter++;
- file = e.target.files[i];
- _this._attachmentsItems.push({
- id: fileId,
- file: file,
- });
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.readAsDataURL(file);
- (function (file, fileId) {
- reader.addEventListener('load', function (e) {
- var removeLink =
- '<div class="form-attachments__item" data-id="' +
- fileId +
- '">' +
- '<div class="form-attachments__item-wrapper">' +
- '<img class="form-attachments__item-image" src="' +
- e.target.result +
- '" alt="' +
- file.name +
- '">' +
- '<div class="form-attachments__item-name">' +
- file.name +
- '</div>' +
- '<div class="form-attachments__item-size">' +
- (file.size / 1024).toFixed(1) +
- 'Кб' +
- '</div>' +
- '<div class="form-attachments__item-link" data-id="' +
- fileId +
- '">×</div>' +
- '</div>' +
- '</div>';
- _this._$form.querySelector('.form-attachments__items').innerHTML += removeLink;
- });
- })(file, fileId);
- continue;
- }
- removeLink =
- '<div class="form-attachments__item" data-id="' +
- fileId +
- '">' +
- '<div class="form-attachments__item-wrapper">' +
- '<div class="form-attachments__item-name">' +
- file.name +
- '</div>' +
- '<div class="form-attachments__item-size">' +
- (file.size / 1024).toFixed(1) +
- 'Кб' +
- '</div>' +
- '<div class="form-attachments__item-link" data-id="' +
- fileId +
- '">×</div>' +
- '</div>' +
- '</div>';
- _this._$form.querySelector('.form-attachments__items').innerHTML += removeLink;
- }
- e.target.value = null;
- });
- }
-};
diff --git a/feedback/js/process-forms.back.js b/feedback/js/process-forms.back.js
deleted file mode 100644
index c29da81..0000000
--- a/feedback/js/process-forms.back.js
+++ /dev/null
@@ -1,381 +0,0 @@
-/*!
- * Форма обратной связи (https://github.com/itchief/feedback-form)
- * Страница с описанием: https://itchief.ru/lessons/php/feedback-form-for-website
- * Copyright 2016-2020 Alexander Maltsev
- * Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE)
- */
-
-'use strict';
-
-var ProcessForm = function (settings) {
- this._settings = {
- selector: '#feedback-form', // дефолтный селектор
- attachmentsMaxFileSize: 512, // дефолтный максимальный размер файла в Кб
- attachmentsFileExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов
- isUseDefaultSuccessMessage: true // отображать дефолтное сообщение об успешной отправки формы
- };
- this._isCaptchaSection = false; // имеется ли в форме блок с капчей
- this._isAgreementSection = false; // имеется ли в форме блок с пользовательским соглашением
- this._isAttachmentsSection = false; // имеется ли в форме блок для добавления к ней файлов
- this._attachmentsIdCounter = 0; // счетчик, хранящий количество добавленных к форме файлов
- this._attachmentsMaxItems = 5; // переменная, определяющее максимальное количество файлов, которые можно прикрепить к форме
- this._attachmentsItems = []; // переменная, хранящая массив файлов, которые нужно прекрепить к форме
-
- for (var propName in settings) {
- if (settings.hasOwnProperty(propName)) {
- this._settings[propName] = settings[propName];
- }
- }
- this._form = $(this._settings.selector).eq(0);
-};
-
-ProcessForm.prototype = function () {
- // переключить во включенное или выключенное состояние кнопку submit
- var _changeStateSubmit = function (_this, state) {
- _this._form.find('[type="submit"]').prop('disabled', state);
- };
-
- // обновление капчи
- var _refreshCaptcha = function (_this) {
- var
- captchaImg = _this._form.find('.form-captcha__image'),
- captchaSrc = captchaImg.attr('data-src'),
- captchaPrefix = captchaSrc.indexOf('?id') !== -1 ? '&rnd=' : '?rnd=',
- captchaNewSrc = captchaSrc + captchaPrefix + (new Date()).getTime();
- captchaImg.attr('src', captchaNewSrc);
- };
-
- // изменение состояния элемента формы (success, error, clear)
- var _setStateValidaion = function (input, state, message) {
- input = $(input);
- if (state === 'error') {
- input
- .removeClass('is-valid').addClass('is-invalid')
- .siblings('.invalid-feedback').text(message);
- } else if (state === 'success') {
- input.removeClass('is-invalid').addClass('is-valid');
- } else {
- input.removeClass('is-valid is-invalid');
- }
- };
-
- // метод, возвращающий результат проверки расширения файла допустимому
- var _validateFileExtension = function (filename, validFileExtensions) {
- // получаем расширение файла
- var fileExtension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
- // если есть расширение, то проверяем соотвествует ли оно допустимому
- if (fileExtension) {
- for (var i = 0; i <= validFileExtensions.length; i++) {
- if (validFileExtensions[i] === fileExtension.toLowerCase()) {
- return true;
- }
- }
- }
- return false;
- };
-
- // валилация формы
- var _validateForm = function (_this) {
- var valid = true;
- _this._form.find('input, textarea').not('[type="file"], [name="agree"]').each(function () {
- if (this.checkValidity()) {
- _setStateValidaion(this, 'success');
- } else {
- _setStateValidaion(this, 'error', this.validationMessage);
- valid = false;
- }
- });
- if (_this._attachmentsItems.length > 0) {
- var files = _this._attachmentsItems;
- for (var i = 0, length = files.length; i < length; i++) {
- // проверим размер и расширение файла
- if (files[i].file.size > _this._settings.attachmentsMaxFileSize * 1024) {
- _this._form.find('.form-attachments__item[data-id="' + i + '"]').attr('title', 'Размер файла больше ' + _this._settings.attachmentsMaxFileSize + 'Кб').addClass('is-invalid');
- valid = false;
- } else if (!_validateFileExtension(files[i].file.name, _this._settings.attachmentsFileExt)) {
- _this._form.find('.form-attachments__item[data-id="' + i + '"]').attr('title', 'Тип не соответствует разрешённым').addClass('is-invalid');
- valid = false;
- } else {
- _this._form.find('.form-attachments__item[data-id="' + i + '"]').attr('title', '').addClass('is-valid');
- }
- }
- }
- return valid;
- };
-
- var _showForm = function (_this) {
- if (!_this._form.find('.form-error').hasClass('d-none')) {
- _this._form.find('.form-error').addClass('d-none');
- }
- _this._form.siblings('.form-result-success').addClass('d-none').removeClass('d-flex');
- _this._form[0].reset();
- _this._form.find('input, textarea').each(function () {
- _setStateValidaion(this, 'clear');
- });
- if (_this._isCaptchaSection) {
- _refreshCaptcha(_this);
- }
- if (_this._isAgreementSection) {
- _changeStateSubmit(_this, true);
- } else {
- _changeStateSubmit(_this, false);
- }
- if (_this._isAttachmentsSection) {
- _this._form.find('.form-attachments__item').remove();
- }
- if (_this._form.find('.progress-bar').length) {
- _this._form.find('.progress-bar')
- .attr('aria-valuenow', '0')
- .width('0')
- .find('.sr-only').text('0%');
- }
- };
-
- var _changeStateImages = function (_this, state) {
- if (!_this._isAttachmentsSection) {
- return;
- }
- _this._form.find('[name="attachment[]"]').prop('disabled', state);
- };
-
- // собираем данные для отправки на сервер
- var _collectData = function (_this) {
- var output;
- _changeStateImages(_this, true);
- output = new FormData(_this._form[0]);
- _changeStateImages(_this, false);
- for (var i = 0, length = _this._attachmentsItems.length; i < length; i++) {
- output.append('attachment[]', _this._attachmentsItems[i].file);
- }
- return output;
- };
-
- // отправка формы
- var _sendForm = function (_this) {
- $(document).trigger('beforeSubmit', [_this._form]);
- if (!_validateForm(_this)) {
- if (_this._form.find('.is-invalid').length > 0) {
- if (_this._form.find('.is-invalid').hasClass('file')) {
- _this._form.find('input[type="file"]').focus();
- } else {
- _this._form.find('.is-invalid')[0].focus();
- }
- }
- return;
- }
-
- if (!_this._form.find('.form-error').hasClass('d-none')) {
- _this._form.find('.form-error').addClass('d-none');
- }
-
- $.ajax({
- context: _this,
- type: "POST",
- url: _this._form.attr('action'),
- data: _collectData(_this), // данные для отправки на сервер
- contentType: false,
- processData: false,
- cache: false,
- beforeSend: function () {
- _changeStateSubmit(_this, true);
- },
- xhr: function () {
- var myXhr = $.ajaxSettings.xhr();
- if (_this._form.find('.progress').hasClass('d-none')) {
- _this._form.find('.progress').removeClass('d-none');
- }
- if (myXhr.upload) {
- myXhr.upload.addEventListener('progress', function (event) {
- // если известно количество байт для пересылки
- if (event.lengthComputable) {
- // получаем общее количество байт для пересылки
- var total = event.total;
- // получаем какое количество байт уже отправлено
- var loaded = event.loaded;
- // определяем процент отправленных данных на сервер
- var progress = ((loaded * 100) / total).toFixed(1);
- // обновляем состояние прогресс бара Bootstrap
- var progressBar = _this._form.find('.progress-bar');
- progressBar.attr('aria-valuenow', progress);
- progressBar.width(progress + '%');
- progressBar.find('.sr-only').text(progress + '%');
- }
- }, false);
- }
- return myXhr;
- }
- })
- .done(_success)
- .fail(_error)
- };
-
- // при получении успешного ответа от сервера
- var _success = function (data) {
- var _this = this;
- if (_this._form.find('.progress').length) {
- _this._form
- .find('.progress').addClass('d-none')
- .find('.progress-bar').attr('aria-valuenow', '0').width('0')
- .find('.sr-only').text('0%');
- }
- // при успешной отправки формы
- if (data.result === "success") {
- $(document).trigger('pf_success', {data: this});
- if (_this._settings.isUseDefaultSuccessMessage) {
- _this._form.parent().find('.form-result-success')
- .removeClass('d-none')
- .addClass('d-flex');
- }
- return;
- }
- // если произошли ошибки при отправке
- _this._form.find('.form-error').removeClass('d-none');
- _changeStateSubmit(this, false);
-
- _this._form.find('.form-attachments__item').attr('title', '').removeClass('is-valid is-invalid');
-
- // выводим ошибки которые прислал сервер
- for (var error in data) {
- if (!data.hasOwnProperty(error)) {
- continue;
- }
- switch (error) {
- case 'captcha':
- _refreshCaptcha(_this);
- _setStateValidaion(_this._form.find('[name="' + error + '"]'), 'error', data[error]);
- break;
- case 'attachment':
- $.each(data[error], function (key, value) {
- _this._form.find('.form-attachments__item[data-id="' + _this._attachmentsItems[key].id + '"]').attr('title', value).addClass('is-invalid');
- });
- break;
- case 'log':
- $.each(data[error], function (key, value) {
- console.log(value);
- });
- break;
- default:
- _setStateValidaion(_this._form.find('[name="' + error + '"]'), 'error', data[error]);
- }
- }
- // устанавливаем фокус на 1 невалидный элемент
- if (_this._form.find('.is-invalid').length > 0) {
- if (_this._form.find('.is-invalid').hasClass('file')) {
- _this._form.find('input[type="file"]').focus();
- } else {
- _this._form.find('.is-invalid')[0].focus();
- }
- }
- _this._form.find('.form-attachments__item').not('.is-invalid').addClass('is-valid');
- };
-
- // если не получили успешный ответ от сервера
- var _error = function () {
- this._form.find('.form-error').removeClass('d-none');
- };
-
- // функция для инициализации
- var _init = function () {
- // устанавливаем значение свойства _isCaptchaSection в завимости от того имеется ли у формы секция с капчей или нет
- this._isCaptchaSection = this._form.find('.form-captcha').length > 0;
- // устанавливаем значение свойства _isAgreementSection в завимости от того имеется ли у формы секция с пользовательским соглашением или нет
- this._isAgreementSection = this._form.find('.form-agreement').length > 0;
- // устанавливаем значения свойств _isAttachmentsSection и _attachmentsMaxItems в завимости от того имеется ли у формы секция с секцией для добавления к ней файлов
- var formAttachments = this._form.find('.form-attachments');
- if (formAttachments.length) {
- this._isAttachmentsSection = true;
- if (formAttachments.attr('data-count')) {
- this._attachmentsMaxItems = +formAttachments.attr('data-count');
- }
- }
- _setupListener(this);
- };
-
- var _reset = function () {
- _showForm(this);
- };
-
- // устанавливаем обработчики событий
- var _setupListener = function (_this) {
- $(document).on('change', _this._settings.selector + ' [name="agree"]', function () {
- _changeStateSubmit(_this, !this.checked);
- });
- $(document).on('submit', _this._settings.selector, function (e) {
- e.preventDefault();
- _sendForm(_this);
- });
- $(document).on('click', _this._settings.selector + ' .form-captcha__refresh', function (e) {
- e.preventDefault();
- _refreshCaptcha(_this);
- });
- $(document).on('click', '[data-target="' + _this._settings.selector + '"]', function (e) {
- e.preventDefault();
- _showForm(_this);
- });
- // если у формы имеется .form-attachment
- if (_this._isAttachmentsSection) {
- // события для удаления добавленного к форме файла
- $(document).on('click', _this._settings.selector + ' .form-attachments__item-link', function () {
- var
- link = $(this),
- fileId = +link.attr('data-id'),
- file = link.closest('.form-attachments__item');
- for (var i = 0, length = _this._attachmentsItems.length; i < length; i++) {
- if (_this._attachmentsItems[i].id === fileId) {
- _this._attachmentsItems.splice(i, 1);
- break;
- }
- }
- file.remove();
- });
- // событие при изменении элемента input с type="file" (name="attachment[])
- $(document).on('change', _this._settings.selector + ' input[name="attachment[]"]', function (e) {
- var file, fileId, removeLink;
-
- for (var i = 0, length = e.target.files.length; i < length; i++) {
- if (_this._attachmentsItems.length === _this._attachmentsMaxItems) {
- e.target.value = '';
- break;
- }
- fileId = _this._attachmentsIdCounter++;
- file = e.target.files[i];
- _this._attachmentsItems.push({
- id: fileId,
- file: file
- });
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.readAsDataURL(file);
- (function (file, fileId) {
- reader.addEventListener('load', function (e) {
- var removeLink = '<div class="form-attachments__item" data-id="' + fileId + '">' +
- '<div class="form-attachments__item-wrapper">' +
- '<img class="form-attachments__item-image" src="' + e.target.result + '" alt="' + file.name + '">' +
- '<div class="form-attachments__item-name">' + file.name + '</div>' +
- '<div class="form-attachments__item-size">' + (file.size / 1024).toFixed(1) + 'Кб' + '</div>' +
- '<div class="form-attachments__item-link" data-id="' + fileId + '">×</div>' +
- '</div>' +
- '</div>';
- _this._form.find('.form-attachments__items').append(removeLink);
- });
- })(file, fileId);
- continue;
- }
- removeLink = '<div class="form-attachments__item" data-id="' + fileId + '">' +
- '<div class="form-attachments__item-wrapper">' +
- '<div class="form-attachments__item-name">' + file.name + '</div>' +
- '<div class="form-attachments__item-size">' + (file.size / 1024).toFixed(1) + 'Кб' + '</div>' +
- '<div class="form-attachments__item-link" data-id="' + fileId + '">×</div>' +
- '</div>' +
- '</div>';
- _this._form.find('.form-attachments__items').append(removeLink);
- }
- e.target.value = null;
- });
- }
- };
- return {
- init: _init,
- reset: _reset
- }
-}();
diff --git a/feedback/js/process-forms.dev.js b/feedback/js/process-forms.dev.js
deleted file mode 100644
index e971838..0000000
--- a/feedback/js/process-forms.dev.js
+++ /dev/null
@@ -1,420 +0,0 @@
-/*!
- * Форма обратной связи (https://github.com/itchief/feedback-form)
- * Страница с описанием: https://itchief.ru/lessons/php/feedback-form-for-website
- * Copyright 2016-2022 Alexander Maltsev
- * Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE)
- */
-
-'use strict';
-
-class ItcSubmitForm {
- constructor(target, config) {
- this._form = typeof target === 'string' ? document.querySelector(target) : target;
- const defaultConfig = {
- attachMaxSize: 512, // дефолтный максимальный размер файла в Кб
- attachExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов
- isUseDefaultSuccessMessage: true, // отображать дефолтное сообщение об успешной отправки формы
- };
- this._config = Object.assign(defaultConfig, config);
- this._isCaptchaSection = false; // имеется ли в форме блок с капчей
- this._isAgreementSection = false; // имеется ли в форме блок с пользовательским соглашением
- this._isAttachmentsSection = false; // имеется ли в форме блок для добавления к ней файлов
- this._attachmentsIdCounter = 0; // счетчик, хранящий количество добавленных к форме файлов
- this._attachmentsMaxItems = 5; // переменная, определяющее максимальное количество файлов, которые можно прикрепить к форме
- this._attachItems = []; // переменная, хранящая массив файлов, которые нужно прекрепить к форме
- // инициализация формы
- this._init();
- }
- // проверка расширения файла
- _checkExt(filename) {
- // расширение файла
- const ext = filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
- // проверка на соответствие допустимому
- return this._config.attachExt.indexOf(ext.toLowerCase()) !== -1;
- }
- _setElAttach(elAttach, title, state) {
- elAttach.setAttribute('title', title);
- elAttach.classList.add(state);
- }
- // переключение состояния disabled у кнопки submit
- _changeStateSubmit(state) {
- this._form.querySelector('[type="submit"]').disabled = state;
- }
- // обновление капчи
- _refreshCaptcha() {
- var captchaImg = this._form.querySelector('.form-captcha__image');
- var captchaSrc = captchaImg.getAttribute('data-src');
- var captchaPrefix = captchaSrc.indexOf('?id') !== -1 ? '&rnd=' : '?rnd=';
- var captchaNewSrc = captchaSrc + captchaPrefix + new Date().getTime();
- captchaImg.setAttribute('src', captchaNewSrc);
- }
- // изменение состояния элемента формы (success, error, clear)
- _setStateValidaion(input, state, message) {
- if (state === 'error') {
- input.classList.remove('is-valid');
- input.classList.add('is-invalid');
- input.parentElement.querySelector('.invalid-feedback').textContent = message;
- } else if (state === 'success') {
- input.classList.remove('is-invalid');
- input.classList.add('is-valid');
- } else {
- input.classList.remove('is-valid');
- input.classList.remove('is-invalid');
- }
- }
- // валидация формы
- _validate() {
- let result = true;
- // валидация input и textarea
- this._form.querySelectorAll('input, textarea').forEach((el) => {
- if (el.type === 'file' || el.name === 'agree') {
- return;
- }
- if (el.checkValidity()) {
- this._setStateValidaion(el, 'success');
- } else {
- this._setStateValidaion(el, 'error', el.validationMessage);
- result = false;
- }
- });
- if (!this._attachItems.length) {
- return result;
- }
- // валидация файлов
- this._attachItems.forEach((el, index) => {
- const elAttach = this._form.querySelector(`.form-attachments__item[data-id="${index}"]`);
- if (el.file.size > this._config.attachMaxSize * 1024) {
- this._setElAttach(elAttach, `Размер файла больше ${this._config.attachMaxSize}Кб`, 'is-invalid');
- result = false;
- } else if (!this._checkExt(el.file.name)) {
- this._setElAttach(elAttach, 'Тип не соответствует разрешённым', 'is-invalid');
- result = false;
- } else {
- if (elAttach) {
- this._setElAttach(elAttach, '', 'is-valid');
- }
- }
- })
- return result;
- }
-
- _showForm() {
- this._form.querySelector('.form-error').classList.add('d-none');
- var $resultSuccess = this._form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.add('d-none');
- $resultSuccess.classList.remove('d-flex');
- this._form.reset();
- var $elements = this._form.querySelectorAll('input, textarea');
- for (var i = 0, length = $elements.length; i < length; i++) {
- this._setStateValidaion($elements[i], 'clear');
- }
- if (this._isCaptchaSection) {
- this._refreshCaptcha();
- }
- if (this._isAgreementSection) {
- this._changeStateSubmit(true);
- } else {
- this._changeStateSubmit(false);
- }
-
- // удаление attachment items
- var $attachs = this._form.querySelectorAll('.form-attachments__item');
- if ($attachs.length > 0) {
- for (var i = 0, length = $attachs.length; i < length; i++) {
- $attachs[i].parentElement.removeChild($attachs[i]);
- }
- }
-
- if (this._form.querySelector('.progress-bar').length) {
- var $progressBar = this._form.querySelector('.progress-bar');
- $progressBar.setAttribute('aria-valuenow', '0');
- $progressBar.style.width = 0;
- }
- };
-
- // переключение disabled для name="attachment[]"
- _disabledAttach(state) {
- var $attach = this._form.querySelector('[name="attachment[]"]');
- if ($attach) {
- $attach.disabled = state;
- }
- };
-
- // данные для отправки на сервер
- _getFormData() {
- // отключаем добавление данных из name="attachment[]" в FormData
- this._disabledAttach(true);
- let formData = new FormData(this._form);
- // включаем доступность name="attachment[]"
- this._disabledAttach(false);
- // добавляем данные из name="attachment[]" в FormData
- this._attachItems.forEach(el => {
- formData.append('attachment[]', el.file);
- })
- return formData;
- };
-
- // отправка формы
- _send() {
- this._form.dispatchEvent(new Event('beforeSubmit'));
- if (!this._validate()) {
- if (this._form.querySelectorAll('.is-invalid').length > 0) {
- if (this._form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- this._form.querySelector('input[type="file"]').focus();
- } else {
- this._form.querySelectorAll('.is-invalid')[0].focus();
- }
- }
- return;
- }
-
- if (!this._form.querySelector('.form-error').classList.contains('d-none')) {
- this._form.querySelector('.form-error').classList.add('d-none');
- }
-
- this._changeStateSubmit(true);
-
- var _this = this;
-
- var request = new XMLHttpRequest();
- request.onreadystatechange = function () {
- if (request.readyState === 0 || request.readyState === 4) {
- if (request.status == 200) {
- _success(JSON.parse(request.responseText), _this);
- } else {
- _error(_this);
- }
- //done();
- }
- };
- if (this._form.querySelector('.progress').classList.contains('d-none')) {
- this._form.querySelector('.progress').classList.remove('d-none');
- }
- request.upload.onprogress = function (e) {
- // если известно количество байт для пересылки
- if (e.lengthComputable) {
- // получаем общее количество байт для пересылки
- var total = e.total;
- // получаем какое количество байт уже отправлено
- var loaded = e.loaded;
- // определяем процент отправленных данных на сервер
- var progress = ((loaded * 100) / total).toFixed(1);
- // обновляем состояние прогресс бара Bootstrap
- var progressBar = this._form.querySelector('.progress-bar');
- progressBar.setAttribute('aria-valuenow', progress);
- progressBar.style.width = progress + '%';
- //progressBar.querySelector('.sr-only').textContent = progress + '%';
- }
- }.bind(this);
-
- request.open('POST', this._form.getAttribute('action'), true);
- request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- //request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
- request.send(this._getFormData());
-
- // при получении успешного ответа от сервера
- function _success(data, _this) {
- var _this = _this;
-
- var $progress = _this._form.querySelector('.progress');
- if ($progress) {
- $progress.classList.add('d-none');
- var progressBar = $progress.querySelector('.progress-bar');
- progressBar.setAttribute('aria-valuenow', '0');
- progressBar.style.width = '0';
- //progressBar.querySelector('.sr-only').textContent = '0%';
- }
-
- // при успешной отправки формы
- if (data.result === 'success') {
- _this._form.dispatchEvent(new Event('pf_success'));
- if (_this._config.isUseDefaultSuccessMessage) {
- var $resultSuccess = _this._form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.remove('d-none');
- $resultSuccess.classList.add('d-flex');
- }
- return;
- }
- // если произошли ошибки при отправке
- _this._form.querySelector('.form-error').classList.remove('d-none');
- _this._changeStateSubmit(false);
-
- var attach = _this._form.querySelector('.form-attachments__item');
- if (attach) {
- attach.setAttribute('title', '');
- attach.classList.remove('is-valid');
- attach.classList.remove('is-invalid');
- }
-
- // выводим ошибки которые прислал сервер
- for (var error in data) {
- if (!data.hasOwnProperty(error)) {
- continue;
- }
- switch (error) {
- case 'captcha':
- _this._refreshCaptcha(_this);
- var $element = _this._form.querySelector('[name="' + error + '"]');
- _this._setStateValidaion($element, 'error', data[error]);
- break;
- case 'attachment':
- var attachs = data[error];
- for (let key in attachs) {
- var id = _this._attachItems[key].id;
- var selector = '.form-attachments__item[data-id="' + id + '"]';
- var $attach = _this._form.querySelector(selector);
- $attach.setAttribute('title', attachs[key]);
- $attach.classList.add('is-invalid');
- }
- break;
- case 'log':
- var logs = data[error];
- for (var i = 0, length = logs.length; i < length; i++) {
- console.log(logs[i]);
- }
- break;
- default:
- var $element = _this._form.querySelector('[name="' + error + '"]');
- if ($element) {
- _this._setStateValidaion($element, 'error', data[error]);
- }
- }
- }
- // устанавливаем фокус на не валидный элемент
- if (_this._form.querySelectorAll('.is-invalid').length > 0) {
- if (_this._form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- _this._form.querySelector('input[type="file"]').focus();
- } else {
- _this._form.querySelectorAll('.is-invalid')[0].focus();
- }
- }
- var its = _this._form.querySelectorAll('.form-attachments__item :not(.is-invalid)');
- for (var i = 0, length = its.lengt; i < length; i++) {
- its[i].classList.add('is-valid');
- }
- }
-
- // если не получили успешный ответ от сервера
- function _error(_this) {
- _this._form.querySelector('.form-error').classList.remove('d-none');
- }
- };
-
- // функция для инициализации
- _init() {
- // устанавливаем значение свойства _isCaptchaSection в завимости от того имеется ли у формы секция с капчей или нет
- this._isCaptchaSection = this._form.querySelectorAll('.form-captcha').length > 0;
- // устанавливаем значение свойства _isAgreementSection в завимости от того имеется ли у формы секция с пользовательским соглашением или нет
- this._isAgreementSection = this._form.querySelectorAll('.form-agreement').length > 0;
- // устанавливаем значения свойств _isAttachmentsSection и _attachmentsMaxItems в завимости от того имеется ли у формы секция с секцией для добавления к ней файлов
- var formAttachments = this._form.querySelectorAll('.form-attachments');
- if (formAttachments.length) {
- this._isAttachmentsSection = true;
- if (formAttachments[0].getAttribute('data-count')) {
- this._attachmentsMaxItems = +formAttachments[0].getAttribute('data-count');
- }
- }
- this._setupListener.call(this);
- };
-
- _reset() {
- this._showForm();
- }
-
- // устанавливаем обработчики событий
- _setupListener() {
- const $form = this._form;
- this._form.onchange = (e) => {
- const el = e.target;
- if (el.name === 'agree') {
- this._changeStateSubmit(!el.checked);
- }
- };
- this._form.onsubmit = (e) => {
- e.preventDefault();
- this._send(this);
- };
- // обработка события click
- $form.onclick = function (e) {
- var $element = e.target;
- if ($element.classList.contains('form-captcha__refresh')) {
- // при нажатии click на form-captcha__refresh
- e.preventDefault();
- this._refreshCaptcha();
- } else if ($element.classList.contains('form-attachments__item-link')) {
- // при нажатии click на form-attachments__item-link
- var id = +$element.dataset.id;
- var $item = $form.querySelector('.form-attachments__item[data-id="' + id + '"]');
- var $attachs = this._attachItems;
- for (var i = 0, length = $attachs.length; i < length; i++) {
- if ($attachs[i].id === id) {
- $attachs.splice(i, 1);
- break;
- }
- }
- $item.parentElement.removeChild($item);
- }
- }.bind(this);
-
- $form.parentElement.onclick = function (e) {
- var $element = e.target;
- if ($element.dataset.target === ('#' + this._form.id)) {
- e.preventDefault();
- this._showForm();
- }
- }.bind(this);
-
- var _this = this;
-
- // если у формы имеется .form-attachment
- if (this._isAttachmentsSection) {
- // событие при изменении элемента input с type="file" (name="attachment[])
- _this._form.addEventListener('change', function (e) {
- if (e.target.name !== 'attachment[]') {
- return;
- }
-
- var file, fileId, removeLink;
-
- for (var i = 0, length = e.target.files.length; i < length; i++) {
- if (_this._attachItems.length === _this._attachmentsMaxItems) {
- e.target.value = '';
- break;
- }
- fileId = _this._attachmentsIdCounter++;
- file = e.target.files[i];
- _this._attachItems.push({
- id: fileId,
- file: file,
- });
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.readAsDataURL(file);
- (function (file, fileId) {
- reader.addEventListener('load', (e) => {
- var removeLink = `<div class="form-attachments__item" data-id="${fileId}">
- <div class="form-attachments__item-wrapper">
- <img class="form-attachments__item-image" src="${e.target.result}" alt="${file.name}">
- <div class="form-attachments__item-name">${file.name}</div>
- <div class="form-attachments__item-size">${(file.size / 1024).toFixed(1)}Кб</div>
- <div class="form-attachments__item-link" data-id="${fileId}">×</div>
- </div></div>`;
- _this._form.querySelector('.form-attachments__items').innerHTML += removeLink;
- });
- })(file, fileId);
- continue;
- }
- removeLink = `<div class="form-attachments__item" data-id="${fileId}">
- <div class="form-attachments__item-wrapper">
- <div class="form-attachments__item-name">${file.name}</div>
- <div class="form-attachments__item-size">${(file.size / 1024).toFixed(1)}Кб</div>
- <div class="form-attachments__item-link" data-id="${fileId}">×</div>
- </div></div>`;
- _this._form.querySelector('.form-attachments__items').innerHTML += removeLink;
- }
- e.target.value = null;
- });
- }
- };
-
-}
diff --git a/feedback/js/process-forms.js b/feedback/js/process-forms.js
index 5e2017b..1e5bd4b 100644
--- a/feedback/js/process-forms.js
+++ b/feedback/js/process-forms.js
@@ -1,58 +1,45 @@
/*!
* Форма обратной связи (https://github.com/itchief/feedback-form)
- * Страница с описанием: https://itchief.ru/lessons/php/feedback-form-for-website
- * Copyright 2016-2020 Alexander Maltsev
+ * Описание: https://itchief.ru/php/feedback-form
+ * Copyright 2016-2022 Alexander Maltsev
* Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE)
*/
-class ProcessForm {
- constructor(settings) {
- this._settings = {
- selector: '#feedback-form', // дефолтный селектор
- attachmentsMaxFileSize: 512, // дефолтный максимальный размер файла в Кб
- attachmentsFileExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов
- isUseDefaultSuccessMessage: true, // отображать дефолтное сообщение об успешной отправки формы
+class ItcSubmitForm {
+ constructor(selector = 'form', config = {}) {
+ this._attach = {
+ index: 0,
+ maxItems: config['attachMaxItems'] || 5,
+ maxFileSize: config['attachMaxFileSize'] || 512, // максимальный размер файла
+ ext: config['attachExt'] || ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов
+ items: []
};
- this._isCaptchaSection = false; // имеется ли в форме блок с капчей
- this._isAgreementSection = false; // имеется ли в форме блок с пользовательским соглашением
- this._isAttachmentsSection = false; // имеется ли в форме блок для добавления к ней файлов
- this._attachmentsIdCounter = 0; // счетчик, хранящий количество добавленных к форме файлов
- this._attachmentsMaxItems = 5; // переменная, определяющее максимальное количество файлов, которые можно прикрепить к форме
- this._attachmentsItems = []; // переменная, хранящая массив файлов, которые нужно прекрепить к форме
-
- for (var propName in settings) {
- if (settings.hasOwnProperty(propName)) {
- this._settings[propName] = settings[propName];
- }
- }
- this._$form = document.querySelector(this._settings.selector);
- this._elForm = this._$form;
- // инициализация формы
+ this._isCheckValidationOnClient = config['isCheckValidationOnClient'] !== false;
+ this._elForm = document.querySelector(selector);
this._init();
}
- // функция для проверки расширения файла
- static validateFileExtension(filename, validFileExtensions) {
- // получаем расширение файла
- var fileExtension = filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
- // если есть расширение, то проверяем соответствует ли оно допустимому
- if (fileExtension) {
- for (var i = 0; i <= validFileExtensions.length; i++) {
- if (validFileExtensions[i] === fileExtension.toLowerCase()) {
- return true;
- }
- }
- }
- return false;
+ // проверка расширения файла
+ static _checkExt(filename, ext) {
+ // расширение файла
+ const extFile = filename.slice(((filename.lastIndexOf('.') - 1) >>> 0) + 2);
+ // проверка на соответствие допустимому
+ return ext.indexOf(extFile.toLowerCase()) !== -1;
}
- // переключение состояния disabled у кнопки submit
- _changeStateSubmit(state) {
- this._$form.querySelector('[type="submit"]').disabled = state;
+ // статический метод для получения шаблона form-attach__item
+ static _getAttachTemplate(id, file, target) {
+ const itemImg = target ? `<img class="form-attach__image" src="${target.result}" alt="${file.name}"></img>` : '';
+ return `<div class="form-attach__item" data-index="${id}" data-id="${id}">
+ ${itemImg}
+ <div class="form-attach__name">${file.name}</div>
+ <div class="form-attach__size">${(file.size / 1024).toFixed(1)}Кб</div>
+ <div class="form-attach__link" data-id="${id}">×</div>
+ </div>`;
}
- // обновление капчи
- _refreshCaptcha() {
+ // получение новой капчи
+ _reloadСaptcha() {
var captchaImg = this._elForm.querySelector('.form-captcha__image');
var captchaSrc = captchaImg.getAttribute('data-src');
var captchaPrefix = captchaSrc.indexOf('?id') !== -1 ? '&rnd=' : '?rnd=';
@@ -60,129 +47,78 @@ class ProcessForm {
captchaImg.setAttribute('src', captchaNewSrc);
}
- // изменение состояния элемента формы (success, error, clear)
+ // установка статуса валидации
_setStateValidaion(input, state, message) {
- if (state === 'error') {
- input.classList.remove('is-valid');
- input.classList.add('is-invalid');
- input.parentElement.querySelector('.invalid-feedback').textContent = message;
- } else if (state === 'success') {
- input.classList.remove('is-invalid');
- input.classList.add('is-valid');
- } else {
- input.classList.remove('is-valid');
- input.classList.remove('is-invalid');
+ const className = state === 'success' ? 'is-valid' : 'is-invalid';
+ const text = state === 'success' ? '' : message;
+ if (input.classList.contains('form-attach__item')) {
+ input.setAttribute('title', text);
+ input.classList.add(className);
+ return;
+ }
+ input.classList.remove('is-valid');
+ input.classList.remove('is-invalid');
+ input.closest('.form-group').querySelector('.invalid-feedback').textContent = '';
+ if (state === 'error' || state === 'success') {
+ input.classList.add(className);
+ input.closest('.form-group').querySelector('.invalid-feedback').textContent = text;
}
}
// валидация формы
- _validateForm() {
- var valid = true;
- var $elements = this._$form.querySelectorAll('input, textarea');
- for (var i = 0; i < $elements.length; i++) {
- if ($elements[i].type === 'file' || $elements[i].name === 'agree') {
- continue;
+ _checkValidity() {
+ let valid = true;
+ // input, textarea
+ this._elForm.querySelectorAll('input, textarea').forEach(el => {
+ if (el.type === 'file') {
+ return;
}
- if ($elements[i].checkValidity()) {
- this._setStateValidaion($elements[i], 'success');
+ if (el.checkValidity()) {
+ this._setStateValidaion(el, 'success');
} else {
- this._setStateValidaion($elements[i], 'error', $elements[i].validationMessage);
+ this._setStateValidaion(el, 'error', el.validationMessage);
valid = false;
}
- }
-
- // для теста >
- if (this._attachmentsItems.length === 0) {
- var files = this._attachmentsItems;
- for (var i = 0, length = files.length; i < length; i++) {
- // проверим размер и расширение файла
- if (files[i].file.size > this._settings.attachmentsMaxFileSize * 1024) {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- attach.setAttribute(
- 'title',
- 'Размер файла больше ' + this._settings.attachmentsMaxFileSize + 'Кб'
- );
- attach.classList.add('is-invalid');
- valid = false;
- } else if (
- !ProcessForm.validateFileExtension(files[i].file.name, this._settings.attachmentsFileExt)
- ) {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- attach.setAttribute('title', 'Тип не соответствует разрешённым');
- attach.classList.add('is-invalid');
- valid = false;
- } else {
- var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]');
- if (attach) {
- attach.setAttribute('title', '');
- attach.classList.add('is-valid');
- }
- }
+ })
+ // attach
+ const elAttach = this._elForm.querySelector('.form-attach');
+ if (elAttach) {
+ elAttach.classList.remove('is-invalid');
+ elAttach.querySelector('.invalid-feedback').textContent = '';
+ const isRequired = elAttach.querySelector('[type="file"]').required;
+ if (isRequired && this._attach.items.length === 0) {
+ elAttach.classList.add('is-invalid');
+ elAttach.querySelector('.invalid-feedback').textContent = 'Заполните это поле.';
}
}
- return valid;
- }
-
- _showForm() {
- this._$form.querySelector('.form-error').classList.add('d-none');
- var $resultSuccess = this._$form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.add('d-none');
- $resultSuccess.classList.remove('d-flex');
- this._$form.reset();
- var $elements = this._$form.querySelectorAll('input, textarea');
- for (var i = 0, length = $elements.length; i < length; i++) {
- this._setStateValidaion($elements[i], 'clear');
- }
- if (this._isCaptchaSection) {
- this._refreshCaptcha();
- }
- if (this._isAgreementSection) {
- this._changeStateSubmit(true);
- } else {
- this._changeStateSubmit(false);
- }
-
- // удаление attachment items
- var $attachs = this._$form.querySelectorAll('.form-attachments__item');
- if ($attachs.length > 0) {
- for (var i = 0, length = $attachs.length; i < length; i++) {
- $attachs[i].parentElement.removeChild($attachs[i]);
+ this._attach.items.forEach((item) => {
+ const elAttach = this._elForm.querySelector('.form-attach__item[data-index="' + item.index + '"]');
+ if (item.file.size > this._attach.maxFileSize * 1024) {
+ this._setStateValidaion(elAttach, 'error', `Размер файла больше ${this._attach.maxFileSize}Кб`);
+ valid = false;
+ } else if (!ProcessForm._checkExt(item.file.name, this._attach.ext)) {
+ this._setStateValidaion(elAttach, 'error', 'Тип не является допустимым');
+ valid = false;
+ } else {
+ this._setStateValidaion(elAttach, 'success', '');
}
- }
-
- if (this._$form.querySelector('.progress-bar').length) {
- var $progressBar = this._$form.querySelector('.progress-bar');
- $progressBar.setAttribute('aria-valuenow', '0');
- $progressBar.style.width = 0;
- }
- }
-
- // переключение disabled для name="attachment[]"
- _disabledAttach(state) {
- var $attach = this._$form.querySelector('[name="attachment[]"]');
- if ($attach) {
- $attach.disabled = state;
- }
+ })
+ return valid;
}
// собираем данные для отправки на сервер
- _collectData() {
- var data;
- var $attachs = this._attachmentsItems;
- // отключаем добавление данных из name="attachment[]" в FormData
- this._disabledAttach(true);
- data = new FormData(this._$form);
- // включаем доступность name="attachment[]"
- this._disabledAttach(false);
- // добавляем данные из name="attachment[]" в FormData
- for (var i = 0, length = $attachs.length; i < length; i++) {
- data.append('attachment[]', $attachs[i].file);
- }
- return data;
+ _getFormData() {
+ const formData = new FormData(this._elForm);
+ formData.delete('attach[]');
+ this._attach.items.forEach(item => {
+ formData.append('attach[]', item.file);
+ });
+ return formData;
};
+ // при получении успешного ответа от сервера
_successXHR(data) {
- // при получении успешного ответа от сервера
+
var _this = this;
const elProgress = this._elForm.querySelector('.progress');
@@ -194,73 +130,46 @@ class ProcessForm {
}
// при успешной отправки формы
- if (data.result === 'success') {
- _this._$form.dispatchEvent(new Event('pf_success'));
- if (_this._settings.isUseDefaultSuccessMessage) {
- var $resultSuccess = _this._$form.parentElement.querySelector('.form-result-success');
- $resultSuccess.classList.remove('d-none');
- $resultSuccess.classList.add('d-flex');
- }
+ if (data['result'] === 'success') {
+ this._elForm.dispatchEvent(new Event('success'));
return;
}
- // если произошли ошибки при отправке
- _this._$form.querySelector('.form-error').classList.remove('d-none');
- _this._changeStateSubmit(false);
-
- var attach = _this._$form.querySelector('.form-attachments__item');
- if (attach) {
- attach.setAttribute('title', '');
- attach.classList.remove('is-valid');
- attach.classList.remove('is-invalid');
- }
+ this._elForm.querySelector('.form-error').classList.remove('form-error_hidden');
- // выводим ошибки которые прислал сервер
- for (var error in data) {
- if (!data.hasOwnProperty(error)) {
- continue;
- }
- switch (error) {
- case 'captcha':
- _this._refreshCaptcha(_this);
- var $element = _this._$form.querySelector('[name="' + error + '"]');
- _this._setStateValidaion($element, 'error', data[error]);
- break;
- case 'attachment':
- var attachs = data[error];
- for (let key in attachs) {
- var id = _this._attachmentsItems[key].id;
- var selector = '.form-attachments__item[data-id="' + id + '"]';
- var $attach = _this._$form.querySelector(selector);
- $attach.setAttribute('title', attachs[key]);
- $attach.classList.add('is-invalid');
- }
- break;
- case 'log':
- var logs = data[error];
- for (var i = 0, length = logs.length; i < length; i++) {
- console.log(logs[i]);
- }
- break;
- default:
- var $element = _this._$form.querySelector('[name="' + error + '"]');
- if ($element) {
- _this._setStateValidaion($element, 'error', data[error]);
- }
+ // выводим ошибки
+ for (let key in data) {
+ if (key === 'attach') {
+ const attachs = data[key];
+ for (let attach in attachs) {
+ let index = this._attach.items[attach].index;
+ var elAttach = this._elForm.querySelector('.form-attach__item[data-index="' + index + '"]');
+ this._setStateValidaion(elAttach, 'error', attachs[attach]);
+ }
+ } else if (key === 'log') {
+ data[key].forEach((message) => {
+ console.log(message);
+ })
+ } else {
+ key === 'captcha' ? this._reloadСaptcha() : null;
+ const el = this._elForm.querySelector('[name="' + key + '"]');
+ el ? this._setStateValidaion(el, 'error', data[key]) : null;
}
}
+
// устанавливаем фокус на не валидный элемент
- if (_this._$form.querySelectorAll('.is-invalid').length > 0) {
- if (_this._$form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- _this._$form.querySelector('input[type="file"]').focus();
+ const elInvalid = this._elForm.querySelector('.is-invalid');
+ if (elInvalid) {
+ if (elInvalid.classList.contains('form-attach')) {
+ elInvalid.querySelector('input[type="file"]').focus();
} else {
- _this._$form.querySelectorAll('.is-invalid')[0].focus();
+ elInvalid.focus();
}
}
- var its = _this._$form.querySelectorAll('.form-attachments__item :not(.is-invalid)');
- for (var i = 0, length = its.length; i < length; i++) {
- its[i].classList.add('is-valid');
- }
+
+ this._elForm.querySelectorAll('.form-attach__item :not(.is-invalid)').forEach(el => {
+ el.classList.add('is-valid');
+ })
}
_errorXHR() {
@@ -268,37 +177,39 @@ class ProcessForm {
}
// отправка формы
- _sendForm() {
-
- this._elForm.dispatchEvent(new Event('beforeSubmit'));
-
- if (!this._validateForm()) {
- if (this._$form.querySelectorAll('.is-invalid').length > 0) {
- if (this._$form.querySelectorAll('.is-invalid')[0].classList.contains('file')) {
- this._$form.querySelector('input[type="file"]').focus();
- } else {
- this._$form.querySelectorAll('.is-invalid')[0].focus();
+ _onSubmit() {
+
+ this._elForm.dispatchEvent(new Event('before-send'));
+
+ if (this._isCheckValidationOnClient) {
+ if (!this._checkValidity()) {
+ const elInvalid = this._elForm.querySelector('.is-invalid');
+ if (elInvalid) {
+ if (elInvalid.classList.contains('form-attach')) {
+ elInvalid.querySelector('input[type="file"]').focus();
+ } else {
+ elInvalid.focus();
+ }
}
+ return;
}
- return;
}
- this._$form.querySelector('.form-error').classList.add('d-none');
-
- this._changeStateSubmit(true);
+ this._elForm.querySelector('[type="submit"]').disabled = true;
+ this._elForm.querySelector('.form-error').classList.add('form-error_hide');
var xhr = new XMLHttpRequest();
xhr.open('POST', this._elForm.action);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.responseType = 'json';
xhr.onload = () => {
+ this._elForm.querySelector('[type="submit"]').disabled = false;
if (xhr.status == 200) {
this._successXHR(xhr.response);
} else {
this._errorXHR();
}
}
-
this._elForm.querySelector('.progress').classList.remove('d-none');
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
@@ -308,127 +219,93 @@ class ProcessForm {
el.style.width = value + '%';
}
}
-
- xhr.send(this._collectData());
+ xhr.send(this._getFormData());
};
// функция для инициализации
_init() {
- // устанавливаем значение свойства _isCaptchaSection в завимости от того имеется ли у формы секция с капчей или нет
- this._isCaptchaSection = this._$form.querySelectorAll('.form-captcha').length > 0;
- // устанавливаем значение свойства _isAgreementSection в завимости от того имеется ли у формы секция с пользовательским соглашением или нет
- this._isAgreementSection = this._$form.querySelectorAll('.form-agreement').length > 0;
- // устанавливаем значения свойств _isAttachmentsSection и _attachmentsMaxItems в завимости от того имеется ли у формы секция с секцией для добавления к ней файлов
- var formAttachments = this._$form.querySelectorAll('.form-attachments');
- if (formAttachments.length) {
- this._isAttachmentsSection = true;
- if (formAttachments[0].getAttribute('data-count')) {
- this._attachmentsMaxItems = +formAttachments[0].getAttribute('data-count');
- }
- }
- this._setupListener.call(this);
+ const elFormAttachCount = this._elForm.querySelector('.form-attach__count');
+ elFormAttachCount ? elFormAttachCount.textContent = this._attach.maxItems : null;
+ this._addEventListener();
}
- _reset() {
- this._showForm();
- }
-
- // устанавливаем обработчики событий
- _setupListener() {
- var $form = this._$form;
- $form.onchange = function (e) {
- var $element = e.target;
- if ($element.name === 'agree') {
- this._changeStateSubmit(!$element.checked);
- }
- }.bind(this);
- $form.onsubmit = function (e) {
- var $form = e.target;
- if ($form === this._$form) {
- e.preventDefault();
- this._sendForm(this);
- }
- }.bind(this);
+ // добавляем обработчики для событий
+ _addEventListener() {
+ // обработка события submit
+ this._elForm.addEventListener('submit', (e) => {
+ e.preventDefault();
+ this._onSubmit();
+ });
// обработка события click
- $form.onclick = function (e) {
- var $element = e.target;
- if ($element.classList.contains('form-captcha__refresh')) {
- // при нажатии click на form-captcha__refresh
+ this._elForm.addEventListener('click', (e) => {
+ const target = e.target;
+ if (target.closest('.form-captcha__refresh')) {
e.preventDefault();
- this._refreshCaptcha();
- } else if ($element.classList.contains('form-attachments__item-link')) {
- // при нажатии click на form-attachments__item-link
- var id = +$element.dataset.id;
- var $item = $form.querySelector('.form-attachments__item[data-id="' + id + '"]');
- var $attachs = this._attachmentsItems;
- for (var i = 0, length = $attachs.length; i < length; i++) {
- if ($attachs[i].id === id) {
- $attachs.splice(i, 1);
- break;
+ this._reloadСaptcha();
+ } else if (target.closest('.form-attach__link')) {
+ const el = target.closest('.form-attach__item');
+ const index = +el.dataset.index;
+ this._attach.items.forEach((item, i) => {
+ if (item['index'] === index) {
+ this._attach.items.splice(i, 1);
+ el.remove();
+ return;
}
- }
- $item.parentElement.removeChild($item);
+ });
}
- }.bind(this);
-
- $form.parentElement.onclick = function (e) {
- var $element = e.target;
- if ($element.dataset.target === this._settings.selector) {
- e.preventDefault();
- this._showForm();
+ })
+ // обработка события change
+ this._elForm.addEventListener('change', (e) => {
+ const target = e.target;
+ if (target.name !== 'attach[]') {
+ return;
}
- }.bind(this);
-
- var _this = this;
-
- // если у формы имеется .form-attachment
- if (this._isAttachmentsSection) {
- // событие при изменении элемента input с type="file" (name="attachment[])
- _this._$form.addEventListener('change', function (e) {
- if (e.target.name !== 'attachment[]') {
- return;
+ for (let i = 0, length = target.files.length; i < length; i++) {
+ if (this._attach.items.length >= this._attach.maxItems) {
+ target.value = '';
+ break;
}
-
- var file, fileId, removeLink;
-
- for (var i = 0, length = e.target.files.length; i < length; i++) {
- if (_this._attachmentsItems.length === _this._attachmentsMaxItems) {
- e.target.value = '';
- break;
- }
- fileId = _this._attachmentsIdCounter++;
- file = e.target.files[i];
- _this._attachmentsItems.push({
- id: fileId,
- file: file,
+ const index = this._attach.index++;
+ const file = target.files[i];
+ this._attach.items.push({
+ index,
+ file
+ });
+ if (file.type.match(/image.*/)) {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.addEventListener('load', (e) => {
+ this._elForm.querySelector('.form-attach__items').innerHTML += ProcessForm._getAttachTemplate(index, file, e.target);
});
- if (file.type.match(/image.*/)) {
- var reader = new FileReader();
- reader.readAsDataURL(file);
- (function (file, fileId) {
- reader.addEventListener('load', function (e) {
- var removeLink = `<div class="form-attachments__item" data-id="${fileId}">
- <div class="form-attachments__item-wrapper">
- <img class="form-attachments__item-image" src="${e.target.result}" alt="${file.name}">
- <div class="form-attachments__item-name">${file.name}</div>
- <div class="form-attachments__item-size">${(file.size / 1024).toFixed(1)}Кб</div>
- <div class="form-attachments__item-link" data-id="${fileId}">×</div>
- </div></div>`;
- _this._$form.querySelector('.form-attachments__items').innerHTML += removeLink;
- });
- })(file, fileId);
- continue;
- }
- removeLink = `<div class="form-attachments__item" data-id="${fileId}">
- <div class="form-attachments__item-wrapper">
- <div class="form-attachments__item-name">${file.name}</div>
- <div class="form-attachments__item-size">${(file.size / 1024).toFixed(1)}Кб</div>
- <div class="form-attachments__item-link" data-id="${fileId}">×</div>
- </div></div>`;
- _this._$form.querySelector('.form-attachments__items').innerHTML += removeLink;
+ } else {
+ this._elForm.querySelector('.form-attach__items').innerHTML += ProcessForm._getAttachTemplate(index, file);
}
- e.target.value = null;
- });
+ }
+ target.value = '';
+ });
+ }
+ // сброс формы
+ reset() {
+ if (this._elForm.querySelector('.form-error')) {
+ this._elForm.querySelector('.form-error').classList.add('form-error_hide');
+ }
+ this._elForm.reset();
+ this._elForm.querySelectorAll('input, textarea').forEach(el => {
+ this._setStateValidaion(el);
+ });
+ document.querySelector('[name="captcha"]') ? this._reloadСaptcha() : null;
+ if (this._elForm.querySelector('.form-attach')) {
+ this._attach['index'] = 0;
+ this._attach['items'] = [];
+ this._elForm.querySelector('.form-attach__items').textContent = '';
+ if (this._elForm.querySelector('.is-invalid')) {
+ this._elForm.querySelector('.is-invalid').classList.remove('is-invalid');
+ }
+ }
+ if (this._elForm.querySelector('.form-progress')) {
+ const elProgressBar = this._elForm.querySelector('.progress-bar');
+ elProgressBar.setAttribute('aria-valuenow', '0');
+ elProgressBar.style.width = 0;
}
}
}
diff --git a/feedback/process/process.php b/feedback/process/process.php
index 72b978b..10221e2 100644
--- a/feedback/process/process.php
+++ b/feedback/process/process.php
@@ -138,34 +138,34 @@ if (IS_CHECK_CAPTCHA == true) {
}
/* 6 ЭТАП - ВАЛИДАЦИЯ ФАЙЛОВ */
-if (isset($_FILES['attachment'])) {
+if (isset($_FILES['attach'])) {
// перебор массива $_FILES['attachment']
- foreach ($_FILES['attachment']['error'] as $key => $error) {
+ foreach ($_FILES['attach']['error'] as $key => $error) {
// если файл был успешно загружен на сервер (ошибок не возникло), то...
if ($error == UPLOAD_ERR_OK) {
// получаем имя файла
- $fileName = $_FILES['attachment']['name'][$key];
+ $fileName = $_FILES['attach']['name'][$key];
// получаем расширение файла в нижнем регистре
$fileExtension = mb_strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// получаем размер файла
- $fileSize = $_FILES['attachment']['size'][$key];
+ $fileSize = $_FILES['attach']['size'][$key];
// результат проверки расширения файла
$resultCheckExtension = true;
// проверяем расширение загруженного файла
if (!in_array($fileExtension, ALLOWED_EXTENSIONS)) {
$resultCheckExtension = false;
- $data['attachment'][$key] = 'Файл имеет не разрешённый тип.';
+ $data['attach'][$key] = 'Файл имеет не разрешённый тип.';
$data['result'] = 'error';
log_write('Произошла ошибка! Файл ' . $fileName . ' имеет не разрешённый тип.');
}
// проверяем размер файла
if ($resultCheckExtension && ($fileSize > MAX_FILE_SIZE)) {
- $data['attachment'][$key] = 'Размер файла превышает допустимый.';
+ $data['attach'][$key] = 'Размер файла превышает допустимый.';
$data['result'] = 'error';
log_write('Произошла ошибка! Файл ' . $fileName . ' имеет не разрешённый размер.');
}
} else {
- $data['attachment'][$key] = 'Ошибка при загрузке файла.';
+ $data['attach'][$key] = 'Ошибка при загрузке файла.';
$data['result'] = 'error';
log_write('Произошла ошибка при загрузке файла на сервер!');
}
@@ -175,19 +175,19 @@ if (isset($_FILES['attachment'])) {
// переменная для хранения имён файлов
$attachments = array();
// перемещение файлов в директорию $uploadPath
- foreach ($_FILES['attachment']['name'] as $key => $attachment) {
+ foreach ($_FILES['attach']['name'] as $key => $attachment) {
// получаем имя файла
- $fileName = basename($_FILES['attachment']['name'][$key]);
+ $fileName = basename($_FILES['attach']['name'][$key]);
// получаем расширение файла в нижнем регистре
$fileExtension = mb_strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// временное имя файла на сервере
- $fileTmp = $_FILES['attachment']['tmp_name'][$key];
+ $fileTmp = $_FILES['attach']['tmp_name'][$key];
// создаём уникальное имя
$fileNewName = uniqid('upload_', true) . '.' . $fileExtension;
// перемещаем файл в директорию
if (!move_uploaded_file($fileTmp, $uploadPath . $fileNewName)) {
// ошибка при перемещении файла
- $data['attachment'][$key] = 'Ошибка при загрузке файла.';
+ $data['attach'][$key] = 'Ошибка при загрузке файла.';
$data['result'] = 'error';
log_write('Произошла ошибка при перемещении файла в директорию, определяемою переменной $uploadPath!');
} else {
@@ -306,4 +306,4 @@ if ($data['result'] == 'success' && IS_WRITE_LOG) {
}
/* ФИНАЛЬНЫЙ ЭТАП - ВОЗВРАЩАЕМ РЕЗУЛЬТАТЫ РАБОТЫ В ФОРМАТЕ JSON */
-echo json_encode($data); \ No newline at end of file
+echo json_encode($data);