diff options
author | itchief <alex.malcev1980@gmail.com> | 2022-02-23 14:52:52 +0300 |
---|---|---|
committer | itchief <alex.malcev1980@gmail.com> | 2022-02-23 14:52:52 +0300 |
commit | 593a44cf1dc1bb70c8489729b23cf1616e3bc31c (patch) | |
tree | 846541223899529a624fba8dda225a77e67a7b34 | |
parent | d73bd294c7c5fbd1320accfef3bd5eee387ae237 (diff) |
Update
-rw-r--r-- | feedback/css/style.css | 735 | ||||
-rw-r--r-- | feedback/index.html | 7 | ||||
-rw-r--r-- | feedback/js/process-forms.back.js | 381 | ||||
-rw-r--r-- | feedback/js/process-forms.js | 729 | ||||
-rw-r--r-- | feedback/js/process-forms1.js | 448 |
5 files changed, 1144 insertions, 1156 deletions
diff --git a/feedback/css/style.css b/feedback/css/style.css index 56fbd6e..c46eb52 100644 --- a/feedback/css/style.css +++ b/feedback/css/style.css @@ -1,38 +1,38 @@ *, *::before, *::after { - box-sizing: border-box; + box-sizing: border-box; } .d-none { - display: none !important; + display: none !important; } html { - font-family: sans-serif; - line-height: 1.15; - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #212529; - text-align: left; - background-color: #fff; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; } label { - display: inline-block; - margin-bottom: 0.5rem; + display: inline-block; + margin-bottom: 0.5rem; } button { - border-radius: 0; + border-radius: 0; } input, @@ -40,590 +40,591 @@ button, select, optgroup, textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } button, input { - overflow: visible; + overflow: visible; } button, select { - text-transform: none; + text-transform: none; } select { - word-wrap: normal; + word-wrap: normal; } button, [type="button"], [type="reset"], [type="submit"] { - -webkit-appearance: button; + -webkit-appearance: button; } button:not(:disabled), [type="button"]:not(:disabled), [type="reset"]:not(:disabled), [type="submit"]:not(:disabled) { - cursor: pointer; + cursor: pointer; } button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { - padding: 0; - border-style: none; + padding: 0; + border-style: none; } input[type="radio"], input[type="checkbox"] { - box-sizing: border-box; - padding: 0; + box-sizing: border-box; + padding: 0; } input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"] { - -webkit-appearance: listbox; + -webkit-appearance: listbox; } textarea { - overflow: auto; - resize: vertical; + overflow: auto; + resize: vertical; } .container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; - max-width: 560px; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; + max-width: 560px; } h1 { - margin-top: 0; - margin-bottom: 1rem; - font-size: 1.5rem; - font-weight: 500; - line-height: 1.2; - text-align: center; + margin-top: 0; + margin-bottom: 1rem; + font-size: 1.5rem; + font-weight: 500; + line-height: 1.2; + text-align: center; } .form__wrapper { - position: relative; - padding: 1rem; - border: 1px solid rgba(0, 0, 0, .125); - border-radius: 4px; - margin-bottom: 2rem; + position: relative; + padding: 1rem; + border: 1px solid rgba(0, 0, 0, .125); + border-radius: 4px; + margin-bottom: 2rem; } .form-row { - display: -ms-flexbox; - display: flex; - -ms-flex-wrap: wrap; - flex-wrap: wrap; - margin-right: -5px; - margin-left: -5px; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; } @media (min-width: 576px) { - .form-row > .form-group { - -ms-flex: 0 0 50%; - flex: 0 0 50%; - max-width: 50%; - padding-right: 5px; - padding-left: 5px; - } + .form-row>.form-group { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + padding-right: 5px; + padding-left: 5px; + } } .form-group { - margin-bottom: 1rem; + margin-bottom: 1rem; } .form-control { - display: block; - width: 100%; - height: calc(1.5em + .75rem + 2px); - padding: .375rem .75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #495057; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; - border-radius: .25rem; - transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; + display: block; + width: 100%; + height: calc(1.5em + .75rem + 2px); + padding: .375rem .75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: .25rem; + transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; } .form-control:focus { - color: #495057; - background-color: #fff; - border-color: #80bdff; - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, .25); + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, .25); } label { - display: inline-block; - margin-bottom: .5rem; + display: inline-block; + margin-bottom: .5rem; } .invalid-feedback { - display: none; - width: 100%; - margin-top: .25rem; - font-size: 80%; - color: #dc3545; + display: none; + width: 100%; + margin-top: .25rem; + font-size: 80%; + color: #dc3545; } input[type=checkbox], input[type=radio] { - box-sizing: border-box; - padding: 0; + box-sizing: border-box; + padding: 0; } textarea.form-control { - overflow: auto; - resize: vertical; - height: auto; + overflow: auto; + resize: vertical; + height: auto; } /* Стили для секции, с помощью которой можно добавить к форме файлы */ +.form__attach-label { + margin-bottom: 0.5rem; +} + .form-attachments__wrapper { - position: relative; - background: #fff; - border: 3px dashed #ccc; - border-radius: 5px; - min-height: 50px; - display: flex; - align-items: center; - justify-content: center; - padding: 4px; + 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 { - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: 100%; - filter: alpha(opacity=0); - opacity: 0; - outline: none; - cursor: pointer; - display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 100%; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + cursor: pointer; + display: block; } .form-attachments__description { - width: 100%; - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - padding: 15px 10px; -} - -.form-attachments__description > :first-child { - font-weight: 700; -} - -.form-attachments__description > :last-child { - color: #6c757d; - font-size: 0.8125rem; + width: 100%; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + padding: 15px 10px; + color: #757575; } .form-attachments__items { - display: flex; - flex-wrap: wrap; - flex: 0 0 100%; + display: flex; + flex-wrap: wrap; + flex: 0 0 100%; } .form-attachments__item { - flex: 0 0 25%; - overflow: hidden; - padding: 4px; - font-size: 0.75rem; + flex: 0 0 25%; + overflow: hidden; + padding: 4px; + font-size: 0.75rem; } .form-attachments__item-wrapper { - border: 1px solid #e0e0e0; - background: #f5f5f5; - padding: 26px 4px; - border-radius: 4px; - position: relative; - min-height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; + border: 1px solid #eee; + padding: 1.625rem 0.25rem; + border-radius: 0.25rem; + position: relative; + min-height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; } .form-attachments__item-image { - display: block; - max-width: 100%; - height: auto; - margin-top: auto; - margin-left: auto; - margin-right: auto; + display: block; + max-width: 100%; + height: auto; + margin-top: auto; + margin-left: auto; + margin-right: auto; + border: 1px solid #eee; } .form-attachments__item-name { - margin-top: auto; - max-width: 100%; - overflow: hidden; - word-wrap: break-word; + margin-top: auto; + max-width: 100%; + overflow: hidden; + word-wrap: break-word; + color: #757575; + text-align: center; } .form-attachments__item-size { - position: absolute; - bottom: 0; - left: 0; - right: 0; - padding: 3px 6px; - font-weight: bold; - text-align: right; + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 3px 6px; + font-weight: bold; + text-align: right; } .form-attachments__item-link { - position: absolute; - top: 0; - right: 0; - padding: 0 .375rem 0.5rem; - color: inherit; - cursor: pointer; - float: right; - font-size: 1.5rem; - font-weight: 700; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - opacity: .5; + position: absolute; + top: 0; + right: 0; + padding: 0 .375rem 0.5rem; + color: inherit; + cursor: pointer; + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; } .form-attachments__item.is-valid .form-attachments__item-wrapper { - border-color: #28a745; - background-color: #f8fcf9; + border-color: #28a745; + background-color: #f8fcf9; } .form-attachments__item.is-invalid .form-attachments__item-wrapper { - border-color: #dc3545; - background-color: #fefbfb; + border-color: #dc3545; + background-color: #fefbfb; +} + +.text-sm { + font-size: 0.75rem; } /* Стили секции, в которой выводится капча */ .form-captcha { - display: flex; - align-items: center; - margin-bottom: 2rem; + display: flex; + align-items: center; + margin-bottom: 2rem; } .form-captcha__image { - border-radius: 4px; - margin-right: 0.5rem; + border-radius: 4px; + margin-right: 0.5rem; } .form-captcha__refresh { - padding: 0.5rem 0.75rem; - line-height: 1; - border: 1px solid #ced4da; - margin-right: 0.5rem; - border-radius: 4px; - cursor: pointer; + padding: 0.5rem 0.75rem; + line-height: 1; + border: 1px solid #ced4da; + margin-right: 0.5rem; + border-radius: 4px; + cursor: pointer; } .form-captcha__refresh:hover { - background-color: #eee; + background-color: #eee; } .form-captcha__input { - flex-grow: 1; - position: relative; - margin-bottom: 0; + flex-grow: 1; + position: relative; + margin-bottom: 0; } @media (max-width: 400px) { - .form-captcha { - flex-wrap: wrap; - } + .form-captcha { + flex-wrap: wrap; + } - .form-captcha__input { - min-width: 200px; - margin-top: 0.5rem; - } + .form-captcha__input { + min-width: 200px; + margin-top: 0.5rem; + } } .form-captcha__input .invalid-feedback { - position: absolute; - bottom: -1.5rem; + position: absolute; + bottom: -1.5rem; } /* CSS для секции "Пользовтельское соглашение" */ .custom-control { - position: relative; - display: block; - min-height: 1.5rem; - padding-left: 1.5rem; + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; } .custom-control-input { - position: absolute; - z-index: -1; - opacity: 0; + position: absolute; + z-index: -1; + opacity: 0; } .custom-control-label { - position: relative; - margin-bottom: 0; - vertical-align: top; + position: relative; + margin-bottom: 0; + vertical-align: top; } .custom-checkbox .custom-control-label::before { - border-radius: .25rem; + border-radius: .25rem; } .custom-control-label::before { - transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; } .custom-control-label::before { - position: absolute; - top: .25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - pointer-events: none; - content: ""; - background-color: #fff; - border: #adb5bd solid 1px; + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; } .custom-control-label::after { - position: absolute; - top: .25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background: no-repeat 50%/50% 50%; + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50%/50% 50%; } -.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); +.custom-checkbox .custom-control-input:checked~.custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); } .custom-control-label::after { - position: absolute; - top: .25rem; - left: -1.5rem; - display: block; - width: 1rem; - height: 1rem; - content: ""; - background: no-repeat 50%/50% 50%; + position: absolute; + top: .25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50%/50% 50%; } -.custom-control-input:checked ~ .custom-control-label::before { - color: #fff; - border-color: #007bff; - background-color: #007bff; +.custom-control-input:checked~.custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff; } -.custom-control-input:focus ~ .custom-control-label::before { - box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); +.custom-control-input:focus~.custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } -.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { - border-color: #80bdff; +.custom-control-input:focus:not(:checked)~.custom-control-label::before { + border-color: #80bdff; } -.custom-control-input:active ~ .custom-control-label::before { - color: #fff; - background-color: #b3d7ff; - border-color: #b3d7ff; +.custom-control-input:active~.custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff; } .form-agreement label { - font-size: 0.875rem; + font-size: 0.875rem; } /* CSS для индикации полей форме при их валидности */ .form-control.is-valid { - border-color: #28a745; - padding-right: calc(1.5em + .75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: center right calc(.375em + .1875rem); - background-size: calc(.75em + .375rem) calc(.75em + .375rem); + border-color: #28a745; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: center right calc(.375em + .1875rem); + background-size: calc(.75em + .375rem) calc(.75em + .375rem); } textarea.form-control.is-valid { - padding-right: calc(1.5em + .75rem); - background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem); + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem); } .form-control.is-valid:focus { - border-color: #28a745; - box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, .25); + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, .25); } /* CSS для индикации полей форме при их не валидности */ .form-control.is-invalid { - border-color: #dc3545; - padding-right: calc(1.5em + .75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); - background-repeat: no-repeat; - background-position: center right calc(.375em + .1875rem); - background-size: calc(.75em + .375rem) calc(.75em + .375rem); + border-color: #dc3545; + padding-right: calc(1.5em + .75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); + background-repeat: no-repeat; + background-position: center right calc(.375em + .1875rem); + background-size: calc(.75em + .375rem) calc(.75em + .375rem); } textarea.form-control.is-invalid { - padding-right: calc(1.5em + .75rem); - background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem); + padding-right: calc(1.5em + .75rem); + background-position: top calc(.375em + .1875rem) right calc(.375em + .1875rem); } .form-control.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, .25); + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, .25); } -.form-control.is-invalid ~ .invalid-feedback { - display: block; +.form-control.is-invalid~.invalid-feedback { + display: block; } /* CSS для индикатора */ .progress { - display: -ms-flexbox; - display: flex; - height: 1rem; - overflow: hidden; - font-size: .75rem; - background-color: #e9ecef; - border-radius: .25rem; - margin-bottom: 0.5rem; + display: -ms-flexbox; + display: flex; + height: 1rem; + overflow: hidden; + font-size: .75rem; + background-color: #e9ecef; + border-radius: .25rem; + margin-bottom: 0.5rem; } .progress-bar { - display: -ms-flexbox; - display: flex; - -ms-flex-direction: column; - flex-direction: column; - -ms-flex-pack: center; - justify-content: center; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #007bff; - transition: width .6s ease; - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; + display: -ms-flexbox; + display: flex; + -ms-flex-direction: column; + flex-direction: column; + -ms-flex-pack: center; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width .6s ease; + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; } .form-submit { - text-align: right; + text-align: right; } /* CSS для кнопки submit */ button[type="submit"] { - display: inline-block; - font-weight: 400; - color: #fff; - text-align: center; - vertical-align: middle; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-color: #007bff; - border: 1px solid #007bff; - padding: .375rem .75rem .375rem 2rem; - font-size: 1rem; - line-height: 1.5; - border-radius: .25rem; - transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; - position: relative; + display: inline-block; + font-weight: 400; + color: #fff; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: #007bff; + border: 1px solid #007bff; + padding: .375rem .75rem .375rem 2rem; + font-size: 1rem; + line-height: 1.5; + border-radius: .25rem; + transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out; + position: relative; } button[type="submit"]::before { - content: ""; - position: absolute; - width: 1rem; - height: 1rem; - left: .5rem; - top: 50%; - transform: translateY(-50%); - background: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512.333 512'%3E%3Cpath fill='%23fff' d='M476 3.2L12.5 270.6c-18.1 10.4-15.8 35.6 2.2 43.2L121 358.4l287.3-253.2c5.5-4.9 13.3 2.6 8.6 8.3L176 407v80.5c0 23.6 28.5 32.9 42.5 15.8L282 426l124.6 52.2c14.2 6 30.4-2.9 33-18.2l72-432C515 7.8 493.3-6.8 476 3.2z'/%3E%3C/svg%3E") transparent no-repeat center center; - background-size: 100% 100%; + content: ""; + position: absolute; + width: 1rem; + height: 1rem; + left: .5rem; + top: 50%; + transform: translateY(-50%); + background: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512.333 512'%3E%3Cpath fill='%23fff' d='M476 3.2L12.5 270.6c-18.1 10.4-15.8 35.6 2.2 43.2L121 358.4l287.3-253.2c5.5-4.9 13.3 2.6 8.6 8.3L176 407v80.5c0 23.6 28.5 32.9 42.5 15.8L282 426l124.6 52.2c14.2 6 30.4-2.9 33-18.2l72-432C515 7.8 493.3-6.8 476 3.2z'/%3E%3C/svg%3E") transparent no-repeat center center; + background-size: 100% 100%; } button[type="submit"]:hover { - color: #fff; - background-color: #0069d9; - border-color: #0062cc; + color: #fff; + background-color: #0069d9; + border-color: #0062cc; } button[type="submit"]:focus { - outline: 0; - box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); } button[type="submit"]:disabled { - opacity: 0.65; + opacity: 0.65; } /* Стили для сообщения об ошибках */ .form-error { - position: relative; - padding: .5rem 1rem 0.6rem; - margin-bottom: 1rem; - border-radius: .25rem; - color: #dc3545; - background-color: #dc35450d; - border: 1px solid #dc3545; + position: relative; + padding: .5rem 1rem 0.6rem; + margin-bottom: 1rem; + border-radius: .25rem; + color: #dc3545; + background-color: #dc35450d; + border: 1px solid #dc3545; } /* Стили для сообщения об успешной отправки */ .form-result-success { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 1000; - display: flex; - text-align: center; - justify-content: center; - align-items: center; - color: #fff; - background: rgba(0, 0, 0, .6); - font-size: 1.25rem; - border-radius: 4px; -} - -.form-result-success > div { - position: relative; - padding: .75rem 1.25rem; - margin-bottom: 1rem; - border: 1px solid #eff4f1; - z-index: 1001; - border-radius: 0; - color: #28a745; - background-color: #eff4f1; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; + text-align: center; + justify-content: center; + align-items: center; + color: #fff; + background: rgba(0, 0, 0, .6); + font-size: 1.25rem; + border-radius: 4px; +} + +.form-result-success>div { + position: relative; + padding: .75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid #eff4f1; + z-index: 1001; + border-radius: 0; + color: #28a745; + background-color: #eff4f1; } .form-result-success a { - color: #28a745; - background-color: transparent; - font-weight: 700; -}
\ No newline at end of file + color: #28a745; + background-color: transparent; + font-weight: 700; +} diff --git a/feedback/index.html b/feedback/index.html index 240faff..bb49b39 100644 --- a/feedback/index.html +++ b/feedback/index.html @@ -42,12 +42,13 @@ <!-- Файлы, для прикрепления к форме -->
<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>Можно добавить до 5 файлов с разрешением jpg, jpeg, bmp, gif, png и размером до 512 Кбайт.</div>
+ <div>Нажмите для загрузки файлов или перетащите их</div>
+ <div class="text-sm">PNG, JPG, GIF (до 512 Кбайт)</div>
</div>
</div>
</div>
@@ -110,7 +111,7 @@ </div>
- <script src="/feedback/js/process-forms-without-jquery.js"></script>
+ <script src="/feedback/js/process-forms.js"></script>
<script>
/*
Параметры указываются в виде:
diff --git a/feedback/js/process-forms.back.js b/feedback/js/process-forms.back.js new file mode 100644 index 0000000..c29da81 --- /dev/null +++ b/feedback/js/process-forms.back.js @@ -0,0 +1,381 @@ +/*! + * Форма обратной связи (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.js b/feedback/js/process-forms.js index c29da81..5e2017b 100644 --- a/feedback/js/process-forms.js +++ b/feedback/js/process-forms.js @@ -5,14 +5,13 @@ * Licensed under MIT (https://github.com/itchief/feedback-form/blob/master/LICENSE) */ -'use strict'; - -var ProcessForm = function (settings) { +class ProcessForm { + constructor(settings) { this._settings = { - selector: '#feedback-form', // дефолтный селектор - attachmentsMaxFileSize: 512, // дефолтный максимальный размер файла в Кб - attachmentsFileExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов - isUseDefaultSuccessMessage: true // отображать дефолтное сообщение об успешной отправки формы + selector: '#feedback-form', // дефолтный селектор + attachmentsMaxFileSize: 512, // дефолтный максимальный размер файла в Кб + attachmentsFileExt: ['jpg', 'jpeg', 'bmp', 'gif', 'png'], // дефолтные допустимые расширения для файлов + isUseDefaultSuccessMessage: true, // отображать дефолтное сообщение об успешной отправки формы }; this._isCaptchaSection = false; // имеется ли в форме блок с капчей this._isAgreementSection = false; // имеется ли в форме блок с пользовательским соглашением @@ -22,360 +21,414 @@ var ProcessForm = function (settings) { this._attachmentsItems = []; // переменная, хранящая массив файлов, которые нужно прекрепить к форме for (var propName in settings) { - if (settings.hasOwnProperty(propName)) { - this._settings[propName] = settings[propName]; + if (settings.hasOwnProperty(propName)) { + this._settings[propName] = settings[propName]; + } + } + this._$form = document.querySelector(this._settings.selector); + this._elForm = this._$form; + // инициализация формы + 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; } + } } - this._form = $(this._settings.selector).eq(0); -}; + return false; + } -ProcessForm.prototype = function () { - // переключить во включенное или выключенное состояние кнопку submit - var _changeStateSubmit = function (_this, state) { - _this._form.find('[type="submit"]').prop('disabled', state); - }; + // переключение состояния disabled у кнопки submit + _changeStateSubmit(state) { + this._$form.querySelector('[type="submit"]').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); - }; + // обновление капчи + _refreshCaptcha() { + var captchaImg = this._elForm.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; + } + } - // изменение состояния элемента формы (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'); + // для теста > + 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 { - input.removeClass('is-valid is-invalid'); + var attach = this._$form.querySelector('.form-attachments__item[data-id="' + i + '"]'); + if (attach) { + attach.setAttribute('title', ''); + attach.classList.add('is-valid'); + } } - }; + } + } + return valid; + } - // метод, возвращающий результат проверки расширения файла допустимому - 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; - }; + _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); + } - // валилация формы - 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; - }; + // удаление 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]); + } + } - 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); + 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; + }; + + _successXHR(data) { + // при получении успешного ответа от сервера + var _this = this; + + const elProgress = this._elForm.querySelector('.progress'); + if (elProgress) { + elProgress.classList.add('d-none'); + const elProgressBar = elProgress.querySelector('.progress-bar'); + elProgressBar.setAttribute('aria-valuenow', '0'); + elProgressBar.style.width = '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.length; i < length; i++) { + its[i].classList.add('is-valid'); + } + } + + _errorXHR() { + this._elForm.querySelector('.form-error').classList.remove('d-none'); + } + + // отправка формы + _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 { - _changeStateSubmit(_this, false); - } - if (_this._isAttachmentsSection) { - _this._form.find('.form-attachments__item').remove(); + this._$form.querySelectorAll('.is-invalid')[0].focus(); } - if (_this._form.find('.progress-bar').length) { - _this._form.find('.progress-bar') - .attr('aria-valuenow', '0') - .width('0') - .find('.sr-only').text('0%'); - } - }; + } + return; + } - var _changeStateImages = function (_this, state) { - if (!_this._isAttachmentsSection) { - return; - } - _this._form.find('[name="attachment[]"]').prop('disabled', state); - }; + this._$form.querySelector('.form-error').classList.add('d-none'); - // собираем данные для отправки на сервер - 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; - }; + this._changeStateSubmit(true); - // отправка формы - 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; - } + var xhr = new XMLHttpRequest(); + xhr.open('POST', this._elForm.action); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status == 200) { + this._successXHR(xhr.response); + } else { + this._errorXHR(); + } + } - if (!_this._form.find('.form-error').hasClass('d-none')) { - _this._form.find('.form-error').addClass('d-none'); - } + this._elForm.querySelector('.progress').classList.remove('d-none'); + xhr.upload.onprogress = (e) => { + if (e.lengthComputable) { + const value = ((e.loaded * 100) / e.total).toFixed(1); + const el = this._elForm.querySelector('.progress-bar'); + el.setAttribute('aria-valuenow', value); + el.style.width = value + '%'; + } + } - $.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) - }; + xhr.send(this._collectData()); + }; - // при получении успешного ответа от сервера - 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(); - } + // функция для инициализации + _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; + } } - _this._form.find('.form-attachments__item').not('.is-invalid').addClass('is-valid'); - }; + $item.parentElement.removeChild($item); + } + }.bind(this); - // если не получили успешный ответ от сервера - var _error = function () { - this._form.find('.form-error').removeClass('d-none'); - }; + $form.parentElement.onclick = function (e) { + var $element = e.target; + if ($element.dataset.target === this._settings.selector) { + e.preventDefault(); + this._showForm(); + } + }.bind(this); - // функция для инициализации - 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'); - } + 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; } - _setupListener(this); - }; - var _reset = function () { - _showForm(this); - }; + var file, fileId, removeLink; - // устанавливаем обработчики событий - 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; - }); + 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; } - }; - return { - init: _init, - reset: _reset + e.target.value = null; + }); } -}(); + } +} diff --git a/feedback/js/process-forms1.js b/feedback/js/process-forms1.js deleted file mode 100644 index 429d6f3..0000000 --- a/feedback/js/process-forms1.js +++ /dev/null @@ -1,448 +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) - */ - -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; - }); - } - } -} |