Files
edu/js/common/LearningGuideModal.js
T

376 lines
9.1 KiB
JavaScript

/**
* 학습 가이드 PDF 팝업
* - btn-guide 클릭 시 레이어 오픈
* - 탭 전환 시 pdf.js viewer iframe src 교체
*/
const LearningGuideModal = (function () {
const DEFAULT_TAB_KEY = "player";
const VIEWER_PATH = "/js/lib/pdfjs-viewer/web/viewer.html";
let layer = null;
let viewer = null;
let closeBtn = null;
let triggerBtn = null;
let tabSwiper = null;
let tabs = [];
let activeTabKey = DEFAULT_TAB_KEY;
let isOpen = false;
function resolvePdfUrl(pdfPath) {
if (!pdfPath) return "";
try {
return new URL(pdfPath, window.location.origin).href;
} catch (error) {
console.error("[LearningGuideModal] PDF URL resolve failed:", error);
return pdfPath;
}
}
function buildViewerUrl(pdfPath) {
if (!pdfPath) return "";
const viewerUrl = new URL(VIEWER_PATH, window.location.origin);
const fileUrl = resolvePdfUrl(pdfPath);
viewerUrl.searchParams.set("file", fileUrl);
viewerUrl.hash = "zoom=60&textlayer=off";
return viewerUrl.href;
}
function getTabButtons() {
if (!layer) return [];
return Array.from(layer.querySelectorAll(".learning-guide-tab"));
}
function getTabButton(key) {
return getTabButtons().find(function (tab) {
return tab.dataset.guideKey === key;
});
}
function detectTabKeyFromPath() {
const path = window.location.pathname.toLowerCase();
const pathMap = [
["index", "main"],
["main", "main"],
["myclass", "myclass"],
["onboarding", "onboarding"],
["learning", "legal"],
["legal_edu", "legal"],
["legal", "legal"],
["leadership", "leadership"],
["insight", "insight"],
["biztrend", "biztrend"],
["mypage", "mypage"],
["player", "player"],
];
for (let i = 0; i < pathMap.length; i += 1) {
const needle = pathMap[i][0];
const key = pathMap[i][1];
if (path.indexOf(needle) !== -1 && getTabButton(key)) return key;
}
return null;
}
function getPageDefaultTabKey() {
if (!layer) return DEFAULT_TAB_KEY;
const fromPath = detectTabKeyFromPath();
if (fromPath) return fromPath;
const btnKey =
triggerBtn && (triggerBtn.dataset.defaultGuideKey || "").trim();
if (btnKey && getTabButton(btnKey)) return btnKey;
const key = (layer.dataset.defaultGuideKey || "").trim();
if (key && getTabButton(key)) return key;
return DEFAULT_TAB_KEY;
}
function getFirstAvailableTabKey() {
const availableTab = getTabButtons().find(function (tab) {
return tab.dataset.pdf && !tab.disabled;
});
return availableTab ? availableTab.dataset.guideKey : getPageDefaultTabKey();
}
function initDefaultTab() {
activeTabKey = getPageDefaultTabKey();
updateTabStates(activeTabKey);
}
function syncOpenState(tabKey) {
const nextKey = tabKey || getPageDefaultTabKey();
loadPdfByKey(nextKey, { forceReload: true });
}
function setDefaultTab(key) {
if (!layer || !key) return;
const nextKey = String(key).trim();
if (!getTabButton(nextKey)) return;
layer.dataset.defaultGuideKey = nextKey;
activeTabKey = nextKey;
updateTabStates(nextKey);
if (isOpen) {
loadPdfByKey(nextKey, { forceReload: true });
return;
}
updateTabLayout(0);
}
function updateTabStates(key) {
getTabButtons().forEach(function (tab) {
const isActive = tab.dataset.guideKey === key;
tab.classList.toggle("is-active", isActive);
tab.setAttribute("aria-selected", isActive ? "true" : "false");
});
activeTabKey = key;
}
function getTabIndex(key) {
const tab = getTabButton(key);
if (!tab || !tabSwiper) return -1;
const slide = tab.closest(".swiper-slide");
if (!slide) return -1;
return Array.from(tabSwiper.slides).indexOf(slide);
}
function isTabOverflow() {
if (!tabSwiper) return false;
const swiperEl = tabSwiper.el;
// 자연 너비 기준 측정을 위해 scroll 모드로 일시 전환
swiperEl.classList.remove("is-tab-even");
swiperEl.classList.add("is-tab-scroll");
tabSwiper.update();
return tabSwiper.wrapperEl.scrollWidth > swiperEl.clientWidth + 1;
}
function updateTabLayout(speed) {
if (!tabSwiper) return;
const swiperEl = tabSwiper.el;
const overflow = isTabOverflow();
swiperEl.classList.toggle("is-tab-scroll", overflow);
swiperEl.classList.toggle("is-tab-even", !overflow);
tabSwiper.params.centeredSlides = overflow;
tabSwiper.params.centeredSlidesBounds = overflow;
tabSwiper.update();
const index = getTabIndex(activeTabKey);
if (index >= 0) {
tabSwiper.slideTo(index, speed !== undefined ? speed : 300);
}
}
function isViewerBlank() {
if (!viewer) return true;
const attrSrc = viewer.getAttribute("src");
return !attrSrc || !attrSrc.trim();
}
function loadPdfByKey(key, options) {
const tab = getTabButton(key);
if (!tab || !tab.dataset.pdf) return;
const forceReload = options && options.forceReload;
updateTabStates(key);
if (viewer) {
const nextSrc = buildViewerUrl(tab.dataset.pdf);
if (forceReload) {
viewer.removeAttribute("src");
requestAnimationFrame(function () {
viewer.src = nextSrc;
});
} else {
viewer.src = nextSrc;
}
}
updateTabLayout(300);
}
function initTabSwiper() {
if (typeof Swiper === "undefined" || !layer) return;
const swiperEl = layer.querySelector(".learning-guide-tab-swiper");
if (!swiperEl) return;
tabs = getTabButtons();
tabSwiper = new Swiper(swiperEl, {
slidesPerView: "auto",
spaceBetween: 1,
centeredSlides: false,
centeredSlidesBounds: false,
slideToClickedSlide: true,
watchOverflow: true,
speed: 300,
resistanceRatio: 0.65,
});
}
function destroyTabSwiper() {
if (tabSwiper) {
tabSwiper.destroy(true, true);
tabSwiper = null;
}
}
function open(tabKey) {
if (!layer || isOpen) return;
layer.classList.remove("hidden");
layer.classList.add("is-open");
layer.setAttribute("aria-hidden", "false");
isOpen = true;
if (typeof scrollManager !== "undefined") {
scrollManager.lock();
} else if (typeof bodyLock === "function") {
bodyLock();
}
requestAnimationFrame(function () {
requestAnimationFrame(function () {
syncOpenState(tabKey);
});
});
if (closeBtn) {
closeBtn.focus();
}
}
function close() {
if (!layer || !isOpen) return;
layer.classList.remove("is-open");
layer.classList.add("hidden");
layer.setAttribute("aria-hidden", "true");
isOpen = false;
if (typeof scrollManager !== "undefined") {
scrollManager.unlock();
} else if (typeof bodyUnlock === "function") {
bodyUnlock();
}
if (triggerBtn) {
triggerBtn.focus();
}
}
function handleTabClick(event) {
const tab = event.currentTarget;
if (!tab || tab.disabled || tab.classList.contains("is-disabled")) return;
const key = tab.dataset.guideKey;
if (!key || !tab.dataset.pdf) return;
if (key === activeTabKey) return;
loadPdfByKey(key);
}
function handleLayerClick(event) {
if (event.target === layer) {
close();
}
}
function handleKeydown(event) {
if (!isOpen || event.key !== "Escape") return;
close();
}
let resizeTimer = null;
function handleResize() {
if (!tabSwiper) return;
if (resizeTimer) clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
tabSwiper.update();
updateTabLayout(0);
}, 150);
}
function bindEvents() {
if (!triggerBtn) {
triggerBtn = document.querySelector(".btn-guide");
}
if (triggerBtn) {
triggerBtn.addEventListener("click", function () {
open();
});
}
if (closeBtn) {
closeBtn.addEventListener("click", function (event) {
event.stopPropagation();
close();
});
}
if (layer) {
layer.addEventListener("click", handleLayerClick);
const content = layer.querySelector(".learning-guide-content");
if (content) {
content.addEventListener("click", function (event) {
event.stopPropagation();
});
}
}
getTabButtons().forEach(function (tab) {
tab.addEventListener("click", handleTabClick);
});
document.addEventListener("keydown", handleKeydown);
window.addEventListener("resize", handleResize);
}
function init() {
layer = document.getElementById("learningGuideLayer");
viewer = document.getElementById("learningGuideViewer");
closeBtn = document.getElementById("btnCloseLearningGuide");
triggerBtn = document.querySelector(".btn-guide");
if (!layer || !viewer) return;
initTabSwiper();
initDefaultTab();
bindEvents();
}
return {
init: init,
open: open,
close: close,
switchTab: loadPdfByKey,
setDefaultTab: setDefaultTab,
getDefaultTab: getPageDefaultTabKey,
buildViewerUrl: buildViewerUrl,
destroy: destroyTabSwiper,
};
})();
if (typeof window !== "undefined") {
window.LearningGuideModal = LearningGuideModal;
}