편집 요약 없음 |
편집 요약 없음 태그: 되돌려진 기여 |
||
| 23번째 줄: | 23번째 줄: | ||
/* | /* | ||
* 중복 ID가 있을 수 있으므로 #id 대신 attribute selector 사용 | * 중복 ID가 있을 수 있으므로 | ||
* #id 대신 attribute selector 사용 | |||
*/ | */ | ||
var $targets = $content.find('[id="' + targetId + '"]'); | var $targets = $content.find('[id="' + targetId + '"]'); | ||
| 30번째 줄: | 31번째 줄: | ||
return; | return; | ||
} | } | ||
/* | /* | ||
* ===================================================== | * ===================================================== | ||
* | * 아나운서형 특수 구조 판별 | ||
* | * ===================================================== | ||
* | * | ||
* | * 동일 ID가 2개 이상이고 | ||
* 실제 | * 실제 패널 쪽에 wikitable이 존재하는 경우 | ||
*/ | */ | ||
var $specialPanel = $targets.filter(function () { | var $specialPanel = $targets.filter(function () { | ||
| 58번째 줄: | 53번째 줄: | ||
return; | return; | ||
} | } | ||
/* | /* | ||
| 72번째 줄: | 66번째 줄: | ||
}); | }); | ||
}); | }); | ||
| 85번째 줄: | 78번째 줄: | ||
/* | /* | ||
* 일반 | * 일반 구조: | ||
* | * | ||
* .mw-collapsible | * .mw-collapsible | ||
* └ .mw-collapsible-content | * └ .mw-collapsible-content | ||
*/ | */ | ||
var $contentTarget = $target.children('.mw-collapsible-content'); | var $contentTarget = $target.children('.mw-collapsible-content'); | ||
| 104번째 줄: | 95번째 줄: | ||
/* | /* | ||
* | * 초기 접근성 상태 | ||
*/ | */ | ||
var initiallyOpen = $animTarget.is(':visible'); | var initiallyOpen = $animTarget.is(':visible'); | ||
| 122번째 줄: | 113번째 줄: | ||
/* | /* | ||
* 애니메이션 중 | * 애니메이션 중 연타 방지 | ||
*/ | */ | ||
if ($animTarget.is(':animated')) { | if ($animTarget.is(':animated')) { | ||
| 140번째 줄: | 130번째 줄: | ||
/* | /* | ||
* | * MediaWiki collapsed 상태 먼저 제거 | ||
*/ | */ | ||
$target.removeClass('mw-collapsed'); | $target.removeClass('mw-collapsed'); | ||
$animTarget | $animTarget | ||
.stop(true, false) | .stop(true, false) | ||
.hide() | .hide() | ||
.slideDown( | .slideDown(220, function () { | ||
/* | /* | ||
* | * jQuery 애니메이션 잔여 스타일 정리 | ||
*/ | */ | ||
$(this).css({ | $(this).css({ | ||
| 180번째 줄: | 167번째 줄: | ||
$animTarget | $animTarget | ||
.stop(true, false) | .stop(true, false) | ||
.slideUp( | .slideUp(220, function () { | ||
/* | /* | ||
* | * 애니메이션 완료 후 collapsed 복원 | ||
*/ | */ | ||
$target.addClass('mw-collapsed'); | $target.addClass('mw-collapsed'); | ||
| 214번째 줄: | 201번째 줄: | ||
* ============================================================= | * ============================================================= | ||
* | * | ||
* DOM | * DOM 구조: | ||
* | * | ||
* [남성 버튼 wrapper] | * [남성 상단 버튼 wrapper] | ||
* [남성 실제 패널] | * [남성 실제 패널] | ||
* [여성 버튼 wrapper] | * [여성 상단 버튼 wrapper] | ||
* [여성 실제 패널] | * [여성 실제 패널] | ||
* | * | ||
* | * 패널 위치는 바꾸지 않는다. | ||
* | * | ||
* | * 버튼 + 패널을 하나의 그룹으로 묶고 | ||
* 그룹 height 하나만 animate한다. | |||
* ============================================================= | * ============================================================= | ||
*/ | */ | ||
| 230번째 줄: | 217번째 줄: | ||
/* | /* | ||
* wikitable이 들어 있는 | * wikitable이 들어 있는 요소 = 실제 패널 | ||
*/ | */ | ||
var $panel = $targets.filter(function () { | var $panel = $targets.filter(function () { | ||
| 256번째 줄: | 243번째 줄: | ||
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) { | ||
| 264번째 줄: | 250번째 줄: | ||
/* | /* | ||
* ===================================================== | |||
* 버튼 + 패널 그룹 생성 | |||
* ===================================================== | |||
*/ | |||
var $group; | |||
if ($topWrapper.parent().hasClass('smooth-collapsible-group')) { | |||
$group = $topWrapper.parent(); | |||
} else { | |||
$group = $('<div class="smooth-collapsible-group"></div>'); | |||
$group.css({ | |||
width: '100%', | |||
overflow: 'hidden' | |||
}); | |||
/* | |||
* 기존 위치에 그룹 삽입 | |||
*/ | |||
$topWrapper.before($group); | |||
/* | |||
* DOM 순서 유지 | |||
*/ | |||
$group.append($topWrapper); | |||
$group.append($panel); | |||
} | |||
/* | |||
* ===================================================== | |||
* 최초 상태 | * 최초 상태 | ||
* ===================================================== | |||
*/ | */ | ||
$panel | $panel | ||
.addClass('mw-collapsed') | .addClass('mw-collapsed') | ||
.hide(); | .hide(); | ||
$topWrapper.show(); | |||
$topToggle.attr('aria-expanded', 'false'); | $topToggle.attr('aria-expanded', 'false'); | ||
$panelToggle.attr('aria-expanded', 'false'); | $panelToggle.attr('aria-expanded', 'false'); | ||
/* | /* | ||
* | * ===================================================== | ||
* | * 상단 버튼 클릭 → 펼치기 | ||
* | * ===================================================== | ||
*/ | */ | ||
$topToggle | $topToggle | ||
| 289번째 줄: | 311번째 줄: | ||
/* | /* | ||
* | * 애니메이션 중 연타 방지 | ||
*/ | */ | ||
if ( | if ($group.is(':animated')) { | ||
return false; | |||
} | |||
if ($panel.is(':visible')) { | |||
return false; | return false; | ||
} | } | ||
/* | |||
* 현재 접힌 상태 높이 | |||
*/ | |||
var startHeight = $group.outerHeight(); | |||
| 307번째 줄: | 335번째 줄: | ||
/* | /* | ||
* 상단 | * ================================================= | ||
* | * 최종 패널 높이 측정 | ||
* ================================================= | |||
* | |||
* position:absolute 상태로 측정하여 | |||
* 뒤쪽 여성 버튼 등이 먼저 움직이지 않게 함 | |||
*/ | |||
$panel.css({ | |||
display: 'block', | |||
visibility: 'hidden', | |||
position: 'absolute', | |||
left: 0, | |||
top: 0, | |||
width: '100%', | |||
height: 'auto' | |||
}); | |||
var endHeight = $panel.outerHeight(); | |||
/* | |||
* 측정 후 다시 원상복구 | |||
*/ | |||
$panel.css({ | |||
display: 'none', | |||
visibility: '', | |||
position: '', | |||
left: '', | |||
top: '', | |||
width: '', | |||
height: '' | |||
}); | |||
/* | |||
* 현재 그룹 높이 고정 | |||
*/ | |||
$group.css({ | |||
height: startHeight, | |||
overflow: 'hidden' | |||
}); | |||
/* | |||
* ================================================= | |||
* 중요 | |||
* | |||
* 패널 안쪽 [남성 아나운서] / [여성 아나운서] | |||
* 글자를 애니메이션 동안 숨김 | |||
* | |||
* 상단 제목과 겹쳐 보이는 현상 방지 | |||
* ================================================= | |||
*/ | |||
$panelToggle.css('visibility', 'hidden'); | |||
/* | |||
* 상단 버튼 제거 | |||
*/ | */ | ||
$topWrapper | $topWrapper.hide(); | ||
/* | |||
* 실제 패널 표시 | |||
*/ | |||
$panel.css({ | |||
display: 'block' | |||
}); | |||
/* | /* | ||
* | * ================================================= | ||
* 그룹 높이 하나만 애니메이션 | |||
* ================================================= | |||
*/ | */ | ||
$ | $group | ||
.stop(true, false) | .stop(true, false) | ||
. | .animate( | ||
{ | |||
height: endHeight | |||
}, | |||
{ | |||
duration: 240, | |||
easing: 'swing', | |||
complete: function () { | |||
/* | |||
* 그룹 자동 높이 복원 | |||
*/ | |||
$group.css({ | |||
height: '', | |||
overflow: '' | |||
}); | |||
/* | |||
* 패널 잔여 스타일 정리 | |||
*/ | |||
$panel.css({ | |||
display: '', | |||
height: '', | |||
overflow: '' | |||
}); | |||
/* | |||
* 애니메이션 완료 후 | |||
* 패널 안쪽 제목 표시 | |||
*/ | |||
$panelToggle.css( | |||
'visibility', | |||
} | '' | ||
); | |||
} | |||
} | |||
); | |||
| 353번째 줄: | 463번째 줄: | ||
/* | /* | ||
* | * ===================================================== | ||
* 패널 안쪽 버튼 | * 펼쳐진 패널 안쪽 버튼 → 접기 | ||
* | * ===================================================== | ||
*/ | */ | ||
$panelToggle | $panelToggle | ||
| 366번째 줄: | 476번째 줄: | ||
/* | /* | ||
* | * 애니메이션 중 연타 방지 | ||
*/ | */ | ||
if ( | if ($group.is(':animated')) { | ||
return false; | |||
$ | } | ||
if (!$panel.is(':visible')) { | |||
return false; | return false; | ||
} | } | ||
| 377번째 줄: | 488번째 줄: | ||
/* | /* | ||
* | * ================================================= | ||
* 안쪽 제목부터 숨김 | |||
* | |||
* 상단 제목과 겹쳐지는 현상 방지 | |||
* ================================================= | |||
*/ | */ | ||
$ | $panelToggle.css( | ||
. | 'visibility', | ||
. | 'hidden' | ||
); | |||
/* | |||
* 현재 펼쳐진 높이 | |||
*/ | |||
var startHeight = $group.outerHeight(); | |||
/* | |||
* ================================================= | |||
* 상단 버튼의 최종 높이 측정 | |||
* ================================================= | |||
*/ | |||
$topWrapper.css({ | |||
display: 'block', | |||
visibility: 'hidden', | |||
position: 'absolute', | |||
left: 0, | |||
top: 0 | |||
}); | |||
var endHeight = $topWrapper.outerHeight(); | |||
/* | |||
* 측정 후 다시 숨김 | |||
*/ | |||
$topWrapper.css({ | |||
display: 'none', | |||
visibility: '', | |||
position: '', | |||
left: '', | |||
top: '' | |||
}); | |||
/* | |||
* 현재 그룹 높이 고정 | |||
*/ | |||
$group.css({ | |||
height: startHeight, | |||
overflow: 'hidden' | |||
}); | |||
/* | |||
* 패널 → 상단 버튼으로 교체 | |||
*/ | |||
$panel.hide(); | |||
$topWrapper.show(); | |||
/* | /* | ||
* | * ================================================= | ||
* 그룹 높이만 축소 | |||
* ================================================= | |||
*/ | */ | ||
$ | $group | ||
.stop(true, false) | .stop(true, false) | ||
. | .animate( | ||
{ | |||
height: endHeight | |||
}, | |||
{ | |||
duration: 240, | |||
easing: 'swing', | |||
complete: function () { | |||
/* | |||
* 패널을 완전히 접힌 상태로 복원 | |||
*/ | |||
$panel | |||
.addClass('mw-collapsed') | |||
.css({ | |||
display: 'none', | |||
height: '', | |||
overflow: '' | |||
}); | |||
/* | |||
* 상단 wrapper 정리 | |||
*/ | |||
$topWrapper.css({ | |||
display: '', | |||
height: '', | |||
overflow: '' | |||
}); | |||
/* | |||
* 그룹 정리 | |||
*/ | |||
$group.css({ | |||
height: '', | |||
overflow: '' | |||
}); | |||
/* | |||
* 다음에 다시 펼칠 때를 위해 | |||
* 패널 안쪽 제목 visibility 복구 | |||
*/ | |||
$panelToggle.css( | |||
'visibility', | |||
'' | |||
); | |||
} | |||
} | |||
); | |||
2026년 8월 20일 (목) 15:06 판
/* 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;
}
/*
* =====================================================
* 아나운서형 특수 구조 판별
* =====================================================
*
* 동일 ID가 2개 이상이고
* 실제 패널 쪽에 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);
/*
* 일반 구조:
*
* .mw-collapsible
* └ .mw-collapsible-content
*/
var $contentTarget = $target.children('.mw-collapsible-content');
if (!$contentTarget.length) {
$contentTarget = $target.find('.mw-collapsible-content').first();
}
var $animTarget = $contentTarget.length
? $contentTarget
: $target;
/*
* 초기 접근성 상태
*/
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();
/*
* 애니메이션 중 연타 방지
*/
if ($animTarget.is(':animated')) {
return false;
}
var opening = !$animTarget.is(':visible');
/*
* -------------------------------------------------
* 펼치기
* -------------------------------------------------
*/
if (opening) {
/*
* MediaWiki collapsed 상태 먼저 제거
*/
$target.removeClass('mw-collapsed');
$animTarget
.stop(true, false)
.hide()
.slideDown(220, function () {
/*
* jQuery 애니메이션 잔여 스타일 정리
*/
$(this).css({
height: '',
overflow: '',
paddingTop: '',
paddingBottom: '',
marginTop: '',
marginBottom: ''
});
});
$toggles.attr('aria-expanded', 'true');
}
/*
* -------------------------------------------------
* 접기
* -------------------------------------------------
*/
else {
$animTarget
.stop(true, false)
.slideUp(220, 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]
* [여성 실제 패널]
*
* 패널 위치는 바꾸지 않는다.
*
* 버튼 + 패널을 하나의 그룹으로 묶고
* 그룹 height 하나만 animate한다.
* =============================================================
*/
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;
}
/*
* =====================================================
* 버튼 + 패널 그룹 생성
* =====================================================
*/
var $group;
if ($topWrapper.parent().hasClass('smooth-collapsible-group')) {
$group = $topWrapper.parent();
} else {
$group = $('<div class="smooth-collapsible-group"></div>');
$group.css({
width: '100%',
overflow: 'hidden'
});
/*
* 기존 위치에 그룹 삽입
*/
$topWrapper.before($group);
/*
* DOM 순서 유지
*/
$group.append($topWrapper);
$group.append($panel);
}
/*
* =====================================================
* 최초 상태
* =====================================================
*/
$panel
.addClass('mw-collapsed')
.hide();
$topWrapper.show();
$topToggle.attr('aria-expanded', 'false');
$panelToggle.attr('aria-expanded', 'false');
/*
* =====================================================
* 상단 버튼 클릭 → 펼치기
* =====================================================
*/
$topToggle
.off('click.smoothSlide')
.on('click.smoothSlide', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
/*
* 애니메이션 중 연타 방지
*/
if ($group.is(':animated')) {
return false;
}
if ($panel.is(':visible')) {
return false;
}
/*
* 현재 접힌 상태 높이
*/
var startHeight = $group.outerHeight();
/*
* MediaWiki collapsed 상태 해제
*/
$panel.removeClass('mw-collapsed');
/*
* =================================================
* 최종 패널 높이 측정
* =================================================
*
* position:absolute 상태로 측정하여
* 뒤쪽 여성 버튼 등이 먼저 움직이지 않게 함
*/
$panel.css({
display: 'block',
visibility: 'hidden',
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: 'auto'
});
var endHeight = $panel.outerHeight();
/*
* 측정 후 다시 원상복구
*/
$panel.css({
display: 'none',
visibility: '',
position: '',
left: '',
top: '',
width: '',
height: ''
});
/*
* 현재 그룹 높이 고정
*/
$group.css({
height: startHeight,
overflow: 'hidden'
});
/*
* =================================================
* 중요
*
* 패널 안쪽 [남성 아나운서] / [여성 아나운서]
* 글자를 애니메이션 동안 숨김
*
* 상단 제목과 겹쳐 보이는 현상 방지
* =================================================
*/
$panelToggle.css('visibility', 'hidden');
/*
* 상단 버튼 제거
*/
$topWrapper.hide();
/*
* 실제 패널 표시
*/
$panel.css({
display: 'block'
});
/*
* =================================================
* 그룹 높이 하나만 애니메이션
* =================================================
*/
$group
.stop(true, false)
.animate(
{
height: endHeight
},
{
duration: 240,
easing: 'swing',
complete: function () {
/*
* 그룹 자동 높이 복원
*/
$group.css({
height: '',
overflow: ''
});
/*
* 패널 잔여 스타일 정리
*/
$panel.css({
display: '',
height: '',
overflow: ''
});
/*
* 애니메이션 완료 후
* 패널 안쪽 제목 표시
*/
$panelToggle.css(
'visibility',
''
);
}
}
);
$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 ($group.is(':animated')) {
return false;
}
if (!$panel.is(':visible')) {
return false;
}
/*
* =================================================
* 안쪽 제목부터 숨김
*
* 상단 제목과 겹쳐지는 현상 방지
* =================================================
*/
$panelToggle.css(
'visibility',
'hidden'
);
/*
* 현재 펼쳐진 높이
*/
var startHeight = $group.outerHeight();
/*
* =================================================
* 상단 버튼의 최종 높이 측정
* =================================================
*/
$topWrapper.css({
display: 'block',
visibility: 'hidden',
position: 'absolute',
left: 0,
top: 0
});
var endHeight = $topWrapper.outerHeight();
/*
* 측정 후 다시 숨김
*/
$topWrapper.css({
display: 'none',
visibility: '',
position: '',
left: '',
top: ''
});
/*
* 현재 그룹 높이 고정
*/
$group.css({
height: startHeight,
overflow: 'hidden'
});
/*
* 패널 → 상단 버튼으로 교체
*/
$panel.hide();
$topWrapper.show();
/*
* =================================================
* 그룹 높이만 축소
* =================================================
*/
$group
.stop(true, false)
.animate(
{
height: endHeight
},
{
duration: 240,
easing: 'swing',
complete: function () {
/*
* 패널을 완전히 접힌 상태로 복원
*/
$panel
.addClass('mw-collapsed')
.css({
display: 'none',
height: '',
overflow: ''
});
/*
* 상단 wrapper 정리
*/
$topWrapper.css({
display: '',
height: '',
overflow: ''
});
/*
* 그룹 정리
*/
$group.css({
height: '',
overflow: ''
});
/*
* 다음에 다시 펼칠 때를 위해
* 패널 안쪽 제목 visibility 복구
*/
$panelToggle.css(
'visibility',
''
);
}
}
);
$topToggle.attr('aria-expanded', 'false');
$panelToggle.attr('aria-expanded', 'false');
return false;
});
}
});