diff options
author | itchief <alex.malcev1980@gmail.com> | 2022-03-09 07:56:11 +0300 |
---|---|---|
committer | itchief <alex.malcev1980@gmail.com> | 2022-03-09 07:56:11 +0300 |
commit | 42bfc7e4b18127401d74d7d77d9193331e62a4bf (patch) | |
tree | bd88cc22f8a87c8968db781632f8f65f4773d87e | |
parent | 593a44cf1dc1bb70c8489729b23cf1616e3bc31c (diff) |
Updatev4.0.0
-rw-r--r-- | feedback/css/style.css | 160 | ||||
-rw-r--r-- | feedback/index.html | 72 | ||||
-rw-r--r-- | feedback/js/process-forms-without-jquery.js | 474 | ||||
-rw-r--r-- | feedback/js/process-forms.back.js | 381 | ||||
-rw-r--r-- | feedback/js/process-forms.dev.js | 420 | ||||
-rw-r--r-- | feedback/js/process-forms.js | 529 | ||||
-rw-r--r-- | feedback/process/process.php | 24 |
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); |