Neopets: Stocks enhancer

Show only the stocks you want, and adds columns for monthly highs and lows

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Neopets: Stocks enhancer
// @author       Tombaugh Regio
// @version      1.4
// @description  Show only the stocks you want, and adds columns for monthly highs and lows
// @namespace    https://greasyfork.org/users/780470
// @match        http://www.neopets.com/stockmarket.phtml?type=list&search=%&bargain=true
// @match        http://www.neopets.com/stockmarket.phtml?type=portfolio
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_openInTab
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// ======================================================================

const PRICE = {
    //Lowest price that can be purchased. Default lowest is 15
    lowest: 15,

    //Highest price to consider
    highest: 15
}

// ======================================================================

//Get stock market data from Neostocks
GM_xmlhttpRequest({
    method: 'GET',
    url: 'https://neostocks.info/?period=1m',
    headers: {
        'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
        'Accept': 'application/atom+xml,application/xml,text/xml',
    },
    onload: function(responseDetails) {
        return new Promise((resolve, reject) => {
            resolve(GM_setValue("responseText", responseDetails.responseText))
        })
    }
})

const stockData = GM_getValue("responseText").match(/{"summary_data":{.+"1m":\[{(.+)}\],"all":.+}/)[1]
const stockTickerInfos = stockData.split("},{")
const monthlyStocks = []

for(const datapoints of stockTickerInfos) {
    const datapoint = datapoints.split(",")
    const ticker = datapoint[0].match(/.+:"(.+)"/)[1]
    const high = datapoint[4].match(/\d+/)[0]
    const low = datapoint[6].match(/\d+/)[0]

    monthlyStocks.push({ticker: ticker, high: high, low: low})
}

let stocksTable, stockContainer
const URL = document.URL

if(URL.includes("portfolio")) {

    //Get the table with all the stock information
    stocksTable = document.evaluate("//a[contains(., 'here')]",
                                    document, null, XPathResult.ANY_TYPE, null ).iterateNext()
        .parentNode.querySelector("table")
    stocksTable.style.marginTop = "2em"
    stockContainer = stocksTable.querySelectorAll("tr")

    //Separate stock headers from rows, and push headers into an array
    let portfolioHeaders, portfolioSubheaders, portfolioRows
    [portfolioHeaders, portfolioSubheaders, ...portfolioRows] = stockContainer

    let topHeaderTitles = [], subheaderTitles = []

    const setCustomHeader = (i, number, header, HTML) => {if(i == number) header.innerHTML = HTML}
    function setNewHeaders(headers, titles, ARRAY){
        for (const [i, header] of headers.childNodes.entries()) {
            for(const OBJECT of ARRAY) {
                setCustomHeader(i, OBJECT.number, header, OBJECT.HTML)
            }
            if (header.nodeName != "#text") {titles.push(header.textContent)}
        }
    }

    setNewHeaders(portfolioHeaders, topHeaderTitles, [{number: 3, HTML: `<b>Month</b>`}])
    setNewHeaders(portfolioSubheaders, subheaderTitles, [{number: 5, HTML: `<b>High</b>`},{number: 9, HTML: `<b>Low</b>`}])


    for (const row of portfolioRows) {
        const cells = row.querySelectorAll("td")
        for (const [i, cell] of cells.entries()) {
            if (cells.length == 9){

                //Find the columns with highs and lows, and set their contents to data from Neostocks
                if (i == subheaderTitles.indexOf("Ticker")) {
                    const cellTicker = cell.querySelector("a").textContent
                    for (const stock of monthlyStocks) {
                        if (cellTicker == stock.ticker) {
                            const cellHigh = cells[subheaderTitles.indexOf("High")]
                            const cellLow = cells[subheaderTitles.indexOf("Low")]
                            cellHigh.innerHTML = `<a href="https://neostocks.info/tickers/${
                                                    stock.ticker}?period=1m" ><b>${
                                                    stock.high}</b></a>`

                            cellLow.innerHTML = `<a href="https://neostocks.info/tickers/${
                                                    stock.ticker}?period=1m" ><b>${
                                                    stock.low}</b></a>`

                            cellHigh.classList.add("monthly-high")
                            cellLow.classList.add("monthly-low")
                        }
                    }
                }

                //Style the current price if it's above the monthly high or below the monthly low
                if (i == subheaderTitles.indexOf("Current Price")) {
                    const cellHigh = cells[subheaderTitles.indexOf("High")]
                    const cellLow = cells[subheaderTitles.indexOf("Low")]

                    if (parseInt(cell.textContent) > parseInt(cellHigh.textContent)) {
                        cell.innerHTML = `<font color="green"><b>${cell.textContent}</b></font>`
                    }

                    if (parseInt(cell.textContent) < parseInt(cellLow.textContent)) {
                        cell.innerHTML = `<font color="red"><b>${cell.textContent}</b></font>`
                    }
                }

                //Hide stock logos
                if (i == subheaderTitles.indexOf("Icon")) {
                    const images = cell.querySelectorAll("img")
                    for(const image of images) {
                        if (image.title.length > 0) {
                            image.style.display = "none"
                        }
                    }
                }
            }
        }
    }

    //Swap rows
    for (const [i, row] of stockContainer.entries()) {
        if (i != 0 && row.children[3] != undefined) {row.children[3].after(row.children[2])}
    }
}

if(URL.includes("bargain")) {

    //Get the table with all the stock information
    stocksTable = document.evaluate("//b[contains(., 'Bargain Stocks')]",
                                    document, null, XPathResult.ANY_TYPE, null ).iterateNext()
        .nextSibling.nextSibling.nextSibling.nextSibling

    stocksTable.style.marginTop = "2em"
    stockContainer = stocksTable.querySelectorAll("tr")

    //Add columns for monthly lows and highs
    stockContainer.forEach(row => {
        row.appendChild(row.lastChild.cloneNode(true))
        row.appendChild(row.lastChild.cloneNode(true))
    })

    //Separate stock headers from rows, and push headers into an array
    let stockHeader, stockRows
    [stockHeader, ...stockRows] = stockContainer

    let headers = []
    stockHeader.childNodes.forEach((header, i) => {
        if (i == 7) {header.innerHTML = `<font color = "white"><b>High</b></font>`}
        if(i == 8) {header.innerHTML = `<font color = "white"><b>Low</b></font>`}
        headers.push(header.innerText)
    })

    //Hide logo and change columns
    for (const row of stockContainer) {
        for (const [i, column] of row.childNodes.entries()) {
            const logoColumn = headers.findIndex(e => e == "Logo")
            const changeColumn = headers.findIndex(e => e == "Change")

            if (i == logoColumn || i == changeColumn) {column.style.display = "none"}
        }
    }

    //For each row, add monthly highs and lows, and hide them if they're outside the specified price range
    for(const row of stockRows) {
        let monthlyHighHTML = `<a href="https://neostocks.info/bargain?period=1m"
                                  title="Click to check if stocks have updated.">n/a</a>`
        let monthlyLowHTML = monthlyHighHTML

        for (const [i, cell] of row.childNodes.entries()) {
            const cellText = cell.innerText
            const tickerColumn = headers.findIndex(e => e == "Ticker")
            const priceColumn = headers.findIndex(e => e == "Curr")
            const highColumn = headers.findIndex(e => e == "High")
            const lowColumn = headers.findIndex(e => e == "Low")

            //Add monthly highs and lows
            for (const stock of monthlyStocks) {
                if (i == tickerColumn && cellText == stock.ticker) {
                    monthlyHighHTML = `<a href="https://neostocks.info/tickers/${
                    stock.ticker}?period=1m" ><b>${
                    stock.high}</b></a>`
                    monthlyLowHTML = `<a href="https://neostocks.info/tickers/${
                    stock.ticker}?period=1m" ><b>${
                    stock.low}</b></a>`
                }
            }

            if (i == highColumn) {cell.innerHTML = monthlyHighHTML}
            if (i == lowColumn) {cell.innerHTML = monthlyLowHTML}


            //Style the price column like the hidden change column
            if (i == priceColumn) {
                const fontColor = cell.nextSibling.childNodes[0].color
                const tooltipText = cell.nextSibling.textContent
                cell.innerHTML = `<a href="#" title="${
                tooltipText}"><font color="${
                fontColor}"><b>${cellText}</b></font></a>`
            }

            //If price is outside prescribed range, hide the row
            if (i == priceColumn && (cellText < PRICE.lowest || cellText > PRICE.highest)) {
                row.style.display = "none"
            }
        }
    }
}