MediaWiki:Gadget-livePricesMMG-core.js: Difference between revisions

no edit summary
(Created page with "// // TODO: Do I just do something kind of dumb like use an object here for constant lookup? EXEMPT_FROM_TAX = [ 'Old school bond', 'Chisel', 'Gardening trowel', 'Glassblowing pipe', 'Hammer', 'Needle', 'Pestle and mortar', 'Rake', 'Saw', 'Secateurs', 'Seed dibber', 'Shears', 'Spade', 'Watering can (0)' ]; // Tax is never higher than 5m per item MAX_TAX_AMOUNT = 5000000; MMG_SMW_DATA_ENDPOINT = "https://oldschool.runescape.wiki/api.php?acti...")
 
No edit summary
 
Line 1:
"use strict";
 
//
 
// TODO: Do I just do something kind of dumb like use an object here for constant lookup?
EXEMPT_FROM_TAX = ['Old school bond', 'Chisel', 'Gardening trowel', 'Glassblowing pipe', 'Hammer', 'Needle', 'Pestle and mortar', 'Rake', 'Saw', 'Secateurs', 'Seed dibber', 'Shears', 'Spade', 'Watering can (0)'];
EXEMPT_FROM_TAX = [
'Old school bond',
'Chisel',
'Gardening trowel',
'Glassblowing pipe',
'Hammer',
'Needle',
'Pestle and mortar',
'Rake',
'Saw',
'Secateurs',
'Seed dibber',
'Shears',
'Spade',
'Watering can (0)'
];
 
// Tax is never higher than 5m per item
MAX_TAX_AMOUNT = 5000000;
MMG_SMW_DATA_ENDPOINT = "https://oldschool.runescape.wiki/api.php?action=ask&query=[[MMG%20JSON::%2B]]%7C%3FMMG%20JSON%7Climit=10000&format=json";
 
MMG_SMW_DATA_ENDPOINT = "https://oldschool.runescape.wiki/api.php?action=ask&query=[[MMG%20JSON::%2B]]%7C%3FMMG%20JSON%7Climit=10000&format=json"
MAPPING_ENDPOINT = 'https://prices.runescape.wiki/api/v1/osrs/mapping';
ALLOWED_GRANULARITIES = ['latest', '5m', '1h', '6h', '24h'];
 
SHOULD_TAX_ON_BUY = false;
SHOULD_TAX_ON_SELL = true;
 
mmgJs = {
init: function init() {
mmgJs.livePrices = {};
mmgJs.officialPrices = {};
Line 44 ⟶ 28:
mmgJs.loadMMGData();
},
initButtons: function initButtons() {
 
initButtons: function () {
// Set up the ButtonSelectWidget that toggles between live and official prices
var buttonDiv = document.querySelector('.mmg-list-table-buttons');
 
var officialPricesButton = new OO.ui.ButtonOptionWidget({
title: 'Official Prices',
label: 'Official Prices',
data: mmgJs.updateTableOfficialPrices
});
var livePricesButton = new OO.ui.ButtonOptionWidget({
title: 'Live Prices',
label: 'Live Prices',
data: function data() { mmgJs.initUpdateTableLivePrices('24h') }
mmgJs.initUpdateTableLivePrices('24h');
})
}
mmgJs.buttonSelect = new OO.ui.ButtonSelectWidget({ items: [officialPricesButton, livePricesButton] })
});
mmgJs.buttonSelect.on('select', function(item) { item.data() })
$(buttonDiv).append(mmgJs.buttonSelect = new OO.$element)ui.ButtonSelectWidget({
items: [officialPricesButton, livePricesButton]
});
mmgJs.buttonSelect.on('select', function (item) {
item.data();
});
$(buttonDiv).append(mmgJs.buttonSelect.$element);
},
 
// Build a map of MMG name -> tr for access later
indexTableRows: function indexTableRows() {
var table = document.querySelector('.mmg-list-table');
var trs = table.getElementsByClassName('mmg-list-table-row');
mmgJs.trsIndexedByName = {};
for (var i = 0; i < trs.length; i++) {
var tr = trs[i];
Line 74 ⟶ 61:
}
},
getTrMmgName: function getTrMmgName(thisTr) {
 
getTrMmgName: function (thisTr) {
return thisTr.querySelector('a').title;
},
loadMapping: function loadMapping(callback) {
 
loadMapping: function (callback) {
// Get the live prices mapping data since we will need it to match names and ids (is there a better place to look?)
if (mmgJs.mapping) {
callback();
} else {
else {
$.ajax({
type: "GET",
url: MAPPING_ENDPOINT,
dataType: "json",
success: function success(msg) {
mmgJs.mapping = {};
for (var index in msg) {
Line 95 ⟶ 79:
}
},
error: function error(req) {
console.log('ERROR: Mapping endpoint failed');
}
}).done(callback);
}
},
loadLivePrices: function loadLivePrices(granularity) {
 
loadLivePrices: function (granularity) {
// Check for mapping
var endpoint = "https://prices.runescape.wiki/api/v1/osrs/" + granularity;
// Get the live prices data
$.ajax({
Line 110 ⟶ 93:
url: endpoint,
dataType: "json",
indexValue: { granularity: granularity },
granularity: granularity
success: function (msg) {
},
success: function success(msg) {
mmgJs.livePrices[this.indexValue.granularity] = {};
for (var key in msg['data']) {
Line 118 ⟶ 103:
continue;
}
var itemName = mmgJs.mapping[key]['name'];
mmgJs.livePrices[this.indexValue.granularity][itemName] = msg['data'][key];
}
mmgJs.updateTableLivePrices(granularity);
},
error: function error(req) {
onFailure();
}
});
},
loadMMGData: function loadMMGData() {
 
loadMMGData: function () {
// Gets the MMG data via SMW. This should be called once per run.
$.ajax({
Line 135 ⟶ 119:
url: MMG_SMW_DATA_ENDPOINT,
dataType: "json",
success: function success(msg) {
mmgJs.mmgData = msg;
mmgJs.results = msg['query']['results'];
Line 144 ⟶ 128:
mmgJs.buttonSelect.selectItemByLabel('Live Prices');
},
error: function error(req) {
console.log('ERROR: MMG SMW call failed...Aborting');
}
});
},
updateTableOfficialPrices: function updateTableOfficialPrices() {
 
updateTableOfficialPrices: function () {
// We only get one price with the official prices so we have to assume buying and selling at that price
mmgJs.buyingPriceMap = mmgJs.officialPrices;
Line 156 ⟶ 139:
mmgJs.createList();
},
storeOfficialPrices: function storeOfficialPrices(mmgData) {
 
storeOfficialPrices: function (mmgData) {
// TODO: There's probably a smarter way to get these values, but this is likely not our bottleneck anyway
 
mmgJs.officialPrices = {};
for (var mmg in mmgData['query']['results']) {
var d = mmgData['query']['results'][mmg]['printouts']['MMG JSON'][0];
Line 166 ⟶ 148:
var inputs = parsedData['inputs'];
var outputs = parsedData['outputs'];
 
for (var index in inputs) {
// TODO: Is there something idiomatic in old js? I think this is for-of now
var item = inputs[index];
if (item['pricetype'] == 'gemw') {
mmgJs.officialPrices[item['name']] = item['value'];
}
}
Line 178 ⟶ 159:
var item = outputs[index];
if (item['pricetype'] == 'gemw') {
mmgJs.officialPrices[item['name']] = item['value'];
}
}
 
}
},
storeMMGIO: function storeMMGIO() {
 
storeMMGIO: function () {
mmgJs.parsedResults = {};
for (var mmg in mmgJs.results) {
Line 192 ⟶ 171:
}
},
initUpdateTableLivePrices: function initUpdateTableLivePrices(granularity) {
 
initUpdateTableLivePrices: function (granularity) {
// Build a price list based on what we asked for
if (!ALLOWED_GRANULARITIES.includes(granularity)) {
Line 200 ⟶ 178:
// We will call updateTableLivePrices via a callback in loadLivePrices if we don't already have the prices
if (mmgJs.livePrices[granularity] === undefined) {
mmgJs.loadMapping(function () { mmgJs.loadLivePrices(granularity) });
mmgJs.loadLivePrices(granularity);
}
else { });
} else {
mmgJs.updateTableLivePrices(granularity);
}
 
},
updateTableLivePrices: function updateTableLivePrices(granularity) {
 
updateTableLivePrices: function (granularity) {
var granularityMap = mmgJs.livePrices[granularity];
mmgJs.buyingPriceMap = {};
Line 216 ⟶ 193:
mmgJs.buyingPriceMap[itemName] = granularityMap[itemName]['high'];
mmgJs.sellingPriceMap[itemName] = granularityMap[itemName]['low'];
} else {
else {
mmgJs.buyingPriceMap[itemName] = granularityMap[itemName]['avgHighPrice'];
mmgJs.sellingPriceMap[itemName] = granularityMap[itemName]['avgLowPrice'];
Line 224 ⟶ 200:
mmgJs.createList();
},
createList: function createList() {
 
createList: function () {
 
for (var mmg in mmgJs.parsedResults) {
var mmgName = mmgJs.results[mmg]['fulltext'];
 
var mmgName = mmgJs.results[mmg]['fulltext']
 
// Identify what row this is
Line 237 ⟶ 210:
continue;
}
var numActions = mmgJs.getKphLocalStorage(mmgName) || mmgJs.parsedResults[mmg]['prices']['default_kph'];
if (thisTr.querySelector('.mmg-kph-selector') === null) {
mmgJs.addCellToggle(thisTr, numActions);
Line 246 ⟶ 219:
}
},
updateTableRow: function updateTableRow(thisTr, numActions) {
 
updateTableRow: function (thisTr, numActions) {
//console.log('Updating row with numActions = ' + numActions);
 
Line 258 ⟶ 230:
// Add the correct class to the table cell
if (profit > 0) {
profitCell.classList.remove('coins-neg');
profitCell.classList.add('coins-pos');
} else if (profit < 0) {
profitCell.classList.remove('coins-pos');
profitCell.classList.add('coins-neg');
} else {
profitCell.classList.remove('coins-pos');
profitCell.classList.remove('coins-neg');
}
else if (profit < 0) {
profitCell.classList.remove('coins-pos')
profitCell.classList.add('coins-neg')
}
else {
profitCell.classList.remove('coins-pos')
profitCell.classList.remove('coins-neg')
}
 
profitCell.innerHTML = profit.toLocaleString();
},
addCellToggle: function addCellToggle(thisTr, valueToUse) {
 
addCellToggle: function (thisTr, valueToUse) {
var mmgName = mmgJs.getTrMmgName(thisTr);
 
var kphCell = thisTr.querySelector('.mmg-list-table-kph-cell');
var kphField = new OO.ui.NumberInputWidget({
min: 0,
input: { value: valueToUse },
value: valueToUse
},
classes: ['mmg-kph-selector'],
showButtons: false,
title: mmgJs.parsedResults[mmgName]['prices']['kph_text'] || 'Kills per hour'
});
 
function updateThisRow() {
mmgJs.setKphLocalStorage(mmgName, kphField.getNumericValue());
return mmgJs.updateTableRow(thisTr, kphField.getNumericValue());
}
 
kphField.on('change', updateThisRow);
 
 
var resetButton = new OO.ui.ButtonWidget({
icon: 'reload',
Line 303 ⟶ 268:
kphField.setValue(mmgJs.parsedResults[mmgName]['prices']['default_kph']);
mmgJs.resetKphLocalStorage(mmgName);
});
 
var layout = new OO.ui.ActionFieldLayout(kphField, resetButton, {
classes: ['mmg-kph-selector-field']
});
 
kphCell.innerHTML = '';
$(kphCell).append(layout.$element);
},
calculateProfit: function calculateProfit(mmg, buyingPriceMap, sellingPriceMap, numActions) {
 
calculateProfit: function (mmg, buyingPriceMap, sellingPriceMap, numActions) {
var inputMap = mmgJs.parsedResults[mmg]['inputs'];
var outputMap = mmgJs.parsedResults[mmg]['outputs'];
 
var inputAmount = mmgJs.calculateValue(inputMap, buyingPriceMap, numActions, SHOULD_TAX_ON_BUY);
var outputAmount = mmgJs.calculateValue(outputMap, sellingPriceMap, numActions, SHOULD_TAX_ON_SELL);
return Math.floor(outputAmount - inputAmount);
},
getItemPrice: function getItemPrice(item, givenPrice) {
 
getItemPrice: function (item, givenPrice) {
// If the item does not use GE prices, always return the price as is
if (item['pricetype'] != 'gemw') return item['value'];
if (givenPrice === undefined || givenPrice === null) {
console.log('WARNING: This item has no price in the price map you gave me! ' + item['name']);
return item['value'];
if (givenPrice === undefined || givenPrice === null) {
console.log('WARNING: This item has no price in the price map you gave me! ' + item['name'])
return item['value']
}
return givenPrice;
},
calculateValue: function calculateValue(itemAmountMap, priceMap, numActions, useTax) {
 
calculateValue: function (itemAmountMap, priceMap, numActions, useTax) {
var sum = 0;
for (var index in itemAmountMap) {
Line 340 ⟶ 298:
// For each item, determine the value
// console.log("Item: " + JSON.stringify(item));
 
 
// Stub this out to get the correct price
Line 352 ⟶ 309:
if (item['isph']) {
numberUsed = quantity;
} else {
else {
numberUsed = quantity * numActions;
}
 
if (useTax) {
// Subtract tax since it is per item and not per txn
sum += numberUsed * mmgJs.applyTax(item['name'], value);
} else {
else {
sum += numberUsed * value;
}
Line 367 ⟶ 321:
return sum;
},
applyTax: function applyTax(itemName, price) {
 
if (EXEMPT_FROM_TAX.includes(itemName)) return price;
applyTax: function (itemName, price) {
if (EXEMPT_FROM_TAX.includes(itemName))
return price;
return price - Math.min(Math.floor(price / 100), MAX_TAX_AMOUNT);
},
 
/**
* LocalStorage helper methods to retrieve and set values
*/
 
getLSKeyNameForMmg: function getLSKeyNameForMmg(mmgName) {
// mmgName should always have a "Money making guide/" prefix
// This will work for anything that is a subpage and we could have some default for a top level page, but I don't want to pollute LS
var mmg = mmgName.split('/')[1];
if (mmg === undefined) return undefined;
return undefined;
return mmg + '-mmg-kph';
},
getKphLocalStorage: function getKphLocalStorage(mmgName) {
 
getKphLocalStorage: function (mmgName) {
var lsKey = mmgJs.getLSKeyNameForMmg(mmgName);
if (lsKey !== undefined) return localStorage.getItem(lsKey);
return localStorage.getItem(lsKey);
},
setKphLocalStorage: function setKphLocalStorage(mmgName, valueToUse) {
 
setKphLocalStorage: function (mmgName, valueToUse) {
var lsKey = mmgJs.getLSKeyNameForMmg(mmgName);
if (lsKey !== undefined) localStorage.setItem(lsKey, valueToUse);
localStorage.setItem(lsKey, valueToUse);
},
resetKphLocalStorage: function resetKphLocalStorage(mmgName) {
 
resetKphLocalStorage: function (mmgName) {
var lsKey = mmgJs.getLSKeyNameForMmg(mmgName);
if (lsKey !== undefined) localStorage.removeItem(lsKey);
localStorage.removeItem(lsKey);
}
};
 
}
 
$(mmgJs.init);