DH2 Fixed's temporary fixed

Improve Diamond Hunt 2's DH2 Fixed

当前为 2018-01-24 提交的版本,查看 最新版本

  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.51
  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', 'strange', 'ancient'];
  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 Plant'
  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. , 'meditate1': 'Meditate level 1'
  4638. , 'meditate2': 'Meditate level 2'
  4639. , 'meditate3': 'Meditate level 3'
  4640. , 'meditate4': 'Meditate level 4'
  4641. , 'meditate5': 'Meditate level 5'
  4642. , 'meditate6': 'Meditate level 6'
  4643. , 'meditate7': 'Meditate level 7'
  4644. , 'meditate8': 'Meditate level 8'
  4645. , 'meditate9': 'Meditate level 9'
  4646. , 'gardener': 'Gardener'
  4647. , 'planter': 'Planter'
  4648. , 'boundBrewingKit': 'Brewing Kit'
  4649. , 'cooksBook': 'Cooks Book'
  4650. , 'cooksPage': 'Cooks Page'
  4651. , 'combatDropTable': 'Loot Table'
  4652. , 'magicBook': 'Spell Book'
  4653. , 'magicShop': 'Magic Shop'
  4654. , 'crackedSpinningWheel': 'Cracked Wheel'
  4655. , 'woodenSpinningWheel': 'Wooden Wheel'
  4656. , 'oakSpinningWheel': 'Oak Wheel'
  4657. , 'willowSpinningWheel': 'Willow Wheel'
  4658. , 'mapleSpinningWheel': 'Maple Wheel'
  4659. , 'stardustSpinningWheel': 'Stardust Wheel'
  4660. , 'strangeSpinningWheel': 'Strange Wheel'
  4661. , 'ancientSpinningWheel': 'Ancient Wheel'
  4662. };
  4663. for (var key in key2Name)
  4664. {
  4665. var span = addSpan2ItemBox(key);
  4666. if (span)
  4667. {
  4668. span.textContent = key2Name[key];
  4669. }
  4670. }
  4671. }
  4672. // show current tier
  4673. function addTierCaption()
  4674. {
  4675. 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");
  4676.  
  4677. function addOrbObserver(key, spanList)
  4678. {
  4679. var boundOrbKey = getBoundKey('Blue' + capitalize(key) + 'Orb');
  4680.  
  4681. function checkOrb()
  4682. {
  4683. var classAction = getGameValue(boundOrbKey) > 0 ? 'add' : 'remove';
  4684. for (var _i = 0, spanList_1 = spanList; _i < spanList_1.length; _i++)
  4685. {
  4686. var span = spanList_1[_i];
  4687. span.classList[classAction]('orb');
  4688. }
  4689. }
  4690. checkOrb();
  4691. observer.add(boundOrbKey, function ()
  4692. {
  4693. return checkOrb();
  4694. });
  4695. }
  4696. var remainingOrbItems = ORB_ITEMS;
  4697. for (var _i = 0, TIER_ITEMS_2 = TIER_ITEMS; _i < TIER_ITEMS_2.length; _i++)
  4698. {
  4699. var tierItem = TIER_ITEMS_2[_i];
  4700. var isBindable = TIER_ITEMS_NOT_BINDABLE.indexOf(tierItem) === -1;
  4701. var spanList = [];
  4702. for (var i = 0; i < TIER_LEVELS.length; i++)
  4703. {
  4704. var key = getTierKey(tierItem, i);
  4705. var toolKey = isBindable ? getBoundKey(key) : key;
  4706. var tierSpan = addSpan2ItemBox(toolKey);
  4707. if (tierSpan)
  4708. {
  4709. tierSpan.classList.add('tier');
  4710. tierSpan.textContent = TIER_NAMES[i];
  4711. spanList.push(tierSpan);
  4712. }
  4713. }
  4714. var orbIndex = remainingOrbItems.indexOf(tierItem);
  4715. if (orbIndex !== -1)
  4716. {
  4717. addOrbObserver(tierItem, spanList);
  4718. remainingOrbItems.splice(orbIndex, 1);
  4719. }
  4720. }
  4721. for (var _a = 0, remainingOrbItems_1 = remainingOrbItems; _a < remainingOrbItems_1.length; _a++)
  4722. {
  4723. var itemKey = remainingOrbItems_1[_a];
  4724. var captionSpan = document.querySelector('#item-box-' + getBoundKey(itemKey) + ' > span:last-of-type');
  4725. if (!captionSpan)
  4726. {
  4727. continue;
  4728. }
  4729. addOrbObserver(itemKey, [captionSpan]);
  4730. }
  4731. }
  4732. var boatTimerKeys = BOAT_LIST.map(function (boatKey)
  4733. {
  4734. return boatKey + 'Timer';
  4735. });
  4736.  
  4737. function checkBoat(span, timerKey, init)
  4738. {
  4739. if (init === void 0)
  4740. {
  4741. init = false;
  4742. }
  4743. var isInTransit = getGameValue(timerKey) > 0;
  4744. var otherInTransit = boatTimerKeys.some(function (k)
  4745. {
  4746. return k != timerKey && getGameValue(k) > 0 && !boundBoatingDock;
  4747. });
  4748. span.textContent = isInTransit ? 'In transit' : 'Ready';
  4749. span.style.visibility = otherInTransit ? 'hidden' : '';
  4750. var parent = span.parentElement;
  4751. parent.style.opacity = otherInTransit ? '.5' : '';
  4752. if (init)
  4753. {
  4754. observer.add(boatTimerKeys, function ()
  4755. {
  4756. return checkBoat(span, timerKey, false);
  4757. });
  4758. }
  4759. }
  4760. // show boat progress
  4761. function addBoatCaption()
  4762. {
  4763. addStyle("\n#item-box-boundSailBoat.item-box > span[data-item-display] + span + span\n{\n\tdisplay: none;\n}\n\t\t");
  4764. for (var i = 0; i < BOAT_LIST.length; i++)
  4765. {
  4766. var span = addSpan2ItemBox(getBoundKey(BOAT_LIST[i]));
  4767. if (span)
  4768. {
  4769. checkBoat(span, boatTimerKeys[i], true);
  4770. }
  4771. }
  4772. }
  4773. // show bonemeal
  4774. function addBonemealCaption()
  4775. {
  4776. var noBonemealSpan = addSpan2ItemBox('boundBonemealBin');
  4777. if (!noBonemealSpan)
  4778. {
  4779. return;
  4780. }
  4781. noBonemealSpan.textContent = 'Bonemeal: 0';
  4782. var bonemealSpan = addSpan2ItemBox('boundFilledBonemealBin');
  4783. if (!bonemealSpan)
  4784. {
  4785. return;
  4786. }
  4787. bonemealSpan.dataset.itemDisplay = 'bonemeal';
  4788. bonemealSpan.textContent = format.number(win.bonemeal);
  4789. var captionSpan = document.createElement('span');
  4790. captionSpan.className = 'caption';
  4791. captionSpan.textContent = 'Bonemeal: ';
  4792. bonemealSpan.parentElement.insertBefore(captionSpan, bonemealSpan);
  4793. }
  4794.  
  4795. function warningBeforeSellingGems()
  4796. {
  4797. var _sellNPCItemDialogue = win.sellNPCItemDialogue;
  4798. win.sellNPCItemDialogue = function (item, amount)
  4799. {
  4800. if (item == 'sapphire' || item == 'emerald' || item == 'ruby' || item == 'diamond' || item == 'bloodDiamond')
  4801. {
  4802. var itemName = key2Name(amount == 1 ? item : item.replace(/y$/, 'ie') + 's', true);
  4803. if (amount == 0
  4804. || !win.confirm('Gems are precious and rare. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  4805. {
  4806. return;
  4807. }
  4808. }
  4809. else if (item == 'logs' || item == 'oakLogs' || item == 'willowLogs' || item == 'mapleLogs' || item == 'stardustLogs' || item == 'strangeLogs' || item == 'ancientLogs')
  4810. {
  4811. var itemName = key2Name(amount == 1 ? item.replace(/s$/, '') : item, true);
  4812. if (amount == 0
  4813. || !win.confirm('Logs are time consuming to collect. Please consider carefully:\nDo you really want to sell ' + amount + ' ' + itemName + '?'))
  4814. {
  4815. return;
  4816. }
  4817. }
  4818. _sellNPCItemDialogue(item, amount);
  4819. };
  4820. }
  4821.  
  4822. function addWikiaLinks()
  4823. {
  4824. var WIKIA_CLASS = 'wikia-links';
  4825. 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");
  4826.  
  4827. function setWikiaLinksVisibility(init)
  4828. {
  4829. if (init === void 0)
  4830. {
  4831. init = false;
  4832. }
  4833. var show = settings.get(settings.KEY.wikiaLinks);
  4834. document.body.classList[show ? 'add' : 'remove'](WIKIA_CLASS);
  4835. if (init)
  4836. {
  4837. settings.observe(settings.KEY.wikiaLinks, function ()
  4838. {
  4839. return setWikiaLinksVisibility();
  4840. });
  4841. }
  4842. }
  4843. setWikiaLinksVisibility(true);
  4844. var boxes = document.getElementsByClassName('item-box');
  4845.  
  4846. function disableClickPropagation(el)
  4847. {
  4848. el.addEventListener('click', function (event)
  4849. {
  4850. event.stopPropagation();
  4851. });
  4852. }
  4853. for (var i = 0; i < boxes.length; i++)
  4854. {
  4855. var box = boxes.item(i);
  4856. var key = box.id.replace(/^item-box-/, '');
  4857. var linkArea = document.createElement('a');
  4858. linkArea.className = 'wikia-link';
  4859. linkArea.href = getWikiaLink(key);
  4860. linkArea.target = '_blank';
  4861. disableClickPropagation(linkArea);
  4862. box.appendChild(linkArea);
  4863. var tooltipEl = ensureTooltip('wikiLink', linkArea);
  4864. if (tooltipEl.innerHTML === '')
  4865. {
  4866. tooltipEl.innerHTML = "Click to open the wikia page about this item.";
  4867. }
  4868. }
  4869. }
  4870.  
  4871. function init()
  4872. {
  4873. addCaptionStyle();
  4874. addFurnaceCaption();
  4875. addOilStorageCaption();
  4876. addOilCaption();
  4877. addWandCaption();
  4878. addVariousCaptions();
  4879. addTierCaption();
  4880. addBoatCaption();
  4881. addBonemealCaption();
  4882. warningBeforeSellingGems();
  4883. addWikiaLinks();
  4884. }
  4885. itemBoxes.init = init;
  4886. })(itemBoxes || (itemBoxes = {}));
  4887.  
  4888. /**
  4889. * add new chat
  4890. */
  4891. var chat;
  4892. (function (chat)
  4893. {
  4894. chat.name = 'chat';
  4895. // min time difference between repeated messages to not be considered as spam
  4896. var MIN_DIFF_REPEATED_MSG = 5e3;
  4897. var KEYWORD_LIST_KEY = 'keywordList';
  4898. chat.keywordList = store.has(KEYWORD_LIST_KEY) ? store.get(KEYWORD_LIST_KEY) : [];
  4899. var CHAT_HISTORY_KEY = 'chatHistory';
  4900. var MAX_CHAT_HISTORY_LENGTH = 100;
  4901. var PM_HISTORY_KEY = 'pmHistory';
  4902. var MAX_PM_HISTORY_LENGTH = 50;
  4903. var Type;
  4904. (function (Type)
  4905. {
  4906. Type[Type["reload"] = -1] = "reload";
  4907. Type[Type["normal"] = 0] = "normal";
  4908. Type[Type["pmReceived"] = 1] = "pmReceived";
  4909. Type[Type["pmSent"] = 2] = "pmSent";
  4910. Type[Type["serverMsg"] = 3] = "serverMsg";
  4911. })(Type || (Type = {}));;
  4912. var Tag;
  4913. (function (Tag)
  4914. {
  4915. Tag[Tag["none"] = 0] = "none";
  4916. Tag[Tag["donor"] = 1] = "donor";
  4917. Tag[Tag["contributor"] = 2] = "contributor";
  4918. Tag[Tag["mod"] = 3] = "mod";
  4919. Tag[Tag["dev"] = 4] = "dev";
  4920. Tag[Tag["server"] = 5] = "server";
  4921. })(Tag || (Tag = {}));;
  4922. /**
  4923. * The chunk hiding starts with at least 10 chunks.
  4924. * So there are at least
  4925. * (chunkHidingMinChunks-1) * msgChunkSize + 1 = 9 * 100 + 1 = 901
  4926. * messages before the chunk hiding mechanism starts.
  4927. */
  4928. var CHUNK_HIDING_MIN_CHUNKS = 10;
  4929. var MSG_CHUNK_SIZE = 100;
  4930. var RELOADED_CHAT_DATA = {
  4931. timestamp: 0
  4932. , username: ''
  4933. , userlevel: 0
  4934. , icon: 0
  4935. , tag: 0
  4936. , type: Type.reload
  4937. , msg: '[...]'
  4938. };
  4939. var CHAT_BOX_ID = 'div-chat';
  4940. var DEFAULT_CHAT_DIV_ID = 'div-chat-area';
  4941. var GENERAL_CHAT_DIV_ID = 'div-chat-general';
  4942. var PM_CHAT_TAB_PREFIX = 'tab-chat-pm-';
  4943. var PM_CHAT_DIV_PREFIX = 'div-chat-pm-';
  4944. var CHAT_TABS_ID = 'chat-tabs';
  4945. var CHAT_INPUT_ID = 'chat-input-text';
  4946. var CHAT_CLASS = 'div-chat-area';
  4947. var COLORIZE_CLASS = 'colorize';
  4948. var SpecialTab;
  4949. (function (SpecialTab)
  4950. {
  4951. SpecialTab[SpecialTab["default"] = 0] = "default";
  4952. SpecialTab[SpecialTab["general"] = 1] = "general";
  4953. SpecialTab[SpecialTab["filler"] = 2] = "filler";
  4954. })(SpecialTab || (SpecialTab = {}));;
  4955. var CHAT_SPECIAL_TAB_ID = (_a = {}
  4956. , _a[SpecialTab.default] = 'tab-chat-default'
  4957. , _a[SpecialTab.general] = 'tab-chat-general'
  4958. , _a[SpecialTab.filler] = 'tab-chat-filler'
  4959. , _a);
  4960. var CONTEXTMENU_ID = 'player-contextmenu';
  4961. var CHAT_ICONS = [
  4962. {
  4963. key: ''
  4964. , title: ''
  4965. }
  4966. , {
  4967. key: 'halloween2015'
  4968. , title: 'Halloween Gamer (2015)'
  4969. }
  4970. , {
  4971. key: 'christmas2015'
  4972. , title: 'Chirstmas Gamer (2015)'
  4973. }
  4974. , {
  4975. key: 'easter2016'
  4976. , title: 'Easter Gamer (2016)'
  4977. }
  4978. , {
  4979. key: 'halloween2016'
  4980. , title: 'Halloween Gamer (2016)'
  4981. }
  4982. , {
  4983. key: 'christmas2016'
  4984. , title: 'Chirstmas Gamer (2016)'
  4985. }
  4986. , {
  4987. key: 'dh1Max'
  4988. , title: 'DH1 Pro'
  4989. }
  4990. , {
  4991. key: 'hardcore'
  4992. , title: 'Hardcore Player'
  4993. }
  4994. , {
  4995. key: 'quest'
  4996. , title: 'Questmaster'
  4997. }
  4998. , {
  4999. key: 'maxMining'
  5000. , title: 'Mastery in mining'
  5001. }
  5002. , {
  5003. key: 'maxCrafting'
  5004. , title: 'Mastery in crafting'
  5005. }
  5006. , {
  5007. key: 'maxWC'
  5008. , title: 'Mastery in woodcutting'
  5009. }
  5010. , {
  5011. key: 'maxFarming'
  5012. , title: 'Mastery in farming'
  5013. }
  5014. , {
  5015. key: 'maxBrewing'
  5016. , title: 'Mastery in brewing'
  5017. }
  5018. , {
  5019. key: 'maxCombat'
  5020. , title: 'Mastery in combat'
  5021. }
  5022. , {
  5023. key: 'maxMagic'
  5024. , title: 'Mastery in magic'
  5025. }
  5026. , {
  5027. key: 'maxFishing'
  5028. , title: 'Mastery in fishing'
  5029. }
  5030. , {
  5031. key: 'maxCooking'
  5032. , title: 'Mastery in cooking'
  5033. }
  5034. , {
  5035. key: 'maxLevel'
  5036. , title: 'Mastery of all skills'
  5037. }
  5038. , {
  5039. key: 'birdcage'
  5040. , title: 'Stole a birdcage'
  5041. }
  5042. , {
  5043. key: 'achievement'
  5044. , title: 'Achievement Hunter'
  5045. }
  5046. , {
  5047. key: 'pinkPartyHat'
  5048. , title: 'Pink Party Hat'
  5049. }
  5050. , {
  5051. key: 'redPartyHat'
  5052. , title: 'Red Party Hat'
  5053. }
  5054. , {
  5055. key: 'greenPartyHat'
  5056. , title: 'Green Party Hat'
  5057. }
  5058. , {
  5059. key: 'yellowPartyHat'
  5060. , title: 'Yellow Party Hat'
  5061. }
  5062. , {
  5063. key: 'whitePartyHat'
  5064. , title: 'White Party Hat'
  5065. }
  5066. , {
  5067. key: 'bluePartyHat'
  5068. , title: 'Blue Party Hat'
  5069. }];
  5070. var getUnknownChatIcon = function (icon)
  5071. {
  5072. return {
  5073. key: 'unknown'
  5074. , title: ''
  5075. , img: '<img src="images/chat-icons/' + icon + '.png" class="image-icon-20" />'
  5076. };
  5077. };
  5078. var CHAT_TAGS = [
  5079. null
  5080. , {
  5081. key: 'donor'
  5082. , name: ''
  5083. }
  5084. , {
  5085. key: 'contributor'
  5086. , name: 'Contributor'
  5087. }
  5088. , {
  5089. key: 'mod'
  5090. , name: 'Moderator'
  5091. }
  5092. , {
  5093. key: 'dev'
  5094. , name: 'Dev'
  5095. }
  5096. , {
  5097. key: 'yell'
  5098. , name: 'Server Message'
  5099. }
  5100. ];
  5101. var LOCALE = 'en-US';
  5102. var LOCALE_OPTIONS = {
  5103. hour12: false
  5104. , year: 'numeric'
  5105. , month: 'long'
  5106. , day: 'numeric'
  5107. , hour: '2-digit'
  5108. , minute: '2-digit'
  5109. , second: '2-digit'
  5110. };
  5111. // game commands
  5112. var COMMANDS = [
  5113. 'pm'
  5114. , 'mute'
  5115. , 'clear'
  5116. , 'ipmute'
  5117. ];
  5118. var CLEAR_CMD = 'clear';
  5119. var TUTORIAL_CMD = 'tutorial';
  5120. // load chat history
  5121. var chatHistory = store.get(CHAT_HISTORY_KEY) || [];
  5122. var pmHistory = store.get(PM_HISTORY_KEY) || [];
  5123. // store chat colors for each user
  5124. var user2Color;
  5125. var usedColors;
  5126. // reserve color for special messages (e.g. server messages): white
  5127. var reservedColors = ['#ffffff'];
  5128. // message chunks
  5129. var msgChunkMap = new Map();
  5130. // for adding elements at startup
  5131. var chatboxFragments = new Map();
  5132. var chatInitialized = false;
  5133. // find index of last message which is not a pm
  5134. var isLastMsgNotReload = false;
  5135. for (var i = chatHistory.length - 1; i >= 0; i--)
  5136. {
  5137. if (!isDataPM(chatHistory[i]))
  5138. {
  5139. isLastMsgNotReload = chatHistory[i].type != Type.reload;
  5140. break;
  5141. }
  5142. }
  5143. // insert a placeholder for a reloaded chat
  5144. if (isLastMsgNotReload)
  5145. {
  5146. RELOADED_CHAT_DATA.timestamp = (new Date()).getTime();
  5147. chatHistory.push(RELOADED_CHAT_DATA);
  5148. }
  5149.  
  5150. function isMuted(user)
  5151. {
  5152. return user !== win.username
  5153. && win.mutedPeople.some(function (name)
  5154. {
  5155. return user.indexOf(name) > -1;
  5156. });
  5157. }
  5158.  
  5159. function isSpam(data)
  5160. {
  5161. // allow all own messages, messages from contributors, mods, devs and all server messages
  5162. if (data.username === win.username || data.tag != Tag.none)
  5163. {
  5164. return false;
  5165. }
  5166. /**
  5167. * get last message of current user
  5168. */
  5169. var historyIndex = chatHistory.indexOf(data);
  5170. if (historyIndex == -1)
  5171. {
  5172. historyIndex = chatHistory.length;
  5173. }
  5174. var lastData = null;
  5175. for (var i = historyIndex - 1; i >= 0 && (lastData === null); i--)
  5176. {
  5177. var dataBefore = chatHistory[i];
  5178. if (dataBefore.username === data.username)
  5179. {
  5180. lastData = dataBefore;
  5181. }
  5182. }
  5183. /**
  5184. * compare message and don't allow the same message twice
  5185. */
  5186. if (lastData
  5187. && lastData.msg === data.msg
  5188. && (data.timestamp - lastData.timestamp) < MIN_DIFF_REPEATED_MSG)
  5189. {
  5190. return true;
  5191. }
  5192. return false;
  5193. }
  5194.  
  5195. function saveKeywordList()
  5196. {
  5197. store.set(KEYWORD_LIST_KEY, chat.keywordList);
  5198. }
  5199.  
  5200. function addKeyword(keyword)
  5201. {
  5202. if (keyword !== '' && chat.keywordList.indexOf(keyword) === -1)
  5203. {
  5204. chat.keywordList.push(keyword);
  5205. saveKeywordList();
  5206. return true;
  5207. }
  5208. return false;
  5209. }
  5210. chat.addKeyword = addKeyword;
  5211.  
  5212. function removeKeyword(keyword)
  5213. {
  5214. var index = chat.keywordList.indexOf(keyword);
  5215. if (index !== -1)
  5216. {
  5217. chat.keywordList.splice(index, 1);
  5218. saveKeywordList();
  5219. return true;
  5220. }
  5221. return false;
  5222. }
  5223. chat.removeKeyword = removeKeyword;
  5224.  
  5225. function handleScrolling(chatbox)
  5226. {
  5227. if (win.isAutoScrolling)
  5228. {
  5229. setTimeout(function ()
  5230. {
  5231. return chatbox.scrollTop = chatbox.scrollHeight;
  5232. });
  5233. }
  5234. }
  5235. // for chat messages which arrive before DOMContentLoaded and can not be displayed since the DOM isn't ready
  5236. function processChatData(username, iconString, tagString, msg, isPM)
  5237. {
  5238. var tag = parseInt(tagString, 10);
  5239. var userlevel = 0;
  5240. var type = Type.normal;
  5241. if (isPM == 1)
  5242. {
  5243. var match = msg.match(/^\s*\[(PM from|Sent to) ([A-Za-z0-9_ ]+)\]: (.+?)\s*$/) || ['', '', username, msg];
  5244. type = match[1] == 'Sent to' ? Type.pmSent : Type.pmReceived;
  5245. username = match[2];
  5246. if (username !== 'sexy_squid')
  5247. {
  5248. username = username.replace(/_/g, ' ');
  5249. }
  5250. msg = match[3];
  5251. }
  5252. else if (tag == Tag.server)
  5253. {
  5254. type = Type.serverMsg;
  5255. }
  5256. else
  5257. {
  5258. var match = msg.match(/^\s*\((\d+)\): (.+?)\s*$/);
  5259. if (match)
  5260. {
  5261. userlevel = parseInt(match[1], 10);
  5262. msg = match[2];
  5263. }
  5264. else
  5265. {
  5266. userlevel = win.getGlobalLevel();
  5267. }
  5268. }
  5269. // unlinkify when using DH2QoL to store the plain message
  5270. if (win.addToChatBox.toString().includes('linkify(arguments[3])'))
  5271. {
  5272. msg = msg.replace(/<a href='([^']+)' target='_blank'>\1<\/a>/ig, '$1');
  5273. }
  5274. if (type == Type.pmSent)
  5275. {
  5276. // turn some critical characters into HTML entities
  5277. msg = msg.replace(/[<>]/g, function (char)
  5278. {
  5279. return '&#' + char.charCodeAt(0) + ';';
  5280. });
  5281. }
  5282. return {
  5283. timestamp: now()
  5284. , username: username
  5285. , userlevel: userlevel
  5286. , icon: parseInt(iconString, 10)
  5287. , tag: tag
  5288. , type: type
  5289. , msg: msg
  5290. };
  5291. }
  5292.  
  5293. function saveChatHistory()
  5294. {
  5295. store.set(CHAT_HISTORY_KEY, chatHistory);
  5296. }
  5297.  
  5298. function savePmHistory()
  5299. {
  5300. store.set(PM_HISTORY_KEY, pmHistory);
  5301. }
  5302.  
  5303. function add2ChatHistory(data)
  5304. {
  5305. if (data.type === Type.pmReceived
  5306. || data.type === Type.pmSent)
  5307. {
  5308. pmHistory.push(data);
  5309. pmHistory = pmHistory.slice(-MAX_PM_HISTORY_LENGTH);
  5310. savePmHistory();
  5311. }
  5312. else
  5313. {
  5314. chatHistory.push(data);
  5315. chatHistory = chatHistory.slice(-MAX_CHAT_HISTORY_LENGTH);
  5316. saveChatHistory();
  5317. }
  5318. }
  5319.  
  5320. function username2Id(username)
  5321. {
  5322. return username.replace(/ /g, '_');
  5323. }
  5324.  
  5325. function setNewCounter(tab, num, force)
  5326. {
  5327. if (force === void 0)
  5328. {
  5329. force = false;
  5330. }
  5331. var panel = getChatPanel(tab.dataset.username || '');
  5332. if (force
  5333. || !tab.classList.contains('selected')
  5334. || !win.isAutoScrolling && panel.scrollHeight > panel.scrollTop + panel.offsetHeight)
  5335. {
  5336. tab.dataset.new = num.toString();
  5337. }
  5338. }
  5339.  
  5340. function incrementNewCounter(tab)
  5341. {
  5342. setNewCounter(tab, parseInt(tab.dataset.new || '0', 10) + 1);
  5343. }
  5344.  
  5345. function getChatTab(username, specialTab)
  5346. {
  5347. var id = (specialTab != null)
  5348. ? CHAT_SPECIAL_TAB_ID[specialTab]
  5349. : PM_CHAT_TAB_PREFIX + username2Id(username);
  5350. var tab = document.getElementById(id);
  5351. if (!tab)
  5352. {
  5353. tab = document.createElement('div');
  5354. tab.className = 'chat-tab';
  5355. if (specialTab != null)
  5356. {
  5357. tab.classList.add(SpecialTab[specialTab]);
  5358. }
  5359. tab.id = id;
  5360. tab.dataset.username = username;
  5361. setNewCounter(tab, 0, true);
  5362. if (username.length > 0)
  5363. {
  5364. tab.textContent = username;
  5365. // thanks /u/Spino-Prime for pointing out this was missing
  5366. var closeSpan = document.createElement('span');
  5367. closeSpan.className = 'close';
  5368. tab.appendChild(closeSpan);
  5369. }
  5370. var chatTabs = document.getElementById(CHAT_TABS_ID);
  5371. var filler = chatTabs.querySelector('.filler');
  5372. if (filler)
  5373. {
  5374. chatTabs.insertBefore(tab, filler);
  5375. }
  5376. else
  5377. {
  5378. chatTabs.appendChild(tab);
  5379. }
  5380. }
  5381. return tab;
  5382. }
  5383.  
  5384. function getChatPanel(username)
  5385. {
  5386. var id = username == '' ? GENERAL_CHAT_DIV_ID : PM_CHAT_DIV_PREFIX + username2Id(username);
  5387. var panel = document.getElementById(id);
  5388. if (!panel)
  5389. {
  5390. panel = document.createElement('div');
  5391. panel.setAttribute('disabled', 'disabled');
  5392. panel.id = id;
  5393. panel.className = CHAT_CLASS;
  5394. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5395. var height = defaultChat.style.height;
  5396. panel.style.height = height;
  5397. var chatDiv = defaultChat.parentElement;
  5398. chatDiv.insertBefore(panel, defaultChat);
  5399. }
  5400. return panel;
  5401. }
  5402.  
  5403. function changeChatTab(oldTab, newTab)
  5404. {
  5405. if (oldTab)
  5406. {
  5407. oldTab.classList.remove('selected');
  5408. var oldChatPanel = void 0;
  5409. if (oldTab.classList.contains('default'))
  5410. {
  5411. oldChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5412. }
  5413. else
  5414. {
  5415. oldChatPanel = getChatPanel(oldTab.dataset.username || '');
  5416. }
  5417. oldChatPanel.classList.remove('selected');
  5418. }
  5419. newTab.classList.add('selected');
  5420. setNewCounter(newTab, 0, true);
  5421. var newChatPanel;
  5422. if (newTab.classList.contains('default'))
  5423. {
  5424. newChatPanel = document.getElementById(DEFAULT_CHAT_DIV_ID);
  5425. }
  5426. else
  5427. {
  5428. newChatPanel = getChatPanel(newTab.dataset.username || '');
  5429. }
  5430. newChatPanel.classList.add('selected');
  5431. var toUsername = newTab.dataset.username;
  5432. var newTextPlaceholder = toUsername == '' ? win.username + ':' : 'PM to ' + toUsername + ':';
  5433. document.getElementById(CHAT_INPUT_ID).placeholder = newTextPlaceholder;
  5434. handleScrolling(newChatPanel);
  5435. }
  5436.  
  5437. function clearChat(username)
  5438. {
  5439. if (username === '')
  5440. {
  5441. // clean server chat
  5442. chatHistory = [];
  5443. saveChatHistory();
  5444. }
  5445. else
  5446. {
  5447. // delete pms stored for that user
  5448. for (var i = 0; i < pmHistory.length; i++)
  5449. {
  5450. var data = pmHistory[i];
  5451. if (data.username == username)
  5452. {
  5453. pmHistory.splice(i, 1);
  5454. i--;
  5455. }
  5456. }
  5457. savePmHistory();
  5458. }
  5459. // clear pm-chat panel
  5460. var panel = getChatPanel(username);
  5461. while (panel.children.length > 0)
  5462. {
  5463. panel.removeChild(panel.children[0]);
  5464. }
  5465. msgChunkMap.delete(username);
  5466. return panel;
  5467. }
  5468.  
  5469. function closeChatTab(username)
  5470. {
  5471. // clear pm-chat panel and remove message-history
  5472. clearChat(username);
  5473. // remove pm-tab (and change tab if necessary)
  5474. var selectedTab = getSelectedTab();
  5475. var tab2Close = getChatTab(username, null);
  5476. if (selectedTab.dataset.username == username)
  5477. {
  5478. var generalTab = getChatTab('', SpecialTab.general);
  5479. changeChatTab(tab2Close, generalTab);
  5480. }
  5481. var tabContainer = tab2Close.parentElement;
  5482. tabContainer.removeChild(tab2Close);
  5483. }
  5484.  
  5485. function isDataPM(data)
  5486. {
  5487. return data.type === Type.pmSent || data.type === Type.pmReceived;
  5488. }
  5489.  
  5490. function colorizeMsg(username)
  5491. {
  5492. if (username == '')
  5493. {
  5494. return null;
  5495. }
  5496. if (!user2Color.has(username))
  5497. {
  5498. var color = void 0;
  5499. do {
  5500. var colorizer = settings.getSub(settings.KEY.colorizeChat, 'colorizer');
  5501. if (colorizer == 1)
  5502. {
  5503. color = colorGenerator.getRandom(
  5504. {
  5505. luminosity: 'light'
  5506. });
  5507. }
  5508. else if (colorizer == 2)
  5509. {
  5510. color = colorGenerator.getRandom(
  5511. {
  5512. luminosity: 'dark'
  5513. });
  5514. }
  5515. else
  5516. {
  5517. color = colorGenerator.getEquallyDistributed();
  5518. }
  5519. } while (usedColors.has(color));
  5520. user2Color.set(username, color);
  5521. usedColors.add(color);
  5522. addStyle("\n#" + CHAT_BOX_ID + "." + COLORIZE_CLASS + " .chat-msg[data-username=\"" + username + "\"]\n{\n\tbackground-color: " + color + ";\n}\n\t\t\t", 'name-color');
  5523. }
  5524. return user2Color.get(username);
  5525. }
  5526.  
  5527. function createMessageSegment(data)
  5528. {
  5529. var isThisPm = isDataPM(data);
  5530. var msgUsername = data.type === Type.pmSent ? win.username : data.username;
  5531. var history = isThisPm ? pmHistory : chatHistory;
  5532. var historyIndex = history.indexOf(data);
  5533. var isSameUser = null;
  5534. var isSameTime = null;
  5535. for (var i = historyIndex - 1; i >= 0 && (isSameUser === null || isSameTime === null); i--)
  5536. {
  5537. var dataBefore = history[i];
  5538. if (isThisPm === isDataPM(dataBefore))
  5539. {
  5540. if (isSameUser === null)
  5541. {
  5542. var beforeUsername = dataBefore.type == Type.pmSent ? win.username : dataBefore.username;
  5543. isSameUser = beforeUsername === msgUsername;
  5544. }
  5545. if (dataBefore.type != Type.reload)
  5546. {
  5547. isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(dataBefore.timestamp / 1000 / 60) === 0;
  5548. }
  5549. }
  5550. }
  5551. var d = new Date(data.timestamp);
  5552. var hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
  5553. var minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
  5554. var icon = CHAT_ICONS[data.icon] || getUnknownChatIcon(data.icon);
  5555. var tag = CHAT_TAGS[data.tag] ||
  5556. {
  5557. key: ''
  5558. , name: ''
  5559. };
  5560. var formattedMsg = data.msg
  5561. .replace(/<a href='(.+?)' target='_blank'>\1<\/a>/g, '$1')
  5562. .replace(/(https?:\/\/[^\s"<>]+)/g, '<a target="_blank" href="$1">$1</a>');
  5563. colorizeMsg(msgUsername);
  5564. var msgTitle = data.type == Type.reload ? 'Chat loaded on ' + d.toLocaleString(LOCALE, LOCALE_OPTIONS) : '';
  5565. var user = data.type === Type.serverMsg ? 'Server Message' : msgUsername;
  5566. var levelAppendix = data.type == Type.normal ? ' (' + data.userlevel + ')' : '';
  5567. var userTitle = data.tag != Tag.server ? tag.name : '';
  5568. return "<span class=\"chat-msg\" data-type=\"" + data.type + "\" data-tag=\"" + tag.key + "\" data-username=\"" + msgUsername + "\">"
  5569. + ("<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>")
  5570. + ("<span class=\"user\" data-name=\"" + msgUsername + "\" data-same-user=\"" + isSameUser + "\">")
  5571. + ("<span class=\"icon " + icon.key + "\" title=\"" + icon.title + "\"></span>")
  5572. + ("<span class=\"name chat-tag-" + tag.key + "\" title=\"" + userTitle + "\">" + user + levelAppendix + ":</span>")
  5573. + "</span>"
  5574. + ("<span class=\"msg\" title=\"" + msgTitle + "\">" + formattedMsg + "</span>")
  5575. + "</span>";
  5576. }
  5577.  
  5578. function add2Chat(data)
  5579. {
  5580. if (!chatInitialized)
  5581. {
  5582. return;
  5583. }
  5584. var isThisPm = isDataPM(data);
  5585. // don't mute pms (you can just ignore pm-tab if you like)
  5586. if (!isThisPm && isMuted(data.username))
  5587. {
  5588. return;
  5589. }
  5590. var userKey = isThisPm ? data.username : '';
  5591. if (isThisPm)
  5592. {
  5593. win.lastPMUser = data.username;
  5594. }
  5595. // username is 3-12 characters long
  5596. var chatbox = getChatPanel(userKey);
  5597. var msgChunk = msgChunkMap.get(userKey);
  5598. if (!msgChunk || msgChunk.children.length >= MSG_CHUNK_SIZE)
  5599. {
  5600. msgChunk = document.createElement('div');
  5601. msgChunk.className = 'msg-chunk';
  5602. msgChunkMap.set(userKey, msgChunk);
  5603. if (chatboxFragments != null)
  5604. {
  5605. if (!chatboxFragments.has(userKey))
  5606. {
  5607. chatboxFragments.set(userKey, document.createDocumentFragment());
  5608. }
  5609. chatboxFragments.get(userKey).appendChild(msgChunk);
  5610. }
  5611. else
  5612. {
  5613. chatbox.appendChild(msgChunk);
  5614. }
  5615. }
  5616. var tmp = document.createElement('templateWrapper');
  5617. tmp.innerHTML = createMessageSegment(data);
  5618. msgChunk.appendChild(tmp.children[0]);
  5619. handleScrolling(chatbox);
  5620. // add delay because handleScrolling is will set scrollTop delayed
  5621. setTimeout(function ()
  5622. {
  5623. var chatTab = getChatTab(userKey, isThisPm ? null : SpecialTab.general);
  5624. incrementNewCounter(chatTab);
  5625. });
  5626. }
  5627.  
  5628. function applyChatStyle()
  5629. {
  5630. 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");
  5631. }
  5632.  
  5633. function initColorizer(init)
  5634. {
  5635. if (init === void 0)
  5636. {
  5637. init = false;
  5638. }
  5639. var usernameList = user2Color && Array.from(user2Color.keys()) || [];
  5640. user2Color = new Map();
  5641. usedColors = new Set();
  5642. for (var _i = 0, reservedColors_1 = reservedColors; _i < reservedColors_1.length; _i++)
  5643. {
  5644. var color = reservedColors_1[_i];
  5645. usedColors.add(color);
  5646. }
  5647. var colorStyle = getStyle('name-color');
  5648. colorStyle.innerHTML = '';
  5649. for (var _a = 0, usernameList_1 = usernameList; _a < usernameList_1.length; _a++)
  5650. {
  5651. var username = usernameList_1[_a];
  5652. colorizeMsg(username);
  5653. }
  5654. if (init)
  5655. {
  5656. settings.observeSub(settings.KEY.colorizeChat, 'colorizer', function ()
  5657. {
  5658. return initColorizer();
  5659. });
  5660. }
  5661. }
  5662.  
  5663. function addIntelligentScrolling()
  5664. {
  5665. // add checkbox instead of button for toggling auto scrolling
  5666. var btn = document.querySelector('input[value="Toggle Autoscroll"]');
  5667. var btnParent = btn.parentElement;
  5668. var checkboxId = 'chat-toggle-autoscroll';
  5669. // create checkbox
  5670. var toggleCheckbox = document.createElement('input');
  5671. toggleCheckbox.type = 'checkbox';
  5672. toggleCheckbox.id = checkboxId;
  5673. toggleCheckbox.checked = true;
  5674. // create label
  5675. var toggleLabel = document.createElement('label');
  5676. toggleLabel.htmlFor = checkboxId;
  5677. toggleLabel.textContent = 'Autoscroll';
  5678. btnParent.insertBefore(toggleCheckbox, btn);
  5679. btnParent.insertBefore(toggleLabel, btn);
  5680. btn.style.display = 'none';
  5681. var chatArea = document.getElementById(GENERAL_CHAT_DIV_ID);
  5682. var showScrollTextTimeout = null;
  5683.  
  5684. function setAutoScrolling(value, full)
  5685. {
  5686. if (full === void 0)
  5687. {
  5688. full = false;
  5689. }
  5690. if (win.isAutoScrolling != value)
  5691. {
  5692. toggleCheckbox.checked = value;
  5693. win.isAutoScrolling = value;
  5694. var icon_2 = 'none';
  5695. var color_1 = value ? 'lime' : 'red';
  5696. var text_1 = (value ? 'En' : 'Dis') + 'abled' + (full ? ' Autoscroll' : '');
  5697. if (full)
  5698. {
  5699. if (showScrollTextTimeout)
  5700. {
  5701. win.clearTimeout(showScrollTextTimeout);
  5702. }
  5703. showScrollTextTimeout = win.setTimeout(function ()
  5704. {
  5705. return win.scrollText(icon_2, color_1, text_1);
  5706. }, 300);
  5707. }
  5708. else
  5709. {
  5710. win.scrollText(icon_2, color_1, text_1);
  5711. }
  5712. setNewCounter(getSelectedTab(), 0, true);
  5713. return true;
  5714. }
  5715. return false;
  5716. }
  5717. toggleCheckbox.addEventListener('change', function ()
  5718. {
  5719. setAutoScrolling(this.checked);
  5720. if (this.checked && settings.get(settings.KEY.intelligentScrolling))
  5721. {
  5722. chatArea.scrollTop = chatArea.scrollHeight - chatArea.clientHeight;
  5723. }
  5724. });
  5725. var placeholderTemplate = document.createElement('div');
  5726. placeholderTemplate.className = 'placeholder';
  5727. var childStore = new WeakMap();
  5728.  
  5729. function scrollHugeChat()
  5730. {
  5731. // # of children
  5732. var chunkNum = chatArea.children.length;
  5733. // start chunk hiding at a specific amount of chunks
  5734. if (chunkNum < CHUNK_HIDING_MIN_CHUNKS)
  5735. {
  5736. return;
  5737. }
  5738. var visibleTop = chatArea.scrollTop;
  5739. var visibleBottom = visibleTop + chatArea.clientHeight;
  5740. var referenceTop = visibleTop - win.innerHeight;
  5741. var referenceBottom = visibleBottom + win.innerHeight;
  5742. var top = 0;
  5743. // never hide the last element since its size may change at any time when a new message gets appended
  5744. for (var i = 0; i < chunkNum - 1; i++)
  5745. {
  5746. var child = chatArea.children[i];
  5747. var height = child.clientHeight;
  5748. var bottom = top + height;
  5749. var isVisible = top >= referenceTop && top <= referenceBottom
  5750. || bottom >= referenceTop && bottom <= referenceBottom
  5751. || top < referenceTop && bottom > referenceBottom;
  5752. var isPlaceholder = child.classList.contains('placeholder');
  5753. if (!isVisible && !isPlaceholder)
  5754. {
  5755. var newPlaceholder = placeholderTemplate.cloneNode(false);
  5756. newPlaceholder.style.height = height + 'px';
  5757. chatArea.replaceChild(newPlaceholder, child);
  5758. childStore.set(newPlaceholder, child);
  5759. }
  5760. else if (isVisible && isPlaceholder)
  5761. {
  5762. var oldChild = childStore.get(child);
  5763. chatArea.replaceChild(oldChild, child);
  5764. childStore.delete(child);
  5765. }
  5766. top = bottom;
  5767. }
  5768. }
  5769. var delayedScrollStart = null;
  5770. var delayedScrollTimeout = null;
  5771. // does not consider pm tabs; may be changed in a future version?
  5772. chatArea.addEventListener('scroll', function ()
  5773. {
  5774. if (settings.get(settings.KEY.intelligentScrolling))
  5775. {
  5776. var scrolled2Bottom = (chatArea.scrollTop + chatArea.clientHeight) >= chatArea.scrollHeight - 1;
  5777. setAutoScrolling(scrolled2Bottom, true);
  5778. }
  5779. var n = now();
  5780. if (delayedScrollStart == null)
  5781. {
  5782. delayedScrollStart = n;
  5783. }
  5784. if (delayedScrollStart + 300 > n)
  5785. {
  5786. if (delayedScrollTimeout)
  5787. {
  5788. win.clearTimeout(delayedScrollTimeout);
  5789. }
  5790. delayedScrollTimeout = win.setTimeout(function ()
  5791. {
  5792. delayedScrollStart = null;
  5793. delayedScrollTimeout = null;
  5794. scrollHugeChat();
  5795. }, 50);
  5796. }
  5797. });
  5798. }
  5799.  
  5800. function getSelectedTab()
  5801. {
  5802. return document.querySelector('#' + CHAT_TABS_ID + ' .chat-tab.selected');
  5803. }
  5804.  
  5805. function getSelectedTabUsername()
  5806. {
  5807. var selectedTab = getSelectedTab();
  5808. return selectedTab.dataset.username || '';
  5809. }
  5810.  
  5811. function clickChatTab(newTab)
  5812. {
  5813. var oldTab = getSelectedTab();
  5814. if (newTab == oldTab)
  5815. {
  5816. return;
  5817. }
  5818. changeChatTab(oldTab, newTab);
  5819. }
  5820.  
  5821. function clickCloseChatTab(tab)
  5822. {
  5823. var username = tab.dataset.username || '';
  5824. var chatPanel = getChatPanel(username);
  5825. if (chatPanel.children.length === 0
  5826. || confirm("Do you want to close the pm tab of \"" + username + "\"?"))
  5827. {
  5828. closeChatTab(username);
  5829. }
  5830. }
  5831.  
  5832. function checkSetting(init)
  5833. {
  5834. if (init === void 0)
  5835. {
  5836. init = false;
  5837. }
  5838. var enabled = settings.get(settings.KEY.useNewChat);
  5839. // dis-/enable chat tabs
  5840. var chatTabs = document.getElementById(CHAT_TABS_ID);
  5841. chatTabs.style.display = enabled ? '' : 'none';
  5842. // dis-/enable checkbox for intelligent scrolling
  5843. var intelScrollId = 'chat-toggle-intelligent-scroll';
  5844. var input = document.getElementById(intelScrollId);
  5845. if (input)
  5846. {
  5847. input.style.display = enabled ? '' : 'none';
  5848. }
  5849. var label = document.querySelector('label[for="' + intelScrollId + '"]');
  5850. if (label)
  5851. {
  5852. label.style.display = enabled ? '' : 'none';
  5853. }
  5854. // virtually click on a tab
  5855. var defaultTab = getChatTab('', SpecialTab.default);
  5856. var generalTab = getChatTab('', SpecialTab.general);
  5857. clickChatTab(enabled ? generalTab : defaultTab);
  5858. if (init)
  5859. {
  5860. settings.observe(settings.KEY.useNewChat, function ()
  5861. {
  5862. return checkSetting(false);
  5863. });
  5864. }
  5865. }
  5866.  
  5867. function addChatTabs()
  5868. {
  5869. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  5870. var chatTabs = document.createElement('div');
  5871. chatTabs.id = CHAT_TABS_ID;
  5872. chatTabs.addEventListener('click', function (event)
  5873. {
  5874. var newTab = event.target;
  5875. if (newTab.classList.contains('close'))
  5876. {
  5877. return clickCloseChatTab(newTab.parentElement);
  5878. }
  5879. if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler'))
  5880. {
  5881. return;
  5882. }
  5883. clickChatTab(newTab);
  5884. });
  5885. chatBoxArea.appendChild(chatTabs);
  5886. // default tab (for disabled new chat)
  5887. getChatTab('', SpecialTab.default);
  5888. // general server chat
  5889. var generalTab = getChatTab('', SpecialTab.general);
  5890. generalTab.textContent = 'Server';
  5891. getChatPanel('');
  5892. getChatTab('', SpecialTab.filler);
  5893. var _sendChat = win.sendChat;
  5894. win.sendChat = function (inputEl)
  5895. {
  5896. var msg = inputEl.value;
  5897. var selectedTab = document.querySelector('.chat-tab.selected');
  5898. if (selectedTab.dataset.username != '' && msg[0] != '/')
  5899. {
  5900. inputEl.value = '/pm ' + (selectedTab.dataset.username || '').replace(/ /g, '_') + ' ' + msg;
  5901. }
  5902. _sendChat(inputEl);
  5903. };
  5904. }
  5905.  
  5906. function switch2PmTab(username)
  5907. {
  5908. var newTab = getChatTab(username, null);
  5909. clickChatTab(newTab);
  5910. }
  5911.  
  5912. function notifyPm(data)
  5913. {
  5914. notifications.event('Message from "' + data.username + '"'
  5915. , {
  5916. body: data.msg
  5917. , onclick: function ()
  5918. {
  5919. return switch2PmTab(data.username);
  5920. }
  5921. , whenActive: getSelectedTab().dataset.username != data.username
  5922. });
  5923. }
  5924.  
  5925. function checkMentionAndKeywords(data)
  5926. {
  5927. var lowerMsg = data.msg.toLowerCase();
  5928. var usernameRegex = new RegExp('\\b' + win.username + '\\b', 'i');
  5929. if (settings.getSub(settings.KEY.showNotifications, 'mention') && usernameRegex.test(lowerMsg))
  5930. // if (lowerMsg.indexOf(win.username) > -1)
  5931. {
  5932. notifications.event('You\'ve been mentioned'
  5933. , {
  5934. body: data.msg
  5935. });
  5936. }
  5937. var match = [];
  5938. for (var _i = 0, keywordList_1 = chat.keywordList; _i < keywordList_1.length; _i++)
  5939. {
  5940. var keyword = keywordList_1[_i];
  5941. var regex = new RegExp('\\b' + keyword + '\\b', 'i');
  5942. if (regex.test(lowerMsg))
  5943. // if (lowerMsg.indexOf(keyword) > -1)
  5944. {
  5945. match.push(keyword);
  5946. }
  5947. }
  5948. if (settings.getSub(settings.KEY.showNotifications, 'keyword') && match.length > 0)
  5949. {
  5950. notifications.event('Keyword: "' + match.join('", "') + '"'
  5951. , {
  5952. body: data.msg
  5953. });
  5954. }
  5955. }
  5956. var addToChatBox_ = null;
  5957.  
  5958. function newAddToChatBox(username, icon, tag, msg, isPM)
  5959. {
  5960. var data = processChatData(username, icon, tag, msg, isPM);
  5961. var isThisSpam = false;
  5962. if (isDataPM(data))
  5963. {
  5964. if (data.type == Type.pmSent)
  5965. {
  5966. switch2PmTab(data.username);
  5967. }
  5968. else
  5969. {
  5970. notifyPm(data);
  5971. }
  5972. }
  5973. else
  5974. {
  5975. isThisSpam = settings.get(settings.KEY.enableSpamDetection) && isSpam(data);
  5976. if (!isThisSpam && data.username != win.username)
  5977. {
  5978. // check mentioning and keywords only for non-pms and only for messages from other players
  5979. checkMentionAndKeywords(data);
  5980. }
  5981. }
  5982. if (isThisSpam)
  5983. {
  5984. console.info('detected spam:', data);
  5985. }
  5986. else
  5987. {
  5988. add2ChatHistory(data);
  5989. add2Chat(data);
  5990. }
  5991. var fn = addToChatBox_ == null ? win.addToChatBox : addToChatBox_;
  5992. fn(username, icon, tag, msg, isPM);
  5993. }
  5994. chat.newAddToChatBox = newAddToChatBox;
  5995.  
  5996. function openPmTab(username)
  5997. {
  5998. if (username == win.username || username == '')
  5999. {
  6000. return;
  6001. }
  6002. var userTab = getChatTab(username, null);
  6003. clickChatTab(userTab);
  6004. var input = document.getElementById(CHAT_INPUT_ID);
  6005. input.focus();
  6006. }
  6007.  
  6008. function newChat()
  6009. {
  6010. addChatTabs();
  6011. applyChatStyle();
  6012. initColorizer(true);
  6013. addToChatBox_ = win.addToChatBox;
  6014. win.addToChatBox = newAddToChatBox;
  6015. chatInitialized = true;
  6016. var chatbox = document.getElementById(CHAT_BOX_ID);
  6017. chatbox.addEventListener('click', function (event)
  6018. {
  6019. var target = event.target;
  6020. var userEl = target && target.parentElement;
  6021. if (!target || !userEl || !target.classList.contains('name') || !userEl.classList.contains('user'))
  6022. {
  6023. return;
  6024. }
  6025. if (userEl.dataset.sameUser != 'true')
  6026. {
  6027. openPmTab(userEl.dataset.name || '');
  6028. }
  6029. });
  6030. chatbox.addEventListener('mouseover', function (event)
  6031. {
  6032. var target = event.target;
  6033. if (!target.classList.contains('timestamp') || !target.dataset.timestamp)
  6034. {
  6035. return;
  6036. }
  6037. var timestamp = parseInt(target.dataset.timestamp || '0', 10);
  6038. target.dataset.fulltime = (new Date(timestamp)).toLocaleDateString(LOCALE, LOCALE_OPTIONS);
  6039. target.dataset.timestamp = '';
  6040. });
  6041. // add context menu
  6042. var contextmenu = document.createElement('ul');
  6043. contextmenu.id = CONTEXTMENU_ID;
  6044. contextmenu.style.display = 'none';
  6045. 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>";
  6046. document.body.appendChild(contextmenu);
  6047. win.$(contextmenu).menu(
  6048. {
  6049. items: '> :not(.ui-widget-header)'
  6050. });
  6051. var nameListEl = contextmenu.querySelector('.name');
  6052. var nameDivEl = nameListEl.firstElementChild;
  6053. var muteEl = contextmenu.querySelector('.mute');
  6054. var unmuteEl = contextmenu.querySelector('.unmute');
  6055. chatbox.addEventListener('contextmenu', function (event)
  6056. {
  6057. var target = event.target;
  6058. var userEl = target && target.parentElement;
  6059. if (!userEl || !userEl.classList.contains('user'))
  6060. {
  6061. return;
  6062. }
  6063. var username = userEl.dataset.name;
  6064. // ignore clicks on server messages or other special messages
  6065. if (!username || userEl.dataset.sameUser == 'true')
  6066. {
  6067. return;
  6068. }
  6069. contextmenu.style.left = event.clientX + 'px';
  6070. contextmenu.style.top = event.clientY + 'px';
  6071. contextmenu.style.display = '';
  6072. contextmenu.dataset.username = username;
  6073. nameDivEl.textContent = username;
  6074. var isMuted = win.mutedPeople.indexOf(username) !== -1;
  6075. muteEl.style.display = isMuted ? 'none' : '';
  6076. unmuteEl.style.display = isMuted ? '' : 'none';
  6077. event.stopPropagation();
  6078. event.preventDefault();
  6079. });
  6080. // add click listener for context menu and stop propagation
  6081. contextmenu.addEventListener('click', function (event)
  6082. {
  6083. var target = event.target;
  6084. event.stopPropagation();
  6085. while (target && target.id != CONTEXTMENU_ID && target.tagName != 'LI')
  6086. {
  6087. target = target.parentElement;
  6088. }
  6089. if (!target || target.id == CONTEXTMENU_ID)
  6090. {
  6091. return;
  6092. }
  6093. var username = contextmenu.dataset.username || '';
  6094. if (target.classList.contains('open-pm'))
  6095. {
  6096. openPmTab(username);
  6097. }
  6098. else if (target.classList.contains('stats'))
  6099. {
  6100. win.lookup(username);
  6101. }
  6102. else if (target.classList.contains('mute'))
  6103. {
  6104. if (username == '')
  6105. {
  6106. return;
  6107. }
  6108. win.mutedPeople.push(username);
  6109. win.scrollText('none', 'lime', '<em>' + username + '</em> muted');
  6110. }
  6111. else if (target.classList.contains('unmute'))
  6112. {
  6113. if (username == '')
  6114. {
  6115. return;
  6116. }
  6117. var index = win.mutedPeople.indexOf(username);
  6118. if (index !== -1)
  6119. {
  6120. win.mutedPeople.splice(index, 1);
  6121. }
  6122. win.scrollText('none', 'red', '<em>' + username + '</em> unmuted');
  6123. }
  6124. else
  6125. {
  6126. return;
  6127. }
  6128. contextmenu.style.display = 'none';
  6129. });
  6130. // add click listener to hide context menu
  6131. document.addEventListener('click', function (event)
  6132. {
  6133. if (contextmenu.style.display != 'none')
  6134. {
  6135. contextmenu.style.display = 'none';
  6136. }
  6137. });
  6138. win.addEventListener('contextmenu', function (event)
  6139. {
  6140. if (contextmenu.style.display != 'none')
  6141. {
  6142. contextmenu.style.display = 'none';
  6143. }
  6144. });
  6145. // handle settings
  6146. var showSettings = [settings.KEY.showTimestamps, settings.KEY.showIcons, settings.KEY.showTags];
  6147.  
  6148. function setShowSetting(key)
  6149. {
  6150. var enabled = settings.get(key);
  6151. chatbox.classList[enabled ? 'add' : 'remove'](settings.KEY[key]);
  6152. }
  6153. for (var _i = 0, showSettings_1 = showSettings; _i < showSettings_1.length; _i++)
  6154. {
  6155. var key = showSettings_1[_i];
  6156. setShowSetting(key);
  6157. settings.observe(key, function (k)
  6158. {
  6159. return setShowSetting(k);
  6160. });
  6161. }
  6162. }
  6163.  
  6164. function addCommandSuggester()
  6165. {
  6166. var input = document.getElementById(CHAT_INPUT_ID);
  6167. input.addEventListener('keyup', function (event)
  6168. {
  6169. if (event.key == 'Backspace' || event.key == 'Delete' || event.key == 'Enter' || event.key == 'Tab'
  6170. || input.selectionStart != input.selectionEnd
  6171. || input.selectionStart != input.value.length
  6172. || !input.value.startsWith('/'))
  6173. {
  6174. return;
  6175. }
  6176. var value = input.value.substr(1);
  6177. for (var _i = 0, COMMANDS_1 = COMMANDS; _i < COMMANDS_1.length; _i++)
  6178. {
  6179. var cmd = COMMANDS_1[_i];
  6180. if (cmd.startsWith(value))
  6181. {
  6182. input.value = '/' + cmd;
  6183. input.selectionStart = 1 + value.length;
  6184. input.selectionEnd = input.value.length;
  6185. break;
  6186. }
  6187. }
  6188. });
  6189. }
  6190.  
  6191. function addOwnCommands()
  6192. {
  6193. COMMANDS.push(TUTORIAL_CMD);
  6194.  
  6195. function processOwnCommands(value)
  6196. {
  6197. if (!value.startsWith('/'))
  6198. {
  6199. return value;
  6200. }
  6201. var msgPrefix = '/';
  6202. var msg = value.substr(1);
  6203. if (msg.startsWith('pm'))
  6204. {
  6205. var split = msg.split(' ');
  6206. msgPrefix = '/' + split.slice(0, 2).join(' ') + ' ';
  6207. msg = split.slice(2).join(' ');
  6208. }
  6209. if (msg.startsWith(CLEAR_CMD))
  6210. {
  6211. // clear current chat (pm chat, or general chat)
  6212. var username = getSelectedTabUsername();
  6213. clearChat(username);
  6214. }
  6215. else if (msg.startsWith(TUTORIAL_CMD))
  6216. {
  6217. // thanks aguyd (https://greasyfork.org/forum/profile/aguyd) for the idea
  6218. var name_2 = msg.substr(TUTORIAL_CMD.length).trim();
  6219. msgPrefix = '';
  6220. msg = 'https://www.reddit.com/r/DiamondHunt/comments/5vrufh/diamond_hunt_2_starter_faq/';
  6221. if (name_2.length != 0)
  6222. {
  6223. // maybe add '@' before the name?
  6224. msg = name_2 + ', ' + msg;
  6225. }
  6226. }
  6227. return msgPrefix + msg;
  6228. }
  6229. var _sendChat = win.sendChat;
  6230. win.sendChat = function (inputEl)
  6231. {
  6232. inputEl.value = processOwnCommands(inputEl.value);
  6233. _sendChat(inputEl);
  6234. };
  6235. }
  6236.  
  6237. function checkColorize(init)
  6238. {
  6239. if (init === void 0)
  6240. {
  6241. init = false;
  6242. }
  6243. var chatDiv = document.getElementById(CHAT_BOX_ID);
  6244. chatDiv.classList[settings.get(settings.KEY.colorizeChat) ? 'add' : 'remove'](COLORIZE_CLASS);
  6245. if (init)
  6246. {
  6247. settings.observe(settings.KEY.colorizeChat, function ()
  6248. {
  6249. return checkColorize(false);
  6250. });
  6251. }
  6252. }
  6253.  
  6254. function init()
  6255. {
  6256. newChat();
  6257. addIntelligentScrolling();
  6258. addCommandSuggester();
  6259. addOwnCommands();
  6260. checkColorize(true);
  6261. checkSetting(true);
  6262. var _enlargeChat = win.enlargeChat;
  6263. var chatBoxArea = document.getElementById(CHAT_BOX_ID);
  6264.  
  6265. function setChatBoxHeight(height)
  6266. {
  6267. var defaultChat = document.getElementById(DEFAULT_CHAT_DIV_ID);
  6268. defaultChat.style.height = height;
  6269. var generalChat = document.getElementById(GENERAL_CHAT_DIV_ID);
  6270. generalChat.style.height = height;
  6271. var chatDivs = chatBoxArea.querySelectorAll('div[id^="' + PM_CHAT_DIV_PREFIX + '"]');
  6272. for (var i = 0; i < chatDivs.length; i++)
  6273. {
  6274. chatDivs[i].style.height = height;
  6275. }
  6276. }
  6277. win.enlargeChat = function (enlargeB)
  6278. {
  6279. _enlargeChat(enlargeB);
  6280. var defaultChatDiv = document.getElementById(DEFAULT_CHAT_DIV_ID);
  6281. var height = defaultChatDiv.style.height;
  6282. store.set('chat.height', height);
  6283. setChatBoxHeight(height);
  6284. handleScrolling(defaultChatDiv);
  6285. };
  6286. setChatBoxHeight(store.get('chat.height'));
  6287. // add history to chat
  6288. // TEMP >>>
  6289. // move pm entries to pm history
  6290. var changed = false;
  6291. for (var i = 0; i < chatHistory.length; i++)
  6292. {
  6293. var data = chatHistory[i];
  6294. if (isDataPM(data))
  6295. {
  6296. chatHistory.splice(i, 1);
  6297. i--;
  6298. pmHistory.push(data);
  6299. changed = true;
  6300. }
  6301. }
  6302. if (changed)
  6303. {
  6304. saveChatHistory();
  6305. savePmHistory();
  6306. }
  6307. // TEMP <<<
  6308. chatHistory.forEach(function (d)
  6309. {
  6310. return add2Chat(d);
  6311. });
  6312. pmHistory.forEach(function (d)
  6313. {
  6314. return add2Chat(d);
  6315. });
  6316. if (chatboxFragments)
  6317. {
  6318. chatboxFragments.forEach(function (fragment, key)
  6319. {
  6320. var chatbox = getChatPanel(key);
  6321. chatbox.appendChild(fragment);
  6322. });
  6323. chatboxFragments = null;
  6324. }
  6325. // reset the new counter for all tabs
  6326. var tabs = document.querySelectorAll('.chat-tab');
  6327. for (var i = 0; i < tabs.length; i++)
  6328. {
  6329. setNewCounter(tabs[i], 0, true);
  6330. }
  6331. }
  6332. chat.init = init;
  6333. var _a;
  6334. })(chat || (chat = {}));
  6335.  
  6336. /**
  6337. * hopefully only temporary fixes
  6338. */
  6339. var temporaryFixes;
  6340. (function (temporaryFixes)
  6341. {
  6342. temporaryFixes.name = 'temporaryFixes';
  6343. // update spells being clickable in combat
  6344. function setSpellsClickable()
  6345. {
  6346. var spellbox = document.getElementById('fight-spellboox');
  6347. if (spellbox)
  6348. {
  6349. for (var i = 0; i < spellbox.children.length; i++)
  6350. {
  6351. var child = spellbox.children.item(i);
  6352. if (!win.isInCombat() && child.hasAttribute('onclick'))
  6353. {
  6354. child.dataset.onclick = child.getAttribute('onclick') || '';
  6355. child.removeAttribute('onclick');
  6356. }
  6357. else if (win.isInCombat() && !!child.dataset.onclick)
  6358. {
  6359. child.setAttribute('onclick', child.dataset.onclick || '');
  6360. child.dataset.onclick = '';
  6361. }
  6362. }
  6363. }
  6364. }
  6365. // warn before unloading/reloading the tab if combat is in progress
  6366. function combatWarnOnUnload()
  6367. {
  6368. if (!win.isInCombat())
  6369. {
  6370. win.onbeforeunload = null;
  6371. }
  6372. else
  6373. {
  6374. if (win.onbeforeunload == null)
  6375. {
  6376. win.onbeforeunload = function ()
  6377. {
  6378. return 'You are in a fight!';
  6379. };
  6380. }
  6381. }
  6382. }
  6383.  
  6384. function fixCombatCountdown()
  6385. {
  6386. var el = document.getElementById('combat-countdown');
  6387. if (!el)
  6388. {
  6389. return;
  6390. }
  6391. if (win.isInCombat())
  6392. {
  6393. el.style.display = '';
  6394. var visible = win.combatCommenceTimer != 0;
  6395. el.style.visibility = visible ? '' : 'hidden';
  6396. }
  6397. }
  6398. // fix exhaustion timer and updating brewing and cooking recipes
  6399. function fixExhaustionTimer()
  6400. {
  6401. if (document.getElementById('tab-container-combat').style.display != 'none')
  6402. {
  6403. win.combatNotFightingTick();
  6404. }
  6405. }
  6406.  
  6407. function fixClientGameLoop()
  6408. {
  6409. var _clientGameLoop = win.clientGameLoop;
  6410. win.clientGameLoop = function ()
  6411. {
  6412. _clientGameLoop();
  6413. //setSpellsClickable();
  6414. combatWarnOnUnload();
  6415. fixCombatCountdown();
  6416. fixExhaustionTimer();
  6417. };
  6418. }
  6419. // fix elements of scrollText (e.g. when joining the game and receiving xp at that moment)
  6420. function fixScroller()
  6421. {
  6422. var textEls = document.querySelectorAll('div.scroller');
  6423. for (var i = 0; i < textEls.length; i++)
  6424. {
  6425. var scroller = textEls[i];
  6426. if (scroller.style.position != 'absolute')
  6427. {
  6428. scroller.style.display = 'none';
  6429. }
  6430. }
  6431. }
  6432. // fix style of tooltips
  6433. function fixTooltipStyle()
  6434. {
  6435. addStyle("\nbody > div.tooltip > h2:first-child\n{\n\tmargin-top: 0;\n\tfont-size: 20pt;\n\tfont-weight: normal;\n}\n\t\t");
  6436. }
  6437. // fix buiulding magic table dynamically
  6438. function fixRefreshingMagicRecipes()
  6439. {
  6440. // define missing properties for checking the needed materials
  6441. win.enchantStargemPotionMagic = 0;
  6442. win.changeWeatherMagic = 0;
  6443. win.refreshLoadMagicTable = false;
  6444. var _processMagicTab = win.processMagicTab;
  6445. win.processMagicTab = function ()
  6446. {
  6447. var _refreshLoadCraftingTable = win.refreshLoadCraftingTable;
  6448. win.refreshLoadCraftingTable = win.refreshLoadMagicTable;
  6449. _processMagicTab();
  6450. win.refreshLoadCraftingTable = _refreshLoadCraftingTable;
  6451. if (win.magicPage3 == 1)
  6452. {
  6453. win.showMateriesNeededAndLevelLabelsMagic('enchantStargemPotion');
  6454. win.showMateriesNeededAndLevelLabelsMagic('beam');
  6455. win.showMateriesNeededAndLevelLabelsMagic('changeWeather');
  6456. }
  6457. };
  6458. }
  6459.  
  6460. function moveItemBox(itemKey, targetElId, color1, color2)
  6461. {
  6462. var itemBox = document.getElementById('item-box-' + itemKey);
  6463. var targetContainer = document.getElementById(targetElId);
  6464. targetContainer.appendChild(itemBox);
  6465. // remove event listeners before binding the tooltip to it
  6466. var $itemBox = win.$(itemBox);
  6467. $itemBox.off('mouseover').off('mouseleave');
  6468. itemBox.title = '';
  6469. // bind tooltip to item box
  6470. ensureTooltip('ingredient-secondary', itemBox);
  6471. // change color
  6472. itemBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  6473. $itemBox
  6474. .mouseover(function ()
  6475. {
  6476. itemBox.style.background = 'none';
  6477. itemBox.style.backgroundColor = color2;
  6478. })
  6479. .mouseleave(function ()
  6480. {
  6481. itemBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
  6482. });
  6483. }
  6484. // move the strange leaf to brewing tab (thanks lasse_brus for this idea)
  6485. function moveStrangeLeafs()
  6486. {
  6487. //moveItemBox('strangeBlueLeaf', 'tab-sub-container-brewing', '#800080', '#990099');
  6488. //moveItemBox('strangePinkLeaf', 'tab-sub-container-brewing', '#800080', '#990099');
  6489. }
  6490. // fix height of map item
  6491. function fixTreasureMap()
  6492. {
  6493. var mapBox = document.getElementById('item-box-treasureMap');
  6494. var numSpan = mapBox.lastElementChild;
  6495. numSpan.style.display = '';
  6496. numSpan.style.visibility = 'hidden';
  6497. }
  6498. // fix wobbling tree places on hover (in wood cutting)
  6499. function fixWoodcutting()
  6500. {
  6501. addStyle("\nimg.woodcutting-tree-img\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  6502. }
  6503. // fix wobbling quest rows on hover (in quest book)
  6504. function fixQuestBook()
  6505. {
  6506. addStyle("\n#table-quest-list tr\n{\n\tborder: 1px solid transparent;\n}\n\t\t");
  6507. }
  6508.  
  6509. function fixScrollImages()
  6510. {
  6511. function fixIcon(icon)
  6512. {
  6513. return icon + (icon != 'none' && !/\..{3,4}$/.test(icon) ? '.png' : '');
  6514. }
  6515. var _scrollTextHitSplat = win.scrollTextHitSplat;
  6516. win.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  6517. {
  6518. _scrollTextHitSplat(fixIcon(icon), color, text, elId, cbType);
  6519. };
  6520. var _scrollText = win.scrollText;
  6521. win.scrollText = function (icon, color, text)
  6522. {
  6523. _scrollText(fixIcon(icon), color, text);
  6524. };
  6525. }
  6526.  
  6527. function fixQuest8BraveryRecipe()
  6528. {
  6529. observer.add([
  6530. 'quest8'
  6531. , 'braveryPotion'
  6532. ], function ()
  6533. {
  6534. var show = win.quest8 > 0 && win.braveryPotion == 0;
  6535. var recipe = document.getElementById('brewing-braveryPotion');
  6536. if (recipe)
  6537. {
  6538. recipe.style.display = show ? '' : 'none';
  6539. }
  6540. });
  6541. }
  6542.  
  6543. function fixHitText()
  6544. {
  6545. win.scrollTextHitSplat = function (icon, color, text, elId, cbType)
  6546. {
  6547. var imgTag = icon != 'none' ? "<img src=\"images/" + icon + "\" class=\"image-icon-50\" />" : '';
  6548. var elementChosen = document.getElementById(elId);
  6549. if (!elementChosen)
  6550. {
  6551. return;
  6552. }
  6553. var rect = elementChosen.getBoundingClientRect();
  6554. var xCoord = (rect.left + rect.right) / 2;
  6555. var yCoord = (rect.bottom + rect.top) / 2;
  6556. var extraStyle = '';
  6557. if (cbType == 'melee')
  6558. {
  6559. extraStyle = 'border: 1px solid red; background-color: #4d0000;';
  6560. }
  6561. else if (cbType == 'heal')
  6562. {
  6563. extraStyle = 'border: 1px solid green; background-color: lime;';
  6564. }
  6565. var $elementToAppend = win.$("<div class=\"scroller\" style=\"" + extraStyle + " color: " + color + "; position: fixed;\">" + imgTag + text + "</div>").appendTo('body');
  6566. if (xCoord == 0 && yCoord == 0)
  6567. {
  6568. var tab = document.getElementById('tab-container-bar-combat');
  6569. var tabRect = tab.getBoundingClientRect();
  6570. var boxRect = $elementToAppend.get(0).getBoundingClientRect();
  6571. xCoord = elId == 'img-hero' ? (tabRect.left - boxRect.width) : tabRect.right;
  6572. yCoord = tabRect.top;
  6573. }
  6574. $elementToAppend
  6575. .css(
  6576. {
  6577. left: xCoord
  6578. , top: yCoord
  6579. })
  6580. .animate(
  6581. {
  6582. top: '-=50px'
  6583. }, function ()
  6584. {
  6585. return $elementToAppend.fadeOut(1000, function ()
  6586. {
  6587. return $elementToAppend.remove();
  6588. });
  6589. });
  6590. };
  6591. }
  6592.  
  6593. function fixBoatTooltips()
  6594. {
  6595. var boatBox = document.getElementById('item-box-boundRowBoat');
  6596. var boatTooltip = boatBox && document.getElementById(boatBox.dataset.tooltipId || '');
  6597. var tooltipParent = boatTooltip && boatTooltip.parentElement;
  6598. if (!boatBox || !boatTooltip || !tooltipParent)
  6599. {
  6600. return;
  6601. }
  6602.  
  6603. function setTripDuration(durationEl, boatKey)
  6604. {
  6605. var durationStr = TRIP_DURATION.hasOwnProperty(boatKey) ? TRIP_DURATION[boatKey].toString(10) : '?';
  6606. durationEl.innerHTML = "<strong>Trip duration:</strong> " + durationStr + " hours";
  6607. }
  6608. boatTooltip.id = boatBox.dataset.tooltipId = 'tooltip-boundRowBoat';
  6609. boatTooltip.appendChild(document.createElement('br'));
  6610. var boatDuration = document.createElement('span');
  6611. boatDuration.className = 'trip-duration';
  6612. setTripDuration(boatDuration, 'rowBoat');
  6613. boatTooltip.appendChild(boatDuration);
  6614. for (var _i = 0, BOAT_LIST_1 = BOAT_LIST; _i < BOAT_LIST_1.length; _i++)
  6615. {
  6616. var boatKey = BOAT_LIST_1[_i];
  6617. var boundKey = getBoundKey(boatKey);
  6618. var itemBox = document.getElementById('item-box-' + boundKey);
  6619. if (!itemBox)
  6620. {
  6621. continue;
  6622. }
  6623. var tooltip = document.getElementById('tooltip-' + boundKey);
  6624. if (!tooltip)
  6625. {
  6626. tooltip = boatTooltip.cloneNode(true);
  6627. tooltip.id = 'tooltip-' + boundKey;
  6628. var header = tooltip.firstElementChild;
  6629. header.textContent = capitalize(split2Words(boatKey));
  6630. tooltipParent.appendChild(tooltip);
  6631. itemBox.dataset.tooltipId = 'tooltip-' + boundKey;
  6632. }
  6633. var durationEl = tooltip.getElementsByClassName('trip-duration').item(0);
  6634. if (durationEl)
  6635. {
  6636. setTripDuration(durationEl, boatKey);
  6637. }
  6638. }
  6639. }
  6640.  
  6641. function fixAlignments()
  6642. {
  6643. 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");
  6644. }
  6645. function fixFontSize11Tabs()
  6646. {
  6647. var tabKey = ['items', 'skills', 'mining', 'crafting', 'woodcutting', 'farming', 'brewing', 'combat', 'magic', 'cooking', 'shop'];
  6648. for (var i = 0; i < tabKey.length; i++)
  6649. {
  6650. var tab = document.getElementById('tab-container-bar-'+ tabKey[i] +'-label');
  6651. tab.style.fontSize = "15px";
  6652. }
  6653. }
  6654. function addSpellTooltips()
  6655. {
  6656. var heal = document.querySelector('[data-spell-name="heal"]');
  6657. heal.title = 'Mana: 2';
  6658. win.$(heal).tooltip();
  6659. var pound = document.querySelector('[data-spell-name="pound"]');
  6660. pound.title = 'Mana: 2';
  6661. win.$(pound).tooltip();
  6662. var teleport = document.querySelector('[data-spell-name="teleport"]');
  6663. teleport.title = 'Mana: 10';
  6664. win.$(teleport).tooltip();
  6665. var gust = document.querySelector('[data-spell-name="gust"]');
  6666. gust.title = 'Mana: 3';
  6667. win.$(gust).tooltip();
  6668. var bubble = document.querySelector('[data-spell-name="bubble"]');
  6669. bubble.title = 'Mana: 10';
  6670. win.$(bubble).tooltip();
  6671. var sandstorm = document.querySelector('[data-spell-name="sandstorm"]');
  6672. sandstorm.title = 'Mana: 20';
  6673. win.$(sandstorm).tooltip();
  6674. var ghostScan = document.querySelector('[data-spell-name="ghostScan"]');
  6675. ghostScan.title = 'Mana: 20';
  6676. win.$(ghostScan).tooltip();
  6677. var beam = document.querySelector('[data-spell-name="beam"]');
  6678. beam.title = 'Mana: 15';
  6679. win.$(beam).tooltip();
  6680. var reflect = document.querySelector('[data-spell-name="reflect"]');
  6681. reflect.title = 'Mana: 20';
  6682. win.$(reflect).tooltip();
  6683. var superHeal = document.querySelector('[data-spell-name="superHeal"]');
  6684. superHeal.title = 'Mana: 8';
  6685. win.$(superHeal).tooltip();
  6686. var barrier = document.querySelector('[data-spell-name="barrier"]');
  6687. barrier.title = 'Mana: 25';
  6688. win.$(barrier).tooltip();
  6689. var healSteal = document.querySelector('[data-spell-name="healSteal"]');
  6690. healSteal.title = 'Mana: 25';
  6691. win.$(healSteal).tooltip();
  6692. var poison = document.querySelector('[data-spell-name="poison"]');
  6693. poison.title = 'Mana: 30';
  6694. win.$(poison).tooltip();
  6695. }
  6696. function modifySpellImages()
  6697. {
  6698. var spell = document.querySelectorAll('.fight-spell');
  6699. for (var i = 0; i < 15; i++){
  6700. spell[i].style.width = '43px';
  6701. spell[i].style.height = '43px';
  6702. }
  6703. var img= [];
  6704. for (var i = 0; i < 15; i++){
  6705. img[i] = spell[i].getElementsByTagName('img')[0];
  6706. img[i].style.width = '43px';
  6707. }
  6708. }
  6709. function addHeroStatTooltips()
  6710. {
  6711. var table = document.querySelector('#hero-area table.table-hero-stats');
  6712. if (!table)
  6713. {
  6714. return;
  6715. }
  6716. var statRow = table.rows.item(0);
  6717. var attackCell = statRow.cells.item(0);
  6718. attackCell.title = 'Attack Damage';
  6719. win.$(attackCell).tooltip();
  6720. var accuracyCell = statRow.cells.item(1);
  6721. accuracyCell.title = 'Attack Accuracy';
  6722. win.$(accuracyCell).tooltip();
  6723. var speedCell = statRow.cells.item(2);
  6724. speedCell.title = 'Attack Speed';
  6725. win.$(speedCell).tooltip();
  6726. var defenseCell = statRow.cells.item(3);
  6727. defenseCell.title = 'Defense';
  6728. win.$(defenseCell).tooltip();
  6729. var magicCell = statRow.cells.item(4);
  6730. magicCell.title = 'Magic Bonus';
  6731. win.$(magicCell).tooltip();
  6732. }
  6733.  
  6734. function unifyTooltips()
  6735. {
  6736. function getLastNonEmptyChild(parent)
  6737. {
  6738. for (var i = parent.childNodes.length - 1; i >= 0; i--)
  6739. {
  6740. var child = parent.childNodes.item(i);
  6741. if (child.nodeType === Node.TEXT_NODE
  6742. && (child.textContent || '').trim() !== '')
  6743. {
  6744. return null;
  6745. }
  6746. else if (child.nodeType === Node.ELEMENT_NODE)
  6747. {
  6748. return child;
  6749. }
  6750. }
  6751. return null;
  6752. }
  6753. // clean unnecessary br-tags in tooltips
  6754. var tooltips = document.querySelectorAll('#tooltip-list > div[id^="tooltip-"]');
  6755. for (var i = 0; i < tooltips.length; i++)
  6756. {
  6757. var tooltip = tooltips[i];
  6758. var lneChild = void 0;
  6759. while ((lneChild = getLastNonEmptyChild(tooltip)) && lneChild.tagName == 'BR')
  6760. {
  6761. tooltip.removeChild(lneChild);
  6762. }
  6763. }
  6764.  
  6765. function getTooltip(item)
  6766. {
  6767. return document.getElementById('tooltip-' + item);
  6768. }
  6769. var boldify = [
  6770. 'oilBarrel'
  6771. , 'boundEmptyPickaxe'
  6772. , 'boundEmptyShovel'
  6773. , 'boundRocket'
  6774. , 'ashes'
  6775. , 'iceBones'
  6776. ];
  6777. var lastDotRegex = /\.\s*$/;
  6778. for (var _i = 0, boldify_1 = boldify; _i < boldify_1.length; _i++)
  6779. {
  6780. var item = boldify_1[_i];
  6781. var tooltip = getTooltip(item);
  6782. if (!tooltip)
  6783. {
  6784. continue;
  6785. }
  6786. var textNode = tooltip.lastChild;
  6787. while (textNode && (textNode.nodeType != Node.TEXT_NODE || (textNode.textContent || '').trim() === ''))
  6788. {
  6789. if (textNode.nodeName === 'SPAN')
  6790. {
  6791. textNode = textNode.lastChild;
  6792. }
  6793. else
  6794. {
  6795. textNode = textNode.previousSibling;
  6796. }
  6797. }
  6798. if (!textNode)
  6799. {
  6800. continue;
  6801. }
  6802. var text = textNode.textContent || '';
  6803. var split = text.split(/\.(?=\s*\S+)/);
  6804. var clickText = split[split.length - 1];
  6805. textNode.textContent = text.replace(clickText, '');
  6806. if (split.length > 1)
  6807. {
  6808. tooltip.appendChild(document.createElement('br'));
  6809. tooltip.appendChild(document.createElement('br'));
  6810. }
  6811. var boldText = document.createElement('b');
  6812. boldText.textContent = clickText;
  6813. tooltip.appendChild(boldText);
  6814. }
  6815.  
  6816. function prepareTooltip(item, editText, createOnMissing)
  6817. {
  6818. if (createOnMissing === void 0)
  6819. {
  6820. createOnMissing = false;
  6821. }
  6822. var tooltip = getTooltip(item);
  6823. if (!tooltip)
  6824. {
  6825. return;
  6826. }
  6827. // try to find the b-node:
  6828. var bNode = getLastNonEmptyChild(tooltip);
  6829. if (bNode && bNode.tagName === 'SPAN')
  6830. {
  6831. bNode = getLastNonEmptyChild(bNode);
  6832. }
  6833. if (!bNode || bNode.tagName !== 'B')
  6834. {
  6835. if (!createOnMissing)
  6836. {
  6837. bNode = null;
  6838. }
  6839. else
  6840. {
  6841. tooltip.appendChild(document.createElement('br'));
  6842. tooltip.appendChild(document.createElement('br'));
  6843. bNode = document.createElement('b');
  6844. tooltip.appendChild(bNode);
  6845. }
  6846. }
  6847. if (bNode)
  6848. {
  6849. bNode.textContent = editText(bNode);
  6850. }
  6851. }
  6852. // remove dots
  6853. for (var i = 0; i < tooltips.length; i++)
  6854. {
  6855. var item = tooltips.item(i).id.replace(/^tooltip-/, '');
  6856. prepareTooltip(item, function (bNode)
  6857. {
  6858. var text = bNode.textContent || '';
  6859. if (/Click to /.test(text))
  6860. {
  6861. return text.replace(lastDotRegex, '');
  6862. }
  6863. return text;
  6864. });
  6865. }
  6866. // add click texts
  6867. function setText(item, text)
  6868. {
  6869. prepareTooltip(item, function ()
  6870. {
  6871. return text;
  6872. }, true);
  6873. }
  6874. for (var _a = 0, FURNACE_LEVELS_1 = FURNACE_LEVELS; _a < FURNACE_LEVELS_1.length; _a++)
  6875. {
  6876. var furnaceLevel = FURNACE_LEVELS_1[_a];
  6877. var furnaceItem = getBoundKey(furnaceLevel + 'Furnace');
  6878. setText(furnaceItem, 'Click to operate');
  6879. var ovenItem = getBoundKey(furnaceLevel + 'Oven');
  6880. setText(ovenItem, 'Click to operate');
  6881. }
  6882. // fix tooltip of quests-book
  6883. var questBookTooltip = getTooltip('quests-book');
  6884. if (questBookTooltip)
  6885. {
  6886. var childNodes = questBookTooltip.childNodes;
  6887. for (var i = 0; i < childNodes.length; i++)
  6888. {
  6889. var node = childNodes[i];
  6890. if (node.nodeType === Node.TEXT_NODE
  6891. && (node.textContent || '').indexOf('Click to see a list of quests.') > -1)
  6892. {
  6893. var next = node.nextSibling;
  6894. if (next)
  6895. {
  6896. questBookTooltip.removeChild(next);
  6897. }
  6898. questBookTooltip.removeChild(node);
  6899. }
  6900. }
  6901. }
  6902. // fix tooltip of axe
  6903. var axeTooltip = getTooltip('boundEmptyAxe');
  6904. if (axeTooltip)
  6905. {
  6906. axeTooltip.insertBefore(document.createElement('br'), axeTooltip.lastElementChild);
  6907. }
  6908. var texts = {
  6909. 'quests-book': 'Click to see the list of quests'
  6910. , 'achievementBook': 'Click to see the list of achievements'
  6911. , 'boundEmptyChisel': 'Click to use'
  6912. , 'rake': 'Click to upgrade your rake'
  6913. , 'boundBoat': 'Click to send boat'
  6914. };
  6915. for (var item in texts)
  6916. {
  6917. setText(item, texts[item]);
  6918. }
  6919. for (var _b = 0, BOAT_LIST_2 = BOAT_LIST; _b < BOAT_LIST_2.length; _b++)
  6920. {
  6921. var boatKey = BOAT_LIST_2[_b];
  6922. setText(getBoundKey(boatKey), 'Click to send boat');
  6923. }
  6924. }
  6925. var cached = {
  6926. scrollWidth: 0
  6927. , scrollHeight: 0
  6928. };
  6929.  
  6930. function changeTooltipPosition(event)
  6931. {
  6932. var tooltipX = event.pageX - 8;
  6933. var tooltipY = event.pageY + 8;
  6934. var el = document.querySelector('body > div.tooltip');
  6935. if (!el)
  6936. {
  6937. return;
  6938. }
  6939. if (!this)
  6940. {
  6941. // init
  6942. cached.scrollWidth = document.body.scrollWidth;
  6943. cached.scrollHeight = document.body.scrollHeight;
  6944. }
  6945. var rect = el.getBoundingClientRect();
  6946. var css = {
  6947. left: tooltipX
  6948. , top: tooltipY
  6949. , width: ''
  6950. , height: ''
  6951. , maxWidth: cached.scrollWidth
  6952. , maxHeight: cached.scrollHeight
  6953. };
  6954. var diffX = cached.scrollWidth - 20 - tooltipX - rect.width;
  6955. if (diffX < 0)
  6956. {
  6957. css.left += diffX;
  6958. css.width = rect.width - 42;
  6959. }
  6960. var diffY = cached.scrollHeight - 20 - tooltipY - rect.height;
  6961. if (diffY < 0)
  6962. {
  6963. css.top += diffY;
  6964. css.height = rect.height - 22;
  6965. }
  6966. win.$(el).css(css);
  6967. }
  6968.  
  6969. function fixTooltipPositioning()
  6970. {
  6971. win.changeTooltipPosition = changeTooltipPosition;
  6972. win.loadTooltips();
  6973. }
  6974.  
  6975. function fixCombatNavigation()
  6976. {
  6977. var backBtns = document.querySelectorAll('span.medium-button[onclick*="openTab(\'combat\')"]');
  6978. for (var i = 0; i < backBtns.length; i++)
  6979. {
  6980. var btn = backBtns.item(i);
  6981. var img = btn.firstElementChild;
  6982. var textNode = btn.lastChild;
  6983. if (!img || img.tagName != 'IMG' || !textNode)
  6984. {
  6985. continue;
  6986. }
  6987. img.className = img.className.replace(/(-\d+)-b/, '$1');
  6988. textNode.textContent = ' back';
  6989. }
  6990. }
  6991.  
  6992. function fixPromethiumSmeltingTime()
  6993. {
  6994. var _getTimerPerBar = win.getTimerPerBar;
  6995. win.getTimerPerBar = function (bar)
  6996. {
  6997. if (bar == 'promethiumBar')
  6998. {
  6999. return 80;
  7000. }
  7001. return _getTimerPerBar(bar);
  7002. };
  7003. }
  7004.  
  7005. function fixImage()
  7006. {
  7007. var oxygenEl = document.querySelector('img[src="images/oxygenPotion"]');
  7008. if (oxygenEl)
  7009. {
  7010. oxygenEl.src += '.png';
  7011. }
  7012. }
  7013.  
  7014. function init()
  7015. {
  7016. fixClientGameLoop();
  7017. fixScroller();
  7018. fixTooltipStyle();
  7019. //fixRefreshingMagicRecipes();
  7020. moveStrangeLeafs();
  7021. fixTreasureMap();
  7022. fixWoodcutting();
  7023. fixQuestBook();
  7024. // apply fix for scroll images later to fix images in this code too
  7025. fixHitText();
  7026. fixScrollImages();
  7027. fixQuest8BraveryRecipe();
  7028. fixBoatTooltips();
  7029. fixAlignments();
  7030. addHeroStatTooltips();
  7031. fixFontSize11Tabs();
  7032. //addSpellTooltips();
  7033. modifySpellImages();
  7034. unifyTooltips();
  7035. fixTooltipPositioning();
  7036. fixCombatNavigation();
  7037. fixPromethiumSmeltingTime();
  7038. fixImage();
  7039. }
  7040. temporaryFixes.init = init;
  7041. })(temporaryFixes || (temporaryFixes = {}));
  7042.  
  7043. /**
  7044. * improve timer
  7045. */
  7046. var timer;
  7047. (function (timer)
  7048. {
  7049. timer.name = 'timer';
  7050. var IMPROVED_CLASS = 'improved';
  7051. var NOTIFICATION_AREA_ID = 'notifaction-area';
  7052. var PERCENT_CLASS = 'percent';
  7053. var REMAINING_CLASS = 'remaining';
  7054. var TIMER_CLASS = 'timer';
  7055.  
  7056. function bindNewFormatter()
  7057. {
  7058. function doBind()
  7059. {
  7060. win.formatTime = win.formatTimeShort = win.formatTimeShort2 = function (seconds)
  7061. {
  7062. return format.timer(seconds);
  7063. };
  7064. }
  7065. win.addEventListener('load', function ()
  7066. {
  7067. return setTimeout(function ()
  7068. {
  7069. return doBind();
  7070. }, 100);
  7071. });
  7072. doBind();
  7073. setTimeout(function ()
  7074. {
  7075. return doBind();
  7076. }, 100);
  7077. }
  7078.  
  7079. function applyStyle()
  7080. {
  7081. 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");
  7082. }
  7083.  
  7084. function improveSmeltingTimer()
  7085. {
  7086. var el = document.getElementById('notif-smelting');
  7087. if (!el)
  7088. {
  7089. return;
  7090. }
  7091. var smeltingNotifBox = el;
  7092. smeltingNotifBox.classList.add(IMPROVED_CLASS);
  7093. var smeltingTimerEl = document.createElement('span');
  7094. smeltingTimerEl.className = TIMER_CLASS;
  7095. smeltingNotifBox.appendChild(smeltingTimerEl);
  7096. var remainingBarsEl = document.createElement('span');
  7097. remainingBarsEl.className = REMAINING_CLASS;
  7098. smeltingNotifBox.appendChild(remainingBarsEl);
  7099. var delta = 0;
  7100.  
  7101. function updatePercValues(init)
  7102. {
  7103. if (init === void 0)
  7104. {
  7105. init = false;
  7106. }
  7107. updateSmeltingTimer(delta = 0);
  7108. if (init)
  7109. {
  7110. observer.add('smeltingPercD', function ()
  7111. {
  7112. return updatePercValues();
  7113. });
  7114. observer.add('smeltingPerc', function ()
  7115. {
  7116. return updatePercValues();
  7117. });
  7118. }
  7119. }
  7120.  
  7121. function updateSmeltingTimer(delta)
  7122. {
  7123. if (delta === void 0)
  7124. {
  7125. delta = 0;
  7126. }
  7127. var totalTime = win.smeltingPercD;
  7128. // thanks at /u/marcus898 for your bug report
  7129. var elapsedTime = Math.round(win.smeltingPerc * totalTime / 100) + delta;
  7130. smeltingTimerEl.textContent = format.timer(Math.max(totalTime - elapsedTime, 0));
  7131. remainingBarsEl.textContent = (win.smeltingTotalAmount - win.smeltingAmount).toString();
  7132. }
  7133. observer.addTick(function ()
  7134. {
  7135. return updateSmeltingTimer(delta++);
  7136. });
  7137. updatePercValues(true);
  7138. }
  7139.  
  7140. function improveTimer(cssRulePrefix, textColor, timerColor, infoIdPrefx, containerPrefix, updateFn)
  7141. {
  7142. 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");
  7143. let iteration = cssRulePrefix == '.woodcutting-tree' ? 6 : 7;
  7144. for (var i = 0; i < iteration; i++)
  7145. {
  7146. var num = i + 1;
  7147. var infoId = infoIdPrefx + num;
  7148. var container = document.getElementById(containerPrefix + num);
  7149. container.style.position = 'relative';
  7150. var infoEl = document.createElement('div');
  7151. infoEl.className = 'info';
  7152. infoEl.id = infoId;
  7153. infoEl.innerHTML = "<div class=\"name\"></div><div class=\"timer\"></div>";
  7154. container.appendChild(infoEl);
  7155. updateFn(num, infoId, true);
  7156. }
  7157. }
  7158.  
  7159. function updateTreeInfo(placeId, infoElId, init)
  7160. {
  7161. if (init === void 0)
  7162. {
  7163. init = false;
  7164. }
  7165. var infoEl = document.getElementById(infoElId);
  7166. var nameEl = infoEl.firstElementChild;
  7167. var timerEl = infoEl.lastElementChild;
  7168. var idKey = 'treeId' + placeId;
  7169. var growTimerKey = 'treeGrowTimer' + placeId;
  7170. var lockedKey = 'treeUnlocked' + placeId;
  7171. var treeId = getGameValue(idKey);
  7172. if (treeId == 0)
  7173. {
  7174. var isLocked = (placeId == 5 || placeId == 6) && win.donorWoodcuttingPatch < win.currentTimeMillis;
  7175. nameEl.textContent = isLocked ? 'Locked' : 'Empty';
  7176. timerEl.textContent = '';
  7177. }
  7178. else
  7179. {
  7180. nameEl.textContent = key2Name(win.getTreeName(treeId)) || 'Unknown Tree';
  7181. var remainingTime = win.TREE_GROW_TIME[treeId - 1] - getGameValue(growTimerKey);
  7182. timerEl.textContent = remainingTime > 0 ? '(' + format.timer(remainingTime) + ')' : 'Fully grown';
  7183. }
  7184. if (init)
  7185. {
  7186. observer.add([idKey, growTimerKey, lockedKey], function ()
  7187. {
  7188. return updateTreeInfo(placeId, infoElId, false);
  7189. });
  7190. }
  7191. }
  7192. // add tree grow timer
  7193. function improveTreeGrowTimer()
  7194. {
  7195. improveTimer('.woodcutting-tree', 'white', 'yellow', 'wc-tree-info-', 'wc-div-tree-', updateTreeInfo);
  7196. }
  7197.  
  7198. function updatePatchInfo(patchId, infoElId, init)
  7199. {
  7200. if (init === void 0)
  7201. {
  7202. init = false;
  7203. }
  7204. var infoEl = document.getElementById(infoElId);
  7205. var nameEl = infoEl.querySelector('.name');
  7206. var timerEl = infoEl.querySelector('.timer');
  7207. var idKey = 'farmingPatchSeed' + patchId;
  7208. var growTimeKey = 'farmingPatchGrowTime' + patchId;
  7209. var timerKey = 'farmingPatchTimer' + patchId;
  7210. var stageKey = 'farmingPatchStage' + patchId;
  7211. var stage = getGameValue(stageKey);
  7212. var seedName = PLANT_NAME[getGameValue(idKey)] || 'Unkown Plant';
  7213. if (stage == 0)
  7214. {
  7215. var isLocked = (patchId == 5 || patchId == 6) && win.donorFarmingPatch < win.currentTimeMillis;
  7216. nameEl.textContent = isLocked ? 'Locked' : 'Click to grow';
  7217. timerEl.textContent = '';
  7218. }
  7219. else if (stage >= 4)
  7220. {
  7221. nameEl.textContent = stage > 4 ? 'Dead Plant' : seedName;
  7222. timerEl.textContent = stage > 4 ? 'Click to remove' : 'Click to harvest';
  7223. }
  7224. else
  7225. {
  7226. nameEl.textContent = seedName;
  7227. var remainingTime = getGameValue(growTimeKey) - getGameValue(timerKey);
  7228. timerEl.textContent = '(' + format.timer(remainingTime) + ')';
  7229. }
  7230. if (init)
  7231. {
  7232. observer.add([idKey, timerKey, stageKey, 'donorFarmingPatch'], function ()
  7233. {
  7234. return updatePatchInfo(patchId, infoElId, false);
  7235. });
  7236. }
  7237. }
  7238. // add seed name and change color of timer
  7239. function getSoonestTreeTimer()
  7240. {
  7241. if (win.treeStage1 == 4
  7242. || win.treeStage2 == 4
  7243. || win.treeStage3 == 4
  7244. || win.treeStage4 == 4
  7245. || win.treeStage5 == 4
  7246. || win.treeStage6 == 4)
  7247. {
  7248. return -1;
  7249. }
  7250. var minTimer = null;
  7251. for (var i = 1; i <= 6; i++)
  7252. {
  7253. var treeId = getGameValue('treeId' + i);
  7254. var unlocked = getGameValue('treeUnlocked' + i) == 1;
  7255. var timerValue = getGameValue('treeGrowTimer' + i);
  7256. if (unlocked && treeId !== 0 && timerValue > 0)
  7257. {
  7258. var remainingTime = win.TREE_GROW_TIME[treeId - 1] - timerValue;
  7259. minTimer = minTimer === null ? remainingTime : Math.min(minTimer, remainingTime);
  7260. }
  7261. }
  7262. return minTimer || 0;
  7263. }
  7264.  
  7265. function getSoonestFarmingTimer()
  7266. {
  7267. if (win.farmingPatchStage1 == 0 || win.farmingPatchStage1 == 4
  7268. || win.farmingPatchStage2 == 0 || win.farmingPatchStage2 == 4
  7269. || win.farmingPatchStage3 == 0 || win.farmingPatchStage3 == 4
  7270. || win.farmingPatchStage4 == 0 || win.farmingPatchStage4 == 4
  7271. || win.donorFarmingPatch > win.currentTimeMillis && (win.farmingPatchStage5 == 0 || win.farmingPatchStage5 == 4
  7272. || win.farmingPatchStage6 == 0 || win.farmingPatchStage6 == 4))
  7273. {
  7274. return -1;
  7275. }
  7276. var minTimer = null;
  7277. for (var i = 1; i <= ((win.donorFarmingPatch > win.currentTimeMillis) ? 6 : 4); i++)
  7278. {
  7279. var remainingTimer = getGameValue('farmingPatchGrowTime' + i) - getGameValue('farmingPatchTimer' + i);
  7280. minTimer = minTimer === null ? remainingTimer : Math.min(minTimer, remainingTimer);
  7281. }
  7282. return minTimer || 0;
  7283. }
  7284.  
  7285. function improveSeedGrowTimer()
  7286. {
  7287. improveTimer('div[id^="farming-patch-area"]', 'black', 'blue', 'farming-patch-info-', 'farming-patch-area-', updatePatchInfo);
  7288. }
  7289.  
  7290. function addTabTimer()
  7291. {
  7292. var TAB_TIMER_KEY = 'tabTimer';
  7293. 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");
  7294.  
  7295. function getTabEl(key)
  7296. {
  7297. return document.getElementById('tab-container-bar-' + key);
  7298. }
  7299.  
  7300. function addInfoDiv(key)
  7301. {
  7302. var infoDiv = document.createElement('div');
  7303. infoDiv.className = 'info';
  7304. var tab = getTabEl(key);
  7305. if (tab)
  7306. {
  7307. tab.appendChild(infoDiv);
  7308. }
  7309. return infoDiv;
  7310. }
  7311.  
  7312. function createTabTimer(key, timerFn)
  7313. {
  7314. var tab = getTabEl(key);
  7315. var timerDiv = addInfoDiv(key);
  7316. if (!tab || !timerDiv)
  7317. {
  7318. return;
  7319. }
  7320. timerDiv.classList.add('timer');
  7321.  
  7322. function updateTimer()
  7323. {
  7324. var minTimer = timerFn();
  7325. if (tab)
  7326. {
  7327. tab.classList[minTimer == -1 ? 'add' : 'remove']('ready');
  7328. }
  7329. timerDiv.textContent = minTimer <= 0 ? '' : format.timer(minTimer);
  7330. }
  7331. updateTimer();
  7332. observer.addTick(function ()
  7333. {
  7334. return updateTimer();
  7335. });
  7336. }
  7337. createTabTimer('woodcutting', getSoonestTreeTimer);
  7338. createTabTimer('farming', getSoonestFarmingTimer);
  7339. createTabTimer('combat', function ()
  7340. {
  7341. return win.combatGlobalCooldown;
  7342. });
  7343. var energyDiv = addInfoDiv('combat');
  7344. energyDiv.classList.add('extra');
  7345.  
  7346. function updateEnergy()
  7347. {
  7348. energyDiv.innerHTML = '<img src="images/steak.png" class="image-icon-15"> ' + format.number(win.energy);
  7349. }
  7350. updateEnergy();
  7351. observer.add('energy', function ()
  7352. {
  7353. return updateEnergy();
  7354. });
  7355. // add highlight for stardust potions
  7356. var potionDiv = addInfoDiv('brewing');
  7357. potionDiv.classList.add('extra');
  7358. var potionList = ['stardustPotion', 'superStardustPotion'];
  7359. var potionImageList = [];
  7360.  
  7361. function updatePotion(key, img, init)
  7362. {
  7363. if (init === void 0)
  7364. {
  7365. init = false;
  7366. }
  7367. var timerKey = key + 'Timer';
  7368. var show = getGameValue(key) > 0 && getGameValue(timerKey) === 0;
  7369. img.style.display = show ? '' : 'none';
  7370. if (init)
  7371. {
  7372. observer.add(key, function ()
  7373. {
  7374. return updatePotion(key, img);
  7375. });
  7376. observer.add(timerKey, function ()
  7377. {
  7378. return updatePotion(key, img);
  7379. });
  7380. }
  7381. }
  7382. for (var i = 0; i < potionList.length; i++)
  7383. {
  7384. var key = potionList[i];
  7385. var img = document.createElement('img');
  7386. img.src = 'images/' + key + '.png';
  7387. img.className = 'image-icon-15';
  7388. potionImageList[i] = img;
  7389. potionDiv.appendChild(img);
  7390. updatePotion(key, img, true);
  7391. }
  7392.  
  7393. function updateVisibility()
  7394. {
  7395. document.body.classList[settings.get(settings.KEY.showTabTimer) ? 'add' : 'remove'](TAB_TIMER_KEY);
  7396. }
  7397. updateVisibility();
  7398. settings.observe(settings.KEY.showTabTimer, function ()
  7399. {
  7400. return updateVisibility();
  7401. });
  7402. observer.add('profileShortTabs', function ()
  7403. {
  7404. var short = !!win.profileShortTabs;
  7405. document.body.classList[short ? 'add' : 'remove']('short-tabs');
  7406. });
  7407. }
  7408.  
  7409. function addOilInfo()
  7410. {
  7411. var NULL_TYPE = 'null';
  7412. var PLUS_TYPE = 'plus';
  7413. var MINUS_TYPE = 'minus';
  7414. 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");
  7415. var oilFlow = document.getElementById('oil-flow-values');
  7416. var parent = oilFlow && oilFlow.parentElement;
  7417. if (!oilFlow || !parent)
  7418. {
  7419. return;
  7420. }
  7421. var container = document.createElement('div');
  7422. container.id = 'oil-filling-level';
  7423. var fillingLevel = document.createElement('div');
  7424. container.appendChild(fillingLevel);
  7425. var first = parent.firstElementChild;
  7426. if (first)
  7427. {
  7428. parent.insertBefore(container, first);
  7429. }
  7430. else
  7431. {
  7432. parent.appendChild(container);
  7433. }
  7434. parent.style.position = 'relative';
  7435. var netFlow = document.createElement('span');
  7436. netFlow.id = 'oil-flow-net';
  7437. parent.insertBefore(netFlow, oilFlow);
  7438. var next = oilFlow.nextElementSibling;
  7439. var netTimer = document.createElement('span');
  7440. netTimer.id = 'oil-flow-net-timer';
  7441. if (next)
  7442. {
  7443. parent.insertBefore(netTimer, next);
  7444. }
  7445. else
  7446. {
  7447. parent.appendChild(netTimer);
  7448. }
  7449. var oilNet;
  7450. var oilNetType;
  7451.  
  7452. function updateNetFlow(init)
  7453. {
  7454. if (init === void 0)
  7455. {
  7456. init = false;
  7457. }
  7458. oilNet = win.oilIn - win.oilOut;
  7459. oilNetType = oilNet === 0 ? NULL_TYPE : (oilNet > 0 ? PLUS_TYPE : MINUS_TYPE);
  7460. netFlow.dataset.type = oilNetType;
  7461. var sign = oilNet === 0 ? PLUS_MINUS_SIGN : (oilNet > 0 ? '+' : '');
  7462. netFlow.textContent = sign + oilNet;
  7463. if (init)
  7464. {
  7465. observer.add('oilIn', function ()
  7466. {
  7467. return updateNetFlow();
  7468. });
  7469. observer.add('oilOut', function ()
  7470. {
  7471. return updateNetFlow();
  7472. });
  7473. }
  7474. updateFullTimer(init);
  7475. }
  7476. var hour2Color = (_a = {}
  7477. , // 30min
  7478. _a[.5 * 60 * 60] = 'rgb(255, 0, 0)'
  7479. , _a[5 * 60 * 60] = 'rgb(255, 255, 0)'
  7480. , _a[8 * 60 * 60] = 'rgb(255, 255, 255)'
  7481. , _a);
  7482.  
  7483. function updateFullTimer(init)
  7484. {
  7485. if (init === void 0)
  7486. {
  7487. init = false;
  7488. }
  7489. netTimer.dataset.type = oilNetType;
  7490. var time = 0;
  7491. if (oilNet > 0)
  7492. {
  7493. netTimer.title = 'full in...';
  7494. var diff = win.maxOil - win.oil;
  7495. time = diff / oilNet;
  7496. }
  7497. else if (oilNet < 0)
  7498. {
  7499. netTimer.title = 'empty in...';
  7500. time = win.oil / Math.abs(oilNet);
  7501. }
  7502. netTimer.textContent = '(' + format.timer(Math.ceil(time)) + ')';
  7503. var filledPercent = win.oil / win.maxOil * 100;
  7504. fillingLevel.style.height = (100 - filledPercent) + '%';
  7505. /**
  7506. * colorize filling level according to the time it needs to be full/empty:
  7507. * - red iff oil storage full/empty in 30min
  7508. * - yellow iff oil storage full/empty in 5h
  7509. * - white iff oil storage full/empty in 8h or more
  7510. */
  7511. var color = oilNet === 0 ? '#ffffff' : colorGenerator.getColorTransition(time, hour2Color);
  7512. container.style.borderColor = color;
  7513. if (init)
  7514. {
  7515. observer.add('maxOil', function ()
  7516. {
  7517. return updateFullTimer();
  7518. });
  7519. observer.add('oil', function ()
  7520. {
  7521. return updateFullTimer();
  7522. });
  7523. observer.addTick(function ()
  7524. {
  7525. return updateFullTimer();
  7526. });
  7527. }
  7528. }
  7529. updateNetFlow(true);
  7530. var _a;
  7531. }
  7532.  
  7533. function addRocketTimer()
  7534. {
  7535. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7536. if (!notifArea)
  7537. {
  7538. return;
  7539. }
  7540. var notifBox = document.createElement('span');
  7541. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7542. notifBox.id = 'notif-rocket';
  7543. notifBox.style.display = 'none';
  7544. 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>";
  7545. var AVG_KM_PER_SEC = rocketDestination == 1 ? 15 : 392;
  7546. notifBox.title = 'This value is only an estimation based on an average speed of '+AVG_KM_PER_SEC+'km per second.';
  7547. notifArea.appendChild(notifBox);
  7548. var img = notifBox.getElementsByTagName('img').item(0);
  7549. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7550. var percentEl = notifBox.getElementsByClassName(PERCENT_CLASS).item(0);
  7551. var smoothedTime = 0;
  7552.  
  7553. function updateRocketKm()
  7554. {
  7555. if(AVG_KM_PER_SEC != (rocketDestination == 1 ? 15 : 392))
  7556. {
  7557. AVG_KM_PER_SEC = rocketDestination == 1 ? 15 : 392;
  7558. notifBox.title = 'This value is only an estimation based on an average speed of '+AVG_KM_PER_SEC+'km per second.';
  7559. }
  7560. var hideStatic = win.rocketKm < (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM);
  7561. var hideTimer = win.rocketKm <= 0 || !hideStatic;
  7562. notifBox.style.display = hideTimer ? 'none' : '';
  7563. var percent = win.rocketKm / (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM);
  7564. var diff = (rocketDestination == 1 ? MAX_ROCKET_MOON_KM : MAX_ROCKET_MARS_KM) - win.rocketKm;
  7565. if (win.rocketMoonId < 0)
  7566. {
  7567. percent = 1 - percent;
  7568. diff = win.rocketKm;
  7569. }
  7570. var avgRemainingTime = Math.round(diff / AVG_KM_PER_SEC);
  7571. // be more accurate in the last few seconds (may be the last 2 up to 16 seconds)
  7572. var threshold = smoothedTime < 10 ? 1 : 8;
  7573. if (Math.abs(smoothedTime - avgRemainingTime) >= threshold)
  7574. {
  7575. smoothedTime = avgRemainingTime + 1;
  7576. }
  7577. percentEl.textContent = Math.floor(percent * 100).toString();
  7578. }
  7579.  
  7580. function tickRocketTimer()
  7581. {
  7582. if (smoothedTime > 0)
  7583. {
  7584. smoothedTime = Math.max(smoothedTime - 1, 0);
  7585. timerEl.textContent = format.timer(smoothedTime);
  7586. }
  7587. }
  7588. updateRocketKm();
  7589. observer.add('rocketKm', function (key, oldValue, newValue)
  7590. {
  7591. return updateRocketKm();
  7592. });
  7593. observer.addTick(function ()
  7594. {
  7595. return tickRocketTimer();
  7596. });
  7597.  
  7598. function updateRocketDirection()
  7599. {
  7600. // alternatively: `transform: rotateZ(180deg) rotateY(180deg)`
  7601. var transform = win.rocketMoonId >= 0 ? '' : 'rotate(90deg)';
  7602. img.style.transform = transform;
  7603. var itemBox = document.getElementById('default-item-img-tag-boundRocket');
  7604. if (itemBox)
  7605. {
  7606. itemBox.style.transform = transform;
  7607. }
  7608. }
  7609. updateRocketDirection();
  7610. observer.add('rocketMoonId', function ()
  7611. {
  7612. return updateRocketDirection();
  7613. });
  7614. }
  7615. function addVendorRefreshTimer()
  7616. {
  7617. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7618. if (!notifArea)
  7619. {
  7620. return;
  7621. }
  7622. var notifBox = document.createElement('span');
  7623. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7624. notifBox.id = 'notif-vendorRefresh';
  7625. notifBox.style.display = 'none';
  7626. 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>";
  7627. notifArea.appendChild(notifBox);
  7628. var img = notifBox.getElementsByTagName('img').item(0);
  7629. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7630.  
  7631. function updateVendorRefreshTimer()
  7632. {
  7633. var hideTimer = win.vendorNotification == 1;
  7634. notifBox.style.display = hideTimer ? 'none' : '';
  7635. var timeLeft = Math.floor( (12*60*60) - (Date.now()-vendorLastChanged)/1000 );
  7636. timerEl.textContent = format.timer(timeLeft);
  7637. }
  7638. updateVendorRefreshTimer();
  7639. observer.addTick(function ()
  7640. {
  7641. return updateVendorRefreshTimer();
  7642. });
  7643. }
  7644. function addBobsUncleTimer()
  7645. {
  7646. var notifArea = document.getElementById(NOTIFICATION_AREA_ID);
  7647. if (!notifArea)
  7648. {
  7649. return;
  7650. }
  7651. var notifBox = document.createElement('span');
  7652. notifBox.className = 'notif-box ' + IMPROVED_CLASS;
  7653. notifBox.id = 'notif-bobsUncle';
  7654. notifBox.style.display = 'none';
  7655. 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>";
  7656. notifArea.appendChild(notifBox);
  7657. var img = notifBox.getElementsByTagName('img').item(0);
  7658. var timerEl = notifBox.getElementsByClassName(TIMER_CLASS).item(0);
  7659.  
  7660. function updateBobsUncleTimer()
  7661. {
  7662. var hideTimer = win.farmingPatchStage7 == 0 || win.farmingPatchStage7 == 4;
  7663. notifBox.style.display = hideTimer ? 'none' : '';
  7664. var timeLeft = getGameValue('farmingPatchGrowTime7') - getGameValue('farmingPatchTimer7');
  7665. timerEl.textContent = format.timer(timeLeft);
  7666. }
  7667. updateBobsUncleTimer();
  7668. observer.addTick(function ()
  7669. {
  7670. return updateBobsUncleTimer();
  7671. });
  7672. }
  7673. function getLogTypeList()
  7674. {
  7675. var list = [];
  7676. var els = document.querySelectorAll('input[id^="input-charcoalFoundry-"]');
  7677. for (var i = 0; i < els.length; i++)
  7678. {
  7679. list.push(els[i].id.replace(/^input-charcoalFoundry-/i, ''));
  7680. }
  7681. return list;
  7682. }
  7683.  
  7684. function improveFoundryTimer()
  7685. {
  7686. var el = document.getElementById('notif-charcoalFoundry');
  7687. if (!el)
  7688. {
  7689. return;
  7690. }
  7691. var notifBox = el;
  7692. notifBox.classList.add(IMPROVED_CLASS);
  7693. var timerEl = document.createElement('span');
  7694. timerEl.className = TIMER_CLASS;
  7695. notifBox.appendChild(timerEl);
  7696. var remainingEl = document.createElement('span');
  7697. remainingEl.className = REMAINING_CLASS;
  7698. notifBox.appendChild(remainingEl);
  7699. var logTypeList = null;
  7700. observer.add('charcoalFoundryN', function (key, oldValue, newValue)
  7701. {
  7702. timerEl.textContent = format.timer(win.charcoalFoundryD - win.charcoalFoundryN);
  7703. // init log type list when needed
  7704. if (!logTypeList)
  7705. {
  7706. logTypeList = getLogTypeList();
  7707. }
  7708. var woodAmount = win.charcoalFoundryTotal - win.charcoalFoundryCurrent;
  7709. var coalPerLog = win.getCharcoalPerLog(logTypeList[win.charcoalFoundryLogId - 1]);
  7710. var remainingCoal = woodAmount * (isNaN(coalPerLog) ? 1 : coalPerLog);
  7711. remainingEl.textContent = remainingCoal.toString();
  7712. });
  7713. }
  7714.  
  7715. function init()
  7716. {
  7717. bindNewFormatter();
  7718. applyStyle();
  7719. improveSmeltingTimer();
  7720. improveTreeGrowTimer();
  7721. improveSeedGrowTimer();
  7722. addTabTimer();
  7723. addOilInfo();
  7724. addRocketTimer();
  7725. improveFoundryTimer();
  7726. addVendorRefreshTimer();
  7727. addBobsUncleTimer();
  7728. }
  7729. timer.init = init;
  7730. })(timer || (timer = {}));
  7731.  
  7732. /**
  7733. * improve smelting dialog
  7734. */
  7735. var smelting;
  7736. (function (smelting)
  7737. {
  7738. smelting.name = 'smelting';
  7739. var TIME_NEEDED_ID = 'smelting-time-needed';
  7740. var LAST_SMELTING_AMOUNT_KEY = 'lastSmeltingAmount';
  7741. var LAST_SMELTING_BAR_KEY = 'lastSmeltingBar';
  7742. var smeltingValue = null;
  7743. var amountInput;
  7744.  
  7745. function prepareAmountInput()
  7746. {
  7747. amountInput = document.getElementById('input-smelt-bars-amount');
  7748. amountInput.type = 'number';
  7749. amountInput.min = '0';
  7750. amountInput.step = '5';
  7751.  
  7752. function onValueChange()
  7753. {
  7754. smeltingValue = null;
  7755. win.selectBar('', null, amountInput, document.getElementById('smelting-furnace-capacity').value);
  7756. }
  7757. amountInput.addEventListener('mouseup', onValueChange);
  7758. amountInput.addEventListener('keyup', onValueChange);
  7759. amountInput.setAttribute('onkeyup', '');
  7760. }
  7761.  
  7762. function setBarCap(bar, capacity)
  7763. {
  7764. if (bar == '')
  7765. {
  7766. bar = win.selectedBar;
  7767. }
  7768. var requirements = SMELTING_REQUIREMENTS[bar];
  7769. var maxAmount = parseInt(capacity, 10);
  7770. for (var key in requirements)
  7771. {
  7772. var req = requirements[key];
  7773. maxAmount = Math.min(Math.floor(getGameValue(key) / req), maxAmount);
  7774. }
  7775. var value = parseInt(amountInput.value, 10);
  7776. if (value > maxAmount)
  7777. {
  7778. smeltingValue = value;
  7779. amountInput.value = maxAmount.toString();
  7780. }
  7781. else if (smeltingValue != null)
  7782. {
  7783. amountInput.value = Math.min(smeltingValue, maxAmount).toString();
  7784. if (smeltingValue <= maxAmount)
  7785. {
  7786. smeltingValue = null;
  7787. }
  7788. }
  7789. }
  7790.  
  7791. function prepareTimeNeeded()
  7792. {
  7793. var neededMatsEl = document.getElementById('dialogue-furnace-mats-needed');
  7794. var parent = neededMatsEl && neededMatsEl.parentElement;
  7795. if (!neededMatsEl || !parent)
  7796. {
  7797. return;
  7798. }
  7799. var br = document.createElement('br');
  7800. var timeBox = document.createElement('div');
  7801. timeBox.className = 'basic-smallbox';
  7802. timeBox.innerHTML = "<img src=\"images/icons/hourglass.png\" class=\"image-icon-30\">\n\t\tDuration: <span id=\"" + TIME_NEEDED_ID + "\"></span>";
  7803. var next = neededMatsEl.nextElementSibling;
  7804. parent.insertBefore(br, next);
  7805. parent.insertBefore(timeBox, next);
  7806. }
  7807.  
  7808. function updateTimeNeeded(value)
  7809. {
  7810. var timeEl = document.getElementById(TIME_NEEDED_ID);
  7811. if (!timeEl)
  7812. {
  7813. return;
  7814. }
  7815. var num = parseInt(value, 10);
  7816. var timePerBar = win.getTimerPerBar(win.selectedBar);
  7817. timeEl.textContent = format.timer(timePerBar * num);
  7818. }
  7819.  
  7820. function init()
  7821. {
  7822. prepareAmountInput();
  7823. prepareTimeNeeded();
  7824. var _selectBar = win.selectBar;
  7825. var updateSmeltingRequirements = function (bar, inputElement, inputBarsAmountEl, capacity)
  7826. {
  7827. _selectBar(bar, inputElement, inputBarsAmountEl, capacity);
  7828. var matsArea = document.getElementById('dialogue-furnace-mats-needed');
  7829. if (matsArea)
  7830. {
  7831. matsArea.innerHTML = format.numbersInText(matsArea.innerHTML);
  7832. }
  7833. updateTimeNeeded(inputBarsAmountEl.value);
  7834. };
  7835. win.selectBar = function (bar, inputElement, inputBarsAmountEl, capacity)
  7836. {
  7837. setBarCap(bar, capacity);
  7838. // save selected bar
  7839. if (bar != '')
  7840. {
  7841. store.set(LAST_SMELTING_BAR_KEY, bar);
  7842. }
  7843. // save amount
  7844. store.set(LAST_SMELTING_AMOUNT_KEY, inputBarsAmountEl.value);
  7845. updateSmeltingRequirements(bar, inputElement, inputBarsAmountEl, capacity);
  7846. };
  7847. var lastBar = store.get(LAST_SMELTING_BAR_KEY);
  7848. var lastAmount = store.get(LAST_SMELTING_AMOUNT_KEY);
  7849. var _openFurnaceDialogue = win.openFurnaceDialogue;
  7850. win.openFurnaceDialogue = function (furnace)
  7851. {
  7852. var capacity = win.getFurnaceCapacity(furnace);
  7853. if (win.smeltingBarType == 0)
  7854. {
  7855. amountInput.max = capacity.toString();
  7856. }
  7857. // restore amount
  7858. var inputBarsAmountEl = document.getElementById('input-smelt-bars-amount');
  7859. if (inputBarsAmountEl && inputBarsAmountEl.value == '-1' && lastAmount != null)
  7860. {
  7861. inputBarsAmountEl.value = lastAmount;
  7862. }
  7863. _openFurnaceDialogue(furnace);
  7864. // restore selected bar
  7865. if ((!win.selectedBar || win.selectedBar == 'none') && lastBar != null)
  7866. {
  7867. win.selectedBar = lastBar;
  7868. }
  7869. // update whether requirements are fulfilled
  7870. var barInputId = 'input-furnace-' + split2Words(win.selectedBar, '-').toLowerCase();
  7871. var inputElement = document.getElementById(barInputId);
  7872. if (inputElement && inputBarsAmountEl)
  7873. {
  7874. updateSmeltingRequirements(win.selectedBar, inputElement, inputBarsAmountEl, capacity.toString());
  7875. }
  7876. };
  7877. }
  7878. smelting.init = init;
  7879. })(smelting || (smelting = {}));
  7880.  
  7881. /**
  7882. * add chance to time calculator
  7883. */
  7884. var fishingInfo;
  7885. (function (fishingInfo)
  7886. {
  7887. fishingInfo.name = 'fishingInfo';
  7888. /**
  7889. * calculates the number of seconds until the event with the given chance happened at least once with the given
  7890. * probability p (in percent)
  7891. */
  7892. function calcSecondsTillP(chancePerSecond, p)
  7893. {
  7894. return Math.round(Math.log(1 - p / 100) / Math.log(1 - chancePerSecond));
  7895. }
  7896.  
  7897. function addChanceTooltip(headline, chancePerSecond, elId, targetEl)
  7898. {
  7899. // ensure tooltip exists and is correctly binded
  7900. var tooltipEl = ensureTooltip('chance-' + elId, targetEl);
  7901. // set elements content
  7902. var percValues = [1, 10, 20, 50, 80, 90, 99];
  7903. var percRows = '';
  7904. for (var _i = 0, percValues_1 = percValues; _i < percValues_1.length; _i++)
  7905. {
  7906. var p = percValues_1[_i];
  7907. 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>";
  7908. }
  7909. 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";
  7910. }
  7911.  
  7912. function addChanceStyle()
  7913. {
  7914. 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");
  7915. }
  7916.  
  7917. function addXp()
  7918. {
  7919. var table = document.querySelector('#dialogue-id-fishingRod table');
  7920. if (!table)
  7921. {
  7922. return;
  7923. }
  7924. var rows = table.rows;
  7925. for (var i = 0; i < rows.length; i++)
  7926. {
  7927. var row = rows.item(i);
  7928. if (row.classList.contains('xp-added'))
  7929. {
  7930. continue;
  7931. }
  7932. if (i == 0)
  7933. {
  7934. var xpCell = document.createElement('th');
  7935. xpCell.textContent = 'XP';
  7936. row.appendChild(xpCell);
  7937. }
  7938. else
  7939. {
  7940. var cell = row.insertCell(-1);
  7941. var rawFish = row.id.replace('dialogue-fishing-rod-tr-', '');
  7942. var xp = FISH_XP[rawFish];
  7943. cell.textContent = xp == null ? '?' : format.number(xp);
  7944. }
  7945. row.classList.add('xp-added');
  7946. }
  7947. }
  7948.  
  7949. function chance2TimeCalculator()
  7950. {
  7951. var table = document.querySelector('#dialogue-id-fishingRod table');
  7952. if (!table)
  7953. {
  7954. return;
  7955. }
  7956. var rows = table.rows;
  7957. for (var i = 1; i < rows.length; i++)
  7958. {
  7959. var row = rows.item(i);
  7960. var rawFish = row.id.replace('dialogue-fishing-rod-tr-', '');
  7961. var fish = rawFish.replace('raw', '').toLowerCase();
  7962. if (!rawFish || !fish)
  7963. {
  7964. continue;
  7965. }
  7966. var chanceCell = row.cells.item(row.cells.length - 2);
  7967. var chance = (chanceCell.textContent || '')
  7968. .replace(/[^\d\/]/g, '')
  7969. .split('/')
  7970. .reduce(function (p, c)
  7971. {
  7972. return p / parseInt(c, 10);
  7973. }, 1);
  7974. addChanceTooltip("One raw " + fish + " at least every:", chance, rawFish, row);
  7975. }
  7976. }
  7977.  
  7978. function init()
  7979. {
  7980. addChanceStyle();
  7981. var _clicksShovel = win.clicksShovel;
  7982. win.clicksShovel = function ()
  7983. {
  7984. _clicksShovel();
  7985. var shovelChance = document.getElementById('dialogue-shovel-chance');
  7986. var titleEl = shovelChance.parentElement;
  7987. var chance = 1 / win.getChanceOfDiggingSand();
  7988. addChanceTooltip('One sand at least every:', chance, 'shovel', titleEl);
  7989. };
  7990. // depends on fishingXp
  7991. var _clicksFishingRod = win.clicksFishingRod;
  7992. win.clicksFishingRod = function ()
  7993. {
  7994. _clicksFishingRod();
  7995. addXp();
  7996. chance2TimeCalculator();
  7997. };
  7998. }
  7999. fishingInfo.init = init;
  8000. })(fishingInfo || (fishingInfo = {}));
  8001.  
  8002. /**
  8003. * add tooltips for recipes
  8004. */
  8005. var recipeTooltips;
  8006. (function (recipeTooltips)
  8007. {
  8008. recipeTooltips.name = 'recipeTooltips';
  8009.  
  8010. function updateRecipeTooltips(recipeKey, recipes)
  8011. {
  8012. var table = document.getElementById('table-' + recipeKey + '-recipe');
  8013. var rows = table.rows;
  8014.  
  8015. function recipe2Title(recipe)
  8016. {
  8017. return recipe.recipe
  8018. .map(function (name, i)
  8019. {
  8020. return format.number(recipe.recipeCost[i]) + String.fromCharCode(160)
  8021. + split2Words(name).toLowerCase();
  8022. })
  8023. .join(' + ');
  8024. };
  8025. for (var i = 1; i < rows.length; i++)
  8026. {
  8027. var row = rows.item(i);
  8028. var key = row.id.replace(recipeKey + '-', '');
  8029. var recipe = recipes[key];
  8030. var requirementCell = row.cells.item(3);
  8031. requirementCell.title = recipe2Title(recipe);
  8032. win.$(requirementCell).tooltip();
  8033. }
  8034. }
  8035.  
  8036. function updateTooltipsOnReinitRecipes(key)
  8037. {
  8038. var capitalKey = capitalize(key);
  8039. var processKey = 'process' + capitalKey + 'Tab';
  8040. var _processTab = win[processKey];
  8041. win[processKey] = function ()
  8042. {
  8043. var reinit = !!getGameValue('refreshLoad' + capitalKey + 'Table');
  8044. _processTab();
  8045. if (reinit)
  8046. {
  8047. updateRecipeTooltips(key, getGameValue(key + 'Recipes'));
  8048. }
  8049. };
  8050. }
  8051.  
  8052. function init()
  8053. {
  8054. updateTooltipsOnReinitRecipes('crafting');
  8055. updateTooltipsOnReinitRecipes('brewing');
  8056. updateTooltipsOnReinitRecipes('magic');
  8057. updateTooltipsOnReinitRecipes('cooksBook');
  8058. }
  8059. recipeTooltips.init = init;
  8060. })(recipeTooltips || (recipeTooltips = {}));
  8061.  
  8062. /**
  8063. * fix formatting of numbers
  8064. */
  8065. var fixNumbers;
  8066. (function (fixNumbers)
  8067. {
  8068. fixNumbers.name = 'fixNumbers';
  8069.  
  8070. function prepareRecipeForTable(recipe)
  8071. {
  8072. // create a copy of the recipe to prevent requirement check from failing
  8073. var newRecipe = JSON.parse(JSON.stringify(recipe));
  8074. newRecipe.recipeCost = recipe.recipeCost.map(function (cost)
  8075. {
  8076. return format.number(cost);
  8077. });
  8078. newRecipe.description = format.numbersInText(newRecipe.description);
  8079. newRecipe.xp = format.number(recipe.xp);
  8080. return newRecipe;
  8081. }
  8082.  
  8083. function init()
  8084. {
  8085. var _addRecipeToBrewingTable = win.addRecipeToBrewingTable;
  8086. win.addRecipeToBrewingTable = function (brewingRecipe)
  8087. {
  8088. _addRecipeToBrewingTable(prepareRecipeForTable(brewingRecipe));
  8089. };
  8090. var _addRecipeToMagicTable = win.addRecipeToMagicTable;
  8091. win.addRecipeToMagicTable = function (magicRecipe)
  8092. {
  8093. _addRecipeToMagicTable(prepareRecipeForTable(magicRecipe));
  8094. };
  8095. var _addRecipeToCooksBookTable = win.addRecipeToCooksBookTable;
  8096. win.addRecipeToCooksBookTable = function (cooksBookRecipe)
  8097. {
  8098. _addRecipeToCooksBookTable(prepareRecipeForTable(cooksBookRecipe));
  8099. };
  8100. var tooltipList = document.querySelectorAll('#tooltip-list div[id^="tooltip-"][id$="Seeds"]');
  8101. for (var i = 0; i < tooltipList.length; i++)
  8102. {
  8103. var tooltip = tooltipList[i];
  8104. tooltip.innerHTML = format.numbersInText(tooltip.innerHTML);
  8105. }
  8106. var fightEnergyCells = document.querySelectorAll('#dialogue-fight tr > td:nth-child(4)');
  8107. for (var i = 0; i < fightEnergyCells.length; i++)
  8108. {
  8109. var cell = fightEnergyCells[i];
  8110. cell.innerHTML = format.numbersInText(cell.innerHTML);
  8111. }
  8112. var _rocketTick = win.rocketTick;
  8113. win.rocketTick = function ()
  8114. {
  8115. _rocketTick();
  8116. var rocketBox = document.getElementById('itembox-rocket');
  8117. if (rocketBox && /^\d+\s*Km$/i.test(rocketBox.textContent || ''))
  8118. {
  8119. rocketBox.innerHTML = format.numbersInText(rocketBox.innerHTML).replace('Km', 'km');
  8120. }
  8121. };
  8122. }
  8123. fixNumbers.init = init;
  8124. })(fixNumbers || (fixNumbers = {}));
  8125.  
  8126. /**
  8127. * add slider for machines
  8128. */
  8129. var machineDialog;
  8130. (function (machineDialog)
  8131. {
  8132. machineDialog.name = 'machineDialog';
  8133. var $slider;
  8134.  
  8135. function createSlider()
  8136. {
  8137. var br = document.querySelector('#dialogue-machinery-current-total ~ br');
  8138. var parent = br && br.parentElement;
  8139. if (!br || !parent)
  8140. {
  8141. return;
  8142. }
  8143. 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");
  8144. var slider = document.createElement('div');
  8145. parent.insertBefore(slider, br);
  8146. $slider = win.$(slider)
  8147. .slider(
  8148. {
  8149. range: 'max'
  8150. , min: 0
  8151. , max: 10
  8152. , value: 0
  8153. , slide: function (event, ui)
  8154. {
  8155. return updateValue(ui.value);
  8156. }
  8157. });
  8158. // hide br and up/down arrows
  8159. br.style.display = 'none';
  8160. var arrows = document.querySelectorAll('input[onclick^="turnOn("]');
  8161. for (var i = 0; i < arrows.length; i++)
  8162. {
  8163. arrows[i].style.display = 'none';
  8164. }
  8165. var els = document.querySelectorAll('[onclick*="openMachineryDialogue("]');
  8166. var boundMachineKeyList = [];
  8167. for (var i = 0; i < els.length; i++)
  8168. {
  8169. var match = els[i].id.match(/openMachineryDialogue\('(.+?)'\)/);
  8170. if (match)
  8171. {
  8172. boundMachineKeyList.push(getBoundKey(match[1]));
  8173. }
  8174. }
  8175. observer.add(boundMachineKeyList, function ()
  8176. {
  8177. return updateMax();
  8178. });
  8179. }
  8180.  
  8181. function updateMax()
  8182. {
  8183. var machineEl = document.getElementById('dialogue-machinery-chosen');
  8184. if (machineEl && machineEl.value != '')
  8185. {
  8186. var boundMachineKey = getBoundKey(machineEl.value);
  8187. var ownedNum = getGameValue(boundMachineKey);
  8188. $slider.slider('option', 'max', ownedNum);
  8189. $slider.get(0).dataset.owned = ownedNum.toString();
  8190. }
  8191. }
  8192.  
  8193. function updateValue(value)
  8194. {
  8195. var typeEl = document.getElementById('dialogue-machinery-chosen');
  8196. var numEl = document.getElementById('dialogue-machinery-current-on');
  8197. if (numEl && typeEl)
  8198. {
  8199. var valueBefore = parseInt(numEl.textContent || '0', 10);
  8200. var machine = typeEl.value;
  8201. var increment = valueBefore < value;
  8202. var diff = Math.abs(valueBefore - value);
  8203. for (var i = 0; i < diff; i++)
  8204. {
  8205. win.turnOn(machine, increment);
  8206. }
  8207. }
  8208. }
  8209.  
  8210. function init()
  8211. {
  8212. if (!settings.get(settings.KEY.changeMachineDialog))
  8213. {
  8214. return;
  8215. }
  8216. createSlider();
  8217. var _openMachineryDialogue = win.openMachineryDialogue;
  8218. win.openMachineryDialogue = function (machineType)
  8219. {
  8220. _openMachineryDialogue(machineType);
  8221. updateMax();
  8222. $slider.slider('value', getGameValue(machineType + 'On'));
  8223. };
  8224. }
  8225. machineDialog.init = init;
  8226. })(machineDialog || (machineDialog = {}));
  8227.  
  8228. /**
  8229. * improve behaviour of amount inputs
  8230. */
  8231. var amountInputs;
  8232. (function (amountInputs)
  8233. {
  8234. amountInputs.name = 'amountInputs';
  8235.  
  8236. function getVialType(recipe)
  8237. {
  8238. return recipe.levelReq < 35 ? 'vialOfWater' : (recipe.levelReq < 65 ? 'largeVialOfWater' : 'hugeVialOfWater');
  8239. }
  8240.  
  8241. function getSimpleMax(recipe)
  8242. {
  8243. var max = Number.MAX_SAFE_INTEGER;
  8244. for (var i = 0; i < recipe.recipe.length; i++)
  8245. {
  8246. max = Math.min(max, Math.floor(getGameValue(recipe.recipe[i]) / recipe.recipeCost[i]));
  8247. }
  8248. return max;
  8249. }
  8250.  
  8251. function getMax(recipe)
  8252. {
  8253. var max = getSimpleMax(recipe);
  8254. if (/Potion$/.test(recipe.itemName))
  8255. {
  8256. var vialType = getVialType(recipe);
  8257. max = Math.min(max, getGameValue(vialType));
  8258. }
  8259. return max;
  8260. }
  8261.  
  8262. function ensureNumberInput(idOrEl)
  8263. {
  8264. var numInput = typeof idOrEl === 'string' ? document.getElementById(idOrEl) : idOrEl;
  8265. if (numInput)
  8266. {
  8267. if (numInput.type != 'number' && settings.get(settings.KEY.makeNumberInputs))
  8268. {
  8269. var width = numInput.clientWidth;
  8270. if (width !== 0)
  8271. {
  8272. numInput.style.width = width + 'px';
  8273. }
  8274. numInput.type = 'number';
  8275. numInput.min = '0';
  8276. var onkeyup_1 = numInput.getAttribute('onkeyup');
  8277. if (onkeyup_1)
  8278. {
  8279. numInput.setAttribute('onmouseup', onkeyup_1);
  8280. }
  8281. }
  8282. else if (numInput.type == 'number' && !settings.get(settings.KEY.makeNumberInputs))
  8283. {
  8284. numInput.style.width = '';
  8285. numInput.type = '';
  8286. numInput.removeAttribute('onmouseup');
  8287. }
  8288. }
  8289. return numInput;
  8290. }
  8291.  
  8292. function getCurrentMax(keyId, recipeCollection)
  8293. {
  8294. var keyEl = document.getElementById(keyId);
  8295. if (!keyEl)
  8296. {
  8297. return 0;
  8298. }
  8299. var key = keyEl.value;
  8300. return getMax(recipeCollection[key]);
  8301. };
  8302.  
  8303. function ensureMaxBtn(keyId, inputId, recipeCollection, key)
  8304. {
  8305. var recipe = recipeCollection[key];
  8306. var numInput = ensureNumberInput(inputId);
  8307. var next = numInput && numInput.nextElementSibling;
  8308. var parent = numInput && numInput.parentElement;
  8309. if (numInput && parent)
  8310. {
  8311. if ((!next || next.nodeName !== 'BUTTON') && settings.get(settings.KEY.addMaxBtn))
  8312. {
  8313. var btn = document.createElement('button');
  8314. btn.textContent = 'Max';
  8315. btn.addEventListener('click', function ()
  8316. {
  8317. numInput.value = getCurrentMax(keyId, recipeCollection).toString();
  8318. });
  8319. parent.appendChild(btn);
  8320. }
  8321. else if (next && next.nodeName === 'BUTTON' && !settings.get(settings.KEY.addMaxBtn))
  8322. {
  8323. parent.removeChild(next);
  8324. }
  8325. numInput.value = Math.min(1, getMax(recipe)).toString();
  8326. numInput.select();
  8327. }
  8328. }
  8329.  
  8330. function watchKeepInput(event)
  8331. {
  8332. var itemInput = document.getElementById('npc-sell-item-chosen');
  8333. var numInput = ensureNumberInput('dialogue-input-cmd');
  8334. if (!itemInput || !numInput)
  8335. {
  8336. return;
  8337. }
  8338. var item = itemInput.value;
  8339. var newValue = Math.max(getGameValue(item) - Number(this.value), 0);
  8340. numInput.value = newValue.toString();
  8341. }
  8342.  
  8343. function updateKeepMaxValue(keepInput, init)
  8344. {
  8345. if (init === void 0)
  8346. {
  8347. init = false;
  8348. }
  8349. var itemInput = document.getElementById('npc-sell-item-chosen');
  8350. if (!itemInput)
  8351. {
  8352. return;
  8353. }
  8354. var item = itemInput.value;
  8355. var max = getGameValue(item);
  8356. keepInput.max = max.toString();
  8357. if (init)
  8358. {
  8359. observer.addTick(function ()
  8360. {
  8361. return updateKeepMaxValue(keepInput);
  8362. });
  8363. }
  8364. }
  8365.  
  8366. function ensureKeepInput(item)
  8367. {
  8368. var numInput = ensureNumberInput('dialogue-input-cmd');
  8369. var parent = numInput && numInput.parentElement;
  8370. var next = numInput && numInput.nextElementSibling;
  8371. var nextNext = next && next.nextElementSibling;
  8372. if (next && nextNext && parent)
  8373. {
  8374. if (nextNext.nodeName === 'BR' && settings.get(settings.KEY.addKeepInput))
  8375. {
  8376. var div = document.createElement('div');
  8377. var text = document.createTextNode('Keep: ');
  8378. div.appendChild(text);
  8379. var keepInput = document.createElement('input');
  8380. keepInput.type = 'number';
  8381. keepInput.value = keepInput.min = '0';
  8382. keepInput.max = getGameValue(item).toString();
  8383. keepInput.addEventListener('keyup', watchKeepInput);
  8384. keepInput.addEventListener('mouseup', watchKeepInput);
  8385. updateKeepMaxValue(keepInput, true);
  8386. div.appendChild(keepInput);
  8387. parent.insertBefore(div, nextNext);
  8388. }
  8389. else if (nextNext.nodeName !== 'BR' && !settings.get(settings.KEY.addKeepInput))
  8390. {
  8391. var br = document.createElement('br');
  8392. parent.insertBefore(br, nextNext);
  8393. parent.removeChild(nextNext);
  8394. }
  8395. }
  8396. }
  8397.  
  8398. function init()
  8399. {
  8400. var _multiCraft = win.multiCraft;
  8401. win.multiCraft = function (item)
  8402. {
  8403. _multiCraft(item);
  8404. ensureMaxBtn('dialogue-multicraft-chosen', 'dialogue-multicraft-input', win.craftingRecipes, item);
  8405. };
  8406. var _brew = win.brew;
  8407. win.brew = function (potion)
  8408. {
  8409. _brew(potion);
  8410. ensureMaxBtn('dialogue-potion-chosen', 'dialogue-brewing-input', win.brewingRecipes, potion);
  8411. };
  8412. var _cooksBookInputDialogue = win.cooksBookInputDialogue;
  8413. win.cooksBookInputDialogue = function (food)
  8414. {
  8415. _cooksBookInputDialogue(food);
  8416. ensureMaxBtn('dialogue-cooksBook-chosen', 'dialogue-cooksBook-input', win.cooksBookRecipes, food);
  8417. };
  8418. var _openSellNPCDialogue = win.openSellNPCDialogue;
  8419. win.openSellNPCDialogue = function (item)
  8420. {
  8421. _openSellNPCDialogue(item);
  8422. ensureKeepInput(item);
  8423. };
  8424. var allowedInputs = [
  8425. 'dialogue-ashes'
  8426. , 'dialogue-bindDonorCoins'
  8427. , 'dialogue-bonemeal'
  8428. , 'dialogue-bones'
  8429. , 'dialogue-brewing'
  8430. , 'dialogue-buy-item-2'
  8431. , 'dialogue-buyFromMarket'
  8432. , 'dialogue-charcoalFoundry'
  8433. , 'dialogue-consume'
  8434. , 'dialogue-cooksBook'
  8435. , 'dialogue-createArrows'
  8436. , 'dialogue-createFireArrows'
  8437. , 'dialogue-createIceArrows'
  8438. , 'dialogue-furnace'
  8439. , 'dialogue-iceBones'
  8440. , 'dialogue-id-boundHammer'
  8441. , 'dialogue-id-boundPickaxe'
  8442. , 'dialogue-id-cook-food'
  8443. , 'dialogue-id-oven-addheat'
  8444. , 'dialogue-market-chosenpostitem'
  8445. , 'dialogue-multicraft'
  8446. , 'dialogue-oilBarrels'
  8447. , 'dialogue-oilFactory'
  8448. , 'dialogue-sell-item'
  8449. , 'dialogue-stardustCrystals'
  8450. , 'dialogue-wand'
  8451. ];
  8452. var _openDialogue = win.openDialogue;
  8453. win.openDialogue = function (id, width, position)
  8454. {
  8455. _openDialogue(id, width, position);
  8456. if (allowedInputs.indexOf(id) === -1
  8457. || id === 'dialogue-buyFromMarket' && market.detectTedsUI()
  8458. || id === 'dialogue-market-chosenpostitem' && market.detectTedsUI())
  8459. {
  8460. return;
  8461. }
  8462. var dialog = document.getElementById(id);
  8463. var input = dialog && dialog.querySelector('input[type="text"],input[type="number"]');
  8464. if (!input)
  8465. {
  8466. return;
  8467. }
  8468. ensureNumberInput(input);
  8469. };
  8470. }
  8471. amountInputs.init = init;
  8472. })(amountInputs || (amountInputs = {}));
  8473.  
  8474. /**
  8475. * improves the top bar
  8476. */
  8477. var newTopbar;
  8478. (function (newTopbar)
  8479. {
  8480. newTopbar.name = 'newTopbar';
  8481. var linkCell, tabCell, infoCell;
  8482. var addQueues = {
  8483. link: []
  8484. , tab: []
  8485. , info: []
  8486. };
  8487.  
  8488. function createPipeNode()
  8489. {
  8490. return document.createTextNode('|');
  8491. }
  8492.  
  8493. function addLinkEntry(el)
  8494. {
  8495. if (!linkCell)
  8496. {
  8497. addQueues.link.push(el);
  8498. }
  8499. else
  8500. {
  8501. linkCell.appendChild(createPipeNode());
  8502. linkCell.appendChild(el);
  8503. }
  8504. }
  8505. newTopbar.addLinkEntry = addLinkEntry;
  8506.  
  8507. function addTabEntry(el)
  8508. {
  8509. if (!tabCell)
  8510. {
  8511. addQueues.tab.push(el);
  8512. }
  8513. else
  8514. {
  8515. tabCell.appendChild(createPipeNode());
  8516. tabCell.appendChild(el);
  8517. }
  8518. }
  8519. newTopbar.addTabEntry = addTabEntry;
  8520.  
  8521. function addInfoEntry(el)
  8522. {
  8523. if (!infoCell)
  8524. {
  8525. addQueues.info.push(el);
  8526. }
  8527. else
  8528. {
  8529. if (infoCell.firstChild)
  8530. {
  8531. infoCell.insertBefore(createPipeNode(), infoCell.firstChild);
  8532. infoCell.insertBefore(el, infoCell.firstChild);
  8533. }
  8534. else
  8535. {
  8536. infoCell.appendChild(createPipeNode());
  8537. infoCell.appendChild(el);
  8538. }
  8539. }
  8540. }
  8541. newTopbar.addInfoEntry = addInfoEntry;
  8542.  
  8543. function init()
  8544. {
  8545. if (!settings.get(settings.KEY.useNewToolbar))
  8546. {
  8547. return;
  8548. }
  8549. 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");
  8550. var table = document.querySelector('table.top-links');
  8551. if (!table)
  8552. {
  8553. return;
  8554. }
  8555. var row = table.rows.item(0);
  8556. var cells = row.cells;
  8557. var tabIdx = [2, 5];
  8558. var infoIdx = [6, 7];
  8559. var newRow = table.insertRow(-1);
  8560. linkCell = newRow.insertCell(-1);
  8561. tabCell = newRow.insertCell(-1);
  8562. tabCell.style.textAlign = 'center';
  8563. infoCell = newRow.insertCell(-1);
  8564. infoCell.style.textAlign = 'right';
  8565. for (var i = 0; i < cells.length; i++)
  8566. {
  8567. var container = linkCell;
  8568. if (tabIdx.indexOf(i) != -1)
  8569. {
  8570. container = tabCell;
  8571. }
  8572. else if (infoIdx.indexOf(i) != -1)
  8573. {
  8574. container = infoCell;
  8575. }
  8576. var cell = cells.item(i);
  8577. var el = cell.firstElementChild;
  8578. if (cell.childNodes.length > 1)
  8579. {
  8580. el = document.createElement('span');
  8581. el.style.color = 'yellow';
  8582. while (cell.childNodes.length > 0)
  8583. {
  8584. el.appendChild(cell.childNodes[0]);
  8585. }
  8586. }
  8587. if (container.children.length > 0)
  8588. {
  8589. container.appendChild(createPipeNode());
  8590. }
  8591. if (el)
  8592. {
  8593. container.appendChild(el);
  8594. }
  8595. }
  8596. var parent = row.parentElement;
  8597. if (parent)
  8598. {
  8599. parent.removeChild(row);
  8600. }
  8601. for (var _i = 0, _a = addQueues.link; _i < _a.length; _i++)
  8602. {
  8603. var el = _a[_i];
  8604. addLinkEntry(el);
  8605. }
  8606. for (var _b = 0, _c = addQueues.tab; _b < _c.length; _b++)
  8607. {
  8608. var el = _c[_b];
  8609. addTabEntry(el);
  8610. }
  8611. for (var _d = 0, _e = addQueues.info; _d < _e.length; _d++)
  8612. {
  8613. var el = _e[_d];
  8614. addInfoEntry(el);
  8615. }
  8616. var _openTab = win.openTab;
  8617. win.openTab = function (newTab)
  8618. {
  8619. var oldTab = win.currentOpenTab;
  8620. _openTab(newTab);
  8621. var children = tabCell.children;
  8622. for (var i = 0; i < children.length; i++)
  8623. {
  8624. var el = children[i];
  8625. var match = (el.getAttribute('onclick') || '').match(/openTab\('([^']+)'\)/);
  8626. if (!match)
  8627. {
  8628. continue;
  8629. }
  8630. var tab = match[1];
  8631. if (oldTab == tab)
  8632. {
  8633. el.style.color = '';
  8634. }
  8635. if (newTab == tab)
  8636. {
  8637. el.style.color = 'white';
  8638. }
  8639. }
  8640. };
  8641. }
  8642. newTopbar.init = init;
  8643. })(newTopbar || (newTopbar = {}));
  8644.  
  8645. /**
  8646. * style tweaks
  8647. */
  8648. var styleTweaks;
  8649. (function (styleTweaks)
  8650. {
  8651. styleTweaks.name = 'styleTweaks';
  8652. var bodyRegex = /(\bbody)(\s|$)/i;
  8653.  
  8654. function addTweakStyle(setting, style)
  8655. {
  8656. if (setting != '')
  8657. {
  8658. var prefix_1 = setting === '' ? '' : 'body.' + setting + ' ';
  8659. style = style
  8660. .replace(/(^\s*|\}\s*)([^\{\}]+)(?=\s*\{)/g, function (wholeMatch, before, rules)
  8661. {
  8662. return before + rules.split(',').map(function (rule)
  8663. {
  8664. if (bodyRegex.test(rule) && setting !== '')
  8665. {
  8666. return rule.replace(bodyRegex, '$1.' + setting + '$2');
  8667. }
  8668. return rule.replace(/^(\s*\n\s*)?/, '$1' + prefix_1);
  8669. }).join(',');
  8670. });
  8671. document.body.classList.add(setting);
  8672. }
  8673. addStyle(style, setting != '' ? setting : null);
  8674. }
  8675. // tweak oil production/consumption
  8676. function tweakOil()
  8677. {
  8678. 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");
  8679. // make room for oil cell on small devices
  8680. var oilFlowValues = document.getElementById('oil-flow-values');
  8681. var oilFlowCell = oilFlowValues.parentElement;
  8682. oilFlowCell.style.width = '30%';
  8683. }
  8684.  
  8685. function tweakSelection()
  8686. {
  8687. 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");
  8688. }
  8689. // tweak stardust monitor of DH2QoL to keep it in place
  8690. function tweakStardust()
  8691. {
  8692. 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");
  8693. }
  8694.  
  8695. function tweakSkillLevelText()
  8696. {
  8697. addTweakStyle('', "\ndiv.skill-xp-label\n{\n\ttext-shadow: white 0px 0px 0.5rem;\n}\n\t\t");
  8698. }
  8699.  
  8700. function tweakFightDialog()
  8701. {
  8702. 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");
  8703. }
  8704.  
  8705. function addAdditionalSkillBars()
  8706. {
  8707. var _loadSkillTabs = win.loadSkillTabs;
  8708. win.loadSkillTabs = function ()
  8709. {
  8710. _loadSkillTabs();
  8711. for (var _i = 0, SKILL_LIST_1 = SKILL_LIST; _i < SKILL_LIST_1.length; _i++)
  8712. {
  8713. var skill = SKILL_LIST_1[_i];
  8714. var unlocked = getGameValue(skill + 'Unlocked') == 1;
  8715. if (!unlocked)
  8716. {
  8717. continue;
  8718. }
  8719. var xp = getGameValue(skill + 'Xp');
  8720. var currentLevelXp = win.getXpNeeded(win.getLevel(xp));
  8721. var nextLevelXp = win.getXpNeeded(win.getLevel(xp) + 1);
  8722. var perc = (xp - currentLevelXp) / (nextLevelXp - currentLevelXp) * 100;
  8723. var progress = document.getElementById('skill-progress-' + skill);
  8724. if (progress)
  8725. {
  8726. if (currentLevelXp >= 10000000)
  8727. {
  8728. perc = 100;
  8729. progress.style.backgroundColor = "yellow";
  8730. }
  8731. progress.style.width = perc + '%';
  8732. }
  8733. }
  8734. };
  8735. // init additional skill bars
  8736. 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");
  8737. for (var _i = 0, SKILL_LIST_2 = SKILL_LIST; _i < SKILL_LIST_2.length; _i++)
  8738. {
  8739. var skill = SKILL_LIST_2[_i];
  8740. var cell = document.getElementById('top-bar-level-td-' + skill);
  8741. if (!cell)
  8742. {
  8743. continue;
  8744. }
  8745. var levelBar = document.createElement('div');
  8746. levelBar.className = 'skill-bar';
  8747. var progress = document.createElement('div');
  8748. progress.id = 'skill-progress-' + skill;
  8749. progress.className = 'skill-progress';
  8750. levelBar.appendChild(progress);
  8751. cell.appendChild(levelBar);
  8752. // update skill level progress bars on click
  8753. levelBar.addEventListener('click', function ()
  8754. {
  8755. return win.loadSkillTabs();
  8756. });
  8757. }
  8758. win.loadSkillTabs();
  8759. }
  8760. // highlight cooking level requirement when not matched
  8761. function highlightCookinglevel()
  8762. {
  8763. var _cookFoodDialogue = win.cookFoodDialogue;
  8764. win.cookFoodDialogue = function (rawFood)
  8765. {
  8766. _cookFoodDialogue(rawFood);
  8767. var dialog = document.getElementById('dialogue-id-cook-food');
  8768. if (!dialog)
  8769. {
  8770. return;
  8771. }
  8772. var levelReq = document.getElementById('dialogue-cook-levelReq');
  8773. var levelReqLabel = levelReq && levelReq.previousElementSibling;
  8774. if (!levelReq || !levelReqLabel)
  8775. {
  8776. return;
  8777. }
  8778. var fulfilled = win.getCookingLevelReq(rawFood) > win.getLevel(win.cookingXp);
  8779. levelReq.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  8780. levelReq.style.fontWeight = fulfilled ? 'bold' : '';
  8781. levelReqLabel.style.color = fulfilled ? 'rgb(204, 0, 0)' : '';
  8782. var ratioEl = document.getElementById('dialogue-cook-ratio');
  8783. if (!ratioEl)
  8784. {
  8785. var cookReqBox = levelReq.parentElement;
  8786. var br = document.createElement('br');
  8787. cookReqBox.appendChild(br);
  8788. var b = document.createElement('b');
  8789. 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\">: ";
  8790. cookReqBox.appendChild(b);
  8791. ratioEl = document.createElement('span');
  8792. ratioEl.id = 'dialogue-cook-ratio';
  8793. cookReqBox.appendChild(ratioEl);
  8794. }
  8795. var heat = win.getHeatNeeded(rawFood);
  8796. var energy = win.getEnergyGained(rawFood);
  8797. ratioEl.textContent = format.number(Math.round(energy / heat * 100) / 100);
  8798. };
  8799. }
  8800.  
  8801. function amountStyle()
  8802. {
  8803. var tweakName = 'amount-symbol';
  8804. 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");
  8805.  
  8806. function setAmountSymbolVisibility(init)
  8807. {
  8808. if (init === void 0)
  8809. {
  8810. init = false;
  8811. }
  8812. var show = settings.get(settings.KEY.amountSymbol);
  8813. document.body.classList[show ? 'add' : 'remove'](tweakName);
  8814. if (init)
  8815. {
  8816. settings.observe(settings.KEY.amountSymbol, function ()
  8817. {
  8818. return setAmountSymbolVisibility();
  8819. });
  8820. }
  8821. }
  8822. setAmountSymbolVisibility(true);
  8823. }
  8824.  
  8825. function efficiency()
  8826. {
  8827. var EFFICIENCY_CLASS = 'efficiency';
  8828. 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");
  8829. var farmingTab = document.getElementById('tab-container-farming');
  8830. if (farmingTab)
  8831. {
  8832. removeWhitespaceChildNodes(farmingTab);
  8833. }
  8834. var combatSubTab = document.getElementById('tab-sub-container-combat');
  8835. if (combatSubTab)
  8836. {
  8837. removeWhitespaceChildNodes(combatSubTab);
  8838. }
  8839.  
  8840. function checkSetting(init)
  8841. {
  8842. if (init === void 0)
  8843. {
  8844. init = false;
  8845. }
  8846. var show = settings.get(settings.KEY.useEfficiencyStyle);
  8847. document.body.classList[show ? 'add' : 'remove'](EFFICIENCY_CLASS);
  8848. if (init)
  8849. {
  8850. settings.observe(settings.KEY.useEfficiencyStyle, function ()
  8851. {
  8852. return checkSetting();
  8853. });
  8854. }
  8855. }
  8856. checkSetting(true);
  8857. }
  8858.  
  8859. function hardcore()
  8860. {
  8861. if (win.isHardcore != 1)
  8862. {
  8863. return;
  8864. }
  8865. addStyle("\nspan#shop-giant-button-playermarket\n{\n\tbackground-color: gray;\n\tbackground-image: none;\n\tcursor: not-allowed;\n}\n\t\t");
  8866. var marketBtn = document.getElementById('shop-giant-button-playermarket');
  8867. if (marketBtn)
  8868. {
  8869. marketBtn.removeAttribute('onclick');
  8870. marketBtn.setAttribute('title', 'The player market is disabled for hardcore accounts');
  8871. }
  8872. }
  8873.  
  8874. function smallScreen()
  8875. {
  8876. addStyle("\ntable.top-links\n{\n\tz-index: 10;\n}\n\t\t");
  8877. }
  8878.  
  8879. function init()
  8880. {
  8881. tweakOil();
  8882. tweakSelection();
  8883. tweakStardust();
  8884. tweakSkillLevelText();
  8885. tweakFightDialog();
  8886. addAdditionalSkillBars();
  8887. highlightCookinglevel();
  8888. amountStyle();
  8889. efficiency();
  8890. hardcore();
  8891. smallScreen();
  8892. }
  8893. styleTweaks.init = init;
  8894. })(styleTweaks || (styleTweaks = {}));
  8895.  
  8896. /**
  8897. * add ingame notification boxes
  8898. */
  8899. var notifBoxes;
  8900. (function (notifBoxes)
  8901. {
  8902. notifBoxes.name = 'notifBoxes';
  8903.  
  8904. function addNotifBox(imageKey, itemKey, showFront)
  8905. {
  8906. if (itemKey === void 0)
  8907. {
  8908. itemKey = null;
  8909. }
  8910. if (showFront === void 0)
  8911. {
  8912. showFront = false;
  8913. }
  8914. var notifBox = document.createElement('span');
  8915. notifBox.className = 'notif-box';
  8916. notifBox.id = 'notification-static-' + imageKey;
  8917. notifBox.style.display = 'none';
  8918. if (showFront)
  8919. {
  8920. notifBox.style.cssFloat = 'left';
  8921. }
  8922. notifBox.innerHTML = "<img src=\"images/" + imageKey + ".png\" class=\"image-icon-50\" id=\"notif-" + imageKey + "-img\">";
  8923. if (itemKey != null)
  8924. {
  8925. notifBox.innerHTML += "<span data-item-display=\"" + itemKey + "\" style=\"margin-left: 10px;\"></span>";
  8926. }
  8927. var notifArea = document.getElementById('notifaction-area');
  8928. if (notifArea)
  8929. {
  8930. notifArea.appendChild(notifBox);
  8931. }
  8932. return notifBox;
  8933. }
  8934.  
  8935. function addWorker()
  8936. {
  8937. var notifBox = addNotifBox('workers', null, true);
  8938.  
  8939. function setVisibility()
  8940. {
  8941. var show = win.workersTimer === 1 && settings.get(settings.KEY.useWorkerNotif);
  8942. notifBox.style.display = show ? '' : 'none';
  8943. }
  8944. setVisibility();
  8945. observer.addTick(function ()
  8946. {
  8947. return setVisibility();
  8948. });
  8949. }
  8950. function addFaradoxCrystal()
  8951. {
  8952. var notifBox = addNotifBox('faradoxsLair/tombIcon', null, true);
  8953.  
  8954. function setVisibility()
  8955. {
  8956. var show = win.tombCd == 0 && quest16 == -1 && settings.get(settings.KEY.useTombNotif);
  8957. notifBox.style.display = show ? '' : 'none';
  8958. }
  8959. setVisibility();
  8960. observer.addTick(function ()
  8961. {
  8962. return setVisibility();
  8963. });
  8964. }
  8965. function addBobsUncle()
  8966. {
  8967. var notifBox = addNotifBox('bobsUncle', null, true);
  8968.  
  8969. function setVisibility()
  8970. {
  8971. var show = win.farmingPatchStage7 == 4 && settings.get(settings.KEY.useBobsUncleNotif);
  8972. notifBox.style.display = show ? '' : 'none';
  8973. }
  8974. setVisibility();
  8975. observer.addTick(function ()
  8976. {
  8977. return setVisibility();
  8978. });
  8979. }
  8980. function init()
  8981. {
  8982. 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");
  8983. // remove pure text nodes
  8984. var notifArea = document.getElementById('notifaction-area');
  8985. if (notifArea)
  8986. {
  8987. removeWhitespaceChildNodes(notifArea);
  8988. }
  8989. addWorker();
  8990. addBobsUncle();
  8991. addFaradoxCrystal();
  8992. }
  8993. notifBoxes.init = init;
  8994. })(notifBoxes || (notifBoxes = {}));
  8995.  
  8996.  
  8997. /**
  8998. * extend market
  8999. */
  9000. var market;
  9001. (function (market)
  9002. {
  9003. market.name = 'market';
  9004. // max limit age: 5min
  9005. var MAX_LIMIT_AGE = 5 * 60 * 1e3;
  9006. var PRICE_HISTORY_KEY = 'priceHistory';
  9007. // 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)
  9008. var MAX_ENTRIES_PER_ITEM = 2e3;
  9009. var SYNC_URL_REGEX = /^(?:https?:\/\/)?(?:(?:www\.)?myjson\.com\/|api\.myjson\.com\/bins\/)([^\/]+)$/i;
  9010. var detectedTedsUIOnce = false;
  9011.  
  9012. function detectTedsUI()
  9013. {
  9014. return detectedTedsUIOnce = detectedTedsUIOnce || typeof win.changeSetting === 'function';
  9015. }
  9016. market.detectTedsUI = detectTedsUI;
  9017. var priceHistory = store.has(PRICE_HISTORY_KEY) ? store.get(PRICE_HISTORY_KEY) :
  9018. {};
  9019. var getItemColor = function (H, S, L)
  9020. {
  9021. return [
  9022. "hsl(" + H + ", " + S + "%, " + L + "%)"
  9023. , "hsl(" + H + ", " + S + "%, " + (L < 35 ? L + 35 : L - 35) + "%)"
  9024. ];
  9025. };
  9026. var itemColor = {
  9027. 'blewitMushroom': getItemColor(255, 100, 78)
  9028. , 'bronzeBar': getItemColor(39, 100, 46)
  9029. , 'crystalLeaf': getItemColor(226, 100, 50)
  9030. , 'diamond': getItemColor(186, 76, 82)
  9031. , 'dottedGreenLeaf': getItemColor(92, 63, 19)
  9032. , 'emerald': getItemColor(110, 100, 48)
  9033. , 'goldLeaf': getItemColor(50, 100, 50)
  9034. , 'goldBar': getItemColor(54, 100, 46)
  9035. , 'greenLeaf': getItemColor(92, 63, 28)
  9036. , 'ironBar': getItemColor(44, 11, 46)
  9037. , 'limeLeaf': getItemColor(110, 72, 40)
  9038. , 'promethiumBar': getItemColor(354, 81, 46)
  9039. , 'redMushroom': getItemColor(0, 83, 48)
  9040. , 'ruby': getItemColor(5, 87, 45)
  9041. , 'sapphire': getItemColor(197, 100, 32)
  9042. , 'shrimp': getItemColor(17, 88, 50)
  9043. , 'silverBar': getItemColor(0, 0, 74)
  9044. , 'snapegrass': getItemColor(120, 99, 42)
  9045. , 'stardust': getItemColor(37, 100, 50)
  9046. //, 'strangeLeaf': getItemColor(195, 100, 40)
  9047. };
  9048. // use ambassadors to name the categories
  9049. var categoryAmbassador2CategoryName = {
  9050. 'stone': 'Ores' // 0
  9051. , 'emptyChisel': 'Crystals' // 1
  9052. , 'bronzeBar': 'Bars' // 2
  9053. , 'dottedGreenLeafSeeds': 'Seeds' // 3
  9054. , 'logs': 'Logs' // 4
  9055. , 'dottedGreenLeaf': 'Ingredients' // 5
  9056. , 'rawShrimp': 'Fish' // 6
  9057. , 'shrimp': 'Food' // 7
  9058. , 'stinger': 'Equipment' // 8
  9059. , 'promethiumHelmetMould': 'Mould' // 9
  9060. //, 'essence': 'Magic' // 10
  9061. , 'blueFishingRodOrb': 'Orbs' // 10
  9062. , 'stardust': 'Other' // 11
  9063. };
  9064. var item2Category = new Map();
  9065. var category2Name = new Map();
  9066. var item2Resolver = new Map();
  9067. var itemLimits = new Map();
  9068. var offerPerItem = new Map();
  9069. var offerList = new Array();
  9070. var lastSyncValue = '{}';
  9071.  
  9072. function getSyncUrl()
  9073. {
  9074. if (!settings.get(settings.KEY.syncPriceHistory))
  9075. {
  9076. return null;
  9077. }
  9078. var url = settings.getSub(settings.KEY.syncPriceHistory, 'url');
  9079. var match = url.match(SYNC_URL_REGEX);
  9080. if (!match)
  9081. {
  9082. console.error('URL "' + url + '" does not match the expected pattern: ' + SYNC_URL_REGEX.source);
  9083. return null;
  9084. }
  9085. return 'https://api.myjson.com/bins/' + match[1];
  9086. }
  9087.  
  9088. function integratePriceData(data, responseText)
  9089. {
  9090. var changed = recIntegrate(data, priceHistory);
  9091. lastSyncValue = responseText;
  9092. return changed;
  9093.  
  9094. function recIntegrate(source, target)
  9095. {
  9096. var changed = false;
  9097. if (typeof source !== typeof target)
  9098. {
  9099. console.error('Different data types. Could not integrate data into local price history.\nsource: ' + JSON.stringify(source) + '\ntarget: ' + JSON.stringify(target));
  9100. }
  9101. else if (typeof source === 'object')
  9102. {
  9103. for (var key in source)
  9104. {
  9105. if (source.hasOwnProperty(key))
  9106. {
  9107. if (!target.hasOwnProperty(key))
  9108. {
  9109. target[key] = source[key];
  9110. changed = true;
  9111. }
  9112. else if (recIntegrate(source[key], target[key]))
  9113. {
  9114. changed = true;
  9115. }
  9116. }
  9117. }
  9118. }
  9119. else
  9120. {
  9121. // do nothing and prefer the local value
  9122. }
  9123. return changed;
  9124. }
  9125. }
  9126.  
  9127. function loadPriceHistory()
  9128. {
  9129. var url = getSyncUrl();
  9130. if (url)
  9131. {
  9132. win.$.get(url, function (data, textStatus, jqXHR)
  9133. {
  9134. if (integratePriceData(data, jqXHR.responseText))
  9135. {
  9136. savePriceHistory(true);
  9137. }
  9138. });
  9139. }
  9140. }
  9141.  
  9142. function savePriceHistory(forceWrite)
  9143. {
  9144. if (forceWrite === void 0)
  9145. {
  9146. forceWrite = false;
  9147. }
  9148. for (var itemKey in priceHistory)
  9149. {
  9150. var history_1 = priceHistory[itemKey];
  9151. var timestampList = Object.keys(history_1).sort();
  9152. var i = 0;
  9153. for (var _i = 0, timestampList_1 = timestampList; _i < timestampList_1.length; _i++)
  9154. {
  9155. var timestamp = timestampList_1[_i];
  9156. i++;
  9157. if (i > MAX_ENTRIES_PER_ITEM)
  9158. {
  9159. delete history_1[timestamp];
  9160. }
  9161. }
  9162. }
  9163. store.set(PRICE_HISTORY_KEY, priceHistory);
  9164. var url = getSyncUrl();
  9165. if (url)
  9166. {
  9167. var doPut_1 = function ()
  9168. {
  9169. $.ajax(
  9170. {
  9171. url: url
  9172. , type: 'PUT'
  9173. , data: JSON.stringify(priceHistory)
  9174. , contentType: 'application/json; charset=utf-8'
  9175. , dataType: 'json'
  9176. , success: function (data, textStatus, jqXHR)
  9177. {
  9178. lastSyncValue = jqXHR.responseText;
  9179. }
  9180. });
  9181. };
  9182. if (forceWrite === true)
  9183. {
  9184. doPut_1();
  9185. }
  9186. else
  9187. {
  9188. win.$.get(url, function (data, textStatus, jqXHR)
  9189. {
  9190. if (lastSyncValue !== jqXHR.responseText)
  9191. {
  9192. integratePriceData(data, jqXHR.responseText);
  9193. }
  9194. doPut_1();
  9195. });
  9196. }
  9197. }
  9198. }
  9199.  
  9200. function processMarketData(data)
  9201. {
  9202. var nowKey = now();
  9203. offerPerItem = new Map();
  9204. offerList = new Array();
  9205. if (data != 'NONE')
  9206. {
  9207. offerList = data.split(';').map(function (offerData)
  9208. {
  9209. var values = offerData.split('~');
  9210. var itemId = Number(values[1]);
  9211. var itemKey = win.jsItemArray[itemId];
  9212. var itemName = key2Name(itemKey);
  9213. var categoryId = item2Category.has(itemKey) ? item2Category.get(itemKey) : -1;
  9214. var offer = {
  9215. offerId: Number(values[0])
  9216. , itemId: itemId
  9217. , itemKey: itemKey
  9218. , itemName: itemName
  9219. , categoryId: categoryId
  9220. , amount: Number(values[2])
  9221. , price: Number(values[3])
  9222. , timeLeft: values[4]
  9223. , playerId: Number(values[5])
  9224. };
  9225. if (!offerPerItem.has(itemKey))
  9226. {
  9227. offerPerItem.set(itemKey, []);
  9228. }
  9229. offerPerItem.get(itemKey).push(offer);
  9230. var history = priceHistory[itemKey];
  9231. if (!history)
  9232. {
  9233. history = {};
  9234. priceHistory[itemKey] = history;
  9235. }
  9236. if (!history.hasOwnProperty(nowKey)
  9237. || history[nowKey] > offer.price)
  9238. {
  9239. history[nowKey] = offer.price;
  9240. }
  9241. return offer;
  9242. });
  9243. }
  9244. savePriceHistory();
  9245. }
  9246.  
  9247. function processItemLimits(itemKey, lowerLimit, upperLimit)
  9248. {
  9249. var limit = {
  9250. timestamp: now()
  9251. , min: lowerLimit
  9252. , max: upperLimit
  9253. };
  9254. itemLimits.set(itemKey, limit);
  9255. if (item2Resolver.has(itemKey))
  9256. {
  9257. var limitArr_1 = [lowerLimit, upperLimit];
  9258. item2Resolver.get(itemKey).forEach(function (resolve)
  9259. {
  9260. return resolve(limitArr_1);
  9261. });
  9262. item2Resolver.delete(itemKey);
  9263. return false;
  9264. }
  9265. return true;
  9266. }
  9267.  
  9268. function showOfferCancelCooldown()
  9269. {
  9270. if (detectTedsUI())
  9271. {
  9272. return;
  9273. }
  9274. 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");
  9275.  
  9276. function slotCooldown(i, init)
  9277. {
  9278. if (init === void 0)
  9279. {
  9280. init = false;
  9281. }
  9282. var cooldownKey = 'marketCancelCooldownSlot' + i;
  9283. var btn = document.getElementById('market-slot-' + i + '-cancel-btn');
  9284. if (btn)
  9285. {
  9286. btn.dataset.cooldown = detectTedsUI() ? '0' : getGameValue(cooldownKey).toString();
  9287. }
  9288. if (init)
  9289. {
  9290. observer.add(cooldownKey, function ()
  9291. {
  9292. return slotCooldown(i);
  9293. });
  9294. }
  9295. }
  9296. for (var i = 1; i <= 3; i++)
  9297. {
  9298. slotCooldown(i, true);
  9299. }
  9300. }
  9301.  
  9302. function addExtraBtns()
  9303. {
  9304. var browseBtn = document.querySelector('.market-browse-button');
  9305. if (!browseBtn)
  9306. {
  9307. return;
  9308. }
  9309. var HISTORY_CLASS = 'local-history';
  9310. var paddingLeft = 30 + 42;
  9311. var paddingRight = 30;
  9312. 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");
  9313. var historyBtn = document.createElement('span');
  9314. historyBtn.className = 'market-browse-button ' + HISTORY_CLASS;
  9315. browseBtn.appendChild(historyBtn);
  9316. var historyItemKey = null;
  9317. var _postItemDialogue = win.postItemDialogue;
  9318. win.postItemDialogue = function (offerTypeEl, itemName, inputEl)
  9319. {
  9320. historyItemKey = itemName;
  9321. _postItemDialogue(offerTypeEl, itemName, inputEl);
  9322. };
  9323. var PRICE_HISTORY_DIALOG_ID = 'dialog-price-history';
  9324. var PRICE_HISTORY_ID = 'price-history';
  9325. var PRICE_HISTORY_ITEM_SELECT_ID = 'price-history-item-select';
  9326. 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");
  9327. var dialog = document.createElement('dialog');
  9328. dialog.id = PRICE_HISTORY_DIALOG_ID;
  9329. dialog.style.display = 'none';
  9330. dialog.style.overflowX = 'hidden';
  9331. 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";
  9332. document.body.appendChild(dialog);
  9333. var itemSelect = document.getElementById(PRICE_HISTORY_ITEM_SELECT_ID);
  9334. var $itemSelect = win.$(itemSelect);
  9335.  
  9336. function loadScripts(urlList, callback)
  9337. {
  9338. var url = urlList[0];
  9339. if (!url)
  9340. {
  9341. callback && callback();
  9342. return;
  9343. }
  9344. var script = document.createElement('script');
  9345. script.src = url;
  9346. script.onload = function ()
  9347. {
  9348. return loadScripts(urlList.slice(1), callback);
  9349. };
  9350. document.head.appendChild(script);
  9351. }
  9352. var monthArray = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  9353. var lastHistoryItemKey;
  9354. var itemKey2SeriesId = {};
  9355. var chart;
  9356. var stage;
  9357. // add style for select2
  9358. var style = document.createElement('link');
  9359. style.rel = 'stylesheet';
  9360. style.href = 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css';
  9361. document.head.appendChild(style);
  9362. loadScripts([
  9363. 'https://cdn.anychart.com/js/7.14.3/anychart.min.js'
  9364. , 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js'
  9365. ], function ()
  9366. {
  9367. chart = win.anychart.area();
  9368. // pass the container id, chart will be displayed there
  9369. stage = anychart.graphics.create(PRICE_HISTORY_ID);
  9370. chart.container(stage);
  9371. var tooltip = chart.tooltip();
  9372. tooltip.displayMode('union');
  9373. tooltip.format(passThis(function (context)
  9374. {
  9375. var name = context.seriesName || 'Price';
  9376. return name + ': ' + (isNaN(context.value) ? '-' : format.number(context.value));
  9377. }));
  9378. tooltip.titleFormat(passThis(function (context)
  9379. {
  9380. var d = new Date(context.x);
  9381. return monthArray[d.getMonth()] + ' ' + d.getDate() + ' @ ' + zeroPadLeft(d.getHours()) + ':' + zeroPadLeft(d.getMinutes()) + ':' + zeroPadLeft(d.getSeconds());
  9382. }));
  9383. var valueAxis = chart.yAxis();
  9384. valueAxis.title().text('Price').enabled(true);
  9385. valueAxis.labels().format(passThis(function (context)
  9386. {
  9387. return format.number(context.value);
  9388. }));
  9389. var timeAxis = chart.xAxis();
  9390. timeAxis.labels().format(passThis(function (context)
  9391. {
  9392. var d = new Date(context.tickValue);
  9393. return d.getDate() + '. ' + monthArray[d.getMonth()];
  9394. }));
  9395. var timeScale = win.anychart.scales.dateTime();
  9396. var ticks = timeScale.ticks();
  9397. ticks.interval(0, 0, 1);
  9398. chart.xScale(timeScale);
  9399. var timeScroller = chart.xScroller();
  9400. timeScroller.enabled(true);
  9401. chart.animation(true, 300);
  9402. chart.legend(true);
  9403. });
  9404. historyBtn.addEventListener('click', function (event)
  9405. {
  9406. event.preventDefault();
  9407. event.stopPropagation();
  9408. var height = Math.floor(.66 * window.innerHeight);
  9409. var width = Math.min(Math.floor(.66 * window.innerWidth), window.innerWidth - 30);
  9410. win.$(dialog).dialog(
  9411. {
  9412. title: 'Price history from local data'
  9413. , height: height
  9414. , width: width
  9415. });
  9416. dialog.style.height = (height) + 'px';
  9417. var itemKeyList = Object.keys(priceHistory).sort();
  9418. itemSelect.innerHTML = "";
  9419. var category2OptGroup = {};
  9420.  
  9421. function ensureOptGroup(categoryId)
  9422. {
  9423. var optGroup = category2OptGroup[categoryId];
  9424. if (!optGroup)
  9425. {
  9426. optGroup = document.createElement('optgroup');
  9427. optGroup.label = category2Name.get(categoryId) || 'Stuff';
  9428. itemSelect.appendChild(optGroup);
  9429. category2OptGroup[categoryId] = optGroup;
  9430. }
  9431. return optGroup;
  9432. }
  9433. var categoryList = Array.from(category2Name.keys()).map(function (id)
  9434. {
  9435. return Number(id);
  9436. }).sort();
  9437. for (var _i = 0, categoryList_1 = categoryList; _i < categoryList_1.length; _i++)
  9438. {
  9439. var categoryId = categoryList_1[_i];
  9440. ensureOptGroup(categoryId);
  9441. }
  9442. var itemKey2EnabledFn = {};
  9443.  
  9444. function replaceEnabled(itemKey, series)
  9445. {
  9446. var _enabled = series.enabled.bind(series);
  9447. itemKey2EnabledFn[itemKey] = _enabled;
  9448. series.enabled = function (value)
  9449. {
  9450. if (value !== undefined)
  9451. {
  9452. var itemList = $itemSelect.val();
  9453. var index = itemList.indexOf(itemKey);
  9454. if (index !== -1)
  9455. {
  9456. itemList.splice(index, 1);
  9457. }
  9458. else
  9459. {
  9460. itemList.push(itemKey);
  9461. }
  9462. $itemSelect.val(itemList).trigger('change');
  9463. }
  9464. return _enabled(value);
  9465. };
  9466. }
  9467. var min = Number.MAX_SAFE_INTEGER;
  9468. var max = 0;
  9469. var enabledSeriesList = [];
  9470. var _loop_1 = function (itemKey)
  9471. {
  9472. if (!itemColor[itemKey])
  9473. {
  9474. var baseColor = colorGenerator.getRandom(
  9475. {
  9476. format: 'hslArray'
  9477. });
  9478. var borderColor = baseColor.slice(0);
  9479. if (borderColor[2] < 35)
  9480. {
  9481. borderColor[2] += 35;
  9482. }
  9483. else
  9484. {
  9485. borderColor[2] -= 35;
  9486. }
  9487. itemColor[itemKey] = [
  9488. "hsl(" + baseColor[0] + ", " + baseColor[1] + "%, " + baseColor[2] + "%)"
  9489. , "hsl(" + borderColor[0] + ", " + borderColor[1] + "%, " + borderColor[2] + "%)"
  9490. ];
  9491. }
  9492. var history_2 = priceHistory[itemKey];
  9493. var keyList = Object.keys(history_2).sort();
  9494. var data = keyList
  9495. .map(function (n)
  9496. {
  9497. return ([
  9498. Number(n)
  9499. , history_2[n]
  9500. ]);
  9501. });
  9502. min = Math.min(Number(keyList[0]), min);
  9503. max = Math.max(Number(keyList[keyList.length - 1]), max);
  9504. var id = itemKey2SeriesId[itemKey];
  9505. var series = void 0;
  9506. if (id != null)
  9507. {
  9508. series = chart.getSeries(id);
  9509. series.data(data);
  9510. }
  9511. else
  9512. {
  9513. var hoverifyColor = function (hslColor)
  9514. {
  9515. return (
  9516. {
  9517. color: hslColor
  9518. , opacity: .8
  9519. });
  9520. };
  9521. series = chart.area(data);
  9522. itemKey2SeriesId[itemKey] = series.id();
  9523. series.name(key2Name(itemKey));
  9524. var bgColor = itemColor[itemKey][0];
  9525. var strokeColor = itemColor[itemKey][1];
  9526. series.fill(bgColor);
  9527. var bgColorHover = hoverifyColor(bgColor);
  9528. series.selectFill(bgColorHover);
  9529. series.hoverFill(bgColorHover);
  9530. series.stroke(strokeColor, 2);
  9531. var strokeColorHover = hoverifyColor(strokeColor);
  9532. series.hoverStroke(strokeColorHover, 2);
  9533. series.selectStroke(strokeColorHover, 2);
  9534. var markerOptions = {
  9535. fill: strokeColor
  9536. , size: 5
  9537. , type: 'circle'
  9538. };
  9539. series.hoverMarkers(markerOptions);
  9540. series.selectMarkers(markerOptions);
  9541. replaceEnabled(itemKey, series);
  9542. }
  9543. if (lastHistoryItemKey !== historyItemKey)
  9544. {
  9545. if (itemKey === historyItemKey)
  9546. {
  9547. enabledSeriesList.push(series);
  9548. }
  9549. series.enabled(false);
  9550. }
  9551. var categoryId = item2Category.has(itemKey) ? item2Category.get(itemKey) : -1;
  9552. var optGroup = ensureOptGroup(categoryId);
  9553. var option = document.createElement('option');
  9554. option.value = itemKey;
  9555. option.textContent = key2Name(itemKey);
  9556. optGroup.appendChild(option);
  9557. };
  9558. for (var _a = 0, itemKeyList_1 = itemKeyList; _a < itemKeyList_1.length; _a++)
  9559. {
  9560. var itemKey = itemKeyList_1[_a];
  9561. _loop_1(itemKey);
  9562. }
  9563. stage.listenOnce('renderfinish', function ()
  9564. {
  9565. enabledSeriesList.forEach(function (series)
  9566. {
  9567. return series.enabled(true);
  9568. });
  9569. });
  9570. var timeScale = chart.xScale();
  9571. timeScale.minimum(min);
  9572. timeScale.maximum(max);
  9573. var timeZoom = chart.xZoom();
  9574. var threeDaysLong = 3 * 24 * 60 * 60 * 1e3;
  9575. timeZoom.setToValues(Math.max(max - threeDaysLong, min), max);
  9576. // call the chart draw() method to initiate chart display
  9577. chart.draw(true);
  9578. // init item select
  9579. if ($itemSelect.data('select2'))
  9580. {
  9581. $itemSelect.select2('destroy');
  9582. }
  9583. $itemSelect.select2();
  9584.  
  9585. function getEnabledFn(event)
  9586. {
  9587. var data = event.params.data;
  9588. var itemKey = data.id;
  9589. var enabledFn = itemKey2EnabledFn[itemKey];
  9590. if (enabledFn)
  9591. {
  9592. return enabledFn;
  9593. }
  9594. else
  9595. {
  9596. var id = itemKey2SeriesId[itemKey];
  9597. var series = chart.getSeries(id);
  9598. return series.enabled.bind(series);
  9599. }
  9600. }
  9601. $itemSelect.on('select2:select', function (event)
  9602. {
  9603. getEnabledFn(event)(true);
  9604. });
  9605. $itemSelect.on('select2:unselect', function (event)
  9606. {
  9607. getEnabledFn(event)(false);
  9608. // close select menu when it was closed before an element has been removed
  9609. var openBefore = $itemSelect.data('select2').$container.hasClass('select2-container--open');
  9610. setTimeout(function ()
  9611. {
  9612. if (!openBefore && $itemSelect.data('select2').$container.hasClass('select2-container--open'))
  9613. {
  9614. $itemSelect.select2('close');
  9615. }
  9616. });
  9617. });
  9618. lastHistoryItemKey = historyItemKey;
  9619. });
  9620. }
  9621. var categoryList = [-1];
  9622. var itemListPerCategory = new Map();
  9623.  
  9624. function improveOfferList()
  9625. {
  9626. var itemArea = document.getElementById('dialogue-market-items-area');
  9627. if (itemArea)
  9628. {
  9629. var children = itemArea.children;
  9630. for (var i = 1; i < children.length; i++)
  9631. {
  9632. var categoryId = i - 1;
  9633. categoryList.push(categoryId);
  9634. var box = children.item(i);
  9635. var inputs = box.children;
  9636. for (var j = 0; j < inputs.length; j++)
  9637. {
  9638. var match = inputs.item(j).src.match(/images\/([^\/]+)\.(?:png|jpe?g|gif)/);
  9639. if (!match)
  9640. {
  9641. continue;
  9642. }
  9643. var itemKey = match[1];
  9644. item2Category.set(itemKey, categoryId);
  9645. if (categoryAmbassador2CategoryName[itemKey])
  9646. {
  9647. category2Name.set(categoryId, categoryAmbassador2CategoryName[itemKey]);
  9648. }
  9649. if (!itemListPerCategory.has(categoryId))
  9650. {
  9651. itemListPerCategory.set(categoryId, []);
  9652. }
  9653. itemListPerCategory.get(categoryId).push(itemKey);
  9654. }
  9655. }
  9656. }
  9657. }
  9658.  
  9659. function getItemLimit(itemKey)
  9660. {
  9661. // TODO: combine list of offers with min/max-boundries
  9662. var limit = itemLimits.get(itemKey);
  9663. if (limit && limit.timestamp > now() - MAX_LIMIT_AGE)
  9664. {
  9665. return Promise.resolve([limit.min, limit.max]);
  9666. }
  9667. else if (!win.jsTradalbeItems.hasOwnProperty(itemKey))
  9668. {
  9669. return Promise.resolve([0, 0]);
  9670. }
  9671. return new Promise(function (resolve, reject)
  9672. {
  9673. win.postItemDialogue(
  9674. {
  9675. value: 'sell'
  9676. }, itemKey, null);
  9677. if (!item2Resolver.has(itemKey))
  9678. {
  9679. item2Resolver.set(itemKey, []);
  9680. }
  9681. item2Resolver.get(itemKey).push(resolve);
  9682. setTimeout(function ()
  9683. {
  9684. return reject(new Error('Request timed out'));
  9685. }, 30e3);
  9686. });
  9687. }
  9688.  
  9689. function calcMarketValue(items)
  9690. {
  9691. var itemKeyList = Object.keys(items);
  9692. return Promise.all(itemKeyList.map(function (key)
  9693. {
  9694. return getItemLimit(key);
  9695. }))
  9696. .then(function (limitList)
  9697. {
  9698. var sum = [0, 0];
  9699. for (var i = 0; i < itemKeyList.length; i++)
  9700. {
  9701. var amount = items[itemKeyList[i]];
  9702. var limit = limitList[i];
  9703. sum[0] += amount * limit[0];
  9704. sum[1] += amount * limit[1];
  9705. }
  9706. return sum;
  9707. });
  9708. }
  9709. market.calcMarketValue = calcMarketValue;
  9710.  
  9711. function init()
  9712. {
  9713. showOfferCancelCooldown();
  9714. addExtraBtns();
  9715. improveOfferList();
  9716. var _chosenPostItemDialogue = win.chosenPostItemDialogue;
  9717. win.chosenPostItemDialogue = function (itemName, lowerLimit, upperLimit)
  9718. {
  9719. if (processItemLimits(itemName, Number(lowerLimit), Number(upperLimit)))
  9720. {
  9721. _chosenPostItemDialogue(itemName, lowerLimit, upperLimit);
  9722. }
  9723. };
  9724. var _addToPlayerMarket = win.addToPlayerMarket;
  9725. win.addToPlayerMarket = function (data)
  9726. {
  9727. processMarketData(data);
  9728. _addToPlayerMarket(data);
  9729. };
  9730. loadPriceHistory();
  9731. // delay (debounce) sending the request for 3s
  9732. var startDebouncedRequest = debounce(function ()
  9733. {
  9734. return loadPriceHistory();
  9735. }, 3e3);
  9736. settings.observe(settings.KEY.syncPriceHistory, function ()
  9737. {
  9738. return startDebouncedRequest();
  9739. });
  9740. settings.observeSub(settings.KEY.syncPriceHistory, 'url', function ()
  9741. {
  9742. return startDebouncedRequest();
  9743. });
  9744. }
  9745. market.init = init;
  9746. })(market || (market = {}));
  9747.  
  9748. var combat;
  9749. (function (combat)
  9750. {
  9751. combat.name = 'combat';
  9752. var LOOT_TABLE_URL = '/wiki/combat.php';
  9753. var COMBAT_LOOT_TABLES_ID = 'combat-loot-tables';
  9754. var CAT_2_NAME = {
  9755. 'always': 'Always'
  9756. , 'common': 'Common'
  9757. , 'uncommon': 'Uncommon'
  9758. , 'rare': 'Rare'
  9759. , 'veryrare': 'Very Rare'
  9760. };
  9761. var lootInfoInitialized = false;
  9762. var lootInfo = {};
  9763.  
  9764. function readLootTable(table)
  9765. {
  9766. var monsterImg = table.getElementsByTagName('img').item(0);
  9767. var src = monsterImg.getAttribute('src') || '';
  9768. var monsterId = src.replace(/.+npc\/(\d+)\.png$/, '$1');
  9769. var info = {
  9770. always: []
  9771. , common: []
  9772. , uncommon: []
  9773. , rare: []
  9774. , veryrare: []
  9775. };
  9776. for (var i = 2; i < table.rows.length; i++)
  9777. {
  9778. var row = table.rows.item(i);
  9779. var match = row.cells.item(0).innerHTML.match(/images\/(.+)\.png/) || row.cells.item(0).innerHTML.match(/images\/(.+)\.gif/);
  9780. if (!match)
  9781. {
  9782. console.error('no item key found:', row.innerHTML);
  9783. continue;
  9784. }
  9785. var itemKey = match[1];
  9786. var amount = row.cells.item(1).textContent || '';
  9787. var rarityCategory = row.cells.item(2).className;
  9788. if (!info.hasOwnProperty(rarityCategory))
  9789. {
  9790. console.error('unknown rarity category:', rarityCategory);
  9791. continue;
  9792. }
  9793. info[rarityCategory].push(
  9794. {
  9795. key: itemKey
  9796. , amount: amount.split(' - ').map(function (s)
  9797. {
  9798. return Number(s.replace(/\D/g, ''));
  9799. })
  9800. });
  9801. }
  9802. lootInfo[monsterId] = info;
  9803. lootInfoInitialized = true;
  9804. }
  9805.  
  9806. function updateLootTableInfo()
  9807. {
  9808. return doGet(LOOT_TABLE_URL)
  9809. .then(function (response)
  9810. {
  9811. var parser = new DOMParser();
  9812. var doc = parser.parseFromString(response, 'text/html');
  9813. var tables = doc.getElementsByTagName('table');
  9814. for (var i = 0; i < tables.length; i++)
  9815. {
  9816. readLootTable(tables.item(i));
  9817. }
  9818. return lootInfo;
  9819. })
  9820. .then(function (info)
  9821. {
  9822. setLootTableTabContent(info);
  9823. });
  9824. }
  9825.  
  9826. function addLootTableTab()
  9827. {
  9828. var subTabContainer = document.getElementById('tab-sub-container-combat');
  9829. var itemContainer = document.getElementById('tab-sub-container-combat-large-btns');
  9830. var afterEl = itemContainer && itemContainer.previousElementSibling;
  9831. if (!subTabContainer || !afterEl)
  9832. {
  9833. return;
  9834. }
  9835. 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");
  9836. var REFRESH_LOOT_TABLE_ID = 'refresh-loot-table';
  9837. var subTab = document.createElement('span');
  9838. subTab.className = 'large-button';
  9839. subTab.innerHTML = "<img class=\"image-icon-50\" src=\"images/combatDropTable.png\" style=\"filter: grayscale(100%);\">Loot";
  9840. subTab.addEventListener('click', function ()
  9841. {
  9842. var _confirmDialogue = win.confirmDialogue;
  9843. win.confirmDialogue = function () {};
  9844. win.clicksOpenDropTable();
  9845. win.confirmDialogue = _confirmDialogue;
  9846. win.openSubTab('loot');
  9847. });
  9848.  
  9849. function setLootTabVisibility()
  9850. {
  9851. var show = settings.get(settings.KEY.showLootTab);
  9852. subTab.style.display = show ? '' : 'none';
  9853. var dropTableItemBox = document.getElementById('item-box-combatDropTable');
  9854. if (dropTableItemBox)
  9855. {
  9856. dropTableItemBox.style.display = show ? 'none' : '';
  9857. }
  9858. if (show && !lootInfoInitialized)
  9859. {
  9860. updateLootTableInfo();
  9861. }
  9862. }
  9863. setLootTabVisibility();
  9864. settings.observe(settings.KEY.showLootTab, function ()
  9865. {
  9866. return setLootTabVisibility();
  9867. });
  9868. subTabContainer.insertBefore(subTab, afterEl);
  9869. var combatSubTab = document.getElementById('tab-sub-container-combat');
  9870. var equipSubTab = document.getElementById('tab-sub-container-equip');
  9871. var spellsSubTab = document.getElementById('tab-sub-container-spells');
  9872. var subPanelContainer = combatSubTab.parentElement;
  9873. var lootSubTab = document.createElement('div');
  9874. lootSubTab.id = 'tab-sub-container-loot';
  9875. lootSubTab.style.display = 'none';
  9876. 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>";
  9877. subPanelContainer.appendChild(lootSubTab);
  9878. var refreshBtn = document.getElementById(REFRESH_LOOT_TABLE_ID);
  9879. if (refreshBtn)
  9880. {
  9881. refreshBtn.addEventListener('click', function ()
  9882. {
  9883. if (refreshBtn.classList.contains('active'))
  9884. {
  9885. return;
  9886. }
  9887. refreshBtn.classList.add('active');
  9888. updateLootTableInfo()
  9889. .then(function ()
  9890. {
  9891. return refreshBtn.classList.remove('active');
  9892. })
  9893. .catch(function ()
  9894. {
  9895. return refreshBtn.classList.remove('active');
  9896. });
  9897. });
  9898. }
  9899. var _openSubTab = win.openSubTab;
  9900. win.openSubTab = function (tab)
  9901. {
  9902. combatSubTab.style.display = 'none';
  9903. equipSubTab.style.display = 'none';
  9904. spellsSubTab.style.display = 'none';
  9905. lootSubTab.style.display = 'none';
  9906. _openSubTab(tab);
  9907. if (tab == 'loot')
  9908. {
  9909. lootSubTab.style.display = 'block';
  9910. }
  9911. };
  9912. var _loadDefaultCombatTab = win.loadDefaultCombatTab;
  9913. win.loadDefaultCombatTab = function ()
  9914. {
  9915. _loadDefaultCombatTab();
  9916. lootSubTab.style.display = 'none';
  9917. };
  9918. }
  9919.  
  9920. function setLootTableTabContent(lootInfo)
  9921. {
  9922. var combatTableWrapper = document.getElementById(COMBAT_LOOT_TABLES_ID);
  9923. if (!combatTableWrapper)
  9924. {
  9925. return;
  9926. }
  9927. combatTableWrapper.innerHTML = "";
  9928. for (var monsterId in lootInfo)
  9929. {
  9930. var info = lootInfo[monsterId];
  9931. var monsterNum = Number(monsterId);
  9932. if (monsterNum > 1 && monsterNum % 3 === 1)
  9933. {
  9934. var lineBreak = document.createElement('div');
  9935. lineBreak.style.clear = 'both';
  9936. lineBreak.innerHTML = "<br>";
  9937. combatTableWrapper.appendChild(lineBreak);
  9938. }
  9939. var table = document.createElement('table');
  9940. table.className = 'hiscores-table';
  9941. var imgRow = table.insertRow(-1);
  9942. 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>";
  9943. var headerRow = table.insertRow(-1);
  9944. headerRow.innerHTML = "<th>Item</th><th>Amount</th><th>Rarity</th>";
  9945. for (var rarityCategory in info)
  9946. {
  9947. var itemList = info[rarityCategory];
  9948. for (var i = 0; i < itemList.length; i++)
  9949. {
  9950. var item = itemList[i];
  9951. var row = table.insertRow(-1);
  9952. row.innerHTML = "<td><img src=\"../images/" + item.key + ".png\" class=\"image-icon-40\"></td><td>" + item.amount.map(function (n)
  9953. {
  9954. return format.number(n);
  9955. }).join(' - ') + "</td><td class=\"" + rarityCategory + "\">" + CAT_2_NAME[rarityCategory] + "</td>";
  9956. }
  9957. }
  9958. combatTableWrapper.appendChild(table);
  9959. }
  9960. }
  9961.  
  9962. function init()
  9963. {
  9964. addLootTableTab();
  9965. if (settings.get(settings.KEY.showLootTab))
  9966. {
  9967. updateLootTableInfo();
  9968. }
  9969. }
  9970. combat.init = init;
  9971. })(combat || (combat = {}));
  9972.  
  9973. /**
  9974. * farming improvements
  9975. */
  9976. var farming;
  9977. (function (farming)
  9978. {
  9979. farming.name = 'farming';
  9980. var SEED_INFO_REGEX = {
  9981. minLevel: />\s*Level:/
  9982. , stopsDyingLevel: />\s*Stops\s+Dying\s+Level:/
  9983. , bonemeal: />\s*Bonemeal:/
  9984. , woodcuttingLevel: />\s*Woodcutting\s+Level:/
  9985. };
  9986. var seedInfoSpans = {};
  9987. var seedInfo = {};
  9988. var checkInfo = {
  9989. bonemeal: function (amount)
  9990. {
  9991. return amount <= win.bonemeal;
  9992. }
  9993. , minLevel: function (level)
  9994. {
  9995. return level <= win.getLevel(win.farmingXp);
  9996. }
  9997. , stopsDyingLevel: function (level)
  9998. {
  9999. return level <= win.getLevel(win.farmingXp);
  10000. }
  10001. , woodcuttingLevel: function (level)
  10002. {
  10003. return level <= win.getLevel(win.woodcuttingXp);
  10004. }
  10005. };
  10006. var RED = 'rgb(204, 0, 0)';
  10007.  
  10008. function addBetterStyle()
  10009. {
  10010. var CLASS_NAME = 'seedHighlight';
  10011. 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");
  10012. // seedHighlight
  10013. function updateHoverStyle()
  10014. {
  10015. document.body.classList[settings.get(settings.KEY.highlightUnplantableSeed) ? 'add' : 'remove'](CLASS_NAME);
  10016. }
  10017. updateHoverStyle();
  10018. settings.observe(settings.KEY.highlightUnplantableSeed, function ()
  10019. {
  10020. return updateHoverStyle();
  10021. });
  10022. }
  10023.  
  10024. function readSeedInfo(seedName, tooltipEl)
  10025. {
  10026. var spans = tooltipEl.querySelectorAll(':scope > span');
  10027. var infoSpans = {
  10028. bonemeal: null
  10029. , minLevel: null
  10030. , stopsDyingLevel: null
  10031. , woodcuttingLevel: null
  10032. };
  10033. var info = {
  10034. bonemeal: 0
  10035. , minLevel: 0
  10036. , stopsDyingLevel: 0
  10037. , woodcuttingLevel: 0
  10038. };
  10039. var i = 0;
  10040. for (var key in SEED_INFO_REGEX)
  10041. {
  10042. if (SEED_INFO_REGEX[key].test(spans[i].innerHTML))
  10043. {
  10044. infoSpans[key] = spans.item(i);
  10045. var textNode = spans.item(i).lastChild;
  10046. info[key] = parseInt(textNode.textContent || '', 10);
  10047. i++;
  10048. }
  10049. }
  10050. seedInfoSpans[seedName] = infoSpans;
  10051. seedInfo[seedName] = info;
  10052. }
  10053.  
  10054. function checkSpan(span, fulfilled)
  10055. {
  10056. span.style.color = fulfilled ? '' : RED;
  10057. span.style.fontWeight = fulfilled ? '' : 'bold';
  10058. }
  10059.  
  10060. function checkSeedInfo(seedName, init)
  10061. {
  10062. if (init === void 0)
  10063. {
  10064. init = false;
  10065. }
  10066. var highlight = settings.get(settings.KEY.highlightUnplantableSeed);
  10067. var info = seedInfo[seedName];
  10068. var spans = seedInfoSpans[seedName];
  10069. var canBePlanted = true;
  10070. for (var key in info)
  10071. {
  10072. var span = spans[key];
  10073. if (span)
  10074. {
  10075. var fulfilled = checkInfo[key](info[key]);
  10076. checkSpan(span, !highlight || fulfilled);
  10077. canBePlanted = !highlight || canBePlanted && (key == 'stopsDyingLevel' || fulfilled);
  10078. }
  10079. }
  10080. var itemBox = document.getElementById('item-box-' + seedName);
  10081. if (itemBox)
  10082. {
  10083. itemBox.style.opacity = (!highlight || canBePlanted) ? '' : '.5';
  10084. }
  10085. var plantInput = document.getElementById('dialogue-plant-' + seedName);
  10086. if (plantInput)
  10087. {
  10088. plantInput.style.backgroundColor = (!highlight || canBePlanted) ? '' : 'hsla(0, 100%, 50%, .5)';
  10089. }
  10090. if (init)
  10091. {
  10092. observer.add('bonemeal', function ()
  10093. {
  10094. return checkSeedInfo(seedName);
  10095. });
  10096. observer.add('farmingXp', function ()
  10097. {
  10098. return checkSeedInfo(seedName);
  10099. });
  10100. observer.add('woodcuttingXp', function ()
  10101. {
  10102. return checkSeedInfo(seedName);
  10103. });
  10104. settings.observe(settings.KEY.highlightUnplantableSeed, function ()
  10105. {
  10106. return checkSeedInfo(seedName);
  10107. });
  10108. }
  10109. }
  10110.  
  10111. function getSeedInfo(seedName)
  10112. {
  10113. return seedInfo[seedName];
  10114. }
  10115. farming.getSeedInfo = getSeedInfo;
  10116.  
  10117. function init()
  10118. {
  10119. addBetterStyle();
  10120. // read all seed information
  10121. var tooltipEls = document.querySelectorAll('div[id^="tooltip-"][id$="Seeds"]');
  10122. for (var i = 0; i < tooltipEls.length; i++)
  10123. {
  10124. var tooltipEl = tooltipEls[i];
  10125. var seedName = tooltipEl.id.replace(/^tooltip-/, '');
  10126. readSeedInfo(seedName, tooltipEl);
  10127. checkSeedInfo(seedName, true);
  10128. }
  10129. }
  10130. farming.init = init;
  10131. })(farming || (farming = {}));
  10132.  
  10133. /**
  10134. * general features which doesn't really belong anywhere
  10135. */
  10136. var general;
  10137. (function (general)
  10138. {
  10139. general.name = 'general';
  10140. // disable the drink button for 3 seconds
  10141. var DRINK_DELAY = 3;
  10142.  
  10143. function getSentBoat()
  10144. {
  10145. for (var i = 0; i < BOAT_LIST.length; i++)
  10146. {
  10147. if (getGameValue(BOAT_LIST[i] + 'Timer') > 0)
  10148. {
  10149. return BOAT_LIST[i];
  10150. }
  10151. }
  10152. return null;
  10153. }
  10154.  
  10155. function checkBoat(boat)
  10156. {
  10157. var boatDialog = null;
  10158. var sendBtn = null;
  10159. var initiatedDialogs = document.querySelectorAll('div[role="dialog"]');
  10160. for (var i = 0; i < initiatedDialogs.length; i++)
  10161. {
  10162. var dialog = initiatedDialogs[i];
  10163. if (dialog.style.display !== 'none')
  10164. {
  10165. var btn = dialog.querySelector('input[type="button"][value="Send Boat"]');
  10166. if (btn)
  10167. {
  10168. sendBtn = btn;
  10169. boatDialog = dialog;
  10170. break;
  10171. }
  10172. }
  10173. }
  10174. if (!boatDialog || !sendBtn)
  10175. {
  10176. return;
  10177. }
  10178. var smallboxes = boatDialog.querySelectorAll('div.basic-smallbox');
  10179. var baitBox = smallboxes[0];
  10180. var runningBox = smallboxes[1];
  10181. if (smallboxes.length === 1)
  10182. {
  10183. runningBox = document.createElement('div');
  10184. runningBox.className = 'basic-smallbox';
  10185. runningBox.style.display = 'none';
  10186. var parent_1 = baitBox.parentElement;
  10187. var next = baitBox.nextElementSibling;
  10188. if (parent_1)
  10189. {
  10190. if (next)
  10191. {
  10192. parent_1.insertBefore(runningBox, next);
  10193. }
  10194. else
  10195. {
  10196. parent_1.appendChild(runningBox);
  10197. }
  10198. }
  10199. }
  10200. var sentBoat = getSentBoat();
  10201. baitBox.style.display = sentBoat !== null && !boundBoatingDock ? 'none' : '';
  10202. runningBox.style.display = sentBoat !== null && !boundBoatingDock ? '' : 'none';
  10203. // just in case Smitty changes this game mechanic somehow, don't disable the button:
  10204. // sendBtn.disabled = sentBoat !== null;
  10205. if(!boundBoatingDock)
  10206. sendBtn.style.color = sentBoat !== null ? 'gray' : '';
  10207. win.$(boatDialog).on('dialogclose', function ()
  10208. {
  10209. if (sendBtn)
  10210. {
  10211. sendBtn.style.color = '';
  10212. }
  10213. });
  10214. if (sentBoat === boat)
  10215. {
  10216. runningBox.innerHTML = "<b>Returning in:</b> <span data-item-display=\"" + boat + "Timer\">" + format.timer(getGameValue(boat + 'Timer')) + "</span>";
  10217. }
  10218. else if (sentBoat !== null && !boundBoatingDock)
  10219. {
  10220. runningBox.innerHTML = "Wait for the other boat to return.";
  10221. }
  10222. else
  10223. {
  10224. var enoughBaitAndCoal = win.fishingBait >= win.fishingBaitCost(boat)
  10225. && (boat !== 'steamBoat' || win.charcoal >= 300);
  10226. baitBox.style.color = enoughBaitAndCoal ? '' : 'red';
  10227. }
  10228. }
  10229.  
  10230. function initBoatDialog()
  10231. {
  10232. var _clicksBoat = win.clicksBoat;
  10233. win.clicksBoat = function (boat)
  10234. {
  10235. _clicksBoat(boat);
  10236. checkBoat(boat);
  10237. };
  10238. var _doCommand = win.doCommand;
  10239. win.doCommand = function (data)
  10240. {
  10241. _doCommand(data);
  10242. if (data.startsWith('RUN_FUNC=SAIL_BOAT_WIND'))
  10243. {
  10244. checkBoat('sailBoat');
  10245. }
  10246. };
  10247. }
  10248. var potionDrinkEnable = null;
  10249. var POTION_ACTIVE_HTML = "<br>It's already active.";
  10250. function updateDialogEls(timerKey, dialog, close)
  10251. {
  10252. if (close === void 0)
  10253. {
  10254. close = false;
  10255. }
  10256. var timer = getGameValue(timerKey);
  10257. var showActive = settings.get(settings.KEY.usePotionWarning) && timer > 0 && !close;
  10258. var confirmText = document.getElementById('dialogue-confirm-text');
  10259. var br = confirmText && confirmText.nextElementSibling;
  10260. if (confirmText && br)
  10261. {
  10262. if (showActive)
  10263. {
  10264. confirmText.innerHTML += POTION_ACTIVE_HTML;
  10265. }
  10266. else
  10267. {
  10268. confirmText.innerHTML = confirmText.innerHTML.replace(POTION_ACTIVE_HTML, '');
  10269. }
  10270. br.style.display = showActive ? 'none' : '';
  10271. }
  10272. var confirmBtn = document.getElementById('dialogue-confirm-yes');
  10273. if (confirmBtn && showActive)
  10274. {
  10275. confirmBtn.disabled = true;
  10276. var i_1 = DRINK_DELAY;
  10277. var updateValue_1 = function ()
  10278. {
  10279. confirmBtn.value = 'Drink' + (i_1 > 0 ? ' (' + i_1 + ')' : '');
  10280. if (i_1 === 0)
  10281. {
  10282. potionDrinkEnable && potionDrinkEnable();
  10283. }
  10284. else
  10285. {
  10286. i_1--;
  10287. }
  10288. };
  10289. var countDownInterval_1;
  10290. var dialogClose_1 = function ()
  10291. {
  10292. return potionDrinkEnable && potionDrinkEnable();
  10293. };
  10294. potionDrinkEnable = function ()
  10295. {
  10296. potionDrinkEnable = null;
  10297. win.$(dialog).off('dialogclose', dialogClose_1);
  10298. countDownInterval_1 && clearInterval(countDownInterval_1);
  10299. confirmBtn.disabled = false;
  10300. confirmBtn.value = 'Drink';
  10301. };
  10302. updateValue_1();
  10303. countDownInterval_1 = setInterval(function ()
  10304. {
  10305. return updateValue_1();
  10306. }, 1e3);
  10307. win.$(dialog).on('dialogclose', dialogClose_1);
  10308. }
  10309. else if (!showActive)
  10310. {
  10311. potionDrinkEnable && potionDrinkEnable();
  10312. }
  10313. }
  10314.  
  10315. function checkPotionActive(potion)
  10316. {
  10317. var dialog = document.getElementById('dialogue-confirm');
  10318. var parent = dialog && dialog.parentElement;
  10319. if (!dialog || !parent || parent.style.display === 'none')
  10320. {
  10321. return;
  10322. }
  10323. var timerKey = potion + 'Timer';
  10324. updateDialogEls(timerKey, dialog);
  10325. var fn = observer.add(timerKey, function (key, oldValue, newValue)
  10326. {
  10327. if (oldValue < newValue && oldValue === 0
  10328. || oldValue > newValue && newValue === 0)
  10329. {
  10330. updateDialogEls(timerKey, dialog);
  10331. }
  10332. });
  10333. win.$(dialog).on('dialogclose', function ()
  10334. {
  10335. updateDialogEls(timerKey, dialog, true);
  10336. observer.remove(timerKey, fn);
  10337. });
  10338. }
  10339.  
  10340. function initPotionDialog()
  10341. {
  10342. var _confirmDialogue = win.confirmDialogue;
  10343. win.confirmDialogue = function (width, text, btn1Text, btn2Text, cmd)
  10344. {
  10345. potionDrinkEnable && potionDrinkEnable();
  10346. _confirmDialogue(width, text, btn1Text, btn2Text, cmd);
  10347. };
  10348. var _clicksPotion = win.clicksPotion;
  10349. win.clicksPotion = function (potion)
  10350. {
  10351. _clicksPotion(potion);
  10352. checkPotionActive(potion);
  10353. };
  10354. }
  10355.  
  10356. function init()
  10357. {
  10358. initBoatDialog();
  10359. initPotionDialog();
  10360. }
  10361. general.init = init;
  10362. })(general || (general = {}));
  10363.  
  10364. /**
  10365. * init
  10366. */
  10367. var scriptInitialized = false;
  10368.  
  10369. function init()
  10370. {
  10371. console.info('[%s] "DH2 Fixed %s" up and running.', (new Date).toLocaleTimeString(), version);
  10372. scriptInitialized = true;
  10373. var initModules = [
  10374. settings
  10375. , notifications
  10376. , log
  10377. , gameEvents
  10378. , temporaryFixes
  10379. , crafting
  10380. , itemBoxes
  10381. , chat
  10382. , timer
  10383. , smelting
  10384. , fishingInfo
  10385. //, recipeTooltips
  10386. //, fixNumbers
  10387. , machineDialog
  10388. , amountInputs
  10389. , newTopbar
  10390. , styleTweaks
  10391. , notifBoxes
  10392. , market
  10393. , combat
  10394. , farming
  10395. , general
  10396. ];
  10397. for (var _i = 0, initModules_1 = initModules; _i < initModules_1.length; _i++)
  10398. {
  10399. var module = initModules_1[_i];
  10400. try
  10401. {
  10402. module.init();
  10403. }
  10404. catch (error)
  10405. {
  10406. console.error('Error during initialization in module "' + module.name + '":', error);
  10407. }
  10408. }
  10409. }
  10410. document.addEventListener('DOMContentLoaded', function ()
  10411. {
  10412. var oldValues = new Map();
  10413. var _doCommand = win.doCommand;
  10414. win.doCommand = function (data)
  10415. {
  10416. if (data.startsWith('REFRESH_ITEMS='))
  10417. {
  10418. oldValues = new Map();
  10419. for (var _i = 0, _a = win.jsItemArray; _i < _a.length; _i++)
  10420. {
  10421. var key = _a[_i];
  10422. oldValues.set(key, getGameValue(key));
  10423. }
  10424. _doCommand(data);
  10425. if (!scriptInitialized)
  10426. {
  10427. init();
  10428. }
  10429. return;
  10430. }
  10431. else if (!scriptInitialized)
  10432. {
  10433. if (data.startsWith('CHAT='))
  10434. {
  10435. var parts = data.substr(5).split('~');
  10436. return chat.newAddToChatBox(parts[0], parts[1], parts[2], parts[3], 0);
  10437. }
  10438. else if (data.startsWith('PM='))
  10439. {
  10440. return chat.newAddToChatBox(win.username, '0', '0', data.substr(3), 1);
  10441. }
  10442. }
  10443. var ret = commands.process(data);
  10444. if (ret === void 0)
  10445. {
  10446. ret = _doCommand(commands.formatData(data));
  10447. }
  10448. return ret;
  10449. };
  10450. var _refreshItemValues = win.refreshItemValues;
  10451. win.refreshItemValues = function (itemKeyList, firstLoad)
  10452. {
  10453. _refreshItemValues(itemKeyList, firstLoad);
  10454. for (var _i = 0, itemKeyList_2 = itemKeyList; _i < itemKeyList_2.length; _i++)
  10455. {
  10456. var key = itemKeyList_2[_i];
  10457. observer.notify(key, oldValues.get(key));
  10458. }
  10459. observer.notifyTick();
  10460. };
  10461. });
  10462.  
  10463. /**
  10464. * fix web socket errors
  10465. */
  10466. var main;
  10467. (function (main)
  10468. {
  10469. var WS_TIMEOUT_SEC = 30;
  10470. var WS_TIMEOUT_CODE = 3000;
  10471. var WS_OPEN_TIMEOUT_SEC = 2 * 60; // 2 minutes
  10472. // reload the page after 5 consecutive reconnect attempts without successfully opening the websocket once
  10473. var MAX_RECONNECTS = 5;
  10474.  
  10475. function webSocketLoaded(event)
  10476. {
  10477. if (win.webSocket == null)
  10478. {
  10479. console.error('WebSocket instance not initialized!');
  10480. return;
  10481. }
  10482. // cache old event listener
  10483. var _onClose = win.webSocket.onclose;
  10484. var _onError = win.webSocket.onerror;
  10485. var _onMessage = win.webSocket.onmessage;
  10486. var _onOpen = win.webSocket.onopen;
  10487. var commandQueue = [];
  10488. var _cBytes = win.cBytes;
  10489. win.cBytes = function (command)
  10490. {
  10491. if (win.webSocket && win.webSocket.readyState === WebSocket.OPEN)
  10492. {
  10493. _cBytes(command);
  10494. }
  10495. else
  10496. {
  10497. commandQueue.push(command);
  10498. }
  10499. };
  10500. var pageLoaded = false;
  10501. var wsTimeout = null;
  10502. var reconnectAttempts = 0;
  10503.  
  10504. function onTimeout()
  10505. {
  10506. wsTimeout = null;
  10507. // renew the websocket
  10508. if (reconnectAttempts <= MAX_RECONNECTS)
  10509. {
  10510. win.webSocket = new WebSocket(win.SSL_ENABLED);
  10511. win.ignoreBytesTracker = Date.now();
  10512. initWSListener(win.webSocket);
  10513. reconnectAttempts++;
  10514. }
  10515. if (win.webSocket)
  10516. {
  10517. win.webSocket.close(WS_TIMEOUT_CODE, 'Connection timed out after ' + WS_TIMEOUT_SEC + ' seconds');
  10518. }
  10519. }
  10520.  
  10521. function updateWSTimeout()
  10522. {
  10523. if (wsTimeout)
  10524. {
  10525. win.clearTimeout(wsTimeout);
  10526. }
  10527. wsTimeout = win.setTimeout(onTimeout, WS_TIMEOUT_SEC * 1e3);
  10528. }
  10529. var messageQueue = [];
  10530.  
  10531. function onMessage(event)
  10532. {
  10533. if (pageLoaded)
  10534. {
  10535. updateWSTimeout();
  10536. return _onMessage.call(this, event);
  10537. }
  10538. else
  10539. {
  10540. messageQueue.push(event);
  10541. }
  10542. };
  10543. var wsOpenTimeout = null;
  10544.  
  10545. function onOpenTimeout()
  10546. {
  10547. wsOpenTimeout = null;
  10548. location.reload();
  10549. }
  10550.  
  10551. function onOpen(event)
  10552. {
  10553. reconnectAttempts = 0;
  10554. if (wsOpenTimeout)
  10555. {
  10556. win.clearTimeout(wsOpenTimeout);
  10557. wsOpenTimeout = null;
  10558. }
  10559. // do the handshake first
  10560. _onOpen.call(this, event);
  10561. commandQueue.forEach(function (command)
  10562. {
  10563. return win.cBytes(command);
  10564. });
  10565. }
  10566.  
  10567. function onError(event)
  10568. {
  10569. console.error('error in websocket:', event);
  10570. return _onError.call(this, event);
  10571. }
  10572.  
  10573. function onClose(event)
  10574. {
  10575. console.info('websocket closed:', event);
  10576. if (event.code !== WS_TIMEOUT_CODE || reconnectAttempts > MAX_RECONNECTS)
  10577. {
  10578. location.reload();
  10579. }
  10580. return _onClose.call(this, event);
  10581. }
  10582.  
  10583. function initWSListener(ws)
  10584. {
  10585. if (ws.readyState === WebSocket.CONNECTING)
  10586. {
  10587. wsOpenTimeout = win.setTimeout(onOpenTimeout, WS_OPEN_TIMEOUT_SEC * 1e3);
  10588. }
  10589. ws.onclose = onClose;
  10590. ws.onerror = onError;
  10591. ws.onmessage = onMessage;
  10592. ws.onopen = onOpen;
  10593. }
  10594. initWSListener(win.webSocket);
  10595. document.addEventListener('DOMContentLoaded', function ()
  10596. {
  10597. pageLoaded = true;
  10598. messageQueue.forEach(function (event)
  10599. {
  10600. return win.webSocket.onmessage(event);
  10601. });
  10602. });
  10603. }
  10604.  
  10605. function isScriptElement(el)
  10606. {
  10607. return el.nodeName === 'SCRIPT';
  10608. }
  10609.  
  10610. function isWebSocketScript(script)
  10611. {
  10612. return script.src.includes('socket.js');
  10613. }
  10614. var found = false;
  10615. if (document.head)
  10616. {
  10617. var scripts = document.head.querySelectorAll('script');
  10618. for (var i = 0; i < scripts.length; i++)
  10619. {
  10620. if (isWebSocketScript(scripts[i]))
  10621. {
  10622. // does this work?
  10623. scripts[i].onload = webSocketLoaded;
  10624. found = true;
  10625. }
  10626. }
  10627. }
  10628. if (!found)
  10629. {
  10630. // create an observer instance
  10631. var mutationObserver_1 = new MutationObserver(function (mutationList)
  10632. {
  10633. mutationList.forEach(function (mutation)
  10634. {
  10635. if (mutation.addedNodes.length === 0)
  10636. {
  10637. return;
  10638. }
  10639. for (var i = 0; i < mutation.addedNodes.length; i++)
  10640. {
  10641. var node = mutation.addedNodes[i];
  10642. if (isScriptElement(node) && isWebSocketScript(node))
  10643. {
  10644. mutationObserver_1.disconnect();
  10645. node.onload = webSocketLoaded;
  10646. return;
  10647. }
  10648. }
  10649. });
  10650. });
  10651. mutationObserver_1.observe(document.head
  10652. , {
  10653. childList: true
  10654. });
  10655. }
  10656. // fix scrollText (e.g. when joining the game and receiving xp at that moment)
  10657. win.mouseX = win.innerWidth / 2;
  10658. win.mouseY = win.innerHeight / 2;
  10659. var _confirm = win.confirm;
  10660. win.confirm = function (message)
  10661. {
  10662. // don't show the annoying update confirm box (instead of a confirm box, an ingame dialog could be used...)
  10663. if (message && message.indexOf('Ted\'s Market Script') !== -1)
  10664. {
  10665. return false;
  10666. }
  10667. return _confirm(message);
  10668. };
  10669. })(main || (main = {}));
  10670.  
  10671. })();