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

편집 요약 없음
편집 요약 없음
 
(같은 사용자의 중간 판 33개는 보이지 않습니다)
1번째 줄: 1번째 줄:
/**
/* MediaWiki:Common.js */
* =========================================================
mw.hook('wikipage.content').add(function ($content) {
* Animated Collapse / Expand
* MediaWiki:Common.js
* =========================================================
*
* 사용 예:
*
* <div
*    class="animated-toggle"
*    data-collapse-target="example-1"
*    role="button"
*    tabindex="0"
* >
*     <b>[ 펼치기 · 접기 ]</b>
* </div>
*
* <div
*    id="example-1"
*    class="animated-content"
* >
*    <div class="animated-content-inner">
*        내용
*    </div>
* </div>
*
*/


    var initialized = {};


(function ($, mw) {
    $content.find('[class*="mw-customtoggle-"]').each(function () {
    'use strict';
        var classList = (this.className || '').split(/\s+/);


        classList.forEach(function (cls) {
            if (cls.indexOf('mw-customtoggle-') !== 0) {
                return;
            }


    /**
            var suffix = cls.substring('mw-customtoggle-'.length);
    * -----------------------------------------------------
    * 펼치기 초기화
    * -----------------------------------------------------
    */


    function initAnimatedCollapse($content) {
            if (initialized[suffix]) {
 
                return;
        $content.find('.animated-toggle').each(function () {
            }


             var $toggle = $(this);
             initialized[suffix] = true;


            var targetId = 'mw-customcollapsible-' + suffix;


             /*
             /*
             * 이미 초기화되었다면 중복 실행하지 않음
             * 중복 ID가 있을 수 있으므로
            * #id 대신 attribute selector 사용
             */
             */
             if ($toggle.data('animatedCollapseInitialized')) {
             var $targets = $content.find('[id="' + targetId + '"]');
 
            if (!$targets.length) {
                 return;
                 return;
             }
             }
54번째 줄: 34번째 줄:


             /*
             /*
             * 대상 ID
             * =====================================================
            * 특수형
            *
            * 아나운서 표처럼:
            *
            * [상단 버튼 wrapper]
            * [실제 내용 패널]
            *
            * 두 요소가 같은 ID를 공유하고,
            * 실제 패널 안에 wikitable이 있는 구조
            * =====================================================
             */
             */
             var targetId = $toggle.attr('data-collapse-target');
             var $specialPanel = $targets.filter(function () {
                return $(this).find('table.wikitable').length > 0;
            });


            if ($targets.length > 1 && $specialPanel.length) {
                initStackedCollapsible(
                    $content,
                    cls,
                    $targets
                );


            /*
            * target이 없는 경우 무시
            */
            if (!targetId) {
                 return;
                 return;
             }
             }
68번째 줄: 62번째 줄:


             /*
             /*
             * 특수문자가 ID에 들어갈 가능성을 고려해서
             * =====================================================
             * document.getElementById 사용
             * 일반 MediaWiki collapsible
            * =====================================================
             */
             */
             var targetElement = document.getElementById(targetId);
             initNormalCollapsible(
                $content,
                cls,
                $targets.first()
            );
        });
    });




            if (!targetElement) {
                return;
            }


    /*
    * =============================================================
    * 일반 펼치기 · 접기
    * =============================================================
    */
    function initNormalCollapsible($content, cls, $target) {


            var $target = $(targetElement);
        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();
            */
        }
             $toggle.data(
                'animatedCollapseInitialized',
                true
            );


        var $animTarget = $contentTarget.length
            ? $contentTarget
            : $target;


            /**
            * ---------------------------------------------
            * 접근성 속성 초기화
            * ---------------------------------------------
            */


            if (!$toggle.attr('role')) {
        /*
                $toggle.attr('role', 'button');
        * 현재 상태를 기준으로 초기 aria 설정
            }
        */
        var initiallyOpen = $animTarget.is(':visible');


        $toggles.attr(
            'aria-expanded',
            initiallyOpen ? 'true' : 'false'
        );


            if (!$toggle.attr('tabindex')) {
                $toggle.attr('tabindex', '0');
            }


        $toggles
            .off('click.smoothSlide')
            .on('click.smoothSlide', function (e) {


            /*
                e.preventDefault();
            * aria-controls 연결
                 e.stopImmediatePropagation();
            */
            $toggle.attr(
                 'aria-controls',
                targetId
            );




            /*
                /*
            * 초기 열린 상태 확인
                * 애니메이션 중 재클릭 방지
            *
                */
            * is-open 클래스가 있으면 펼쳐진 상태로 시작
                 if ($animTarget.is(':animated')) {
            */
                    return false;
            var isInitiallyOpen =
                }
                 $target.hasClass('is-open');




            $toggle.attr(
                var opening = !$animTarget.is(':visible');
                'aria-expanded',
                isInitiallyOpen ? 'true' : 'false'
            );




            $target.attr(
                /*
                'aria-hidden',
                * -------------------------------------------------
                 isInitiallyOpen ? 'false' : 'true'
                * 펼치기
            );
                * -------------------------------------------------
                */
                 if (opening) {


                    /*
                    * MediaWiki collapsed 상태 제거
                    */
                    $target.removeClass('mw-collapsed');


            /**
            * ---------------------------------------------
            * 열기
            * ---------------------------------------------
            */


            function openCollapse() {
                    /*
                    * 내용만 펼침
                    */
                    $animTarget
                        .stop(true, true)
                        .hide()
                        .slideDown(250, function () {


                $target.addClass('is-open');
                            /*
                            * jQuery 애니메이션 inline style 정리
                            */
                            $(this).css({
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });
                        });


                $toggle.attr(
                    'aria-expanded',
                    'true'
                );


                $target.attr(
                    $toggles.attr('aria-expanded', 'true');
                    'aria-hidden',
                 }
                    'false'
                 );




                 /*
                 /*
                 * 필요하면 사용자 정의 이벤트도 발생
                 * -------------------------------------------------
                * 접기
                * -------------------------------------------------
                 */
                 */
                 $target.trigger(
                 else {
                     'animatedcollapse:open'
 
                );
                    $animTarget
             }
                        .stop(true, true)
                        .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 위치에서 펼친다.
    *
    * 중요한 점:
    *
    * wrapper와 panel을 둘 다 애니메이션하지 않는다.
    *
    * 버튼 wrapper는 즉시 교체하고,
    * 실제 패널 하나만 slide 애니메이션한다.
    *
    * 따라서 이중 움직임이나
    * 중간 겹침 현상이 최소화된다.
    * =============================================================
    */
    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;
        }


            /**
            * ---------------------------------------------
            * 닫기
            * ---------------------------------------------
            */


            function closeCollapse() {


                $target.removeClass('is-open');
        /*
        * =========================================================
        * 최초 상태
        * =========================================================
        */
        $panel
            .stop(true, true)
            .addClass('mw-collapsed')
            .hide()
            .css({
                height: '',
                overflow: '',
                paddingTop: '',
                paddingBottom: '',
                marginTop: '',
                marginBottom: ''
            });


                $toggle.attr(
                    'aria-expanded',
                    'false'
                );


                $target.attr(
        $topWrapper
                    'aria-hidden',
            .stop(true, true)
                    'true'
            .show()
                 );
            .css({
                height: '',
                overflow: '',
                paddingTop: '',
                paddingBottom: '',
                marginTop: '',
                 marginBottom: ''
            });




                $target.trigger(
        $topToggle.attr('aria-expanded', 'false');
                    'animatedcollapse:close'
        $panelToggle.attr('aria-expanded', 'false');
                );
            }




            /**
            * ---------------------------------------------
            * 토글
            * ---------------------------------------------
            */


             function toggleCollapse() {
        /*
        * =========================================================
        * 위쪽 버튼 클릭 → 펼치기
        * =========================================================
        */
        $topToggle
             .off('click.smoothSlide')
            .on('click.smoothSlide', function (e) {


                 if ($target.hasClass('is-open')) {
                 e.preventDefault();
                e.stopImmediatePropagation();


                    closeCollapse();


                 } else {
                /*
                * 애니메이션 중 재클릭 방지
                */
                if ($panel.is(':animated')) {
                    return false;
                 }


                    openCollapse();


                /*
                * 이미 열려 있으면 중단
                */
                if ($panel.is(':visible')) {
                    return false;
                 }
                 }
            }




            /**
                /*
            * ---------------------------------------------
                * -------------------------------------------------
            * 클릭
                * 상단 버튼 wrapper는 즉시 숨김
            * ---------------------------------------------
                *
            */
                * wrapper 자체를 애니메이션하지 않으므로
                * 이중 움직임이 발생하지 않는다.
                * -------------------------------------------------
                */
                $topWrapper
                    .stop(true, true)
                    .hide();


            $toggle.on(
                'click.animatedCollapse',
                function (event) {


                    event.preventDefault();
                /*
                * MediaWiki collapsed 상태 제거
                */
                $panel.removeClass('mw-collapsed');


                    toggleCollapse();
                }
            );


                /*
                * -------------------------------------------------
                * 실제 패널 하나만 부드럽게 펼침
                * -------------------------------------------------
                */
                $panel
                    .stop(true, true)
                    .hide()
                    .slideDown(260, function () {


            /**
                        /*
            * ---------------------------------------------
                        * animation 종료 후
            * 키보드
                        * jQuery가 만든 inline style 정리
            *
                        */
            * Enter
                        $(this).css({
            * Space
                            height: '',
            * ---------------------------------------------
                            overflow: '',
            */
                            paddingTop: '',
                            paddingBottom: '',
                            marginTop: '',
                            marginBottom: ''
                        });
                    });


            $toggle.on(
                'keydown.animatedCollapse',
                function (event) {


                    if (
                /*
                        event.key === 'Enter' ||
                * ARIA
                        event.key === ' '
                */
                    ) {
                $topToggle.attr('aria-expanded', 'true');
                $panelToggle.attr('aria-expanded', 'true');


                        event.preventDefault();


                        toggleCollapse();
                return false;
                    }
            });
                }
            );


        });


    }


        /*
        * =========================================================
        * 패널 안쪽 버튼 클릭 → 접기
        * =========================================================
        */
        $panelToggle
            .off('click.smoothSlide')
            .on('click.smoothSlide', function (e) {


    /**
                e.preventDefault();
    * -----------------------------------------------------
                e.stopImmediatePropagation();
    * MediaWiki 페이지 최초 로드
    * -----------------------------------------------------
    */


    $(function () {


        initAnimatedCollapse(
                /*
            $(document)
                * 애니메이션 중 재클릭 방지
        );
                */
                if ($panel.is(':animated')) {
                    return false;
                }
 


    });
                /*
                * -------------------------------------------------
                * 실제 패널 하나만 접음
                * -------------------------------------------------
                */
                $panel
                    .stop(true, true)
                    .slideUp(260, function () {


                        /*
                        * 패널을 완전히 닫은 뒤
                        * collapsed 상태 복원
                        */
                        $(this)
                            .addClass('mw-collapsed')
                            .css({
                                display: 'none',
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });


    /**
    * -----------------------------------------------------
    * MediaWiki 동적 콘텐츠 대응
    *
    * VisualEditor,
    * AJAX,
    * 확장기능,
    * Lua 등에서 콘텐츠가 나중에 생성돼도
    * 자동으로 초기화
    * -----------------------------------------------------
    */


    if (
                        /*
        typeof mw !== 'undefined' &&
                        * -------------------------------------------------
        mw.hook
                        * 원래 상단 버튼 즉시 복귀
    ) {
                        *
                        * 별도 애니메이션을 하지 않기 때문에
                        * 닫힘 동작도 한 번의 움직임으로 보인다.
                        * -------------------------------------------------
                        */
                        $topWrapper
                            .stop(true, true)
                            .show()
                            .css({
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });
                    });


        mw.hook('wikipage.content').add(
            function ($content) {


                 initAnimatedCollapse(
                 /*
                    $content
                * ARIA
                 );
                */
                $topToggle.attr('aria-expanded', 'false');
                 $panelToggle.attr('aria-expanded', 'false');


            }
        );


                return false;
            });
     }
     }


 
});
})(jQuery, mediaWiki);

2026년 8월 20일 (목) 15:13 기준 최신판

/* 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();


                /*
                 * 애니메이션 중 재클릭 방지
                 */
                if ($animTarget.is(':animated')) {
                    return false;
                }


                var opening = !$animTarget.is(':visible');


                /*
                 * -------------------------------------------------
                 * 펼치기
                 * -------------------------------------------------
                 */
                if (opening) {

                    /*
                     * MediaWiki collapsed 상태 제거
                     */
                    $target.removeClass('mw-collapsed');


                    /*
                     * 내용만 펼침
                     */
                    $animTarget
                        .stop(true, true)
                        .hide()
                        .slideDown(250, function () {

                            /*
                             * jQuery 애니메이션 inline style 정리
                             */
                            $(this).css({
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });
                        });


                    $toggles.attr('aria-expanded', 'true');
                }


                /*
                 * -------------------------------------------------
                 * 접기
                 * -------------------------------------------------
                 */
                else {

                    $animTarget
                        .stop(true, true)
                        .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 위치에서 펼친다.
     *
     * 중요한 점:
     *
     * wrapper와 panel을 둘 다 애니메이션하지 않는다.
     *
     * 버튼 wrapper는 즉시 교체하고,
     * 실제 패널 하나만 slide 애니메이션한다.
     *
     * 따라서 이중 움직임이나
     * 중간 겹침 현상이 최소화된다.
     * =============================================================
     */
    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
            .stop(true, true)
            .addClass('mw-collapsed')
            .hide()
            .css({
                height: '',
                overflow: '',
                paddingTop: '',
                paddingBottom: '',
                marginTop: '',
                marginBottom: ''
            });


        $topWrapper
            .stop(true, true)
            .show()
            .css({
                height: '',
                overflow: '',
                paddingTop: '',
                paddingBottom: '',
                marginTop: '',
                marginBottom: ''
            });


        $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')) {
                    return false;
                }


                /*
                 * 이미 열려 있으면 중단
                 */
                if ($panel.is(':visible')) {
                    return false;
                }


                /*
                 * -------------------------------------------------
                 * 상단 버튼 wrapper는 즉시 숨김
                 *
                 * wrapper 자체를 애니메이션하지 않으므로
                 * 이중 움직임이 발생하지 않는다.
                 * -------------------------------------------------
                 */
                $topWrapper
                    .stop(true, true)
                    .hide();


                /*
                 * MediaWiki collapsed 상태 제거
                 */
                $panel.removeClass('mw-collapsed');


                /*
                 * -------------------------------------------------
                 * 실제 패널 하나만 부드럽게 펼침
                 * -------------------------------------------------
                 */
                $panel
                    .stop(true, true)
                    .hide()
                    .slideDown(260, function () {

                        /*
                         * animation 종료 후
                         * jQuery가 만든 inline style 정리
                         */
                        $(this).css({
                            height: '',
                            overflow: '',
                            paddingTop: '',
                            paddingBottom: '',
                            marginTop: '',
                            marginBottom: ''
                        });
                    });


                /*
                 * ARIA
                 */
                $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')) {
                    return false;
                }


                /*
                 * -------------------------------------------------
                 * 실제 패널 하나만 접음
                 * -------------------------------------------------
                 */
                $panel
                    .stop(true, true)
                    .slideUp(260, function () {

                        /*
                         * 패널을 완전히 닫은 뒤
                         * collapsed 상태 복원
                         */
                        $(this)
                            .addClass('mw-collapsed')
                            .css({
                                display: 'none',
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });


                        /*
                         * -------------------------------------------------
                         * 원래 상단 버튼 즉시 복귀
                         *
                         * 별도 애니메이션을 하지 않기 때문에
                         * 닫힘 동작도 한 번의 움직임으로 보인다.
                         * -------------------------------------------------
                         */
                        $topWrapper
                            .stop(true, true)
                            .show()
                            .css({
                                height: '',
                                overflow: '',
                                paddingTop: '',
                                paddingBottom: '',
                                marginTop: '',
                                marginBottom: ''
                            });
                    });


                /*
                 * ARIA
                 */
                $topToggle.attr('aria-expanded', 'false');
                $panelToggle.attr('aria-expanded', 'false');


                return false;
            });
    }

});