1if (typeof NMBI === "undefined") { 2 NMBI = {}; 3} 4if (typeof NMBI.Common === "undefined") { 5 NMBI.Common = {}; 6} 7 8(function () { 9 /** 10 * NMBI.Common 11 */ 12 13 "use strict"; 14 15 const _errorMessageLinks = new Map(); 16 17 const _addAllowFutureDateOnlyValidator = function (fieldName, fieldLabel = "", isRequired = false) { 18 19 if (typeof Page_Validators === "undefined") return; 20 21 if (isRequired) { 22 $("#" + fieldName + "_label").parent().addClass("required"); 23 } 24 25 if (fieldLabel === "") { 26 fieldLabel = $("#" + fieldName + "_label").text().trim(); 27 } 28 29 var validatorField = ""; 30 31 var newValidator = document.createElement("span"); 32 newValidator.style.display = "none"; 33 newValidator.id = validatorField = "FutureDateFieldValidator" + fieldName; 34 newValidator.controltovalidate = fieldName; 35 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + fieldLabel + " cannot be in the past.</a>"; 36 newValidator.validationGroup = ""; 37 newValidator.initialvalue = ""; 38 newValidator.evaluationfunction = function () { 39 return _isFutureDate($("#" + fieldName).val()); 40 }; 41 42 // Add the new validator to the page validators array: 43 var validatorExists = Page_Validators.some(function (validator) { 44 return validator.id === validatorField; 45 }); 46 47 if (!validatorExists) { 48 Page_Validators.push(newValidator); 49 } 50 51 // Wire-up the click event handler of the validation summary link 52 $("a[href='#" + fieldName + "_label']").on("click", function () { 53 scrollToAndFocus(fieldName + "_label", fieldName); 54 }); 55 }; 56 57 const _addDropdownLoader = function (fieldName) { 58 // Append loading gif after the dropdown 59 var newLoaderImg = document.createElement("img"); 60 newLoaderImg.id = fieldName + 'dropdownLoader'; 61 newLoaderImg.src = '/DropdownSpinnerBgTrans.gif'; 62 newLoaderImg.alt = "Loading..."; 63 64 // Apply styles 65 newLoaderImg.style.position = "absolute"; 66 newLoaderImg.style.top = "50%"; 67 newLoaderImg.style.left = "50%"; 68 newLoaderImg.style.transform = "translate(-50%, -50%)"; 69 newLoaderImg.style.width = "45px"; 70 newLoaderImg.style.height = "45px"; 71 newLoaderImg.style.display = "none"; 72 newLoaderImg.style.zIndex = "10"; 73 74 // Append to a container (e.g., wrapper of your dropdown) 75 var wrapper = $("#" + fieldName).parent(); 76 77 if (wrapper) { 78 $(wrapper).css("position", "relative").append(newLoaderImg); 79 }; 80 }; 81 82 const _addFieldValidator = function (fieldName, fieldType, fieldLabel = "", isRequired = true) { 83 _addRequiredFieldValidator(fieldName, fieldType, fieldLabel, isRequired); 84 }; 85 86 const _addFieldValidatorCustomErrorMessage = function (fieldName, fieldType, customErrorMessage, isRequired = true) { 87 88 if (typeof Page_Validators === "undefined") return; 89 90 const validatorName = "RequiredFieldValidator" + fieldName; 91 92 const validatorExists = Page_Validators.some(validator => validator.id === validatorName); 93 if (validatorExists) return; 94 95 const labelElementId = fieldName + "_label" 96 97 const labelElement = document.getElementById(labelElementId); 98 99 if (labelElement && isRequired) { 100 if (labelElement.parentElement && !labelElement.parentElement.classList.contains("required")) { 101 labelElement.parentElement.classList.add("required"); 102 } 103 } 104 105 const newValidator = document.createElement("span"); 106 newValidator.style.display = "none"; 107 newValidator.id = validatorName; 108 newValidator.controltovalidate = fieldName; 109 newValidator.errormessage = "<a href='#" + labelElementId + "'>" + customErrorMessage + "</a>"; 110 newValidator.validationGroup = ""; 111 newValidator.initialvalue = ""; 112 newValidator.evaluationfunction = () => _hasValue(fieldName, fieldType); 113 114 Page_Validators.push(newValidator); 115 116 document 117 .querySelectorAll("a[href='#" + labelElementId + "']") 118 .forEach(link => { 119 _errorMessageLinks.set(link, _handleErrorMessageClick); 120 link.addEventListener("click", _handleErrorMessageClick); 121 }); 122 }; 123 124 const _addFieldValidatorCustomFunction = function (fieldName, customErrorMessage, customValidationFn = null, isRequired = false) { 125 126 if (typeof Page_Validators === "undefined") return; 127 128 if (isRequired) { 129 $("#" + fieldName + "_label").parent().addClass("required"); 130 } 131 132 const fieldLabel = $("#" + fieldName + "_label").text().trim(); 133 const validatorId = "CustomFunctionFieldValidator" + fieldName;
134 var validatorField = ""; 135 // Create validator 136 var newValidator = document.createElement("span"); 137 newValidator.style.display = "none"; 138 newValidator.id = validatorField = validatorId; 139 newValidator.controltovalidate = fieldName; 140 newValidator.errormessage = `<a href='#${fieldName}_label'>${customErrorMessage}</a>`; 141 newValidator.validationGroup = ""; 142 newValidator.initialvalue = ""; 143 144 // Custom evaluation logic 145 newValidator.evaluationfunction = function () { 146 let result = null; 147 148 if (typeof customValidationFn === "function") { 149 result = customValidationFn(); 150 } 151 152 if (typeof result === "object") { 153 if (!result.isValid && result.errorMessage) { 154 newValidator.errormessage = `<a href='#${fieldName}_label'>${result.errorMessage}</a>`; 155 } 156 return result.isValid; 157 } 158 159 return !!result; 160 }; 161 162 // Add the new validator to the page validators array: 163 var validatorExists = Page_Validators.some(function (validator) { 164 return validator.id === validatorField; 165 }); 166 167 if (!validatorExists) 168 Page_Validators.push(newValidator); 169 170 // Scroll on error click 171 $(`a[href='#${fieldName}_label']`).on("click", function () { 172 scrollToAndFocus(fieldName + "_label", fieldName); 173 }); 174 }; 175 176 const _addFileTypeValidation = function (fieldName) { 177 178 $("#" + fieldName + "_label").before('<div role="alert" id="' + fieldName + '_picNotification" class="alert alert-danger" style="display: none;"></div>'); 179 var fileUploadControls = [fieldName + "_input_file"]; 180 $.each(fileUploadControls, function (key, ctrl) { 181 $("#" + fieldName + "_input_file").on("change", function () { 182 var files = this.files; 183 var valid = true; 184 var validFileTypes = ["image/jpeg", "image/png", "application/pdf"]; 185 var invalidFileChars = ["%", "&", "$", "£", "!", "@", "~", "#"]; 186 187 $.each(files, function (index, file) { 188 if (!validFileTypes.includes(file.type)) { 189 valid = false; 190 //$('.container-filelink-delete.file-link-and-delete').hide(); 191 let buttonControl = $("#" + fieldName + "_delete_button").closest(".container-filelink-delete.file-link-and-delete"); 192 buttonControl.hide(); 193 $("#" + fieldName + "_delete_button").click(); 194 var message = "This file type is invalid."; 195 $("#" + fieldName + "_picNotification") 196 .html('<i class="fa fa-info-circle"></i> ' + message) 197 .css("color", "gray"); 198 $("#" + fieldName + "_picNotification").fadeIn(); 199 return false; 200 } else { 201 let buttonControl = $("#" + fieldName + "_delete_button").closest(".container-filelink-delete.file-link-and-delete"); 202 buttonControl.show(); 203 $("#" + fieldName + "_picNotification").fadeOut(); 204 205 if (invalidFileChars.some((char) => file.name.includes(char))) { 206 valid = false; 207 $("#" + fieldName + "_delete_button") 208 .closest(".container-filelink-delete.file-link-and-delete") 209 .hide(); 210 var message = "Please rename the file, the file name cannot contain the following characters " + invalidFileChars; 211 $("#" + fieldName + "_picNotification") 212 .html('<i class="fa fa-info-circle"></i> ' + message) 213 .css("color", "gray"); 214 $("#" + fieldName + "_picNotification").fadeIn(); 215 return false; 216 } else { 217 $("#" + fieldName + "_delete_button") 218 .closest(".container-filelink-delete.file-link-and-delete") 219 .show(); 220 } 221 } 222 }); 223 }); 224 }); 225 }; 226 227 const _addFileTypeValidationHTML = function () { 228 for (let i = 0; i < $(".file-control-container").length; ++i) { 229 const newDiv = document.createElement("div"); 230 newDiv.setAttribute("id", "file__" + i); 231 const newContent = document.createTextNode("Allowed file types: pdf, png, jpg, jpeg"); 232 newDiv.appendChild(newContent); 233 newDiv.classList.add("file-Upload-control-description-below"); 234 $(".file-control-container")[i].after(newDiv); 235 } 236 }; 237 238 const _addFileValidator = function (fieldName, fieldLabel) { 239 240 if (fieldLabel === "") 241 fieldLabel = $("#" + fieldName + "_label") 242 .text() 243 .trim(); 244 if (typeof Page_Validators === "undefined") 245 return; 246 // Create new validator 247 $("#" + fieldName + "_label") 248 .parent() 249 .addClass("required"); 250 var newValidator = document.createElement("span"); 251 newValidator.style.display = "none"; 252 newValidator.id = "RequiredFieldValidator" + fieldName; 253 newValidator.controltovalidate = ""; 254 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + fieldLabel + " is a mandatory field.</a>"; 255 newValidator.validationGroup = ""; 256 newValidator.initialvalue = ""; 257 newValidator.evaluationfunction = function () { 258 if ($("#" + fieldName + "_delete_button:visible").length == 0) { 259 return false; 260 } else { 261 return true; 262 } 263 }; 264 // Add the new validator to the page validators array: 265 Page_Validators.push(newValidator); 266 // Wire-up the click event handler of the validation summary link 267 $("a[href='#" + fieldName + "_label']").on("click", function () { 268 scrollToAndFocus(fieldName + "_label", fieldName); 269 }); 270 }; 271 272 const _addFileValidatorCustomErrorMessage = function (fieldName, customErrorMessage) { 273 if (typeof Page_Validators === "undefined") 274 return; 275 // Create new validator 276 $("#" + fieldName + "_label") 277 .parent() 278 .addClass("required"); 279 var newValidator = document.createElement("span"); 280 newValidator.style.display = "none"; 281 newValidator.id = "RequiredFieldValidator" + fieldName; 282 newValidator.controltovalidate = ""; 283 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + customErrorMessage + "</a>"; 284 newValidator.validationGroup = ""; 285 newValidator.initialvalue = ""; 286 newValidator.evaluationfunction = function () { 287 if ($("#" + fieldName + "_delete_button:visible").length == 0) { 288 return false; 289 } else { 290 return true; 291 } 292 }; 293 // Add the new validator to the page validators array: 294 Page_Validators.push(newValidator); 295 // Wire-up the click event handler of the validation summary link 296 $("a[href='#" + fieldName + "_label']").on("click", function () { 297 scrollToAndFocus(fieldName + "_label", fieldName); 298 }); 299 }; 300 301 const _addFutureDateValidator = function (fieldName, fieldLabel = "", isRequired = false) { 302 if (typeof Page_Validators === "undefined") 303 return; 304 305 if (isRequired) { 306 $("#" + fieldName + "_label").parent().addClass("required"); 307 } 308 309 if (fieldLabel === "") { 310 fieldLabel = $("#" + fieldName + "_label").text().trim(); 311 } 312
313 var validatorField = ""; 314 315 var newValidator = document.createElement("span"); 316 newValidator.style.display = "none"; 317 newValidator.id = validatorField = "FutureDateFieldValidator" + fieldName; 318 newValidator.controltovalidate = fieldName; 319 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + fieldLabel + " cannot be in the future.</a>"; 320 newValidator.validationGroup = ""; 321 newValidator.initialvalue = ""; 322 newValidator.evaluationfunction = function () { 323 return !_isFutureDate($("#" + fieldName).val()); 324 }; 325 326 // Add the new validator to the page validators array: 327 var validatorExists = Page_Validators.some(function (validator) { 328 return validator.id === validatorField; 329 }); 330 331 if (!validatorExists) { 332 Page_Validators.push(newValidator); 333 } 334 335 // Wire-up the click event handler of the validation summary link 336 $("a[href='#" + fieldName + "_label']").on("click", function () { 337 scrollToAndFocus(fieldName + "_label", fieldName); 338 }); 339 }; 340 341 const _addFutureDateValidatorCustomErrorMessage = function (fieldName, customErrorMessage, isRequired = true) { 342 if (typeof Page_Validators === "undefined") 343 return; 344 345 if (isRequired) { 346 $("#" + fieldName + "_label").parent().addClass("required"); 347 } 348 349 if (fieldLabel === "") { 350 fieldLabel = $("#" + fieldName + "_label").text().trim(); 351 352 } 353 354 var validatorField = ""; 355 356 var newValidator = document.createElement("span"); 357 newValidator.style.display = "none"; 358 newValidator.id = validatorField = "FutureDateFieldValidator" + fieldName; 359 newValidator.controltovalidate = fieldName; 360 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + customErrorMessage + "</a>"; 361 newValidator.validationGroup = ""; 362 newValidator.initialvalue = ""; 363 newValidator.evaluationfunction = function () { 364 return !_isFutureDate($("#" + fieldName).val()); 365 }; 366 // Add the new validator to the page validators array: 367 var validatorExists = Page_Validators.some(function (validator) { 368 return validator.id === validatorField; 369 }); 370 371 if (!validatorExists) 372 Page_Validators.push(newValidator); 373 // Wire-up the click event handler of the validation summary link 374 $("a[href='#" + fieldName + "_label']").on("click", function () { 375 scrollToAndFocus(fieldName + "_label", fieldName); 376 }); 377 }; 378 379 const _addRadioValidator = function (fieldName, fieldLabel, isRequired = true) { 380 if (typeof Page_Validators === "undefined") 381 return; 382 383 if (isRequired) { 384 $("#" + fieldName + "_label").parent().addClass("required"); 385 } 386 387 if (fieldLabel === "") { 388 fieldLabel = $("#" + fieldName + "_label").text().trim(); 389 } 390 391 var validatorField = ""; 392 393 var newValidator = document.createElement("span"); 394 newValidator.style.display = "none"; 395 newValidator.id = "RequiredFieldValidator" + fieldName; 396 newValidator.controltovalidate = ""; 397 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + fieldLabel + " is a mandatory field.</a>"; 398 newValidator.validationGroup = ""; 399 newValidator.initialvalue = ""; 400 newValidator.evaluationfunction = function () { 401 var fieldVal = $("#" + fieldName) 402 .find("input[type='radio']:checked") 403 .val(); 404 if (fieldVal == undefined) { 405 return false; 406 } else { 407 return true; 408 } 409 }; 410 411 // Add the new validator to the page validators array: 412 var validatorExists = Page_Validators.some(function (validator) { 413 return validator.id === validatorField; 414 }); 415 416 if (!validatorExists) 417 Page_Validators.push(newValidator); 418 419 // Wire-up the click event handler of the validation summary link 420 $("a[href='#" + fieldName + "_label']").on("click", function () { 421 scrollToAndFocus(fieldName + "_label", fieldName); 422 }); 423 }; 424 425 const _addRadioValidatorCustomErrorMessage = function (fieldName, customErrorMessage, isRequired = true) { 426 if (typeof Page_Validators === "undefined") 427 return; 428 429 if (isRequired) { 430 $("#" + fieldName + "_label").parent().addClass("required"); 431 } 432 433 if (fieldLabel === "") { 434 fieldLabel = $("#" + fieldName + "_label").text().trim(); 435 } 436
437 var validatorField = ""; 438 439 var newValidator = document.createElement("span"); 440 newValidator.style.display = "none"; 441 newValidator.id = "RequiredFieldValidator" + fieldName; 442 newValidator.controltovalidate = ""; 443 newValidator.errormessage = "<a href='#" + fieldName + "_label'>" + customErrorMessage + "</a>"; 444 newValidator.validationGroup = ""; 445 newValidator.initialvalue = ""; 446 newValidator.evaluationfunction = function () { 447 var fieldVal = $("#" + fieldName) 448 .find("input[type='radio']:checked") 449 .val(); 450 if (fieldVal == undefined) { 451 return false; 452 } else { 453 return true; 454 } 455 }; 456 457 // Add the new validator to the page validators array: 458 var validatorExists = Page_Validators.some(function (validator) { 459 return validator.id === validatorField; 460 }); 461 462 if (!validatorExists) 463 Page_Validators.push(newValidator); 464 465 // Wire-up the click event handler of the validation summary link 466 $("a[href='#" + fieldName + "_label']").on("click", function () { 467 scrollToAndFocus(fieldName + "_label", fieldName); 468 }); 469 }; 470 471 const _addRequiredFieldValidator = function (fieldName, fieldType, fieldLabel = "", isRequired = true) { 472 473 if (typeof Page_Validators === "undefined") return; 474 475 const validatorName = "RequiredFieldValidator" + fieldName; 476 477 const validatorExists = Page_Validators.some(validator => validator.id === validatorName); 478 if (validatorExists) return; 479 480 const labelElementId = fieldName + "_label" 481 482 const labelElement = document.getElementById(labelElementId); 483 484 if (labelElement && isRequired) { 485 fieldLabel = labelElement.textContent.trim(); 486 if (labelElement.parentElement && !labelElement.parentElement.classList.contains("required")) { 487 labelElement.parentElement.classList.add("required"); 488 } 489 } 490 491 const newValidator = document.createElement("span"); 492 newValidator.style.display = "none"; 493 newValidator.id = validatorName; 494 newValidator.controltovalidate = fieldName; 495 newValidator.errormessage = "<a href='#" + labelElementId + "'>" + fieldLabel + " is a mandatory field.</a>"; 496 newValidator.validationGroup = ""; 497 newValidator.initialvalue = ""; 498 newValidator.evaluationfunction = () => _hasValue(fieldName, fieldType); 499 500 Page_Validators.push(newValidator); 501 502 document 503 .querySelectorAll("a[href='#" + labelElementId + "']") 504 .forEach(link => { 505 _errorMessageLinks.set(link, _handleErrorMessageClick); 506 link.addEventListener("click", _handleErrorMessageClick); 507 }); 508 509 /// console.log(`rfv added: ${fieldName}`); 510 }; 511 512 const _addStartDateAfterEndDateValidator = function (startDateFieldName, endDateFieldName) { 513 try { 514 if (typeof Page_Validators === undefined) 515 return; 516 517 const validatorName = "StartDateAfterEndDateValidator" + startDateFieldName + endDateFieldName; 518 const validatorExists = Page_Validators.some( 519 validator => validator.id === validatorName); 520 521 if (validatorExists) 522 return; 523 if (document.querySelector('#' + startDateFieldName) === null) 524 return; 525 if (document.querySelector('#' + endDateFieldName) === null) 526 return; 527 528 const newValidator = document.createElement('span'); 529 newValidator.style.display = "none"; 530 newValidator.id = validatorName; 531 newValidator.controltovalidate = endDateFieldName; 532 newValidator.errormessage = "<a href='#" + endDateFieldName + "_label'>'" 533 + $("#" + endDateFieldName + "_label").text().trim() + "' cannot be before '" 534 + $("#" + startDateFieldName + "_label").text().trim() + "'.</a>"; 535 newValidator.initialvalue = ""; 536 newValidator.evaluationfunction = function () { 537 const startDate = $('#' + startDateFieldName).val(); 538 const endDate = $('#' + endDateFieldName).val(); 539 540 // Don't validate if either date is empty 541 if (!startDate || !endDate) { 542 return true; // Skip validation â assume valid if one is empty 543 } 544 545 if (!isNaN(Date.parse(startDate)) && !isNaN(Date.parse(endDate))) { 546 const start = new Date(startDate); 547 const end = new Date(endDate); 548 return end > start; 549 } 550 return false; 551 }; 552 553 Page_Validators.push(newValidator); 554 555 } catch (e) { 556 throw e; 557 } 558 }; 559
560 const _callExportpage = async function (datasetName, otherUrlParams, filename) { 561 // Call Export template to generate CSV file. 562 const exportUrl = `/export/?dataset=${datasetName}&${otherUrlParams}`; 563 564 try { 565 const response = await fetch(exportUrl); 566 let text = await response.text(); 567 568 if (!text || text.trim().length < 10) { 569 alert("No data available for this " + otherUrlParams + "!"); 570 return; 571 } 572 573 //Convert literal "\n" into real line breaks 574 text = text.replace(/\\n/g, "\n"); 575 576 const lines = text.split("\n").map(line => line.replace(/[^\x20-\x7E]/g, "").trim()); 577 const filteredLines = lines.filter(line => line && !line.startsWith("£ Len")); 578 const finalCsv = filteredLines.join("\n"); 579 580 // Trigger CSV download 581 const blob = new Blob([finalCsv], { 582 type: "text/csv;charset=utf-8;" 583 }); 584 const link = document.createElement("a"); 585 link.href = URL.createObjectURL(blob); 586 link.download = fileName; 587 document.body.appendChild(link); 588 link.click(); 589 document.body.removeChild(link); 590 URL.revokeObjectURL(link.href); 591 592 } catch (err) { 593 console.error("Error exporting CSV:", err); 594 alert("Failed to fetch or export data."); 595 } 596 }; 597 598 const _cleanFetchForWebAPI = function (fetchXml) { 599 return fetchXml.replace(/\r?\n|\r/g, "").replace(/>\s+</g, '><'); 600 }; 601 602 const _clearFieldErrorMessage = function (fieldName, validatorId) { 603 const errorSpan = document.getElementById(validatorId); 604 if (errorSpan) { 605 errorSpan.style.display = "none"; 606 } 607 608 // 2ï¸Remove validator from Page_Validators (so it doesnât reappear) 609 if (typeof Page_Validators !== "undefined" && Array.isArray(Page_Validators)) { 610 const index = Page_Validators.findIndex(v => v.id === validatorId); 611 if (index > -1) { 612 Page_Validators.splice(index, 1); 613 } 614 } 615 616 // 3Remove the corresponding <li> in the ValidationSummary div 617 const summaryDiv = document.getElementById("ValidationSummaryEntityFormView"); 618 if (summaryDiv) { 619 const links = summaryDiv.querySelectorAll(`a[href='#${fieldName}_label']`); 620 links.forEach(link => { 621 const li = link.closest("li"); 622 if (li) 623 li.remove(); 624 }); 625 626 // Hide the whole summary if itâs empty now 627 const remaining = summaryDiv.querySelectorAll("li"); 628 if (remaining.length === 0) { 629 summaryDiv.style.display = "none"; 630 } 631 } 632 }; 633 634 const _clearFileUpload = function (fieldName) { 635 $("#" + fieldName + "_delete_button").prop("checked", true).trigger("click"); 636 }; 637 638 const _decodeHtml = function (str) { 639 return new DOMParser().parseFromString(str, "text/html").documentElement.textContent; 640 }; 641 642 const _decodeObject = function (obj) { 643 if (typeof obj !== "object" || obj === null) { 644 return typeof obj === "string" ? _decodeHtml(obj) : obj; 645 } 646 if (Array.isArray(obj)) { 647 return obj.map((item) => _decodeObject(item)); 648 } 649 const decodedObj = {}; 650 for (const key in obj) { 651 if (obj.hasOwnProperty(key)) { 652 decodedObj[key] = _decodeObject(obj[key]); 653 } 654 } 655 return decodedObj; 656 }; 657 658 const _getUrlParameters = function (url) { 659 const params = {}; 660 const queryString = url ? new URL(url).search : window.location.search; 661 const searchParams = new URLSearchParams(queryString); 662 for (const [key, value] of searchParams.entries()) { 663 params[key] = value; 664 } 665 return params; 666 }; 667 668 const _getParamFromQueryStringUrl = function (paramName) { 669 const urlParams = new URLSearchParams(window.location.search); 670 return urlParams.get(paramName); // Change "accountid" to match your query parameter 671 }; 672 673 const _handleErrorMessageClick = function (e) { 674 e.preventDefault(); 675 const fieldName = e.currentTarget.getAttribute("href").replace("#", "").replace("_label", ""); 676 scrollToAndFocus(fieldName + "_label", fieldName); 677 }; 678 679 const _hasValue = function (fieldName, fieldType) { 680 681 const $field = $("#" + fieldName); 682 683 switch (fieldType) { 684 case "radio": 685 return $field.find("input[type='radio']:checked").length > 0; 686 687 case "file": 688 return $("#" + fieldName + "_delete_button:visible").length > 0;
689 690 case "futuredate": { 691 const value = $field.val(); 692 return value ? !_isFutureDate(value) : false; 693 } 694 695 case "date": { 696 const value = $field.val(); 697 const datePickerValue = $("#" + fieldName + "_datepicker_description").val(); 698 return !!(value || datePickerValue); 699 } 700 701 default: 702 return !!$field.val(); 703 } 704 }; 705 706 const _initialisePage = function () { 707 708 try { 709 const amendChildUrl = function () { 710 Array.from(document.querySelectorAll('a.details-link')).filter(a => !a.href.includes("returnid=")).forEach( 711 function (item, index) { 712 item.href += item.href.includes("?") ? "&returnid=" + NMBI.LiquidData.Portal.entityId : "?returnid=" + NMBI.LiquidData.Portal.entityId; 713 }); 714 Array.from(document.querySelectorAll('a.btn')).filter(a => !a.href.includes("returnid=")).forEach( 715 function (item, index) { 716 item.href += item.href.includes("?") ? "&returnid=" + NMBI.LiquidData.Portal.entityId : "?returnid=" + NMBI.LiquidData.Portal.entityId; 717 }); 718 } 719 720 const subGridObserver = new MutationObserver((mutationsList) => { 721 for (const mutation of mutationsList) { 722 if (mutation.type === 'childList') { 723 amendChildUrl(); 724 } 725 } 726 }); 727 728 document.querySelectorAll('.entity-grid, .subgrid').forEach((subGrid) => { 729 subGridObserver.observe(subGrid, { 730 childList: true, 731 subtree: true 732 }); 733 amendChildUrl(); 734 }); 735 } 736 catch (e) { 737 throw e; 738 } 739 }; 740 741 const _hideProgressBarLoader = function (intervalId) { 742 clearInterval(intervalId); 743 $('#progress-bar-fill').css('width', '100%').attr('aria-valuenow', 100).text('Complete'); 744 setTimeout(() => $('#custom-loader').fadeOut(), 500); 745 }; 746 747 /*const _toDataverseUtc = function (dateString) { 748 if (!dateString) 749 return ""; 750 751 // Expecting dd/MM/yyyy or MM/dd/yyyy from portal 752 var parts = dateString.split(/[\/\-]/); 753 754 // Adjust order if your portal uses dd/MM/yyyy 755 var day = parts[0]; 756 var month = parts[1] - 1; 757 var year = parts[2]; 758 759 var date = new Date(Date.UTC(year, month, day, 0, 0, 0)); 760 761 return date.toISOString().replace("000Z", "0000000Z"); 762 };*/ 763 764 const _isFutureDate = function (inputDate) { 765 const today = new Date(); 766 today.setHours(0, 0, 0, 0); // Remove time part for accurate comparison 767 const selectedDate = new Date(inputDate); 768 return selectedDate > today; // Returns true if the date is in the future 769 }; 770 771 const _makeApiCall = function (apiUrl, callType, isAsync, successCallback, errorCallback) { 772 return webapi.safeAjax({ 773 url: apiUrl, 774 type: callType, 775 async: isAsync, 776 headers: { 777 "OData-MaxVersion": "4.0", 778 "OData-Version": "4.0", 779 "Accept": "application/json", 780 "Content-Type": "application/json; charset=utf-8", 781 "Prefer": "odata.include-annotations=\"*\"", 782 "Cache-Control": "no-cache, no-store, must-revalidate", 783 "Pragma": "no-cache", 784 "Expires": "0" 785 }, 786 success: successCallback, 787 error: errorCallback 788 }); 789 }; 790 791 const _makeFormReadOnly = function () { 792 try { 793 document.querySelectorAll("form input, form textarea, form select").forEach((node) => { 794 if (node.type === "text" || node.type === "password" || node.type === "number" || node.type === "email" || node.type === "date") { 795 node.readOnly = true; 796 } else if (node.type === "select-one") { 797 let elm = document.createElement("input"); 798 elm.className = "form-control input-text-box"; 799 elm.id = node.id + "_readonly"; 800 elm.readOnly = true; 801 elm.type = "text"; 802 elm.value = node.options[node.selectedIndex].innerText; 803 node.parentNode.appendChild(elm); 804 node.style.display = "none"; 805 } else if (node.type === "checkbox") { 806 let elm = document.createElement("input"); 807 elm.checked = node.checked; 808 elm.className = node.className; 809 elm.disabled = true; 810 elm.id = node.id + "_readonly"; 811 elm.readOnly = true; 812 elm.type = "checkbox"; 813 node.parentNode.appendChild(elm); 814 node.style.display = "none"; 815 } else if (node.type === "radio" || node.type === "file") {
816 //TO-DO: NOT IMPLEMENTED 817 } 818 }); 819 820 document.querySelectorAll("button").forEach((node) => { 821 node.disabled = true; 822 }); 823 824 } catch (e) { 825 throw e; 826 } 827 }; 828 829 const _removeAnyValidator = function (fieldName) { 830 _removeRequiredFieldValidator(fieldName); 831 }; 832 833 const _removeRequiredFieldValidator = function (fieldName) { 834 835 if (typeof Page_Validators === "undefined") return; 836 837 const validatorName = "RequiredFieldValidator" + fieldName; 838 839 Page_Validators = Page_Validators.filter(v => v.id !== validatorName); 840 841 const labelElement = document.getElementById(fieldName + "_label"); 842 if (labelElement && labelElement.parentElement && labelElement.parentElement.classList.contains("required")) { 843 labelElement.parentElement.classList.remove("required"); 844 } 845 846 document 847 .querySelectorAll("a[href='#" + fieldName + "_label']") 848 .forEach(link => { 849 const handler = _errorMessageLinks.get(link); 850 if (handler) { 851 link.removeEventListener("click", handler); 852 _errorMessageLinks.delete(link); 853 } 854 }); 855 856 /// console.log(`rfv removed: ${fieldName}`); 857 }; 858 859 const _removeStartDateAfterEndDateValidator = function (startDateFieldName, endDateFieldName) { 860 861 if (typeof Page_Validators === "undefined") return; 862 863 const validatorName = "StartDateAfterEndDateValidator" + startDateFieldName + endDateFieldName; 864 865 Page_Validators = Page_Validators.filter(v => v.id !== validatorName); 866 867 const labelElement = document.getElementById(endDateFieldName + "_label"); 868 if (labelElement && labelElement.parentElement && labelElement.parentElement.classList.contains("required")) { 869 labelElement.parentElement.classList.remove("required"); 870 } 871 872 document 873 .querySelectorAll("a[href='#" + endDateFieldName + "_label']") 874 .forEach(link => { 875 const handler = _errorMessageLinks.get(link); 876 if (handler) { 877 link.removeEventListener("click", handler); 878 _errorMessageLinks.delete(link); 879 } 880 }); 881 }; 882 883 const _removeValidator = function (fieldName) { 884 _removeRequiredFieldValidator(fieldName); 885 }; 886 887 const _renderField = function (isVisible, fieldName, fieldType, isRequired = true, raiseEvent = true) { 888 _setResetHideShowField(isVisible, fieldName, fieldType, isRequired, raiseEvent); 889 }; 890 891 const _replaceLoopupOptions = function (fieldName, arrayValueText, removeFirst = true) { 892 // Clear all options except the first one 893 if (removeFirst) 894 $("#" + fieldName + " option:not(:first)").remove(); 895 // Loop through each record and add as an option 896 $.each(arrayValueText, function (index, record) { 897 $("#" + fieldName).append( 898 $("<option>", { 899 value: record[0], // ID 900 text: record[1], // Name 901 class: "dropdown-item", 902 })); 903 }); 904 }; 905 906 const _resetCheckbox = function (fieldName) { 907 $("#" + fieldName).prop("checked", false); 908 }; 909 910 const _resetDatePickerControl = function (fieldName) { 911 $("#" + fieldName + "_datepicker_description").val(null); 912 $("#" + fieldName) 913 .datepicker() 914 .val(""); 915 $("#" + fieldName).val(""); 916 }; 917 918 const _resetFileControl = function (fieldName) { 919 $("#" + fieldName + "_delete_button").click(); 920 $("#" + fieldName).val(""); 921 $("#" + fieldName + "_picNotification").hide(); 922 $("#" + fieldName + "_label").hide(); 923 }; 924 925 const _resetLookupField = function (fieldName) { 926 $("#" + fieldName).val(""); 927 }; 928 929 const _resetRadioButton = function (fieldName) { 930 $("#" + fieldName) 931 .find("input[type='radio']:checked") 932 .prop("checked", false); 933 }; 934 935 const _resetTextField = function (fieldName) { 936 $("#" + fieldName).val(""); 937 }; 938 939 const _setFieldRequired = function (fieldName, fieldType, fieldLabel = "") { 940 _addFieldValidator(fieldName, fieldType, fieldLabel); 941 }; 942 943 const _setFieldVisibility = function (fieldName, visible) { 944 const el = document.getElementById(fieldName); 945 if (el) { 946 const tr = el.closest("tr"); 947 if (tr) { 948 tr.style.display = visible ? "" : "none"; 949 } 950 } 951 }; 952 953 const _setRadioFieldRequired = function (fieldName, isRequired, fieldLabel = "") { 954 if (isRequired) { 955 _addRadioValidator(fieldName, fieldLabel); 956 } else { 957 _removeAnyValidator(fieldName); 958 } 959 }; 960 961 const _setRadioValue = function (fieldName, strVal) { 962 if (strVal === "0") { 963 $("#" + fieldName + " #" + fieldName + "_1").prop("checked", true); 964 } else if (strVal === "1") { 965 $("#" + fieldName + " #" + fieldName + "_0").prop("checked", true); 966 } 967 }; 968 969 const _setResetHideShowField = function (isVisible, fieldName, fieldType, isRequired = true, raiseEvent = false) { 970 _setFieldVisibility(fieldName, isVisible); 971 if (fieldType == "file") { 972 _setFieldVisibility(fieldName + "_label", isVisible); 973 } 974 else { 975 _setFieldVisibility(fieldName, isVisible); 976 } 977 978 if (isVisible) { 979 if (isRequired) { 980 _setFieldRequired(fieldName, fieldType); 981 } 982 else { 983 _removeRequiredFieldValidator(fieldName); 984 } 985 $(`#${fieldName}_label`).show(); 986 } 987 else { 988 switch (fieldType) { 989 case "radio": 990 _resetRadioButton(fieldName); 991 break; 992 case "file": 993 _resetFileControl(fieldName); 994 break; 995 case "date": 996 _resetDatePickerControl(fieldName); 997 break; 998 case "checkbox": 999 _resetCheckbox(fieldName); 1000 break; 1001 default: 1002 _resetLookupField(fieldName); 1003 } 1004 /// console.log(`reset: ${fieldName}`); 1005 if (raiseEvent) $(`#${fieldName}`).trigger("change"); 1006 _removeAnyValidator(fieldName); 1007 } 1008 }; 1009 1010 const _setReturnIdHref = function (entityId) { 1011 setTimeout( 1012 function () { 1013 Array.from(document.querySelectorAll('a.details-link')).filter(a =>
1013 !a.href.includes("returnid=")).forEach( 1014 function (item, index) { 1015 item.href += item.href.includes("?") ? "&returnid=" + entityId : "?returnid=" + entityId; 1016 }); 1017 Array.from(document.querySelectorAll('a.btn')).filter(a => !a.href.includes("returnid=")).forEach( 1018 function (item, index) { 1019 item.href += item.href.includes("?") ? "&returnid=" + entityId : "?returnid=" + entityId; 1020 }); 1021 }, 1000); 1022 }; 1023 1024 const _setEvidenceFields = function (fileUploadControlNames, fileUploadHiddenInputName) { 1025 1026 NMBI.Common.AddFieldValidator(fileUploadHiddenInputName, "text", ""); 1027 1028 let hiddenInput = document.getElementById(fileUploadHiddenInputName); 1029 let selectedKeys = []; 1030 1031 // Read any preselected values first 1032 if (hiddenInput && hiddenInput.value) { 1033 try { 1034 const values = JSON.parse(hiddenInput.value); 1035 selectedKeys = values.map(item => item.Value.toString()); 1036 } catch (e) { 1037 console.error("Error parsing initial JSON:", e); 1038 } 1039 } 1040 1041 // Only hide fields that are *not already selected* 1042 for (const key in fileUploadControlNames) { 1043 const fieldName = fileUploadControlNames[key]; 1044 if (!selectedKeys.includes(key)) { 1045 NMBI.Common.SetResetHideShowField(false, fieldName, "file"); 1046 } 1047 NMBI.Common.AddFileTypeValidation(fieldName); 1048 } 1049 1050 NMBI.Common.AddFileTypeValidationHTML(); 1051 1052 hiddenInput = document.getElementById(fileUploadHiddenInputName); 1053 const observer = new MutationObserver((mutations) => { 1054 mutations.forEach((mutation) => { 1055 if (mutation.type === "attributes" && mutation.attributeName === "value") { 1056 let selectedKeys = []; 1057 1058 // Read any preselected values first 1059 if (hiddenInput && hiddenInput.value) { 1060 try { 1061 const values = JSON.parse(hiddenInput.value); 1062 selectedKeys = values.map(item => item.Value.toString()); 1063 } catch (e) { 1064 console.error("Error parsing initial JSON:", e); 1065 } 1066 } 1067 //Hideallevidenceboxes 1068 for (const key in fileUploadControlNames) { 1069 const fieldName = fileUploadControlNames[key]; 1070 if (!selectedKeys.includes(key)) { 1071 NMBI.Common.SetResetHideShowField(false, fieldName, "file"); 1072 } 1073 } 1074 if (hiddenInput.value) { 1075 try { 1076 const values = JSON.parse(hiddenInput.value); 1077 const keyPairArray = values.map((item) => ({ 1078 key: item.Value, 1079 value: item.Label.UserLocalizedLabel.Label, 1080 })); 1081 1082 keyPairArray.forEach((pair) => { 1083 if (fileUploadControlNames[pair.key]) { 1084 NMBI.Common.SetResetHideShowField(true, fileUploadControlNames[pair.key], "file"); 1085 } 1086 }); 1087 } catch (error) { 1088 console.error("Error parsing JSON:", error); 1089 } 1090 } else { 1091 console.log("No data"); 1092 } 1093 } 1094 }); 1095 }); 1096 1097 observer.observe(hiddenInput, { 1098 attributes: true, 1099 }); 1100 }; 1101 1102 const _showProgressBarLoader = function () { 1103 $('#custom-loader').show(); 1104 let progress = 0; 1105 let interval = setInterval(function () { 1106 if (progress < 90) { 1107 progress += 10; 1108 $('#progress-bar-fill').css('width', progress + '%').attr('aria-valuenow', progress); 1109 } else { 1110 clearInterval(interval); 1111 } 1112 }, 300); 1113 return interval; 1114 }; 1115 1116 const _showHideClosestFieldset = function (show, fieldName) { 1117 if (show) { 1118 $("#" + fieldName).closest("fieldset").show(); 1119 } else { 1120 $("#" + fieldName).closest("fieldset").hide(); 1121 } 1122 }; 1123 1124 const _showFieldErrorMessage = function (fieldName, validatorId, message) { 1125 const errorSpan = document.getElementById(validatorId); 1126 if (errorSpan) { 1127 errorSpan.style.display = "inline"; 1128 errorSpan.innerHTML = `<a href='#${fieldName}_label'>${message}</a>`; 1129 } 1130 1131 // Add to Validation Summary (if exists) 1132 const summaryDiv = document.getElementById("ValidationSummaryEntityFormView"); 1133 if (summaryDiv) { 1134 const ul = summaryDiv.querySelector("ul"); 1135 if (ul && !ul.querySelector(`a[href='#${fieldName}_label']`)) { 1136 const li = document.createElement("li"); 1137 li.innerHTML = `<a href='#${fieldName}_label' 1138 onclick="javascript:scrollToAndFocus('${fieldName}_label','${fieldName}');return false;"> 1139 ${message}</a>`; 1140 ul.appendChild(li); 1141 summaryDiv.style.display = ""; 1142 } 1143 } 1144 }; 1145 1146 const _toggleFieldVisibility = function (yesNoField, targetField) { 1147 var yesNoValue = $("#" + yesNoField) 1148 .find("input[type='radio']:checked") 1149 .val(); 1150 if (yesNoValue === "0" || yesNoValue === undefined) { 1151 // If "No" (assuming "0" means No) 1152 $("#" + targetField) 1153 .parent() 1154 .parent() 1155 .parent() 1156 .hide(); // Hide field 1157 } else { 1158 // If "Yes" (assuming "1" means Yes) 1159 $("#" + targetField) 1160 .parent() 1161 .parent() 1162 .parent() 1163 .show(); // Show field 1164 } 1165 }; 1166 1167 const _webaApiCall = async function (apiUrl) { 1168 try { 1169 const [path, queryString] = apiUrl.split("?"); 1170 let encodedURI = path; 1171 1172 if (queryString) { 1173 const encodedQuery = queryString 1174 .split("&") 1175 .map(param => { 1176 const [key, ...valueParts] = param.split("="); 1177 const value = valueParts.join("="); // Handles '=' inside values 1178 return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; 1179 }) 1180 .join("&"); 1181 1182 encodedURI += "?" + encodedQuery; 1183 } 1184 1185 const results = await webapi.safeAjax({ 1186 url: apiUrl, 1187 type: "GET", 1188 async: true, 1189 headers: { 1190 "OData-MaxVersion": "4.0", 1191 "OData-Version": "4.0", 1192 Accept: "application/json", 1193 "Content-Type": "application/json; charset=utf-8", 1194 Prefer: 'odata.include-annotations="*"', 1195 }, 1196 }); 1197 1198 return results; 1199 } catch (error) { 1200 console.log(apiUrl);
1201 console.error("API error:", error); 1202 return null; 1203 } 1204 }; 1205 1206 /** 1207 * Public members 1208 */ 1209 this.AddAllowFutureDateOnlyValidator = _addAllowFutureDateOnlyValidator; 1210 this.AddDropdownLoader = _addDropdownLoader; 1211 this.AddFieldValidator = _addFieldValidator; 1212 this.AddFieldValidatorCustomErrorMessage = _addFieldValidatorCustomErrorMessage; 1213 this.AddFieldValidatorCustomFunction = _addFieldValidatorCustomFunction; 1214 this.AddFileTypeValidation = _addFileTypeValidation; 1215 this.AddFileTypeValidationHTML = _addFileTypeValidationHTML; 1216 this.AddFileValidator = _addFileValidator; 1217 this.AddFileValidatorCustomErrorMessage = _addFileValidatorCustomErrorMessage; 1218 this.AddFutureDateValidator = _addFutureDateValidator; 1219 this.AddFutureDateValidatorCustomErrorMessage = _addFutureDateValidatorCustomErrorMessage; 1220 this.AddRadioValidator = _addRadioValidator; 1221 this.AddRadioValidatorCustomErrorMessage = _addRadioValidatorCustomErrorMessage; 1222 this.AddRequiredFieldValidator = _addRequiredFieldValidator; 1223 this.AddStartDateAfterEndDateValidator = _addStartDateAfterEndDateValidator; 1224 this.CallExportpage = _callExportpage; 1225 this.CleanFetchForWebAPI = _cleanFetchForWebAPI; 1226 this.ClearFieldErrorMessage = _clearFieldErrorMessage; 1227 this.ClearFileUpload = _clearFileUpload; 1228 this.DecodeHtml = _decodeHtml; 1229 this.DecodeObject = _decodeObject; 1230 this.GetParamFromQueryStringUrl = _getParamFromQueryStringUrl; 1231 this.GetUrlParameters = _getUrlParameters; 1232 this.HasValue = _hasValue; 1233 this.HideProgressBarLoader = _hideProgressBarLoader; 1234 this.InitialisePage = _initialisePage; 1235 this.MakeApiCall = _makeApiCall; 1236 this.MakeFormReadOnly = _makeFormReadOnly; 1237 this.RemoveAnyValidator = _removeAnyValidator; 1238 this.RemoveRequiredFieldValidator = _removeRequiredFieldValidator; 1239 this.RemoveStartDateAfterEndDateValidator = _removeStartDateAfterEndDateValidator; 1240 this.RemoveValidator = _removeValidator; 1241 this.RenderField = _renderField; 1242 this.ReplaceLoopupOptions = _replaceLoopupOptions; 1243 this.ResetCheckbox = _resetCheckbox; 1244 this.ResetDatePickerControl = _resetDatePickerControl; 1245 this.ResetFileControl = _resetFileControl; 1246 this.ResetLookupField = _resetLookupField; 1247 this.ResetRadioButton = _resetRadioButton; 1248 this.ResetTextField = _resetTextField; 1249 this.SetEvidenceFields = _setEvidenceFields; 1250 this.SetFieldRequired = _setFieldRequired; 1251 this.SetFieldVisibility = _setFieldVisibility; 1252 this.SetRadioFieldRequired = _setRadioFieldRequired; 1253 this.SetRadioValue = _setRadioValue; 1254 this.SetResetHideShowField = _setResetHideShowField; 1255 this.SetReturnIdHref = _setReturnIdHref; 1256 this.ShowFieldErrorMessage = _showFieldErrorMessage; 1257 this.ShowHideClosestFieldset = _showHideClosestFieldset; 1258 this.ShowProgressBarLoader = _showProgressBarLoader; 1259 this.ToggleFieldVisibility = _toggleFieldVisibility; 1260 this.WebaApiCall = _webaApiCall; 1261 1262}).apply(NMBI.Common);
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.