참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.
- 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
- 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
- 인터넷 익스플로러 / 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
- 오페라: Ctrl-F5를 입력.
/**
* MediaWiki 커스텀 펼치기/접기 애니메이션
*
* 틀 구조를 수정하지 않고 작동합니다.
*
* 구조:
*
* <div id="mw-customcollapsible-XXX" class="mw-collapsible">
*
* <div class="mw-customtoggle-XXX mw-customtoggle">
* [ 펼치기 / 접기 ]
* </div>
*
* 실제 내용...
*
* </div>
*
* 토글 버튼은 항상 남겨두고
* 토글 버튼 뒤의 내용만 애니메이션합니다.
*/
( function () {
'use strict';
var DURATION = 300;
var BODY_CLASS = 'mw-customcollapse-animation-body';
var READY_CLASS = 'mw-customcollapse-animation-ready';
var OPEN_CLASS = 'mw-customcollapse-animation-open';
var ANIMATING_CLASS = 'mw-customcollapse-animation-running';
/**
* 토글 이름 추출
*
* mw-customtoggle-aurann_01
* →
* aurann_01
*/
function getToggleName( toggle ) {
var prefix = 'mw-customtoggle-';
var classes = toggle.classList;
var i;
for ( i = 0; i < classes.length; i++ ) {
if ( classes[ i ].indexOf( prefix ) === 0 ) {
return classes[ i ].substring(
prefix.length
);
}
}
return null;
}
/**
* 토글과 연결된 부모 찾기
*/
function getTarget( toggle ) {
var name = getToggleName( toggle );
if ( !name ) {
return null;
}
/*
* 현재 구조에서는 toggle 자체가
* collapsible 안에 있으므로 우선 closest 사용
*/
var target = toggle.closest(
'.mw-collapsible'
);
if (
target &&
target.id ===
'mw-customcollapsible-' + name
) {
return target;
}
/*
* 혹시 구조가 다를 경우 ID로 검색
*/
return document.getElementById(
'mw-customcollapsible-' + name
);
}
/**
* 토글 버튼 이후의 모든 내용을
* 애니메이션용 DIV로 감싼다.
*
* 틀 원본 HTML은 수정할 필요 없음.
*/
function createAnimationBody( target, toggle ) {
var existing = target.querySelector(
':scope > .' + BODY_CLASS
);
if ( existing ) {
return existing;
}
var body = document.createElement( 'div' );
body.className = BODY_CLASS;
/*
* toggle 다음의 모든 노드를 body 안으로 이동
*
* table뿐만 아니라 div, 텍스트 등도 지원
*/
var node = toggle.nextSibling;
var nodes = [];
while ( node ) {
nodes.push( node );
node = node.nextSibling;
}
nodes.forEach( function ( child ) {
body.appendChild( child );
} );
target.appendChild( body );
return body;
}
/**
* 초기 상태 확인
*/
function getInitialOpenState( target, toggle, body ) {
/*
* MediaWiki가 mw-collapsed를 붙였다면 접힌 상태
*/
if ( target.classList.contains( 'mw-collapsed' ) ) {
return false;
}
/*
* body가 display:none이면 접힌 상태
*/
if (
window.getComputedStyle( body ).display === 'none'
) {
return false;
}
/*
* aria-expanded가 명시적으로 false
*/
if (
toggle.getAttribute( 'aria-expanded' ) ===
'false'
) {
return false;
}
return true;
}
/**
* 펼치기
*/
function openTarget( target, toggle, body ) {
if (
target.classList.contains(
ANIMATING_CLASS
)
) {
return;
}
target.classList.add(
ANIMATING_CLASS
);
target.classList.remove(
'mw-collapsed'
);
body.style.display = 'block';
body.style.overflow = 'hidden';
/*
* 실제 최종 높이 계산
*/
body.style.height = 'auto';
var endHeight = body.scrollHeight;
/*
* 0부터 시작
*/
body.style.height = '0px';
/*
* 강제 reflow
*/
body.offsetHeight;
var animation = body.animate(
[
{
height: '0px'
},
{
height: endHeight + 'px'
}
],
{
duration: DURATION,
easing: 'ease-out',
fill: 'forwards'
}
);
animation.onfinish = function () {
target.classList.remove(
ANIMATING_CLASS
);
target.classList.add(
OPEN_CLASS
);
body.style.height = '';
body.style.overflow = '';
body.style.display = '';
toggle.setAttribute(
'aria-expanded',
'true'
);
animation.cancel();
};
}
/**
* 접기
*/
function closeTarget( target, toggle, body ) {
if (
target.classList.contains(
ANIMATING_CLASS
)
) {
return;
}
target.classList.add(
ANIMATING_CLASS
);
var startHeight =
body.getBoundingClientRect().height;
/*
* 이미 접혀있는 경우
*/
if ( startHeight <= 0 ) {
target.classList.remove(
ANIMATING_CLASS
);
return;
}
body.style.height =
startHeight + 'px';
body.style.overflow =
'hidden';
/*
* 강제 reflow
*/
body.offsetHeight;
var animation = body.animate(
[
{
height:
startHeight + 'px'
},
{
height: '0px'
}
],
{
duration: DURATION,
easing: 'ease-in',
fill: 'forwards'
}
);
animation.onfinish = function () {
target.classList.remove(
ANIMATING_CLASS
);
target.classList.remove(
OPEN_CLASS
);
target.classList.add(
'mw-collapsed'
);
body.style.display = 'none';
body.style.height = '';
body.style.overflow = '';
toggle.setAttribute(
'aria-expanded',
'false'
);
animation.cancel();
};
}
/**
* 실제 클릭 처리
*/
function toggleTarget( toggle ) {
var target = getTarget( toggle );
if ( !target ) {
return false;
}
var body = target.querySelector(
':scope > .' + BODY_CLASS
);
if ( !body ) {
body = createAnimationBody(
target,
toggle
);
}
if ( !body ) {
return false;
}
var isOpen =
toggle.getAttribute(
'aria-expanded'
) === 'true' &&
!target.classList.contains(
'mw-collapsed'
);
if ( isOpen ) {
closeTarget(
target,
toggle,
body
);
} else {
openTarget(
target,
toggle,
body
);
}
return true;
}
/**
* 각 custom collapsible 초기화
*/
function initializeTarget( toggle ) {
var target = getTarget( toggle );
if ( !target ) {
return;
}
if (
target.classList.contains(
READY_CLASS
)
) {
return;
}
var body = createAnimationBody(
target,
toggle
);
if ( !body ) {
return;
}
target.classList.add(
READY_CLASS
);
var isOpen = getInitialOpenState(
target,
toggle,
body
);
if ( isOpen ) {
target.classList.add(
OPEN_CLASS
);
target.classList.remove(
'mw-collapsed'
);
body.style.display = '';
toggle.setAttribute(
'aria-expanded',
'true'
);
} else {
target.classList.remove(
OPEN_CLASS
);
target.classList.add(
'mw-collapsed'
);
body.style.display = 'none';
toggle.setAttribute(
'aria-expanded',
'false'
);
}
}
/**
* 페이지 전체 초기화
*/
function initializeAll() {
var toggles = document.querySelectorAll(
'.mw-customtoggle'
);
toggles.forEach(
function ( toggle ) {
if ( getToggleName( toggle ) ) {
initializeTarget(
toggle
);
}
}
);
}
/**
* 클릭
*
* MediaWiki 기본 토글보다 먼저 실행해서
* 기본 display:none 처리 방지.
*/
document.addEventListener(
'click',
function ( event ) {
var toggle =
event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
if (
!getToggleName( toggle )
) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
toggleTarget(
toggle
);
},
true
);
/**
* Enter / Space
*/
document.addEventListener(
'keydown',
function ( event ) {
if (
event.key !== 'Enter' &&
event.key !== ' '
) {
return;
}
var toggle =
event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
if (
!getToggleName( toggle )
) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
toggleTarget(
toggle
);
},
true
);
/**
* 초기 실행
*/
if (
document.readyState ===
'loading'
) {
document.addEventListener(
'DOMContentLoaded',
initializeAll
);
} else {
initializeAll();
}
/**
* MediaWiki AJAX/동적 콘텐츠 대응
*/
if (
window.mw &&
mw.hook
) {
mw.hook(
'wikipage.content'
).add(
function () {
initializeAll();
}
);
}
}() );