您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlights stocks that meet thresholds for buying and selling.
// ==UserScript== // @name [GC] Smarter Stock Market // @description Highlights stocks that meet thresholds for buying and selling. // @icon https://www.google.com/s2/favicons?sz=64&domain=grundos.cafe // @match https://www.grundos.cafe/games/stockmarket/* // @author aether // @namespace https://github.com/hlmartin/gc-userscripts/ // @version 1.1.0 // @license MIT // @supportURL https://github.com/hlmartin/gc-userscripts/issues // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/sortable.min.js // @resource SORTABLE_CSS https://cdn.jsdelivr.net/gh/tofsjonas/[email protected]/dist/sortable-base.min.css // @grant GM_addStyle // @grant GM_getResourceText // ==/UserScript== // ------------------------------------------------------ // User editable variables const BUY_THRESHOLD = 15; // in neopoints const SELL_THRESHOLD = 150.00; // in change percentage const HIGHLIGHT_COLOUR = ''; // a hex colour or HTML-safe colour name // ------------------------------------------------------ const sortableCss = GM_getResourceText("SORTABLE_CSS"); GM_addStyle(sortableCss); const isDarkMode = document.querySelector('html').getAttribute('data-dark-mode') === 'true'; const defaultHighlightColour = isDarkMode ? '#313171' : '#bdbdff'; const highlightColour = HIGHLIGHT_COLOUR === '' ? defaultHighlightColour : HIGHLIGHT_COLOUR; const customCss = ` .highlighted-cell { background-color: ${highlightColour} !important; } .no-stocks { font-size: 24px; text-align: center; } `; GM_addStyle(customCss); let tableName; const tableClass = () => `.${tableName}-table`; const cellClass = () => `.${tableName}-cell`; const sortTable = ({applyHighlightFn, displayNoHighlightMessage, noHighlightMessage}) => { const table = document.querySelector(tableClass()); if (!table) { return; } table.classList.add("sortable"); // Exclude the top-most headers and logo column from being sortable const hasTieredHeaders = document.querySelectorAll(`${tableClass()} > thead > tr`).length > 1; if (hasTieredHeaders) { const topHeaders = document.querySelectorAll(`${tableClass()} > thead > tr:first-of-type > th`); topHeaders.forEach((header) => header.classList.add("no-sort")); } const logo = document.querySelector(`${tableClass()} > thead > tr:last-of-type > th:first-of-type`); logo.classList.add("no-sort"); // Give the totals row a unique id so we can find it later const lastRow = document.querySelector(`${tableClass()} > tbody > tr:last-of-type`); if (lastRow.textContent.includes("Totals")) { lastRow.id = "totals-row"; } // Give the % Change rows a more normative sort value so it properly sorts them // in the order of positive, zero, and negative percentages. const rows = document.querySelectorAll(`${tableClass()} > tbody > tr`); rows.forEach((row) => { const isHidden = window.getComputedStyle(row).display === "none"; if (isHidden) { return; } const change = row.querySelector(`${cellClass()}:last-of-type`); const sortValue = change.textContent.replace(/\s|%|\+/g, ''); change.setAttribute("data-sort", sortValue) if (applyHighlightFn(row)) { row.classList.add("highlighted-row"); Object.values(row.children).forEach((cell) => cell.classList.add("highlighted-cell")); } }); const highlightedRow = document.querySelector(".highlighted-row"); if (!highlightedRow && displayNoHighlightMessage) { const div = document.createElement("div"); div.textContent = noHighlightMessage; div.classList.add("no-stocks"); const tableContainer = table.closest("div.center"); tableContainer.prepend(div); }; } const isSellable = (row) => { const change = row.querySelector(`${cellClass()}:last-of-type`); const sortValue = change.getAttribute("data-sort"); return parseFloat(sortValue) >= SELL_THRESHOLD; }; const isBuyable = (row) => { const currentValue = row.querySelector(`${cellClass()}:nth-child(6)`).textContent; return parseInt(currentValue) == BUY_THRESHOLD; }; const sortPortfolio = () => { const config = { applyHighlightFn: isSellable, displayNoHighlightMessage: false }; sortTable(config); // default sort by % change const change = document.querySelector(`${tableClass()} > thead > tr:last-of-type > th:last-of-type`) change.click(); } const sortStocks = () => { const config = { applyHighlightFn: isBuyable, displayNoHighlightMessage: true, noHighlightMessage: "😭 There are no buyable stocks at this time." }; sortTable(config); highlightsToTop(); } const highlightsToTop = () => { // Moves any highlighted values to the top. const highlighted = document.querySelectorAll(".highlighted-row"); if (!highlighted) { return; } const tbody = document.querySelector(`${tableClass()} > tbody`); highlighted.forEach((row) => { const clonedRow = row.cloneNode(true); tbody.prepend(clonedRow); row.remove(); }); } const totalsToBottom = () => { // Ensures the total stays at the bottom. const totals = document.getElementById("totals-row"); if (!totals) { return; } const tbody = document.querySelector(`${tableClass()} > tbody`); const cloneTotals = totals.cloneNode(true); tbody.append(cloneTotals); totals.remove(); } document.addEventListener('sort-end', function() { totalsToBottom(); highlightsToTop(); }); window.addEventListener('load', function() { switch (document.location.pathname) { case "/games/stockmarket/stocks/": tableName = 'stock'; sortStocks(); break; case "/games/stockmarket/portfolio/": tableName = 'portfolio'; sortPortfolio(); break; } });