Warning: fopen(/www/sites/update.greasyfork.icu/index/store/forever/6106fbf3c8025c1dacb98ac5985ae5b1.js): failed to open stream: No space left on device in /www/sites/update.greasyfork.icu/index/scriptControl.php on line 65
// ==UserScript== // @name SE Preview on hover // @description Shows preview of the linked questions/answers on hover while Ctrl key is held // @version 0.0.2 // @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 // @downloadURL none // ==/UserScript== /* jshint lastsemic:true, multistr:true, laxbreak:true, -W030, -W041, -W084 */ const PREVIEW_DELAY = 100; const COLORS = { question: { back: '80, 133, 195', fore: '#265184', }, answer: { back: '112, 195, 80', fore: '#3f7722', }, }; 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 = ``; 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(${COLORS.question.back}); } #SEpreview.SEpreviewIsAnswer { border-color: rgb(${COLORS.answer.back}); } `); 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', ``); 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'); var answers; // = answerIdMatch ? null : $$(doc, '.answer'); $$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( '', '' ); afterBodyHtml = ''; } $$(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] = ''); stylesToGet.delete(e.href); maybeRender(); }, }); } }); } function maybeRender() { if (stylesToGet.size) return; if (!preview) { preview = document.createElement('iframe'); preview.id = 'SEpreview'; preview.sandbox = 'allow-same-origin allow-scripts'; } preview.classList.toggle('SEpreviewIsAnswer', !!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(` ${headHtml} ${title}
${bodyHtml}
${!answers ? '' : '
' + answers.length + ' answer' + (answers.length > 1 ? 's' : '') + '
'} ${afterBodyHtml} `); pvDoc.close(); pvDoc.addEventListener('mouseover', retainMainScrollPos); if (answers) $(pvDoc, '#SEpreviewAnswers').addEventListener('click', revealAnswers); } } function retainMainScrollPos(e) { var scrollPos = {x:scrollX, y:scrollY}; document.addEventListener('scroll', preventScroll); document.addEventListener('mouseover', releaseScrollLock); function preventScroll(e) { scrollTo(scrollPos.x, scrollPos.y); } function releaseScrollLock(e) { document.removeEventListener('mouseout', releaseScrollLock); document.removeEventListener('scroll', preventScroll); } } 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()); }