init
This commit is contained in:
35
chrome-extension/background.js
Normal file
35
chrome-extension/background.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// CORS
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === 'fetchPrice') {
|
||||
fetch(`https://www.ely.gg/chart/${request.itemId}/prices`)
|
||||
.then(response => response.json())
|
||||
.then(data => sendResponse({ success: true, data }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
} else if (request.action === 'fetchData') {
|
||||
fetch(request.url)
|
||||
.then(response => response.json())
|
||||
.then(data => sendResponse({ success: true, data }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
//Firefox compatibility
|
||||
if (typeof browser !== 'undefined') {
|
||||
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.action === 'fetchPrice') {
|
||||
fetch(`https://www.ely.gg/chart/${request.itemId}/prices`)
|
||||
.then(response => response.json())
|
||||
.then(data => sendResponse({ success: true, data }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
} else if (request.action === 'fetchData') {
|
||||
fetch(request.url)
|
||||
.then(response => response.json())
|
||||
.then(data => sendResponse({ success: true, data }))
|
||||
.catch(error => sendResponse({ success: false, error: error.message }));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
257
chrome-extension/content.js
Normal file
257
chrome-extension/content.js
Normal file
@@ -0,0 +1,257 @@
|
||||
(async function() {
|
||||
const browserAPI = typeof browser !== 'undefined' ? browser : chrome;
|
||||
try {
|
||||
const getStorage = (key) => {
|
||||
return new Promise((resolve) => {
|
||||
const api = (typeof chrome !== 'undefined' && chrome.storage) ? chrome : browserAPI;
|
||||
if (api.storage && api.storage.sync) {
|
||||
api.storage.sync.get([key], (result) => {
|
||||
resolve(result ? result[key] : null);
|
||||
});
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const sendMessage = (message) => {
|
||||
return new Promise((resolve) => {
|
||||
const api = (typeof chrome !== 'undefined' && chrome.runtime) ? chrome : browserAPI;
|
||||
try {
|
||||
api.runtime.sendMessage(message, (response) => {
|
||||
// Chrome callback
|
||||
if (api.runtime.lastError) {
|
||||
resolve(null);
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
});
|
||||
|
||||
} catch(e) {
|
||||
if (browserAPI && browserAPI.runtime && browserAPI.runtime.sendMessage) {
|
||||
browserAPI.runtime.sendMessage(message).then(resolve).catch(() => resolve(null));
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const customUrl = await getStorage('customDataUrl');
|
||||
let itemData;
|
||||
|
||||
if (customUrl) {
|
||||
const response = await sendMessage({
|
||||
action: 'fetchData',
|
||||
url: customUrl
|
||||
});
|
||||
if (response && response.success) {
|
||||
itemData = response.data;
|
||||
} else {
|
||||
console.error('Failed to fetch custom data, falling back to local');
|
||||
const localResponse = await fetch(browserAPI.runtime.getURL('new_data.json'));
|
||||
itemData = await localResponse.json();
|
||||
}
|
||||
} else {
|
||||
const response = await fetch(browserAPI.runtime.getURL('new_data.json'));
|
||||
itemData = await response.json();
|
||||
}
|
||||
|
||||
const pageTitleElement = document.querySelector('.mw-page-title-main');
|
||||
if (!pageTitleElement) return;
|
||||
|
||||
const originalCursor = pageTitleElement.style.cursor;
|
||||
|
||||
const pageTitle = pageTitleElement.textContent.trim();
|
||||
|
||||
let itemId = null;
|
||||
|
||||
if (itemData[pageTitle]) {
|
||||
itemId = itemData[pageTitle];
|
||||
} else {
|
||||
const urlPath = window.location.pathname;
|
||||
const urlTitle = urlPath.replace('/w/', '').replace(/_/g, ' ');
|
||||
|
||||
if (itemData[urlTitle]) {
|
||||
itemId = itemData[urlTitle];
|
||||
} else {
|
||||
for (const [itemName, id] of Object.entries(itemData)) {
|
||||
const itemNameBase = itemName.split('(')[0].trim();
|
||||
if (itemName.toLowerCase() === pageTitle.toLowerCase() ||
|
||||
itemName.toLowerCase() === urlTitle.toLowerCase() ||
|
||||
itemNameBase.toLowerCase() === pageTitle.toLowerCase() ||
|
||||
itemNameBase.toLowerCase() === urlTitle.toLowerCase()) {
|
||||
itemId = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemId) {
|
||||
try {
|
||||
const response = await browserAPI.runtime.sendMessage({
|
||||
action: 'fetchPrice',
|
||||
itemId: itemId
|
||||
});
|
||||
|
||||
if (response.success && response.data.items && response.data.items.length > 0) {
|
||||
const lastItem = response.data.items[response.data.items.length - 1];
|
||||
const price = lastItem.price.toLocaleString();
|
||||
const date = new Date(lastItem.date).toLocaleDateString();
|
||||
const saleType = lastItem.purchase;
|
||||
var saleTypeShort = saleType;
|
||||
if (saleType.toLowerCase() === "sold") {
|
||||
saleTypeShort = "inb"
|
||||
} else if (saleType.toLowerCase() === "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);
|
||||
}
|
||||
} catch (priceError) {
|
||||
console.error('Error fetching price data:', priceError);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
pageTitleElement.style.cursor = 'pointer';
|
||||
pageTitleElement.addEventListener('click', async () => {
|
||||
try {
|
||||
const response = await sendMessage({
|
||||
action: 'fetchPrice',
|
||||
itemId: itemId
|
||||
});
|
||||
|
||||
if (response.success && response.data.items && response.data.items.length > 0) {
|
||||
showSalesPopup(response.data.items, itemId);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching sales data:', error);
|
||||
window.open(`https://www.ely.gg/view_item/${itemId}`, '_blank');
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading item data:', error);
|
||||
}
|
||||
|
||||
function showSalesPopup(items, itemId) {
|
||||
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).toLocaleString()} gp`;
|
||||
price.style.fontWeight = 'bold';
|
||||
|
||||
const date = document.createElement('span');
|
||||
date.textContent = new Date(sale.date).toLocaleDateString();
|
||||
date.style.color = '#666';
|
||||
|
||||
const type = document.createElement('span');
|
||||
type.textContent = sale.purchase.toLowerCase() === 'sold' ? 'inb' : 'ins';
|
||||
type.style.color = sale.purchase === 'sold' ? '#d9534f' : '#5cb85c';
|
||||
type.style.fontWeight = 'bold';
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
buttonContainer.appendChild(elyButton);
|
||||
buttonContainer.appendChild(closeButton);
|
||||
popup.appendChild(buttonContainer);
|
||||
|
||||
document.body.appendChild(popup);
|
||||
|
||||
document.addEventListener('click', function closePopup(e) {
|
||||
if (!popup.contains(e.target) && e.target !== pageTitleElement) {
|
||||
popup.remove();
|
||||
document.removeEventListener('click', closePopup);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
BIN
chrome-extension/img/128.png
Normal file
BIN
chrome-extension/img/128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
chrome-extension/img/64.png
Normal file
BIN
chrome-extension/img/64.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
38
chrome-extension/manifest-firefox.json
Normal file
38
chrome-extension/manifest-firefox.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "RS Wiki Ely.gg Price Display",
|
||||
"version": "1.0",
|
||||
"description": "Displays item IDs from ely.gg on RuneScape Wiki pages",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"https://www.ely.gg/*"
|
||||
],
|
||||
"icons": {
|
||||
"48": "img/64.png",
|
||||
"96": "img/128.png"
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": "img/64.png",
|
||||
"default_title": "Configure Ely Extension",
|
||||
"default_popup": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
]
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://runescape.wiki/*"
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"new_data.json"
|
||||
]
|
||||
}
|
||||
51
chrome-extension/manifest-v3.json
Normal file
51
chrome-extension/manifest-v3.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "RS Wiki Ely.gg Price Display",
|
||||
"version": "1.0",
|
||||
"description": "Displays item IDs from ely.gg on RuneScape Wiki pages",
|
||||
"permissions": [
|
||||
"storage"
|
||||
],
|
||||
"host_permissions": [
|
||||
"https://www.ely.gg/*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"icons": {
|
||||
"48": "img/64.png",
|
||||
"96": "img/128.png"
|
||||
},
|
||||
"action": {
|
||||
"default_icon": "img/64.png",
|
||||
"default_title": "Configure Ely Extension",
|
||||
"default_popup": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://runescape.wiki/*"
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"new_data.json"
|
||||
],
|
||||
"matches": [
|
||||
"https://runescape.wiki/*"
|
||||
]
|
||||
}
|
||||
],
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"open_in_tab": false
|
||||
}
|
||||
}
|
||||
44
chrome-extension/manifest.json
Normal file
44
chrome-extension/manifest.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "RS Wiki Ely.gg Price Display",
|
||||
"version": "1.0",
|
||||
"description": "Displays item IDs from ely.gg on RuneScape Wiki pages",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"https://www.ely.gg/*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"icons": {
|
||||
"48": "img/64.png",
|
||||
"96": "img/128.png"
|
||||
},
|
||||
"browser_action": {
|
||||
"default_icon": "img/64.png",
|
||||
"default_title": "Configure Ely Extension",
|
||||
"default_popup": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
]
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://runescape.wiki/*"
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"new_data.json"
|
||||
],
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"open_in_tab": false
|
||||
}
|
||||
}
|
||||
1090
chrome-extension/new_data.json
Normal file
1090
chrome-extension/new_data.json
Normal file
File diff suppressed because it is too large
Load Diff
69
chrome-extension/options.html
Normal file
69
chrome-extension/options.html
Normal file
@@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Ely Wiki Extension Options</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
padding: 20px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
background-color: #5a8c5a;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #487048;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-top: 10px;
|
||||
font-size: 0.9em;
|
||||
color: green;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 0.8em;
|
||||
color: #666;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<label for="dataUrl">Custom Data JSON URL:</label>
|
||||
<input type="text" id="dataUrl" placeholder="Leave empty to use default local data">
|
||||
|
||||
<button id="save">Save</button>
|
||||
<div id="status" class="status"></div>
|
||||
|
||||
<div class="note">
|
||||
Leave empty to use the bundled extension data. <br>
|
||||
Should link to the raw json file of items.
|
||||
</div>
|
||||
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
19
chrome-extension/options.js
Normal file
19
chrome-extension/options.js
Normal file
@@ -0,0 +1,19 @@
|
||||
document.getElementById('save').addEventListener('click', () => {
|
||||
const dataUrl = document.getElementById('dataUrl').value.trim();
|
||||
|
||||
chrome.storage.sync.set({ customDataUrl: dataUrl }, () => {
|
||||
const status = document.getElementById('status');
|
||||
status.textContent = 'Options saved.';
|
||||
setTimeout(() => {
|
||||
status.textContent = '';
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
chrome.storage.sync.get(['customDataUrl'], (items) => {
|
||||
if (items.customDataUrl) {
|
||||
document.getElementById('dataUrl').value = items.customDataUrl;
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user