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

MediaWiki:QuoteCopy.js

来自Dhamma.cn

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
  • Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/* =========================================================
   Dhamma.cn
   QuoteCopy.js

   功能:
   选中文字
       ↓
   松开鼠标
       ↓
   出现引用工具
       ↓
   复制原文 / 复制出处 / 复制学术脚注

   MediaWiki 1.43
   使用现有 UrlShortener
   ========================================================= */

(function () {
    'use strict';


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

    var MIN_LENGTH = 8;
    var MAX_LENGTH = 8000;

    var popup = null;

    var selectedText = '';

    /*
     * 当前页面短网址。
     *
     * 同一个页面只请求一次。
     */
    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;
        }


        /*
         * 如果明确存在引用元数据,
         * 启用。
         */
        if (
            document.getElementById(
                'dhamma-quote-metadata'
            )
        ) {
            return true;
        }


        /*
         * 如果属于:
         *
         * 分类:隆波帕默尊者
         *
         * 自动启用。
         */
        var categories =
            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') || [];


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


        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 (
            title.charAt(0) === '《' &&
            title.charAt(
                title.length - 1
            ) === '》'
        ) {
            return title;
        }


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


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

    function createPopup() {

        if (popup) {
            return popup;
        }


        popup =
            document.createElement('div');

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


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


        popup.setAttribute(
            'aria-label',
            'Dhamma.cn 引用工具'
        );


        popup.innerHTML =

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

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

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

                    '<button type="button" ' +
                        '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" ' +
                        'class="dhamma-quote-button" ' +
                        'data-action="footnote">' +
                        '复制学术脚注' +
                    '</button>' +

                '</div>' +

            '</div>';


        document.body.appendChild(
            popup
        );


        /*
         * 不让点击 Popup 时
         * 改变当前选区。
         */
        popup.addEventListener(
            'mousedown',
            function (event) {

                event.preventDefault();

            }
        );


        /*
         * 按钮事件
         */
        popup.addEventListener(
            'click',
            function (event) {

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


                if (!button) {
                    return;
                }


                var action =
                    button.getAttribute(
                        'data-action'
                    );


                if (
                    action === 'copy'
                ) {
                    copyPlainText();
                }


                if (
                    action === 'source'
                ) {
                    copyWithSource();
                }


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

            }
        );


        return popup;
    }


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

    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;


        if (
            container.nodeType ===
            Node.TEXT_NODE
        ) {
            container =
                container.parentElement;
        }


        if (!container) {
            return null;
        }


        /*
         * 必须来自正文。
         */
        var content =
            container.closest(
                '#mw-content-text .mw-parser-output'
            );


        if (!content) {
            return null;
        }


        return {
            text: normalizeText(text),
            range: range.cloneRange()
        };
    }


    /* =====================================================
       7. 清理选中文字
       ===================================================== */

    function normalizeText(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();
    }


    /* =====================================================
       8. 获取 Canonical URL
       ===================================================== */

    function getCanonicalUrl() {

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


        if (url) {
            return url;
        }


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


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

            return mw.util.getUrl(
                pageName
            );
        }


        return window.location.href;
    }


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

    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) {

                    if (
                        data &&
                        data.shortenurl &&
                        data.shortenurl.shorturl
                    ) {

                        pageShortUrl =
                            data.shortenurl.shorturl;


                        return pageShortUrl;
                    }


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

            ).catch(

                function (error) {

                    shortUrlPromise =
                        null;

                    throw error;
                }
            );


        return shortUrlPromise;
    }


    /* =====================================================
       10. 获取日期
       ===================================================== */

    function getAccessDate() {

        var now =
            new Date();


        var year =
            now.getFullYear();


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


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


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


    /* =====================================================
       11. Clipboard
       ===================================================== */

    function copyText(
        text,
        successMessage
    ) {

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

            navigator.clipboard
                .writeText(text)
                .then(

                    function () {

                        showMessage(
                            successMessage
                        );

                    }

                )
                .catch(

                    function () {

                        fallbackCopy(
                            text,
                            successMessage
                        );

                    }
                );


            return;
        }


        /*
         * 后备
         */
        fallbackCopy(
            text,
            successMessage
        );
    }


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

    function fallbackCopy(
        text,
        successMessage
    ) {

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


        textarea.value =
            text;


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


        textarea.style.position =
            'fixed';

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

        textarea.style.top =
            '0';


        document.body.appendChild(
            textarea
        );


        textarea.focus();
        textarea.select();


        var success = false;


        try {

            success =
                document.execCommand(
                    'copy'
                );

        } catch (error) {

            success = false;

        }


        document.body.removeChild(
            textarea
        );


        if (success) {

            showMessage(
                successMessage
            );

        } else {

            showMessage(
                '复制失败,请使用 Ctrl+C。'
            );
        }
    }


    /* =====================================================
       13. 复制原文
       ===================================================== */

    function copyPlainText() {

        if (!selectedText) {
            return;
        }


        copyText(
            selectedText,
            '已复制选中的法谈内容'
        );


        hidePopup();
    }


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

    function copyWithSource() {

        if (!selectedText) {
            return;
        }


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


        getShortUrl()

            .then(

                function (shortUrl) {

                    var metadata =
                        getMetadata();


                    var title =
                        formatTitle(
                            getPageTitle()
                        );


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

                        '----------------------------------------' +
                        '\n' +

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

                        '该篇网址:' +
                        shortUrl +
                        '\n' +

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


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


                    hidePopup();
                }
            )

            .catch(

                function (error) {

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


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


                    var title =
                        formatTitle(
                            getPageTitle()
                        );


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

                        '----------------------------------------' +
                        '\n' +

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

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


                    copyText(
                        output,
                        '短网址暂时无法生成,已复制出处'
                    );


                    hidePopup();
                }
            );
    }


    /* =====================================================
       15. 学术脚注
       ===================================================== */

    function copyFootnote() {

        if (!selectedText) {
            return;
        }


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


        getShortUrl()

            .then(

                function (shortUrl) {

                    var metadata =
                        getMetadata();


                    var author =
                        metadata.author ||
                        '隆波帕默尊者';


                    var source =
                        metadata.source ||
                        'Dhamma.cn';


                    var title =
                        getPageTitle();


                    var output =
                        author +
                        ':《' +
                        title +
                        '》,' +
                        source +
                        ',' +
                        shortUrl +
                        '(访问日期:' +
                        getAccessDate() +
                        ')。';


                    copyText(
                        output,
                        '已复制学术脚注'
                    );


                    hidePopup();
                }
            )

            .catch(

                function (error) {

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


                    var metadata =
                        getMetadata();


                    var author =
                        metadata.author ||
                        '隆波帕默尊者';


                    var source =
                        metadata.source ||
                        'Dhamma.cn';


                    var title =
                        getPageTitle();


                    var output =
                        author +
                        ':《' +
                        title +
                        '》,' +
                        source +
                        '(访问日期:' +
                        getAccessDate() +
                        ')。';


                    copyText(
                        output,
                        '短网址生成失败,已复制脚注'
                    );


                    hidePopup();
                }
            );
    }


    /* =====================================================
       16. Popup 定位
       ===================================================== */

    function positionPopup(range) {

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


        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';
    }


    /* =====================================================
       17. 显示 Popup
       ===================================================== */

    function showPopup(range) {

        createPopup();

        popup.style.display =
            'block';

        positionPopup(range);
    }


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

    function hidePopup() {

        if (!popup) {
            return;
        }


        popup.style.display =
            'none';
    }


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

    function showMessage(message) {

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

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

        } else {

            console.log(
                message
            );
        }
    }


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

    function handleMouseUp() {

        setTimeout(

            function () {

                var data =
                    getSelectionData();


                if (!data) {
                    return;
                }


                selectedText =
                    data.text;


                showPopup(
                    data.range
                );

            },

            20
        );
    }


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

    function handleDocumentMouseDown(
        event
    ) {

        if (!popup) {
            return;
        }


        if (
            popup.contains(
                event.target
            )
        ) {
            return;
        }


        hidePopup();
    }


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

    function handleKeyDown(event) {

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

            hidePopup();
        }
    }


    /* =====================================================
       23. 页面滚动
       ===================================================== */

    function handleScroll() {

        hidePopup();
    }


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

    function init() {

        if (!shouldEnable()) {
            return;
        }


        createPopup();


        document.addEventListener(
            'mouseup',
            handleMouseUp
        );


        document.addEventListener(
            'mousedown',
            handleDocumentMouseDown
        );


        document.addEventListener(
            'keydown',
            handleKeyDown
        );


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


        window.addEventListener(
            'resize',
            handleScroll
        );
    }


    /* =====================================================
       25. 启动
       ===================================================== */

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

        document.addEventListener(
            'DOMContentLoaded',
            init
        );

    } else {

        init();

    }

})();