I think it would be better to remove this big library. Here's the converted code with a focus on removing jQuery and using vanilla JavaScript (does not works):// Constants (use descriptive names) const MATCHING_PAGE = /watch\?/; const MAX_THUMBNAILS = 400; const MIN_THUMBNAIL_WIDTH = 4; const LOGGING_ENABLED = true; const DISABLE_SPF = GM_getValue('ytDisableSPF', false); const ADD_AND_BUTTS = GM_getValue('ytAndButts', false); const THUMBNAIL_POSITION = true; // Before title const TIMEOUT_DELAY = 50; const MAX_TITLE_SEARCH_TRIES = 1; const DIV_PADDING = 10; const TD_PADDING = 2;
// Global variables (minimize usage) let scriptStarted = false; let scriptFinished = false; let storyboardSpec = null; let bestStoryboardSize = null; let videoLengthSeconds = 0; let currentVideoId = null; let thumbnailDiv = null; let titleDiv = null;
function log(...messages) { LOGGING_ENABLED && console.log(...messages); }
if (!titleDiv || titleDiv.nodeName !== 'DIV') { console.log("no title div"); if (tries++ < MAX_TRIES) { console.log("trying again for title div"); setTimeout(tryTitle, TIMEOUT_DELAY); } else { console.log("done trying for title div"); scriptFinished = true; } } else { console.log('found title div', titleDiv); setUp(); } }
function setUp() { if (storyboard_spec) { console.log('sb_spec', storyboard_spec); storyboard = parseStoryboardSpec(storyboard_spec); console.log('sb', storyboard); best_size_idx = chooseBestStoryboardSize(storyboard); best_size = storyboard.sizes[best_size_idx]; console.log(best_size); if (!len_seconds > 0) { var len_str = document.querySelector('div.ytp-progress-bar').getAttribute('aria-valuemax') || yp.config.args.length_seconds; len_seconds = parseInt(len_str); } console.log("len: ", len_seconds);
// Create elements using vanilla JavaScript const thumbDiv = document.createElement('div'); thumbDiv.id = 'thumbDiv'; thumbDiv.classList.add('style-scope', 'yt-card', 'yes-thumbs', 'thumb-inactive'); thumbDiv.dataset.videoId = video_id;
// ... (rest of the code with jQuery parts replaced) } else if (unsafeWindow.ytplayer) { // ... similar replacements for element creation and event listeners } else { // ... }
if (BEFORE_TITLE) { titleDiv.before(thumbDiv); } else { titleDiv.after(thumbDiv); } console.log($titleDiv);
// ... (rest of the code with jQuery parts replaced)
var badImage = false; (function loadImage(imgNum) { if (badImage === false && imgNum < num_thumbs && imgNum < MAX_IMAGES) { // ... (link creation logic using vanilla string manipulation)
// Create table element and timestamps using vanilla JavaScript var newTable = document.createElement('table'); newTable.classList.add('imgTable'); // ... (timestamp creation logic using textContent and createElement)
function parseStoryboardSpec(spec) { // EX: https:\/\/i.ytimg.com\/sb\/Pk2oW4SDDxY\/storyboard3_L$L\/$N.jpg|48#27#100#10#10#0#default#vpw4l5h3xmm2AkCT6nMZbvFIyJw|80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o|160#90#90#5#5#2000#M$M#ys1MKEnwYXA1QAcFiugAA_cZ81Q var sections = spec.split('|'); console.log(sections); var sb = { sizes: [], baseUrl: sections.shift() };
// EX: 80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o // EX: 160# 90# 90# 5# 5# 2000# M$M# ys1MKEnwYXA1QAcFiugAA_cZ81Q sections.forEach(function(value, idx) { var data = value.split('#'); console.log(data); var new_size = { width : +data[0], height : +data[1], qual : +data[2], // image quality??? cols : +data[3], rows : +data[4], duration : +data[5], // duration of each snapshot in milliseconds img_name : data[6], sigh : data[7] }; sb.sizes[idx] = new_size; }); console.log(sb); return sb; }
function chooseBestStoryboardSize(sb) { var sizes = sb.sizes; var widest = 0; var widest_idx = -1; for (var i = 0; i < sizes.length; i++) { if (widest < sizes[i].width || (widest == sizes[i].width && sizes[widest_idx].cols < sizes[i].cols)) { if (sizes[i].cols >= MIN_WIDTH) { widest = sizes[i].width; widest_idx = i; } } } return widest_idx; }
function killSPF() { if (DISABLE_SPF) { console.log('trying to kill SPF'); document.querySelectorAll('a.spf-link, a.yt-simple-endpoint').forEach(function(link) { link.classList.remove('spf-link'); link.classList.remove('yt-simple-endpoint'); link.classList.add('old-endpoint'); // No need for `off()` in vanilla JavaScript, event listeners are specific to each function }); } //AND_BUTTS && document.querySelector('#info-contents h1.title').textContent += " and Butts"; }
function getParameters(str) { var r = {}; str.split('&').forEach(str => { var split = str.split('='); r[split[0]] = decodeURIComponent(split[1]); }); return r; }
// Use addEventListener for event handling window.addEventListener('spfdone', findStuff); window.addEventListener('yt-navigate-finish', findStuff); window.setTimeout(findStuff, 200);
I think it would be better to remove this big library. Here's the converted code with a focus on removing jQuery and using vanilla JavaScript (does not works):// Constants (use descriptive names)
const MATCHING_PAGE = /watch\?/;
const MAX_THUMBNAILS = 400;
const MIN_THUMBNAIL_WIDTH = 4;
const LOGGING_ENABLED = true;
const DISABLE_SPF = GM_getValue('ytDisableSPF', false);
const ADD_AND_BUTTS = GM_getValue('ytAndButts', false);
const THUMBNAIL_POSITION = true; // Before title
const TIMEOUT_DELAY = 50;
const MAX_TITLE_SEARCH_TRIES = 1;
const DIV_PADDING = 10;
const TD_PADDING = 2;
// Global variables (minimize usage)
let scriptStarted = false;
let scriptFinished = false;
let storyboardSpec = null;
let bestStoryboardSize = null;
let videoLengthSeconds = 0;
let currentVideoId = null;
let thumbnailDiv = null;
let titleDiv = null;
function log(...messages) {
LOGGING_ENABLED && console.log(...messages);
}
// Event listeners for script execution
window.addEventListener('spfdone', findStuff);
window.addEventListener('yt-navigate-finish', findStuff);
window.setTimeout(findStuff, 200);
function findStuff() {
log("Started...");
if (!MATCHING_PAGE.test(window.location.href) || (scriptStarted && !scriptFinished)) {
return;
}
scriptStarted = true;
scriptFinished = false;
storyboardSpec = null;
currentVideoId = new URL(window.location.href).searchParams.get('v');
// Remove any existing thumbnail divs
const existingThumbDivs = document.querySelectorAll('#thumbDiv');
existingThumbDivs.forEach(div => div.remove());
tryTitle();
}
// Replace jQuery selectors with vanilla JavaScript equivalents
var thumbDivYesThumbs = document.querySelector('#thumbDiv.yes-thumbs');
if (thumbDivYesThumbs) {
console.log('current_id', current_id, 'existing id', thumbDivYesThumbs.dataset.videoId);
if (thumbDivYesThumbs.dataset.videoId === current_id) {
scriptFinished = true;
return;
} else {
thumbDivYesThumbs.remove();
}
} else {
var thumbDiv = document.querySelector('#thumbDiv');
if (thumbDiv) {
thumbDiv.remove();
}
}
function getInfoFromXHR(args) {
console.log('args from xhr', args);
storyboard_spec = args.storyboard_spec;
len_seconds = parseInt(args.length_seconds);
video_id = args.video_id;
if (!storyboard_spec) {
var pr = JSON.parse(args.player_response);
storyboard_spec = pr.storyboards.playerStoryboardSpecRenderer.spec;
}
}
function tryTitle() {
console.log('tryTitle');
// Replace jQuery selectors with vanilla JavaScript equivalents
const possibleTitleSelectors = [
'div#info.ytd-watch-flexy',
'div#info.ytd-watch',
'#watch-header'
];
const titleDiv = possibleTitleSelectors.find(selector => {
return document.querySelector(selector);
});
if (!titleDiv || titleDiv.nodeName !== 'DIV') {
console.log("no title div");
if (tries++ < MAX_TRIES) {
console.log("trying again for title div");
setTimeout(tryTitle, TIMEOUT_DELAY);
} else {
console.log("done trying for title div");
scriptFinished = true;
}
} else {
console.log('found title div', titleDiv);
setUp();
}
}
function setUp() {
if (storyboard_spec) {
console.log('sb_spec', storyboard_spec);
storyboard = parseStoryboardSpec(storyboard_spec);
console.log('sb', storyboard);
best_size_idx = chooseBestStoryboardSize(storyboard);
best_size = storyboard.sizes[best_size_idx];
console.log(best_size);
if (!len_seconds > 0) {
var len_str = document.querySelector('div.ytp-progress-bar').getAttribute('aria-valuemax') || yp.config.args.length_seconds;
len_seconds = parseInt(len_str);
}
console.log("len: ", len_seconds);
// Create elements using vanilla JavaScript
const thumbDiv = document.createElement('div');
thumbDiv.id = 'thumbDiv';
thumbDiv.classList.add('style-scope', 'yt-card', 'yes-thumbs', 'thumb-inactive');
thumbDiv.dataset.videoId = video_id;
const thumbHeader = document.createElement('h1');
thumbHeader.width = '100%';
thumbHeader.classList.add('title', 'ytd-video-primary-info-renderer');
thumbHeader.textContent = 'Thumbnails';
thumbDiv.appendChild(thumbHeader);
thumbDiv.addEventListener('click', showThumbs);
// ... (rest of the code with jQuery parts replaced)
} else if (unsafeWindow.ytplayer) {
// ... similar replacements for element creation and event listeners
} else {
// ...
}
if (BEFORE_TITLE) {
titleDiv.before(thumbDiv);
} else {
titleDiv.after(thumbDiv);
}
console.log($titleDiv);
console.log("Script done");
console.log("finished.");
scriptFinished = true;
}
function showThumbs(event) {
var duration = best_size.duration / 1000;
var num_thumbs = 1;
if (duration > 0) {
num_thumbs = Math.ceil(len_seconds / duration / best_size.cols / best_size.rows);
} else {
duration = len_seconds / best_size.cols / best_size.rows;
}
console.log("Thumb header clicked. Loading " + num_thumbs + " images.");
// ... (rest of the code with jQuery parts replaced)
var badImage = false;
(function loadImage(imgNum) {
if (badImage === false && imgNum < num_thumbs && imgNum < MAX_IMAGES) {
// ... (link creation logic using vanilla string manipulation)
// Create table element and timestamps using vanilla JavaScript
var newTable = document.createElement('table');
newTable.classList.add('imgTable');
// ... (timestamp creation logic using textContent and createElement)
newTable.addEventListener('error', function() {
badImage = true;
this.remove();
console.log("Hid bad image");
});
// Use newTable.onload instead of .load()
newTable.onload = function() {
loadImage(imgNum + 1);
};
newTable.style.backgroundImage = 'url(' + link + ')';
newTable.style.width = best_size.width * best_size.cols + 'px';
thumbDiv.appendChild(newTable);
setTimeout(loadImage, 1, imgNum + 1);
}
// Use querySelectorAll and addEventListener for click events
document.querySelectorAll('.imgTable a').forEach(function(link) {
link.addEventListener('click', function(event) {
event.preventDefault();
var seekTime = parseInt(link.dataset.seekTo);
console.log('seek time', seekTime);
document.querySelector('video.html5-main-video').currentTime = seekTime;
window.scrollTo(0, 0);
event.stopImmediatePropagation();
});
});
})(0);
console.log("Done making thumb div");
thumbDiv.removeEventListener('click', showThumbs);
thumbDiv.addEventListener('click', hideThumbs);
}
function hideThumbs(event) {
if (event.target === thumbDiv || event.target.closest('#thumbDiv h1')) {
thumbDiv.querySelectorAll('table').forEach(function(table) {
table.style.display = 'none';
});
thumbDiv.removeEventListener('click', showThumbsAgain);
thumbDiv.addEventListener('click', hideThumbs);
thumbDiv.classList.remove('thumb-active');
thumbDiv.classList.add('thumb-inactive');
} else {
window.scrollTo(0, 0);
}
}
function showThumbsAgain(event) {
if (event.target === thumbDiv || event.target.closest('#thumbDiv h1')) {
thumbDiv.querySelectorAll('table').forEach(function(table) {
table.style.display = 'block';
});
thumbDiv.removeEventListener('click', hideThumbs);
thumbDiv.addEventListener('click', showThumbsAgain);
thumbDiv.classList.remove('thumb-inactive');
thumbDiv.classList.add('thumb-active');
}
}
function parseStoryboardSpec(spec) {
// EX: https:\/\/i.ytimg.com\/sb\/Pk2oW4SDDxY\/storyboard3_L$L\/$N.jpg|48#27#100#10#10#0#default#vpw4l5h3xmm2AkCT6nMZbvFIyJw|80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o|160#90#90#5#5#2000#M$M#ys1MKEnwYXA1QAcFiugAA_cZ81Q
var sections = spec.split('|');
console.log(sections);
var sb = {
sizes: [],
baseUrl: sections.shift()
};
// EX: 80#45#90#10#10#2000#M$M#hCWDvBSbgeV52mPYmOHjgdLFI1o
// EX: 160# 90# 90# 5# 5# 2000# M$M# ys1MKEnwYXA1QAcFiugAA_cZ81Q
sections.forEach(function(value, idx) {
var data = value.split('#');
console.log(data);
var new_size = {
width : +data[0],
height : +data[1],
qual : +data[2], // image quality???
cols : +data[3],
rows : +data[4],
duration : +data[5], // duration of each snapshot in milliseconds
img_name : data[6],
sigh : data[7]
};
sb.sizes[idx] = new_size;
});
console.log(sb);
return sb;
}
function chooseBestStoryboardSize(sb) {
var sizes = sb.sizes;
var widest = 0;
var widest_idx = -1;
for (var i = 0; i < sizes.length; i++) {
if (widest < sizes[i].width || (widest == sizes[i].width && sizes[widest_idx].cols < sizes[i].cols)) {
if (sizes[i].cols >= MIN_WIDTH) {
widest = sizes[i].width;
widest_idx = i;
}
}
}
return widest_idx;
}
function killSPF() {
if (DISABLE_SPF) {
console.log('trying to kill SPF');
document.querySelectorAll('a.spf-link, a.yt-simple-endpoint').forEach(function(link) {
link.classList.remove('spf-link');
link.classList.remove('yt-simple-endpoint');
link.classList.add('old-endpoint');
// No need for `off()` in vanilla JavaScript, event listeners are specific to each function
});
}
//AND_BUTTS && document.querySelector('#info-contents h1.title').textContent += " and Butts";
}
function getParameters(str) {
var r = {};
str.split('&').forEach(str => {
var split = str.split('=');
r[split[0]] = decodeURIComponent(split[1]);
});
return r;
}
// Use addEventListener for event handling
window.addEventListener('spfdone', findStuff);
window.addEventListener('yt-navigate-finish', findStuff);
window.setTimeout(findStuff, 200);