// ==UserScript==
// @name Bilibili 干净链接
// @namespace Motoori Kashin
// @version 2.1.5
// @description 去除bilibili链接中不需要的参数,如spm_id_from/from_sourse/from/等,还地址栏以清白干净
// @author Motoori Kashin
// @match *://*.bilibili.com/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
function isURL(url,base) {
try {
if (typeof url === "string" && /^[\W\w]+\.[\W\w]+/.test(url) && !/^[a-z]+:/.test(url)) {
// 处理省略协议头情况
const str = url.startsWith("//") ? "" : "//";
url = location.protocol + str + url;
}
return new URL(url, base);
} catch (e) {
return false;
}
}
/** 垃圾参数 */
const paramsSet = new Set([
'spm_id_from',
'from_source',
'msource',
'bsource',
'seid',
'source',
'session_id',
'visit_id',
'sourceFrom',
'from_spmid',
'share_source',
'share_medium',
'share_plat',
'share_session_id',
'share_tag',
'unique_k',
"csource",
"vd_source",
"tab",
"is_story_h5",
"share_from",
"plat_id",
"-Arouter",
"spmid",
]);
/** 节点监听暂存 */
const nodelist = [];
/**
* 清理url
* @param str 原url
* @returns 新url
*/
function clean(str) {
if(/.*:\/\/.*.bilibili.com\/.*/.test(str) && !str.includes('passport.bilibili.com')){
const url = isURL(str);
if(url){
paramsSet.forEach(d => {
url.searchParams.delete(d);
});
return url.toJSON();
}
}
return str;
}
/** 地址备份 */
let locationBackup;
/** 处理地址栏 */
function cleanLocation() {
const { href } = location;
if (href === locationBackup) return;
replaceUrl(locationBackup = clean(href));
}
/** 处理href属性 */
function anchor(list) {
list.forEach(d => {
if (!d.href) return;
d.href.includes("bilibili.tv") && (d.href = d.href.replace("bilibili.tv", "bilibili.com")); // tv域名失效
d.href = clean(d.href);
});
}
/** 检查a标签 */
function click(e) { // 代码copy自B站spm.js
var f = e.target;
for (; f && "A" !== f.tagName;) {
f = f.parentNode
}
if ("A" !== (null == f ? void 0 : f.tagName)) {
return
}
anchor([f]);
}
/**
* 修改当前URL而不出发重定向
* **无法跨域操作!**
* @param url 新URL
*/
function replaceUrl(url) {
window.history.replaceState(window.history.state, "", url);
}
cleanLocation(); // 及时处理地址栏
// 处理注入的节点
let timer = 0;
observerAddedNodes((node) => {
clearTimeout(timer);
timer = setTimeout(() => {
cleanLocation();
anchor(document.querySelectorAll("a"));
});
});
// 处理点击事件
window.addEventListener("click", click, !1);
// 处理右键菜单
window.addEventListener("contextmenu", click, !1);
// 页面载入完成
document.addEventListener("load", ()=>anchor(document.querySelectorAll("a")), !1);
/**
* 注册节点添加监听
* **监听节点变动开销极大,如非必要请改用其他方法并且用后立即销毁!**
* @param callback 添加节点后执行的回调函数
* @returns 注册编号,用于使用`removeObserver`销毁监听
*/
function observerAddedNodes(callback) {
try {
if (typeof callback === "function") nodelist.push(callback);
return nodelist.length - 1;
} catch (e) { console.error(e) }
}
const observe = new MutationObserver(d => d.forEach(d => {
d.addedNodes[0] && nodelist.forEach(async f => {
try {
f(d.addedNodes[0])
} catch (e) { console.error(d) }
})
}));
observe.observe(document, { childList: true, subtree: true });
window.open = ((__open__) => {
return (url, name, params) => {
return __open__(clean(url), name, params)
}
})(window.open)
window.navigation && window.navigation.addEventListener('navigate', e => {
const newURL = clean(e.destination.url)
if(e.destination.url!=newURL) {
e.preventDefault(); // 返回前还是先阻止原事件吧
if(newURL == window.location.href) return // 如果清理后和原来一样就直接返回
// 否则就处理清理后的链接
window.history.replaceState(window.history.state, "", newURL)
return
}
});
})();