// ==UserScript==
// @name Pornhub Pro-ish
// @namespace https://www.reddit.com/user/Alpacinator
// @version 3.5.7
// @include *://*.pornhub.com/*
// @grant none
// @description Alters and improves the PH experience, see addition info
// ==/UserScript==
(function() {
'use strict';
// central logging function
function log(message) {
console.log(`Pornhub Pro-ish: ${message}`);
}
// still gotta find a better place for this
let inputFilterWords;
// these are the toggles that are dynamically created.
// Only the changeEvent should be created manually.
// the creation of the locally stored states is done automatically thanks to createToggle & getToggleState
const toggles = [{
label: 'Sort within playlists',
key: 'sortWithinPlaylistsState',
changeEvent: initializeSortWithinPlaylists,
id: 'sortWithinPlaylistsToggle',
defaultState: false
}, {
label: 'Sort videos by 🏆',
key: 'sortByTrophyState',
changeEvent: initializeSortByTrophy,
id: 'sortByTrophyToggle',
defaultState: false
}, {
label: 'Sort videos by duration',
key: 'sortByDurationState',
changeEvent: initializeSortByDuration,
id: 'sortByDurationToggle',
defaultState: false
}, {
label: 'Hide watched videos',
key: 'hideWatchedState',
changeEvent: hideVideos,
id: 'hideWatchedToggle',
defaultState: false
}, {
label: 'Hide paid content',
key: 'hidePaidContentState',
changeEvent: hideVideos,
id: 'hidePaidContentToggle',
defaultState: true
}, {
label: 'Always use English',
key: 'redirectToEnglishState',
changeEvent: redirectToEnglish,
id: 'redirectToEnglishToggle',
defaultState: true
}, {
label: 'Mute by default',
key: 'muteState',
changeEvent: initializeMuteVideos,
id: 'muteToggle',
defaultState: false
}, {
label: 'Hide cursor on video',
key: 'cursorHideState',
changeEvent: initializeCursorHide,
id: 'cursorHideToggle',
defaultState: true
}];
function createSideMenu() {
log('Creating menu..');
let menuShowState = getToggleState('menuShowState', true); // Get initial state from localStorage for the menu, default is true(show)
const sideMenu = document.createElement('div');
sideMenu.id = 'sideMenu'; // Assign an ID for easier reference
sideMenu.style.position = 'fixed';
sideMenu.style.top = '0';
sideMenu.style.left = '0';
sideMenu.style.padding = '60px 20px 20px 20px'; // Top padding 40px, rest 20px
sideMenu.style.height = '100%'; // Full height for mobile view
sideMenu.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
sideMenu.style.zIndex = '9999';
sideMenu.style.display = 'flex';
sideMenu.style.flexDirection = 'column'; // Stack items vertically
sideMenu.style.justifyContent = 'center';
sideMenu.style.alignItems = 'left';
// Add the toggles dynamically to the side menu
// using const toggles and createToggle
toggles.forEach(({
label,
key,
changeEvent,
id,
defaultState
}) => {
const toggle = createToggle(label, key, changeEvent, id, defaultState);
sideMenu.appendChild(toggle);
});
// create the two manual buttons
var sortByTrophyManualButton = createButton(
'Put 🏆 first manually',
'black',
sortByTrophyButton
);
sideMenu.appendChild(sortByTrophyManualButton);
var sortBylengthManualButton = createButton(
'Sort by duration',
'black',
sortByDurationButton
);
sideMenu.appendChild(sortBylengthManualButton);
// this shows a little label above the filter input for the user
var filterInfo = document.createElement('span');
filterInfo.textContent = 'Words to filter out:';
filterInfo.style.color = 'white';
filterInfo.style.marginTop = '20px';
filterInfo.style.paddingLeft = '20%';
filterInfo.style.lineHeight = '20px'; // Match the height of the toggle for vertical alignment
filterInfo.style.alignItems = 'center';
filterInfo.style.justifyContent = 'center';
filterInfo.style.fontSize = '14px'; // Adjust font size if needed
sideMenu.appendChild(filterInfo);
// create the text input for the filter
inputFilterWords = createTextInput('inputFilterWords', 'Seperate with space or comma', updateFilterWords);
var savedFilterWords = localStorage.getItem('savedFilterWords');
if (savedFilterWords) {
inputFilterWords.value = savedFilterWords; // Set the value of the input field
}
sideMenu.appendChild(inputFilterWords);
// Insert the menu into the body
document.body.insertBefore(sideMenu, document.body.firstChild);
// Add the expand menu toggle
createMenuToggle();
// load the visibility state of the side menu and apply the according styling
syncSideMenu();
}
// this block is responsible for hiding and showing the side menu
// creating the toggle, and syncing the state with the saved show/hide menu state
function createMenuToggle() {
const symbol = document.createElement('div');
const menuShowState = getToggleState('menuShowState');
symbol.id = 'menuToggle';
symbol.textContent = menuShowState ? 'Hide Menu' : 'Show Menu';
symbol.style.position = 'fixed';
symbol.style.left = '5px'; // Position on the right
symbol.style.top = '5px';
symbol.style.fontSize = '12pt';
symbol.style.color = 'orange';
symbol.style.cursor = 'pointer';
symbol.style.zIndex = '10000';
symbol.style.transition = 'all 0.3s';
symbol.style.padding = '5px 10px';
symbol.style.backgroundColor = 'black';
symbol.style.border = '1px solid orange';
symbol.style.borderRadius = '15px';
symbol.addEventListener('click', toggleMenuShow);
document.body.appendChild(symbol);
}
function showMenu() {
const sideMenu = document.getElementById('sideMenu');
if (sideMenu) {
// Show menu
sideMenu.style.visibility = 'visible';
sideMenu.style.transition = 'opacity 0.5s ease, transform 0.5s ease, visibility 0s ease';
sideMenu.style.opacity = '1';
sideMenu.style.transform = 'translateX(0)';
const menuToggle = document.getElementById('menuToggle');
menuToggle.textContent = 'Hide Menu';
}
}
function hideMenu() {
const sideMenu = document.getElementById('sideMenu');
if (sideMenu) {
// Hide menu
sideMenu.style.visibility = 'hidden';
sideMenu.style.transition = 'opacity 0.5s ease, transform 0.5s ease, visibility 0s ease 0.5s';
sideMenu.style.opacity = '0';
sideMenu.style.transform = 'translateX(-100%)';
const menuToggle = document.getElementById('menuToggle');
menuToggle.textContent = 'Show Menu';
}
}
function toggleMenuShow() {
const sideMenu = document.getElementById('sideMenu');
let menuShowState = getToggleState('menuShowState');
if (sideMenu) {
if (!menuShowState) {
showMenu();
} else {
hideMenu();
}
menuShowState = !menuShowState;
localStorage.setItem('menuShowState', menuShowState);
}
}
function syncSideMenu() {
let menuShowState = getToggleState('menuShowState');
if (menuShowState) {
showMenu();
} else {
hideMenu();
}
}
// end block for showing/hiding menu
// Update the toggle creation to include default states
function createToggle(labelText, localStorageKey, changeEvent, id, defaultState = false) {
var container = document.createElement('div');
container.style.display = 'flex';
container.style.alignItems = 'center';
container.style.marginBottom = '15px'; // Add space between toggle and next element if needed
var toggle = document.createElement('div');
toggle.style.position = 'relative';
toggle.style.width = '40px';
toggle.style.height = '20px';
toggle.style.backgroundColor = getToggleState(localStorageKey, defaultState) ? 'orange' : 'grey';
toggle.style.borderRadius = '20px';
toggle.style.cursor = 'pointer';
toggle.style.transition = 'background-color 0.2s';
toggle.id = id;
var slider = document.createElement('div');
slider.style.position = 'absolute';
slider.style.left = getToggleState(localStorageKey, defaultState) ? '22px' : '2px';
slider.style.width = '16px';
slider.style.height = '16px';
slider.style.backgroundColor = 'white';
slider.style.borderRadius = '50%';
slider.style.transition = 'left 0.2s';
slider.style.top = '2px'; // Vertically center the slider within the toggle
toggle.appendChild(slider);
// this is used to save the state of the toggle to localStorage automatically
toggle.addEventListener('click', function() {
var currentState = getToggleState(localStorageKey, defaultState);
localStorage.setItem(localStorageKey, !currentState);
toggle.style.backgroundColor = !currentState ? 'orange' : 'grey';
slider.style.left = !currentState ? '22px' : '2px';
changeEvent();
});
var span = document.createElement('span');
span.textContent = labelText;
span.style.color = 'white';
span.style.marginLeft = '15px'; // Space between toggle and text
span.style.lineHeight = '20px'; // Match the height of the toggle for vertical alignment
span.style.fontSize = '14px'; // Adjust font size if needed
span.style.paddingTop = '3px'; // Adjust font size if needed
container.appendChild(toggle);
container.appendChild(span);
return container;
}
// this checks the current state of the toggles using the ID and what they should be according to the saved values in LocalStorage
function updateToggleStates() {
toggles.forEach(({
id,
key
}) => {
const toggleElement = document.getElementById(id);
const currentState = getToggleState(key);
if (toggleElement) {
const expectedColor = currentState ? 'orange' : 'grey';
const slider = toggleElement.querySelector('div');
toggleElement.style.backgroundColor = expectedColor;
if (slider) {
slider.style.left = currentState ? '22px' : '2px';
}
}
});
}
function createButton(text, bgColor, clickEvent) {
var button = document.createElement('button');
button.textContent = text;
button.style.marginRight = '0px';
button.style.marginBottom = '15px';
button.style.padding = '5px 10px';
button.style.backgroundColor = bgColor;
button.style.color = 'white';
button.style.border = '1px solid white';
button.style.borderRadius = '10px';
button.style.cursor = 'pointer';
button.style.transition = 'background-color 0.3s, color 0.3s, border-color 0.3s';
// JavaScript hover effects
button.addEventListener('mouseover', function() {
button.style.color = 'orange';
button.style.borderColor = 'orange';
});
button.addEventListener('mouseout', function() {
button.style.color = 'white';
button.style.borderColor = 'white';
});
button.addEventListener('click', function() {
button.style.backgroundColor = 'orange';
setTimeout(function() {
button.style.backgroundColor = bgColor;
}, 100);
clickEvent();
});
return button;
}
// dynamically create text inputs function for the menu
function createTextInput(id, placeholder, inputEvent) {
var input = document.createElement('input');
input.type = 'text';
input.id = id;
input.placeholder = placeholder;
input.style.marginRight = '0px';
input.style.marginBottom = '15px';
input.style.border = '1px solid grey';
input.style.borderRadius = '15px';
input.style.padding = '5px 10px';
input.addEventListener('input', inputEvent);
return input;
}
// this is used to fetch the state of a setting in LocalStorage
function getToggleState(localStorageKey, defaultState = false) {
const savedState = localStorage.getItem(localStorageKey);
if (savedState === null) { // No saved state exists
localStorage.setItem(localStorageKey, defaultState); // Set the default state in localStorage
return defaultState;
}
return savedState === 'true';
}
// manage the filter words to hide certain videos if the title includes certain words
function updateFilterWords() {
var inputFilterWordsValue = inputFilterWords.value;
localStorage.setItem('savedFilterWords', inputFilterWordsValue);
hideVideos();
}
// Check whatever the state of the toggles is and execute the functions accordingly
function initializeMuteVideos() {
var muteVideosEnabled = getToggleState('muteState');
if (muteVideosEnabled) {
clickMuteButton();
}
}
function initializeSortByTrophy() {
var reorderItemsEnabled = getToggleState('sortByTrophyState');
if (reorderItemsEnabled) {
sortByTrophy();
}
}
function initializeSortByDuration() {
var sortByDurationEnabled = getToggleState('sortByDurationState');
if (sortByDurationEnabled) {
sortByDuration();
}
}
// this one will only be run when the toggle is flipped because it consists of two other initializes that are already called upon mutation/page load
function initializeSortWithinPlaylists() {
initializeSortByTrophy();
initializeSortByDuration();
}
function initializeCursorHide() {
var cursorHideStateEnabled = getToggleState('cursorHideState');
if (cursorHideStateEnabled) {
cursorHide(true);
} else {
cursorHide(false);
}
}
function hideVideos() {
var hideWatchedEnabled = getToggleState('hideWatchedState');
var hidePaidContentEnabled = getToggleState('hidePaidContentState');
// Use findVideoULs to get the <ul> elements
const ulElements = findVideoULs(true);
// Get all <li> elements within the found <ul> elements
const liElements = ulElements.flatMap(ul => Array.from(ul.querySelectorAll('li')));
const savedFilterWords = localStorage.getItem('savedFilterWords') || ''; // Handle null case
const filterWords = savedFilterWords ? savedFilterWords.split(/,\s*|\s+/).map(word => word.trim().toLowerCase()) : [];
const hidePaidContentState = getToggleState('hidePaidContentState');
const hideWatchedState = getToggleState('hideWatchedState');
liElements.forEach(function(li) {
const liTextContent = li.textContent.toLowerCase();
// Check if the current <li> has a watched indicator, excluding those with the 'hidden' class
const watchedDiv = li.querySelector('.watchedVideoText, .watchedVideo');
const isWatched = watchedDiv && !(watchedDiv.classList.contains('watchedVideoText') && watchedDiv.classList.contains('hidden'));
// Selectors for paid/premium/private content
const priceSpan = li.querySelector('span.price');
const premiumIcon = li.querySelector('.premiumicon'); // Corrected selector
const aHrefJavaVoid = li.querySelector('a');
const privateOverlay = li.querySelector('img.privateOverlay');
// Determine if the video should be hidden
const shouldHideByFilterWords = filterWords.length > 0 && filterWords.some(word => word.length >= 3 && liTextContent.includes(word));
const shouldHideWatchedVideos = hideWatchedState && isWatched; // Corrected condition to check isWatched
const shouldHidePaidContent = hidePaidContentState && (priceSpan || premiumIcon || privateOverlay || (aHrefJavaVoid && aHrefJavaVoid.getAttribute('href') === 'javascript:void(0)'));
// Hide or show the <li> element based on conditions
li.style.display = (shouldHideByFilterWords || shouldHideWatchedVideos || shouldHidePaidContent) ? 'none' : 'block';
});
}
// Utility function to find all <ul> elements with class 'videos',
// excluding those with specified IDs
// used for sortByDuration and sortByTrophy
function findVideoULs(sortWithinPlaylistsEnabled = true) {
// Define IDs for <ul> elements that should be excluded when sorting within playlists is disabled
const playlistIdsToExclude = ['videoPlaylist', 'videoPlaylistSection', 'playListSection'];
// Get all <ul> elements with class 'videos' or 'videoList'
const allVideoULs = document.querySelectorAll('ul.videos, ul.videoList');
// Convert NodeList to an Array
const videoULArray = Array.from(allVideoULs);
// Filter <ul> elements: Exclude playlists if sorting within playlists is disabled
const filteredULs = videoULArray.filter(ul => {
// Exclude IDs only when sortWithinPlaylistsEnabled is false
const shouldExclude = !sortWithinPlaylistsEnabled && playlistIdsToExclude.includes(ul.id);
// Include the <ul> if it should not be excluded
return !shouldExclude;
});
return filteredULs;
}
function sortByDuration() {
function parseDuration(durationString) {
const [minutes, seconds] = durationString.split(':').map(Number);
return minutes * 60 + seconds;
}
function sortLiElementsByDurationDesc(ulElement) {
const liElements = Array.from(ulElement.querySelectorAll('li'))
.filter(li => li.querySelector('.duration'));
if (liElements.length === 0) {
console.warn(`No elements with duration found in the list container.`);
return;
}
liElements.sort((a, b) => {
const durationA = parseDuration(a.querySelector('.duration').textContent);
const durationB = parseDuration(b.querySelector('.duration').textContent);
return durationB - durationA;
});
liElements.forEach(li => {
ulElement.appendChild(li);
});
}
// Use forced disable toggle or check the actual toggle state
const sortWithinPlaylistsEnabled = getToggleState('sortWithinPlaylistsState');
const ulsToSort = findVideoULs(sortWithinPlaylistsEnabled);
if (ulsToSort.length === 0) {
console.warn('No <ul> elements with class "videos" found.');
return;
}
ulsToSort.forEach(ulElement => {
sortLiElementsByDurationDesc(ulElement);
});
}
function sortByTrophy() {
function sortLiElementsByTrophy(ulElement) {
const liElements = Array.from(ulElement.querySelectorAll('li'));
const freePremiumVideoItems = [];
const otherItems = [];
liElements.forEach(li => {
const childSpan = li.querySelector('span.award-icon');
if (childSpan) {
freePremiumVideoItems.push(li);
} else {
otherItems.push(li);
}
});
const reorderedLiElements = freePremiumVideoItems.concat(otherItems);
reorderedLiElements.forEach(li => {
ulElement.appendChild(li);
});
}
// Use forced disable toggle or check the actual toggle state
const sortWithinPlaylistsEnabled = getToggleState('sortWithinPlaylistsState');
const ulsToSort = findVideoULs(sortWithinPlaylistsEnabled);
if (ulsToSort.length === 0) {
console.warn('No <ul> elements with class "videos" found.');
return;
}
ulsToSort.forEach(ulElement => {
sortLiElementsByTrophy(ulElement);
});
}
function sortByDurationButton() {
function parseDuration(durationString) {
const [minutes, seconds] = durationString.split(':').map(Number);
return minutes * 60 + seconds;
}
function sortLiElementsByDurationDesc(ulElement) {
const liElements = Array.from(ulElement.querySelectorAll('li'))
.filter(li => li.querySelector('.duration'));
if (liElements.length === 0) {
console.warn(`No elements with duration found in the list container.`);
return;
}
liElements.sort((a, b) => {
const durationA = parseDuration(a.querySelector('.duration').textContent);
const durationB = parseDuration(b.querySelector('.duration').textContent);
return durationB - durationA;
});
liElements.forEach(li => {
ulElement.appendChild(li);
});
}
const ulsToSort = findVideoULs(true);
if (ulsToSort.length === 0) {
console.warn('No <ul> elements with class "videos" found.');
return;
}
ulsToSort.forEach(ulElement => {
sortLiElementsByDurationDesc(ulElement);
});
}
function sortByTrophyButton() {
function sortLiElementsByTrophy(ulElement) {
const liElements = Array.from(ulElement.querySelectorAll('li'));
const freePremiumVideoItems = [];
const otherItems = [];
liElements.forEach(li => {
const childSpan = li.querySelector('span.award-icon');
if (childSpan) {
freePremiumVideoItems.push(li);
} else {
otherItems.push(li);
}
});
const reorderedLiElements = freePremiumVideoItems.concat(otherItems);
reorderedLiElements.forEach(li => {
ulElement.appendChild(li);
});
}
// Use forced disable toggle or check the actual toggle state
const ulsToSort = findVideoULs(true);
if (ulsToSort.length === 0) {
console.warn('No <ul> elements with class "videos" found.');
return;
}
ulsToSort.forEach(ulElement => {
sortLiElementsByTrophy(ulElement);
});
}
// this simulates a click on the mute button in the videoplayer
function clickMuteButton() {
function simulateMouse(element, eventType) {
const event = new Event(eventType, {
view: window,
bubbles: true,
cancelable: true
});
element.dispatchEvent(event);
}
const muteDivs = document.querySelectorAll('div.mgp_volume > div[data-text="Mute"]');
if (muteDivs.length === 0) return;
muteDivs.forEach((div, index) => {
simulateMouse(div, 'mouseover');
simulateMouse(div, 'focus');
simulateMouse(div, 'mousedown');
simulateMouse(div, 'mouseup');
simulateMouse(div, 'click');
if (div) {
const event = new MouseEvent('mouseover', {
view: window,
bubbles: true
});
div.dispatchEvent(event);
}
});
log(`${muteDivs.length} video elements were muted.`);
}
// Hide the load more buttons, all the items should already show
function hideElements() {
log('Hiding elements..');
// Hide the welcoming messages regarding different countries and the store
if (document.getElementById('countryRedirectMessage')) {
document.getElementById('countryRedirectMessage').style.display = 'none';
}
if (document.getElementById('welcome')) {
document.getElementById('welcome').style.display = 'none';
}
// This will hide the ugly long empty blocks under videos while going through pages
// Select all divs with the class 'pornInLangWrapper'
const divs = document.querySelectorAll('div.pornInLangWrapper');
// Loop through each div and hide it
divs.forEach(div => {
div.style.display = 'none';
});
// remove load more buttons
// under the video
if (document.getElementById('loadMoreRelatedVideosCenter')) {
document.getElementById('loadMoreRelatedVideosCenter').style.display = 'none';
}
// on the right side of the video
const recommendedLoadMoreElements = document.querySelectorAll('[data-label="recommended_load_more"]');
recommendedLoadMoreElements.forEach(element => {
element.style.display = 'none';
});
}
// Usage
// cursorHide(true); // To enable hiding the cursor
// cursorHide(false); // To disable hiding the cursor
// this makes use of the class that is added to the videowrapper when a video is playing, it is removed when paused
function cursorHide(enable) {
// Check if the style element already exists
let existingStyle = document.getElementById('cursor-hide-style');
if (enable) {
if (!existingStyle) {
log("cursorHide is enabled but the style doesn't exist yet, creating cursorHide style..");
// Create and append the style element if it doesn't exist
const style = document.createElement('style');
style.id = 'cursor-hide-style';
style.textContent = `
/* Define the cursor hiding animation */
@keyframes hideCursor {
0% {
cursor: default;
}
99% {
cursor: default;
}
100% {
cursor: none;
}
}
/* Apply the animation on hover */
.mgp_playingState {
animation: none;
}
.mgp_playingState:hover {
animation: hideCursor 3s forwards;
}
`;
document.head.appendChild(style);
log('cursorHide style added.');
}
} else {
if (existingStyle) {
log("cursorHide is disabled but the style still exists, removing cursorHide style..");
// Remove the style element if it exists
existingStyle.remove();
log('cursorHide style removed.');
}
}
}
// Change the language of the website to English if that is not the case
// Since this script sometimes uses words to function this is still neccesary
function redirectToEnglish() {
var redirectToEnglishStateEnabled = getToggleState('redirectToEnglishState');
if (redirectToEnglishStateEnabled) {
const isNotEnglish = () => {
const languageDropdownLI = document.querySelector('li.languageDropdown');
return !languageDropdownLI || (languageDropdownLI.querySelector('span.networkTab') && languageDropdownLI.querySelector('span.networkTab').textContent.trim().toLowerCase() !== 'en');
};
const findEnLink = () => {
const enLanguageOptionLI = document.querySelector('li[data-lang="en"]');
if (!enLanguageOptionLI) return null;
const enLanguageLink = enLanguageOptionLI.querySelector('a.networkTab');
return !enLanguageLink ? console.error('Anchor element with class "networkTab" not found within the specified <li> to check the current language.') : enLanguageLink;
};
function checkAndClick() {
if (isNotEnglish()) {
const enLink = findEnLink();
// If we've already tried clicking and it didn't work, stop trying
if (!enLink) return log('No English link to click. Giving up.');
enLink.click();
log('Clicked the link:', enLink);
} else {
log('Current language is already English. No action needed.');
}
}
setTimeout(checkAndClick, 1000); // Adjust delay if necessary
}
}
// this adds a transparant red square over items if you press delete
// to show you which you have deleted when editing a playlist
function addRedOverlay(element) {
var overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'red';
overlay.style.opacity = '0.5';
overlay.style.pointerEvents = 'none';
var parentLi = element.closest('li');
if (parentLi) {
parentLi.style.position = 'relative';
parentLi.appendChild(overlay);
}
}
// when editing playlists, whenever the delete button on a video is clicked, this will create a transparent red square around the video
document.addEventListener('click', function(event) {
if (event.target && event.target.matches('button[onclick="deleteFromPlaylist(this);"]')) {
addRedOverlay(event.target);
log('Added red overlay to the playlist item');
}
});
// run updateToggleStates when changing to a tab (using visibilityState to see if it is active/visible) so the state will be synced:
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'visible') {
log('Tab is visible, updating toggle states..');
// update toggle states
updateToggleStates();
// this should trigger a DOM change, so runOnMutation should run and things should update
// make sure the menu visibility matches the state
syncSideMenu();
// change language to English
redirectToEnglish();
// check if the cursor should be hidden
initializeCursorHide();
}
});
// this will be run when the observer spots mutations
function initializeEverything() {
initializeSortByTrophy();
initializeSortByDuration();
initializeMuteVideos();
hideVideos();
}
// run this when the page is fully loaded
window.onload = function() {
// hide 'Load More' buttons and the like
hideElements();
// create the menu
createSideMenu();
// change lang to english
redirectToEnglish();
// check if the cursor should be hidden
initializeCursorHide();
// no need to initialize everything manually, that will trigger on page load (runOnMutation will take care of the rest)
// but this makes the page ready to go as soon as it is done loading, so we will keep it for now
initializeEverything();
// keep checking for mutations using an observer
function runOnMutation() {
clearTimeout(runOnMutation.timeout);
runOnMutation.timeout = setTimeout(() => {
initializeEverything();
}, 500);
}
const observer = new MutationObserver(function(mutations) {
runOnMutation();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
};
})();