Jessie223

disabling animated avatars

Recommended Posts

Jessie223    684

the greasemonkey userscript:

Spoiler

// ==UserScript==
// @name		no animated avatars
// @version		1
// @match		https://forums.kleientertainment.com/*
// @run-at		document-end
// @noframes
// ==/UserScript==

"use strict";

if (window.top !== window.self)
	return;

let canvas = document.createElement("canvas"),
	ctx = canvas.getContext("2d"),
	recognised = {};

let canvas_toBlob = function(img) {
	let _recognised = recognised;

	return function(blob) {
		let url = URL.createObjectURL(blob);

		for (let im of _recognised[img.src])
			im.src = url;

		// remember images that have already been converted,
		// this saves a lot of time
		_recognised[img.src] = url;

		img.src = url;
	};
};

let img2_onload = function(img, sig) {
	let _canvas = canvas,
		_ctx = ctx,
		_canvas_toBlob = canvas_toBlob;

	return function() {
		let w, h;

		if (sig) {
			// signature image, use normal height & width
			w = img.naturalWidth;
			h = img.naturalHeight;
		} else {
			// avatar image, limit height & width to up to 120
			let nw = img.naturalWidth,
				nh = img.naturalHeight;

			if (nw === nh) {
				w = Math.min(120, nw);
				h = w;
			} else if (nw > nh) {
				w = Math.min(120, nw);
				h = Math.round(nh / nw * w);
			} else if (nh > nw) {
				h = Math.min(120, nh);
				w = Math.round(nw / nh * h);
			}
		}

		_canvas.width = w;
		_canvas.height = h;

		_ctx.drawImage(img, 0, 0, w, h);

		_canvas.toBlob(_canvas_toBlob(img), "image/png", 1);
	};
};

let convertimage = function(img, sig) {
	// create a new image element
	// its purpose is to wait for the image to finish loading
	let img2 = new Image();
	img2.onload = img2_onload(img, sig);
	img2.src = img.src;
};

let xhr_onload = function(img, sig) {
	let _convertimage = convertimage,
		_recognised = recognised;

	return function() {
		if (this.readyState !== this.DONE)
			return;

		let mime = this.getResponseHeader("Content-Type");

		if ( // check mime type for gif for apng
			mime === "image/gif" ||
			mime === "image/apng" ||
			// just assume it's animated if the mime type is unknown
			mime === "application/x-unknown" ||
			mime === ""
		) {
			// animated
			_convertimage(img, sig);
			return;
		}

		// not animated
		for (let im of _recognised[img.src])
			im.src = img.src;

		_recognised[img.src] = img.src;
	};
};

// loop through an array of image elements
// to check if they should be converted or not
let convertimages = function(imgs, sig) {
	for (let i = 0, len = imgs.length; i < len; i++) {
		let img = imgs[i];

		if (img.ignore)
			continue;
		else
			img.ignore = true;

		if (img.tagName !== "IMG")
			// not an image element,
			// use its child instead
			img = img.children[0];

		let src = img.src,
			rec = recognised[src];

		if (!rec) {
			// image isn't recognised,
			// proceed normally
		} else if (typeof rec === "string") {
			// image was already converted,
			// skip the whole process
			img.src = rec;
			continue;
		} else {
			// image is being processed
			rec.push(img);
			continue;
		}

		let lower = src.toLowerCase(),
			start = lower.lastIndexOf(".");

		if ( // check for file extension, ignore if it's not animated
			lower.indexOf(".png", start) === start ||
			lower.indexOf(".jpg", start) === start ||
			lower.indexOf(".jpeg", start) === start
		)
			continue;
		else if (
			lower.indexOf(".gif", start) === start ||
			lower.indexOf(".apng", start) === start
		) {
			recognised[src] = [];
			convertimage(img, sig);
			continue;
		}
		recognised[src] = [];

		// this is reached if the available extension is not gif/png/jpg/jpeg
		// desperate measure: use an xhr request to get the mimetype
		let xhr = new XMLHttpRequest();
		xhr.open("HEAD", src, true);
		xhr.onload = xhr_onload(img, sig);
		xhr.send();
	}
};

// look for animated images within signatures
let checksignatures = function(sigs) {
	for (let i = 0, len = sigs.length; i < len; i++) {
		let sig = sigs[i];

		if (sig.ignore)
			continue;
		else
			sig.ignore = true;

		convertimages(sig.getElementsByTagName("IMG"), true);
	}
};

let iframe_onload = function() {
	convertimages(this.document.getElementsByClassName("ipsUserPhoto"), false);
};

// look for animated avatars within iframes
let checkiframes = function(iframes) {
	for (let i = 0, len = iframes.length; i < len; i++) {
		let iframe = iframes[i];

		if (iframe.ignore)
			continue;
		else
			iframe.ignore = true;

		if (iframe.src.indexOf("https://forums.kleientertainment.com") !== 0)
			continue;

		iframe.contentWindow.addEventListener("load", iframe_onload);
	}
};

let avatars = document.getElementsByClassName("ipsUserPhoto"),
	signatures = document.querySelectorAll("#siglimit"),
	iframes = document.getElementsByTagName("IFRAME");

// look for avatars when the main page is loaded
convertimages(avatars, false);
checksignatures(signatures);
checkiframes(iframes);

// look for avatars when new elements get added
new MutationObserver(function() {
	convertimages(avatars, false);
	checksignatures(signatures);
	checkiframes(iframes);
}).observe(document.body, {
	childList: true, subtree: true,
	attributes: false, characterData: false
});

 

i've kinda grown tired of seeing a lot of animated avatars on this forum, so here's my attempt at stopping the eyesore

what this script does is look for animated avatars, then convert the first frame into a png image so it can't be animated (as a bonus, this affects animated signatures too)

example of how it should look like:
image.png.8a9bf81ddde07572aef2542b6a9a8772.png

to use it, install Greasemonkey on your browser, create a new userscript, copy+paste the code above, then save it

tested on:
- extension = Greasemonkey 4.9
- browser = Mozilla Firefox 71.0b3
- os = Debian 11 (bullseye)

i'm not really used to writing javascript, so if you think you can improve or optimise the script, then please tell me

- - - - - - - - - - - - - - - - - - - -

edit: here's a version that's compatible with tampermonkey and google chrome:

Spoiler

// ==UserScript==
// @name		no animated avatars
// @version		1
// @grant		GM.xmlHttpRequest
// @match		https://forums.kleientertainment.com/*
// @connect		klei.com
// @connect		googleusercontent.com
// @connect		gravatar.com
// @connect		wp.com
// @connect		*
// @run-at		document-end
// @noframes
// ==/UserScript==

"use strict";

if (window.top !== window.self)
	return;

let canvas = document.createElement("canvas"),
	ctx = canvas.getContext("2d"),
	recognised = {};

let canvas_toBlob = function(img) {
	let _recognised = recognised;

	return function(blob) {
		let url = URL.createObjectURL(blob);

		for (let im of _recognised[img.src])
			im.src = url;

		// remember images that have already been converted,
		// this saves a lot of time
		_recognised[img.src] = url;

		img.src = url;
	};
};

let convertimage = function(img, sig) {
	let _canvas = canvas,
		_ctx = ctx,
		_canvas_toBlob = canvas_toBlob;

	return function(response) {
		let w, h;

		if (sig) {
			// signature image, use normal height & width
			w = img.naturalWidth;
			h = img.naturalHeight;
		} else {
			// avatar image, limit height & width to up to 120
			let nw = img.naturalWidth,
				nh = img.naturalHeight;

			if (nw === nh) {
				w = Math.min(120, nw);
				h = w;
			} else if (nw > nh) {
				w = Math.min(120, nw);
				h = Math.round(nh / nw * w);
			} else if (nh > nw) {
				h = Math.min(120, nh);
				w = Math.round(nw / nh * h);
			}
		}

		_canvas.width = w;
		_canvas.height = h;

		_ctx.drawImage(response, 0, 0, w, h);

		_canvas.toBlob(_canvas_toBlob(img), "image/png", 1);
	};
};

let xhr_onload = function(img, sig) {
	let _convertimage = convertimage,
		_recognised = recognised;

	return function(xhr) {
		if (xhr.readyState !== xhr.DONE)
			return;

		let mime = xhr.response.type;

		if ( // check mime type for gif for apng
			mime === "image/gif" ||
			mime === "image/apng" ||
			// just assume it's animated if the mime type is unknown
			mime === "application/x-unknown" ||
			mime === ""
		) {
			// animated
			createImageBitmap(xhr.response).then(_convertimage(img, sig));
			return;
		}

		// not animated
		for (let im of _recognised[img.src])
			im.src = img.src;

		_recognised[img.src] = img.src;
	};
};

// loop through an array of image elements
// to check if they should be converted or not
let convertimages = function(imgs, sig) {
	for (let i = 0, len = imgs.length; i < len; i++) {
		let img = imgs[i];

		if (img.ignore)
			continue;
		else
			img.ignore = true;

		if (img.tagName !== "IMG")
			// not an image element,
			// use its child instead
			img = img.children[0];

		let src = img.src,
			rec = recognised[src];

		if (!rec) {
			// image isn't recognised,
			// proceed normally
		} else if (typeof rec === "string") {
			// image was already converted,
			// skip the whole process
			img.src = rec;
			continue;
		} else {
			// image is being processed
			rec.push(img);
			continue;
		}

		let lower = src.toLowerCase(),
			start = lower.lastIndexOf(".");

		if ( // check for file extension, ignore if it's not animated
			lower.indexOf(".png", start) === start ||
			lower.indexOf(".jpg", start) === start ||
			lower.indexOf(".jpeg", start) === start
		)
			continue;

		recognised[src] = [];

		GM.xmlHttpRequest({
			method: "GET",
			url: src,
			responseType: "blob",
			onload: xhr_onload(img, sig)
		});
	}
};

// look for animated images within signatures
let checksignatures = function(sigs) {
	for (let i = 0, len = sigs.length; i < len; i++) {
		let sig = sigs[i];

		if (sig.ignore)
			continue;
		else
			sig.ignore = true;

		convertimages(sig.getElementsByTagName("IMG"), true);
	}
};

let iframe_onload = function() {
	convertimages(this.document.getElementsByClassName("ipsUserPhoto"), false);
};

// look for animated avatars within iframes
let checkiframes = function(iframes) {
	for (let i = 0, len = iframes.length; i < len; i++) {
		let iframe = iframes[i];

		if (iframe.ignore)
			continue;
		else
			iframe.ignore = true;

		if (iframe.src.indexOf("https://forums.kleientertainment.com") !== 0)
			continue;

		iframe.contentWindow.addEventListener("load", iframe_onload);
	}
};

let avatars = document.getElementsByClassName("ipsUserPhoto"),
	signatures = document.querySelectorAll("#siglimit"),
	iframes = document.getElementsByTagName("IFRAME");

// look for avatars when the main page is loaded
convertimages(avatars, false);
checksignatures(signatures);
checkiframes(iframes);

// look for avatars when new elements get added
new MutationObserver(function() {
	convertimages(avatars, false);
	checksignatures(signatures);
	checkiframes(iframes);
}).observe(document.body, {
	childList: true, subtree: true,
	attributes: false, characterData: false
});

 

to use it, install Tampermonkey on your browser, create a new userscript, copy+paste the code above, then save it

choose either the "always allow" or the "always allow domain" option when this screen appears:
image.thumb.png.75d7ff1dbdc544bc7461b8d24b1823b6.png

tested on:
- extension = Tampermonkey 4.8.41
- browser = Ungoogled Chromium 76.0.3809.132
- os = Debian 11 (bullseye)

Edited by Jessie223
  • Like 1
  • Confused 2

Share this post


Link to post
Share on other sites
watermelen671    14,583

Finally, I can be rid of the eye cancer that is certain people's profile pictures. :wilson_sneaky:

  • Like 1
  • Haha 2

Share this post


Link to post
Share on other sites
Jessie223    684
9 hours ago, Xenologist said:

Here's a question, is there a way of getting it without any of the web browser extensions?

no

Share this post


Link to post
Share on other sites
DragonMage156    25,337
On 10/24/2019 at 10:11 PM, I_Link_I said:

2.You monster!How could you do that to the poor GIFs?!

I feel more sorry for @Asparagus being the example.

On 10/26/2019 at 3:10 AM, watermelen671 said:

Finally, I can be rid of the eye cancer that is certain people's profile pictures. :wilson_sneaky:

Sorry Melen XD but I promise it'll end after halloween (atleast mine will).

Share this post


Link to post
Share on other sites
watermelen671    14,583
2 hours ago, DragonMage156 said:

Sorry Melen XD but I promise it'll end after halloween (atleast mine will).

Doesn't even work...smh. :wilson_cry:

Share this post


Link to post
Share on other sites
Jessie223    684
2 minutes ago, watermelen671 said:

Doesn't even work...smh. :wilson_cry:

which script are you using for which extension and which browser?

the first script should only work with greasemonkey + firefox

the second script should work with tampermonkey + firefox/chromium/edge/safari/opera (note i've only tested them on firefox and chromium)

Share this post


Link to post
Share on other sites
watermelen671    14,583
2 minutes ago, Jessie223 said:

which script are you using for which extension and which browser?

the first script should only work with greasemonkey + firefox

the second script should work with tampermonkey + firefox/chromium/edge/safari/opera (note i've only tested them on firefox and chromium)

I added the second one...I'm on chrome, and @DragonMage156's profile pic is still moving.

Mage...please...just change it. :wilson_cry:

Share this post


Link to post
Share on other sites
Jessie223    684
1 minute ago, watermelen671 said:

I added the second one...I'm on chrome, and @DragonMage156's profile pic is still moving.

Mage...please...just change it. :wilson_cry:

open the console (ctrl + shift + j) and refresh the page

if you see this error message "Uncaught (in promise) DOMException: Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported." then you're probably running chrome with the "disable-reading-from-canvas" flag

if different error messages appear, then please tell me

also make sure it's enabled properly, like so:
image.png.504078919d5f30db57e4049029daa2d1.png

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now