main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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. range: [0, null],
  144. gridcolor: '#323232'
  145. },
  146. plot_bgcolor: '#252525',
  147. paper_bgcolor: '#252525',
  148. dragmode: 'pan'
  149. },
  150. config: {
  151. displayModeBar: true,
  152. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  153. displaylogo: false,
  154. scrollZoom: true
  155. }
  156. });
  157. }
  158. function generateTopCompanyGraph(container, companyData, knownCompanies, month, metric)
  159. {
  160. // Convert the data object into an array of [companyID, volume] pairs
  161. const volumeArray = Object.entries(companyData.totals).map(([companyID, info]) => ({
  162. companyID,
  163. volume: info[metric]
  164. }));
  165. // Sort the array by volume in descending order
  166. volumeArray.sort((a, b) => b.volume - a.volume);
  167. // Extract tickers and volumes into separate arrays
  168. const companyIDs = volumeArray.map(item => item.companyID);
  169. const volumes = volumeArray.map(item => item.volume);
  170. const companyNames = [];
  171. // Print unknown top 40 companies
  172. companyIDs.slice(0,40).forEach(id => {
  173. if(!knownCompanies[id])
  174. {
  175. console.log(id)
  176. }
  177. });
  178. companyIDs.forEach(id => {
  179. companyNames.push(knownCompanies[id] || (id.slice(0, 5) + "..."));
  180. });
  181. Plotly.newPlot(container, {
  182. data: [{ x: companyNames, y: volumes, type: 'bar' , marker: {color: 'rgb(247, 166, 0)'}, hovertemplate: '%{x}: %{y:,.3~s}<extra></extra>'}],
  183. layout: { width: 800, height: 400,
  184. title: {text: 'Top Companies (' + prettyModeNames[metric] + ') - ' + prettyMonthName(month),
  185. font: {color: '#eee', family: '"Droid Sans", sans-serif'},
  186. },
  187. xaxis: {
  188. title: {
  189. text: 'Player',
  190. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  191. },
  192. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  193. range: [-0.5, 29.5],
  194. tickangle: -45
  195. },
  196. yaxis: {
  197. title: {
  198. text: prettyModeNames[metric] + ' [$/day]',
  199. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  200. },
  201. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  202. range: [0, null],
  203. gridcolor: '#323232'
  204. },
  205. plot_bgcolor: '#252525',
  206. paper_bgcolor: '#252525',
  207. dragmode: 'pan',
  208. margin: {b: 120}
  209. },
  210. config: {
  211. displayModeBar: true,
  212. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  213. displaylogo: false,
  214. scrollZoom: true
  215. }
  216. });
  217. }
  218. function generateMatGraph(container, months, data, ticker, metric)
  219. {
  220. const titles = {
  221. 'profit': 'Production Profit History of ',
  222. 'volume': 'Production Volume History of ',
  223. 'amount': 'Production Amount History of ',
  224. 'price': 'Price History of '
  225. }
  226. const yAxis = {
  227. 'profit': 'Daily Profit [$/day]',
  228. 'volume': 'Daily Volume [$/day]',
  229. 'amount': 'Daily Production [per day]',
  230. 'price': 'Price [$]'
  231. }
  232. Plotly.newPlot(container, {
  233. data: [{ x: months, y: data, type: 'bar' , marker: {color: 'rgb(247, 166, 0)'}, hovertemplate: '%{x}: %{y:,.3~s}<extra></extra>'}],
  234. layout: { width: 800, height: 400,
  235. title: {text: titles[metric] + ticker,
  236. font: {color: '#eee', family: '"Droid Sans", sans-serif'},
  237. },
  238. xaxis: {
  239. title: {
  240. text: 'Month',
  241. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  242. },
  243. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  244. tickangle: -45
  245. },
  246. yaxis: {
  247. title: {
  248. text: yAxis[metric],
  249. font: {color: '#bbb', family: '"Droid Sans", sans-serif'}
  250. },
  251. tickfont: {color: '#666', family: '"Droid Sans", sans-serif'},
  252. gridcolor: '#323232'
  253. },
  254. plot_bgcolor: '#252525',
  255. paper_bgcolor: '#252525',
  256. dragmode: 'pan'
  257. },
  258. config: {
  259. displayModeBar: true,
  260. modeBarButtonsToRemove: ['lasso2d'], // Remove unwanted buttons
  261. displaylogo: false,
  262. scrollZoom: true
  263. }
  264. });
  265. }
  266. // Util functions
  267. function prettyMonthName(monthStr)
  268. {
  269. const monthAbv = monthStr.substring(0,3);
  270. const monthNum = monthStr.substring(3);
  271. return fullMonthNames[monthAbv] + " 30" + monthNum;
  272. }
  273. // Remove all the children of a given element
  274. function clearChildren(elem)
  275. {
  276. elem.textContent = "";
  277. while(elem.children[0])
  278. {
  279. elem.removeChild(elem.children[0]);
  280. }
  281. return;
  282. }
  283. // Add options to a selector
  284. function addOptions(selector, options, values)
  285. {
  286. for(var i = 0; i < options.length; i++)
  287. {
  288. const optionElem = document.createElement("option");
  289. optionElem.textContent = options[i];
  290. optionElem.value = values ? values[i] : options[i];
  291. selector.appendChild(optionElem);
  292. }
  293. }
  294. function wrapInDiv(elem) // Wrap selector element in a div to center it and give it margin
  295. {
  296. const div = document.createElement('div');
  297. div.appendChild(elem);
  298. return div;
  299. }
  300. function addInput(graphTypeSelector, inputType, id, label, values)
  301. {
  302. const labelElem = document.createElement('label');
  303. labelElem.textContent = label;
  304. const inputElem = document.createElement(inputType);
  305. inputElem.id = id;
  306. inputElem.classList.add("plotSelector");
  307. if(inputType == 'select')
  308. {
  309. addOptions(inputElem, values[0], values[1]);
  310. }
  311. inputElem.addEventListener("change", function() {
  312. switchPlot(graphTypeSelector);
  313. });
  314. labelElem.appendChild(inputElem);
  315. return wrapInDiv(labelElem);
  316. }
  317. const fullMonthNames = {
  318. "jan": "January",
  319. "feb": "February",
  320. "mar": "March",
  321. "apr": "April",
  322. "may": "May",
  323. "jun": "June",
  324. "jul": "July",
  325. "aug": "August",
  326. "sep": "September",
  327. "oct": "October",
  328. "nov": "November",
  329. "dec": "December"
  330. }
  331. const prettyModeNames = {
  332. "amount": "Amount",
  333. "profit": "Profit",
  334. "volume": "Volume",
  335. "price": "Price"
  336. }