您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Embeds PDFs directly in the page instead of downloading, with fallback and cleanup
// ==UserScript== // @name PDF Viewer Embedder // @namespace * // @version 1.7 // @author zinchaiku // @description Embeds PDFs directly in the page instead of downloading, with fallback and cleanup // @match *://*/* // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; let currentBlobUrl = null; function showPDF(srcUrl, isBlob = false) { const existing = document.getElementById('tampermonkey-pdf-viewer'); if (existing) existing.remove(); const overlay = document.createElement('div'); overlay.id = 'tampermonkey-pdf-viewer'; overlay.style.position = 'fixed'; overlay.style.top = 0; overlay.style.left = 0; overlay.style.width = '100vw'; overlay.style.height = '100vh'; overlay.style.background = 'rgba(0,0,0,0.8)'; overlay.style.zIndex = 99999; overlay.style.display = 'flex'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; const iframe = document.createElement('iframe'); iframe.src = srcUrl; iframe.style.width = '80vw'; iframe.style.height = '90vh'; iframe.style.border = 'none'; iframe.style.background = '#fff'; overlay.appendChild(iframe); // Fallback if iframe fails to load (e.g. X-Frame-Options) iframe.onerror = () => { overlay.remove(); if (isBlob && currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } window.open(srcUrl, '_blank'); }; // Close button const closeBtn = document.createElement('button'); closeBtn.textContent = 'Close PDF'; closeBtn.style.position = 'absolute'; closeBtn.style.top = '20px'; closeBtn.style.right = '200px'; closeBtn.style.zIndex = 100000; closeBtn.style.padding = '2px 6px'; closeBtn.style.fontSize = '1.2em'; closeBtn.onclick = () => { overlay.remove(); if (isBlob && currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } }; overlay.appendChild(closeBtn); // "Open in New Tab" button const newTabBtn = document.createElement('button'); newTabBtn.textContent = 'Open in New Tab'; newTabBtn.style.position = 'absolute'; newTabBtn.style.top = '20px'; newTabBtn.style.right = '40px'; newTabBtn.style.zIndex = 100000; newTabBtn.style.padding = '2px 6px'; newTabBtn.style.fontSize = '1.2em'; newTabBtn.onclick = () => { window.open(srcUrl, '_blank'); }; overlay.appendChild(newTabBtn); document.body.appendChild(overlay); // Track for cleanup if (isBlob) { currentBlobUrl = srcUrl; } else { currentBlobUrl = null; } } function isPDFLink(href) { try { const url = new URL(href, window.location.href); return url.pathname.toLowerCase().endsWith('.pdf'); } catch { return false; } } function attachPDFInterceptors() { document.querySelectorAll('a[href]').forEach(link => { const href = link.getAttribute('href'); if (!href || link.dataset.tmPdfBound) return; const isWrapper = href.includes('_download.html'); const isDirectPDF = isPDFLink(href); if (isWrapper || isDirectPDF) { link.dataset.tmPdfBound = "1"; link.addEventListener('click', function (e) { e.preventDefault(); if (isDirectPDF) { showPDF(link.href); } else { fetch(link.href, { credentials: 'include' }) .then(res => { const contentType = res.headers.get('Content-Type') || ''; if (contentType.includes('pdf')) { return res.blob().then(blob => { const blobUrl = URL.createObjectURL(blob); showPDF(blobUrl, true); }); } else { window.location.href = link.href; } }) .catch(err => { alert("Could not open PDF: " + err); window.location.href = link.href; }); } }); } }); } window.addEventListener('keydown', (e) => { if (e.key === 'Escape') { const viewer = document.getElementById('tampermonkey-pdf-viewer'); if (viewer) { viewer.remove(); if (currentBlobUrl) { URL.revokeObjectURL(currentBlobUrl); currentBlobUrl = null; } } } }); attachPDFInterceptors(); const observer = new MutationObserver(attachPDFInterceptors); observer.observe(document.body, { childList: true, subtree: true }); })();