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.5
// @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 ---
    GM_addStyle(`
        @keyframes scanline {
            0% { transform: translateY(-100%); }
            100% { transform: translateY(100%); }
        }

        .tech-btn {
            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;
            touch-action: manipulation;
        }

        .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);
        }

        .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;
        }
    `);

    // --- Unified Activation Function ---
    const handleActivation = (button, ticker) => {
        if (event) {
            event.preventDefault();
            event.stopPropagation();
        }

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

    // --- Script for Single Stock Page ---
    // (This part doesn't need event delegation as the button is not re-rendered)
    function setupSingleStockButton() {
        function waitForElement(selector, callback) {
            const element = document.querySelector(selector);
            if (element) {
                callback(element);
            } else {
                const observer = new MutationObserver((mutations, obs) => {
                    const foundElement = document.querySelector(selector);
                    if (foundElement) {
                        obs.disconnect();
                        callback(foundElement);
                    }
                });
                observer.observe(document.body, { childList: true, subtree: true });
            }
        }

        waitForElement('.js-recent-quote-ticker', (tickerElement) => {
            const ticker = tickerElement.textContent.trim().replace(/-/g, '.');
            const button = document.createElement('button');
            button.className = 'tech-btn';
            button.textContent = `Open ${ticker} In Robinhood`;
            button.title = `Go to ${ticker} on Robinhood`;
            button.style.cssText = `
                position: fixed;
                bottom: 20px;
                left: 20px;
                z-index: 1000;
                padding: 14px 28px;
                font-size: 20px;
                border-radius: 9999px;
            `;

            button.addEventListener('touchstart', (e) => handleActivation(button, ticker), { passive: false });
            button.addEventListener('click', (e) => handleActivation(button, ticker));

            document.body.appendChild(button);
        });
    }
    
    // --- Script for Screener Page (Modified for Event Delegation) ---
    function setupScreenerButtons() {
        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;
                touch-action: manipulation;
            }
        `);

        // The logic for adding buttons remains mostly the same, but the event listener is moved.
        const updateButtons = () => {
            const tickerLinks = document.querySelectorAll('a.tab-link[href*="quote.ashx?t="]');
            tickerLinks.forEach(link => {
                const parentCell = link.closest('td');
                if (!parentCell || parentCell.querySelector('.rh-floating-btn')) return;

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

                const button = document.createElement('button');
                button.className = 'tech-btn rh-floating-btn';
                button.textContent = originalTicker;
                button.dataset.ticker = robinhoodTicker; // Store ticker on the element

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

        const screenerTable = document.getElementById('screener-views-table');
        if (screenerTable) {
            // Attach ONE event listener to the parent table
            screenerTable.addEventListener('touchstart', (e) => {
                const targetButton = e.target.closest('.tech-btn');
                if (targetButton) {
                    handleActivation(targetButton, targetButton.dataset.ticker);
                }
            }, { passive: false });

            screenerTable.addEventListener('click', (e) => {
                const targetButton = e.target.closest('.tech-btn');
                if (targetButton) {
                    handleActivation(targetButton, targetButton.dataset.ticker);
                }
            });

            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();
    }
})();