DH2 Fixed

Improve Diamond Hunt 2

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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 = {}));

})();