1const defaultSettings = { 2 alerts: { 3 errorItem: undefined, 4 errorEmail: undefined, 5 errorPhone: undefined, 6 errorServer: undefined, 7 } 8} 9 10function Callback(selector, settings = defaultSettings) { 11 this.form = document.querySelector(selector); 12 this.ajaxUrl = this.form.action; 13 this.submit = (event) => { 14 event.preventDefault(); 15 if (event.target.classList.contains('active')) 16 return; 17 18 this._ajax(this.ajaxUrl, this.load); 19 } 20 this.clearForm = () => { 21 for (let i = 0; i < this.form.length; i++) { 22 let item = this.form[i]; 23 this._removeErrorClass(item); 24 if (item.type !== 'hidden') { 25 item.value = ''; 26 } 27 if (item.type === 'textarea') { 28 item.removeAttribute('filled'); 29 } 30 if (item.type === 'file') { 31 const labels = item.labels; 32 labels.forEach(label => { 33 const labelField = label.querySelector('.field-file__label--text'); 34 if (labelField) { 35 if (labelField.dataset.defaultField) { 36 labelField.innerHTML = ''; 37 labelField.insertAdjacentHTML('beforeend', labelField.dataset.defaultField); 38 } 39 } else { 40 if (label.dataset.defaultField) { 41 label.innerHTML = ''; 42 label.insertAdjacentHTML('beforeend', label.dataset.defaultField); 43 } 44 } 45 }); 46 } 47 } 48 } 49 this.update = () => { 50 this._getSettings(); 51 this._addListener(); 52 this._addMask(); 53 this._addDragAndDrop(); 54 } 55 this._checkError = () => { 56 for (let key in this.errors) { 57 if (this.errors[key]) { 58 return true; 59 } 60 } 61 return false; 62 } 63 this._addMask = () => { 64 let phoneInputs = this.form.querySelectorAll(this.phoneMaskSelector); 65 phoneInputs.forEach((e) => { 66 e.addEventListener("input", this._mask); 67 e.addEventListener("focus", this._mask); 68 e.addEventListener("blur", this._mask); 69 }); 70 } 71 this._addDragAndDrop = () => { 72 const formElements = this.form.elements; 73 for (const element of formElements) { 74 if (element.type !== 'file') 75 continue; 76 const 77 labels = element.labels, 78 actionClearFiles = event => { 79 const target = event.target.closest('a.clear'); 80 81 if (!target) 82 return; 83 84 event.preventDefault(); 85 86 target.classList.remove('show'); 87 88 element.value = ''; 89 labels.forEach(label => { 90 let labelField = label.querySelector('.field-file__label--text'); 91 if (labelField) { 92 labelField.textContent = labelField.dataset.defaultField; 93 } else { 94 label.textContent = label.dataset.defaultField; 95 } 96 }); 97 }, 98 actionDragEnterOver = event => { 99 event.preventDefault(); 100 element.parentElement.parentElement.classList.add('active'); 101 }, 102 actionDragLeave = event => { 103 event.preventDefault(); 104 element.parentElement.parentElement.classList.remove('active'); 105 }, 106 actionDragDrop = event => { 107 event.preventDefault(); 108 element.files = event.dataTransfer.files; 109 element.parentElement.parentElement.classList.remove('active'); 110 const clearButton = element.parentElement.parentElement.querySelector('a.clear'); 111 if (clearButton) { 112 clearButton.classList.add('show'); 113 } 114 labels.forEach(label => { 115 let labelField = label.querySelector('.field-file__label--text'); 116 if (labelField) { 117 if (!labelField.dataset.defaultField) { 118 labelField.dataset.defaultField = labelField.textContent; 119 } 120 labelField.textContent = ''; 121 for (const file of event.dataTransfer.files) { 122 labelField.textContent += file.name + ';'; 123 } 124 } else { 125 if (!label.dataset.defaultField) { 126 label.dataset.defaultField = label.textContent; 127 } 128 label.textContent = ''; 129 for (const file of event.dataTransfer.files) { 130 label.textContent += file.name + ';'; 131 } 132 } 133 }); 134 }, 135 actionInputChange = () => {
136 labels.forEach(label => { 137 let labelField = label.querySelector('.field-file__label--text'); 138 const clearButton = element.parentElement.querySelector('a.clear'); 139 if (clearButton) { 140 clearButton.classList.add('show'); 141 } 142 if (labelField) { 143 if (!labelField.dataset.defaultField) { 144 labelField.dataset.defaultField = labelField.textContent; 145 } 146 labelField.textContent = ''; 147 for (const file of element.files) { 148 labelField.textContent += file.name + ';\n'; 149 } 150 if (element.files.length < 1) { 151 labelField.textContent = labelField.dataset.defaultField; 152 if (clearButton) { 153 clearButton.classList.remove('show'); 154 } 155 } 156 } else { 157 if (!label.dataset.defaultField) { 158 label.dataset.defaultField = label.textContent; 159 } 160 label.textContent = ''; 161 for (const file of element.files) { 162 label.textContent += file.name + ';'; 163 } 164 if (element.files.length < 1) { 165 label.textContent = label.dataset.defaultField; 166 if (clearButton) { 167 clearButton.classList.remove('show'); 168 } 169 } 170 171 } 172 }); 173 }; 174 element.addEventListener('change', actionInputChange); 175 labels.forEach(label => { 176 label.addEventListener('dragenter', actionDragEnterOver); 177 label.addEventListener('dragover', actionDragEnterOver); 178 label.addEventListener('dragleave', actionDragLeave); 179 label.addEventListener('drop', actionDragDrop); 180 label.parentElement.addEventListener('click', actionClearFiles); 181 }); 182 } 183 } 184 this._setCursorPosition = function (pos, elem) { 185 elem.focus(); 186 if (elem.setSelectionRange) { 187 elem.setSelectionRange(pos, pos) 188 } 189 } 190 this._mask = (event) => { 191 let input = event.target; 192 193 let matrix; 194 if (window.location.host === 'alumica.by') { 195 matrix = '+375 (__) ___-__-__'; 196 } else { 197 matrix = '+7 (___) ___-__-__'; 198 } 199 let i = 0, 200 def = matrix.replace(/\D/g, ""), 201 val = input.value.replace(/\D/g, ""); 202 if (def.length >= val.length) 203 val = def; 204 input.value = matrix.replace(/./g, function (a) { 205 return /[_\d]/.test(a) && i < val.length ? val.charAt(i++) : i >= val.length ? "" : a 206 }); 207 if (event.type === "blur") { 208 if (input.value.length === 2) 209 input.value = "" 210 } else 211 this._setCursorPosition(input.value.length, input) 212 } 213 214 this._addPostSettings = () => { 215 for (let key in settings.POST) { 216 this.dataPOST.append(key, settings.POST[key]); 217 } 218 } 219 this._ajax = (ajaxUrl, load) => { 220 this._checkFill(); 221 if (!this._checkError()) { 222 let _self = this; 223 224 const loader = _self.form.querySelector('.btn.button'); 225 if (loader) { 226 loader.classList.add('active'); 227 } 228 const action = () => { 229 _self.dataPOST = new FormData(_self.form); 230 _self._addPostSettings(); 231 232 _self.alert.classList.add('inactive'); 233 _self.alert.style.top = ''; 234 235 _self.alert.classList.remove(_self.alertClasses.error); 236 _self.alert.querySelector('.alert-form__text').innerHTML = ''; 237 238 let request = new XMLHttpRequest(); 239 request.open("POST", ajaxUrl); 240 request.addEventListener("load", () => { 241 if (loader) { 242 loader.classList.remove('active'); 243 } 244 if (request.response >= 1) { 245 if (load !== undefined) { 246 load(request); 247 } 248 249 _self.clearForm(); 250 _self.alert.classList.add(_self.alertClasses.success); 251 setTimeout(() => { 252 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alerts.success; 253 _self.alert.classList.remove('inactive'); 254 _self.alert.style.top = ((_self.alert.clientHeight + 15) * -1) + 'px'; 255 }); 256 setTimeout(() => { 257 _self.alert.classList.add('inactive'); 258 _self.alert.style.top = ''; 259 _self.alert.querySelector('.alert-form__text').innerHTML = ''; 260 _self.alert.classList.remove(_self.alertClasses.success); 261 262 const modalContent = this.form.closest('.modal'); 263 264 if (modalContent) { 265 const modal = bootstrap.Modal.getOrCreateInstance(modalContent); 266 modal.hide(); 267 } 268 269 270 }, 5000); 271 272 document.dispatchEvent(new CustomEvent('formSubmit', { 273 detail: { 274 id: request.response, 275 // type: settings.eventType, 276 } 277 })); 278 279 // ym(window.metrikaCode, 'reachGoal', 'form_send_success'); 280 // gtag("event", "form_send", {}); 281 } else { 282 _self.alert.classList.add(_self.alertClasses.error); 283 setTimeout(() => { 284 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alerts.error.server; 285 _self.alert.classList.remove('inactive'); 286 _self.alert.style.top = ((_self.alert.clientHeight + 15) * -1) + 'px'; 287 }); 288 } 289 290 }, false); 291 request.send(_self.dataPOST); 292 } 293 294 if (settings.captchaPublicKey) { 295 grecaptcha.ready(function () { 296 grecaptcha.execute(settings.captchaPublicKey, {action: 'submit'}).then(function (token) { 297 let request = new XMLHttpRequest(); 298 request.open('POST', settings.captchaCheck); 299 request.addEventListener('load', () => { 300 if (request.response === 'OK') { 301 action(); 302 } else { 303 if (loader) { 304 loader.classList.remove('active'); 305 } 306 307 _self.errors['captcha'] = true; 308 309 let activeHtml = false;
310 const actionTransitionEnd = () => { 311 _self.alert.removeEventListener('transitionend', actionTransitionEnd); 312 for (let key in _self.errors) { 313 if (_self.errors[key]) { 314 if (!activeHtml) { 315 activeHtml = true; 316 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alerts.error[key] 317 } else { 318 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alert.querySelector('.alert-form__text').innerHTML + '<br><br>' + _self.alerts.error[key] 319 } 320 } 321 } 322 _self.alert.classList.add(_self.alertClasses.error); 323 _self.alert.classList.remove('inactive'); 324 _self.alert.style.top = ((_self.alert.clientHeight + 15) * -1) + 'px'; 325 }; 326 if (!_self.alert.classList.contains('inactive')) { 327 _self.alert.classList.add('inactive'); 328 _self.alert.style.top = ''; 329 _self.alert.addEventListener('transitionend', actionTransitionEnd); 330 } else { 331 actionTransitionEnd(); 332 } 333 } 334 }); 335 const sendData = new FormData; 336 sendData.append('token', token.toString()); 337 request.send(sendData); 338 }); 339 }); 340 } else if (settings.YCaptchaKey) { 341 const tokenContainer = this.form.querySelector('[name="smart-token"]'); 342 343 // console.log(tokenContainer); 344 345 if (!tokenContainer.value) { 346 window.smartCaptcha.execute(window.logicloud[settings.YCaptchaId]); 347 348 const YCaptchaAction = event => { 349 let request = new XMLHttpRequest(); 350 request.open('POST', settings.YCaptchaCheck); 351 request.addEventListener('load', () => { 352 if (request.response === 'OK') { 353 window.smartCaptcha.reset(window.logicloud[settings.YCaptchaId]); 354 action(); 355 } else { 356 const loader = _self.form.querySelector('.loader-container'); 357 358 if (loader) { 359 loader.classList.remove('active'); 360 } 361 362 _self.errors['captcha'] = true; 363 364 let activeHtml = false; 365 const actionTransitionEnd = () => { 366 _self.alert.removeEventListener('transitionend', actionTransitionEnd); 367 for (let key in _self.errors) { 368 if (_self.errors[key]) { 369 if (!activeHtml) { 370 activeHtml = true; 371 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alerts.error[key] 372 } else { 373 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alert.querySelector('.alert-form__text').innerHTML + '<br><br>' + _self.alerts.error[key] 374 } 375 } 376 } 377 _self.alert.classList.add(_self.alertClasses.error); 378 _self.alert.classList.remove('inactive'); 379 _self.alert.style.top = ((_self.alert.clientHeight + 15) * -1) + 'px'; 380 }; 381 if (!_self.alert.classList.contains('inactive')) { 382 _self.alert.classList.add('inactive'); 383 _self.alert.style.top = ''; 384 _self.alert.addEventListener('transitionend', actionTransitionEnd); 385 } else { 386 actionTransitionEnd(); 387 } 388 } 389 }); 390 391 const sendData = new FormData; 392 sendData.append('token', event.detail.token.toString()); 393 request.send(sendData); 394 395 document.removeEventListener('YCaptchaExecute', YCaptchaAction); 396 } 397 398 document.addEventListener('YCaptchaExecute', YCaptchaAction); 399 } else { 400 let request = new XMLHttpRequest(); 401 request.open('POST', settings.YCaptchaCheck); 402 request.addEventListener('load', () => { 403 if (request.response === 'OK') { 404 action(); 405 } else { 406 const loader = _self.form.querySelector('.loader-container'); 407 408 if (loader) { 409 loader.classList.remove('active'); 410 } 411 412 _self.errors['captcha'] = true; 413 414 let activeHtml = false;
415 const actionTransitionEnd = () => { 416 _self.alert.removeEventListener('transitionend', actionTransitionEnd); 417 for (let key in _self.errors) { 418 if (_self.errors[key]) { 419 if (!activeHtml) { 420 activeHtml = true; 421 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alerts.error[key] 422 } else { 423 _self.alert.querySelector('.alert-form__text').innerHTML = _self.alert.querySelector('.alert-form__text').innerHTML + '<br><br>' + _self.alerts.error[key] 424 } 425 } 426 } 427 _self.alert.classList.add(_self.alertClasses.error); 428 _self.alert.classList.remove('inactive'); 429 _self.alert.style.top = ((_self.alert.clientHeight + 15) * -1) + 'px'; 430 }; 431 if (!_self.alert.classList.contains('inactive')) { 432 _self.alert.classList.add('inactive'); 433 _self.alert.style.top = ''; 434 _self.alert.addEventListener('transitionend', actionTransitionEnd); 435 } else { 436 actionTransitionEnd(); 437 } 438 } 439 }); 440 const sendData = new FormData; 441 sendData.append('token', tokenContainer.value.toString()); 442 request.send(sendData); 443 } 444 } else { 445 action(); 446 } 447 448 } else { 449 450 let activeHtml = false; 451 const actionTransitionEnd = () => { 452 this.alert.removeEventListener('transitionend', actionTransitionEnd); 453 for (let key in this.errors) { 454 if (this.errors[key]) { 455 if (!activeHtml) { 456 activeHtml = true; 457 this.alert.querySelector('.alert-form__text').innerHTML = this.alerts.error[key] 458 } else { 459 this.alert.querySelector('.alert-form__text').innerHTML = this.alert.querySelector('.alert-form__text').innerHTML + '<br><br>' + this.alerts.error[key] 460 } 461 } 462 } 463 this.alert.classList.add(this.alertClasses.error); 464 this.alert.classList.remove('inactive'); 465 this.alert.style.top = ((this.alert.clientHeight + 15) * -1) + 'px'; 466 467 setTimeout(() => { 468 this.alert.classList.add('inactive'); 469 this.alert.style.top = ''; 470 this.alert.querySelector('.alert-form__text').innerHTML = ''; 471 this.alert.classList.remove(this.alertClasses.error); 472 }, 5000); 473 }; 474 if (!this.alert.classList.contains('inactive')) { 475 this.alert.classList.add('inactive'); 476 this.alert.style.top = ''; 477 this.alert.addEventListener('transitionend', actionTransitionEnd); 478 479 } else { 480 actionTransitionEnd(); 481 } 482 483 } 484 } 485 this._addListener = () => { 486 this.btnSubmit.addEventListener('click', this.submit); 487 const requiredInputs = this.form.querySelectorAll('input[required], textarea[required]'); 488 489 requiredInputs.forEach(input => { 490 input.addEventListener('input', () => { 491 if (input.value === '') { 492 input.classList.add('error'); 493 } else { 494 input.classList.remove('error'); 495 } 496 }) 497 }) 498 } 499 this._checkFill = () => { 500 this.errors = { 501 item: false, 502 email: false, 503 phone: false, 504 personal: false, 505 captcha: false, 506 } 507 let checkedForm = this.form; 508 for (let i = 0; i < checkedForm.length; i++) { 509 let checkedItem = checkedForm[i]; 510 let customCheckedTypes = ['email', 'tel', 'checkbox', 'hidden']; 511 let isClassicItemType = checkedItem.required && !customCheckedTypes.includes(checkedItem.type); 512 let isCustomItemType = checkedItem.required && customCheckedTypes.includes(checkedItem.type) || checkedItem.value !== ''; 513 if (isClassicItemType) { 514 if (checkedItem.value !== '') { 515 this._removeErrorClass(checkedItem); 516 } else { 517 this._addErrorClass(checkedItem); 518 this.errors.item = true; 519 } 520 } else if (isCustomItemType) { 521 522 if (checkedItem.type === 'email') { 523 this._checkEmail(checkedItem); 524 } else if (checkedItem.type === 'tel') { 525 526 this._checkPhone(checkedItem); 527 } else if (checkedItem.type === 'checkbox') { 528 this._checkCheckbox(checkedItem); 529 } 530 } 531 } 532 } 533 this._checkCheckbox = (checkedItem) => { 534 if (!checkedItem.checked) { 535 this._addErrorClass(checkedItem); 536 this.errors.personal = true; 537 } else { 538 this._removeErrorClass(checkedItem); 539 } 540 } 541 this._checkPhone = (checkedItem) => { 542 if (checkedItem.value.length < 18) { 543 this._addErrorClass(checkedItem); 544 this.errors.phone = true; 545 } else { 546 this._removeErrorClass(checkedItem); 547 } 548 } 549 this._checkEmail = (checkedItem) => { 550 if (checkedItem.value !== '') { 551 if (checkedItem.value.length > 0 && (checkedItem.value.match(/.+?@.+/g) || []).length !== 1) { 552 this._addErrorClass(checkedItem); 553 this.errors.email = true; 554 } else { 555 this._removeErrorClass(checkedItem); 556 } 557 } else { 558 this._addErrorClass(checkedItem); 559 this.errors.email = true; 560 } 561 } 562 this._getSettings = () => { 563 if (settings.alert) { 564 this.alert = document.querySelector(settings.alert); 565 } else { 566 this.alert = this.form.querySelector('.alert-form'); 567 } 568 if (settings.btnSubmit) { 569 this.btnSubmit = document.querySelector(settings.btnSubmit); 570 } else { 571 this.btnSubmit = this.form.querySelector('[type=submit]'); 572 } 573 if (settings.load) { 574 this.load = settings.load; 575 } 576 if (settings.phoneMaskSelector) { 577 this.phoneMaskSelector = settings.phoneMaskSelector; 578 } else { 579 this.phoneMaskSelector = 'input.phone'; 580 } 581 this.alerts = { 582 error: { 583 item: (settings?.alerts?.errorItem !== undefined) ? settings.alerts.errorItem : 'ÐаполниÑе обÑзаÑелÑнÑе полÑ', 584 email: (settings?.alerts?.errorEmail !== undefined) ? settings.alerts.errorEmail : 'ÐведиÑе коÑÑекÑнÑй Email', 585 phone: (settings?.alerts?.errorPhone !== undefined) ? settings.alerts.errorPhone : 'ÐведиÑе коÑÑекÑнÑй ÑелеÑон', 586 server: (settings?.alerts?.errorServer !== undefined) ? settings.alerts.errorServer : 'ÐÑибка ÑеÑвеÑа, попÑобÑйÑе позже', 587 personal: (settings?.alerts?.personal !== undefined) ? settings.alerts.personal : 'ÐодÑвеÑдиÑе ÑоглаÑие на обÑабоÑÐºÑ Ð¿ÐµÑÑоналÑнÑÑ Ð´Ð°Ð½Ð½ÑÑ ', 588 captcha: (settings?.alerts?.captcha !== undefined) ? settings.alerts.captcha : 'Ðозможно Ð²Ñ ÑобоÑ' 589 }, 590 success: (settings?.alerts?.success !== undefined) ? settings.alerts.success : 'ÐаÑвка ÑÑпеÑно оÑпÑавлена' 591 } 592 this.alertClasses = { 593 error: (settings?.alertClasses?.error !== undefined) ? settings.alertClasses.error : 'danger', 594 success: (settings?.alertClasses?.success !== undefined) ? settings.alertClasses.success : 'success' 595 } 596 } 597 this._addErrorClass = (item, className = 'error') => { 598 if (item !== undefined && typeof (item) === 'object') { 599 item.classList.add(className) 600 } 601 } 602 this._removeErrorClass = (item, className = 'error') => { 603 if (item !== undefined && typeof (item) === 'object') { 604 item.classList.remove(className) 605 } 606 } 607 608 this.update(); 609}
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.