UNPKG

xvideos-boss-mode

Version:

Boss mode for xvideos.com. Copy the code into your browser console to enable.

328 lines (297 loc) 10.3 kB
(() => { const globalStyle = ` body:not(.normal) { --shadow-bg: rgba(255,255,255,0.98); --hole-x: -500%; --hole-y: -500%; --mask-size: 0px; --fake-content: ""; --shadow-filter: blur(10px); } body:not(.normal)::after { content: var(--fake-content); display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: var(--shadow-bg); z-index: 99999999; pointer-events: none; overflow: hidden; mask-image: radial-gradient(circle var(--mask-size) at var(--hole-x) var(--hole-y), #ffffff00 10%, #ffffffff 100%); mask-repeat: no-repeat; mask-composite: exclude; backdrop-filter: var(--shadow-filter); transition all 0.2s; } body.mask::after { } body.clip::after { clip-path: var(--clipped-area); } body.clip #html5video { filter: contrast(50%) opacity(50%); } body.mask img { user-drag: none; -webkit-user-drag: none; } body:not(.normal) #fake-loading {width: 100%; height: 100%: display: flex; justify-content: center; align-items: center;} body:not(.normal) #fake-loading::after {content: "Loading"; display: block; font-size: 20px; color: #398;} `; const ClickStrategy = Object.freeze({ NONE: 0, NEXT: 1, RESET: 2, PREV: 3, RESUME: 4, NORMAL: 5, }); const MAX_MASK_SIZE = 500; const DEFAULT_MASK_SIZE = 0; const DEFAULT_MASK_OPACITY = 0.98; const DEFAULT_MASK_MODE = 0; function bindEvent(ctx, name, callback, ...otherArgs) { if (!ctx) { return; } if ( ctx instanceof Document || ctx instanceof DocumentFragment || ctx === globalThis ) { otherArgs[0] = { ...otherArgs[0], passive: false }; } globalThis._registeredCallbacks.push({ ctx, name, callback, opts: otherArgs, }); return ctx.addEventListener(name, callback, ...otherArgs); } function initRuntime() { globalThis.onerror = () => {}; // 记录所有的 callback。 globalThis._registeredCallbacks = globalThis._registeredCallbacks || []; globalThis._registeredCallbacks.forEach(({ name, callback, ctx }) => { ctx.removeEventListener(name, callback); }); bindEvent(window, "DOMContentLoaded", () => { initDOM(); initStyle(); initEvents(); }); } function initDOM() {} function initStyle() { const style = document.createElement("style"); style.id = "shadow"; style.innerHTML = globalStyle; document.querySelector("#shadow")?.remove(); document.head.appendChild(style); document.body.style.setProperty("--fake-content", getFakeLoadingSVG()); resetMask(document.body); } let maskModeIndex = DEFAULT_MASK_MODE, maskSize = DEFAULT_MASK_SIZE, maskOpacity = DEFAULT_MASK_OPACITY, // 鼠标移出等特殊情况下 forceMask 是 true,此时临时重置。 forceMask = false; const maskModes = ["mask", "clip"]; function updateMaskStyles(el) { el.classList.remove("normal"); maskModes.forEach((cls, i) => { if (i === (forceMask ? 0 : maskModeIndex)) { switch (cls) { case "clip": el.style.setProperty("--clipped-area", generateCurrentClipPath()); default: el.style.setProperty( "--mask-size", `${forceMask ? DEFAULT_MASK_MODE : maskSize}px` ); el.style.setProperty( "--shadow-bg", forceMask ? "#fff" : `rgba(255,255,255,${maskOpacity})` ); el.style.setProperty( "--shadow-filter", forceMask ? "blur(10px)" : `blur(${10.2 * maskOpacity}px)` ); } el.classList.add(cls); } else { el.classList.remove(cls); } }); } function normalMode(el) { el.classList.add("normal"); } function resetMask(el) { maskModeIndex = DEFAULT_MASK_MODE; maskSize = DEFAULT_MASK_SIZE; maskOpacity = DEFAULT_MASK_OPACITY; updateMaskStyles(el); } function toggleMaskMode(el, step, forceValue) { maskModeIndex = typeof forceValue === "number" ? forceValue : typeof step === "number" ? (maskModeIndex + maskModes.length + step) % maskModes.length : 0; updateMaskStyles(el); } function setMaskSize(el, delta, forceValue) { maskSize = typeof forceValue === "number" ? forceValue : typeof delta === "number" ? Math.min(Math.max(maskSize + delta, 0), MAX_MASK_SIZE) : 0; updateMaskStyles(el); } function setMaskOpacity(el, delta, forceValue) { maskOpacity = typeof forceValue === "number" ? forceValue : typeof delta === "number" ? Math.min(Math.max(maskOpacity + delta, 0), 1) : 0; updateMaskStyles(el); } function generateCurrentClipPath() { const player = document.querySelector("#html5video"); if (player) { const { clientWidth: vw, clientHeight: vh } = document.body; const { x, y, right, bottom } = player.getBoundingClientRect(); return `path(evenodd, "M 0 0 L ${vw} 0 L ${vw} ${vh} L 0 ${vh} L 0 0 M ${x} ${y} L ${right} ${y} L ${right} ${bottom} L ${x} ${bottom} L ${x} ${y} Z")`; } return ""; } function preventEventDefault(e) { e.stopPropagation(); e.preventDefault(); } function updateMousePosition(el, e) { const x = e.clientX; const y = e.clientY; el.style.setProperty("--hole-x", `${x}px`); el.style.setProperty("--hole-y", `${y}px`); } function initEvents() { const body = document.body; bindEvent(document, "pointermove", (e) => { if (body.classList.contains("normal")) { return; } if (maskOpacity > 0 && maskSize > 0) { updateMousePosition(body, e); } }); bindEvent(document.querySelector(".progress-bar"), "pointermove", (e) => { if (body.classList.contains("normal")) { return; } if (maskOpacity > 0 && maskSize > 0) { updateMousePosition(body, e); } }); bindEvent(document, "pointerdown", (e) => { const strategy = getClickStrategyByPointerEvent(e); switch (strategy) { case ClickStrategy.NEXT: preventEventDefault(e); return toggleMaskMode(body, 1); case ClickStrategy.PREV: preventEventDefault(e); return toggleMaskMode(body, -1); case ClickStrategy.RESET: preventEventDefault(e); return resetMask(body); case ClickStrategy.NORMAL: preventEventDefault(e); return normalMode(body); } }); bindEvent(document, "contextmenu", (e) => { if (getClickStrategyByPointerEvent(e) !== ClickStrategy.NONE) { preventEventDefault(e); } }); bindEvent(document, "wheel", (e) => { if (body.classList.contains("normal")) { return; } const { clientX: x, deltaY, altKey, ctrlKey } = e; const { clientWidth: width } = document.documentElement; const depth = getClickDepth(e); if (altKey || (depth <= 3 && x < width / 3)) { preventEventDefault(e); updateMousePosition(body, e); setMaskSize(body, deltaY); } else if (ctrlKey || (depth <= 3 && x > (width * 2) / 3)) { preventEventDefault(e); updateMousePosition(body, e); setMaskOpacity(body, deltaY > 0 ? 0.02 : deltaY < 0 ? -0.02 : 0); } }); bindEvent(document, "scroll", (e) => { if (body.classList.contains("normal")) { return; } if (body.classList.contains("clip")) { body.style.setProperty("--clipped-area", generateCurrentClipPath()); } }); bindEvent(document, "pointerleave", () => { if (body.classList.contains("normal")) { return; } forceMask = true; updateMaskStyles(body); }); bindEvent(document, "pointerenter", () => { if (body.classList.contains("normal")) { return; } forceMask = false; updateMaskStyles(body); }); bindEvent(window, "resize", () => { if (body.classList.contains("normal")) { return; } document.body.style.setProperty("--fake-content", getFakeLoadingSVG()); }); } function getClickDepth(e) { const { clientX: x, clientY: y } = e; const targets = document.elementsFromPoint(x, y); return targets.length; } function getClickStrategyByPointerEvent(e) { const { buttons, shiftKey, ctrlKey, altKey } = e; const depth = getClickDepth(e); if (buttons !== 1 && buttons !== 2) { if (altKey) { // 如果按住了 Alt 键,且鼠标的非左右按钮被按下,则暂时退出 boss 模式: return ClickStrategy.NORMAL; } return ClickStrategy.RESET; } if (document.body.classList.contains("normal")) { return ClickStrategy.NONE; } // 如果在页面空白处按鼠标,或者按住了 Ctrl 键,则切换。 if (ctrlKey || depth <= 3) { switch (buttons) { case 1: return shiftKey ? ClickStrategy.PREV : ClickStrategy.NEXT; case 2: return shiftKey ? ClickStrategy.NEXT : ClickStrategy.PREV; } } return ClickStrategy.NONE; } function getFakeLoadingSVG() { const { clientWidth: width, clientHeight: height } = document.documentElement; const fontSize = height / 20; const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="100%" height="100%"> <style>text {font-family: sans-serif; font-size: ${fontSize}px; font-weight: bold;}</style> <text fill="#489" alignment-baseline="hanging"> Temporarily Unavailable <animate attributeName="x" values="0;${width - fontSize * 11.56};0" dur="43s" repeatCount="indefinite" /> <animate attributeName="y" values="0;${height - fontSize};0" dur="73s" repeatCount="indefinite" /> </text> </svg>`; const urlPart = svg .replace(/\>\s+/gm, ">") .replace(/\s+(?=\<)/gm, "") .replace(/\s+/gm, " ") .replace(/[<>":/=%#; ]/gm, (c) => `%${c.charCodeAt(0).toString(16)}`); return `url("data:image/svg+xml,${urlPart}")`; } initRuntime(); })();