Skip to content

Instantly share code, notes, and snippets.

@Kenya-West
Last active April 2, 2024 11:19
Show Gist options
  • Save Kenya-West/7f058b8240e540a344706cdd10fd0183 to your computer and use it in GitHub Desktop.
Save Kenya-West/7f058b8240e540a344706cdd10fd0183 to your computer and use it in GitHub Desktop.
InoReader highlight transparent images script hightlights transparent images in InoReader with a white border
// ==UserScript==
// @name InoReader highlight transparent images
// @namespace http://tampermonkey.net/
// @version 0.0.4
// @description Hightlights transparent images in InoReader with a white border
// @author Kenya-West
// @require https://cdnjs.cloudflare.com/ajax/libs/color-thief/2.3.0/color-thief.umd.js
// @match https://*.inoreader.com/feed*
// @match https://*.inoreader.com/article*
// @match https://*.inoreader.com/folder*
// @match https://*.inoreader.com/starred*
// @match https://*.inoreader.com/library*
// @match https://*.inoreader.com/channel*
// @match https://*.inoreader.com/teams*
// @match https://*.inoreader.com/dashboard*
// @match https://*.inoreader.com/pocket*
// @icon https://inoreader.com/favicon.ico?v=8
// @license MIT
// ==/UserScript==
// @ts-check
(function () {
"use strict";
// @ts-ignore
const colorThief = new ColorThief();
const appConfig = {
restoreImagesInArticleView: true,
outlineColor: "white",
outlineWidth: 1,
delay: 5000,
thresholdOfDarkness: 8,
};
// @ts-ignore
const appState = {};
// Select the node that will be observed for mutations
const targetNode = document.body;
// Options for the observer (which mutations to observe)
const mutationObserverGlobalConfig = {
attributes: false,
childList: true,
subtree: true,
};
const querySelectorPathArticleRoot =
".article_full_contents .article_content";
/**
* Callback function to execute when mutations are observed
* @param {MutationRecord[]} mutationsList - List of mutations observed
* @param {MutationObserver} observer - The MutationObserver instance
*/
// @ts-ignore
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === Node.ELEMENT_NODE) {
runHightlightImagesInArticleView(node);
}
});
}
}
};
/**
*
* @param {Node} node
* @returns {void}
*/
function runHightlightImagesInArticleView(node) {
setTimeout(() => {
start();
}, appConfig.delay ?? 2000);
function start() {
if (!appConfig.restoreImagesInArticleView) {
return;
}
/**
* @type {HTMLDivElement}
*/
// @ts-ignore
const nodeElement = node;
/**
* @type {HTMLDivElement | null}
*/
const articleRoot = nodeElement?.querySelector(
querySelectorPathArticleRoot
);
if (articleRoot) {
const images = getImages(articleRoot);
images.forEach((targetImage) => {
if (
targetImage.complete &&
_getImageHeight(targetImage) > 0 &&
_getImageWidth(targetImage) > 0
) {
/**
* @type {[number, number, number]}
*/
let colors;
try {
colors = colorThief.getColor(targetImage);
} catch (error) {
colors = [255, 255, 255];
}
const threshold = appConfig.thresholdOfDarkness ?? 10;
if (colors.every((color) => color > threshold)) {
return;
}
const canvas = createCanvas(targetImage);
const img = new Image();
img.src = targetImage.src;
// add event listener to draw on image load
/**
* @type {HTMLCanvasElement | undefined}
*/
let newCanvas;
img.onload = function () {
/**
* @type {HTMLCanvasElement}
*/
// newCanvas = draw(canvas, img);
// replaceImageSrc(targetImage, newCanvas);
invertColor(targetImage);
};
}
});
return;
}
}
/**
*
* @param {HTMLDivElement} articleRoot
* @returns {NodeListOf<HTMLImageElement>} images
*/
function getImages(articleRoot) {
return articleRoot.querySelectorAll("img[src]");
}
/**
*
* @param {HTMLImageElement} targetImage
* @returns {HTMLCanvasElement}
*/
function createCanvas(targetImage) {
const canvas = document.createElement("canvas");
let width = _getImageWidth(targetImage);
let height = _getImageHeight(targetImage);
if (width > 0) {
canvas.width = targetImage.width;
}
if (height > 0) {
canvas.height = targetImage.height;
}
return canvas;
}
/**
*
* @param {HTMLImageElement} img
*/
function invertColor(img) {
img.style.filter = "invert(1)";
}
/**
*
* @param {HTMLCanvasElement} canvas
* @param {HTMLImageElement} img
*
* @returns {HTMLCanvasElement}
*/
function draw(canvas, img) {
// first - set height as canvas may not get height in previous step
const height = _getImageHeight(img);
if (height) {
canvas.height = height + appConfig.outlineWidth * 2;
}
const ctx = canvas.getContext("2d");
let dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1], // offset array
s = appConfig.outlineWidth || 2, // thickness scale
i = 0, // iterator
x = 5, // final position
y = 5;
// draw images at offsets from the array scaled by s
for (; i < dArr.length; i += 2)
// @ts-ignore
ctx.drawImage(img, x + dArr[i] * s, y + dArr[i + 1] * s);
// fill with color
// @ts-ignore
ctx.globalCompositeOperation = "source-in";
// @ts-ignore
ctx.fillStyle = appConfig.outlineColor || "white";
// @ts-ignore
ctx.fillRect(0, 0, canvas.width, canvas.height);
// draw original image in normal mode
// @ts-ignore
ctx.globalCompositeOperation = "source-over";
// @ts-ignore
ctx.drawImage(img, x, y);
return canvas;
}
/**
*
* @param {HTMLImageElement} targetImg
* @param {HTMLCanvasElement} canvas
*/
// @ts-ignore
function replaceImageSrc(targetImg, canvas) {
// replace image with canvas
targetImg.parentNode?.replaceChild(canvas, targetImg);
}
}
/**
*
* @param {HTMLImageElement} img
* @returns {number} width
*/
function _getImageWidth(img) {
return img.width || img.clientWidth || img.naturalWidth;
}
/**
*
* @param {HTMLImageElement | undefined} img
* @returns {number} height
*/
function _getImageHeight(img) {
return img?.height || img?.clientHeight || img?.naturalHeight || 0;
}
// Create an observer instance linked to the callback function
const tmObserverImageRestore = new MutationObserver(callback);
// Start observing the target node for configured mutations
tmObserverImageRestore.observe(targetNode, mutationObserverGlobalConfig);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment