您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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(); } })();