1<!DOCTYPE html> 2<html lang="en" class="scroll-smooth"> 3 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>NAISS Lab | Networked AI Systems and Security Lab</title> 8 <link rel="stylesheet" href="assets/site.css?v=786269ed3f9d"> 9 <link rel="preconnect" href="https://fonts.googleapis.com"> 10 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 11 <link 12 href="https://fonts.googleapis.com/css2?family=Noto+Serif:wght@500;600;700&family=Noto+Sans:wght@400;500;600;700&display=swap" 13 rel="stylesheet"> 14</head> 15 16<body class="antialiased"> 17 18 <header id="header" class="site-header fixed top-0 left-0 right-0 z-50 shadow-sm"> 19 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 20 <div class="flex items-center justify-between h-16 sm:h-[4.35rem]"> 21 <div class="flex-shrink-0"> 22 <a href="#home" class="lab-brand text-3xl leading-none font-semibold">NAISS Lab</a> 23 </div> 24 <div class="hidden md:block"> 25 <nav class="ml-8 flex items-baseline gap-1 lg:gap-3"> 26 <a href="#about" class="nav-link px-3 py-2 text-xs sm:text-sm uppercase">About</a> 27 <a href="#research" class="nav-link px-3 py-2 text-xs sm:text-sm uppercase">Research</a> 28 <a href="#people" class="nav-link px-3 py-2 text-xs sm:text-sm uppercase">People</a> 29 <a href="#publications" class="nav-link px-3 py-2 text-xs sm:text-sm uppercase">Publications</a> 30 <a href="#news" class="nav-link px-3 py-2 text-xs sm:text-sm uppercase">News</a> 31 </nav> 32 </div> 33 <div class="-mr-1 flex md:hidden"> 34 <button id="mobile-menu-button" type="button" 35 class="inline-flex items-center justify-center p-2 rounded-md border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-teal-900/20" 36 aria-controls="mobile-menu" aria-expanded="false"> 37 <span class="sr-only">Open main menu</span> 38 <svg class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" 39 stroke="currentColor" aria-hidden="true"> 40 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 41 d="M4 6h16M4 12h16M4 18h16" /> 42 </svg> 43 <svg class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" 44 stroke="currentColor" aria-hidden="true"> 45 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" 46 d="M6 18L18 6M6 6l12 12" /> 47 </svg> 48 </button> 49 </div> 50 </div> 51 </div> 52 <div class="md:hidden hidden border-t border-slate-200 bg-white/95 shadow-lg" id="mobile-menu"> 53 <div class="px-3 py-3 space-y-1"> 54 <a href="#about" class="block nav-link px-3 py-2 rounded-md text-sm uppercase">About</a> 55 <a href="#research" class="block nav-link px-3 py-2 rounded-md text-sm uppercase">Research</a> 56 <a href="#people" class="block nav-link px-3 py-2 rounded-md text-sm uppercase">People</a> 57 <a href="#publications" class="block nav-link px-3 py-2 rounded-md text-sm uppercase">Publications</a> 58 <a href="#news" class="block nav-link px-3 py-2 rounded-md text-sm uppercase">News</a> 59 </div> 60 </div> 61 </header> 62 63 <main> 64 <section id="home" class="hero-bg pt-16"> 65 <div class="hero-content max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 sm:py-28 md:py-36 text-white text-center"> 66 <div class="hero-title-wrap mx-auto"> 67 <p class="hero-ghost">ML Systems, Networking, and Security</p> 68 <h1 class="hero-main-title text-4xl sm:text-5xl lg:text-7xl"> 69 Networked AI Systems and Security Lab 70 <!-- <span class="block">Security Lab</span> --> 71 </h1> 72 </div>
73 <p class="hero-lab-name mt-2 sm:mt-3">NAISS Lab</p> 74 <p class="mt-6 max-w-3xl mx-auto text-lg sm:text-xl md:text-2xl text-slate-100/90 leading-relaxed"> 75 Developing the next generation of intelligent systems to ensure our networked world is accessible, 76 reliable, and secure. 77 </p> 78 </div> 79 </section> 80 81 <section id="about" class="section-shell soft py-16 sm:py-20"> 82 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 83 <div class="text-center"> 84 <h2 class="section-title text-4xl font-semibold tracking-tight sm:text-5xl">Our Mission</h2> 85 <p class="mt-6 max-w-4xl mx-auto text-base sm:text-lg leading-relaxed text-slate-600"> 86 The NAISS (pronounced as "nice") Lab in the Department of Data and Systems Engineering at The University of Hong Kong 87 operates at the critical intersection of machine learning systems, computer networking, and 88 security. Our research follows a virtuous cycle: we build foundational ML systems and 89 techniques, apply them to secure critical network infrastructure, and extend them to improve 90 interactions and well-beings with the cyber-physical world. Contact us at 91 <a href="mailto:[email protected]" class="publication-link">[email protected]</a>. 92 </p> 93 </div> 94 </div> 95 </section> 96 97 <section id="research" class="section-shell py-16 sm:py-20"> 98 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 99 <div class="text-center"> 100 <h2 class="section-title text-4xl font-semibold tracking-tight sm:text-5xl">Research Directions</h2> 101 <p class="mt-6 max-w-4xl mx-auto text-base sm:text-lg text-slate-600"> 102 Explore the topics and publications behind our three research directions. 103 </p> 104 </div> 105 <div id="research-container" class="mt-12 grid gap-6 md:grid-cols-3"> 106 </div> 107 </div> 108 </section> 109 110 <section id="people" class="section-shell soft py-16 sm:py-20"> 111 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 112 <div class="text-center"> 113 <h2 class="section-title text-4xl font-semibold tracking-tight sm:text-5xl">Our Team</h2> 114 <p class="mt-6 max-w-4xl mx-auto text-base sm:text-lg text-slate-600"> 115 Meet the researchers and students driving innovation at the NAISS Lab. 116 </p> 117 </div> 118 <div class="mt-8 flex justify-center flex-wrap gap-2" id="people-filter-controls"> 119 </div> 120 <div id="people-container" class="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> 121 </div> 122 </div> 123 </section> 124 125 <section id="publications" class="section-shell py-16 sm:py-20"> 126 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 127 <div class="text-center"> 128 <h2 class="section-title text-4xl font-semibold tracking-tight sm:text-5xl">Publications</h2> 129 <p class="mt-6 max-w-4xl mx-auto text-base sm:text-lg text-slate-600"> 130 A selection of our scholarly output. 131 </p> 132 </div> 133 <div class="mt-8 max-w-4xl mx-auto flex flex-col sm:flex-row gap-3"> 134 <select id="pub-year-filter" aria-label="Publication year or preprints" class="select-control w-full sm:w-1/2 p-2.5 rounded-md"> 135 <option value="all">All Years</option> 136 </select> 137 <select id="pub-area-filter" aria-label="Research area" class="select-control w-full sm:w-1/2 p-2.5 rounded-md"> 138 <option value="all">All Research Areas</option> 139 </select> 140 </div> 141 <div id="publications-container" class="mt-6 grid gap-2.5 max-w-6xl mx-auto"> 142 </div> 143 </div> 144 </section> 145 146 <section id="news" class="section-shell soft py-16 sm:py-20"> 147 <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> 148 <div class="text-center"> 149 <h2 class="section-title text-4xl font-semibold tracking-tight sm:text-5xl">Latest News</h2> 150 </div> 151 <div id="news-container" class="mt-6 max-w-6xl mx-auto grid gap-2.5"> 152 </div> 153 </div> 154 </section> 155 156 </main> 157 158 <footer class="bg-slate-900 text-slate-100"> 159 <div class="max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8 text-center"> 160 <p class="text-sm sm:text-base">© 2026 Networked AI Systems and Security (NAISS) Lab</p> 161 <p class="text-sm text-slate-400 mt-2">The University of Hong Kong</p> 162 </div> 163 </footer> 164 165
165<script src="data/research.js?v=d6dd5af3a1e9"></script>
165 166
166<script src="data/people.js?v=f20935bd08d1"></script>
166 167
167<script src="data/publication.js?v=875cb7ddb51f"></script>
167 168
168<script src="data/news.js?v=a9906eeffbc7"></script>
168 169 170
170<script> 171 document.addEventListener('DOMContentLoaded', function () { 172 // Data is now loaded from the external files above. 173 // The large data arrays have been removed from this file. 174 const labMemberNames = new Set([...peopleData, ...alumniData].flatMap(person => [ 175 `${person.firstName} ${person.lastName}`, 176 ...(person.aliases || []) 177 ])); 178 179 // Render Functions 180 function escapeHtml(value) { 181 return String(value).replace(/[&<>"']/g, char => ({ 182 '&': '&', 183 '<': '<', 184 '>': '>', 185 '"': '"', 186 "'": ''' 187 })[char]); 188 } 189 190 function normalizeAuthorName(name) { 191 return name.replace(/\*/g, '').replace(/\s+/g, ' ').trim(); 192 } 193 194 function renderPublicationAuthors(authors) { 195 return authors.split(/,\s*/).map(author => { 196 const authorName = author.trim(); 197 const escapedName = escapeHtml(authorName); 198 199 if (labMemberNames.has(normalizeAuthorName(authorName))) { 200 return `<strong class="publication-author-lab-member" title="NAISS Lab member">${escapedName}</strong>`; 201 } 202 203 return escapedName; 204 }).join(', '); 205 } 206 207 function renderResearch() { 208 const container = document.getElementById('research-container'); 209 if (!container) return; 210 container.innerHTML = researchData.map(item => ` 211 <article class="research-card flex flex-col bg-white/95 p-6 rounded-xl cursor-pointer" data-id="${item.id}"> 212 <div class="text-3xl">${item.icon}</div> 213 <h3 class="mt-3 text-2xl font-semibold">${item.title}</h3> 214 <p class="mt-2 text-slate-600 leading-relaxed">${item.description}</p> 215 <button type="button" data-research-toggle="${item.id}" 216 class="mt-4 self-start inline-flex items-center gap-2 text-sm font-semibold text-accent rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-teal-800" 217 aria-expanded="false" aria-controls="subtopics-${item.id}"> 218 Topics & selected work 219 <svg class="h-4 w-4 transition-transform" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"> 220 <path d="m5 7.5 5 5 5-5" /> 221 </svg> 222 </button> 223 <div id="subtopics-${item.id}" class="hidden mt-4 pl-4 border-l-2 border-teal-700/30"> 224 <ul class="list-disc list-outside ml-4 text-slate-600 space-y-5 text-sm sm:text-base"> 225 ${item.subtopics.map(sub => ` 226 <li class="research-subtopic"> 227 <p class="font-medium leading-relaxed">${escapeHtml(sub.title)}</p> 228 <ul class="list-none mt-2 space-y-1.5 text-sm leading-relaxed"> 229 ${(sub.featuredPublications || []).map(featured => { 230 const publication = publicationsData.find(pub => pub.id === featured.id); 231 if (!publication) return ''; 232 const externalUrl = publication.links.website || publication.links.pdf; 233 const url = externalUrl || `#publication-${publication.id}`; 234 const linkAttributes = externalUrl 235 ? 'target="_blank" rel="noopener noreferrer"' 236 : `data-publication-id="${escapeHtml(publication.id)}"`; 237 return `<li><a href="${escapeHtml(url)}" ${linkAttributes} 238 class="publication-link rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-teal-800" 239 title="${escapeHtml(publication.title)}">${escapeHtml(featured.label)}</a></li>`; 240 }).join('')} 241 </ul> 242 </li> 243 `).join('')} 244 </ul> 245 </div> 246 <a href="#publications" data-research-area="${item.id}" 247 class="publication-link mt-auto pt-5 self-start inl
247ine-flex items-center gap-2 text-sm rounded-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-teal-800" 248 aria-label="View publications in ${item.title}"> 249 View related publications (${publicationsData.filter(pub => pub.area.includes(item.id)).length}) 250 <span aria-hidden="true">→</span> 251 </a> 252 </article> 253 `).join(''); 254 } 255 256 function renderPersonCards(people) { 257 return people.map(person => { 258 const personNameHtml = person.website 259 ? `<a href="${person.website}" target="_blank" rel="noopener noreferrer" class="news-link">${person.firstName} ${person.lastName}</a>` 260 : `${person.firstName} ${person.lastName}`; 261 262 return ` 263 <div class="person-card text-center bg-white/90 border border-slate-200 p-5 rounded-xl shadow-sm" data-role="${person.role}"> 264 <img src="${person.photo}" alt="${person.firstName} ${person.lastName}" width="96" height="96" loading="lazy" decoding="async" class="w-24 h-24 rounded-full mx-auto object-cover"> 265 <h3 class="mt-3 font-semibold text-xl">${personNameHtml}</h3> 266 <p class="text-accent font-semibold text-sm">${person.role}</p> 267 <p class="text-slate-500 text-sm">${person.status}</p> 268 <p class="text-slate-500 text-sm">${person.education}</p> 269 ${person.next ? `<p class="text-slate-500 text-sm">${person.next}</p>` : ''} 270 </div> 271 `; 272 }).join(''); 273 } 274 275 function renderAlumniGroup() { 276 return ` 277 <div class="sm:col-span-2 lg:col-span-3 xl:col-span-4 pt-6 mt-3 border-t border-slate-200"> 278 <h3 class="text-2xl font-semibold text-center">Alumni</h3> 279 </div> 280 ${renderPersonCards(alumniData)} 281 `; 282 } 283 284 function renderPeople(filter = 'all') { 285 const container = document.getElementById('people-container'); 286 if (!container) return; 287 const director = peopleData.find(p => p.role === 'Faculty'); 288 const others = peopleData.filter(p => p.role !== 'Faculty'); 289 const normalizedFilter = filter.toLowerCase().replace(/\./g, ''); 290 291 let filteredPeople = others; 292 if (normalizedFilter === 'alumni') { 293 filteredPeople = []; 294 } else if (normalizedFilter !== 'all') { 295 filteredPeople = others.filter(p => p.role.toLowerCase().replace(/\./g, '') === normalizedFilter); 296 } 297 298 const directorNameHtml = director.website 299 ? `<a href="${director.website}" target="_blank" rel="noopener noreferrer" class="news-link">${director.firstName} ${director.lastName}</a>` 300 : `${director.firstName} ${director.lastName}`; 301 302 const directorHtml = ` 303 <div class="sm:col-span-2 lg:col-span-3 xl:col-span-4 mb-3"> 304 <div class="flex flex-col sm:flex-row items-center bg-white/95 border border-slate-200 rounded-xl shadow-sm p-5 sm:p-6"> 305 <img src="${director.photo}" alt="${director.firstName} ${director.lastName}" width="128" height="128" loading="lazy" decoding="async" class="w-32 h-32 rounded-full object-cover flex-shrink-0"> 306 <div class="sm:ml-6 mt-4 sm:mt-0 text-center sm:text-left"> 307 <h3 class="text-2xl font-semibold">${directorNameHtml}</h3> 308 <p class="text-accent font-semibold">${director.status}</p> 309 <p class="text-slate-600">${director.education}</p> 310 </div> 311 </div> 312 </div> 313 `; 314 315 const alumniHtml = (normalizedFilter === 'all' || normalizedFilter === 'alumni') 316 ? renderAlumniGroup() 317 : ''; 318 319 container.innerHTML = directorHtml + renderPersonCards(filteredPeople) + alumniHtml; 320 } 321 322 function renderPeopleFilters() { 323 const controlsContainer = document.getElementById('people-filter-control
323s'); 324 if (!controlsContainer) return; 325 const roles = ['All', 'Postdoc Scholar', 'Ph.D. Student', 'MPhil Student', 'Intern', 'Alumni']; 326 controlsContainer.innerHTML = roles.map((role, index) => ` 327 <button class="filter-btn px-4 py-1.5 rounded-full text-sm ${index === 0 ? 'active' : ''}" data-filter="${role.toLowerCase().replace(/\./g, '')}">${role}</button> 328 `).join(''); 329 } 330 331 function renderPublications(yearFilter = 'all', areaFilter = 'all') { 332 const container = document.getElementById('publications-container'); 333 if (!container) return; 334 335 let filteredPubs; 336 if (yearFilter === 'preprint') { 337 filteredPubs = publicationsData.filter(pub => 338 pub.type === 'Preprint' && 339 (areaFilter === 'all' || pub.area.includes(areaFilter)) 340 ); 341 } else { 342 filteredPubs = publicationsData.filter(pub => 343 (yearFilter === 'all' || pub.year == yearFilter) && 344 (areaFilter === 'all' || pub.area.includes(areaFilter)) 345 ); 346 } 347 348 if (filteredPubs.length === 0) { 349 container.innerHTML = `<p class="text-center text-slate-500">No publications match the selected filters.</p>`; 350 return; 351 } 352 353 container.innerHTML = filteredPubs.map(pub => { 354 const extraLinks = [ 355 pub.links.website ? `<a href="${pub.links.website}" class="publication-link" target="_blank" rel="noopener noreferrer">Website</a>` : '', 356 pub.links.code ? `<a href="${pub.links.code}" class="publication-link" target="_blank" rel="noopener noreferrer">Code</a>` : '', 357 pub.links.media ? `<a href="${pub.links.media}" class="publication-link" target="_blank" rel="noopener noreferrer">Media</a>` : '', 358 pub.links.slides ? `<a href="${pub.links.slides}" class="publication-link" target="_blank" rel="noopener noreferrer">Slides</a>` : '', 359 pub.links.video ? `<a href="${pub.links.video}" class="publication-link" target="_blank" rel="noopener noreferrer">Video</a>` : '', 360 pub.links.data ? `<a href="${pub.links.data}" class="publication-link" target="_blank" rel="noopener noreferrer">Data</a>` : '', 361 pub.links.model ? `<a href="${pub.links.model}" class="publication-link" target="_blank" rel="noopener noreferrer">Model</a>` : '' 362 ].filter(Boolean); 363 364 const extraLinksHtml = extraLinks.length > 0 365 ? `<div class="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-sm">${extraLinks.join('')}</div>` 366 : ''; 367 368 const pdfActionHtml = pub.links.pdf 369 ? `<a href="${pub.links.pdf}" class="publication-link pdf-action-link" target="_blank" rel="noopener noreferrer">PDF</a>` 370 : ''; 371 372 return ` 373 <article ${pub.id ? `id="publication-${escapeHtml(pub.id)}" tabindex="-1"` : ''} class="publication-item ${pub.type === 'Preprint' ? 'preprint' : ''}"> 374 <div class="publication-meta"> 375 <span class="pub-badge">${pub.year}</span> 376 <span class="pub-badge type">${pub.type}</span> 377 </div> 378 <div class="publication-body"> 379 ${pub.award ? `<p class="publication-award">${pub.award}</p>` : ''} 380 <h3 class="font-semibold text-xl leading-tight">${pub.title}</h3> 381 <p class="text-slate-600 mt-1 text-sm">${renderPublicationAuthors(pub.authors)}</p> 382 <p class="text-slate-500 italic mt-1 text-sm">${pub.venue}</p> 383 ${extraLinksHtml} 384 </div> 385 <div class="publication-actions"> 386 ${pdfActionHtml} 387 </div> 388 </article> 389 `; 390 }).join(''); 391 } 392 393 function populatePubFilters() { 394 const yearSelect = document.getElementById('pub-year-filter'); 395 const areaSelect = document.getElementById('pub-area-filter'); 396 if (!yearSelect || !areaSelect) return; 397 398 // Add Preprints option 399 const preprintOption = document.createElement('option'); 400 preprintOption.value = 'preprint'; 401 preprintOption.textContent = 'Preprints'; 402 yearSelect.appendChild(preprintOption); 403 404 const years = [...new Set(publicationsData.filter(p => p.type !== 'Preprint').map(p => p.year))].sort((a, b) => b - a);
405 years.forEach(year => { 406 const option = document.createElement('option'); 407 option.value = year; 408 option.textContent = year; 409 yearSelect.appendChild(option); 410 }); 411 412 researchData.forEach(area => { 413 const option = document.createElement('option'); 414 option.value = area.id; 415 option.textContent = area.title; 416 areaSelect.appendChild(option); 417 }); 418 } 419 420 function renderNews() { 421 const container = document.getElementById('news-container'); 422 if (!container) return; 423 424 container.innerHTML = newsData.map(item => { 425 const titleHtml = item.url 426 ? `<a href="${item.url}" class="news-link" target="_blank" rel="noopener noreferrer">${item.title}</a>` 427 : item.title; 428 429 return ` 430 <article class="news-item"> 431 <p class="news-date">${item.date}</p> 432 <div> 433 <h3 class="text-xl font-semibold leading-tight">${titleHtml}</h3> 434 <p class="mt-1 text-slate-600 text-sm">${item.content}</p> 435 </div> 436 </article> 437 `; 438 }).join(''); 439 } 440 441 442 // Event Listeners 443 document.getElementById('mobile-menu-button').addEventListener('click', function () { 444 const menu = document.getElementById('mobile-menu'); 445 const button = this; 446 const icons = button.querySelectorAll('svg'); 447 menu.classList.toggle('hidden'); 448 icons[0].classList.toggle('hidden'); 449 icons[1].classList.toggle('hidden'); 450 }); 451 452 const sections = Array.from(document.querySelectorAll('section')); 453 const navLinks = Array.from(document.querySelectorAll('header a.nav-link')); 454 455 function setActiveNavLink(sectionId) { 456 navLinks.forEach(link => { 457 const target = link.getAttribute('href'); 458 link.classList.toggle('active', Boolean(sectionId) && target === `#${sectionId}`); 459 }); 460 } 461 462 function getCurrentSectionId() { 463 if (sections.length === 0) return ''; 464 const probeY = Math.max(90, window.innerHeight * 0.24); 465 466 for (const section of sections) { 467 const rect = section.getBoundingClientRect(); 468 if (rect.top <= probeY && rect.bottom > probeY) { 469 return section.id; 470 } 471 } 472 473 if (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 4) { 474 return sections[sections.length - 1].id; 475 } 476 477 return sections[0].id; 478 } 479 480 function syncActiveNavToViewport() {
481 setActiveNavLink(getCurrentSectionId()); 482 } 483 484 document.querySelectorAll('#mobile-menu a, header nav a').forEach(link => { 485 link.addEventListener('click', () => { 486 const sectionId = link.getAttribute('href').replace('#', ''); 487 setActiveNavLink(sectionId); 488 489 if (link.closest('#mobile-menu')) { 490 document.getElementById('mobile-menu').classList.add('hidden'); 491 const icons = document.getElementById('mobile-menu-button').querySelectorAll('svg'); 492 icons[0].classList.remove('hidden'); 493 icons[1].classList.add('hidden'); 494 } 495 496 // Smooth scroll updates hash/position asynchronously; reconcile shortly after. 497 window.setTimeout(syncActiveNavToViewport, 220); 498 }); 499 }); 500 501 document.getElementById('research-container').addEventListener('click', function (e) { 502 const selectedWorkLink = e.target.closest('[data-publication-id]'); 503 if (selectedWorkLink) { 504 document.getElementById('pub-year-filter').value = 'all'; 505 document.getElementById('pub-area-filter').value = 'all'; 506 renderPublications(); 507 return; 508 } 509 const publicationLink = e.target.closest('[data-research-area]'); 510 if (publicationLink) { 511 const area = publicationLink.dataset.researchArea; 512 document.getElementById('pub-year-filter').value = 'all'; 513 document.getElementById('pub-area-filter').value = area; 514 renderPublications('all', area); 515 return; 516 } 517 if (e.target.closest('a')) return; 518 519 const card = e.target.closest('.research-card'); 520 if (card) { 521 const subtopicsId = `subtopics-${card.dataset.id}`; 522 const subtopicsEl = document.getElementById(subtopicsId); 523 const isExpanded = !subtopicsEl.classList.toggle('hidden'); 524 const toggle = card.querySelector('[data-research-toggle]'); 525 toggle.setAttribute('aria-expanded', String(isExpanded)); 526 toggle.querySelector('svg').classList.toggle('rotate-180', isExpanded); 527 const container = document.getElementById('research-container'); 528 container.classList.toggle('items-start', Boolean(container.querySelector('[data-research-toggle][aria-expanded="true"]'))); 529 } 530 }); 531 532 document.getElementById('people-filter-controls').addEventListener('click', function (e) { 533 if (e.target.tagName === 'BUTTON') { 534 document.querySelectorAll('#people-filter-controls .filter-btn').forEach(btn => btn.classList.remove('active')); 535 e.target.classList.add('active'); 536 renderPeople(e.target.dataset.filter); 537 } 538 }); 539 540 document.getElementById('pub-year-filter').addEventListener('change', () => { 541 renderPublications(document.getElementById('pub-year-filter').value, document.getElementById('pub-area-filter').value); 542 }); 543 544 document.getElementById('pub-area-filter').addEventListener('change', () => { 545 renderPublications(document.getElementById('pub-year-filter').value, document.getElementById('pub-area-filter').value); 546 }); 547 548 window.addEventListener('scroll', syncActiveNavToViewport, { passive: true }); 549 window.addEventListener('resize', syncActiveNavToViewport); 550 window.addEventListener('hashchange', () => { 551 const hashSectionId = window.location.hash.replace('#', ''); 552 if (hashSectionId) {
553 setActiveNavLink(hashSectionId); 554 window.setTimeout(syncActiveNavToViewport, 120); 555 } 556 }); 557 558 // Initial Render 559 renderResearch(); 560 renderPeopleFilters(); 561 renderPeople(); 562 populatePubFilters(); 563 renderPublications(); 564 renderNews(); 565 syncActiveNavToViewport(); 566 }); 567 </script>
567 568</body> 569 570</html>
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.