// ==UserScript==
// @name 在新标签页中打开链接
// @namespace http://tampermonkey.net/
// @version 1.32
// @description 在页面上显示一个功能开关,改变网页跳转方式改为打开新标签页进行跳转,请自行配置脚本作用域
// @author 晚风知我意
// @match https://yesicon.app/*
// @match https://bbs.binmt.cc/*
// @grant none
// @license GNU AGPLv3
// ==/UserScript==
(function() {
'use strict';
const domain = location.hostname;
const toggleKey = `linkToggleEnabled_${domain}`;
const ignoredElementsKey = `ignoredElements_${domain}`;
if (localStorage.getItem(toggleKey) === null) {
localStorage.setItem(toggleKey, 'true');
}
let isEnabled = localStorage.getItem(toggleKey) === 'true';
const ignoredElements = new Set(JSON.parse(localStorage.getItem(ignoredElementsKey)) || []);
const button = document.createElement("div");
button.style.position = "fixed";
button.style.right = "10px";
button.style.top = "50%";
button.style.transform = "translateY(-50%)";
button.style.cursor = "pointer";
button.style.zIndex = "9999";
button.style.width = "50px";
button.style.height = "50px";
button.style.borderRadius = "50%";
button.style.display = "flex";
button.style.alignItems = "center";
button.style.justifyContent = "center";
button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="m12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035q-.016-.005-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427q-.004-.016-.017-.018m.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093q.019.005.029-.008l.004-.014l-.034-.614q-.005-.018-.02-.022m-.715.002a.02.02 0 0 0-.027.006l-.006.014l-.034.614q.001.018.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01z"/><path fill="#cccccc" class="icon-path" d="M11.298 2.195a2 2 0 0 1 1.232-.055l.172.055l7 2.625a2 2 0 0 1 1.291 1.708l.007.165v5.363a9 9 0 0 1-4.709 7.911l-.266.139l-3.354 1.677a1.5 1.5 0 0 1-1.198.062l-.144-.062l-3.354-1.677a9 9 0 0 1-4.97-7.75l-.005-.3V6.693a2 2 0 0 1 1.145-1.808l.153-.065zM12 4.068L5 6.693v5.363a7 7 0 0 0 3.635 6.138l.235.123L12 19.882l3.13-1.565a7 7 0 0 0 3.865-5.997l.005-.264V6.693zm3.433 4.561a1 1 0 0 1 1.497 1.32l-.083.094l-5.234 5.235a1.1 1.1 0 0 1-1.46.085l-.096-.085l-2.404-2.404a1 1 0 0 1 1.32-1.498l.094.083l1.768 1.768z"/></g></svg>`;
document.body.appendChild(button);
let longPressTimeout;
let isDragging = false;
button.addEventListener("mousedown", (e) => {
e.preventDefault();
longPressTimeout = setTimeout(() => {
isDragging = true;
document.addEventListener('mousemove', mouseMoveHandler);
}, 500);
});
button.addEventListener("click", (e) => {
if (!isDragging) {
toggleFunctionality();
}
});
document.addEventListener("mouseup", () => {
clearTimeout(longPressTimeout);
isDragging = false;
document.removeEventListener('mousemove', mouseMoveHandler);
});
function mouseMoveHandler(e) {
if (isDragging) {
e.preventDefault();
button.style.left = (e.clientX - button.offsetWidth / 2) + 'px';
button.style.top = (e.clientY - button.offsetHeight / 2) + 'px';
}
}
button.addEventListener('touchstart', (e) => {
e.preventDefault();
longPressTimeout = setTimeout(() => {
isDragging = true;
document.addEventListener('touchmove', touchMoveHandler);
}, 500);
});
button.addEventListener("touchend", () => {
clearTimeout(longPressTimeout);
if (!isDragging) {
toggleFunctionality();
}
isDragging = false;
document.removeEventListener('touchmove', touchMoveHandler);
});
function touchMoveHandler(e) {
if (isDragging) {
e.preventDefault();
button.style.left = (e.touches[0].clientX - button.offsetWidth / 2) + 'px';
button.style.top = (e.touches[0].clientY - button.offsetHeight / 2) + 'px';
}
}
function toggleFunctionality() {
isEnabled = !isEnabled;
localStorage.setItem(toggleKey, isEnabled);
updateButtonState(isEnabled);
if (isEnabled) {
enableLinkOpening();
} else {
disableLinkOpening();
}
}
function updateButtonState(newState) {
const paths = button.querySelectorAll('.icon-path');
paths.forEach(path => {
path.setAttribute('fill', newState ? "#FDB122" : "#cccccc");
});
}
function enableLinkOpening() {
document.addEventListener('click', handleLinkClick, true);
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) {
const newLinks = node.querySelectorAll('a');
newLinks.forEach(newLink => {
newLink.addEventListener('click', handleLinkClick, true);
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
function disableLinkOpening() {
document.removeEventListener('click', handleLinkClick, true);
}
function isExcludedElement(element) {
return element.closest('input, textarea, button, .modal, .dialog, [data-popup], [data-modal], .sidebar, .sidebar *, .dropdown, .dropdown *, [role="button"], [onclick], [data-ignore-link], .fixed-container *');
}
function handleLinkClick(event) {
const isLinkElement = event.target.closest('a');
if (!isLinkElement) return;
if (isExcludedElement(event.target)) {
return;
}
const url = new URL(event.currentTarget.href);
const clickedElementInfo = {
tag: event.target.tagName,
classes: event.target.className,
id: event.target.id,
href: url.href
};
const newTab = window.open(url.href, '_blank');
setTimeout(() => {
try {
if (!newTab || newTab.closed || typeof newTab.closed === 'undefined' || newTab.document.title === "无标题") {
ignoredElements.add(JSON.stringify(clickedElementInfo));
localStorage.setItem(ignoredElementsKey, JSON.stringify(Array.from(ignoredElements)));
console.log(`Added to ignored elements: ${JSON.stringify(clickedElementInfo)}`);
}
} catch (e) {
ignoredElements.add(JSON.stringify(clickedElementInfo));
localStorage.setItem(ignoredElementsKey, JSON.stringify(Array.from(ignoredElements)));
console.log(`Added to ignored elements (cross-origin error): ${JSON.stringify(clickedElementInfo)}`);
}
}, 500);
event.preventDefault();
event.stopImmediatePropagation();
}
updateButtonState(isEnabled);
if (isEnabled) {
enableLinkOpening();
}
})();