1(function () { 2 'use strict'; 3 4 class FetchIt { 5 static forms = []; 6 static instances = new Map(); 7 static events = { 8 before: 'fetchit:before', 9 success: 'fetchit:success', 10 error: 'fetchit:error', 11 after: 'fetchit:after', 12 reset: 'fetchit:reset', 13 } 14 15 constructor (form, config) { 16 if (!(form instanceof HTMLFormElement)) { 17 throw new Error('Ðе ÑоÑма'); 18 } 19 20 this.form = form; 21 this.config = config; 22 23 this.request = new Request(this.config.actionUrl, { 24 method: 'post', 25 credentials: 'same-origin', 26 headers: { 27 'Accept': 'application/json', 28 'X-FetchIt-Action': this.config.action, 29 }, 30 }); 31 32 this.prepareEvents(); 33 34 FetchIt.forms.push(this.form); 35 FetchIt.instances.set(this.form, this); 36 } 37 38 prepareEvents() { 39 this.form.addEventListener('submit', async e => { 40 e.preventDefault(); 41 42 this.formData = new FormData(this.form); 43 this.formData.set('pageId', this.config.pageId); 44 45 this.clearErrors(); 46 47 const beforeEvent = new CustomEvent(FetchIt.events.before, { 48 cancelable: true, 49 detail: { 50 form: this.form, 51 formData: this.formData, 52 fetchit: this, 53 }, 54 }); 55 56 FetchIt?.Message?.before?.(); 57 58 if (!document.dispatchEvent(beforeEvent)) { 59 return; 60 } 61 62 this.disableFields(); 63 64 try { 65 const query = await fetch(this.request, { body: this.formData }); 66 const response = await query.json(); 67 68 const afterEvent = new CustomEvent(FetchIt.events.after, { 69 cancelable: true, 70 detail: { 71 form: this.form, 72 formData: this.formData, 73 response, 74 fetchit: this, 75 }, 76 }); 77 78 FetchIt?.Message?.after?.(response.message); 79 80 if (!document.dispatchEvent(afterEvent)) { 81 return; 82 } 83 84 if (!response.success) { 85 FetchIt?.Message?.error?.(response.message); 86 87 const errorEvent = new CustomEvent(FetchIt.events.error, { 88 cancelable: true, 89 detail: { 90 form: this.form, 91 formData: this.formData, 92 response, 93 fetchit: this, 94 }, 95 }); 96 97 if (!document.dispatchEvent(errorEvent)) { 98 return; 99 } 100 101 for (const [ name, message ] of Object.entries(response.data)) { 102 this.setError(name, message); 103 } 104 105 return; 106 } 107 108 this.clearErrors(); 109 FetchIt?.Message?.success?.(response.message); 110 111 const successEvent = new CustomEvent(FetchIt.events.success, { 112 detail: { 113 form: this.form, 114 formData: this.formData, 115 response, 116 fetchit: this, 117 }, 118 }); 119 120 if (!document.dispatchEvent(successEvent)) { 121 return; 122 } 123 124 if (typeof window.grecaptcha !== 'undefined') { 125 window.grecaptcha.reset(); 126 } 127 128 if (this.config.clearFieldsOnSuccess) { 129 this.form.reset(); 130 } 131 } catch (e) { 132 console.error(e); 133 } finally { 134 this.enableFields(); 135 } 136 }); 137 138 this.form.addEventListener('reset', () => { 139 const resetEvent = new CustomEvent(FetchIt.events.reset, { 140 detail: { 141 form: this.form, 142 fetchit: this, 143 }, 144 }); 145 146 document.dispatchEvent(resetEvent); 147 this.clearErrors(); 148 FetchIt?.Message?.reset?.(); 149 }); 150 151 ['change', 'input'].forEach(eventName => { 152 this.form.addEventListener(eventName, ({ target }) => { 153 this.clearError(target.getAttribute('name')); 154 }); 155 }); 156 } 157 158 clearErrors () { 159 this.fields.forEach(field => this.clearError(field.getAttribute('name'))); 160 } 161 162 clearError (name) { 163 const fields = this.getFields(name); 164 fields.forEach(field => { 165 if (this.inputInvalidClasses) { 166 field.classList.remove(...this.inputInvalidClasses); 167 } 168 field.removeAttribute('aria-invalid'); 169 // field.setCustomValidity(''); 170 }); 171 172 const errors = this.getErrors(name);
173 errors.forEach(error => { 174 error.style.display = 'none'; 175 error.innerHTML = ''; 176 }); 177 178 const customErrors = this.getCustomErrors(name); 179 if (this.customInvalidClasses) { 180 customErrors.forEach(({ classList }) => classList.remove(...this.customInvalidClasses)); 181 } 182 183 return { 184 fields, 185 errors, 186 customErrors, 187 }; 188 } 189 190 setError (name, message = '') { 191 this.getFields(name).forEach(field => { 192 if (this.inputInvalidClasses) { 193 field.classList.add(...this.inputInvalidClasses); 194 } 195 field.setAttribute('aria-invalid', true); 196 // if (!this.form.noValidate) { 197 // field.setCustomValidity(FetchIt.sanitizeHTML(message)); 198 // field.reportValidity(); 199 // } 200 }); 201 202 if (this.customInvalidClasses) { 203 this.getCustomErrors(name).forEach(({ classList }) => classList.add(...this.customInvalidClasses)); 204 } 205 206 this.getErrors(name).forEach(error => { 207 error.style.display = ''; 208 error.innerHTML = message; 209 }); 210 } 211 212 enableFields () { 213 this.elements.forEach(field => field.removeAttribute('disabled')); 214 } 215 216 disableFields () { 217 this.elements.forEach(field => field.setAttribute('disabled', '')); 218 } 219 220 getFields (name) { 221 if (!name) { 222 return []; 223 } 224 225 return Array.from(this.form.querySelectorAll(`[name="${name}"], [name="${name}[]"]`)); 226 } 227 228 getErrors (name) { 229 if (!name) { 230 return []; 231 } 232 233 return Array.from(this.form.querySelectorAll(`[data-error="${name}"], [data-error="${name}[]"]`)); 234 } 235 236 getCustomErrors (name) { 237 if (!name) { 238 return []; 239 } 240 241 return Array.from(this.form.querySelectorAll(`[data-custom="${name}"]`)); 242 } 243 244 get elements () { 245 return Array.from(this.form.elements); 246 } 247 248 get fields () { 249 return this.elements.filter(({ tagName }) => ['select', 'input', 'textarea'].includes(tagName.toLowerCase())); 250 } 251 252 get inputInvalidClasses() { 253 return this.config.inputInvalidClass ? this.config.inputInvalidClass.split(' ') : []; 254 } 255 256 get customInvalidClasses() { 257 return this.config.customInvalidClass ? this.config.customInvalidClass.split(' ') : []; 258 } 259 260 static sanitizeHTML (str = '') { 261 return str.replace(/(<([^>]+)>)/gi, ''); 262 } 263 264 static create(config) { 265 if ( 266 config.defaultNotifier 267 && typeof window.Notyf === 'function' 268 && typeof FetchIt.Message === 'undefined' 269 ) { 270 const notyf = new Notyf(); 271 272 FetchIt.Message = { 273 success(message) { 274 notyf.success(message); 275 }, 276 error(message) { 277 notyf.error(message); 278 }, 279 }; 280 } 281 282 if (!config.action) { 283 throw new Error('ÐÐµÑ Ð¸Ð´ÐµÐ½ÑиÑикаÑоÑа ÑоÑÐ¼Ñ FetchIt'); 284 } 285 286 const forms = document.querySelectorAll(`form[data-fetchit="${config.action}"]`); 287 if (!forms) { 288 throw new Error(`РдокÑменÑе не найдено ÑоÑм по ÑелекÑоÑÑ: form[data-fetchit="${config.action}"]`); 289 } 290 291 forms.forEach(form => { 292 new this(form, config); 293 }); 294 } 295 } 296 297 window.FetchIt = FetchIt; 298 299})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.