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

편집 요약 없음
편집 요약 없음
1번째 줄: 1번째 줄:
(function ($) {
/**
     'use strict';
* MediaWiki 커스텀 펼치기 / 접기 애니메이션
*
* 지원 구조:
*
* 1.
* .mw-customtoggle-XXX
* #mw-customcollapsible-XXX
*     └─ .mw-collapsible-content
*
* 2.
* .mw-customtoggle-XXX
* #mw-customcollapsible-XXX
*    ├─ .mw-customtoggle-XXX
*    └─ table / .sakura-table-wrapper / 기타 콘텐츠
*
* 동일한 mw-customcollapsible-* ID가 여러 개 존재하는
* 둘러보기 틀 형태도 지원.
*/


    var DURATION = 300;
( function ( $, mw ) {
'use strict';


    function getToggleName(toggle) {
var DURATION = 300;
        var prefix = 'mw-customtoggle-';
var EASING = 'swing';
        var classes = toggle.classList;
        var i;


        for (i = 0; i < classes.length; i++) {
var DATA_TARGET =
            if (classes[i].indexOf(prefix) === 0) {
'mwAnimatedCollapseTarget';
                return classes[i].substring(prefix.length);
            }
        }


        return null;
var DATA_DISPLAY =
    }
'mwAnimatedNaturalDisplay';




    function findTarget(name) {
/**
        var id = 'mw-customcollapsible-' + name;
* 토글 클래스에서 이름 추출
        var targets = document.querySelectorAll(
*
            '[id="' + id + '"]'
* mw-customtoggle-Flora_LACTEA_01
        );
* →
* Flora_LACTEA_01
*/
function getToggleName( toggle ) {
var prefix = 'mw-customtoggle-';
var classes = toggle.classList;
var i;


        if (!targets.length) {
for ( i = 0; i < classes.length; i++ ) {
            return null;
if (
        }
classes[ i ].indexOf( prefix ) === 0
) {
return classes[ i ].substring(
prefix.length
);
}
}


        /*
return null;
        * 실제 mw-collapsible-content를 가진 요소 우선
}
        */
        var i;


        for (i = 0; i < targets.length; i++) {
            if (
                getDirectContent(targets[i])
            ) {
                return targets[i];
            }
        }


        return targets[0];
/**
    }
* CSS attribute selector escape
*/
function escapeAttribute( value ) {
return String( value )
.replace( /\\/g, '\\\\' )
.replace( /"/g, '\\"' );
}




    function getDirectContent(target) {
/**
        var children = target.children;
* 해당 요소의 직계 mw-collapsible-content 찾기
        var i;
*
* 중첩된 collapsible을 잘못 잡지 않도록
* querySelector가 아니라 children 검사
*/
function getDirectCollapsibleContent( target ) {
var children = target.children;
var i;


        for (i = 0; i < children.length; i++) {
for ( i = 0; i < children.length; i++ ) {
            if (
if (
                children[i].classList.contains(
children[ i ].classList.contains(
                    'mw-collapsible-content'
'mw-collapsible-content'
                )
)
            ) {
) {
                return children[i];
return children[ i ];
            }
}
        }
}


        return null;
return null;
    }
}




    function toggle(toggle) {
/**
        var name = getToggleName(toggle);
* 실제로 펼치고 접을 요소 결정
*
* .mw-collapsible-content가 있으면 그것을 사용.
*
* 없으면 target 자체가 display:none 되는 구조이므로
* target 자체를 애니메이션.
*/
function getAnimatedElement( target ) {
var content =
getDirectCollapsibleContent( target );


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


        var target = findTarget(name);
return target;
}


        if (!target) {
            return;
        }


        var content = getDirectContent(target);
/**
* candidate가 단순한 버튼 wrapper인지 판단
*/
function isToggleOnlyWrapper( candidate ) {
var children = candidate.children;


        if (!content) {
if ( children.length !== 1 ) {
            return;
return false;
        }
}


        var $content = $(content);
return !!getToggleName(
children[ 0 ]
);
}


        /*
        * 중복 애니메이션 방지
        */
        $content.stop(true, true);


        var collapsed =
/**
            target.classList.contains('mw-collapsed') ||
* 동일한 ID가 여러 개 있어도 실제 펼침 영역 선택
            $content.css('display') === 'none';
*/
function findTarget( name ) {
if ( !name ) {
return null;
}


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


        if (collapsed) {
var candidates =
document.querySelectorAll(
'[id="' +
escapeAttribute( id ) +
'"]'
);


            /*
if ( !candidates.length ) {
            * 펼치기
return null;
            */
}
            target.classList.remove('mw-collapsed');


            $content
                .hide()
                .slideDown(DURATION);


            toggle.setAttribute(
/*
                'aria-expanded',
* 이전에 실제 target으로 판정한 요소 우선
                'true'
*/
            );
var i;


        } else {
for ( i = 0; i < candidates.length; i++ ) {
if (
$( candidates[ i ] ).data(
DATA_TARGET
)
) {
return candidates[ i ];
}
}


            /*
            * 접기
            */
            $content.slideUp(
                DURATION,
                function () {
                    target.classList.add(
                        'mw-collapsed'
                    );
                }
            );


            toggle.setAttribute(
if ( candidates.length === 1 ) {
                'aria-expanded',
$( candidates[ 0 ] ).data(
                'false'
DATA_TARGET,
            );
true
        }
);
    }


return candidates[ 0 ];
}


    document.addEventListener(
        'click',
        function (event) {
            var toggleElement =
                event.target.closest(
                    '.mw-customtoggle'
                );


            if (!toggleElement) {
var best = null;
                return;
var bestScore = -Infinity;
            }


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


            /*
for ( i = 0; i < candidates.length; i++ ) {
            * MediaWiki 기본 클릭 동작 차단
var candidate = candidates[ i ];
            */
var score = 0;
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();


            toggle(toggleElement);
        },
        true
    );


/*
* 초기 상태가 mw-collapsed라면
* 실제 내용 영역일 가능성이 매우 높음
*/
if (
candidate.classList.contains(
'mw-collapsed'
)
) {
score += 10000;
}


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


            var toggleElement =
/*
                event.target.closest(
* MediaWiki 표준 content wrapper
                    '.mw-customtoggle'
*/
                );
if (
getDirectCollapsibleContent(
candidate
)
) {
score += 8000;
}


            if (!toggleElement) {
                return;
            }


            if (!getToggleName(toggleElement)) {
/*
                return;
* Sakura 표 wrapper
            }
*/
if (
candidate.querySelector(
':scope > .sakura-table-wrapper'
)
) {
score += 6000;
}


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


            toggle(toggleElement);
/*
        },
* 실제 표 포함
        true
*/
    );
if (
candidate.querySelector(
'table'
)
) {
score += 3000;
}


}(jQuery));
 
if (
candidate.querySelector(
'.wikitable'
)
) {
score += 1000;
}
 
 
/*
* 버튼 하나만 들어간 wrapper는 감점
*/
if (
isToggleOnlyWrapper(
candidate
)
) {
score -= 10000;
}
 
 
/*
* 내부 콘텐츠가 많을수록 가산
*/
score +=
candidate.querySelectorAll( '*' )
.length;
 
 
if ( score > bestScore ) {
bestScore = score;
best = candidate;
}
}
 
 
if ( best ) {
$( best ).data(
DATA_TARGET,
true
);
}
 
 
return best;
}
 
 
/**
* 자연스러운 display 값 계산
*
* 현재 display:none인 경우 직접 태그별 기본값 사용
*/
function getNaturalDisplay( element ) {
 
var saved =
$( element ).data(
DATA_DISPLAY
);
 
if ( saved ) {
return saved;
}
 
 
var display =
window.getComputedStyle(
element
).display;
 
 
if (
display &&
display !== 'none'
) {
$( element ).data(
DATA_DISPLAY,
display
);
 
return display;
}
 
 
switch ( element.tagName ) {
 
case 'TABLE':
display = 'table';
break;
 
case 'TBODY':
display = 'table-row-group';
break;
 
case 'THEAD':
display = 'table-header-group';
break;
 
case 'TFOOT':
display = 'table-footer-group';
break;
 
case 'TR':
display = 'table-row';
break;
 
case 'TD':
case 'TH':
display = 'table-cell';
break;
 
case 'LI':
display = 'list-item';
break;
 
default:
 
/*
* 인라인 스타일상 inline-block인
* collapsible도 많으므로 먼저 확인
*/
if (
element.style.display &&
element.style.display !== 'none'
) {
display =
element.style.display;
} else {
display = 'block';
}
 
break;
}
 
 
$( element ).data(
DATA_DISPLAY,
display
);
 
return display;
}
 
 
/**
* 현재 접힘 상태
*/
function isCollapsed(
target,
animatedElement
) {
 
if (
target.classList.contains(
'mw-collapsed'
)
) {
return true;
}
 
return (
window.getComputedStyle(
animatedElement
).display === 'none'
);
}
 
 
/**
* 같은 name을 가진 모든 toggle의 aria-expanded 변경
*/
function updateToggleState(
name,
expanded
) {
 
var prefix =
'mw-customtoggle-' + name;
 
var toggles =
document.getElementsByClassName(
prefix
);
 
var i;
 
for (
i = 0;
i < toggles.length;
i++
) {
toggles[ i ].setAttribute(
'aria-expanded',
expanded ? 'true' : 'false'
);
}
}
 
 
/**
* 펼치기
*/
function expand(
name,
target,
animatedElement
) {
 
var $element =
$( animatedElement );
 
 
/*
* 기존 애니메이션 즉시 중단
*/
$element.stop(
true,
false
);
 
 
target.classList.remove(
'mw-collapsed'
);
 
 
/*
* target 자체가 애니메이션 대상인 경우
*
* 기존 inline style="display:none"을
* slideDown이 처리할 수 있도록 복구
*/
if (
animatedElement === target
) {
 
var naturalDisplay =
getNaturalDisplay(
target
);
 
/*
* jQuery slideDown은 inline-block 같은
* display 복원에서 상황에 따라 문제가 있으므로
* display를 직접 설정한 다음 height를 애니메이션
*/
$element.css(
'display',
naturalDisplay
);
 
$element.css(
{
overflow: 'hidden',
height: 0,
opacity: 0
}
);
 
 
var targetHeight =
animatedElement.scrollHeight;
 
 
$element.animate(
{
height:
targetHeight,
opacity: 1
},
{
duration:
DURATION,
 
easing:
EASING,
 
complete:
function () {
 
$element.css(
{
height: '',
overflow: '',
opacity: ''
}
);
 
 
updateToggleState(
name,
true
);
}
}
);
 
} else {
 
/*
* 표준 MediaWiki 구조
*
* mw-collapsible-content만 펼침
*/
$element
.stop( true, true )
.slideDown(
DURATION,
function () {
 
$element.css(
{
height: '',
overflow: ''
}
);
 
 
updateToggleState(
name,
true
);
}
);
}
}
 
 
/**
* 접기
*/
function collapse(
name,
target,
animatedElement
) {
 
var $element =
$( animatedElement );
 
 
$element.stop(
true,
false
);
 
 
/*
* target 자체가 display:none 되는 구조
*/
if (
animatedElement === target
) {
 
var startHeight =
animatedElement
.getBoundingClientRect()
.height;
 
 
$element.css(
{
height:
startHeight,
overflow:
'hidden',
opacity:
1
}
);
 
 
$element.animate(
{
height: 0,
opacity: 0
},
{
duration:
DURATION,
 
easing:
EASING,
 
complete:
function () {
 
target.classList.add(
'mw-collapsed'
);
 
 
$element.css(
{
display:
'none',
height:
'',
overflow:
'',
opacity:
''
}
);
 
 
updateToggleState(
name,
false
);
}
}
);
 
} else {
 
/*
* 표준 mw-collapsible-content
*/
$element.slideUp(
DURATION,
function () {
 
target.classList.add(
'mw-collapsed'
);
 
 
updateToggleState(
name,
false
);
}
);
}
}
 
 
/**
* 토글 실행
*/
function toggleCollapsible(
toggle
) {
 
var name =
getToggleName(
toggle
);
 
if ( !name ) {
return false;
}
 
 
var target =
findTarget(
name
);
 
if ( !target ) {
return false;
}
 
 
var animatedElement =
getAnimatedElement(
target
);
 
 
if (
isCollapsed(
target,
animatedElement
)
) {
 
expand(
name,
target,
animatedElement
);
 
} else {
 
collapse(
name,
target,
animatedElement
);
}
 
 
return true;
}
 
 
/**
* 클릭
*
* MediaWiki 기본 customtoggle보다
* 먼저 처리
*/
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();
 
 
toggleCollapsible(
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();
 
 
toggleCollapsible(
toggle
);
},
true
);
 
 
}( jQuery, mediaWiki ) );

2026년 8월 20일 (목) 14:06 판

/**
 * MediaWiki 커스텀 펼치기 / 접기 애니메이션
 *
 * 지원 구조:
 *
 * 1.
 * .mw-customtoggle-XXX
 * #mw-customcollapsible-XXX
 *     └─ .mw-collapsible-content
 *
 * 2.
 * .mw-customtoggle-XXX
 * #mw-customcollapsible-XXX
 *     ├─ .mw-customtoggle-XXX
 *     └─ table / .sakura-table-wrapper / 기타 콘텐츠
 *
 * 동일한 mw-customcollapsible-* ID가 여러 개 존재하는
 * 둘러보기 틀 형태도 지원.
 */

( function ( $, mw ) {
	'use strict';

	var DURATION = 300;
	var EASING = 'swing';

	var DATA_TARGET =
		'mwAnimatedCollapseTarget';

	var DATA_DISPLAY =
		'mwAnimatedNaturalDisplay';


	/**
	 * 토글 클래스에서 이름 추출
	 *
	 * mw-customtoggle-Flora_LACTEA_01
	 * →
	 * Flora_LACTEA_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;
	}


	/**
	 * CSS attribute selector escape
	 */
	function escapeAttribute( value ) {
		return String( value )
			.replace( /\\/g, '\\\\' )
			.replace( /"/g, '\\"' );
	}


	/**
	 * 해당 요소의 직계 mw-collapsible-content 찾기
	 *
	 * 중첩된 collapsible을 잘못 잡지 않도록
	 * querySelector가 아니라 children 검사
	 */
	function getDirectCollapsibleContent( target ) {
		var children = target.children;
		var i;

		for ( i = 0; i < children.length; i++ ) {
			if (
				children[ i ].classList.contains(
					'mw-collapsible-content'
				)
			) {
				return children[ i ];
			}
		}

		return null;
	}


	/**
	 * 실제로 펼치고 접을 요소 결정
	 *
	 * .mw-collapsible-content가 있으면 그것을 사용.
	 *
	 * 없으면 target 자체가 display:none 되는 구조이므로
	 * target 자체를 애니메이션.
	 */
	function getAnimatedElement( target ) {
		var content =
			getDirectCollapsibleContent( target );

		if ( content ) {
			return content;
		}

		return target;
	}


	/**
	 * candidate가 단순한 버튼 wrapper인지 판단
	 */
	function isToggleOnlyWrapper( candidate ) {
		var children = candidate.children;

		if ( children.length !== 1 ) {
			return false;
		}

		return !!getToggleName(
			children[ 0 ]
		);
	}


	/**
	 * 동일한 ID가 여러 개 있어도 실제 펼침 영역 선택
	 */
	function findTarget( name ) {
		if ( !name ) {
			return null;
		}

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

		var candidates =
			document.querySelectorAll(
				'[id="' +
				escapeAttribute( id ) +
				'"]'
			);

		if ( !candidates.length ) {
			return null;
		}


		/*
		 * 이전에 실제 target으로 판정한 요소 우선
		 */
		var i;

		for ( i = 0; i < candidates.length; i++ ) {
			if (
				$( candidates[ i ] ).data(
					DATA_TARGET
				)
			) {
				return candidates[ i ];
			}
		}


		if ( candidates.length === 1 ) {
			$( candidates[ 0 ] ).data(
				DATA_TARGET,
				true
			);

			return candidates[ 0 ];
		}


		var best = null;
		var bestScore = -Infinity;


		for ( i = 0; i < candidates.length; i++ ) {
			var candidate = candidates[ i ];
			var score = 0;


			/*
			 * 초기 상태가 mw-collapsed라면
			 * 실제 내용 영역일 가능성이 매우 높음
			 */
			if (
				candidate.classList.contains(
					'mw-collapsed'
				)
			) {
				score += 10000;
			}


			/*
			 * MediaWiki 표준 content wrapper
			 */
			if (
				getDirectCollapsibleContent(
					candidate
				)
			) {
				score += 8000;
			}


			/*
			 * Sakura 표 wrapper
			 */
			if (
				candidate.querySelector(
					':scope > .sakura-table-wrapper'
				)
			) {
				score += 6000;
			}


			/*
			 * 실제 표 포함
			 */
			if (
				candidate.querySelector(
					'table'
				)
			) {
				score += 3000;
			}


			if (
				candidate.querySelector(
					'.wikitable'
				)
			) {
				score += 1000;
			}


			/*
			 * 버튼 하나만 들어간 wrapper는 감점
			 */
			if (
				isToggleOnlyWrapper(
					candidate
				)
			) {
				score -= 10000;
			}


			/*
			 * 내부 콘텐츠가 많을수록 가산
			 */
			score +=
				candidate.querySelectorAll( '*' )
					.length;


			if ( score > bestScore ) {
				bestScore = score;
				best = candidate;
			}
		}


		if ( best ) {
			$( best ).data(
				DATA_TARGET,
				true
			);
		}


		return best;
	}


	/**
	 * 자연스러운 display 값 계산
	 *
	 * 현재 display:none인 경우 직접 태그별 기본값 사용
	 */
	function getNaturalDisplay( element ) {

		var saved =
			$( element ).data(
				DATA_DISPLAY
			);

		if ( saved ) {
			return saved;
		}


		var display =
			window.getComputedStyle(
				element
			).display;


		if (
			display &&
			display !== 'none'
		) {
			$( element ).data(
				DATA_DISPLAY,
				display
			);

			return display;
		}


		switch ( element.tagName ) {

			case 'TABLE':
				display = 'table';
				break;

			case 'TBODY':
				display = 'table-row-group';
				break;

			case 'THEAD':
				display = 'table-header-group';
				break;

			case 'TFOOT':
				display = 'table-footer-group';
				break;

			case 'TR':
				display = 'table-row';
				break;

			case 'TD':
			case 'TH':
				display = 'table-cell';
				break;

			case 'LI':
				display = 'list-item';
				break;

			default:

				/*
				 * 인라인 스타일상 inline-block인
				 * collapsible도 많으므로 먼저 확인
				 */
				if (
					element.style.display &&
					element.style.display !== 'none'
				) {
					display =
						element.style.display;
				} else {
					display = 'block';
				}

				break;
		}


		$( element ).data(
			DATA_DISPLAY,
			display
		);

		return display;
	}


	/**
	 * 현재 접힘 상태
	 */
	function isCollapsed(
		target,
		animatedElement
	) {

		if (
			target.classList.contains(
				'mw-collapsed'
			)
		) {
			return true;
		}

		return (
			window.getComputedStyle(
				animatedElement
			).display === 'none'
		);
	}


	/**
	 * 같은 name을 가진 모든 toggle의 aria-expanded 변경
	 */
	function updateToggleState(
		name,
		expanded
	) {

		var prefix =
			'mw-customtoggle-' + name;

		var toggles =
			document.getElementsByClassName(
				prefix
			);

		var i;

		for (
			i = 0;
			i < toggles.length;
			i++
		) {
			toggles[ i ].setAttribute(
				'aria-expanded',
				expanded ? 'true' : 'false'
			);
		}
	}


	/**
	 * 펼치기
	 */
	function expand(
		name,
		target,
		animatedElement
	) {

		var $element =
			$( animatedElement );


		/*
		 * 기존 애니메이션 즉시 중단
		 */
		$element.stop(
			true,
			false
		);


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


		/*
		 * target 자체가 애니메이션 대상인 경우
		 *
		 * 기존 inline style="display:none"을
		 * slideDown이 처리할 수 있도록 복구
		 */
		if (
			animatedElement === target
		) {

			var naturalDisplay =
				getNaturalDisplay(
					target
				);

			/*
			 * jQuery slideDown은 inline-block 같은
			 * display 복원에서 상황에 따라 문제가 있으므로
			 * display를 직접 설정한 다음 height를 애니메이션
			 */
			$element.css(
				'display',
				naturalDisplay
			);

			$element.css(
				{
					overflow: 'hidden',
					height: 0,
					opacity: 0
				}
			);


			var targetHeight =
				animatedElement.scrollHeight;


			$element.animate(
				{
					height:
						targetHeight,
					opacity: 1
				},
				{
					duration:
						DURATION,

					easing:
						EASING,

					complete:
						function () {

							$element.css(
								{
									height: '',
									overflow: '',
									opacity: ''
								}
							);


							updateToggleState(
								name,
								true
							);
						}
				}
			);

		} else {

			/*
			 * 표준 MediaWiki 구조
			 *
			 * mw-collapsible-content만 펼침
			 */
			$element
				.stop( true, true )
				.slideDown(
					DURATION,
					function () {

						$element.css(
							{
								height: '',
								overflow: ''
							}
						);


						updateToggleState(
							name,
							true
						);
					}
				);
		}
	}


	/**
	 * 접기
	 */
	function collapse(
		name,
		target,
		animatedElement
	) {

		var $element =
			$( animatedElement );


		$element.stop(
			true,
			false
		);


		/*
		 * target 자체가 display:none 되는 구조
		 */
		if (
			animatedElement === target
		) {

			var startHeight =
				animatedElement
					.getBoundingClientRect()
					.height;


			$element.css(
				{
					height:
						startHeight,
					overflow:
						'hidden',
					opacity:
						1
				}
			);


			$element.animate(
				{
					height: 0,
					opacity: 0
				},
				{
					duration:
						DURATION,

					easing:
						EASING,

					complete:
						function () {

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


							$element.css(
								{
									display:
										'none',
									height:
										'',
									overflow:
										'',
									opacity:
										''
								}
							);


							updateToggleState(
								name,
								false
							);
						}
				}
			);

		} else {

			/*
			 * 표준 mw-collapsible-content
			 */
			$element.slideUp(
				DURATION,
				function () {

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


					updateToggleState(
						name,
						false
					);
				}
			);
		}
	}


	/**
	 * 토글 실행
	 */
	function toggleCollapsible(
		toggle
	) {

		var name =
			getToggleName(
				toggle
			);

		if ( !name ) {
			return false;
		}


		var target =
			findTarget(
				name
			);

		if ( !target ) {
			return false;
		}


		var animatedElement =
			getAnimatedElement(
				target
			);


		if (
			isCollapsed(
				target,
				animatedElement
			)
		) {

			expand(
				name,
				target,
				animatedElement
			);

		} else {

			collapse(
				name,
				target,
				animatedElement
			);
		}


		return true;
	}


	/**
	 * 클릭
	 *
	 * MediaWiki 기본 customtoggle보다
	 * 먼저 처리
	 */
	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();


			toggleCollapsible(
				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();


			toggleCollapsible(
				toggle
			);
		},
		true
	);


}( jQuery, mediaWiki ) );