PageSourceSearch

https://vpela.org.au/iparts/Events/Events/EventProgramDisplayDisplay.js

js vpela.org.au collected 2026-10-08 09:22:38 UTC 36,011 bytes, 852 lines download raw bytes

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.