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

MediaWiki:QuoteCopy.js:修订间差异

来自Dhamma.cn
法藏​(留言 | 贡献)
 
法藏​(留言 | 贡献)
小无编辑摘要
 
第1行: 第1行:
/* =========================================================
/* =========================================================
   Dhamma.cn
   Dhamma.cn
   QuoteCopy.js
   法谈划词引用
 
  MediaWiki 1.43


   功能:
   功能:
   选中文字
   1. 在隆波帕默尊者法谈正文中选择文字
      ↓
  2. 松开鼠标左键
   松开鼠标
  3. 弹出唯一按钮:
      ↓
      “复制该段开示及出处”
   出现引用工具
  4. 调用 UrlShortener
      ↓
  5. 自动生成短网址
   复制原文 / 复制出处 / 复制学术脚注
  6. 复制完整引用
 
  示例:
 
   觉知不是控制心,而是如实地知道身心……
 
   ----------------------------------------
 
   法谈摘自:Dhamma.cn 《隆波帕默尊者开示:觉知的要领》


   MediaWiki 1.43
   该篇网址:https://dhamma.cn/s/x9K2
   使用现有 UrlShortener
 
   转载请保持原文,随喜分享,Sadhu!
   ========================================================= */
   ========================================================= */


(function () {
(function () {
     'use strict';
     'use strict';


第24行: 第36行:
       ===================================================== */
       ===================================================== */


     var MIN_LENGTH = 8;
     var MIN_LENGTH = 2;
 
     var MAX_LENGTH = 8000;
     var MAX_LENGTH = 8000;


第32行: 第45行:


     /*
     /*
     * 当前页面短网址。
     * 当前页面短网址缓存
     *
     *
     * 同一个页面只请求一次。
     * 同一页面只调用一次 UrlShortener。
     */
     */
     var pageShortUrl = null;
     var pageShortUrl = null;
第51行: 第64行:
         */
         */
         if (
         if (
             mw.config.get('wgNamespaceNumber') !== 0
             mw.config.get(
                'wgNamespaceNumber'
            ) !== 0
         ) {
         ) {
             return false;
             return false;
第58行: 第73行:


         /*
         /*
         * 只在普通阅读页面
         * 只在正常阅读页面
         */
         */
         if (
         if (
             mw.config.get('wgAction') !== 'view'
             mw.config.get(
                'wgAction'
            ) !== 'view'
         ) {
         ) {
             return false;
             return false;
第68行: 第85行:


         /*
         /*
         * 如果明确存在引用元数据,
         * 如果页面属于:
        * 启用。
        */
        if (
            document.getElementById(
                'dhamma-quote-metadata'
            )
        ) {
            return true;
        }
 
 
        /*
        * 如果属于:
         *
         *
         * 分类:隆波帕默尊者
         * 分类:隆波帕默尊者
第88行: 第92行:
         */
         */
         var categories =
         var categories =
             mw.config.get('wgCategories') || [];
             mw.config.get(
 
                 'wgCategories'
        return categories.indexOf(
             ) || [];
            '隆波帕默尊者'
        ) !== -1;
    }
 
 
    /* =====================================================
      2. 获取引用信息
      ===================================================== */
 
    function getMetadata() {
 
        var metadata =
            document.getElementById(
                 'dhamma-quote-metadata'
             );
 
 
        /*
        * 默认设置
        */
        var result = {
            author: '',
            source: 'Dhamma.cn',
            title: '',
            date: ''
        };
 
 
        /*
        * 如果页面有 metadata
        */
        if (metadata) {
 
            result.author =
                metadata.getAttribute(
                    'data-author'
                ) || '';
 
            result.source =
                metadata.getAttribute(
                    'data-source'
                ) || 'Dhamma.cn';
 
            result.title =
                metadata.getAttribute(
                    'data-title'
                ) || '';
 
            result.date =
                metadata.getAttribute(
                    'data-date'
                ) || '';
 
            return result;
        }
 
 
        /*
        * 没有 metadata 时,
        * 如果是隆波分类,
        * 自动判断作者。
        */
        var categories =
            mw.config.get('wgCategories') || [];




第162行: 第102行:
             ) !== -1
             ) !== -1
         ) {
         ) {
             result.author =
             return true;
                '隆波帕默尊者';
         }
         }
        return result;
    }
    /* =====================================================
      3. 获取页面标题
      ===================================================== */
    function getPageTitle() {
        var metadata =
            getMetadata();




         /*
         /*
         * metadata 中指定标题
         * 允许将来通过隐藏元数据
         * 优先使用。
         * 手动启用。
        */
        if (metadata.title) {
            return metadata.title;
        }
 
 
        /*
        * MediaWiki 页面标题
        */
        var title =
            mw.config.get('wgTitle');
 
        if (title) {
            return title;
        }
 
 
        /*
        * 后备方案
        */
        var heading =
            document.querySelector(
                '#firstHeading, .mw-page-title-main'
            );
 
        if (heading) {
            return heading.textContent.trim();
        }
 
 
        /*
        * 最终后备
        */
        return document.title
            .replace(/\s*-\s*.*$/, '')
            .trim();
    }
 
 
    /* =====================================================
      4. 标题格式处理
      ===================================================== */
 
    function formatTitle(title) {
 
        if (!title) {
            return '';
        }
 
 
        /*
        * 如果本身已经有《》
        * 不重复添加。
         */
         */
         if (
         if (
             title.charAt(0) === '《' &&
             document.getElementById(
            title.charAt(
                 'dhamma-quote-metadata'
                 title.length - 1
             )
             ) === '》'
         ) {
         ) {
             return title;
             return true;
         }
         }




         return '《' + title + '》';
         return false;
     }
     }




     /* =====================================================
     /* =====================================================
       5. 创建引用 Popup
       2. 创建 Popup
       ===================================================== */
       ===================================================== */


第259行: 第130行:


         if (popup) {
         if (popup) {
             return popup;
             return;
         }
         }




         popup =
         popup =
             document.createElement('div');
             document.createElement(
                'div'
            );
 


         popup.id =
         popup.id =
第278行: 第152行:
         popup.setAttribute(
         popup.setAttribute(
             'aria-label',
             'aria-label',
             'Dhamma.cn 引用工具'
             '复制该段开示及出处'
         );
         );




        /*
        * 只有一个按钮。
        */
         popup.innerHTML =
         popup.innerHTML =


             '<div class="dhamma-quote-popup-inner">' +
             '<button ' +


                 '<div class="dhamma-quote-popup-label">' +
                 'type="button" ' +
                    '引用此段开示' +
                '</div>' +


                 '<div class="dhamma-quote-popup-buttons">' +
                 'class="dhamma-quote-button" ' +


                    '<button type="button" ' +
                'data-action="source">' +
                        'class="dhamma-quote-button" ' +
                        'data-action="copy">' +
                        '复制原文' +
                    '</button>' +


                    '<button type="button" ' +
                '复制该段开示及出处' +
                        'class="dhamma-quote-button dhamma-quote-primary" ' +
                        'data-action="source">' +
                        '复制并附出处' +
                    '</button>' +


                    '<button type="button" ' +
            '</button>';
                        'class="dhamma-quote-button" ' +
                        'data-action="footnote">' +
                        '复制学术脚注' +
                    '</button>' +
 
                '</div>' +
 
            '</div>';




第321行: 第180行:


         /*
         /*
         * 不让点击 Popup 时
         * 点击按钮时,
         * 改变当前选区。
         * 不让 Selection 因失焦消失。
         */
         */
         popup.addEventListener(
         popup.addEventListener(
第335行: 第194行:


         /*
         /*
         * 按钮事件
         * 点击按钮
         */
         */
         popup.addEventListener(
         popup.addEventListener(
第352行: 第211行:




                 var action =
                 copyWithSource();
                    button.getAttribute(
 
                        'data-action'
            }
                    );
        );
    }
 
 
    /* =====================================================
      3. 获取当前页面标题
      ===================================================== */
 
    function getRawPageTitle() {
 
        /*
        * MediaWiki 当前页面标题
        */
        var title =
            mw.config.get(
                'wgTitle'
            );




                if (
        if (title) {
                    action === 'copy'
            return title.trim();
                ) {
        }
                    copyPlainText();
                }




                if (
        /*
                    action === 'source'
        * 后备方案
                ) {
        */
                    copyWithSource();
        var heading =
                }
            document.querySelector(
                '#firstHeading, .mw-page-title-main'
            );




                if (
        if (heading) {
                    action === 'footnote'
                ) {
                    copyFootnote();
                }


             }
             return heading
        );
                .textContent
                .trim();
        }




         return popup;
         return document.title
            .replace(
                /\s*-\s*.*$/,
                ''
            )
            .trim();
     }
     }




     /* =====================================================
     /* =====================================================
       6. 获取选中文字
       4. 获取引用标题
       ===================================================== */
       ===================================================== */


     function getSelectionData() {
     function getCitationTitle() {
 
        /*
        * 如果页面存在明确的引用 metadata,
        * 优先使用。
        */
        var metadata =
            document.getElementById(
                'dhamma-quote-metadata'
            );
 
 
        if (metadata) {
 
            var customTitle =
                metadata.getAttribute(
                    'data-title'
                );


        var selection =
            window.getSelection();


            if (
                customTitle &&
                customTitle.trim()
            ) {


        if (
                return normalizeCitationTitle(
            !selection ||
                    customTitle
            selection.rangeCount === 0
                );
        ) {
             }
             return null;
         }
         }




         var text =
        /*
             selection.toString().trim();
        * 自动从页面标题判断。
        */
         var title =
             getRawPageTitle();
 
 
        return normalizeCitationTitle(
            title
        );
    }
 
 
    /* =====================================================
      5. 标题清理
      ===================================================== */


    function normalizeCitationTitle(
        title
    ) {


         if (!text) {
         if (!title) {
             return null;
             return '隆波帕默尊者开示';
         }
         }




         if (
         title =
             text.length < MIN_LENGTH
            title.trim();
        ) {
 
             return null;
 
        }
        /*
        * MediaWiki 页面标题中的下划线
        */
        title =
             title.replace(
                /_/g,
                ' '
             );




         if (
         /*
            text.length > MAX_LENGTH
        * -------------------------------------------------
        ) {
        * 情况一:
            return null;
        *
        }
        *  《觉知的要领》-隆波帕默尊者-2026年...
        *
        * 提取第一个《...》
        * -------------------------------------------------
        */


        var bookMatch =
            title.match(
                /《([^》]+)》/
            );


        var range =
            selection.getRangeAt(0);


        if (bookMatch) {
            title =
                bookMatch[1].trim();
        }


        var container =
            range.commonAncestorContainer;


        /*
        * -------------------------------------------------
        * 情况二:
        *
        * 隆波帕默尊者开示:觉知的要领
        * -------------------------------------------------
        */


         if (
         if (
             container.nodeType ===
             title.indexOf(
            Node.TEXT_NODE
                '隆波帕默尊者开示:'
            ) === 0
         ) {
         ) {
            container =
                container.parentElement;
        }


 
             return title;
        if (!container) {
             return null;
         }
         }




         /*
         /*
         * 必须来自正文。
         * -------------------------------------------------
        * 去掉常见前缀
        *
        * D24.91
        * D24.91-
        * D24.91:
        * -------------------------------------------------
         */
         */
         var content =
 
             container.closest(
         title =
                 '#mw-content-text .mw-parser-output'
             title.replace(
                 /^D\d+(?:\.\d+)?[\s::._-]*/,
                ''
             );
             );




         if (!content) {
         /*
             return null;
        * 去除“隆波帕默尊者”后缀
        }
        */
        title =
            title.replace(
                /[-–—_\s]*隆波帕默尊者.*$/u,
                ''
             );




         return {
         /*
             text: normalizeText(text),
        * 去除常见日期后缀
             range: range.cloneRange()
        *
        };
        * 例如:
    }
        * -2024年10月20日
        * 2024年10月20日
        */
        title =
             title.replace(
                /[-–—_\s]*\d{4}年\d{1,2}月\d{1,2}日.*$/u,
                ''
             );




    /* =====================================================
        /*
      7. 清理选中文字
        * 去除多余的连接符
      ===================================================== */
        */
 
        title =
    function normalizeText(text) {
            title.replace(
                /^[-–—_\s]+|[-–—_\s]+$/g,
                ''
            );


        return text


             .replace(
        title =
                /\u00a0/g,
             title.trim();
                ' '
            )


            .replace(
                /\r\n/g,
                '\n'
            )


            .replace(
        /*
                /[ \t]+\n/g,
        * 如果清理后为空,
                '\n'
        * 使用原页面标题。
            )
        */
        if (!title) {


             .replace(
             title =
                 /\n[ \t]+/g,
                 getRawPageTitle();
                '\n'
        }
            )


            .replace(
                /\n{3,}/g,
                '\n\n'
            )


             .trim();
        /*
        * 最终统一成:
        *
        * 隆波帕默尊者开示:XXXX
        */
        return (
             '隆波帕默尊者开示:' +
            title
        );
     }
     }




     /* =====================================================
     /* =====================================================
       8. 获取 Canonical URL
       6. 获取规范 URL
       ===================================================== */
       ===================================================== */


     function getCanonicalUrl() {
     function getCanonicalUrl() {


         var url =
         var canonical =
             mw.config.get(
             mw.config.get(
                 'wgCanonicalUrl'
                 'wgCanonicalUrl'
第519行: 第471行:




         if (url) {
         if (canonical) {
             return url;
             return canonical;
         }
         }




         /*
         /*
         * 后备
         * 后备方案
         */
         */
         var pageName =
         var pageName =
第550行: 第502行:


     /* =====================================================
     /* =====================================================
       9. 获取短网址
       7. 获取短网址
       ===================================================== */
       ===================================================== */


第556行: 第508行:


         /*
         /*
         * 当前页面已经取得。
         * 当前页面已经有短网址
         */
         */
         if (pageShortUrl) {
         if (pageShortUrl) {
第567行: 第519行:


         /*
         /*
         * 已经正在请求。
         * 已经正在请求
         */
         */
         if (shortUrlPromise) {
         if (shortUrlPromise) {
第579行: 第531行:
         */
         */
         shortUrlPromise =
         shortUrlPromise =
             new mw.Api().post({
             new mw.Api().post({


                 action: 'shortenurl',
                 action:
                    'shortenurl',


                 url:
                 url:
                     getCanonicalUrl()
                     getCanonicalUrl()


             }).then(
             })


            .then(
                 function (data) {
                 function (data) {


                    /*
                    * UrlShortener API
                    *
                    * {
                    *  shortenurl: {
                    *      shorturl: "..."
                    *  }
                    * }
                    */
                     if (
                     if (
                         data &&
                         data &&
第605行: 第570行:


                     throw new Error(
                     throw new Error(
                         'UrlShortener 未返回短网址'
                         'UrlShortener 未返回 shorturl'
                     );
                     );
                 }
                 }
            )


            ).catch(


            .catch(
                 function (error) {
                 function (error) {


                    /*
                    * 允许以后重新请求
                    */
                     shortUrlPromise =
                     shortUrlPromise =
                         null;
                         null;


                     throw error;
                     throw error;
第626行: 第596行:


     /* =====================================================
     /* =====================================================
       10. 获取日期
       8. 获取选中文字
       ===================================================== */
       ===================================================== */


     function getAccessDate() {
     function getSelectionData() {


         var now =
         var selection =
             new Date();
             window.getSelection();




         var year =
         if (
             now.getFullYear();
            !selection ||
             selection.rangeCount === 0
        ) {


            return null;
        }


        var month =
            String(
                now.getMonth() + 1
            ).padStart(
                2,
                '0'
            );


        var text =
            selection
                .toString()
                .trim();


        var day =
            String(
                now.getDate()
            ).padStart(
                2,
                '0'
            );


        if (!text) {
            return null;
        }


        return (
            year +
            '-' +
            month +
            '-' +
            day
        );
    }


        if (
            text.length <
            MIN_LENGTH
        ) {


    /* =====================================================
            return null;
      11. Clipboard
        }
      ===================================================== */


    function copyText(
        text,
        successMessage
    ) {


        /*
        * 现代浏览器
        */
         if (
         if (
             navigator.clipboard &&
             text.length >
             window.isSecureContext
             MAX_LENGTH
         ) {
         ) {


             navigator.clipboard
             return null;
                .writeText(text)
        }
                .then(


                    function () {


                        showMessage(
        var range =
                            successMessage
            selection.getRangeAt(0);
                        );


                    }


                )
        var container =
                .catch(
            range.commonAncestorContainer;


                    function () {


                        fallbackCopy(
        /*
                            text,
        * 如果是 Text Node,
                            successMessage
        * 取得它的父元素。
                        );
        */
        if (
            container.nodeType ===
            Node.TEXT_NODE
        ) {


                    }
            container =
                 );
                 container.parentElement;
        }




             return;
        if (!container) {
             return null;
         }
         }




         /*
         /*
         * 后备
         * 只允许从正文中选择。
        *
        * 使用 .mw-parser-output,
        * 不依赖 #mw-content-text,
        * 对不同 Skin 更兼容。
         */
         */
         fallbackCopy(
         var content =
            text,
            container.closest(
             successMessage
                '.mw-parser-output'
        );
             );
    }
 


        if (!content) {


    /* =====================================================
            return null;
      12. 备用复制
        }
      ===================================================== */


    function fallbackCopy(
        text,
        successMessage
    ) {


         var textarea =
         return {
            document.createElement(
                'textarea'
            );


            text:
                normalizeSelectedText(
                    text
                ),


        textarea.value =
            range:
            text;
                range.cloneRange()
        };
    }




        textarea.setAttribute(
    /* =====================================================
            'readonly',
      9. 清理选中文本
            ''
      ===================================================== */
        );


    function normalizeSelectedText(
        text
    ) {


         textarea.style.position =
         return text
            'fixed';


        textarea.style.left =
            .replace(
            '-9999px';
                /\u00a0/g,
                ' '
            )


        textarea.style.top =
            .replace(
            '0';
                /\r\n/g,
                '\n'
            )


            .replace(
                /[ \t]+\n/g,
                '\n'
            )


        document.body.appendChild(
            .replace(
             textarea
                /\n[ \t]+/g,
        );
                '\n'
             )


            .replace(
                /\n{3,}/g,
                '\n\n'
            )


        textarea.focus();
            .trim();
        textarea.select();
    }




        var success = false;
    /* =====================================================
      10. 生成最终引用文字
      ===================================================== */


    function buildCitation(
        shortUrl
    ) {


         try {
         var citationTitle =
            getCitationTitle();


            success =
                document.execCommand(
                    'copy'
                );


         } catch (error) {
         return (


             success = false;
             selectedText +


        }
            '\n\n' +


            '----------------------------------------' +


        document.body.removeChild(
             '\n\n' +
             textarea
        );


            '法谈摘自:Dhamma.cn 《' +
            citationTitle +
            '》' +


        if (success) {
            '\n\n' +


             showMessage(
             '该篇网址:' +
                successMessage
             shortUrl +
             );


        } else {
            '\n\n' +


             showMessage(
             '转载请保持原文,随喜分享,Sadhu!'
                '复制失败,请使用 Ctrl+C。'
        );
            );
        }
     }
     }




     /* =====================================================
     /* =====================================================
       13. 复制原文
       11. 复制到剪贴板
       ===================================================== */
       ===================================================== */


     function copyPlainText() {
     function copyText(
        text
    ) {


         if (!selectedText) {
        /*
             return;
        * 现代浏览器
        */
         if (
            navigator.clipboard &&
            window.isSecureContext
        ) {
 
             return navigator.clipboard.writeText(
                text
            );
         }
         }




         copyText(
         /*
             selectedText,
        * 后备方案
            '已复制选中的法谈内容'
        */
        return fallbackCopy(
             text
         );
         );
        hidePopup();
     }
     }




     /* =====================================================
     /* =====================================================
       14. 复制并附出处
       12. 后备复制
       ===================================================== */
       ===================================================== */


     function copyWithSource() {
     function fallbackCopy(
        text
    ) {


         if (!selectedText) {
         return new Promise(
             return;
             function (
        }
                resolve,
                reject
            ) {


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


        showMessage(
            '正在生成 Dhamma.cn 短网址……'
        );


                textarea.value =
                    text;


        getShortUrl()


            .then(
                textarea.setAttribute(
                    'readonly',
                    ''
                );


                function (shortUrl) {


                    var metadata =
                textarea.style.position =
                        getMetadata();
                    'fixed';


                textarea.style.left =
                    '-9999px';


                    var title =
                textarea.style.top =
                        formatTitle(
                    '0';
                            getPageTitle()
                        );


                textarea.style.opacity =
                    '0';


                    var output =
                        selectedText +
                        '\n\n' +


                        '----------------------------------------' +
                document.body.appendChild(
                        '\n' +
                    textarea
                );


                        '法谈摘自:' +
                        metadata.source +
                        ' ' +
                        title +
                        '\n' +


                        '该篇网址:' +
                textarea.focus();
                        shortUrl +
                        '\n' +


                        '转载请保持原文,随喜分享,Sadhu!';
                textarea.select();




                    copyText(
                try {
                        output,
                        '已复制法谈内容及出处'
                    );


                    var success =
                        document.execCommand(
                            'copy'
                        );


                    hidePopup();
                }
            )


            .catch(
                    document.body.removeChild(
                        textarea
                    );


                function (error) {


                     console.error(
                     if (success) {
                        'Dhamma.cn UrlShortener:',
                        error
                    );


                        resolve();


                     /*
                     } else {
                    * 短网址失败时,
                    * 不复制长 URL。
                    */
                    var metadata =
                        getMetadata();


 
                         reject(
                    var title =
                             new Error(
                         formatTitle(
                                '复制失败'
                             getPageTitle()
                            )
                         );
                         );
                    }




                    var output =
                } catch (error) {
                        selectedText +
                        '\n\n' +


                        '----------------------------------------' +
                    document.body.removeChild(
                        '\n' +
                         textarea
 
                        '法谈摘自:' +
                        metadata.source +
                        ' ' +
                        title +
                        '\n' +
 
                        '转载请保持原文,随喜分享,Sadhu!';
 
 
                    copyText(
                         output,
                        '短网址暂时无法生成,已复制出处'
                     );
                     );




                     hidePopup();
                     reject(error);
                 }
                 }
             );
 
             }
        );
     }
     }




     /* =====================================================
     /* =====================================================
       15. 学术脚注
       13. 复制并附出处
       ===================================================== */
       ===================================================== */


     function copyFootnote() {
     function copyWithSource() {


         if (!selectedText) {
         if (!selectedText) {
第951行: 第916行:




         showMessage(
         notify(
             '正在生成 Dhamma.cn 短网址……'
             '正在生成 Dhamma.cn 短网址……'
         );
         );
第959行: 第924行:


             .then(
             .then(
                function (
                    shortUrl
                ) {


                function (shortUrl) {
                    return copyText(
 
                         buildCitation(
                    var metadata =
                            shortUrl
                         getMetadata();
                         )
 
                     );
 
                }
                    var author =
            )
                         metadata.author ||
                        '隆波帕默尊者';
 
 
                    var source =
                        metadata.source ||
                        'Dhamma.cn';
 
 
                     var title =
                        getPageTitle();
 
 
                    var output =
                        author +
                        ':《' +
                        title +
                        '》,' +
                        source +
                        ',' +
                        shortUrl +
                        '(访问日期:' +
                        getAccessDate() +
                        ')。';


            .then(
                function () {


                     copyText(
                     notify(
                        output,
                         '已复制该段开示及出处'
                         '已复制学术脚注'
                     );
                     );


第1,004行: 第949行:


             .catch(
             .catch(
                 function (error) {
                 function (error) {


                     console.error(
                     console.error(
                         'Dhamma.cn UrlShortener:',
                         'Dhamma.cn 引用复制失败:',
                         error
                         error
                     );
                     );




                     var metadata =
                     /*
                        getMetadata();
                    * 短网址失败时,
 
                    * 不偷偷复制长 URL。
 
                    */
                    var author =
                     notify(
                        metadata.author ||
                         '短网址生成失败,请稍后再试'
                        '隆波帕默尊者';
 
 
                    var source =
                        metadata.source ||
                        'Dhamma.cn';
 
 
                    var title =
                        getPageTitle();
 
 
                    var output =
                        author +
                        ':《' +
                        title +
                        '》,' +
                        source +
                        '(访问日期:' +
                        getAccessDate() +
                        ')。';
 
 
                     copyText(
                        output,
                         '短网址生成失败,已复制脚注'
                     );
                     );
                    hidePopup();
                 }
                 }
             );
             );
第1,055行: 第970行:


     /* =====================================================
     /* =====================================================
       16. Popup 定位
       14. 显示 Popup
       ===================================================== */
       ===================================================== */


     function positionPopup(range) {
     function showPopup(
        range
    ) {


         if (
         createPopup();
            !popup ||
            !range
        ) {
            return;
        }




第1,076行: 第988行:
             rect.height === 0
             rect.height === 0
         ) {
         ) {
             return;
             return;
         }
         }
第1,084行: 第997行:




        /*
        * 必须先显示,
        * 才能取得实际宽高。
        */
         var popupRect =
         var popupRect =
             popup.getBoundingClientRect();
             popup.getBoundingClientRect();
第1,099行: 第1,016行:




         var margin = 10;
         var margin =
            10;




第1,108行: 第1,026行:
             left < margin
             left < margin
         ) {
         ) {
             left = margin;
 
             left =
                margin;
         }
         }


第1,130行: 第1,050行:


         /*
         /*
         * 底部空间不足
         * 如果下方空间不足,
        * 改到选区上方。
         */
         */
         if (
         if (
第1,147行: 第1,068行:


         /*
         /*
         * 顶部边界
         * 顶部
         */
         */
         if (
         if (
             top < margin
             top < margin
         ) {
         ) {
             top = margin;
 
             top =
                margin;
         }
         }


第1,160行: 第1,083行:
                 left +
                 left +
                 window.scrollX
                 window.scrollX
             ) + 'px';
             ) +
            'px';




第1,167行: 第1,091行:
                 top +
                 top +
                 window.scrollY
                 window.scrollY
             ) + 'px';
             ) +
    }
             'px';
 
 
    /* =====================================================
      17. 显示 Popup
      ===================================================== */
 
    function showPopup(range) {
 
        createPopup();
 
        popup.style.display =
             'block';
 
        positionPopup(range);
     }
     }




     /* =====================================================
     /* =====================================================
       18. 隐藏 Popup
       15. 隐藏 Popup
       ===================================================== */
       ===================================================== */


第1,203行: 第1,113行:


     /* =====================================================
     /* =====================================================
       19. MediaWiki 提示
       16. 鼠标松开
      ===================================================== */
 
    function showMessage(message) {
 
        if (
            typeof mw.notify ===
            'function'
        ) {
 
            mw.notify(
                message,
                {
                    type: 'info',
                    autoHide: true
                }
            );
 
        } else {
 
            console.log(
                message
            );
        }
    }
 
 
    /* =====================================================
      20. 鼠标松开
       ===================================================== */
       ===================================================== */


     function handleMouseUp() {
     function handleMouseUp() {


        /*
        * 延迟一点,
        * 等浏览器完成 Selection。
        */
         setTimeout(
         setTimeout(
             function () {
             function () {


第1,245行: 第1,130行:


                 if (!data) {
                 if (!data) {
                     return;
                     return;
                 }
                 }
第1,258行: 第1,144行:


             },
             },
 
             30
             20
         );
         );
     }
     }
第1,265行: 第1,150行:


     /* =====================================================
     /* =====================================================
       21. 点击页面其他位置
       17. 点击其他地方
       ===================================================== */
       ===================================================== */


第1,277行: 第1,162行:




        /*
        * 点击 Popup 内部不关闭。
        */
         if (
         if (
             popup.contains(
             popup.contains(
第1,282行: 第1,170行:
             )
             )
         ) {
         ) {
             return;
             return;
         }
         }
第1,291行: 第1,180行:


     /* =====================================================
     /* =====================================================
       22. ESC
       18. ESC
       ===================================================== */
       ===================================================== */


     function handleKeyDown(event) {
     function handleKeyDown(
        event
    ) {


         if (
         if (
第1,307行: 第1,198行:


     /* =====================================================
     /* =====================================================
       23. 页面滚动
       19. 提示
       ===================================================== */
       ===================================================== */


     function handleScroll() {
     function notify(
        message
    ) {
 
        if (
            typeof mw.notify ===
            'function'
        ) {
 
            mw.notify(
                message,
                {
                    type: 'info',
                    autoHide: true
                }
            );
 
        } else {


        hidePopup();
            console.log(
                message
            );
        }
     }
     }




     /* =====================================================
     /* =====================================================
       24. 初始化
       20. 初始化
       ===================================================== */
       ===================================================== */


第1,323行: 第1,234行:


         if (!shouldEnable()) {
         if (!shouldEnable()) {
             return;
             return;
         }
         }
第1,350行: 第1,262行:
         window.addEventListener(
         window.addEventListener(
             'scroll',
             'scroll',
             handleScroll,
             hidePopup,
             { passive: true }
             {
                passive: true
            }
         );
         );


第1,357行: 第1,271行:
         window.addEventListener(
         window.addEventListener(
             'resize',
             'resize',
             handleScroll
             hidePopup
        );
 
 
        console.log(
            'Dhamma QuoteCopy: initialized'
         );
         );
     }
     }
第1,363行: 第1,282行:


     /* =====================================================
     /* =====================================================
       25. 启动
       21. 确保 MediaWiki API 已加载
       ===================================================== */
       ===================================================== */


     if (
     mw.loader.using(
         document.readyState ===
         [
         'loading'
            'mediawiki.api'
     ) {
         ]
     ).then(
        function () {


        document.addEventListener(
             init();
            'DOMContentLoaded',
             init
        );


    } else {
        }
 
    );
        init();
 
    }


})();
})();

2026年9月19日 (六) 22:18的最新版本

/* =========================================================
   Dhamma.cn
   法谈划词引用

   MediaWiki 1.43

   功能:
   1. 在隆波帕默尊者法谈正文中选择文字
   2. 松开鼠标左键
   3. 弹出唯一按钮:
      “复制该段开示及出处”
   4. 调用 UrlShortener
   5. 自动生成短网址
   6. 复制完整引用

   示例:

   觉知不是控制心,而是如实地知道身心……

   ----------------------------------------

   法谈摘自:Dhamma.cn 《隆波帕默尊者开示:觉知的要领》

   该篇网址:https://dhamma.cn/s/x9K2

   转载请保持原文,随喜分享,Sadhu!
   ========================================================= */

(function () {

    'use strict';


    /* =====================================================
       基本设置
       ===================================================== */

    var MIN_LENGTH = 2;

    var MAX_LENGTH = 8000;

    var popup = null;

    var selectedText = '';

    /*
     * 当前页面短网址缓存
     *
     * 同一页面只调用一次 UrlShortener。
     */
    var pageShortUrl = null;

    var shortUrlPromise = null;


    /* =====================================================
       1. 判断是否启用
       ===================================================== */

    function shouldEnable() {

        /*
         * 只在 Main namespace
         */
        if (
            mw.config.get(
                'wgNamespaceNumber'
            ) !== 0
        ) {
            return false;
        }


        /*
         * 只在正常阅读页面
         */
        if (
            mw.config.get(
                'wgAction'
            ) !== 'view'
        ) {
            return false;
        }


        /*
         * 如果页面属于:
         *
         * 分类:隆波帕默尊者
         *
         * 自动启用。
         */
        var categories =
            mw.config.get(
                'wgCategories'
            ) || [];


        if (
            categories.indexOf(
                '隆波帕默尊者'
            ) !== -1
        ) {
            return true;
        }


        /*
         * 允许将来通过隐藏元数据
         * 手动启用。
         */
        if (
            document.getElementById(
                'dhamma-quote-metadata'
            )
        ) {
            return true;
        }


        return false;
    }


    /* =====================================================
       2. 创建 Popup
       ===================================================== */

    function createPopup() {

        if (popup) {
            return;
        }


        popup =
            document.createElement(
                'div'
            );


        popup.id =
            'dhamma-quote-popup';


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


        popup.setAttribute(
            'aria-label',
            '复制该段开示及出处'
        );


        /*
         * 只有一个按钮。
         */
        popup.innerHTML =

            '<button ' +

                'type="button" ' +

                'class="dhamma-quote-button" ' +

                'data-action="source">' +

                '复制该段开示及出处' +

            '</button>';


        document.body.appendChild(
            popup
        );


        /*
         * 点击按钮时,
         * 不让 Selection 因失焦消失。
         */
        popup.addEventListener(
            'mousedown',
            function (event) {

                event.preventDefault();

            }
        );


        /*
         * 点击按钮
         */
        popup.addEventListener(
            'click',
            function (event) {

                var button =
                    event.target.closest(
                        '.dhamma-quote-button'
                    );


                if (!button) {
                    return;
                }


                copyWithSource();

            }
        );
    }


    /* =====================================================
       3. 获取当前页面标题
       ===================================================== */

    function getRawPageTitle() {

        /*
         * MediaWiki 当前页面标题
         */
        var title =
            mw.config.get(
                'wgTitle'
            );


        if (title) {
            return title.trim();
        }


        /*
         * 后备方案
         */
        var heading =
            document.querySelector(
                '#firstHeading, .mw-page-title-main'
            );


        if (heading) {

            return heading
                .textContent
                .trim();
        }


        return document.title
            .replace(
                /\s*-\s*.*$/,
                ''
            )
            .trim();
    }


    /* =====================================================
       4. 获取引用标题
       ===================================================== */

    function getCitationTitle() {

        /*
         * 如果页面存在明确的引用 metadata,
         * 优先使用。
         */
        var metadata =
            document.getElementById(
                'dhamma-quote-metadata'
            );


        if (metadata) {

            var customTitle =
                metadata.getAttribute(
                    'data-title'
                );


            if (
                customTitle &&
                customTitle.trim()
            ) {

                return normalizeCitationTitle(
                    customTitle
                );
            }
        }


        /*
         * 自动从页面标题判断。
         */
        var title =
            getRawPageTitle();


        return normalizeCitationTitle(
            title
        );
    }


    /* =====================================================
       5. 标题清理
       ===================================================== */

    function normalizeCitationTitle(
        title
    ) {

        if (!title) {
            return '隆波帕默尊者开示';
        }


        title =
            title.trim();


        /*
         * MediaWiki 页面标题中的下划线
         */
        title =
            title.replace(
                /_/g,
                ' '
            );


        /*
         * -------------------------------------------------
         * 情况一:
         *
         *  《觉知的要领》-隆波帕默尊者-2026年...
         *
         * 提取第一个《...》
         * -------------------------------------------------
         */

        var bookMatch =
            title.match(
                /《([^》]+)》/
            );


        if (bookMatch) {

            title =
                bookMatch[1].trim();
        }


        /*
         * -------------------------------------------------
         * 情况二:
         *
         * 隆波帕默尊者开示:觉知的要领
         * -------------------------------------------------
         */

        if (
            title.indexOf(
                '隆波帕默尊者开示:'
            ) === 0
        ) {

            return title;
        }


        /*
         * -------------------------------------------------
         * 去掉常见前缀
         *
         * D24.91
         * D24.91-
         * D24.91:
         * -------------------------------------------------
         */

        title =
            title.replace(
                /^D\d+(?:\.\d+)?[\s::._-]*/,
                ''
            );


        /*
         * 去除“隆波帕默尊者”后缀
         */
        title =
            title.replace(
                /[-–—_\s]*隆波帕默尊者.*$/u,
                ''
            );


        /*
         * 去除常见日期后缀
         *
         * 例如:
         * -2024年10月20日
         * 2024年10月20日
         */
        title =
            title.replace(
                /[-–—_\s]*\d{4}年\d{1,2}月\d{1,2}日.*$/u,
                ''
            );


        /*
         * 去除多余的连接符
         */
        title =
            title.replace(
                /^[-–—_\s]+|[-–—_\s]+$/g,
                ''
            );


        title =
            title.trim();


        /*
         * 如果清理后为空,
         * 使用原页面标题。
         */
        if (!title) {

            title =
                getRawPageTitle();
        }


        /*
         * 最终统一成:
         *
         * 隆波帕默尊者开示:XXXX
         */
        return (
            '隆波帕默尊者开示:' +
            title
        );
    }


    /* =====================================================
       6. 获取规范 URL
       ===================================================== */

    function getCanonicalUrl() {

        var canonical =
            mw.config.get(
                'wgCanonicalUrl'
            );


        if (canonical) {
            return canonical;
        }


        /*
         * 后备方案
         */
        var pageName =
            mw.config.get(
                'wgPageName'
            );


        if (
            pageName &&
            mw.util &&
            mw.util.getUrl
        ) {

            return mw.util.getUrl(
                pageName
            );
        }


        return window.location.href;
    }


    /* =====================================================
       7. 获取短网址
       ===================================================== */

    function getShortUrl() {

        /*
         * 当前页面已经有短网址
         */
        if (pageShortUrl) {

            return Promise.resolve(
                pageShortUrl
            );
        }


        /*
         * 已经正在请求
         */
        if (shortUrlPromise) {

            return shortUrlPromise;
        }


        /*
         * 调用 MediaWiki UrlShortener
         */
        shortUrlPromise =

            new mw.Api().post({

                action:
                    'shortenurl',

                url:
                    getCanonicalUrl()

            })


            .then(
                function (data) {

                    /*
                     * UrlShortener API
                     *
                     * {
                     *   shortenurl: {
                     *      shorturl: "..."
                     *   }
                     * }
                     */
                    if (
                        data &&
                        data.shortenurl &&
                        data.shortenurl.shorturl
                    ) {

                        pageShortUrl =
                            data.shortenurl.shorturl;


                        return pageShortUrl;
                    }


                    throw new Error(
                        'UrlShortener 未返回 shorturl'
                    );
                }
            )


            .catch(
                function (error) {

                    /*
                     * 允许以后重新请求
                     */
                    shortUrlPromise =
                        null;


                    throw error;
                }
            );


        return shortUrlPromise;
    }


    /* =====================================================
       8. 获取选中文字
       ===================================================== */

    function getSelectionData() {

        var selection =
            window.getSelection();


        if (
            !selection ||
            selection.rangeCount === 0
        ) {

            return null;
        }


        var text =
            selection
                .toString()
                .trim();


        if (!text) {
            return null;
        }


        if (
            text.length <
            MIN_LENGTH
        ) {

            return null;
        }


        if (
            text.length >
            MAX_LENGTH
        ) {

            return null;
        }


        var range =
            selection.getRangeAt(0);


        var container =
            range.commonAncestorContainer;


        /*
         * 如果是 Text Node,
         * 取得它的父元素。
         */
        if (
            container.nodeType ===
            Node.TEXT_NODE
        ) {

            container =
                container.parentElement;
        }


        if (!container) {
            return null;
        }


        /*
         * 只允许从正文中选择。
         *
         * 使用 .mw-parser-output,
         * 不依赖 #mw-content-text,
         * 对不同 Skin 更兼容。
         */
        var content =
            container.closest(
                '.mw-parser-output'
            );


        if (!content) {

            return null;
        }


        return {

            text:
                normalizeSelectedText(
                    text
                ),

            range:
                range.cloneRange()
        };
    }


    /* =====================================================
       9. 清理选中文本
       ===================================================== */

    function normalizeSelectedText(
        text
    ) {

        return text

            .replace(
                /\u00a0/g,
                ' '
            )

            .replace(
                /\r\n/g,
                '\n'
            )

            .replace(
                /[ \t]+\n/g,
                '\n'
            )

            .replace(
                /\n[ \t]+/g,
                '\n'
            )

            .replace(
                /\n{3,}/g,
                '\n\n'
            )

            .trim();
    }


    /* =====================================================
       10. 生成最终引用文字
       ===================================================== */

    function buildCitation(
        shortUrl
    ) {

        var citationTitle =
            getCitationTitle();


        return (

            selectedText +

            '\n\n' +

            '----------------------------------------' +

            '\n\n' +

            '法谈摘自:Dhamma.cn 《' +
            citationTitle +
            '》' +

            '\n\n' +

            '该篇网址:' +
            shortUrl +

            '\n\n' +

            '转载请保持原文,随喜分享,Sadhu!'
        );
    }


    /* =====================================================
       11. 复制到剪贴板
       ===================================================== */

    function copyText(
        text
    ) {

        /*
         * 现代浏览器
         */
        if (
            navigator.clipboard &&
            window.isSecureContext
        ) {

            return navigator.clipboard.writeText(
                text
            );
        }


        /*
         * 后备方案
         */
        return fallbackCopy(
            text
        );
    }


    /* =====================================================
       12. 后备复制
       ===================================================== */

    function fallbackCopy(
        text
    ) {

        return new Promise(
            function (
                resolve,
                reject
            ) {

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


                textarea.value =
                    text;


                textarea.setAttribute(
                    'readonly',
                    ''
                );


                textarea.style.position =
                    'fixed';

                textarea.style.left =
                    '-9999px';

                textarea.style.top =
                    '0';

                textarea.style.opacity =
                    '0';


                document.body.appendChild(
                    textarea
                );


                textarea.focus();

                textarea.select();


                try {

                    var success =
                        document.execCommand(
                            'copy'
                        );


                    document.body.removeChild(
                        textarea
                    );


                    if (success) {

                        resolve();

                    } else {

                        reject(
                            new Error(
                                '复制失败'
                            )
                        );
                    }


                } catch (error) {

                    document.body.removeChild(
                        textarea
                    );


                    reject(error);
                }

            }
        );
    }


    /* =====================================================
       13. 复制并附出处
       ===================================================== */

    function copyWithSource() {

        if (!selectedText) {
            return;
        }


        notify(
            '正在生成 Dhamma.cn 短网址……'
        );


        getShortUrl()

            .then(
                function (
                    shortUrl
                ) {

                    return copyText(
                        buildCitation(
                            shortUrl
                        )
                    );
                }
            )

            .then(
                function () {

                    notify(
                        '已复制该段开示及出处'
                    );


                    hidePopup();
                }
            )

            .catch(
                function (error) {

                    console.error(
                        'Dhamma.cn 引用复制失败:',
                        error
                    );


                    /*
                     * 短网址失败时,
                     * 不偷偷复制长 URL。
                     */
                    notify(
                        '短网址生成失败,请稍后再试'
                    );
                }
            );
    }


    /* =====================================================
       14. 显示 Popup
       ===================================================== */

    function showPopup(
        range
    ) {

        createPopup();


        var rect =
            range.getBoundingClientRect();


        if (
            rect.width === 0 &&
            rect.height === 0
        ) {

            return;
        }


        popup.style.display =
            'block';


        /*
         * 必须先显示,
         * 才能取得实际宽高。
         */
        var popupRect =
            popup.getBoundingClientRect();


        var left =
            rect.left +
            rect.width / 2 -
            popupRect.width / 2;


        var top =
            rect.bottom +
            10;


        var margin =
            10;


        /*
         * 左边界
         */
        if (
            left < margin
        ) {

            left =
                margin;
        }


        /*
         * 右边界
         */
        if (
            left +
            popupRect.width >
            window.innerWidth -
            margin
        ) {

            left =
                window.innerWidth -
                popupRect.width -
                margin;
        }


        /*
         * 如果下方空间不足,
         * 改到选区上方。
         */
        if (
            top +
            popupRect.height >
            window.innerHeight -
            margin
        ) {

            top =
                rect.top -
                popupRect.height -
                10;
        }


        /*
         * 顶部
         */
        if (
            top < margin
        ) {

            top =
                margin;
        }


        popup.style.left =
            Math.round(
                left +
                window.scrollX
            ) +
            'px';


        popup.style.top =
            Math.round(
                top +
                window.scrollY
            ) +
            'px';
    }


    /* =====================================================
       15. 隐藏 Popup
       ===================================================== */

    function hidePopup() {

        if (!popup) {
            return;
        }


        popup.style.display =
            'none';
    }


    /* =====================================================
       16. 鼠标松开
       ===================================================== */

    function handleMouseUp() {

        /*
         * 延迟一点,
         * 等浏览器完成 Selection。
         */
        setTimeout(
            function () {

                var data =
                    getSelectionData();


                if (!data) {

                    return;
                }


                selectedText =
                    data.text;


                showPopup(
                    data.range
                );

            },
            30
        );
    }


    /* =====================================================
       17. 点击其他地方
       ===================================================== */

    function handleDocumentMouseDown(
        event
    ) {

        if (!popup) {
            return;
        }


        /*
         * 点击 Popup 内部不关闭。
         */
        if (
            popup.contains(
                event.target
            )
        ) {

            return;
        }


        hidePopup();
    }


    /* =====================================================
       18. ESC
       ===================================================== */

    function handleKeyDown(
        event
    ) {

        if (
            event.key ===
            'Escape'
        ) {

            hidePopup();
        }
    }


    /* =====================================================
       19. 提示
       ===================================================== */

    function notify(
        message
    ) {

        if (
            typeof mw.notify ===
            'function'
        ) {

            mw.notify(
                message,
                {
                    type: 'info',
                    autoHide: true
                }
            );

        } else {

            console.log(
                message
            );
        }
    }


    /* =====================================================
       20. 初始化
       ===================================================== */

    function init() {

        if (!shouldEnable()) {

            return;
        }


        createPopup();


        document.addEventListener(
            'mouseup',
            handleMouseUp
        );


        document.addEventListener(
            'mousedown',
            handleDocumentMouseDown
        );


        document.addEventListener(
            'keydown',
            handleKeyDown
        );


        window.addEventListener(
            'scroll',
            hidePopup,
            {
                passive: true
            }
        );


        window.addEventListener(
            'resize',
            hidePopup
        );


        console.log(
            'Dhamma QuoteCopy: initialized'
        );
    }


    /* =====================================================
       21. 确保 MediaWiki API 已加载
       ===================================================== */

    mw.loader.using(
        [
            'mediawiki.api'
        ]
    ).then(
        function () {

            init();

        }
    );

})();