userscripts/Image Loader Placeholder Re...

243 lines
9.3 KiB
JavaScript

// ==UserScript==
// @name Image Loader Placeholder Remover
// @namespace blankie-scripts
// @match http*://*/*
// @grant none
// @version 1.16.0
// @author blankie
// @run-at document-end
// @description Removes image loading placeholders
// ==/UserScript==
"use strict";
function isUrlLike(url) {
return url && /^(?:\/|https?:\/\/)\S+$/.test(url);
}
function findUrl(element) {
if (window.location.host === "www.vice.com") {
let picture = element.parentElement;
let source = picture && picture.localName === "picture" ? picture.querySelector("source") : null;
if (source) {
return source.srcset;
}
}
// Examples of data-src:
// - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex
// - https://daramiblog.com
// - https://daramiblog.com/how-to-group-irregular-verbs-for-more-efficient-learning/
// - https://www.bleepingcomputer.com/
// - https://www.bleepingcomputer.com/news/microsoft/windows-11-snipping-tool-privacy-bug-exposes-cropped-image-content/
// - https://blog.joshumax.me/general/2021/08/11/running-doom-on-captioncall.html
// - https://legendsoflocalization.com/common-problems-when-translating-games-into-japanese/
// - https://knowyourmeme.com/
// - https://knowyourmeme.com/memes/saddam-husseins-hiding-place
if (isUrlLike(element.dataset.src)) {
return element.dataset.src;
}
for (let attr of element.attributes) {
// Examples of lazy:
// - https://daramiblog.com/
// - https://daramiblog.com/how-to-group-irregular-verbs-for-more-efficient-learning/
// - https://www.theautopian.com/
// - https://www.theautopian.com/nobody-wants-touch-screen-glove-box-latches-and-it-needs-to-stop-now/
// - https://vulcan.io/blog/
// - https://vulcan.io/blog/ai-hallucinations-package-risk
// Examples of src:
// - https://vulcan.io/blog/
// - https://vulcan.io/blog/ai-hallucinations-package-risk
// Examples of loading:
// - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex
// Examples of original:
// - https://www.pcgamer.com/
// - https://www.pcgamer.com/windows-95-theme-for-windows-10/
// Example of img-url:
// - https://www.makeuseof.com/windows-11-resize-taskbar/
if (!attr.name.includes("lazy") && !(attr.name.includes("src") && attr.name !== "src") && !attr.name.includes("loading") && !attr.name.includes("original") && !attr.name.includes("img-url")) {
continue;
}
if (!isUrlLike(attr.value)) {
continue;
}
return attr.value;
}
return null;
}
function unhideElement(element) {
let style = getComputedStyle(element);
// Examples of opacity:
// - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex
// - https://www.vice.com/en/article/dy73n7/ehallpass-1000-thousand-schools-monitor-bathroom
// - https://daramiblog.com/
// - https://daramiblog.com/how-to-group-irregular-verbs-for-more-efficient-learning/
// - https://www.wired.com/
// - https://www.wired.com/story/researcher-fooled-a-google-ai-into-thinking-a-rifle-was-a-helicopter/
if (style.opacity === "0") {
element.style.opacity = 1;
}
// Examples of display:
// - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex
if (style.display === "none") {
element.style.display = "block";
}
// Example: https://www.makeuseof.com/windows-11-resize-taskbar/
if (window.location.host === "www.makeuseof.com") {
element.style.paddingBottom = 0;
}
}
function getLazyloaderClasses(element) {
let classes = [];
for (let className of element.classList) {
// Examples of loading:
// - https://closeronline.co.uk
// - https://closeronline.co.uk/real-life/news/ever-used-excuses-documented-spreadsheet-man-used-expose-wife-s-lack-sex
// Examples of lazy:
// - https://www.vice.com/en/article/dy73n7/ehallpass-1000-thousand-schools-monitor-bathroom
// - https://www.pcgamer.com/
// - https://www.pcgamer.com/windows-95-theme-for-windows-10/
// - https://www.theautopian.com/
// - https://www.theautopian.com/nobody-wants-touch-screen-glove-box-latches-and-it-needs-to-stop-now/
// - https://vulcan.io/blog/
// - https://vulcan.io/blog/ai-hallucinations-package-risk
// Examples of responsive:
// - https://www.vice.com/en/article/dy73n7/ehallpass-1000-thousand-schools-monitor-bathroom
// - https://www.wired.com (it's a parent of lazyloaded <img>s)
// - https://www.wired.com/story/researcher-fooled-a-google-ai-into-thinking-a-rifle-was-a-helicopter/ (it's a parent of lazyloaded <img>s)
// Examples of preload:
// - https://restofworld.org/
// - https://restofworld.org/2023/parent-facing-matchmaking-apps-china/
// Examples of placeholder:
// - https://theconversation.com
// - https://theconversation.com/yall-that-most-southern-of-southernisms-is-going-mainstream-and-its-about-time-193265
if (className.includes("loading") || className.includes("lazy") || className.includes("responsive") || className.includes("preload") || className.includes("placeholder")) {
classes.push(className);
}
}
return classes;
}
function removePlaceholder(img) {
let hasLinkParent = false;
let parentElement = img.parentElement;
while (parentElement) {
hasLinkParent = hasLinkParent || parentElement.localName === "a";
// Examples of hidden parents:
// - https://www.wired.com/
// - https://www.wired.com/story/researcher-fooled-a-google-ai-into-thinking-a-rifle-was-a-helicopter/
let lazyLoaderClasses = getLazyloaderClasses(parentElement);
if (lazyLoaderClasses.length !== 0) {
unhideElement(parentElement);
// Examples of pages with a blur applied to lazyloader classes:
// - https://restofworld.org/
// - https://restofworld.org/2023/parent-facing-matchmaking-apps-china/
parentElement.classList.remove(...lazyLoaderClasses);
}
parentElement = parentElement.parentElement;
}
let url = findUrl(img);
if (!url) {
return;
}
let originalUrl = url;
if (window.location.host === "www.vice.com") {
let picture = img.parentElement;
img.style.filter = "none";
picture.style.opacity = 1;
let urlObject = new URL(url, window.location);
urlObject.search = "";
originalUrl = urlObject.href;
urlObject.search = "?resize=1024:*";
url = urlObject.href;
for (let source of picture.querySelectorAll("source")) {
source.remove();
}
} else if (window.location.host === "closeronline.co.uk") {
let urlObject = new URL(url, window.location);
urlObject.search = "";
originalUrl = urlObject.href;
img.closest(".image-container").querySelector(".image-loading").remove();
} else if (window.location.host === "theconversation.com") {
let urlObject = new URL(url, window.location);
urlObject.search = "";
originalUrl = urlObject.href;
}
img.src = url;
unhideElement(img);
// commented out as some sites may visually break, such as:
// https://legendsoflocalization.com/japans-mysterious-love-of-the-word-lets/#why-lets-is-so-common
//img.classList.remove(...getLazyloaderClasses(img));
if (!hasLinkParent) {
let wrapper = document.createElement("a");
img.replaceWith(wrapper);
wrapper.href = originalUrl;
wrapper.appendChild(img);
}
}
for (let img of document.querySelectorAll("img")) {
removePlaceholder(img);
}
// the reason we check for mutations for 1s after the page loads is because of <noscript> elements being faked by umatrix
let observer = new MutationObserver(function (mutations) {
for (let mutation of mutations) {
if (mutation.type !== "childList") {
continue;
}
for (let node of mutation.addedNodes) {
if (node.nodeType !== 1) {
continue;
}
for (let img of node.querySelectorAll("img")) {
removePlaceholder(img);
}
}
}
});
observer.observe(document.body, {childList: true, subtree: true});
setTimeout(function() {
observer.disconnect();
}, 1000);
// https://www.wikihow.com/Tie-Your-Shoes
if (window.location.host === "www.wikihow.com") {
let style = document.createElement("style");
style.textContent = ".image {display: block !important}";
document.head.appendChild(style);
for (let videoPlayer of document.querySelectorAll(".video-player")) {
videoPlayer.querySelector(".m-video-controls").remove();
let video = videoPlayer.querySelector("video");
video.controls = 1;
video.src = `/video${video.dataset.src}`;
}
}