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

github.com/itchief/feedbackForm.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoritchief <alex.malcev1980@gmail.com>2022-02-23 14:52:52 +0300
committeritchief <alex.malcev1980@gmail.com>2022-02-23 14:52:52 +0300
commit593a44cf1dc1bb70c8489729b23cf1616e3bc31c (patch)
tree846541223899529a624fba8dda225a77e67a7b34
parentd73bd294c7c5fbd1320accfef3bd5eee387ae237 (diff)
Update
-rw-r--r--feedback/css/style.css735
-rw-r--r--feedback/index.html7
-rw-r--r--feedback/js/process-forms.back.js381
-rw-r--r--feedback/js/process-forms.js729
-rw-r--r--feedback/js/process-forms1.js448
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;
- });
- }
- }
-}