Greasy Fork is available in English.
随心所欲,不离不弃。我在此地,不曾离去。我希望停留在此地,但我也想看着锅里的,吃着碗里的。
当前为
// ==UserScript==
// @name 小窗预览
// @version 3.8.2
// @description 随心所欲,不离不弃。我在此地,不曾离去。我希望停留在此地,但我也想看着锅里的,吃着碗里的。
// @match *://*/*
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_info
// @namespace http://greasyfork.icu/users/217852
// ==/UserScript==
(function() {
'use strict';
// State to track dragging, popup status, and overlay visibility
const state = {
isDragging: false,
linkToPreload: null,
popupWindow: null,
acrylicOverlay: null,
overlayVisible: false,
preloadElement: null, // Track the created preload element
};
// Configuration settings with defaults
const config = {
windowWidth: GM_getValue('windowWidth', 870),
windowHeight: GM_getValue('windowHeight', 530),
blurIntensity: GM_getValue('blurIntensity', 20),
blurEnabled: GM_getValue('blurEnabled', true),
closeOnMouseClick: GM_getValue('closeOnMouseClick', true),
closeOnScroll: GM_getValue('closeOnScroll', true),
transitionDuration: 220, // 300ms fade-in/fade-out
};
// Website-specific configurations
const specificSites = {
"bilibili.com": { windowWidth: 1000, windowHeight: 565, enableLeftClickPopup: true },
"douyin.com": { windowWidth: 1000, windowHeight: 500, enableLeftClickPopup: true },
"x.com": { windowWidth: 1000, windowHeight: 700, enableLeftClickPopup: true },
"youtube.com": { windowWidth: 1040, windowHeight: 700, enableLeftClickPopup: false }
};
// Utility to delay operations (e.g., to allow preload to complete)
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Preload a link using a <link rel="prefetch"> element
async function preloadLink(link) {
try {
// Remove any existing preload element to avoid duplication
removePreloadedLink();
const preloadElement = document.createElement('link');
preloadElement.rel = 'prefetch';
preloadElement.href = link;
preloadElement.as = 'document';
preloadElement.onload = () => console.log(`Prefetch successful: ${link}`);
preloadElement.onerror = () => console.log(`Prefetch failed: ${link}`);
document.head.appendChild(preloadElement);
state.preloadElement = preloadElement;
await delay(1); // Ensure the prefetch operation starts
} catch (error) {
console.error('Error in prefetch operation:', error);
}
}
// Remove the preloaded link element from the DOM
function removePreloadedLink() {
if (state.preloadElement) {
state.preloadElement.remove();
state.preloadElement = null;
}
}
// Create an acrylic overlay with blur effect and fade-in transition
function createAcrylicOverlay() {
const acrylicOverlay = document.createElement('div');
Object.assign(acrylicOverlay.style, {
position: 'fixed',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '9999',
backdropFilter: config.blurEnabled ? `blur(${config.blurIntensity}px)` : 'none',
opacity: '0',
transition: `opacity ${config.transitionDuration}ms ease-in-out`,
});
if (config.closeOnMouseClick) {
acrylicOverlay.addEventListener('click', handleAcrylicOverlayClick);
}
document.body.appendChild(acrylicOverlay);
// Trigger fade-in effect
requestAnimationFrame(() => {
acrylicOverlay.style.opacity = '1';
state.overlayVisible = true;
});
return acrylicOverlay;
}
// Handle click on the acrylic overlay (close popup)
function handleAcrylicOverlayClick(event) {
if (event.target === state.acrylicOverlay) {
closePopupWindow();
}
}
// Remove the acrylic overlay with fade-out transition
function removeAcrylicOverlay() {
if (state.acrylicOverlay && state.overlayVisible) {
state.acrylicOverlay.style.opacity = '0';
setTimeout(() => {
if (state.acrylicOverlay) {
state.acrylicOverlay.remove();
state.acrylicOverlay = null;
state.overlayVisible = false;
}
}, config.transitionDuration);
}
}
// Open a popup window centered on the screen
function openPopupWindow(link, width = config.windowWidth, height = config.windowHeight) {
const screenLeft = (window.screen.width - width) / 2;
const screenTop = (window.screen.height - height) / 3;
if (!state.popupWindow || state.popupWindow.closed) {
state.acrylicOverlay = createAcrylicOverlay();
state.popupWindow = window.open(link, '_blank', `width=${width},height=${height},left=${screenLeft},top=${screenTop}`);
state.popupWindowChecker = setInterval(checkPopupWindowStatus, 200);
}
}
// Close the popup window and cleanup
function closePopupWindow() {
if (state.popupWindow && !state.popupWindow.closed) {
state.popupWindow.close();
state.popupWindow = null;
removeAcrylicOverlay();
removePreloadedLink();
window.removeEventListener('scroll', closePopupOnScroll);
}
}
// Check if the popup window is still open; remove overlay if closed
function checkPopupWindowStatus() {
if (state.popupWindow && state.popupWindow.closed) {
removeAcrylicOverlay();
clearInterval(state.popupWindowChecker);
}
}
// Close the popup window when the page is scrolled
function closePopupOnScroll() {
closePopupWindow();
}
// Check if the current site matches a specific configuration
function getSiteConfig() {
const hostname = window.location.hostname;
return specificSites[Object.keys(specificSites).find(domain => hostname.includes(domain))];
}
// Register menu commands for the script
function registerMenuCommand(label, action) {
return GM_registerMenuCommand(label, () => {
action();
updateMenuCommands();
});
}
// Menu commands for configuring the script
const menuCommands = [
{ label: `模糊旧时刻 (${config.blurEnabled ? '开' : '关'})`, action: toggleBlurEffect },
{ label: `模糊的强弱 (${config.blurIntensity})`, action: setBlurIntensity },
{ label: `轻点关闭否 (${config.closeOnMouseClick ? '开' : '关'})`, action: toggleCloseOnMouseClick },
{ label: `滚动关闭否 (${config.closeOnScroll ? '开' : '关'})`, action: toggleCloseOnScroll },
{ label: `不变的大小 (${config.windowWidth}x${config.windowHeight})`, action: setWindowSize },
];
// Toggle blur effect on/off
function toggleBlurEffect() {
config.blurEnabled = !config.blurEnabled;
GM_setValue('blurEnabled', config.blurEnabled);
}
// Prompt user to set blur intensity
function setBlurIntensity() {
const intensity = prompt('输入模糊强度(0-10):', config.blurIntensity);
if (intensity !== null) {
config.blurIntensity = parseInt(intensity, 10);
GM_setValue('blurIntensity', config.blurIntensity);
}
}
// Toggle the option to close the popup on mouse click
function toggleCloseOnMouseClick() {
config.closeOnMouseClick = !config.closeOnMouseClick;
GM_setValue('closeOnMouseClick', config.closeOnMouseClick);
}
// Toggle the option to close the popup on scroll
function toggleCloseOnScroll() {
config.closeOnScroll = !config.closeOnScroll;
handleScrollCommand();
GM_setValue('closeOnScroll', config.closeOnScroll);
}
// Handle scroll event based on user settings
function handleScrollCommand() {
if (config.closeOnScroll) {
window.addEventListener('scroll', closePopupOnScroll, { once: true });
} else {
window.removeEventListener('scroll', closePopupOnScroll);
}
}
// Prompt user to set window size
function setWindowSize() {
const size = prompt(`输入小窗口宽度x高度(例如: 870x530):`, `${config.windowWidth}x${config.windowHeight}`);
if (size !== null) {
const [width, height] = size.split('x').map(val => parseInt(val.trim(), 10));
if (!isNaN(width) && !isNaN(height)) {
config.windowWidth = width;
config.windowHeight = height;
GM_setValue('windowWidth', config.windowWidth);
GM_setValue('windowHeight', config.windowHeight);
if (state.popupWindow && !state.popupWindow.closed) {
state.popupWindow.resizeTo(config.windowWidth, config.windowHeight);
}
}
}
}
// Update the menu commands when configuration changes
function updateMenuCommands() {
menuCommands.forEach((command) => {
registerMenuCommand(command.label, command.action);
});
}
// Initialize menu commands
updateMenuCommands();
// Event listeners for drag-and-drop and other interactions
document.body.addEventListener('dragstart', async (event) => {
const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a');
if (linkElement) {
state.isDragging = true;
state.linkToPreload = linkElement.href;
await preloadLink(state.linkToPreload);
if (config.closeOnScroll) {
window.addEventListener('scroll', closePopupOnScroll, { once: true });
}
}
});
document.body.addEventListener('dragend', () => {
if (state.isDragging && state.linkToPreload) {
state.isDragging = false;
openPopupWindow(state.linkToPreload);
state.linkToPreload = null;
}
});
document.body.addEventListener('mousedown', async (event) => {
const linkElement = event.target.tagName === 'A' ? event.target : event.target.closest('a');
if (event.button === 0 && linkElement) { // Left mouse button
const siteConfig = getSiteConfig();
if (siteConfig && siteConfig.enableLeftClickPopup && !event.shiftKey) {
// Custom behavior for specific sites: Prefetch and open popup
await preloadLink(linkElement.href);
openPopupWindow(linkElement.href, siteConfig.windowWidth, siteConfig.windowHeight);
event.preventDefault();
}
}
});
document.body.addEventListener('wheel', () => {
if (config.closeOnScroll) {
closePopupWindow();
}
});
document.body.addEventListener('click', (event) => {
if (event.target === state.acrylicOverlay) {
removeAcrylicOverlay();
}
});
})();