Skip to content

Instantly share code, notes, and snippets.

@ameboide
Created July 9, 2026 21:53
Show Gist options
  • Select an option

  • Save ameboide/0341e235bedda3f232f164f0359a1d50 to your computer and use it in GitHub Desktop.

Select an option

Save ameboide/0341e235bedda3f232f164f0359a1d50 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Embiggen Images
// @namespace ameboide
// @author ameboide
// @description Shows CSS-resized images (and some thumbnails) in their original size by holding Shift while hovering over the image (or links to images)
// @include *
// @version 1.13
// @grant none
// ==/UserScript==
var show = false;
var keep = false;
var testing = null;
var keyShow = 16; //shift
var keyKeep = 18; //alt
//match common thumbnail urls
var reThumb = [
[/(wikipedia\/commons\/)thumb\/(.+)\/\d+px[^\/]+$/, '$1$2'], //wikipedia
[/(-|_)\d+x\d+(?=\.\w+$)|:thumb$|(_mini|_reasonably_small|_normal|_bigger)(?=\.\w+$)|\?(w|h)=\d+&(w|h)=\d+$/, ''],
[/(\.gravatar\.com\/avatar\/\w+)(\?.+)?$/, '$1?s=529'], //gravatar
[/(\.gravatar\.com\/avatar)\.php\?.*gravatar_id=(\w+).*$/, '$1/$2?s=529'], //gravatar alt
[/\._.+(\.\w+)$/, '$1'], //imdb, amazon
[/\/mini\//, '/large/'], //twitpic
[/_500(\.\w+)$/, '_1280$1'], //tumblr
[/(fbcdn.+_)q(\.\w+)$/, '$1n$2'], //facebook avatars
[/^.+(\?|&)url=([^&]+)(&.+)?/, '$2'], //proxy (g+)
[/(\/photos\/images\/)list\//, '$1newsfeed/'], //knowyourmeme
[/eldefinido.cl\/portadas\/650\//, 'eldefinido.cl/portadas/1200/'],
[/(yt3.ggpht.com\/ytc\/.+=)(s\d+-)/, '$1s512-'], //youtube
[/\bs=\d+\b/, ''],
[/(boardgamearena.net\/.+\/avatar\/.+default-\d+)_\d+.jpg?/, '$1.jpg'],
[/(boardgamearena.net\/.+\/avatar\/.+)_\d+.jpg?/, '$1_184.jpg'],
];
//match links to images
var reImg = [
[/^(?!.+wikipedia.org\/wiki\/\w+:).+\.(png|jpg|jpeg|gif|svg)(\?.+)?$/, '$&'],
[/^.+&imgurl=([^&]+).*$/, function(x, url){ //google images
return decodeURIComponent(decodeURI(url));
}],
[/(imgur\.com\/\w+)$/, 'i.$&.jpg']
];
//match css urls
var reCssUrl = /^url\("(.+)"\)$/;
var lastTarget = null;
var testingClass = 'testing-embiggenable';
var div, bigImg, testImg;
function init(){
div = document.createElement('div');
div.style.position = 'fixed';
div.style.top = 0;
div.style.left = 0;
div.style.maxWidth = '100%';
div.style.maxHeight = '100%';
div.style.overflow = 'auto';
div.style.display = 'none';
div.style.zIndex = (1<<30)*2-1; //max int32
document.body.appendChild(div);
bigImg = document.createElement('img');
div.appendChild(bigImg);
bigImg.addEventListener('click', function(e){
fitToWindow(bigImg);
});
var css = document.createElement('style');
css.innerHTML = '.'+testingClass+'{border:1px dotted red !important;}';
div.appendChild(css);
testImg = document.createElement('img');
testImg.addEventListener('load', function(){
if(show && testing){
bigImg.src = testImg.src;
bigImg.setAttribute('style', '');
div.style.display = '';
}
endTesting();
}, true);
testImg.addEventListener('error', endTesting, true);
testImg.addEventListener('abort', endTesting, true);
}
function testTarget(element, src){
if(!div) init();
endTesting();
element.classList.add(testingClass);
testing = element;
testImg.src = src;
}
function endTesting(hide){
if(hide){
show = false;
if(div) div.style.display = 'none';
}
if(testing){
testing.classList.remove(testingClass);
testing = null;
}
}
function testReList(img, src, reList){
for(var i=0; i<reList.length; i++){
var re = reList[i];
if (re[0].test(src)){
testTarget(img, src.replace(re[0], re[1]));
return true;
}
}
return false;
}
function processTarget(img){
var parent = img.parentNode;
if(img.tagName == 'IMG'){
if(img.width != img.naturalWidth || img.height != img.naturalHeight){
testTarget(img, img.src);
}
if(parent.tagName == 'A' && testReList(img, parent.href, reImg)){
return true;
}
return testReList(img, img.src, reThumb);
}
if(img.tagName == 'A' && testReList(img, img.href, reImg)){
return true;
}
while(true){
var style = window.getComputedStyle(img);
if(reCssUrl.test(style.backgroundImage)){
var src = style.backgroundImage.replace(reCssUrl, '$1');
if(style.backgroundSize != 'auto auto'){
testTarget(img, src);
}
return testReList(img, src, reThumb);
}
if(img.offsetWidth != parent.offsetWidth || img.offsetHeight != parent.offsetHeight){
return false;
}
//fake target?
img = parent;
parent = img.parentNode;
}
}
function fitToWindow(img){
var widthRatio = img.naturalWidth / window.innerWidth;
var heightRatio = img.naturalHeight / window.innerHeight;
var ratio = Math.max(widthRatio, heightRatio);
if(ratio > 1){
if(img.style.width){
img.style.width = '';
}
else{
img.style.width = Math.floor(img.naturalWidth / ratio) + 'px';
}
}
}
window.addEventListener('keydown', function(e){
if(e.keyCode == keyShow && !show){
show = true;
if(lastTarget){
processTarget(lastTarget);
}
}
else if(e.keyCode == keyKeep){
// keep = true;
}
}, true);
window.addEventListener('keyup', function(e){
if(e.keyCode == keyKeep){
keep = false;
}
else if(!keep && e.keyCode == keyShow){
endTesting(true);
}
}, true);
window.addEventListener('mouseover', function(e){
if(e.target == bigImg) return;
lastTarget = e.target;
if(show){
processTarget(lastTarget);
}
}, true);
//left click + right click
var b1 = false, stopClick = 0;
window.addEventListener('mousedown', function(e){
if(b1 && e.buttons == 3){
if(show){
endTesting(true);
}
else{
show = true;
lastTarget = e.target;
processTarget(lastTarget);
}
stopClick = 3;
}
b1 = e.buttons == 1;
}, true);
window.addEventListener('click', function(e){
var b = e.button || 1;
if(b & stopClick){
e.preventDefault();
e.stopPropagation();
stopClick ^= b;
}
}, true);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment