您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows preview of the linked questions/answers on hover while Ctrl key is held
当前为
- // ==UserScript==
- // @name SE Preview on hover
- // @description Shows preview of the linked questions/answers on hover while Ctrl key is held
- // @version 0.0.1
- // @author wOxxOm
- // @namespace wOxxOm.scripts
- // @license MIT License
- // @match *://*.stackoverflow.com/*
- // @match *://*.superuser.com/*
- // @match *://*.serverfault.com/*
- // @match *://*.askubuntu.com/*
- // @match *://*.stackapps.com/*
- // @match *://*.mathoverflow.net/*
- // @match *://*.stackexchange.com/*
- // @require https://greasyfork.org/scripts/12228/code/setMutationHandler.js
- // @grant GM_addStyle
- // @grant GM_xmlhttpRequest
- // @connect stackoverflow.com
- // @connect superuser.com
- // @connect serverfault.com
- // @connect askubuntu.com
- // @connect stackapps.com
- // @connect mathoverflow.net
- // @connect stackexchange.com
- // @connect cdn.sstatic.net
- // @run-at document-end
- // @noframes
- // ==/UserScript==
- const PREVIEW_DELAY = 100;
- var xhr;
- var preview;
- var previewLink;
- var previewTimer;
- var previewCSScache = {};
- var hovering = {stoppedAt: {x:0, y:0}};
- const rx = getURLregexForMatchedSites();
- const thisPageBaseUrl = (location.href.match(rx) || [])[0];
- const thisPageBaseUrlShort = thisPageBaseUrl ? thisPageBaseUrl.replace('/questions/', '/q/') : undefined;
- const stylesOverride = `<style>
- body, html {
- min-width: unset!important;
- box-shadow: none!important;
- }
- html, body {
- background: unset!important;;
- }
- body {
- display: flex;
- flex-direction: column;
- height: 100%;
- }
- #SEpreviewTitle {
- all: unset;
- display: block;
- padding: 20px 30px;
- font-weight: bold;
- font-size: 20px;
- line-height: 1.3;
- background-color: rgba(80, 133, 195, 0.37);
- color: #265184;
- }
- #SEpreviewTitle:hover {
- text-decoration: underline;
- }
- #SEpreviewBody {
- padding: 30px!important;
- overflow: auto;
- }
- #SEpreviewBody::-webkit-scrollbar {
- background-color: rgba(80, 133, 195, 0.1);
- }
- #SEpreviewBody::-webkit-scrollbar-thumb {
- background-color: rgba(80, 133, 195, 0.2);
- }
- #SEpreviewBody::-webkit-scrollbar-thumb:hover {
- background-color: rgba(80, 133, 195, 0.3);
- }
- #SEpreviewBody::-webkit-scrollbar-thumb:active {
- background-color: rgba(80, 133, 195, 0.75);
- }
- body.SEpreviewAnswer #SEpreviewTitle {
- background-color: rgba(112, 195, 80, 0.37);
- color: #3f7722;
- }
- }
- body.SEpreviewAnswer #SEpreviewBody::-webkit-scrollbar {
- background-color: rgba(112, 195, 80, 0.1);
- }
- body.SEpreviewAnswer #SEpreviewBody::-webkit-scrollbar-thumb {
- background-color: rgba(112, 195, 80, 0.2);
- }
- body.SEpreviewAnswer #SEpreviewBody::-webkit-scrollbar-thumb:hover {
- background-color: rgba(112, 195, 80, 0.3);
- }
- body.SEpreviewAnswer #SEpreviewBody::-webkit-scrollbar-thumb:active {
- background-color: rgba(112, 195, 80, 0.75);
- }
- </style>`;
- GM_addStyle(`
- #SEpreview {
- all: unset;
- box-sizing: content-box;
- width: 720px; /* 660px + 30px + 30px */
- height: 33%;
- min-height: 200px;
- position: fixed;
- transition: opacity .25s ease-in-out;
- right: 0;
- bottom: 0;
- padding: 0;
- margin: 0;
- background: white;
- box-shadow: 0 0 100px rgba(0,0,0,0.5);
- z-index: 999999;
- border: 8px solid rgb(80, 133, 195);
- }
- #SEpreview.SEpreviewAnswer {
- border-color: rgb(112, 195, 80);
- }
- `);
- processExistingAndSetMutationHandler('a', onLinkAdded);
- /**************************************************************/
- function onLinkAdded(links) {
- for (var i = 0, link; (link = links[i++]); ) {
- if (rx.test(link.href) &&
- !link.href.startsWith(thisPageBaseUrl) &&
- !link.href.startsWith(thisPageBaseUrlShort)
- ) {
- link.removeAttribute('title');
- link.addEventListener('mouseover', onLinkHovered);
- }
- }
- }
- function onLinkHovered(e) {
- if (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)
- return;
- previewLink = this;
- previewLink.addEventListener('mousemove', onLinkMouseMove);
- previewLink.addEventListener('mouseout', abortPreview);
- previewLink.addEventListener('mousedown', abortPreview);
- restartPreviewTimer();
- }
- function onLinkMouseMove(e) {
- if (Math.abs(hovering.stoppedAt.x - e.clientX) < 2 && Math.abs(hovering.stoppedAt.y - e.clientY) < 2)
- return;
- hovering.stoppedAt.x = e.clientX;
- hovering.stoppedAt.y = e.clientY;
- restartPreviewTimer();
- }
- function restartPreviewTimer() {
- clearTimeout(previewTimer);
- previewTimer = setTimeout(() => {
- previewTimer = 0;
- if (!previewLink.matches(':hover'))
- return;
- downloadPage();
- }, PREVIEW_DELAY);
- }
- function abortPreview() {
- previewLink.removeEventListener('mousemove', onLinkMouseMove);
- previewLink.removeEventListener('mouseout', abortPreview);
- previewLink.removeEventListener('mousedown', abortPreview);
- clearTimeout(previewTimer);
- previewTimer = setTimeout(() => {
- previewTimer = 0;
- if (preview && !preview.matches(':hover'))
- hidePreview();
- }, 500);
- if (xhr)
- xhr.abort();
- }
- function downloadPage() {
- xhr = GM_xmlhttpRequest({
- method: 'GET',
- url: previewLink.href,
- onload: showPreview,
- });
- }
- function showPreview(data) {
- var doc = new DOMParser().parseFromString(data.responseText, 'text/html');
- if (!doc || !doc.head) {
- console.error(GM_info.script.name, 'empty document received:', data);
- return;
- }
- if (!$(doc, 'base'))
- doc.head.insertAdjacentHTML('afterbegin', `<base href="${data.finalUrl}">`);
- var answerIdMatch = data.finalUrl.match(/questions\/.+?\/(\d+)/);
- var postId = answerIdMatch ? '#answer-' + answerIdMatch[1] : '#question';
- var post = $(doc, postId + ' .post-text');
- if (!post)
- return;
- var title = $(doc, 'meta[property="og:title"]').content;
- var comments = $(doc, postId + ' .comments');
- $$remove(doc, 'script, .post-menu');
- var externalsReady = [stylesOverride];
- var stylesToGet = new Set();
- var afterBodyHtml = '';
- fetchExternals();
- maybeRender();
- function fetchExternals() {
- var codeBlocks = $$(post, 'pre code');
- if (codeBlocks.length) {
- codeBlocks.forEach(e => e.parentElement.classList.add('prettyprint'));
- externalsReady.push(
- '<script> StackExchange = {}; </script>',
- '<script src="https://cdn.sstatic.net/Js/prettify-full.en.js"></script>'
- );
- afterBodyHtml = '<script> prettyPrint(); </script>';
- }
- $$(doc, 'style, link[rel="stylesheet"]').forEach(e => {
- if (e.localName == 'style')
- externalsReady.push(e.outerHTML);
- else if (e.href in previewCSScache)
- externalsReady.push(previewCSScache[e.href]);
- else {
- stylesToGet.add(e.href);
- GM_xmlhttpRequest({
- method: 'GET',
- url: e.href,
- onload: data => {
- externalsReady.push(previewCSScache[e.href] = '<style>' + data.responseText + '</style>');
- stylesToGet.delete(e.href);
- maybeRender();
- },
- });
- }
- });
- }
- function maybeRender() {
- if (stylesToGet.size)
- return;
- initPreview();
- preview.classList.toggle('SEpreviewAnswer', !!answerIdMatch);
- document.body.appendChild(preview);
- var headHtml = externalsReady.join('');
- var bodyHtml = [post.parentElement, comments].map(e => e ? e.outerHTML || e : '').join('');
- var pvDoc = preview.contentDocument;
- pvDoc.open();
- pvDoc.write(`
- <head>${headHtml}</head>
- <body${answerIdMatch ? ' class="SEpreviewAnswer"' : ''}>
- <a id="SEpreviewTitle" href="${data.finalUrl}">${title}</a>
- <div id="SEpreviewBody">${bodyHtml}</div>
- ${afterBodyHtml}
- </body>`);
- pvDoc.close();
- }
- }
- function initPreview() {
- if (preview)
- return;
- preview = document.createElement('iframe');
- preview.id = 'SEpreview';
- preview.sandbox = 'allow-same-origin allow-scripts';
- preview.addEventListener('mouseenter', retainMainScrollPos);
- }
- function retainMainScrollPos(e) {
- var scrollPos;
- document.addEventListener('scroll', onScroll, {passive: false});
- preview.addEventListener('mouseleave', onMouseLeave);
- function onScroll(e) {
- if (scrollPos)
- scrollTo(scrollPos.x, scrollPos.y);
- }
- function onMouseLeave(e) {
- scrollPos = null;
- preview.removeEventListener('mouseleave', onMouseLeave);
- document.removeEventListener('scroll', onScroll, {passive: false});
- }
- }
- function hidePreview() {
- preview.remove();
- }
- function getURLregexForMatchedSites() {
- return new RegExp('https?://(\\w*\\.)*(' + GM_info.script.matches.map(m =>
- m.match(/^.*?\/\/\W*(\w.*?)\//)[1].replace(/\./g, '\\.')
- ).join('|') + ')/(questions|q|a)/\\d+');
- }
- function $(node__optional, selector) {
- // or $(selector) {
- return (node__optional || document).querySelector(selector || node__optional);
- }
- function $$(node__optional, selector) {
- // or $$(selector) {
- return (node__optional || document).querySelectorAll(selector || node__optional);
- }
- function $$remove(node__optional, selector) {
- // or $$remove(selector) {
- (node__optional || document).querySelectorAll(selector || node__optional)
- .forEach(e => e.remove());
- }