DH2 Fixed

Improve Diamond Hunt 2

当前为 2017-05-12 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name DH2 Fixed
  3. // @namespace FileFace
  4. // @description Improve Diamond Hunt 2
  5. // @version 0.184.1
  6. // @author Zorbing
  7. // @license ISC; http://opensource.org/licenses/ISC
  8. // @grant none
  9. // @run-at document-start
  10. // @include http://www.diamondhunt.co/game.php
  11. // ==/UserScript==
  12.  
  13. /**
  14. * ISC License (ISC)
  15. *
  16. * Copyright (c) 2017, Martin Boekhoff
  17. *
  18. * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
  19. * granted, provided that the above copyright notice and this permission notice appear in all copies.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  23. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  24. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  25. * PERFORMANCE OF THIS SOFTWARE.
  26. *
  27. * Source: http://opensource.org/licenses/ISC
  28. */
  29.  
  30. (function ()
  31. {
  32. 'use strict';
  33. var version = '0.184.1';
  34.  
  35. /**
  36. * observer
  37. */
  38. var observer;
  39. (function (observer)
  40. {
  41. var observedKeys = new Map();
  42.  
  43. function add(key, fn)
  44. {
  45. if (key instanceof Array)
  46. {
  47. for (var _i = 0, key_1 = key; _i < key_1.length; _i++)
  48. {
  49. var k = key_1[_i];
  50. add(k, fn);
  51. }
  52. }
  53. else
  54. {
  55. if (!observedKeys.has(key))
  56. {
  57. observedKeys.set(key, new Set());
  58. }
  59. observedKeys.get(key).add(fn);
  60. }
  61. return fn;
  62. }
  63. observer.add = add;
  64.  
  65. function notify(key, oldValue)
  66. {
  67. var newValue = getGameValue(key);
  68. window[key] = newValue;
  69. if (observedKeys.has(key))
  70. {
  71. observedKeys.get(key).forEach(function (fn)
  72. {
  73. return fn(key, oldValue, newValue);
  74. });
  75. }
  76. }
  77. observer.notify = notify;
  78.  
  79. function remove(key, fn)
  80. {
  81. if (key instanceof Array)
  82. {
  83. var ret = [];
  84. for (var _i = 0, key_2 = key; _i < key_2.length; _i++)
  85. {
  86. var k = key_2[_i];
  87. ret.push(remove(k, fn));
  88. }
  89. return ret;
  90. }
  91. if (!observedKeys.has(key))
  92. {
  93. return false;
  94. }
  95. return observedKeys.get(key).delete(fn);
  96. }
  97. observer.remove = remove;
  98. })(observer || (observer = {}));
  99. /**
  100. * global constants
  101. */
  102. var TIER_LEVELS = ['empty', 'sapphire', 'emerald', 'ruby', 'diamond'];
  103. var TIER_NAMES = ['Standard', 'Sapphire', 'Emerald', 'Ruby', 'Diamond'];
  104. var TIER_ITEMS = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'fishingRod', 'chisel'];
  105. var FURNACE_LEVELS = ['stone', 'bronze', 'iron', 'silver', 'gold'];
  106. var OVEN_LEVELS = ['bronze', 'iron', 'silver', 'gold'];
  107. var WAND_LEVELS = ['wooden', 'oak', 'willow', 'maple', 'stardust'];
  108. var OIL_STORAGE_SIZES = [10e3, 50e3, 100e3, 300e3, 600e3, 2e6];
  109. var HIDE_RECIPES = {
  110. 'drills':
  111. {
  112. max: 10
  113. }
  114. , 'crushers':
  115. {
  116. max: 10
  117. }
  118. , 'giantDrills':
  119. {
  120. max: 10
  121. }
  122. , 'excavators':
  123. {
  124. max: 10
  125. }
  126. , 'oilPipe':
  127. {
  128. max: 1
  129. }
  130. , 'pumpjacks':
  131. {
  132. max: 10
  133. }
  134. , 'rowBoat':
  135. {
  136. max: 1
  137. }
  138. , 'canoe':
  139. {
  140. max: 1
  141. }
  142. // thanks aguyd
  143. , 'bonemealBin':
  144. {
  145. extraKeys: ['boundFilledBonemealBin']
  146. , max: 1
  147. }
  148. , 'oilFactory':
  149. {
  150. max: 1
  151. }
  152. , 'brewingKit':
  153. {
  154. max: 1
  155. }
  156. , 'rocket':
  157. {
  158. max: 1
  159. }
  160. };
  161. var SMELTING_REQUIREMENTS = {
  162. 'glass':
  163. {
  164. sand: 1
  165. , oil: 10
  166. }
  167. , 'bronzeBar':
  168. {
  169. copper: 1
  170. , tin: 1
  171. , oil: 10
  172. }
  173. , 'ironBar':
  174. {
  175. iron: 1
  176. , oil: 100
  177. }
  178. , 'silverBar':
  179. {
  180. silver: 1
  181. , oil: 300
  182. }
  183. , 'goldBar':
  184. {
  185. gold: 1
  186. , oil: 1e3
  187. }
  188. , 'promethiumBar':
  189. {
  190. promethium: 1
  191. , charcoal: 1
  192. }
  193. };
  194. var PLANT_NAME = {
  195. '1': 'Dark Mushrooms'
  196. , '2': 'Red Mushrooms'
  197. , '3': 'Dotted Green Leafs'
  198. , '4': 'Green Leafs'
  199. , '5': 'Lime Leafs'
  200. , '6': 'Gold Leafs'
  201. , '7': 'Striped Gold Leafs'
  202. , '8': 'Crystal Leafs'
  203. , '9': 'Striped Crystal Leafs'
  204. , '10': 'Blewit Mushrooms'
  205. , '11': 'Snapegrass'
  206. , '12': 'Tree'
  207. , '13': 'Oak Tree'
  208. , '14': 'Wheat'
  209. , '15': 'Willow Tree'
  210. , '16': 'Grass'
  211. , '17': 'Maple Tree'
  212. , '18': 'Stardust Tree'
  213. , '19': 'Carrots'
  214. , '20': 'Tomatoes'
  215. , '21': 'Potatoes'
  216. };
  217. var SKILL_LIST = ['mining', 'crafting', 'woodcutting', 'farming', 'brewing', 'combat', 'fishing', 'cooking', 'magic'];
  218. var AREA_LIST = ['fields', 'forests', 'caves', 'volcano', 'northFields', 'hauntedMansion'];
  219. var AREA_NAMES = ['Fields', 'Forests', 'Caves', 'Volcano', 'Northern Fields', 'Haunted Mansion'];
  220. var MONSTER_NAMES = ['Chicken', 'Rat', 'Bee', 'Snake', 'Tree', 'Thief', 'Bear', 'Bat', 'Skeleton', 'Little Demon', 'Fire Bird', 'Healer', 'Zombie Goblin', 'Dead Tree', 'Ice Bird', 'Phantom', 'Ghost', 'The Death'];
  221. var FISH_XP = {
  222. 'rawShrimp': 50
  223. , 'rawSardine': 500
  224. , 'rawTuna': 3e3
  225. , 'rawSwordfish': 5e3
  226. , 'rawShark': 12e3
  227. };
  228. var BOAT_LIST = ['rowBoat', 'canoe'];
  229.  
  230. var format;
  231. (function (format)
  232. {
  233. var UNITS = [
  234. {
  235. threshold: 10e3
  236. , factor: 1e3
  237. , token: 'k'
  238. }
  239. , {
  240. threshold: 1e6
  241. , factor: 1e6
  242. , token: 'M'
  243. }
  244. , {
  245. threshold: 1e9
  246. , factor: 1e9
  247. , token: 'B'
  248. }
  249. , {
  250. threshold: 1e12
  251. , factor: 1e12
  252. , token: 'T'
  253. }
  254. , {
  255. threshold: 1e15
  256. , factor: 1e15
  257. , token: 'Q'
  258. }];
  259. var TIME_STEPS = [
  260. {
  261. threshold: 1
  262. , name: 'second'
  263. , short: 'sec'
  264. , padp: 0
  265. }
  266. , {
  267. threshold: 60
  268. , name: 'minute'
  269. , short: 'min'
  270. , padp: 0
  271. }
  272. , {
  273. threshold: 3600
  274. , name: 'hour'
  275. , short: 'h'
  276. , padp: 1
  277. }
  278. , {
  279. threshold: 86400
  280. , name: 'day'
  281. , short: 'd'
  282. , padp: 2
  283. }];
  284.  
  285. function zeroPadLeft(num)
  286. {
  287. return (num < 10 ? '0' : '') + num;
  288. }
  289.  
  290. function ensureNumber(num)
  291. {
  292. return (typeof num === 'number' ? num : Number(num));
  293. }
  294.  
  295. function number(num, shorten)
  296. {
  297. if (shorten === void 0)
  298. {
  299. shorten = false;
  300. }
  301. num = ensureNumber(num);
  302. if (shorten)
  303. {
  304. for (var i = UNITS.length - 1; i >= 0; i--)
  305. {
  306. var unit = UNITS[i];
  307. if (num >= unit.threshold)
  308. {
  309. return number(Math.round(num / unit.factor)) + unit.token;
  310. }
  311. }
  312. }
  313. return num.toLocaleString('en');
  314. }
  315. format.number = number;
  316.  
  317. function numbersInText(text)
  318. {
  319. return text.replace(/\d(?:[\d',\.]*\d)?/g, function (numStr)
  320. {
  321. return number(numStr.replace(/\D/g, ''));
  322. });
  323. }
  324. format.numbersInText = numbersInText;
  325. // use time format established in DHQoL (https://greasyfork.org/scripts/16041-dhqol)
  326. function timer(timer, shorten)
  327. {
  328. if (shorten === void 0)
  329. {
  330. shorten = true;
  331. }
  332. if (typeof timer === 'string')
  333. {
  334. timer = parseInt(timer, 10);
  335. }
  336. timer = Math.max(timer, 0);
  337. var days = Math.floor(timer / 86400); // 24 * 60 * 60
  338. var hours = Math.floor((timer % 86400) / 3600); // 60 * 60
  339. var minutes = Math.floor((timer % 3600) / 60);
  340. var seconds = timer % 60;
  341. return (shorten && days === 0 ? '' : days + 'd ')
  342. + (shorten && days === 0 && hours === 0 ? '' : zeroPadLeft(hours) + ':')
  343. + zeroPadLeft(minutes) + ':'
  344. + zeroPadLeft(seconds);
  345. }
  346. format.timer = timer;
  347.  
  348. function time2NearestUnit(time, long)
  349. {
  350. if (long === void 0)
  351. {
  352. long = false;
  353. }
  354. var step = TIME_STEPS[0];
  355. for (var i = TIME_STEPS.length - 1; i > 0; i--)
  356. {
  357. if (time >= TIME_STEPS[i].threshold)
  358. {
  359. step = TIME_STEPS[i];
  360. break;
  361. }
  362. }
  363. var factor = Math.pow(10, step.padp);
  364. var num = Math.round(time / step.threshold * factor) / factor;
  365. var unit = long ? step.name + (num === 1 ? '' : 's') : step.short;
  366. return num + ' ' + unit;
  367. }
  368. format.time2NearestUnit = time2NearestUnit;
  369.  
  370. function min2Str(minutes)
  371. {
  372. if (typeof minutes === 'string')
  373. {
  374. minutes = parseInt(minutes, 10);
  375. }
  376. var m = minutes % 60;
  377. var h = Math.floor(minutes / 60);
  378. return (h > 0 ? h + ' hour' + (h == 1 ? '' : 's') + ' and ' : '')
  379. + m + ' minute' + (m == 1 ? '' : 's');
  380. }
  381. format.min2Str = min2Str;
  382. })(format || (format = {}));
  383.  
  384. /**
  385. * general functions
  386. */
  387. function getStyle(elId)
  388. {
  389. var id = elId != null ? 'style-' + elId : null;
  390. var styleElement = id != null ? document.getElementById(id) : null;
  391. if (styleElement == null)
  392. {
  393. styleElement = document.createElement('style');
  394. if (id != null)
  395. {
  396. styleElement.id = id;
  397. }
  398. styleElement.type = 'text/css';
  399. document.head.appendChild(styleElement);
  400. }
  401. return styleElement;
  402. }
  403.  
  404. function addStyle(styleCode, elId)
  405. {
  406. var styleElement = getStyle(elId);
  407. styleElement.innerHTML += styleCode;
  408. }
  409.  
  410. function capitalize(str)
  411. {
  412. return str[0].toUpperCase() + str.substr(1);
  413. }
  414.  
  415. function key2Name(key, lowerCase)
  416. {
  417. if (lowerCase === void 0)
  418. {
  419. lowerCase = false;
  420. }
  421. var name = key.replace(/[A-Z]/g, function (c)
  422. {
  423. return ' ' + (lowerCase ? c.toLowerCase() : c);
  424. });
  425. return lowerCase ? name : capitalize(name);
  426. }
  427.  
  428. function split2Words(str, char)
  429. {
  430. if (char === void 0)
  431. {
  432. char = ' ';
  433. }
  434. return str.replace(/[A-Z]/g, char + '$&');
  435. }
  436.  
  437. function getBoundKey(key)
  438. {
  439. return 'bound' + capitalize(key);
  440. }
  441.  
  442. function getTierKey(key, tierLevel)
  443. {
  444. return TIER_LEVELS[tierLevel] + capitalize(key);
  445. }
  446.  
  447. function getWikiaKey(key)
  448. {
  449. return key2Name(key.replace(/^bound-?|^special-case-/i, '').replace(/\d+[km]?$/i, ''))
  450. .replace(/^\s/, '').replace(/[ -]/g, '_')
  451. .replace(/^(?:Empty|Sapphire|Emerald|Ruby|Diamond|Raw|Uncooked|Filled)_/, '')
  452. .replace(/^(?:Bronze|Iron|Silver|Gold|Promethium|Runite)_(?!Bar)/, '')
  453. .replace(/^Npc_/, 'Monster_')
  454. .replace(/_(?:Unlocked|Quest)$/, '');
  455. }
  456.  
  457. function getWikiaLink(key)
  458. {
  459. return 'http://diamondhuntonline.wikia.com/wiki/' + getWikiaKey(key);
  460. }
  461.  
  462. function now()
  463. {
  464. return (new Date()).getTime();
  465. }
  466.  
  467. function ensureTooltip(id, target)
  468. {
  469. var tooltipId = 'tooltip-' + id;
  470. var tooltipEl = document.getElementById(tooltipId);
  471. if (!tooltipEl)
  472. {
  473. tooltipEl = document.createElement('div');
  474. tooltipEl.id = tooltipId;
  475. tooltipEl.style.display = 'none';
  476. var tooltipList = document.getElementById('tooltip-list');
  477. tooltipList.appendChild(tooltipEl);
  478. }
  479. // ensure binded events to show the tooltip
  480. if (target.dataset.tooltipId == null)
  481. {
  482. target.dataset.tooltipId = tooltipId;
  483. window.$(target).bind(
  484. {
  485. mousemove: window.changeTooltipPosition
  486. , mouseenter: window.showTooltip
  487. , mouseleave: function (event)
  488. {
  489. var target = event.target;
  490. var parent = target.parentElement;
  491. // ensure tooltips inside an tooltip element is possible
  492. if (parent && !!parent.dataset.tooltipId)
  493. {
  494. window.showTooltip.call(parent, event);
  495. }
  496. else
  497. {
  498. window.hideTooltip(event);
  499. }
  500. }
  501. });
  502. }
  503. return tooltipEl;
  504. }
  505. var timeStr2Sec = (function ()
  506. {
  507. var unitFactors = {
  508. 'd': 24 * 60 * 60
  509. , 'h': 60 * 60
  510. , 'm': 60
  511. , 's': 1
  512. };
  513. return function timeStr2Sec(str)
  514. {
  515. return str
  516. .replace(/(\d+)([hms])/g, function (wholeMatch, num, unit)
  517. {
  518. return parseInt(num) * (unitFactors[unit] || 1) + '+';
  519. })
  520. .split('+')
  521. .map(function (s)
  522. {
  523. return parseInt(s, 10);
  524. })
  525. .filter(function (n)
  526. {
  527. return !isNaN(n);
  528. })
  529. .reduce(function (p, c)
  530. {
  531. return p + c;
  532. }, 0);
  533. };
  534. })();
  535.  
  536. function getGameValue(key)
  537. {
  538. return window[key];
  539. }
  540.  
  541. function getFurnaceLevel()
  542. {
  543. for (var i = FURNACE_LEVELS.length - 1; i >= 0; i--)
  544. {
  545. if (getGameValue(getBoundKey(FURNACE_LEVELS[i] + 'Furnace')) > 0)
  546. {
  547. return i;
  548. }
  549. }
  550. return -1;
  551. }
  552.  
  553. function getFurnaceLevelName()
  554. {
  555. return FURNACE_LEVELS[getFurnaceLevel()] || '';
  556. }
  557.  
  558. function getPrice(item)
  559. {
  560. var price = window.getPrice(item);
  561. if (typeof price === 'number')
  562. {
  563. return price;
  564. }
  565. var match = price.match(/(\d+)([kM])/);
  566. if (!match)
  567. {
  568. return parseInt(price, 10);
  569. }
  570. var FACTORS = {
  571. 'k': 1e3
  572. , 'M': 1e6
  573. };
  574. return parseInt(match[1], 10) * (FACTORS[match[2]] || 1);
  575. }
  576. /**
  577. * persistence store
  578. */
  579. var store;
  580. (function (store)
  581. {
  582. var oldPrefix = 'dh2-';
  583. var storePrefix = 'dh2.';
  584.  
  585. function update(key, keepOldValue)
  586. {
  587. if (keepOldValue === void 0)
  588. {
  589. keepOldValue = true;
  590. }
  591. if (localStorage.hasOwnProperty(oldPrefix + key))
  592. {
  593. if (keepOldValue)
  594. {
  595. localStorage.setItem(storePrefix + key, localStorage.getItem(oldPrefix + key));
  596. }
  597. localStorage.removeItem(oldPrefix + key);
  598. }
  599. }
  600. var changeListener = new Map();
  601.  
  602. function changeDetected(key, oldValue, newValue)
  603. {
  604. if (changeListener.has(key))
  605. {
  606. setTimeout(function ()
  607. {
  608. changeListener.get(key).forEach(function (fn)
  609. {
  610. return fn(key, oldValue, newValue);
  611. });
  612. });
  613. }
  614. }
  615.  
  616. function watchFn(fnName)
  617. {
  618. var _fn = localStorage[fnName];
  619. localStorage[fnName] = function (key)
  620. {
  621. var args = [];
  622. for (var _i = 1; _i < arguments.length; _i++)
  623. {
  624. args[_i - 1] = arguments[_i];
  625. }
  626. var oldValue = localStorage.getItem(key);
  627. _fn.apply(localStorage, [key].concat(args));
  628. var newValue = localStorage.getItem(key);
  629. if (oldValue !== newValue)
  630. {
  631. changeDetected(key, oldValue, newValue);
  632. }
  633. };
  634. }
  635. watchFn('setItem');
  636. watchFn('removeItem');
  637. var _clear = localStorage.clear;
  638. localStorage.clear = function ()
  639. {
  640. var oldValues = new Map();
  641. for (var i = 0; i < localStorage.length; i++)
  642. {
  643. var key = localStorage.key(i);
  644. oldValues.set(key, localStorage.getItem(key));
  645. }
  646. _clear();
  647. for (var key in oldValues)
  648. {
  649. var newValue = localStorage.getItem(key);
  650. if (oldValues.get(key) !== newValue)
  651. {
  652. changeDetected(key, oldValues.get(key), newValue);
  653. }
  654. }
  655. };
  656.  
  657. function addChangeListener(key, fn)
  658. {
  659. if (!changeListener.has(key))
  660. {
  661. changeListener.set(key, new Set());
  662. }
  663. changeListener.get(key).add(fn);
  664. }
  665. store.addChangeListener = addChangeListener;
  666.  
  667. function removeChangeListener(key, fn)
  668. {
  669. if (changeListener.has(key))
  670. {
  671. changeListener.get(key).delete(fn);
  672. }
  673. }
  674. store.removeChangeListener = removeChangeListener;
  675.  
  676. function get(key)
  677. {
  678. update(key);
  679. var value = localStorage.getItem(storePrefix + key);
  680. if (value != null)
  681. {
  682. try
  683. {
  684. return JSON.parse(value);
  685. }
  686. catch (e)
  687. {}
  688. }
  689. return value;
  690. }
  691. store.get = get;
  692.  
  693. function has(key)
  694. {
  695. update(key);
  696. return localStorage.hasOwnProperty(storePrefix + key);
  697. }
  698. store.has = has;
  699.  
  700. function remove(key)
  701. {
  702. update(key, false);
  703. localStorage.removeItem(storePrefix + key);
  704. }
  705. store.remove = remove;
  706.  
  707. function set(key, value)
  708. {
  709. update(key, false);
  710. localStorage.setItem(storePrefix + key, JSON.stringify(value));
  711. }
  712. store.set = set;
  713. })(store || (store = {}));
  714.  
  715. var settings;
  716. (function (settings)
  717. {
  718. settings.name = 'settings';
  719. var DIALOG_WIDTH = 450;
  720. var KEY;
  721. (function (KEY)
  722. {
  723. KEY[KEY["hideCraftingRecipes"] = 0] = "hideCraftingRecipes";
  724. KEY[KEY["hideUselessItems"] = 1] = "hideUselessItems";
  725. KEY[KEY["useNewChat"] = 2] = "useNewChat";
  726. KEY[KEY["colorizeChat"] = 3] = "colorizeChat";
  727. KEY[KEY["intelligentScrolling"] = 4] = "intelligentScrolling";
  728. KEY[KEY["showTimestamps"] = 5] = "showTimestamps";
  729. KEY[KEY["showIcons"] = 6] = "showIcons";
  730. KEY[KEY["showTags"] = 7] = "showTags";
  731. KEY[KEY["enableSpamDetection"] = 8] = "enableSpamDetection";
  732. KEY[KEY["showNotifications"] = 9] = "showNotifications";
  733. KEY[KEY["showEssencePopup"] = 10] = "showEssencePopup";
  734. KEY[KEY["wikiaLinks"] = 11] = "wikiaLinks";
  735. KEY[KEY["newXpAnimation"] = 12] = "newXpAnimation";
  736. KEY[KEY["amountSymbol"] = 13] = "amountSymbol";
  737. })(KEY = settings.KEY || (settings.KEY = {}));;
  738. var CFG = (_a = {}
  739. , _a[KEY.hideCraftingRecipes] = {
  740. name: 'Hide crafting recipes of finished items'
  741. , description: "Hides crafting recipes of:\n\t\t\t\t<ul style=\"margin: .5rem 0 0;\">\n\t\t\t\t\t<li>furnace, oil storage and oven recipes if they aren't better than the current level</li>\n\t\t\t\t\t<li>machines if the user has the maximum amount of this type (counts bound and unbound items)</li>\n\t\t\t\t\t<li>non-stackable items which the user already owns (counts bound and unbound items)</li>\n\t\t\t\t</ul>"
  742. , defaultValue: true
  743. }
  744. , _a[KEY.hideUselessItems] = {
  745. name: 'Hide useless items'
  746. , description: "Hides <em>unbound</em> items which may has been crafted accidentially and are of no use for the player:\n\t\t\t\t<ul style=\"margin: .5rem 0 0;\">\n\t\t\t\t\t<li>furnace, oil storage and oven recipes if they aren't better than the current level</li>\n\t\t\t\t\t<li>machines if the user has already bound the maximum amount of this type</li>\n\t\t\t\t\t<li>non-stackable items which the user has already bound</li>\n\t\t\t\t</ul>"
  747. , defaultValue: false
  748. }
  749. , _a[KEY.useNewChat] = {
  750. name: 'Use the new chat'
  751. , description: "Enables using the completely new chat with pm tabs, clickable links, clickable usernames to send a pm, intelligent scrolling and suggesting commands while typing"
  752. , defaultValue: true
  753. }
  754. , _a[KEY.colorizeChat] = {
  755. name: 'Colorize chat messages'
  756. , description: "Colorize chat messages according to a unique color for each user"
  757. , defaultValue: false
  758. , sub:
  759. {
  760. 'colorizer':
  761. {
  762. defaultValue: 0
  763. , label: ['Equally Distributed', 'Random (light colors)', 'Random (dark colors)']
  764. , options: ['equallyDistributed', 'random1', 'random2']
  765. }
  766. }
  767. }
  768. , _a[KEY.intelligentScrolling] = {
  769. name: 'Intelligent scrolling'
  770. , description: "Autoscroll gets disabled when you scroll up and gets enabled again when you scroll all the way down to the bottom of the chat."
  771. , defaultValue: true
  772. }
  773. , _a[KEY.showTimestamps] = {
  774. name: 'Show timestamps'
  775. , description: "Enables showing timestamps in chat"
  776. , defaultValue: true
  777. }
  778. , _a[KEY.showIcons] = {
  779. name: 'Show user-icons'
  780. , description: "Enables showing icons (formerly sigils) for each user in chat"
  781. , defaultValue: true
  782. }
  783. , _a[KEY.showTags] = {
  784. name: 'Show user-tags'
  785. , description: "Enables showing tags (Dev, Mod, Contributor) and colors for messages in chat"
  786. , defaultValue: true
  787. }
  788. , _a[KEY.enableSpamDetection] = {
  789. name: 'Enable spam detection'
  790. , description: "Enables simple spam detection"
  791. , defaultValue: true
  792. }
  793. , _a[KEY.showNotifications] = {
  794. name: 'Show browser notifications'
  795. , description: "Shows browser notifications for enabled events (click the little gear for more options)"
  796. , defaultValue: true
  797. , sub:
  798. {
  799. 'showType':
  800. {
  801. defaultValue: 0
  802. , label: ['only when window inactive', 'always']
  803. , options: ['whenInactive', 'always']
  804. }
  805. , 'smelting':
  806. {
  807. defaultValue: true
  808. , label: 'Smelting finishes'
  809. }
  810. , 'chopping':
  811. {
  812. defaultValue: true
  813. , label: 'A tree is fully grown'
  814. }
  815. , 'harvest':
  816. {
  817. defaultValue: true
  818. , label: 'A plant can be harvested'
  819. }
  820. , 'potionEffect':
  821. {
  822. defaultValue: true
  823. , label: 'A potion\'s effect ends'
  824. }
  825. , 'boatReturned':
  826. {
  827. defaultValue: true
  828. , label: 'The boat or canoe returns'
  829. }
  830. , 'heroReady':
  831. {
  832. defaultValue: true
  833. , label: 'The hero is fully recovered and ready to fight'
  834. }
  835. , 'itemsSold':
  836. {
  837. defaultValue: true
  838. , label: 'Items are sold on the market'
  839. }
  840. , 'pirate':
  841. {
  842. defaultValue: true
  843. , label: 'A pirate has found a treasure map'
  844. }
  845. , 'essence':
  846. {
  847. defaultValue: true
  848. , label: 'An essence was found'
  849. }
  850. , 'pm':
  851. {
  852. defaultValue: true
  853. , label: 'A private messages (pm) arrives'
  854. }
  855. , 'mention':
  856. {
  857. defaultValue: true
  858. , label: 'The username is mentioned in chat'
  859. }
  860. , 'keyword':
  861. {
  862. defaultValue: true
  863. , label: 'A keyword is mentioned in chat'
  864. }
  865. , 'serverMsg':
  866. {
  867. defaultValue: true
  868. , label: 'Server messages (like <em>Server is restarting...</em>)'
  869. }
  870. }
  871. }
  872. , _a[KEY.showEssencePopup] = {
  873. name: 'Show essence popup'
  874. , description: "Shown a popup (like the ones when a diamond is found or the server is restarting) for finding an essence"
  875. , defaultValue: false
  876. }
  877. , _a[KEY.wikiaLinks] = {
  878. name: 'Show wikia links'
  879. , description: "Show wikia links for every item on hover (the little icon in the upper left corner)"
  880. , defaultValue: true
  881. }
  882. , _a[KEY.newXpAnimation] = {
  883. name: 'New XP-gain animation'
  884. , description: "Show gained xp on top skill bar instead on the position of the mouse"
  885. , defaultValue: true
  886. }
  887. , _a[KEY.amountSymbol] = {
  888. name: 'Show \u00D7 on items'
  889. , description: "Show a tiny \u00D7-symbol before amount numbers of items"
  890. , defaultValue: true
  891. }
  892. , _a);
  893. var SETTINGS_TABLE_ID = 'dh2-settings';
  894. var SETTING_ID_PREFIX = 'dh2-setting-';
  895. var settings2Init = Object.keys(CFG);
  896. /**
  897. * settings
  898. */
  899. function toName(key, subKey)
  900. {
  901. var name = typeof key === 'string' ? key : KEY[key];
  902. if (subKey !== undefined)
  903. {
  904. return name + '.' + subKey;
  905. }
  906. return name;
  907. }
  908.  
  909. function getStoreKey(key, subKey)
  910. {
  911. return 'setting.' + toName(key, subKey);
  912. }
  913. var observedSettings = new Map();
  914. var observedSubSettings = new Map();
  915.  
  916. function observe(key, fn)
  917. {
  918. var n = toName(key);
  919. if (!observedSettings.has(n))
  920. {
  921. observedSettings.set(n, new Set());
  922. }
  923. observedSettings.get(n).add(fn);
  924. }
  925. settings.observe = observe;
  926.  
  927. function observeSub(key, subKey, fn)
  928. {
  929. var n = toName(key, subKey);
  930. if (!observedSubSettings.has(n))
  931. {
  932. observedSubSettings.set(n, new Set());
  933. }
  934. observedSubSettings.get(n).add(fn);
  935. }
  936. settings.observeSub = observeSub;
  937.  
  938. function unobserve(key, fn)
  939. {
  940. var n = toName(key);
  941. if (!observedSettings.has(n))
  942. {
  943. return false;
  944. }
  945. return observedSettings.get(n).delete(fn);
  946. }
  947. settings.unobserve = unobserve;
  948.  
  949. function unobserveSub(key, subKey, fn)
  950. {
  951. var n = toName(key, subKey);
  952. if (!observedSubSettings.has(n))
  953. {
  954. return false;
  955. }
  956. return observedSubSettings.get(n).delete(fn);
  957. }
  958. settings.unobserveSub = unobserveSub;
  959. var settingsProxies = new Map();
  960.  
  961. function get(key)
  962. {
  963. if (!CFG.hasOwnProperty(key))
  964. {
  965. return false;
  966. }
  967. if (settingsProxies.has(key))
  968. {
  969. var proxy = settingsProxies.get(key);
  970. return proxy.get(key);
  971. }
  972. var name = getStoreKey(key);
  973. return store.has(name) ? store.get(name) : CFG[key].defaultValue;
  974. }
  975. settings.get = get;
  976.  
  977. function getSub(key, subKey)
  978. {
  979. if (!CFG.hasOwnProperty(key))
  980. {
  981. return null;
  982. }
  983. var name = getStoreKey(key, subKey);
  984. return store.has(name) ? store.get(name) : CFG[key].sub[subKey].defaultValue;
  985. }
  986. settings.getSub = getSub;
  987.  
  988. function set(key, newValue)
  989. {
  990. if (!CFG.hasOwnProperty(key))
  991. {
  992. return;
  993. }
  994. var oldValue = get(key);
  995. var n = toName(key);
  996. if (settingsProxies.has(key))
  997. {
  998. var proxy = settingsProxies.get(key);
  999. proxy.set(key, oldValue, newValue);
  1000. }
  1001. else
  1002. {
  1003. store.set(getStoreKey(key), newValue);
  1004. }
  1005. if (oldValue !== newValue && observedSettings.has(n))
  1006. {
  1007. observedSettings.get(n).forEach(function (fn)
  1008. {
  1009. return fn(key, oldValue, newValue);
  1010. });
  1011. }
  1012. }
  1013. settings.set = set;
  1014.  
  1015. function setSub(key, subKey, newValue)
  1016. {
  1017. if (!CFG.hasOwnProperty(key))
  1018. {
  1019. return;
  1020. }
  1021. var oldValue = getSub(key, subKey);
  1022. var n = toName(key, subKey);
  1023. store.set(getStoreKey(key, subKey), newValue);
  1024. if (oldValue !== newValue && observedSubSettings.has(n))
  1025. {
  1026. observedSubSettings.get(n).forEach(function (fn)
  1027. {
  1028. return fn(key, subKey, oldValue, newValue);
  1029. });
  1030. }
  1031. }
  1032. settings.setSub = setSub;
  1033.  
  1034. function getSubCfg(key)
  1035. {
  1036. if (!CFG.hasOwnProperty(key))
  1037. {
  1038. return;
  1039. }
  1040. return CFG[key].sub;
  1041. }
  1042. settings.getSubCfg = getSubCfg;
  1043.  
  1044. function initSettingsStyle()
  1045. {
  1046. addStyle("\ntable.table-style1 tr:not([onclick])\n{\n\tcursor: initial;\n}\n#tab-container-profile h2.section-title\n{\n\tcolor: orange;\n\tline-height: 1.2rem;\n\tmargin-top: 2rem;\n}\n#tab-container-profile h2.section-title > span.note\n{\n\tfont-size: 0.9rem;\n}\n#" + SETTINGS_TABLE_ID + " tr.reload td:first-child::after\n{\n\tcontent: '*';\n\tfont-weight: bold;\n\tmargin-left: 3px;\n}\n#" + SETTINGS_TABLE_ID + " tr.sub td\n{\n\tposition: relative;\n}\n#" + SETTINGS_TABLE_ID + " tr.sub td button:last-child\n{\n\tmargin: -1px;\n\tposition: absolute;\n\tright: 0;\n}\n\n.ui-dialog-content > h2:first-child\n{\n\tmargin-top: 0;\n}\n\n.settings-container\n{\n\tlist-style: none;\n\tmargin: 5px 30px;\n\tpadding: 0;\n}\n.ui-dialog-content .settings-container\n{\n\tmargin: 5px 0;\n}\n.settings-container > li.setting\n{\n\tbackground-color: silver;\n\tborder: 1px solid black;\n\tborder-top-width: 0;\n\tdisplay: flex;\n}\n.settings-container > li.setting:first-child\n{\n\tborder-top-width: 1px;\n}\n.ui-dialog-content .settings-container > li.setting,\n.ui-dialog-content .settings-container > li.setting:hover\n{\n\tbackground-color: transparent;\n\tborder: 0;\n\tmargin: .25rem 0;\n}\n.settings-container > li.setting,\n.settings-container > li.setting *\n{\n\tcursor: pointer;\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n.settings-container > li.setting:hover\n{\n\tbackground-color: gray;\n}\n.settings-container > li.setting > input[type=\"checkbox\"]\n{\n\tdisplay: none;\n}\n.settings-container > li.setting > label\n{\n\tdisplay: block;\n\tflex-grow: 1;\n\tpadding: .25rem .5rem;\n}\n.settings-container > li.setting > label.ui-checkboxradio-label\n{\n\ttext-align: left;\n}\n.settings-container > li.setting > label.ui-checkboxradio-label .ui-checkboxradio-icon-space\n{\n\tmargin-right: .25rem;\n}\n.settings-container > li.setting > input + label:not(.ui-checkboxradio-label)::before\n{\n\tbackground-image: url(images/icons/x.png);\n\tbackground-size: 20px;\n\tcontent: '';\n\tdisplay: inline-block;\n\theight: 20px;\n\tmargin: 0 .25rem;\n\twidth: 20px;\n\tvertical-align: middle;\n}\n.settings-container > li.setting > input:checked + label:not(.ui-checkboxradio-label)::before\n{\n\tbackground-image: url(images/icons/check.png);\n}\n.ui-dialog-content .settings-container > li.setting > label + button\n{\n\tmargin-left: -.2rem;\n\tz-index: 1;\n}\n.settings-container.sortable > li.setting > span.ui-icon.handle\n{\n\tfloat: left;\n\tmargin: 6px 10px;\n\tz-index: 10;\n}\n.settings-container > li.setting span.ui-selectmenu-button\n{\n\twidth: calc(100% - 2em - 2*3px + 2*.1em);\n}\n\t\t");
  1047. }
  1048.  
  1049. function getSettingId(key, subKey)
  1050. {
  1051. var name = toName(key) + (subKey !== undefined ? '-' + subKey : '');
  1052. return SETTING_ID_PREFIX + split2Words(name, '-').toLowerCase();
  1053. }
  1054.  
  1055. function initSettingTable()
  1056. {
  1057. function insertAfter(newChild, oldChild)
  1058. {
  1059. var parent = oldChild.parentElement;
  1060. if (oldChild.nextElementSibling == null)
  1061. {
  1062. parent.appendChild(newChild);
  1063. }
  1064. else
  1065. {
  1066. parent.insertBefore(newChild, oldChild.nextElementSibling);
  1067. }
  1068. }
  1069.  
  1070. function getCheckImageSrc(value)
  1071. {
  1072. return 'images/icons/' + (value ? 'check' : 'x') + '.png';
  1073. }
  1074. var profileTable = document.getElementById('profile-toggleTable');
  1075. if (!profileTable)
  1076. {
  1077. return;
  1078. }
  1079. var settingsHeader = document.createElement('h2');
  1080. settingsHeader.className = 'section-title';
  1081. settingsHeader.innerHTML = "Userscript \"DH2 Fixed\"<br>\n\t\t\t<span class=\"note\" style=\"display: none;\">(* changes require reloading the tab)</span>";
  1082. var requiresReloadNote = settingsHeader.querySelector('.note');
  1083. insertAfter(settingsHeader, profileTable);
  1084. var settingsTable = document.createElement('table');
  1085. settingsTable.id = SETTINGS_TABLE_ID;
  1086. settingsTable.className = 'table-style1';
  1087. settingsTable.width = '40%';
  1088. settingsTable.innerHTML = "\n\t\t<tr style=\"background-color:grey;\">\n\t\t\t<th>Setting</th>\n\t\t\t<th>Enabled</th>\n\t\t</tr>\n\t\t";
  1089.  
  1090. function addRowClickListener(row, key, settingId)
  1091. {
  1092. row.addEventListener('click', function ()
  1093. {
  1094. var newValue = !get(key);
  1095. set(key, newValue);
  1096. document.getElementById(settingId).src = getCheckImageSrc(newValue);
  1097. });
  1098. }
  1099.  
  1100. function addSubClickListener(btn, dialog)
  1101. {
  1102. btn.addEventListener('click', function (event)
  1103. {
  1104. initJQueryDialog(dialog);
  1105. event.stopPropagation();
  1106. event.preventDefault();
  1107. });
  1108. }
  1109. for (var _i = 0, settings2Init_1 = settings2Init; _i < settings2Init_1.length; _i++)
  1110. {
  1111. var k = settings2Init_1[_i];
  1112. // convert it into a KEY
  1113. var key = parseInt(k, 10);
  1114. var setting = CFG[key];
  1115. if (setting == null)
  1116. {
  1117. console.error('missing setting entry:', key, toName(key));
  1118. continue;
  1119. }
  1120. var settingId = getSettingId(key);
  1121. var row = settingsTable.insertRow(-1);
  1122. row.classList.add('setting');
  1123. if (setting.requiresReload)
  1124. {
  1125. row.classList.add('reload');
  1126. requiresReloadNote.style.display = '';
  1127. }
  1128. row.setAttribute('onclick', '');
  1129. row.innerHTML = "\n\t\t\t<td>" + setting.name + "</td>\n\t\t\t<td><img src=\"" + getCheckImageSrc(get(key)) + "\" id=\"" + settingId + "\" class=\"image-icon-20\"></td>\n\t\t\t";
  1130. if (setting.sub)
  1131. {
  1132. row.classList.add('sub');
  1133. var subBtn = document.createElement('button');
  1134. subBtn.innerHTML = "<img src=\"images/icons/gearOff.gif\" class=\"image-icon-15\">";
  1135. row.cells.item(0).appendChild(subBtn);
  1136. var dialog = createSubSettingDialog(key);
  1137. addSubClickListener(subBtn, dialog);
  1138. }
  1139. var tooltipEl = ensureTooltip(settingId, row);
  1140. tooltipEl.innerHTML = setting.description;
  1141. if (setting.requiresReload)
  1142. {
  1143. tooltipEl.innerHTML += "<span style=\"color: hsla(20, 100%, 50%, 1); font-size: .9rem; display: block; margin-top: 0.5rem;\">You have to reload the browser tab to apply changes to this setting.</span>";
  1144. }
  1145. addRowClickListener(row, key, settingId);
  1146. }
  1147. insertAfter(settingsTable, settingsHeader);
  1148. }
  1149.  
  1150. function initProxies()
  1151. {
  1152. var row = document.querySelector('tr[data-tooltip-id="tooltip-profile-removeCraftingFilter"]');
  1153. if (row)
  1154. {
  1155. var valueCache_1 = getGameValue('profileRemoveCraftingFilter') != 1;
  1156. settingsProxies.set(KEY.hideCraftingRecipes
  1157. , {
  1158. get: function (key)
  1159. {
  1160. return getGameValue('profileRemoveCraftingFilter') != 1;
  1161. }
  1162. , set: function (key, oldValue, newValue)
  1163. {
  1164. if (valueCache_1 != newValue)
  1165. {
  1166. row.click();
  1167. valueCache_1 = newValue;
  1168. }
  1169. }
  1170. });
  1171. observer.add('profileRemoveCraftingFilter', function ()
  1172. {
  1173. set(KEY.hideCraftingRecipes, getGameValue('profileRemoveCraftingFilter') != 1);
  1174. });
  1175. }
  1176. }
  1177. var subDialog;
  1178. (function (subDialog)
  1179. {
  1180. function defaultHandler(key, dialog)
  1181. {
  1182. var setting = CFG[key];
  1183. var subSettings = setting.sub;
  1184. var settingContainer = createSubSettingsContainer(key, subSettings);
  1185. dialog.appendChild(settingContainer);
  1186. }
  1187.  
  1188. function colorizeChat(dialog)
  1189. {
  1190. defaultHandler(KEY.colorizeChat, dialog);
  1191. }
  1192. subDialog.colorizeChat = colorizeChat;
  1193.  
  1194. function showNotifications(dialog)
  1195. {
  1196. dialog.appendChild(document.createTextNode('Show notifications\u2026'));
  1197. defaultHandler(KEY.showNotifications, dialog);
  1198. dialog.appendChild(document.createTextNode('Events for which notifications are shown:'));
  1199. var ulNotifType = dialog.lastElementChild;
  1200. var ulEvents = ulNotifType.cloneNode(false);
  1201. while (ulNotifType.children.length > 1)
  1202. {
  1203. ulEvents.appendChild(ulNotifType.children.item(1));
  1204. }
  1205. dialog.appendChild(ulEvents);
  1206. }
  1207. subDialog.showNotifications = showNotifications;
  1208. })(subDialog || (subDialog = {}));
  1209.  
  1210. function createSubSettingDialog(key)
  1211. {
  1212. var settingId = getSettingId(key);
  1213. var setting = CFG[key];
  1214. var dialog = document.createElement('div');
  1215. dialog.id = 'dialog-' + settingId;
  1216. dialog.style.display = 'none';
  1217. dialog.innerHTML = "<h2>" + setting.name + "</h2>";
  1218. var name = toName(key);
  1219. if (subDialog.hasOwnProperty(name))
  1220. {
  1221. subDialog[name](dialog);
  1222. }
  1223. else
  1224. {
  1225. console.warn('missing setting handler for "%s"', name);
  1226. var todoEl = document.createElement('span');
  1227. todoEl.textContent = 'TODO';
  1228. dialog.appendChild(todoEl);
  1229. }
  1230. document.body.appendChild(dialog);
  1231. return dialog;
  1232. }
  1233.  
  1234. function createSubSettingsContainer(parentKey, subSettings)
  1235. {
  1236. var settingsContainer = document.createElement('ul');
  1237. settingsContainer.className = 'settings-container';
  1238.  
  1239. function addCheckbox(listEl, subKey, id, setting)
  1240. {
  1241. var checkbox = document.createElement('input');
  1242. checkbox.type = 'checkbox';
  1243. checkbox.id = id;
  1244. checkbox.name = id;
  1245. checkbox.checked = getSub(parentKey, subKey);
  1246. var label = document.createElement('label');
  1247. label.htmlFor = id;
  1248. label.innerHTML = setting.label;
  1249. checkbox.addEventListener('change', function ()
  1250. {
  1251. return setSub(parentKey, subKey, checkbox.checked);
  1252. });
  1253. listEl.appendChild(checkbox);
  1254. listEl.appendChild(label);
  1255. }
  1256.  
  1257. function addSelectmenu(listEl, subKey, id, setting)
  1258. {
  1259. var select = document.createElement('select');
  1260. select.id = id;
  1261. select.name = id;
  1262. var options = setting.options;
  1263. var selectedIndex = getSub(parentKey, subKey);
  1264. for (var i = 0; i < options.length; i++)
  1265. {
  1266. var option = document.createElement('option');
  1267. option.value = options[i];
  1268. if (setting.label)
  1269. {
  1270. option.innerHTML = setting.label[i];
  1271. }
  1272. else
  1273. {
  1274. option.innerHTML = key2Name(options[i]);
  1275. }
  1276. option.selected = i == selectedIndex;
  1277. select.appendChild(option);
  1278. }
  1279. select.addEventListener('change', function ()
  1280. {
  1281. return setSub(parentKey, subKey, select.selectedIndex);
  1282. });
  1283. listEl.appendChild(select);
  1284. }
  1285. var keyList = Object.keys(subSettings);
  1286. var orderIndex = keyList.findIndex(function (k)
  1287. {
  1288. return subSettings[k].defaultValue instanceof Array;
  1289. });
  1290. var isSortable = orderIndex != -1;
  1291. if (isSortable)
  1292. {
  1293. keyList = getSub(parentKey, keyList[orderIndex]);
  1294. }
  1295. for (var _i = 0, keyList_1 = keyList; _i < keyList_1.length; _i++)
  1296. {
  1297. var subKey = keyList_1[_i];
  1298. var settingId = getSettingId(parentKey, subKey);
  1299. var setting = subSettings[subKey];
  1300. var listEl = document.createElement('li');
  1301. listEl.classList.add('setting');
  1302. if (isSortable)
  1303. {
  1304. listEl.dataset.subKey = subKey;
  1305. var sortableIcon = document.createElement('span');
  1306. sortableIcon.className = 'ui-icon ui-icon-arrowthick-2-n-s handle';
  1307. listEl.appendChild(sortableIcon);
  1308. }
  1309. if (setting.options)
  1310. {
  1311. addSelectmenu(listEl, subKey, settingId, setting);
  1312. }
  1313. else
  1314. {
  1315. addCheckbox(listEl, subKey, settingId, setting);
  1316. }
  1317. settingsContainer.appendChild(listEl);
  1318. }
  1319. return settingsContainer;
  1320. }
  1321.  
  1322. function initJQueryDialog(dialog)
  1323. {
  1324. var $dialog = window.$(dialog);
  1325. $dialog.dialog(
  1326. {
  1327. width: DIALOG_WIDTH + 'px'
  1328. });
  1329. $dialog.find('input[type="checkbox"]').checkboxradio()
  1330. .next().children(':first-child').removeClass('ui-state-hover');
  1331. $dialog.find('select').selectmenu(
  1332. {
  1333. change: function (event, ui)
  1334. {
  1335. var changeEvent = document.createEvent('HTMLEvents');
  1336. changeEvent.initEvent('change', false, true);
  1337. event.target.dispatchEvent(changeEvent);
  1338. }
  1339. });
  1340. $dialog.find('.sortable').sortable(
  1341. {
  1342. handle: '.handle'
  1343. , update: function (event, ui)
  1344. {
  1345. var newOrder = [];
  1346. var children = event.target.children;
  1347. for (var i = 0; i < children.length; i++)
  1348. {
  1349. var child = children[i];
  1350. newOrder.push(child.dataset.subKey);
  1351. }
  1352. var updateEvent = new CustomEvent('sortupdate'
  1353. , {
  1354. detail: newOrder
  1355. });
  1356. event.target.dispatchEvent(updateEvent);
  1357. }
  1358. });
  1359. return $dialog;
  1360. }
  1361.  
  1362. function createSettingsContainer(settingList)
  1363. {
  1364. var settingsContainer = document.createElement('ul');
  1365. settingsContainer.className = 'settings-container';
  1366.  
  1367. function addOpenDialogClickListener(el, dialog)
  1368. {
  1369. el.addEventListener('click', function (event)
  1370. {
  1371. initJQueryDialog(dialog);
  1372. event.stopPropagation();
  1373. event.preventDefault();
  1374. });
  1375. }
  1376.  
  1377. function addChangeListener(key, checkbox)
  1378. {
  1379. checkbox.addEventListener('change', function ()
  1380. {
  1381. set(key, checkbox.checked);
  1382. });
  1383. }
  1384. for (var _i = 0, settingList_1 = settingList; _i < settingList_1.length; _i++)
  1385. {
  1386. var key = settingList_1[_i];
  1387. var settingId = getSettingId(key);
  1388. var setting = CFG[key];
  1389. var index = settings2Init.indexOf(key.toString());
  1390. if (index != -1)
  1391. {
  1392. settings2Init.splice(index, 1);
  1393. }
  1394. var listEl = document.createElement('li');
  1395. listEl.classList.add('setting');
  1396. if (setting.requiresReload)
  1397. {
  1398. listEl.classList.add('reload');
  1399. }
  1400. var checkbox = document.createElement('input');
  1401. checkbox.type = 'checkbox';
  1402. checkbox.id = settingId;
  1403. checkbox.checked = get(key);
  1404. var label = document.createElement('label');
  1405. label.htmlFor = settingId;
  1406. label.textContent = setting.name;
  1407. addChangeListener(key, checkbox);
  1408. listEl.appendChild(checkbox);
  1409. listEl.appendChild(label);
  1410. if (setting.sub)
  1411. {
  1412. var moreBtn = document.createElement('button');
  1413. moreBtn.innerHTML = "<img src=\"images/icons/gearOff.gif\" class=\"image-icon-20\" />";
  1414. listEl.appendChild(moreBtn);
  1415. var dialog = createSubSettingDialog(key);
  1416. addOpenDialogClickListener(moreBtn, dialog);
  1417. }
  1418. settingsContainer.appendChild(listEl);
  1419. var tooltipEl = ensureTooltip(settingId, listEl);
  1420. tooltipEl.innerHTML = setting.description;
  1421. if (setting.requiresReload)
  1422. {
  1423. tooltipEl.innerHTML += "<span style=\"color: hsla(20, 100%, 50%, 1); font-size: .9rem; display: block; margin-top: 0.5rem;\">You have to reload the browser tab to apply changes to this setting.</span>";
  1424. }
  1425. }
  1426. return settingsContainer;
  1427. }
  1428.  
  1429. function initCraftingSettings()
  1430. {
  1431. var craftingItems = document.getElementById('tab-sub-container-crafting');
  1432. if (!craftingItems)
  1433. {
  1434. return;
  1435. }
  1436. var br = craftingItems.nextElementSibling;
  1437. var after = br.nextElementSibling;
  1438. var parent = after.parentElement;
  1439. var settingList = [KEY.hideCraftingRecipes, KEY.hideUselessItems];
  1440. var settingsContainer = createSettingsContainer(settingList);
  1441. parent.insertBefore(settingsContainer, after);
  1442. }
  1443.  
  1444. function initChatSettings()
  1445. {
  1446. var controlDiv = document.querySelector('#div-chat > div:first-child');
  1447. if (!controlDiv)
  1448. {
  1449. return;
  1450. }
  1451. var btn = document.createElement('button');
  1452. btn.textContent = 'Chat Settings';
  1453. controlDiv.appendChild(btn);
  1454. var dialog = document.createElement('div');
  1455. dialog.id = 'dialog-chat-settings';
  1456. dialog.style.display = 'none';
  1457. dialog.innerHTML = "<h2>Chat Settings</h2>";
  1458. var settingList = [KEY.useNewChat, KEY.colorizeChat, KEY.intelligentScrolling, KEY.showTimestamps, KEY.showIcons, KEY.showTags, KEY.enableSpamDetection];
  1459. var settingsContainer = createSettingsContainer(settingList);
  1460. dialog.appendChild(settingsContainer);
  1461. document.body.appendChild(dialog);
  1462. btn.addEventListener('click', function ()
  1463. {
  1464. initJQueryDialog(dialog);
  1465. });
  1466. }
  1467.  
  1468. function init()
  1469. {
  1470. initProxies();
  1471. initSettingsStyle();
  1472. initCraftingSettings();
  1473. initChatSettings();
  1474. initSettingTable();
  1475. }
  1476. settings.init = init;
  1477. var _a;
  1478. })(settings || (settings = {}));
  1479. /**
  1480. * Code from https://github.com/davidmerfield/randomColor
  1481. */
  1482. var colorGenerator;
  1483. (function (colorGenerator)
  1484. {
  1485. // seed to get repeatable colors
  1486. var seed = null;
  1487. var COLOR_NOT_FOUND = {
  1488. hueRange: []
  1489. , lowerBounds: []
  1490. , saturationRange: []
  1491. , brightnessRange: []
  1492. };
  1493. var COLOR_BOUNDS = {
  1494. 'monochrome':
  1495. {
  1496. hueRange: []
  1497. , lowerBounds: [
  1498. [0, 0]
  1499. , [100, 0]
  1500. ]
  1501. }
  1502. , 'red':
  1503. {
  1504. hueRange: [-26, 18]
  1505. , lowerBounds: [
  1506. [20, 100]
  1507. , [30, 92]
  1508. , [40, 89]
  1509. , [50, 85]
  1510. , [60, 78]
  1511. , [70, 70]
  1512. , [80, 60]
  1513. , [90, 55]
  1514. , [100, 50]
  1515. ]
  1516. }
  1517. , 'orange':
  1518. {
  1519. hueRange: [19, 46]
  1520. , lowerBounds: [
  1521. [20, 100]
  1522. , [30, 93]
  1523. , [40, 88]
  1524. , [50, 86]
  1525. , [60, 85]
  1526. , [70, 70]
  1527. , [100, 70]
  1528. ]
  1529. }
  1530. , 'yellow':
  1531. {
  1532. hueRange: [47, 62]
  1533. , lowerBounds: [
  1534. [25, 100]
  1535. , [40, 94]
  1536. , [50, 89]
  1537. , [60, 86]
  1538. , [70, 84]
  1539. , [80, 82]
  1540. , [90, 80]
  1541. , [100, 75]
  1542. ]
  1543. }
  1544. , 'green':
  1545. {
  1546. hueRange: [63, 178]
  1547. , lowerBounds: [
  1548. [30, 100]
  1549. , [40, 90]
  1550. , [50, 85]
  1551. , [60, 81]
  1552. , [70, 74]
  1553. , [80, 64]
  1554. , [90, 50]
  1555. , [100, 40]
  1556. ]
  1557. }
  1558. , 'blue':
  1559. {
  1560. hueRange: [179, 257]
  1561. , lowerBounds: [
  1562. [20, 100]
  1563. , [30, 86]
  1564. , [40, 80]
  1565. , [50, 74]
  1566. , [60, 60]
  1567. , [70, 52]
  1568. , [80, 44]
  1569. , [90, 39]
  1570. , [100, 35]
  1571. ]
  1572. }
  1573. , 'purple':
  1574. {
  1575. hueRange: [258, 282]
  1576. , lowerBounds: [
  1577. [20, 100]
  1578. , [30, 87]
  1579. , [40, 79]
  1580. , [50, 70]
  1581. , [60, 65]
  1582. , [70, 59]
  1583. , [80, 52]
  1584. , [90, 45]
  1585. , [100, 42]
  1586. ]
  1587. }
  1588. , 'pink':
  1589. {
  1590. hueRange: [283, 334]
  1591. , lowerBounds: [
  1592. [20, 100]
  1593. , [30, 90]
  1594. , [40, 86]
  1595. , [60, 84]
  1596. , [80, 80]
  1597. , [90, 75]
  1598. , [100, 73]
  1599. ]
  1600. }
  1601. };
  1602. // shared color dictionary
  1603. var colorDictionary = {};
  1604.  
  1605. function defineColor(name, hueRange, lowerBounds)
  1606. {
  1607. var _a = lowerBounds[0]
  1608. , sMin = _a[0]
  1609. , bMax = _a[1];
  1610. var _b = lowerBounds[lowerBounds.length - 1]
  1611. , sMax = _b[0]
  1612. , bMin = _b[1];
  1613. colorDictionary[name] = {
  1614. hueRange: hueRange
  1615. , lowerBounds: lowerBounds
  1616. , saturationRange: [sMin, sMax]
  1617. , brightnessRange: [bMin, bMax]
  1618. };
  1619. }
  1620.  
  1621. function loadColorBounds()
  1622. {
  1623. for (var name_1 in COLOR_BOUNDS)
  1624. {
  1625. defineColor(name_1, COLOR_BOUNDS[name_1].hueRange, COLOR_BOUNDS[name_1].lowerBounds);
  1626. }
  1627. }
  1628.  
  1629. function randomWithin(min, max)
  1630. {
  1631. if (min === void 0)
  1632. {
  1633. min = 0;
  1634. }
  1635. if (max === void 0)
  1636. {
  1637. max = 0;
  1638. }
  1639. if (seed === null)
  1640. {
  1641. return Math.floor(min + Math.random() * (max + 1 - min));
  1642. }
  1643. else
  1644. {
  1645. // seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
  1646. seed = (seed * 9301 + 49297) % 233280;
  1647. var rnd = seed / 233280.0;
  1648. return Math.floor(min + rnd * (max - min));
  1649. }
  1650. }
  1651.  
  1652. function getColorInfo(hue)
  1653. {
  1654. // maps red colors to make picking hue easier
  1655. if (hue >= 334 && hue <= 360)
  1656. {
  1657. hue -= 360;
  1658. }
  1659. for (var colorName in colorDictionary)
  1660. {
  1661. var color = colorDictionary[colorName];
  1662. if (color.hueRange.length > 0
  1663. && hue >= color.hueRange[0]
  1664. && hue <= color.hueRange[1])
  1665. {
  1666. return colorDictionary[colorName];
  1667. }
  1668. }
  1669. return COLOR_NOT_FOUND;
  1670. }
  1671.  
  1672. function getHueRange(colorInput)
  1673. {
  1674. var number = typeof colorInput === 'undefined' ? Number.NaN : colorInput;
  1675. if (typeof number === 'string')
  1676. {
  1677. number = parseInt(number, 10);
  1678. }
  1679. if (colorInput && isNaN(number) && colorDictionary.hasOwnProperty(colorInput))
  1680. {
  1681. var color = colorDictionary[colorInput];
  1682. if (color.hueRange.length > 0)
  1683. {
  1684. return color.hueRange;
  1685. }
  1686. }
  1687. else if (!isNaN(number) && number < 360 && number > 0)
  1688. {
  1689. return [number, number];
  1690. }
  1691. return [0, 360];
  1692. }
  1693.  
  1694. function pickHue(options)
  1695. {
  1696. var hueRange = getHueRange(options.hue);
  1697. var hue = randomWithin(hueRange[0], hueRange[1]);
  1698. // instead of storing red as two seperate ranges, we group them, using negative numbers
  1699. if (hue < 0)
  1700. {
  1701. return 360 + hue;
  1702. }
  1703. return hue;
  1704. }
  1705.  
  1706. function getSaturationRange(hue)
  1707. {
  1708. return getColorInfo(hue).saturationRange;
  1709. }
  1710.  
  1711. function pickSaturation(hue, options)
  1712. {
  1713. if (options.luminosity === 'random')
  1714. {
  1715. return randomWithin(0, 100);
  1716. }
  1717. if (options.hue === 'monochrome')
  1718. {
  1719. return 0;
  1720. }
  1721. var _a = getSaturationRange(hue)
  1722. , sMin = _a[0]
  1723. , sMax = _a[1];
  1724. switch (options.luminosity)
  1725. {
  1726. case 'bright':
  1727. sMin = 55;
  1728. break;
  1729. case 'dark':
  1730. sMin = sMax - 10;
  1731. break;
  1732. case 'light':
  1733. sMax = 55;
  1734. break;
  1735. }
  1736. return randomWithin(sMin, sMax);
  1737. }
  1738.  
  1739. function getMinimumBrightness(H, S)
  1740. {
  1741. var lowerBounds = getColorInfo(H).lowerBounds;
  1742. for (var i = 0; i < lowerBounds.length - 1; i++)
  1743. {
  1744. var _a = lowerBounds[i]
  1745. , s1 = _a[0]
  1746. , v1 = _a[1];
  1747. var _b = lowerBounds[i + 1]
  1748. , s2 = _b[0]
  1749. , v2 = _b[1];
  1750. if (S >= s1 && S <= s2)
  1751. {
  1752. var m = (v2 - v1) / (s2 - s1);
  1753. var b = v1 - m * s1;
  1754. return m * S + b;
  1755. }
  1756. }
  1757. return 0;
  1758. }
  1759.  
  1760. function pickBrightness(H, S, options)
  1761. {
  1762. var bMin = getMinimumBrightness(H, S);
  1763. var bMax = 100;
  1764. switch (options.luminosity)
  1765. {
  1766. case 'dark':
  1767. bMax = bMin + 20;
  1768. break;
  1769. case 'light':
  1770. bMin = (bMax + bMin) / 2;
  1771. break;
  1772. case 'random':
  1773. bMin = 0;
  1774. bMax = 100;
  1775. break;
  1776. }
  1777. return randomWithin(bMin, bMax);
  1778. }
  1779.  
  1780. function HSVtoHSL(hsv)
  1781. {
  1782. var h = hsv[0];
  1783. var s = hsv[1] / 100;
  1784. var v = hsv[2] / 100;
  1785. var k = (2 - s) * v;
  1786. return [
  1787. h
  1788. , Math.round(s * v / (k < 1 ? k : 2 - k) * 10000) / 100
  1789. , k / 2 * 100
  1790. ];
  1791. }
  1792.  
  1793. function HSVtoRGB(hsv)
  1794. {
  1795. // this doesn't work for the values of 0 and 360 here's the hacky fix
  1796. var h = Math.min(Math.max(hsv[0], 1), 359);
  1797. // Rebase the h,s,v values
  1798. h = h / 360;
  1799. var s = hsv[1] / 100;
  1800. var v = hsv[2] / 100;
  1801. var h_i = Math.floor(h * 6);
  1802. var f = h * 6 - h_i;
  1803. var p = v * (1 - s);
  1804. var q = v * (1 - f * s);
  1805. var t = v * (1 - (1 - f) * s);
  1806. var r = 256;
  1807. var g = 256;
  1808. var b = 256;
  1809. switch (h_i)
  1810. {
  1811. case 0:
  1812. r = v;
  1813. g = t;
  1814. b = p;
  1815. break;
  1816. case 1:
  1817. r = q;
  1818. g = v;
  1819. b = p;
  1820. break;
  1821. case 2:
  1822. r = p;
  1823. g = v;
  1824. b = t;
  1825. break;
  1826. case 3:
  1827. r = p;
  1828. g = q;
  1829. b = v;
  1830. break;
  1831. case 4:
  1832. r = t;
  1833. g = p;
  1834. b = v;
  1835. break;
  1836. case 5:
  1837. r = v;
  1838. g = p;
  1839. b = q;
  1840. break;
  1841. }
  1842. return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
  1843. }
  1844.  
  1845. function HSVtoHex(hsv)
  1846. {
  1847. function componentToHex(c)
  1848. {
  1849. var hex = c.toString(16);
  1850. return hex.length == 1 ? '0' + hex : hex;
  1851. }
  1852. var rgb = HSVtoRGB(hsv);
  1853. return '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
  1854. }
  1855.  
  1856. function setFormat(hsv, options)
  1857. {
  1858. switch (options.format)
  1859. {
  1860. case 'hsvArray':
  1861. return hsv;
  1862. case 'hslArray':
  1863. return HSVtoHSL(hsv);
  1864. case 'hsl':
  1865. var hsl = HSVtoHSL(hsv);
  1866. return 'hsl(' + hsl[0] + ', ' + hsl[1] + '%, ' + hsl[2] + '%)';
  1867. case 'hsla':
  1868. var hslColor = HSVtoHSL(hsv);
  1869. var alpha = options.alpha || Math.random();
  1870. return 'hsla(' + hslColor[0] + ', ' + hslColor[1] + '%, ' + hslColor[2] + '%, ' + alpha + ')';
  1871. case 'rgbArray':
  1872. return HSVtoRGB(hsv);
  1873. case 'rgb':
  1874. var rgb = HSVtoRGB(hsv);
  1875. return 'rgb(' + rgb.join(', ') + ')';
  1876. case 'rgba':
  1877. var rgbColor = HSVtoRGB(hsv);
  1878. var alpha = options.alpha || Math.random();
  1879. return 'rgba(' + rgbColor.join(', ') + ', ' + alpha + ')';
  1880. case 'hex':
  1881. default:
  1882. return HSVtoHex(hsv);
  1883. }
  1884. }
  1885.  
  1886. function generateColor(options)
  1887. {
  1888. // pick a hue (H)
  1889. var H = pickHue(options);
  1890. // use H to determine saturation (S)
  1891. var S = pickSaturation(H, options);
  1892. // use S and H to determine brightness (B)
  1893. var B = pickBrightness(H, S, options);
  1894. // return the HSB color in the desired format
  1895. return setFormat([H, S, B], options);
  1896. }
  1897.  
  1898. function getRandom(options)
  1899. {
  1900. options = options ||
  1901. {};
  1902. seed = options.seed == null ? null : options.seed;
  1903. // check if we need to generate multiple colors
  1904. if (options.count !== null && options.count !== undefined)
  1905. {
  1906. var totalColors = options.count;
  1907. var colors = [];
  1908. options.count = null;
  1909. while (totalColors > colors.length)
  1910. {
  1911. // Since we're generating multiple colors, the seed has to be incrememented.
  1912. // Otherwise we'd just generate the same color each time...
  1913. if (seed !== null)
  1914. {
  1915. seed += 1;
  1916. }
  1917. colors.push(generateColor(options));
  1918. }
  1919. options.count = totalColors;
  1920. return colors;
  1921. }
  1922. return generateColor(options);
  1923. }
  1924. colorGenerator.getRandom = getRandom;
  1925. var ColorInterval = (function ()
  1926. {
  1927. function ColorInterval(start, end)
  1928. {
  1929. this.start = start;
  1930. this.end = end;
  1931. this.left = null;
  1932. this.right = null;
  1933. this.value = null;
  1934. }
  1935. ColorInterval.prototype.getNextValue = function ()
  1936. {
  1937. if (this.value == null)
  1938. {
  1939. this.value = (this.start + this.end) / 2;
  1940. return this.value;
  1941. }
  1942. if (this.left == null)
  1943. {
  1944. this.left = new ColorInterval(this.start, this.value);
  1945. return this.left.getNextValue();
  1946. }
  1947. if (this.right == null)
  1948. {
  1949. this.right = new ColorInterval(this.value, this.end);
  1950. return this.right.getNextValue();
  1951. }
  1952. if (this.left.getHeight() <= this.right.getHeight())
  1953. {
  1954. return this.left.getNextValue();
  1955. }
  1956. else
  1957. {
  1958. return this.right.getNextValue();
  1959. }
  1960. };
  1961. ColorInterval.prototype.getHeight = function ()
  1962. {
  1963. return 1
  1964. + (this.left == null ? 0 : this.left.getHeight())
  1965. + (this.right == null ? 0 : this.right.getHeight());
  1966. };
  1967. return ColorInterval;
  1968. }());
  1969. var rootInterval = new ColorInterval(0, 360);
  1970.  
  1971. function getEquallyDistributed()
  1972. {
  1973. return 'hsl(' + rootInterval.getNextValue() + ', 100%, 80%)';
  1974. }
  1975. colorGenerator.getEquallyDistributed = getEquallyDistributed;
  1976. // populate the color dictionary
  1977. loadColorBounds();
  1978. })(colorGenerator || (colorGenerator = {}));
  1979.  
  1980. /**
  1981. * notifications
  1982. */
  1983. var notifications;
  1984. (function (notifications)
  1985. {
  1986. notifications.name = 'notifications';
  1987.  
  1988. function event(title, options)
  1989. {
  1990. if ((!options || options.whenActive !== true)
  1991. && !document.hidden && document.hasFocus()
  1992. && settings.getSub(settings.KEY.showNotifications, 'showType') !== 1)
  1993. {
  1994. return;
  1995. }
  1996. if (!settings.get(settings.KEY.showNotifications))
  1997. {
  1998. // notifications disabled: return stub notification
  1999. return Promise.resolve(
  2000. {
  2001. close: function () {}
  2002. });
  2003. }
  2004. if (!("Notification" in window))
  2005. {
  2006. return Promise.reject('Your browser does not support notifications.');
  2007. }
  2008. return Notification.requestPermission()
  2009. .then(function (permission)
  2010. {
  2011. if (permission === 'granted')
  2012. {
  2013. var n_1 = new Notification(title, options);
  2014. n_1.onclick = function (event)
  2015. {
  2016. if (options && options.autoFocus !== false)
  2017. {
  2018. window.focus();
  2019. }
  2020. if (options && options.autoClose !== false)
  2021. {
  2022. n_1.close();
  2023. }
  2024. if (options && options.onclick)
  2025. {
  2026. options.onclick(n_1, event);
  2027. }
  2028. };
  2029. return Promise.resolve(n_1);
  2030. }
  2031. else
  2032. {
  2033. return Promise.reject('Notification permission denied');
  2034. }
  2035. });
  2036. }
  2037. notifications.event = event;
  2038.  
  2039. function requestPermission()
  2040. {
  2041. if (settings.get(settings.KEY.showNotifications))
  2042. {
  2043. Notification.requestPermission();
  2044. }
  2045. }
  2046.  
  2047. function init()
  2048. {
  2049. requestPermission();
  2050. settings.observe(settings.KEY.showNotifications, function ()
  2051. {
  2052. return requestPermission();
  2053. });
  2054. }
  2055. notifications.init = init;
  2056. })(notifications || (notifications = {}));
  2057.  
  2058. /**
  2059. * process commands
  2060. */
  2061. var commands;
  2062. (function (commands)
  2063. {
  2064. var XP_GAIN_KEY = 'xpGain';
  2065. var MAX_XP_GAIN_HISTORY_LENGTH = 100;
  2066. var IMAGE2SKILL = {
  2067. // mining = #cc0000
  2068. 'icons/pickaxe': 'mining'
  2069. // crafting = #cc0000
  2070. , 'icons/anvil': 'crafting'
  2071. // woodcutting = cyan
  2072. , 'icons/woodcutting': 'woodcutting'
  2073. // farming = green
  2074. , 'icons/watering-can': 'farming'
  2075. // brewing = #800080
  2076. , 'vialOfWater': 'brewing'
  2077. , 'largeVialOfWater': 'brewing'
  2078. , 'hugeVialOfWater': 'brewing'
  2079. // combat = lime
  2080. , 'icons/combat': 'combat'
  2081. // magic = blue
  2082. , 'icons/wizardhat': 'magic'
  2083. // fishing = blue
  2084. , 'tuna': 'fishing'
  2085. // cooking = yellow
  2086. , 'icons/cooking': 'cooking'
  2087. };
  2088. var xpGainHistory = store.has(XP_GAIN_KEY) ? store.get(XP_GAIN_KEY) :
  2089. {};
  2090. addStyle("\n.scroller.xp\n{\n\tfont-size: 18pt;\n\tposition: absolute;\n\ttext-align: center;\n}\n\t");
  2091.  
  2092. function fishingXp2Fish(xp)
  2093. {
  2094. for (var fish in FISH_XP)
  2095. {
  2096. if (FISH_XP[fish] == xp)
  2097. {
  2098. return fish;
  2099. }
  2100. }
  2101. return '';
  2102. }
  2103.  
  2104. function minutes2String(data)
  2105. {
  2106. return data.replace(/Your account has been running for: (\d+) minutes./, function (wholeMatch, minutes)
  2107. {
  2108. return 'Your account has been running for ' + format.min2Str(minutes) + '.';
  2109. });
  2110. }
  2111.  
  2112. function processLoot(data)
  2113. {
  2114. if (!/^SM=Your boat found nothing\.$|^SHOW_LOOT_DIAG=/.test(data))
  2115. {
  2116. return false;
  2117. }
  2118. var loot = {
  2119. type: 'loot'
  2120. , title: ''
  2121. , itemList: []
  2122. };
  2123. if (data.startsWith('SM='))
  2124. {
  2125. loot.title = 'Boat';
  2126. loot.emptyText = 'Your boat found nothing.';
  2127. }
  2128. else if (data.startsWith('SHOW_LOOT_DIAG='))
  2129. {
  2130. var split = data.substr('SHOW_LOOT_DIAG='.length).split('~');
  2131. loot.title = split[0];
  2132. for (var i = 1; i < split.length; i += 2)
  2133. {
  2134. loot.itemList.push(
  2135. {
  2136. key: split[i]
  2137. , amount: Number(split[i + 1])
  2138. });
  2139. }
  2140. }
  2141. log.add(loot);
  2142. return true;
  2143. }
  2144. var XP_GAIN_REGEX = /^ST=([^~]+)\.png~([^~]+)~\+(\d+)\s*xp$/;
  2145. var animationQueue = {};
  2146.  
  2147. function queueXpAnimation(skill, cell, color, xpAmount)
  2148. {
  2149. if (!settings.get(settings.KEY.newXpAnimation))
  2150. {
  2151. return;
  2152. }
  2153. animationQueue[skill] = animationQueue[skill] || [];
  2154. animationQueue[skill].push(
  2155. {
  2156. cell: cell
  2157. , color: color
  2158. , xpAmount: xpAmount
  2159. });
  2160. if (animationQueue[skill].length === 1)
  2161. {
  2162. nextAnimation(skill);
  2163. }
  2164. }
  2165.  
  2166. function nextAnimation(skill)
  2167. {
  2168. var entry = animationQueue[skill][0];
  2169. if (!entry || !settings.get(settings.KEY.newXpAnimation))
  2170. {
  2171. return;
  2172. }
  2173. var cell = entry.cell
  2174. , color = entry.color
  2175. , xpAmount = entry.xpAmount;
  2176. var rect = cell.getBoundingClientRect();
  2177. var $el = window.$("<div class=\"scroller xp\" style=\"color: " + color + "; left: " + (rect.left + 50) + "px; top: " + (document.body.scrollTop + rect.top) + "px; width: " + (rect.width - 2 * 20 - 50) + "px;\">+" + format.number(xpAmount) + "</div>")
  2178. .appendTo('body');
  2179. // ensure the existence of $el, so the complete-function can be called instantly if the window is hidden
  2180. $el
  2181. .animate(
  2182. {
  2183. top: '-=15px'
  2184. }
  2185. , {
  2186. duration: 1500
  2187. , easing: 'easeOutQuad'
  2188. , complete: function ()
  2189. {
  2190. animationQueue[skill].shift();
  2191. nextAnimation(skill);
  2192. }
  2193. })
  2194. .fadeOut(
  2195. {
  2196. duration: 2500
  2197. , queue: false
  2198. , complete: function ()
  2199. {
  2200. return $el.remove();
  2201. }
  2202. });
  2203. }
  2204.  
  2205. function processXpGain(data)
  2206. {
  2207. var match = data.match(XP_GAIN_REGEX);
  2208. if (!match)
  2209. {
  2210. return false;
  2211. }
  2212. var icon = match[1];
  2213. var skill = IMAGE2SKILL[icon] || '';
  2214. var color = match[2];
  2215. var xpAmount = Number(match[3]);
  2216. var cell = document.getElementById('top-bar-level-td-' + skill);
  2217. if (!cell)
  2218. {
  2219. console.debug('match (no cell found):', match);
  2220. return false;
  2221. }
  2222. var entry = {
  2223. time: now()
  2224. , amount: xpAmount
  2225. };
  2226. if (skill == 'fishing')
  2227. {
  2228. var caughtFish = fishingXp2Fish(xpAmount);
  2229. log.add(
  2230. {
  2231. type: 'fish'
  2232. , fish: caughtFish
  2233. });
  2234. }
  2235. // save the xp event
  2236. var list = xpGainHistory[skill] || [];
  2237. list.push(entry);
  2238. xpGainHistory[skill] = list.slice(-MAX_XP_GAIN_HISTORY_LENGTH);
  2239. store.set(XP_GAIN_KEY, xpGainHistory);
  2240. if (settings.get(settings.KEY.newXpAnimation))
  2241. {
  2242. queueXpAnimation(skill, cell, color, xpAmount);
  2243. }
  2244. return true;
  2245. }
  2246.  
  2247. function processLevelUp(data)
  2248. {
  2249. if (!data.startsWith('LVL_UP='))
  2250. {
  2251. return false;
  2252. }
  2253. var skill = data.substr('LVL_UP='.length);
  2254. var xp = getGameValue(skill + 'Xp');
  2255. var oldLvl = window.getLevel(xp);
  2256. log.add(
  2257. {
  2258. type: 'lvlup'
  2259. , skill: skill
  2260. , newLevel: oldLvl + 1
  2261. });
  2262. return true;
  2263. }
  2264.  
  2265. function processCombat(data)
  2266. {
  2267. var match = data.match(/^STHS=(.+)~(.+)~(\d+)~img-(.+)~(melee|heal)$/);
  2268. if (!match)
  2269. {
  2270. return false;
  2271. }
  2272. // keep track of different battles and add the data to the current battle
  2273. log.add(
  2274. {
  2275. type: 'combat'
  2276. , monsterId: window.fightMonsterId
  2277. , parts: [
  2278. {
  2279. type: match[5]
  2280. , who: match[4]
  2281. , number: Number(match[3])
  2282. }]
  2283. });
  2284. return true;
  2285. }
  2286.  
  2287. function processEnergy(data)
  2288. {
  2289. var match = data.match(/^ST=steak\.png~orange~\+([\d',]+)$/);
  2290. if (!match)
  2291. {
  2292. return false;
  2293. }
  2294. log.add(
  2295. {
  2296. type: 'energy'
  2297. , energy: Number(match[1].replace(/\D/g, ''))
  2298. });
  2299. return true;
  2300. }
  2301.  
  2302. function processHeat(data)
  2303. {
  2304. var match = data.match(/^ST=icons\/fire\.png~red~\+([\d',]+)$/);
  2305. if (!match)
  2306. {
  2307. return false;
  2308. }
  2309. log.add(
  2310. {
  2311. type: 'heat'
  2312. , heat: Number(match[1].replace(/\D/g, ''))
  2313. });
  2314. return true;
  2315. }
  2316. var RUNNING_ACCOUNT_STR = 'Your account has been running for:';
  2317.  
  2318. function formatData(data)
  2319. {
  2320. if (data.startsWith('STHS=')
  2321. || data.startsWith('STE=')
  2322. || data.startsWith('SM=')
  2323. || data.startsWith('ST=')
  2324. || data.startsWith('SHOW_LOOT_DIAG='))
  2325. {
  2326. if (data.indexOf(RUNNING_ACCOUNT_STR) != -1)
  2327. {
  2328. data = minutes2String(data);
  2329. }
  2330. data = format.numbersInText(data);
  2331. }
  2332. return data;
  2333. }
  2334. commands.formatData = formatData;
  2335.  
  2336. function process(data)
  2337. {
  2338. // prepare for logging events in an activity log
  2339. if (processLoot(data))
  2340. {
  2341. return;
  2342. }
  2343. else if (processXpGain(data))
  2344. {
  2345. // return undefined to let the original function be called
  2346. return settings.get(settings.KEY.newXpAnimation) ? null : void 0;
  2347. }
  2348. else if (processLevelUp(data))
  2349. {
  2350. return;
  2351. }
  2352. else if (processCombat(data))
  2353. {
  2354. return;
  2355. }
  2356. else if (processEnergy(data))
  2357. {
  2358. return;
  2359. }
  2360. else if (processHeat(data))
  2361. {
  2362. return;
  2363. }
  2364. else if (data.startsWith('SM='))
  2365. {}
  2366. else if (data.startsWith('STHS=') || data.startsWith('STE=') || data.startsWith('ST='))
  2367. {}
  2368. // notifications for this kind of message: "SM=An update has been scheduled for today."
  2369. if (data.startsWith('SM='))
  2370. {
  2371. if (settings.getSub(settings.KEY.showNotifications, 'serverMsg'))
  2372. {
  2373. var msg = data.substr(3)
  2374. .replace(/<br\s*\/?>/g, '\n')
  2375. .replace(/<img src='images\/(.+?)\.png'.+?\/?> (\d+)/g, function (wholeMatch, key, amount)
  2376. {
  2377. return format.number(amount) + ' ' + split2Words(key) + ', ';
  2378. })
  2379. .replace(/(\s)\1+/g, '$1')
  2380. .replace(/, $/, '')
  2381. .replace(/<.+?>/g, '');
  2382. notifications.event('Message from server'
  2383. , {
  2384. body: minutes2String(msg)
  2385. });
  2386. }
  2387. }
  2388. return;
  2389. }
  2390. commands.process = process;
  2391. })(commands || (commands = {}));
  2392.  
  2393. /**
  2394. * log activities and stuff
  2395. */
  2396. var log;
  2397. (function (log)
  2398. {
  2399. log.name = 'log';
  2400. var LOG_KEY = 'activityLog';
  2401. var MAX_LOG_SIZE = 100;
  2402. var logList = store.has(LOG_KEY) ? store.get(LOG_KEY) : [];
  2403. var currentCombat = null;
  2404. var currentCombatEl = null;
  2405. var logEl;
  2406.  
  2407. function createLi(entry)
  2408. {
  2409. var entryEl = document.createElement('li');
  2410. entryEl.dataset.time = (new Date(entry.time || 0)).toLocaleString();
  2411. entryEl.dataset.type = entry.type;
  2412. return entryEl;
  2413. }
  2414.  
  2415. function appendLi(entryEl)
  2416. {
  2417. if (logEl.firstChild)
  2418. {
  2419. logEl.insertBefore(entryEl, logEl.firstChild);
  2420. }
  2421. else
  2422. {
  2423. logEl.appendChild(entryEl);
  2424. }
  2425. }
  2426.  
  2427. function setGenericEntry(entry)
  2428. {
  2429. var el = createLi(entry);;
  2430. el.innerHTML = typeof entry.data === 'string' ? entry.data : JSON.stringify(entry.data);
  2431. appendLi(el);
  2432. }
  2433.  
  2434. function setLootEntry(entry)
  2435. {
  2436. var el = createLi(entry);
  2437. var header = document.createElement('h1');
  2438. header.className = 'container-title';
  2439. header.textContent = entry.title;
  2440. el.appendChild(header);
  2441. var itemContainer = document.createElement('span');
  2442. if (entry.itemList.length === 0)
  2443. {
  2444. itemContainer.innerHTML = "<span class=\"dialogue-loot\">" + entry.emptyText + "</span>";
  2445. }
  2446. else
  2447. {
  2448. for (var _i = 0, _a = entry.itemList; _i < _a.length; _i++)
  2449. {
  2450. var item = _a[_i];
  2451. var itemEl = document.createElement('span');
  2452. itemEl.className = 'dialogue-loot';
  2453. itemEl.innerHTML = "<img src=\"" + item.key + "\" class=\"image-icon-50\"> " + item.amount;
  2454. itemContainer.appendChild(itemEl);
  2455. itemContainer.appendChild(document.createTextNode(' '));
  2456. }
  2457. }
  2458. el.appendChild(itemContainer);
  2459. appendLi(el);
  2460. }
  2461.  
  2462. function setFishEntry(entry)
  2463. {
  2464. var el = createLi(entry);
  2465. el.innerHTML = "You caught a " + key2Name(entry.fish, true) + ".";
  2466. appendLi(el);
  2467. }
  2468.  
  2469. function setEnergyEntry(entry)
  2470. {
  2471. var el = createLi(entry);
  2472. el.innerHTML = "Your hero gained " + format.number(entry.energy) + " energy.";
  2473. appendLi(el);
  2474. }
  2475.  
  2476. function setHeatEntry(entry)
  2477. {
  2478. var el = createLi(entry);
  2479. el.innerHTML = "You added " + format.number(entry.heat) + " heat to your oven.";
  2480. appendLi(el);
  2481. }
  2482.  
  2483. function setLevelUpEntry(entry)
  2484. {
  2485. var el = createLi(entry);
  2486. el.innerHTML = "You advanced your " + entry.skill + "-skill to level " + entry.newLevel + ".";
  2487. appendLi(el);
  2488. }
  2489.  
  2490. function setCombatEntry(entry)
  2491. {
  2492. var created = currentCombatEl == null;
  2493. if (currentCombatEl == null)
  2494. {
  2495. currentCombatEl = createLi(entry);
  2496. }
  2497. var heroHeal = 0;
  2498. var heroDamage = 0;
  2499. var monsterHeal = 0;
  2500. var monsterDamage = 0;
  2501. for (var i = 0; i < entry.parts.length; i++)
  2502. {
  2503. var part = entry.parts[i];
  2504. if (part.who == 'hero')
  2505. {
  2506. if (part.type == 'heal')
  2507. {
  2508. heroHeal += part.number;
  2509. }
  2510. else
  2511. {
  2512. heroDamage += part.number;
  2513. }
  2514. }
  2515. else
  2516. {
  2517. if (part.type == 'heal')
  2518. {
  2519. monsterHeal += part.number;
  2520. }
  2521. else
  2522. {
  2523. monsterDamage += part.number;
  2524. }
  2525. }
  2526. }
  2527. // TODO: names for monster id
  2528. var monsterName = MONSTER_NAMES[entry.monsterId] || '(' + (entry.monsterId % 3 + 1) + ')';
  2529. var areaId = Math.floor(entry.monsterId / 3);
  2530. var areaName = AREA_NAMES[areaId] || '(' + (areaId + 1) + ')';
  2531. currentCombatEl.innerHTML = "<h2>Combat against " + monsterName + " in " + areaName + "</h2>\n\t\t<div>Hero: <span style=\"color: green;\">+" + heroHeal + "</span> <span style=\"color: red;\">-" + heroDamage + "</span></div>\n\t\t<div>Monster: <span style=\"color: green;\">+" + monsterHeal + "</span> <span style=\"color: red;\">-" + monsterDamage + "</span></div>";
  2532. if (created)
  2533. {
  2534. appendLi(currentCombatEl);
  2535. }
  2536. }
  2537. var entryType2Fn = {
  2538. 'loot': setLootEntry
  2539. , 'fish': setFishEntry
  2540. , 'energy': setEnergyEntry
  2541. , 'heat': setHeatEntry
  2542. , 'lvlup': setLevelUpEntry
  2543. , 'combat': setCombatEntry
  2544. };
  2545.  
  2546. function updateLog(entry)
  2547. {
  2548. if (!logEl)
  2549. {
  2550. return;
  2551. }
  2552. if (entry.type && entryType2Fn.hasOwnProperty(entry.type))
  2553. {
  2554. entryType2Fn[entry.type](entry);
  2555. }
  2556. else
  2557. {
  2558. setGenericEntry(entry);
  2559. }
  2560. }
  2561.  
  2562. function add(entry)
  2563. {
  2564. if (!entry.time)
  2565. {
  2566. entry.time = now();
  2567. }
  2568. if (entry.type == 'combat')
  2569. {
  2570. if (currentCombat == null)
  2571. {
  2572. currentCombat = entry;
  2573. }
  2574. else
  2575. {
  2576. (_a = currentCombat.parts).push.apply(_a, entry.parts);
  2577. }
  2578. entry = currentCombat;
  2579. }
  2580. logList.push(entry);
  2581. logList = logList.slice(-MAX_LOG_SIZE);
  2582. store.set(LOG_KEY, logList);
  2583. updateLog(entry);
  2584. var _a;
  2585. }
  2586. log.add = add;
  2587.  
  2588. function init()
  2589. {
  2590. addStyle("\n#show-activity-log\n{\n\tdisplay: none;\n}\nbody\n{\n\toverflow-y: scroll;\n}\n#activity-log-label\n{\n\tcolor: pink;\n\tcursor: pointer;\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n#activity-log-overlay\n{\n\tbackground-color: transparent;\n\tcolor: transparent;\n\tpointer-events: none;\n\tposition: fixed;\n\tbottom: 0;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\ttransition: background-color .3s ease-out;\n\tz-index: 1000;\n}\n#show-activity-log:checked ~ #activity-log-overlay\n{\n\tbackground-color: rgba(0, 0, 0, 0.4);\n\tpointer-events: all;\n}\n#activity-log\n{\n\tbackground-color: white;\n\tcolor: black;\n\tlist-style: none;\n\tmargin: 0;\n\toverflow-y: scroll;\n\tpadding: .4rem .8rem;\n\tposition: fixed;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\ttransform: translateX(100%);\n\ttransition: transform .3s ease-out;\n\tmin-width: 15rem;\n\twidth: 40%;\n\tmax-width: 30rem;\n\tz-index: 1000;\n}\n#show-activity-log:checked ~ #activity-log\n{\n\ttransform: translateX(0%);\n}\n#activity-log::before\n{\n\tcontent: 'Activity Log';\n\tdisplay: block;\n\tfont-size: 1rem;\n\tfont-weight: bold;\n\tmargin-bottom: 0.8rem;\n}\n#activity-log:empty::after\n{\n\tcontent: 'Activities will be listed here.';\n}\n#activity-log li\n{\n\tborder: 1px solid gray;\n\tborder-radius: .2rem;\n\tmargin: .2rem 0;\n\tpadding: .4rem .8rem;\n}\n#activity-log li::before\n{\n\tcolor: gray;\n\tcontent: attr(data-time);\n\tdisplay: block;\n\tfont-size: 0.8rem;\n\tmargin: -4px 0 4px -4px;\n}\n\t\t");
  2591. // add new tab "Activity Log"
  2592. var checkboxId = 'show-activity-log';
  2593. var activityLogLabel = document.createElement('label');
  2594. activityLogLabel.id = 'activity-log-label';
  2595. activityLogLabel.htmlFor = checkboxId;
  2596. activityLogLabel.textContent = 'Activity Log';
  2597. newTopbar.addTabEntry(activityLogLabel);
  2598. var checkbox = document.createElement('input');
  2599. checkbox.id = checkboxId;
  2600. checkbox.type = 'checkbox';
  2601. checkbox.style.display = 'none';
  2602. document.body.insertBefore(checkbox, document.body.firstChild);
  2603. var label = document.createElement('label');
  2604. label.id = 'activity-log-overlay';
  2605. label.htmlFor = checkboxId;
  2606. document.body.appendChild(label);
  2607. logEl = document.createElement('ul');
  2608. logEl.id = 'activity-log';
  2609. document.body.appendChild(logEl);
  2610. // add all stored elements
  2611. logList.forEach(function (e)
  2612. {
  2613. return updateLog(e);
  2614. });
  2615. observer.add('fightMonsterId', function (key, oldValue, newValue)
  2616. {
  2617. if (newValue === 0)
  2618. {
  2619. currentCombat = null;
  2620. currentCombatEl = null;
  2621. }
  2622. });
  2623. }
  2624. log.init = init;
  2625. })(log || (log = {}));
  2626.  
  2627. /**
  2628. * game events
  2629. */
  2630. var gameEvents;
  2631. (function (gameEvents)
  2632. {
  2633. gameEvents.name = 'gameEvents';
  2634. // min time difference between two notifications with the same title (10 seconds)
  2635. var MIN_TIME_DIFFERENCE = 10;
  2636. gameEvents.enabled = {
  2637. smelting: true
  2638. , chopping: true
  2639. , harvest: true
  2640. , boat: true
  2641. , battle: true
  2642. , brewing: true
  2643. , market: true
  2644. };
  2645. var lastTimestamp = new Map();
  2646.  
  2647. function notifyTabClickable(title, body, icon, tabKey, whenActive)
  2648. {
  2649. if (whenActive === void 0)
  2650. {
  2651. whenActive = false;
  2652. }
  2653. var now = (new Date).getTime();
  2654. var timeDiff = now - (lastTimestamp.get(title) || 0);
  2655. if (timeDiff < MIN_TIME_DIFFERENCE * 1e3)
  2656. {
  2657. return;
  2658. }
  2659. var promise = notifications.event(title
  2660. , {
  2661. body: body
  2662. , icon: 'images/' + icon
  2663. , whenActive: whenActive
  2664. , onclick: function ()
  2665. {
  2666. var tabNames = tabKey.split('.');
  2667. window.openTab(tabNames[0]);
  2668. if (tabNames.length > 1)
  2669. {
  2670. window.openSubTab(tabNames[1]);
  2671. }
  2672. }
  2673. });
  2674. if (promise)
  2675. {
  2676. lastTimestamp.set(title, now);
  2677. }
  2678. }
  2679.  
  2680. function observeTimer(k, fn)
  2681. {
  2682. observer.add(k, function (key, oldValue, newValue)
  2683. {
  2684. if (oldValue > 0 && newValue == 0)
  2685. {
  2686. fn(key, oldValue, newValue);
  2687. }
  2688. });
  2689. }
  2690.  
  2691. function smelting()
  2692. {
  2693. observeTimer('smeltingPercD', function (key, oldValue, newValue)
  2694. {
  2695. if (!gameEvents.enabled.smelting || !settings.getSub(settings.KEY.showNotifications, 'smelting'))
  2696. {
  2697. return;
  2698. }
  2699. notifyTabClickable('Hot topic', 'Your smelting has finished.', getFurnaceLevelName() + 'Furnace.png', 'crafting');
  2700. });
  2701. }
  2702.  
  2703. function chopping()
  2704. {
  2705. observer.add([
  2706. 'treeStage1'
  2707. , 'treeStage2'
  2708. , 'treeStage3'
  2709. , 'treeStage4'
  2710. , 'treeStage5'
  2711. , 'treeStage6'
  2712. ], function (key, oldValue, newValue)
  2713. {
  2714. if (!gameEvents.enabled.chopping || !settings.getSub(settings.KEY.showNotifications, 'chopping'))
  2715. {
  2716. return;
  2717. }
  2718. if (newValue == 4)
  2719. {
  2720. notifyTabClickable('Wood you be mine?', 'One or more of your trees are fully grown and can be chopped.', 'icons/woodcutting.png', 'woodcutting');
  2721. }
  2722. });
  2723. }
  2724.  
  2725. function harvest()
  2726. {
  2727. observer.add([
  2728. 'farmingPatchStage1'
  2729. , 'farmingPatchStage2'
  2730. , 'farmingPatchStage3'
  2731. , 'farmingPatchStage4'
  2732. , 'farmingPatchStage5'
  2733. , 'farmingPatchStage6'
  2734. ], function (key, oldValue, newValue)
  2735. {
  2736. if (!gameEvents.enabled.harvest || !settings.getSub(settings.KEY.showNotifications, 'harvest'))
  2737. {
  2738. return;
  2739. }
  2740. if (newValue == 4)
  2741. {
  2742. notifyTabClickable('Green thumb', 'One or more of your crops is ready for harvest.', 'icons/watering-can.png', 'farming');
  2743. }
  2744. else if (newValue > 4)
  2745. {
  2746. notifyTabClickable('I didn\'t plant this', 'One or more of your crops died.', 'icons/watering-can.png', 'farming');
  2747. }
  2748. });
  2749. }
  2750.  
  2751. function boat()
  2752. {
  2753. observeTimer([
  2754. 'rowBoatTimer'
  2755. , 'canoeTimer'
  2756. ], function (k, oldValue, newValue)
  2757. {
  2758. if (!gameEvents.enabled.boat || !settings.getSub(settings.KEY.showNotifications, 'boatReturned'))
  2759. {
  2760. return;
  2761. }
  2762. var key = k.replace(/Timer$/, '');
  2763. notifyTabClickable('Fishy business', 'Your ' + split2Words(key).toLowerCase() + ' returned from its trip.', key + '.png', 'combat');
  2764. });
  2765. }
  2766.  
  2767. function battle()
  2768. {
  2769. observeTimer('combatGlobalCooldown', function (key, oldValue, newValue)
  2770. {
  2771. if (!gameEvents.enabled.battle || !settings.getSub(settings.KEY.showNotifications, 'heroReady'))
  2772. {
  2773. return;
  2774. }
  2775. notifyTabClickable('Ready to work', 'Your hero is eager to fight.', 'icons/combat.png', 'combat');
  2776. });
  2777. }
  2778.  
  2779. function brewing()
  2780. {
  2781. observeTimer([
  2782. 'barPotionTimer'
  2783. , 'seedPotionTimer'
  2784. , 'stardustPotionTimer'
  2785. , 'superStardustPotionTimer'
  2786. ], function (key, oldValue, newValue)
  2787. {
  2788. if (!gameEvents.enabled.brewing || !settings.getSub(settings.KEY.showNotifications, 'potionEffect'))
  2789. {
  2790. return;
  2791. }
  2792. var potionKey = key.replace(/Timer$/, '');
  2793. if (getGameValue(potionKey) > 0)
  2794. {
  2795. notifyTabClickable('Cheers!', 'You can drink another ' + split2Words(potionKey) + '.', key.replace(/Timer$/, '') + '.png', 'brewing');
  2796. }
  2797. });
  2798. }
  2799.  
  2800. function market()
  2801. {
  2802. var _refreshMarketSlot = window.refreshMarketSlot;
  2803. window.refreshMarketSlot = function (offerId, itemName, amount, price, collectText, slotId, timeLeft)
  2804. {
  2805. if (gameEvents.enabled.market && settings.getSub(settings.KEY.showNotifications, 'itemsSold') && collectText > 0)
  2806. {
  2807. if (amount > 0)
  2808. {
  2809. notifyTabClickable('Ka-ching', 'You\'ve sold some ' + split2Words(itemName).toLowerCase() + ' to the market.', 'icons/shop.png', 'playermarket');
  2810. }
  2811. else
  2812. {
  2813. notifyTabClickable('Sold out', 'You\'ve sold all ' + split2Words(itemName).toLowerCase() + ' to the market.', 'icons/shop.png', 'playermarket');
  2814. }
  2815. }
  2816. _refreshMarketSlot(offerId, itemName, amount, price, collectText, slotId, timeLeft);
  2817. };
  2818. }
  2819.  
  2820. function gameValues()
  2821. {
  2822. observer.add('treasureMap', function (key, oldValue, newValue)
  2823. {
  2824. if (settings.getSub(settings.KEY.showNotifications, 'pirate') && oldValue > newValue)
  2825. {
  2826. notifyTabClickable('Arrrr!', 'Your pirate found a treasure map.', 'treasureMap.png', 'items');
  2827. }
  2828. });
  2829. observer.add('essence', function (key, oldValue, newValue)
  2830. {
  2831. if (oldValue < newValue)
  2832. {
  2833. var diff = newValue - oldValue;
  2834. var num = ['one', 'two', 'three'][diff - 1] || diff;
  2835. var text = 'You found ' + num + ' essence' + (diff > 1 ? 's' : '') + '.';
  2836. if (settings.get(settings.KEY.showEssencePopup))
  2837. {
  2838. window.confirmDialogue(400, text, 'Close', '', '');
  2839. }
  2840. if (settings.getSub(settings.KEY.showNotifications, 'essence'))
  2841. {
  2842. notifyTabClickable('Essence of Life', text, 'essence.png', 'combat.spells');
  2843. }
  2844. }
  2845. });
  2846. }
  2847.  
  2848. function init()
  2849. {
  2850. smelting();
  2851. chopping();
  2852. harvest();
  2853. boat();
  2854. battle();
  2855. brewing();
  2856. market();
  2857. gameValues();
  2858. }
  2859. gameEvents.init = init;
  2860. })(gameEvents || (gameEvents = {}));
  2861.  
  2862. /**
  2863. * hide crafting recipes of lower tiers or of maxed machines
  2864. */
  2865. var crafting;
  2866. (function (crafting)
  2867. {
  2868. crafting.name = 'crafting';
  2869. /**
  2870. * hide crafted recipes
  2871. */
  2872. function setRecipeVisibility(key, visible)
  2873. {
  2874. var recipeRow = document.getElementById('crafting-' + key);
  2875. if (recipeRow)
  2876. {
  2877. recipeRow.style.display = (!settings.get(settings.KEY.hideCraftingRecipes) || visible) ? '' : 'none';
  2878. }
  2879. }
  2880.  
  2881. function hideLeveledRecipes(max, getKey, init)
  2882. {
  2883. if (init === void 0)
  2884. {
  2885. init = false;
  2886. }
  2887. var keys2Observe = [];
  2888. var maxLevel = 0;
  2889. for (var i = max - 1; i >= 0; i--)
  2890. {
  2891. var level = i + 1;
  2892. var key = getKey(i);
  2893. var boundKey = getBoundKey(key);
  2894. keys2Observe.push(key);
  2895. keys2Observe.push(boundKey);
  2896. if (getGameValue(key) > 0 || getGameValue(boundKey) > 0)
  2897. {
  2898. maxLevel = Math.max(maxLevel, level);
  2899. }
  2900. setRecipeVisibility(key, level > maxLevel);
  2901. }
  2902. if (init)
  2903. {
  2904. observer.add(keys2Observe, function ()
  2905. {
  2906. return hideLeveledRecipes(max, getKey, false);
  2907. });
  2908. }
  2909. }
  2910.  
  2911. function hideToolRecipe(key, init)
  2912. {
  2913. if (init === void 0)
  2914. {
  2915. init = false;
  2916. }
  2917. var emptyKey = getTierKey(key, 0);
  2918. var keys2Observe = [emptyKey];
  2919. var hasTool = getGameValue(emptyKey) > 0;
  2920. for (var i = 0; i < TIER_LEVELS.length; i++)
  2921. {
  2922. var boundKey = getBoundKey(getTierKey(key, i));
  2923. hasTool = hasTool || getGameValue(boundKey) > 0;
  2924. keys2Observe.push(boundKey);
  2925. }
  2926. setRecipeVisibility(emptyKey, !hasTool);
  2927. if (init)
  2928. {
  2929. observer.add(keys2Observe, function ()
  2930. {
  2931. return hideToolRecipe(key, false);
  2932. });
  2933. }
  2934. }
  2935.  
  2936. function hideRecipe(key, hideInfo, init)
  2937. {
  2938. if (init === void 0)
  2939. {
  2940. init = false;
  2941. }
  2942. var maxValue = typeof hideInfo.max === 'function' ? hideInfo.max() : hideInfo.max;
  2943. var boundKey = getBoundKey(key);
  2944. var unbound = getGameValue(key);
  2945. var bound = getGameValue(boundKey);
  2946. var extra = (hideInfo.extraKeys || []).map(function (k)
  2947. {
  2948. return getGameValue(k);
  2949. }).reduce(function (p, c)
  2950. {
  2951. return p + c;
  2952. }, 0);
  2953. setRecipeVisibility(key, (bound + unbound + extra) < maxValue);
  2954. if (init)
  2955. {
  2956. observer.add([key, boundKey], function ()
  2957. {
  2958. return hideRecipe(key, hideInfo, false);
  2959. });
  2960. }
  2961. }
  2962. /**
  2963. * hide useless items
  2964. */
  2965. function setItemVisibility(key, visible)
  2966. {
  2967. var itemBox = document.getElementById('item-box-' + key);
  2968. if (itemBox)
  2969. {
  2970. itemBox.style.display = getGameValue(key) > 0 && (!settings.get(settings.KEY.hideUselessItems) || visible) ? '' : 'none';
  2971. }
  2972. }
  2973.  
  2974. function hideLeveledItems(max, getKey, init)
  2975. {
  2976. if (init === void 0)
  2977. {
  2978. init = false;
  2979. }
  2980. var keys2Observe = [];
  2981. var maxLevel = 0;
  2982. for (var i = max - 1; i >= 0; i--)
  2983. {
  2984. var level = i + 1;
  2985. var key = getKey(i);
  2986. var boundKey = getBoundKey(key);
  2987. keys2Observe.push(key);
  2988. keys2Observe.push(boundKey);
  2989. if (getGameValue(boundKey) > 0)
  2990. {
  2991. maxLevel = Math.max(maxLevel, level);
  2992. }
  2993. setItemVisibility(key, level > maxLevel);
  2994. }
  2995. if (init)
  2996. {
  2997. observer.add(keys2Observe, function ()
  2998. {
  2999. return hideLeveledItems(max, getKey, false);
  3000. });
  3001. }
  3002. }
  3003.  
  3004. function hideItem(key, hideInfo, init)
  3005. {
  3006. if (init === void 0)
  3007. {
  3008. init = false;
  3009. }
  3010. var maxValue = typeof hideInfo.max === 'function' ? hideInfo.max() : hideInfo.max;
  3011. var boundKey = getBoundKey(key);
  3012. var bound = getGameValue(boundKey);
  3013. var extra = (hideInfo.extraKeys || []).map(function (k)
  3014. {
  3015. return getGameValue(k);
  3016. }).reduce(function (p, c)
  3017. {
  3018. return p + c;
  3019. }, 0);
  3020. setItemVisibility(key, (bound + extra) < maxValue);
  3021. if (init)
  3022. {
  3023. observer.add([key, boundKey], function ()
  3024. {
  3025. return hideItem(key, hideInfo, false);
  3026. });
  3027. }
  3028. }
  3029.  
  3030. function init()
  3031. {
  3032. function processRecipes(init)
  3033. {
  3034. if (init === void 0)
  3035. {
  3036. init = false;
  3037. }
  3038. // furnace
  3039. hideLeveledRecipes(FURNACE_LEVELS.length, function (i)
  3040. {
  3041. return FURNACE_LEVELS[i] + 'Furnace';
  3042. }, init);
  3043. // oil storage
  3044. hideLeveledRecipes(OIL_STORAGE_SIZES.length, function (i)
  3045. {
  3046. return 'oilStorage' + (i + 1);
  3047. }, init);
  3048. // oven recipes
  3049. hideLeveledRecipes(OVEN_LEVELS.length, function (i)
  3050. {
  3051. return OVEN_LEVELS[i] + 'Oven';
  3052. }, init);
  3053. // tools
  3054. for (var _i = 0, TIER_ITEMS_1 = TIER_ITEMS; _i < TIER_ITEMS_1.length; _i++)
  3055. {
  3056. var tool = TIER_ITEMS_1[_i];
  3057. hideToolRecipe(tool, init);
  3058. }
  3059. // other stuff
  3060. for (var key in HIDE_RECIPES)
  3061. {
  3062. hideRecipe(key, HIDE_RECIPES[key], init);
  3063. }
  3064. if (init)
  3065. {
  3066. settings.observe(settings.KEY.hideCraftingRecipes, function ()
  3067. {
  3068. return processRecipes(false);
  3069. });
  3070. }
  3071. }
  3072. processRecipes(true);
  3073. var _processCraftingTab = window.processCraftingTab;
  3074. window.processCraftingTab = function ()
  3075. {
  3076. var reinit = !!window.refreshLoadCraftingTable;
  3077. _processCraftingTab();
  3078. if (reinit)
  3079. {
  3080. processRecipes(false);
  3081. }
  3082. };
  3083.  
  3084. function processItems(init)
  3085. {
  3086. if (init === void 0)
  3087. {
  3088. init = false;
  3089. }
  3090. // furnace
  3091. hideLeveledItems(FURNACE_LEVELS.length, function (i)
  3092. {
  3093. return FURNACE_LEVELS[i] + 'Furnace';
  3094. }, init);
  3095. // oil storage
  3096. hideLeveledItems(OIL_STORAGE_SIZES.length, function (i)
  3097. {
  3098. return 'oilStorage' + (i + 1);
  3099. }, init);
  3100. // oven recipes
  3101. hideLeveledItems(OVEN_LEVELS.length, function (i)
  3102. {
  3103. return OVEN_LEVELS[i] + 'Oven';
  3104. }, init);
  3105. // other stuff
  3106. for (var key in HIDE_RECIPES)
  3107. {
  3108. hideItem(key, HIDE_RECIPES[key], init);
  3109. }
  3110. if (init)
  3111. {
  3112. settings.observe(settings.KEY.hideUselessItems, function ()
  3113. {
  3114. return processItems(false);
  3115. });
  3116. }
  3117. }
  3118. processItems(true);
  3119. }
  3120. crafting.init = init;
  3121. })(crafting || (crafting = {}));
  3122.  
  3123. /**
  3124. * improve item boxes
  3125. */
  3126. var itemBoxes;
  3127. (function (itemBoxes)
  3128. {
  3129. itemBoxes.name = 'itemBoxes';
  3130.  
  3131. function hideNumberInItemBox(key, setVisibility)
  3132. {
  3133. if (setVisibility === void 0)
  3134. {
  3135. setVisibility = false;
  3136. }
  3137. var itemBox = document.getElementById('item-box-' + key);
  3138. if (!itemBox)
  3139. {
  3140. return;
  3141. }
  3142. var numberElement = itemBox.lastElementChild;
  3143. if (!numberElement)
  3144. {
  3145. return;
  3146. }
  3147. if (setVisibility)
  3148. {
  3149. numberElement.style.visibility = 'hidden';
  3150. }
  3151. else
  3152. {
  3153. numberElement.style.display = 'none';
  3154. }
  3155. }
  3156.  
  3157. function addSpan2ItemBox(key)
  3158. {
  3159. hideNumberInItemBox(key);
  3160. var itemBox = document.getElementById('item-box-' + key);
  3161. if (!itemBox)
  3162. {
  3163. return;
  3164. }
  3165. var span = document.createElement('span');
  3166. itemBox.appendChild(span);
  3167. return span;
  3168. }
  3169.  
  3170. function setOilPerSecond(span, oil)
  3171. {
  3172. span.innerHTML = "+ " + format.number(oil) + " L/s <img src=\"images/oil.png\" class=\"image-icon-20\" style=\"margin-top: -2px;\">";
  3173. }
  3174. // show capacity of furnace
  3175. function addFurnaceCaption()
  3176. {
  3177. for (var i = 0; i < FURNACE_LEVELS.length; i++)
  3178. {
  3179. var key = FURNACE_LEVELS[i] + 'Furnace';
  3180. var boundKey = getBoundKey(key);
  3181. var capacitySpan = addSpan2ItemBox(boundKey);
  3182. if (capacitySpan)
  3183. {
  3184. capacitySpan.className = 'capacity';
  3185. capacitySpan.textContent = 'Capacity: ' + format.number(window.getFurnaceCapacity(boundKey), true);
  3186. }
  3187. }
  3188. // charcoal foundry
  3189. var foundryCapacitySpan = addSpan2ItemBox('charcoalFoundry');
  3190. if (foundryCapacitySpan)
  3191. {
  3192. foundryCapacitySpan.className = 'capacity';
  3193. foundryCapacitySpan.textContent = 'Capacity: 100';
  3194. }
  3195. }
  3196. // show oil cap of oil storage
  3197. function addOilStorageCaption()
  3198. {
  3199. for (var i = 0; i < OIL_STORAGE_SIZES.length; i++)
  3200. {
  3201. var key = 'oilStorage' + (i + 1);
  3202. var capSpan = addSpan2ItemBox(getBoundKey(key));
  3203. if (capSpan)
  3204. {
  3205. capSpan.className = 'oil-cap';
  3206. capSpan.textContent = 'Oil cap: ' + format.number(OIL_STORAGE_SIZES[i], true);
  3207. }
  3208. }
  3209. }
  3210. // show oil per second
  3211. function addOilCaption()
  3212. {
  3213. var handheldOilSpan = addSpan2ItemBox('handheldOilPump');
  3214. if (handheldOilSpan)
  3215. {
  3216. setOilPerSecond(handheldOilSpan, 1 * window.miner);
  3217. observer.add('miner', function ()
  3218. {
  3219. return setOilPerSecond(handheldOilSpan, 1 * window.miner);
  3220. });
  3221. }
  3222. var oilPipeSpan = addSpan2ItemBox('boundOilPipe');
  3223. if (oilPipeSpan)
  3224. {
  3225. setOilPerSecond(oilPipeSpan, 50);
  3226. }
  3227. // add number of workers as caption to oil factory
  3228. var workerSpan = addSpan2ItemBox('boundOilFactory');
  3229. if (workerSpan)
  3230. {
  3231. var setCaption_1 = function ()
  3232. {
  3233. return workerSpan.textContent = 'Workers: ' + format.number(window.oilFactoryCheapWorkers, true);
  3234. };
  3235. setCaption_1();
  3236. observer.add('oilFactoryCheapWorkers', function ()
  3237. {
  3238. return setCaption_1();
  3239. });
  3240. }
  3241. }
  3242.  
  3243. function addWandCaption()
  3244. {
  3245. for (var i = 0; i < WAND_LEVELS.length; i++)
  3246. {
  3247. var level = WAND_LEVELS[i];
  3248. var key = level + 'Wand';
  3249. var wandSpan = addSpan2ItemBox(key);
  3250. if (wandSpan)
  3251. {
  3252. wandSpan.textContent = capitalize(level) + ' Wand';
  3253. }
  3254. }
  3255. }
  3256.  
  3257. function addVariousCaptions()
  3258. {
  3259. var key2Name = {
  3260. 'emptyAnvil': 'Anvil'
  3261. , 'tap': 'Tree Tap'
  3262. , 'farmer': 'Farmer'
  3263. , 'gardener': 'Gardener'
  3264. , 'planter': 'Planter'
  3265. , 'boundBrewingKit': 'Brewing Kit'
  3266. , 'cooksBook': 'Cooks Book'
  3267. , 'cooksPage': 'Cooks Page'
  3268. , 'combatDropTable': 'Loot Table'
  3269. , 'magicBook': 'Spell Book'
  3270. };
  3271. for (var key in key2Name)
  3272. {
  3273. var span = addSpan2ItemBox(key);
  3274. if (span)
  3275. {
  3276. span.textContent = key2Name[key];
  3277. }
  3278. }
  3279. }
  3280. // show current tier
  3281. function addTierCaption()
  3282. {
  3283. for (var _i = 0, TIER_ITEMS_2 = TIER_ITEMS; _i < TIER_ITEMS_2.length; _i++)
  3284. {
  3285. var tierItem = TIER_ITEMS_2[_i];
  3286. for (var i = 0; i < TIER_LEVELS.length; i++)
  3287. {
  3288. var key = getTierKey(tierItem, i);
  3289. var toolKey = tierItem == 'rake' ? key : getBoundKey(key);
  3290. var tierSpan = addSpan2ItemBox(toolKey);
  3291. if (tierSpan)
  3292. {
  3293. tierSpan.className = 'tier';
  3294. tierSpan.textContent = TIER_NAMES[i];
  3295. }
  3296. }
  3297. }
  3298. }
  3299. var boatKeys = ['rowBoat', 'canoe'];
  3300. var boatTimerKeys = boatKeys.map(function (k)
  3301. {
  3302. return k + 'Timer';
  3303. });
  3304.  
  3305. function checkBoat(span, timerKey, init)
  3306. {
  3307. if (init === void 0)
  3308. {
  3309. init = false;
  3310. }
  3311. var isInTransit = getGameValue(timerKey) > 0;
  3312. var otherInTransit = boatTimerKeys.some(function (k)
  3313. {
  3314. return k != timerKey && getGameValue(k) > 0;
  3315. });
  3316. span.textContent = isInTransit ? 'In transit' : 'Ready';
  3317. span.style.visibility = otherInTransit ? 'hidden' : '';
  3318. if (init)
  3319. {
  3320. observer.add(boatTimerKeys, function ()
  3321. {
  3322. return checkBoat(span, timerKey, false);
  3323. });
  3324. }
  3325. }
  3326. // show boat progress
  3327. function addBoatCaption()
  3328. {
  3329. for (var i = 0; i < boatKeys.length; i++)
  3330. {
  3331. var span = addSpan2ItemBox(getBoundKey(boatKeys[i]));
  3332. if (span)
  3333. {
  3334. checkBoat(span, boatTimerKeys[i], true);
  3335. }
  3336. }
  3337. }
  3338. // show bonemeal
  3339. function addBonemealCaption()
  3340. {
  3341. var noBonemealSpan = addSpan2ItemBox('boundBonemealBin');
  3342. if (!noBonemealSpan)
  3343. {
  3344. return;
  3345. }
  3346. noBonemealSpan.textContent = 'Bonemeal: 0';
  3347. var bonemealSpan = addSpan2ItemBox('boundFilledBonemealBin');
  3348. if (!bonemealSpan)
  3349. {
  3350. return;
  3351. }
  3352. bonemealSpan.dataset.itemDisplay = 'bonemeal';
  3353. bonemealSpan.textContent = format.number(window.bonemeal);
  3354. var captionSpan = document.createElement('span');
  3355. captionSpan.textContent = 'Bonemeal: ';
  3356. bonemealSpan.parentElement.insertBefore(captionSpan, bonemealSpan);
  3357. }
  3358.  
  3359. function warningBeforeSellingGems()
  3360. {
  3361. var _sellNPCItemDialogue = window.sellNPCItemDialogue;
  3362. window.sellNPCItemDialogue = function (item, amount)
  3363. {
  3364. if (item == 'sapphire' || item == 'emerald' || item == 'ruby' || item == 'diamond' || item == 'bloodDiamond')
  3365. {
  3366. var itemName = key2Name(amount == 1 ? item : item.replace(/y$/, 'ie') + 's', true);
  3367. if (amount == 0
  3368. || !window.confirm('Gems are precious and rare. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  3369. {
  3370. return;
  3371. }
  3372. }
  3373. else if (item == 'logs' || item == 'oakLogs' || item == 'willowLogs' || item == 'mapleLogs' || item == 'stardustLogs' || item == 'ancientLogs')
  3374. {
  3375. var itemName = key2Name(amount == 1 ? item.replace(/s$/, '') : item, true);
  3376. if (amount == 0
  3377. || !window.confirm('Logs are time consuming to collect. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  3378. {
  3379. return;
  3380. }
  3381. }
  3382. _sellNPCItemDialogue(item, amount);
  3383. };
  3384. }
  3385.  
  3386. function addWikiaLinks()
  3387. {
  3388. var WIKIA_CLASS = 'wikia-links';
  3389. addStyle("\n." + WIKIA_CLASS + " .item-box\n{\n\tposition: relative;\n}\n.item-box > .wikia-link\n{\n\tbackground: black url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30\" height=\"30\" viewBox=\"-2 -2 26 27\"><defs><linearGradient id=\"a\" x1=\"0%\" x2=\"63.85%\" y1=\"100%\" y2=\"32.54%\"><stop stop-color=\"#94D11F\" offset=\"0%\"/><stop stop-color=\"#09D3BF\" offset=\"100%\"/></linearGradient></defs><path fill=\"url(#a)\" fill-rule=\"evenodd\" d=\"M10.18 16.8c0 .2-.05.46-.26.67l-.8.7-7.38-6.95v-2.7l8.1 7.62c.12.12.33.36.33.66zm11.2-8.1v2.53l-9.15 8.86a.67.67 0 0 1-.5.2.73.73 0 0 1-.5-.2l-.85-.77 11-10.62zm-6.97 4.5l-2.53 2.43-8.04-7.67a2 2 0 0 1 0-2.9l2.53-2.43 8.04 7.67c.84.8.84 2.1 0 2.9zm-1.5-6.68L15.56 4c.4-.4.94-.6 1.52-.6.57 0 1.1.2 1.52.6l2.72 2.6-4.16 3.98-1.52-1.45-2.73-2.6zm10.18-.4l-6-5.8L17 .2l-.14.12-5.22 5.03L6.96.87l-.6-.48-.12-.1-.1.1-6.1 5.7-.04.06v5.76l.05.05 11.4 10.87.12.1.12-.1 11.37-10.87.05-.05V6.17l-.05-.05z\"/></svg>') no-repeat;\n\tdisplay: none;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\twidth: 30px;\n\theight: 30px;\n}\n." + WIKIA_CLASS + " .item-box:hover > .wikia-link\n{\n\tdisplay: block;\n}\n\t\t");
  3390.  
  3391. function setWikiaLinksVisibility(init)
  3392. {
  3393. if (init === void 0)
  3394. {
  3395. init = false;
  3396. }
  3397. var show = settings.get(settings.KEY.wikiaLinks);
  3398. document.body.classList[show ? 'add' : 'remove'](WIKIA_CLASS);
  3399. if (init)
  3400. {
  3401. settings.observe(settings.KEY.wikiaLinks, function ()
  3402. {
  3403. return setWikiaLinksVisibility();
  3404. });
  3405. }
  3406. }
  3407. setWikiaLinksVisibility(true);
  3408. var boxes = document.getElementsByClassName('item-box');
  3409.  
  3410. function disableClickPropagation(el)
  3411. {
  3412. el.addEventListener('click', function (event)
  3413. {
  3414. event.stopPropagation();
  3415. });
  3416. }
  3417. for (var i = 0; i < boxes.length; i++)
  3418. {
  3419. var box = boxes.item(i);
  3420. var key = box.id.replace(/^item-box-/, '');
  3421. var linkArea = document.createElement('a');
  3422. linkArea.className = 'wikia-link';
  3423. linkArea.href = getWikiaLink(key);
  3424. linkArea.target = '_blank';
  3425. disableClickPropagation(linkArea);
  3426. box.appendChild(linkArea);
  3427. var tooltipEl = ensureTooltip('wikiLink', linkArea);
  3428. if (tooltipEl.innerHTML === '')
  3429. {
  3430. tooltipEl.innerHTML = "Click to open the wikia page about this item.";
  3431. }
  3432. }
  3433. }
  3434.  
  3435. function init()
  3436. {
  3437. addFurnaceCaption();
  3438. addOilStorageCaption();
  3439. addOilCaption();
  3440. addWandCaption();
  3441. addVariousCaptions();
  3442. addTierCaption();
  3443. addBoatCaption();
  3444. addBonemealCaption();
  3445. warningBeforeSellingGems();
  3446. addWikiaLinks();
  3447. }
  3448. itemBoxes.init = init;
  3449. })(itemBoxes || (itemBoxes = {}));
  3450.  
  3451. /**
  3452. * add new chat
  3453. */
  3454. var chat;
  3455. (function (chat)
  3456. {
  3457. chat.name = 'chat';
  3458. // min time difference between repeated messages to not be considered as spam
  3459. var MIN_DIFF_REPEATED_MSG = 5e3;
  3460. var KEYWORD_LIST_KEY = 'keywordList';
  3461. var keywordList = store.get(KEYWORD_LIST_KEY) || [];
  3462. var CHAT_HISTORY_KEY = 'chatHistory';
  3463. var MAX_CHAT_HISTORY_LENGTH = 100;
  3464. var Type;
  3465. (function (Type)
  3466. {
  3467. Type[Type["reload"] = -1] = "reload";
  3468. Type[Type["normal"] = 0] = "normal";
  3469. Type[Type["pmReceived"] = 1] = "pmReceived";
  3470. Type[Type["pmSent"] = 2] = "pmSent";
  3471. Type[Type["serverMsg"] = 3] = "serverMsg";
  3472. })(Type || (Type = {}));;
  3473. var Tag;
  3474. (function (Tag)
  3475. {
  3476. Tag[Tag["none"] = 0] = "none";
  3477. Tag[Tag["donor"] = 1] = "donor";
  3478. Tag[Tag["contributor"] = 2] = "contributor";
  3479. Tag[Tag["mod"] = 3] = "mod";
  3480. Tag[Tag["dev"] = 4] = "dev";
  3481. Tag[Tag["server"] = 5] = "server";
  3482. })(Tag || (Tag = {}));;
  3483. /**
  3484. * The chunk hiding starts with at least 10 chunks.
  3485. * So there are at least
  3486. * (chunkHidingMinChunks-1) * msgChunkSize + 1 = 9 * 100 + 1 = 901
  3487. * messages before the chunk hiding mechanism starts.
  3488. */
  3489. var CHUNK_HIDING_MIN_CHUNKS = 10;
  3490. var MSG_CHUNK_SIZE = 100;
  3491. var RELOADED_CHAT_DATA = {
  3492. timestamp: 0
  3493. , username: ''
  3494. , userlevel: 0
  3495. , icon: 0
  3496. , tag: 0
  3497. , type: Type.reload
  3498. , msg: '[...]'
  3499. };
  3500. var CHAT_BOX_ID = 'div-chat';
  3501. var DEFAULT_CHAT_DIV_ID = 'div-chat-area';
  3502. var GENERAL_CHAT_DIV_ID = 'div-chat-general';
  3503. var PM_CHAT_TAB_PREFIX = 'tab-chat-pm-';
  3504. var PM_CHAT_DIV_PREFIX = 'div-chat-pm-';
  3505. var CHAT_TABS_ID = 'chat-tabs';
  3506. var CHAT_INPUT_ID = 'chat-input-text';
  3507. var CHAT_CLASS = 'div-chat-area';
  3508. var COLORIZE_CLASS = 'colorize';
  3509. var SpecialTab;
  3510. (function (SpecialTab)
  3511. {
  3512. SpecialTab[SpecialTab["default"] = 0] = "default";
  3513. SpecialTab[SpecialTab["general"] = 1] = "general";
  3514. SpecialTab[SpecialTab["filler"] = 2] = "filler";
  3515. })(SpecialTab || (SpecialTab = {}));;
  3516. var CHAT_SPECIAL_TAB_ID = (_a = {}
  3517. , _a[SpecialTab.default] = 'tab-chat-default'
  3518. , _a[SpecialTab.general] = 'tab-chat-general'
  3519. , _a[SpecialTab.filler] = 'tab-chat-filler'
  3520. , _a);
  3521. var CONTEXTMENU_ID = 'player-contextmenu';
  3522. var CHAT_ICONS = [
  3523. null
  3524. , {
  3525. key: 'halloween2015'
  3526. , title: 'Halloween 2015'
  3527. }
  3528. , {
  3529. key: 'christmas2015'
  3530. , title: 'Chirstmas 2015'
  3531. }
  3532. , {
  3533. key: 'easter2016'
  3534. , title: 'Holiday'
  3535. }
  3536. , {
  3537. key: 'halloween2016'
  3538. , title: 'Halloween 2016'
  3539. }
  3540. , {
  3541. key: 'christmas2016'
  3542. , title: 'Chirstmas 2016'
  3543. }
  3544. , {
  3545. key: 'dh1Max'
  3546. , title: 'Max Level in DH1'
  3547. }
  3548. , {
  3549. key: 'hardcore'
  3550. , title: 'Hardcore Account'
  3551. }
  3552. , {
  3553. key: 'quest'
  3554. , title: 'Questmaster'
  3555. }
  3556. ];
  3557. var CHAT_TAGS = [
  3558. null
  3559. , {
  3560. key: 'donor'
  3561. , name: ''
  3562. }
  3563. , {
  3564. key: 'contributor'
  3565. , name: 'Contributor'
  3566. }
  3567. , {
  3568. key: 'mod'
  3569. , name: 'Moderator'
  3570. }
  3571. , {
  3572. key: 'dev'
  3573. , name: 'Dev'
  3574. }
  3575. , {
  3576. key: 'yell'
  3577. , name: 'Server Message'
  3578. }
  3579. ];
  3580. var LOCALE = 'en-US';
  3581. var LOCALE_OPTIONS = {
  3582. hour12: false
  3583. , year: 'numeric'
  3584. , month: 'long'
  3585. , day: 'numeric'
  3586. , hour: '2-digit'
  3587. , minute: '2-digit'
  3588. , second: '2-digit'
  3589. };
  3590. // game commands
  3591. var COMMANDS = [
  3592. 'pm'
  3593. , 'mute'
  3594. , 'clear'
  3595. , 'ipmute'
  3596. ];
  3597. var CLEAR_CMD = 'clear';
  3598. var TUTORIAL_CMD = 'tutorial';
  3599. var KEYWORD_ADD_CMD = 'keyword add';
  3600. var KEYWORD_REMOVE_CMD = 'keyword remove';
  3601. // load chat history
  3602. var chatHistory = store.get(CHAT_HISTORY_KEY) || [];
  3603. // store chat colors for each user
  3604. var user2Color;
  3605. var usedColors;
  3606. // reserve color for special messages (e.g. server messages): white
  3607. var reservedColors = ['#ffffff'];
  3608. // message chunks
  3609. var msgChunkMap = new Map();
  3610. // for adding elements at startup
  3611. var chatboxFragments = new Map();
  3612. var chatInitialized = false;
  3613. // find index of last message which is not a pm
  3614. var isLastMsgNotReload = false;
  3615. for (var i = chatHistory.length - 1; i >= 0; i--)
  3616. {
  3617. if (!isDataPM(chatHistory[i]))
  3618. {
  3619. isLastMsgNotReload = chatHistory[i].type != Type.reload;
  3620. break;
  3621. }
  3622. }
  3623. // insert a placeholder for a reloaded chat
  3624. if (isLastMsgNotReload)
  3625. {
  3626. RELOADED_CHAT_DATA.timestamp = (new Date()).getTime();
  3627. chatHistory.push(RELOADED_CHAT_DATA);
  3628. }
  3629.  
  3630. function isMuted(user)
  3631. {
  3632. // ATTENTION: this will filter all people who are named similar to one of the muted people!
  3633. return window.mutedPeople.some(function (name)
  3634. {
  3635. return user.indexOf(name) > -1;
  3636. });
  3637. // return window.mutedPeople.indexOf(user) !== -1;
  3638. }
  3639.  
  3640. function isSpam(data)
  3641. {
  3642. // allow all messages from contributors, mods, devs and all server messages
  3643. if (data.tag != Tag.none)
  3644. {
  3645. return false;
  3646. }
  3647. /**
  3648. * get last message of current user
  3649. */
  3650. var isThisPm = isDataPM(data);
  3651. var msgUsername = data.type === Type.pmSent ? window.username : data.username;
  3652. var historyIndex = chatHistory.indexOf(data);
  3653. if (historyIndex == -1)
  3654. {
  3655. historyIndex = chatHistory.length;
  3656. }
  3657. var lastData = null;
  3658. for (var i = historyIndex - 1; i >= 0 && (lastData === null); i--)
  3659. {
  3660. var dataBefore = chatHistory[i];
  3661. if (isThisPm === isDataPM(dataBefore))
  3662. {
  3663. var beforeUsername = dataBefore.type == Type.pmSent ? window.username : dataBefore.username;
  3664. if (beforeUsername === msgUsername)
  3665. {
  3666. lastData = dataBefore;
  3667. }
  3668. }
  3669. }
  3670. /**
  3671. * compare message and don't allow the same message twice
  3672. */
  3673. if (lastData
  3674. && lastData.msg === data.msg
  3675. && (data.timestamp - lastData.timestamp) < MIN_DIFF_REPEATED_MSG)
  3676. {
  3677. return true;
  3678. }
  3679. return false;
  3680. }
  3681.  
  3682. function handleScrolling(chatbox)
  3683. {
  3684. if (window.isAutoScrolling)
  3685. {
  3686. setTimeout(function ()
  3687. {
  3688. return chatbox.scrollTop = chatbox.scrollHeight;
  3689. });
  3690. }
  3691. }
  3692. // for chat messages which arrive before DOMContentLoaded and can not be displayed since the DOM isn't ready
  3693. function processChatData(username, iconString, tagString, msg, isPM)
  3694. {
  3695. var tag = parseInt(tagString, 10);
  3696. var userlevel = 0;
  3697. var type = Type.normal;
  3698. if (isPM == 1)
  3699. {
  3700. var match = msg.match(/^\s*\[(PM from|Sent to) ([A-Za-z0-9 ]+)\]: (.+?)\s*$/) || ['', '', username, msg];
  3701. type = match[1] == 'Sent to' ? Type.pmSent : Type.pmReceived;
  3702. username = match[2].replace(/_/g, ' ');
  3703. msg = match[3];
  3704. }
  3705. else if (tag == Tag.server)
  3706. {
  3707. type = Type.serverMsg;
  3708. }
  3709. else
  3710. {
  3711. var match = msg.match(/^\s*\((\d+)\): (.+?)\s*$/);
  3712. if (match)
  3713. {
  3714. userlevel = parseInt(match[1], 10);
  3715. msg = match[2];
  3716. }
  3717. else
  3718. {
  3719. userlevel = window.getGlobalLevel();
  3720. }
  3721. }
  3722. // unlinkify when using DH2QoL to store the plain message
  3723. if (window.addToChatBox.toString().includes('linkify(arguments[3])'))
  3724. {
  3725. msg = msg.replace(/<a href='([^']+)' target='_blank'>\1<\/a>/ig, '$1');
  3726. }
  3727. if (type == Type.pmSent)
  3728. {
  3729. // turn some critical characters into HTML entities
  3730. msg = msg.replace(/[<>]/g, function (char)
  3731. {
  3732. return '&#' + char.charCodeAt(0) + ';';
  3733. });
  3734. }
  3735. return {
  3736. timestamp: now()
  3737. , username: username
  3738. , userlevel: userlevel
  3739. , icon: parseInt(iconString, 10)
  3740. , tag: tag
  3741. , type: type
  3742. , msg: msg
  3743. };
  3744. }
  3745.  
  3746. function saveChatHistory()
  3747. {
  3748. store.set(CHAT_HISTORY_KEY, chatHistory);
  3749. }
  3750.  
  3751. function add2ChatHistory(data)
  3752. {
  3753. chatHistory.push(data);
  3754. chatHistory = chatHistory.slice(-MAX_CHAT_HISTORY_LENGTH);
  3755. saveChatHistory();
  3756. }
  3757.  
  3758. function username2Id(username)
  3759. {
  3760. return username.replace(/ /g, '_');
  3761. }
  3762.  
  3763. function getChatTab(username, specialTab)
  3764. {
  3765. var id = (specialTab != null)
  3766. ? CHAT_SPECIAL_TAB_ID[specialTab]
  3767. : PM_CHAT_TAB_PREFIX + username2Id(username);
  3768. var tab = document.getElementById(id);
  3769. if (!tab)
  3770. {
  3771. tab = document.createElement('div');
  3772. tab.className = 'chat-tab';
  3773. if (specialTab != null)
  3774. {
  3775. tab.classList.add(SpecialTab[specialTab]);
  3776. }
  3777. tab.id = id;
  3778. tab.dataset.username = username;
  3779. tab.dataset.new = '0';
  3780. if (username.length > 2)
  3781. {
  3782. tab.textContent = username;
  3783. // thanks /u/Spino-Prime for pointing out this was missing
  3784. var closeSpan = document.createElement('span');
  3785. closeSpan.className = 'close';
  3786. tab.appendChild(closeSpan);
  3787. }
  3788. var chatTabs = document.getElementById(CHAT_TABS_ID);
  3789. var filler = chatTabs.querySelector('.filler');
  3790. if (filler)
  3791. {
  3792. chatTabs.insertBefore(tab, filler);
  3793. }
  3794. else
  3795. {
  3796. chatTabs.appendChild(tab);
  3797. }
  3798. }
  3799. return tab;
  3800. }
  3801.  
  3802. function getChatPanel(username)
  3803. {
  3804. var id = username == '' ? GENERAL_CHAT_DIV_ID : PM_CHAT_DIV_PREFIX + username2Id(username);
  3805. var panel = document.getElementById(id);
  3806. if (!panel)
  3807. {
  3808. panel = document.createElement('div');
  3809. panel.setAttribute('disabled', 'disabled');
  3810. panel.id = id;
  3811. panel.className = CHAT_CLASS;
  3812. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  3813. var height = defaultChat.style.height;
  3814. panel.style.height = height;
  3815. var chatDiv = defaultChat.parentElement;
  3816. chatDiv.insertBefore(panel, defaultChat);
  3817. }
  3818. return panel;
  3819. }
  3820.  
  3821. function changeChatTab(oldTab, newTab)
  3822. {
  3823. if (oldTab)
  3824. {
  3825. oldTab.classList.remove('selected');
  3826. var oldChatPanel = void 0;
  3827. if (oldTab.classList.contains('default'))
  3828. {
  3829. oldChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  3830. }
  3831. else
  3832. {
  3833. oldChatPanel = getChatPanel(oldTab.dataset.username || '');
  3834. }
  3835. oldChatPanel.classList.remove('selected');
  3836. }
  3837. newTab.classList.add('selected');
  3838. newTab.dataset.new = '0';
  3839. var newChatPanel;
  3840. if (newTab.classList.contains('default'))
  3841. {
  3842. newChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  3843. }
  3844. else
  3845. {
  3846. newChatPanel = getChatPanel(newTab.dataset.username || '');
  3847. }
  3848. newChatPanel.classList.add('selected');
  3849. var toUsername = newTab.dataset.username;
  3850. var newTextPlaceholder = toUsername == '' ? window.username + ':' : 'PM to ' + toUsername + ':';
  3851. document.getElementById(CHAT_INPUT_ID).placeholder = newTextPlaceholder;
  3852. if (window.isAutoScrolling)
  3853. {
  3854. setTimeout(function ()
  3855. {
  3856. return newChatPanel.scrollTop = newChatPanel.scrollHeight;
  3857. });
  3858. }
  3859. }
  3860.  
  3861. function clearChat(username)
  3862. {
  3863. // delete pms stored for that user
  3864. var isPMChat = username != '';
  3865. for (var i = 0; i < chatHistory.length; i++)
  3866. {
  3867. var data = chatHistory[i];
  3868. if (isPMChat == isDataPM(data) && (!isPMChat || data.username == username))
  3869. {
  3870. chatHistory.splice(i, 1);
  3871. i--;
  3872. }
  3873. }
  3874. saveChatHistory();
  3875. // clear pm-chat panel
  3876. var panel = getChatPanel(username);
  3877. while (panel.children.length > 0)
  3878. {
  3879. panel.removeChild(panel.children[0]);
  3880. }
  3881. msgChunkMap.delete(username);
  3882. return panel;
  3883. }
  3884.  
  3885. function closeChatTab(username)
  3886. {
  3887. // clear pm-chat panel and remove message-history
  3888. clearChat(username);
  3889. // remove pm-tab (and change tab if necessary)
  3890. var selectedTab = getSelectedTab();
  3891. var tab2Close = getChatTab(username, null);
  3892. if (selectedTab.dataset.username == username)
  3893. {
  3894. var generalTab = getChatTab('', SpecialTab.general);
  3895. changeChatTab(tab2Close, generalTab);
  3896. }
  3897. var tabContainer = tab2Close.parentElement;
  3898. tabContainer.removeChild(tab2Close);
  3899. }
  3900.  
  3901. function isDataPM(data)
  3902. {
  3903. return data.type === Type.pmSent || data.type === Type.pmReceived;
  3904. }
  3905.  
  3906. function colorizeMsg(username)
  3907. {
  3908. if (username == '')
  3909. {
  3910. return null;
  3911. }
  3912. if (!user2Color.has(username))
  3913. {
  3914. var color = void 0;
  3915. do {
  3916. var colorizer = settings.getSub(settings.KEY.colorizeChat, 'colorizer');
  3917. if (colorizer == 1)
  3918. {
  3919. color = colorGenerator.getRandom(
  3920. {
  3921. luminosity: 'light'
  3922. });
  3923. }
  3924. else if (colorizer == 2)
  3925. {
  3926. color = colorGenerator.getRandom(
  3927. {
  3928. luminosity: 'dark'
  3929. });
  3930. }
  3931. else
  3932. {
  3933. color = colorGenerator.getEquallyDistributed();
  3934. }
  3935. } while (usedColors.has(color));
  3936. user2Color.set(username, color);
  3937. usedColors.add(color);
  3938. addStyle("\n#" + CHAT_BOX_ID + "." + COLORIZE_CLASS + " .chat-msg[data-username=\"" + username + "\"]\n{\n\tbackground-color: " + color + ";\n}\n\t\t\t", 'name-color');
  3939. }
  3940. return user2Color.get(username);
  3941. }
  3942.  
  3943. function createMessageSegment(data)
  3944. {
  3945. var isThisPm = isDataPM(data);
  3946. var msgUsername = data.type === Type.pmSent ? window.username : data.username;
  3947. var historyIndex = chatHistory.indexOf(data);
  3948. var isSameUser = null;
  3949. var isSameTime = null;
  3950. for (var i = historyIndex - 1; i >= 0 && (isSameUser === null || isSameTime === null); i--)
  3951. {
  3952. var dataBefore = chatHistory[i];
  3953. if (isThisPm === isDataPM(dataBefore))
  3954. {
  3955. if (isSameUser === null)
  3956. {
  3957. var beforeUsername = dataBefore.type == Type.pmSent ? window.username : dataBefore.username;
  3958. isSameUser = beforeUsername === msgUsername;
  3959. }
  3960. if (dataBefore.type != Type.reload)
  3961. {
  3962. isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(dataBefore.timestamp / 1000 / 60) === 0;
  3963. }
  3964. }
  3965. }
  3966. var d = new Date(data.timestamp);
  3967. var hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
  3968. var minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
  3969. var icon = CHAT_ICONS[data.icon] ||
  3970. {
  3971. key: ''
  3972. , title: ''
  3973. };
  3974. var tag = CHAT_TAGS[data.tag] ||
  3975. {
  3976. key: ''
  3977. , name: ''
  3978. };
  3979. var formattedMsg = data.msg
  3980. .replace(/<a href='(.+?)' target='_blank'>\1<\/a>/g, '$1')
  3981. .replace(/(https?:\/\/[^\s"<>]+)/g, '<a target="_blank" href="$1">$1</a>');
  3982. colorizeMsg(msgUsername);
  3983. var msgTitle = data.type == Type.reload ? 'Chat loaded on ' + d.toLocaleString(LOCALE, LOCALE_OPTIONS) : '';
  3984. var user = data.type === Type.serverMsg ? 'Server Message' : msgUsername;
  3985. var levelAppendix = data.type == Type.normal ? ' (' + data.userlevel + ')' : '';
  3986. var userTitle = data.tag != Tag.server ? tag.name : '';
  3987. return "<span class=\"chat-msg\" data-type=\"" + data.type + "\" data-tag=\"" + tag.key + "\" data-username=\"" + msgUsername + "\">"
  3988. + ("<span\n\t\t\t\tclass=\"timestamp\"\n\t\t\t\tdata-timestamp=\"" + data.timestamp + "\"\n\t\t\t\tdata-same-time=\"" + isSameTime + "\">" + hour + ":" + minute + "</span>")
  3989. + ("<span class=\"user\" data-name=\"" + msgUsername + "\" data-same-user=\"" + isSameUser + "\">")
  3990. + ("<span class=\"icon " + icon.key + "\" title=\"" + icon.title + "\"></span>")
  3991. + ("<span class=\"name chat-tag-" + tag.key + "\" title=\"" + userTitle + "\">" + user + levelAppendix + ":</span>")
  3992. + "</span>"
  3993. + ("<span class=\"msg\" title=\"" + msgTitle + "\">" + formattedMsg + "</span>")
  3994. + "</span>";
  3995. }
  3996.  
  3997. function add2Chat(data)
  3998. {
  3999. if (!chatInitialized)
  4000. {
  4001. return;
  4002. }
  4003. var isThisPm = isDataPM(data);
  4004. // don't mute pms (you can just ignore pm-tab if you like)
  4005. if (!isThisPm && isMuted(data.username))
  4006. {
  4007. return;
  4008. }
  4009. var userKey = isThisPm ? data.username : '';
  4010. var chatTab = getChatTab(userKey, isThisPm ? null : SpecialTab.general);
  4011. if (!chatTab.classList.contains('selected'))
  4012. {
  4013. chatTab.dataset.new = (parseInt(chatTab.dataset.new || '0', 10) + 1).toString();
  4014. }
  4015. if (isThisPm)
  4016. {
  4017. window.lastPMUser = data.username;
  4018. }
  4019. // username is 3-12 characters long
  4020. var chatbox = getChatPanel(userKey);
  4021. var msgChunk = msgChunkMap.get(userKey);
  4022. if (!msgChunk || msgChunk.children.length >= MSG_CHUNK_SIZE)
  4023. {
  4024. msgChunk = document.createElement('div');
  4025. msgChunk.className = 'msg-chunk';
  4026. msgChunkMap.set(userKey, msgChunk);
  4027. if (chatboxFragments != null)
  4028. {
  4029. if (!chatboxFragments.has(userKey))
  4030. {
  4031. chatboxFragments.set(userKey, document.createDocumentFragment());
  4032. }
  4033. chatboxFragments.get(userKey).appendChild(msgChunk);
  4034. }
  4035. else
  4036. {
  4037. chatbox.appendChild(msgChunk);
  4038. }
  4039. }
  4040. var tmp = document.createElement('templateWrapper');
  4041. tmp.innerHTML = createMessageSegment(data);
  4042. msgChunk.appendChild(tmp.children[0]);
  4043. handleScrolling(chatbox);
  4044. }
  4045.  
  4046. function applyChatStyle()
  4047. {
  4048. addStyle("\nspan.chat-msg\n{\n\tdisplay: flex;\n\tmin-height: 21px;\n\tpadding: 1px 0;\n}\n#" + CHAT_BOX_ID + ":not(." + COLORIZE_CLASS + ") span.chat-msg:nth-child(2n)\n{\n\tbackground-color: hsla(0, 0%, 90%, 1);\n}\n.chat-msg[data-type=\"" + Type.reload + "\"]\n{\n\tfont-size: 0.8rem;\n\tline-height: 1.2rem;\n}\n.chat-msg .timestamp\n{\n\tdisplay: none;\n}\n#" + CHAT_BOX_ID + ".showTimestamps .chat-msg:not([data-type=\"" + Type.reload + "\"]) .timestamp\n{\n\tcolor: hsla(0, 0%, 50%, 1);\n\tdisplay: inline-block;\n\tfont-size: .9rem;\n\tmargin: 0;\n\tmargin-right: 5px;\n\tposition: relative;\n\twidth: 2.5rem;\n}\n.chat-msg .timestamp[data-same-time=\"true\"]\n{\n\tcolor: hsla(0, 0%, 50%, .1);\n}\n.chat-msg:not([data-type=\"" + Type.reload + "\"]) .timestamp:hover::after\n{\n\tbackground-color: hsla(0, 0%, 12%, 1);\n\tborder-radius: .2rem;\n\tcontent: attr(data-fulltime);\n\tcolor: hsla(0, 0%, 100%, 1);\n\tline-height: 1.35rem;\n\tpadding: .4rem .8rem;\n\tpointer-events: none;\n\tposition: absolute;\n\tleft: 2.5rem;\n\ttop: -0.4rem;\n\ttext-align: center;\n\twhite-space: nowrap;\n}\n\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-type=\"" + Type.pmReceived + "\"] { color: purple; }\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-type=\"" + Type.pmSent + "\"] { color: purple; }\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-type=\"" + Type.serverMsg + "\"] { color: blue; }\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-tag=\"contributor\"] { color: green; }\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-tag=\"mod\"] { color: #669999; }\n#" + CHAT_BOX_ID + ".showTags .chat-msg[data-tag=\"dev\"] { color: #666600; }\n.chat-msg:not([data-type=\"" + Type.reload + "\"]) .user\n{\n\tflex: 0 0 132px;\n\tmargin-right: 5px;\n\twhite-space: nowrap;\n}\n#" + GENERAL_CHAT_DIV_ID + " .chat-msg:not([data-type=\"" + Type.reload + "\"]) .user\n{\n\tflex-basis: 182px;\n}\n#" + CHAT_BOX_ID + ".showIcons #" + GENERAL_CHAT_DIV_ID + " .chat-msg:not([data-type=\"" + Type.reload + "\"]) .user\n{\n\tpadding-left: 22px;\n}\n.chat-msg .user[data-same-user=\"true\"]:not([data-name=\"\"])\n{\n\tcursor: default;\n\topacity: 0;\n}\n\n.chat-msg .user .icon\n{\n\tdisplay: none;\n}\n#" + CHAT_BOX_ID + ".showIcons .chat-msg .user .icon\n{\n\tdisplay: inline-block;\n\tmargin-left: -22px;\n}\n.chat-msg .user .icon::before\n{\n\tbackground-size: 20px 20px;\n\tcontent: '';\n\tdisplay: inline-block;\n\tmargin-right: 2px;\n\twidth: 20px;\n\theight: 20px;\n\tvertical-align: middle;\n}\n.chat-msg .user .icon.halloween2015::before\t{ background-image: url('images/chat-icons/1.png'); }\n.chat-msg .user .icon.christmas2015::before\t{ background-image: url('images/chat-icons/2.png'); }\n.chat-msg .user .icon.easter2016::before\t{ background-image: url('images/chat-icons/3.png'); }\n.chat-msg .user .icon.halloween2016::before\t{ background-image: url('images/chat-icons/4.png'); }\n.chat-msg .user .icon.christmas2016::before\t{ background-image: url('images/chat-icons/5.png'); }\n.chat-msg .user .icon.dh1Max::before\t\t{ background-image: url('images/chat-icons/6.png'); }\n.chat-msg .user .icon.hardcore::before\t\t{ background-image: url('images/chat-icons/7.png'); }\n.chat-msg .user .icon.quest::before\t\t\t{ background-image: url('images/chat-icons/8.png'); }\n\n.chat-msg .user:not([data-same-user=\"true\"]) .name\n{\n\tcolor: rgba(0, 0, 0, 0.7);\n\tcursor: pointer;\n}\n.chat-msg .user .name.chat-tag-donor::before\n{\n\tbackground-image: url('images/chat-icons/donor.png');\n\tbackground-size: 20px 20px;\n\tcontent: '';\n\tdisplay: inline-block;\n\theight: 20px;\n\twidth: 20px;\n\tvertical-align: middle;\n}\n.chat-msg .user .name.chat-tag-yell\n{\n\tcursor: default;\n}\n#" + CHAT_BOX_ID + ".showTags .chat-msg .user .name.chat-tag-contributor,\n#" + CHAT_BOX_ID + ".showTags .chat-msg .user .name.chat-tag-mod,\n#" + CHAT_BOX_ID + ".showTags .chat-msg .user .name.chat-tag-dev,\n#" + CHAT_BOX_ID + ".showTags .chat-msg .user .name.chat-tag-yell\n{\n\tcolor: white;\n\tdisplay: inline-block;\n\tfont-size: 10pt;\n\tmargin-bottom: -1px;\n\tmargin-top: -1px;\n\tpadding-bottom: 2px;\n\ttext-align: center;\n\t/* 2px border, 10 padding */\n\twidth: calc(100% - 2*1px - 2*5px);\n}\n#" + CHAT_BOX_ID + ":not(.showTags) .chat-msg .user .name.chat-tag-contributor,\n#" + CHAT_BOX_ID + ":not(.showTags) .chat-msg .user .name.chat-tag-mod,\n#" + CHAT_BOX_ID + ":not(.showTags) .chat-msg .user .name.chat-tag-dev,\n#" + CHAT_BOX_ID + ":not(.showTags) .chat-msg .user .name.chat-tag-yell\n{\n\tbackground: initial;\n\tborder: inherit;\n\tfont-family: inherit;\n\tfont-size: inherit;\n\tpadding: initial;\n}\n\n.chat-msg[data-type=\"" + Type.reload + "\"] .user > *,\n.chat-msg[data-type=\"" + Type.pmReceived + "\"] .user > .icon,\n.chat-msg[data-type=\"" + Type.pmSent + "\"] .user > .icon\n{\n\tdisplay: none;\n}\n\n.chat-msg .msg\n{\n\tmin-width: 0;\n\toverflow: hidden;\n\tword-wrap: break-word;\n}\n\n#" + CHAT_BOX_ID + " ." + CHAT_CLASS + "\n{\n\twidth: 100%;\n\theight: 130px;\n\tdisplay: none;\n}\n#" + CHAT_BOX_ID + " ." + CHAT_CLASS + ".selected\n{\n\tdisplay: block;\n}\n#" + CHAT_TABS_ID + "\n{\n\tdisplay: flex;\n\tmargin: 10px -6px -6px;\n\tflex-wrap: wrap;\n}\n#" + CHAT_TABS_ID + " .chat-tab\n{\n\tbackground-color: gray;\n\tborder-top: 1px solid black;\n\tborder-right: 1px solid black;\n\tcursor: pointer;\n\tdisplay: inline-block;\n\tfont-weight: normal;\n\tpadding: 0.3rem .6rem;\n\tposition: relative;\n}\n#" + CHAT_TABS_ID + " .chat-tab.selected\n{\n\tbackground-color: transparent;\n\tborder-top-color: transparent;\n}\n#" + CHAT_TABS_ID + " .chat-tab.default\n{\n\tdisplay: none;\n}\n#" + CHAT_TABS_ID + " .chat-tab.filler\n{\n\tbackground-color: hsla(0, 0%, 90%, 1);\n\tborder-right: 0;\n\tbox-shadow: inset 5px 5px 5px -5px rgba(0, 0, 0, 0.5);\n\tcolor: transparent;\n\tcursor: default;\n\tflex-grow: 1;\n}\n#" + CHAT_TABS_ID + " .chat-tab::after\n{\n\tcolor: white;\n\tcontent: '(' attr(data-new) ')';\n\tfont-size: .9rem;\n\tfont-weight: bold;\n\tmargin-left: .4rem;\n}\n#" + CHAT_TABS_ID + " .chat-tab[data-new=\"0\"]::after\n{\n\tcolor: inherit;\n\tfont-weight: normal;\n}\n#" + CHAT_TABS_ID + " .chat-tab:not(.general).selected::after,\n#" + CHAT_TABS_ID + " .chat-tab:not(.general):hover::after\n{\n\tvisibility: hidden;\n}\n#" + CHAT_TABS_ID + " .chat-tab:not(.general).selected .close::after,\n#" + CHAT_TABS_ID + " .chat-tab:not(.general):hover .close::after\n{\n\tcontent: '\u00D7';\n\tfont-size: 1.5rem;\n\tposition: absolute;\n\ttop: 0;\n\tright: .6rem;\n\tbottom: 0;\n}\n\n#" + CONTEXTMENU_ID + "\n{\n\tbox-shadow: rgba(0, 0, 0, 0.8) 4px 4px 4px -2px;\n\tposition: fixed;\n}\n#" + CONTEXTMENU_ID + " .ui-widget-header\n{\n\tcursor: default;\n\tpadding: .25rem;\n}\n\t\t");
  4049. }
  4050.  
  4051. function initColorizer(init)
  4052. {
  4053. if (init === void 0)
  4054. {
  4055. init = false;
  4056. }
  4057. var usernameList = user2Color && Array.from(user2Color.keys()) || [];
  4058. user2Color = new Map();
  4059. usedColors = new Set();
  4060. for (var _i = 0, reservedColors_1 = reservedColors; _i < reservedColors_1.length; _i++)
  4061. {
  4062. var color = reservedColors_1[_i];
  4063. usedColors.add(color);
  4064. }
  4065. var colorStyle = getStyle('name-color');
  4066. colorStyle.innerHTML = '';
  4067. for (var _a = 0, usernameList_1 = usernameList; _a < usernameList_1.length; _a++)
  4068. {
  4069. var username = usernameList_1[_a];
  4070. colorizeMsg(username);
  4071. }
  4072. if (init)
  4073. {
  4074. settings.observeSub(settings.KEY.colorizeChat, 'colorizer', function ()
  4075. {
  4076. return initColorizer();
  4077. });
  4078. }
  4079. }
  4080.  
  4081. function addIntelligentScrolling()
  4082. {
  4083. // add checkbox instead of button for toggling auto scrolling
  4084. var btn = document.querySelector('input[value="Toggle Autoscroll"]');
  4085. var btnParent = btn.parentElement;
  4086. var checkboxId = 'chat-toggle-autoscroll';
  4087. // create checkbox
  4088. var toggleCheckbox = document.createElement('input');
  4089. toggleCheckbox.type = 'checkbox';
  4090. toggleCheckbox.id = checkboxId;
  4091. toggleCheckbox.checked = true;
  4092. // create label
  4093. var toggleLabel = document.createElement('label');
  4094. toggleLabel.htmlFor = checkboxId;
  4095. toggleLabel.textContent = 'Autoscroll';
  4096. btnParent.insertBefore(toggleCheckbox, btn);
  4097. btnParent.insertBefore(toggleLabel, btn);
  4098. btn.style.display = 'none';
  4099. var chatArea = document.getElementById(GENERAL_CHAT_DIV_ID);
  4100. var showScrollTextTimeout = null;
  4101.  
  4102. function setAutoScrolling(value, full)
  4103. {
  4104. if (full === void 0)
  4105. {
  4106. full = false;
  4107. }
  4108. if (window.isAutoScrolling != value)
  4109. {
  4110. toggleCheckbox.checked = value;
  4111. window.isAutoScrolling = value;
  4112. var icon_1 = 'none';
  4113. var color_1 = value ? 'lime' : 'red';
  4114. var text_1 = (value ? 'En' : 'Dis') + 'abled' + (full ? ' Autoscroll' : '');
  4115. if (full)
  4116. {
  4117. if (showScrollTextTimeout)
  4118. {
  4119. window.clearTimeout(showScrollTextTimeout);
  4120. }
  4121. showScrollTextTimeout = window.setTimeout(function ()
  4122. {
  4123. return window.scrollText(icon_1, color_1, text_1);
  4124. }, 300);
  4125. }
  4126. else
  4127. {
  4128. window.scrollText(icon_1, color_1, text_1);
  4129. }
  4130. return true;
  4131. }
  4132. return false;
  4133. }
  4134. toggleCheckbox.addEventListener('change', function ()
  4135. {
  4136. setAutoScrolling(this.checked);
  4137. if (this.checked && settings.get(settings.KEY.intelligentScrolling))
  4138. {
  4139. chatArea.scrollTop = chatArea.scrollHeight - chatArea.clientHeight;
  4140. }
  4141. });
  4142. var placeholderTemplate = document.createElement('div');
  4143. placeholderTemplate.className = 'placeholder';
  4144. var childStore = new WeakMap();
  4145.  
  4146. function scrollHugeChat()
  4147. {
  4148. // # of children
  4149. var chunkNum = chatArea.children.length;
  4150. // start chunk hiding at a specific amount of chunks
  4151. if (chunkNum < CHUNK_HIDING_MIN_CHUNKS)
  4152. {
  4153. return;
  4154. }
  4155. var visibleTop = chatArea.scrollTop;
  4156. var visibleBottom = visibleTop + chatArea.clientHeight;
  4157. var referenceTop = visibleTop - window.innerHeight;
  4158. var referenceBottom = visibleBottom + window.innerHeight;
  4159. var top = 0;
  4160. // never hide the last element since its size may change at any time when a new message gets appended
  4161. for (var i = 0; i < chunkNum - 1; i++)
  4162. {
  4163. var child = chatArea.children[i];
  4164. var height = child.clientHeight;
  4165. var bottom = top + height;
  4166. var isVisible = top >= referenceTop && top <= referenceBottom
  4167. || bottom >= referenceTop && bottom <= referenceBottom
  4168. || top < referenceTop && bottom > referenceBottom;
  4169. var isPlaceholder = child.classList.contains('placeholder');
  4170. if (!isVisible && !isPlaceholder)
  4171. {
  4172. var newPlaceholder = placeholderTemplate.cloneNode(false);
  4173. newPlaceholder.style.height = height + 'px';
  4174. chatArea.replaceChild(newPlaceholder, child);
  4175. childStore.set(newPlaceholder, child);
  4176. }
  4177. else if (isVisible && isPlaceholder)
  4178. {
  4179. var oldChild = childStore.get(child);
  4180. chatArea.replaceChild(oldChild, child);
  4181. childStore.delete(child);
  4182. }
  4183. top = bottom;
  4184. }
  4185. }
  4186. var delayedScrollStart = null;
  4187. var delayedScrollTimeout = null;
  4188. // does not consider pm tabs; may be changed in a future version?
  4189. chatArea.addEventListener('scroll', function ()
  4190. {
  4191. if (settings.get(settings.KEY.intelligentScrolling))
  4192. {
  4193. var scrolled2Bottom = (chatArea.scrollTop + chatArea.clientHeight) >= chatArea.scrollHeight;
  4194. setAutoScrolling(scrolled2Bottom, true);
  4195. }
  4196. var n = now();
  4197. if (delayedScrollStart == null)
  4198. {
  4199. delayedScrollStart = n;
  4200. }
  4201. if (delayedScrollStart + 300 > n)
  4202. {
  4203. if (delayedScrollTimeout)
  4204. {
  4205. window.clearTimeout(delayedScrollTimeout);
  4206. }
  4207. delayedScrollTimeout = window.setTimeout(function ()
  4208. {
  4209. delayedScrollStart = null;
  4210. delayedScrollTimeout = null;
  4211. scrollHugeChat();
  4212. }, 50);
  4213. }
  4214. });
  4215. }
  4216.  
  4217. function getSelectedTab()
  4218. {
  4219. return document.querySelector('#' + CHAT_TABS_ID + ' .chat-tab.selected');
  4220. }
  4221.  
  4222. function getSelectedTabUsername()
  4223. {
  4224. var selectedTab = getSelectedTab();
  4225. return selectedTab.dataset.username || '';
  4226. }
  4227.  
  4228. function clickChatTab(newTab)
  4229. {
  4230. var oldTab = getSelectedTab();
  4231. if (newTab == oldTab)
  4232. {
  4233. return;
  4234. }
  4235. changeChatTab(oldTab, newTab);
  4236. }
  4237.  
  4238. function clickCloseChatTab(tab)
  4239. {
  4240. var username = tab.dataset.username || '';
  4241. var chatPanel = getChatPanel(username);
  4242. if (chatPanel.children.length === 0
  4243. || confirm("Do you want to close the pm tab of \"" + username + "\"?"))
  4244. {
  4245. closeChatTab(username);
  4246. }
  4247. }
  4248.  
  4249. function checkSetting(init)
  4250. {
  4251. if (init === void 0)
  4252. {
  4253. init = false;
  4254. }
  4255. var enabled = settings.get(settings.KEY.useNewChat);
  4256. // dis-/enable chat tabs
  4257. var chatTabs = document.getElementById(CHAT_TABS_ID);
  4258. chatTabs.style.display = enabled ? '' : 'none';
  4259. // dis-/enable checkbox for intelligent scrolling
  4260. var intelScrollId = 'chat-toggle-intelligent-scroll';
  4261. var input = document.getElementById(intelScrollId);
  4262. if (input)
  4263. {
  4264. input.style.display = enabled ? '' : 'none';
  4265. }
  4266. var label = document.querySelector('label[for="' + intelScrollId + '"]');
  4267. if (label)
  4268. {
  4269. label.style.display = enabled ? '' : 'none';
  4270. }
  4271. // virtually click on a tab
  4272. var defaultTab = getChatTab('', SpecialTab.default);
  4273. var generalTab = getChatTab('', SpecialTab.general);
  4274. clickChatTab(enabled ? generalTab : defaultTab);
  4275. if (init)
  4276. {
  4277. settings.observe(settings.KEY.useNewChat, function ()
  4278. {
  4279. return checkSetting(false);
  4280. });
  4281. }
  4282. }
  4283.  
  4284. function addChatTabs()
  4285. {
  4286. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  4287. var chatTabs = document.createElement('div');
  4288. chatTabs.id = CHAT_TABS_ID;
  4289. chatTabs.addEventListener('click', function (event)
  4290. {
  4291. var newTab = event.target;
  4292. if (newTab.classList.contains('close'))
  4293. {
  4294. return clickCloseChatTab(newTab.parentElement);
  4295. }
  4296. if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler'))
  4297. {
  4298. return;
  4299. }
  4300. clickChatTab(newTab);
  4301. });
  4302. chatBoxArea.appendChild(chatTabs);
  4303. // default tab (for disabled new chat)
  4304. getChatTab('', SpecialTab.default);
  4305. // general server chat
  4306. var generalTab = getChatTab('', SpecialTab.general);
  4307. generalTab.textContent = 'Server';
  4308. getChatPanel('');
  4309. getChatTab('', SpecialTab.filler);
  4310. var _sendChat = window.sendChat;
  4311. window.sendChat = function (inputEl)
  4312. {
  4313. var msg = inputEl.value;
  4314. var selectedTab = document.querySelector('.chat-tab.selected');
  4315. if (selectedTab.dataset.username != '' && msg[0] != '/')
  4316. {
  4317. inputEl.value = '/pm ' + (selectedTab.dataset.username || '').replace(/ /g, '_') + ' ' + msg;
  4318. }
  4319. _sendChat(inputEl);
  4320. };
  4321. }
  4322.  
  4323. function switch2PmTab(username)
  4324. {
  4325. var newTab = getChatTab(username, null);
  4326. clickChatTab(newTab);
  4327. }
  4328.  
  4329. function notifyPm(data)
  4330. {
  4331. notifications.event('Message from "' + data.username + '"'
  4332. , {
  4333. body: data.msg
  4334. , onclick: function ()
  4335. {
  4336. return switch2PmTab(data.username);
  4337. }
  4338. , whenActive: getSelectedTab().dataset.username != data.username
  4339. });
  4340. }
  4341.  
  4342. function checkMentionAndKeywords(data)
  4343. {
  4344. var lowerMsg = data.msg.toLowerCase();
  4345. var usernameRegex = new RegExp('\\b' + window.username + '\\b', 'i');
  4346. if (settings.getSub(settings.KEY.showNotifications, 'mention') && usernameRegex.test(lowerMsg))
  4347. // if (lowerMsg.indexOf(window.username) > -1)
  4348. {
  4349. notifications.event('You\'ve been mentioned'
  4350. , {
  4351. body: data.msg
  4352. });
  4353. }
  4354. var match = [];
  4355. for (var _i = 0, keywordList_1 = keywordList; _i < keywordList_1.length; _i++)
  4356. {
  4357. var keyword = keywordList_1[_i];
  4358. var regex = new RegExp('\\b' + keyword + '\\b', 'i');
  4359. if (regex.test(lowerMsg))
  4360. // if (lowerMsg.indexOf(keyword) > -1)
  4361. {
  4362. match.push(keyword);
  4363. }
  4364. }
  4365. if (settings.getSub(settings.KEY.showNotifications, 'keyword') && match.length > 0)
  4366. {
  4367. notifications.event('Keyword: "' + match.join('", "') + '"'
  4368. , {
  4369. body: data.msg
  4370. });
  4371. }
  4372. }
  4373. var addToChatBox_ = null;
  4374.  
  4375. function newAddToChatBox(username, icon, tag, msg, isPM)
  4376. {
  4377. var data = processChatData(username, icon, tag, msg, isPM);
  4378. var isThisSpam = false;
  4379. if (isDataPM(data))
  4380. {
  4381. if (data.type == Type.pmSent)
  4382. {
  4383. switch2PmTab(data.username);
  4384. }
  4385. notifyPm(data);
  4386. }
  4387. else
  4388. {
  4389. isThisSpam = settings.get(settings.KEY.enableSpamDetection) && isSpam(data);
  4390. if (!isThisSpam)
  4391. {
  4392. // check mentioning and keywords only for non-pms
  4393. checkMentionAndKeywords(data);
  4394. }
  4395. }
  4396. if (isThisSpam)
  4397. {
  4398. console.info('detected spam:', data);
  4399. }
  4400. else
  4401. {
  4402. add2ChatHistory(data);
  4403. add2Chat(data);
  4404. }
  4405. var fn = addToChatBox_ == null ? window.addToChatBox : addToChatBox_;
  4406. fn(username, icon, tag, msg, isPM);
  4407. }
  4408. chat.newAddToChatBox = newAddToChatBox;
  4409.  
  4410. function openPmTab(username)
  4411. {
  4412. if (username == window.username || username == '')
  4413. {
  4414. return;
  4415. }
  4416. var userTab = getChatTab(username, null);
  4417. clickChatTab(userTab);
  4418. var input = document.getElementById(CHAT_INPUT_ID);
  4419. input.focus();
  4420. }
  4421.  
  4422. function newChat()
  4423. {
  4424. addChatTabs();
  4425. applyChatStyle();
  4426. initColorizer(true);
  4427. addToChatBox_ = window.addToChatBox;
  4428. window.addToChatBox = newAddToChatBox;
  4429. chatInitialized = true;
  4430. var chatbox = document.getElementById(CHAT_BOX_ID);
  4431. chatbox.addEventListener('click', function (event)
  4432. {
  4433. var target = event.target;
  4434. var userEl = target && target.parentElement;
  4435. if (!target || !userEl || !target.classList.contains('name') || !userEl.classList.contains('user'))
  4436. {
  4437. return;
  4438. }
  4439. if (userEl.dataset.sameUser != 'true')
  4440. {
  4441. openPmTab(userEl.dataset.name || '');
  4442. }
  4443. });
  4444. chatbox.addEventListener('mouseover', function (event)
  4445. {
  4446. var target = event.target;
  4447. if (!target.classList.contains('timestamp') || !target.dataset.timestamp)
  4448. {
  4449. return;
  4450. }
  4451. var timestamp = parseInt(target.dataset.timestamp || '0', 10);
  4452. target.dataset.fulltime = (new Date(timestamp)).toLocaleDateString(LOCALE, LOCALE_OPTIONS);
  4453. target.dataset.timestamp = '';
  4454. });
  4455. // add context menu
  4456. var contextmenu = document.createElement('ul');
  4457. contextmenu.id = CONTEXTMENU_ID;
  4458. contextmenu.style.display = 'none';
  4459. contextmenu.innerHTML = "<li class=\"name ui-widget-header\"><div></div></li>\n\t\t<li class=\"open-pm\"><div>Open pm tab</div></li>\n\t\t<li class=\"stats\"><div>Open stats</div></li>\n\t\t<li class=\"mute\"><div>Mute</div></li>\n\t\t<li class=\"unmute\"><div>Unmute</div></li>";
  4460. document.body.appendChild(contextmenu);
  4461. window.$(contextmenu).menu(
  4462. {
  4463. items: '> :not(.ui-widget-header)'
  4464. });
  4465. var nameListEl = contextmenu.querySelector('.name');
  4466. var nameDivEl = nameListEl.firstElementChild;
  4467. var muteEl = contextmenu.querySelector('.mute');
  4468. var unmuteEl = contextmenu.querySelector('.unmute');
  4469. chatbox.addEventListener('contextmenu', function (event)
  4470. {
  4471. var target = event.target;
  4472. var userEl = target && target.parentElement;
  4473. if (!userEl || !userEl.classList.contains('user'))
  4474. {
  4475. return;
  4476. }
  4477. var username = userEl.dataset.name;
  4478. // ignore clicks on server messages or other special messages
  4479. if (!username || userEl.dataset.sameUser == 'true')
  4480. {
  4481. return;
  4482. }
  4483. contextmenu.style.left = event.clientX + 'px';
  4484. contextmenu.style.top = event.clientY + 'px';
  4485. contextmenu.style.display = '';
  4486. contextmenu.dataset.username = username;
  4487. nameDivEl.textContent = username;
  4488. var isMuted = window.mutedPeople.indexOf(username) !== -1;
  4489. muteEl.style.display = isMuted ? 'none' : '';
  4490. unmuteEl.style.display = isMuted ? '' : 'none';
  4491. event.stopPropagation();
  4492. event.preventDefault();
  4493. });
  4494. // add click listener for context menu and stop propagation
  4495. contextmenu.addEventListener('click', function (event)
  4496. {
  4497. var target = event.target;
  4498. event.stopPropagation();
  4499. while (target && target.id != CONTEXTMENU_ID && target.tagName != 'LI')
  4500. {
  4501. target = target.parentElement;
  4502. }
  4503. if (!target || target.id == CONTEXTMENU_ID)
  4504. {
  4505. return;
  4506. }
  4507. var username = contextmenu.dataset.username || '';
  4508. if (target.classList.contains('open-pm'))
  4509. {
  4510. openPmTab(username);
  4511. }
  4512. else if (target.classList.contains('stats'))
  4513. {
  4514. window.lookup(username);
  4515. }
  4516. else if (target.classList.contains('mute'))
  4517. {
  4518. if (username == '')
  4519. {
  4520. return;
  4521. }
  4522. window.mutedPeople.push(username);
  4523. window.scrollText('none', 'lime', '<em>' + username + '</em> muted');
  4524. }
  4525. else if (target.classList.contains('unmute'))
  4526. {
  4527. if (username == '')
  4528. {
  4529. return;
  4530. }
  4531. var index = window.mutedPeople.indexOf(username);
  4532. if (index !== -1)
  4533. {
  4534. window.mutedPeople.splice(index, 1);
  4535. }
  4536. window.scrollText('none', 'red', '<em>' + username + '</em> unmuted');
  4537. }
  4538. else
  4539. {
  4540. return;
  4541. }
  4542. contextmenu.style.display = 'none';
  4543. });
  4544. // add click listener to hide context menu
  4545. document.addEventListener('click', function (event)
  4546. {
  4547. if (contextmenu.style.display != 'none')
  4548. {
  4549. contextmenu.style.display = 'none';
  4550. }
  4551. });
  4552. window.addEventListener('contextmenu', function (event)
  4553. {
  4554. if (contextmenu.style.display != 'none')
  4555. {
  4556. contextmenu.style.display = 'none';
  4557. }
  4558. });
  4559. // handle settings
  4560. var showSettings = [settings.KEY.showTimestamps, settings.KEY.showIcons, settings.KEY.showTags];
  4561.  
  4562. function setShowSetting(key)
  4563. {
  4564. var enabled = settings.get(key);
  4565. chatbox.classList[enabled ? 'add' : 'remove'](settings.KEY[key]);
  4566. }
  4567. for (var _i = 0, showSettings_1 = showSettings; _i < showSettings_1.length; _i++)
  4568. {
  4569. var key = showSettings_1[_i];
  4570. setShowSetting(key);
  4571. settings.observe(key, function (k)
  4572. {
  4573. return setShowSetting(k);
  4574. });
  4575. }
  4576. }
  4577.  
  4578. function addCommandSuggester()
  4579. {
  4580. var input = document.getElementById(CHAT_INPUT_ID);
  4581. input.addEventListener('keyup', function (event)
  4582. {
  4583. if (event.key == 'Backspace' || event.key == 'Delete' || event.key == 'Enter' || event.key == 'Tab'
  4584. || input.selectionStart != input.selectionEnd
  4585. || input.selectionStart != input.value.length
  4586. || !input.value.startsWith('/'))
  4587. {
  4588. return;
  4589. }
  4590. var value = input.value.substr(1);
  4591. for (var _i = 0, COMMANDS_1 = COMMANDS; _i < COMMANDS_1.length; _i++)
  4592. {
  4593. var cmd = COMMANDS_1[_i];
  4594. if (cmd.startsWith(value))
  4595. {
  4596. input.value = '/' + cmd;
  4597. input.selectionStart = 1 + value.length;
  4598. input.selectionEnd = input.value.length;
  4599. break;
  4600. }
  4601. }
  4602. });
  4603. }
  4604.  
  4605. function addOwnCommands()
  4606. {
  4607. COMMANDS.push(TUTORIAL_CMD);
  4608. COMMANDS.push(KEYWORD_ADD_CMD);
  4609. COMMANDS.push(KEYWORD_REMOVE_CMD);
  4610.  
  4611. function processOwnCommands(value)
  4612. {
  4613. if (!value.startsWith('/'))
  4614. {
  4615. return value;
  4616. }
  4617. var msgPrefix = '/';
  4618. var msg = value.substr(1);
  4619. if (msg.startsWith('pm'))
  4620. {
  4621. var split = msg.split(' ');
  4622. msgPrefix = '/' + split.slice(0, 2).join(' ') + ' ';
  4623. msg = split.slice(2).join(' ');
  4624. }
  4625. if (msg.startsWith(CLEAR_CMD))
  4626. {
  4627. // clear current chat (pm chat, or general chat)
  4628. var username = getSelectedTabUsername();
  4629. clearChat(username);
  4630. }
  4631. else if (msg.startsWith(TUTORIAL_CMD))
  4632. {
  4633. // thanks aguyd (https://greasyfork.org/forum/profile/aguyd) for the idea
  4634. var name_2 = msg.substr(TUTORIAL_CMD.length).trim();
  4635. msgPrefix = '';
  4636. msg = 'https://www.reddit.com/r/DiamondHunt/comments/5vrufh/diamond_hunt_2_starter_faq/';
  4637. if (name_2.length != 0)
  4638. {
  4639. // maybe add '@' before the name?
  4640. msg = name_2 + ', ' + msg;
  4641. }
  4642. }
  4643. else if (msg.startsWith(KEYWORD_ADD_CMD))
  4644. {
  4645. var keyword = msg.substr(KEYWORD_ADD_CMD.length).trim().toLowerCase();
  4646. if (keywordList.indexOf(keyword) == -1)
  4647. {
  4648. keywordList.push(keyword);
  4649. store.set(KEYWORD_LIST_KEY, keywordList);
  4650. }
  4651. window.scrollText('none', 'lime', 'Keyword added: <em>' + keyword + '</em>');
  4652. }
  4653. else if (msg.startsWith(KEYWORD_REMOVE_CMD))
  4654. {
  4655. var keyword = msg.substr(KEYWORD_REMOVE_CMD.length).trim().toLowerCase();
  4656. var index = keywordList.indexOf(keyword);
  4657. if (index != -1)
  4658. {
  4659. keywordList.splice(index, 1);
  4660. store.set(KEYWORD_LIST_KEY, keywordList);
  4661. }
  4662. window.scrollText('none', 'lime', 'Keyword removed: <em>' + keyword + '</em>');
  4663. }
  4664. return msgPrefix + msg;
  4665. }
  4666. var _sendChat = window.sendChat;
  4667. window.sendChat = function (inputEl)
  4668. {
  4669. inputEl.value = processOwnCommands(inputEl.value);
  4670. _sendChat(inputEl);
  4671. };
  4672. }
  4673.  
  4674. function checkColorize(init)
  4675. {
  4676. if (init === void 0)
  4677. {
  4678. init = false;
  4679. }
  4680. var chatDiv = document.getElementById(CHAT_BOX_ID);
  4681. chatDiv.classList[settings.get(settings.KEY.colorizeChat) ? 'add' : 'remove'](COLORIZE_CLASS);
  4682. if (init)
  4683. {
  4684. settings.observe(settings.KEY.colorizeChat, function ()
  4685. {
  4686. return checkColorize(false);
  4687. });
  4688. }
  4689. }
  4690.  
  4691. function init()
  4692. {
  4693. newChat();
  4694. addIntelligentScrolling();
  4695. addCommandSuggester();
  4696. addOwnCommands();
  4697. checkColorize(true);
  4698. checkSetting(true);
  4699. var _enlargeChat = window.enlargeChat;
  4700. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  4701.  
  4702. function setChatBoxHeight(height)
  4703. {
  4704. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  4705. defaultChat.style.height = height;
  4706. var generalChat = document.getElementById(GENERAL_CHAT_DIV_ID);
  4707. generalChat.style.height = height;
  4708. var chatDivs = chatBoxArea.querySelectorAll('div[id^="' + PM_CHAT_DIV_PREFIX + '"]');
  4709. for (var i = 0; i < chatDivs.length; i++)
  4710. {
  4711. chatDivs[i].style.height = height;
  4712. }
  4713. }
  4714. window.enlargeChat = function (enlargeB)
  4715. {
  4716. _enlargeChat(enlargeB);
  4717. var defaultChatDiv = document.getElementById(DEFAULT_CHAT_DIV_ID);
  4718. var height = defaultChatDiv.style.height;
  4719. store.set('chat.height', height);
  4720. setChatBoxHeight(height);
  4721. handleScrolling(defaultChatDiv);
  4722. };
  4723. setChatBoxHeight(store.get('chat.height'));
  4724. // TEMP >>> (due to a naming issue, migrate the data)
  4725. var oldChatHistoryKey = 'chatHistory2';
  4726. var oldChatHistory = store.get(oldChatHistoryKey);
  4727. if (oldChatHistory != null)
  4728. {
  4729. store.set(CHAT_HISTORY_KEY, oldChatHistory);
  4730. store.remove(oldChatHistoryKey);
  4731. }
  4732. // TEMP <<<
  4733. // add history to chat
  4734. chatHistory.forEach(function (d)
  4735. {
  4736. return add2Chat(d);
  4737. });
  4738. if (chatboxFragments)
  4739. {
  4740. chatboxFragments.forEach(function (fragment, key)
  4741. {
  4742. var chatbox = getChatPanel(key);
  4743. chatbox.appendChild(fragment);
  4744. });
  4745. chatboxFragments = null;
  4746. }
  4747. // reset the new counter for all tabs
  4748. var tabs = document.querySelectorAll('.chat-tab');
  4749. for (var i = 0; i < tabs.length; i++)
  4750. {
  4751. tabs[i].dataset.new = '0';
  4752. }
  4753. }
  4754. chat.init = init;
  4755. var _a;
  4756. })(chat || (chat = {}));
  4757.  
  4758. /**
  4759. * hopefully only temporary fixes
  4760. */
  4761. var temporaryFixes;
  4762. (function (temporaryFixes)
  4763. {
  4764. temporaryFixes.name = 'temporaryFixes';
  4765. // update spells being clickable in combat
  4766. function setSpellsClickable()
  4767. {
  4768. var spellbox = document.getElementById('fight-spellboox');
  4769. if (spellbox)
  4770. {
  4771. spellbox.style.pointerEvents = window.isInCombat() ? '' : 'none';
  4772. }
  4773. }
  4774. // warn before unloading/reloading the tab if combat is in progress
  4775. function combatWarnOnUnload()
  4776. {
  4777. if (!window.isInCombat())
  4778. {
  4779. window.onbeforeunload = null;
  4780. }
  4781. else
  4782. {
  4783. if (window.onbeforeunload == null)
  4784. {
  4785. window.onbeforeunload = function ()
  4786. {
  4787. return 'You are in a fight!';
  4788. };
  4789. }
  4790. }
  4791. }
  4792.  
  4793. function fixCombatCountdown()
  4794. {
  4795. var el = document.getElementById('combat-countdown');
  4796. if (!el)
  4797. {
  4798. return;
  4799. }
  4800. if (window.isInCombat())
  4801. {
  4802. el.style.display = '';
  4803. var visible = window.combatCommenceTimer != 0;
  4804. el.style.visibility = visible ? '' : 'hidden';
  4805. }
  4806. }
  4807. // fix exhaustion timer and updating brewing and cooking recipes
  4808. function fixExhaustionTimer()
  4809. {
  4810. if (document.getElementById('tab-container-combat').style.display != 'none')
  4811. {
  4812. window.combatNotFightingTick();
  4813. }
  4814. }
  4815.  
  4816. function fixClientGameLoop()
  4817. {
  4818. var _clientGameLoop = window.clientGameLoop;
  4819. window.clientGameLoop = function ()
  4820. {
  4821. _clientGameLoop();
  4822. setSpellsClickable();
  4823. combatWarnOnUnload();
  4824. fixCombatCountdown();
  4825. fixExhaustionTimer();
  4826. };
  4827. }
  4828. // fix elements of scrollText (e.g. when joining the game and receiving xp at that moment)
  4829. function fixScroller()
  4830. {
  4831. var textEls = document.querySelectorAll('div.scroller');
  4832. for (var i = 0; i < textEls.length; i++)
  4833. {
  4834. var scroller = textEls[i];
  4835. if (scroller.style.position != 'absolute')
  4836. {
  4837. scroller.style.display = 'none';
  4838. }
  4839. }
  4840. }
  4841. // fix style of tooltips
  4842. function fixTooltipStyle()
  4843. {
  4844. addStyle("\nbody > div.tooltip > h2:first-child\n{\n\tmargin-top: 0;\n\tfont-size: 20pt;\n\tfont-weight: normal;\n}\n\t\t");
  4845. }
  4846. // fix buiulding magic table dynamically
  4847. function fixRefreshingMagicRecipes()
  4848. {
  4849. window.refreshLoadMagicTable = true;
  4850. var _processMagicTab = window.processMagicTab;
  4851. window.processMagicTab = function ()
  4852. {
  4853. var _refreshLoadCraftingTable = window.refreshLoadCraftingTable;
  4854. window.refreshLoadCraftingTable = window.refreshLoadMagicTable;
  4855. _processMagicTab();
  4856. window.refreshLoadCraftingTable = _refreshLoadCraftingTable;
  4857. };
  4858. }
  4859. // move the strange leaf to brewing tab (thanks lasse_brus for this idea)
  4860. function moveStrangeLeafs()
  4861. {
  4862. var strangeLeafBox = document.getElementById('item-box-strangeLeaf');
  4863. var brewingContainer = document.getElementById('tab-sub-container-brewing');
  4864. brewingContainer.appendChild(strangeLeafBox);
  4865. // remove event listeners before binding the tooltip to it
  4866. var $strangeLeafBox = window.$(strangeLeafBox);
  4867. $strangeLeafBox.off('mouseover').off('mouseleave');
  4868. strangeLeafBox.title = '';
  4869. // bind tooltip to item box
  4870. ensureTooltip('ingredient-secondary', strangeLeafBox);
  4871. // change color
  4872. var color1 = '#800080';
  4873. var color2 = '#990099';
  4874. strangeLeafBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  4875. $strangeLeafBox
  4876. .mouseover(function ()
  4877. {
  4878. strangeLeafBox.style.background = 'none';
  4879. strangeLeafBox.style.backgroundColor = color2;
  4880. })
  4881. .mouseleave(function ()
  4882. {
  4883. strangeLeafBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  4884. });
  4885. }
  4886. // fix height of map item
  4887. function fixTreasureMap()
  4888. {
  4889. var mapBox = document.getElementById('item-box-treasureMap');
  4890. var numSpan = mapBox.lastElementChild;
  4891. numSpan.style.display = '';
  4892. numSpan.style.visibility = 'hidden';
  4893. }
  4894. // fix wobbling tree places on hover (in wood cutting)
  4895. function fixWoodcutting()
  4896. {
  4897. addStyle("\nimg.woodcutting-tree-img\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  4898. }
  4899. // fix wobbling quest rows on hover (in quest book)
  4900. function fixQuestBook()
  4901. {
  4902. addStyle("\n#table-quest-list tr\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  4903. }
  4904.  
  4905. function fixScrollImages()
  4906. {
  4907. function fixIcon(icon)
  4908. {
  4909. return icon + (icon != 'none' && !/\..{3,4}$/.test(icon) ? '.png' : '');
  4910. }
  4911. var _scrollTextHitSplat = window.scrollTextHitSplat;
  4912. window.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  4913. {
  4914. _scrollTextHitSplat(fixIcon(icon), color, text, elId, cbType);
  4915. };
  4916. var _scrollText = window.scrollText;
  4917. window.scrollText = function (icon, color, text)
  4918. {
  4919. _scrollText(fixIcon(icon), color, text);
  4920. };
  4921. }
  4922.  
  4923. function fixQuest8BraveryRecipe()
  4924. {
  4925. observer.add([
  4926. 'quest8'
  4927. , 'braveryPotion'
  4928. ], function ()
  4929. {
  4930. var show = window.quest8 > 0 && window.braveryPotion == 0;
  4931. var recipe = document.getElementById('brewing-braveryPotion');
  4932. if (recipe)
  4933. {
  4934. recipe.style.display = show ? '' : 'none';
  4935. }
  4936. });
  4937. }
  4938.  
  4939. function fixHitText()
  4940. {
  4941. window.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  4942. {
  4943. var imgTag = icon != 'none' ? "<img src=\"images/" + icon + "\" class=\"image-icon-50\" />" : '';
  4944. var elementChosen = document.getElementById(elId);
  4945. if (!elementChosen)
  4946. {
  4947. return;
  4948. }
  4949. var rect = elementChosen.getBoundingClientRect();
  4950. var xCoord = (rect.left + rect.right) / 2;
  4951. var yCoord = (rect.bottom + rect.top) / 2;
  4952. var extraStyle = '';
  4953. if (cbType == 'melee')
  4954. {
  4955. extraStyle = 'border: 1px solid red; background-color: #4d0000;';
  4956. }
  4957. else if (cbType == 'heal')
  4958. {
  4959. extraStyle = 'border: 1px solid green; background-color: lime;';
  4960. }
  4961. var $elementToAppend = window.$("<div class=\"scroller\" style=\"" + extraStyle + " color: " + color + "; position: fixed;\">" + imgTag + text + "</div>").appendTo('body');
  4962. if (xCoord == 0 && yCoord == 0)
  4963. {
  4964. var tab = document.getElementById('tab-container-bar-combat');
  4965. var tabRect = tab.getBoundingClientRect();
  4966. var boxRect = $elementToAppend.get(0).getBoundingClientRect();
  4967. xCoord = elId == 'img-hero' ? (tabRect.left - boxRect.width) : tabRect.right;
  4968. yCoord = tabRect.top;
  4969. }
  4970. $elementToAppend
  4971. .css(
  4972. {
  4973. left: xCoord
  4974. , top: yCoord
  4975. })
  4976. .animate(
  4977. {
  4978. top: '-=50px'
  4979. }, function ()
  4980. {
  4981. return $elementToAppend.fadeOut(1000, function ()
  4982. {
  4983. return $elementToAppend.remove();
  4984. });
  4985. });
  4986. };
  4987. }
  4988.  
  4989. function fixBoatTooltips()
  4990. {
  4991. var boatBox = document.getElementById('item-box-boundRowBoat');
  4992. var boatTooltip = boatBox && document.getElementById(boatBox.dataset.tooltipId || '');
  4993. var canoeBox = document.getElementById('item-box-boundCanoe');
  4994. if (!boatBox || !canoeBox || !boatTooltip)
  4995. {
  4996. return;
  4997. }
  4998. var canoeTooltip = boatTooltip.cloneNode(true);
  4999. canoeTooltip.id = 'tooltip-boundCanoe';
  5000. var header = canoeTooltip.firstElementChild;
  5001. header.textContent = 'Canoe';
  5002. boatTooltip.parentElement.appendChild(canoeTooltip);
  5003. canoeBox.dataset.tooltipId = 'tooltip-boundCanoe';
  5004. boatTooltip.appendChild(document.createElement('br'));
  5005. var boatDuration = document.createElement('span');
  5006. boatDuration.innerHTML = '<strong>Trip duration:</strong> 3 hours';
  5007. boatTooltip.appendChild(boatDuration);
  5008. canoeTooltip.appendChild(document.createElement('br'));
  5009. var canoeDuration = document.createElement('span');
  5010. canoeDuration.innerHTML = '<strong>Trip duration:</strong> 6 hours';
  5011. canoeTooltip.appendChild(canoeDuration);
  5012. }
  5013.  
  5014. function fixAlignments()
  5015. {
  5016. addStyle("\n#item-box-special-case-questsUnlocked > img.image-icon-20\n{\n\tmargin-top: -1px;\n}\n\n#tab-container-crafting .settings-container\n{\n\tmargin: 5px 30px;\n}\n#table-crafting-recipe,\n#table-brewing-recipe,\n#table-magic-recipe\n{\n\twidth: calc(100% - 2*20px - 2*10px);\n}\n#tab-sub-container-magic-items\n{\n\tmargin: 5px 0px;\n}\n#table-magic-recipe\n{\n\twidth: calc(100% - 2*10px);\n}\n\n#tab-container-farming\n{\n\tpadding: 0 20px;\n}\n#tab-sub-container-farming\n{\n\tmargin: 5px 0;\n\tmargin-bottom: -10px;\n}\ndiv.farming-patch,\ndiv.farming-patch-locked\n{\n\tmargin: 10px;\n}\n\n#combat-table-area\n{\n\tborder-spacing: 0;\n}\n#combat-table-area > tbody > tr > td\n{\n\tvertical-align: top;\n}\ndiv#hero-area.hero,\ndiv#monster-area.monster\n{\n\tmargin-left: 20px;\n\tmargin-right: 20px;\n\tmargin-top: 10px;\n}\ntable.table-hero-stats,\ndiv.hp-bar,\n#hero-area div.fight-spellbook\n{\n\tmargin-left: 0;\n}\ndiv.hp-bar\n{\n\tmin-width: calc(100% - 2px);\n}\n#hero-area span.fight-spell\n{\n\tmargin-bottom: 0;\n\tmargin-top: 0;\n}\n#hero-area span.fight-spell:first-child\n{\n\tmargin-left: 0;\n}\n#hero-area span.fight-spell:last-child\n{\n\tmargin-right: 0;\n}\n#hero-area > div:last-child,\n.imageMonster\n{\n\theight: 556px !important;\n\tmargin-top: -50px;\n}\n#monster-area div.hp-bar\n{\n\tmargin-top: 66px;\n\tmargin-bottom: 74px;\n}\n#monster-area > br:first-child,\n#monster-area table.table-hero-stats + br\n{\n\tdisplay: none;\n}\n.imageMonster\n{\n\talign-items: flex-end;\n\tdisplay: flex;\n\tposition: relative;\n}\n#combat-table-area[style$=\"auto;\"]\n{\n\tborder-color: transparent;\n}\n#img-monster\n{\n\tposition: absolute;\n}\n#img-monster[src$=\"/1.png\"]\n{\n\theight: 250px;\n}\n#img-monster[src$=\"/2.png\"]\n{\n\ttransform: translateY(30px);\n}\n#img-monster[src$=\"/3.png\"]\n{\n\theight: 180px;\n\ttransform: translateY(-350px);\n}\n#img-monster[src$=\"/4.png\"]\n{\n\theight: 180px;\n}\n#img-monster[src$=\"/5.png\"]\n{\n\theight: 700px;\n\ttransform: translateY(130px);\n}\n#img-monster[src$=\"/7.png\"]\n{\n\theight: 450px;\n\ttransform: translateY(30px);\n}\n#img-monster[src$=\"/8.png\"]\n{\n\theight: 280px;\n\ttransform: translateY(-260px);\n}\n#img-monster[src$=\"/9.png\"]\n{\n\theight: 450px;\n\ttransform: translateY(-10px);\n}\n#img-monster[src$=\"/11.png\"],\n#img-monster[src$=\"/15.png\"]\n{\n\ttransform: translateY(-180px);\n}\n#img-monster[src$=\"/14.png\"]\n{\n\theight: 500px;\n\tmargin-left: -50px;\n\tmargin-right: -50px;\n}\n#img-monster[src$=\"/100.png\"]\n{\n\theight: 300px;\n}\n#img-monster[src$=\"/101.png\"]\n{\n\ttransform: translateY(-10px);\n}\n#tab-sub-container-combat > .large-button > .image-icon-50\n{\n\theight: 70px;\n\tmargin-top: -10px;\n\twidth: 70px;\n}\n#combat-table-area span.large-button,\n#combat-table-area span.medium-button\n{\n\tmargin: 10px;\n}\n#combat-table-area span.medium-button + br + br\n{\n\tdisplay: none;\n}\n\t\t");
  5017. }
  5018.  
  5019. function addHeroStatTooltips()
  5020. {
  5021. var table = document.querySelector('#hero-area table.table-hero-stats');
  5022. if (!table)
  5023. {
  5024. return;
  5025. }
  5026. var statRow = table.rows.item(0);
  5027. var attackCell = statRow.cells.item(0);
  5028. attackCell.title = 'Attack Damage';
  5029. window.$(attackCell).tooltip();
  5030. var accuracyCell = statRow.cells.item(1);
  5031. accuracyCell.title = 'Attack Accuracy';
  5032. window.$(accuracyCell).tooltip();
  5033. var speedCell = statRow.cells.item(2);
  5034. speedCell.title = 'Attack Speed';
  5035. window.$(speedCell).tooltip();
  5036. var defenseCell = statRow.cells.item(3);
  5037. defenseCell.title = 'Defense';
  5038. window.$(defenseCell).tooltip();
  5039. }
  5040.  
  5041. function unifyTooltips()
  5042. {
  5043. function getLastNonEmptyChild(parent)
  5044. {
  5045. for (var i = parent.childNodes.length - 1; i >= 0; i--)
  5046. {
  5047. var child = parent.childNodes.item(i);
  5048. if (child.nodeType === Node.TEXT_NODE
  5049. && (child.textContent || '').trim() !== '')
  5050. {
  5051. return null;
  5052. }
  5053. else if (child.nodeType === Node.ELEMENT_NODE)
  5054. {
  5055. return child;
  5056. }
  5057. }
  5058. return null;
  5059. }
  5060. // clean unnecessary br-tags in tooltips
  5061. var tooltips = document.querySelectorAll('#tooltip-list > div[id^="tooltip-"]');
  5062. for (var i = 0; i < tooltips.length; i++)
  5063. {
  5064. var tooltip = tooltips[i];
  5065. var lneChild = void 0;
  5066. while ((lneChild = getLastNonEmptyChild(tooltip)) && lneChild.tagName == 'BR')
  5067. {
  5068. tooltip.removeChild(lneChild);
  5069. }
  5070. }
  5071.  
  5072. function getTooltip(item)
  5073. {
  5074. return document.getElementById('tooltip-' + item);
  5075. }
  5076. var boldify = [
  5077. 'oilBarrel'
  5078. , 'boundEmptyPickaxe'
  5079. , 'boundEmptyShovel'
  5080. , 'boundEmptyChisel'
  5081. , 'ashes'
  5082. , 'iceBones'
  5083. ];
  5084. var lastDotRegex = /\.\s*$/;
  5085. for (var _i = 0, boldify_1 = boldify; _i < boldify_1.length; _i++)
  5086. {
  5087. var item = boldify_1[_i];
  5088. var tooltip = getTooltip(item);
  5089. if (!tooltip)
  5090. {
  5091. continue;
  5092. }
  5093. var textNode = tooltip.lastChild;
  5094. while (textNode && (textNode.nodeType != Node.TEXT_NODE || (textNode.textContent || '').trim() === ''))
  5095. {
  5096. textNode = textNode.previousSibling;
  5097. }
  5098. if (!textNode)
  5099. {
  5100. continue;
  5101. }
  5102. var text = textNode.textContent || '';
  5103. var split = text.split(/\.(?=\s*\S+)/);
  5104. var clickText = split[split.length - 1];
  5105. textNode.textContent = text.replace(clickText, '');
  5106. if (split.length > 1)
  5107. {
  5108. tooltip.appendChild(document.createElement('br'));
  5109. tooltip.appendChild(document.createElement('br'));
  5110. }
  5111. var boldText = document.createElement('b');
  5112. boldText.textContent = clickText;
  5113. tooltip.appendChild(boldText);
  5114. }
  5115.  
  5116. function prepareTooltip(item, editText, createOnMissing)
  5117. {
  5118. if (createOnMissing === void 0)
  5119. {
  5120. createOnMissing = false;
  5121. }
  5122. var tooltip = getTooltip(item);
  5123. if (!tooltip)
  5124. {
  5125. return;
  5126. }
  5127. // try to find the b-node:
  5128. var bNode = getLastNonEmptyChild(tooltip);
  5129. if (bNode && bNode.tagName === 'SPAN')
  5130. {
  5131. bNode = getLastNonEmptyChild(bNode);
  5132. }
  5133. if (!bNode || bNode.tagName !== 'B')
  5134. {
  5135. if (!createOnMissing)
  5136. {
  5137. bNode = null;
  5138. }
  5139. else
  5140. {
  5141. tooltip.appendChild(document.createElement('br'));
  5142. tooltip.appendChild(document.createElement('br'));
  5143. bNode = document.createElement('b');
  5144. tooltip.appendChild(bNode);
  5145. }
  5146. }
  5147. if (bNode)
  5148. {
  5149. bNode.textContent = editText(bNode);
  5150. }
  5151. }
  5152. // remove dots
  5153. for (var i = 0; i < tooltips.length; i++)
  5154. {
  5155. var item = tooltips.item(i).id.replace(/^tooltip-/, '');
  5156. prepareTooltip(item, function (bNode)
  5157. {
  5158. var text = bNode.textContent || '';
  5159. if (/Click to /.test(text))
  5160. {
  5161. return text.replace(lastDotRegex, '');
  5162. }
  5163. return text;
  5164. });
  5165. }
  5166. // add click texts
  5167. function setText(item, text)
  5168. {
  5169. prepareTooltip(item, function ()
  5170. {
  5171. return text;
  5172. }, true);
  5173. }
  5174. for (var _a = 0, FURNACE_LEVELS_1 = FURNACE_LEVELS; _a < FURNACE_LEVELS_1.length; _a++)
  5175. {
  5176. var furnaceLevel = FURNACE_LEVELS_1[_a];
  5177. var furnaceItem = getBoundKey(furnaceLevel + 'Furnace');
  5178. setText(furnaceItem, 'Click to operate');
  5179. var ovenItem = getBoundKey(furnaceLevel + 'Oven');
  5180. setText(ovenItem, 'Click to operate');
  5181. }
  5182. // fix tooltip of quests-book
  5183. var questBookTooltip = getTooltip('quests-book');
  5184. if (questBookTooltip)
  5185. {
  5186. var childNodes = questBookTooltip.childNodes;
  5187. for (var i = 0; i < childNodes.length; i++)
  5188. {
  5189. var node = childNodes[i];
  5190. if (node.nodeType === Node.TEXT_NODE
  5191. && (node.textContent || '').indexOf('Click to see a list of quests.') > -1)
  5192. {
  5193. var next = node.nextSibling;
  5194. if (next)
  5195. {
  5196. questBookTooltip.removeChild(next);
  5197. }
  5198. questBookTooltip.removeChild(node);
  5199. }
  5200. }
  5201. }
  5202. // fix tooltip of axe
  5203. var axeTooltip = getTooltip('boundEmptyAxe');
  5204. if (axeTooltip)
  5205. {
  5206. axeTooltip.insertBefore(document.createElement('br'), axeTooltip.lastElementChild);
  5207. }
  5208. var texts = {
  5209. 'quests-book': 'Click to see a list of quests'
  5210. , 'rake': 'Click to upgrade your rake'
  5211. , 'boundBoat': 'Click to send boat'
  5212. , 'boundCanoe': 'Click to send boat'
  5213. };
  5214. for (var item in texts)
  5215. {
  5216. setText(item, texts[item]);
  5217. }
  5218. }
  5219. var cached = {
  5220. scrollWidth: 0
  5221. , scrollHeight: 0
  5222. };
  5223.  
  5224. function changeTooltipPosition(event)
  5225. {
  5226. var tooltipX = event.pageX - 8;
  5227. var tooltipY = event.pageY + 8;
  5228. var el = document.querySelector('body > div.tooltip');
  5229. if (!el)
  5230. {
  5231. return;
  5232. }
  5233. if (!this)
  5234. {
  5235. // init
  5236. cached.scrollWidth = document.body.scrollWidth;
  5237. cached.scrollHeight = document.body.scrollHeight;
  5238. }
  5239. var rect = el.getBoundingClientRect();
  5240. var css = {
  5241. left: tooltipX
  5242. , top: tooltipY
  5243. , width: ''
  5244. , height: ''
  5245. , maxWidth: cached.scrollWidth
  5246. , maxHeight: cached.scrollHeight
  5247. };
  5248. var diffX = cached.scrollWidth - 20 - tooltipX - rect.width;
  5249. if (diffX < 0)
  5250. {
  5251. css.left += diffX;
  5252. css.width = rect.width - 42;
  5253. }
  5254. var diffY = cached.scrollHeight - 20 - tooltipY - rect.height;
  5255. if (diffY < 0)
  5256. {
  5257. css.top += diffY;
  5258. css.height = rect.height - 22;
  5259. }
  5260. window.$(el).css(css);
  5261. }
  5262.  
  5263. function fixTooltipPositioning()
  5264. {
  5265. window.changeTooltipPosition = changeTooltipPosition;
  5266. window.loadTooltips();
  5267. }
  5268. // fix tooltips of some food items
  5269. function fixTooltips()
  5270. {
  5271. var foodTooltipTemplate = document.getElementById('tooltip-uncookedBread');
  5272. if (!foodTooltipTemplate)
  5273. {
  5274. return;
  5275. }
  5276.  
  5277. function ensureTooltip(item, tooltipTemplate, onCreate)
  5278. {
  5279. var tooltipId = 'tooltip-' + item;
  5280. if (document.getElementById(tooltipId) != null)
  5281. {
  5282. return;
  5283. }
  5284. var newTooltip = tooltipTemplate.cloneNode(true);
  5285. newTooltip.id = tooltipId;
  5286. onCreate(newTooltip);
  5287. tooltipTemplate.parentElement.appendChild(newTooltip);
  5288. }
  5289.  
  5290. function ensureFoodTooltip(key, title, energyAmount)
  5291. {
  5292. ensureTooltip(key, foodTooltipTemplate, function (newTooltip)
  5293. {
  5294. var spans = newTooltip.getElementsByTagName('span');
  5295. var titleSpan = spans[0];
  5296. var energyNode = spans[1].firstChild;
  5297. titleSpan.textContent = title;
  5298. energyNode.textContent = '+'
  5299. + (typeof energyAmount === 'string' ? energyAmount : format.number(energyAmount))
  5300. + ' ';
  5301. });
  5302. }
  5303. ensureFoodTooltip('uncookedCarrotCake', 'Uncooked Carrot Cake', 400);
  5304. ensureFoodTooltip('carrotCake', 'Carrot Cake', 400);
  5305. ensureFoodTooltip('uncookedPizza', 'Uncooked Pizza', 1000);
  5306. ensureFoodTooltip('pizza', 'Pizza', 1000);
  5307. ensureFoodTooltip('carrotSoup', 'Carrot Soup', 300);
  5308. ensureFoodTooltip('tomatoSoup', 'Tomato Soup', 600);
  5309. ensureFoodTooltip('salad', 'Salad', 1100);
  5310. }
  5311.  
  5312. function init()
  5313. {
  5314. fixClientGameLoop();
  5315. fixScroller();
  5316. fixTooltipStyle();
  5317. fixRefreshingMagicRecipes();
  5318. moveStrangeLeafs();
  5319. fixTreasureMap();
  5320. fixWoodcutting();
  5321. fixQuestBook();
  5322. // apply fix for scroll images later to fix images in this code too
  5323. fixHitText();
  5324. fixScrollImages();
  5325. fixQuest8BraveryRecipe();
  5326. fixBoatTooltips();
  5327. fixAlignments();
  5328. addHeroStatTooltips();
  5329. unifyTooltips();
  5330. fixTooltipPositioning();
  5331. fixTooltips();
  5332. }
  5333. temporaryFixes.init = init;
  5334. })(temporaryFixes || (temporaryFixes = {}));
  5335.  
  5336. /**
  5337. * improve timer
  5338. */
  5339. var timer;
  5340. (function (timer)
  5341. {
  5342. timer.name = 'timer';
  5343.  
  5344. function bindNewFormatter()
  5345. {
  5346. function doBind()
  5347. {
  5348. window.formatTime = window.formatTimeShort = window.formatTimeShort2 = function (seconds)
  5349. {
  5350. return format.timer(seconds);
  5351. };
  5352. }
  5353. window.addEventListener('load', function ()
  5354. {
  5355. return setTimeout(function ()
  5356. {
  5357. return doBind();
  5358. }, 100);
  5359. });
  5360. doBind();
  5361. setTimeout(function ()
  5362. {
  5363. return doBind();
  5364. }, 100);
  5365. }
  5366.  
  5367. function improveSmeltingTimer()
  5368. {
  5369. addStyle("\n#notif-smelting > span:not(.timer)\n{\n\tdisplay: none;\n}\n\t\t");
  5370. var smeltingNotifBox = document.getElementById('notif-smelting');
  5371. var smeltingTimerEl = document.createElement('span');
  5372. smeltingTimerEl.className = 'timer';
  5373. smeltingNotifBox.appendChild(smeltingTimerEl);
  5374. var smeltingInterval;
  5375.  
  5376. function updatePercValues()
  5377. {
  5378. if (smeltingInterval)
  5379. {
  5380. window.clearInterval(smeltingInterval);
  5381. }
  5382. var delta = 0;
  5383. smeltingInterval = window.setInterval(function ()
  5384. {
  5385. return updateSmeltingTimer(++delta);
  5386. }, 1000);
  5387. updateSmeltingTimer();
  5388. }
  5389.  
  5390. function updateSmeltingTimer(delta)
  5391. {
  5392. if (delta === void 0)
  5393. {
  5394. delta = 0;
  5395. }
  5396. var totalTime = window.smeltingPercD;
  5397. // thanks at /u/marcus898 for your bug report
  5398. var elapsedTime = Math.round(window.smeltingPerc * totalTime / 100) + delta;
  5399. smeltingTimerEl.textContent = format.timer(Math.max(totalTime - elapsedTime, 0));
  5400. }
  5401. observer.add('smeltingPercD', function ()
  5402. {
  5403. return updatePercValues();
  5404. });
  5405. observer.add('smeltingPerc', function ()
  5406. {
  5407. return updatePercValues();
  5408. });
  5409. updatePercValues();
  5410. }
  5411.  
  5412. function improveTimer(cssRulePrefix, textColor, timerColor, infoIdPrefx, containerPrefix, updateFn)
  5413. {
  5414. addStyle("\n/* hide built in timer elements */\n" + cssRulePrefix + " > *:not(img):not(.info)\n{\n\tdisplay: none;\n}\n" + cssRulePrefix + " > div.info\n{\n\tcolor: " + textColor + ";\n\tmargin-top: 5px;\n\tpointer-events: none;\n\ttext-align: center;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n}\n" + cssRulePrefix + " > div.info > div.name\n{\n\tfont-size: 1.2rem;\n}\n" + cssRulePrefix + " > div.info > div.timer\n{\n\tcolor: " + timerColor + ";\n}\n\t\t");
  5415. for (var i = 0; i < 6; i++)
  5416. {
  5417. var num = i + 1;
  5418. var infoId = infoIdPrefx + num;
  5419. var container = document.getElementById(containerPrefix + num);
  5420. container.style.position = 'relative';
  5421. var infoEl = document.createElement('div');
  5422. infoEl.className = 'info';
  5423. infoEl.id = infoId;
  5424. infoEl.innerHTML = "<div class=\"name\"></div><div class=\"timer\"></div>";
  5425. container.appendChild(infoEl);
  5426. updateFn(num, infoId, true);
  5427. }
  5428. }
  5429.  
  5430. function updateTreeInfo(placeId, infoElId, init)
  5431. {
  5432. if (init === void 0)
  5433. {
  5434. init = false;
  5435. }
  5436. var infoEl = document.getElementById(infoElId);
  5437. var nameEl = infoEl.firstElementChild;
  5438. var timerEl = infoEl.lastElementChild;
  5439. var idKey = 'treeId' + placeId;
  5440. var growTimerKey = 'treeGrowTimer' + placeId;
  5441. var lockedKey = 'treeUnlocked' + placeId;
  5442. var treeId = getGameValue(idKey);
  5443. if (treeId == 0)
  5444. {
  5445. var isLocked = placeId > 4 && getGameValue(lockedKey) == 0;
  5446. nameEl.textContent = isLocked ? 'Locked' : 'Empty';
  5447. timerEl.textContent = '';
  5448. }
  5449. else
  5450. {
  5451. nameEl.textContent = key2Name(window.getTreeName(treeId)) || 'Unknown Tree';
  5452. var remainingTime = window.TREE_GROW_TIME[treeId - 1] - getGameValue(growTimerKey);
  5453. timerEl.textContent = remainingTime > 0 ? '(' + format.timer(remainingTime) + ')' : 'Fully grown';
  5454. }
  5455. if (init)
  5456. {
  5457. observer.add([idKey, growTimerKey, lockedKey], function ()
  5458. {
  5459. return updateTreeInfo(placeId, infoElId, false);
  5460. });
  5461. }
  5462. }
  5463. // add tree grow timer
  5464. function improveTreeGrowTimer()
  5465. {
  5466. improveTimer('.woodcutting-tree', 'white', 'yellow', 'wc-tree-info-', 'wc-div-tree-', updateTreeInfo);
  5467. }
  5468.  
  5469. function updatePatchInfo(patchId, infoElId, init)
  5470. {
  5471. if (init === void 0)
  5472. {
  5473. init = false;
  5474. }
  5475. var infoEl = document.getElementById(infoElId);
  5476. var nameEl = infoEl.querySelector('.name');
  5477. var timerEl = infoEl.querySelector('.timer');
  5478. var idKey = 'farmingPatchSeed' + patchId;
  5479. var growTimeKey = 'farmingPatchGrowTime' + patchId;
  5480. var timerKey = 'farmingPatchTimer' + patchId;
  5481. var stageKey = 'farmingPatchStage' + patchId;
  5482. var stage = getGameValue(stageKey);
  5483. var seedName = PLANT_NAME[getGameValue(idKey)] || 'Unkown Plant';
  5484. if (stage == 0)
  5485. {
  5486. var isLocked = patchId > 4 && window.donorFarmingPatch == 0;
  5487. nameEl.textContent = isLocked ? 'Locked' : 'Click to grow';
  5488. timerEl.textContent = '';
  5489. }
  5490. else if (stage >= 4)
  5491. {
  5492. nameEl.textContent = stage > 4 ? 'Dead Plant' : seedName;
  5493. timerEl.textContent = stage > 4 ? 'Click to remove' : 'Click to harvest';
  5494. }
  5495. else
  5496. {
  5497. nameEl.textContent = seedName;
  5498. var remainingTime = getGameValue(growTimeKey) - getGameValue(timerKey);
  5499. timerEl.textContent = '(' + format.timer(remainingTime) + ')';
  5500. }
  5501. if (init)
  5502. {
  5503. observer.add([idKey, timerKey, stageKey, 'donorFarmingPatch'], function ()
  5504. {
  5505. return updatePatchInfo(patchId, infoElId, false);
  5506. });
  5507. }
  5508. }
  5509. // add seed name and change color of timer
  5510. function improveSeedGrowTimer()
  5511. {
  5512. improveTimer('div[id^="farming-patch-area-"]', 'black', 'blue', 'farming-patch-info-', 'farming-patch-area-', updatePatchInfo);
  5513. }
  5514.  
  5515. function init()
  5516. {
  5517. bindNewFormatter();
  5518. improveSmeltingTimer();
  5519. improveTreeGrowTimer();
  5520. improveSeedGrowTimer();
  5521. }
  5522. timer.init = init;
  5523. })(timer || (timer = {}));
  5524.  
  5525. /**
  5526. * improve smelting dialog
  5527. */
  5528. var smelting;
  5529. (function (smelting)
  5530. {
  5531. smelting.name = 'smelting';
  5532. var TIME_NEEDED_ID = 'smelting-time-needed';
  5533. var LAST_SMELTING_AMOUNT_KEY = 'lastSmeltingAmount';
  5534. var LAST_SMELTING_BAR_KEY = 'lastSmeltingBar';
  5535. var smeltingValue = null;
  5536. var amountInput;
  5537.  
  5538. function prepareAmountInput()
  5539. {
  5540. amountInput = document.getElementById('input-smelt-bars-amount');
  5541. amountInput.type = 'number';
  5542. amountInput.min = '0';
  5543. amountInput.step = '5';
  5544.  
  5545. function onValueChange()
  5546. {
  5547. smeltingValue = null;
  5548. window.selectBar('', null, amountInput, document.getElementById('smelting-furnace-capacity').value);
  5549. }
  5550. amountInput.addEventListener('mouseup', onValueChange);
  5551. amountInput.addEventListener('keyup', onValueChange);
  5552. amountInput.setAttribute('onkeyup', '');
  5553. }
  5554.  
  5555. function setBarCap(bar, capacity)
  5556. {
  5557. if (bar == '')
  5558. {
  5559. bar = window.selectedBar;
  5560. }
  5561. var requirements = SMELTING_REQUIREMENTS[bar];
  5562. var maxAmount = parseInt(capacity, 10);
  5563. for (var key in requirements)
  5564. {
  5565. var req = requirements[key];
  5566. maxAmount = Math.min(Math.floor(getGameValue(key) / req), maxAmount);
  5567. }
  5568. var value = parseInt(amountInput.value, 10);
  5569. if (value > maxAmount)
  5570. {
  5571. smeltingValue = value;
  5572. amountInput.value = maxAmount.toString();
  5573. }
  5574. else if (smeltingValue != null)
  5575. {
  5576. amountInput.value = Math.min(smeltingValue, maxAmount).toString();
  5577. if (smeltingValue <= maxAmount)
  5578. {
  5579. smeltingValue = null;
  5580. }
  5581. }
  5582. }
  5583.  
  5584. function prepareTimeNeeded()
  5585. {
  5586. var neededMatsEl = document.getElementById('dialogue-furnace-mats-needed');
  5587. var parent = neededMatsEl && neededMatsEl.parentElement;
  5588. if (!neededMatsEl || !parent)
  5589. {
  5590. return;
  5591. }
  5592. var br = document.createElement('br');
  5593. var timeBox = document.createElement('div');
  5594. timeBox.className = 'basic-smallbox';
  5595. timeBox.innerHTML = "<img src=\"images/icons/hourglass.png\" class=\"image-icon-30\">\n\t\tDuration: <span id=\"" + TIME_NEEDED_ID + "\"></span>";
  5596. var next = neededMatsEl.nextElementSibling;
  5597. parent.insertBefore(br, next);
  5598. parent.insertBefore(timeBox, next);
  5599. }
  5600.  
  5601. function updateTimeNeeded(value)
  5602. {
  5603. var timeEl = document.getElementById(TIME_NEEDED_ID);
  5604. if (!timeEl)
  5605. {
  5606. return;
  5607. }
  5608. var num = parseInt(value, 10);
  5609. var timePerBar = window.getTimerPerBar(window.selectedBar);
  5610. timeEl.textContent = format.timer(timePerBar * num);
  5611. }
  5612.  
  5613. function init()
  5614. {
  5615. prepareAmountInput();
  5616. prepareTimeNeeded();
  5617. var _selectBar = window.selectBar;
  5618. var updateSmeltingRequirements = function (bar, inputElement, inputBarsAmountEl, capacity)
  5619. {
  5620. _selectBar(bar, inputElement, inputBarsAmountEl, capacity);
  5621. updateTimeNeeded(inputBarsAmountEl.value);
  5622. };
  5623. window.selectBar = function (bar, inputElement, inputBarsAmountEl, capacity)
  5624. {
  5625. setBarCap(bar, capacity);
  5626. // save selected bar
  5627. if (bar != '')
  5628. {
  5629. store.set(LAST_SMELTING_BAR_KEY, bar);
  5630. }
  5631. // save amount
  5632. store.set(LAST_SMELTING_AMOUNT_KEY, inputBarsAmountEl.value);
  5633. updateSmeltingRequirements(bar, inputElement, inputBarsAmountEl, capacity);
  5634. };
  5635. var lastBar = store.get(LAST_SMELTING_BAR_KEY);
  5636. var lastAmount = store.get(LAST_SMELTING_AMOUNT_KEY);
  5637. var _openFurnaceDialogue = window.openFurnaceDialogue;
  5638. window.openFurnaceDialogue = function (furnace)
  5639. {
  5640. var capacity = window.getFurnaceCapacity(furnace);
  5641. if (window.smeltingBarType == 0)
  5642. {
  5643. amountInput.max = capacity.toString();
  5644. }
  5645. // restore amount
  5646. var inputBarsAmountEl = document.getElementById('input-smelt-bars-amount');
  5647. if (inputBarsAmountEl && inputBarsAmountEl.value == '-1' && lastAmount != null)
  5648. {
  5649. inputBarsAmountEl.value = lastAmount;
  5650. }
  5651. _openFurnaceDialogue(furnace);
  5652. // restore selected bar
  5653. if ((!window.selectedBar || window.selectedBar == 'none') && lastBar != null)
  5654. {
  5655. window.selectedBar = lastBar;
  5656. }
  5657. // update whether requirements are fulfilled
  5658. var barInputId = 'input-furnace-' + split2Words(window.selectedBar, '-').toLowerCase();
  5659. var inputElement = document.getElementById(barInputId);
  5660. if (inputElement && inputBarsAmountEl)
  5661. {
  5662. updateSmeltingRequirements(window.selectedBar, inputElement, inputBarsAmountEl, capacity.toString());
  5663. }
  5664. };
  5665. }
  5666. smelting.init = init;
  5667. })(smelting || (smelting = {}));
  5668.  
  5669. /**
  5670. * add chance to time calculator
  5671. */
  5672. var fishingInfo;
  5673. (function (fishingInfo)
  5674. {
  5675. fishingInfo.name = 'fishingInfo';
  5676. /**
  5677. * calculates the number of seconds until the event with the given chance happened at least once with the given
  5678. * probability p (in percent)
  5679. */
  5680. function calcSecondsTillP(chancePerSecond, p)
  5681. {
  5682. return Math.round(Math.log(1 - p / 100) / Math.log(1 - chancePerSecond));
  5683. }
  5684.  
  5685. function addChanceTooltip(headline, chancePerSecond, elId, targetEl)
  5686. {
  5687. // ensure tooltip exists and is correctly binded
  5688. var tooltipEl = ensureTooltip('chance-' + elId, targetEl);
  5689. // set elements content
  5690. var percValues = [1, 10, 20, 50, 80, 90, 99];
  5691. var percRows = '';
  5692. for (var _i = 0, percValues_1 = percValues; _i < percValues_1.length; _i++)
  5693. {
  5694. var p = percValues_1[_i];
  5695. percRows += "\n\t\t\t\t<tr>\n\t\t\t\t\t<td>" + p + "%</td>\n\t\t\t\t\t<td>" + format.time2NearestUnit(calcSecondsTillP(chancePerSecond, p), true) + "</td>\n\t\t\t\t</tr>";
  5696. }
  5697. tooltipEl.innerHTML = "<h2>" + headline + "</h2>\n\t\t\t<table class=\"chance\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th>Probability</th>\n\t\t\t\t\t<th>Time</th>\n\t\t\t\t</tr>\n\t\t\t\t" + percRows + "\n\t\t\t</table>\n\t\t";
  5698. }
  5699.  
  5700. function addChanceStyle()
  5701. {
  5702. addStyle("\ntable.chance\n{\n\tborder-spacing: 0;\n}\ntable.chance th\n{\n\tborder-bottom: 1px solid gray;\n}\ntable.chance td:first-child\n{\n\tborder-right: 1px solid gray;\n\ttext-align: center;\n}\ntable.chance th,\ntable.chance td\n{\n\tpadding: 4px 8px;\n}\ntable.chance tr:nth-child(2n) td\n{\n\tbackground-color: white;\n}\n\t\t");
  5703. }
  5704.  
  5705. function addXp()
  5706. {
  5707. var table = document.querySelector('#dialogue-id-fishingRod table');
  5708. if (!table)
  5709. {
  5710. return;
  5711. }
  5712. var rows = table.rows;
  5713. for (var i = 0; i < rows.length; i++)
  5714. {
  5715. var row = rows.item(i);
  5716. if (row.classList.contains('xp-added'))
  5717. {
  5718. continue;
  5719. }
  5720. if (i == 0)
  5721. {
  5722. var xpCell = document.createElement('th');
  5723. xpCell.textContent = 'XP';
  5724. row.appendChild(xpCell);
  5725. }
  5726. else
  5727. {
  5728. var cell = row.insertCell(-1);
  5729. var rawFish = row.id.replace('dialogue-fishing-rod-tr-', '');
  5730. var xp = FISH_XP[rawFish];
  5731. cell.textContent = xp == null ? '?' : format.number(xp);
  5732. }
  5733. row.classList.add('xp-added');
  5734. }
  5735. }
  5736.  
  5737. function chance2TimeCalculator()
  5738. {
  5739. var fishList = ['shrimp', 'sardine', 'tuna', 'swordfish', 'shark'];
  5740. for (var _i = 0, fishList_1 = fishList; _i < fishList_1.length; _i++)
  5741. {
  5742. var fish = fishList_1[_i];
  5743. var rawFish = 'raw' + capitalize(fish);
  5744. var row = document.getElementById('dialogue-fishing-rod-tr-' + rawFish);
  5745. if (!row)
  5746. {
  5747. continue;
  5748. }
  5749. var chanceCell = row.cells.item(4);
  5750. var chance = (chanceCell.textContent || '')
  5751. .replace(/[^\d\/]/g, '')
  5752. .split('/')
  5753. .reduce(function (p, c)
  5754. {
  5755. return p / parseInt(c, 10);
  5756. }, 1);
  5757. addChanceTooltip("One raw " + fish + " at least every:", chance, rawFish, row);
  5758. }
  5759. }
  5760.  
  5761. function init()
  5762. {
  5763. addChanceStyle();
  5764. var _clicksShovel = window.clicksShovel;
  5765. window.clicksShovel = function ()
  5766. {
  5767. _clicksShovel();
  5768. var shovelChance = document.getElementById('dialogue-shovel-chance');
  5769. var titleEl = shovelChance.parentElement;
  5770. var chance = 1 / window.getChanceOfDiggingSand();
  5771. addChanceTooltip('One sand at least every:', chance, 'shovel', titleEl);
  5772. };
  5773. // depends on fishingXp
  5774. var _clicksFishingRod = window.clicksFishingRod;
  5775. window.clicksFishingRod = function ()
  5776. {
  5777. _clicksFishingRod();
  5778. addXp();
  5779. chance2TimeCalculator();
  5780. };
  5781. }
  5782. fishingInfo.init = init;
  5783. })(fishingInfo || (fishingInfo = {}));
  5784.  
  5785. /**
  5786. * add tooltips for recipes
  5787. */
  5788. var recipeTooltips;
  5789. (function (recipeTooltips)
  5790. {
  5791. recipeTooltips.name = 'recipeTooltips';
  5792.  
  5793. function updateRecipeTooltips(recipeKey, recipes)
  5794. {
  5795. var table = document.getElementById('table-' + recipeKey + '-recipe');
  5796. var rows = table.rows;
  5797.  
  5798. function recipe2Title(recipe)
  5799. {
  5800. return recipe.recipe
  5801. .map(function (name, i)
  5802. {
  5803. return format.number(recipe.recipeCost[i]) + ' '
  5804. + split2Words(name).toLowerCase();
  5805. })
  5806. .join(' + ');
  5807. };
  5808. for (var i = 1; i < rows.length; i++)
  5809. {
  5810. var row = rows.item(i);
  5811. var key = row.id.replace(recipeKey + '-', '');
  5812. var recipe = recipes[key];
  5813. var requirementCell = row.cells.item(3);
  5814. requirementCell.title = recipe2Title(recipe);
  5815. window.$(requirementCell).tooltip();
  5816. }
  5817. }
  5818.  
  5819. function updateTooltipsOnReinitRecipes(key)
  5820. {
  5821. var capitalKey = capitalize(key);
  5822. var processKey = 'process' + capitalKey + 'Tab';
  5823. var _processTab = window[processKey];
  5824. window[processKey] = function ()
  5825. {
  5826. var reinit = !!getGameValue('refreshLoad' + capitalKey + 'Table');
  5827. _processTab();
  5828. if (reinit)
  5829. {
  5830. updateRecipeTooltips(key, getGameValue(key + 'Recipes'));
  5831. }
  5832. };
  5833. }
  5834.  
  5835. function init()
  5836. {
  5837. updateTooltipsOnReinitRecipes('crafting');
  5838. updateTooltipsOnReinitRecipes('brewing');
  5839. updateTooltipsOnReinitRecipes('magic');
  5840. updateTooltipsOnReinitRecipes('cooksBook');
  5841. }
  5842. recipeTooltips.init = init;
  5843. })(recipeTooltips || (recipeTooltips = {}));
  5844.  
  5845. /**
  5846. * fix formatting of numbers
  5847. */
  5848. var fixNumbers;
  5849. (function (fixNumbers)
  5850. {
  5851. fixNumbers.name = 'fixNumbers';
  5852.  
  5853. function prepareRecipeForTable(recipe)
  5854. {
  5855. // create a copy of the recipe to prevent requirement check from failing
  5856. var newRecipe = JSON.parse(JSON.stringify(recipe));
  5857. newRecipe.recipeCost = recipe.recipeCost.map(function (cost)
  5858. {
  5859. return format.number(cost);
  5860. });
  5861. newRecipe.xp = format.number(recipe.xp);
  5862. return newRecipe;
  5863. }
  5864.  
  5865. function fixNumberFormat()
  5866. {
  5867. var _addRecipeToBrewingTable = window.addRecipeToBrewingTable;
  5868. window.addRecipeToBrewingTable = function (brewingRecipe)
  5869. {
  5870. _addRecipeToBrewingTable(prepareRecipeForTable(brewingRecipe));
  5871. };
  5872. var _addRecipeToMagicTable = window.addRecipeToMagicTable;
  5873. window.addRecipeToMagicTable = function (magicRecipe)
  5874. {
  5875. _addRecipeToMagicTable(prepareRecipeForTable(magicRecipe));
  5876. };
  5877. var tooltipList = document.querySelectorAll('#tooltip-list div[id^="tooltip-"][id$="Seeds"]');
  5878. for (var i = 0; i < tooltipList.length; i++)
  5879. {
  5880. var tooltip = tooltipList[i];
  5881. tooltip.innerHTML = format.numbersInText(tooltip.innerHTML);
  5882. }
  5883. var fightEnergyCells = document.querySelectorAll('#dialogue-fight tr > td:nth-child(4)');
  5884. for (var i = 0; i < fightEnergyCells.length; i++)
  5885. {
  5886. var cell = fightEnergyCells[i];
  5887. cell.innerHTML = format.numbersInText(cell.innerHTML);
  5888. }
  5889. }
  5890.  
  5891. function init()
  5892. {
  5893. fixNumberFormat();
  5894. }
  5895. fixNumbers.init = init;
  5896. })(fixNumbers || (fixNumbers = {}));
  5897.  
  5898. /**
  5899. * add slider for machines
  5900. */
  5901. var machineDialog;
  5902. (function (machineDialog)
  5903. {
  5904. machineDialog.name = 'machineDialog';
  5905. var $slider;
  5906.  
  5907. function createSlider()
  5908. {
  5909. var br = document.querySelector('#dialogue-machinery-current-total ~ br');
  5910. var parent = br && br.parentElement;
  5911. if (!br || !parent)
  5912. {
  5913. return;
  5914. }
  5915. addStyle("\n#dialogue-id-boundMachinery .ui-slider\n{\n\tmargin: 10px 5px;\n}\n#dialogue-id-boundMachinery .ui-slider:not([data-owned=\"10\"])::after\n{\n\tbackground: hsla(0, 0%, 0%, 1);\n\tborder: 1px solid #c5c5c5;\n\tborder-radius: 3px;\n\tborder-top-left-radius: 0;\n\tborder-bottom-left-radius: 0;\n\tcontent: '';\n\tmargin-left: -3px;\n\tpadding-left: 3px;\n\theight: 100%;\n\twidth: 0%;\n\tposition: absolute;\n\tleft: 100%;\n\ttop: -1px;\n}\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"9\"] { width: calc((100% - 10px - 2px) / 10*9); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"9\"]::after { width: calc(100% / 9 * 1); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"8\"] { width: calc((100% - 10px - 2px) / 10*8); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"8\"]::after { width: calc(100% / 8 * 2); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"7\"] { width: calc((100% - 10px - 2px) / 10*7); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"7\"]::after { width: calc(100% / 7 * 3); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"6\"] { width: calc((100% - 10px - 2px) / 10*6); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"6\"]::after { width: calc(100% / 6 * 4); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"5\"] { width: calc((100% - 10px - 2px) / 10*5); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"5\"]::after { width: calc(100% / 5 * 5); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"4\"] { width: calc((100% - 10px - 2px) / 10*4); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"4\"]::after { width: calc(100% / 4 * 6); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"3\"] { width: calc((100% - 10px - 2px) / 10*3); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"3\"]::after { width: calc(100% / 3 * 7); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"2\"] { width: calc((100% - 10px - 2px) / 10*2); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"2\"]::after { width: calc(100% / 2 * 8); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"1\"] { width: calc((100% - 10px - 2px) / 10*1); }\n#dialogue-id-boundMachinery .ui-slider[data-owned=\"1\"]::after { width: calc(100% / 1 * 9); }\n\t\t");
  5916. var slider = document.createElement('div');
  5917. parent.insertBefore(slider, br);
  5918. $slider = window.$(slider)
  5919. .slider(
  5920. {
  5921. range: 'max'
  5922. , min: 0
  5923. , max: 10
  5924. , value: 0
  5925. , slide: function (event, ui)
  5926. {
  5927. return updateValue(ui.value);
  5928. }
  5929. });
  5930. // hide br and up/down arrows
  5931. br.style.display = 'none';
  5932. var arrows = document.querySelectorAll('input[onclick^="turnOn("]');
  5933. for (var i = 0; i < arrows.length; i++)
  5934. {
  5935. arrows[i].style.display = 'none';
  5936. }
  5937. var els = document.querySelectorAll('[onclick*="openMachineryDialogue("]');
  5938. var boundMachineKeyList = [];
  5939. for (var i = 0; i < els.length; i++)
  5940. {
  5941. var match = els[i].id.match(/openMachineryDialogue\('(.+?)'\)/);
  5942. if (match)
  5943. {
  5944. boundMachineKeyList.push(getBoundKey(match[1]));
  5945. }
  5946. }
  5947. observer.add(boundMachineKeyList, function ()
  5948. {
  5949. return updateMax();
  5950. });
  5951. }
  5952.  
  5953. function updateMax()
  5954. {
  5955. var machineEl = document.getElementById('dialogue-machinery-chosen');
  5956. if (machineEl && machineEl.value != '')
  5957. {
  5958. var boundMachineKey = getBoundKey(machineEl.value);
  5959. var ownedNum = getGameValue(boundMachineKey);
  5960. $slider.slider('option', 'max', ownedNum);
  5961. $slider.get(0).dataset.owned = ownedNum.toString();
  5962. }
  5963. }
  5964.  
  5965. function updateValue(value)
  5966. {
  5967. var typeEl = document.getElementById('dialogue-machinery-chosen');
  5968. var numEl = document.getElementById('dialogue-machinery-current-on');
  5969. if (numEl && typeEl)
  5970. {
  5971. var valueBefore = parseInt(numEl.textContent || '0', 10);
  5972. var machine = typeEl.value;
  5973. var increment = valueBefore < value;
  5974. var diff = Math.abs(valueBefore - value);
  5975. for (var i = 0; i < diff; i++)
  5976. {
  5977. window.turnOn(machine, increment);
  5978. }
  5979. }
  5980. }
  5981.  
  5982. function init()
  5983. {
  5984. createSlider();
  5985. var _openMachineryDialogue = window.openMachineryDialogue;
  5986. window.openMachineryDialogue = function (machineType)
  5987. {
  5988. _openMachineryDialogue(machineType);
  5989. updateMax();
  5990. $slider.slider('value', getGameValue(machineType + 'On'));
  5991. };
  5992. }
  5993. machineDialog.init = init;
  5994. })(machineDialog || (machineDialog = {}));
  5995.  
  5996. /**
  5997. * improve behaviour of amount inputs
  5998. */
  5999. var amountInputs;
  6000. (function (amountInputs)
  6001. {
  6002. amountInputs.name = 'amountInputs';
  6003.  
  6004. function getMax(recipe)
  6005. {
  6006. var max = Number.MAX_SAFE_INTEGER;
  6007. for (var i = 0; i < recipe.recipe.length; i++)
  6008. {
  6009. max = Math.min(max, Math.floor(getGameValue(recipe.recipe[i]) / recipe.recipeCost[i]));
  6010. }
  6011. return max;
  6012. }
  6013.  
  6014. function setAmount(id, recipeCollection, key)
  6015. {
  6016. var numInput = document.getElementById(id);
  6017. if (!numInput)
  6018. {
  6019. return;
  6020. }
  6021. var max = getMax(recipeCollection[key]);
  6022. numInput.value = max.toString();
  6023. numInput.select();
  6024. }
  6025.  
  6026. function init()
  6027. {
  6028. var _multiCraft = window.multiCraft;
  6029. window.multiCraft = function (item)
  6030. {
  6031. _multiCraft(item);
  6032. setAmount('dialogue-multicraft-input', window.craftingRecipes, item);
  6033. };
  6034. var _brew = window.brew;
  6035. window.brew = function (potion)
  6036. {
  6037. _brew(potion);
  6038. setAmount('dialogue-brewing-input', window.brewingRecipes, potion);
  6039. };
  6040. var _cooksBookInputDialogue = window.cooksBookInputDialogue;
  6041. window.cooksBookInputDialogue = function (food)
  6042. {
  6043. _cooksBookInputDialogue(food);
  6044. setAmount('dialogue-cooksBook-input', window.cooksBookRecipes, food);
  6045. };
  6046. }
  6047. amountInputs.init = init;
  6048. })(amountInputs || (amountInputs = {}));
  6049.  
  6050. /**
  6051. * improves the top bar
  6052. */
  6053. var newTopbar;
  6054. (function (newTopbar)
  6055. {
  6056. newTopbar.name = 'newTopbar';
  6057. var linkCell, tabCell, infoCell;
  6058. var addQueues = {
  6059. link: []
  6060. , tab: []
  6061. , info: []
  6062. };
  6063.  
  6064. function addLinkEntry(el)
  6065. {
  6066. if (!linkCell)
  6067. {
  6068. addQueues.link.push(el);
  6069. }
  6070. else
  6071. {
  6072. linkCell.appendChild(document.createTextNode('|'));
  6073. linkCell.appendChild(el);
  6074. }
  6075. }
  6076. newTopbar.addLinkEntry = addLinkEntry;
  6077.  
  6078. function addTabEntry(el)
  6079. {
  6080. if (!tabCell)
  6081. {
  6082. addQueues.tab.push(el);
  6083. }
  6084. else
  6085. {
  6086. tabCell.appendChild(document.createTextNode('|'));
  6087. tabCell.appendChild(el);
  6088. }
  6089. }
  6090. newTopbar.addTabEntry = addTabEntry;
  6091.  
  6092. function addInfoEntry(el)
  6093. {
  6094. if (!infoCell)
  6095. {
  6096. addQueues.info.push(el);
  6097. }
  6098. else
  6099. {
  6100. infoCell.appendChild(document.createTextNode('|'));
  6101. infoCell.appendChild(el);
  6102. }
  6103. }
  6104. newTopbar.addInfoEntry = addInfoEntry;
  6105.  
  6106. function init()
  6107. {
  6108. addStyle("\ntable.top-links,\ntable.top-links *\n{\n\tpadding: 0;\n}\ntable.top-links td > *\n{\n\tdisplay: inline-block;\n\tpadding: 2px 6px;\n}\n\t\t");
  6109. var table = document.querySelector('table.top-links');
  6110. if (!table)
  6111. {
  6112. return;
  6113. }
  6114. var row = table.rows.item(0);
  6115. var cells = row.cells;
  6116. var tabIdx = [2, 4];
  6117. var infoIdx = [5, 6];
  6118. var newRow = table.insertRow(-1);
  6119. linkCell = newRow.insertCell(-1);
  6120. tabCell = newRow.insertCell(-1);
  6121. tabCell.style.textAlign = 'center';
  6122. infoCell = newRow.insertCell(-1);
  6123. infoCell.style.textAlign = 'right';
  6124. for (var i = 0; i < cells.length; i++)
  6125. {
  6126. var container = linkCell;
  6127. if (tabIdx.indexOf(i) != -1)
  6128. {
  6129. container = tabCell;
  6130. }
  6131. else if (infoIdx.indexOf(i) != -1)
  6132. {
  6133. container = infoCell;
  6134. }
  6135. var cell = cells.item(i);
  6136. var el = cell.firstElementChild;
  6137. if (cell.childNodes.length > 1)
  6138. {
  6139. el = document.createElement('span');
  6140. el.style.color = 'yellow';
  6141. while (cell.childNodes.length > 0)
  6142. {
  6143. el.appendChild(cell.childNodes[0]);
  6144. }
  6145. }
  6146. if (container.children.length > 0)
  6147. {
  6148. container.appendChild(document.createTextNode('|'));
  6149. }
  6150. if (el)
  6151. {
  6152. container.appendChild(el);
  6153. }
  6154. }
  6155. var parent = row.parentElement;
  6156. if (parent)
  6157. {
  6158. parent.removeChild(row);
  6159. }
  6160. for (var _i = 0, _a = addQueues.link; _i < _a.length; _i++)
  6161. {
  6162. var el = _a[_i];
  6163. addLinkEntry(el);
  6164. }
  6165. for (var _b = 0, _c = addQueues.tab; _b < _c.length; _b++)
  6166. {
  6167. var el = _c[_b];
  6168. addTabEntry(el);
  6169. }
  6170. for (var _d = 0, _e = addQueues.info; _d < _e.length; _d++)
  6171. {
  6172. var el = _e[_d];
  6173. addInfoEntry(el);
  6174. }
  6175. var _openTab = window.openTab;
  6176. window.openTab = function (newTab)
  6177. {
  6178. var oldTab = window.currentOpenTab;
  6179. _openTab(newTab);
  6180. var children = tabCell.children;
  6181. for (var i = 0; i < children.length; i++)
  6182. {
  6183. var el = children[i];
  6184. var match = (el.getAttribute('onclick') || '').match(/openTab\('([^']+)'\)/);
  6185. if (!match)
  6186. {
  6187. continue;
  6188. }
  6189. var tab = match[1];
  6190. if (oldTab == tab)
  6191. {
  6192. el.style.color = '';
  6193. }
  6194. if (newTab == tab)
  6195. {
  6196. el.style.color = 'white';
  6197. }
  6198. }
  6199. };
  6200. }
  6201. newTopbar.init = init;
  6202. })(newTopbar || (newTopbar = {}));
  6203.  
  6204. /**
  6205. * style tweaks
  6206. */
  6207. var styleTweaks;
  6208. (function (styleTweaks)
  6209. {
  6210. styleTweaks.name = 'styleTweaks';
  6211.  
  6212. function addTweakStyle(setting, style)
  6213. {
  6214. if (setting != '')
  6215. {
  6216. var prefix = setting == '' ? '' : 'body.' + setting;
  6217. style = style.replace(/(^\s*|,\s*|\}\s*)([^\{\},]+)(,|\s*\{)/g, '$1' + prefix + ' $2$3');
  6218. document.body.classList.add(setting);
  6219. }
  6220. addStyle(style, setting != '' ? setting : null);
  6221. }
  6222. // tweak oil production/consumption
  6223. function tweakOil()
  6224. {
  6225. addTweakStyle('tweak-oil', "\nspan#oil-flow-values\n{\n\tmargin-left: .5em;\n\tpadding-left: 2rem;\n\tposition: relative;\n}\n#oil-flow-values > span:nth-child(-n+2)\n{\n\tfont-size: 0px;\n\tposition: absolute;\n\tleft: 0;\n\ttop: -0.75rem;\n\tvisibility: hidden;\n}\n#oil-flow-values > span:nth-child(-n+2) > span\n{\n\tfont-size: 1rem;\n\tvisibility: visible;\n}\n#oil-flow-values > span:nth-child(2)\n{\n\ttop: 0.75rem;\n}\n#oil-flow-values span[data-item-display=\"oilIn\"]::before\n{\n\tcontent: '+';\n}\n#oil-flow-values span[data-item-display=\"oilOut\"]::before\n{\n\tcontent: '-';\n}\n\t\t");
  6226. // make room for oil cell on small devices
  6227. var oilFlowValues = document.getElementById('oil-flow-values');
  6228. var oilFlowCell = oilFlowValues.parentElement;
  6229. oilFlowCell.style.width = '30%';
  6230. }
  6231.  
  6232. function tweakSelection()
  6233. {
  6234. addTweakStyle('no-select', "\ntable.tab-bar,\nspan.item-box,\ndiv.farming-patch,\ndiv.farming-patch-locked,\ndiv#tab-sub-container-combat > span,\ntable.top-links a,\n#hero-area > div:last-child\n{\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n\t\t");
  6235. }
  6236. // tweak stardust monitor of DH2QoL to keep it in place
  6237. function tweakStardust()
  6238. {
  6239. addTweakStyle('dh2qol', "\n#dh2qol-stardustMonitor\n{\n\tdisplay: inline-block;\n\tmargin-left: .25rem;\n\ttext-align: left;\n\twidth: 2.5rem;\n}\n\t\t");
  6240. }
  6241.  
  6242. function tweakSkillLevelText()
  6243. {
  6244. addTweakStyle('', "\ndiv.skill-xp-label\n{\n\ttext-shadow: white 0px 0px 0.5rem;\n}\n\t\t");
  6245. }
  6246.  
  6247. function tweakFightDialog()
  6248. {
  6249. addTweakStyle('smaller-fight-dialog', "\n#dialogue-fight img[width=\"300px\"]\n{\n\twidth: 120px;\n\theight: 50px;\n}\n#dialogue-fight img[src=\"images/icons/combat.png\"] ~ br\n{\n\tdisplay: none;\n}\n\t\t");
  6250. }
  6251.  
  6252. function addAdditionalSkillBars()
  6253. {
  6254. var _loadSkillTabs = window.loadSkillTabs;
  6255. window.loadSkillTabs = function ()
  6256. {
  6257. _loadSkillTabs();
  6258. for (var _i = 0, SKILL_LIST_1 = SKILL_LIST; _i < SKILL_LIST_1.length; _i++)
  6259. {
  6260. var skill = SKILL_LIST_1[_i];
  6261. var unlocked = getGameValue(skill + 'Unlocked') == 1;
  6262. if (!unlocked)
  6263. {
  6264. continue;
  6265. }
  6266. var xp = getGameValue(skill + 'Xp');
  6267. var currentLevelXp = window.getXpNeeded(window.getLevel(xp));
  6268. var nextLevelXp = window.getXpNeeded(window.getLevel(xp) + 1);
  6269. var perc = (xp - currentLevelXp) / (nextLevelXp - currentLevelXp) * 100;
  6270. var progress = document.getElementById('skill-progress-' + skill);
  6271. if (progress)
  6272. {
  6273. progress.style.width = perc + '%';
  6274. }
  6275. }
  6276. };
  6277. // init additional skill bars
  6278. addStyle("\ntd[id^=\"top-bar-level-td-\"]\n{\n\tposition: relative;\n}\n#top-bar-levels .skill-bar\n{\n\tbackground-color: grey;\n\theight: 5px;\n\tposition: absolute;\n\tbottom: 5px;\n\tleft: 60px;\n\tright: 10px;\n}\n#top-bar-levels .skill-bar > .skill-progress\n{\n\tbackground-color: rgb(51, 204, 51);\n\theight: 100%;\n\twidth: 0%;\n}\n\t\t");
  6279. for (var _i = 0, SKILL_LIST_2 = SKILL_LIST; _i < SKILL_LIST_2.length; _i++)
  6280. {
  6281. var skill = SKILL_LIST_2[_i];
  6282. var cell = document.getElementById('top-bar-level-td-' + skill);
  6283. if (!cell)
  6284. {
  6285. continue;
  6286. }
  6287. var levelBar = document.createElement('div');
  6288. levelBar.className = 'skill-bar';
  6289. var progress = document.createElement('div');
  6290. progress.id = 'skill-progress-' + skill;
  6291. progress.className = 'skill-progress';
  6292. levelBar.appendChild(progress);
  6293. cell.appendChild(levelBar);
  6294. // update skill level progress bars on click
  6295. levelBar.addEventListener('click', function ()
  6296. {
  6297. return window.loadSkillTabs();
  6298. });
  6299. }
  6300. window.loadSkillTabs();
  6301. }
  6302. // highlight cooking level requirement when not matched
  6303. function highlightCookinglevel()
  6304. {
  6305. var _cookFoodDialogue = window.cookFoodDialogue;
  6306. window.cookFoodDialogue = function (rawFood)
  6307. {
  6308. _cookFoodDialogue(rawFood);
  6309. var dialog = document.getElementById('dialogue-id-cook-food');
  6310. if (!dialog)
  6311. {
  6312. return;
  6313. }
  6314. var levelReq = document.getElementById('dialogue-cook-levelReq');
  6315. var levelReqLabel = levelReq && levelReq.previousElementSibling;
  6316. if (!levelReq || !levelReqLabel)
  6317. {
  6318. return;
  6319. }
  6320. var fulfilled = window.getCookingLevelReq(rawFood) > window.getLevel(window.cookingXp);
  6321. levelReq.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  6322. levelReq.style.fontWeight = fulfilled ? 'bold' : '';
  6323. levelReqLabel.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  6324. };
  6325. }
  6326.  
  6327. function amountStyle()
  6328. {
  6329. var tweakName = 'amount-symbol';
  6330. addTweakStyle(tweakName, "\n.item-box:not(#item-box-special-case-questsUnlocked):not(#item-box-pirate):not(#item-box-miner):not(#item-box-boundPumpjacks):not([onclick^=\"openMachineryDialogue(\"]):not(#item-box-sandCollectorsQuest):not(#item-box-boundFilledBonemealBin) > span[data-item-display]::before\n{\n\tcontent: '\\0D7';\n\tmargin-right: .25rem;\n\tmargin-left: -.5rem;\n}\n\t\t");
  6331.  
  6332. function setAmountSymbolVisibility(init)
  6333. {
  6334. if (init === void 0)
  6335. {
  6336. init = false;
  6337. }
  6338. var show = settings.get(settings.KEY.amountSymbol);
  6339. document.body.classList[show ? 'add' : 'remove'](tweakName);
  6340. if (init)
  6341. {
  6342. settings.observe(settings.KEY.amountSymbol, function ()
  6343. {
  6344. return setAmountSymbolVisibility();
  6345. });
  6346. }
  6347. }
  6348. setAmountSymbolVisibility(true);
  6349. }
  6350.  
  6351. function init()
  6352. {
  6353. tweakOil();
  6354. tweakSelection();
  6355. tweakStardust();
  6356. tweakSkillLevelText();
  6357. tweakFightDialog();
  6358. addAdditionalSkillBars();
  6359. highlightCookinglevel();
  6360. amountStyle();
  6361. }
  6362. styleTweaks.init = init;
  6363. })(styleTweaks || (styleTweaks = {}));
  6364.  
  6365. /**
  6366. * add ingame notification boxes
  6367. */
  6368. var notifBoxes;
  6369. (function (notifBoxes)
  6370. {
  6371. notifBoxes.name = 'notifBoxes';
  6372.  
  6373. function addNotifBox(imageKey, itemKey, showFront)
  6374. {
  6375. if (itemKey === void 0)
  6376. {
  6377. itemKey = null;
  6378. }
  6379. if (showFront === void 0)
  6380. {
  6381. showFront = false;
  6382. }
  6383. var notifBox = document.createElement('span');
  6384. notifBox.className = 'notif-box';
  6385. notifBox.id = 'notif-' + imageKey;
  6386. notifBox.style.display = 'none';
  6387. if (showFront)
  6388. {
  6389. notifBox.style.cssFloat = 'left';
  6390. }
  6391. notifBox.innerHTML = "<img src=\"images/" + imageKey + ".png\" class=\"image-icon-50\" id=\"notif-" + imageKey + "-img\">";
  6392. if (itemKey != null)
  6393. {
  6394. notifBox.innerHTML += "<span data-item-display=\"" + itemKey + "\" style=\"margin-left: 10px;\"></span>";
  6395. }
  6396. var notifArea = document.getElementById('notifaction-area');
  6397. if (notifArea)
  6398. {
  6399. notifArea.appendChild(notifBox);
  6400. }
  6401. return notifBox;
  6402. }
  6403.  
  6404. function addTreasureMap()
  6405. {
  6406. var notifBox = addNotifBox('treasureMap', null, true);
  6407. notifBox.setAttribute('onclick', 'openTab("items")');
  6408.  
  6409. function setVisibility()
  6410. {
  6411. var show = window.treasureMap > 0;
  6412. notifBox.style.display = show ? '' : 'none';
  6413. }
  6414. setVisibility();
  6415. observer.add('treasureMap', function ()
  6416. {
  6417. return setVisibility();
  6418. });
  6419. }
  6420.  
  6421. function addWorker()
  6422. {
  6423. var notifBox = addNotifBox('workers', null, true);
  6424.  
  6425. function setVisibility()
  6426. {
  6427. var show = window.workersTimer === 1;
  6428. notifBox.style.display = show ? '' : 'none';
  6429. }
  6430. setVisibility();
  6431. observer.add('workersTimer', function ()
  6432. {
  6433. return setVisibility();
  6434. });
  6435. }
  6436.  
  6437. function init()
  6438. {
  6439. addTreasureMap();
  6440. addWorker();
  6441. }
  6442. notifBoxes.init = init;
  6443. })(notifBoxes || (notifBoxes = {}));
  6444.  
  6445. /**
  6446. * extend market
  6447. */
  6448. var market;
  6449. (function (market)
  6450. {
  6451. market.name = 'market';
  6452. var detectedTedsUIOnce = false;
  6453.  
  6454. function detectTedsUI()
  6455. {
  6456. return detectedTedsUIOnce = detectedTedsUIOnce || typeof window.changeSetting === 'function';
  6457. }
  6458.  
  6459. function showOfferCancelCooldown()
  6460. {
  6461. if (detectTedsUI())
  6462. {
  6463. return;
  6464. }
  6465. addStyle("\n.market-slot-cancel:not([data-cooldown=\"0\"])\n{\n\tbackground: linear-gradient(hsla(12, 40%, 50%, 1), hsla(12, 40%, 40%, 1));\n\tcursor: not-allowed;\n\tposition: relative;\n}\n.market-slot-cancel:not([data-cooldown=\"0\"]):hover\n{\n\tbackground-color: hsla(0, 40%, 50%, 1);\n}\n.market-slot-cancel:not([data-cooldown=\"0\"])::after\n{\n\tcontent: attr(data-cooldown);\n\tposition: absolute;\n\tright: 10px;\n}\n\t\t");
  6466.  
  6467. function slotCooldown(i, init)
  6468. {
  6469. if (init === void 0)
  6470. {
  6471. init = false;
  6472. }
  6473. var cooldownKey = 'marketCancelCooldownSlot' + i;
  6474. var btn = document.getElementById('market-slot-' + i + '-cancel-btn');
  6475. if (btn)
  6476. {
  6477. btn.dataset.cooldown = detectTedsUI() ? '0' : getGameValue(cooldownKey).toString();
  6478. }
  6479. if (init)
  6480. {
  6481. observer.add(cooldownKey, function ()
  6482. {
  6483. return slotCooldown(i);
  6484. });
  6485. }
  6486. }
  6487. for (var i = 1; i <= 3; i++)
  6488. {
  6489. slotCooldown(i, true);
  6490. }
  6491. }
  6492.  
  6493. function init()
  6494. {
  6495. showOfferCancelCooldown();
  6496. }
  6497. market.init = init;
  6498. })(market || (market = {}));
  6499.  
  6500. /**
  6501. * init
  6502. */
  6503. var scriptInitialized = false;
  6504.  
  6505. function init()
  6506. {
  6507. console.info('[%s] "DH2 Fixed %s" up and running.', (new Date).toLocaleTimeString(), version);
  6508. scriptInitialized = true;
  6509. var initModules = [
  6510. settings
  6511. , notifications
  6512. , log
  6513. , gameEvents
  6514. , temporaryFixes
  6515. , crafting
  6516. , itemBoxes
  6517. , chat
  6518. , timer
  6519. , smelting
  6520. , fishingInfo
  6521. , recipeTooltips
  6522. , fixNumbers
  6523. , machineDialog
  6524. , amountInputs
  6525. , newTopbar
  6526. , styleTweaks
  6527. , notifBoxes
  6528. , market
  6529. ];
  6530. window.initModules = initModules;
  6531. for (var _i = 0, initModules_1 = initModules; _i < initModules_1.length; _i++)
  6532. {
  6533. var module = initModules_1[_i];
  6534. try
  6535. {
  6536. module.init();
  6537. }
  6538. catch (error)
  6539. {
  6540. console.error('Error during initialization in module "' + module.name + '":', error);
  6541. }
  6542. }
  6543. }
  6544. document.addEventListener('DOMContentLoaded', function ()
  6545. {
  6546. var oldValues = new Map();
  6547. var _doCommand = window.doCommand;
  6548. window.doCommand = function (data)
  6549. {
  6550. if (data.startsWith('REFRESH_ITEMS='))
  6551. {
  6552. oldValues = new Map();
  6553. for (var _i = 0, _a = window.jsItemArray; _i < _a.length; _i++)
  6554. {
  6555. var key = _a[_i];
  6556. oldValues.set(key, getGameValue(key));
  6557. }
  6558. _doCommand(data);
  6559. if (!scriptInitialized)
  6560. {
  6561. init();
  6562. }
  6563. return;
  6564. }
  6565. else if (!scriptInitialized)
  6566. {
  6567. if (data.startsWith('CHAT='))
  6568. {
  6569. var parts = data.substr(5).split('~');
  6570. return chat.newAddToChatBox(parts[0], parts[1], parts[2], parts[3], 0);
  6571. }
  6572. else if (data.startsWith('PM='))
  6573. {
  6574. return chat.newAddToChatBox(window.username, '0', '0', data.substr(3), 1);
  6575. }
  6576. }
  6577. var ret = commands.process(data);
  6578. if (ret === void 0)
  6579. {
  6580. ret = _doCommand(commands.formatData(data));
  6581. }
  6582. return ret;
  6583. };
  6584. var _refreshItemValues = window.refreshItemValues;
  6585. window.refreshItemValues = function (itemKeyList, firstLoad)
  6586. {
  6587. _refreshItemValues(itemKeyList, firstLoad);
  6588. for (var _i = 0, itemKeyList_1 = itemKeyList; _i < itemKeyList_1.length; _i++)
  6589. {
  6590. var key = itemKeyList_1[_i];
  6591. observer.notify(key, oldValues.get(key));
  6592. }
  6593. };
  6594. });
  6595.  
  6596. /**
  6597. * fix web socket errors
  6598. */
  6599. var main;
  6600. (function (main)
  6601. {
  6602. var WS_TIMEOUT_SEC = 30;
  6603. var WS_TIMEOUT_CODE = 3000;
  6604. var WS_OPEN_TIMEOUT_SEC = 2 * 60; // 2 minutes
  6605. // reload the page after 5 consecutive reconnect attempts without successfully opening the websocket once
  6606. var MAX_RECONNECTS = 5;
  6607.  
  6608. function webSocketLoaded(event)
  6609. {
  6610. if (window.webSocket == null)
  6611. {
  6612. console.error('WebSocket instance not initialized!');
  6613. return;
  6614. }
  6615. // cache old event listener
  6616. var _onClose = window.webSocket.onclose;
  6617. var _onError = window.webSocket.onerror;
  6618. var _onMessage = window.webSocket.onmessage;
  6619. var _onOpen = window.webSocket.onopen;
  6620. var commandQueue = [];
  6621. var _cBytes = window.cBytes;
  6622. window.cBytes = function (command)
  6623. {
  6624. if (window.webSocket && window.webSocket.readyState === WebSocket.OPEN)
  6625. {
  6626. _cBytes(command);
  6627. }
  6628. else
  6629. {
  6630. commandQueue.push(command);
  6631. }
  6632. };
  6633. var pageLoaded = false;
  6634. var wsTimeout = null;
  6635. var reconnectAttempts = 0;
  6636.  
  6637. function onTimeout()
  6638. {
  6639. wsTimeout = null;
  6640. // renew the websocket
  6641. if (reconnectAttempts <= MAX_RECONNECTS)
  6642. {
  6643. window.webSocket = new WebSocket(window.SSL_ENABLED);
  6644. window.ignoreBytesTracker = Date.now();
  6645. initWSListener(window.webSocket);
  6646. reconnectAttempts++;
  6647. }
  6648. if (window.webSocket)
  6649. {
  6650. window.webSocket.close(WS_TIMEOUT_CODE, 'Connection timed out after ' + WS_TIMEOUT_SEC + ' seconds');
  6651. }
  6652. }
  6653.  
  6654. function updateWSTimeout()
  6655. {
  6656. if (wsTimeout)
  6657. {
  6658. window.clearTimeout(wsTimeout);
  6659. }
  6660. wsTimeout = window.setTimeout(onTimeout, WS_TIMEOUT_SEC * 1e3);
  6661. }
  6662. var messageQueue = [];
  6663.  
  6664. function onMessage(event)
  6665. {
  6666. if (pageLoaded)
  6667. {
  6668. updateWSTimeout();
  6669. return _onMessage.call(this, event);
  6670. }
  6671. else
  6672. {
  6673. messageQueue.push(event);
  6674. }
  6675. };
  6676. var wsOpenTimeout = null;
  6677.  
  6678. function onOpenTimeout()
  6679. {
  6680. wsOpenTimeout = null;
  6681. location.reload();
  6682. }
  6683.  
  6684. function onOpen(event)
  6685. {
  6686. reconnectAttempts = 0;
  6687. if (wsOpenTimeout)
  6688. {
  6689. window.clearTimeout(wsOpenTimeout);
  6690. wsOpenTimeout = null;
  6691. }
  6692. // do the handshake first
  6693. _onOpen.call(this, event);
  6694. commandQueue.forEach(function (command)
  6695. {
  6696. return window.cBytes(command);
  6697. });
  6698. }
  6699.  
  6700. function onError(event)
  6701. {
  6702. console.error('error in websocket:', event);
  6703. return _onError.call(this, event);
  6704. }
  6705.  
  6706. function onClose(event)
  6707. {
  6708. console.info('websocket closed:', event);
  6709. if (event.code !== WS_TIMEOUT_CODE || reconnectAttempts > MAX_RECONNECTS)
  6710. {
  6711. location.reload();
  6712. }
  6713. return _onClose.call(this, event);
  6714. }
  6715.  
  6716. function initWSListener(ws)
  6717. {
  6718. if (ws.readyState === WebSocket.CONNECTING)
  6719. {
  6720. wsOpenTimeout = window.setTimeout(onOpenTimeout, WS_OPEN_TIMEOUT_SEC * 1e3);
  6721. }
  6722. ws.onclose = onClose;
  6723. ws.onerror = onError;
  6724. ws.onmessage = onMessage;
  6725. ws.onopen = onOpen;
  6726. }
  6727. initWSListener(window.webSocket);
  6728. document.addEventListener('DOMContentLoaded', function ()
  6729. {
  6730. pageLoaded = true;
  6731. messageQueue.forEach(function (event)
  6732. {
  6733. return window.webSocket.onmessage(event);
  6734. });
  6735. });
  6736. }
  6737.  
  6738. function isScriptElement(el)
  6739. {
  6740. return el.nodeName === 'SCRIPT';
  6741. }
  6742.  
  6743. function isWebSocketScript(script)
  6744. {
  6745. return script.src.includes('socket.js');
  6746. }
  6747. var found = false;
  6748. var scripts = document.head ? document.head.querySelectorAll('script') : [];
  6749. for (var i = 0; i < scripts.length; i++)
  6750. {
  6751. if (isWebSocketScript(scripts[i]))
  6752. {
  6753. // does this work?
  6754. scripts[i].onload = webSocketLoaded;
  6755. found = true;
  6756. }
  6757. }
  6758. if (!found)
  6759. {
  6760. // create an observer instance
  6761. var mutationObserver_1 = new MutationObserver(function (mutationList)
  6762. {
  6763. mutationList.forEach(function (mutation)
  6764. {
  6765. if (mutation.addedNodes.length === 0)
  6766. {
  6767. return;
  6768. }
  6769. for (var i = 0; i < mutation.addedNodes.length; i++)
  6770. {
  6771. var node = mutation.addedNodes[i];
  6772. if (isScriptElement(node) && isWebSocketScript(node))
  6773. {
  6774. mutationObserver_1.disconnect();
  6775. node.onload = webSocketLoaded;
  6776. return;
  6777. }
  6778. }
  6779. });
  6780. });
  6781. mutationObserver_1.observe(document.head
  6782. , {
  6783. childList: true
  6784. });
  6785. }
  6786. // fix scrollText (e.g. when joining the game and receiving xp at that moment)
  6787. window.mouseX = window.innerWidth / 2;
  6788. window.mouseY = window.innerHeight / 2;
  6789. })(main || (main = {}));
  6790.  
  6791. })();