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>2020-10-24 12:58:48 +0300
committeritchief <alex.malcev1980@gmail.com>2020-10-24 12:58:48 +0300
commit8e1c87f9db670c9a9c327ae335789cc3864121f2 (patch)
tree958dc509cd5e75e6208cd1136c893de2e2805c86
parentc1b59068e5d0b7e446a6a82f0508fbb50f47f0fa (diff)
add js without jquery
-rw-r--r--feedback/index.html199
-rw-r--r--feedback/js/process-forms-without-jquery.js474
2 files changed, 573 insertions, 100 deletions
diff --git a/feedback/index.html b/feedback/index.html
index 35bbce8..754d463 100644
--- a/feedback/index.html
+++ b/feedback/index.html
@@ -9,132 +9,131 @@
<body>
-<div class="container">
- <h1>Форма обратной связи</h1>
-
- <div class="form__wrapper">
- <!-- Форма обратной связи -->
- <form id="feedback-form" action="/feedback/process/process.php" enctype="multipart/form-data" novalidate>
- <div class="form-row">
- <!-- Имя пользователя -->
- <div class="form-group">
- <label for="name" class="control-label">Имя</label>
- <input id="name" type="text" name="name" class="form-control" value="" placeholder="Имя" minlength="2"
- maxlength="30" required="required">
- <div class="invalid-feedback"></div>
+ <div class="container">
+ <h1>Форма обратной связи</h1>
+
+ <div class="form__wrapper">
+ <!-- Форма обратной связи -->
+ <form id="feedback-form" action="/feedback/process/process.php" enctype="multipart/form-data" novalidate>
+ <div class="form-row">
+ <!-- Имя пользователя -->
+ <div class="form-group">
+ <label for="name" class="control-label">Имя</label>
+ <input id="name" type="text" name="name" class="form-control" value="" placeholder="Имя" minlength="2"
+ maxlength="30" required="required">
+ <div class="invalid-feedback"></div>
+ </div>
+ <!-- Email пользователя -->
+ <div class="form-group">
+ <label for="email" class="control-label">Email-адрес</label>
+ <input id="email" type="email" name="email" required="required" class="form-control" value=""
+ placeholder="Email-адрес">
+ <div class="invalid-feedback"></div>
+ </div>
</div>
- <!-- Email пользователя -->
+ <!-- Сообщение пользователя -->
<div class="form-group">
- <label for="email" class="control-label">Email-адрес</label>
- <input id="email" type="email" name="email" required="required" class="form-control" value=""
- placeholder="Email-адрес">
+ <label for="message" class="control-label">Сообщение (не менее 20 символов)</label>
+ <textarea id="message" name="message" class="form-control" rows="3"
+ placeholder="Сообщение (не менее 20 символов)" minlength="20" maxlength="500"
+ required="required"></textarea>
<div class="invalid-feedback"></div>
</div>
- </div>
- <!-- Сообщение пользователя -->
- <div class="form-group">
- <label for="message" class="control-label">Сообщение (не менее 20 символов)</label>
- <textarea id="message" name="message" class="form-control" rows="3"
- placeholder="Сообщение (не менее 20 символов)" minlength="20" maxlength="500"
- required="required"></textarea>
- <div class="invalid-feedback"></div>
- </div>
- <!-- Файлы, для прикрепления к форме -->
- <div class="form-group form-attachments" data-count="5">
- <div class="form-attachments__wrapper">
- <input type="file" name="attachment[]" multiple>
- <div class="form-attachments__items">
- <div class="form-attachments__description">
- <div>Нажмите чтобы добавить файлы к сообщению.</div>
- <div>Можно добавить до 5 файлов с разрешением jpg, jpeg, bmp, gif, png и размером до 512 Кбайт.</div>
+ <!-- Файлы, для прикрепления к форме -->
+ <div class="form-group form-attachments" data-count="5">
+ <div class="form-attachments__wrapper">
+ <input type="file" name="attachment[]" multiple>
+ <div class="form-attachments__items">
+ <div class="form-attachments__description">
+ <div>Нажмите чтобы добавить файлы к сообщению.</div>
+ <div>Можно добавить до 5 файлов с разрешением jpg, jpeg, bmp, gif, png и размером до 512 Кбайт.</div>
+ </div>
</div>
</div>
</div>
- </div>
- <!-- Капча -->
- <div class="form-group form-captcha">
- <img class="form-captcha__image" src="/feedback/captcha/captcha.php" data-src="/feedback/captcha/captcha.php"
- width="132" height="46" alt="Капча">
- <div class="form-captcha__refresh">
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
- <path fill="currentColor"
- d="M440.65 12.57l4 82.77A247.16 247.16 0 0 0 255.83 8C134.73 8 33.91 94.92 12.29 209.82A12 12 0 0 0 24.09 224h49.05a12 12 0 0 0 11.67-9.26 175.91 175.91 0 0 1 317-56.94l-101.46-4.86a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12H500a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12h-47.37a12 12 0 0 0-11.98 12.57zM255.83 432a175.61 175.61 0 0 1-146-77.8l101.8 4.87a12 12 0 0 0 12.57-12v-47.4a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12V500a12 12 0 0 0 12 12h47.35a12 12 0 0 0 12-12.6l-4.15-82.57A247.17 247.17 0 0 0 255.83 504c121.11 0 221.93-86.92 243.55-201.82a12 12 0 0 0-11.8-14.18h-49.05a12 12 0 0 0-11.67 9.26A175.86 175.86 0 0 1 255.83 432z">
- </path>
- </svg>
- </div>
- <div class="form-group form-captcha__input">
- <label for="captcha" class="control-label d-none">Код, показанный на изображении</label>
- <input type="text" name="captcha" maxlength="6" required="required" id="captcha"
- class="form-control captcha" placeholder="******" autocomplete="off" value="">
- <div class="invalid-feedback"></div>
+ <!-- Капча -->
+ <div class="form-group form-captcha">
+ <img class="form-captcha__image" src="/feedback/captcha/captcha.php" data-src="/feedback/captcha/captcha.php"
+ width="132" height="46" alt="Капча">
+ <div class="form-captcha__refresh">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="16" height="16">
+ <path fill="currentColor"
+ d="M440.65 12.57l4 82.77A247.16 247.16 0 0 0 255.83 8C134.73 8 33.91 94.92 12.29 209.82A12 12 0 0 0 24.09 224h49.05a12 12 0 0 0 11.67-9.26 175.91 175.91 0 0 1 317-56.94l-101.46-4.86a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12H500a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12h-47.37a12 12 0 0 0-11.98 12.57zM255.83 432a175.61 175.61 0 0 1-146-77.8l101.8 4.87a12 12 0 0 0 12.57-12v-47.4a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12V500a12 12 0 0 0 12 12h47.35a12 12 0 0 0 12-12.6l-4.15-82.57A247.17 247.17 0 0 0 255.83 504c121.11 0 221.93-86.92 243.55-201.82a12 12 0 0 0-11.8-14.18h-49.05a12 12 0 0 0-11.67 9.26A175.86 175.86 0 0 1 255.83 432z">
+ </path>
+ </svg>
+ </div>
+ <div class="form-group form-captcha__input">
+ <label for="captcha" class="control-label d-none">Код, показанный на изображении</label>
+ <input type="text" name="captcha" maxlength="6" required="required" id="captcha"
+ class="form-control captcha" placeholder="******" autocomplete="off" value="">
+ <div class="invalid-feedback"></div>
+ </div>
</div>
- </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 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>
- </div>
- <!-- Сообщение при ошибке -->
- <div class="form-error d-none">
- Исправьте данные и отправьте форму ещё раз.
- </div>
+ <!-- Сообщение при ошибке -->
+ <div class="form-error d-none">
+ Исправьте данные и отправьте форму ещё раз.
+ </div>
- <!-- Индикация отправки данных формы на сервер -->
- <div class="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="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>
- </div>
+ <!-- Кнопка для отправки формы на сервер -->
+ <div class="form-submit">
+ <button type="submit" disabled>Отправить сообщение</button>
+ </div>
- </form>
+ </form>
- <!-- Сообщение об успешной отправки формы -->
- <div class="form-result-success d-none">
- <div>Форма успешно отправлена. Нажмите на <a href="#" data-target="#feedback-form">ссылку</a>, чтобы отправить ещё
- одно сообщение.
+ <!-- Сообщение об успешной отправки формы -->
+ <div class="form-result-success d-none">
+ <div>Форма успешно отправлена. Нажмите на <a href="#" data-target="#feedback-form">ссылку</a>, чтобы отправить
+ ещё одно сообщение.</div>
</div>
+
</div>
</div>
-</div>
-
-<script src="/feedback/vendors/jquery/jquery-3.4.1.min.js"></script>
-<script src="/feedback/js/process-forms.js"></script>
-<script>
+ <script src="/feedback/vendors/jquery/jquery-3.4.1.min.js"></script>
+ <script src="/feedback/js/process-forms-without-jquery.js"></script>
+ <script>
//после загрузки DOM
$(function () {
- /*
- Параметры указываются в виде:
- {
- ключ: значение;
- ключ: значение;
- ...
- }
- Основные параметры
- selector - селектор формы (по умолчанию '#feedback-form')
- attachmentsMaxFileSize - максимальный размер файла в Кбайтах (по умолчанию 512)
- attachmentsFileExt - допустимые расширения файлов для загрузки (по умолчанию 'jpg','jpeg','bmp','gif','png')
- isUseDefaultSuccessMessage - отображать дефолтное сообщение после отправки
- */
- var form1 = new ProcessForm();
- form1.init();
+ /*
+ Параметры указываются в виде:
+ {
+ ключ: значение;
+ ключ: значение;
+ ...
+ }
+ Основные параметры
+ selector - селектор формы (по умолчанию '#feedback-form')
+ attachmentsMaxFileSize - максимальный размер файла в Кбайтах (по умолчанию 512)
+ attachmentsFileExt - допустимые расширения файлов для загрузки (по умолчанию 'jpg','jpeg','bmp','gif','png')
+ isUseDefaultSuccessMessage - отображать дефолтное сообщение после отправки
+ */
+ var form1 = new ProcessForm();
+ form1.init();
});
-</script>
+ </script>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/feedback/js/process-forms-without-jquery.js b/feedback/js/process-forms-without-jquery.js
new file mode 100644
index 0000000..853c541
--- /dev/null
+++ b/feedback/js/process-forms-without-jquery.js
@@ -0,0 +1,474 @@
+/*!
+ * Форма обратной связи (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;
+ });
+ }
+};