편집 요약 없음 |
편집 요약 없음 |
||
| 23번째 줄: | 23번째 줄: | ||
/* | /* | ||
* | * 중복 ID가 있을 수 있으므로 #id 대신 attribute selector 사용 | ||
*/ | */ | ||
var $targets = $content.find('[id="' + targetId + '"]'); | var $targets = $content.find('[id="' + targetId + '"]'); | ||
| 34번째 줄: | 34번째 줄: | ||
/* | /* | ||
* ===================================================== | * ===================================================== | ||
* | * 특수형 | ||
* | * | ||
* 같은 | * 아나운서 표처럼: | ||
* | * | ||
* 상단 버튼 wrapper | |||
* 실제 내용 패널 | |||
* | |||
* 이 같은 ID를 공유하고, | |||
* 실제 패널 안에 wikitable이 있는 구조 | |||
* ===================================================== | * ===================================================== | ||
*/ | */ | ||
var $specialPanel = $targets.filter(function () { | |||
return $(this).find('table.wikitable').length > 0; | |||
}); | |||
if ($targets.length > 1 && $specialPanel.length) { | |||
initStackedCollapsible( | initStackedCollapsible( | ||
$content, | $content, | ||
| 58번째 줄: | 62번째 줄: | ||
/* | /* | ||
* ===================================================== | * ===================================================== | ||
* | * 일반 MediaWiki collapsible | ||
* ===================================================== | * ===================================================== | ||
*/ | */ | ||
| 68번째 줄: | 72번째 줄: | ||
}); | }); | ||
}); | }); | ||
/* | /* | ||
* | * ============================================================= | ||
* 일반 펼치기 · 접기 | * 일반 펼치기 · 접기 | ||
* | * ============================================================= | ||
*/ | */ | ||
function initNormalCollapsible($content, cls, $target) { | function initNormalCollapsible($content, cls, $target) { | ||
| 80번째 줄: | 85번째 줄: | ||
/* | /* | ||
* | * 일반 MediaWiki 구조: | ||
* | |||
* .mw-collapsible | |||
* └ .mw-collapsible-content | |||
* | |||
* content가 있으면 content만 애니메이션 | |||
*/ | */ | ||
var $contentTarget = $target.children('.mw-collapsible-content'); | var $contentTarget = $target.children('.mw-collapsible-content'); | ||
| 88번째 줄: | 98번째 줄: | ||
} | } | ||
var $animTarget = $contentTarget.length | var $animTarget = $contentTarget.length | ||
? $contentTarget | ? $contentTarget | ||
: $target; | : $target; | ||
/* | |||
* 현재 상태를 기준으로 초기 aria 설정 | |||
*/ | |||
var initiallyOpen = $animTarget.is(':visible'); | |||
$toggles.attr( | |||
'aria-expanded', | |||
initiallyOpen ? 'true' : 'false' | |||
); | |||
| 101번째 줄: | 118번째 줄: | ||
.on('click.smoothSlide', function (e) { | .on('click.smoothSlide', function (e) { | ||
e.preventDefault(); | e.preventDefault(); | ||
e.stopImmediatePropagation(); | e.stopImmediatePropagation(); | ||
/* | |||
* 애니메이션 중 다시 눌렀을 때 | |||
* queue가 꼬이는 것을 방지 | |||
*/ | |||
if ($animTarget.is(':animated')) { | |||
return false; | |||
} | |||
var opening = !$animTarget.is(':visible'); | var opening = !$animTarget.is(':visible'); | ||
/* | |||
* ------------------------------------------------- | |||
* 펼치기 | |||
* ------------------------------------------------- | |||
*/ | |||
if (opening) { | if (opening) { | ||
/* | /* | ||
* 먼저 | * MediaWiki의 collapsed 상태를 먼저 제거 | ||
*/ | */ | ||
$target.removeClass('mw-collapsed'); | $target.removeClass('mw-collapsed'); | ||
/* | |||
* slideDown | |||
*/ | |||
$animTarget | $animTarget | ||
.stop(true, false) | .stop(true, false) | ||
.slideDown( | .hide() | ||
.slideDown(250, function () { | |||
/* | |||
* jQuery가 남기는 애니메이션용 inline style 정리 | |||
*/ | |||
$(this).css({ | $(this).css({ | ||
height: '', | height: '', | ||
| 132번째 줄: | 166번째 줄: | ||
}); | }); | ||
} else { | |||
$toggles.attr('aria-expanded', 'true'); | |||
} | |||
/* | |||
* ------------------------------------------------- | |||
* 접기 | |||
* ------------------------------------------------- | |||
*/ | |||
else { | |||
$animTarget | $animTarget | ||
.stop(true, false) | .stop(true, false) | ||
.slideUp( | .slideUp(250, function () { | ||
/* | /* | ||
* | * 애니메이션이 끝난 뒤 collapsed 복원 | ||
*/ | */ | ||
$target.addClass('mw-collapsed'); | $target.addClass('mw-collapsed'); | ||
| 153번째 줄: | 197번째 줄: | ||
}); | }); | ||
}); | }); | ||
$toggles.attr('aria-expanded', 'false'); | |||
} | } | ||
return false; | return false; | ||
| 168번째 줄: | 210번째 줄: | ||
/* | /* | ||
* | * ============================================================= | ||
* | * 아나운서형 특수 펼치기 · 접기 | ||
* ============================================================= | |||
* | |||
* DOM 예: | |||
* | |||
* [남성 버튼 wrapper] | |||
* [남성 실제 패널] | |||
* [여성 버튼 wrapper] | |||
* [여성 실제 패널] | |||
* | * | ||
* | * 패널을 다른 위치로 이동하지 않고 | ||
* | * 원래 DOM 위치에서 펼친다. | ||
* | * | ||
* | * 그래서 남성을 펼치면 여성 버튼이 자동으로 아래로 밀린다. | ||
* | * ============================================================= | ||
*/ | */ | ||
function initStackedCollapsible($content, cls, $targets) { | function initStackedCollapsible($content, cls, $targets) { | ||
/* | /* | ||
* wikitable이 들어 있는 | * wikitable이 들어 있는 요소를 실제 패널로 사용 | ||
*/ | */ | ||
var $panel = $targets.filter(function () { | var $panel = $targets.filter(function () { | ||
| 194번째 줄: | 242번째 줄: | ||
/* | /* | ||
* | * 실제 패널이 아닌 동일 ID 요소 중 | ||
* 해당 toggle을 포함하는 요소 = 상단 버튼 wrapper | |||
*/ | */ | ||
var $topWrapper = $targets.not($panel).filter(function () { | var $topWrapper = $targets.not($panel).filter(function () { | ||
return $(this).find('.' + cls).length > 0; | return $(this).find('.' + cls).length > 0; | ||
}).first(); | }).first(); | ||
if (!$topWrapper.length) { | |||
return; | |||
} | |||
var $topToggle = $topWrapper.find('.' + cls).first(); | var $topToggle = $topWrapper.find('.' + cls).first(); | ||
var $panelToggle = $panel.find('.' + cls).first(); | var $panelToggle = $panel.find('.' + cls).first(); | ||
if (!$topToggle.length || !$panelToggle.length) { | if (!$topToggle.length || !$panelToggle.length) { | ||
| 209번째 줄: | 264번째 줄: | ||
/* | /* | ||
* | * 최초 상태 | ||
*/ | */ | ||
$panel.hide(); | $panel | ||
.addClass('mw-collapsed') | |||
.hide(); | |||
$topToggle.attr('aria-expanded', 'false'); | |||
$panelToggle.attr('aria-expanded', 'false'); | |||
/* | /* | ||
* | * ========================================================= | ||
* 위쪽 버튼 클릭 → 펼치기 | |||
* ========================================================= | |||
*/ | */ | ||
$topToggle | $topToggle | ||
| 224번째 줄: | 287번째 줄: | ||
e.stopImmediatePropagation(); | e.stopImmediatePropagation(); | ||
if ($panel.is(':animated') || $panel.is(':visible')) { | |||
/* | |||
* 중복 클릭 방지 | |||
*/ | |||
if ( | |||
$panel.is(':animated') || | |||
$topWrapper.is(':animated') || | |||
$panel.is(':visible') | |||
) { | |||
return false; | return false; | ||
} | } | ||
| 230번째 줄: | 301번째 줄: | ||
/* | /* | ||
* | * MediaWiki collapsed 상태 해제 | ||
*/ | */ | ||
$ | $panel.removeClass('mw-collapsed'); | ||
/* | /* | ||
* | * 상단 버튼을 즉시 hide()하지 않고 | ||
* | * 짧게 slideUp하여 "툭" 끊기는 느낌 완화 | ||
*/ | |||
$topWrapper | |||
.stop(true, false) | |||
.slideUp(140, function () { | |||
$(this).css({ | |||
height: '', | |||
overflow: '', | |||
paddingTop: '', | |||
paddingBottom: '', | |||
marginTop: '', | |||
marginBottom: '' | |||
}); | |||
}); | |||
/* | |||
* 실제 패널은 원래 위치에서 부드럽게 펼침 | |||
*/ | */ | ||
$panel | $panel | ||
.stop(true, false) | .stop(true, false) | ||
.hide() | .hide() | ||
.slideDown( | .slideDown(260, function () { | ||
$(this).css({ | $(this).css({ | ||
height: '', | height: '', | ||
| 263번째 줄: | 349번째 줄: | ||
return false; | return false; | ||
}); | }); | ||
/* | /* | ||
* | * ========================================================= | ||
* 패널 안쪽 버튼 클릭 → 접기 | |||
* ========================================================= | |||
*/ | */ | ||
$panelToggle | $panelToggle | ||
| 275번째 줄: | 364번째 줄: | ||
e.stopImmediatePropagation(); | e.stopImmediatePropagation(); | ||
if ($panel.is(':animated')) { | |||
/* | |||
* 중복 클릭 방지 | |||
*/ | |||
if ( | |||
$panel.is(':animated') || | |||
$topWrapper.is(':animated') | |||
) { | |||
return false; | return false; | ||
} | } | ||
/* | |||
* 실제 패널 접기 | |||
*/ | |||
$panel | $panel | ||
.stop(true, false) | .stop(true, false) | ||
.slideUp( | .slideUp(260, function () { | ||
$(this) | $(this) | ||
| 295번째 줄: | 394번째 줄: | ||
marginBottom: '' | marginBottom: '' | ||
}); | }); | ||
}); | |||
/* | |||
* 위쪽 원래 버튼은 동시에 복귀 | |||
*/ | |||
$topWrapper | |||
$ | .stop(true, false) | ||
.slideDown(140, function () { | |||
$(this).css({ | |||
height: '', | |||
overflow: '', | |||
paddingTop: '', | |||
paddingBottom: '', | |||
marginTop: '', | |||
marginBottom: '' | |||
}); | |||
}); | }); | ||
2026년 8월 20일 (목) 15:03 판
/* MediaWiki:Common.js */
mw.hook('wikipage.content').add(function ($content) {
var initialized = {};
$content.find('[class*="mw-customtoggle-"]').each(function () {
var classList = (this.className || '').split(/\s+/);
classList.forEach(function (cls) {
if (cls.indexOf('mw-customtoggle-') !== 0) {
return;
}
var suffix = cls.substring('mw-customtoggle-'.length);
if (initialized[suffix]) {
return;
}
initialized[suffix] = true;
var targetId = 'mw-customcollapsible-' + suffix;
/*
* 중복 ID가 있을 수 있으므로 #id 대신 attribute selector 사용
*/
var $targets = $content.find('[id="' + targetId + '"]');
if (!$targets.length) {
return;
}
/*
* =====================================================
* 특수형
*
* 아나운서 표처럼:
*
* 상단 버튼 wrapper
* 실제 내용 패널
*
* 이 같은 ID를 공유하고,
* 실제 패널 안에 wikitable이 있는 구조
* =====================================================
*/
var $specialPanel = $targets.filter(function () {
return $(this).find('table.wikitable').length > 0;
});
if ($targets.length > 1 && $specialPanel.length) {
initStackedCollapsible(
$content,
cls,
$targets
);
return;
}
/*
* =====================================================
* 일반 MediaWiki collapsible
* =====================================================
*/
initNormalCollapsible(
$content,
cls,
$targets.first()
);
});
});
/*
* =============================================================
* 일반 펼치기 · 접기
* =============================================================
*/
function initNormalCollapsible($content, cls, $target) {
var $toggles = $content.find('.' + cls);
/*
* 일반 MediaWiki 구조:
*
* .mw-collapsible
* └ .mw-collapsible-content
*
* content가 있으면 content만 애니메이션
*/
var $contentTarget = $target.children('.mw-collapsible-content');
if (!$contentTarget.length) {
$contentTarget = $target.find('.mw-collapsible-content').first();
}
var $animTarget = $contentTarget.length
? $contentTarget
: $target;
/*
* 현재 상태를 기준으로 초기 aria 설정
*/
var initiallyOpen = $animTarget.is(':visible');
$toggles.attr(
'aria-expanded',
initiallyOpen ? 'true' : 'false'
);
$toggles
.off('click.smoothSlide')
.on('click.smoothSlide', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
/*
* 애니메이션 중 다시 눌렀을 때
* queue가 꼬이는 것을 방지
*/
if ($animTarget.is(':animated')) {
return false;
}
var opening = !$animTarget.is(':visible');
/*
* -------------------------------------------------
* 펼치기
* -------------------------------------------------
*/
if (opening) {
/*
* MediaWiki의 collapsed 상태를 먼저 제거
*/
$target.removeClass('mw-collapsed');
/*
* slideDown
*/
$animTarget
.stop(true, false)
.hide()
.slideDown(250, function () {
/*
* jQuery가 남기는 애니메이션용 inline style 정리
*/
$(this).css({
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
$toggles.attr('aria-expanded', 'true');
}
/*
* -------------------------------------------------
* 접기
* -------------------------------------------------
*/
else {
$animTarget
.stop(true, false)
.slideUp(250, function () {
/*
* 애니메이션이 끝난 뒤 collapsed 복원
*/
$target.addClass('mw-collapsed');
$(this).css({
display: 'none',
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
$toggles.attr('aria-expanded', 'false');
}
return false;
});
}
/*
* =============================================================
* 아나운서형 특수 펼치기 · 접기
* =============================================================
*
* DOM 예:
*
* [남성 버튼 wrapper]
* [남성 실제 패널]
* [여성 버튼 wrapper]
* [여성 실제 패널]
*
* 패널을 다른 위치로 이동하지 않고
* 원래 DOM 위치에서 펼친다.
*
* 그래서 남성을 펼치면 여성 버튼이 자동으로 아래로 밀린다.
* =============================================================
*/
function initStackedCollapsible($content, cls, $targets) {
/*
* wikitable이 들어 있는 요소를 실제 패널로 사용
*/
var $panel = $targets.filter(function () {
return $(this).find('table.wikitable').length > 0;
}).first();
if (!$panel.length) {
return;
}
/*
* 실제 패널이 아닌 동일 ID 요소 중
* 해당 toggle을 포함하는 요소 = 상단 버튼 wrapper
*/
var $topWrapper = $targets.not($panel).filter(function () {
return $(this).find('.' + cls).length > 0;
}).first();
if (!$topWrapper.length) {
return;
}
var $topToggle = $topWrapper.find('.' + cls).first();
var $panelToggle = $panel.find('.' + cls).first();
if (!$topToggle.length || !$panelToggle.length) {
return;
}
/*
* 최초 상태
*/
$panel
.addClass('mw-collapsed')
.hide();
$topToggle.attr('aria-expanded', 'false');
$panelToggle.attr('aria-expanded', 'false');
/*
* =========================================================
* 위쪽 버튼 클릭 → 펼치기
* =========================================================
*/
$topToggle
.off('click.smoothSlide')
.on('click.smoothSlide', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
/*
* 중복 클릭 방지
*/
if (
$panel.is(':animated') ||
$topWrapper.is(':animated') ||
$panel.is(':visible')
) {
return false;
}
/*
* MediaWiki collapsed 상태 해제
*/
$panel.removeClass('mw-collapsed');
/*
* 상단 버튼을 즉시 hide()하지 않고
* 짧게 slideUp하여 "툭" 끊기는 느낌 완화
*/
$topWrapper
.stop(true, false)
.slideUp(140, function () {
$(this).css({
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
/*
* 실제 패널은 원래 위치에서 부드럽게 펼침
*/
$panel
.stop(true, false)
.hide()
.slideDown(260, function () {
$(this).css({
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
$topToggle.attr('aria-expanded', 'true');
$panelToggle.attr('aria-expanded', 'true');
return false;
});
/*
* =========================================================
* 패널 안쪽 버튼 클릭 → 접기
* =========================================================
*/
$panelToggle
.off('click.smoothSlide')
.on('click.smoothSlide', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
/*
* 중복 클릭 방지
*/
if (
$panel.is(':animated') ||
$topWrapper.is(':animated')
) {
return false;
}
/*
* 실제 패널 접기
*/
$panel
.stop(true, false)
.slideUp(260, function () {
$(this)
.addClass('mw-collapsed')
.css({
display: 'none',
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
/*
* 위쪽 원래 버튼은 동시에 복귀
*/
$topWrapper
.stop(true, false)
.slideDown(140, function () {
$(this).css({
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
$topToggle.attr('aria-expanded', 'false');
$panelToggle.attr('aria-expanded', 'false');
return false;
});
}
});