diff --git a/docs/assets/js/src/back-to-top-button.js b/docs/assets/js/src/back-to-top-button.js index 1c903a385..fb65a5f28 100644 --- a/docs/assets/js/src/back-to-top-button.js +++ b/docs/assets/js/src/back-to-top-button.js @@ -1,64 +1,64 @@ // Add back-to-top button // Create/style button -const button = document.createElement('button'); -button.classList.add('back-to-top'); +const button = document.createElement('button') +button.classList.add('back-to-top') // Create button SVG -const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); -svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); -svgElement.setAttribute('height', '2em'); -svgElement.setAttribute('viewBox', '0 0 448 512'); +const svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg') +svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg') +svgElement.setAttribute('height', '2em') +svgElement.setAttribute('viewBox', '0 0 448 512') // Create button SVG path -const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); -pathElement.setAttribute('d', 'M246.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L224 109.3 361.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160zm160 352l-160-160c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L224 301.3 361.4 438.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3z'); +const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path') +pathElement.setAttribute('d', 'M246.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L224 109.3 361.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160zm160 352l-160-160c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L224 301.3 361.4 438.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3z') // Merge elements -svgElement.append(pathElement); button.append(svgElement); -document.body.append(button); +svgElement.append(pathElement) ; button.append(svgElement) +document.body.append(button) function fadeIn(el, duration) { - if (el.classList.contains('done')) return; - el.classList.add('done'); el.style.opacity = 0; - let last = +new Date(); + if (el.classList.contains('done')) return + el.classList.add('done') ; el.style.opacity = 0 + let last = +new Date() const tick = () => { - el.style.opacity = +el.style.opacity + (new Date() - last) / duration; - last = +new Date(); + el.style.opacity = +el.style.opacity + (new Date() - last) / duration + last = +new Date() if (+el.style.opacity < 1) (window.requestAnimationFrame && requestAnimationFrame(tick)) || - setTimeout(tick, 16); - else el.style.display = 'block'; - }; - tick(); + setTimeout(tick, 16) + else el.style.display = 'block' + } + tick() } function fadeOut(el, duration) { - if (!el.classList.contains('done')) return; - el.classList.remove('done'); el.style.opacity = 1; - let last = +new Date(); + if (!el.classList.contains('done')) return + el.classList.remove('done') ; el.style.opacity = 1 + let last = +new Date() const tick = () => { - el.style.opacity = +el.style.opacity - (new Date() - last) / duration; - last = +new Date(); + el.style.opacity = +el.style.opacity - (new Date() - last) / duration + last = +new Date() if (+el.style.opacity > 0) (window.requestAnimationFrame && requestAnimationFrame(tick)) || - setTimeout(tick, 16); - else el.style.display = 'none'; - }; - tick(); + setTimeout(tick, 16) + else el.style.display = 'none' + } + tick() } function scrollToTop() { const c = document.documentElement.scrollTop || document.body.scrollTop; if (c > 0) { - window.requestAnimationFrame(scrollToTop); - window.scrollTo(0, c - c / 8); + window.requestAnimationFrame(scrollToTop) + window.scrollTo(0, c - c / 8) } } -button.addEventListener('click', scrollToTop); +button.addEventListener('click', scrollToTop) window.addEventListener('scroll', () => { const scrollTop = window.scrollY || document.documentElement.scrollTop; - if (scrollTop > 0) fadeIn(button, 500); - else fadeOut(button, 500); -}); \ No newline at end of file + if (scrollTop > 0) fadeIn(button, 500) + else fadeOut(button, 500) +}); diff --git a/docs/assets/js/src/copy-code-button.js b/docs/assets/js/src/copy-code-button.js index 51692878e..4162312c7 100644 --- a/docs/assets/js/src/copy-code-button.js +++ b/docs/assets/js/src/copy-code-button.js @@ -5,19 +5,19 @@ return (s = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { - return typeof o; + return typeof o } : function (o) { - return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; - })(o); + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o + })(o) } !(function (o, e) { - void 0 === e && (e = {}); - var t = e.insertAt; + void 0 === e && (e = {}) + var t = e.insertAt if (o && "undefined" != typeof document) { var n = document.head || document.getElementsByTagName("head")[0], c = document.createElement("style"); - (c.type = "text/css"), "top" === t && n.firstChild ? n.insertBefore(c, n.firstChild) : n.append(c), c.styleSheet ? (c.styleSheet.cssText = o) : c.append(document.createTextNode(o)); + (c.type = "text/css"), "top" === t && n.firstChild ? n.insertBefore(c, n.firstChild) : n.append(c), c.styleSheet ? (c.styleSheet.cssText = o) : c.append(document.createTextNode(o)) } })( ".docsify-copy-code-button,.docsify-copy-code-button span{cursor:pointer}.docsify-copy-code-button{position:absolute;z-index:1;top:0;right:0;overflow:visible;padding:.65em .8em;border:0;border-radius:0;outline:0;font-size:1em;background:grey;background:var(--theme-color,grey);color:#fff;opacity:1}.docsify-copy-code-button span{border-radius:3px;background:inherit;pointer-events:none}.docsify-copy-code-button .error,.docsify-copy-code-button .success{position:absolute;z-index:1000;top:24px;left:12px;padding:.5em .65em;font-size:.825em;opacity:0;transform:translateX(-88%)translateY(-50%)}.docsify-copy-code-button.error .error,.docsify-copy-code-button.success .success{left:12px;top:24px;opacity:1;,pre:hover .docsify-copy-code-button{opacity:1}" @@ -27,9 +27,9 @@ init: function () { return function (o, e) { o.ready(function () { - console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary."); - }); - }; + console.warn("[Deprecation] Manually initializing docsify-copy-code using window.DocsifyCopyCodePlugin.init() is no longer necessary.") + }) + } }, }), (window.$docsify = window.$docsify || {}), @@ -37,28 +37,28 @@ function (o, r) { o.doneEach(function () { var o = Array.apply(null, document.querySelectorAll("pre[data-lang]")), - c = { buttonText: "<> Copy code", errorText: "Error", successText: "Code copied!" }; + c = { buttonText: "<> Copy code", errorText: "Error", successText: "Code copied!" } r.config.copyCode && Object.keys(c).forEach(function (t) { - var n = r.config.copyCode[t]; + var n = r.config.copyCode[t] "string" == typeof n ? (c[t] = n) : "object" === s(n) && Object.keys(n).some(function (o) { - var e = -1 < location.href.indexOf(o); - return (c[t] = e ? n[o] : c[t]), e; - }); - }); + var e = -1 < location.href.indexOf(o) + return (c[t] = e ? n[o] : c[t]), e + }) + }) var e = [ '", - ].join(""); + ].join("") o.forEach(function (o) { o.insertAdjacentHTML("beforeend", e); - }); + }) }), o.mounted(function () { document.querySelector(".content").addEventListener("click", function (o) { @@ -66,26 +66,26 @@ var e = "BUTTON" === o.target.tagName ? o.target : o.target.parentNode, t = document.createRange(), n = e.parentNode.querySelector("code"), - c = window.getSelection(); - t.selectNode(n), c.removeAllRanges(), c.addRange(t); + c = window.getSelection() + t.selectNode(n), c.removeAllRanges(), c.addRange(t) try { document.execCommand("copy") && (e.classList.add("success"), e.querySelector('.label').style.display = 'none', setTimeout(function () { - e.classList.remove("success"); - e.querySelector('.label').style.display = 'inline'; - }, 2000)); + e.classList.remove("success") + e.querySelector('.label').style.display = 'inline'; + }, 2000)) } catch (o) { console.error("docsify-copy-code: ".concat(o)), e.classList.add("error"), setTimeout(function () { - e.classList.remove("error"); + e.classList.remove("error") }, 2000); } - "function" == typeof (c = window.getSelection()).removeRange ? c.removeRange(t) : "function" == typeof c.removeAllRanges && c.removeAllRanges(); + "function" == typeof (c = window.getSelection()).removeRange ? c.removeRange(t) : "function" == typeof c.removeAllRanges && c.removeAllRanges() } - }); - }); + }) + }) }, - ].concat(window.$docsify.plugins || [])); -})(); \ No newline at end of file + ].concat(window.$docsify.plugins || [])) +})(); diff --git a/docs/assets/js/src/onload-hacks.js b/docs/assets/js/src/onload-hacks.js index 5aa26e53d..a45d0612f 100644 --- a/docs/assets/js/src/onload-hacks.js +++ b/docs/assets/js/src/onload-hacks.js @@ -1,10 +1,10 @@ /* Hack page elements on load */ -const taglineWords = []; // for iObserver's scrambleText() + randomizeCase() +const taglineWords = [] // for iObserver's scrambleText() + randomizeCase() const features = [ // for iObserver's typeText() to #feature-list '>> Feature-rich', '>> Object-oriented', '>> Easy-to-use', - '>> Lightweight (yet optimally performant)' ]; -const visibilityMap = []; // to store flags for section visibility + '>> Lightweight (yet optimally performant)' ] +const visibilityMap = [] // to store flags for section visibility const sectionColors = [ // for mdLoaded.then's scroll color hacks '#64ffff', // Importing the Library '#f9ee16', // Greasemonkey @@ -12,154 +12,154 @@ const sectionColors = [ // for mdLoaded.then's scroll color hacks 'orange', // Usage '#b981f9', // Made w/ chatgpt.js '#f581f9', // ChatGPT Infinity tile - '#81f9c3' ]; // Contributors + '#81f9c3' ] // Contributors const iniStarZvelocity = window.starVelocity.z, - warpDuration = 1600, hiWarpDuration = 1400, starResetDelay = 15; + warpDuration = 1600, hiWarpDuration = 1400, starResetDelay = 15 // Define OBSERVERS const mdLoaded = new Promise((resolve) => { const mdObserver = new MutationObserver((mutationsList, observer) => { - if (document.querySelector('#shields')) { observer.disconnect(); resolve(); }}); - mdObserver.observe(document.body, { childList: true, subtree: true }); -}); + if (document.querySelector('#shields')) { observer.disconnect() ; resolve() }}) + mdObserver.observe(document.body, { childList: true, subtree: true }) +}) const iObserver = new IntersectionObserver(entries => { entries.forEach(entry => { // Set visibility FLAG - const key = entry.target.id || entry.target.className; - visibilityMap[key] = entry.isIntersecting; + const key = entry.target.id || entry.target.className + visibilityMap[key] = entry.isIntersecting // Handle COVER if (entry.target.className === 'cover-main') { if (entry.isIntersecting) { // Reset colors - document.querySelector('#kudoai a').style.color = 'white'; + document.querySelector('#kudoai a').style.color = 'white' window.starColor = 'white'; (document.querySelector('#scrollbar-style') || {}).innerText = ( ':root { scrollbar-color: rgb(210,210,210) #1a1a1a }' - + 'body::-webkit-scrollbar-thumb { background-color: white }'); + + 'body::-webkit-scrollbar-thumb { background-color: white }') // Animate KudoAI logo - const kudo = document.querySelector('.kudo'); - kudo.classList.add('hover'); - setTimeout(() => { kudo.classList.remove('hover'); }, 955); + const kudo = document.querySelector('.kudo') + kudo.classList.add('hover') + setTimeout(() => { kudo.classList.remove('hover') }, 955) // Scramble entire tagline + add case randomization layer Array.from( // clear tagline spans to maintain grow effect document.querySelectorAll('span[id^="tagline"]')) - .forEach(span => { span.textContent = ''; }); - scrambleText([taglineWords[0]], document.querySelector('#tagline-pre-adj')); - scrambleText(taglineWords[1], document.querySelector('#tagline-adj'), 750); - scrambleText([taglineWords[2]], document.querySelector('#tagline-post-adj')); - randomizeCase(document.querySelector('#tagline-pre-adj')); - randomizeCase(document.querySelector('#tagline-post-adj')); + .forEach(span => { span.textContent = '' }) + scrambleText([taglineWords[0]], document.querySelector('#tagline-pre-adj')) + scrambleText(taglineWords[1], document.querySelector('#tagline-adj'), 750) + scrambleText([taglineWords[2]], document.querySelector('#tagline-post-adj')) + randomizeCase(document.querySelector('#tagline-pre-adj')) + randomizeCase(document.querySelector('#tagline-post-adj')) // Star boost if (window.starVelocity.z <= iniStarZvelocity) { // to avoid reverse boost from scroll-ups - window.starVelocity.z += .024; // boost velocity + window.starVelocity.z += .024 // boost velocity setTimeout(() => { // slow velocity - window.starVelocity.z -= .02; }, 1155); + window.starVelocity.z -= .02 }, 1155) setTimeout(() => { // slow velocity to original - window.starVelocity.z = iniStarZvelocity; }, 1355); + window.starVelocity.z = iniStarZvelocity }, 1355) } } else // stop scrambling tagline adjective - clearTimeout(scrambleText.timeoutID); + clearTimeout(scrambleText.timeoutID) // Handle FEATURE LIST } else if (entry.target.id === 'feature-list') { // type features or clear content/timeouts - if (entry.isIntersecting) typeText(features, entry.target, 20); - else { entry.target.innerHTML = ''; clearTimeout(typeText.timeoutID); } + if (entry.isIntersecting) typeText(features, entry.target, 20) + else { entry.target.innerHTML = '' ; clearTimeout(typeText.timeoutID) } } -});}); +})}) const onLoadObserver = new MutationObserver(() => { // Exit if not loaded - if (!document.querySelector('.cover-main blockquote p')) return; + if (!document.querySelector('.cover-main blockquote p')) return // Activate SMOOTH SCROLL - smoothScroll(document, 155, 9); + smoothScroll(document, 155, 9) // Hack HOMEPAGE if (/#\/(?:\w{2}(?:-\w{2})?\/)?$/.test(location.hash)) { // Hide SIDEBAR - if (!isMobileDevice()) document.body.className = 'ready close'; + if (!isMobileDevice()) document.body.className = 'ready close' // Populate [taglineWords] for iObserver's scrambleText() + randomizeCase() - const taglineSpans = Array.from(document.querySelectorAll('span[id^="tagline"]')); + const taglineSpans = Array.from(document.querySelectorAll('span[id^="tagline"]')) taglineSpans.map(span => { taglineWords.push( - /pre|post/.exec(span.id) ? span.textContent : span.textContent.split('|')); }); - taglineSpans.forEach(span => { span.textContent = ''; }); // clear them out + /pre|post/.exec(span.id) ? span.textContent : span.textContent.split('|')) }) + taglineSpans.forEach(span => { span.textContent = '' }) // clear them out // Observe COVER for visibility change tagline hacks - iObserver.observe(document.querySelector('.cover-main')); + iObserver.observe(document.querySelector('.cover-main')) // Add TOP GRADIENT const cover = document.querySelector('.cover'), - topGradient = document.createElement('div'); - topGradient.classList.add('top-gradient'); - document.body.append(topGradient); - updateTGvisibility(); // since page load can be below fold + topGradient = document.createElement('div') + topGradient.classList.add('top-gradient') + document.body.append(topGradient) + updateTGvisibility() // since page load can be below fold function updateTGvisibility() { topGradient.style.display = ( // hide/show when fold is 85% at top - window.scrollY > 0.85 * cover.offsetHeight ? '' : 'none' ); } + window.scrollY > 0.85 * cover.offsetHeight ? '' : 'none' ) } mdLoaded.then(() => { // Scroll slightly to overcome Chromium bug preventing parallax if (navigator.userAgent.includes('Chrome')) - window.scrollBy(0, 200); setTimeout(() => window.scrollBy(0, -200), 600); + window.scrollBy(0, 200); setTimeout(() => window.scrollBy(0, -200), 600) // Disable SEARCH - document.querySelector('.search').style.display = 'none'; - document.querySelector('.sidebar-nav').style.paddingTop = '102px'; + document.querySelector('.search').style.display = 'none' + document.querySelector('.sidebar-nav').style.paddingTop = '102px' // Create/select FEATURE LIST const featureListDiv = document.querySelector('#feature-list') || // select div - document.createElement('div'); // ...or create it + document.createElement('div') // ...or create it if (!featureListDiv.parentElement) { // append created div if not in DOM - featureListDiv.setAttribute('id', 'feature-list'); - const introDiv = document.querySelector('#intro'); + featureListDiv.setAttribute('id', 'feature-list') + const introDiv = document.querySelector('#intro') introDiv.parentElement.insertBefore( // insert after description - featureListDiv, introDiv.nextElementSibling.nextElementSibling); + featureListDiv, introDiv.nextElementSibling.nextElementSibling) } // ...then observe for visibility change to apply typing hack - iObserver.observe(featureListDiv); + iObserver.observe(featureListDiv) // Append COPYRIGHT NOTICE footer const article = document.querySelector('article'), // to insert at end of - copyrightFooter = document.createElement('div'); - copyrightFooter.id = 'copyright-footer'; + copyrightFooter = document.createElement('div') + copyrightFooter.id = 'copyright-footer' copyrightFooter.innerHTML = 'Copyright © 2023–' + new Date().getFullYear() + ' KudoAI.
' + 'Designed by Adam Lui / ' + 'Powered by Docsify / ' - + 'Hosted by GitHub'; - article.append(copyrightFooter); + + 'Hosted by GitHub' + article.append(copyrightFooter) // Replace GitHub demo embed w/ YouTube one const ghDemo = document.querySelector('a[href*="/assets/10906554/f53c740f-d5e0-49b6-ae02-3b3140b0f8a4"]'), - ytDemo = document.createElement('iframe'); - ytDemo.setAttribute('width', '855'); ytDemo.setAttribute('height', '455'); - ytDemo.setAttribute('src', 'https://www.youtube.com/embed/yG8DtsEo0PM?rel=0'); + ytDemo = document.createElement('iframe') + ytDemo.setAttribute('width', '855'); ytDemo.setAttribute('height', '455') + ytDemo.setAttribute('src', 'https://www.youtube.com/embed/yG8DtsEo0PM?rel=0') ytDemo.allow = 'web-share' + ( !navigator.userAgent.includes('Firefox') ? - 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' : '' ); - ytDemo.setAttribute('allowfullscreen', ''); - ytDemo.style.minWidth = 'fit-content'; ytDemo.style.width = '855px'; ytDemo.style.marginBottom = '30px'; - ghDemo.parentNode.replaceChild(ytDemo, ghDemo); - ytDemo.parentNode.style.textAlign = 'center'; + 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' : '' ) + ytDemo.setAttribute('allowfullscreen', '') + ytDemo.style.minWidth = 'fit-content'; ytDemo.style.width = '855px'; ytDemo.style.marginBottom = '30px' + ghDemo.parentNode.replaceChild(ytDemo, ghDemo) + ytDemo.parentNode.style.textAlign = 'center' // Strip blockquote wrappers from showcase app descriptions document.querySelectorAll('blockquote').forEach(blockquote => { - const parent = blockquote.parentNode, content = blockquote.innerHTML; - parent.replaceChild(document.createRange().createContextualFragment(content), blockquote); - }); + const parent = blockquote.parentNode, content = blockquote.innerHTML + parent.replaceChild(document.createRange().createContextualFragment(content), blockquote) + }) // Convert weserv.nl img srcs in contributor avatars into renderable ones document.querySelectorAll('img[src], source[srcset]').forEach(elem => { @@ -172,31 +172,31 @@ const onLoadObserver = new MutationObserver(() => { }) // Add FADE classes to elements - const fadeUpElements = [], fadeRightElements = [], fadeLeftElements = []; + const fadeUpElements = [], fadeRightElements = [], fadeLeftElements = [] fadeUpElements.push(...document.querySelectorAll( '.cover-main img, .cover-main a,' // cover elements + 'h2, h3, p, pre, main li,' // general elements - + 'div#partners-collage, #copyright-footer')); // footer elements - fadeUpElements.forEach((element) => { element.classList.add('content-fadeup'); }); + + 'div#partners-collage, #copyright-footer')) // footer elements + fadeUpElements.forEach((element) => { element.classList.add('content-fadeup') }) fadeUpElements.push( // language selector - document.querySelector('#language-menu')); - fadeUpElements[fadeUpElements.length - 1].classList.add('menu-fadeup'); + document.querySelector('#language-menu')) + fadeUpElements[fadeUpElements.length - 1].classList.add('menu-fadeup') fadeRightElements.push(...document.querySelectorAll( // left-side showcase apps `#showcase ~ h3:nth-of-type(odd):not(#contributors ~ *), - #showcase ~ h3 + p:nth-of-type(odd):not(#contributors ~ *`)); - fadeRightElements.forEach((element) => { element.classList.add('content-faderight'); }); + #showcase ~ h3 + p:nth-of-type(odd):not(#contributors ~ *`)) + fadeRightElements.forEach((element) => { element.classList.add('content-faderight') }) fadeLeftElements.push(...document.querySelectorAll( // right-side showcase apps `#showcase ~ h3:nth-of-type(even):not(#contributors ~ *), - #showcase ~ h3 + p:nth-of-type(even):not(#contributors ~ *`)); - fadeLeftElements.forEach((element) => { element.classList.add('content-fadeleft'); }); - const fadeElements = [...fadeUpElements, ...fadeRightElements, ...fadeLeftElements]; + #showcase ~ h3 + p:nth-of-type(even):not(#contributors ~ *`)) + fadeLeftElements.forEach((element) => { element.classList.add('content-fadeleft') }) + const fadeElements = [...fadeUpElements, ...fadeRightElements, ...fadeLeftElements] // ...then observe for visibility change to update element/sidebar states - const sideNavItems = [...document.querySelectorAll('.sidebar-nav li')]; + const sideNavItems = [...document.querySelectorAll('.sidebar-nav li')] const fadeObserver = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { - entry.target.classList.add('visible'); + entry.target.classList.add('visible') // Update sidebar w/ active class for headings if (entry.target.tagName.startsWith('H')) { @@ -204,39 +204,39 @@ const onLoadObserver = new MutationObserver(() => { // Find the nav item that matches intersecting heading const headingText = entry.target.querySelector('a').textContent, activeNavItem = (document.querySelector( - `a[title="${ headingText }"]`) || {}).parentElement; + `a[title="${ headingText }"]`) || {}).parentElement // Add `nav-active` class to matched nav item if (activeNavItem) { - sideNavItems.forEach(item => item.classList.remove('nav-active')); - activeNavItem.classList.add('nav-active'); + sideNavItems.forEach(item => item.classList.remove('nav-active')) + activeNavItem.classList.add('nav-active') } } - } else entry.target.classList.remove('visible'); - });}, { root: null, threshold: 0.02 }); - fadeElements.forEach((element) => { fadeObserver.observe(element); }); + } else entry.target.classList.remove('visible') + })}, { root: null, threshold: 0.02 }) + fadeElements.forEach((element) => { fadeObserver.observe(element) }) // Change stars shield link to repo const starsShieldLink = document.querySelector('a[href$="stargazers"]'), - href = starsShieldLink.getAttribute('href'); - starsShieldLink.setAttribute('href', href.replace('/stargazers', '')); + href = starsShieldLink.getAttribute('href') + starsShieldLink.setAttribute('href', href.replace('/stargazers', '')) // Establish TRIGGER POINTS for scroll FX - const triggerElements = [], triggerPoints = []; - triggerElements.push(...document.querySelectorAll('h2')); - triggerElements.push(document.querySelector('h3#-greasemonkey')); - triggerElements.push(document.querySelector('h3#-chrome')); + const triggerElements = [], triggerPoints = [] + triggerElements.push(...document.querySelectorAll('h2')) + triggerElements.push(document.querySelector('h3#-greasemonkey')) + triggerElements.push(document.querySelector('h3#-chrome')) triggerElements.push( // 1st showcase tile - document.querySelector('img[src*="chatgpt-infinity"]')); + document.querySelector('img[src*="chatgpt-infinity"]')) triggerElements.forEach(element => { - const elementPos = element.getBoundingClientRect().top; + const elementPos = element.getBoundingClientRect().top const vOffsetDivisor = ( // higher = lower pos element.id.includes('⚡') ? 1.5 // Importing the Library section : element.tagName === 'IMG' ? 0.8 // 1st showcase tile - : 8.8 ); // headings - triggerPoints.push(elementPos - window.innerHeight/vOffsetDivisor); - }); - triggerPoints.sort((a, b) => a - b); // sort ascending + : 8.8 ) // headings + triggerPoints.push(elementPos - window.innerHeight/vOffsetDivisor) + }) + triggerPoints.sort((a, b) => a - b) // sort ascending // Update COLORS + STAR VELOCITY on scroll window.addEventListener('scroll', () => { @@ -245,55 +245,55 @@ const onLoadObserver = new MutationObserver(() => { if (visibilityMap['cover-main'] || visibilityMap['feature-list']) return; // Determine current section - let currentSection = 0; + let currentSection = 0 while (window.scrollY > triggerPoints[currentSection] && currentSection < triggerPoints.length) - currentSection++; + currentSection++ // Color/animate logo/stars + color scrollbar if section changed - const sectionColor = sectionColors[currentSection - 2]; + const sectionColor = sectionColors[currentSection - 2] if (sectionColor !== window.starColor) { // Color/animate stars - window.starColor = sectionColor; + window.starColor = sectionColor setTimeout(() => { // schedule color reset if (window.starVelocity.z <= iniStarZvelocity) { - window.starColor = 'white'; }}, warpDuration + starResetDelay); - window.starVelocity.z += .0045; // boost velocity + window.starColor = 'white' }}, warpDuration + starResetDelay) + window.starVelocity.z += .0045 // boost velocity setTimeout(() => { // slow velocity - window.starVelocity.z = Math.max(iniStarZvelocity, window.starVelocity.z - .0025); - }, hiWarpDuration); + window.starVelocity.z = Math.max(iniStarZvelocity, window.starVelocity.z - .0025) + }, hiWarpDuration) setTimeout(() => { // slow velocity to original - window.starVelocity.z = Math.max(iniStarZvelocity, window.starVelocity.z - .002); - }, warpDuration); + window.starVelocity.z = Math.max(iniStarZvelocity, window.starVelocity.z - .002) + }, warpDuration) // Color/animate logo const kudoAIlogo = document.querySelector('#kudoai a'), - kudo = document.querySelector('.kudo'); - kudoAIlogo.style.color = sectionColor; - kudo.classList.add('hover'); // trigger slide animation + kudo = document.querySelector('.kudo') + kudoAIlogo.style.color = sectionColor + kudo.classList.add('hover') // trigger slide animation setTimeout(() => { // schedule color/animation reset if (window.starVelocity.z <= iniStarZvelocity) { - kudoAIlogo.style.color = 'white'; - kudo.classList.remove('hover'); - }}, warpDuration + 5); + kudoAIlogo.style.color = 'white' + kudo.classList.remove('hover') + }}, warpDuration + 5) // Color scrollbar const scrollbarStyle = document.querySelector('#scrollbar-style') || // select div - document.createElement('style'); // ...or create it + document.createElement('style') // ...or create it if (!scrollbarStyle.parentElement) { // append created div if not in DOM - scrollbarStyle.setAttribute('id', 'scrollbar-style'); - document.head.append(scrollbarStyle); + scrollbarStyle.setAttribute('id', 'scrollbar-style') + document.head.append(scrollbarStyle) } scrollbarStyle.innerText = ( `:root { scrollbar-color: ${ sectionColor } #1a1a1a }` - + `body::-webkit-scrollbar-thumb { background-color: ${ sectionColor } }`); + + `body::-webkit-scrollbar-thumb { background-color: ${ sectionColor } }`) setTimeout(() => { // schedule color reset if (window.starVelocity.z <= iniStarZvelocity) { scrollbarStyle.innerText = ( ':root { scrollbar-color: rgb(210,210,210) #1a1a1a }' - + 'body::-webkit-scrollbar-thumb { background-color: white }'); - }}, warpDuration + 5); + + 'body::-webkit-scrollbar-thumb { background-color: white }') + }}, warpDuration + 5) } }); @@ -302,51 +302,51 @@ const onLoadObserver = new MutationObserver(() => { document.querySelectorAll('picture').forEach(picture => { const srcElement = picture.querySelector('source'), srcSet = srcElement.getAttribute('srcset'), - imgElement = document.createElement('img'); - imgElement.setAttribute('src', srcSet); - picture.parentNode.replaceChild(imgElement, picture); - }); + imgElement = document.createElement('img') + imgElement.setAttribute('src', srcSet) + picture.parentNode.replaceChild(imgElement, picture) + }) // Append EMAIL SIGNUP footer const partnersCollage = document.getElementById('partners-collage'), // to insert after - emailFooter = document.createElement('div'); + emailFooter = document.createElement('div') fetch('assets/html/footer.html') .then(response => response.text()).then(html => { - emailFooter.innerHTML = html; - partnersCollage.insertAdjacentElement('afterend', emailFooter); - }); + emailFooter.innerHTML = html + partnersCollage.insertAdjacentElement('afterend', emailFooter) + }) // Remove readme's BACK-TO-TOP link - const readmeBTTlink = [...document.querySelectorAll('a')].find(link => link.textContent.includes('↑')); - readmeBTTlink?.previousSibling.remove(); readmeBTTlink?.remove(); + const readmeBTTlink = [...document.querySelectorAll('a')].find(link => link.textContent.includes('↑')) + readmeBTTlink?.previousSibling.remove() ; readmeBTTlink?.remove() setTimeout(() => { // Add PARALLAX // Target TRIGGERS - const parallaxTriggers = []; + const parallaxTriggers = [] document.querySelectorAll('#main, h2:not([id="about"])').forEach(trigger => { - const y = trigger.getBoundingClientRect().top - window.innerHeight / 1.2; - const triggerElem = trigger.tagName === 'H2' ? trigger.parentElement : trigger; - parallaxTriggers.push({ element: triggerElem, y }); - }); + const y = trigger.getBoundingClientRect().top - window.innerHeight / 1.2 + const triggerElem = trigger.tagName === 'H2' ? trigger.parentElement : trigger + parallaxTriggers.push({ element: triggerElem, y }) + }) // Add SCROLL listener window.addEventListener('scroll', () => { - updateTGvisibility(); + updateTGvisibility() parallaxTriggers.forEach(trigger => { if (window.scrollY >= trigger.y && window.scrollY < trigger.y + window.innerHeight) { // Target previous elements to hack - const prevElems = []; + const prevElems = [] if (trigger.element.id === 'main') - prevElems.push(document.querySelector('.cover-main')); + prevElems.push(document.querySelector('.cover-main')) else { // target previous 6 siblings - let currentElem = trigger.element.previousElementSibling; - for (let i = 0; i < 7; i++) { + let currentElem = trigger.element.previousElementSibling + for (let i = 0 ; i < 7 ; i++) { if (currentElem) { - prevElems.push(currentElem); - currentElem = currentElem.previousElementSibling; - } else break; + prevElems.push(currentElem) + currentElem = currentElem.previousElementSibling + } else break } } @@ -359,49 +359,49 @@ const onLoadObserver = new MutationObserver(() => { parallaxOffset = topGap * -0.55, scaleDelay = 285, // px from trigger.y to delay scaling scaleFactor = topGap > -scaleDelay ? 1 - : 1 - Math.abs(topGap + scaleDelay) / 5 / window.innerHeight; + : 1 - Math.abs(topGap + scaleDelay) / 5 / window.innerHeight - try { elem.classList.remove('content-fadeup'); } catch (err) {} - elem.style.opacity = newOpacity; - elem.style.transform = `translateY(${parallaxOffset}px) scale(${scaleFactor})`; - elem.style.filter = `blur(${blurAmount}px)`; - }); + try { elem.classList.remove('content-fadeup') } catch (err) {} + elem.style.opacity = newOpacity + elem.style.transform = `translateY(${parallaxOffset}px) scale(${scaleFactor})` + elem.style.filter = `blur(${blurAmount}px)` + }) - }});});}, 100); - }); + }})})}, 100) + }) // Hide SITE LANG SELECTOR from NON-HOME pages - } else document.querySelector('.app-nav').style.display = 'none'; + } else document.querySelector('.app-nav').style.display = 'none' // Hack LICENSE/SECURIY pages if (/LICENSE|SECURITY/.test(location.hash)) { // Hide SIDEBAR - if (!isMobileDevice()) document.body.className = 'ready close'; + if (!isMobileDevice()) document.body.className = 'ready close' // Correct DOC LANG SELECTOR links mdLoaded.then(() => { - const docLangSelector = document.querySelectorAll('h5 a'); + const docLangSelector = document.querySelectorAll('h5 a') for (const lang of docLangSelector) - lang.href = lang.href.replace(/(https?:\/\/[^/]+\/)([^.]+)\.md/, '$1#/$2'); - }); + lang.href = lang.href.replace(/(https?:\/\/[^/]+\/)([^.]+)\.md/, '$1#/$2') + }) } // DISCONNECT observer - onLoadObserver.disconnect(); + onLoadObserver.disconnect() -}); +}) // Define FUNCTIONS function isMobileDevice() { - return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); } + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) } function validateIntArg(arg, name, defaultVal) { - if (arg === undefined) return defaultVal; // no validation required + if (arg === undefined) return defaultVal // no validation required if (!Number.isInteger(arg) && !/^\d+$/.test(arg)) - throw new Error(name + ' must be an integer.'); - return parseInt(arg, 10); + throw new Error(name + ' must be an integer.') + return parseInt(arg, 10) } function smoothScroll(target, speed, smooth) { @@ -411,40 +411,40 @@ function smoothScroll(target, speed, smooth) { target = (document.scrollingElement || document.documentElement || document.body.parentNode - || document.body); // cross browser support for document scrolling + || document.body) // cross browser support for document scrolling // Init variables - let moving = false, pos = target.scrollTop; + let moving = false, pos = target.scrollTop const frame = target === document.body && document.documentElement ? document.documentElement - : target; // safari + : target // safari // Add listeners - target.addEventListener('mousewheel', scrolled, { passive: false }); - target.addEventListener('DOMMouseScroll', scrolled, { passive: false }); + target.addEventListener('mousewheel', scrolled, { passive: false }) + target.addEventListener('DOMMouseScroll', scrolled, { passive: false }) function scrolled(e) { - e.preventDefault(); // disable default scrolling - const delta = normalizeWheelDelta(e); - pos += -delta * speed; + e.preventDefault() // disable default scrolling + const delta = normalizeWheelDelta(e) + pos += -delta * speed pos = ( // limit scrolling - Math.max(0, Math.min(pos, target.scrollHeight - frame.clientHeight))); - if (!moving) update(); + Math.max(0, Math.min(pos, target.scrollHeight - frame.clientHeight))) + if (!moving) update() } function normalizeWheelDelta(e) { if (e.detail) { if (e.wheelDelta) - return e.wheelDelta/e.detail/40 * (e.detail>0 ? 1 : -1); // Opera - else return -e.detail/3; // Firefox - } else return e.wheelDelta/120; // IE/Safari/Chrome + return e.wheelDelta/e.detail/40 * (e.detail>0 ? 1 : -1) // Opera + else return -e.detail/3 // Firefox + } else return e.wheelDelta/120 // IE/Safari/Chrome } function update() { - moving = true; - const delta = (pos - target.scrollTop) / smooth; - target.scrollTop += delta; - if (Math.abs(delta) > 0.5) requestFrame(update); - else moving = false; + moving = true + const delta = (pos - target.scrollTop) / smooth + target.scrollTop += delta + if (Math.abs(delta) > 0.5) requestFrame(update) + else moving = false } const requestFrame = function() { // requestAnimationFrame cross browser @@ -455,30 +455,30 @@ function smoothScroll(target, speed, smooth) { window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(func) { window.setTimeout(func, 1000 / 50); } - ); - }(); + ) + }() } function scrambleText(text, destination, delayBetweenWords, textIdx = 0) { // Validate args - if (typeof text === 'string') text = [text]; // array of strings to scramble + if (typeof text === 'string') text = [text] // array of strings to scramble if (!destination?.nodeName) // DOM element to scramble to - throw new Error('Destination (2nd arg) must be a DOM element'); + throw new Error('Destination (2nd arg) must be a DOM element') if (delayBetweenWords) { // ms to delay between scrambles if (!Number.isInteger(delayBetweenWords) && !/^\d+$/.test(delayBetweenWords)) - throw new Error('Delay betweeen words (3nd arg) must be an integer (ms)'); - delayBetweenWords = parseInt(delayBetweenWords, 10); + throw new Error('Delay betweeen words (3nd arg) must be an integer (ms)') + delayBetweenWords = parseInt(delayBetweenWords, 10) } // Scramble text - const textToScramble = new Scramble(destination); + const textToScramble = new Scramble(destination) textToScramble.setText(text[textIdx]) .then(() => { if (delayBetweenWords && visibilityMap['cover-main']) { scrambleText.timeoutID = setTimeout(() => { - scrambleText(text, destination, delayBetweenWords, (textIdx + 1) % text.length); }, - delayBetweenWords); - }}); + scrambleText(text, destination, delayBetweenWords, (textIdx + 1) % text.length) }, + delayBetweenWords) + }}) } function randomizeCase(targetNode, iniDelay, finalDelay, incrementA, incrementB, inflectionPt) { @@ -487,130 +487,130 @@ function randomizeCase(targetNode, iniDelay, finalDelay, incrementA, incrementB, if (!targetNode?.nodeName) // DOM element to randomize case of text content throw new Error('Target node (1st arg) must be a DOM element'); iniDelay = validateIntArg( // ms to initially between case switches - iniDelay, 'Initial delay', 5); + iniDelay, 'Initial delay', 5) finalDelay = validateIntArg( // ms to finally delay between case switches - finalDelay, 'Final delay', 1000); + finalDelay, 'Final delay', 1000) incrementA = validateIntArg( // ms to initially increment from iniDelay to finalDelay - incrementA, 'Increment A', 10); + incrementA, 'Increment A', 10) incrementB = validateIntArg( // ms to increment from iniDelay to finalDelay after inflection - incrementB, 'Increment B', 111); + incrementB, 'Increment B', 111) inflectionPt = validateIntArg( // ms of iniDelay state before inflecting to Increment B - inflectionPt, 'Inflection point', 265); + inflectionPt, 'Inflection point', 265) // Randomize case targetNode.textContent = targetNode.textContent.split('').map(letter => { - return Math.random() < 0.5 ? letter.toUpperCase() : letter.toLowerCase(); - }).join(''); - randomizeCase.iniDelay = randomizeCase.iniDelay || iniDelay; - randomizeCase.iniDelay += randomizeCase.iniDelay < inflectionPt ? incrementA : incrementB; - if (randomizeCase.iniDelay > finalDelay) randomizeCase.iniDelay = finalDelay; // cap at `finalDelay` + return Math.random() < 0.5 ? letter.toUpperCase() : letter.toLowerCase() + }).join('') + randomizeCase.iniDelay = randomizeCase.iniDelay || iniDelay + randomizeCase.iniDelay += randomizeCase.iniDelay < inflectionPt ? incrementA : incrementB + if (randomizeCase.iniDelay > finalDelay) randomizeCase.iniDelay = finalDelay // cap at `finalDelay` setTimeout(() => { - randomizeCase(targetNode, iniDelay, finalDelay, incrementA, incrementB, inflectionPt); - }, randomizeCase.iniDelay); + randomizeCase(targetNode, iniDelay, finalDelay, incrementA, incrementB, inflectionPt) + }, randomizeCase.iniDelay) } function typeText(txtToType, destination, typeDelay, iniTxtToType, iniTxtPos, linesToScrollAt) { // Validate args - if (typeof txtToType === 'string') txtToType = [txtToType]; // array of strings to type + if (typeof txtToType === 'string') txtToType = [txtToType] // array of strings to type if (!destination?.nodeName) // DOM element to type to - throw new Error('Destination must be a DOM element'); + throw new Error('Destination must be a DOM element') typeDelay = validateIntArg( // ms to delay between chars typed - typeDelay, 'Typing delay', 30); + typeDelay, 'Typing delay', 30) iniTxtToType = validateIntArg( // index of txt array to start typing - iniTxtToType, 'Initial text array index', 0); + iniTxtToType, 'Initial text array index', 0) iniTxtPos = validateIntArg( // position in txt string to start typing from - iniTxtPos, 'Initial text string position', 3); + iniTxtPos, 'Initial text string position', 3) linesToScrollAt = validateIntArg( // lines reached before scrolling up - linesToScrollAt, 'Lines to scroll at', 5); + linesToScrollAt, 'Lines to scroll at', 5) // Init variables let typeContent = ' ', - iniRow = Math.max(0, iniTxtToType - linesToScrollAt); + iniRow = Math.max(0, iniTxtToType - linesToScrollAt) // Type text - while (iniRow < iniTxtToType) typeContent += txtToType[iniRow++] + '

'; - destination.innerHTML = typeContent + txtToType[iniTxtToType].substring(0, iniTxtPos) + '_'; + while (iniRow < iniTxtToType) typeContent += txtToType[iniRow++] + '

' + destination.innerHTML = typeContent + txtToType[iniTxtToType].substring(0, iniTxtPos) + '_' if (iniTxtPos++ == txtToType[iniTxtToType].length) { - iniTxtPos = 0; iniTxtToType++; + iniTxtPos = 0 ; iniTxtToType++ if (iniTxtToType != txtToType.length) { // if end of string reached typeText.timeoutID = setTimeout(() => { - typeText(txtToType, destination, typeDelay, iniTxtToType, iniTxtPos); - }, 88); // pause til next string + typeText(txtToType, destination, typeDelay, iniTxtToType, iniTxtPos) + }, 88) // pause til next string }} else typeText.timeoutID = setTimeout(() => { - typeText(txtToType, destination, typeDelay, iniTxtToType, iniTxtPos); - }, typeDelay + (Math.random() * 220) - 110); + typeText(txtToType, destination, typeDelay, iniTxtToType, iniTxtPos) + }, typeDelay + (Math.random() * 220) - 110) } // Define SCRAMBLE class class Scramble { constructor(el) { - this.el = el; - this.chars = '!<>-_\\/[]{}—=+*^?#________'; - this.update = this.update.bind(this); + this.el = el + this.chars = '!<>-_\\/[]{}—=+*^?#________' + this.update = this.update.bind(this) } setText(newText) { const oldText = this.el.innerText, length = Math.max(oldText.length, newText.length), - promise = new Promise((resolve) => this.resolve = resolve); - this.queue = []; - for (let i = 0; i < length; i++) { + promise = new Promise((resolve) => this.resolve = resolve) + this.queue = [] + for (let i = 0 ; i < length ; i++) { const from = oldText[i] || '', to = newText[i] || '', start = Math.floor(Math.random() * 45), // speed of beginning scramble - end = start + Math.floor(Math.random() * 45); // speed of end scramble - this.queue.push({ from, to, start, end }); + end = start + Math.floor(Math.random() * 45) // speed of end scramble + this.queue.push({ from, to, start, end }) } - cancelAnimationFrame(this.frameRequest); - this.frame = 0; this.update(); return promise; + cancelAnimationFrame(this.frameRequest) + this.frame = 0 ; this.update() ; return promise } update() { - let output = '', complete = 0; - for (let i = 0, n = this.queue.length; i < n; i++) { - let { from, to, start, end, char } = this.queue[i]; - if (this.frame >= end) { complete++; output += to; } + let output = '', complete = 0 + for (let i = 0, n = this.queue.length ; i < n ; i++) { + let { from, to, start, end, char } = this.queue[i] + if (this.frame >= end) { complete++ ; output += to } else if (this.frame >= start) { if (!char || Math.random() < 0.28) { - char = this.randomChar(); - this.queue[i].char = char; + char = this.randomChar() + this.queue[i].char = char } - output += `${ char }`; - } else output += from; + output += `${ char }` + } else output += from } - this.el.innerHTML = output; - if (complete === this.queue.length) this.resolve(); + this.el.innerHTML = output + if (complete === this.queue.length) this.resolve() else { - this.frameRequest = requestAnimationFrame(this.update); - this.frame++; + this.frameRequest = requestAnimationFrame(this.update) + this.frame++ } } randomChar() { - return this.chars[Math.floor(Math.random() * this.chars.length)]; } + return this.chars[Math.floor(Math.random() * this.chars.length)] } } // Run MAIN routine // Add listeners to language selector const langMenu = document.getElementById('language-menu'), - langSelector = document.getElementById('language-selector'); -let hideTimeout; // to account for gap between button & menu + langSelector = document.getElementById('language-selector') +let hideTimeout // to account for gap between button & menu langSelector.onmouseover = langSelector.onmouseout = langMenu.onmouseover = langMenu.onmouseout = event => { - clearTimeout(hideTimeout); - if (event.type == 'mouseover') langMenu.style.display = 'block'; + clearTimeout(hideTimeout) + if (event.type == 'mouseover') langMenu.style.display = 'block' else if (event.type == 'mouseout') - hideTimeout = setTimeout(() => langMenu.style.display = 'none', 55); + hideTimeout = setTimeout(() => langMenu.style.display = 'none', 55) }; document.querySelectorAll('#language-selector a').forEach(link => { // add listener to hide tooltips - link.addEventListener('mouseenter', () => { link.removeAttribute('title'); });}); + link.addEventListener('mouseenter', () => { link.removeAttribute('title') })}) document.querySelectorAll('.dropdown-link').forEach(link => { // add listener to dismisss menu - link.addEventListener('click', () => langMenu.style.display = 'none');}); + link.addEventListener('click', () => langMenu.style.display = 'none')}) // Observe for load + re-connect on nav to new hash -onLoadObserver.observe(document.body, { childList: true, subtree: true }); -let fromUnhashedURL = window.location.href.includes('#'); +onLoadObserver.observe(document.body, { childList: true, subtree: true }) +let fromUnhashedURL = window.location.href.includes('#') window.addEventListener('hashchange', () => { - if (!fromUnhashedURL) fromUnhashedURL = true; + if (!fromUnhashedURL) fromUnhashedURL = true else if (fromUnhashedURL) - onLoadObserver.observe(document.body, { childList: true, subtree: true }); + onLoadObserver.observe(document.body, { childList: true, subtree: true }) }); diff --git a/docs/assets/js/src/starry-background.js b/docs/assets/js/src/starry-background.js index 9b55c08b0..c9f97e663 100644 --- a/docs/assets/js/src/starry-background.js +++ b/docs/assets/js/src/starry-background.js @@ -3,109 +3,109 @@ * . . . * . . * */ // Init variables -window.starColor = 'white'; -window.starVelocity = { x: 0, y: 0, tx: 0, ty: 0, z: 0.0005 }; +window.starColor = 'white' +window.starVelocity = { x: 0, y: 0, tx: 0, ty: 0, z: 0.0005 } const starSize = 6, starMinScale = 0.00000000025, overflowThreshold = 50, starCount = ( window.innerWidth + window.innerHeight ) / ( navigator.userAgent.indexOf('Firefox') > -1 ? 18 : 11 ), canvas = document.querySelector( 'canvas' ), - context = canvas.getContext( '2d' ); + context = canvas.getContext( '2d' ) let scale = 0.5, // device pixel ratio stars = [], width, height, pointerX, pointerY, - touchInput = false; + touchInput = false // Generate/animate stars -for (let i = 0; i < starCount; i++) - stars.push({ x: 0, y: 0, z: starMinScale + Math.random() * ( 1 - starMinScale )}); -updateCanvasSize(); animateStars(); +for (let i = 0 ; i < starCount ; i++) + stars.push({ x: 0, y: 0, z: starMinScale + Math.random() * ( 1 - starMinScale )}) +updateCanvasSize() ; animateStars() // Add listeners -window.onresize = updateCanvasSize; +window.onresize = updateCanvasSize document.onmousemove = (event) => { - touchInput = false; movePointer(event.clientX, event.clientY); -}; + touchInput = false ; movePointer(event.clientX, event.clientY) +} document.ontouchmove = (event) => { - touchInput = true; - movePointer(event.touches[0].clientX, event.touches[0].clientY); - event.preventDefault(); -}; -document.ontouchend = () => { pointerX = null; pointerY = null; }; -document.onmouseleave = () => { pointerX = null; pointerY = null; }; + touchInput = true + movePointer(event.touches[0].clientX, event.touches[0].clientY) + event.preventDefault() +} +document.ontouchend = () => { pointerX = null ; pointerY = null } +document.onmouseleave = () => { pointerX = null ; pointerY = null } // Define FUNCTIONS function updateCanvasSize() { - width = window.innerWidth * scale; height = window.innerHeight * scale; - canvas.width = width; canvas.height = height; + width = window.innerWidth * scale; height = window.innerHeight * scale + canvas.width = width ; canvas.height = height stars.forEach((star) => { // position it - star.x = Math.random() * width; star.y = Math.random() * height; }); + star.x = Math.random() * width ; star.y = Math.random() * height }) } function animateStars() { // Clear previous frame - context.clearRect(0, 0, width, height); + context.clearRect(0, 0, width, height) // Update star positions - window.starVelocity.tx *= 0.86; window.starVelocity.ty *= 0.86; // proportional to momentum - window.starVelocity.x += ( window.starVelocity.tx - window.starVelocity.x ) * 0.8; - window.starVelocity.y += ( window.starVelocity.ty - window.starVelocity.y ) * 0.8; + window.starVelocity.tx *= 0.86 ; window.starVelocity.ty *= 0.86 // proportional to momentum + window.starVelocity.x += ( window.starVelocity.tx - window.starVelocity.x ) * 0.8 + window.starVelocity.y += ( window.starVelocity.ty - window.starVelocity.y ) * 0.8 stars.forEach((star) => { - star.x += window.starVelocity.x * star.z; - star.y += window.starVelocity.y * star.z; - star.x += (star.x - width/2) * window.starVelocity.z * star.z; - star.y += (star.y - height/2) * window.starVelocity.z * star.z; - star.z += window.starVelocity.z; + star.x += window.starVelocity.x * star.z + star.y += window.starVelocity.y * star.z + star.x += (star.x - width/2) * window.starVelocity.z * star.z + star.y += (star.y - height/2) * window.starVelocity.z * star.z + star.z += window.starVelocity.z // Recycle star when out-of-bounds if (star.x < -overflowThreshold || star.x > width + overflowThreshold || star.y < -overflowThreshold || star.y > height + overflowThreshold) { - let direction = 'z', vx = Math.abs(window.starVelocity.x), vy = Math.abs(window.starVelocity.y); + let direction = 'z', vx = Math.abs(window.starVelocity.x), vy = Math.abs(window.starVelocity.y) if (vx > 1 || vy > 1) { - let axis; - if (vx > vy) axis = Math.random() < vx / ( vx + vy ) ? 'h' : 'v'; - else axis = Math.random() < vy / ( vx + vy ) ? 'v' : 'h'; - if (axis === 'h') direction = window.starVelocity.x > 0 ? 'l' : 'r'; - else direction = window.starVelocity.y > 0 ? 't' : 'b'; + let axis + if (vx > vy) axis = Math.random() < vx / ( vx + vy ) ? 'h' : 'v' + else axis = Math.random() < vy / ( vx + vy ) ? 'v' : 'h' + if (axis === 'h') direction = window.starVelocity.x > 0 ? 'l' : 'r' + else direction = window.starVelocity.y > 0 ? 't' : 'b' } - star.z = starMinScale + Math.random() * ( 1 - starMinScale ); - if (direction === 'z') { star.z = 0.1; star.x = Math.random() * width; star.y = Math.random() * height; } - else if (direction === 'l') { star.x = -overflowThreshold; star.y = height * Math.random(); } - else if (direction === 'r') { star.x = width + overflowThreshold; star.y = height * Math.random(); } - else if (direction === 't') { star.x = width * Math.random(); star.y = -overflowThreshold; } - else if (direction === 'b') { star.x = width * Math.random(); star.y = height + overflowThreshold; } + star.z = starMinScale + Math.random() * ( 1 - starMinScale ) + if (direction === 'z') { star.z = 0.1 ; star.x = Math.random() * width ; star.y = Math.random() * height } + else if (direction === 'l') { star.x = -overflowThreshold ; star.y = height * Math.random() } + else if (direction === 'r') { star.x = width + overflowThreshold ; star.y = height * Math.random() } + else if (direction === 't') { star.x = width * Math.random() ; star.y = -overflowThreshold } + else if (direction === 'b') { star.x = width * Math.random() ; star.y = height + overflowThreshold } } }); // Render stars stars.forEach((star) => { - context.beginPath(); - context.lineCap = 'round'; - context.lineWidth = starSize * star.z * scale; - context.globalAlpha = 0.5 + 0.5*Math.random(); - context.strokeStyle = window.starColor; - context.beginPath(); - context.moveTo( star.x, star.y ); + context.beginPath() + context.lineCap = 'round' + context.lineWidth = starSize * star.z * scale + context.globalAlpha = 0.5 + 0.5*Math.random() + context.strokeStyle = window.starColor + context.beginPath() + context.moveTo( star.x, star.y ) let tailX = window.starVelocity.x * 2, - tailY = window.starVelocity.y * 2; + tailY = window.starVelocity.y * 2 // stroke() wont work on an invisible line - if (Math.abs(tailX) < 0.1) tailX = 0.5; - if (Math.abs(tailY) < 0.1) tailY = 0.5; + if (Math.abs(tailX) < 0.1) tailX = 0.5 + if (Math.abs(tailY) < 0.1) tailY = 0.5 - context.lineTo( star.x + tailX, star.y + tailY ); - context.stroke(); - }); + context.lineTo( star.x + tailX, star.y + tailY ) + context.stroke() + }) // Loop animation - requestAnimationFrame(animateStars); + requestAnimationFrame(animateStars) } function movePointer(x, y) { if (typeof pointerX === 'number' && typeof pointerY === 'number') { - let ox = x - pointerX, oy = y - pointerY; - window.starVelocity.tx = window.starVelocity.tx + ( ox / 8*scale ) * ( touchInput ? 1 : -1 ); - window.starVelocity.ty = window.starVelocity.ty + ( oy / 8*scale ) * ( touchInput ? 1 : -1 ); + let ox = x - pointerX, oy = y - pointerY + window.starVelocity.tx = window.starVelocity.tx + ( ox / 8*scale ) * ( touchInput ? 1 : -1 ) + window.starVelocity.ty = window.starVelocity.ty + ( oy / 8*scale ) * ( touchInput ? 1 : -1 ) } - pointerX = x; pointerY = y; + pointerX = x ; pointerY = y }