Endless scrolling between image posts with mousewheel
// ==UserScript==
// @name Twitter Endless Scroll [X]
// @namespace Twitter
// @version 1.7
// @description Endless scrolling between image posts with mousewheel
// @author NightLancerX
// @match https://x.com/*
// @match https://twitter.com/*
// @match https://mobile.twitter.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @license CC-BY-NC-SA
// @grant none
// @require https://code.jquery.com/jquery-3.3.1.min.js
// @run-at document-end
// @noframes
// ==/UserScript==
(function() {
'use strict';
const DEBUG = false;
let nextPost, shifted = false, lastIndex = 0, img_src;
let img = new Image();
let block_selector = 'article'
let content_selector = 'img[src*="https://pbs.twimg.com/media/"]'; //, img[src*="pbs.twimg.com/tweet_video_thumb/"]';//, img[src*="pbs.twimg.com/amplify_video_thumb/"]';
let offset_selector = '[data-testid="cellInnerDiv"]'
function changePost(shift, useFirst = true){
let posts = [...document.querySelectorAll(`${block_selector}:has(${content_selector})`)];
if (DEBUG) console.log(posts);
let index = posts.indexOf(document.querySelector(`${block_selector}:has([href='${location.pathname}'])`));
if (DEBUG) console.log(`lastIndex: ${lastIndex}`);
if (DEBUG) console.log(`index: ${index}`);
if (index<0){ //needed anymore?
index = lastIndex;
}
nextPost = posts[index+shift];
if (DEBUG) console.log(nextPost);
if (nextPost){
nextPost.scrollIntoView();
let elements = nextPost.querySelectorAll(content_selector);
((elements.length==1 || useFirst) ? elements[0] : elements[elements.length - 1])?.click();
//nextPost.querySelector(content_selector)?.click();
shifted = true;
lastIndex = index+shift;
//pre-cache
// img_src = posts[index+shift*2]?.querySelector(content_selector)?.src;
// if (DEBUG) console.log(`pre img_src: ${img_src}`); //absolutely random and chaotic
//findValidImage(img_src, ["large", "4096x4096"]);
}
}
// async function findValidImage(img_src, qualityList) {
// const prefix = img_src.split("name=")[0];
// let i = 0;
// function checkImage(url) {
// return new Promise(resolve => {
// const img = new Image();
// img.onload = () => resolve(true);
// img.onerror = () => {
// if (DEBUG) console.log(`img.onerror`);
// resolve(false);
// }
// img.src = url;
// });
// }
// for (let name of qualityList) {
// const testUrl = prefix + "name=" + name;
// const isValid = await checkImage(testUrl);
// if (isValid) {
// if (DEBUG) console.log(`img_src: ${testUrl}`);
// return testUrl;
// }
// }
// }
function image_count(){
return document.querySelectorAll(`${block_selector} [href*='${location.pathname.replace(/\/\d+$/, "")}']`)?.length ?? 0
}
$('body').on('wheel', '[aria-labelledby="modal-header"]', function(e){
e.preventDefault();
e.stopPropagation();
let left;
if (e.originalEvent.deltaY < 0){
if ((left = document.querySelector('[data-testid="Carousel-NavLeft"]')) && image_count() > 1){
left.click();
}
else
changePost(-1, false);
}
let right;
if (e.originalEvent.deltaY > 0){
if ((right = document.querySelector('[data-testid="Carousel-NavRight"]')) && image_count() > 1){
right.click();
}
else
changePost(+1);
}
});
$('body').on('click', '[aria-labelledby="modal-header"] [data-testid="swipe-to-dismiss"]', function(e){
if (shifted) setTimeout(()=>{
let offset = nextPost?.closest(`${offset_selector}`).style.transform.match(/\d+/)?.[0];
window.scroll(0, offset);
shifted = false;
}, 500);
})
})();