미디어위키:Common.js: 두 판 사이의 차이

편집 요약 없음
태그: 수동 되돌리기
편집 요약 없음
1번째 줄: 1번째 줄:
/**
/**
  * MediaWiki 커스텀 펼치기/접기 애니메이션
  * MediaWiki 커스텀 펼치기/접기 슬라이드 애니메이션
  *
  *
  * 지원 예:
  * 같은 mw-customcollapsible-XXX ID가 2개 존재하는 틀 지원
  *
  *
  * .mw-customtoggle-KSC_LEADER
  * 예:
*          ↕
* .mw-customcollapsible-KSC_LEADER
  *
  *
  * 식별자 이름은 자유롭게 사용 가능.
  * [첫 번째]
* <div id="mw-customcollapsible-XXX">
*    <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
* </div>
*
* [두 번째]
* <div id="mw-customcollapsible-XXX" class="mw-collapsed">
*    <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
*    <table>내용</table>
* </div>
*
* 틀 원본 수정 필요 없음.
  */
  */


14번째 줄: 23번째 줄:
'use strict';
'use strict';


var DURATION = 300;
var DURATION = 350;
var ANIMATING_CLASS = 'mw-custom-collapse-animating';
 
var INITIALIZED = 'data-slide-collapse-ready';
var CONTENT_CLASS = 'mw-slide-collapse-content';
var ANIMATING_CLASS = 'mw-slide-collapse-animating';
 


/**
/* --------------------------------
* 토글 요소에서 식별자 추출
* mw-customtoggle-XXX 에서 XXX 추출
*
* -------------------------------- */
* 예:
 
* mw-customtoggle-KSC_LEADER
function getName( toggle ) {
* → KSC_LEADER
*/
function getToggleName( toggle ) {
var prefix = 'mw-customtoggle-';
var prefix = 'mw-customtoggle-';
var classes = toggle.classList;
var classes = toggle.classList;
31번째 줄: 41번째 줄:
for ( i = 0; i < classes.length; i++ ) {
for ( i = 0; i < classes.length; i++ ) {
if ( classes[ i ].indexOf( prefix ) === 0 ) {
if ( classes[ i ].indexOf( prefix ) === 0 ) {
return classes[ i ].substring( prefix.length );
return classes[ i ].substring(
prefix.length
);
}
}
}
}
38번째 줄: 50번째 줄:
}
}


/**
 
* CSS selector escape
/* --------------------------------
*/
* 같은 ID를 가진 collapsible 전부 찾기
function escapeSelector( value ) {
*
if ( window.CSS && CSS.escape ) {
* getElementById() 사용하지 않음!
return CSS.escape( value );
* -------------------------------- */
 
function getTargets( name ) {
if ( !name ) {
return [];
}
}


return value.replace(
var id =
/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g,
'mw-customcollapsible-' + name;
'\\\\$1'
 
return Array.prototype.slice.call(
document.querySelectorAll(
'[id="' +
CSS.escape( id ) +
'"]'
)
);
);
}
}


/**
* 식별자에 대응하는 collapsible 대상 찾기
*/
function getTarget( name ) {
if ( !name ) {
return null;
}


var target = document.querySelector(
/* --------------------------------
'.mw-customcollapsible-' +
* 실제 내용이 들어있는 collapsible 찾기
escapeSelector( name )
*
);
* table 등이 들어있는 두 번째 div를 선택
* -------------------------------- */


if ( target ) {
function getContentTarget( name ) {
return target;
var targets = getTargets( name );
}
var i;


target = document.getElementById(
/*
'mw-customcollapsible-' + name
* 버튼 외에 다른 요소가 들어있는 target을 찾는다.
);
*/
for ( i = 0; i < targets.length; i++ ) {
var target = targets[ i ];
var toggle = target.querySelector(
'[class*="mw-customtoggle-"]'
);


return target;
if ( !toggle ) {
}
continue;
}


/**
var node = toggle.nextElementSibling;
* 실제 애니메이션을 적용할 콘텐츠 찾기
*/
function getContent( target ) {
var content = target.querySelector(
'.mw-collapsible-content'
);


if ( content ) {
if ( node ) {
return content;
return target;
}
}
}


return target;
return null;
}
}


/**
 
* 요소 원래 display 타입 보존
/* --------------------------------
* 실제 내용 wrapper 생성
*
*
* table 요소에 display:block을 강제하면
* table 자체의 높이를 변경하지 않고
* 레이아웃이 깨질 수 있으므로 태그별 기본값 사용.
* 바깥 DIV 높이를 애니메이션
*/
* -------------------------------- */
function getNaturalDisplay( element ) {
switch ( element.tagName ) {
case 'TABLE':
return 'table';


case 'TBODY':
function prepareContent( target ) {
return 'table-row-group';
var existing =
target.querySelector(
':scope > .' + CONTENT_CLASS
);


case 'THEAD':
if ( existing ) {
return 'table-header-group';
return existing;
}


case 'TFOOT':
var toggle =
return 'table-footer-group';
target.querySelector(
':scope > [class*="mw-customtoggle-"]'
);


case 'TR':
if ( !toggle ) {
return 'table-row';
return null;
}


case 'TD':
var firstContent =
case 'TH':
toggle.nextSibling;
return 'table-cell';


case 'COLGROUP':
if ( !firstContent ) {
return 'table-column-group';
return null;
}


case 'COL':
var wrapper =
return 'table-column';
document.createElement( 'div' );


case 'CAPTION':
wrapper.className =
return 'table-caption';
CONTENT_CLASS;


case 'LI':
/*
return 'list-item';
* toggle 뒤의 모든 노드를 wrapper로 이동
*/
var node = firstContent;
var nodes = [];


default:
while ( node ) {
return 'block';
nodes.push( node );
node = node.nextSibling;
}
}
nodes.forEach( function ( child ) {
wrapper.appendChild( child );
} );
target.appendChild( wrapper );
return wrapper;
}
}


/**
* 현재 접혀있는지 확인
*/
function isCollapsed( target, content ) {
if ( target.classList.contains( 'mw-collapsed' ) ) {
return true;
}


return window.getComputedStyle( content ).display === 'none';
/* --------------------------------
* 같은 이름의 토글들 aria 상태 변경
* -------------------------------- */
 
function setToggleState( name, expanded ) {
var selector =
'.mw-customtoggle-' +
CSS.escape( name );
 
var toggles =
document.querySelectorAll( selector );
 
toggles.forEach( function ( toggle ) {
toggle.setAttribute(
'aria-expanded',
expanded ? 'true' : 'false'
);
} );
}
}


/**
 
/* --------------------------------
* 펼치기
* 펼치기
*/
* -------------------------------- */
function expand( target, content, toggle ) {
 
if ( content.classList.contains( ANIMATING_CLASS ) ) {
function expand( name, target, content ) {
if (
content.classList.contains(
ANIMATING_CLASS
)
) {
return;
return;
}
}


content.classList.add( ANIMATING_CLASS );
content.classList.add(
ANIMATING_CLASS
);


target.classList.remove( 'mw-collapsed' );
/*
* MediaWiki 접힌 상태 제거
*/
target.classList.remove(
'mw-collapsed'
);


/*
/*
* display:none 상태에서는 높이를 구할 수 없으므로
* 부모 자체가 display:none 된 경우 복구
* 원래 요소 유형에 맞는 display로 임시 복원
*/
target.style.display =
target.dataset.originalDisplay ||
'inline-block';
 
/*
* 내용 표시
*/
content.style.display = 'block';
content.style.overflow = 'hidden';
 
/*
* 자연 높이를 먼저 계산
*/
*/
content.style.display = getNaturalDisplay( content );
content.style.height = 'auto';
content.style.height = 'auto';
content.style.overflow = 'hidden';


var targetHeight = content.scrollHeight;
var endHeight =
content.scrollHeight;


/*
/*
* 0 높이에서 시작
* 시작 높이
*/
*/
content.style.height = '0px';
content.style.height = '0px';


/*
/*
* 강제 reflow
* 0px 상태를 브라우저에 확정
*/
*/
content.offsetHeight;
content.offsetHeight;


var animation = content.animate(
content.style.transition =
[
'height ' +
{
DURATION +
height: '0px'
'ms cubic-bezier(0.4, 0, 0.2, 1)';
},
 
{
requestAnimationFrame(
height: targetHeight + 'px'
function () {
}
content.style.height =
],
endHeight + 'px';
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
}
);
);


animation.onfinish = function () {
setToggleState(
content.classList.remove( ANIMATING_CLASS );
name,
true
);


/*
window.setTimeout(
* 원래 CSS가 다시 레이아웃을 결정하도록
function () {
* 인라인 스타일 제거
content.style.transition = '';
*/
content.style.height = '';
content.style.display = '';
content.style.overflow = '';
content.style.height = '';
content.style.display = '';
content.style.overflow = '';


toggle.setAttribute(
content.classList.remove(
'aria-expanded',
ANIMATING_CLASS
'true'
);
);
},
DURATION + 30
);
}


animation.cancel();
};


animation.oncancel = function () {
/* --------------------------------
content.classList.remove( ANIMATING_CLASS );
* 접기
};
* -------------------------------- */
}


/**
function collapse( name, target, content ) {
* 접기
if (
*/
content.classList.contains(
function collapse( target, content, toggle ) {
ANIMATING_CLASS
if ( content.classList.contains( ANIMATING_CLASS ) ) {
)
) {
return;
return;
}
}


content.classList.add( ANIMATING_CLASS );
content.classList.add(
ANIMATING_CLASS
);


/*
* 현재 높이 측정
*/
var startHeight =
var startHeight =
content.getBoundingClientRect().height;
content.getBoundingClientRect()
.height;


/*
content.style.display = 'block';
* 애니메이션 동안 display 타입 유지
*/
content.style.display = getNaturalDisplay( content );
content.style.height = startHeight + 'px';
content.style.overflow = 'hidden';
content.style.overflow = 'hidden';
content.style.height =
startHeight + 'px';


/*
/*
* 강제 reflow
* 현재 높이 확정
*/
*/
content.offsetHeight;
content.offsetHeight;


var animation = content.animate(
content.style.transition =
[
'height ' +
{
DURATION +
height: startHeight + 'px'
'ms cubic-bezier(0.4, 0, 0.2, 1)';
},
 
{
requestAnimationFrame(
height: '0px'
function () {
}
content.style.height =
],
'0px';
{
duration: DURATION,
easing: 'ease',
fill: 'forwards'
}
}
);
);


animation.onfinish = function () {
setToggleState(
target.classList.add( 'mw-collapsed' );
name,
false
);


content.classList.remove( ANIMATING_CLASS );
window.setTimeout(
function () {
content.style.transition = '';
content.style.height = '';
content.style.overflow = '';
content.style.display = 'none';


/*
target.classList.add(
* 접힌 상태 유지
'mw-collapsed'
*/
);
content.style.display = 'none';


/*
content.classList.remove(
* height와 overflow만 제거
ANIMATING_CLASS
*/
);
content.style.height = '';
},
content.style.overflow = '';
DURATION + 30
);
}


toggle.setAttribute(
'aria-expanded',
'false'
);


animation.cancel();
/* --------------------------------
};
* 현재 열린 상태인지 검사
* -------------------------------- */
 
function isOpen( target, content ) {
if (
target.classList.contains(
'mw-collapsed'
)
) {
return false;
}
 
if (
window.getComputedStyle(
content
).display === 'none'
) {
return false;
}


animation.oncancel = function () {
return true;
content.classList.remove( ANIMATING_CLASS );
};
}
}


/**
 
* 실제 토글 처리
/* --------------------------------
*/
* 버튼 클릭
function toggleCollapsible( toggle ) {
* -------------------------------- */
var name = getToggleName( toggle );
 
function handleToggle( event ) {
var toggle =
event.target.closest(
'[class*="mw-customtoggle-"]'
);
 
if ( !toggle ) {
return;
}
 
var name =
getName( toggle );


if ( !name ) {
if ( !name ) {
return false;
return;
}
}


var target = getTarget( name );
var target =
getContentTarget( name );


/*
* 우리가 처리할 수 없는 구조면
* MediaWiki 기본 기능을 그대로 사용
*/
if ( !target ) {
if ( !target ) {
return false;
return;
}
}


var content = getContent( target );
var content =
prepareContent( target );


if ( !content ) {
if ( !content ) {
return false;
return;
}
}


if ( isCollapsed( target, content ) ) {
/*
expand(
* 여기까지 왔을 때만
* MediaWiki 기본 즉시 토글 차단
*/
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
 
if (
isOpen(
target,
content
)
) {
collapse(
name,
target,
target,
content,
content
toggle
);
);
} else {
} else {
collapse(
expand(
name,
target,
target,
content,
content
toggle
);
);
}
}
}


return true;
}


/**
/* --------------------------------
* 마우스 클릭
* 초기 설정
*
* -------------------------------- */
* MediaWiki 기본 customtoggle보다 먼저 실행하기 위해
 
* capture=true 사용
function initialize() {
*/
var toggles =
document.addEventListener(
document.querySelectorAll(
'click',
'[class*="mw-customtoggle-"]'
function ( event ) {
var toggle = event.target.closest(
'.mw-customtoggle'
);
);


if ( !toggle ) {
toggles.forEach(
return;
function ( toggle ) {
if (
toggle.hasAttribute(
INITIALIZED
)
) {
return;
}
 
var name =
getName( toggle );
 
if ( !name ) {
return;
}
 
var target =
getContentTarget(
name
);
 
if ( !target ) {
return;
}
 
var content =
prepareContent(
target
);
 
if ( !content ) {
return;
}
 
/*
* 원래 display 기억
*/
if (
!target.dataset.originalDisplay
) {
target.dataset.originalDisplay =
target.style.display ||
'inline-block';
}
 
/*
* 원래 mw-collapsed인 경우
* 내용만 숨긴다.
*
* 버튼 부모 전체를 숨기지 않음.
*/
if (
target.classList.contains(
'mw-collapsed'
)
) {
content.style.display =
'none';
 
setToggleState(
name,
false
);
}
 
toggle.setAttribute(
INITIALIZED,
'1'
);
}
}
);
}


if ( !getToggleName( toggle ) ) {
return;
}


event.preventDefault();
/* --------------------------------
event.stopPropagation();
* 클릭 이벤트
event.stopImmediatePropagation();
*
* capture에서 처리하되,
* 실제 target을 찾은 경우에만
* MediaWiki 기본 이벤트 차단
* -------------------------------- */


toggleCollapsible( toggle );
document.addEventListener(
},
'click',
handleToggle,
true
true
);
);


/**
 
* Enter / Space 키 지원
/* --------------------------------
*/
* 키보드
* -------------------------------- */
 
document.addEventListener(
document.addEventListener(
'keydown',
'keydown',
373번째 줄: 554번째 줄:
}
}


var toggle = event.target.closest(
handleToggle( event );
'.mw-customtoggle'
},
);
true
);
 


if ( !toggle ) {
/* --------------------------------
return;
* 실행
}
* -------------------------------- */


if ( !getToggleName( toggle ) ) {
if (
return;
document.readyState === 'loading'
}
) {
document.addEventListener(
'DOMContentLoaded',
initialize
);
} else {
initialize();
}


event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();


toggleCollapsible( toggle );
/*
},
* MediaWiki 동적 로딩 대응
true
*/
);
if (
window.mw &&
mw.hook
) {
mw.hook(
'wikipage.content'
).add(
function () {
initialize();
}
);
}


}() );
}() );

2026년 8월 20일 (목) 13:30 판

/**
 * MediaWiki 커스텀 펼치기/접기 슬라이드 애니메이션
 *
 * 같은 mw-customcollapsible-XXX ID가 2개 존재하는 틀 지원
 *
 * 예:
 *
 * [첫 번째]
 * <div id="mw-customcollapsible-XXX">
 *     <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
 * </div>
 *
 * [두 번째]
 * <div id="mw-customcollapsible-XXX" class="mw-collapsed">
 *     <div class="mw-customtoggle-XXX">[ 버튼 ]</div>
 *     <table>내용</table>
 * </div>
 *
 * 틀 원본 수정 필요 없음.
 */

( function () {
	'use strict';

	var DURATION = 350;

	var INITIALIZED = 'data-slide-collapse-ready';
	var CONTENT_CLASS = 'mw-slide-collapse-content';
	var ANIMATING_CLASS = 'mw-slide-collapse-animating';


	/* --------------------------------
	 * mw-customtoggle-XXX 에서 XXX 추출
	 * -------------------------------- */

	function getName( 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;
	}


	/* --------------------------------
	 * 같은 ID를 가진 collapsible 전부 찾기
	 *
	 * getElementById() 사용하지 않음!
	 * -------------------------------- */

	function getTargets( name ) {
		if ( !name ) {
			return [];
		}

		var id =
			'mw-customcollapsible-' + name;

		return Array.prototype.slice.call(
			document.querySelectorAll(
				'[id="' +
				CSS.escape( id ) +
				'"]'
			)
		);
	}


	/* --------------------------------
	 * 실제 내용이 들어있는 collapsible 찾기
	 *
	 * table 등이 들어있는 두 번째 div를 선택
	 * -------------------------------- */

	function getContentTarget( name ) {
		var targets = getTargets( name );
		var i;

		/*
		 * 버튼 외에 다른 요소가 들어있는 target을 찾는다.
		 */
		for ( i = 0; i < targets.length; i++ ) {
			var target = targets[ i ];
			var toggle = target.querySelector(
				'[class*="mw-customtoggle-"]'
			);

			if ( !toggle ) {
				continue;
			}

			var node = toggle.nextElementSibling;

			if ( node ) {
				return target;
			}
		}

		return null;
	}


	/* --------------------------------
	 * 실제 내용 wrapper 생성
	 *
	 * table 자체의 높이를 변경하지 않고
	 * 바깥 DIV 높이를 애니메이션
	 * -------------------------------- */

	function prepareContent( target ) {
		var existing =
			target.querySelector(
				':scope > .' + CONTENT_CLASS
			);

		if ( existing ) {
			return existing;
		}

		var toggle =
			target.querySelector(
				':scope > [class*="mw-customtoggle-"]'
			);

		if ( !toggle ) {
			return null;
		}

		var firstContent =
			toggle.nextSibling;

		if ( !firstContent ) {
			return null;
		}

		var wrapper =
			document.createElement( 'div' );

		wrapper.className =
			CONTENT_CLASS;

		/*
		 * toggle 뒤의 모든 노드를 wrapper로 이동
		 */
		var node = firstContent;
		var nodes = [];

		while ( node ) {
			nodes.push( node );
			node = node.nextSibling;
		}

		nodes.forEach( function ( child ) {
			wrapper.appendChild( child );
		} );

		target.appendChild( wrapper );

		return wrapper;
	}


	/* --------------------------------
	 * 같은 이름의 토글들 aria 상태 변경
	 * -------------------------------- */

	function setToggleState( name, expanded ) {
		var selector =
			'.mw-customtoggle-' +
			CSS.escape( name );

		var toggles =
			document.querySelectorAll( selector );

		toggles.forEach( function ( toggle ) {
			toggle.setAttribute(
				'aria-expanded',
				expanded ? 'true' : 'false'
			);
		} );
	}


	/* --------------------------------
	 * 펼치기
	 * -------------------------------- */

	function expand( name, target, content ) {
		if (
			content.classList.contains(
				ANIMATING_CLASS
			)
		) {
			return;
		}

		content.classList.add(
			ANIMATING_CLASS
		);

		/*
		 * MediaWiki 접힌 상태 제거
		 */
		target.classList.remove(
			'mw-collapsed'
		);

		/*
		 * 부모 자체가 display:none 된 경우 복구
		 */
		target.style.display =
			target.dataset.originalDisplay ||
			'inline-block';

		/*
		 * 내용 표시
		 */
		content.style.display = 'block';
		content.style.overflow = 'hidden';

		/*
		 * 자연 높이를 먼저 계산
		 */
		content.style.height = 'auto';

		var endHeight =
			content.scrollHeight;

		/*
		 * 시작 높이
		 */
		content.style.height = '0px';

		/*
		 * 0px 상태를 브라우저에 확정
		 */
		content.offsetHeight;

		content.style.transition =
			'height ' +
			DURATION +
			'ms cubic-bezier(0.4, 0, 0.2, 1)';

		requestAnimationFrame(
			function () {
				content.style.height =
					endHeight + 'px';
			}
		);

		setToggleState(
			name,
			true
		);

		window.setTimeout(
			function () {
				content.style.transition = '';
				content.style.height = '';
				content.style.overflow = '';
				content.style.display = '';

				content.classList.remove(
					ANIMATING_CLASS
				);
			},
			DURATION + 30
		);
	}


	/* --------------------------------
	 * 접기
	 * -------------------------------- */

	function collapse( name, target, content ) {
		if (
			content.classList.contains(
				ANIMATING_CLASS
			)
		) {
			return;
		}

		content.classList.add(
			ANIMATING_CLASS
		);

		var startHeight =
			content.getBoundingClientRect()
				.height;

		content.style.display = 'block';
		content.style.overflow = 'hidden';

		content.style.height =
			startHeight + 'px';

		/*
		 * 현재 높이 확정
		 */
		content.offsetHeight;

		content.style.transition =
			'height ' +
			DURATION +
			'ms cubic-bezier(0.4, 0, 0.2, 1)';

		requestAnimationFrame(
			function () {
				content.style.height =
					'0px';
			}
		);

		setToggleState(
			name,
			false
		);

		window.setTimeout(
			function () {
				content.style.transition = '';
				content.style.height = '';
				content.style.overflow = '';
				content.style.display = 'none';

				target.classList.add(
					'mw-collapsed'
				);

				content.classList.remove(
					ANIMATING_CLASS
				);
			},
			DURATION + 30
		);
	}


	/* --------------------------------
	 * 현재 열린 상태인지 검사
	 * -------------------------------- */

	function isOpen( target, content ) {
		if (
			target.classList.contains(
				'mw-collapsed'
			)
		) {
			return false;
		}

		if (
			window.getComputedStyle(
				content
			).display === 'none'
		) {
			return false;
		}

		return true;
	}


	/* --------------------------------
	 * 버튼 클릭
	 * -------------------------------- */

	function handleToggle( event ) {
		var toggle =
			event.target.closest(
				'[class*="mw-customtoggle-"]'
			);

		if ( !toggle ) {
			return;
		}

		var name =
			getName( toggle );

		if ( !name ) {
			return;
		}

		var target =
			getContentTarget( name );

		/*
		 * 우리가 처리할 수 없는 구조면
		 * MediaWiki 기본 기능을 그대로 사용
		 */
		if ( !target ) {
			return;
		}

		var content =
			prepareContent( target );

		if ( !content ) {
			return;
		}

		/*
		 * 여기까지 왔을 때만
		 * MediaWiki 기본 즉시 토글 차단
		 */
		event.preventDefault();
		event.stopPropagation();
		event.stopImmediatePropagation();

		if (
			isOpen(
				target,
				content
			)
		) {
			collapse(
				name,
				target,
				content
			);
		} else {
			expand(
				name,
				target,
				content
			);
		}
	}


	/* --------------------------------
	 * 초기 설정
	 * -------------------------------- */

	function initialize() {
		var toggles =
			document.querySelectorAll(
				'[class*="mw-customtoggle-"]'
			);

		toggles.forEach(
			function ( toggle ) {
				if (
					toggle.hasAttribute(
						INITIALIZED
					)
				) {
					return;
				}

				var name =
					getName( toggle );

				if ( !name ) {
					return;
				}

				var target =
					getContentTarget(
						name
					);

				if ( !target ) {
					return;
				}

				var content =
					prepareContent(
						target
					);

				if ( !content ) {
					return;
				}

				/*
				 * 원래 display 기억
				 */
				if (
					!target.dataset.originalDisplay
				) {
					target.dataset.originalDisplay =
						target.style.display ||
						'inline-block';
				}

				/*
				 * 원래 mw-collapsed인 경우
				 * 내용만 숨긴다.
				 *
				 * 버튼 부모 전체를 숨기지 않음.
				 */
				if (
					target.classList.contains(
						'mw-collapsed'
					)
				) {
					content.style.display =
						'none';

					setToggleState(
						name,
						false
					);
				}

				toggle.setAttribute(
					INITIALIZED,
					'1'
				);
			}
		);
	}


	/* --------------------------------
	 * 클릭 이벤트
	 *
	 * capture에서 처리하되,
	 * 실제 target을 찾은 경우에만
	 * MediaWiki 기본 이벤트 차단
	 * -------------------------------- */

	document.addEventListener(
		'click',
		handleToggle,
		true
	);


	/* --------------------------------
	 * 키보드
	 * -------------------------------- */

	document.addEventListener(
		'keydown',
		function ( event ) {
			if (
				event.key !== 'Enter' &&
				event.key !== ' '
			) {
				return;
			}

			handleToggle( event );
		},
		true
	);


	/* --------------------------------
	 * 실행
	 * -------------------------------- */

	if (
		document.readyState === 'loading'
	) {
		document.addEventListener(
			'DOMContentLoaded',
			initialize
		);
	} else {
		initialize();
	}


	/*
	 * MediaWiki 동적 로딩 대응
	 */
	if (
		window.mw &&
		mw.hook
	) {
		mw.hook(
			'wikipage.content'
		).add(
			function () {
				initialize();
			}
		);
	}

}() );