PageSourceSearch

https://www.cbolson.com/assets/js/main.js?1790925191

js cbolson.com collected 2026-10-02 07:13:15 UTC 6,779 bytes, 241 lines download raw bytes

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.