msos/main.js

476 lines
23 KiB
JavaScript
Raw 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.

document.addEventListener('DOMContentLoaded', function() {
/**
* ===================================================================
* 1. OSNOVNA FUNKCIONALNOST MOBILNE NAVIGACIJE
* ===================================================================
*/
// === MOBILNI MENI: Preklop vidnosti menija ===
try {
const icon = document.querySelector('.mobile-menu-icon');
const root = document.documentElement;
if (!icon) {
console.error('Mobile menu icon not found.');
} else {
icon.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
root.classList.toggle('nav-open');
});
document.addEventListener('click', (e) => {
if (root.classList.contains('nav-open') && !e.target.closest('.mobile-nav-panel') && !e.target.closest('.mobile-menu-icon')) {
root.classList.remove('nav-open');
}
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
root.classList.remove('nav-open');
}
});
}
} catch (err) {
console.error('Error in Mobile Menu Toggle:', err);
}
// --- Mobilni Accordion podmeni ---
try {
const mobileNav = document.querySelector('.mobile-nav-panel .mobile-nav');
if (mobileNav) {
const menuItemsWithChildren = mobileNav.querySelectorAll('.menu-item-has-children');
menuItemsWithChildren.forEach(item => {
const link = item.querySelector('a');
if (link) {
link.addEventListener('click', (event) => {
if (event.currentTarget.parentElement.classList.contains('menu-item-has-children')) {
event.preventDefault();
}
const parentLi = event.currentTarget.parentElement;
menuItemsWithChildren.forEach(otherItem => {
if (otherItem !== parentLi && otherItem.classList.contains('open')) {
otherItem.classList.remove('open');
}
});
parentLi.classList.toggle('open');
});
}
});
}
} catch (error) {
console.error('Error in Mobile Accordion Menu:', error);
}
/**
* ===================================================================
* 2. IZBOLJŠAVE UPORABNIŠKE IZKUŠNJE (UX/UI)
* ===================================================================
*/
// --- Funkcija za poudarjanje aktivne povezave v navigaciji ---
function highlightActiveLink() {
try {
const currentPath = window.location.pathname;
const navLinks = document.querySelectorAll('.navigation a');
let bestMatch = null;
let longestMatch = 0;
navLinks.forEach(link => {
if (link.closest('.dropdown-menu')) return;
const linkPath = new URL(link.href, window.location.origin).pathname;
if (currentPath.startsWith(linkPath) && linkPath.length > longestMatch) {
longestMatch = linkPath.length;
bestMatch = link;
}
});
if (bestMatch) {
const parentMenuItem = bestMatch.closest('.menu-item');
if (parentMenuItem) {
parentMenuItem.classList.add('active-page');
}
}
} catch (error) {
console.error("Error highlighting active link:", error);
}
}
// --- Funkcionalnost za preklopnik jezika (Desktop Dropdown + Mobile Modal) ---
function setupLanguageSwitcher() {
try {
const path = window.location.pathname;
const pathParts = path.split('/').filter(Boolean);
const validLangs = ['en', 'si', 'mk'];
let pathWithoutLang = '/';
if (pathParts.length > 0 && validLangs.includes(pathParts[0])) {
pathWithoutLang = '/' + pathParts.slice(1).join('/');
if (path.endsWith('/') && pathWithoutLang !== '/') {
pathWithoutLang += '/';
}
} else {
pathWithoutLang = path;
}
document.querySelectorAll('a[data-lang]').forEach(link => {
const targetLang = link.getAttribute('data-lang');
const newUrl = `/${targetLang}${pathWithoutLang}`;
link.setAttribute('href', newUrl.replace(/\/{2,}/g, '/'));
});
const desktopSelectors = document.querySelectorAll('.header-right-wrapper .language-selector');
desktopSelectors.forEach(selector => {
const currentLang = selector.querySelector('.current-lang');
if (currentLang) {
currentLang.addEventListener('click', (event) => {
event.stopPropagation();
const isActive = selector.classList.contains('active');
document.querySelectorAll('.language-selector.active').forEach(s => s.classList.remove('active'));
if (!isActive) {
selector.classList.add('active');
}
});
}
});
window.addEventListener('click', () => {
desktopSelectors.forEach(s => s.classList.remove('active'));
});
const mobileLangButton = document.getElementById('mobile-lang-trigger');
const languageModal = document.getElementById('language-modal-overlay');
const closeModalButton = document.getElementById('language-modal-close');
if (mobileLangButton && languageModal && closeModalButton) {
mobileLangButton.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
languageModal.classList.add('active');
});
const closeModal = () => {
languageModal.classList.remove('active');
};
closeModalButton.addEventListener('click', closeModal);
languageModal.addEventListener('click', (e) => {
if (e.target === languageModal) closeModal();
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && languageModal.classList.contains('active')) closeModal();
});
}
} catch (error) {
console.error('Error in Language Switcher setup:', error);
}
}
/**
* ===================================================================
* 3. OSTALE FUNKCIONALNOSTI (FAQ, ACTIVITIES, ITD.)
* ===================================================================
*/
// --- FAQ Accordion ---
try {
document.querySelectorAll('.faq-item h3').forEach(header => {
header.addEventListener('click', () => {
const faqItem = header.parentElement;
faqItem.classList.toggle('open');
});
});
} catch (error) {
console.error('Error in FAQ Accordion:', error);
}
// --- Activities Tab ---
try {
const activityNavItems = document.querySelectorAll('.activities-nav li');
const activityDetailsText = document.querySelector('.activity-details p');
const activityDetailsImage = document.querySelector('.activity-details img');
if (activityNavItems.length > 0 && activityDetailsText && activityDetailsImage) {
const activitiesData = {
'Student Support': {
text: "We know moving to a new country can feel overwhelming, so were here to make it easier. From helping you with university applications to finding your way around student life in Slovenia, you can always count on us. We offer free consultations, answer your questions on Instagram and Facebook (yes, even late at night), and host events where you can meet others and hear real experiences first-hand. With mentorship, info sessions, and a friendly community by your side, youll never have to go through the journey alone.",
imgSrc: "../images/img1-08.09.2025.webp",
altText: "Students receiving support and guidance"
},
'Fun & Social Life': {
text: "Student life is so much more than lectures and exams — its about the moments in between. At MSOS, we create those moments: from parties, concerts, and casual hangouts, to cinema nights, excursions, and team-building trips. We also host talks and events where you can share ideas, meet new people, and feel part of something bigger. Whether youre connecting with Macedonian students or making friends with Slovenians and internationals, our activities are all about building a community where you can relax, have fun, and create memories that last long after university.",
imgSrc: "../images/img2-08.09.2025.webp",
altText: "Students socializing and having fun at an event"
},
'Education & Culture': {
text: "Studying abroad isnt only about lectures and exams — its also about growing as a person. Thats why we mix learning with culture. We run study groups and small workshops where you can share knowledge and pick up new skills. At the same time, we keep our traditions alive with cultural evenings, music, food, and gatherings that remind us of home. Its a balance: support for your studies, and a space to stay connected to who we are.",
imgSrc: "../images/img3-08.09.2025.webp",
altText: "An educational workshop or cultural event"
},
'Student Representation': {
text: "Studying abroad comes with challenges, and sometimes students need a voice thats heard. At MSOS, we take that role seriously. We organize meetings with government representatives, ministries, embassies, universities, and other key institutions to make sure the interests of Macedonian students in Slovenia are on the table. From campus issues to academic policies and integration, we work to solve problems, push for positive changes, and create a better experience for all of us.",
imgSrc: "../images/img4-08.09.2025.webp",
altText: "MSOS members representing students at a formal meeting"
}
};
activityNavItems.forEach(item => {
item.addEventListener('click', () => {
activityNavItems.forEach(navItem => navItem.classList.remove('active'));
item.classList.add('active');
const activityName = item.textContent.trim();
if (activitiesData[activityName]) {
activityDetailsText.textContent = activitiesData[activityName].text;
activityDetailsImage.src = activitiesData[activityName].imgSrc;
activityDetailsImage.alt = activitiesData[activityName].altText;
}
});
});
}
} catch (error) {
console.error('Error in Activities Tab:', error);
}
/**
* ===================================================================
* 4. INTERAKTIVNA SEKCIJA "MISSION & VISION"
* ===================================================================
*/
try {
const missionVisionContainer = document.getElementById('mission-vision-interactive');
if (missionVisionContainer) {
const missionBox = missionVisionContainer.querySelector('.mission-box');
const visionBox = missionVisionContainer.querySelector('.vision-box');
missionBox.addEventListener('mouseenter', () => {
missionVisionContainer.classList.remove('show-vision');
});
visionBox.addEventListener('mouseenter', () => {
missionVisionContainer.classList.add('show-vision');
});
}
} catch(error) {
console.error('Error in Mission/Vision interaction setup:', error);
}
/**
* ===================================================================
* 5. SCROLLSPY IN PROGRESS BAR ZA STRANI ČLANKOV (POSODOBLJENO)
* ===================================================================
*/
try {
const articlePage = document.querySelector('.article-page');
if (articlePage) {
const articleContainer = articlePage.querySelector('.article-container');
const articleBody = articlePage.querySelector('.article-body');
const headings = articleBody ? Array.from(articleBody.querySelectorAll('h2')) : [];
const articleTitleEl = articlePage.querySelector('.article-header h1');
const articleTitle = articleTitleEl ? articleTitleEl.textContent.trim()
: (document.title || '').trim();
// Label for a heading in the table of contents. An optional
// data-nav-title attribute lets an article override the full <h2>
// text with a shorter label; otherwise we use the heading text.
const navLabel = (heading) =>
(heading.getAttribute('data-nav-title') || heading.textContent).trim();
// --- 5a. Zagotovi, da ima vsak naslov enoličen id (za sidra) ---
// Transliterate then slugify so anchors are readable in every language:
// Slovenian diacritics (č/š/ž/…) and Macedonian Cyrillic map to ASCII.
// Anything still unmapped is dropped and falls back to section-N below.
const TRANSLIT = {
// Slovenian / Latin diacritics
'č':'c','ć':'c','š':'s','ž':'z','đ':'dj',
'á':'a','à':'a','â':'a','ä':'a','ã':'a','é':'e','è':'e','ê':'e','ë':'e',
'í':'i','ì':'i','î':'i','ï':'i','ó':'o','ò':'o','ô':'o','ö':'o','õ':'o',
'ú':'u','ù':'u','û':'u','ü':'u','ñ':'n','ç':'c','ß':'ss',
// Macedonian Cyrillic
'а':'a','б':'b','в':'v','г':'g','д':'d','ѓ':'gj','е':'e','ж':'zh','з':'z',
'ѕ':'dz','и':'i','ј':'j','к':'k','л':'l','љ':'lj','м':'m','н':'n','њ':'nj',
'о':'o','п':'p','р':'r','с':'s','т':'t','ќ':'kj','у':'u','ф':'f','х':'h',
'ц':'c','ч':'ch','џ':'dj','ш':'sh'
};
const usedIds = new Set();
const transliterate = (text) =>
text.replace(/[^\x00-\x7F]/g, (ch) => (ch in TRANSLIT ? TRANSLIT[ch] : ''));
const slugify = (text) => transliterate(text.toLowerCase())
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
headings.forEach((heading, i) => {
let id = heading.id || slugify(heading.textContent);
if (id.length < 2) id = 'section-' + (i + 1);
let unique = id, n = 2;
while (usedIds.has(unique)) { unique = id + '-' + (n++); }
usedIds.add(unique);
heading.id = unique;
});
// --- 5b. Zagotovi postavitveni ovoj in stransko vrstico (Desktop) ---
// Older article pages (SI/MK and some EN) ship without the TOC
// scaffolding, so we build it here from the page's own headings.
let layoutContainer = articlePage.querySelector('.article-layout-container');
if (!layoutContainer && articleContainer) {
layoutContainer = document.createElement('div');
layoutContainer.className = 'article-layout-container';
articleContainer.parentNode.insertBefore(layoutContainer, articleContainer);
layoutContainer.appendChild(articleContainer);
}
let sidebar = articlePage.querySelector('.article-sidebar');
if (!sidebar && layoutContainer && headings.length > 0) {
sidebar = document.createElement('aside');
sidebar.className = 'article-sidebar';
sidebar.innerHTML = '<nav class="scrollspy-nav"><ul></ul></nav>';
layoutContainer.insertBefore(sidebar, layoutContainer.firstChild);
}
const scrollspyNav = sidebar ? sidebar.querySelector('.scrollspy-nav') : null;
const desktopUl = scrollspyNav ? scrollspyNav.querySelector('ul') : null;
// --- 5c. Napolni namizni seznam, če je prazen (ohrani ročno napisanega) ---
if (desktopUl && desktopUl.children.length === 0 && headings.length > 0) {
headings.forEach(heading => {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = '#' + heading.id;
a.textContent = navLabel(heading);
li.appendChild(a);
desktopUl.appendChild(li);
});
}
// --- 5d. Naslov članka nad kazalom (Desktop) ---
if (sidebar && !sidebar.querySelector('.article-sidebar-title') && articleTitle) {
const titleEl = document.createElement('p');
titleEl.className = 'article-sidebar-title';
titleEl.textContent = articleTitle;
sidebar.insertBefore(titleEl, sidebar.firstChild);
}
// --- 5e. Zagotovi lepljivo mobilno glavo z naslovom + progress bar ---
let mobileHeader = articlePage.querySelector('.mobile-article-header');
if (!mobileHeader && headings.length > 0) {
mobileHeader = document.createElement('div');
mobileHeader.className = 'mobile-article-header';
mobileHeader.innerHTML =
'<span id="mobile-article-title"></span>' +
'<i class="fas fa-chevron-down"></i>' +
'<div class="progress-bar-container"><div class="progress-bar"></div></div>' +
'<div class="mobile-scrollspy-dropdown"><ul id="mobile-scrollspy-links"></ul></div>';
articlePage.insertBefore(mobileHeader, articlePage.firstChild);
}
const mobileTitle = document.getElementById('mobile-article-title');
if (mobileTitle && !mobileTitle.textContent.trim()) {
mobileTitle.textContent = articleTitle; // known at load; scrollspy refines it
}
const mobileDropdown = articlePage.querySelector('.mobile-scrollspy-dropdown');
const mobileLinksContainer = document.getElementById('mobile-scrollspy-links');
const progressBar = articlePage.querySelector('.progress-bar');
const desktopNavLinks = desktopUl ? Array.from(desktopUl.querySelectorAll('a')) : [];
// --- 5f. Napolni mobilni spustni seznam ---
if (headings.length > 0 && mobileLinksContainer && mobileLinksContainer.children.length === 0) {
headings.forEach(heading => {
const listItem = document.createElement('li');
const link = document.createElement('a');
link.href = '#' + heading.id;
link.textContent = navLabel(heading);
listItem.appendChild(link);
mobileLinksContainer.appendChild(listItem);
});
}
// --- 5g. Odpiranje/zapiranje mobilnega spustnega seznama ---
if (mobileHeader && mobileDropdown) {
mobileHeader.addEventListener('click', (e) => {
if (!e.target.closest('a')) {
mobileHeader.classList.toggle('open');
mobileDropdown.classList.toggle('open');
}
});
mobileDropdown.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
mobileHeader.classList.remove('open');
mobileDropdown.classList.remove('open');
});
});
}
// --- 5h. Progress Bar ---
function updateProgressBar() {
if (!progressBar) return;
const scrollableHeight = document.documentElement.scrollHeight - window.innerHeight;
const scrollTop = window.scrollY;
progressBar.style.width = scrollableHeight > 0
? `${(scrollTop / scrollableHeight) * 100}%`
: '0%';
}
// --- 5i. Scrollspy (posodobi navigacijo, naslov in URL sidro) ---
if (headings.length > 0) {
let activeId = null;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
const id = entry.target.id;
if (id === activeId) return;
activeId = id;
// Namizna navigacija
const desktopNavLink = desktopNavLinks.find(link => link.getAttribute('href') === `#${id}`);
if (desktopNavLink) {
desktopNavLinks.forEach(link => link.classList.remove('active'));
desktopNavLink.classList.add('active');
}
// Mobilni naslov + aktivna povezava
if (mobileTitle) mobileTitle.textContent = navLabel(entry.target);
if (mobileLinksContainer) {
const mobileNavLink = mobileLinksContainer.querySelector(`a[href="#${id}"]`);
if (mobileNavLink) {
mobileLinksContainer.querySelectorAll('a').forEach(link => link.classList.remove('active'));
mobileNavLink.classList.add('active');
}
}
// Posodobi URL sidro brez skoka in brez polnjenja zgodovine
if (window.history && history.replaceState) {
history.replaceState(null, '', '#' + id);
}
});
}, {
rootMargin: "-100px 0px -55% 0px" // Sproži, ko je naslov v zgornjem delu zaslona
});
headings.forEach(heading => observer.observe(heading));
}
window.addEventListener('scroll', updateProgressBar);
updateProgressBar();
}
} catch (error) {
console.error('Error in Article Scrollspy/Progress Bar setup:', error);
}
/**
* ===================================================================
* 6. ZAGON FUNKCIJ PO NALOŽITVI STRANI
* ===================================================================
*/
highlightActiveLink();
setupLanguageSwitcher();
});