참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.
- 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
- 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
- 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
/* All JavaScript here will be loaded for users of the MinervaNeue skin */
/* 미네르바 노이에 전용 커스텀 메뉴 (PC/모바일 공통)
* MediaWiki:Minerva.js 에 넣는 것을 기준으로 작성되었습니다.
* 이 스크립트는 미네르바 스킨의 햄버거 메뉴 안에
* "게시판 / 버그/기능 개선 / 도구 / 도움말" 메뉴와 하위 메뉴를 추가합니다.
*/
( function ( mw, $ ) {
'use strict';
// 미네르바 스킨이 아닐 경우 아무 것도 하지 않는다.
if ( mw.config.get( 'skin' ) !== 'minerva' ) {
return;
}
/* -----------------------------
* 1. 메뉴 설정 (질문에서 주신 구조 그대로)
* ----------------------------- */
// 미네르바에서 사용할 아이콘 클래스는
// https://www.mediawiki.org/wiki/Topic:Vqy1kx6q4e0bzvyb 등에서 정리된 목록에 기반합니다. :contentReference[oaicite:5]{index=5}
// 필요하면 iconClass 값을 수정하여 원하는 아이콘으로 교체하면 됩니다.
var gaonMenuConfig = [
{
// * icon=comments | display=게시판
label: '게시판',
iconClass: 'mw-ui-icon-minerva-speechBubbles',
children: [
// ** icon=pencil-alt | display=위키방 | link=https://bbs.gaonwiki.com | title=https://bbs.gaonwiki.com
{
label: '위키방',
href: 'https://bbs.gaonwiki.com',
title: 'https://bbs.gaonwiki.com',
iconClass: 'mw-ui-icon-minerva-speechBubbles'
},
// ** icon=copyright | display=저작권 처리 게시판 | link=https://bbs.gaonwiki.com/copyrights | title=https://bbs.gaonwiki.com/copyrights
{
label: '저작권 처리 게시판',
href: 'https://bbs.gaonwiki.com/copyrights',
title: 'https://bbs.gaonwiki.com/copyrights',
iconClass: 'mw-ui-icon-minerva-speechBubbles'
},
// ** icon=comments | display=자유게시판 | link=https://bbs.gaonwiki.com/free | title=https://bbs.gaonwiki.com/free
{
label: '자유게시판',
href: 'https://bbs.gaonwiki.com/free',
title: 'https://bbs.gaonwiki.com/free',
iconClass: 'mw-ui-icon-minerva-speechBubbles'
}
]
},
{
// * icon=exclamation | display=버그/기능 개선
label: '버그/기능 개선',
iconClass: 'mw-ui-icon-minerva-die', // 경고/이슈 느낌의 주사위 아이콘 사용 예시
children: [
// ** icon=bullhorn | display=이슈 등록 | link=https://bbs.gaonwiki.com/issue | title=https://bbs.gaonwiki.com/issue
{
label: '이슈 등록',
href: 'https://bbs.gaonwiki.com/issue',
title: 'https://bbs.gaonwiki.com/issue',
iconClass: 'mw-ui-icon-minerva-die'
},
// ** icon=hands-helping | display=기능 구현 도움 | link=... | title=...
{
label: '기능 구현 도움',
href: 'https://www.gaonwiki.com/w/%EA%B0%80%EC%98%A8_%EC%9C%84%ED%82%A4:%EA%B8%B0%EB%8A%A5_%EA%B5%AC%ED%98%84_%EB%B6%88%EA%B0%80-%EB%88%84%EA%B0%80_%ED%95%B4%EA%B2%B0%EC%B1%85%EC%9D%84_%EC%95%8C%EB%A0%A4%EC%A3%BC%EC%84%B8%EC%9A%94!',
title: 'https://www.gaonwiki.com/w/가온_위키:기능_구현_불가-누가_해결책을_알려주세요!',
iconClass: 'mw-ui-icon-wikimedia-edit-base20'
},
// ** icon=bug | display=알려진 문제들 | link=... | title=...
{
label: '알려진 문제들',
href: 'https://www.gaonwiki.com/w/%EA%B0%80%EC%98%A8_%EC%9C%84%ED%82%A4/%EC%95%8C%EB%A0%A4%EC%A7%84_%EB%AC%B8%EC%A0%9C%EB%93%A4',
title: 'https://www.gaonwiki.com/w/가온_위키/알려진_문제들',
iconClass: 'mw-ui-icon-wikimedia-history-base20'
}
]
},
{
// * icon=gear | display=도구
label: '도구',
iconClass: 'mw-ui-icon-minerva-settings',
children: [
// ** icon=wrench | display=특수 문서 목록 | link=특수:특수문서 | title=특수:특수문서
{
label: '특수 문서 목록',
// mw.util.getUrl 을 사용하면 위키 내 링크를 환경에 맞게 자동으로 생성합니다.
href: mw.util.getUrl( '특수:특수문서' ),
title: '특수:특수문서',
iconClass: 'mw-ui-icon-minerva-settings'
},
// ** icon=upload | display=파일 올리기
{
label: '파일 올리기',
href: '#', // 상위 항목은 드롭다운용이므로 실제 링크는 서브 메뉴에 둡니다.
title: '파일 올리기',
iconClass: 'mw-ui-icon-minerva-download',
children: [
// *** icon=cloud-upload-alt | display=(기존) 업로드 | link=특수:올리기 | title=특수:올리기
{
label: '(기존) 업로드',
href: mw.util.getUrl( '특수:올리기' ),
title: '특수:올리기',
iconClass: 'mw-ui-icon-minerva-download'
},
// *** icon=file-upload | display=(신규) 업로드 마법사 | link=특수:UploadWizard | title=특수:UploadWizard
{
label: '(신규) 업로드 마법사',
href: mw.util.getUrl( '특수:UploadWizard' ),
title: '특수:UploadWizard',
iconClass: 'mw-ui-icon-minerva-download'
}
]
},
// ** icon=flag | display=이슈 트래커 | link=https://bbs.gaonwiki.com/issue/ | title=...
{
label: '이슈 트래커',
href: 'https://bbs.gaonwiki.com/issue/',
title: 'https://bbs.gaonwiki.com/issue/',
iconClass: 'mw-ui-icon-minerva-die'
},
// ** icon=flask | display=가온 위키 베타 | link=https://beta.gaonwiki.com | title=...
{
label: '가온 위키 베타',
href: 'https://beta.gaonwiki.com',
title: 'https://beta.gaonwiki.com',
iconClass: 'mw-ui-icon-minerva-mapPin'
}
]
},
{
// * icon=book | display=도움말
label: '도움말',
iconClass: 'mw-ui-icon-wikimedia-help-base20', // 도움말 느낌의 아이콘
children: [
// ** icon=spell-check | display=위키 문법 | link=도움말:위키_문법 | title=도움말:위키_문법
{
label: '위키 문법',
href: mw.util.getUrl( '도움말:위키_문법' ),
title: '도움말:위키_문법',
iconClass: 'mw-ui-icon-wikimedia-help-base20'
},
// ** icon=book | display=설명서 | link=도움말:설명서 | title=도움말:설명서
{
label: '설명서',
href: mw.util.getUrl( '도움말:설명서' ),
title: '도움말:설명서',
iconClass: 'mw-ui-icon-wikimedia-help-base20'
}
]
}
];
/* -----------------------------
* 2. DOM 유틸리티 함수
* ----------------------------- */
/**
* 하위 항목(2단계, 3단계)을 생성하고, 지정된 <ul> 안에 넣는 재귀 함수입니다.
*
* @param {jQuery} $ul 현재 레벨의 <ul> 요소(jQuery 객체)
* @param {Object} item 메뉴 항목 정의 (label, href, title, iconClass, children 등)
* @param {number} level 현재 레벨(2, 3 ...)
*/
function appendGaonMenuItem( $ul, item, level ) {
// <li> 생성
var $li = $( '<li>' )
.addClass( 'gaon-menu-item' )
.addClass( 'gaon-menu-item-level' + level );
// 실제 링크가 없고 드롭다운 전용인 항목이면 href 를 '#' 으로 둡니다.
var href = item.href || '#';
// <a> 생성 및 속성 설정
var $a = $( '<a>' )
.attr( 'href', href )
// title 이 지정되어 있으면 title 사용, 없으면 label 사용
.attr( 'title', item.title || item.label || '' )
// 미네르바 아이콘 스타일 적용
.addClass( 'mw-ui-icon mw-ui-icon-before' );
// 아이콘 클래스가 지정되어 있다면 추가
if ( item.iconClass ) {
$a.addClass( item.iconClass );
}
// 화면에 표시될 텍스트는 <span> 안에 넣습니다.
var $span = $( '<span>' ).text( item.label || '' );
$a.append( $span );
$li.append( $a );
$ul.append( $li );
// children 이 있다면 하위 <ul> 을 하나 더 만들어 재귀적으로 추가합니다.
if ( Array.isArray( item.children ) && item.children.length > 0 ) {
var nextLevel = level + 1;
var $subUl = $( '<ul>' )
.addClass( 'gaon-menu' )
.addClass( 'gaon-menu-level' + nextLevel );
item.children.forEach( function ( child ) {
appendGaonMenuItem( $subUl, child, nextLevel );
} );
// 여기서는 부모 <li> 아래에 중첩 <ul> 을 넣어 구조를 명확히 합니다.
$li.append( $subUl );
}
}
/**
* 상위 그룹(게시판, 버그/기능 개선 등)을 생성합니다.
* 각 그룹은 level1 <ul> 안에 헤더 <li> 와 하위 level2 <ul> 을 가지게 됩니다.
*
* @returns {jQuery} 햄버거 메뉴에 삽입할 상위 DOM 묶음(jQuery 객체)
*/
function buildGaonMenu() {
// 루트 컨테이너 역할을 할 <div> 를 하나 만든 뒤,
// 그 안에 여러 개의 <ul class="gaon-menu gaon-menu-level1"> 을 쌓습니다.
var $container = $( '<div>' ).addClass( 'gaon-menu-container' );
gaonMenuConfig.forEach( function ( group, index ) {
// 각 그룹마다 level1 <ul> 하나씩 생성
var $level1 = $( '<ul>' )
.addClass( 'gaon-menu' )
.addClass( 'gaon-menu-level1' )
.attr( 'data-gaon-group-index', index );
// 헤더(클릭 시 하위 level2 열고 닫는 부분) <li>
var $headerLi = $( '<li>' )
.addClass( 'gaon-menu-header' );
var $headerA = $( '<a>' )
// 상위 그룹은 토글 전용이므로 href="#" 로 두고 기본 동작을 막습니다.
.attr( 'href', '#' )
.attr( 'title', group.label || '' )
.addClass( 'mw-ui-icon mw-ui-icon-before' );
if ( group.iconClass ) {
$headerA.addClass( group.iconClass );
}
$headerA.append(
$( '<span>' ).text( group.label || '' )
);
$headerLi.append( $headerA );
$level1.append( $headerLi );
// 하위 메뉴(level2) 생성
if ( Array.isArray( group.children ) && group.children.length > 0 ) {
var $level2 = $( '<ul>' )
.addClass( 'gaon-menu' )
.addClass( 'gaon-menu-level2' );
group.children.forEach( function ( item ) {
appendGaonMenuItem( $level2, item, 2 );
} );
$level1.append( $level2 );
}
$container.append( $level1 );
} );
// 컨테이너 안의 자식 요소들만 반환합니다.
return $container.children();
}
/* -----------------------------
* 3. 메뉴를 미네르바 햄버거 메뉴 안에 삽입
* ----------------------------- */
/**
* 실제로 DOM 을 찾아서, 기본 메뉴 아래에 가온 메뉴를 삽입하는 함수입니다.
*/
function insertGaonMenuIntoMinerva() {
// 미네르바 햄버거 메뉴에서, 기존 최상위 <ul> 을 찾습니다.
// Qiita 예제에서는 '.menu ul:first' 를 기준으로 사용합니다. :contentReference[oaicite:6]{index=6}
var $firstUl = $( '.menu ul:first' );
if ( !$firstUl.length ) {
return;
}
// 중복 삽입 방지: 이미 가온 메뉴를 추가했으면 다시 하지 않습니다.
if ( $firstUl.data( 'gaonMenuInjected' ) ) {
return;
}
$firstUl.data( 'gaonMenuInjected', true );
// 우리의 메뉴 DOM 을 생성합니다.
var $gaonMenuNodes = buildGaonMenu();
// 기존 첫 번째 <ul> 바로 뒤에 삽입합니다.
$firstUl.after( $gaonMenuNodes );
// 하위 레벨 메뉴(level2, level3 …)는 기본적으로 숨겨 둡니다.
$( '.gaon-menu-level2, .gaon-menu-level3' ).hide();
// 상위 그룹(게시판 / 버그/기능 개선 / 도구 / 도움말) 클릭 시,
// 해당 그룹 내부의 level2 <ul> 을 토글합니다.
$( '.gaon-menu-header > a' ).on( 'click', function ( e ) {
e.preventDefault();
var $header = $( this );
var $level1 = $header.closest( '.gaon-menu-level1' );
var $level2 = $level1.children( '.gaon-menu-level2' );
$level2.slideToggle( 200 );
} );
// "파일 올리기" 같은 중간 단계에 children 이 있는 항목은
// 자신의 하위 level3 <ul> 을 토글합니다.
$( '.gaon-menu-item-level2 > a' ).each( function () {
var $a = $( this );
var $li = $a.closest( 'li' );
var $subUl = $li.children( '.gaon-menu-level3' );
if ( $subUl.length ) {
// children 이 있는 항목은 토글 버튼 역할을 하도록 클릭 핸들러를 붙입니다.
$a.on( 'click', function ( e ) {
e.preventDefault();
$subUl.slideToggle( 200 );
} );
}
} );
}
/* -----------------------------
* 4. 메뉴 DOM 생성 시점 기다리기
* ----------------------------- */
// 미네르바 메뉴 HTML 은 JS 로 동적으로 만들어지는 경우가 있기 때문에,
// DOM 이 준비될 때까지 주기적으로 검사하는 방식(Qiita 예제와 동일한 방식)을 사용합니다. :contentReference[oaicite:7]{index=7}
var maxAttempts = 50; // 최대 50번(약 5초)까지 시도
var attemptCount = 0;
function waitForMinervaMenu() {
var $firstUl = $( '.menu ul:first' );
if ( $firstUl.length ) {
// 메뉴를 찾았으면 바로 삽입 시도
insertGaonMenuIntoMinerva();
} else if ( attemptCount < maxAttempts ) {
attemptCount += 1;
setTimeout( waitForMinervaMenu, 100 );
}
}
// 문서 로드 이후에 검사 시작
$( waitForMinervaMenu );
}( mediaWiki, jQuery ) );