미디어위키:Common.js
참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.
- 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
- 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
- 인터넷 익스플로러 / 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
- 오페라: Ctrl-F5를 입력.
/* 이 자바스크립트 설정은 모든 문서, 모든 사용자에게 적용됩니다. */
/* MediaWiki:Common.js 에 이 파일 내용 전체를 붙여넣으세요. (CSS는 넣지 말 것!) */
/* ===== 외부 링크 새 창 열기 ===== */
$(document).ready(function () {
$('#bodyContent a.external').attr('target', '_blank').attr('rel', 'noopener noreferrer');
});
/* ===== 목차 박스 드래그 이동 =====
(원치 않으면 이 블록 전체를 삭제해도 됩니다.
새 디자인은 목차가 본문 안 박스로 정리되어 있어 고정(fixed) 배치는 껐습니다) */
mw.loader.using('jquery.ui', function () {
if ($('#toc').length === 0) return;
$('#toc').css({ cursor: 'move' });
$('#toc').draggable({
containment: 'window',
scroll: false
});
});
/* ===== Google 자동 번역 위젯 =====
처음에는 배너 옆(카드 오른쪽 여백)에 있다가,
스크롤해서 지나치면 화면 상단에 고정되어 계속 보입니다. */
mw.loader.using('mediawiki.util', function () {
/* ----- 브라우저 언어 자동 번역 -----
영어/일본어/중국어 브라우저로 접속하면 자동으로 번역 상태로 시작.
- googtrans 쿠키가 이미 있으면(이전에 언어를 고른 적 있음) 건드리지 않음
- 사용자가 위젯을 직접 조작한 적 있으면(gtUserChose) 자동 적용 안 함 */
try {
var userChose = localStorage.getItem('gtUserChose');
var hasCookie = /(^|;\s*)googtrans=/.test(document.cookie);
var nav = ((navigator.languages && navigator.languages[0]) || navigator.language || '').toLowerCase();
var target = '';
if (nav.indexOf('ja') === 0) target = 'ja';
else if (nav.indexOf('en') === 0) target = 'en';
else if (nav.indexOf('zh') === 0) {
// 대만/홍콩/마카오·번체 표기는 번체(zh-TW), 그 외 중국어는 간체(zh-CN)
target = /zh-(tw|hk|mo|hant)/.test(nav) ? 'zh-TW' : 'zh-CN';
}
if (target && !hasCookie && !userChose) {
document.cookie = 'googtrans=/ko/' + target + '; path=/';
}
// 위젯을 직접 클릭하면 이후로는 사용자의 선택을 존중 (자동 적용 중지)
document.addEventListener('click', function (e) {
if (e.target.closest && e.target.closest('#google_translate_element')) {
localStorage.setItem('gtUserChose', '1');
}
}, true);
} catch (err) { /* 쿠키/스토리지 차단 환경이면 자동 적용만 생략 */ }
var $div = $('<div id="google_translate_element"></div>');
// 본문 카드(.mw-body)에 붙여서 카드 기준으로 위치 고정 (없으면 body)
var $anchor = $('.mw-body').first();
($anchor.length ? $anchor : $('body')).append($div);
// 스크롤 시 상단 고정 전환
var PIN_AT = 200; // 이만큼 스크롤하면 상단 고정 (배너를 지나는 지점)
function updateTranslatePos() {
var el = document.getElementById('google_translate_element');
var body = document.querySelector('.mw-body');
if (!el || !body) return;
var pinned = window.scrollY > PIN_AT;
el.classList.toggle('gt-pinned', pinned);
// 넓은 화면에서 고정 시: 카드 오른쪽 모서리에 맞춰 가로 위치 유지
if (pinned && window.innerWidth > 1420) {
el.style.left = (body.getBoundingClientRect().right + 22) + 'px';
} else {
el.style.left = '';
}
}
window.addEventListener('scroll', updateTranslatePos, { passive: true });
window.addEventListener('resize', updateTranslatePos);
updateTranslatePos();
/* ----- 게임 용어 고정 번역 (용어집) -----
Google이 게임 고유명사를 엉뚱하게 번역하는 것을 막습니다.
원리: Google 번역이 돌기 전에, 본문에서 아래 용어를 찾아
해당 언어의 공식 표기로 미리 바꾸고 notranslate 표시를 붙임
→ Google은 그 부분을 건너뛰고 나머지 문장만 번역.
★ 용어 추가 방법: 아래에 '한국어 용어': '고정 표기' 한 줄씩 추가.
긴 용어를 먼저 매칭하므로 순서는 상관없음.
★ 주의: 문장 어순까지 고쳐주지는 못함 (용어만 고정) */
var GT_GLOSSARY = {
en: {
'네바레스': 'Nevareth',
'카발': 'CABAL',
'블러디 아이스': 'Bloody Ice',
'데저트 스크림': 'Desert Scream',
'그린 디스파이어': 'Green Despair',
'포트 룩스': 'Port Lux',
'포트 루이나': 'Fort Ruina',
'언데드 그라운드': 'Undead Ground',
'포가튼 루인': 'Forgotten Ruin',
'뮤턴트 포레스트': 'Mutant Forest',
'폰투스 페룸': 'Pontus Ferrum',
'포르타 인페르노': 'Porta Inferno',
'아케인 트레이스': 'Arcane Trace',
'레이크 사이드': 'Lakeside',
'앱솔루트 소울 코어': 'Absolute Soul Core'
},
ja: {
// 예시: '네바레스': 'ネヴァレス',
},
'zh-CN': {
// 예시: '네바레스': '内瓦雷斯',
},
'zh-TW': {
// 예시: '네바레스': '內瓦雷斯',
}
};
function gtTargetLang() {
var m = document.cookie.match(/(?:^|;\s*)googtrans=\/ko\/([a-zA-Z-]+)/);
return m ? m[1] : '';
}
function gtEscapeRegExp(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function gtApplyGlossary() {
var lang = gtTargetLang();
if (!lang || lang === 'ko') return;
var dict = GT_GLOSSARY[lang];
if (!dict) return;
var terms = Object.keys(dict).sort(function (a, b) { return b.length - a.length; });
if (!terms.length) return;
var re = new RegExp(terms.map(gtEscapeRegExp).join('|'), 'g');
var roots = [document.getElementById('mw-content-text'), document.getElementById('firstHeading')];
roots.forEach(function (root) {
if (!root) return;
var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) {
var p = node.parentElement;
if (!p) return NodeFilter.FILTER_REJECT;
if (p.closest('script, style, .notranslate, code, pre')) return NodeFilter.FILTER_REJECT;
return re.test(node.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
}
});
var nodes = [];
while (walker.nextNode()) nodes.push(walker.currentNode);
nodes.forEach(function (node) {
var text = node.nodeValue;
var frag = document.createDocumentFragment();
var last = 0, m;
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) {
if (m.index > last) frag.appendChild(document.createTextNode(text.slice(last, m.index)));
var span = document.createElement('span');
span.className = 'notranslate';
span.setAttribute('translate', 'no');
span.textContent = dict[m[0]];
frag.appendChild(span);
last = m.index + m[0].length;
}
if (last < text.length) frag.appendChild(document.createTextNode(text.slice(last)));
node.parentNode.replaceChild(frag, node);
});
});
}
// 번역 대상 언어가 정해진 상태라면 Google 번역 전에 용어 교체
gtApplyGlossary();
// 위젯에서 언어를 바꾸면(쿠키 변경 감지) 새로고침해서 용어집을 새 언어로 다시 적용
var gtLangAtLoad = gtTargetLang();
setInterval(function () {
var now = gtTargetLang();
if (now !== gtLangAtLoad) location.reload();
}, 1000);
window.googleTranslateElementInit = function () {
new google.translate.TranslateElement({
pageLanguage: 'ko',
includedLanguages: 'en,ja,ko,zh-CN,zh-TW',
layout: google.translate.TranslateElement.InlineLayout.SIMPLE,
autoDisplay: false
}, 'google_translate_element');
};
var script = document.createElement('script');
script.src = '//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit';
document.body.appendChild(script);
});