TweetDeck lightboxes

Shows an inline lightbox for images instead of opening a new tab/etc.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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);
})();