// ==UserScript== // @name RuneScape Wiki Ely Prices // @namespace https://ely.gg/ // @version 1.0.0 // @description Show Ely prices on RuneScape Wiki pages // @match https://runescape.wiki/w/* // @grant GM_xmlhttpRequest // @connect www.ely.gg // @connect gist.yorgei.dev // ==/UserScript== (async function () { const dataUrl = 'https://git.yorgei.dev/yorgei/rs-wiki-ely/raw/branch/main/new_data.json'; function gmFetchJson(url) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url, onload: res => { try { const json = JSON.parse(res.responseText); resolve(json); } catch (e) { reject(e); } }, onerror: err => reject(err) }); }); } async function fetchPriceData(itemId) { const url = `https://www.ely.gg/chart/${itemId}/prices`; const data = await gmFetchJson(url); return data; } try { const pageTitleElement = document.querySelector('.mw-page-title-main'); if (!pageTitleElement) return; const originalCursor = pageTitleElement.style.cursor; const dataResponse = await fetch(dataUrl); const itemData = await dataResponse.json(); const pageTitle = pageTitleElement.textContent.trim(); const urlPath = window.location.pathname; const urlTitle = urlPath.replace('/w/', '').replace(/_/g, ' '); let itemId = null; if (itemData[pageTitle]) { itemId = itemData[pageTitle]; } else if (itemData[urlTitle]) { itemId = itemData[urlTitle]; } else { for (const [itemName, id] of Object.entries(itemData)) { const itemNameBase = itemName.split('(')[0].trim(); const lowerItem = itemName.toLowerCase(); const lowerBase = itemNameBase.toLowerCase(); const lowerTitle = pageTitle.toLowerCase(); const lowerUrlTitle = urlTitle.toLowerCase(); if ( lowerItem === lowerTitle || lowerItem === lowerUrlTitle || lowerBase === lowerTitle || lowerBase === lowerUrlTitle ) { itemId = id; break; } } } if (!itemId) return; try { const priceData = await fetchPriceData(itemId); if (!priceData.items || !priceData.items.length) return; const lastItem = priceData.items[priceData.items.length - 1]; const price = Number(lastItem.price).toLocaleString(); const date = new Date(lastItem.date).toLocaleDateString(); const saleType = String(lastItem.purchase || '').toLowerCase(); let saleTypeShort = saleType; if (saleType === 'sold') { saleTypeShort = 'inb'; } else if (saleType === 'bought') { saleTypeShort = 'ins'; } const priceDisplay = document.createElement('span'); priceDisplay.textContent = ` (${price} gp - ${saleTypeShort} - ${date})`; priceDisplay.style.color = '#5a8c5a'; priceDisplay.style.fontWeight = 'bold'; priceDisplay.style.fontSize = '0.9em'; pageTitleElement.appendChild(priceDisplay); pageTitleElement.style.cursor = 'pointer'; pageTitleElement.addEventListener('click', async () => { try { const latestPriceData = await fetchPriceData(itemId); if (latestPriceData.items && latestPriceData.items.length > 0) { showSalesPopup(latestPriceData.items, itemId, pageTitleElement, originalCursor); } else { window.open(`https://www.ely.gg/view_item/${itemId}`, '_blank'); } } catch (e) { window.open(`https://www.ely.gg/view_item/${itemId}`, '_blank'); } }); } catch (e) { const idDisplay = document.createElement('span'); idDisplay.textContent = ` (ID: ${itemId}) | Failed to get price.`; idDisplay.style.color = '#5a8c5a'; idDisplay.style.fontWeight = 'bold'; idDisplay.style.fontSize = '0.9em'; pageTitleElement.appendChild(idDisplay); } } catch (e) { console.error('Error loading Ely userscript data:', e); } function showSalesPopup(items, itemId, pageTitleElement, originalCursor) { const existingPopup = document.getElementById('ely-sales-popup'); if (existingPopup) { existingPopup.remove(); } const popup = document.createElement('div'); popup.id = 'ely-sales-popup'; popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.backgroundColor = '#fff'; popup.style.border = '1px solid #ccc'; popup.style.borderRadius = '8px'; popup.style.boxShadow = '0 4px 8px rgba(0,0,0,0.1)'; popup.style.padding = '20px'; popup.style.zIndex = '10000'; popup.style.maxWidth = '500px'; popup.style.width = '90%'; popup.style.maxHeight = '70vh'; popup.style.overflowY = 'auto'; const header = document.createElement('h3'); header.textContent = 'Recent Sales'; header.style.marginTop = '0'; header.style.marginBottom = '15px'; header.style.color = '#333'; popup.appendChild(header); const salesList = document.createElement('div'); salesList.style.marginBottom = '20px'; const recentSales = items.slice(-10).reverse(); recentSales.forEach(sale => { const saleItem = document.createElement('div'); saleItem.style.display = 'flex'; saleItem.style.justifyContent = 'space-between'; saleItem.style.padding = '8px 0'; saleItem.style.borderBottom = '1px solid #eee'; const price = document.createElement('span'); price.textContent = `${parseInt(sale.price, 10).toLocaleString()} gp`; price.style.fontWeight = 'bold'; const type = document.createElement('span'); const purchaseLower = String(sale.purchase || '').toLowerCase(); type.textContent = purchaseLower === 'sold' ? 'inb' : 'ins'; type.style.color = purchaseLower === 'sold' ? '#d9534f' : '#5cb85c'; type.style.fontWeight = 'bold'; const date = document.createElement('span'); date.textContent = new Date(sale.date).toLocaleDateString(); date.style.color = '#666'; saleItem.appendChild(price); saleItem.appendChild(type); saleItem.appendChild(date); salesList.appendChild(saleItem); }); popup.appendChild(salesList); const buttonContainer = document.createElement('div'); buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'space-between'; buttonContainer.style.marginTop = '10px'; const elyButton = document.createElement('button'); elyButton.textContent = 'Go to Ely'; elyButton.style.backgroundColor = '#5a8c5a'; elyButton.style.color = 'white'; elyButton.style.border = 'none'; elyButton.style.padding = '8px 16px'; elyButton.style.borderRadius = '4px'; elyButton.style.cursor = 'pointer'; elyButton.addEventListener('click', () => { window.open(`https://www.ely.gg/view_item/${itemId}`, '_blank'); popup.remove(); pageTitleElement.style.cursor = originalCursor; }); const closeButton = document.createElement('button'); closeButton.textContent = 'Close'; closeButton.style.backgroundColor = '#6c757d'; closeButton.style.color = 'white'; closeButton.style.border = 'none'; closeButton.style.padding = '8px 16px'; closeButton.style.borderRadius = '4px'; closeButton.style.cursor = 'pointer'; closeButton.addEventListener('click', () => { popup.remove(); pageTitleElement.style.cursor = originalCursor; }); buttonContainer.appendChild(elyButton); buttonContainer.appendChild(closeButton); popup.appendChild(buttonContainer); document.body.appendChild(popup); function closePopupOnOutsideClick(e) { if (!popup.contains(e.target) && e.target !== pageTitleElement) { popup.remove(); document.removeEventListener('click', closePopupOnOutsideClick); pageTitleElement.style.cursor = originalCursor; } } document.addEventListener('click', closePopupOnOutsideClick); } })();