diff --git a/_includes/head.html b/_includes/head.html index 0c9fbb8c..acaa4876 100644 --- a/_includes/head.html +++ b/_includes/head.html @@ -64,8 +64,7 @@ {% else %} {% endif %} - - + diff --git a/_includes/i18n-notice.html b/_includes/i18n-notice.html index 05b33241..6c5adf9e 100644 --- a/_includes/i18n-notice.html +++ b/_includes/i18n-notice.html @@ -7,4 +7,4 @@ {% assign notice_link_text = site.data.en.general.i18n_notice_link_text %} {% endif %}

{{ notice }} {{ notice_link_text}}.

-
\ No newline at end of file +
\ No newline at end of file diff --git a/_layouts/home.html b/_layouts/home.html index c5f77e29..8d5abafc 100644 --- a/_layouts/home.html +++ b/_layouts/home.html @@ -14,8 +14,8 @@
{% if page.lang != 'en' %} -
- {% include i18n-notice.html %} + {% endif %} diff --git a/_layouts/page.html b/_layouts/page.html index f86e816d..0a93f56c 100644 --- a/_layouts/page.html +++ b/_layouts/page.html @@ -15,7 +15,7 @@
{% if page.lang != 'en' %} -
+ {% endif %} diff --git a/css/style.css b/css/style.css index 8793968e..3dd880e3 100644 --- a/css/style.css +++ b/css/style.css @@ -474,7 +474,7 @@ pre:has(code) button.failed { } } -/* top button */ +/* scroll to top button */ .scroll #top { opacity: .5; @@ -611,6 +611,7 @@ table ul { font-weight: 600; } +/* i18n box */ .doc-notice { padding-block: 1rem; padding-inline: 2.5rem; @@ -618,6 +619,11 @@ table ul { border-radius: 0 6px 6px 0; background: var(--notice-bg); border-left: 3px solid var(--notice-accent); + margin-inline: auto; + margin-block-start: 2rem; + position: relative; + grid-area: i18n; + display: block; } .doc-notice a{ @@ -625,6 +631,10 @@ table ul { text-decoration: underline; } +.doc-notice[hidden] { + display: none; +} + .doc-info { background: var(--info-bg); border-left: 3px solid var(--info-accent); @@ -645,13 +655,6 @@ table ul { text-decoration: underline; } -#i18n-notice-box { - margin-inline: auto; - margin-block-start: 2rem; - position: relative; - grid-area: i18n; -} - #close-i18n-notice-box { position: absolute; top: 3px; diff --git a/js/app.js b/js/app.js index 2418c2f3..ea24dc2f 100644 --- a/js/app.js +++ b/js/app.js @@ -1,122 +1,124 @@ -$(function(){ - var doc = $(document); +const languageElement = document.getElementById('languageData'); +const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : []; +const langDisplay = document.getElementById('current-lang'); +const i18nMsgBox = document.getElementById("i18n-notice-box"); +const scrollToTopBtn = document.getElementById("top"); - // top link - $('#top').click(function(e){ - $('html, body').animate({scrollTop : 0}, 500); - return false; - }); +// display current language in language picker component +if (langDisplay) { + const currentLanguage = window.location.pathname.split('/')[1]; + const matchedLang = languagesData.find(lang => lang.code === currentLanguage); + langDisplay.textContent = matchedLang ? matchedLang.name : 'English'; +} - // scrolling links - var added; - doc.scroll(function(e){ - if (doc.scrollTop() > 5) { - if (added) return; - added = true; - $('body').addClass('scroll'); - } else { - $('body').removeClass('scroll'); - added = false; - } - }) - - // menu bar - - var headings = $('h2, h3').map(function(i, el){ - return { - top: $(el).offset().top - 200, - id: el.id - } - }); - - function closest() { - var h; - var top = $(window).scrollTop(); - var i = headings.length; - while (i--) { - h = headings[i]; - if (top >= h.top) return h; - } - } - - var currentApiPrefix; - var parentMenuSelector; - var lastApiPrefix; - - if (document.readyState !== 'loading') { - const languageElement = document.getElementById('languageData'); - const languagesData = languageElement ? JSON.parse(languageElement.dataset.languages) : []; - - const langDisplay = document.getElementById('current-lang'); - - if (langDisplay) { - const currentLanguage = window.location.pathname.split('/')[1]; - const matchedLang = languagesData.find(lang => lang.code === currentLanguage); - langDisplay.textContent = matchedLang ? matchedLang.name : 'English'; - } - } - - $(document).scroll(function() { - var h = closest(); - if (!h) return; - - currentApiPrefix = h.id.split('.')[0]; - parentMenuSelector = '#'+ currentApiPrefix + '-menu'; - - $(parentMenuSelector).addClass('active'); - - if (lastApiPrefix && (lastApiPrefix != currentApiPrefix)) { - $('#'+ lastApiPrefix + '-menu').removeClass('active'); - } - - $('#menu li a').removeClass('active'); - - var a = $('a[href="#' + h.id + '"]'); - a.addClass('active'); - - lastApiPrefix = currentApiPrefix.split('.')[0]; - }) - - // i18n notice - if (readCookie('i18nClose')) { - $('#i18n-notice-box').hide(); - $("#i18n-notice-box").addClass("hidden"); - } - else { - $('#close-i18n-notice-box').on('click', function () { - $('#i18n-notice-box').hide(); - $("#i18n-notice-box").addClass("hidden"); - createCookie('i18nClose', 1); +// scroll to top of the page +if (scrollToTopBtn) { + scrollToTopBtn.addEventListener("click", function (e) { + e.preventDefault(); + window.scrollTo({ + top: 0, + behavior: "smooth" }) + }) +} + +// add/remove class 'scroll' on scroll by 5px +const scrollTarget = document.querySelector('.logo-container'); +const scrollObserver = new IntersectionObserver( + ([entry]) => { + if (!entry.isIntersecting) { + document.body.classList.add('scroll'); + } else { + document.body.classList.remove('scroll'); + } + }, + { + root: null, + threshold: 0, + rootMargin: '0px 0px 0px 0px' } -}) +); +if (scrollTarget) scrollObserver.observe(scrollTarget); +// heighlight current Menu on scroll +const headings = Array.from(document.querySelectorAll("h2, h3")); +const menuLinks = document.querySelectorAll("#menu li a"); + +const observerOptions = { + root: null, + rootMargin: "-10% 0px -65% 0px", + threshold: 1, +}; + +const menuObserver = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const currentApiPrefix = entry.target.id.split(".")[0]; + const parentMenuSelector = `#${currentApiPrefix}-menu`; + const parentMenuEl = document.querySelector(parentMenuSelector); + + // open submenu on scroll + if (parentMenuEl) parentMenuEl.classList.add("active"); + + // Remove active class from last menu item + const lastActiveMenu = document.querySelector(".active[id$='-menu']"); + if (lastActiveMenu && lastActiveMenu.id !== parentMenuEl.id) { + lastActiveMenu.classList.remove("active"); + } + + // Update active link + menuLinks.forEach((link) => link.classList.remove("active")); + const activeLink = document.querySelector(`a[href="#${entry.target.id}"]`); + if (activeLink && !activeLink.classList.contains("active")) activeLink.classList.add("active"); + } + }); +}, observerOptions); + +headings.forEach((heading) => menuObserver.observe(heading)); + +// i18n message box : this box appears hidden for all page.lang != 'en' +const isI18nCookie = readCookie('i18nClose'); +if (i18nMsgBox && !isI18nCookie) { + const closeI18nBtn = document.getElementById("close-i18n-notice-box"); + // show notice box + i18nMsgBox.hidden = false; + // close notice box + if (closeI18nBtn) { + closeI18nBtn.addEventListener("click", () => { + // hide notice + i18nMsgBox.hidden = true; + // set session cookie + createCookie('i18nClose', 1); + }); + + // keyboard a11y + closeI18nBtn.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + closeI18nBtn.click(); + } + }); + } +}; function createCookie(name, value, days) { - var expires; - + let expires = ""; if (days) { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - expires = "; expires=" + date.toGMTString(); - } else { - expires = ""; + const date = new Date(); + date.setTime(date.getTime() + (days * 864e5)); + expires = "; expires=" + date.toUTCString(); } - document.cookie = encodeURIComponent(name) + "=" + encodeURIComponent(value) + expires + "; path=/"; + document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${expires}; path=/; SameSite=Lax; Secure`; } function readCookie(name) { - var nameEQ = encodeURIComponent(name) + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; + const nameEQ = encodeURIComponent(name) + "="; + const ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; while (c.charAt(0) === ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length, c.length)); } return null; } - -function eraseCookie(name) { - createCookie(name, "", -1); -} \ No newline at end of file