OkCupid Utils

Add keybinds for easy navigation

// ==UserScript==
// @name        OkCupid Utils
// @match       https://www.okcupid.com/*
// @grant       none
// @version     1.1.2
// @author      kelo & Asafff
// @license     BSD
// @description Add keybinds for easy navigation
// @namespace   https://greasyfork.org/users/1090195
// ==/UserScript==

(function() {
    'use strict';

    /* ========================================
     * Who Likes You - Unblur Functionality
     * ========================================
     * This section is currently disabled as the paywall bypass no longer works.
     * Kept for reference in case OkCupid reverts changes or exposes similar functionality.
     *
     * To re-enable: uncomment this section and the waitForElementToDisplay call at the bottom.
     */

    /*
    function waitForElementToDisplay(selector, time) {
        if(document.querySelector(selector) != null) {
            removePaywallClickEvent();
            hideElement();
            changeFilterProperty();
            allowOverflow();
            return;
        }
        else {
            setTimeout(function() {
                waitForElementToDisplay(selector, time);
            }, time);
        }
    }

    function changeFilterProperty() {
        let elements = document.querySelectorAll(".usercard-placeholder-thumb");

        elements.forEach(function(element) {
            element.style.filter = "none";
        });
    }

    function allowOverflow() {
        let usercardElem = document.querySelector('.usercard-placeholder');
        let parent = usercardElem.parentElement;
        let grandparent = parent.parentElement;

        // Remove fading gradient
        parent.style.width = '81.5%';
        parent.style.zIndex = 0;

        // Allow overflow
        grandparent.style.height = '100%';
        grandparent.style.maxHeight = '100%';
    }

    function hideElement() {
        let elementToHide = document.querySelector(".likes-you-paywall-explainer-cta");

        if(elementToHide) {
            elementToHide.style.display = "none";
        }
    }

    function removePaywallClickEvent() {
        const elementToReplace = document.querySelector('.likes-you-paywall-with-likes-link');

        if (elementToReplace) {
            const newDiv = document.createElement('div');
            newDiv.innerHTML = elementToReplace.innerHTML;
            elementToReplace.parentNode.replaceChild(newDiv, elementToReplace);
        }
    }
    */

    /* ========================================
     * Navigation Keybinds
     * ========================================
     */

    /**
     * Determines if the keyboard event originated from an input element
     * where text editing is expected and hotkeys should be suppressed.
     * @param {KeyboardEvent} event - The keyboard event to check
     * @returns {boolean} True if event originated from a text input context
     */
    function isTextInputContext(event) {
        const target = event.target;
        const tagName = target.tagName.toLowerCase();
        const inputType = target.type?.toLowerCase();

        // Check for text input elements
        if (tagName === 'textarea') {
            return true;
        }

        if (tagName === 'input') {
            const textInputTypes = ['text', 'email', 'password', 'search', 'tel', 'url', 'number'];
            return !inputType || textInputTypes.includes(inputType);
        }

        // Check for contenteditable elements
        if (target.isContentEditable) {
            return true;
        }

        return false;
    }

    /**
     * Checks if the photo overlay modal is currently open.
     * @returns {boolean} True if the modal is open
     */
    function isPhotoModalOpen() {
        const modal = document.querySelector('.photo-overlay-fullscreenoverlay');
        return modal !== null;
    }

    /**
     * Closes the photo overlay modal by clicking the close button.
     */
    function closePhotoModal() {
        const closeButton = document.getElementById('closeButton');
        if (closeButton) {
            closeButton.click();
        }
    }

    /**
     * Executes navigation actions based on keyboard input.
     * Ignores events originating from text input contexts.
     *
     * Keybinds:
     * - X: Pass on current profile
     * - V: Like current profile
     * - F or Right Arrow: Next picture
     * - D or Left Arrow: Previous picture
     * - Space: Open picture in fullscreen
     * - Up Arrow: Scroll up (closes photo modal if open)
     * - Down Arrow: Scroll down (closes photo modal if open)
     *
     * @param {KeyboardEvent} event - The keyboard event to handle
     */
    function handleNavigationKeyPress(event) {
        // Suppress hotkeys when typing in input fields
        if (isTextInputContext(event)) {
            return;
        }

        const modalOpen = isPhotoModalOpen();

        // Close modal and scroll when up/down arrows are pressed while modal is open
        if (modalOpen && (event.code === 'ArrowUp' || event.code === 'ArrowDown')) {
            event.preventDefault();
            closePhotoModal();

            // Small delay to allow modal to close before scrolling
            setTimeout(() => {
                const scrollAmount = event.code === 'ArrowUp' ? -100 : 100;
                window.scrollBy({ top: scrollAmount, behavior: 'smooth' });
            }, 100);
            return;
        }

        // Allow native left/right navigation in the modal
        if (modalOpen && (event.code === 'ArrowLeft' || event.code === 'ArrowRight' ||
                          event.code === 'KeyD' || event.code === 'KeyF')) {
            return;
        }

        const passBtn = document.querySelector('button.dt-action-buttons-button.pass');
        const likeBtn = document.querySelector('button.dt-action-buttons-button.like');
        const picturesPrev = document.querySelector('button.sliding-pagination-button.prev');
        const picturesNext = document.querySelector('button.sliding-pagination-button.next');
        const picSliderDiv = document.querySelector('div.sliding-pagination-inner-content');

        switch(event.code) {
            case 'KeyX':
                event.preventDefault();
                passBtn?.click();
                break;
            case 'KeyV':
                event.preventDefault();
                likeBtn?.click();
                break;
            case 'ArrowRight':
            case 'KeyF':
                event.preventDefault();
                picturesNext?.click();
                break;
            case 'ArrowLeft':
            case 'KeyD':
                event.preventDefault();
                picturesPrev?.click();
                break;
            case 'Space':
                event.preventDefault();
                picSliderDiv?.firstChild?.click();
                break;
            case 'ArrowUp':
                event.preventDefault();
                window.scrollBy({ top: -100, behavior: 'smooth' });
                break;
            case 'ArrowDown':
                event.preventDefault();
                window.scrollBy({ top: 100, behavior: 'smooth' });
                break;
            default:
                break;
        }
    }

    document.addEventListener('keydown', handleNavigationKeyPress);

    /* ========================================
     * Initialization
     * ========================================
     */

    // Uncomment to re-enable "Who Likes You" unblur functionality:
    // waitForElementToDisplay(".userrows-main", 4000);
})();