Last active
October 24, 2024 14:00
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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