/**************************************************** * ✅ ±âÁ¸ ÄÚµå À¯Áö (bluring / getUrl / goLocate) ****************************************************/ console.log('✅ main script file loaded'); function bluring() { try { if (event.srcElement.tagName === 'A' || event.srcElement.tagName === 'IMG') { document.body.focus(); } } catch (e) {} } try { document.onfocusin = bluring; } catch (e) {} var rurl = location.href; var purl = getUrl(rurl); function getUrl(url_str) { var real_url; if (url_str.indexOf('/') > 0) { real_url = url_str.split('/'); real_url = real_url[0] + '//' + real_url[2] + '/' + real_url[3] + '/'; } return real_url; } function goLocate(go_url) { document.location = purl + go_url; } /**************************************************** * ✅ (2) °í°´»ç ·Î°í ¹«ÇÑ ·Ñ¸µ (JS only, º¹Á¦´Â JS¿¡¼­¸¸) ****************************************************/ function initCustomerLogoMarquee() { const viewport = document.querySelector('#customer .customer-logos'); if (!viewport) return; const items = Array.from(viewport.querySelectorAll('.logo-item')); if (items.length === 0) return; // ÀÌ¹Ì Æ®·¢ÀÌ ÀÖÀ¸¸é Áߺ¹ »ý¼º ¹æÁö if (viewport.querySelector('.customer-logos-track')) return; const track = document.createElement('div'); track.className = 'customer-logos-track'; items.forEach((el) => track.appendChild(el)); viewport.appendChild(track); items.forEach((el) => { const clone = el.cloneNode(true); clone.classList.add('logo-item-clone'); track.appendChild(clone); }); let running = true; const pxPerSec = 60; let lastTs = null; let x = 0; let oneSetWidth = 0; function refreshWidth() { oneSetWidth = track.scrollWidth / 2; if (!oneSetWidth) requestAnimationFrame(refreshWidth); } window.addEventListener('load', refreshWidth); refreshWidth(); function step(ts) { if (!running) return; if (lastTs == null) lastTs = ts; const dt = (ts - lastTs) / 1000; lastTs = ts; if (oneSetWidth > 0) { x -= pxPerSec * dt; if (Math.abs(x) >= oneSetWidth) { x += oneSetWidth; } track.style.transform = `translate3d(${x}px, 0, 0)`; } requestAnimationFrame(step); } requestAnimationFrame(step); document.addEventListener('visibilitychange', () => { if (document.hidden) { running = false; } else { running = true; lastTs = null; requestAnimationFrame(step); } }); window.addEventListener('resize', refreshWidth); } /**************************************************** * ✅ DOMContentLoaded ****************************************************/ document.addEventListener('DOMContentLoaded', function () { console.log('✅ DOMContentLoaded fired'); /********************** * ¸ð¹ÙÀÏ ¸Þ´º Åä±Û **********************/ const mobileMenuToggle = document.getElementById('mobileMenuToggle'); const mainMenu = document.getElementById('mainMenu'); if (mobileMenuToggle && mainMenu) { mobileMenuToggle.addEventListener('click', function () { mainMenu.classList.toggle('mobile-menu-open'); }); const menuItems = mainMenu.querySelectorAll('.navbar_items'); menuItems.forEach(function (item) { item.addEventListener('click', function () { mainMenu.classList.remove('mobile-menu-open'); }); }); } /********************** * 1) ºÎµå·¯¿î ½ºÅ©·Ñ **********************/ const scrollLinks = document.querySelectorAll('.js-scroll'); scrollLinks.forEach((link) => { link.addEventListener('click', function (e) { const targetId = this.getAttribute('href'); if (!targetId || !targetId.startsWith('#')) return; const targetEl = document.querySelector(targetId); if (!targetEl) return; e.preventDefault(); const topMenu = document.querySelector('#topmenu'); const topMenuHeight = topMenu ? topMenu.offsetHeight : 0; const targetPosition = targetEl.offsetTop - topMenuHeight; window.scrollTo({ top: targetPosition, behavior: 'smooth' }); }); }); /********************** * 2) ½ºÅ©·Ñ ½Ã »ó´Ü ¸Þ´º ½ºÅ¸ÀÏ º¯°æ + 3) active Àû¿ë **********************/ const topMenu = document.querySelector('#topmenu'); const navItems = document.querySelectorAll('.navbar_items'); function updateTopMenuAndActive() { if (!topMenu) return; const currentScroll = window.pageYOffset; const topMenuHeight = topMenu.offsetHeight; // ¸Þ´º ½ºÅ¸ÀÏ if (currentScroll > 100) { topMenu.style.background = 'rgba(255,255,255,0.98)'; topMenu.style.boxShadow = '0 4px 20px rgba(0,0,0,0.1)'; } else { topMenu.style.background = 'rgba(255,255,255,0.98)'; topMenu.style.boxShadow = '0 2px 10px rgba(0,0,0,0.05)'; } // active °è»ê (contact´Â contact-section ¿µ¿ª¿¡¼­ href="#contact"¸¦ active) const sections = document.querySelectorAll('section[id]'); let current = ''; sections.forEach((section) => { const isContact = section.id === 'contact-section'; const offset = isContact ? 200 : 100; const sectionTop = section.offsetTop - topMenuHeight - offset; const sectionBottom = sectionTop + section.clientHeight; if (currentScroll >= sectionTop && currentScroll < sectionBottom) { current = section.id; } }); navItems.forEach((item) => { item.classList.remove('active'); const href = item.getAttribute('href'); if (current === 'contact-section' && href === '#contact') { item.classList.add('active'); return; } if (href === '#' + current) { item.classList.add('active'); } }); } window.addEventListener('scroll', updateTopMenuAndActive); updateTopMenuAndActive(); /********************** * 5) °í°´»ç ·Î°í ¹«ÇÑ ·Ñ¸µ **********************/ initCustomerLogoMarquee(); }); /**************************************************** * ✅ Æ÷Æ®Æú¸®¿À ¸®½ºÆ® (Ä«Å×°í¸® ajax ±³Ã¼) ****************************************************/ (function () { function extractUrlFromOnclick(el) { const onclick = el && el.getAttribute && el.getAttribute('onclick'); if (!onclick) return null; const m = onclick.match(/linkMove\('([^']+)'\)/); return m ? m[1] : null; } function normalizeUrl(url) { const raw = String(url || '').replace(/&/g, '&'); try { return new URL(raw, location.href).toString(); } catch { return raw; } } async function fetchHtml(url) { const res = await fetch(url, { credentials: 'same-origin' }); const buf = await res.arrayBuffer(); let text = new TextDecoder('euc-kr').decode(buf); const m = text.match(/charsets*=\s*["']?([a-zA-Z0-9-_]+)["']?/i); const enc = m && m[1] ? m[1].toLowerCase() : ''; if (enc.includes('utf')) text = new TextDecoder('utf-8').decode(buf); return text; } function findListContainer(root) { let el = root.querySelector('#container_list'); if (el) return el; el = root.querySelector('[id^="container_list"]'); if (el) return el; el = root.querySelector('#portfolio .portfolio-board, .portfolio-board'); return el; } async function replaceBoardList(url) { const curScrollY = window.scrollY; const html = await fetchHtml(url); const doc = new DOMParser().parseFromString(html, 'text/html'); const newEl = findListContainer(doc); const curEl = findListContainer(document); if (!newEl || !curEl) { location.href = url; return; } curEl.innerHTML = newEl.innerHTML; history.replaceState(null, '', url); requestAnimationFrame(() => { window.scrollTo(0, curScrollY); }); } async function onCategoryClick(e) { const tab = e.target.closest('#category_navi > div'); if (!tab) return; const urlRaw = extractUrlFromOnclick(tab); if (!urlRaw) return; const url = normalizeUrl(urlRaw); e.preventDefault(); e.stopImmediatePropagation(); e.stopPropagation(); try { await replaceBoardList(url); } catch (err) { console.error(err); location.href = url; } } ['mousedown', 'touchstart', 'click'].forEach((evt) => { document.addEventListener(evt, onCategoryClick, true); }); // ⚠️ linkMove °ü·Ã ¿¡·¯°¡ ³ª´Â ȯ°æ¿¡¼­´Â board.js Áߺ¹ ·Îµå °¡´É¼ºÀÌ Å­. // ±×·¡µµ inline onclick ´ëÀÀÀ» À§ÇØ window.linkMove¸¦ "¼±¾ð"ÀÌ ¾Æ´Ï¶ó "ÇÁ·ÎÆÛƼ"·Î¸¸ ¼³Á¤. function lockedLinkMove(url) { const normalized = normalizeUrl(url); replaceBoardList(normalized).catch((err) => { console.error(err); location.href = normalized; }); return false; } try { // configurableÀº true·Î µÎ¾î, ±âÁ¸ ½ºÅ©¸³Æ®¿Í Ãæµ¹ ½Ã º¹±¸ °¡´ÉÇÏ°Ô Object.defineProperty(window, 'linkMove', { value: lockedLinkMove, writable: true, configurable: true, }); } catch (e) { window.linkMove = lockedLinkMove; } })(); /**************************************************** * ✅ Æ÷Æ®Æú¸®¿À ¸ð´Þ ****************************************************/ (function () { function ensureModal() { let overlay = document.querySelector('.pf-modal-overlay'); if (overlay) return overlay; overlay = document.createElement('div'); overlay.className = 'pf-modal-overlay'; overlay.innerHTML = ` `; document.body.appendChild(overlay); overlay.addEventListener('click', (e) => { if (e.target === overlay) closeModal(); }); overlay.querySelector('.pf-modal-close').addEventListener('click', closeModal); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeModal(); }); return overlay; } function openModal({ imgSrc, title }) { const overlay = ensureModal(); const img = overlay.querySelector('.pf-modal-img'); const titleEl = overlay.querySelector('.pf-modal-title'); img.src = imgSrc || ''; titleEl.textContent = title || ''; overlay.style.display = 'flex'; document.documentElement.style.overflow = 'hidden'; } function closeModal() { const overlay = document.querySelector('.pf-modal-overlay'); if (!overlay) return; overlay.style.display = 'none'; document.documentElement.style.overflow = ''; } document.addEventListener( 'click', function (e) { const a = e.target.closest('.gallery_item_table .item_cell_media a'); if (!a) return; e.preventDefault(); e.stopImmediatePropagation(); const table = a.closest('.gallery_item_table'); const imgEl = a.querySelector('img'); const titleEl = table?.querySelector('.item_cell_subject span'); const title = (titleEl?.textContent || '').replace(/\s+/g, ' ').trim(); let imgSrc = imgEl?.getAttribute('src') || ''; if (imgSrc && !imgSrc.startsWith('http')) imgSrc = new URL(imgSrc, location.href).toString(); openModal({ imgSrc, title }); }, true ); })(); /**************************************************** * ✅ Achievement ¼ýÀÚ Ä«¿îÆ® ¾Ö´Ï¸ÞÀÌ¼Ç ****************************************************/ console.log('Achievement counter script loaded!'); const intFormatter = new Intl.NumberFormat('ko-KR'); function formatInt(value) { const num = Math.floor(Number(value) || 0); return intFormatter.format(num); } function getOrCreateNumberTextNode(numberEl) { const suffixSpan = numberEl.querySelector('span'); if (!suffixSpan) return null; if (numberEl.lastChild !== suffixSpan) numberEl.appendChild(suffixSpan); let numNode = null; Array.from(numberEl.childNodes).forEach((node) => { if (node === suffixSpan) return; if (node.nodeType === Node.TEXT_NODE) { const v = node.nodeValue || ''; if (/\d/.test(v)) { if (!numNode) numNode = node; else node.remove(); } else { node.remove(); } } else { node.remove(); } }); if (!numNode) { numNode = document.createTextNode(''); numberEl.insertBefore(numNode, suffixSpan); } return numNode; } function animateCounter(numberEl, target, isDecimal, duration = 3000) { let start = 0; const increment = target / (duration / 16); const suffixSpan = numberEl.querySelector('span'); const numberTextNode = suffixSpan ? getOrCreateNumberTextNode(numberEl) : null; const timer = setInterval(() => { start += increment; const val = start >= target ? target : start; let displayText; if (isDecimal) displayText = Number(val).toFixed(2); else displayText = formatInt(val); if (numberTextNode) numberTextNode.nodeValue = displayText + ' '; else numberEl.textContent = displayText; if (start >= target) { if (!isDecimal) { if (numberTextNode) numberTextNode.nodeValue = formatInt(target) + ' '; else numberEl.textContent = formatInt(target); } clearInterval(timer); } }, 16); } function checkAchievementSection() { const achievementSection = document.querySelector('.achievement-section'); if (!achievementSection) return; const achievementNumbers = document.querySelectorAll('.achievement-number'); if (achievementNumbers.length === 0) return; const sectionTop = achievementSection.offsetTop; const scrollPosition = window.pageYOffset + window.innerHeight; if (scrollPosition > sectionTop && !achievementSection.classList.contains('counted')) { achievementSection.classList.add('counted'); achievementNumbers.forEach((numberEl) => { const text = (numberEl.textContent || '').trim(); const target = parseFloat(text.replace(/[^0-9.]/g, '')); const isDecimal = text.includes('.') || target % 1 !== 0; const suffixSpan = numberEl.querySelector('span'); if (suffixSpan) { const tn = getOrCreateNumberTextNode(numberEl); tn.nodeValue = isDecimal ? '0.00 ' : '0 '; } else { numberEl.textContent = isDecimal ? '0.00' : '0'; } setTimeout(() => animateCounter(numberEl, target, isDecimal, 3000), 100); }); } } window.addEventListener('scroll', checkAchievementSection); checkAchievementSection(); /**************************************************** * ✅ ScrollMagic (½ÇÆÐ ½Ã IntersectionObserver fallback) ****************************************************/ function initScrollSpyFallback() { const spyEls = document.querySelectorAll('section.scroll-spy'); const observer = new IntersectionObserver( function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) entry.target.classList.add('show'); }); }, { threshold: 0.2 } ); spyEls.forEach(function (spyEl) { observer.observe(spyEl); }); } function initScrollSpy() { if (typeof ScrollMagic === 'undefined') { initScrollSpyFallback(); return; } try { const controller = new ScrollMagic.Controller(); const spyEls = document.querySelectorAll('section.scroll-spy'); spyEls.forEach((spyEl) => { new ScrollMagic.Scene({ triggerElement: spyEl, triggerHook: 0.8, }) .setClassToggle(spyEl, 'show') .addTo(controller); }); } catch (error) { console.error('ScrollMagic ÃʱâÈ­ ¿À·ù:', error); initScrollSpyFallback(); } } (function startInit() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { setTimeout(initScrollSpy, 100); }); } else { setTimeout(initScrollSpy, 100); } })();