DH2 Fixed's temporary fixed

Improve Diamond Hunt 2's DH2 Fixed

目前为 2018-01-17 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name DH2 Fixed's temporary fixed
  3. // @namespace FileFace
  4. // @description Improve Diamond Hunt 2's DH2 Fixed
  5. // @version 1.1.0
  6. // @author Zorbing
  7. // @license ISC; http://opensource.org/licenses/ISC
  8. // @grant none
  9. // @run-at document-start
  10. // @include *.diamondhunt.co/*
  11. // @match https://www.diamondhunt.co
  12. // ==/UserScript==
  13.  
  14. // This is a continue developing script from Zorbing's since he has been inactive for a long time, with help from other players
  15. // It might be removed once the author is back and he ask to put it down
  16. // Credits: bazzaspam, agrodon, ktnn
  17. /**
  18. * ISC License (ISC)
  19. *
  20. * Copyright (c) 2017, Martin Boekhoff
  21. *
  22. * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
  23. * granted, provided that the above copyright notice and this permission notice appear in all copies.
  24. *
  25. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  26. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  27. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  28. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  29. * PERFORMANCE OF THIS SOFTWARE.
  30. *
  31. * Source: http://opensource.org/licenses/ISC
  32. */
  33.  
  34. (function ()
  35. {
  36. 'use strict';
  37. var version = '0.246.2';
  38. var buildTime = new Date('2017-08-12T16:37:11.942Z');
  39. var win = typeof unsafeWindow !== 'undefined' ? unsafeWindow : window;
  40. "use strict";
  41.  
  42. /**
  43. * observer
  44. */
  45. var observer;
  46. (function (observer)
  47. {
  48. observer.GAME_TICK_KEY = 'dh2.gameTick';
  49. var observedKeys = new Map();
  50.  
  51. function add(key, fn)
  52. {
  53. if (key instanceof Array)
  54. {
  55. for (var _i = 0, key_1 = key; _i < key_1.length; _i++)
  56. {
  57. var k = key_1[_i];
  58. add(k, fn);
  59. }
  60. }
  61. else
  62. {
  63. if (!observedKeys.has(key))
  64. {
  65. observedKeys.set(key, new Set());
  66. }
  67. observedKeys.get(key).add(fn);
  68. }
  69. return fn;
  70. }
  71. observer.add = add;
  72.  
  73. function notify(key, oldValue)
  74. {
  75. var newValue = getGameValue(key);
  76. if (observedKeys.has(key))
  77. {
  78. observedKeys.get(key).forEach(function (fn)
  79. {
  80. return fn(key, oldValue, newValue);
  81. });
  82. }
  83. }
  84. observer.notify = notify;
  85.  
  86. function notifyTick()
  87. {
  88. notify(observer.GAME_TICK_KEY, Math.floor(now() / 1000));
  89. }
  90. observer.notifyTick = notifyTick;
  91.  
  92. function remove(key, fn)
  93. {
  94. if (key instanceof Array)
  95. {
  96. var ret = [];
  97. for (var _i = 0, key_2 = key; _i < key_2.length; _i++)
  98. {
  99. var k = key_2[_i];
  100. ret.push(remove(k, fn));
  101. }
  102. return ret;
  103. }
  104. if (!observedKeys.has(key))
  105. {
  106. return false;
  107. }
  108. return observedKeys.get(key).delete(fn);
  109. }
  110. observer.remove = remove;
  111.  
  112. function addTick(fn)
  113. {
  114. return add(observer.GAME_TICK_KEY, fn);
  115. }
  116. observer.addTick = addTick;
  117.  
  118. function removeTick(fn)
  119. {
  120. return remove(observer.GAME_TICK_KEY, fn);
  121. }
  122. observer.removeTick = removeTick;
  123. })(observer || (observer = {}));
  124. /**
  125. * global constants
  126. */
  127. var PLUS_MINUS_SIGN = String.fromCharCode(177);
  128. var TIER_LEVELS = ['empty', 'sapphire', 'emerald', 'ruby', 'diamond'];
  129. var TIER_NAMES = ['Standard', 'Sapphire', 'Emerald', 'Ruby', 'Diamond'];
  130. var TIER_ITEMS = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'trowel', 'fishingRod', 'chisel'];
  131. var ORB_ITEMS = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'trowel', 'fishingRod', 'chisel', 'oilPipe'];
  132. var TIER_ITEMS_NOT_BINDABLE = ['rake', 'trowel'];
  133. var FURNACE_LEVELS = ['stone', 'bronze', 'iron', 'silver', 'gold', 'promethium'];
  134. var OVEN_LEVELS = ['bronze', 'iron', 'silver', 'gold', 'promethium'];
  135. var WAND_LEVELS = ['wooden', 'oak', 'willow', 'maple', 'stardust'];
  136. var OIL_STORAGE_SIZES = [10e3, 50e3, 100e3, 300e3, 600e3, 2e6];
  137. var RECIPE_MAX = {
  138. 'brewing':
  139. {
  140. 'braveryPotion':
  141. {
  142. max: 1
  143. }
  144. , 'stardustCrystalPotion':
  145. {
  146. max: 1
  147. }
  148. }
  149. , 'cooksBook':
  150. {}
  151. , 'crafting':
  152. {
  153. 'drills':
  154. {
  155. max: 10
  156. }
  157. , 'crushers':
  158. {
  159. max: 10
  160. }
  161. , 'giantDrills':
  162. {
  163. max: 10
  164. }
  165. , 'excavators':
  166. {
  167. max: 10
  168. }
  169. , 'oilPipe':
  170. {
  171. max: 1
  172. }
  173. , 'pumpjacks':
  174. {
  175. max: 10
  176. }
  177. , 'rowBoat':
  178. {
  179. max: 1
  180. }
  181. , 'canoe':
  182. {
  183. max: 1
  184. }
  185. , 'sailBoat':
  186. {
  187. max: 1
  188. }
  189. , 'steamBoat':
  190. {
  191. max: 1
  192. }
  193. // thanks aguyd
  194. , 'bonemealBin':
  195. {
  196. extraKeys: ['boundFilledBonemealBin']
  197. , max: 1
  198. }
  199. , 'oilFactory':
  200. {
  201. max: 1
  202. }
  203. , 'brewingKit':
  204. {
  205. max: 1
  206. }
  207. , 'rocket':
  208. {
  209. max: 1
  210. }
  211. }
  212. , 'magic':
  213. {}
  214. };
  215. var SMELTING_REQUIREMENTS = {
  216. 'glass':
  217. {
  218. sand: 1
  219. , oil: 10
  220. }
  221. , 'bronzeBar':
  222. {
  223. copper: 1
  224. , tin: 1
  225. , oil: 10
  226. }
  227. , 'ironBar':
  228. {
  229. iron: 1
  230. , oil: 100
  231. }
  232. , 'silverBar':
  233. {
  234. silver: 1
  235. , oil: 300
  236. }
  237. , 'goldBar':
  238. {
  239. gold: 1
  240. , oil: 1e3
  241. }
  242. , 'promethiumBar':
  243. {
  244. promethium: 1
  245. , charcoal: 1
  246. }
  247. };
  248. var PLANT_NAME = {
  249. '1': 'Dark Mushrooms'
  250. , '2': 'Red Mushrooms'
  251. , '3': 'Dotted Green Leaves'
  252. , '4': 'Green Leaves'
  253. , '5': 'Lime Leaves'
  254. , '6': 'Gold Leaves'
  255. , '7': 'Striped Gold Leaves'
  256. , '8': 'Crystal Leaves'
  257. , '9': 'Striped Crystal Leaves'
  258. , '10': 'Blewit Mushrooms'
  259. , '11': 'Snapegrass'
  260. , '12': 'Tree'
  261. , '13': 'Oak Tree'
  262. , '14': 'Wheat'
  263. , '15': 'Willow Tree'
  264. , '16': 'Grass'
  265. , '17': 'Maple Tree'
  266. , '18': 'Stardust Tree'
  267. , '19': 'Carrots'
  268. , '20': 'Tomatoes'
  269. , '21': 'Potatoes'
  270. , '22': 'Strange Leaf Tree'
  271. , '23': 'Light Mushrooms'
  272. , '24': 'Pumpkins'
  273. , '25': 'Ancient Tree'
  274. , '26': 'Stardust Tree'
  275. , '27': 'White Leaf'
  276. };
  277. var SKILL_LIST = ['mining', 'crafting', 'woodcutting', 'farming', 'brewing', 'combat', 'fishing', 'cooking', 'magic'];
  278. var AREA_LIST = ['fields', 'forests', 'caves', 'volcano', 'northFields', 'hauntedMansion'];
  279. var AREA_NAMES = ['Fields', 'Forests', 'Caves', 'Volcano', 'Northern Fields', 'Haunted Mansion', 'Moon', 'Dark Forest'];
  280.  
  281. function getAreaName(areaId)
  282. {
  283. if (areaId === 33)
  284. {
  285. return 'Quest';
  286. }
  287. else if (areaId === 34)
  288. {
  289. return "Faradox's Tombs";
  290. }
  291. else if (areaId === 35)
  292. {
  293. return "Goblin Hideout";
  294. }
  295. else
  296. {
  297. return AREA_NAMES[areaId];
  298. }
  299. }
  300. var MONSTER_NAMES = ['Chicken', 'Rat', 'Bee', 'Snake', 'Forest Tree', 'Thief', 'Bear', 'Bat', 'Skeleton', 'Golem', 'Fire Bird', 'Volcano Mage', 'Lizard', 'Northern Tree', 'Ice Bird', 'Phantom', 'Ghost', 'Grim Reaper', 'Troll', 'Five Eyed', 'Robot', 'Dark Mage', 'Pirate Skeleton', 'Dark Witch'];
  301.  
  302. function getMonsterName(monsterId)
  303. {
  304. if (monsterId === 101)
  305. {
  306. return 'Ghostly Old Mage';
  307. }
  308. else if (monsterId === 99)
  309. {
  310. return 'Evil Snake';
  311. }
  312. else if (monsterId === 100)
  313. {
  314. return 'Easter Bunny';
  315. }
  316. else if (monsterId === 102)
  317. {
  318. return '1st Tomb Monster';
  319. }
  320. else if (monsterId === 103)
  321. {
  322. return '2nd Tomb Monster';
  323. }
  324. else if (monsterId === 104)
  325. {
  326. return '3rd Tomb Monster';
  327. }
  328. else if (monsterId === 105)
  329. {
  330. return '4th Tomb Monster';
  331. }
  332. else if (monsterId === 106)
  333. {
  334. return '5th Tomb Monster';
  335. }
  336. else if (monsterId === 107)
  337. {
  338. return 'Gem Goblin';
  339. }
  340. else
  341. {
  342. return MONSTER_NAMES[monsterId];
  343. }
  344. }
  345. var FISH_XP = {
  346. 'rawShrimp': 50
  347. , 'rawSardine': 500
  348. , 'rawSalmon': 700
  349. , 'rawTuna': 3e3
  350. , 'rawLobster': 5e3
  351. , 'rawSwordfish': 5e3
  352. , 'rawEel': 6e3
  353. , 'rawShark': 12e3
  354. , 'rawWhale': 20e3
  355. , 'rawRainbowFish': 30e3
  356. };
  357. var BOAT_LIST = ['rowBoat', 'canoe', 'sailBoat', 'steamBoat'];
  358. var TRIP_DURATION = {
  359. 'rowBoat': 3
  360. , 'canoe': 5
  361. , 'sailBoat': 7
  362. , 'steamBoat': 10
  363. };
  364. var MAX_ROCKET_MOON_KM = 384400;
  365. var MAX_ROCKET_MARS_KM = 54600000;
  366. var format;
  367. (function (format)
  368. {
  369. var UNITS = [
  370. {
  371. threshold: 10e3
  372. , factor: 1e3
  373. , token: 'k'
  374. }
  375. , {
  376. threshold: 1e6
  377. , factor: 1e6
  378. , token: 'M'
  379. }
  380. , {
  381. threshold: 1e9
  382. , factor: 1e9
  383. , token: 'B'
  384. }
  385. , {
  386. threshold: 1e12
  387. , factor: 1e12
  388. , token: 'T'
  389. }
  390. , {
  391. threshold: 1e15
  392. , factor: 1e15
  393. , token: 'Q'
  394. }];
  395. var TIME_STEPS = [
  396. {
  397. threshold: 1
  398. , name: 'second'
  399. , short: 'sec'
  400. , padp: 0
  401. }
  402. , {
  403. threshold: 60
  404. , name: 'minute'
  405. , short: 'min'
  406. , padp: 0
  407. }
  408. , {
  409. threshold: 3600
  410. , name: 'hour'
  411. , short: 'h'
  412. , padp: 1
  413. }
  414. , {
  415. threshold: 86400
  416. , name: 'day'
  417. , short: 'd'
  418. , padp: 2
  419. }];
  420.  
  421. function ensureNumber(num)
  422. {
  423. return (typeof num === 'number' ? num : Number(num));
  424. }
  425.  
  426. function number(num, shorten)
  427. {
  428. if (shorten === void 0)
  429. {
  430. shorten = false;
  431. }
  432. num = ensureNumber(num);
  433. if (shorten)
  434. {
  435. for (var i = UNITS.length - 1; i >= 0; i--)
  436. {
  437. var unit = UNITS[i];
  438. if (num >= unit.threshold)
  439. {
  440. return number(Math.round(num / unit.factor)) + unit.token;
  441. }
  442. }
  443. }
  444. return num.toLocaleString('en');
  445. }
  446. format.number = number;
  447.  
  448. function numbersInText(text)
  449. {
  450. return text.replace(/\d(?:[\d',\.]*\d)?/g, function (numStr)
  451. {
  452. return number(numStr.replace(/\D/g, ''));
  453. });
  454. }
  455. format.numbersInText = numbersInText;
  456. // use time format established in DHQoL (https://greasyfork.org/scripts/16041-dhqol)
  457. function timer(timer, shorten)
  458. {
  459. if (shorten === void 0)
  460. {
  461. shorten = true;
  462. }
  463. if (typeof timer === 'string')
  464. {
  465. timer = parseInt(timer, 10);
  466. }
  467. timer = Math.max(timer, 0);
  468. var days = Math.floor(timer / 86400); // 24 * 60 * 60
  469. var hours = Math.floor((timer % 86400) / 3600); // 60 * 60
  470. var minutes = Math.floor((timer % 3600) / 60);
  471. var seconds = timer % 60;
  472. return (shorten && days === 0 ? '' : days + 'd ')
  473. + (shorten && days === 0 && hours === 0 ? '' : zeroPadLeft(hours) + ':')
  474. + zeroPadLeft(minutes) + ':'
  475. + zeroPadLeft(seconds);
  476. }
  477. format.timer = timer;
  478.  
  479. function time2NearestUnit(time, long)
  480. {
  481. if (long === void 0)
  482. {
  483. long = false;
  484. }
  485. var step = TIME_STEPS[0];
  486. for (var i = TIME_STEPS.length - 1; i > 0; i--)
  487. {
  488. if (time >= TIME_STEPS[i].threshold)
  489. {
  490. step = TIME_STEPS[i];
  491. break;
  492. }
  493. }
  494. var factor = Math.pow(10, step.padp);
  495. var num = Math.round(time / step.threshold * factor) / factor;
  496. var unit = long ? step.name + (num === 1 ? '' : 's') : step.short;
  497. return num + ' ' + unit;
  498. }
  499. format.time2NearestUnit = time2NearestUnit;
  500.  
  501. function sec2Str(seconds)
  502. {
  503. seconds = Number(seconds);
  504. if (seconds < 0)
  505. {
  506. return seconds.toString();
  507. }
  508. var s = seconds % 60;
  509. var m = Math.floor(seconds / 60) % 60;
  510. var h = Math.floor(seconds / 3600);
  511. var strs = [];
  512. if (h > 0)
  513. {
  514. strs.push(h + ' hour' + (h == 1 ? '' : 's'));
  515. }
  516. if (m > 0)
  517. {
  518. strs.push(m + ' minute' + (m == 1 ? '' : 's'));
  519. }
  520. if (s > 0)
  521. {
  522. strs.push(s + ' second' + (s == 1 ? '' : 's'));
  523. }
  524. if (strs.length > 1)
  525. {
  526. var glue = ' and ';
  527. for (var i = strs.length - 2; i >= 0; i--)
  528. {
  529. strs[i] = strs[i] + glue + strs[i + 1];
  530. glue = ', ';
  531. }
  532. return strs[0];
  533. }
  534. else
  535. {
  536. return strs[0] || '';
  537. }
  538. }
  539. format.sec2Str = sec2Str;
  540.  
  541. function min2Str(minutes)
  542. {
  543. return sec2Str(Number(minutes) * 60);
  544. }
  545. format.min2Str = min2Str;
  546. })(format || (format = {}));
  547.  
  548. /**
  549. * general functions
  550. */
  551. function getStyle(elId)
  552. {
  553. var id = elId != null ? 'style-' + elId : null;
  554. var styleElement = id != null ? document.getElementById(id) : null;
  555. if (styleElement == null)
  556. {
  557. styleElement = document.createElement('style');
  558. if (id != null)
  559. {
  560. styleElement.id = id;
  561. }
  562. styleElement.type = 'text/css';
  563. document.head.appendChild(styleElement);
  564. }
  565. return styleElement;
  566. }
  567.  
  568. function addStyle(styleCode, elId)
  569. {
  570. var styleElement = getStyle(elId);
  571. styleElement.innerHTML += styleCode;
  572. }
  573.  
  574. function zeroPadLeft(num)
  575. {
  576. return (num < 10 ? '0' : '') + num;
  577. }
  578.  
  579. function capitalize(str)
  580. {
  581. return str[0].toUpperCase() + str.substr(1);
  582. }
  583.  
  584. function key2Name(key, lowerCase)
  585. {
  586. if (lowerCase === void 0)
  587. {
  588. lowerCase = false;
  589. }
  590. var name = key.replace(/[A-Z]/g, function (c)
  591. {
  592. return ' ' + (lowerCase ? c.toLowerCase() : c);
  593. });
  594. return lowerCase ? name : capitalize(name);
  595. }
  596.  
  597. function pluralize(name)
  598. {
  599. return name.replace(/([^aeiou])y$/, '$1ie').replace(/s?$/, '') + 's';
  600. }
  601.  
  602. function split2Words(str, char)
  603. {
  604. if (char === void 0)
  605. {
  606. char = ' ';
  607. }
  608. return str.replace(/[A-Z]/g, char + '$&');
  609. }
  610.  
  611. function getBoundKey(key)
  612. {
  613. return 'bound' + capitalize(key);
  614. }
  615.  
  616. function getTierKey(key, tierLevel)
  617. {
  618. return TIER_LEVELS[tierLevel] + capitalize(key);
  619. }
  620.  
  621. function getWikiaKey(key)
  622. {
  623. return key2Name(key.replace(/^bound-?|^special-case-/i, '').replace(/\d+[km]?$/i, ''))
  624. .replace(/^\s/, '').replace(/[ -]/g, '_')
  625. .replace(/^(?:Empty|Sapphire|Emerald|Ruby|Diamond|Raw|Uncooked|Filled)_/, '')
  626. .replace(/^(?:Bronze|Iron|Silver|Gold|Promethium|Runite)_(?!Bar)/, '')
  627. .replace(/^Npc_/, 'Monster_')
  628. .replace(/_(?:Unlocked|Quest)$/, '');
  629. }
  630.  
  631. function getWikiaLink(key)
  632. {
  633. return 'http://diamondhuntonline.wikia.com/wiki/' + getWikiaKey(key);
  634. }
  635.  
  636. function now()
  637. {
  638. return (new Date()).getTime();
  639. }
  640.  
  641. function ensureTooltip(id, target)
  642. {
  643. var tooltipId = 'tooltip-' + id;
  644. var tooltipEl = document.getElementById(tooltipId);
  645. if (!tooltipEl)
  646. {
  647. tooltipEl = document.createElement('div');
  648. tooltipEl.id = tooltipId;
  649. tooltipEl.style.display = 'none';
  650. var tooltipList = document.getElementById('tooltip-list');
  651. tooltipList.appendChild(tooltipEl);
  652. }
  653. // ensure binded events to show the tooltip
  654. if (target.dataset.tooltipId == null)
  655. {
  656. target.dataset.tooltipId = tooltipId;
  657. win.$(target).bind(
  658. {
  659. mousemove: win.changeTooltipPosition
  660. , mouseenter: win.showTooltip
  661. , mouseleave: function (event)
  662. {
  663. var target = event.target;
  664. var parent = target.parentElement;
  665. // ensure tooltips inside an tooltip element is possible
  666. if (!!target.dataset.tooltipId && parent && !!parent.dataset.tooltipId)
  667. {
  668. win.showTooltip.call(parent, event);
  669. }
  670. else
  671. {
  672. win.hideTooltip(event);
  673. }
  674. }
  675. });
  676. }
  677. return tooltipEl;
  678. }
  679. var timeStr2Sec = (function ()
  680. {
  681. var unitFactors = {
  682. 'd': 24 * 60 * 60
  683. , 'h': 60 * 60
  684. , 'm': 60
  685. , 's': 1
  686. };
  687. return function timeStr2Sec(str)
  688. {
  689. return str
  690. .replace(/(\d+)([hms])/g, function (wholeMatch, num, unit)
  691. {
  692. return parseInt(num) * (unitFactors[unit] || 1) + '+';
  693. })
  694. .split('+')
  695. .map(function (s)
  696. {
  697. return parseInt(s, 10);
  698. })
  699. .filter(function (n)
  700. {
  701. return !isNaN(n);
  702. })
  703. .reduce(function (p, c)
  704. {
  705. return p + c;
  706. }, 0);
  707. };
  708. })();
  709.  
  710. function getGameValue(key)
  711. {
  712. return win[key];
  713. }
  714.  
  715. function getFurnaceLevel()
  716. {
  717. for (var i = FURNACE_LEVELS.length - 1; i >= 0; i--)
  718. {
  719. if (getGameValue(getBoundKey(FURNACE_LEVELS[i] + 'Furnace')) > 0)
  720. {
  721. return i;
  722. }
  723. }
  724. return -1;
  725. }
  726.  
  727. function getFurnaceLevelName()
  728. {
  729. return FURNACE_LEVELS[getFurnaceLevel()] || '';
  730. }
  731.  
  732. function getPrice(item)
  733. {
  734. var price = win.getPrice(item);
  735. if (typeof price === 'number')
  736. {
  737. return price;
  738. }
  739. var match = price.match(/(\d+)([kM])/);
  740. if (!match)
  741. {
  742. return parseInt(price, 10);
  743. }
  744. var FACTORS = {
  745. 'k': 1e3
  746. , 'M': 1e6
  747. };
  748. return parseInt(match[1], 10) * (FACTORS[match[2]] || 1);
  749. }
  750.  
  751. function doGet(url)
  752. {
  753. return new Promise(function (resolve, reject)
  754. {
  755. var request = new XMLHttpRequest();
  756. request.onreadystatechange = function (event)
  757. {
  758. if (request.readyState != XMLHttpRequest.DONE)
  759. {
  760. return;
  761. }
  762. if (request.status != 200)
  763. {
  764. return reject(event);
  765. }
  766. resolve(request.responseText);
  767. };
  768. request.open('GET', url);
  769. request.send();
  770. });
  771. }
  772.  
  773. function removeWhitespaceChildNodes(el)
  774. {
  775. for (var i = 0; i < el.childNodes.length; i++)
  776. {
  777. var child = el.childNodes.item(i);
  778. if (child.nodeType === Node.TEXT_NODE && /^\s*$/.test(child.textContent || ''))
  779. {
  780. el.removeChild(child);
  781. i--;
  782. }
  783. }
  784. }
  785.  
  786. function debounce(func, wait, immediate)
  787. {
  788. var timeout;
  789. return function ()
  790. {
  791. var _this = this;
  792. var args = [];
  793. for (var _i = 0; _i < arguments.length; _i++)
  794. {
  795. args[_i] = arguments[_i];
  796. }
  797. var callNow = immediate && !timeout;
  798. timeout && clearTimeout(timeout);
  799. timeout = setTimeout(function ()
  800. {
  801. timeout = null;
  802. if (!immediate)
  803. {
  804. func.apply(_this, args);
  805. }
  806. }, wait);
  807. if (callNow)
  808. {
  809. func.apply(this, args);
  810. }
  811. };
  812. }
  813.  
  814. function passThis(fn)
  815. {
  816. return function ()
  817. {
  818. var args = [];
  819. for (var _i = 0; _i < arguments.length; _i++)
  820. {
  821. args[_i] = arguments[_i];
  822. }
  823. return fn.apply(void 0, [this].concat(args));
  824. };
  825. }
  826. /**
  827. * persistence store
  828. */
  829. var store;
  830. (function (store)
  831. {
  832. var oldPrefix = 'dh2-';
  833. var storePrefix = 'dh2.';
  834.  
  835. function update(key, keepOldValue)
  836. {
  837. if (keepOldValue === void 0)
  838. {
  839. keepOldValue = true;
  840. }
  841. if (localStorage.hasOwnProperty(oldPrefix + key))
  842. {
  843. if (keepOldValue)
  844. {
  845. localStorage.setItem(storePrefix + key, localStorage.getItem(oldPrefix + key));
  846. }
  847. localStorage.removeItem(oldPrefix + key);
  848. }
  849. }
  850. var changeListener = new Map();
  851.  
  852. function changeDetected(key, oldValue, newValue)
  853. {
  854. if (changeListener.has(key))
  855. {
  856. setTimeout(function ()
  857. {
  858. changeListener.get(key).forEach(function (fn)
  859. {
  860. return fn(key, oldValue, newValue);
  861. });
  862. });
  863. }
  864. }
  865.  
  866. function watchFn(fnName)
  867. {
  868. var _fn = localStorage[fnName];
  869. localStorage[fnName] = function (key)
  870. {
  871. var args = [];
  872. for (var _i = 1; _i < arguments.length; _i++)
  873. {
  874. args[_i - 1] = arguments[_i];
  875. }
  876. var oldValue = localStorage.getItem(key);
  877. _fn.apply(localStorage, [key].concat(args));
  878. var newValue = localStorage.getItem(key);
  879. if (oldValue !== newValue)
  880. {
  881. changeDetected(key, oldValue, newValue);
  882. }
  883. };
  884. }
  885. watchFn('setItem');
  886. watchFn('removeItem');
  887. var _clear = localStorage.clear;
  888. localStorage.clear = function ()
  889. {
  890. var oldValues = new Map();
  891. for (var i = 0; i < localStorage.length; i++)
  892. {
  893. var key = localStorage.key(i);
  894. oldValues.set(key, localStorage.getItem(key));
  895. }
  896. _clear();
  897. for (var key in oldValues)
  898. {
  899. var newValue = localStorage.getItem(key);
  900. if (oldValues.get(key) !== newValue)
  901. {
  902. changeDetected(key, oldValues.get(key), newValue);
  903. }
  904. }
  905. };
  906.  
  907. function addChangeListener(key, fn)
  908. {
  909. if (!changeListener.has(key))
  910. {
  911. changeListener.set(key, new Set());
  912. }
  913. changeListener.get(key).add(fn);
  914. }
  915. store.addChangeListener = addChangeListener;
  916.  
  917. function removeChangeListener(key, fn)
  918. {
  919. if (changeListener.has(key))
  920. {
  921. changeListener.get(key).delete(fn);
  922. }
  923. }
  924. store.removeChangeListener = removeChangeListener;
  925.  
  926. function get(key)
  927. {
  928. update(key);
  929. var value = localStorage.getItem(storePrefix + key);
  930. if (value != null)
  931. {
  932. try
  933. {
  934. return JSON.parse(value);
  935. }
  936. catch (e)
  937. {}
  938. }
  939. return value;
  940. }
  941. store.get = get;
  942.  
  943. function has(key)
  944. {
  945. update(key);
  946. return localStorage.hasOwnProperty(storePrefix + key);
  947. }
  948. store.has = has;
  949.  
  950. function remove(key)
  951. {
  952. update(key, false);
  953. localStorage.removeItem(storePrefix + key);
  954. }
  955. store.remove = remove;
  956.  
  957. function set(key, value)
  958. {
  959. update(key, false);
  960. localStorage.setItem(storePrefix + key, JSON.stringify(value));
  961. }
  962. store.set = set;
  963. })(store || (store = {}));
  964.  
  965. var settings;
  966. (function (settings)
  967. {
  968. settings.name = 'settings';
  969. var DIALOG_WIDTH = 450;
  970. var KEY;
  971. (function (KEY)
  972. {
  973. KEY[KEY["hideCraftingRecipes"] = 0] = "hideCraftingRecipes";
  974. KEY[KEY["hideUselessItems"] = 1] = "hideUselessItems";
  975. KEY[KEY["useNewChat"] = 2] = "useNewChat";
  976. KEY[KEY["colorizeChat"] = 3] = "colorizeChat";
  977. KEY[KEY["intelligentScrolling"] = 4] = "intelligentScrolling";
  978. KEY[KEY["showTimestamps"] = 5] = "showTimestamps";
  979. KEY[KEY["showIcons"] = 6] = "showIcons";
  980. KEY[KEY["showTags"] = 7] = "showTags";
  981. KEY[KEY["enableSpamDetection"] = 8] = "enableSpamDetection";
  982. KEY[KEY["useTombNotif"] = 9] = "useTombNotif";
  983. KEY[KEY["useWorkerNotif"] = 10] = "useWorkerNotif";
  984. KEY[KEY["useBobsUncleNotif"] = 11] = "useBobsUncleNotif";
  985. KEY[KEY["showNotifications"] = 12] = "showNotifications";
  986. KEY[KEY["showEssencePopup"] = 13] = "showEssencePopup";
  987. KEY[KEY["wikiaLinks"] = 14] = "wikiaLinks";
  988. KEY[KEY["newXpAnimation"] = 15] = "newXpAnimation";
  989. KEY[KEY["amountSymbol"] = 16] = "amountSymbol";
  990. KEY[KEY["showTabTimer"] = 17] = "showTabTimer";
  991. KEY[KEY["showLootTab"] = 18] = "showLootTab";
  992. KEY[KEY["useEfficiencyStyle"] = 19] = "useEfficiencyStyle";
  993. KEY[KEY["makeNumberInputs"] = 20] = "makeNumberInputs";
  994. KEY[KEY["addKeepInput"] = 21] = "addKeepInput";
  995. KEY[KEY["addMaxBtn"] = 22] = "addMaxBtn";
  996. KEY[KEY["highlightUnplantableSeed"] = 23] = "highlightUnplantableSeed";
  997. KEY[KEY["showSdChange"] = 24] = "showSdChange";
  998. KEY[KEY["usePotionWarning"] = 25] = "usePotionWarning";
  999. KEY[KEY["showCaptions"] = 26] = "showCaptions";
  1000. KEY[KEY["syncPriceHistory"] = 27] = "syncPriceHistory";
  1001. KEY[KEY["useNewToolbar"] = 28] = "useNewToolbar";
  1002. KEY[KEY["changeMachineDialog"] = 29] = "changeMachineDialog";
  1003. })(KEY = settings.KEY || (settings.KEY = {}));;
  1004. var CFG = (_a = {}
  1005. , _a[KEY.hideCraftingRecipes] = {
  1006. name: 'Hide crafting recipes of finished items'
  1007. , 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>"
  1008. , defaultValue: true
  1009. }
  1010. , _a[KEY.hideUselessItems] = {
  1011. name: 'Hide useless items'
  1012. , 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>"
  1013. , defaultValue: false
  1014. }
  1015. , _a[KEY.useNewChat] = {
  1016. name: 'Use the new chat'
  1017. , 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"
  1018. , defaultValue: true
  1019. }
  1020. , _a[KEY.colorizeChat] = {
  1021. name: 'Colorize chat messages'
  1022. , description: "Colorize chat messages according to a unique color for each user"
  1023. , defaultValue: false
  1024. , sub:
  1025. {
  1026. 'colorizer':
  1027. {
  1028. defaultValue: 0
  1029. , label: ['Equally Distributed', 'Random (light colors)', 'Random (dark colors)']
  1030. , options: ['equallyDistributed', 'random1', 'random2']
  1031. }
  1032. }
  1033. }
  1034. , _a[KEY.intelligentScrolling] = {
  1035. name: 'Intelligent scrolling'
  1036. , 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."
  1037. , defaultValue: true
  1038. }
  1039. , _a[KEY.showTimestamps] = {
  1040. name: 'Show timestamps'
  1041. , description: "Enables showing timestamps in chat"
  1042. , defaultValue: true
  1043. }
  1044. , _a[KEY.showIcons] = {
  1045. name: 'Show user-icons'
  1046. , description: "Enables showing icons (formerly sigils) for each user in chat"
  1047. , defaultValue: true
  1048. }
  1049. , _a[KEY.showTags] = {
  1050. name: 'Show user-tags'
  1051. , description: "Enables showing tags (Dev, Mod, Contributor) and colors for messages in chat"
  1052. , defaultValue: true
  1053. }
  1054. , _a[KEY.enableSpamDetection] = {
  1055. name: 'Enable spam detection'
  1056. , description: "Enables simple spam detection"
  1057. , defaultValue: true
  1058. }
  1059. , _a[KEY.useTombNotif] = {
  1060. name: 'Use Tomb notification'
  1061. , description: "Shows notification when Faradox's tomb is ready to fight"
  1062. , defaultValue: false
  1063. }
  1064. , _a[KEY.useWorkerNotif] = {
  1065. name: 'Use Worker notification'
  1066. , description: "Shows notification when workers come back"
  1067. , defaultValue: true
  1068. }
  1069. , _a[KEY.useBobsUncleNotif] = {
  1070. name: 'Use Bobs uncle notification'
  1071. , description: "Shows notification when Bob's uncle farm is ready to harvest"
  1072. , defaultValue: true
  1073. }
  1074. , _a[KEY.showNotifications] = {
  1075. name: 'Show browser notifications'
  1076. , description: "Shows browser notifications for enabled events (click the little gear for more options)"
  1077. , defaultValue: true
  1078. , sub:
  1079. {
  1080. 'showType':
  1081. {
  1082. defaultValue: 0
  1083. , label: ['only when window inactive', 'always']
  1084. , options: ['whenInactive', 'always']
  1085. }
  1086. , 'smelting':
  1087. {
  1088. defaultValue: true
  1089. , label: 'Smelting finishes'
  1090. }
  1091. , 'chopping':
  1092. {
  1093. defaultValue: true
  1094. , label: 'A tree is fully grown'
  1095. }
  1096. , 'harvest':
  1097. {
  1098. defaultValue: true
  1099. , label: 'A plant can be harvested'
  1100. }
  1101. , 'potionEffect':
  1102. {
  1103. defaultValue: true
  1104. , label: 'A potion\'s effect ends'
  1105. }
  1106. , 'boatReturned':
  1107. {
  1108. defaultValue: true
  1109. , label: 'A boat returns'
  1110. }
  1111. , 'heroReady':
  1112. {
  1113. defaultValue: true
  1114. , label: 'The hero is fully recovered and ready to fight'
  1115. }
  1116. , 'itemsSold':
  1117. {
  1118. defaultValue: true
  1119. , label: 'Items are sold on the market'
  1120. }
  1121. , 'pirate':
  1122. {
  1123. defaultValue: true
  1124. , label: 'A pirate has found a treasure map'
  1125. }
  1126. , 'essence':
  1127. {
  1128. defaultValue: true
  1129. , label: 'An essence was found'
  1130. }
  1131. , 'rocket':
  1132. {
  1133. defaultValue: true
  1134. , label: 'The rocket has landed on the moon or earth'
  1135. }
  1136. , 'wind':
  1137. {
  1138. defaultValue: true
  1139. , label: 'The wind for the sail boat has changed'
  1140. }
  1141. , 'perk':
  1142. {
  1143. defaultValue: true
  1144. , label: 'A new perk is unlocked (achievement set completed)'
  1145. }
  1146. , 'pm':
  1147. {
  1148. defaultValue: true
  1149. , label: 'A private messages (pm) arrives'
  1150. }
  1151. , 'mention':
  1152. {
  1153. defaultValue: true
  1154. , label: 'The username is mentioned in chat'
  1155. }
  1156. , 'keyword':
  1157. {
  1158. defaultValue: true
  1159. , label: 'A keyword is mentioned in chat'
  1160. }
  1161. , 'serverMsg':
  1162. {
  1163. defaultValue: true
  1164. , label: 'Server messages (like <em>Server is restarting...</em>)'
  1165. }
  1166. }
  1167. }
  1168. , _a[KEY.showEssencePopup] = {
  1169. name: 'Show essence popup'
  1170. , description: "Shown a popup (like the ones when a diamond is found or the server is restarting) for finding an essence"
  1171. , defaultValue: false
  1172. }
  1173. , _a[KEY.wikiaLinks] = {
  1174. name: 'Show wikia links'
  1175. , description: "Show wikia links for every item on hover (the little icon in the upper left corner)"
  1176. , defaultValue: true
  1177. }
  1178. , _a[KEY.newXpAnimation] = {
  1179. name: 'New XP-gain animation'
  1180. , description: "Show gained xp on top skill bar instead on the position of the mouse"
  1181. , defaultValue: true
  1182. }
  1183. , _a[KEY.amountSymbol] = {
  1184. name: 'Show \u00D7 on items'
  1185. , description: "Show a tiny \u00D7-symbol before amount numbers of items"
  1186. , defaultValue: true
  1187. }
  1188. , _a[KEY.showTabTimer] = {
  1189. name: 'Show tab timer and info'
  1190. , description: "Show timer on tabs for trees, plants and hero"
  1191. , defaultValue: true
  1192. }
  1193. , _a[KEY.showLootTab] = {
  1194. name: 'Show sub tab for loot table'
  1195. , description: "Show a sub tab for combat drop table in combat"
  1196. , defaultValue: true
  1197. }
  1198. , _a[KEY.useEfficiencyStyle] = {
  1199. name: 'Use space efficient style'
  1200. , description: "Use a space efficient style with less blank space"
  1201. , defaultValue: false
  1202. }
  1203. , _a[KEY.makeNumberInputs] = {
  1204. name: 'Turn text inputs into number inputs'
  1205. , description: "Number inputs allow you to change the amount via arrow buttons"
  1206. , defaultValue: true
  1207. }
  1208. , _a[KEY.addKeepInput] = {
  1209. name: 'Add keep input for selling to npc shop'
  1210. , description: "A keep input allows you to set the amount of items you want to keep when selling"
  1211. , defaultValue: true
  1212. }
  1213. , _a[KEY.addMaxBtn] = {
  1214. name: 'Add max button for some crafting inputs'
  1215. , description: "Add max button for crafting (e.g. vials), brewing potions and cooking food"
  1216. , defaultValue: true
  1217. }
  1218. , _a[KEY.highlightUnplantableSeed] = {
  1219. name: 'Show whether a seed can be planted'
  1220. , description: "Fades the item box of a seed when it's not plantable"
  1221. , defaultValue: true
  1222. }
  1223. , _a[KEY.showSdChange] = {
  1224. name: 'Show stardust change'
  1225. , description: "Shows the amount of stardust earned or spent in the last tick"
  1226. , defaultValue: true
  1227. }
  1228. , _a[KEY.usePotionWarning] = {
  1229. name: 'Use drink warning for active potions'
  1230. , description: "Disable drink button for 3 seconds if the potion is already active"
  1231. , defaultValue: true
  1232. }
  1233. , _a[KEY.showCaptions] = {
  1234. name: 'Show item captions'
  1235. , description: "Show item captions for some items instead of the number of owned items"
  1236. , defaultValue: true
  1237. }
  1238. , _a[KEY.syncPriceHistory] = {
  1239. name: 'Sync price history'
  1240. , description: "Synchronize the local price history"
  1241. , defaultValue: false
  1242. , sub:
  1243. {
  1244. 'url':
  1245. {
  1246. defaultValue: ''
  1247. , label: 'paste url here'
  1248. }
  1249. }
  1250. }
  1251. , _a[KEY.useNewToolbar] = {
  1252. name: 'Use new toolbar'
  1253. , description: "Use new reordered toolbar"
  1254. , defaultValue: true
  1255. , requiresReload: true
  1256. }
  1257. , _a[KEY.changeMachineDialog] = {
  1258. name: 'Use slider for machine dialog'
  1259. , description: "Change buttons in machine dialog into slider"
  1260. , defaultValue: true
  1261. , requiresReload: true
  1262. }
  1263. , _a);
  1264. var SETTINGS_TABLE_ID = 'dh2-settings';
  1265. var SETTING_ID_PREFIX = 'dh2-setting-';
  1266. var settings2Init = Object.keys(CFG);
  1267. /**
  1268. * settings
  1269. */
  1270. function toName(key, subKey)
  1271. {
  1272. var name = typeof key === 'string' ? key : KEY[key];
  1273. if (subKey !== undefined)
  1274. {
  1275. return name + '.' + subKey;
  1276. }
  1277. return name;
  1278. }
  1279.  
  1280. function getStoreKey(key, subKey)
  1281. {
  1282. return 'setting.' + toName(key, subKey);
  1283. }
  1284. var observedSettings = new Map();
  1285. var observedSubSettings = new Map();
  1286.  
  1287. function observe(key, fn)
  1288. {
  1289. var n = toName(key);
  1290. if (!observedSettings.has(n))
  1291. {
  1292. observedSettings.set(n, new Set());
  1293. }
  1294. observedSettings.get(n).add(fn);
  1295. }
  1296. settings.observe = observe;
  1297.  
  1298. function observeSub(key, subKey, fn)
  1299. {
  1300. var n = toName(key, subKey);
  1301. if (!observedSubSettings.has(n))
  1302. {
  1303. observedSubSettings.set(n, new Set());
  1304. }
  1305. observedSubSettings.get(n).add(fn);
  1306. }
  1307. settings.observeSub = observeSub;
  1308.  
  1309. function unobserve(key, fn)
  1310. {
  1311. var n = toName(key);
  1312. if (!observedSettings.has(n))
  1313. {
  1314. return false;
  1315. }
  1316. return observedSettings.get(n).delete(fn);
  1317. }
  1318. settings.unobserve = unobserve;
  1319.  
  1320. function unobserveSub(key, subKey, fn)
  1321. {
  1322. var n = toName(key, subKey);
  1323. if (!observedSubSettings.has(n))
  1324. {
  1325. return false;
  1326. }
  1327. return observedSubSettings.get(n).delete(fn);
  1328. }
  1329. settings.unobserveSub = unobserveSub;
  1330. var settingsProxies = new Map();
  1331.  
  1332. function get(key)
  1333. {
  1334. if (!CFG.hasOwnProperty(key))
  1335. {
  1336. return false;
  1337. }
  1338. if (settingsProxies.has(key))
  1339. {
  1340. var proxy = settingsProxies.get(key);
  1341. return proxy.get(key);
  1342. }
  1343. var name = getStoreKey(key);
  1344. return store.has(name) ? store.get(name) : CFG[key].defaultValue;
  1345. }
  1346. settings.get = get;
  1347.  
  1348. function getSub(key, subKey)
  1349. {
  1350. if (!CFG.hasOwnProperty(key))
  1351. {
  1352. return null;
  1353. }
  1354. var name = getStoreKey(key, subKey);
  1355. var def = CFG[key].sub[subKey].defaultValue;
  1356. if (store.has(name))
  1357. {
  1358. var stored = store.get(name);
  1359. if (def instanceof Array)
  1360. {
  1361. for (var i = 0; i < def.length; i++)
  1362. {
  1363. if (stored.indexOf(def[i]) === -1)
  1364. {
  1365. stored.push(def[i]);
  1366. }
  1367. }
  1368. for (var i = 0; i < stored.length; i++)
  1369. {
  1370. if (def.indexOf(stored[i]) === -1)
  1371. {
  1372. stored.splice(i, 1);
  1373. i--;
  1374. }
  1375. }
  1376. }
  1377. return stored;
  1378. }
  1379. else
  1380. {
  1381. return def;
  1382. }
  1383. }
  1384. settings.getSub = getSub;
  1385.  
  1386. function set(key, newValue)
  1387. {
  1388. if (!CFG.hasOwnProperty(key))
  1389. {
  1390. return;
  1391. }
  1392. var oldValue = get(key);
  1393. var n = toName(key);
  1394. if (settingsProxies.has(key))
  1395. {
  1396. var proxy = settingsProxies.get(key);
  1397. proxy.set(key, oldValue, newValue);
  1398. }
  1399. else
  1400. {
  1401. store.set(getStoreKey(key), newValue);
  1402. }
  1403. if (oldValue !== newValue && observedSettings.has(n))
  1404. {
  1405. observedSettings.get(n).forEach(function (fn)
  1406. {
  1407. return fn(key, oldValue, newValue);
  1408. });
  1409. }
  1410. }
  1411. settings.set = set;
  1412.  
  1413. function setSub(key, subKey, newValue)
  1414. {
  1415. if (!CFG.hasOwnProperty(key))
  1416. {
  1417. return;
  1418. }
  1419. var oldValue = getSub(key, subKey);
  1420. var n = toName(key, subKey);
  1421. store.set(getStoreKey(key, subKey), newValue);
  1422. if (oldValue !== newValue && observedSubSettings.has(n))
  1423. {
  1424. observedSubSettings.get(n).forEach(function (fn)
  1425. {
  1426. return fn(key, subKey, oldValue, newValue);
  1427. });
  1428. }
  1429. }
  1430. settings.setSub = setSub;
  1431.  
  1432. function getSubCfg(key)
  1433. {
  1434. if (!CFG.hasOwnProperty(key))
  1435. {
  1436. return;
  1437. }
  1438. return CFG[key].sub;
  1439. }
  1440. settings.getSubCfg = getSubCfg;
  1441.  
  1442. function initSettingsStyle()
  1443. {
  1444. 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 > a.version\n{\n\tcolor: orange;\n\tfont-size: 1.2rem;\n\ttext-decoration: none;\n}\n#tab-container-profile h2.section-title > a.version:hover\n{\n\tcolor: white;\n\ttext-decoration: underline;\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-left: 0;\n\tborder-right: 0;\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.settings-container > li.setting > button.ui-button\n{\n\twidth: 100%;\n}\n.ui-textfield\n{\n\tbackground: none;\n\tcolor: inherit;\n\tcursor: text;\n\tfont: inherit;\n\toutline: none;\n\ttext-align: inherit;\n}\n.ui-textfield.ui-state-active,\n.ui-widget-content .ui-textfield.ui-state-active,\n.ui-widget-header .ui-textfield.ui-state-active,\n.ui-button.ui-textfield:active,\n.ui-button.ui-textfield.ui-state-active:hover\n{\n\tbackground: transparent;\n\tborder: 1px solid #c5c5c5;\n\tcolor: #333333;\n\tfont-weight: normal;\n}\n.settings-container.list > li\n{\n\tborder: 1px solid #c5c5c5;\n\tborder-radius: 3px;\n\tdisplay: flex;\n\tmargin: 5px 0;\n}\n.settings-container.list > li > span.content\n{\n\tflex: 1 0 auto;\n\tline-height: 2rem;\n\tmargin: 0 5px 0 1rem;\n}\n.settings-container.list > li > button.ui-button\n{\n\tmargin: -1px;\n}\n.instruction\n{\n\tcursor: default;\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n.instruction code,\n.instruction a\n{\n\tcursor: initial;\n\t-webkit-user-select: text;\n\t-moz-user-select: text;\n\t-ms-user-select: text;\n\tuser-select: text;\n}\n.instruction code\n{\n\tbackground-color: lightgray;\n\tdisplay: inline-block;\n\tpadding: .25rem;\n}\n\t\t");
  1445. }
  1446.  
  1447. function getSettingId(key, subKey)
  1448. {
  1449. var name = toName(key) + (subKey !== undefined ? '-' + subKey : '');
  1450. return SETTING_ID_PREFIX + split2Words(name, '-').toLowerCase();
  1451. }
  1452.  
  1453. function initSettingTable()
  1454. {
  1455. function insertAfter(newChild, oldChild)
  1456. {
  1457. var parent = oldChild.parentElement;
  1458. if (oldChild.nextElementSibling == null)
  1459. {
  1460. parent.appendChild(newChild);
  1461. }
  1462. else
  1463. {
  1464. parent.insertBefore(newChild, oldChild.nextElementSibling);
  1465. }
  1466. }
  1467.  
  1468. function getCheckImageSrc(value)
  1469. {
  1470. return 'images/icons/' + (value ? 'check' : 'x') + '.png';
  1471. }
  1472. var profileTable = document.getElementById('profile-toggleTable');
  1473. if (!profileTable)
  1474. {
  1475. return;
  1476. }
  1477. var settingsHeader = document.createElement('h2');
  1478. settingsHeader.className = 'section-title';
  1479. settingsHeader.innerHTML = "Userscript \"DH2 Fixed\" <a class=\"version\" href=\"https://greasyfork.org/scripts/27642-dh2-fixed\" target=\"_blank\">v" + version + "</a><br>\n\t\t\t<span class=\"note\" style=\"display: none;\">(* changes require reloading the tab)</span>";
  1480. var requiresReloadNote = settingsHeader.querySelector('.note');
  1481. insertAfter(settingsHeader, profileTable);
  1482. var settingsTable = document.createElement('table');
  1483. settingsTable.id = SETTINGS_TABLE_ID;
  1484. settingsTable.className = 'table-style1';
  1485. settingsTable.width = '40%';
  1486. 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";
  1487.  
  1488. function addRowClickListener(row, key, settingId)
  1489. {
  1490. row.addEventListener('click', function ()
  1491. {
  1492. var newValue = !get(key);
  1493. set(key, newValue);
  1494. document.getElementById(settingId).src = getCheckImageSrc(newValue);
  1495. });
  1496. }
  1497.  
  1498. function addSubClickListener(btn, dialog)
  1499. {
  1500. btn.addEventListener('click', function (event)
  1501. {
  1502. initJQueryDialog(dialog);
  1503. event.stopPropagation();
  1504. event.preventDefault();
  1505. });
  1506. }
  1507. for (var _i = 0, settings2Init_1 = settings2Init; _i < settings2Init_1.length; _i++)
  1508. {
  1509. var k = settings2Init_1[_i];
  1510. // convert it into a KEY
  1511. var key = parseInt(k, 10);
  1512. var setting = CFG[key];
  1513. if (setting == null)
  1514. {
  1515. console.error('missing setting entry:', key, toName(key));
  1516. continue;
  1517. }
  1518. var settingId = getSettingId(key);
  1519. var row = settingsTable.insertRow(-1);
  1520. row.classList.add('setting');
  1521. if (setting.requiresReload)
  1522. {
  1523. row.classList.add('reload');
  1524. requiresReloadNote.style.display = '';
  1525. }
  1526. row.setAttribute('onclick', '');
  1527. 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";
  1528. if (setting.sub)
  1529. {
  1530. row.classList.add('sub');
  1531. var subBtn = document.createElement('button');
  1532. subBtn.innerHTML = "<img src=\"images/icons/gearOff.gif\" class=\"image-icon-15\">";
  1533. row.cells.item(0).appendChild(subBtn);
  1534. var dialog = createSubSettingDialog(key);
  1535. addSubClickListener(subBtn, dialog);
  1536. }
  1537. var tooltipEl = ensureTooltip(settingId, row);
  1538. tooltipEl.innerHTML = setting.description;
  1539. if (setting.requiresReload)
  1540. {
  1541. 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>";
  1542. }
  1543. addRowClickListener(row, key, settingId);
  1544. }
  1545. insertAfter(settingsTable, settingsHeader);
  1546. }
  1547.  
  1548. function initProxies()
  1549. {
  1550. var row = document.querySelector('tr[data-tooltip-id="tooltip-profile-removeCraftingFilter"]');
  1551. if (row)
  1552. {
  1553. var valueCache_1 = getGameValue('profileRemoveCraftingFilter') != 1;
  1554. settingsProxies.set(KEY.hideCraftingRecipes
  1555. , {
  1556. get: function (key)
  1557. {
  1558. return getGameValue('profileRemoveCraftingFilter') != 1;
  1559. }
  1560. , set: function (key, oldValue, newValue)
  1561. {
  1562. if (valueCache_1 != newValue)
  1563. {
  1564. row.click();
  1565. valueCache_1 = newValue;
  1566. }
  1567. }
  1568. });
  1569. observer.add('profileRemoveCraftingFilter', function ()
  1570. {
  1571. set(KEY.hideCraftingRecipes, getGameValue('profileRemoveCraftingFilter') != 1);
  1572. });
  1573. }
  1574. }
  1575. var subDialog;
  1576. (function (subDialog)
  1577. {
  1578. function defaultHandler(key, dialog)
  1579. {
  1580. var setting = CFG[key];
  1581. var subSettings = setting.sub;
  1582. var settingContainer = createSubSettingsContainer(key, subSettings);
  1583. dialog.appendChild(settingContainer);
  1584. }
  1585.  
  1586. function colorizeChat(dialog)
  1587. {
  1588. defaultHandler(KEY.colorizeChat, dialog);
  1589. }
  1590. subDialog.colorizeChat = colorizeChat;
  1591.  
  1592. function showNotifications(dialog)
  1593. {
  1594. dialog.appendChild(document.createTextNode('Show notifications\u2026'));
  1595. defaultHandler(KEY.showNotifications, dialog);
  1596. dialog.appendChild(document.createTextNode('Events for which notifications are shown:'));
  1597. var ulNotifType = dialog.lastElementChild;
  1598. var ulEvents = ulNotifType.cloneNode(false);
  1599. while (ulNotifType.children.length > 1)
  1600. {
  1601. ulEvents.appendChild(ulNotifType.children.item(1));
  1602. }
  1603. dialog.appendChild(ulEvents);
  1604. }
  1605. subDialog.showNotifications = showNotifications;
  1606.  
  1607. function syncPriceHistory(dialog)
  1608. {
  1609. var setting = CFG[KEY.syncPriceHistory];
  1610. var subSettings = setting.sub;
  1611. var instructionEl = document.createElement('div');
  1612. instructionEl.className = 'instruction';
  1613. instructionEl.innerHTML = "Go to <a href=\"http://myjson.com/\" target=\"_blank\">http://myjson.com/</a>, insert <code>{}</code> and press \"<em>Save</em>\". Then copy the URL of the created store (e.g. <code>http://myjson.com/ltk51</code>) and insert it into the following input:";
  1614. dialog.appendChild(instructionEl);
  1615. var settingContainer = createSubSettingsContainer(KEY.syncPriceHistory, subSettings);
  1616. dialog.appendChild(settingContainer);
  1617. }
  1618. subDialog.syncPriceHistory = syncPriceHistory;
  1619. })(subDialog || (subDialog = {}));
  1620.  
  1621. function createSubSettingDialog(key)
  1622. {
  1623. var settingId = getSettingId(key);
  1624. var setting = CFG[key];
  1625. var dialog = document.createElement('div');
  1626. dialog.id = 'dialog-' + settingId;
  1627. dialog.style.display = 'none';
  1628. dialog.innerHTML = "<h2>" + setting.name + "</h2>";
  1629. var name = toName(key);
  1630. if (subDialog.hasOwnProperty(name))
  1631. {
  1632. subDialog[name](dialog);
  1633. }
  1634. else
  1635. {
  1636. console.warn('missing setting handler for "%s"', name);
  1637. var todoEl = document.createElement('span');
  1638. todoEl.textContent = 'TODO';
  1639. dialog.appendChild(todoEl);
  1640. }
  1641. document.body.appendChild(dialog);
  1642. return dialog;
  1643. }
  1644.  
  1645. function createSubSettingsContainer(parentKey, subSettings)
  1646. {
  1647. var settingsContainer = document.createElement('ul');
  1648. settingsContainer.className = 'settings-container';
  1649.  
  1650. function addCheckbox(listEl, subKey, id, setting)
  1651. {
  1652. var checkbox = document.createElement('input');
  1653. checkbox.type = 'checkbox';
  1654. checkbox.id = id;
  1655. checkbox.name = id;
  1656. checkbox.checked = getSub(parentKey, subKey);
  1657. var label = document.createElement('label');
  1658. label.htmlFor = id;
  1659. label.innerHTML = setting.label;
  1660. checkbox.addEventListener('change', function ()
  1661. {
  1662. return setSub(parentKey, subKey, checkbox.checked);
  1663. });
  1664. listEl.appendChild(checkbox);
  1665. listEl.appendChild(label);
  1666. }
  1667.  
  1668. function addSelectmenu(listEl, subKey, id, setting)
  1669. {
  1670. var select = document.createElement('select');
  1671. select.id = id;
  1672. select.name = id;
  1673. var options = setting.options;
  1674. var selectedIndex = getSub(parentKey, subKey);
  1675. for (var i = 0; i < options.length; i++)
  1676. {
  1677. var option = document.createElement('option');
  1678. option.value = options[i];
  1679. if (setting.label)
  1680. {
  1681. option.innerHTML = setting.label[i];
  1682. }
  1683. else
  1684. {
  1685. option.innerHTML = key2Name(options[i]);
  1686. }
  1687. option.selected = i == selectedIndex;
  1688. select.appendChild(option);
  1689. }
  1690. select.addEventListener('change', function ()
  1691. {
  1692. return setSub(parentKey, subKey, select.selectedIndex);
  1693. });
  1694. listEl.appendChild(select);
  1695. }
  1696.  
  1697. function addInput(listEl, subKey, id, setting)
  1698. {
  1699. var input = document.createElement('input');
  1700. input.type = 'text';
  1701. input.placeholder = setting.label || '';
  1702. input.value = getSub(parentKey, subKey);
  1703. var onChange = function ()
  1704. {
  1705. return setSub(parentKey, subKey, input.value);
  1706. };
  1707. input.addEventListener('click', onChange);
  1708. input.addEventListener('change', onChange);
  1709. input.addEventListener('keyup', onChange);
  1710. listEl.appendChild(input);
  1711. }
  1712. var keyList = Object.keys(subSettings);
  1713. var orderIndex = keyList.findIndex(function (k)
  1714. {
  1715. return subSettings[k].defaultValue instanceof Array;
  1716. });
  1717. var isSortable = orderIndex != -1;
  1718. if (isSortable)
  1719. {
  1720. keyList = getSub(parentKey, keyList[orderIndex]);
  1721. }
  1722. for (var _i = 0, keyList_1 = keyList; _i < keyList_1.length; _i++)
  1723. {
  1724. var subKey = keyList_1[_i];
  1725. var settingId = getSettingId(parentKey, subKey);
  1726. var setting = subSettings[subKey];
  1727. var listEl = document.createElement('li');
  1728. listEl.classList.add('setting');
  1729. if (isSortable)
  1730. {
  1731. listEl.dataset.subKey = subKey;
  1732. var sortableIcon = document.createElement('span');
  1733. sortableIcon.className = 'ui-icon ui-icon-arrowthick-2-n-s handle';
  1734. listEl.appendChild(sortableIcon);
  1735. }
  1736. if (setting.options)
  1737. {
  1738. addSelectmenu(listEl, subKey, settingId, setting);
  1739. }
  1740. else if (typeof setting.defaultValue === 'boolean')
  1741. {
  1742. addCheckbox(listEl, subKey, settingId, setting);
  1743. }
  1744. else if (typeof setting.defaultValue === 'string')
  1745. {
  1746. addInput(listEl, subKey, settingId, setting);
  1747. }
  1748. settingsContainer.appendChild(listEl);
  1749. }
  1750. return settingsContainer;
  1751. }
  1752.  
  1753. function initJQueryDialog(dialog)
  1754. {
  1755. var $dialog = win.$(dialog);
  1756. $dialog.dialog(
  1757. {
  1758. width: DIALOG_WIDTH + 'px'
  1759. });
  1760. $dialog.find('input[type="checkbox"]').checkboxradio()
  1761. .next().children(':first-child').removeClass('ui-state-hover');
  1762. $dialog.find('button:not(.sub)').button();
  1763. $dialog.find('input:text').button()
  1764. .addClass('ui-textfield')
  1765. .off('mouseenter').off('mousedown').off('keydown');
  1766. $dialog.find('select').selectmenu(
  1767. {
  1768. change: function (event, ui)
  1769. {
  1770. var changeEvent = document.createEvent('HTMLEvents');
  1771. changeEvent.initEvent('change', false, true);
  1772. event.target.dispatchEvent(changeEvent);
  1773. }
  1774. });
  1775. $dialog.find('.sortable').sortable(
  1776. {
  1777. handle: '.handle'
  1778. , update: function (event, ui)
  1779. {
  1780. var newOrder = [];
  1781. var children = event.target.children;
  1782. for (var i = 0; i < children.length; i++)
  1783. {
  1784. var child = children[i];
  1785. newOrder.push(child.dataset.subKey);
  1786. }
  1787. var updateEvent = new CustomEvent('sortupdate'
  1788. , {
  1789. detail: newOrder
  1790. });
  1791. event.target.dispatchEvent(updateEvent);
  1792. }
  1793. });
  1794. return $dialog;
  1795. }
  1796.  
  1797. function createSettingsContainer(settingList)
  1798. {
  1799. var settingsContainer = document.createElement('ul');
  1800. settingsContainer.className = 'settings-container';
  1801.  
  1802. function addOpenDialogClickListener(el, dialog)
  1803. {
  1804. el.addEventListener('click', function (event)
  1805. {
  1806. initJQueryDialog(dialog);
  1807. event.stopPropagation();
  1808. event.preventDefault();
  1809. });
  1810. }
  1811.  
  1812. function addChangeListener(key, checkbox)
  1813. {
  1814. checkbox.addEventListener('change', function ()
  1815. {
  1816. set(key, checkbox.checked);
  1817. });
  1818. }
  1819. for (var _i = 0, settingList_1 = settingList; _i < settingList_1.length; _i++)
  1820. {
  1821. var key = settingList_1[_i];
  1822. var settingId = getSettingId(key);
  1823. var setting = CFG[key];
  1824. var index = settings2Init.indexOf(key.toString());
  1825. if (index != -1)
  1826. {
  1827. settings2Init.splice(index, 1);
  1828. }
  1829. var listEl = document.createElement('li');
  1830. listEl.classList.add('setting');
  1831. if (setting.requiresReload)
  1832. {
  1833. listEl.classList.add('reload');
  1834. }
  1835. var checkbox = document.createElement('input');
  1836. checkbox.type = 'checkbox';
  1837. checkbox.id = settingId;
  1838. checkbox.checked = get(key);
  1839. var label = document.createElement('label');
  1840. label.htmlFor = settingId;
  1841. label.textContent = setting.name;
  1842. addChangeListener(key, checkbox);
  1843. listEl.appendChild(checkbox);
  1844. listEl.appendChild(label);
  1845. if (setting.sub)
  1846. {
  1847. var moreBtn = document.createElement('button');
  1848. moreBtn.className = 'sub';
  1849. moreBtn.innerHTML = "<img src=\"images/icons/gearOff.gif\" class=\"image-icon-20\" />";
  1850. listEl.appendChild(moreBtn);
  1851. var dialog = createSubSettingDialog(key);
  1852. addOpenDialogClickListener(moreBtn, dialog);
  1853. }
  1854. settingsContainer.appendChild(listEl);
  1855. var tooltipEl = ensureTooltip(settingId, listEl);
  1856. tooltipEl.innerHTML = setting.description;
  1857. if (setting.requiresReload)
  1858. {
  1859. 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>";
  1860. }
  1861. }
  1862. return settingsContainer;
  1863. }
  1864.  
  1865. function initCraftingSettings()
  1866. {
  1867. var craftingItems = document.getElementById('tab-sub-container-crafting');
  1868. if (!craftingItems)
  1869. {
  1870. return;
  1871. }
  1872. var br = craftingItems.nextElementSibling;
  1873. var after = br.nextElementSibling;
  1874. var parent = after.parentElement;
  1875. var settingList = [KEY.hideCraftingRecipes, KEY.hideUselessItems];
  1876. var settingsContainer = createSettingsContainer(settingList);
  1877. parent.insertBefore(settingsContainer, after);
  1878. }
  1879.  
  1880. function initMuteDialog(settingsContainer)
  1881. {
  1882. // muted people dialog
  1883. var dialog = document.createElement('div');
  1884. dialog.id = 'dialog-chat-muted-people';
  1885. dialog.style.display = 'none';
  1886. dialog.innerHTML = "<h2>Muted people</h2>";
  1887. var input = document.createElement('input');
  1888. input.type = 'text';
  1889. input.placeholder = 'username';
  1890. dialog.appendChild(input);
  1891. var addBtn = document.createElement('button');
  1892. addBtn.textContent = '+';
  1893. dialog.appendChild(addBtn);
  1894. var listEl = document.createElement('ul');
  1895. listEl.className = 'settings-container list';
  1896. var username2Item = {};
  1897. var username2Btn = {};
  1898.  
  1899. function removeListener(event)
  1900. {
  1901. var target = event.target;
  1902. var username = target.dataset.username || '';
  1903. var index = win.mutedPeople.indexOf(username);
  1904. if (index !== -1)
  1905. {
  1906. win.mutedPeople.splice(index, 1);
  1907. }
  1908. }
  1909.  
  1910. function add2List(username)
  1911. {
  1912. var item = document.createElement('li');
  1913. item.innerHTML = "<span class=\"content\">" + username + "</span>";
  1914. var removeBtn = document.createElement('button');
  1915. removeBtn.dataset.username = username;
  1916. removeBtn.textContent = '-';
  1917. win.$(removeBtn).button();
  1918. removeBtn.addEventListener('click', removeListener);
  1919. username2Btn[username] = removeBtn;
  1920. item.appendChild(removeBtn);
  1921. username2Item[username] = item;
  1922. listEl.appendChild(item);
  1923. }
  1924. var _push = win.mutedPeople.push;
  1925. win.mutedPeople.push = function ()
  1926. {
  1927. var items = [];
  1928. for (var _i = 0; _i < arguments.length; _i++)
  1929. {
  1930. items[_i] = arguments[_i];
  1931. }
  1932. items.forEach(function (username)
  1933. {
  1934. return add2List(username);
  1935. });
  1936. return _push.call.apply(_push, [win.mutedPeople].concat(items));
  1937. };
  1938. var _splice = win.mutedPeople.splice;
  1939. win.mutedPeople.splice = function (start, deleteCount)
  1940. {
  1941. var items = [];
  1942. for (var _i = 2; _i < arguments.length; _i++)
  1943. {
  1944. items[_i - 2] = arguments[_i];
  1945. }
  1946. for (var i = 0; i < deleteCount; i++)
  1947. {
  1948. var username = win.mutedPeople[start + i];
  1949. var item = username2Item[username];
  1950. delete username2Item[username];
  1951. listEl.removeChild(item);
  1952. var btn = username2Btn[username];
  1953. delete username2Btn[username];
  1954. btn.removeEventListener('click', removeListener);
  1955. }
  1956. items.forEach(function (username)
  1957. {
  1958. return add2List(username);
  1959. });
  1960. return _splice.call.apply(_splice, [win.mutedPeople, start, deleteCount].concat(items));
  1961. };
  1962. dialog.appendChild(listEl);
  1963. addBtn.addEventListener('click', function ()
  1964. {
  1965. win.mutedPeople.push(input.value);
  1966. input.value = '';
  1967. });
  1968. document.body.appendChild(dialog);
  1969. var listItem = document.createElement('li');
  1970. listItem.classList.add('setting');
  1971. var dialogBtn = document.createElement('button');
  1972. dialogBtn.innerHTML = "List of muted people";
  1973. dialogBtn.addEventListener('click', function ()
  1974. {
  1975. initJQueryDialog(dialog);
  1976. });
  1977. listItem.appendChild(dialogBtn);
  1978. settingsContainer.appendChild(listItem);
  1979. }
  1980.  
  1981. function initKeywordDialog(settingsContainer)
  1982. {
  1983. // keyword dialog
  1984. var dialog = document.createElement('div');
  1985. dialog.id = 'dialog-chat-keyword-list';
  1986. dialog.style.display = 'none';
  1987. dialog.innerHTML = "<h2>Keywords</h2>";
  1988. var input = document.createElement('input');
  1989. input.type = 'text';
  1990. input.placeholder = 'keyword';
  1991. dialog.appendChild(input);
  1992. var addBtn = document.createElement('button');
  1993. addBtn.textContent = '+';
  1994. dialog.appendChild(addBtn);
  1995. var listEl = document.createElement('ul');
  1996. listEl.className = 'settings-container list';
  1997.  
  1998. function add2List(keyword)
  1999. {
  2000. var item = document.createElement('li');
  2001. item.innerHTML = "<span class=\"content\">" + keyword + "</span>";
  2002. var removeBtn = document.createElement('button');
  2003. removeBtn.textContent = '-';
  2004. win.$(removeBtn).button();
  2005. var remove = function ()
  2006. {
  2007. if (chat.removeKeyword(keyword))
  2008. {
  2009. listEl.removeChild(item);
  2010. removeBtn.removeEventListener('click', remove);
  2011. }
  2012. };
  2013. removeBtn.addEventListener('click', remove);
  2014. item.appendChild(removeBtn);
  2015. listEl.appendChild(item);
  2016. }
  2017. // add all keywords
  2018. chat.keywordList.forEach(function (keyword)
  2019. {
  2020. return add2List(keyword);
  2021. });
  2022. dialog.appendChild(listEl);
  2023. addBtn.addEventListener('click', function ()
  2024. {
  2025. var keyword = input.value;
  2026. if (chat.addKeyword(keyword))
  2027. {
  2028. add2List(keyword);
  2029. input.value = '';
  2030. }
  2031. });
  2032. document.body.appendChild(dialog);
  2033. var listItem = document.createElement('li');
  2034. listItem.classList.add('setting');
  2035. var dialogBtn = document.createElement('button');
  2036. dialogBtn.innerHTML = "Manage list of keywords";
  2037. dialogBtn.addEventListener('click', function ()
  2038. {
  2039. initJQueryDialog(dialog);
  2040. });
  2041. listItem.appendChild(dialogBtn);
  2042. settingsContainer.appendChild(listItem);
  2043. }
  2044.  
  2045. function initChatSettings()
  2046. {
  2047. var controlDiv = document.querySelector('#div-chat > div:first-child');
  2048. if (!controlDiv)
  2049. {
  2050. return;
  2051. }
  2052. var btn = document.createElement('button');
  2053. btn.textContent = 'Chat Settings';
  2054. controlDiv.appendChild(btn);
  2055. var dialog = document.createElement('div');
  2056. dialog.id = 'dialog-chat-settings';
  2057. dialog.style.display = 'none';
  2058. dialog.innerHTML = "<h2>Chat Settings</h2>";
  2059. var settingList = [KEY.useNewChat, KEY.colorizeChat, KEY.intelligentScrolling, KEY.showTimestamps, KEY.showIcons, KEY.showTags, KEY.enableSpamDetection];
  2060. var settingsContainer = createSettingsContainer(settingList);
  2061. initMuteDialog(settingsContainer);
  2062. initKeywordDialog(settingsContainer);
  2063. dialog.appendChild(settingsContainer);
  2064. document.body.appendChild(dialog);
  2065. btn.addEventListener('click', function ()
  2066. {
  2067. initJQueryDialog(dialog);
  2068. });
  2069. }
  2070.  
  2071. function init()
  2072. {
  2073. initProxies();
  2074. initSettingsStyle();
  2075. initCraftingSettings();
  2076. initChatSettings();
  2077. initSettingTable();
  2078. }
  2079. settings.init = init;
  2080. var _a;
  2081. })(settings || (settings = {}));
  2082. /**
  2083. * Code from https://github.com/davidmerfield/randomColor
  2084. */
  2085. var colorGenerator;
  2086. (function (colorGenerator)
  2087. {
  2088. // seed to get repeatable colors
  2089. var seed = null;
  2090. var COLOR_NOT_FOUND = {
  2091. hueRange: []
  2092. , lowerBounds: []
  2093. , saturationRange: []
  2094. , brightnessRange: []
  2095. };
  2096. var COLOR_BOUNDS = {
  2097. 'monochrome':
  2098. {
  2099. hueRange: []
  2100. , lowerBounds: [
  2101. [0, 0]
  2102. , [100, 0]
  2103. ]
  2104. }
  2105. , 'red':
  2106. {
  2107. hueRange: [-26, 18]
  2108. , lowerBounds: [
  2109. [20, 100]
  2110. , [30, 92]
  2111. , [40, 89]
  2112. , [50, 85]
  2113. , [60, 78]
  2114. , [70, 70]
  2115. , [80, 60]
  2116. , [90, 55]
  2117. , [100, 50]
  2118. ]
  2119. }
  2120. , 'orange':
  2121. {
  2122. hueRange: [19, 46]
  2123. , lowerBounds: [
  2124. [20, 100]
  2125. , [30, 93]
  2126. , [40, 88]
  2127. , [50, 86]
  2128. , [60, 85]
  2129. , [70, 70]
  2130. , [100, 70]
  2131. ]
  2132. }
  2133. , 'yellow':
  2134. {
  2135. hueRange: [47, 62]
  2136. , lowerBounds: [
  2137. [25, 100]
  2138. , [40, 94]
  2139. , [50, 89]
  2140. , [60, 86]
  2141. , [70, 84]
  2142. , [80, 82]
  2143. , [90, 80]
  2144. , [100, 75]
  2145. ]
  2146. }
  2147. , 'green':
  2148. {
  2149. hueRange: [63, 178]
  2150. , lowerBounds: [
  2151. [30, 100]
  2152. , [40, 90]
  2153. , [50, 85]
  2154. , [60, 81]
  2155. , [70, 74]
  2156. , [80, 64]
  2157. , [90, 50]
  2158. , [100, 40]
  2159. ]
  2160. }
  2161. , 'blue':
  2162. {
  2163. hueRange: [179, 257]
  2164. , lowerBounds: [
  2165. [20, 100]
  2166. , [30, 86]
  2167. , [40, 80]
  2168. , [50, 74]
  2169. , [60, 60]
  2170. , [70, 52]
  2171. , [80, 44]
  2172. , [90, 39]
  2173. , [100, 35]
  2174. ]
  2175. }
  2176. , 'purple':
  2177. {
  2178. hueRange: [258, 282]
  2179. , lowerBounds: [
  2180. [20, 100]
  2181. , [30, 87]
  2182. , [40, 79]
  2183. , [50, 70]
  2184. , [60, 65]
  2185. , [70, 59]
  2186. , [80, 52]
  2187. , [90, 45]
  2188. , [100, 42]
  2189. ]
  2190. }
  2191. , 'pink':
  2192. {
  2193. hueRange: [283, 334]
  2194. , lowerBounds: [
  2195. [20, 100]
  2196. , [30, 90]
  2197. , [40, 86]
  2198. , [60, 84]
  2199. , [80, 80]
  2200. , [90, 75]
  2201. , [100, 73]
  2202. ]
  2203. }
  2204. };
  2205. // shared color dictionary
  2206. var colorDictionary = {};
  2207.  
  2208. function defineColor(name, hueRange, lowerBounds)
  2209. {
  2210. var _a = lowerBounds[0]
  2211. , sMin = _a[0]
  2212. , bMax = _a[1];
  2213. var _b = lowerBounds[lowerBounds.length - 1]
  2214. , sMax = _b[0]
  2215. , bMin = _b[1];
  2216. colorDictionary[name] = {
  2217. hueRange: hueRange
  2218. , lowerBounds: lowerBounds
  2219. , saturationRange: [sMin, sMax]
  2220. , brightnessRange: [bMin, bMax]
  2221. };
  2222. }
  2223.  
  2224. function loadColorBounds()
  2225. {
  2226. for (var name_1 in COLOR_BOUNDS)
  2227. {
  2228. defineColor(name_1, COLOR_BOUNDS[name_1].hueRange, COLOR_BOUNDS[name_1].lowerBounds);
  2229. }
  2230. }
  2231.  
  2232. function randomWithin(min, max)
  2233. {
  2234. if (min === void 0)
  2235. {
  2236. min = 0;
  2237. }
  2238. if (max === void 0)
  2239. {
  2240. max = 0;
  2241. }
  2242. if (seed === null)
  2243. {
  2244. return Math.floor(min + Math.random() * (max + 1 - min));
  2245. }
  2246. else
  2247. {
  2248. // seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
  2249. seed = (seed * 9301 + 49297) % 233280;
  2250. var rnd = seed / 233280.0;
  2251. return Math.floor(min + rnd * (max - min));
  2252. }
  2253. }
  2254.  
  2255. function getColorInfo(hue)
  2256. {
  2257. // maps red colors to make picking hue easier
  2258. if (hue >= 334 && hue <= 360)
  2259. {
  2260. hue -= 360;
  2261. }
  2262. for (var colorName in colorDictionary)
  2263. {
  2264. var color = colorDictionary[colorName];
  2265. if (color.hueRange.length > 0
  2266. && hue >= color.hueRange[0]
  2267. && hue <= color.hueRange[1])
  2268. {
  2269. return colorDictionary[colorName];
  2270. }
  2271. }
  2272. return COLOR_NOT_FOUND;
  2273. }
  2274.  
  2275. function getHueRange(colorInput)
  2276. {
  2277. var number = typeof colorInput === 'undefined' ? Number.NaN : colorInput;
  2278. if (typeof number === 'string')
  2279. {
  2280. number = parseInt(number, 10);
  2281. }
  2282. if (colorInput && isNaN(number) && colorDictionary.hasOwnProperty(colorInput))
  2283. {
  2284. var color = colorDictionary[colorInput];
  2285. if (color.hueRange.length > 0)
  2286. {
  2287. return color.hueRange;
  2288. }
  2289. }
  2290. else if (!isNaN(number) && number < 360 && number > 0)
  2291. {
  2292. return [number, number];
  2293. }
  2294. return [0, 360];
  2295. }
  2296.  
  2297. function pickHue(options)
  2298. {
  2299. var hueRange = getHueRange(options.hue);
  2300. var hue = randomWithin(hueRange[0], hueRange[1]);
  2301. // instead of storing red as two seperate ranges, we group them, using negative numbers
  2302. if (hue < 0)
  2303. {
  2304. return 360 + hue;
  2305. }
  2306. return hue;
  2307. }
  2308.  
  2309. function getSaturationRange(hue)
  2310. {
  2311. return getColorInfo(hue).saturationRange;
  2312. }
  2313.  
  2314. function pickSaturation(hue, options)
  2315. {
  2316. if (options.luminosity === 'random')
  2317. {
  2318. return randomWithin(0, 100);
  2319. }
  2320. if (options.hue === 'monochrome')
  2321. {
  2322. return 0;
  2323. }
  2324. var _a = getSaturationRange(hue)
  2325. , sMin = _a[0]
  2326. , sMax = _a[1];
  2327. switch (options.luminosity)
  2328. {
  2329. case 'bright':
  2330. sMin = 55;
  2331. break;
  2332. case 'dark':
  2333. sMin = sMax - 10;
  2334. break;
  2335. case 'light':
  2336. sMax = 55;
  2337. break;
  2338. }
  2339. return randomWithin(sMin, sMax);
  2340. }
  2341.  
  2342. function getMinimumBrightness(H, S)
  2343. {
  2344. var lowerBounds = getColorInfo(H).lowerBounds;
  2345. for (var i = 0; i < lowerBounds.length - 1; i++)
  2346. {
  2347. var _a = lowerBounds[i]
  2348. , s1 = _a[0]
  2349. , v1 = _a[1];
  2350. var _b = lowerBounds[i + 1]
  2351. , s2 = _b[0]
  2352. , v2 = _b[1];
  2353. if (S >= s1 && S <= s2)
  2354. {
  2355. var m = (v2 - v1) / (s2 - s1);
  2356. var b = v1 - m * s1;
  2357. return m * S + b;
  2358. }
  2359. }
  2360. return 0;
  2361. }
  2362.  
  2363. function pickBrightness(H, S, options)
  2364. {
  2365. var bMin = getMinimumBrightness(H, S);
  2366. var bMax = 100;
  2367. switch (options.luminosity)
  2368. {
  2369. case 'dark':
  2370. bMax = bMin + 20;
  2371. break;
  2372. case 'light':
  2373. bMin = (bMax + bMin) / 2;
  2374. break;
  2375. case 'random':
  2376. bMin = 0;
  2377. bMax = 100;
  2378. break;
  2379. }
  2380. return randomWithin(bMin, bMax);
  2381. }
  2382. var HSVColor = (function ()
  2383. {
  2384. function HSVColor(H, S, V)
  2385. {
  2386. this.H = H;
  2387. this.S = S;
  2388. this.V = V;
  2389. }
  2390. HSVColor.fromHSVArray = function (hsv)
  2391. {
  2392. return new HSVColor(hsv[0], hsv[1], hsv[2]);
  2393. };
  2394. HSVColor.prototype.toHex = function ()
  2395. {
  2396. var rgb = this.toRGB();
  2397. return '#' + this.componentToHex(rgb[0]) + this.componentToHex(rgb[1]) + this.componentToHex(rgb[2]);
  2398. };
  2399. HSVColor.prototype.toHSL = function ()
  2400. {
  2401. var h = this.H;
  2402. var s = this.S / 100;
  2403. var v = this.V / 100;
  2404. var k = (2 - s) * v;
  2405. return [
  2406. h
  2407. , Math.round(s * v / (k < 1 ? k : 2 - k) * 10e3) / 100
  2408. , k / 2 * 100
  2409. ];
  2410. };
  2411. HSVColor.prototype.toHSLString = function (alpha)
  2412. {
  2413. var hsl = this.toHSL();
  2414. if (alpha !== undefined)
  2415. {
  2416. return "hsla(" + hsl[0] + ", " + hsl[1] + "%, " + hsl[2] + "%, " + alpha + ")";
  2417. }
  2418. else
  2419. {
  2420. return "hsl(" + hsl[0] + ", " + hsl[1] + "%, " + hsl[2] + "%)";
  2421. }
  2422. };
  2423. HSVColor.prototype.toRGB = function ()
  2424. {
  2425. // this doesn't work for the values of 0 and 360 here's the hacky fix
  2426. var h = Math.min(Math.max(this.H, 1), 359);
  2427. // Rebase the h,s,v values
  2428. h = h / 360;
  2429. var s = this.S / 100;
  2430. var v = this.V / 100;
  2431. var h_i = Math.floor(h * 6);
  2432. var f = h * 6 - h_i;
  2433. var p = v * (1 - s);
  2434. var q = v * (1 - f * s);
  2435. var t = v * (1 - (1 - f) * s);
  2436. var r = 256;
  2437. var g = 256;
  2438. var b = 256;
  2439. switch (h_i)
  2440. {
  2441. case 0:
  2442. r = v;
  2443. g = t;
  2444. b = p;
  2445. break;
  2446. case 1:
  2447. r = q;
  2448. g = v;
  2449. b = p;
  2450. break;
  2451. case 2:
  2452. r = p;
  2453. g = v;
  2454. b = t;
  2455. break;
  2456. case 3:
  2457. r = p;
  2458. g = q;
  2459. b = v;
  2460. break;
  2461. case 4:
  2462. r = t;
  2463. g = p;
  2464. b = v;
  2465. break;
  2466. case 5:
  2467. r = v;
  2468. g = p;
  2469. b = q;
  2470. break;
  2471. }
  2472. return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
  2473. };
  2474. HSVColor.prototype.toRGBString = function (alpha)
  2475. {
  2476. var rgb = this.toRGB();
  2477. if (alpha !== undefined)
  2478. {
  2479. return "rgba(" + rgb.join(', ') + ", " + alpha + ")";
  2480. }
  2481. else
  2482. {
  2483. return "rgb(" + rgb.join(', ') + ")";
  2484. }
  2485. };
  2486. HSVColor.prototype.componentToHex = function (c)
  2487. {
  2488. var hex = c.toString(16);
  2489. return hex.length == 1 ? '0' + hex : hex;
  2490. };
  2491. return HSVColor;
  2492. }());
  2493. colorGenerator.HSVColor = HSVColor;
  2494.  
  2495. function setFormat(hsv, options)
  2496. {
  2497. var color = HSVColor.fromHSVArray(hsv);
  2498. switch (options.format)
  2499. {
  2500. case 'object':
  2501. return color;
  2502. case 'hsvArray':
  2503. return hsv;
  2504. case 'hslArray':
  2505. return color.toHSL();
  2506. case 'hsl':
  2507. return color.toHSLString();
  2508. case 'hsla':
  2509. return color.toHSLString(options.alpha || Math.random());
  2510. case 'rgbArray':
  2511. return color.toRGB();
  2512. case 'rgb':
  2513. return color.toRGBString();
  2514. case 'rgba':
  2515. return color.toRGBString(options.alpha || Math.random());
  2516. case 'hex':
  2517. default:
  2518. return color.toHex();
  2519. }
  2520. }
  2521.  
  2522. function generateColor(options)
  2523. {
  2524. // pick a hue (H)
  2525. var H = pickHue(options);
  2526. // use H to determine saturation (S)
  2527. var S = pickSaturation(H, options);
  2528. // use S and H to determine brightness (B)
  2529. var B = pickBrightness(H, S, options);
  2530. // return the HSB color in the desired format
  2531. return setFormat([H, S, B], options);
  2532. }
  2533.  
  2534. function getRandom(options)
  2535. {
  2536. options = options ||
  2537. {};
  2538. seed = options.seed == null ? null : options.seed;
  2539. // check if we need to generate multiple colors
  2540. if (options.count !== null && options.count !== undefined)
  2541. {
  2542. var colors = [];
  2543. while (options.count > colors.length)
  2544. {
  2545. // Since we're generating multiple colors, the seed has to be incrememented.
  2546. // Otherwise we'd just generate the same color each time...
  2547. if (seed !== null)
  2548. {
  2549. seed += 1;
  2550. }
  2551. colors.push(generateColor(options));
  2552. }
  2553. return colors;
  2554. }
  2555. return generateColor(options);
  2556. }
  2557. colorGenerator.getRandom = getRandom;
  2558. var ColorInterval = (function ()
  2559. {
  2560. function ColorInterval(start, end)
  2561. {
  2562. this.start = start;
  2563. this.end = end;
  2564. this.left = null;
  2565. this.right = null;
  2566. this.value = null;
  2567. }
  2568. ColorInterval.prototype.getNextValue = function ()
  2569. {
  2570. if (this.value == null)
  2571. {
  2572. this.value = (this.start + this.end) / 2;
  2573. return this.value;
  2574. }
  2575. if (this.left == null)
  2576. {
  2577. this.left = new ColorInterval(this.start, this.value);
  2578. return this.left.getNextValue();
  2579. }
  2580. if (this.right == null)
  2581. {
  2582. this.right = new ColorInterval(this.value, this.end);
  2583. return this.right.getNextValue();
  2584. }
  2585. if (this.left.getHeight() <= this.right.getHeight())
  2586. {
  2587. return this.left.getNextValue();
  2588. }
  2589. else
  2590. {
  2591. return this.right.getNextValue();
  2592. }
  2593. };
  2594. ColorInterval.prototype.getHeight = function ()
  2595. {
  2596. return 1
  2597. + (this.left == null ? 0 : this.left.getHeight())
  2598. + (this.right == null ? 0 : this.right.getHeight());
  2599. };
  2600. return ColorInterval;
  2601. }());
  2602. colorGenerator.ColorInterval = ColorInterval;
  2603. var defaultRootInterval = new ColorInterval(0, 360);
  2604.  
  2605. function getEquallyDistributed(rootInterval)
  2606. {
  2607. if (rootInterval === void 0)
  2608. {
  2609. rootInterval = defaultRootInterval;
  2610. }
  2611. return 'hsl(' + rootInterval.getNextValue() + ', 100%, 80%)';
  2612. }
  2613. colorGenerator.getEquallyDistributed = getEquallyDistributed;
  2614. var Color = (function ()
  2615. {
  2616. function Color(r, g, b)
  2617. {
  2618. this.r = r;
  2619. this.g = g;
  2620. this.b = b;
  2621. }
  2622. Color.fromHex = function (hex)
  2623. {
  2624. return new Color(parseInt(hex.substr(1, 2), 16), parseInt(hex.substr(3, 2), 16), parseInt(hex.substr(5, 2), 16));
  2625. };
  2626. Color.fromRgb = function (rgb)
  2627. {
  2628. var match = rgb.match(this.rgbRegex);
  2629. return new Color(parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10));
  2630. };
  2631. Color.fromString = function (str)
  2632. {
  2633. if (this.hexRegex.test(str))
  2634. {
  2635. return this.fromHex(str);
  2636. }
  2637. else if (this.rgbRegex.test(str))
  2638. {
  2639. return this.fromRgb(str);
  2640. }
  2641. else
  2642. {
  2643. throw new Error('Unexpected color format: ' + str);
  2644. }
  2645. };
  2646. Color.prototype.toString = function (hex)
  2647. {
  2648. if (hex === void 0)
  2649. {
  2650. hex = true;
  2651. }
  2652. return '#' + this.toHex(this.r) + this.toHex(this.g) + this.toHex(this.b);
  2653. };
  2654. Color.prototype.toHex = function (x)
  2655. {
  2656. var xStr = x.toString(16);
  2657. return (xStr.length == 1 ? '0' : '') + xStr;
  2658. };
  2659. Color.hexRegex = /^#(?:[0-9a-f]{3}){1,2}$/i;
  2660. Color.rgbRegex = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/i;
  2661. return Color;
  2662. }());
  2663.  
  2664. function ratioColor(color1, color2, ratio)
  2665. {
  2666. var color = new Color(Math.ceil(color1.r * (1 - ratio) + color2.r * ratio), Math.ceil(color1.g * (1 - ratio) + color2.g * ratio), Math.ceil(color1.b * (1 - ratio) + color2.b * ratio));
  2667. return color.toString();
  2668. }
  2669.  
  2670. function getColorTransition(value, colorStrings)
  2671. {
  2672. var smallerValue = -1;
  2673. var biggerValue = Number.MAX_SAFE_INTEGER;
  2674. var colors = {};
  2675. for (var v in colorStrings)
  2676. {
  2677. var vNum = Number(v);
  2678. if (vNum === value)
  2679. {
  2680. return colorStrings[v];
  2681. }
  2682. else if (vNum < value)
  2683. {
  2684. smallerValue = Math.max(smallerValue, vNum);
  2685. }
  2686. else
  2687. {
  2688. biggerValue = Math.min(biggerValue, vNum);
  2689. }
  2690. colors[v] = Color.fromString(colorStrings[v]);
  2691. }
  2692. if (smallerValue === -1)
  2693. {
  2694. return colorStrings[biggerValue];
  2695. }
  2696. if (biggerValue === Number.MAX_SAFE_INTEGER)
  2697. {
  2698. return colorStrings[smallerValue];
  2699. }
  2700. var ratio = (value - smallerValue) / (biggerValue - smallerValue);
  2701. return ratioColor(colors[smallerValue], colors[biggerValue], ratio);
  2702. }
  2703. colorGenerator.getColorTransition = getColorTransition;
  2704. // populate the color dictionary
  2705. loadColorBounds();
  2706. })(colorGenerator || (colorGenerator = {}));
  2707.  
  2708. /**
  2709. * provides icons
  2710. */
  2711. var icons;
  2712. (function (icons)
  2713. {
  2714. icons.CHART_LINE = 'M16,11.78L20.24,4.45L21.97,5.45L16.74,14.5L10.23,10.75L5.46,19H22V21H2V3H4V17.54L9.5,8L16,11.78Z';
  2715. icons.WIKIA = '<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"/>';
  2716.  
  2717. function getSvgAsUrl(svg)
  2718. {
  2719. return "url('data:image/svg+xml;base64," + btoa(svg) + "')";
  2720. }
  2721. icons.getSvgAsUrl = getSvgAsUrl;
  2722.  
  2723. function wrapCodeWithSvg(code, viewBox, width, height)
  2724. {
  2725. return "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"" + width + "\" height=\"" + height + "\" viewBox=\"" + viewBox + "\">" + code + "</svg>";
  2726. }
  2727. icons.wrapCodeWithSvg = wrapCodeWithSvg;
  2728.  
  2729. function getMd(pathDots, color, width, height)
  2730. {
  2731. if (color === void 0)
  2732. {
  2733. color = 'black';
  2734. }
  2735. if (width === void 0)
  2736. {
  2737. width = '30';
  2738. }
  2739. if (height === void 0)
  2740. {
  2741. height = '30';
  2742. }
  2743. return getSvgAsUrl(wrapCodeWithSvg("<path fill=\"" + color + "\" d=\"" + pathDots + "\" />", '0 0 24 24', width, height));
  2744. }
  2745. icons.getMd = getMd;
  2746. })(icons || (icons = {}));
  2747.  
  2748. /**
  2749. * notifications
  2750. */
  2751. var notifications;
  2752. (function (notifications)
  2753. {
  2754. notifications.name = 'notifications';
  2755.  
  2756. function event(title, options)
  2757. {
  2758. if ((!options || options.whenActive !== true)
  2759. && !document.hidden && document.hasFocus()
  2760. && settings.getSub(settings.KEY.showNotifications, 'showType') !== 1)
  2761. {
  2762. return;
  2763. }
  2764. if (!settings.get(settings.KEY.showNotifications))
  2765. {
  2766. // notifications disabled: return stub notification
  2767. return Promise.resolve(
  2768. {
  2769. close: function () {}
  2770. });
  2771. }
  2772. if (!("Notification" in win))
  2773. {
  2774. return Promise.reject('Your browser does not support notifications.');
  2775. }
  2776. return Notification.requestPermission()
  2777. .then(function (permission)
  2778. {
  2779. if (permission === 'granted')
  2780. {
  2781. var n_1 = new Notification(title, options);
  2782. n_1.onclick = function (event)
  2783. {
  2784. if (options && options.autoFocus !== false)
  2785. {
  2786. win.focus();
  2787. }
  2788. if (options && options.autoClose !== false)
  2789. {
  2790. n_1.close();
  2791. }
  2792. if (options && options.onclick)
  2793. {
  2794. options.onclick(n_1, event);
  2795. }
  2796. };
  2797. return Promise.resolve(n_1);
  2798. }
  2799. else
  2800. {
  2801. return Promise.reject('Notification permission denied');
  2802. }
  2803. });
  2804. }
  2805. notifications.event = event;
  2806.  
  2807. function requestPermission()
  2808. {
  2809. if (settings.get(settings.KEY.showNotifications))
  2810. {
  2811. Notification.requestPermission();
  2812. }
  2813. }
  2814.  
  2815. function init()
  2816. {
  2817. requestPermission();
  2818. settings.observe(settings.KEY.showNotifications, function ()
  2819. {
  2820. return requestPermission();
  2821. });
  2822. }
  2823. notifications.init = init;
  2824. })(notifications || (notifications = {}));
  2825.  
  2826. /**
  2827. * process commands
  2828. */
  2829. var commands;
  2830. (function (commands)
  2831. {
  2832. var XP_GAIN_KEY = 'xpGain';
  2833. var MAX_XP_GAIN_HISTORY_LENGTH = 100;
  2834. var IMAGE2SKILL = {
  2835. // mining = #cc0000
  2836. 'icons/pickaxe': 'mining'
  2837. // crafting = #cc0000
  2838. , 'icons/anvil': 'crafting'
  2839. // woodcutting = cyan
  2840. , 'icons/woodcutting': 'woodcutting'
  2841. // farming = green
  2842. , 'icons/watering-can': 'farming'
  2843. // brewing = #800080
  2844. , 'vialOfWater': 'brewing'
  2845. , 'largeVialOfWater': 'brewing'
  2846. , 'hugeVialOfWater': 'brewing'
  2847. // combat = lime
  2848. , 'icons/combat': 'combat'
  2849. // magic = blue
  2850. , 'icons/wizardhat': 'magic'
  2851. // fishing = blue
  2852. , 'tuna': 'fishing'
  2853. // cooking = yellow
  2854. , 'icons/cooking': 'cooking'
  2855. };
  2856. var xpGainHistory = store.has(XP_GAIN_KEY) ? store.get(XP_GAIN_KEY) :
  2857. {};
  2858. addStyle("\n.scroller.xp\n{\n\tfont-size: 18pt;\n\tposition: absolute;\n\ttext-align: center;\n}\n\t");
  2859.  
  2860. function minutes2String(data)
  2861. {
  2862. return data.replace(/Your account has been running for: (\d+) minutes./, function (wholeMatch, minutes)
  2863. {
  2864. return 'Your account has been running for ' + format.min2Str(minutes) + '.';
  2865. });
  2866. }
  2867. var LOOT_MSG_PREFIX = 'SHOW_LOOT_DIAG=';
  2868.  
  2869. function processLoot(data)
  2870. {
  2871. if (!/^SM=Your boat found nothing\.$|^SHOW_LOOT_DIAG=/.test(data))
  2872. {
  2873. return false;
  2874. }
  2875. var loot = {
  2876. type: 'loot'
  2877. , title: ''
  2878. , itemList: []
  2879. };
  2880. if (data.startsWith('SM='))
  2881. {
  2882. loot.title = 'Boat';
  2883. loot.emptyText = 'Your boat found nothing.';
  2884. }
  2885. else if (data.startsWith(LOOT_MSG_PREFIX))
  2886. {
  2887. var split = data.substr(LOOT_MSG_PREFIX.length).split('~');
  2888. loot.title = split[0];
  2889. for (var i = 1; i < split.length; i += 2)
  2890. {
  2891. loot.itemList.push(
  2892. {
  2893. icon: split[i]
  2894. , text: split[i + 1]
  2895. });
  2896. }
  2897. }
  2898. log.add(loot);
  2899. return true;
  2900. }
  2901. var XP_GAIN_REGEX = /^ST=([^~]+)\.png~([^~]+)~\+(\d+)\s*xp(.*)$/;
  2902. var animationQueue = {};
  2903.  
  2904. function queueXpAnimation(skill, cell, color, xpAmount, extraXp)
  2905. {
  2906. if (!settings.get(settings.KEY.newXpAnimation))
  2907. {
  2908. return;
  2909. }
  2910. animationQueue[skill] = animationQueue[skill] || [];
  2911. animationQueue[skill].push(
  2912. {
  2913. cell: cell
  2914. , color: color
  2915. , xpAmount: xpAmount
  2916. , extraXp: extraXp
  2917. });
  2918. if (animationQueue[skill].length === 1)
  2919. {
  2920. nextAnimation(skill);
  2921. }
  2922. }
  2923.  
  2924. function nextAnimation(skill)
  2925. {
  2926. var entry = animationQueue[skill][0];
  2927. if (!entry || !settings.get(settings.KEY.newXpAnimation))
  2928. {
  2929. return;
  2930. }
  2931. var cell = entry.cell
  2932. , color = entry.color
  2933. , xpAmount = entry.xpAmount
  2934. , extraXp = entry.extraXp;
  2935. var rect = cell.getBoundingClientRect();
  2936. var extraXpStr = extraXp > 0 ? " (+" + extraXp + ")" : '';
  2937. var $el = win.$("<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) + extraXpStr + "</div>")
  2938. .appendTo('body');
  2939. // ensure the existence of $el, so the complete-function can be called instantly if the window is hidden
  2940. $el
  2941. .animate(
  2942. {
  2943. top: '-=15px'
  2944. }
  2945. , {
  2946. duration: 1500
  2947. , easing: 'easeOutQuad'
  2948. , complete: function ()
  2949. {
  2950. animationQueue[skill].shift();
  2951. nextAnimation(skill);
  2952. }
  2953. })
  2954. .fadeOut(
  2955. {
  2956. duration: 2500
  2957. , queue: false
  2958. , complete: function ()
  2959. {
  2960. return $el.remove();
  2961. }
  2962. });
  2963. }
  2964.  
  2965. function processXpGain(data)
  2966. {
  2967. var match = data.match(XP_GAIN_REGEX);
  2968. if (!match)
  2969. {
  2970. return false;
  2971. }
  2972. var icon = match[1];
  2973. var skill = IMAGE2SKILL[icon] || '';
  2974. var color = match[2];
  2975. var xpAmount = Number(match[3]);
  2976. var extra = match[4];
  2977. var cell = document.getElementById('top-bar-level-td-' + skill);
  2978. if (!cell)
  2979. {
  2980. console.debug('match (no cell found):', match);
  2981. return false;
  2982. }
  2983. var entry = {
  2984. time: now()
  2985. , amount: xpAmount
  2986. };
  2987. if (match[4])
  2988. {
  2989. entry.extra = match[4];
  2990. }
  2991. if (skill == 'fishing')
  2992. {
  2993. log.processFishingXpChange(xpAmount);
  2994. }
  2995. var extraXp = 0;
  2996. if (extra && settings.get(settings.KEY.newXpAnimation))
  2997. {
  2998. var extraMatch = extra.match(/^\s*\(<img[^>]+src=(['"])images\/([^']+)\.png\1[^>]+>\s*(.+)\)$/);
  2999. var extraXpMatch = extra.match(/^\s*\(\+(\d+)\s*xp\)\s*$/);
  3000. if (extraMatch)
  3001. {
  3002. var icon_1 = extraMatch[2];
  3003. var text = extraMatch[3];
  3004. if (icon_1 == 'brewingKit')
  3005. {
  3006. text = '+' + text;
  3007. }
  3008. win.scrollText(icon_1, color, text);
  3009. }
  3010. else if (extraXpMatch)
  3011. {
  3012. extraXp = Number(extraXpMatch[1]);
  3013. }
  3014. else
  3015. {
  3016. win.scrollText('none', color, extra);
  3017. }
  3018. }
  3019. // save the xp event
  3020. var list = xpGainHistory[skill] || [];
  3021. list.push(entry);
  3022. xpGainHistory[skill] = list.slice(-MAX_XP_GAIN_HISTORY_LENGTH);
  3023. store.set(XP_GAIN_KEY, xpGainHistory);
  3024. if (settings.get(settings.KEY.newXpAnimation))
  3025. {
  3026. queueXpAnimation(skill, cell, color, xpAmount, extraXp);
  3027. }
  3028. return true;
  3029. }
  3030.  
  3031. function processLevelUp(data)
  3032. {
  3033. if (!data.startsWith('LVL_UP='))
  3034. {
  3035. return false;
  3036. }
  3037. var skill = data.substr('LVL_UP='.length);
  3038. var xp = getGameValue(skill + 'Xp');
  3039. var oldLvl = win.getLevel(xp);
  3040. log.add(
  3041. {
  3042. type: 'lvlup'
  3043. , skill: skill
  3044. , newLevel: oldLvl + 1
  3045. });
  3046. return true;
  3047. }
  3048.  
  3049. function processCombat(data)
  3050. {
  3051. var match = data.match(/^STHS=([^~]+)~([^~]+)~([^~]+)~img-(.+)~(melee|heal)$/);
  3052. if (!match)
  3053. {
  3054. return false;
  3055. }
  3056. // keep track of different battles and add the data to the current battle
  3057. var number = match[3];
  3058. if (!/\D/.test(number))
  3059. {
  3060. number = Number(number);
  3061. }
  3062. log.add(
  3063. {
  3064. type: 'combat'
  3065. , what: match[5]
  3066. , who: match[4]
  3067. , text: number
  3068. });
  3069. return true;
  3070. }
  3071.  
  3072. function processEnergy(data)
  3073. {
  3074. var match = data.match(/^ST=steak\.png~orange~\+([\d',]+)$/);
  3075. if (!match)
  3076. {
  3077. return false;
  3078. }
  3079. log.add(
  3080. {
  3081. type: 'energy'
  3082. , energy: Number(match[1].replace(/\D/g, ''))
  3083. });
  3084. return true;
  3085. }
  3086.  
  3087. function processHeat(data)
  3088. {
  3089. var match = data.match(/^ST=icons\/fire\.png~red~\+([\d',]+)$/);
  3090. if (!match)
  3091. {
  3092. return false;
  3093. }
  3094. log.add(
  3095. {
  3096. type: 'heat'
  3097. , heat: Number(match[1].replace(/\D/g, ''))
  3098. });
  3099. return true;
  3100. }
  3101.  
  3102. function processMarket(data)
  3103. {
  3104. if (data === 'ST=icons/shop.png~orange~Item Purchased')
  3105. {
  3106. log.add(
  3107. {
  3108. type: 'market'
  3109. });
  3110. return true;
  3111. }
  3112. var match = data.match(/^ST=coins\.png~yellow~\+([\d',]+)$/);
  3113. if (!match)
  3114. {
  3115. return false;
  3116. }
  3117. var coins = Number(match[1].replace(/\D/g, ''));
  3118. log.add(
  3119. {
  3120. type: 'market'
  3121. , coins: coins
  3122. });
  3123. return true;
  3124. }
  3125.  
  3126. function processBonemeal(data)
  3127. {
  3128. var match = data.match(/^ST=filledBonemealBin\.png~white~\+([\d',]+)$/);
  3129. if (!match)
  3130. {
  3131. return false;
  3132. }
  3133. var bonemeal = Number(match[1].replace(/\D/g, ''));
  3134. log.add(
  3135. {
  3136. type: 'bonemeal'
  3137. , bonemeal: bonemeal
  3138. });
  3139. return true;
  3140. }
  3141.  
  3142. function processCrafting(data)
  3143. {
  3144. if (data === 'ST=none~#806600~Item Crafted')
  3145. {
  3146. log.add(
  3147. {
  3148. type: 'crafting'
  3149. });
  3150. return true;
  3151. }
  3152. return false;
  3153. }
  3154.  
  3155. function processStardust(data)
  3156. {
  3157. var match = data.match(/^ST=(?:icons\/)?stardust\.png~yellow~\+([\d',]+)$/);
  3158. if (!match)
  3159. {
  3160. return false;
  3161. }
  3162. var stardust = Number(match[1].replace(/\D/g, ''));
  3163. log.add(
  3164. {
  3165. type: 'stardust'
  3166. , stardust: stardust
  3167. });
  3168. return true;
  3169. }
  3170. var RUNNING_ACCOUNT_STR = 'Your account has been running for:';
  3171.  
  3172. function formatData(data)
  3173. {
  3174. if (data.startsWith('STHS=')
  3175. || data.startsWith('STE=')
  3176. || data.startsWith('SM=')
  3177. || data.startsWith('ST=')
  3178. || data.startsWith('SHOW_LOOT_DIAG='))
  3179. {
  3180. if (data.indexOf(RUNNING_ACCOUNT_STR) != -1)
  3181. {
  3182. data = minutes2String(data);
  3183. }
  3184. data = format.numbersInText(data);
  3185. }
  3186. return data;
  3187. }
  3188. commands.formatData = formatData;
  3189.  
  3190. function process(data)
  3191. {
  3192. // prepare for logging events in an activity log
  3193. if (processLoot(data))
  3194. {
  3195. return;
  3196. }
  3197. else if (processXpGain(data))
  3198. {
  3199. // return undefined to let the original function be called
  3200. return settings.get(settings.KEY.newXpAnimation) ? null : void 0;
  3201. }
  3202. else if (processLevelUp(data)
  3203. || processCombat(data)
  3204. || processEnergy(data)
  3205. || processHeat(data)
  3206. || processMarket(data)
  3207. || processBonemeal(data)
  3208. || processCrafting(data)
  3209. || processStardust(data))
  3210. {
  3211. return;
  3212. }
  3213. else if (data.startsWith('SM='))
  3214. {
  3215. log.add(
  3216. {
  3217. data: minutes2String(data.replace(/^[^=]+=/, ''))
  3218. });
  3219. }
  3220. else if (data.startsWith('STHS=') || data.startsWith('STE=') || data.startsWith('ST='))
  3221. {}
  3222. // notifications for this kind of message: "SM=An update has been scheduled for today."
  3223. if (data.startsWith('SM='))
  3224. {
  3225. if (settings.getSub(settings.KEY.showNotifications, 'serverMsg'))
  3226. {
  3227. var msg = data.substr(3)
  3228. .replace(/<br\s*\/?>/g, '\n')
  3229. .replace(/<img src='images\/(.+?)\.png'.+?\/?> (\d+)/g, function (wholeMatch, key, amount)
  3230. {
  3231. return format.number(amount) + ' ' + split2Words(key) + ', ';
  3232. })
  3233. .replace(/<.+?>/g, '')
  3234. .replace(/(\s)\1+/g, '$1')
  3235. .replace(/, $/, '');
  3236. notifications.event('Message from server'
  3237. , {
  3238. body: minutes2String(msg)
  3239. });
  3240. }
  3241. }
  3242. return;
  3243. }
  3244. commands.process = process;
  3245. })(commands || (commands = {}));
  3246.  
  3247. /**
  3248. * log activities and stuff
  3249. */
  3250. var log;
  3251. (function (log)
  3252. {
  3253. log.name = 'log';
  3254. var LOG_KEY = 'activityLog';
  3255. var MAX_LOG_SIZE = 100;
  3256. var logList = store.has(LOG_KEY) ? store.get(LOG_KEY) : [];
  3257. var currentCombat = null;
  3258. var currentCombatEl = null;
  3259. var LOG_FILTER = {
  3260. 'combat':
  3261. {
  3262. title: 'Combat'
  3263. , img: 'images/icons/combat.png'
  3264. }
  3265. , 'loot':
  3266. {
  3267. title: 'Loot'
  3268. , img: 'images/npcLoot0.png'
  3269. }
  3270. , 'fish':
  3271. {
  3272. title: 'Caught fish'
  3273. , img: 'images/tuna.png'
  3274. }
  3275. , 'skill':
  3276. {
  3277. title: 'Skill advance'
  3278. , img: 'images/icons/skills.png'
  3279. }
  3280. , 'other':
  3281. {
  3282. title: 'All other'
  3283. , label: 'Other'
  3284. }
  3285. };
  3286. var logEl;
  3287.  
  3288. function isFightStarted()
  3289. {
  3290. return win.fightMonsterId !== 0;
  3291. }
  3292.  
  3293. function saveLog()
  3294. {
  3295. store.set(LOG_KEY, logList);
  3296. }
  3297.  
  3298. function createLi(entry)
  3299. {
  3300. var entryEl = document.createElement('li');
  3301. entryEl.dataset.time = (new Date(entry.time || 0)).toLocaleString();
  3302. entryEl.dataset.type = entry.type;
  3303. return entryEl;
  3304. }
  3305.  
  3306. function appendLi(entryEl)
  3307. {
  3308. var filterEl = logEl.firstElementChild;
  3309. var next = filterEl && filterEl.nextElementSibling;
  3310. if (next)
  3311. {
  3312. logEl.insertBefore(entryEl, next);
  3313. }
  3314. else
  3315. {
  3316. logEl.appendChild(entryEl);
  3317. }
  3318. logEl.classList.remove('empty');
  3319. }
  3320.  
  3321. function setGenericEntry(entry, init)
  3322. {
  3323. var el = createLi(entry);;
  3324. el.innerHTML = typeof entry.data === 'string' ? format.numbersInText(entry.data) : JSON.stringify(entry.data);
  3325. appendLi(el);
  3326. }
  3327.  
  3328. function setLootEntry(entry, init)
  3329. {
  3330. var el = createLi(entry);
  3331. var header = document.createElement('h1');
  3332. header.className = 'container-title';
  3333. header.textContent = entry.title;
  3334. el.appendChild(header);
  3335. var itemContainer = document.createElement('span');
  3336. if (entry.itemList.length === 0)
  3337. {
  3338. itemContainer.innerHTML = "<span class=\"dialogue-loot\">" + entry.emptyText + "</span>";
  3339. }
  3340. else
  3341. {
  3342. var update = false;
  3343. for (var _i = 0, _a = entry.itemList; _i < _a.length; _i++)
  3344. {
  3345. var item = _a[_i];
  3346. if (item.hasOwnProperty('key'))
  3347. {
  3348. item.icon = item.key;
  3349. delete item.key;
  3350. update = true;
  3351. }
  3352. if (item.hasOwnProperty('amount'))
  3353. {
  3354. item.text = (item.amount || Number.NaN).toString();
  3355. delete item.amount;
  3356. update = true;
  3357. }
  3358. var itemEl = document.createElement('span');
  3359. itemEl.className = 'dialogue-loot';
  3360. itemEl.innerHTML = "<img src=\"" + item.icon + "\" class=\"image-icon-50\"> " + format.numbersInText(item.text);
  3361. itemContainer.appendChild(itemEl);
  3362. itemContainer.appendChild(document.createTextNode(' '));
  3363. }
  3364. if (update)
  3365. {
  3366. saveLog();
  3367. }
  3368. }
  3369. el.appendChild(itemContainer);
  3370. var valueContainer = document.createElement('div');
  3371. valueContainer.className = 'total-value';
  3372. valueContainer.appendChild(document.createTextNode('Total value: '));
  3373. var totalValue = document.createElement('span');
  3374. totalValue.style.cursor = 'pointer';
  3375. totalValue.textContent = 'Click to calculate';
  3376. valueContainer.appendChild(totalValue);
  3377. totalValue.addEventListener('click', function ()
  3378. {
  3379. var items = {};
  3380. for (var _i = 0, _a = entry.itemList; _i < _a.length; _i++)
  3381. {
  3382. var item = _a[_i];
  3383. if (item.text.indexOf('xp') === -1)
  3384. {
  3385. var key = item.icon.replace(/^.+\/([^\/]+)\.png$/, '$1');
  3386. var num = Number(item.text.replace(/\D/g, ''));
  3387. items[key] = (items[key] || 0) + num;
  3388. }
  3389. }
  3390. market.calcMarketValue(items)
  3391. .then(function (sum)
  3392. {
  3393. totalValue.innerHTML = "<img class=\"image-icon-20\" src=\"images/coins.png\"> " + format.number(sum[0]) + " - <img class=\"image-icon-20\" src=\"images/coins.png\"> " + format.number(sum[1]);
  3394. });
  3395. });
  3396. el.appendChild(valueContainer);
  3397. appendLi(el);
  3398. }
  3399.  
  3400. function setFishEntry(entry, init)
  3401. {
  3402. var el = createLi(entry);
  3403. el.innerHTML = "You caught a " + key2Name(entry.fish, true) + ".";
  3404. appendLi(el);
  3405. }
  3406.  
  3407. function setEnergyEntry(entry, init)
  3408. {
  3409. var el = createLi(entry);
  3410. el.innerHTML = "Your hero gained " + format.number(entry.energy) + " energy.";
  3411. appendLi(el);
  3412. }
  3413.  
  3414. function setHeatEntry(entry, init)
  3415. {
  3416. var el = createLi(entry);
  3417. el.innerHTML = "You added " + format.number(entry.heat) + " heat to your oven.";
  3418. appendLi(el);
  3419. }
  3420.  
  3421. function setLevelUpEntry(entry, init)
  3422. {
  3423. var el = createLi(entry);
  3424. el.innerHTML = "You advanced your " + entry.skill + " skill to level " + entry.newLevel + ".";
  3425. appendLi(el);
  3426. }
  3427.  
  3428. function getCombatInfo(data, initHp, scaleX, width)
  3429. {
  3430. var points = [];
  3431. var startHp = -1;
  3432. var hp = initHp;
  3433. for (var tick in data)
  3434. {
  3435. hp = data[tick];
  3436. if (startHp === -1)
  3437. {
  3438. startHp = hp;
  3439. }
  3440. points.push((scaleX * Number(tick)) + ' ' + hp);
  3441. }
  3442. if (points.length === 0)
  3443. {
  3444. points.push('0 ' + initHp);
  3445. }
  3446. points.push(width + ' ' + hp, width + ' 0', '0 0');
  3447. return {
  3448. points: points
  3449. , startHp: startHp === -1 ? initHp : startHp
  3450. , endHp: hp
  3451. };
  3452. }
  3453.  
  3454. function getHTMLFromCombatInfo(info, name)
  3455. {
  3456. return "<div class=\"combat-log-graph\">\n\t\t\t<span>" + name + " (" + info.startHp + " Hp to " + info.endHp + " Hp):</span><br>\n\t\t\t<svg style=\"height: " + info.startHp + "px;\"><polygon points=\"" + info.points.join(',') + "\"></polygon></svg>\n\t\t</div>";
  3457. }
  3458.  
  3459. function setCombatEntry(entry, init)
  3460. {
  3461. var created = init || currentCombatEl == null;
  3462. if (init || currentCombatEl == null)
  3463. {
  3464. currentCombatEl = createLi(entry);
  3465. }
  3466. var HTML = '';
  3467. // support old log format
  3468. if (!entry.hasOwnProperty('ticks'))
  3469. {
  3470. var info = {
  3471. hero:
  3472. {
  3473. heal: 0
  3474. , melee: 0
  3475. }
  3476. , monster:
  3477. {
  3478. heal: 0
  3479. , melee: 0
  3480. }
  3481. };
  3482. for (var i = 0; i < entry.parts.length; i++)
  3483. {
  3484. var part = entry.parts[i];
  3485. info[part.who][part.type] += part.number;
  3486. }
  3487. HTML = "<div>Hero: <span style=\"color: green;\">+" + info.hero.heal + "</span> <span style=\"color: red;\">-" + info.hero.melee + "</span></div>\n\t\t\t<div>Monster: <span style=\"color: green;\">+" + info.monster.heal + "</span> <span style=\"color: red;\">-" + info.monster.melee + "</span></div>";
  3488. }
  3489. else
  3490. {
  3491. var currentTick = Math.max(entry.ticks, 0);
  3492. var width = logEl.scrollWidth - 4 * 12.8 - 2;
  3493. var scaleX = currentTick === 0 ? 0 : width / currentTick;
  3494. var hero = getCombatInfo(entry.hero, win.heroHp, scaleX, width);
  3495. var monster = getCombatInfo(entry.monster, win.fightMonsterHp, scaleX, width);
  3496. // TODO: who won?
  3497. HTML = "The fight took " + format.sec2Str(currentTick) + ".\n\t\t\t" + getHTMLFromCombatInfo(hero, 'Hero') + "\n\t\t\t" + getHTMLFromCombatInfo(monster, 'Monster') + "\n\t\t\t";
  3498. }
  3499. // map monster name and area name from monster id (the ids are starting at 1)
  3500. var isShiny = entry.monsterId > 1e3;
  3501. var mId = entry.monsterId - (isShiny ? 1001 : 1);
  3502. var monsterName = (isShiny ? 'Shiny ' : '') + (getMonsterName(mId) || '(' + (mId % 3 + 1) + ')');
  3503. var areaId = (mId == 105 || mId == 106) ? 34 : Math.floor(mId / 3);
  3504. var areaName = getAreaName(areaId) || '(' + (areaId + 1) + ')';
  3505. currentCombatEl.innerHTML = "<h2>Combat against " + monsterName + " in " + areaName + "</h2>\n\t\t" + HTML;
  3506. if (created)
  3507. {
  3508. appendLi(currentCombatEl);
  3509. }
  3510. if (!isFightStarted())
  3511. {
  3512. currentCombatEl = null;
  3513. }
  3514. }
  3515.  
  3516. function setMarketEntry(entry, init)
  3517. {
  3518. var el = createLi(entry);
  3519. if (entry.coins)
  3520. {
  3521. el.innerHTML = "You collected " + format.number(entry.coins) + " from market.";
  3522. }
  3523. else
  3524. {
  3525. el.innerHTML = "You purchased an item on market.";
  3526. }
  3527. appendLi(el);
  3528. }
  3529.  
  3530. function setBonemealEntry(entry, init)
  3531. {
  3532. var el = createLi(entry);
  3533. el.innerHTML = "You added " + format.number(entry.bonemeal) + " bonemeal to your bonemeal bin.";
  3534. appendLi(el);
  3535. }
  3536.  
  3537. function setCraftingEntry(entry, init)
  3538. {
  3539. var el = createLi(entry);
  3540. el.innerHTML = "You crafted an item.";
  3541. appendLi(el);
  3542. }
  3543.  
  3544. function setStardustEntry(entry, init)
  3545. {
  3546. var el = createLi(entry);
  3547. el.innerHTML = "You got " + format.number(entry.stardust) + " stardust.";
  3548. appendLi(el);
  3549. }
  3550. var entryType2Fn = {
  3551. 'loot': setLootEntry
  3552. , 'fish': setFishEntry
  3553. , 'energy': setEnergyEntry
  3554. , 'heat': setHeatEntry
  3555. , 'lvlup': setLevelUpEntry
  3556. , 'combat': setCombatEntry
  3557. , 'market': setMarketEntry
  3558. , 'bonemeal': setBonemealEntry
  3559. , 'crafting': setCraftingEntry
  3560. , 'stardust': setStardustEntry
  3561. };
  3562.  
  3563. function updateLog(entry, init)
  3564. {
  3565. if (init === void 0)
  3566. {
  3567. init = false;
  3568. }
  3569. if (!logEl)
  3570. {
  3571. return;
  3572. }
  3573. if (entry.type && entryType2Fn.hasOwnProperty(entry.type))
  3574. {
  3575. entryType2Fn[entry.type](entry, init);
  3576. }
  3577. else
  3578. {
  3579. setGenericEntry(entry, init);
  3580. }
  3581. }
  3582.  
  3583. function add2Log(entry)
  3584. {
  3585. logList.push(entry);
  3586. logList = logList.slice(-MAX_LOG_SIZE);
  3587. saveLog();
  3588. }
  3589. // use the last stored combat, compare monster id and health state to check whether this combat might be interrupted last time (hero health != 0 && monster health != 0) and continue logging to that fight
  3590. function findCurrentCombat()
  3591. {
  3592. for (var i = logList.length - 1; i >= 0; i--)
  3593. {
  3594. if (logList[i].type == 'combat')
  3595. {
  3596. var entry = logList[i];
  3597. if (entry.monsterId == win.fightMonsterId
  3598. && entry.hero[entry.ticks] !== 0
  3599. && entry.hero[entry.ticks] !== 0)
  3600. {
  3601. return entry;
  3602. }
  3603. break;
  3604. }
  3605. }
  3606. return null;
  3607. }
  3608.  
  3609. function add(entry)
  3610. {
  3611. if (!entry.time)
  3612. {
  3613. entry.time = now();
  3614. }
  3615. if (entry.type == 'combat')
  3616. {
  3617. currentCombat = currentCombat || findCurrentCombat();
  3618. if (!currentCombat)
  3619. {
  3620. return;
  3621. }
  3622. // skip entries without further information
  3623. if (typeof entry.text !== 'number' || entry.text === 0)
  3624. {
  3625. return;
  3626. }
  3627. var hp = entry.who == 'hero' ? win.heroHp : win.fightMonsterHp;
  3628. // the hp values are updated after this event, so I have to calculate the new value by myself
  3629. hp += (entry.what == 'heal' ? 1 : -1) * entry.text;
  3630. currentCombat[entry.who][currentCombat.ticks] = hp;
  3631. saveLog();
  3632. updateLog(currentCombat);
  3633. }
  3634. else
  3635. {
  3636. add2Log(entry);
  3637. updateLog(entry);
  3638. }
  3639. }
  3640. log.add = add;
  3641.  
  3642. function addLogEl()
  3643. {
  3644. 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:not(.filter)\n{\n\tborder: 1px solid gray;\n\tborder-radius: .2rem;\n\tdisplay: none;\n\tmargin: .2rem 0;\n\tpadding: .4rem .8rem;\n}\n#activity-log li:not(.filter)::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.combat-log-graph > svg\n{\n\ttransform: scaleY(-1);\n\twidth: 100%;\n}\n.combat-log-graph > svg polygon\n{\n\tfill: green;\n\tstroke: black;\n\tstroke-width: 1px;\n}\n#activity-log.combat > li[data-type=\"combat\"]\n{\n\tdisplay: block;\n}\n#activity-log.loot > li[data-type=\"loot\"]\n{\n\tdisplay: block;\n}\n#activity-log.fish > li[data-type=\"fish\"]\n{\n\tdisplay: block;\n}\n#activity-log.lvlup > li[data-type=\"lvlup\"]\n{\n\tdisplay: block;\n}\n#activity-log.other > li[data-type=\"energy\"],\n#activity-log.other > li[data-type=\"heat\"],\n#activity-log.other > li[data-type=\"market\"],\n#activity-log.other > li[data-type=\"bonemeal\"],\n#activity-log.other > li[data-type=\"crafting\"],\n#activity-log.other > li[data-type=\"stardust\"],\n#activity-log.other > li[data-type=\"undefined\"]\n{\n\tdisplay: block;\n}\n\t\t");
  3645. // add new tab "Activity Log"
  3646. var checkboxId = 'show-activity-log';
  3647. var activityLogLabel = document.createElement('label');
  3648. activityLogLabel.id = 'activity-log-label';
  3649. activityLogLabel.htmlFor = checkboxId;
  3650. activityLogLabel.textContent = 'Activity Log';
  3651. newTopbar.addTabEntry(activityLogLabel);
  3652. var checkbox = document.createElement('input');
  3653. checkbox.id = checkboxId;
  3654. checkbox.type = 'checkbox';
  3655. checkbox.style.display = 'none';
  3656. document.body.insertBefore(checkbox, document.body.firstChild);
  3657. var label = document.createElement('label');
  3658. label.id = 'activity-log-overlay';
  3659. label.htmlFor = checkboxId;
  3660. document.body.appendChild(label);
  3661. logEl = document.createElement('ul');
  3662. logEl.id = 'activity-log';
  3663. var classList = [];
  3664. var html = '';
  3665. for (var key in LOG_FILTER)
  3666. {
  3667. // TODO: load saved filter
  3668. var checked = true;
  3669. classList.push(key);
  3670. html += "<label for=\"log-filter-" + key + "\" title=\"" + LOG_FILTER[key].title + "\">\n\t\t\t\t" + (LOG_FILTER[key].img ? "<img class=\"image-icon-20\" src=\"" + LOG_FILTER[key].img + "\">" : LOG_FILTER[key].label) + "\n\t\t\t</label>\n\t\t\t<input type=\"checkbox\" id=\"log-filter-" + key + "\" " + (checked ? 'checked' : '') + ">";
  3671. }
  3672. logEl.className = 'empty ' + classList.join(' ');
  3673. logEl.innerHTML = "<li class=\"filter\">\n\t\t\t" + html + "\n\t\t</li>";
  3674. document.body.appendChild(logEl);
  3675. var $checkboxes = win.$('li.filter > input[id^="log-filter-"]');
  3676. $checkboxes.checkboxradio(
  3677. {
  3678. icon: false
  3679. });
  3680. $checkboxes.change(function (event)
  3681. {
  3682. var id = event.target.id;
  3683. var key = id.replace('log-filter-', '');
  3684. var checked = document.getElementById(id).checked;
  3685. logEl.classList[checked ? 'add' : 'remove'](key);
  3686. // TODO: save current state
  3687. });
  3688. // add all stored elements
  3689. logList.forEach(function (e)
  3690. {
  3691. return updateLog(e, true);
  3692. });
  3693. }
  3694.  
  3695. function observeCombat()
  3696. {
  3697. observer.add('fightMonsterId', function (key, oldValue, newValue)
  3698. {
  3699. if (isFightStarted())
  3700. {
  3701. currentCombat = {
  3702. type: 'combat'
  3703. , time: now()
  3704. , monsterId: newValue
  3705. , ticks: -5
  3706. , hero:
  3707. {}
  3708. , monster:
  3709. {}
  3710. };
  3711. add2Log(currentCombat);
  3712. updateLog(currentCombat);
  3713. }
  3714. else
  3715. {
  3716. if (currentCombat)
  3717. {
  3718. currentCombat.ticks--;
  3719. saveLog();
  3720. }
  3721. currentCombat = null;
  3722. currentCombatEl = null;
  3723. }
  3724. });
  3725. observer.addTick(function ()
  3726. {
  3727. if (currentCombat !== null)
  3728. {
  3729. currentCombat.ticks++;
  3730. if (currentCombat.ticks === 0)
  3731. {
  3732. currentCombat.hero[0] = win.heroHp;
  3733. currentCombat.monster[0] = win.fightMonsterHp;
  3734. }
  3735. updateLog(currentCombat);
  3736. }
  3737. });
  3738. }
  3739. var possiblyCaughtFish;
  3740. var lastFishingXpChange = 0;
  3741.  
  3742. function fishObserver(key, oldValue, newValue)
  3743. {
  3744. if (oldValue < newValue && lastFishingXpChange >= now() - 5e3)
  3745. {
  3746. var idx = possiblyCaughtFish.indexOf(key);
  3747. if (idx !== -1)
  3748. {
  3749. add(
  3750. {
  3751. type: 'fish'
  3752. , fish: key
  3753. });
  3754. possiblyCaughtFish = [];
  3755. lastFishingXpChange = 0;
  3756. }
  3757. }
  3758. }
  3759.  
  3760. function processFishingXpChange(xp)
  3761. {
  3762. lastFishingXpChange = now();
  3763. possiblyCaughtFish = [];
  3764. for (var fish in FISH_XP)
  3765. {
  3766. if (FISH_XP[fish] == xp)
  3767. {
  3768. possiblyCaughtFish.push(fish);
  3769. }
  3770. }
  3771. }
  3772. log.processFishingXpChange = processFishingXpChange;
  3773.  
  3774. function observeFishing()
  3775. {
  3776. for (var fish in FISH_XP)
  3777. {
  3778. observer.add(fish, fishObserver);
  3779. }
  3780. }
  3781.  
  3782. function init()
  3783. {
  3784. addLogEl();
  3785. observeCombat();
  3786. observeFishing();
  3787. }
  3788. log.init = init;
  3789. })(log || (log = {}));
  3790.  
  3791. /**
  3792. * game events
  3793. */
  3794. var gameEvents;
  3795. (function (gameEvents)
  3796. {
  3797. gameEvents.name = 'gameEvents';
  3798. // min time difference between two notifications with the same title (10 seconds)
  3799. var MIN_TIME_DIFFERENCE = 10;
  3800. gameEvents.enabled = {
  3801. smelting: true
  3802. , chopping: true
  3803. , harvest: true
  3804. , boat: true
  3805. , battle: true
  3806. , brewing: true
  3807. , market: true
  3808. , map: true
  3809. , essence: true
  3810. , rocket: true
  3811. , wind: true
  3812. , perk: true
  3813. };
  3814. var lastTimestamp = new Map();
  3815.  
  3816. function notifyTabClickable(title, body, icon, tabKey, whenActive)
  3817. {
  3818. if (whenActive === void 0)
  3819. {
  3820. whenActive = false;
  3821. }
  3822. var now = (new Date).getTime();
  3823. var timeDiff = now - (lastTimestamp.get(title) || 0);
  3824. if (timeDiff < MIN_TIME_DIFFERENCE * 1e3)
  3825. {
  3826. return;
  3827. }
  3828. var promise = notifications.event(title
  3829. , {
  3830. body: body
  3831. , icon: 'images/' + icon
  3832. , whenActive: whenActive
  3833. , onclick: function ()
  3834. {
  3835. var tabNames = tabKey.split('.');
  3836. win.openTab(tabNames[0]);
  3837. if (tabNames.length > 1)
  3838. {
  3839. win.openSubTab(tabNames[1]);
  3840. }
  3841. }
  3842. });
  3843. if (promise)
  3844. {
  3845. lastTimestamp.set(title, now);
  3846. }
  3847. }
  3848.  
  3849. function observeTimer(k, fn)
  3850. {
  3851. observer.add(k, function (key, oldValue, newValue)
  3852. {
  3853. if (oldValue > 0 && newValue == 0)
  3854. {
  3855. fn(key, oldValue, newValue);
  3856. }
  3857. });
  3858. }
  3859.  
  3860. function smelting()
  3861. {
  3862. observeTimer('smeltingPercD', function (key, oldValue, newValue)
  3863. {
  3864. if (!gameEvents.enabled.smelting || !settings.getSub(settings.KEY.showNotifications, 'smelting'))
  3865. {
  3866. return;
  3867. }
  3868. notifyTabClickable('Hot topic', 'Your smelting has finished.', getFurnaceLevelName() + 'Furnace.png', 'crafting');
  3869. });
  3870. }
  3871.  
  3872. function chopping()
  3873. {
  3874. observer.add([
  3875. 'treeStage1'
  3876. , 'treeStage2'
  3877. , 'treeStage3'
  3878. , 'treeStage4'
  3879. , 'treeStage5'
  3880. , 'treeStage6'
  3881. ], function (key, oldValue, newValue)
  3882. {
  3883. if (!gameEvents.enabled.chopping || !settings.getSub(settings.KEY.showNotifications, 'chopping'))
  3884. {
  3885. return;
  3886. }
  3887. if (newValue == 4)
  3888. {
  3889. notifyTabClickable('Wood you be mine?', 'One or more of your trees are fully grown and can be chopped.', 'icons/woodcutting.png', 'woodcutting');
  3890. }
  3891. });
  3892. }
  3893.  
  3894. function harvest()
  3895. {
  3896. observer.add([
  3897. 'farmingPatchStage1'
  3898. , 'farmingPatchStage2'
  3899. , 'farmingPatchStage3'
  3900. , 'farmingPatchStage4'
  3901. , 'farmingPatchStage5'
  3902. , 'farmingPatchStage6'
  3903. ], function (key, oldValue, newValue)
  3904. {
  3905. if (!gameEvents.enabled.harvest || !settings.getSub(settings.KEY.showNotifications, 'harvest'))
  3906. {
  3907. return;
  3908. }
  3909. if (newValue == 4)
  3910. {
  3911. notifyTabClickable('Green thumb', 'One or more of your crops is ready for harvest.', 'icons/watering-can.png', 'farming');
  3912. }
  3913. else if (newValue > 4)
  3914. {
  3915. notifyTabClickable('I didn\'t plant this', 'One or more of your crops died.', 'icons/watering-can.png', 'farming');
  3916. }
  3917. });
  3918. }
  3919.  
  3920. function boat()
  3921. {
  3922. var timerKeys = BOAT_LIST.map(function (boatKey)
  3923. {
  3924. return boatKey + 'Timer';
  3925. });
  3926. observeTimer(timerKeys, function (key, oldValue, newValue)
  3927. {
  3928. if (!gameEvents.enabled.boat || !settings.getSub(settings.KEY.showNotifications, 'boatReturned'))
  3929. {
  3930. return;
  3931. }
  3932. var boatKey = key.replace(/Timer$/, '');
  3933. notifyTabClickable('Fishy business', 'Your ' + split2Words(boatKey).toLowerCase() + ' returned from its trip.', boatKey + '.png', 'combat');
  3934. });
  3935. }
  3936.  
  3937. function battle()
  3938. {
  3939. observeTimer('combatGlobalCooldown', function (key, oldValue, newValue)
  3940. {
  3941. if (!gameEvents.enabled.battle || !settings.getSub(settings.KEY.showNotifications, 'heroReady'))
  3942. {
  3943. return;
  3944. }
  3945. notifyTabClickable('Ready to work', 'Your hero is eager to fight.', 'icons/combat.png', 'combat');
  3946. });
  3947. }
  3948.  
  3949. function brewing()
  3950. {
  3951. observeTimer([
  3952. 'barPotionTimer'
  3953. , 'seedPotionTimer'
  3954. , 'stardustPotionTimer'
  3955. , 'superStardustPotionTimer'
  3956. ], function (key, oldValue, newValue)
  3957. {
  3958. if (!gameEvents.enabled.brewing || !settings.getSub(settings.KEY.showNotifications, 'potionEffect'))
  3959. {
  3960. return;
  3961. }
  3962. var potionKey = key.replace(/Timer$/, '');
  3963. if (getGameValue(potionKey) > 0)
  3964. {
  3965. notifyTabClickable('Cheers!', 'You can drink another ' + split2Words(potionKey) + '.', key.replace(/Timer$/, '') + '.png', 'brewing');
  3966. }
  3967. });
  3968. }
  3969.  
  3970. function market()
  3971. {
  3972. var _refreshMarketSlot = win.refreshMarketSlot;
  3973. var lastCollectText = 0;
  3974. win.refreshMarketSlot = function (offerId, itemKey, amount, price, collectText, slotId, timeLeft)
  3975. {
  3976. var diff = collectText - lastCollectText;
  3977. lastCollectText = collectText;
  3978. if (gameEvents.enabled.market && settings.getSub(settings.KEY.showNotifications, 'itemsSold') && collectText > 0)
  3979. {
  3980. var soldAmount = diff / price;
  3981. var amountText = ['one (1)', 'two (2)', 'three (3)'][soldAmount - 1] || format.number(soldAmount);
  3982. var itemName = split2Words(itemKey).toLowerCase();
  3983. if (soldAmount > 1)
  3984. {
  3985. itemName = pluralize(itemName);
  3986. }
  3987. var textTemplate = function (itemText)
  3988. {
  3989. return "You've sold " + itemText + " to the market.";
  3990. };
  3991. if (amount > 0)
  3992. {
  3993. notifyTabClickable('Ka-ching', textTemplate(amountText + ' ' + itemName), 'icons/shop.png', 'playermarket');
  3994. }
  3995. else
  3996. {
  3997. notifyTabClickable('Sold out', textTemplate((soldAmount === 1 ? 'your' : 'all') + ' ' + amountText + ' ' + itemName), 'icons/shop.png', 'playermarket');
  3998. }
  3999. }
  4000. _refreshMarketSlot(offerId, itemKey, amount, price, collectText, slotId, timeLeft);
  4001. };
  4002. }
  4003.  
  4004. function gameValues()
  4005. {
  4006. observer.add('treasureMap', function (key, oldValue, newValue)
  4007. {
  4008. if (gameEvents.enabled.map && settings.getSub(settings.KEY.showNotifications, 'pirate') && oldValue < newValue)
  4009. {
  4010. notifyTabClickable('Arrrr!', 'Your pirate found a treasure map.', 'treasureMap.png', 'items');
  4011. }
  4012. });
  4013. observer.add('essence', function (key, oldValue, newValue)
  4014. {
  4015. if (oldValue < newValue)
  4016. {
  4017. var diff = newValue - oldValue;
  4018. var num = ['an', 'two', 'three'][diff - 1] || diff;
  4019. var text = 'You found ' + num + ' essence' + (diff > 1 ? 's' : '') + '.';
  4020. if (gameEvents.enabled.essence && settings.get(settings.KEY.showEssencePopup))
  4021. {
  4022. win.confirmDialogue(400, text, 'Close', '', '');
  4023. }
  4024. if (gameEvents.enabled.essence && settings.getSub(settings.KEY.showNotifications, 'essence'))
  4025. {
  4026. notifyTabClickable('Essence of Life', text, 'essence.png', 'combat.spells');
  4027. }
  4028. }
  4029. });
  4030. observer.add('rocketMoonId', function (key, oldValue, newValue)
  4031. {
  4032. if (gameEvents.enabled.rocket && settings.getSub(settings.KEY.showNotifications, 'rocket'))
  4033. {
  4034. if (newValue > 0)
  4035. {
  4036. notifyTabClickable('One small step for a man...', 'Your rocket landed on the moon.', 'rocket.png', 'mining');
  4037. }
  4038. else if (oldValue < 0 && newValue === 0)
  4039. {
  4040. notifyTabClickable('Back home', 'Your rocket returned to earth.', 'rocket.png', 'mining');
  4041. }
  4042. }
  4043. });
  4044. var WIND_DESCRIPTION = [
  4045. 'The sea is sleeping like a baby'
  4046. , 'There is a slight breeze'
  4047. , 'A normal day on the sea'
  4048. , 'There is a storm coming'
  4049. , 'The sea is raging'
  4050. ];
  4051. var WIND_CATEGORY = ['none', 'low', 'medium', 'high', 'very high'];
  4052. var _setSailBoatWind = win.setSailBoatWind;
  4053. var oldValue = -1;
  4054. win.setSailBoatWind = function (windLevel)
  4055. {
  4056. _setSailBoatWind(windLevel);
  4057. var newValue = win.sailBoatWindGlobal;
  4058. if (oldValue !== -1
  4059. && oldValue !== newValue
  4060. && win.boundSailBoat > 0
  4061. && gameEvents.enabled.wind
  4062. && settings.getSub(settings.KEY.showNotifications, 'wind'))
  4063. {
  4064. var windText = (WIND_DESCRIPTION[win.sailBoatWindGlobal] || 'The wind is turning')
  4065. + ' (' + (WIND_CATEGORY[win.sailBoatWindGlobal] || 'level ' + win.sailBoatWindGlobal) + ' wind).';
  4066. notifyTabClickable('Wind of change', windText, 'sailBoat.png', 'combat');
  4067. }
  4068. oldValue = newValue;
  4069. };
  4070. // trigger getting the wind level once at page load
  4071. // so the script can distinguish between getting the wind initially and an actual wind change
  4072. win.processTab('combat');
  4073. // achievements (e.g. achBrewingEasyCompleted)
  4074. var achRegex = /^ach([A-Z][a-z]+)([A-Z][a-z]+)Completed$/;
  4075.  
  4076. function checkAchievement(key, oldValue, newValue)
  4077. {
  4078. if (gameEvents.enabled.perk && settings.getSub(settings.KEY.showNotifications, 'perk') && oldValue < newValue)
  4079. {
  4080. var match = key.match(/^ach([A-Z][a-z]+)([A-Z][a-z]+)Completed$/);
  4081. var skillName = match[1].toLowerCase();
  4082. var difficulty = match[2].toLowerCase();
  4083. notifyTabClickable('New perk unlocked', 'You completed the ' + difficulty + ' ' + skillName + ' achievement set.', 'achievementBook.png', 'achievements');
  4084. }
  4085. }
  4086. for (var _i = 0, _a = win.jsItemArray; _i < _a.length; _i++)
  4087. {
  4088. var key = _a[_i];
  4089. if (achRegex.test(key))
  4090. {
  4091. observer.add(key, checkAchievement);
  4092. }
  4093. }
  4094. var stardustEl = document.querySelector('span[data-item-display="stardust"]');
  4095. var parent = stardustEl && stardustEl.parentElement;
  4096. if (stardustEl && parent)
  4097. {
  4098. addStyle("\n#dh2qol-stardustMonitor\n{\n\tdisplay: none !important;\n}\n#stardust-change\n{\n\tcolor: grey;\n\tdisplay: inline-block;\n\tmargin-left: .25rem;\n\ttext-align: left;\n\twidth: 2.5rem;\n}\n#stardust-change.hide\n{\n\tvisibility: hidden;\n}\n\t\t\t");
  4099. var changeEl_1 = document.createElement('span');
  4100. changeEl_1.className = 'hide';
  4101. changeEl_1.id = 'stardust-change';
  4102. parent.appendChild(changeEl_1);
  4103. var HIDE_AFTER_TICKS_1 = 5;
  4104. var ticksSinceSdChange_1 = HIDE_AFTER_TICKS_1;
  4105. var sdDiff_1 = 0;
  4106. observer.add('stardust', function (key, oldValue, newValue)
  4107. {
  4108. sdDiff_1 = Math.max(newValue - oldValue, 0);
  4109. if (sdDiff_1 > 0)
  4110. {
  4111. ticksSinceSdChange_1 = 0;
  4112. }
  4113. });
  4114. observer.addTick(function ()
  4115. {
  4116. var show = settings.get(settings.KEY.showSdChange) && ticksSinceSdChange_1 < HIDE_AFTER_TICKS_1;
  4117. changeEl_1.classList[show ? 'remove' : 'add']('hide');
  4118. ticksSinceSdChange_1++;
  4119. var diff = ticksSinceSdChange_1 > 1 ? 0 : sdDiff_1;
  4120. var sign = diff > 0 ? '+' : PLUS_MINUS_SIGN;
  4121. changeEl_1.textContent = '(' + sign + format.number(diff) + ')';
  4122. });
  4123. }
  4124. }
  4125.  
  4126. function init()
  4127. {
  4128. smelting();
  4129. chopping();
  4130. harvest();
  4131. boat();
  4132. battle();
  4133. brewing();
  4134. market();
  4135. gameValues();
  4136. }
  4137. gameEvents.init = init;
  4138. })(gameEvents || (gameEvents = {}));
  4139.  
  4140. /**
  4141. * hide crafting recipes of lower tiers or of maxed machines
  4142. */
  4143. var crafting;
  4144. (function (crafting)
  4145. {
  4146. crafting.name = 'crafting';
  4147. /**
  4148. * hide crafted recipes
  4149. */
  4150. function setRecipeVisibility(key, visible)
  4151. {
  4152. var recipeRow = document.getElementById('crafting-' + key);
  4153. if (recipeRow)
  4154. {
  4155. recipeRow.style.display = (!settings.get(settings.KEY.hideCraftingRecipes) || visible) ? '' : 'none';
  4156. }
  4157. }
  4158.  
  4159. function hideLeveledRecipes(max, getKey, init)
  4160. {
  4161. if (init === void 0)
  4162. {
  4163. init = false;
  4164. }
  4165. var keys2Observe = [];
  4166. var maxLevel = 0;
  4167. for (var i = max - 1; i >= 0; i--)
  4168. {
  4169. var level = i + 1;
  4170. var key = getKey(i);
  4171. var boundKey = getBoundKey(key);
  4172. keys2Observe.push(key);
  4173. keys2Observe.push(boundKey);
  4174. if (getGameValue(key) > 0 || getGameValue(boundKey) > 0)
  4175. {
  4176. maxLevel = Math.max(maxLevel, level);
  4177. }
  4178. setRecipeVisibility(key, level > maxLevel);
  4179. }
  4180. if (init)
  4181. {
  4182. observer.add(keys2Observe, function ()
  4183. {
  4184. return hideLeveledRecipes(max, getKey, false);
  4185. });
  4186. }
  4187. }
  4188.  
  4189. function hideToolRecipe(key, init)
  4190. {
  4191. if (init === void 0)
  4192. {
  4193. init = false;
  4194. }
  4195. var emptyKey = getTierKey(key, 0);
  4196. var keys2Observe = [emptyKey];
  4197. var hasTool = getGameValue(emptyKey) > 0;
  4198. for (var i = 0; i < TIER_LEVELS.length; i++)
  4199. {
  4200. var boundKey = getBoundKey(getTierKey(key, i));
  4201. hasTool = hasTool || getGameValue(boundKey) > 0;
  4202. keys2Observe.push(boundKey);
  4203. }
  4204. setRecipeVisibility(emptyKey, !hasTool);
  4205. if (init)
  4206. {
  4207. observer.add(keys2Observe, function ()
  4208. {
  4209. return hideToolRecipe(key, false);
  4210. });
  4211. }
  4212. }
  4213.  
  4214. function hideRecipe(key, init)
  4215. {
  4216. if (init === void 0)
  4217. {
  4218. init = false;
  4219. }
  4220. var info = RECIPE_MAX.crafting[key];
  4221. var maxValue = typeof info.max === 'function' ? info.max() : info.max;
  4222. var boundKey = getBoundKey(key);
  4223. var unbound = getGameValue(key);
  4224. var bound = getGameValue(boundKey);
  4225. var extra = (info.extraKeys || []).map(function (k)
  4226. {
  4227. return getGameValue(k);
  4228. }).reduce(function (p, c)
  4229. {
  4230. return p + c;
  4231. }, 0);
  4232. setRecipeVisibility(key, maxValue - (bound + unbound + extra) > 0);
  4233. if (init)
  4234. {
  4235. observer.add([key, boundKey], function ()
  4236. {
  4237. return hideRecipe(key, false);
  4238. });
  4239. }
  4240. }
  4241. /**
  4242. * hide useless items
  4243. */
  4244. function setItemVisibility(key, visible)
  4245. {
  4246. var itemBox = document.getElementById('item-box-' + key);
  4247. if (itemBox)
  4248. {
  4249. itemBox.style.display = getGameValue(key) > 0 && (!settings.get(settings.KEY.hideUselessItems) || visible) ? '' : 'none';
  4250. }
  4251. }
  4252.  
  4253. function hideLeveledItems(max, getKey, init)
  4254. {
  4255. if (init === void 0)
  4256. {
  4257. init = false;
  4258. }
  4259. var keys2Observe = [];
  4260. var maxLevel = 0;
  4261. for (var i = max - 1; i >= 0; i--)
  4262. {
  4263. var level = i + 1;
  4264. var key = getKey(i);
  4265. var boundKey = getBoundKey(key);
  4266. keys2Observe.push(key);
  4267. keys2Observe.push(boundKey);
  4268. if (getGameValue(boundKey) > 0)
  4269. {
  4270. maxLevel = Math.max(maxLevel, level);
  4271. }
  4272. setItemVisibility(key, level > maxLevel);
  4273. }
  4274. if (init)
  4275. {
  4276. observer.add(keys2Observe, function ()
  4277. {
  4278. return hideLeveledItems(max, getKey, false);
  4279. });
  4280. }
  4281. }
  4282.  
  4283. function hideItem(key, hideInfo, init)
  4284. {
  4285. if (init === void 0)
  4286. {
  4287. init = false;
  4288. }
  4289. var maxValue = typeof hideInfo.max === 'function' ? hideInfo.max() : hideInfo.max;
  4290. var boundKey = getBoundKey(key);
  4291. var bound = getGameValue(boundKey);
  4292. var extra = (hideInfo.extraKeys || []).map(function (k)
  4293. {
  4294. return getGameValue(k);
  4295. }).reduce(function (p, c)
  4296. {
  4297. return p + c;
  4298. }, 0);
  4299. setItemVisibility(key, (bound + extra) < maxValue);
  4300. if (init)
  4301. {
  4302. observer.add([key, boundKey], function ()
  4303. {
  4304. return hideItem(key, hideInfo, false);
  4305. });
  4306. }
  4307. }
  4308.  
  4309. function init()
  4310. {
  4311. function processRecipes(init)
  4312. {
  4313. if (init === void 0)
  4314. {
  4315. init = false;
  4316. }
  4317. // furnace
  4318. hideLeveledRecipes(FURNACE_LEVELS.length, function (i)
  4319. {
  4320. return FURNACE_LEVELS[i] + 'Furnace';
  4321. }, init);
  4322. // oil storage
  4323. hideLeveledRecipes(OIL_STORAGE_SIZES.length, function (i)
  4324. {
  4325. return 'oilStorage' + (i + 1);
  4326. }, init);
  4327. // oven recipes
  4328. hideLeveledRecipes(OVEN_LEVELS.length, function (i)
  4329. {
  4330. return OVEN_LEVELS[i] + 'Oven';
  4331. }, init);
  4332. // tools
  4333. for (var _i = 0, TIER_ITEMS_1 = TIER_ITEMS; _i < TIER_ITEMS_1.length; _i++)
  4334. {
  4335. var tool = TIER_ITEMS_1[_i];
  4336. hideToolRecipe(tool, init);
  4337. }
  4338. // other stuff
  4339. for (var key in RECIPE_MAX.crafting)
  4340. {
  4341. hideRecipe(key, init);
  4342. }
  4343. if (init)
  4344. {
  4345. settings.observe(settings.KEY.hideCraftingRecipes, function ()
  4346. {
  4347. return processRecipes(false);
  4348. });
  4349. }
  4350. }
  4351. processRecipes(true);
  4352. var _processCraftingTab = win.processCraftingTab;
  4353. win.processCraftingTab = function ()
  4354. {
  4355. var reinit = !!win.refreshLoadCraftingTable;
  4356. _processCraftingTab();
  4357. if (reinit)
  4358. {
  4359. processRecipes(false);
  4360. }
  4361. };
  4362.  
  4363. function processItems(init)
  4364. {
  4365. if (init === void 0)
  4366. {
  4367. init = false;
  4368. }
  4369. // furnace
  4370. hideLeveledItems(FURNACE_LEVELS.length, function (i)
  4371. {
  4372. return FURNACE_LEVELS[i] + 'Furnace';
  4373. }, init);
  4374. // oil storage
  4375. hideLeveledItems(OIL_STORAGE_SIZES.length, function (i)
  4376. {
  4377. return 'oilStorage' + (i + 1);
  4378. }, init);
  4379. // oven recipes
  4380. hideLeveledItems(OVEN_LEVELS.length, function (i)
  4381. {
  4382. return OVEN_LEVELS[i] + 'Oven';
  4383. }, init);
  4384. // other stuff
  4385. for (var key in RECIPE_MAX.crafting)
  4386. {
  4387. hideItem(key, RECIPE_MAX.crafting[key], init);
  4388. }
  4389. if (init)
  4390. {
  4391. settings.observe(settings.KEY.hideUselessItems, function ()
  4392. {
  4393. return processItems(false);
  4394. });
  4395. }
  4396. }
  4397. processItems(true);
  4398. }
  4399. crafting.init = init;
  4400. })(crafting || (crafting = {}));
  4401.  
  4402. /**
  4403. * improve item boxes
  4404. */
  4405. var itemBoxes;
  4406. (function (itemBoxes)
  4407. {
  4408. itemBoxes.name = 'itemBoxes';
  4409.  
  4410. function hideNumberInItemBox(key, setVisibility)
  4411. {
  4412. if (setVisibility === void 0)
  4413. {
  4414. setVisibility = false;
  4415. }
  4416. var itemBox = document.getElementById('item-box-' + key);
  4417. if (!itemBox)
  4418. {
  4419. return;
  4420. }
  4421. var numberElement = itemBox.querySelector('span[data-item-display]');
  4422. if (!numberElement)
  4423. {
  4424. return;
  4425. }
  4426. numberElement.classList.add('number-caption');
  4427. if (setVisibility)
  4428. {
  4429. numberElement.classList.remove('hide');
  4430. numberElement.classList.add('hidden');
  4431. }
  4432. else
  4433. {
  4434. numberElement.classList.remove('hidden');
  4435. numberElement.classList.add('hide');
  4436. }
  4437. }
  4438.  
  4439. function addSpan2ItemBox(key, replace, setVisibility)
  4440. {
  4441. if (replace === void 0)
  4442. {
  4443. replace = true;
  4444. }
  4445. if (setVisibility === void 0)
  4446. {
  4447. setVisibility = false;
  4448. }
  4449. if (replace)
  4450. {
  4451. hideNumberInItemBox(key, setVisibility);
  4452. }
  4453. var itemBox = document.getElementById('item-box-' + key);
  4454. if (!itemBox)
  4455. {
  4456. return;
  4457. }
  4458. var span = document.createElement('span');
  4459. span.className = 'caption';
  4460. itemBox.appendChild(span);
  4461. return span;
  4462. }
  4463.  
  4464. function addCaptionStyle()
  4465. {
  4466. var CLASS_NAME = 'show-captions';
  4467. addStyle("\nbody:not(." + CLASS_NAME + ") span.caption\n{\n\tdisplay: none;\n}\nbody." + CLASS_NAME + " span.number-caption.hidden\n{\n\tvisibility: hidden;\n}\nbody." + CLASS_NAME + " span.number-caption.hide\n{\n\tdisplay: none;\n}\n\t\t");
  4468.  
  4469. function updateBodyClass()
  4470. {
  4471. var show = settings.get(settings.KEY.showCaptions);
  4472. document.body.classList[show ? 'add' : 'remove'](CLASS_NAME);
  4473. }
  4474. updateBodyClass();
  4475. settings.observe(settings.KEY.showCaptions, function ()
  4476. {
  4477. return updateBodyClass();
  4478. });
  4479. }
  4480.  
  4481. function setOilPerSecond(span, oil)
  4482. {
  4483. span.innerHTML = "+ " + format.number(oil) + " L/s <img src=\"images/oil.png\" class=\"image-icon-20\">";
  4484. }
  4485. // show capacity of furnace
  4486. function addFurnaceCaption()
  4487. {
  4488. for (var i = 0; i < FURNACE_LEVELS.length; i++)
  4489. {
  4490. var key = FURNACE_LEVELS[i] + 'Furnace';
  4491. var boundKey = getBoundKey(key);
  4492. var capacitySpan = addSpan2ItemBox(boundKey);
  4493. if (capacitySpan)
  4494. {
  4495. capacitySpan.classList.add('capacity');
  4496. capacitySpan.textContent = 'Capacity: ' + format.number(win.getFurnaceCapacity(boundKey), true);
  4497. }
  4498. }
  4499. // charcoal foundry
  4500. var foundryCapacitySpan = addSpan2ItemBox('charcoalFoundry');
  4501. if (foundryCapacitySpan)
  4502. {
  4503. foundryCapacitySpan.classList.add('capacity');
  4504. foundryCapacitySpan.textContent = 'Capacity: 100';
  4505. }
  4506. }
  4507. // show oil cap of oil storage
  4508. function addOilStorageCaption()
  4509. {
  4510. for (var i = 0; i < OIL_STORAGE_SIZES.length; i++)
  4511. {
  4512. var key = 'oilStorage' + (i + 1);
  4513. var capSpan = addSpan2ItemBox(getBoundKey(key));
  4514. if (capSpan)
  4515. {
  4516. capSpan.classList.add('oil-cap');
  4517. capSpan.textContent = 'Oil cap: ' + format.number(OIL_STORAGE_SIZES[i], true);
  4518. }
  4519. }
  4520. }
  4521. var oilPipeOrbKey = 'boundBlueOilPipeOrb';
  4522.  
  4523. function setOilPipeCaption(span)
  4524. {
  4525. setOilPerSecond(span, 50 + win.achMiningEasyCompleted * 50 + getGameValue(oilPipeOrbKey) * 100);
  4526. }
  4527. // show oil per second
  4528. function addOilCaption()
  4529. {
  4530. addStyle("\n#item-box-handheldOilPump,\n#item-box-boundOilPipe,\n#item-box-boundPumpjacks,\n#item-box-boundOilFactory\n{\n\tposition: relative;\n}\nspan.caption.oil\n{\n\tfont-size: .9rem;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n}\nspan.caption.oil img[src=\"images/oil.png\"]\n{\n\t-webkit-filter: drop-shadow(0px 0px 5px rgb(255,255,255));\n\tfilter: url(#drop-shadow);\n\t-ms-filter: \"progid:DXImageTransform.Microsoft.Dropshadow(OffX=0, OffY=0, Color='#FFF')\";\n\tfilter: \"progid:DXImageTransform.Microsoft.Dropshadow(OffX=0, OffY=0, Color='#FFF')\";\n}\n\t\t");
  4531. var tpl = document.createElement('templateWrapper');
  4532. tpl.innerHTML = "\n<svg height=\"0\" xmlns=\"http://www.w3.org/2000/svg\">\n <filter id=\"drop-shadow\">\n <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"1\"></feGaussianBlur>\n <feOffset dx=\"0\" dy=\"0\" result=\"offsetblur\"></feOffset>\n <feFlood flood-color=\"rgba(255,255,255,1)\"></feFlood>\n <feComposite in2=\"offsetblur\" operator=\"in\"></feComposite>\n <feMerge>\n\n <feMergeNode></feMergeNode><feMergeNode in=\"SourceGraphic\"></feMergeNode>\n </feMerge>\n </filter>\n</svg>\n\t\t";
  4533. var shadowDrop = tpl.firstElementChild;
  4534. document.body.appendChild(shadowDrop);
  4535. var handheldOilSpan = addSpan2ItemBox('handheldOilPump', true, true);
  4536. if (handheldOilSpan)
  4537. {
  4538. handheldOilSpan.classList.add('oil');
  4539. setOilPerSecond(handheldOilSpan, 1 * win.miner);
  4540. observer.add('miner', function ()
  4541. {
  4542. return setOilPerSecond(handheldOilSpan, 1 * win.miner);
  4543. });
  4544. }
  4545. var oilPipeSpan = addSpan2ItemBox('boundOilPipe', true, true);
  4546. if (oilPipeSpan)
  4547. {
  4548. oilPipeSpan.classList.add('oil');
  4549. setOilPipeCaption(oilPipeSpan);
  4550. observer.add(oilPipeOrbKey, function ()
  4551. {
  4552. return setOilPipeCaption(oilPipeSpan);
  4553. });
  4554. }
  4555. // add pump jack oil display
  4556. var pumpjackSpan = addSpan2ItemBox('boundPumpjacks', false);
  4557. if (pumpjackSpan)
  4558. {
  4559. pumpjackSpan.classList.add('oil');
  4560. var setCaption_1 = function ()
  4561. {
  4562. return setOilPerSecond(pumpjackSpan, win.boundPumpjacks * 10);
  4563. };
  4564. setCaption_1();
  4565. observer.add('boundPumpjacks', function ()
  4566. {
  4567. return setCaption_1();
  4568. });
  4569. }
  4570. // add number of workers as caption to oil factory
  4571. var workerSpan = addSpan2ItemBox('boundOilFactory');
  4572. if (workerSpan)
  4573. {
  4574. var setCaption_2 = function ()
  4575. {
  4576. return workerSpan.textContent = 'Workers: ' + format.number(win.oilFactoryCheapWorkers, true);
  4577. };
  4578. setCaption_2();
  4579. observer.add('oilFactoryCheapWorkers', function ()
  4580. {
  4581. return setCaption_2();
  4582. });
  4583. }
  4584. var factoryOilSpan = addSpan2ItemBox('boundOilFactory');
  4585. if (factoryOilSpan)
  4586. {
  4587. factoryOilSpan.classList.add('oil');
  4588. var setCaption_3 = function ()
  4589. {
  4590. return setOilPerSecond(factoryOilSpan, win.oilFactoryCheapWorkers);
  4591. };
  4592. setCaption_3();
  4593. observer.add('oilFactoryCheapWorkers', function ()
  4594. {
  4595. return setCaption_3();
  4596. });
  4597. }
  4598. }
  4599.  
  4600. function addWandCaption()
  4601. {
  4602. for (var i = 0; i < WAND_LEVELS.length; i++)
  4603. {
  4604. var level = WAND_LEVELS[i];
  4605. var key = level + 'Wand';
  4606. var wandSpan = addSpan2ItemBox(key);
  4607. if (wandSpan)
  4608. {
  4609. wandSpan.textContent = capitalize(level) + ' Wand';
  4610. }
  4611. }
  4612. }
  4613.  
  4614. function addVariousCaptions()
  4615. {
  4616. var key2Name = {
  4617. 'achievementBook': 'Achievements'
  4618. , 'emptyAnvil': 'Anvil'
  4619. , 'tap': 'Tree Tap'
  4620. , 'farmer': 'Farmer'
  4621. , 'spellScroll1': 'Spell Scroll 1'
  4622. , 'boundRuniteSpyglass': 'Spyglass'
  4623. , 'bobsUncle': "Bob's Uncle"
  4624. , 'boundBoatingDock': 'Boating Dock'
  4625. , 'lumberjack': 'Lumberjack'
  4626. , 'boundOilPipe': 'Oil Pipe'
  4627. , 'handheldOilPump': 'Oil Pump'
  4628. , 'boundNeedle': 'Needle'
  4629. , 'vendor': 'Vendor'
  4630. , 'boundOilStorage1': 'Oil Storage 1'
  4631. , 'boundOilStorage2': 'Oil Storage 2'
  4632. , 'boundOilStorage3': 'Oil Storage 3'
  4633. , 'boundOilStorage4': 'Oil Storage 4'
  4634. , 'boundOilStorage5': 'Oil Storage 5'
  4635. , 'boundOilStorage6': 'Oil Storage 6'
  4636. , 'boundOilStorage7': 'Oil Storage 7'
  4637. , 'strangeWand': 'Strange Wand'
  4638. , 'stardustWand': 'Stardust Wand'
  4639. , 'mapleWand': 'Maple Wand'
  4640. , 'willowWand': 'Willow Wand'
  4641. , 'oakWand': 'Oak Wand'
  4642. , 'woodenWand': 'Wooden Wand'
  4643. , 'gardener': 'Gardener'
  4644. , 'planter': 'Planter'
  4645. , 'boundBrewingKit': 'Brewing Kit'
  4646. , 'cooksBook': 'Cooks Book'
  4647. , 'cooksPage': 'Cooks Page'
  4648. , 'combatDropTable': 'Loot Table'
  4649. , 'magicBook': 'Spell Book'
  4650. };
  4651. for (var key in key2Name)
  4652. {
  4653. var span = addSpan2ItemBox(key);
  4654. if (span)
  4655. {
  4656. span.textContent = key2Name[key];
  4657. }
  4658. }
  4659. }
  4660. // show current tier
  4661. function addTierCaption()
  4662. {
  4663. addStyle("\nspan.item-box > span.orb::before\n{\n\tbackground-color: aqua;\n\tborder: 1px solid silver;\n\tborder-radius: 100%;\n\tcontent: '';\n\tdisplay: inline-block;\n\tmargin-left: -5px;\n\tmargin-right: 5px;\n\twidth: 10px;\n\theight: 10px;\n}\n\t\t");
  4664.  
  4665. function addOrbObserver(key, spanList)
  4666. {
  4667. var boundOrbKey = getBoundKey('Blue' + capitalize(key) + 'Orb');
  4668.  
  4669. function checkOrb()
  4670. {
  4671. var classAction = getGameValue(boundOrbKey) > 0 ? 'add' : 'remove';
  4672. for (var _i = 0, spanList_1 = spanList; _i < spanList_1.length; _i++)
  4673. {
  4674. var span = spanList_1[_i];
  4675. span.classList[classAction]('orb');
  4676. }
  4677. }
  4678. checkOrb();
  4679. observer.add(boundOrbKey, function ()
  4680. {
  4681. return checkOrb();
  4682. });
  4683. }
  4684. var remainingOrbItems = ORB_ITEMS;
  4685. for (var _i = 0, TIER_ITEMS_2 = TIER_ITEMS; _i < TIER_ITEMS_2.length; _i++)
  4686. {
  4687. var tierItem = TIER_ITEMS_2[_i];
  4688. var isBindable = TIER_ITEMS_NOT_BINDABLE.indexOf(tierItem) === -1;
  4689. var spanList = [];
  4690. for (var i = 0; i < TIER_LEVELS.length; i++)
  4691. {
  4692. var key = getTierKey(tierItem, i);
  4693. var toolKey = isBindable ? getBoundKey(key) : key;
  4694. var tierSpan = addSpan2ItemBox(toolKey);
  4695. if (tierSpan)
  4696. {
  4697. tierSpan.classList.add('tier');
  4698. tierSpan.textContent = TIER_NAMES[i];
  4699. spanList.push(tierSpan);
  4700. }
  4701. }
  4702. var orbIndex = remainingOrbItems.indexOf(tierItem);
  4703. if (orbIndex !== -1)
  4704. {
  4705. addOrbObserver(tierItem, spanList);
  4706. remainingOrbItems.splice(orbIndex, 1);
  4707. }
  4708. }
  4709. for (var _a = 0, remainingOrbItems_1 = remainingOrbItems; _a < remainingOrbItems_1.length; _a++)
  4710. {
  4711. var itemKey = remainingOrbItems_1[_a];
  4712. var captionSpan = document.querySelector('#item-box-' + getBoundKey(itemKey) + ' > span:last-of-type');
  4713. if (!captionSpan)
  4714. {
  4715. continue;
  4716. }
  4717. addOrbObserver(itemKey, [captionSpan]);
  4718. }
  4719. }
  4720. var boatTimerKeys = BOAT_LIST.map(function (boatKey)
  4721. {
  4722. return boatKey + 'Timer';
  4723. });
  4724.  
  4725. function checkBoat(span, timerKey, init)
  4726. {
  4727. if (init === void 0)
  4728. {
  4729. init = false;
  4730. }
  4731. var isInTransit = getGameValue(timerKey) > 0;
  4732. var otherInTransit = boatTimerKeys.some(function (k)
  4733. {
  4734. return k != timerKey && getGameValue(k) > 0 && !boundBoatingDock;
  4735. });
  4736. span.textContent = isInTransit ? 'In transit' : 'Ready';
  4737. span.style.visibility = otherInTransit ? 'hidden' : '';
  4738. var parent = span.parentElement;
  4739. parent.style.opacity = otherInTransit ? '.5' : '';
  4740. if (init)
  4741. {
  4742. observer.add(boatTimerKeys, function ()
  4743. {
  4744. return checkBoat(span, timerKey, false);
  4745. });
  4746. }
  4747. }
  4748. // show boat progress
  4749. function addBoatCaption()
  4750. {
  4751. addStyle("\n#item-box-boundSailBoat.item-box > span[data-item-display] + span + span\n{\n\tdisplay: none;\n}\n\t\t");
  4752. for (var i = 0; i < BOAT_LIST.length; i++)
  4753. {
  4754. var span = addSpan2ItemBox(getBoundKey(BOAT_LIST[i]));
  4755. if (span)
  4756. {
  4757. checkBoat(span, boatTimerKeys[i], true);
  4758. }
  4759. }
  4760. }
  4761. // show bonemeal
  4762. function addBonemealCaption()
  4763. {
  4764. var noBonemealSpan = addSpan2ItemBox('boundBonemealBin');
  4765. if (!noBonemealSpan)
  4766. {
  4767. return;
  4768. }
  4769. noBonemealSpan.textContent = 'Bonemeal: 0';
  4770. var bonemealSpan = addSpan2ItemBox('boundFilledBonemealBin');
  4771. if (!bonemealSpan)
  4772. {
  4773. return;
  4774. }
  4775. bonemealSpan.dataset.itemDisplay = 'bonemeal';
  4776. bonemealSpan.textContent = format.number(win.bonemeal);
  4777. var captionSpan = document.createElement('span');
  4778. captionSpan.className = 'caption';
  4779. captionSpan.textContent = 'Bonemeal: ';
  4780. bonemealSpan.parentElement.insertBefore(captionSpan, bonemealSpan);
  4781. }
  4782.  
  4783. function warningBeforeSellingGems()
  4784. {
  4785. var _sellNPCItemDialogue = win.sellNPCItemDialogue;
  4786. win.sellNPCItemDialogue = function (item, amount)
  4787. {
  4788. if (item == 'sapphire' || item == 'emerald' || item == 'ruby' || item == 'diamond' || item == 'bloodDiamond')
  4789. {
  4790. var itemName = key2Name(amount == 1 ? item : item.replace(/y$/, 'ie') + 's', true);
  4791. if (amount == 0
  4792. || !win.confirm('Gems are precious and rare. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  4793. {
  4794. return;
  4795. }
  4796. }
  4797. else if (item == 'logs' || item == 'oakLogs' || item == 'willowLogs' || item == 'mapleLogs' || item == 'stardustLogs' || item == 'ancientLogs')
  4798. {
  4799. var itemName = key2Name(amount == 1 ? item.replace(/s$/, '') : item, true);
  4800. if (amount == 0
  4801. || !win.confirm('Logs are time consuming to collect. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  4802. {
  4803. return;
  4804. }
  4805. }
  4806. _sellNPCItemDialogue(item, amount);
  4807. };
  4808. }
  4809.  
  4810. function addWikiaLinks()
  4811. {
  4812. var WIKIA_CLASS = 'wikia-links';
  4813. addStyle("\n." + WIKIA_CLASS + " .item-box\n{\n\tposition: relative;\n}\n.item-box > .wikia-link\n{\n\tbackground-color: black;\n\tbackground-image: " + icons.getSvgAsUrl(icons.wrapCodeWithSvg(icons.WIKIA, '-2 -2 26 27', 30, 30)) + ";\n\tbackground-repeat: 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");
  4814.  
  4815. function setWikiaLinksVisibility(init)
  4816. {
  4817. if (init === void 0)
  4818. {
  4819. init = false;
  4820. }
  4821. var show = settings.get(settings.KEY.wikiaLinks);
  4822. document.body.classList[show ? 'add' : 'remove'](WIKIA_CLASS);
  4823. if (init)
  4824. {
  4825. settings.observe(settings.KEY.wikiaLinks, function ()
  4826. {
  4827. return setWikiaLinksVisibility();
  4828. });
  4829. }
  4830. }
  4831. setWikiaLinksVisibility(true);
  4832. var boxes = document.getElementsByClassName('item-box');
  4833.  
  4834. function disableClickPropagation(el)
  4835. {
  4836. el.addEventListener('click', function (event)
  4837. {
  4838. event.stopPropagation();
  4839. });
  4840. }
  4841. for (var i = 0; i < boxes.length; i++)
  4842. {
  4843. var box = boxes.item(i);
  4844. var key = box.id.replace(/^item-box-/, '');
  4845. var linkArea = document.createElement('a');
  4846. linkArea.className = 'wikia-link';
  4847. linkArea.href = getWikiaLink(key);
  4848. linkArea.target = '_blank';
  4849. disableClickPropagation(linkArea);
  4850. box.appendChild(linkArea);
  4851. var tooltipEl = ensureTooltip('wikiLink', linkArea);
  4852. if (tooltipEl.innerHTML === '')
  4853. {
  4854. tooltipEl.innerHTML = "Click to open the wikia page about this item.";
  4855. }
  4856. }
  4857. }
  4858.  
  4859. function init()
  4860. {
  4861. addCaptionStyle();
  4862. addFurnaceCaption();
  4863. addOilStorageCaption();
  4864. addOilCaption();
  4865. addWandCaption();
  4866. addVariousCaptions();
  4867. addTierCaption();
  4868. addBoatCaption();
  4869. addBonemealCaption();
  4870. warningBeforeSellingGems();
  4871. addWikiaLinks();
  4872. }
  4873. itemBoxes.init = init;
  4874. })(itemBoxes || (itemBoxes = {}));
  4875.  
  4876. /**
  4877. * add new chat
  4878. */
  4879. var chat;
  4880. (function (chat)
  4881. {
  4882. chat.name = 'chat';
  4883. // min time difference between repeated messages to not be considered as spam
  4884. var MIN_DIFF_REPEATED_MSG = 5e3;
  4885. var KEYWORD_LIST_KEY = 'keywordList';
  4886. chat.keywordList = store.has(KEYWORD_LIST_KEY) ? store.get(KEYWORD_LIST_KEY) : [];
  4887. var CHAT_HISTORY_KEY = 'chatHistory';
  4888. var MAX_CHAT_HISTORY_LENGTH = 100;
  4889. var PM_HISTORY_KEY = 'pmHistory';
  4890. var MAX_PM_HISTORY_LENGTH = 50;
  4891. var Type;
  4892. (function (Type)
  4893. {
  4894. Type[Type["reload"] = -1] = "reload";
  4895. Type[Type["normal"] = 0] = "normal";
  4896. Type[Type["pmReceived"] = 1] = "pmReceived";
  4897. Type[Type["pmSent"] = 2] = "pmSent";
  4898. Type[Type["serverMsg"] = 3] = "serverMsg";
  4899. })(Type || (Type = {}));;
  4900. var Tag;
  4901. (function (Tag)
  4902. {
  4903. Tag[Tag["none"] = 0] = "none";
  4904. Tag[Tag["donor"] = 1] = "donor";
  4905. Tag[Tag["contributor"] = 2] = "contributor";
  4906. Tag[Tag["mod"] = 3] = "mod";
  4907. Tag[Tag["dev"] = 4] = "dev";
  4908. Tag[Tag["server"] = 5] = "server";
  4909. })(Tag || (Tag = {}));;
  4910. /**
  4911. * The chunk hiding starts with at least 10 chunks.
  4912. * So there are at least
  4913. * (chunkHidingMinChunks-1) * msgChunkSize + 1 = 9 * 100 + 1 = 901
  4914. * messages before the chunk hiding mechanism starts.
  4915. */
  4916. var CHUNK_HIDING_MIN_CHUNKS = 10;
  4917. var MSG_CHUNK_SIZE = 100;
  4918. var RELOADED_CHAT_DATA = {
  4919. timestamp: 0
  4920. , username: ''
  4921. , userlevel: 0
  4922. , icon: 0
  4923. , tag: 0
  4924. , type: Type.reload
  4925. , msg: '[...]'
  4926. };
  4927. var CHAT_BOX_ID = 'div-chat';
  4928. var DEFAULT_CHAT_DIV_ID = 'div-chat-area';
  4929. var GENERAL_CHAT_DIV_ID = 'div-chat-general';
  4930. var PM_CHAT_TAB_PREFIX = 'tab-chat-pm-';
  4931. var PM_CHAT_DIV_PREFIX = 'div-chat-pm-';
  4932. var CHAT_TABS_ID = 'chat-tabs';
  4933. var CHAT_INPUT_ID = 'chat-input-text';
  4934. var CHAT_CLASS = 'div-chat-area';
  4935. var COLORIZE_CLASS = 'colorize';
  4936. var SpecialTab;
  4937. (function (SpecialTab)
  4938. {
  4939. SpecialTab[SpecialTab["default"] = 0] = "default";
  4940. SpecialTab[SpecialTab["general"] = 1] = "general";
  4941. SpecialTab[SpecialTab["filler"] = 2] = "filler";
  4942. })(SpecialTab || (SpecialTab = {}));;
  4943. var CHAT_SPECIAL_TAB_ID = (_a = {}
  4944. , _a[SpecialTab.default] = 'tab-chat-default'
  4945. , _a[SpecialTab.general] = 'tab-chat-general'
  4946. , _a[SpecialTab.filler] = 'tab-chat-filler'
  4947. , _a);
  4948. var CONTEXTMENU_ID = 'player-contextmenu';
  4949. var CHAT_ICONS = [
  4950. {
  4951. key: ''
  4952. , title: ''
  4953. }
  4954. , {
  4955. key: 'halloween2015'
  4956. , title: 'Halloween Gamer (2015)'
  4957. }
  4958. , {
  4959. key: 'christmas2015'
  4960. , title: 'Chirstmas Gamer (2015)'
  4961. }
  4962. , {
  4963. key: 'easter2016'
  4964. , title: 'Easter Gamer (2016)'
  4965. }
  4966. , {
  4967. key: 'halloween2016'
  4968. , title: 'Halloween Gamer (2016)'
  4969. }
  4970. , {
  4971. key: 'christmas2016'
  4972. , title: 'Chirstmas Gamer (2016)'
  4973. }
  4974. , {
  4975. key: 'dh1Max'
  4976. , title: 'DH1 Pro'
  4977. }
  4978. , {
  4979. key: 'hardcore'
  4980. , title: 'Hardcore Player'
  4981. }
  4982. , {
  4983. key: 'quest'
  4984. , title: 'Questmaster'
  4985. }
  4986. , {
  4987. key: 'maxMining'
  4988. , title: 'Mastery in mining'
  4989. }
  4990. , {
  4991. key: 'maxCrafting'
  4992. , title: 'Mastery in crafting'
  4993. }
  4994. , {
  4995. key: 'maxWC'
  4996. , title: 'Mastery in woodcutting'
  4997. }
  4998. , {
  4999. key: 'maxFarming'
  5000. , title: 'Mastery in farming'
  5001. }
  5002. , {
  5003. key: 'maxBrewing'
  5004. , title: 'Mastery in brewing'
  5005. }
  5006. , {
  5007. key: 'maxCombat'
  5008. , title: 'Mastery in combat'
  5009. }
  5010. , {
  5011. key: 'maxMagic'
  5012. , title: 'Mastery in magic'
  5013. }
  5014. , {
  5015. key: 'maxFishing'
  5016. , title: 'Mastery in fishing'
  5017. }
  5018. , {
  5019. key: 'maxCooking'
  5020. , title: 'Mastery in cooking'
  5021. }
  5022. , {
  5023. key: 'maxLevel'
  5024. , title: 'Mastery of all skills'
  5025. }
  5026. , {
  5027. key: 'birdcage'
  5028. , title: 'Stole a birdcage'
  5029. }
  5030. , {
  5031. key: 'achievement'
  5032. , title: 'Achievement Hunter'
  5033. }
  5034. , {
  5035. key: 'pinkPartyHat'
  5036. , title: 'Pink Party Hat'
  5037. }
  5038. , {
  5039. key: 'redPartyHat'
  5040. , title: 'Red Party Hat'
  5041. }
  5042. , {
  5043. key: 'greenPartyHat'
  5044. , title: 'Green Party Hat'
  5045. }
  5046. , {
  5047. key: 'yellowPartyHat'
  5048. , title: 'Yellow Party Hat'
  5049. }
  5050. , {
  5051. key: 'whitePartyHat'
  5052. , title: 'White Party Hat'
  5053. }
  5054. , {
  5055. key: 'bluePartyHat'
  5056. , title: 'Blue Party Hat'
  5057. }];
  5058. var getUnknownChatIcon = function (icon)
  5059. {
  5060. return {
  5061. key: 'unknown'
  5062. , title: ''
  5063. , img: '<img src="images/chat-icons/' + icon + '.png" class="image-icon-20" />'
  5064. };
  5065. };
  5066. var CHAT_TAGS = [
  5067. null
  5068. , {
  5069. key: 'donor'
  5070. , name: ''
  5071. }
  5072. , {
  5073. key: 'contributor'
  5074. , name: 'Contributor'
  5075. }
  5076. , {
  5077. key: 'mod'
  5078. , name: 'Moderator'
  5079. }
  5080. , {
  5081. key: 'dev'
  5082. , name: 'Dev'
  5083. }
  5084. , {
  5085. key: 'yell'
  5086. , name: 'Server Message'
  5087. }
  5088. ];
  5089. var LOCALE = 'en-US';
  5090. var LOCALE_OPTIONS = {
  5091. hour12: false
  5092. , year: 'numeric'
  5093. , month: 'long'
  5094. , day: 'numeric'
  5095. , hour: '2-digit'
  5096. , minute: '2-digit'
  5097. , second: '2-digit'
  5098. };
  5099. // game commands
  5100. var COMMANDS = [
  5101. 'pm'
  5102. , 'mute'
  5103. , 'clear'
  5104. , 'ipmute'
  5105. ];
  5106. var CLEAR_CMD = 'clear';
  5107. var TUTORIAL_CMD = 'tutorial';
  5108. // load chat history
  5109. var chatHistory = store.get(CHAT_HISTORY_KEY) || [];
  5110. var pmHistory = store.get(PM_HISTORY_KEY) || [];
  5111. // store chat colors for each user
  5112. var user2Color;
  5113. var usedColors;
  5114. // reserve color for special messages (e.g. server messages): white
  5115. var reservedColors = ['#ffffff'];
  5116. // message chunks
  5117. var msgChunkMap = new Map();
  5118. // for adding elements at startup
  5119. var chatboxFragments = new Map();
  5120. var chatInitialized = false;
  5121. // find index of last message which is not a pm
  5122. var isLastMsgNotReload = false;
  5123. for (var i = chatHistory.length - 1; i >= 0; i--)
  5124. {
  5125. if (!isDataPM(chatHistory[i]))
  5126. {
  5127. isLastMsgNotReload = chatHistory[i].type != Type.reload;
  5128. break;
  5129. }
  5130. }
  5131. // insert a placeholder for a reloaded chat
  5132. if (isLastMsgNotReload)
  5133. {
  5134. RELOADED_CHAT_DATA.timestamp = (new Date()).getTime();
  5135. chatHistory.push(RELOADED_CHAT_DATA);
  5136. }
  5137.  
  5138. function isMuted(user)
  5139. {
  5140. return user !== win.username
  5141. && win.mutedPeople.some(function (name)
  5142. {
  5143. return user.indexOf(name) > -1;
  5144. });
  5145. }
  5146.  
  5147. function isSpam(data)
  5148. {
  5149. // allow all own messages, messages from contributors, mods, devs and all server messages
  5150. if (data.username === win.username || data.tag != Tag.none)
  5151. {
  5152. return false;
  5153. }
  5154. /**
  5155. * get last message of current user
  5156. */
  5157. var historyIndex = chatHistory.indexOf(data);
  5158. if (historyIndex == -1)
  5159. {
  5160. historyIndex = chatHistory.length;
  5161. }
  5162. var lastData = null;
  5163. for (var i = historyIndex - 1; i >= 0 && (lastData === null); i--)
  5164. {
  5165. var dataBefore = chatHistory[i];
  5166. if (dataBefore.username === data.username)
  5167. {
  5168. lastData = dataBefore;
  5169. }
  5170. }
  5171. /**
  5172. * compare message and don't allow the same message twice
  5173. */
  5174. if (lastData
  5175. && lastData.msg === data.msg
  5176. && (data.timestamp - lastData.timestamp) < MIN_DIFF_REPEATED_MSG)
  5177. {
  5178. return true;
  5179. }
  5180. return false;
  5181. }
  5182.  
  5183. function saveKeywordList()
  5184. {
  5185. store.set(KEYWORD_LIST_KEY, chat.keywordList);
  5186. }
  5187.  
  5188. function addKeyword(keyword)
  5189. {
  5190. if (keyword !== '' && chat.keywordList.indexOf(keyword) === -1)
  5191. {
  5192. chat.keywordList.push(keyword);
  5193. saveKeywordList();
  5194. return true;
  5195. }
  5196. return false;
  5197. }
  5198. chat.addKeyword = addKeyword;
  5199.  
  5200. function removeKeyword(keyword)
  5201. {
  5202. var index = chat.keywordList.indexOf(keyword);
  5203. if (index !== -1)
  5204. {
  5205. chat.keywordList.splice(index, 1);
  5206. saveKeywordList();
  5207. return true;
  5208. }
  5209. return false;
  5210. }
  5211. chat.removeKeyword = removeKeyword;
  5212.  
  5213. function handleScrolling(chatbox)
  5214. {
  5215. if (win.isAutoScrolling)
  5216. {
  5217. setTimeout(function ()
  5218. {
  5219. return chatbox.scrollTop = chatbox.scrollHeight;
  5220. });
  5221. }
  5222. }
  5223. // for chat messages which arrive before DOMContentLoaded and can not be displayed since the DOM isn't ready
  5224. function processChatData(username, iconString, tagString, msg, isPM)
  5225. {
  5226. var tag = parseInt(tagString, 10);
  5227. var userlevel = 0;
  5228. var type = Type.normal;
  5229. if (isPM == 1)
  5230. {
  5231. var match = msg.match(/^\s*\[(PM from|Sent to) ([A-Za-z0-9_ ]+)\]: (.+?)\s*$/) || ['', '', username, msg];
  5232. type = match[1] == 'Sent to' ? Type.pmSent : Type.pmReceived;
  5233. username = match[2];
  5234. if (username !== 'sexy_squid')
  5235. {
  5236. username = username.replace(/_/g, ' ');
  5237. }
  5238. msg = match[3];
  5239. }
  5240. else if (tag == Tag.server)
  5241. {
  5242. type = Type.serverMsg;
  5243. }
  5244. else
  5245. {
  5246. var match = msg.match(/^\s*\((\d+)\): (.+?)\s*$/);
  5247. if (match)
  5248. {
  5249. userlevel = parseInt(match[1], 10);
  5250. msg = match[2];
  5251. }
  5252. else
  5253. {
  5254. userlevel = win.getGlobalLevel();
  5255. }
  5256. }
  5257. // unlinkify when using DH2QoL to store the plain message
  5258. if (win.addToChatBox.toString().includes('linkify(arguments[3])'))
  5259. {
  5260. msg = msg.replace(/<a href='([^']+)' target='_blank'>\1<\/a>/ig, '$1');
  5261. }
  5262. if (type == Type.pmSent)
  5263. {
  5264. // turn some critical characters into HTML entities
  5265. msg = msg.replace(/[<>]/g, function (char)
  5266. {
  5267. return '&#' + char.charCodeAt(0) + ';';
  5268. });
  5269. }
  5270. return {
  5271. timestamp: now()
  5272. , username: username
  5273. , userlevel: userlevel
  5274. , icon: parseInt(iconString, 10)
  5275. , tag: tag
  5276. , type: type
  5277. , msg: msg
  5278. };
  5279. }
  5280.  
  5281. function saveChatHistory()
  5282. {
  5283. store.set(CHAT_HISTORY_KEY, chatHistory);
  5284. }
  5285.  
  5286. function savePmHistory()
  5287. {
  5288. store.set(PM_HISTORY_KEY, pmHistory);
  5289. }
  5290.  
  5291. function add2ChatHistory(data)
  5292. {
  5293. if (data.type === Type.pmReceived
  5294. || data.type === Type.pmSent)
  5295. {
  5296. pmHistory.push(data);
  5297. pmHistory = pmHistory.slice(-MAX_PM_HISTORY_LENGTH);
  5298. savePmHistory();
  5299. }
  5300. else
  5301. {
  5302. chatHistory.push(data);
  5303. chatHistory = chatHistory.slice(-MAX_CHAT_HISTORY_LENGTH);
  5304. saveChatHistory();
  5305. }
  5306. }
  5307.  
  5308. function username2Id(username)
  5309. {
  5310. return username.replace(/ /g, '_');
  5311. }
  5312.  
  5313. function setNewCounter(tab, num, force)
  5314. {
  5315. if (force === void 0)
  5316. {
  5317. force = false;
  5318. }
  5319. var panel = getChatPanel(tab.dataset.username || '');
  5320. if (force
  5321. || !tab.classList.contains('selected')
  5322. || !win.isAutoScrolling && panel.scrollHeight > panel.scrollTop + panel.offsetHeight)
  5323. {
  5324. tab.dataset.new = num.toString();
  5325. }
  5326. }
  5327.  
  5328. function incrementNewCounter(tab)
  5329. {
  5330. setNewCounter(tab, parseInt(tab.dataset.new || '0', 10) + 1);
  5331. }
  5332.  
  5333. function getChatTab(username, specialTab)
  5334. {
  5335. var id = (specialTab != null)
  5336. ? CHAT_SPECIAL_TAB_ID[specialTab]
  5337. : PM_CHAT_TAB_PREFIX + username2Id(username);
  5338. var tab = document.getElementById(id);
  5339. if (!tab)
  5340. {
  5341. tab = document.createElement('div');
  5342. tab.className = 'chat-tab';
  5343. if (specialTab != null)
  5344. {
  5345. tab.classList.add(SpecialTab[specialTab]);
  5346. }
  5347. tab.id = id;
  5348. tab.dataset.username = username;
  5349. setNewCounter(tab, 0, true);
  5350. if (username.length > 0)
  5351. {
  5352. tab.textContent = username;
  5353. // thanks /u/Spino-Prime for pointing out this was missing
  5354. var closeSpan = document.createElement('span');
  5355. closeSpan.className = 'close';
  5356. tab.appendChild(closeSpan);
  5357. }
  5358. var chatTabs = document.getElementById(CHAT_TABS_ID);
  5359. var filler = chatTabs.querySelector('.filler');
  5360. if (filler)
  5361. {
  5362. chatTabs.insertBefore(tab, filler);
  5363. }
  5364. else
  5365. {
  5366. chatTabs.appendChild(tab);
  5367. }
  5368. }
  5369. return tab;
  5370. }
  5371.  
  5372. function getChatPanel(username)
  5373. {
  5374. var id = username == '' ? GENERAL_CHAT_DIV_ID : PM_CHAT_DIV_PREFIX + username2Id(username);
  5375. var panel = document.getElementById(id);
  5376. if (!panel)
  5377. {
  5378. panel = document.createElement('div');
  5379. panel.setAttribute('disabled', 'disabled');
  5380. panel.id = id;
  5381. panel.className = CHAT_CLASS;
  5382. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5383. var height = defaultChat.style.height;
  5384. panel.style.height = height;
  5385. var chatDiv = defaultChat.parentElement;
  5386. chatDiv.insertBefore(panel, defaultChat);
  5387. }
  5388. return panel;
  5389. }
  5390.  
  5391. function changeChatTab(oldTab, newTab)
  5392. {
  5393. if (oldTab)
  5394. {
  5395. oldTab.classList.remove('selected');
  5396. var oldChatPanel = void 0;
  5397. if (oldTab.classList.contains('default'))
  5398. {
  5399. oldChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5400. }
  5401. else
  5402. {
  5403. oldChatPanel = getChatPanel(oldTab.dataset.username || '');
  5404. }
  5405. oldChatPanel.classList.remove('selected');
  5406. }
  5407. newTab.classList.add('selected');
  5408. setNewCounter(newTab, 0, true);
  5409. var newChatPanel;
  5410. if (newTab.classList.contains('default'))
  5411. {
  5412. newChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5413. }
  5414. else
  5415. {
  5416. newChatPanel = getChatPanel(newTab.dataset.username || '');
  5417. }
  5418. newChatPanel.classList.add('selected');
  5419. var toUsername = newTab.dataset.username;
  5420. var newTextPlaceholder = toUsername == '' ? win.username + ':' : 'PM to ' + toUsername + ':';
  5421. document.getElementById(CHAT_INPUT_ID).placeholder = newTextPlaceholder;
  5422. handleScrolling(newChatPanel);
  5423. }
  5424.  
  5425. function clearChat(username)
  5426. {
  5427. if (username === '')
  5428. {
  5429. // clean server chat
  5430. chatHistory = [];
  5431. saveChatHistory();
  5432. }
  5433. else
  5434. {
  5435. // delete pms stored for that user
  5436. for (var i = 0; i < pmHistory.length; i++)
  5437. {
  5438. var data = pmHistory[i];
  5439. if (data.username == username)
  5440. {
  5441. pmHistory.splice(i, 1);
  5442. i--;
  5443. }
  5444. }
  5445. savePmHistory();
  5446. }
  5447. // clear pm-chat panel
  5448. var panel = getChatPanel(username);
  5449. while (panel.children.length > 0)
  5450. {
  5451. panel.removeChild(panel.children[0]);
  5452. }
  5453. msgChunkMap.delete(username);
  5454. return panel;
  5455. }
  5456.  
  5457. function closeChatTab(username)
  5458. {
  5459. // clear pm-chat panel and remove message-history
  5460. clearChat(username);
  5461. // remove pm-tab (and change tab if necessary)
  5462. var selectedTab = getSelectedTab();
  5463. var tab2Close = getChatTab(username, null);
  5464. if (selectedTab.dataset.username == username)
  5465. {
  5466. var generalTab = getChatTab('', SpecialTab.general);
  5467. changeChatTab(tab2Close, generalTab);
  5468. }
  5469. var tabContainer = tab2Close.parentElement;
  5470. tabContainer.removeChild(tab2Close);
  5471. }
  5472.  
  5473. function isDataPM(data)
  5474. {
  5475. return data.type === Type.pmSent || data.type === Type.pmReceived;
  5476. }
  5477.  
  5478. function colorizeMsg(username)
  5479. {
  5480. if (username == '')
  5481. {
  5482. return null;
  5483. }
  5484. if (!user2Color.has(username))
  5485. {
  5486. var color = void 0;
  5487. do {
  5488. var colorizer = settings.getSub(settings.KEY.colorizeChat, 'colorizer');
  5489. if (colorizer == 1)
  5490. {
  5491. color = colorGenerator.getRandom(
  5492. {
  5493. luminosity: 'light'
  5494. });
  5495. }
  5496. else if (colorizer == 2)
  5497. {
  5498. color = colorGenerator.getRandom(
  5499. {
  5500. luminosity: 'dark'
  5501. });
  5502. }
  5503. else
  5504. {
  5505. color = colorGenerator.getEquallyDistributed();
  5506. }
  5507. } while (usedColors.has(color));
  5508. user2Color.set(username, color);
  5509. usedColors.add(color);
  5510. addStyle("\n#" + CHAT_BOX_ID + "." + COLORIZE_CLASS + " .chat-msg[data-username=\"" + username + "\"]\n{\n\tbackground-color: " + color + ";\n}\n\t\t\t", 'name-color');
  5511. }
  5512. return user2Color.get(username);
  5513. }
  5514.  
  5515. function createMessageSegment(data)
  5516. {
  5517. var isThisPm = isDataPM(data);
  5518. var msgUsername = data.type === Type.pmSent ? win.username : data.username;
  5519. var history = isThisPm ? pmHistory : chatHistory;
  5520. var historyIndex = history.indexOf(data);
  5521. var isSameUser = null;
  5522. var isSameTime = null;
  5523. for (var i = historyIndex - 1; i >= 0 && (isSameUser === null || isSameTime === null); i--)
  5524. {
  5525. var dataBefore = history[i];
  5526. if (isThisPm === isDataPM(dataBefore))
  5527. {
  5528. if (isSameUser === null)
  5529. {
  5530. var beforeUsername = dataBefore.type == Type.pmSent ? win.username : dataBefore.username;
  5531. isSameUser = beforeUsername === msgUsername;
  5532. }
  5533. if (dataBefore.type != Type.reload)
  5534. {
  5535. isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(dataBefore.timestamp / 1000 / 60) === 0;
  5536. }
  5537. }
  5538. }
  5539. var d = new Date(data.timestamp);
  5540. var hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
  5541. var minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
  5542. var icon = CHAT_ICONS[data.icon] || getUnknownChatIcon(data.icon);
  5543. var tag = CHAT_TAGS[data.tag] ||
  5544. {
  5545. key: ''
  5546. , name: ''
  5547. };
  5548. var formattedMsg = data.msg
  5549. .replace(/<a href='(.+?)' target='_blank'>\1<\/a>/g, '$1')
  5550. .replace(/(https?:\/\/[^\s"<>]+)/g, '<a target="_blank" href="$1">$1</a>');
  5551. colorizeMsg(msgUsername);
  5552. var msgTitle = data.type == Type.reload ? 'Chat loaded on ' + d.toLocaleString(LOCALE, LOCALE_OPTIONS) : '';
  5553. var user = data.type === Type.serverMsg ? 'Server Message' : msgUsername;
  5554. var levelAppendix = data.type == Type.normal ? ' (' + data.userlevel + ')' : '';
  5555. var userTitle = data.tag != Tag.server ? tag.name : '';
  5556. return "<span class=\"chat-msg\" data-type=\"" + data.type + "\" data-tag=\"" + tag.key + "\" data-username=\"" + msgUsername + "\">"
  5557. + ("<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>")
  5558. + ("<span class=\"user\" data-name=\"" + msgUsername + "\" data-same-user=\"" + isSameUser + "\">")
  5559. + ("<span class=\"icon " + icon.key + "\" title=\"" + icon.title + "\"></span>")
  5560. + ("<span class=\"name chat-tag-" + tag.key + "\" title=\"" + userTitle + "\">" + user + levelAppendix + ":</span>")
  5561. + "</span>"
  5562. + ("<span class=\"msg\" title=\"" + msgTitle + "\">" + formattedMsg + "</span>")
  5563. + "</span>";
  5564. }
  5565.  
  5566. function add2Chat(data)
  5567. {
  5568. if (!chatInitialized)
  5569. {
  5570. return;
  5571. }
  5572. var isThisPm = isDataPM(data);
  5573. // don't mute pms (you can just ignore pm-tab if you like)
  5574. if (!isThisPm && isMuted(data.username))
  5575. {
  5576. return;
  5577. }
  5578. var userKey = isThisPm ? data.username : '';
  5579. if (isThisPm)
  5580. {
  5581. win.lastPMUser = data.username;
  5582. }
  5583. // username is 3-12 characters long
  5584. var chatbox = getChatPanel(userKey);
  5585. var msgChunk = msgChunkMap.get(userKey);
  5586. if (!msgChunk || msgChunk.children.length >= MSG_CHUNK_SIZE)
  5587. {
  5588. msgChunk = document.createElement('div');
  5589. msgChunk.className = 'msg-chunk';
  5590. msgChunkMap.set(userKey, msgChunk);
  5591. if (chatboxFragments != null)
  5592. {
  5593. if (!chatboxFragments.has(userKey))
  5594. {
  5595. chatboxFragments.set(userKey, document.createDocumentFragment());
  5596. }
  5597. chatboxFragments.get(userKey).appendChild(msgChunk);
  5598. }
  5599. else
  5600. {
  5601. chatbox.appendChild(msgChunk);
  5602. }
  5603. }
  5604. var tmp = document.createElement('templateWrapper');
  5605. tmp.innerHTML = createMessageSegment(data);
  5606. msgChunk.appendChild(tmp.children[0]);
  5607. handleScrolling(chatbox);
  5608. // add delay because handleScrolling is will set scrollTop delayed
  5609. setTimeout(function ()
  5610. {
  5611. var chatTab = getChatTab(userKey, isThisPm ? null : SpecialTab.general);
  5612. incrementNewCounter(chatTab);
  5613. });
  5614. }
  5615.  
  5616. function applyChatStyle()
  5617. {
  5618. addStyle("\ndiv.div-chat-area\n{\n\tpadding-left: 0;\n}\nspan.chat-msg\n{\n\tdisplay: flex;\n\tmin-height: 21px;\n\tpadding: 1px 0;\n\tpadding-left: 5px;\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.unknown > img,\n.chat-msg .user .icon:not(.unknown)::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.chat-msg .user .icon.maxMining::before\t\t{ background-image: url('images/chat-icons/9.png'); }\n.chat-msg .user .icon.maxCrafting::before\t{ background-image: url('images/chat-icons/10.png'); }\n.chat-msg .user .icon.maxWC::before\t\t\t{ background-image: url('images/chat-icons/11.png'); }\n.chat-msg .user .icon.maxFarming::before\t{ background-image: url('images/chat-icons/12.png'); }\n.chat-msg .user .icon.maxBrewing::before\t{ background-image: url('images/chat-icons/13.png'); }\n.chat-msg .user .icon.maxCombat::before\t\t{ background-image: url('images/chat-icons/14.png'); }\n.chat-msg .user .icon.maxMagic::before\t\t{ background-image: url('images/chat-icons/15.png'); }\n.chat-msg .user .icon.maxFishing::before\t{ background-image: url('images/chat-icons/16.png'); }\n.chat-msg .user .icon.maxCooking::before\t{ background-image: url('images/chat-icons/17.png'); }\n.chat-msg .user .icon.maxLevel::before\t\t{ background-image: url('images/chat-icons/18.png'); }\n.chat-msg .user .icon.birdcage::before\t\t{ background-image: url('images/chat-icons/19.png'); }\n.chat-msg .user .icon.achievement::before\t{ background-image: url('images/chat-icons/20.png'); }\n.chat-msg .user .icon.pinkPartyHat::before\t{ background-image: url('images/chat-icons/21.png'); }\n.chat-msg .user .icon.redPartyHat::before\t{ background-image: url('images/chat-icons/22.png'); }\n.chat-msg .user .icon.greenPartyHat::before\t{ background-image: url('images/chat-icons/23.png'); }\n.chat-msg .user .icon.yellowPartyHat::before\t{ background-image: url('images/chat-icons/24.png'); }\n.chat-msg .user .icon.whitePartyHat::before\t{ background-image: url('images/chat-icons/25.png'); }\n.chat-msg .user .icon.bluePartyHat::before\t{ background-image: url('images/chat-icons/26.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: calc(100% - 5px);\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 -5px -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.selected::after\n{\n\tcolor: gray;\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");
  5619. }
  5620.  
  5621. function initColorizer(init)
  5622. {
  5623. if (init === void 0)
  5624. {
  5625. init = false;
  5626. }
  5627. var usernameList = user2Color && Array.from(user2Color.keys()) || [];
  5628. user2Color = new Map();
  5629. usedColors = new Set();
  5630. for (var _i = 0, reservedColors_1 = reservedColors; _i < reservedColors_1.length; _i++)
  5631. {
  5632. var color = reservedColors_1[_i];
  5633. usedColors.add(color);
  5634. }
  5635. var colorStyle = getStyle('name-color');
  5636. colorStyle.innerHTML = '';
  5637. for (var _a = 0, usernameList_1 = usernameList; _a < usernameList_1.length; _a++)
  5638. {
  5639. var username = usernameList_1[_a];
  5640. colorizeMsg(username);
  5641. }
  5642. if (init)
  5643. {
  5644. settings.observeSub(settings.KEY.colorizeChat, 'colorizer', function ()
  5645. {
  5646. return initColorizer();
  5647. });
  5648. }
  5649. }
  5650.  
  5651. function addIntelligentScrolling()
  5652. {
  5653. // add checkbox instead of button for toggling auto scrolling
  5654. var btn = document.querySelector('input[value="Toggle Autoscroll"]');
  5655. var btnParent = btn.parentElement;
  5656. var checkboxId = 'chat-toggle-autoscroll';
  5657. // create checkbox
  5658. var toggleCheckbox = document.createElement('input');
  5659. toggleCheckbox.type = 'checkbox';
  5660. toggleCheckbox.id = checkboxId;
  5661. toggleCheckbox.checked = true;
  5662. // create label
  5663. var toggleLabel = document.createElement('label');
  5664. toggleLabel.htmlFor = checkboxId;
  5665. toggleLabel.textContent = 'Autoscroll';
  5666. btnParent.insertBefore(toggleCheckbox, btn);
  5667. btnParent.insertBefore(toggleLabel, btn);
  5668. btn.style.display = 'none';
  5669. var chatArea = document.getElementById(GENERAL_CHAT_DIV_ID);
  5670. var showScrollTextTimeout = null;
  5671.  
  5672. function setAutoScrolling(value, full)
  5673. {
  5674. if (full === void 0)
  5675. {
  5676. full = false;
  5677. }
  5678. if (win.isAutoScrolling != value)
  5679. {
  5680. toggleCheckbox.checked = value;
  5681. win.isAutoScrolling = value;
  5682. var icon_2 = 'none';
  5683. var color_1 = value ? 'lime' : 'red';
  5684. var text_1 = (value ? 'En' : 'Dis') + 'abled' + (full ? ' Autoscroll' : '');
  5685. if (full)
  5686. {
  5687. if (showScrollTextTimeout)
  5688. {
  5689. win.clearTimeout(showScrollTextTimeout);
  5690. }
  5691. showScrollTextTimeout = win.setTimeout(function ()
  5692. {
  5693. return win.scrollText(icon_2, color_1, text_1);
  5694. }, 300);
  5695. }
  5696. else
  5697. {
  5698. win.scrollText(icon_2, color_1, text_1);
  5699. }
  5700. setNewCounter(getSelectedTab(), 0, true);
  5701. return true;
  5702. }
  5703. return false;
  5704. }
  5705. toggleCheckbox.addEventListener('change', function ()
  5706. {
  5707. setAutoScrolling(this.checked);
  5708. if (this.checked && settings.get(settings.KEY.intelligentScrolling))
  5709. {
  5710. chatArea.scrollTop = chatArea.scrollHeight - chatArea.clientHeight;
  5711. }
  5712. });
  5713. var placeholderTemplate = document.createElement('div');
  5714. placeholderTemplate.className = 'placeholder';
  5715. var childStore = new WeakMap();
  5716.  
  5717. function scrollHugeChat()
  5718. {
  5719. // # of children
  5720. var chunkNum = chatArea.children.length;
  5721. // start chunk hiding at a specific amount of chunks
  5722. if (chunkNum < CHUNK_HIDING_MIN_CHUNKS)
  5723. {
  5724. return;
  5725. }
  5726. var visibleTop = chatArea.scrollTop;
  5727. var visibleBottom = visibleTop + chatArea.clientHeight;
  5728. var referenceTop = visibleTop - win.innerHeight;
  5729. var referenceBottom = visibleBottom + win.innerHeight;
  5730. var top = 0;
  5731. // never hide the last element since its size may change at any time when a new message gets appended
  5732. for (var i = 0; i < chunkNum - 1; i++)
  5733. {
  5734. var child = chatArea.children[i];
  5735. var height = child.clientHeight;
  5736. var bottom = top + height;
  5737. var isVisible = top >= referenceTop && top <= referenceBottom
  5738. || bottom >= referenceTop && bottom <= referenceBottom
  5739. || top < referenceTop && bottom > referenceBottom;
  5740. var isPlaceholder = child.classList.contains('placeholder');
  5741. if (!isVisible && !isPlaceholder)
  5742. {
  5743. var newPlaceholder = placeholderTemplate.cloneNode(false);
  5744. newPlaceholder.style.height = height + 'px';
  5745. chatArea.replaceChild(newPlaceholder, child);
  5746. childStore.set(newPlaceholder, child);
  5747. }
  5748. else if (isVisible && isPlaceholder)
  5749. {
  5750. var oldChild = childStore.get(child);
  5751. chatArea.replaceChild(oldChild, child);
  5752. childStore.delete(child);
  5753. }
  5754. top = bottom;
  5755. }
  5756. }
  5757. var delayedScrollStart = null;
  5758. var delayedScrollTimeout = null;
  5759. // does not consider pm tabs; may be changed in a future version?
  5760. chatArea.addEventListener('scroll', function ()
  5761. {
  5762. if (settings.get(settings.KEY.intelligentScrolling))
  5763. {
  5764. var scrolled2Bottom = (chatArea.scrollTop + chatArea.clientHeight) >= chatArea.scrollHeight - 1;
  5765. setAutoScrolling(scrolled2Bottom, true);
  5766. }
  5767. var n = now();
  5768. if (delayedScrollStart == null)
  5769. {
  5770. delayedScrollStart = n;
  5771. }
  5772. if (delayedScrollStart + 300 > n)
  5773. {
  5774. if (delayedScrollTimeout)
  5775. {
  5776. win.clearTimeout(delayedScrollTimeout);
  5777. }
  5778. delayedScrollTimeout = win.setTimeout(function ()
  5779. {
  5780. delayedScrollStart = null;
  5781. delayedScrollTimeout = null;
  5782. scrollHugeChat();
  5783. }, 50);
  5784. }
  5785. });
  5786. }
  5787.  
  5788. function getSelectedTab()
  5789. {
  5790. return document.querySelector('#' + CHAT_TABS_ID + ' .chat-tab.selected');
  5791. }
  5792.  
  5793. function getSelectedTabUsername()
  5794. {
  5795. var selectedTab = getSelectedTab();
  5796. return selectedTab.dataset.username || '';
  5797. }
  5798.  
  5799. function clickChatTab(newTab)
  5800. {
  5801. var oldTab = getSelectedTab();
  5802. if (newTab == oldTab)
  5803. {
  5804. return;
  5805. }
  5806. changeChatTab(oldTab, newTab);
  5807. }
  5808.  
  5809. function clickCloseChatTab(tab)
  5810. {
  5811. var username = tab.dataset.username || '';
  5812. var chatPanel = getChatPanel(username);
  5813. if (chatPanel.children.length === 0
  5814. || confirm("Do you want to close the pm tab of \"" + username + "\"?"))
  5815. {
  5816. closeChatTab(username);
  5817. }
  5818. }
  5819.  
  5820. function checkSetting(init)
  5821. {
  5822. if (init === void 0)
  5823. {
  5824. init = false;
  5825. }
  5826. var enabled = settings.get(settings.KEY.useNewChat);
  5827. // dis-/enable chat tabs
  5828. var chatTabs = document.getElementById(CHAT_TABS_ID);
  5829. chatTabs.style.display = enabled ? '' : 'none';
  5830. // dis-/enable checkbox for intelligent scrolling
  5831. var intelScrollId = 'chat-toggle-intelligent-scroll';
  5832. var input = document.getElementById(intelScrollId);
  5833. if (input)
  5834. {
  5835. input.style.display = enabled ? '' : 'none';
  5836. }
  5837. var label = document.querySelector('label[for="' + intelScrollId + '"]');
  5838. if (label)
  5839. {
  5840. label.style.display = enabled ? '' : 'none';
  5841. }
  5842. // virtually click on a tab
  5843. var defaultTab = getChatTab('', SpecialTab.default);
  5844. var generalTab = getChatTab('', SpecialTab.general);
  5845. clickChatTab(enabled ? generalTab : defaultTab);
  5846. if (init)
  5847. {
  5848. settings.observe(settings.KEY.useNewChat, function ()
  5849. {
  5850. return checkSetting(false);
  5851. });
  5852. }
  5853. }
  5854.  
  5855. function addChatTabs()
  5856. {
  5857. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  5858. var chatTabs = document.createElement('div');
  5859. chatTabs.id = CHAT_TABS_ID;
  5860. chatTabs.addEventListener('click', function (event)
  5861. {
  5862. var newTab = event.target;
  5863. if (newTab.classList.contains('close'))
  5864. {
  5865. return clickCloseChatTab(newTab.parentElement);
  5866. }
  5867. if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler'))
  5868. {
  5869. return;
  5870. }
  5871. clickChatTab(newTab);
  5872. });
  5873. chatBoxArea.appendChild(chatTabs);
  5874. // default tab (for disabled new chat)
  5875. getChatTab('', SpecialTab.default);
  5876. // general server chat
  5877. var generalTab = getChatTab('', SpecialTab.general);
  5878. generalTab.textContent = 'Server';
  5879. getChatPanel('');
  5880. getChatTab('', SpecialTab.filler);
  5881. var _sendChat = win.sendChat;
  5882. win.sendChat = function (inputEl)
  5883. {
  5884. var msg = inputEl.value;
  5885. var selectedTab = document.querySelector('.chat-tab.selected');
  5886. if (selectedTab.dataset.username != '' && msg[0] != '/')
  5887. {
  5888. inputEl.value = '/pm ' + (selectedTab.dataset.username || '').replace(/ /g, '_') + ' ' + msg;
  5889. }
  5890. _sendChat(inputEl);
  5891. };
  5892. }
  5893.  
  5894. function switch2PmTab(username)
  5895. {
  5896. var newTab = getChatTab(username, null);
  5897. clickChatTab(newTab);
  5898. }
  5899.  
  5900. function notifyPm(data)
  5901. {
  5902. notifications.event('Message from "' + data.username + '"'
  5903. , {
  5904. body: data.msg
  5905. , onclick: function ()
  5906. {
  5907. return switch2PmTab(data.username);
  5908. }
  5909. , whenActive: getSelectedTab().dataset.username != data.username
  5910. });
  5911. }
  5912.  
  5913. function checkMentionAndKeywords(data)
  5914. {
  5915. var lowerMsg = data.msg.toLowerCase();
  5916. var usernameRegex = new RegExp('\\b' + win.username + '\\b', 'i');
  5917. if (settings.getSub(settings.KEY.showNotifications, 'mention') && usernameRegex.test(lowerMsg))
  5918. // if (lowerMsg.indexOf(win.username) > -1)
  5919. {
  5920. notifications.event('You\'ve been mentioned'
  5921. , {
  5922. body: data.msg
  5923. });
  5924. }
  5925. var match = [];
  5926. for (var _i = 0, keywordList_1 = chat.keywordList; _i < keywordList_1.length; _i++)
  5927. {
  5928. var keyword = keywordList_1[_i];
  5929. var regex = new RegExp('\\b' + keyword + '\\b', 'i');
  5930. if (regex.test(lowerMsg))
  5931. // if (lowerMsg.indexOf(keyword) > -1)
  5932. {
  5933. match.push(keyword);
  5934. }
  5935. }
  5936. if (settings.getSub(settings.KEY.showNotifications, 'keyword') && match.length > 0)
  5937. {
  5938. notifications.event('Keyword: "' + match.join('", "') + '"'
  5939. , {
  5940. body: data.msg
  5941. });
  5942. }
  5943. }
  5944. var addToChatBox_ = null;
  5945.  
  5946. function newAddToChatBox(username, icon, tag, msg, isPM)
  5947. {
  5948. var data = processChatData(username, icon, tag, msg, isPM);
  5949. var isThisSpam = false;
  5950. if (isDataPM(data))
  5951. {
  5952. if (data.type == Type.pmSent)
  5953. {
  5954. switch2PmTab(data.username);
  5955. }
  5956. else
  5957. {
  5958. notifyPm(data);
  5959. }
  5960. }
  5961. else
  5962. {
  5963. isThisSpam = settings.get(settings.KEY.enableSpamDetection) && isSpam(data);
  5964. if (!isThisSpam && data.username != win.username)
  5965. {
  5966. // check mentioning and keywords only for non-pms and only for messages from other players
  5967. checkMentionAndKeywords(data);
  5968. }
  5969. }
  5970. if (isThisSpam)
  5971. {
  5972. console.info('detected spam:', data);
  5973. }
  5974. else
  5975. {
  5976. add2ChatHistory(data);
  5977. add2Chat(data);
  5978. }
  5979. var fn = addToChatBox_ == null ? win.addToChatBox : addToChatBox_;
  5980. fn(username, icon, tag, msg, isPM);
  5981. }
  5982. chat.newAddToChatBox = newAddToChatBox;
  5983.  
  5984. function openPmTab(username)
  5985. {
  5986. if (username == win.username || username == '')
  5987. {
  5988. return;
  5989. }
  5990. var userTab = getChatTab(username, null);
  5991. clickChatTab(userTab);
  5992. var input = document.getElementById(CHAT_INPUT_ID);
  5993. input.focus();
  5994. }
  5995.  
  5996. function newChat()
  5997. {
  5998. addChatTabs();
  5999. applyChatStyle();
  6000. initColorizer(true);
  6001. addToChatBox_ = win.addToChatBox;
  6002. win.addToChatBox = newAddToChatBox;
  6003. chatInitialized = true;
  6004. var chatbox = document.getElementById(CHAT_BOX_ID);
  6005. chatbox.addEventListener('click', function (event)
  6006. {
  6007. var target = event.target;
  6008. var userEl = target && target.parentElement;
  6009. if (!target || !userEl || !target.classList.contains('name') || !userEl.classList.contains('user'))
  6010. {
  6011. return;
  6012. }
  6013. if (userEl.dataset.sameUser != 'true')
  6014. {
  6015. openPmTab(userEl.dataset.name || '');
  6016. }
  6017. });
  6018. chatbox.addEventListener('mouseover', function (event)
  6019. {
  6020. var target = event.target;
  6021. if (!target.classList.contains('timestamp') || !target.dataset.timestamp)
  6022. {
  6023. return;
  6024. }
  6025. var timestamp = parseInt(target.dataset.timestamp || '0', 10);
  6026. target.dataset.fulltime = (new Date(timestamp)).toLocaleDateString(LOCALE, LOCALE_OPTIONS);
  6027. target.dataset.timestamp = '';
  6028. });
  6029. // add context menu
  6030. var contextmenu = document.createElement('ul');
  6031. contextmenu.id = CONTEXTMENU_ID;
  6032. contextmenu.style.display = 'none';
  6033. 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>";
  6034. document.body.appendChild(contextmenu);
  6035. win.$(contextmenu).menu(
  6036. {
  6037. items: '> :not(.ui-widget-header)'
  6038. });
  6039. var nameListEl = contextmenu.querySelector('.name');
  6040. var nameDivEl = nameListEl.firstElementChild;
  6041. var muteEl = contextmenu.querySelector('.mute');
  6042. var unmuteEl = contextmenu.querySelector('.unmute');
  6043. chatbox.addEventListener('contextmenu', function (event)
  6044. {
  6045. var target = event.target;
  6046. var userEl = target && target.parentElement;
  6047. if (!userEl || !userEl.classList.contains('user'))
  6048. {
  6049. return;
  6050. }
  6051. var username = userEl.dataset.name;
  6052. // ignore clicks on server messages or other special messages
  6053. if (!username || userEl.dataset.sameUser == 'true')
  6054. {
  6055. return;
  6056. }
  6057. contextmenu.style.left = event.clientX + 'px';
  6058. contextmenu.style.top = event.clientY + 'px';
  6059. contextmenu.style.display = '';
  6060. contextmenu.dataset.username = username;
  6061. nameDivEl.textContent = username;
  6062. var isMuted = win.mutedPeople.indexOf(username) !== -1;
  6063. muteEl.style.display = isMuted ? 'none' : '';
  6064. unmuteEl.style.display = isMuted ? '' : 'none';
  6065. event.stopPropagation();
  6066. event.preventDefault();
  6067. });
  6068. // add click listener for context menu and stop propagation
  6069. contextmenu.addEventListener('click', function (event)
  6070. {
  6071. var target = event.target;
  6072. event.stopPropagation();
  6073. while (target && target.id != CONTEXTMENU_ID && target.tagName != 'LI')
  6074. {
  6075. target = target.parentElement;
  6076. }
  6077. if (!target || target.id == CONTEXTMENU_ID)
  6078. {
  6079. return;
  6080. }
  6081. var username = contextmenu.dataset.username || '';
  6082. if (target.classList.contains('open-pm'))
  6083. {
  6084. openPmTab(username);
  6085. }
  6086. else if (target.classList.contains('stats'))
  6087. {
  6088. win.lookup(username);
  6089. }
  6090. else if (target.classList.contains('mute'))
  6091. {
  6092. if (username == '')
  6093. {
  6094. return;
  6095. }
  6096. win.mutedPeople.push(username);
  6097. win.scrollText('none', 'lime', '<em>' + username + '</em> muted');
  6098. }
  6099. else if (target.classList.contains('unmute'))
  6100. {
  6101. if (username == '')
  6102. {
  6103. return;
  6104. }
  6105. var index = win.mutedPeople.indexOf(username);
  6106. if (index !== -1)
  6107. {
  6108. win.mutedPeople.splice(index, 1);
  6109. }
  6110. win.scrollText('none', 'red', '<em>' + username + '</em> unmuted');
  6111. }
  6112. else
  6113. {
  6114. return;
  6115. }
  6116. contextmenu.style.display = 'none';
  6117. });
  6118. // add click listener to hide context menu
  6119. document.addEventListener('click', function (event)
  6120. {
  6121. if (contextmenu.style.display != 'none')
  6122. {
  6123. contextmenu.style.display = 'none';
  6124. }
  6125. });
  6126. win.addEventListener('contextmenu', function (event)
  6127. {
  6128. if (contextmenu.style.display != 'none')
  6129. {
  6130. contextmenu.style.display = 'none';
  6131. }
  6132. });
  6133. // handle settings
  6134. var showSettings = [settings.KEY.showTimestamps, settings.KEY.showIcons, settings.KEY.showTags];
  6135.  
  6136. function setShowSetting(key)
  6137. {
  6138. var enabled = settings.get(key);
  6139. chatbox.classList[enabled ? 'add' : 'remove'](settings.KEY[key]);
  6140. }
  6141. for (var _i = 0, showSettings_1 = showSettings; _i < showSettings_1.length; _i++)
  6142. {
  6143. var key = showSettings_1[_i];
  6144. setShowSetting(key);
  6145. settings.observe(key, function (k)
  6146. {
  6147. return setShowSetting(k);
  6148. });
  6149. }
  6150. }
  6151.  
  6152. function addCommandSuggester()
  6153. {
  6154. var input = document.getElementById(CHAT_INPUT_ID);
  6155. input.addEventListener('keyup', function (event)
  6156. {
  6157. if (event.key == 'Backspace' || event.key == 'Delete' || event.key == 'Enter' || event.key == 'Tab'
  6158. || input.selectionStart != input.selectionEnd
  6159. || input.selectionStart != input.value.length
  6160. || !input.value.startsWith('/'))
  6161. {
  6162. return;
  6163. }
  6164. var value = input.value.substr(1);
  6165. for (var _i = 0, COMMANDS_1 = COMMANDS; _i < COMMANDS_1.length; _i++)
  6166. {
  6167. var cmd = COMMANDS_1[_i];
  6168. if (cmd.startsWith(value))
  6169. {
  6170. input.value = '/' + cmd;
  6171. input.selectionStart = 1 + value.length;
  6172. input.selectionEnd = input.value.length;
  6173. break;
  6174. }
  6175. }
  6176. });
  6177. }
  6178.  
  6179. function addOwnCommands()
  6180. {
  6181. COMMANDS.push(TUTORIAL_CMD);
  6182.  
  6183. function processOwnCommands(value)
  6184. {
  6185. if (!value.startsWith('/'))
  6186. {
  6187. return value;
  6188. }
  6189. var msgPrefix = '/';
  6190. var msg = value.substr(1);
  6191. if (msg.startsWith('pm'))
  6192. {
  6193. var split = msg.split(' ');
  6194. msgPrefix = '/' + split.slice(0, 2).join(' ') + ' ';
  6195. msg = split.slice(2).join(' ');
  6196. }
  6197. if (msg.startsWith(CLEAR_CMD))
  6198. {
  6199. // clear current chat (pm chat, or general chat)
  6200. var username = getSelectedTabUsername();
  6201. clearChat(username);
  6202. }
  6203. else if (msg.startsWith(TUTORIAL_CMD))
  6204. {
  6205. // thanks aguyd (https://greasyfork.org/forum/profile/aguyd) for the idea
  6206. var name_2 = msg.substr(TUTORIAL_CMD.length).trim();
  6207. msgPrefix = '';
  6208. msg = 'https://www.reddit.com/r/DiamondHunt/comments/5vrufh/diamond_hunt_2_starter_faq/';
  6209. if (name_2.length != 0)
  6210. {
  6211. // maybe add '@' before the name?
  6212. msg = name_2 + ', ' + msg;
  6213. }
  6214. }
  6215. return msgPrefix + msg;
  6216. }
  6217. var _sendChat = win.sendChat;
  6218. win.sendChat = function (inputEl)
  6219. {
  6220. inputEl.value = processOwnCommands(inputEl.value);
  6221. _sendChat(inputEl);
  6222. };
  6223. }
  6224.  
  6225. function checkColorize(init)
  6226. {
  6227. if (init === void 0)
  6228. {
  6229. init = false;
  6230. }
  6231. var chatDiv = document.getElementById(CHAT_BOX_ID);
  6232. chatDiv.classList[settings.get(settings.KEY.colorizeChat) ? 'add' : 'remove'](COLORIZE_CLASS);
  6233. if (init)
  6234. {
  6235. settings.observe(settings.KEY.colorizeChat, function ()
  6236. {
  6237. return checkColorize(false);
  6238. });
  6239. }
  6240. }
  6241.  
  6242. function init()
  6243. {
  6244. newChat();
  6245. addIntelligentScrolling();
  6246. addCommandSuggester();
  6247. addOwnCommands();
  6248. checkColorize(true);
  6249. checkSetting(true);
  6250. var _enlargeChat = win.enlargeChat;
  6251. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  6252.  
  6253. function setChatBoxHeight(height)
  6254. {
  6255. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  6256. defaultChat.style.height = height;
  6257. var generalChat = document.getElementById(GENERAL_CHAT_DIV_ID);
  6258. generalChat.style.height = height;
  6259. var chatDivs = chatBoxArea.querySelectorAll('div[id^="' + PM_CHAT_DIV_PREFIX + '"]');
  6260. for (var i = 0; i < chatDivs.length; i++)
  6261. {
  6262. chatDivs[i].style.height = height;
  6263. }
  6264. }
  6265. win.enlargeChat = function (enlargeB)
  6266. {
  6267. _enlargeChat(enlargeB);
  6268. var defaultChatDiv = document.getElementById(DEFAULT_CHAT_DIV_ID);
  6269. var height = defaultChatDiv.style.height;
  6270. store.set('chat.height', height);
  6271. setChatBoxHeight(height);
  6272. handleScrolling(defaultChatDiv);
  6273. };
  6274. setChatBoxHeight(store.get('chat.height'));
  6275. // add history to chat
  6276. // TEMP >>>
  6277. // move pm entries to pm history
  6278. var changed = false;
  6279. for (var i = 0; i < chatHistory.length; i++)
  6280. {
  6281. var data = chatHistory[i];
  6282. if (isDataPM(data))
  6283. {
  6284. chatHistory.splice(i, 1);
  6285. i--;
  6286. pmHistory.push(data);
  6287. changed = true;
  6288. }
  6289. }
  6290. if (changed)
  6291. {
  6292. saveChatHistory();
  6293. savePmHistory();
  6294. }
  6295. // TEMP <<<
  6296. chatHistory.forEach(function (d)
  6297. {
  6298. return add2Chat(d);
  6299. });
  6300. pmHistory.forEach(function (d)
  6301. {
  6302. return add2Chat(d);
  6303. });
  6304. if (chatboxFragments)
  6305. {
  6306. chatboxFragments.forEach(function (fragment, key)
  6307. {
  6308. var chatbox = getChatPanel(key);
  6309. chatbox.appendChild(fragment);
  6310. });
  6311. chatboxFragments = null;
  6312. }
  6313. // reset the new counter for all tabs
  6314. var tabs = document.querySelectorAll('.chat-tab');
  6315. for (var i = 0; i < tabs.length; i++)
  6316. {
  6317. setNewCounter(tabs[i], 0, true);
  6318. }
  6319. }
  6320. chat.init = init;
  6321. var _a;
  6322. })(chat || (chat = {}));
  6323.  
  6324. /**
  6325. * hopefully only temporary fixes
  6326. */
  6327. var temporaryFixes;
  6328. (function (temporaryFixes)
  6329. {
  6330. temporaryFixes.name = 'temporaryFixes';
  6331. // update spells being clickable in combat
  6332. function setSpellsClickable()
  6333. {
  6334. var spellbox = document.getElementById('fight-spellboox');
  6335. if (spellbox)
  6336. {
  6337. for (var i = 0; i < spellbox.children.length; i++)
  6338. {
  6339. var child = spellbox.children.item(i);
  6340. if (!win.isInCombat() && child.hasAttribute('onclick'))
  6341. {
  6342. child.dataset.onclick = child.getAttribute('onclick') || '';
  6343. child.removeAttribute('onclick');
  6344. }
  6345. else if (win.isInCombat() && !!child.dataset.onclick)
  6346. {
  6347. child.setAttribute('onclick', child.dataset.onclick || '');
  6348. child.dataset.onclick = '';
  6349. }
  6350. }
  6351. }
  6352. }
  6353. // warn before unloading/reloading the tab if combat is in progress
  6354. function combatWarnOnUnload()
  6355. {
  6356. if (!win.isInCombat())
  6357. {
  6358. win.onbeforeunload = null;
  6359. }
  6360. else
  6361. {
  6362. if (win.onbeforeunload == null)
  6363. {
  6364. win.onbeforeunload = function ()
  6365. {
  6366. return 'You are in a fight!';
  6367. };
  6368. }
  6369. }
  6370. }
  6371.  
  6372. function fixCombatCountdown()
  6373. {
  6374. var el = document.getElementById('combat-countdown');
  6375. if (!el)
  6376. {
  6377. return;
  6378. }
  6379. if (win.isInCombat())
  6380. {
  6381. el.style.display = '';
  6382. var visible = win.combatCommenceTimer != 0;
  6383. el.style.visibility = visible ? '' : 'hidden';
  6384. }
  6385. }
  6386. // fix exhaustion timer and updating brewing and cooking recipes
  6387. function fixExhaustionTimer()
  6388. {
  6389. if (document.getElementById('tab-container-combat').style.display != 'none')
  6390. {
  6391. win.combatNotFightingTick();
  6392. }
  6393. }
  6394.  
  6395. function fixClientGameLoop()
  6396. {
  6397. var _clientGameLoop = win.clientGameLoop;
  6398. win.clientGameLoop = function ()
  6399. {
  6400. _clientGameLoop();
  6401. //setSpellsClickable();
  6402. combatWarnOnUnload();
  6403. fixCombatCountdown();
  6404. fixExhaustionTimer();
  6405. };
  6406. }
  6407. // fix elements of scrollText (e.g. when joining the game and receiving xp at that moment)
  6408. function fixScroller()
  6409. {
  6410. var textEls = document.querySelectorAll('div.scroller');
  6411. for (var i = 0; i < textEls.length; i++)
  6412. {
  6413. var scroller = textEls[i];
  6414. if (scroller.style.position != 'absolute')
  6415. {
  6416. scroller.style.display = 'none';
  6417. }
  6418. }
  6419. }
  6420. // fix style of tooltips
  6421. function fixTooltipStyle()
  6422. {
  6423. addStyle("\nbody > div.tooltip > h2:first-child\n{\n\tmargin-top: 0;\n\tfont-size: 20pt;\n\tfont-weight: normal;\n}\n\t\t");
  6424. }
  6425. // fix buiulding magic table dynamically
  6426. function fixRefreshingMagicRecipes()
  6427. {
  6428. // define missing properties for checking the needed materials
  6429. win.enchantStargemPotionMagic = 0;
  6430. win.changeWeatherMagic = 0;
  6431. win.refreshLoadMagicTable = false;
  6432. var _processMagicTab = win.processMagicTab;
  6433. win.processMagicTab = function ()
  6434. {
  6435. var _refreshLoadCraftingTable = win.refreshLoadCraftingTable;
  6436. win.refreshLoadCraftingTable = win.refreshLoadMagicTable;
  6437. _processMagicTab();
  6438. win.refreshLoadCraftingTable = _refreshLoadCraftingTable;
  6439. if (win.magicPage3 == 1)
  6440. {
  6441. win.showMateriesNeededAndLevelLabelsMagic('enchantStargemPotion');
  6442. win.showMateriesNeededAndLevelLabelsMagic('beam');
  6443. win.showMateriesNeededAndLevelLabelsMagic('changeWeather');
  6444. }
  6445. };
  6446. }
  6447.  
  6448. function moveItemBox(itemKey, targetElId, color1, color2)
  6449. {
  6450. var itemBox = document.getElementById('item-box-' + itemKey);
  6451. var targetContainer = document.getElementById(targetElId);
  6452. targetContainer.appendChild(itemBox);
  6453. // remove event listeners before binding the tooltip to it
  6454. var $itemBox = win.$(itemBox);
  6455. $itemBox.off('mouseover').off('mouseleave');
  6456. itemBox.title = '';
  6457. // bind tooltip to item box
  6458. ensureTooltip('ingredient-secondary', itemBox);
  6459. // change color
  6460. itemBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  6461. $itemBox
  6462. .mouseover(function ()
  6463. {
  6464. itemBox.style.background = 'none';
  6465. itemBox.style.backgroundColor = color2;
  6466. })
  6467. .mouseleave(function ()
  6468. {
  6469. itemBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  6470. });
  6471. }
  6472. // move the strange leaf to brewing tab (thanks lasse_brus for this idea)
  6473. function moveStrangeLeafs()
  6474. {
  6475. moveItemBox('strangeLeaf', 'tab-sub-container-brewing', '#800080', '#990099');
  6476. moveItemBox('strangerLeaf', 'tab-sub-container-brewing', '#800080', '#990099');
  6477. }
  6478. // fix height of map item
  6479. function fixTreasureMap()
  6480. {
  6481. var mapBox = document.getElementById('item-box-treasureMap');
  6482. var numSpan = mapBox.lastElementChild;
  6483. numSpan.style.display = '';
  6484. numSpan.style.visibility = 'hidden';
  6485. }
  6486. // fix wobbling tree places on hover (in wood cutting)
  6487. function fixWoodcutting()
  6488. {
  6489. addStyle("\nimg.woodcutting-tree-img\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  6490. }
  6491. // fix wobbling quest rows on hover (in quest book)
  6492. function fixQuestBook()
  6493. {
  6494. addStyle("\n#table-quest-list tr\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  6495. }
  6496.  
  6497. function fixScrollImages()
  6498. {
  6499. function fixIcon(icon)
  6500. {
  6501. return icon + (icon != 'none' && !/\..{3,4}$/.test(icon) ? '.png' : '');
  6502. }
  6503. var _scrollTextHitSplat = win.scrollTextHitSplat;
  6504. win.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  6505. {
  6506. _scrollTextHitSplat(fixIcon(icon), color, text, elId, cbType);
  6507. };
  6508. var _scrollText = win.scrollText;
  6509. win.scrollText = function (icon, color, text)
  6510. {
  6511. _scrollText(fixIcon(icon), color, text);
  6512. };
  6513. }
  6514.  
  6515. function fixQuest8BraveryRecipe()
  6516. {
  6517. observer.add([
  6518. 'quest8'
  6519. , 'braveryPotion'
  6520. ], function ()
  6521. {
  6522. var show = win.quest8 > 0 && win.braveryPotion == 0;
  6523. var recipe = document.getElementById('brewing-braveryPotion');
  6524. if (recipe)
  6525. {
  6526. recipe.style.display = show ? '' : 'none';
  6527. }
  6528. });
  6529. }
  6530.  
  6531. function fixHitText()
  6532. {
  6533. win.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  6534. {
  6535. var imgTag = icon != 'none' ? "<img src=\"images/" + icon + "\" class=\"image-icon-50\" />" : '';
  6536. var elementChosen = document.getElementById(elId);
  6537. if (!elementChosen)
  6538. {
  6539. return;
  6540. }
  6541. var rect = elementChosen.getBoundingClientRect();
  6542. var xCoord = (rect.left + rect.right) / 2;
  6543. var yCoord = (rect.bottom + rect.top) / 2;
  6544. var extraStyle = '';
  6545. if (cbType == 'melee')
  6546. {
  6547. extraStyle = 'border: 1px solid red; background-color: #4d0000;';
  6548. }
  6549. else if (cbType == 'heal')
  6550. {
  6551. extraStyle = 'border: 1px solid green; background-color: lime;';
  6552. }
  6553. var $elementToAppend = win.$("<div class=\"scroller\" style=\"" + extraStyle + " color: " + color + "; position: fixed;\">" + imgTag + text + "</div>").appendTo('body');
  6554. if (xCoord == 0 && yCoord == 0)
  6555. {
  6556. var tab = document.getElementById('tab-container-bar-combat');
  6557. var tabRect = tab.getBoundingClientRect();
  6558. var boxRect = $elementToAppend.get(0).getBoundingClientRect();
  6559. xCoord = elId == 'img-hero' ? (tabRect.left - boxRect.width) : tabRect.right;
  6560. yCoord = tabRect.top;
  6561. }
  6562. $elementToAppend
  6563. .css(
  6564. {
  6565. left: xCoord
  6566. , top: yCoord
  6567. })
  6568. .animate(
  6569. {
  6570. top: '-=50px'
  6571. }, function ()
  6572. {
  6573. return $elementToAppend.fadeOut(1000, function ()
  6574. {
  6575. return $elementToAppend.remove();
  6576. });
  6577. });
  6578. };
  6579. }
  6580.  
  6581. function fixBoatTooltips()
  6582. {
  6583. var boatBox = document.getElementById('item-box-boundRowBoat');
  6584. var boatTooltip = boatBox && document.getElementById(boatBox.dataset.tooltipId || '');
  6585. var tooltipParent = boatTooltip && boatTooltip.parentElement;
  6586. if (!boatBox || !boatTooltip || !tooltipParent)
  6587. {
  6588. return;
  6589. }
  6590.  
  6591. function setTripDuration(durationEl, boatKey)
  6592. {
  6593. var durationStr = TRIP_DURATION.hasOwnProperty(boatKey) ? TRIP_DURATION[boatKey].toString(10) : '?';
  6594. durationEl.innerHTML = "<strong>Trip duration:</strong> " + durationStr + " hours";
  6595. }
  6596. boatTooltip.id = boatBox.dataset.tooltipId = 'tooltip-boundRowBoat';
  6597. boatTooltip.appendChild(document.createElement('br'));
  6598. var boatDuration = document.createElement('span');
  6599. boatDuration.className = 'trip-duration';
  6600. setTripDuration(boatDuration, 'rowBoat');
  6601. boatTooltip.appendChild(boatDuration);
  6602. for (var _i = 0, BOAT_LIST_1 = BOAT_LIST; _i < BOAT_LIST_1.length; _i++)
  6603. {
  6604. var boatKey = BOAT_LIST_1[_i];
  6605. var boundKey = getBoundKey(boatKey);
  6606. var itemBox = document.getElementById('item-box-' + boundKey);
  6607. if (!itemBox)
  6608. {
  6609. continue;
  6610. }
  6611. var tooltip = document.getElementById('tooltip-' + boundKey);
  6612. if (!tooltip)
  6613. {
  6614. tooltip = boatTooltip.cloneNode(true);
  6615. tooltip.id = 'tooltip-' + boundKey;
  6616. var header = tooltip.firstElementChild;
  6617. header.textContent = capitalize(split2Words(boatKey));
  6618. tooltipParent.appendChild(tooltip);
  6619. itemBox.dataset.tooltipId = 'tooltip-' + boundKey;
  6620. }
  6621. var durationEl = tooltip.getElementsByClassName('trip-duration').item(0);
  6622. if (durationEl)
  6623. {
  6624. setTripDuration(durationEl, boatKey);
  6625. }
  6626. }
  6627. }
  6628.  
  6629. function fixAlignments()
  6630. {
  6631. addStyle("\nspan.item-box[id^=\"item-box-\"] > img:not(.image-icon-100),\nspan.item-box[id^=\"item-box-\"] > span > img\n{\n\tmargin-top: -2px;\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}\nimg.farming-patch-img\n{\n\twidth: 349px;\n\theight: 400px;\n}\n/* fix position of some plant images */\nimg.farming-patch-img[src$=\"/3_1.png\"]\n{\n\theight: 398px;\n\tmargin-top: -2px;\n\tmargin-bottom: 4px;\n}\nimg.farming-patch-img[src$=\"/3_2.png\"]\n{\n\theight: 399px;\n\tmargin-top: -1px;\n\tmargin-bottom: 2px;\n\tmargin-left: 2px;\n\tmargin-right: -2px;\n}\nimg.farming-patch-img[src$=\"/3_4.png\"]\n{\n\tmargin-top: 1px;\n\tmargin-bottom: -1px;\n\tmargin-left: -2px;\n\tmargin-right: 2px;\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 div.fight-spellbook\n{\n\tmargin: 0 -3px;\n}\n#hero-area span.fight-spell\n{\n\tmargin-bottom: 0;\n\tmargin-top: 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.large-button\n{\n\tfont-size: 3rem;\n}\n#combat-table-area span.medium-button + br + br\n{\n\tdisplay: none;\n}\n\t\t");
  6632. }
  6633.  
  6634. function addHeroStatTooltips()
  6635. {
  6636. var table = document.querySelector('#hero-area table.table-hero-stats');
  6637. if (!table)
  6638. {
  6639. return;
  6640. }
  6641. var statRow = table.rows.item(0);
  6642. var attackCell = statRow.cells.item(0);
  6643. attackCell.title = 'Attack Damage';
  6644. win.$(attackCell).tooltip();
  6645. var accuracyCell = statRow.cells.item(1);
  6646. accuracyCell.title = 'Attack Accuracy';
  6647. win.$(accuracyCell).tooltip();
  6648. var speedCell = statRow.cells.item(2);
  6649. speedCell.title = 'Attack Speed';
  6650. win.$(speedCell).tooltip();
  6651. var defenseCell = statRow.cells.item(3);
  6652. defenseCell.title = 'Defense';
  6653. win.$(defenseCell).tooltip();
  6654. }
  6655.  
  6656. function unifyTooltips()
  6657. {
  6658. function getLastNonEmptyChild(parent)
  6659. {
  6660. for (var i = parent.childNodes.length - 1; i >= 0; i--)
  6661. {
  6662. var child = parent.childNodes.item(i);
  6663. if (child.nodeType === Node.TEXT_NODE
  6664. && (child.textContent || '').trim() !== '')
  6665. {
  6666. return null;
  6667. }
  6668. else if (child.nodeType === Node.ELEMENT_NODE)
  6669. {
  6670. return child;
  6671. }
  6672. }
  6673. return null;
  6674. }
  6675. // clean unnecessary br-tags in tooltips
  6676. var tooltips = document.querySelectorAll('#tooltip-list > div[id^="tooltip-"]');
  6677. for (var i = 0; i < tooltips.length; i++)
  6678. {
  6679. var tooltip = tooltips[i];
  6680. var lneChild = void 0;
  6681. while ((lneChild = getLastNonEmptyChild(tooltip)) && lneChild.tagName == 'BR')
  6682. {
  6683. tooltip.removeChild(lneChild);
  6684. }
  6685. }
  6686.  
  6687. function getTooltip(item)
  6688. {
  6689. return document.getElementById('tooltip-' + item);
  6690. }
  6691. var boldify = [
  6692. 'oilBarrel'
  6693. , 'boundEmptyPickaxe'
  6694. , 'boundEmptyShovel'
  6695. , 'boundRocket'
  6696. , 'ashes'
  6697. , 'iceBones'
  6698. ];
  6699. var lastDotRegex = /\.\s*$/;
  6700. for (var _i = 0, boldify_1 = boldify; _i < boldify_1.length; _i++)
  6701. {
  6702. var item = boldify_1[_i];
  6703. var tooltip = getTooltip(item);
  6704. if (!tooltip)
  6705. {
  6706. continue;
  6707. }
  6708. var textNode = tooltip.lastChild;
  6709. while (textNode && (textNode.nodeType != Node.TEXT_NODE || (textNode.textContent || '').trim() === ''))
  6710. {
  6711. if (textNode.nodeName === 'SPAN')
  6712. {
  6713. textNode = textNode.lastChild;
  6714. }
  6715. else
  6716. {
  6717. textNode = textNode.previousSibling;
  6718. }
  6719. }
  6720. if (!textNode)
  6721. {
  6722. continue;
  6723. }
  6724. var text = textNode.textContent || '';
  6725. var split = text.split(/\.(?=\s*\S+)/);
  6726. var clickText = split[split.length - 1];
  6727. textNode.textContent = text.replace(clickText, '');
  6728. if (split.length > 1)
  6729. {
  6730. tooltip.appendChild(document.createElement('br'));
  6731. tooltip.appendChild(document.createElement('br'));
  6732. }
  6733. var boldText = document.createElement('b');
  6734. boldText.textContent = clickText;
  6735. tooltip.appendChild(boldText);
  6736. }
  6737.  
  6738. function prepareTooltip(item, editText, createOnMissing)
  6739. {
  6740. if (createOnMissing === void 0)
  6741. {
  6742. createOnMissing = false;
  6743. }
  6744. var tooltip = getTooltip(item);
  6745. if (!tooltip)
  6746. {
  6747. return;
  6748. }
  6749. // try to find the b-node:
  6750. var bNode = getLastNonEmptyChild(tooltip);
  6751. if (bNode && bNode.tagName === 'SPAN')
  6752. {
  6753. bNode = getLastNonEmptyChild(bNode);
  6754. }
  6755. if (!bNode || bNode.tagName !== 'B')
  6756. {
  6757. if (!createOnMissing)
  6758. {
  6759. bNode = null;
  6760. }
  6761. else
  6762. {
  6763. tooltip.appendChild(document.createElement('br'));
  6764. tooltip.appendChild(document.createElement('br'));
  6765. bNode = document.createElement('b');
  6766. tooltip.appendChild(bNode);
  6767. }
  6768. }
  6769. if (bNode)
  6770. {
  6771. bNode.textContent = editText(bNode);
  6772. }
  6773. }
  6774. // remove dots
  6775. for (var i = 0; i < tooltips.length; i++)
  6776. {
  6777. var item = tooltips.item(i).id.replace(/^tooltip-/, '');
  6778. prepareTooltip(item, function (bNode)
  6779. {
  6780. var text = bNode.textContent || '';
  6781. if (/Click to /.test(text))
  6782. {
  6783. return text.replace(lastDotRegex, '');
  6784. }
  6785. return text;
  6786. });
  6787. }
  6788. // add click texts
  6789. function setText(item, text)
  6790. {
  6791. prepareTooltip(item, function ()
  6792. {
  6793. return text;
  6794. }, true);
  6795. }
  6796. for (var _a = 0, FURNACE_LEVELS_1 = FURNACE_LEVELS; _a < FURNACE_LEVELS_1.length; _a++)
  6797. {
  6798. var furnaceLevel = FURNACE_LEVELS_1[_a];
  6799. var furnaceItem = getBoundKey(furnaceLevel + 'Furnace');
  6800. setText(furnaceItem, 'Click to operate');
  6801. var ovenItem = getBoundKey(furnaceLevel + 'Oven');
  6802. setText(ovenItem, 'Click to operate');
  6803. }
  6804. // fix tooltip of quests-book
  6805. var questBookTooltip = getTooltip('quests-book');
  6806. if (questBookTooltip)
  6807. {
  6808. var childNodes = questBookTooltip.childNodes;
  6809. for (var i = 0; i < childNodes.length; i++)
  6810. {
  6811. var node = childNodes[i];
  6812. if (node.nodeType === Node.TEXT_NODE
  6813. && (node.textContent || '').indexOf('Click to see a list of quests.') > -1)
  6814. {
  6815. var next = node.nextSibling;
  6816. if (next)
  6817. {
  6818. questBookTooltip.removeChild(next);
  6819. }
  6820. questBookTooltip.removeChild(node);
  6821. }
  6822. }
  6823. }
  6824. // fix tooltip of axe
  6825. var axeTooltip = getTooltip('boundEmptyAxe');
  6826. if (axeTooltip)
  6827. {
  6828. axeTooltip.insertBefore(document.createElement('br'), axeTooltip.lastElementChild);
  6829. }
  6830. var texts = {
  6831. 'quests-book': 'Click to see the list of quests'
  6832. , 'achievementBook': 'Click to see the list of achievements'
  6833. , 'boundEmptyChisel': 'Click to use'
  6834. , 'rake': 'Click to upgrade your rake'
  6835. , 'boundBoat': 'Click to send boat'
  6836. };
  6837. for (var item in texts)
  6838. {
  6839. setText(item, texts[item]);
  6840. }
  6841. for (var _b = 0, BOAT_LIST_2 = BOAT_LIST; _b < BOAT_LIST_2.length; _b++)
  6842. {
  6843. var boatKey = BOAT_LIST_2[_b];
  6844. setText(getBoundKey(boatKey), 'Click to send boat');
  6845. }
  6846. }
  6847. var cached = {
  6848. scrollWidth: 0
  6849. , scrollHeight: 0
  6850. };
  6851.  
  6852. function changeTooltipPosition(event)
  6853. {
  6854. var tooltipX = event.pageX - 8;
  6855. var tooltipY = event.pageY + 8;
  6856. var el = document.querySelector('body > div.tooltip');
  6857. if (!el)
  6858. {
  6859. return;
  6860. }
  6861. if (!this)
  6862. {
  6863. // init
  6864. cached.scrollWidth = document.body.scrollWidth;
  6865. cached.scrollHeight = document.body.scrollHeight;
  6866. }
  6867. var rect = el.getBoundingClientRect();
  6868. var css = {
  6869. left: tooltipX
  6870. , top: tooltipY
  6871. , width: ''
  6872. , height: ''
  6873. , maxWidth: cached.scrollWidth
  6874. , maxHeight: cached.scrollHeight
  6875. };
  6876. var diffX = cached.scrollWidth - 20 - tooltipX - rect.width;
  6877. if (diffX < 0)
  6878. {
  6879. css.left += diffX;
  6880. css.width = rect.width - 42;
  6881. }
  6882. var diffY = cached.scrollHeight - 20 - tooltipY - rect.height;
  6883. if (diffY < 0)
  6884. {
  6885. css.top += diffY;
  6886. css.height = rect.height - 22;
  6887. }
  6888. win.$(el).css(css);
  6889. }
  6890.  
  6891. function fixTooltipPositioning()
  6892. {
  6893. win.changeTooltipPosition = changeTooltipPosition;
  6894. win.loadTooltips();
  6895. }
  6896.  
  6897. function fixCombatNavigation()
  6898. {
  6899. var backBtns = document.querySelectorAll('span.medium-button[onclick*="openTab(\'combat\')"]');
  6900. for (var i = 0; i < backBtns.length; i++)
  6901. {
  6902. var btn = backBtns.item(i);
  6903. var img = btn.firstElementChild;
  6904. var textNode = btn.lastChild;
  6905. if (!img || img.tagName != 'IMG' || !textNode)
  6906. {
  6907. continue;
  6908. }
  6909. img.className = img.className.replace(/(-\d+)-b/, '$1');
  6910. textNode.textContent = ' back';
  6911. }
  6912. }
  6913.  
  6914. function fixPromethiumSmeltingTime()
  6915. {
  6916. var _getTimerPerBar = win.getTimerPerBar;
  6917. win.getTimerPerBar = function (bar)
  6918. {
  6919. if (bar == 'promethiumBar')
  6920. {
  6921. return 80;
  6922. }
  6923. return _getTimerPerBar(bar);
  6924. };
  6925. }
  6926.  
  6927. function fixImage()
  6928. {
  6929. var oxygenEl = document.querySelector('img[src="images/oxygenPotion"]');
  6930. if (oxygenEl)
  6931. {
  6932. oxygenEl.src += '.png';
  6933. }
  6934. }
  6935.  
  6936. function init()
  6937. {
  6938. fixClientGameLoop();
  6939. fixScroller();
  6940. fixTooltipStyle();
  6941. fixRefreshingMagicRecipes();
  6942. moveStrangeLeafs();
  6943. fixTreasureMap();
  6944. fixWoodcutting();
  6945. fixQuestBook();
  6946. // apply fix for scroll images later to fix images in this code too
  6947. fixHitText();
  6948. fixScrollImages();
  6949. fixQuest8BraveryRecipe();
  6950. fixBoatTooltips();
  6951. fixAlignments();
  6952. addHeroStatTooltips();
  6953. unifyTooltips();
  6954. fixTooltipPositioning();
  6955. fixCombatNavigation();
  6956. fixPromethiumSmeltingTime();
  6957. fixImage();
  6958. }
  6959. temporaryFixes.init = init;
  6960. })(temporaryFixes || (temporaryFixes = {}));
  6961.  
  6962. /**
  6963. * improve timer
  6964. */
  6965. var timer;
  6966. (function (timer)
  6967. {
  6968. timer.name = 'timer';
  6969. var IMPROVED_CLASS = 'improved';
  6970. var NOTIFICATION_AREA_ID = 'notifaction-area';
  6971. var PERCENT_CLASS = 'percent';
  6972. var REMAINING_CLASS = 'remaining';
  6973. var TIMER_CLASS = 'timer';
  6974.  
  6975. function bindNewFormatter()
  6976. {
  6977. function doBind()
  6978. {
  6979. win.formatTime = win.formatTimeShort = win.formatTimeShort2 = function (seconds)
  6980. {
  6981. return format.timer(seconds);
  6982. };
  6983. }
  6984. win.addEventListener('load', function ()
  6985. {
  6986. return setTimeout(function ()
  6987. {
  6988. return doBind();
  6989. }, 100);
  6990. });
  6991. doBind();
  6992. setTimeout(function ()
  6993. {
  6994. return doBind();
  6995. }, 100);
  6996. }
  6997.  
  6998. function applyStyle()
  6999. {
  7000. addStyle("\nspan.notif-box." + IMPROVED_CLASS + "\n{\n\tposition: relative;\n}\nspan.notif-box." + IMPROVED_CLASS + " > span:not(." + TIMER_CLASS + "):not(." + REMAINING_CLASS + "):not(." + PERCENT_CLASS + ")\n{\n\tdisplay: none;\n}\nspan.notif-box." + IMPROVED_CLASS + " > span." + REMAINING_CLASS + ",\nspan.notif-box." + IMPROVED_CLASS + " > span." + PERCENT_CLASS + "\n{\n\tposition: absolute;\n\tleft: 10px;\n\tfont-size: 0.9rem;\n\tbottom: 0px;\n\twidth: 50px;\n\ttext-align: right;\n\ttext-shadow: 1px 1px 4px black;\n}\nspan.notif-box." + IMPROVED_CLASS + " > span." + REMAINING_CLASS + "::before\n{\n\tcontent: '\\0D7';\n\tmargin-right: .25rem;\n\tmargin-left: -.5rem;\n}\nspan.notif-box." + IMPROVED_CLASS + " > span." + PERCENT_CLASS + "::after\n{\n\tcontent: '%';\n}\n\t\t");
  7001. }
  7002.  
  7003. function improveSmeltingTimer()
  7004. {
  7005. var el = document.getElementById('notif-smelting');
  7006. if (!el)
  7007. {
  7008. return;
  7009. }
  7010. var smeltingNotifBox = el;
  7011. smeltingNotifBox.classList.add(IMPROVED_CLASS);
  7012. var smeltingTimerEl = document.createElement('span');
  7013. smeltingTimerEl.className = TIMER_CLASS;
  7014. smeltingNotifBox.appendChild(smeltingTimerEl);
  7015. var remainingBarsEl = document.createElement('span');
  7016. remainingBarsEl.className = REMAINING_CLASS;
  7017. smeltingNotifBox.appendChild(remainingBarsEl);
  7018. var delta = 0;
  7019.  
  7020. function updatePercValues(init)
  7021. {
  7022. if (init === void 0)
  7023. {
  7024. init = false;
  7025. }
  7026. updateSmeltingTimer(delta = 0);
  7027. if (init)
  7028. {
  7029. observer.add('smeltingPercD', function ()
  7030. {
  7031. return updatePercValues();
  7032. });
  7033. observer.add('smeltingPerc', function ()
  7034. {
  7035. return updatePercValues();
  7036. });
  7037. }
  7038. }
  7039.  
  7040. function updateSmeltingTimer(delta)
  7041. {
  7042. if (delta === void 0)
  7043. {
  7044. delta = 0;
  7045. }
  7046. var totalTime = win.smeltingPercD;
  7047. // thanks at /u/marcus898 for your bug report
  7048. var elapsedTime = Math.round(win.smeltingPerc * totalTime / 100) + delta;
  7049. smeltingTimerEl.textContent = format.timer(Math.max(totalTime - elapsedTime, 0));
  7050. remainingBarsEl.textContent = (win.smeltingTotalAmount - win.smeltingAmount).toString();
  7051. }
  7052. observer.addTick(function ()
  7053. {
  7054. return updateSmeltingTimer(delta++);
  7055. });
  7056. updatePercValues(true);
  7057. }
  7058.  
  7059. function improveTimer(cssRulePrefix, textColor, timerColor, infoIdPrefx, containerPrefix, updateFn)
  7060. {
  7061. 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");
  7062. let iteration = cssRulePrefix == '.woodcutting-tree' ? 6 : 7;
  7063. for (var i = 0; i < iteration; i++)
  7064. {
  7065. var num = i + 1;
  7066. var infoId = infoIdPrefx + num;
  7067. var container = document.getElementById(containerPrefix + num);
  7068. container.style.position = 'relative';
  7069. var infoEl = document.createElement('div');
  7070. infoEl.className = 'info';
  7071. infoEl.id = infoId;
  7072. infoEl.innerHTML = "<div class=\"name\"></div><div class=\"timer\"></div>";
  7073. container.appendChild(infoEl);
  7074. updateFn(num, infoId, true);
  7075. }
  7076. }
  7077.  
  7078. function updateTreeInfo(placeId, infoElId, init)
  7079. {
  7080. if (init === void 0)
  7081. {
  7082. init = false;
  7083. }
  7084. var infoEl = document.getElementById(infoElId);
  7085. var nameEl = infoEl.firstElementChild;
  7086. var timerEl = infoEl.lastElementChild;
  7087. var idKey = 'treeId' + placeId;
  7088. var growTimerKey = 'treeGrowTimer' + placeId;
  7089. var lockedKey = 'treeUnlocked' + placeId;
  7090. var treeId = getGameValue(idKey);
  7091. if (treeId == 0)
  7092. {
  7093. var isLocked = (placeId == 5 || placeId == 6) && win.donorWoodcuttingPatch < win.currentTimeMillis;
  7094. nameEl.textContent = isLocked ? 'Locked' : 'Empty';
  7095. timerEl.textContent = '';
  7096. }
  7097. else
  7098. {
  7099. nameEl.textContent = key2Name(win.getTreeName(treeId)) || 'Unknown Tree';
  7100. var remainingTime = win.TREE_GROW_TIME[treeId - 1] - getGameValue(growTimerKey);
  7101. timerEl.textContent = remainingTime > 0 ? '(' + format.timer(remainingTime) + ')' : 'Fully grown';
  7102. }
  7103. if (init)
  7104. {
  7105. observer.add([idKey, growTimerKey, lockedKey], function ()
  7106. {
  7107. return updateTreeInfo(placeId, infoElId, false);
  7108. });
  7109. }
  7110. }
  7111. // add tree grow timer
  7112. function improveTreeGrowTimer()
  7113. {
  7114. improveTimer('.woodcutting-tree', 'white', 'yellow', 'wc-tree-info-', 'wc-div-tree-', updateTreeInfo);
  7115. }
  7116.  
  7117. function updatePatchInfo(patchId, infoElId, init)
  7118. {
  7119. if (init === void 0)
  7120. {
  7121. init = false;
  7122. }
  7123. var infoEl = document.getElementById(infoElId);
  7124. var nameEl = infoEl.querySelector('.name');
  7125. var timerEl = infoEl.querySelector('.timer');
  7126. var idKey = 'farmingPatchSeed' + patchId;
  7127. var growTimeKey = 'farmingPatchGrowTime' + patchId;
  7128. var timerKey = 'farmingPatchTimer' + patchId;
  7129. var stageKey = 'farmingPatchStage' + patchId;
  7130. var stage = getGameValue(stageKey);
  7131. var seedName = PLANT_NAME[getGameValue(idKey)] || 'Unkown Plant';
  7132. if (stage == 0)
  7133. {
  7134. var isLocked = (patchId == 5 || patchId == 6) && win.donorFarmingPatch < win.currentTimeMillis;
  7135. nameEl.textContent = isLocked ? 'Locked' : 'Click to grow';
  7136. timerEl.textContent = '';
  7137. }
  7138. else if (stage >= 4)
  7139. {
  7140. nameEl.textContent = stage > 4 ? 'Dead Plant' : seedName;
  7141. timerEl.textContent = stage > 4 ? 'Click to remove' : 'Click to harvest';
  7142. }
  7143. else
  7144. {
  7145. nameEl.textContent = seedName;
  7146. var remainingTime = getGameValue(growTimeKey) - getGameValue(timerKey);
  7147. timerEl.textContent = '(' + format.timer(remainingTime) + ')';
  7148. }
  7149. if (init)
  7150. {
  7151. observer.add([idKey, timerKey, stageKey, 'donorFarmingPatch'], function ()
  7152. {
  7153. return updatePatchInfo(patchId, infoElId, false);
  7154. });
  7155. }
  7156. }
  7157. // add seed name and change color of timer
  7158. function getSoonestTreeTimer()
  7159. {
  7160. if (win.treeStage1 == 4
  7161. || win.treeStage2 == 4
  7162. || win.treeStage3 == 4
  7163. || win.treeStage4 == 4
  7164. || win.treeStage5 == 4
  7165. || win.treeStage6 == 4)
  7166. {
  7167. return -1;
  7168. }
  7169. var minTimer = null;
  7170. for (var i = 1; i <= 6; i++)
  7171. {
  7172. var treeId = getGameValue('treeId' + i);
  7173. var unlocked = getGameValue('treeUnlocked' + i) == 1;
  7174. var timerValue = getGameValue('treeGrowTimer' + i);
  7175. if (unlocked && treeId !== 0 && timerValue > 0)
  7176. {
  7177. var remainingTime = win.TREE_GROW_TIME[treeId - 1] - timerValue;
  7178. minTimer = minTimer === null ? remainingTime : Math.min(minTimer, remainingTime);
  7179. }
  7180. }
  7181. return minTimer || 0;
  7182. }
  7183.  
  7184. function getSoonestFarmingTimer()
  7185. {
  7186. if (win.farmingPatchStage1 == 0 || win.farmingPatchStage1 == 4
  7187. || win.farmingPatchStage2 == 0 || win.farmingPatchStage2 == 4
  7188. || win.farmingPatchStage3 == 0 || win.farmingPatchStage3 == 4
  7189. || win.farmingPatchStage4 == 0 || win.farmingPatchStage4 == 4
  7190. || win.donorFarmingPatch > win.currentTimeMillis && (win.farmingPatchStage5 == 0 || win.farmingPatchStage5 == 4
  7191. || win.farmingPatchStage6 == 0 || win.farmingPatchStage6 == 4))
  7192. {
  7193. return -1;
  7194. }
  7195. var minTimer = null;
  7196. for (var i = 1; i <= ((win.donorFarmingPatch > win.currentTimeMillis) ? 6 : 4); i++)
  7197. {
  7198. var remainingTimer = getGameValue('farmingPatchGrowTime' + i) - getGameValue('farmingPatchTimer' + i);
  7199. minTimer = minTimer === null ? remainingTimer : Math.min(minTimer, remainingTimer);
  7200. }
  7201. return minTimer || 0;
  7202. }
  7203.  
  7204. function improveSeedGrowTimer()
  7205. {
  7206. improveTimer('div[id^="farming-patch-area"]', 'black', 'blue', 'farming-patch-info-', 'farming-patch-area-', updatePatchInfo);
  7207. }
  7208.  
  7209. function addTabTimer()
  7210. {
  7211. var TAB_TIMER_KEY = 'tabTimer';
  7212. addStyle("\ntable.tab-bar td\n{\n\tposition: relative;\n}\n." + TAB_TIMER_KEY + " table.tab-bar td.ready > img:first-child\n{\n\tbackground-image: linear-gradient(#161618, #48ab32);\n\tmargin: -2px -5px -3px;\n\tpadding: 6px 5px 7px;\n}\ntable.tab-bar td .info\n{\n\tcolor: yellow;\n\tdisplay: none;\n\tfont-size: 0.8rem;\n\tpadding-left: 50px;\n\tposition: absolute;\n\tleft: 0;\n\tright: 0;\n\ttext-align: center;\n}\n." + TAB_TIMER_KEY + " table.tab-bar td .info\n{\n\tdisplay: block;\n}\ntable.tab-bar td .info.timer\n{\n\tbottom: 0;\n\tpadding-bottom: 5px;\n}\ntable.tab-bar td .info.timer:not(:empty)::before\n{\n\tcontent: '(';\n}\ntable.tab-bar td .info.timer:not(:empty)::after\n{\n\tcontent: ')';\n}\nbody.short-tabs table.tab-bar td .info.timer\n{\n\tdisplay: none;\n}\ntable.tab-bar td .info.extra\n{\n\tcolor: white;\n\tpadding-top: 5px;\n\ttop: 0;\n}\nbody.short-tabs table.tab-bar td .info.extra\n{\n\tdisplay: none;\n}\n." + TAB_TIMER_KEY + " #dhqol-notif-woodcutting,\n." + TAB_TIMER_KEY + " #dhqol-notif-farming,\n." + TAB_TIMER_KEY + " #dhqol-notif-combat,\n." + TAB_TIMER_KEY + " #dhqol-notif-vial\n{\n\tdisplay: none !important;\n}\n\t\t");
  7213.  
  7214. function getTabEl(key)
  7215. {
  7216. return document.getElementById('tab-container-bar-' + key);
  7217. }
  7218.  
  7219. function addInfoDiv(key)
  7220. {
  7221. var infoDiv = document.createElement('div');
  7222. infoDiv.className = 'info';
  7223. var tab = getTabEl(key);
  7224. if (tab)
  7225. {
  7226. tab.appendChild(infoDiv);
  7227. }
  7228. return infoDiv;
  7229. }
  7230.  
  7231. function createTabTimer(key, timerFn)
  7232. {
  7233. var tab = getTabEl(key);
  7234. var timerDiv = addInfoDiv(key);
  7235. if (!tab || !timerDiv)
  7236. {
  7237. return;
  7238. }
  7239. timerDiv.classList.add('timer');
  7240.  
  7241. function updateTimer()
  7242. {
  7243. var minTimer = timerFn();
  7244. if (tab)
  7245. {
  7246. tab.classList[minTimer == -1 ? 'add' : 'remove']('ready');
  7247. }
  7248. timerDiv.textContent = minTimer <= 0 ? '' : format.timer(minTimer);
  7249. }
  7250. updateTimer();
  7251. observer.addTick(function ()
  7252. {
  7253. return updateTimer();
  7254. });
  7255. }
  7256. createTabTimer('woodcutting', getSoonestTreeTimer);
  7257. createTabTimer('farming', getSoonestFarmingTimer);
  7258. createTabTimer('combat', function ()
  7259. {
  7260. return win.combatGlobalCooldown;
  7261. });
  7262. var energyDiv = addInfoDiv('combat');
  7263. energyDiv.classList.add('extra');
  7264.  
  7265. function updateEnergy()
  7266. {
  7267. energyDiv.innerHTML = '<img src="images/steak.png" class="image-icon-15"> ' + format.number(win.energy);
  7268. }
  7269. updateEnergy();
  7270. observer.add('energy', function ()
  7271. {
  7272. return updateEnergy();
  7273. });
  7274. // add highlight for stardust potions
  7275. var potionDiv = addInfoDiv('brewing');
  7276. potionDiv.classList.add('extra');
  7277. var potionList = ['stardustPotion', 'superStardustPotion'];
  7278. var potionImageList = [];
  7279.  
  7280. function updatePotion(key, img, init)
  7281. {
  7282. if (init === void 0)
  7283. {
  7284. init = false;
  7285. }
  7286. var timerKey = key + 'Timer';
  7287. var show = getGameValue(key) > 0 && getGameValue(timerKey) === 0;
  7288. img.style.display = show ? '' : 'none';
  7289. if (init)
  7290. {
  7291. observer.add(key, function ()
  7292. {
  7293. return updatePotion(key, img);
  7294. });
  7295. observer.add(timerKey, function ()
  7296. {
  7297. return updatePotion(key, img);
  7298. });
  7299. }
  7300. }
  7301. for (var i = 0; i < potionList.length; i++)
  7302. {
  7303. var key = potionList[i];
  7304. var img = document.createElement('img');
  7305. img.src = 'images/' + key + '.png';
  7306. img.className = 'image-icon-15';
  7307. potionImageList[i] = img;
  7308. potionDiv.appendChild(img);
  7309. updatePotion(key, img, true);
  7310. }
  7311.  
  7312. function updateVisibility()
  7313. {
  7314. document.body.classList[settings.get(settings.KEY.showTabTimer) ? 'add' : 'remove'](TAB_TIMER_KEY);
  7315. }
  7316. updateVisibility();
  7317. settings.observe(settings.KEY.showTabTimer, function ()
  7318. {
  7319. return updateVisibility();
  7320. });
  7321. observer.add('profileShortTabs', function ()
  7322. {
  7323. var short = !!win.profileShortTabs;
  7324. document.body.classList[short ? 'add' : 'remove']('short-tabs');
  7325. });
  7326. }
  7327.  
  7328. function addOilInfo()
  7329. {
  7330. var NULL_TYPE = 'null';
  7331. var PLUS_TYPE = 'plus';
  7332. var MINUS_TYPE = 'minus';
  7333. addStyle("\n#oil-filling-level\n{\n\tbackground-color: black;\n\tborder: 1px solid white;\n\tdisplay: inline-block;\n\tposition: absolute;\n\tbottom: 0;\n\ttop: 0;\n\ttransform: translateX(-10px);\n\twidth: 8px;\n}\n#oil-filling-level > div\n{\n\tbackground-color: white;\n\twidth: 100%;\n}\n\ntable.top-bar span[id^=\"dh2qol-oil\"]\n{\n\tdisplay: none;\n}\n#oil-flow-net\n{\n\tcolor: hsla(195, 100%, 50%, 1);\n\tfont-weight: bold;\n}\n#oil-flow-net[data-type=\"" + NULL_TYPE + "\"]\n{\n\tcolor: hsla(195, 100%, 50%, 1);\n}\n#oil-flow-net[data-type=\"" + PLUS_TYPE + "\"]\n{\n\tcolor: green;\n}\n#oil-flow-net[data-type=\"" + MINUS_TYPE + "\"]\n{\n\tcolor: red;\n}\n#oil-flow-net-timer[data-type=\"" + NULL_TYPE + "\"]\n{\n\tdisplay: none;\n}\n#oil-flow-net-timer[data-type=\"" + PLUS_TYPE + "\"]\n{\n\tcolor: yellow;\n}\n#oil-flow-net-timer[data-type=\"" + MINUS_TYPE + "\"]\n{\n\tcolor: orange;\n}\n\t\t");
  7334. var oilFlow = document.getElementById('oil-flow-values');
  7335. var parent = oilFlow && oilFlow.parentElement;
  7336. if (!oilFlow || !parent)
  7337. {
  7338. return;
  7339. }
  7340. var container = document.createElement('div');
  7341. container.id = 'oil-filling-level';
  7342. var fillingLevel = document.createElement('div');
  7343. container.appendChild(fillingLevel);
  7344. var first = parent.firstElementChild;
  7345. if (first)
  7346. {
  7347. parent.insertBefore(container, first);
  7348. }
  7349. else
  7350. {
  7351. parent.appendChild(container);
  7352. }
  7353. parent.style.position = 'relative';
  7354. var netFlow = document.createElement('span');
  7355. netFlow.id = 'oil-flow-net';
  7356. parent.insertBefore(netFlow, oilFlow);
  7357. var next = oilFlow.nextElementSibling;
  7358. var netTimer = document.createElement('span');
  7359. netTimer.id = 'oil-flow-net-timer';
  7360. if (next)
  7361. {
  7362. parent.insertBefore(netTimer, next);
  7363. }
  7364. else
  7365. {
  7366. parent.appendChild(netTimer);
  7367. }
  7368. var oilNet;
  7369. var oilNetType;
  7370.  
  7371. function updateNetFlow(init)
  7372. {
  7373. if (init === void 0)
  7374. {
  7375. init = false;
  7376. }
  7377. oilNet = win.oilIn - win.oilOut;
  7378. oilNetType = oilNet === 0 ? NULL_TYPE : (oilNet > 0 ? PLUS_TYPE : MINUS_TYPE);
  7379. netFlow.dataset.type = oilNetType;
  7380. var sign = oilNet === 0 ? PLUS_MINUS_SIGN : (oilNet > 0 ? '+' : '');
  7381. netFlow.textContent = sign + oilNet;
  7382. if (init)
  7383. {
  7384. observer.add('oilIn', function ()
  7385. {
  7386. return updateNetFlow();
  7387. });
  7388. observer.add('oilOut', function ()
  7389. {
  7390. return updateNetFlow();
  7391. });
  7392. }
  7393. updateFullTimer(init);
  7394. }
  7395. var hour2Color = (_a = {}
  7396. , // 30min
  7397. _a[.5 * 60 * 60] = 'rgb(255, 0, 0)'
  7398. , _a[5 * 60 * 60] = 'rgb(255, 255, 0)'
  7399. , _a[8 * 60 * 60] = 'rgb(255, 255, 255)'
  7400. , _a);
  7401.  
  7402. function updateFullTimer(init)
  7403. {
  7404. if (init === void 0)
  7405. {
  7406. init = false;
  7407. }
  7408. netTimer.dataset.type = oilNetType;
  7409. var time = 0;
  7410. if (oilNet > 0)
  7411. {
  7412. netTimer.title = 'full in...';
  7413. var diff = win.maxOil - win.oil;
  7414. time = diff / oilNet;
  7415. }
  7416. else if (oilNet < 0)
  7417. {
  7418. netTimer.title = 'empty in...';
  7419. time = win.oil / Math.abs(oilNet);
  7420. }
  7421. netTimer.textContent = '(' + format.timer(Math.ceil(time)) + ')';
  7422. var filledPercent = win.oil / win.maxOil * 100;
  7423. fillingLevel.style.height = (100 - filledPercent) + '%';
  7424. /**
  7425. * colorize filling level according to the time it needs to be full/empty:
  7426. * - red iff oil storage full/empty in 30min
  7427. * - yellow iff oil storage full/empty in 5h
  7428. * - white iff oil storage full/empty in 8h or more
  7429. */
  7430. var color = oilNet === 0 ? '#ffffff' : colorGenerator.getColorTransition(time, hour2Color);
  7431. container.style.borderColor = color;
  7432. if (init)
  7433. {
  7434. observer.add('maxOil', function ()
  7435. {
  7436. return updateFullTimer();
  7437. });
  7438. observer.add('oil', function ()
  7439. {
  7440. return updateFullTimer();
  7441. });
  7442. observer.addTick(function ()
  7443. {
  7444. return updateFullTimer();
  7445. });
  7446. }
  7447. }
  7448. updateNetFlow(true);
  7449. var _a;
  7450. }
  7451.  
  7452. function addRocketTimer()
  7453. {
  7454. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7455. if (!notifArea)
  7456. {
  7457. return;
  7458. }
  7459. var notifBox = document.createElement('span');
  7460. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7461. notifBox.id = 'notif-rocket';
  7462. notifBox.style.display = 'none';
  7463. notifBox.innerHTML = "<img src=\"images/rocket.png\" class=\"image-icon-50\" id=\"notif-rocket-img\" style=\"margin-right: 10px;\"><span class=\"timer\" data-item-display=\"rocketTimer\"></span><span class=\"" + PERCENT_CLASS + "\" data-item-display=\"rocketPercent\"></span>";
  7464. var AVG_KM_PER_SEC = rocketDestination == 1 ? 15 : 392;
  7465. notifBox.title = 'This value is only an estimation based on an average speed of '+AVG_KM_PER_SEC+'km per second.';
  7466. notifArea.appendChild(notifBox);
  7467. var img = notifBox.getElementsByTagName('img').item(0);
  7468. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7469. var percentEl = notifBox.getElementsByClassName(PERCENT_CLASS).item(0);
  7470. var smoothedTime = 0;
  7471.  
  7472. function updateRocketKm()
  7473. {
  7474. if(AVG_KM_PER_SEC != (rocketDestination == 1 ? 15 : 392))
  7475. {
  7476. AVG_KM_PER_SEC = rocketDestination == 1 ? 15 : 392;
  7477. notifBox.title = 'This value is only an estimation based on an average speed of '+AVG_KM_PER_SEC+'km per second.';
  7478. }
  7479. var hideStatic = win.rocketKm < (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM);
  7480. var hideTimer = win.rocketKm <= 0 || !hideStatic;
  7481. notifBox.style.display = hideTimer ? 'none' : '';
  7482. var percent = win.rocketKm / (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM);
  7483. var diff = (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM) - win.rocketKm;
  7484. if (win.rocketMoonId < 0)
  7485. {
  7486. percent = 1 - percent;
  7487. diff = win.rocketKm;
  7488. }
  7489. var avgRemainingTime = Math.round(diff / AVG_KM_PER_SEC);
  7490. // be more accurate in the last few seconds (may be the last 2 up to 16 seconds)
  7491. var threshold = smoothedTime < 10 ? 1 : 8;
  7492. if (Math.abs(smoothedTime - avgRemainingTime) >= threshold)
  7493. {
  7494. smoothedTime = avgRemainingTime + 1;
  7495. }
  7496. percentEl.textContent = Math.floor(percent * 100).toString();
  7497. }
  7498.  
  7499. function tickRocketTimer()
  7500. {
  7501. if (smoothedTime > 0)
  7502. {
  7503. smoothedTime = Math.max(smoothedTime - 1, 0);
  7504. timerEl.textContent = format.timer(smoothedTime);
  7505. }
  7506. }
  7507. updateRocketKm();
  7508. observer.add('rocketKm', function (key, oldValue, newValue)
  7509. {
  7510. return updateRocketKm();
  7511. });
  7512. observer.addTick(function ()
  7513. {
  7514. return tickRocketTimer();
  7515. });
  7516.  
  7517. function updateRocketDirection()
  7518. {
  7519. // alternatively: `transform: rotateZ(180deg) rotateY(180deg)`
  7520. var transform = win.rocketMoonId >= 0 ? '' : 'rotate(90deg)';
  7521. img.style.transform = transform;
  7522. var itemBox = document.getElementById('default-item-img-tag-boundRocket');
  7523. if (itemBox)
  7524. {
  7525. itemBox.style.transform = transform;
  7526. }
  7527. }
  7528. updateRocketDirection();
  7529. observer.add('rocketMoonId', function ()
  7530. {
  7531. return updateRocketDirection();
  7532. });
  7533. }
  7534. function addVendorRefreshTimer()
  7535. {
  7536. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7537. if (!notifArea)
  7538. {
  7539. return;
  7540. }
  7541. var notifBox = document.createElement('span');
  7542. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7543. notifBox.id = 'notif-vendorRefresh';
  7544. notifBox.style.display = 'none';
  7545. notifBox.innerHTML = "<img src=\"images/vendor.png\" class=\"image-icon-50\" id=\"notif-vendorRefresh-img\" style=\"margin-right: 10px;\"><span class=\"timer\" data-item-display=\"vendorRefreshTimer\"></span>";
  7546. notifArea.appendChild(notifBox);
  7547. var img = notifBox.getElementsByTagName('img').item(0);
  7548. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7549.  
  7550. function updateVendorRefreshTimer()
  7551. {
  7552. var hideTimer = win.vendorNotification == 1;
  7553. notifBox.style.display = hideTimer ? 'none' : '';
  7554. var timeLeft = Math.floor( (12*60*60) - (Date.now()-vendorLastChanged)/1000 );
  7555. timerEl.textContent = format.timer(timeLeft);
  7556. }
  7557. updateVendorRefreshTimer();
  7558. observer.addTick(function ()
  7559. {
  7560. return updateVendorRefreshTimer();
  7561. });
  7562. }
  7563. function addBobsUncleTimer()
  7564. {
  7565. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7566. if (!notifArea)
  7567. {
  7568. return;
  7569. }
  7570. var notifBox = document.createElement('span');
  7571. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7572. notifBox.id = 'notif-bobsUncle';
  7573. notifBox.style.display = 'none';
  7574. notifBox.innerHTML = "<img src=\"images/bobsUncle.png\" class=\"image-icon-50\" id=\"notif-bobsUncle-img\" style=\"margin-right: 10px;\"><span class=\"timer\" data-item-display=\"bobsUncleTimer\"></span>";
  7575. notifArea.appendChild(notifBox);
  7576. var img = notifBox.getElementsByTagName('img').item(0);
  7577. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7578.  
  7579. function updateBobsUncleTimer()
  7580. {
  7581. var hideTimer = win.farmingPatchStage7 == 0 || win.farmingPatchStage7 == 4;
  7582. notifBox.style.display = hideTimer ? 'none' : '';
  7583. var timeLeft = getGameValue('farmingPatchGrowTime7') - getGameValue('farmingPatchTimer7');
  7584. timerEl.textContent = format.timer(timeLeft);
  7585. }
  7586. updateBobsUncleTimer();
  7587. observer.addTick(function ()
  7588. {
  7589. return updateBobsUncleTimer();
  7590. });
  7591. }
  7592. function getLogTypeList()
  7593. {
  7594. var list = [];
  7595. var els = document.querySelectorAll('input[id^="input-charcoalFoundry-"]');
  7596. for (var i = 0; i < els.length; i++)
  7597. {
  7598. list.push(els[i].id.replace(/^input-charcoalFoundry-/i, ''));
  7599. }
  7600. return list;
  7601. }
  7602.  
  7603. function improveFoundryTimer()
  7604. {
  7605. var el = document.getElementById('notif-charcoalFoundry');
  7606. if (!el)
  7607. {
  7608. return;
  7609. }
  7610. var notifBox = el;
  7611. notifBox.classList.add(IMPROVED_CLASS);
  7612. var timerEl = document.createElement('span');
  7613. timerEl.className = TIMER_CLASS;
  7614. notifBox.appendChild(timerEl);
  7615. var remainingEl = document.createElement('span');
  7616. remainingEl.className = REMAINING_CLASS;
  7617. notifBox.appendChild(remainingEl);
  7618. var logTypeList = null;
  7619. observer.add('charcoalFoundryN', function (key, oldValue, newValue)
  7620. {
  7621. timerEl.textContent = format.timer(win.charcoalFoundryD - win.charcoalFoundryN);
  7622. // init log type list when needed
  7623. if (!logTypeList)
  7624. {
  7625. logTypeList = getLogTypeList();
  7626. }
  7627. var woodAmount = win.charcoalFoundryTotal - win.charcoalFoundryCurrent;
  7628. var coalPerLog = win.getCharcoalPerLog(logTypeList[win.charcoalFoundryLogId - 1]);
  7629. var remainingCoal = woodAmount * (isNaN(coalPerLog) ? 1 : coalPerLog);
  7630. remainingEl.textContent = remainingCoal.toString();
  7631. });
  7632. }
  7633.  
  7634. function init()
  7635. {
  7636. bindNewFormatter();
  7637. applyStyle();
  7638. improveSmeltingTimer();
  7639. improveTreeGrowTimer();
  7640. improveSeedGrowTimer();
  7641. addTabTimer();
  7642. addOilInfo();
  7643. addRocketTimer();
  7644. improveFoundryTimer();
  7645. addVendorRefreshTimer();
  7646. addBobsUncleTimer();
  7647. }
  7648. timer.init = init;
  7649. })(timer || (timer = {}));
  7650.  
  7651. /**
  7652. * improve smelting dialog
  7653. */
  7654. var smelting;
  7655. (function (smelting)
  7656. {
  7657. smelting.name = 'smelting';
  7658. var TIME_NEEDED_ID = 'smelting-time-needed';
  7659. var LAST_SMELTING_AMOUNT_KEY = 'lastSmeltingAmount';
  7660. var LAST_SMELTING_BAR_KEY = 'lastSmeltingBar';
  7661. var smeltingValue = null;
  7662. var amountInput;
  7663.  
  7664. function prepareAmountInput()
  7665. {
  7666. amountInput = document.getElementById('input-smelt-bars-amount');
  7667. amountInput.type = 'number';
  7668. amountInput.min = '0';
  7669. amountInput.step = '5';
  7670.  
  7671. function onValueChange()
  7672. {
  7673. smeltingValue = null;
  7674. win.selectBar('', null, amountInput, document.getElementById('smelting-furnace-capacity').value);
  7675. }
  7676. amountInput.addEventListener('mouseup', onValueChange);
  7677. amountInput.addEventListener('keyup', onValueChange);
  7678. amountInput.setAttribute('onkeyup', '');
  7679. }
  7680.  
  7681. function setBarCap(bar, capacity)
  7682. {
  7683. if (bar == '')
  7684. {
  7685. bar = win.selectedBar;
  7686. }
  7687. var requirements = SMELTING_REQUIREMENTS[bar];
  7688. var maxAmount = parseInt(capacity, 10);
  7689. for (var key in requirements)
  7690. {
  7691. var req = requirements[key];
  7692. maxAmount = Math.min(Math.floor(getGameValue(key) / req), maxAmount);
  7693. }
  7694. var value = parseInt(amountInput.value, 10);
  7695. if (value > maxAmount)
  7696. {
  7697. smeltingValue = value;
  7698. amountInput.value = maxAmount.toString();
  7699. }
  7700. else if (smeltingValue != null)
  7701. {
  7702. amountInput.value = Math.min(smeltingValue, maxAmount).toString();
  7703. if (smeltingValue <= maxAmount)
  7704. {
  7705. smeltingValue = null;
  7706. }
  7707. }
  7708. }
  7709.  
  7710. function prepareTimeNeeded()
  7711. {
  7712. var neededMatsEl = document.getElementById('dialogue-furnace-mats-needed');
  7713. var parent = neededMatsEl && neededMatsEl.parentElement;
  7714. if (!neededMatsEl || !parent)
  7715. {
  7716. return;
  7717. }
  7718. var br = document.createElement('br');
  7719. var timeBox = document.createElement('div');
  7720. timeBox.className = 'basic-smallbox';
  7721. timeBox.innerHTML = "<img src=\"images/icons/hourglass.png\" class=\"image-icon-30\">\n\t\tDuration: <span id=\"" + TIME_NEEDED_ID + "\"></span>";
  7722. var next = neededMatsEl.nextElementSibling;
  7723. parent.insertBefore(br, next);
  7724. parent.insertBefore(timeBox, next);
  7725. }
  7726.  
  7727. function updateTimeNeeded(value)
  7728. {
  7729. var timeEl = document.getElementById(TIME_NEEDED_ID);
  7730. if (!timeEl)
  7731. {
  7732. return;
  7733. }
  7734. var num = parseInt(value, 10);
  7735. var timePerBar = win.getTimerPerBar(win.selectedBar);
  7736. timeEl.textContent = format.timer(timePerBar * num);
  7737. }
  7738.  
  7739. function init()
  7740. {
  7741. prepareAmountInput();
  7742. prepareTimeNeeded();
  7743. var _selectBar = win.selectBar;
  7744. var updateSmeltingRequirements = function (bar, inputElement, inputBarsAmountEl, capacity)
  7745. {
  7746. _selectBar(bar, inputElement, inputBarsAmountEl, capacity);
  7747. var matsArea = document.getElementById('dialogue-furnace-mats-needed');
  7748. if (matsArea)
  7749. {
  7750. matsArea.innerHTML = format.numbersInText(matsArea.innerHTML);
  7751. }
  7752. updateTimeNeeded(inputBarsAmountEl.value);
  7753. };
  7754. win.selectBar = function (bar, inputElement, inputBarsAmountEl, capacity)
  7755. {
  7756. setBarCap(bar, capacity);
  7757. // save selected bar
  7758. if (bar != '')
  7759. {
  7760. store.set(LAST_SMELTING_BAR_KEY, bar);
  7761. }
  7762. // save amount
  7763. store.set(LAST_SMELTING_AMOUNT_KEY, inputBarsAmountEl.value);
  7764. updateSmeltingRequirements(bar, inputElement, inputBarsAmountEl, capacity);
  7765. };
  7766. var lastBar = store.get(LAST_SMELTING_BAR_KEY);
  7767. var lastAmount = store.get(LAST_SMELTING_AMOUNT_KEY);
  7768. var _openFurnaceDialogue = win.openFurnaceDialogue;
  7769. win.openFurnaceDialogue = function (furnace)
  7770. {
  7771. var capacity = win.getFurnaceCapacity(furnace);
  7772. if (win.smeltingBarType == 0)
  7773. {
  7774. amountInput.max = capacity.toString();
  7775. }
  7776. // restore amount
  7777. var inputBarsAmountEl = document.getElementById('input-smelt-bars-amount');
  7778. if (inputBarsAmountEl && inputBarsAmountEl.value == '-1' && lastAmount != null)
  7779. {
  7780. inputBarsAmountEl.value = lastAmount;
  7781. }
  7782. _openFurnaceDialogue(furnace);
  7783. // restore selected bar
  7784. if ((!win.selectedBar || win.selectedBar == 'none') && lastBar != null)
  7785. {
  7786. win.selectedBar = lastBar;
  7787. }
  7788. // update whether requirements are fulfilled
  7789. var barInputId = 'input-furnace-' + split2Words(win.selectedBar, '-').toLowerCase();
  7790. var inputElement = document.getElementById(barInputId);
  7791. if (inputElement && inputBarsAmountEl)
  7792. {
  7793. updateSmeltingRequirements(win.selectedBar, inputElement, inputBarsAmountEl, capacity.toString());
  7794. }
  7795. };
  7796. }
  7797. smelting.init = init;
  7798. })(smelting || (smelting = {}));
  7799.  
  7800. /**
  7801. * add chance to time calculator
  7802. */
  7803. var fishingInfo;
  7804. (function (fishingInfo)
  7805. {
  7806. fishingInfo.name = 'fishingInfo';
  7807. /**
  7808. * calculates the number of seconds until the event with the given chance happened at least once with the given
  7809. * probability p (in percent)
  7810. */
  7811. function calcSecondsTillP(chancePerSecond, p)
  7812. {
  7813. return Math.round(Math.log(1 - p / 100) / Math.log(1 - chancePerSecond));
  7814. }
  7815.  
  7816. function addChanceTooltip(headline, chancePerSecond, elId, targetEl)
  7817. {
  7818. // ensure tooltip exists and is correctly binded
  7819. var tooltipEl = ensureTooltip('chance-' + elId, targetEl);
  7820. // set elements content
  7821. var percValues = [1, 10, 20, 50, 80, 90, 99];
  7822. var percRows = '';
  7823. for (var _i = 0, percValues_1 = percValues; _i < percValues_1.length; _i++)
  7824. {
  7825. var p = percValues_1[_i];
  7826. 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>";
  7827. }
  7828. 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";
  7829. }
  7830.  
  7831. function addChanceStyle()
  7832. {
  7833. 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");
  7834. }
  7835.  
  7836. function addXp()
  7837. {
  7838. var table = document.querySelector('#dialogue-id-fishingRod table');
  7839. if (!table)
  7840. {
  7841. return;
  7842. }
  7843. var rows = table.rows;
  7844. for (var i = 0; i < rows.length; i++)
  7845. {
  7846. var row = rows.item(i);
  7847. if (row.classList.contains('xp-added'))
  7848. {
  7849. continue;
  7850. }
  7851. if (i == 0)
  7852. {
  7853. var xpCell = document.createElement('th');
  7854. xpCell.textContent = 'XP';
  7855. row.appendChild(xpCell);
  7856. }
  7857. else
  7858. {
  7859. var cell = row.insertCell(-1);
  7860. var rawFish = row.id.replace('dialogue-fishing-rod-tr-', '');
  7861. var xp = FISH_XP[rawFish];
  7862. cell.textContent = xp == null ? '?' : format.number(xp);
  7863. }
  7864. row.classList.add('xp-added');
  7865. }
  7866. }
  7867.  
  7868. function chance2TimeCalculator()
  7869. {
  7870. var table = document.querySelector('#dialogue-id-fishingRod table');
  7871. if (!table)
  7872. {
  7873. return;
  7874. }
  7875. var rows = table.rows;
  7876. for (var i = 1; i < rows.length; i++)
  7877. {
  7878. var row = rows.item(i);
  7879. var rawFish = row.id.replace('dialogue-fishing-rod-tr-', '');
  7880. var fish = rawFish.replace('raw', '').toLowerCase();
  7881. if (!rawFish || !fish)
  7882. {
  7883. continue;
  7884. }
  7885. var chanceCell = row.cells.item(row.cells.length - 2);
  7886. var chance = (chanceCell.textContent || '')
  7887. .replace(/[^\d\/]/g, '')
  7888. .split('/')
  7889. .reduce(function (p, c)
  7890. {
  7891. return p / parseInt(c, 10);
  7892. }, 1);
  7893. addChanceTooltip("One raw " + fish + " at least every:", chance, rawFish, row);
  7894. }
  7895. }
  7896.  
  7897. function init()
  7898. {
  7899. addChanceStyle();
  7900. var _clicksShovel = win.clicksShovel;
  7901. win.clicksShovel = function ()
  7902. {
  7903. _clicksShovel();
  7904. var shovelChance = document.getElementById('dialogue-shovel-chance');
  7905. var titleEl = shovelChance.parentElement;
  7906. var chance = 1 / win.getChanceOfDiggingSand();
  7907. addChanceTooltip('One sand at least every:', chance, 'shovel', titleEl);
  7908. };
  7909. // depends on fishingXp
  7910. var _clicksFishingRod = win.clicksFishingRod;
  7911. win.clicksFishingRod = function ()
  7912. {
  7913. _clicksFishingRod();
  7914. addXp();
  7915. chance2TimeCalculator();
  7916. };
  7917. }
  7918. fishingInfo.init = init;
  7919. })(fishingInfo || (fishingInfo = {}));
  7920.  
  7921. /**
  7922. * add tooltips for recipes
  7923. */
  7924. var recipeTooltips;
  7925. (function (recipeTooltips)
  7926. {
  7927. recipeTooltips.name = 'recipeTooltips';
  7928.  
  7929. function updateRecipeTooltips(recipeKey, recipes)
  7930. {
  7931. var table = document.getElementById('table-' + recipeKey + '-recipe');
  7932. var rows = table.rows;
  7933.  
  7934. function recipe2Title(recipe)
  7935. {
  7936. return recipe.recipe
  7937. .map(function (name, i)
  7938. {
  7939. return format.number(recipe.recipeCost[i]) + String.fromCharCode(160)
  7940. + split2Words(name).toLowerCase();
  7941. })
  7942. .join(' + ');
  7943. };
  7944. for (var i = 1; i < rows.length; i++)
  7945. {
  7946. var row = rows.item(i);
  7947. var key = row.id.replace(recipeKey + '-', '');
  7948. var recipe = recipes[key];
  7949. var requirementCell = row.cells.item(3);
  7950. requirementCell.title = recipe2Title(recipe);
  7951. win.$(requirementCell).tooltip();
  7952. }
  7953. }
  7954.  
  7955. function updateTooltipsOnReinitRecipes(key)
  7956. {
  7957. var capitalKey = capitalize(key);
  7958. var processKey = 'process' + capitalKey + 'Tab';
  7959. var _processTab = win[processKey];
  7960. win[processKey] = function ()
  7961. {
  7962. var reinit = !!getGameValue('refreshLoad' + capitalKey + 'Table');
  7963. _processTab();
  7964. if (reinit)
  7965. {
  7966. updateRecipeTooltips(key, getGameValue(key + 'Recipes'));
  7967. }
  7968. };
  7969. }
  7970.  
  7971. function init()
  7972. {
  7973. updateTooltipsOnReinitRecipes('crafting');
  7974. updateTooltipsOnReinitRecipes('brewing');
  7975. updateTooltipsOnReinitRecipes('magic');
  7976. updateTooltipsOnReinitRecipes('cooksBook');
  7977. }
  7978. recipeTooltips.init = init;
  7979. })(recipeTooltips || (recipeTooltips = {}));
  7980.  
  7981. /**
  7982. * fix formatting of numbers
  7983. */
  7984. var fixNumbers;
  7985. (function (fixNumbers)
  7986. {
  7987. fixNumbers.name = 'fixNumbers';
  7988.  
  7989. function prepareRecipeForTable(recipe)
  7990. {
  7991. // create a copy of the recipe to prevent requirement check from failing
  7992. var newRecipe = JSON.parse(JSON.stringify(recipe));
  7993. newRecipe.recipeCost = recipe.recipeCost.map(function (cost)
  7994. {
  7995. return format.number(cost);
  7996. });
  7997. newRecipe.description = format.numbersInText(newRecipe.description);
  7998. newRecipe.xp = format.number(recipe.xp);
  7999. return newRecipe;
  8000. }
  8001.  
  8002. function init()
  8003. {
  8004. var _addRecipeToBrewingTable = win.addRecipeToBrewingTable;
  8005. win.addRecipeToBrewingTable = function (brewingRecipe)
  8006. {
  8007. _addRecipeToBrewingTable(prepareRecipeForTable(brewingRecipe));
  8008. };
  8009. var _addRecipeToMagicTable = win.addRecipeToMagicTable;
  8010. win.addRecipeToMagicTable = function (magicRecipe)
  8011. {
  8012. _addRecipeToMagicTable(prepareRecipeForTable(magicRecipe));
  8013. };
  8014. var _addRecipeToCooksBookTable = win.addRecipeToCooksBookTable;
  8015. win.addRecipeToCooksBookTable = function (cooksBookRecipe)
  8016. {
  8017. _addRecipeToCooksBookTable(prepareRecipeForTable(cooksBookRecipe));
  8018. };
  8019. var tooltipList = document.querySelectorAll('#tooltip-list div[id^="tooltip-"][id$="Seeds"]');
  8020. for (var i = 0; i < tooltipList.length; i++)
  8021. {
  8022. var tooltip = tooltipList[i];
  8023. tooltip.innerHTML = format.numbersInText(tooltip.innerHTML);
  8024. }
  8025. var fightEnergyCells = document.querySelectorAll('#dialogue-fight tr > td:nth-child(4)');
  8026. for (var i = 0; i < fightEnergyCells.length; i++)
  8027. {
  8028. var cell = fightEnergyCells[i];
  8029. cell.innerHTML = format.numbersInText(cell.innerHTML);
  8030. }
  8031. var _rocketTick = win.rocketTick;
  8032. win.rocketTick = function ()
  8033. {
  8034. _rocketTick();
  8035. var rocketBox = document.getElementById('itembox-rocket');
  8036. if (rocketBox && /^\d+\s*Km$/i.test(rocketBox.textContent || ''))
  8037. {
  8038. rocketBox.innerHTML = format.numbersInText(rocketBox.innerHTML).replace('Km', 'km');
  8039. }
  8040. };
  8041. }
  8042. fixNumbers.init = init;
  8043. })(fixNumbers || (fixNumbers = {}));
  8044.  
  8045. /**
  8046. * add slider for machines
  8047. */
  8048. var machineDialog;
  8049. (function (machineDialog)
  8050. {
  8051. machineDialog.name = 'machineDialog';
  8052. var $slider;
  8053.  
  8054. function createSlider()
  8055. {
  8056. var br = document.querySelector('#dialogue-machinery-current-total ~ br');
  8057. var parent = br && br.parentElement;
  8058. if (!br || !parent)
  8059. {
  8060. return;
  8061. }
  8062. 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");
  8063. var slider = document.createElement('div');
  8064. parent.insertBefore(slider, br);
  8065. $slider = win.$(slider)
  8066. .slider(
  8067. {
  8068. range: 'max'
  8069. , min: 0
  8070. , max: 10
  8071. , value: 0
  8072. , slide: function (event, ui)
  8073. {
  8074. return updateValue(ui.value);
  8075. }
  8076. });
  8077. // hide br and up/down arrows
  8078. br.style.display = 'none';
  8079. var arrows = document.querySelectorAll('input[onclick^="turnOn("]');
  8080. for (var i = 0; i < arrows.length; i++)
  8081. {
  8082. arrows[i].style.display = 'none';
  8083. }
  8084. var els = document.querySelectorAll('[onclick*="openMachineryDialogue("]');
  8085. var boundMachineKeyList = [];
  8086. for (var i = 0; i < els.length; i++)
  8087. {
  8088. var match = els[i].id.match(/openMachineryDialogue\('(.+?)'\)/);
  8089. if (match)
  8090. {
  8091. boundMachineKeyList.push(getBoundKey(match[1]));
  8092. }
  8093. }
  8094. observer.add(boundMachineKeyList, function ()
  8095. {
  8096. return updateMax();
  8097. });
  8098. }
  8099.  
  8100. function updateMax()
  8101. {
  8102. var machineEl = document.getElementById('dialogue-machinery-chosen');
  8103. if (machineEl && machineEl.value != '')
  8104. {
  8105. var boundMachineKey = getBoundKey(machineEl.value);
  8106. var ownedNum = getGameValue(boundMachineKey);
  8107. $slider.slider('option', 'max', ownedNum);
  8108. $slider.get(0).dataset.owned = ownedNum.toString();
  8109. }
  8110. }
  8111.  
  8112. function updateValue(value)
  8113. {
  8114. var typeEl = document.getElementById('dialogue-machinery-chosen');
  8115. var numEl = document.getElementById('dialogue-machinery-current-on');
  8116. if (numEl && typeEl)
  8117. {
  8118. var valueBefore = parseInt(numEl.textContent || '0', 10);
  8119. var machine = typeEl.value;
  8120. var increment = valueBefore < value;
  8121. var diff = Math.abs(valueBefore - value);
  8122. for (var i = 0; i < diff; i++)
  8123. {
  8124. win.turnOn(machine, increment);
  8125. }
  8126. }
  8127. }
  8128.  
  8129. function init()
  8130. {
  8131. if (!settings.get(settings.KEY.changeMachineDialog))
  8132. {
  8133. return;
  8134. }
  8135. createSlider();
  8136. var _openMachineryDialogue = win.openMachineryDialogue;
  8137. win.openMachineryDialogue = function (machineType)
  8138. {
  8139. _openMachineryDialogue(machineType);
  8140. updateMax();
  8141. $slider.slider('value', getGameValue(machineType + 'On'));
  8142. };
  8143. }
  8144. machineDialog.init = init;
  8145. })(machineDialog || (machineDialog = {}));
  8146.  
  8147. /**
  8148. * improve behaviour of amount inputs
  8149. */
  8150. var amountInputs;
  8151. (function (amountInputs)
  8152. {
  8153. amountInputs.name = 'amountInputs';
  8154.  
  8155. function getVialType(recipe)
  8156. {
  8157. return recipe.levelReq < 35 ? 'vialOfWater' : (recipe.levelReq < 65 ? 'largeVialOfWater' : 'hugeVialOfWater');
  8158. }
  8159.  
  8160. function getSimpleMax(recipe)
  8161. {
  8162. var max = Number.MAX_SAFE_INTEGER;
  8163. for (var i = 0; i < recipe.recipe.length; i++)
  8164. {
  8165. max = Math.min(max, Math.floor(getGameValue(recipe.recipe[i]) / recipe.recipeCost[i]));
  8166. }
  8167. return max;
  8168. }
  8169.  
  8170. function getMax(recipe)
  8171. {
  8172. var max = getSimpleMax(recipe);
  8173. if (/Potion$/.test(recipe.itemName))
  8174. {
  8175. var vialType = getVialType(recipe);
  8176. max = Math.min(max, getGameValue(vialType));
  8177. }
  8178. return max;
  8179. }
  8180.  
  8181. function ensureNumberInput(idOrEl)
  8182. {
  8183. var numInput = typeof idOrEl === 'string' ? document.getElementById(idOrEl) : idOrEl;
  8184. if (numInput)
  8185. {
  8186. if (numInput.type != 'number' && settings.get(settings.KEY.makeNumberInputs))
  8187. {
  8188. var width = numInput.clientWidth;
  8189. if (width !== 0)
  8190. {
  8191. numInput.style.width = width + 'px';
  8192. }
  8193. numInput.type = 'number';
  8194. numInput.min = '0';
  8195. var onkeyup_1 = numInput.getAttribute('onkeyup');
  8196. if (onkeyup_1)
  8197. {
  8198. numInput.setAttribute('onmouseup', onkeyup_1);
  8199. }
  8200. }
  8201. else if (numInput.type == 'number' && !settings.get(settings.KEY.makeNumberInputs))
  8202. {
  8203. numInput.style.width = '';
  8204. numInput.type = '';
  8205. numInput.removeAttribute('onmouseup');
  8206. }
  8207. }
  8208. return numInput;
  8209. }
  8210.  
  8211. function getCurrentMax(keyId, recipeCollection)
  8212. {
  8213. var keyEl = document.getElementById(keyId);
  8214. if (!keyEl)
  8215. {
  8216. return 0;
  8217. }
  8218. var key = keyEl.value;
  8219. return getMax(recipeCollection[key]);
  8220. };
  8221.  
  8222. function ensureMaxBtn(keyId, inputId, recipeCollection, key)
  8223. {
  8224. var recipe = recipeCollection[key];
  8225. var numInput = ensureNumberInput(inputId);
  8226. var next = numInput && numInput.nextElementSibling;
  8227. var parent = numInput && numInput.parentElement;
  8228. if (numInput && parent)
  8229. {
  8230. if ((!next || next.nodeName !== 'BUTTON') && settings.get(settings.KEY.addMaxBtn))
  8231. {
  8232. var btn = document.createElement('button');
  8233. btn.textContent = 'Max';
  8234. btn.addEventListener('click', function ()
  8235. {
  8236. numInput.value = getCurrentMax(keyId, recipeCollection).toString();
  8237. });
  8238. parent.appendChild(btn);
  8239. }
  8240. else if (next && next.nodeName === 'BUTTON' && !settings.get(settings.KEY.addMaxBtn))
  8241. {
  8242. parent.removeChild(next);
  8243. }
  8244. numInput.value = Math.min(1, getMax(recipe)).toString();
  8245. numInput.select();
  8246. }
  8247. }
  8248.  
  8249. function watchKeepInput(event)
  8250. {
  8251. var itemInput = document.getElementById('npc-sell-item-chosen');
  8252. var numInput = ensureNumberInput('dialogue-input-cmd');
  8253. if (!itemInput || !numInput)
  8254. {
  8255. return;
  8256. }
  8257. var item = itemInput.value;
  8258. var newValue = Math.max(getGameValue(item) - Number(this.value), 0);
  8259. numInput.value = newValue.toString();
  8260. }
  8261.  
  8262. function updateKeepMaxValue(keepInput, init)
  8263. {
  8264. if (init === void 0)
  8265. {
  8266. init = false;
  8267. }
  8268. var itemInput = document.getElementById('npc-sell-item-chosen');
  8269. if (!itemInput)
  8270. {
  8271. return;
  8272. }
  8273. var item = itemInput.value;
  8274. var max = getGameValue(item);
  8275. keepInput.max = max.toString();
  8276. if (init)
  8277. {
  8278. observer.addTick(function ()
  8279. {
  8280. return updateKeepMaxValue(keepInput);
  8281. });
  8282. }
  8283. }
  8284.  
  8285. function ensureKeepInput(item)
  8286. {
  8287. var numInput = ensureNumberInput('dialogue-input-cmd');
  8288. var parent = numInput && numInput.parentElement;
  8289. var next = numInput && numInput.nextElementSibling;
  8290. var nextNext = next && next.nextElementSibling;
  8291. if (next && nextNext && parent)
  8292. {
  8293. if (nextNext.nodeName === 'BR' && settings.get(settings.KEY.addKeepInput))
  8294. {
  8295. var div = document.createElement('div');
  8296. var text = document.createTextNode('Keep: ');
  8297. div.appendChild(text);
  8298. var keepInput = document.createElement('input');
  8299. keepInput.type = 'number';
  8300. keepInput.value = keepInput.min = '0';
  8301. keepInput.max = getGameValue(item).toString();
  8302. keepInput.addEventListener('keyup', watchKeepInput);
  8303. keepInput.addEventListener('mouseup', watchKeepInput);
  8304. updateKeepMaxValue(keepInput, true);
  8305. div.appendChild(keepInput);
  8306. parent.insertBefore(div, nextNext);
  8307. }
  8308. else if (nextNext.nodeName !== 'BR' && !settings.get(settings.KEY.addKeepInput))
  8309. {
  8310. var br = document.createElement('br');
  8311. parent.insertBefore(br, nextNext);
  8312. parent.removeChild(nextNext);
  8313. }
  8314. }
  8315. }
  8316.  
  8317. function init()
  8318. {
  8319. var _multiCraft = win.multiCraft;
  8320. win.multiCraft = function (item)
  8321. {
  8322. _multiCraft(item);
  8323. ensureMaxBtn('dialogue-multicraft-chosen', 'dialogue-multicraft-input', win.craftingRecipes, item);
  8324. };
  8325. var _brew = win.brew;
  8326. win.brew = function (potion)
  8327. {
  8328. _brew(potion);
  8329. ensureMaxBtn('dialogue-potion-chosen', 'dialogue-brewing-input', win.brewingRecipes, potion);
  8330. };
  8331. var _cooksBookInputDialogue = win.cooksBookInputDialogue;
  8332. win.cooksBookInputDialogue = function (food)
  8333. {
  8334. _cooksBookInputDialogue(food);
  8335. ensureMaxBtn('dialogue-cooksBook-chosen', 'dialogue-cooksBook-input', win.cooksBookRecipes, food);
  8336. };
  8337. var _openSellNPCDialogue = win.openSellNPCDialogue;
  8338. win.openSellNPCDialogue = function (item)
  8339. {
  8340. _openSellNPCDialogue(item);
  8341. ensureKeepInput(item);
  8342. };
  8343. var allowedInputs = [
  8344. 'dialogue-ashes'
  8345. , 'dialogue-bindDonorCoins'
  8346. , 'dialogue-bonemeal'
  8347. , 'dialogue-bones'
  8348. , 'dialogue-brewing'
  8349. , 'dialogue-buy-item-2'
  8350. , 'dialogue-buyFromMarket'
  8351. , 'dialogue-charcoalFoundry'
  8352. , 'dialogue-consume'
  8353. , 'dialogue-cooksBook'
  8354. , 'dialogue-createArrows'
  8355. , 'dialogue-createFireArrows'
  8356. , 'dialogue-createIceArrows'
  8357. , 'dialogue-furnace'
  8358. , 'dialogue-iceBones'
  8359. , 'dialogue-id-boundHammer'
  8360. , 'dialogue-id-boundPickaxe'
  8361. , 'dialogue-id-cook-food'
  8362. , 'dialogue-id-oven-addheat'
  8363. , 'dialogue-market-chosenpostitem'
  8364. , 'dialogue-multicraft'
  8365. , 'dialogue-oilBarrels'
  8366. , 'dialogue-oilFactory'
  8367. , 'dialogue-sell-item'
  8368. , 'dialogue-stardustCrystals'
  8369. , 'dialogue-wand'
  8370. ];
  8371. var _openDialogue = win.openDialogue;
  8372. win.openDialogue = function (id, width, position)
  8373. {
  8374. _openDialogue(id, width, position);
  8375. if (allowedInputs.indexOf(id) === -1
  8376. || id === 'dialogue-buyFromMarket' && market.detectTedsUI()
  8377. || id === 'dialogue-market-chosenpostitem' && market.detectTedsUI())
  8378. {
  8379. return;
  8380. }
  8381. var dialog = document.getElementById(id);
  8382. var input = dialog && dialog.querySelector('input[type="text"],input[type="number"]');
  8383. if (!input)
  8384. {
  8385. return;
  8386. }
  8387. ensureNumberInput(input);
  8388. };
  8389. }
  8390. amountInputs.init = init;
  8391. })(amountInputs || (amountInputs = {}));
  8392.  
  8393. /**
  8394. * improves the top bar
  8395. */
  8396. var newTopbar;
  8397. (function (newTopbar)
  8398. {
  8399. newTopbar.name = 'newTopbar';
  8400. var linkCell, tabCell, infoCell;
  8401. var addQueues = {
  8402. link: []
  8403. , tab: []
  8404. , info: []
  8405. };
  8406.  
  8407. function createPipeNode()
  8408. {
  8409. return document.createTextNode('|');
  8410. }
  8411.  
  8412. function addLinkEntry(el)
  8413. {
  8414. if (!linkCell)
  8415. {
  8416. addQueues.link.push(el);
  8417. }
  8418. else
  8419. {
  8420. linkCell.appendChild(createPipeNode());
  8421. linkCell.appendChild(el);
  8422. }
  8423. }
  8424. newTopbar.addLinkEntry = addLinkEntry;
  8425.  
  8426. function addTabEntry(el)
  8427. {
  8428. if (!tabCell)
  8429. {
  8430. addQueues.tab.push(el);
  8431. }
  8432. else
  8433. {
  8434. tabCell.appendChild(createPipeNode());
  8435. tabCell.appendChild(el);
  8436. }
  8437. }
  8438. newTopbar.addTabEntry = addTabEntry;
  8439.  
  8440. function addInfoEntry(el)
  8441. {
  8442. if (!infoCell)
  8443. {
  8444. addQueues.info.push(el);
  8445. }
  8446. else
  8447. {
  8448. if (infoCell.firstChild)
  8449. {
  8450. infoCell.insertBefore(createPipeNode(), infoCell.firstChild);
  8451. infoCell.insertBefore(el, infoCell.firstChild);
  8452. }
  8453. else
  8454. {
  8455. infoCell.appendChild(createPipeNode());
  8456. infoCell.appendChild(el);
  8457. }
  8458. }
  8459. }
  8460. newTopbar.addInfoEntry = addInfoEntry;
  8461.  
  8462. function init()
  8463. {
  8464. if (!settings.get(settings.KEY.useNewToolbar))
  8465. {
  8466. return;
  8467. }
  8468. 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");
  8469. var table = document.querySelector('table.top-links');
  8470. if (!table)
  8471. {
  8472. return;
  8473. }
  8474. var row = table.rows.item(0);
  8475. var cells = row.cells;
  8476. var tabIdx = [2, 5];
  8477. var infoIdx = [6, 7];
  8478. var newRow = table.insertRow(-1);
  8479. linkCell = newRow.insertCell(-1);
  8480. tabCell = newRow.insertCell(-1);
  8481. tabCell.style.textAlign = 'center';
  8482. infoCell = newRow.insertCell(-1);
  8483. infoCell.style.textAlign = 'right';
  8484. for (var i = 0; i < cells.length; i++)
  8485. {
  8486. var container = linkCell;
  8487. if (tabIdx.indexOf(i) != -1)
  8488. {
  8489. container = tabCell;
  8490. }
  8491. else if (infoIdx.indexOf(i) != -1)
  8492. {
  8493. container = infoCell;
  8494. }
  8495. var cell = cells.item(i);
  8496. var el = cell.firstElementChild;
  8497. if (cell.childNodes.length > 1)
  8498. {
  8499. el = document.createElement('span');
  8500. el.style.color = 'yellow';
  8501. while (cell.childNodes.length > 0)
  8502. {
  8503. el.appendChild(cell.childNodes[0]);
  8504. }
  8505. }
  8506. if (container.children.length > 0)
  8507. {
  8508. container.appendChild(createPipeNode());
  8509. }
  8510. if (el)
  8511. {
  8512. container.appendChild(el);
  8513. }
  8514. }
  8515. var parent = row.parentElement;
  8516. if (parent)
  8517. {
  8518. parent.removeChild(row);
  8519. }
  8520. for (var _i = 0, _a = addQueues.link; _i < _a.length; _i++)
  8521. {
  8522. var el = _a[_i];
  8523. addLinkEntry(el);
  8524. }
  8525. for (var _b = 0, _c = addQueues.tab; _b < _c.length; _b++)
  8526. {
  8527. var el = _c[_b];
  8528. addTabEntry(el);
  8529. }
  8530. for (var _d = 0, _e = addQueues.info; _d < _e.length; _d++)
  8531. {
  8532. var el = _e[_d];
  8533. addInfoEntry(el);
  8534. }
  8535. var _openTab = win.openTab;
  8536. win.openTab = function (newTab)
  8537. {
  8538. var oldTab = win.currentOpenTab;
  8539. _openTab(newTab);
  8540. var children = tabCell.children;
  8541. for (var i = 0; i < children.length; i++)
  8542. {
  8543. var el = children[i];
  8544. var match = (el.getAttribute('onclick') || '').match(/openTab\('([^']+)'\)/);
  8545. if (!match)
  8546. {
  8547. continue;
  8548. }
  8549. var tab = match[1];
  8550. if (oldTab == tab)
  8551. {
  8552. el.style.color = '';
  8553. }
  8554. if (newTab == tab)
  8555. {
  8556. el.style.color = 'white';
  8557. }
  8558. }
  8559. };
  8560. }
  8561. newTopbar.init = init;
  8562. })(newTopbar || (newTopbar = {}));
  8563.  
  8564. /**
  8565. * style tweaks
  8566. */
  8567. var styleTweaks;
  8568. (function (styleTweaks)
  8569. {
  8570. styleTweaks.name = 'styleTweaks';
  8571. var bodyRegex = /(\bbody)(\s|$)/i;
  8572.  
  8573. function addTweakStyle(setting, style)
  8574. {
  8575. if (setting != '')
  8576. {
  8577. var prefix_1 = setting === '' ? '' : 'body.' + setting + ' ';
  8578. style = style
  8579. .replace(/(^\s*|\}\s*)([^\{\}]+)(?=\s*\{)/g, function (wholeMatch, before, rules)
  8580. {
  8581. return before + rules.split(',').map(function (rule)
  8582. {
  8583. if (bodyRegex.test(rule) && setting !== '')
  8584. {
  8585. return rule.replace(bodyRegex, '$1.' + setting + '$2');
  8586. }
  8587. return rule.replace(/^(\s*\n\s*)?/, '$1' + prefix_1);
  8588. }).join(',');
  8589. });
  8590. document.body.classList.add(setting);
  8591. }
  8592. addStyle(style, setting != '' ? setting : null);
  8593. }
  8594. // tweak oil production/consumption
  8595. function tweakOil()
  8596. {
  8597. 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");
  8598. // make room for oil cell on small devices
  8599. var oilFlowValues = document.getElementById('oil-flow-values');
  8600. var oilFlowCell = oilFlowValues.parentElement;
  8601. oilFlowCell.style.width = '30%';
  8602. }
  8603.  
  8604. function tweakSelection()
  8605. {
  8606. 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");
  8607. }
  8608. // tweak stardust monitor of DH2QoL to keep it in place
  8609. function tweakStardust()
  8610. {
  8611. 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");
  8612. }
  8613.  
  8614. function tweakSkillLevelText()
  8615. {
  8616. addTweakStyle('', "\ndiv.skill-xp-label\n{\n\ttext-shadow: white 0px 0px 0.5rem;\n}\n\t\t");
  8617. }
  8618.  
  8619. function tweakFightDialog()
  8620. {
  8621. addTweakStyle('smaller-fight-dialog', "\n#dialogue-fight img[width=\"150px\"]\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");
  8622. }
  8623.  
  8624. function addAdditionalSkillBars()
  8625. {
  8626. var _loadSkillTabs = win.loadSkillTabs;
  8627. win.loadSkillTabs = function ()
  8628. {
  8629. _loadSkillTabs();
  8630. for (var _i = 0, SKILL_LIST_1 = SKILL_LIST; _i < SKILL_LIST_1.length; _i++)
  8631. {
  8632. var skill = SKILL_LIST_1[_i];
  8633. var unlocked = getGameValue(skill + 'Unlocked') == 1;
  8634. if (!unlocked)
  8635. {
  8636. continue;
  8637. }
  8638. var xp = getGameValue(skill + 'Xp');
  8639. var currentLevelXp = win.getXpNeeded(win.getLevel(xp));
  8640. var nextLevelXp = win.getXpNeeded(win.getLevel(xp) + 1);
  8641. var perc = (xp - currentLevelXp) / (nextLevelXp - currentLevelXp) * 100;
  8642. var progress = document.getElementById('skill-progress-' + skill);
  8643. if (progress)
  8644. {
  8645. if (perc >= 100)
  8646. {
  8647. perc = 100;
  8648. progress.style.backgroundColor = "yellow";
  8649. }
  8650. progress.style.width = perc + '%';
  8651. }
  8652. }
  8653. };
  8654. // init additional skill bars
  8655. 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");
  8656. for (var _i = 0, SKILL_LIST_2 = SKILL_LIST; _i < SKILL_LIST_2.length; _i++)
  8657. {
  8658. var skill = SKILL_LIST_2[_i];
  8659. var cell = document.getElementById('top-bar-level-td-' + skill);
  8660. if (!cell)
  8661. {
  8662. continue;
  8663. }
  8664. var levelBar = document.createElement('div');
  8665. levelBar.className = 'skill-bar';
  8666. var progress = document.createElement('div');
  8667. progress.id = 'skill-progress-' + skill;
  8668. progress.className = 'skill-progress';
  8669. levelBar.appendChild(progress);
  8670. cell.appendChild(levelBar);
  8671. // update skill level progress bars on click
  8672. levelBar.addEventListener('click', function ()
  8673. {
  8674. return win.loadSkillTabs();
  8675. });
  8676. }
  8677. win.loadSkillTabs();
  8678. }
  8679. // highlight cooking level requirement when not matched
  8680. function highlightCookinglevel()
  8681. {
  8682. var _cookFoodDialogue = win.cookFoodDialogue;
  8683. win.cookFoodDialogue = function (rawFood)
  8684. {
  8685. _cookFoodDialogue(rawFood);
  8686. var dialog = document.getElementById('dialogue-id-cook-food');
  8687. if (!dialog)
  8688. {
  8689. return;
  8690. }
  8691. var levelReq = document.getElementById('dialogue-cook-levelReq');
  8692. var levelReqLabel = levelReq && levelReq.previousElementSibling;
  8693. if (!levelReq || !levelReqLabel)
  8694. {
  8695. return;
  8696. }
  8697. var fulfilled = win.getCookingLevelReq(rawFood) > win.getLevel(win.cookingXp);
  8698. levelReq.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  8699. levelReq.style.fontWeight = fulfilled ? 'bold' : '';
  8700. levelReqLabel.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  8701. var ratioEl = document.getElementById('dialogue-cook-ratio');
  8702. if (!ratioEl)
  8703. {
  8704. var cookReqBox = levelReq.parentElement;
  8705. var br = document.createElement('br');
  8706. cookReqBox.appendChild(br);
  8707. var b = document.createElement('b');
  8708. b.innerHTML = "<img src=\"images/steak.png\" class=\"image-icon-20\" title=\"Energy\"> per <img src=\"images/icons/fire.png\" class=\"image-icon-20\" title=\"Heat\">: ";
  8709. cookReqBox.appendChild(b);
  8710. ratioEl = document.createElement('span');
  8711. ratioEl.id = 'dialogue-cook-ratio';
  8712. cookReqBox.appendChild(ratioEl);
  8713. }
  8714. var heat = win.getHeatNeeded(rawFood);
  8715. var energy = win.getEnergyGained(rawFood);
  8716. ratioEl.textContent = format.number(Math.round(energy / heat * 100) / 100);
  8717. };
  8718. }
  8719.  
  8720. function amountStyle()
  8721. {
  8722. var tweakName = 'amount-symbol';
  8723. 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");
  8724.  
  8725. function setAmountSymbolVisibility(init)
  8726. {
  8727. if (init === void 0)
  8728. {
  8729. init = false;
  8730. }
  8731. var show = settings.get(settings.KEY.amountSymbol);
  8732. document.body.classList[show ? 'add' : 'remove'](tweakName);
  8733. if (init)
  8734. {
  8735. settings.observe(settings.KEY.amountSymbol, function ()
  8736. {
  8737. return setAmountSymbolVisibility();
  8738. });
  8739. }
  8740. }
  8741. setAmountSymbolVisibility(true);
  8742. }
  8743.  
  8744. function efficiency()
  8745. {
  8746. var EFFICIENCY_CLASS = 'efficiency';
  8747. addTweakStyle(EFFICIENCY_CLASS, "\nbody\n{\n\tmargin: 0;\n}\nbody > br\n{\n\tdisplay: none;\n}\ntable.top-links\n{\n\tborder-left-width: 0px;\n\tborder-right-width: 0px;\n}\n#game-div\n{\n\tmargin-top: 29px;\n}\n#game-div > table.top-bar,\n#game-div > table.tab-bar,\n#div-chat\n{\n\tborder-width: 0;\n\tmargin-top: 0;\n}\n#game-div > table.top-bar#top-bar-levels\n{\n\tborder-width: 1px 0;\n}\n#notifaction-area\n{\n\tpadding: 0;\n}\nspan.notif-box\n{\n\tmargin: -1px;\n\tmargin-left: 0;\n\tpadding: 5px;\n}\n#game-div > div.tab-container\n{\n\tborder-width: 1px 0 0;\n\tpadding: 0;\n}\ndiv.tab-container > h1.container-title:first-child\n{\n\tdisplay: none;\n}\ndiv.item-box-area,\n#tab-sub-container-farming,\n#tab-sub-container-magic-items\n{\n\tmargin: 0;\n\tpadding: 1px 1px 0 0;\n}\nspan.item-box\n{\n\tmargin: -1px -1px 0 0;\n}\ndiv.tab-container > center > table.table-default,\n#table-crafting-recipe,\n#table-brewing-recipe,\n#table-magic-recipe\n{\n\twidth: calc(100% - 1px);\n}\nul.settings-container,\n#tab-container-crafting .settings-container\n{\n\tmargin: 0;\n}\ndiv.tab-container > br:last-child,\n#tab-sub-container-crafting + br,\n#tab-sub-container-woodcutting + br,\n#tab-sub-container-farming + br,\n#tab-sub-container-brewing + br,\n#tab-sub-container-spells > br,\n#tab-container-shop > br:last-child\n{\n\tdisplay: none;\n}\n\ndiv.side-by-side > div\n{\n\tmargin: 0 !important;\n\twidth: 50%;\n}\n.side-by-side h1.container-title\n{\n\tmargin: 2px;\n}\n.side-by-side h1.container-title + br,\n.side-by-side h1.container-title + br + br,\n.side-by-side input[type=\"image\"] + br,\n#hiscores-table-ingame + br\n{\n\tdisplay: none;\n}\n\ndiv.farming-patch,\ndiv.farming-patch-locked\n{\n\tborder-width: 0;\n\tmargin: 0;\n}\n\n#combat-table-area\n{\n\tborder-width: 0;\n}\n#combat-table-area > tbody > tr > td\n{\n\tborder-width: 0;\n\tborder-right-width: 1px;\n}\n#combat-table-area > tbody > tr > td:last-child\n{\n\tborder-right-width: 0;\n}\n#combat-table-area span.large-button,\n#combat-table-area span.medium-button\n{\n\tmargin: 2px 2px 4px;\n}\n#combat-loot-tables\n{\n\tmargin-top: -3px;\n}\n#combat-loot-tables > table.hiscores-table\n{\n\tmargin: 2px -1px 0 0;\n\twidth: calc(33.33% - 4px);\n}\n#combat-loot-tables > div[style*=\"both\"]\n{\n\theight: 0px;\n}\n\t\t");
  8748. var farmingTab = document.getElementById('tab-container-farming');
  8749. if (farmingTab)
  8750. {
  8751. removeWhitespaceChildNodes(farmingTab);
  8752. }
  8753. var combatSubTab = document.getElementById('tab-sub-container-combat');
  8754. if (combatSubTab)
  8755. {
  8756. removeWhitespaceChildNodes(combatSubTab);
  8757. }
  8758.  
  8759. function checkSetting(init)
  8760. {
  8761. if (init === void 0)
  8762. {
  8763. init = false;
  8764. }
  8765. var show = settings.get(settings.KEY.useEfficiencyStyle);
  8766. document.body.classList[show ? 'add' : 'remove'](EFFICIENCY_CLASS);
  8767. if (init)
  8768. {
  8769. settings.observe(settings.KEY.useEfficiencyStyle, function ()
  8770. {
  8771. return checkSetting();
  8772. });
  8773. }
  8774. }
  8775. checkSetting(true);
  8776. }
  8777.  
  8778. function hardcore()
  8779. {
  8780. if (win.isHardcore != 1)
  8781. {
  8782. return;
  8783. }
  8784. addStyle("\nspan#shop-giant-button-playermarket\n{\n\tbackground-color: gray;\n\tbackground-image: none;\n\tcursor: not-allowed;\n}\n\t\t");
  8785. var marketBtn = document.getElementById('shop-giant-button-playermarket');
  8786. if (marketBtn)
  8787. {
  8788. marketBtn.removeAttribute('onclick');
  8789. marketBtn.setAttribute('title', 'The player market is disabled for hardcore accounts');
  8790. }
  8791. }
  8792.  
  8793. function smallScreen()
  8794. {
  8795. addStyle("\ntable.top-links\n{\n\tz-index: 10;\n}\n\t\t");
  8796. }
  8797.  
  8798. function init()
  8799. {
  8800. tweakOil();
  8801. tweakSelection();
  8802. tweakStardust();
  8803. tweakSkillLevelText();
  8804. tweakFightDialog();
  8805. addAdditionalSkillBars();
  8806. highlightCookinglevel();
  8807. amountStyle();
  8808. efficiency();
  8809. hardcore();
  8810. smallScreen();
  8811. }
  8812. styleTweaks.init = init;
  8813. })(styleTweaks || (styleTweaks = {}));
  8814.  
  8815. /**
  8816. * add ingame notification boxes
  8817. */
  8818. var notifBoxes;
  8819. (function (notifBoxes)
  8820. {
  8821. notifBoxes.name = 'notifBoxes';
  8822.  
  8823. function addNotifBox(imageKey, itemKey, showFront)
  8824. {
  8825. if (itemKey === void 0)
  8826. {
  8827. itemKey = null;
  8828. }
  8829. if (showFront === void 0)
  8830. {
  8831. showFront = false;
  8832. }
  8833. var notifBox = document.createElement('span');
  8834. notifBox.className = 'notif-box';
  8835. notifBox.id = 'notification-static-' + imageKey;
  8836. notifBox.style.display = 'none';
  8837. if (showFront)
  8838. {
  8839. notifBox.style.cssFloat = 'left';
  8840. }
  8841. notifBox.innerHTML = "<img src=\"images/" + imageKey + ".png\" class=\"image-icon-50\" id=\"notif-" + imageKey + "-img\">";
  8842. if (itemKey != null)
  8843. {
  8844. notifBox.innerHTML += "<span data-item-display=\"" + itemKey + "\" style=\"margin-left: 10px;\"></span>";
  8845. }
  8846. var notifArea = document.getElementById('notifaction-area');
  8847. if (notifArea)
  8848. {
  8849. notifArea.appendChild(notifBox);
  8850. }
  8851. return notifBox;
  8852. }
  8853.  
  8854. function addWorker()
  8855. {
  8856. var notifBox = addNotifBox('workers', null, true);
  8857.  
  8858. function setVisibility()
  8859. {
  8860. var show = win.workersTimer === 1 && settings.get(settings.KEY.useWorkerNotif);
  8861. notifBox.style.display = show ? '' : 'none';
  8862. }
  8863. setVisibility();
  8864. observer.addTick(function ()
  8865. {
  8866. return setVisibility();
  8867. });
  8868. }
  8869. function addFaradoxCrystal()
  8870. {
  8871. var notifBox = addNotifBox('faradoxsLair/tombIcon', null, true);
  8872.  
  8873. function setVisibility()
  8874. {
  8875. var show = win.tombCd == 0 && quest16 == -1 && settings.get(settings.KEY.useTombNotif);
  8876. notifBox.style.display = show ? '' : 'none';
  8877. }
  8878. setVisibility();
  8879. observer.addTick(function ()
  8880. {
  8881. return setVisibility();
  8882. });
  8883. }
  8884. function addBobsUncle()
  8885. {
  8886. var notifBox = addNotifBox('bobsUncle', null, true);
  8887.  
  8888. function setVisibility()
  8889. {
  8890. var show = win.farmingPatchStage7 == 4 && settings.get(settings.KEY.useBobsUncleNotif);
  8891. notifBox.style.display = show ? '' : 'none';
  8892. }
  8893. setVisibility();
  8894. observer.addTick(function ()
  8895. {
  8896. return setVisibility();
  8897. });
  8898. }
  8899. function init()
  8900. {
  8901. addStyle("\n#notifaction-area\n{\n\tpadding: 5px 0;\n}\nspan.notif-box\n{\n\tfont-size: 1rem;\n\tmargin: 0;\n\tmargin-right: 5px;\n}\ntable.tab-bar\n{\n\tmargin-top: 0;\n}\nspan.notif-box,\ntable.tab-bar td\n{\n\tborder-color: gray;\n}\nspan.notif-box[id^=\"notification-static-\"]\n{\n\tbackground: linear-gradient(rgb(22, 22, 24), rgb(72, 171, 50));\n}\n\t\t");
  8902. // remove pure text nodes
  8903. var notifArea = document.getElementById('notifaction-area');
  8904. if (notifArea)
  8905. {
  8906. removeWhitespaceChildNodes(notifArea);
  8907. }
  8908. addWorker();
  8909. addBobsUncle();
  8910. addFaradoxCrystal();
  8911. }
  8912. notifBoxes.init = init;
  8913. })(notifBoxes || (notifBoxes = {}));
  8914.  
  8915.  
  8916. /**
  8917. * extend market
  8918. */
  8919. var market;
  8920. (function (market)
  8921. {
  8922. market.name = 'market';
  8923. // max limit age: 5min
  8924. var MAX_LIMIT_AGE = 5 * 60 * 1e3;
  8925. var PRICE_HISTORY_KEY = 'priceHistory';
  8926. // restrict the size of the history of each item to 2000 entries (for a number comparison: 1 entry per minute, would result in 1440 entries per day)
  8927. var MAX_ENTRIES_PER_ITEM = 2e3;
  8928. var SYNC_URL_REGEX = /^(?:https?:\/\/)?(?:(?:www\.)?myjson\.com\/|api\.myjson\.com\/bins\/)([^\/]+)$/i;
  8929. var detectedTedsUIOnce = false;
  8930.  
  8931. function detectTedsUI()
  8932. {
  8933. return detectedTedsUIOnce = detectedTedsUIOnce || typeof win.changeSetting === 'function';
  8934. }
  8935. market.detectTedsUI = detectTedsUI;
  8936. var priceHistory = store.has(PRICE_HISTORY_KEY) ? store.get(PRICE_HISTORY_KEY) :
  8937. {};
  8938. var getItemColor = function (H, S, L)
  8939. {
  8940. return [
  8941. "hsl(" + H + ", " + S + "%, " + L + "%)"
  8942. , "hsl(" + H + ", " + S + "%, " + (L < 35 ? L + 35 : L - 35) + "%)"
  8943. ];
  8944. };
  8945. var itemColor = {
  8946. 'blewitMushroom': getItemColor(255, 100, 78)
  8947. , 'bronzeBar': getItemColor(39, 100, 46)
  8948. , 'crystalLeaf': getItemColor(226, 100, 50)
  8949. , 'diamond': getItemColor(186, 76, 82)
  8950. , 'dottedGreenLeaf': getItemColor(92, 63, 19)
  8951. , 'emerald': getItemColor(110, 100, 48)
  8952. , 'goldLeaf': getItemColor(50, 100, 50)
  8953. , 'goldBar': getItemColor(54, 100, 46)
  8954. , 'greenLeaf': getItemColor(92, 63, 28)
  8955. , 'ironBar': getItemColor(44, 11, 46)
  8956. , 'limeLeaf': getItemColor(110, 72, 40)
  8957. , 'promethiumBar': getItemColor(354, 81, 46)
  8958. , 'redMushroom': getItemColor(0, 83, 48)
  8959. , 'ruby': getItemColor(5, 87, 45)
  8960. , 'sapphire': getItemColor(197, 100, 32)
  8961. , 'shrimp': getItemColor(17, 88, 50)
  8962. , 'silverBar': getItemColor(0, 0, 74)
  8963. , 'snapegrass': getItemColor(120, 99, 42)
  8964. , 'stardust': getItemColor(37, 100, 50)
  8965. , 'strangeLeaf': getItemColor(195, 100, 40)
  8966. };
  8967. // use ambassadors to name the categories
  8968. var categoryAmbassador2CategoryName = {
  8969. 'stone': 'Ores' // 0
  8970. , 'emptyChisel': 'Crystals' // 1
  8971. , 'bronzeBar': 'Bars' // 2
  8972. , 'dottedGreenLeafSeeds': 'Seeds' // 3
  8973. , 'logs': 'Logs' // 4
  8974. , 'dottedGreenLeaf': 'Ingredients' // 5
  8975. , 'rawShrimp': 'Fish' // 6
  8976. , 'shrimp': 'Food' // 7
  8977. , 'stinger': 'Equipment' // 8
  8978. , 'promethiumHelmetMould': 'Mould' // 9
  8979. , 'essence': 'Magic' // 10
  8980. , 'blueFishingRodOrb': 'Orbs' // 11
  8981. , 'stardust': 'Other' // 12
  8982. };
  8983. var item2Category = new Map();
  8984. var category2Name = new Map();
  8985. var item2Resolver = new Map();
  8986. var itemLimits = new Map();
  8987. var offerPerItem = new Map();
  8988. var offerList = new Array();
  8989. var lastSyncValue = '{}';
  8990.  
  8991. function getSyncUrl()
  8992. {
  8993. if (!settings.get(settings.KEY.syncPriceHistory))
  8994. {
  8995. return null;
  8996. }
  8997. var url = settings.getSub(settings.KEY.syncPriceHistory, 'url');
  8998. var match = url.match(SYNC_URL_REGEX);
  8999. if (!match)
  9000. {
  9001. console.error('URL "' + url + '" does not match the expected pattern: ' + SYNC_URL_REGEX.source);
  9002. return null;
  9003. }
  9004. return 'https://api.myjson.com/bins/' + match[1];
  9005. }
  9006.  
  9007. function integratePriceData(data, responseText)
  9008. {
  9009. var changed = recIntegrate(data, priceHistory);
  9010. lastSyncValue = responseText;
  9011. return changed;
  9012.  
  9013. function recIntegrate(source, target)
  9014. {
  9015. var changed = false;
  9016. if (typeof source !== typeof target)
  9017. {
  9018. console.error('Different data types. Could not integrate data into local price history.\nsource: ' + JSON.stringify(source) + '\ntarget: ' + JSON.stringify(target));
  9019. }
  9020. else if (typeof source === 'object')
  9021. {
  9022. for (var key in source)
  9023. {
  9024. if (source.hasOwnProperty(key))
  9025. {
  9026. if (!target.hasOwnProperty(key))
  9027. {
  9028. target[key] = source[key];
  9029. changed = true;
  9030. }
  9031. else if (recIntegrate(source[key], target[key]))
  9032. {
  9033. changed = true;
  9034. }
  9035. }
  9036. }
  9037. }
  9038. else
  9039. {
  9040. // do nothing and prefer the local value
  9041. }
  9042. return changed;
  9043. }
  9044. }
  9045.  
  9046. function loadPriceHistory()
  9047. {
  9048. var url = getSyncUrl();
  9049. if (url)
  9050. {
  9051. win.$.get(url, function (data, textStatus, jqXHR)
  9052. {
  9053. if (integratePriceData(data, jqXHR.responseText))
  9054. {
  9055. savePriceHistory(true);
  9056. }
  9057. });
  9058. }
  9059. }
  9060.  
  9061. function savePriceHistory(forceWrite)
  9062. {
  9063. if (forceWrite === void 0)
  9064. {
  9065. forceWrite = false;
  9066. }
  9067. for (var itemKey in priceHistory)
  9068. {
  9069. var history_1 = priceHistory[itemKey];
  9070. var timestampList = Object.keys(history_1).sort();
  9071. var i = 0;
  9072. for (var _i = 0, timestampList_1 = timestampList; _i < timestampList_1.length; _i++)
  9073. {
  9074. var timestamp = timestampList_1[_i];
  9075. i++;
  9076. if (i > MAX_ENTRIES_PER_ITEM)
  9077. {
  9078. delete history_1[timestamp];
  9079. }
  9080. }
  9081. }
  9082. store.set(PRICE_HISTORY_KEY, priceHistory);
  9083. var url = getSyncUrl();
  9084. if (url)
  9085. {
  9086. var doPut_1 = function ()
  9087. {
  9088. $.ajax(
  9089. {
  9090. url: url
  9091. , type: 'PUT'
  9092. , data: JSON.stringify(priceHistory)
  9093. , contentType: 'application/json; charset=utf-8'
  9094. , dataType: 'json'
  9095. , success: function (data, textStatus, jqXHR)
  9096. {
  9097. lastSyncValue = jqXHR.responseText;
  9098. }
  9099. });
  9100. };
  9101. if (forceWrite === true)
  9102. {
  9103. doPut_1();
  9104. }
  9105. else
  9106. {
  9107. win.$.get(url, function (data, textStatus, jqXHR)
  9108. {
  9109. if (lastSyncValue !== jqXHR.responseText)
  9110. {
  9111. integratePriceData(data, jqXHR.responseText);
  9112. }
  9113. doPut_1();
  9114. });
  9115. }
  9116. }
  9117. }
  9118.  
  9119. function processMarketData(data)
  9120. {
  9121. var nowKey = now();
  9122. offerPerItem = new Map();
  9123. offerList = new Array();
  9124. if (data != 'NONE')
  9125. {
  9126. offerList = data.split(';').map(function (offerData)
  9127. {
  9128. var values = offerData.split('~');
  9129. var itemId = Number(values[1]);
  9130. var itemKey = win.jsItemArray[itemId];
  9131. var itemName = key2Name(itemKey);
  9132. var categoryId = item2Category.has(itemKey) ? item2Category.get(itemKey) : -1;
  9133. var offer = {
  9134. offerId: Number(values[0])
  9135. , itemId: itemId
  9136. , itemKey: itemKey
  9137. , itemName: itemName
  9138. , categoryId: categoryId
  9139. , amount: Number(values[2])
  9140. , price: Number(values[3])
  9141. , timeLeft: values[4]
  9142. , playerId: Number(values[5])
  9143. };
  9144. if (!offerPerItem.has(itemKey))
  9145. {
  9146. offerPerItem.set(itemKey, []);
  9147. }
  9148. offerPerItem.get(itemKey).push(offer);
  9149. var history = priceHistory[itemKey];
  9150. if (!history)
  9151. {
  9152. history = {};
  9153. priceHistory[itemKey] = history;
  9154. }
  9155. if (!history.hasOwnProperty(nowKey)
  9156. || history[nowKey] > offer.price)
  9157. {
  9158. history[nowKey] = offer.price;
  9159. }
  9160. return offer;
  9161. });
  9162. }
  9163. savePriceHistory();
  9164. }
  9165.  
  9166. function processItemLimits(itemKey, lowerLimit, upperLimit)
  9167. {
  9168. var limit = {
  9169. timestamp: now()
  9170. , min: lowerLimit
  9171. , max: upperLimit
  9172. };
  9173. itemLimits.set(itemKey, limit);
  9174. if (item2Resolver.has(itemKey))
  9175. {
  9176. var limitArr_1 = [lowerLimit, upperLimit];
  9177. item2Resolver.get(itemKey).forEach(function (resolve)
  9178. {
  9179. return resolve(limitArr_1);
  9180. });
  9181. item2Resolver.delete(itemKey);
  9182. return false;
  9183. }
  9184. return true;
  9185. }
  9186.  
  9187. function showOfferCancelCooldown()
  9188. {
  9189. if (detectTedsUI())
  9190. {
  9191. return;
  9192. }
  9193. 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");
  9194.  
  9195. function slotCooldown(i, init)
  9196. {
  9197. if (init === void 0)
  9198. {
  9199. init = false;
  9200. }
  9201. var cooldownKey = 'marketCancelCooldownSlot' + i;
  9202. var btn = document.getElementById('market-slot-' + i + '-cancel-btn');
  9203. if (btn)
  9204. {
  9205. btn.dataset.cooldown = detectTedsUI() ? '0' : getGameValue(cooldownKey).toString();
  9206. }
  9207. if (init)
  9208. {
  9209. observer.add(cooldownKey, function ()
  9210. {
  9211. return slotCooldown(i);
  9212. });
  9213. }
  9214. }
  9215. for (var i = 1; i <= 3; i++)
  9216. {
  9217. slotCooldown(i, true);
  9218. }
  9219. }
  9220.  
  9221. function addExtraBtns()
  9222. {
  9223. var browseBtn = document.querySelector('.market-browse-button');
  9224. if (!browseBtn)
  9225. {
  9226. return;
  9227. }
  9228. var HISTORY_CLASS = 'local-history';
  9229. var paddingLeft = 30 + 42;
  9230. var paddingRight = 30;
  9231. addStyle("\ncenter > span.market-browse-button,\n#ted-market-ui > span.market-browse-button\n{\n\tposition: relative;\n}\ncenter > span.market-browse-button\n{\n\tpadding-left: " + paddingLeft + "px;\n\tpadding-right: " + paddingRight + "px;\n}\nspan.market-browse-button > span.market-browse-button\n{\n\tpadding: 10px 20px;\n\tposition: absolute;\n\ttop: -1px;\n\tbottom: -1px;\n}\n/*\n*/\nspan.market-browse-button > span.market-browse-button." + HISTORY_CLASS + "\n{\n\tleft: -1px;\n}\nspan.market-browse-button > span.market-browse-button::before\n{\n\tbackground-color: transparent;\n\tbackground-position: center;\n\tbackground-repeat: no-repeat;\n\tbackground-size: 30px;\n\tcontent: '';\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\tbottom: 0;\n\tright: 0;\n}\n/*\n*/\nspan.market-browse-button." + HISTORY_CLASS + "::before\n{\n\tbackground-image: " + icons.getMd(icons.CHART_LINE) + ";\n}\n/*\n*/\n#ted-market-ui > span.market-browse-button > span.market-browse-button." + HISTORY_CLASS + "\n{\n\tborder-left: 0;\n\tleft: 0;\n}\n\t\t");
  9232. var historyBtn = document.createElement('span');
  9233. historyBtn.className = 'market-browse-button ' + HISTORY_CLASS;
  9234. browseBtn.appendChild(historyBtn);
  9235. var historyItemKey = null;
  9236. var _postItemDialogue = win.postItemDialogue;
  9237. win.postItemDialogue = function (offerTypeEl, itemName, inputEl)
  9238. {
  9239. historyItemKey = itemName;
  9240. _postItemDialogue(offerTypeEl, itemName, inputEl);
  9241. };
  9242. var PRICE_HISTORY_DIALOG_ID = 'dialog-price-history';
  9243. var PRICE_HISTORY_ID = 'price-history';
  9244. var PRICE_HISTORY_ITEM_SELECT_ID = 'price-history-item-select';
  9245. addStyle("\n#" + PRICE_HISTORY_DIALOG_ID + "\n{\n\tdisplay: flex;\n\tflex-direction: column;\n}\n#" + PRICE_HISTORY_ID + "\n{\n\tflex-grow: 1;\n\tposition: relative;\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n#" + PRICE_HISTORY_ID + " > div\n{\n\tposition: absolute !important;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n\tbottom: 0;\n}\n#" + PRICE_HISTORY_ID + " .anychart-credits\n{\n\tdisplay: none;\n}\n\t\t");
  9246. var dialog = document.createElement('dialog');
  9247. dialog.id = PRICE_HISTORY_DIALOG_ID;
  9248. dialog.style.display = 'none';
  9249. dialog.style.overflowX = 'hidden';
  9250. dialog.innerHTML = "\n\t\t<select id=\"" + PRICE_HISTORY_ITEM_SELECT_ID + "\" multiple=\"multiple\" data-placeholder=\"Add items\" style=\"width: 100%\"></select>\n\t\t<div id=\"" + PRICE_HISTORY_ID + "\"></div>\n\t\t";
  9251. document.body.appendChild(dialog);
  9252. var itemSelect = document.getElementById(PRICE_HISTORY_ITEM_SELECT_ID);
  9253. var $itemSelect = win.$(itemSelect);
  9254.  
  9255. function loadScripts(urlList, callback)
  9256. {
  9257. var url = urlList[0];
  9258. if (!url)
  9259. {
  9260. callback && callback();
  9261. return;
  9262. }
  9263. var script = document.createElement('script');
  9264. script.src = url;
  9265. script.onload = function ()
  9266. {
  9267. return loadScripts(urlList.slice(1), callback);
  9268. };
  9269. document.head.appendChild(script);
  9270. }
  9271. var monthArray = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  9272. var lastHistoryItemKey;
  9273. var itemKey2SeriesId = {};
  9274. var chart;
  9275. var stage;
  9276. // add style for select2
  9277. var style = document.createElement('link');
  9278. style.rel = 'stylesheet';
  9279. style.href = 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css';
  9280. document.head.appendChild(style);
  9281. loadScripts([
  9282. 'https://cdn.anychart.com/js/7.14.3/anychart.min.js'
  9283. , 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
  9284. ], function ()
  9285. {
  9286. chart = win.anychart.area();
  9287. // pass the container id, chart will be displayed there
  9288. stage = anychart.graphics.create(PRICE_HISTORY_ID);
  9289. chart.container(stage);
  9290. var tooltip = chart.tooltip();
  9291. tooltip.displayMode('union');
  9292. tooltip.format(passThis(function (context)
  9293. {
  9294. var name = context.seriesName || 'Price';
  9295. return name + ': ' + (isNaN(context.value) ? '-' : format.number(context.value));
  9296. }));
  9297. tooltip.titleFormat(passThis(function (context)
  9298. {
  9299. var d = new Date(context.x);
  9300. return monthArray[d.getMonth()] + ' ' + d.getDate() + ' @ ' + zeroPadLeft(d.getHours()) + ':' + zeroPadLeft(d.getMinutes()) + ':' + zeroPadLeft(d.getSeconds());
  9301. }));
  9302. var valueAxis = chart.yAxis();
  9303. valueAxis.title().text('Price').enabled(true);
  9304. valueAxis.labels().format(passThis(function (context)
  9305. {
  9306. return format.number(context.value);
  9307. }));
  9308. var timeAxis = chart.xAxis();
  9309. timeAxis.labels().format(passThis(function (context)
  9310. {
  9311. var d = new Date(context.tickValue);
  9312. return d.getDate() + '. ' + monthArray[d.getMonth()];
  9313. }));
  9314. var timeScale = win.anychart.scales.dateTime();
  9315. var ticks = timeScale.ticks();
  9316. ticks.interval(0, 0, 1);
  9317. chart.xScale(timeScale);
  9318. var timeScroller = chart.xScroller();
  9319. timeScroller.enabled(true);
  9320. chart.animation(true, 300);
  9321. chart.legend(true);
  9322. });
  9323. historyBtn.addEventListener('click', function (event)
  9324. {
  9325. event.preventDefault();
  9326. event.stopPropagation();
  9327. var height = Math.floor(.66 * window.innerHeight);
  9328. var width = Math.min(Math.floor(.66 * window.innerWidth), window.innerWidth - 30);
  9329. win.$(dialog).dialog(
  9330. {
  9331. title: 'Price history from local data'
  9332. , height: height
  9333. , width: width
  9334. });
  9335. dialog.style.height = (height) + 'px';
  9336. var itemKeyList = Object.keys(priceHistory).sort();
  9337. itemSelect.innerHTML = "";
  9338. var category2OptGroup = {};
  9339.  
  9340. function ensureOptGroup(categoryId)
  9341. {
  9342. var optGroup = category2OptGroup[categoryId];
  9343. if (!optGroup)
  9344. {
  9345. optGroup = document.createElement('optgroup');
  9346. optGroup.label = category2Name.get(categoryId) || 'Stuff';
  9347. itemSelect.appendChild(optGroup);
  9348. category2OptGroup[categoryId] = optGroup;
  9349. }
  9350. return optGroup;
  9351. }
  9352. var categoryList = Array.from(category2Name.keys()).map(function (id)
  9353. {
  9354. return Number(id);
  9355. }).sort();
  9356. for (var _i = 0, categoryList_1 = categoryList; _i < categoryList_1.length; _i++)
  9357. {
  9358. var categoryId = categoryList_1[_i];
  9359. ensureOptGroup(categoryId);
  9360. }
  9361. var itemKey2EnabledFn = {};
  9362.  
  9363. function replaceEnabled(itemKey, series)
  9364. {
  9365. var _enabled = series.enabled.bind(series);
  9366. itemKey2EnabledFn[itemKey] = _enabled;
  9367. series.enabled = function (value)
  9368. {
  9369. if (value !== undefined)
  9370. {
  9371. var itemList = $itemSelect.val();
  9372. var index = itemList.indexOf(itemKey);
  9373. if (index !== -1)
  9374. {
  9375. itemList.splice(index, 1);
  9376. }
  9377. else
  9378. {
  9379. itemList.push(itemKey);
  9380. }
  9381. $itemSelect.val(itemList).trigger('change');
  9382. }
  9383. return _enabled(value);
  9384. };
  9385. }
  9386. var min = Number.MAX_SAFE_INTEGER;
  9387. var max = 0;
  9388. var enabledSeriesList = [];
  9389. var _loop_1 = function (itemKey)
  9390. {
  9391. if (!itemColor[itemKey])
  9392. {
  9393. var baseColor = colorGenerator.getRandom(
  9394. {
  9395. format: 'hslArray'
  9396. });
  9397. var borderColor = baseColor.slice(0);
  9398. if (borderColor[2] < 35)
  9399. {
  9400. borderColor[2] += 35;
  9401. }
  9402. else
  9403. {
  9404. borderColor[2] -= 35;
  9405. }
  9406. itemColor[itemKey] = [
  9407. "hsl(" + baseColor[0] + ", " + baseColor[1] + "%, " + baseColor[2] + "%)"
  9408. , "hsl(" + borderColor[0] + ", " + borderColor[1] + "%, " + borderColor[2] + "%)"
  9409. ];
  9410. }
  9411. var history_2 = priceHistory[itemKey];
  9412. var keyList = Object.keys(history_2).sort();
  9413. var data = keyList
  9414. .map(function (n)
  9415. {
  9416. return ([
  9417. Number(n)
  9418. , history_2[n]
  9419. ]);
  9420. });
  9421. min = Math.min(Number(keyList[0]), min);
  9422. max = Math.max(Number(keyList[keyList.length - 1]), max);
  9423. var id = itemKey2SeriesId[itemKey];
  9424. var series = void 0;
  9425. if (id != null)
  9426. {
  9427. series = chart.getSeries(id);
  9428. series.data(data);
  9429. }
  9430. else
  9431. {
  9432. var hoverifyColor = function (hslColor)
  9433. {
  9434. return (
  9435. {
  9436. color: hslColor
  9437. , opacity: .8
  9438. });
  9439. };
  9440. series = chart.area(data);
  9441. itemKey2SeriesId[itemKey] = series.id();
  9442. series.name(key2Name(itemKey));
  9443. var bgColor = itemColor[itemKey][0];
  9444. var strokeColor = itemColor[itemKey][1];
  9445. series.fill(bgColor);
  9446. var bgColorHover = hoverifyColor(bgColor);
  9447. series.selectFill(bgColorHover);
  9448. series.hoverFill(bgColorHover);
  9449. series.stroke(strokeColor, 2);
  9450. var strokeColorHover = hoverifyColor(strokeColor);
  9451. series.hoverStroke(strokeColorHover, 2);
  9452. series.selectStroke(strokeColorHover, 2);
  9453. var markerOptions = {
  9454. fill: strokeColor
  9455. , size: 5
  9456. , type: 'circle'
  9457. };
  9458. series.hoverMarkers(markerOptions);
  9459. series.selectMarkers(markerOptions);
  9460. replaceEnabled(itemKey, series);
  9461. }
  9462. if (lastHistoryItemKey !== historyItemKey)
  9463. {
  9464. if (itemKey === historyItemKey)
  9465. {
  9466. enabledSeriesList.push(series);
  9467. }
  9468. series.enabled(false);
  9469. }
  9470. var categoryId = item2Category.has(itemKey) ? item2Category.get(itemKey) : -1;
  9471. var optGroup = ensureOptGroup(categoryId);
  9472. var option = document.createElement('option');
  9473. option.value = itemKey;
  9474. option.textContent = key2Name(itemKey);
  9475. optGroup.appendChild(option);
  9476. };
  9477. for (var _a = 0, itemKeyList_1 = itemKeyList; _a < itemKeyList_1.length; _a++)
  9478. {
  9479. var itemKey = itemKeyList_1[_a];
  9480. _loop_1(itemKey);
  9481. }
  9482. stage.listenOnce('renderfinish', function ()
  9483. {
  9484. enabledSeriesList.forEach(function (series)
  9485. {
  9486. return series.enabled(true);
  9487. });
  9488. });
  9489. var timeScale = chart.xScale();
  9490. timeScale.minimum(min);
  9491. timeScale.maximum(max);
  9492. var timeZoom = chart.xZoom();
  9493. var threeDaysLong = 3 * 24 * 60 * 60 * 1e3;
  9494. timeZoom.setToValues(Math.max(max - threeDaysLong, min), max);
  9495. // call the chart draw() method to initiate chart display
  9496. chart.draw(true);
  9497. // init item select
  9498. if ($itemSelect.data('select2'))
  9499. {
  9500. $itemSelect.select2('destroy');
  9501. }
  9502. $itemSelect.select2();
  9503.  
  9504. function getEnabledFn(event)
  9505. {
  9506. var data = event.params.data;
  9507. var itemKey = data.id;
  9508. var enabledFn = itemKey2EnabledFn[itemKey];
  9509. if (enabledFn)
  9510. {
  9511. return enabledFn;
  9512. }
  9513. else
  9514. {
  9515. var id = itemKey2SeriesId[itemKey];
  9516. var series = chart.getSeries(id);
  9517. return series.enabled.bind(series);
  9518. }
  9519. }
  9520. $itemSelect.on('select2:select', function (event)
  9521. {
  9522. getEnabledFn(event)(true);
  9523. });
  9524. $itemSelect.on('select2:unselect', function (event)
  9525. {
  9526. getEnabledFn(event)(false);
  9527. // close select menu when it was closed before an element has been removed
  9528. var openBefore = $itemSelect.data('select2').$container.hasClass('select2-container--open');
  9529. setTimeout(function ()
  9530. {
  9531. if (!openBefore && $itemSelect.data('select2').$container.hasClass('select2-container--open'))
  9532. {
  9533. $itemSelect.select2('close');
  9534. }
  9535. });
  9536. });
  9537. lastHistoryItemKey = historyItemKey;
  9538. });
  9539. }
  9540. var categoryList = [-1];
  9541. var itemListPerCategory = new Map();
  9542.  
  9543. function improveOfferList()
  9544. {
  9545. var itemArea = document.getElementById('dialogue-market-items-area');
  9546. if (itemArea)
  9547. {
  9548. var children = itemArea.children;
  9549. for (var i = 1; i < children.length; i++)
  9550. {
  9551. var categoryId = i - 1;
  9552. categoryList.push(categoryId);
  9553. var box = children.item(i);
  9554. var inputs = box.children;
  9555. for (var j = 0; j < inputs.length; j++)
  9556. {
  9557. var match = inputs.item(j).src.match(/images\/([^\/]+)\.(?:png|jpe?g|gif)/);
  9558. if (!match)
  9559. {
  9560. continue;
  9561. }
  9562. var itemKey = match[1];
  9563. item2Category.set(itemKey, categoryId);
  9564. if (categoryAmbassador2CategoryName[itemKey])
  9565. {
  9566. category2Name.set(categoryId, categoryAmbassador2CategoryName[itemKey]);
  9567. }
  9568. if (!itemListPerCategory.has(categoryId))
  9569. {
  9570. itemListPerCategory.set(categoryId, []);
  9571. }
  9572. itemListPerCategory.get(categoryId).push(itemKey);
  9573. }
  9574. }
  9575. }
  9576. }
  9577.  
  9578. function getItemLimit(itemKey)
  9579. {
  9580. // TODO: combine list of offers with min/max-boundries
  9581. var limit = itemLimits.get(itemKey);
  9582. if (limit && limit.timestamp > now() - MAX_LIMIT_AGE)
  9583. {
  9584. return Promise.resolve([limit.min, limit.max]);
  9585. }
  9586. else if (!win.jsTradalbeItems.hasOwnProperty(itemKey))
  9587. {
  9588. return Promise.resolve([0, 0]);
  9589. }
  9590. return new Promise(function (resolve, reject)
  9591. {
  9592. win.postItemDialogue(
  9593. {
  9594. value: 'sell'
  9595. }, itemKey, null);
  9596. if (!item2Resolver.has(itemKey))
  9597. {
  9598. item2Resolver.set(itemKey, []);
  9599. }
  9600. item2Resolver.get(itemKey).push(resolve);
  9601. setTimeout(function ()
  9602. {
  9603. return reject(new Error('Request timed out'));
  9604. }, 30e3);
  9605. });
  9606. }
  9607.  
  9608. function calcMarketValue(items)
  9609. {
  9610. var itemKeyList = Object.keys(items);
  9611. return Promise.all(itemKeyList.map(function (key)
  9612. {
  9613. return getItemLimit(key);
  9614. }))
  9615. .then(function (limitList)
  9616. {
  9617. var sum = [0, 0];
  9618. for (var i = 0; i < itemKeyList.length; i++)
  9619. {
  9620. var amount = items[itemKeyList[i]];
  9621. var limit = limitList[i];
  9622. sum[0] += amount * limit[0];
  9623. sum[1] += amount * limit[1];
  9624. }
  9625. return sum;
  9626. });
  9627. }
  9628. market.calcMarketValue = calcMarketValue;
  9629.  
  9630. function init()
  9631. {
  9632. showOfferCancelCooldown();
  9633. addExtraBtns();
  9634. improveOfferList();
  9635. var _chosenPostItemDialogue = win.chosenPostItemDialogue;
  9636. win.chosenPostItemDialogue = function (itemName, lowerLimit, upperLimit)
  9637. {
  9638. if (processItemLimits(itemName, Number(lowerLimit), Number(upperLimit)))
  9639. {
  9640. _chosenPostItemDialogue(itemName, lowerLimit, upperLimit);
  9641. }
  9642. };
  9643. var _addToPlayerMarket = win.addToPlayerMarket;
  9644. win.addToPlayerMarket = function (data)
  9645. {
  9646. processMarketData(data);
  9647. _addToPlayerMarket(data);
  9648. };
  9649. loadPriceHistory();
  9650. // delay (debounce) sending the request for 3s
  9651. var startDebouncedRequest = debounce(function ()
  9652. {
  9653. return loadPriceHistory();
  9654. }, 3e3);
  9655. settings.observe(settings.KEY.syncPriceHistory, function ()
  9656. {
  9657. return startDebouncedRequest();
  9658. });
  9659. settings.observeSub(settings.KEY.syncPriceHistory, 'url', function ()
  9660. {
  9661. return startDebouncedRequest();
  9662. });
  9663. }
  9664. market.init = init;
  9665. })(market || (market = {}));
  9666.  
  9667. var combat;
  9668. (function (combat)
  9669. {
  9670. combat.name = 'combat';
  9671. var LOOT_TABLE_URL = '/wiki/combat.php';
  9672. var COMBAT_LOOT_TABLES_ID = 'combat-loot-tables';
  9673. var CAT_2_NAME = {
  9674. 'always': 'Always'
  9675. , 'common': 'Common'
  9676. , 'uncommon': 'Uncommon'
  9677. , 'rare': 'Rare'
  9678. , 'veryrare': 'Very Rare'
  9679. };
  9680. var lootInfoInitialized = false;
  9681. var lootInfo = {};
  9682.  
  9683. function readLootTable(table)
  9684. {
  9685. var monsterImg = table.getElementsByTagName('img').item(0);
  9686. var src = monsterImg.getAttribute('src') || '';
  9687. var monsterId = src.replace(/.+npc\/(\d+)\.png$/, '$1');
  9688. var info = {
  9689. always: []
  9690. , common: []
  9691. , uncommon: []
  9692. , rare: []
  9693. , veryrare: []
  9694. };
  9695. for (var i = 2; i < table.rows.length; i++)
  9696. {
  9697. var row = table.rows.item(i);
  9698. var match = row.cells.item(0).innerHTML.match(/images\/(.+)\.png/) || row.cells.item(0).innerHTML.match(/images\/(.+)\.gif/);
  9699. if (!match)
  9700. {
  9701. console.error('no item key found:', row.innerHTML);
  9702. continue;
  9703. }
  9704. var itemKey = match[1];
  9705. var amount = row.cells.item(1).textContent || '';
  9706. var rarityCategory = row.cells.item(2).className;
  9707. if (!info.hasOwnProperty(rarityCategory))
  9708. {
  9709. console.error('unknown rarity category:', rarityCategory);
  9710. continue;
  9711. }
  9712. info[rarityCategory].push(
  9713. {
  9714. key: itemKey
  9715. , amount: amount.split(' - ').map(function (s)
  9716. {
  9717. return Number(s.replace(/\D/g, ''));
  9718. })
  9719. });
  9720. }
  9721. lootInfo[monsterId] = info;
  9722. lootInfoInitialized = true;
  9723. }
  9724.  
  9725. function updateLootTableInfo()
  9726. {
  9727. return doGet(LOOT_TABLE_URL)
  9728. .then(function (response)
  9729. {
  9730. var parser = new DOMParser();
  9731. var doc = parser.parseFromString(response, 'text/html');
  9732. var tables = doc.getElementsByTagName('table');
  9733. for (var i = 0; i < tables.length; i++)
  9734. {
  9735. readLootTable(tables.item(i));
  9736. }
  9737. return lootInfo;
  9738. })
  9739. .then(function (info)
  9740. {
  9741. setLootTableTabContent(info);
  9742. });
  9743. }
  9744.  
  9745. function addLootTableTab()
  9746. {
  9747. var subTabContainer = document.getElementById('tab-sub-container-combat');
  9748. var itemContainer = document.getElementById('tab-sub-container-combat-large-btns');
  9749. var afterEl = itemContainer && itemContainer.previousElementSibling;
  9750. if (!subTabContainer || !afterEl)
  9751. {
  9752. return;
  9753. }
  9754. addStyle("\nspan.medium-button.active\n{\n\tbackground: hsla(109, 55%, 43%, 1);\n\tcursor: not-allowed;\n}\n#combat-table-area:not([style$=\"auto;\"]) > tbody > tr > td:last-child\n{\n\twidth: 100%;\n}\n#" + COMBAT_LOOT_TABLES_ID + " td.always\n{\n\tbackground-color: #ccffff;\n}\n#" + COMBAT_LOOT_TABLES_ID + " td.common\n{\n\tbackground-color: #ccffcc;\n}\n#" + COMBAT_LOOT_TABLES_ID + " td.uncommon\n{\n\tbackground-color: #ffffcc;\n}\n#" + COMBAT_LOOT_TABLES_ID + " td.rare\n{\n\tbackground-color: #ffcc99;\n}\n#" + COMBAT_LOOT_TABLES_ID + " td.veryrare\n{\n\tbackground-color: #ff9999;\n}\n\n#" + COMBAT_LOOT_TABLES_ID + " table.hiscores-table\n{\n\tfloat: left;\n\tmargin: 0 10px;\n\twidth: calc(33.3% - 20px);\n}\n#" + COMBAT_LOOT_TABLES_ID + " table.hiscores-table img.image-icon-50\n{\n\twidth: auto;\n}\n\t\t");
  9755. var REFRESH_LOOT_TABLE_ID = 'refresh-loot-table';
  9756. var subTab = document.createElement('span');
  9757. subTab.className = 'large-button';
  9758. subTab.innerHTML = "<img class=\"image-icon-50\" src=\"images/combatDropTable.png\" style=\"filter: grayscale(100%);\">Loot";
  9759. subTab.addEventListener('click', function ()
  9760. {
  9761. var _confirmDialogue = win.confirmDialogue;
  9762. win.confirmDialogue = function () {};
  9763. win.clicksOpenDropTable();
  9764. win.confirmDialogue = _confirmDialogue;
  9765. win.openSubTab('loot');
  9766. });
  9767.  
  9768. function setLootTabVisibility()
  9769. {
  9770. var show = settings.get(settings.KEY.showLootTab);
  9771. subTab.style.display = show ? '' : 'none';
  9772. var dropTableItemBox = document.getElementById('item-box-combatDropTable');
  9773. if (dropTableItemBox)
  9774. {
  9775. dropTableItemBox.style.display = show ? 'none' : '';
  9776. }
  9777. if (show && !lootInfoInitialized)
  9778. {
  9779. updateLootTableInfo();
  9780. }
  9781. }
  9782. setLootTabVisibility();
  9783. settings.observe(settings.KEY.showLootTab, function ()
  9784. {
  9785. return setLootTabVisibility();
  9786. });
  9787. subTabContainer.insertBefore(subTab, afterEl);
  9788. var combatSubTab = document.getElementById('tab-sub-container-combat');
  9789. var equipSubTab = document.getElementById('tab-sub-container-equip');
  9790. var spellsSubTab = document.getElementById('tab-sub-container-spells');
  9791. var subPanelContainer = combatSubTab.parentElement;
  9792. var lootSubTab = document.createElement('div');
  9793. lootSubTab.id = 'tab-sub-container-loot';
  9794. lootSubTab.style.display = 'none';
  9795. lootSubTab.innerHTML = "<span onclick=\"openTab('combat')\" class=\"medium-button\"><img class=\"image-icon-30\" src=\"images/icons/back.png\"> back</span>\n\t\t<span id=\"" + REFRESH_LOOT_TABLE_ID + "\" class=\"medium-button\">refresh</span>\n\t\t<div id=\"" + COMBAT_LOOT_TABLES_ID + "\">Loading...</div>";
  9796. subPanelContainer.appendChild(lootSubTab);
  9797. var refreshBtn = document.getElementById(REFRESH_LOOT_TABLE_ID);
  9798. if (refreshBtn)
  9799. {
  9800. refreshBtn.addEventListener('click', function ()
  9801. {
  9802. if (refreshBtn.classList.contains('active'))
  9803. {
  9804. return;
  9805. }
  9806. refreshBtn.classList.add('active');
  9807. updateLootTableInfo()
  9808. .then(function ()
  9809. {
  9810. return refreshBtn.classList.remove('active');
  9811. })
  9812. .catch(function ()
  9813. {
  9814. return refreshBtn.classList.remove('active');
  9815. });
  9816. });
  9817. }
  9818. var _openSubTab = win.openSubTab;
  9819. win.openSubTab = function (tab)
  9820. {
  9821. combatSubTab.style.display = 'none';
  9822. equipSubTab.style.display = 'none';
  9823. spellsSubTab.style.display = 'none';
  9824. lootSubTab.style.display = 'none';
  9825. _openSubTab(tab);
  9826. if (tab == 'loot')
  9827. {
  9828. lootSubTab.style.display = 'block';
  9829. }
  9830. };
  9831. var _loadDefaultCombatTab = win.loadDefaultCombatTab;
  9832. win.loadDefaultCombatTab = function ()
  9833. {
  9834. _loadDefaultCombatTab();
  9835. lootSubTab.style.display = 'none';
  9836. };
  9837. }
  9838.  
  9839. function setLootTableTabContent(lootInfo)
  9840. {
  9841. var combatTableWrapper = document.getElementById(COMBAT_LOOT_TABLES_ID);
  9842. if (!combatTableWrapper)
  9843. {
  9844. return;
  9845. }
  9846. combatTableWrapper.innerHTML = "";
  9847. for (var monsterId in lootInfo)
  9848. {
  9849. var info = lootInfo[monsterId];
  9850. var monsterNum = Number(monsterId);
  9851. if (monsterNum > 1 && monsterNum % 3 === 1)
  9852. {
  9853. var lineBreak = document.createElement('div');
  9854. lineBreak.style.clear = 'both';
  9855. lineBreak.innerHTML = "<br>";
  9856. combatTableWrapper.appendChild(lineBreak);
  9857. }
  9858. var table = document.createElement('table');
  9859. table.className = 'hiscores-table';
  9860. var imgRow = table.insertRow(-1);
  9861. imgRow.innerHTML = "<td colspan=\"3\">\n\t\t\t\t<img src=\"../images/hero/npc/" + monsterId + ".png\" class=\"image-icon-50\">\n\t\t\t</td>";
  9862. var headerRow = table.insertRow(-1);
  9863. headerRow.innerHTML = "<th>Item</th><th>Amount</th><th>Rarity</th>";
  9864. for (var rarityCategory in info)
  9865. {
  9866. var itemList = info[rarityCategory];
  9867. for (var i = 0; i < itemList.length; i++)
  9868. {
  9869. var item = itemList[i];
  9870. var row = table.insertRow(-1);
  9871. row.innerHTML = "<td><img src=\"../images/" + item.key + ".png\" class=\"image-icon-40\"></td><td>" + item.amount.map(function (n)
  9872. {
  9873. return format.number(n);
  9874. }).join(' - ') + "</td><td class=\"" + rarityCategory + "\">" + CAT_2_NAME[rarityCategory] + "</td>";
  9875. }
  9876. }
  9877. combatTableWrapper.appendChild(table);
  9878. }
  9879. }
  9880.  
  9881. function init()
  9882. {
  9883. addLootTableTab();
  9884. if (settings.get(settings.KEY.showLootTab))
  9885. {
  9886. updateLootTableInfo();
  9887. }
  9888. }
  9889. combat.init = init;
  9890. })(combat || (combat = {}));
  9891.  
  9892. /**
  9893. * farming improvements
  9894. */
  9895. var farming;
  9896. (function (farming)
  9897. {
  9898. farming.name = 'farming';
  9899. var SEED_INFO_REGEX = {
  9900. minLevel: />\s*Level:/
  9901. , stopsDyingLevel: />\s*Stops\s+Dying\s+Level:/
  9902. , bonemeal: />\s*Bonemeal:/
  9903. , woodcuttingLevel: />\s*Woodcutting\s+Level:/
  9904. };
  9905. var seedInfoSpans = {};
  9906. var seedInfo = {};
  9907. var checkInfo = {
  9908. bonemeal: function (amount)
  9909. {
  9910. return amount <= win.bonemeal;
  9911. }
  9912. , minLevel: function (level)
  9913. {
  9914. return level <= win.getLevel(win.farmingXp);
  9915. }
  9916. , stopsDyingLevel: function (level)
  9917. {
  9918. return level <= win.getLevel(win.farmingXp);
  9919. }
  9920. , woodcuttingLevel: function (level)
  9921. {
  9922. return level <= win.getLevel(win.woodcuttingXp);
  9923. }
  9924. };
  9925. var RED = 'rgb(204, 0, 0)';
  9926.  
  9927. function addBetterStyle()
  9928. {
  9929. var CLASS_NAME = 'seedHighlight';
  9930. addStyle("\n#dialogue-plant-farming input.input-img-farming-patch-dialogue-seeds\n{\n\tpadding: 2px 4px;\n}\n#dialogue-plant-farming #dialogue-plant-grassSeeds\n{\n\theight: 75px;\n\tpadding: 0;\n\twidth: 75px;\n}\n#dialogue-plant-farming #dialogue-plant-treeSeeds,\n#dialogue-plant-farming #dialogue-plant-oakTreeSeeds,\n#dialogue-plant-farming #dialogue-plant-willowTreeSeeds,\n#dialogue-plant-farming #dialogue-plant-mapleTreeSeeds\n{\n\tpadding: 0;\n}\n\nbody." + CLASS_NAME + " #dialogue-plant-farming input.input-img-farming-patch-dialogue-seeds:hover\n{\n\tbackground-color: transparent;\n\tborder: 1px solid black;\n\tmargin: -1px;\n\ttransform: scale(1.1);\n}\n\t\t");
  9931. // seedHighlight
  9932. function updateHoverStyle()
  9933. {
  9934. document.body.classList[settings.get(settings.KEY.highlightUnplantableSeed) ? 'add' : 'remove'](CLASS_NAME);
  9935. }
  9936. updateHoverStyle();
  9937. settings.observe(settings.KEY.highlightUnplantableSeed, function ()
  9938. {
  9939. return updateHoverStyle();
  9940. });
  9941. }
  9942.  
  9943. function readSeedInfo(seedName, tooltipEl)
  9944. {
  9945. var spans = tooltipEl.querySelectorAll(':scope > span');
  9946. var infoSpans = {
  9947. bonemeal: null
  9948. , minLevel: null
  9949. , stopsDyingLevel: null
  9950. , woodcuttingLevel: null
  9951. };
  9952. var info = {
  9953. bonemeal: 0
  9954. , minLevel: 0
  9955. , stopsDyingLevel: 0
  9956. , woodcuttingLevel: 0
  9957. };
  9958. var i = 2;
  9959. for (var key in SEED_INFO_REGEX)
  9960. {
  9961. if (SEED_INFO_REGEX[key].test(spans[i].innerHTML))
  9962. {
  9963. infoSpans[key] = spans.item(i);
  9964. var textNode = spans.item(i).lastChild;
  9965. info[key] = parseInt(textNode.textContent || '', 10);
  9966. i++;
  9967. }
  9968. }
  9969. seedInfoSpans[seedName] = infoSpans;
  9970. seedInfo[seedName] = info;
  9971. }
  9972.  
  9973. function checkSpan(span, fulfilled)
  9974. {
  9975. span.style.color = fulfilled ? '' : RED;
  9976. span.style.fontWeight = fulfilled ? '' : 'bold';
  9977. }
  9978.  
  9979. function checkSeedInfo(seedName, init)
  9980. {
  9981. if (init === void 0)
  9982. {
  9983. init = false;
  9984. }
  9985. var highlight = settings.get(settings.KEY.highlightUnplantableSeed);
  9986. var info = seedInfo[seedName];
  9987. var spans = seedInfoSpans[seedName];
  9988. var canBePlanted = true;
  9989. for (var key in info)
  9990. {
  9991. var span = spans[key];
  9992. if (span)
  9993. {
  9994. var fulfilled = checkInfo[key](info[key]);
  9995. checkSpan(span, !highlight || fulfilled);
  9996. canBePlanted = !highlight || canBePlanted && (key == 'stopsDyingLevel' || fulfilled);
  9997. }
  9998. }
  9999. var itemBox = document.getElementById('item-box-' + seedName);
  10000. if (itemBox)
  10001. {
  10002. itemBox.style.opacity = (!highlight || canBePlanted) ? '' : '.5';
  10003. }
  10004. var plantInput = document.getElementById('dialogue-plant-' + seedName);
  10005. if (plantInput)
  10006. {
  10007. plantInput.style.backgroundColor = (!highlight || canBePlanted) ? '' : 'hsla(0, 100%, 50%, .5)';
  10008. }
  10009. if (init)
  10010. {
  10011. observer.add('bonemeal', function ()
  10012. {
  10013. return checkSeedInfo(seedName);
  10014. });
  10015. observer.add('farmingXp', function ()
  10016. {
  10017. return checkSeedInfo(seedName);
  10018. });
  10019. observer.add('woodcuttingXp', function ()
  10020. {
  10021. return checkSeedInfo(seedName);
  10022. });
  10023. settings.observe(settings.KEY.highlightUnplantableSeed, function ()
  10024. {
  10025. return checkSeedInfo(seedName);
  10026. });
  10027. }
  10028. }
  10029.  
  10030. function getSeedInfo(seedName)
  10031. {
  10032. return seedInfo[seedName];
  10033. }
  10034. farming.getSeedInfo = getSeedInfo;
  10035.  
  10036. function init()
  10037. {
  10038. addBetterStyle();
  10039. // read all seed information
  10040. var tooltipEls = document.querySelectorAll('div[id^="tooltip-"][id$="Seeds"]');
  10041. for (var i = 0; i < tooltipEls.length; i++)
  10042. {
  10043. var tooltipEl = tooltipEls[i];
  10044. var seedName = tooltipEl.id.replace(/^tooltip-/, '');
  10045. readSeedInfo(seedName, tooltipEl);
  10046. checkSeedInfo(seedName, true);
  10047. }
  10048. }
  10049. farming.init = init;
  10050. })(farming || (farming = {}));
  10051.  
  10052. /**
  10053. * general features which doesn't really belong anywhere
  10054. */
  10055. var general;
  10056. (function (general)
  10057. {
  10058. general.name = 'general';
  10059. // disable the drink button for 3 seconds
  10060. var DRINK_DELAY = 3;
  10061.  
  10062. function getSentBoat()
  10063. {
  10064. for (var i = 0; i < BOAT_LIST.length; i++)
  10065. {
  10066. if (getGameValue(BOAT_LIST[i] + 'Timer') > 0)
  10067. {
  10068. return BOAT_LIST[i];
  10069. }
  10070. }
  10071. return null;
  10072. }
  10073.  
  10074. function checkBoat(boat)
  10075. {
  10076. var boatDialog = null;
  10077. var sendBtn = null;
  10078. var initiatedDialogs = document.querySelectorAll('div[role="dialog"]');
  10079. for (var i = 0; i < initiatedDialogs.length; i++)
  10080. {
  10081. var dialog = initiatedDialogs[i];
  10082. if (dialog.style.display !== 'none')
  10083. {
  10084. var btn = dialog.querySelector('input[type="button"][value="Send Boat"]');
  10085. if (btn)
  10086. {
  10087. sendBtn = btn;
  10088. boatDialog = dialog;
  10089. break;
  10090. }
  10091. }
  10092. }
  10093. if (!boatDialog || !sendBtn)
  10094. {
  10095. return;
  10096. }
  10097. var smallboxes = boatDialog.querySelectorAll('div.basic-smallbox');
  10098. var baitBox = smallboxes[0];
  10099. var runningBox = smallboxes[1];
  10100. if (smallboxes.length === 1)
  10101. {
  10102. runningBox = document.createElement('div');
  10103. runningBox.className = 'basic-smallbox';
  10104. runningBox.style.display = 'none';
  10105. var parent_1 = baitBox.parentElement;
  10106. var next = baitBox.nextElementSibling;
  10107. if (parent_1)
  10108. {
  10109. if (next)
  10110. {
  10111. parent_1.insertBefore(runningBox, next);
  10112. }
  10113. else
  10114. {
  10115. parent_1.appendChild(runningBox);
  10116. }
  10117. }
  10118. }
  10119. var sentBoat = getSentBoat();
  10120. baitBox.style.display = sentBoat !== null && !boundBoatingDock ? 'none' : '';
  10121. runningBox.style.display = sentBoat !== null && !boundBoatingDock ? '' : 'none';
  10122. // just in case Smitty changes this game mechanic somehow, don't disable the button:
  10123. // sendBtn.disabled = sentBoat !== null;
  10124. if(!boundBoatingDock)
  10125. sendBtn.style.color = sentBoat !== null ? 'gray' : '';
  10126. win.$(boatDialog).on('dialogclose', function ()
  10127. {
  10128. if (sendBtn)
  10129. {
  10130. sendBtn.style.color = '';
  10131. }
  10132. });
  10133. if (sentBoat === boat)
  10134. {
  10135. runningBox.innerHTML = "<b>Returning in:</b> <span data-item-display=\"" + boat + "Timer\">" + format.timer(getGameValue(boat + 'Timer')) + "</span>";
  10136. }
  10137. else if (sentBoat !== null && !boundBoatingDock)
  10138. {
  10139. runningBox.innerHTML = "Wait for the other boat to return.";
  10140. }
  10141. else
  10142. {
  10143. var enoughBaitAndCoal = win.fishingBait >= win.fishingBaitCost(boat)
  10144. && (boat !== 'steamBoat' || win.charcoal >= 300);
  10145. baitBox.style.color = enoughBaitAndCoal ? '' : 'red';
  10146. }
  10147. }
  10148.  
  10149. function initBoatDialog()
  10150. {
  10151. var _clicksBoat = win.clicksBoat;
  10152. win.clicksBoat = function (boat)
  10153. {
  10154. _clicksBoat(boat);
  10155. checkBoat(boat);
  10156. };
  10157. var _doCommand = win.doCommand;
  10158. win.doCommand = function (data)
  10159. {
  10160. _doCommand(data);
  10161. if (data.startsWith('RUN_FUNC=SAIL_BOAT_WIND'))
  10162. {
  10163. checkBoat('sailBoat');
  10164. }
  10165. };
  10166. }
  10167. var potionDrinkEnable = null;
  10168. var POTION_ACTIVE_HTML = "<br>It's already active.";
  10169. function updateDialogEls(timerKey, dialog, close)
  10170. {
  10171. if (close === void 0)
  10172. {
  10173. close = false;
  10174. }
  10175. var timer = getGameValue(timerKey);
  10176. var showActive = settings.get(settings.KEY.usePotionWarning) && timer > 0 && !close;
  10177. var confirmText = document.getElementById('dialogue-confirm-text');
  10178. var br = confirmText && confirmText.nextElementSibling;
  10179. if (confirmText && br)
  10180. {
  10181. if (showActive)
  10182. {
  10183. confirmText.innerHTML += POTION_ACTIVE_HTML;
  10184. }
  10185. else
  10186. {
  10187. confirmText.innerHTML = confirmText.innerHTML.replace(POTION_ACTIVE_HTML, '');
  10188. }
  10189. br.style.display = showActive ? 'none' : '';
  10190. }
  10191. var confirmBtn = document.getElementById('dialogue-confirm-yes');
  10192. if (confirmBtn && showActive)
  10193. {
  10194. confirmBtn.disabled = true;
  10195. var i_1 = DRINK_DELAY;
  10196. var updateValue_1 = function ()
  10197. {
  10198. confirmBtn.value = 'Drink' + (i_1 > 0 ? ' (' + i_1 + ')' : '');
  10199. if (i_1 === 0)
  10200. {
  10201. potionDrinkEnable && potionDrinkEnable();
  10202. }
  10203. else
  10204. {
  10205. i_1--;
  10206. }
  10207. };
  10208. var countDownInterval_1;
  10209. var dialogClose_1 = function ()
  10210. {
  10211. return potionDrinkEnable && potionDrinkEnable();
  10212. };
  10213. potionDrinkEnable = function ()
  10214. {
  10215. potionDrinkEnable = null;
  10216. win.$(dialog).off('dialogclose', dialogClose_1);
  10217. countDownInterval_1 && clearInterval(countDownInterval_1);
  10218. confirmBtn.disabled = false;
  10219. confirmBtn.value = 'Drink';
  10220. };
  10221. updateValue_1();
  10222. countDownInterval_1 = setInterval(function ()
  10223. {
  10224. return updateValue_1();
  10225. }, 1e3);
  10226. win.$(dialog).on('dialogclose', dialogClose_1);
  10227. }
  10228. else if (!showActive)
  10229. {
  10230. potionDrinkEnable && potionDrinkEnable();
  10231. }
  10232. }
  10233.  
  10234. function checkPotionActive(potion)
  10235. {
  10236. var dialog = document.getElementById('dialogue-confirm');
  10237. var parent = dialog && dialog.parentElement;
  10238. if (!dialog || !parent || parent.style.display === 'none')
  10239. {
  10240. return;
  10241. }
  10242. var timerKey = potion + 'Timer';
  10243. updateDialogEls(timerKey, dialog);
  10244. var fn = observer.add(timerKey, function (key, oldValue, newValue)
  10245. {
  10246. if (oldValue < newValue && oldValue === 0
  10247. || oldValue > newValue && newValue === 0)
  10248. {
  10249. updateDialogEls(timerKey, dialog);
  10250. }
  10251. });
  10252. win.$(dialog).on('dialogclose', function ()
  10253. {
  10254. updateDialogEls(timerKey, dialog, true);
  10255. observer.remove(timerKey, fn);
  10256. });
  10257. }
  10258.  
  10259. function initPotionDialog()
  10260. {
  10261. var _confirmDialogue = win.confirmDialogue;
  10262. win.confirmDialogue = function (width, text, btn1Text, btn2Text, cmd)
  10263. {
  10264. potionDrinkEnable && potionDrinkEnable();
  10265. _confirmDialogue(width, text, btn1Text, btn2Text, cmd);
  10266. };
  10267. var _clicksPotion = win.clicksPotion;
  10268. win.clicksPotion = function (potion)
  10269. {
  10270. _clicksPotion(potion);
  10271. checkPotionActive(potion);
  10272. };
  10273. }
  10274.  
  10275. function init()
  10276. {
  10277. initBoatDialog();
  10278. initPotionDialog();
  10279. }
  10280. general.init = init;
  10281. })(general || (general = {}));
  10282.  
  10283. /**
  10284. * init
  10285. */
  10286. var scriptInitialized = false;
  10287.  
  10288. function init()
  10289. {
  10290. console.info('[%s] "DH2 Fixed %s" up and running.', (new Date).toLocaleTimeString(), version);
  10291. scriptInitialized = true;
  10292. var initModules = [
  10293. settings
  10294. , notifications
  10295. , log
  10296. , gameEvents
  10297. , temporaryFixes
  10298. , crafting
  10299. , itemBoxes
  10300. , chat
  10301. , timer
  10302. , smelting
  10303. , fishingInfo
  10304. , recipeTooltips
  10305. , fixNumbers
  10306. , machineDialog
  10307. , amountInputs
  10308. , newTopbar
  10309. , styleTweaks
  10310. , notifBoxes
  10311. , market
  10312. , combat
  10313. , farming
  10314. , general
  10315. ];
  10316. for (var _i = 0, initModules_1 = initModules; _i < initModules_1.length; _i++)
  10317. {
  10318. var module = initModules_1[_i];
  10319. try
  10320. {
  10321. module.init();
  10322. }
  10323. catch (error)
  10324. {
  10325. console.error('Error during initialization in module "' + module.name + '":', error);
  10326. }
  10327. }
  10328. }
  10329. document.addEventListener('DOMContentLoaded', function ()
  10330. {
  10331. var oldValues = new Map();
  10332. var _doCommand = win.doCommand;
  10333. win.doCommand = function (data)
  10334. {
  10335. if (data.startsWith('REFRESH_ITEMS='))
  10336. {
  10337. oldValues = new Map();
  10338. for (var _i = 0, _a = win.jsItemArray; _i < _a.length; _i++)
  10339. {
  10340. var key = _a[_i];
  10341. oldValues.set(key, getGameValue(key));
  10342. }
  10343. _doCommand(data);
  10344. if (!scriptInitialized)
  10345. {
  10346. init();
  10347. }
  10348. return;
  10349. }
  10350. else if (!scriptInitialized)
  10351. {
  10352. if (data.startsWith('CHAT='))
  10353. {
  10354. var parts = data.substr(5).split('~');
  10355. return chat.newAddToChatBox(parts[0], parts[1], parts[2], parts[3], 0);
  10356. }
  10357. else if (data.startsWith('PM='))
  10358. {
  10359. return chat.newAddToChatBox(win.username, '0', '0', data.substr(3), 1);
  10360. }
  10361. }
  10362. var ret = commands.process(data);
  10363. if (ret === void 0)
  10364. {
  10365. ret = _doCommand(commands.formatData(data));
  10366. }
  10367. return ret;
  10368. };
  10369. var _refreshItemValues = win.refreshItemValues;
  10370. win.refreshItemValues = function (itemKeyList, firstLoad)
  10371. {
  10372. _refreshItemValues(itemKeyList, firstLoad);
  10373. for (var _i = 0, itemKeyList_2 = itemKeyList; _i < itemKeyList_2.length; _i++)
  10374. {
  10375. var key = itemKeyList_2[_i];
  10376. observer.notify(key, oldValues.get(key));
  10377. }
  10378. observer.notifyTick();
  10379. };
  10380. });
  10381.  
  10382. /**
  10383. * fix web socket errors
  10384. */
  10385. var main;
  10386. (function (main)
  10387. {
  10388. var WS_TIMEOUT_SEC = 30;
  10389. var WS_TIMEOUT_CODE = 3000;
  10390. var WS_OPEN_TIMEOUT_SEC = 2 * 60; // 2 minutes
  10391. // reload the page after 5 consecutive reconnect attempts without successfully opening the websocket once
  10392. var MAX_RECONNECTS = 5;
  10393.  
  10394. function webSocketLoaded(event)
  10395. {
  10396. if (win.webSocket == null)
  10397. {
  10398. console.error('WebSocket instance not initialized!');
  10399. return;
  10400. }
  10401. // cache old event listener
  10402. var _onClose = win.webSocket.onclose;
  10403. var _onError = win.webSocket.onerror;
  10404. var _onMessage = win.webSocket.onmessage;
  10405. var _onOpen = win.webSocket.onopen;
  10406. var commandQueue = [];
  10407. var _cBytes = win.cBytes;
  10408. win.cBytes = function (command)
  10409. {
  10410. if (win.webSocket && win.webSocket.readyState === WebSocket.OPEN)
  10411. {
  10412. _cBytes(command);
  10413. }
  10414. else
  10415. {
  10416. commandQueue.push(command);
  10417. }
  10418. };
  10419. var pageLoaded = false;
  10420. var wsTimeout = null;
  10421. var reconnectAttempts = 0;
  10422.  
  10423. function onTimeout()
  10424. {
  10425. wsTimeout = null;
  10426. // renew the websocket
  10427. if (reconnectAttempts <= MAX_RECONNECTS)
  10428. {
  10429. win.webSocket = new WebSocket(win.SSL_ENABLED);
  10430. win.ignoreBytesTracker = Date.now();
  10431. initWSListener(win.webSocket);
  10432. reconnectAttempts++;
  10433. }
  10434. if (win.webSocket)
  10435. {
  10436. win.webSocket.close(WS_TIMEOUT_CODE, 'Connection timed out after ' + WS_TIMEOUT_SEC + ' seconds');
  10437. }
  10438. }
  10439.  
  10440. function updateWSTimeout()
  10441. {
  10442. if (wsTimeout)
  10443. {
  10444. win.clearTimeout(wsTimeout);
  10445. }
  10446. wsTimeout = win.setTimeout(onTimeout, WS_TIMEOUT_SEC * 1e3);
  10447. }
  10448. var messageQueue = [];
  10449.  
  10450. function onMessage(event)
  10451. {
  10452. if (pageLoaded)
  10453. {
  10454. updateWSTimeout();
  10455. return _onMessage.call(this, event);
  10456. }
  10457. else
  10458. {
  10459. messageQueue.push(event);
  10460. }
  10461. };
  10462. var wsOpenTimeout = null;
  10463.  
  10464. function onOpenTimeout()
  10465. {
  10466. wsOpenTimeout = null;
  10467. location.reload();
  10468. }
  10469.  
  10470. function onOpen(event)
  10471. {
  10472. reconnectAttempts = 0;
  10473. if (wsOpenTimeout)
  10474. {
  10475. win.clearTimeout(wsOpenTimeout);
  10476. wsOpenTimeout = null;
  10477. }
  10478. // do the handshake first
  10479. _onOpen.call(this, event);
  10480. commandQueue.forEach(function (command)
  10481. {
  10482. return win.cBytes(command);
  10483. });
  10484. }
  10485.  
  10486. function onError(event)
  10487. {
  10488. console.error('error in websocket:', event);
  10489. return _onError.call(this, event);
  10490. }
  10491.  
  10492. function onClose(event)
  10493. {
  10494. console.info('websocket closed:', event);
  10495. if (event.code !== WS_TIMEOUT_CODE || reconnectAttempts > MAX_RECONNECTS)
  10496. {
  10497. location.reload();
  10498. }
  10499. return _onClose.call(this, event);
  10500. }
  10501.  
  10502. function initWSListener(ws)
  10503. {
  10504. if (ws.readyState === WebSocket.CONNECTING)
  10505. {
  10506. wsOpenTimeout = win.setTimeout(onOpenTimeout, WS_OPEN_TIMEOUT_SEC * 1e3);
  10507. }
  10508. ws.onclose = onClose;
  10509. ws.onerror = onError;
  10510. ws.onmessage = onMessage;
  10511. ws.onopen = onOpen;
  10512. }
  10513. initWSListener(win.webSocket);
  10514. document.addEventListener('DOMContentLoaded', function ()
  10515. {
  10516. pageLoaded = true;
  10517. messageQueue.forEach(function (event)
  10518. {
  10519. return win.webSocket.onmessage(event);
  10520. });
  10521. });
  10522. }
  10523.  
  10524. function isScriptElement(el)
  10525. {
  10526. return el.nodeName === 'SCRIPT';
  10527. }
  10528.  
  10529. function isWebSocketScript(script)
  10530. {
  10531. return script.src.includes('socket.js');
  10532. }
  10533. var found = false;
  10534. if (document.head)
  10535. {
  10536. var scripts = document.head.querySelectorAll('script');
  10537. for (var i = 0; i < scripts.length; i++)
  10538. {
  10539. if (isWebSocketScript(scripts[i]))
  10540. {
  10541. // does this work?
  10542. scripts[i].onload = webSocketLoaded;
  10543. found = true;
  10544. }
  10545. }
  10546. }
  10547. if (!found)
  10548. {
  10549. // create an observer instance
  10550. var mutationObserver_1 = new MutationObserver(function (mutationList)
  10551. {
  10552. mutationList.forEach(function (mutation)
  10553. {
  10554. if (mutation.addedNodes.length === 0)
  10555. {
  10556. return;
  10557. }
  10558. for (var i = 0; i < mutation.addedNodes.length; i++)
  10559. {
  10560. var node = mutation.addedNodes[i];
  10561. if (isScriptElement(node) && isWebSocketScript(node))
  10562. {
  10563. mutationObserver_1.disconnect();
  10564. node.onload = webSocketLoaded;
  10565. return;
  10566. }
  10567. }
  10568. });
  10569. });
  10570. mutationObserver_1.observe(document.head
  10571. , {
  10572. childList: true
  10573. });
  10574. }
  10575. // fix scrollText (e.g. when joining the game and receiving xp at that moment)
  10576. win.mouseX = win.innerWidth / 2;
  10577. win.mouseY = win.innerHeight / 2;
  10578. var _confirm = win.confirm;
  10579. win.confirm = function (message)
  10580. {
  10581. // don't show the annoying update confirm box (instead of a confirm box, an ingame dialog could be used...)
  10582. if (message && message.indexOf('Ted\'s Market Script') !== -1)
  10583. {
  10584. return false;
  10585. }
  10586. return _confirm(message);
  10587. };
  10588. })(main || (main = {}));
  10589.  
  10590. })();