Finviz Redirect to robinhood

Adds buttons linking you to Robinhood from both the single stock page and the screener list.

// ==UserScript==
// @name        Finviz Redirect to robinhood
// @namespace   http://tampermonkey.net/
// @version     1.0
// @description Adds buttons linking you to Robinhood from both the single stock page and the screener list.
// @author      Game Abuse Studios
// @match       https://elite.finviz.com/*
// @license     MIT
// @grant       GM_addStyle
// @grant       window.open
// @run-at      document-idle
// ==/UserScript==

(function() {
    'use strict';

    // --- Inject Global Styles with Gradient Border and Active State ---
    GM_addStyle(`
        @keyframes scanline {
            0% { transform: translateY(-100%); }
            100% { transform: translateY(100%); }
        }

        .tech-btn {
            /* Existing styles */
            border: 2px solid transparent;
            background: linear-gradient(#1a1f2b, #1a1f2b) padding-box,
                        linear-gradient(to right, royalblue, blueviolet) border-box;

            color: #c9d1d9;
            font-family: 'monospace', 'Lucida Console', Monaco, Consolas;
            font-weight: bold;
            box-shadow: 0 2px 5px rgba(0,0,0,0.4);
            transition: all 0.2s ease-in-out;
            cursor: pointer;
            position: relative;
            overflow: hidden;
        }

        /* The :hover and .active classes will now trigger the bright state */
        .tech-btn:hover, .tech-btn.active {
            background: linear-gradient(#2a2f3b, #2a2f3b) padding-box,
                        linear-gradient(to right, dodgerblue, mediumorchid) border-box;
            color: #ffffff;
            transform: scale(1.03);
        }

        /* The scanline effect is now triggered by a separate class, .scanline-active */
        .tech-btn::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(to bottom, transparent 0%, rgba(138, 43, 226, 0.1) 50%, transparent 100%);
            opacity: 0;
            transform: translateY(-100%);
            transition: opacity 0.2s ease;
        }

        .tech-btn.scanline-active::after {
            opacity: 1;
            animation: scanline 0.6s linear;
        }
    `);

    // --- Script for Single Stock Page ---
    function setupSingleStockButton() {
        const DESTINATION_URL = "https://robinhood.com/stocks/";

        function waitForElement(selector, callback) {
            if (document.querySelector(selector)) {
                callback();
            } else {
                const observer = new MutationObserver(() => {
                    if (document.querySelector(selector)) {
                        observer.disconnect();
                        callback();
                    }
                });
                observer.observe(document.body, { childList: true, subtree: true });
            }
        }

        function addButton() {
            const tickerElement = document.querySelector('.js-recent-quote-ticker');
            if (!tickerElement) return;

            const ticker = tickerElement.textContent.trim();
            const cleanedTicker = ticker.replace(/-/g, '.');

            const button = document.createElement('button');
            button.className = 'tech-btn';
            button.textContent = `Open ${cleanedTicker} In Robinhood`;
            button.title = `Go to ${cleanedTicker} on Robinhood`;

            // Apply specific positioning and sizing
            button.style.cssText = `
                position: fixed;
                bottom: 20px;
                left: 20px;
                z-index: 1000;
                padding: 14px 28px;
                font-size: 20px;
                border-radius: 9999px;
            `;
            
            // Handle touch and click events for immediate response
            button.addEventListener('mousedown', () => button.classList.add('active'));
            button.addEventListener('mouseup', () => button.classList.remove('active'));
            button.addEventListener('touchstart', () => button.classList.add('active'));
            button.addEventListener('touchend', () => button.classList.remove('active'));

            button.addEventListener('click', (e) => {
                // This will trigger immediately on touch or click
                button.classList.add('scanline-active'); // Triggers the animation
                window.open(`${DESTINATION_URL}${cleanedTicker}`, '_blank');
                // Remove the animation class after a short delay
                setTimeout(() => button.classList.remove('scanline-active'), 600);
            });

            document.body.appendChild(button);
        }
        waitForElement('.js-recent-quote-ticker', addButton);
    }

    // --- Script for Screener Page ---
    function setupScreenerButtons() {
        // Additional styles for the SMALL screener buttons
        GM_addStyle(`
            .rh-floating-btn {
                height: 18px;
                font-size: 12px;
                border-radius: 9999px;
                padding: 0 5px;
                display: flex;
                align-items: center;
                justify-content: center;
                z-index: 9998;
                border-width: 1px;
                width: auto;
            }
        `);

        function updateButtons(mutationsList, observer) {
            const tickerLinks = document.querySelectorAll('a.tab-link[href*="quote.ashx?t="]');

            tickerLinks.forEach(link => {
                const originalTicker = link.textContent.trim();
                if (!originalTicker.match(/^[A-Z-]+$/)) return;

                const parentCell = link.closest('td');
                if (!parentCell) return;

                if (parentCell.querySelector('.rh-floating-btn')) return;

                const robinhoodTicker = originalTicker.replace('-', '.');

                const button = document.createElement('button');
                button.className = 'tech-btn rh-floating-btn';
                button.textContent = originalTicker;

                // Handle touch and click events for immediate response
                button.addEventListener('mousedown', () => button.classList.add('active'));
                button.addEventListener('mouseup', () => button.classList.remove('active'));
                button.addEventListener('touchstart', () => button.classList.add('active'));
                button.addEventListener('touchend', () => button.classList.remove('active'));

                button.onclick = (e) => {
                    e.stopPropagation();
                    button.classList.add('scanline-active');
                    window.open(`https://robinhood.com/stocks/${robinhoodTicker}`, '_blank');
                    setTimeout(() => button.classList.remove('scanline-active'), 600);
                };

                parentCell.insertBefore(button, link);
                link.remove();
            });
        }

        const screenerTable = document.getElementById('screener-views-table');
        if (screenerTable) {
            const observer = new MutationObserver(updateButtons);
            observer.observe(screenerTable, { childList: true, subtree: true });
            updateButtons();
        }
    }

    // --- Main Logic ---
    if (window.location.href.includes('screener.ashx')) {
        setupScreenerButtons();
    } else {
        setupSingleStockButton();
    }
})();