您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
https://bgm.tv/dev/app/2647 的修改版,鼠标悬浮在用户链接上方时出现悬浮框,添加了wiki协助记录
// ==UserScript== // @name Bangumi User Hover Panel-change // @name:zh-CN Bangumi 用户悬浮面板-改 // @namespace https://github.com/stay206/bangumi/ // @version 1.0.0 // @description fork of https://bgm.tv/dev/app/2647. Display a hover panel when mouse hover on user link. // @description:zh-CN https://bgm.tv/dev/app/2647 的修改版,鼠标悬浮在用户链接上方时出现悬浮框,添加了wiki协助记录 // @author cureDovahkiin + CryoVit + 墨云 // @match https://bangumi.tv/* // @match https://bgm.tv/* // @match https://chii.in/* // @icon https://bgm.tv/img/favicon.ico // @grant none // @license MIT // ==/UserScript== (function () { /* 2 = timeline 4 = stats 8 = sinkuro 16 = anime 32 = game 64 = book 128 = [reserved] for music 256 = [reserved] for real the value is the sum of the entries to show, e.g. 28 = 4 + 8 + 16, means show stats, sinkuro and anime */ if (localStorage.getItem('hover-panel-config') === null) { // default config localStorage.setItem('hover-panel-config', '28'); // 4 + 8 + 16 } const entryStates = [ ['在看', '看过', '想看', '搁置', '抛弃'], ['在玩', '玩过', '想玩', '搁置', '抛弃'], ['在读', '读过', '想读', '搁置', '抛弃'] ]; const cfgNames = ['时间线', '统计', '同步率', '动画', '游戏', '书籍']; const cfgTimeline = 2; const cfgStats = 4; const cfgSinkuro = 8; const cfgAnime = 16; let locker = false $('[href*="/user/"],#pm_sidebar a[onclick^="AddMSG"]').each(function () { let timer = null $(this).hover(function () { timer = setTimeout(() => { if (locker) return false if (this.text == "查看好友列表" || $(this).find('.avatarSize75').length > 0) return false locker = true const layout = document.createElement('div') let timer = null $(layout).addClass('user-hover') if ($(this).hasClass('avatar')) { $(layout).addClass('fix-avatar-hover') } if (document.body.clientWidth - this.getBoundingClientRect().right < 430) { $(layout).addClass('fix-right-hover') } layout.innerHTML = `<div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>` const userData = {} if (this.onclick) { userData.id = this.onclick.toString().split("'")[1] } else { let urlSplit = /.*\/user\/([^\/]*)\/?(.*)/.exec(this.href) if (urlSplit[2]) return userData.id = urlSplit[1] } userData.href = '/user/' + userData.id const req = { req1: null, req2: null } Promise.all([ new Promise((r, j) => { req.req1 = $.ajax({ url: userData.href, dataType: 'text', success: e => { userData.self = /<a class="avatar" href="([^"]*)">/.exec(e)[1].split('/').pop() if (userData.self != userData.id) { userData.sinkuro = /mall class="hot">\/([^<]*)<\/small>/.exec(e)[1] userData.sinkuroritsu = /<span class="percent" style="width:([^"]*)">/.exec(e)[1] userData.addFriend = /<a href="([^"']*)" id="connectFrd" class="chiiBtn">/.exec(e) userData.addFriend = userData.addFriend ? userData.addFriend[1] : false } userData.joinDate = /Bangumi<\/span> <span class="tip">([^<]*)<\/span>/.exec(e)[1] // userData.lastEvent = /<small class="time">([^<]*)<\/small><\/li>/.exec(e) userData.entry = [ Array.from(e.match(/<a href="\/anime\/list[^>=]*>([0-9]{1,4}[^<]*)/g) || [], el => />([0-9]{1,5}.*)/.exec(el)[1]).map(el => el.split('部')), Array.from(e.match(/<a href="\/game\/list[^>=]*>([0-9]{1,4}[^<]*)/g) || [], el => />([0-9]{1,5}.*)/.exec(el)[1]).map(el => el.split('部')), Array.from(e.match(/<a href="\/book\/list[^>=]*>([0-9]{1,4}[^<]*)/g) || [], el => />([0-9]{1,5}.*)/.exec(el)[1]).map(el => el.split('本')) ] userData.stats = /<div class="gridStats">([\s\S]*)<\/div>/.exec(e)[1] userData.stats = Array.from(userData.stats.match(/<div[^>]*>([\s\S]*?)<\/div>/g).slice(0, 6), el => /<div[^>]*>([\s\S]*?)<\/div>/.exec(el)[1]) userData.stats = userData.stats.map(el => Array.from(el.match(/<span[^>]*>([\s\S]*?)<\/span>/g), el => /<span[^>]*>([\s\S]*?)<\/span>/.exec(el)[1])) userData.timeline = /<ul class="timeline">([\s\S]*?)<\/ul>/.exec(e)[1] // console.log(userData) r() }, error: () => { j() } }) }), new Promise((r, j) => { req.req2 = $.ajax({ url: 'https://api.bgm.tv/user/' + userData.id, dataType: 'json', success: e => { userData.name = e.nickname userData.avatar = e.avatar.large.replace(/https?/, 'https') userData.sign = e.sign userData.url = e.url userData.message = `https://bgm.tv/pm/compose/${e.id}.chii` userData.wiki = `https://patch.bgm38.tv/contrib/${e.id}` r() }, error: () => { j() } }) }) ]).then(() => { layout.innerHTML = ` <img class='avater' src="${userData.avatar}"/> <div class='user-info'> <p class='user-name'><a href="${userData.href}" target="_blank">${userData.name}</a></p> <p class='user-joindate'>${userData.joinDate}</p><span class='user-id'>@${userData.id}</span> <p class='user-sign'>${userData.sign}</p> </div> ${ ((localStorage.getItem('hover-panel-config') & cfgSinkuro) && userData.sinkuro) ? ` <div class="shinkuro"> <div style="width:${userData.sinkuroritsu}" class="shinkuroritsu"></div> <div class="shinkuro-text"> <span>${userData.sinkuro}</span> <span>同步率:${userData.sinkuroritsu}</span> </div> </div> `: '' } <div class='user-stats'> ${(function () { const cfg = localStorage.getItem('hover-panel-config') let html = '' let odd = true for (let i = 0; i < 3; i++) { if (cfg & (cfgAnime << i)) { html += '<div class="stats-' + (odd ? 'odd' : 'even') + '">' let dt_j = 0 for (let st_j = 0; st_j < 5; st_j++) { if (dt_j >= userData.entry[i].length || userData.entry[i][dt_j][1] != entryStates[i][st_j]) { html += `<span class="stats-zero">${entryStates[i][st_j]} <strong>0</strong></span>` } else { html += `<span>${entryStates[i][st_j]} <strong>${userData.entry[i][dt_j][0]}</strong></span>` dt_j++ } } html += '</div>' odd = !odd } } if (cfg & cfgStats) { html += '<div class="stats-' + (odd ? 'odd' : 'even') + '">' for (let i = 0; i < 6; i++) { if (i == 2) { continue } if (userData.stats[i][0] == 0) { // '0.00' == 0 html += `<span class="stats-zero">${userData.stats[i][1]} <strong>${userData.stats[i][0]}</strong></span>` } else { html += `<span>${userData.stats[i][1]} <strong>${userData.stats[i][0]}</strong></span>` } } html += '</div>' odd = !odd } return html })()} </div> ${ (localStorage.getItem('hover-panel-config') & cfgTimeline) ? ` <ul class="timeline" id="panel-timeline">${userData.timeline}</ul> `: '' } <!-- <span class='user-lastevent'>Last @ ${userData.lastEvent ? userData.lastEvent[1] : ''}</span> --> <a class = 'hover-panel-btn' href="${userData.message}" target="_blank">发送短信</a> <a class = 'hover-panel-btn' href="${userData.wiki}" target="_blank">协助记录</a> <span id="panel-friend"> ${ userData.addFriend ? ` <a class='hover-panel-btn' href="${userData.addFriend}" id='PanelconnectFrd' href="javascript:void(0)">添加好友</a> `: ` ${ userData.id == userData.self ? '' : `<span class = 'my-friend' >我的好友</span>`} `} </span> ` let cb = document.createElement('a') cb.className = 'hover-panel-btn' cb.id = 'cfg-btn' cb.href = 'javascript:void(0)' cb.onclick = function () { let cfg = localStorage.getItem('hover-panel-config') let sub = document.createElement('div') sub.className = 'user-hover' sub.id = 'hover-panel-sub' sub.innerHTML = ` <fieldset> <legend>设置显示项目</legend> ${(function () { let html = '' for (let i = 0; i < 6; i++) { html += `<div class='hover-cfg-item'> <input type='checkbox' id='hover-cfg-${i}' ${cfg & (2 << i) ? 'checked' : ''}> <label for='hover-cfg-${i}'>${cfgNames[i]}</label> </div>` } return html })()} </div> </fieldset> ` let cancel = document.createElement('a') cancel.className = 'hover-panel-btn' cancel.id = 'cfg-cancel-btn' cancel.href = 'javascript:void(0)' cancel.innerText = '取消' cancel.onclick = function () { $('#hover-panel-sub').remove() } sub.appendChild(cancel) let save = document.createElement('a') save.className = 'hover-panel-btn' save.id = 'cfg-save-btn' save.href = 'javascript:void(0)' save.innerText = '保存' save.onclick = function () { let cfg = 0 for (let i = 0; i < 6; i++) { if (document.getElementById(`hover-cfg-${i}`).checked) { cfg |= (2 << i) } } localStorage.setItem('hover-panel-config', cfg) $('#hover-panel-sub').remove() } sub.appendChild(save) document.body.appendChild(sub) } cb.innerText = '设置' layout.appendChild(cb) $(layout).addClass('dataready') $('#PanelconnectFrd').click(function () { $('#panel-friend').html(`<span class='my-friend'>正在添加</span>`) $("#robot").fadeIn(500) $("#robot_balloon").html(AJAXtip['wait'] + AJAXtip['addingFrd']) $.ajax({ type: "GET", url: this + '&ajax=1', success: function (html) { $('#PanelconnectFrd').hide() $('#panel-friend').html(`<span class = 'my-friend' >我的好友</span>`) $("#robot_balloon").html(AJAXtip['addFrd']) $("#robot").animate({ opacity: 1 }, 1000).fadeOut(500) localStorage.removeItem('bgmFriends') }, error: function (html) { $("#robot_balloon").html(AJAXtip['error']) $("#robot").animate({ opacity: 1 }, 1000).fadeOut(500) $('#panel-friend').html(`<span class='my-friend-fail'>添加失败</span>`) } }) return false }) }).catch(() => { layout.innerHTML = ` <p style='font-size:16px; margin:25px 30px'> <img style="height:15px;width:16px" src='/img/smiles/tv/15.gif'/><br/> 请求失败,请稍后再试。<br/><br/>或者使用<a href='https://bgm.tv'>bgm.tv</a>域名,</p>` $(layout).addClass('dataready') }) function removeLayout () { setTimeout(() => { $(layout).remove() locker = false req.req1.abort() req.req2.abort() }, 200); } $(this).after(layout).mouseout(function () { timer = setTimeout(() => { removeLayout() }, 500); }) $(layout).hover(function () { clearTimeout(timer) }, function () { removeLayout() }) return false }, 500) }, function () { clearTimeout(timer) } ) }) // prevent user link at (1) page header (2) footer dock (3) reply form (4) timeline // from triggering hover panel $("#headerNeue2, #dock, #reply_wrapper, .tml_item").find("a[href*='/user/']").unbind(); const style = document.createElement("style"); const heads = document.getElementsByTagName("head"); style.setAttribute("type", "text/css"); style.innerHTML = ` :root { --bg-color: #fff; --text-color: #010101; --bg-pink: #fce9e9; --bg-sky: #c2e1fc; --box-shadow: #ddd; --text-gray: #6e6e6e; --bg-filter: blur(10px) contrast(90%); } [data-theme='dark'] { --bg-color: #2d2e2f; --text-color: #f7f7f7; --bg-pink: #3c3c3c; --bg-sky: #3c3c3c; --box-shadow: #6e6e6e; --text-gray: #aaa; --bg-filter: blur(10px) contrast(50%); } .user-hover { position: absolute; width: 430px; /* background: var(--bg-color); */ box-shadow: 0px 0px 4px 1px var(--box-shadow); transition: all .2s ease-in; transform: translate(0,6 px); font-size: 12px; z-index:999; color: var(--text-color); line-height: 130%; border-radius: 15px; -webkit-border-radius: 15px; backdrop-filter: var(--bg-filter); -webkit-backdrop-filter: var(--bg-filter); } .fix-avatar-hover { transform: translate(55px, 20px) } .fix-right-hover { transform: translate(-430px, 6px) } .fix-avatar-hover.fix-right-hover { transform: translate(-485px, 20px) } /* basic info */ div.dataready { padding: 8px; font-weight: normal; text-align: left; } /* span.user-lastevent { margin-top: 3px; display: inline-block; vertical-align: top; color: var(--text-gray); } */ div.dataready img { height: 75px; width:75px; border-radius: 5px; } .user-info { display: inline-block; vertical-align: top; max-width: 250px; margin: 0 0 10px 10px; } .user-info .user-name { font-size: 20px; font-weight: bold; } .user-info .user-joindate { background-color: #f09199; display: inline-block; color: #f7f7f7; border-radius: 10px; padding: 0 10px; margin: 8px 4px 3px 0; } .user-info .user-id{ font-size: 12px; font-weight:normal; color: var(--text-gray); } .user-info .user-sign { word-break: break-all; margin-top: 3px; color: var(--text-gray); } /* stats */ .user-stats { padding: 10px 0px 5px; margin-bottom: 0; } .user-stats span { display: inline-block; padding: 4px; width: 19%; box-sizing: border-box; border-left: 4px solid #f09199; background-color: var(--bg-pink); color: var(--text-color); margin: 0 1% 1% 0; } .stats-even span { border-left: 4px solid #369cf8; background-color: var(--bg-sky); } .stats-zero { opacity: 0.5; } /* shinkuro */ .shinkuro { width: 100%; height: 20px; background-color: var(--bg-sky); line-height: 20px; border-radius: 10px; margin-top: 5px; } .shinkuro-text { position: absolute; width: 100%; height: 20px; display: flex; align-items: center; justify-content: space-between; } .shinkuro-text span { color: var(--text-color); } .shinkuroritsu { height: 20px; float: left; border-radius: 10px; background: #369cf8; } .shinkuro-text span:nth-of-type(1) { margin-left: 10px; } .shinkuro-text span:nth-of-type(2) { margin-right: 26px; } /* timeline */ #panel-timeline li { margin-top: 0; } #panel-timeline a { display: inline !important; } #panel-timeline .time { color: var(--text-gray); } /* buttons */ a.hover-panel-btn, span.my-friend, span.my-friend-fail { display: inline-block; float: right; color: white; padding: 1px 8px; border-radius: 10px; margin: 8px 0 0 10px; transition: all .2s ease-in; } a.hover-panel-btn { background: #f09199; transition: all .2s ease-in; } span.my-friend { background: #6eb76e; } span.my-friend-fail { background: red; } #cfg-btn { background: #369cf8; float: left; margin-left: 0; } /* animation */ .lds-roller { display: inline-block; position: relative; width: 64px; height: 64px; margin:10px 20px } .lds-roller div { animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; transform-origin: 32px 32px; } .lds-roller div:after { content: " "; display: block; position: absolute; width: 6px; height: 6px; border-radius: 50%; background: #f09199; margin: -3px 0 0 -3px; } .lds-roller div:nth-child(1) { animation-delay: -0.036s; } .lds-roller div:nth-child(1):after { top: 50px; left: 50px; } .lds-roller div:nth-child(2) { animation-delay: -0.072s; } .lds-roller div:nth-child(2):after { top: 54px; left: 45px; } .lds-roller div:nth-child(3) { animation-delay: -0.108s; } .lds-roller div:nth-child(3):after { top: 57px; left: 39px; } .lds-roller div:nth-child(4) { animation-delay: -0.144s; } .lds-roller div:nth-child(4):after { top: 58px; left: 32px; } .lds-roller div:nth-child(5) { animation-delay: -0.18s; } .lds-roller div:nth-child(5):after { top: 57px; left: 25px; } .lds-roller div:nth-child(6) { animation-delay: -0.216s; } .lds-roller div:nth-child(6):after { top: 54px; left: 19px; } .lds-roller div:nth-child(7) { animation-delay: -0.252s; } .lds-roller div:nth-child(7):after { top: 50px; left: 14px; } .lds-roller div:nth-child(8) { animation-delay: -0.288s; } .lds-roller div:nth-child(8):after { top: 45px; left: 10px; } @keyframes lds-roller { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } #comment_list div.sub_reply_collapse { opacity: 1; } /* config panel */ #hover-panel-sub { width: 150px; height: 160px; padding: 5px; line-height: 1.5; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 1000; } #hover-panel-sub legend { font-size: 14px; font-weight: bold; text-align: center; } #hover-panel-sub fieldset { padding: 0 5px; } #hover-panel-sub .hover-panel-btn { display: inline-block; text-align: center; } #cfg-cancel-btn { position: absolute; left: 14px; bottom: 6px; background: #f09199; } #cfg-save-btn { position: absolute; right: 24px; bottom: 6px; background: #6eb76e; } ` heads[0].append(style) })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址