您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Spoof mobile signals, enforce viewport, fix media queries, optional m. redirects
// ==UserScript== // @name Force Mobile View (iOS Safari, XS Max hard mode) // @description Spoof mobile signals, enforce viewport, fix media queries, optional m. redirects // @namespace https://gf.qytechs.cn/users/1508709 // @version 1.0.0 // @author https://github.com/sitien173 // @match *://*/* // @inject-into page // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-start // ==/UserScript== /* eslint-disable */ /* global GM_getValue, GM_setValue */ (function () { // ---- Config (tweak if you want) ----------------------------------------- const MOBILE_UA = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1"; // iPhone XS Max CSS metrics (portrait): 414x896, DPR=3 const CSS_WIDTH = 414; const CSS_HEIGHT = 896; const DPR = 3; const VIEWPORT_CONTENT = "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"; const MOBILE_REDIRECTS = [ { from: /^www\.youtube\.com$/i, to: "m.youtube.com" }, { from: /^twitter\.com$/i, to: "mobile.twitter.com" }, { from: /^www\.facebook\.com$/i, to: "m.facebook.com" }, { from: /^en\.wikipedia\.org$/i, to: "m.wikipedia.org" }, { from: /^www\.reddit\.com$/i, to: "m.reddit.com" }, ]; const host = location.hostname; const perSiteKey = (k) => `forceMobile:${host}:${k}`; const isRedirectEnabled = () => GM_getValue(perSiteKey("redirectEnabled"), true); const setRedirectEnabled = (v) => GM_setValue(perSiteKey("redirectEnabled"), !!v); if (typeof GM_registerMenuCommand === "function") { GM_registerMenuCommand( `[${isRedirectEnabled() ? "✓" : " "}] Mobile redirect on this site`, () => { setRedirectEnabled(!isRedirectEnabled()); location.reload(); } ); } // ---- 0) Optional: auto-redirect common desktop → mobile hosts ----------- (function maybeRedirect() { if (!isRedirectEnabled()) return; const rule = MOBILE_REDIRECTS.find((r) => r.from.test(host)); if (rule && host.toLowerCase() !== rule.to.toLowerCase()) { const url = new URL(location.href); url.host = rule.to; location.replace(url.toString()); } })(); // ---- Helper to (attempt to) redefine read-only props where possible ----- function defineRW(obj, prop, getter) { try { const d = Object.getOwnPropertyDescriptor(obj, prop); if (!d || d.configurable) { Object.defineProperty(obj, prop, { get: getter, configurable: true }); } } catch (_) { /* ignore */ } } // ---- 1) Spoof mobile signals that many SPAs inspect --------------------- try { defineRW(Navigator.prototype, "userAgent", () => MOBILE_UA); defineRW(Navigator.prototype, "appVersion", () => MOBILE_UA); defineRW(Navigator.prototype, "platform", () => "iPhone"); defineRW(Navigator.prototype, "vendor", () => "Apple Computer, Inc."); defineRW(Navigator.prototype, "maxTouchPoints", () => 5); // userAgentData (if present) if ("userAgentData" in Navigator.prototype) { defineRW(Navigator.prototype, "userAgentData", () => ({ mobile: true, platform: "iOS", brands: [{ brand: "Safari", version: "17" }], toString() { return "[object NavigatorUAData]"; } })); } // Screen metrics & DPR defineRW(Screen.prototype, "width", () => CSS_WIDTH); defineRW(Screen.prototype, "height", () => CSS_HEIGHT); defineRW(window, "devicePixelRatio", () => DPR); // Legacy signals some libs still check try { Object.defineProperty(window, "orientation", { value: 0, configurable: true }); } catch (_) {} try { window.chrome ??= { runtime: {} }; } catch (_) {} } catch (_) { /* ignore */ } // ---- 2) Make CSS media queries think we're on touch / non-hover --------- try { const origMM = window.matchMedia.bind(window); window.matchMedia = function (q) { const query = String(q).trim().toLowerCase(); // Force common mobile signals if (/\(hover:\s*none\)/.test(query) || /\(any-hover:\s*none\)/.test(query)) { return Object.assign(origMM(q), { matches: true }); } if (/\(hover:\s*hover\)/.test(query) || /\(any-hover:\s*hover\)/.test(query)) { return Object.assign(origMM(q), { matches: false }); } if (/\(pointer:\s*coarse\)/.test(query) || /\(any-pointer:\s*coarse\)/.test(query)) { return Object.assign(origMM(q), { matches: true }); } if (/\(pointer:\s*fine\)/.test(query) || /\(any-pointer:\s*fine\)/.test(query)) { return Object.assign(origMM(q), { matches: false }); } return origMM(q); }; } catch (_) { /* ignore */ } // ---- 3) Aggressively enforce a mobile viewport -------------------------- function setViewportMeta(node) { if (!node) return; node.setAttribute("name", "viewport"); node.setAttribute("content", VIEWPORT_CONTENT); } function ensureViewport() { let vp = document.querySelector('meta[name="viewport"]'); if (!vp) { vp = document.createElement("meta"); setViewportMeta(vp); (document.head || document.documentElement).prepend(vp); } else { setViewportMeta(vp); } } // run ASAP… ensureViewport(); // …and keep it enforced even if the site changes it later const headObs = new MutationObserver((muts) => { for (const m of muts) { if (m.type === "childList") { m.addedNodes.forEach((n) => { if (n.nodeType === 1 && n.matches?.('meta[name="viewport"]')) setViewportMeta(n); }); } if (m.type === "attributes" && m.target?.matches?.('meta[name="viewport"]')) { setViewportMeta(m.target); } } }); const startObs = () => { const head = document.head || document.documentElement; headObs.observe(head, { childList: true, subtree: true, attributes: true, attributeFilter: ["content", "name"] }); }; if (document.readyState === "loading") { startObs(); document.addEventListener("DOMContentLoaded", ensureViewport, { once: true }); } else { startObs(); ensureViewport(); } // ---- 4) Safety net: CSS to prevent desktop widths from leaking in ------- const style = document.createElement("style"); style.textContent = ` html, body { max-width: 100vw !important; overflow-x: hidden !important; } /* Prevent frameworks from locking viewport to 980px etc. */ body { width: auto !important; } `; (document.head || document.documentElement).appendChild(style); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址