1// Global variable to hold responses for a given event function. 2var FormFieldResponseData = []; 3 4// Update a global collection with the formFieldDefinition's property ID and the value of the control 5// so that a function's responses can be persisted. 6this.SaveFormFieldValue = function (inputControl, fieldPropertyId) { 7 var myValue = ''; 8 if (inputControl.id.indexOf('radioButtonList') > -1) { 9 myValue = inputControl.rows[0].cells[0].firstChild.checked ? 'True' : 'False'; 10 } 11 else if (inputControl.type != null 12 && (inputControl.type == 'text' || inputControl.type == 'textarea' || inputControl.type == 'select-one')) { 13 14 myValue = inputControl.value; 15 } 16 else return; 17 18 // FormFieldResponseData is a global var, defined in EventProgramDisplayDisplay.js 19 FormFieldResponseData[fieldPropertyId] = myValue; 20}; 21 22function Asi_Web_iParts_Events_EventProgramDisplayDisplay( 23 refreshButtonId, registerButtonId, unregisterButtonId, saveFunctionResponseButtonHiddenId, 24 functionKeyHiddenId, functionRegistrationQuanityHiddenId, formEventFunctionHiddenId, formFunctionResponseValuesHiddenId, saveMessageLabelHiddenId, 25 registerFunctionHyperlinkMarkerClass, eventSetupMode, groupWrapperId, hiddenScrollToElementId, hiddenWaitListChoiceId, hdnAddFromWaitListBrn, HdnUndoPendingCheckoutFromWaitlistBtn, openDetailsInPopup) { 26 27 // public functions 28 var hiddenScrollToElement = jQuery("#" + hiddenScrollToElementId); //hidden field containing target element id for auto-scrolling 29 var waitListSelected = false; 30 31 // on ajax panel sync or asyinc postback (acts like document ready) , finds and scrolls to previously registered function 32 Sys.Application.add_load(function () { 33 RepositionScrollBar(); 34 FormFieldResponseData = []; 35 DisplaySavedFormMessage(); 36 DisableScrollDivTabStops(); 37 CheckConflictWarningMessage(); 38 }); 39 40 // Process responses to a function's questions 41 this.SubmitResponses = function (sender, eventFunctionId, eventFormSectionId, saveMessageLabelId) { 42 if (typeof Page_ClientValidate == "function" && !Page_ClientValidate(eventFormSectionId)) // Only fire off the validators for the form field(s) for this function. 43 return; 44 if (formEventFunctionHiddenId != null) 45 $(formEventFunctionHiddenId).value = eventFunctionId; 46 if (saveMessageLabelHiddenId != null) 47 $(saveMessageLabelHiddenId).value = saveMessageLabelId; 48 if (formFunctionResponseValuesHiddenId != null) { 49 // flatten the global FormFieldResponseData array and save it for access by the code-behind. 50 var flattened = ''; 51 52 for (key in FormFieldResponseData) { 53 if (FormFieldResponseData.hasOwnProperty(key)) { 54 flattened += key + '--ffrdkv--' + FormFieldResponseData[key] + '--ffrdentry--'; 55 } 56 } 57 $(formFunctionResponseValuesHiddenId).value = flattened; 58 } 59 if (saveFunctionResponseButtonHiddenId != null) { 60 DisableRegisterFunctionHyperLinks(); // Just for consistency with, e.g., RegisterFunction in this file. 61 document.getElementById(saveFunctionResponseButtonHiddenId).click(); 62 StoreButtonIdForScrolling(sender.id); 63 var actionValue = sender.getAttribute("data-action"); 64 if (actionValue != null && actionValue.toLowerCase() === "save") 65 { 66 setTimeout(function () { 67 location.reload(); 68 }, 200); 69 } 70 } 71 }; 72 73 // display a function 74 this.DisplayFunction = function (eventKey, functionKey, registrantId, url, isTemplate, openDetailsInPopup) { 75 if (url == null || url == "") return; 76 if (url.indexOf("?") >= 0) 77 url += "&EventKey=" + eventKey + "&FunctionKey=" + functionKey + "&RegistrantId=" + registrantId; 78 else
79 url += "?EventKey=" + eventKey + "&FunctionKey=" + functionKey + "&RegistrantId=" + registrantId; 80 url += "&IsTemplate=" + isTemplate; 81 82 if (openDetailsInPopup.toUpperCase() === "TRUE" || openDetailsInPopup === null) 83 ShowDialog(url, null, "1366", "768", "Display Program Item", null, "E", null, null, false, false, DisplayFunctionOnClose, null); 84 else { 85 if (url.indexOf("~") === 0) 86 url = window.gWebRoot + url.substring(1); 87 window.location = url; 88 } 89 90 if (functionKeyHiddenId != null) 91 $(functionKeyHiddenId).value = functionKey; 92 }; 93 94 // add a function 95 this.AddFunction = function (eventKey, url, refreshButtonId, windowTitle) { 96 if (url == null || url == "") return; 97 98 if (eventKey == "") 99 eventKey = jQuery("input[type=hidden][name$=HiddenEventCode]").val(); 100 101 if (url.indexOf("?") >= 0) 102 url += "&EventKey=" + eventKey; 103 else 104 url += "?EventKey=" + eventKey; 105 var dialog = ShowDialog(url, null, "1366", "768", windowTitle, null, "E", null, null, false, false, AddFunctionOnClose, null); 106 }; 107 108 // edit a registration option 109 this.EditRegistrationOption = function (eventKey, functionKey, registrantId, url, windowTitle) { 110 if (url == null || url == "") return; 111 if (url.indexOf("?") >= 0) 112 url += "&EventKey=" + eventKey + "&FunctionKey=" + functionKey + "&RegistrantId=" + registrantId + "&RegOpt=true"; 113 else 114 url += "?EventKey=" + eventKey + "&FunctionKey=" + functionKey + "&RegistrantId=" + registrantId + "&RegOpt=true"; 115 var dialog = ShowDialog(url, null, "1366", "768", windowTitle, null, "E", null, null, false, false, AddFunctionOnClose, null); 116 }; 117 118 // register for a function or add to itinerary 119 this.RegisterFunction = function (eventKey, functionKey, quantityInputId, inItineraryId, programQuantityId, isStaffUser, sender, divWaitListId, hiddenWaitlistButtonId) { 120 121 var isStaff = false; 122 if (typeof (isStaffUser) == "string") { 123 isStaff = isStaffUser.toUpperCase() === "TRUE"; 124 } else if (typeof (isStaffUser) == "boolean") { 125 isStaff = isStaffUser; 126 } 127 128 // record scroll position 129 if (sender != "undefined" && sender != null) { 130 if (jQuery(sender).length > 0) { 131 StoreButtonIdForScrolling(jQuery(sender).attr('id')); 132 } 133 } 134 //clear the hidden waitlist value so it's not picked up mistakingly in following function 135 jQuery("#" + hiddenWaitListChoiceId).val(""); 136 137 var $waitListDiv = jQuery('#' + divWaitListId); 138 if ($waitListDiv != null && $waitListDiv.is(":visible")) { 139 var $waitListChoiceDiv = $waitListDiv.find("div[id$='DivWaitList']"); 140 if ($waitListChoiceDiv.length > 0 && !$waitListChoiceDiv.is(":visible")) { 141 142 var $waitListRadioButtons = $waitListChoiceDiv.find("input[name$='WaitListRadioButtons']"); 143 if ($waitListRadioButtons != null) { 144 145 // Non-staff users can only Waitlist. 146 var $AddToWaitlistRadioButton = $waitListRadioButtons.filter(function () { 147 return this.value === "Waitlist"; 148 }); 149 if ($AddToWaitlistRadioButton != null && !isStaff) { 150 $AddToWaitlistRadioButton.prop("checked", true); 151 jQuery("#" + hiddenWaitListChoiceId).val($AddToWaitlistRadioButton.val()); 152 jQuery("#" + hiddenWaitlistButtonId).click(); 153 return; 154 } 155 156 $waitListChoiceDiv.show(); 157 // Give staff users option to Waitlist or Register. 158 $waitListRadioButtons.change(function () { 159 if (jQuery(this).is(":checked")) { 160 jQuery("#" + hiddenWaitListChoiceId).val(jQuery(this).val()); 161 jQuery("#" + hiddenWaitlistButtonId).click(); 162 } 163 }); 164 } 165 } 166 return; 167 } 168 169 this.RegisterFunctionActual(eventKey, functionKey, quantityInputId, inItineraryId, programQuantityId, isStaff); 170 }; 171 172 this.RegisterOrWaitListFunction = function (eventKey, functionKey, quantityInputId, inItineraryId, programQuantityId, isStaffUser) { 173 var isStaff = false; 174 175 if (typeof (isStaffUser) == "string") { 176 isStaff = isStaffUser.toUpperCase() === "TRUE"; 177 } else if (typeof (isStaffUser) == "boolean") { 178 isStaff = isStaffUser; 179 } 180 this.RegisterFunctionActual(eventKey, functionKey, quantityInputId, inItineraryId, programQuantityId, isStaff); 181 }; 182 183 this.RegisterFunctionActual = function (eventKey, functionKey, quantityInputId, inItineraryId, programQuantityId, isStaff) { 184 var isValid = true; 185 186 if (quantityInputId != null && typeof Page_ClientValidate === "function" && !isStaff) 187 isValid = Page_ClientValidate(quantityInputId); //Using quantityInputId as the validation group name 188 if (quantityInputId != null && typeof Page_ClientValidate === "function" && isStaff) 189 isValid = true; //For staff user override the client side validation for quantity 190 if (isValid == true) { 191 if (functionKeyHiddenId != null) 192 $(functionKeyHiddenId).value = functionKey; 193 if (programQuantityId != null && quantityInputId != null) { 194 if ($(quantityInputId) != null) { 195 $(programQuantityId).value = $(quantityInputId).value; 196 $(functionRegistrationQuanityHiddenId).value = $(quantityInputId).value; 197 } else { 198 $(programQuantityId).value = 1; 199 $(functionRegistrationQuanityHiddenId).value = 1; 200 } 201 } 202 if (registerButtonId != null) { 203 DisableRegisterFunctionHyperLinks(); 204 $(registerButtonId).click(); 205 // update the cart item count 206 updateCartItemCount(); 207 } 208 } 209 }; 210 211 212 // unregister for a function or remove from itinerary 213 this.UnregisterFunction = function (eventKey, functionKey, buttonId) { 214 if (functionKeyHiddenId != null) 215 $(functionKeyHiddenId).value = functionKey; 216 if (unregisterButtonId != null) { 217 DisableRegisterFunctionHyperLinks(); 218 StoreButtonIdForScrolling(buttonId); 219 if (typeof (Page_ValidationActive) != "undefined") { 220 Page_ValidationActive = false; 221 } 222 $(unregisterButtonId).click(); 223 updateCartItemCount(); 224 CheckConflictWarningMessage(); 225 } 226 }; 227 // unregister for a function or remove from itinerary 228 this.UndoPendingCheckoutFromWaitlist = function (eventKey, functionKey, buttonId) { 229 if (functionKeyHiddenId != null) 230 $(functionKeyHiddenId).value = functionKey; 231 if (unregisterButtonId != null) { 232 DisableRegisterFunctionHyperLinks(); 233 StoreButtonIdForScrolling(buttonId); 234 if (typeof (Page_ValidationActive) != "undefined") { 235 Page_ValidationActive = false; 236 } 237 $(HdnUndoPendingCheckoutFromWaitlistBtn).click(); 238 updateCartItemCount(); 239 CheckConflictWarningMessage(); 240 } 241 }; 242 243 // switch a function registration from the waitlist...force into a regular registration 244 this.RegisterFromWaitListFunction = function (eventKey, functionKey, buttonId) { 245 if (functionKeyHiddenId != null) 246 $(functionKeyHiddenId).value = functionKey; 247 if (hdnAddFromWaitListBrn != null) { 248 DisableRegisterFunctionHyperLinks(); 249 StoreButtonIdForScrolling(buttonId); 250 if (typeof (Page_ValidationActive) != "undefined") { 251 Page_ValidationActive = false; 252 } 253 $(hdnAddFromWaitListBrn).click(); 254 updateCartItemCount(); 255 } 256 }; 257 258 259 // private functions 260 261 function CheckConflictWarningMessage() { 262 var messagesExist = jQuery('span[id$=ConflictWarningMessage]'); 263 if (messagesExist.length == 0) 264 jQuery('div[id$=ConflictWarningMessage]').hide(); 265 } 266 267 // Show form save success or save failure message
268 function DisplaySavedFormMessage() { 269 if (jQuery('input[data-jQueryId="SaveMessagesLabelId"]').length < 1) { 270 return; 271 } 272 if (jQuery('input[data-jQueryId="SaveFormSuccess"]').length < 1) { 273 return; 274 } 275 var messageLabelId = jQuery('input[data-jQueryId="SaveMessagesLabelId"]').val(); 276 var saveSuccessMessage = jQuery('input[data-jQueryId="SaveFormSuccess"]').val(); 277 278 if (typeof (messageLabelId) == "undefined" || 279 typeof (saveSuccessMessage) == "undefined" || 280 messageLabelId.length < 1 || 281 saveSuccessMessage.length < 1) { 282 return; 283 } 284 285 if (jQuery("#" + messageLabelId).length < 1) { 286 return; 287 } 288 289 // Clear out message so its blank next time we do a postback 290 jQuery("#" + messageLabelId).text(saveSuccessMessage); 291 jQuery('input[data-jQueryId="SaveMessagesLabelId"]').val(""); 292 } 293 294 //For WCAG compliance, we do not want to tab onto programs containers as they do nothing 295 function DisableScrollDivTabStops() { 296 //change radgrid tabindex to skip it 297 var radGrids = jQuery('#' + groupWrapperId).find(".RadGrid"); 298 if (!radGrids || radGrids.length < 1) return; 299 radGrids.each(function () { 300 jQuery(this).attr("tabindex", '-1'); 301 }); 302 } 303 304 //upon refresh we need to reposition scrolling div to show function just added/removed 305 function RepositionScrollBar() { 306 if (typeof (hiddenScrollToElement) === "undefined") { 307 return; 308 } 309 310 // Get positioning details 311 var position = hiddenScrollToElement.value; 312 hiddenScrollToElement.value = ""; 313 if (typeof (position) === "undefined" || position.length < 2)//stored value doesn't exist or is blank 314 { 315 return; 316 } 317 318 // Separate the elements of the position value 319 var panelId, containerId, scrollPos, top; 320 var elements = position.split("||"); 321 if (elements.length != 4) return; 322 panelId = elements[0]; 323 scrollPos = parseInt(elements[1]); 324 containerId = elements[2]; 325 top = parseInt(elements[3]); 326 if (isNaN(scrollPos) || scrollPos <= 0) return; 327 if (isNaN(top) || top <= 0) return; 328 329 // Look for the panel using the custom attribute 330 var panel = jQuery("div[data-scrollingpanel-id=\"" + panelId + "\"]").eq(0); 331 if (panel == null || panel.length == 0) 332 return; 333 334 // Mow look for the container within it 335 var container = panel.find("div[data-programitemcontainer-id=\"" + containerId + "\"]").eq(0); 336 if (container == null || container.length == 0) 337 return; 338 339 // The first thing we do is scroll to the previous scroll position 340 panel.scrollTop(scrollPos); 341 342 // Now we check to see if the div containing the program item has the same top position as it had before. 343 // Most the time it will. 344 // If it doesn't it means that the contents of the program item list have changed during the postback. 345 // This is unlikely but theoretically possible e.g. 346 // One of the Program Items in the list has had its description changed (e.g. it requires more room), or its date/time changed 347 // which would affect the sort order etc. 348 // If we detect a difference the we attempt to correct for it. 349 var diff = parseInt(container.position().top) - top; 350 if (diff != 0) { 351 // correct the scroll position 352 scrollPos = scrollPos + diff; 353 panel.scrollTop(scrollPos > 0 ? scrollPos : 0); 354 } 355 356 } 357 358 //called when registering or removing a function - supports auto-scroll and WCAG focusing 359 //this sets what button id we need to look for on refresh (add or remove) to anchor on 360 function StoreButtonIdForScrolling(buttonId) { 361 if (typeof (hiddenScrollToElement) !== "undefined" && typeof (buttonId) !== "undefined") { 362 var lastButtonId = buttonId; 363 var useButtonId = false; 364 if (buttonId.indexOf("AddButton") > -1) { 365 lastButtonId = buttonId.replace("AddButton", "RemoveLinkButton"); 366 useButtonId = true; 367 } 368 else if (buttonId.indexOf("RemoveLinkButton") > -1) { 369 lastButtonId = buttonId.replace("RemoveLinkButton", "AddButton"); 370 useButtonId = true; 371 } 372 else if (buttonId.indexOf("DisplayLink") > -1 || buttonId.indexOf("SaveResponses") > -1) { 373 useButtonId = true; 374 375 } 376 // Find the programitemcontainer div the button is in 377 hiddenScrollToElement.value = ""; 378 var container = jQuery("#" + buttonId).closest("div[data-programitemcontainer-id]"); 379 if (container && container.length > 0) { 380 381 // now get the scrolling panel that the container is in 382 var panel = container.closest("div[data-scrollingpanel-id]"); 383 if (panel && panel.length > 0) { 384 385 // store the positions 386 hiddenScrollToElement.value = panel.attr("data-scrollingpanel-id") + "||" + panel.scrollTop() + "||" + container.attr("data-programitemcontainer-id") + "||" + container.position().top; 387 lastButtonId += "||" + panel.scrollTop() + "||" + container.attr("data-programitemcontainer-id") + "||" + container.position().top; 388 } 389 390 } 391 392 //set focus 393 var hiddenLastClickedElement = jQuery("[id*='lastClickedElementId']").eq(0); 394 if (hiddenLastClickedElement && hiddenLastClickedElement.length > 0) { 395 if (useButtonId) { 396 hiddenLastClickedElement.val("id-" + lastButtonId + "-no_overwrite"); 397 } 398 else { 399 hiddenLastClickedElement.val("id-" + hiddenScrollToElement.value + "-no_overwrite"); 400 } 401 } 402 } 403 } 404 405 // called when closing the function edit popup 406 function AddFunctionOnClose(radWin) { 407 var refresh = false; 408 if (radWin != null && typeof (radWin.IsDirty) == 'boolean') { 409 refresh = radWin.IsDirty; 410 } 411 else { 412 // if for some reason we dont get a rad window then assume we should refresh 413 refresh = true; 414 } 415 if (refresh) { 416 if(refreshButtonId != null) 417 $(refreshButtonId).click(); 418 //fix for pricing losing context 419 var pricingRefreshButton = jQuery("[id$='Pricing_RefreshTrigger']"); 420 if (pricingRefreshButton != null) 421 pricingRefreshButton.click(); 422 } 423 }; 424 425 // called when closing the function display popup
426 function DisplayFunctionOnClose(radWin) { 427 var refresh = false; 428 if (radWin != null && typeof (radWin.IsDirty) == 'boolean') { 429 refresh = radWin.IsDirty; 430 } 431 else { 432 // if for some reason we dont get a rad window then assume we should refresh 433 refresh = true; 434 } 435 if (refresh && refreshButtonId != null) 436 $(refreshButtonId).click(); 437 }; 438 439 // disable the add/remove registration/itinerary hyperlinks in the program listing 440 // permanently disables them, the page must postback in order for them to be restored 441 function DisableRegisterFunctionHyperLinks() { 442 jQuery("." + registerFunctionHyperlinkMarkerClass).removeAttr("href").css("color", "#bebebe"); 443 } 444 445 // Scroll position manager for window and panels 446 (function () { 447 'use strict'; 448 449 // ----- Configuration ----- 450 var RESTORE_DELAY = 100; 451 var BUTTON_SUFFIXES = [ 452 '_CancelRegistrationButton', 453 '_RedirectToCartButton', 454 '_RegisterSomeoneElseButton', 455 '_SubmitEventRegistrationButton' 456 ]; 457 458 // ----- State ----- 459 var eventKey = getEventKey(); 460 var storageKey = eventKey ? 'savedScrollPos_' + eventKey : null; 461 var isInitialized = false; 462 var pageHidden = false; 463 464 // ----- Early Page Hiding (runs immediately) ----- 465 466 // Only hide page if EventKey exists AND there's saved scroll data 467 if (storageKey) { 468 try { 469 var hasSavedData = sessionStorage.getItem(storageKey); 470 if (hasSavedData) { 471 // Hide page content and scrollbar immediately 472 var style = document.createElement('style'); 473 style.id = 'scroll-restore-hide'; 474 style.textContent = 'html{visibility:hidden;overflow:hidden!important;}'; 475 document.head.appendChild(style); 476 pageHidden = true; 477 } 478 } catch (e) { 479 // sessionStorage not available, don't hide 480 } 481 } 482 483 // Exit early if no EventKey 484 if (!storageKey) return; 485 486 // ----- Utility Functions ----- 487 488 function getEventKey() { 489 var search = window.location.search; 490 if (!search) return null; 491 492 var params = search.substring(1).split('&'); 493 for (var i = 0; i < params.length; i++) { 494 var pair = params[i].split('='); 495 if (pair[0].toLowerCase() === 'eventkey' && pair[1]) { 496 return decodeURIComponent(pair[1]); 497 } 498 } 499 return null; 500 } 501 502 function showPage() { 503 if (!pageHidden) return; 504 505 var hideStyle = document.getElementById('scroll-restore-hide'); 506 if (hideStyle && hideStyle.parentNode) { 507 hideStyle.parentNode.removeChild(hideStyle); 508 } 509 pageHidden = false; 510 } 511 512 function isAspNetPostBack() { 513 try { 514 // Check for async postback 515 if (typeof Sys !== 'undefined' && Sys.WebForms && Sys.WebForms.PageRequestManager) { 516 var prm = Sys.WebForms.PageRequestManager.getInstance(); 517 if (prm && prm.get_isInAsyncPostBack && prm.get_isInAsyncPostBack()) { 518 return true; 519 } 520 } 521 // Check for synchronous postback 522 return !!(window.__doPostBack && typeof __EVENTTARGET !== 'undefined'); 523 } catch (e) { 524 return false; 525 } 526 } 527 528 function safeSessionStorage(action, key, value) { 529 try { 530 if (action === 'get') { 531 return sessionStorage.getItem(key); 532 } else if (action === 'set') { 533 sessionStorage.setItem(key, value); 534 return true; 535 } else if (action === 'remove') { 536 sessionStorage.removeItem(key); 537 return true; 538 } 539 } catch (e) { 540 // Storage quota exceeded or disabled 541 return action === 'get' ? null : false; 542 } 543 } 544 545 // ----- Core Functions ----- 546 547 function saveScrollPositions() { 548 var positions = {}; 549 550 // Load existing positions to preserve data 551 var existing = safeSessionStorage('get', storageKey); 552 if (existing) { 553 try { 554 positions = JSON.parse(existing); 555 } catch (e) { 556 positions = {}; 557 } 558 } 559 560 // Save main window scroll 561 positions._page = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0; 562 563 // Save panel scrolls 564 var panels = document.querySelectorAll('[data-scrollingpanel-id]'); 565 for (var i = 0; i < panels.length; i++) { 566 var id = panels[i].getAttribute('data-scrollingpanel-id'); 567 if (id) { 568 positions[id] = panels[i].scrollTop || 0; 569 } 570 } 571 572 safeSessionStorage('set', storageKey, JSON.stringify(positions)); 573 } 574 575 function restoreScrollPositions() { 576 var saved = safeSessionStorage('get', storageKey); 577 if (!saved) { 578 showPage(); 579 return; 580 } 581 582 var positions; 583 try { 584 positions = JSON.parse(saved); 585 } catch (e) { 586 showPage(); 587 return; 588 } 589 590 // Restore main window scroll with delay for DOM readiness 591 if (typeof positions._page === 'number') { 592 setTimeout(function () { 593 window.scrollTo(0, positions._page); 594 // Show page after scroll completes 595 setTimeout(showPage, 50); 596 }, RESTORE_DELAY); 597 } else { 598 showPage(); 599 } 600 601 // Restore panel scrolls immediately 602 var panels = document.querySelectorAll('[data-scrollingpanel-id]'); 603 for (var i = 0; i < panels.length; i++) { 604 var id = panels[i].getAttribute('data-scrollingpanel-id'); 605 if (id && typeof positions[id] === 'number') { 606 panels[i].scrollTop = positions[id]; 607 } 608 } 609 610 // Clear storage after restore 611 safeSessionStorage('remove', storageKey); 612 } 613 614 function clearScrollData() { 615 window.scrollTo(0, 0); 616 safeSessionStorage('remove', storageKey); 617 } 618 619 function attachClearButtonListeners() { 620 // Use getElementsByTagName for better ES5 compatibility 621 var buttons = document.getElementsByTagName('button'); 622 var inputs = document.getElementsByTagName('input'); 623 var i, j, btn, id; 624 625 // Process button elements 626 for (i = 0; i < buttons.length; i++) { 627 btn = buttons[i]; 628 id = btn.id; 629 if (!id) continue; 630 631 for (j = 0; j < BUTTON_SUFFIXES.length; j++) { 632 if (endsWithSuffix(id, BUTTON_SUFFIXES[j])) { 633 btn.removeEventListener('click', clearScrollData); 634 btn.addEventListener('click', clearScrollData); 635 break; 636 } 637 } 638 } 639 640 // Process input[type="button"] elements 641 for (i = 0; i < inputs.length; i++) {
642 btn = inputs[i]; 643 if (btn.type && btn.type.toLowerCase() !== 'button') continue; 644 645 id = btn.id; 646 if (!id) continue; 647 648 for (j = 0; j < BUTTON_SUFFIXES.length; j++) { 649 if (endsWithSuffix(id, BUTTON_SUFFIXES[j])) { 650 btn.removeEventListener('click', clearScrollData); 651 btn.addEventListener('click', clearScrollData); 652 break; 653 } 654 } 655 } 656 } 657 658 function endsWithSuffix(str, suffix) { 659 return str.indexOf(suffix) === str.length - suffix.length; 660 } 661 662 // ----- Event Handlers ----- 663 664 function handleBeforeUnload() { 665 if (isAspNetPostBack()) { 666 saveScrollPositions(); 667 } else { 668 safeSessionStorage('remove', storageKey); 669 } 670 } 671 672 function handleLoad() { 673 if (isInitialized) return; 674 isInitialized = true; 675 676 restoreScrollPositions(); 677 attachClearButtonListeners(); 678 679 // Failsafe: ensure page is shown even if restore didn't run 680 setTimeout(function () { 681 showPage(); 682 }, RESTORE_DELAY + 200); 683 } 684 685 function handleUpdatePanelEnd() { 686 restoreScrollPositions(); 687 attachClearButtonListeners(); 688 } 689 690 // ----- Initialize Event Listeners ----- 691 692 window.addEventListener('beforeunload', handleBeforeUnload); 693 window.addEventListener('load', handleLoad); 694 695 // Handle ASP.NET UpdatePanel postbacks 696 if (typeof Sys !== 'undefined' && Sys.WebForms && Sys.WebForms.PageRequestManager) { 697 var prm = Sys.WebForms.PageRequestManager.getInstance(); 698 if (prm) { 699 prm.add_endRequest(handleUpdatePanelEnd); 700 } 701 } 702 703 })(); 704 705 // Panel state manager for expanded/collapsible panels 706 (function () { 707 'use strict'; 708 709 var WRAPPER_SUFFIX = '_DivPanelListerWrapper'; 710 var HEADER_SUFFIX = '_Head'; 711 var BODY_SUFFIX = '_Body'; 712 var STATE_SUFFIX = '_CollapsiblePanelExtender_ClientState'; 713 var SELECT_SUFFIX = '_SelectProgramsBy'; 714 var hasFind = (typeof $find === 'function'); 715 716 // --- Safe sessionStorage helpers --- 717 function safeSet(key, val) { 718 try { if (sessionStorage.getItem(key) !== val) sessionStorage.setItem(key, val); } catch (e) { } 719 } 720 function safeGet(key) { 721 try { return sessionStorage.getItem(key); } catch (e) { return null; } 722 } 723 function safeRemove(key) { 724 try { sessionStorage.removeItem(key); } catch (e) { } 725 } 726 727 // --- Clear all panel state from sessionStorage --- 728 function clearPanelState() { 729 try { 730 for (var i = sessionStorage.length - 1; i >= 0; i--) { 731 var key = sessionStorage.key(i); 732 if (key && key.indexOf(STATE_SUFFIX) !== -1) safeRemove(key); 733 } 734 } catch (e) { } 735 } 736 737 // --- Add listener to SelectProgramsBy dropdown --- 738 function attachSelectListener() { 739 var select = document.querySelector('[id$="' + SELECT_SUFFIX + '"]'); 740 if (select) select.addEventListener('change', clearPanelState); 741 } 742 743 // --- Initialize single header --- 744 function initHeader(header, panels) { 745 var baseId = header.id.substring(0, header.id.length - HEADER_SUFFIX.length); 746 var stateId = baseId + STATE_SUFFIX; 747 var stateInput = document.getElementById(stateId); 748 if (!stateInput) return; 749 750 panels.push({ baseId: baseId, stateInput: stateInput, header: header }); 751 752 // Initialize storage if missing 753 if (safeGet(stateInput.id) === null) safeSet(stateInput.id, stateInput.value); 754 755 // Restore value from storage 756 var storedState = safeGet(stateInput.id); 757 if (storedState !== null) stateInput.value = storedState; 758 759 // Persist changes on click 760 header.addEventListener('click', function () { 761 setTimeout(function () { 762 if (stateInput && stateInput.id) safeSet(stateInput.id, stateInput.value); 763 }, 50); 764 }); 765 } 766 767 // --- Initialize headers and panel states --- 768 function initPanelStateManager() { 769 var wrapper = document.querySelector('[id$="' + WRAPPER_SUFFIX + '"]'); 770 if (!wrapper) return; 771 772 var headers = wrapper.querySelectorAll('div[id$="' + HEADER_SUFFIX + '"]'); 773 if (!headers.length || headers.length === 1) return; 774 775 var panels = []; 776 777 for (var i = 0; i < headers.length; i++) initHeader(headers[i], panels); 778 779 var bodies = wrapper.querySelectorAll('div[id$="' + BODY_SUFFIX + '"]'); 780 781 // Hide headers and bodies immediately 782 for (var i = 0; i < headers.length; i++) headers[i].style.visibility = 'hidden'; 783 for (var i = 0; i < bodies.length; i++) bodies[i].style.visibility = 'hidden'; 784 785 // --- Apply stored states safely --- 786 function applyStates() { 787 var anyExpanded = false; 788 789 for (var i = 0; i < panels.length; i++) { 790 var p = panels[i]; 791 var extender = hasFind ? $find(p.baseId + '_CollapsiblePanelExtender') : null; 792 if (!extender) continue; 793 794 var storedState = safeGet(p.stateInput.id); 795 if (storedState !== null) p.stateInput.value = storedState; 796 797 if (p.stateInput.value === 'true') { 798 extender.collapsePanel(); 799 } else { 800 extender.expandPanel(); 801 anyExpanded = true; 802 } 803 } 804 805 // If all collapsed, expand first 806 if (!anyExpanded && panels.length > 0) { 807 var first = panels[0]; 808 var firstExtender = hasFind ? $find(first.baseId + '_CollapsiblePanelExtender') : null; 809 if (firstExtender) { 810 firstExtender.expandPanel(); 811 first.stateInput.value = 'false'; 812 safeSet(first.stateInput.id, 'false'); 813 } 814 } 815 816 // --- Reveal all headers and bodies with a small delay --- 817 setTimeout(function () { 818 for (var i = 0; i < headers.length; i++) headers[i].style.visibility = 'visible'; 819 for (var i = 0; i < bodies.length; i++) bodies[i].style.visibility = 'visible'; 820 }, 300); // slight delay avoids showing animation 821 } 822 823 // Apply immediately if extenders are ready, otherwise wait for ASP.NET AJAX 824 if (hasFind) { 825 applyStates(); 826 } else if (window.Sys && Sys.Application && typeof Sys.Application.add_load === 'function') { 827 Sys.Application.add_load(applyStates); 828 } else { 829 document.addEventListener('DOMContentLoaded', applyStates); 830 } 831 } 832 833 // --- Safe initialization handler --- 834 function initialize() { 835 attachSelectListener(); 836 initPanelStateManager(); 837 } 838 839 // --- Hook into ASP.NET or DOM load --- 840 if (window.Sys && Sys.Application && typeof Sys.Application.add_load === 'function') { 841 Sys.Application.add_load(initialize); 842 } else { 843 if (document.readyState === 'complete' || document.readyState === 'interactive') { 844 initialize(); 845 } else { 846 document.addEventListener('DOMContentLoaded', initialize); 847 } 848 } 849 850 })(); 851 852}
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.