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-02-20 13:47:47 +0300
committeritchief <alex.malcev1980@gmail.com>2022-02-20 13:47:47 +0300
commitd73bd294c7c5fbd1320accfef3bd5eee387ae237 (patch)
tree9e9c28159e161059d66e1c440741ff17cc877100
parent7a22933418e5475cd1caddbdbab075b89acefb13 (diff)
Update
-rw-r--r--feedback/js/process-forms1.js448
1 files changed, 448 insertions, 0 deletions
diff --git a/feedback/js/process-forms1.js b/feedback/js/process-forms1.js
new file mode 100644
index 0000000..429d6f3
--- /dev/null
+++ b/feedback/js/process-forms1.js
@@ -0,0 +1,448 @@
+/*!
+ * Форма обратной связи (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)
+ */
+
+class ProcessForm {
+ constructor(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();
+ }
+
+ // функция для проверки расширения файла
+ 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;
+ }
+
+ // переключение состояния 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');
+ }
+ }
+
+ // валидация формы
+ _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;
+ }
+ 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;
+ }
+
+ _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;
+ }
+ }
+
+ // собираем данные для отправки на сервер
+ _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;
+ };
+
+ // отправка формы
+ _sendForm() {
+ 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');
+ }
+ };
+
+ // функция для инициализации
+ _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() {
+ 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;
+ });
+ }
+ }
+}