편집 요약 없음 |
편집 요약 없음 |
||
| 11번째 줄: | 11번째 줄: | ||
(function() { | (function() { | ||
var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100; | var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100; | ||
var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') | var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100; | ||
var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false'; | var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false'; | ||
transitionSound.volume = sfxOn ? master * sfx : 0; | transitionSound.volume = sfxOn ? master * sfx : 0; | ||
| 18번째 줄: | 18번째 줄: | ||
function playStaticSound() { | function playStaticSound() { | ||
var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100; | var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100; | ||
var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') | var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100; | ||
var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false'; | var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false'; | ||
if (!sfxOn) return; | if (!sfxOn) return; | ||
| 30번째 줄: | 30번째 줄: | ||
var langData = document.getElementById('clbi-lang-data'); | var langData = document.getElementById('clbi-lang-data'); | ||
return langData ? (langData.getAttribute('data-lang') || 'ko') : 'ko'; | return langData ? (langData.getAttribute('data-lang') || 'ko') : 'ko'; | ||
} | |||
function normalizePageName(value) { | |||
return String(value || '') | |||
.split('?')[0] | |||
.replace(/^\/index\.php\//, '') | |||
.replace(/_/g, ' ') | |||
.trim(); | |||
} | |||
function buildWikiPath(title) { | |||
return '/index.php/' + encodeURI(String(title || '').replace(/ /g, '_')); | |||
} | } | ||
| 38번째 줄: | 50번째 줄: | ||
return; | return; | ||
} | } | ||
var langData = document.getElementById('clbi-lang-data'); | var langData = document.getElementById('clbi-lang-data'); | ||
var currentLang = getCurrentLang(); | var currentLang = getCurrentLang(); | ||
| 43번째 줄: | 56번째 줄: | ||
var names = (window.LANG_NAMES && window.LANG_NAMES[currentLang]) ? window.LANG_NAMES[currentLang] : window.LANG_NAMES['ko']; | var names = (window.LANG_NAMES && window.LANG_NAMES[currentLang]) ? window.LANG_NAMES[currentLang] : window.LANG_NAMES['ko']; | ||
var cl = (window.CAT_LINKS && window.CAT_LINKS[currentLang]) ? window.CAT_LINKS[currentLang] : window.CAT_LINKS['ko']; | var cl = (window.CAT_LINKS && window.CAT_LINKS[currentLang]) ? window.CAT_LINKS[currentLang] : window.CAT_LINKS['ko']; | ||
var allLangs = langData ? { | var allLangs = langData ? { | ||
ko: langData.getAttribute('data-ko') || '', | ko: langData.getAttribute('data-ko') || '', | ||
| 51번째 줄: | 65번째 줄: | ||
$('#clbi-lang-current').text(names[currentLang]); | $('#clbi-lang-current').text(names[currentLang]); | ||
var langHtml = ''; | var langHtml = ''; | ||
$.each(['ko', 'en', 'zh', 'ja'], function(i, lang) { | $.each(['ko', 'en', 'zh', 'ja'], function(i, lang) { | ||
if (lang === currentLang) return; | if (lang === currentLang) return; | ||
var target = allLangs[lang]; | var target = allLangs[lang]; | ||
if (target) { | if (target) { | ||
langHtml += '<div class="clbi-lang-link"><a href=" | langHtml += '<div class="clbi-lang-link"><a href="' + buildWikiPath(target) + '?uselang=' + lang + '">' + names[lang] + '</a></div>'; | ||
} else { | } else { | ||
langHtml += '<div class="clbi-lang-wip">' + names[lang] + '</div>'; | langHtml += '<div class="clbi-lang-wip">' + names[lang] + '</div>'; | ||
| 73번째 줄: | 89번째 줄: | ||
$('#clbi-title-playlist').text(t.playlist); | $('#clbi-title-playlist').text(t.playlist); | ||
$('#clbi-cat-main a').attr('href', | $('#clbi-cat-main a').attr('href', buildWikiPath(cl.main)); | ||
$('#clbi-cat-main .clbi-cat-label').text(t.mainMenu); | $('#clbi-cat-main .clbi-cat-label').text(t.mainMenu); | ||
$('#clbi-cat-nations a').attr('href', | |||
$('#clbi-cat-nations a').attr('href', buildWikiPath(cl.nations)); | |||
$('#clbi-cat-nations .clbi-cat-label').text(t.nations); | $('#clbi-cat-nations .clbi-cat-label').text(t.nations); | ||
$('#clbi-cat-corporations a').attr('href', | |||
$('#clbi-cat-corporations a').attr('href', buildWikiPath(cl.corporations)); | |||
$('#clbi-cat-corporations .clbi-cat-label').text(t.corporations); | $('#clbi-cat-corporations .clbi-cat-label').text(t.corporations); | ||
$('#clbi-cat-military a').attr('href', | |||
$('#clbi-cat-military a').attr('href', buildWikiPath(cl.military)); | |||
$('#clbi-cat-military .clbi-cat-label').text(t.military); | $('#clbi-cat-military .clbi-cat-label').text(t.military); | ||
$('#clbi-cat-history a').attr('href', | |||
$('#clbi-cat-history a').attr('href', buildWikiPath(cl.history)); | |||
$('#clbi-cat-history .clbi-cat-label').text(t.history); | $('#clbi-cat-history .clbi-cat-label').text(t.history); | ||
$('#clbi-cat-personnel a').attr('href', | |||
$('#clbi-cat-personnel a').attr('href', buildWikiPath(cl.personnel)); | |||
$('#clbi-cat-personnel .clbi-cat-label').text(t.personnel); | $('#clbi-cat-personnel .clbi-cat-label').text(t.personnel); | ||
$('#clbi-btn-contribution').text(t.contribution); | $('#clbi-btn-contribution').text(t.contribution); | ||
$('#clbi-btn-watchlist').text(t.watchlist); | $('#clbi-btn-watchlist').text(t.watchlist); | ||
$('#clbi-btn-preferences').text(t.preferences); | $('#clbi-btn-preferences').text(t.preferences); | ||
| 93번째 줄: | 113번째 줄: | ||
$('#clbi-btn-login').text(t.login); | $('#clbi-btn-login').text(t.login); | ||
var pageName = mw.config.get('wgPageName'); | var pageName = normalizePageName(mw.config.get('wgPageName')); | ||
var specialPage = String(mw.config.get('wgCanonicalSpecialPageName') || ''); | |||
$('.clbi-cat-btn').removeClass('clbi-cat-active'); | $('.clbi-cat-btn').removeClass('clbi-cat-active'); | ||
$.each(['main', 'nations', 'corporations', 'military', 'history', 'personnel'], function(i, key) { | $.each(['main', 'nations', 'corporations', 'military', 'history', 'personnel'], function(i, key) { | ||
if (cl[key] && pageName === cl[key] | if (cl[key] && pageName === normalizePageName(cl[key])) { | ||
$('#clbi-cat-' + key).addClass('clbi-cat-active'); | $('#clbi-cat-' + key).addClass('clbi-cat-active'); | ||
} | } | ||
}); | }); | ||
$('.clbi-user-btn').removeClass('clbi-user-btn-active'); | |||
if ( | |||
specialPage === 'Contributions' || | |||
specialPage === '기여' || | |||
pageName.indexOf('특수:기여') === 0 || | |||
pageName.indexOf('Special:Contributions') === 0 | |||
) { | |||
$('#clbi-btn-contribution').addClass('clbi-user-btn-active'); | |||
} | |||
if (specialPage === 'Watchlist') { | |||
$('#clbi-btn-watchlist').addClass('clbi-user-btn-active'); | |||
} | |||
if ( | |||
specialPage === '설정' || | |||
pageName === '특수:설정' || | |||
pageName === 'Special:설정' | |||
) { | |||
$('#clbi-btn-preferences').addClass('clbi-user-btn-active'); | |||
} | |||
$('.toggleBtn').each(function() { | $('.toggleBtn').each(function() { | ||
var btn = $(this); | var btn = $(this); | ||
| 127번째 줄: | 160번째 줄: | ||
var specialPage = mw.config.get('wgCanonicalSpecialPageName'); | var specialPage = mw.config.get('wgCanonicalSpecialPageName'); | ||
if (specialPage === 'Preferences') return; | if (specialPage === 'Preferences') return; | ||
var pageName = mw.config.get('wgPageName'); | |||
var pageName = normalizePageName(mw.config.get('wgPageName')); | |||
if (pageName === '대문') { | if (pageName === '대문') { | ||
$('.liberty-content-header').css('display', 'none'); | $('.liberty-content-header').css('display', 'none'); | ||
| 147번째 줄: | 182번째 줄: | ||
$('#clbi-main-logo').remove(); | $('#clbi-main-logo').remove(); | ||
} | } | ||
updateSidebar(); | updateSidebar(); | ||
} | } | ||
| 164번째 줄: | 200번째 줄: | ||
var isLoggedIn = username !== null; | var isLoggedIn = username !== null; | ||
var avatarSrc = isLoggedIn | var avatarSrc = isLoggedIn | ||
? '/index.php?title=특수:Redirect/file/Pfp-' + username + '.png' | |||
: '/index.php?title=특수:Redirect/file/Pfp-default.png'; | |||
var userBox; | var userBox; | ||
| 185번째 줄: | 221번째 줄: | ||
'<div class="clbi-right-box">' + | '<div class="clbi-right-box">' + | ||
'<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' + | '<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' + | ||
'<img src="/index.php?title=특수:Redirect/file/ | '<img src="/index.php?title=특수:Redirect/file/Pfp-default.png" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' + | ||
'<span style="font-size:13px;font-weight:700;color:#E2E2E2;">Guest</span>' + | '<span style="font-size:13px;font-weight:700;color:#E2E2E2;">Guest</span>' + | ||
'</div>' + | '</div>' + | ||
| 228번째 줄: | 264번째 줄: | ||
if (query) window.location.href = '/index.php?search=' + encodeURIComponent(query); | if (query) window.location.href = '/index.php?search=' + encodeURIComponent(query); | ||
}); | }); | ||
$('#clbi-search-input').keypress(function(e) { | $('#clbi-search-input').keypress(function(e) { | ||
if (e.which == 13) $('#clbi-search-btn').click(); | if (e.which === 13) $('#clbi-search-btn').click(); | ||
}); | }); | ||
| 235번째 줄: | 272번째 줄: | ||
var items = data.query.recentchanges; | var items = data.query.recentchanges; | ||
var html = ''; | var html = ''; | ||
$.each(items, function(i, item) { | $.each(items, function(i, item) { | ||
var label = timeAgo(item.timestamp); | var label = timeAgo(item.timestamp); | ||
| 245번째 줄: | 283번째 줄: | ||
'</div>'; | '</div>'; | ||
}); | }); | ||
$('#clbi-recent-list').html(html); | $('#clbi-recent-list').html(html); | ||
| 267번째 줄: | 306번째 줄: | ||
// 왼쪽 사이드바 | // 왼쪽 사이드바 | ||
if ($('#clbi-left-sidebar').length === 0) { | if ($('#clbi-left-sidebar').length === 0) { | ||
var leftSidebar = '<div id="clbi-left-sidebar">' + | var leftSidebar = | ||
'<div id="clbi-left-sidebar">' + | |||
'<div class="clbi-left-box">' + | |||
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-001)"></span> <span id="clbi-title-language">언어</span></div>' + | |||
'<div class="clbi-left-content clbi-lang-box" id="clbi-lang-box">' + | |||
'<div class="clbi-lang-current" id="clbi-lang-current">한국어</div>' + | |||
'<div id="clbi-lang-list" style="display:none;"></div>' + | |||
'</div>' + | |||
'</div>' + | |||
'<div class="clbi-left-box">' + | |||
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> <span id="clbi-title-categories">카테고리</span></div>' + | |||
'<div class="clbi-left-content clbi-cat-box">' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-main"><a href="/index.php/대문"><div class="clbi-cat-text"><span class="clbi-cat-label">메인 메뉴</span><span class="clbi-cat-sub">MAIN MENU</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-nations"><a href="/index.php/국가_및_조합"><div class="clbi-cat-text"><span class="clbi-cat-label">국가 및 조합</span><span class="clbi-cat-sub">NATIONS & FACTIONS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-corporations"><a href="/index.php/기업_및_공동체"><div class="clbi-cat-text"><span class="clbi-cat-label">기업 및 공동체</span><span class="clbi-cat-sub">CORPORATIONS & COMMUNITIES</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-military"><a href="/index.php/군_정치집단"><div class="clbi-cat-text"><span class="clbi-cat-label">군, 정치집단</span><span class="clbi-cat-sub">MILITARY & POLITICS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-history"><a href="/index.php/역사적_사건"><div class="clbi-cat-text"><span class="clbi-cat-label">역사적 사건</span><span class="clbi-cat-sub">HISTORICAL EVENTS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'<div class="clbi-cat-btn" id="clbi-cat-personnel"><a href="/index.php/인물"><div class="clbi-cat-text"><span class="clbi-cat-label">인물</span><span class="clbi-cat-sub">KEY PERSONNEL</span></div></a><div class="clbi-cat-arrow">▶</div></div>' + | |||
'</div>' + | |||
'</div>' + | |||
'<div class="clbi-left-box">' + | |||
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> <span id="clbi-title-links">링크</span></div>' + | |||
'<div class="clbi-left-content clbi-link-box">' + | |||
'<ul>' + | |||
'<li><a href="https://discord.gg/ctaeJ9d3Q5" target="_blank">Discord</a></li>' + | |||
'<li><a href="https://www.youtube.com/@nxdsxn" target="_blank">YouTube</a></li>' + | |||
'<li><a href="https://x.com/nxd_sxn" target="_blank">X</a></li>' + | |||
'</ul>' + | |||
'</div>' + | |||
'</div>' + | |||
'</div>'; | '</div>'; | ||
| 310번째 줄: | 353번째 줄: | ||
applyMainPageStyle(); | applyMainPageStyle(); | ||
mw.loader.using(['mediawiki.api']).then(function() { | mw.loader.using(['mediawiki.api']).then(function() { | ||
setTimeout(function() { | setTimeout(function() { | ||
| 331번째 줄: | 375번째 줄: | ||
if (window._spaInitialized) return; | if (window._spaInitialized) return; | ||
window._spaInitialized = true; | window._spaInitialized = true; | ||
function isInternal(url) { | function isInternal(url) { | ||
var a = document.createElement('a'); | var a = document.createElement('a'); | ||
| 337번째 줄: | 382번째 줄: | ||
} | } | ||
function loadPage(url) { | function loadPage(url) { | ||
fetch(url) | fetch(url) | ||
.then(function(res) { return res.text(); }) | .then(function(res) { return res.text(); }) | ||
| 350번째 줄: | 395번째 줄: | ||
if (src.indexOf('wgNamespaceNumber') !== -1) { | if (src.indexOf('wgNamespaceNumber') !== -1) { | ||
var match = src.match(/"wgNamespaceNumber":(-?\d+)/); | var match = src.match(/"wgNamespaceNumber":(-?\d+)/); | ||
if (match) mw.config.set('wgNamespaceNumber', parseInt(match[1])); | if (match) mw.config.set('wgNamespaceNumber', parseInt(match[1], 10)); | ||
var matchTitle = src.match(/"wgTitle":"([^"]+)"/); | var matchTitle = src.match(/"wgTitle":"([^"]+)"/); | ||
if (matchTitle) mw.config.set('wgTitle', matchTitle[1]); | if (matchTitle) mw.config.set('wgTitle', matchTitle[1]); | ||
var matchPage = src.match(/"wgPageName":"([^"]+)"/); | var matchPage = src.match(/"wgPageName":"([^"]+)"/); | ||
if (matchPage) mw.config.set('wgPageName', matchPage[1] | if (matchPage) mw.config.set('wgPageName', matchPage[1]); | ||
var matchSpecial = src.match(/"wgCanonicalSpecialPageName":"([^"]+)"/); | var matchSpecial = src.match(/"wgCanonicalSpecialPageName":"([^"]+)"/); | ||
if (matchSpecial) { | if (matchSpecial) { | ||
| 368번째 줄: | 416번째 줄: | ||
var newTitle = doc.querySelector('.mw-page-title-main'); | var newTitle = doc.querySelector('.mw-page-title-main'); | ||
var newHead = doc.querySelector('title'); | var newHead = doc.querySelector('title'); | ||
var newHeader = doc.querySelector('.liberty-content-header'); | |||
if (newContent) { | if (newContent) { | ||
$('.liberty-content-main').html(newContent.innerHTML); | $('.liberty-content-main').html(newContent.innerHTML); | ||
| 373번째 줄: | 423번째 줄: | ||
$('body').removeClass('page-loading'); | $('body').removeClass('page-loading'); | ||
} | } | ||
if (newTitle) { | if (newTitle) { | ||
$('.mw-page-title-main').html(newTitle.innerHTML); | $('.mw-page-title-main').html(newTitle.innerHTML); | ||
} | } | ||
if (newHead) { | if (newHead) { | ||
document.title = newHead.textContent; | document.title = newHead.textContent; | ||
} | } | ||
if (newHeader) { | if (newHeader) { | ||
$('.liberty-content-header').html(newHeader.innerHTML); | $('.liberty-content-header').html(newHeader.innerHTML); | ||
| 387번째 줄: | 439번째 줄: | ||
$('#clbi-lang-list').hide(); | $('#clbi-lang-list').hide(); | ||
$('#clbi-lang-current').css('margin-bottom', '0'); | $('#clbi-lang-current').css('margin-bottom', '0'); | ||
mw.hook('wikipage.content').fire($('.liberty-content-main')); | mw.hook('wikipage.content').fire($('.liberty-content-main')); | ||
applyMainPageStyle(); | applyMainPageStyle(); | ||
mw.loader.using(['mediawiki.api']).then(function() { | mw.loader.using(['mediawiki.api']).then(function() { | ||
initProfile(); | initProfile(); | ||
| 428번째 줄: | 482번째 줄: | ||
// 펼접 토글 | // 펼접 토글 | ||
$(function() { | $(function() { | ||
$(document).on('click', '.toggleBtn', function() { | $(document).on('click', '.toggleBtn', function() { | ||
var targetId = $(this).data('target'); | |||
var target = $('#' + targetId); | |||
var scrollY = window.scrollY; | |||
var lang = getCurrentLang(); | |||
var t = (window.LANG && window.LANG[lang]) ? window.LANG[lang] : (window.LANG ? window.LANG['ko'] : { expand: '펼치기', collapse: '접기' }); | |||
if (target.hasClass('folding-open')) { | |||
target.css('max-height', target[0].scrollHeight + 'px'); | |||
target[0].offsetHeight; | |||
target.css('max-height', '0px'); | |||
target.removeClass('folding-open'); | |||
$(this).text(t.expand); | |||
} else { | |||
target.css('max-height', '0px'); | |||
target[0].offsetHeight; | |||
target.css('max-height', target[0].scrollHeight + 'px'); | |||
target.addClass('folding-open'); | |||
$(this).text(t.collapse); | |||
} | |||
window.scrollTo(0, scrollY); | |||
}); | |||
} | |||
}); | }); | ||
| 472번째 줄: | 517번째 줄: | ||
renderProfile(profileUser); | renderProfile(profileUser); | ||
} | } | ||
if (mw.config.get('wgCanonicalSpecialPageName') === '사용자정보') { | if (mw.config.get('wgCanonicalSpecialPageName') === '사용자정보') { | ||
initUserProfilePage(); | initUserProfilePage(); | ||
| 483번째 줄: | 529번째 줄: | ||
list: 'users', | list: 'users', | ||
ususers: username, | ususers: username, | ||
usprop: 'editcount' | usprop: 'editcount' | ||
}).then(function(data) { | }).then(function(data) { | ||
var user = data.query.users[0]; | var user = data.query.users[0]; | ||
| 543번째 줄: | 589번째 줄: | ||
role: profile.role || '', | role: profile.role || '', | ||
bio: profile.bio || '', | bio: profile.bio || '', | ||
badges: profile.badges || '' | badges: profile.badges || '' | ||
}); | }); | ||
}).fail(function() { | }).fail(function() { | ||
updateProfileFields(card, { name: '', discord: '', role: '', bio: '', badges: '' }); | updateProfileFields(card, { | ||
name: '', | |||
discord: '', | |||
role: '', | |||
bio: '', | |||
badges: '' | |||
}); | |||
}); | }); | ||
} | } | ||
| 559번째 줄: | 611번째 줄: | ||
if (nameEl) nameEl.textContent = data.name || ''; | if (nameEl) nameEl.textContent = data.name || ''; | ||
if (roleEl) roleEl.textContent = data.role || ''; | if (roleEl) roleEl.textContent = data.role || ''; | ||
if (discordEl | if (discordEl) discordEl.textContent = data.discord ? ('디스코드: ' + data.discord) : ''; | ||
if (bioEl) bioEl.textContent = data.bio || ''; | if (bioEl) bioEl.textContent = data.bio || ''; | ||
if (badgesEl | |||
if (badgesEl) { | |||
if (data.badges) { | |||
var badges = data.badges.split(','); | |||
var html = ''; | |||
for (var i = 0; i < badges.length; i++) { | |||
html += '<span class="clbi-badge">' + badges[i].trim() + '</span>'; | |||
} | |||
badgesEl.innerHTML = html; | |||
} else { | |||
badgesEl.innerHTML = ''; | |||
} | } | ||
} | } | ||
} | } | ||
| 575번째 줄: | 632번째 줄: | ||
var saveBtn = document.getElementById('pref-save'); | var saveBtn = document.getElementById('pref-save'); | ||
if (!saveBtn) return; | if (!saveBtn) return; | ||
var adminEl = document.getElementById('pref-is-admin'); | var adminEl = document.getElementById('pref-is-admin'); | ||
var isAdmin = adminEl && adminEl.getAttribute('data-admin') === '1'; | var isAdmin = adminEl && adminEl.getAttribute('data-admin') === '1'; | ||
| 629번째 줄: | 687번째 줄: | ||
function openGallery() { | function openGallery() { | ||
gModal.style.display = 'flex'; | gModal.style.display = 'flex'; | ||
var username = mw.config.get('wgUserName'); | var username = mw.config.get('wgUserName'); | ||
api.get({ | api.get({ | ||
| 641번째 줄: | 699번째 줄: | ||
var page = pages[Object.keys(pages)[0]]; | var page = pages[Object.keys(pages)[0]]; | ||
if (!page.imageinfo || page.imageinfo.length === 0) return; | if (!page.imageinfo || page.imageinfo.length === 0) return; | ||
var historyEl = document.getElementById('clbi-gallery-history'); | var historyEl = document.getElementById('clbi-gallery-history'); | ||
var sectionEl = document.getElementById('clbi-gallery-history-section'); | var sectionEl = document.getElementById('clbi-gallery-history-section'); | ||
historyEl.innerHTML = ''; | historyEl.innerHTML = ''; | ||
page.imageinfo.forEach(function(info, idx) { | page.imageinfo.forEach(function(info, idx) { | ||
var wrap = document.createElement('div'); | var wrap = document.createElement('div'); | ||
wrap.style.cssText = 'position:relative;cursor:pointer;'; | wrap.style.cssText = 'position:relative;cursor:pointer;'; | ||
var img = document.createElement('img'); | var img = document.createElement('img'); | ||
img.src = info.url; | img.src = info.url; | ||
img.style.cssText = 'width:72px;height:72px;object-fit:cover;border-radius:8px;border:2px solid #444;flex-shrink:0;'; | img.style.cssText = 'width:72px;height:72px;object-fit:cover;border-radius:8px;border:2px solid #444;flex-shrink:0;'; | ||
if (idx === 0) { | if (idx === 0) { | ||
img.style.borderColor = '#854369'; | img.style.borderColor = '#854369'; | ||
| 657번째 줄: | 719번째 줄: | ||
wrap.appendChild(badge); | wrap.appendChild(badge); | ||
} | } | ||
img.addEventListener('mouseenter', function() { if (idx !== 0) img.style.borderColor = '#854369'; }); | |||
img.addEventListener('mouseleave', function() { if (idx !== 0) img.style.borderColor = '#444'; }); | img.addEventListener('mouseenter', function() { | ||
if (idx !== 0) img.style.borderColor = '#854369'; | |||
}); | |||
img.addEventListener('mouseleave', function() { | |||
if (idx !== 0) img.style.borderColor = '#444'; | |||
}); | |||
img.addEventListener('click', function() { | img.addEventListener('click', function() { | ||
fetch(info.url) | fetch(info.url) | ||
| 669번째 줄: | 738번째 줄: | ||
}); | }); | ||
}); | }); | ||
wrap.appendChild(img); | wrap.appendChild(img); | ||
historyEl.appendChild(wrap); | historyEl.appendChild(wrap); | ||
}); | }); | ||
sectionEl.style.display = 'block'; | sectionEl.style.display = 'block'; | ||
}); | }); | ||
| 679번째 줄: | 750번째 줄: | ||
cropImage.src = src; | cropImage.src = src; | ||
cModal.style.display = 'flex'; | cModal.style.display = 'flex'; | ||
if (cropper) { cropper.destroy(); cropper = null; } | |||
if (cropper) { | |||
cropper.destroy(); | |||
cropper = null; | |||
} | |||
setTimeout(function() { | setTimeout(function() { | ||
cropper = new Cropper(cropImage, { | cropper = new Cropper(cropImage, { | ||
| 687번째 줄: | 763번째 줄: | ||
autoCropArea: 0.8, | autoCropArea: 0.8, | ||
cropBoxResizable: true, | cropBoxResizable: true, | ||
cropBoxMovable: true | cropBoxMovable: true | ||
}); | }); | ||
}, 150); | }, 150); | ||
} | } | ||
document.getElementById('pref-pfp-btn').addEventListener('click', function() { | document.getElementById('pref-pfp-btn').addEventListener('click', function() { | ||
openGallery(); | openGallery(); | ||
}); | }); | ||
document.getElementById('clbi-gallery-upload-btn').addEventListener('click', function() { | document.getElementById('clbi-gallery-upload-btn').addEventListener('click', function() { | ||
pfpInput.click(); | pfpInput.click(); | ||
}); | }); | ||
document.getElementById('clbi-gallery-close').addEventListener('click', function() { | document.getElementById('clbi-gallery-close').addEventListener('click', function() { | ||
gModal.style.display = 'none'; | gModal.style.display = 'none'; | ||
}); | }); | ||
pfpInput.addEventListener('change', function() { | pfpInput.addEventListener('change', function() { | ||
var file = this.files[0]; | var file = this.files[0]; | ||
if (!file) return; | if (!file) return; | ||
gModal.style.display = 'none'; | gModal.style.display = 'none'; | ||
var reader = new FileReader(); | var reader = new FileReader(); | ||
| 719번째 줄: | 792번째 줄: | ||
}); | }); | ||
document.getElementById('clbi-crop-cancel').addEventListener('click', function() { | document.getElementById('clbi-crop-cancel').addEventListener('click', function() { | ||
cModal.style.display = 'none'; | cModal.style.display = 'none'; | ||
if (cropper) { cropper.destroy(); cropper = null; } | if (cropper) { | ||
cropper.destroy(); | |||
cropper = null; | |||
} | |||
pfpInput.value = ''; | pfpInput.value = ''; | ||
}); | }); | ||
document.getElementById('clbi-crop-confirm').addEventListener('click', function() { | document.getElementById('clbi-crop-confirm').addEventListener('click', function() { | ||
if (!cropper) return; | if (!cropper) return; | ||
var canvas = cropper.getCroppedCanvas({ width: 256, height: 256 }); | var canvas = cropper.getCroppedCanvas({ width: 256, height: 256 }); | ||
if (!canvas) return; | if (!canvas) return; | ||
canvas.toBlob(function(blob) { | canvas.toBlob(function(blob) { | ||
selectedFile = new File([blob], 'profile.png', { type: 'image/png' }); | selectedFile = new File([blob], 'profile.png', { type: 'image/png' }); | ||
| 741번째 줄: | 817번째 줄: | ||
}); | }); | ||
// 이메일 변경 | // 이메일 변경 | ||
var emailSaveBtn = document.getElementById('pref-email-save'); | var emailSaveBtn = document.getElementById('pref-email-save'); | ||
if (emailSaveBtn) { | if (emailSaveBtn) { | ||
emailSaveBtn.addEventListener('click', function() { | |||
var statusEl = document.getElementById('pref-email-status'); | |||
var newEmail = document.getElementById('pref-new-email').value; | |||
var password = document.getElementById('pref-email-password').value; | |||
if (!newEmail || !password) { | |||
statusEl.textContent = '이메일과 비밀번호를 입력해주세요.'; | |||
return; | |||
} | |||
statusEl.textContent = '변경 중...'; | |||
api.postWithToken('csrf', { | |||
action: 'changeemail', | |||
email: newEmail, | |||
password: password | |||
}).then(function() { | |||
statusEl.textContent = '✓ 이메일 변경됨'; | |||
document.getElementById('pref-new-email').value = ''; | |||
document.getElementById('pref-email-password').value = ''; | |||
setTimeout(function() { | |||
statusEl.textContent = ''; | |||
}, 3000); | |||
}).fail(function(code, data) { | |||
var msg = data && data.error && data.error.info ? data.error.info : '변경 실패'; | |||
statusEl.textContent = msg; | |||
}); | |||
}); | }); | ||
} | |||
} | |||
// 통합 저장 | // 통합 저장 | ||
| 788번째 줄: | 868번째 줄: | ||
var fields = ['name', 'discord', 'role', 'bio']; | var fields = ['name', 'discord', 'role', 'bio']; | ||
if (isAdmin) fields.push('badges'); | if (isAdmin) fields.push('badges'); | ||
for (var i = 0; i < fields.length; i++) { | for (var i = 0; i < fields.length; i++) { | ||
var el = document.getElementById('pref-' + fields[i]); | var el = document.getElementById('pref-' + fields[i]); | ||
| 794번째 줄: | 875번째 줄: | ||
action: 'options', | action: 'options', | ||
optionname: 'profile-' + fields[i], | optionname: 'profile-' + fields[i], | ||
optionvalue: el.value | optionvalue: el.value | ||
})); | })); | ||
} | } | ||
| 802번째 줄: | 883번째 줄: | ||
selectedFile = null; | selectedFile = null; | ||
document.getElementById('pref-pfp-btn').textContent = '사진 선택'; | document.getElementById('pref-pfp-btn').textContent = '사진 선택'; | ||
setTimeout(function() { statusEl.textContent = ''; }, 2000); | setTimeout(function() { | ||
statusEl.textContent = ''; | |||
}, 2000); | |||
}).fail(function() { | }).fail(function() { | ||
statusEl.textContent = '저장 실패'; | statusEl.textContent = '저장 실패'; | ||
2026년 4월 20일 (월) 16:46 판
mw.loader.load('https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.13/cropper.min.js');
mw.loader.load('/index.php?title=미디어위키:Lang.js&action=raw&ctype=text/javascript');
$(function() {
$('body').prepend('<div class="WW-bg" style="position:fixed;top:0px;left:0px;width:100%;height:100vh;"></div>');
});
// 페이지 전환 사운드
var transitionSound = new Audio('/index.php?title=특수:Redirect/file/Sfx-ui-001.mp3');
(function() {
var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';
transitionSound.volume = sfxOn ? master * sfx : 0;
})();
function playStaticSound() {
var master = parseFloat(localStorage.getItem('clbi-audio-master') || 80) / 100;
var sfx = parseFloat(localStorage.getItem('clbi-audio-sfx') || 60) / 100;
var sfxOn = localStorage.getItem('clbi-audio-sfxOn') !== 'false';
if (!sfxOn) return;
transitionSound.volume = master * sfx;
transitionSound.currentTime = 0;
transitionSound.play();
}
// 현재 언어 감지
function getCurrentLang() {
var langData = document.getElementById('clbi-lang-data');
return langData ? (langData.getAttribute('data-lang') || 'ko') : 'ko';
}
function normalizePageName(value) {
return String(value || '')
.split('?')[0]
.replace(/^\/index\.php\//, '')
.replace(/_/g, ' ')
.trim();
}
function buildWikiPath(title) {
return '/index.php/' + encodeURI(String(title || '').replace(/ /g, '_'));
}
// 사이드바 업데이트
function updateSidebar() {
if (!window.LANG || !window.LANG_NAMES || !window.CAT_LINKS) {
setTimeout(updateSidebar, 100);
return;
}
var langData = document.getElementById('clbi-lang-data');
var currentLang = getCurrentLang();
var t = (window.LANG && window.LANG[currentLang]) ? window.LANG[currentLang] : window.LANG['ko'];
var names = (window.LANG_NAMES && window.LANG_NAMES[currentLang]) ? window.LANG_NAMES[currentLang] : window.LANG_NAMES['ko'];
var cl = (window.CAT_LINKS && window.CAT_LINKS[currentLang]) ? window.CAT_LINKS[currentLang] : window.CAT_LINKS['ko'];
var allLangs = langData ? {
ko: langData.getAttribute('data-ko') || '',
en: langData.getAttribute('data-en') || '',
zh: langData.getAttribute('data-zh') || '',
ja: langData.getAttribute('data-ja') || ''
} : { ko: '', en: '', zh: '', ja: '' };
$('#clbi-lang-current').text(names[currentLang]);
var langHtml = '';
$.each(['ko', 'en', 'zh', 'ja'], function(i, lang) {
if (lang === currentLang) return;
var target = allLangs[lang];
if (target) {
langHtml += '<div class="clbi-lang-link"><a href="' + buildWikiPath(target) + '?uselang=' + lang + '">' + names[lang] + '</a></div>';
} else {
langHtml += '<div class="clbi-lang-wip">' + names[lang] + '</div>';
}
});
$('#clbi-lang-list').html(langHtml);
$('#clbi-title-language').text(t.language);
$('#clbi-title-categories').text(t.categories);
$('#clbi-title-links').text(t.links);
$('#clbi-title-search a').text(t.search);
$('#clbi-search-input').attr('placeholder', t.search + '...');
$('#clbi-title-recent a').text(t.recentChanges);
$('#clbi-title-guide').text(t.guide);
$('#clbi-guide-link').text(t.getStarted);
$('#clbi-title-playlist').text(t.playlist);
$('#clbi-cat-main a').attr('href', buildWikiPath(cl.main));
$('#clbi-cat-main .clbi-cat-label').text(t.mainMenu);
$('#clbi-cat-nations a').attr('href', buildWikiPath(cl.nations));
$('#clbi-cat-nations .clbi-cat-label').text(t.nations);
$('#clbi-cat-corporations a').attr('href', buildWikiPath(cl.corporations));
$('#clbi-cat-corporations .clbi-cat-label').text(t.corporations);
$('#clbi-cat-military a').attr('href', buildWikiPath(cl.military));
$('#clbi-cat-military .clbi-cat-label').text(t.military);
$('#clbi-cat-history a').attr('href', buildWikiPath(cl.history));
$('#clbi-cat-history .clbi-cat-label').text(t.history);
$('#clbi-cat-personnel a').attr('href', buildWikiPath(cl.personnel));
$('#clbi-cat-personnel .clbi-cat-label').text(t.personnel);
$('#clbi-btn-contribution').text(t.contribution);
$('#clbi-btn-watchlist').text(t.watchlist);
$('#clbi-btn-preferences').text(t.preferences);
$('#clbi-btn-logout').text(t.logout);
$('#clbi-btn-login').text(t.login);
var pageName = normalizePageName(mw.config.get('wgPageName'));
var specialPage = String(mw.config.get('wgCanonicalSpecialPageName') || '');
$('.clbi-cat-btn').removeClass('clbi-cat-active');
$.each(['main', 'nations', 'corporations', 'military', 'history', 'personnel'], function(i, key) {
if (cl[key] && pageName === normalizePageName(cl[key])) {
$('#clbi-cat-' + key).addClass('clbi-cat-active');
}
});
$('.clbi-user-btn').removeClass('clbi-user-btn-active');
if (
specialPage === 'Contributions' ||
specialPage === '기여' ||
pageName.indexOf('특수:기여') === 0 ||
pageName.indexOf('Special:Contributions') === 0
) {
$('#clbi-btn-contribution').addClass('clbi-user-btn-active');
}
if (specialPage === 'Watchlist') {
$('#clbi-btn-watchlist').addClass('clbi-user-btn-active');
}
if (
specialPage === '설정' ||
pageName === '특수:설정' ||
pageName === 'Special:설정'
) {
$('#clbi-btn-preferences').addClass('clbi-user-btn-active');
}
$('.toggleBtn').each(function() {
var btn = $(this);
if (!$('#' + btn.data('target')).hasClass('folding-open')) {
btn.text(t.expand);
} else {
btn.text(t.collapse);
}
});
}
// 대문 스타일
function applyMainPageStyle() {
var specialPage = mw.config.get('wgCanonicalSpecialPageName');
if (specialPage === 'Preferences') return;
var pageName = normalizePageName(mw.config.get('wgPageName'));
if (pageName === '대문') {
$('.liberty-content-header').css('display', 'none');
$('.mw-page-title-main').addClass('clbi-hide');
$('.content-tools').css('display', 'none');
$('.liberty-content-main').css('border-radius', '5px');
if ($('#clbi-main-logo').length === 0) {
$('.liberty-content').prepend(
'<div id="clbi-main-logo" style="text-align:center;padding:20px 0;">' +
'<img src="/index.php?title=특수:Redirect/file/Img-clbi-001.png" style="width:800px;height:auto;">' +
'</div>'
);
}
} else {
$('.liberty-content-header').css('display', '');
$('.mw-page-title-main').removeClass('clbi-hide');
$('.content-tools').css('display', '');
$('.liberty-content-main').css('border-radius', '');
$('#clbi-main-logo').remove();
}
updateSidebar();
}
// 초기화 함수
function initSidebars() {
// 편집 버튼들 붙이기
var header = $('.liberty-content-header');
var content = $('.liberty-content');
if (header.length && content.length) {
header.prependTo(content);
}
// 오른쪽 사이드바
if ($('#clbi-right-sidebar').length === 0) {
var username = mw.config.get('wgUserName');
var isLoggedIn = username !== null;
var avatarSrc = isLoggedIn
? '/index.php?title=특수:Redirect/file/Pfp-' + username + '.png'
: '/index.php?title=특수:Redirect/file/Pfp-default.png';
var userBox;
if (isLoggedIn) {
userBox =
'<div class="clbi-right-box">' +
'<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
'<img src="' + avatarSrc + '" onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png\'" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
'<a href="/index.php/사용자:' + username + '" style="font-size:13px;font-weight:700;color:#E2E2E2 !important;text-decoration:none !important;">' + username + '</a>' +
'</div>' +
'<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
'<a href="/index.php/특수:기여/' + username + '" class="clbi-user-btn" id="clbi-btn-contribution">기여</a>' +
'<a href="/index.php/특수:주시문서목록" class="clbi-user-btn" id="clbi-btn-watchlist">주시문서 목록</a>' +
'<a href="/index.php/특수:설정" class="clbi-user-btn" id="clbi-btn-preferences">설정</a>' +
'<a href="/index.php?title=특수:로그아웃&returnto=대문" class="clbi-user-btn clbi-user-btn-logout" id="clbi-btn-logout">로그아웃</a>' +
'</div></div>';
} else {
userBox =
'<div class="clbi-right-box">' +
'<div style="display:flex;flex-direction:column;align-items:center;padding:14px 14px 10px;background:linear-gradient(to bottom, #171114 0%, #0a0909 100%);">' +
'<img src="/index.php?title=특수:Redirect/file/Pfp-default.png" style="width:64px;height:64px;border-radius:5px;object-fit:cover;border:2px solid #854369;margin-bottom:8px;">' +
'<span style="font-size:13px;font-weight:700;color:#E2E2E2;">Guest</span>' +
'</div>' +
'<div class="clbi-right-content" style="border-top:1px solid #2a2a2a;padding:8px;">' +
'<a href="/index.php?title=특수:로그인&returnto=대문" class="clbi-user-btn" id="clbi-btn-login">로그인</a>' +
'</div></div>';
}
var recentBox =
'<div class="clbi-right-box">' +
'<div class="clbi-right-title" id="clbi-title-recent"><span class="clbi-icon" style="--icon:var(--ic-ui-006)"></span> <a href="/index.php/특수:최근바뀜" style="color:#E2E2E2 !important;text-decoration:none !important;">최근 변경</a></div>' +
'<div class="clbi-right-content" id="clbi-recent-list">불러오는 중...</div>' +
'</div>';
var spotifyBox =
'<div class="clbi-right-box" style="overflow:hidden;">' +
'<div class="clbi-right-title" id="clbi-title-playlist"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> 플레이리스트</div>' +
'<iframe style="border-radius:0;width:100%;height:380px;border:none;" src="https://open.spotify.com/embed/playlist/32l4ke6djdQn8LoBp1ipR9?utm_source=generator&theme=0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>' +
'</div>';
var sidebar =
userBox +
'<div class="clbi-right-box">' +
'<div class="clbi-right-title" id="clbi-title-search"><span class="clbi-icon" style="--icon:var(--ic-ui-005)"></span> <a href="/index.php/특수:검색" style="color:#E2E2E2 !important;text-decoration:none !important;">검색</a></div>' +
'<div class="clbi-right-content">' +
'<input id="clbi-search-input" type="text" placeholder="검색...">' +
'<button id="clbi-search-btn">GO</button>' +
'</div></div>' +
recentBox +
'<div class="clbi-right-box">' +
'<div class="clbi-right-title" id="clbi-title-guide"><span class="clbi-icon" style="--icon:var(--ic-ui-007)"></span> 가이드</div>' +
'<div class="clbi-right-content">' +
'<a href="/index.php/CLBI_Wiki/KR_시작하기_(CLBI)" id="clbi-guide-link">시작하기</a>' +
'</div></div>' +
spotifyBox;
var wrapper = '<div id="clbi-right-sidebar">' + sidebar + '</div>';
$('.content-wrapper').append(wrapper);
$('#clbi-search-btn').click(function() {
var query = $('#clbi-search-input').val();
if (query) window.location.href = '/index.php?search=' + encodeURIComponent(query);
});
$('#clbi-search-input').keypress(function(e) {
if (e.which === 13) $('#clbi-search-btn').click();
});
$.getJSON('/api.php?action=query&list=recentchanges&rclimit=5&rcprop=title|timestamp&format=json&rcnamespace=0&rctype=edit|new', function(data) {
var items = data.query.recentchanges;
var html = '';
$.each(items, function(i, item) {
var label = timeAgo(item.timestamp);
html +=
'<div class="clbi-recent-item">' +
'<div class="clbi-recent-title-wrap">' +
'<a href="/index.php/' + encodeURIComponent(item.title) + '" class="clbi-recent-title">' + item.title + '</a>' +
'</div>' +
'<span class="clbi-recent-time">' + label + '</span>' +
'</div>';
});
$('#clbi-recent-list').html(html);
$('#clbi-recent-list .clbi-recent-item').each(function() {
var wrap = $(this).find('.clbi-recent-title-wrap');
var title = $(this).find('.clbi-recent-title');
var wrapW = wrap.width();
var titleW = title[0].scrollWidth;
if (titleW > wrapW + 20) {
var duration = titleW / 40;
title.css({
'animation': 'clbi-scroll ' + duration + 's linear infinite',
'--scroll-dist': '-' + (titleW - wrapW + 8) + 'px'
});
}
});
}).fail(function() {
$('#clbi-recent-list').html('불러오기 실패');
});
}
// 왼쪽 사이드바
if ($('#clbi-left-sidebar').length === 0) {
var leftSidebar =
'<div id="clbi-left-sidebar">' +
'<div class="clbi-left-box">' +
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-001)"></span> <span id="clbi-title-language">언어</span></div>' +
'<div class="clbi-left-content clbi-lang-box" id="clbi-lang-box">' +
'<div class="clbi-lang-current" id="clbi-lang-current">한국어</div>' +
'<div id="clbi-lang-list" style="display:none;"></div>' +
'</div>' +
'</div>' +
'<div class="clbi-left-box">' +
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-002)"></span> <span id="clbi-title-categories">카테고리</span></div>' +
'<div class="clbi-left-content clbi-cat-box">' +
'<div class="clbi-cat-btn" id="clbi-cat-main"><a href="/index.php/대문"><div class="clbi-cat-text"><span class="clbi-cat-label">메인 메뉴</span><span class="clbi-cat-sub">MAIN MENU</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'<div class="clbi-cat-btn" id="clbi-cat-nations"><a href="/index.php/국가_및_조합"><div class="clbi-cat-text"><span class="clbi-cat-label">국가 및 조합</span><span class="clbi-cat-sub">NATIONS & FACTIONS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'<div class="clbi-cat-btn" id="clbi-cat-corporations"><a href="/index.php/기업_및_공동체"><div class="clbi-cat-text"><span class="clbi-cat-label">기업 및 공동체</span><span class="clbi-cat-sub">CORPORATIONS & COMMUNITIES</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'<div class="clbi-cat-btn" id="clbi-cat-military"><a href="/index.php/군_정치집단"><div class="clbi-cat-text"><span class="clbi-cat-label">군, 정치집단</span><span class="clbi-cat-sub">MILITARY & POLITICS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'<div class="clbi-cat-btn" id="clbi-cat-history"><a href="/index.php/역사적_사건"><div class="clbi-cat-text"><span class="clbi-cat-label">역사적 사건</span><span class="clbi-cat-sub">HISTORICAL EVENTS</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'<div class="clbi-cat-btn" id="clbi-cat-personnel"><a href="/index.php/인물"><div class="clbi-cat-text"><span class="clbi-cat-label">인물</span><span class="clbi-cat-sub">KEY PERSONNEL</span></div></a><div class="clbi-cat-arrow">▶</div></div>' +
'</div>' +
'</div>' +
'<div class="clbi-left-box">' +
'<div class="clbi-left-title"><span class="clbi-icon" style="--icon:var(--ic-ui-003)"></span> <span id="clbi-title-links">링크</span></div>' +
'<div class="clbi-left-content clbi-link-box">' +
'<ul>' +
'<li><a href="https://discord.gg/ctaeJ9d3Q5" target="_blank">Discord</a></li>' +
'<li><a href="https://www.youtube.com/@nxdsxn" target="_blank">YouTube</a></li>' +
'<li><a href="https://x.com/nxd_sxn" target="_blank">X</a></li>' +
'</ul>' +
'</div>' +
'</div>' +
'</div>';
$('.content-wrapper').prepend(leftSidebar);
$(document).on('click', '#clbi-lang-current', function() {
if ($('#clbi-lang-list').is(':hidden')) {
$('#clbi-lang-current').css('margin-bottom', '8px');
$('#clbi-lang-list').slideDown(200);
} else {
$('#clbi-lang-list').slideUp(200, function() {
$('#clbi-lang-current').css('margin-bottom', '0');
});
}
});
}
applyMainPageStyle();
mw.loader.using(['mediawiki.api']).then(function() {
setTimeout(function() {
initProfile();
}, 300);
});
}
$(function() {
setTimeout(function() {
initSidebars();
}, 100);
});
// SPA 네비게이션
function shouldSkip(url) {
return url.match(/action=edit|action=submit|action=history|action=delete|action=protect|action=purge|특수:로그인|특수:로그아웃|Special:UserLogin|Special:UserLogout|특수:사용자정보|특수:비밀번호바꾸기|uselang=/);
}
$(function() {
if (window._spaInitialized) return;
window._spaInitialized = true;
function isInternal(url) {
var a = document.createElement('a');
a.href = url;
return a.hostname === window.location.hostname;
}
function loadPage(url) {
fetch(url)
.then(function(res) { return res.text(); })
.then(function(html) {
var parser = new DOMParser();
var doc = parser.parseFromString(html, 'text/html');
// mw.config 값을 새 페이지에서 파싱해서 업데이트
var scripts = doc.querySelectorAll('script');
for (var i = 0; i < scripts.length; i++) {
var src = scripts[i].textContent;
if (src.indexOf('wgNamespaceNumber') !== -1) {
var match = src.match(/"wgNamespaceNumber":(-?\d+)/);
if (match) mw.config.set('wgNamespaceNumber', parseInt(match[1], 10));
var matchTitle = src.match(/"wgTitle":"([^"]+)"/);
if (matchTitle) mw.config.set('wgTitle', matchTitle[1]);
var matchPage = src.match(/"wgPageName":"([^"]+)"/);
if (matchPage) mw.config.set('wgPageName', matchPage[1]);
var matchSpecial = src.match(/"wgCanonicalSpecialPageName":"([^"]+)"/);
if (matchSpecial) {
mw.config.set('wgCanonicalSpecialPageName', matchSpecial[1]);
} else {
mw.config.set('wgCanonicalSpecialPageName', false);
}
break;
}
}
var newContent = doc.querySelector('.liberty-content-main');
var newTitle = doc.querySelector('.mw-page-title-main');
var newHead = doc.querySelector('title');
var newHeader = doc.querySelector('.liberty-content-header');
if (newContent) {
$('.liberty-content-main').html(newContent.innerHTML);
$('.profile-card').remove();
$('body').removeClass('page-loading');
}
if (newTitle) {
$('.mw-page-title-main').html(newTitle.innerHTML);
}
if (newHead) {
document.title = newHead.textContent;
}
if (newHeader) {
$('.liberty-content-header').html(newHeader.innerHTML);
}
window.scrollTo(0, 0);
$('#clbi-lang-list').hide();
$('#clbi-lang-current').css('margin-bottom', '0');
mw.hook('wikipage.content').fire($('.liberty-content-main'));
applyMainPageStyle();
mw.loader.using(['mediawiki.api']).then(function() {
initProfile();
});
});
}
$(document).on('click', 'a', function(e) {
var href = $(this).attr('href');
if (!href) return;
if (!isInternal(href)) return;
if (shouldSkip(href)) return;
if (href.startsWith('#')) return;
e.preventDefault();
playStaticSound();
$('body').addClass('page-loading');
history.pushState(null, '', href);
loadPage(href);
});
window.addEventListener('popstate', function() {
loadPage(window.location.href);
});
});
// 시간 계산 함수
function timeAgo(timestamp) {
var now = new Date();
var date = new Date(timestamp);
var diff = Math.floor((now - date) / 1000);
if (diff < 60) return diff + '초 전';
if (diff < 3600) return Math.floor(diff / 60) + '분 전';
if (diff < 86400) return Math.floor(diff / 3600) + '시간 전';
return Math.floor(diff / 86400) + '일 전';
}
// 펼접 토글
$(function() {
$(document).on('click', '.toggleBtn', function() {
var targetId = $(this).data('target');
var target = $('#' + targetId);
var scrollY = window.scrollY;
var lang = getCurrentLang();
var t = (window.LANG && window.LANG[lang]) ? window.LANG[lang] : (window.LANG ? window.LANG['ko'] : { expand: '펼치기', collapse: '접기' });
if (target.hasClass('folding-open')) {
target.css('max-height', target[0].scrollHeight + 'px');
target[0].offsetHeight;
target.css('max-height', '0px');
target.removeClass('folding-open');
$(this).text(t.expand);
} else {
target.css('max-height', '0px');
target[0].offsetHeight;
target.css('max-height', target[0].scrollHeight + 'px');
target.addClass('folding-open');
$(this).text(t.collapse);
}
window.scrollTo(0, scrollY);
});
});
// ========== 프로필 시스템 ==========
function initProfile() {
$('.profile-card').remove();
var ns = mw.config.get('wgNamespaceNumber');
var title = mw.config.get('wgTitle');
if (ns === 2) {
var profileUser = title.split('/')[0];
renderProfile(profileUser);
}
if (mw.config.get('wgCanonicalSpecialPageName') === '사용자정보') {
initUserProfilePage();
}
}
function renderProfile(username) {
var api = new mw.Api();
api.get({
action: 'query',
list: 'users',
ususers: username,
usprop: 'editcount'
}).then(function(data) {
var user = data.query.users[0];
var contentEl = document.getElementById('mw-content-text');
if (!contentEl) return;
var pageContent = contentEl.querySelector('.mw-parser-output') || contentEl;
injectProfileCard(username, user, pageContent);
});
}
function injectProfileCard(username, userData, container) {
var isOwnPage = mw.config.get('wgUserName') === username;
var editCount = (userData && userData.editcount) ? userData.editcount : 0;
var editBtn = isOwnPage
? '<a href="/index.php/특수:사용자정보" class="profile-edit-btn">프로필 수정</a>'
: '';
var card = document.createElement('div');
card.className = 'profile-card';
card.innerHTML =
'<div class="profile-header">' +
'<div class="profile-avatar">' +
'<img src="/index.php?title=특수:Redirect/file/Pfp-' + username + '.png&width=120"' +
' onerror="this.src=\'/index.php?title=특수:Redirect/file/Pfp-default.png&width=120\'"' +
' alt="' + username + '">' +
'</div>' +
'<div class="profile-info">' +
'<h2 class="profile-username">' + username + '</h2>' +
'<div class="profile-name" data-field="name"></div>' +
'<div class="profile-role" data-field="role"></div>' +
'<div class="profile-discord" data-field="discord"></div>' +
'<div class="profile-bio" data-field="bio"></div>' +
'<div class="profile-badges" data-field="badges"></div>' +
'</div>' +
editBtn +
'</div>' +
'<div class="profile-stats">' +
'<div class="profile-stat">' +
'<span class="clbi-stat-value">' + editCount + '</span>' +
'<span class="clbi-stat-label">수정 횟수</span>' +
'</div>' +
'</div>';
$('.profile-card').remove();
container.insertBefore(card, container.firstChild);
loadProfileFields(username, card);
}
function loadProfileFields(username, card) {
var api = new mw.Api();
api.get({
action: 'userprofile',
user: username
}).then(function(data) {
var profile = data.userprofile;
updateProfileFields(card, {
name: profile.name || '',
discord: profile.discord || '',
role: profile.role || '',
bio: profile.bio || '',
badges: profile.badges || ''
});
}).fail(function() {
updateProfileFields(card, {
name: '',
discord: '',
role: '',
bio: '',
badges: ''
});
});
}
function updateProfileFields(card, data) {
var nameEl = card.querySelector('[data-field="name"]');
var roleEl = card.querySelector('[data-field="role"]');
var discordEl = card.querySelector('[data-field="discord"]');
var bioEl = card.querySelector('[data-field="bio"]');
var badgesEl = card.querySelector('[data-field="badges"]');
if (nameEl) nameEl.textContent = data.name || '';
if (roleEl) roleEl.textContent = data.role || '';
if (discordEl) discordEl.textContent = data.discord ? ('디스코드: ' + data.discord) : '';
if (bioEl) bioEl.textContent = data.bio || '';
if (badgesEl) {
if (data.badges) {
var badges = data.badges.split(',');
var html = '';
for (var i = 0; i < badges.length; i++) {
html += '<span class="clbi-badge">' + badges[i].trim() + '</span>';
}
badgesEl.innerHTML = html;
} else {
badgesEl.innerHTML = '';
}
}
}
// ========== 프로필 시스템 끝 ==========
function initUserProfilePage() {
var saveBtn = document.getElementById('pref-save');
if (!saveBtn) return;
var adminEl = document.getElementById('pref-is-admin');
var isAdmin = adminEl && adminEl.getAttribute('data-admin') === '1';
var api = new mw.Api();
var selectedFile = null;
var cropper = null;
// ── 갤러리 모달 ──
if (!document.getElementById('clbi-gallery-modal')) {
var gModal = document.createElement('div');
gModal.id = 'clbi-gallery-modal';
gModal.style.cssText = 'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';
gModal.innerHTML =
'<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:480px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
'<div style="display:flex;justify-content:space-between;align-items:center;">' +
'<span style="font-size:14px;font-weight:700;color:#e2e2e2;">프로필 사진 선택</span>' +
'<button type="button" id="clbi-gallery-close" style="background:none;border:none;color:#aaa;font-size:18px;cursor:pointer;">✕</button>' +
'</div>' +
'<button type="button" id="clbi-gallery-upload-btn" style="background:#2a2a2a;border:2px dashed #854369;border-radius:8px;padding:32px;color:#e2e2e2;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:8px;font-size:13px;width:100%;">' +
'<span style="font-size:32px;">🖼️</span>새 사진 업로드' +
'</button>' +
'<div id="clbi-gallery-history-section" style="display:none;">' +
'<div style="font-size:11px;color:#888;margin-bottom:8px;">이전 사진 — 클릭하면 바로 적용</div>' +
'<div id="clbi-gallery-history" style="display:flex;gap:8px;flex-wrap:wrap;"></div>' +
'</div>' +
'</div>';
document.body.appendChild(gModal);
}
// ── 크롭 모달 ──
if (!document.getElementById('clbi-crop-modal')) {
var cModal = document.createElement('div');
cModal.id = 'clbi-crop-modal';
cModal.style.cssText = 'display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.85);z-index:99999;align-items:center;justify-content:center;';
cModal.innerHTML =
'<div style="background:#1e1e1e;border:2px solid #854369;border-radius:12px;padding:24px;max-width:500px;width:90%;display:flex;flex-direction:column;gap:16px;">' +
'<div style="font-size:14px;font-weight:700;color:#e2e2e2;">사진 조정</div>' +
'<div style="width:100%;max-height:380px;overflow:hidden;border-radius:8px;">' +
'<img id="clbi-crop-image" style="max-width:100%;">' +
'</div>' +
'<div style="display:flex;gap:8px;justify-content:flex-end;">' +
'<button type="button" id="clbi-crop-cancel" style="background:#2a2a2a;color:#e2e2e2;border:1px solid #444;padding:8px 16px;border-radius:6px;cursor:pointer;">취소</button>' +
'<button type="button" id="clbi-crop-confirm" style="background:#854369;color:#fff;border:none;padding:8px 16px;border-radius:6px;cursor:pointer;">확정</button>' +
'</div>' +
'</div>';
document.body.appendChild(cModal);
}
var gModal = document.getElementById('clbi-gallery-modal');
var cModal = document.getElementById('clbi-crop-modal');
var cropImage = document.getElementById('clbi-crop-image');
var pfpInput = document.getElementById('pref-pfp-input');
function openGallery() {
gModal.style.display = 'flex';
var username = mw.config.get('wgUserName');
api.get({
action: 'query',
titles: '파일:Pfp-' + username + '.png',
prop: 'imageinfo',
iiprop: 'url|timestamp',
iilimit: 6
}).then(function(data) {
var pages = data.query.pages;
var page = pages[Object.keys(pages)[0]];
if (!page.imageinfo || page.imageinfo.length === 0) return;
var historyEl = document.getElementById('clbi-gallery-history');
var sectionEl = document.getElementById('clbi-gallery-history-section');
historyEl.innerHTML = '';
page.imageinfo.forEach(function(info, idx) {
var wrap = document.createElement('div');
wrap.style.cssText = 'position:relative;cursor:pointer;';
var img = document.createElement('img');
img.src = info.url;
img.style.cssText = 'width:72px;height:72px;object-fit:cover;border-radius:8px;border:2px solid #444;flex-shrink:0;';
if (idx === 0) {
img.style.borderColor = '#854369';
var badge = document.createElement('div');
badge.textContent = '현재';
badge.style.cssText = 'position:absolute;bottom:4px;left:50%;transform:translateX(-50%);background:#854369;color:#fff;font-size:9px;padding:1px 6px;border-radius:10px;';
wrap.appendChild(badge);
}
img.addEventListener('mouseenter', function() {
if (idx !== 0) img.style.borderColor = '#854369';
});
img.addEventListener('mouseleave', function() {
if (idx !== 0) img.style.borderColor = '#444';
});
img.addEventListener('click', function() {
fetch(info.url)
.then(function(r) { return r.blob(); })
.then(function(blob) {
selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
gModal.style.display = 'none';
document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
});
});
wrap.appendChild(img);
historyEl.appendChild(wrap);
});
sectionEl.style.display = 'block';
});
}
function openCrop(src) {
cropImage.src = src;
cModal.style.display = 'flex';
if (cropper) {
cropper.destroy();
cropper = null;
}
setTimeout(function() {
cropper = new Cropper(cropImage, {
aspectRatio: 1,
viewMode: 1,
dragMode: 'move',
autoCropArea: 0.8,
cropBoxResizable: true,
cropBoxMovable: true
});
}, 150);
}
document.getElementById('pref-pfp-btn').addEventListener('click', function() {
openGallery();
});
document.getElementById('clbi-gallery-upload-btn').addEventListener('click', function() {
pfpInput.click();
});
document.getElementById('clbi-gallery-close').addEventListener('click', function() {
gModal.style.display = 'none';
});
pfpInput.addEventListener('change', function() {
var file = this.files[0];
if (!file) return;
gModal.style.display = 'none';
var reader = new FileReader();
reader.onload = function(e) {
openCrop(e.target.result);
};
reader.readAsDataURL(file);
});
document.getElementById('clbi-crop-cancel').addEventListener('click', function() {
cModal.style.display = 'none';
if (cropper) {
cropper.destroy();
cropper = null;
}
pfpInput.value = '';
});
document.getElementById('clbi-crop-confirm').addEventListener('click', function() {
if (!cropper) return;
var canvas = cropper.getCroppedCanvas({ width: 256, height: 256 });
if (!canvas) return;
canvas.toBlob(function(blob) {
selectedFile = new File([blob], 'profile.png', { type: 'image/png' });
document.getElementById('pref-pfp-preview').src = URL.createObjectURL(blob);
cModal.style.display = 'none';
cropper.destroy();
cropper = null;
document.getElementById('pref-pfp-btn').textContent = '✓ 사진 선택됨';
}, 'image/png');
});
// 이메일 변경
var emailSaveBtn = document.getElementById('pref-email-save');
if (emailSaveBtn) {
emailSaveBtn.addEventListener('click', function() {
var statusEl = document.getElementById('pref-email-status');
var newEmail = document.getElementById('pref-new-email').value;
var password = document.getElementById('pref-email-password').value;
if (!newEmail || !password) {
statusEl.textContent = '이메일과 비밀번호를 입력해주세요.';
return;
}
statusEl.textContent = '변경 중...';
api.postWithToken('csrf', {
action: 'changeemail',
email: newEmail,
password: password
}).then(function() {
statusEl.textContent = '✓ 이메일 변경됨';
document.getElementById('pref-new-email').value = '';
document.getElementById('pref-email-password').value = '';
setTimeout(function() {
statusEl.textContent = '';
}, 3000);
}).fail(function(code, data) {
var msg = data && data.error && data.error.info ? data.error.info : '변경 실패';
statusEl.textContent = msg;
});
});
}
// 통합 저장
saveBtn.addEventListener('click', function() {
var statusEl = document.getElementById('pref-status');
statusEl.textContent = '저장 중...';
var promises = [];
if (selectedFile) {
var username = mw.config.get('wgUserName');
promises.push(api.postWithToken('csrf', {
action: 'upload',
filename: 'Pfp-' + username + '.png',
ignorewarnings: true,
file: selectedFile,
format: 'json'
}, { contentType: 'multipart/form-data' }));
}
var fields = ['name', 'discord', 'role', 'bio'];
if (isAdmin) fields.push('badges');
for (var i = 0; i < fields.length; i++) {
var el = document.getElementById('pref-' + fields[i]);
if (!el) continue;
promises.push(api.postWithToken('csrf', {
action: 'options',
optionname: 'profile-' + fields[i],
optionvalue: el.value
}));
}
$.when.apply($, promises).then(function() {
statusEl.textContent = '✓ 저장됨';
selectedFile = null;
document.getElementById('pref-pfp-btn').textContent = '사진 선택';
setTimeout(function() {
statusEl.textContent = '';
}, 2000);
}).fail(function() {
statusEl.textContent = '저장 실패';
});
});
}