-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathscript.js
More file actions
310 lines (265 loc) · 11.5 KB
/
script.js
File metadata and controls
310 lines (265 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// Smooth scroll behavior for navigation links
document.addEventListener('DOMContentLoaded', function() {
// Handle navigation link clicks
const navLinks = document.querySelectorAll('a[href^="#"]');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
const href = this.getAttribute('href');
// Skip if href is just '#'
if (href === '#') {
e.preventDefault();
return;
}
const targetId = href.substring(1);
const targetElement = document.getElementById(targetId);
if (targetElement) {
e.preventDefault();
// Calculate offset for sticky navbar
const navbar = document.querySelector('.navbar');
const navbarHeight = navbar ? navbar.offsetHeight : 0;
const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - navbarHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
// Add active state to navigation links based on scroll position
const sections = document.querySelectorAll('section[id]');
const navMenuLinks = document.querySelectorAll('.nav-menu a[href^="#"]');
function updateActiveNavLink() {
const scrollPosition = window.scrollY + 100;
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.offsetHeight;
const sectionId = section.getAttribute('id');
if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) {
navMenuLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${sectionId}`) {
link.classList.add('active');
}
});
}
});
}
// Throttle scroll event for performance
let scrollTimeout;
window.addEventListener('scroll', function() {
if (scrollTimeout) {
window.cancelAnimationFrame(scrollTimeout);
}
scrollTimeout = window.requestAnimationFrame(function() {
updateActiveNavLink();
});
});
// Initial check
updateActiveNavLink();
// Add animation on scroll for cards and sections
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '0';
entry.target.style.transform = 'translateY(20px)';
// Trigger animation
setTimeout(() => {
entry.target.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, 100);
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe elements for animation
const animatedElements = document.querySelectorAll('.goal-card, .schedule-day, .topic-category, .resource-item, .logistics-card');
animatedElements.forEach(el => {
observer.observe(el);
});
// Mobile menu toggle (if needed in future)
const createMobileMenu = () => {
const navbar = document.querySelector('.navbar');
const navMenu = document.querySelector('.nav-menu');
if (window.innerWidth <= 768) {
if (!document.querySelector('.menu-toggle')) {
const menuToggle = document.createElement('button');
menuToggle.className = 'menu-toggle';
menuToggle.innerHTML = '☰';
menuToggle.setAttribute('aria-label', 'Toggle menu');
menuToggle.addEventListener('click', () => {
navMenu.classList.toggle('active');
menuToggle.classList.toggle('active');
});
navbar.querySelector('.container').insertBefore(menuToggle, navMenu);
}
}
};
// Handle window resize
let resizeTimeout;
window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
// Update any layout-dependent features
updateActiveNavLink();
}, 250);
});
// External links - open in new tab with security
const externalLinks = document.querySelectorAll('a[href^="http"]');
externalLinks.forEach(link => {
if (!link.hasAttribute('target')) {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener noreferrer');
}
});
// Add loading state for registration button
const registerButtons = document.querySelectorAll('.cta-button, .btn-register, .btn-secondary');
registerButtons.forEach(button => {
button.addEventListener('click', function(e) {
// Only add loading state for external registration links
if (this.href && this.href.includes('forms.gle')) {
this.style.opacity = '0.7';
this.style.pointerEvents = 'none';
setTimeout(() => {
this.style.opacity = '1';
this.style.pointerEvents = 'auto';
}, 2000);
}
});
});
// Keyboard navigation support
document.addEventListener('keydown', function(e) {
// Escape key closes mobile menu if open
if (e.key === 'Escape') {
const navMenu = document.querySelector('.nav-menu');
const menuToggle = document.querySelector('.menu-toggle');
if (navMenu && navMenu.classList.contains('active')) {
navMenu.classList.remove('active');
if (menuToggle) menuToggle.classList.remove('active');
}
}
});
// Add focus visible styles for accessibility
document.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
document.body.classList.add('keyboard-navigation');
}
});
document.addEventListener('mousedown', function() {
document.body.classList.remove('keyboard-navigation');
});
// Performance: Lazy load images if needed
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// Console message for developers
console.log('%cSCIPE Workshop on LLMs', 'color: #1a365d; font-size: 20px; font-weight: bold;');
console.log('%cWebsite built with modern web standards', 'color: #4a5568; font-size: 12px;');
console.log('Interested in the source? Check out our GitHub repository!');
// Filter functionality for resources by difficulty level
const filterButtons = document.querySelectorAll('.filter-btn');
const resourceItems = document.querySelectorAll('.resource-item[data-level]');
if (filterButtons.length > 0) {
filterButtons.forEach(button => {
button.addEventListener('click', function() {
const filterValue = this.getAttribute('data-filter');
// Update active button state
filterButtons.forEach(btn => btn.classList.remove('active'));
this.classList.add('active');
// Filter resources
resourceItems.forEach(item => {
const itemLevel = item.getAttribute('data-level');
if (filterValue === 'all') {
item.classList.remove('hidden');
// Animate items back in
item.style.animation = 'fadeIn 0.5s ease';
} else if (itemLevel === filterValue) {
item.classList.remove('hidden');
item.style.animation = 'fadeIn 0.5s ease';
} else {
item.classList.add('hidden');
}
});
// Smooth scroll to first visible item after filtering
setTimeout(() => {
const firstVisible = document.querySelector('.resource-item:not(.hidden)');
if (firstVisible && filterValue !== 'all') {
const navbar = document.querySelector('.navbar');
const navbarHeight = navbar ? navbar.offsetHeight : 0;
const filterSection = document.querySelector('.filter-section');
const filterHeight = filterSection ? filterSection.offsetHeight : 0;
const targetPosition = firstVisible.getBoundingClientRect().top + window.pageYOffset - navbarHeight - filterHeight - 40;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
}, 100);
});
});
// Add keyboard support for filter buttons
filterButtons.forEach((button, index) => {
button.addEventListener('keydown', function(e) {
if (e.key === 'ArrowRight' && filterButtons[index + 1]) {
e.preventDefault();
filterButtons[index + 1].focus();
} else if (e.key === 'ArrowLeft' && filterButtons[index - 1]) {
e.preventDefault();
filterButtons[index - 1].focus();
} else if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
});
}
});
// Add CSS for keyboard navigation focus styles
const style = document.createElement('style');
style.textContent = `
.keyboard-navigation *:focus {
outline: 2px solid #4a90e2 !important;
outline-offset: 2px;
}
.menu-toggle {
display: none;
background: none;
border: none;
font-size: 1.5rem;
color: var(--primary-color);
cursor: pointer;
padding: 0.5rem;
}
@media (max-width: 768px) {
.menu-toggle {
display: block;
}
.nav-menu {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.nav-menu.active {
max-height: 500px;
}
}
`;
document.head.appendChild(style);