DH2 Fixed

Improve Diamond Hunt 2

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