Sleazy Fork is available in English.

Pornhub Pro-ish

Alters and improves the PH experience, see addition info

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==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
        });

    };

})();