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