OkCupid Utils

Add keybinds for easy navigation

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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://greatest.deepsurf.us/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);
})();