|
97 | 97 | @keyframes fadeIn{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}} |
98 | 98 | .muted{color:var(--muted)} |
99 | 99 |
|
| 100 | + /* Organizers */ |
| 101 | + .organizers{display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:2rem;margin-top:1.5rem} |
| 102 | + .organizer{display:flex;flex-direction:column;align-items:center;text-align:center;padding:1.5rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--panel);transition:transform .18s ease, box-shadow .18s ease;position:relative} |
| 103 | + .organizer:hover{transform:translateY(-3px);box-shadow:0 12px 26px rgba(0,0,0,.15)} |
| 104 | + .organizer strong{display:block;font-size:1.2rem;margin-bottom:.5rem;color:var(--fg)} |
| 105 | + .organizer .title{color:var(--muted);font-weight:500;margin-bottom:.3rem;font-size:0.95rem;font-style:italic} |
| 106 | + .organizer .aff{color:var(--accent-2);font-weight:600;margin-bottom:.3rem} |
| 107 | + .organizer-bio{margin-top:1rem;padding:1rem;background:rgba(250,178,43,.05);border:1px solid rgba(250,178,43,.2);border-radius:8px;font-size:.9rem;line-height:1.5;color:var(--fg);display:none;text-align:left} |
| 108 | + .organizer-bio.show{display:block;animation:fadeIn .3s ease} |
| 109 | + |
100 | 110 | /* Table */ |
101 | 111 | table{border-collapse:separate;border-spacing:0;width:100%;background:var(--panel);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden} |
102 | 112 | th,td{padding:.85rem;border-bottom:1px solid var(--border);text-align:left} |
@@ -262,19 +272,8 @@ <h2>Confirmed Speakers</h2> |
262 | 272 | <section id="organizers"> |
263 | 273 | <div class="container"> |
264 | 274 | <h2>Organizers</h2> |
265 | | - <div class="organizers" style="display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:2rem;margin-top:1.5rem;"> |
266 | | - <a href="https://www.linkedin.com/in/ge-lei-04706b28b/?originalSubdomain=uk" target="_blank" rel="noopener" class="organizer" style="display:flex;flex-direction:column;align-items:center;text-align:center;padding:1.5rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--panel);transition:transform .18s ease, box-shadow .18s ease;cursor:pointer;text-decoration:none;color:inherit;" onmouseover="this.style.transform='translateY(-3px)'; this.style.boxShadow='0 12px 26px rgba(0,0,0,.15)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'"> |
267 | | - <img src="images/lei-ge.JPG" alt="Lei Ge" style="width:120px;height:120px;border-radius:50%;object-fit:cover;border:4px solid var(--accent);margin-bottom:1rem;box-shadow:0 4px 16px rgba(250,178,43,.3);" onerror="this.style.display='none'"> |
268 | | - <strong style="display:block;font-size:1.2rem;margin-bottom:.5rem;color:var(--fg);">Lei Ge</strong> |
269 | | - <div style="color:var(--muted);font-weight:500;margin-bottom:.3rem;font-size:0.95rem;">PhD Student</div> |
270 | | - <div style="color:var(--accent-2);font-weight:600;margin-bottom:.3rem;">Imperial College London</div> |
271 | | - </a> |
272 | | - <a href="https://profiles.imperial.ac.uk/samuel.cooper" target="_blank" rel="noopener" class="organizer" style="display:flex;flex-direction:column;align-items:center;text-align:center;padding:1.5rem;border:1px solid var(--border);border-radius:var(--radius);background:var(--panel);transition:transform .18s ease, box-shadow .18s ease;cursor:pointer;text-decoration:none;color:inherit;" onmouseover="this.style.transform='translateY(-3px)'; this.style.boxShadow='0 12px 26px rgba(0,0,0,.15)'" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='none'"> |
273 | | - <img src="images/samuel-cooper.jpg" alt="Dr. Samuel J. Cooper" style="width:120px;height:120px;border-radius:50%;object-fit:cover;border:4px solid var(--accent);margin-bottom:1rem;box-shadow:0 4px 16px rgba(250,178,43,.3);" onerror="this.style.display='none'"> |
274 | | - <strong style="display:block;font-size:1.2rem;margin-bottom:.5rem;color:var(--fg);">Dr. Samuel J. Cooper</strong> |
275 | | - <div style="color:var(--muted);font-weight:500;margin-bottom:.3rem;font-size:0.95rem;">Associate Professor in AI for Materials Design</div> |
276 | | - <div style="color:var(--accent-2);font-weight:600;margin-bottom:.3rem;">Imperial College London</div> |
277 | | - </a> |
| 275 | + <div class="organizers" id="organizers-container"> |
| 276 | + <!-- Organizers will be dynamically loaded from config --> |
278 | 277 | </div> |
279 | 278 | </div> |
280 | 279 | </section> |
@@ -448,6 +447,7 @@ <h2>Get in touch</h2> |
448 | 447 | </footer> |
449 | 448 |
|
450 | 449 | <script src="speakers-config.js"></script> |
| 450 | + <script src="organizers-config.js"></script> |
451 | 451 | <script> |
452 | 452 | // Set current year |
453 | 453 | document.getElementById('year').textContent = new Date().getFullYear(); |
@@ -523,8 +523,84 @@ <h2>Get in touch</h2> |
523 | 523 | } |
524 | 524 | } |
525 | 525 |
|
526 | | - // Load speakers when page loads |
527 | | - document.addEventListener('DOMContentLoaded', loadSpeakers); |
| 526 | + // Convert URLs in text to clickable links |
| 527 | + function convertLinksInText(text) { |
| 528 | + // Regular expression to match URLs (http, https, www) |
| 529 | + const urlRegex = /(https?:\/\/[^\s]+|www\.[^\s]+)/g; |
| 530 | + |
| 531 | + return text.replace(urlRegex, function(url) { |
| 532 | + let href = url; |
| 533 | + // Add https:// if it's a www link |
| 534 | + if (url.startsWith('www.')) { |
| 535 | + href = 'https://' + url; |
| 536 | + } |
| 537 | + return `<a href="${href}" target="_blank" rel="noopener" style="color:var(--accent-2);text-decoration:underline;" onclick="event.stopPropagation();">${url}</a>`; |
| 538 | + }); |
| 539 | + } |
| 540 | + |
| 541 | + // Load organizers from config and render them |
| 542 | + function loadOrganizers() { |
| 543 | + const organizersContainer = document.getElementById('organizers-container'); |
| 544 | + |
| 545 | + Object.keys(organizersConfig).forEach(organizerId => { |
| 546 | + const organizer = organizersConfig[organizerId]; |
| 547 | + |
| 548 | + const organizerDiv = document.createElement('div'); |
| 549 | + organizerDiv.className = 'organizer'; |
| 550 | + |
| 551 | + organizerDiv.innerHTML = ` |
| 552 | + <button class="expand-btn" id="btn-${organizerId}" onclick="event.stopPropagation(); toggleOrganizerBio('${organizerId}')">+</button> |
| 553 | + <img src="${organizer.image}" alt="${organizer.name}" style="width:120px;height:120px;border-radius:50%;object-fit:cover;border:4px solid var(--accent);margin-bottom:1rem;box-shadow:0 4px 16px rgba(250,178,43,.3);" onerror="this.style.display='none'"> |
| 554 | + <strong>${organizer.name}</strong> |
| 555 | + <div class="title">${organizer.title}</div> |
| 556 | + <div class="aff">${organizer.affiliation}</div> |
| 557 | + <div class="organizer-bio" id="bio-${organizerId}" onclick="event.stopPropagation();"> |
| 558 | + ${convertLinksInText(organizer.bio)} |
| 559 | + </div> |
| 560 | + `; |
| 561 | + |
| 562 | + // Add click handler for the entire card (except expand button and bio) |
| 563 | + organizerDiv.style.cursor = 'pointer'; |
| 564 | + organizerDiv.addEventListener('click', (event) => { |
| 565 | + // Don't navigate if clicking on expand button or bio |
| 566 | + if (event.target.closest('.expand-btn') || event.target.closest('.organizer-bio')) { |
| 567 | + return; |
| 568 | + } |
| 569 | + window.open(organizer.linkedin, '_blank', 'noopener'); |
| 570 | + }); |
| 571 | + |
| 572 | + organizersContainer.appendChild(organizerDiv); |
| 573 | + }); |
| 574 | + } |
| 575 | + |
| 576 | + // Organizer bio toggle functionality |
| 577 | + function toggleOrganizerBio(organizerId) { |
| 578 | + const bioElement = document.getElementById(`bio-${organizerId}`); |
| 579 | + const btnElement = document.getElementById(`btn-${organizerId}`); |
| 580 | + |
| 581 | + if (bioElement.classList.contains('show')) { |
| 582 | + bioElement.classList.remove('show'); |
| 583 | + btnElement.textContent = '+'; |
| 584 | + } else { |
| 585 | + // Hide all other organizer bios first |
| 586 | + document.querySelectorAll('.organizer-bio.show').forEach(bio => { |
| 587 | + bio.classList.remove('show'); |
| 588 | + }); |
| 589 | + document.querySelectorAll('.organizer .expand-btn').forEach(btn => { |
| 590 | + btn.textContent = '+'; |
| 591 | + }); |
| 592 | + |
| 593 | + // Show this bio |
| 594 | + bioElement.classList.add('show'); |
| 595 | + btnElement.textContent = '−'; |
| 596 | + } |
| 597 | + } |
| 598 | + |
| 599 | + // Load speakers and organizers when page loads |
| 600 | + document.addEventListener('DOMContentLoaded', () => { |
| 601 | + loadSpeakers(); |
| 602 | + loadOrganizers(); |
| 603 | + }); |
528 | 604 | </script> |
529 | 605 | </body> |
530 | 606 | </html> |
0 commit comments