fix: 전체 UI 개선
Deploy EG-BIM QA Gateway / deploy (push) Successful in 1m0s

This commit is contained in:
root committed 2026-11-02 13:26:14 +09:00
1 parent c62630e22d
commit 308c06516c
142 files changed
+48475

No files matched your search

@@ -0,0 +1,963 @@
const STORAGE_KEY = "baron_sso_session";
const PENDING_LOGIN_KEY = "baron_sso_pending_login";
const STATE_KEY = "baron_sso_state";
const VERIFIER_KEY = "baron_sso_code_verifier";
const RETURN_TO_KEY = "baron_sso_return_to";
const FORCE_LOGIN_KEY = "baron_sso_force_login";
const OVERLAY_ID = "baron-sso-overlay";
const STYLE_ID = "baron-sso-style";
const messages = {
ko: {
loadingTitle: "BARON SSO 확인 중",
loadingDescription: "접근 권한을 확인하고 있습니다.",
redirectTitle: "BARON SSO 로그인으로 이동합니다",
redirectDescription: "내부 사용자 인증이 필요합니다.",
redirectAction: "다시 로그인",
unauthorizedTitle: "접근 권한이 없습니다",
unauthorizedDescription: "BARON 내부 사용자 계정으로 로그인해야 합니다.",
retryAction: "다시 로그인",
configTitle: "BARON SSO 설정이 필요합니다",
configDescription: "clientId, authorizeUrl, tokenUrl 값을 먼저 설정하세요.",
exchangeTitle: "로그인 처리에 실패했습니다",
exchangeDescription: "인증 응답을 처리하지 못했습니다. 다시 로그인해 주세요.",
logoutAction: "로그아웃",
},
en: {
loadingTitle: "Checking BARON SSO",
loadingDescription: "Verifying your access.",
redirectTitle: "Redirecting to BARON SSO",
redirectDescription: "Internal user authentication is required.",
redirectAction: "Sign in again",
unauthorizedTitle: "Access denied",
unauthorizedDescription: "You must sign in with an internal BARON account.",
retryAction: "Sign in again",
configTitle: "BARON SSO configuration required",
configDescription: "Set clientId, authorizeUrl, and tokenUrl before enabling the gate.",
exchangeTitle: "Login failed",
exchangeDescription: "The authentication response could not be processed. Please try again.",
logoutAction: "Logout",
},
};
async function mergeConfig(overrides = {}) {
const module = await import("./baron-sso-config.js?v=20260721-worker2");
const baseConfig = module.default || {};
const windowConfig = window.BARON_SSO_CONFIG || {};
const config = {
...baseConfig,
...windowConfig,
...overrides,
};
config.allowedEmailDomains = normalizeArray(config.allowedEmailDomains);
config.allowedEmails = normalizeArray(config.allowedEmails);
config.requiredGroups = normalizeArray(config.requiredGroups);
config.publicPaths = normalizeArray(config.publicPaths);
config.extraAuthorizeParams = config.extraAuthorizeParams || {};
config.extraTokenParams = config.extraTokenParams || {};
config.locale = config.locale || detectLocale();
return config;
}
function normalizeArray(value) {
return Array.isArray(value) ? value.filter(Boolean) : [];
}
function getRootPrefix(pathname = window.location.pathname) {
const pathSegments = pathname.split("/").filter(Boolean);
const rootMarkerSegments = ["ko", "en", "callback", "assets", "protected", "public", "recruit"];
return pathSegments.length && !rootMarkerSegments.includes(pathSegments[0])
? `/${pathSegments[0]}`
: "";
}
function normalizePath(path) {
if (!path) {
return "/";
}
const normalizedPath = path.replace(/\/index\.html$/i, "/");
if (normalizedPath.length > 1 && normalizedPath.endsWith("/")) {
return normalizedPath.slice(0, -1);
}
return normalizedPath;
}
function matchesPublicPath(path, entry) {
const normalizedPath = normalizePath(path);
const normalizedEntry = normalizePath(entry);
if (normalizedEntry.endsWith("_")) {
return normalizedPath.startsWith(normalizedEntry);
}
return normalizedPath === normalizedEntry || normalizedPath.startsWith(`${normalizedEntry}/`);
}
function detectLocale() {
return window.location.pathname.startsWith("/en/") ? "en" : "ko";
}
function isLocalEnvironment() {
return window.location.hostname !== "baroncs.co.kr";
}
function getMessage(config, key) {
const localeMessages = messages[config.locale] || messages.ko;
return localeMessages[key];
}
function getWorkerManagedAuth() {
const workerAuth = window.BARON_WORKER_AUTH;
if (!workerAuth || workerAuth.mode !== "worker") {
return null;
}
return workerAuth;
}
function attachWorkerManagedLogout(workerAuth) {
window.baronSsoLogout = () => {
const logoutUrl = workerAuth?.logoutUrl || "/auth/logout";
window.location.assign(logoutUrl);
};
}
function installStyles() {
if (document.getElementById(STYLE_ID)) {
return;
}
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = `
html[data-baron-auth-pending="true"] body > :not(#${OVERLAY_ID}) {
visibility: hidden;
}
#${OVERLAY_ID} {
position: fixed;
inset: 0;
z-index: 99999;
display: none;
align-items: center;
justify-content: center;
padding: 24px;
background: rgba(9, 17, 27, 0.78);
font-family: "Pretendard", "Noto Sans KR", sans-serif;
}
#${OVERLAY_ID}.is-visible {
display: flex;
}
#${OVERLAY_ID} .baron-sso-panel {
width: min(480px, 100%);
padding: 32px 28px;
border-radius: 20px;
background: #ffffff;
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.18);
color: #111827;
text-align: center;
}
#${OVERLAY_ID} .baron-sso-title {
margin: 0;
font-size: 28px;
font-weight: 700;
line-height: 1.3;
}
#${OVERLAY_ID} .baron-sso-description {
margin: 12px 0 0;
font-size: 16px;
line-height: 1.6;
color: #4b5563;
}
#${OVERLAY_ID} .baron-sso-actions {
display: flex;
gap: 12px;
justify-content: center;
margin-top: 24px;
flex-wrap: wrap;
}
#${OVERLAY_ID} button {
min-width: 160px;
padding: 12px 18px;
border: 0;
border-radius: 999px;
background: #101828;
color: #ffffff;
font-size: 15px;
font-weight: 600;
cursor: pointer;
}
#${OVERLAY_ID} button.baron-sso-secondary {
background: #e5e7eb;
color: #111827;
}
`;
document.head.appendChild(style);
}
function waitForBody() {
if (document.body) {
return Promise.resolve();
}
return new Promise((resolve) => {
document.addEventListener("DOMContentLoaded", resolve, { once: true });
});
}
function setPending(isPending) {
document.documentElement.toggleAttribute("data-baron-auth-pending", isPending);
}
function ensureOverlay() {
let overlay = document.getElementById(OVERLAY_ID);
if (!overlay) {
overlay = document.createElement("div");
overlay.id = OVERLAY_ID;
overlay.innerHTML = `
<div class="baron-sso-panel">
<h1 class="baron-sso-title"></h1>
<p class="baron-sso-description"></p>
<div class="baron-sso-actions"></div>
</div>
`;
document.body.appendChild(overlay);
}
return overlay;
}
function renderOverlay(config, options) {
installStyles();
const overlay = ensureOverlay();
const title = overlay.querySelector(".baron-sso-title");
const description = overlay.querySelector(".baron-sso-description");
const actions = overlay.querySelector(".baron-sso-actions");
title.textContent = options.title;
description.textContent = options.description;
actions.innerHTML = "";
(options.actions || []).forEach((action) => {
const button = document.createElement("button");
button.type = "button";
button.textContent = action.label;
if (action.secondary) {
button.className = "baron-sso-secondary";
}
button.addEventListener("click", action.onClick);
actions.appendChild(button);
});
overlay.classList.add("is-visible");
setPending(true);
}
function hideOverlay() {
const overlay = document.getElementById(OVERLAY_ID);
if (overlay) {
overlay.classList.remove("is-visible");
}
setPending(false);
}
function isConfigured(config) {
return Boolean(config.clientId && config.authorizeUrl && config.tokenUrl);
}
function isPublicPath(config) {
const currentPath = normalizePath(window.location.pathname);
return config.publicPaths.some((entry) => matchesPublicPath(currentPath, entry));
}
function buildRedirectUri(config) {
return config.redirectUri || `${window.location.origin}${window.location.pathname}`;
}
function getCallbackPaths(config) {
const configuredPath = normalizePath(new URL(buildRedirectUri(config)).pathname);
const rootPrefix = getRootPrefix();
const candidatePaths = [
configuredPath,
`${rootPrefix}/callback.html`,
`${rootPrefix}/callback/index.html`,
];
return [...new Set(candidatePaths)];
}
function buildReturnToUrl() {
const currentUrl = new URL(window.location.href);
currentUrl.search = "";
currentUrl.hash = "";
return currentUrl.toString();
}
function isCallbackPath(config) {
return getCallbackPaths(config).includes(normalizePath(window.location.pathname));
}
function getCryptoObject() {
return window.crypto || window.msCrypto || null;
}
function fillRandomValues(bytes) {
const cryptoObject = getCryptoObject();
if (cryptoObject?.getRandomValues) {
cryptoObject.getRandomValues(bytes);
return bytes;
}
for (let index = 0; index < bytes.length; index += 1) {
bytes[index] = Math.floor(Math.random() * 256);
}
return bytes;
}
function rightRotate(value, amount) {
return (value >>> amount) | (value << (32 - amount));
}
function sha256Fallback(message) {
const constants = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
const initialHash = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
const bytes = new TextEncoder().encode(message);
const bitLength = bytes.length * 8;
const paddedLength = (((bytes.length + 9 + 63) >> 6) << 6);
const padded = new Uint8Array(paddedLength);
padded.set(bytes);
padded[bytes.length] = 0x80;
const bitLengthHigh = Math.floor(bitLength / 0x100000000);
const bitLengthLow = bitLength >>> 0;
padded[padded.length - 8] = (bitLengthHigh >>> 24) & 0xff;
padded[padded.length - 7] = (bitLengthHigh >>> 16) & 0xff;
padded[padded.length - 6] = (bitLengthHigh >>> 8) & 0xff;
padded[padded.length - 5] = bitLengthHigh & 0xff;
padded[padded.length - 4] = (bitLengthLow >>> 24) & 0xff;
padded[padded.length - 3] = (bitLengthLow >>> 16) & 0xff;
padded[padded.length - 2] = (bitLengthLow >>> 8) & 0xff;
padded[padded.length - 1] = bitLengthLow & 0xff;
const words = new Uint32Array(64);
const hash = initialHash.slice();
for (let offset = 0; offset < padded.length; offset += 64) {
for (let index = 0; index < 16; index += 1) {
const position = offset + index * 4;
words[index] = (
(padded[position] << 24) |
(padded[position + 1] << 16) |
(padded[position + 2] << 8) |
padded[position + 3]
) >>> 0;
}
for (let index = 16; index < 64; index += 1) {
const sigma0 = rightRotate(words[index - 15], 7) ^ rightRotate(words[index - 15], 18) ^ (words[index - 15] >>> 3);
const sigma1 = rightRotate(words[index - 2], 17) ^ rightRotate(words[index - 2], 19) ^ (words[index - 2] >>> 10);
words[index] = (words[index - 16] + sigma0 + words[index - 7] + sigma1) >>> 0;
}
let a = hash[0];
let b = hash[1];
let c = hash[2];
let d = hash[3];
let e = hash[4];
let f = hash[5];
let g = hash[6];
let h = hash[7];
for (let index = 0; index < 64; index += 1) {
const sigma1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
const choice = (e & f) ^ (~e & g);
const temp1 = (h + sigma1 + choice + constants[index] + words[index]) >>> 0;
const sigma0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
const majority = (a & b) ^ (a & c) ^ (b & c);
const temp2 = (sigma0 + majority) >>> 0;
h = g;
g = f;
f = e;
e = (d + temp1) >>> 0;
d = c;
c = b;
b = a;
a = (temp1 + temp2) >>> 0;
}
hash[0] = (hash[0] + a) >>> 0;
hash[1] = (hash[1] + b) >>> 0;
hash[2] = (hash[2] + c) >>> 0;
hash[3] = (hash[3] + d) >>> 0;
hash[4] = (hash[4] + e) >>> 0;
hash[5] = (hash[5] + f) >>> 0;
hash[6] = (hash[6] + g) >>> 0;
hash[7] = (hash[7] + h) >>> 0;
}
const digest = new Uint8Array(32);
hash.forEach((value, index) => {
const position = index * 4;
digest[position] = (value >>> 24) & 0xff;
digest[position + 1] = (value >>> 16) & 0xff;
digest[position + 2] = (value >>> 8) & 0xff;
digest[position + 3] = value & 0xff;
});
return digest;
}
function createRandomString(length = 64) {
const bytes = new Uint8Array(length);
fillRandomValues(bytes);
return base64UrlEncode(bytes).slice(0, length);
}
function base64UrlEncode(input) {
const bytes = input instanceof Uint8Array ? input : new TextEncoder().encode(String(input));
let binary = "";
bytes.forEach((byte) => {
binary += String.fromCharCode(byte);
});
return window.btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
}
async function createCodeChallenge(verifier) {
const cryptoObject = getCryptoObject();
if (cryptoObject?.subtle?.digest) {
const digest = await cryptoObject.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
return base64UrlEncode(new Uint8Array(digest));
}
return base64UrlEncode(sha256Fallback(verifier));
}
function savePendingLogin(state, verifier, returnTo, redirectUri) {
sessionStorage.setItem(STATE_KEY, state);
sessionStorage.setItem(VERIFIER_KEY, verifier);
sessionStorage.setItem(RETURN_TO_KEY, returnTo);
if (redirectUri) {
sessionStorage.setItem(`${PENDING_LOGIN_KEY}_redirect_uri`, redirectUri);
}
localStorage.setItem(PENDING_LOGIN_KEY, JSON.stringify({ state, verifier, returnTo, redirectUri }));
}
function clearPendingLogin() {
sessionStorage.removeItem(STATE_KEY);
sessionStorage.removeItem(VERIFIER_KEY);
sessionStorage.removeItem(RETURN_TO_KEY);
sessionStorage.removeItem(`${PENDING_LOGIN_KEY}_redirect_uri`);
localStorage.removeItem(PENDING_LOGIN_KEY);
}
function loadPendingLogin() {
const state = sessionStorage.getItem(STATE_KEY);
const verifier = sessionStorage.getItem(VERIFIER_KEY);
const returnTo = sessionStorage.getItem(RETURN_TO_KEY);
const redirectUri = sessionStorage.getItem(`${PENDING_LOGIN_KEY}_redirect_uri`);
if (state && verifier) {
return {
state,
verifier,
returnTo,
redirectUri,
};
}
const raw = localStorage.getItem(PENDING_LOGIN_KEY);
if (!raw) {
return null;
}
try {
const pendingLogin = JSON.parse(raw);
if (!pendingLogin?.state || !pendingLogin?.verifier) {
localStorage.removeItem(PENDING_LOGIN_KEY);
return null;
}
sessionStorage.setItem(STATE_KEY, pendingLogin.state);
sessionStorage.setItem(VERIFIER_KEY, pendingLogin.verifier);
if (pendingLogin.returnTo) {
sessionStorage.setItem(RETURN_TO_KEY, pendingLogin.returnTo);
}
if (pendingLogin.redirectUri) {
sessionStorage.setItem(`${PENDING_LOGIN_KEY}_redirect_uri`, pendingLogin.redirectUri);
}
return pendingLogin;
} catch {
localStorage.removeItem(PENDING_LOGIN_KEY);
return null;
}
}
function markForceLogin() {
sessionStorage.setItem(FORCE_LOGIN_KEY, "1");
}
function consumeForceLogin() {
const shouldForce = sessionStorage.getItem(FORCE_LOGIN_KEY) === "1";
sessionStorage.removeItem(FORCE_LOGIN_KEY);
return shouldForce;
}
function saveSession(session) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(session));
}
function loadSession() {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) {
return null;
}
try {
return JSON.parse(raw);
} catch {
localStorage.removeItem(STORAGE_KEY);
return null;
}
}
function clearSession() {
localStorage.removeItem(STORAGE_KEY);
}
function isExpired(session) {
if (!session || !session.expiresAt) {
return true;
}
return Date.now() >= Number(session.expiresAt);
}
function decodeJwt(token) {
if (!token || token.split(".").length < 2) {
return null;
}
try {
const payload = token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
const normalized = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), "=");
return JSON.parse(window.atob(normalized));
} catch {
return null;
}
}
async function fetchUserInfo(config, accessToken) {
if (!config.userInfoUrl || !accessToken) {
return null;
}
const response = await fetch(config.userInfoUrl, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
if (!response.ok) {
throw new Error(`User info request failed: ${response.status}`);
}
return response.json();
}
async function resolveProfile(config, tokenResult) {
const idTokenClaims = decodeJwt(tokenResult.id_token);
const accessTokenClaims = decodeJwt(tokenResult.access_token);
if (idTokenClaims) {
return idTokenClaims;
}
if (accessTokenClaims) {
return accessTokenClaims;
}
return fetchUserInfo(config, tokenResult.access_token);
}
function readProfileEmail(profile) {
return (
profile?.email ||
profile?.preferred_username ||
profile?.upn ||
profile?.unique_name ||
""
)
.trim()
.toLowerCase();
}
function hasRequiredGroup(config, profile) {
if (!config.requiredGroups.length) {
return true;
}
const groupValue = profile?.[config.groupClaim];
const groups = Array.isArray(groupValue) ? groupValue : typeof groupValue === "string" ? [groupValue] : [];
return config.requiredGroups.some((group) => groups.includes(group));
}
function isAuthorized(config, profile) {
if (!profile) {
return false;
}
const email = readProfileEmail(profile);
if (config.allowedEmails.length && !config.allowedEmails.includes(email)) {
return false;
}
if (config.allowedEmailDomains.length) {
const hasDomain = config.allowedEmailDomains.some((domain) => email.endsWith(`@${domain.toLowerCase()}`));
if (!hasDomain) {
return false;
}
}
return hasRequiredGroup(config, profile);
}
async function exchangeCode(config, code, verifier) {
const pendingLogin = loadPendingLogin();
const redirectUri = pendingLogin?.redirectUri || buildRedirectUri(config);
const body = new URLSearchParams({
grant_type: "authorization_code",
client_id: config.clientId,
code,
code_verifier: verifier,
redirect_uri: redirectUri,
});
Object.entries(config.extraTokenParams).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
body.set(key, String(value));
}
});
const response = await fetch(config.tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body,
});
if (!response.ok) {
let errorDetail = "";
try {
const responseText = await response.text();
if (responseText) {
errorDetail = responseText;
}
} catch {
errorDetail = "";
}
const errorMessage = errorDetail
? `Token request failed: ${response.status} ${errorDetail}`
: `Token request failed: ${response.status}`;
throw new Error(errorMessage);
}
return response.json();
}
async function handleCallback(config) {
const currentUrl = new URL(window.location.href);
const code = currentUrl.searchParams.get("code");
const state = currentUrl.searchParams.get("state");
if (!code || !state) {
return false;
}
const pendingLogin = loadPendingLogin();
const expectedState = pendingLogin?.state || null;
const verifier = pendingLogin?.verifier || null;
const returnTo = pendingLogin?.returnTo || buildRedirectUri(config);
if (!expectedState || !verifier || expectedState !== state) {
clearPendingLogin();
throw new Error("Invalid OAuth state");
}
const tokenResult = await exchangeCode(config, code, verifier);
const profile = await resolveProfile(config, tokenResult);
if (!isAuthorized(config, profile)) {
clearPendingLogin();
clearSession();
throw new Error("User is not authorized");
}
const expiresIn = Number(tokenResult.expires_in || 3600);
saveSession({
accessToken: tokenResult.access_token,
idToken: tokenResult.id_token || "",
tokenType: tokenResult.token_type || "Bearer",
profile,
expiresAt: Date.now() + Math.max(expiresIn - 30, 30) * 1000,
});
clearPendingLogin();
return {
handled: true,
returnTo,
};
}
async function startLogin(config) {
const verifier = createRandomString(96);
const state = createRandomString(48);
const challenge = await createCodeChallenge(verifier);
const returnTo = buildReturnToUrl();
const redirectUri = buildRedirectUri(config);
const shouldForceLogin = consumeForceLogin();
savePendingLogin(state, verifier, returnTo, redirectUri);
const authorizeUrl = new URL(config.authorizeUrl);
authorizeUrl.searchParams.set("response_type", "code");
authorizeUrl.searchParams.set("client_id", config.clientId);
authorizeUrl.searchParams.set("redirect_uri", redirectUri);
authorizeUrl.searchParams.set("scope", config.scope);
authorizeUrl.searchParams.set("state", state);
authorizeUrl.searchParams.set("code_challenge", challenge);
authorizeUrl.searchParams.set("code_challenge_method", "S256");
Object.entries(config.extraAuthorizeParams).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
authorizeUrl.searchParams.set(key, String(value));
}
});
if (shouldForceLogin && !authorizeUrl.searchParams.has("prompt")) {
authorizeUrl.searchParams.set("prompt", "login");
}
renderOverlay(config, {
title: getMessage(config, "redirectTitle"),
description: getMessage(config, "redirectDescription"),
actions: [
{
label: getMessage(config, "redirectAction"),
onClick: () => {
window.location.assign(authorizeUrl.toString());
},
},
],
});
window.location.assign(authorizeUrl.toString());
}
async function restoreSession(config) {
const session = loadSession();
if (!session || isExpired(session)) {
clearSession();
return null;
}
if (!isAuthorized(config, session.profile)) {
clearSession();
return null;
}
return session;
}
function attachLogout(config) {
window.baronSsoLogout = () => {
const session = loadSession();
const idTokenHint = session?.idToken;
const localRedirectTarget = config.postLogoutRedirectUri || `${window.location.origin}/`;
const shouldUseLocalOnlyLogout = isLocalEnvironment() || !config.logoutUrl || !idTokenHint;
clearPendingLogin();
clearSession();
window.BARON_SSO_SESSION = null;
if (shouldUseLocalOnlyLogout) {
markForceLogin();
window.location.assign(localRedirectTarget);
return;
}
const logoutUrl = new URL(config.logoutUrl);
logoutUrl.searchParams.set("id_token_hint", idTokenHint);
logoutUrl.searchParams.set("client_id", config.clientId);
if (config.postLogoutRedirectUri) {
logoutUrl.searchParams.set("post_logout_redirect_uri", config.postLogoutRedirectUri);
}
window.location.assign(logoutUrl.toString());
};
}
export async function ensureBaronSsoAuth(overrides = {}) {
const currentUrl = new URL(window.location.href);
const hasCallbackParams = currentUrl.searchParams.has("code") || currentUrl.searchParams.has("state");
const workerAuth = getWorkerManagedAuth();
const workerPublicPaths = normalizeArray(overrides.publicPaths);
const workerPublicPath = workerPublicPaths.some((entry) => matchesPublicPath(window.location.pathname, entry));
const workerForceAuth = Boolean(overrides.forceAuth);
if (workerAuth) {
attachWorkerManagedLogout(workerAuth);
if (workerAuth.authenticated) {
const session = {
workerManaged: true,
profile: workerAuth.profile || {},
expiresAt: Number.MAX_SAFE_INTEGER,
};
window.BARON_SSO_SESSION = session;
hideOverlay();
return { status: "worker-authenticated", session };
}
if (workerForceAuth || !workerPublicPath) {
window.location.assign(workerAuth.loginUrl || "/auth/login");
return { status: "worker-redirecting" };
}
return { status: "public" };
}
const config = await mergeConfig(overrides);
const publicPath = isPublicPath(config);
if (!config.enabled) {
return { status: "disabled" };
}
attachLogout(config);
if (publicPath && !config.forceAuth) {
const session = await restoreSession(config);
if (session) {
window.BARON_SSO_SESSION = session;
hideOverlay();
return { status: "authenticated-public", session };
}
return { status: "public" };
}
await waitForBody();
renderOverlay(config, {
title: getMessage(config, "loadingTitle"),
description: getMessage(config, "loadingDescription"),
});
if (!isConfigured(config)) {
renderOverlay(config, {
title: getMessage(config, "configTitle"),
description: getMessage(config, "configDescription"),
});
return { status: "misconfigured" };
}
try {
if (isCallbackPath(config) && !hasCallbackParams) {
window.location.replace(config.postLogoutRedirectUri || `${window.location.origin}/`);
return { status: "callback-idle" };
}
const callbackResult = await handleCallback(config);
if (callbackResult?.handled) {
const destination = callbackResult.returnTo || `${window.location.origin}/`;
if (destination !== window.location.href) {
window.location.replace(destination);
return { status: "redirected", returnTo: destination };
}
}
const session = await restoreSession(config);
if (session) {
window.BARON_SSO_SESSION = session;
hideOverlay();
return { status: "authenticated", session };
}
await startLogin(config);
return { status: "redirecting" };
} catch (error) {
console.error("BARON SSO authentication failed.", error);
const isUnauthorized = error.message === "User is not authorized";
const detailDescription = isUnauthorized
? getMessage(config, "unauthorizedDescription")
: isLocalEnvironment()
? `${getMessage(config, "exchangeDescription")} (${error.message})`
: getMessage(config, "exchangeDescription");
clearSession();
clearPendingLogin();
renderOverlay(config, {
title: getMessage(config, "unauthorizedTitle"),
description: detailDescription,
actions: [
{
label: getMessage(config, "retryAction"),
onClick: () => {
startLogin(config);
},
},
{
label: getMessage(config, "logoutAction"),
secondary: true,
onClick: () => {
window.baronSsoLogout();
},
},
],
});
return { status: "failed", error };
}
}
@@ -0,0 +1,56 @@
const currentUrl = new URL(window.location.href);
const appPathSegments = currentUrl.pathname.split("/").filter(Boolean);
const rootMarkerSegments = ["ko", "en", "callback", "assets", "protected", "public", "recruit"];
const rootPrefix = appPathSegments.length && !rootMarkerSegments.includes(appPathSegments[0])
? `/${appPathSegments[0]}`
: "";
const appOrigin = currentUrl.origin;
const productionClientHosts = new Set(["baroncs.co.kr", "www.baroncs.co.kr", "test.baroncs.co.kr"]);
const fixedProductionRedirectHosts = new Set(["baroncs.co.kr", "www.baroncs.co.kr"]);
const usesProductionClient = productionClientHosts.has(currentUrl.hostname);
const usesFixedProductionRedirect = fixedProductionRedirectHosts.has(currentUrl.hostname);
const defaultRedirectUri = usesFixedProductionRedirect
? "https://baroncs.co.kr/callback.html"
: `${appOrigin}${rootPrefix}/callback.html`;
const defaultPostLogoutRedirectUri = usesFixedProductionRedirect
? "https://baroncs.co.kr/"
: `${appOrigin}${rootPrefix}/`;
const productionConfig = {
clientId: "b5821f81-da7a-4778-ba97-7742e4ff4c39",
authorizeUrl: "https://app.brsw.kr/oidc/oauth2/auth",
tokenUrl: "https://app.brsw.kr/oidc/oauth2/token",
logoutUrl: "https://app.brsw.kr/oidc/oauth2/sessions/logout",
userInfoUrl: "https://app.brsw.kr/oidc/userinfo",
scope: "openid",
};
const stagingConfig = {
clientId: "037b546a-86b7-4ff4-bdaf-c41c98fb6766",
authorizeUrl: "https://sso.hmac.kr/oidc/oauth2/auth",
tokenUrl: "https://sso.hmac.kr/oidc/oauth2/token",
logoutUrl: "https://sso.hmac.kr/oidc/oauth2/sessions/logout",
userInfoUrl: "https://sso.hmac.kr/oidc/userinfo",
scope: "openid tenants profile email",
};
const activeConfig = usesProductionClient ? productionConfig : stagingConfig;
export default {
enabled: true,
clientId: activeConfig.clientId,
authorizeUrl: activeConfig.authorizeUrl,
tokenUrl: activeConfig.tokenUrl,
logoutUrl: activeConfig.logoutUrl,
userInfoUrl: activeConfig.userInfoUrl,
scope: activeConfig.scope,
redirectUri: defaultRedirectUri,
postLogoutRedirectUri: defaultPostLogoutRedirectUri,
allowedEmailDomains: [],
allowedEmails: [],
requiredGroups: [],
groupClaim: "groups",
publicPaths: [],
extraAuthorizeParams: {},
extraTokenParams: {},
};
@@ -0,0 +1,699 @@
import { ensureBaronSsoAuth } from "./baron-sso-auth.js?v=20260721-worker2";
import "./header-login.js?v=20261030-1";
// --window-inner-height: 실제 뷰포트 높이를 CSS 변수로 반영 (모바일 100vh 이슈 대응, kngil 레이아웃에서 사용)
function updateWindowInnerHeight() {
document.documentElement.style.setProperty("--window-inner-height", window.innerHeight + "px");
}
updateWindowInnerHeight();
window.addEventListener("resize", updateWindowInnerHeight);
const pathSegments = location.pathname.split('/').filter(Boolean);
const rootMarkerSegments = ['ko', 'en', 'callback', 'assets', 'protected', 'public', 'recruit'];
const rootPrefix = pathSegments.length && !rootMarkerSegments.includes(pathSegments[0]) ? `/${pathSegments[0]}` : '';
const normalizePath = (path) => {
const normalizedPath = (path || '/').replace(/\/index\.html$/i, '/');
if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {
return normalizedPath.slice(0, -1);
}
return normalizedPath;
};
const currentPath = normalizePath(location.pathname);
const matchesPublicPath = (path, entry) => {
const normalizedEntry = normalizePath(entry);
if (normalizedEntry.endsWith('_')) {
return path.startsWith(normalizedEntry);
}
return path === normalizedEntry || path.startsWith(`${normalizedEntry}/`);
};
const landingPagePaths = [
`${rootPrefix}/ko`,
`${rootPrefix}/ko/`,
`${rootPrefix}/ko/index.html`,
`${rootPrefix}/en`,
`${rootPrefix}/en/`,
`${rootPrefix}/en/index.html`,
];
const publicPagePaths = [
...landingPagePaths,
`${rootPrefix}/ko/br_`,
`${rootPrefix}/en/br_`,
`${rootPrefix}/ko/dt_`,
`${rootPrefix}/en/dt_`,
`${rootPrefix}/recruit/`,
`${rootPrefix}/ko/sv_sw.html`,
`${rootPrefix}/en/sv_sw.html`,
`${rootPrefix}/ko/sv_sw_`,
`${rootPrefix}/en/sv_sw_`,
`${rootPrefix}/ko/egbim`,
`${rootPrefix}/en/egbim`,
`${rootPrefix}/ko/tova`,
`${rootPrefix}/en/tova`,
`${rootPrefix}/ko/pr_`,
`${rootPrefix}/en/pr_`,
`${rootPrefix}/ko/gaia`,
`${rootPrefix}/en/gaia`,
`${rootPrefix}/ko/kngil`,
`${rootPrefix}/en/kngil`,
`${rootPrefix}/ko/sv_bigroom.html`,
`${rootPrefix}/en/sv_bigroom.html`,
];
const normalizedLandingPagePaths = landingPagePaths.map(normalizePath);
const isLandingPage = normalizedLandingPagePaths.includes(currentPath);
const isPublicInfoPage = publicPagePaths.some((entry) => matchesPublicPath(currentPath, entry));
const loginRequested = isPublicInfoPage && new URL(window.location.href).searchParams.get('login') === '1';
if (isLandingPage) {
document.documentElement.dataset.baronPageMode = 'landing';
} else if (isPublicInfoPage) {
document.documentElement.dataset.baronPageMode = 'public-info';
}
const authResult = await ensureBaronSsoAuth(
isPublicInfoPage
? {
publicPaths: publicPagePaths,
forceAuth: loginRequested,
}
: {}
);
const isAuthenticated = Boolean(authResult?.session);
document.documentElement.dataset.baronAuthState = isAuthenticated ? 'authenticated' : 'anonymous';
// Disabled links in anonymous navigation must not navigate, including keyboard/programmatic clicks.
document.addEventListener('click', (event) => {
const target = event.target instanceof Element ? event.target : null;
const disabledLink = target?.closest('a[data-auth-disabled="true"]');
if (!disabledLink) return;
event.preventDefault();
event.stopImmediatePropagation();
}, true);
function followLoginTooltip(event) {
const target = event.target instanceof Element ? event.target : null;
const menuItem = target?.closest('li.nav-login-required[data-login-tooltip]');
if (!menuItem) return;
const rect = menuItem.getBoundingClientRect();
const margin = 8;
const offset = 16;
const tooltipWidth = Math.min(320, Math.max(0, window.innerWidth - margin * 2));
const tooltipHeight = 84;
let left = event.clientX + offset;
if (left + tooltipWidth > window.innerWidth - margin) {
left = event.clientX - tooltipWidth - offset;
}
left = Math.max(margin, left);
let top = event.clientY + offset;
if (top + tooltipHeight > window.innerHeight - margin) {
top = event.clientY - tooltipHeight - offset;
}
top = Math.max(margin, top);
menuItem.style.setProperty('--login-tooltip-left', `${left - rect.left}px`);
menuItem.style.setProperty('--login-tooltip-top', `${top - rect.top}px`);
}
document.addEventListener('pointermove', followLoginTooltip, { passive: true });
// ── AJAX 관련 SCRIPT
$(function () {
const includeVersion = '20261030-1';
// 비로그인 요청은 Worker가 nav-public.html만 허용하므로 로그인 상태에 맞는 메뉴 파일을 선택한다.
const navIncludeFile = isAuthenticated ? 'nav.html' : 'nav-public.html';
const includeBase = `${rootPrefix}/_include`;
$.ajaxSetup({ cache: false });
// Share both in-flight and completed static includes within this page.
const htmlRequests = new Map();
function loadHTML(url, target, callback) {
let request = htmlRequests.get(url);
if (!request) {
request = $.ajax({ url, cache: true, dataType: "html", timeout: 5000 });
htmlRequests.set(url, request);
request.fail(function () { htmlRequests.delete(url); });
}
request.done(function (data) {
$(target).html(data);
if (typeof callback === "function") callback();
}).fail(function (xhr, status, error) {
console.error(`Failed to load ${url}:`, error || status);
if (typeof callback === "function") callback();
});
}
function normalizeSiteLinks(root) {
if (!root) {
return;
}
root.querySelectorAll('a[href], img[src], source[src]').forEach((node) => {
const attributeName = node.hasAttribute('href') ? 'href' : 'src';
const rawValue = node.getAttribute(attributeName);
if (!rawValue || /^(?:https?:|mailto:|tel:|javascript:|#)/i.test(rawValue)) {
return;
}
const normalizedValue = rawValue
.replace(/^\.\.\/(ko|en|recruit)\//, `${rootPrefix}/$1/`)
.replace(/^\/(ko|en|recruit)\//, `${rootPrefix}/$1/`)
.replace(/^\.\.\/callback\.html$/, `${rootPrefix}/callback.html`)
.replace(/^\/callback\.html$/, `${rootPrefix}/callback.html`);
if (normalizedValue !== rawValue) {
node.setAttribute(attributeName, normalizedValue);
}
});
}
// nav 연결 (depth3 지원)
function connectNavToMapList() {
const currentPathname = location.pathname; // 전체 경로로 비교 (egbim/tova/gaia value.html 구분)
const navLinks = document.querySelectorAll(
"header .corp nav ol li.depth1 ul.depth2 li a"
);
let matchedLink = null;
navLinks.forEach((link) => {
const href = link.getAttribute("href"); // 절대경로 그대로 사용
if (href === currentPathname || currentPathname.endsWith(href)) {
if (matchedLink && matchedLink.closest('.depth3')) {
return;
}
if (!matchedLink || link.closest('.depth3')) {
matchedLink = link;
}
}
});
if (matchedLink) {
const mapList = document.querySelector(".map_list");
if (mapList) {
mapList.innerHTML = "";
matchedLink.closest(".depth1").classList.add("active");
const hasDepth3 = matchedLink.closest("li.has_depth3");
if (hasDepth3) {
matchedLink.closest(".depth1").querySelectorAll("li.has_depth3").forEach(el => {
el.classList.remove("active");
});
hasDepth3.classList.add("active");
}
const liHome = document.createElement("li");
liHome.innerHTML = '<i class="home"></i>';
mapList.appendChild(liHome);
const categoryTitle = matchedLink
.closest(".depth1")
?.querySelector("span")
?.textContent.trim();
const subTitle = matchedLink
.closest(".depth1")
?.querySelector("em")
?.textContent.trim();
const mainTitle = categoryTitle.replace(subTitle, "").trim();
const liCategory = document.createElement("li");
liCategory.textContent = mainTitle || "";
mapList.appendChild(liCategory);
const depth3El = matchedLink.closest('.depth3');
if (depth3El) {
const depth2Link = matchedLink.closest('.has_depth3')?.querySelector('a');
const depth2Title = depth2Link ? depth2Link.textContent.trim() : "";
const liDepth2 = document.createElement("li");
liDepth2.textContent = depth2Title;
mapList.appendChild(liDepth2);
const targetTitle = matchedLink.textContent.trim();
const liOn = document.createElement("li");
liOn.classList.add("on");
liOn.textContent = targetTitle || "";
mapList.appendChild(liOn);
} else {
const targetTitle = matchedLink.textContent.trim();
const liOn = document.createElement("li");
liOn.classList.add("on");
liOn.textContent = targetTitle || "";
mapList.appendChild(liOn);
}
}
}
}
// PC GNB 패키지 S/W / 서비스 S/W 3depth 탭 전환 기능
// 드롭다운이 여러 개(패키지 S/W, 서비스 S/W 등) 존재할 수 있으므로 각각 독립적으로 스코프 처리
function initPackageSWTab() {
$('.package_sw_dropdown').each(function () {
const $dropdown = $(this);
const $menuItems = $dropdown.find('> li.has_depth3');
if (!$menuItems.length) return;
// 1depth 메뉴 호버 시 드롭다운이 열리면 첫 번째 항목 기본 활성화
$dropdown.closest('.depth1').on('mouseenter', function () {
if (!$menuItems.filter('.active').length) {
$menuItems.removeClass('active');
$menuItems.first().addClass('active');
}
});
// 2depth 메뉴 호버 시 active 클래스 전환
$menuItems.on('mouseenter', function () {
$menuItems.removeClass('active');
$(this).addClass('active');
});
});
}
function trimPopupDepth3Nav() {
const popupNav = document.querySelector('.popup_wrap.sitemap .popup_contents_wrap nav');
if (!popupNav) {
return;
}
popupNav.querySelectorAll('li.has_depth3 > .depth3').forEach((depth3) => {
depth3.remove();
});
}
function configureHeaderActions(root) {
if (!root) {
return;
}
const loginWrap = root.querySelector('.header_login');
const loginLink = root.querySelector('.header_login_link');
if (!loginWrap || !loginLink) {
return;
}
if (isAuthenticated) {
loginWrap.hidden = false;
loginLink.textContent = '로그아웃';
loginLink.href = '#';
loginLink.onclick = (event) => {
event.preventDefault();
window.baronSsoLogout?.();
};
return;
}
if (!isPublicInfoPage) {
loginWrap.hidden = true;
return;
}
loginWrap.hidden = false;
loginLink.textContent = '로그인';
loginLink.href = `${location.pathname}?login=1`;
loginLink.onclick = null;
}
function loadSitemapNav() {
if (!$('.container').hasClass('recruit')) {
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '.popup_wrap.sitemap .popup_contents_wrap nav', function () {
normalizeSiteLinks(document.querySelector('.popup_wrap.sitemap .popup_contents_wrap nav'));
mobileMenu();
});
} else {
loadHTML(`${includeBase}/nav_recruit.html?v=${includeVersion}`, '.popup_wrap.sitemap .popup_contents_wrap nav', function () {
mobileMenu();
});
}
}
if (!$('.container').hasClass('recruit')) {
loadHTML(`${includeBase}/header.html?v=${includeVersion}`, '#header', function () {
normalizeSiteLinks(document.querySelector('#header'));
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '#header .corp .nav', function () {
normalizeSiteLinks(document.querySelector('#header .corp .nav'));
connectNavToMapList();
initPackageSWTab();
});
configureHeaderActions(document.querySelector('#header'));
loadSitemapNav();
});
} else {
loadHTML(`${includeBase}/header_recruit.html?v=${includeVersion}`, "#header_recruit", function () {
loadSitemapNav();
if (!$(".container").hasClass("recruit")) {
} else {
const currentPath = location.pathname.split("/").pop();
const navLinks = document.querySelectorAll(
"header .recruit nav ol li.depth1 a"
);
navLinks.forEach((link) => {
const href = link.getAttribute("href").split("/").pop();
if (href === currentPath) {
link.closest(".depth1").classList.add("active");
}
});
}
});
}
loadHTML(`${includeBase}/footer.html?v=${includeVersion}`, '#footer', function () {
normalizeSiteLinks(document.querySelector('#footer'));
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '#footer .nav', function () {
normalizeSiteLinks(document.querySelector('#footer .nav'));
});
});
});
// ── TITLE 관련 SCRIPT
// ── S: title 다일링기
$(function () {
const currentPath = location.pathname;
if (currentPath.indexOf("/recruit/") > 0) {
document.querySelector("head title").textContent =
"인재채용 | (주)바론컨설턴트";
} else {
document.querySelector("head title").textContent = "(주)바론컨설턴트";
}
});
// E : title 다일링기
// ── S : header 숨기기
$(function () {
const showNav = gsap
.from("#header, #header_recruit", {
yPercent: -200,
paused: true,
duration: 0.2,
onStart: () => {
const stHeader = document.querySelector("header");
if (window.scrollY > 100) {
stHeader.classList.add("open");
}
},
onReverseComplete: () => {
const stHeader = document.querySelector("header");
stHeader.classList.remove("open");
},
})
.progress(1);
let showMapList = null;
const headerNavMM = gsap.matchMedia();
headerNavMM.add("(max-width: 1400px)", () => {
showMapList = gsap
.from(".map_list", {
y: -40,
autoAlpha: 0,
paused: true,
duration: 0.2,
})
.progress(1);
return () => {
showMapList = null;
};
});
ScrollTrigger.create({
start: "top top",
end: 99999,
onUpdate: (self) => {
if (lenis && lenis.isStopped) return;
if (self.direction === -1) {
showNav.play();
showMapList && showMapList.play();
} else {
showNav.reverse();
showMapList && showMapList.reverse();
}
},
});
});
// E : header 숨기기
// ── POPUP SCRIPT
// ── S : 팝업 열기
$(function () {
$(document).on("click", "button", function () {
const button = this;
const value = button.getAttribute("data-value");
const popup = document.querySelector(`.popup_wrap.${value}`);
if (popup) {
// 현재 스크롤 위치 저장
const scrollY = window.scrollY;
document.body.setAttribute("data-scroll-lock", scrollY);
// body 고정
document.body.style.position = "fixed";
document.body.style.top = `-${scrollY}px`;
document.body.style.left = "0";
document.body.style.right = "0";
// Lenis 일시 중단
if (lenis) {
lenis.destroy();
}
// 팝업 표시
popup.style.display = "block";
// 팝업 내부 스크롤 강제 활성화
const popupContents = popup.querySelector(".popup_contents_wrap");
if (popupContents) {
popupContents.removeAttribute("data-lenis-prevent-wheel");
popupContents.removeAttribute("data-lenis-prevent");
popupContents.style.cssText = `
overflow-y: auto !important;
-webkit-overflow-scrolling: touch !important;
overscroll-behavior: contain !important;
height: 100vh !important;
touch-action: pan-y !important;
`;
// 터치 이벤트 강제 허용
popupContents.addEventListener(
"touchstart",
function (e) {
e.stopPropagation();
},
{ passive: true }
);
popupContents.addEventListener(
"touchmove",
function (e) {
e.stopPropagation();
},
{ passive: true }
);
}
}
});
});
// E : 팝업 열기
// ── S : 팝업 닫기
$(function () {
$(document).on("click", function (e) {
const $target = $(e.target);
// 닫기 버튼 클릭 시
if ($target.closest(".btn_close").length) {
const $popupWrap = $target.closest(".popup_wrap");
if ($popupWrap.length) {
$popupWrap.hide();
enableBodyScroll();
}
return;
}
// popup_contents_wrap 클릭 자체가 배경 클릭인 경우 닫기
const $popupContents = $target.closest(".popup_contents_wrap");
if ($popupContents.length && $target.is(".popup_contents_wrap")) {
const $popupWrap = $target.closest(".popup_wrap");
if ($popupWrap.length) {
$popupWrap.hide();
enableBodyScroll();
}
}
});
// 팝업 닫힐 때 body 스크롤 복원
function enableBodyScroll() {
// body 스크롤 위치 복원
const scrollY = document.body.style.top;
document.body.style.position = "";
document.body.style.top = "";
document.body.style.width = "";
window.scrollTo({
top: parseInt(scrollY || "0") * -1,
left: 0,
behavior: "instant", // 또는 'auto'
});
// Lenis 재시작
handleStartLenis();
}
});
// E : 팝업 닫기
// ── S : map list 움직이기 /* 250604추가 */
let lastScrollY = window.scrollY;
const stickyBox = document.querySelector(".map_list");
window.addEventListener("scroll", () => {
const currentScrollY = window.scrollY;
const width = window.innerWidth;
if (width > 1440) {
//250604 추가
if (stickyBox != null) {
if (currentScrollY > lastScrollY) {
// 아래로 스크롤할 때
stickyBox.style.top = "24px";
} else {
// 위로 스크롤할 때
stickyBox.style.top = "124px";
}
}
} else {
if (stickyBox != null) {
if (currentScrollY > lastScrollY) {
// 아래로 스크롤할 때
stickyBox.style.top = "24px";
} else {
// 위로 스크롤할 때
stickyBox.style.top = "74px";
}
}
}
lastScrollY = currentScrollY;
});
// E : map list 움직이기
// ── SCROLL SCRIPT
// ── AOS
if (window.AOS && typeof window.AOS.init === "function") {
window.AOS.init();
}
// ── Lenis
let lenis;
let lenisTickerCallback;
function handleStartLenis() {
if (lenisTickerCallback) {
gsap.ticker.remove(lenisTickerCallback);
lenisTickerCallback = null;
}
if (lenis) {
lenis.destroy();
lenis = null;
}
if (typeof window.Lenis !== "function") {
return;
}
lenis = new Lenis({
lerp: 0.1, // 스크롤의 부드러운 정도
smoothWheel: true,
smoothTouch: false,
});
lenis.on('scroll', () => {
if (window.ScrollTrigger) {
window.ScrollTrigger.update();
}
});
lenisTickerCallback = (time) => {
if (!lenis || typeof lenis.raf !== "function") return;
lenis.raf(time * 1000);
};
gsap.ticker.add(lenisTickerCallback);
gsap.ticker.lagSmoothing(0);
}
handleStartLenis();
export default lenis;
// ── 로드 UI fallback 해제
function releaseLoadingUI() {
document.documentElement.classList.remove("loading");
document.body.classList.remove("loading", "is-loading");
[".loading", ".loading_wrap", ".loader", ".preloader"].forEach((selector) => {
document.querySelectorAll(selector).forEach((el) => {
el.style.display = "none";
el.setAttribute("aria-hidden", "true");
});
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", releaseLoadingUI, { once: true });
} else {
releaseLoadingUI();
}
window.addEventListener("load", releaseLoadingUI, { once: true });
window.setTimeout(releaseLoadingUI, 5000);
window.addEventListener("pageshow", releaseLoadingUI);
window.addEventListener("error", releaseLoadingUI);
window.addEventListener("unhandledrejection", releaseLoadingUI);
function mobileMenu() {
const mNav = document.querySelectorAll(".sitemap ol > li.depth1");
mNav.forEach((item) => {
const trigger = item.querySelector(":scope > span");
const depth2 = item.querySelector(":scope > .depth2");
if (!trigger || !depth2 || trigger.dataset.mobileMenuBound === "true") {
return;
}
trigger.dataset.mobileMenuBound = "true";
trigger.addEventListener("click", function () {
if (window.innerWidth > 1440) {
return;
}
const isActive = item.classList.contains("active");
// 다른 모든 활성화된 메뉴 닫기 (아코디언 동작)
mNav.forEach((otherItem) => {
if (otherItem !== item) {
otherItem.classList.remove("active");
const otherDepth2 = otherItem.querySelector(":scope > .depth2");
if (otherDepth2) {
otherDepth2.style.maxHeight = null;
}
}
});
if (!isActive) {
item.classList.add("active");
depth2.style.maxHeight = depth2.scrollHeight + "px";
} else {
item.classList.remove("active");
depth2.style.maxHeight = null;
}
});
});
}
@@ -0,0 +1,627 @@
import { ensureBaronSsoAuth } from "../baron-sso-auth.js?v=20260721-worker2";
import "../header-login.js?v=20261030-1";
const pathSegments = location.pathname.split('/').filter(Boolean);
const rootMarkerSegments = ['ko', 'en', 'callback', 'assets', 'protected', 'public', 'recruit'];
const rootPrefix = pathSegments.length && !rootMarkerSegments.includes(pathSegments[0]) ? `/${pathSegments[0]}` : '';
const normalizePath = (path) => {
const normalizedPath = (path || '/').replace(/\/index\.html$/i, '/');
if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {
return normalizedPath.slice(0, -1);
}
return normalizedPath;
};
const currentPath = normalizePath(location.pathname);
const matchesPublicPath = (path, entry) => {
const normalizedEntry = normalizePath(entry);
if (normalizedEntry.endsWith('_')) {
return path.startsWith(normalizedEntry);
}
return path === normalizedEntry || path.startsWith(`${normalizedEntry}/`);
};
const landingPagePaths = [
`${rootPrefix}/ko`,
`${rootPrefix}/ko/`,
`${rootPrefix}/ko/index.html`,
`${rootPrefix}/en`,
`${rootPrefix}/en/`,
`${rootPrefix}/en/index.html`,
];
const publicPagePaths = [
...landingPagePaths,
`${rootPrefix}/ko/br_`,
`${rootPrefix}/en/br_`,
`${rootPrefix}/ko/dt_`,
`${rootPrefix}/en/dt_`,
`${rootPrefix}/recruit/`,
`${rootPrefix}/ko/sv_sw.html`,
`${rootPrefix}/en/sv_sw.html`,
`${rootPrefix}/ko/sv_sw_`,
`${rootPrefix}/en/sv_sw_`,
`${rootPrefix}/ko/egbim`,
`${rootPrefix}/en/egbim`,
`${rootPrefix}/ko/tova`,
`${rootPrefix}/en/tova`,
`${rootPrefix}/ko/pr_`,
`${rootPrefix}/en/pr_`,
`${rootPrefix}/ko/gaia`,
`${rootPrefix}/en/gaia`,
`${rootPrefix}/ko/kngil`,
`${rootPrefix}/en/kngil`,
`${rootPrefix}/ko/sv_bigroom.html`,
`${rootPrefix}/en/sv_bigroom.html`,
];
const normalizedLandingPagePaths = landingPagePaths.map(normalizePath);
const isLandingPage = normalizedLandingPagePaths.includes(currentPath);
const isPublicInfoPage = publicPagePaths.some((entry) => matchesPublicPath(currentPath, entry));
const loginRequested = isPublicInfoPage && new URL(window.location.href).searchParams.get('login') === '1';
if (isLandingPage) {
document.documentElement.dataset.baronPageMode = 'landing';
} else if (isPublicInfoPage) {
document.documentElement.dataset.baronPageMode = 'public-info';
}
const authResult = await ensureBaronSsoAuth(
isPublicInfoPage
? {
publicPaths: publicPagePaths,
forceAuth: loginRequested,
}
: {}
);
const isAuthenticated = Boolean(authResult?.session);
document.documentElement.dataset.baronAuthState = isAuthenticated ? 'authenticated' : 'anonymous';
// Disabled links in anonymous navigation must not navigate, including keyboard/programmatic clicks.
document.addEventListener('click', (event) => {
const target = event.target instanceof Element ? event.target : null;
const disabledLink = target?.closest('a[data-auth-disabled="true"]');
if (!disabledLink) return;
event.preventDefault();
event.stopImmediatePropagation();
}, true);
function followLoginTooltip(event) {
const target = event.target instanceof Element ? event.target : null;
const menuItem = target?.closest('li.nav-login-required[data-login-tooltip]');
if (!menuItem) return;
const rect = menuItem.getBoundingClientRect();
const margin = 8;
const offset = 16;
const tooltipWidth = Math.min(320, Math.max(0, window.innerWidth - margin * 2));
const tooltipHeight = 84;
let left = event.clientX + offset;
if (left + tooltipWidth > window.innerWidth - margin) {
left = event.clientX - tooltipWidth - offset;
}
left = Math.max(margin, left);
let top = event.clientY + offset;
if (top + tooltipHeight > window.innerHeight - margin) {
top = event.clientY - tooltipHeight - offset;
}
top = Math.max(margin, top);
menuItem.style.setProperty('--login-tooltip-left', `${left - rect.left}px`);
menuItem.style.setProperty('--login-tooltip-top', `${top - rect.top}px`);
}
document.addEventListener('pointermove', followLoginTooltip, { passive: true });
// ?�� AJAX 관??SCRIPT
$(function () {
const includeVersion = '20261030-1';
// 비로그인 요청은 Worker가 nav-public.html만 허용하므로 로그인 상태에 맞는 메뉴 파일을 선택한다.
const navIncludeFile = isAuthenticated ? 'nav.html' : 'nav-public.html';
const includeBase = `${rootPrefix}/_include/eng`;
$.ajaxSetup({ cache: false });
// Share both in-flight and completed static includes within this page.
const htmlRequests = new Map();
function loadHTML(url, target, callback) {
let request = htmlRequests.get(url);
if (!request) {
request = $.ajax({ url, cache: true, dataType: "html", timeout: 5000 });
htmlRequests.set(url, request);
request.fail(function () { htmlRequests.delete(url); });
}
request.done(function (data) {
$(target).html(data);
if (typeof callback === "function") callback();
}).fail(function (xhr, status, error) {
console.error(`Failed to load ${url}:`, error || status);
if (typeof callback === "function") callback();
});
}
function normalizeSiteLinks(root) {
if (!root) {
return;
}
root.querySelectorAll('a[href], img[src], source[src]').forEach((node) => {
const attributeName = node.hasAttribute('href') ? 'href' : 'src';
const rawValue = node.getAttribute(attributeName);
if (!rawValue || /^(?:https?:|mailto:|tel:|javascript:|#)/i.test(rawValue)) {
return;
}
const normalizedValue = rawValue
.replace(/^\.\.\/(ko|en|recruit)\//, `${rootPrefix}/$1/`)
.replace(/^\/(ko|en|recruit)\//, `${rootPrefix}/$1/`)
.replace(/^\.\.\/callback\.html$/, `${rootPrefix}/callback.html`)
.replace(/^\/callback\.html$/, `${rootPrefix}/callback.html`);
if (normalizedValue !== rawValue) {
node.setAttribute(attributeName, normalizedValue);
}
});
}
// nav 연결 (depth3 지원)
function connectNavToMapList() {
const currentPathname = location.pathname; // 전체 경로로 비교
const navLinks = document.querySelectorAll(
"header .corp nav ol li.depth1 ul.depth2 li a"
);
let matchedLink = null;
navLinks.forEach((link) => {
const href = link.getAttribute("href"); // 절대경로 그대로
if (href === currentPathname || currentPathname.endsWith(href)) {
if (matchedLink && matchedLink.closest('.depth3')) {
return;
}
if (!matchedLink || link.closest('.depth3')) {
matchedLink = link;
}
}
});
if (matchedLink) {
const mapList = document.querySelector(".map_list");
if (mapList) {
mapList.innerHTML = "";
matchedLink.closest(".depth1").classList.add("active");
const hasDepth3 = matchedLink.closest("li.has_depth3");
if (hasDepth3) {
matchedLink.closest(".depth1").querySelectorAll("li.has_depth3").forEach(el => {
el.classList.remove("active");
});
hasDepth3.classList.add("active");
}
const liHome = document.createElement("li");
liHome.innerHTML = '<i class="home"></i>';
mapList.appendChild(liHome);
const categoryTitle = matchedLink
.closest(".depth1")
?.querySelector("span")
?.textContent.trim();
const subTitle = matchedLink
.closest(".depth1")
?.querySelector("em")
?.textContent.trim();
const mainTitle = categoryTitle.replace(subTitle, "").trim();
const liCategory = document.createElement("li");
liCategory.textContent = mainTitle || "";
mapList.appendChild(liCategory);
const depth3El = matchedLink.closest('.depth3');
if (depth3El) {
const depth2Link = matchedLink.closest('.has_depth3')?.querySelector('a');
const depth2Title = depth2Link ? depth2Link.textContent.trim() : "";
const liDepth2 = document.createElement("li");
liDepth2.textContent = depth2Title;
mapList.appendChild(liDepth2);
const targetTitle = matchedLink.textContent.trim();
const liOn = document.createElement("li");
liOn.classList.add("on");
liOn.textContent = targetTitle || "";
mapList.appendChild(liOn);
} else {
const targetTitle = matchedLink.textContent.trim();
const liOn = document.createElement("li");
liOn.classList.add("on");
liOn.textContent = targetTitle || "";
mapList.appendChild(liOn);
}
}
}
}
// PC GNB 패키지 S/W 3depth 탭 전환 기능
function initPackageSWTab() {
const $dropdown = $('.package_sw_dropdown');
if (!$dropdown.length) return;
const $menuItems = $dropdown.find('> li.has_depth3');
// 패키지 S/W 1depth 메뉴 호버 시 드롭다운이 열리면 첫 번째 항목 기본 활성화
$dropdown.closest('.depth1').on('mouseenter', function () {
if (!$menuItems.filter('.active').length) {
$menuItems.removeClass('active');
$menuItems.first().addClass('active');
}
});
// 2depth 메뉴 호버 시 active 클래스 전환
$menuItems.on('mouseenter', function () {
$menuItems.removeClass('active');
$(this).addClass('active');
});
}
function trimPopupDepth3Nav() {
const popupNav = document.querySelector(".popup_wrap.sitemap .popup_contents_wrap nav");
if (!popupNav) {
return;
}
popupNav.querySelectorAll("li.has_depth3 > .depth3").forEach((depth3) => {
depth3.remove();
});
}
function configureHeaderActions(root) {
if (!root) {
return;
}
const loginWrap = root.querySelector('.header_login');
const loginLink = root.querySelector('.header_login_link');
if (!loginWrap || !loginLink) {
return;
}
if (isAuthenticated) {
loginWrap.hidden = false;
loginLink.textContent = 'LOGOUT';
loginLink.href = '#';
loginLink.onclick = (event) => {
event.preventDefault();
window.baronSsoLogout?.();
};
return;
}
if (!isPublicInfoPage) {
loginWrap.hidden = true;
return;
}
loginWrap.hidden = false;
loginLink.textContent = 'LOGIN';
loginLink.href = `${location.pathname}?login=1`;
loginLink.onclick = null;
}
function loadSitemapNav() {
if (!$('.container').hasClass('recruit')) {
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '.popup_wrap.sitemap .popup_contents_wrap nav', function () {
normalizeSiteLinks(document.querySelector('.popup_wrap.sitemap .popup_contents_wrap nav'));
mobileMenu();
});
} else {
loadHTML(`${includeBase}/nav_recruit.html?v=${includeVersion}`, '.popup_wrap.sitemap .popup_contents_wrap nav', function () {
mobileMenu();
});
}
}
if (!$('.container').hasClass('recruit')) {
loadHTML(`${includeBase}/header.html?v=${includeVersion}`, '#header', function () {
normalizeSiteLinks(document.querySelector('#header'));
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '#header .corp .nav', function () {
normalizeSiteLinks(document.querySelector('#header .corp .nav'));
connectNavToMapList();
initPackageSWTab();
});
configureHeaderActions(document.querySelector('#header'));
loadSitemapNav();
});
} else {
loadHTML(`${includeBase}/header_recruit.html?v=${includeVersion}`, '#header_recruit', function () {
loadSitemapNav();
if (!$(".container").hasClass("recruit")) {
} else {
const currentPath = location.pathname.split("/").pop();
const navLinks = document.querySelectorAll(
"header .recruit nav ol li.depth1 a"
);
navLinks.forEach((link) => {
const href = link.getAttribute("href").split("/").pop();
if (href === currentPath) {
link.closest(".depth1").classList.add("active");
}
});
}
});
}
loadHTML(`${includeBase}/footer.html?v=${includeVersion}`, '#footer', function () {
normalizeSiteLinks(document.querySelector('#footer'));
loadHTML(`${includeBase}/${navIncludeFile}?v=${includeVersion}`, '#footer .nav', function () {
normalizeSiteLinks(document.querySelector('#footer .nav'));
});
});
});
// ?�� TITLE 관??SCRIPT
// ?�� S: title ?�일?�기
$(function () {
const currentPath = location.pathname;
if (currentPath.indexOf("/recruit/") > 0) {
document.querySelector("head title").textContent =
"?�재채용 | (�?바론컨설?�트";
} else {
document.querySelector("head title").textContent = "BaronConsultant";
}
});
// E : title ?�일?�기
// ?�� S : header ?�기�?
$(function () {
const showNav = gsap
.from("#header, #header_recruit", {
yPercent: -200,
paused: true,
duration: 0.2,
onStart: () => {
const stHeader = document.querySelector("header");
if (window.scrollY > 100) {
stHeader.classList.add("open");
}
},
onReverseComplete: () => {
const stHeader = document.querySelector("header");
stHeader.classList.remove("open");
},
})
.progress(1);
let showMapList = null;
const headerNavMM = gsap.matchMedia();
headerNavMM.add("(max-width: 1400px)", () => {
showMapList = gsap
.from(".map_list", {
y: -40,
autoAlpha: 0,
paused: true,
duration: 0.2,
})
.progress(1);
return () => {
showMapList = null;
};
});
ScrollTrigger.create({
start: "top top",
end: 99999,
onUpdate: (self) => {
if (lenis.isStopped) return;
if (self.direction === -1) {
showNav.play();
showMapList && showMapList.play();
} else {
showNav.reverse();
showMapList && showMapList.reverse();
}
},
});
});
// E : header ?�기�?
// ?�� POPUP SCRIPT
// ?��S : ?�업 ?�기
// ?��S : ?�업 ?�기
$(function () {
$(document).on("click", "button", function () {
const button = this;
const value = button.getAttribute("data-value");
const popup = document.querySelector(`.popup_wrap.${value}`);
if (popup) {
// ?�재 ?�크�??�치 ?�??
const scrollY = window.scrollY;
document.body.setAttribute('data-scroll-lock', scrollY);
// body 고정
document.body.style.position = 'fixed';
document.body.style.top = `-${scrollY}px`;
document.body.style.left = '0';
document.body.style.right = '0';
// Lenis ?�전 중�?
if (lenis) {
lenis.destroy();
}
// ?�업 ?�시
popup.style.display = 'block';
// ?�업 ?��? ?�치 ?�크�?강제 ?�성??
const popupContents = popup.querySelector('.popup_contents_wrap');
if (popupContents) {
popupContents.removeAttribute('data-lenis-prevent-wheel');
popupContents.removeAttribute('data-lenis-prevent');
popupContents.style.cssText = `
overflow-y: auto !important;
-webkit-overflow-scrolling: touch !important;
overscroll-behavior: contain !important;
height: 100vh !important;
touch-action: pan-y !important;
`;
// ?�치 ?�벤??강제 ?�용
popupContents.addEventListener('touchstart', function(e) {
e.stopPropagation();
}, { passive: true });
popupContents.addEventListener('touchmove', function(e) {
e.stopPropagation();
}, { passive: true });
}
}
});
});
// E : ?�업 ?�기
// ?��S : ?�업 ?�기
$(function () {
$(document).on("click", function (e) {
const $target = $(e.target);
// ?��?�기 버튼 ?�릭 ??
if ($target.closest(".btn_close").length) {
const $popupWrap = $target.closest(".popup_wrap");
if ($popupWrap.length) {
$popupWrap.hide();
enableBodyScroll();
}
return;
}
// ?��popup_contents_wrap ?�릭 ?�도 ?�함?�서 ?�기
const $popupContents = $target.closest(".popup_contents_wrap");
if ($popupContents.length && $target.is(".popup_contents_wrap")) {
const $popupWrap = $target.closest(".popup_wrap");
if ($popupWrap.length) {
$popupWrap.hide();
enableBodyScroll();
}
}
});
// ?��?�업 ?�힐 ??body ?�크�??�성??
function enableBodyScroll() {
// body ?�크�??�치 복원
const scrollY = document.body.style.top;
document.body.style.position = '';
document.body.style.top = '';
document.body.style.width = '';
window.scrollTo(0, parseInt(scrollY || '0') * -1);
// Lenis ?�시??
lenis.start();
}
});
// E : ?�업 ?�기
// ?��S : map list ?�직이�? /* 250604추�??�� */
let lastScrollY = window.scrollY;
const stickyBox = document.querySelector(".map_list");
window.addEventListener("scroll", () => {
const currentScrollY = window.scrollY;
const width = window.innerWidth;
if (width > 1440) {
//250604 추�?
if (stickyBox != null) {
if (currentScrollY > lastScrollY) {
// ?�래�??�크롤할 ??
stickyBox.style.top = "24px";
} else {
// ?�로 ?�크롤할 ??
stickyBox.style.top = "124px";
}
}
} else {
if (stickyBox != null) {
if (currentScrollY > lastScrollY) {
// ?�래�??�크롤할 ??
stickyBox.style.top = "24px";
} else {
// ?�로 ?�크롤할 ??
stickyBox.style.top = "74px";
}
}
}
lastScrollY = currentScrollY;
});
// E : map list ?�직이�?
// ?�� SCROLL SCRIPT
// ?��AOS
AOS.init();
// ?��Lenis
let lenis;
function handleStartLenis() {
lenis = new Lenis({
lerp: 0.1, // ?�크롤의 부?�러???�도
smoothWheel: true,
smoothTouch: false,
});
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
}
handleStartLenis();
export default lenis;
function mobileMenu() {
const mNav = document.querySelectorAll(".sitemap ol > li.depth1");
mNav.forEach((item) => {
const trigger = item.querySelector(":scope > span");
const depth2 = item.querySelector(":scope > .depth2");
if (!trigger || !depth2 || trigger.dataset.mobileMenuBound === "true") {
return;
}
trigger.dataset.mobileMenuBound = "true";
trigger.addEventListener("click", function () {
if (window.innerWidth > 1440) {
return;
}
const isActive = item.classList.contains("active");
// 다른 모든 활성화된 메뉴 닫기 (아코디언 동작)
mNav.forEach((otherItem) => {
if (otherItem !== item) {
otherItem.classList.remove("active");
const otherDepth2 = otherItem.querySelector(":scope > .depth2");
if (otherDepth2) {
otherDepth2.style.maxHeight = null;
}
}
});
if (!isActive) {
item.classList.add("active");
depth2.style.maxHeight = depth2.scrollHeight + "px";
} else {
item.classList.remove("active");
depth2.style.maxHeight = null;
}
});
});
}
@@ -0,0 +1,84 @@
// 헤더 로그인 팝오버 보조 스크립트
// - 기존 CSS의 hover/focus-within 표시는 그대로 두고, 클릭(터치) 열기·바깥 클릭 닫기·Esc 닫기·aria-expanded만 보완한다.
// - 로그인/로그아웃 링크의 주소, 문구, 노출 여부는 common.js(configureHeaderActions)가 SSO 상태에 맞춰 설정하므로 여기서 다루지 않는다.
// - 헤더는 AJAX로 늦게 삽입되므로 document 이벤트 위임으로 처리한다.
const WRAPPER_SELECTOR = ".header_login";
const TRIGGER_SELECTOR = ".header_login_trigger";
function setExpanded(wrapper, expanded) {
wrapper.querySelector(TRIGGER_SELECTOR)?.setAttribute("aria-expanded", String(expanded));
}
function openMenu(wrapper) {
document.querySelectorAll(`${WRAPPER_SELECTOR}.is-open`).forEach((other) => {
if (other !== wrapper) closeMenu(other);
});
wrapper.classList.remove("is-dismissed");
wrapper.classList.add("is-open");
setExpanded(wrapper, true);
}
// dismiss: hover/focus가 남아 있어도 포인터·포커스가 벗어날 때까지 닫힌 상태를 유지한다(Esc).
function closeMenu(wrapper, { dismiss = false } = {}) {
wrapper.classList.remove("is-open");
wrapper.classList.toggle("is-dismissed", dismiss);
setExpanded(wrapper, false);
}
function isMenuVisible(wrapper) {
if (wrapper.hidden || wrapper.classList.contains("is-dismissed")) return false;
return wrapper.classList.contains("is-open") || wrapper.matches(":hover, :focus-within");
}
if (!window.__baronHeaderLoginBound) {
window.__baronHeaderLoginBound = true;
document.addEventListener("pointerover", (event) => {
if (event.pointerType === "touch") return;
const wrapper = event.target.closest?.(WRAPPER_SELECTOR);
if (wrapper && !wrapper.contains(event.relatedTarget) && !wrapper.classList.contains("is-dismissed")) {
setExpanded(wrapper, true);
}
});
document.addEventListener("pointerout", (event) => {
if (event.pointerType === "touch") return;
const wrapper = event.target.closest?.(WRAPPER_SELECTOR);
if (wrapper && !wrapper.contains(event.relatedTarget) && !wrapper.contains(document.activeElement)) {
closeMenu(wrapper);
}
});
document.addEventListener("focusin", (event) => {
const wrapper = event.target.closest?.(WRAPPER_SELECTOR);
if (wrapper && !wrapper.classList.contains("is-dismissed")) setExpanded(wrapper, true);
});
document.addEventListener("focusout", (event) => {
const wrapper = event.target.closest?.(WRAPPER_SELECTOR);
if (wrapper && !wrapper.contains(event.relatedTarget) && !wrapper.matches(":hover")) {
closeMenu(wrapper);
}
});
document.addEventListener("click", (event) => {
const trigger = event.target.closest?.(TRIGGER_SELECTOR);
const wrapper = trigger?.closest(WRAPPER_SELECTOR);
if (wrapper) {
openMenu(wrapper);
return;
}
if (!event.target.closest?.(WRAPPER_SELECTOR)) {
document.querySelectorAll(`${WRAPPER_SELECTOR}.is-open`).forEach((openWrapper) => closeMenu(openWrapper));
}
});
document.addEventListener("keydown", (event) => {
if (event.key !== "Escape") return;
const wrapper = [...document.querySelectorAll(WRAPPER_SELECTOR)].find(isMenuVisible);
if (!wrapper) return;
const hadFocus = wrapper.contains(document.activeElement);
closeMenu(wrapper, { dismiss: true });
if (hadFocus) wrapper.querySelector(TRIGGER_SELECTOR)?.focus({ preventScroll: true });
});
}
@@ -0,0 +1,135 @@
// Localized copy for the inquiry design preview. No live customer data.
const translations = {
"ko": {
"title": "문의하기 | 바론컨설턴트",
"categories": {
"notice": "공지사항",
"general": "일반문의",
"improvement": "개선문의",
"error": "오류문의"
},
"statuses": {
"new": "문의접수",
"done": "답변완료",
"review": "검토중",
"patch": "패치예정"
},
"notices": [
"이용 안내 및 문의 작성 방법",
"프로그램 업데이트 안내",
"이용가이드 안내",
"설치 및 실행 전 확인사항",
"통합로그인 이용 안내"
],
"questions": {
"egbim": [
"PDF 출력 시 이미지 표시 관련 문의",
"도면 편집 기능 개선 요청",
"프로그램 설치 방법을 문의합니다",
"다중출력 설정 관련 문의",
"도면관리 기능 사용 방법 문의"
],
"gaia": [
"시추주상도 출력 설정 관련 문의",
"성과품 양식 추가 요청",
"프로그램 설치 방법을 문의합니다",
"지층 정보 입력 시 확인 요청",
"시추 데이터 불러오기 관련 문의"
],
"tova": [
"네트워크 데이터 불러오기 관련 문의",
"교통 분석 결과 화면 개선 요청",
"프로그램 설치 방법을 문의합니다",
"공로 분석 설정 관련 문의",
"대중교통 분석 기능 사용 방법 문의"
]
},
"noticeNumber": "공지",
"company": "바론컨설턴트",
"support": "고객지원",
"admin": "관리자",
"placeholderCompany": "회사명",
"departments": [
"설계부",
"기술부",
"사업부"
],
"userPrefix": "사용자",
"noticeContent": "{service} 서비스 이용 안내입니다.\n\n게시판의 제목, 내용, 작성 정보가 표시되는 디자인 예시입니다.",
"questionContent": "{service} 이용 중 궁금한 사항을 문의합니다.\n\n이 영역에 문의 내용과 첨부파일, 답변이 표시됩니다. 현재 내용은 디자인 확인을 위한 예시입니다.",
"replies": "답변 {count}개",
"empty": "검색 조건에 맞는 문의가 없습니다.",
"previous": "이전 페이지",
"next": "다음 페이지",
"page": "{page}페이지",
"results": "{service} 문의 {count}건, {page} / {pages}페이지",
"submitPreview": "디자인 미리보기 화면으로 문의는 전송되지 않습니다."
},
"en": {
"title": "Software Inquiries | BARON Consultants",
"categories": {
"notice": "Notice",
"general": "General",
"improvement": "Suggestion",
"error": "Bug report"
},
"statuses": {
"new": "Received",
"done": "Answered",
"review": "In review",
"patch": "Scheduled"
},
"notices": [
"Getting started and submitting an inquiry",
"Software update notice",
"User guide",
"Before installing and launching the software",
"Single sign-on guide"
],
"questions": {
"egbim": [
"Images missing from PDF exports",
"Suggestion for drawing editing tools",
"How do I install the software?",
"Help with batch printing settings",
"How to use drawing management"
],
"gaia": [
"Help with borehole log print settings",
"Request for an additional report template",
"How do I install the software?",
"Checking geological layer information",
"Help with importing borehole data"
],
"tova": [
"Help with importing network data",
"Suggestion for the traffic analysis results screen",
"How do I install the software?",
"Help with road traffic analysis settings",
"How to use public transport analysis"
]
},
"noticeNumber": "Notice",
"company": "Baron Consultants",
"support": "Support",
"admin": "Admin",
"placeholderCompany": "Company",
"departments": [
"Design",
"Engineering",
"Business"
],
"userPrefix": "User ",
"noticeContent": "A guide to using {service}.\n\nThis sample shows how a post title, content, and author information will appear.",
"questionContent": "I have a question about using {service}.\n\nInquiry details, attachments, and replies will appear here. This is sample content for the design preview.",
"replies": "Replies: {count}",
"empty": "No inquiries match your search.",
"previous": "Previous page",
"next": "Next page",
"page": "Page {page}",
"results": "{service}: {count} inquiries, page {page} of {pages}",
"submitPreview": "This is a design preview. Your inquiry has not been sent."
}
};
export const inquiryCopy = translations[document.documentElement.lang === "en" ? "en" : "ko"];
@@ -0,0 +1,231 @@
import { inquiryCopy as copy } from "./inquiry-copy.js";
// Design preview only: all rows below are fictional fixtures.
// No API requests, authentication checks, uploads, storage, or submission are performed.
const SERVICES = { egbim: 'EG-BIM', gaia: 'GAIA', tova: 'TOVA' };
const CATEGORY_LABELS = copy.categories;
const STATUS_LABELS = copy.statuses;
const NOTICE_TITLES = copy.notices;
const SERVICE_QUESTIONS = copy.questions;
function message(key, values = {}) {
return copy[key].replace(/\{(\w+)\}/g, (_, name) => values[name] ?? '');
}
// Replace this fixture factory with developer-supplied data when integrating the board.
function createPreviewRows(service) {
const notices = NOTICE_TITLES.map((title, index) => ({
id: `${service}-notice-${index}`, number: copy.noticeNumber, category: 'notice', company: copy.company, team: copy.support, author: copy.admin,
title: `${SERVICES[service]} ${title}`, date: `2026-10-${String(25 - index).padStart(2, '0')}`, status: '', comments: index === 2 ? 1 : 0, mine: false,
content: message('noticeContent', {service: SERVICES[service]})
}));
const questions = Array.from({ length: 15 }, (_, index) => ({
id: `${service}-question-${index}`, number: 15 - index, category: ['error', 'improvement', 'general'][index % 3],
company: copy.placeholderCompany, team: copy.departments[index % 3], author: `${copy.userPrefix}${String(index + 1).padStart(2, '0')}`,
title: SERVICE_QUESTIONS[service][index % 5], date: `2026-10-${String(25 - index).padStart(2, '0')}`,
status: ['done', 'new', 'review', 'patch', 'done'][index % 5], comments: index % 3 ? 0 : 1, mine: index % 4 === 0,
content: message('questionContent', {service: SERVICES[service]})
}));
return [...notices, ...questions];
}
const pageSize = 10;
let service = Object.hasOwn(SERVICES, location.hash.slice(1)) ? location.hash.slice(1) : 'egbim';
let currentPage = 1;
let query = '';
const searchForm = document.querySelector('#inquiry-search');
const rowsElement = document.querySelector('#inquiry-rows');
const pagination = document.querySelector('#inquiry-pagination');
const tabs = [...document.querySelectorAll('.inquiry-tabs [role="tab"]')];
const detail = document.querySelector('#inquiry-detail');
const compose = document.querySelector('#inquiry-compose');
const composeForm = document.querySelector('#inquiry-compose-form');
let dialogOpener;
let previousPrevent;
function textCell(text, className = '') {
const cell = document.createElement('td');
cell.textContent = text;
cell.className = className;
return cell;
}
function renderRows() {
const category = searchForm.elements.category.value;
const mineOnly = searchForm.elements.mine.checked;
const filtered = createPreviewRows(service).filter(row =>
(category === 'all' || row.category === category)
&& (!mineOnly || row.mine)
&& `${row.title} ${row.content}`.toLocaleLowerCase().includes(query)
);
const pages = Math.max(1, Math.ceil(filtered.length / pageSize));
currentPage = Math.min(currentPage, pages);
rowsElement.replaceChildren();
const visible = filtered.slice((currentPage - 1) * pageSize, currentPage * pageSize);
for (const row of visible) {
const tr = document.createElement('tr');
if (row.category === 'notice') tr.className = 'inquiry-notice';
tr.append(textCell(row.number), textCell(CATEGORY_LABELS[row.category]), textCell(row.company), textCell(row.team), textCell(row.author));
const subject = textCell('', 'inquiry-subject');
const button = document.createElement('button');
button.type = 'button';
const title = document.createElement('span');
title.textContent = row.title;
button.append(title);
if (row.comments) {
const comments = document.createElement('span');
comments.className = 'inquiry-comments';
comments.setAttribute('aria-label', message('replies', {count: row.comments}));
comments.innerHTML = '<svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 2c-5 0-9 3-9 7 0 2 1 4 3 5l-1 4 5-2h2c5 0 9-3 9-7s-4-7-9-7Z"/></svg>';
comments.append(String(row.comments));
button.append(comments);
}
button.addEventListener('click', () => {
document.querySelector('#inquiry-detail-title').textContent = row.title;
document.querySelector('#inquiry-detail-meta').textContent = `${SERVICES[service]} · ${CATEGORY_LABELS[row.category]} · ${row.author} · ${row.date}`;
document.querySelector('#inquiry-detail-body').textContent = row.content;
openDialog(detail, button);
});
subject.append(button);
const status = textCell('');
if (row.status) {
const badge = document.createElement('span');
badge.className = `inquiry-status inquiry-status--${row.status}`;
badge.textContent = STATUS_LABELS[row.status];
status.append(badge);
}
tr.append(subject, textCell(row.date), status);
rowsElement.append(tr);
}
if (!visible.length) {
const tr = document.createElement('tr');
const cell = textCell(copy.empty, 'inquiry-empty');
cell.colSpan = 8;
tr.append(cell);
rowsElement.append(tr);
}
pagination.replaceChildren();
function pageButton(label, target, disabled = false) {
const button = document.createElement('button');
button.type = 'button';
button.textContent = label;
button.disabled = disabled;
button.setAttribute('aria-label', label === '‹' ? copy.previous : label === '›' ? copy.next : message('page', {page: target}));
if (String(target) === label && target === currentPage) button.setAttribute('aria-current', 'page');
button.addEventListener('click', () => {
currentPage = target;
renderRows();
pagination.querySelector('[aria-current="page"]')?.focus({ preventScroll: true });
});
pagination.append(button);
}
pageButton('‹', currentPage - 1, currentPage === 1);
for (let page = 1; page <= pages; page++) pageButton(String(page), page);
pageButton('›', currentPage + 1, currentPage === pages);
document.querySelector('#inquiry-results').textContent = message('results', {service: SERVICES[service], count: filtered.length, page: currentPage, pages});
window.ScrollTrigger?.refresh();
}
function selectService(next) {
service = next;
currentPage = 1;
query = '';
searchForm.reset();
tabs.forEach(tab => {
const active = tab.dataset.service === service;
tab.setAttribute('aria-selected', String(active));
tab.tabIndex = active ? 0 : -1;
});
document.querySelector('#inquiry-panel').setAttribute('aria-labelledby', `tab-${service}`);
document.querySelectorAll('[data-service-name]').forEach(el => { el.textContent = SERVICES[service]; });
updateLanguageLinks();
renderRows();
}
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
selectService(tab.dataset.service);
history.replaceState(null, '', `#${service}`);
});
tab.addEventListener('keydown', event => {
let next;
if (event.key === 'ArrowRight') next = (index + 1) % tabs.length;
if (event.key === 'ArrowLeft') next = (index + tabs.length - 1) % tabs.length;
if (event.key === 'Home') next = 0;
if (event.key === 'End') next = tabs.length - 1;
if (next === undefined) return;
event.preventDefault();
tabs[next].focus();
tabs[next].click();
});
});
window.addEventListener('hashchange', () => {
const next = location.hash.slice(1);
if (Object.hasOwn(SERVICES, next)) selectService(next);
});
searchForm.addEventListener('submit', event => {
event.preventDefault();
query = searchForm.elements.keyword.value.trim().toLocaleLowerCase();
currentPage = 1;
renderRows();
});
searchForm.addEventListener('change', event => {
if (!['category', 'mine'].includes(event.target.name)) return;
currentPage = 1;
renderRows();
});
function openDialog(dialog, opener) {
dialogOpener = opener;
if (!document.documentElement.classList.contains('inquiry-modal-open')) {
previousPrevent = document.documentElement.getAttribute('data-lenis-prevent');
}
document.documentElement.setAttribute('data-lenis-prevent', '');
document.documentElement.classList.add('inquiry-modal-open');
dialog.showModal();
}
for (const dialog of [detail, compose]) {
dialog.querySelectorAll('[data-close]').forEach(button => button.addEventListener('click', () => dialog.close()));
dialog.addEventListener('click', event => {
const rect = dialog.getBoundingClientRect();
if (event.target === dialog && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom)) dialog.close();
});
dialog.addEventListener('close', () => {
if (document.querySelector('.inquiry-dialog[open]')) return;
document.documentElement.classList.remove('inquiry-modal-open');
if (previousPrevent === null) document.documentElement.removeAttribute('data-lenis-prevent');
else document.documentElement.setAttribute('data-lenis-prevent', previousPrevent);
dialogOpener?.focus({ preventScroll: true });
});
}
document.querySelector('#inquiry-write').addEventListener('click', event => {
composeForm.reset();
document.querySelector('#inquiry-compose-service').value = service;
document.querySelector('#inquiry-compose-feedback').textContent = '';
openDialog(compose, event.currentTarget);
});
composeForm.addEventListener('submit', event => {
event.preventDefault();
document.querySelector('#inquiry-compose-feedback').textContent = copy.submitPreview;
});
selectService(service);
// Run after the shared header's jQuery-ready title initialization.
window.jQuery(() => { document.title = copy.title; });
// Keep the current product selected when switching the inquiry page language.
function updateLanguageLinks() {
const links = [...document.querySelectorAll('#header .language a')];
const routes = document.querySelector('.inquiry-page').dataset;
for (const link of links) {
const language = link.textContent.trim();
const route = language === 'KR' ? routes.inquiryKo : language === 'EN' ? routes.inquiryEn : '';
if (route) link.setAttribute('href', route + '#' + service);
}
return links.length >= 2;
}
// Shared headers may be included asynchronously.
if (!updateLanguageLinks()) {
const header = document.querySelector('#header');
const observer = new MutationObserver(() => {
if (updateLanguageLinks()) observer.disconnect();
});
observer.observe(header, {childList: true, subtree: true});
}
+1
View File
@@ -0,0 +1 @@
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AOS=t():e.AOS=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return e[o].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="dist/",t(0)}([function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},r=n(1),a=(o(r),n(6)),u=o(a),c=n(7),f=o(c),s=n(8),d=o(s),l=n(9),p=o(l),m=n(10),b=o(m),v=n(11),y=o(v),g=n(14),h=o(g),w=[],k=!1,x=document.all&&!window.atob,j={offset:120,delay:0,easing:"ease",duration:400,disable:!1,once:!1,startEvent:"DOMContentLoaded",throttleDelay:99,debounceDelay:50,disableMutationObserver:!1},O=function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(e&&(k=!0),k)return w=(0,y.default)(w,j),(0,b.default)(w,j.once),w},_=function(){w=(0,h.default)(),O()},S=function(){w.forEach(function(e,t){e.node.removeAttribute("data-aos"),e.node.removeAttribute("data-aos-easing"),e.node.removeAttribute("data-aos-duration"),e.node.removeAttribute("data-aos-delay")})},z=function(e){return e===!0||"mobile"===e&&p.default.mobile()||"phone"===e&&p.default.phone()||"tablet"===e&&p.default.tablet()||"function"==typeof e&&e()===!0},A=function(e){return j=i(j,e),w=(0,h.default)(),z(j.disable)||x?S():(document.querySelector("body").setAttribute("data-aos-easing",j.easing),document.querySelector("body").setAttribute("data-aos-duration",j.duration),document.querySelector("body").setAttribute("data-aos-delay",j.delay),"DOMContentLoaded"===j.startEvent&&["complete","interactive"].indexOf(document.readyState)>-1?O(!0):"load"===j.startEvent?window.addEventListener(j.startEvent,function(){O(!0)}):document.addEventListener(j.startEvent,function(){O(!0)}),window.addEventListener("resize",(0,f.default)(O,j.debounceDelay,!0)),window.addEventListener("orientationchange",(0,f.default)(O,j.debounceDelay,!0)),window.addEventListener("scroll",(0,u.default)(function(){(0,b.default)(w,j.once)},j.throttleDelay)),j.disableMutationObserver||(0,d.default)("[data-aos]",_),w)};e.exports={init:A,refresh:O,refreshHard:_}},function(e,t){},,,,,function(e,t){(function(t){"use strict";function n(e,t,n){function o(t){var n=b,o=v;return b=v=void 0,k=t,g=e.apply(o,n)}function r(e){return k=e,h=setTimeout(s,t),_?o(e):g}function a(e){var n=e-w,o=e-k,i=t-n;return S?j(i,y-o):i}function c(e){var n=e-w,o=e-k;return void 0===w||n>=t||n<0||S&&o>=y}function s(){var e=O();return c(e)?d(e):void(h=setTimeout(s,a(e)))}function d(e){return h=void 0,z&&b?o(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),k=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(O())}function m(){var e=O(),n=c(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(s,t),o(w)}return void 0===h&&(h=setTimeout(s,t)),g}var b,v,y,g,h,w,k=0,_=!1,S=!1,z=!0;if("function"!=typeof e)throw new TypeError(f);return t=u(t)||0,i(n)&&(_=!!n.leading,S="maxWait"in n,y=S?x(u(n.maxWait)||0,t):y,z="trailing"in n?!!n.trailing:z),m.cancel=l,m.flush=p,m}function o(e,t,o){var r=!0,a=!0;if("function"!=typeof e)throw new TypeError(f);return i(o)&&(r="leading"in o?!!o.leading:r,a="trailing"in o?!!o.trailing:a),n(e,t,{leading:r,maxWait:t,trailing:a})}function i(e){var t="undefined"==typeof e?"undefined":c(e);return!!e&&("object"==t||"function"==t)}function r(e){return!!e&&"object"==("undefined"==typeof e?"undefined":c(e))}function a(e){return"symbol"==("undefined"==typeof e?"undefined":c(e))||r(e)&&k.call(e)==d}function u(e){if("number"==typeof e)return e;if(a(e))return s;if(i(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(l,"");var n=m.test(e);return n||b.test(e)?v(e.slice(2),n?2:8):p.test(e)?s:+e}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f="Expected a function",s=NaN,d="[object Symbol]",l=/^\s+|\s+$/g,p=/^[-+]0x[0-9a-f]+$/i,m=/^0b[01]+$/i,b=/^0o[0-7]+$/i,v=parseInt,y="object"==("undefined"==typeof t?"undefined":c(t))&&t&&t.Object===Object&&t,g="object"==("undefined"==typeof self?"undefined":c(self))&&self&&self.Object===Object&&self,h=y||g||Function("return this")(),w=Object.prototype,k=w.toString,x=Math.max,j=Math.min,O=function(){return h.Date.now()};e.exports=o}).call(t,function(){return this}())},function(e,t){(function(t){"use strict";function n(e,t,n){function i(t){var n=b,o=v;return b=v=void 0,O=t,g=e.apply(o,n)}function r(e){return O=e,h=setTimeout(s,t),_?i(e):g}function u(e){var n=e-w,o=e-O,i=t-n;return S?x(i,y-o):i}function f(e){var n=e-w,o=e-O;return void 0===Line truncated
+10
View File
@@ -0,0 +1,10 @@
/*!
* GSAP 3.12.5
* https://gsap.com
*
* @license Copyright 2024, GreenSock. All rights reserved.
* Subject to the terms at https://gsap.com/standard-license or for Club GSAP members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function P(t){return(i=yt(t,ot))&&ze}function Q(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function R(t,e){return!e&&console.warn(t)}function S(t,e){return t&&(ot[t]=e)&&i&&(i[t]=e)||ot}function T(){return 0}function ea(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=gt.length;r--&&!gt[r].targetTest(i););e=gt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new Vt(t[r],e)))||t.splice(r,1);return t}function fa(t){return t._gsap||ea(Mt(t))[0]._gsap}function ga(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ha(t,e){return(t=t.split(",")).forEach(e)||t}function ia(t){return Math.round(1e5*t)/1e5||0}function ja(t){return Math.round(1e7*t)/1e7||0}function ka(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function la(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++i<r;);return i<r}function ma(){var t,e,r=dt.length,i=dt.slice(0);for(ct={},t=dt.length=0;t<r;t++)(e=i[t])&&e._lazy&&(e.render(e._lazy[0],e._lazy[1],!0)._lazy=0)}function na(t,e,r,i){dt.length&&!L&&ma(),t.render(e,r,i||L&&e<0&&(t._initted||t._startAt)),dt.length&&!L&&ma()}function oa(t){var e=parseFloat(t);return(e||0===e)&&(t+"").match(at).length<2?e:r(t)?t.trim():t}function pa(t){return t}function qa(t,e){for(var r in e)r in t||(t[r]=e[r]);return t}function ta(t,e){for(var r in e)"__proto__"!==r&&"constructor"!==r&&"prototype"!==r&&(t[r]=v(e[r])?ta(t[r]||(t[r]={}),e[r]):e[r]);return t}function ua(t,e){var r,i={};for(r in t)r in e||(i[r]=t[r]);return i}function va(t){var e=t.parent||I,r=t.keyframes?function _setKeyframeDefaults(i){return function(t,e){for(var r in e)r in t||"duration"===r&&i||"ease"===r||(t[r]=e[r])}}(Z(t.keyframes)):qa;if(w(t.inherit))for(;e;)r(t,e.vars.defaults),e=e.parent||e._dp;return t}function xa(t,e,r,i,n){void 0===r&&(r="_first"),void 0===i&&(i="_last");var a,s=t[i];if(n)for(a=e[n];s&&s[n]>a;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function ya(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function za(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Aa(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Ca(t,e,r,i){return t._startAt&&(L?t._startAt.revert(ht):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ea(t){return t._repeat?Tt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ga(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ha(t){return t._end=ja(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function Ia(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=ja(r._time-(0<t._ts?e/t._ts:((t._dirty?t.totalDuration():t._tDur)-e)/-t._ts)),Ha(t),r._dirty||Aa(r,t)),t}function Ja(t,e){var r;if((e._time||!e._dur&&e._initted||e._start<t._time&&(e._dur||!e.add))&&(r=Ga(t.rawTime(),e),(!e._dur||Ot(0,e.totalDuration(),r)-e._tTime>X)&&e.render(r,!0)),Aa(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur<t.duration())for(r=t;r._dp;)0<=r.rawTime()&&r.totalTime(r._tTime),r=r._dp;t._zTime=-X}}function Ka(e,r,i,n){return r.parent&&za(r),r._start=ja((t(i)?i:i||e!==I?xt(e,i,r):e._time)+r._delay),r._end=ja(r._start+(r.totalDuration()/Math.abs(r.timeScale())||0)),xa(e,r,"_first","_last",e._sort?"_start":0),bt(r)||(e._recent=r),n||Ja(e,r),e._ts<0&&Ia(e,e._tTime),e}function La(t,e){return(ot.ScrollTrigger||Q("scrollTrigger",e))&&ot.ScrollTrigger.create(e,t)}function Ma(t,e,r,i,n){return Qt(t,e,n),t._initted?!r&&t._pt&&!L&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&f!==Rt.frame?(dt.push(t),t._lazy=[n,i],1):void 0:1}function Ra(t,e,r,i){var n=t._repeat,a=ja(e)||0,s=t._tTime/t._tDur;return s&&!i&&(t._time*=a/t._dur),t._dur=a,t._tDur=n?n<0?1e10:ja(a*(n+1)+t._rDelay*n):a,0<s&&!i&&Ia(t,t._tTime=t._tDur*s),t.parent&&Ha(t),r||Aa(t.parent,t),t}function Sa(t){return t instanceof Xt?Aa(t):Ra(t,t._dur)}function Va(e,r,i){var n,aLine truncated
@@ -0,0 +1,2 @@
/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0<t&&t-1 in e)}S.fn=S.prototype={jquery:f,constructor:S,length:0,toArray:function(){return s.call(this)},get:function(e){return null==e?s.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=S.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return S.each(this,e)},map:function(n){return this.pushStack(S.map(this,function(e,t){return n.call(e,t,e)}))},slice:function(){return this.pushStack(s.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},even:function(){return this.pushStack(S.grep(this,function(e,t){return(t+1)%2}))},odd:function(){return this.pushStack(S.grep(this,function(e,t){return t%2}))},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(0<=n&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:u,sort:t.sort,splice:t.splice},S.extend=S.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||m(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)r=e[t],"__proto__"!==t&&a!==r&&(l&&r&&(S.isPlainObject(r)||(i=Array.isArray(r)))?(n=a[t],o=i&&!Array.isArray(n)?[]:i||S.isPlainObject(n)?n:{},i=!1,a[t]=S.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},S.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==o.call(e))&&(!(t=r(e))||"function"==typeof(n=y.call(t,"constructor")&&t.constructor)&&a.call(n)===l)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e,t,n){b(e,{nonce:t&&t.nonce},n)},each:function(e,t){var n,r=0;if(p(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},makeArray:function(e,t){var n=t||[];return null!=e&&(p(Object(e))?S.merge(n,"string"==typeof e?[e]:e):u.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:i.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r=[],i=0,o=e.length,a=!n;i<o;i++)!t(e[i],i)!==a&&r.push(e[i]);return r},map:function(e,t,n){var r,i,o=0,a=[];if(p(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&a.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&a.push(i);return g(a)},guid:1,support:v}),"function"==typeof Symbol&&(S.fn[Symbol.iterator]=t[Symbol.iterator]),S.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){n["[object "+t+"]"]=t.toLowerCase()});var d=function(n){var e,d,b,o,i,h,f,g,w,u,l,T,C,a,E,y,s,c,v,S="sizzle"+1*new Date,p=n.document,k=0,r=0,m=ue(),x=ue(),A=ue(),N=ue(),j=function(e,t){return e===t&&(l=!0),0},D={}.hasOwnProperty,t=[],q=t.pop,L=t.push,H=t.push,O=t.slice,P=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",I="(?:\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",W="\\["+M+"*("+I+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+I+"))|)"+M+"*\\]",F=":("+I+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+W+")*)|.*)\\)|)",$=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=new RegExp("^"+M+"*,"+M+"*"),z=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^Line truncated
+2
View File
@@ -0,0 +1,2 @@
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).Lenis=i()}(this,(function(){"use strict";function clamp(t,i,e){return Math.max(t,Math.min(i,e))}class Animate{constructor(){this.isRunning=!1,this.value=0,this.from=0,this.to=0,this.duration=0,this.currentTime=0}advance(t){var i;if(!this.isRunning)return;let e=!1;if(this.duration&&this.easing){this.currentTime+=t;const i=clamp(0,this.currentTime/this.duration,1);e=i>=1;const s=e?1:this.easing(i);this.value=this.from+(this.to-this.from)*s}else this.lerp?(this.value=function damp(t,i,e,s){return function lerp(t,i,e){return(1-e)*t+e*i}(t,i,1-Math.exp(-e*s))}(this.value,this.to,60*this.lerp,t),Math.round(this.value)===this.to&&(this.value=this.to,e=!0)):(this.value=this.to,e=!0);e&&this.stop(),null===(i=this.onUpdate)||void 0===i||i.call(this,this.value,e)}stop(){this.isRunning=!1}fromTo(t,i,{lerp:e,duration:s,easing:o,onStart:n,onUpdate:l}){this.from=this.value=t,this.to=i,this.lerp=e,this.duration=s,this.easing=o,this.currentTime=0,this.isRunning=!0,null==n||n(),this.onUpdate=l}}class Dimensions{constructor({wrapper:t,content:i,autoResize:e=!0,debounce:s=250}={}){this.width=0,this.height=0,this.scrollWidth=0,this.scrollHeight=0,this.resize=()=>{this.onWrapperResize(),this.onContentResize()},this.onWrapperResize=()=>{this.wrapper===window?(this.width=window.innerWidth,this.height=window.innerHeight):this.wrapper instanceof HTMLElement&&(this.width=this.wrapper.clientWidth,this.height=this.wrapper.clientHeight)},this.onContentResize=()=>{this.wrapper===window?(this.scrollHeight=this.content.scrollHeight,this.scrollWidth=this.content.scrollWidth):this.wrapper instanceof HTMLElement&&(this.scrollHeight=this.wrapper.scrollHeight,this.scrollWidth=this.wrapper.scrollWidth)},this.wrapper=t,this.content=i,e&&(this.debouncedResize=function debounce(t,i){let e;return function(){let s=arguments,o=this;clearTimeout(e),e=setTimeout((function(){t.apply(o,s)}),i)}}(this.resize,s),this.wrapper===window?window.addEventListener("resize",this.debouncedResize,!1):(this.wrapperResizeObserver=new ResizeObserver(this.debouncedResize),this.wrapperResizeObserver.observe(this.wrapper)),this.contentResizeObserver=new ResizeObserver(this.debouncedResize),this.contentResizeObserver.observe(this.content)),this.resize()}destroy(){var t,i;null===(t=this.wrapperResizeObserver)||void 0===t||t.disconnect(),null===(i=this.contentResizeObserver)||void 0===i||i.disconnect(),window.removeEventListener("resize",this.debouncedResize,!1)}get limit(){return{x:this.scrollWidth-this.width,y:this.scrollHeight-this.height}}}class Emitter{constructor(){this.events={}}emit(t,...i){let e=this.events[t]||[];for(let t=0,s=e.length;t<s;t++)e[t](...i)}on(t,i){var e;return(null===(e=this.events[t])||void 0===e?void 0:e.push(i))||(this.events[t]=[i]),()=>{var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}}off(t,i){var e;this.events[t]=null===(e=this.events[t])||void 0===e?void 0:e.filter((t=>i!==t))}destroy(){this.events={}}}const t=100/6;class VirtualScroll{constructor(i,{wheelMultiplier:e=1,touchMultiplier:s=1}){this.lastDelta={x:0,y:0},this.windowWidth=0,this.windowHeight=0,this.onTouchStart=t=>{const{clientX:i,clientY:e}=t.targetTouches?t.targetTouches[0]:t;this.touchStart.x=i,this.touchStart.y=e,this.lastDelta={x:0,y:0},this.emitter.emit("scroll",{deltaX:0,deltaY:0,event:t})},this.onTouchMove=t=>{var i,e,s,o;const{clientX:n,clientY:l}=t.targetTouches?t.targetTouches[0]:t,r=-(n-(null!==(e=null===(i=this.touchStart)||void 0===i?void 0:i.x)&&void 0!==e?e:0))*this.touchMultiplier,h=-(l-(null!==(o=null===(s=this.touchStart)||void 0===s?void 0:s.y)&&void 0!==o?o:0))*this.touchMultiplier;this.touchStart.x=n,this.touchStart.y=l,this.lastDelta={x:r,y:h},this.emitter.emit("scroll",{deltaX:r,deltaY:h,event:t})},this.onTouchEnd=t=>{this.emitter.emit("scroll",{deltaX:this.lastDelta.x,deltaY:this.lastDelta.y,event:t})},this.onWheel=i=>{let{deltaX:e,deltaY:s,deltaMode:o}=i;e*=1===o?t:2===o?this.windowWidth:1,s*=1===o?t:2===o?this.windowHeight:1,e*=this.wheelMultiplier,s*=this.wheelMultiplier,this.emitter.emit("scroll",{deltaX:e,deltaY:s,event:i})},this.onWindowResize=()=>{this.windowWidth=window.innerWidth,this.windowHeight=window.innerHeight},this.element=i,this.wheelMultiplier=e,this.touchMultiplier=s,this.touchStart={x:null,y:null},this.emitter=new Emitter,window.addEventListener("resize",this.onWindowResize,!1),this.onWindowResize(),this.element.addEventListener("wheel",this.onWheel,{passive:!1}),this.element.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.element.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.element.addEventListener("touchend",this.onTouchEnd,{passive:!1})}on(t,i){return this.emitter.on(t,i)}destroy(){this.emitter.destroy(),window.removeEventListener("resize",this.onWindowResize,!1),this.elemeLine truncated
//# sourceMappingURL=lenis.min.js.map
@@ -0,0 +1,10 @@
/*!
* ScrollToPlugin 3.12.5
* https://gsap.com
*
* @license Copyright 2024, GreenSock. All rights reserved.
* Subject to the terms at https://gsap.com/standard-license or for Club GSAP members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function l(){return"undefined"!=typeof window}function m(){return f||l()&&(f=window.gsap)&&f.registerPlugin&&f}function n(e){return"string"==typeof e}function o(e){return"function"==typeof e}function p(e,t){var o="x"===t?"Width":"Height",n="scroll"+o,r="client"+o;return e===T||e===i||e===c?Math.max(i[n],c[n])-(T["inner"+o]||i[r]||c[r]):e[n]-e["offset"+o]}function q(e,t){var o="scroll"+("x"===t?"Left":"Top");return e===T&&(null!=e.pageXOffset?o="page"+t.toUpperCase()+"Offset":e=null!=i[o]?i:c),function(){return e[o]}}function s(e,t){if(!(e=y(e)[0])||!e.getBoundingClientRect)return console.warn("scrollTo target doesn't exist. Using 0")||{x:0,y:0};var o=e.getBoundingClientRect(),n=!t||t===T||t===c,r=n?{top:i.clientTop-(T.pageYOffset||i.scrollTop||c.scrollTop||0),left:i.clientLeft-(T.pageXOffset||i.scrollLeft||c.scrollLeft||0)}:t.getBoundingClientRect(),l={x:o.left-r.left,y:o.top-r.top};return!n&&t&&(l.x+=q(t,"x")(),l.y+=q(t,"y")()),l}function t(e,t,o,r,l){return isNaN(e)||"object"==typeof e?n(e)&&"="===e.charAt(1)?parseFloat(e.substr(2))*("-"===e.charAt(0)?-1:1)+r-l:"max"===e?p(t,o)-l:Math.min(p(t,o),s(e,t)[o]-l):parseFloat(e)-l}function u(){f=m(),l()&&f&&"undefined"!=typeof document&&document.body&&(T=window,c=document.body,i=document.documentElement,y=f.utils.toArray,f.config({autoKillThreshold:7}),h=f.config(),a=1)}var f,a,T,i,c,y,h,v,r={version:"3.12.5",name:"scrollTo",rawVars:1,register:function register(e){f=e,u()},init:function init(e,r,l,s,i){a||u();var p=this,c=f.getProperty(e,"scrollSnapType");p.isWin=e===T,p.target=e,p.tween=l,r=function _clean(e,t,r,l){if(o(e)&&(e=e(t,r,l)),"object"!=typeof e)return n(e)&&"max"!==e&&"="!==e.charAt(1)?{x:e,y:e}:{y:e};if(e.nodeType)return{y:e,x:e};var s,i={};for(s in e)i[s]="onAutoKill"!==s&&o(e[s])?e[s](t,r,l):e[s];return i}(r,s,e,i),p.vars=r,p.autoKill=!!r.autoKill,p.getX=q(e,"x"),p.getY=q(e,"y"),p.x=p.xPrev=p.getX(),p.y=p.yPrev=p.getY(),v=v||f.core.globals().ScrollTrigger,"smooth"===f.getProperty(e,"scrollBehavior")&&f.set(e,{scrollBehavior:"auto"}),c&&"none"!==c&&(p.snap=1,p.snapInline=e.style.scrollSnapType,e.style.scrollSnapType="none"),null!=r.x?(p.add(p,"x",p.x,t(r.x,e,"x",p.x,r.offsetX||0),s,i),p._props.push("scrollTo_x")):p.skipX=1,null!=r.y?(p.add(p,"y",p.y,t(r.y,e,"y",p.y,r.offsetY||0),s,i),p._props.push("scrollTo_y")):p.skipY=1},render:function render(e,t){for(var o,n,r,l,s,i=t._pt,c=t.target,u=t.tween,f=t.autoKill,a=t.xPrev,y=t.yPrev,d=t.isWin,g=t.snap,x=t.snapInline;i;)i.r(e,i.d),i=i._next;o=d||!t.skipX?t.getX():a,r=(n=d||!t.skipY?t.getY():y)-y,l=o-a,s=h.autoKillThreshold,t.x<0&&(t.x=0),t.y<0&&(t.y=0),f&&(!t.skipX&&(s<l||l<-s)&&o<p(c,"x")&&(t.skipX=1),!t.skipY&&(s<r||r<-s)&&n<p(c,"y")&&(t.skipY=1),t.skipX&&t.skipY&&(u.kill(),t.vars.onAutoKill&&t.vars.onAutoKill.apply(u,t.vars.onAutoKillParams||[]))),d?T.scrollTo(t.skipX?o:t.x,t.skipY?n:t.y):(t.skipY||(c.scrollTop=t.y),t.skipX||(c.scrollLeft=t.x)),!g||1!==e&&0!==e||(n=c.scrollTop,o=c.scrollLeft,x?c.style.scrollSnapType=x:c.style.removeProperty("scroll-snap-type"),c.scrollTop=n+1,c.scrollLeft=o+1,c.scrollTop=n,c.scrollLeft=o),t.xPrev=t.x,t.yPrev=t.y,v&&v.update()},kill:function kill(e){var t="scrollTo"===e,o=this._props.indexOf(e);return!t&&"scrollTo_x"!==e||(this.skipX=1),!t&&"scrollTo_y"!==e||(this.skipY=1),-1<o&&this._props.splice(o,1),!this._props.length}};r.max=p,r.getOffset=s,r.buildGetter=q,m()&&f.registerPlugin(r),e.ScrollToPlugin=r,e.default=r;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});
@@ -0,0 +1,10 @@
/*!
* ScrollTrigger 3.12.5
* https://gsap.com
*
* @license Copyright 2024, GreenSock. All rights reserved.
* Subject to the terms at https://gsap.com/standard-license or for Club GSAP members, the agreement issued with that membership.
* @author: Jack Doyle, jack@greensock.com
*/
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function _defineProperties(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function r(){return Ce||"undefined"!=typeof window&&(Ce=window.gsap)&&Ce.registerPlugin&&Ce}function z(e,t){return~Le.indexOf(e)&&Le[Le.indexOf(e)+1][t]}function A(e){return!!~t.indexOf(e)}function B(e,t,r,n,o){return e.addEventListener(t,r,{passive:!1!==n,capture:!!o})}function C(e,t,r,n){return e.removeEventListener(t,r,!!n)}function F(){return De&&De.isPressed||Ie.cache++}function G(r,n){function dd(e){if(e||0===e){o&&(Se.history.scrollRestoration="manual");var t=De&&De.isPressed;e=dd.v=Math.round(e)||(De&&De.iOS?1:0),r(e),dd.cacheID=Ie.cache,t&&i("ss",e)}else(n||Ie.cache!==dd.cacheID||i("ref"))&&(dd.cacheID=Ie.cache,dd.v=r());return dd.v+dd.offset}return dd.offset=0,r&&dd}function J(e,t){return(t&&t._ctx&&t._ctx.selector||Ce.utils.toArray)(e)[0]||("string"==typeof e&&!1!==Ce.config().nullTargetWarn?console.warn("Element not found:",e):null)}function K(t,e){var r=e.s,n=e.sc;A(t)&&(t=ke.scrollingElement||Pe);var o=Ie.indexOf(t),i=n===Fe.sc?1:2;~o||(o=Ie.push(t)-1),Ie[o+i]||B(t,"scroll",F);var a=Ie[o+i],s=a||(Ie[o+i]=G(z(t,r),!0)||(A(t)?n:G(function(e){return arguments.length?t[r]=e:t[r]})));return s.target=t,a||(s.smooth="smooth"===Ce.getProperty(t,"scrollBehavior")),s}function L(e,t,o){function Cd(e,t){var r=ze();t||n<r-s?(a=i,i=e,l=s,s=r):o?i+=e:i=a+(e-a)/(r-l)*(s-l)}var i=e,a=e,s=ze(),l=s,n=t||50,c=Math.max(500,3*n);return{update:Cd,reset:function reset(){a=i=o?0:i,l=s=0},getVelocity:function getVelocity(e){var t=l,r=a,n=ze();return!e&&0!==e||e===i||Cd(e),s===l||c<n-l?0:(i+(o?r:-r))/((o?n:s)-t)*1e3}}}function M(e,t){return t&&!e._gsapAllow&&e.preventDefault(),e.changedTouches?e.changedTouches[0]:e}function N(e){var t=Math.max.apply(Math,e),r=Math.min.apply(Math,e);return Math.abs(t)>=Math.abs(r)?t:r}function O(){(Ae=Ce.core.globals().ScrollTrigger)&&Ae.core&&function _integrate(){var e=Ae.core,r=e.bridge||{},t=e._scrollers,n=e._proxies;t.push.apply(t,Ie),n.push.apply(n,Le),Ie=t,Le=n,i=function _bridge(e,t){return r[e](t)}}()}function P(e){return Ce=e||r(),!Te&&Ce&&"undefined"!=typeof document&&document.body&&(Se=window,Pe=(ke=document).documentElement,Me=ke.body,t=[Se,ke,Pe,Me],Ce.utils.clamp,Be=Ce.core.context||function(){},Oe="onpointerenter"in Me?"pointer":"mouse",Ee=k.isTouch=Se.matchMedia&&Se.matchMedia("(hover: none), (pointer: coarse)").matches?1:"ontouchstart"in Se||0<navigator.maxTouchPoints||0<navigator.msMaxTouchPoints?2:0,Re=k.eventTypes=("ontouchstart"in Pe?"touchstart,touchmove,touchcancel,touchend":"onpointerdown"in Pe?"pointerdown,pointermove,pointercancel,pointerup":"mousedown,mousemove,mouseup,mouseup").split(","),setTimeout(function(){return o=0},500),O(),Te=1),Te}var Ce,Te,Se,ke,Pe,Me,Ee,Oe,Ae,t,De,Re,Be,o=1,qe=[],Ie=[],Le=[],ze=Date.now,i=function _bridge(e,t){return t},n="scrollLeft",a="scrollTop",Ye={s:n,p:"left",p2:"Left",os:"right",os2:"Right",d:"width",d2:"Width",a:"x",sc:G(function(e){return arguments.length?Se.scrollTo(e,Fe.sc()):Se.pageXOffset||ke[n]||Pe[n]||Me[n]||0})},Fe={s:a,p:"top",p2:"Top",os:"bottom",os2:"Bottom",d:"height",d2:"Height",a:"y",op:Ye,sc:G(function(e){return arguments.length?Se.scrollTo(Ye.sc(),e):Se.pageYOffset||ke[a]||Pe[a]||Me[a]||0})};Ye.op=Fe,Ie.cache=0;var k=(Observer.prototype.init=function init(e){Te||P(Ce)||console.warn("Please gsap.registerPlugin(Observer)"),Ae||O();var o=e.tolerance,a=e.dragMinimum,t=e.type,i=e.target,r=e.lineHeight,n=e.debounce,s=e.preventDefault,l=e.onStop,c=e.onStopDelay,u=e.ignore,f=e.wheelSpeed,d=e.event,p=e.onDragStart,g=e.onDragEnd,h=e.onDrag,v=e.onPress,b=e.onRelease,m=e.onRight,y=e.onLeft,x=e.onUp,w=e.onDown,_=e.onChangeX,T=e.onChangeY,S=e.onChange,k=e.onToggleX,E=e.onToggleY,D=e.onHover,R=e.onHoverEnd,q=e.onMove,I=e.ignoreCheck,z=e.isNormalizer,Y=e.onGestureStart,H=e.onGestureEnd,X=e.onWheel,W=e.onEnable,V=e.onDisable,U=e.onClick,j=e.scrollSpeed,G=e.capture,Q=e.allowClicks,Z=e.lockAxis,$=e.onLockAxis;function cf(){return xe=ze()}function df(e,t){return(se.event=e)&&u&&~u.indexOf(e.target)||t&&he&&"touch"!==e.pointerType||I&&I(e,t)}function ff(){var e=se.deltaX=N(me),t=se.deltaY=N(ye),r=Math.abs(e)>=o,n=Math.abs(t)>=o;S&&(r||n)&&S(se,e,t,me,ye),r&&(m&&0<se.deltaX&&m(se),y&&se.deltaX<0&&y(se),_&&_(se),k&&se.deltaX<0!=le<0&&k(se),le=se.deltaX,me[0]=me[1]=me[2]=0),n&&(w&&0<se.deltaY&&w(se),x&&se.deltaY<0&&x(se),T&&T(se),E&&se.deltaY<0!=ce<0&&E(se),ce=se.deltaY,ye[0]=ye[1]=ye[2]=0),(ne||re)&&(q&&q(se),re&&(h(se),re=!1),ne=!1),ie&&!(ie=!1)&&$&&$(se),oe&&(X(se),oe=!1),ee=0}function gf(e,t,r){me[r]+=e,ye[r]+=t,se._vx.update(e),se._vy.update(t),n?ee=ee||requestAnimationFrame(ff):ff()}function hf(e,t){Z&&!ae&&(se.axis=ae=Math.abs(e)>Math.abs(t)?"xLine truncated