미디어위키:NationsPanel.js

Nxdsxn (토론 | 기여)님의 2026년 7월 5일 (일) 03:30 판

참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.

  • 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
  • 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
  • 인터넷 익스플로러 / 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
  • 오페라: Ctrl-F5를 입력.
/* =========================================
   COASTLINE: BLACK ICE - Nations Panel
   국가 및 조합: 시대 전환 / 역사 연도 / 대륙 탭 제어
   ========================================= */
(function () {
    'use strict';

    var DEFAULT_ERAS = [
        '1950', '1960', '1970', '1980', '1990', '2000', '2010', '2020',
        '2030', '2040', '2050', '2060', '2070', '2080', '2090', '2100'
    ];

    function toArray(list) {
        return Array.prototype.slice.call(list || []);
    }

    function unique(values) {
        var seen = {};
        var out = [];

        values.forEach(function (value) {
            value = String(value || '').trim();
            if (!value || seen[value]) return;
            seen[value] = true;
            out.push(value);
        });

        return out;
    }

    function parseEraList(stack) {
        var raw = stack ? stack.getAttribute('data-nations-era-years') : '';
        var values = raw ? raw.split(/[\s,|]+/) : [];

        values = unique(values.filter(function (value) {
            return /^\d{4}$/.test(value);
        }));

        return values.length ? values : DEFAULT_ERAS.slice();
    }

    function getEraIndex(eras, era) {
        var index = eras.indexOf(String(era || ''));
        return index >= 0 ? index : 0;
    }

    function getWrappedEra(eras, index) {
        if (!eras.length) return '';
        index = ((index % eras.length) + eras.length) % eras.length;
        return eras[index];
    }

    function setHidden(element, hidden) {
        if (!element) return;
        if (hidden) {
            element.setAttribute('hidden', 'hidden');
            element.setAttribute('aria-hidden', 'true');
            element.classList.remove('is-active');
        } else {
            element.removeAttribute('hidden');
            element.setAttribute('aria-hidden', 'false');
            element.classList.add('is-active');
        }
    }

    function setActiveButton(buttons, activeButton) {
        buttons.forEach(function (button) {
            var active = button === activeButton;

            button.classList.toggle('is-active', active);
            button.setAttribute('aria-selected', active ? 'true' : 'false');
            button.setAttribute('tabindex', active ? '0' : '-1');
        });
    }

    function activatePanelsByAttribute(panels, attrName, value) {
        panels.forEach(function (panel) {
            setHidden(panel, panel.getAttribute(attrName) !== value);
        });
    }

    function replaceYearTemplate(template, era) {
        return String(template || '').replace(/\{year\}/g, String(era || ''));
    }

    function readTemplate(root, attrName, fallback) {
        if (!root) return fallback;
        return root.getAttribute(attrName) || fallback;
    }

    function setAttributeFromTemplate(root, attrName, templateName, fallback, era) {
        var template = readTemplate(root, templateName, fallback);
        if (!template) {
            root.removeAttribute(attrName);
            return;
        }
        root.setAttribute(attrName, replaceYearTemplate(template, era));
    }

    function resetGlobeDom(globe) {
        var instance;

        if (!globe) return;

        instance = globe.CLBI_NationsGlobeInstance || null;
        if (instance && typeof instance.dispose === 'function') {
            try { instance.dispose(); } catch (err) {}
        }

        globe.CLBI_NationsGlobeInstance = null;
        globe.removeAttribute('data-nations-globe-ready');
        globe.classList.remove('is-ready', 'has-error', 'is-dragging');
        globe.innerHTML = '';
    }

    function reloadGlobe(globe) {
        if (!globe) return;

        if (window.CLBI_NationsGlobe && typeof window.CLBI_NationsGlobe.reload === 'function') {
            window.CLBI_NationsGlobe.reload(globe);
            return;
        }

        resetGlobeDom(globe);

        if (window.CLBI_NationsGlobe && typeof window.CLBI_NationsGlobe.init === 'function') {
            window.CLBI_NationsGlobe.init(globe.parentNode || document);
            return;
        }

        window.setTimeout(function () {
            if (window.CLBI_NationsGlobe && typeof window.CLBI_NationsGlobe.init === 'function') {
                window.CLBI_NationsGlobe.init(globe.parentNode || document);
            }
        }, 0);
    }

    function applyGlobeEra(globe, era, reload) {
        if (!globe || !era) return;

        globe.setAttribute('data-era-active', era);
        globe.setAttribute('data-era', replaceYearTemplate(
            readTemplate(globe, 'data-era-readout-template', '{year} · CLBI BORDER LAYER'),
            era
        ));

        setAttributeFromTemplate(
            globe,
            'data-geojson-url',
            'data-era-border-url-template',
            '/index.php/Special:Redirect/file/{year}_Border_Lines.geojson',
            era
        );
        setAttributeFromTemplate(
            globe,
            'data-polygon-overlay-url',
            'data-era-polygon-overlay-url-template',
            '/index.php/Special:Redirect/file/{year}_Polygon_Overlay.webp',
            era
        );
        setAttributeFromTemplate(
            globe,
            'data-polygon-hit-url',
            'data-era-polygon-hit-url-template',
            '/index.php/Special:Redirect/file/{year}_Polygon_Hit.geojson',
            era
        );
        setAttributeFromTemplate(
            globe,
            'data-nation-link-map-url',
            'data-era-nation-link-map-url-template',
            '/index.php?title=MediaWiki:{year}_Nation_Link_Map.json&action=raw&ctype=application/json',
            era
        );
        setAttributeFromTemplate(
            globe,
            'data-lakes-overlay-url',
            'data-era-lakes-overlay-url-template',
            '/index.php/Special:Redirect/file/{year}_Lakes_Overlay.webp',
            era
        );

        if (reload) {
            reloadGlobe(globe);
        }
    }

    function initContinentPanel(panel) {
        var tabs;
        var bodies;

        if (!panel || panel.getAttribute('data-nations-tabpanel-ready') === '1') return;
        panel.setAttribute('data-nations-tabpanel-ready', '1');

        tabs = toArray(panel.querySelectorAll('.clbi-nations-tabpanel-tab[data-continent]'));
        bodies = toArray(panel.querySelectorAll('.clbi-nations-tabpanel-continent[data-continent-panel]'));

        function activate(continent) {
            var activeTab = null;

            tabs.forEach(function (tab) {
                if (tab.getAttribute('data-continent') === continent) activeTab = tab;
            });

            if (!activeTab && tabs.length) {
                activeTab = tabs[0];
                continent = activeTab.getAttribute('data-continent');
            }

            setActiveButton(tabs, activeTab);
            activatePanelsByAttribute(bodies, 'data-continent-panel', continent);
        }

        tabs.forEach(function (tab) {
            tab.setAttribute('role', 'tab');
            tab.setAttribute('tabindex', tab.classList.contains('is-active') ? '0' : '-1');

            tab.addEventListener('click', function () {
                activate(tab.getAttribute('data-continent'));
            });

            tab.addEventListener('keydown', function (event) {
                if (event.key === 'Enter' || event.key === ' ') {
                    event.preventDefault();
                    activate(tab.getAttribute('data-continent'));
                }
            });
        });

        activate((panel.querySelector('.clbi-nations-tabpanel-tab.is-active[data-continent]') || tabs[0] || {}).getAttribute && (panel.querySelector('.clbi-nations-tabpanel-tab.is-active[data-continent]') || tabs[0]).getAttribute('data-continent'));
    }

    function initHistoryYearPanel(panel) {
        var tabs;
        var pages;

        if (!panel || panel.getAttribute('data-nations-history-ready') === '1') return;
        panel.setAttribute('data-nations-history-ready', '1');

        tabs = toArray(panel.querySelectorAll('.clbi-nations-history-year-button[data-year]'));
        pages = toArray(panel.querySelectorAll('.clbi-nations-history-page[data-year-panel]'));

        function activate(year) {
            var activeTab = null;

            tabs.forEach(function (tab) {
                if (tab.getAttribute('data-year') === year) activeTab = tab;
            });

            if (!activeTab && tabs.length) {
                activeTab = tabs[0];
                year = activeTab.getAttribute('data-year');
            }

            setActiveButton(tabs, activeTab);
            activatePanelsByAttribute(pages, 'data-year-panel', year);
        }

        tabs.forEach(function (tab) {
            tab.setAttribute('role', 'tab');
            tab.setAttribute('tabindex', tab.classList.contains('is-active') ? '0' : '-1');

            tab.addEventListener('click', function () {
                activate(tab.getAttribute('data-year'));
            });

            tab.addEventListener('keydown', function (event) {
                if (event.key === 'Enter' || event.key === ' ') {
                    event.preventDefault();
                    activate(tab.getAttribute('data-year'));
                }
            });
        });

        activate((panel.querySelector('.clbi-nations-history-year-button.is-active[data-year]') || tabs[0] || {}).getAttribute && (panel.querySelector('.clbi-nations-history-year-button.is-active[data-year]') || tabs[0]).getAttribute('data-year'));
    }

    function initEraStack(stack) {
        var eras;
        var titleNode;
        var titlePlates;
        var contentPanels;
        var leftButton;
        var rightButton;
        var globe;
        var currentEra;

        if (!stack || stack.getAttribute('data-nations-era-controller-ready') === '1') return;
        stack.setAttribute('data-nations-era-controller-ready', '1');

        eras = parseEraList(stack);
        titleNode = stack.querySelector('[data-nations-era-title]');
        titlePlates = toArray(stack.querySelectorAll('.clbi-nations-era-title-plate[data-era]'));
        contentPanels = toArray(stack.querySelectorAll('.clbi-nations-era-content[data-era-content]'));
        leftButton = stack.querySelector('[data-nations-era-move="prev"], .clbi-nations-era-side-button-left');
        rightButton = stack.querySelector('[data-nations-era-move="next"], .clbi-nations-era-side-button-right');
        globe = stack.querySelector('.clbi-nations-globe-window');
        currentEra = stack.getAttribute('data-nations-current-era') || stack.getAttribute('data-era') || eras[0];

        function setEra(era, options) {
            var index;
            var prevEra;
            var nextEra;
            var reload;

            options = options || {};
            era = String(era || eras[0]);
            index = getEraIndex(eras, era);
            era = eras[index];
            prevEra = getWrappedEra(eras, index - 1);
            nextEra = getWrappedEra(eras, index + 1);
            reload = !!options.reloadGlobe;

            stack.setAttribute('data-nations-current-era', era);
            stack.setAttribute('data-era', era);

            if (titleNode) {
                titleNode.textContent = era + '년';
                titleNode.setAttribute('data-era', era);
            }

            titlePlates.forEach(function (plate) {
                setHidden(plate, plate.getAttribute('data-era') !== era);
            });

            if (leftButton) {
                leftButton.setAttribute('data-target-era', prevEra);
                leftButton.setAttribute('title', prevEra + '년으로 이동');
                leftButton.setAttribute('aria-label', prevEra + '년으로 이동');
            }

            if (rightButton) {
                rightButton.setAttribute('data-target-era', nextEra);
                rightButton.setAttribute('title', nextEra + '년으로 이동');
                rightButton.setAttribute('aria-label', nextEra + '년으로 이동');
            }

            contentPanels.forEach(function (panel) {
                setHidden(panel, panel.getAttribute('data-era-content') !== era);
            });

            applyGlobeEra(globe, era, reload);
        }

        function move(direction) {
            var active = stack.getAttribute('data-nations-current-era') || currentEra || eras[0];
            var index = getEraIndex(eras, active);
            var target = getWrappedEra(eras, index + direction);

            if (!target) return false;
            setEra(target, { reloadGlobe: true });
            return true;
        }

        if (leftButton) {
            leftButton.addEventListener('click', function (event) {
                event.preventDefault();
                move(-1);
            });
        }

        if (rightButton) {
            rightButton.addEventListener('click', function (event) {
                event.preventDefault();
                move(1);
            });
        }

        stack.CLBI_NationsEraController = {
            eras: eras.slice(),
            setEra: function (era) { setEra(era, { reloadGlobe: true }); },
            move: move
        };

        setEra(currentEra, { reloadGlobe: false });
    }

    function init(root) {
        var scope = root && root.querySelectorAll ? root : document;

        toArray(scope.querySelectorAll('.clbi-nations-tabpanel')).forEach(initContinentPanel);
        toArray(scope.querySelectorAll('.clbi-nations-history-panel')).forEach(initHistoryYearPanel);
        toArray(scope.querySelectorAll('.clbi-nations-panel-stack')).forEach(initEraStack);
    }

    function findEraStack() {
        return document.querySelector('.clbi-nations-panel-stack');
    }

    function moveEra(direction) {
        var stack = findEraStack();

        if (!stack || !stack.CLBI_NationsEraController) return false;
        return stack.CLBI_NationsEraController.move(direction);
    }

    function setEra(era) {
        var stack = findEraStack();

        if (!stack || !stack.CLBI_NationsEraController) return false;
        stack.CLBI_NationsEraController.setEra(era);
        return true;
    }

    window.CLBI_NATIONS_PANEL = {
        init: init,
        moveEra: moveEra,
        setEra: setEra
    };

    if (window.mw && mw.hook) {
        mw.hook('wikipage.content').add(function ($content) {
            init($content && $content[0] ? $content[0] : document);
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function () { init(document); });
    } else {
        init(document);
    }
}());