DH2 Fixed

Improve Diamond Hunt 2

目前為 2017-03-19 提交的版本,檢視 最新版本

// ==UserScript==
// @name         DH2 Fixed
// @namespace    FileFace
// @description  Improve Diamond Hunt 2
// @version      0.80.3
// @author       Zorbing
// @license      ISC; http://opensource.org/licenses/ISC
// @grant        none
// @run-at       document-start
// @include      http://www.diamondhunt.co/game.php
// ==/UserScript==

/**
 * ISC License (ISC)
 * 
 * Copyright (c) 2017, Martin Boekhoff
 * 
 * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
 * granted, provided that the above copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 * 
 * Source: http://opensource.org/licenses/ISC
 */

(function ()
{
'use strict';
/**
 * observer
 */
var observer;
(function (observer) {
	var observedKeys = new Map();
	function add(key, fn) {
		if (key instanceof Array) {
			for (var _i = 0, key_1 = key; _i < key_1.length; _i++) {
				var k = key_1[_i];
				add(k, fn);
			}
		}
		else {
			if (!observedKeys.has(key)) {
				observedKeys.set(key, new Set());
			}
			observedKeys.get(key).add(fn);
		}
		return fn;
	}
	observer.add = add;
	function notify(key, oldValue) {
		var newValue = window[key];
		// convert the value to number if possible
		if (!isNaN(newValue)) {
			newValue = Number(newValue);
		}
		window[key] = newValue;
		if (observedKeys.has(key)) {
			observedKeys.get(key).forEach(function (fn) { return fn(key, oldValue, newValue); });
		}
	}
	observer.notify = notify;
	function remove(key, fn) {
		if (key instanceof Array) {
			var ret = [];
			for (var _i = 0, key_2 = key; _i < key_2.length; _i++) {
				var k = key_2[_i];
				ret.push(remove(k, fn));
			}
			return ret;
		}
		if (!observedKeys.has(key)) {
			return false;
		}
		return observedKeys.get(key).delete(fn);
	}
	observer.remove = remove;
})(observer || (observer = {}));
/**
 * global constants
 */
var TIER_LEVELS = ['empty', 'sapphire', 'emerald', 'ruby', 'diamond'];
var TIER_NAMES = ['Standard', 'Sapphire', 'Emerald', 'Ruby', 'Diamond'];
var TIER_ITEMS = ['pickaxe', 'shovel', 'hammer', 'axe', 'rake', 'fishingRod'];
var FURNACE_LEVELS = ['stone', 'bronze', 'iron', 'silver', 'gold'];
var FURNACE_CAPACITIES = [10, 30, 75, 150, 300];
var OVEN_LEVELS = ['bronze', 'iron', 'silver', 'gold'];
var OIL_STORAGE_SIZES = [10e3, 50e3, 100e3, 300e3, 600e3];
var TREE_INFO = {
	1: {
		name: 'Normal tree',
		// 3h = 10800s
		growTime: 3 * 60 * 60
	},
	2: {
		name: 'Oak tree',
		// 6h = 21600s
		growTime: 6 * 60 * 60
	},
	3: {
		name: 'Willow tree',
		// 8h = 28800s
		growTime: 8 * 60 * 60
	},
	4: {
		name: 'Maple tree',
		// 12h = 43200s
		growTime: 12 * 60 * 60
	}
};
var HIDE_RECIPES = {
	'drills': {
		max: 10
	},
	'crushers': {
		max: 10
	},
	'oilPipe': {
		max: 1
	},
	'pumpjacks': {
		max: 10
	},
	'rowBoat': {
		max: 1
	},
	'canoe': {
		max: 1
	},
	// thanks aguyd
	'bonemealBin': {
		extraKeys: ['boundFilledBonemealBin'],
		max: 1
	}
};
var SMELTING_REQUIREMENTS = {
	'glass': {
		sand: 1,
		oil: 10
	},
	'bronzeBar': {
		copper: 1,
		tin: 1,
		oil: 10
	},
	'ironBar': {
		iron: 1,
		oil: 100
	},
	'silverBar': {
		silver: 1,
		oil: 300
	},
	'goldBar': {
		gold: 1,
		oil: 1e3
	}
};
var format;
(function (format) {
	var TIME_STEPS = [
		{
			threshold: 1,
			name: 'second',
			short: 'sec',
			padp: 0
		},
		{
			threshold: 60,
			name: 'minute',
			short: 'min',
			padp: 0
		},
		{
			threshold: 3600,
			name: 'hour',
			short: 'h',
			padp: 1
		},
		{
			threshold: 86400,
			name: 'day',
			short: 'd',
			padp: 2
		}
	];
	function zeroPadLeft(num) {
		return (num < 10 ? '0' : '') + num;
	}
	function number(num) {
		return (typeof num === 'number' ? num : Number(num)).toLocaleString('en');
	}
	format.number = number;
	function numbersInText(text) {
		return text.replace(/\d(?:[\d',\.]*\d)?/g, function (numStr) {
			return number(Number(numStr.replace(/\D/g, '')));
		});
	}
	format.numbersInText = numbersInText;
	// use time format established in DHQoL (https://greasyfork.org/scripts/16041-dhqol)
	function timer(timer) {
		if (typeof timer === 'string') {
			timer = parseInt(timer, 10);
		}
		timer = Math.max(timer, 0);
		var hours = Math.floor(timer / 3600);
		var minutes = Math.floor((timer % 3600) / 60);
		var seconds = timer % 60;
		return zeroPadLeft(hours) + ':' + zeroPadLeft(minutes) + ':' + zeroPadLeft(seconds);
	}
	format.timer = timer;
	function time2NearestUnit(time, long) {
		if (long === void 0) { long = false; }
		var step = TIME_STEPS[0];
		for (var i = TIME_STEPS.length - 1; i > 0; i--) {
			if (time >= TIME_STEPS[i].threshold) {
				step = TIME_STEPS[i];
				break;
			}
		}
		var factor = Math.pow(10, step.padp);
		var num = Math.round(time / step.threshold * factor) / factor;
		var unit = long ? step.name + (num === 1 ? '' : 's') : step.short;
		return num + ' ' + unit;
	}
	format.time2NearestUnit = time2NearestUnit;
})(format || (format = {}));
/**
 * general functions
 */
var styleElement = null;
function addStyle(styleCode) {
	if (styleElement === null) {
		styleElement = document.createElement('style');
		document.head.appendChild(styleElement);
	}
	styleElement.innerHTML += styleCode;
}
function getBoundKey(key) {
	return 'bound' + key[0].toUpperCase() + key.substr(1);
}
function getTierKey(key, tierLevel) {
	return TIER_LEVELS[tierLevel] + key[0].toUpperCase() + key.substr(1);
}
function now() {
	return (new Date()).getTime();
}
function ensureTooltip(id, target) {
	var tooltipId = 'tooltip-' + id;
	var tooltipEl = document.getElementById(tooltipId);
	if (!tooltipEl) {
		tooltipEl = document.createElement('div');
		tooltipEl.id = tooltipId;
		tooltipEl.style.display = 'none';
		var tooltipList = document.getElementById('tooltip-list');
		tooltipList.appendChild(tooltipEl);
	}
	// ensure binded events to show the tooltip
	if (target.dataset.tooltipId == null) {
		target.dataset.tooltipId = tooltipId;
		window.$(target).bind({
			mousemove: window.changeTooltipPosition,
			mouseenter: window.showTooltip,
			mouseleave: window.hideTooltip
		});
	}
	return tooltipEl;
}
/**
 * persistence store
 */
var store;
(function (store) {
	var storePrefix = 'dh2-';
	function get(key) {
		var value = localStorage.getItem(storePrefix + key);
		if (value != null) {
			try {
				return JSON.parse(value);
			}
			catch (e) { }
		}
		return value;
	}
	store.get = get;
	function has(key) {
		return localStorage.hasOwnProperty(storePrefix + key);
	}
	store.has = has;
	function remove(key) {
		localStorage.removeItem(storePrefix + key);
	}
	store.remove = remove;
	function set(key, value) {
		localStorage.setItem(storePrefix + key, JSON.stringify(value));
	}
	store.set = set;
})(store || (store = {}));
var settings;
(function (settings) {
	var KEY;
	(function (KEY) {
		KEY[KEY["hideCraftingRecipes"] = 0] = "hideCraftingRecipes";
		KEY[KEY["useNewChat"] = 1] = "useNewChat";
		KEY[KEY["colorizeChat"] = 2] = "colorizeChat";
	})(KEY = settings.KEY || (settings.KEY = {}));
	;
	var CFG = (_a = {},
		_a[KEY.hideCraftingRecipes] = {
			name: 'Hide crafting recipes of finished items',
			title: "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>",
			defaultValue: true
		},
		_a[KEY.useNewChat] = {
			name: 'Use the new chat',
			title: "Enables using the completely new chat with pm tabs, clickable links, clickable usernames to send a pm, intelligent scrolling and suggesting commands while typing",
			defaultValue: true,
			requiresReload: true
		},
		_a[KEY.colorizeChat] = {
			name: 'Colorize chat messages',
			title: "Colorize chat messages according to a unique color for each user",
			defaultValue: false
		},
		_a);
	var SETTINGS_TABLE_ID = 'd2h-settings';
	var SETTING_ID_PREFIX = 'dh2-setting-';
	/**
	 * settings
	 */
	function getStoreKey(key) {
		return 'setting.' + name(key);
	}
	function name(key) {
		return typeof key === 'string' ? key : KEY[key];
	}
	var observedSettings = new Map();
	function observe(key, fn) {
		var n = name(key);
		if (!observedSettings.has(n)) {
			observedSettings.set(n, new Set());
		}
		observedSettings.get(n).add(fn);
	}
	settings.observe = observe;
	function unobserve(key, fn) {
		var n = name(key);
		if (!observedSettings.has(n)) {
			return false;
		}
		return observedSettings.get(n).delete(fn);
	}
	settings.unobserve = unobserve;
	function get(key) {
		if (!CFG.hasOwnProperty(key)) {
			return false;
		}
		var name = getStoreKey(key);
		return store.has(name) ? store.get(name) : CFG[key].defaultValue;
	}
	settings.get = get;
	function set(key, newValue) {
		if (!CFG.hasOwnProperty(key)) {
			return;
		}
		var oldValue = get(key);
		var n = name(key);
		store.set(getStoreKey(key), newValue);
		if (oldValue !== newValue && observedSettings.has(n)) {
			observedSettings.get(n).forEach(function (fn) { return fn(key, oldValue, newValue); });
		}
	}
	settings.set = set;
	function init() {
		addStyle("\n\ttable.table-style1 tr:not([onclick])\n\t{\n\t\tcursor: initial;\n\t}\n\t#tab-container-profile h2.section-title\n\t{\n\t\tcolor: orange;\n\t\tline-height: 1.2rem;\n\t\tmargin-top: 2rem;\n\t}\n\t#tab-container-profile h2.section-title > span.note\n\t{\n\t\tfont-size: 0.9rem;\n\t}\n\t#" + SETTINGS_TABLE_ID + " tr.reload td:first-child::after\n\t{\n\t\tcontent: '*';\n\t\tfont-weight: bold;\n\t\tmargin-left: 3px;\n\t}\n\t\t");
		function insertAfter(newChild, oldChild) {
			var parent = oldChild.parentElement;
			if (oldChild.nextElementSibling == null) {
				parent.appendChild(newChild);
			}
			else {
				parent.insertBefore(newChild, oldChild.nextElementSibling);
			}
		}
		function getCheckImageSrc(value) {
			return 'images/icons/' + (value ? 'check' : 'x') + '.png';
		}
		var profileTable = document.getElementById('profile-toggleTable');
		if (!profileTable) {
			return;
		}
		var settingsHeader = document.createElement('h2');
		settingsHeader.className = 'section-title';
		settingsHeader.innerHTML = "Userscript \"DH2 Fixed\"<br>\n\t\t\t<span class=\"note\">(* changes require reloading the tab)</span>";
		insertAfter(settingsHeader, profileTable);
		var settingsTable = document.createElement('table');
		settingsTable.id = SETTINGS_TABLE_ID;
		settingsTable.className = 'table-style1';
		settingsTable.width = '40%';
		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";
		var _loop_1 = function (k) {
			// convert it into a KEY
			var key = parseInt(k, 10);
			var setting = CFG[key];
			if (setting == null) {
				console.error('missing setting entry:', key, name(key));
				return "continue";
			}
			var settingId = SETTING_ID_PREFIX + name(key);
			var row = settingsTable.insertRow(-1);
			row.classList.add('setting');
			if (setting.requiresReload) {
				row.classList.add('reload');
			}
			row.setAttribute('onclick', '');
			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";
			var tooltipEl = ensureTooltip(settingId, row);
			tooltipEl.innerHTML = setting.title;
			if (setting.requiresReload) {
				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>";
			}
			row.addEventListener('click', function () {
				var newValue = !get(key);
				set(key, newValue);
				document.getElementById(settingId).src = getCheckImageSrc(newValue);
			});
		};
		for (var k in CFG) {
			_loop_1(k);
		}
		insertAfter(settingsTable, settingsHeader);
	}
	settings.init = init;
	var _a;
})(settings || (settings = {}));
/**
 * Code from https://github.com/davidmerfield/randomColor
 */
var randomColor;
(function (randomColor) {
	// seed to get repeatable colors
	var seed = null;
	var COLOR_NOT_FOUND = {
		hueRange: [],
		lowerBounds: [],
		saturationRange: [],
		brightnessRange: []
	};
	var COLOR_BOUNDS = {
		'monochrome': {
			hueRange: [],
			lowerBounds: [[0, 0], [100, 0]]
		},
		'red': {
			hueRange: [-26, 18],
			lowerBounds: [[20, 100], [30, 92], [40, 89], [50, 85], [60, 78], [70, 70], [80, 60], [90, 55], [100, 50]]
		},
		'orange': {
			hueRange: [19, 46],
			lowerBounds: [[20, 100], [30, 93], [40, 88], [50, 86], [60, 85], [70, 70], [100, 70]]
		},
		'yellow': {
			hueRange: [47, 62],
			lowerBounds: [[25, 100], [40, 94], [50, 89], [60, 86], [70, 84], [80, 82], [90, 80], [100, 75]]
		},
		'green': {
			hueRange: [63, 178],
			lowerBounds: [[30, 100], [40, 90], [50, 85], [60, 81], [70, 74], [80, 64], [90, 50], [100, 40]]
		},
		'blue': {
			hueRange: [179, 257],
			lowerBounds: [[20, 100], [30, 86], [40, 80], [50, 74], [60, 60], [70, 52], [80, 44], [90, 39], [100, 35]]
		},
		'purple': {
			hueRange: [258, 282],
			lowerBounds: [[20, 100], [30, 87], [40, 79], [50, 70], [60, 65], [70, 59], [80, 52], [90, 45], [100, 42]]
		},
		'pink': {
			hueRange: [283, 334],
			lowerBounds: [[20, 100], [30, 90], [40, 86], [60, 84], [80, 80], [90, 75], [100, 73]]
		}
	};
	// shared color dictionary
	var colorDictionary = {};
	function defineColor(name, hueRange, lowerBounds) {
		var _a = lowerBounds[0], sMin = _a[0], bMax = _a[1];
		var _b = lowerBounds[lowerBounds.length - 1], sMax = _b[0], bMin = _b[1];
		colorDictionary[name] = {
			hueRange: hueRange,
			lowerBounds: lowerBounds,
			saturationRange: [sMin, sMax],
			brightnessRange: [bMin, bMax]
		};
	}
	function loadColorBounds() {
		for (var name_1 in COLOR_BOUNDS) {
			defineColor(name_1, COLOR_BOUNDS[name_1].hueRange, COLOR_BOUNDS[name_1].lowerBounds);
		}
	}
	function randomWithin(min, max) {
		if (min === void 0) { min = 0; }
		if (max === void 0) { max = 0; }
		if (seed === null) {
			return Math.floor(min + Math.random() * (max + 1 - min));
		}
		else {
			// seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
			seed = (seed * 9301 + 49297) % 233280;
			var rnd = seed / 233280.0;
			return Math.floor(min + rnd * (max - min));
		}
	}
	function getColorInfo(hue) {
		// maps red colors to make picking hue easier
		if (hue >= 334 && hue <= 360) {
			hue -= 360;
		}
		for (var colorName in colorDictionary) {
			var color = colorDictionary[colorName];
			if (color.hueRange.length > 0 &&
				hue >= color.hueRange[0] &&
				hue <= color.hueRange[1]) {
				return colorDictionary[colorName];
			}
		}
		return COLOR_NOT_FOUND;
	}
	function getHueRange(colorInput) {
		var number = typeof colorInput === 'undefined' ? Number.NaN : colorInput;
		if (typeof number === 'string') {
			number = parseInt(number, 10);
		}
		if (colorInput && isNaN(number) && colorDictionary.hasOwnProperty(colorInput)) {
			var color = colorDictionary[colorInput];
			if (color.hueRange.length > 0) {
				return color.hueRange;
			}
		}
		else if (!isNaN(number) && number < 360 && number > 0) {
			return [number, number];
		}
		return [0, 360];
	}
	function pickHue(options) {
		var hueRange = getHueRange(options.hue);
		var hue = randomWithin(hueRange[0], hueRange[1]);
		// instead of storing red as two seperate ranges, we group them, using negative numbers
		if (hue < 0) {
			return 360 + hue;
		}
		return hue;
	}
	function getSaturationRange(hue) {
		return getColorInfo(hue).saturationRange;
	}
	function pickSaturation(hue, options) {
		if (options.luminosity === 'random') {
			return randomWithin(0, 100);
		}
		if (options.hue === 'monochrome') {
			return 0;
		}
		var _a = getSaturationRange(hue), sMin = _a[0], sMax = _a[1];
		switch (options.luminosity) {
			case 'bright':
				sMin = 55;
				break;
			case 'dark':
				sMin = sMax - 10;
				break;
			case 'light':
				sMax = 55;
				break;
		}
		return randomWithin(sMin, sMax);
	}
	function getMinimumBrightness(H, S) {
		var lowerBounds = getColorInfo(H).lowerBounds;
		for (var i = 0; i < lowerBounds.length - 1; i++) {
			var _a = lowerBounds[i], s1 = _a[0], v1 = _a[1];
			var _b = lowerBounds[i + 1], s2 = _b[0], v2 = _b[1];
			if (S >= s1 && S <= s2) {
				var m = (v2 - v1) / (s2 - s1);
				var b = v1 - m * s1;
				return m * S + b;
			}
		}
		return 0;
	}
	function pickBrightness(H, S, options) {
		var bMin = getMinimumBrightness(H, S);
		var bMax = 100;
		switch (options.luminosity) {
			case 'dark':
				bMax = bMin + 20;
				break;
			case 'light':
				bMin = (bMax + bMin) / 2;
				break;
			case 'random':
				bMin = 0;
				bMax = 100;
				break;
		}
		return randomWithin(bMin, bMax);
	}
	function HSVtoHSL(hsv) {
		var h = hsv[0];
		var s = hsv[1] / 100;
		var v = hsv[2] / 100;
		var k = (2 - s) * v;
		return [
			h,
			Math.round(s * v / (k < 1 ? k : 2 - k) * 10000) / 100,
			k / 2 * 100
		];
	}
	function HSVtoRGB(hsv) {
		// this doesn't work for the values of 0 and 360 here's the hacky fix
		var h = Math.min(Math.max(hsv[0], 1), 359);
		// Rebase the h,s,v values
		h = h / 360;
		var s = hsv[1] / 100;
		var v = hsv[2] / 100;
		var h_i = Math.floor(h * 6);
		var f = h * 6 - h_i;
		var p = v * (1 - s);
		var q = v * (1 - f * s);
		var t = v * (1 - (1 - f) * s);
		var r = 256;
		var g = 256;
		var b = 256;
		switch (h_i) {
			case 0:
				r = v;
				g = t;
				b = p;
				break;
			case 1:
				r = q;
				g = v;
				b = p;
				break;
			case 2:
				r = p;
				g = v;
				b = t;
				break;
			case 3:
				r = p;
				g = q;
				b = v;
				break;
			case 4:
				r = t;
				g = p;
				b = v;
				break;
			case 5:
				r = v;
				g = p;
				b = q;
				break;
		}
		return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
	}
	function HSVtoHex(hsv) {
		function componentToHex(c) {
			var hex = c.toString(16);
			return hex.length == 1 ? '0' + hex : hex;
		}
		var rgb = HSVtoRGB(hsv);
		return '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
	}
	function setFormat(hsv, options) {
		switch (options.format) {
			case 'hsvArray':
				return hsv;
			case 'hslArray':
				return HSVtoHSL(hsv);
			case 'hsl':
				var hsl = HSVtoHSL(hsv);
				return 'hsl(' + hsl[0] + ', ' + hsl[1] + '%, ' + hsl[2] + '%)';
			case 'hsla':
				var hslColor = HSVtoHSL(hsv);
				var alpha = options.alpha || Math.random();
				return 'hsla(' + hslColor[0] + ', ' + hslColor[1] + '%, ' + hslColor[2] + '%, ' + alpha + ')';
			case 'rgbArray':
				return HSVtoRGB(hsv);
			case 'rgb':
				var rgb = HSVtoRGB(hsv);
				return 'rgb(' + rgb.join(', ') + ')';
			case 'rgba':
				var rgbColor = HSVtoRGB(hsv);
				var alpha = options.alpha || Math.random();
				return 'rgba(' + rgbColor.join(', ') + ', ' + alpha + ')';
			case 'hex':
			default:
				return HSVtoHex(hsv);
		}
	}
	function generateColor(options) {
		// pick a hue (H)
		var H = pickHue(options);
		// use H to determine saturation (S)
		var S = pickSaturation(H, options);
		// use S and H to determine brightness (B)
		var B = pickBrightness(H, S, options);
		// return the HSB color in the desired format
		return setFormat([H, S, B], options);
	}
	function generate(options) {
		options = options || {};
		seed = options.seed == null ? null : options.seed;
		// check if we need to generate multiple colors
		if (options.count !== null && options.count !== undefined) {
			var totalColors = options.count;
			var colors = [];
			options.count = null;
			while (totalColors > colors.length) {
				// Since we're generating multiple colors, the seed has to be incrememented.
				// Otherwise we'd just generate the same color each time...
				if (seed !== null) {
					seed += 1;
				}
				colors.push(generateColor(options));
			}
			options.count = totalColors;
			return colors;
		}
		return generateColor(options);
	}
	randomColor.generate = generate;
	// populate the color dictionary
	loadColorBounds();
})(randomColor || (randomColor = {}));
/**
 * hide crafting recipes of lower tiers or of maxed machines
 */
var hideCraftedRecipes;
(function (hideCraftedRecipes) {
	function setRecipeVisibility(key, visible) {
		var recipeRow = document.getElementById('crafting-' + key);
		if (recipeRow) {
			recipeRow.style.display = (!settings.get(settings.KEY.hideCraftingRecipes) || visible) ? '' : 'none';
		}
	}
	function hideLeveledRecipes(max, getKey, init) {
		if (init === void 0) { init = false; }
		var keys2Observe = [];
		var maxLevel = 0;
		for (var i = max - 1; i >= 0; i--) {
			var level = i + 1;
			var key = getKey(i);
			var boundKey = getBoundKey(key);
			keys2Observe.push(key);
			keys2Observe.push(boundKey);
			if (window[key] > 0 || window[boundKey] > 0) {
				maxLevel = Math.max(maxLevel, level);
			}
			setRecipeVisibility(key, level > maxLevel);
		}
		if (init) {
			observer.add(keys2Observe, function () { return hideLeveledRecipes(max, getKey, false); });
		}
	}
	function hideToolRecipe(key, init) {
		if (init === void 0) { init = false; }
		var emptyKey = getTierKey(key, 0);
		var keys2Observe = [emptyKey];
		var hasTool = window[emptyKey] > 0;
		for (var i = 0; i < TIER_LEVELS.length; i++) {
			var boundKey = getBoundKey(getTierKey(key, i));
			hasTool = hasTool || window[boundKey] > 0;
			keys2Observe.push(boundKey);
		}
		setRecipeVisibility(emptyKey, !hasTool);
		if (init) {
			observer.add(keys2Observe, function () { return hideToolRecipe(key, false); });
		}
	}
	function hideRecipe(key, hideInfo, init) {
		if (init === void 0) { init = false; }
		var maxValue = typeof hideInfo.max === 'function' ? hideInfo.max() : hideInfo.max;
		var boundKey = getBoundKey(key);
		var unbound = window[key];
		var bound = window[boundKey];
		var extra = (hideInfo.extraKeys || []).map(function (k) { return window[k]; }).reduce(function (p, c) { return p + c; }, 0);
		setRecipeVisibility(key, (bound + unbound + extra) < maxValue);
		if (init) {
			observer.add([key, boundKey], function () { return hideRecipe(key, hideInfo, false); });
		}
	}
	function init() {
		function processRecipes(init) {
			if (init === void 0) { init = false; }
			// furnace
			hideLeveledRecipes(FURNACE_LEVELS.length, function (i) { return FURNACE_LEVELS[i] + 'Furnace'; }, init);
			// oil storage
			hideLeveledRecipes(7, function (i) { return 'oilStorage' + (i + 1); }, init);
			// oven recipes
			hideLeveledRecipes(OVEN_LEVELS.length, function (i) { return OVEN_LEVELS[i] + 'Oven'; }, init);
			// tools
			for (var _i = 0, TIER_ITEMS_1 = TIER_ITEMS; _i < TIER_ITEMS_1.length; _i++) {
				var tool = TIER_ITEMS_1[_i];
				hideToolRecipe(tool, init);
			}
			// other stuff
			for (var key in HIDE_RECIPES) {
				hideRecipe(key, HIDE_RECIPES[key], init);
			}
			if (init) {
				settings.observe(settings.KEY.hideCraftingRecipes, function () { return processRecipes(false); });
			}
		}
		processRecipes(true);
		var _processCraftingTab = window.processCraftingTab;
		window.processCraftingTab = function () {
			var reinit = !!window.refreshLoadCraftingTable;
			_processCraftingTab();
			if (reinit) {
				processRecipes(false);
			}
		};
	}
	hideCraftedRecipes.init = init;
})(hideCraftedRecipes || (hideCraftedRecipes = {}));
/**
 * improve item boxes
 */
var improveItemBoxes;
(function (improveItemBoxes) {
	function hideNumberInItemBox(key, setVisibility) {
		if (setVisibility === void 0) { setVisibility = false; }
		var itemBox = document.getElementById('item-box-' + key);
		var numberElement = itemBox.lastElementChild;
		if (setVisibility) {
			numberElement.style.visibility = 'hidden';
		}
		else {
			numberElement.style.display = 'none';
		}
	}
	function addSpan2ItemBox(key) {
		hideNumberInItemBox(key);
		var itemBox = document.getElementById('item-box-' + key);
		var span = document.createElement('span');
		itemBox.appendChild(span);
		return span;
	}
	function setOilPerSecond(span, oil) {
		span.innerHTML = "+ " + format.number(oil) + " L/s <img src=\"images/oil.png\" class=\"image-icon-20\" style=\"margin-top: -2px;\">";
	}
	// show capacity of furnace
	function addFurnaceCaption() {
		for (var i = 0; i < FURNACE_LEVELS.length; i++) {
			var key = FURNACE_LEVELS[i] + 'Furnace';
			var capacitySpan = addSpan2ItemBox(getBoundKey(key));
			capacitySpan.className = 'capacity';
			capacitySpan.textContent = 'Capacity: ' + format.number(FURNACE_CAPACITIES[i]);
		}
	}
	// show oil cap of oil storage
	function addOilStorageCaption() {
		for (var i = 0; i < OIL_STORAGE_SIZES.length; i++) {
			var key = 'oilStorage' + (i + 1);
			var capSpan = addSpan2ItemBox(getBoundKey(key));
			capSpan.className = 'oil-cap';
			capSpan.textContent = 'Oil cap: ' + format.number(OIL_STORAGE_SIZES[i]);
		}
	}
	// show oil per second
	function addOilCaption() {
		var handheldOilSpan = addSpan2ItemBox('handheldOilPump');
		setOilPerSecond(handheldOilSpan, 1 * window.miner);
		observer.add('miner', function () { return setOilPerSecond(handheldOilSpan, 1 * window.miner); });
		var oilPipeSpan = addSpan2ItemBox('boundOilPipe');
		setOilPerSecond(oilPipeSpan, 50);
	}
	function hideNumberCaption() {
		hideNumberInItemBox('emptyAnvil', true);
		hideNumberInItemBox('farmer', true);
		hideNumberInItemBox('planter', true);
		hideNumberInItemBox('cooksBook', true);
		hideNumberInItemBox('cooksPage', true);
		hideNumberInItemBox('combatDropTable', true);
	}
	// show current tier
	function addTierCaption() {
		for (var _i = 0, TIER_ITEMS_2 = TIER_ITEMS; _i < TIER_ITEMS_2.length; _i++) {
			var tierItem = TIER_ITEMS_2[_i];
			for (var i = 0; i < TIER_LEVELS.length; i++) {
				var key = getTierKey(tierItem, i);
				var toolKey = tierItem == 'rake' ? key : getBoundKey(key);
				var tierSpan = addSpan2ItemBox(toolKey);
				tierSpan.className = 'tier';
				tierSpan.textContent = TIER_NAMES[i];
			}
		}
	}
	var boatKeys = ['rowBoat', 'canoe'];
	var boatTimerKeys = boatKeys.map(function (k) { return k + 'Timer'; });
	function checkBoat(span, timerKey, init) {
		if (init === void 0) { init = false; }
		var isInTransit = window[timerKey] > 0;
		var otherInTransit = boatTimerKeys.some(function (k) { return k != timerKey && window[k] > 0; });
		span.textContent = isInTransit ? 'In transit' : 'Ready';
		span.style.visibility = otherInTransit ? 'hidden' : '';
		if (init) {
			observer.add(boatTimerKeys, function () { return checkBoat(span, timerKey, false); });
		}
	}
	// show boat progress
	function addBoatCaption() {
		for (var i = 0; i < boatKeys.length; i++) {
			var span = addSpan2ItemBox(getBoundKey(boatKeys[i]));
			checkBoat(span, boatTimerKeys[i], true);
		}
	}
	// show bonemeal
	function addBonemealCaption() {
		var noBonemealSpan = addSpan2ItemBox('boundBonemealBin');
		noBonemealSpan.textContent = 'Bonemeal: 0';
		var bonemealSpan = addSpan2ItemBox('boundFilledBonemealBin');
		bonemealSpan.dataset.itemDisplay = 'bonemeal';
		bonemealSpan.textContent = format.number(window.bonemeal);
		var captionSpan = document.createElement('span');
		captionSpan.textContent = 'Bonemeal: ';
		bonemealSpan.parentElement.insertBefore(captionSpan, bonemealSpan);
	}
	function init() {
		addFurnaceCaption();
		addOilStorageCaption();
		addOilCaption();
		hideNumberCaption();
		addTierCaption();
		addBoatCaption();
		addBonemealCaption();
	}
	improveItemBoxes.init = init;
})(improveItemBoxes || (improveItemBoxes = {}));
/**
 * add new chat
 */
var chat;
(function (chat) {
	var CHAT_HISTORY_KEY = 'chatHistory';
	var MAX_CHAT_HISTORY_LENGTH = 100;
	var Type;
	(function (Type) {
		Type[Type["reload"] = -1] = "reload";
		Type[Type["normal"] = 0] = "normal";
		Type[Type["pmReceived"] = 1] = "pmReceived";
		Type[Type["pmSent"] = 2] = "pmSent";
		Type[Type["serverMsg"] = 3] = "serverMsg";
	})(Type || (Type = {}));
	;
	/**
	 * The chunk hiding starts with at least 10 chunks.
	 * So there are at least
	 *	(chunkHidingMinChunks-1) * msgChunkSize + 1 = 9 * 100 + 1 = 901
	 * messages before the chunk hiding mechanism starts.
	 */
	var CHUNK_HIDING_MIN_CHUNKS = 10;
	var MSG_CHUNK_SIZE = 100;
	var RELOADED_CHAT_DATA = {
		timestamp: 0,
		username: '',
		userlevel: 0,
		icon: 0,
		tag: 0,
		type: Type.reload,
		msg: '[...]'
	};
	var CHAT_BOX_ID = 'div-chat';
	var GENERAL_CHAT_TAB_ID = 'tab-chat-general';
	var GENERAL_CHAT_DIV_ID = 'div-chat-area';
	var PM_CHAT_TAB_PREFIX = 'tab-chat-pm-';
	var PM_CHAT_DIV_PREFIX = 'div-chat-pm-';
	var CHAT_INPUT_ID = 'chat-input-text';
	var CHAT_CLASS = 'div-chat-area';
	var COLORIZE_CLASS = 'colorize';
	var CHAT_ICONS = [
		null,
		{ key: 'halloween2015', title: 'Halloween 2015' },
		{ key: 'christmas2015', title: 'Chirstmas 2015' },
		{ key: 'easter2016', title: 'Holiday' },
		{ key: 'halloween2016', title: 'Halloween 2016' },
		{ key: 'christmas2016', title: 'Chirstmas 2016' },
		{ key: 'dh1Max', title: 'Max Level in DH1' },
		{ key: 'hardcore', title: 'Hardcore Account' },
		{ key: 'quest', title: 'Questmaster' }
	];
	var CHAT_TAGS = [
		null,
		{ key: 'donor', name: '' },
		{ key: 'contributor', name: 'Contributor' },
		{ key: 'mod', name: 'Moderator' },
		{ key: 'dev', name: 'Dev' },
		{ key: 'yell', name: 'Server Message' }
	];
	var LOCALE = 'en-US';
	var LOCALE_OPTIONS = {
		hour12: false,
		year: 'numeric',
		month: 'long',
		day: 'numeric',
		hour: '2-digit',
		minute: '2-digit',
		second: '2-digit'
	};
	var TUTORIAL_CMD = 'tutorial';
	// load chat history
	var chatHistory = store.get(CHAT_HISTORY_KEY) || [];
	// find index of last message which is not a pm
	var lastNotPM = chatHistory.slice(0).reverse().find(function (d) {
		return !isPM(d);
	});
	// insert a placeholder for a reloaded chat
	if (lastNotPM && lastNotPM.type != Type.reload) {
		RELOADED_CHAT_DATA.timestamp = (new Date()).getTime();
		chatHistory.push(RELOADED_CHAT_DATA);
	}
	// store chat colors for each user
	var user2Color = new Map();
	var usedColors = new Set();
	// reserve color for special messages (e.g. server messages): white
	usedColors.add('#ffffff');
	function isMuted(user) {
		// return window.mutedPeople.some((name) => user.indexOf(name) > -1);
		return window.mutedPeople.indexOf(user) !== -1;
	}
	function handleScrolling(chatbox) {
		if (window.isAutoScrolling) {
			setTimeout(function () { return chatbox.scrollTop = chatbox.scrollHeight; });
		}
	}
	// for chat messages which arrive before DOMContentLoaded and can not be displayed since the DOM isn't ready
	var chatInitialized = false;
	function processChatData(username, icon, tag, msg, isPM) {
		var userlevel = 0;
		var type = Type.normal;
		if (isPM == 1) {
			var match = msg.match(/^\s*\[(.+) ([A-Za-z0-9 ]+)\]: (.+?)\s*$/) || ['', '', username, msg];
			type = match[1] == 'Sent to' ? Type.pmSent : Type.pmReceived;
			username = match[2];
			msg = match[3];
		}
		else if (tag == '5') {
			type = Type.serverMsg;
		}
		else {
			var match = msg.match(/^\s*\((\d+)\): (.+?)\s*$/);
			if (match) {
				userlevel = parseInt(match[1], 10);
				msg = match[2];
			}
			else {
				userlevel = window.getGlobalLevel();
			}
		}
		// unlinkify when using DH2QoL to store the plain message
		if (window.addToChatBox.toString().includes('linkify(arguments[3])')) {
			msg = msg.replace(/<a href='([^']+)' target='_blank'>\1<\/a>/ig, '$1');
		}
		if (type == Type.pmSent) {
			// turn some critical characters into HTML entities
			msg = msg.replace(/[;=<>]/g, function (char) { return '&#' + char.charCodeAt(0) + ';'; });
		}
		return {
			timestamp: now(),
			username: username,
			userlevel: userlevel,
			icon: parseInt(icon, 10),
			tag: parseInt(tag, 10),
			type: type,
			msg: msg
		};
	}
	function add2ChatHistory(data) {
		chatHistory.push(data);
		chatHistory = chatHistory.slice(-MAX_CHAT_HISTORY_LENGTH);
		store.set(CHAT_HISTORY_KEY, chatHistory);
	}
	function username2Id(username) {
		return username.replace(/[ \+]/g, '_');
	}
	function getChatTab(username) {
		var id = username == '' ? GENERAL_CHAT_TAB_ID : PM_CHAT_TAB_PREFIX + username2Id(username);
		var tab = document.getElementById(id);
		if (!tab) {
			tab = document.createElement('div');
			tab.className = 'chat-tab';
			tab.id = id;
			tab.dataset.username = username;
			tab.dataset.new = '0';
			tab.textContent = username;
			// thanks /u/Spino-Prime for pointing out this was missing
			var closeSpan = document.createElement('span');
			closeSpan.className = 'close';
			tab.appendChild(closeSpan);
			var chatTabs = document.getElementById('chat-tabs');
			var filler = chatTabs.querySelector('.filler');
			if (filler) {
				chatTabs.insertBefore(tab, filler);
			}
			else {
				chatTabs.appendChild(tab);
			}
		}
		return tab;
	}
	function getChatDiv(username) {
		var id = username == '' ? GENERAL_CHAT_DIV_ID : PM_CHAT_DIV_PREFIX + username2Id(username);
		var div = document.getElementById(id);
		if (!div) {
			div = document.createElement('div');
			div.setAttribute('disabled', 'disabled');
			div.id = id;
			div.className = CHAT_CLASS;
			var generalChat = document.getElementById(GENERAL_CHAT_DIV_ID);
			var height = generalChat.style.height;
			div.style.height = height;
			var chatDiv = generalChat.parentElement;
			chatDiv.insertBefore(div, generalChat);
		}
		return div;
	}
	function changeChatTab(oldTab, newTab) {
		oldTab.classList.remove('selected');
		newTab.classList.add('selected');
		newTab.dataset.new = '0';
		var oldChatDiv = getChatDiv(oldTab.dataset.username || '');
		oldChatDiv.classList.remove('selected');
		var newChatDiv = getChatDiv(newTab.dataset.username || '');
		newChatDiv.classList.add('selected');
		var toUsername = newTab.dataset.username;
		var newTextPlaceholder = toUsername == '' ? window.username + ':' : 'PM to ' + toUsername + ':';
		document.getElementById(CHAT_INPUT_ID).placeholder = newTextPlaceholder;
		if (window.isAutoScrolling) {
			setTimeout(function () { return newChatDiv.scrollTop = newChatDiv.scrollHeight; });
		}
	}
	function closeChatTab(username) {
		// TODO: maybe delete pms stored for that user?
		var oldTab = document.querySelector('#chat-tabs .chat-tab.selected');
		var tab2Close = getChatTab(username);
		if (oldTab.dataset.username == username) {
			var generalTab = getChatTab('');
			changeChatTab(tab2Close, generalTab);
		}
		var tabContainer = tab2Close.parentElement;
		tabContainer.removeChild(tab2Close);
	}
	function isPM(data) {
		return data.type === Type.pmSent || data.type === Type.pmReceived;
	}
	var msgChunkMap = new Map();
	var chatboxFragments = new Map();
	function colorizeMsg(username) {
		if (username == '') {
			return null;
		}
		if (!user2Color.has(username)) {
			var color = void 0;
			do {
				color = randomColor.generate({ luminosity: 'light' });
			} while (usedColors.has(color));
			user2Color.set(username, color);
			usedColors.add(color);
			addStyle("\n#" + CHAT_BOX_ID + "." + COLORIZE_CLASS + " .chat-msg[data-username=\"" + username + "\"]\n{\n\tbackground-color: " + color + ";\n}\n\t\t\t");
		}
		return user2Color.get(username);
	}
	function createMessageSegment(data) {
		var isThisPm = isPM(data);
		var msgUsername = data.type === Type.pmSent ? window.username : data.username;
		var historyIndex = chatHistory.indexOf(data);
		var isSameUser = null;
		var isSameTime = null;
		for (var i = historyIndex - 1; i >= 0 && (isSameUser === null || isSameTime === null); i--) {
			var dataBefore = chatHistory[i];
			if (isThisPm && isPM(dataBefore) ||
				!isThisPm && !isPM(dataBefore)) {
				if (isSameUser === null) {
					var beforeUsername = dataBefore.type == Type.pmSent ? window.username : dataBefore.username;
					isSameUser = beforeUsername === msgUsername;
				}
				if (dataBefore.type != Type.reload) {
					isSameTime = Math.floor(data.timestamp / 1000 / 60) - Math.floor(dataBefore.timestamp / 1000 / 60) === 0;
				}
			}
		}
		var d = new Date(data.timestamp);
		var hour = (d.getHours() < 10 ? '0' : '') + d.getHours();
		var minute = (d.getMinutes() < 10 ? '0' : '') + d.getMinutes();
		var icon = CHAT_ICONS[data.icon] || { key: '', title: '' };
		var tag = CHAT_TAGS[data.tag] || { key: '', name: '' };
		var formattedMsg = data.msg
			.replace(/<a href='(.+?)' target='_blank'>\1<\/a>/g, '$1')
			.replace(/(https?:\/\/[^\s"<>]+)/g, '<a target="_blank" href="$1">$1</a>');
		colorizeMsg(msgUsername);
		var msgTitle = data.type == Type.reload ? 'Chat loaded on ' + d.toLocaleString(LOCALE, LOCALE_OPTIONS) : '';
		var user = data.type === Type.serverMsg ? 'Server Message' : msgUsername;
		var levelAppendix = data.type == Type.normal ? ' (' + data.userlevel + ')' : '';
		var userTitle = data.tag != 5 ? tag.name : '';
		return "<span class=\"chat-msg\" data-type=\"" + data.type + "\" data-tag=\"" + tag.key + "\" data-username=\"" + msgUsername + "\">"
			+ ("<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>")
			+ ("<span class=\"user\" data-name=\"" + msgUsername + "\" data-same-user=\"" + isSameUser + "\">")
			+ ("<span class=\"icon " + icon.key + "\" title=\"" + icon.title + "\"></span>")
			+ ("<span class=\"name chat-tag-" + tag.key + "\" title=\"" + userTitle + "\">" + user + levelAppendix + ":</span>")
			+ "</span>"
			+ ("<span class=\"msg\" title=\"" + msgTitle + "\">" + formattedMsg + "</span>")
			+ "</span>";
	}
	function add2Chat(data) {
		if (!chatInitialized) {
			return;
		}
		var isThisPm = isPM(data);
		// don't mute pms (you can just ignore pm-tab if you like)
		if (!isThisPm && isMuted(data.username)) {
			return;
		}
		var userKey = isThisPm ? data.username : '';
		var chatTab = getChatTab(userKey);
		if (!chatTab.classList.contains('selected')) {
			chatTab.dataset.new = (parseInt(chatTab.dataset.new || '0', 10) + 1).toString();
		}
		if (isThisPm) {
			window.lastPMUser = data.username;
		}
		// username is 3-12 characters long
		var chatbox = getChatDiv(userKey);
		var msgChunk = msgChunkMap.get(userKey);
		if (!msgChunk || msgChunk.children.length >= MSG_CHUNK_SIZE) {
			msgChunk = document.createElement('div');
			msgChunk.className = 'msg-chunk';
			msgChunkMap.set(userKey, msgChunk);
			if (chatboxFragments != null) {
				if (!chatboxFragments.has(userKey)) {
					chatboxFragments.set(userKey, document.createDocumentFragment());
				}
				chatboxFragments.get(userKey).appendChild(msgChunk);
			}
			else {
				chatbox.appendChild(msgChunk);
			}
		}
		var tmp = document.createElement('templateWrapper');
		tmp.innerHTML = createMessageSegment(data);
		msgChunk.appendChild(tmp.children[0]);
		handleScrolling(chatbox);
	}
	function applyChatStyle() {
		addStyle("\nspan.chat-msg\n{\n\tdisplay: flex;\n\tpadding: 1px 0;\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}\n.chat-msg .timestamp\n{\n\tdisplay: none;\n}\n.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\tposition: absolute;\n\tleft: 2.5rem;\n\ttop: -0.4rem;\n\ttext-align: center;\n\twhite-space: nowrap;\n}\n\n.chat-msg[data-type=\"" + Type.pmReceived + "\"] { color: purple; }\n.chat-msg[data-type=\"" + Type.pmSent + "\"] { color: purple; }\n.chat-msg[data-type=\"" + Type.serverMsg + "\"] { color: blue; }\n.chat-msg[data-tag=\"contributor\"] { color: green; }\n.chat-msg[data-tag=\"mod\"] { color: #669999; }\n.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\tpadding-left: 22px;\n}\n.chat-msg .user[data-same-user=\"true\"]:not([data-name=\"\"])\n{\n\topacity: 0;\n}\n\n.chat-msg .user .icon\n{\n\tmargin-left: -22px;\n}\n.chat-msg .user .icon::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\n.chat-msg .user .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-msg .user .name.chat-tag-contributor,\n.chat-msg .user .name.chat-tag-mod,\n.chat-msg .user .name.chat-tag-dev,\n.chat-msg .user .name.chat-tag-yell\n{\n\tcolor: white;\n\tdisplay: inline-block;\n\tfont-size: 10pt;\n\tmargin-top: -1px;\n\tpadding-bottom: 0;\n\ttext-align: center;\n\t/* 2px border, 10 padding */\n\twidth: calc(100% - 2*1px - 2*5px);\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: 100%;\n\theight: 130px;\n\tdisplay: none;\n}\n#" + CHAT_BOX_ID + " ." + CHAT_CLASS + ".selected\n{\n\tdisplay: block;\n}\n#chat-tabs\n{\n\tdisplay: flex;\n\tmargin: 10px -6px -6px;\n\tflex-wrap: wrap;\n}\n#chat-tabs .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 .chat-tab.selected\n{\n\tbackground-color: transparent;\n\tborder-top-color: transparent;\n}\n#chat-tabs .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 .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 .chat-tab[data-new=\"0\"]::after\n{\n\tcolor: inherit;\n\tfont-weight: normal;\n}\n#chat-tabs .chat-tab:not(.general).selected::after,\n#chat-tabs .chat-tab:not(.general):hover::after\n{\n\tvisibility: hidden;\n}\n#chat-tabs .chat-tab:not(.general).selected .close::after,\n#chat-tabs .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\t\t");
	}
	function addIntelligentScrolling() {
		// add checkbox instead of button for toggling auto scrolling
		var btn = document.querySelector('input[value="Toggle Autoscroll"]');
		var btnParent = btn.parentElement;
		var checkboxId = 'chat-toggle-autoscroll';
		// create checkbox
		var toggleCheckbox = document.createElement('input');
		toggleCheckbox.type = 'checkbox';
		toggleCheckbox.id = checkboxId;
		toggleCheckbox.checked = true;
		// create label
		var toggleLabel = document.createElement('label');
		toggleLabel.htmlFor = checkboxId;
		toggleLabel.textContent = 'Autoscroll';
		btnParent.insertBefore(toggleCheckbox, btn);
		btnParent.insertBefore(toggleLabel, btn);
		btn.style.display = 'none';
		// add checkbox for intelligent scrolling
		var isCheckboxId = 'chat-toggle-intelligent-scroll';
		var intScrollCheckbox = document.createElement('input');
		intScrollCheckbox.type = 'checkbox';
		intScrollCheckbox.id = isCheckboxId;
		intScrollCheckbox.checked = true;
		// add label
		var intScrollLabel = document.createElement('label');
		intScrollLabel.htmlFor = isCheckboxId;
		intScrollLabel.textContent = 'Intelligent Scrolling';
		btnParent.appendChild(intScrollCheckbox);
		btnParent.appendChild(intScrollLabel);
		var chatArea = document.getElementById(GENERAL_CHAT_DIV_ID);
		var showScrollTextTimeout = null;
		function setAutoScrolling(value, full) {
			if (full === void 0) { full = false; }
			if (window.isAutoScrolling != value) {
				toggleCheckbox.checked = value;
				window.isAutoScrolling = value;
				var icon_1 = 'none';
				var color_1 = value ? 'lime' : 'red';
				var text_1 = (value ? 'En' : 'Dis') + 'abled' + (full ? ' Autoscroll' : '');
				if (full) {
					if (showScrollTextTimeout) {
						window.clearTimeout(showScrollTextTimeout);
					}
					showScrollTextTimeout = window.setTimeout(function () { return window.scrollText(icon_1, color_1, text_1); }, 300);
				}
				else {
					window.scrollText(icon_1, color_1, text_1);
				}
				return true;
			}
			return false;
		}
		toggleCheckbox.addEventListener('change', function () {
			setAutoScrolling(this.checked);
			if (this.checked && intScrollCheckbox.checked) {
				chatArea.scrollTop = chatArea.scrollHeight - chatArea.clientHeight;
			}
		});
		var placeholderTemplate = document.createElement('div');
		placeholderTemplate.className = 'placeholder';
		var childStore = new WeakMap();
		function scrollHugeChat() {
			// # of children
			var chunkNum = chatArea.children.length;
			// start chunk hiding at a specific amount of chunks
			if (chunkNum < CHUNK_HIDING_MIN_CHUNKS) {
				return;
			}
			var visibleTop = chatArea.scrollTop;
			var visibleBottom = visibleTop + chatArea.clientHeight;
			var referenceTop = visibleTop - window.innerHeight;
			var referenceBottom = visibleBottom + window.innerHeight;
			var top = 0;
			// never hide the last element since its size may change at any time when a new message gets appended
			for (var i = 0; i < chunkNum - 1; i++) {
				var child = chatArea.children[i];
				var height = child.clientHeight;
				var bottom = top + height;
				var isVisible = top >= referenceTop && top <= referenceBottom
					|| bottom >= referenceTop && bottom <= referenceBottom
					|| top < referenceTop && bottom > referenceBottom;
				var isPlaceholder = child.classList.contains('placeholder');
				if (!isVisible && !isPlaceholder) {
					var newPlaceholder = placeholderTemplate.cloneNode(false);
					newPlaceholder.style.height = height + 'px';
					chatArea.replaceChild(newPlaceholder, child);
					childStore.set(newPlaceholder, child);
				}
				else if (isVisible && isPlaceholder) {
					var oldChild = childStore.get(child);
					chatArea.replaceChild(oldChild, child);
					childStore.delete(child);
				}
				top = bottom;
			}
		}
		var delayedScrollStart = null;
		var delayedScrollTimeout = null;
		// does not consider pm tabs; may be changed in a future version?
		chatArea.addEventListener('scroll', function () {
			if (intScrollCheckbox.checked) {
				var scrolled2Bottom = (chatArea.scrollTop + chatArea.clientHeight) >= chatArea.scrollHeight;
				setAutoScrolling(scrolled2Bottom, true);
			}
			var n = now();
			if (delayedScrollStart == null) {
				delayedScrollStart = n;
			}
			if (delayedScrollStart + 300 > n) {
				if (delayedScrollTimeout) {
					window.clearTimeout(delayedScrollTimeout);
				}
				delayedScrollTimeout = window.setTimeout(function () {
					delayedScrollStart = null;
					delayedScrollTimeout = null;
					scrollHugeChat();
				}, 50);
			}
		});
	}
	function clickChatTab(newTab) {
		var oldTab = document.querySelector('#chat-tabs .chat-tab.selected');
		if (newTab == oldTab) {
			return;
		}
		changeChatTab(oldTab, newTab);
	}
	function clickCloseChatTab(tab) {
		var username = tab.dataset.username || '';
		var chatDiv = getChatDiv(username);
		if (chatDiv.children.length === 0 ||
			confirm("Do you want to close the pm tab of \"" + username + "\"?")) {
			closeChatTab(username);
		}
	}
	function addChatTabs() {
		var chatBoxArea = document.getElementById(CHAT_BOX_ID);
		var chatTabs = document.createElement('div');
		chatTabs.id = 'chat-tabs';
		chatTabs.addEventListener('click', function (event) {
			var newTab = event.target;
			if (newTab.classList.contains('close')) {
				return clickCloseChatTab(newTab.parentElement);
			}
			if (!newTab.classList.contains('chat-tab') || newTab.classList.contains('filler')) {
				return;
			}
			clickChatTab(newTab);
		});
		chatBoxArea.appendChild(chatTabs);
		var generalTab = getChatTab('');
		generalTab.classList.add('general');
		generalTab.classList.add('selected');
		generalTab.textContent = 'Server';
		var generalChatDiv = getChatDiv('');
		generalChatDiv.classList.add('selected');
		// works only if username length of 1 isn't allowed
		var fillerTab = getChatTab('f');
		fillerTab.classList.add('filler');
		fillerTab.textContent = '';
		var _sendChat = window.sendChat;
		window.sendChat = function (inputEl) {
			var msg = inputEl.value;
			var selectedTab = document.querySelector('.chat-tab.selected');
			if (selectedTab.dataset.username != '' && msg[0] != '/') {
				inputEl.value = '/pm ' + (selectedTab.dataset.username || '').replace(/ /g, '+') + ' ' + msg;
			}
			_sendChat(inputEl);
		};
	}
	function newAddToChatBox(username, icon, tag, msg, isPM) {
		var data = processChatData(username, icon, tag, msg, isPM);
		add2ChatHistory(data);
		if (settings.get(settings.KEY.useNewChat)) {
			add2Chat(data);
		}
		else {
			window.addToChatBox(username, icon, tag, msg, isPM);
		}
	}
	chat.newAddToChatBox = newAddToChatBox;
	function newChat() {
		addChatTabs();
		applyChatStyle();
		window.addToChatBox = newAddToChatBox;
		chatInitialized = true;
		var chatbox = document.getElementById(CHAT_BOX_ID);
		chatbox.addEventListener('click', function (event) {
			var target = event.target;
			while (target && target.id != CHAT_BOX_ID && !target.classList.contains('user')) {
				target = target.parentElement;
			}
			if (!target || target.id == CHAT_BOX_ID) {
				return;
			}
			var username = target.dataset.name || '';
			if (username == window.username || username == '') {
				return;
			}
			var userTab = getChatTab(username);
			clickChatTab(userTab);
			var input = document.getElementById(CHAT_INPUT_ID);
			input.focus();
		});
		chatbox.addEventListener('mouseover', function (event) {
			var target = event.target;
			if (!target.classList.contains('timestamp') || !target.dataset.timestamp) {
				return;
			}
			var timestamp = parseInt(target.dataset.timestamp || '0', 10);
			target.dataset.fulltime = (new Date(timestamp)).toLocaleDateString(LOCALE, LOCALE_OPTIONS);
			target.dataset.timestamp = '';
		});
	}
	var commands = ['pm', 'mute', 'ipmute'];
	function addCommandSuggester() {
		var input = document.getElementById(CHAT_INPUT_ID);
		input.addEventListener('keyup', function (event) {
			if (event.key != 'Backspace' && event.key != 'Delete' &&
				input.selectionStart == input.selectionEnd &&
				input.selectionStart == input.value.length &&
				input.value.startsWith('/')) {
				var value_1 = input.value.substr(1);
				var suggestions = commands.filter(function (c) { return c.startsWith(value_1); });
				if (suggestions.length == 1) {
					input.value = '/' + suggestions[0];
					input.selectionStart = 1 + value_1.length;
					input.selectionEnd = input.value.length;
				}
			}
		});
	}
	function addOwnCommands() {
		commands.push(TUTORIAL_CMD);
		function processOwnCommands(value) {
			if (!value.startsWith('/')) {
				return value;
			}
			var msgPrefix = '/';
			var msg = value.substr(1);
			if (msg.startsWith('pm')) {
				var split = msg.split(' ');
				msgPrefix = '/' + split.slice(0, 2).join(' ') + ' ';
				msg = split.slice(2).join(' ');
			}
			if (msg.startsWith(TUTORIAL_CMD)) {
				// thanks aguyd (https://greasyfork.org/forum/profile/aguyd) for the idea
				var name_2 = msg.substr(TUTORIAL_CMD.length).trim();
				msgPrefix = '';
				msg = 'https://www.reddit.com/r/DiamondHunt/comments/5vrufh/diamond_hunt_2_starter_faq/';
				if (name_2.length != 0) {
					// maybe add '@' before the name?
					msg = name_2 + ', ' + msg;
				}
			}
			return msgPrefix + msg;
		}
		var _sendChat = window.sendChat;
		window.sendChat = function (inputEl) {
			inputEl.value = processOwnCommands(inputEl.value);
			_sendChat(inputEl);
		};
	}
	function checkColorize(init) {
		if (init === void 0) { init = false; }
		var chatDiv = document.getElementById(CHAT_BOX_ID);
		chatDiv.classList[settings.get(settings.KEY.colorizeChat) ? 'add' : 'remove'](COLORIZE_CLASS);
		if (init) {
			settings.observe(settings.KEY.colorizeChat, function () { return checkColorize(false); });
		}
	}
	function init() {
		if (!settings.get(settings.KEY.useNewChat)) {
			return;
		}
		newChat();
		addIntelligentScrolling();
		addCommandSuggester();
		addOwnCommands();
		checkColorize(true);
		var _enlargeChat = window.enlargeChat;
		var chatBoxArea = document.getElementById(CHAT_BOX_ID);
		function setChatBoxHeight(height) {
			var generalChat = document.getElementById(GENERAL_CHAT_DIV_ID);
			generalChat.style.height = height;
			var chatDivs = chatBoxArea.querySelectorAll('div[id^="' + PM_CHAT_DIV_PREFIX + '"]');
			for (var i = 0; i < chatDivs.length; i++) {
				chatDivs[i].style.height = height;
			}
		}
		window.enlargeChat = function (enlargeB) {
			_enlargeChat(enlargeB);
			var generalChatDiv = document.getElementById(GENERAL_CHAT_DIV_ID);
			var height = generalChatDiv.style.height;
			store.set('chat.height', height);
			setChatBoxHeight(height);
			handleScrolling(generalChatDiv);
		};
		setChatBoxHeight(store.get('chat.height'));
		// TEMP >>> (due to a naming issue, migrate the data)
		var oldChatHistoryKey = 'chatHistory2';
		var oldChatHistory = store.get(oldChatHistoryKey);
		if (oldChatHistory != null) {
			store.set(CHAT_HISTORY_KEY, oldChatHistory);
			store.remove(oldChatHistoryKey);
		}
		// TEMP <<<
		// add history to chat
		chatHistory.forEach(function (d) { return add2Chat(d); });
		if (chatboxFragments) {
			chatboxFragments.forEach(function (fragment, key) {
				var chatbox = getChatDiv(key);
				chatbox.appendChild(fragment);
			});
			chatboxFragments = null;
		}
		// reset the new counter for all tabs
		var tabs = document.querySelectorAll('.chat-tab');
		for (var i = 0; i < tabs.length; i++) {
			tabs[i].dataset.new = '0';
		}
	}
	chat.init = init;
})(chat || (chat = {}));
/**
 * hopefully only temporary fixes
 */
var temporaryFixes;
(function (temporaryFixes) {
	function fixSeedGrowTime() {
		// fix grow time of some seeds
		var seeds = {
			'limeLeafSeeds': {
				replace: '1 hour',
				replaceWith: '1 hour and 30 minutes'
			}
		};
		for (var seedName in seeds) {
			var tooltip = document.getElementById('tooltip-' + seedName);
			var timeSpan = tooltip.lastElementChild;
			var timeNode = timeSpan.lastChild;
			var seed = seeds[seedName];
			timeNode.textContent = (timeNode.textContent || '').replace(seed.replace, seed.replaceWith);
		}
	}
	// update hero being clickable in combat
	function setHeroClickable() {
		var heroArea = document.getElementById('hero-area');
		var equipment = heroArea.lastElementChild;
		equipment.style.pointerEvents = window.isInCombat() ? 'none' : '';
	}
	// warn before unloading/reloading the tab if combat is in progress
	function combatWarnOnUnload() {
		if (!window.isInCombat()) {
			window.onbeforeunload = null;
		}
		else {
			if (window.onbeforeunload == null) {
				window.onbeforeunload = function () { return 'You are in a fight!'; };
			}
		}
	}
	function fixCombatCountdown() {
		if (window.isInCombat() && window.combatCommenceTimer != 0) {
			document.getElementById('combat-countdown').style.display = '';
		}
	}
	// fix exhaustion timer and updating brewing and cooking recipes
	function fixExhaustionTimer() {
		if (document.getElementById('tab-container-combat').style.display != 'none') {
			window.combatNotFightingTick();
		}
	}
	function fixUpdatingBrewingTab() {
		if (window.currentOpenTab == 'brewing') {
			window.processBrewingTab();
		}
	}
	function fixUpdatingCookingTab() {
		if (window.currentOpenTab == 'cooksBook') {
			window.processCooksBookTab();
		}
	}
	function fixClientGameLoop() {
		var _clientGameLoop = window.clientGameLoop;
		window.clientGameLoop = function () {
			_clientGameLoop();
			setHeroClickable();
			combatWarnOnUnload();
			fixCombatCountdown();
			fixExhaustionTimer();
			fixUpdatingBrewingTab();
			fixUpdatingCookingTab();
		};
	}
	// fix elements of scrollText (e.g. when joining the game and receiving xp at that moment)
	function fixScroller() {
		var textEls = document.querySelectorAll('div.scroller');
		for (var i = 0; i < textEls.length; i++) {
			var scroller = textEls[i];
			if (scroller.style.position != 'absolute') {
				scroller.style.display = 'none';
			}
		}
	}
	// fix style of tooltips
	function fixTooltipStyle() {
		addStyle("\n\tbody > div.tooltip > h2:first-child\n\t{\n\t\tmargin-top: 0;\n\t\tfont-size: 20pt;\n\t\tfont-weight: normal;\n\t}\n\t\t");
	}
	// fix buiulding magic table dynamically
	function fixRefreshingMagicRecipes() {
		window.refreshLoadMagicTable = true;
		var _processMagicTab = window.processMagicTab;
		window.processMagicTab = function () {
			var _refreshLoadCraftingTable = window.refreshLoadCraftingTable;
			window.refreshLoadCraftingTable = window.refreshLoadMagicTable;
			_processMagicTab();
			window.refreshLoadCraftingTable = _refreshLoadCraftingTable;
		};
	}
	// move the strange leaf to brewing tab (thanks lasse_brus for this idea)
	function moveStrangeLeafs() {
		var strangeLeafBox = document.getElementById('item-box-strangeLeaf');
		var brewingContainer = document.getElementById('tab-sub-container-brewing');
		brewingContainer.appendChild(strangeLeafBox);
		// remove event listeners before binding the tooltip to it
		var $strangeLeafBox = window.$(strangeLeafBox);
		$strangeLeafBox.off('mouseover').off('mouseleave');
		strangeLeafBox.title = '';
		// bind tooltip to item box
		ensureTooltip('ingredient-secondary', strangeLeafBox);
		// change color
		var color1 = '#800080';
		var color2 = '#990099';
		strangeLeafBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
		$strangeLeafBox
			.mouseover(function () {
			strangeLeafBox.style.background = 'none';
			strangeLeafBox.style.backgroundColor = color2;
		})
			.mouseleave(function () {
			strangeLeafBox.style.background = 'linear-gradient(' + color1 + ', ' + color2 + ')';
		});
	}
	// fix height of map item
	function fixTreasureMap() {
		var mapBox = document.getElementById('item-box-treasureMap');
		var numSpan = mapBox.lastElementChild;
		numSpan.style.display = '';
		numSpan.style.visibility = 'hidden';
	}
	// fix wood cutting
	function fixWoodcutting() {
		addStyle("\n\timg.woodcutting-tree-img\n\t{\n\t\tborder: 1px solid transparent;\n\t}\n\t\t");
	}
	// fix gear spinning of giant drills
	function fixSpinningGear() {
		var _processMiningTab = window.processMiningTab;
		window.processMiningTab = function () {
			_processMiningTab();
			var crusherGears = document.getElementById('boundCrushers-img-icon-gear');
			window.manageGear(crusherGears, 'crushers');
			var giantDrillsGear = document.getElementById('boundGiantDrills-img-icon-gear');
			window.manageGear(giantDrillsGear, 'giantDrills');
		};
	}
	// fix rake dialog
	function fixRakeDialog() {
		var _clicksRake = window.clicksRake;
		window.clicksRake = function () {
			_clicksRake();
			var upgradeBtn = document.querySelector('#dialogue-id-upgrade-rake input[value="Upgrade"]');
			if (upgradeBtn) {
				var hide = window.diamondRake == 1;
				upgradeBtn.style.display = hide ? 'none' : '';
			}
		};
	}
	function init() {
		fixSeedGrowTime();
		fixClientGameLoop();
		fixScroller();
		fixTooltipStyle();
		fixRefreshingMagicRecipes();
		moveStrangeLeafs();
		fixTreasureMap();
		fixWoodcutting();
		fixSpinningGear();
		fixRakeDialog();
		// fix tooltip of whale/rainbowfish
		var tooltipTemplate = document.getElementById('tooltip-rawShark');
		function createRawFishTooltip(id, name) {
			var newTooltip = tooltipTemplate.cloneNode(true);
			newTooltip.id = 'tooltip-' + id;
			newTooltip.firstChild.textContent = name;
			newTooltip.lastChild.firstChild.textContent = '+? ';
			tooltipTemplate.parentElement.appendChild(newTooltip);
		}
		createRawFishTooltip('rawWhale', 'Raw Whale');
		createRawFishTooltip('rawRainbowFish', 'Raw Rainbowfish');
	}
	temporaryFixes.init = init;
})(temporaryFixes || (temporaryFixes = {}));
/**
 * improve timer
 */
var improveTimer;
(function (improveTimer) {
	function bindNewFormatter() {
		window.formatTime = function (seconds) {
			return format.timer(seconds);
		};
		window.formatTimeShort2 = function (seconds) {
			return format.timer(seconds);
		};
	}
	function improveSmeltingTimer() {
		addStyle("\n\t#notif-smelting > span:not(.timer)\n\t{\n\t\tdisplay: none;\n\t}\n\t\t");
		var smeltingNotifBox = document.getElementById('notif-smelting');
		var smeltingTimerEl = document.createElement('span');
		smeltingTimerEl.className = 'timer';
		smeltingNotifBox.appendChild(smeltingTimerEl);
		function updateSmeltingTimer() {
			var totalTime = window.smeltingPercD;
			var elapsedTime = window.smeltingPercN;
			smeltingTimerEl.textContent = format.timer(Math.max(totalTime - elapsedTime, 0));
		}
		observer.add('smeltingPercD', function () { return updateSmeltingTimer(); });
		observer.add('smeltingPercN', function () { return updateSmeltingTimer(); });
		updateSmeltingTimer();
	}
	function updateTreeInfo(place, infoElId, init) {
		if (init === void 0) { init = false; }
		var infoEl = document.getElementById(infoElId);
		var nameEl = infoEl.firstElementChild;
		var timerEl = infoEl.lastElementChild;
		var idKey = 'treeId' + place;
		var growTimerKey = 'treeGrowTimer' + place;
		var lockedKey = 'treeUnlocked' + place;
		var info = TREE_INFO[window[idKey]];
		if (!info) {
			var isLocked = place > 4 && window[lockedKey] == 0;
			nameEl.textContent = isLocked ? 'Locked' : 'Empty';
			timerEl.textContent = '';
		}
		else {
			nameEl.textContent = info.name;
			var remainingTime = info.growTime - window[growTimerKey];
			timerEl.textContent = remainingTime > 0 ? '(' + format.timer(remainingTime) + ')' : 'Fully grown';
		}
		if (init) {
			observer.add([idKey, growTimerKey, lockedKey], function () { return updateTreeInfo(place, infoElId, false); });
		}
	}
	function addTreeGrowTimer() {
		// add tree grow timer
		addStyle("\n/* hide timer elements of DH2QoL, because I can :P */\n.woodcutting-tree > *:not(img):not(.timer)\n{\n\tdisplay: none;\n}\n.woodcutting-tree > div.timer\n{\n\tcolor: white;\n\tmargin-top: 5px;\n\tpointer-events: none;\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tright: 0;\n}\n\t\t");
		for (var i = 0; i < 6; i++) {
			var treePlace = i + 1;
			var infoElId = 'wc-tree-timer-' + treePlace;
			var treeContainer = document.getElementById('wc-div-tree-' + treePlace);
			treeContainer.style.position = 'relative';
			var infoEl = document.createElement('div');
			infoEl.className = 'timer';
			infoEl.id = infoElId;
			var treeName = document.createElement('div');
			treeName.style.fontSize = '1.2rem';
			infoEl.appendChild(treeName);
			var treeTimer = document.createElement('div');
			infoEl.appendChild(treeTimer);
			treeContainer.appendChild(infoEl);
			updateTreeInfo(treePlace, infoElId, true);
		}
	}
	function init() {
		bindNewFormatter();
		improveSmeltingTimer();
		addTreeGrowTimer();
	}
	improveTimer.init = init;
})(improveTimer || (improveTimer = {}));
/**
 * improve smelting dialog
 */
var improveSmelting;
(function (improveSmelting) {
	var smeltingValue = null;
	var amountInput;
	function prepareAmountInput() {
		amountInput = document.getElementById('input-smelt-bars-amount');
		amountInput.type = 'number';
		amountInput.min = '0';
		amountInput.step = '5';
		function onValueChange() {
			smeltingValue = null;
			window.selectBar('', null, amountInput, document.getElementById('smelting-furnace-capacity').value);
		}
		amountInput.addEventListener('mouseup', onValueChange);
		amountInput.addEventListener('keyup', onValueChange);
		amountInput.setAttribute('onkeyup', '');
	}
	function setBarCap(bar, capacity) {
		var requirements = SMELTING_REQUIREMENTS[bar];
		var maxAmount = parseInt(capacity, 10);
		for (var key in requirements) {
			var req = requirements[key];
			maxAmount = Math.min(Math.floor(window[key] / req), maxAmount);
		}
		var value = parseInt(amountInput.value, 10);
		if (value > maxAmount) {
			smeltingValue = value;
			amountInput.value = maxAmount.toString();
		}
		else if (smeltingValue != null) {
			amountInput.value = Math.min(smeltingValue, maxAmount).toString();
			if (smeltingValue <= maxAmount) {
				smeltingValue = null;
			}
		}
	}
	function init() {
		prepareAmountInput();
		var _selectBar = window.selectBar;
		window.selectBar = function (bar, inputElement, inputBarsAmountEl, capacity) {
			setBarCap(bar, capacity);
			return _selectBar(bar, inputElement, inputBarsAmountEl, capacity);
		};
		var _openFurnaceDialogue = window.openFurnaceDialogue;
		window.openFurnaceDialogue = function (furnace) {
			if (window.smeltingBarType == 0) {
				amountInput.max = window.getFurnaceCapacity(furnace).toString();
			}
			return _openFurnaceDialogue(furnace);
		};
	}
	improveSmelting.init = init;
})(improveSmelting || (improveSmelting = {}));
/**
 * add chance to time calculator
 */
var chanceTooltips;
(function (chanceTooltips) {
	/**
	 * calculates the number of seconds until the event with the given chance happened at least once with the given
	 * probability p (in percent)
	 */
	function calcSecondsTillP(chancePerSecond, p) {
		return Math.round(Math.log(1 - p / 100) / Math.log(1 - chancePerSecond));
	}
	function addChanceTooltip(headline, chancePerSecond, elId, targetEl) {
		// ensure tooltip exists and is correctly binded
		var tooltipEl = ensureTooltip('chance-' + elId, targetEl);
		// set elements content
		var percValues = [1, 10, 20, 50, 80, 90, 99];
		var percRows = '';
		for (var _i = 0, percValues_1 = percValues; _i < percValues_1.length; _i++) {
			var p = percValues_1[_i];
			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>";
		}
		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";
	}
	function addChanceStyle() {
		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");
	}
	function chance2TimeCalculator() {
		var _clicksShovel = window.clicksShovel;
		window.clicksShovel = function () {
			_clicksShovel();
			var shovelChance = document.getElementById('dialogue-shovel-chance');
			var titleEl = shovelChance.parentElement;
			var chance = 1 / window.getChanceOfDiggingSand();
			addChanceTooltip('One sand at least every:', chance, 'shovel', titleEl);
		};
		// depends on fishingXp
		var _clicksFishingRod = window.clicksFishingRod;
		window.clicksFishingRod = function () {
			_clicksFishingRod();
			var fishList = ['shrimp', 'sardine', 'tuna', 'swordfish', 'shark'];
			for (var _i = 0, fishList_1 = fishList; _i < fishList_1.length; _i++) {
				var fish = fishList_1[_i];
				var rawFish = 'raw' + fish[0].toUpperCase() + fish.substr(1);
				var row = document.getElementById('dialogue-fishing-rod-tr-' + rawFish);
				var chanceCell = row.cells[4];
				var chance = (chanceCell.textContent || '')
					.replace(/[^\d\/]/g, '')
					.split('/')
					.reduce(function (p, c) { return p / parseInt(c, 10); }, 1);
				addChanceTooltip("One raw " + fish + " at least every:", chance, rawFish, row);
			}
		};
	}
	function init() {
		addChanceStyle();
		chance2TimeCalculator();
	}
	chanceTooltips.init = init;
})(chanceTooltips || (chanceTooltips = {}));
/**
 * add tooltips for recipes
 */
var recipeTooltips;
(function (recipeTooltips) {
	function updateRecipeTooltips(recipeKey, recipes) {
		var table = document.getElementById('table-' + recipeKey + '-recipe');
		var rows = table.rows;
		var _loop_2 = function (i) {
			var row = rows.item(i);
			var key = row.id.replace(recipeKey + '-', '');
			var recipe = recipes[key];
			var requirementCell = row.cells.item(3);
			requirementCell.title = recipe.recipe
				.map(function (name, i) {
				return format.number(recipe.recipeCost[i]) + ' '
					+ name.replace(/[A-Z]/g, function (match) { return ' ' + match.toLowerCase(); });
			})
				.join(' + ');
			window.$(requirementCell).tooltip();
		};
		for (var i = 1; i < rows.length; i++) {
			_loop_2(i);
		}
	}
	function updateTooltipsOnReinitRecipes(key) {
		var capitalKey = key[0].toUpperCase() + key.substr(1);
		var processKey = 'process' + capitalKey + 'Tab';
		var _processTab = window[processKey];
		window[processKey] = function () {
			var reinit = !!window['refreshLoad' + capitalKey + 'Table'];
			_processTab();
			if (reinit) {
				updateRecipeTooltips(key, window[key + 'Recipes']);
			}
		};
	}
	function init() {
		updateTooltipsOnReinitRecipes('crafting');
		updateTooltipsOnReinitRecipes('brewing');
		updateTooltipsOnReinitRecipes('magic');
		updateTooltipsOnReinitRecipes('cooksBook');
	}
	recipeTooltips.init = init;
})(recipeTooltips || (recipeTooltips = {}));
/**
 * fix formatting of numbers
 */
var fixNumbers;
(function (fixNumbers) {
	function prepareRecipeForTable(recipe) {
		// create a copy of the recipe to prevent requirement check from failing
		var newRecipe = JSON.parse(JSON.stringify(recipe));
		newRecipe.recipeCost = recipe.recipeCost.map(function (cost) { return format.number(cost); });
		newRecipe.xp = format.number(recipe.xp);
		return newRecipe;
	}
	function fixNumberFormat() {
		var _addRecipeToBrewingTable = window.addRecipeToBrewingTable;
		window.addRecipeToBrewingTable = function (brewingRecipe) {
			_addRecipeToBrewingTable(prepareRecipeForTable(brewingRecipe));
		};
		var _addRecipeToMagicTable = window.addRecipeToMagicTable;
		window.addRecipeToMagicTable = function (magicRecipe) {
			_addRecipeToMagicTable(prepareRecipeForTable(magicRecipe));
		};
	}
	function init() {
		fixNumberFormat();
	}
	fixNumbers.init = init;
})(fixNumbers || (fixNumbers = {}));
/**
 * style tweaks
 */
var styleTweaks;
(function (styleTweaks) {
	function addTweakStyle(setting, style) {
		if (setting != '') {
			var prefix = setting == '' ? '' : 'body.' + setting;
			style = style.replace(/(^\s*|,\s*|\}\s*)([^\{\},]+)(,|\s*\{)/g, '$1' + prefix + ' $2$3');
			document.body.classList.add(setting);
		}
		addStyle(style);
	}
	// tweak oil production/consumption
	function tweakOil() {
		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");
		// make room for oil cell on small devices
		var oilFlowValues = document.getElementById('oil-flow-values');
		var oilFlowCell = oilFlowValues.parentElement;
		oilFlowCell.style.width = '30%';
	}
	function tweakSelection() {
		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");
	}
	// tweak stardust monitor of DH2QoL to keep it in place
	function tweakStardust() {
		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");
	}
	function init() {
		tweakOil();
		tweakSelection();
		tweakStardust();
	}
	styleTweaks.init = init;
})(styleTweaks || (styleTweaks = {}));
/**
 * init
 */
var scriptInitialized = false;
function init() {
	settings.init();
	temporaryFixes.init();
	hideCraftedRecipes.init();
	improveItemBoxes.init();
	chat.init();
	improveTimer.init();
	improveSmelting.init();
	chanceTooltips.init();
	recipeTooltips.init();
	fixNumbers.init();
	styleTweaks.init();
	scriptInitialized = true;
}
document.addEventListener('DOMContentLoaded', function () {
	var oldValues = new Map();
	var _doCommand = window.doCommand;
	window.doCommand = function (data) {
		if (data.startsWith('REFRESH_ITEMS=')) {
			oldValues = new Map();
			for (var _i = 0, _a = window.jsItemArray; _i < _a.length; _i++) {
				var key = _a[_i];
				oldValues.set(key, window[key]);
			}
			_doCommand(data);
			if (!scriptInitialized) {
				init();
			}
			return;
		}
		else if (!scriptInitialized) {
			if (data.startsWith('CHAT=')) {
				var parts = data.substr(5).split('~');
				return chat.newAddToChatBox(parts[0], parts[1], parts[2], parts[3], 0);
			}
			else if (data.startsWith("PM=")) {
				return chat.newAddToChatBox(window.username, '0', '0', data.substr(3), 1);
			}
		}
		return _doCommand(data);
	};
	var _refreshItemValues = window.refreshItemValues;
	window.refreshItemValues = function (itemKeyList, firstLoad) {
		_refreshItemValues(itemKeyList, firstLoad);
		for (var _i = 0, itemKeyList_1 = itemKeyList; _i < itemKeyList_1.length; _i++) {
			var key = itemKeyList_1[_i];
			observer.notify(key, oldValues.get(key));
		}
	};
});
/**
 * fix web socket errors
 */
var main;
(function (main) {
	function webSocketLoaded(event) {
		var ws = window.webSocket;
		if (ws == null) {
			console.error('no webSocket instance found!');
			return;
		}
		var messageQueue = [];
		var _onMessage = ws.onmessage;
		ws.onmessage = function (event) { return messageQueue.push(event); };
		document.addEventListener('DOMContentLoaded', function () {
			messageQueue.forEach(function (event) { return window.onMessage(event); });
			ws.onmessage = _onMessage;
		});
		var commandQueue = [];
		var _sendBytes = window.sendBytes;
		window.sendBytes = function (command) { return commandQueue.push(command); };
		var _onOpen = ws.onopen;
		ws.onopen = function (event) {
			window.sendBytes = _sendBytes;
			commandQueue.forEach(function (command) { return window.sendBytes(command); });
			return _onOpen.call(ws, event);
		};
	}
	function isScriptElement(el) {
		return el.nodeName === 'SCRIPT';
	}
	function isWebSocketScript(script) {
		return script.src.includes('socket.js');
	}
	var found = false;
	var scripts = document.head ? document.head.querySelectorAll('script') : [];
	for (var i = 0; i < scripts.length; i++) {
		if (isWebSocketScript(scripts[i])) {
			// does this work?
			scripts[i].onload = webSocketLoaded;
			found = true;
		}
	}
	if (!found) {
		// create an observer instance
		var mutationObserver_1 = new MutationObserver(function (mutationList) {
			mutationList.forEach(function (mutation) {
				if (mutation.addedNodes.length === 0) {
					return;
				}
				for (var i = 0; i < mutation.addedNodes.length; i++) {
					var node = mutation.addedNodes[i];
					if (isScriptElement(node) && isWebSocketScript(node)) {
						mutationObserver_1.disconnect();
						node.onload = webSocketLoaded;
						return;
					}
				}
			});
		});
		mutationObserver_1.observe(document.head, {
			childList: true
		});
	}
	// fix scrollText (e.g. when joining the game and receiving xp at that moment)
	window.mouseX = window.innerWidth / 2;
	window.mouseY = window.innerHeight / 2;
})(main || (main = {}));

})();