236 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
| (function () {
 | |
|     if (document.body.scrollIntoView) {
 | |
|         var docnav = document.querySelector('.doc-nav');
 | |
|         var active = docnav.querySelector('li.active');
 | |
|         if (active) {
 | |
|             active.scrollIntoView({ block: 'center', inline: 'nearest' });
 | |
|         }
 | |
|     }
 | |
|     setupScrollSpy();
 | |
|     setupMobileToggle();
 | |
|     setupDarkMode();
 | |
|     setupSearch();
 | |
|     setupCollapse();
 | |
| })();
 | |
| 
 | |
| function setupScrollSpy() {
 | |
|     var sectionPositions = [];
 | |
|     var sections = document.querySelectorAll('section');
 | |
|     sections.forEach(function (section) {
 | |
|         sectionPositions.push(section.offsetTop);
 | |
|     });
 | |
|     var scrollPos = 0;
 | |
|     window.addEventListener('scroll', function (_) {
 | |
|         // Reset classes
 | |
|         document.querySelectorAll('.doc-toc a[class="active"]').forEach(function (link) {
 | |
|             link.classList.remove('active');
 | |
|         });
 | |
|         // Set current menu link as active
 | |
|         var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
 | |
|         for (var i = 0; i < sectionPositions.length; i++) {
 | |
|             var section = sections[i];
 | |
|             var position = sectionPositions[i];
 | |
|             if (position >= scrollPosition) {
 | |
|                 var link = document.querySelector('.doc-toc a[href="#' + section.id + '"]');
 | |
|                 if (link) {
 | |
|                     link.classList.add('active');
 | |
|                     var docToc = document.querySelector('.doc-toc');
 | |
|                     var tocHeight = docToc.clientHeight;
 | |
|                     var scrollTop = docToc.scrollTop;
 | |
|                     if ((document.body.getBoundingClientRect()).top < scrollPos && scrollTop < link.offsetTop - 10) {
 | |
|                         docToc.scrollTop = link.clientHeight + link.offsetTop - tocHeight + 10;
 | |
|                     } else if (scrollTop > link.offsetTop - 10) {
 | |
|                         docToc.scrollTop = link.offsetTop - 10;
 | |
|                     }
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         scrollPos = (document.body.getBoundingClientRect()).top;
 | |
|     });
 | |
| }
 | |
| 
 | |
| function setupMobileToggle() {
 | |
|     var toggle = document.getElementById('toggle-menu');
 | |
|     toggle.addEventListener('click', function (_) {
 | |
|         var docNav = document.querySelector('.doc-nav');
 | |
|         var isHidden = docNav.classList.contains('hidden');
 | |
|         docNav.classList.toggle('hidden');
 | |
|         var search = document.querySelector('.doc-nav > .search');
 | |
|         console.log(search);
 | |
|         var searchHasResults = search.classList.contains('has-results');
 | |
|         if (isHidden && searchHasResults) {
 | |
|             search.classList.remove('mobile-hidden');
 | |
|         } else {
 | |
|             search.classList.add('mobile-hidden');
 | |
|         }
 | |
|         var content = document.querySelector('.doc-nav .content');
 | |
|         content.classList.toggle('hidden');
 | |
|         content.classList.toggle('show');
 | |
|     });
 | |
| }
 | |
| 
 | |
| function setupDarkMode() {
 | |
|     var html = document.getElementsByTagName('html')[0];
 | |
|     var darkModeToggle = document.getElementById('dark-mode-toggle');
 | |
|     darkModeToggle.addEventListener('click', function () {
 | |
|         html.classList.toggle('dark');
 | |
|         var isDarkModeEnabled = html.classList.contains('dark');
 | |
|         localStorage.setItem('dark-mode', isDarkModeEnabled);
 | |
|         darkModeToggle.setAttribute('aria-checked', isDarkModeEnabled)
 | |
|     });
 | |
|     // Check if css var() is supported and enable dark mode toggle
 | |
|     if (window.CSS && CSS.supports('color', 'var(--fake-var)')) {
 | |
|         darkModeToggle.style.visibility = 'unset';
 | |
|     }
 | |
| }
 | |
| 
 | |
| function setupSearch() {
 | |
|     var searchInput = document.getElementById('search');
 | |
|     var onInputChange = debounce(function (e) {
 | |
|         var searchValue = e.target.value.toLowerCase();
 | |
|         var menu = document.querySelector('.doc-nav > .content');
 | |
|         var search = document.querySelector('.doc-nav > .search');
 | |
|         if (searchValue === '') {
 | |
|             // reset to default
 | |
|             menu.style.display = '';
 | |
|             if (!search.classList.contains('hidden')) {
 | |
|                 search.classList.add('hidden');
 | |
|                 search.classList.remove('has-results');
 | |
|             }
 | |
|         } else if (searchValue.length >= 2) {
 | |
|             // search for less than 2 characters can display too much results
 | |
|             search.innerHTML = '';
 | |
|             menu.style.display = 'none';
 | |
|             if (search.classList.contains('hidden')) {
 | |
|                 search.classList.remove('hidden');
 | |
|                 search.classList.remove('mobile-hidden');
 | |
|                 search.classList.add('has-results');
 | |
|             }
 | |
|             // cache length for performance
 | |
|             var foundModule = false;
 | |
|             var searchModuleIndexLength = searchModuleIndex.length;
 | |
|             var ul = document.createElement('ul');
 | |
|             search.appendChild(ul);
 | |
|             for (var i = 0; i < searchModuleIndexLength; i++) {
 | |
|                 // no toLowerCase needed because modules are always lowercase
 | |
|                 var title = searchModuleIndex[i];
 | |
|                 if (title.indexOf(searchValue) === -1) {
 | |
|                     continue
 | |
|                 }
 | |
|                 foundModule = true;
 | |
|                 // [description, link]
 | |
|                 var data = searchModuleData[i];
 | |
|                 var description = data[0];
 | |
|                 var link = data[1];
 | |
|                 var el = createSearchResult({
 | |
|                     link: link,
 | |
|                     title: title,
 | |
|                     description: description,
 | |
|                     badge: 'module',
 | |
|                 });
 | |
|                 ul.appendChild(el);
 | |
|             }
 | |
|             if (foundModule) {
 | |
|                 var hr = document.createElement('hr');
 | |
|                 hr.classList.add('separator');
 | |
|                 search.appendChild(hr);
 | |
|             }
 | |
|             var searchIndexLength = searchIndex.length;
 | |
|             var results = [];
 | |
|             for (var i = 0; i < searchIndexLength; i++) {
 | |
|                 var title = searchIndex[i];
 | |
|                 if (title.toLowerCase().indexOf(searchValue) === -1) {
 | |
|                     continue
 | |
|                 }
 | |
|                 // [badge, description, link]
 | |
|                 var data = searchData[i];
 | |
|                 var badge = data[0];
 | |
|                 var description = data[1];
 | |
|                 var link = data[2];
 | |
|                 var prefix = data[3];
 | |
|                 results.push({
 | |
|                     badge: badge,
 | |
|                     description: description,
 | |
|                     link: link,
 | |
|                     title: prefix + ' ' + title,
 | |
|                 });
 | |
|             }
 | |
|             results.sort(function (a, b) {
 | |
|                 if (a.title < b.title) {
 | |
|                     return -1;
 | |
|                 }
 | |
|                 if (a.title > b.title) {
 | |
|                     return 1;
 | |
|                 }
 | |
|                 return 0;
 | |
|             });
 | |
|             var ul = document.createElement('ul');
 | |
|             search.appendChild(ul);
 | |
|             for (var i = 0; i < results.length; i++) {
 | |
|                 var result = results[i];
 | |
|                 var el = createSearchResult(result);
 | |
|                 ul.appendChild(el);
 | |
|             }
 | |
|         }
 | |
|     });
 | |
|     searchInput.addEventListener('input', onInputChange);
 | |
| }
 | |
| 
 | |
| function createSearchResult(data) {
 | |
|     var li = document.createElement('li');
 | |
|     li.classList.add('result');
 | |
|     var a = document.createElement('a');
 | |
|     a.href = data.link;
 | |
|     a.classList.add('link');
 | |
|     li.appendChild(a);
 | |
|     var defintion = document.createElement('div');
 | |
|     defintion.classList.add('definition');
 | |
|     a.appendChild(defintion);
 | |
|     if (data.description) {
 | |
|         var description = document.createElement('div');
 | |
|         description.classList.add('description');
 | |
|         description.textContent = data.description;
 | |
|         a.appendChild(description);
 | |
|     }
 | |
|     var title = document.createElement('span');
 | |
|     title.classList.add('title');
 | |
|     title.textContent = data.title;
 | |
|     defintion.appendChild(title);
 | |
|     var badge = document.createElement('badge');
 | |
|     badge.classList.add('badge');
 | |
|     badge.textContent = data.badge;
 | |
|     defintion.appendChild(badge);
 | |
|     return li;
 | |
| }
 | |
| 
 | |
| function setupCollapse() {
 | |
|     var dropdownArrows = document.querySelectorAll('.dropdown-arrow');
 | |
|     for (var i = 0; i < dropdownArrows.length; i++) {
 | |
|         var dropdownArrow = dropdownArrows[i];
 | |
|         dropdownArrow.addEventListener('click', function (e) {
 | |
|             var parent = e.target.parentElement.parentElement.parentElement;
 | |
|             parent.classList.toggle('open');
 | |
|         });
 | |
|     }
 | |
| }
 | |
| 
 | |
| function debounce(func, timeout) {
 | |
|     var timer;
 | |
|     return (...args) => {
 | |
|         const next = () => func(...args);
 | |
|         if (timer) {
 | |
|             clearTimeout(timer);
 | |
|         }
 | |
|         timer = setTimeout(next, timeout > 0 ? timeout : 300);
 | |
|     }
 | |
| }
 | |
| 
 | |
| document.addEventListener('keypress', (ev) => {
 | |
|   if (ev.key == '/') {
 | |
|     let search = document.getElementById('search');
 | |
|     ev.preventDefault();
 | |
|     search.focus();
 | |
|   }
 | |
| });
 |