Plex now playing badge

Display a badge on favicon with a number of users streaming from the server

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Plex now playing badge
// @namespace   V@no
// @description Display a badge on favicon with a number of users streaming from the server
// @license     MIT
// @include     http://localhost:32400/web/*
// @include     http://127.0.0.1:32400/web/*
// @include     https://localhost:32400/web/*
// @include     https://127.0.0.1:32400/web/*
// @include     https://app.plex.tv/desktop
// @include     https://app.plex.tv/desktop/*
// @include     https://app.plex.tv/desktop#*
// @icon        data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACVklEQVRYhc2Xy08TURTGZ6NAn7RMp1xo587ChVUTY9mCCfFPMFZicGlMNNGli7YoIsYBNDyCDyKmQVGpQMJC0aVuTKCkoFCbEhQoCNrUkBDWn4uWR8s0tHVu603O+vzuPed857scpRTFDO6/Bxi4VIWAhyjGpJvglENkC1B3UsSEWxkg4CHwX6liC0Apxc2ztowAU02VcNXZ2QIcPUIxmfYKU16CYLMVM3cEfG6yQpIYN2Grqzrl1tO3rfh6V8CcbMG3Dh5yI2ELIEkUn24QBLwE082J5KE2HuEHFYh0mRHpNMN5LPeGzGkMz9XaEbxViS+tAuaSyee7zfj+sByLT4x457awBaCUYvgawew9C8L3d5Mv9RkRfWbAqk+PC2eq2QI4HSJmZR6RTjMWek1YTCb/OaDD+qAW4UeGnBoyLyWUGwnme0z48bgc0f5E8l8vtYj5NYiPlKHrssAWQJIogjKPpT4jVnx6rA9qERvS4M9oKTbGShAfLUXNiey0Ie9d0FBvw/LT5O1faREfLsPGWAk23x7G1vtD+NhhYAtAKcW4h8facx1+v9bgA8flFf8EUHNcRNSnR2yo2AD+XYBsjyoA414eay90KS9QMICGehui/YZ9PbB9OI5TDFUAJIki2FaRMgUFBZAvEiz0mlJ04KASqAbgdIgItfM7SricVMKCAYxcJwi1798FmQBULYGr1o6ZFqviNmQOsG1IMvkB5iXYsWQZHBFTgHRTmu4JJ1oEtmOoZMv3uuLzp23sAA76mLy5mnDDzKQ4269ZUZbR3ijKOlYD4C/uwVlNS+Cv+wAAAABJRU5ErkJggg==
// @ require     https://openuserjs.org/src/libs/sizzle/GM_config.js
// @author      V@no
// @version     2.5.0
// @grant       GM_registerMenuCommand

// ==/UserScript==

var log = console.log.bind(console),
		prefsDefault = {
			position: 2, //0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
			offsetX: 0, //move badge away from x egde
			offsetY: 0, //move badge away from y edge
			textSize: 0, //text size, 0 = auto, 1 = 5px, 2 = 10px, so on
			textMargin: -1, //margin around text, use negative number for auto scale based on text size
			textColor: "#000000", // text color
			backgroundColor: "#FFFFFF", //background color
			borderColor: "#B90000", //border color
			borderWidth: -1, //border width, -1 = auto based on text size
			borderRadius: 0, //border corners radius
			sizeIcon: 16, //image size in pixels, 0 = original
		},
		optionsMenu = (function()
		{
			let a = document.createElement("button");
			a.innerText = "Plex Badge Config";
			a.href = "#";
			a.addEventListener("click", function(e)
			{
				e.preventDefault();
				configOpen();
			}, false);
			return a;
		})();

function clone(orig)
{
	let obj = {};
	for (let n in orig)
		obj[n] = orig[n];

	return obj;
}

function prefsUpdate ()
{
	for(let n in prefs)
	{
		let pref = prefs[n];
		if (typeof(pref) == "object" || typeof(pref) == "function")
			continue;

		pref = $(configPrefix + n).value;
		pref = fixType.do(prefs[n], pref);

		if (n.indexOf("Color") != -1)
			pref = pref.replace(/[^#a-zA-Z0-9\-]+/g, "");

		if ($(configPrefix + n).value != pref)
			$(configPrefix + n).value = pref;

		prefs[n] = pref;
	}
}

function getPrefs()
{
	return prefs;
}
function drawText(text, prefs)
{
	if (!img.loaded)
		return;

	if (!prefs)
		prefs = getPrefs();

	if (prefs.sizeIcon)
		img.width = img.height = prefs.sizeIcon;
	else
	{
		img.width = img._width;
		img.height = img._height;
	}
	let size = img.height;
	canvas.width = canvas.height = size;
	ctx.save();
	ctx.drawImage(img, 0, 0, size, size);

	if (text)
	{
		let multi = prefs.textSize ? prefs.textSize : size / 16,
				textArray = text.toString().toUpperCase().split(''),
				textHeight = 0,
				textWidth = textArray.reduce(function (prev, cur)
				{
					let px = getPixelMap(cur);
					if (px.length * multi > textHeight)
						textHeight = px.length * multi;

					return prev + px[0].length * multi + 1;
				}, -1),
				borderWidth = prefs.borderWidth == -1 ? multi : prefs.borderWidth,
				textMargin = prefs.textMargin < 0 ? -prefs.textMargin * multi : prefs.textMargin,
				width = textWidth + textMargin * 2 + borderWidth,
				height = textHeight + textMargin * 2 + borderWidth,
				xy = -borderWidth / 2,
//				offsetX = prefs.offsetX < 0 ? -prefs.offsetX * multi : prefs.offsetX,
//				offsetY = prefs.offsetY < 0 ? -prefs.offsetY * multi : prefs.offsetY,
				offsetX = prefs.offsetX * multi,
				offsetY = prefs.offsetY * multi,
				x, y;

		switch (prefs.position)
		{
			case 0:
				x = borderWidth + offsetX;
				y = borderWidth + offsetY;
				break;
			case 1:
				x = size - width - offsetX;
				y = borderWidth + offsetY;
				break;
			default:
			case 2:
				x = size - width - offsetX;
				y = size - height - offsetY;
				break;
			case 3:
				x = borderWidth + offsetX;
				y = size - height - offsetX;
				break;
		}
		ctx.translate(x, y);

		// Draw Box
		ctx.fillStyle = hexToRgbA(prefs.backgroundColor);//backgborderRadius
		ctx.strokeStyle = hexToRgbA(prefs.borderColor);//border

		ctx.lineWidth = borderWidth;
		ctx.borderRadiusRect(xy, xy, width, height, prefs.borderRadius * multi).fill();
		if (borderWidth)
			ctx.borderRadiusRect(xy, xy, width, height, prefs.borderRadius * multi).stroke();

		// Draw Text
		ctx.fillStyle = hexToRgbA(prefs.textColor);
		ctx.translate(textMargin, textMargin);
		let pa = [];
		for(let i = 0; i < textArray.length; i++)
		{
			let px = getPixelMap(textArray[i]),
					_y = 0;

			for (let y = 0; y < px.length; y++)
			{
				let _x = 0;
				for (let x = 0; x < px[y].length; x++)
				{
					if (px[y] && px[y][x])
					{
						ctx.fillRect(_x, _y, 1, 1);
						for(let mx = 0; mx < multi; mx++)
						{
							for(let my = 0; my < multi; my++)
							{
								ctx.fillRect(_x + mx, _y + my, 1, 1);
							}
						}
					}
/*
// double, not bold
					if (multi)
					{
						if (px[y] && px[y][x])
						{
							if (x && px[y] && px[y][x-1])
								ctx.fillRect(_x-1, _y, 1, 1);

							if (y && px[y-1] && px[y-1][x])
								ctx.fillRect(_x, _y-1, 1, 1);
						}
						else
							if ((x && px[y] && px[y][x-1])
									&& (y && x && px[y-1] && px[y-1][x])
									&& !(y && x && px[y-1] && px[y-1][x-1])
								)
							{
								ctx.fillRect(_x-1, _y-1, 1, 1);
							}
					}
*/
					_x += multi;
				}
				_y += multi;

			}
			ctx.translate(px[0].length * multi + 1, 0);
		}
	}
	let data = canvas.toDataURL("image/x-icon");
	ctx.restore();
	ctx.clearRect(0, 0, size, size);
	return data;
}//drawText()

function hexToRgbA(hex)
{
	let c;
	if (/^#(([A-Fa-f0-9]{3}){1,2}|[A-Fa-f0-9]{7,8}|[A-Fa-f0-9]{4})$/.test(hex))
	{
		c = hex.substring(1).split('');
		if(c.length == 3)
				c= [c[0], c[0], c[1], c[1], c[2], c[2], "F", "F"];
		if(c.length == 4)
				c= [c[0], c[0], c[1], c[1], c[2], c[2], c[3], c[3]];

		if (c.length < 7)
			c = c.concat(["F", "F"]);
		else if (c.length < 8)
			c[7] = c[6];

		c = '0x' + c.join('');
		return 'rgba('+[(c>>24)&255, (c>>16)&255, (c>>8)&255, ((c&255)*100/255)/100].join(',') + ')';
	}
	return hex;
}

//borrowed from https://chrome.google.com/webstore/detail/favicon-badges/fjnaohmeicdkcipkhddeaibfhmbobbfm/related?hl=en-US
/**
 * Gets a character's pixel map
 */
function getPixelMap(sym)
{
	let px = PIXELMAPS[sym];
	if (!px)
		px = PIXELMAPS['0'];
	return px;
}
var PIXELMAPS = {
	'0': [
		[1,1,1],
		[1,0,1],
		[1,0,1],
		[1,0,1],
		[1,1,1]
	],
	'1': [
		[0,1,0],
		[1,1,0],
		[0,1,0],
		[0,1,0],
		[1,1,1]
	],
	'2': [
		[1,1,1],
		[0,0,1],
		[1,1,1],
		[1,0,0],
		[1,1,1]
	],
	'3': [
		[1,1,1],
		[0,0,1],
		[0,1,1],
		[0,0,1],
		[1,1,1]
	],
	'4': [
		[1,0,1],
		[1,0,1],
		[1,1,1],
		[0,0,1],
		[0,0,1]
	],
	'5': [
		[1,1,1],
		[1,0,0],
		[1,1,1],
		[0,0,1],
		[1,1,1]
	],
	'6': [
		[1,1,1],
		[1,0,0],
		[1,1,1],
		[1,0,1],
		[1,1,1]
	],
	'7': [
		[1,1,1],
		[0,0,1],
		[0,0,1],
		[0,1,0],
		[0,1,0]
	],
	'8': [
		[1,1,1],
		[1,0,1],
		[1,1,1],
		[1,0,1],
		[1,1,1]
	],
	'9': [
		[1,1,1],
		[1,0,1],
		[1,1,1],
		[0,0,1],
		[1,1,1]
	],
	'X': [
		[1,0,1],
		[0,1,0],
		[1,0,1]
	],
};

//https://stackoverflow.com/a/7838871/2930038
CanvasRenderingContext2D.prototype.borderRadiusRect = function (x, y, w, h, r)
{
	if (w < 2 * r) r = w / 2;
	if (h < 2 * r) r = h / 2;
	this.beginPath();
	this.moveTo(x+r, y);
	this.arcTo(x+w,  y,   x+w, y+h, r);
	this.arcTo(x+w,  y+h, x,   y+h, r);
	this.arcTo(x,    y+h, x,   y,   r);
	this.arcTo(x,    y,   x+w, y,   r);
	this.closePath();
	return this;
};

(function loop()
{
	let button = $("id-7");
	if (!button)
		return setTimeout(loop, 100);

	button.addEventListener("click", function()
	{
		(function loop()
		{
			let n = $("id-9");
			n = n && n.getElementsByClassName("Menu-menuScroller-1TF6o Scroller-vertical-VScFL Scroller-scroller-3GqQc Scroller-vertical-VScFL Scroller-auto-LsWiW")[0];
			if (!n)
				return setTimeout(loop, 100);

			nav = n;
			let c = n.getElementsByClassName("MenuSeparator-separator-vr6OL");
			c = c && c[c.length-1];
			if (c)
			{
				c.parentNode.insertBefore(c.cloneNode(false), c);
				c.parentNode.insertBefore(optionsMenu, c);
				optionsMenu.className = "MenuItem-menuItem-1s02m MenuItem-default-2SKQ8 Link-link-2n0yJ Link-default-2XA2b";
			}
		})();
	}, true);
})();
function loop(conf)
{
	let isConf = typeof(conf) != "undefined";
	if (!isConf)
		clearTimeout(loop.timer);

	if (!link || link.parentNode !== head)
	{
		let links = document.getElementsByTagName("link");
		for(let i = 0; i < links.length; i++)
		{
			if (links[i].getAttribute("rel") == "shortcut icon")
			{
				link = links[i];
				break;
			}
		}
		if (link && !img.loaded)
		{
			img.setAttribute('crossOrigin','anonymous');
			img.src = link.href;
			img.onload = function()
			{
				img._width = img.width;
				img._height = img.height;
				img.loaded = true;
			};
		}
	}
	let data,
			act = ["NavBarActivityButton-label-2ZN0g", "activity-badge badge badge-transparent", "NavBarActivityButton-label-WHdP8x"],
			activityBox = null;
	
	for(let i = 0; i < act.length; i++)
	{
		let span = document.getElementsByClassName(act[i]);
		if (!span.length)
			continue;

		activityBox = span[0];
	}
	let badge = activityBox ? activityBox.innerText : "",
			text = parseInt(badge);

	if(conf === true)
	{
		text = testField.value === "" ? Math.floor(Math.random() * 99) + 1 : testField.value;
		prev = null;
	}
	else if ((isConf || configOpened) && conf !== false)
		text = testField.value === "" ? Math.floor(Math.random() * 99) + 1 : testField.value;


	if (isNaN(text))
		text = 0;

	if (prev != text && (data = drawText(text)))
	{
		link.href = data;
		testImg.src = data;
		prev = text;
	}
	// if (!nav)
	// {
		nav = document.querySelector(".Menu-menuPortal-EGqW0f");//$("nav-dropdown");
		if (nav)
		{
			let li = optionsMenu;//document.createElement("button"),
					b = nav.querySelector("button"),
					lis = nav.querySelector("button").parentNode.children;
			li.className = b.className;
			for(let i = lis.length - 1; i >= 0; i--)
			{
				let c = lis[i];
				if (c.classList.contains("MenuSeparator-separator-ljlcCS"))
				{
					// li.appendChild(optionsMenu);
					c.parentNode.insertBefore(c.cloneNode(false), c);
					c.parentNode.insertBefore(li, c);
					break;
				}
			}
		}
	// }
	if (!isConf)
		loop.timer = setTimeout(loop, 3000);
}

function $(id)
{
	return document.getElementById(id);
}

function configOpen()
{
	prefsClone = clone(prefs);
	configOpened = true;
	document.body.setAttribute("config", "");
	$("plexNPBConfigBase").style.marginLeft = -$("plexNPBConfigBase").offsetWidth / 2 + "px";
	$("plexNPBConfigBase").style.marginTop = -$("plexNPBConfigBase").offsetHeight / 2 + "px";
	for(let i in prefsDefault)
	{
		let pref = prefsDefault[i];
		$(configPrefix + i).value = prefs[i];
	}
	loop(null);
}

function configClose()
{
	configOpened = false;
	document.body.removeAttribute("config");
	prefs = clone(prefsClone);
	loop(false);
}

function configReset(e)
{
	for(let i in prefsDefault)
	{
		let pref = prefsDefault[i];
		$(configPrefix + i).value = prefsDefault[i];
	}
	prefsClone = clone(prefs);
	prefsUpdate();
	loop(true);
}

function configSave(e)
{
	prefsClone = clone(prefs);
	prefsUpdate();
	ls("pnpbPrefs", prefs);
	loop(true);
	configClose(e);
}

function configInput(e)
{
	if (e.target.id == configPrefix + "test")
	{
		let pos = e.target.selectionEnd, i = 0;
		e.target.value = e.target.value.replace(/[^0-9]+/g, function(a,b,c,d)
		{
			if (pos >= b)
				i += a.length;

			return "";
		}).substring(0, 3);
		e.target.selectionStart = e.target.selectionEnd = pos-i;
	}
	prefsUpdate();
	loop(true);
}

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){}
	}
	return r;
}

function validateNumber(e)
{
	let key = e.keyCode,
			char = String.fromCharCode(e.charCode || e.which).toLowerCase();

	if ((char > "9" || char < "0") &&
			[e.DOM_VK_BACK_SPACE, e.DOM_VK_DELETE, e.DOM_VK_TAB, e.DOM_VK_RETURN, e.DOM_VK_ESCAPE,
					e.DOM_VK_LEFT, e.DOM_VK_RIGHT, e.DOM_VK_UP, e.DOM_VK_DOWN, e.DOM_VK_PAGE_UP,
					e.DOM_VK_PAGE_DOWN, e.DOM_VK_HOME, e.DOM_VK_END, e.DOM_VK_INSERT].indexOf(key) == -1 &&
			(key > e.DOM_VK_F22 || key < e.DOM_VK_F1) &&
			!((e.ctrlKey || e.metaKey) && !char != "c") && //copy
			!((e.ctrlKey || e.metaKey) && !char != "v") && //paste
			!((e.ctrlKey || e.metaKey) && !char != "x") && //cut
			!((e.ctrlKey || e.metaKey) && !char != "z") && //undo
			!((e.ctrlKey || e.metaKey) && !char != "y") //redo
			)
	{
		e.returnValue = false;
		e.preventDefault();
		e.stopPropagation();
	}
}

var prefs = {},
		_prefs = ls("pnpbPrefs") || {},
		prefsClone = {},
		configPrefix = "plexNPBConfig_",
		configOpened = false,
		fixType = {
			string: String,
			number: Number,
			boolean: Boolean,
			do: function(o, n)
			{
				if (typeof(o) in this)
					n = this[typeof(o)](n);

				return n;
			}
		},
		link = null,
		head = document.getElementsByTagName('head')[0],
		prev = null,
		img = new Image(),
		canvas = document.createElement('canvas'),
		ctx = canvas.getContext('2d'),
		size = 16,
		multi,
		nav,
		title = "Plex Badge Config",
		testImg = {},
		testField = {value:""},
		configBase = document.createElement("div"),
		plexNPBConfigBlur = document.createElement("div"),
		style = document.createElement("style");

head.appendChild(style);

plexNPBConfigBlur.id = "plexNPBConfigBlur";
plexNPBConfigBlur.addEventListener("click", function(){configClose();}, false);

configBase.id = "plexNPBConfigBase";
document.body.appendChild(plexNPBConfigBlur);
document.body.appendChild(configBase);
style.innerHTML = function(){/*
#plexNPBConfigBlur
{
	z-index: 99998;
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	bottom: 0;
	background-color: grey;
	opacity: 0.6;
	display: none;
}
body[config] #plexNPBConfigBase,
body[config] #plexNPBConfigBlur
{
	display: block;
}

body[config] #plex > div
{
	filter: blur(5px);
}

#plexNPBConfigBase
{
	z-index: 99999;
	position: absolute;
	left: 50%;
	top: 50%;
	border: 1px solid black;
	background-color: white;
	padding: 7px;
	text-align: center;
	display: none;
	box-shadow: 10px 10px 50px 1px #000;
}
#plexNPBConfigBase *
{
	font-family: arial,tahoma,myriad pro,sans-serif;
	color: #000;
}

#plexNPBConfigBase .config_header
{
	font-size: 20pt;
	margin: 0;
	padding: 0;
	text-align: center;
}

.testicon > div
{
	display: inline-block;
/*
	position: absolute;
	top: 7px;
	left: 7px;
*//*
	width: 32px;
	height: 32px;
	text-align: end;
	vertical-align: middle;
}

.testicon img
{
	vertical-align: middle;
}

#plexNPBConfigBase .config_var
{
	display: table-row;
	margin: 0 0 4px;
}

#plexNPBConfigBase .config_var > *
{
	margin: 3px 3px 3px 0.5em;
	width: 90%;
}

#plexNPBConfigBase .config_var > label
{
	display: table-cell;
	white-space: nowrap;
	text-align: end;
	width: 0;
	vertical-align: middle;
	font-size: 12px;
	font-weight: bold;
	margin-right: 6px;
}

.testicon > div > div
{
	float: left;
}

#plexNPBConfigBase .config_buttons
{
	color: #000;
	text-align: center;
}
#plexNPBConfigBase input
{
	border-width: 1px;
	line-height: initial;
}
#plexNPBConfigBase .saveclose_buttons
{
	margin: 16px 0 10px;
	padding: 2px 12px;
	width: 45%
}
#plexNPBConfigBase #plexNPBConfig_saveBtn
{
	margin-right: 5px;
}
#plexNPBConfigBase #plexNPBConfig_cancelBtn
{
	margin-left: 5px;
}
#plexNPBConfig_test
{
	margin-top: 1em;
	width: 80%;
}
.reset_holder
{
	text-align: right;
}
*/}.toString().slice(14,-3).split("*//*").join("*/");

configBase.innerHTML = function(){/*
<DIV class="config_header block center">Plex Badge Config</DIV>
<DIV class="config_var">
	<LABEL for="##position" class="field_label">Position</LABEL>
	<SELECT id="##position">
		<OPTION value="0">Top-Left</OPTION>
		<OPTION value="1">Top-Right</OPTION>
		<OPTION value="2">Bottom-Right</OPTION>
		<OPTION value="3">Bottom-Left</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##offsetX" class="field_label">Offset X</LABEL>
	<SELECT id="##offsetX">
		<OPTION value="-8">-8</OPTION>
		<OPTION value="-7">-7</OPTION>
		<OPTION value="-6">-6</OPTION>
		<OPTION value="-5">-5</OPTION>
		<OPTION value="-4">-4</OPTION>
		<OPTION value="-3">-3</OPTION>
		<OPTION value="-2">-2</OPTION>
		<OPTION value="-1">-1</OPTION>
		<OPTION value="0">0</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
		<OPTION value="4">4</OPTION>
		<OPTION value="5">5</OPTION>
		<OPTION value="6">6</OPTION>
		<OPTION value="7">7</OPTION>
		<OPTION value="8">8</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##offsetY" class="field_label">Offset Y</LABEL>
	<SELECT id="##offsetY">
		<OPTION value="-8">-8</OPTION>
		<OPTION value="-7">-7</OPTION>
		<OPTION value="-6">-6</OPTION>
		<OPTION value="-5">-5</OPTION>
		<OPTION value="-4">-4</OPTION>
		<OPTION value="-3">-3</OPTION>
		<OPTION value="-2">-2</OPTION>
		<OPTION value="-1">-1</OPTION>
		<OPTION value="0">0</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
		<OPTION value="4">4</OPTION>
		<OPTION value="5">5</OPTION>
		<OPTION value="6">6</OPTION>
		<OPTION value="7">7</OPTION>
		<OPTION value="8">8</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##textSize" class="field_label">Text size</LABEL>
	<SELECT id="##textSize">
		<OPTION value="0">Auto</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##textMargin" class="field_label">Text margin</LABEL>
	<SELECT id="##textMargin">
		<OPTION value="-4">Auto x4</OPTION>
		<OPTION value="-3">Auto x3</OPTION>
		<OPTION value="-2">Auto x2</OPTION>
		<OPTION value="-1">Auto</OPTION>
		<OPTION value="0">0</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
		<OPTION value="4">4</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##textColor" class="field_label">Text color</LABEL>
	<INPUT id="##textColor" size="25" type="text">
</DIV>
<DIV class="config_var">
	<LABEL for="##backgroundColor" class="field_label">Background color</LABEL>
	<INPUT id="##backgroundColor" size="25" type="text">
</DIV>
<DIV class="config_var">
	<LABEL for="##borderColor" class="field_label">Border color</LABEL>
	<INPUT id="##borderColor" size="25" type="text">
</DIV>
<DIV class="config_var">
	<LABEL for="##borderWidth" class="field_label">Border width</LABEL>
	<SELECT id="##borderWidth">
		<OPTION value="-1">Auto</OPTION>
		<OPTION value="0">0</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
		<OPTION value="4">4</OPTION>
		<OPTION value="5">5</OPTION>
		<OPTION value="6">6</OPTION>
		<OPTION value="7">7</OPTION>
		<OPTION value="8">8</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##borderRadius" class="field_label">Border radius</LABEL>
	<SELECT id="##borderRadius">
		<OPTION value="0">0</OPTION>
		<OPTION value="1">1</OPTION>
		<OPTION value="2">2</OPTION>
		<OPTION value="3">3</OPTION>
		<OPTION value="4">4</OPTION>
		<OPTION value="5">5</OPTION>
	</SELECT>
</DIV>
<DIV class="config_var">
	<LABEL for="##sizeIcon" class="field_label">Icon size</LABEL>
	<SELECT id="##sizeIcon">
		<OPTION value="0">Original</OPTION>
		<OPTION value="16">16x16</OPTION>
		<OPTION value="32">32x32</OPTION>
	</SELECT>
</DIV>

<DIV class="testicon">
	<DIV>
		<IMG id="##testicon" name="##testicon">
	</DIV>
	<INPUT id="##test" type="text" placeholder="Enter any number for test">
</DIV>
<DIV class="config_buttons">
	<BUTTON id="##saveBtn" title="Save settings" class="saveclose_buttons">Save</BUTTON>
	<BUTTON id="##cancelBtn" title="Close window" class="saveclose_buttons">Cancel</BUTTON>
	<DIV class="reset_holder">
		<A id="##reset" href="#" title="Reset fields to default values" class="reset" name="##reset">Reset to defaults</A>
	</DIV>
</DIV>

*/}.toString().slice(14,-3).split("*//*").join("*/").replace(/##/g, configPrefix);

for(let i in prefsDefault)
{
	prefs[i] = i in _prefs ? _prefs[i] : prefsDefault[i];
	$(configPrefix + i).addEventListener("input", configInput, true);
	$(configPrefix + i).value = prefs[i];
}
prefsUpdate();

testField = $(configPrefix + "test");
testField.addEventListener("input", configInput, true);

$(configPrefix + "reset").addEventListener("click", function(e)
{
	e.preventDefault();
	e.stopPropagation();
	configReset(e);
}, false);

testField.addEventListener("keypress", validateNumber, false);
$(configPrefix + "saveBtn").addEventListener("click", configSave, false);
$(configPrefix + "cancelBtn").addEventListener("click", configClose, false);
document.body.addEventListener("keypress", function(e)
{
	if (configOpened && e.keyCode == e.DOM_VK_ESCAPE)
		configClose();

	if (configOpened && e.keyCode == e.DOM_VK_RETURN && e.target.id && e.target.id.replace(configPrefix, "") in prefs)
		configSave();

}, true);

testImg = $(configPrefix + "testicon");
loop();

GM_registerMenuCommand(title, function () { configOpen(); });




/*
setTimeout(function()
{
	let prefs = {
		position: 0, //0 = top-left, 1 = top-right, 2 = bottom-right, 3 = bottom-left
		offsetX: 0, //move badge away from x egde
		offsetY: 0, //move badge away from y edge
		textSize: 3, //text size, 0 = auto, 1 = 5px, 2 = 10px, so on
		textMargin: -1, //margin around text, use negative number for auto scale based on text size
		textColor: "#000000", // text color
		backgroundColor: "#FFFFFF", //background color
		borderColor: "#B90000", //border color
		borderWidth: 1, //border width, -1 = auto based on text size
		borderRadius: 0, //border corners radius
		sizeIcon: 32, //image size in pixels, 0 = original
	},
	opt = {
		"tl" : {
			position: 0,
		},
		"tr" : {
			position: 1,
		},
		"br" : {
			position: 2,
		},
		"bl" : {
			position: 3,
		},
		"stl" : {
			sizeIcon: 16,
			textSize: 0,
			position: 0,
		},
		"str" : {
			sizeIcon: 16,
			textSize: 0,
			position: 1,
		},
		"sbr" : {
			sizeIcon: 16,
			textSize: 0,
			position: 2,
		},
		"sbl" : {
			sizeIcon: 16,
			textSize: 0,
			position: 3,
		},
	},
	div = document.createElement("div");
	document.body.appendChild(div);
	div.style.position = "absolute";
	div.style.backgroundColor = "white";
	div.style.zIndex = 9999999;
	for(let o in opt)
	{
		for(let p in opt[o])
			prefs[p] = opt[o][p];

		for(let i = 0; i < 11; i++)
		{
			let img = document.createElement('img'),
					a = document.createElement("a"),
					p = clone(prefs),
					v = i < 10 ? i : "x";
			setTimeout(function()
			{
				img.src = drawText(String(v), p);
				a.href = img.src;
			},100);
			a.setAttribute("download", "plex_" + o + "_" + v + ".png");
			a.appendChild(img);
			div.appendChild(a);
		}
		div.appendChild(document.createElement("br"));
	}
	let im = document.createElement("img");
	im.src = img.src;
	div.appendChild(im);
}, 3000);
*/