// ==UserScript== // @name 💡WebPreview - 信息直达 // @namespace https://ez118.github.io/ // @version 1.7.1 // @description 支持搜索引擎的搜索结果快速预览。点击搜索结果旁的预览按钮,即可在速览窗中快速查看目标网站所含图片、链接、标题大纲、文本。 // @author ZZY_WISU // @match *://*/* // @connect * // @license GNU GPLv3 // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAYAAAAe2bNZAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAKVJREFUeJztlskNwCAMBGkkbaa/9JMi8orEg8P2+tgHlngRZgfEkdbAuu7n/RvKguvIlMhooVaZ7fdZSy7OiRZS86OEzNzVgB46axauuiQiKUddEpYipA0IE0LuEnchBOgqg87OdXU8QGLG7khGyQxzqWQsIA8ZCESxgXtg9tglkOLSs4Bp3qeSl7tUhE6mD9T2hcqU/eXRy2j7jszZMxQyMzGU9QGdyuqQn9XecwAAAABJRU5ErkJggg== // @run-at document-end // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant window.onurlchange // @require https://update.greasyfork.org/scripts/503290/1426017/ultra-slim-jquery.js // @require https://unpkg.com/turndown@7.2.0/dist/turndown.js // @require https://unpkg.com/marked@14.0.0/marked.min.js // ==/UserScript== const contentEleSelList = { "blog.csdn.net": "#article_content", "zhuanlan.zhihu.com": ".Post-RichTextContainer", "jingyan.baidu.com": "#format-exp", "www.bilibili.com": "#article-content", "zhidao.baidu.com": "#qb-content", "www.cnblogs.com": "#topics", "www.sohu.com": "#mp-editor" }; /* 储存特定网站内容优化数据(文章主体的父元素) */ const mediaPrevSupport = [ { "site": "https://v.youku.com/v_show/*.html", "player": "https://player.youku.com/embed/*", "type": "video" }, { "site": "https://v.qq.com/x/page/*.html", "player": "https://v.qq.com/txp/iframe/player.html?vid=*", "type": "video" }, { "site": "https://www.bilibili.com/video/BV*/", "player": "https://www.bilibili.com/blackboard/html5mobileplayer.html?bvid=*", "type": "video" }, { "site": "https://www.bilibili.com/video/av*/", "player": "https://www.bilibili.com/blackboard/html5mobileplayer.html?aid=*", "type": "video" }, { "site": "https://www.youtube.com/watch?v=*", "player": "https://www.youtube.com/embed/*", "type": "video" }, { "site": "https://music.163.com/#/song?id=*", "player": "https://music.163.com/outchain/player?type=2&id=*&auto=0&height=66", "type": "music" }, { "site": "https://music.163.com/song?id=*", "player": "https://music.163.com/outchain/player?type=2&id=*&auto=0&height=66", "type": "music" }, { "site": "https://open.spotify.com/track/*", "player": "https://open.spotify.com/embed/track/*", "type": "music" }, { "site": "https://music.apple.com/cn/song/*", "player": "https://embed.music.apple.com/cn/album/*", "type": "music" }, { "site": "https://music.youtube.com/watch?v=*", "player": "https://www.youtube.com/embed/*", "type": "music" } ]; /* 储存支持预览播放视频/预览试听音乐的网站及其嵌入播放器链接 */ function runAsync(url,send_type,data_ry) { var p = new Promise((resolve, reject)=> { GM_xmlhttpRequest({ method: send_type, url: url, headers: {"Content-Type": "application/x-www-form-urlencoded;charset=utf-8"}, data: data_ry, onload: function(response){resolve(response.responseText);}, onerror: function(response){reject("[WebPrvw] 请求失败");} }); }); return p; } function judgeMediaSupport(url){ var jflag = null; mediaPrevSupport.forEach(function(item, index) { if (url.includes(item.site.split("*")[0])) { jflag = { "state": true, "data": item }; } }); return jflag || { "state": false, "data": null }; } function getOutline(markdown) { /* 文章大纲提取 */ var lines = markdown.split('\n'); var titleElementArr = []; var preTitleElement = null; lines.forEach(line => { const match = line.match(/^(#{1,6})\s+(.*)/); if (match) { var id = Math.random().toString(36).substr(2, 7); var level = 1; var tag = match[1].length; var title = match[2]; if (preTitleElement != null) { var tagPre = preTitleElement.tag; var levelPre = preTitleElement.level; if (tagPre > tag) { level = levelPre - (tagPre - tag); } else if (tagPre < tag) { level = levelPre + 1; } else { level = levelPre; } } if (title.trim().length > 0) { var titleElement = { tag: tag, title: title, level: level, id: id }; titleElementArr.push(titleElement); preTitleElement = titleElement; } } }); return titleElementArr; } function getWebContents(txt) { var links = []; var images = []; var content = ""; var outline = []; /* 获取所有链接 */ txt.replace(/]*href=['"]([^'"]+)['"][^>]*>/g, function(match, capture){ links.push(capture); }); /* 获取所有图片 */ txt.replace(/]*src=['"]([^'"]+)['"][^>]*>/g, function(match, capture){ images.push(capture); }); /* 去掉影响转换的标签 */ var markdown = txt.replace(/.*?<\/script>/gis, "") .replace(/.*?<\/style>/gis, "") .replace(/.*?<\/nav>/gis, ""); /* html转markdown */ const turndownService = new TurndownService(); markdown = turndownService.turndown(markdown); /* markdown转html */ content = marked.parse(markdown); /* 获取大纲信息 */ try{ outline = getOutline(markdown);} catch{ console.log("[WebPrvw] 无法解析大纲") } var final_data = {"link": links, "image": images, "content": content, "outline": outline}; return final_data; } function openReader(url) { /* 打开阅读器 */ /* 阅读器加载提示 */ var closeBtn = $("#userscript-closeBtn"); var previewReader = $("#userscript-webPreviewReader"); previewReader.html("

正在载入...
" + url + "

"); previewReader.show(); closeBtn.show(); /* 判断当前链接是支持预览的视频网站,并作出对应处理 */ var showMedia = judgeMediaSupport(url); if(showMedia.state){ /* 被支持的视频网站的处理 */ var origUrl = url; var frameUrl = ""; var mediaType = (showMedia.data.type == "video") ? "视频" : "音乐"; url = url.replace(showMedia.data.site.split("*")[0], ""); url = url + "?#"; url = url.split("#")[0].split("?")[0]; url = url.replace(showMedia.data.site.split("*")[1], ""); frameUrl = showMedia.data.player.replace("*", url); previewReader.html(`
`); $("#FadeInContainer").show(); } else { /* 普通网站的处理 */ runAsync(url, "GET", "").then((result)=>{ return result; }).then(function(result){ /* 源数据处理(csdn存在利用img的onerror属性注入xss脚本的行为) */ result = result.replace(/]*src\s*=\s*["']{2}[^>]*>/gi, ''); /* 删除src为空的标签 */ result = result.replace(/]*)onerror\s*=\s*(['"]?[^'">]*['"]?)([^>]*)>/gi, ''); /* 删除所有img标签的onerror属性 */ /* 对指定网站进行内容过滤,指定元素获取 */ let orig_result_backup = result; const domain = url.split("/")[2]; if (contentEleSelList[domain]) { try { const selector = contentEleSelList[domain]; result = $(result).find(selector).html(); } catch (e) { console.log("[WebPrvw] 无法对特定网站进行内容优化") } } if (!result) { result = orig_result_backup; } /* 调用解析网页 */ let reslist = getWebContents(result); let linkhtml = "", imghtml = "", outlinehtml = ""; /* 处理链接列表 */ reslist.link.forEach(link_tmp => { if(link_tmp.includes("//")){ linkhtml += " 🔗 " + link_tmp + "
"; } }); /* 处理图片列表 */ reslist.image.forEach(image => { imghtml += ""; }); /* 处理大纲 */ reslist.outline.forEach(outlineItem => { let space = ""; for(let j = 1; j < outlineItem.level; j++){ space += "  "; } outlinehtml += space + "+ " + outlineItem.title + "
"; }); /* 将所有结果添加进阅读器,并显示 */ previewReader.html(` `); /* 隐藏不存在的项 */ if(reslist.image.length == 0) { $(".ImageList").hide(); } if(reslist.link.length == 0) { $(".LinkList").hide(); } if(reslist.outline.length == 0) { $(".OutlineShow").hide(); } $("#FadeInContainer").show(); }); } /* 执行结束 */ } /* ======[ 搜索结果分析 ]===== */ /* * 自动判断当前元素下是否 具有搜索结果特征 * 解释:判断一个父元素下存在 大于等于5个的 具有相同class的 子元素。 * 该函数用于统计当前元素的子元素的各个class的数量,若其中存在一个class的数量大于5次,则判断为搜索结果 */ function checkSearchResults(parentElement) { var classList = []; var countList = []; for(let i = 0; i < parentElement.children.length; i ++) { var child = parentElement.children[i]; var childClass = child.classList; for(let j = 0; j < childClass.length; j ++) { if(classList.indexOf(childClass[j]) !== -1) { /* 对列表中的class出现次数进行计数 */ var p = classList.indexOf(childClass[j]); countList[p] += 1; } else { /* 对列表中未出现的class,插入列表 */ classList.push(childClass[j]); countList.push(0); } } } var countMax = Math.max.apply(null, countList); return (countMax >= 5); } /* 遍历元素 */ function traverseElements(element, callback) { if (!element || !element.children || element.children.length === 0) { return; } var returnCode = callback(element); if (returnCode) { return; } /* 如果返回值为true,则代表该元素已包含搜索结果,无需继续遍历 */ for (let i = 0; i < element.children.length; i++) { traverseElements(element.children[i], callback); } } /* * 运用上述的 遍历函数 和 分析判断函数 实现在满足要求的搜索结果旁插入“速览按钮” * 解释:遍历DOM,获取搜索列表,插入按钮 * 该函数是全程序 分析部分 的起始 */ function initAnalyze() { traverseElements(document.body, function(element) { var status = checkSearchResults(element); if(status) { console.log("[WebPrvw] 存在搜索结果:", status); let resultItems = element.children; for(let i = 0; i < resultItems.length; i ++) { try { let resultItemLink = resultItems[i].getElementsByTagName("a")[0].href; let resultItemTitleEle = resultItems[i].getElementsByTagName("a")[0].parentNode; let resultItemText = resultItems[i].getElementsByTagName("a")[0].innerText; if(resultItemText.length <= 5 || !resultItemLink){ continue; } if(resultItemLink.includes("javascript:") && resultItemLink[0] == "j") { continue; } /* 向每一个搜索结果的标题部分添加按钮 */ let previewBtn = document.createElement("button"); previewBtn.setAttribute("class", "userscript-webPreviewBtn"); previewBtn.setAttribute("link-data", resultItemLink); previewBtn.innerText = "预览"; resultItemTitleEle.appendChild(previewBtn); previewBtn.addEventListener("click", function(evt){ let linkData = previewBtn.getAttribute("link-data"); openReader(linkData); }, true); } catch(e) { //console.log("[ERROR] ELE(" + i + ") \n" + e); } } return true; } else { return false; } }); } /* =========================== */ function init(){ /* 初始化 */ /* 插入样式 */ GM_addStyle(` :root{--bg-color:#FFFFFFAA;--text-color:#386a1f;--border-color:#285a0f;--hover-bg-color:#edf1e5;--active-bg-color:#d7e1cd;--close-btn-bg:#386a1f;--close-btn-text:#FFF;--reader-bg:#fdfdf6;--reader-text-color:#131f0d;--link-color:#386a1f;--link-hover:#487631;--pre-bg-color:#eeeee8;--pre-border-color:#dee5d8;--code-bg-color:#e2e3dd} @media (prefers-color-scheme:dark){:root{--bg-color:#00390a55;--text-color:#7edb7b;--border-color:#7edb7b;--hover-bg-color:#00390aAA;--active-bg-color:#7edb7b;--close-btn-bg:#7edb7b;--close-btn-text:#00390a;--reader-bg:#1a1c19;--reader-text-color:#e2e3dd;--link-color:#7edb7b;--link-hover:#76cd74;--pre-bg-color:#1e201d;--pre-border-color:#424940;--code-bg-color:#42494047}} .userscript-webPreviewBtn{user-select:none;background:var(--bg-color);color:var(--text-color);padding:1px 8px;font-size:12px;font-weight:normal;height:fit-content;margin-left:5px;border-radius:16px;border:1px solid var(--border-color);cursor:pointer} .userscript-webPreviewBtn:hover{background:var(--hover-bg-color)} .userscript-webPreviewBtn:active{background:var(--active-bg-color);color:var(--close-btn-text)} .userscript-webPreviewBtn img{height:16px} .userscript-closeBtn{position:fixed;top:calc(8% + 5px);right:26px;z-index:100000;background:var(--close-btn-bg);color:var(--close-btn-text);padding:8px 20px;margin:6px;border-radius:30px;font-weight:bold;border:0;border-bottom:1px solid var(--border-color);cursor:pointer} .userscript-closeBtn:hover{background:var(--link-hover)} .userscript-webPreviewReader{font-size:medium;text-align:left;position:fixed;top:8vh;right:10px;bottom:0px;z-index:99999;width:35%;height:calc(100vh - 8%);min-width:340px;background:var(--reader-bg);color:var(--reader-text-color);overflow:hidden;box-shadow:0 0 0 1px rgba(0,0,0,.1),0 2px 4px 1px rgba(0,0,0,.18);border-radius:28px 28px 0px 0px} .userscript-webPreviewReader .ShowList{margin:0;padding:0;width:100%;cursor:pointer;color:var(--link-color);user-select:none} .userscript-webPreviewReader .image{height:85px;margin-bottom:8px;margin-right:5px;border-radius:15px;object-fit:contain;max-width:calc(100% - 20px)} .userscript-webPreviewReader .link{text-decoration:none;color:var(--link-color) !important;margin-left:5px} .userscript-webPreviewReader .link:hover{text-decoration:underline} .ImageList,.LinkList,.OutlineShow,.ContentShow{padding:16px;margin:8px;background:var(--code-bg-color);border-radius:30px;overflow:hidden;color:var(--reader-text-color);box-shadow:0 .5px 1.5px 0 rgba(0,0,0,.19),0 0 1px 0 rgba(0,0,0,.039)} .ContentShow img{max-width:92% !important;max-height:85vh !important;position:relative !important;top:0 !important;left:0 !important;border-radius:10px} .ContentShow a{color:var(--link-color);text-decoration:underline 1px solid var(--link-hover);margin:0px 3px} .ContentShow code{font-family:Consolas,Courier,Courier New,monospace} .ContentShow pre{color:var(--reader-text-color);background:var(--pre-bg-color);width:90%;padding:5px;margin:5px 0px;overflow-y:auto;height:fit-content;border:1px solid var(--pre-border-color);border-radius:5px} .ContentShow code:not(pre code){color:var(--reader-text-color);background:var(--code-bg-color);border-radius:0.25rem;padding:.125rem .375rem;line-height:1.75;word-wrap:break-word;border:1px solid var(--pre-border-color)} .userscript-webPreviewReader #videoFrame{width:calc(100% - 10px);height:calc(100% - 120px);background:var(--code-bg-color);border:none;border-radius:30px;margin:5px 5px;box-shadow:0 .5px 1.5px 0 rgba(0,0,0,.19),0 0 1px 0 rgba(0,0,0,.039)} .userscript-webPreviewReader #FadeInContainer{overflow-y:scroll;overflow-x:hidden;border-radius:15px 15px 0px 0px;width:100%;height:100%} `); /* 页面加载时插入DOM */ /* 阅读器 */ if( $("#userscript-webPreviewReader").length == 0 ){ var $previewReader = $('
', { class: 'userscript-webPreviewReader', id: 'userscript-webPreviewReader' }).appendTo('body'); var $closeBtn = $('