1/* Utility functions */ 2var util = { 3 navigate: function(href) { 4 document.location = href; 5 return true; 6 }, 7 8 navigateEncoded: function(href) { 9 document.location = encodeURI(href); 10 return true; 11 }, 12 13 print: function() { 14 window.print(); 15 return false; 16 } 17} 18 19function browserSupportFormValidation() { 20 return Modernizr.formvalidation; 21} 22 23function gotoLink (linkValue) { 24 document.location=linkValue; 25 return true; 26} 27 28function scrollToDivContainer(divContainer) { 29 $('html,body').animate({scrollTop: divContainer.offset().top},'slow'); 30} 31 32/* Form functions */ 33function checkModifiedStatus(form) { 34 if (!form) { 35 return $("form").data('changed'); 36 } 37 else { 38 return $(form).data('changed'); 39 } 40} 41 42function showCancelConfirmationChangeContentAndButtonLabel(cancellationMessageDivId, method, form, okButtonLabel, cancelButtonLabel) { 43 $("#buttonConfirmationPromptOK").html(okButtonLabel); 44 $("#buttonConfirmationPromptCancel").html(cancelButtonLabel); 45 var cancellationMessage = $("#"+cancellationMessageDivId).html(); 46 return showCancelConfirmation(cancellationMessage, method, form); 47} 48 49function parentSiteNaviation(navigationType, defaultUrl) { 50 if (typeof parentSite !== 'undefined' && parentSite !== 'null' && parentSite.length > 0) { 51 var parentSiteLink = $("a[data-destiny-parent-site='" + parentSite + "'][data-destiny-parent-site-link-type='" + navigationType + "']"); 52 if (parentSiteLink.length > 0) { 53 $("#confirmationMessagePromptWrapper").html(parentSiteLink.first().html()); 54 $("#buttonConfirmationPromptOK").on("click",function() { 55 util.navigate(parentSiteLink.first().attr('href')); 56 }); 57 $("#confirmPromptModal").modal("show"); 58 return false; 59 } 60 } 61 62 util.navigate(defaultUrl); 63 return false; 64} 65 66function showCancelConfirmation(cancellationMessage, method, form) { 67 $("#confirmationMessagePromptWrapper").html(cancellationMessage); 68 $("#buttonConfirmationPromptOK").on("click",function() { 69 submitMethodSkipValidation(method, form); 70 }); 71 $("#confirmPromptModal").modal("show"); 72} 73 74function submitMethodSkipValidationWithVariable(method, form, inputId, inputValue) { 75 if (!form) { 76 form = $("#"+inputId).closest("form").get(0); 77 } 78 $("#"+inputId).val(inputValue); 79 return submitMethodSkipValidation(method, form); 80} 81 82function submitMethodSkipValidation(method, form) { 83 var formJQ = $(form); 84 formJQ.find("input[name=method]").val(method); 85 clearFileInputs(form);//need to clear non uploaded files to avoid struts errors 86 87 formJQ.trigger("submit"); 88 return false; 89} 90 91function submitMethodWithVariable(buttonId, method, formElement, inputId, inputValue) { 92 $("#"+inputId).val(inputValue); 93 return submitMethod(buttonId, method, formElement); 94} 95 96function submitMethodWithNamedVariable(buttonId, method, formElement, name, nameValue) { 97 $("input[name=" + name +"]").val(nameValue); 98 return submitMethod(buttonId, method, formElement); 99} 100 101function doPrint(method, form) { 102 form.target = "_blank"; 103 104 var formJQ = $(form); 105 formJQ.find("input[name=method]").val(method); 106 formJQ.trigger("submit"); 107 108 form.target = "_self"; 109 return false; 110} 111 112/** 113 * Calls submitMethod with the specified arguments. If this calls returns true the method invokes the submit method 114 * on the specified formElement, otherwise it returns the return value. 115 * 116 * Buttons on forms with an onsubmit="return false" should call this method instead of submitMethod to allow the form 117 * to be submitted correctly. Forms which include a text field with an onchange event making an ajax call need to use 118 * this approach to prevent a bug in iOS/Mac OS Safari 119 * 120 * @param buttonId 121 * @param method 122 * @param formElement 123 */ 124function submitMethodJS(buttonId, method, formElement) { 125 var retVal = submitMethod(buttonId, method, formElement); 126 if (retVal) { 127 formElement.submit(); 128 } 129 return retVal; 130} 131 132function submitMethod(buttonId, method, formElement) { 133 var button; 134 if (buttonId == null || buttonId.trim() == "") { 135 var trgt = this.event.target; 136 if (trgt != null && trgt.tagName == "BUTTON") { 137 button = $(trgt); 138 } 139 } else { 140 button = $("#" + buttonId); 141 } 142 var allPrimaryButtons = $(".btn-primary"); 143 disableElementsWithTagging(allPrimaryButtons); 144 145 146 var formJQ; 147 if (formElement != null) { 148 formJQ = $(formElement); 149 } else { 150 formJQ = button.closest("form"); 151 } 152 153 154 155 formJQ.find("input[name=method]").val(method); 156 formJQ.off( "submit.ds").on("submit.ds", function(event) { 157 disableElementsForSubmit(formElement); 158 if (typeof submitMethodSpinnerDisplay !== 'undefined' && submitMethodSpinnerDisplay) { 159 160 event.preventDefault(); 161 162 $("#ajaxLoadingModal").one('shown.bs.modal', function () { 163 formJQ.off( "submit.ds"); 164 formJQ.submit(); 165 }); 166 $("#ajaxLoadingModal").modal("show"); 167 } 168 });
169 170 171 172 if (button.attr("type") == 'submit' && browserSupportFormValidation()) { 173 enableElementsWithTagging(); 174 return true; //let the browser perform form validation 175 } else { 176 return checkForRequiredFields(button, formJQ.get(0), allPrimaryButtons); 177 } 178} 179 180function disableElementsWithTagging(elementsToDisable) { 181 elementsToDisable.filter(":not(:disabled)") 182 .attr("disabled", true) 183 .attr("data-lllee-submit-disable", 'true'); 184} 185 186function enableElementsWithTagging() { 187 document.querySelectorAll('[data-lllee-submit-disable="true"]').forEach(element => { 188 element.disabled = false; 189 element.removeAttribute('data-lllee-submit-disable'); 190 }); 191} 192 193function submitAndDisableAllButtons(buttonId, method, formElement) { 194 disableSecondaryButtonElements(); 195 submitMethod(buttonId, method, formElement); 196} 197 198function checkForRequiredFields(button, formElement, allPrimaryButtons) { 199 var fieldsMissingRequired = "<br/>"; 200 var fieldMissing = false; 201 var focused = false; 202 203 button.closest('form').find('input[required], select[required], textarea[required]').each(function() { 204 var value = $(this).val(); 205 var parentDiv = $(this).parent(); 206 var inputLabel = ''; 207 var hasValue = true; 208 209 if (value == null || value == "") { 210 hasValue = false; 211 var inputLabelJQ = parentDiv.find("label"); 212 if (inputLabelJQ.length == 0) { 213 inputLabel = $(this).closest("div.form-group").find("label").text(); 214 } else{ 215 inputLabel = inputLabelJQ.text(); 216 } 217 } else if ($(this).is(":checkbox") && !$(this).is(":checked")) { 218 hasValue = false; 219 inputLabel = parentDiv.text(); 220 } 221 222 if (hasValue) { 223 parentDiv.removeClass("has-error"); 224 } else { 225 inputLabel = inputLabel.replace("*", ""); 226 if (inputLabel.length > 0) { 227 fieldsMissingRequired = fieldsMissingRequired + inputLabel + "<br/>"; 228 parentDiv.addClass("has-error"); 229 fieldMissing = true; 230 if (!focused) { 231 $(this).trigger("focus"); 232 focused = true; 233 } 234 } 235 } 236 }); 237 238 if (fieldMissing) { 239 var buttonId = button.attr("id"); 240 $("#warningPopupMissingRequiredFieldsPopupMessage").html(fieldsMissingRequired); 241 showPopover(buttonId, "warningPopupMissingRequiredFields", ""); 242 $('input, select, textarea').on("click", function(e) { 243 $("#" + buttonId).popover('dispose'); 244 }); 245 $('input, select, textarea').on("blur", function(e) { 246 $("#" + buttonId).popover('dispose'); 247 }); 248 enableElementsForSubmit(formElement); 249 return false; 250 } else { 251 clearFileInputs(formElement);//need to clear non uploaded files to avoid struts errors 252 if (button.attr("type") == 'submit') { 253 enableElementsWithTagging(); 254 return true; // if button is to submit form, let it submit 255 } else { 256 formElement.submit(); 257 return false; 258 } 259 } 260 261} 262 263function showPopover(buttonId, divId, placementLocation) { 264 var titleText = $("#"+divId).attr("title"); 265 var contentText = $("#"+divId).html(); 266 if (placementLocation == '') { 267 placementLocation = 'bottom'; 268 } 269 270 $("#"+buttonId).popover({title: titleText, content: contentText, placement: placementLocation, html: true, trigger: 'manual' }); 271 $("#"+buttonId).popover('show'); 272} 273 274function showWarningPopover(fieldId, errorPopupId, errorMsg) { 275 $("#"+errorPopupId).html(errorMsg); 276 $(".has-error").removeClass("has-error"); 277 $("#"+fieldId).closest("div.form-group").addClass("has-error"); 278 showPopover(fieldId, errorPopupId, "bottom"); 279 $('input').on("blur", function(e) { 280 $("#"+fieldId).popover('dispose'); 281 });
282} 283 284function removeInvalidCharFromAttributeName(attrName) { 285 if(attrName) { 286 return attrName.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g,''); 287 } else { 288 return attrName; 289 } 290} 291 292function escapeAllDataTarget() { 293 $('a[data-target]').each(function() { 294 var dataTargetId = $(this).attr("data-target"); 295 var escapedDataTargetId = removeInvalidCharFromAttributeName(dataTargetId); 296 if(escapedDataTargetId != null && escapedDataTargetId != "") { 297 $(this).attr("data-target", "#" + escapedDataTargetId); 298 var targetDiv = document.getElementById(dataTargetId.substring(1)); 299 if (targetDiv) { 300 targetDiv.id = escapedDataTargetId; 301 } 302 } else { 303 $(this).removeAttr("data-target"); 304 } 305 }); 306} 307 308function addRequiredSymbol() { 309 var showRequiredInstructions = false; 310 $('input[required], select[required], textarea[required], .requiredFieldset').each( 311 function() { 312 showRequiredInstructions = true; 313 addRequiredSymbolToLabel($(this), false); 314 } 315 ); 316 $('input.requiredNotValidated, select.requiredNotValidated, textarea.requiredNotValidated').each( 317 function() { 318 showRequiredInstructions = true; 319 addRequiredSymbolToLabel($(this), false); 320 } 321 ); 322 $('input.requiredConditionally, select.requiredConditionally, textarea.requiredConditionally').each( 323 function() { 324 showRequiredInstructions = true; 325 addRequiredSymbolToLabel($(this), true); 326 } 327 ); 328 329 if (showRequiredInstructions) { 330 $("#requiredFieldInstruction,.requiredFieldInstructionAdditional").removeClass("d-none"); 331 } 332 333} 334 335/** 336 * Creates a JQuery ID selector from the specified ID, escaping any problematic characters 337 * @param myid 338 * @returns {string} a JQuery ID selector built from the specified ID (after escaping) 339 */ 340function createEscapedIDSelector( myid ) { 341 if (!myid) { 342 return ""; 343 } 344 return "#" + myid.replace( /(:|\.|\[|\]|\(|\))/g, "\\$1" ); 345} 346 347function addRequiredSymbolToLabel(elementJQ, hideRequiredLabel) { 348 if (elementJQ.is('fieldset')) { 349 parentDiv = elementJQ; 350 } else if (elementJQ.is(':radio')) { 351 parentDiv = elementJQ.closest("div.radio"); 352 } else if (elementJQ.is(':checkbox')) { 353 parentDiv = elementJQ.closest("div.checkbox"); 354 } else { 355 parentDiv = elementJQ.closest("div.form-group"); 356 } 357 358 var label = parentDiv.find("label"); 359 var symbolAdded = false; 360 361 // for radio button, the required symbol should be added to the legend of the corresponding fieldset and it should only be added once per fieldset. 362 if (elementJQ.is(':radio')) { 363 var fieldset = parentDiv.closest("fieldset"); 364 var legend = fieldset.find("legend").first(); 365 if (legend.length > 0) { 366 if (legend.find("span.requiredFieldLabel").length == 0) { 367 label = legend; 368 } else { 369 symbolAdded = true; 370 } 371 }
371 else if (fieldset.find("span.requiredFieldLabel").length > 0) { 372 symbolAdded = true; 373 } 374 } else if (elementJQ.is("fieldset")) { 375 label = elementJQ.find("legend"); 376 } else if (elementJQ.is(':checkbox')) { 377 if (parentDiv.find("span.requiredFieldLabel").length > 0) { 378 symbolAdded = true; 379 } 380 } 381 382 if (!symbolAdded) { 383 if (label.find("span.labelContent").length === 0) { 384 var labelContent = label.html(); 385 labelContent = "<span class='labelContent'>" + labelContent +"</span>"; 386 label.html(labelContent); 387 if (label.is("legend") && label.parent("fieldset.accessibility").length > 0) { 388 // to prevent two colons from being displayed after the label (one before the required symbol and one after) 389 // add a CSS class to hide the colon displayed after the required symbol 390 label.addClass("noColonLegend"); 391 } 392 } 393 var checkboxes = label.find("span.nicerCheckableCheckboxLabel"); 394 if (checkboxes.length == 1){ 395 checkboxes.addClass("requiredFieldLabel"); 396 if (hideRequiredLabel) 397 checkboxes.addClass("noRequiredFieldLabel"); 398 } 399 /* Only set the innermost span to have the requiredFieldLabel class */ 400 else if (checkboxes.length > 1){ 401 for (var i = 0; i < checkboxes.length; i++){ 402 var checkbox = $(checkboxes[i]); 403 if (checkbox.find("span.nicerCheckableCheckboxLabel").length == 0){ 404 checkbox.addClass("requiredFieldLabel"); 405 if (hideRequiredLabel) 406 checkbox.addClass("noRequiredFieldLabel"); 407 break; 408 } 409 } 410 } 411 else if (elementJQ.is(':radio') || elementJQ.is(':checkbox')) { 412 label.append("<span class='requiredFieldLabel'/>"); 413 if (hideRequiredLabel) { 414 label.find("span.requiredFieldLabel").addClass("noRequiredFieldLabel"); 415 } 416 } 417 else { 418 label.addClass("requiredFieldLabel"); 419 if (hideRequiredLabel) 420 label.addClass("noRequiredFieldLabel"); 421 } 422 } 423} 424 425/** 426 * Returns the selector with ID of fieldIdOrName if found. 427 * If not found, returns the selector with name of fieldIdOrName. 428 * If none found, or multiple found, empty selector will be returned. 429 * */ 430function getIDOrNameSelector(fieldIdOrName) { 431 var fieldJQ = $(createEscapedIDSelector(fieldIdOrName)); 432 if (fieldJQ.length === 1) { 433 return fieldJQ; 434 } 435 436 var fieldByNameJQ = $('[name="' + fieldIdOrName + '"]'); 437 if (fieldByNameJQ.length === 1) { 438 return fieldByNameJQ; 439 } 440 return fieldJQ; 441} 442 443/* 444 * Applies Bootstrap 4 error message styling for the specified field and error message. 445 * If the field is not found, the error message is added to the default error panel 446 */ 447function attachBootstrapFieldIsInvalidMessage(fieldIdOrName, errorMessage) { 448 var fieldJQ = getIDOrNameSelector(fieldIdOrName); 449 var errorMessageAppended = false; 450 if (fieldJQ.length > 0) { // Field found 451 var fieldToMarkInvalidJQ = fieldJQ; 452 if (fieldToMarkInvalidJQ.attr("type") === "hidden") { 453 // find the closest visible input 454 fieldToMarkInvalidJQ = fieldToMarkInvalidJQ.prevAll("input[type!='hidden']:first"); 455 } 456 if (fieldToMarkInvalidJQ.length > 0) { 457 fieldToMarkInvalidJQ.addClass('is-invalid'); 458 fieldToMarkInvalidJQ.attr("aria-invalid", "true"); 459 var invalidFeedbackHTML = "<div class='invalid-feedback w-100'>" + errorMessage + "</div>"; 460 if (fieldJQ.is('fieldset')) { 461 fieldJQ.after(invalidFeedbackHTML); 462 } else { 463 var fieldParentJQ = fieldJQ.parent(); 464 if (fieldJQ.siblings('.float-info-container').length > 0) { 465 fieldJQ.addClass('form-control-with-icon'); 466 } 467 fieldParentJQ.addClass("d-flex flex-wrap"); 468 fieldParentJQ.append(invalidFeedbackHTML); 469 } 470 errorMessageAppended = true; 471 } 472 } else { // Field not found 473 var preferredField = fieldIdOrName.replace(/(telephoneFax|email|address)ListArray\[(\d)\]\.(.*)/,"$1ListArray$2Preferred") 474 if (preferredField !== fieldIdOrName) { // Tel/Email/Address special case 475 fieldJQ = $(createEscapedIDSelector(preferredField)); 476 if (fieldJQ.length > 0 && fieldJQ.attr("type") !== "hidden") { 477 // append all error messages to the targetted element 478 if (!fieldJQ.hasClass('is-invalid')) { 479 fieldJQ.addClass('is-invalid'); 480 fieldJQ.parent().append('<div class="invalid-feedback w-100"></div>'); 481 } 482 var preferredFieldInvalidFeedbackJQ = fieldJQ.parent().find('div.invalid-feedback'); 483 preferredFieldInvalidFeedbackJQ.append("<div>" + errorMessage + "</div>"); 484 errorMessageAppended = true; 485 } 486 } 487 } 488 489 // If we were unable to append the message to the field, add to the default error panel. 490 if (errorMessageAppended ) { 491 if ($('#fieldValidationErrorsExistMessage').length === 0) { 492 $('#errorPanel ul').append("<li id='fieldValidationErrorsExistMessage'>" + fieldValidationErrorsExistMessage + "</li>"); 493 } 494 appendErrorIconToScrollSpyMenuItems(fieldJQ); 495 } else { 496 $('#errorPanel ul').append("<li>" + errorMessage + "</li>") 497 } 498} 499 500/** 501 * Appends an error icon to the scroll spy menu item referencing the panel that this fieldJQ is housed in. 502 * 503 */ 504function appendErrorIconToScrollSpyMenuItems(fieldJQ) { 505 if (fieldJQ.length > 0) { 506 var fieldSectionId = fieldJQ.closest("section").attr("id"); 507 if (fieldSectionId.length > 0) { 508 var fieldSectionMenuItemJQ = $(".scrollSpyMenu").find("a.nav-link[href=\"#" + fieldSectionId + "\"]"); 509 if (fieldSectionMenuItemJQ.length > 0 && fieldSectionMenuItemJQ.find(".scrollSpyMenuItemWarning").length === 0) { 510 fieldSectionMenuItemJQ.prepend( 511 "<i class='fas fa-exclamation-circle fa-2x scrollSpyMenuItemWarning'></i>\n" + 512 "<span class='sr-only'>" + scrollSpyMenuItemWarningSrOnlyMessage + "</span>"); 513 } 514 } 515 } 516} 517 518/** 519 * Removes an error icon to the scroll spy menu item referencing the panel that this fieldJQ is housed in. 520 * 521 */ 522function removeErrorIconFromScrollSpyMenuItems(fieldJQ) { 523 if (fieldJQ.length > 0) { 524 var fieldSectionJQ = fieldJQ.closest("section"); 525 var fieldSectionId = fieldSectionJQ.attr("id"); 526 if (fieldSectionId.length > 0 && fieldSectionJQ.find(".is-invalid").length === 0) { 527 var fieldSectionMenuItemJQ = $(".scrollSpyMenu").find("a.nav-link[href=\"#" + fieldSectionId + "\"]"); 528 var scrollSpyMenuItemWarningJQ = fieldSectionMenuItemJQ.find(".scrollSpyMenuItemWarning"); 529 if (fieldSectionMenuItemJQ.length > 0 && scrollSpyMenuItemWarningJQ.length > 0) { 530 fieldSectionMenuItemJQ.find(".scrollSpyMenuItemWarning").remove(); 531 } 532 } 533 } 534} 535 536/* 537 * Applies Bootstrap 4 styling for the specified valid field and message. 538 */ 539function attachBootstrapFieldIsValidMessage(fieldId, message) { 540 var fieldJQ = $(createEscapedIDSelector(fieldId)); 541 if (fieldJQ.length > 0) { 542 fieldJQ.addClass('is-valid');
543 var validFeedbackHTML = "<div class='valid-feedback w-100'>" + message + "</div>"; 544 if (fieldJQ.is('fieldset')) { 545 fieldJQ.after(validFeedbackHTML); 546 } else { 547 var fieldParentJQ = fieldJQ.parent(); 548 if (fieldJQ.siblings('.float-info-container').length > 0) { 549 fieldJQ.addClass('form-control-with-icon'); 550 } 551 fieldParentJQ.addClass("d-flex flex-wrap"); 552 fieldParentJQ.append(validFeedbackHTML); 553 } 554 } 555 removeErrorIconFromScrollSpyMenuItems(fieldJQ); 556} 557 558/* Disable all clickable elements in form */ 559function disableForm(form) { 560 var formVarJQ = $(form); 561 formVarJQ.find("input").attr("disabled", true); 562 formVarJQ.find("select").attr("disabled", true); 563 formVarJQ.find("button").attr("disabled", true); 564 formVarJQ.find("a").addClass("disabled"); // disable pretty checkbox and radio 565 formVarJQ.find('a[data-toggle]').prop("data-toggle", false); // disable popups 566} 567 568function addDisableLinkEvent() { 569 $('a[class=disabled]').on("click", function () {return false;}); 570} 571 572function addDisableLinkAfterOneClickEvent(){ 573 $('a.disabled-on-click').one('click',function(event){ 574 //get the link clicked 575 var elem = $(this); 576 //disable it 577 elem.on("click", function(){ 578 return false; 579 }) 580 }); 581} 582 583function setModifiedStatus(element) { 584 $(element).closest('form').data('changed', true); 585} 586 587/** 588 * Adds event handler to all input-type elements (input, button, textarea, select) inside a form 589 * for handling the change event. If any of the input-type elements are changed, 590 * the form is marked as changed. Used for determining if the lose unsaved changes confirmation 591 * dialog should be displayed. 592 * @param form [OPTIONAL] the target form element 593 */ 594function trackFormChanged(form) { 595 if (form) { 596 $("#" + form.id + " :input").on("change", function () { 597 $(form).data('changed', true); 598 }); 599 } 600 else { 601 $("form :input").on("change", function () { 602 $(this).closest('form').data('changed', true); 603 }); 604 } 605} 606 607function popupURL(modalDivId, url, forceReload, postProcessCallbackMethodName, postProcessCallbackArgs, allowCache) { 608 if (modalDivId == '') { 609 return false; 610 } 611 612 if(!allowCache) { 613 allowCache = false; 614 } 615 616 var modalDiv = $('#'+modalDivId); 617 var contentLoaded = modalDiv.find("#" + modalDivId + "LoadingDiv").hasClass("d-none"); 618 if(!contentLoaded || forceReload) { 619 modalDiv.find("#" + modalDivId + "LoadingDiv").removeClass("d-none"); 620 modalDiv.find("#" + modalDivId + "ContentDiv").empty(); 621 } 622 623 modalDiv.modal('show'); 624 625 if (!contentLoaded || forceReload) { 626 $.ajax({
627 url: url, 628 type: "GET", 629 dataType: "html", 630 cache: allowCache, 631 success: function( msg ) { 632 modalDiv.find("#" + modalDivId + "LoadingDiv").addClass("d-none"); 633 var modalContentDiv = modalDiv.find("#" + modalDivId + "ContentDiv"); 634 modalContentDiv.html(msg); 635 if (postProcessCallbackMethodName) { 636 postProcessCallbackMethodName.apply(this, postProcessCallbackArgs); 637 } 638 }, 639 error: function( msg ) { 640 modalDiv.find("#" + modalDivId + "LoadingDiv").addClass("d-none"); 641 var modalContentDiv = modalDiv.find("#" + modalDivId + "ContentDiv"); 642 var contentText = $('#ajaxError').html(); 643 modalContentDiv.html(contentText); 644 } 645 }); 646 } 647 648 return false; 649} 650 651/** 652 * Opens a modal dialog loaded via an asynchronous HTTP (Ajax) call. The dialog is removed from the DOM once it is closed. 653 * Prerequisites: The #ajaxLoadingModal dialog must be present on the page 654 * 655 * @param actionURL {string} the action URL method 656 * @param actionMethod {string} the action method that should be called 657 * @param {Object} settings the settings object which can specify the following properties 658 * @param {Element} settings.modalDialogTarget the HTML element to which the modal dialog should be appended. If not specified the dialog is appended to the body 659 * @param {string} settings.linkId the ID of the element that was clicked to popup this modal dialog. It is disabled while the modal loads 660 * @param {string} settings.modalId the HTML ID of the modal dialog 661 * @param {string} settings.httpMethod The HTTP method to use for the Ajax request (e.g. "POST", "GET", "PUT"). Defaults to GET when not specified 662 * @param {string} settings.dataType The type of data that you're expecting back from the server, e.g. "xml", "html", "json". Defaults to "html" 663 * @param {string|PlainObject|Array} settings.data Data to be sent to the server. It is converted to a query string, if not already a string. 664 * It's appended to the url for GET-requests 665 * 666 * @see {@link https://api.jquery.com/jquery.ajax/|jQuery.ajax()} 667 * @returns {boolean} false 668 */ 669function openAjaxModal(actionURL, actionMethod, settings) { 670 const requestType = settings.httpMethod || 'POST'; 671 const dataType = settings.dataType || 'html'; 672 const appendTargetElementJQ = settings.modalDialogTarget ? $(settings.modalDialogTarget) : $("body"); 673 const data = settings.data; 674 const linkId = settings.linkId; 675 const linkJQ = $("#" + linkId); 676 const modalId = settings.modalId; 677 const ajaxLoadingModalJQ = $('#ajaxLoadingModal'); 678 ajaxLoadingModalJQ.one('shown.bs.modal', function() { 679 $.ajax({
680 url : actionURL, 681 type : requestType, 682 data : data, 683 dataType : dataType, 684 cache : false, 685 success : function(msg) { 686 ajaxLoadingModalJQ.one('hidden.bs.modal', function() { 687 appendTargetElementJQ.append(msg); 688 const modalDialogJQ = $("#" + modalId); 689 modalDialogJQ.modal('show'); 690 linkJQ.prop("disabled", false); 691 692 // when modal is closed, remove the popup and destroy the modal 693 modalDialogJQ.on('hidden.bs.modal', function() { 694 $(this).data('modal', null); 695 modalDialogJQ.remove(); 696 }); 697 }); 698 ajaxLoadingModalJQ.modal('hide'); 699 }, 700 error : function(jqXHR, textStatus, errorThrown) { 701 ajaxLoadingModalJQ.one('hidden.bs.modal', function() { 702 linkJQ.prop("disabled", false); 703 showPopover(linkId, "ajaxError", ""); 704 }); 705 ajaxLoadingModalJQ.modal('hide'); 706 } 707 }); 708 // disable button to prevent multiple submit 709 linkJQ.attr('disabled', 'disabled'); 710 }); 711 ajaxLoadingModalJQ.modal('show'); 712 713 return false; 714} 715 716 717function popup(modalId){ 718 if (modalId == null || modalId == '') { 719 return false; 720 } 721 722 $('#' + modalId).modal('show'); 723 return false; 724} 725 726function fixYouTubeVideoZIndex(){ 727 var paramToAdd = 'wmode=transparent'; 728 $('iframe[src*="youtube.com"]').each(function(){ 729 var url = $(this).attr('src'); 730 var separator = '&'; 731 if (!(url.indexOf('?') > -1)) separator = '?'; 732 $(this).attr('src', url + separator + paramToAdd); 733 }); 734} 735 736function loadEmbeddableLogin(button) { 737 var container = button.parent(); 738 var href = container.attr("data-login-href"); 739 var iframeContainer = $("#studentLoginFrameContainer"); 740 if (iframeContainer.height() > 1) { 741 iframeContainer.empty(); 742 button.children(".glyphicon-chevron-down").removeClass("glyphicon-chevron-down").addClass("glyphicon-chevron-left"); 743 iframeContainer.animate({height: "0px"}, 500); 744 iframeContainer.hide(); 745 } else { 746 if (href != "") { 747 iframeContainer.empty(); 748 iframeContainer.append('<iframe id="studentLoginFrame" frameborder="0" src="'+href+'" width="100%" height="44px" scrolling="no"></iframe>'); 749 button.children(".glyphicon-chevron-left").removeClass("glyphicon-chevron-left").addClass("glyphicon-chevron-down"); 750 iframeContainer.show(); 751 iframeContainer.animate({height: "44px"}, 500); 752 } 753 } 754} 755 756function togglePanelIcon(panel) { 757 $(panel) 758 .prev('.card-header') 759 .find("span.panelHeaderIcon") 760 .toggleClass('glyphicon-plus glyphicon-minus'); 761 762 var panelIconElement = $(panel) 763 .prev('.card-header') 764 .find("span.panelHeaderIcon"); 765 var panelCurrentlyExpanded = $(panel).prev('.card-header').attr('aria-expanded'); 766 if(panelCurrentlyExpanded === "true") { 767 $(panelIconElement).attr('aria-pressed', 'false'); 768 } else { 769 $(panelIconElement).attr('aria-pressed', 'true'); 770 } 771} 772 773function togglePanelStyleClass(panel) { 774 $(panel).prev().parent().toggleClass('panel-default card-info'); 775} 776 777function handlePanelCollapse(e) { 778 /* check for other implementations of handlePanelCollapse() in other JS when modifying this one*/ 779 togglePanelIcon($(e.target)); 780 togglePanelStyleClass($(e.target)); 781} 782 783function initAccordions(){
784 initCollapsibles(); 785} 786 787function initCollapsibles(){ 788 $('.panel-default').on('hide.bs.collapse', handlePanelCollapse); 789 $('.panel-default').on('show.bs.collapse', handlePanelCollapse); 790 $('.card-info').on('hide.bs.collapse', handlePanelCollapse); 791 $('.card-info').on('show.bs.collapse', handlePanelCollapse); 792} 793 794function toggleAlreadyExpandedAccordion(section) { 795 if ($(section).find('.panel-collapse').hasClass('show')) { 796 togglePanelIcon($(section).find('.panel-collapse')); 797 $(section).find('.panel-collapse').each(function() { 798 togglePanelStyleClass($(this)); 799 }); 800 $(section).find('.card-header').toggleClass('collapsed') 801 } 802} 803 804function forceCloseBootstrapModal() { 805 $('body').removeClass('modal-open'); 806 $('.modal-backdrop').remove(); 807} 808 809function focusRequiredFieldInAccordian() { 810 /* Loop through each required input field and verify if the value is empty. If it is empty, focus on that accordian */ 811 $(':input[required=""],:input[required]').each(function() { 812 813 if($(this).is(":disabled")){ 814 return true; 815 } 816 817 //for radio buttons or checkboxes 818 if ($(this).is('input:radio') || $(this).is(':checkbox')) { 819 var name = $(this).attr("name"); 820 var count = $("input[name ='"+name+"']:checked").length; 821 if(count == 0) { 822 showAccordianContainingObject($(this)); 823 return false; 824 } 825 } 826 827 //for multi-selection 828 if ($(this).is('[multiple]')) { 829 var res = $(this).val(); 830 if(res == null || res.length == 0) { 831 showAccordianContainingObject($(this)); 832 return false; 833 } 834 } 835 836 //for all other input types including text, textarea, datepicker, etc. 837 else if ($(this).val().trim() == "") { 838 showAccordianContainingObject($(this)); 839 return false; 840 } 841 }); 842} 843 844function showAccordianContainingObject(currObject) { 845 //Need to manually collapse and expand panels because of a bug in bootstrap that breaks the accordian functionality 846 //if .collapse('hide') or .collapse('show') functions are used 847 848 //collapse all panels 849 var allPanels = $(".panel-collapse.show"); 850 togglePanelIcon(allPanels); 851 togglePanelStyleClass(allPanels); 852 allPanels.addClass("collapsed"); 853 allPanels.attr("aria-expanded", false); 854 allPanels.removeClass("show"); 855 856 //Expand the panel of interest 857 var currPanel = currObject.closest(".panel-collapse"); 858 togglePanelIcon(currPanel); 859 togglePanelStyleClass(currPanel); 860 currPanel.removeClass("collapsed"); 861 currPanel.attr("aria-expanded", true); 862 currPanel.addClass("show").css("height","auto"); 863 864 //show validation error if present. 865 if ($('#validationErrorMessage').length > 0) { 866 $('#validationErrorMessage').show(); 867 } 868} 869 870function isMobileView() { 871 return $(".accordionButtonContainer:visible").length > 0; 872} 873 874function clearFileInputs(form) { 875 if (form != null && form.enctype != "multipart/form-data") { 876 var formJQ = $(form); 877 var inputs = formJQ.find("input[type=file]"); 878 for (var i = 0; i < inputs.length; i++) { 879 inputs[i].value = ""; 880 inputs[i].disabled=true; 881 } 882 } 883} 884 885function enableFileInputs(form) { 886 var formJQ = $(form); 887 var inputs = formJQ.find("input[type=file]"); 888 for (var i = 0; i < inputs.length; i++) { 889 inputs[i].value = ""; 890 inputs[i].disabled=false; 891 } 892} 893 894function disableElementsForSubmit(formElement) { 895 var allPrimaryButtons = $(".btn-primary"); 896 disableElementsWithTagging(allPrimaryButtons); 897 clearFileInputs(formElement);//need to clear non uploaded files to avoid struts errors 898} 899 900function enableElementsForSubmit(formElement) { 901 enableElementsWithTagging(); 902 enableFileInputs(formElement); 903} 904 905function disableSecondaryButtonElements() { 906 var allSecondaryButtons = $(".btn-secondary"); 907 if (allSecondaryButtons) { 908 allSecondaryButtons.attr("disabled", true); 909 } 910} 911 912/* Function will return null if no closest element found */ 913function findClosestForm(element) { 914 var elementJQ = $(element); 915 var formJQ = elementJQ.closest("form"); 916 if (formJQ.length > 0) { 917 return formJQ.get(0); 918 } else { 919 return null; 920 } 921} 922 923/* appends a unique page id for autotests */ 924function appendTimestamp() { 925 $("#footer").append("<div id='timestamp' class='d-none'>"+Date.now()+"</div>"); 926} 927 928function changeLanguage(locale, useSecureCookie) { 929 var now = new Date(); 930 now.setFullYear(now.getFullYear() + 1); 931 if(useSecureCookie == 'true') { 932 document.cookie = "locale=" + locale + "; expires=" + now + "; path=/;" + "secure"; 933 } else { 934 document.cookie = "locale=" + locale + "; expires=" + now + "; path=/;"; 935 } 936 937 let isOverridingTargetUrl = false; 938 let overrideTargetFormElement = document.querySelector(".changeLocaleTargetAction"); 939 if (overrideTargetFormElement) { 940 isOverridingTargetUrl = true; 941 } 942 943 let targetUrl = isOverridingTargetUrl ? overrideTargetFormElement.action : window.location.href; 944 945 var requestURL = "?method=changeLocale" 946 var params = parseUri(targetUrl).queryKey; 947 var keys = Object.keys(params); 948 949 for (var key in keys) { 950 if (keys[key] != 'method' && keys[key] != 'lang' ) { 951 requestURL += '&'; 952 requestURL += keys[key]; 953 requestURL += '='; 954 requestURL += params[keys[key]]; 955 } 956 } 957 958 if (isOverridingTargetUrl) { 959 let pathOnly = parseUri(targetUrl).path; 960 window.location.href = "" + pathOnly + requestURL + "&lang=" + locale; 961 } else { 962 let appendHash = window.location.hash.split("#/").filter((item) => item).length > 0;
963 window.location.href = "" + requestURL + "&lang=" + locale + (appendHash ? window.location.hash : ''); 964 } 965 return false; 966} 967 968/*Start of 3rd party functions */ 969// parseUri 1.2.2 970// (c) Steven Levithan <stevenlevithan.com> 971// MIT License 972function parseUri (str) { 973 var o = parseUri.options, 974 m = o.parser[o.strictMode ? "strict" : "loose"].exec(str), 975 uri = {}, 976 i = 14; 977 978 while (i--) uri[o.key[i]] = m[i] || ""; 979 980 uri[o.q.name] = {}; 981 uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) { 982 if ($1) uri[o.q.name][$1] = $2; 983 }); 984 985 return uri; 986}; 987 988parseUri.options = { 989 strictMode: false, 990 key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], 991 q: { 992 name: "queryKey", 993 parser: /(?:^|&)([^&=]*)=?([^&]*)/g 994 }, 995 parser: { 996 strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/, 997 loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/ 998 } 999}; 1000/* End of 3rd party functions */ 1001 1002/* Ajax table functions. This is to support the <srs:displayTable> tag. */ 1003function doAjaxReplace(url, divId) { 1004 $("#"+divId).load(url + " #"+divId+"_div", 1005 function(response, status, xhr) { 1006 if(status == "error") { 1007 $("#"+divId).html(ajaxErrorMessage + " " + xhr.status + " " + xhr.statusText); 1008 } else { 1009 replaceAjaxTableLinks(divId); 1010 } 1011 } 1012 ); 1013} 1014 1015function replaceAjaxTableLinks(divId){ 1016 $("div#"+divId+" table > thead > tr > th > a").each( 1017 function() { 1018 var linkStr = $(this).attr('href'); 1019 if(!linkStr.match("^javascript:doAjaxReplace")) { 1020 $(this).attr("href", makeAjaxLink($(this).attr("href"), divId)); 1021 } 1022 } 1023 ); 1024 1025 $("div#"+divId+" span.pagelinks a").each( 1026 function() { 1027 var linkStr = $(this).attr('href'); 1028 if(!linkStr.match("^javascript:doAjaxReplace")) { 1029 $(this).attr("href", makeAjaxLink($(this).attr("href"), divId)); 1030 } 1031 } 1032 ); 1033} 1034 1035function makeAjaxLink(href, divId) { 1036 return "javascript:doAjaxReplace('" + href + "&ajax=true&ajaxId="+divId+"&dt="+(new Date).getTime()+"', '"+divId+"');"; 1037} 1038 1039function initAllAjaxTable() { 1040 $(".ajaxTableOuterWrapper").each( 1041 function() { 1042 replaceAjaxTableLinks(this.id); 1043 } 1044 ); 1045 1046} 1047/* Ends Ajax table functions */ 1048 1049function centeredPopup(page, popWidth, popHeight, close_only) { 1050 1051 var popF; 1052 if ( popF && !popF.closed && popF.close ) { 1053 popF.close(); 1054 } 1055 if ( !close_only ) { 1056 if (popWidth==null) { popWidth=400 } 1057 if (popHeight==null) { popHeight=400 } 1058 winX=(screen.width-popWidth)/2 1059 winY=(screen.height-popHeight)/2 1060 popF = window.open(page, "Window", "width="+ popWidth + ",height=" + popHeight + ",resizable=yes,scrollbars=yes,toolbar=no,menubar=no,location=no,top="+winY+",left="+winX); 1061 1062 } 1063 return false; 1064} 1065 1066if (!String.prototype.startsWith) { 1067 String.prototype.startsWith = function(searchString, position){ 1068 return this.substr(position || 0, searchString.length) === searchString; 1069 }; 1070} 1071 1072function doCoreDelayedLoadSetup() { 1073 /* Add all document ready functions to callbacks so they can be re-called in the AJAX success handler */ 1074 onAJAXSuccessCallbacks.add( escapeAllDataTarget ); 1075 onAJAXSuccessCallbacks.add( addRequiredSymbol ); 1076 onAJAXSuccessCallbacks.add( addDisableLinkEvent ); 1077 onAJAXSuccessCallbacks.add( initDatePicker ); 1078 1079 escapeAllDataTarget(); 1080 addRequiredSymbol(); 1081 addDisableLinkEvent(); 1082 //did not put this last one in AJAX Success callbacks because it is currently only used on student login button which loads a new page on click 1083 addDisableLinkAfterOneClickEvent(); 1084 disableInstructorClassListAfterOneClickEvent(); 1085 1086 initAllAjaxTable(); 1087 initDatePicker(); 1088 1089 /* enable EmbeddableLogin on Click */ 1090 $('#loginEmbeddableMenuButton').on("click", function() { 1091 loadEmbeddableLogin($(this)); 1092 });
1093 1094 1095 $(document).on("keyup", function(e) { 1096 if (e.keyCode == 27) { // escape key maps to keycode `27` 1097 $('.navbar .dropdown').removeClass('open') 1098 // close tooltips on escape for accessibility 1099 $('[data-toggle="tooltip"]').tooltip("hide"); 1100 } 1101 }); 1102 1103 /* Fix z-index for you-tube iframes */ 1104 fixYouTubeVideoZIndex(); 1105 1106 applyAccessibilityMarkup(); 1107} 1108 1109$(document).ready(function() { 1110 doCoreDelayedLoadSetup(); 1111 // doCoreDelayedLoadSetup is also invoked by paymentPageDelayedLoad 1112 // Functions that attaches listeners to document should be placed here so that they will only be called once 1113 appendTimestamp(); 1114 initAccordions(); 1115 trapFocus(); 1116}); 1117 1118function trapFocus(){ 1119 var tabSelectorTypes = "*[contenteditable], a[href], area[href], embed, *[tabindex], " + 1120 "select, textarea, iframe, button, input, object"; 1121 var tabSelectorGeneral = ":visible:not([disabled])"; 1122 var previousFocused = null; 1123 var tabMode = false; 1124 var dialogue = null; 1125 1126 function findFocused(action){ 1127 var focused = document.activeElement; 1128 // assume focused is visible and active 1129 if (focused && !$(focused).is(tabSelectorTypes)) focused = $(":focus")[0]; 1130 if (focused && $(focused).is(tabSelectorTypes)) { 1131 if (action) action(focused); 1132 return focused; 1133 } 1134 return null; 1135 }; 1136 1137 $(document).on("keydown", function(e) { 1138 if (e.key !== "Tab") return; 1139 tabMode = true; 1140 if (dialogue === null) return; 1141 e.preventDefault(); 1142 var focusPoints = $(tabSelectorTypes, dialogue).filter(tabSelectorGeneral); 1143 if (focusPoints.length === 0) { 1144 findFocused( function(item){ item.blur() }); 1145 return; 1146 } 1147 var maxTab = focusPoints.length - 1; 1148 var focused = findFocused(); 1149 // if for whatever reason the focus is not in focusPoints 1150 // index would return -1, and the following code will set the focus to 1151 // first item if it is tab 1152 // last item if it is shift tab 1153 var increment = (focused == null ? 0 : 1); 1154 var nextIndex = focusPoints.index(focused) + (e.shiftKey ? -1 : increment); 1155 if (nextIndex < 0) nextIndex = maxTab; 1156 if (nextIndex > maxTab) nextIndex = 0; 1157 focusPoints[nextIndex].focus(); 1158 }); 1159 1160 $(document).on("focusin", function(e) { 1161 if (dialogue === null) return; 1162 var focusPoints = $(tabSelectorTypes, dialogue).filter(tabSelectorGeneral); 1163 if (focusPoints.length === 0) { 1164 findFocused( function(item){ item.blur() }); 1165 return; 1166 } 1167 var focused = findFocused(); 1168 // if for whatever reason the focus is not in focusPoints 1169 // index would return -1, and the following code will set the focus to first 1170 if (focusPoints.index(focused) < 0) focusPoints[0].focus(); 1171 }); 1172 1173 $(document).on("pointerdown", function(e) { tabMode = false; }); 1174 1175 $(document).on("show.bs.modal", ".modal", function(e) { 1176 findFocused( function(item){ previousFocused = item }); 1177 }); 1178 1179 $(document).on("shown.bs.modal", ".modal", function(e) { 1180 dialogue = e.target; 1181 var focusPoints = $(tabSelectorTypes, dialogue).filter(tabSelectorGeneral); 1182 if (tabMode && focusPoints.length > 0){ 1183 focusPoints[0].focus(); 1184 } else { 1185 findFocused( function(item){ item.blur() }); 1186 } 1187 }); 1188 1189 $(document).on("hidden.bs.modal", ".modal", function(e) { 1190 dialogue = null; 1191 if (tabMode && previousFocused) previousFocused.focus(); 1192 }); 1193} 1194 1195function hideSkipToMainContainer(){ 1196 $("#skip-to-main-container").removeClass('skipToMainContainerVisible'); 1197 $("#skip-to-main").removeClass('skipToMainContentVisible').addClass('skipToMainContentHidden'); 1198} 1199 1200function showSkipToMainContainer(){ 1201 $("#skip-to-main").removeClass('skipToMainContentHidden').addClass('skipToMainContentVisible'); 1202 $("#skip-to-main-container").addClass('skipToMainContainerVisible'); 1203} 1204 1205 1206function clickElementById(elementId) { 1207 $(elementId).trigger("click"); 1208 return false; 1209} 1210 1211 1212function refreshCartGlyphicon() { 1213 var requestURL = '/userSessionInfo.do'; 1214 $.post(requestURL, { 1215 'method' : 'userSessionInfo' 1216 }, 1217 function (data) { 1218 $("#cartCount").text(data.numCartItems); 1219 $("#headerCartCount").text(data.numCartItems + " " + $("#numItemsInCartMessage").val()); 1220 var itemText = data.numCartItems == 1 ? $("#itemHelpText").attr("value") : $("#itemsHelpText").attr("value"); 1221 var cartTitle = $("#numItemsInCartHelpText").attr("value") + 1222 " " + data.numCartItems + 1223 " " + (itemText != null ? itemText.toLowerCase() : ""); 1224 $("#shoppingCartLink").attr("title", cartTitle).attr("aria-label", cartTitle); 1225 }, 1226 "json" 1227 ); 1228} 1229 1230function disableInstructorClassListAfterOneClickEvent(){
1231 $('a[id=instructorClassList]').one('click',function(event){ 1232 //get the link clicked 1233 var elem = $(this); 1234 1235 elem.on("click", function(){ 1236 checkModifiedStatus(); 1237 //disable it 1238 return false; 1239 }) 1240 }); 1241} 1242 1243function submitProfileTemplatePreviewSettings(actionUrl, inputContainerId, completionRedirect) { 1244 var dataString=$("#" + inputContainerId).find("input,select").serialize(); 1245 var request = $.ajax({ 1246 type: "POST", 1247 url: actionUrl, 1248 data: dataString, 1249 dataType: "html", 1250 cache: false 1251 }).always(function() { 1252 gotoLink(completionRedirect); 1253 }); 1254} 1255 1256function onChangePreviewProgramOffice() { 1257 $("#profileTemplatePreviewCU option").addClass("hidden"); 1258 $("#profileTemplatePreviewCU option:first").removeClass("hidden"); 1259 $("#profileTemplatePreviewCU").val(""); 1260 1261 var selectedPO = $("#profileTemplatePreviewPO").val(); 1262 if (selectedPO !== '') { 1263 if ($("#profileTemplatePreviewCU option[data-parentId='" + selectedPO + "']").length > 0) { 1264 $("#profileTemplatePreviewCU option[data-parentId='" + selectedPO + "']").removeClass("hidden"); 1265 $("#profileTemplatePreviewCU option[data-parentId='empty']").addClass("hidden"); 1266 $("#profileTemplatePreviewCU").val($("#profileTemplatePreviewCU option[data-parentId='" + selectedPO + "']:first").val()); 1267 } else { 1268 $("#profileTemplatePreviewCU option[data-parentId='empty']").removeClass("hidden"); 1269 } 1270 } 1271} 1272 1273function accessibleTooltip(targetElementList, tooltipParamObj) { 1274 1275 let accessibleTooltipParams = { trigger: "manual" }; 1276 if (tooltipParamObj) { 1277 tooltipParamObj = {...tooltipParamObj, ...accessibleTooltipParams}; 1278 } else { 1279 tooltipParamObj = accessibleTooltipParams; 1280 } 1281 1282 targetElementList 1283 .attr("tabindex", 0) 1284 .tooltip(tooltipParamObj) 1285 .mouseover(event => { 1286 $(event.target).tooltip("show"); 1287 $(".tooltip").on("mouseleave", function() { 1288 $(event.target).tooltip("hide"); 1289 }); 1290 }) 1291 .mouseout(event => { 1292 setTimeout(() => { 1293 if (!$(".tooltip:hover").length) { 1294 $(event.target).tooltip("hide"); 1295 } 1296 }, 100); 1297 }) 1298 .focus(event => { 1299 $(event.target).tooltip("show"); 1300 }) 1301 .blur(event => { 1302 $(event.target).tooltip("hide"); 1303 }); 1304} 1305 1306window.addEventListener('pageshow', function(event) { 1307 // if navigating back using browser-back, re-enable any elements disabled for the submit 1308 let restoreDisableState = event.persisted; 1309 if (performance && performance.getEntriesByType('navigation')[0]) { 1310 const navEntry = performance.getEntriesByType('navigation')[0]; 1311 restoreDisableState = restoreDisableState || (navEntry.type === 'back_forward'); 1312 } 1313 if (restoreDisableState) { 1314 enableElementsWithTagging(); 1315 } 1316}); 1317
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.