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