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