Airdates.tv enhancer

Control how many past weeks you want to see and many other enhancements

当前为 2018-01-16 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Airdates.tv enhancer
// @namespace   V@no
// @author      V@no
// @description Control how many past weeks you want to see and many other enhancements
// @include     http://www.airdates.tv/*
// @include     https://www.airdates.tv/*
// @include     https://disqus.com/embed/comments/*
// @icon        data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAAEiElEQVRYw+2VW4hVVRjHf+uyz97nnLkcHWfGuWYzDlKUhUrUgynkQxREFJFEDwbzFkXQg0I9hIRF9BDRk1AyigkhouJDBuEEPlcQNdqo1cnRzozpzDnOuezL+no4czVnqIfBl/m/7L2+tde3/vv7/uu/YBWruMdQCweXRkff2Ltv36elUun3MAzFswnabiCq5qiGIX1NIT3uD8TzEJXDmo2kMbAOLvddoTg5RXNDI1YPEIce63vuo1fyrPcm0RmfX0cr97/wylvv9PX3HJjd0y4kkP8zv+nMmTPs379/wyObN3PzVhbPOwDuCF0JHPd38YF7n6zVoL7G6PeYXgebLz7MwTcPEmoIbUwUDpIOrjCQUbyrPuQrPUhLWKWv6xAvvjzauXDPRQQ8L0U6SLPrqV1s2bplJrphbv43OoEnmAbg5lx8ujVkW+nxBZkEShEAEWsp0kYRaG3dSENjsqgF+s6OCI4oDgGo1cA5mZuNFndsPkl89zhAgpn/QZP8e+3ioSBOEGFFIFLPvyQBESGKImSFGLjE4RK3XAUgTpKVI+ASXLKMBhQgzq0YgThOcLJcC2YrwMoQiFxM7OKlCcyqT5z7z0n/D1yckMTLtGD2v1fqFERxjHPLiHC29yulgcQlJHeIcJETzm5sbf3p+4sTqKW0oZYjPD8XhjFRsowG7l6B+Xe1hBMqdWd8fo1esKZSrRLFiwksqoCZcc3duzWtbVCtQHNTlkwWrgjsKWeRqfo3xwPDS00Q3IDCQxkeKIPT4BzktgXYXyAPfPSZZeh1iBLY97ZiZMQtTSAM6xfI2NgPjI3lAB+4ARgwlhGmILmEQ0BfpjloobdsqepmLiQ/gWhIhFxvO6nSbcbLFcKWv4ECnlEYc51ytW9pAr6fVu1tnQTpvWSzoJQQR60kbhOec5w332L0lyilyakcvXQhHjRGf/FYzw6sZ/FTPrq7B2+gn3QqxeFPjnLo4yGMMdRqNZ57/lX/rgREZODw0NBgc64B319DHIVESUzKC3BRREiCHwd0JB2AQitNiSLaGCpGoVvWzOnRK04ixVukrYfVlkqlgmiFiHDhwsigiBxVSg3PERCR7PC5c+dPnT6dEhHa2zsoThVRClK+T6VSwRiDUgqjTf1WAxJxWAXKOZwDqzVaa5qbmylXyigB63mEYUhDJsP18QInTp4kt3btKRF5UCk1pkRkIJ/Pf372m7PbNYrJyUmqtSrWWJwTtNaghFq1hvU80kGKWhSThDFWK0ARR1VwjiCVwg8CfC/AGEu2sZHYJXjaooW6B/iWQqHAkzt3/vzo1i171HfDw18cO3bstfGJCYLAB6kfq7ppODzPm7mmQ5I4RmlDd1cHjQ0NlCshTamA2AiV8m0mCuOgFJ71SZwj5XmEtRqJc2htUAqM1oS1Globnn72mUN2YmLiyLVr1/J9/f391tpGpVUkru4FSlG3TgFtNL6f4urVcX788Xs6OteTL0zT5sXcLJUJgR3bt+OcULpd8tpza4rdPd3XnYjVWiMzfqGVIpPJpC5dvOiM0SdYxSruNf4Bbv4W546hynoAAAAASUVORK5CYII=
// @license     MIT
// @version     1.27
// @run-at      document-start
// @grant       none
// ==/UserScript==

//tab = 2 spaces


let log = console.log;

let adeName = "Airdates.tv enhancer",
		adeVersion = "n/a",
		force = false,
		enginesDefault = [],
		_engines = [],
		_hidden = ls("hidden") || [],
		loo = 1000,
		_enginesList = [];


try
{
	adeName = GM_info.script.name;
	adeVersion = GM_info.script.version;
}catch(e){};
function ls(id, data)
{
	let r;
	if (typeof(data) == "undefined")
	{
		r = localStorage.getItem(id);
		try
		{
			r = JSON.parse(r);
		}catch(e){}
	}
	else
	{
		try
		{
			r = localStorage.setItem(id, JSON.stringify(data));
		}
		catch(e){log(e);}
	}
	return r;
}
function cs(id, data)
{
	let r;
	if (typeof(data) == "undefined")
	{
		r = readCookie(id);
		try
		{
			r = JSON.parse(r);
		}catch(e){}
	}
	else
	{
		try
		{
			r = createCookie(id, JSON.stringify(data));
		}
		catch(e){}
	}
	return r;
}
cs.list = ["s","n"];
cs.listLegacy = {
	sh: "showHidden",
	sn: "showNew",
	sr: "showReturn",
	cm: "collapseMulti",
	w: "weeks",
	wa: "enableWatched",
	middleClick: "middleClick"
};
function multiline(func)
{
	func = func.toString();
	return func.slice(func.indexOf("/*") + 2, func.lastIndexOf("*/")).split("*//*").join("*/").replace(/[\n\t]*/g, "");
}

let enginesHide = ls("enginesHide") || [];
let Settings = {
	SORT_NAME: 0,
	SORT_COLOR: 1,
	inited: false,
	box: null,
	prefs: {},
	prefsDef: {
		enableWatched: 0,
		shortTitle: 0,
		shortTitleExpand: 1,
//		animateExpand: 0,
		showHidden: 0,
		collapseMulti: 0,
		showNew: 0,
		showReturn: 0,
		middleClick: [],
		weeks: 0,
		sortBy: 0,
	},
	pref: function(id, val)
	{
		if (typeof(val) == "undefined")
			return typeof(this.prefs[id]) == "undefined" ? null : this.prefs[id];

		this.prefs[id] = val;
		this.save();
	},
	init: function()
	{
		if (this.inited)
			return;

		this.prefs = ls("settings") || this.prefsDef;
		for(i in this.prefsDef)
		{
			if (!(i in this.prefs))
				this.prefs[i] = this.prefsDef[i];
		}
		let c = cs("wa"),
				s = false;

		if (c !== null)
		{
			this.prefs.enableWatched = c ? 1 : 0;
			eraseCookie("wa");
			s = true;
		}
		c = cs("showhidden");
		if (c !== null)
		{
			this.prefs.showHidden = c ? 1 : 0;
			eraseCookie("showhidden");
			s = true;
		}
		c = cs("sh");
		if (c !== null)
		{
			this.prefs.showHidden = c ? 1 : 0;
			eraseCookie("sh");
			s = true;
		}

		c = cs("cm");
		if (c !== null)
		{
			this.prefs.collapseMulti = c ? 1 : 0;
			eraseCookie("cm");
			s = true;
		}

		c = cs("middleClick");
		if (c !== null)
		{
			this.prefs.middleClick = c ? c : [];
			eraseCookie("middleClick");
			s = true;
		}

		//splitting New/Returning into two separate filters
		c = cs("sn");
		if (c === null && this.pref("showNew") === null)
		{
			let n = cs("n") ? 1 : 0;
			this.prefs.showNew = n;
			this.prefs.showReturn = n;
		}
		else if (c !== null)
		{
			this.prefs.showNew = c;
			eraseCookie("sn");
			s = true;
		}

		c = cs("sr");
		if (c !== null)
		{
			this.prefs.showReturn = c;
			eraseCookie("sr");
			s = true;
		}

		c = cs("w");
		if (c !== null)
		{
			this.prefs.weeks = c;
			eraseCookie("w");
			s = true;
		}

		if (s)
			this.save();

		this.create();
	},
	create: function(callback)
	{
		if (Settings.box)
			return callback ? callback() : true;

		let html = multiline(function(){/*
<div id="settings-popup">
	<div id="settings-popup-content">
		<div class="header">
			<div class="back">
				<svg viewBox="0 0 24 24">
					<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" />
				</svg>
			</div>
			<h4>Options</h4>
			<div class="close">
				<svg viewBox="0 0 24 24">
					<path d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z"></path>
				</svg>
				<svg viewBox="0 0 24 24">
					<path d="M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M19,19H5V5H19V19M17,8.4L13.4,12L17,15.6L15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4Z"></path>
				</svg>
			</div>
		</div>
		<div class="content">
		</div>
	</div>
</div>
		*/});//html

		let popup = $(html).appendTo("body"),
				content = popup.find(".content"),
				a = document.createElement("a"),
				i = document.createElement("span"),
				span = document.createElement("div");

		content.toggleClass("settings", true);
		span.appendChild(i);
		span.appendChild(a);

		Settings.box = popup;
		popup.find(".back").click(function()
		{
			Settings.hide();
			setTimeout(function()
			{
				$("#account-overview").click();
			});
		});
		content.append(createCheckbox("enableWatched", "Enable watched", this.prefs.enableWatched ? true : false, this.callback, null, "pointer"));
		let opt = createCheckbox("shortTitle", "Truncate long titles", this.prefs.shortTitle ? true : false, this.callback, null, "pointer");
		opt.title = "Shorten titles to fit into single row.";
		content.append(opt);
		opt = createCheckbox("shortTitleExpand", "Auto expand truncated titles", this.prefs.shortTitleExpand ? true : false, this.callback, null, "pointer");
		opt.title = "Show full title when cursor over it. If disabled you still be able see full title in tooltip or when show is opened.";
		content.append(opt);
//		content.append(createCheckbox("animateExpand", "Animate during expanding", this.prefs.animateExpand ? true : false, this.callback, null, "pointer"));
		content.append('<div class="spacer"/>');

		a.href = "#";
		i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M12,3A9,9 0 0,0 3,12H0L4,16L8,12H5A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19C10.5,19 9.09,18.5 7.94,17.7L6.5,19.14C8.04,20.3 9.94,21 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3M14,12A2,2 0 0,0 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12Z" /></svg>';
		a.textContent = "Backup settings";
		span.title = "Backup all settings that also include links settings, watched and hidden shows lists, middle click selection.";
		let backup = function()
		{
			let cookies = {},
					str = "",
					obj = {
						version: adeVersion,
						settings: Settings.prefs
					};

			for(let i = 0; i < cs.list.length; i++)
			{
				let v = cs(cs.list[i]);
				if (v !== null)
				{
					cookies[cs.list[i]] = v;
				}
			}
			for(let i in cookies)
			{
				obj.cookies = cookies;
				break;
			}
			for(let i in _hidden)
			{
				obj.hidden = _hidden;
				break;
			}
			for(let i in watched._list)
			{
				obj.watched = watched._list;
				break;
			}
			for(let i in customLinks._list)
			{
				obj.customLinks = customLinks._list;
				break;
			}
			for(let i in enginesHide)
			{
				obj.enginesHide = enginesHide;
				break;
			}
			for(let i in enginesSort._list)
			{
				obj.enginesSort = enginesSort._list;
				break;
			}
			str = JSON.stringify(obj);

			return str;
		}
		a.addEventListener("click", function(e)
		{
			e.preventDefault();

			let str = backup();
			if (str)
					prompt( adeName + " Settings \nYou can save it in a normal textfile and/or restore it to another computer/browser.", str );
			else
					alert("Nothing to backup");
			return false;
		}, false);
		let d = a.cloneNode(true);


		d.addEventListener("click", function(e)
		{
			if (e.isTrigger)
				return;

			e.preventDefault();
			e.stopPropagation();

			let d = new Date(),
					date = d.getFullYear() + pad(d.getMonth()+1) + pad(d.getDate()) + pad(d.getHours()) + pad(d.getMinutes()) + pad(d.getSeconds());

			fileSave("Airdates.tv_enhancer_v" + adeVersion + "_settings_" + date + ".json", backup());
		}, false);
		d.textContent = "save to file";
		d.className = "file";
		span.appendChild(d);
		content.append(span);

		span = span.cloneNode(true);
		span.title = "Note, your watched and hidden shows list will stay intact, new shows will be added to it. Custom links will only overwrite existing with matched ID. Everything else will be overwritten";
		i = span.firstChild;
		a = i.nextSibling;
		d = span.lastChild;
		i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M13,3A9,9 0 0,0 4,12H1L4.89,15.89L4.96,16.03L9,12H6A7,7 0 0,1 13,5A7,7 0 0,1 20,12A7,7 0 0,1 13,19C11.07,19 9.32,18.21 8.06,16.94L6.64,18.36C8.27,20 10.5,21 13,21A9,9 0 0,0 22,12A9,9 0 0,0 13,3M12,8V13L16.28,15.54L17,14.33L13.5,12.25V8H12Z" /></svg>';
		a.textContent = "Restore settings";
		function restore(str)
		{
			let reload = false,
					txt = "Error restoring settings";
			if(!str)
				return;

			let hiddenNum = 0,
					watchedNum = 0,
					settingsNum = 0,
					enginesNum = 0,
					engineHideNum = 0;

			let json = null;
			try
			{
				json = JSON.parse(str);
			}
			catch(e){}
			if (json)
			{
				if ("hidden" in json)
				{
					hiddenNum = json.hidden.length;
					for(let i = 0; i < hiddenNum; i++)
						showHide(parseInt(json.hidden[i]), 1);
				}
				if ("watched" in json)
				{
					for(let id in json.watched)
					{
						for(let i = 0; i < json.watched[id].length; i++)
						{
							let ep = json.watched[id][i];
							watched.add(id, ep);
							watchedNum++;
							$('div.entry[data-series-id="' + id + '"]').each(function(n, entry)
							{
								if (watched.title(entry) == ep)
									watched.update(entry, true);
							});
						}
					}
					watched.save(true);
				}
				if (!json.cookies)
					json.cookies = {};

				if ("settings" in json)
				{
					let save = false;
					for(let i in json.settings)
					{
						let val = json.settings[i];
						if (i in cs.listLegacy && !(i in Settings.prefsDef))
						{
							val = json.settings[i];
							i = cs.listLegacy[i];
						}

						if (!(i in Settings.prefsDef))
						{
							if (!(i in json.cookies) && cs.list.indexOf(i) != -1)
								json.cookies[i] = val;

							continue;
						}

						if (!command(i, val))
						{
							settingsNum++;
							Settings.prefs[i] = val;
							reload = true;
							save = true;
						}
					}
					if (save)
						Settings.save();
				}

				if ("cookies" in json)
				{
					for(let i in json.cookies)
					{
						if (cs.list.indexOf(i) == -1)
							continue;

						if (!command(i, json.cookies[i]))
						{
							settingsNum++;
							cs(i, json.cookies[i]);
							reload = true;
						}
					}
				}
				let enginesNew = [];
				if ("customLinks" in json)
				{
					let changed = false;
					for (i in json.customLinks)
					{
						enginesNum++;
						changed = reload = true;
						customLinks._list[i] = json.customLinks[i];
						enginesNew.push(json.customLinks[i]);
					}
					if (changed)
						ls("customLinks", customLinks._list);

				}
				if ("enginesHide" in json)
				{
					let changed = false;
					let list = [];
					for(let i = 0; i < json.enginesHide.length; i++)
					{
						if (enginesFind(json.enginesHide[i]) != -1 || enginesFind(json.enginesHide[i], enginesNew) != -1)
						{
							list[list.length] = json.enginesHide[i];
							changed = reload = true;
							engineHideNum++;
						}
					}
					enginesHide = list;
					if (changed)
						ls("enginesHide", enginesHide);
				}
				if ("enginesSort" in json)
				{
					let changed = false;
					enginesSort._list = [];
					for(let i = 0; i < json.enginesSort.length; i++)
					{
						if (enginesFind(json.enginesSort[i]) != -1 || enginesFind(json.enginesSort[i], enginesNew) != -1)
						{
							changed = reload = true;
							enginesSort._list.push(json.enginesSort[i]);
						}
					}
					if (changed)
						ls("enginesSort", enginesSort._list);
				}
				txt = settingsNum + " setting" + (settingsNum > 1 ? "s" : "") + " imported" + ((hiddenNum || watchedNum) ? " and marked " : "");
				if (hiddenNum)
					txt += hiddenNum + " show" + (hiddenNum > 1 ? "s" : "") + " as hidden";
				if (watchedNum)
					txt += (hiddenNum ? ", " : " ") + watchedNum + " as watched";
			}
			alert(txt);
			if (reload)
				window.location.reload();
		}
		a.addEventListener("click", function(e)
		{
			e.preventDefault();
			restore(prompt( "Please enter the " + adeName + " settings text" ));
		}, false);

		d.addEventListener("click", function(e)
		{
			e.preventDefault();
			e.stopPropagation();
			fileLoad(function(text)
			{
				restore(text);
			});
		}, false);
		d.textContent = "load from file";
		span.appendChild(d);
		content.append(span);


	},//Settings.create()

	callback: function(e, id, check)
	{
		Settings.prefs[id] = check ? 1 : 0;
		Settings.save();
	},

	save: function(f)
	{
		ls("settings", this.prefs);
	},

	show: function()
	{
		Settings.box.show();
	},

	hide: function()
	{
		Settings.box.hide();
	},
}//Settings

function fileLoad(callback, ext)
{
	let f = document.createElement("input");
	f.type = "file";
	if (typeof(ext) == "undefined")
		ext = ".json";

	if (ext)
		f.setAttribute("accept", ext);

	function readFile(e)
	{
		f.removeEventListener("change", readFile, false);
		let files = f.files;
		if (!f.files.length)
		{
			alert('Please select a file!');
			return;
		}
		let reader = new FileReader();

		reader.onloadend = function(evt)
		{
			if (evt.target.readyState == FileReader.DONE)
			{
				callback(evt.target.result);
			}
		};

		let blob = f.files[0].slice(0, f.files[0].size);
		reader.readAsBinaryString(blob);
	}
	f.addEventListener("change", readFile, false);
	f.click();
}

function fileSave(name, data, type)
{
	type = type ? type : 'text/json';
	let blob = new Blob([data], {type: type}),
			a = document.createElement("a"),
			d = new Date(),
			date = d.getFullYear() + pad(d.getMonth()+1) + pad(d.getDate()) + pad(d.getHours()) + pad(d.getMinutes()) + pad(d.getSeconds());

	a.download = name;
	a.href = window.URL.createObjectURL(blob);
	a.dataset.downloadurl = [type, a.download, a.href].join(':');
	document.body.appendChild(a);
	a.click();
	a.parentNode.removeChild(a);
}

function exportGetColors()
{
	let str = $.map(DB.savedColors,function(e,i){return i + "=" + e;}).join(";");
	return str.replace(/;?[0-9]+=#FFFFFF/i, "");
}

function pad(t)
{
	return "" + t < 10 ? "0" + t : t;
}

function enginesBackup()
{
	if (!enginesBackup.backup)
		enginesBackup.backup = window.engines;
}

function enginesRestore()
{
	if (enginesBackup.backup)
	{
		window.engines = enginesBackup.backup;
		enginesBackup.backup = null
	}
}

function enginesAdd(id)
{
	for(let i = 0; i < _engines.length; i++)
	{
		if (_engines[i].name == id || _engines[i].host == id)
			return;
	}
	for(let i = 0; i < engines.length; i++)
	{
		if (engines[i].name == id || engines[i].host == id)
		{
			_enginesList.push(engines[i].host);
			_engines.push(engines[i]);
			Settings.pref("middleClick", _enginesList);
			return;
		}
	}
};

function enginesRemove(id)
{
	for(let i = 0; i < _engines.length; i++)
	{
		if (_engines[i].name == id || _engines[i].host == id)
		{
			let n = _enginesList.indexOf(_engines[i].host);
			if (n != -1)
				_enginesList.splice(n, 1);

			Settings.pref("middleClick", _enginesList);
			_engines.splice(i, 1);
			return;
		}
	}
};

function enginesCheck(id)
{
	for(let i = 0; i < _engines.length; i++)
	{
		if (_engines[i].name == id || _engines[i].host == id)
			return i;
	}
	return -1;
};

function enginesFind(id, obj)
{
	obj = typeof(obj) == "undefined" ? window.engines : obj;
	for(let i = 0; i < obj.length; i++)
	{
		if (obj[i].name == id || obj[i].host == id)
			return i;
	}
	return -1;
};

function enginesSort(list, save)
{
	if (typeof(list) == "undefined")
	{
		list = ls("enginesSort") || [];
		save = true;
	}

	let newList = [],
			newEnginesList = [];

	for(let i = 0; i < list.length; i++)
	{
		let index = enginesFind(list[i]);
		if (index == -1 || newList.indexOf(window.engines[index].host) != -1)
			continue;

		newEnginesList[newEnginesList.length] = window.engines[index];
		newList[newList.length] = window.engines[index].host;
	}
	for(let i = 0; i < window.engines.length; i++)
	{
		if (newList.indexOf(window.engines[i].host) == -1)
		{
			newEnginesList[newEnginesList.length] = window.engines[i];
			newList[newList.length] = window.engines[i].host;
		}
	}
	for(let i in customLinks._list)
	{
		if (newList.indexOf(i) == -1)
		{
			newEnginesList[newEnginesList.length] = customLinks._list[i];
			newList[newList.length] = i;
		}
	}
	if (save)
		enginesSort._list = newList;

	window.engines = newEnginesList;
}
enginesSort.changed = function()
{
	let list = [];
	for (let i = 0; i < enginesDefault.length; i++)
	{
		list[list.length] = enginesDefault[i].host;
	}
	for (i in customLinks._list)
	{
		if (list.indexOf(i) == -1)
			list[list.length] = i;
	}
	return !isEqual(list, enginesSort._list);
}

function isEqual (a, b)
{
	for(let i in a)
	{
		if (!(i in b) || a[i] != b[i])
			return false
	}
	for(let i in b)
	{
		if (!(i in a) || a[i] != b[i])
			return false
	}
	return true;
}

function cleanName(id)
{
	return id.replace(/[^a-zA-Z0-9-_]/g, "_");
}

function getHost(url)
{
	let a = document.createElement('a');
	a.href = url;
	return a.hostname;
}
function engineFixHost(engine)
{
	if (engine.host)
		return;

	engine.host = getHost(engine.href);
}

function showHide(id, t)
{
	let hidden = _hidden.indexOf(id);
	if (typeof(t) == "undefined")
		return hidden != -1;

	if (t == 2)
		t = hidden == -1 ? 1 : 0;

	switch(t)
	{
		case 0:
			_hidden.splice(hidden, 1);
			if (!DB.savedColors[id])
				DB.infoRemove(id);

			hidden = false;
			break;
		case 1:
			if (hidden == -1)
			{
				_hidden.push(id);
				DB.infoAdd(id);
			}

			hidden = true;
			break;
	}
	let css = $("#css" + id);
	if (hidden)
	{
 		if (!css.length)
 		{
			let style = "<style id='css" + id + "'>"+
									"div.calendar:not(.showHidden).activeOnly .entry[data-series-id='" + id + "'][opened]:not(.multi){ display: block !important; }"+
									"div.calendar:not(.showHidden) .entry[data-series-id='" + id + "']:not([opened]):not(.searchResult){ display: none !important; }"+
									".entry[data-series-id='" + id + "'] .title{ font-style: italic; }"+
									"div:not(#searchResults) > .entry[data-series-id='" + id + "']:not([opened]):not(.searchResult){ opacity: 0.3;}"+
									".entry[data-series-id='" + id + "'] .showhide0{ display: inline; }"+
									".entry[data-series-id='" + id + "'] .showhide1{ display: none; }"+
									"</style>";
			$(style).appendTo("body");
		}
	}
	else
		css.remove();

	ls("hidden", _hidden);
	return hidden;
}


function command(id, val)
{
	if (!command.list[id])
		return false;

	try
	{
		command.list[id].func({preventDefault:function(){},stopPropagation:function(){}}, val);
	}
	catch(e)
	{
		return false;
	}
	return true;
}
command.list = {};
command.add = function(id, objId, func)
{
	command.list[id] = {
		id: id,
		objId: objId,
		func: func
	};
};

//watched checkbox
function watched(entry)
{
	if (entry._input.checked)
		watched.add(entry.getAttribute("data-series-id"), watched.title(entry));
	else
		watched.remove(entry.getAttribute("data-series-id"), watched.title(entry));

	watched.update(entry, entry._input.checked);
}
watched._list = ls("watched") || {};
watched._saving = false;
watched.add = function(id, episode)
{
	if (!watched._list[id])
		watched._list[id] = [];

	if (watched._list[id].indexOf(episode) == -1)
		watched._list[id].push(episode);

	watched.save();
};

watched.remove = function(id, episode)
{
	if (!watched._list[id])
		return;

	let n = watched._list[id].indexOf(episode);
	if (n == -1)
		return;

	watched._list[id].splice(n, 1);
	if (!watched._list[id].length)
		delete watched._list[id];

	watched.save();
};

watched.save = function(f)
{
	if (watched._saving && !f)
		return;

	function func()
	{
		watched._saving = false;
		ls("watched", watched._list);
	}
	clearTimeout(watched._saving);
	if (f)
		func();
	else
		watched._saving = setTimeout(func, 500);
};

watched.has = function(entry)
{
	let id = entry.getAttribute("data-series-id");
	return watched._list[id] && watched._list[id].indexOf(watched.title(entry)) != -1;
};

watched.title = function(entry)
{
	let txt = entry._title && entry._title._titleOrig ? entry._title._titleOrig : $(entry).find("div.title").text();
	return txt.substring(txt.lastIndexOf(" ") + 1).replace(/\s+$/g, "");
};

watched.update = function(entry, enable)
{
	let text = "Watched";
	if (enable)
	{
		entry.setAttribute("watched", "");
	}
	else
	{
		text = "Not watched";
		entry.removeAttribute("watched");
	}
	entry.title = $(entry).find(".title > span")[0].lastChild.textContent + " (" + text + ")";
	$(entry).find(".title > input").prop("title", text);
	entry._input.checked = enable;

};

watched.attach = function(i,entry)
{
	if (entry._input)
		return;

	let input = document.createElement("input");
	input.type = "checkbox";
	input.checked = watched.has(entry);
	entry._input = input;

	input.addEventListener("change", function(e)
	{
		let title = watched.title(entry);
		$('div.entry[data-series-id="' + entry.getAttribute("data-series-id") + '"]').each(function(i, entry)
		{
			if (watched.title(entry) == title)
				watched.update(entry, input.checked);
		});
		watched(entry);
	}, false);
	let title = $(entry).find(".title")[0],
			text = title.innerHTML;

	title.innerHTML = "";
	title.appendChild(input);
	$(title).append("<span>" + text.trim() + "</span>");
	watched.update(entry, input.checked);
};


//custom links
function customLinks(obj)
{

}
customLinks._list = ls("customLinks") || {};
customLinks.show = function()
{
	$(customLinks.div).show();
}
customLinks.hide = function()
{
	$(customLinks.div).hide();
}


customLinks.manager = function customLinksManager(callback)
{
	if (customLinks.div)
		return callback ? callback() : true;

	let html = multiline(function(){/*
<div id="manage-links-popup">
	<div id="manage-links-popup-content">
		<div class="header">
			<div class="back">
				<svg viewBox="0 0 24 24">
					<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" />
				</svg>
			</div>
			<h4>Links Manager</h4>
			<div class="close">
				<svg viewBox="0 0 24 24">
					<path d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z"></path>
				</svg>
				<svg viewBox="0 0 24 24">
					<path d="M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M19,19H5V5H19V19M17,8.4L13.4,12L17,15.6L15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4Z"></path>
				</svg>
			</div>
		</div>
		<div class="content"></div>
	</div>
</div>
		*/});//html

	let popup = $(html).appendTo("body");
	customLinks.div = popup[0];
	popup.find(".back").click(function()
	{
		customLinks.hide();
		setTimeout(function()
		{
			$("#account-overview").click();
		});
	});
	$(popup).find("[id^=account]").each(function()
	{
		this.id = this.id.replace("account", "manage-links");
	});

	let	content = $(popup).find(".content").html("");
	html = multiline(function(){/*
<div class="reset">
	<a id="sort-reset" href="#">reset sort</a>
</div>
<form>
	<div id="engine-edit">
		<div>
			<label>Name:</label>
			<input id="engine-name">
		</div>
		<div>
			<label>URL:</label>
			<input id="engine-url">
			<select id="engine-tags" size="1">
				<option value=""></option>
				<option value="MONKEY_N">Name</option>
				<option value="MONKEY">Name+Episode</option>
				<option value="MONKEY_ID">ID</option>
				<option value="{WIKI_TITLE}">Wiki page</option>
				<option value="MONKEY_ARCHIVELINK">Archive link</option>
			</select>
		</div>
		<div>
			<label>ID:</label>
			<input id="engine-id" placeholder="&lt;optional&gt;">
		</div>
		<div>
			<label></label>
			<input id="engine-submit" type="button" value="Add">
			<input id="engine-reset" type="reset" value="Reset">
		</div>
		<div>
			<label>Result:</label>
			<div id="engine-res"></div>
		</div>
	</div>
</form>
	*/});//html

	$(html).appendTo(content.parent());
	let engId = $("#engine-id"),
			engUrl = $("#engine-url"),
			engName = $("#engine-name"),
			engSubmit = $("#engine-submit"),
			engTags = $("#engine-tags"),
			engRes = $("#engine-res"),
			engReset = $("#engine-reset"),
			engSortReset = $("#sort-reset"),
			engResHidden = $('<div id="engine-hidden" class="entry" data-series-id="1234" data-series-source="List of Monsuno episodes" data-date="20120223"><div class="title">Monsuno S01E01</div></div>').appendTo(popup),
			prevTarget = null,
			prevVal = null;

			if (enginesSort.changed())
				popup.attr("changed", true);

	function change(e)
	{
		if (prevTarget === e.target && prevVal === e.target.value)
			return;

		clearTimeout(change.timer);
		change.timer = setTimeout(function()
		{
			prevVal = e.target.value;
			prevTarget = e.target;
			engResHidden.find(".details").remove();
			let opened = $('.details[style="display: block;"]').toggleClass("details", false);
			enginesBackup();
			let eng = [{
						name: engName.val(),
						host: engId.val(),
						href: engUrl.val()
					}];

			if (!eng[0].href.match(/[a-z]+:\/\//i))
				eng[0].href = "http://" + eng[0].href;

			engineFixHost(eng[0]);
			window.engines = eng;
			engResHidden.find(".title").trigger("click");
			opened.toggleClass("details", true);
			setTimeout(function()
			{
				let list = engResHidden.find(".engines").children(),
						a = list.filter("a")[0],
						img = list.filter("img")[0],
						children = engRes.children();

				img.src = "http://www.google.com/s2/favicons?domain=" + getHost(eng[0].href);

				if (!children.length)
				{
					engRes.append(img).append(a);
				}
				else
				{
					$(children[0]).replaceWith(img);
					$(children[1]).replaceWith(a);
				}
				if (enginesBackup)
				{
					enginesRestore();
				}
			});
		}, 300);
	}//change()
	engId.on("input change", change);
	engUrl.on("input change", change);
	engName.on("input change", change);

	engTags.on("change", function(e)
	{
		let start = engUrl[0].selectionStart,
				end = engUrl[0].selectionEnd,
				endNew = start + e.target.value.length,
				startNew = endNew,
				txt = engUrl.val();
		engUrl.val(txt.substring(0, start) + e.target.value + txt.substring(end));
		engUrl[0].selectionStart = startNew;
		engUrl[0].selectionEnd = endNew;
		engUrl.focus();
		engUrl.trigger("input");
		e.target.value = "";
	});
	engReset.click(function(e)
	{
		engRes.html("");
		prevVal = null;
	});
	function flash(obj, color)
	{
		obj.toggleClass("update", false);
		obj.css("background-color", color ? color : "#90FF90");
		setTimeout(function()
		{
			obj.toggleClass("update", true);
			obj.removeAttr("style");
		}, 100);
	}
	engSubmit.click(function(e)
	{
		let engine = {
					name: engName.val().trim(),
					href: engUrl.val().trim(),
					host: engId.val().trim()
				};
		if (!engine.href.match(/[a-z]+:\/\//i))
			engine.href = "http://" + engine.href;

//		engUrl.val(engine.href);
		engineFixHost(engine);
		let id = "engine_" + cleanName(engine.host),
				update = $("#" + id),
				exists = customLinks._list[engine.host];

		if (!engine.name || !engine.href)
			return false;

		let equal = false,
				n = -1;
		for(let i = 0; i < window.engines.length; i++)
		{
			if (window.engines[i].host == engine.host)
			{
				equal = isEqual(window.engines[i], engine);
				n = i;
				break;
			}
		}
		if (equal)
		{
			flash(update);
			return false;
		}

		if (!exists || update.length)
		{
			customLinks._list[engine.host] = engine;
			if (update.length)
			{
				if (n > -1)
					window.engines[n] = engine;

			}
			else
				window.engines.push(engine);

			ls("customLinks", customLinks._list);
			customLinksAddCss(id);
		}
		function updater(update)
		{
			flash($(update));
			updateDetails();
		}
		if (update.length)
		{
			if (exists)
			{
				for (let i = 0; i < enginesDefault.length; i++)
				{
					if (isEqual(enginesDefault[i], engine))
					{
						update.find(".del").trigger("click");
						return;
					}
				}
			}
			update.replaceWith(create(engine, !exists ? updater : function(update)
			{
				flash($(update));
			}));
			let entry = $("div.entry").find("." + id);
			entry.filter("a.link").attr("href", engine.href).text(engine.name);
			flash(update);
		}
		else
		{
			let last = content.children(":last-child");
			content.append(create(engine, updater));
//			updateDetails();
		}

		engName.val("");
		engId.val("");
		engUrl.val("");
	});//engSubmit.click()

	function updateDetails()
	{
		let clone = engResHidden.clone();
		clone.find(".details").remove();
		$("body").append(clone);
		let opened = $('.details[style="display: block;"]').toggleClass("details", false);
		force = true;
		clone.find(".title").trigger("click");
		opened.toggleClass("details", true);
		setTimeout(function()
		{
			$('div:not(#engine-hidden) .details[style="display: block;"]').find(".engines").replaceWith(clone.find(".engines"));
			clone.remove();
		}, 300);
	}
	engSortReset.click(function(e)
	{
		e.stopPropagation();
		e.preventDefault();
		let host = engId.val(),
				name = engName.val(),
				href = engUrl.val(),
				list = [];

		for (let i = 0; i < enginesDefault.length; i++)
		{
			list[list.length] = enginesDefault[i].host;
		}
		for (i in customLinks._list)
		{
			list[list.length] = customLinks._list[i].host;
		}
		enginesSort(list, true);
		ls("enginesSort", []);
		updateDetails();
		$(customLinks.div).remove();
		customLinks.div = null;
		popup.removeAttr("changed");
		customLinks.manager(function()
		{
			if (host || name || href)
			{
				$("#engine-id").val(host);
				$("#engine-name").val(name);
				$("#engine-url").val(href);
				$("#engine-url").trigger("input");
			}
		});
		customLinks.show();
	});
	let dragged = null;

	function dndEvent(e)
	{
		let r = null;
		switch(e.type)
		{
			case "mousedown":
				if (typeof(e.target.className) != "string" || e.target.className.indexOf("dndh") != -1)
					dragged = e.target.parentNode;
				break;
			case "dragstart":
				if (!dragged)
					return;

				e.dataTransfer.effectAllowed = 'move';
				dragged = this;
				dragged.classList.add("dragging");
				this.parentNode.classList.add("dragging");
				setTimeout(function()
				{
					dragged.classList.add("hide");
				});
				e.dataTransfer.setData('application/x-moz-node', dragged);
				break;
			case "dragenter":
				if (!dragged)
					return;

				let node = this,
						up = (node.nextSibling == dragged),
						moveold = up ? node : node.nextSibling,
/*
						anim1 = up ? "up" : "down",
						anim2 = up ? "down" : "up",
*/
						movenew = dragged;
/*
				movenew.classList.add(anim1);
				moveold.classList.add(anim2);
				movenew.classList.remove(anim2);
				moveold.classList.remove(anim1);
//				$(dragged).toggleClass(anim1, true);
//				$(moveto).toggleClass(anim2, true);
//				clearTimeout(dragged.__timer);
//				clearTimeout(moveto.__timer);
				movenew.__timer = setTimeout(function()
				{
					movenew.classList.remove(anim1);
//					$(dragged).toggleClass(anim1, false);
				}, 1000);
				moveold.__timer = setTimeout(function()
				{
					moveold.classList.remove(anim2);
//					$(moveto).toggleClass(anim2, false);
				}, 1000);
*/
				node.parentNode.insertBefore(movenew, moveold);
				break;
			case "dragover":
				if (!dragged)
					return;

				e.dataTransfer.dropEffect = 'move';
				if (e.stopPropagation)
					e.stopPropagation();

				if (e.preventDefault)
						e.preventDefault(); // Necessary. Allows us to drop.

				r = false;
				break;
			case "dragleave":
				break;
			case "dragend":
				if (!dragged)
					return;

				dragged.classList.remove("dragging");
				dragged.classList.remove("hide");
				let div = document.querySelectorAll('#manage-links-popup-content .content [draggable]'),
						list = [];
				[].forEach.call(div, function (col)
				{
					list[list.length] = col._engine.host;
				});
				dragged.parentNode.classList.remove("dragging");
				enginesSort(list, true);
				ls("enginesSort", list);
				if (enginesSort.changed())
					popup.attr("changed", true);
				else
					popup.removeAttr("changed");

				updateDetails();
				dragged = false;
				break;
			case "drop":
				if (!dragged)
					return;

				if (e.stopPropagation)
					e.stopPropagation();
				if (e.preventDefault)
						e.preventDefault(); // Necessary. Allows us to drop.
				break;
		}
		if (r !== null)
			return r;
	}
	function create(engine, callback)
	{
		let div = document.createElement("div"),
				id = "engine_" + cleanName(engine.host),
				cb = createCheckbox(id, "", enginesHide.indexOf(engine.host) == -1, function(e, id, checked)
				{
					if (checked)
						enginesHide.splice(enginesHide.indexOf(engine.host), 1);
					else
						enginesHide.push(engine.host);

					ls("enginesHide", enginesHide);
			}, ["Visible", "Hidden"]),
				dndHandle = document.createElement("span");
		div._engine = engine;
		div.id = id;

		content[0].setAttribute("draggable", true);
		div.setAttribute("draggable", true);
		div.addEventListener('dragstart', dndEvent, false);
		div.addEventListener('dragenter', dndEvent, false)
		div.addEventListener('dragover', dndEvent, false);
		div.addEventListener('dragleave', dndEvent, false);
		div.addEventListener('drop', dndEvent, false);
		div.addEventListener('dragend', dndEvent, false);
		div.addEventListener("mousedown", dndEvent, false);

		dndHandle.innerText = "☰";
		dndHandle.className = "dndh";
		div.appendChild(dndHandle);
		div.appendChild(cb);
		let clone = engResHidden.clone();
		$("body").append(clone);
		clone.find(".details").remove();
		let opened = $('.details[style="display: block;"]').toggleClass("details", false);

		enginesBackup();
		window.engines = [engine];
		clone.find(".title").trigger("click");
		opened.toggleClass("details", true);
		let n = 100;
		setTimeout(function loop()
		{
//			let list = clone.find(".engines").children().filter(".engine_" + cleanName(engine.host)),
			let list = clone.find(".engines").children();
			if (!list.length && n--)
			{
				setTimeout(loop);
				return;
			}
			let	a = list.filter("a")[0],
					img = list.filter("img")[0],
					def = false;

			for (let i = 0; i < enginesDefault.length; i++)
			{
				if (enginesDefault[i].host == engine.host)
				{
					def = true;
					break;
				}
			}
			if (a && img)
				img.src = "http://www.google.com/s2/favicons?domain=" + getHost(a.href);

			$(div).append(img);
			$(div).append(a);
			if (def)
				$(div).toggleClass("def", true);

			enginesRestore();
			clone.remove();

			$('<span class="edit" title="Edit"><svg viewBox="0 0 24 24"><path d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z"></path></svg></span>').appendTo(div).click(function(e)
			{
				engId.val((getHost(engine.href) == engine.host) ? "" : engine.host);
				engName.val(engine.name);
				engUrl.val(engine.href.replace(/http:\/\//i, ""));
				engId.trigger("input");
			});
			if (customLinks._list[engine.host])
			{
	//				$('<span class="del" title="delete">&#9249;</span>').appendTo(div).click(function(e)
				$('<span class="del" title="' + (def ? "Clear" : "Delete") + '">' + '<svg viewBox="0 0 24 24"><path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path></svg>' + '</span>').appendTo(div).click(function(e)
				{
					e.stopPropagation();
					e.preventDefault();
					flash($("#" + id), e.isTrigger ? null : "#FF9090");
					setTimeout(function()
					{
						let host = engId.val(),
								name = engName.val(),
								href = engUrl.val();

						delete customLinks._list[engine.host];
						let update = false;
						for(let i = 0; i < window.engines.length; i++)
						{
							if (window.engines[i].host == engine.host)
							{
								for(let d = 0; d < enginesDefault.length; d++)
								{
									if (enginesDefault[d].host == engine.host)
									{
										update = d;
										break;
									}
								}
								if (update !== false)
									window.engines[i] = enginesDefault[update];
								else
								{
									let index = enginesSort._list.indexOf(engine.host);

									if (index != -1)
									{
										enginesSort._list.splice(i, 1);
										ls("enginesSort", enginesSort._list);
									}
									window.engines.splice(i, 1);
								}
								break;
							}
						}
						ls("customLinks", customLinks._list);
						if (update === false)
						{
							$("#" + id).remove();
							$(".engines").find("." + id).remove();
							$("." + id).toggleClass(id, false);
							$("#css_" + id).remove();
							let index = enginesHide.indexOf(engine.host);
							if (index != -1)
							{
								enginesHide.splice(index, 1);
								ls("enginesHide", enginesHide);
							}
						}
						$(customLinks.div).remove();
						customLinks.div = null;
						customLinks.manager(function()
						{
							if (update !== false)
							{
								let aNew = $("#manage-links-popup").find("#" + id).find("a.link"),
										a = $('.details[style="display: block;"]').find(".engines").find("a." + id);

								a.html(aNew.html());
								a.attr("href", aNew.attr("href"));
							}
							if (host || name || href)
							{
								$("#engine-id").val(host);
								$("#engine-name").val(name);
								$("#engine-url").val(href);
								$("#engine-url").trigger("input");
							}
						});
						customLinks.show();
					}, 200);
				});
			}
			if (callback)
				callback(div);
		});
		return div;
	}//create();
	enginesBackup();

	let eng = window.engines,
			n = eng.length;
	function finished()
	{
		if (!--n)
			callback();
	}
	for(let i = 0; i < eng.length; i++)
	{
		content.append(create(eng[i], callback ? finished : null));
	};
	setTimeout(enginesRestore,100);
}//customLinks.manager()

function customLinksAdd()
{

	let listNew = [],
			remove = [],
			list = [];
	for(let l in customLinks._list)
	{
		listNew.push(customLinks._list[l]);
	}
	let hosts = [];
	for(let n = 0; n < window.engines.length; n++)
	{
		hosts[hosts.length] = window.engines[n].host;
		enginesDefault[enginesDefault.length] = window.engines[n];
	}
	for(let i = 0; i < listNew.length; i++)
	{
		let n = hosts.indexOf(listNew[i].host);
		if (n != -1)
			window.engines[n] = listNew[i];
		else
			list.push(listNew[i]);

		customLinks._list[listNew[i].host] = listNew[i];
	}
	ls("customLinks", customLinks._list);
	window.engines = window.engines.concat(list);

	enginesSort();
	let css = [],
			css2 = [];

	for (let i = 0; i < window.engines.length; i++)
	{
		let id = "engine_" + cleanName(window.engines[i].host);
		customLinksAddCss(id);
	}
	customLinks.manager();
	$(customLinks.div).remove();
	customLinks.div = null;


	//middle click on day's title opens selected engines for user's shows
	_enginesList = Settings.pref("middleClick") || [];
	let engines = enginesBackup.backup ? enginesBackup.backup : window.engines,
			del = [],
			add = [];

	for(let i = 0; i < engines.length; i++)
	{
		let n = _enginesList.indexOf(engines[i].host)

		if (n != -1)
			_engines.push(engines[i]);

	}
	for(let i = 0; i < _enginesList.length; i++)
	{
		let found = false;
		for (let n = 0; n < engines.length; n++)
		{
			if (engines[n].name == _enginesList[i])
				_enginesList[i] = engines[n].host

			if (engines[n].host == _enginesList[i])
			{
				_engines.push(engines[n]);

				if (!i && engines[n].host == "thepiratebay.org")
				{
					//sort by date instead of seeds
					engines[n].href = engines[n].href.replace(/\.se\//, ".org").replace(/\/0\/7\/0$/, "/0/3/0");
				}

				found = true;
				break
			}
		}
		if (!found)
			del.push(_enginesList[i]);
	}
	for(let i = 0; i < del.length; i++)
	{
		_enginesList.splice(_enginesList.indexOf(del[i]), 1);
	}
	let l = _enginesList.length;
	try
	{
		_enginesList = Array.from(new Set(_enginesList));
	}catch(e){}
	if (l != _enginesList)
		Settings.pref("middleClick", _enginesList);

}//customLinksAdd()

function customLinksAddCss(id)
{
	let css = 'body:not(.' + id + ') .engines .' + id + "{display:none;}",
			css2 = 'body:not(.' + id + ') #' + id + " img";

	css2 += ',body:not(.' + id + ') #' + id + " a.link";
	css2 += "{font-style: italic; opacity: 0.5;}";
	$('<style id="css_' + id + '"></style>').html(css+css2).appendTo("head");
}


function createCheckbox(id, label, cookie, callback, title, className)
{
	let span = document.createElement("span"),
			checkon = document.createElement("span"),
			checkoff = document.createElement("span"),
			a = document.createElement("a"),
			check = typeof(cookie) == "boolean" ? cookie : cs(cookie) ? true : false;
	if (title)
	{
		checkon.title = title[0];
		checkoff.title = title[1] || title[0];
		a.title = title[2] || title[0];
	}
	a.className = (typeof(className) == "undefined" ? "filter " : className + " ") + id;
	checkon.className = "checkon nu";
	checkoff.className = "checkoff nu";
	checkon.innerHTML = "☑";
//	checkon.innerHTML = '<svg style="width:1em;height:1em;" viewBox="0 0 24 24"><path d="M19,19H5V5H15V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V11H19M7.91,10.08L6.5,11.5L11,16L21,6L19.59,4.58L11,13.17L7.91,10.08Z" /></svg>';
	checkoff.innerHTML = "☐";
//	checkoff.innerHTML = '<svg style="width:1em;height:1em;" viewBox="0 0 24 24"><path d="M19,3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3M19,5V19H5V5H19Z" /></svg>';
//	a.href = "#";
	span.appendChild(checkon);
	span.appendChild(checkoff);
	span.appendChild(document.createTextNode(label));
	a.appendChild(span);
	$(a).insertBefore("#nu-showing");
	let func = function(e, val)
	{
		e.preventDefault();
		e.stopPropagation();
		let check = span.hasAttribute("checked");
		if (val !== undefined)
			check = !val;

		if (check)
			span.removeAttribute("checked");
		else
			span.setAttribute("checked", "checked");

		$(".calendar").toggleClass(id, !check);
		$("body").toggleClass(id, !check);
		if (typeof(cookie) != "boolean")
			cs(cookie, check ? 0 : 1);

		return (typeof(callback) == "function") ? callback(e, id, !check) : e;
	};
	command.add(cookie, id, func);
	a.addEventListener("click", func, false);
	if (check)
		span.setAttribute("checked", "checked");

	$(".calendar").toggleClass(id, check);
	$("body").toggleClass(id, check);

	return a;
}

function rand(min, max)
{
	return Math.floor(Math.random() * (max - min + 1)) + min;
}

(function loop()
{
	if ((typeof(engines) == "undefined" || !engines.length) && --loo)
		return setTimeout(loop, 0);

	if (!loo)
		return;

	Settings.init();
	customLinksAdd();
})();


let func = function(event)
{
	Settings.init();

	// fix highlighting today would remove /u/user ..
	$( "#linkToday" ).click(function click(e)
	{
		e.stopPropagation();
		e.preventDefault();
		e.stopImmediatePropagation();
		loadArchiveFromPathname("#today", "#today");
		return false;  
	});

	let _today,
			showHidden = Settings.pref("showHidden") ? true : false;
			_assignColor = assignColor;

	/*
	work around for expand/collapse same series in one day
	*/
	window.assignColor = function assignColor ( seriesId, color, permanent )
	{
		let r = _assignColor(seriesId, color, permanent),
				css = $("#css_"+seriesId),
				html = css.html();

		if (html)
			css.html(html.replace(/(\s+)(\.activeOnly)([^\{]+)\{/, "$1$2$3:not(.multi),body:not(.collapseMulti) $2$3,$2 .day.expand $3,$2 .day.opened $3{"));

		return r;
	};
	/*
	fixing browser history inflating after each page refresh and prev/next history jump don't work
	*/
	var prevPath;
	//MS Edge get's /undefined in the address
	if (window.location.pathname.match(/\/undefined/))
	{
		history.go(-1);
		let path = window.location.href;
		history.go(1)
		history.replaceState({}, "", path);
		history.go(-1);
	}

	function loadArchiveFromPathname(originalPath,highlightSelector)
	{
		path = originalPath||document.location.pathname;
		var match = path.match(/^\/archive\/([0-9]+)-([0-9]+)$/);
		var ym = match && match.length == 3? (~~match[1]*12+~~match[2]-1) : ymToday;
	//start fix - browser history inflating after each page refresh
		if (originalPath && prevPath != originalPath)
		{
			prevPath = originalPath;
			history.pushState({originalPath:originalPath,highlightSelector:highlightSelector}, "", originalPath);
		}

	//end fix
		$("#archive-prev").attr("href", "/archive/" + ym2str(ym-1));
		$("#archive-next").attr("href", "/archive/" + ym2str(ym+1));
		$("body").toggleClass("archive", !!match&&match.length==3);
		$("#leaveArchive").text(ym>ymToday?"Leave future":"Leave archive");

		let whenDone = function(){
			// set title
			$("#archive-current").text((!!match&&match.length==3)?ym2str(ym):"Go waste your time");
			// highlight today with a happy yellow background
			$( "div.day[data-date='" + today.getFullYear() + pad2( today.getMonth() + 1 ) + pad2( today.getDate() ) + "']" ).addClass( "today" ).attr( "id", "today" );
			markSearchResults();

			var $hl = $(highlightSelector||".does-not-exist-and-never-will");
			if($hl.get().length>0){
				// blink
				for( var i = 0; i < 8; i++ ) $hl.animate( {"opacity":i%2}, 1 ).delay( 100 );

				// scroll to
				var val = function(){return $hl.offset().top-Math.max(document.documentElement.clientHeight, window.innerHeight || 0)/2;};
				$('html, body').animate({
							scrollTop: val()
					},{duration:500, step:function(top,info){
						info.end = val();
					}});
			}
	//injecting userscript function execution
			showPast(function()
			{
	//adding watched checkboxes
				$("div.day > div.entry").each(watched.attach);
	//collapse multiple entries of the same series in one day
				$("div.day").each(collapseMulti);
			});
			showHideLoad();
		};

		if(ymCurrent != ym)
		{
			$(".days").load("/_archive/" + ~~(ym/12) + "-" + ~~((ym%12)+1), function()
			{
				ymCurrent = ym;
				whenDone();
			});
		}
		else
		{
			whenDone();
		}
	}
	window.loadArchiveFromPathname = loadArchiveFromPathname;
	/*
	end fixing browser history inflating after each page refresh and prev/next history jump don't work
	*/

	//collapse multiple entries of the same series in one day
	let collapseMulti = function collapseMulti(i, day)
	{
		if (!$(day).find("div.entry > .title > span").length)
			return;

		if (day.list)
		{
			collapseMulti.setTitle(day.list, Settings.prefs.collapseMulti && !$(day).hasClass("opened") ? "_titleCollapsed" : "_titleOrig");
			return;
		}
		day.list = {};
		let list = {};
		day.addEventListener("mouseenter", function(e)
		{
			collapseMulti.mouseOver(e, day);
		}, true);
		day.addEventListener("mouseleave", function(e)
		{
			collapseMulti.mouseOut(e, day);
		}, true);

		$(day).find("div.entry").each(function(i, entry)
		{
			let id = entry.getAttribute("data-series-id");
			entry._title = $(entry).find(".title>span");
			entry._title._titleOrig = entry._title.text();
			if (list[id])
				$(entry).toggleClass("multi", true);
			else
			{
				list[id] = [];
			}

			list[id].push(entry);
		});
		for(let i in list)
		{
			if (list[i].length < 2)
				continue;

			list[i][0]._title._titleCollapsed = list[i][0]._title.text() + "-" + list[i][list[i].length - 1]._title.text().replace(/.* ([^ ]+)$/, "$1");
			$(list[i][0]).toggleClass("multif", true);

			day.list[i] = list[i][0]._title;
			if (Settings.prefs.collapseMulti)
				$(day.list[i]).html(day.list[i]._titleCollapsed);
		}
	};


	collapseMulti.setTitle = function(list, title)
	{
		for (let i in list)
			$(list[i]).html(list[i][title]);
	};

	collapseMulti.mouseOver = function(e, day)
	{
		if (e.target != day)
			return;

		day._type = "over";
		clearTimeout(day._collapseMultiTimer);
		if (Settings.prefs.animateExpand)
		{
			let func = function()
			{
				collapseMulti.animate(day, "expand", true, Settings.prefs.collapseMulti ? "_titleOrig" : "");
			};
			if (!$(day).hasClass("expand") && !$(day).hasClass("opened"))
			{
				func();
			}
			else
			{
				$(day).toggleClass("expand", true);
				if (!Settings.prefs.collapseMulti)
					return;

				collapseMulti.setTitle(day.list, "_titleOrig");
			}
		}
		else
		{
			$(day).toggleClass("expand", true);
			if (!Settings.prefs.collapseMulti)
				return;

			collapseMulti.setTitle(day.list, "_titleOrig");
		}
	};

	collapseMulti.mouseOut = function(e, day, id)
	{
		if (e.target != day)
			return;

		day._type = "out";
		day._collapseMultiTimer = setTimeout(function()
		{
			if (Settings.prefs.animateExpand)
			{
				collapseMulti.animate(day, "expand", false, Settings.prefs.collapseMulti && !$(day).hasClass("opened") ? "_titleCollapsed" : "")
			}
			else
			{
				$(day).toggleClass("expand", false);
				if (!Settings.prefs.collapseMulti)
					return;

				if (!$(day).hasClass("opened"))
					collapseMulti.setTitle(day.list, "_titleCollapsed");
			}
		}, 300)
	};

	collapseMulti.animateDone = function(e, n)
	{
		e.elem.style.overflow = "";
		e.elem.style.height = "";
		e.elem.style.display = "";
		e.elem.style.textOverflow = "";
		$(e.elem).find(".title").css("white-space", "");
	};
	collapseMulti.animate = function(day, cl, val, title)
	{
		if (!day._animatingQuie)
			day._animatingQuie = {};

		if (day._animating)
		{
			clearTimeout(day._animatingQuie[cl+val]);
			return day._animatingQuie[cl+val] = setTimeout(function()
			{
				collapseMulti.animate(day, cl, val, title);
			});
		}

		let type = day._type;
		if (Settings.prefs.collapseMulti)
			clearTimeout(day._collapseMultiTimer);

		let clone = day.cloneNode(true);
		$(clone).toggleClass(cl, val);
		if (title)
		{
			clone.list = {};
			for (let i in day.list)
			{
				clone.list[i] = $($(clone).find(".multif[data-series-id=" + i + "]")[0]);
				clone.list[i]._titleCollapsed = day.list[i]._titleCollapsed;
				clone.list[i]._titleOrig = day.list[i]._titleOrig;
			}
			collapseMulti.setTitle(clone.list, title);
		}
		clone.style.visibility = "hidden";
		clone.style.position = "absolute";
		clone.style.top = 0;
		clone.style.width = day.clientWidth + "px";
		$(clone).toggleClass("clone", true);
		day.parentNode.appendChild(clone);
		let n = 0;
		collapseMulti.setTitle(day.list, title);
		let stopped = false;
		day._animating = true;
		$(clone).find(".entry").each(function(i, c)
		{
			n++;
			let e = $(day).find(".entry")[i];
			e.style.height = (e.clientHeight ? e.clientHeight : 1) + "px";
			e.style.overflow = "hidden";
			e.style.textOverflow = "unset";
			e.style.display = "block !important";
			$(e).find(".title").css("white-space", "normal");
			$(e).show();
			$(e).css("display", "block !important");
			if (!$(e).is( ":visible" ))
			{
				e.style.setProperty("display", $(c).css("display"), "important");
			}
			$(e).stop(true);
			$(e).animate({
				height: c.clientHeight,
			},
			{
				duration: 200,
				progress: function(e)
				{
					if (day._type != type)
					{
						e.stop(true);
						stopped = true;
					}
				},
				always: function(e)
				{
					if (!--n)
					{
						if (!stopped)
							$(day).toggleClass(cl, val);

						day._animating = false;
					}
					collapseMulti.animateDone(e);
				}
			});
		});
	};

	collapseMulti.onOff = function(e, id, checked)
	{
		Settings.prefs.collapseMulti = checked;
		$("div.day").each(collapseMulti);
		Settings.callback(e, id, checked);
	};

	//fixing prev/next history jump does nothing
	$(window).on("popstate", function(e)
	{
		let state = e.originalEvent.state;
		prevPath = (state ? state.originalPath : undefined) || e.target.location.pathname;

		loadArchiveFromPathname(prevPath, state ? state.highlightSelector : undefined);
	});

	function showPast(callback)
	{

	// hide all past days
		let	d = new Date();

		_today = $("div.day[data-date='" + d.getFullYear() + pad2((new Date()).getMonth() + 1) + pad2((new Date()).getDate()) + "']");

		if (!_today || $(".archive").length)
		{
			if (callback)
				return callback();

			return;
		}

		let	firstDay = $(".days").children().first(),
				firstDate = firstDay.attr("data-date");

		if (!firstDay.length)
			return;

		let div = [document.createElement("div")],
				y = firstDate.slice(0, 4),
				m = firstDate.slice(4, 6);

		if (parseInt(firstDate.slice(6, 8)) < 15)
			div.push(document.createElement("div"));

		let c = div.length;
		for (let n = 0; n < div.length; n++)
		{
			let d = new Date(y, m - n - 1);
			$(div[n]).load("/_archive/" + d.getFullYear() + "-" + pad2(d.getMonth() + 1), pastLoadDone);
		}
		function pastLoadDone()
		{
			if (--c > 0)
				return;

			let found = false,
					func = function (i, o)
					{
						if ($(this).attr("data-date") == firstDate)
							found = true;

						if (found)
							$(this).remove();
					};
			for (let n = 0; n < div.length; n++)
			{
				found = false;
			//remove any duplicate dates that already exist;
				$(div[n]).children().each(func);
				$(div[n]).children().prependTo($("div.days"));
			}
			pastLoaded();
			if (callback)
				callback();
		}
	}//showPast()

	function pastLoaded()
	{
		let	hasLoaded = $("#pastWeeks").length,
				stop = false;

		$("div.days").children().each(function(i, o)
		{
			if ($(this).is(_today))
			{
				$(this).attr("today", "true");
				stop = true;
			}
			if (stop)
			{
				$(this).removeClass("past");
			}
			else
			{
				$(this).addClass("past");
			}
		});

		let week = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"],
				match = $(_today).find(".date").text().toLowerCase().match(/([^,]{3})/),
				daysNum = match ? week.indexOf(match[1]) : -1;

		let prev = _today.prev();
		//there is no identication of which week a day belong to, so we must make sure that previous days of current week don't count as previous week.
		if (daysNum > 0)
		{
			for(let i = 0; i < daysNum && prev; i++)
			{
				prev.removeClass("past");
				prev = prev.prev();
			}
		}
		//remove more then 4 weeks worth of days.
		for(let i = 0; i < 27 && prev; i++)
		{
			prev = prev.prev();
		}
		if (prev)
		{
			let found = false,
					func = function(i, o)
					{
						if (found)
							return;

						if ($(this).is(prev))
							found = true;
						else
							$(this).remove();
					};
			$("div.days").children().each(func);
		}
		let	daysPast = $('div.past'),
				weeks = parseInt(Settings.pref("weeks")),
				weeksMax = Math.round((daysPast.length) / 7);//how many past weeks do we have available?

		if (isNaN(weeks))
			weeks = readCookieRaw("p") == "1" ? weeksMax : 0;

		if (weeks > weeksMax)
			weeks = weeksMax;

		//main function that shows/hides past days
		function showWeeks()
		{

			//get week numbers from dropdown
			let weeks = parseInt($("#pastWeeks").val());

			//just some sanity check
			if (weeks * 7 > daysPast.length)
				weeks = weeksMax;

			if (weeks < 1)
				weeks = 0;

			for(let i = weeksMax; i > 0; i--)
				$( "div.calendar" ).toggleClass("showPast" + i, (weeks >= i));

			Settings.pref("weeks", weeks);
		}
		//add new class pastNN to each past day, where NN is a week number.
		let func = function(i)
		{
			$(this).addClass("past" + (Math.ceil((daysPast.length - i) / 7 % (weeksMax + 1))));
		};
		daysPast.each(func);

		if (!hasLoaded)
		{
			//create dropdown menu with number of available past weeks
			let dropdown = document.createElement("select");
			dropdown.id = "pastWeeks";

			for(let i = 0; i <= weeksMax; i++)
			{
				let option = document.createElement("option");
				option.value = i;
				option.text = i;
				option.selected = (weeks == i);
				dropdown.appendChild(option);
			}

			//add event listener to dropdown
			$(dropdown).change(showWeeks);

			let span = document.createElement("span");
			span.id = "past-weeks";
			span.appendChild(document.createTextNode("Show past weeks:"));
			span.appendChild(dropdown);
			//insert our dropdown and new text into document
			$(".calendar").prepend(span);

			//hide "Show/Hide past month" links
			$("#past-showing").toggle(true);
			//togglePast();//make sure we disable "Show past month"
			$("#past-showing").toggle(false);
			$("#past-hidden").toggle(false);
		}
		showWeeks();
	}//pastLoaded()

	var prevOpened = null,
			prevParentOpened = null,
			prevParentOpenTimer = null;
	//adding attribute "opened" to the entry allows us show/hide things from CSS based on entry state
	$("body").on("click", "div.entry div.title", function(e)
	{
		if (e.isTrigger)
			return;

		let $entry = $( this ).parent(),
				parent = $entry.parent();

		if (prevOpened)
		{
			let po = prevOpened,
					ppo = prevParentOpened;

			prevParentOpenTimer = setTimeout(function()
			{
				collapseMulti.setTitle(ppo[0].list, Settings.prefs.collapseMulti && !ppo.hasClass("expand")? "_titleCollapsed" : "_titleOrig");

				po.parent().toggleClass("opened", false);
			}, 400);

			prevOpened.attr("opened", "");
			setTimeout(function()
			{
				po.removeAttr("opened");
			}, 300);
			prevOpened = null;
		}


		if ($entry.attr("opened") === undefined)
		{
			$entry.attr("opened", "");
			parent.toggleClass("opened", true);
			collapseMulti.setTitle(parent[0].list, "_titleOrig");
	//idealy this should've been done via "on complete" function submitted for slideUp/slideDown
			if (prevParentOpened && parent[0] == prevParentOpened[0])
				clearTimeout(prevParentOpenTimer);

			setTimeout(function()
			{
				$entry.attr("opened", "1");
			}, 300);

			prevParentOpened = parent;
			prevOpened = $entry;
		}
	});

	//create stylesheet. A little trick to have multi-line text in javascript
	let	style = document.createElement("style"),
			css = multiline(function(){/*
	svg
	{
		width: 1em;
		height: 1em;
	}
	body.userViewer .importColors,
	body.userViewer .importColorsIcon,
	body.userViewer .clearColorsIcon,
	body.userViewer .clearColors,
	body.userViewer .colors
	{
		display: none !important;
	}
	div.showPast1 div.past1,
	div.showPast2 div.past2,
	div.showPast3 div.past3,
	div.showPast4 div.past4,
	div.showPast5 div.past5,
	div.showPast6 div.past6,
	div.showPast7 div.past7,
	div.showPast8 div.past8,
	div.showPast9 div.past9
	{
		display: block;
	}

	/*
	div.calendar.searching > #past-weeks
	{
		text-decoration: line-through;
	}
	*//*
	#past-weeks
	{
		margin-left: 0.2%;
	}

	#pastWeeks
	{
		margin-left: 0.5em;
	}

	.archive #past-weeks
	{
		text-decoration: line-through;
	}


	/* today column border *//*
	div.today
	{
		border-color: #FFC800;
	}


	/* higlighted title under cursor *//*
	div.title > input[type="checkbox"]:hover + div.title,
	div.title:hover
	{
		background-color: #ffffd5;
		-webkit-transition: background 0s;
		color: black;
	}

	/* highlight opened entry *//*
	.entry[opened]
	{
		border: 1px solid black;
	}

	div[opened] + div
	{
		border-top: 0;
	}

	.engines input[type="checkbox"]
	{
		width: unset !important;
		vertical-align: top;
	}
	.engines img
	{
		vertical-align: text-bottom !important;
	}

	div.entry
	{
		float: left;
		width: 100%;
	}
	div.entry,
	.pointer,
	.date
	{
		cursor: pointer;
	}

	.filter
	{
		margin-left: 1em;
		cursor: pointer;
	}

	.past,
	.showhide0,
	span[checked] > .checkoff,
	span:not([checked]) > .checkon,
	/*separate showing searies and season premiere*//*
	div.calendar.showNew div.entry:not(.series-premiere):not(.searchResult),
	div.calendar.showReturn div.entry:not(.season-premiere):not(.searchResult)
	{
		display: none;
	}


	/*separate showing searies and season premiere*//*
	#nu-showing,
	#nu-hidden,
	#selected-only-hidden
	{
		display: none !important;
	}

	div.calendar.showNew div.entry.series-premiere:not(.searchResult),
	div.calendar.showReturn div.entry.season-premiere:not(.searchResult)
	{
		display: block;
	}


	/*Search field overlaps text below in Firefox*//*
	#searchFieldContainer
	{
		background-color: unset;
	}


	/*Watched*//*
	body:not(.enableWatched) div.title > input[type="checkbox"]
	{
		display: none;
	}

	body.enableWatched div.entry[watched] > div.title
	{
		text-decoration: line-through;
	}

	div.title > input[type="checkbox"]
	{
		vertical-align: middle;
		margin: 1px 3px 0 1px;
		height: 1em;
	}

	#searchResults div.title > input[type="checkbox"]
	{
		width: unset;
		float: left;
	}

	#searchResults > div.entry:last-child
	{
		margin-bottom: 1em;
	}
	/*Past related*//*
	.past
	{
		opacity: 0.5;
	}

	body.collapseMulti div.day:not(.expand):not(.opened) div.entry.multi
	{
		display: none !important;
	}

	body.collapseMulti div.day:not(.expand):not(.opened) div.entry.multif div.title:after
	{
		content: "";
		background-color: black;
		border-left: 1px solid white;
	}

	body.collapseMulti div.day:not(.expand):not(.opened) div.entry.multif div.title:after
	{
		position: absolute;
		height: 100%;
		right: -1px;
		top: 0;
		width: 3px;
	}
	/*Account popup*//*
	#settings-popup .content h4,
	#manage-links-popup .content h4,
	#account-popup-content .content h4
	{
		margin-top: 1em;
		margin-bottom: 1em;
	}
	#settings-popup .header h4,
	#manage-links-popup .header h4
	{
		margin: 0.2em 0 0 0;
	}
	#settings-popup .content,
	#manage-links-popup .content,
	#account-popup-content .content
	{
		padding-top: 0;
		padding-bottom: 1em;
	}
	#settings-popup-content .content > div > *:first-child,
	#account-popup-content .content > div > *:first-child
	{
		width: 1.6em;
		display: inline-block;
		vertical-align: bottom;
	}

	#settings-popup,
	#manage-links-popup
	{
		position: absolute;
		z-index: 1234;
		top: 50px;
		left: 4.7%;
		width: 1px;
		display: none;
	}
	#settings-popup-content,
	#manage-links-popup-content
	{
		position: absolute;
		top: 0px;
		left: 0px;
		background-color: #ffff00;
		border: 1px dotted black;
		width: 300px;
		min-height: 10px;
	}
	#settings-popup-content div.back,
	#manage-links-popup-content div.back
	{
		font-size: 1.2em;
		width: 1.3em;
		height: 1.3em;
		cursor: pointer;
		position: absolute;
		top: 5px;
		left: 5px;
	}
	.close > svg:first-child
	{
		display: none;
	}
	#settings-popup-content div.close,
	#manage-links-popup-content div.close
	{
		font-size: 1.2em;
		width: 1.3em;
		height: 1.3em;
		cursor: pointer;
		position: absolute;
		top: 0;
		right: 0;
		padding: 5px;
	}
	#settings-popup .header,
	#manage-links-popup .header
	{
		background-color: #7FFFFF;
		padding: 5px;
		text-align: center;
		height: 1.5em;
	}
	#settings-popup .content,
	#manage-links-popup .content,
	#engine-edit
	{
		padding: 3px 10px;
	}
	#engine-edit > div:not(#engine-res),
	#manage-links-popup .content > div:not(#engine-res)
	{
		display: table-row;
		white-space: nowrap;
	}
	#engine-edit > div:not(#engine-res) > *,
	#manage-links-popup .content > div:not(#engine-res) > *
	{
		display: table-cell;
		vertical-align: middle;
		margin: 2px 4px 2px 1px;
	}
	#manage-links-popup .content > div:not(#engine-res) > img
	{
		vertical-align: bottom;
		display: list-item;
	}

	#manage-links-popup .content > div:not(#engine-res) > filter
	{
		padding-left: 0.3em;
	}
	#manage-links-popup .content > div:not(#engine-res) > .edit,
	#manage-links-popup .content > div:not(#engine-res) > .del
	{
		cursor: pointer;
		margin: 3px;
		position: relative;
		top: -0.1em;
		font-size: 80%;
		padding: 3px;
		display: inline-block;
		height: 1em;
		float: left;
	}
	#settings-popup-content div.back:hover,
	#manage-links-popup-content div.back:hover,
	#manage-links-popup .content > div.dragging:not(.hide),
	#manage-links-popup .content:not(.dragging) > div:not(#engine-res):hover
	{
		background-color: #FFFFB7;
		outline: 1px dotted grey;
	}
	.close:hover > svg
	{
		background-color: #e81123;
	}
	.close:hover > svg:last-child
	{
		display: none;
	}
	.close:hover > svg:first-child
	{
		fill: #FFFFFF;
		display: inline-block;
	}

	#manage-links-popup .content > div.dragging.hide > *
	{
		opacity: 0;
	}
	#manage-links-popup .content > div.dragging.hide
	{
		outline: 1px dashed grey;
	}
	#manage-links-popup .content > div:not(.def) > a.link:after
	{
		content: "*";
		text-decoration: none;
		display: inline-block;
		color: red;
		margin-left: 0.2em;
	}
	#engine-edit > div:not(#engine-res) > label
	{
		text-align: right;
	}
	#engine-edit > div:not(#engine-res) > input
	{
		width: 95%;
		margin-left: 0.5em;
		padding-right: 1.5em;
	}
	#engine-edit > div:not(#engine-res) > select
	{
		width: 1.5em;
		position: relative;
		left: -1.8em;
		margin: 0;
		padding: 0;
	}
	#engine-res
	{
		overflow-x: auto;
		overflow-y: hidden;
		min-height: 18px;
		display: block !important;
		margin-left: 0.5em !important;
	}
	#engine-hidden
	{
		display: none !important;
	}
	#engine-res > *
	{
		vertical-align: middle;
		display: inline-block;
		margin: 0 4px 0 0;
	}
	#engine-submit,
	#engine-reset
	{
		font-size: 90%;
		display: inline-block !important;
		width: 50% !important;
	}
	#engine-reset
	{
		margin-left: 1em;
	}
	div.reset
	{
		display: none !important;
	}
	#manage-links-popup[changed] div.reset
	{
		display: block !important;
	}
	#sort-reset
	{
		display: block !important;
		text-align: right;
		margin-right: 0.5em;
		margin-bottom: 0.5em;
	}
	#engine-edit
	{
		 border-top: 1px dotted;
	}
	#manage-links-popup .nu,
	#manage-links-popup .nu
	{
		vertical-align: bottom;
		font-size: 1.3em;
	}
	#manage-links-popup-content .content > div.update
	{
		-webkit-transition:background-color 0.4s ease-in;
		-moz-transition:background-color 0.4s ease-in;
		-o-transition:background-color 0.4s ease-in;
		transition:background-color 0.4s ease-in;
	}

	[draggable]
	{
		-moz-user-select: none;
		-khtml-user-select: none;
		-webkit-user-select: none;
		user-select: none;
		/* Required to make elements draggable in old WebKit *//*
		-khtml-user-drag: element;
		-webkit-user-drag: element;
	}
	[draggable] .dndh
	{
		cursor: move;
		margin: 0 0.5em 0 0 !important;
		font-size: 1.2em;
		float: left;
		position: relative;
		top: -2px;
		padding-left: 3px;
		padding-right: 3px;
	}

	#settings-popup-content > .content > *
	{
		margin: 3px 0 3px 0;
		display: table;
	}

	.user > svg
	{
		margin-right: 0.3em;
	}

/*fixing menu wrapping too soon*//*
	@media screen and (max-width: 1000px)
	{
		body #menu li,
		body:not(.userViewer) #menu li
		{ display: inline-block; }
		body #menu li .nu,
		body:not(.userViewer) #menu li .nu
		{ min-width: unset; }
		body #account-popup,
		body:not(.userViewer) #account-popup
		{ top: 50px; }
	}
	#menu .nu
	{
		font-weight: bold;
	}
	#menu .close
	{
		font-size: 0.9em;
		position: relative;
		top: -0.6em;
		cursor: pointer;
		padding: 0.3em;
	}
	#menu .close > svg
	{
		width: 0.9em;
		height: 0.9em;
		position: unset;
	}

	.user > svg,
	.back > svg,
	.content > div > span > svg
	{
		width: 1.3em;
		height: 1.3em;
		vertical-align: bottom;
	}
	#searchResults > div.entry > div.title,
	body:not(.shortTitle) div > div.entry > div.title,
	body.shortTitle.shortTitleExpand div.day.expand > div.entry > div.title,
	body.shortTitle div.day.opened > div.entry > div.title
	{
		display: flex;
	}
	body.shortTitle:not(.shortTitleExpand) div.day.expand:not(.opened) > div.entry > div.title > input[type="checkbox"],
	body.shortTitle div.day:not(.expand):not(.opened) div.title > input[type="checkbox"]
	{
		float: left;
	}
	body.shortTitle:not(.shortTitleExpand) div.day.expand:not(.opened) > div.entry > div.title,
	body.shortTitle div.day:not(.expand):not(.opened) > div.entry > div.title
	{
		text-overflow: ellipsis;
		white-space: nowrap;
		overflow: hidden;
	}
	div.spacer
	{
		height: 0;
		border-bottom: 1px dotted silver;
		margin-top: 0.5em !important;
		margin-bottom: 0.5em !important;
		display: block !important;
	}
	a.file
	{
		margin-left: 1em;
	}
	a.file:before
	{
		text-decoration: none;
		display: inline-block;
		content: "(";
	}
	a.file:after
	{
		text-decoration: none;
		display: inline-block;
		content: ")";
	}

	#searchResults > .info
	{
		margin-bottom: 0.7em;
	}
	
	#searchResults > .info > small
	{
		vertical-align: text-bottom;
	}
	#searchResults > .info > small > span:not(.info)
	{
		position: absolute;
		right: 1em;
	}
	span.button
	{
		cursor: pointer;
		background-color: #DDDDDD;
		border-radius: 4px;
		padding: 3px;
	}
	
/* fix search field box covers text on bottom *//*
	#searchFieldContainer
	{
		height: 0;
	}
*/});//css

	style.innerHTML = css;
	$("head").append(style);


	//fix incorrect initial color in colorpicker
	//fix clicking outside of colorpicker saves selected color instead of discarding
	//fix close colorpicker by pressing escape button

	var editingSeriesId = -1;
	var clone = $("#colorPickerHolder").clone();
	$("#colorPickerHolder").remove();
	var picker = clone.colorPicker(
	{
		animationSpeed: 0,
		opacity: false,
		buildCallback: function($elm)
		{
			let cp = this;
			$elm
				.append('<div class="cp-disp"><input type="button" value="save"> <input type="button" value="cancel"></div>')
				.on( "click", "input", function()
				{
					if (!this.value)
						return;

					if (this.value == 'save')
					{
						assignColor( editingSeriesId, "#" + cp.color.colors.HEX, true );
						editingSeriesId = -1;
					}
					cp.toggle();
				});
			$("body").on("keydown", function(e)
			{
	//ESC(27) = cancel
				if (e.which == 27 && $elm.is(":visible"))
					cp.toggle();
			});
		},
		cssAddon: ".cp-disp{ padding-bottom: 2px; clear:both; }",
		renderCallback: function($elm, toggled) {
			var colors = this.color.colors;
			// on show
			if( toggled === true ){
			}
			// on change
			else if( toggled === undefined ){
	//preview new color
				assignColor( editingSeriesId, "#" + colors.HEX, false );
			}
			// on hide
			else if( toggled === false ){
	//restore entry color
				if( editingSeriesId > 0 )
					loadColor( editingSeriesId );

				editingSeriesId = -1;
				picker.detach().appendTo("body");
			}

		}
	}).attr("id", "colorPickerHolderNew"); //replace ID so it won't initialize in main.js

	$("body").off("click", ".picker");

	// now hook up the picker to the picker icons
	$("body").on("click", ".picker", function(e){
		if( !$(e.target).hasClass("picker") ) return;

		editingSeriesId = $(this).parents("[data-series-id]").data("series-id");
	//set initial color in colorpicker based on current entry or default to white
		picker.val(coalesce(DB.savedColors[editingSeriesId], "#ffffff"));
		picker.detach().appendTo(this).click();
	});

	function middleClick(e, search)
	{
		if (e.button != 1 && !(!e.button && e.ctrlKey) )
			return;

		e.stopPropagation();
		e.preventDefault();
		let parent,
				entry = true;
		if (search)
			parent = $(search);
		else if ($(this).is("div.title"))
			parent = $(this).parent();
		else
		{
			parent = $(this).parent().find("div.entry");
			entry = false;
		}
		let func = function(e)
		{
			let el = $(this),
					MONKEY_ID = encodeURIComponent( el.data("series-id") );
			if (!entry && !DB.getColor(MONKEY_ID))
				return;

	//		let title = el.children("div.title").text().replace("?", "").replace(/[0-9]+-[0-9]+-[0-9]+/, ""),
			let title = this._title && this._title._titleOrig ? this._title._titleOrig : el.children("div.title").text();
			title = title.replace("?", "").replace(/[0-9]+-[0-9]+-[0-9]+/, "");
	//				MONKEY = encodeURIComponent( title ),
			let MONKEY = encodeURIComponent( title.replace(/ E[0-9]+/g, "") ),
					MONKEY_N = encodeURIComponent( title.replace( /S[0-9]+E[0-9]+$/g, '' ) ),
					WIKI_TITLE = encodeURIComponent( el.data("series-source") );

			$.each( _engines, function( i, engine )
			{
				if (!$("body").hasClass("engine_" + cleanName(engine.host)))
					return;

				let href = engine.href
					.replace( "MONKEY_ID", MONKEY_ID )
					.replace( "MONKEY_N", MONKEY_N )
					.replace( "MONKEY", MONKEY )
					.replace( "{WIKI_TITLE}", WIKI_TITLE );

				href = fixLink(href, engine.name);
				window.open(href, (MONKEY + engine.name).replace(/ /g, "").replace(/%.{2}/g, ""));
			});
		};
		parent.each(func);
	}
	$("body").on("mousedown", "div.date,div.title", middleClick);

	let _markSearchResults = markSearchResults;
	window.markSearchResults = function markSearchResults()
	{
		if (showMyShows.box || showMyHidden.box)
			return

		let entries = $("#searchResults").find("div.entry");
		entries.each(watched.attach);
		setTimeout(function()
		{
			$("div.day").each(collapseMulti);
		});

		return _markSearchResults();
	};

	$("body").on("click", 'div.entry div.title>input[type="checkbox"]', function(e)
	{
		e.stopPropagation();
	});
	//sanitizing engine links
	$("body").on("click", "div.entry div.title", function(e)
	{
		if (e.isTrigger && !force)
			return;

		force = false;
		let obj = this;
		setTimeout(function()
		{
			let details = $(obj).parent().find(".details");
			if (!details.length || details[0].inited)
				return;

			details[0].inited = true;
			let showHideObj = document.createElement("a"),
					showId = $(obj.parentNode).attr("data-series-id"),
					show = document.createElement("span"),
					hide = document.createElement("span");

			show.innerHTML = "Show";
			show.className = "showhide0";
			hide.innerHTML = "Hide";
			hide.className = "showhide1";
			showHideObj.appendChild(show);
			showHideObj.appendChild(hide);
			showHideObj.appendChild(document.createTextNode(" this show"));

			showHideObj.className = "showhide";
			showHideObj.href = "#";
			showHideObj.addEventListener("click", function(e)
			{
				e.stopPropagation();
				e.preventDefault();
				showHide(parseInt(showId), 2);
			}, false);

			let list = $(obj).parent().find(".engines").append(showHideObj).children();
			for (let i = 0; i < list.length; i += 3)
			{
				let img = list[i],
						a = list[i+1],
						br = list[i+2],
						checkbox = document.createElement("input"),
						engine = window.engines[i/3] ? window.engines[i/3].host : img.src ? img.src.match(/\?domain=(.*)/)[1] : null,
						id = "engine_" + cleanName(engine),
						engineInfo = window.engines[enginesFind(engine)],
						domain = engineInfo ? getHost(engineInfo.href) : null;

				if (domain)
					img.src = "http://www.google.com/s2/favicons?domain=" + domain;

				$(img).toggleClass(id, true);
				$(a).toggleClass(id, true);
				$(br).toggleClass(id, true);
				$(checkbox).toggleClass(id, true);
				checkbox.engine = engine;
				checkbox.type = "checkbox";
				checkbox.title = "Open with middle click on title";
				checkbox.checked = enginesCheck(engine) != -1;
				img.parentNode.insertBefore(checkbox, img);
				if (a.className.indexOf("archive-link") != -1 && (showMyShows.box || showMyHidden.box))
				{
					$(a).toggleClass("archive-link", false);
					a.textContent = "Show all episodes";
					a.href = "javascript:search('info:" + showId + "');";
				}
				if (engine == "airdates.tv" || a.className == "showhide")
				{
					checkbox.style.visibility = "hidden";
					return;
				}
				a.href = fixLink(a.href, a.text);

				$(checkbox).on("click", "", function(e)
				{
					if (checkbox.checked)
						enginesAdd(checkbox.engine);
					else
						enginesRemove(checkbox.engine);

					$("input." + id).prop("checked", checkbox.checked);
				});

			};
		});
	});//$("body").on("click", "div.entry div.title", function(e)

	function fixLink(link, engine)
	{
		link = link.replace("%3F", "").replace(/[0-9]+-[0-9]+-[0-9]+(%20)?/, "").replace(/( |%20)E[0-9]+/, "");
		if (engine == "Piratebay")
			link = link.replace(/['"`!]/g, '');
		return link;
	}

	function showHideLoad()
	{

		if (showHideLoad.inited)
			return;

		$(".days").before(createCheckbox("showHidden", "Show hidden", Settings.prefs.showHidden ? true : false, Settings.callback));
		$(".days").before(createCheckbox("collapseMulti", "Collapse multiple", Settings.prefs.collapseMulti ? true : false, collapseMulti.onOff));
		for(let i = 0; i < _hidden.length; i++)
		{
			showHide(_hidden[i], 1);
		}
		showHideLoad.inited = true;


		let clearColors = $(".clearColors"),
				clearHidden = clearColors.clone(false),
				clearWatched = clearColors.clone(false);

		clearHidden.removeClass();
		clearHidden.addClass("clearHidden");
		clearHidden.html("Clear hidden");
		clearHidden.insertAfter(clearColors);
		clearWatched.removeClass();
		clearWatched.addClass("clearWatched");
		clearWatched.html("Clear watched");
		clearWatched.insertAfter(clearHidden);
		clearColors.after(" | ");
		clearHidden.after(" | ");
		clearHidden.on( "click", function()
		{
			let array = [];
			for(let i = 0; i < _hidden.length; i++)
				array.push(_hidden[i]);

			for(let i = 0; i < array.length; i++)
				showHide(array[i], 0);

			alert( "Done!" );
			return false;
		});
		clearWatched.on( "click", function()
		{
			watched._list = {};
			watched.save();
			$("div.entry").each(function(i, entry)
			{
				if (entry._input)
					watched.update(entry, false);
			});

			alert( "Done!" );
			return false;
		});
			$("body").off("click", ".exportColors");
			$("body").on( "click", ".exportColors", function(e)
			{
				e.stopImmediatePropagation();
				let str = exportGetColors();
				if (str)
						prompt( "This is the crazy text. \nYou can save it in a normal textfile and/or import it to another computer/browser.", str );
				else
						alert("Nothing to export");
				return false;
			});
	} //showHideLoad()
	DB.viewing = DB.username != DB.loggedInUsername;
	let ul = $("#menu").find("ul");
	if (DB.viewing)
	{
		ul.children("li:last-child").css("margin-right", "1.5em");
		let li = $(multiline(function(){/*
<li>Viewing as 
	<span class="user">
		<svg viewBox="0 0 24 24">
			<path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"></path>
		</svg>
	</span>
	<span class="nu"></span>
	<a class="close" href="*/}) + window.location.href.replace(/\/u\/[^?#]+/, "") + multiline(function(){/*">
		<svg viewBox="0 0 24 24">
			<path d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z"></path>
		</svg>
		<svg viewBox="0 0 24 24">
			<path d="M19,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M19,19H5V5H19V19M17,8.4L13.4,12L17,15.6L15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4Z"></path>
		</svg>
	</a>
</li>
*/})).appendTo(ul);
		li.find("span.nu").text(DB.username);
//fix auto wrap on small screen
		let width = li.prop("clientWidth");
		width = Math.round(width * 2 + 760 + width / 6);
		$("head").append("<style>@media screen and (max-width: " + width + multiline(function(){/*
px)
{
	body.userViewer #menu li:not(:nth-last-child(2)):not(:last-child){ display: block; }
	body.userViewer #menu li .nu{ min-width: 15px; }
	body.userViewer #account-popup,
	body.userViewer #settings-popup,
	body.userViewer #manage-links-popup
	{ top: 80px; }
}
</style>*/}));
	}
	else
	{
		setTimeout(function()
		{
		let li = ul.children().last();
		let width = li.prop("clientWidth");
		width = Math.round(width * 2 + 600 + width / 6);
		$("head").append("<style>@media screen and (max-width: " + width + multiline(function(){/*
px)
{
	body:not(.userViewer) #menu li{ display: block; }
	body:not(.userViewer) #menu li .nu{ min-width: 15px; }
	body:not(.userViewer) #account-popup,
	body:not(.userViewer) #settings-popup,
	body:not(.userViewer) #manage-links-popup
	{ top: 80px; }
}
</style>*/}));
});
	}


	//fix when removing color it still saves it in database as #FFFFFF
	window.DB._setColor = window.DB.setColor;
	window.DB.setColor = function setColor(id, c)
	{
		if (c == "#FFFFFF")
			c = "";

		if (c)
			DB.infoAdd(id);
		else if (_hidden.indexOf(id) == -1)
			DB.infoRemove(id);
		if (showMyShows.box)
			showMyShows();
		else if (showMyHidden.box)
		{
			showMyHidden();
		}

		this._setColor(id, c);
	};

	//hiding old combined checkboxes
	$( "#nu-showing" ).toggle(false);
	$( "#nu-hidden" ).toggle(false);
	//add new separate checkboxes
	createCheckbox("showNew", "New shows", Settings.prefs.showNew ? true : false, Settings.callback);
	createCheckbox("showReturn", "Returning shows", Settings.prefs.showReturn ? true : false, Settings.callback);

	//replacing existing "my shows" checkboxes with ours
	let _toggleSelectedOnly = window.toggleSelectedOnly,
			_updateSelectedOnly = window.updateSelectedOnly;

	window.toggleSelectedOnly = function(){};
	window.updateSelectedOnly = function(){};
	$("#selected-only-showing").toggle(false);
	$("#selected-only-hidden").toggle(false);
	createCheckbox("activeOnly", (DB.viewing ? DB.username + " user's" : "My") + " shows", "s", function(e, id, checked){}, [$("#selected-only-hidden").prop("title")]);

	//fix auto clear search field on click
	setTimeout(function()
	{
		$(document).off("click", ".onX, .x").on('touchstart click', '.onX', function( ev )
		{
			ev.preventDefault();
			$(this).removeClass('x onX').val('').change();
		});
	//fix no X button after page refresh and browser auto fill search bar with previous text
		$('input.clearable').trigger("input");
	});



	//injecting userscript function execution
		showPast(function()
		{
	//adding watched checkboxes
			$("div.day > div.entry").each(watched.attach);
	//collapse multiple entries of the same series in one day
			$("div.day").each(collapseMulti);
		});
		showHideLoad();

	//list of user's shows
	DB.infoNameClean = function(entry)
	{
		let title = entry._title && entry._title._titleOrig ? entry._title._titleOrig : $(entry).find("div.title").text();
		return title.substring(0, title.lastIndexOf(" "));
	};

	DB.infoSave = function()
	{
		ls("info", DB.info);
	};

	DB.infoAdd = function(id, name, nosave)
	{
		if (typeof(name) == "undefined")
		{
			return DB.infoAdd(id, DB.infoGet(id), nosave);
		}
		else if (!name)
		{
			setTimeout(function()
			{
				DB.infoLoad(id);
			}, rand(100, 2000));
			return;
		}
		DB.info[id] = name;
		if (!nosave)
			DB.infoSave();
	};

	DB.infoRemove = function(id)
	{
		delete DB.info[id];
		DB.infoSave();
	};

	DB.infoGet = function(id)
	{
		let entry = $('div.days div[data-series-id="' + id + '"]').first()[0];
		if (entry)
			return [DB.infoNameClean(entry), entry.getAttribute("data-series-source")];

		return null;
	};

	DB.infoLoad = function(id)
	{
		let obj = document.createElement("div");
		$(obj).load("/s?"+$.param({q:"info:" + id}), function(e)
		{
			let title = $(obj).find("b").first().text();
			title = title.substring(0, title.lastIndexOf("(") - 1);
			if (title)
				DB.infoAdd(id, [title, $(obj).find(".entry").attr("data-series-source")]);
		});
	};

	function showMyShows(e)
	{
		if (!DB.infoLoaded)
		{
			setTimeout(showMyShows, 100);
			return;
		}
		$(".entry").removeClass("searchResult");
		$("#searchStatus").css("visibility","hidden");
		let list = [];

		for (let i in DB.savedColors)
		{
			if (!DB.info[i])
				continue;

//http://runtime-era.blogspot.com/2011/11/grouping-html-hex-colors-by-hue-in.html
			/* Get the hex value without hash symbol. */
			let hex = DB.savedColors[i].substring(1);
			/* Get the RGB values to calculate the Hue. */
			let r = parseInt(hex.substring(0,2),16)/255,
					g = parseInt(hex.substring(2,4),16)/255,
					b = parseInt(hex.substring(4,6),16)/255;

			/* Getting the Max and Min values for Chroma. */
			let max = Math.max.apply(Math, [r,g,b]),
					min = Math.min.apply(Math, [r,g,b]);

			/* Variables for HSV value of hex color. */
			let chr = max-min,
					hue = 0,
					val = max,
					sat = 0;

			if (val > 0)
			{
				/* Calculate Saturation only if Value isn't 0. */
				sat = chr/val;
				if (sat > 0)
				{
					if (r == max)
					{ 
						hue = 60 * (((g - min) - (b - min)) / chr);
						if (hue < 0)
							hue += 360;
					}
					else if (g == max)
					{ 
						hue = 120 + 60 * (((b - min) - (r - min)) / chr); 
					} else if (b == max)
					{ 
						hue = 240 + 60 * (((r - min) - (g - min)) / chr); 
					}
				}
			}
			
			/* Modifies existing objects by adding HSV values. */
			list[list.length] = {
				id: i,
				hex: hex,
				hue: hue,
				sat: sat,
				val: val,
				lum: (r * 299 + g * 587 + b * 114) / 1000
			}
		}
		(function display()
		{
			if (Settings.prefs.sortBy == Settings.SORT_COLOR)
			{
				list = list.sort(function(a,b)
				{
					let r = a.hue - b.hue;
					if (!r)
						r = a.val - b.val;
					if (!r)
						r = a.sat - b.sat;
					if (!r)
						r = DB.info[a.id][0].toLowerCase().localeCompare(DB.info[b.id][0].toLowerCase());
					return r;
				});
			}
			else
			{
				list = list.sort(function(a, b)
				{
					return DB.info[a.id][0].toLowerCase().localeCompare(DB.info[b.id][0].toLowerCase());
				});
			}
			$(showMyShows.box).find("span.button").off("click");
			showMyShows.box = document.createElement("div");
			let entry = document.createElement("div"),
					title = document.createElement("div"),
					count = 0;

			entry.appendChild(title);
			entry.className = "entry";
			title.className = "title";
			for(let i = 0; i < list.length; i++)
			{
				let id = list[i].id;

				count++;
				entry = entry.cloneNode(true);
				title = entry.firstChild;
				entry.setAttribute("data-series-id", id);
				entry.setAttribute("data-series-source", DB.info[id][1]);
				title.textContent = DB.info[id][0];
				showMyShows.box.appendChild(entry);
			}
			let text = "You don't have any shows";
		
			if (count)
			{
				function getSortName()
				{
					let a = ["Name", "Color"];
					return  a[Settings.prefs.sortBy + 1] || a[0];
				}
				text = (DB.viewing ? DB.username + " has " : "You have ") + count + " show" + (count > 1 ? "s" : "") + '. <span>Sort by <span class="button">' + getSortName() + "</span></span>";
			}
			$(showMyShows.box).prepend('<div class="info"><small>' + text + '</small><div>');
			$("#searchResults").html(showMyShows.box.innerHTML).find("span.button").click(function(e)
			{
				if (++Settings.prefs.sortBy > 1)
					Settings.prefs.sortBy = 0;
				
				Settings.save();
				display();
			});;
		})();
	}

	function showMyHidden(e)
	{
		if (!DB.infoLoaded)
		{
			setTimeout(showMyHidden, 100);
			return;
		}
		$(".entry").removeClass("searchResult");
		$("#searchStatus").css("visibility","hidden");
		showMyHidden.box = document.createElement("div");
		let entry = document.createElement("div"),
				title = document.createElement("div");

		entry.appendChild(title);
		entry.className = "entry";
		title.className = "title";
		let list = [],
				count = 0;

		for(let i = 0; i < _hidden.length; i++)
		{
			list[list.length] = _hidden[i];
		}
		list.sort(function(a, b)
		{
			return !DB.info[a] || !DB.info[b] ? -1 : DB.info[a][0].toLowerCase().localeCompare(DB.info[b][0].toLowerCase());
		});
		for(let i = 0; i < list.length; i++)
		{
			let id = list[i];
			if (!DB.info[id])
				continue;

			count++;
			entry = entry.cloneNode(true);
			title = entry.firstChild;
			entry.setAttribute("data-series-id", id);
			entry.setAttribute("data-series-source", DB.info[id][1]);
			title.textContent = DB.info[id][0];
			showMyHidden.box.appendChild(entry);
		}
		let text = "There are no hidden shows";
		if (count)
		{
			text = "There are " + count + " hidden show" + (count > 1 ? "s" : "") + ":";
		}
		$(showMyHidden.box).prepend('<small>' + text + '</small><div style="height: 0.5em;"></div>');
		$("#searchResults").html(showMyHidden.box.innerHTML);
	}
	$(document).ready(function()
	{
		function hideNode(id)
		{
			$("." + id).each(function(i, e)
			{
				let prev = e.previousSibling;
				if (prev.nodeType == prev.TEXT_NODE)
				{
					let span = document.createElement("span");
					prev.parentNode.insertBefore(span, prev);
					span.appendChild(prev);
					prev = span;
				}
				$(prev).toggleClass(id + "Icon", true)
			});
		}
		if (DB.viewing)
		{
			$("body").toggleClass("userViewer", true);
			hideNode("clearColors");
			hideNode("importColors");
		}
		let accountLoop = 50;
		$("#account-popup").attr("hidden", "");
		$("#account-overview").click(function(e)
		{
			if (e.isTrigger)
				return;

			Settings.hide();
			customLinks.hide();
		});
		$("#account-overview").click(function loop(e)
		{
			if (e.isTrigger)
				return;

			Settings.hide();
			customLinks.hide();
			$("#account-popup").removeAttr("hidden");
			let as = $("#account-popup-content .content a");
			if( !$( "#account-popup" ).hasClass("loaded") || (!as.length && accountLoop--))
				return setTimeout(function()
				{
					loop(e)
				}, 100);

			if (!as.length)
				return;

			let header = $("#account-popup-content").find("div.header");
			//account reset password
			if (DB.loggedInUsername)
			{
				let div = $("<div/>").html(header.html().replace(/(\s)+/g, "$1")),
//						a = $('<a href="/_u/forgot-password#' + DB.username +'">Reset password</a>');
						a = $('<a href="/_u/forgot-password">Reset password</a>');
				header.html("").append(div);
				div.find("a").after(a).after(" | ");
			}

			//account overview neat icons
			$("#account-popup-content").find("div.content").contents().filter(function()
			{
				if (this.tagName == "BR")
					this.parentNode.removeChild(this);

				if (this.nodeType != Node.TEXT_NODE || !this.textContent.trim())
					return;

				let span = document.createElement("span"),
						div = document.createElement("div");
				div.appendChild(span);
				div.appendChild(this.nextSibling);
				span.textContent = this.textContent.trim();
				this.parentNode.replaceChild(div, this);
			});

			$("#account-overview").unbind("click", loop);

			let d = document.createElement("a");
			d.addEventListener("click", function(e)
			{
				if (e.isTrigger)
					return;

				e.preventDefault();
				e.stopPropagation();

				let data = exportGetColors(),
						d = new Date(),
						date = d.getFullYear() + pad(d.getMonth()+1) + pad(d.getDate()) + pad(d.getHours()) + pad(d.getMinutes()) + pad(d.getSeconds());
				if (data)
					fileSave("Airdates.tv_colors_" + (DB.username ? DB.username.replace(/[\/\\?%*:|"<>]/g, "_") + "_" : "") + date + ".txt", data);
				else
					alert("Nothing to export");
			}, false);
			d.textContent = "save to file";
			d.className = "file";
			$("#account-popup").find(".exportColors").append(d);
			d = d.cloneNode(true);
			d.addEventListener("click", function(e)
			{
				if (e.isTrigger)
					return;

				e.preventDefault();
				e.stopPropagation();

				fileLoad(function(str)
				{
					if (str)
					{
						$.each( str.split( ";" ), function( i, e ){
							assignColor.apply( null, e.split("=").concat([true] ) );
						} );
						alert( "kk, imported " + str.split(";").length + " colors" );
					}
					else
					{
						alert( "Error importing" );
					}
				}, ".txt");

			}, false);
			d.textContent = "load from file";
			$("#account-popup").find(".importColors").append(d);

			hideNode("importColors");
			let a = document.createElement("a"),
					i = document.createElement("span"),
					span = document.createElement("div"),
					h = document.createElement("h4"),
					item = span,
					parent = as[as.length-1].parentNode.parentNode;

			h.textContent = adeName + " v" + adeVersion;
			parent.appendChild(h);
			span.appendChild(i);
			span.appendChild(a);

			span = span.cloneNode(true);
			a = span.lastChild;
			i = span.firstChild;
			i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" /></svg>';
			a.href = '#myshows';
			a.id = "";
			a.addEventListener("click", function(e)
			{
				e.preventDefault();
				search("info:myshows");
				$("#account-popup").toggle(false);
			}, false);
			a.textContent = (DB.viewing ? DB.username + " user's" : "My") + " shows";
			parent.appendChild(span);

			span = span.cloneNode(true);
			a = span.lastChild;
			i = span.firstChild;
			i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" /></svg>';
			a.href = '#hidden';
			a.id = "";
			a.addEventListener("click", function(e)
			{
				e.preventDefault();
				search("info:hidden");
				$("#account-popup").toggle(false);
			}, false);
			a.textContent = "Hidden shows";
			parent.appendChild(span);

			span = span.cloneNode(true);
			a = span.lastChild;
			i = span.firstChild;
			i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M3,5H9V11H3V5M5,7V9H7V7H5M11,7H21V9H11V7M11,15H21V17H11V15M5,20L1.5,16.5L2.91,15.09L5,17.17L9.59,12.59L11,14L5,20Z" /></svg>';
			a.href = '#';
			a.id = "manage-links-open";
			a.addEventListener("click", function(e)
			{
				e.preventDefault();
				customLinks.manager(function()
				{
					customLinks.show();
					$("#account-popup").toggle(false);
				});

			}, false);
			a.textContent = "Links manager";
			parent.appendChild(span);

			span = span.cloneNode(true);
			a = span.lastChild;
			i = span.firstChild;
			i.innerHTML = '<svg viewBox="0 0 24 24"><path d="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z" /></svg>';
			a.href = '#';
			a.id = "settings-open";
			a.addEventListener("click", function(e)
			{
				e.preventDefault();
				Settings.show();
				$("#account-popup").toggle(false);
			}, false);
			a.textContent = "Options";
			parent.appendChild(span);
//			a.parentNode.insertBefore(i, a);
		});//$("#account-overview").click()

		let repeat = DB.username ? 20 : 2,
				list = ls("info") || {};
		//to avoid hit a ceiling of max data allowed store in local storage, we only save names of user's current shows, nothing else.
		DB.info = {};
		DB.infoLoaded = false;
		//unfortunately we don't know when saved colors are done loading for registered users, so we must wait in a loop
		(function loop()
		{
			let added = false;
			for(let id in DB.savedColors)
			{
				repeat = 0;
				if (id in list)
				{
					DB.info[id] = list[id];
					continue;
				}
				added = true;
				DB.infoAdd(id);
			}
			if (repeat--)
				return setTimeout(loop, 300);

			for(let i = 0; i < _hidden.length; i++)
			{
				let id = _hidden[i];
				if (id in list)
				{
					DB.info[id] = list[id];
					continue;
				}
				added = true;
				DB.infoAdd(id);
			}

			DB.infoLoaded = true;
			if (added)
				DB.infoSave();
		})();

	//fix paste via right click: adding input event
		$( "#searchBecauseNoOneChecks" ).on( "input keyup change search", function(e)
		{
			let q = this.value.trim();
			if (q == "info:myshows" || q.match(/^info:([a-zA-Z]+|$)/))
			{
				e.preventDefault();
				e.stopPropagation();
				if (q == "info:hidden")
					showMyHidden(e);
				else if (q == "info:myshows")
					showMyShows(e);
				else
				{
					showMyShows.box = showMyHidden.box = document.createElement("div");
					$("#searchResults").html('<ul id="resultsBecauseNoOneChecks"><li>No results :(</li></ul><small>Search took 0.000 seconds</small>');
				}
	//fix paste via right click
				if (e.type == "input")
					$(this).trigger("change");
			}
			else
			{
				showMyShows.box = null;
				showMyHidden.box = null;
			}

		}).trigger("change");

		//fix clear colors for members
		$(document.body).off( "click", ".clearColors");
		$(document.body).on( "click", ".clearColors", function(e)
		{
			e.stopPropagation();
			e.preventDefault();
			if (!DB.viewing)
			{
				$.each( DB.savedColors, function( i, c )
				{ 
					assignColor( i, null, true );
				} );
				alert( "Done!" ); 
			}
			else
				alert( "You don't have permission!" );

			return false; 
		});
	});//document.ready()

	$(document.body).on( "click touchstart", function(e)
	{
		if (e.isTrigger)
			return;

		let close = $(e.target).parents(".close").get().length,
				p = $( e.target ).parents("#manage-links-popup").get().length;

		if (e.target.id != "manage-links-open" && (p == 0 || (p && close)))
			customLinks.hide();

		p = $( e.target ).parents("#settings-popup").get().length;
		if (e.target.id != "settings-open" && (p == 0 || (p && close)))
			Settings.hide();
	});
	//reset password auto fill username
	if (location.pathname == "/_u/forgot-password")
		$("#username").val(DB.loggedInUsername);

	$(window).on("hashchange", function(e)
	{
		if (location.hash == "#myshows")
			search("info:myshows");
		else if (location.hash == "#hidden")
			search("info:hidden");

	}).trigger("hashchange");

};//func()

//disqus
if (window.top !== window.self)
{
	func = function(){};

//disqus troll filter
	if (window.location.href.indexOf("disqus.com") != -1)
	{
		let trollHide = false;
		let trollList = ls("trolls"),
				trollTimer,
				comments = {};

		if (!trollList || typeof(trollList) != "object")
			trollList = ["Tubasing"];

		let isTroll = function (name)
		{
			return trollList.indexOf(name);
		};

		let trollSave = function ()
		{
			clearTimeout(trollTimer);
			trollTimer = setTimeout(function()
			{
				ls("trolls", trollList);
			}, 1000);
		};

		let trollAdd = function (name)
		{
			if (isTroll(name) == -1)
				trollList.push(name);

			trollSave();
		};

		let trollRemove = function (name)
		{
			let n = isTroll(name);
			if (n != -1)
				trollList.splice(n, 1);

			trollSave();
		};

		let isMatch = function (needle, array)
		{
			let r = -1;
			for(let i = 0; i < array.length; i++)
			{
				r = needle.indexOf(array[i]);
				if (r != -1)
					break;
			}
			return r;
		};
		let findParent = function (obj, c)
		{
			if (typeof(c) != "object")
				c = [c];

			if (obj && obj.className && isMatch(obj.className, c) != -1)
				return obj;

			if (obj)
				return findParent(obj.parentNode, c);

			return null;
		};
		let censor = function (a, b, c, d, e)
		{
			let p = "blah",
					r = "",
					caps = true,
					t,
					i,
					isStr = isNaN(a.replace(/['`].*/, ''));

			if ((a.length < 2 && isStr) || (a.length < 7 && a.match(/['`]/) && isStr) || a.match(/^\$?[0-9]+k?/i))
				return a;

			for(i = 0; i < a.length; i++)
				if (a[i] == a[i].toLowerCase())
				{
					caps = false;
					break;
				}

			for(i = 0; i < p.length; i++)
			{
				t = p[i];

				if (a.length < i + 1)
					continue;

				if (caps || (a.length > i && a[i] != a[i].toLowerCase()))
					t = t.toUpperCase();

				r += t;
			}
			return r;
		};

		let toggleTroll = function (comment, f)
		{
			//f = 0: orig
			//f = 1: troll
			//f = 2: toggle
			f = typeof(f) == "undefined" ? 2 : f;
			let parent = comment._parent,
					t = true;
			if (f == 1 || (f == 2 && parent.getAttribute("troll") != "true"))
			{
				if (trollHide)
					comment.innerHTML = '<img src="' + trollSrc + '" class="trollComment">';
				else
					censorText(comment);
			}
			else
			{
				censorText(comment, true);
				t = false;
			}
			parent.setAttribute("troll", t);
			return t;
		};

		let censorText = function (obj, orig)
		{
			let c = obj.childNodes;
			for(let i = 0; i < c.length; i++)
			{
				if (c[i].nodeName == "#text")
				{
					if (!("nodeValueOrig" in c[i]))
						c[i].nodeValueOrig = c[i].nodeValue;

					let t;
					if (orig)
						t = c[i].nodeValueOrig;
					else
					{
						if (!("nodeValueTroll" in c[i]))
							c[i].nodeValueTroll = c[i].nodeValue.replace(/(\w+(['`]\w+)?)/g, censor);

						t = c[i].nodeValueTroll;
					}

					c[i].nodeValue = t;
				}
				else if (c[i].childNodes.length && c[i].className != "see-more")
					censorText(c[i], orig);
			}
			return obj.innerHTML;
		};

		window.addEventListener("load", function(e)
		{
			let timer = setInterval (function()
			{
				let posts = document.getElementById("post-list");
				if (!posts)
					return;

				clearInterval(timer);
				timer = setInterval (function()
				{
					let names = posts.getElementsByClassName("author");
					if (!names.length)
						return;
					clearInterval(timer);

					let style = document.createElement("style");
					style.innerHTML = multiline(function(){/*
.trollComment
{
	cursor: pointer;
	opacity: 0.4;
	transform: scaleX(-1)
}
.post-byline:not([troll="true"]) img.troll
{
	opacity: 0.1;
	transform: scaleX(-1);
}
img.troll
{
	background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAsSAAALEgHS3X78AAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M1cbXjNgAAABZ0RVh0Q3JlYXRpb24gVGltZQAxMi8yOC8xMZSAjI8AAAA/UExURf///wAAAAEBAQICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCw0NDQ8PDxAQEBISEhMTExYWFvr6+v////L9RbYAAAABdFJOUwBA5thmAAAAZklEQVQYGY3BWw6CMABFwXtQKLZafJz9r1WaaPgycSb5wwys8KT1DFcPZIeHlgQBZ5jcbUnTByq4oDXpopMDd88JWlCZEEtSKUsHRCUJr1W8iVqTIBeWAirZ4Rdk6A6cWPMBHfLbGzqjB8v8i7F/AAAAAElFTkSuQmCC");
	width: 16px;
	height: 16px;
	cursor: pointer;
	margin-left: 0.3em;
	vertical-align: top;
	display: inline-block;
}
.trollMenuImg
{
	vertical-align: top;
}
.trollBox
{
	height: auto;
	line-height: 1.3em;
}
*/
					});
					document.getElementsByTagName("head")[0].appendChild(style);
					for(let i = 0; i < names.length; i++)
					{

						let body = findParent(names[i], "post-content");
						if (!body)
							continue;

						let parent = findParent(names[i], "post-byline"),
								img = document.createElement("img"),
								name = names[i].innerText,
								troll = (isTroll(name) != -1),
								post = body.getElementsByClassName("post-message")[0];

						post._parent = parent;

						img.className = "troll";
						parent.insertBefore(img, parent.firstChild.nextSibling);

						parent.setAttribute("troll", troll);

						names[i].innerHTML = names[i].innerHTML.replace("Tubasing", "Tubashit");

						if (typeof(comments[name]) == "undefined")
							comments[name] = [];

						comments[name].push({
							parent: parent,
							post: post
						});

						img.addEventListener("click", function(e)
						{
							let troll = toggleTroll(post);
							if (troll)
								trollAdd(name);
							else
								trollRemove(name);

							for(let i = 0; i < comments[name].length; i++)
							{
								if (post === comments[name][i].post)
									continue;

								toggleTroll(comments[name][i].post);
							}
						}, false);



						let timer,
								prev,
								eventHandler = function(e)
								{
									if (parent.getAttribute("troll") != "true")
										return;

									let type = e.type == "mouseenter";

									if (prev == e.type)
										return;

									prev = e.type;
									clearTimeout(timer);
									timer = setTimeout(function()
									{
										censorText(post, type);
									}, type ? 100 : 1000);
								};
						body.addEventListener("mouseenter", eventHandler, false);
						body.addEventListener("mouseleave", eventHandler, false);

						if (!troll)
							continue;

						censorText(post);
					}
				}, 100);
			}, 100);
		}, false);
	}
}//disqus
if (document.readyState != "loading")
	func();
else
	document.addEventListener("DOMContentLoaded", func ,true);