1// theme toggle 2const btnTheme = document.querySelector("#theme-toggle"); 3const body = document.body; 4 5let theme = localStorage.getItem("theme"); 6if (theme === "darkmode") { 7 //console.log("darkmode enabled"); 8 body.classList.add(`${theme}`); 9} 10const enableDarkMode = () => { 11 body.classList.add("darkmode"); 12 localStorage.setItem("theme", "darkmode"); 13 theme = "darkmode"; 14}; 15 16const disableDarkMode = () => { 17 body.classList.remove("darkmode"); 18 localStorage.setItem("theme", "lightmode"); 19 body.classList.add("lightmode"); 20 theme = ""; 21}; 22 23console.log(body); 24 25btnTheme.addEventListener("click", () => { 26 //console.log(darkMode); 27 28 body.classList.add("swap-theme"); 29 setTimeout(() => { 30 if (theme !== "darkmode") enableDarkMode(); 31 else disableDarkMode(); 32 body.classList.remove("swap-theme"); 33 }, 250); 34}); 35 36/* 37// responsive nav 38const hamburger = document.querySelector("#hamburger"); 39 40hamburger.addEventListener("click", () => { 41 const isOpened = hamburger.getAttribute("aria-expanded") === "true"; 42 if (isOpened ? toggleMenu("false") : toggleMenu("true") ); 43 44}); 45 46function toggleMenu(state){ 47 hamburger.setAttribute("aria-expanded", `${state}`); 48} 49*/ 50 51const navToggleBtn = document.getElementById("btn-nav-toggle"); 52const navMenu = document.getElementById("nav-menu"); 53 54// toggle nav button 55navToggleBtn.addEventListener("click", (event) => { 56 event.stopPropagation(); // Prevent immediate closing when clicking the button 57 const isExpanded = navToggleBtn.getAttribute("aria-expanded") === "true"; 58 toggleNav(!isExpanded); 59}); 60 61// toggle nav panel 62function toggleNav(expand) { 63 navToggleBtn.setAttribute("aria-expanded", String(expand)); 64 navMenu.setAttribute("aria-hidden", String(!expand)); 65 navMenu.toggleAttribute("inert", !expand); 66 67 // add/remove event handler on dom to close nav panel 68 expand && document.addEventListener("click", handleOutsideClick); 69 !expand && document.removeEventListener("click", handleOutsideClick); 70} 71function handleOutsideClick(event) { 72 if (!navMenu.contains(event.target) && !navToggleBtn.contains(event.target)) { 73 toggleNav(false); 74 } 75} 76 77// select all internal links inside the nav menu 78navMenu.querySelectorAll("a[href^='#']").forEach(link => { 79 link.addEventListener("click", () => { 80 toggleNav(false); // close the nav panel 81 }); 82}); 83 84 85// sliders 86const sliders = document.querySelectorAll(".slide-in"); 87const appearOptions = { 88 threshold: 0, 89 rootMargin: "0px 0px -50px 0px", 90}; 91 92const LETTERS = "ABCEDEFGHIJKLMNOOPQRSTUVWXYZ"; 93const hyper = document.querySelector(".hyper"); 94const arrValues = hyper.getAttribute("data-values").split(","); 95const shuffleMode = hyper.getAttribute("shuffle-mode"); 96const arrLength = arrValues.length;
97const delay = 3000; // delay between each word shuffle 98let counter = 0; 99// true = use random charaters for each new word, 100// false = use last word as base for next word 101function shuffleAll() { 102 shuffleWord(arrValues[counter]); 103 counter++; 104 if (counter == arrLength) { 105 counter = 0; 106 } 107 setTimeout(shuffleAll, delay); 108} 109setTimeout(shuffleAll, 0); 110 111function shuffleWord(word) { 112 let iterations = 0; 113 txtLength = word.length; 114 currentWord = hyper.innerText; 115 const interval = setInterval(() => { 116 hyper.innerText = word 117 .split("") 118 .map((letter, idx) => { 119 if (idx == 0) { 120 // return first letter straight away 121 return letter; 122 } 123 if (idx < iterations) { 124 // return letters to this position 125 return letter; 126 } 127 // return random letter (or letter at this position from previous word) 128 if (shuffleMode == "lastword") return currentWord.charAt(idx); 129 else return LETTERS[Math.floor(Math.random() * 26)]; 130 }) 131 .join(""); 132 133 if (iterations >= txtLength) { 134 // stop when we have completed the word 135 clearInterval(interval); 136 hyper.innerText += "."; 137 } 138 // number of steps to take on each iteration 139 iterations += 1; 140 }, 60); 141} 142 143/* 144const LETTERS = "ABCEDEFGHIJKLMNOOPQRSTUVWXYZ"; 145const hyper = document.querySelector(".hyper"); 146hyper.onmouseover = event => { 147 let iterations = 0; 148 149 const interval = setInterval(() =>{ 150 event.target.innerText = event.target.innerText.split("") 151 .map((letter, index) => { 152 if(index < iterations){ 153 return event.target.dataset.value[index]; 154 } 155 return LETTERS[Math.floor(Math.random() * 26)] 156 }).join(""); 157 158 if(iterations>=event.target.dataset.value.length) clearInterval(interval); 159 iterations += 1 / 3; 160 },30); 161 162} 163*/ 164const faders = document.querySelectorAll(".show-me"); 165const appearOnScroll = new IntersectionObserver(function ( 166 entries, 167 appearOnScroll 168) { 169 entries.forEach((entry) => { 170 if (!entry.isIntersecting) { 171 //entry.target.classList.remove("appear"); 172 return; 173 } 174 entry.target.classList.add("appear"); 175 //appearOnScroll.unobserve(entry.target); 176 }); 177}, 178appearOptions); 179 180faders.forEach((fader) => { 181 appearOnScroll.observe(fader); 182}); 183// add delay to each item 184/* 185sliders.forEach((slider,i) => { 186 // add transiktion delay 187 slider.style.transitionDelay = `${i * 100}ms`; 188 // slide in 189 appearOnScroll.observe(slider); 190}) 191*/ 192 193// tech hover 194/* 195 The tech logos are from shield.io which doesn't appear to allow a hover state 196 I have manually added the color for each logo as an attribute called "hover-clr" 197 This code will create a "hover" src for each item and toggle it with the original src on mouseover/mouseout 198*/ 199const tech = document.querySelectorAll(".tech-stack li img"); 200tech.forEach((item) => { 201 // get hover color 202 const hoverClr = item.getAttribute("hover-clr"); 203 const hoverBg = item.getAttribute("hover-bg"); 204 205 // swap complete src.... probably a terrible idea - I should at least get this just once on load 206 const srcOrig = item.getAttribute("src"); 207 let srcHover = srcOrig.replace("&logoColor=000000", `&logoColor=${hoverClr}`); 208 srcHover = srcHover.replace("-DDDDDD", `-${hoverBg}`); 209 210 item.addEventListener("mouseover", () => { 211 item.setAttribute("src", srcHover); 212 }); 213 item.addEventListener("mouseout", () => { 214 item.setAttribute("src", srcOrig); 215 }); 216}); 217 218/* 219const logo = document.querySelector("[logo]"); 220const height = document.documentElement.scrollHeight - document.documentElement.clientHeight; 221let moveVal = 0; 222 223window.addEventListener('scroll', () => { 224 let scrollTop =document.body.scrollTop || document.documentElement.scrollTop; 225 moveVal = (scrollTop / height) * 360 ; 226 logo.style.transform = `rotate(${moveVal}deg)`; 227}); 228*/ 229 230/* 231const rotateLogo = () => { 232 233 234 moveVal = (height / 360) * window.scrollY; 235 // console.log(moveVal); 236 logo.style.transform = `rotate(${moveVal}deg)`; // rotate with scroll 237 requestAnimationFrame(rotateLogo); 238}; 239 240requestAnimationFrame(rotateLogo); 241*/
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.