1var aoPostLoadFormCallback = aoPostLoadFormCallback || {}; 2var aoPostSubmitFormCallback = aoPostSubmitFormCallback || {}; 3 4var MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1GB in bytes 5 6var DEFAULT_SUPPORTED_FORMATS = []; 7 8var ACTON_BASE_URL = "/acton/file-upload-service"; 9 10const dropzoneFileCollections = {}; 11const inputToDropzoneMap = new Map(); 12var maxUploads = 10; 13 14var FILE_SCAN_ERROR_TEXT = "Virus scan failed. File rejected."; 15 16var pathname = window.location.pathname; 17 18function getErrorIconSVG() { 19 return '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">' + 20 '<g clip-path="url(#clip0_533_1217)">' + 21 '<path fill-rule="evenodd" clip-rule="evenodd" d="M0 6C0 2.68629 2.68629 0 6 0C9.31371 0 12 2.68629 12 6C12 9.31371 9.31371 12 6 12C2.68629 12 0 9.31371 0 6ZM6.49989 6.49969V3.50002C6.49989 3.22391 6.27606 3.00008 5.99995 3.00008C5.72384 3.00008 5.5 3.22391 5.5 3.50002V6.49969C5.5 6.7758 5.72384 6.99963 5.99995 6.99963C6.27606 6.99963 6.49989 6.7758 6.49989 6.49969ZM5.5 8.50176V8.49678C5.5 8.22201 5.72384 7.99927 5.99995 7.99927C6.27606 7.99927 6.49989 8.22201 6.49989 8.49678V8.50176C6.49989 8.77652 6.27606 8.99927 5.99995 8.99927C5.72384 8.99927 5.5 8.77652 5.5 8.50176Z" fill="#F25656"/>' + 22 '</g>' + 23 '<defs>' + 24 '<clipPath id="clip0_533_1217">' + 25 '<rect width="12" height="12" fill="white"/>' + 26 '</clipPath>' + 27 '</defs>' + 28 '</svg>'; 29} 30 31function getSuccessIconSVG() { 32 return '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">' + 33 '<g clip-path="url(#clip0_533_1203)">' + 34 '<path fill-rule="evenodd" clip-rule="evenodd" d="M0 6C0 2.68629 2.68629 0 6 0C9.31371 0 12 2.68629 12 6C12 9.31371 9.31371 12 6 12C2.68629 12 0 9.31371 0 6ZM8.0274 4.08387C8.25717 4.23704 8.31925 4.54748 8.16608 4.77724L6.16608 7.77724C6.0129 8.007 5.70247 8.06909 5.4727 7.91592L3.9727 6.91592C3.74294 6.76274 3.68085 6.4523 3.83403 6.22254C3.9872 5.99278 4.29764 5.93069 4.5274 6.08387L5.61138 6.80651L7.33403 4.22254C7.4872 3.99278 7.79764 3.93069 8.0274 4.08387Z" fill="#69B466"/>' + 35 '</g>' + 36 '<defs>' + 37 '<clipPath id="clip0_533_1203">' + 38 '<rect width="12" height="12" fill="white"/>' + 39 '</clipPath>' + 40 '</defs>' + 41 '</svg>'; 42} 43 44function getRemoveIconSvg() { 45 return '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">' + 46 '<rect width="24" height="24" rx="4" fill="#FFEAEB"/>' + 47 '<path fill-rule="evenodd" clip-rule="evenodd" d="M15 8V7.5C15 7.10217 14.842 6.72064 14.5607 6.43934C14.2794 6.15804 13.8978 6 13.5 6H10.5C10.1022 6 9.72064 6.15804 9.43934 6.43934C9.15804 6.72064 9 7.10218 9 7.5V8H8.00071C8.00054 8 8.00087 8 8.00071 8H7.5C7.22386 8 7 8.22386 7 8.5C7 8.77614 7.22386 9 7.5 9H7.52717L7.95079 16.5829C7.972 16.9661 8.13928 17.3266 8.41824 17.5902C8.69711 17.8538 9.06635 18.0004 9.45001 18L14.5542 18C14.9379 18.0004 15.3079 17.8538 15.5868 17.5902C15.8658 17.3265 16.0331 16.9658 16.0542 16.5825L16.4732 9H16.5C16.7761 9 17 8.77614 17 8.5C17 8.22386 16.7761 8 16.5 8H15ZM10.5 7C10.3674 7 10.2402 7.05268 10.1464 7.14645C10.0527 7.24021 10 7.36739 10 7.5V8H14V7.5C14 7.36739 13.9473 7.24022 13.8536 7.14645C13.7598 7.05268 13.6326 7 13.5 7H10.5ZM8.52873 9L8.94923 16.5271C8.95627 16.6549 9.01206 16.7755 9.10507 16.8634C9.19809 16.9513 9.32127 17.0002 9.44925 17L14.555 17C14.683 17.0002 14.8069 16.9513 14.8999 16.8634C14.993 16.7755 15.0487 16.6553 15.0558 16.5275L15.4716 9H8.52873Z" fill="#E81212"/>' + 48 '</svg>'; 49} 50 51function showOrHideSubmitButtons(currentForm) { 52 var submitButtons = !currentForm 53 ? document.querySelectorAll(".ao-form-submit") 54 : currentForm.querySelectorAll(".ao-form-submit"); 55 var hasWarnings; 56 57 const isProgresBarVisible = document.querySelectorAll("[id^='progress-bar-wrapper']"); 58 const hasVisibleProgressBars = Array.from(isProgresBarVisible).some( 59 (progressBar) => progressBar.style.display === "block" 60 ); 61 62 for (const dropzoneKey in dropzoneFileCollections) { 63 const dropzone = dropzoneFileCollections[dropzoneKey]; 64 for (const fileStatus of dropzone.fileStatuses) { 65 if (fileStatus.status === "WARNING" || fileStatus.status === "IN_PROGRESS" || hasVisibleProgressBars) { 66 hasWarnings = true; 67 break; 68 } 69 } 70 if (hasWarnings) break; 71 } 72 73 for (var i = 0; i < submitButtons.length; i++) { 74 if (hasWarnings) { 75 submitButtons[i].disabled = true; 76 submitButtons[i].style.opacity = "0.5"; 77 submitButtons[i].style.pointerEvents = "none"; 78 } else { 79 submitButtons[i].disabled = false;
80 submitButtons[i].style.opacity = "1"; 81 submitButtons[i].style.pointerEvents = "auto"; 82 } 83 } 84} 85 86var removeFileStatus = function(uniqueDropzoneId, fileName) { 87 const dropzoneData = dropzoneFileCollections[uniqueDropzoneId]; 88 dropzoneData.fileStatuses = dropzoneData.fileStatuses.filter(file => file.fileName !== fileName); 89 showOrHideSubmitButtons(); 90}; 91 92const updateFileStatus = (allowMultiple, uniqueDropzoneId, fileName, status, attachmentId, jwtToken) => { 93 const dropzoneData = dropzoneFileCollections[uniqueDropzoneId]; 94 const { fileStatuses } = dropzoneData; 95 96 if (!allowMultiple) { 97 dropzoneData.fileStatuses = [{ fileName, status, attachmentId, jwtToken}]; 98 } else { 99 const existingFileIndex = fileStatuses.findIndex(file => file.fileName === fileName); 100 if (existingFileIndex !== -1) { 101 fileStatuses[existingFileIndex] = { fileName, status, attachmentId, jwtToken }; 102 } else { 103 fileStatuses.push({ fileName, status, attachmentId, jwtToken}); 104 } 105 dropzoneData.fileStatuses = fileStatuses; 106 } 107}; 108 109function deleteAttachmentByToken(jwtToken, accountId, domain, scheme) { 110 return new Promise((resolve, reject) => { 111 if (!jwtToken) { 112 resolve(false); 113 return; 114 } 115 116 var url = scheme + domain + ACTON_BASE_URL + "/attachment/delete-by-token?jwtToken=" + jwtToken; 117 118 const xhr = new XMLHttpRequest(); 119 xhr.open('DELETE', url, true); 120 xhr.setRequestHeader( 'Content-Type', 'application/json' ); 121 122 123 xhr.onload = function () { 124 if (xhr.status === 200) { 125 try { 126 const response = JSON.parse(xhr.responseText); 127 if (response) { 128 resolve(true); 129 } else { 130 resolve(false); 131 } 132 } catch (e) { 133 resolve(false); 134 } 135 } else { 136 resolve(false); 137 } 138 }; 139 140 xhr.onerror = function () { 141 resolve(false); 142 }; 143 144 xhr.send(JSON.stringify(jwtToken)); 145 }); 146} 147 148function updateFileUploadMetadata(uiniqueCollection, index) { 149 const uploadedFiles = uiniqueCollection.fileStatuses 150 .filter(fileStatus => fileStatus.status === "DONE" && fileStatus.attachmentId) 151 .map(fileStatus => ({ 152 name: fileStatus.fileName, 153 id: fileStatus.attachmentId, 154 })); 155 156 const jsonValue = JSON.stringify({ 157 type: "FILE", 158 data: uploadedFiles, 159 }); 160 161 const fileUploadMetaData = document.querySelectorAll("[class^='file-upload-metadata']")[index]; 162 if (fileUploadMetaData) { 163 fileUploadMetaData.value = jsonValue; 164 } 165} 166 167function getElementsByAttribute (tagName, index) { 168 return document.querySelectorAll(`[${tagName}]`)[index]?.getAttribute(tagName)?.trim() 169} 170 171function getAllowedFileTypes(accountId, domain, scheme) { 172 return new Promise(function (resolve, reject) { 173 var query = "action=getAllowedFileTypesList&accountId=" + accountId; 174 var url = scheme + domain + "/acton/file/upload.jsp?" + query; 175 var xhr = new XMLHttpRequest(); 176 177 xhr.open("GET", url, true); 178 xhr.onload = function () { 179 if (xhr.status >= 200 && xhr.status < 300) { 180 var data = JSON.parse(xhr.responseText); 181 DEFAULT_SUPPORTED_FORMATS = data.allowedFileUploadTypes; 182 resolve(); 183 } else { 184 reject("Error: " + xhr.statusText); 185 } 186 }; 187 188 xhr.onerror = function () { 189 reject("Network Error"); 190 }; 191 192 xhr.send(); 193 }); 194} 195 196 197var _aoFormLoader = (function(w){ 198 var stylesLoaded = false; 199 var captchaLoaded = false; 200 var gfLoaded = false; 201 var htmlId = false; 202 var formsLoadedCount = 0; 203 var formsCount = 0; 204 205 var scheme = document.location.protocol + "//"; 206 207 function populateDateField(inputElem, value) { 208 if(!value || !inputElem) { 209 return; 210 } 211 212 var dateWrapper = inputElem.parentElement; 213 var dateElements = dateWrapper.querySelectorAll('input.ao-form-field-date'); 214 var amPm; 215 216 if (value.indexOf("AM")) { 217 value = value.replace("AM", ""); 218 amPm = "AM"; 219 } else if (value.indexOf("PM")) { 220 value = value.replace("PM", ""); 221 amPm = "PM"; 222 } 223 var dateFormat = value.split(/[:\/\s]+/); 224 225 // Populate all date input fields
226 dateElements.forEach(function(elem, index) { 227 elem.value = dateFormat[index] || ''; 228 }); 229 230 // Populate AM/PM dropdown 231 if(amPm && dateWrapper.querySelector("option[value='" + amPm + "']")) { 232 dateWrapper.querySelector("option[value='" + amPm + "']").selected = true; 233 } 234 } 235 236 var loadForm = function(options){ 237 formsCount += 1; //increment for each form that is on the page 238 loadStylesheet(options); 239 240 var previewMarkup = '<p style="text-align: center;">Unable to preview form, it has reached the submission cap or expired.</p>'; 241 var oReq = new XMLHttpRequest(); 242 oReq.onload = function (e) { 243 244 var response; 245 246 if (e.target.responseType === 'json') { 247 response = e.target.response || false; 248 } else { 249 response = (e.target.response) ? JSON.parse(e.target.response) : false; 250 } 251 252 if(!response){ 253 return false; 254 } else if (response && typeof response.processedTemplate === 'undefined' && response.linkURL && response.linkURL != '') { 255 var linkURL = response.linkURL; 256 var queryString = window.location.search; 257 if (queryString) { 258 var params = queryString.substring(1).split('&'); 259 var filteredParams = []; 260 for (var i = 0; i < params.length; i++) { 261 var paramName = params[i].split('=')[0]; 262 // Skip internal Act-On parameters 263 if (paramName !== 'a' && paramName !== 'p' && paramName !== 't' && 264 paramName !== 'fm' && paramName !== 'preview' && paramName !== 'r' && 265 paramName !== 's' && paramName !== 'jsembed' && paramName !== 'tracking' && 266 paramName !== 'src' && paramName !== 'cmp' && paramName !== 'cmpid' && 267 paramName !== 'k' && paramName !== 'sid' && paramName !== 'd' && 268 paramName !== 'rptpw') { 269 filteredParams.push(params[i]); 270 } 271 } 272 if (filteredParams.length > 0) { 273 linkURL += (linkURL.indexOf('?') > 0 ? '&' : '?') + filteredParams.join('&'); 274 } 275 } 276 277 window.location = linkURL; 278 return false; 279 } 280 281 var idArr = options.id.split(":"); 282 283 htmlId = (options.uniqueId) ? options.id + options.uniqueId : options.id; 284 divId = (options.uniqueId) ? idArr[0] + options.uniqueId : idArr[0]; 285 286 var existingDiv = document.getElementById('aoform-' + divId); 287 if(existingDiv) 288 existingDiv.parentNode.removeChild(existingDiv); 289 290 var div = document.createElement('div'); 291 var scriptTag = document.getElementById('aoform-script-' + htmlId); 292 div.id = 'aoform-' + divId; 293 div.style.visibility = "hidden"; 294 scriptTag.parentNode.insertBefore(div, scriptTag); 295 div.innerHTML = response && response.processedTemplate ? response.processedTemplate : previewMarkup; 296 formsLoadedCount += 1; //increment for when a form is loaded onto the page 297 298 const dropzones = document.querySelectorAll("[id^='files-dropzone']"); 299 const fileInputs = document.querySelectorAll("[id^='file-upload']"); 300 const fileLists = document.querySelectorAll("[id^='filelist']"); 301 302 getAllowedFileTypes(options.accountId, options.domain, scheme).then(function () { 303 dropzones.forEach((dropzone, index) => { 304 const fileSizeErrorMessage = getElementsByAttribute('data-filesizeerrormessage', index); 305 const fileGeneralErrorMessage = getElementsByAttribute('data-filegeneralerrormessage', index); 306 const fileAllowedTypesErrorMessage = getElementsByAttribute('data-fileallowedtypeserrormsg', index) || 'The uploaded file is not a supported file format'; 307 308 const fileInput = fileInputs[index]; 309 const fileList = fileLists[index]; 310 311 const uniqueDropzoneId = `dropzone-${index}`; 312 313 dropzoneFileCollections[uniqueDropzoneId] = { 314 files: [], 315 fileElementsMap: new Map(), 316 fileStatuses: [], 317 pollingState:{}, 318 fileMessages: { fileSizeErrorMessage, fileGeneralErrorMessage, fileAllowedTypesErrorMessage } 319 }; 320 321 if (fileInput) { 322 inputToDropzoneMap.set(fileInput, uniqueDropzoneId); 323 } 324 325 if (dropzone && fileInput && fileList) { 326 const allowedFileTypes = getElementsByAttribute('data-allowedfiletypes', index); 327 const otherFileTypes = getElementsByAttribute('data-otherfiletypes', index); 328 const unqiueCollection = dropzoneFileCollections[uniqueDropzoneId]; 329 330 unqiueCollection['SUPPORTED_FORMATS'] = DEFAULT_SUPPORTED_FORMATS; 331 332 if (allowedFileTypes) { 333 var typesArray = allowedFileTypes.split(','); 334 unqiueCollection['SUPPORTED_FORMATS'] = typesArray; 335 } 336 337 // only keep other file types if specified 338 if (otherFileTypes) { 339 var extensions = otherFileTypes.match(/\w+/g); 340 if( allowedFileTypes) { 341 unqiueCollection['SUPPORTED_FORMATS'] = unqiueCollection['SUPPORTED_FORMATS'].concat(extensions); 342 } else { 343 unqiueCollection['SUPPORTED_FORMATS'] = extensions; 344 } 345 } 346 347 const defaultMessage = dropzone.querySelector("p"); 348 const allowMultiple = getElementsByAttribute('data-multiple', index) === "true"; 349 fileInput.multiple = allowMultiple; 350 351 const fileWrapper = document.createElement("div"); 352 fileWrapper.id = "file-wrapper"; 353 354 if (!(pathname.includes('landingPagePreview') || pathname.includes('showLandingPage'))) { 355 fileWrapper.style.minWidth = "45%"; 356 } 357
358 fileList.appendChild(fileWrapper); 359 360 dropzone.addEventListener("dragover", function (event) { 361 event.preventDefault(); 362 }); 363 364 dropzone.addEventListener('drop', function (event) { 365 event.preventDefault(); 366 const files = Array.from(event.dataTransfer.files); 367 368 const dataTransfer = new DataTransfer(); 369 files.forEach(file => dataTransfer.items.add(file)); 370 fileInput.files = dataTransfer.files; 371 handleNewFiles(files); 372 validateField(fileInput, { isFileUpload: true }); 373 }); 374 375 fileInput.addEventListener('change', function () { 376 const files = Array.from(fileInput.files); 377 if (files.length === 0) { 378 return; 379 } 380 381 const dataTransfer = new DataTransfer(); 382 files.forEach(file => dataTransfer.items.add(file)); 383 fileInput.files = dataTransfer.files; 384 385 handleNewFiles(files); 386 validateField(fileInput, { isFileUpload: true }); 387 }); 388 389 function handleNewFiles(files) { 390 const { files: storedFiles, fileElementsMap } = unqiueCollection; 391 392 if (!allowMultiple) { 393 const newFile = files[0]; 394 if (!newFile) return; 395 396 const existingFileNames = storedFiles.map(file => file.name); 397 if (existingFileNames.includes(newFile.name)) { 398 return; 399 } 400 401 unqiueCollection.files = [newFile]; 402 unqiueCollection.fileStatuses = [{ fileName: newFile.name, status: "PENDING" }]; 403 fileElementsMap.clear(); 404 fileWrapper.innerHTML = ""; 405 updateDropzoneContent([newFile]); 406 } else { 407 const existingFileNames = storedFiles.map(file => file.name); 408 const uniqueFiles = files.filter(file => !existingFileNames.includes(file.name)); 409 410 const remainingSlots = maxUploads - storedFiles.length; 411 if (remainingSlots <= 0) { 412 alert(`You can only upload up to ${maxUploads} files.`); 413 return; 414 } 415 416 const filesToAdd = uniqueFiles.slice(0, remainingSlots); 417 418 if (filesToAdd.length === 0) { 419 return; 420 } 421 422 unqiueCollection.files = mergeFiles(storedFiles, filesToAdd); 423 424 filesToAdd.forEach(file => { 425 unqiueCollection.fileStatuses.push({ 426 fileName: file.name, 427 status: "PENDING", 428 }); 429 }); 430 updateDropzoneContent(filesToAdd); 431 } 432 updateUploadStatusVisibility(); 433 showOrHideSubmitButtons() 434 } 435 436 function mergeFiles(existingFiles, newFiles) { 437 const fileMap = new Map(); 438 439 existingFiles.forEach(file => fileMap.set(file.name, file)); 440 newFiles.forEach(file => { 441 if (!fileMap.has(file.name)) { 442 fileMap.set(file.name, file); 443 } 444 }); 445 446 return Array.from(fileMap.values()); 447 } 448 449 function updateDropzoneContent(files) { 450 const { fileElementsMap } = unqiueCollection; 451 452 if (files.length === 0) { 453 defaultMessage.style.display = "block"; 454 fileList.style.display = "none"; 455 } else { 456 defaultMessage.style.display = "none"; 457 fileList.style.display = "block"; 458
459 files.forEach(function (file) { 460 if (!fileElementsMap.has(file.name)) { 461 var fileContainer = document.createElement("div"); 462 fileContainer.id = "file-container"; 463 464 var removeIcon = document.createElement("div"); 465 removeIcon.id = "remove-icon"; 466 removeIcon.innerHTML = getRemoveIconSvg(); 467 468 var tooltip = document.createElement("div"); 469 tooltip.id = "tooltip"; 470 tooltip.textContent = "Remove file"; 471 472 var tooltipArrow = document.createElement("div"); 473 tooltip.appendChild(tooltipArrow); 474 475 removeIcon.addEventListener("click", function () { 476 const { fileStatuses } = unqiueCollection; 477 478 const fileStatus = fileStatuses.find(f => f.fileName === file.name); 479 if (fileStatus && fileStatus.status === "DONE") { 480 const accountId = options.accountId; 481 const jwtToken = fileStatus.jwtToken; 482 483 if (jwtToken) { 484 removeIcon.style.disabled = true; 485 removeIcon.style.opacity = "0.7"; 486 removeIcon.style.cursor = "wait"; 487 removeIcon.style.pointerEvents = "none"; 488 489 deleteAttachmentByToken(jwtToken, accountId, options.domain, scheme) 490 .then((isSuccessful) => { 491 if (isSuccessful) { 492 removeFileLocally(); 493 } else { 494 restoreRemoveIconStyles(); 495 alert("Something went wrong. We couldn’t delete the file. Please try again."); 496 } 497 }) 498 .catch(() => { 499 restoreRemoveIconStyles(); 500 alert("Network error occurred."); 501 }); 502 } else { 503 restoreRemoveIconStyles(); 504 alert("Invalid attachmentId or accountId for removal."); 505 } 506 } else { 507 removeFileLocally(); 508 } 509 510 function removeFileLocally() { 511 fileWrapper.removeChild(fileContainer); 512 513 unqiueCollection.files = unqiueCollection.files.filter(storedFile => storedFile.name !== file.name); 514 unqiueCollection.fileStatuses = fileStatuses.filter(f => f.fileName !== file.name); 515 516 const dataTransfer = new DataTransfer(); 517 unqiueCollection.files.forEach(file => dataTransfer.items.add(file)); 518 fileInput.files = dataTransfer.files; 519 520 unqiueCollection.fileElementsMap.delete(file.name); 521 522 if (unqiueCollection.files.length === 0) { 523 const dropzoneElements = document.querySelectorAll("#dropzone-text"); 524 const dropzoneEl = dropzoneElements[index]; 525 526 dropzoneEl.style.display = "block"; 527 fileList.style.display = "none"; 528 } 529 530 updateFileUploadMetadata(unqiueCollection, index); 531 removeFileStatus(uniqueDropzoneId, file.name); 532 updateUploadStatusVisibility(); 533 } 534 535 function restoreRemoveIconStyles() { 536 removeIcon.style.disabled = false;
537 removeIcon.style.opacity = "1"; 538 removeIcon.style.cursor = "pointer"; 539 removeIcon.style.pointerEvents = "auto"; 540 } 541 }); 542 543 removeIcon.addEventListener("mouseenter", function () { 544 removeIcon.appendChild(tooltip); 545 tooltip.style.left = "50%"; 546 tooltip.style.bottom = `${removeIcon.offsetHeight + 10}px`; 547 tooltip.style.transform = "translateX(-50%)"; 548 549 tooltip.style.opacity = "1"; 550 tooltip.style.transform = "translate(-50%, 0)"; 551 }); 552 553 removeIcon.addEventListener("mouseleave", function () { 554 tooltip.style.opacity = "0"; 555 setTimeout(() => { 556 if (tooltip.parentNode === removeIcon) { 557 removeIcon.removeChild(tooltip); 558 } 559 }, 200); 560 }); 561 562 fileContainer.addEventListener("mouseover", function () { 563 if (progressBarWrapper.style.display !== "block") { 564 removeIcon.style.display = "block"; 565 } 566 }); 567 568 fileContainer.addEventListener("mouseout", function () { 569 removeIcon.style.display = "none"; 570 }); 571 572 var fileDetailsWrapper = document.createElement("div"); 573 fileDetailsWrapper.id = "file-details-wrapper"; 574 575 var fileNameContainer = document.createElement("div"); 576 fileNameContainer.id = "file-name-container"; 577 578 var fileName = document.createElement("div"); 579 fileName.id = "file-name"; 580 fileName.textContent = shortenFileName(file.name, 12); 581 582 fileNameContainer.appendChild(fileName); 583 584 var fileSizeContainer = document.createElement("div"); 585 fileSizeContainer.id = "file-size-container"; 586 587 var fileSize = document.createElement("div"); 588 fileSize.id = "file-size"; 589 fileSize.textContent = "0 KB of " + (file.size / 1024).toFixed(2) + " KB"; 590 591 fileSizeContainer.appendChild(fileSize); 592 593 fileDetailsWrapper.appendChild(fileNameContainer); 594 fileDetailsWrapper.appendChild(fileSizeContainer); 595 596 var statusWrapper = document.createElement("div"); 597 statusWrapper.id = "status-wrapper"; 598 599 var progressBarWrapper = document.createElement("div"); 600 progressBarWrapper.id = "progress-bar-wrapper"; 601 602 var progressBar = document.createElement("div"); 603 progressBar.id = "progress-bar"; 604 605 var statusIconContainer = document.createElement("div"); 606 statusIconContainer.id = "status-icon-container"; 607 608 var statusIcon = document.createElement("div"); 609 statusIcon.id = "status-icon"; 610 statusIcon.innerHTML = ""; 611 statusIconContainer.appendChild(statusIcon); 612 613 var statusMessageContainer = document.createElement("div"); 614 statusMessageContainer.id = "status-message-container"; 615 616 var statusMessage = document.createElement("div"); 617 statusMessage.id = "status-message"; 618 statusMessage.textContent = ""; 619 620 statusMessageContainer.appendChild(status
620Message); 621 622 progressBarWrapper.appendChild(progressBar); 623 statusWrapper.appendChild(progressBarWrapper); 624 statusWrapper.appendChild(statusIconContainer); 625 statusWrapper.appendChild(statusMessageContainer); 626 627 fileContainer.appendChild(removeIcon); 628 fileContainer.appendChild(fileDetailsWrapper); 629 fileContainer.appendChild(statusWrapper); 630 631 fileWrapper.appendChild(fileContainer); 632 633 fileElementsMap.set(file.name, { 634 fileContainer, 635 progressBarWrapper, 636 progressBar, 637 statusMessage, 638 fileSize, 639 statusIcon, 640 }); 641 642 uploadFile(file, progressBar, statusMessage, fileSize, statusIcon, statusWrapper, fileContainer, removeIcon, progressBarWrapper); 643 } 644 }); 645 } 646 } 647 648 649 function shortenFileName(fileName, maxLength) { 650 if (fileName.length <= maxLength) return fileName; 651 var fileExtension = fileName.split('.').pop(); 652 var baseName = fileName.substring(0, maxLength - fileExtension.length - 3); 653 return baseName + "..." + fileExtension; 654 } 655 656 function checkImageDimensions(file) { 657 const maxPixels = 10000; 658 659 return new Promise(function(resolve, reject) { 660 createImageBitmap(file).then(function(bitmap) { 661 const width = bitmap.width; 662 const height = bitmap.height; 663 664 bitmap.close(); 665 666 resolve({ 667 valid: width <= maxPixels && height <= maxPixels, 668 width: width, 669 height: height 670 }); 671 }).catch(function() { 672 reject(new Error('Failed to read image dimensions')); 673 }); 674 }); 675 } 676 677 function uploadFile(file, progressBar, statusMessage, fileSize, statusIcon, statusWrapper, fileContainer, removeIcon, progressBarWrapper) { 678 679 const fileExtension = file.name.split('.').pop().toLowerCase(); 680 const fileGeneralErrorMessage = unqiueCollection.fileMessages.fileGeneralErrorMessage; 681 const fileSizeErrorMessage = unqiueCollection.fileMessages.fileSizeErrorMessage 682 const fileAllowedTypesErrorMessage = unqiueCollection.fileMessages.fileAllowedTypesErrorMessage 683 const imageMaxDimensionsErrorMessage = 'Image dimensions exceed the maximum size of 10,000 pixels' 684 685 if (file.size > MAX_FILE_SIZE) { 686 statusMessage.textContent = fileSizeErrorMessage; 687 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "WARNING"); 688 if (fileSizeErrorMessage.length > 52) { 689 fileContainer.style.alignItems = 'baseline'; 690 statusWrapper.style.alignItems = 'baseline'; 691 statusMessage.style.maxWidth = "318px"; 692 } 693 statusIcon.innerHTML = getErrorIconSVG(); 694 showOrHideSubmitButtons(); 695 696 return; 697 } 698 699 700 if (!unqiueCollection['SUPPORTED_FORMATS'].some(el => el && el.includes(fileExtension))) { 701 statusMessage.textContent = fileAllowedTypesErrorMessage; 702 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "WARNING"); 703 if (fileAllowedTypesErrorMessage.length > 52) { 704 fileContainer.style.alignItems = 'baseline'; 705 statusWrapper.style.alignItems = 'baseline'; 706 statusMessage.style.maxWidth = "318px"; 707 } 708 709 statusIcon.innerHTML = getErrorIconSVG(); 710 showOrHideSubmitButtons(); 711 return; 712 } 713 714 const progressStep = 33.33; 715 let currentProgress = 0; 716 let simulatedProgress = 66; 717 let updateInterval; 718 719 const updateProgress = (targetProgress) => { 720 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "IN_PROGRESS"); 721 722 if (updateInterval) clearInterval(updateInterval); 723 updateInterval = setInterval(() => { 724 if (currentProgress < targetProgress) { 725 currentProgress += 0.5; 726 progressBar.style.width = `${currentProgress}%`; 727 fileSize.textContent = `${((currentProgress / 100) * file.size / 1024).toFixed(2)} KB of ${(file.size / 1024).toFixed(2)} KB`; 728 } else { 729 clearInterval(updateInterval); 730 } 731 }, 50); 732 }; 733 734 const finalizeProgress = (attachmentId, jwtToken) => { 735 clearInterval(updateInterval); 736 progressBar.style.width = "100%"; 737 fileSize.textContent = `${(file.size / 1024).toFixed(2)} KB of ${(file.size / 1024).toFixed(2)} KB`; 738 setTimeout(() => { 739 statusMessage.textContent = "Completed"; 740 statusIcon.innerHTML = getSuccessIconSVG(); 741 progressBarWrapper.style.display = "none"; 742 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "DONE", attachmentId, jwtToken); 743 744 updateFileUploadMetadata(unqiueCollection, index); 745 updateUploadStatusVisibility(); 746 showOrHideSubmitButtons(); 747 }, 2000); 748 749 }; 750 751 const showError = (message) => { 752 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "WARNING"); 753 clearInterval(updateInterval); 754 progressBarWrapper.style.display = "none"; 755 statusMessage.textContent = message; 756 statusIcon.innerHTML = getErrorIconSVG(); 757 updateUploadStatusVisibility(); 758 showOrHideSubmitButtons(); 759 }; 760 761 // 1. Store Request (0–33%) 762 const storeRequest = () => { 763 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "IN_PROGRESS"); 764 765 const controller = new AbortController(); 766 const signal = controller.signal; 767 768 const xhr = new XMLHttpRequest(); 769 770 var url = scheme + options.domain + ACTON_BASE_URL + "/file-process/"; 771 xhr.open("POST", url, true); 772 xhr.setRequestHeader( 'Content-Type', 'application/json' ); 773 774 xhr.signal = signal; 775 776 xhr.onload = () => { 777 updateProgress(progressStep); 778 if (xhr.status === 200) { 779 const response = JSON.parse(xhr.responseText); 780 const uploadUrl = response.uploadUrl; 781 uploadToS3(uploadUrl); 782 } else { 783 showError(fileGeneralErrorMessage); 784 controller.abort(); 785 } 786 }; 787 788 xhr.onerror = () => { 789 showError(fileGeneralErrorMessage); 790 controller.abort(); 791 }; 792 793 xhr.send( 794 JSON.stringify({ 795 accountId: options.accountId, 796 userId: 1, 797 identifier: options.id.replace(':', '_'), 798 fullFileName: file.name, 799 objectId: options.id.split(":")[0], 800 objectType: "FORM", 801 mimeType: file.type, 802 }) 803 ); 804 }; 805 806 // 2. Upload to S3 (33–66%) 807 const uploadToS3 = (uploadUrl) => { 808 const controller = new AbortController(); 809 const signal = controller.signal; 810 811 const xhr = new XMLHttpRequest(); 812 xhr.open("PUT", uploadUrl, true); 813 xhr.setRequestHeader("Content-Type", file.type); 814 xhr.signal = signal; 815 816 xhr.upload.onprogress = (event) => { 817 if (event.lengthComputable) { 818 const targetProgress = progressStep + (event.loaded / event.total) * progressStep; 819 updateProgress(targetProgress); 820 } 821 }; 822 823 xhr.onload = () => { 824 if (xhr.status === 200) { 825 confirmUpload(uploadUrl); 826 } else { 827 showError(fileGeneralErrorMessage); 828 controller.abort(); 829 } 830 }; 831 832 xhr.onerror = () => { 833 showError(fileGeneralErrorMessage); 834 controller.abort(); 835 }; 836 837 xhr.send(file); 838 }; 839 840 // 3. Confirm Upload and File-Scan (66–100%) 841 const confirmUpload = (uploadUrl) => { 842 const controller = new AbortController(); 843 const signal = controller.signal; 844 845 const xhr = new XMLHttpRequest(); 846 847 var url = scheme + options.domain + ACTON_BASE_URL + "/file-process/confirm"; 848 xhr.open("POST", url, true); 849 xhr.setRequestHeader( 'Content-Type', 'application/json' ); 850 xhr.signal = signal; 851 852 xhr.onload = () => { 853 if (xhr.status === 200) { 854 pollFileStatus(uploadUrl); 855 } else {
856 showError(FILE_SCAN_ERROR_TEXT); 857 controller.abort(); 858 } 859 }; 860 861 xhr.onerror = () => { 862 showError(FILE_SCAN_ERROR_TEXT); 863 controller.abort(); 864 }; 865 866 xhr.send(JSON.stringify({ tempUrl: uploadUrl })); 867 }; 868 869 const handlePollingError = (fileName, dropzoneId, errorMessage, controller) => { 870 const dropzoneState = dropzoneFileCollections[dropzoneId]["pollingState"][fileName]; 871 if (dropzoneState) { 872 dropzoneState.isPolling = false; 873 clearInterval(dropzoneState.interval); 874 } 875 controller.abort(); 876 showError(errorMessage); 877 }; 878 879 const pollFileStatus = (uploadUrl) => { 880 const controller = new AbortController(); 881 const signal = controller.signal; 882 883 const pollingIntervalMs = 4000; 884 const maxPollingTimeMs = 1 * 60 * 1000; 885 const maxAttempts = Math.floor(maxPollingTimeMs / pollingIntervalMs); 886 let attemptCount = 0; 887 888 const pollingState = unqiueCollection["pollingState"]; 889 if (!pollingState[file.name]) { 890 pollingState[file.name] = { isPolling: false, interval: null }; 891 } 892 893 const dropzoneState = unqiueCollection["pollingState"][file.name]; 894 895 if (dropzoneState.isPolling) return; 896 dropzoneState.isPolling = true; 897 898 const startPolling = () => { 899 dropzoneState.interval = setInterval(() => { 900 if (attemptCount >= maxAttempts) { 901 handlePollingError(file.name, uniqueDropzoneId, "Polling timed out. Please try again."); 902 controller.abort(); // Abort the request if polling times out 903 return; 904 } 905 906 const xhr = new XMLHttpRequest(); 907 908 var url = scheme + options.domain + ACTON_BASE_URL + "/file-scanning/statuses"; 909 xhr.open("POST", url, true); 910 xhr.setRequestHeader( 'Content-Type', 'application/json' ); 911 xhr.signal = signal; 912 913 xhr.onload = () => { 914 simulatedProgress = Math.min(simulatedProgress + Math.floor(Math.random() * 11), 99); 915 progressBar.style.width = `${simulatedProgress}%`; 916 fileSize.textContent = `${((simulatedProgress / 100) * file.size / 1024).toFixed(2)} KB of ${(file.size / 1024).toFixed(2)} KB`; 917 918 if (xhr.status === 200) { 919 const response = JSON.parse(xhr.responseText); 920 const statuses = response; 921 922 if (statuses && statuses.length > 0) { 923 const status = statuses[0]; 924 if (status.status === "IN_PROGRESS") { 925 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "IN_PROGRESS"); 926 } else if (status.status === "DONE") { 927 if (!(status.attachmentId || status.url)) { 928 pollFileStatus(uploadUrl); 929 } else { 930 dropzoneState.isPolling = false;
931 clearInterval(dropzoneState.interval); 932 finalizeProgress(status.attachmentId, status.jwtToken); 933 } 934 } else if (status.status === "FAILED") { 935 handlePollingError(file.name, uniqueDropzoneId, FILE_SCAN_ERROR_TEXT, controller); 936 } 937 } else { 938 handlePollingError(file.name, uniqueDropzoneId, FILE_SCAN_ERROR_TEXT, controller); 939 } 940 } else { 941 handlePollingError(file.name, uniqueDropzoneId, FILE_SCAN_ERROR_TEXT, controller); 942 } 943 }; 944 945 xhr.onerror = () => { 946 handlePollingError(file.name, uniqueDropzoneId, FILE_SCAN_ERROR_TEXT, controller); 947 }; 948 949 xhr.send(JSON.stringify([uploadUrl])); 950 attemptCount++; 951 }, pollingIntervalMs); 952 }; 953 954 startPolling(); 955 }; 956 957 if(file.type.startsWith('image/')){ 958 checkImageDimensions(file).then(function(result) { 959 if(!result.valid){ 960 statusMessage.textContent = imageMaxDimensionsErrorMessage; 961 updateFileStatus(allowMultiple, uniqueDropzoneId, file.name, "WARNING"); 962 fileContainer.style.alignItems = 'baseline'; 963 statusWrapper.style.alignItems = 'baseline'; 964 statusMessage.style.maxWidth = "318px"; 965 statusIcon.innerHTML = getErrorIconSVG(); 966 showOrHideSubmitButtons(); 967 968 return; 969 } 970 else { 971 progressBarWrapper.style.display = "block"; 972 removeIcon.style.display = "none"; 973 storeRequest(); 974 } 975 }) 976 } else { 977 progressBarWrapper.style.display = "block"; 978 removeIcon.style.display = "none"; 979 storeRequest(); 980 } 981 } 982 983 function updateUploadStatusVisibility() { 984 const uploadStatuses = document.querySelectorAll('#upload-status'); 985 const statusEL = uploadStatuses[index]; 986 const anyInProgress = Array.from(unqiueCollection.fileElementsMap.values()).some( 987 (fileData) => fileData.progressBarWrapper.style.display === "block" 988 ); 989 statusEL.style.display = anyInProgress ? "block" : "none"; 990 } 991 } 992 }); 993 }); 994 995 if(response.formProperties && ! response.formProperties.invalidDomain){ 996 //Set referrer 997 div.querySelector("input[name='ao_refurl']").value = document.referrer; 998 div.querySelector("input[name='ao_target']").value = window.location.href; 999 div.querySelector("input[name='ao_refemail']").value = getParameterByName("aoRefEmail"); 1000 div.querySelector("input[name='ao_campid']").value = getParameterByName("cmpid"); 1001 div.querySelector("input[name='ao_gatedpage']").value = getParameterByName("ao_gatedpage"); 1002 div.querySelector("input[name='ao_gatedasset']").value = getParameterByName("ao_gatedasset"); 1003 1004 var ajaxSubmit = response && response.formProperties && response.formProperties.hasAjaxResponse ? response.formProperties.hasAjaxResponse : false;
1005 1006 loadCaptcha(); 1007 !!response.formProperties.removeFormTabIndexes 1008 ? removeTabIndexes() 1009 : updateTabIndexes(); 1010 loadGoogleFonts(); 1011 1012 formValidationEvents(div, ajaxSubmit); 1013 1014 1015 // Form Prefill 1016 if ( response.prefill && Object.keys( response.prefill ).length > 0 ) { 1017 var fields = Object.keys( response.prefill ); 1018 1019 fields.forEach( function( field ) { 1020 var input = div.querySelector("input[name='" + field + "']"); 1021 var select = div.querySelector("select[name='" + field + "']"); 1022 var textarea = div.querySelector("textarea[name='" + field + "']"); 1023 1024 if ( ! input && ! select && ! textarea ) { 1025 return; 1026 } 1027 1028 if(input){ 1029 if(input.type== "radio"){ 1030 var radios = div.querySelectorAll("input[name='" + field + "']"); 1031 for(var i = 0; i<radios.length; i++){ 1032 if(radios[i].value == response.prefill[field]){ 1033 radios[i].checked = true; 1034 } 1035 } 1036 } else if(input.type== "checkbox") { 1037 if ( ! response.prefill[field] ) { return true; } 1038 var checkboxes = div.querySelectorAll("input[name='" + field + "']"); 1039 var values = response.prefill[field].split(/[;,]/); 1040 1041 for(var i = 0; i<checkboxes.length; i++){ 1042 for(var j = 0; j<values.length; j++) { 1043 var strippedVal = values[j].replace(/^\^(.+(?=\^$))\^$/, '$1'); 1044 if (checkboxes[i].value == strippedVal) { 1045 checkboxes[i].checked = true; 1046 } 1047 } 1048 } 1049 } else if (input.type=="text"){ 1050 div.querySelector("input[name='" + field + "']").value = response.prefill[field]; 1051 } else if (input.type === 'hidden' && input.className === 'ao-form-field-date-input') { 1052 populateDateField(input, response.prefill[field]); 1053 } 1054 } else if(textarea) { 1055 div.querySelector("textarea[name='" + field + "']").value = response.prefill[field]; 1056 } else if(select){ 1057 for (var i = 0; i < select.options.length; i++) { 1058 if (select.options[i].text == response.prefill[field]) { 1059 select.options[i].selected = true; 1060 } 1061 } 1062 } 1063 }); 1064 } 1065 1066 // init the rule listeners here 1067 if ( response.formProperties && response.formProperties.hasRules === true ) { 1068 loadRules( options.accountId, options.id, response.watchFields, options, div); 1069 } else { 1070 div.style.visibility = "visible"; 1071 } 1072 1073 //Post Load Callback (universal) 1074 if (typeof aoPostLoadCallback === 'function') { 1075 aoPostLoadCallback(); 1076 } 1077 1078 //Post Load Callback (form specific) 1079 if(aoPostLoadFormCallback[divId] && typeof aoPostLoadFormCallback[divId] === 'function'){ 1080 aoPostLoadFormCallback[divId](); 1081 } 1082 1083 //Post Load Callback 1084 if (typeof aoPostLoadCallbackInternal === 'function') { 1085 aoPostLoadCallbackInternal(); 1086 } 1087 } else { 1088 div.style.visibility = "visible"; 1089 } 1090 }; 1091 1092 var url; 1093 1094 if(options.isTemp) { 1095 url = scheme + options.domain + '/acton/internalapi/FormBuilder/' + options.accountId + '/form/temp/' + options.id + '?ts=' + new Date().getTime() + "&noStyle=" + ((options.noStyle) ? "1" : "") + "&prefilledData=" + ((options.prefilledData) ? options.prefilledData : ""); 1096 } else if(options.recId) { 1097 url = scheme + options.domain + '/acton/internalapi/FormBuilder/' + options.accountId + '/form/' + options.id + '?ts=' + new Date().getTime() + "&noStyle=" + ((options.noStyle) ? "1" : "") + "&prefilledData=" + ((options.prefilledData) ? options.prefilledData : "") + "&recId=" + ((options.recId) ? options.recId : ""); 1098 } else { 1099 var useParentDomain = ''; 1100 if (document.location.pathname.includes("subscriberPreviewPane.jsp") || document.location.pathname.includes("read/snippet/form")) { 1101 useParentDomain = '&assetNotAccepted=' + true; 1102 } 1103 url = scheme + options.domain + "/acton/openapi/form/v1/" + options.accountId + "/" + options.id + '?ts=' + 1104 new Date().getTime() + (options.noStyle ? "&noStyle=1" : "" ) + (options.prefill ? "&prefill=1" : "") + 1105 useParentDomain; 1106 } 1107 1108 var gated_asset = getParameterByName("ao_gatedasset"); 1109 url += gated_asset ? "&ga=" + gated_asset : ''; 1110 1111 oReq.open('GET', url, true); 1112 oReq.responseType = 'json'; 1113 oReq.withCredentials = true; 1114 oReq.send(); 1115 }; 1116 1117 var loadStylesheet = function(options){ 1118 if(! stylesLoaded){ 1119 var styleTag = document.createElement("link"); 1120 styleTag.rel = "stylesheet"; 1121 styleTag.type = "text/css"; 1122 styleTag.href = scheme + options.domain + "/acton/content/form_flattener.css"; 1123 styleTag.media = "all"; 1124 document.getElementsByTagName('head')[0].appendChild(styleTag); 1125 stylesLoaded = true; 1126 } 1127 }; 1128 1129 var loadCaptcha = function(){ 1130 1131 //Only load captcha if at least one form on the page actually has a reCAPTCHA block rendered 1132 if(! captchaLoaded && (formsCount == formsLoadedCount) && document.querySelector(".g-recaptcha")){ //Don't load captcha until all forms that exist on page are loaded 1133 var scriptTag = document.createElement("script"); 1134 1135 scriptTag.src = "https://www.google.com/recaptcha/api.js"; 1136 scriptTag.defer = true; 1137 scriptTag.async = true; 1138 1139 document.getElementsByTagName('head')[0].appendChild(scriptTag); 1140 captchaLoaded = true; 1141 1142 1143 } 1144 }; 1145 1146 var removeTabIndexes = function() { 1147 if (formsLoadedCount > 0) { 1148 const actonForms = document.querySelectorAll("form.ao-form"); 1149 for (let i = 0; i < actonForms.length; i++) { 1150 const inputFields = actonForms[i].querySelectorAll('[tabindex]'); 1151 for (let j = 0; j < inputFields.length; j++) { 1152 inputFields[j].removeAttribute("tabindex"); 1153 } 1154 } 1155 } 1156 1157 } 1158 1159 var updateTabIndexes = function(){ 1160 //If multiple forms on one page, update each new form with higher tab index (100+) than previous form) 1161 if(formsCount == formsLoadedCount && formsLoadedCount > 1) { 1162 var actonForms = document.querySelectorAll("form.ao-form"); 1163 1164 var startingTabIndex = 100; 1165 for(var i = 1; i<actonForms.length; i++){ 1166 var inputFields = actonForms[i].querySelectorAll('[tabindex]'); 1167 for(var j=0; j<inputFields.length; j++){ 1168 inputFields[j].tabIndex = inputFields[j].tabIndex + startingTabIndex; 1169 } 1170 startingTabIndex = startingTabIndex + 100; 1171 } 1172 } 1173 }; 1174 1175 var loadGoogleFonts = function(){ 1176 1177 if(! gfLoaded){ 1178 var scriptTag = document.createElement("script"); 1179 1180 scriptTag.src = "https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js"; 1181 scriptTag.defer = true; 1182 scriptTag.async = true; 1183 1184 document.getElementsByTagName('head')[0].appendChild(scriptTag); 1185 gfLoaded = true; 1186 } 1187 }; 1188 1189 var evalFacts = function( accountId, formId, watchFields, options, containerDiv) { 1190 1191 var fact = { 1192 data : {} 1193 }; 1194 1195 if ( formId.indexOf(':') > -1 ) { 1196 formId = formId.substr(0, formId.indexOf(':')); 1197 } 1198 1199 if(options.uniqueId){ 1200 formId += (options.uniqueId) ? options.uniqueId : ''; 1201 } 1202 1203 var formByID = document.getElementById("aoform-" + formId); 1204 1205 watchFields.forEach( function ( field ) { 1206 1207 // Webinar form block IDs are just numbers and do not start with `block-` or `b` 1208 var fieldElm = field.startsWith("b") 1209 ? formByID.querySelector("#" + field) 1210 : document.getElementById(field); 1211 1212 if (!fieldElm) { 1213 fieldElm = formByID.querySelector("#block-" + field); 1214 } 1215 1216 if(fieldElm){ 1217 if(fieldElm.nodeName == "DIV" && fieldElm.classList.contains("ao-combo-layout")) { 1218 var radios = fieldElm.querySelectorAll("input[type='radio'],input[type='checkbox']"); 1219 fact.data[field] = ""; 1220 1221 for (var i = 0; i < radios.length; i++) { 1222 if (radios[i].checked) { 1223 fact.data[field] += radios[i].value; 1224 } 1225 } 1226 }else if(fieldElm.nodeName=="DIV" && fieldElm.classList.contains("ao-date-block")) { 1227 /* 1228 Get parent div of a date control, then get all "ao-form-field-date" objects from that div 1229 Build a date object from those, and set as value. If it's an invali
1229d date, value = null 1230 1231 Date fields are tagged with id "date-<PART>-<ID>" 1232 */ 1233 1234 var dtMonth = ""; 1235 var dtDay = ""; 1236 var dtYear = ""; 1237 var txtInputs = fieldElm.querySelectorAll("input[type='text']"); 1238 var dtMonth = ""; 1239 var dtDay = ""; 1240 var dtYear = ""; 1241 var txtInputs = fieldElm.querySelectorAll("input[type='text']"); 1242 var hasDateError = false; 1243 for(var i=0; i < txtInputs.length; i++){ 1244 if (txtInputs[i].value == ""){ 1245 hasDateError = true; 1246 } 1247 switch(txtInputs[i].getAttribute("data-format")){ 1248 case "DD": 1249 dtDay = txtInputs[i].value; 1250 break; 1251 case "MM": 1252 dtMonth = txtInputs[i].value; 1253 break; 1254 case "YY": 1255 dtYear = txtInputs[i].value; 1256 break; 1257 case "YYYY": 1258 dtYear = txtInputs[i].value; 1259 break; 1260 } 1261 } 1262 if (!hasDateError){ 1263 fact.data[field] = dtYear + dtMonth + dtDay; 1264 }else{ 1265 fact.data[field] = ""; 1266 } 1267 } else { 1268 fact.data[field] = fieldElm.value; 1269 } 1270 } 1271 }); 1272 1273 var body = { 1274 payload : { 1275 accountId : accountId, 1276 key : formId, 1277 facts : [fact] 1278 } 1279 }; 1280 1281 var xhr = new XMLHttpRequest(); 1282 xhr.open( 'PUT', scheme + options.domain + '/acton/openapi/form/v1/' + options.accountId + '/' + formId + '/factEval' ); 1283 xhr.setRequestHeader( 'Content-Type', 'application/json' ); 1284 xhr.send( JSON.stringify( body ) ); 1285 1286 // Check if the active element lost focus due to conflicting conditionals such as hide / show, 1287 // Give focus back if lost focus and not hidden 1288 function refocusIfLostFocus(activeEl) { 1289 if(activeEl !== document.activeElement && !activeEl.classList.contains("ao-condition-hide")) { 1290 activeEl.focus(); 1291 } 1292 } 1293 1294 xhr.onreadystatechange = function() { 1295 if ( xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200 ) { 1296 // Get element that has focus before hiding/showing elements 1297 const activeEl = document.activeElement; 1298 var response = JSON.parse(xhr.response); 1299 1300 //Go through the non true results first and set base display 1301 for(var i=0; i<response.results[0].result.length; i++) { 1302 1303 var result = response.results[0].result[i].result; 1304 var ruleName = response.results[0].result[i].name; 1305 var consequence = response.rules[ruleName].consequences; 1306 1307 if (!result) { 1308 for (var j = 0; j < consequence.length; j++) { 1309 1310 var state = consequence[j].state; 1311 1312 if (state !== "alert") { 1313 var field = consequence[j].field; 1314 var elm = formByID.querySelector("#block-" + field) || formByID.querySelector("#row-" + field); 1315 1316 if(elm){ 1317 switch (state) { 1318 1319 case "show": 1320 elm.style.display = "none"; 1321 formElms = elm.querySelectorAll("input, select, textarea"); 1322 for(var k=0; k<formElms.length; k++) { 1323 formElms[k].disabled = true; 1324 } 1325 elm.classList.add("ao-condition-hide"); 1326 break; 1327 1328 case "hide": 1329 elm.style.removeProperty("display"); 1330 formElms = elm.querySelectorAll("input, select, textarea"); 1331 for(var l=0; l<formElms.length; l++) { 1332 formElms[l].disabled = false;
1333 } 1334 elm.classList.remove("ao-condition-hide"); 1335 break; 1336 } 1337 } 1338 } 1339 } 1340 // Check if the previously focused element is no longer focused to avoid hide / show unfocusing 1341 refocusIfLostFocus(activeEl); 1342 } 1343 } 1344 1345 //Show form on initial load after fields have been hidden 1346 if(containerDiv){ 1347 containerDiv.style.visibility = "visible"; 1348 } 1349 1350 //Now go through the true results first and set correct display 1351 for(var i=0; i<response.results[0].result.length; i++) { 1352 1353 var result = response.results[0].result[i].result; 1354 var ruleName = response.results[0].result[i].name; 1355 var consequence = response.rules[ruleName].consequences; 1356 1357 if (result) { 1358 for (var j = 0; j < consequence.length; j++) { 1359 1360 var state = consequence[j].state; 1361 1362 if (state !== "alert") { 1363 var field = consequence[j].field; 1364 var elm = formByID.querySelector("#block-" + field) || formByID.querySelector("#row-" + field); 1365 1366 if(elm) { 1367 switch (state) { 1368 1369 case "show": 1370 elm.style.removeProperty("display"); 1371 1372 formElms = elm.querySelectorAll("input, select, textarea"); 1373 for(var k=0; k<formElms.length; k++) { 1374 formElms[k].disabled = false; 1375 } 1376 elm.classList.remove("ao-condition-hide"); 1377 break; 1378 1379 case "hide": 1380 elm.style.display = "none"; 1381 formElms = elm.querySelectorAll("input, select, textarea"); 1382 for(var l=0; l<formElms.length; l++) { 1383 formElms[l].disabled = true; 1384 } 1385 elm.classList.add("ao-condition-hide"); 1386 break; 1387 } 1388 } 1389 } 1390 } 1391 // Check if the previously focused element is no longer focused to avoid hide / show unfocusing 1392 refocusIfLostFocus(activeEl); 1393 } 1394 } 1395 } else { 1396 // an error occurred 1397 } 1398 }; 1399 }; 1400 1401 var loadRules = function( accountId, formId, watchFields, options, containerDiv) { 1402 if ( formId.indexOf(':') > -1 ) { 1403 formId = formId.substr( 0, formId.indexOf(':') ) 1404 } 1405 1406 if ( !watchFields ){ 1407 //Show form on initial load 1408 containerDiv.style.visibility = "visible"; 1409 return; 1410 } 1411 1412 //Remove duplicate watches 1413 var watchFieldsSeen = {}; 1414 var watchFieldsUnique = watchFields.filter(function(item) { 1415 return watchFieldsSeen.hasOwnProperty(item) ? false : (watchFieldsSeen[item] = true); 1416 }); 1417 1418 watchFieldsUnique.forEach( function ( field ) { 1419 1420 // Webinar form block IDs are just numbers and do not start with `block-` or `b` 1421 var el = field.startsWith("b") 1422 ? containerDiv.querySelector("#" + field) 1423 : document.getElementById(field); 1424 1425 /*If fieldElm is not found, will need to check for alternatives 1426 * Most likely candidate, if "date", look for a the block, then a named child 1427 * EG: div: block-fieldID child named date*/ 1428 if(!el){ 1429 el = containerDiv.querySelector("#block-" + field); 1430 } 1431 1432 if(el){ 1433 if(el.nodeName == "SELECT"){ 1434 el.addEventListener( 'change', function ( event ) { 1435 // put a debounce in here so we're making requests on every keystroke 1436 var result = evalFacts( accountId, formId, watchFieldsUnique, options ); 1437 }, false ); 1438 } else if(el.nodeName == "DIV" && el.classList.contains("ao-combo-layout")) { 1439 var inputs = el.querySelectorAll("input"); 1440 1441 for (i = 0; i < inputs.length; i++) { 1442 inputs[i].addEventListener('click', function (event) { 1443 // put a debounce in here so we're making requests on every keystroke 1444 if (el.classList.contains("ao-form-error")) { 1445 el.classList.remove("ao-form-error"); 1446 } 1447 var result = evalFacts(accountId, formId, watchFieldsUnique, options); 1448 }, false); 1449 } 1450 } 1451 else if(el.nodeName == "DIV" && el.classList.contains("ao-date-block")) { 1452 var inputs = el.querySelectorAll("input"); 1453 1454 for (i = 0; i < inputs.length; i++) { 1455 if(!inputs[i].classList.contains("ao-form-field-date-input")) { 1456 inputs[i].addEventListener( 'keyup', debounce(function () { 1457 evalFacts( accountId, formId, watchFieldsUnique, options ); 1458 }, 500), false); 1459 } 1460 } 1461 1462 } else { 1463 el.addEventListener( 'keyup', debounce(function () { 1464 evalFacts( accountId, formId, watchFieldsUnique, options ); 1465 }, 500), false); 1466 } 1467 } 1468 }); 1469 1470 //Initial Load 1471 evalFacts( accountId, formId, watchFieldsUnique, options, containerDiv); 1472 }; 1473 1474 function getParameterByName(name) { 1475 url = window.location.href; 1476 name = name.replace(/[\[\]]/g, "\\$&"); 1477 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)", "i"), 1478 results = regex.exec(url); 1479 if (!results) return null; 1480 if (!results[2]) return ''; 1481 return decodeURIComponent(results[2].replace(/\+/g, " ")); 1482 } 1483 1484 var formValidationEvents = function(form, ajaxSubmit){ 1485 var actonForms = form.querySelectorAll("form.ao-form"); 1486 1487 var findRecaptchaElements = function(form, hasError){ 1488 //Find the recaptcha wrapper 1489 var recaptchaWrapper = form.querySelectorAll(".ao-recaptcha-error-wrapper"); 1490 if (recaptchaWrapper.length > 0) { 1491 if (hasError) { 1492 //If found and has error then apply the style that will show the red border around the element 1493 recaptchaWrapper[0].classList.add("ao-recaptcha-error"); 1494 } else { 1495 //Remove the style because the user has attempted to work with the recaptcha again 1496 recaptchaWrapper[0].classList.remove("ao-recaptcha-error"); 1497 } 1498 } 1499 1500 //Find the element that will show the failed to completed the recaptcha 1501 var recaptchaMsg = form.querySelectorAll(".ao-recaptcha-error-robot-message"); 1502 if (recaptchaMsg.length > 0) { 1503 if (hasError) { 1504 //Show the text 1505 recaptchaMsg[0].style.display = "block"; 1506 } else { 1507 //Hide the text 1508 recaptchaMsg[0].style.display = "none"; 1509 } 1510 } 1511 }; 1512 1513 var invalidCaptcha = function(form){ 1514 //Wait for the Recaptcha widget before we build the error logging aorund it 1515 setTimeout(function () { 1516 1517 //Apply the styling that says the user failed to validate the Recaptcha 1518 findRecaptchaElements(form, true); 1519 1520 }, 1000); 1521 1522 //Keep checking if the user has attempted to validate the Recaptcha, we will know because the token will go into the hidden input the cap sends on submission 1523 var recaptchaInterval = setInterval(function () { 1524 var recaptchaBoxCode = form.querySelectorAll("#g-recaptcha-response"); 1525 1526 //If we find the element and the value is not empty then a user has interacted with the cap, validation happens on the submission not now 1527 if (recaptchaBoxCode.length > 0 && recaptchaBoxCode[0].value !== '') { 1528 1529 //Remove the styling that was generated by the error 1530 findRecaptchaElements(form, false); 1531 1532 //Remove the interval that listens for the check 1533 window.clearInterval(recaptchaInterval); 1534 } 1535 }, 2000); 1536 }; 1537 1538 for(var i=0; i<actonForms.length; i++) { 1539 1540 //Set On Blur Events For All Inputs
1541 var validationFields = actonForms[i].querySelectorAll(".ao-form-field[data-validator]:not([data-attached])"); 1542 var validationCombos = actonForms[i].querySelectorAll(".ao-combo-layout[data-validator]:not([data-attached])"); 1543 var validationDates = actonForms[i].querySelectorAll(".ao-form-field-date-wrapper[data-validator]:not([data-attached])"); 1544 var validationDateFields = actonForms[i].querySelectorAll(".ao-form-field-date"); 1545 var dynamicHiddenFields = actonForms[i].querySelectorAll(".ao-hidden-block-dynamic"); 1546 var validationFileUpload = actonForms[i].querySelectorAll(".ao-fileupload-field[data-validator]"); 1547 var submitBlock = actonForms[i].querySelectorAll(".ao-submit-block"); 1548 1549 //Set All dynamic fields on load. Will set again on submit. 1550 for (var j = 0; j < dynamicHiddenFields.length; j++) { 1551 var fieldMatch = dynamicHiddenFields[j].getAttribute("data-dynamic-match"); 1552 var fieldDefault = dynamicHiddenFields[j].getAttribute("data-dynamic-default"); 1553 dynamicHiddenFields[j].value = getParameterByName(fieldMatch) || fieldDefault; 1554 } 1555 1556 for (var j = 0; j < validationDateFields.length; j++) { 1557 var currentField = validationDateFields[j]; 1558 1559 (function(currentField) { 1560 currentField.addEventListener( "keyup", function(){ 1561 var e = event || evt; // for trans-browser compatibility 1562 var charCode = e.which || e.keyCode; // for trans-browser compatibility 1563 1564 if(charCode != 9 && charCode != 16 && this.value.length >= this.getAttribute('maxlength') ){ 1565 var node = this.nextSibling; 1566 while (node) { 1567 if (node.tagName=='INPUT') { 1568 node.focus(); 1569 break; 1570 } 1571 node = node.nextSibling; 1572 } 1573 } 1574 }) 1575 })(currentField); 1576 } 1577 1578 //check if the form id matches the param sent back on the user submission redirect of the formid that failed user submit 1579 if(htmlId.indexOf(getParameterByName("validRecaptcha")) > -1) { 1580 invalidCaptcha(form); 1581 } 1582 1583 function enableOrDisableSubmitButton(e, isEnable){ 1584 if(isEnable){ 1585 e.submitter.style.opacity = 1; 1586 e.submitter.style.pointerEvents = "auto"; 1587 e.submitter.disabled = false; 1588 } 1589 else { 1590 e.submitter.style.opacity = 0.5; 1591 e.submitter.style.pointerEvents = "none"; 1592 e.submitter.disabled = true; 1593 } 1594 } 1595 1596 if(actonForms[i].hasAttribute('data-validate-blur')){ 1597 1598 for (var j = 0; j < validationFields.length; j++) { 1599 var currentField = validationFields[j]; 1600 1601 (function(currentField) { 1602 currentField.addEventListener( "focusout", function(){ 1603 validateField(currentField); 1604 }); 1605 currentField.addEventListener( "change", function(){ 1606 validateField(currentField); 1607 }); 1608 })(currentField); 1609 } 1610 1611 for (var j = 0; j < validationFileUpload.length; j++) { 1612 var currentField = validationFileUpload[j]; 1613 1614 (function(currentField) { 1615 currentField.addEventListener( "focusout", function(){ 1616 validateField(currentField); 1617 }); 1618 currentField.addEventListener( "change", function(){ 1619 validateField(currentField); 1620 }); 1621 })(currentField); 1622 } 1623 } 1624 1625 //Set On Submit Event 1626 var currentForm = actonForms[i]; 1627 1628 (function(currentForm, ajaxSubmit) { 1629 currentForm.addEventListener("submit", function(e){ 1630 var passedValidation = true; 1631 for (var i = 0; i < validationFields.length; i++) { 1632 //Check visibility of field. offsetWidth/Height of null = not visible 1633 if (!!( validationFields[i].offsetWidth || validationFields[i].offsetHeight || validationFields[i].getClientRects().length )){
1634 if (!validateField(validationFields[i])) { 1635 passedValidation = false; 1636 } 1637 } 1638 } 1639 1640 for (var i = 0; i < validationFileUpload.length; i++) { 1641 //Check visibility of field. offsetWidth/Height of null = not visible 1642 if (!validateField(validationFileUpload[i], {isFileUpload: true})) { 1643 passedValidation = false; 1644 } 1645 } 1646 1647 for (var i = 0; i < validationCombos.length; i++) { 1648 //Check visibility of field. offsetWidth/Height of null = not visible 1649 if (!!( validationCombos[i].offsetWidth || validationCombos[i].offsetHeight || validationCombos[i].getClientRects().length )) { 1650 if (!validateField(validationCombos[i], {isCombo: true})) { 1651 passedValidation = false; 1652 } 1653 } 1654 } 1655 1656 for (var i = 0; i < validationDates.length; i++) { 1657 if (!!( validationDates[i].offsetWidth || validationDates[i].offsetHeight || validationDates[i].getClientRects().length )) { 1658 if (!validateField(validationDates[i], {isDate: true})) { 1659 passedValidation = false; 1660 } 1661 } 1662 } 1663 1664 for (var i = 0; i < submitBlock.length; i++) { 1665 if (!( submitBlock[i].offsetWidth || submitBlock[i].offsetHeight || submitBlock[i].getClientRects().length )) { 1666 passedValidation = false; 1667 } 1668 } 1669 1670 for (var i = 0; i < dynamicHiddenFields.length; i++) { 1671 var fieldMatch = dynamicHiddenFields[i].getAttribute("data-dynamic-match"); 1672 var fieldDefault = dynamicHiddenFields[i].getAttribute("data-dynamic-default"); 1673 dynamicHiddenFields[i].value = getParameterByName(fieldMatch) || fieldDefault; 1674 } 1675 1676 //Set Date fields 1677 var dateBlocks = currentForm.querySelectorAll(".ao-date-block"); 1678 for(var i = 0; i < dateBlocks.length; i++){ 1679 var dateBlock = dateBlocks[i]; 1680 var dateBlockInput = dateBlock.querySelector(".ao-form-field-date-input"); 1681 var dateBlockPartials = dateBlock.querySelectorAll(".ao-form-field-date"); 1682 1683 var dateVal = ""; 1684 1685 for(var j=0; j<dateBlockPartials.length; j++){ 1686 var partial = dateBlockPartials[j]; 1687 var format = partial.getAttribute("data-format"); 1688 var isHour = format === "h" || format === "H"; 1689 1690 if(format == "MM" || format == "DD" || format == "YY" || format == "YYYY"){ 1691 if(dateVal){ 1692 dateVal = dateVal + "/"; 1693 } 1694 dateVal += partial.value; 1695 } 1696 1697 if(isHour || format == "mm"){ 1698 if(dateVal && isHour){ 1699 dateVal = dateVal + " "; 1700 } 1701 1702 dateVal += partial.value; 1703 1704 if(isHour && partial.value){ 1705 dateVal = dateVal + ":"; 1706 } 1707 } 1708 1709 if(format == "tt"){ 1710 if(dateVal){ 1711 dateVal = dateVal + " "; 1712 } 1713 dateVal += partial.value; 1714 } 1715 } 1716 1717 dateBlockInput.value = dateVal; 1718 } 1719 1720 var captcha = currentForm.querySelector('.g-recaptcha-response'); 1721 1722 // if a captcha is being used check it 1723 if ( captcha !== null ) { 1724 var captchaValue = captcha.value; 1725 1726 // if the captcha value is empty invalidate the form 1727 if ( captchaValue === '' ) { 1728 passedValidation = false;
1729 invalidCaptcha( currentForm ); 1730 } 1731 } 1732 1733 if(! passedValidation || ajaxSubmit){ 1734 e.preventDefault(); 1735 } 1736 1737 if(passedValidation){ 1738 showOrHideSubmitButtons(currentForm); 1739 enableOrDisableSubmitButton(e, false); 1740 } 1741 1742 1743 if(passedValidation && ajaxSubmit){ 1744 var data = new FormData(currentForm); 1745 1746 var req = new XMLHttpRequest(); 1747 req.onerror = function() { 1748 enableOrDisableSubmitButton(e, true); 1749 1750 }; 1751 1752 req.onload = function(){ 1753 enableOrDisableSubmitButton(e, true); 1754 1755 var response; 1756 1757 try { 1758 response = JSON.parse(this.response); 1759 } catch(e) { 1760 response = this.response; 1761 } 1762 1763 if(typeof response === 'object' && response.error && response.error == "captcha"){ 1764 invalidCaptcha(currentForm); 1765 } else { 1766 var divId = currentForm.id.replace("ao-form", "aoform"); 1767 var formId = currentForm.id.replace("ao-form-", ""); 1768 var div = document.getElementById(divId); 1769 1770 div.innerHTML = typeof response === 'string' ? response : ''; 1771 1772 //Post Submit Callback 1773 if (typeof aoPostSubmitCallback === 'function') { 1774 aoPostSubmitCallback(); 1775 } 1776 1777 //Post Submit Callback (form specific) 1778 formId = ( formId.indexOf(':') > -1 ) ? formId.substr( 0, formId.indexOf(':') ) : formId; 1779 1780 if(aoPostSubmitFormCallback[formId] && typeof aoPostSubmitFormCallback[formId] === 'function'){ 1781 aoPostSubmitFormCallback[formId](); 1782 } 1783 1784 //Inline gated content URL is only used in the case of iOS devices 1785 var inlineURL = document.getElementById("ao_inline-" + formId); 1786 1787 if (inlineURL && isIOS()) { 1788 window.location = inlineURL.value; 1789 } 1790 } 1791 }; 1792 req.open(currentForm.method, currentForm.action); 1793 req.withCredentials = true; 1794 req.send(data); 1795 } 1796 }) 1797 })(currentForm, ajaxSubmit); 1798 } 1799 }; 1800 1801 function isIOS() { 1802 return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; 1803 } 1804 1805 // special chracter escaped regex from https://emailregex.com/ with capital letter allowance added 1806 var validEmailRegex="^(?:[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&\'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-zA-Z0-9-]*[a-zA-Z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$" 1807 var validEmailRegexObj = new RegExp(validEmailRegex); 1808 1809 var validateField = function(field, options){ 1810 1811 var o = options ? options : {}; 1812
1813 var validators = field.getAttribute('data-validator').split("|"); 1814 var errorSpan = field.getAttribute('type') === 'file' ? field.parentNode.nextElementSibling : field.parentNode.querySelector(".ao-form-error-message"); 1815 errorSpan.innerHTML = " "; 1816 1817 var errorMessageArr = []; 1818 var errorMessageOverrideArr = (field.getAttribute('data-error-message')) ? field.getAttribute('data-error-message').split("::") : false; 1819 1820 field.classList.remove("ao-form-error"); 1821 1822 if(field.getAttribute('type') === 'file'){ 1823 field.parentNode.classList.remove("ao-file-upload-error"); 1824 field.previousElementSibling.querySelector('label').classList.remove("ao-file-upload-error-label"); 1825 } 1826 1827 for(var i=0; i<errorMessageOverrideArr.length; i++){ 1828 var errorMessageOverride = errorMessageOverrideArr[i].split("|"); 1829 errorMessageArr[ errorMessageOverride[0] ] = errorMessageOverride[1]; 1830 } 1831 1832 1833 for(var j=0; j<validators.length; j++) { 1834 var validationType = validators[j]; 1835 var hasErrors = false; 1836 1837 switch (validationType) { 1838 1839 case "required": 1840 if (o.isCombo) { 1841 var validationComboFields = field.querySelectorAll("input"); 1842 var hasRequired = false; 1843 1844 for (var k = 0; k < validationComboFields.length; k++) { 1845 if (validationComboFields[k].checked) { 1846 hasRequired = true; 1847 } 1848 } 1849 1850 if (!hasRequired) { 1851 hasErrors = true; 1852 } 1853 } 1854 else if(o.isDate) { 1855 var validationDateFields = field.querySelectorAll("input:not(.ao-form-field-date-input), select"); 1856 var hasRequired = true; 1857 1858 for (var l = 0; l < validationDateFields.length; l++) { 1859 if (! validationDateFields[l].value.trim()) { 1860 validationDateFields[l].classList.add("ao-form-error"); 1861 hasRequired = false; 1862 } else { 1863 validationDateFields[l].classList.remove("ao-form-error"); 1864 } 1865 } 1866 1867 if (!hasRequired) { 1868 hasErrors = true; 1869 } 1870 } else if (!field.value.trim()) { 1871 if (field.getAttribute("type") === "file") { 1872 const uniqueDropzoneId = inputToDropzoneMap.get(field); 1873 if (uniqueDropzoneId) { 1874 const dropzoneData = dropzoneFileCollections[uniqueDropzoneId]; 1875 if (dropzoneData && dropzoneData.files && dropzoneData.files.length > 0) { 1876 return false; 1877 } 1878 } 1879 } 1880 hasErrors = true; 1881 } 1882 break; 1883 case "date": 1884 if(o.isDate) { 1885 var validationDateFields = field.querySelectorAll("input"); 1886 1887 for (var m = 0; m < validationDateFields.length; m++) { 1888 var dateType = validationDateFields[m].getAttribute("placeholder"); 1889 1890 switch(dateType){ 1891 case "MM": 1892 if(validationDateFields[m].value && !/^([0]?[1-9]|1[012])$/.test(validationDateFields[m].value) ){ 1893 validationDateFields[m].classList.add("ao-form-error"); 1894 hasErrors = true; 1895 } else { 1896 validationDateFields[m].classList.remove("ao-form-error"); 1897 } 1898 break; 1899 1900 case "DD": 1901 if(validationDateFields[m].value && !/^([0]?[1-9]|[12]\d|3[01])$/.test(validationDateFields[m].value) ){ 1902 validationDateFields[m].classList.add("ao-form-error"); 1903 hasErrors = true; 1904 } else { 1905 validationDateFields[m].classList.remove("ao-form-error"); 1906 } 1907 break; 1908 1909 case "YY": 1910 if(validationDateFields[m].value && !/^(0?[1-9]|[1-9][0-9])$/.test(validationDateFields[m].value) ){ 1911 validationDateFields[m].classList.add("ao-form-error"); 1912 hasErrors = true; 1913 } else { 1914 validationDateFields[m].classList.remove("ao-form-error"); 1915 } 1916 break; 1917 1918 case "YYYY": 1919 if(validationDateFields[m].value && !/^[0-9]{4}$/.test(validationDateFields[m].value) ){ 1920 validationDateFields[m].classList.add("ao-form-error"); 1921 hasErrors = true; 1922 } else { 1923 validationDateFields[m].classList.remove("ao-form-error"); 1924 } 1925 break; 1926 1927 case "h": 1928 if(validationDateFields[m].value && !/^([0]?[1-9]|1[012])$/.test(validationDateFields[m].value) ){ 1929 validationDateFields[m].classList.add("ao-form-error"); 1930 hasErrors = true; 1931 } else { 1932 validationDateFields[m].classList.remove("ao-form-error"); 1933 } 1934 break; 1935 1936 case "H": 1937 if(validationDateFields[m].value && !/^([0]?[0-9]|1[0-9]|2[0-3])$/.test(validationDateFields[m].value) ){ 1938 validationDateFields[m].classList.add("ao-form-error"); 1939 hasErrors = true; 1940 } else { 1941 validationDateFields[m].classList.remove("ao-form-error"); 1942 } 1943 break; 1944 1945 case "mm": 1946 if(validationDateFields[m].value && !/^[0-5][0-9]$/.test(validationDateFields[m].value) ){ 1947 validationDateFields[m].classList.add("ao-form-error"); 1948 hasErrors = true; 1949 } else { 1950 validationDateFields[m].classList.remove("ao-form-error"); 1951 } 1952 break; 1953 } 1954 } 1955 } 1956 break; 1957 case "email": 1958 if (field.value && !validEmailRegexObj.test(field.value)) { 1959 hasErrors = true; 1960 } 1961 break; 1962 case "nc_email": 1963 if (field.value && (/^.*@(163[.]com|aol[.]com|bellsouth[.]net|blueyonder[.]co[.][a-z]{2}|btconnect[.]com|charter[.]com|comcast[.]net|cox[.]net|earthlink[.]net|email[.]com|gmail[.]co.[a-z]{2}|gmail[.]com|hotmail[.]co[.][a-z]{2}|hotmail[.]com|juno[.]com|mail[.]com|mail[.]ru|mindspring[.]com|msn[.]com|ntlworld[.]com|orange[.]fr|rogers[.]com|sbcglobal[.]net|shaw[.]ca|sympatico[.]ca|telus[.]net|verizon[.]net|virgin[.]net|virginmedia[.]com|yahoo[.]ca|yahoo[.]co[.][a-z]{2}|yahoo[.]com|ymail[.]com|icloud[.]com|me[.]com|mac[.]com|outlook[.]com|live[.]com|att[.]net|.*[.]rr[.]com|rr[.]com|qq[.]com|gmx[.]de|yandex[.]ru|libero[.]it|mailinator[.]com)$/i.test(field.value) 1964 || field.value && !validEmailRegexObj.test(field.value))) { 1965 hasErrors = true; 1966 } 1967 break; 1968 case "ng_email": 1969 if (field.value && (!/^(?!(abuse@|accounting@|accounts@|adm@|admin@|administration@|administrator@|admissions@|ads@|all@|answers@|anti-spam@|antispam@|asdf@|billing@|ceo@|comments@|compliance@|contact@|contactus@|customer@|
1969customercare@|customerservice@|database@|decliend@|decline@|declined@|denied@|designer@|devnull@|director@|dns@|email@|employment@|enquiries@|everyone@|fbl@|feedback@|finance@|ftp@|general@|hello@|helpdesk@|home@|hostmaster@|hr@|info@|information@|inoc@|investorrelations@|ispfeedback@|ispsupport@|jobs@|lawyer@|lawyers@|legal@|list@|list-request@|mail@|mailbox@|mail-daemon@|maildaemon@|mail-deamon@|manager@|managers@|marketing@|me@|media@|mediarelations@|mkt@|news@|noc@|noreplies@|no-reply@|noreply@|noemail@|nospam@|nothanks@|null@|office@|operations@|orders@|phish@|phishing@|post@|postbox@|postmaster@|prepress@|president@|press@|privacy@|purchasing@|qwer@|qwert@|qwerty@|reception@|refuse@|refused@|registrar@|remove@|request@|reservations@|returns@|root@|sales@|secretary@|security@|service@|services@|shop@|spam@|staff@|studio@|subscribe@|support@|sysadmin@|tech@|undisclosed-recipients@|unsubscribe@|usenet@|users@|uucp@|web@|webmaster@|welcome@|www@)).*/i.test(field.value) 1970 || field.value && !validEmailRegexObj.test(field.value) ) ){ 1971 hasErrors = true; 1972 } 1973 break; 1974 case "numeric": 1975 if (field.value && !/^\d+$/.test(field.value)) { 1976 hasErrors = true; 1977 } 1978 break; 1979 case "alphabetic": 1980 if (field.value && !/^[a-zA-Z]+$/.test(field.value)) { 1981 hasErrors = true; 1982 } 1983 break; 1984 case "alphanumeric": 1985 if (field.value && !/^[a-zA-Z0-9]*$/.test(field.value)) { 1986 hasErrors = true; 1987 } 1988 break; 1989 case "minlength": 1990 if (field.value && field.value.length < field.getAttribute('data-minlength') || 0) { 1991 hasErrors = true; 1992 } 1993 break; 1994 case "maxlength": 1995 if (field.value && field.value.length > field.getAttribute('maxlength') || 0) { 1996 hasErrors = true; 1997 } 1998 break; 1999 case "minyear": 2000 if(o.isDate) { 2001 var validationDateFields = field.querySelectorAll("input"); 2002 2003 for (var m = 0; m < validationDateFields.length; m++) { 2004 var dateType = validationDateFields[m].getAttribute("placeholder"); 2005 2006 switch(dateType){ 2007 case "YY": 2008 case "YYYY": 2009 if(validationDateFields[m].value < field.getAttribute('data-minyear') || 0 ){ 2010 validationDateFields[m].classList.add("ao-form-error"); 2011 hasErrors = true; 2012 } else { 2013 validationDateFields[m].classList.remove("ao-form-error"); 2014 } 2015 break; 2016 } 2017 } 2018 } 2019 break; 2020 case "maxyear": 2021 if(o.isDate) { 2022 var validationDateFields = field.querySelectorAll("input"); 2023 2024 for (var m = 0; m < validationDateFields.length; m++) { 2025 var dateType = validationDateFields[m].getAttribute("placeholder"); 2026 2027 switch(dateType){ 2028 case "YY": 2029 case "YYYY": 2030 if(validationDateFields[m].value > field.getAttribute('data-maxyear') || 0 ){ 2031 validationDateFields[m].classList.add("ao-form-error"); 2032 hasErrors = true; 2033 } else { 2034 validationDateFields[m].classList.remove("ao-form-error"); 2035 } 2036 break; 2037 } 2038 } 2039 } 2040 break; 2041 case "match": 2042 var matchField = document.getElementById(field.getAttribute('data-match')); 2043 if (field && matchField && field.value != matchField.value ) { 2044 hasErrors = true; 2045 } 2046 break; 2047 case "phoneus": 2048 if(field.value && !/^(\+?1?)?(-?\s?\.?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?\s?\.?[2-9]([0-9]{2})\s?-?\.?\d{4}$/.test(field.value)) { 2049 hasErrors = true; 2050 } 2051 break; 2052 case "phoneint": 2053 if(field.value && !/^\+(?:[0-9] ?){6,14}[0-9]$/.test(field.value)) { 2054 hasErrors = true; 2055 } 2056 case "phoneall": 2057 if(field.value && !/(^(\+?1?)?(-?\s?\.?)?(\([2-9]([02-9]\d|1[02-9])\)|[2-9]([02-9]\d|1[02-9]))-?\s?\.?[2-9]([0-9]{2})\s?-?\.?\d{4}$)|(^\+(?:[0-9] ?){6,14}[0-9]$)/.test(field.value)) { 2058 hasErrors = true; 2059 } 2060 break; 2061 case "url": 2062 if(field.value && !/(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/.test(field.value)) { 2063 hasErrors = true; 2064 } 2065 break;
2066 case "custom": 2067 var pattern = new RegExp(field.getAttribute('data-custom')); 2068 2069 if (field.value && !pattern.test(field.value)) { 2070 hasErrors = true; 2071 } 2072 break; 2073 } 2074 2075 if (hasErrors){ 2076 if(! o.isDate) 2077 field.classList.add("ao-form-error"); 2078 2079 field.setAttribute("aria-invalid", "true"); 2080 errorSpan.innerHTML = ( errorMessageArr[validationType] ) ? errorMessageArr[validationType] : "Invalid"; 2081 2082 if(o.isFileUpload){ 2083 field.parentNode.classList.add("ao-file-upload-error"); 2084 field.previousElementSibling.querySelector('label').classList.add("ao-file-upload-error-label"); 2085 } 2086 2087 return false; 2088 } 2089 } 2090 2091 return true; 2092 }; 2093 2094 function debounce(a,b,c){var d;return function(){var e=this,f=arguments;clearTimeout(d),d=setTimeout(function(){d=null,c||a.apply(e,f)},b),c&&!d&&a.apply(e,f)}} 2095 2096 return { 2097 load: function(opts) { 2098 loadForm(opts); 2099 }, 2100 loadAll: function(){ 2101 for(var i=0; i<w._aoForms.length; i++){ 2102 loadForm(w._aoForms[i]); 2103 } 2104 } 2105 }; 2106 2107}(window)); 2108 2109/*** FormData for older browsers ***/ 2110(function(w) { 2111 if (w.FormData) 2112 return; 2113 function FormData() { 2114 this.fake = true; 2115 this.boundary = "--------FormData" + Math.random(); 2116 this._fields = []; 2117 } 2118 FormData.prototype.append = function(key, value) { 2119 this._fields.push([key, value]); 2120 } 2121 FormData.prototype.toString = function() { 2122 var boundary = this.boundary; 2123 var body = ""; 2124 this._fields.forEach(function(field) { 2125 body += "--" + boundary + "\r\n"; 2126 // file upload 2127 if (field[1].name) { 2128 var file = field[1]; 2129 body += "Content-Disposition: form-data; name=\""+ field[0] +"\"; filename=\""+ file.name +"\"\r\n"; 2130 body += "Content-Type: "+ file.type +"\r\n\r\n"; 2131 body += file.getAsBinary() + "\r\n"; 2132 } else { 2133 body += "Content-Disposition: form-data; name=\""+ field[0] +"\";\r\n\r\n"; 2134 body += field[1] + "\r\n"; 2135 } 2136 }); 2137 body += "--" + boundary +"--"; 2138 return body; 2139 } 2140 w.FormData = FormData; 2141})(window);
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.