有觉性 以安住且中立的心 照见身心的实相

MediaWiki:Common.js:修订间差异

来自Dhamma.cn
法藏​(留言 | 贡献)
小无编辑摘要
法藏​(留言 | 贡献)
小无编辑摘要
第1行: 第1行:
```javascript
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */


第42行: 第43行:


     /* =====================================================
     /* =====================================================
       2. 从 localStorage 读取
       2. 可用选项
       ===================================================== */
       ===================================================== */


     var settings = {};
     var allowedThemes = [
        'default',
        'cream',
        'zen',
        'dark'
    ];
 
    var allowedFonts = [
        'system',
        'serif',
        'sans'
    ];
 
 
    /* =====================================================
      3. 字体
      ===================================================== */
 
    var fonts = {
 
        system:
            '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "Microsoft YaHei", Arial, sans-serif',
 
        serif:
            '"Noto Serif SC", "Source Han Serif SC", "Songti SC", "SimSun", serif',
 
        sans:
            '"Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", Arial, sans-serif'
 
    };
 
 
    /* =====================================================
      4. 从 localStorage 读取设置
      ===================================================== */
 
    var settings;


     try {
     try {


         var saved =
         var saved =
             localStorage.getItem('dhammaReaderSettings');
             localStorage.getItem(
                'dhammaReaderSettings'
            );


         if (saved) {
         if (saved) {
            var parsed =
                JSON.parse(saved);


             settings =
             settings =
第58行: 第100行:
                     {},
                     {},
                     defaults,
                     defaults,
                     JSON.parse(saved)
                     parsed
                 );
                 );


第64行: 第106行:


             settings =
             settings =
                 Object.assign({}, defaults);
                 Object.assign(
                    {},
                    defaults
                );


         }
         }
第71行: 第116行:


         settings =
         settings =
             Object.assign({}, defaults);
             Object.assign(
                {},
                defaults
            );
 
    }
 
 
    /* =====================================================
      5. 检查并修正设置
      ===================================================== */
 
    function validateSettings() {
 
        /* -----------------------------
          主题
          ----------------------------- */
 
        if (
            allowedThemes.indexOf(
                settings.theme
            ) === -1
        ) {
 
            settings.theme =
                defaults.theme;
 
        }
 
 
        /* -----------------------------
          字体
          ----------------------------- */
 
        if (
            allowedFonts.indexOf(
                settings.fontFamily
            ) === -1
        ) {
 
            settings.fontFamily =
                defaults.fontFamily;
 
        }
 
 
        /* -----------------------------
          字号
          ----------------------------- */
 
        settings.fontSize =
            parseInt(
                settings.fontSize,
                10
            );
 
        if (
            isNaN(settings.fontSize) ||
            settings.fontSize < 14 ||
            settings.fontSize > 26
        ) {
 
            settings.fontSize =
                defaults.fontSize;
 
        }
 
 
        /* -----------------------------
          行距
          ----------------------------- */
 
        settings.lineHeight =
            parseFloat(
                settings.lineHeight
            );
 
        if (
            isNaN(settings.lineHeight) ||
            settings.lineHeight < 1.4 ||
            settings.lineHeight > 2.4
        ) {
 
            settings.lineHeight =
                defaults.lineHeight;
 
        }
 
 
        /* -----------------------------
          阅读宽度
          ----------------------------- */
 
        settings.maxWidth =
            parseInt(
                settings.maxWidth,
                10
            );
 
        if (
            isNaN(settings.maxWidth) ||
            settings.maxWidth < 680 ||
            settings.maxWidth > 1200
        ) {
 
            settings.maxWidth =
                defaults.maxWidth;
 
        }


     }
     }
    validateSettings();




     /* =====================================================
     /* =====================================================
       3. 保存设置
       6. 保存设置
       ===================================================== */
       ===================================================== */


第91行: 第247行:
         } catch (e) {
         } catch (e) {


             // 浏览器禁用 localStorage 时忽略
             /*
            * 浏览器禁用 localStorage 时,
            * 忽略保存错误,不影响页面使用。
            */
 
         }
         }


第98行: 第258行:


     /* =====================================================
     /* =====================================================
       4. 字体
       7. 应用设置
       ===================================================== */
       ===================================================== */


     var fonts = {
     function applySettings() {
 
        var root =
            document.documentElement;
 
        var body =
            document.body;
 
 
        /*
        * 如果 body 尚未建立,
        * 暂时不执行。
        */
 
        if (!body) {
 
            return;
 
        }


        system:
            '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "Microsoft YaHei", Arial, sans-serif',


         serif:
         /* -----------------------------
            '"Noto Serif SC", "Source Han Serif SC", "Songti SC", "SimSun", serif',
          背景主题
          ----------------------------- */


         sans:
         body.classList.remove(
             '"Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", Arial, sans-serif'
             'reader-theme-default',
            'reader-theme-cream',
            'reader-theme-zen',
            'reader-theme-dark'
        );


    };
        body.classList.add(
            'reader-theme-' +
            settings.theme
        );




    /* =====================================================
        /* -----------------------------
      5. 应用设置
          字号
      ===================================================== */
          ----------------------------- */


  function applySettings() {
        root.style.setProperty(
            '--reader-font-size',
            settings.fontSize + 'px'
        );


    var root =
        document.documentElement;


    var body =
        /* -----------------------------
        document.body;
          行距
          ----------------------------- */


    if (!body) {
        root.style.setProperty(
         return;
            '--reader-line-height',
    }
            settings.lineHeight
         );


    body.classList.remove(
        'reader-theme-default',
        'reader-theme-cream',
        'reader-theme-zen',
        'reader-theme-dark'
    );


    body.classList.add(
         /* -----------------------------
         'reader-theme-' + settings.theme
          阅读宽度
    );
          ----------------------------- */


    root.style.setProperty(
        root.style.setProperty(
        '--reader-font-size',
            '--reader-max-width',
        settings.fontSize + 'px'
            settings.maxWidth + 'px'
    );
        );


    root.style.setProperty(
        '--reader-line-height',
        settings.lineHeight
    );


    root.style.setProperty(
         /* -----------------------------
         '--reader-max-width',
          字体
        settings.maxWidth + 'px'
          ----------------------------- */
    );


    root.style.setProperty(
        root.style.setProperty(
        '--reader-font-family',
            '--reader-font-family',
        fonts[settings.fontFamily]
            fonts[settings.fontFamily]
    );
        );


}
    }




     /* =====================================================
     /* =====================================================
       6. 创建设置按钮
       8. 创建阅读设置按钮
       ===================================================== */
       ===================================================== */


第176行: 第352行:
             )
             )
         ) {
         ) {
             return;
             return;
        }
        if (!document.body) {
            return;
         }
         }




         var button =
         var button =
             document.createElement('button');
             document.createElement(
                'button'
            );
 


         button.id =
         button.id =
             'reader-settings-button';
             'reader-settings-button';


         button.type =
         button.type =
             'button';
             'button';


         button.setAttribute(
         button.setAttribute(
第193行: 第383行:
             '阅读设置'
             '阅读设置'
         );
         );


         button.setAttribute(
         button.setAttribute(
第198行: 第389行:
             '阅读设置'
             '阅读设置'
         );
         );


         button.innerHTML =
         button.innerHTML =
第203行: 第395行:




         document.body.appendChild(button);
         document.body.appendChild(
            button
        );




第214行: 第408行:
                         'reader-settings-panel'
                         'reader-settings-panel'
                     );
                     );


                 if (!panel) {
                 if (!panel) {
                     return;
                     return;
                 }
                 }


                 panel.classList.toggle(
                 panel.classList.toggle(
第230行: 第428行:


     /* =====================================================
     /* =====================================================
       7. 创建设置面板
       9. 创建设置面板
       ===================================================== */
       ===================================================== */


第240行: 第438行:
             )
             )
         ) {
         ) {
             return;
             return;
        }
        if (!document.body) {
            return;
         }
         }




         var panel =
         var panel =
             document.createElement('div');
             document.createElement(
                'div'
            );
 


         panel.id =
         panel.id =
第255行: 第465行:
             'dialog'
             'dialog'
         );
         );


         panel.setAttribute(
         panel.setAttribute(
第283行: 第494行:
                     <button
                     <button
                         type="button"
                         type="button"
                         class="reader-theme-button reader-theme-default-button"
                         class="reader-theme-button"
                         data-theme="default">
                         data-theme="default">
                         默认
                         默认
第290行: 第501行:
                     <button
                     <button
                         type="button"
                         type="button"
                         class="reader-theme-button reader-theme-cream-button"
                         class="reader-theme-button"
                         data-theme="cream">
                         data-theme="cream">
                         米白
                         米白
第297行: 第508行:
                     <button
                     <button
                         type="button"
                         type="button"
                         class="reader-theme-button reader-theme-zen-button"
                         class="reader-theme-button"
                         data-theme="zen">
                         data-theme="zen">
                         禅意
                         禅意
第304行: 第515行:
                     <button
                     <button
                         type="button"
                         type="button"
                         class="reader-theme-button reader-theme-dark-button"
                         class="reader-theme-button"
                         data-theme="dark">
                         data-theme="dark">
                         夜间
                         夜间
第469行: 第680行:




         document.body.appendChild(panel);
         document.body.appendChild(
            panel
        );




第480行: 第693行:


     /* =====================================================
     /* =====================================================
       8. 绑定事件
       10. 绑定设置事件
       ===================================================== */
       ===================================================== */


第494行: 第707行:
                 '.reader-theme-button'
                 '.reader-theme-button'
             )
             )
             .forEach(function (button) {
             .forEach(
                function (button) {


                button.addEventListener(
                    button.addEventListener(
                    'click',
                        'click',
                    function () {
                        function () {


                        settings.theme =
                            settings.theme =
                            this.dataset.theme;
                                this.dataset.theme;


                        applySettings();
                            applySettings();


                        saveSettings();
                            saveSettings();


                        updateSettingsUI();
                            updateSettingsUI();


                    }
                        }
                );
                    );


             });
                }
             );




第524行: 第739行:
             );
             );


        fontSize.addEventListener(
            'input',
            function () {


                 settings.fontSize =
        if (fontSize) {
                    parseInt(
 
                        this.value,
            fontSize.addEventListener(
                        10
                 'input',
                    );
                function () {
 
                    settings.fontSize =
                        parseInt(
                            this.value,
                            10
                        );


                applySettings();
                    applySettings();


                saveSettings();
                    saveSettings();


                updateSettingsUI();
                    updateSettingsUI();
 
                }
            );


            }
        }
        );




第553行: 第773行:
             );
             );


        lineHeight.addEventListener(
            'input',
            function () {


                 settings.lineHeight =
        if (lineHeight) {
                    parseFloat(
 
                        this.value
            lineHeight.addEventListener(
                     );
                 'input',
                function () {
 
                    settings.lineHeight =
                        parseFloat(
                            this.value
                        );
 
                     applySettings();


                applySettings();
                    saveSettings();


                saveSettings();
                    updateSettingsUI();


                 updateSettingsUI();
                 }
            );


            }
        }
        );




第581行: 第806行:
             );
             );


        maxWidth.addEventListener(
            'input',
            function () {


                 settings.maxWidth =
        if (maxWidth) {
                    parseInt(
 
                        this.value,
            maxWidth.addEventListener(
                         10
                'input',
                     );
                 function () {
 
                    settings.maxWidth =
                        parseInt(
                            this.value,
                            10
                         );
 
                     applySettings();


                applySettings();
                    saveSettings();


                saveSettings();
                    updateSettingsUI();


                 updateSettingsUI();
                 }
            );


            }
        }
        );




第609行: 第839行:
                 '.reader-font-button'
                 '.reader-font-button'
             )
             )
             .forEach(function (button) {
             .forEach(
                function (button) {


                button.addEventListener(
                    button.addEventListener(
                    'click',
                        'click',
                    function () {
                        function () {


                        settings.fontFamily =
                            settings.fontFamily =
                            this.dataset.font;
                                this.dataset.font;


                        applySettings();
                            applySettings();


                        saveSettings();
                            saveSettings();


                        updateSettingsUI();
                            updateSettingsUI();


                    }
                        }
                );
                    );


             });
                }
             );




第634行: 第866行:
           ----------------------------- */
           ----------------------------- */


         document
         var resetButton =
             .getElementById(
             document.getElementById(
                 'reader-settings-reset'
                 'reader-settings-reset'
             )
             );
             .addEventListener(
 
 
        if (resetButton) {
 
             resetButton.addEventListener(
                 'click',
                 'click',
                 function () {
                 function () {
第647行: 第883行:
                             defaults
                             defaults
                         );
                         );


                     applySettings();
                     applySettings();
第656行: 第893行:
                 }
                 }
             );
             );
        }


     }
     }
第661行: 第900行:


     /* =====================================================
     /* =====================================================
       9. 更新界面
       11. 更新设置界面
       ===================================================== */
       ===================================================== */


第675行: 第914行:
                 '.reader-theme-button'
                 '.reader-theme-button'
             )
             )
             .forEach(function (button) {
             .forEach(
                function (button) {


                button.classList.toggle(
                    button.classList.toggle(
                    'active',
                        'active',
                    button.dataset.theme ===
                        button.dataset.theme ===
                    settings.theme
                        settings.theme
                );
                    );


             });
                }
             );




第694行: 第935行:
                 'reader-font-size'
                 'reader-font-size'
             );
             );


         var fontSizeValue =
         var fontSizeValue =
第700行: 第942行:
             );
             );


         if (fontSize) {
 
         if (
            fontSize &&
            fontSizeValue
        ) {


             fontSize.value =
             fontSize.value =
                 settings.fontSize;
                 settings.fontSize;


             fontSizeValue.textContent =
             fontSizeValue.textContent =
第719行: 第966行:
                 'reader-line-height'
                 'reader-line-height'
             );
             );


         var lineHeightValue =
         var lineHeightValue =
第725行: 第973行:
             );
             );


         if (lineHeight) {
 
         if (
            lineHeight &&
            lineHeightValue
        ) {


             lineHeight.value =
             lineHeight.value =
                 settings.lineHeight;
                 settings.lineHeight;


             lineHeightValue.textContent =
             lineHeightValue.textContent =
第744行: 第997行:
                 'reader-max-width'
                 'reader-max-width'
             );
             );


         var maxWidthValue =
         var maxWidthValue =
第750行: 第1,004行:
             );
             );


         if (maxWidth) {
 
         if (
            maxWidth &&
            maxWidthValue
        ) {


             maxWidth.value =
             maxWidth.value =
                 settings.maxWidth;
                 settings.maxWidth;


             maxWidthValue.textContent =
             maxWidthValue.textContent =
第769行: 第1,028行:
                 '.reader-font-button'
                 '.reader-font-button'
             )
             )
             .forEach(function (button) {
             .forEach(
                function (button) {


                button.classList.toggle(
                    button.classList.toggle(
                    'active',
                        'active',
                    button.dataset.font ===
                        button.dataset.font ===
                    settings.fontFamily
                        settings.fontFamily
                );
                    );


             });
                }
             );


     }
     }
第783行: 第1,044行:


     /* =====================================================
     /* =====================================================
       10. 点击页面其他地方关闭面板
       12. 点击页面其他地方关闭面板
       ===================================================== */
       ===================================================== */


第796行: 第1,057行:
                         'reader-settings-panel'
                         'reader-settings-panel'
                     );
                     );


                 var button =
                 var button =
第802行: 第1,064行:
                     );
                     );


                 if (!panel || !button) {
 
                 if (
                    !panel ||
                    !button
                ) {
 
                     return;
                     return;
                 }
                 }




                 if (
                 if (
                     panel.contains(event.target) ||
                     panel.contains(
                     button.contains(event.target)
                        event.target
                    ) ||
                     button.contains(
                        event.target
                    )
                 ) {
                 ) {
                     return;
                     return;
                 }
                 }


第826行: 第1,100行:


     /* =====================================================
     /* =====================================================
       11. 初始化
       13. 初始化
       ===================================================== */
       ===================================================== */


     function initReaderSettings() {
     function initReaderSettings() {
        if (!document.body) {
            return;
        }
        validateSettings();


         applySettings();
         applySettings();
第842行: 第1,125行:




     /*
     /* =====================================================
    * MediaWiki 页面加载完成以后执行。
      14. MediaWiki 页面初始化
    */
      ===================================================== */


      
     function startReaderSettings() {
/* =====================================================
  MediaWiki 初始化
  ===================================================== */


function startReaderSettings() {
        if (
            document.getElementById(
                'reader-settings-button'
            )
        ) {


    if (
             return;
        document.getElementById(
             'reader-settings-button'
        )
    ) {
        return;
    }


    initReaderSettings();
        }
}




        initReaderSettings();


    }


/* =====================================================
  MediaWiki 初始化
  ===================================================== */


function startReaderSettings() {
    /* =====================================================
      15. 页面加载后执行
      ===================================================== */


     if (
     if (
         document.getElementById(
         document.readyState ===
            'reader-settings-button'
        'loading'
        )
     ) {
     ) {
        return;
    }


    initReaderSettings();
        document.addEventListener(
}
            'DOMContentLoaded',
            startReaderSettings
        );


    } else {


/* =====================================================
        startReaderSettings();
  页面加载后初始化
  ===================================================== */


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


    startReaderSettings();


}
})();
```

2026年9月18日 (五) 08:02的版本

```javascript
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */


/**
 * =========================================================
 * Dhamma.cn 阅读显示设置
 * MediaWiki 1.43
 *
 * 功能:
 *  - 背景主题
 *  - 字号
 *  - 行距
 *  - 阅读宽度
 *  - 字体
 *  - localStorage 保存
 * =========================================================
 */

(function () {

    'use strict';


    /* =====================================================
       1. 默认设置
       ===================================================== */

    var defaults = {

        theme: 'default',

        fontSize: 18,

        lineHeight: 1.9,

        maxWidth: 900,

        fontFamily: 'system'

    };


    /* =====================================================
       2. 可用选项
       ===================================================== */

    var allowedThemes = [
        'default',
        'cream',
        'zen',
        'dark'
    ];

    var allowedFonts = [
        'system',
        'serif',
        'sans'
    ];


    /* =====================================================
       3. 字体
       ===================================================== */

    var fonts = {

        system:
            '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "Microsoft YaHei", Arial, sans-serif',

        serif:
            '"Noto Serif SC", "Source Han Serif SC", "Songti SC", "SimSun", serif',

        sans:
            '"Noto Sans SC", "Source Han Sans SC", "Microsoft YaHei", Arial, sans-serif'

    };


    /* =====================================================
       4. 从 localStorage 读取设置
       ===================================================== */

    var settings;

    try {

        var saved =
            localStorage.getItem(
                'dhammaReaderSettings'
            );

        if (saved) {

            var parsed =
                JSON.parse(saved);

            settings =
                Object.assign(
                    {},
                    defaults,
                    parsed
                );

        } else {

            settings =
                Object.assign(
                    {},
                    defaults
                );

        }

    } catch (e) {

        settings =
            Object.assign(
                {},
                defaults
            );

    }


    /* =====================================================
       5. 检查并修正设置
       ===================================================== */

    function validateSettings() {

        /* -----------------------------
           主题
           ----------------------------- */

        if (
            allowedThemes.indexOf(
                settings.theme
            ) === -1
        ) {

            settings.theme =
                defaults.theme;

        }


        /* -----------------------------
           字体
           ----------------------------- */

        if (
            allowedFonts.indexOf(
                settings.fontFamily
            ) === -1
        ) {

            settings.fontFamily =
                defaults.fontFamily;

        }


        /* -----------------------------
           字号
           ----------------------------- */

        settings.fontSize =
            parseInt(
                settings.fontSize,
                10
            );

        if (
            isNaN(settings.fontSize) ||
            settings.fontSize < 14 ||
            settings.fontSize > 26
        ) {

            settings.fontSize =
                defaults.fontSize;

        }


        /* -----------------------------
           行距
           ----------------------------- */

        settings.lineHeight =
            parseFloat(
                settings.lineHeight
            );

        if (
            isNaN(settings.lineHeight) ||
            settings.lineHeight < 1.4 ||
            settings.lineHeight > 2.4
        ) {

            settings.lineHeight =
                defaults.lineHeight;

        }


        /* -----------------------------
           阅读宽度
           ----------------------------- */

        settings.maxWidth =
            parseInt(
                settings.maxWidth,
                10
            );

        if (
            isNaN(settings.maxWidth) ||
            settings.maxWidth < 680 ||
            settings.maxWidth > 1200
        ) {

            settings.maxWidth =
                defaults.maxWidth;

        }

    }


    validateSettings();


    /* =====================================================
       6. 保存设置
       ===================================================== */

    function saveSettings() {

        try {

            localStorage.setItem(
                'dhammaReaderSettings',
                JSON.stringify(settings)
            );

        } catch (e) {

            /*
             * 浏览器禁用 localStorage 时,
             * 忽略保存错误,不影响页面使用。
             */

        }

    }


    /* =====================================================
       7. 应用设置
       ===================================================== */

    function applySettings() {

        var root =
            document.documentElement;

        var body =
            document.body;


        /*
         * 如果 body 尚未建立,
         * 暂时不执行。
         */

        if (!body) {

            return;

        }


        /* -----------------------------
           背景主题
           ----------------------------- */

        body.classList.remove(
            'reader-theme-default',
            'reader-theme-cream',
            'reader-theme-zen',
            'reader-theme-dark'
        );

        body.classList.add(
            'reader-theme-' +
            settings.theme
        );


        /* -----------------------------
           字号
           ----------------------------- */

        root.style.setProperty(
            '--reader-font-size',
            settings.fontSize + 'px'
        );


        /* -----------------------------
           行距
           ----------------------------- */

        root.style.setProperty(
            '--reader-line-height',
            settings.lineHeight
        );


        /* -----------------------------
           阅读宽度
           ----------------------------- */

        root.style.setProperty(
            '--reader-max-width',
            settings.maxWidth + 'px'
        );


        /* -----------------------------
           字体
           ----------------------------- */

        root.style.setProperty(
            '--reader-font-family',
            fonts[settings.fontFamily]
        );

    }


    /* =====================================================
       8. 创建阅读设置按钮
       ===================================================== */

    function createSettingsButton() {

        if (
            document.getElementById(
                'reader-settings-button'
            )
        ) {

            return;

        }


        if (!document.body) {

            return;

        }


        var button =
            document.createElement(
                'button'
            );


        button.id =
            'reader-settings-button';


        button.type =
            'button';


        button.setAttribute(
            'aria-label',
            '阅读设置'
        );


        button.setAttribute(
            'title',
            '阅读设置'
        );


        button.innerHTML =
            '⚙';


        document.body.appendChild(
            button
        );


        button.addEventListener(
            'click',
            function () {

                var panel =
                    document.getElementById(
                        'reader-settings-panel'
                    );


                if (!panel) {

                    return;

                }


                panel.classList.toggle(
                    'reader-settings-open'
                );

            }
        );

    }


    /* =====================================================
       9. 创建设置面板
       ===================================================== */

    function createSettingsPanel() {

        if (
            document.getElementById(
                'reader-settings-panel'
            )
        ) {

            return;

        }


        if (!document.body) {

            return;

        }


        var panel =
            document.createElement(
                'div'
            );


        panel.id =
            'reader-settings-panel';


        panel.setAttribute(
            'role',
            'dialog'
        );


        panel.setAttribute(
            'aria-label',
            '阅读显示设置'
        );


        panel.innerHTML = `

            <div class="reader-settings-title">
                阅读显示设置
            </div>


            <!-- =========================
                 主题
                 ========================= -->

            <div class="reader-setting-group">

                <span class="reader-setting-label">
                    背景
                </span>

                <div class="reader-theme-buttons">

                    <button
                        type="button"
                        class="reader-theme-button"
                        data-theme="default">
                        默认
                    </button>

                    <button
                        type="button"
                        class="reader-theme-button"
                        data-theme="cream">
                        米白
                    </button>

                    <button
                        type="button"
                        class="reader-theme-button"
                        data-theme="zen">
                        禅意
                    </button>

                    <button
                        type="button"
                        class="reader-theme-button"
                        data-theme="dark">
                        夜间
                    </button>

                </div>

            </div>


            <!-- =========================
                 字号
                 ========================= -->

            <div class="reader-setting-group">

                <span class="reader-setting-label">
                    字号
                </span>

                <div class="reader-range-row">

                    <span>A−</span>

                    <input
                        id="reader-font-size"
                        class="reader-range"
                        type="range"
                        min="14"
                        max="26"
                        step="1">

                    <span>A+</span>

                    <span
                        id="reader-font-size-value"
                        class="reader-range-value">
                    </span>

                </div>

            </div>


            <!-- =========================
                 行距
                 ========================= -->

            <div class="reader-setting-group">

                <span class="reader-setting-label">
                    行距
                </span>

                <div class="reader-range-row">

                    <span>窄</span>

                    <input
                        id="reader-line-height"
                        class="reader-range"
                        type="range"
                        min="1.4"
                        max="2.4"
                        step="0.1">

                    <span>宽</span>

                    <span
                        id="reader-line-height-value"
                        class="reader-range-value">
                    </span>

                </div>

            </div>


            <!-- =========================
                 阅读宽度
                 ========================= -->

            <div class="reader-setting-group">

                <span class="reader-setting-label">
                    阅读宽度
                </span>

                <div class="reader-range-row">

                    <span>窄</span>

                    <input
                        id="reader-max-width"
                        class="reader-range"
                        type="range"
                        min="680"
                        max="1200"
                        step="20">

                    <span>宽</span>

                    <span
                        id="reader-max-width-value"
                        class="reader-range-value">
                    </span>

                </div>

            </div>


            <!-- =========================
                 字体
                 ========================= -->

            <div class="reader-setting-group">

                <span class="reader-setting-label">
                    字体
                </span>

                <div class="reader-font-buttons">

                    <button
                        type="button"
                        class="reader-font-button"
                        data-font="system">
                        系统字体
                    </button>

                    <button
                        type="button"
                        class="reader-font-button"
                        data-font="serif">
                        宋体
                    </button>

                    <button
                        type="button"
                        class="reader-font-button"
                        data-font="sans">
                        黑体
                    </button>

                </div>

            </div>


            <!-- =========================
                 恢复默认
                 ========================= -->

            <button
                id="reader-settings-reset"
                type="button">

                恢复默认设置

            </button>

        `;


        document.body.appendChild(
            panel
        );


        bindSettingsEvents();

        updateSettingsUI();

    }


    /* =====================================================
       10. 绑定设置事件
       ===================================================== */

    function bindSettingsEvents() {


        /* -----------------------------
           主题
           ----------------------------- */

        document
            .querySelectorAll(
                '.reader-theme-button'
            )
            .forEach(
                function (button) {

                    button.addEventListener(
                        'click',
                        function () {

                            settings.theme =
                                this.dataset.theme;

                            applySettings();

                            saveSettings();

                            updateSettingsUI();

                        }
                    );

                }
            );


        /* -----------------------------
           字号
           ----------------------------- */

        var fontSize =
            document.getElementById(
                'reader-font-size'
            );


        if (fontSize) {

            fontSize.addEventListener(
                'input',
                function () {

                    settings.fontSize =
                        parseInt(
                            this.value,
                            10
                        );

                    applySettings();

                    saveSettings();

                    updateSettingsUI();

                }
            );

        }


        /* -----------------------------
           行距
           ----------------------------- */

        var lineHeight =
            document.getElementById(
                'reader-line-height'
            );


        if (lineHeight) {

            lineHeight.addEventListener(
                'input',
                function () {

                    settings.lineHeight =
                        parseFloat(
                            this.value
                        );

                    applySettings();

                    saveSettings();

                    updateSettingsUI();

                }
            );

        }


        /* -----------------------------
           阅读宽度
           ----------------------------- */

        var maxWidth =
            document.getElementById(
                'reader-max-width'
            );


        if (maxWidth) {

            maxWidth.addEventListener(
                'input',
                function () {

                    settings.maxWidth =
                        parseInt(
                            this.value,
                            10
                        );

                    applySettings();

                    saveSettings();

                    updateSettingsUI();

                }
            );

        }


        /* -----------------------------
           字体
           ----------------------------- */

        document
            .querySelectorAll(
                '.reader-font-button'
            )
            .forEach(
                function (button) {

                    button.addEventListener(
                        'click',
                        function () {

                            settings.fontFamily =
                                this.dataset.font;

                            applySettings();

                            saveSettings();

                            updateSettingsUI();

                        }
                    );

                }
            );


        /* -----------------------------
           恢复默认
           ----------------------------- */

        var resetButton =
            document.getElementById(
                'reader-settings-reset'
            );


        if (resetButton) {

            resetButton.addEventListener(
                'click',
                function () {

                    settings =
                        Object.assign(
                            {},
                            defaults
                        );


                    applySettings();

                    saveSettings();

                    updateSettingsUI();

                }
            );

        }

    }


    /* =====================================================
       11. 更新设置界面
       ===================================================== */

    function updateSettingsUI() {


        /* -----------------------------
           主题按钮
           ----------------------------- */

        document
            .querySelectorAll(
                '.reader-theme-button'
            )
            .forEach(
                function (button) {

                    button.classList.toggle(
                        'active',
                        button.dataset.theme ===
                        settings.theme
                    );

                }
            );


        /* -----------------------------
           字号
           ----------------------------- */

        var fontSize =
            document.getElementById(
                'reader-font-size'
            );


        var fontSizeValue =
            document.getElementById(
                'reader-font-size-value'
            );


        if (
            fontSize &&
            fontSizeValue
        ) {

            fontSize.value =
                settings.fontSize;


            fontSizeValue.textContent =
                settings.fontSize + 'px';

        }


        /* -----------------------------
           行距
           ----------------------------- */

        var lineHeight =
            document.getElementById(
                'reader-line-height'
            );


        var lineHeightValue =
            document.getElementById(
                'reader-line-height-value'
            );


        if (
            lineHeight &&
            lineHeightValue
        ) {

            lineHeight.value =
                settings.lineHeight;


            lineHeightValue.textContent =
                settings.lineHeight;

        }


        /* -----------------------------
           阅读宽度
           ----------------------------- */

        var maxWidth =
            document.getElementById(
                'reader-max-width'
            );


        var maxWidthValue =
            document.getElementById(
                'reader-max-width-value'
            );


        if (
            maxWidth &&
            maxWidthValue
        ) {

            maxWidth.value =
                settings.maxWidth;


            maxWidthValue.textContent =
                settings.maxWidth + 'px';

        }


        /* -----------------------------
           字体
           ----------------------------- */

        document
            .querySelectorAll(
                '.reader-font-button'
            )
            .forEach(
                function (button) {

                    button.classList.toggle(
                        'active',
                        button.dataset.font ===
                        settings.fontFamily
                    );

                }
            );

    }


    /* =====================================================
       12. 点击页面其他地方关闭面板
       ===================================================== */

    function bindOutsideClick() {

        document.addEventListener(
            'click',
            function (event) {

                var panel =
                    document.getElementById(
                        'reader-settings-panel'
                    );


                var button =
                    document.getElementById(
                        'reader-settings-button'
                    );


                if (
                    !panel ||
                    !button
                ) {

                    return;

                }


                if (
                    panel.contains(
                        event.target
                    ) ||
                    button.contains(
                        event.target
                    )
                ) {

                    return;

                }


                panel.classList.remove(
                    'reader-settings-open'
                );

            }
        );

    }


    /* =====================================================
       13. 初始化
       ===================================================== */

    function initReaderSettings() {

        if (!document.body) {

            return;

        }


        validateSettings();

        applySettings();

        createSettingsButton();

        createSettingsPanel();

        bindOutsideClick();

    }


    /* =====================================================
       14. MediaWiki 页面初始化
       ===================================================== */

    function startReaderSettings() {

        if (
            document.getElementById(
                'reader-settings-button'
            )
        ) {

            return;

        }


        initReaderSettings();

    }


    /* =====================================================
       15. 页面加载后执行
       ===================================================== */

    if (
        document.readyState ===
        'loading'
    ) {

        document.addEventListener(
            'DOMContentLoaded',
            startReaderSettings
        );

    } else {

        startReaderSettings();

    }


})();
```