User:SunAfterRain/js/InternalLinkHelperV1.js
外观
< User:SunAfterRain | js
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google Chrome、Firefox、Microsoft Edge及Safari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
警告:User:SunAfterRain/js下的所有script幾乎都是本人隨興撰寫的,不保證可用性。如要使用,請確保您的瀏覽器支援ES6、async & await等語法糖,並接受本人突然改變任何內容以至於用法改變或是毀損無法使用。任何問題請至:User talk:SunAfterRain
/**
* InternalLinkHelperV1
* 復刻舊版 InternalLinkHelper 小工具
*
* 使用方法:
* window.wgInternalLinkHelperConfig = { ... }; // 可選,選項請參考下方的 Config 之 jsdoc
* importScript( 'User:SunAfterRain/js/InternalLinkHelperV1.js' ); // backlink: [[User:SunAfterRain/js/InternalLinkHelperV1.js]]
*
* 舊式用法(已棄用):
* window.wgInternalLinkHelperMode = 'redonly'; // 或是 redtipsy、redplain、external、suffix、cravix、altcolor、ilbluehl
* importScript( 'User:SunAfterRain/js/InternalLinkHelperV1.js' ); // backlink: [[User:SunAfterRain/js/InternalLinkHelperV1.js]]
*
* @author SunAfterRain
* @license CC-BY-SA 4.0
*
* 備註:
* 1. jquery.tipsy 和 MediaWiki:Tooltips.js 已被替換成 OO.ui.PopupWidget,所以樣式和行為模式可能略有不同
* 2. 怪 bug 請隨意反饋,但除非是不屬於 1 引入的問題並且原版無法出現,不然我大概不會修
*/
/* _addText: {{User:SunAfterRain/js}} */
/* global $ */
mw.loader.load('/w/index.php?title=User:SunAfterRain/js/InternalLinkHelperV1.css&action=raw&ctype=text/css', 'text/css');
Promise.all([
$.ready,
mw.loader.using(['mediawiki.util', 'oojs-ui-core', 'ext.gadget.HanAssist'])
]).then(async ([_, require]) => {
const HanAssist = require('ext.gadget.HanAssist');
/**
* @typedef {Object} Config
* @property {'only-red' | 'red-plain' | 'external' | 'tooltip'} mode
* * only-red: 僅顯示紅色連結
* * red-plain: 顯示紅色連結和無連結的原文
* * external: 只顯示到外文維基的連結
* * tooltip: 顯示彈窗
* @property {boolean} [displaySuffix=false] 僅在 mode=external 時有效,顯示語言名字後綴
* @property {boolean} [greenLink=false] 僅在 mode=tooltip 時有效,顯示綠色連結
* @property {boolean} [clickTrigger=false] 僅在 mode=tooltip 時有效,Tooltip 改以點擊連結時顯示
* @property {boolean} [highlightExisting=false] 僅在 mode=tooltip 時有效,對於已存在頁面的情況下高亮表示
*/
/** @type {Record<string, Config>} */
const gadgetModeMapConfig = {
// redonly - 只顯示紅鏈
redonly: {
mode: 'only-red',
},
// redtipsy - 在 Tooltip 中顯示原文連結
redtipsy: {
mode: 'tooltip'
},
// redplain - 顯示紅色連結和無連結的原文
redplain: {
mode: 'red-plain',
},
// external - 直接指向原文
external: {
mode: 'external',
},
// suffix - 繼承 external,指向原文和語言名後綴
suffix: {
mode: 'external',
displaySuffix: true,
},
// cravix - 與 redtipsy 類似,但連結顏色是綠色並且 Tooltip 是點擊連結時打開
cravix: {
mode: 'tooltip',
greenLink: true,
clickTrigger: true,
},
// altcolor - 繼承 redtipsy,只是連結顏色換了
altcolor: {
mode: 'tooltip',
greenLink: true,
},
// ilbluehl - 繼承 altcolor,對於已存在頁面的情況下高亮表示
ilbluehl: {
mode: 'tooltip',
greenLink: true,
highlightExisting: true,
},
};
const notifyMessage = HanAssist.convByVar({
hans: '条目“$1”尚未创建,可参考$2维基百科的对应页面:',
hant: '條目「$1」尚未創建,可參考$2維基百科的對應頁面:'
});
let $popupContainor;
function getContainor() {
if (!$popupContainor) {
$popupContainor = $('<div>').attr({
class: 'userjs-InternalLinkHelperV1-container'
});
$popupContainor.appendTo(document.body);
}
return $popupContainor;
}
function clearContainor() {
$popupContainor?.empty();
}
function buildTooltip($self, allowCloseByUser = false) {
const origTitle = $self.data('orig-title'),
$foreignSpan = $self.find('.ilh-link'),
$linkAnchor = $self.find('.ilh-page a'),
$langSpan = $self.find('.ilh-lang'),
langName = $langSpan.text();
if (!$linkAnchor.length) {
return false;
}
const popupWidget = new OO.ui.PopupWidget({
$content: $('<div>')
.append(
document.createTextNode(
mw.format(notifyMessage, origTitle, langName)
),
$foreignSpan.clone()
),
padded: true,
head: allowCloseByUser,
anchor: false,
align: 'center',
autoFlip: false,
autoclose: false,
$floatableContainer: $linkAnchor
});
popupWidget.$element.appendTo(getContainor());
return { popupWidget, $linkAnchor };
}
let config;
if ('wgInternalLinkHelperConfig' in window) {
config = window.wgInternalLinkHelperConfig;
} else if ('wgInternalLinkHelperMode' in window) {
const mode = window.wgInternalLinkHelperMode?.toLowerCase();
config = gadgetModeMapConfig[mode];
if (!config) {
throw new Error('InternalLinkHelperV1 gadget mode is empty or invalid.');
}
} else {
config = gadgetModeMapConfig.ilbluehl;
}
function addClass(className) {
document.documentElement.classList.add(`userjs-ilhv1-${className}`);
}
let hook = false;
switch (config.mode) {
case 'only-red':
addClass('no-comment');
break;
case 'red-plain':
addClass('red-plain');
hook = ($content) => {
for (const self of $content.find('.ilh-link')) {
$(self).text($(self).text()); // remove link
}
};
break;
case 'external':
addClass('external');
if (config.displaySuffix) {
addClass('suffix');
}
hook = ($content) => {
for (const self of $content.find('.ilh-link')) {
const $extra = $(self).find('.ilh-link a');
if ($extra.length == 0) {
continue;
}
$(self).find('.ilh-page a')
.removeClass('new')
.addClass('extiw')
.attr('href', $extra.attr('href'))
.attr('title', $extra.attr('title'));
}
};
break;
case 'tooltip':
addClass('no-comment');
if (config.greenLink) {
addClass('green-link');
}
if (config.highlightExisting) {
addClass('highlight-existing');
}
if (config.clickTrigger) {
hook = ($content) => {
const ilhList = $content.find('.ilh-all:not(.ilh-blue)').get();
if (!ilhList.length) {
return;
}
clearContainor();
for (const self of ilhList) {
const $self = $(self);
const { popupWidget, $linkAnchor } = buildTooltip($self, true);
if (!popupWidget) {
continue;
}
$linkAnchor.on('click', (ev) => {
ev.preventDefault();
$(ilhList.filter(element => !$self.is(element))).trigger('internalLinkHelper-close');
popupWidget.toggle(true);
});
$self.on('internalLinkHelper-close', () => {
popupWidget.toggle(false);
});
}
};
} else {
hook = ($content) => {
const ilhList = $content.find('.ilh-all:not(.ilh-blue)').get();
if (!ilhList.length) {
return;
}
clearContainor();
for (const self of ilhList) {
const $self = $(self);
const { popupWidget, $linkAnchor } = buildTooltip($self);
if (!popupWidget) {
continue;
}
const { maybeClearTimeout, mouseenter, mouseleave } = (() => {
let timeout = null;
const maybeClearTimeout = () => {
if (timeout !== null) {
clearTimeout(timeout);
}
};
const autoSetTimeout = (...args) => {
maybeClearTimeout();
timeout = setTimeout(...args);
};
const timeoutFunction = () => {
popupWidget.toggle(false);
timeout = null;
};
return {
maybeClearTimeout,
mouseenter() {
if (popupWidget.isVisible()) {
maybeClearTimeout();
} else {
$(ilhList.filter(element => !$self.is(element))).trigger('internalLinkHelper-close');
popupWidget.toggle(true);
}
},
mouseleave() {
autoSetTimeout(() => {
popupWidget.toggle(false);
}, 500);
}
};
})();
$linkAnchor.add(popupWidget.$popup).on('mouseenter', mouseenter).on('mouseleave', mouseleave);
$self.on('internalLinkHelper-close', () => {
maybeClearTimeout();
popupWidget.toggle(false);
});
}
};
}
break;
default:
throw new Error('InternalLinkHelperV1 mode is empty or invalid.');
}
if (hook) {
mw.hook('wikipage.content').add(hook);
}
});