MediaWiki:Gadget-Tooltip.js
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Internet Explorer或Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
- Opera:按 Ctrl-F5。
/* <nowiki> ← 避免被归类为有脚本错误的页面*/
// 当添加新类型浮层时,需要对应在“★”注释之后的部分添加代码
$(function (){
if (!['view', 'submit'].includes(mw.config.get('wgAction'))) return;
var TOOLTIP_CACHE_BLOCK_ID = 'bili-tt-cache-block';
if (document.getElementById(TOOLTIP_CACHE_BLOCK_ID)) return; /* 避免重复调用 */
var TOOLTIP_CACHE_BLOCK = $();
var Link_CLASS_NAME = 'bili-tt';
var CACHE_TIME = 3600; // 解析内容保存在服务器和本地的缓存时间,单位是秒,如果内容不经常改的话,可以设长一些减少服务器压力,增加读取速度
var actions = {};
actions.replace = function(block){ // 提取块中的第一个<a>的地址,并将整个块替换为<a>
var inner_link = block.querySelector('a[href]'); // 获取块内的第一个a,获取其href和title,然后移除
if (!inner_link) return block;
var link = document.createElement('a');
link.setAttribute('href', inner_link.getAttribute('href'));
if (inner_link.innerHTML.trim() !== ""){
var span = document.createElement('span');
span.innerHTML = inner_link.innerHTML;
inner_link.parentElement.insertBefore(span, inner_link);
}
inner_link.remove();
var attrs = block.attributes;
for(var i = attrs.length - 1; i >= 0; i--){ // 转移各项属性
var attr = attrs[i];
block.removeAttributeNode(attr);
link.setAttributeNode(attr);
}
link.innerHTML = block.innerHTML;
block.parentElement.insertBefore(link, block); // 取代位置
block.remove();
return link;
};
if (!fIsMobile()){
actions.listen = function(link){
$(link).mouseenter(function(ev){
var $this = $(this);
var $div = get_div($this);
$this.data('event',ev); // 记录鼠标位置,用于在load_tooltip_lua后重新set_tt_position
$div.show();
TOOLTIP_CACHE_BLOCK.show();
set_position($this);
}).mouseleave(function(){
var $this = $(this);
TOOLTIP_CACHE_BLOCK.hide();
get_div($this).hide();
});
};
actions.init = function(range){
// 添加缓存浮层的区域
if (TOOLTIP_CACHE_BLOCK.length === 0){
$("body").append('<div id="' + TOOLTIP_CACHE_BLOCK_ID + '"></div>');
TOOLTIP_CACHE_BLOCK = $('#' + TOOLTIP_CACHE_BLOCK_ID);
}
// 为所有浮层添加事件
$(range).find('.' + Link_CLASS_NAME).addBack('.' + Link_CLASS_NAME).each(function(){
actions.listen(actions.replace(this));
});
};
}
else {
actions.listen = function(link) {
$(link).click(function(){
var $this = $(this);
var $div = get_div($this);
$div.show();// 显示对应的内容
TOOLTIP_CACHE_BLOCK.show();
$('#tt-modal').modal('show'); // 显示模态框
var $btn = $('#tt-modal-btn');
if ($this.attr('href')){ // 跳转按钮
$btn.attr('href', $this.attr('href'));
$btn.show();
}
else{ // 如果不是链接,隐藏按钮
$btn.hide();
}
return false;
});
};
actions.init = function(range){
if (TOOLTIP_CACHE_BLOCK.length === 0){
$("body").append('<div class="modal fade" id="tt-modal" tabindex="-1" role="dialog">'
+ '<div class="modal-dialog">'
+ '<div id="' + TOOLTIP_CACHE_BLOCK_ID + '">'
+ '</div>'
+ '<a id="tt-modal-btn" class="btn btn-primary">前往详情页</a>'
+ '</div>'
+ '</div>');
TOOLTIP_CACHE_BLOCK = $('#' + TOOLTIP_CACHE_BLOCK_ID);
$('#tt-modal').click(function(){
$('#tt-modal').modal('hide'); // 任意位置点击后隐藏模态框
TOOLTIP_CACHE_BLOCK.children('.bili-tt-cache').hide();
});
}
$(range).find('.' + Link_CLASS_NAME).addBack('.' + Link_CLASS_NAME).each(function(){
actions.listen(actions.replace(this));
});
};
}
actions.init(document.getElementById('mw-content-text'));
// 监听dom变动,为新插入的tt注册事件
new MutationObserver(function(mutationsList){
mutationsList.forEach(function(mutation){
if (mutation.addedNodes) mutation.addedNodes.forEach(actions.init);
});
}).observe(document.getElementById('mw-content-text'), {
childList: true, // 监视节点增删事件
subtree: true // 包含所有后代节点(而不只是子节点)
});
function get_div($link){
var tt_type = get_data($link, 'type');
var tt_name = get_data($link, 'name').replace(' ', '_');
var $div = $(document.getElementById(get_div_id(tt_type,tt_name)));
if ($div.length === 0){
$div = new_div($link, fIsMobile() ? null : function(){set_position($link);});
}
return $div;
}
function get_div_id(type, name){
return encodeURIComponent('tt-' + type + '-' + name).replace(/%/g,'.').replace(/[~'!()*]/g,'_');
}
function get_data($node, name){
return $node.attr("data-" + name) || "";
}
// 根据浮层大小和当前元素的位置,决定浮层的位置(尽量让浮层显示在屏幕中)
function set_position($link) {
var window_h = $(window).height();
var window_w = $(window).width();
var top = $link.offset().top - $(document).scrollTop();
var left = $link.offset().left; // 元素左侧绝对位置,若发生换行则为第二行的开始位置
var width = $link.outerWidth();
var parent = $link.parent();
var relativeLeft = $link[0].offsetLeft; // 元素第一行左侧相对于容器的位置
var ttWidth = TOOLTIP_CACHE_BLOCK.outerWidth();
var ttHeight = TOOLTIP_CACHE_BLOCK.outerHeight();
TOOLTIP_CACHE_BLOCK.css("left","unset");
TOOLTIP_CACHE_BLOCK.css("right","unset");
if (relativeLeft + width > parent.innerWidth()){ // 元素左侧相对偏移+元素外侧宽度>容器内侧宽度,发生换行
var mouseEvent = $link.data('event');
if (mouseEvent.clientX > window_w / 2){ // 鼠标在左侧
TOOLTIP_CACHE_BLOCK.css("right", window_w - (left + relativeLeft) + 10); // 元素第一行的左侧位置
}
else {
TOOLTIP_CACHE_BLOCK.css("left", mouseEvent.clientX + 10); // 鼠标滑入位置
}
}
else if (left > window_w / 2){
TOOLTIP_CACHE_BLOCK.css("right", window_w - left + 10);
}
else {
TOOLTIP_CACHE_BLOCK.css("left", left + width + 10);
}
TOOLTIP_CACHE_BLOCK.css("top", top);
}
// 生成浮层
function new_div($link, callback){
callback = callback || function(){};
var tt_type = get_data($link, 'type');
if (tt_type === 'child'){
$link.attr('data-name', Math.random().toString().replace('0.',''));
}
var tt_name = get_data($link, 'name').replace(' ', '_');
var $div = $('<div></div>');
$div.attr('id', get_div_id(tt_type,tt_name)).addClass('bili-tt-cache');
// 可以为每种type的浮层,写不同的占位文本
// 如果浮层内容简单,也可以直接将其内容使用js生成
switch (tt_type){
// 可以写多种类型
// ★添加类型时,需要在此处添加新的case来为不同类型指定未加载前的占位代码
// case 'test':
// ...
// break;
case 'raw':
$div.text(tt_name);
break;
case 'child':
$link.children('.tt-child').show().appendTo($div);
break;
}
$div.appendTo(TOOLTIP_CACHE_BLOCK);
// 异步读取模板解析结果
// 如果提示框内容比较简单,则可以在上面用js直接生成,不需要在此处配置
var wikitext = '';
switch (tt_type.slice(0,1)){
case '@': // 模板
var template_name = tt_type.slice(1);
wikitext = '{{' + template_name + '|' + tt_name.replace('_', '').replaceAll('\\n', '<br>') + '}}'; // 调用tt_type对应的模板,参数为tt_name
break;
case '$': // 模块
var module_name = tt_type.slice(1);
wikitext = '{{#invoke:' + module_name + '|tooltip|' + tt_name + '}}'; // 调用tt_type对应模块的tooltip方法,参数为tt_name
break;
default:
switch (tt_type){
case 'test':
wikitext = '{{#if:1|test|' + tt_name + '}}';
break;
case 'wikitext':
wikitext = tt_name;
break;
}
}
if (wikitext){
$div.text('加载中...');
load_wikitext(wikitext, $div).then(callback);
}
else{
callback();
}
return $div;
}
// 根据wikitext生成浮层内容
function load_wikitext(wikitext, $div, again) {
// 文档:https://wiki.biligame.com/pcr/api.php?action=help&modules=parse
return $.get('/octopathsp/api.php',{
action: "parse", // 解析
format: "json", // 返回内容的格式
disablelimitreport: true, // 不返回使用内存、时间信息
prop: "text", // 返回解析后的文本
contentmodel: "wikitext", // 内容模型
smaxage: CACHE_TIME,
maxage: CACHE_TIME,
text: wikitext, // 待解析文本
_: mw.config.get('debug') ? Date.now() : null // 调试时不使用缓存
}).then(function(result){
if (result && result.parse && result.parse.text){
$div.html(result.parse.text['*']);
}
else{
throw result;
}
})['catch'](function(error){ // 辣鸡解析器,不让我直接.catch
if(again){
$div.text('读取失败');
console.log('获取"' + wikitext + '"失败', error);
}
else{
$div.text('再次尝试...');
return load_wikitext(wikitext, $div, true);
}
});
}
});
/* </nowiki> */