{"version":3,"sources":["webpack:///./app/webpacker/src/javascripts/www/display.dropdowns.js","webpack:///./app/webpacker/src/javascripts/www/utils.js","webpack:///./app/webpacker/src/javascripts/www/search.offcanvas.js","webpack:///./app/webpacker/src/javascripts/www/search.identifiers.js","webpack:///./app/webpacker/src/javascripts/_browser-update.ts","webpack:///./app/webpacker/src/javascripts/www/display.hours.js","webpack:///./app/webpacker/src/javascripts/www/display.events.js","webpack:///./app/webpacker/src/javascripts/_utils.ts","webpack:///./app/webpacker/src/javascripts/www/main.js","webpack:///./app/webpacker/src/javascripts/www/display.to-top.js","webpack:///./app/webpacker/src/javascripts/www/display.anchors.js","webpack:///./app/webpacker/src/javascripts/www/modal.map.js","webpack:///./app/webpacker/src/javascripts/www/search.highlight.js","webpack:///./app/webpacker/src/javascripts/www/display.chat.js","webpack:///./app/webpacker/src/javascripts/www/display.basics.js","webpack:///./app/webpacker/src/javascripts/www/display.alerts.js","webpack:///./app/webpacker/src/javascripts/www/search-dropdowns.js","webpack:///./app/webpacker/packs/base.js.erb"],"names":["DropdownManager","constructor","config","arguments","length","undefined","_defineProperty","renderDivider","item","classes","parent_id","index","renderIcon","icon","renderInfoIcons","icons","text","parent_text","url","this","resolveURL","classStrings","mega_icon_links","icon_classes","renderAnnotation","mega_link_annotation","annotation","renderText","is_last","dropdown_last_item","templates","renderDropdownLink","renderHours","cid","renderMegaMenuLink","caret_icon","info_icons","inner","mega_links","renderMegaMenuList","group_obj","list","items","indexOf","id","type","renderDropdownList","entry","children","renderDropdownMenu","minWidth","min_width","dropdown_menu","renderGroupHeading","inner_content","mega_heading_links","heading","mega_headings","renderGroupList","renderColumnGroup","renderMegaColumnGroups","column_obj","groups","renderMegaColumn","renderColumns","columns","cols","renderMegaMenu","mega_menu","obj","baseURL","window","location","origin","dataSrc","wwwJsShims","dropdowns","ready","Promise","resolve","resolveReady","init","async","data","fetchData","timeout","retries","retryDelay","console","error","defaultBehaviorNoDropdownData","renderAllMenus","path","URL","protocol","e","href","document","querySelectorAll","forEach","toggle","removeAttribute","svg","querySelector","classList","add","menu_obj","dropdown_config","rendered","insertRenderedMenu","insertion_point","insertAdjacentHTML","initNewTooltip","element","tooltip","bootstrap","Tooltip","setWithExpiry","key","value","ttl","version","useSessionStorage","expiry","Date","getTime","sessionStorage","localStorage","setItem","JSON","stringify","getWithExpiry","storage","itemStr","getItem","parse","removeItem","attempt","controller","AbortController","timeoutId","setTimeout","abort","shouldRetry","response","fetch","signal","clearTimeout","ok","status","delay","json","message","ms","togglePasswordVisibility","togglePasswordButtons","button","addEventListener","target","getAttribute","input","setAttribute","onDomReady","callback","readyState","optInTooltips","selector","warn","optInPopovers","container","Popover","OffCanvasSearch","offCanvasSearch","offCanvasSearchInput","offCanvasSearchTrigger","focusOnRevealedSearchInput","setupEventListeners","event","focus","handleEscapeKey","Collapse","getOrCreateInstance","hide","handleClickOutside","contains","handleSlashKey","includes","tagName","preventDefault","show","bind","remove","removeEventListener","IdentifierHelper","doiRegex","urlRegex","proxyUrl","urls","ill","proxyTools","proxyString","queryString","prxyTyp","doiPrefix","openDOIString","doi","summonDOIString","illiadURLString","encodeURIComponent","selectors","doiInputs","doiText","strings","header","label","instructions","note","ifPaywall","endMsg","directAccess","doiOpenAccess","summon","illiad","illiadNote","externalLinkText","buildModal","prxyStr","existingModal","destroyModal","listGroupItems","listGroupItemFlex","externalLink","listGroupItemBold","join","summonFallback","illiadFallback","learnMore","makeBody","isDoi","makeDialogEl","makeModal","body","fragement","createDocumentFragment","modalEl","createElement","innerHTML","appendChild","pasted","clipboardData","getData","handlePaste","initializeModalEventListeners","modalInput","proxyableDirectAccess","doiSummonAccess","itemIlliadAccess","initiatedItem","resetModal","inputStr","handleIcon","closest","Modal","clickHandler","match","trim","replace","showTooltip","modalInstance","getInstance","dispose","trigger","title","required","f","o","s","c","insecure","unsupported","msg","msgmore","bupdate","bignore","remind","bnever","style","onshow","info","log","browser","t","dayjs","extend","utc","timezone","advancedFormat","dropdownConfig","untLib","dropdownManager","contentSection","h2Elements","skippedH2Count","h2","shouldSkipInsertion","toTopSnippet","generateToTopSnippet","el","hours","currentVersion","then","storedHours","storageKey","fetchHours","setTodaysDate","setHTMLElementHours","week","generateWeeklyTable","weekHours","parseInt","hoursData","find","lid","days","textContent","time","todayElement","tz","format","spacesArray","spaces","map","space","calendarUrl","endPoint","today","todayFormatted","nextWeek","name","dates","Object","keys","date","formatHours","storeData","todayData","day","expires","currentDay","from","to","hourTableDiv","lidFilter","split","filteredData","filter","hour","String","baseDate","tableHTML","i","className","headerDate","dateKey","dayInfo","displayTime","start","end","allday","apiURL","URLSearchParams","apiArgs","events","isValidEvent","isCampusIncluded","campus","public","registration","seats","reoccurs_future","future_dates","category","categorizeEvent","combinedText","description","presenter","toLowerCase","ignoreList","ignore","term","campusId","allowedCampusIds","searchFields","categories","some","keyword","now","filterEventsByCriteria","_ref","campuses","campusCondition","categoryCondition","groupEventsByDate","reduce","groupedEvents","push","makeDay","group","isLastItem","dateParts","monthName","makeItem","rangeStr","formatTimeStr","shortTitle","substring","makeShortTitle","eventElement","calendarIcon","month","itemContent","itemEl","makeList","insertEventsToDom","wrapper","kind","max","maxDays","eventBody","campusFilter","categoryFilter","filteredEvents","slice","maxDate","isBefore","isAfter","eventDates","noEvents","noEventsMessage","fetchAndProcessEvents","headings","makeAnchorLinks","copyToClipBoardListener","jumpToTopOfPage","hasAttribute","idValue","createIcon","link","pathname","navigator","clipboard","writeText","catch","scrollTo","top","behavior","modal","image","downloadLink","infoLink","listeners","triggerEl","relatedTarget","src","alt","contentArea","urlParams","search","searchText","get","Mark","markInstance","mark","totalMarks","chat","expireTime","apiUrl","chat_config","concat","endpoint","widgetUrl","chatBtn","chatBtnText","offCanvasAsUs","getFallbackText","chatStatus","checkStorage","fetchChatStatus","loadChat","showBtn","offlineStatus","online","setStorage","script","ask_us_text","active","alerts","js_config","expireDays","days_expire_dismiss","publishOn","publish_on","hideOn","hide_on","alertElement","dismissBtn","checkAlertVisibility","attachDismissEvent","globalAlert","publishDate","hideDate","isWithinDateRange","isSame","showAlert","hideAlert","display","SearchDropdown","dropdownId","watchId","initDropdown","attachEventListeners","getElementById","urlTemplate","searchTerm","changeLocation","fullUrl","searchDropdowns","browserUpdate"],"mappings":"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","file":"js/base-6f071d3b657faa67e84c.chunk.js","sourcesContent":["// Changes from source noted below with \"EDITED\"\nimport { fetchData } from './utils.js';\n\nexport class DropdownManager {\n\tconstructor(config = {}) {\n\t\tthis.config = {\n\t\t\tbaseURL: window.location.origin, // Default to current domain for relative paths\n\t\t\tdataSrc: '/assets/omni/data/dropdowns.json',\n\t\t\t...config,\n\t\t};\n\t\tthis.classStrings = {\n\t\t\tdropdown_menu: window.wwwJsShims.dropdowns.classes.dropdown_menu, // EDITED to use config on window object\n\t\t\tdropdown_last_item: window.wwwJsShims.dropdowns.classes.dropdown_last_item, // EDITED to use config on window object\n\t\t\tmega_menu: window.wwwJsShims.dropdowns.classes.mega_menu, // EDITED to use config on window object\n\t\t\tmega_headings: window.wwwJsShims.dropdowns.classes.mega_headings, // EDITED to use config on window object\n\t\t\tmega_heading_links: window.wwwJsShims.dropdowns.classes.mega_heading_links, // EDITED to use config on window object\n\t\t\tmega_links: window.wwwJsShims.dropdowns.classes.mega_links, // EDITED to use config on window object\n\t\t\tmega_icon_links: window.wwwJsShims.dropdowns.classes.mega_icon_links, // EDITED to use config on window object\n\t\t\tmega_link_annotation: window.wwwJsShims.dropdowns.classes.mega_link_annotation, // EDITED to use config on window object\n\t\t};\n\t\tthis.ready = new Promise(resolve => {\n\t\t\tthis.resolveReady = resolve;\n\t\t});\n\t\tthis.init();\n\t}\n\n\tasync init() {\n\t\tconst data = await fetchData(this.resolveURL(this.config.dataSrc), { timeout: 5000, retries: 3, retryDelay: 500 });\n\t\t// break if data fetch fails\n\t\tif (!data) {\n\t\t\tconsole.error('Initialization failed due to fetch error.');\n\t\t\tthis.defaultBehaviorNoDropdownData();\n\t\t\treturn;\n\t\t}\n\t\tthis.renderAllMenus(data);\n\t\tthis.resolveReady();\n\t}\n\n\tresolveURL(path) {\n\t\ttry {\n\t\t\t// If the path is already an absolute URL, the constructor will parse it successfully\n\t\t\tconst url = new URL(path);\n\t\t\t// Check if it's a valid absolute URL\n\t\t\tif (url.protocol === \"http:\" || url.protocol === \"https:\") {\n\t\t\t\treturn path; // Return the original absolute URL\n\t\t\t}\n\t\t} catch (e) {\n\t\t\t// If URL constructor fails, it's a relative path\n\t\t}\n\t\t// Combine baseURL with relative path\n\t\treturn new URL(path, this.config.baseURL).href;\n\t}\n\n\tdefaultBehaviorNoDropdownData() {\n\t\tconst dropdownToggles = document.querySelectorAll('[data-bs-toggle=\"dropdown\"]');\n\t\tdropdownToggles.forEach(toggle => {\n\t\t\ttoggle.removeAttribute('data-bs-toggle');\n\t\t\ttoggle.removeAttribute('aria-expanded');\n\t\t\tconst svg = toggle.querySelector('svg');\n\t\t\tif (svg) {\n\t\t\t\tsvg.classList.add('d-none');\n\t\t\t}\n\t\t});\n\t}\n\n\trenderAllMenus(data) {\n\t\tfor (let menu_obj in data) {\n\t\t\tfor (let obj of data[menu_obj]) {\n\t\t\t\tlet index = data[menu_obj].indexOf(obj) + 1;\n\t\t\t\tif (obj.type === 'dropdown') {\n\t\t\t\t\tthis.renderDropdownMenu(obj, menu_obj, index);\n\t\t\t\t} else if (obj.type === 'mega-menu') {\n\t\t\t\t\tthis.renderMegaMenu(obj, menu_obj, index);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trenderDropdownMenu(obj, menu_obj, index) {\n\t\tlet dropdown_config = {\n\t\t\tid: `${menu_obj}-item-${index}`,\n\t\t\tmin_width: obj.min_width,\n\t\t\tchildren: obj.items\n\t\t};\n\t\tlet rendered = this.templates.renderDropdownMenu(dropdown_config, this.classStrings);\n\t\tthis.insertRenderedMenu(rendered, dropdown_config.id);\n\t}\n\n\trenderMegaMenu(obj, menu_obj, index) {\n\t\tlet dropdown_config = {\n\t\t\tid: `${menu_obj}-item-${index}`,\n\t\t\tobj: obj,\n\t\t};\n\t\tlet rendered = this.templates.renderMegaMenu(dropdown_config, this.classStrings);\n\t\tthis.insertRenderedMenu(rendered, dropdown_config.id);\n\t}\n\n\tinsertRenderedMenu(rendered, id) {\n\t\tlet insertion_point = document.querySelector(`#${id}-dropdown`);\n\t\t// test for existence of insertion point\n\t\tif (!insertion_point) {\n\t\t\treturn;\n\t\t}\n\t\tinsertion_point.insertAdjacentHTML('beforeend', rendered);\n\t\t//this.initNewTooltip(insertion_point);\n\t}\n\n\tinitNewTooltip(element) {\n\t\tconst tooltips = element.querySelectorAll('[data-bs-toggle=\"tooltip\"]');\n\t\ttooltips.forEach(tooltip => new bootstrap.Tooltip(tooltip));\n\t}\n\n\ttemplates = {\n\t\trenderDivider: (item) => {\n\t\t\tconst divider = `<li class=\"${item.classes ? item.classes : ''}\"\n                           id=\"${item.parent_id}-dropdown-item-${item.index}\">\n                           <hr class=\"dropdown-divider\">\n                       </li>`;\n\t\t\treturn divider;\n\t\t},\n\t\trenderIcon: (icon) => {\n\t\t\tconst span = icon ? `<span class=\"${icon}\"></span>` : '';\n\t\t\treturn span;\n\t\t},\n\t\trenderInfoIcons: (item) => {\n\t\t\tlet icons = \"\";\n\t\t\tfor (let icon of item.icons) {\n\t\t\t\ticon.text = icon.text ? icon.text : item.parent_text;\n\n\t\t\t\tif(icon.url){\n\t\t\t\t\tlet url = this.resolveURL(icon.url);\n\t\t\t\t\ticons += `<a href=\"${url}\"\n                         class=\"${this.classStrings.mega_icon_links}\"\n                         data-bs-toggle=\"tooltip\"\n                         data-bs-title=\"${icon.text}\">\n                        <span class=\"${icon.icon_classes} fa-fw\"></span>\n                      </a>`;\n\t\t\t\t} else {\n\t\t\t\t\ticons += `<span data-bs-toggle=\"tooltip\"\n                          data-bs-title=\"${icon.text}\">\n                        <span class=\"${icon.icon_classes} text-body-tertiary fa-fw\"></span>\n                      </span>`;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn icons;\n\t\t},\n\t\trenderAnnotation: (item) => {\n\t\t\tconst annotation = `<div class=\"${this.classStrings.mega_link_annotation}\">\n                            ${item.annotation}\n                          </div>`;\n\t\t\treturn annotation;\n\t\t},\n\t\trenderText: (item) => {\n\t\t\t// null values to empty string\n\t\t\titem.classes = item.classes ? item.classes : '';\n\t\t\t// append classStrings.dropdown_last_item to the last item in the list\n\t\t\titem.classes += item.is_last ? ` ${this.classStrings.dropdown_last_item}` : '';\n\t\t\tconst text = `<li class=\"text-unset px-3 fw-light ${item.classes}\"\n                       id=\"${item.parent_id}-dropdown-item-${item.index}\">\n                         ${this.templates.renderIcon(item.icon_classes)}\n                         ${item.text}\n                     </li>`;\n\t\t\treturn text;\n\t\t},\n\t\trenderDropdownLink: (item) => {\n\t\t\t// null values to empty string\n\t\t\titem.classes = item.classes ? item.classes : '';\n\t\t\t// append classStrings.dropdown_last_item to the last item in the list\n\t\t\titem.classes += item.is_last ? ` ${this.classStrings.dropdown_last_item}` : '';\n\t\t\tconst url = this.resolveURL(item.url);\n\t\t\tconst link = `<li id=\"${item.parent_id}-dropdown-item-${item.index}\">\n                        <a href=\"${url}\"\n                         class=\"dropdown-item darker ${item.classes}\">\n                          ${this.templates.renderIcon(item.icon_classes)}\n                          ${item.text}\n                        </a>\n                    </li>`;\n\t\t\treturn link;\n\t\t},\n\t\trenderHours: (item) => {\n\t\t\t// null values to empty string\n\t\t\titem.classes = item.classes ? item.classes : '';\n\t\t\t// append classStrings.dropdown_last_item to the last item in the list\n\t\t\titem.classes += item.is_last ? ` ${this.classStrings.dropdown_last_item}` : '';\n\t\t\tconst url = this.resolveURL(item.url);\n\t\t\tconst elem = `<li id=\"${item.parent_id}-dropdown-item-${item.index}\">\n                        <a href=\"${url}\"\n                          class=\"dropdown-item darker d-flex justify-content-between ${item.classes}\">\n                          <span>\n                            ${this.templates.renderIcon(item.icon_classes)}\n                            ${item.text}\n                          </span>\n                          <span class=\"live-hours badge text-secondary-emphasis small\"\n                              data-untl-hours-id=\"${item.cid}\"></span>\n                        </a>\n                    </li>`;\n\t\t\treturn elem;\n\t\t},\n\t\trenderMegaMenuLink: (item) => {\n\t\t\t// null values to empty string\n\t\t\titem.classes = item.classes ? item.classes : '';\n\t\t\titem.caret_icon = item.caret_icon ? item.caret_icon : 'fas fa-caret-right text-success';\n\t\t\titem.parent_text = item.text;\n\t\t\tconst info_icons = item.icons ? this.templates.renderInfoIcons(item) : '';\n\t\t\tconst annotation = item.annotation ? this.templates.renderAnnotation(item) : '';\n\t\t\tconst url = this.resolveURL(item.url);\n\t\t\tlet inner = \"\";\n\t\t\tif (info_icons === \"\") {\n\t\t\t\tinner = `<a href=\"${url}\"\n                    class=\"${this.classStrings.mega_links} ${item.classes}\">\n                  ${this.templates.renderIcon(item.icon_classes)}\n                  ${item.text}\n                 </a>`;\n\t\t\t} else {\n\t\t\t\tinner = `<span class=\"d-flex justify-content-between\">\n                  <a href=\"${url}\"\n                     class=\"flex-fill ${this.classStrings.mega_links} ${item.classes}\">\n                      ${this.templates.renderIcon(item.icon_classes)}\n                      ${item.text}\n                  </a>\n                  <span>\n                      ${info_icons}\n                  </span>\n                </span>`;\n\t\t\t}\n\t\t\tconst link = `<li class=\"mb-2 py-1\">\n                    <span class=\"fa-li d-none d-md-block\">\n                      <span class=\"${item.caret_icon}\"></span>\n                    </span>\n                    ${inner}\n                    ${annotation}\n                </li>`;\n\t\t\treturn link;\n\t\t},\n\t\trenderMegaMenuList: (group_obj) => {\n\t\t\tlet list = '';\n\t\t\tfor (let item of group_obj.items) {\n\t\t\t\t// the forloop index + 1\n\t\t\t\titem.index = group_obj.items.indexOf(item) + 1;\n\t\t\t\titem.parent_id = group_obj.id;\n\t\t\t\tif (item.type === 'divider') {\n\t\t\t\t\tlist += this.templates.renderDivider(item);\n\t\t\t\t}\n\t\t\t\telse if (item.type === 'link') {\n\t\t\t\t\tlist += this.templates.renderMegaMenuLink(item);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn list;\n\t\t},\n\t\trenderDropdownList: (entry) => {\n\t\t\tlet list = '';\n\t\t\tfor (let item of entry.children) {\n\t\t\t\t// the forloop index + 1\n\t\t\t\titem.index = entry.children.indexOf(item) + 1;\n\t\t\t\titem.parent_id = entry.id;\n\t\t\t\t// set a boolean value to indicate if this is the last item in the list\n\t\t\t\titem.is_last = item.index === entry.children.length;\n\t\t\t\tif (item.type === 'divider') {\n\t\t\t\t\tlist += this.templates.renderDivider(item);\n\t\t\t\t}\n\t\t\t\telse if (item.type === 'text') {\n\t\t\t\t\tlist += this.templates.renderText(item);\n\t\t\t\t}\n\t\t\t\telse if (item.type === 'link') {\n\t\t\t\t\tlist += this.templates.renderDropdownLink(item);\n\t\t\t\t}\n\t\t\t\telse if (item.type === 'hour') {\n\t\t\t\t\tlist += this.templates.renderHours(item);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn list;\n\t\t},\n\t\trenderDropdownMenu: (entry) => {\n\t\t\tconst minWidth = entry.min_width ? `style=\"min-width:${entry.min_width}px;\"` : '';\n\t\t\tconst menu = `<ul class=\"${this.classStrings.dropdown_menu}\" ${minWidth}\n                      id=\"${entry.id}-dropdown-menu\">\n                      ${this.templates.renderDropdownList(entry)}\n                    </ul>`;\n\t\t\treturn menu;\n\t\t},\n\t\trenderGroupHeading: (group_obj) => {\n\t\t\tconst inner_content = group_obj.url ? `<a href=\"${this.resolveURL(group_obj.url)}\" class=\"${this.classStrings.mega_heading_links}\">${group_obj.heading}</a>` : `${group_obj.heading}`;\n\t\t\tconst heading = `<h3 class=\"${this.classStrings.mega_headings}\">\n                         ${inner_content}\n                       </h3>`;\n\t\t\treturn heading;\n\t\t},\n\t\trenderGroupList: (group_obj) => {\n\t\t\tgroup_obj.classes = 'test_string';\n\t\t\tconst list = `<ul class=\"fa-ul ms-0 ms-lg-4 mb-0 mb-lg-2 ${group_obj.classes || \"\"}\">\n                      ${this.templates.renderMegaMenuList(group_obj)}\n                    </ul>`;\n\t\t\treturn list;\n\t\t},\n\t\trenderColumnGroup: (group_obj) => {\n\t\t\tconst column = `<div class=\"m-lg-3 mx-0 my-3 ${group_obj.classes || \"\" }\">\n                          ${group_obj.heading ? this.templates.renderGroupHeading(group_obj) : ''}\n                          ${group_obj.items ? this.templates.renderGroupList(group_obj) : ''}\n                      </div>`;\n\t\t\treturn column;\n\t\t},\n\t\trenderMegaColumnGroups: (column_obj) => {\n\t\t\tlet groups = '';\n\t\t\tfor (let group_obj of column_obj.groups) {\n\t\t\t\tgroups += this.templates.renderColumnGroup(group_obj);\n\t\t\t}\n\t\t\treturn groups;\n\t\t},\n\t\trenderMegaColumn: (column_obj) => {\n\t\t\tconst column = `<div class=\"${column_obj.classes}\">\n                          ${this.templates.renderMegaColumnGroups(column_obj)}\n                      </div>`;\n\t\t\treturn column;\n\t\t},\n\t\trenderColumns: (columns) => {\n\t\t\tlet cols = '';\n\t\t\tfor (let column_obj of columns) {\n\t\t\t\tcols += this.templates.renderMegaColumn(column_obj);\n\t\t\t}\n\t\t\treturn cols;\n\t\t},\n\t\trenderMegaMenu: (entry) => {\n\t\t\tconst menu = `<div class=\"${this.classStrings.mega_menu}\"\n                         id=\"${entry.id}-dropdown-menu\">\n                      <div class=\"container-fluid\">\n                        <div class=\"container-3xl px-0 px-lg-2\">\n                          <div class=\"row g-2\">\n                            ${this.templates.renderColumns(entry.obj.columns)}\n                          </div>\n                        </div>\n                      </div>\n                    </div>`;\n\t\t\treturn menu;\n\t\t}\n\t}\n}\n","// utils.js\n\n/**\n * Stores a value with an expiration time into either localStorage or sessionStorage.\n *\n * @param {string} key - The key under which the value will be stored.\n * @param {any} value - The value to be stored.\n * @param {number} ttl - Time to live in milliseconds.\n * @param {string} version - The version of the data being stored. Allows for forced updates.\n * @param {boolean} useSessionStorage - If true, uses sessionStorage instead of localStorage.\n */\nexport function setWithExpiry(key, value, ttl, version, useSessionStorage = false) {\n  const now = new Date();\n  const item = {\n    value: value,\n    expiry: now.getTime() + ttl,\n    version: version\n  };\n  const storage = useSessionStorage ? sessionStorage : localStorage;\n  storage.setItem(key, JSON.stringify(item));\n}\n\n/**\n * Retrieves an item from either localStorage or sessionStorage if it hasn't expired or the version string is mismatched.\n *\n * @param {string} key - The key of the item to retrieve.\n * @param {string} version - The version of the data being stored. Allows for forced updates.\n * @param {boolean} useSessionStorage - If true, uses sessionStorage instead of localStorage.\n * @returns {any|null} Returns the stored item if it hasn't expired; otherwise, null.\n */\nexport function getWithExpiry(key, version, useSessionStorage = false) {\n  const storage = useSessionStorage ? sessionStorage : localStorage;\n  const itemStr = storage.getItem(key);\n  if (!itemStr) {\n    return null;\n  }\n  const item = JSON.parse(itemStr);\n  const now = new Date();\n  if (now.getTime() > item.expiry || item.version !== version) {\n    storage.removeItem(key);\n    return null;\n  }\n  return item.value;\n}\n\n/**\n * Fetch data from a specified source with error handling, timeout, and retry logic.\n * @param {string} url - The URL to fetch data from.\n * @param {object} [options={}] - Configuration options\n * @param {number} [options.timeout=5000] - Timeout in milliseconds.\n * @param {number} [options.retries=0] - Number of retry attempts before giving up.\n * @param {number} [options.retryDelay=500] - Initial delay between retries in milliseconds (will use exponential backoff).\n * @returns {Promise<object[]|false>} - Promise resolving to parsed JSON or false if all attempts fail.\n */\nexport async function fetchData(url, { timeout = 5000, retries = 0, retryDelay = 500 } = {}) {\n  for (let attempt = 0; attempt <= retries; attempt++) {\n    let controller = new AbortController();\n    let timeoutId = setTimeout(() => controller.abort(), timeout);\n    let shouldRetry = attempt < retries; // Determine if we can attempt another retry after this one\n\n    try {\n      const response = await fetch(url, { signal: controller.signal });\n      clearTimeout(timeoutId);\n\n      if (!response.ok) {\n        console.error(`HTTP error! Status: ${response.status}`);\n        if (shouldRetry) {\n          await delay(retryDelay * (2 ** attempt)); // Exponential backoff\n          continue; // Retry the fetch\n        } else {\n          return false;\n        }\n      }\n\n      return await response.json();\n    } catch (error) {\n      clearTimeout(timeoutId);\n      console.error('Fetch error:', error.message);\n\n      // If this was our last attempt, return false\n      if (!shouldRetry) {\n        return false;\n      }\n\n      // Otherwise, wait before retrying\n      await delay(retryDelay * (2 ** attempt));\n    }\n  }\n\n  // If we exit the loop without returning, something unexpected happened\n  return false;\n}\n\nfunction delay(ms) {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\n\nexport function makeSVG(useID, title = '') {\n  const svgNS = \"http://www.w3.org/2000/svg\";\n  const xlinkNS = \"http://www.w3.org/1999/xlink\";\n  const svg = document.createElementNS(svgNS, 'svg');\n  svg.setAttribute('class', \"svg-inline--fa fa-lg fa-fw\");\n  svg.setAttribute('role', 'img');\n  svg.setAttribute('xmlns', svgNS);\n  svg.setAttribute('viewBox', \"0 0 640 640\");\n  const titleEl = document.createElementNS(svgNS, 'title');\n  titleEl.textContent = title;\n  const use = document.createElementNS(svgNS, 'use');\n  use.setAttributeNS(xlinkNS, 'xlink:href', `#${useID}`);\n  svg.appendChild(titleEl);\n  svg.appendChild(use);\n  return svg;\n\n}\n\n/**\n * Toggle an input between type password and text based on button click.\n */\nexport function togglePasswordVisibility() {\n  const togglePasswordButtons = document.querySelectorAll('[data-untl-toggle=\"password\"]');\n  if (!togglePasswordButtons.length) {\n    return;\n  }\n  togglePasswordButtons.forEach(button => {\n    button.addEventListener('click', () => {\n      const target = button.getAttribute('data-untl-target');\n      const input = document.querySelector(target);\n      const icon = button.querySelector('svg');\n      const type = input.getAttribute('type') === 'password' ? 'text' : 'password';\n      input.setAttribute('type', type);\n      // set the class of the button to eye-slash if password and eye if text\n      icon.classList.toggle('fa-eye-slash', type === 'text');\n      icon.classList.toggle('fa-eye', type === 'password');\n    });\n  });\n}\n/**\n * Utility function to wait for DOM ready.\n * Executes the callback when the DOM is fully loaded.\n * @param {Function} callback\n */\nexport function onDomReady(callback) {\n  if (document.readyState !== 'loading') {\n    callback();\n  } else {\n    document.addEventListener('DOMContentLoaded', callback);\n  }\n}\n\n/**\n * Opt-in for Bootstrap tooltips.\n * Binds to a wrapper element, allowing for dynamic content insertion.\n * @param {string} selector - The CSS selector for tooltips.\n */\nexport function optInTooltips(selector = '[data-bs-toggle=\"tooltip\"]') {\n  if (typeof bootstrap === 'undefined') {\n    console.warn('Bootstrap is not defined.');\n    return;\n  }\n  new bootstrap.Tooltip('body', { selector });\n}\n\n/**\n * Opt-in for Bootstrap popovers.\n * Binds to a wrapper element, allowing for dynamic content insertion.\n * @param {string} container - The container selector for popovers.\n * @param {string} selector - The CSS selector for popovers.\n */\nexport function optInPopovers(\n  container = '#main-container',\n  selector = '[data-bs-toggle=\"popover\"]'\n) {\n  if (typeof bootstrap === 'undefined') {\n    console.warn('Bootstrap is not defined.');\n    return;\n  }\n  new bootstrap.Popover(container, { selector });\n}\n","import { onDomReady } from './utils.js';\n\nexport class OffCanvasSearch {\n  constructor() {\n    this.offCanvasSearch = document.querySelector('#offcanvas-search');\n    this.offCanvasSearchInput = document.querySelector(\"#bento-offcanvas-q\");\n    this.offCanvasSearchTrigger = document.querySelector('button.top-bar-item[data-bs-target=\"#offcanvas-search\"]');\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n\n  init() {\n    this.focusOnRevealedSearchInput();\n    this.setupEventListeners();\n  }\n\n  focusOnRevealedSearchInput() {\n    this.offCanvasSearch.addEventListener('shown.bs.collapse', event => {\n      this.offCanvasSearchInput.focus();\n    });\n  }\n\n  handleEscapeKey(event) {\n    if (event.key === 'Escape') {\n      const searchWrapper = bootstrap.Collapse.getOrCreateInstance('#offcanvas-search');\n      searchWrapper.hide();\n    }\n  }\n\n  handleClickOutside(event) {\n    if (!this.offCanvasSearch.contains(event.target)) {\n      const searchWrapper = bootstrap.Collapse.getOrCreateInstance('#offcanvas-search');\n      searchWrapper.hide();\n    }\n  }\n\n  handleSlashKey(event) {\n    if (event.key !== '/') {\n      return;\n    }\n    // Check if the key pressed is \"/\" and the target is not an input or textarea\n    if (event.key === '/' && !['INPUT', 'TEXTAREA'].includes(event.target.tagName)) {\n      event.preventDefault(); // Prevent default behavior (like quick find in Firefox)\n      const searchWrapper = bootstrap.Collapse.getOrCreateInstance('#offcanvas-search');\n      searchWrapper.show();\n    }\n  }\n\n  setupEventListeners() {\n    this.offCanvasSearch.addEventListener('show.bs.collapse', () => {\n      this.offCanvasSearchTrigger.classList.add(\"dashed-focus\");\n      document.addEventListener('keydown', this.handleEscapeKey.bind(this));\n      document.addEventListener('click', this.handleClickOutside.bind(this), true);\n    });\n\n    this.offCanvasSearch.addEventListener('hidden.bs.collapse', () => {\n      this.offCanvasSearchTrigger.classList.remove(\"dashed-focus\");\n      document.removeEventListener('keydown', this.handleEscapeKey.bind(this));\n      document.removeEventListener('click', this.handleClickOutside.bind(this), true);\n      this.offCanvasSearchTrigger.focus();\n    });\n    document.addEventListener('keydown', this.handleSlashKey.bind(this));\n  }\n\n}\n","import { onDomReady } from './utils.js';\n\nexport class IdentifierHelper {\n  constructor() {\n    this.doiRegex = /10.\\d{4,9}\\/[-._;()\\/:A-Z0-9]+/ig;\n    this.urlRegex = /https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)/;\n    this.proxyUrl = 'https://libproxy.library.unt.edu:443/login?url=';\n    this.urls = {\n      ill: 'https://library.unt.edu/services/ill-document-delivery/',\n      proxyTools: 'https://library.unt.edu/proxy-tools',\n    };\n    this.proxyString = (queryString, prxyTyp) => {\n      const doiPrefix = prxyTyp === 'doi' ? 'https://doi.org/' : '';\n      return `${this.proxyUrl}${doiPrefix}${queryString}`;\n    };\n    this.openDOIString = (doi) => {\n      return `https://unpaywall.org/${doi}`;\n    };\n    this.summonDOIString = (doi) => {\n      return `https://untexas.summon.serialssolutions.com/search#!/search?ho=f&l=en&q=(DOI:(${doi}))`;\n    };\n    this.illiadURLString = (queryString, prxyTyp) => {\n      if (prxyTyp === 'url') {\n        queryString = encodeURIComponent(queryString);\n      }\n      const queryArgs = prxyTyp === 'doi' ? `&doi=${queryString}` : `&notes=${queryString}`;\n      return `https://ill.library.unt.edu/illiad/illiad.dll?Action=10&Form=22${queryArgs}`;\n    };\n    this.selectors = {\n      doiInputs: document.querySelectorAll(\"input.proxyable-target\"),\n      doiText: document.querySelector('#proxyable-string'),\n    };\n    this.strings = {\n      doi: {\n        tooltip: 'DOI found. See options.',\n        header: 'Accessing Items with a Digital Object Identifier (DOI)',\n        label: 'We think your search contains a DOI:',\n        instructions: 'Try these first:',\n        note: 'Note: you may be directed to a login prompt first.',\n        ifPaywall: 'If you hit a paywall:',\n        endMsg: '*DOI not valid? Look for spaces, trailing punctuation, and/or check your source.',\n        directAccess: '1. Direct Access',\n        doiOpenAccess: '2. Find Open Access Copy',\n        summon: `3. Try this search, then look for the buttons <strong>\"Find It\"</strong> or\n                 <strong>\"Check More Sources\"</strong> on the results\n                 page, and follow those links.`,\n        illiad: '4. Submit an Interlibrary Loan request.',\n        illiadNote: `Items not found in a subscription services can\n               typically be requested and delivered to you.`,\n        externalLinkText: 'opens in a new tab',\n      },\n      url: {\n        tooltip: 'URL found. See options.',\n        header: 'Getting Access to Paywalled Content',\n        label: 'Your search contains a URL:',\n        instructions: `Never pay for articles! The library\n                       can usually get them to you for free.*`,\n        note: `You will be routed through a library\n               login prompt. If we subscribe to the source, you should\n               have instant access. Note: If we don't subscribe to the source,\n               you'll hit a white screen and will need to try a different option.`,\n        ifPaywall: null,\n        endMsg: '*Current Students, Faculty, & Staff.',\n        directAccess: `1. Try to \"Proxify\" the URL. `,\n        doiOpenAccess: 'Find Open Access Copy',\n        summon: null,\n        illiad: '2. Submit an Interlibrary Loan request.',\n        illiadNote: `Items not found in a subscription services can\n                     typically be requested and delivered to you. You will\n                     need to provide publication details on the request form.`,\n        externalLinkText: 'opens in a new tab',\n      },\n    }\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n  buildModal(prxyStr, prxyTyp) {\n    const existingModal = document.querySelector('#proxyable-modal');\n    if (existingModal) {\n      this.destroyModal(existingModal);\n    }\n    const strings = prxyTyp === 'doi' ? this.strings.doi : this.strings.url;\n    const classes = {\n      listGroupItems: [\"list-group-item\", \"list-group-item-action\", \"link-success\"],\n      listGroupItemFlex: [\"d-flex\", \"justify-content-between\", \"align-items-center\"],\n      externalLink: [\"far\", \"fa-external-link\"],\n      listGroupItemBold: [\"list-group-item\", \"fw-bold\"],\n    };\n    const search = () => {\n      return `<div class=\"list-group-item\">\n                <div class=\"form-floating mb-3\">\n                 <input class=\"form-control fw-bolder\"\n                     id=\"proxyable-string\"\n                     value=\"${ prxyStr }\" />\n                 <label class=\"fw-light\"\n                     for=\"proxyable-string\">\n                  ${ strings.label }\n                 </label>\n                </div>\n              </div>`;\n    };\n    const instruct = () => {\n      return `<div class=\"${ classes.listGroupItemBold.join(' ') }\">\n            ${ strings.instructions }\n          </div>`;\n    };\n    const directAccess = () => {\n      return `<a id=\"proxyable-direct-access\"\n                 target=\"_blank\"\n                 class=\"${ classes.listGroupItems.join(' ') }\"\n                 href=\"${ this.proxyString(prxyStr, prxyTyp) } \">\n               <div class=\"${ classes.listGroupItemFlex.join(' ') } pb-1\">\n                ${ strings.directAccess }\n                <span class=\"${ classes.externalLink.join(' ') }\"\n                   title=\"${ strings.externalLinkText }\"></span>\n               </div>\n               <div class=\"small fst-italic text-secondary\">\n                ${ strings.note }\n               </div>\n              </a>`;\n    };\n    const doiOpenAccess = () => {\n      return `<a id=\"doi-open-access\"\n                 target=\"_blank\"\n                 class=\"${ classes.listGroupItems.join(' ') } ${ classes.listGroupItemFlex.join(' ') }\"\n                 href=\"${ this.openDOIString(prxyStr) }\">\n                ${ strings.doiOpenAccess }\n                <span class=\"${ classes.externalLink.join(' ') }\"\n                   title=\"${ strings.externalLinkText }\"></span>\n              </a>`;\n    };\n    const summonFallback = () => {\n      return `<a id=\"doi-summon-access\"\n                 target=\"_blank\"\n                 class=\"${ classes.listGroupItems.join(' ') } ${ classes.listGroupItemFlex.join(' ') }\"\n                 href=\"${ this.summonDOIString(prxyStr) }\">\n                <span>${ strings.summon }</span>\n                <span class=\"${ classes.externalLink.join(' ') }\"\n                   title=\"${ strings.externalLinkText }\"></span>\n              </a>`;\n    };\n    const illiadFallback = () => {\n      return `<a id=\"proxyable-illiad-access\"\n                 target=\"_blank\"\n                 class=\"${ classes.listGroupItems.join(' ') }\"\n                 href=\"${ this.illiadURLString(prxyStr, prxyTyp) }\">\n                <div class=\"${ classes.listGroupItemFlex.join(' ') } pb-1\">\n                  <span>${ strings.illiad }</span>\n                  <span class=\"${ classes.externalLink.join(' ') }\"\n                     title=\"${ strings.externalLinkText }\"></span>\n                </div>\n                <div class=\"small fst-italic text-secondary\">\n                  ${ strings.illiadNote }\n                </div>\n              </a>`;\n    };\n    const learnMore = () => {\n      return `<div class=\"list-group-item ${ classes.listGroupItemFlex.join(' ') } pb-1\">\n                <span>\n                 For additional help see:\n                 <a class=\"link-success\"\n                  target=\"_blank\"\n                  href=\"${this.urls.ill}\">\n                  Interlibrary Loan</a>,\n                 <a class=\"link-success\"\n                  target=\"_blank\"\n                  href=\"${this.urls.proxyTools}\">Proxy Tools</a>.\n                </span>\n              </div>`;\n    };\n    const ifPaywall = () => {\n      return `<div class=\"${ classes.listGroupItemBold.join(' ') }\">\n                ${ strings.ifPaywall }\n              </div>`;\n    };\n    const endMsg = () => {\n      return `<div class=\"list-group-item font-italic\">\n                ${ strings.endMsg }\n              </div>`;\n    };\n    const makeBody = () => {\n      const isDoi = prxyTyp === 'doi';\n      return `<div class=\"modal-body\">\n                <div class=\"list-group list-group-flush\">\n                  ${search()}\n                  ${instruct()}\n                  ${directAccess()}\n                  ${isDoi ? doiOpenAccess() : '' }\n                  ${isDoi ? ifPaywall() : '' }\n                  ${isDoi ? summonFallback() : '' }\n                  ${illiadFallback()}\n                  ${learnMore()}\n                  ${endMsg()}\n                </div>\n              </div>`;\n    };\n    const makeHeader = () => {\n      return `<div class=\"modal-header\">\n                <h5 class=\"modal-title\">\n                 ${ strings.header }\n                </h5>\n                <button type=\"button\"\n                    class=\"btn-close\"\n                    data-bs-dismiss=\"modal\"\n                    aria-label=\"Close\"></button>\n              </div>`;\n    };\n    const makeDialogEl = () => {\n      return `<div class=\"modal-dialog modal-lg modal-dialog-centered\">\n                <div class=\"modal-content\">\n                  ${ makeHeader() }\n                  ${ makeBody() }\n                </div>\n              </div>`;\n    };\n    const makeModal = () => {\n      const body = document.querySelector('body');\n      const fragement = document.createDocumentFragment();\n      const modalEl = document.createElement('div');\n      modalEl.classList.add('modal', 'fade');\n      modalEl.setAttribute('id', 'proxyable-modal');\n      modalEl.setAttribute('tabindex', '-1');\n      modalEl.setAttribute('aria-hidden', 'true');\n      modalEl.innerHTML = makeDialogEl();\n      fragement.appendChild(modalEl);\n      body.appendChild(fragement);\n      return modalEl;\n    };\n    return makeModal();\n  }\n\n  init() {\n    // exit early if no doiInputs\n    if (this.selectors.doiInputs.length === 0) {\n      return;\n    }\n    this.selectors.doiInputs.forEach((input) => {\n      input.addEventListener(\"paste\", (e) => {\n        const pasted = (e.clipboardData || window.clipboardData).getData(\"text\");\n        this.handlePaste(pasted, input);\n      });\n    });\n  }\n\n  initializeModalEventListeners(modalEl, prxyStr, prxyTyp) {\n    const modalInput = modalEl.querySelector('#proxyable-string');\n    const proxyableDirectAccess = modalEl.querySelector(\"#proxyable-direct-access\");\n    const doiOpenAccess = modalEl.querySelector(\"#doi-open-access\");\n    const doiSummonAccess = modalEl.querySelector(\"#doi-summon-access\");\n    const itemIlliadAccess = modalEl.querySelector(\"#proxyable-illiad-access\");\n    modalEl.addEventListener('hidden.bs.modal', () => {\n      this.destroyModal(modalEl);\n      const initiatedItem = document.querySelector('.initiated');\n      this.resetModal(initiatedItem);\n      initiatedItem.focus();\n      initiatedItem.classList.remove('initiated');\n    });\n    modalInput.addEventListener('input', (event) => {\n      const inputStr = event.target.value;\n      if (proxyableDirectAccess) {\n        proxyableDirectAccess.setAttribute( 'href', this.proxyString(inputStr, prxyTyp) );\n      }\n      if (doiOpenAccess) {\n        doiOpenAccess.setAttribute( 'href', this.openDOIString(inputStr) );\n      }\n      if (doiSummonAccess) {\n        doiSummonAccess.setAttribute( 'href', this.summonDOIString(inputStr) );\n      }\n      if (itemIlliadAccess) {\n        itemIlliadAccess.setAttribute( 'href', this.illiadURLString(inputStr, prxyTyp) );\n      }\n    });\n  }\n\n  handleIcon(input, modalEl) {\n    const closestForm = input.closest('form');\n    const icon = closestForm.querySelector('.proxyable-icon');\n    const modal = bootstrap.Modal.getOrCreateInstance(modalEl);\n    icon.removeEventListener('click', icon.clickHandler);\n    icon.clickHandler = () => {\n      const modal = bootstrap.Modal.getOrCreateInstance(modalEl);\n      modal.show();\n    };\n    icon.addEventListener('click', icon.clickHandler);\n    icon.classList.remove(\"d-none\");\n    input.classList.add(\"initiated\");\n    return icon;\n  }\n\n  handlePaste(value, input) {\n    value = value.trim();\n    value = value.replace(/%2f/gi, '/');\n    let match = value.match(this.doiRegex) || value.match(this.urlRegex);\n    if (match) {\n      const prxyStr = match[0];\n      const prxyTyp = value.match(this.doiRegex) ? 'doi' : 'url';\n      const modalEl = this.buildModal(prxyStr, prxyTyp);\n      const icon = this.handleIcon(input, modalEl);\n      this.showTooltip(icon, prxyTyp);\n      this.initializeModalEventListeners(modalEl, prxyStr, prxyTyp);\n    } else {\n      this.resetModal(input);\n    }\n  }\n\n  destroyModal(modalEl) {\n    if (!modalEl) return;\n    const modalInstance = bootstrap.Modal.getInstance(modalEl);\n    if (modalInstance) {\n      modalInstance.dispose();\n    }\n    modalEl.remove();\n  }\n\n  showTooltip(icon, prxyTyp) {\n    const text = prxyTyp === 'doi' ? this.strings.doi.tooltip : this.strings.url.tooltip;\n    let tooltip = new bootstrap.Tooltip(icon, {\n      trigger: 'manual',\n      title: text,\n    });\n    tooltip.show();\n    setTimeout(() => icon.focus(), 100);\n    icon.addEventListener('blur', () => tooltip.hide());\n  }\n\n  resetModal(input) {\n    const closestForm = input.closest('form');\n    const icon = closestForm.querySelector('.proxyable-icon');\n    icon.classList.add(\"d-none\");\n    let tooltip = bootstrap.Tooltip.getInstance(icon);\n    if (tooltip) {\n      tooltip.hide();\n    }\n  }\n}\n","import browserUpdate from 'browser-update';\n\nconst config = {\n  required: {\n    e: -3,\n    f: -3,\n    o: -3,\n    s: -1,\n    c: -3,\n  },\n  insecure: true,\n  unsupported: true,\n  text: {\n    msg: 'Your web browser ({brow_name}) is out of date.',\n    msgmore: 'Some features on this site may not work.',\n    bupdate: 'Update browser',\n    bignore: 'Ignore',\n    remind: 'You will be reminded in {days} days.',\n    bnever: 'Never show again',\n  },\n  style: 'bottom',\n  onshow(info: { browser: { t: string; }; }) {\n    console.log(`Browser ${info.browser.t} is out of date.`);\n  },\n};\n\nexport default function runBrowserUpdate() {\n  // @ts-ignore\n  browserUpdate(config);\n}\n","// Changes from source noted below with \"EDITED\"\nimport dayjs from 'dayjs'; // EDITED to import dayjs\nimport utc from 'dayjs/plugin/utc'; // EDITED to import utc\nimport timezone from 'dayjs/plugin/timezone'; // EDITED to import timezone\nimport { setWithExpiry, getWithExpiry, fetchData } from './utils.js';\nimport { DropdownManager } from './display.dropdowns.js';\n\n// Extend day.js with the plugins\ndayjs.extend(utc); // EDITED to extend utc\ndayjs.extend(timezone); // EDITED to extend timezone\n\n/**\n * Manages operational hours fetched from the LibCal API, updates DOM elements, and constructs weekly tables.\n */\nexport class LibraryHours {\n    constructor(dropdownManager) {\n      this.config = window.wwwJsShims.hours; // EDITED to use config on window object\n      this.currentVersion = 'v1.1.1'; // Update this version as needed to force resets\n      dropdownManager.ready.then(() => this.init());\n    }\n\n  async init() {\n    let storedHours = getWithExpiry(this.config.storageKey, this.currentVersion, true); // Attempt to get stored hours from session storage\n    if (storedHours === null) {\n      storedHours = await this.fetchHours(); // Fetch and store hours if not present\n    }\n    this.setTodaysDate();\n    this.setHTMLElementHours(storedHours.week); // Update the DOM using today's hours\n    this.generateWeeklyTable(storedHours.week); // Generate weekly hours table if applicable\n  }\n\n  setHTMLElementHours(weekHours) {\n    const liveHoursElements = document.querySelectorAll(\".live-hours\");\n    liveHoursElements.forEach(element => {\n      const id = parseInt(element.getAttribute('data-untl-hours-id'), 10);\n      // get the hours for the first day\n\n      const hoursData = weekHours.find(item => item.lid === id).days[0];\n      if (hoursData) {\n        element.textContent = hoursData.time; // Update the text content with the hours\n      }\n      else {\n        element.textContent = \"Call\";\n      }\n    });\n  }\n\n  setTodaysDate() {\n    const todayElement = document.querySelector('#todays-date');\n    if (todayElement) {\n      todayElement.textContent = dayjs().tz('America/Chicago').format('dddd, MMMM D.');\n    }\n  }\n\n  async fetchHours() {\n    const spacesArray = this.config.spaces.map(space => space.cid);\n    //const url = `${this.config.calendarUrl}${this.config.endPoint}${spacesArray.join(',')}?key=${this.config.pubKey}`;\n    const url = `${this.config.calendarUrl}${this.config.endPoint}${spacesArray.join(',')}`;\n    const today = dayjs().tz('America/Chicago');\n    const todayFormatted = today.format('YYYY-MM-DD');\n    const nextWeek = today.add(7, 'days').format('YYYY-MM-DD');\n    const hoursData = await fetchData(`${url}?from=${todayFormatted}&to=${nextWeek}`, { timeout: 30000, retries: 3, retryDelay: 500 }); // 30-second timeout // with proxy!\n\n    if (!hoursData) {\n      console.error('Failed to fetch library hours');\n      return;\n    }\n\n    let weekHours = hoursData.map(el => {\n      return {\n        lid: el.lid,\n        name: el.name,\n        days: el.dates ? Object.keys(el.dates).map(date => ({\n          date: date,\n          time: this.formatHours(el.dates[date])\n        })) : []\n      };\n    });\n\n    const todayHours = weekHours.map(location => {\n      const todayData = location.days.find(day => day.date === todayFormatted);\n      return {\n        lid: location.lid,\n        name: location.name,\n        time: todayData ? this.formatHours(todayData.time) : \"Call\"  // Provide default if no data found\n      };\n    });\n\n    const storeData = { today: todayHours, week: weekHours };\n    setWithExpiry(this.config.storageKey, storeData, this.config.expires, this.currentVersion, true);\n    console.log(\"Hours refreshed.\");\n    return storeData;\n  }\n\n  formatHours(currentDay) {\n    if (!currentDay) return \"Call\";\n    const status = currentDay.status;\n    const hours = currentDay.hours && currentDay.hours.length > 0 && currentDay.hours[0].from && currentDay.hours[0].to\n      ? `${currentDay.hours[0].from}-${currentDay.hours[0].to}`\n      : \"Call\";\n    switch (status) {\n      case \"24hours\":\n        return \"Open 24 hours\";\n      case \"ByApp\":\n        return \"By appointment\";\n      case \"closed\":\n        return \"Closed\";\n      default:\n        return hours;\n    }\n  }\n\n  generateWeeklyTable(weekHours) {\n    const hourTableDiv = document.querySelector('[data-untl-hours-table]');\n    if (!hourTableDiv) return;\n\n    const lidFilter = hourTableDiv.getAttribute('data-untl-hours-table').split(',');\n    const filteredData = weekHours.filter(hour => lidFilter.includes(String(hour.lid)));\n    const baseDate = dayjs().tz('America/Chicago');  // Starting from today\n\n    let tableHTML = '<table class=\"table table-striped table-hover\"><tr class=\"table-success\"><th>Location</th>';\n    for (let i = 0; i < 7; i++) {\n      const className = i === 0 ? 'table-dark opacity-75' : '';\n      const headerDate = baseDate.add(i, 'days');\n      tableHTML += `<th class=\"${className}\">${headerDate.format('MMM DD</br />dddd')}</th>`;\n    }\n    tableHTML += '</tr>';\n\n    filteredData.forEach(location => {\n      tableHTML += `<tr><td>${location.name}</td>`;\n      for (let i = 0; i < 7; i++) {\n        // if first iteration store classnames for today\n        const className = i === 0 ? 'bg-info bg-opacity-10' : '';\n        const dateKey = baseDate.add(i, 'days').format('YYYY-MM-DD');\n        const dayInfo = location.days.find(day => day.date === dateKey);\n        const displayTime = dayInfo ? dayInfo.time : 'Call';  // Ensuring a value is always displayed\n        tableHTML += `<td class=\"${className}\">${displayTime}</td>`;\n      }\n      tableHTML += '</tr>';\n    });\n\n    tableHTML += '</table>';\n    hourTableDiv.innerHTML = tableHTML;\n  }\n}\n","// Changes from source noted below with \"EDITED\"\n// utils.js imports\nimport { onDomReady, fetchData, setWithExpiry, getWithExpiry } from './utils.js';\nimport dayjs from 'dayjs'; // EDITED to import dayjs\nimport advancedFormat from 'dayjs/plugin/advancedFormat'; // EDITED to import advancedFormat\ndayjs.extend(advancedFormat); // EDITED to extend advancedFormat\n\nexport class EventManager {\n  constructor() {\n      this.config = window.wwwJsShims.events; // EDITED to use config on window object\n      this.now = dayjs();\n      this.currentVersion = 'v1.2'; // Update this version as needed to force resets\n      // autorun\n      onDomReady(() => {\n        this.init();\n      });\n  }\n\n  formatTimeStr = (start, end, allday) => {\n    if (allday) {\n      return 'All Day';\n    } else {\n      let startStr = dayjs(start).format('h:mm A');\n      let endStr = dayjs(end).format('h:mm A');\n      return `${startStr}-${endStr}`;\n    }\n  }\n\n  /**\n   * Fetch, filter, and return a map of events.\n   */\n  fetchAndProcessEvents = async () => {\n    const apiURL = `${this.config.apiURL}?${new URLSearchParams(this.config.apiArgs)}`;\n    const data = await fetchData(apiURL, { timeout: 5000, retries: 3, retryDelay: 500 });\n    return data.events\n      .filter(\n        event => this.isValidEvent(event) && this.isCampusIncluded(event.campus.id)\n      )\n      .map(event => ({\n        //...event,\n        title: event.title,\n        allday: event.allday || false,\n        url: event.url.public,\n        location: event.location.name || false,\n        campus: event.campus.name || false,\n        registration: event.registration || false,\n        seats: event.seats || false,\n        reoccurs_future: event.future_dates.length > 0 || false,\n        start: new Date(event.start),\n        end: event.end ? new Date(event.end) : false,\n        category: this.categorizeEvent(event)\n      }));\n  }\n\n  /**\n   * valid events must have a title, public url and not include 'private' in the title\n   */\n  isValidEvent = (event) => {\n    // Check that the event has the minimal requirements\n    if (!event.title || !event.url || !event.url.public) {\n      return false;\n    }\n    // Check these fields for ignored words\n    const fieldsToCheck = [\n      event.title,\n      event.description || '',\n      event.presenter || ''\n    ];\n    const combinedText = fieldsToCheck.join(' ').toLowerCase();\n    const ignoreList = this.config.ignore || [];\n    // Check if any of the ignore terms appear in the combined text\n    for (const term of ignoreList) {\n      if (combinedText.includes(term.toLowerCase().trim())) {\n        return false; // Found an ignored term, so this event is not valid.\n      }\n    }\n    // If we get here, no ignored terms were found\n    return true;\n  }\n\n  /**\n   * Check if the event's campus is included in the allowedCampusIds array in config\n   */\n  isCampusIncluded = (campusId) => {\n    return this.config.allowedCampusIds.includes(campusId);\n  }\n\n  /**\n   * Categorize events based on keywords in the title, description, or campus name, default to genera\n   * cats are based on values in config.categories.\n   */\n  categorizeEvent = (event) => {\n    const searchFields = [event.title, event.description, event.campus.name].join(' ').toLowerCase();\n    return Object.keys(this.config.categories).find(category =>\n      this.config.categories[category].some(keyword => searchFields.includes(keyword))\n    ) || 'general';\n  }\n\n  /**\n   * Filter events based on the provided criteria\n   */\n  filterEventsByCriteria(events, {campuses = [], categories = []}) {\n    return events.filter(event => {\n      const campusCondition = campuses.length === 0 || campuses.includes(event.campus);\n      const categoryCondition = categories.length === 0 || categories.includes(event.category);\n      return campusCondition && categoryCondition;\n    });\n  }\n\n  /**\n   * group an array of items by their start date\n   */\n  groupEventsByDate(events) {\n    return events.reduce((groupedEvents, event) => {\n      const date = dayjs(event.start).format('YYYY-MM-DD');\n      if (!groupedEvents[date]) {\n        groupedEvents[date] = [];\n      }\n      groupedEvents[date].push(event);\n      return groupedEvents;\n    }, {});\n  }\n\n  /**\n   * generate events for a single day\n   */\n  makeDay(group, date, isLastItem) {\n    const dateParts = date.split('-');\n    const month = dateParts[1];\n    // get the 3 letter version of the month\n    const monthName = dayjs(date).format('MMM');\n    const day = dateParts[2];\n    const items = group[date];\n\n    // calendar icon\n    const calendarIcon = (month, day) => {\n      return `\n        <div class=\"text-center\">\n          <span class=\"fa-layers fa-fw fa-5x d-block mb-2\">\n            <i class=\"fad fa-calendar\"></i>\n            <span class=\"fa-layers-text fa-inverse text-uppercase\"\n                  data-fa-transform=\"shrink-14 up-4\">${monthName}</span>\n            <span class=\"fa-layers-text fa-inverse\"\n                  data-fa-transform=\"shrink-8 down-3\">${day}</span>\n          </span>\n        </div>`;\n    }\n\n    // shorten titles, cause some of these things are looooong.\n    const makeShortTitle = (title) => {\n      let shortTitle = title;\n      // check if the title contains a colon\n      if (title.includes(':') && title.toLowerCase().includes('spark')) {\n        shortTitle = title.split(':')[1].trim();\n      }\n      // add spaces between / and alphanumeric characters to prevent runons\n      if (shortTitle.includes('/')) {\n        shortTitle = shortTitle.replace(/\\/([a-zA-Z0-9])/g, '/ $1');\n      }\n      // truncate and add ellipsis to shorTitles longer than 50 characters.\n      if (shortTitle.length > 50) {\n        shortTitle = shortTitle.substring(0, 50) + '...';\n      }\n      return shortTitle;\n    }\n\n    // a single list item in the day.\n    const makeItem = (item) => {\n      const rangeStr = this.formatTimeStr(item.start, item.end, item.allday);\n      const shortTitle = makeShortTitle(item.title);\n      // first letter of each word in campus\n      //const abbrev = item.campus.split(' ').map(word => word[0]).join('');\n      const locationStr = item.location ? `${item.location}, ${item.campus}` : '${item.campus}';\n      return `\n        <li>\n          <span class=\"badge text-bg-dark\">${rangeStr}</span>\n          <a href=\"${item.url}\" class=\"text-decoration-none link-dark-green\">\n            ${shortTitle}\n          </a>\n        </li>`;\n    }\n\n    // make an unordered list of titles from items.\n    const makeList = (items) => {\n      const list = document.createElement('ul');\n      list.classList.add('list-unstyled');\n      let itemContent = '';\n      items.forEach(item => {\n        const itemEl = makeItem(item);\n        itemContent += itemEl;\n      });\n      list.innerHTML = itemContent;\n      return list;\n    }\n\n    // create a div\n    const eventElement = document.createElement('div');\n    eventElement.className = 'd-flex py-2 border-bottom border-secondary position-relative';\n\n    // if last item remove the border classes\n    if (isLastItem) {\n      eventElement.classList.remove('border-bottom');\n    }\n    // make an icon\n    const icon = calendarIcon(month, day);\n    // make and return the element\n    eventElement.innerHTML = icon;\n    eventElement.appendChild(makeList(items));\n    return eventElement;\n  }\n\n\n  /**\n   * Build event DOM elements using configs set on DOM data- attributes.\n   */\n  insertEventsToDom(events) {\n    // gather all the event wrapper elements\n    const eventWrappers = document.querySelectorAll('.event-wrapper');\n    eventWrappers.forEach(wrapper => {\n      // get parse details for each dom element which will control which events show in this element\n      const campuses = wrapper.getAttribute('data-untl-event-campuses');\n      const categories = wrapper.getAttribute('data-untl-event-categories');\n      const kind = wrapper.getAttribute('data-untl-event-kind') || 'standard';\n      let max = wrapper.getAttribute('data-untl-event-max');\n      let maxDays = wrapper.getAttribute('data-untl-event-max-days') || 30;\n      const eventBody = wrapper.querySelector(\".card-body\");\n\n      // filter lists, categories and campuses\n      let campusFilter = campuses ? campuses.split(',').map(item => item.trim()) : [];\n      let categoryFilter = categories ? categories.split(',').map(item => item.trim()) : [];\n\n      let filteredEvents = this.filterEventsByCriteria(events, {\n          campuses: campusFilter,\n          categories: categoryFilter\n      });\n      // max items filter\n      if (max) {\n        max = parseInt(max, 10);\n        filteredEvents = filteredEvents.slice(0, max);\n      }\n      // max days filter\n      let maxDate = null;\n      if (maxDays){\n        maxDays = parseInt(maxDays, 10);\n        maxDate = this.now.add(maxDays, 'day');\n        filteredEvents = filteredEvents.filter(event => dayjs(event.start).isBefore(maxDate));\n      }\n      // filter out events in the past\n      filteredEvents = filteredEvents.filter(event => dayjs(event.end).isAfter(this.now));\n\n      // for now everything is a standard display. TODO support other kinds in the future.\n      if (kind === 'standard') {\n        let groupedEvents = this.groupEventsByDate(filteredEvents);\n        let eventDates = Object.keys(groupedEvents);\n        eventDates.map((date, index) => {\n          if (groupedEvents[date].length > 0) {\n            // Determine if this is the last item in the array\n            const isLastItem = index === eventDates.length - 1;\n            // Pass isLastItem as the third argument to this.makeDay\n            const makeDay = this.makeDay(groupedEvents, date, isLastItem);\n            eventBody.appendChild(makeDay);\n          }\n        });\n        // test to see if groupedEvents is false. Append no events message if so.\n        if (Object.keys(groupedEvents).length === 0) {\n          const noEvents = document.createElement('p');\n\n          const upcomingFragment = maxDays ? `There are no events scheduled between now and ${maxDate.format('MMMM D, YYYY')}` : 'There are no upcoming events.';\n\n          const noEventsMessage = `\n            ${upcomingFragment}. <a href=\"{{ site.data.urls.calendar.events }}\">See our complete calendar for future events.</a>\n            `;\n          noEvents.innerHTML = noEventsMessage;\n          eventBody.appendChild(noEvents);\n\n        }\n      }\n    });\n  }\n\n  async init() {\n    // get existing events from storage\n    let events = getWithExpiry(this.config.storageKey, this.currentVersion, true);\n    if (!events) {\n      // no events fetch fresh events from libcal\n      events = await this.fetchAndProcessEvents();\n      // refresh every 30 minutes or when script version change.\n      setWithExpiry(this.config.storageKey, events, 30 * 60000, this.currentVersion, true);\n    }\n    // build the display\n    this.insertEventsToDom(events);\n  };\n}\n","// utility function to create function with timeout, used in ga events\nexport function actWithTimeOut(callback: () => void, optTimeout?: number) {\n  let called = false;\n  function fn() {\n    if (!called) {\n      called = true;\n      callback();\n    }\n  }\n  setTimeout(fn, optTimeout ?? 1000);\n  return fn;\n}\n\n/**\n * This function overrides the same function from upstream www/utils.js\n * Utility function to wait for DOM ready.\n * Executes the callback when the DOM is fully loaded.\n * @param {Function} callback\n */\nexport function onDomReady(callback: () => void) {\n  document.addEventListener('turbolinks:load', callback);\n}\n","// Changes from source noted below with \"EDITED\"\nimport { OffCanvasSearch } from './search.offcanvas.js';\nimport { DropdownManager } from './display.dropdowns.js';\nimport { LibraryHours } from './display.hours.js';\nimport { EventManager } from './display.events.js';\nimport { AnchorUtility } from './display.anchors.js';\nimport { ModalMapHandler } from './modal.map.js';\nimport { HighlightSearchTermMatch } from './search.highlight.js';\nimport { IdentifierHelper } from './search.identifiers.js';\n// import { ShortcutManager } from './home.shortcuts.js'; // EDITED to remove home.shortcuts.js\nimport { ChatManager } from './display.chat.js';\nimport { DisplayBasics } from './display.basics.js';\n// import { DidYouKnowRenderer } from './display.dyk.js'; // EDITED to remove display.dyk.js\n\nimport { InsertToTopButtons } from './display.to-top.js';\n\nimport { optInTooltips, optInPopovers } from './utils.js'; // EDITED to not import onDomReady\nimport { onDomReady } from '../_utils.ts'; // EDITED to override onDomReady function\n\n// Initialize tooltips and popovers if needed\nonDomReady(() => {\n  optInTooltips();\n  optInPopovers();\n});\n\n// Override dropdown configuration if it is set in the global window object\nconst dropdownConfig = window.untLib?.dropdownConfig || {};\n\nconst insertToTopButtons = new InsertToTopButtons();\nconst offCanvasSearchUtil = new OffCanvasSearch();\nconst dropdownManager = new DropdownManager(dropdownConfig); // EDITED to use config on window object\nconst libraryHours = new LibraryHours(dropdownManager);\nconst eventManager = new EventManager();\nconst anchorUtility = new AnchorUtility();\nconst identifierHelper = new IdentifierHelper();\nconst modalMapHandler = new ModalMapHandler();\nconst highlightSearchTermMatch = new HighlightSearchTermMatch();\n// const shortcutManager = new ShortcutManager('#shortcuts', '.shortcuts-toggle', 4); // EDITED to remove home.shortcuts.js\nconst chatManager = new ChatManager();\nconst displayBasics = new DisplayBasics();\n// const displayDyk = new DidYouKnowRenderer() // EDITED to remove display.dyk.js\n","import { onDomReady, setWithExpiry, getWithExpiry } from './utils.js';\n\nexport class InsertToTopButtons {\n  constructor() {\n    onDomReady(() => {\n      this.init();\n    });\n  }\n\n  init() {\n    const contentSection = document.querySelector('#content');\n    if (!contentSection) return;\n\n    const h2Elements = contentSection.querySelectorAll('h2');\n    if (h2Elements.length < 3) return;\n\n    let skippedH2Count = 0;\n\n    h2Elements.forEach((h2) => {\n      if (this.shouldSkipInsertion(h2)) return;\n\n      if (skippedH2Count < 2) {\n        skippedH2Count++;\n        return;\n      }\n\n      const toTopSnippet = this.generateToTopSnippet();\n      h2.insertAdjacentHTML('beforebegin', toTopSnippet);\n    });\n\n    // Also insert before elements with the `.to-top-before` class\n    document.querySelectorAll('.to-top-before').forEach((el) => {\n      if (!this.shouldSkipInsertion(el)) {\n        const toTopSnippet = this.generateToTopSnippet();\n        el.insertAdjacentHTML('beforebegin', toTopSnippet);\n      }\n    });\n  }\n\n  shouldSkipInsertion(element) {\n    return element.closest(\"[data-untl-to-top-of-page='false']\");\n  }\n\n  generateToTopSnippet() {\n    return `\n      <div class=\"clearfix\"></div>\n      <div class=\"border-top my-5 position-relative\" markdown=\"0\">\n        <a href=\"#top\" class=\"to-top-btn bg-white btn btn-outline-light-green opacity-75 p-1 position-absolute rounded-circle start-50 translate-middle\"\n           data-bs-toggle=\"tooltip\"\n           data-bs-placement=\"right\"\n           title=\"Back to Top\">\n          <span class=\"fa-kit fa-unt-eagle fa-lg\" aria-hidden=\"true\" title=\"UNT Eagle\"></span>\n        </a>\n      </div>\n    `;\n  }\n}\n","import { onDomReady } from './utils.js';\n\nexport class AnchorUtility {\n    constructor() {\n        this.selectors = '.content h2, .content h3, .content h4, .content h5, .content h6, .content .anchored';\n        // autorun\n        onDomReady(() => {\n            this.init();\n        });\n    }\n\n    init() {\n        const headings = document.querySelectorAll(this.selectors);\n        // create the anchor links\n        this.makeAnchorLinks(headings);\n        // attach the copy to clipboard event listener\n        this.copyToClipBoardListener();\n        // add a scroll to top behavior to top of page links\n        this.jumpToTopOfPage();\n    }\n\n    makeAnchorLinks(headings) {\n        headings.forEach(heading => {\n            // test if heading has and id\n            if (!heading.classList.contains('no-toc') && heading.hasAttribute('id') && heading.id !== '' ) {\n                const idValue = heading.id;\n                const icon = this.createIcon(idValue);\n                heading.appendChild(icon);\n            }\n        });\n    }\n\n    copyToClipBoardListener() {\n        const links = document.querySelectorAll('.anchor-link');\n        links.forEach(link => {\n            link.addEventListener('click', (event) => {\n                //event.preventDefault();\n                const url = `${window.location.origin}${window.location.pathname}#${link.href.split('#')[1]}`;\n                navigator.clipboard.writeText(url).then(() => {\n                    link.setAttribute('data-bs-title', 'Copied to Clipboard');\n                }).catch((error) => {\n                    console.error('Failed to copy:', error);\n                });\n            });\n        });\n    }\n\n    createIcon(idValue) {\n        const link = document.createElement('a');\n        link.classList.add(\n            'anchor-link',\n            'ms-2'\n        )\n        link.href = `#${idValue}`;\n        const icon = document.createElement('span');\n        icon.className = 'fas fa-link text-secondary-subtle';\n        icon.setAttribute('title', 'Update URL to this section');\n        link.setAttribute('data-bs-toggle', 'tooltip');\n        link.setAttribute('data-bs-title', 'Link to this section');\n        link.appendChild(icon);\n        return link;\n    }\n\n    jumpToTopOfPage(){\n        const jumpButtons = document.querySelectorAll(\".to-top-btn\");\n        // for each button add an event listener, prevent default behaviour and scroll to the top of the pages\n        jumpButtons.forEach(button => {\n            button.addEventListener(\"click\", (e) => {\n                e.preventDefault();\n                window.scrollTo({\n                    top: 0,\n                    behavior: \"smooth\"\n                });\n            });\n        });\n    }\n}\n","import { onDomReady } from './utils.js';\n\nexport class ModalMapHandler {\n  constructor() {\n    this.config = {\n      selectors: {\n        modal: document.querySelector('#map-dialog'),\n        image: document.querySelector(\"#map-dialog-image\"),\n        downloadLink: document.querySelector(\"#map-dialog-download-link\"),\n        infoLink: document.querySelector(\"#map-dialog-info-link\"),\n      }\n    };\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n  listeners() {\n    this.config.selectors.modal.addEventListener('show.bs.modal', (event) => {\n      const triggerEl = event.relatedTarget;\n      const info = {\n        'src': triggerEl.getAttribute('data-untl-map-src'),\n        'alt': triggerEl.getAttribute('data-untl-map-alt'),\n        'href': triggerEl.getAttribute('data-untl-map-href'),\n      }\n      this.config.selectors.image.src = info.src;\n      // set image alt\n      this.config.selectors.image.alt = info.alt;\n      this.config.selectors.downloadLink.href = info.src;\n      this.config.selectors.infoLink.href = info.href;\n    });\n    this.config.selectors.modal.addEventListener('hidden.bs.modal', (event) => {\n      this.config.selectors.image.src = '';\n      this.config.selectors.downloadLink.href = '';\n      this.config.selectors.infoLink.href = '';\n    });\n\n  }\n  init() {\n    if (!this.config.selectors.modal) {\n      return;\n    }\n    this.listeners();\n  }\n}\n","import { onDomReady } from './utils.js';\n\nexport class HighlightSearchTermMatch {\n  constructor() {\n    this.contentArea = document.querySelector('#content');\n    this.urlParams = new URLSearchParams(window.location.search);\n    this.searchText = this.urlParams.get('h');\n    // autorun\n    onDomReady(() => {\n      this.init(this.searchText);\n    });\n\n  }\n\n  init(text) {\n    if (!this.contentArea || !text || Mark === 'undefined') {\n      return;\n    }\n    const markInstance = new Mark(this.contentArea);\n    let totalMarks = 0;\n    markInstance.mark(text, {\n      'element': 'span',\n      'className': 'bg-warning fw-semibold bg-opacity-20',\n      'separateWordSearch': true,\n      'accuracy': 'complementary',\n      'each': (element) => {\n        totalMarks++;\n      }\n    });\n  }\n}\n","// Changes from source noted below with \"EDITED\"\nimport { onDomReady, setWithExpiry, getWithExpiry } from './utils.js';\n\n/**\n * Manages the display and loading of the chat widget.\n * Check against springshare's api to see if chat is online and\n * store value for 15 minutes before checking again.\n */\nexport class ChatManager {\n  /**\n   * Constructor.\n   */\n  constructor() {\n    this.storageKey = window.wwwJsShims.chat.storageKey; // EDITED to use config on window object\n    this.expireTime = (5 * 60000); // 5 minutes\n    this.apiUrl = window.wwwJsShims.chat.chat_config.apiURL.concat(window.wwwJsShims.chat.chat_config.endpoint); // EDITED to use config on window object\n    this.widgetUrl = window.wwwJsShims.chat.chat_config.widgetUrl; // EDITED to use config on window object\n    this.chatBtn = document.querySelector('#global-chat-btn');\n    this.chatBtnText = document.querySelector('#global-chat-btn-text');\n    this.offCanvasAsUs = document.querySelector('#offcanvas-ask-us');\n    this.currentVersion = 'v1.0'; // Update this version as needed to force resets\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n\n  /**\n   * Initializes on content load.\n   */\n  init() {\n    // exit early if no chat button or off canvas element\n    if (!this.chatBtn || !this.offCanvasAsUs) return;\n    // append fallback text to offCanvasAsUs no matter the circumstances.\n    this.offCanvasAsUs.innerHTML = this.getFallbackText();\n    // check storage\n    const chatStatus = this.checkStorage();\n    if (chatStatus === null) {\n      // status doesn't exist; it has expired or never existed. Check status.\n      this.fetchChatStatus();\n    } else {\n      if (chatStatus) {\n        // chat is available, load the widget\n        this.loadChat();\n        this.showBtn();\n      } else {\n        // chat is not available, do nothing.\n        // 'false' state will expire within 15 min.\n        // and a re-check will result in null\n        //this.loadChat(); // testing.\n        this.offlineStatus();\n        this.showBtn();\n      }\n    }\n  }\n\n  /**\n   * Fetch chat status from springshare api using async\n   * set storage with the boolean value and return the value.\n   */\n  async fetchChatStatus() {\n    try {\n      const response = await fetch(this.apiUrl);\n      const data = await response.json();\n      let status = false;\n      if (data.online) {\n        this.setStorage(true);\n        this.loadChat();\n        this.showBtn();\n        status = true;\n      } else {\n        this.setStorage(false);\n        this.offlineStatus();\n        this.showBtn();\n      }\n      return status;\n    } catch (error) {\n      console.error('Error:', error);\n      return false;\n    }\n  }\n\n  /**\n   * Note offline status.\n   */\n  offlineStatus() {\n    this.chatBtnText.innerHTML = \"Chat is offline\";\n  }\n\n  /**\n   * reveal button.\n   */\n  showBtn() {\n    this.chatBtn.classList.remove('d-none');\n  }\n\n  /**\n   * load chat widget by appending script element to body\n   */\n  loadChat() {\n    const script = document.createElement('script');\n    script.src = this.widgetUrl;\n    script.async = true;\n    document.body.appendChild(script);\n  }\n\n  /**\n   * fallBack Text\n   *\n   */\n  getFallbackText() {\n    return window.wwwJsShims.chat.ask_us_text; // EDITED to use config on window object\n  }\n\n  /**\n   * Set storage with current chat status and a 5 minute expiry.\n   * @param {boolean} active - true if chat is active, false if not.\n   * @returns {boolean} - true if chat is active, false if not.\n   */\n  setStorage(active) {\n    setWithExpiry(this.storageKey, active, this.expireTime, this.currentVersion, true);\n  }\n\n  /**\n   * Check storage if chat is currently active. returning true or null.\n   * @returns {boolean | null} - true if chat is active, null if not.\n   */\n  checkStorage() {\n    const storageVal = getWithExpiry(this.storageKey, this.currentVersion, true);\n    return storageVal;\n  }\n}\n\n// Usage example:\n// new ChatManager();\n","import { onDomReady, togglePasswordVisibility } from './utils.js';\nexport class DisplayBasics {\n  constructor(){\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n  init(){\n    togglePasswordVisibility();\n  }\n}\n","// Changes from source noted below with \"EDITED\"\n// alert.js\nimport { onDomReady, setWithExpiry, getWithExpiry } from './utils.js';\nimport dayjs from 'dayjs'; // EDITED\nimport utc from 'dayjs/plugin/utc'; // EDITED\n\n// Extend day.js with the plugins\ndayjs.extend(utc); // EDITED to extend utc plugin\n\n/**\n * Manages the display and dismissal of global alerts on the website.\n */\nclass AlertManager {\n  /**\n   * Constructs the alert manager setting up the basic properties and initiating event listeners.\n   */\n  constructor() {\n    this.storageKey = window.wwwJsShims.alerts.js_config.storageKey; // EDITED to use window object\n    this.expireDays = window.wwwJsShims.alerts.alerts.days_expire_dismiss; // EDITED to use window object\n    this.publishOn = window.wwwJsShims.alerts.alerts.publish_on; // EDITED to use window object\n    this.hideOn = window.wwwJsShims.alerts.alerts.hide_on; // EDITED to use window object\n    this.currentVersion = window.wwwJsShims.alerts.alerts.version; // EDITED to use window object\n    this.alertElement = document.querySelector('#global-alert');\n    this.dismissBtn = document.querySelector('#global-alert-dismiss');\n    // autorun\n    onDomReady(() => {\n      this.init();\n    });\n  }\n\n  /**\n   * Initializes the alert system.\n   */\n  init() {\n    this.checkAlertVisibility();\n    if (this.alertElement && this.dismissBtn) {\n      this.attachDismissEvent();\n    }\n  }\n\n  /**\n   * Checks the visibility of the global alert based on local storage settings and publish/hide dates.\n   * If no setting is found, the setting has expired, or the alert is within the publish/hide date range, shows the alert.\n   */\n  checkAlertVisibility() {\n    const globalAlert = getWithExpiry(this.storageKey, this.currentVersion);\n\n    // Parse the publish and hide dates using Day.js in UTC\n    const publishDate = this.publishOn ? dayjs(this.publishOn).utc() : null;\n    const hideDate = this.hideOn ? dayjs(this.hideOn).utc() : null;\n\n    // Get the current date/time in UTC\n    const now = dayjs().utc();\n\n    // Check if the alert should be shown based on date conditions\n    const isWithinDateRange =\n      (!publishDate || now.isAfter(publishDate) || now.isSame(publishDate)) &&\n      (!hideDate || now.isBefore(hideDate) || now.isSame(hideDate));\n\n    if (!globalAlert && isWithinDateRange) {\n      this.showAlert();\n    } else {\n      this.hideAlert();\n    }\n  }\n\n  /**\n   * Hides the alert element by setting its display style to 'none'.\n   */\n  hideAlert() {\n    if (this.alertElement) this.alertElement.style.display = 'none';\n  }\n\n  /**\n   * Shows the alert element by removing 'display: none' style if it exists.\n   */\n  showAlert() {\n    if (this.alertElement) this.alertElement.style.display = '';\n  }\n\n  /**\n   * Attaches a click event listener to the dismiss button that will store a dismissal record in local storage\n   * and hide the alert element. The record expiry is set based on `expireDays`.\n   */\n  attachDismissEvent() {\n    this.dismissBtn.addEventListener('click', () => {\n      setWithExpiry(\n        this.storageKey,\n        true,\n        this.expireDays * 24 * 60 * 60 * 1000, // X days in the future\n        this.currentVersion\n      );\n      this.hideAlert();\n    });\n  }\n}\n\n// Create an instance of AlertManager when the script is loaded\nnew AlertManager();\n","// Changes from source noted below with \"EDITED\"\n\n// EDITED to export SearchDropdown class\nexport default class SearchDropdown {\n  constructor(dropdownId, watchId, data) {\n    this.dropdownId = dropdownId;\n    this.watchId = watchId;\n    this.data = data;\n    this.initDropdown();\n    this.attachEventListeners();\n  }\n\n  initDropdown() {\n    const dropdownMenu = document.getElementById(this.dropdownId);\n    dropdownMenu.innerHTML = this.data.map(item => {\n      if (item.type === 'divider') {\n        return `<div class=\"dropdown-divider\"></div>`; // Bootstrap's class for dividers\n      } else {\n        return `\n          <button class=\"dropdown-item\" type=\"button\" data-url-template=\"${item.urlTemplate}\">\n            ${item.label}\n          </button>\n        `;\n      }\n    }).join('');\n  }\n\n\n  attachEventListeners() {\n    const dropdownMenu = document.getElementById(this.dropdownId);\n    dropdownMenu.addEventListener('click', (event) => {\n      if (event.target.classList.contains('dropdown-item')) {\n        event.preventDefault();\n        const urlTemplate = event.target.getAttribute('data-url-template');\n        const searchTerm = document.getElementById(this.watchId).value;\n        this.changeLocation(urlTemplate, searchTerm);\n      }\n    });\n  }\n\n  changeLocation(urlTemplate, searchTerm) {\n    const fullUrl = urlTemplate.replace('{query}', encodeURIComponent(searchTerm));\n    window.location.href = fullUrl;\n  }\n}\n","// Script files that should be included in all pages\nimport 'intersection-observer';\nimport Rails from '@rails/ujs';\nimport Turbolinks from 'turbolinks';\nimport * as bootstrap from 'bootstrap';\nimport runBrowserUpdate from '../src/javascripts/_browser-update';\nimport { initPrefilters, searchSelector } from '../src/javascripts/_search';\nimport { animateSearchIcon, bindDismissBannerCookie, linkify } from \"../src/javascripts/_ui\";\nimport './base.scss';\n\n// Javascript inherited from www\nimport '../src/javascripts/www/main'; // This is the main entry point for the www pack\nimport '../src/javascripts/www/display.alerts';\nimport SearchDropdown from '../src/javascripts/www/search-dropdowns.js';\n\ndocument.addEventListener('turbolinks:load', () => {\n  // Initialize search dropdowns using window object\n  new SearchDropdown('bento-offcanvas-other-search-options', 'bento-offcanvas-q', window.wwwJsShims.searchDropdowns['default_bento']);\n});\n\ndocument.addEventListener(\"DOMContentLoaded\", () => {\n  Rails.start();\n  Turbolinks.start();\n});\n\n// Blacklight Frontend javascript\nimport 'blacklight-frontend/app/javascript/blacklight/core';\nimport 'blacklight-frontend/app/javascript/blacklight/modal';\nimport 'blacklight-frontend/app/javascript/blacklight/button_focus';\nimport 'blacklight-frontend/app/javascript/blacklight/search_context';\n\ndocument.addEventListener('turbolinks:load', () => {\n  animateSearchIcon();\n  initPrefilters();\n  searchSelector();\n  linkify();\n  bindDismissBannerCookie();\n  runBrowserUpdate();\n});\n"],"sourceRoot":""}