MediaWiki:Common.js

提供: MochiuWiki : SUSE, EC, PCB

2025年11月10日 (月) 08:45時点におけるWiki (トーク | 投稿記録)による版

注意: 保存後、変更を確認するにはブラウザーのキャッシュを消去する必要がある場合があります。

  • Firefox / Safari: Shift を押しながら 再読み込み をクリックするか、Ctrl-F5 または Ctrl-R を押してください (Mac では ⌘-R)
  • Google Chrome: Ctrl-Shift-R を押してください (Mac では ⌘-Shift-R)
  • Microsoft Edge: Ctrl を押しながら 最新の情報に更新 をクリックするか、Ctrl-F5 を押してください。
/* ここにあるすべてのJavaScriptは、すべてのページ読み込みですべての利用者に対して読み込まれます */

// ========================================
// ダークモード切り替え機能(サイドバー&上部バー配置版)
// MediaWiki:Common.js に追加してください
// ========================================

(function() {
    'use strict';
    
    // 設定
    const THEME_KEY = 'fluent-theme-preference';
    const THEME_DARK = 'dark';
    const THEME_LIGHT = 'light';
    const THEME_AUTO = 'auto';
    
    // 現在のテーマを取得
    function getCurrentTheme() {
        return localStorage.getItem(THEME_KEY) || THEME_AUTO;
    }
    
    // テーマを適用
    function applyTheme(theme) {
        const html = document.documentElement;
        html.classList.remove('theme-dark', 'theme-light', 'theme-auto');
        html.classList.add('theme-' + theme);
        localStorage.setItem(THEME_KEY, theme);
        updateAllThemeButtons(theme);
    }
    
    // すべてのボタンを更新
    function updateAllThemeButtons(theme) {
        const buttons = document.querySelectorAll('.theme-toggle-button, .theme-toggle-link');
        buttons.forEach(function(button) {
            updateThemeButton(button, theme);
        });
    }
    
    // ボタンの表示を更新
    function updateThemeButton(button, theme) {
        if (!button) return;
        
        const configs = {
            'light': { 
                icon: '☀️', 
                text: 'ライトモード', 
                title: 'ライトモードに切り替え',
                nextMode: 'ダークモードに切り替え'
            },
            'dark': { 
                icon: '🌙', 
                text: 'ダークモード', 
                title: 'ダークモードに切り替え',
                nextMode: '自動モードに切り替え'
            },
            'auto': { 
                icon: '🌗', 
                text: '自動切替', 
                title: '自動(システム設定)',
                nextMode: 'ライトモードに切り替え'
            }
        };
        
        const config = configs[theme];
        
        // アイコン要素があれば更新
        const iconSpan = button.querySelector('.theme-icon');
        if (iconSpan) {
            iconSpan.textContent = config.icon;
        }
        
        // テキスト要素があれば更新
        const textSpan = button.querySelector('.theme-text');
        if (textSpan) {
            textSpan.textContent = config.text;
        }
        
        // ボタン全体のテキストを更新(テキストリンク用)
        if (button.classList.contains('theme-toggle-text-only')) {
            button.textContent = config.icon + ' ' + config.text;
        }
        
        button.title = config.nextMode;
        button.setAttribute('aria-label', config.nextMode);
    }
    
    // テーマを切り替え
    function toggleTheme(e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        
        const current = getCurrentTheme();
        let next;
        
        switch(current) {
            case THEME_LIGHT:
                next = THEME_DARK;
                break;
            case THEME_DARK:
                next = THEME_AUTO;
                break;
            case THEME_AUTO:
            default:
                next = THEME_LIGHT;
                break;
        }
        
        applyTheme(next);
    }
    
    // サイドバーにボタンを追加
    function addSidebarButton() {
        // 既に存在する場合はスキップ
        if (document.getElementById('theme-toggle-sidebar')) return;
        
        // サイドバーの「ツール」セクションを探す
        const sideNavigation = document.getElementById('site-navigation');
        if (!sideNavigation) return;
        
        // ボタンコンテナを作成
        const container = document.createElement('div');
        container.id = 'theme-toggle-sidebar';
        container.className = 'theme-toggle-container';
        
        // タイトル(オプション)
        const title = document.createElement('h3');
        title.textContent = '表示設定';
        title.className = 'theme-section-title';
        
        // ボタンを作成
        const button = document.createElement('button');
        button.className = 'theme-toggle-button theme-toggle-sidebar-button';
        button.setAttribute('aria-label', 'テーマ切り替え');
        button.setAttribute('type', 'button');
        
        const iconSpan = document.createElement('span');
        iconSpan.className = 'theme-icon';
        
        const textSpan = document.createElement('span');
        textSpan.className = 'theme-text';
        
        button.appendChild(iconSpan);
        button.appendChild(textSpan);
        button.addEventListener('click', toggleTheme);
        
        container.appendChild(title);
        container.appendChild(button);
        
        // サイドバーの先頭に追加
        sideNavigation.insertBefore(container, sideNavigation.firstChild);
        
        return button;
    }
    
    // 上部バーにボタンを追加
    function addTopBarButton() {
        // 既に存在する場合はスキップ
        if (document.getElementById('theme-toggle-topbar')) return;
        
        // ページツール(編集、履歴などのエリア)を探す
        const pageTools = document.getElementById('page-tools');
        if (!pageTools) return;
        
        // ボタンを作成
        const button = document.createElement('button');
        button.id = 'theme-toggle-topbar';
        button.className = 'theme-toggle-button theme-toggle-topbar-button';
        button.setAttribute('aria-label', 'テーマ切り替え');
        button.setAttribute('type', 'button');
        
        const iconSpan = document.createElement('span');
        iconSpan.className = 'theme-icon';
        iconSpan.style.marginRight = '4px';
        
        const textSpan = document.createElement('span');
        textSpan.className = 'theme-text';
        
        button.appendChild(iconSpan);
        button.appendChild(textSpan);
        button.addEventListener('click', toggleTheme);
        
        // ページツールの最後に追加
        pageTools.appendChild(button);
        
        return button;
    }
    
    // ヘッダーにシンプルなボタンを追加(右上)
    function addHeaderButton() {
        // 既に存在する場合はスキップ
        if (document.getElementById('theme-toggle-header')) return;
        
        const userTools = document.getElementById('user-tools');
        if (!userTools) return;
        
        // シンプルなアイコンボタンを作成
        const button = document.createElement('button');
        button.id = 'theme-toggle-header';
        button.className = 'theme-toggle-button theme-toggle-header-button';
        button.setAttribute('aria-label', 'テーマ切り替え');
        button.setAttribute('type', 'button');
        
        const iconSpan = document.createElement('span');
        iconSpan.className = 'theme-icon';
        
        button.appendChild(iconSpan);
        button.addEventListener('click', toggleTheme);
        
        // search-iconの後に挿入
        const searchIcon = document.getElementById('search-icon');
        if (searchIcon && searchIcon.nextSibling) {
            userTools.insertBefore(button, searchIcon.nextSibling);
        } else {
            userTools.appendChild(button);
        }
        
        return button;
    }
    
    // 初期化
    function init() {
        // 複数の場所にボタンを追加
        addSidebarButton();    // サイドバーに追加
        addTopBarButton();      // 上部バー(ページツール)に追加
        addHeaderButton();      // ヘッダー右上に追加
        
        // 保存されたテーマを適用
        const savedTheme = getCurrentTheme();
        applyTheme(savedTheme);
    }
    
    // DOMContentLoaded時に初期化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
    
    // MediaWikiフックとの連携
    if (typeof mw !== 'undefined' && mw.hook) {
        mw.hook('wikipage.content').add(function() {
            const savedTheme = getCurrentTheme();
            applyTheme(savedTheme);
        });
    }
})();