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