Guest User

Company Assistant

a guest
Jan 21st, 2017
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 26.08 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Company assistant
  3. // @namespace Company assistant
  4. // @author --
  5. // @description Various features to assist a company director
  6. // @include *.torn.com/companies.php*
  7. // @version 1.8.3
  8. // @grant none
  9. // @require http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js
  10. // @require http://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js
  11. // ==/UserScript==
  12.  
  13. this.$ = this.jQuery = jQuery.noConflict(true);
  14.  
  15. (function(XMLHttpRequest) {
  16. 'use strict';
  17.  
  18. var COMPANIES = {
  19. 'Oil Rig': {
  20. positions: {
  21. 'Driller': {'primary': {'stat': 'man', 'cap': 150000}, 'secondary': {'stat': 'int', 'cap': 75000}},
  22. 'Roughneck': {'primary': {'stat': 'man', 'cap': 75000}, 'secondary': {'stat': 'end', 'cap': 37500}},
  23. 'Derrick Hand': {'primary': {'stat': 'man', 'cap': 94000}, 'secondary': {'stat': 'end', 'cap': 47000}},
  24. 'Secretary': {'primary': {'stat': 'end', 'cap': 112500}, 'secondary': {'stat': 'int', 'cap': 56250}},
  25. 'Inspector': {'primary': {'stat': 'int', 'cap': 225000}, 'secondary': {'stat': 'end', 'cap': 112500}},
  26. 'Sales Executive': {'primary': {'stat': 'int', 'cap': 131500}, 'secondary': {'stat': 'end', 'cap': 65750}},
  27. 'Motor Hand': {'primary': {'stat': 'man', 'cap': 112500}, 'secondary': {'stat': 'int', 'cap': 56250}}
  28. }
  29. },
  30. 'Television Network': {
  31. positions: {
  32. 'Producer': {'primary': {'stat': 'int', 'cap': 99000}, 'secondary': {'stat': 'end', 'cap': 49500}},
  33. 'Programmer': {'primary': {'stat': 'int', 'cap': 66000}, 'secondary': {'stat': 'end', 'cap': 33000}},
  34. 'Camera Operator': {'primary': {'stat': 'int', 'cap': 49500}, 'secondary': {'stat': 'man', 'cap': 24750}},
  35. 'Sales Executive': {'primary': {'stat': 'end', 'cap': 49500}, 'secondary': {'stat': 'int', 'cap': 24750}},
  36. 'Cleaner': {'primary': {'stat': 'man', 'cap': 33000}, 'secondary': {'stat': 'end', 'cap': 16500}},
  37. 'Attorney': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  38. 'Secretary': {'primary': {'stat': 'end', 'cap': 99000}, 'secondary': {'stat': 'int', 'cap': 49500}},
  39. 'Marketer': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  40. 'Writer': {'primary': {'stat': 'int', 'cap': 115500}, 'secondary': {'stat': 'end', 'cap': 57570}},
  41. 'Stagehand': {'primary': {'stat': 'man', 'cap': 33000}, 'secondary': {'stat': 'end', 'cap': 16500}},
  42. 'Anchor': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  43. 'Reporter': {'primary': {'stat': 'int', 'cap': 82500}, 'secondary': {'stat': 'end', 'cap': 41250}}
  44.  
  45. }
  46. },
  47. 'Property Broker': {
  48. positions: {
  49. 'Property Broker': {'primary': {'stat': 'end', 'cap': 1500}, 'secondary': {'stat': 'int', 'cap': 750}},
  50. 'Valuation Specialist': {'primary': {'stat': 'int', 'cap': 3000}, 'secondary': {'stat': 'end', 'cap': 1500}},
  51. 'Associate Broker': {'primary': {'stat': 'end', 'cap': 500}, 'secondary': {'stat': 'int', 'cap': 250}},
  52. 'Cleaner': {'primary': {'stat': 'man', 'cap': 1000}, 'secondary': {'stat': 'end', 'cap': 500}},
  53. 'Team Manager': {'primary': {'stat': 'end', 'cap': 3000}, 'secondary': {'stat': 'int', 'cap': 1500}},
  54. 'Receptionist': {'primary': {'stat': 'end', 'cap': 2500}, 'secondary': {'stat': 'int', 'cap': 1250}},
  55. 'Graphic Designer': {'primary': {'stat': 'int', 'cap': 3000}, 'secondary': {'stat': 'end', 'cap': 1500}},
  56. 'Broker Support': {'primary': {'stat': 'int', 'cap': 4500}, 'secondary': {'stat': 'end', 'cap': 2250}}
  57. }
  58. },
  59.  
  60. 'Restaurant': {
  61. positions: {
  62. 'Waiter': {'primary': {'stat': 'end', 'cap': 2500}, 'secondary': {'stat': 'man', 'cap': 1250}},
  63. 'Sous Chef': {'primary': {'stat': 'int', 'cap': 4000}, 'secondary': {'stat': 'end', 'cap': 2000}},
  64. 'Head Chef': {'primary': {'stat': 'end', 'cap': 5000}, 'secondary': {'stat': 'int', 'cap': 2500}},
  65. 'Cleaner': {'primary': {'stat': 'man', 'cap': 1500}, 'secondary': {'stat': 'end', 'cap': 750}},
  66. 'Kitchen Assistant': {'primary': {'stat': 'end', 'cap': 3000}, 'secondary': {'stat': 'int', 'cap': 1500}},
  67. 'Head Waiter': {'primary': {'stat': 'end', 'cap': 4000}, 'secondary': {'stat': 'int', 'cap': 2000}},
  68. 'Line Cook': {'primary': {'stat': 'int', 'cap': 2500}, 'secondary': {'stat': 'man', 'cap': 1250}},
  69. 'Chef': {'primary': {'stat': 'int', 'cap': 3000}, 'secondary': {'stat': 'man', 'cap': 1500}},
  70. 'Apprentice Chef': {'primary': {'stat': 'int', 'cap': 1500}, 'secondary': {'stat': 'man', 'cap': 750}},
  71. 'Dishwasher': {'primary': {'stat': 'man', 'cap': 1500}, 'secondary': {'stat': 'end', 'cap': 750}}
  72. }
  73. },
  74. 'Sweet Shop': {
  75. positions: {
  76. 'Confectionist': {'primary': {'stat': 'int', 'cap': 2500}, 'secondary': {'stat': 'end', 'cap': 1250}},
  77. 'Packager': {'primary': {'stat': 'end', 'cap': 1500}, 'secondary': {'stat': 'man', 'cap': 750}},
  78. 'Cleaner': {'primary': {'stat': 'man', 'cap': 1000}, 'secondary': {'stat': 'end', 'cap': 500}},
  79. 'Manager': {'primary': {'stat': 'end', 'cap': 4000}, 'secondary': {'stat': 'int', 'cap': 2000}},
  80. 'Bookkeeper': {'primary': {'stat': 'end', 'cap': 3000}, 'secondary': {'stat': 'int', 'cap': 1500}},
  81. 'Marketer': {'primary': {'stat': 'int', 'cap': 4000}, 'secondary': {'stat': 'end', 'cap': 2000}},
  82. 'Clerk': {'primary': {'stat': 'end', 'cap': 2000}, 'secondary': {'stat': 'man', 'cap': 1000}}
  83. }
  84. },
  85. 'Ladies Strip Club': {
  86. positions: {
  87. 'Male Stripper': {'primary': {'stat': 'end', 'cap': 14500}, 'secondary': {'stat': 'man', 'cap': 7250}},
  88. 'Security': {'primary': {'stat': 'man', 'cap': 29000}, 'secondary': {'stat': 'end', 'cap': 14500}},
  89. 'Cleaner': {'primary': {'stat': 'man', 'cap': 8500}, 'secondary': {'stat': 'end', 'cap': 4250}},
  90. 'Manager': {'primary': {'stat': 'end', 'cap': 33000}, 'secondary': {'stat': 'int', 'cap': 16500}},
  91. 'Bookkeeper': {'primary': {'stat': 'end', 'cap': 25000}, 'secondary': {'stat': 'int', 'cap': 12500}},
  92. 'Photographer': {'primary': {'stat': 'int', 'cap': 33000}, 'secondary': {'stat': 'end', 'cap': 16500}},
  93. }
  94. },
  95. 'Television Network ': {
  96. positions: {
  97. 'Producer': {'primary': {'stat': 'int', 'cap': 99000}, 'secondary': {'stat': 'end', 'cap': 49500}},
  98. 'Programmer': {'primary': {'int': 'man', 'cap': 66000}, 'secondary': {'stat': 'end', 'cap': 33000}},
  99. 'Camera Operator': {'primary': {'stat': 'int', 'cap': 49500}, 'secondary': {'stat': 'man', 'cap': 24750}},
  100. 'Sales Executive': {'primary': {'stat': 'end', 'cap': 49500}, 'secondary': {'stat': 'int', 'cap': 24750}},
  101. 'Cleaner': {'primary': {'stat': 'man', 'cap': 33000}, 'secondary': {'stat': 'end', 'cap': 16500}},
  102. 'Attorney': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  103. 'Secretary': {'primary': {'stat': 'end', 'cap': 99000}, 'secondary': {'stat': 'int', 'cap': 49500}},
  104. 'Marketer': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  105. 'Writer': {'primary': {'stat': 'int', 'cap': 115500}, 'secondary': {'stat': 'end', 'cap': 57750}},
  106. 'Stagehand': {'primary': {'stat': 'man', 'cap': 33000}, 'secondary': {'stat': 'end', 'cap': 16500}},
  107. 'Anchor': {'primary': {'stat': 'int', 'cap': 132000}, 'secondary': {'stat': 'end', 'cap': 66000}},
  108. 'Reporter': {'primary': {'stat': 'int', 'cap': 82500}, 'secondary': {'stat': 'end', 'cap': 41250}},
  109. }
  110. },
  111. 'Game Shop': {
  112. positions: {
  113. 'Game Advisor': {'primary': {'stat': 'int', 'cap': 4500}, 'secondary': {'stat': 'end', 'cap': 2250}},
  114. 'Cleaner': {'primary': {'stat': 'man', 'cap': 1500}, 'secondary': {'stat': 'end', 'cap': 750}},
  115. 'Store Manager': {'primary': {'stat': 'end', 'cap': 6000}, 'secondary': {'stat': 'int', 'cap': 3000}},
  116. 'Accountant': {'primary': {'stat': 'end', 'cap': 4500}, 'secondary': {'stat': 'int', 'cap': 2250}},
  117. 'Marketer': {'primary': {'stat': 'int', 'cap': 4000}, 'secondary': {'stat': 'end', 'cap': 2000}},
  118. 'Clerk': {'primary': {'stat': 'end', 'cap': 3000}, 'secondary': {'stat': 'man', 'cap': 1500}}
  119. }
  120. },
  121. };
  122.  
  123.  
  124. // set value of header 'X-Requested-With' to an empty string (necessary to access some pages)
  125. $.ajaxSettings.beforeSend = function(xhr){
  126. xhr.setRequestHeader('X-Requested-With', {toString: function(){ return ''; }});
  127. };
  128.  
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  130. // Utilities
  131. ///////////////////////////////////////////////////////////////////////////////////////////////////
  132.  
  133. /**
  134. * Adds CSS to the HEAD of the document
  135. * @param {string} css
  136. */
  137. function addCss(css) {
  138. var head = document.head,
  139. style = document.createElement('style');
  140.  
  141. style.type = 'text/css';
  142. style.appendChild(document.createTextNode(css));
  143.  
  144. head.appendChild(style);
  145. }
  146.  
  147. /**
  148. * Returns information about a player:
  149. * - last action
  150. * @param {string} id
  151. * @return {object}
  152. */
  153. function getPlayerInformation(id) {
  154. return $.ajax({
  155. url: 'profiles.php?XID=' + id
  156. }).then(function(data) {
  157. data = $(data);
  158.  
  159. var info = {
  160. lastAction: {
  161. text: data.find('.basic-list > li:nth-child(12)')[0].childNodes[2].nodeValue.trim()
  162. }
  163. };
  164.  
  165. var time = parseFloat(info.lastAction.text, 10);
  166.  
  167. if (info.lastAction.text.indexOf('minute') !== -1) {
  168. info.lastAction.minutes = time;
  169. } else if (info.lastAction.text.indexOf('hour') !== -1) {
  170. info.lastAction.hours = time;
  171. } else if (info.lastAction.text.indexOf('day') !== -1) {
  172. info.lastAction.days = time;
  173. }
  174.  
  175. return info;
  176. });
  177. }
  178.  
  179. /**
  180. * Parses one working stat in the format used on the company page
  181. * @param {string} text i.e.: "Manual: 11.2k" or "Intelligence: 718"
  182. * @return {int}
  183. */
  184. function parseWorkingStat(text) {
  185. text = text.slice(text.indexOf(':') + 1).trim();
  186.  
  187. var multiplyByThousand = text.indexOf('k') !== -1;
  188.  
  189. var stat = parseFloat(text, 10);
  190.  
  191. if (multiplyByThousand) {
  192. return stat * 1000;
  193. } else {
  194. return stat;
  195. }
  196. }
  197.  
  198. var Efficiency = (function() {
  199. //0-25% red 26%-50 orange 51%-75 yellow %76%-99 green %100+ blue
  200. var colors = [
  201. [150, 'rgba(144, 216, 245, 1)'],
  202. [100, 'rgba(178, 245, 144, 1)'],
  203. [75, 'rgba(245, 237, 144, 1)'],
  204. [50, 'rgba(245, 190, 144, 1)'],
  205. [25, 'rgba(245, 144, 144, 1)']
  206. ];
  207.  
  208. function getColor(efficiency) {
  209. var color = 'transparent';
  210.  
  211. colors.forEach(function(colorLevel) {
  212. if (efficiency <= colorLevel[0]) {
  213. color = colorLevel[1];
  214. }
  215. });
  216.  
  217. return color;
  218. }
  219.  
  220. return {
  221. getColor: getColor
  222. };
  223. }());
  224.  
  225. /**
  226. * Returns the efficiency of a position drop down item
  227. * @param {String} positionText i.e.: "Driller (83%)"
  228. * @return {int}
  229. */
  230. function parsePositionEfficiency(positionText) {
  231. var match = positionText.match(/\((\d+)/);
  232.  
  233. // there can be positions without an efficiency ("Unassigned")
  234. return match ? parseInt(match[1], 10) : 0;
  235. }
  236.  
  237. /**
  238. * Returns the position name of a position drop down item
  239. * @param {String} positionText i.e.: "Driller (83%)"
  240. * @return {string}
  241. */
  242. function parsePositionName(positionText) {
  243. var posParenthesis = positionText.indexOf(' (');
  244.  
  245. if (posParenthesis === -1) {
  246. return positionText;
  247. } else {
  248. return positionText.slice(0, posParenthesis);
  249. }
  250. }
  251.  
  252. /**
  253. * Returns the information about the current company
  254. * @return {object}
  255. */
  256. function getInformationForCurrentCompany() {
  257. var companyType = $('div.details-wrap').get(0).lastChild.textContent.trim();
  258.  
  259. return COMPANIES[companyType];
  260. }
  261.  
  262. ///////////////////////////////////////////////////////////////////////////////////////////////////
  263. // MAIN
  264. ///////////////////////////////////////////////////////////////////////////////////////////////////
  265.  
  266. /**
  267. * Basic initialisation after page load
  268. */
  269. function init() {
  270. addCss(
  271. '#vinkuun-companyStatusTable { margin: 0 10px }' +
  272. '#vinkuun-companyStatusTable thead td { color: #222; font-weight: bold; vertical-align: middle }' +
  273. '#vinkuun-companyStatusTable table { width: 100% }' +
  274. '#vinkuun-companyStatusTable tbody { background-color: #f2f2f2 }' +
  275. '#vinkuun-companyStatusTable tbody td:nth-child(1) { width: 220px }' +
  276. '#vinkuun-companyStatusTable td { border: 1px solid #ccc; text-align: center; padding: 4px 6px }' +
  277. '#vinkuun-companyStatusTable .statusOk { background-color: rgb(196, 255, 185) }' +
  278. '#vinkuun-companyStatusTable .statusWarning { background-color: rgb(253, 255, 185) }' +
  279. '#vinkuun-companyStatusTable .statusCritical { background-color: rgb(236, 66, 66) }' +
  280. '#vinkuun-companyCalculatorPanel { padding: 5px 10px; margin: 5px 10px 0; border: 1px solid rgba(176, 176, 176, 1); background: linear-gradient(to bottom, #FFF 0, #E4E4E4 100%) repeat 0 0; box-shadow: 0 0 5px rgba(189, 189, 189, 1) } ' +
  281. '#vinkuun-companyCalculatorPanel input { border: 1px solid #BBB; padding: 2px 4px; width: 50px; line-height: 16px }' +
  282. '#vinkuun-companyCalculatorPanel select, #vinkuun-companyCalculatorPanel input { margin-left: 5px; }' +
  283. '#vinkuun-companyCalculatorPanel label { margin-right: 6px }' +
  284. '#vinkuun-companyCalculatorPanel h5 { margin: 5px 0px 10px }'
  285. );
  286. }
  287.  
  288. /**
  289. * Calculates the efficiency based on primary and secondary working stats
  290. * @param {int} primaryStat
  291. * @param {int} secondaryStat
  292. * @param {int} primaryStatCap
  293. * @param {int} secondaryStatCap
  294. * @return {int}
  295. */
  296. function calculateEfficiency(workingStats, positionName) {
  297. if (!COMPANY.positions[positionName]) {
  298. throw new Error('Invalid position name: ' + positionName);
  299. }
  300.  
  301. var pos = COMPANY.positions[positionName];
  302.  
  303. var primaryStat = workingStats[pos.primary.stat];
  304. var secondaryStat = workingStats[pos.secondary.stat];
  305. var primaryStatCap = pos.primary.cap;
  306. var secondaryStatCap = pos.secondary.cap;
  307.  
  308. var primaryStatOverCap = primaryStat - primaryStatCap;
  309.  
  310. if (primaryStatOverCap < 0) {
  311. primaryStatOverCap = 0;
  312. }
  313.  
  314. var secondaryStatOverCap = secondaryStat - secondaryStatCap;
  315.  
  316. if (secondaryStatOverCap < 0) {
  317. secondaryStatOverCap = 0;
  318. }
  319.  
  320. var statsOverCap = primaryStatOverCap + secondaryStatOverCap;
  321.  
  322. var efficiency = ((primaryStat + secondaryStat) - statsOverCap / 2) / (primaryStatCap + secondaryStatCap);
  323.  
  324. // efficiency is capped
  325. if (efficiency > 1.5) {
  326. efficiency = 1.5;
  327. }
  328.  
  329. // return in percent
  330. return Math.round(efficiency * 100);
  331. }
  332.  
  333. /**
  334. * Adds an efficiency calculator after the company information block
  335. */
  336. function addEfficiencyCalculator(applicationsTab) {
  337. var calculatorPanel = $('<div>', {'class': 'border-round', 'id': 'vinkuun-companyCalculatorPanel'});
  338. var defaultEfficiency = 'N/A';
  339.  
  340. var statMan = $('<input>', {type: 'text', rel: 'man'}).on('keyup', recalculateEfficiency);
  341. var statInt = $('<input>', {type: 'text', rel: 'int'}).on('keyup', recalculateEfficiency);
  342. var statEnd = $('<input>', {type: 'text', rel: 'end'}).on('keyup', recalculateEfficiency);
  343.  
  344. var currentEfficiency = $('<span>', {text: defaultEfficiency, style: 'border: 1px solid #bbb; padding: 2px 5px'});
  345.  
  346. var positionSelect = $('<select>').on('change keyup', recalculateEfficiency);
  347. // populate position drop down
  348. for (var position in COMPANY.positions) {
  349. positionSelect.append($('<option>', {text: position}));
  350. }
  351.  
  352. function recalculateEfficiency() {
  353. if (statMan.val() !== '' && statInt.val() !== '' && statEnd.val() !== '') {
  354. var workingStats = {
  355. 'man': parseInt(statMan.val(), 10),
  356. 'int': parseInt(statInt.val(), 10),
  357. 'end': parseInt(statEnd.val(), 10)
  358. };
  359.  
  360. var position = positionSelect.find('option:selected').text();
  361.  
  362. var eff = calculateEfficiency(workingStats, position);
  363.  
  364. currentEfficiency.text(eff + '%');
  365. currentEfficiency.css('background-color', Efficiency.getColor(eff));
  366. } else {
  367. currentEfficiency.text(defaultEfficiency);
  368. currentEfficiency.css('background-color', '');
  369. }
  370. }
  371.  
  372. calculatorPanel
  373. .append($('<h5>', {text: 'Efficiency calcuator'}))
  374. .append($('<label>', {text: 'MAN:'}).append(statMan))
  375. .append($('<label>', {text: 'INT:'}).append(statInt))
  376. .append($('<label>', {text: 'END:'}).append(statEnd))
  377. .append($('<label>', {text: 'Position:'}).append(positionSelect))
  378. .append($('<label>', {text: 'Efficiency = '}).append(currentEfficiency));
  379.  
  380. applicationsTab.prepend(calculatorPanel);
  381.  
  382. // close the gap between "Manage Company" and the tabs
  383. applicationsTab.css('padding-top', '5px');
  384. }
  385.  
  386. /**
  387. * Appends the efficiency to an element
  388. * @param {HTMLNode} element
  389. * @param {Object} workingStats
  390. */
  391. function appendEfficiencyToElement(element, workingStats) {
  392. var position = element.textContent.trim();
  393.  
  394. if (position !== 'Unassigned') {
  395. var eff = calculateEfficiency(workingStats, position);
  396.  
  397. element.textContent = element.textContent + ' (' + eff + '%)';
  398. }
  399. }
  400.  
  401. /**
  402. * Adds efficiencies to the position dropdown
  403. * @param {jQuery-Object} employeesTab
  404. */
  405. function addEmployeeEfficiency(employeesTab) {
  406. employeesTab.find('.ui-selectmenu-status').each(function() {
  407. var $this = $(this);
  408. var rankCell = $this.parents('.rank');
  409.  
  410. // get current employee stats
  411. var statElement = $this.parents('.acc-body').find('.stats')[0];
  412. var employeeStats = {
  413. 'man': parseWorkingStat(statElement.children[0].textContent),
  414. 'int': parseWorkingStat(statElement.children[2].textContent),
  415. 'end': parseWorkingStat(statElement.children[4].textContent)
  416. };
  417.  
  418. // add position efficiency to position name
  419. appendEfficiencyToElement(this, employeeStats);
  420.  
  421. // add color to the cell
  422. rankCell.css('background-color', Efficiency.getColor(parsePositionEfficiency(this.textContent)));
  423.  
  424. // find ui select box items and add efficiency to each option
  425. rankCell.find('.employee-rank-list li a').each(function() {
  426. appendEfficiencyToElement(this, employeeStats);
  427. });
  428.  
  429. // observe a rank change
  430. new MutationObserver(function(mutations) {
  431. mutations.forEach(function(mutation) {
  432. // update background color
  433. var positionEfficiency = parsePositionEfficiency(mutation.addedNodes[0].textContent);
  434.  
  435. rankCell.css('background-color', Efficiency.getColor(positionEfficiency));
  436.  
  437. // re-sort the table after a rank was changed
  438. sortEmployeesByRank(employeesTab);
  439. });
  440. }).observe(this, { childList: true });
  441. });
  442. }
  443.  
  444. /**
  445. * Adds a status table after the existing employee table
  446. * @param {jQuery-Object} employeesTab
  447. */
  448. function addEmployeeLastAction(employeesTab) {
  449. var blockTitle = $('<div>', {'class': 'title-black border-round', 'text': 'Employee status', 'style': 'margin: 10px 10px 0 10px; cursor: pointer', 'title': 'Show/hide status'})
  450. .on('click', function() {
  451. var $this = $(this);
  452.  
  453. var existingTable = $('#vinkuun-companyStatusTable');
  454. if (existingTable.length === 1) {
  455. $this.removeClass('top-round').addClass('border-round');
  456. existingTable.remove();
  457. } else {
  458. $this.removeClass('border-round').addClass('top-round');
  459. loadEmployeeStatus();
  460. }
  461. }).appendTo(employeesTab);
  462.  
  463. function loadEmployeeStatus() {
  464. var container = $('<div>', {'id': 'vinkuun-companyStatusTable', 'class': 'cont-gray bottom-round'}).appendTo(employeesTab);
  465. var table = $('<table>').appendTo(container);
  466.  
  467. // add header
  468. $('<thead class="white-grad"><tr><td>Company Employees</td><td>Last Action</td></tr></thead>').appendTo(table);
  469.  
  470. var body = $('<tbody>').appendTo(table);
  471.  
  472. employeesTab.find('.user.name').each(function() {
  473. var employeeId = this.href.match(/XID=(\d+)/)[1];
  474. var userbar = this.children[0].cloneNode();
  475.  
  476. var row = $('<tr>').appendTo(body);
  477.  
  478. // add name as first column
  479. $('<td>').append(userbar).appendTo(row);
  480.  
  481. var lastAction = $('<td>').appendTo(row);
  482.  
  483. getPlayerInformation(employeeId).then(function(info) {
  484. lastAction.text(info.lastAction.text);
  485.  
  486. if (info.lastAction.days) {
  487. if (info.lastAction.days < 3) {
  488. lastAction.addClass('statusWarning');
  489. } else {
  490. lastAction.addClass('statusCritical');
  491. }
  492. } else {
  493. lastAction.addClass('statusOk');
  494. }
  495. });
  496. });
  497. }
  498. }
  499.  
  500. /**
  501. * Sorts the employees ascending by their current position and their efficiency
  502. * @param {jQuery-Object} employeesTab
  503. */
  504. function sortEmployeesByRank(employeesTab) {
  505. var list = employeesTab.find('ul.employee-list');
  506. var rows = list.find('> li').get();
  507.  
  508. rows.sort(function(a,b) {
  509. var positionEffA = $(a).find('.ui-selectmenu-status').text();
  510. var positionEffB = $(b).find('.ui-selectmenu-status').text();
  511.  
  512. // extract position name
  513. var positionA = parsePositionName(positionEffA);
  514. var positionB = parsePositionName(positionEffB);
  515.  
  516. // sort by position name
  517. if (positionA < positionB) {
  518. return -1;
  519. } else if (positionA > positionB) {
  520. return 1;
  521. } else {
  522. // sort descending by efficiency
  523. var effA = parsePositionEfficiency(positionEffA);
  524. var effB = parsePositionEfficiency(positionEffB);
  525.  
  526. if (effA < effB) {
  527. return 1;
  528. } else if (effA > effB) {
  529. return -1;
  530. }
  531. }
  532.  
  533. return 0;
  534. });
  535.  
  536. rows.forEach(function(row) {
  537. list.append(row);
  538. });
  539. }
  540.  
  541. /**
  542. * Adds a block after the employee list which shows how many employees are assigned to the company's ranks
  543. * @param {jQuery-Object} employeesTab
  544. */
  545. function addWorkersPerRankInformation(employeesTab) {
  546. var panel = $('<p>').appendTo(employeesTab.find('form div').eq(0));
  547. var text = 'Your employees are currently working in the following positions: ';
  548.  
  549. for (var position in COMPANY.positions) {
  550. var count = employeesTab.find('.ui-selectmenu-status:contains("' + position + '")').length;
  551.  
  552. text += count + 'x ' + position + ', ';
  553. }
  554.  
  555. // remove last ', '
  556. text = text.slice(0, text.length - 2);
  557.  
  558. text += '.';
  559.  
  560. panel.text(text);
  561. }
  562.  
  563. /**
  564. * Adds a link after the working stats of an applicant.
  565. * Clicking that link enters the stats into the calculator.
  566. *
  567. * @param {jQuery-Object} applicationsTab
  568. */
  569. function addEnterApplicationStatsIntoCalculator(applicationsTab) {
  570. var calculator = $('#vinkuun-companyCalculatorPanel');
  571. var calcIntField = calculator.find('input[rel="int"]');
  572. var calcManField = calculator.find('input[rel="man"]');
  573. var calcEndField = calculator.find('input[rel="end"]');
  574.  
  575. applicationsTab.find('div.effectiveness').each(function() {
  576. var $this = $(this);
  577.  
  578. var stats = {
  579. 'man': parseInt($this.find('.manual')[0].lastChild.textContent.replace(',', ''), 10),
  580. 'int': parseInt($this.find('.intelligence')[0].lastChild.textContent.replace(',', ''), 10),
  581. 'end': parseInt($this.find('.endurance')[0].lastChild.textContent.replace(',', ''), 10)
  582. };
  583.  
  584. var enterLink = $('<a>', {text: 'Enter into calculator', style: 'cursor: pointer'})
  585. .on('click', function (e) {
  586. e.preventDefault();
  587.  
  588. enterStatsIntoCalculator(stats);
  589. });
  590.  
  591. $this.append(enterLink);
  592. });
  593.  
  594. function enterStatsIntoCalculator(stats) {
  595. calcIntField.val(stats['int']);
  596. calcManField.val(stats['man']);
  597. calcEndField.val(stats['end']);
  598.  
  599. calcEndField.trigger('keyup');
  600. }
  601. }
  602.  
  603. /**
  604. * Executes the provided watchers after the employee tab was loaded
  605. * @param {array} watchers
  606. */
  607. function watchEmployeesTab(watchers) {
  608. var employeesTab = $('#employees');
  609.  
  610. new MutationObserver(function(mutations) {
  611. mutations.forEach(function(mutation) {
  612. if (mutation.addedNodes.length === 3) {
  613. watchers.forEach(function(watcher) {
  614. watcher(employeesTab);
  615. });
  616. }
  617. });
  618. }).observe(employeesTab[0], { childList: true });
  619. }
  620.  
  621. /**
  622. * Executes the provided watchers after the employee tab was loaded
  623. * @param {array} watchers
  624. */
  625. function watchApplicationsTab(watchers) {
  626. var tab = $('#applications');
  627.  
  628. new MutationObserver(function(mutations) {
  629. mutations.forEach(function(mutation) {
  630. if (mutation.addedNodes.length === 9) {
  631. watchers.forEach(function(watcher) {
  632. watcher(tab);
  633. });
  634. }
  635. });
  636. }).observe(tab[0], { childList: true });
  637. }
  638.  
  639. ///////////////////////////////////////////////////////////////////////////////////////////////////
  640. // Main
  641. ///////////////////////////////////////////////////////////////////////////////////////////////////
  642. init();
  643.  
  644. var COMPANY = getInformationForCurrentCompany();
  645.  
  646. // exit if the company is not supported
  647. if (!COMPANY) {
  648. alert('Unsupported company.');
  649. return;
  650. }
  651.  
  652. watchEmployeesTab([
  653. addWorkersPerRankInformation,
  654. addEmployeeEfficiency,
  655. addEmployeeLastAction,
  656. sortEmployeesByRank
  657. ]);
  658.  
  659. watchApplicationsTab([
  660. addEfficiencyCalculator,
  661. addEnterApplicationStatsIntoCalculator
  662. ]);
  663. }(XMLHttpRequest));
Add Comment
Please, Sign In to add comment