Skip to content

Instantly share code, notes, and snippets.

@Kenya-West
Last active October 24, 2024 14:00
Show Gist options
  • Save Kenya-West/9c0ca555d4e229a56dbdf4d02b2b07ef to your computer and use it in GitHub Desktop.
Save Kenya-West/9c0ca555d4e229a56dbdf4d02b2b07ef to your computer and use it in GitHub Desktop.
InoReader dynamic height of tiles in the card view. Requires https://greasyfork.org/ru/scripts/513854-inoreader-viewing-api-for-userscripts
// ==UserScript==
// @name InoReader dynamic height of tiles in the card view
// @namespace http://tampermonkey.net/
// @version 0.1.0
// @description Makes cards' heights to be dynamic depending on image height
// @author Kenya-West
// @match https://*.inoreader.com/*
// @icon https://inoreader.com/favicon.ico?v=8
// @license MIT
// ==/UserScript==
// @ts-check
(function () {
"use strict";
document.addEventListener('tm_inoreader-viewing-api-for-userscripts_articleAdded', (e) => {
const { element } = e.detail?.details;
setTimeout(() => {
start(element);
}, 3500);
// the second attempt is needed because some images or videos are loaded after the first attempt
setTimeout(() => {
start(element);
}, 10000);
});
const querySelectorPathArticleRoot =
".article_full_contents .article_content";
const querySelectorArticleContentWrapper = ".article_tile_content_wraper";
const querySelectorArticleFooter = ".article_tile_footer";
document.head.insertAdjacentHTML("beforeend", `
<style>
.tm_dynamic_height {
height: auto !important;
}
.tm_remove_position_setting {
position: unset !important;
}
</style>`);
/**
*
* @param {Node} node
*/
function start(node) {
/**
* @type {Node & HTMLDivElement}
*/
// @ts-ignore
const element = node;
if (
element?.hasChildNodes() &&
element?.id?.includes("article_") &&
element?.classList.contains("ar") &&
!element?.classList.contains("tm_dynamic_height")
) {
// @ts-ignore
const cardWidth = element.clientWidth ?? element.offsetWidth ?? element.scrollWidth;
// @ts-ignore
const cardHeight = element.clientHeight ?? element.offsetHeight ?? element.scrollHeight;
// 1. Set card height dynamic
setDynamicHeight(element);
// 2. Set cotnent wrapper height dynamic
const articleContentWrapperElement = element.querySelector(
querySelectorArticleContentWrapper
);
if (articleContentWrapperElement) {
setDynamicHeight(articleContentWrapperElement);
}
// 3. Remove position setting from article footer
const articleFooter = element.querySelector(
querySelectorArticleFooter
);
if (articleFooter) {
removePositionSetting(articleFooter);
}
// 4. Find image height
/**
* @type {HTMLDivElement | null}
*/
const divImageElement = element.querySelector(
"a[href] > .article_tile_picture[style*='background-image']"
);
if (!divImageElement) {
return;
}
const imageUrl = getImageLink(divImageElement);
if (!imageUrl) {
return;
}
const dimensions = getImageDimensions(imageUrl);
// 5. Set image height (and - automatically - the card height)
dimensions.then(([width, height]) => {
if (height > 0) {
const calculatedHeight = Math.round(
(cardWidth / width) * height
);
const pictureOldHeight =
(divImageElement.clientHeight ??
divImageElement.offsetHeight ??
divImageElement.scrollHeight) ||
cardHeight;
/**
* @type {HTMLDivElement}
*/
// @ts-ignore
const div = divImageElement;
if (calculatedHeight > pictureOldHeight) {
div.style.height = `${calculatedHeight}px`;
}
// 5.1. Set card class to `.tm_dynamic_height` to not process it again next time
element.classList?.add("tm_dynamic_height");
}
});
}
}
/**
*
* @param {Element} element
* @returns {void}
*/
function setDynamicHeight(element) {
element.classList?.add("tm_dynamic_height");
}
/**
*
* @param {Element} element
* @returns {void}
*/
function removeDynamicHeight(element) {
const div = element.querySelector("img");
if (!div) {
return;
}
div.classList?.remove("tm_dynamic_height");
}
/**
*
* @param {Element} element
* @returns {void}
*/
function removePositionSetting(element) {
element.classList?.add("tm_remove_position_setting");
}
/**
*
* @param {Element} element
* @returns {void}
*/
function restorePositionSetting(element) {
element.classList?.remove("tm_remove_position_setting");
}
/**
*
* @param {HTMLDivElement} div
* @returns {string | null}
*/
function getImageLink(div) {
const backgroundImageUrl = div?.style.backgroundImage;
/**
* @type {string | undefined}
*/
let imageUrl;
try {
imageUrl = backgroundImageUrl?.match(/url\("(.*)"\)/)?.[1];
} catch (error) {
imageUrl = backgroundImageUrl?.slice(5, -2);
}
if (!imageUrl || imageUrl == "undefined") {
return null;
}
if (!imageUrl?.startsWith("http")) {
console.error(
`The image could not be parsed. Image URL: ${imageUrl}`
);
return null;
}
return imageUrl;
}
/**
*
* @param {string} url
* @returns {Promise<[number, number]>}
*/
async function getImageDimensions(url) {
const img = new Image();
img.src = url;
try {
await img.decode();
} catch (error) {
return Promise.reject(error);
}
return Promise.resolve([img.width, img.height]);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment