- // ==UserScript==
- // @name IcePetsPlus
- // @namespace
- // @version 1.0
- // @description Some minor QOL improvements for icepets
- // @author Cullen
- // @match https://www.icepets.com/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=icepets.com
- // @grant GM_addStyle
- // @namespace https://greasyfork.org/users/428778
- // ==/UserScript==
- (function() {
- 'use strict';
-
- const globals = {
- sortAsc: false
- }
-
- // RUN THIS THROUGH THIS SOMETIMES
- // https://beautifier.io/
-
- GM_addStyle(`
- input:not([type='text']), button {
- padding: 5px 10px;
- margin: 1px;
- cursor: pointer;
- }
-
- .copyTooltip {
- position: fixed;
- z-index: 1000;
- padding: 5px 10px;
- border: 1px solid #111;
- background: #f2f2f5;
- font-size: 12px;
- font-family: verdana;
- }
-
- .tableheader td {
- cursor:pointer;
- }
- `);
-
- const routes = () => {
- // Globals
- setupToolstips()
-
- // Routed
- const path = window.location.pathname;
- const search = window.location.search;
- switch (path) {
- case '/halipar-jungle/companion-reserve':
- case '/snowslide-mountains/snow-jar-igloo':
- case '/glacia/page-turners':
- case '/glacia/frozen-collectives-emporium':
- case '/glacia/post-office':
- case '/glacia/plushie-palace':
- case '/glacia/toy-trunk':
- case '/misty-isle/grooming-parlour':
- case '/glacia/glacial-grocer':
- case '/misty-isle/battle-shop':
- case '/snowslide-mountains/sugar-rush':
- case '/snowslide-mountains/affogato':
- case '/misty-isle/golden-touch':
- setupShopPage();
- break;
- case '/halipar-jungle/collectors-quest':
- case '/quests/collect.php':
- setupCollectorPage();
- break;
- case '/usershops.php':
- search == '?stock' ? setupShopStockPage() : null;
- break;
- case '/arcade/gs.php':
- setupSlotsPage();
- break;
- default:
- break;
- }
- }
-
- // Notes/weirdthings in comments below
-
- /*
- const apiURL = "https://new.icepets.com/api/v1/search/items?item=&category=Companion&perPage=9999";
-
- fetch(apiURL)
- .then(response => response.json())
- .then(data => {
- const filteredObjects = data.data.map(obj => !obj.slug.includes("-") ? obj.name : null).filter(x=>x!=null);
- console.log(filteredObjects);
- })
- .catch(error => {
- console.error("Error:", error);
- });
-
- */
-
- // https://www.icepets.com/snowslide-mountains/found-snow-jar
-
- const setupCollectorPage = () => {
- console.log('setupCollectorPage')
- document.querySelectorAll('table table tbody td img').forEach(questItem => {
- const substrStart = questItem.src.indexOf('/items/') + 7;
- const substrEnd = questItem.src.length - 4
-
- const itemId = questItem.src.substring(substrStart, substrEnd);
-
- const button = document.createElement('button')
- button.style.transition = "background-color 0.2s ease-out";
- button.style.width = '90px';
- button.style.margin = '1px'
- button.innerHTML = 'Buy';
- button.addEventListener('click', function() {
- button.innerHTML = 'Checking...';
- _buyScrapItem(itemId, button);
- });
-
- questItem.parentElement.append(button)
- })
- }
-
- const setupShopPage = () => {
- console.log('setupShopPage')
- const timerCountdown = () => {
- const timer = document.querySelector('b')
- const time = timer.innerText.split(' seconds')[0]
- let timeInSeconds = time.indexOf(':') != -1 ? Number(time.split(':')[0] * 60) + Number(time.split(':')[1]) : time;
- timeInSeconds--;
- const minutes = Math.floor(timeInSeconds / 60);
- const seconds = (timeInSeconds % 60).toLocaleString('en-US', {
- minimumIntegerDigits: 2,
- useGrouping: false
- })
- if (timeInSeconds == -1) return
- timer.innerHTML = minutes == 0 ? `${seconds} seconds` : `${minutes}:${seconds} seconds`
- // Wtf workers make timeouts work even in inactive tabs
- // https://stackoverflow.com/questions/5927284/how-can-i-make-setinterval-also-work-when-a-tab-is-inactive-in-chrome/5927432#12522580
- const blob = new Blob(["setTimeout(function(){postMessage('')}, 1000)"])
- const worker = new Worker(window.URL.createObjectURL(blob))
- worker.onmessage = timerCountdown;
- }
-
- timerCountdown()
- }
-
- const setupShopStockPage = () => {
- console.log('setupShopStockPage')
- // Copy "Update Stock" button to the top
-
- const updateButtonCopy = document.querySelector('.submitbutton').cloneNode(true);
- document.querySelector('.submitbutton').parentElement.prepend(updateButtonCopy)
- updateButtonCopy.after(document.createElement('br'))
- updateButtonCopy.after(document.createElement('br'))
-
- // Add sorting to the stock table
- // https://stackoverflow.com/questions/14267781/sorting-html-table-with-javascript/49041392#49041392
- const headers = document.querySelectorAll('tr.tableheader td');
- headers.forEach(th => th.addEventListener('click', (() => _sortTableByHeader(th, headers))));
-
- // Sort manually the first time to sort price low to high
- const th = document.querySelectorAll('tr.tableheader td')[2];
- _sortTableByHeader(th, headers);
- }
-
- const setupSlotsPage = () => {
- const verify = document.querySelector('input[name="gs_verify"]')
-
- if (verify) verify.focus();
- document.body.addEventListener('keyup', (e) => {
- if (e.key == 'Enter') document.querySelector('input[name="submit"]').click()
- })
-
- }
-
- const setupToolstips = () => {
- if (['/news.php', '/newboards/viewposts.php'] // Skip these
- .indexOf(window.location.pathname) != -1) return
-
- console.log('setupToolstips')
- GM_addStyle(`
- img[src*="images/items"] {
- cursor: pointer;
- }
- `);
- Array.from(document.querySelectorAll(':not(a) img[src*="images/items"]'))
- .filter(item => item.parentElement.tagName != 'A')
- .forEach(shopItem => {
- const itemName = _getItemNameFromNode(shopItem);
- shopItem.addEventListener('click', e => {
- navigator.clipboard.writeText(itemName);
- _addTooltip(shopItem, 'Copied item name!', e)
- });
- })
- }
-
- const _addTooltip = (element, text, e, speed) => {
- // Create a tooltip element.
- var tooltip = document.createElement('div');
- tooltip.textContent = text;
- tooltip.className = 'copyTooltip'
- tooltip.style.top = (e.clientY + 20) + "px";
- tooltip.style.left = (e.clientX + 20) + "px"
-
- // Add the tooltip to the DOM.
- element.parentNode.prepend(tooltip);
- setTimeout(() => _removeFadeOut(tooltip, speed ?? 500), 1000);
- }
-
- const _buyScrapItem = (itemId, button, price) => {
- const XHR = new XMLHttpRequest();
- const FD = new FormData();
- const dataFormat = {
- offer_price: price ?? '1618',
- action_buy: 'Buy Another'
- }
-
- for (const [name, value] of Object.entries(dataFormat)) {
- FD.append(name, value);
- }
-
- XHR.addEventListener("load", (event) => {
- //console.log("Yeah! Data sent and response loaded.", event);
- });
-
- XHR.addEventListener("error", (event) => {
- _setButtonSuccess(false, 'Oops! Error', button)
- });
-
- XHR.onreadystatechange = () => {
- if (XHR.readyState == 4) {
- if (XHR.status == 200) {
- // The request was successful
- if (XHR.responseText.indexOf('The item you were purchasing is no longer available')) {
- _setButtonSuccess(true, 'Bought! :)', button)
- } else {
- _setButtonSuccess(false, 'Nope :(', button)
- }
- } else {
- _setButtonSuccess(false, 'Oops! Error', button)
- }
- return false
- }
- };
-
- XHR.open("POST", `https://www.icepets.com/scrapshop/index.php?act=buyitem&item=${itemId}`);
-
- XHR.send(FD);
- }
-
- const _checkAllWordsStartWithCapitalLetter = (str) => {
- const words = str?.split(' ');
- if (!words) return false
- for (const word of words) {
- if (word[0]?.toUpperCase() !== word[0]) {
- return false;
- }
- }
- return true;
- }
-
- const _checkTokenDabuRewards = (shopItem) => {
- if (!shopItem?.parentElement?.childNodes) return undefined
- return Array.from(shopItem?.parentElement?.childNodes)
- .filter(node => node?.tagName == "STRONG" &&
- _checkAllWordsStartWithCapitalLetter(node?.textContent) &&
- node?.children?.length == 0 &&
- node?.textContent?.indexOf(':') == -1
- )[0]?.textContent
- }
-
- const _checkNovitariaQuest = (shopItem) => {
- if (!shopItem?.parentElement?.childNodes) return undefined
- return Array.from(shopItem?.parentElement?.childNodes)
- .filter(node => node?.wholeText && node?.textContent.length > 2 && node?.textContent?.length < 100)[0]
- .textContent
- }
-
- const _compareSortingValues = (v1, v2) => {
- const validVersions = v1 !== '' && v2 !== '' && !isNaN(v1) && !isNaN(v2)
- return validVersions ? v1 - v2 : v1.toString().localeCompare(v2)
- }
-
- const _getCellValue = (tr, idx) => {
- return tr.children[idx].children[0]?.value || // input fields
- tr.children[idx]?.innerText || // text from first node
- tr.children[idx]?.textContent; // text from all child nodes
- }
-
- const _getItemNameFromNode = (shopItem) => {
- return shopItem?.parentElement?.querySelector('b')?.innerHTML ?? // NPC Shops
- _checkTokenDabuRewards(shopItem) ?? // Token Dabu reward
- shopItem?.parentElement?.querySelectorAll('strong')?.[2]?.innerHTML ?? // Beauty King
- shopItem?.parentElement?.querySelector('strong')?.innerHTML ?? // Collector Quest, Shop Purchase Confirmation, Cube Grab Main Page, Storage
- (shopItem?.alt.length > 0 ? shopItem?.alt : null) ?? // Solitary Sprite
- _checkNovitariaQuest(shopItem) ?? // Novitaria Quest
- shopItem?.parentElement?.lastChild?.textContent ?? // Scratchcard prizes, Shop Stock
- ''; // Uhhhhh
- }
-
- const _removeFadeOut = (el, speed) => {
- var seconds = speed / 1000;
- el.style.transition = "opacity " + seconds + "s ease";
-
- el.style.opacity = 0;
- setTimeout(function() {
- el.parentNode.removeChild(el);
- }, speed);
- }
-
- const _setButtonSuccess = (success, message, button) => {
- button.innerHTML = message;
- button.style.backgroundColor = success ? '#8fdf96' : '#df8f8f';
-
- setTimeout(() => {
- button.style.backgroundColor = '#d6e7ff';
- button.innerHTML = 'Buy';
- }, 1250)
- }
-
- const _sortTableByHeader = (th, headers) => {
- const table = th.closest('table');
- const tableRows = Array.from(th.parentNode.children);
- const thIndex = tableRows.indexOf(th)
-
- headers.forEach(th2 => {
- th2.innerHTML = th2.innerHTML.replace(' ↓', '').replace(' ↑', '')
- })
- th.innerHTML += globals.sortAsc ? ' ↑' : ' ↓';
-
- Array.from(table.querySelectorAll('tr:nth-child(n+2)'))
- .sort(_sortTableSortHandler(thIndex))
- .forEach(tr => table.appendChild(tr));
- }
-
- const _sortTableSortHandler = (idx) => {
- globals.sortAsc = !globals.sortAsc;
- // .sort((a, b) => yourFunctionBlock)
- return (a, b) => {
- return _compareSortingValues(_getCellValue(globals.sortAsc ? a : b, idx), _getCellValue(globals.sortAsc ? b : a, idx));
- }
- }
-
- routes()
- })();