Update stocks script with features when buying stocks
This commit is contained in:
parent
9d3fd00dbf
commit
0cea3d1481
|
@ -11,7 +11,7 @@ Once you have Tampermonkey, click the name of the script you want to install. A
|
||||||
|
|
||||||
### Safety notes:
|
### Safety notes:
|
||||||
|
|
||||||
All of my scripts that have been approved by the /r/neopets Discord mod team as safe and fair are marked with a ✅ after their name.
|
All of my scripts that have been approved by the /r/neopets Discord mod team as safe and fair are marked with a ✅ after their name. If a previous version has been approved, a link to that one will be provided separately.
|
||||||
|
|
||||||
**No** userscripts, not mine or anyone else's, have been officially approved by TNT. Make peace with that before installing any script to use on Neopets.
|
**No** userscripts, not mine or anyone else's, have been officially approved by TNT. Make peace with that before installing any script to use on Neopets.
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ On the status page for the Swashbuckling Academy, Mystery Island Training School
|
||||||
|
|
||||||
\*I haven't tested this script at the Secret Ninja Training School because I don't have a pet even *close* to level 250. I have reason to believe it should work there, but I would appreciate confirmation from anyone who's able to test it out.
|
\*I haven't tested this script at the Secret Ninja Training School because I don't have a pet even *close* to level 250. I have reason to believe it should work there, but I would appreciate confirmation from anyone who's able to test it out.
|
||||||
|
|
||||||
## [Stock Highlighter](https://git.hollymcfarland.com/monorail/NeopetsUserscripts/raw/branch/main/scripts/stock-highlighter.user.js) ✅
|
## [Stock Highlighter](https://git.hollymcfarland.com/monorail/NeopetsUserscripts/raw/branch/main/scripts/stock-highlighter.user.js) ([Approved version](https://git.hollymcfarland.com/monorail/NeopetsUserscripts/raw/commit/91e72b22d2d508240da43239d24f4ee32d3c3931/scripts/stock-highlighter.user.js) ✅)
|
||||||
|
|
||||||
When checking your stock portfolio, automatically sort all stocks by current value and highlight any that are over your sell price. The sell price defaults to 60 neopoints but can be configured on the page itself. Custom sell prices are handled in real time and are automatically saved.
|
When checking your stock portfolio, automatically sort all stocks by current value and highlight any that are over your sell price. The sell price defaults to 60 neopoints but can be configured on the page itself. When viewing lists of stocks to purchase, sort them by current value, deemphasize those that are too cheap to buy, and highlight those at exactly the buy price.
|
||||||
|
|
||||||
|
Custom sell prices and a checkbox for whether or not the stock boon is enabled are handled in real time and are automatically saved.
|
|
@ -1,10 +1,10 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name Stock Highlighter
|
// @name Stock Highlighter
|
||||||
// @namespace https://hollymcfarland.com
|
// @namespace https://hollymcfarland.com
|
||||||
// @version 1.1
|
// @version 2.0
|
||||||
// @description Sort stocks in your portfolio and highlight ones that are ready to sell
|
// @description Sort stocks in your portfolio, highlight ready to sell. Hide stocks too cheap to buy
|
||||||
// @author monorail
|
// @author monorail
|
||||||
// @match https://www.neopets.com/stockmarket.phtml?type=portfolio
|
// @match https://www.neopets.com/stockmarket.phtml*
|
||||||
// @icon https://www.google.com/s2/favicons?sz=64&domain=neopets.com
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=neopets.com
|
||||||
// @grant none
|
// @grant none
|
||||||
// ==/UserScript==
|
// ==/UserScript==
|
||||||
|
@ -13,135 +13,275 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const DEFAULT_SELL_PRICE = 60;
|
const DEFAULT_SELL_PRICE = 60;
|
||||||
const SELL_PRICE_KEY = "__monorail_userscript_sell_price"; // Unlikely to collide with anything, I think
|
const READY_COLOUR = "#77FF77";
|
||||||
const SELL_COLOUR = "#77FF77";
|
|
||||||
|
|
||||||
function getPairs(iter) {
|
const KEY_PREFIX = "__monorail_userscript_"; // Unlikely to collide with anything, I think
|
||||||
|
const SELL_PRICE_KEY = `${KEY_PREFIX}sell_price`;
|
||||||
|
const BOON_KEY = `${KEY_PREFIX}has_boon`;
|
||||||
|
|
||||||
|
function classIf(element, className, condition) {
|
||||||
/*
|
/*
|
||||||
Given an iterable (something that can be converted to an array, anyway), return
|
If `condition`, add `className` to `element`.classList
|
||||||
an array of two-length arrays containing each element once, in the same order
|
Otherwise, remove `className` from `element`.classList
|
||||||
|
Adding an element that's already there or removing one that isn't are
|
||||||
e.g. getPairs([1, 2, 3, 4, 5, 6]) => [[1, 2], [3, 4], [5, 6]]
|
noops, so there's no harm in not checking whether the class is present
|
||||||
*/
|
*/
|
||||||
|
if (condition) {
|
||||||
// The function getting mapped here behaves differently depending on the parity
|
element.classList.add(className);
|
||||||
// of the index.
|
} else {
|
||||||
// Odd index => Empty array
|
element.classList.remove(className);
|
||||||
// Even index => Array of array(!!) of the element at that index and the one after it
|
|
||||||
// Then because it's getting _flat_mapped, the whole thing is flattened by one level.
|
|
||||||
// Empty arrays disappear and the arrays of arrays become just two-length arrays
|
|
||||||
// https://stackoverflow.com/a/57851980/2114129
|
|
||||||
return [...iter].flatMap((_, i, a) => i % 2 ? [] : [a.slice(i, i + 2)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrice(stock) {
|
|
||||||
/*
|
|
||||||
Given a row from the portfolio table representing a stock, return the current value
|
|
||||||
*/
|
|
||||||
return Number(stock.children[3].innerText);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stockCompare(a, b) {
|
|
||||||
/*
|
|
||||||
Sort function for comparing stocks
|
|
||||||
*/
|
|
||||||
return getPrice(b) - getPrice(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the stocks from the DOM
|
|
||||||
const portfolio = document.querySelector("#postForm > table > tbody");
|
|
||||||
const stocks = getPairs(portfolio.children);
|
|
||||||
|
|
||||||
const footer = stocks.pop()[0]; // Store footer elsewhere
|
|
||||||
|
|
||||||
stocks.shift(); // Remove first element of the array, the two header rows
|
|
||||||
stocks.sort((a, b) => (stockCompare(a[0], b[0])));
|
|
||||||
|
|
||||||
// Append all stocks to the table they're already in, moving them to the bottom
|
|
||||||
// in sorted order
|
|
||||||
for (let [stock, sellStock] of stocks) {
|
|
||||||
portfolio.appendChild(stock);
|
|
||||||
portfolio.appendChild(sellStock);
|
|
||||||
|
|
||||||
// While we're iterating over all the stocks anyway, remove the bgcolor
|
|
||||||
// attribute on them. The stocks have alternating colours and moving them
|
|
||||||
// around breaks that, so we're going to remove it entirely and reimplement
|
|
||||||
// that in a more modern way.
|
|
||||||
stock.removeAttribute("bgcolor");
|
|
||||||
}
|
|
||||||
|
|
||||||
// That last step had the side effect of moving the footer to the top
|
|
||||||
// Move that to the bottom as well
|
|
||||||
portfolio.append(footer);
|
|
||||||
|
|
||||||
// Now we add a custom stylesheet. This will do two things:
|
|
||||||
// 1. Reintroduce the alternating colour pattern that we removed earlier
|
|
||||||
// 2. Add a class to highlight stocks that are above the sell price
|
|
||||||
|
|
||||||
const stylesheet = new CSSStyleSheet();
|
|
||||||
// Every fourth row, starting with the third, is given the #EEEEFF background
|
|
||||||
// colour unless:
|
|
||||||
// - There's a "bgcolor" attribute already set.
|
|
||||||
// We removed that attribute from each stock, so this avoids recolouring the footer.
|
|
||||||
// - It has the .readyToSell class
|
|
||||||
// That's going to be set on any row that's ready to sell, where we'll need to override
|
|
||||||
// the background colour. This rule is really specific, so we can't override it easily
|
|
||||||
// with specificity, we'll just have to turn it off.
|
|
||||||
//
|
|
||||||
// It's every fourth row, not second, because the hidden rows for selling stocks still
|
|
||||||
// count in this case.
|
|
||||||
stylesheet.insertRule("#postForm > table > tbody > tr:not([bgcolor]):not(.readyToSell):nth-child(4n+3) { background-color: #EEEEFF; }");
|
|
||||||
|
|
||||||
// Any stocks with a value higher than the sell price are highlighted
|
|
||||||
stylesheet.insertRule(`.readyToSell { background-color: ${SELL_COLOUR}; }`);
|
|
||||||
|
|
||||||
document.adoptedStyleSheets = [stylesheet];
|
|
||||||
|
|
||||||
// Now that we're all set up, we can start working on highlighting stocks
|
|
||||||
function highlightStocks(sellPrice) {
|
|
||||||
for (let [stock, _] of stocks) {
|
|
||||||
// There's no reason not to add a class that already exists, or not to
|
|
||||||
// remove one that doesn't exist, that's all handled for us. So we can
|
|
||||||
// simply add or remove the class based on the price and it all works out
|
|
||||||
if (getPrice(stock) >= sellPrice) {
|
|
||||||
stock.classList.add("readyToSell");
|
|
||||||
} else {
|
|
||||||
stock.classList.remove("readyToSell");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We define the function and then call it right away, rather than just doing the
|
|
||||||
// work inline, so that we can update the classes dynamically.
|
|
||||||
// We also use the customized sell price if it exists, and 60 otherwise.
|
|
||||||
highlightStocks(Number(localStorage.getItem(SELL_PRICE_KEY) ?? DEFAULT_SELL_PRICE));
|
|
||||||
|
|
||||||
// These next few blocks are all just for creating and lightly styling the input for
|
const currentPage = new URL(window.location.href).searchParams.get("type");
|
||||||
// setting a custom sell price.
|
if (currentPage === "portfolio") {
|
||||||
const configContainer = document.createElement("div");
|
/*
|
||||||
configContainer.style.margin = "auto";
|
Handle everything on the portfolio page
|
||||||
configContainer.style.paddingTop = "1rem";
|
- Sort stocks by price, descending
|
||||||
configContainer.style.width = "fit-content";
|
- Highlight stocks that are equal or greater to the defined sell price
|
||||||
|
- Handle sell price config
|
||||||
|
*/
|
||||||
|
|
||||||
const sellPriceLabel = document.createElement("label");
|
function getPairs(iter) {
|
||||||
sellPriceLabel.setAttribute("for", "sellprice");
|
/*
|
||||||
sellPriceLabel.innerText = "Sell price: ";
|
Given an iterable (something that can be converted to an array, anyway), return
|
||||||
sellPriceLabel.style.fontWeight = "bold";
|
an array of two-length arrays containing each element once, in the same order
|
||||||
configContainer.appendChild(sellPriceLabel);
|
|
||||||
|
|
||||||
const sellPriceInput = document.createElement("input");
|
e.g. getPairs([1, 2, 3, 4, 5, 6]) => [[1, 2], [3, 4], [5, 6]]
|
||||||
sellPriceInput.setAttribute("type", "number");
|
*/
|
||||||
sellPriceInput.setAttribute("name", "sellprice");
|
|
||||||
sellPriceInput.setAttribute("min", "0");
|
|
||||||
sellPriceInput.setAttribute("max", "9999");
|
|
||||||
sellPriceInput.value = localStorage.getItem(SELL_PRICE_KEY) ?? DEFAULT_SELL_PRICE;
|
|
||||||
configContainer.appendChild(sellPriceInput);
|
|
||||||
|
|
||||||
document.getElementById("postForm").after(configContainer);
|
// The function getting mapped here behaves differently depending on the parity
|
||||||
|
// of the index.
|
||||||
|
// Odd index => Empty array
|
||||||
|
// Even index => Array of array(!!) of the element at that index and the one after it
|
||||||
|
// Then because it's getting _flat_mapped, the whole thing is flattened by one level.
|
||||||
|
// Empty arrays disappear and the arrays of arrays become just two-length arrays
|
||||||
|
// https://stackoverflow.com/a/57851980/2114129
|
||||||
|
return [...iter].flatMap((_, i, a) => i % 2 ? [] : [a.slice(i, i + 2)]);
|
||||||
|
}
|
||||||
|
|
||||||
// Now that the config input exists, we simply wire it up. Changing the value should
|
function getPrice(stock) {
|
||||||
// update the saved value, as well as rechecking the stocks for any to highlight.
|
/*
|
||||||
sellPriceInput.addEventListener("input", function() {
|
Given a row from the portfolio table representing a stock, return the current value
|
||||||
localStorage.setItem(SELL_PRICE_KEY, this.value);
|
*/
|
||||||
highlightStocks(Number(this.value));
|
return Number(stock.children[3].innerText);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
function stockCompare(a, b) {
|
||||||
|
/*
|
||||||
|
Sort function for comparing stocks
|
||||||
|
*/
|
||||||
|
return getPrice(b) - getPrice(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the stocks from the DOM
|
||||||
|
const portfolio = document.querySelector(".content tbody");
|
||||||
|
const stocks = getPairs(portfolio.children);
|
||||||
|
|
||||||
|
const footer = stocks.pop()[0]; // Store footer elsewhere
|
||||||
|
|
||||||
|
stocks.shift(); // Remove first element of the array, the two header rows
|
||||||
|
stocks.sort((a, b) => (stockCompare(a[0], b[0])));
|
||||||
|
|
||||||
|
// Append all stocks to the table they're already in, moving them to the bottom
|
||||||
|
// in sorted order
|
||||||
|
for (let [stock, sellStock] of stocks) {
|
||||||
|
portfolio.appendChild(stock);
|
||||||
|
portfolio.appendChild(sellStock);
|
||||||
|
|
||||||
|
// While we're iterating over all the stocks anyway, remove the bgcolor
|
||||||
|
// attribute on them. The stocks have alternating colours and moving them
|
||||||
|
// around breaks that, so we're going to remove it entirely and reimplement
|
||||||
|
// that in a more modern way.
|
||||||
|
stock.removeAttribute("bgcolor");
|
||||||
|
}
|
||||||
|
|
||||||
|
// That last step had the side effect of moving the footer to the top
|
||||||
|
// Move that to the bottom as well
|
||||||
|
portfolio.append(footer);
|
||||||
|
|
||||||
|
// Now we add a custom stylesheet. This will do two things:
|
||||||
|
// 1. Reintroduce the alternating colour pattern that we removed earlier
|
||||||
|
// 2. Add a class to highlight stocks that are above the sell price
|
||||||
|
|
||||||
|
const stylesheet = new CSSStyleSheet();
|
||||||
|
// Every fourth row, starting with the third, is given the #EEEEFF background
|
||||||
|
// colour unless:
|
||||||
|
// - There's a "bgcolor" attribute already set.
|
||||||
|
// We removed that attribute from each stock, so this avoids recolouring the footer.
|
||||||
|
// - It has the .readyToSell class
|
||||||
|
// That's going to be set on any row that's ready to sell, where we'll need to override
|
||||||
|
// the background colour. This rule is really specific, so we can't override it easily
|
||||||
|
// with specificity, we'll just have to turn it off.
|
||||||
|
//
|
||||||
|
// It's every fourth row, not second, because the hidden rows for selling stocks still
|
||||||
|
// count in this case.
|
||||||
|
stylesheet.insertRule("#postForm > table > tbody > tr:not([bgcolor]):not(.readyToSell):nth-child(4n+3) { background-color: #EEEEFF; }");
|
||||||
|
|
||||||
|
// Any stocks with a value higher than the sell price are highlighted
|
||||||
|
stylesheet.insertRule(`.readyToSell { background-color: ${READY_COLOUR}; }`);
|
||||||
|
|
||||||
|
document.adoptedStyleSheets = [stylesheet];
|
||||||
|
|
||||||
|
// Now that we're all set up, we can start working on highlighting stocks
|
||||||
|
function highlightStocks(sellPrice) {
|
||||||
|
for (let [stock, _] of stocks) {
|
||||||
|
classIf(stock, "readyToSell", getPrice(stock) >= sellPrice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We define the function and then call it right away, rather than just doing the
|
||||||
|
// work inline, so that we can update the classes dynamically.
|
||||||
|
// We also use the customized sell price if it exists, and 60 otherwise.
|
||||||
|
highlightStocks(Number(localStorage.getItem(SELL_PRICE_KEY) ?? DEFAULT_SELL_PRICE));
|
||||||
|
|
||||||
|
// These next few blocks are all just for creating and lightly styling the input for
|
||||||
|
// setting a custom sell price.
|
||||||
|
const configContainer = document.createElement("div");
|
||||||
|
configContainer.style.margin = "auto";
|
||||||
|
configContainer.style.paddingTop = "1rem";
|
||||||
|
configContainer.style.width = "fit-content";
|
||||||
|
|
||||||
|
const sellPriceLabel = document.createElement("label");
|
||||||
|
sellPriceLabel.setAttribute("for", "sellprice");
|
||||||
|
sellPriceLabel.innerText = "Sell price: ";
|
||||||
|
sellPriceLabel.style.fontWeight = "bold";
|
||||||
|
configContainer.appendChild(sellPriceLabel);
|
||||||
|
|
||||||
|
const sellPriceInput = document.createElement("input");
|
||||||
|
sellPriceInput.setAttribute("type", "number");
|
||||||
|
sellPriceInput.setAttribute("name", "sellprice");
|
||||||
|
sellPriceInput.setAttribute("min", "0");
|
||||||
|
sellPriceInput.setAttribute("max", "9999");
|
||||||
|
sellPriceInput.value = localStorage.getItem(SELL_PRICE_KEY) ?? DEFAULT_SELL_PRICE;
|
||||||
|
configContainer.appendChild(sellPriceInput);
|
||||||
|
|
||||||
|
document.getElementById("postForm").after(configContainer);
|
||||||
|
|
||||||
|
// Now that the config input exists, we simply wire it up. Changing the value should
|
||||||
|
// update the saved value, as well as rechecking the stocks for any to highlight.
|
||||||
|
sellPriceInput.addEventListener("input", function() {
|
||||||
|
localStorage.setItem(SELL_PRICE_KEY, this.value);
|
||||||
|
highlightStocks(Number(this.value));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
Handle buying stocks
|
||||||
|
- Sort stocks by price, ascending
|
||||||
|
- Hide stocks lower than 15 (or 10 with stock boon)
|
||||||
|
- Highlight stocks exactly at 15 (or 10 with stock boon)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getPrice(stock) {
|
||||||
|
/* Given a row representing a stock, return its price */
|
||||||
|
return Number(stock.children[5].innerText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stockCompare(a, b, floor, ignoreBy) {
|
||||||
|
/*
|
||||||
|
Sort function for comparing stocks, with extra functionality
|
||||||
|
|
||||||
|
Any stocks priced below `floor` have `ignoreBy` added to their
|
||||||
|
effective price before sorting. By setting `floor` to 15 (or 10
|
||||||
|
with the stock boon) and ignoreBy to the maximum stock price,
|
||||||
|
any stocks that are too cheap to buy can be moved to the end
|
||||||
|
while staying in sorted order
|
||||||
|
*/
|
||||||
|
return (
|
||||||
|
(getPrice(a) + (getPrice(a) < floor ? ignoreBy : 0))
|
||||||
|
- (getPrice(b) + (getPrice(b) < floor ? ignoreBy : 0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStocks(stocksList, hasBoon) {
|
||||||
|
/*
|
||||||
|
Sort stocks by price, ignoring those too cheap to buy,
|
||||||
|
and highlight those at exactly the buy price
|
||||||
|
*/
|
||||||
|
const buyPrice = hasBoon ? 10 : 15;
|
||||||
|
|
||||||
|
const stocks = [...stocksList.childNodes];
|
||||||
|
stocks.shift(); // Remove first element, i.e. header
|
||||||
|
|
||||||
|
// Maximum price of any stock
|
||||||
|
const max = Math.max(...stocks.map((stock) => (getPrice(stock))));
|
||||||
|
|
||||||
|
stocks.sort((a, b) => (stockCompare(a, b, buyPrice, max)));
|
||||||
|
for (const stock of stocks) {
|
||||||
|
// Append all stocks to the object they're already a child of in
|
||||||
|
// sorted order and apply classes for styling
|
||||||
|
stocksList.appendChild(stock);
|
||||||
|
|
||||||
|
// Apply relevant classes for styling
|
||||||
|
classIf(stock, "atBuyPrice", getPrice(stock) === buyPrice);
|
||||||
|
classIf(stock, "tooCheap", getPrice(stock) < buyPrice);
|
||||||
|
|
||||||
|
// Clean up some bad-practice HTML stuff that's going to get in our way
|
||||||
|
for (const e of stock.childNodes) {
|
||||||
|
e.removeAttribute("bgcolor");
|
||||||
|
if (!e.innerHTML.includes("<img>") && !e.innerHTML.includes("<p>")) {
|
||||||
|
e.innerHTML = `<p>${e.innerHTML}</p>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no way to tell from the URL if there's a table of stocks to buy
|
||||||
|
// (If you search for stocks with a given name, there's no GET params at all)
|
||||||
|
// So we'll just have to look for it
|
||||||
|
|
||||||
|
const stocksList = document.querySelector(".content tbody");
|
||||||
|
// Make sure there's a table and that it has "Logo" in its first row
|
||||||
|
if (!stocksList) return;
|
||||||
|
if (!stocksList.childNodes[0].innerText.includes("Logo")) return;
|
||||||
|
|
||||||
|
// Now we know that we're on a page with a list of stocks
|
||||||
|
|
||||||
|
// Add a custom stylesheet for highlighting stocks at the buy price and hiding
|
||||||
|
// stocks that are too cheap to buy
|
||||||
|
const stylesheet = new CSSStyleSheet();
|
||||||
|
|
||||||
|
// Default background colour, because the bgcolor attribute is removed to make
|
||||||
|
// the styles work
|
||||||
|
stylesheet.insertRule(".content table { background-color: #EEEEFF; border: 1px ssolid black; }")
|
||||||
|
|
||||||
|
// Highlight stocks at the buy price
|
||||||
|
stylesheet.insertRule(`.atBuyPrice { background-color: ${READY_COLOUR}; }`);
|
||||||
|
|
||||||
|
// Hide stocks that are too cheap to buy
|
||||||
|
stylesheet.insertRule(".tooCheap td img, .tooCheap td p { opacity: 0.4; }");
|
||||||
|
|
||||||
|
document.adoptedStyleSheets = [stylesheet];
|
||||||
|
|
||||||
|
// Check saved value for whether the stock boon is enabled
|
||||||
|
const hasBoon = localStorage.getItem(BOON_KEY) === "true";
|
||||||
|
|
||||||
|
// Do the initial sort and styling
|
||||||
|
handleStocks(stocksList, hasBoon);
|
||||||
|
|
||||||
|
// Create and lightly styling the input for setting whether or not you have the boon
|
||||||
|
const configContainer = document.createElement("div");
|
||||||
|
configContainer.style.margin = "auto";
|
||||||
|
configContainer.style.paddingTop = "1rem";
|
||||||
|
configContainer.style.width = "fit-content";
|
||||||
|
|
||||||
|
const boonCheckLabel = document.createElement("label");
|
||||||
|
boonCheckLabel.setAttribute("for", "hasboon");
|
||||||
|
boonCheckLabel.innerText = "Stock boon enabled: ";
|
||||||
|
boonCheckLabel.style.fontWeight = "bold";
|
||||||
|
configContainer.appendChild(boonCheckLabel);
|
||||||
|
|
||||||
|
const boonCheckInput = document.createElement("input");
|
||||||
|
boonCheckInput.setAttribute("type", "checkbox");
|
||||||
|
boonCheckInput.setAttribute("name", "hasboon");
|
||||||
|
boonCheckInput.checked = hasBoon;
|
||||||
|
configContainer.appendChild(boonCheckInput);
|
||||||
|
|
||||||
|
document.querySelector(".content table").after(configContainer);
|
||||||
|
|
||||||
|
// Wire up the input to update the saved boon value and re-sort
|
||||||
|
boonCheckInput.addEventListener("change", function() {
|
||||||
|
localStorage.setItem(BOON_KEY, this.checked);
|
||||||
|
handleStocks(stocksList, this.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
Loading…
Reference in New Issue