143 lines
4.1 KiB
JavaScript
143 lines
4.1 KiB
JavaScript
// ping
|
|
async function checkStatus() {
|
|
const cards = document.querySelectorAll(".card");
|
|
|
|
cards.forEach(async (card) => {
|
|
const url = card.getAttribute("data-url");
|
|
const dot = card.querySelector(".card__decor.card__decor--tr");
|
|
|
|
try {
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
|
|
await fetch(url, {
|
|
mode: "no-cors",
|
|
cache: "no-cache",
|
|
signal: controller.signal,
|
|
});
|
|
|
|
dot.classList.remove("status-offline");
|
|
dot.classList.add("status-online");
|
|
clearTimeout(timeoutId);
|
|
} catch (e) {
|
|
dot.classList.remove("status-online");
|
|
dot.classList.add("status-offline");
|
|
}
|
|
});
|
|
}
|
|
|
|
checkStatus();
|
|
setInterval(checkStatus, 300000);
|
|
|
|
// 🔴 테스트 ON
|
|
// const TEST_ONLY_SECOND_CARD = true;
|
|
// 🟢 테스트 OFF
|
|
const TEST_ONLY_SECOND_CARD = false;
|
|
|
|
// bolt animation
|
|
function startBoltAction() {
|
|
let allBolts = [];
|
|
|
|
if (TEST_ONLY_SECOND_CARD) {
|
|
// 🔧 2번째 카드만 선택 (0-based index)
|
|
const secondCard = document.querySelectorAll(".card")[1];
|
|
if (!secondCard) return;
|
|
|
|
allBolts = secondCard.querySelectorAll(".card__decor");
|
|
} else {
|
|
// 🔹 전체 카드 대상
|
|
allBolts = document.querySelectorAll(".card__decor");
|
|
}
|
|
|
|
if (allBolts.length === 0) return;
|
|
|
|
const availableBolts = Array.from(allBolts).filter(
|
|
(bolt) => !bolt.classList.contains("card__decor-acting")
|
|
);
|
|
|
|
if (availableBolts.length > 0) {
|
|
const randomBolt =
|
|
availableBolts[Math.floor(Math.random() * availableBolts.length)];
|
|
|
|
const baseRot =
|
|
randomBolt.style.getPropertyValue("--r") || "0deg";
|
|
|
|
randomBolt.style.setProperty("--r", baseRot);
|
|
|
|
const BOLT_ANIMATION_DURATION = 16000; // CSS와 반드시 일치
|
|
|
|
randomBolt.classList.add("card__decor-acting");
|
|
|
|
setTimeout(() => {
|
|
randomBolt.classList.remove("card__decor-acting");
|
|
}, BOLT_ANIMATION_DURATION);
|
|
}
|
|
|
|
const nextInterval = Math.random() * 4000 + 3000;
|
|
setTimeout(startBoltAction, nextInterval);
|
|
}
|
|
|
|
window.addEventListener("load", () => {
|
|
setTimeout(startBoltAction, 2000);
|
|
});
|
|
|
|
// 나사 랜덤 각도 부연
|
|
document.querySelectorAll('.card').forEach((card, cardIndex) => {
|
|
card.querySelectorAll('.card__decor').forEach((decor, decorIndex) => {
|
|
|
|
// -30 ~ 150 정도가 나사 느낌 제일 안정적
|
|
const angle = Math.floor(Math.random() * 180) - 30;
|
|
|
|
decor.style.setProperty('--r', `${angle}deg`);
|
|
});
|
|
});
|
|
|
|
// 1. 사용할 테마 리스트
|
|
const themes = ['semi-nova', 'nova', 'semi-solaris', 'solaris'];
|
|
const STORAGE_KEY = 'selected-theme';
|
|
|
|
// 2. 초기 로드 시 테마 적용 (기본값: semi-nova)
|
|
const savedTheme = localStorage.getItem(STORAGE_KEY) || themes[0];
|
|
document.body.classList.add(savedTheme);
|
|
|
|
window.addEventListener('keydown', (e) => {
|
|
// OS별 수정 키 판별 (Mac: Command, Win: Control)
|
|
const isMac = navigator.platform.toUpperCase().includes('MAC');
|
|
const isModifierPressed = isMac ? e.metaKey : e.ctrlKey;
|
|
|
|
// 방향키 좌/우 확인
|
|
const isLeft = e.code === 'ArrowLeft';
|
|
const isRight = e.code === 'ArrowRight';
|
|
|
|
if (
|
|
isModifierPressed &&
|
|
(isLeft || isRight) &&
|
|
// 입력창 안에서 커서 이동을 방해하지 않도록 체크
|
|
!['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName) &&
|
|
!document.activeElement?.isContentEditable // 에디터 영역 대응
|
|
) {
|
|
e.preventDefault();
|
|
|
|
// 현재 인덱스 찾기
|
|
let currentIndex = themes.findIndex(t => document.body.classList.contains(t));
|
|
if (currentIndex === -1) currentIndex = 0;
|
|
|
|
// 3. 좌/우 방향에 따른 인덱스 순환
|
|
if (isRight) {
|
|
// 오른쪽 화살표: 다음 테마
|
|
currentIndex = (currentIndex + 1) % themes.length;
|
|
} else if (isLeft) {
|
|
// 왼쪽 화살표: 이전 테마
|
|
currentIndex = (currentIndex - 1 + themes.length) % themes.length;
|
|
}
|
|
|
|
const nextTheme = themes[currentIndex];
|
|
|
|
// 4. 클래스 교체 및 저장
|
|
themes.forEach(t => document.body.classList.remove(t));
|
|
document.body.classList.add(nextTheme);
|
|
localStorage.setItem(STORAGE_KEY, nextTheme);
|
|
|
|
console.log(`Current Theme: ${nextTheme}`);
|
|
}
|
|
}); |