// ==UserScript==
// @name           FYTE /Fast YouTube Embedded/ Player
// @description    Hugely improves load speed of pages with lots of embedded Youtube videos by instantly showing clickable and immediately accessible placeholders, then the thumbnails are loaded in background. A fast simple HTML5 direct playback (720p max) can be selected if available for the video.
// @description:ru На порядок ускоряет время загрузки страниц с большим количеством вставленных Youtube-видео. С первого момента загрузки страницы появляются заглушки для видео, которые можно щелкнуть для загрузки плеера, и почти сразу же появляются кавер-картинки с названием видео. В опциях можно включить режим использования упрощенного браузерного плеера (макс. 720p).
// @version        2.5.6
// @include        *
// @exclude        https://www.youtube.com/*
// @author         wOxxOm
// @namespace      wOxxOm.scripts
// @license        MIT License
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_addStyle
// @grant          GM_xmlhttpRequest
// @connect        www.youtube.com
// @connect        youtube.com
// @run-at         document-start
// @icon           data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACABAMAAAAxEHz4AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAwUExURUxpcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuxkb8AAAAPdFJOUwDvH0biMvjOZFW6pxJ6kh7r3iMAAAPDSURBVGje7ZlNaBNBFMeXhNDWpkKgFg9iYlBB6SGwiPQkftaCYATjTaRKiwi1xVaKXpqTpHhosR66p4pQhb209CQGbSweK/TiRYwfFy+NkWBM2pR2nHm73abJzuceRMj/kEzSvl92Z9689+atpjXUUEMN1WgpoRupbH41nTbNUaxzlkIhe0C+M810Ov8zmzL0RGeNeeDThUEkof72N/Fqe/8LJK07sR173yJS0EbEATxFSurZtm9DilqxAV9VAZuWfbPbLBOFqtSBP9f/WxIAV2Bc6H5owiKPG7p+IpFIRG11LsPbEfyVrhvTqeyX1dfmaBiM9gFgjgwrTzJSfncMFq7s3EExJuu5/rHte3hPBvfkff84sbuEBxPkUiLygCC5hDV7CvpUtt81axICZBN9UwHsxYalOMxhIaIC8IVhFlvJtlALIWQl57Um/LquBpjBpkOwin1qADKLB7RD9moqiPz2TcAMqQGa4OI9Av5op/DrMzXAHmz6mw4IxEQA67AW825/bhngAVoBMEHzZD+aFQCsQUCkAAor/M2wCYAVdwCqxJmANgD8cmJjPQDt5wK22AD0nAVoBsAiE1BMcgAbAJikAqoTYP1CA4BEtBgdgC6yARUuAC3QI7sDiLMAxUk2YAwiIwNAn4YAhGU+YKcOqAUMCgJQziugHGMALmNAhANAWxkaoEgABS4ADdMyiyiglPMIcJ0GKQAayDAAGQEAuu8VUB/gJAH1AS4IgLAwAA24AAoygAeuAPFbqHPHoNwc1HuCJCDncRl7NG8At7Ak48qugVEGsOBxO7snB58T0ngASlwWjomFpMegOusxrFOLBCexsFMbvUzxCyVXRqEkBpjlpXdOgcEqFlsEKpRynFviMIus0md+kcUEDAuUeaxCcysjUGgySt1yTKTUZRTbOaFim17unxUr92doBw4f9zTKObGInZl+//NTW592VP3g+Q4Onh6Ovjfgt5vsPoSCJuDuPRz/58CFmhEtKPIEvY8kZAd3VxRxRJJSyIXcUu0/VOz3okITJRC2ex9kGdB5ecBVZLtgCyt70fUB2nGTTjOu/HFZohsXXLoOrbQKfDps1ePtTj9wSter2oGWoBnYRZqB+bQ5OnLaShpnrNAz6N6R7OW1I1HJjnmPVFuit7eDV1jNvuAkpJNqgJ0DQPCHiv3dqmULfJe3P7hrB/oej3T0S/Tme7tf1Xp/MArPB/Ayp82X5OlAaJfI8wHsJ2/zWXg6EGV4XXB5CbuN3mUYxnQKNI6HU9i3op0y3tpQQw39b/oLfDt0HcsiqWsAAAAASUVORK5CYII=
// @compatible     chrome
// @compatible     firefox
// @compatible     opera
// ==/UserScript==

/* jshint lastsemic:true, multistr:true, laxbreak:true, -W030, -W041, -W084 */

var resizeMode = GM_getValue('resize', 'Fit to width');
if (typeof resizeMode != 'string')
	resizeMode = resizeMode ? 'Fit to width' : 'Original';

var resizeWidth = GM_getValue('width', 1280) |0;
var resizeHeight = GM_getValue('height', 720) |0;
updateCustomSize();

var playDirectly = !!GM_getValue('playHTML5', false);
var skipCustom = !!GM_getValue('skipCustom', true);
var showStoryboard = !!GM_getValue('showStoryboard', true);
var pinnable = GM_getValue('pinnable', 'on');
if (!/^(on|hide|off)$/.test(pinnable))
	pinnable = !!pinnable ? 'on' : 'hide';

var _ = initTL();

var imageLoader = document.createElement('img');
var imageLoader2 = document.createElement('img');

var fytedom = document.getElementsByClassName('instant-youtube-container');
var iframes = document.getElementsByTagName('iframe');
var oembeds = document.getElementsByTagName('embed');
var persite = (function() {
	var rules = [
		{host: /(^|\.)google\.\w{2,3}(\.\w{2,3})?$/, class:'g-blk', query: 'a[href*="youtube.com/watch"][data-ved]', eatparent: 1},
		{host: 'pikabu.ru', class:'b-video', match: '[data-url*="youtube.com/embed"]', attr: 'data-url'},
		{host: 'androidauthority.com', tag:'iframe', match: '[src*="youtube.com/embed"]', eatparent: '.video-container'},
		{host: 'reddit.com', tag:'iframe',
			match: '[data-url*="youtube.com/"] [src*="/mediaembed"], [data-url*="youtu.be/"] [src*="/mediaembed"]',
			src: function(e) { return e.closest('[data-url*="youtube.com/"], [data-url*="youtu.be/"]').dataset.url }},
	];
	for (var i=0, rule; (i<rules.length) && (rule=rules[i]); i++) {
		var rx = rule.host instanceof RegExp ? rule.host : new RegExp('(^|\\.)' + rule.host.replace(/\./g, '\\.') + '$', 'i');
		if (rx.test(location.hostname))
			return {
				nodes: rule.class ? document.getElementsByClassName(rule.class) : document.getElementsByTagName(rule.tag),
				match: rule.match ? function(e) { return e.matches(rule.match) ? e : null }
				                  : function(e) { return e.querySelector(rule.query) },
				attr: rule.attr,
				src:  rule.src,
				eatparent: rule.eatparent,
			};
	}
})();

findEmbeds();
injectStylesIfNeeded();
new MutationObserver(findEmbeds).observe(document, {subtree:true, childList:true});

document.addEventListener('DOMContentLoaded', function(e) {
	injectStylesIfNeeded();
	adjustNodesIfNeeded(e);
});
window.addEventListener('resize', adjustNodesIfNeeded, true);
window.addEventListener('message', function(e) {
	if (e.data == 'iframe-allowfs') {
		$$('iframe:not([allowfullscreen])').some(function(iframe) {
			if (iframe.contentWindow == e.source) {
				iframe.allowFullscreen = true;
				return true;
			}
		});
		if (window != window.top)
			window.parent.postMessage('iframe-allowfs', '*');
	}
});

function findEmbeds(mutations) {
	var i, len, e;
	if (mutations && mutations.length == 1 && !mutations[0].addedNodes.length)
		return;
	if (persite)
		for (i=0, len=persite.nodes.length; (i<len) && (e=persite.nodes[i]); i++)
			if (e = persite.match(e))
				processEmbed(e, persite.src && persite.src(e) || e.getAttribute(persite.attr));
	for (i=0, len=iframes.length; (i<len) && (e=iframes[i]); i++)
		if (/youtube\.com(\/|%2F)(embed|v)(\/|%2F)/i.test(e.src))
			processEmbed(e);
	for (i=0, len=oembeds.length; (i<len) && (e=oembeds[i]); i++)
		if (/youtube\.com(\/|%2F)(embed|v)\//i.test(e.src))
			processEmbed(e);
}

function processEmbed(node, src) {
	function decodeEmbedUrl(url) {
		return url.indexOf('youtube.com%2Fembed') > 0
			 ? decodeURIComponent(url.replace(/^.*?(http[^&?=]+?youtube.com%2Fembed[^&]+).*$/i, '$1'))
		     : url;
	}
	src = src || node.src || node.href || '';
	var n = node;
	var np = n.parentNode, npw;
	var srcFixed = decodeEmbedUrl(src).replace(/\/(watch\?v=|v\/)/, '/embed/');
	if (src.indexOf('cdn.embedly.com/') > 0 ||
		resizeMode != 'Original' && np && np.children.length == 1 && !np.className && !np.id)
	{
		n = location.hostname == 'disqus.com' ? np.parentNode : np;
		np = n.parentElement;
	}
	if (!np ||
		!np.parentNode ||
		skipCustom && srcFixed.indexOf('enablejsapi=1') > 0 ||
		node.matches('.instant-youtube-embed, .YTLT-embed') ||
		node.onload // skip some retarded loaders
	)
		return;

	var id = srcFixed.match(/(?:embed\/|v[=\/]|youtu\.be\/)([^\s,.()\[\]?]+?)(?:[&?\/].*|$)/);
	if (!id)
		return;
	id = id[1];

	var autoplay = srcFixed.indexOf('autoplay=1') > 0;

	if (np.localName == 'object')
		n = np, np = n.parentElement;

	var eatparent = persite && persite.eatparent || 0;
	if (typeof eatparent == 'string')
		n = np.closest(eatparent) || n, np = n.parentElement;
	else
		while (eatparent--)
			n = np, np = n.parentElement;

	injectStylesIfNeeded('force');

	var div = document.createElement('div');
	div.className = 'instant-youtube-container';
	div.FYTE = {
		state: 'querying',
		srcEmbed: srcFixed.replace(/&$/, ''),
		videoID: id,
		originalWidth: /%/.test(node.width) ? 320 : node.width|0 || n.clientWidth|0,
		originalHeight: /%/.test(node.height) ? 200 : node.height|0 || n.clientHeight|0,
	};
	div.FYTE.srcEmbedFixed = div.FYTE.srcEmbed.replace(/^http:/, 'https:').replace(/&?wmode=\w+/, '').replace(/[?&]feature=oembed/, '');
	div.FYTE.srcWatchFixed = div.FYTE.srcEmbedFixed.replace(/\/embed\//, '/watch?v=');

	var divSize = calcContainerSize(div, n);
	var origStyle = getComputedStyle(n);
	div.style.cssText = important('background-color:transparent; transition:background-color 2s;' +
		(origStyle.hasOwnProperty('position') ? Object.keys(origStyle) : Object.keys(origStyle.__proto__) /*FF*/)
			.filter(function(k) { return !!k.match(/^(position|left|right|top|bottom)$/) })
			.map(function(k) { return k + ':' + origStyle[k] })
			.join(';')
			.replace(/\b[^;:]+:\s*(auto|static|block)\s*(!\s*important)?;/g, '') +
		(origStyle.display == 'inline' ? ';display:inline-block;width:100%' : '') +
		';min-width:' + Math.min(divSize.w, div.FYTE.originalWidth) + 'px' +
		';min-height:' + Math.min(divSize.h, div.FYTE.originalHeight) + 'px' +
		(resizeMode == 'Fit to width' ? ';width:100%' : '') +
		';max-width:' + divSize.w + 'px; height:' + divSize.h + 'px;');
	if (!autoplay) {
		setTimeout(function() { div.style.backgroundColor = '' }, 0);
		setTimeout(function() { div.style.transition = '' }, 2000);
	}

	// consume parents of retardedly positioned videos
	if (div.style.position.match('absolute|relative')) {
		if (np.children.length == 1 && floatPadding(np, getComputedStyle(np, ':after'), 'Top') >= div.FYTE.originalHeight)
			n = np, np = n.parentElement;
		div.style.cssText = div.style.cssText.replace(/\b(position|left|top|right|bottom):[^;]+/g, '');
	}

	var wrapper = div.appendChild(document.createElement('div'));
	wrapper.className = 'instant-youtube-wrapper';

	var img = wrapper.appendChild(document.createElement('img'));
	img.className = 'instant-youtube-thumbnail';
	img.src = 'https://i.ytimg.com/vi/' + id + '/maxresdefault.jpg';
	img.style.cssText = important('transition:opacity 0.1s ease-out; opacity:0; padding:0; margin:auto; position:absolute; left:0; right:0; top:0; bottom:0; max-width:none; max-height:none;');

	img.title = _('Shift-click to use alternative player');
	img.onload = function(e) {
		if (img.naturalWidth <= 120)
			return img.onerror(e);
		var fitToWidth = true;
		if (img.naturalHeight) {
			var ratio = img.naturalWidth / img.naturalHeight;
			if (ratio > 4.1/3 && ratio < divSize.w/divSize.h) {
				img.style.cssText += important('width:auto; height:100%;');
				fitToWidth = false;
			}
		}
		if (fitToWidth) {
			img.style.cssText += important('width:100%; height:auto;');
		}
		if (div.FYTE.videoWidth)
			fixThumbnailAR(div);
		if (!autoplay)
			img.style.opacity = 1;
	};
	img.onerror = function(e) {
		if (img.src.indexOf('maxresdefault') > 0)
			img.src = img.src.replace('maxresdefault','hqdefault');
	};

	GM_xmlhttpRequest({
		method: 'GET',
		url: 'https://www.youtube.com/get_video_info?video_id=' + div.FYTE.videoID + '&el=detailpage',
		headers: {'Accept-Encoding': 'gzip'},
		context: div,
		onload: parseVideoInfo
	});

	translateHTML(wrapper, 'beforeend', '\
		<a class="instant-youtube-title" target="_blank" href="' + div.FYTE.srcWatchFixed + '">&nbsp;</a>\
		<svg class="instant-youtube-play-button"><path fill-rule="evenodd" clip-rule="evenodd" fill="#1F1F1F" class="ytp-large-play-button-svg" d="M84.15,26.4v6.35c0,2.833-0.15,5.967-0.45,9.4c-0.133,1.7-0.267,3.117-0.4,4.25l-0.15,0.95c-0.167,0.767-0.367,1.517-0.6,2.25c-0.667,2.367-1.533,4.083-2.6,5.15c-1.367,1.4-2.967,2.383-4.8,2.95c-0.633,0.2-1.316,0.333-2.05,0.4c-0.767,0.1-1.3,0.167-1.6,0.2c-4.9,0.367-11.283,0.617-19.15,0.75c-2.434,0.034-4.883,0.067-7.35,0.1h-2.95C38.417,59.117,34.5,59.067,30.3,59c-8.433-0.167-14.05-0.383-16.85-0.65c-0.067-0.033-0.667-0.117-1.8-0.25c-0.9-0.133-1.683-0.283-2.35-0.45c-2.066-0.533-3.783-1.5-5.15-2.9c-1.033-1.067-1.9-2.783-2.6-5.15C1.317,48.867,1.133,48.117,1,47.35L0.8,46.4c-0.133-1.133-0.267-2.55-0.4-4.25C0.133,38.717,0,35.583,0,32.75V26.4c0-2.833,0.133-5.95,0.4-9.35l0.4-4.25c0.167-0.966,0.417-2.05,0.75-3.25c0.7-2.333,1.567-4.033,2.6-5.1c1.367-1.434,2.967-2.434,4.8-3c0.633-0.167,1.333-0.3,2.1-0.4c0.4-0.066,0.917-0.133,1.55-0.2c4.9-0.333,11.283-0.567,19.15-0.7C35.65,0.05,39.083,0,42.05,0L45,0.05c2.467,0,4.933,0.034,7.4,0.1c7.833,0.133,14.2,0.367,19.1,0.7c0.3,0.033,0.833,0.1,1.6,0.2c0.733,0.1,1.417,0.233,2.05,0.4c1.833,0.566,3.434,1.566,4.8,3c1.066,1.066,1.933,2.767,2.6,5.1c0.367,1.2,0.617,2.284,0.75,3.25l0.4,4.25C84,20.45,84.15,23.567,84.15,26.4z M33.3,41.4L56,29.6L33.3,17.75V41.4z"></path><polygon fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" points="33.3,41.4 33.3,17.75 56,29.6"></polygon></svg>\
		<span tl class="instant-youtube-link">' + (playDirectly ? 'Play with Youtube player' : 'Play directly (up to 720p)') + '</span>\
		<div tl class="instant-youtube-options-button">Options</div>\
	');

	np.insertBefore(div, n);
	n.remove();

	if (autoplay)
		return startPlaying(div);

	div.addEventListener('click', clickHandler);
}

function adjustNodesIfNeeded(e) {
	if (!fytedom[0])
		return;
	if (adjustNodesIfNeeded.scheduled)
		clearTimeout(adjustNodesIfNeeded.scheduled);
	adjustNodesIfNeeded.scheduled = setTimeout(function() {
		adjustNodes(e);
		adjustNodesIfNeeded.scheduled = 0;
	}, 16);
}

function adjustNodes(e, clickedContainer) {
	var force = !!clickedContainer;
	var nearest = force ? clickedContainer : null;

	var vids = $$('.instant-youtube-container:not([pinned])');

	if (!nearest && e.type != 'DOMContentLoaded') {
		var minDistance = window.innerHeight*3/4 |0;
		var nearTargetY = window.innerHeight/2;
		vids.forEach(function(n) {
			var bounds = n.getBoundingClientRect();
			var distance = Math.abs((bounds.bottom + bounds.top)/2 - nearTargetY);
			if (distance < minDistance) {
				minDistance = distance;
				nearest = n;
			}
		});
	}

	if (nearest) {
		var bounds = nearest.getBoundingClientRect();
		var nearestCenterYpct = (bounds.top + bounds.bottom)/2 / window.innerHeight;
	}

	var resized = false;

	vids.forEach(function(n) {
		var size = calcContainerSize(n);
		var w = size.w, h = size.h;

		// prevent parent clipping
		for (var e=n.parentElement, style; e; e=e.parentElement)
			if (e.style.overflow != 'visible' && (style=getComputedStyle(e)))
				if ((style.overflow+style.overflowX+style.overflowY).match(/hidden|scroll/))
					if (n.offsetTop < e.clientHeight / 2 && n.offsetTop + n.clientHeight > e.clientHeight)
						e.style.cssText = e.style.cssText.replace(/\boverflow(-[xy])?:[^;]+/g, '') +
							important('overflow:visible;overflow-x:visible;overflow-y:visible;');

		if (force && Math.abs(w - parseFloat(n.style.maxWidth)) <= 2)
			return;

		if (n.style.maxWidth != w + 'px') n.style.maxWidth = w + 'px';
		if (n.style.height != h + 'px')   n.style.height = h + 'px';
		if (parseFloat(n.style.minWidth) > w) n.style.minWidth = n.style.maxWidth;
		if (parseFloat(n.style.minHeight) > h) n.style.minHeight = n.style.height;

		fixThumbnailAR(n);
		resized = true;
	});

	if (resized && nearest)
		setTimeout(function() {
			var bounds = nearest.getBoundingClientRect();
			var h = bounds.bottom - bounds.top;
			var projectedCenterY = nearestCenterYpct * window.innerHeight;
			var projectedTop = projectedCenterY - h/2;
			var safeTop = Math.min(Math.max(0, projectedTop), window.innerHeight - h);
			window.scrollBy(0, bounds.top - safeTop);
		}, 16);
}

function calcContainerSize(div, origNode) {
	var w, h;
	origNode = origNode || div;
	switch (resizeMode) {
		case 'Original':
			w = div.FYTE.originalWidth;
			h = div.FYTE.originalHeight;
			break;
		case 'Custom':
			w = resizeWidth;
			h = resizeHeight;
			break;
		case '1080p':
		case '720p':
		case '480p':
		case '360p':
			h = parseInt(resizeMode);
			w = h / 9 * 16;
			break;
		default: // fit-to-width mode
			var n = origNode;
			do {
				n = n.parentElement;
				// find parent node with nonzero width (i.e. independent of our video element)
			} while (n && !(w = n.clientWidth));
			if (w)
				h = w / 16 * 9;
			else {
				w = origNode.clientWidth;
				h = origNode.clientHeight;
			}
	}
	var np = origNode.parentElement;
	var style = getComputedStyle(np);
	var parentWidth = parseFloat(style.width) - floatPadding(np, style, 'Left') - floatPadding(np, style, 'Right');
	if (parentWidth > 0 && parentWidth < w) {
		h = parentWidth / w * h;
		w = parentWidth;
	}
	if (resizeMode == 'Fit to width' && h < div.FYTE.originalHeight*0.9)
		h = Math.min(div.FYTE.originalHeight, w / div.FYTE.originalWidth * div.FYTE.originalHeight);

	return {w: window.chrome ? w : Math.round(w), h:h};
}

function parseVideoInfo(response) {
	var div = response.context;
	var txt = response.responseText;
	var info = tryCatch(function() { return JSON.parse(txt.replace(/(\w+)=?(.*?)(&|$)/g, '"$1":"$2",').replace(/^(.+?),?$/, '{$1}')) }) || {};
	var videoSources = [];

	// parse width & height to adjust the thumbnail
	var m = decodeURIComponent(info.adaptive_fmts || '').match(/size=(\d+)x(\d+)\b/) ||
	        decodeURIComponent(txt).match(/\/(\d+)x(\d+)\//);
	if (m)
		fixThumbnailAR(div, m[1]|0, m[2]|0);

	// parse video sources
	if (info.url_encoded_fmt_stream_map && info.fmt_list) {
		var streams = {};
		decodeURIComponent(info.url_encoded_fmt_stream_map).split(',').forEach(function(stream) {
			var params = {};
			stream.split('&').forEach(function(kv) {
				params[kv.split('=')[0]] = decodeURIComponent(kv.split('=')[1]);
			});
			streams[params.itag] = params;
		});
		decodeURIComponent(info.fmt_list).split(',').forEach(function(fmt) {
			var itag = fmt.split('/')[0];
			var dimensions = fmt.split('/')[1];
			var stream = streams[itag];
			if (stream) {
				videoSources.push({
					src: stream.url,
					title: stream.quality + ', ' + dimensions + ', ' + stream.type
				});
			}
		});
	} else {
		var rx = /url=([^=]+?mime%3Dvideo%252F(?:mp4|webm)[^=]+?)(?:,quality|,itag|.u0026)/g;
		var text = decodeURIComponent(txt).split('url_encoded_fmt_stream_map')[1];
		while (m = rx.exec(text)) {
			videoSources.push({
				src: decodeURIComponent(decodeURIComponent(m[1]))
			});
		}
	}

	var duration = div.FYTE.duration = info.length_seconds|0 || '';
	if (duration) {
		var d = new Date(null);
		d.setSeconds(duration);
		duration = d.toISOString().replace(/^.+?T[0:]{0,4}(.+?)\..+$/, '<span>$1</span>');
	}
	var title = decodeURIComponent(info.title || info.reason || '').replace(/\+/g, ' ');
	if (title || duration)
		$(div, '.instant-youtube-title').innerHTML = (title ? '<strong>' + title + '</strong>' : '') + duration;
	if (pinnable != 'off' && info.title)
		makeDraggable(div);
	if (info.reason)
		div.setAttribute('disabled', '');

	if (videoSources.length)
		div.FYTE.videoSources = videoSources;

	if (info.storyboard_spec) {
		m = decodeURIComponent(decodeURIComponent(info.storyboard_spec)).split('|');
		div.FYTE.storyboard = JSON.parse(m[m.length-1].replace(/^(\d+)#(\d+)#(\d+)#(\d+)#(\d+)#.+$/, '{"w":$1, "h":$2, "len":$3, "rows":$4, "cols":$5}'));
		div.FYTE.storyboard.url = m[0].replace('$L/$N.jpg', (m.length-2) + '/M0.jpg?sigh=' + m[m.length-1].replace(/^.+?#([^#]+)$/, '$1'));
		$(div, '.instant-youtube-options-button').insertAdjacentHTML('beforebegin',
			'<div class="instant-youtube-storyboard"' + (showStoryboard ? '' : ' disabled') + '>' +
				important('<div style="width:' + (div.FYTE.storyboard.w-1) + 'px; height:' + div.FYTE.storyboard.h + 'px;') +
				'">&nbsp;</div>' +
			'</div>');
		if (showStoryboard)
			updateHoverHandler(div);
	}

	injectStylesIfNeeded();

	if (div.FYTE.state == 'scheduled play')
		setTimeout(function() { startPlayingDirectly(div) }, 0);

	div.FYTE.state = '';
}

function fixThumbnailAR(div, w, h) {
	var img = $(div, 'img');
	var thw = img.naturalWidth, thh = img.naturalHeight;
	if (w && h) { // means thumbnail is still loading
		div.FYTE.videoWidth = w;
		div.FYTE.videoHeight = h;
	} else {
		w = div.FYTE.videoWidth;
		h = div.FYTE.videoHeight;
		if (!w || !h)
			return;
	}
	var divw = div.clientWidth, divh = div.clientHeight;
	// if both video and thumbnail are 4:3, fit the image to height
	//console.log(div, divw, divh, thw, thh, w, h, h/w*divw / divh - 1, thh/thw*divw / divh - 1);
	if (Math.abs(h/w*divw / divh - 1) > 0.05 && Math.abs(thh/thw*divw / divh - 1) > 0.05) {
		img.style.maxHeight = img.clientHeight + 'px';
		if (!div.FYTE.videoWidth) // skip animation if thumbnail is already loaded
			img.style.transition = 'height 1s ease, margin-top 1s ease';
		setTimeout(function() {
			img.style.maxHeight = 'none';
			img.style.cssText += important(h/w >= divh/divw ? 'width:auto; height:100%;' : 'width:100%; height:auto;');
			setTimeout(function() {
				img.style.transition = '';
			}, 1000);
		}, 0);
	}
}

function updateHoverHandler(div) {
	var sb = $(div, '.instant-youtube-storyboard');
	if (!showStoryboard) {
		if (!sb.getAttribute('disabled'))
			sb.setAttribute('disabled', '');
		return;
	}
	if (sb.hasAttribute('disabled'))
		sb.removeAttribute('disabled');

	sb.addEventListener('click', storyboardClickHandler);

	var oldIndex = null;
	var style = sb.firstElementChild.style;
	sb.addEventListener('mousemove', storyboardHoverHandler);
	sb.addEventListener('mouseout', storyboardHoverHandler);

	div.addEventListener('mouseover', storyboardPreloader);
	div.addEventListener('mouseout', storyboardPreloader);

	var spinner = document.createElement('span');
	spinner.className = 'instant-youtube-loading-button';

	function storyboardClickHandler(e) {
		sb.removeEventListener('click', storyboardClickHandler);
		var offsetX = e.offsetX || e.clientX - this.getBoundingClientRect().left;
		div.FYTE.startAt = offsetX / this.clientWidth * div.FYTE.duration |0;
		div.FYTE.srcEmbedFixed = setUrlParams(div.FYTE.srcEmbedFixed, {start: div.FYTE.startAt});
		startPlaying(div, {alternateMode: e.shiftKey});
	}

	function storyboardPreloader(e) {
		if (e.type == 'mouseout') {
			imageLoader.onload = null; imageLoader.src = '';
			spinner.remove();
			return;
		}
		if (!div.FYTE.storyboard || div.FYTE.storyboard.preloaded)
			return;
		var lastpart = (div.FYTE.storyboard.len-1)/(div.FYTE.storyboard.rows * div.FYTE.storyboard.cols) |0;
		if (lastpart <= 0)
			return;
		var part = 0;
		imageLoader.src = setStoryboardUrl(part++);
		imageLoader.onload = function() {
			if (part > lastpart) {
				div.FYTE.storyboard.preloaded = true;
				div.removeEventListener('mouseover', storyboardPreloader);
				div.removeEventListener('mouseout', storyboardPreloader);
				imageLoader.onload = null;
				imageLoader.src = '';
				spinner.remove();
				return;
			}
			imageLoader.src = setStoryboardUrl(part++);
		};
	}

	function setStoryboardUrl(part) {
		return div.FYTE.storyboard.url.replace(/M\d+\.jpg\?/, 'M' + part + '.jpg?');
	}

	function storyboardHoverHandler(e) {
		if (!showStoryboard || !div.FYTE.storyboard)
			return;
		if (e.type == 'mouseout')
			return imageLoader2.onload && imageLoader2.onload();
		var w = div.FYTE.storyboard.w;
		var h = div.FYTE.storyboard.h;
		var cols = div.FYTE.storyboard.cols;
		var rows = div.FYTE.storyboard.rows;
		var len = div.FYTE.storyboard.len;
		var partlen = rows * cols;

		var offsetX = e.offsetX || e.clientX - this.getBoundingClientRect().left;
		var left = Math.min(this.clientWidth - w, Math.max(0, offsetX - w)) |0;
		if (!style.left || parseInt(style.left) != left) {
			style.left = left + 'px';
			if (spinner.parentElement)
				spinner.style.cssText = important('left:' + (left + w/2 - 10) + 'px; right:auto;');
		}

		var index = Math.min(offsetX / this.clientWidth * (len+1) |0, len - 1);
		if (index == oldIndex)
			return;

		var part = index/partlen|0;
		if (!oldIndex || part != (oldIndex/partlen|0)) {
			style.cssText = style.cssText.replace(/$|background-image[^;]+;/,
												  'background-image: url(' + setStoryboardUrl(part) + ')!important;');
			if (!div.FYTE.storyboard.preloaded) {
				if (spinner.timer)
					clearTimeout(spinner.timer);
				spinner.timer = setTimeout(function() {
					spinner.timer = 0;
					if (!imageLoader2.src)
						return;
					this.appendChild(spinner);
					spinner.style.cssText = important('left:' + (left + w/2 - 10) + 'px; right:auto;');
				}.bind(this), 50);
				imageLoader2.onload = function() {
					clearTimeout(spinner.timer);
					spinner.remove();
					spinner.timer = 0;
					imageLoader2.onload = null;
					imageLoader2.src = '';
				};
				imageLoader2.src = setStoryboardUrl(part);
			}
		}

		oldIndex = index;
		index = index % partlen;
		style.backgroundPosition = '-' + (index % cols) * w + 'px -' + (index / cols |0) * h + 'px';
	}
}

function clickHandler(e) {
	if (e.target.href)
		return;
	if (e.target.matches('.instant-youtube-options-button')) {
		showOptions(e);
		e.preventDefault();
		e.stopPropagation();
		return;
	}
	if (e.target.matches('.instant-youtube-options, .instant-youtube-options *'))
		return;

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

	var alternateMode = e.shiftKey || e.target.className == 'instant-youtube-link';
	startPlaying(e.target.closest('.instant-youtube-container'), {alternateMode: alternateMode});
}

function startPlaying(div, options) {
	div.removeEventListener('click', clickHandler);

	$$(div, '.instant-youtube-wrapper > *:not(img):not(a)').forEach(function(e) { e.style.cssText = 'display:none!important' });
	$(div, 'svg').outerHTML = '<span class="instant-youtube-loading-button"></span>';

	if (pinnable != 'off') {
		div.firstElementChild.insertAdjacentHTML('beforeend',
			'<div pin="top-left"></div><div pin="top-right"></div><div pin="bottom-right"></div><div pin="bottom-left"></div>');
		$$(div, '[pin]').forEach(function(pin) {
			if (pinnable == 'hide')
				pin.setAttribute('transparent', '');
			pin.onclick = function(e) {
				var pinIt = !div.hasAttribute('pinned') || !pin.hasAttribute('active');
				var corner = pin.getAttribute('pin');
				var video = $(div, 'video');
				if (pinIt) {
					$$(div, '[pin][active]').forEach(function(p) { p.removeAttribute('active') });
					pin.setAttribute('active', '');
					if (!div.FYTE.unpinnedStyle) {
						div.FYTE.unpinnedStyle = div.style.cssText;
						var stub = div.cloneNode();
						var img = $(div, 'img').cloneNode();
						img.style.opacity = 1;
						img.style.display = 'block';
						img.title = '';
						stub.appendChild(img);
						stub.onclick = function(e) { $(div, '[pin][active]').onclick(e) };
						stub.style.cssText += 'opacity:0.3!important;';
						div.FYTE.stub = stub;
						div.parentNode.insertBefore(stub, div);
					}
					div.style.cssText = important(
						'position: fixed;' +
						'contain: inherit;' +
						'width: 400px;' +
						'z-index: 999999999;' +
						'height:' + (400 / div.FYTE.videoWidth * div.FYTE.videoHeight) + 'px;' +
						'top:' + (corner.indexOf('top') >= 0 ? '0' : 'auto') + ';' +
						'bottom:' + (corner.indexOf('bottom') >= 0 ? '0' : 'auto') + ';' +
						'left:' + (corner.indexOf('left') >= 0 ? '0' : 'auto') + ';' +
						'right:' + (corner.indexOf('right') >= 0 ? '0' : 'auto') + ';'
					);
					adjustPinnedOffset(div, corner);
					div.setAttribute('pinned', '');
					if (video && document.body)
						document.body.appendChild(div);
				} else {
					pin.removeAttribute('active');
					div.removeAttribute('pinned');
					div.style.cssText = div.FYTE.unpinnedStyle;
					div.FYTE.unpinnedStyle = '';
					if (div.FYTE.stub) {
						if (video && document.body)
							div.FYTE.stub.parentNode.appendChild(div, div.FYTE.stub);
						div.FYTE.stub.remove();
						div.FYTE.stub = null;
					}
				}
				if (video && video.paused)
					video.play();
			};
		});
		if (options.pin)
			$(div, '[pin="' + options.pin + '"]').click();
	}

	if (window != window.top)
		window.parent.postMessage('iframe-allowfs', '*');

	if ((!!playDirectly + !!options.alternateMode == 1) && (div.FYTE.videoSources || div.FYTE.state == 'querying')) {
		if (div.FYTE.videoSources)
			startPlayingDirectly(div);
		else {
			// playback will start in parseVideoInfo
			div.FYTE.state = 'scheduled play';
			// fallback to iframe in 5s
			setTimeout(function() {
				if (div.FYTE.state) {
					div.FYTE.state = '';
					switchToIFrame.call(div);
				}
			}, 5000);
		}
	}
	else
		switchToIFrame.call(div);
}

function startPlayingDirectly(div) {
	var video = document.createElement('video');
	video.controls = true;
	video.autoplay = true;
	video.style.cssText = important(
		'position:absolute; left:0; top:0; right:0; padding:0; margin:auto; opacity:0; transition:opacity 2s;' +
		'width:100%; height:100%;');
	video.className = 'instant-youtube-embed';
	video.volume = GM_getValue('volume', 0.5);

	div.FYTE.videoSources.forEach(function(src) {
		var srcdom = video.appendChild(document.createElement('source'));
		Object.keys(src).forEach(function(k) { srcdom[k] = src[k] });
		srcdom.onerror = switchToIFrame.bind(div);
	});


	if (window.chrome) {
		video.addEventListener('click', function(e) {
			this.paused ? this.play() : this.pause();
		});
	}
	video.interval = (function() {
		return setInterval(function() {
			if (video.volume != GM_getValue('volume', 0.5))
				GM_setValue('volume', video.volume);
		}, 1000);
	})();
	var title = $(div, '.instant-youtube-title');
	if (title) {
		video.onpause = function() { title.removeAttribute('hidden') };
		video.onplay = function() { title.setAttribute('hidden', true) };
	}
	video.onloadedmetadata = div.FYTE.startAt && function(e) {
		video.currentTime = div.FYTE.startAt;
	};
	video.onloadeddata = function(e) {
		pauseOtherVideos(this);
		div.style.cssText += 'contain:inherit!important'; // allow fullscreen
		div.firstElementChild.appendChild(this);
		div.setAttribute('playing', '');
		this.style.opacity = 1;
		var img = $(div, 'img');
		img.style.transition = 'opacity 1s';
		img.style.opacity = 0;
	};
}

function switchToIFrame(e) {
	var div = this;
	var wrapper = div.firstElementChild;
	if (e) {
		console.log('[FYTE] Direct linking canceled on %s, switching to IFRAME player', div.FYTE.srcEmbed);
		var video = e.target ? e.target.closest('video') : e.path && e.path[e.path.length-1];
		while (video.lastElementChild)
			video.lastElementChild.remove();
	}

	($(div, '[pin]') || wrapper).insertAdjacentHTML(pinnable != 'off' ? 'beforebegin' : 'beforeend',
		'<iframe class="instant-youtube-embed" allowtransparency="true" src="' +
		setUrlParams(div.FYTE.srcEmbedFixed, {
			html5: 1,
			autoplay: 1,
			autohide: 2,
			border: 0,
			controls: 1,
			fs: 1,
			showinfo: 1,
			ssl: 1,
			theme: 'dark',
			enablejsapi: 1,
		}) + '" frameborder="0" allowfullscreen width="100%" height="100%"></iframe>');

	$(div, 'iframe').onload = function() {
		pauseOtherVideos(this);
		this.style.cssText = important(
			'position:absolute; left:0; top:0; right:0; padding:0; margin:auto; opacity:1; transition:opacity 2s;');

		div.setAttribute('iframe', '');
		div.setAttribute('playing', '');
		div.style.cssText += 'contain:none!important'; // allow fullscreen
		setTimeout(function() {
			$(div, 'img').style.display = 'none';
			var title = $(div, '.instant-youtube-title');
			if (title)
				title.remove();
		}, 2000);
	};
}

function setUrlParams(url, params) {
	var names = Object.keys(params);
	url = url.replace(new RegExp('[?&](' + names.join('|') + ')(=[^?&]*)?', 'gi'), '');
	return url +
		(url.indexOf('?') > 0 ? '&' : '?') +
		names.map(function(n) { return n + '=' + params[n] }).join('&');
}

function pauseOtherVideos(activePlayer) {
	$$(activePlayer.ownerDocument, '.instant-youtube-embed').forEach(function(v) {
		if (v == activePlayer)
			return;
		switch (v.localName) {
			case 'video':
				if (!v.paused)
					v.pause();
				break;
			case 'iframe':
				try { v.contentWindow.postMessage('{"event":"command", "func":"pauseVideo", "args":""}', '*') } catch(e) {}
				break;
		}
	});
}

function makeDraggable(div) {
	div.draggable = true;
	div.addEventListener('dragstart', function(e) {
		e.dataTransfer.setData('text/plain', '');

		var dropZone = document.createElement('div');
		var dropZoneHeight = 400 / div.FYTE.videoWidth * div.FYTE.videoHeight;
		dropZone.className = 'instant-youtube-dragndrop-placeholder';

		document.body.addEventListener('dragenter', dragHandler);
		document.body.addEventListener('dragover', dragHandler);
		document.body.addEventListener('dragend', dragHandler);
		document.body.addEventListener('drop', dragHandler);
		function dragHandler(e) {
			e.stopImmediatePropagation();
			e.stopPropagation();
			e.preventDefault();
			switch (e.type) {
				case 'dragover':
					var playing = div.hasAttribute('playing');
					var gizmo = playing
					            ? {left:0, top:0, right:innerWidth, bottom:innerHeight}
					            : div.getBoundingClientRect();
					var x = e.clientX, y = e.clientY;
					var cx = (gizmo.left + gizmo.right) / 2;
					var cy = (gizmo.top + gizmo.bottom) / 2;
					var stay = y >= cy-200 && y <= cy+200 && x >= cx-200 && x <= cx+200;
					overrideCSS(dropZone, {
						top: y < cy || stay ? '0' : 'auto',
						bottom: y > cy || stay ? '0' : 'auto',
						left: x < cx || stay ? '0' : 'auto',
						right: x > cx || stay ? '0' : 'auto',
						width: playing && stay ? (div.FYTE.stub || div).clientWidth+'px' : stay ? '100%' : '400px',
						height: playing && stay  ? (div.FYTE.stub || div).clientHeight+'px' : stay ? '100%' : dropZoneHeight + 'px',
						margin: playing && stay ? 'auto' : '0',
						position: !playing && stay ? 'absolute' : 'fixed',
						'background-color': stay ? 'rgba(255,255,0,0.2)' : 'rgba(0,255,0,0.2)',
					});
					adjustPinnedOffset(dropZone);
					(stay && !playing ? div : document.body).appendChild(dropZone);
					break;
				case 'dragend':
				case 'drop':
					var corner = calcPinnedCorner(dropZone);
					dropZone.remove();
					dropZone = null;
					document.body.removeEventListener('dragenter', dragHandler);
					document.body.removeEventListener('dragover', dragHandler);
					document.body.removeEventListener('dragend', dragHandler);
					document.body.removeEventListener('drop', dragHandler);
					if (e.type == 'dragend')
						break;
					if (div.hasAttribute('playing'))
						(corner ? $(div, '[pin="' + corner + '"]') : div.FYTE.stub).click();
					else
						startPlaying(div, {pin: corner});
			}
		}
	});
}

function adjustPinnedOffset(el, corner) {
	var offset = 0;
	var inverse = el.style.top == '0px' ? 'bottom' : 'top';
	$$('.instant-youtube-container[pinned] [pin="' + (corner || calcPinnedCorner(el)) + '"][active]').forEach(function(pin) {
		var container = pin.closest('[pinned]');
		if (container != el)
			offset = Math.max(offset, container.getBoundingClientRect()[inverse]);
	});
	if (offset)
		el.style[el.style.top == '0px' ? 'top' : 'bottom'] = offset + 'px';
}

function calcPinnedCorner(el) {
	var t = el.style.top != 'auto';
	var b = el.style.bottom != 'auto';
	var l = el.style.left != 'auto';
	var r = el.style.right != 'auto';
	return t && b && l && r ? '' : (t ? 'top' : 'bottom') + '-' + (l ? 'left' : 'right');
}

function showOptions(e) {
	var optionsButton = e.target;
	translateHTML(optionsButton, 'afterend', '\
		<div class="instant-youtube-options">\
			<label tl style="width: 100% !important;">Size:<br>\
				<select data-action="size-mode" style="width:20ex!important">\
					<option tl value="Original">Original\
					<option tl value="Fit to width">Fit to width\
					<option>360p\
					<option>480p\
					<option>720p\
					<option>1080p\
					<option tl value="Custom">Custom...\
				</select>\
			</label>\
			<label data-action="size-custom" ' + (resizeMode != 'Custom' ? 'disabled' : '') + '>\
				<input type="number" min="320" max="9999" tl-placeholder="width" data-action="width" step="1" value="' + (resizeWidth||'') + '">\
				x\
				<input type="number" min="240" max="9999" tl-placeholder="height" data-action="height" step="1" value="' + (resizeHeight||'') + '">\
			</label>\
			<label tl="content,title" title="msgStoryboardTooltip">\
				<input data-action="storyboard" type="checkbox" ' + (showStoryboard ? 'checked' : '') + '>\
				Storyboard thumbs\
			</label>\
			<label tl="content,title" title="msgDirectTooltip">\
				<input data-action="direct" type="checkbox" ' + (playDirectly ? 'checked' : '') + '>\
				Play directly\
			</label>\
			<label tl="content,title" title="Do not process customized videos with enablejsapi=1 parameter (requires page reload)">\
				<input data-action="safe" type="checkbox" ' + (skipCustom ? 'checked' : '') + '>\
				Safe\
			</label>\
			<label tl="content,title" title="msgPinningTooltip">Pinning:\
				<select data-action="pinnable">\
					<option tl value="on">On\
					<option tl value="hide">On, hidden\
					<option tl value="off">Off\
				</select>\
			</label>\
			<span data-action="buttons">\
				<button tl data-action="ok">OK</button>\
				<button tl data-action="cancel">Cancel</button>\
			</span>\
		</div>\
	');
	var options = optionsButton.nextElementSibling;

	options.addEventListener('keydown', function(e) {
		if (e.target.localName == 'input' &&
			!e.shiftKey && !e.altKey && !e.metaKey && !e.ctrlKey && e.key.match(/[.,]/))
			return false;
	});

	$(options, '[data-action="size-mode"]').value = resizeMode;
	$(options, '[data-action="size-mode"]').addEventListener('change', function() {
		var v = this.value != 'Custom';
		var e = $(options, '[data-action="size-custom"]');
		e.children[0].disabled = e.children[1].disabled = v;
		v ? e.setAttribute('disabled', '') : e.removeAttribute('disabled');
	});

	$(options, '[data-action="pinnable"]').value = pinnable;

	$(options, '[data-action="buttons"]').addEventListener('click', function(e) {
		if (e.target.dataset.action != 'ok') {
			options.remove();
			return;
		}
		var v, shouldAdjust;
		if (resizeMode != (v = $(options, '[data-action="size-mode"]').value)) {
			GM_setValue('resize', resizeMode = v);
			shouldAdjust = true;
		}
		if (resizeMode == 'Custom') {
			var w = $(options, '[data-action="width"]').value |0;
			var h = $(options, '[data-action="height"]').value |0;
			if (resizeWidth != w || resizeHeight != h) {
				updateCustomSize(w, h);
				GM_setValue('width', resizeWidth);
				GM_setValue('height', resizeHeight);
				shouldAdjust = true;
			}
		}
		if (showStoryboard != (v = $(options, '[data-action="storyboard"]').checked)) {
			GM_setValue('showStoryboard', showStoryboard = v);
			$$('.instant-youtube-container').forEach(updateHoverHandler);
		}
		if (playDirectly != (v = $(options, '[data-action="direct"]').checked)) {
			GM_setValue('playHTML5', playDirectly = v);
			$$('.instant-youtube-container .instant-youtube-link').forEach(function(e) {
				e.textContent = playDirectly ? 'Play with Youtube player' : 'Play directly (up to 720p)';
			});
		}
		if (skipCustom != (v = $(options, '[data-action="safe"]').checked)) {
			GM_setValue('skipCustom', skipCustom = v);
		}
		if (pinnable != (v = $(options, '[data-action="pinnable"]').value)) {
			GM_setValue('pinnable', pinnable = v);
		}

		options.remove();

		if (shouldAdjust)
			adjustNodes(e, e.target.closest('.instant-youtube-container'));
	});
}

function updateCustomSize(w, h) {
	resizeWidth = Math.min(9999, Math.max(320, w|0 || resizeWidth|0));
	resizeHeight = Math.min(9999, Math.max(240, h|0 || resizeHeight|0));
}

function important(cssText) {
	return cssText.replace(/;/g, '!important;');
}

function tryCatch(func) {
	try {
		return func();
	} catch(e) {
		console.log(e);
	}
}

function getFunctionComment(fn) {
	return fn.toString().match(/\/\*([\s\S]*?)\*\/\s*\}$/)[1];
}

function $(selORnode, sel) {
	return sel ? selORnode.querySelector(sel)
	           : document.querySelector(selORnode);
}

function $$(selORnode, sel) {
	return Array.prototype.slice.call(
		sel ? selORnode.querySelectorAll(sel)
		    : document.querySelectorAll(selORnode));
}

function overrideCSS(e, params) {
	var names = Object.keys(params);
	var style = e.style.cssText.replace(new RegExp('(^|\s|;)(' + names.join('|') + ')(:[^;]+)', 'gi'), '$1');
	e.style.cssText = style.replace(/[^;]\s*$/, '$&;').replace(/^\s*;\s*/, '') +
		names.map(function(n) { return n + ':' + params[n] + '!important' }).join(';') + ';';
}

// fix dumb Firefox bug
function floatPadding(node, style, dir) {
	var padding = style['padding' + dir];
	if (padding.indexOf('%') < 0)
		return parseFloat(padding);
	return parseFloat(padding) * (parseFloat(style.width) || node.clientWidth) / 100;
}

function translateHTML(baseElement, place, html) {
	var tmp = document.createElement('div');
	tmp.innerHTML = html;
	$$(tmp, '[tl]').forEach(function(node) {
		(node.getAttribute('tl') || 'content').split(',').forEach(function(what) {
			var child, src, tl;
			if (what == 'content') {
				for (var i = node.childNodes.length-1, n; (i>=0) && (n=node.childNodes[i]); i--) {
					if (n.nodeType == Node.TEXT_NODE && n.textContent.trim()) {
						child = n;
						break;
					}
				}
			} else
				child = node.getAttributeNode(what);
			if (!child)
				return;
			src = child.textContent;
			srcTrimmed = src.trim();
			tl = src.replace(srcTrimmed, _(srcTrimmed));
			if (src != tl)
				child.textContent = tl;
		});
	});
	baseElement.insertAdjacentHTML(place, tmp.innerHTML);
}

function initTL(src) {
	var tlSource = {
		'watch on Youtube': {
			'ru': 'открыть на Youtube',
		},
		'Play with Youtube player': {
			'ru': 'Включить плеер Youtube',
		},
		'Play directly (up to 720p)': {
			'ru': 'Включить напрямую (макс. 720p)',
		},
		'Shift-click to use alternative player': {
			'ru': 'Shift-клик для смены типа плеера',
		},
		'Options': {
			'ru': 'Опции',
		},
		'Size:': {
			'ru': 'Размер:',
		},
		'Original': {
			'ru': 'Исходный',
		},
		'Fit to width': {
			'ru': 'На всю ширину',
		},
		'Custom...': {
			'ru': 'Настроить...',
		},
		'width': {
			'ru': 'ширина',
		},
		'height': {
			'ru': 'высота',
		},
		'Storyboard thumbs': {
			'ru': 'Раскадровка',
		},
		'msgStoryboardTooltip': {
			'en': 'Show storyboard preview on mouse hover at the bottom',
			'ru': 'Показывать миникадры при наведении мыши на низ кавер-картинки',
		},
		'Play directly': {
			'ru': 'Плеер браузера',
		},
		'msgDirectTooltip': {
			'en': 'Shift-clicking thumbnails will use alternative player',
			'ru': 'Удерживайте клавишу Shift при щелчке на картинке для альтернативного плеера',
		},
		'Safe': {
			'ru': 'Консервативный режим',
		},
		'Pinning': {
			'ru': 'Закрепление',
		},
		'msgPinningTooltip': {
			'en': 'Enable corner pinning controls when a video is playing. \nTo restore the video click the active corner pin or the original video placeholder.',
			'ru': 'Включить шпильки по углам для закрепления видео во время просмотра. \nДля отмены можно нажать еще раз на активированный уголЪ или на заглушку, где исходно было видео',
		},
		'On': {
			'ru': 'Да',
		},
		'On, hide': {
			'ru': 'Да, невидимые',
		},
		'Off': {
			'ru': 'Нет',
		},
		'msgSafe': {
			'en': 'Do not process customized videos with enablejsapi=1 parameter (requires page reload)',
			'ru': 'Не обрабатывать нестандартные видео с параметром enablejsapi=1 (подействует после обновления страницы)',
		},
		'OK': {
			'ru': 'ОК',
		},
		'Cancel': {
			'ru': 'Оменить',
		},
	};
	var browserLang = navigator.language || navigator.languages && navigator.languages[0] || '';
	var browserLangMajor = browserLang.replace(/-.+/, '');
	var tl = {};
	Object.keys(tlSource).forEach(function(k) {
		var langs = tlSource[k];
		var text = langs[browserLang] || langs[browserLangMajor];
		if (text)
			tl[k] = text;
	});
	return function(src) { return tl[src] || src };
}

function injectStylesIfNeeded(force) {
	if (!fytedom[0] && !force)
		return;
	var styledom = $('style#instant-youtube-styles');
	if (styledom) {
		// move our rules to the end of HEAD to increase CSS specificity
		if (styledom.nextElementSibling && document.head)
			document.head.insertBefore(styledom, null);
		return;
	}
	styledom = (document.head || document.documentElement).appendChild(document.createElement('style'));
	styledom.id = 'instant-youtube-styles';
	styledom.textContent = important(getFunctionComment(function() { /*
		.instant-youtube-container {
		    contain: strict;
		    position: relative;
		    overflow: hidden;
		    cursor: pointer;
		    padding: 0;
		    margin: 0;
		    font: normal 14px/1.0 sans-serif, Arial, Helvetica, Verdana;
		    text-align: center;
		    background: black;
		}
		.instant-youtube-container[disabled] {
		    background: #888;
		}
		.instant-youtube-container[disabled] .instant-youtube-storyboard {
		    display: none;
		}
		.instant-youtube-container[pinned] {
		    box-shadow: 0 0 30px black;
		}
		.instant-youtube-container .instant-youtube-wrapper {
		    width: 100%;
		    height: 100%;
		}
		.instant-youtube-container .instant-youtube-play-button {
		    display: block;
		    position: absolute;
		    width: 85px;
		    height: 60px;
		    left: 0;
		    right: 0;
		    top: 0;
		    bottom: 0;
		    margin: auto;
		}
		.instant-youtube-container .instant-youtube-loading-button {
		    display: block;
		    position: absolute;
		    width: 20px;
		    height: 20px;
		    left: 0;
		    right: 0;
		    top: 0;
		    bottom: 0;
		    padding: 0;
		    margin: auto;
			pointer-events: none;
		    background: url("data:image/gif;base64,R0lGODlhFAAUAJEDAMzMzLOzs39/f////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCgADACwAAAAAFAAUAAACPJyPqcuNItyCUJoQBo0ANIxpXOctYHaQpYkiHfM2cUrCNT0nqr4uudsz/IC5na/2Mh4Hu+HR6YBaplRDAQAh+QQFCgADACwEAAIADAAGAAACFpwdcYupC8BwSogR46xWZHl0l8ZYQwEAIfkEBQoAAwAsCAACAAoACgAAAhccMKl2uHxGCCvO+eTNmishcCCYjWEZFgAh+QQFCgADACwMAAQABgAMAAACFxwweaebhl4K4VE6r61DiOd5SfiN5VAAACH5BAUKAAMALAgACAAKAAoAAAIYnD8AeKqcHIwwhGntEWLkO3CcB4biNEIFACH5BAUKAAMALAQADAAMAAYAAAIWnDSpAHa4GHgohCHbGdbipnBdSHphAQAh+QQFCgADACwCAAgACgAKAAACF5w0qXa4fF6KUoVQ75UaA7Bs3yeNYAkWACH5BAUKAAMALAIABAAGAAwAAAIXnCU2iMfaRghqTmMp1moAoHyfIYIkWAAAOw==");
		}
		.instant-youtube-container:hover .ytp-large-play-button-svg {
		    fill: #CC181E;
		}
		.instant-youtube-container .instant-youtube-link {
		    display: block;
		    position: absolute;
		    width: 20em;
		    height: 20px;
		    top: 50%;
		    left: 0;
		    right: 0;
		    margin: 60px auto;
		    padding: 0;
		    border: none;
		    text-align: center;
		    text-decoration: none;
		    text-shadow: 1px 1px 3px black;
		    font-weight: bold;
		    color: white;
		}
		.instant-youtube-container span.instant-youtube-link {
		    z-index: 8;
		    font-weight: normal;
		    font-size: 12px;
		}
		.instant-youtube-container .instant-youtube-link:hover {
		    text-decoration: underline;
		    color: white;
		    background: transparent;
		}
		.instant-youtube-container iframe {
		    z-index: 10;
		}
		.instant-youtube-container .instant-youtube-title {
		    z-index: 9;
		    display: block;
		    position: absolute;
		    width: auto;
		    top: 0;
		    left: 0;
		    right: 0;
		    margin: 0;
		    padding: 7px;
		    border: none;
		    text-shadow: 1px 1px 2px black;
		    text-align: center;
		    text-decoration: none;
		    color: white;
		    background-color: rgba(0, 0, 0, 0.5);
		}
		.instant-youtube-container .instant-youtube-title strong {
		    font: bold 14px/1.0 sans-serif, Arial, Helvetica, Verdana;
		}
		.instant-youtube-container .instant-youtube-title strong:after {
		    content: " - $tl:'watch on Youtube'";
		    font-weight: normal;
		    margin-right: 1ex;
		}
		.instant-youtube-container .instant-youtube-title span {
		    color: inherit;
		}
		.instant-youtube-container .instant-youtube-title span:before {
		    content: "(";
		}
		.instant-youtube-container .instant-youtube-title span:after {
		    content: ")";
		}
		@-webkit-keyframes instant-youtube-fadein {
		    from { opacity: 0 }
		    to { opacity: 1 }
		}
		@-moz-keyframes instant-youtube-fadein {
		    from { opacity: 0 }
		    to { opacity: 1 }
		}
		@keyframes instant-youtube-fadein {
		    from { opacity: 0 }
		    to { opacity: 1 }
		}
		.instant-youtube-container:not(:hover) .instant-youtube-title[hidden] {
		    display: none;
		    margin: 0;
		}
		.instant-youtube-container .instant-youtube-title:hover {
		    text-decoration: underline;
		}
		.instant-youtube-container .instant-youtube-title strong {
		    color: white;
		}
		.instant-youtube-container .instant-youtube-options-button {
		    opacity: 0.6;
		    position: absolute;
		    right: 0;
		    bottom: 0;
		    margin: 0;
		    padding: 1.5ex 2ex;
		    font-size: 11px;
		    text-shadow: 1px 1px 2px black;
		    color: white;
		}
		.instant-youtube-container .instant-youtube-options-button:hover {
		    opacity: 1;
		    background: rgba(0, 0, 0, 0.5);
		}
		.instant-youtube-container .instant-youtube-options {
		    display: flex;
		    position: absolute;
		    right: 0;
		    bottom: 0;
		    margin: 0;
		    padding: 2ex 1ex 2ex 2ex;
		    flex-direction: column;
		    align-items: flex-start;
		    line-height: 1.5;
		    text-align: left;
		    opacity: 1;
		    color: white;
		    background: black;
		}
		.instant-youtube-container .instant-youtube-options * {
		    width: auto;
		    height: auto;
		    margin: 0;
		    padding: 0;
		    font: inherit;
		    font-size: 13px;
		    vertical-align: middle;
		    text-transform: none;
		    text-align: left;
		    border-radius: 0;
		    text-decoration: none;
		    color: white;
		    background: black;
		}
		.instant-youtube-container .instant-youtube-options > label {
		    margin-top: 1ex;
		}
		.instant-youtube-container .instant-youtube-options > label > * {
		    display: inline;
		}
		.instant-youtube-container .instant-youtube-options select {
		    padding: .5ex .25ex;
		    border: 1px solid #444;
		    -webkit-appearance: menulist;
		}
		.instant-youtube-container .instant-youtube-options [data-action="size-custom"] input {
		    width: 9ex;
		    padding: .5ex .5ex .4ex;
		    border: 1px solid #666;
		}
		.instant-youtube-container .instant-youtube-options [data-action="buttons"] {
		    margin-top: 1em;
		}
		.instant-youtube-container .instant-youtube-options button {
		    margin: 0 1ex 0 0;
		    padding: .5ex 2ex;
		    border: 2px solid gray;
		    font-weight: bold;
		}
		.instant-youtube-container .instant-youtube-options button:hover {
		    border-color: white;
		}
		.instant-youtube-container .instant-youtube-options > [disabled] {
		    opacity: 0.25;
		}
		.instant-youtube-container .instant-youtube-storyboard {
		    height: 33%;
			max-height: 90px;
		    display: block;
		    position: absolute;
		    left: 0;
		    right: 0;
		    bottom: 0;
			overflow: visible;
			overflow-x: visible;
			overflow-y: visible;
		}
		.instant-youtube-container .instant-youtube-storyboard:hover {
		    background-color: rgba(0,0,0,0.3);
		}
		.instant-youtube-container .instant-youtube-storyboard[disabled] {
		    display:none;
		}
		.instant-youtube-container .instant-youtube-storyboard div {
		    display: block;
		    position: absolute;
		    bottom: 0px;
			pointer-events: none;
		    border: 3px solid #888;
		    box-shadow: 2px 2px 10px black;
		    transition: opacity .25s ease;
			background-color: transparent;
			background-origin: content-box;
		    opacity: 0;
		}
		.instant-youtube-container .instant-youtube-storyboard:hover div {
			opacity: 1;
		}
		.instant-youtube-container [pin] {
			position: absolute;
			width: 0;
			height: 0;
			margin: 0;
			padding: 0;
			opacity: 1;
			top: auto; bottom: auto; left: auto; right: auto;
			border-style: solid;
			transition: opacity 2.5s ease-in, opacity 0.4s ease-out;
			opacity: 0;
			z-index: 100;
		}
		.instant-youtube-container[playing]:hover [pin]:not([transparent]) {
			opacity: 1;
		}
		.instant-youtube-container[playing] [pin]:hover {
			cursor: alias;
			opacity: 1;
			transition: opacity 0s;
		}
		.instant-youtube-container [pin=top-left][active] { border-top-color: green; }
		.instant-youtube-container [pin=top-left]:hover { border-top-color: #fc0; }
		.instant-youtube-container [pin=top-left] {
			top: 0; left: 0;
			border-width: 10px 10px 0 0;
			border-color: red transparent transparent transparent;
		}
		.instant-youtube-container [pin=top-right][active] { border-right-color: green; }
		.instant-youtube-container [pin=top-right]:hover { border-right-color: #fc0; }
		.instant-youtube-container [pin=top-right] {
			top: 0; right: 0;
			border-width: 0 10px 10px 0;
			border-color: transparent red transparent transparent;
		}
		.instant-youtube-container [pin=bottom-right][active] { border-bottom-color: green; }
		.instant-youtube-container [pin=bottom-right]:hover { border-bottom-color: #fc0; }
		.instant-youtube-container [pin=bottom-right] {
			bottom: 0; right: 0;
			border-width: 0 0 10px 10px;
			border-color: transparent transparent red transparent;
		}
		.instant-youtube-container [pin=bottom-left][active] { border-left-color: green; }
		.instant-youtube-container [pin=bottom-left]:hover { border-left-color: #fc0; }
		.instant-youtube-container [pin=bottom-left] {
			bottom: 0; left: 0;
			border-width: 10px 0 0 10px;
			border-color: transparent transparent transparent red;
		}
		.instant-youtube-dragndrop-placeholder {
			z-index: 999999999;
			margin: 0;
			padding: 0;
			background: rgba(0, 255, 0, 0.1);
			border: 2px dotted green;
			box-sizing: border-box;
		}
	*/}).replace(/\$tl:'(.+?)'/g, function(m, m1) { return _(m1) })
	);
}