您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Shows an inline lightbox for images instead of opening a new tab/etc.
// ==UserScript== // @name TweetDeck lightboxes // @namespace https://yal.cc/ // @version 1.0 // @description Shows an inline lightbox for images instead of opening a new tab/etc. // @author YellowAfterlife // @match https://tweetdeck.twitter.com/* // @grant none // ==/UserScript== /* jshint eqnull:true */ /* jshint esversion:6 */ (function() { 'use strict'; let css = document.createElement("style"); css.type = "text/css"; css.innerHTML = ` .prf-header .imgxis-badge { position: absolute; left: 4px; top: 4px; width: 16px; height: 16px; border-radius: 50%; background: rgba(255, 255, 255, 0.7); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4); } .imgxis-panner { background: rgba(41,47,51,0.9); position: absolute; left: 0; width: 100%; top: 0; height: 100%; z-index: 350; } .imgxis-panner, .imgxis-panner img { cursor: move; } .imgxis-panner.zoomed, .imgxis-panner.zoomed img { -ms-interpolation-mode: nearest-neighbor; image-rendering: optimizeSpeed; image-rendering: -moz-crisp-edges; image-rendering: -webkit-optimize-contrast; image-rendering: -o-crisp-edges; image-rendering: pixelated; } .imgxis-panner img, .imgxis-panner video { position: absolute; transform-origin: top left !important; margin: 0; background: none !important; } .imgxis-panner::after { content: attr(zoom); color: white; display: inline-block; padding: 1px 2px; background: rgba(0, 0, 0, 0.4); position: absolute; top: 0; left: 0; } .imgxis-panner.odd-zoom::after { color: #ffe040; } .imgxis-panner iframe { position: absolute; top: 0; bottom: 0; left: 50px; width: calc(100% - 100px); height: 100%; height: 100vh; border: 0; } `; document.body.appendChild(css); // let img0 = document.createElement("img"); // original let img0failed = false; img0.onerror = (_) => { img0failed = true }; // let img1 = document.createElement("img"); // full-sized let img1failed = false; img1.onerror = (_) => { img1failed = true }; // let video = document.createElement("video"); video.loop = true; video.controls = true; video.autoplay = true; let videoLoaded = false; video.oncanplay = (_) => { videoLoaded = true }; let isVideo = false; // let iframe = document.createElement("iframe"); iframe.setAttribute // let panner = document.createElement("div"); panner.className = "imgxis-panner"; panner.appendChild(img0); panner.appendChild(img1); panner.appendChild(video); panner.appendChild(iframe); let panX = 0, panY = 0, panZ = 0, panM = 1; let panWidth = 0, panHeight = 0; let panIdle = false; // whether nothing happened to it yet let zoomed = false; // function panUpdate() { let pz = (panM >= 1); if (pz != zoomed) { zoomed = pz; let cl = panner.classList; if (pz) cl.add("zoomed"); else cl.remove("zoomed"); } panner.setAttribute("zoom", `${panM*100|0}%`); let tf = `matrix(${panM},0,0,${panM},${-panX|0},${-panY|0})`; img0.style.transform = tf; img1.style.transform = tf; video.style.transform = tf; } // function panWheel(e) { panIdle = false; panner.classList.remove("odd-zoom"); let d = e.deltaY; d = (d < 0 ? 1 : d > 0 ? -1 : 0) * 0.5; let zx = e.pageX, zy = e.pageY; let prev = panM; if (Math.abs(panZ - Math.round(panZ * 2) / 2) > 0.001) { panZ = d > 0 ? Math.ceil(panZ * 2) / 2 : Math.floor(panZ * 2) / 2; } else panZ = Math.round((panZ + d) * 2) / 2; panM = Math.pow(2, panZ); let f = panM / prev; panX = (zx + panX) * f - zx; panY = (zy + panY) * f - zy; panUpdate(); } var mouseX = 0, mouseY = 0, mouseDown = false; function panMove(e) { let lastX = mouseX; mouseX = e.pageX; let lastY = mouseY; mouseY = e.pageY; if (mouseDown) { panX -= (mouseX - lastX); panY -= (mouseY - lastY); panUpdate(); } } function panPress(e) { panIdle = false; panMove(e); if (e.target == panner) { e.preventDefault(); setTimeout(() => panHide(), 1); } else if (e.which != 3) { e.preventDefault(); mouseDown = true; } } function panRelease(e) { panMove(e); mouseDown = false; } function panKeyDown(e) { if (e.keyCode == 27/* ESC */) { e.preventDefault(); e.stopPropagation(); panHide(); return false; } } panner.addEventListener("mousemove", panMove); panner.addEventListener("mousedown", panPress); panner.addEventListener("mouseup", panRelease); panner.addEventListener("wheel", panWheel); // function panFit(lw, lh) { let iw = window.innerWidth, ih = window.innerHeight; panZ = 0; if (lw < iw && lh < ih) { // zoom in (up to 800%) for (let k = 0; k < 3; k++) { if (lw * 2 < iw && lh * 2 < ih) { panZ += 1; lw *= 2; lh *= 2; } } } else { while (lw > iw || lh > ih) { // zoom out until fits panZ -= 1; lw /= 2; lh /= 2; } } panM = Math.pow(2, panZ); panX = -(iw - lw) / 2; panY = -(ih - lh) / 2; console.log(iw, ih, lw, lh, panX, panY, panM); } // let panCheckInt2 = null; function panCheck2() { if (isVideo) { let lw = video.offsetWidth, lh = video.offsetHeight; if (!videoLoaded) return; clearInterval(panCheckInt2); panCheckInt2 = null; panFit(lw, lh); console.log(lw, lh, panX, panY); } else { let lw = img1.width, lh = img1.height; if (lw <= 0 || lh <= 0) return; clearInterval(panCheckInt2); panCheckInt2 = null; // if (img1failed) return; img1.style.visibility = ""; if (/*panIdle*/true) { // it makes sense to rescale to original if idle, but looks odd panZ -= Math.log2(Math.max(lw / img0.width, lh / img0.height)); panM = Math.pow(2, panZ); if (Math.abs((panZ * 2) % 1) > 0.001) { panner.classList.add("odd-zoom"); } } else panFit(lw, lh); img0.width = lw; img0.height = lh; } panUpdate(); } // let panCheckInt = null; function panCheck() { let lw = img0.width, lh = img0.height; if (lw <= 0 || lh <= 0) return; if (img0failed) return; //console.log(lw, lh, img0failed); clearInterval(panCheckInt); panCheckInt = null; panFit(lw, lh); img0.style.visibility = ""; panUpdate(); if (img1.src) { panCheckInt2 = setInterval(panCheck2, 25); } } // var panTickInt = null; function panTick() { let lastWidth = panWidth; panWidth = window.innerWidth; let lastHeight = panHeight; panHeight = window.innerHeight; if (panWidth != lastWidth || panHeight != lastHeight) { panX -= (panWidth - lastWidth) / 2; panY -= (panHeight - lastHeight) / 2; panUpdate(); } } function panShow(url, orig, mode) { isVideo = mode == 1; img1.style.display = img0.style.display = (mode == 0 ? "" : "none"); video.style.display = mode == 1 ? "" : "none"; iframe.style.display = mode == 2 ? "" : "none"; if (mode == 2) { iframe.src = url; } else if (mode == 1) { video.src = url; videoLoaded = false; } else { img0.removeAttribute("width"); img0.removeAttribute("height"); img1.src = url; img0failed = false; img0.src = orig; img1failed = false; img1.style.visibility = "hidden"; img0.style.visibility = "hidden"; } document.querySelector(".application").appendChild(panner); document.addEventListener("keydown", panKeyDown); panWidth = window.innerWidth; panHeight = window.innerHeight; if (mode == 2) return; panTickInt = setInterval(panTick, 100); if (isVideo) { panCheckInt2 = setInterval(panCheck2, 25); } else { panCheckInt = setInterval(panCheck, 25); } } function panHide() { video.src = ""; img0.src = ""; img1.src = ""; iframe.src = ""; panner.parentElement.removeChild(panner); document.removeEventListener("keydown", panKeyDown); clearInterval(panTickInt); panTickInt = null; if (panCheckInt != null) { clearInterval(panCheckInt); panCheckInt = null; } if (panCheckInt2 != null) { clearInterval(panCheckInt2); panCheckInt2 = null; } } // function panGetShow(url, orig, mode) { if (mode == null) mode = 0; return (e) => { e.preventDefault(); e.stopPropagation(); panShow(url, orig, mode); }; } // function getBackgroundUrl(el) { let url = el.style.backgroundImage; if (url == null) return url; return url.slice(4, -1).replace(/"/g, ""); } setInterval(() => { // pictures: for (let query of [ `.js-media-preview-container:not(.is-video):not(.is-gif) .js-media-image-link:not(.imgxis-link)`, `.media-image-container .js-media-image-link:not(.imgxis-link)`, ]) for (let el of document.querySelectorAll(query)) { el.classList.add("imgxis-link"); let url, orig; if (/(?:.jpg|.png|.jpeg|.gif)$/g.test(el.href)) { url = el.href; orig = el.getAttribute("data-original-url") || (url + ":small"); } else { let img = el.querySelector("img"); if (img == null) { orig = getBackgroundUrl(el); if (orig == null) continue; } else { orig = img.src; } url = orig.replace(/(?:\:small|\:large|\?format=.+)$/g, ":orig"); } el.addEventListener("click", panGetShow(url, orig)); } // profile backgrounds: for (let el of document.querySelectorAll(`.prf-header:not(.imgxis-link`)) { el.classList.add("imgxis-link"); let orig = getBackgroundUrl(el); if (orig == null) continue; let url = orig.replace(/\/web$/g, "/1500x500"); let a = document.createElement("a"); a.className = "imgxis-badge"; a.href = "#"; a.title = "View profile background"; a.addEventListener("click", panGetShow(url, orig)); el.appendChild(a); } // avatars: for (let el of document.querySelectorAll(`.prf-img img.avatar:not(.imgxis-link)`)) { el.classList.add("imgxis-link"); let orig = el.src; let url = orig.replace(/(?:_bigger|_normal)\./g, "."); el.addEventListener("click", panGetShow(url, orig)); } // gifs: for (let el of document.querySelectorAll(`.media-item-gif:not(.imgxis-link)`)) { el.classList.add("imgxis-link"); let url = el.src; el.parentElement.addEventListener("click", panGetShow(url, url, 1)); } // videos: for (let el of document.querySelectorAll(`.media-preview-container.is-video:not(.imgxis-link)`)) { el.classList.add("imgxis-link"); let link = el.querySelector("a"); let par = el.parentElement; let url = link && link.href; if (url) url = url.replace("https://www.", "https://"); if (!url) { // } else if (url.startsWith("https://t.co") || url.startsWith("https://twitter.com")) { url = null; } else if (url.startsWith("https://youtube.com/")) { var mt = /v=([\w-]+)/.exec(url); if (mt) url = `https://www.youtube.com/embed/${mt[1]}?autoplay=1`; } if (!url) while (par) { if (par.tagName == "ARTICLE" || par.classList.contains("quoted-tweet")) { url = par.getAttribute("data-tweet-id"); if (url) url = `https://twitter.com/i/videos/tweet/${url}?auto_buffer=1&autoplay=1`; break; } else par = par.parentElement; } if (url) el.parentElement.addEventListener("click", panGetShow(url, url, 2)); } }, 250); })();