미네르바 스킨 메뉴 추가 |
구조 개선 |
||
| 1번째 줄: | 1번째 줄: | ||
/* All JavaScript here will be loaded for users of the MinervaNeue skin */ | /* All JavaScript here will be loaded for users of the MinervaNeue skin */ | ||
/* | /* 가온 위키 Minerva Neue 전용 커스텀 메뉴 | ||
* | * - PC/모바일 공통 | ||
* | * - Minerva 기본 메뉴와 같은 구조/클래스 사용 | ||
* | * (toggle-list__list / toggle-list-item / toggle-list-item__anchor / | ||
* minerva-icon / toggle-list-item__label) | |||
* - 상위 그룹(게시판, 버그/기능 개선, 도구, 도움말)은 접고 펼칠 수 있음 | |||
* - "파일 올리기" 아래에 3단계 메뉴 포함 | |||
*/ | */ | ||
| 9번째 줄: | 12번째 줄: | ||
'use strict'; | 'use strict'; | ||
// | // Minerva 스킨이 아닐 때는 아무 것도 하지 않는다. | ||
if ( mw.config.get( 'skin' ) !== 'minerva' ) { | if ( mw.config.get( 'skin' ) !== 'minerva' ) { | ||
return; | return; | ||
} | } | ||
/* ----------------------------- | /* ------------------------------------------------- | ||
* 1. 메뉴 설정 (질문에서 주신 | * 1. 메뉴 설정 (질문에서 주신 내용을 JS 객체로 변환) | ||
* ----------------------------- */ | * ------------------------------------------------- */ | ||
// icon 필드에는 Minerva 메뉴에서 사용하는 | |||
// `minerva-icon minerva-icon--XXX` 의 XXX 부분만 넣는다. | |||
// 예: icon: 'minerva-icon--speechBubbles' | |||
var gaonMenuConfig = [ | var gaonMenuConfig = [ | ||
{ | { | ||
// | // 게시판 그룹 | ||
label: '게시판', | label: '게시판', | ||
icon: 'minerva-icon--speechBubbles', | |||
children: [ | children: [ | ||
{ | { | ||
label: '위키방', | label: '위키방', | ||
href: 'https://bbs.gaonwiki.com', | href: 'https://bbs.gaonwiki.com', | ||
title: 'https://bbs.gaonwiki.com', | title: 'https://bbs.gaonwiki.com', | ||
icon: 'minerva-icon--speechBubbles' | |||
}, | }, | ||
{ | { | ||
label: '저작권 처리 게시판', | label: '저작권 처리 게시판', | ||
href: 'https://bbs.gaonwiki.com/copyrights', | href: 'https://bbs.gaonwiki.com/copyrights', | ||
title: 'https://bbs.gaonwiki.com/copyrights', | title: 'https://bbs.gaonwiki.com/copyrights', | ||
icon: 'minerva-icon--speechBubbles' | |||
}, | }, | ||
{ | { | ||
label: '자유게시판', | label: '자유게시판', | ||
href: 'https://bbs.gaonwiki.com/free', | href: 'https://bbs.gaonwiki.com/free', | ||
title: 'https://bbs.gaonwiki.com/free', | title: 'https://bbs.gaonwiki.com/free', | ||
icon: 'minerva-icon--speechBubbles' | |||
} | } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
// | // 버그/기능 개선 그룹 | ||
label: '버그/기능 개선', | label: '버그/기능 개선', | ||
icon: 'minerva-icon--die', | |||
children: [ | children: [ | ||
{ | { | ||
label: '이슈 등록', | label: '이슈 등록', | ||
href: 'https://bbs.gaonwiki.com/issue', | href: 'https://bbs.gaonwiki.com/issue', | ||
title: 'https://bbs.gaonwiki.com/issue', | title: 'https://bbs.gaonwiki.com/issue', | ||
icon: 'minerva-icon--die' | |||
}, | }, | ||
{ | { | ||
label: '기능 구현 도움', | 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!', | 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/가온_위키:기능_구현_불가-누가_해결책을_알려주세요!', | title: 'https://www.gaonwiki.com/w/가온_위키:기능_구현_불가-누가_해결책을_알려주세요!', | ||
icon: 'minerva-icon--speechBubbles' | |||
}, | }, | ||
{ | { | ||
label: '알려진 문제들', | 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', | 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/가온_위키/알려진_문제들', | title: 'https://www.gaonwiki.com/w/가온_위키/알려진_문제들', | ||
icon: 'minerva-icon--recentChanges' | |||
} | } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
// | // 도구 그룹 | ||
label: '도구', | label: '도구', | ||
icon: 'minerva-icon--specialPages', | |||
children: [ | children: [ | ||
{ | { | ||
label: '특수 문서 목록', | label: '특수 문서 목록', | ||
href: mw.util.getUrl( '특수:특수문서' ), | href: mw.util.getUrl( '특수:특수문서' ), | ||
title: '특수:특수문서', | title: '특수:특수문서', | ||
icon: 'minerva-icon--specialPages' | |||
}, | }, | ||
{ | { | ||
// "파일 올리기" (3단계 메뉴를 가진 항목) | |||
label: '파일 올리기', | label: '파일 올리기', | ||
href: '#', // | href: '#', // 드롭다운 전용이므로 실제 링크는 없음 | ||
title: '파일 올리기', | title: '파일 올리기', | ||
icon: 'minerva-icon--specialPages', | |||
children: [ | children: [ | ||
{ | { | ||
label: '(기존) 업로드', | label: '(기존) 업로드', | ||
href: mw.util.getUrl( '특수:올리기' ), | href: mw.util.getUrl( '특수:올리기' ), | ||
title: '특수:올리기', | title: '특수:올리기', | ||
icon: 'minerva-icon--specialPages' | |||
}, | }, | ||
{ | { | ||
label: '(신규) 업로드 마법사', | label: '(신규) 업로드 마법사', | ||
href: mw.util.getUrl( '특수:UploadWizard' ), | href: mw.util.getUrl( '특수:UploadWizard' ), | ||
title: '특수:UploadWizard', | title: '특수:UploadWizard', | ||
icon: 'minerva-icon--specialPages' | |||
} | } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
label: '이슈 트래커', | label: '이슈 트래커', | ||
href: 'https://bbs.gaonwiki.com/issue/', | href: 'https://bbs.gaonwiki.com/issue/', | ||
title: 'https://bbs.gaonwiki.com/issue/', | title: 'https://bbs.gaonwiki.com/issue/', | ||
icon: 'minerva-icon--die' | |||
}, | }, | ||
{ | { | ||
label: '가온 위키 베타', | label: '가온 위키 베타', | ||
href: 'https://beta.gaonwiki.com', | href: 'https://beta.gaonwiki.com', | ||
title: 'https://beta.gaonwiki.com', | title: 'https://beta.gaonwiki.com', | ||
icon: 'minerva-icon--specialPages' | |||
} | } | ||
] | ] | ||
}, | }, | ||
{ | { | ||
// | // 도움말 그룹 | ||
label: '도움말', | label: '도움말', | ||
icon: 'minerva-icon--specialPages', | |||
children: [ | children: [ | ||
{ | { | ||
label: '위키 문법', | label: '위키 문법', | ||
href: mw.util.getUrl( '도움말:위키_문법' ), | href: mw.util.getUrl( '도움말:위키_문법' ), | ||
title: '도움말:위키_문법', | title: '도움말:위키_문법', | ||
icon: 'minerva-icon--specialPages' | |||
}, | }, | ||
{ | { | ||
label: '설명서', | label: '설명서', | ||
href: mw.util.getUrl( '도움말:설명서' ), | href: mw.util.getUrl( '도움말:설명서' ), | ||
title: '도움말:설명서', | title: '도움말:설명서', | ||
icon: 'minerva-icon--specialPages' | |||
} | } | ||
] | ] | ||
| 154번째 줄: | 142번째 줄: | ||
]; | ]; | ||
/* ----------------------------- | /* ------------------------------------------------- | ||
* 2. DOM 유틸리티 | * 2. DOM 생성 유틸리티 | ||
* ----------------------------- */ | * ------------------------------------------------- */ | ||
/** | |||
* Minerva 스타일의 아이콘 <span> 요소를 생성한다. | |||
* | |||
* @param {string} iconClass "minerva-icon--XXX" 형식의 클래스 | |||
* @return {jQuery} | |||
*/ | |||
function buildIcon( iconClass ) { | |||
var $icon = $( '<span>' ).addClass( 'minerva-icon' ); | |||
if ( iconClass ) { | |||
$icon.addClass( iconClass ); | |||
} | |||
return $icon; | |||
} | |||
/** | |||
* 메뉴 라벨 <span> 요소를 생성한다. | |||
* | |||
* @param {string} text 화면에 보일 텍스트 | |||
* @return {jQuery} | |||
*/ | |||
function buildLabel( text ) { | |||
return $( '<span>' ) | |||
.addClass( 'toggle-list-item__label' ) | |||
.text( text || '' ); | |||
} | |||
/** | |||
* Minerva 스타일의 <a> 요소를 생성한다. | |||
* | |||
* @param {string} href 링크 URL (없으면 '#') | |||
* @param {string} title title 속성 | |||
* @return {jQuery} | |||
*/ | |||
function buildAnchor( href, title ) { | |||
return $( '<a>' ) | |||
.addClass( 'toggle-list-item__anchor gaon-menu-link' ) | |||
.attr( 'href', href || '#' ) | |||
.attr( 'title', title || '' ); | |||
} | |||
/** | /** | ||
* 하위 항목( | * 하위 메뉴 항목(레벨 2, 3)을 재귀적으로 생성하여 <ul> 안에 추가한다. | ||
* | * | ||
* @param {jQuery} $ul | * @param {jQuery} $ul 현재 레벨의 <ul> | ||
* @param {Object} item | * @param {Object} item 메뉴 정의 객체 | ||
* @param {number} level | * @param {number} level 레벨 번호(2, 3, ...) | ||
*/ | */ | ||
function appendGaonMenuItem( $ul, item, level ) { | function appendGaonMenuItem( $ul, item, level ) { | ||
var hasChildren = Array.isArray( item.children ) && item.children.length > 0; | |||
// <li> 생성 | // <li> 생성 | ||
var $li = $( '<li>' ) | var $li = $( '<li>' ) | ||
.addClass( 'gaon-menu-item | .addClass( 'toggle-list-item gaon-menu-item gaon-menu-item-level' + level ); | ||
if ( hasChildren ) { | |||
if ( | $li.addClass( 'gaon-menu-item--has-children gaon-menu-item--collapsed' ); | ||
$ | |||
} | } | ||
// | // <a> 생성 | ||
var $ | var $a = buildAnchor( item.href, item.title || item.label ); | ||
$a.append( $ | $a.append( buildIcon( item.icon ) ); | ||
$a.append( buildLabel( item.label ) ); | |||
$li.append( $a ); | $li.append( $a ); | ||
$ul.append( $li ); | $ul.append( $li ); | ||
// | // 하위 레벨이 있으면 <ul> 을 만들어 재귀 호출 | ||
if ( | if ( hasChildren ) { | ||
var nextLevel = level + 1; | var nextLevel = level + 1; | ||
var $subUl = $( '<ul>' ) | var $subUl = $( '<ul>' ) | ||
.addClass( 'gaon-menu | .addClass( 'gaon-menu gaon-menu-level' + nextLevel ); | ||
item.children.forEach( function ( child ) { | item.children.forEach( function ( child ) { | ||
| 205번째 줄: | 221번째 줄: | ||
} ); | } ); | ||
// | // 이 부분이 중요: | ||
// 하위 <ul> 은 반드시 <li> 안에 들어가야 한다. | |||
$li.append( $subUl ); | $li.append( $subUl ); | ||
} | } | ||
| 211번째 줄: | 228번째 줄: | ||
/** | /** | ||
* 상위 그룹(게시판 | * 한 개의 상위 그룹(게시판 / 버그/기능 개선 / 도구 / 도움말)을 | ||
* | * Minerva 스타일의 <ul> 블록으로 생성한다. | ||
* | * | ||
* @ | * @param {Object} group 그룹 정의 | ||
* @param {number} index 그룹 index | |||
* @return {jQuery} 완성된 <ul> 요소 | |||
*/ | */ | ||
function | function buildGroup( group, index ) { | ||
// | // Minerva 기본 메뉴와 같은 class 사용 | ||
var $level1 = $( '<ul>' ) | |||
var $ | .addClass( 'toggle-list__list gaon-menu gaon-menu-level1' ) | ||
.attr( 'data-gaon-group-index', index ); | |||
var hasChildren = Array.isArray( group.children ) && group.children.length > 0; | |||
// 헤더 <li> | |||
var $headerLi = $( '<li>' ) | |||
.addClass( 'toggle-list-item gaon-menu-header gaon-menu-header--collapsed' ); | |||
var $headerA = buildAnchor( '#', group.label ); | |||
$headerA | |||
.addClass( 'gaon-menu-header__anchor' ) | |||
.append( buildIcon( group.icon ) ) | |||
.append( buildLabel( group.label ) ); | |||
$headerLi.append( $headerA ); | |||
$ | // 그룹에 하위 항목이 있으면 level2 <ul> 생성 | ||
if ( hasChildren ) { | |||
var $level2 = $( '<ul>' ) | |||
.addClass( 'gaon-menu gaon-menu-level2' ); | |||
group.children.forEach( function ( item ) { | |||
appendGaonMenuItem( $level2, item, 2 ); | |||
} ); | |||
// 하위 | // 헤더 <li> 안에 하위 <ul> 을 넣는다. | ||
$headerLi.append( $level2 ); | |||
} | |||
} | |||
$level1.append( $headerLi ); | |||
return $ | return $level1; | ||
} | } | ||
/* ----------------------------- | /* ------------------------------------------------- | ||
* 3. 메뉴를 | * 3. 메뉴를 실제 Minerva 햄버거 메뉴 안에 삽입 | ||
* ----------------------------- */ | * ------------------------------------------------- */ | ||
/** | /** | ||
* | * 가온 메뉴를 Minerva 왼쪽 메뉴에 삽입한다. | ||
*/ | */ | ||
function insertGaonMenuIntoMinerva() { | function insertGaonMenuIntoMinerva() { | ||
// | // 기본 내비게이션 블록(대문/임의의 문서로)이 들어 있는 첫 번째 toggle-list__list 를 찾는다. | ||
var $firstList = $( '#p-navigation.toggle-list__list' ).first(); | |||
// 혹시 id 가 다르다면 fallback: 메뉴 안의 첫 toggle-list__list | |||
if ( !$firstList.length ) { | |||
$firstList = $( '#mw-mf-page-left .toggle-list__list' ).first(); | |||
} | |||
if ( !$ | if ( !$firstList.length ) { | ||
return; | return; | ||
} | } | ||
// 중복 삽입 방지 | // 중복 삽입 방지 | ||
if ( $ | if ( $firstList.data( 'gaonMenuInjected' ) ) { | ||
return; | return; | ||
} | } | ||
$ | $firstList.data( 'gaonMenuInjected', true ); | ||
// | // 여러 그룹을 한 번에 생성하여 첫 번째 리스트 뒤에 삽입 | ||
var $ | var $allGroups = $( [] ); | ||
gaonMenuConfig.forEach( function ( group, index ) { | |||
$allGroups = $allGroups.add( buildGroup( group, index ) ); | |||
} ); | |||
$firstList.after( $allGroups ); | |||
$ | |||
// 하위 | // 모든 하위 레벨을 기본적으로 숨긴다. | ||
$( '.gaon-menu-level2, .gaon-menu-level3' ).hide(); | $( '.gaon-menu-level2, .gaon-menu-level3' ).hide(); | ||
// 상위 그룹 | // 상위 그룹 헤더 클릭 시 level2 토글 | ||
$( '.gaon-menu-header__anchor' ).on( 'click', function ( e ) { | |||
$( '.gaon-menu- | |||
e.preventDefault(); | e.preventDefault(); | ||
var $ | var $headerLi = $( this ).closest( '.gaon-menu-header' ); | ||
var $sub = $headerLi.children( '.gaon-menu-level2' ); | |||
var $ | |||
if ( !$sub.length ) { | |||
return; | |||
} | |||
$ | $headerLi.toggleClass( 'gaon-menu-header--collapsed' ); | ||
$sub.slideToggle( 150 ); | |||
} ); | } ); | ||
// "파일 올리기" | // "파일 올리기"처럼 children 이 있는 2단계 항목 클릭 시 level3 토글 | ||
$( '.gaon-menu-item--has-children > .gaon-menu-link' ).on( 'click', function ( e ) { | |||
$( '.gaon-menu-item- | e.preventDefault(); | ||
var $li = $( this ).closest( '.gaon-menu-item--has-children' ); | |||
var $sub = $li.children( '.gaon-menu-level3' ); | |||
if ( !$sub.length ) { | |||
return; | |||
} | } | ||
$li.toggleClass( 'gaon-menu-item--collapsed' ); | |||
$sub.slideToggle( 150 ); | |||
} ); | } ); | ||
} | } | ||
/* ----------------------------- | /* ------------------------------------------------- | ||
* 4. 메뉴 | * 4. 메뉴 HTML 로드 완료를 기다렸다가 삽입 | ||
* ----------------------------- */ | * ------------------------------------------------- */ | ||
var maxAttempts = 50; // 최대 50번(약 5초) | var maxAttempts = 50; // 최대 50번(약 5초 정도) 시도 | ||
var attemptCount = 0; | var attemptCount = 0; | ||
function waitForMinervaMenu() { | function waitForMinervaMenu() { | ||
var $ | var $firstList = $( '#mw-mf-page-left .toggle-list__list' ).first(); | ||
if ( $ | if ( $firstList.length ) { | ||
insertGaonMenuIntoMinerva(); | insertGaonMenuIntoMinerva(); | ||
} else if ( attemptCount < maxAttempts ) { | } else if ( attemptCount < maxAttempts ) { | ||
| 351번째 줄: | 358번째 줄: | ||
} | } | ||
$( waitForMinervaMenu ); | $( waitForMinervaMenu ); | ||
}( mediaWiki, jQuery ) ); | }( mediaWiki, jQuery ) ); | ||
2025년 11월 19일 (수) 20:12 기준 최신판
/* All JavaScript here will be loaded for users of the MinervaNeue skin */
/* 가온 위키 Minerva Neue 전용 커스텀 메뉴
* - PC/모바일 공통
* - Minerva 기본 메뉴와 같은 구조/클래스 사용
* (toggle-list__list / toggle-list-item / toggle-list-item__anchor /
* minerva-icon / toggle-list-item__label)
* - 상위 그룹(게시판, 버그/기능 개선, 도구, 도움말)은 접고 펼칠 수 있음
* - "파일 올리기" 아래에 3단계 메뉴 포함
*/
( function ( mw, $ ) {
'use strict';
// Minerva 스킨이 아닐 때는 아무 것도 하지 않는다.
if ( mw.config.get( 'skin' ) !== 'minerva' ) {
return;
}
/* -------------------------------------------------
* 1. 메뉴 설정 (질문에서 주신 내용을 JS 객체로 변환)
* ------------------------------------------------- */
// icon 필드에는 Minerva 메뉴에서 사용하는
// `minerva-icon minerva-icon--XXX` 의 XXX 부분만 넣는다.
// 예: icon: 'minerva-icon--speechBubbles'
var gaonMenuConfig = [
{
// 게시판 그룹
label: '게시판',
icon: 'minerva-icon--speechBubbles',
children: [
{
label: '위키방',
href: 'https://bbs.gaonwiki.com',
title: 'https://bbs.gaonwiki.com',
icon: 'minerva-icon--speechBubbles'
},
{
label: '저작권 처리 게시판',
href: 'https://bbs.gaonwiki.com/copyrights',
title: 'https://bbs.gaonwiki.com/copyrights',
icon: 'minerva-icon--speechBubbles'
},
{
label: '자유게시판',
href: 'https://bbs.gaonwiki.com/free',
title: 'https://bbs.gaonwiki.com/free',
icon: 'minerva-icon--speechBubbles'
}
]
},
{
// 버그/기능 개선 그룹
label: '버그/기능 개선',
icon: 'minerva-icon--die',
children: [
{
label: '이슈 등록',
href: 'https://bbs.gaonwiki.com/issue',
title: 'https://bbs.gaonwiki.com/issue',
icon: 'minerva-icon--die'
},
{
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/가온_위키:기능_구현_불가-누가_해결책을_알려주세요!',
icon: 'minerva-icon--speechBubbles'
},
{
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/가온_위키/알려진_문제들',
icon: 'minerva-icon--recentChanges'
}
]
},
{
// 도구 그룹
label: '도구',
icon: 'minerva-icon--specialPages',
children: [
{
label: '특수 문서 목록',
href: mw.util.getUrl( '특수:특수문서' ),
title: '특수:특수문서',
icon: 'minerva-icon--specialPages'
},
{
// "파일 올리기" (3단계 메뉴를 가진 항목)
label: '파일 올리기',
href: '#', // 드롭다운 전용이므로 실제 링크는 없음
title: '파일 올리기',
icon: 'minerva-icon--specialPages',
children: [
{
label: '(기존) 업로드',
href: mw.util.getUrl( '특수:올리기' ),
title: '특수:올리기',
icon: 'minerva-icon--specialPages'
},
{
label: '(신규) 업로드 마법사',
href: mw.util.getUrl( '특수:UploadWizard' ),
title: '특수:UploadWizard',
icon: 'minerva-icon--specialPages'
}
]
},
{
label: '이슈 트래커',
href: 'https://bbs.gaonwiki.com/issue/',
title: 'https://bbs.gaonwiki.com/issue/',
icon: 'minerva-icon--die'
},
{
label: '가온 위키 베타',
href: 'https://beta.gaonwiki.com',
title: 'https://beta.gaonwiki.com',
icon: 'minerva-icon--specialPages'
}
]
},
{
// 도움말 그룹
label: '도움말',
icon: 'minerva-icon--specialPages',
children: [
{
label: '위키 문법',
href: mw.util.getUrl( '도움말:위키_문법' ),
title: '도움말:위키_문법',
icon: 'minerva-icon--specialPages'
},
{
label: '설명서',
href: mw.util.getUrl( '도움말:설명서' ),
title: '도움말:설명서',
icon: 'minerva-icon--specialPages'
}
]
}
];
/* -------------------------------------------------
* 2. DOM 생성 유틸리티
* ------------------------------------------------- */
/**
* Minerva 스타일의 아이콘 <span> 요소를 생성한다.
*
* @param {string} iconClass "minerva-icon--XXX" 형식의 클래스
* @return {jQuery}
*/
function buildIcon( iconClass ) {
var $icon = $( '<span>' ).addClass( 'minerva-icon' );
if ( iconClass ) {
$icon.addClass( iconClass );
}
return $icon;
}
/**
* 메뉴 라벨 <span> 요소를 생성한다.
*
* @param {string} text 화면에 보일 텍스트
* @return {jQuery}
*/
function buildLabel( text ) {
return $( '<span>' )
.addClass( 'toggle-list-item__label' )
.text( text || '' );
}
/**
* Minerva 스타일의 <a> 요소를 생성한다.
*
* @param {string} href 링크 URL (없으면 '#')
* @param {string} title title 속성
* @return {jQuery}
*/
function buildAnchor( href, title ) {
return $( '<a>' )
.addClass( 'toggle-list-item__anchor gaon-menu-link' )
.attr( 'href', href || '#' )
.attr( 'title', title || '' );
}
/**
* 하위 메뉴 항목(레벨 2, 3)을 재귀적으로 생성하여 <ul> 안에 추가한다.
*
* @param {jQuery} $ul 현재 레벨의 <ul>
* @param {Object} item 메뉴 정의 객체
* @param {number} level 레벨 번호(2, 3, ...)
*/
function appendGaonMenuItem( $ul, item, level ) {
var hasChildren = Array.isArray( item.children ) && item.children.length > 0;
// <li> 생성
var $li = $( '<li>' )
.addClass( 'toggle-list-item gaon-menu-item gaon-menu-item-level' + level );
if ( hasChildren ) {
$li.addClass( 'gaon-menu-item--has-children gaon-menu-item--collapsed' );
}
// <a> 생성
var $a = buildAnchor( item.href, item.title || item.label );
$a.append( buildIcon( item.icon ) );
$a.append( buildLabel( item.label ) );
$li.append( $a );
$ul.append( $li );
// 하위 레벨이 있으면 <ul> 을 만들어 재귀 호출
if ( hasChildren ) {
var nextLevel = level + 1;
var $subUl = $( '<ul>' )
.addClass( 'gaon-menu gaon-menu-level' + nextLevel );
item.children.forEach( function ( child ) {
appendGaonMenuItem( $subUl, child, nextLevel );
} );
// 이 부분이 중요:
// 하위 <ul> 은 반드시 <li> 안에 들어가야 한다.
$li.append( $subUl );
}
}
/**
* 한 개의 상위 그룹(게시판 / 버그/기능 개선 / 도구 / 도움말)을
* Minerva 스타일의 <ul> 블록으로 생성한다.
*
* @param {Object} group 그룹 정의
* @param {number} index 그룹 index
* @return {jQuery} 완성된 <ul> 요소
*/
function buildGroup( group, index ) {
// Minerva 기본 메뉴와 같은 class 사용
var $level1 = $( '<ul>' )
.addClass( 'toggle-list__list gaon-menu gaon-menu-level1' )
.attr( 'data-gaon-group-index', index );
var hasChildren = Array.isArray( group.children ) && group.children.length > 0;
// 헤더 <li>
var $headerLi = $( '<li>' )
.addClass( 'toggle-list-item gaon-menu-header gaon-menu-header--collapsed' );
var $headerA = buildAnchor( '#', group.label );
$headerA
.addClass( 'gaon-menu-header__anchor' )
.append( buildIcon( group.icon ) )
.append( buildLabel( group.label ) );
$headerLi.append( $headerA );
// 그룹에 하위 항목이 있으면 level2 <ul> 생성
if ( hasChildren ) {
var $level2 = $( '<ul>' )
.addClass( 'gaon-menu gaon-menu-level2' );
group.children.forEach( function ( item ) {
appendGaonMenuItem( $level2, item, 2 );
} );
// 헤더 <li> 안에 하위 <ul> 을 넣는다.
$headerLi.append( $level2 );
}
$level1.append( $headerLi );
return $level1;
}
/* -------------------------------------------------
* 3. 메뉴를 실제 Minerva 햄버거 메뉴 안에 삽입
* ------------------------------------------------- */
/**
* 가온 메뉴를 Minerva 왼쪽 메뉴에 삽입한다.
*/
function insertGaonMenuIntoMinerva() {
// 기본 내비게이션 블록(대문/임의의 문서로)이 들어 있는 첫 번째 toggle-list__list 를 찾는다.
var $firstList = $( '#p-navigation.toggle-list__list' ).first();
// 혹시 id 가 다르다면 fallback: 메뉴 안의 첫 toggle-list__list
if ( !$firstList.length ) {
$firstList = $( '#mw-mf-page-left .toggle-list__list' ).first();
}
if ( !$firstList.length ) {
return;
}
// 중복 삽입 방지
if ( $firstList.data( 'gaonMenuInjected' ) ) {
return;
}
$firstList.data( 'gaonMenuInjected', true );
// 여러 그룹을 한 번에 생성하여 첫 번째 리스트 뒤에 삽입
var $allGroups = $( [] );
gaonMenuConfig.forEach( function ( group, index ) {
$allGroups = $allGroups.add( buildGroup( group, index ) );
} );
$firstList.after( $allGroups );
// 모든 하위 레벨을 기본적으로 숨긴다.
$( '.gaon-menu-level2, .gaon-menu-level3' ).hide();
// 상위 그룹 헤더 클릭 시 level2 토글
$( '.gaon-menu-header__anchor' ).on( 'click', function ( e ) {
e.preventDefault();
var $headerLi = $( this ).closest( '.gaon-menu-header' );
var $sub = $headerLi.children( '.gaon-menu-level2' );
if ( !$sub.length ) {
return;
}
$headerLi.toggleClass( 'gaon-menu-header--collapsed' );
$sub.slideToggle( 150 );
} );
// "파일 올리기"처럼 children 이 있는 2단계 항목 클릭 시 level3 토글
$( '.gaon-menu-item--has-children > .gaon-menu-link' ).on( 'click', function ( e ) {
e.preventDefault();
var $li = $( this ).closest( '.gaon-menu-item--has-children' );
var $sub = $li.children( '.gaon-menu-level3' );
if ( !$sub.length ) {
return;
}
$li.toggleClass( 'gaon-menu-item--collapsed' );
$sub.slideToggle( 150 );
} );
}
/* -------------------------------------------------
* 4. 메뉴 HTML 로드 완료를 기다렸다가 삽입
* ------------------------------------------------- */
var maxAttempts = 50; // 최대 50번(약 5초 정도) 시도
var attemptCount = 0;
function waitForMinervaMenu() {
var $firstList = $( '#mw-mf-page-left .toggle-list__list' ).first();
if ( $firstList.length ) {
insertGaonMenuIntoMinerva();
} else if ( attemptCount < maxAttempts ) {
attemptCount += 1;
setTimeout( waitForMinervaMenu, 100 );
}
}
$( waitForMinervaMenu );
}( mediaWiki, jQuery ) );