PageSourceSearch

https://www.nasm.org/scripts/nasmlibraries/nasm-hybrid-nav.js

js nasm.org collected 2026-09-24 08:33:43 UTC 30,473 bytes, 871 lines download raw bytes

1// Build the submenu (Desktop and Mobile)
2
3// TODO:
4// Hide h2:after on Desktop
5// Mobile behavior: add .toggled class and show hidden child(link-section-links) when h2 is clicked
6
7// setTimeout before display:none so fade animation can run
8// fade animation run on blur BG as well, so it fades before disappearing
9
10let menuJSON = null;
11let submenuJSON = null;
12let submenuContent;
13let submenu;
14let content;
15let elementsCache = {};
16let menuIsHovered;
17let cbData = null;
18let istimer = false;
19let continuityBar;
20
21addEventListener('DOMContentLoaded', (event) => {
22// Check if the custom element is already defined
23  if (!customElements.get('continuity-bar')) {
24    // Define the custom element
25    customElements.define('continuity-bar', ContinuityBar);
26  }
27
28  if (!document.querySelector('continuity-bar')) {
29    continuityBar = document.createElement('continuity-bar');
30    document.body.insertBefore(continuityBar, document.body.firstChild);
31  }
32
33  // TODO:
34  // All Courses Layout
35  // Spec layout
36  // Add 'all blah blah blah' links at bottom
37  // Add ability to switch between lists & cards with a key (it's just an if/else with the builder functions)
38  // Which layouts are no longer used?
39  // Just need 3 and 4. We'll call that 2 and 3 now.
40
41  submenu = document.getElementById('main-menu');
42  submenuContent = document.getElementById('nasm-submenu');
43
44  // Get JSON data here. Assume Tabs is an array.
45  // DON'T fetch nasm-submenu.json until the user mouses over a menu item
46  // Menu options (not submenu) are their own, much smaller, file
47
48  checkLayout();
49  window.addEventListener('resize', checkLayout);
50});
51
52function buildSubmenu(isMobile) {
53  // submenuContent.innerHTML = '';
54
55  if (menuJSON == null) {
56    if ($('.continuity-bar-nav')) {
57      // Find and remove any existing continuity-bar-nav
58      $('.continuity-bar-nav').remove();
59    }
60
61    // fetch menu JSON (only once)
62    $.getJSON('/docs/nasmlibraries/json/nasm-menu.json', (data) => {
63      $.getJSON('https://www.nasm.org/docs/nasmlibraries/json/nasm-continuity-bar.json', (data2) => {
64        // Get the JSON for the Continuity Bar and build it out, only if the initial menuJSON was null
65
66        if (data.timer == true) {
67          istimer = true;
68        }
69
70        // buildContinuityBar(data2, "continuity", data["timer"]);
71        // const intervalID = setInterval(function() {runSlickForNav(intervalID)}, 200);
72        // Passing key in manually to avoid cycling through all keys again
73        // Clear out any pre-existing Continuity Bar in case resizing window. Must remove completely.
74        // Also important to strip out the placeholder
75        // Then rebuild it
76
77        if (!customElements.get('continuity-bar')) {
78          // Define the custom element
79          customElements.define('continuity-bar', ContinuityBar);
80        }
81
82        if (!document.querySelector('continuity-bar')) {
83          continuityBar = document.createElement('continuity-bar');
84          document.body.insertBefore(continuityBar, document.body.firstChild);
85        }
86
87        cbData = data2;
88      });
89
90      menuJSON = data;
91
92      createElements(isMobile, menuJSON, submenu);
93    });
94  } else {
95    createElements(isMobile, menuJSON, submenu);
96    // buildContinuityBar(cbData, "continuity", istimer);
97
98    if (!customElements.get('continuity-bar')) {
99      // Define the custom element
100      customElements.define('continuity-bar', ContinuityBar);
101    }
102
103    if (!document.querySelector('continuity-bar')) {
104      continuityBar = document.createElement('continuity-bar');
105      document.body.insertBefore(continuityBar, document.body.firstChild);
106    }
107  }
108}
109
110function createElements(isMobile, menuJSON, submenu) {
111  const searchDropdown = document.getElementById('search-dropdown');
112
113  searchDropdown.addEventListener('mouseleave', () => {
114    setTimeout(() => {
115      // setTimeout to avoid feeling of a "hair trigger"
116
117      document.getElementById('nav_bg_overlay').style.width = '100%';
118      document.getElementById('nav_bg_overlay').style.height = '0';
119      // Close search bar and set blur div height to 0 to remove blur
120      // when mouse pointer leaves search bar
121
122      $('.search-dropdown-hide').toggle();
123      $('.search-wrapper').toggle();
124      // Hide search bar, as well
125
126      // TODO: very fast, slight fade
127    }, 300);
128  });
129
130  const div1 = document.querySelector('#navWrapper');
131  const div2 = document.querySelector('#nasm-submenu');
132
133  div1.addEventListener('mouseleave', checkCloseSubmenu);
134  div2.addEventListener('mouseleave', checkCloseSubmenu);
135
136  function checkCloseSubmenu() {
137    const isDiv1Hovered = div1.matches(':hover');
138    const isDiv2Hovered = div2.matches(':hover');
139
140    if (!isDiv1Hovered && !isDiv2Hovered) {
141      hideSubmenu();
142    }
143  }
144
145  if (!document.querySelector('.option')) {
146    // Only build the menu if the menu options don't exist
147    // No need to rebuild or re-attach event listeners
148
149    optionContainer = document.createElement('ul');
150
151    menuJSON.menu.forEach((option, index) => {
152      let optionElement;
153
154      if (option.visible == true) {
155        optionElement = document.createElement('li');
156        // Only populate with menu items that are intended to be visible
157
158        optionElement.className = 'option';
159        optionElement.classList.add(`blueMouseover-${index}`);
160        optionElement.textContent = option.title;
161        optionElement.dataset.index = index;
162        optionContainer.appendChild(optionElement);
163
164        if (window.innerWidth < 1440) {
165          optionElement.removeEventListener('click', buildDesktopSubmenu.bind(this, optionElement, index, isClick = true));
166          // Could use a curried function, but don't feel up to that level of refactoring at the moment
167          // .bind will do
168          // Remember the first two arguments before your params
169          // Remove Desktop event listener, if it existed
170
171          // TODO: fetch & cache submenu JSON only on first click
172          optionElement.addEventListener('click', buildMobileSubmenu.bind(this, optionElement));
173        } else {
174          const hideOnDesktop = document.querySelector('#hideOnDesktop');
175          hideOnDesktop.style = '';
176
177          const slideoutSearch = document.querySelector('.slideout-search');
178          slideoutSearch.style = '';
179
180          const sideNav = document.querySelector('#mySidenav');
181          sideNav.style = '';
182
183          const mainMenu = document.querySelector('#main-menu');
184          mainMenu.style = '';
185
186          optionElement.removeEventListener('click', buildMobileSubmenu.bind(this, optionElement));
187          // Remove mobile event listener, if it existed
188
189          optionElement.addEventListener('mouseenter', buildDesktopSubmenu.bind(this, optionElement, index, isClick = false));
190          optionElement.addEventListener('click', buildDesktopSubmenu.bind(this, optionElement, index, isClick = true));
191        }
192      }
193    });
194    submenu.appendChild(optionContainer);
195  } else if (window.innerWidth >= 1440) {
196    // On desktop, we may have resized from mobile
197    // Options may be there, but styles need to be reset
198
199    const sideNav = document.getElementById('mySidenav');
200    sideNav.style = '';
201
202    const search = document.querySelector('.search-wrapper-mobile');
203    search.style = '';
204
205    const slideoutSearch = document.querySelector('.slideout-search');
206    slideoutSearch.style = '';
207
208    const mobileHelpDiv = document.getElementById('hideOnDesktop');
209    mobileHelpDiv.style = '';
210
211    submenu.style = '';
212
213    const options = document.querySelectorAll('.option');
214
215    options.forEach((optionElement, index) => {
216      optionElement.removeEventListener('click', buildMobileSubmenu);
217      optionElement.addEventListener('mouseenter', buildDesktopSubmenu.bind(this, optionElement, index, isClick = false));
218      optionElement.addEventListener('click', buildDesktopSubmenu.bind(this, optionElement, index, isClick = true));
219    });
220  }
221
222  // const submenu = document.getElementById("main-menu");
223}
224
225function buildCards(content) {
226  let allCardsDiv = '<div class="submenu-cards">';
227
228  // Create the beginning of this div. Doing it with string concat
229  // allows us to avoid creating elements/appendChild
230
231  content.cards.forEach((card) => {
232    const { badges } = card;
233
234    if (badges !== undefined) {
235      const badgeDivs = badges.map((badge) =>
236      // Map the badge content to individual divs
237
238        `<div class="product-badge">${badge}</div>`).join('');
239
240      const currentCardDiv = `<a href=${card.url}><div class="nav__mobile--slideout--cardHeader">
241                <div class="product-badges">${badgeDivs}</div>
242                <div class="slideout--highlight slideout--highlight--item1 slideout--highlight-padding"><div>${card.promoText}</div></div>
243            </div>
244            <div>${card.header}</div></a>`;
245      allCardsDiv += currentCardDiv;
246      // Add the generated card div to the div containing all of the cards
247    } else {
248      currentCardDiv = `<a href="${card.url}"><div>
249            <h4>${card.title}</h4>
250            <p>${card.copy}</p>
251            </div></a>`;
252      allCardsDiv += currentCardDiv;
253    }
254  });
255
256  if (content.layout == '2') {
257    allCardsDiv += `<p class="more-courses-link-wrapper">
258        <a class="more-courses-link" href="${content['more-courses-link']}">${content['more-courses-link-text']}</a>
259    </p>`;
260  }
261
262  allCardsDiv += '</div>';
263  // Close up the div containing all of the cards
264
265  return allCardsDiv;
266}
267
268function addCachedElement() {
269
270}
271
272function renderTabContent(tab, index) {
273  return new Promise((resolve) => {
274    let submenuContent;
275
276    if (window.innerWidth >= 1440) {
277      // Attach direction to submenu div if on Desktop
278      submenuContent = document.getElementById('nasm-submenu');
279
280      for (const child of submenuContent.children) {
281        if (child.id != `${tab.id}-tab`) {
282          child.style.display = 'none';
283        }
284      }
285    } else {
286      submenuContent = document.getElementById('main-menu');
287    }
288
289    if (elementsCache[`${tab.id}-tab`] != undefined && document.getElementById(`${tab.id}-tab`) !== undefined) {
290      // Will rebuild completely for mobile for now - we have to change where it's appended to anyway
291
292      // If the element already exists in cache, use that
293
294      const newTab = document.getElementById(`${tab.id}-tab`);
295
296      newTab.style.display = 'block';
297      // tab-# is a convention to avoid confusion, could use the number
298      // as a string, but don't want it confused for an array
299      // at first glance
300    } else {
301      // Element has not been buld - build it and cache it
302      let html = '';
303
304      switch (tab.layout) {
305        case '1':
306          html = renderLayout1(tab, index);
307
308          break;
309        case '2':
310          html = renderLayout2(tab, index);
311          break;
312        case '3':
313
314          html = renderLayout3(tab, index);
315
316          break;
317        case '4':
318
319          html = renderLayout4(tab, index);
320          break;
321        default:
322          html = '';
323      }
324
325      elementsCache[`${tab.id}-tab`] = html;
326      // add HTML to cache object at 'tab-index'
327
328      if (window.innerWidth < 1440) {
329        submenuContent.innerHTML = html;
330      } else {
331        submenuContent.innerHTML += html;
332      }
333
334      resolve();
335      toggleMobileOptions();
336    }
337  });
338}
339
340function buildSubmenuTab(optionElement, submenuJSON) {
341  if (submenuJSON == null) {
342    // Fallback just in case... make sure JSON is not null
343
344    $.getJSON('/docs/nasmlibraries/json/nasm-submenu.json', (submenudata) => {
345      submenuJSON = submenudata;
346      const thisIndex = optionElement.getAttribute('data-index');
347
348      renderTabContent(submenudata.tabs[`${thisIndex}`], thisIndex)
349        .then(() => {
350          // Code to run after rendering is complete
351
352          const matchingTab = document.querySelectorAll(`.blueMouseover-${thisIndex}`)[1];
353
354          const navigationEvent = new CustomEvent(`item${thisIndex}`, { detail: `item${thisIndex}-tab` });
355
356          if (matchingTab) {
357            matchingTab.addEventListener('mouseenter', (e) => {
358              optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
359            });
360
361            matchingTab.addEventListener('mouseleave', (e) => {
362              optionElement.style.borderBottom = '3px solid var(--ui-color-light)';
363
364              // Remove the border if they leave the tab
365            });
366          }
367
368          document.dispatchEvent(navigationEvent);
369        })
370        .catch((error) => {
371
372        });
373    });
374  } else {
375    const thisIndex = optionElement.getAttribute('data-index');
376
377    renderTabContent(submenuJSON.tabs[`${thisIndex}`], thisIndex)
378      .then(() => {
379        // Code to run after rendering is complete
380
381        const navigationEvent = new CustomEvent(`item${thisIndex}`, { detail: `item${thisIndex}-tab` });
382        // const submenuWrapper = document.getElementById("nasm-submenu");
383
384        const matchingTab = document.querySelectorAll(`.blueMouseover-${thisIndex}`)[1];
385
386        if (matchingTab) {
387          // Stop looking for the tab
388          matchingTab.addEventListener('mouseenter', (e) => {
389            optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
390          });
391
392          matchingTab.addEventListener('mouseleave', (e) => {
393            optionElement.style.borderBottom = '3px solid var(--ui-color-light)';
394            // Remove the border if they leave the tab
395          });
396        }
397
398        document.dispatchEvent(navigationEvent);
399
400        // TO LISTEN FOR THIS EVENT:
401        // const submenuWrapper = document.getElementById("nasm-submenu");
402        // submenuWrapper.addEventListener("tabRendered",
403        // (e) => {
404        //     /* … */
405        // });
406        // then check the detail to see which tab it was, and run code accordingly
407      })
408      .catch((error) => {
409        console.error('Error occurred during rendering:', error);
410      });
411  }
412}
413
414function buildList(content) {
415  let linkListDiv = '<div class="submenu-sections">';
416
417  if (content.learn !== undefined) {
418    const links = content.learn.map((link) =>
419    // Map the links to paragraph tags containing links
420      `<p><a href="${link.url}">${link.copy}</a></p>`).join('');
421
422    linkListDiv += links;
423    // Add the links to the div
424
425    // // Close up the div
426    linkListDiv += '</div>';
427    // Close up the div
428
429    return linkListDiv;
430  }
431  const linkColumns = content['link-sections'];
432  // Grab the content for the columns
433
434  linkColumns.forEach((column) => {
435    // Create the column and its heading
436
437    let linkColumn = `<div class="link-section">
438            <h2>${column['heading-copy']}</h2>`;
439    links = column.links.map((link) => `<a href="${link.url}">${link.copy}</a>`).join('');
440    // Create the links
441    linkColumn += `<div class="link-section-links">${links}</div>`;
442    linkColumn += '</div>';
443
444    linkListDiv += linkColumn;
445  });
446
447  linkListDiv += '</div>';
448  // Close up the div
449
450  return linkListDiv;
451  // Add the column to the div containing the columns
452}
452
453
454function checkLayout() {
455  // Helper function to rebuild menu for smaller breakpoints
456  const blurOverlay = document.getElementById('nav_bg_overlay');
457  blurOverlay.style = '';
458  // Remove the blur overlay if it was active
459
460  const searchDropdown = document.querySelector('#search-dropdown');
461  searchDropdown.style.display = 'none';
462  // Remove the search dropdown if it's open
463
464  const isMobile = window.innerWidth < 1440;
465
466  if (window.innerWidth < 1440) {
467    if (submenuContent.innerHTML != '') {
468      // If coming from Desktop with an open menu, close it
469      document.getElementById('mySidenav').style.left = '-90%';
470
471      // If coming from Desktop, reset the blur
472      blurOverlay.style.height = '0';
473
474      // Reset search display for mobile
475      $('.search-wrapper-mobile').css('display') == 'block';
476    }
477
478    submenu = document.getElementById('main-menu');
479    submenu.innerHTML = '';
480    submenuContent = document.getElementById('nasm-submenu');
481    submenuContent.innerHTML = '';
482
483    elementsCache = {};
484    // for (const child of submenuContent.children) {
485    //     child.innerHTML = '';
486    //     child.style.display = "none";
487    //   }
488
489    // Clear existing innerHTML for mobile and reset styles
490
491    if (document.querySelector('.submenu-panels') == null || document.querySelector('.slideout-close-button-mobile') !== null) {
492      // Make sure we haven't just changed to a different tab on mobile
493      // by checking for a div that exists only on tabs
494
495      buildSubmenu(window.innerWidth < 1440);
496    }
497  } else {
498    submenu = document.getElementById('main-menu');
499    submenu.innerHTML = '';
500    submenuContent = document.getElementById('nasm-submenu');
501    submenuContent.innerHTML = '';
502    elementsCache = {};
503    buildSubmenu(window.innerWidth < 1440);
504
505    // Just build it on desktop
506  }
507}
508
509//* ************ LAYOUTS ***************/
510function renderLayout1(content, index) {
511  // Create the HTML for submenu layout 1
512  // Drop in the content
513  // TODO: correct the notation for the form of the content that's passed in
514
515  const allCardsDiv = buildCards(content);
516
517  const linkListDiv = buildList(content);
518
519  const submenuHTML = `
520            <div id="${content.id}-tab" class="blueMouseover-${index}" data-index="${index}">
521                <div class="mobile-close-controls">
522                    <p class="slideout-close-button-mobile" onClick="closeSideMenu()">Back</p>
523                    <div class="close-nav-desktop" onClick="hideSubmenu()">X</div>
524                </div>
525                      
526                <section>
527                    <h2 class="mobile-nav-header fade-in hideOnDesktop">${content['promoHeader-mobile']}</h2>
528                    <p class="mobile-nav-header fade-in hideOnDesktop">${content['promoCopy-mobile']}</p> 
529                    <a class="mobile-nav-header fade-in hideOnDesktop" href="${content['learn-more-link']}">${content['learn-more-copy-mobile']}</a>
530                    <div class="submenu-panels fade-in">
531                            
532                        <div id="submenu-cards" class="${content.id}-cards">
533                            <h2>${content.title}</h2>
534                            ${allCardsDiv}
535                            <p class="more-courses-link-wrapper">
536                                <a class="more-courses-link" href="${content['more-courses-link']}">${content['more-courses-link-text']}</a>
537                            </p>
538                        </div>
539                        <div id="submenu-links" class="${content.id}-links">
540                            <h2>${content.title2}</h2>
541                            ${linkListDiv}
542                        </div>
543                        <div id="submenu-image" class="${content.id}-image">
544                                <img src="${content.image}" />
545                        </div>
546                        <section class="nav__mobile--slideout--header">
547                            <h2 class="hideOnMobile">${content['promoHeader-desktop']}</h2>
548                            <p class="hideOnMobile">${content['promoCopy-desktop']}</p>
549                            <a class="hideOnMobile" href="${content['learn-more-link']}">${content['learn-more-copy-desktop']}</a>
550                        </section>
551                    </div>
552                </section>
553                  
554            </div>
555    `;
556  return submenuHTML;
557}
558
559function renderLayout2(content, index) {
560  // Create the HTML for submenu layout 2
561  // This used to be Layout 3
562  // Maybe a slightly different card building function, since the cards have different c
562ontent
563  // OR if it has no .badges key (result is undefined), we know it's a different card
564
565  const allCardsDiv = buildCards(content);
566
567  const submenuHTML = `
568            <div id="${content.id}-tab" class="blueMouseover-${index}" data-index="${index}">
569                <div class="mobile-close-controls">
570                    <p class="slideout-close-button-mobile" onClick="closeSideMenu()"Back</p>
571                    <div class="close-nav-desktop" onClick="hideSubmenu()">X</div>
572                </div>
573                      
574                <section>
575                    <h2 class="mobile-nav-header hideOnDesktop">${content['promoHeader-mobile']}</h2>
576                    <p class="mobile-nav-header hideOnDesktop">${content['promoCopy-mobile']}</p> 
577                    <a class="mobile-nav-header hideOnDesktop" href="${content['learn-more-link']}">${content['learn-more-copy-mobile']}</a>
578                    <div class="submenu-panels fade-in">
579                            
580                        <div id="submenu-cards" class="${content.id}-cards">
581                            <h2>${content.title}</h2>
582                            <div class="allCardsDiv-${index}">${allCardsDiv}</div>
583                        </div>
584                       
585                        <div id="submenu-image" class="${content.id}-image">
586                            <img src="${content.image}" />
587                        </div>  
588                        <section class="nav__mobile--slideout--header">
589                            <h2 class="hideOnMobile">${content['promoHeader-desktop']}</h2>
590                            <p class="hideOnMobile">${content['promoCopy-desktop']}</p>
591                            <a class="hideOnMobile" href="${content['learn-more-link']}">${content['learn-more-copy-desktop']}</a>
592                        </section>
593                    </div>
594                </section>
595                  
596            </div>
597    `;
598
599  return submenuHTML;
600}
601
602function renderLayout3(content, index) {
603  // Create the HTML for submenu layout 3
604  // Resources tab
605
606  const linkListDiv = buildList(content);
607
608  const submenuHTML = `
609            <div id="${content.id}-tab" class="blueMouseover-${index}" data-index="${index}">
610                <div class="mobile-close-controls">
611                    <p class="slideout-close-button-mobile" onClick="closeSideMenu()">Back</p>
612                    <div class="close-nav-desktop" onClick="hideSubmenu()">X</div>
613                </div>
614                      
615                <section id="submenu-cards">
616                    <h2 class="mobile-nav-header hideOnDesktop">${content['promoHeader-mobile']}</h2>
617                    <p class="mobile-nav-header hideOnDesktop">${content['promoCopy-mobile']}</p> 
618                    <a class="mobile-nav-header hideOnDesktop" href="${content['learn-more-link']}">${content['learn-more-copy-mobile']}</a>
619                    <div class="submenu-panels">
620                            
621                    <div class="link-section-columns">${linkListDiv}</div>
622                    </div>
623                </section>
624                  
625            </div>
626    `;
627  return submenuHTML;
628}
629
630function renderLayout4(content, index) {
631  // Create the HTML for submenu layout 4
632  // SINGLE LINK layout, NO submenu
633  // Drop in the content
634
635  const submenuHTML3 = '';
636
637  // TODO: Add anchor tag around the menu item
638}
639
640function buildMobileSubmenu(optionElement) {
641  if (window.innerWidth < 1440) {
642    document.querySelector('.slideout-search').style.display = 'none';
643    const needHelp = document.getElementById('hideOnDesktop');
644    // Need to hide the "Need Help?" section
645    needHelp.classList.add('fade-out');
646
647    setTimeout(() => {
648      needHelp.style.display = 'none';
649      needHelp.classList.remove('fade-out');
650    });
651
652    if (submenuJSON == null) {
653      $.getJSON('/docs/nasmlibraries/json/nasm-submenu.json', (submenudata) => {
654        submenuJSON = submenudata;
655        buildSubmenuTab(optionElement, submenuJSON);
656      });
657    } else {
658      submenu = document.getElementById('main-menu');
659      submenu.innerHTML = '';
660      // Temp: clear innerHTML
661      // TODO: Replace with appropriate animation
662      buildSubmenuTab(optionElement, submenuJSON);
663    }
664  }
665}
666
667function buildDesktopSubmenu(optionElement, index, isClick) {
668  const hideOnDesktop = document.querySelector('#hideOnDesktop');
669  hideOnDesktop.style = '';
670
671  const slideoutSearch = document.querySelector('.slideout-search');
672  slideoutSearch.style = '';
673
674  const sideNav = document.querySelector('#mySidenav');
675  sideNav.style = '';
676
677  setTimeout(() => {
678    const blurOverlay = document.querySelector('#nav_bg_overlay');
679    blurOverlay.style.height = '100vh';
680    blurOverlay.style.width = '100%';
681    document.querySelector('.search-dropdown-hide').style = '';
682  }, 300);
683
684  const matchingTab = document.querySelectorAll(`.blueMouseover-${index}`)[1];
685  if (matchingTab) {
686    matchingTab.addEventListener('mouseenter', (e) => {
687      // optionElement.style.borderBottom = "2px solid var(--brand-color-turquoise)";
688      optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
689    });
690
691    matchingTab.addEventListener('mouseleave', (e) => {
692      setTimeout(() => {
693        // optionElement.style.borderBottom = "2px solid var(--ui-color-light)";
694        optionElement.style.borderBottom = '3px solid var(--ui-color-light)';
695      }, 200);
696    });
697  }
698
699  // optionElement.style.borderBottom = "2px solid var(--brand-color-turquoise)";
700  optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
701
702  optionElement.addEventListener('mouseenter', (e) => {
703    // optionElement.style.borderBottom = "2px solid var(--brand-color-turquoise)";
704    optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
705  });
706
707  optionElement.addEventListener('mouseleave', (e) => {
708    optionElement.style.borderBottom = '3px solid var(--ui-color-light)';
709  });
710
711  if (submenuJSON == null) {
712    $.getJSON('/docs/nasmlibraries/json/nasm-submenu.json', (submenudata) => {
713      submenuJSON = submenudata;
714
715      buildSubmenuTab(optionElement, submenuJSON);
716    });
717  } else {
718    const thisIndex = optionElement.getAttribute('data-index');
719
720    optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
721
722    renderTabContent(submenuJSON.tabs[`${thisIndex}`], thisIndex)
723      .then(() => {
724        const navigationEvent = new CustomEvent(`item${thisIndex}`, { detail: `item${thisIndex}-tab` });
725
726        const matchingTab = document.querySelectorAll(`.blueMouseover-${thisIndex}`)[1];
727
728        matchingTab.addEventListener('mouseenter', (e) => {
729          // optionElement.style.borderBottom = "2px solid var(--brand-color-turquoise)";
730          optionElement.style.borderBottom = '3px solid var(--brand-color-turquoise)';
731        });
732
733        matchingTab.addEventListener('mouseleave', (e) => {
734          setTimeout(() => {
735            // optionElement.style.borderBottom = "2px solid var(--ui-color-light)";
736            optionElement.style.borderBottom = '3px solid var(--ui-color-light)';
737          }, 200);
738        });
739
740        document.dispatchEvent(navigationEvent);
741        toggleMobileOptions();
742      })
743      .catch((error) => {
744        console.error('Error occurred during rendering:', error);
745      });
746  }
747}
748
749function toggleMobileOptions() {
750  // Code for Resources Dropdown, DRY up later
751  console.log('ran mobile options');
752
753  const resourcesLinks = document.querySelectorAll('.link-section h2');
754
755  if (resourcesLinks !== null) {
756    resourcesLinks.forEach((link) => {
757      link.addEventListener('click', (e) => {
758        const linkChild = link.nextElementSibling;
759
760        if (linkChild.style.display !== 'block') {
761          linkChild.style.display = 'block';
762        } else {
763          linkChild.style.display = 'none';
764        }
765      });
766    });
767  }
768
769  const resourceCarats = document.querySelectorAll('.link-section h2:after');
770  console.log('resourceCarats', resourceCarats);
771
772  if (resourceCarats !== null) {
773    resourceCarats.forEach((link) => {
774      link.addEventListener('click', (e) => {
775        const linkChild = link.nextElementSibling;
776
777        if (linkChild.style.display !== 'block') {
778          linkChild.style.display = 'block';
779        } else {
780          linkChild.style.display = 'none';
781        }
782      });
783    });
784  }
785}
786
787function hideH2s(e) {
788  const content = e.target.nextElementSibling;
789  // Get the sibling p tag of just the target
790  if (e.target.classList.contains('toggled')) {
791    e.target.classList.remove('toggled');
792    content.style.display = 'none';
793  } else {
794    e.target.classList.add('toggled');
795    content.style.display = 'block';
796  }
797}
798
799function hideSubmenu() {
800  document.getElementById('nav_bg_overlay').style.width = '100%';
801
802  submenuContent.classList.add('fade-out');
803  // Add fadeout animaton class
804
805  setTimeout(() => {
806    document.getElementById('nav_bg_overlay').style.height = '0';
807    for (const child of submenuContent.children) {
808      child.style.display = 'none';
809    }
810    submenuContent.classList.remove('fade-out');
811  }, 160);
812}
813
814function runSlickForNav(intervalID) {
815  clearInterval(intervalID);
816
817  $('.continuity-bar-nav').slick({
818    speed: 500,
819    autoplay: true,
820    infinite: true,
821    draggable: true,
822    vertical: true,
823    verticalSwiping: true,
824    autoplaySpeed: 6000,
825    pauseOnHover: false,
826    pauseOnFocus: false,
827    touchMove: false,
828  });
829}
830
831const _second = 1000;
832const _minute = _second * 60;
833const _hour = _minute * 60;
834const _day = _hour * 24;
835
836function showRemaining(data, auth, key) {
837  let end;
838
839  if (auth) {
840    end = new Date(`${data['auth-date']} 12:00 AM`);
841  } else {
842    end = new Date(`${data['unauth-date']} 12:00 AM`);
843  }
844
845  const now = new Date();
846  let distance = end - now;
847
848  if (distance < 0) {
849    distance = 0; // Set distance to 0 if it's negative
850
851    // document.getElementById('countdown').innerHTML = 'EXPIRED!';
852
853    // Used class rather than ID because having difficulty grabbing IDs of injected elements but not classes
854    $('.continuity-countdown').html('');
855
856    return;
857  }
858  const days = Math.floor(distance / _day);
859  const hours = Math.floor((distance % _day) / _hour);
860  let totalhours;
861  if (days > 0) {
862    totalhours = Math.floor(days * 24 + hours);
863  } else {
864    totalhours = hours;
865  }
866  const minutes = Math.floor((distance % _hour) / _minute);
867  const seconds = Math.floor((distance % _minute) / _second);
868
869  $(`.${key} .continuity-countdown`).html(`<span class="continuity-countdown-numbers"><p>${totalhours}</p><p>hrs</p></span> <span class="continuity-countdown-numbers"><p>${minutes}</p><p>min</p></span> <span class="continuity-countdown-numbers"><p>${seconds}</p><p>sec</p></span>`);
870  setTimeout(showRemaining, 1000, data, auth, key);
871}

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.