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

편집 요약 없음
태그: 되돌려진 기여
편집 요약 없음
태그: 되돌려진 기여
1번째 줄: 1번째 줄:
/**
* MediaWiki 펼치기/접기 애니메이션
*
* 기존 MediaWiki 토글 동작은 건드리지 않고,
* 실제로 표시/숨김되는 요소에 애니메이션만 추가합니다.
*/
( function () {
( function () {
'use strict';
'use strict';
4번째 줄: 11번째 줄:
var DURATION = 300;
var DURATION = 300;


document.addEventListener(
function getToggleName( toggle ) {
'click',
var prefix = 'mw-customtoggle-';
function ( event ) {
var classes = toggle.classList;
var toggle = event.target.closest( '.mw-customtoggle' );
var i;


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


var name = null;
function getTarget( name ) {
if ( !name ) {
return null;
}


toggle.classList.forEach( function ( className ) {
return document.getElementById(
if ( className.indexOf( 'mw-customtoggle-' ) === 0 ) {
'mw-customcollapsible-' + name
name = className.substring(
);
'mw-customtoggle-'.length
}
);
}
} );


if ( !name ) {
/*
return;
* target 자체 안에 toggle만 있는 경우,
}
* target은 애니메이션 대상으로 사용하지 않습니다.
*
* 실제로 MediaWiki가 숨기고 표시하는 요소를
* 주변 DOM에서 찾습니다.
*/
function findAnimatedContent( target, toggle ) {
var content = target.querySelector(
'.mw-collapsible-content'
);


var target = document.getElementById(
if ( content && !content.contains( toggle ) ) {
'mw-customcollapsible-' + name
return content;
);
}


if ( !target ) {
/*
return;
* 다음 형제 요소가 실제 내용인 구조 대응
}
*/
var next = target.nextElementSibling;


/*
if ( next && !next.classList.contains( 'mw-customtoggle' ) ) {
* 실제 내용 영역이 있으면 그것을 사용하고,
return next;
* 없으면 target 자체 사용.
}
*
* 이제 toggle은 target 밖에 있으므로
* 버튼이 사라지는 문제가 없음.
*/
var content =
target.querySelector( '.mw-collapsible-content' ) ||
target;


var collapsed =
/*
target.classList.contains( 'mw-collapsed' ) ||
* 부모 내부에서 target 다음 요소 탐색
window.getComputedStyle( content ).display === 'none';
*/
if ( target.parentElement ) {
var siblings =
Array.prototype.slice.call(
target.parentElement.children
);


/*
var index = siblings.indexOf( target );
* MediaWiki 기본 토글 방지
*/
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();


if ( collapsed ) {
if (
expand( target, content );
index !== -1 &&
} else {
index + 1 < siblings.length
collapse( target, content );
) {
return siblings[ index + 1 ];
}
}
},
}
true
 
);
return null;
}


function expand( target, content ) {
function animateOpen( element ) {
target.classList.remove( 'mw-collapsed' );
if ( !element ) {
return;
}


content.style.display = 'block';
element.style.overflow = 'hidden';
content.style.height = 'auto';
element.style.height = 'auto';
content.style.overflow = 'hidden';


var height = content.scrollHeight;
var height = element.scrollHeight;


content.style.height = '0px';
element.style.height = '0px';


content.offsetHeight;
element.offsetHeight;


var animation = content.animate(
var animation = element.animate(
[
[
{ height: '0px' },
{ height: '0px' },
92번째 줄: 109번째 줄:


animation.onfinish = function () {
animation.onfinish = function () {
content.style.display = '';
element.style.height = '';
content.style.height = '';
element.style.overflow = '';
content.style.overflow = '';


animation.cancel();
animation.cancel();
100번째 줄: 116번째 줄:
}
}


function collapse( target, content ) {
function animateClose( element ) {
if ( !element ) {
return;
}
 
var height =
var height =
content.getBoundingClientRect().height;
element.getBoundingClientRect().height;
 
if ( height <= 0 ) {
return;
}


content.style.height = height + 'px';
element.style.overflow = 'hidden';
content.style.overflow = 'hidden';
element.style.height = height + 'px';


var animation = content.animate(
var animation = element.animate(
[
[
{ height: height + 'px' },
{ height: height + 'px' },
120번째 줄: 144번째 줄:


animation.onfinish = function () {
animation.onfinish = function () {
target.classList.add( 'mw-collapsed' );
element.style.height = '';
 
element.style.overflow = '';
content.style.display = 'none';
content.style.height = '';
content.style.overflow = '';


animation.cancel();
animation.cancel();
};
};
}
}
document.addEventListener(
'click',
function ( event ) {
var toggle =
event.target.closest(
'.mw-customtoggle'
);
if ( !toggle ) {
return;
}
var name = getToggleName( toggle );
if ( !name ) {
return;
}
var target = getTarget( name );
if ( !target ) {
return;
}
var content =
findAnimatedContent(
target,
toggle
);
if ( !content ) {
/*
* 내용을 특정할 수 없으면
* MediaWiki 기본 동작만 실행.
*
* 버튼 자체를 숨기는 일은 하지 않음.
*/
return;
}
/*
* 여기서는 기본 이벤트를 차단하지 않습니다.
* MediaWiki가 원래대로 펼치고 접게 둡니다.
*/
var wasHidden =
window.getComputedStyle( content )
.display === 'none' ||
content.getBoundingClientRect()
.height === 0;
/*
* MediaWiki 기본 토글 처리 직후 상태를 읽기 위해
* 다음 task에서 실행
*/
window.setTimeout(
function () {
var isHidden =
window.getComputedStyle(
content
).display === 'none' ||
content.getBoundingClientRect()
.height === 0;
if ( wasHidden && !isHidden ) {
animateOpen( content );
} else if (
!wasHidden &&
isHidden
) {
/*
* 이미 display:none이 되어버린 경우에는
* 애니메이션할 높이가 없기 때문에
* 이 구조에서는 접기 애니메이션을
* 완벽히 재현할 수 없습니다.
*/
}
},
0
);
},
false
);


}() );
}() );

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

/**
 * MediaWiki 펼치기/접기 애니메이션
 *
 * 기존 MediaWiki 토글 동작은 건드리지 않고,
 * 실제로 표시/숨김되는 요소에 애니메이션만 추가합니다.
 */

( function () {
	'use strict';

	var DURATION = 300;

	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( name ) {
		if ( !name ) {
			return null;
		}

		return document.getElementById(
			'mw-customcollapsible-' + name
		);
	}

	/*
	 * target 자체 안에 toggle만 있는 경우,
	 * target은 애니메이션 대상으로 사용하지 않습니다.
	 *
	 * 실제로 MediaWiki가 숨기고 표시하는 요소를
	 * 주변 DOM에서 찾습니다.
	 */
	function findAnimatedContent( target, toggle ) {
		var content = target.querySelector(
			'.mw-collapsible-content'
		);

		if ( content && !content.contains( toggle ) ) {
			return content;
		}

		/*
		 * 다음 형제 요소가 실제 내용인 구조 대응
		 */
		var next = target.nextElementSibling;

		if ( next && !next.classList.contains( 'mw-customtoggle' ) ) {
			return next;
		}

		/*
		 * 부모 내부에서 target 다음 요소 탐색
		 */
		if ( target.parentElement ) {
			var siblings =
				Array.prototype.slice.call(
					target.parentElement.children
				);

			var index = siblings.indexOf( target );

			if (
				index !== -1 &&
				index + 1 < siblings.length
			) {
				return siblings[ index + 1 ];
			}
		}

		return null;
	}

	function animateOpen( element ) {
		if ( !element ) {
			return;
		}

		element.style.overflow = 'hidden';
		element.style.height = 'auto';

		var height = element.scrollHeight;

		element.style.height = '0px';

		element.offsetHeight;

		var animation = element.animate(
			[
				{ height: '0px' },
				{ height: height + 'px' }
			],
			{
				duration: DURATION,
				easing: 'ease',
				fill: 'forwards'
			}
		);

		animation.onfinish = function () {
			element.style.height = '';
			element.style.overflow = '';

			animation.cancel();
		};
	}

	function animateClose( element ) {
		if ( !element ) {
			return;
		}

		var height =
			element.getBoundingClientRect().height;

		if ( height <= 0 ) {
			return;
		}

		element.style.overflow = 'hidden';
		element.style.height = height + 'px';

		var animation = element.animate(
			[
				{ height: height + 'px' },
				{ height: '0px' }
			],
			{
				duration: DURATION,
				easing: 'ease',
				fill: 'forwards'
			}
		);

		animation.onfinish = function () {
			element.style.height = '';
			element.style.overflow = '';

			animation.cancel();
		};
	}

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

			if ( !toggle ) {
				return;
			}

			var name = getToggleName( toggle );

			if ( !name ) {
				return;
			}

			var target = getTarget( name );

			if ( !target ) {
				return;
			}

			var content =
				findAnimatedContent(
					target,
					toggle
				);

			if ( !content ) {
				/*
				 * 내용을 특정할 수 없으면
				 * MediaWiki 기본 동작만 실행.
				 *
				 * 버튼 자체를 숨기는 일은 하지 않음.
				 */
				return;
			}

			/*
			 * 여기서는 기본 이벤트를 차단하지 않습니다.
			 * MediaWiki가 원래대로 펼치고 접게 둡니다.
			 */

			var wasHidden =
				window.getComputedStyle( content )
					.display === 'none' ||
				content.getBoundingClientRect()
					.height === 0;

			/*
			 * MediaWiki 기본 토글 처리 직후 상태를 읽기 위해
			 * 다음 task에서 실행
			 */
			window.setTimeout(
				function () {
					var isHidden =
						window.getComputedStyle(
							content
						).display === 'none' ||
						content.getBoundingClientRect()
							.height === 0;

					if ( wasHidden && !isHidden ) {
						animateOpen( content );
					} else if (
						!wasHidden &&
						isHidden
					) {
						/*
						 * 이미 display:none이 되어버린 경우에는
						 * 애니메이션할 높이가 없기 때문에
						 * 이 구조에서는 접기 애니메이션을
						 * 완벽히 재현할 수 없습니다.
						 */
					}
				},
				0
			);
		},
		false
	);

}() );