main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. window.onload = function() {
  2. const graphTypeSelector = document.getElementById("graphType");
  3. const selectorSubtypes = document.getElementById("selectorSubtypes");
  4. graphTypeSelector.addEventListener("change", function() {
  5. updateSelectors(graphTypeSelector, selectorSubtypes);
  6. switchPlot(graphTypeSelector);
  7. });
  8. updateSelectors(graphTypeSelector, selectorSubtypes);
  9. switchPlot(graphTypeSelector);
  10. };
  11. // Update selectors based on graph type
  12. function updateSelectors(graphTypeSelector, selectorSubtypes)
  13. {
  14. clearChildren(selectorSubtypes);
  15. if(graphTypeSelector.value == "topProduction" || graphTypeSelector.value == "topCompanies")
  16. {
  17. selectorSubtypes.appendChild(addInput(graphTypeSelector, 'select', 'metric', 'Metric: ', [['Volume', 'Profit'], ['volume', 'profit']]));
  18. selectorSubtypes.appendChild(addInput(graphTypeSelector, 'select', 'month', 'Month: ', [["March 3025", "April 3025", "May 3025"], ["mar25", "apr25", "may25"]]));
  19. }
  20. else if(graphTypeSelector.value == "matHistory")
  21. {
  22. selectorSubtypes.appendChild(addInput(graphTypeSelector, 'select', 'metric', 'Metric: ', [['Volume', 'Profit', 'Amount', 'Price'], ['volume', 'profit', 'amount', 'price']]));
  23. selectorSubtypes.appendChild(addInput(graphTypeSelector, 'input', 'mat', 'Ticker: '));
  24. }
  25. }
  26. function switchPlot(typeElem)
  27. {
  28. var monthElem;
  29. var metricElem;
  30. var matElem;
  31. const oldGraph = document.getElementById("mainPlot");
  32. oldGraph.remove();
  33. const newGraph = document.createElement("div");
  34. newGraph.id = "mainPlot";
  35. const graphContainer = document.getElementById("mainPlotContainer");
  36. graphContainer.appendChild(newGraph);
  37. switch(typeElem.value)
  38. {
  39. case "topProduction":
  40. metricElem = document.getElementById("metric");
  41. monthElem = document.getElementById("month");
  42. promiseGenerateTopProdGraph("mainPlot", monthElem.value, metricElem.value);
  43. break;
  44. case "topCompanies":
  45. metricElem = document.getElementById("metric");
  46. monthElem = document.getElementById("month");
  47. promiseGenerateTopCompanyGraph("mainPlot", monthElem.value, metricElem.value);
  48. break;
  49. case "matHistory":
  50. metricElem = document.getElementById("metric");
  51. matElem = document.getElementById("mat");
  52. promiseGenerateMatGraph("mainPlot", matElem.value, metricElem.value);
  53. break;
  54. }
  55. }
  56. function promiseGenerateTopCompanyGraph(container, month, metric) // Metric is either 'profit' or 'volume'
  57. {
  58. fetch('data/company-data-' + month + '.json?cb=' + Date.now())
  59. .then(response => response.json()) // Parse JSON data
  60. .then(data => {
  61. fetch('data/knownCompanies2.json?cb=' + Date.now())
  62. .then(response => response.json())
  63. .then(knownCompanies => {
  64. generateTopCompanyGraph(container, data, knownCompanies, month, metric); // Use the JSON data
  65. });
  66. });
  67. }
  68. function promiseGenerateTopProdGraph(container, month, metric) // Metric is either 'profit' or 'volume'
  69. {
  70. fetch('data/prod-data-' + month + '.json?cb=' + Date.now())
  71. .then(response => response.json()) // Parse JSON data
  72. .then(data => {
  73. generateTopProdGraph(container, data, month, metric); // Use the JSON data
  74. });
  75. }
  76. function promiseGenerateMatGraph(container, ticker, metric) // Metric is either 'profit', 'volume', or 'amount'
  77. {
  78. // Validation/sanitizing
  79. if(!ticker){return;}
  80. ticker = ticker.toUpperCase();
  81. // Get data
  82. const months = ['mar25', 'apr25', 'may25']
  83. const fetches = months.map(month =>
  84. fetch('data/prod-data-' + month + '.json?cb=' + Date.now()).then(res => res.json())
  85. );
  86. Promise.all(fetches).then(rawData => {
  87. const data = []
  88. var hasData = false;
  89. rawData.forEach(monthData => {
  90. const dataPoint = monthData[ticker]
  91. if(dataPoint)
  92. {
  93. if(metric == 'price')
  94. {
  95. data.push(dataPoint['amount'] == 0 ? 0 : dataPoint['volume'] / dataPoint['amount']);
  96. }
  97. else
  98. {
  99. data.push(dataPoint[metric])
  100. }
  101. hasData = true;
  102. }
  103. });
  104. if(hasData)
  105. {
  106. generateMatGraph(container, months.map(month => prettyMonthName(month)), data, ticker, metric)
  107. }
  108. });
  109. }
  110. function generateTopProdGraph(container, prodData, month, metric)
  111. {
  112. // Convert the data object into an array of [ticker, volume] pairs
  113. const volumeArray = Object.entries(prodData).map(([ticker, info]) => ({
  114. ticker,
  115. volume: info[metric]
  116. }));
  117. // Sort the array by volume in descending order
  118. volumeArray.sort((a, b) => b.volume - a.volume);
  119. // Extract tickers and volumes into separate arrays
  120. const tickers = volumeArray.map(item => item.ticker);
  121. const volumes = volumeArray.map(item => item.volume);
  122. Plotly.newPlot(container, {
  123. data: [{ x: tickers, y: volumes, type: 'bar' , marker: {color: 'rgb(247, 166, 0)'}, hovertemplate: '%{x}: %{y:,.3~s}<extra></extra>'}],
  124. layout: { width: 800, height: 400,
  125. title: {text: 'Top ' + (metric == 'profit' ? 'Profit Materials - ' : 'Production Volumes - ') + prettyMonthName(month),
  126. font: {color: '#eee', family: '"Droid Sans", sans-serif'},
  127. },
  128. xaxis: {
  129. title: {
  130. text: 'Ticker',
  131. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  132. },
  133. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  134. range: [-0.5, 29.5],
  135. tickangle: -45
  136. },
  137. yaxis: {
  138. title: {
  139. text: prettyModeNames[metric] + ' [$/day]',
  140. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  141. },
  142. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  143. gridcolor: '#323232'
  144. },
  145. plot_bgcolor: '#252525',
  146. paper_bgcolor: '#252525',
  147. dragmode: 'pan'
  148. },
  149. config: {
  150. displayModeBar: true,
  151. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  152. displaylogo: false,
  153. scrollZoom: true
  154. }
  155. });
  156. }
  157. function generateTopCompanyGraph(container, companyData, knownCompanies, month, metric)
  158. {
  159. // Convert the data object into an array of [companyID, volume] pairs
  160. const volumeArray = Object.entries(companyData.totals).map(([companyID, info]) => ({
  161. companyID,
  162. volume: info[metric]
  163. }));
  164. // Sort the array by volume in descending order
  165. volumeArray.sort((a, b) => b.volume - a.volume);
  166. // Extract tickers and volumes into separate arrays
  167. const companyIDs = volumeArray.map(item => item.companyID);
  168. const volumes = volumeArray.map(item => item.volume);
  169. const companyNames = [];
  170. // Print unknown top 40 companies
  171. companyIDs.slice(0,40).forEach(id => {
  172. if(!knownCompanies[id])
  173. {
  174. console.log(id)
  175. }
  176. });
  177. companyIDs.forEach(id => {
  178. companyNames.push(knownCompanies[id] || (id.slice(0, 5) + "..."));
  179. });
  180. Plotly.newPlot(container, {
  181. data: [{ x: companyNames, y: volumes, type: 'bar' , marker: {color: 'rgb(247, 166, 0)'}, hovertemplate: '%{x}: %{y:,.3~s}<extra></extra>'}],
  182. layout: { width: 800, height: 400,
  183. title: {text: 'Top Companies (' + prettyModeNames[metric] + ') - ' + prettyMonthName(month),
  184. font: {color: '#eee', family: '"Droid Sans", sans-serif'},
  185. },
  186. xaxis: {
  187. title: {
  188. text: 'Player',
  189. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  190. },
  191. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  192. range: [-0.5, 29.5],
  193. tickangle: -45
  194. },
  195. yaxis: {
  196. title: {
  197. text: prettyModeNames[metric] + ' [$/day]',
  198. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  199. },
  200. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  201. gridcolor: '#323232'
  202. },
  203. plot_bgcolor: '#252525',
  204. paper_bgcolor: '#252525',
  205. dragmode: 'pan',
  206. margin: {b: 120}
  207. },
  208. config: {
  209. displayModeBar: true,
  210. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  211. displaylogo: false,
  212. scrollZoom: true
  213. }
  214. });
  215. }
  216. function generateMatGraph(container, months, data, ticker, metric)
  217. {
  218. const titles = {
  219. 'profit': 'Production Profit History of ',
  220. 'volume': 'Production Volume History of ',
  221. 'amount': 'Production Amount History of ',
  222. 'price': 'Price History of '
  223. }
  224. const yAxis = {
  225. 'profit': 'Daily Profit [$/day]',
  226. 'volume': 'Daily Volume [$/day]',
  227. 'amount': 'Daily Production [per day]',
  228. 'price': 'Price [$]'
  229. }
  230. Plotly.newPlot(container, {
  231. data: [{ x: months, y: data, type: 'bar' , marker: {color: 'rgb(247, 166, 0)'}, hovertemplate: '%{x}: %{y:,.3~s}<extra></extra>'}],
  232. layout: { width: 800, height: 400,
  233. title: {text: titles[metric] + ticker,
  234. font: {color: '#eee', family: '"Droid Sans", sans-serif'},
  235. },
  236. xaxis: {
  237. title: {
  238. text: 'Month',
  239. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  240. },
  241. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  242. tickangle: -45
  243. },
  244. yaxis: {
  245. title: {
  246. text: yAxis[metric],
  247. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  248. },
  249. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  250. gridcolor: '#323232'
  251. },
  252. plot_bgcolor: '#252525',
  253. paper_bgcolor: '#252525',
  254. dragmode: 'pan'
  255. },
  256. config: {
  257. displayModeBar: true,
  258. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  259. displaylogo: false,
  260. scrollZoom: true
  261. }
  262. });
  263. }
  264. // Util functions
  265. function prettyMonthName(monthStr)
  266. {
  267. const monthAbv = monthStr.substring(0,3);
  268. const monthNum = monthStr.substring(3);
  269. return fullMonthNames[monthAbv] + " 30" + monthNum;
  270. }
  271. // Remove all the children of a given element
  272. function clearChildren(elem)
  273. {
  274. elem.textContent = "";
  275. while(elem.children[0])
  276. {
  277. elem.removeChild(elem.children[0]);
  278. }
  279. return;
  280. }
  281. // Add options to a selector
  282. function addOptions(selector, options, values)
  283. {
  284. for(var i = 0; i < options.length; i++)
  285. {
  286. const optionElem = document.createElement("option");
  287. optionElem.textContent = options[i];
  288. optionElem.value = values ? values[i] : options[i];
  289. selector.appendChild(optionElem);
  290. }
  291. }
  292. function wrapInDiv(elem) // Wrap selector element in a div to center it and give it margin
  293. {
  294. const div = document.createElement('div');
  295. div.appendChild(elem);
  296. return div;
  297. }
  298. function addInput(graphTypeSelector, inputType, id, label, values)
  299. {
  300. const labelElem = document.createElement('label');
  301. labelElem.textContent = label;
  302. const inputElem = document.createElement(inputType);
  303. inputElem.id = id;
  304. inputElem.classList.add("plotSelector");
  305. if(inputType == 'select')
  306. {
  307. addOptions(inputElem, values[0], values[1]);
  308. }
  309. inputElem.addEventListener("change", function() {
  310. switchPlot(graphTypeSelector);
  311. });
  312. labelElem.appendChild(inputElem);
  313. return wrapInDiv(labelElem);
  314. }
  315. const fullMonthNames = {
  316. "jan": "January",
  317. "feb": "February",
  318. "mar": "March",
  319. "apr": "April",
  320. "may": "May",
  321. "jun": "June",
  322. "jul": "July",
  323. "aug": "August",
  324. "sep": "September",
  325. "oct": "October",
  326. "nov": "November",
  327. "dec": "December"
  328. }
  329. const prettyModeNames = {
  330. "amount": "Amount",
  331. "profit": "Profit",
  332. "volume": "Volume",
  333. "price": "Price"
  334. }