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

MediaWiki:Common.js:修订间差异

来自Dhamma.cn
法藏​(留言 | 贡献)
小无编辑摘要
法藏​(留言 | 贡献)
小无编辑摘要
 
(未显示同一用户的13个中间版本)
第1行: 第1行:
/* =========================================================
  Dhamma.cn 划词复制开示及 UrlShortener 短网址落款(移动端遮挡修复版)
  适用于:MediaWiki:Common.js
  ========================================================= */
(function () {
(function () {
     if (window.dhammaCopyLoaded) return;
     if (window.dhammaCopyLoaded) return;
     window.dhammaCopyLoaded = true;
     window.dhammaCopyLoaded = true;


     var cachedShortUrl = '';
     function initDhammaCopy() {
        var cachedShortUrl = '';


    // 异步通过 POST 方式向 UrlShortener API 请求真正的 /s/ 短网址
        // 判断是否为移动端 / 触摸设备
    function getShortUrlAsync() {
        function isMobileDevice() {
        if (cachedShortUrl) return Promise.resolve(cachedShortUrl);
            return ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (window.innerWidth <= 768);
        }


         // 1. 优先在 DOM 中查找侧边栏或页面现成的 /s/ 短链接
         // 1. 统一域名规范化:剥离 www 及所有二级域名(如 reg.dhamma.cn),强转为 https://dhamma.cn
         var sLink = document.querySelector('a[href*="/s/"]');
         function forceMainDomain(url) {
        if (sLink && sLink.href && sLink.href.indexOf('/s/') !== -1) {
            if (!url) return 'https://dhamma.cn';
             cachedShortUrl = sLink.href;
            if (url.indexOf('http') !== 0) {
             return Promise.resolve(cachedShortUrl);
                return 'https://dhamma.cn' + (url.indexOf('/') === 0 ? '' : '/') + url;
             }
             return url.replace(/^https?:\/\/[^\/]+/, 'https://dhamma.cn');
         }
         }


         var tShort = document.querySelector('#t-shorturl a');
         // 2. 异步获取真正的当前页面短网址
         if (tShort && tShort.href && tShort.href.indexOf('/s/') !== -1) {
         function getShortUrlAsync() {
             cachedShortUrl = tShort.href;
             if (cachedShortUrl) return Promise.resolve(cachedShortUrl);
            return Promise.resolve(cachedShortUrl);
        }


        // 2. 构建干净的页面原生 URL(彻底剥离 &oldid=12715 等历史版本参数)
            // 仅在侧边栏/工具栏查找官方短网址,严禁扫描正文(#bodyContent)内的嵌入链接
        var pageName = (window.mw && mw.config && mw.config.get('wgPageName')) || '';
            var tShort = document.querySelector('#t-shorturl a, #p-tb a[href*="/s/"], .mw-portlet-tb a[href*="/s/"], li[id*="shorturl"] a');
        var scriptPath = (window.mw && mw.config && mw.config.get('wgScriptPath')) || '';
            if (tShort && tShort.href && tShort.href.indexOf('/s/') !== -1) {
        var targetUrl = '';
                cachedShortUrl = forceMainDomain(tShort.href);
       
                return Promise.resolve(cachedShortUrl);
        if (pageName) {
            }
            targetUrl = window.location.origin + scriptPath + '/index.php?title=' + pageName;
        } else {
            targetUrl = window.location.href.split('?')[0].split('#')[0];
        }


        // 3. MediaWiki 规范:action=shortenurl 必须以 POST 提交
            // 获取页面规范网址
        var apiUrl = scriptPath + '/api.php';
            var canonicalEl = document.querySelector('link[rel="canonical"]');
        var params = new URLSearchParams();
            var targetUrl = canonicalEl ? canonicalEl.href : window.location.href;
        params.append('action', 'shortenurl');
        params.append('format', 'json');
        params.append('url', targetUrl);


        return fetch(apiUrl, {
             // 优先使用 MediaWiki 原生 Api 模块(带 CSRF Token)
             method: 'POST',
             if (window.mw && mw.Api) {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                return new mw.Api().post({
             body: params.toString()
                    action: 'shortenurl',
        })
                    url: targetUrl
        .then(function (res) { return res.json(); })
                }).then(function (data) {
        .then(function (data) {
                    if (data && data.shortenurl && data.shortenurl.shorturl) {
            if (data && data.shortenurl && data.shortenurl.shorturl) {
                        cachedShortUrl = forceMainDomain(data.shortenurl.shorturl);
                cachedShortUrl = data.shortenurl.shorturl;
                    } else {
                        cachedShortUrl = forceMainDomain(targetUrl);
                    }
                    return cachedShortUrl;
                }).catch(function () {
                    return fallbackFetchApi(targetUrl);
                });
             } else {
             } else {
                 cachedShortUrl = targetUrl;
                 return fallbackFetchApi(targetUrl);
             }
             }
            return cachedShortUrl;
        }
         })
 
         .catch(function () {
         // 降级 Fetch 请求(带 CSRF Token)
             cachedShortUrl = targetUrl;
         function fallbackFetchApi(targetUrl) {
             return cachedShortUrl;
             var scriptPath = (window.mw && mw.config && mw.config.get('wgScriptPath')) || '';
        });
             var apiUrl = scriptPath + '/api.php';
    }
            var token = (window.mw && mw.user && mw.user.tokens && mw.user.tokens.get('csrfToken')) || '+\\';


    // 页面加载时后台静默预取短网址
            var params = new URLSearchParams();
    getShortUrlAsync();
            params.append('action', 'shortenurl');
            params.append('format', 'json');
            params.append('url', targetUrl);
            params.append('token', token);


    // 创建浮窗节点
            return fetch(apiUrl, {
    function getPopover() {
                method: 'POST',
        var pop = document.getElementById('dhamma-copy-popover');
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        if (!pop) {
                body: params.toString()
            pop = document.createElement('div');
            })
             pop.id = 'dhamma-copy-popover';
            .then(function (res) { return res.json(); })
            pop.style.cssText = 'display:none; position:fixed; z-index:2147483647; background:#ffffff; border:1px solid rgba(0,0,0,0.18); border-radius:8px; box-shadow:0 6px 20px rgba(0,0,0,0.25); padding:6px; font-size:13px; line-height:1.4; white-space:nowrap;';
             .then(function (data) {
           
                if (data && data.shortenurl && data.shortenurl.shorturl) {
            var btn = document.createElement('button');
                    cachedShortUrl = forceMainDomain(data.shortenurl.shorturl);
            btn.id = 'dhamma-copy-btn';
                } else {
             btn.innerText = '复制该段开示(带出处)';
                    cachedShortUrl = forceMainDomain(targetUrl);
             btn.style.cssText = 'background:#4d6b57; color:#ffffff; border:none; border-radius:5px; padding:6px 14px; font-size:13px; cursor:pointer; font-weight:600; font-family:sans-serif;';
                }
           
                return cachedShortUrl;
            pop.appendChild(btn);
             })
             document.body.appendChild(pop);
             .catch(function () {
                cachedShortUrl = forceMainDomain(targetUrl);
                return cachedShortUrl;
             });
         }
         }
        return pop;
    }


    var currentText = '';
        // 静默预取短网址
        getShortUrlAsync();


    function handleSelection() {
        // 3. 创建/获取浮窗节点
        var popover = getPopover();
        function getPopover() {
        var btn = document.getElementById('dhamma-copy-btn');
            var pop = document.getElementById('dhamma-copy-popover');
        var sel = window.getSelection();
            if (!pop) {
        var text = sel ? sel.toString().trim() : '';
                pop = document.createElement('div');
                pop.id = 'dhamma-copy-popover';
                pop.style.cssText = 'display:none; position:fixed; z-index:2147483647; background:#ffffff; border:1px solid rgba(0,0,0,0.15); border-radius:30px; box-shadow:0 8px 24px rgba(0,0,0,0.22); padding:6px 8px; font-size:14px; white-space:nowrap; transition:bottom 0.2s ease, opacity 0.2s ease;';
               
                var btn = document.createElement('button');
                btn.id = 'dhamma-copy-btn';
                btn.innerText = ' 复制该段开示(含出处)';
                btn.style.cssText = 'background:#4d6b57; color:#ffffff; border:none; border-radius:20px; padding:8px 18px; font-size:14px; cursor:pointer; font-weight:600; font-family:sans-serif; -webkit-tap-highlight-color:transparent;';
               
                btn.onmouseover = function () { if (this.innerText.indexOf('✅') === -1) this.style.background = '#3b5343'; };
                btn.onmouseout = function () { if (this.innerText.indexOf('✅') === -1) this.style.background = '#4d6b57'; };


        if (!text || text.length < 2) {
                pop.appendChild(btn);
            popover.style.display = 'none';
                document.body.appendChild(pop);
             return;
            }
             return pop;
         }
         }


         currentText = text;
         var currentText = '';


         if (sel.rangeCount > 0) {
         // 4. 处理划词选择逻辑(响应式适配 PC / 移动端定位)
             var range = sel.getRangeAt(0);
        function handleSelection() {
             var rect = range.getBoundingClientRect();
             var popover = getPopover();
            var btn = document.getElementById('dhamma-copy-btn');
            var sel = window.getSelection();
             var text = sel ? sel.toString().trim() : '';


             if (rect.width === 0 || rect.height === 0) {
             if (!text || text.length < 2) {
                 popover.style.display = 'none';
                 popover.style.display = 'none';
                 return;
                 return;
             }
             }


             var popWidth = popover.offsetWidth || 190;
             currentText = text;
            var popHeight = popover.offsetHeight || 38;


             var top = rect.top - popHeight - 10;
             if (sel.rangeCount > 0) {
            var left = rect.left + (rect.width / 2) - (popWidth / 2);
                var isMobile = isMobileDevice();


            if (left < 10) left = 10;
                if (isMobile) {
            if (top < 10) top = rect.bottom + 10;
                    // 📱 移动端解决遮挡的核心方案:固定在屏幕底部中央,并兼容 iPhone 底部安全区
                    popover.style.top = 'auto';
                    popover.style.bottom = 'calc(25px + env(safe-area-inset-bottom, 0px))';
                    popover.style.left = '50%';
                    popover.style.transform = 'translateX(-50%)';
                    popover.style.display = 'block';
                } else {
                    // 💻 PC 端:跟随选中区域在上方居中弹出
                    var range = sel.getRangeAt(0);
                    var rect = range.getBoundingClientRect();


            popover.style.top = top + 'px';
                    if (rect.width === 0 || rect.height === 0) {
            popover.style.left = left + 'px';
                        popover.style.display = 'none';
            popover.style.display = 'block';
                        return;
                    }
 
                    var popWidth = popover.offsetWidth || 200;
                    var popHeight = popover.offsetHeight || 40;
 
                    var top = rect.top - popHeight - 10;
                    var left = rect.left + (rect.width / 2) - (popWidth / 2);


            btn.onclick = function (e) {
                    if (left < 10) left = 10;
                e.stopPropagation();
                    if (top < 10) top = rect.bottom + 10;
                e.preventDefault();


                getShortUrlAsync().then(function (shortUrl) {
                    popover.style.bottom = 'auto';
                     var title = (window.mw && mw.config && mw.config.get('wgTitle')) || document.title;
                     popover.style.transform = 'none';
                     var site = (window.mw && mw.config && mw.config.get('wgSiteName')) || 'Dhamma.cn';
                    popover.style.top = top + 'px';
                     popover.style.left = left + 'px';
                    popover.style.display = 'block';
                }


                // 格式化输出:摘自、标题、地址、声明各独占一行
                btn.onclick = function (e) {
                     var output =
                     e.stopPropagation();
                        '“' + currentText + '”\n\n' +
                    e.preventDefault();
                        '----------------------------------------\n' +
                        '摘自:' + site + '\n' +
                        '标题:' + title + '\n' +
                        '地址:' + shortUrl + '\n' +
                        '转载请保持原文,随喜分享,Sadhu!';


                     if (navigator.clipboard && navigator.clipboard.writeText) {
                     getShortUrlAsync().then(function (shortUrl) {
                        navigator.clipboard.writeText(output).then(function () {
                        var title = (window.mw && mw.config && mw.config.get('wgTitle')) || document.title;
                            showSuccess(btn, popover);
                        var site = (window.mw && mw.config && mw.config.get('wgSiteName')) || 'Dhamma.cn';
                        }).catch(function () {
 
                        var output =
                            '“' + currentText + '”\n' +
                            '---------------------\n' +
                            '来源:' + site + '\n' +
                            '标题:' + title + '\n' +
                            '地址:' + shortUrl + '\n\n' +
                            '转载请保持原文,随喜分享,Sadhu!';
 
                        if (navigator.clipboard && navigator.clipboard.writeText) {
                            navigator.clipboard.writeText(output).then(function () {
                                showSuccess(btn, popover);
                            }).catch(function () {
                                fallbackCopy(output, btn, popover);
                            });
                        } else {
                             fallbackCopy(output, btn, popover);
                             fallbackCopy(output, btn, popover);
                         });
                         }
                    } else {
                    });
                        fallbackCopy(output, btn, popover);
                };
                    }
            }
                });
        }
             };
 
        // 5. 复制成功反馈与降级机制
        function showSuccess(btn, popover) {
            btn.innerText = '✅ 已复制开示及出处';
            btn.style.background = '#2e7d32';
            setTimeout(function () {
                btn.innerText = ' 复制该段开示(含出处)';
                btn.style.background = '#4d6b57';
                popover.style.display = 'none';
            }, 1200);
        }
 
        function fallbackCopy(text, btn, popover) {
            var ta = document.createElement('textarea');
            ta.value = text;
            ta.style.position = 'fixed';
            ta.style.opacity = '0';
            document.body.appendChild(ta);
            ta.select();
            document.execCommand('copy');
            document.body.removeChild(ta);
             showSuccess(btn, popover);
         }
         }
    }


    function showSuccess(btn, popover) {
        document.addEventListener('mouseup', function () { setTimeout(handleSelection, 30); });
         btn.innerText = '✅ 已复制开示及出处';
         document.addEventListener('touchend', function () { setTimeout(handleSelection, 50); });
         btn.style.background = '#2e7d32';
 
        setTimeout(function () {
         document.addEventListener('mousedown', function (e) {
             btn.innerText = '📋 复制该段开示(带出处)';
             var pop = document.getElementById('dhamma-copy-popover');
             btn.style.background = '#4d6b57';
             if (pop && !pop.contains(e.target)) {
            popover.style.display = 'none';
                pop.style.display = 'none';
         }, 1200);
            }
         });
     }
     }


     function fallbackCopy(text, btn, popover) {
     if (document.readyState === 'loading') {
        var ta = document.createElement('textarea');
         document.addEventListener('DOMContentLoaded', initDhammaCopy);
        ta.value = text;
    } else {
        ta.style.position = 'fixed';
         initDhammaCopy();
        ta.style.opacity = '0';
        document.body.appendChild(ta);
        ta.select();
         document.execCommand('copy');
        document.body.removeChild(ta);
         showSuccess(btn, popover);
     }
     }
    document.addEventListener('mouseup', function () { setTimeout(handleSelection, 30); });
    document.addEventListener('touchend', function () { setTimeout(handleSelection, 30); });
    document.addEventListener('mousedown', function (e) {
        var pop = document.getElementById('dhamma-copy-popover');
        if (pop && !pop.contains(e.target)) {
            pop.style.display = 'none';
        }
    });
})();
})();

2026年9月20日 (日) 01:05的最新版本

/* =========================================================
   Dhamma.cn 划词复制开示及 UrlShortener 短网址落款(移动端遮挡修复版)
   适用于:MediaWiki:Common.js
   ========================================================= */
(function () {
    if (window.dhammaCopyLoaded) return;
    window.dhammaCopyLoaded = true;

    function initDhammaCopy() {
        var cachedShortUrl = '';

        // 判断是否为移动端 / 触摸设备
        function isMobileDevice() {
            return ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (window.innerWidth <= 768);
        }

        // 1. 统一域名规范化:剥离 www 及所有二级域名(如 reg.dhamma.cn),强转为 https://dhamma.cn
        function forceMainDomain(url) {
            if (!url) return 'https://dhamma.cn';
            if (url.indexOf('http') !== 0) {
                return 'https://dhamma.cn' + (url.indexOf('/') === 0 ? '' : '/') + url;
            }
            return url.replace(/^https?:\/\/[^\/]+/, 'https://dhamma.cn');
        }

        // 2. 异步获取真正的当前页面短网址
        function getShortUrlAsync() {
            if (cachedShortUrl) return Promise.resolve(cachedShortUrl);

            // 仅在侧边栏/工具栏查找官方短网址,严禁扫描正文(#bodyContent)内的嵌入链接
            var tShort = document.querySelector('#t-shorturl a, #p-tb a[href*="/s/"], .mw-portlet-tb a[href*="/s/"], li[id*="shorturl"] a');
            if (tShort && tShort.href && tShort.href.indexOf('/s/') !== -1) {
                cachedShortUrl = forceMainDomain(tShort.href);
                return Promise.resolve(cachedShortUrl);
            }

            // 获取页面规范网址
            var canonicalEl = document.querySelector('link[rel="canonical"]');
            var targetUrl = canonicalEl ? canonicalEl.href : window.location.href;

            // 优先使用 MediaWiki 原生 Api 模块(带 CSRF Token)
            if (window.mw && mw.Api) {
                return new mw.Api().post({
                    action: 'shortenurl',
                    url: targetUrl
                }).then(function (data) {
                    if (data && data.shortenurl && data.shortenurl.shorturl) {
                        cachedShortUrl = forceMainDomain(data.shortenurl.shorturl);
                    } else {
                        cachedShortUrl = forceMainDomain(targetUrl);
                    }
                    return cachedShortUrl;
                }).catch(function () {
                    return fallbackFetchApi(targetUrl);
                });
            } else {
                return fallbackFetchApi(targetUrl);
            }
        }

        // 降级 Fetch 请求(带 CSRF Token)
        function fallbackFetchApi(targetUrl) {
            var scriptPath = (window.mw && mw.config && mw.config.get('wgScriptPath')) || '';
            var apiUrl = scriptPath + '/api.php';
            var token = (window.mw && mw.user && mw.user.tokens && mw.user.tokens.get('csrfToken')) || '+\\';

            var params = new URLSearchParams();
            params.append('action', 'shortenurl');
            params.append('format', 'json');
            params.append('url', targetUrl);
            params.append('token', token);

            return fetch(apiUrl, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: params.toString()
            })
            .then(function (res) { return res.json(); })
            .then(function (data) {
                if (data && data.shortenurl && data.shortenurl.shorturl) {
                    cachedShortUrl = forceMainDomain(data.shortenurl.shorturl);
                } else {
                    cachedShortUrl = forceMainDomain(targetUrl);
                }
                return cachedShortUrl;
            })
            .catch(function () {
                cachedShortUrl = forceMainDomain(targetUrl);
                return cachedShortUrl;
            });
        }

        // 静默预取短网址
        getShortUrlAsync();

        // 3. 创建/获取浮窗节点
        function getPopover() {
            var pop = document.getElementById('dhamma-copy-popover');
            if (!pop) {
                pop = document.createElement('div');
                pop.id = 'dhamma-copy-popover';
                pop.style.cssText = 'display:none; position:fixed; z-index:2147483647; background:#ffffff; border:1px solid rgba(0,0,0,0.15); border-radius:30px; box-shadow:0 8px 24px rgba(0,0,0,0.22); padding:6px 8px; font-size:14px; white-space:nowrap; transition:bottom 0.2s ease, opacity 0.2s ease;';
                
                var btn = document.createElement('button');
                btn.id = 'dhamma-copy-btn';
                btn.innerText = ' 复制该段开示(含出处)';
                btn.style.cssText = 'background:#4d6b57; color:#ffffff; border:none; border-radius:20px; padding:8px 18px; font-size:14px; cursor:pointer; font-weight:600; font-family:sans-serif; -webkit-tap-highlight-color:transparent;';
                
                btn.onmouseover = function () { if (this.innerText.indexOf('✅') === -1) this.style.background = '#3b5343'; };
                btn.onmouseout = function () { if (this.innerText.indexOf('✅') === -1) this.style.background = '#4d6b57'; };

                pop.appendChild(btn);
                document.body.appendChild(pop);
            }
            return pop;
        }

        var currentText = '';

        // 4. 处理划词选择逻辑(响应式适配 PC / 移动端定位)
        function handleSelection() {
            var popover = getPopover();
            var btn = document.getElementById('dhamma-copy-btn');
            var sel = window.getSelection();
            var text = sel ? sel.toString().trim() : '';

            if (!text || text.length < 2) {
                popover.style.display = 'none';
                return;
            }

            currentText = text;

            if (sel.rangeCount > 0) {
                var isMobile = isMobileDevice();

                if (isMobile) {
                    // 📱 移动端解决遮挡的核心方案:固定在屏幕底部中央,并兼容 iPhone 底部安全区
                    popover.style.top = 'auto';
                    popover.style.bottom = 'calc(25px + env(safe-area-inset-bottom, 0px))';
                    popover.style.left = '50%';
                    popover.style.transform = 'translateX(-50%)';
                    popover.style.display = 'block';
                } else {
                    // 💻 PC 端:跟随选中区域在上方居中弹出
                    var range = sel.getRangeAt(0);
                    var rect = range.getBoundingClientRect();

                    if (rect.width === 0 || rect.height === 0) {
                        popover.style.display = 'none';
                        return;
                    }

                    var popWidth = popover.offsetWidth || 200;
                    var popHeight = popover.offsetHeight || 40;

                    var top = rect.top - popHeight - 10;
                    var left = rect.left + (rect.width / 2) - (popWidth / 2);

                    if (left < 10) left = 10;
                    if (top < 10) top = rect.bottom + 10;

                    popover.style.bottom = 'auto';
                    popover.style.transform = 'none';
                    popover.style.top = top + 'px';
                    popover.style.left = left + 'px';
                    popover.style.display = 'block';
                }

                btn.onclick = function (e) {
                    e.stopPropagation();
                    e.preventDefault();

                    getShortUrlAsync().then(function (shortUrl) {
                        var title = (window.mw && mw.config && mw.config.get('wgTitle')) || document.title;
                        var site = (window.mw && mw.config && mw.config.get('wgSiteName')) || 'Dhamma.cn';

                        var output = 
                            '“' + currentText + '”\n' +
                            '---------------------\n' +
                            '来源:' + site + '\n' +
                            '标题:' + title + '\n' +
                            '地址:' + shortUrl + '\n\n' +
                            '转载请保持原文,随喜分享,Sadhu!';

                        if (navigator.clipboard && navigator.clipboard.writeText) {
                            navigator.clipboard.writeText(output).then(function () {
                                showSuccess(btn, popover);
                            }).catch(function () {
                                fallbackCopy(output, btn, popover);
                            });
                        } else {
                            fallbackCopy(output, btn, popover);
                        }
                    });
                };
            }
        }

        // 5. 复制成功反馈与降级机制
        function showSuccess(btn, popover) {
            btn.innerText = '✅ 已复制开示及出处';
            btn.style.background = '#2e7d32';
            setTimeout(function () {
                btn.innerText = ' 复制该段开示(含出处)';
                btn.style.background = '#4d6b57';
                popover.style.display = 'none';
            }, 1200);
        }

        function fallbackCopy(text, btn, popover) {
            var ta = document.createElement('textarea');
            ta.value = text;
            ta.style.position = 'fixed';
            ta.style.opacity = '0';
            document.body.appendChild(ta);
            ta.select();
            document.execCommand('copy');
            document.body.removeChild(ta);
            showSuccess(btn, popover);
        }

        document.addEventListener('mouseup', function () { setTimeout(handleSelection, 30); });
        document.addEventListener('touchend', function () { setTimeout(handleSelection, 50); });

        document.addEventListener('mousedown', function (e) {
            var pop = document.getElementById('dhamma-copy-popover');
            if (pop && !pop.contains(e.target)) {
                pop.style.display = 'none';
            }
        });
    }

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