有觉性 以安住且中立的心 照见身心的实相
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
法谈划词引用
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();
}
);
})();
