您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Makes cards' heights to be dynamic depending on image height
当前为
// ==UserScript== // @name InoReader dynamic height of tiles in the card view // @namespace http://tampermonkey.net/ // @version 0.0.1 // @description Makes cards' heights to be dynamic depending on image height // @author Kenya-West // @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"; document.head.insertAdjacentHTML("beforeend", ` <style> .tm_dynamic_height { height: auto !important; } .tm_remove_position_setting { position: unset !important; } </style>`); const appConfig = { corsProxy: "https://corsproxy.io/?", }; const appState = { readerPaneExists: false, }; // 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"; const querySelectorArticleContentWrapper = ".article_tile_content_wraper"; const querySelectorArticleFooter = ".article_tile_footer"; /** * Callback function to execute when mutations are observed * @param {MutationRecord[]} mutationsList - List of mutations observed * @param {MutationObserver} observer - The MutationObserver instance */ 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) { stylizeCardsInList(node); } }); } } }; /** * * @param {Node} node * @returns {void} */ function stylizeCardsInList(node) { const readerPane = document.body.querySelector("#reader_pane"); if (readerPane) { if (!appState.readerPaneExists) { appState.readerPaneExists = true; /** * Callback function to execute when mutations are observed * @param {MutationRecord[]} mutationsList - List of mutations observed * @param {MutationObserver} observer - The MutationObserver instance */ 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) { if (appState.readerPaneExists) { setTimeout(() => { start(node); }, 500); } } }); } } }; // Options for the observer (which mutations to observe) const mutationObserverLocalConfig = { attributes: false, childList: true, subtree: false, }; // Create an observer instance linked to the callback function const tmObserverArticleList = new MutationObserver(callback); // Start observing the target node for configured mutations tmObserverArticleList.observe( readerPane, mutationObserverLocalConfig ); } } else { appState.readerPaneExists = false; } /** * * @param {Node} node */ function start(node) { readerPane ?.querySelectorAll(`.ar:has(${querySelectorArticleContentWrapper})`) ?.forEach((element) => { // @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] > div[style*='background-image']" ); if (!divImageElement) { return; } const imageUrl = getImageLink(divImageElement); if (!imageUrl) { return; } const dimensions = getImageDimensions(imageUrl); // 5. Set image height (and automaticlly the card height) dimensions.then(([width, height]) => { if (height > 0) { const calculatedHeight = Math.round((cardWidth / width) * height); /** * @type {HTMLDivElement} */ // @ts-ignore const div = divImageElement; if (calculatedHeight > cardHeight) { div.style.height = `${calculatedHeight}px`; } } }); }); } /** * * @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?.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; await img.decode(); return [img.naturalWidth, img.naturalHeight]; }; } // Create an observer instance linked to the callback function const tmObserverDynamicHeight = new MutationObserver(callback); // Start observing the target node for configured mutations tmObserverDynamicHeight.observe(targetNode, mutationObserverGlobalConfig); /** * * @param {Function | void} mainFunction * @param {number} delay * @returns */ function debounce(mainFunction, delay) { // Declare a variable called 'timer' to store the timer ID /** * @type {number} */ let timer; // Return an anonymous function that takes in any number of arguments /** * @param {...any} args * @returns {void} */ return function (...args) { // Clear the previous timer to prevent the execution of 'mainFunction' clearTimeout(timer); // Set a new timer that will execute 'mainFunction' after the specified delay timer = setTimeout(() => { // @ts-ignore mainFunction(...args); }, delay); }; } })();