Files
2026-06-04 20:09:31 +07:00

364 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ===== LANGUAGE TOGGLE =====
let currentLang = 'id';
function setLangLabel(lang) {
const nextLang = lang === 'id' ? 'EN' : 'ID';
const labelDesktop = document.getElementById('lang-label');
const labelMobile = document.getElementById('lang-label-mobile');
if (labelDesktop) labelDesktop.textContent = nextLang;
if (labelMobile) labelMobile.textContent = nextLang;
}
function applyLang(lang) {
const pageBody = document.getElementById('page-body');
pageBody.classList.toggle('lang-id', lang === 'id');
pageBody.classList.toggle('lang-en', lang === 'en');
document.documentElement.lang = lang;
}
function toggleLang() {
currentLang = currentLang === 'id' ? 'en' : 'id';
applyLang(currentLang);
setLangLabel(currentLang);
}
document.getElementById('lang-toggle')?.addEventListener('click', toggleLang);
document.getElementById('lang-toggle-mobile')?.addEventListener('click', toggleLang);
// ===== SCROLL REVEAL =====
function initScrollReveal() {
const revealEls = document.querySelectorAll('.reveal');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 });
revealEls.forEach(el => observer.observe(el));
}
initScrollReveal();
// ===== BACK TO TOP =====
function initBackToTop() {
const btn = document.getElementById('back-to-top');
if (!btn) return;
btn.addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));
window.addEventListener('scroll', () => {
const isPastThreshold = window.scrollY > 400;
btn.classList.toggle('hidden', !isPastThreshold);
btn.classList.toggle('flex', isPastThreshold);
});
}
initBackToTop();
// ===== PRESTASI FILTER =====
function setActiveTabStyle(btn, isActive) {
btn.classList.toggle('bg-untan-navy', isActive);
btn.classList.toggle('text-white', isActive);
btn.classList.toggle('bg-white', !isActive);
btn.classList.toggle('text-slate-700', !isActive);
btn.classList.toggle('border', !isActive);
btn.classList.toggle('border-slate-200',!isActive);
}
function filterPrestasi(category) {
document.querySelectorAll('.prestasi-tab-btn').forEach(btn => {
setActiveTabStyle(btn, btn.dataset.tab === category);
});
document.querySelectorAll('#prestasi-grid .prestasi-card').forEach(card => {
card.style.display = (category === 'all' || card.dataset.category === category) ? '' : 'none';
});
}
function initPrestasiFilter() {
document.querySelectorAll('.prestasi-tab-btn').forEach(btn => {
btn.addEventListener('click', () => filterPrestasi(btn.dataset.tab));
});
}
initPrestasiFilter();
// ===== MEGA MENU =====
(function initMegaMenu() {
const menuItems = document.querySelectorAll('[data-menu]');
if (!menuItems.length) return;
let activeMenuId = null;
function closeAllMenus() {
document.querySelectorAll('.mega-panel').forEach(p => p.classList.remove('active'));
updateButtonStates(null);
activeMenuId = null;
}
function updateButtonStates(openMenuId) {
menuItems.forEach(item => {
const isActive = item.getAttribute('data-menu') === openMenuId;
const btn = item.querySelector('.nav-menu-btn');
const chevron = item.querySelector('.fa-chevron-down');
if (btn) {
btn.classList.toggle('active', isActive);
btn.setAttribute('aria-expanded', isActive ? 'true' : 'false');
}
if (chevron) chevron.style.transform = isActive ? 'rotate(180deg)' : '';
});
}
menuItems.forEach(item => {
const menuId = item.getAttribute('data-menu');
const btn = item.querySelector('.nav-menu-btn');
if (!btn) return;
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (activeMenuId === menuId) {
closeAllMenus();
} else {
closeAllMenus();
updateButtonStates(menuId);
activeMenuId = menuId;
}
});
});
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeAllMenus(); });
document.addEventListener('click', e => { if (!e.target.closest('#main-nav') && activeMenuId) closeAllMenus(); });
})();
// ===== MOBILE MENU =====
function initMobileMenu() {
const menuBtn = document.getElementById('mobile-menu-btn');
const menu = document.getElementById('mobile-menu');
const closeBtn = document.getElementById('close-mobile-menu');
if (menuBtn && menu) {
menuBtn.addEventListener('click', () => {
const isOpen = menu.classList.toggle('-translate-x-full') === false;
menuBtn.setAttribute('aria-expanded', String(isOpen));
});
}
if (closeBtn && menu) {
closeBtn.addEventListener('click', () => menu.classList.add('-translate-x-full'));
}
}
initMobileMenu();
// ===== LUCO CHATBOT BUBBLE =====
function initLucoBubble() {
const bubble = document.getElementById('luco-bubble');
const closeBtn = document.getElementById('luco-bubble-close');
if (!bubble) return;
setTimeout(() => bubble.classList.remove('hidden'), 3000);
closeBtn?.addEventListener('click', (e) => {
e.stopPropagation();
bubble.classList.add('hidden');
});
}
initLucoBubble();
// ===== #ASSIGNMENT-BODY ENHANCEMENTS =====
(function initAssignmentBody() {
const body = document.getElementById('assignment-body');
if (!body) return;
// --- 1. Scroll-reveal untuk setiap child langsung ---
const revealTargets = body.querySelectorAll('h3, h4, p, ul, table, figure');
const revealObserver = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => entry.target.classList.add('visible'), i * 60);
revealObserver.unobserve(entry.target);
}
});
}, { threshold: 0.08 });
revealTargets.forEach(el => {
el.classList.add('ab-reveal');
revealObserver.observe(el);
});
// --- 2. Collapsible: Alumni & Mitra (sections yang panjang) ---
// Wrap konten setelah h3 "Kisah Sukses Alumni" dalam collapsible
const allH3 = [...body.querySelectorAll('h3')];
const collapsibleTitles = ['Kisah Sukses Alumni', 'Mitra Strategis', 'Fasilitas Akademik'];
allH3.forEach(h3 => {
if (!collapsibleTitles.includes(h3.textContent.trim())) return;
// Kumpulkan sibling sampai h3 berikutnya
const siblings = [];
let next = h3.nextElementSibling;
while (next && next.tagName !== 'H3') {
siblings.push(next);
next = next.nextElementSibling;
}
if (!siblings.length) return;
// Buat wrapper collapsible
const wrapper = document.createElement('div');
wrapper.className = 'ab-collapsible';
h3.parentNode.insertBefore(wrapper, siblings[0]);
siblings.forEach(s => wrapper.appendChild(s));
// Buat tombol toggle
const btn = document.createElement('button');
btn.className = 'ab-readmore-btn';
btn.innerHTML = 'Lihat Detail <span class="arrow">▼</span>';
btn.setAttribute('aria-expanded', 'false');
h3.insertAdjacentElement('afterend', btn);
btn.addEventListener('click', () => {
const isOpen = wrapper.classList.toggle('open');
btn.classList.toggle('open', isOpen);
btn.innerHTML = isOpen
? 'Sembunyikan <span class="arrow">▼</span>'
: 'Lihat Detail <span class="arrow">▼</span>';
btn.setAttribute('aria-expanded', String(isOpen));
});
});
// --- 3. Table row highlight on click ---
body.querySelectorAll('tbody tr').forEach(row => {
row.style.cursor = 'pointer';
row.addEventListener('click', () => {
body.querySelectorAll('tbody tr.selected').forEach(r => r.classList.remove('selected'));
row.classList.add('selected');
row.style.background = '#dbeafe';
row.style.fontWeight = '600';
setTimeout(() => {
row.style.background = '';
row.style.fontWeight = '';
row.classList.remove('selected');
}, 1800);
});
});
// --- 4. Callout block: inject setelah paragraf karir ---
const pEls = [...body.querySelectorAll('p')];
const kariPara = pEls.find(p => p.textContent.includes('Rp 512 Juta'));
if (kariPara) {
const callout = document.createElement('div');
callout.className = 'ab-callout ab-reveal';
callout.textContent = '87% lulusan bekerja di bidang IT sesuai kompetensi, dengan rata-rata waktu tunggu kerja kurang dari 3 bulan.';
kariPara.insertAdjacentElement('afterend', callout);
revealObserver.observe(callout);
}
// --- 5. Tambahkan section label di atas setiap h3 ---
const sectionMap = {
'Mengenal Program Studi': 'Tentang Prodi',
'Visi': 'Identitas',
'Misi': 'Identitas',
'Tonggak Sejarah': 'Sejarah',
'dalam Angka': 'Data & Statistik',
'Pencapaian': 'Prestasi',
'Study Club': 'Komunitas',
'Prospek Karir': 'Karir',
'Kisah Sukses': 'Alumni',
'Fasilitas': 'Infrastruktur',
'Mitra Strategis': 'Kerjasama',
'Informasi Kontak': 'Kontak',
};
allH3.forEach(h3 => {
const key = Object.keys(sectionMap).find(k => h3.textContent.includes(k));
if (!key) return;
const label = document.createElement('div');
label.className = 'ab-section-label';
label.textContent = sectionMap[key];
h3.insertAdjacentElement('beforebegin', label);
});
})();
// =====================================================
// JELLYFISH.CORP — MOCKUP SCRIPTS
// =====================================================
// ===== PRESTASI FILTER (new jf-* classes) =====
(function initPrestasiFilterJF() {
const tabs = document.querySelectorAll('.jf-tab-btn');
const cards = document.querySelectorAll('#prestasi-grid .jf-prestasi-card');
if (!tabs.length) return;
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('jf-tab-active'));
tab.classList.add('jf-tab-active');
const filter = tab.dataset.filter;
cards.forEach(card => {
const show = filter === 'all' || card.dataset.cat === filter;
card.style.display = show ? '' : 'none';
if (show) {
card.style.animation = 'jfFadeIn 0.35s ease forwards';
}
});
});
});
})();
// ===== SCROLL REVEAL for jf-* cards =====
(function initJFScrollReveal() {
const targets = document.querySelectorAll(
'.jf-stat-card, .jf-stat-row-card, .jf-prestasi-card, ' +
'.jf-sc-card, .jf-karir-card, .jf-alumni-card, ' +
'.jf-infra-card, .jf-mitra-card, .jf-life-card, ' +
'.jf-tl-item, .jf-visi-card, .jf-kontak-item'
);
targets.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(24px)';
el.style.transition = 'opacity 0.55s ease, transform 0.55s ease';
});
const obs = new IntersectionObserver((entries) => {
entries.forEach((entry, i) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, i * 70);
obs.unobserve(entry.target);
}
});
}, { threshold: 0.08 });
targets.forEach(el => obs.observe(el));
})();
// ===== ANIMATED COUNTER for jf-stat-num =====
(function initJFCounters() {
const nums = document.querySelectorAll('.jf-stat-num');
const obs = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
const el = entry.target;
const raw = el.textContent.trim();
const num = parseFloat(raw.replace(/[^0-9.]/g, ''));
if (isNaN(num) || num === 0) return;
const suffix = raw.replace(/[0-9.,]/g, '');
const prefix = raw.startsWith('<') ? '<' : '';
const cleanSuffix = prefix ? suffix : suffix;
let start = 0;
const duration = 1400;
const step = 16;
const increment = num / (duration / step);
const timer = setInterval(() => {
start += increment;
if (start >= num) { start = num; clearInterval(timer); }
const display = num % 1 === 0
? Math.floor(start).toLocaleString('id-ID')
: start.toFixed(0);
el.textContent = prefix + display + cleanSuffix;
}, step);
obs.unobserve(el);
});
}, { threshold: 0.5 });
nums.forEach(el => obs.observe(el));
})();