// ==UserScript==
// @name	SaveFrom.net helper
// @namespace http://savefrom.net/
// @version	 7.63.2
// @date	2018-05-14
// @author		Magicbit, Inc
// @description Youtube Downloader: all in one script to get Vimeo, Facebook, Dailymotion videos for free
// @homepage	http://savefrom.net/user.php?helper=userjs
// @icon	http://sf-helper.com/img/icon_16.png
// @icon64		http://sf-helper.com/img/icon_64.png
// @updateURL https://download.sf-helper.com/chrome/helper.meta.js
// @downloadURL https://download.sf-helper.com/chrome/helper.user.js
// @include	 http://*
// @include	 https://*
// @run-at		document-end
// @grant		 GM_listValues
// @grant		 GM_setValue
// @grant		 GM_getValue
// @grant		 GM_deleteValue
// @grant		 GM_xmlhttpRequest
// @grant		 GM_info
// @grant		 GM_openInTab
// @grant		 GM_registerMenuCommand
// @grant		 GM_unregisterMenuCommand
// @grant		 GM_notification
// @grant		 GM_download
// @connect	 vk.com
// @connect	 vk.me
// @connect	 userapi.com
// @connect	 vkuseraudio.net
// @connect	 vkuservideo.net
// @connect	 vk-cdn.net
// @connect	 youtube.com
// @connect	 youtube-nocookie.com
// @connect	 ytimg.com
// @connect	 googlevideo.com
// @connect	 video.google.com
// @connect	 google-analytics.com
// @connect	 dailymotion.com
// @connect	 vimeo.com
// @connect	 vimeocdn.com
// @connect	 odnoklassniki.ru
// @connect	 ok.ru
// @connect	 cdn-ok.com
// @connect	 mycdn.me
// @connect	 pladform.ru
// @connect	 clipyou.ru
// @connect	 soundcloud.com
// @connect	 sndcdn.com
// @connect	 mail.ru
// @connect	 cdninstagram.com
// @connect	 fbcdn.net
// @connect	 facebook.com
// @connect	 sf-helper.com
// @connect	 savefr.com
// @connect	 travelbar.tools
// @connect	 savefrom.net
// @connect	 self
// @connect	 *
// ==/UserScript==

(function() {
 if (typeof JSON === 'undefined') {
	if (window.JSON) {
		JSON = window.JSON;
	} else
	if (document.defaultView && document.defaultView.JSON) {
		JSON = document.defaultView.JSON;
	}
 }

 if (typeof GM_getValue === 'undefined' ||
	typeof GM_setValue === 'undefined' ||
	typeof GM_xmlhttpRequest === 'undefined' ||
	typeof JSON === 'undefined') {
	return console.error('GM API is not ready!');
 }

 var _moduleName = null;

 var _menu;
 var _options;
 var _modules = {};
 var _languageList = {};

 try {
	window.sessionStorage['savefrom-helper-userjs'] = '1';
 } catch (e) {}
 /////////////////////////////////////////////////
 // MENU
 _menu = {
	activeDataAttr: 'data-sfh-active',
	id: 'sfh--ujs-menu',
	tooltipId: 'sfh--ujs-menu-tooltip',
	enabled: true,
	active: false,
	hover: false,
	move: false,
	title: '',
	shakeTimer: 0,
	shakeInterval: 10000,

	menu: null,
	icon: null,

	initMenu: null,

	style: {
		menu: {
	initial: {
	 background: '#fff',
	 borderStyle: 'solid',
	 borderWidth: '2px 0 2px 2px',
	 borderRadius: '5px 0 0 5px',
	 display: 'none',
	 boxSizing: 'content-box',
	 font: '13px/18px Arial,Helvetica,sans-serif',
	 width: 'auto',
	 height: 'auto',
	 padding: 0,
	 margin: 0,
	 overflow: 'visible',
	 position: 'fixed',
	 top: '50px',
	 right: '0',
	 textAlign: 'left',
	 WebkitTransition: 'all 0.2s',
	 transition: 'all 0.2s',
	 zIndex: 99999
	},

	enabled: {
	 borderColor: '#8dc50b'
	},

	disabled: {
	 borderColor: '#d0d0d0'
	},

	tag: {
	 display: 'block',
	 width: '10px',
	 height: '20px',
	 overflow: 'hidden',
	 opacity: 0.5
	},

	tagHover: {
	 width: '20px',
	 opacity: 0.8
	},

	active: {
	 width: 'auto',
	 height: 'auto',
	 overflow: 'visible',
	 opacity: 0.9
	}
		},

		icon: {
	initial: {
	 display: 'inline-block',
	 boxSizing: 'content-box',
	 backgroundPosition: '50% 50%',
	 backgroundRepeat: 'no-repeat',
	 width: '16px',
	 height: '16px',
	 margin: 0,
	 padding: '2px',
	 overflow: 'hidden',
	 verticalAlign: 'middle',
	 cursor: 'pointer'
	},

	enabled: {
	 backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABqUlEQVQ4jY2SzStEURiHf2bMl+w0SrHRZKPY+RtkYyeRspPFsEEZItLM3DCRjxofw0wyK0k+ClNGhBiEmIiwsrChZqvj/sZcznDJ4um8532f9+3ccy5isRg0Qre2V67hxyzxnfkHm5AdDcib6SvLC9e5e5v4TujuY4Dm6A4YPc684xq8sb6Fbq1CZkbNyU7aACVqdPhP8usGouZT7qcuLInpuFnITJ6bE6zRocuezwEdS/B2LuO+ewXJY44dmZ795yYhM6LmWKNDlz1pn+D0o6J5FjeMfbvGx9ETg5BhjjU6dHXvQMMdMcZ9BxAy7k1DXM/F0J6tYSxm9wbOHJ65y9Lh4FnJvLKZfa3sQKSh5lijQ5c97EX7GnJ61nHYF4Egni0klG3jk7bXYI41bc8e9iaP0RhArmsR+92rEP+BLnvSL3ESea1h7LoWVOEP6NDVvcTaQRQ4ZxBtCUPowRqdX/9EUtOFwoZxbDTNQsgwx9qPV9B7mqoOFNX3Y61xAoIwZk73GfWSySFtKK7uxSph/Jv3FQAZKgYVk0q2ir2sEuWEcSpnSjkZWt87G7sZgVx6JvgAAAAASUVORK5CYII=)'
	},

	disabled: {
	 backgroundImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABqUlEQVQ4jY2SzStEURiHf2bMl+w0SrHRZKPY+RtkYyeRspPFsEEZItLM3DCRjxofw0wyK0k+ClNGhBiEmIiwsrChZqvj/sZcznDJ4um8532f9+3ccy5isRg0Qre2V67hxyzxnfkHm5AdDcib6SvLC9e5e5v4TujuY4Dm6A4YPc684xq8sb6Fbq1CZkbNyU7aACVqdPhP8usGouZT7qcuLInpuFnITJ6bE6zRocuezwEdS/B2LuO+ewXJY44dmZ795yYhM6LmWKNDlz1pn+D0o6J5FjeMfbvGx9ETg5BhjjU6dHXvQMMdMcZ9BxAy7k1DXM/F0J6tYSxm9wbOHJ65y9Lh4FnJvLKZfa3sQKSh5lijQ5c97EX7GnJ61nHYF4Egni0klG3jk7bXYI41bc8e9iaP0RhArmsR+92rEP+BLnvSL3ESea1h7LoWVOEP6NDVvcTaQRQ4ZxBtCUPowRqdX/9EUtOFwoZxbDTNQsgwx9qPV9B7mqoOFNX3Y61xAoIwZk73GfWSySFtKK7uxSph/Jv3FQAZKgYVk0q2ir2sEuWEcSpnSjkZWt87G7sZgVx6JvgAAAAASUVORK5CYII=)'
	},

	tag: {
	 padding: '2px'
	},
	active: {
	 padding: '5px'
	}
		},

		title: {
	display: 'inline-block',
	font: 'bold 13px/18px Arial,Helvetica,sans-serif',
	lineHeight: '26px',
	margin: 0,
	padding: 0,
	textAlign: 'left',
	verticalAlign: 'middle',
	cursor: 'pointer'
		}
	},

	setTitle: function(title, version) {
		this.title = title;
		if (version)
	this.title += ' ' + version;
	},

	hide: function() {
		if (!this.menu || !document.body)
	return;
		this.menu.parentNode.removeChild(this.menu);
		this.menu = null;
		this.icon = null;
	},

	create: function() {
		if (this.menu || !document.body)
	return;

		this.moduleName = _moduleName;

		var _this = this;

		var menu = document.createElement('div');

		menu.addEventListener('mouseover', function() {
	if (!_this.active) {
	 _this.hover = true;
	 _this.setElementsStyle('tagHover');
	}
		});

		menu.addEventListener('mouseout', function() {
	if (!_this.active) {
	 _this.hover = false;
	 _this.setElementsStyle('tag');
	}
		});

		var icon = document.createElement('div');
		icon.title = this.title;

		icon.addEventListener('click', function(event) {
	if (_this.move) {
	 return;
	}
	event.preventDefault();
	event.stopPropagation();

	if (event.button === 0) {
	 _this.toggleMenuItems();
	} else if (event.button == 2) {
	 _this.remove();
	}

	return false;
		}, false);

		icon.addEventListener('contextmenu', function(event) {
	if (_this.move) {
	 return;
	}
	event.preventDefault();
	event.stopPropagation();
	_this.remove();
	return false;
		}, false);

		this.icon = menu.appendChild(icon);
		this.menu = document.body.appendChild(menu);

		var iconTopPos = parseInt(_menu.style.menu.initial.top);
		if (iconTopPos < 0) {
	iconTopPos = 0;
		} else
		if (this.moduleName === 'youtube' && iconTopPos < 92) {
	iconTopPos = 92;
		} else
		if (iconTopPos + 24 > parseInt(window.innerHeight)) {
	iconTopPos = parseInt(window.innerHeight) - parseInt(_menu.style.menu.tag.height) - 4;
		}

		_menu.style.menu.initial.top = iconTopPos + 'px';

		this.setElementsStyle('initial');
		this.setEnabled(1);
		this.showTag();
		this.enableMove();
	},

	enableMove: function() {
		var isStart = false;
		var _this = this;
		var icon = _this.icon;
		var menu = _this.menu;
		var startTimer = undefined;
		var sT = (parseInt(menu.style.height) / 2) || 0;
		var noSelectStyle = mono.create('style', {
	text: 'body {-webkit-user-select: none;-moz-user-select: none;-o-user-select: none;user-select: none;}'
		});

		var rbIcon = function() {
	_this.move = false;
	menu.style.position = _this.style.menu.initial.position;
	menu.style.WebkitTransition = _this.style.menu.initial.WebkitTransition;
	menu.style.transition = _this.style.menu.initial.transition;
	menu.style.borderStyle = _this.style.menu.initial.borderStyle;

	if (noSelectStyle.parentNode) {
	 noSelectStyle.parentNode.removeChild(noSelectStyle);
	}
		};

		var initIcon = function(e) {
	e.stopPropagation();
	e.preventDefault();

	_menu.hideMenuItems();

	_this.hover = true;
	_this.setElementsStyle('tagHover');

	_this.move = true;
	menu.style.position = 'fixed';
	var eX = e.y || e.clientY;
	menu.style.top = (eX - sT) + 'px';
	menu.style.WebkitTransition = 'initial';
	menu.style.transition = 'initial';
	menu.style.borderStyle = 'dotted';

	document.body.appendChild(noSelectStyle);
		};

		var onMouseMove = function(e) {
	var eX = e.y || e.clientY;
	var topValue = eX - sT;
	if (_this.moduleName === 'youtube' && topValue < 92) {
	 topValue = 92;
	} else
	if (topValue < 0) {
	 topValue = 0;
	}
	menu.style.top = _this.style.menu.initial.top = topValue + 'px';
		};

		var onStartTimer = function(e) {
	isStart = true;
	initIcon(e);
	document.body.addEventListener('mousemove', onMouseMove);
		};

		menu.addEventListener('dragstart', function(e) {
	e.preventDefault();
		});
		menu.addEventListener('mousedown', function(e) {
	if (e.target !== _this.icon) {
	 return;
	}
	isStart = false;
	startTimer = setTimeout(onStartTimer.bind(null, e), 500);
	var ml = function() {
	 clearTimeout(startTimer);
	 mono.off(menu, 'mouseleave', ml);
	};
	mono.on(menu, 'mouseleave', ml);
	document.body.addEventListener('mouseup', function mu() {
	 clearTimeout(startTimer);
	 document.body.removeEventListener('mouseup', mu);
	 mono.off(menu, 'mouseleave', ml);
	 if (isStart) {
		setTimeout(rbIcon, 100);
		document.body.removeEventListener('mousemove', onMouseMove);
		mono.storage.set({
			gmIconTop: parseInt(_this.style.menu.initial.top)
		});
	 }
	});
		});
	},

	setStyle: function(element, style) {
		if (this[element] && this.style[element] && this.style[element][style])
	SaveFrom_Utils.setStyle(this[element], this.style[element][style]);
	},

	setElementsStyle: function(style) {
		if (!style)
	return;

		var elements = ['menu', 'icon'];

		for (var i = 0; i < elements.length; i++)
	this.setStyle(elements[i], style);
	},

	setEnabled: function(enabled) {
		this.enabled = enabled;
		this.setElementsStyle(this.enabled ? 'enabled' : 'disabled');
	},

	showTag: function() {
		this.setElementsStyle('tag');
	},

	toggleMenuItems: function() {
		return _menu.active ? _menu.hideMenuItems() : _menu.showMenuItems();
	},

	remove: function() {
		if (_menu.menu)
	_menu.menu.parentNode.removeChild(_menu.menu);

		// _menu.removeTooltip();
	},

	menuHTML: '[encoded data, 26744 characters]',

	getMenu: function() {
		var code = atob(this.menuHTML);
		var menu = document.createElement('div');
		menu.classList.add('sf-menu-container');
		menu.setAttribute(this.activeDataAttr, '1');
		menu.style.marginTop = '-24px';
		menu.style.width = 'auto';
		menu.style.minWidth = '482px';
		menu.innerHTML = code;
		return menu;
	},

	showMenuItems: function() {
		if (this.active)
	return;

		this.menu.style.top = this.style.menu.initial.top;

		this.removeActiveItems();

		var menu = this.getMenu();
		if (menu) {
	this.menu.appendChild(menu);
	_menu.initMenu(this.menu);
		}

		this.active = true;
		this.setElementsStyle('active');

		document.addEventListener('click', this.onDocumentClick, false);
		document.addEventListener('keydown', this.onDocumentKeyDown, false);

		var menuHeight = this.menu.clientHeight;
		var menuTop = parseInt(this.menu.style.top);
		var winHeight = window.innerHeight;
		if (menuTop + menuHeight > winHeight) {
	this.menu.style.top = (winHeight - menuHeight - 4) + 'px'
		} else
		if (menuTop < 0) {
	this.menu.style.top = '0px';
		}
	},

	hideMenuItems: function() {
		this.menu.style.top = this.style.menu.initial.top;

		// this.removeTooltip();
		this.removeActiveItems();
		document.removeEventListener('click', this.onDocumentClick, false);
		document.removeEventListener('keydown', this.onDocumentKeyDown, false);

		this.active = false;
		this.showTag();
	},

	onDocumentClick: function(event) {
		var node = event.target;
		if (node != _menu.menu && !SaveFrom_Utils.isParent(node, _menu.menu)) {
	_menu.hideMenuItems();
		}
	},

	onDocumentKeyDown: function(event) {
		if (event.keyCode == 27) {
	_menu.hideMenuItems();
		}
	},

	removeActiveItems: function() {
		var e = null;
		while (e = this.menu.querySelector('div[' + this.activeDataAttr + ']')) {
	e.parentNode.removeChild(e);
		}
	}
 };
 /////////////////////////////////////////////////
 // OPTIONS
 _options = {
	classPrefix: 'sfh--',
	dataAttr: 'data-sfh-option',
	html: '[encoded data, 8296 characters]',

	translate: function(parent) {
		parent = parent || document;
		var e_list = parent.querySelectorAll('*[data-i18n]');
		for (var i = 0, len = e_list.length; i < len; i++) {
	var el = e_list[i];
	el.textContent = engine.language[el.dataset.i18n];
		}
	},

	onCbChange: function(event) {
		var el = event.target;
		var key = el.getAttribute(_options.dataAttr);
		mono.sendMessage({
	action: 'updateOption',
	key: key,
	value: el.checked ? 1 : 0
		});
	},

	createStatBlock: function(parent) {
		var id = 'blockStatEnabled';
		var node = parent.querySelector(id);
		if (!node) {
	var block = mono.create('div', {
	 class: ['sfh--block', id],
	 append: [
		mono.create('label', {
			append: [
		mono.create('input', {
		 type: 'checkbox',
		 data: {
			sfhOption: 'statEnabled'
		 },
		 checked: true
		}),
		String.fromCharCode(160),
		mono.create('span', {
		 text: 'Web Statistic Tracker'
		})
			]
		})
	 ]
	});
	var clearBlock = parent.querySelector('form > .sfh--clear');
	clearBlock.parentNode.insertBefore(block, clearBlock);
		}
	},

	init: function(preference) {
		var parent = document.querySelector('.sfh--options');

		if (!parent) {
	return;
		}

		if (!preference.hasSovetnik) {
	var sovBlock = parent.querySelector('.blockSovetnikEnabled');
	if (sovBlock) {
	 sovBlock.style.display = 'none';
	}
		}

		if (!preference.hasAviaBar) {
	var aviaBlock = parent.querySelector('.blockAviaBarEnabled');
	if (aviaBlock) {
	 aviaBlock.style.display = 'none';
	}
		}

		if (!preference.showUmmyItem) {
	var ummyBlock = parent.querySelector('.blockUmmyInfo');
	if (ummyBlock) {
	 ummyBlock.style.display = 'none';
	}
		}

		if (preference.hasSwStat || preference.hasFsStat) {
	_options.createStatBlock(parent);
		}

		_options.translate(parent);

		var c = parent.querySelectorAll('form input[type="checkbox"]');
		for (var i = 0; i < c.length; i++) {
	var el = c[i];
	var name = el.getAttribute(_options.dataAttr);
	if (name && preference[name] !== undefined) {
	 el.checked = preference[name] !== 0;
	 el.addEventListener('change', _options.onCbChange, false);
	}
		}

		if (typeof GM_download !== 'undefined') {
	var blockGmNativeDownload = parent.querySelector('.blockGmNativeDownload');
	if (blockGmNativeDownload) {
	 blockGmNativeDownload.style.display = 'block';
	}
		}
	},

	hideBlock: function(name, hide) {
		var node = document.querySelector('.' + _options.classPrefix + name);
		if (node)
	node.style.display = hide ? 'none' : '';
	},

	show: function() {
		var height = document.body.clientHeight || 0;
		if (height > 600) {
	height = 600;
		}
		SaveFrom_Utils.popupDiv(function(cnt) {
	cnt.innerHTML = atob(_options.html);
		}, null, 640, height);

		mono.initGlobal(function(resp) {
	_options.init(mono.global.preference);
		});
	}
 };

 _languageList.ru = "%7B%0A%20%20%20%20%22extName%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22extDescription%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%B9%D1%82%D0%B5%20%D1%81%20%D0%92%D0%BA%D0%BE%D0%BD%D1%82%D0%B0%D0%BA%D1%82%D0%B5,%20YouTube,%20%D0%9E%D0%B4%D0%BD%D0%BE%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%BD%D0%B8%D0%BA%D0%B8%20%D0%B8%20%D0%B5%D1%89%D0%B5%2040%20%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%20%D0%B7%D0%B0%201%20%D0%BA%D0%BB%D0%B8%D0%BA.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22extNameLite%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%20lite%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22extDescriptionLite%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%B9%D1%82%D0%B5%20%D1%81%20%D0%92%D0%BA%D0%BE%D0%BD%D1%82%D0%B0%D0%BA%D1%82%D0%B5,%20%D0%9E%D0%B4%D0%BD%D0%BE%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%BD%D0%B8%D0%BA%D0%B8%20%D0%B8%20%D0%B5%D1%89%D0%B5%2040%20%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%20%D0%B7%D0%B0%201%20%D0%BA%D0%BB%D0%B8%D0%BA.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22lang%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22ru%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22titleDefault%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22titleDesabled%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%20%D0%B2%D1%8B%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuEnable%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuDisable%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D1%8B%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22showButton%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D1%83%20%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D1%8F%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22copy%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22download%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D0%B6%D0%BC%D0%B8%D1%82%D0%B5%20%D0%BD%D0%B0%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D1%83,%20%D1%83%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D1%8F%20%D0%BA%D0%BB%D0%B0%D0%B2%D0%B8%D1%88%D1%83%20Alt%20%D0%B8%D0%BB%D0%B8%20Ctrl,%20%D1%87%D1%82%D0%BE%D0%B1%D1%8B%20%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D1%8C%20%D1%84%D0%B0%D0%B9%D0%BB.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22noLinksFound%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%20%D1%83%D0%B4%D0%B0%D0%BB%D0%BE%D1%81%D1%8C%20%D0%BD%D0%B0%D0%B9%D1%82%D0%B8%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22more%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%95%D1%89%D1%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22close%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%97%D0%B0%D0%BA%D1%80%D1%8B%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22kbps%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%BA%D0%B1/%D1%81%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22withoutAudio%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%B1%D0%B5%D0%B7%20%D0%B0%D1%83%D0%B4%D0%B8%D0%BE%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22size%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22subtitles%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D1%83%D0%B1%D1%82%D0%B8%D1%82%D1%80%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22playlist%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22filelist%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadWholePlaylist%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D0%B5%D1%81%D1%8C%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22getFileSizeTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22getFileSizeFailTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%20%D1%83%D0%B4%D0%B0%D0%BB%D0%BE%D1%81%D1%8C%20%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%20%D1%80%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0.%20%D0%9F%D0%BE%D0%BF%D1%80%D0%BE%D0%B1%D1%83%D0%B9%D1%82%D0%B5%20%D0%BF%D0%BE%D0%B2%D1%82%D0%BE%D1%80%D0%B8%D1%82%D1%8C%20%D0%BF%D0%BE%D0%BF%D1%8B%D1%82%D0%BA%D1%83.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22lmButtonTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BB%D1%83%D1%87%D0%B8%20%D0%BF%D1%80%D1%8F%D0%BC%D1%83%D1%8E%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D1%83%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadFromCurrentPage%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%B5%D1%80%D0%B5%D0%B9%D1%82%D0%B8%20%D0%BD%D0%B0%20SaveFrom.net%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22updateLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22updateLinksNotification%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%BE%D0%B1%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadMP3Files%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B0%D1%83%D0%B4%D0%B8%D0%BE%20%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadPlaylist%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22downloadPhotos%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22installFullVersion%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BF%D0%BE%D0%BB%D0%BD%D1%83%D1%8E%20%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8E%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22disable%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D1%8B%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22showOptions%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22reportBug%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BE%D0%BE%D0%B1%D1%89%D0%B8%D1%82%D1%8C%20%D0%BE%D0%B1%20%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22openPoll%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D1%82%D0%B0%D1%82%D1%8C%20%D0%BB%D1%83%D1%87%D1%88%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22disableModule%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%20%D0%BD%D0%B0%20%D1%8D%D1%82%D0%BE%D0%BC%20%D1%81%D0%B0%D0%B9%D1%82%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22enableModule%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D1%8C%20%D0%BD%D0%B0%20%D1%8D%D1%82%D0%BE%D0%BC%20%D1%81%D0%B0%D0%B9%D1%82%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22enableDisableModule%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BA%D0%BB/%D0%92%D1%8B%D0%BA%D0%BB%20%D0%BD%D0%B0%20%D1%8D%D1%82%D0%BE%D0%BC%20%D1%81%D0%B0%D0%B9%D1%82%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22showHideButton%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BA%D0%B0%D0%B7./%D1%81%D0%BA%D1%80.%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D1%83%20%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D1%8F%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22updateTo%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B4%D0%BE%20%25d%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutPage%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%20%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutVersion%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%B5%D1%80%D1%81%D0%B8%D1%8F%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutDescription%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B7%D0%B2%D0%BE%D0%BB%D1%8F%D0%B5%D1%82%20%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B0%D1%82%D1%8C%20%D0%BF%D1%80%D1%8F%D0%BC%D1%8B%D0%B5%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D1%81%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D1%87%D0%B5%D0%BC%2040%20%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2,%20%D1%81%D1%80%D0%B5%D0%B4%D0%B8%20%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D1%85%20%20VK.com,%20YouTube.com%20%D0%B8%20%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D0%B5.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutDescriptionLite%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B7%D0%B2%D0%BE%D0%BB%D1%8F%D0%B5%D1%82%20%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B0%D1%82%D1%8C%20%D0%BF%D1%80%D1%8F%D0%BC%D1%8B%D0%B5%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D1%81%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%20%D1%87%D0%B5%D0%BC%2040%20%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2,%20%D1%81%D1%80%D0%B5%D0%B4%D0%B8%20%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D1%85%20%20VK.com,%20Vimeo%20%D0%B8%20%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D0%B5.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22aboutSupported%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5%20%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22homePage%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%94%D0%BE%D0%BC%D0%B0%D1%88%D0%BD%D1%8F%D1%8F%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%20-%20%D0%9D%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsHandleLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%B0%D1%82%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsMediaHostings%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%20%D0%BC%D0%B5%D0%B4%D0%B8%D0%B0%D1%85%D0%BE%D1%81%D1%82%D0%B8%D0%BD%D0%B3%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsModules%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsYoutube%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22YouTube%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsYTHideLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D1%80%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsDailymotion%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22Dailymotion%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsVimeo%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22Vimeo%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsFacebook%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22Facebook%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsSoundcloud%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SoundCloud%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsVkontakte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BA%D0%BE%D0%BD%D1%82%D0%B0%D0%BA%D1%82%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsOdnoklassniki%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D0%B4%D0%BD%D0%BE%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%BD%D0%B8%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsMailru%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9C%D0%BE%D0%B9%20%D0%BC%D0%B8%D1%80%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsInstagram%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22Instagram%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsRutube%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22Rutube%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsBitrate%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B1%D0%B8%D1%82%D1%80%D0%B5%D0%B9%D1%82%20%D1%81%D1%80%D0%B0%D0%B7%D1%83%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsSovetnikEnabled%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BE%D0%B2%D0%B5%D1%82%D0%BD%D0%B8%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsShowUmmyInfo%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D0%BF%D0%BE%D0%B4%D1%81%D0%BA%D0%B0%D0%B7%D0%BA%D0%B8%20%D0%B4%D0%BB%D1%8F%20Ummy%20Video%20Downloader%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsGmNativeDownload%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9%20%D1%80%D0%B5%D0%B6%D0%B8%D0%BC%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%BA%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsShowUmmyBtn%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D1%82%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B0%D1%82%D1%8C%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D1%83%20Ummy%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsAviaBarEnabled%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BE%D0%B2%D0%B5%D1%82%D0%BD%D0%B8%D0%BA%20%D0%94%D0%B5%D1%88%D0%B5%D0%B2%D1%8B%D1%85%20%D0%90%D0%B2%D0%B8%D0%B0%D0%B1%D0%B8%D0%BB%D0%B5%D1%82%D0%BE%D0%B2%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22optionsSaveAsDialog%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D1%81%D0%B5%D0%B3%D0%B4%D0%B0%20%D0%B2%D1%8B%D0%B4%D0%B0%D0%B2%D0%B0%D1%82%D1%8C%20%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%20%D0%BD%D0%B0%20%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%B8%D0%B5%20%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuDownloadFromCurrentPage%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%20%D1%81%20%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B9%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%8B%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%B0%20SaveFrom.net%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuUpdateLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D0%B6%D0%BC%D0%B8%D1%82%D0%B5,%20%D0%B5%D1%81%D0%BB%D0%B8%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%20%D0%B4%D0%BB%D1%8F%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%BD%D0%B5%20%D0%BF%D0%BE%D1%8F%D0%B2%D0%BB%D1%8F%D0%B5%D1%82%D1%81%D1%8F%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuDownloadMP3Files%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D1%81%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%BD%D0%B0%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B5%20%D0%B0%D1%83%D0%B4%D0%B8%D0%BE%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuDownloadPlaylist%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A4%D0%BE%D1%80%D0%BC%D0%B8%D1%80%D1%83%D0%B5%D1%82%20%D0%B8%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%B5%D1%82%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%20%D1%81%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%D0%BC%D0%B8%20%D0%BD%D0%B0%20%D0%B0%D1%83%D0%B4%D0%B8%D0%BE%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuDownloadPhotos%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D1%81%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%BD%D0%B0%20%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B5%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuReportBug%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%94%D0%B0%D0%B9%D1%82%D0%B5%20%D0%BD%D0%B0%D0%BC%20%D0%B7%D0%BD%D0%B0%D1%82%D1%8C,%20%D0%B5%D1%81%D0%BB%D0%B8%20%D0%B2%D0%BE%D0%B7%D0%BD%D0%B8%D0%BA%D0%BB%D0%B8%20%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuShowOptions%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BA%D0%BB./%D0%BE%D1%82%D0%BA%D0%BB.%20%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5%20%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D1%8B%20%D0%B8%20%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuEnableExtension%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22menuOpenPoll%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B6%D0%B0%D0%BB%D1%83%D0%B9%D1%81%D1%82%D0%B0,%20%D0%BE%D1%86%D0%B5%D0%BD%D0%B8%D1%82%D0%B5%20%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%BE%20%D1%80%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%B8%D1%8F%20%5C%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%5C%22%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22quality%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9A%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%BE%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22qualityNote%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%95%D1%81%D0%BB%D0%B8%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE%20%D0%B2%20%D0%BD%D1%83%D0%B6%D0%BD%D0%BE%D0%BC%20%D0%BA%D0%B0%D1%87%D0%B5%D1%81%D1%82%D0%B2%D0%B5%20%D0%BD%D0%B5%D1%82,%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D0%B5%D1%82%D1%81%D1%8F%20%D0%BB%D1%83%D1%87%D1%88%D0%B5%D0%B5%20%D0%B8%D0%B7%20%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%D0%B3%D0%BE.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22filelistTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BD%D1%8B%D1%85%20%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22filelistInstruction%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A7%D1%82%D0%BE%D0%B1%D1%8B%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D1%81%D0%B5%20%D1%84%D0%B0%D0%B9%D0%BB%D1%8B,%20%D1%81%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D1%83%D0%B9%D1%82%D0%B5%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D1%81%D1%81%D1%8B%D0%BB%D0%BE%D0%BA%20%D0%B8%20%D0%B2%D1%81%D1%82%D0%B0%D0%B2%D1%8C%D1%82%D0%B5%20%D0%B5%D0%B3%D0%BE%20%D0%B2%20',%7Ba:%7Bhref:'http://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BE%D0%BA',text:'%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%B5%D0%BA'%7D%7D,'.%20%D0%95%D1%81%D0%BB%D0%B8%20%D1%83%20%D0%B2%D0%B0%D1%81%20%D0%BD%D0%B5%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%20%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%B5%D0%BA,%20%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D1%83%D0%B5%D0%BC%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20',%7Ba:%7Bhref:'http://www.westbyte.com/dm/',text:'Download%20Master'%7D%7D,'.'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22playlistTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22playlistInstruction%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9%20%D0%BF%D0%BB%D0%B5%D0%B9%D0%BB%D0%B8%D1%81%D1%82%20%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE%20%D0%BE%D1%82%D0%BA%D1%80%D1%8B%D1%82%D1%8C%20%D0%B2%20%D0%BF%D0%BB%D0%B5%D0%B5%D1%80%D0%B5%20%D0%B4%D0%BB%D1%8F%20%20%D0%B2%D0%BE%D1%81%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22ummySfTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22HD%20%D0%B8%D0%BB%D0%B8%20MP3%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22ummyMenuItem%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B%7Bspan:%7B%7D%7D,'%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20Ummy'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22ummyMenuInfo%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B%7Bp:%7Bappend:%5B'%D0%A1%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%B9%D1%82%D0%B5%20HD%20%D0%B8%20MP3%20%D1%81%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E%20%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE%D0%B9%20%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D1%8B',%20%7Bbr:%7B%7D%7D,%20'Ummy%20Video%20Downloader!'%5D%7D%7D,%7Bp:%7Bclass:'center',append:%5B%7Ba:%7Bclass:'green-btn-2%20arrow',href:'%7Burl%7D',target:'_blank',text:'%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C'%7D%7D%5D%7D%7D,%7Bp:%7Bappend:%5B'%D0%9F%D0%BE%D1%81%D0%BB%D0%B5%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B8%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%20%D0%B1%D1%83%D0%B4%D0%B5%D1%82%20%D0%BD%D0%B0%D1%87%D0%B8%D0%BD%D0%B0%D1%82%D1%8C%D1%81%D1%8F%20%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8',%20%7Bbr:%7B%7D%7D,%20'%D0%BF%D1%80%D0%B8%20%D0%BA%D0%BB%D0%B8%D0%BA%D0%B5%20%D0%BD%D0%B0%20',%7Bimg:%7Bsrc:'#'%7D%7D%5D%7D%7D%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22warningPopupTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D1%8C%D1%82%D0%B5%20%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B8%20%D0%B1%D1%80%D0%B0%D1%83%D0%B7%D0%B5%D1%80%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22warningPopupDesc%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D1%8B%D0%B1%D0%BE%D1%80%20%D0%BF%D0%B0%D0%BF%D0%BA%D0%B8%20%D0%BF%D1%80%D0%B8%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B8%20%D0%B4%D0%BE%D0%BB%D0%B6%D0%B5%D0%BD%20%D0%B1%D1%8B%D1%82%D1%8C%20%D0%BE%D1%82%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22readMore%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%B5%D0%B5...%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22noWarning%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%20%D0%BF%D1%80%D0%B5%D0%B4%D1%83%D0%BF%D1%80%D0%B5%D0%B6%D0%B4%D0%B0%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22cancel%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9E%D1%82%D0%BC%D0%B5%D0%BD%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22continue%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D1%80%D0%BE%D0%B4%D0%BE%D0%BB%D0%B6%D0%B8%D1%82%D1%8C%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22beforeDownloadPopupWarn%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%92%D0%BD%D0%B8%D0%BC%D0%B0%D0%BD%D0%B8%D0%B5:%20%D0%BE%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C%20%D0%B8%D0%BB%D0%B8%20%D0%BF%D1%80%D0%B8%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%BA%D1%83%20%D0%B1%D1%83%D0%B4%D0%B5%D1%82%20%D0%BD%D0%B5%D0%B2%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D0%BE.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkInfo%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkInfoTitle%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A0%D0%B0%D0%B7%D0%BC%D0%B5%D1%80%20%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%20%D0%B8%20%D0%B1%D0%B8%D1%82%D1%80%D0%B5%D0%B9%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkMp3LinksNotFound%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D1%8B%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%BD%D0%B0%20MP3%20%D1%84%D0%B0%D0%B9%D0%BB%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkPhotoLinksNotFound%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%20%D0%BD%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D1%8B%20%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B8%20%D0%BD%D0%B0%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B8%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkDownloadPhotoAlbum%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B0%D0%BB%D1%8C%D0%B1%D0%BE%D0%BC%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkDownloadAllPhotoAlbums%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D1%81%D0%B5%20%D0%B0%D0%BB%D1%8C%D0%B1%D0%BE%D0%BC%D1%8B%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFoundPhotos%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BE%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B9%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkNotAvailablePhotos%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%25d%20%D1%84%D0%BE%D1%82%D0%BE%20%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%BD%D0%BE%20%D0%BD%D0%B5%20%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%BD%D0%BE%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFoundLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BE%20%D1%81%D1%81%D1%8B%D0%BB%D0%BE%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFoundOf%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%B8%D0%B7%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkShowAs%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%BA%D0%B0%D0%B7%D0%B0%D1%82%D1%8C%20%D0%BA%D0%B0%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkListOfLinks%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D1%81%D1%81%D1%8B%D0%BB%D0%BE%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkListOfLinksInstruction%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A7%D1%82%D0%BE%D0%B1%D1%8B%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D1%81%D0%B5%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B8,%20%D1%81%D0%BA%D0%BE%D0%BF%D0%B8%D1%80%D1%83%D0%B9%D1%82%D0%B5%20%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D1%81%D1%81%D1%8B%D0%BB%D0%BE%D0%BA%20%D0%B8%20%D0%B2%D1%81%D1%82%D0%B0%D0%B2%D1%8C%D1%82%D0%B5%20%D0%B5%D0%B3%D0%BE%20%D0%B2%20',%7Ba:%7Bhref:'http://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80_%D0%B7%D0%B0%D0%B3%D1%80%D1%83%D0%B7%D0%BE%D0%BA',text:'%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%B5%D0%BA'%7D%7D,'.%20%D0%95%D1%81%D0%BB%D0%B8%20%D1%83%20%D0%B2%D0%B0%D1%81%20%D0%BD%D0%B5%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%20%D0%BC%D0%B5%D0%BD%D0%B5%D0%B4%D0%B6%D0%B5%D1%80%20%D0%B7%D0%B0%D0%BA%D0%B0%D1%87%D0%B5%D0%BA,%20%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D1%83%D0%B5%D0%BC%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20',%7Ba:%7Bhref:'http://www.westbyte.com/dm/',text:'Download%20Master'%7D%7D,'.'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkTableOfThumbnails%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A2%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D1%83%20%D1%8D%D1%81%D0%BA%D0%B8%D0%B7%D0%BE%D0%B2%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkListOfPhotos%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%B9%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkListOfPhotosInstruction%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A7%D1%82%D0%BE%D0%B1%D1%8B%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D1%84%D0%BE%D1%82%D0%BE%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D1%8E,%20%D1%89%D0%B5%D0%BB%D0%BA%D0%BD%D0%B8%D1%82%D0%B5%20%D0%BD%D0%B0%20%D0%BD%D0%B5%D0%B9%20%D0%BB%D0%B5%D0%B2%D0%BE%D0%B9%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D0%BE%D0%B9%20%D0%BC%D1%8B%D1%88%D0%B8.%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkKbps%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%BA%D0%B1/%D1%81%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFileSizeByte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFileSizeKByte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%BA%D0%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFileSizeMByte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9C%D0%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFileSizeGByte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%93%D0%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFileSizeTByte%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A2%D0%91%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkDownloadFromYoutube%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C:%20%D0%BE%D1%82%D0%BA%D1%80%D0%BE%D0%B9%D1%82%D0%B5%20',%7Ba:%7Bhref:'%7Burl%7D',text:'%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%83%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE%20%D1%80%D0%BE%D0%BB%D0%B8%D0%BA%D0%B0'%7D%7D,'%20%D0%B8%20%D0%BD%D0%B0%D0%B6%D0%BC%D0%B8%D1%82%D0%B5%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D1%83%20%5C%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%5C%22%20%D0%BD%D0%B0%D0%B4%20%D0%BF%D0%BB%D0%B5%D0%B5%D1%80%D0%BE%D0%BC'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22vkFoundFiles%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B0%D0%B9%D0%B4%D0%B5%D0%BD%D0%BE%20%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2:%20%25d%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22shareIn%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D0%BE%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C%D1%81%D1%8F%20%D0%B2%20%25w%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22socialDesc%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%20%D0%BF%D0%BE%D0%BC%D0%BE%D0%B3%D0%B0%D0%B5%D1%82%20%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%20%D1%81%20VK.com,%20YouTube.com,%20Odnoklassniki.ru%20%D0%B8%20%D0%B1%D0%BE%D0%BB%D0%B5%D0%B5%2040%20%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D1%85%20%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%20%D0%B2%201%20%D0%BA%D0%BB%D0%B8%D0%BA%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS1Title%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A5%D0%BE%D1%80%D0%BE%D1%88%D0%B8%D0%B5%20%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8!%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS1Main%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A3%20%D1%82%D0%B5%D0%B1%D1%8F%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%20SaveFrom.net%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%BD%D0%B8%D0%BA%20',%7Bimg:%20%7Bsrc:%20'#logo'%7D%7D,',%20%D0%B8%20%D1%82%D0%B5%D0%BF%D0%B5%D1%80%D1%8C%20%D1%82%D1%8B%20%D0%BC%D0%BE%D0%B6%D0%B5%D1%88%D1%8C%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%BB%D1%8E%D0%B1%D0%BE%D0%B5%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE%20%D1%81%20YouTube%20%D0%B2%20%D0%BE%D0%B4%D0%B8%D0%BD%20%D0%BA%D0%BB%D0%B8%D0%BA'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS1Arrow%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A5%D0%BE%D1%87%D0%B5%D1%88%D1%8C%20%D1%83%D0%B7%D0%BD%D0%B0%D1%82%D1%8C%20%D0%BA%D0%B0%D0%BA?',%7Bbr:%7B%7D%7D,'%D0%9D%D0%B0%D0%B6%D0%BC%D0%B8%20%D0%B7%D0%B4%D0%B5%D1%81%D1%8C'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS2Main%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%A7%D1%82%D0%BE%D0%B1%D1%8B%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE,%20%D0%B2%D1%8B%D0%B1%D0%B5%D1%80%D0%B8%20%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%20%D0%B8%20%D0%BD%D0%B0%D0%B6%D0%BC%D0%B8%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D1%83%20%5C%22%D0%A1%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%5C%22%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS3History%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D1%80%D0%BE%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS3Main%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A5%D0%BE%D1%87%D0%B5%D1%88%D1%8C%20%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C%20%D1%83%D0%B6%D0%B5%20%D0%BF%D1%80%D0%BE%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE?',%7Bbr:%7B%7D%7D,'%D0%9D%D0%B0%D0%B6%D0%B8%D0%BC%D0%B0%D0%B9%20%D0%BD%D0%B0%20',%20%7Bimg:%20%7Bsrc:%20'#arrow'%7D%7D,'%20%D0%BD%D0%B0%20%D0%BF%D1%80%D0%B5%D0%B2%D1%8C%D1%8E%20%D0%B2%20%D0%9F%D1%80%D0%BE%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B5%D0%BD%D0%BD%D1%8B%D1%85%20%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS4Main%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%92%D0%B8%D0%B4%D0%B5%D0%BE%20%D0%BD%D0%B5%20%D0%BE%D0%B1%D1%8F%D0%B7%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%20%D0%BF%D1%80%D0%BE%D1%81%D0%BC%D0%B0%D1%82%D1%80%D0%B8%D0%B2%D0%B0%D1%82%D1%8C%20%E2%80%94',%7Bbr:%7B%7D%7D,'%D0%BD%D0%B0%D0%B6%D0%B8%D0%BC%D0%B0%D0%B9%20%D0%BD%D0%B0%20',%20%7Bimg:%20%7Bsrc:%20'#arrow'%7D%7D,',%20%D0%B2%D1%8B%D0%B1%D0%B8%D1%80%D0%B0%D0%B9%20%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%20%D0%B8%20%D1%81%D0%BA%D0%B0%D1%87%D0%B8%D0%B2%D0%B0%D0%B9.%20%D0%AD%D1%82%D0%BE%20%D1%83%D0%B4%D0%BE%D0%B1%D0%BD%D0%BE!'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS5Title%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9F%D1%80%D0%B8%D1%8F%D1%82%D0%BD%D0%BE%D0%B9%20%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D1%8B!%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialS5Main%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%9E%D1%81%D1%82%D0%B0%D0%BB%D0%B8%D1%81%D1%8C%20%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D1%8B?%20%D0%95%D1%81%D1%82%D1%8C%20%D0%BF%D1%80%D0%B5%D0%B4%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F?%20%D0%97%D0%B0%D0%B3%D0%BB%D1%8F%D0%BD%D0%B8%D1%82%D0%B5%20%D0%BD%D0%B0%20',%20%7Ba:%20%7Btext:%20'%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D1%83%20%D0%BF%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%BA%D0%B8',%20href:%20'#support'%7D%7D,'%20%D0%B8%D0%BB%D0%B8%20%D0%B2%D1%81%D1%82%D1%83%D0%BF%D0%B0%D0%B9%D1%82%D0%B5%20%D0%B2%20%D0%BD%D0%B0%D1%88%D0%B5%20',%20%7Ba:%20%7Btext:%20'%D0%92%D0%9A-%D1%81%D0%BE%D0%B1%D1%89%D0%B5%D1%81%D1%82%D0%B2%D0%BE',%20href:%20'#vk'%7D%7D%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tutorialTooltipText%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%A7%D1%82%D0%BE%D0%B1%D1%8B%20',%7Bb:%20%7Btext:%20'%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C'%7D%7D,',%20%D0%BF%D1%80%D0%BE%D1%81%D1%82%D0%BE%20%D0%BA%D0%BB%D0%B8%D0%BA%D0%BD%D0%B8%D1%82%D0%B5%20%D0%BF%D0%BE%20%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D0%B5'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22ummyTooltipConfirm%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%5B'%D0%92%D1%8B%20%D1%83%D0%B6%D0%B5%20%D1%83%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D0%BB%D0%B8',%7Bbr:%7B%7D%7D,%7Bimg:%7B%7D%7D,'%20Ummy%20Video%20Downloader?'%5D%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22yes%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%94%D0%B0%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22no%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%9D%D0%B5%D1%82%22%0A%20%20%20%20%7D,%0A%20%20%20%20%22tooltipHide%22:%20%7B%0A%20%20%20%20%20%20%20%20%22message%22:%20%22%D0%91%D0%BE%D0%BB%D1%8C%D1%88%D0%B5%20%D0%BD%D0%B5%20%D0%BF%D0%BE%D0%BA%D0%B0%D0%B7%D1%8B%D0%B2%D0%B0%D1%82%D1%8C%20%D1%8D%D1%82%D0%BE%20%D1%81%D0%BE%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%B8%D0%B5%22%0A%20%20%20%20%7D%0A%7D";
 _languageList.en = _languageList.ru;

 var mono = (typeof mono !== 'undefined') ? mono : undefined;

 (function(base, factory) {
	"use strict";
	if (mono && mono.isLoaded) {
		return;
	}

	var _mono = mono;
	var fn = function(addon) {
		return factory(_mono, addon);
	};

	mono = base(fn);
	return;
 }(
	function base(factory) {
		if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
	return factory();
		}

		if (typeof GM_getValue !== 'undefined') {
	return factory();
		}

		var base = {
	isLoaded: true,
	onReadyStack: [],
	onReady: function() {
	 base.onReadyStack.push([this, arguments]);
	},
	loadModuleStack: [],
	loadModule: function() {
	 base.loadModuleStack.push([this, arguments]);
	}
		};

		var onLoad = function() {
	document.removeEventListener('DOMContentLoaded', onLoad, false);
	window.removeEventListener('load', onLoad, false);

	mono = factory();

	var item;
	while (item = base.onReadyStack.shift()) {
	 mono.onReady.apply(item[0], item[1]);
	}

	while (item = base.loadModuleStack.shift()) {
	 mono.loadModule.apply(item[0], item[1]);
	}
		};

		document.addEventListener('DOMContentLoaded', onLoad, false);
		window.addEventListener('load', onLoad, false);

		return base;
	},
	function initMono(_mono, _addon) {

		var browserApi = function() {
	"use strict";
	var emptyFn = function() {};

	var cloneObj = function(msg) {
	 var obj = null;
	 try {
		obj = JSON.parse(JSON.stringify({
			w: msg
		})).w;
	 } catch (e) {
		console.error('CloneObj error!', e);
	 }

	 return obj;
	};

	/**
	 * @param {Function} fn
	 * @returns {Function}
	 */
	var onceFn = function(fn) {
	 return function(msg) {
		if (fn) {
			fn(msg);
			fn = null;
		}
	 };
	};

	/**
	 * @param {Function} fn
	 * @returns {Function}
	 */
	var cloneResponse = function(fn) {
	 return function(response) {
		fn(cloneObj(response));
	 };
	};

	var msgTools = {
	 listenerBgList: [],
	 listenerList: []
	};

	var api = {
	 isGM: true,
	 /**
	 * @param {*} msg
	 * @param {Function} [responseCallback]
	 */
	 sendMessageToActiveTab: function(msg, responseCallback) {
		var responseFn = !responseCallback ? emptyFn : onceFn(cloneResponse(responseCallback));

		msgTools.listenerList.forEach(function(fn) {
			setTimeout(function() {
		fn(cloneObj(msg), responseFn);
			}, 0);
		});
	 },
	 /**
	 * @param {*} msg
	 * @param {Function} [responseCallback]
	 */
	 sendMessage: function(msg, responseCallback) {
		var responseFn = !responseCallback ? emptyFn : onceFn(cloneResponse(responseCallback));

		var cloneMsg = cloneObj(msg);
		msgTools.listenerBgList.forEach(function(fn) {
			setTimeout(function() {
		fn(cloneMsg, responseFn);
			}, 0);
		});
	 },
	 onMessage: {
		/**
		 * @param {Function} callback
		 * @param {Object} [details]
		 * @param {Boolean} [details.isBg]
		 */
		addListener: function(callback, details) {
			details = details || {};
			var listenerList = null;
			if (details.isBg) {
		listenerList = msgTools.listenerBgList;
			} else {
		listenerList = msgTools.listenerList;
			}

			if (listenerList.indexOf(callback) === -1) {
		listenerList.push(callback);
			}
		},
		/**
		 * @param {Function} callback
		 * @param {Object} [details]
		 * @param {Boolean} [details.isBg]
		 */
		removeListener: function(callback, details) {
			details = details || {};
			var listenerList = null;
			if (details.isBg) {
		listenerList = msgTools.listenerBgList;
			} else {
		listenerList = msgTools.listenerList;
			}

			var pos = listenerList.indexOf(callback);
			if (pos !== -1) {
		listenerList.splice(pos, 1);
			}
		}
	 }
	};

	if (window.chrome) {
	 api.isTM = true;
	} else
	if (/Maxthon\//.test(navigator.userAgent)) {
	 api.isVM = true;
	} else {
	 api.isGmOnly = true;
	}

	var initGmStorage = function() {
	 var readOldItem = function(value) {
		var result = undefined;
		if (value !== undefined && value !== null && value !== 'undefined' && value !== 'isMonoEmptyValue') {
			if (typeof value !== 'object') {
		result = value;
			} else {
		try {
		 result = JSON.parse(JSON.stringify(value));
		} catch (e) {
		 console.error('GM old storage item read error!', e, value);
		}
			}
		}
		return result;
	 };

	 var jsonRe = /^{(?:"w":.+|)}$/;
	 var readItem = function(value) {
		if (value !== 'MonoEmptyValue' && !jsonRe.test(value)) {
			return readOldItem(value);
		}

		var result = undefined;
		if (typeof value === 'string' && value !== 'MonoEmptyValue') {
			try {
		result = JSON.parse(value).w;
			} catch (e) {
		console.error('GM Storage item read error!', e, value);
			}
		}
		return result;
	 };

	 var writeItem = function(value) {
		return JSON.stringify({
			w: value
		});
	 };

	 return {
		/**
		 * @param {String|[String]|Object|null|undefined} [keys]
		 * @param {Function} callback
		 */
		get: function(keys, callback) {
			var items = {};
			var defaultItems = {};

			var _keys = [];
			if (keys === undefined || keys === null) {
		_keys = GM_listValues();
			} else
			if (Array.isArray(keys)) {
		_keys = keys;
			} else
			if (typeof keys === 'object') {
		_keys = Object.keys(keys);
		defaultItems = keys;
			} else {
		_keys = [keys];
			}

			_keys.forEach(function(key) {
		var value = readItem(GM_getValue(key, 'MonoEmptyValue'));
		if (value === undefined) {
		 value = defaultItems[key];
		}
		if (value !== undefined) {
		 items[key] = value;
		}
			});

			setTimeout(function() {
		callback(items);
			}, 0);
		},
		/**
		 * @param {Object} items
		 * @param {Function} [callback]
		 */
		set: function(items, callback) {
			Object.keys(items).forEach(function(key) {
		if (items[key] !== undefined) {
		 GM_setValue(key, writeItem(items[key]));
		}
			});

			callback && setTimeout(function() {
		callback();
			}, 0);
		},
		/**
		 * @param {String|[String]} [keys]
		 * @param {Function} [callback]
		 */
		remove: function(keys, callback) {
			var _keys = [];
			if (Array.isArray(keys)) {
		_keys = keys;
			} else {
		_keys = [keys];
			}

			_keys.forEach(function(key) {
		GM_deleteValue(key);
			});

			callback && setTimeout(function() {
		callback();
			}, 0);
		},
		/**
		 * @param {Function} [callback]
		 */
		clear: function(callback) {
			this.remove(GM_listValues(), callback);
		}
	 };
	};

	api.storage = initGmStorage();

	var _navigator = null;
	/**
	 * @returns {{language: String, platform: String, userAgent: String}}
	 */
	api.getNavigator = function() {
	 if (_navigator) {
		return _navigator;
	 }

	 _navigator = {};
	 ['language', 'platform', 'userAgent'].forEach(function(key) {
		_navigator[key] = navigator[key] || '';
	 });

	 return _navigator;
	};

	(function checkCompatibility() {
	 var ua = api.getNavigator().userAgent;
	 var m = /Chrome\/(\d+)/.exec(ua);
	 if (m) {
		var version = parseInt(m[1]);
		api.isChromeVersion = version;
		if (version < 31) {
			api.noMouseEnter = true;
		}
	 }
	})();

	/**
	 * @param {string} url
	 * @param {boolean} [active]
	 */
	api.openTab = function(url, active) {
	 active = (active === undefined) ? true : !!active;
	 if (typeof GM_openInTab !== 'undefined') {
		return GM_openInTab(url, {
			active: active,
			insert: true
		});
	 }
	};

	/**
	 * @param {Function} cb
	 */
	api.getActiveTabUrl = function(cb) {
	 cb(location.href);
	};

	/**
	 * @returns {string}
	 */
	api.getVersion = function() {
	 var version = 'GM_unknown';
	 if (typeof GM_info !== 'undefined' && GM_info.script && GM_info.script.version) {
		version = GM_info.script.version;
	 }
	 return version;
	};

	/**
	 * @param {String} locale
	 * @param {Function} cb
	 */
	api.getLanguage = function(locale, cb) {
	 var convert = function(messages) {
		var language = {};
		for (var key in messages) {
			if (messages.hasOwnProperty(key)) {
		language[key] = messages[key].message;
			}
		}
		return language;
	 };

	 try {
		cb(null, convert(JSON.parse(decodeURI(_languageList[locale]))));
	 } catch (e) {
		cb(e);
	 }
	};

	api.getLoadedLocale = function() {
	 return '';
	};

	return {
	 api: api
	};
		};

		var mono = browserApi(_addon).api;
		mono.isLoaded = true;
		mono.onReady = function(cb) {
	return cb();
		};

		//> utils

		/**
		 * @param {string} head
		 * @returns {Object}
		 */
		mono.parseXhrHeader = function(head) {
	head = head.split(/\r?\n/);
	var headers = {};
	head.forEach(function(line) {
	 "use strict";
	 var sep = line.indexOf(':');
	 if (sep === -1) {
		return;
	 }
	 var key = line.substr(0, sep).trim().toLowerCase();
	 var value = line.substr(sep + 1).trim();
	 headers[key] = value;
	});
	return headers;
		};

		var webRequest = (function() {
	var prefix = 'sf-';
	var prefixRe = /^sf-\d+_/;
	var filter = {
	 urls: ["<all_urls>"],
	 types: ["xmlhttprequest"]
	};
	var isListen = false;
	var waitHeaders = {};
	var requestIdObj = {};

	var isEmpty = function(obj) {
	 for (var key in obj) {
		return false;
	 }
	 return true;
	};
	var any = function(details) {
	 delete requestIdObj[details.requestId];
	 if (isEmpty(waitHeaders) && isEmpty(requestIdObj)) {
		removeListener();
	 }
	};
	var beforeSendHeadersListener = function(details) {
	 var requestObj = requestIdObj[details.requestId];
	 var requestHeaders = details.requestHeaders || [];
	 var filtered = [];
	 var changes = [];
	 var filterResponseHeaders = [];

	 if (requestObj) {
		changes = requestObj.changes;
		filtered = requestObj.filtered;
	 } else
	 if (!isEmpty(waitHeaders)) {
		var name, item;
		for (var i = 0, header; header = requestHeaders[i]; i++) {
			name = header.name;
			if (prefixRe.test(name)) {
		item = waitHeaders[name];
		if (item) {
		 header.name = item.name;
		 header.value = item.value;
		 changes.push(header);
		 filtered.push(item.name.toLowerCase());
		 filtered.push(name.toLowerCase());
		 if (/cookie/i.test(header.name)) {
			filterResponseHeaders.push('set-cookie');
		 }
		 clearTimeout(item.timer);
		 delete waitHeaders[name];
		}
			}
		}
	 }

	 if (changes.length) {
		if (!requestObj) {
			requestIdObj[details.requestId] = {
		changes: changes,
		filtered: filtered,
		filterResponseHeaders: filterResponseHeaders
			};
		}

		var result = requestHeaders.filter(function(header) {
			return filtered.indexOf(header.name.toLowerCase()) === -1;
		}).concat(changes);

		return {
			requestHeaders: result
		};
	 }
	};
	var headersReceivedListener = function(details) {
	 var requestObj = requestIdObj[details.requestId];
	 var responseHeaders = details.responseHeaders;
	 if (requestObj && responseHeaders) {
		var filterResponseHeaders = requestObj.filterResponseHeaders;

		var result = responseHeaders.filter(function(header) {
			return filterResponseHeaders.indexOf(header.name.toLowerCase()) === -1;
		});

		return {
			responseHeaders: result
		};
	 }
	};
	var removeListener = function() {
	 if (isListen) {
		isListen = false;
		chrome.webRequest.onBeforeSendHeaders.removeListener(beforeSendHeadersListener, filter, ["blocking", "requestHeaders"]);
		chrome.webRequest.onHeadersReceived.removeListener(headersReceivedListener, filter, ["blocking", "responseHeaders"]);
		chrome.webRequest.onResponseStarted.removeListener(any, filter);
		chrome.webRequest.onErrorOccurred.removeListener(any, filter);

		mono.debug('webRequest', 'rm listener');
	 }
	};
	var addListener = function() {
	 if (!isListen) {
		isListen = true;
		chrome.webRequest.onBeforeSendHeaders.addListener(beforeSendHeadersListener, filter, ["blocking", "requestHeaders"]);
		chrome.webRequest.onHeadersReceived.addListener(headersReceivedListener, filter, ["blocking", "responseHeaders"]);
		chrome.webRequest.onResponseStarted.addListener(any, filter);
		chrome.webRequest.onErrorOccurred.addListener(any, filter);

		mono.debug('webRequest', 'add listener');
	 }
	};
	var permissionRequestLimit = 10;
	var permissionRequested = false;
	var requestPermission = function(cb) {
	 if (isAvailable() || permissionRequested) {
		cb(available);
	 } else
	 if (chrome.permissions && chrome.permissions.request) {
		chrome.permissions.request({
			permissions: ['webRequest', 'webRequestBlocking']
		}, function(grunted) {
			if (grunted || permissionRequestLimit-- <= 0) {
		permissionRequested = true;
			}
			grunted && isAvailable(true);
			cb(available);
		});
	 } else {
		permissionRequested = true;
		cb(available);
	 }
	};
	var isSupported = function() {
	 return mono.isChrome || mono.isFirefox;
	};
	var available = null;
	var isAvailable = function(force) {
	 if (available === null || force) {
		available = !!(isSupported() &&
			chrome.webRequest &&
			chrome.webRequest.onBeforeSendHeaders &&
			chrome.webRequest.onResponseStarted &&
			chrome.webRequest.onErrorOccurred
		);
	 }
	 return available;
	};
	var wrapHeaderKey = function(key, value) {
	 if (isAvailable()) {
		var mKey, limit = 100;
		while (limit-- > 0) {
			mKey = prefix + parseInt(Math.random() * 100000) + '_' + key;
			if (!waitHeaders[mKey]) {
		break;
			}
		}
		waitHeaders[mKey] = {
			name: key,
			value: value,
			timer: setTimeout(function() {
		delete waitHeaders[mKey];
			}, 3 * 1000)
		};

		addListener();

		return mKey;
	 } else {
		return key;
	 }
	};
	var specialHeaderRe = /^user-agent$|^origin$|^cookie$/i;
	var isSpecialHeader = function(key) {
	 return specialHeaderRe.test(key);
	};
	return {
	 wrapHeaderKey: wrapHeaderKey,
	 isSpecialHeader: isSpecialHeader,
	 isSupported: isSupported,
	 requestPermission: requestPermission
	};
		})();

		mono.getDesktopUa = function() {
	return 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36';
		};

		/**
		 * @typedef {Object|string} requestDetails
		 * @property {string} url
		 * @property {string} [method] GET|POST
		 * @property {string} [type] GET|POST
		 * @property {string} [data]
		 * @property {boolean} [cache]
		 * @property {Object} [headers]
		 * @property {string} [contentType]
		 * @property {boolean} [json]
		 * @property {boolean} [xml]
		 * @property {number} [timeout]
		 * @property {string} [mimeType]
		 * @property {boolean} [withCredentials]
		 * @property {boolean} [localXHR]
		 */
		/**
		 * @callback requestResponse
		 * @param {string|null} err
		 * @param {Object} res
		 * @param {string|Object|Array} data
		 */
		/**
		 * @param {requestDetails} obj
		 * @param {requestResponse} [origCb]
		 * @returns {{abort: function}}
		 */
		mono.request = function(obj, origCb) {
	"use strict";
	var result = {};
	var cb = function(e, body) {
	 cb = null;
	 if (request.timeoutTimer) {
		clearTimeout(request.timeoutTimer);
	 }

	 var err = null;
	 if (e) {
		err = String(e.message || e) || 'ERROR';
	 }
	 origCb && origCb(err, getResponse(body), body);
	};

	var getResponse = function(body) {
	 var response = {};

	 response.statusCode = xhr.status;
	 response.statusText = xhr.statusText;

	 var headers = null;
	 var allHeaders = xhr.getAllResponseHeaders();
	 if (typeof allHeaders === 'string') {
		headers = mono.parseXhrHeader(allHeaders);
	 }
	 response.headers = headers || {};

	 response.body = body;

	 return response;
	};

	if (typeof obj !== 'object') {
	 obj = {
		url: obj
	 };
	}

	var url = obj.url;

	var method = obj.method || obj.type || 'GET';
	method = method.toUpperCase();

	var data = obj.data;
	if (typeof data !== "string") {
	 data = mono.param(data);
	}

	if (data && method === 'GET') {
	 url += (/\?/.test(url) ? '&' : '?') + data;
	 data = undefined;
	}

	if (obj.cache === false && ['GET', 'HEAD'].indexOf(method) !== -1) {
	 url += (/\?/.test(url) ? '&' : '?') + '_=' + Date.now();
	}

	if (mono.isFirefox && !/^https?:\/\//.test(url)) {
	 var linkNode = document.createElement('a');
	 linkNode.href = url;
	 url = linkNode.href;
	 linkNode = null;
	}

	obj.headers = obj.headers || {};

	if (data) {
	 obj.headers["Content-Type"] = obj.contentType || obj.headers["Content-Type"] || 'application/x-www-form-urlencoded; charset=UTF-8';
	}

	var request = {};
	request.url = url;
	request.method = method;

	data && (request.data = data);
	obj.json && (request.json = true);
	obj.xml && (request.xml = true);
	obj.timeout && (request.timeout = obj.timeout);
	obj.mimeType && (request.mimeType = obj.mimeType);
	obj.withCredentials && (request.withCredentials = true);
	Object.keys(obj.headers).length && (request.headers = obj.headers);

	if (request.timeout > 0) {
	 request.timeoutTimer = setTimeout(function() {
		cb && cb(new Error('ETIMEDOUT'));
		xhr.abort();
	 }, request.timeout);
	}

	var xhrSuccessStatus = {
	 0: 200,
	 1223: 204
	};

	var xhr = mono.request.getTransport(obj.localXHR);
	xhr.open(request.method, request.url, true);

	if (request.mimeType) {
	 xhr.overrideMimeType(request.mimeType);
	}
	if (request.withCredentials) {
	 xhr.withCredentials = true;
	}

	var specialHeaders = [];
	for (var key in request.headers) {
	 if (webRequest.isSpecialHeader(key)) {
		specialHeaders.push({
			key: key,
			value: request.headers[key]
		});
	 }
	 xhr.setRequestHeader(key, request.headers[key]);
	}

	var readyCallback = xhr.onload = function() {
	 var status = xhrSuccessStatus[xhr.status] || xhr.status;
	 try {
		if (status >= 200 && status < 300 || status === 304) {
			var body = xhr.responseText;
			if (request.json) {
		body = JSON.parse(body);
			} else
			if (request.xml) {
		body = (new DOMParser()).parseFromString(body, "text/xml");
			} else
			if (typeof body !== 'string') {
		console.error('Response is not string!', body);
		throw new Error('Response is not string!');
			}
			return cb && cb(null, body);
		}
		throw new Error(xhr.status + ' ' + xhr.statusText);
	 } catch (e) {
		return cb && cb(e);
	 }
	};

	var errorCallback = xhr.onerror = function() {
	 cb && cb(new Error(xhr.status + ' ' + xhr.statusText));
	};

	var stateChange = null;
	if (xhr.onabort !== undefined) {
	 xhr.onabort = errorCallback;
	} else {
	 stateChange = function() {
		if (xhr.readyState === 4) {
			cb && setTimeout(function() {
		return errorCallback();
			});
		}
	 };
	}

	if (mono.isSafari && mono.badXhrHeadRedirect && request.method === 'HEAD') {
	 stateChange = (function(cb) {
		if (xhr.readyState > 1) {
			// Safari 5 on HEAD 302 redirect fix
			setTimeout(function() {
		xhr.abort();
			});
			return readyCallback();
		}

		return cb && cb();
	 }).bind(null, stateChange);
	}

	if (mono.isOpera && mono.badXhrRedirect) {
	 stateChange = (function(cb) {
		if (xhr.readyState > 1 && (xhr.status === 302 || xhr.status === 0)) {
			// Opera 12 XHR redirect
			if (!obj._redirectCount) {
		obj._redirectCount = 0;
			}
			var location = xhr.getResponseHeader('Location');
			if (location && obj._redirectCount < 5) {
		obj._redirectCount++;
		var redirectObj = mono.extend({}, obj);
		redirectObj.url = location;

		cb = null;
		xhr.abort();
		var redirectRequest = mono.request(redirectObj, origCb);
		mono.extend(result, redirectRequest);
		return;
			}
		}

		return cb && cb();
	 }).bind(null, stateChange);
	}

	if (stateChange) {
	 xhr.onreadystatechange = stateChange;
	}

	var next = function() {
	 try {
		xhr.send(request.data || null);
	 } catch (e) {
		setTimeout(function() {
			cb && cb(e);
		});
	 }
	};

	if (specialHeaders.length && webRequest.isSupported()) {
	 var setSpecialHeaders = function() {
		for (var i = 0, item; item = specialHeaders[i]; i++) {
			xhr.setRequestHeader(webRequest.wrapHeaderKey(item.key, item.value), item.value);
		}
	 };
	 webRequest.requestPermission(function(grunted) {
		grunted && setSpecialHeaders();
		cb && next();
	 });
	} else {
	 next();
	}

	result.abort = function() {
	 cb = null;
	 xhr.abort();
	};

	return result;
		};

		mono.request.getTransport = function(localXHR) {
	if (mono.isGM && !localXHR) {
	 return new mono.request.gmTransport();
	}

	return new XMLHttpRequest();
		};

		mono.request.gmTransport = function() {
	"use strict";
	var _this = this;
	var gmXhr = null;

	var sync = function(type, gmResponse) {
	 _this.readyState = gmResponse.readyState;
	 _this.status = gmResponse.status;
	 _this.statusText = gmResponse.statusText;
	 if (typeof gmResponse.response === 'string') {
		_this.responseText = gmResponse.response;
	 }
	 if (gmResponse.responseText) {
		_this.responseText = gmResponse.responseText;
	 }
	 _this._responseHeaders = gmResponse.responseHeaders;

	 _this.onreadystatechange && _this.onreadystatechange();

	 _this[type] && _this[type]();
	};

	var gmDetails = {
	 headers: {},
	 responseType: 'text',
	 onload: sync.bind(null, 'onload'),
	 onerror: sync.bind(null, 'onerror'),
	 onabort: sync.bind(null, 'onabort'),
	 ontimeout: sync.bind(null, 'ontimeout')
	};

	this._responseHeaders = '';
	this.readyState = 0;
	this.status = 0;
	this.statusText = '';
	this.responseText = '';
	this.response = '';
	this.responseType = '';
	this.responseURL = '';
	this.open = function(method, url) {
	 gmDetails.method = method;
	 gmDetails.url = url;
	};
	this.overrideMimeType = function(mimeType) {
	 gmDetails.overrideMimeType = mimeType;
	};
	this.setRequestHeader = function(key, value) {
	 if (key.toLowerCase() === 'cookie' && value === '') {
		gmDetails.anonymous = true;
	 }
	 gmDetails.headers[key] = value;
	};
	this.getResponseHeader = function(name) {
	 if (!this._responseHeaders) {
		return null;
	 }

	 name = name.toLowerCase();
	 if (!this.headers) {
		this.headers = mono.parseXhrHeader(this._responseHeaders);
	 }
	 if (!this.headers.hasOwnProperty(name)) {
		return null;
	 }
	 return this.headers[name];
	};
	this.getAllResponseHeaders = function() {
	 return this._responseHeaders;
	};
	this.abort = function() {
	 gmXhr && gmXhr.abort();
	};
	this.send = function(data) {
	 gmDetails.data = data;
	 gmXhr = GM_xmlhttpRequest(gmDetails);
	};
	this.onabort = null;
	this.onerror = null;
	this.onload = null;
	this.onreadystatechange = null;
	this.ontimeout = null;
		};

		mono.extend = function() {
	var obj = arguments[0];
	for (var i = 1, len = arguments.length; i < len; i++) {
	 var item = arguments[i];
	 for (var key in item) {
		if (item[key] !== undefined) {
			obj[key] = item[key];
		}
	 }
	}
	return obj;
		};

		mono.extendPos = function() {
	var obj = arguments[0];
	for (var i = 1, len = arguments.length; i < len; i++) {
	 var item = arguments[i];
	 for (var key in item) {
		if (item[key] !== undefined) {
			delete obj[key];
			obj[key] = item[key];
		}
	 }
	}
	return obj;
		};

		mono.param = function(obj) {
	if (typeof obj === 'string') {
	 return obj;
	}
	var itemsList = [];
	for (var key in obj) {
	 if (!obj.hasOwnProperty(key)) {
		continue;
	 }
	 if (obj[key] === undefined || obj[key] === null) {
		obj[key] = '';
	 }
	 itemsList.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
	}
	return itemsList.join('&');
		};

		mono.capitalize = function(word) {
	"use strict";
	return word.charAt(0).toUpperCase() + word.substr(1);
		};

		/**
		 * @param {string|Element|DocumentFragment} tagName
		 * @param {Object} obj
		 * @returns {Element|DocumentFragment}
		 */
		mono.create = function(tagName, obj) {
	"use strict";
	var el;
	var func;
	if (typeof tagName !== 'object') {
	 el = document.createElement(tagName);
	} else {
	 el = tagName;
	}
	for (var attr in obj) {
	 var value = obj[attr];
	 if (func = mono.create.hook[attr]) {
		func(el, value);
		continue;
	 }
	 el[attr] = value;
	}
	return el;
		};
		mono.create.hook = {
	text: function(el, value) {
	 "use strict";
	 el.textContent = value;
	},
	data: function(el, value) {
	 "use strict";
	 for (var item in value) {
		el.dataset[item] = value[item];
	 }
	},
	class: function(el, value) {
	 "use strict";
	 if (Array.isArray(value)) {
		for (var i = 0, len = value.length; i < len; i++) {
			el.classList.add(value[i]);
		}
	 } else {
		el.setAttribute('class', value);
	 }
	},
	style: function(el, value) {
	 "use strict";
	 if (typeof value === 'object') {
		for (var item in value) {
			var key = item;
			if (key === 'float') {
		key = 'cssFloat';
			}
			var _value = value[item];
			if (Array.isArray(_value)) {
		for (var i = 0, len = _value.length; i < len; i++) {
		 el.style[key] = _value[i];
		}
			} else {
		el.style[key] = _value;
			}
		}
	 } else {
		el.setAttribute('style', value);
	 }
	},
	append: function(el, value) {
	 "use strict";
	 if (!Array.isArray(value)) {
		value = [value];
	 }
	 for (var i = 0, len = value.length; i < len; i++) {
		var node = value[i];
		if (!node && node !== 0) {
			continue;
		}
		if (typeof node !== 'object') {
			node = document.createTextNode(node);
		}
		el.appendChild(node);
	 }
	},
	on: function(el, eventList) {
	 "use strict";
	 if (typeof eventList[0] !== 'object') {
		eventList = [eventList];
	 }
	 for (var i = 0, len = eventList.length; i < len; i++) {
		var args = eventList[i];
		if (!Array.isArray(args)) {
			continue;
		}
		mono.on.apply(mono, [el].concat(args));
	 }
	},
	one: function(el, eventList) {
	 "use strict";
	 if (typeof eventList[0] !== 'object') {
		eventList = [eventList];
	 }
	 for (var i = 0, len = eventList.length; i < len; i++) {
		var args = eventList[i];
		if (!Array.isArray(args)) {
			continue;
		}
		mono.one.apply(mono, [el].concat(args));
	 }
	},
	onCreate: function(el, value) {
	 "use strict";
	 value.call(el, el);
	},
	attr: function(el, attrObj) {
	 var prop, value;
	 for (prop in attrObj) {
		value = attrObj[prop];
		el.setAttribute(prop, value);
	 }
	}
		};

		mono.parseTemplate = function(list, details) {
	details = details || {};

	if (typeof list === "string") {
	 if (list[0] !== '[') {
		return document.createTextNode(list);
	 }
	 try {
		list = list.replace(/"/g, '\\u0022').replace(/\\'/g, '\\u0027').replace(/'/g, '"').replace(/([{,])\s*([a-zA-Z0-9]+):/g, '$1"$2":');
		list = JSON.parse(list);
	 } catch (e) {
		return document.createTextNode(list);
	 }
	}
	if (!Array.isArray(list)) {
	 return document.createTextNode(list);
	}
	var fragment = details.fragment || document.createDocumentFragment();
	for (var i = 0, len = list.length; i < len; i++) {
	 var item = list[i];
	 if (typeof item === 'object') {
		for (var tagName in item) {
			var el = item[tagName];
			var append = el.append;
			delete el.append;
			var dEl;
			fragment.appendChild(dEl = mono.create(tagName, el));
			if (append !== undefined) {
		mono.parseTemplate(append, {
		 fragment: dEl
		});
			}
		}
	 } else {
		fragment.appendChild(document.createTextNode(item));
	 }
	}
	return fragment;
		};

		mono.trigger = function(el, type, data) {
	if (data === undefined) {
	 data = {};
	}
	if (data.bubbles === undefined) {
	 data.bubbles = false;
	}
	if (data.cancelable === undefined) {
	 data.cancelable = false;
	}
	var event = null;
	if (typeof MouseEvent === 'function' &&
	 ['click'].indexOf(type) !== -1) {
	 event = new MouseEvent(type, data);
	} else {
	 event = new CustomEvent(type, data);
	}
	el.dispatchEvent(event);
		};

		mono.urlPatternToStrRe = function(value) {
	"use strict";
	if (value === '<all_urls>') {
	 return '^https?:\\/\\/.+$';
	}

	var m = value.match(/(\*|http|https|file|ftp):\/\/([^\/]+)(?:\/(.*))?/);
	if (!m) {
	 throw new Error("Invalid url-pattern");
	}

	var scheme = m[1];
	if (scheme === '*') {
	 scheme = 'https?';
	}

	var host = m[2];
	if (host === '*') {
	 host = '.+';
	} else {
	 host = mono.escapeRegex(host);
	 host = host.replace(/^\\\*\\\./, '(?:[^\/]+\\.)?');
	 host = host.replace(/\\\.\\\*$/g, '\\.[a-z\\.]{2,}');
	}

	var pattern = ['^', scheme, ':\\/\\/', host];

	var path = m[3];
	if (!path) {
	 pattern.push('$');
	} else
	if (path === '*') {
	 path = '(?:|\/.*)';
	 pattern.push(path);
	 pattern.push('$');
	} else
	if (path) {
	 path = '\/' + path;
	 path = mono.escapeRegex(path);
	 path = path.replace(/\\\*/g, '.*');
	 pattern.push(path);
	 pattern.push('$');
	}

	return pattern.join('');
		};

		mono.isIframe = function() {
	return window.top !== window.self;
		};

		mono.userJsCheck = function() {
	"use strict";
	if (mono.isGM) {
	 return;
	}

	if (!mono.hasPageScript()) {
	 return;
	}

	try {
	 if (window.sessionStorage['savefrom-helper-userjs'] === '1') {
		mono.sendMessage({
			action: 'userjsDetected'
		});
	 }
	} catch (e) {}
		};

		mono.hasPageScript = function() {
	"use strict";
	var moduleLoadedList = mono.loadModule.moduleLoadedList;
	return moduleLoadedList.some(function(moduleName) {
	 if (['sovetnik', 'aviaBar', 'fsStat', 'swStat'].indexOf(moduleName) === -1) {
		return true;
	 }
	});
		};

		mono.setExtensionSession = function() {
	"use strict";
	try {
	 if (!mono.hasPageScript()) {
		delete window.sessionStorage['savefrom-helper-extension'];
	 } else {
		window.sessionStorage['savefrom-helper-extension'] = '1';
	 }
	} catch (e) {}
		};

		mono.loadModule = (function() {
	var moduleNameList = [];
	var moduleList = [];
	var loadedModuleList = [];

	var moduleLoad = function(data) {
	 var hasActiveModule = false;

	 var item, availFn, isAvailable, moduleName, fn;
	 while (item = moduleList.shift()) {
		moduleName = item[0];
		fn = item[1];
		availFn = item[2];

		isAvailable = !availFn;
		try {
			!isAvailable && (isAvailable = availFn(data));
		} catch (e) {
			mono.error('Module available error!', e);
		}

		if (isAvailable) {
			loadedModuleList.push(moduleName);
			hasActiveModule = true;
			try {
		mono.debug('run module', moduleName, data);
		fn(moduleName, data);
			} catch (e) {
		mono.error('Module error!', e);
			}
		} else {
			mono.debug('unavailable', moduleName, data);
		}
	 }

	 if (hasActiveModule && !mono.isGM) {
		mono.setExtensionSession();
		mono.userJsCheck();
	 }
	};

	var requestData = function() {
	 "use strict";
	 var limit = 20;

	 var onceResponse = mono.onceFn(function(data) {
		getData = null;

		mono.global.language = data.getLanguage;
		mono.global.preference = data.getPreference;

		return moduleLoad(data);
	 });

	 var getData = function() {
		setTimeout(function() {
			limit--;
			if (limit < 0 || mono.isGM) {
		getData = null;
			}
			getData && getData();
		}, 250);

		mono.sendMessage({
			action: 'getLanguageAndPreference'
		}, onceResponse);
	 };
	 getData();
	};

	var loader = function(moduleName, cb, isAsyncAvailable, syncIsAvailable) {
	 if (moduleNameList.indexOf(moduleName) === -1) {
		moduleNameList.push(moduleName);

		var isAvailable = !syncIsAvailable;
		try {
			!isAvailable && (isAvailable = syncIsAvailable());
		} catch (e) {
			mono.error('Module available error!', e);
		}

		if (isAvailable) {
			moduleList.push(arguments);
			if (moduleList.length === 1) {
		requestData();
			}
		} else {
			mono.debug('sync unavailable', moduleName);
		}
	 } else {
		mono.debug('Module exists', moduleName);
	 }
	};

	loader.moduleLoadedList = loadedModuleList;
	loader.moduleList = moduleList;

	return loader;
		})();

		/**
		 * @param {Element} parent
		 * @param {Element} target
		 * @return {boolean}
		 */
		mono.contains = function(parent, target) {
	var rnative = /^[^{]+\{\s*\[native \w/;
	if (rnative.test(document.compareDocumentPosition) || rnative.test(document.contains)) {
	 mono.contains = function(a, b) {
		// from Sizzle
		var adown = a.nodeType === 9 ? a.documentElement : a,
			bup = b && b.parentNode;
		return a === bup || !!(bup && bup.nodeType === 1 && (
			adown.contains ?
			adown.contains(bup) :
			a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
		));
	 };
	} else {
	 mono.contains = function(a, b) {
		if (b) {
			while (b = b.parentNode) {
		if (b === a) {
		 return true;
		}
			}
		}
		return false;
	 };
	}
	return mono.contains.apply(this, arguments);
		};
		(function() {
	"use strict";
	var getTwoElParent = function(a, b, parentList) {
	 parentList.unshift(b);
	 while (b = b.parentNode) {
		if (mono.contains(b, a)) {
			return b;
		}
		parentList.unshift(b);
	 }
	 return null;
	};
	var wrapEvent = function(origType, fixType, origEvent, capture) {
	 return !capture ? function(event) {
		var related = event.relatedTarget;
		var target = this;
		if (!related || (related !== target && !mono.contains(target, related))) {
			origEvent.call(this, {
		type: origType,
		target: target,
		preventDefault: event.preventDefault,
		stopPropagation: event.stopPropagation
			});
		}
	 } : function(event) {
		var related = event.relatedTarget;
		var target = event.target;
		var parentList = [];
		if (!related || mono.contains(related, target) || (related = getTwoElParent(related, target, parentList))) {
			if (parentList.length === 0) {
		while (target !== related) {
		 parentList.unshift(target);
		 target = target.parentNode;
		}
			}
			while (target = parentList.shift()) {
		origEvent.call(this, {
		 type: origType,
		 target: target,
		 preventDefault: event.preventDefault,
		 stopPropagation: event.stopPropagation
		});
			}
		}
	 };
	};

	var functionMap = {
	 key: 'fixEvent-',
	 eventId: 0,
	 replaceList: {},
	 bindCount: {}
	};

	/**
	 * @param {Node} el
	 * @param {String} type
	 * @param {Function} onEvent
	 * @param {Boolean} [capture]
	 */
	mono.on = function(el, type, onEvent, capture) {
	 if (mono.noMouseEnter && ['mouseenter', 'mouseleave'].indexOf(type) !== -1) {
		var cacheEventKey = functionMap.key;
		var origEvent = onEvent;
		var origType = type;
		var origCapture = capture;

		if (type === 'mouseenter') {
			type = 'mouseover';
		} else
		if (type === 'mouseleave') {
			type = 'mouseout';
		}
		cacheEventKey += type;
		if (capture) {
			cacheEventKey += '-1';
			capture = false;
		}

		var eventId = origEvent[cacheEventKey];
		if (eventId === undefined) {
			eventId = functionMap.eventId++;
			origEvent[cacheEventKey] = eventId;

			onEvent = wrapEvent(origType, type, origEvent, origCapture);

			functionMap.replaceList[eventId] = onEvent;

			if (functionMap.bindCount[eventId] === undefined) {
		functionMap.bindCount[eventId] = 0;
			}
		} else {
			onEvent = functionMap.replaceList[eventId];
		}

		functionMap.bindCount[eventId]++;
	 }

	 el.addEventListener(type, onEvent, capture);
	};

	/**
	 * @param {Node} el
	 * @param {String} type
	 * @param {Function} onEvent
	 * @param {Boolean} [capture]
	 */
	mono.off = function(el, type, onEvent, capture) {
	 if (mono.noMouseEnter && ['mouseenter', 'mouseleave'].indexOf(type) !== -1) {
		var cacheEventKey = functionMap.key;
		if (type === 'mouseenter') {
			type = 'mouseover';
		} else
		if (type === 'mouseleave') {
			type = 'mouseout';
		}
		cacheEventKey += type;
		if (capture) {
			cacheEventKey += '-1';
			capture = false;
		}

		var eventId = onEvent[cacheEventKey];
		if (eventId !== undefined) {
			var origEvent = onEvent;
			onEvent = functionMap.replaceList[eventId];
			functionMap.bindCount[eventId]--;

			if (functionMap.bindCount[eventId] === 0) {
		delete origEvent[cacheEventKey];
		delete functionMap.replaceList[eventId];
		delete functionMap.bindCount[eventId];
			}
		}
	 }

	 el.removeEventListener(type, onEvent, capture);
	};

	/**
	 * @param {Node} el
	 * @param {String} type
	 * @param {Function} onEvent
	 * @param {Boolean} [capture]
	 */
	mono.one = function(el, type, onEvent, capture) {
	 var fnName = ['oneFn', type, !!capture].join('_');
	 var fn = onEvent[fnName];
	 if (!fn) {
		onEvent[fnName] = fn = function(e) {
			mono.off(this, type, fn, capture);
			onEvent.apply(this, arguments);
		}
	 }
	 mono.on(el, type, fn, capture);
	 fnName = null;
	 el = null;
	};
		}());

		mono.global = {};

		mono.initGlobal = function(cb) {
	if (!mono.isGM && mono.global.language && mono.global.preference) {
	 return cb({
		getLanguage: mono.global.language,
		getPreference: mono.global.preference
	 });
	}
	mono.sendMessage({
	 action: 'getLanguageAndPreference'
	}, function(response) {
	 mono.global.language = response.getLanguage;
	 mono.global.preference = response.getPreference;
	 cb(response);
	});
		};

		mono.getParentByClass = function(el, classList) {
	if (!Array.isArray(classList)) {
	 classList = [classList];
	}

	for (var parent = el; parent; parent = parent.parentNode) {
	 if (parent.nodeType !== 1) {
		return null;
	 }
	 for (var i = 0, className; className = classList[i]; i++) {
		if (parent.classList.contains(className)) {
			return parent;
		}
	 }
	}

	return null;
		};

		/**
		 * @param {string} url
		 * @param {Object} [details]
		 * @param {boolean} [details.params] Input params only [false]
		 * @param {string} [details.sep] Separator [&]
		 * @param {boolean} [details.noDecode] Disable decode keys [false]
		 * @returns {{}}
		 */
		mono.parseUrl = function(url, details) {
	details = details || {};
	var query = null;
	if (!details.params && /\?/.test(url)) {
	 query = url.match(/[^?]*\?(.*)/)[1];
	} else {
	 query = url;
	}
	var separator = details.sep || '&';
	var dblParamList = query.split(separator);
	var params = {};
	for (var i = 0, len = dblParamList.length; i < len; i++) {
	 var item = dblParamList[i];
	 var keyValue = item.split('=');
	 var key = keyValue[0];
	 var value = keyValue[1] || '';
	 if (!details.noDecode) {
		try {
			key = decodeURIComponent(key);
		} catch (err) {
			key = unescape(key);
		}
		try {
			params[key] = decodeURIComponent(value);
		} catch (err) {
			params[key] = unescape(value);
		}
	 } else {
		params[key] = value;
	 }
	}
	return params;
		};

		mono.throttle = function(fn, threshhold, scope) {
	threshhold = threshhold || 250;
	var last;
	var deferTimer;
	return function() {
	 var context = scope || this;

	 var now = Date.now();
	 var args = arguments;
	 if (last && now < last + threshhold) {
		// hold on to it
		clearTimeout(deferTimer);
		deferTimer = setTimeout(function() {
			last = now;
			fn.apply(context, args);
		}, threshhold);
	 } else {
		last = now;
		fn.apply(context, args);
	 }
	};
		};

		mono.debounce = function(fn, delay) {
	var timer = null;
	return function() {
	 var context = this,
		args = arguments;
	 clearTimeout(timer);
	 timer = setTimeout(function() {
		fn.apply(context, args);
	 }, delay);
	};
		};

		mono.getDomain = function(url, strip) {
	var m = /:\/\/(?:[^\/?#]*@)?([^:\/?#]+)/.exec(url);
	m = m && m[1];
	if (m) {
	 if (strip) {
		m = m.replace(/^www\./, '');
	 }
	}
	return m;
		};

		// legacy

		mono.getQueryString = function(query, key_prefix, key_suffix) {
	if (!query || typeof(query) != 'object')
	 return '';

	if (key_prefix === undefined)
	 key_prefix = '';

	if (key_suffix === undefined)
	 key_suffix = '';

	var str = '';
	for (var key in query) {
	 if (str.length)
		str += '&';

	 if (query[key] instanceof Object) {
		if (!key_prefix)
			key_prefix = '';

		if (!key_suffix)
			key_suffix = '';

		str += mono.getQueryString(query[key], key_prefix + key + "[", "]" + key_suffix);
	 } else
		str += key_prefix + escape(key) + key_suffix + '=' + escape(query[key]);
	}

	return str;
		};

		/**
		 * @param {string} text
		 * @returns {string}
		 */
		mono.decodeUnicodeEscapeSequence = function(text) {
	try {
	 return JSON.parse(JSON.stringify(text)
		.replace(mono.decodeUnicodeEscapeSequence.re, '$1'));
	} catch (e) {
	 return text;
	}
		};
		mono.decodeUnicodeEscapeSequence.re = /\\(\\u[0-9a-f]{4})/g;

		mono.fileName = {
	maxLength: 80,

	rtrim: /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,

	illegalRe: /[\/?<>\\:*|"~\u202B]/g,

	controlRe: /[\x00-\x1f\x80-\x9f]/g,

	reservedRe: /^\.+/,

	trim: function(text) {
	 return text.replace(this.rtrim, '');
	},

	partsRe: /^(.+)\.([a-z0-9]{1,4})$/i,

	getParts: function(name) {
	 return name.match(this.partsRe);
	},

	specialChars: ('nbsp,iexcl,cent,pound,curren,yen,brvbar,sect,uml,copy,ordf,laquo,not,shy,reg,macr,deg,plusmn,sup2' +
	 ',sup3,acute,micro,para,middot,cedil,sup1,ordm,raquo,frac14,frac12,frac34,iquest,Agrave,Aacute,Acirc,Atilde,Auml' +
	 ',Aring,AElig,Ccedil,Egrave,Eacute,Ecirc,Euml,Igrave,Iacute,Icirc,Iuml,ETH,Ntilde,Ograve,Oacute,Ocirc,Otilde,Ouml' +
	 ',times,Oslash,Ugrave,Uacute,Ucirc,Uuml,Yacute,THORN,szlig,agrave,aacute,acirc,atilde,auml,aring,aelig,ccedil' +
	 ',egrave,eacute,ecirc,euml,igrave,iacute,icirc,iuml,eth,ntilde,ograve,oacute,ocirc,otilde,ouml,divide,oslash' +
	 ',ugrave,uacute,ucirc,uuml,yacute,thorn,yuml').split(','),
	specialCharsList: [
	 ['amp', 'quot', 'lt', 'gt'],
	 [38, 34, 60, 62]
	],

	specialCharsRe: /&([^;]{2,6});/g,

	/**
	 * @param {string} text
	 * @returns {string}
	 */
	decodeSpecialChars: function(text) {
	 var _this = this;
	 return text.replace(this.specialCharsRe, function(text, word) {
		var code = null;
		if (word[0] === '#') {
			code = parseInt(word.substr(1));
			if (isNaN(code)) {
		return '';
			}
			return String.fromCharCode(code);
		}

		var pos = _this.specialCharsList[0].indexOf(word);
		if (pos !== -1) {
			code = _this.specialCharsList[1][pos];
			return String.fromCharCode(code);
		}

		pos = _this.specialChars.indexOf(word);
		if (pos !== -1) {
			code = pos + 160;
			return String.fromCharCode(code);
		}

		return '';
	 });
	},

	decodeHexChars: function(text) {
	 return text.replace(/(\\x[a-zA-Z0-9]{2})/g, function(text, word) {
		var char = word;
		try {
			char = String.fromCharCode(parseInt('0x' + char.substr(2), 16));
		} catch (e) {}
		return char;
	 });
	},

	rnRe: /\r?\n/g,

	re1: /[*?"]/g,

	re2: /</g,

	re3: />/g,

	spaceRe: /[\s\t\uFEFF\xA0]+/g,

	dblRe: /(\.|!|\?|_|,|-|:|\+){2,}/g,

	re4: /[.,:;\/\-_+=']$/g,

	/**
	 * @param {string} name
	 * @returns {string}
	 */
	modify: function(name) {
	 if (!name) {
		return '';
	 }

	 name = mono.decodeUnicodeEscapeSequence(name);

	 try {
		name = decodeURIComponent(name);
	 } catch (err) {
		name = unescape(name);
	 }

	 name = this.decodeSpecialChars(name);

	 name = name.replace(this.rnRe, ' ');

	 name = this.trim(name);

	 name = name.replace(this.re1, '')
		.replace(this.re2, '(')
		.replace(this.re3, ')')
		.replace(this.spaceRe, ' ')
		.replace(this.dblRe, '$1')
		.replace(this.illegalRe, '_')
		.replace(this.controlRe, '')
		.replace(this.reservedRe, '')
		.replace(this.re4, '');

	 if (name.length > this.maxLength) {
		var parts = this.getParts(name);
		if (parts && parts.length == 3) {
			parts[1] = parts[1].substr(0, this.maxLength);
			name = parts[1] + '.' + parts[2];
		}
	 }

	 return this.trim(name);
	}
		};
		/**
		 * @param {number} min
		 * @param {number} max
		 * @returns {number}
		 */
		mono.getRandomInt = function(min, max) {
	return Math.floor(Math.random() * (max - min)) + min;
		};
		/**
		 * @param {string} dataName
		 * @returns {string}
		 */
		mono.dataAttr2Selector = function(dataName) {
	return 'data-' + dataName.replace(/[A-Z]/g, function(lit) {
	 return '-' + lit.toLowerCase();
	});
		};
		/**
		 * @param {Object} obj
		 * @returns {boolean}
		 */
		mono.isEmptyObject = function(obj) {
	for (var item in obj) {
	 return false;
	}
	return true;
		};

		/**
		 * @param {function} cb
		 */
		mono.asyncCall = function(cb) {
	"use strict";
	setTimeout(function() {
	 cb();
	}, 0);
		};

		/**
		 * @param {Function} cb
		 * @param {Object} [scope]
		 * @returns {Function}
		 */
		mono.asyncFn = function(cb, scope) {
	return function() {
	 var context = scope || this;
	 var args = arguments;
	 setTimeout(function() {
		cb.apply(context, args);
	 }, 0);
	};
		};

		/**
		 * @param {Function} cb
		 * @param {Object} [scope]
		 * @returns {Function}
		 */
		mono.onceFn = function(cb, scope) {
	return function() {
	 if (cb) {
		var context = scope || this;
		cb.apply(context, arguments);
		cb = null;
	 }
	};
		};

		/**
		 * @param {Function} fn
		 * @param {Number} [timeout]
		 * @param {Function} alwaysCb
		 * @param {Object} [scope]
		 * @param {Function} [onTimeout]
		 */
		mono.safeFn = function(fn, timeout, alwaysCb, scope, onTimeout) {
	if (typeof timeout === 'function') {
	 onTimeout = scope;
	 scope = alwaysCb;
	 alwaysCb = timeout;
	 timeout = 3000;
	}
	if (typeof scope === 'function') {
	 onTimeout = scope;
	 scope = null;
	}
	var timeoutId = null;
	var onceCb = mono.onceFn(function() {
	 clearTimeout(timeoutId);
	 alwaysCb.apply(this, arguments);
	});

	return function() {
	 var context = scope || this;
	 var args = [].slice.call(arguments);

	 var pos = args.indexOf(alwaysCb);
	 if (pos !== -1) {
		args.splice(pos, 1, onceCb);
	 }

	 setTimeout(function() {
		timeoutId = setTimeout(function() {
			onceCb();
			onTimeout && onTimeout();
		}, timeout);
		fn.apply(context, args);
	 }, 0);
	};
		};

		/**
		 * @param {string} html
		 * @param {RegExp|RegExp[]} match
		 * @returns {string[]}
		 */
		mono.getPageScript = function(html, match) {
	"use strict";
	if (match && !Array.isArray(match)) {
	 match = [match];
	}
	var scriptList = [];
	html.replace(/<script(?:\s*|\s[^>]+[^\/])>/g, function(text, offset) {
	 offset += text.length;
	 var endPos = html.indexOf('<\/script>', offset);
	 if (endPos !== -1) {
		var content = html.substr(offset, endPos - offset);
		if (match) {
			match.every(function(r) {
		return r.test(content);
			}) && scriptList.push(content);
		} else {
			scriptList.push(content);
		}
	 }
	});
	return scriptList;
		};

		mono.findJsObject = function(code) {
	"use strict";
	var readString = function(code) {
	 var char = code[0],
		pos = 0;
	 while (true) {
		pos = code.indexOf(char, pos + 1);
		if (pos === -1) {
			pos = code.length;
			break;
		}
		if (code[pos - 1] !== '\\') {
			break;
		}
	 }
	 var str = '';
	 try {
		if (char === '"') {
			str = JSON.parse('"' + code.substr(1, pos - 1) + '"');
		} else {
			str = JSON.parse('"' + code.substr(1, pos - 1).replace(/\\'/g, '\'').replace(/"/g, '\\"') + '"');
		}
	 } catch (e) {}
	 return {
		data: str,
		i: pos
	 };
	};
	var readObject = function(code) {
	 var items = [],
		item = ['', ''],
		index = 0,
		result, readerFn;
	 for (var i = 1, symbol; symbol = code[i]; i++) {
		readerFn = symbolReader[symbol];
		if (readerFn) {
			result = readerFn(code.substr(i));
			item[index] = index === 0 ? result.data : JSON.stringify(result.data);
			i += result.i;
		} else
		if (symbol === '}') {
			break;
		} else
		if (symbol === ':') {
			index = 1;
		} else
		if (symbol === ',') {
			items.push(JSON.stringify(item[0]) + ':' + item[1]);
			item = ['', ''];
			index = 0;
		} else {
			item[index] = (item[index] + symbol).trim();
		}
	 }
	 if (item[1]) {
		items.push(JSON.stringify(item[0]) + ':' + item[1]);
	 }

	 try {
		var jsonObject = JSON.parse('{' + items.join(',') + '}');
	 } catch (e) {}

	 return {
		data: jsonObject || {},
		i: i
	 };
	};
	var readArray = function(code) {
	 var items = [],
		item = '',
		result, readerFn;
	 for (var i = 1, symbol; symbol = code[i]; i++) {
		readerFn = symbolReader[symbol];
		if (readerFn) {
			result = readerFn(code.substr(i));
			item = JSON.stringify(result.data);
			i += result.i;
		} else
		if (symbol === ']') {
			break;
		} else
		if (symbol === ',') {
			item && items.push(item);
			item = '';
		} else {
			item += symbol;
		}
	 }
	 item && items.push(item);

	 try {
		var jsonArray = JSON.parse('[' + items.join(',') + ']');
	 } catch (e) {}

	 return {
		data: jsonArray || [],
		i: i
	 };
	};
	var readCode = function(code, matchFn) {
	 var result;
	 for (var i = 0, symbol; symbol = code[i]; i++) {
		if (symbol === '[' || symbol === '{') {
			result = symbolReader[symbol](code.substr(i));
			i += result.i;
			if (matchFn(result.data)) {
		return true;
			}
		}
	 }
	};
	var symbolReader = {
	 '[': readArray,
	 '{': readObject,
	 '"': readString,
	 '\'': readString
	};
	return {
	 some: function(matchFn) {
		return readCode(code, matchFn);
	 }
	};
		};

		/**
		 * @param {string} html
		 * @param {RegExp|RegExp[]} match
		 * @returns {Object[]}
		 */
		mono.findJson = function(html, match) {
	"use strict";
	if (match && !Array.isArray(match)) {
	 match = [match];
	}
	var rawJson = [];
	var obj = {
	 '{': 0,
	 '[': 0
	};
	var map = {
	 '}': '{',
	 ']': '['
	};
	var jsonSymbols = /[{}\]\[":0-9.,-]/;
	var whiteSpace = /[\r\n\s\t]/;
	var jsonText = '';
	for (var i = 0, symbol; symbol = html[i]; i++) {
	 if (symbol === '"') {
		var end = i;
		while (end !== -1 && (end === i || html[end - 1] === '\\')) {
			end = html.indexOf('"', end + 1);
		}
		if (end === -1) {
			end = html.length - 1;
		}
		jsonText += html.substr(i, end - i + 1);
		i = end;
		continue;
	 }

	 if (!jsonSymbols.test(symbol)) {
		if (symbol === 't' && html.substr(i, 4) === 'true') {
			jsonText += 'true';
			i += 3;
		} else
		if (symbol === 'f' && html.substr(i, 5) === 'false') {
			jsonText += 'false';
			i += 4;
		} else
		if (symbol === 'n' && html.substr(i, 4) === 'null') {
			jsonText += 'null';
			i += 3;
		} else
		if (!whiteSpace.test(symbol)) {
			obj['{'] = 0;
			obj['['] = 0;
			jsonText = '';
		}
		continue;
	 }

	 jsonText += symbol;

	 if (symbol === '{' || symbol === '[') {
		if (!obj['{'] && !obj['[']) {
			jsonText = symbol;
		}
		obj[symbol]++;
	 } else
	 if (symbol === '}' || symbol === ']') {
		obj[map[symbol]]--;
		if (!obj['{'] && !obj['[']) {
			rawJson.push(jsonText);
		}
	 }
	}
	var jsonList = [];
	for (var i = 0, item; item = rawJson[i]; i++) {
	 if (item === '{}' || item === '[]') {
		continue;
	 }
	 try {
		if (match) {
			match.every(function(r) {
		return r.test(item);
			}) && jsonList.push(JSON.parse(item));
		} else {
			jsonList.push(JSON.parse(item));
		}
	 } catch (e) {
		// console.log('bad json', item);
	 }
	}
	return jsonList;
		};

		mono.findJsonString = function(html, match) {
	"use strict";
	if (match && !Array.isArray(match)) {
	 match = [match];
	}

	var rawString = [];
	var lastPos = -1;
	var pos = -1;
	do {
	 pos = html.indexOf('"', pos + 1);
	 if (pos !== -1) {
		if (html[pos - 1] === '\\') {
			continue;
		}
		if (lastPos !== -1) {
			rawString.push(html.substr(lastPos, pos + 1 - lastPos));
			lastPos = -1;
		} else {
			lastPos = pos;
		}
	 } else {
		lastPos = pos;
	 }
	} while (pos !== -1);

	var stringList = [];
	for (var i = 0, item; item = rawString[i]; i++) {
	 if (item === '""') {
		continue;
	 }
	 try {
		if (match) {
			match.every(function(r) {
		return r.test(item);
			}) && stringList.push(JSON.parse(item));
		} else {
			stringList.push(JSON.parse(item));
		}
	 } catch (e) {
		// console.log('bad json', item);
	 }
	}
	return stringList;
		};

		mono.getHtmlTags = function(tagName, str, match) {
	match = match || [];
	if (!Array.isArray(match)) {
	 match = [match];
	}
	var result = [];
	var tagOpenRe = new RegExp('(<' + tagName + '[^>]*>)', 'i');
	var tagCloseRe = new RegExp('(</' + tagName + '>)', 'i');

	var m = null;
	var openTag = '';
	var closeTag = '';
	var content = '';
	var pos = -1;
	while (m = tagOpenRe.exec(str)) {
	 openTag = m[1];
	 pos = str.indexOf(openTag);
	 if (pos === -1) {
		break;
	 }

	 str = str.substr(pos + openTag.length);
	 m = tagCloseRe.exec(str);
	 if (m) {
		closeTag = m[1];
		content = str.substr(0, str.indexOf(closeTag));
		result.push(openTag + content + closeTag);
	 }
	}
	return result.filter(function(str) {
	 return match.every(function(re) {
		return re.test(str);
	 });
	});
		};

		/**
		 * @param {string} str
		 * @param {string} location
		 * @return {Document}
		 */
		mono.parseHtmlFragment = function(str, location) {
	var parser = new DOMParser();
	var doc = parser.parseFromString('<html><body>' + str + '</body></html>', 'text/html');

	if (location) {
	 var base = doc.head.querySelector('base');
	 if (!base) {
		base = doc.createElement('base');
		base.href = location;
		doc.head.appendChild(base);
	 }
	}

	return doc;
		};

		/**
		 * @param {Object|Array} cssStyleObj
		 * @param {string|Array} [parentSelector]
		 * @returns {string}
		 */
		mono.style2Text = function(cssStyleObj, parentSelector) {
	"use strict";
	var list = [];

	if (!Array.isArray(cssStyleObj)) {
	 cssStyleObj = [cssStyleObj];
	}

	if (parentSelector && !Array.isArray(parentSelector)) {
	 parentSelector = [parentSelector];
	}

	var styleToText = function(selectorArr, styleObj) {
	 "use strict";
	 var content = [];

	 for (var item in styleObj) {
		var value = styleObj[item];

		if (item === 'cssFloat') {
			item = 'float';
		}

		var key = item.replace(/([A-Z])/g, function(text, letter) {
			return '-' + letter.toLowerCase();
		});

		content.push(key + ':' + value);
	 }

	 if (!content.length) {
		return '';
	 } else {
		return [selectorArr.join(','), '{', content.join(';'), '}'].join('');
	 }
	};

	var inheritSelector = function(section, selector) {
	 if (!Array.isArray(selector)) {
		selector = [selector];
	 }

	 if (parentSelector) {
		var _selector = [];
		var join = (section.join || section.join === '') ? section.join : ' ';
		parentSelector.forEach(function(parentSelector) {
			selector.forEach(function(selector) {
		_selector.push(parentSelector + join + selector);
			});
		});
		selector = _selector;
	 }

	 return selector;
	};

	cssStyleObj.forEach(function(section) {
	 var inhSelector = null;
	 var media = section.media;
	 var selector = section.selector;
	 var style = section.style;
	 var append = section.append;

	 if (media && append) {
		list.push([media, '{', mono.style2Text(append, parentSelector), '}'].join(''));
	 } else
	 if (!selector && !style) {
		for (var key in section) {
			if (['append', 'join'].indexOf(key) !== -1) {
		continue;
			}
			selector = key;
			style = section[key];

			append = style.append;
			if (append) {
		delete style.append;
			}

			inhSelector = inheritSelector(section, selector);
			list.push(styleToText(inhSelector, style));

			if (append) {
		list.push(mono.style2Text(append, inhSelector));
			}
		}
	 } else {
		inhSelector = inheritSelector(section, selector);
		list.push(styleToText(inhSelector, style));

		if (append) {
			list.push(mono.style2Text(append, inhSelector));
		}
	 }
	});

	return list.join('');
		};

		mono.styleReset = {
	animation: "none 0s ease 0s 1 normal none running",
	backfaceVisibility: "visible",
	background: "transparent none repeat 0 0 / auto auto padding-box border-box scroll",
	border: "medium none currentColor",
	borderCollapse: "separate",
	borderImage: "none",
	borderRadius: "0",
	borderSpacing: "0",
	bottom: "auto",
	boxShadow: "none",
	boxSizing: "content-box",
	captionSide: "top",
	clear: "none",
	clip: "auto",
	color: "inherit",
	columns: "auto",
	columnCount: "auto",
	columnFill: "balance",
	columnGap: "normal",
	columnRule: "medium none currentColor",
	columnSpan: "1",
	columnWidth: "auto",
	content: "normal",
	counterIncrement: "none",
	counterReset: "none",
	cursor: "auto",
	direction: "ltr",
	display: "inline",
	emptyCells: "show",
	float: "none",
	font: "normal normal normal normal medium/normal inherit",
	height: "auto",
	hyphens: "none",
	left: "auto",
	letterSpacing: "normal",
	listStyle: "disc outside none",
	margin: "0",
	maxHeight: "none",
	maxWidth: "none",
	minHeight: "0",
	minWidth: "0",
	opacity: "1",
	orphans: "0",
	outline: "medium none invert",
	overflow: "visible",
	overflowX: "visible",
	overflowY: "visible",
	padding: "0",
	pageBreakAfter: "auto",
	pageBreakBefore: "auto",
	pageBreakInside: "auto",
	perspective: "none",
	perspectiveOrigin: "50% 50%",
	position: "static",
	right: "auto",
	tabSize: "8",
	tableLayout: "auto",
	textAlign: "inherit",
	textAlignLast: "auto",
	textDecoration: "none solid currentColor",
	textIndent: "0",
	textShadow: "none",
	textTransform: "none",
	top: "auto",
	transform: "none",
	transformOrigin: "50% 50% 0",
	transformStyle: "flat",
	transition: "none 0s ease 0s",
	unicodeBidi: "normal",
	verticalAlign: "baseline",
	visibility: "visible",
	whiteSpace: "normal",
	widows: "0",
	width: "auto",
	wordSpacing: "normal",
	zIndex: "auto",
	all: "initial"
		};

		/**
		 * @param {string} host
		 * @param {Array} hostList
		 * @returns {boolean}
		 */
		mono.matchHost = function(host, hostList) {
	"use strict";
	var dotPos;
	while ((dotPos = host.indexOf('.')) !== -1) {
	 if (hostList.indexOf(host) !== -1) {
		return true;
	 }
	 host = host.substr(dotPos + 1);
	}

	return false;
		};

		/**
		 * @param {string} key
		 * @param {Function} callback
		 */
		mono.storage.getExpire = function(key, callback) {
	"use strict";
	var prefix = mono.storage.getExpire.prefix;
	var now = parseInt(Date.now() / 1000);
	var expireKey = key + prefix;
	return mono.storage.get([key, expireKey], function(storage) {
	 var isExpire = storage[expireKey] === undefined || storage[expireKey] < now;
	 var _storage = {};
	 _storage[key] = storage[key];
	 return callback(_storage, isExpire);
	});
		};

		mono.storage.getExpire.prefix = '_expire_';

		/**
		 * @param {Object} storage
		 * @param {number} time
		 * @param {function} [callback]
		 */
		mono.storage.setExpire = function(storage, time, callback) {
	"use strict";
	var prefix = mono.storage.getExpire.prefix;
	var now = parseInt(Date.now() / 1000);
	var setObj = {};
	for (var key in storage) {
	 setObj[key] = storage[key];
	 setObj[key + prefix] = now + time;
	}
	return mono.storage.set(setObj, function() {
	 return callback && callback();
	});
		};

		mono.onRemoveEventName = 'sf-removed-' + Math.floor(Math.random() * 1000000);
		mono.onRemoveClassName = 'sf-notify-on-remove-' + Math.floor(Math.random() * 1000000);

		/**
		 * @param {Element} node
		 * @param {Function} event
		 */
		mono.onRemoveEvent = function(node, event) {
	"use strict";
	node.classList.add(mono.onRemoveClassName);
	node.addEventListener(mono.onRemoveEventName, event);
		};
		/**
		 * @param {Element} node
		 */
		mono.onRemoveListener = function(node) {
	"use strict";
	mono.trigger(node, mono.onRemoveEventName);
		};
		/**
		 * @param {Element} node
		 * @param {Function} event
		 */
		mono.offRemoveEvent = function(node, event) {
	"use strict";
	node.removeEventListener(mono.onRemoveEventName, event);
		};

		/**
		 * @param {Node|Element} node
		 * @param {string} selector
		 * @returns {boolean}
		 */
		mono.matches = function(node, selector) {
	"use strict";
	var el = document.createElement('div');
	if (typeof el.matches === 'function') {
	 mono.matches = function(node, selector) {
		return node.matches(selector);
	 };
	} else
	if (typeof el.matchesSelector === 'function') {
	 mono.matches = function(node, selector) {
		return node.matchesSelector(selector);
	 };
	} else
	if (typeof el.webkitMatchesSelector === 'function') {
	 mono.matches = function(node, selector) {
		return node.webkitMatchesSelector(selector);
	 };
	} else
	if (typeof el.mozMatchesSelector === 'function') {
	 mono.matches = function(node, selector) {
		return node.mozMatchesSelector(selector);
	 };
	} else
	if (typeof el.oMatchesSelector === 'function') {
	 mono.matches = function(node, selector) {
		return node.oMatchesSelector(selector);
	 };
	} else
	if (typeof el.msMatchesSelector === 'function') {
	 mono.matches = function(node, selector) {
		return node.msMatchesSelector(selector);
	 };
	} else {
	 mono.matches = function(node, selector) {
		return false;
	 };
	}
	el = null;

	return mono.matches.call(this, node, selector);
		};

		/**
		 * @param {Element} node
		 * @param {string} selector
		 * @returns {Node|null}
		 */
		mono.getParent = function(node, selector) {
	if (!node || node.nodeType !== 1) {
	 return null;
	}

	if (mono.matches(node, selector)) {
	 return node;
	}

	if (!mono.matches(node, selector + ' ' + node.tagName)) {
	 return null;
	}

	node = node.parentNode;
	for (var parent = node; parent; parent = parent.parentNode) {
	 if (parent.nodeType !== 1) {
		return null;
	 }

	 if (mono.matches(parent, selector)) {
		return parent;
	 }
	}

	return null;
		};

		/**
		 * @param {string} value
		 * @returns {string}
		 */
		mono.escapeRegex = function(value) {
	"use strict";
	return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
		};

		/**
		 * @param {function} callback
		 * @constructor
		 */
		mono.AsyncList = function(callback) {
	var wait = 0;
	var ready = 0;
	var fired = false;
	this.ready = function(err) {
	 if (fired) {
		return;
	 }

	 if (err) {
		fired = true;
		return callback(err);
	 }

	 ready++;
	 if (wait === ready) {
		fired = true;
		return callback();
	 }
	};
	this.wait = function() {
	 wait++;
	};
		};

		/**
		 * @param {*} msg
		 * @param {string} [hook]
		 * @returns {Promise}
		 */
		mono.sendMessagePromise = function(msg) {
	"use strict";
	return new mono.Promise(function(resolve) {
	 mono.sendMessage(msg, resolve);
	});
		};

		/**
		 * @param {String|[String]|Object|null|undefined} keys
		 * @returns {Promise}
		 */
		mono.storage.getPromise = function(keys) {
	"use strict";
	return new mono.Promise(function(resolve) {
	 mono.storage.get(keys, resolve);
	});
		};

		/**
		 * @param {Object} items
		 * @returns {Promise}
		 */
		mono.storage.setPromise = function(items) {
	"use strict";
	return new mono.Promise(function(resolve) {
	 mono.storage.set(items, resolve);
	});
		};

		/**
		 * @param {String|[String]} keys
		 * @returns {Promise}
		 */
		mono.storage.removePromise = function(keys) {
	"use strict";
	return new mono.Promise(function(resolve) {
	 mono.storage.remove(keys, resolve);
	});
		};

		/**
		 * @returns {Promise}
		 */
		mono.storage.clearPromise = function() {
	"use strict";
	return new mono.Promise(function(resolve) {
	 mono.storage.clear(resolve);
	});
		};

		/**
		 * @param {requestDetails} obj
		 * @returns {Promise}
		 */
		mono.requestPromise = function(obj) {
	"use strict";
	return new mono.Promise(function(resolve, reject) {
	 mono.request(obj, function(err, resp) {
		if (err) {
			reject(err);
		} else {
			resolve(resp);
		}
	 });
	});
		};

		/**
		 * @returns {Number}
		 */
		mono.getTime = function() {
	return parseInt(Date.now() / 1000);
		};

		mono.debug = function() {
	if (mono.debugMode) {
	 var args = [].slice.call(arguments);
	 args.unshift('sf');
	 console.log.apply(console, args);
	}
		};

		mono.error = function() {
	var args = [].slice.call(arguments);
	args.unshift('sf');
	console.error.apply(console, arguments);
		};

		mono.isNewVersion = function(oldVersion, newVersion) {
	var validate = /^[\d.]+$/;
	if (!validate.test(oldVersion) || !validate.test(newVersion)) {
	 throw new Error('Incorrect version');
	}
	var normalize = function(value, len) {
	 while (value.length < len) {
		value = '0' + value;
	 }
	 return value;
	};
	var oldParts = oldVersion.split('.');
	var newParts = newVersion.split('.');
	for (var index = 0; index < newParts.length; index++) {
	 var oldValue = oldParts[index] || '';
	 var newValue = newParts[index] || '';
	 var len = Math.max(oldValue.length, newValue.length);
	 oldValue = parseInt(normalize(oldValue, len));
	 newValue = parseInt(normalize(newValue, len));
	 if (newValue === oldValue) {
		continue;
	 }
	 return newValue > oldValue;
	}
	return false;
		};

		mono.getDataUrl = function(content, type, isForceData) {
	var url = '';
	if (!isForceData && typeof URL !== 'undefined' && typeof Blob !== "undefined" && !mono.isSafari) {
	 var blob = new Blob([content], {
		encoding: "UTF-8",
		type: type
	 });
	 url = URL.createObjectURL(blob);
	} else {
	 var contentUtf8 = SaveFrom_Utils.utf8Encode(content);
	 url = 'data:' + type + ';charset=utf8;base64,' + encodeURIComponent(btoa(contentUtf8))
	}
	return url;
		};

		_mono && (function(tmpMono) {
	"use strict";
	_mono = null;
	var args, list = tmpMono.loadModuleStack;
	if (list) {
	 while (args = list.shift()) {
		// async
		mono.asyncFn(mono.loadModule).apply(mono, args);
	 }
	}
		})(_mono);
		//<utils

		if (typeof Promise === 'function' &&
	typeof Promise.resolve === 'function' &&
	typeof Promise.reject === 'function'
		) {
	mono.Promise = Promise;
		}

		!mono.Promise && (function() {
	// Use polyfill for setImmediate for performance gains
	var asap = (typeof setImmediate === 'function' && setImmediate) ||
	 function(fn) {
		setTimeout(fn, 0);
	 };

	/**
	 @license
	 Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
	 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
	 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
	 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
	 Code distributed by Google as part of the polymer project is also
	 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
	 */
	// @url https://github.com/PolymerLabs/promise-polyfill
	// @version 1.0.0
	function MakePromise(asap) {
	 function Promise(fn) {
		if (typeof this !== 'object' || typeof fn !== 'function') throw new TypeError();
		this._state = null;
		this._value = null;
		this._deferreds = []

		doResolve(fn, resolve.bind(this), reject.bind(this));
	 }

	 function handle(deferred) {
		var me = this;
		if (this._state === null) {
			this._deferreds.push(deferred);
			return
		}
		asap(function() {
			var cb = me._state ? deferred.onFulfilled : deferred.onRejected
			if (typeof cb !== 'function') {
		(me._state ? deferred.resolve : deferred.reject)(me._value);
		return;
			}
			var ret;
			try {
		ret = cb(me._value);
			} catch (e) {
		deferred.reject(e);
		return;
			}
			deferred.resolve(ret);
		})
	 }

	 function resolve(newValue) {
		try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
			if (newValue === this) throw new TypeError();
			if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
		var then = newValue.then;
		if (typeof then === 'function') {
		 doResolve(then.bind(newValue), resolve.bind(this), reject.bind(this));
		 return;
		}
			}
			this._state = true;
			this._value = newValue;
			finale.call(this);
		} catch (e) {
			reject.call(this, e);
		}
	 }

	 function reject(newValue) {
		this._state = false;
		this._value = newValue;
		finale.call(this);
	 }

	 function finale() {
		for (var i = 0, len = this._deferreds.length; i < len; i++) {
			handle.call(this, this._deferreds[i]);
		}
		this._deferreds = null;
	 }

	 /**
	 * Take a potentially misbehaving resolver function and make sure
	 * onFulfilled and onRejected are only called once.
	 *
	 * Makes no guarantees about asynchrony.
	 */
	 function doResolve(fn, onFulfilled, onRejected) {
		var done = false;
		try {
			fn(function(value) {
		if (done) return;
		done = true;
		onFulfilled(value);
			}, function(reason) {
		if (done) return;
		done = true;
		onRejected(reason);
			})
		} catch (ex) {
			if (done) return;
			done = true;
			onRejected(ex);
		}
	 }

	 Promise.prototype['catch'] = function(onRejected) {
		return this.then(null, onRejected);
	 };

	 Promise.prototype.then = function(onFulfilled, onRejected) {
		var me = this;
		return new Promise(function(resolve, reject) {
			handle.call(me, {
		onFulfilled: onFulfilled,
		onRejected: onRejected,
		resolve: resolve,
		reject: reject
			});
		})
	 };

	 Promise.resolve = function(value) {
		if (value && typeof value === 'object' && value.constructor === Promise) {
			return value;
		}

		return new Promise(function(resolve) {
			resolve(value);
		});
	 };

	 Promise.reject = function(value) {
		return new Promise(function(resolve, reject) {
			reject(value);
		});
	 };


	 return Promise;
	}

	mono.Promise = MakePromise(asap);
		})(mono);

		(function(Promise) {
	Promise.all = Promise.all || function() {
	 var args = Array.prototype.slice.call(arguments.length === 1 && Array.isArray(arguments[0]) ? arguments[0] : arguments);

	 return new Promise(function(resolve, reject) {
		if (args.length === 0) return resolve([]);
		var remaining = args.length;

		function res(i, val) {
			try {
		if (val && (typeof val === 'object' || typeof val === 'function')) {
		 var then = val.then;
		 if (typeof then === 'function') {
			then.call(val, function(val) {
				res(i, val)
			}, reject);
			return;
		 }
		}
		args[i] = val;
		if (--remaining === 0) {
		 resolve(args);
		}
			} catch (ex) {
		reject(ex);
			}
		}
		for (var i = 0; i < args.length; i++) {
			res(i, args[i]);
		}
	 });
	};

	Promise.race = Promise.race || function(values) {
	 return new Promise(function(resolve, reject) {
		for (var i = 0, len = values.length; i < len; i++) {
			values[i].then(resolve, reject);
		}
	 });
	};
		})(mono.Promise);
		//@insert

		return mono;
	}
 ));

 var engine = {};

 engine.varCache = {
	// helper name
	helperName: '',
	// extension version
	currentVersion: '',
	langList: ['en', 'de', 'ru', 'tr', 'uk', 'es', 'fr', 'id'],
	meta: {},
	isFirstrun: false,
	isUpgrade: false,
	uuid: '',
	aiid: ''
 };

 engine.extra = {};

 engine.defaultPreferences = {
	version: '0',
	button: 1,
	lmMediaHosting: 1,
	moduleYoutube: 1,
	moduleDailymotion: 1,
	moduleVimeo: 1,
	moduleFacebook: 1,
	moduleSoundcloud: 1,
	moduleVkontakte: 1,
	moduleOdnoklassniki: 1,
	moduleMailru: 1,
	moduleInstagram: 1,
	moduleRutube: 1,
	moduleShowDownloadInfo: 1,
	ytHideFLV: 0,
	ytHideMP4: 0,
	ytHideWebM: 1,
	ytHide3GP: 1,
	ytHide3D: 1,
	ytHideMP4NoAudio: 1,
	ytHideAudio_MP4: 1,
	vkShowBitrate: 0,
	sovetnikEnabled: 1,
	showUmmyInfo: 1,
	showUmmyBtn: 1,
	gmNativeDownload: 0,
	expIndex: 0,
	advPreShow: 0,
	aviaBarEnabled: 1,
	statEnabled: 1,

	showUmmyLanding: 0,
	onceShowYtTutorial: 0,
	onceShowYtTooltip: 0,
	saveAsDialog: 0
 };

 engine.preferences = {
	sfHelperName: '',
	country: '',
	hasSovetnik: undefined,
	hasAviaBar: undefined,
	downloads: undefined,
	ummyDetected: undefined,
	showUmmyItem: undefined
 };

 engine.preferenceMap = {
	youtube: 'moduleYoutube',
	dailymotion: 'moduleDailymotion',
	vimeo: 'moduleVimeo',
	facebook: 'moduleFacebook',
	soundcloud: 'moduleSoundcloud',
	vk: 'moduleVkontakte',
	odnoklassniki: 'moduleOdnoklassniki',
	mailru: 'moduleMailru',
	instagram: 'moduleInstagram',
	rutube: 'moduleRutube'
 };

 engine.modules = {};
 engine.utils = {};

 engine.loader = (function() {
	var DEBUG = false;
	var waitList = [];
	var readyList = [];
	var run = function() {
		waitList.slice(0).forEach(function(obj) {
	var isReady = obj.nameList.every(function(name) {
	 return readyList.indexOf(name) !== -1;
	});
	if (isReady) {
	 var pos = waitList.indexOf(obj);
	 if (pos !== -1) {
		waitList.splice(pos, 1);
		try {
			DEBUG && console.debug('Loader run', obj.nameList);
			obj.fn();
		} catch (e) {
			mono.error('Run error!', e);
		}
	 }
	}
		});
	};
	/**
	 * @param {String} name
	 */
	var ready = function(name) {
		DEBUG && console.trace('Loader ready', name);
		readyList.push(name);
		run();
	};
	/**
	 * @param {String|String[]} nameList
	 * @param {Function} fn
	 */
	var when = function(nameList, fn) {
		DEBUG && console.trace('Loader when', nameList);
		if (!Array.isArray(nameList)) {
	nameList = [nameList];
		}
		waitList.push({
	nameList: nameList,
	fn: fn
		});
		run();
	};

	return {
		waitList: waitList,
		readyList: readyList,
		ready: ready,
		when: when
	}
 })();

 engine.events = (function() {
	var DEBUG = false;
	var listeners = {};
	var slice = [].slice;
	/**
	 * @param {string} event
	 * @param {...*} params
	 */
	var emit = function(event, params) {
		var args = slice.call(arguments).slice(1);
		var arr = listeners[event] || [];
		arr.slice(0).forEach(function(callback) {
	try {
	 DEBUG && console.debug('Events emit', event);
	 callback.apply(null, args);
	} catch (e) {
	 mono.error('Emit error!', e);
	}
		});
	};
	/**
	 * @param {string} event
	 * @param {Function} callback
	 */
	var on = function(event, callback) {
		var arr = listeners[event];
		if (!listeners[event]) {
	arr = listeners[event] = [];
		}
		var pos = arr.indexOf(callback);
		if (pos === -1) {
	DEBUG && console.trace('Events on', event);
	arr.push(callback);
		}
	};
	/**
	 * @param {string} event
	 * @param {Function} callback
	 */
	var off = function(event, callback) {
		var arr = listeners[event] || [];
		var pos = arr.indexOf(callback);
		if (pos !== -1) {
	DEBUG && console.trace('Events off', event);
	arr.splice(pos, 1);
		}
	};
	/**
	 * @param {string} event
	 * @param {Function} callback
	 */
	var once = function(event, callback) {
		DEBUG && console.trace('Events once', event);
		var _callback = function() {
	off(event, callback);
	callback.apply(null, arguments);
		};
		on(event, _callback);
	};

	return {
		listeners: listeners,
		emit: emit,
		on: on,
		off: off,
		once: once
	}
 })();

 engine.getHelperName = function() {
	"use strict";
	var getBrowserName = function() {
		var browser = '';
		var userAgent = mono.getNavigator().userAgent;
		if (userAgent.indexOf('YaBrowser\/') !== -1) {
	browser = 'yabrowser';
		} else
		if (userAgent.indexOf('Maxthon\/') !== -1) {
	browser = 'maxthon';
		} else
		if (userAgent.indexOf('OPR\/') !== -1) {
	browser = 'opera-chromium';
		} else
		if (userAgent.indexOf('Opera\/') !== -1) {
	browser = 'opera';
		} else
		if (userAgent.indexOf('Firefox\/') !== -1) {
	browser = 'firefox';
		} else
		if (userAgent.indexOf('Chrome\/') !== -1) {
	browser = 'chrome';
		} else
		if (userAgent.indexOf('Safari\/') !== -1) {
	browser = 'safari';
		}
		return browser;
	};

	var browser = 'unknown';
	if (mono.isChrome) {
		browser = getBrowserName() || 'chrome';
		if (/sandbox.html#bg/.test(location.href)) {
	browser = 'chameleon';
		}
		if (engine.chromeNoStore) {
	browser += '-sf';
		}
	} else
	if (mono.isFirefox) {
		browser = 'firefox';
		if (mono.isFirefoxMobile) {
	browser += '-mobile';
		}
		if (engine.firefoxNoStore) {
	browser += '-sf';
		}
	} else
	if (mono.isSafari) {
		browser = 'safari';
	} else
	if (mono.isOpera) {
		browser = 'opera';
	} else
	if (mono.isGM) {
		browser = getBrowserName() || browser;
		browser = 'userjs-' + browser;
	} else
	if (mono.isEdge) {
		browser = 'edge';
	}

	return browser;
 };

 engine.getSfHelperName = function() {
	var name = engine.varCache.helperName;
	if (/^firefox/.test(name)) {
		name = name.replace('firefox', 'ff');
	}
	return name;
 };

 engine.dblTrackCheck = function(cb) {
	"use strict";
	var DEBUG = false;
	if (!mono.isGM) {
		return cb();
	}

	if (mono.isIframe()) {
		DEBUG && mono.debug('dblTrack inside frame disabled!');
		return;
	}

	mono.storage.get('dblTrack', function(storage) {
		var now = Date.now();
		if (typeof storage.dblTrack !== 'string') {
	storage.dblTrack = '';
		}

		var dataList = storage.dblTrack.split(',');
		if (dataList[1] > now) {
	DEBUG && mono.debug('dblTrack is not expired!');
	return;
		}

		var uuid = engine.generateUuid();
		var expire = now + 60000;
		mono.storage.set({
	dblTrack: uuid + ',' + expire
		});

		setTimeout(function() {
	mono.storage.get('dblTrack', function(storage) {
	 if (typeof storage.dblTrack === 'string') {
		var dataList = storage.dblTrack.split(',');
		if (dataList[0] === uuid) {
			DEBUG && mono.debug('dblTrack callback');
			cb();
		} else {
			DEBUG && mono.debug('dblTrack uuid is change');
		}
	 }
	});
		}, 5000);
	});
 };

 engine.getUuid = function() {
	"use strict";
	var varCache = engine.varCache;
	if (varCache.uuid) {
		return varCache.uuid;
	}

	var uuid = engine.generateUuid();
	varCache.uuid = uuid;
	mono.storage.set({
		uuid: uuid
	});
	return uuid;
 };

 engine.generateUuid = function() {
	"use strict";
	return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
		var r = Math.random() * 16 | 0,
	v = c == 'x' ? r : (r & 0x3 | 0x8);
		return v.toString(16);
	});
 };

 engine.sovetnikIsAvailable = function() {
	"use strict";
	if (!mono.isFirefox && !mono.isChrome && !mono.isGM && !mono.isOpera) {
		return false;
	}
	if (mono.isChrome && mono.isChromeMobile) {
		return false;
	}
	if (mono.isFirefox && mono.isFirefoxMobile) {
		return false;
	}
	return true;
 };

 /**
 * @returns {string}
 */
 engine.getNavLanguage = function() {
	var language = '';
	var navLanguage = mono.getNavigator().language;
	if (/^\w{2}-|^\w{2}$/.test(navLanguage)) {
		language = navLanguage;
	}
	return language;
 };

 engine.loadLanguage = function(cb) {
	"use strict";
	var language = engine.language;
	var varCache = engine.varCache;
	var langList = varCache.langList;
	var defaultLocale = langList[0];
	var locale = mono.getLoadedLocale();
	if (!locale) {
		var navLanguage = engine.getNavLanguage().substr(0, 2).toLowerCase();
		if (langList.indexOf(navLanguage) !== -1) {
	locale = navLanguage;
		} else {
	locale = defaultLocale;
		}
	}

	(function getLanguage(locale, cb) {
		mono.getLanguage(locale, function(err, _language) {
	if (err) {
	 if (locale !== defaultLocale) {
		getLanguage(defaultLocale, cb);
	 } else {
		cb();
	 }
	} else {
	 mono.extend(language, _language);
	 cb();
	}
		});
	})(locale, cb);
 };

 engine.language = {};
 engine.operaShowButton = (function() {
	var button = null;
	return function(enabled) {
		"use strict";
		if (button !== null) {
	opera.contexts.toolbar.removeItem(button);
	button = null;
		}
		if (enabled) {
	button = opera.contexts.toolbar.createItem({
	 title: 'SaveFrom.net helper',
	 icon: "img/icon_18.png",
	 popup: {
		href: "popup.html",
		width: 482,
		height: 404
	 }
	});
	opera.contexts.toolbar.addItem(button);
		}
	};
 })();

 engine.gmShowButton = function(enabled) {
	if (enabled) {
		_menu.setTitle(engine.language.extName, engine.varCache.currentVersion);
		mono.storage.get('gmIconTop', function(storage) {
	if (storage.gmIconTop === 0 || storage.gmIconTop) {
	 _menu.style.menu.initial.top = storage.gmIconTop + 'px';
	}
	_menu.create(1);
		});
	} else {
		_menu.hide();
	}
 };

 engine.tabListener = (function() {
	"use strict";
	var isListen = false;
	var preferences = engine.preferences;
	var extendJsList = {};
	var excludeList = [
		"*://*.google.*/*",
		"*://*.acidtests.org/*",

		"*://*.savefrom.net/*",
		"*://*.youtube.com/*",
		"*://*.vimeo.com/*",
		"*://*.dailymotion.*/*",
		"*://*.vk.com/*",
		"*://*.vkontakte.ru/*",
		"*://*.odnoklassniki.ru/*",
		"*://my.mail.ru/*",
		"*://*.ok.ru/*",
		"*://*.soundcloud.com/*",
		"*://*.facebook.com/*",
		"*://*.instagram.com/*",
		"*://*.rutube.ru/*"
	];
	var matchCache = null;
	var checkBlackList = function(url) {
		return matchCache.test(url);
	};
	var listener = function(tab) {
		var url = tab.url;
		var inBlackList = checkBlackList(url);
		var scripts = [];

		var addLm = preferences.lmMediaHosting;
		var addSovetnik = preferences.hasSovetnik && preferences.sovetnikEnabled;

		if (mono.isChrome || mono.isEdge || mono.isFirefox) {
	addSovetnik = addSovetnik && window.sovetnik;
		}

		if (!inBlackList) {
	if (addLm) {
	 scripts.push('includes/components.js');
	 scripts.push('includes/link_modifier.js');
	}
	if (addSovetnik) {
	 if (mono.isChrome || mono.isEdge || mono.isFirefox) {
		scripts.push('includes/sovetnik-sf.js');
	 }
	}
		}

		var empty = true;
		for (var key in extendJsList) {
	empty = false;
	var item = extendJsList[key];
	if (!inBlackList || item.noBlackList) {
	 var scriptList = item.getScriptList(url);
	 for (var i = 0, path; path = scriptList[i]; i++) {
		if (scripts.indexOf(path) === -1) {
			scripts.push(path);
		}
	 }
	}
		}

		if (!addLm && !addSovetnik && empty) {
	removeListener();
		} else
		if (scripts.length) {
	scripts.unshift('js/mono.js');

	mono.debug('Inject', url, scripts);

	mono.executeScripts(tab, {
	 files: scripts
	});
		}
	};
	var removeListener = function() {
		mono.tabsRemoveListener(listener);
		isListen = false;
		mono.debug('tabListener', isListen);
	};
	var addListener = function() {
		if (!isListen) {
	mono.tabsAddListener(listener);
	isListen = true;
	mono.debug('tabListener', isListen);
		}
	};
	var buildMatchCache = function() {
		if (!matchCache) {
	matchCache = excludeList.map(function(pattern) {
	 return mono.urlPatternToStrRe(pattern);
	}).join('|');
	matchCache = new RegExp(matchCache);
		}
	};
	var enable = function() {
		if (mono.isChrome || (mono.isFirefox && !mono.isFirefoxMobile) || mono.isEdge) {
	buildMatchCache();
	addListener();
		}
	};
	var disable = function() {
		if (mono.isChrome || (mono.isFirefox && !mono.isFirefoxMobile) || mono.isEdge) {
	removeListener();
		}
	};
	var injectLmInActiveTab = function() {
		var scripts = [
	'js/mono.js',
	'includes/components.js',
	'includes/link_modifier.js'
		];

		mono.getActiveTab(function(tab) {
	if (tab && /^http/.test(tab.url) && !checkBlackList(tab.url)) {
	 mono.executeScripts(tab, {
		files: scripts
	 });
	}
		});
	};
	return {
		extendJsList: extendJsList,
		excludeList: excludeList,
		enable: enable,
		disable: disable,
		injectLmInActiveTab: injectLmInActiveTab
	}
 })();

 engine.getCountry = function() {
	"use strict";
	var preferences = engine.preferences;
	var varCache = engine.varCache;

	var getCountryFromLanguage = function(callback) {
		var lang2country = {
	be: 'by',
	kk: 'kz',
	ru: 'ru',
	uk: 'ua',
	hy: 'am',
	ro: 'md',
	az: 'az',
	ka: 'ge',
	ky: 'kg',
	uz: 'uz',
	lv: 'lv',
	lt: 'lt',
	et: 'ee',
	tg: 'tj',
	fi: 'fi',
	tk: 'tm'
		};

		var navLanguage = engine.getNavLanguage().substr(0, 2).toLowerCase();
		var country = lang2country[navLanguage];

		mono.asyncFn(callback)(country);
	};

	var requestCountry = function(callback) {
		var xh = preferences.sfHelperName + ' ' + varCache.currentVersion;
		mono.request({
	type: 'POST',
	url: 'http://sf-helper.com/geoip/country.php',
	data: {
	 sig: xh.length
	},
	headers: {
	 'X-Helper': xh
	}
		}, function(err, resp, data) {
	var country = '';
	if (!err && data) {
	 country = data.toLowerCase().substr(0, 2);
	}
	callback(country);
		});
	};

	var onGetLanguage = function(country) {
		mono.debug('Set country', country);
		preferences.country = country;
		mono.storage.set({
	country: country
		});
		engine.liteStorage.setExpire('countryExpire', 259200);

		engine.loader.ready('getCountry');
	};

	var isExpire = engine.liteStorage.isExpire('countryExpire');
	var isTimeout = engine.liteStorage.isTimeout('countryTimeout');
	if (isExpire && !isTimeout) {
		engine.liteStorage.setTimeout('countryTimeout', 86400);

		getCountryFromLanguage(function(country) {
	if (country) {
	 mono.debug('Get country via language', country);
	 onGetLanguage(country);
	} else {
	 requestCountry(function(country) {
		mono.debug('Get country via api', country);
		if (country) {
			onGetLanguage(country);
		} else {
			mono.debug('Api country is empty!');
		}
	 });
	}
		});
	}
 };

 engine.forceMetaRequest = false;

 engine.getMeta = (function() {
	"use strict";
	var varCache = engine.varCache;
	var defaultMeta = {};

	var currentDate = function() {
		var _date = new Date();
		var date = _date.getDate();
		var month = _date.getMonth() + 1;
		var cDate = '';

		cDate += date < 10 ? '0' + date : date;
		cDate += month < 10 ? '0' + month : month;
		cDate += _date.getFullYear();

		return cDate;
	};

	var requestMeta = function(callback) {
		engine.liteStorage.setTimeout('metaTimeout', 21600);

		var always = mono.onceFn(function(err, meta) {
	if (err) {
	 mono.debug('Meta request error!', err);
	}

	if (varCache.isFirstrun && !meta) {
	 meta = defaultMeta;
	}

	if (meta) {
	 var _meta = varCache.meta;
	 for (var key in _meta) {
		delete _meta[key];
	 }

	 mono.extend(_meta, meta);

	 engine.loader.ready('getMeta');
	}

	callback();
		});

		setTimeout(function() {
	always('TIMEOUT');
		}, 6000);

		return mono.request({
	url: 'http://sf-helper.com/meta/meta.json' + '?_=' + currentDate(),
	json: true
		}, function(err, resp, json) {
	if (err) {
	 always(err);
	} else {
	 mono.storage.set({
		meta: json
	 });
	 engine.liteStorage.setExpire('metaExpire', 86400);
	 always(null, json);
	}
		});
	};

	var getMeta = function(callback) {
		var onceCb = mono.onceFn(callback);
		var isExpire = engine.liteStorage.isExpire('metaExpire');
		var isTimeout = engine.liteStorage.isTimeout('metaTimeout');
		if (mono.isEmptyObject(engine.expList) && !engine.forceMetaRequest) {
	mono.debug('Meta not require');
	onceCb();
		} else
		if (varCache.isFirstrun || varCache.isUpgrade) {
	mono.debug('Meta force request');
	requestMeta(function() {
	 onceCb();
	});
		} else
		if (isTimeout) {
	mono.debug('Meta timeout');
	onceCb();
		} else
		if (isExpire) {
	mono.debug('Meta is expire');
	requestMeta(function() {
	 onceCb();
	});
		} else {
	mono.debug('Meta is fresh');
	onceCb();
		}
	};

	return getMeta;
 })();

 engine.loader.when('prepare', function() {
	var promise = mono.Promise.resolve();

	if (engine.liteStorage.get('fromId', 0)) {
		return promise;
	}

	if (mono.isGM && mono.isIframe()) {
		return promise;
	}

	var requestFromId = function() {
		return mono.requestPromise({
	url: 'http://savefrom.net/tools/get_vid.php'
		}).then(function(resp) {
	var data = resp.body;
	var id = -1;
	if (/^\d+$/.test(data)) {
	 id = parseInt(data);
	}

	mono.debug('fromId', id);

	engine.liteStorage.set('fromId', id);
		}).catch(function(err) {
	return mono.error("Request fromId error!", err);
		})
	};

	promise = promise.then(function() {
		var isTimeout = engine.liteStorage.isTimeout('fromIdTimeout');
		if (isTimeout) {
	return mono.debug('Request fromId timeout');
		} else {
	engine.liteStorage.setTimeout('fromIdTimeout', 21600);
	return requestFromId();
		}
	}).catch(function(err) {
		mono.error('Request fromId error', err);
	});

	return promise;
 });

 engine.loader.when('init', function() {
	"use strict";
	if (!mono.isSafari) {
		return;
	}

	safari.extension.settings.addEventListener('change', function(event) {
		if (event.key !== 'show_options') {
	return;
		}
		mono.openTab(safari.extension.baseURI + 'options.html', true);
	});

	var checkInterval = null;

	var removePopover = function() {
		if (checkInterval) {
	clearInterval(checkInterval);
	checkInterval = null;
		}

		safari.extension.toolbarItems.forEach(function(btn) {
	if (btn.popover) {
	 if (btn.popover.visible) {
		btn.popover.hide();
	 }
	 btn.popover = null;
	}
		});

		safari.extension.removePopover('popup');
	};

	safari.application.addEventListener('command', function(e) {
		if (e.command === 'openPopup') {
	var currentBtn = null;
	var activeWindow = safari.application.activeBrowserWindow;
	safari.extension.toolbarItems.some(function(btn) {
	 if (btn.browserWindow === activeWindow) {
		currentBtn = btn;
		return true;
	 }
	});
	if (!currentBtn) {
	 return;
	}

	if (currentBtn.popover && currentBtn.popover.visible) {
	 currentBtn.popover.hide();
	 return;
	}

	removePopover();

	currentBtn.popover = safari.extension.createPopover('popup', safari.extension.baseURI + 'popup.html', 482, 404);
	currentBtn.showPopover();

	checkInterval = setInterval(function() {
	 var isShowMenu = safari.extension.toolbarItems.some(function(btn) {
		if (btn.popover && btn.popover.visible) {
			return true;
		}
	 });
	 if (!isShowMenu) {
		removePopover();
	 }
	}, 1000 * 30);
		}
	}, false);
 });

 engine.onOptionChange = {
	button: function(enabled) {
		if (mono.isOpera) {
	engine.operaShowButton(enabled);
		} else
		if (mono.isGM) {
	engine.gmShowButton(enabled);
		}
	},
	sovetnikEnabled: function(value, oldValue) {
		if (value === oldValue) {
	return;
		}

		if (engine.preferences.hasSovetnik) {
	if (value) {
	 engine.tabListener.enable();
	}

	if (mono.isOpera || mono.isChrome || mono.isFirefox) {
	 if (window.sovetnik && window.sovetnik.setRemovedState) {
		window.sovetnik.setRemovedState(!value)
	 }
	}
		}
	},
	statEnabled: function(state) {
		engine.events.emit('statEnabledChange', state);
	},
	lmMediaHosting: function(value) {
		if (value) {
	engine.tabListener.enable();
		}
	},
	gmNativeDownload: function(value) {
		if (!mono.isGM) {
	return;
		}
		engine.preferences.downloads = !!value;
		if (mono.global.preference) {
	// GM only!
	mono.global.preference.downloads = engine.preferences.downloads;
		}
	}
 };

 engine.sendInGa = (function() {
	"use strict";
	var stack = [];
	var lock = false;
	var send = function() {
		if (lock || !stack.length) {
	return;
		}

		lock = true;

		var now = Date.now();
		var items = stack.slice(0, 20);
		var data = items.map(function(item) {
	var time = item.time;
	var params = JSON.parse(JSON.stringify(item.params));
	var qt = now - time;
	if (qt >= 14400000) {
	 qt = 14400000 - ((stack.length + 1) * 1000);
	}
	params.qt = qt;
	return mono.param(params);
		});

		return mono.request({
	url: 'https://www.google-analytics.com/batch?z=' + now,
	type: 'POST',
	contentType: 'text/html',
	data: data.join('\n'),
	timeout: 60 * 1000
		}, function(err) {
	lock = false;
	if (!err) {
	 items.forEach(function(item) {
		var details = item.details;
		var pos = stack.indexOf(item);
		if (pos !== -1) {
			stack.splice(pos, 1);
		}
		try {
			details.onSuccess && details.onSuccess();
		} catch (e) {
			mono.error('sendInGa', 'onSuccess', e);
		}
	 });

	 send();
	}
		});
	};

	var pull = function() {
		if (!stack.length) {
	return;
		}

		var isTimeout = engine.liteStorage.isTimeout('sendInGaTimeout');
		if (!isTimeout) {
	engine.liteStorage.setTimeout('sendInGaTimeout', 1 * 60 * 60);
	send();
		}
	};
	/**
	 *@param {Object} params
	 *@param {Object} [details]
	 *@param {String} details.id
	 *@param {Function} details.onSuccess
	 **/
	var push = function(params, details) {
		var exists = false;
		details = details || {};

		if (details.id) {
	exists = stack.some(function(item) {
	 if (item.details.id === details.id) {
		return true;
	 }
	});
		}

		if (!exists) {
	stack.unshift({
	 time: Date.now(),
	 params: params,
	 details: details
	});

	stack.splice(100);

	setTimeout(function() {
	 send();
	}, 50);
		}
	};

	return {
		stack: stack,
		push: push,
		pull: pull
	};
 })();

 engine.actionList = {
	getMenuDetails: function(msg, response) {
		"use strict";
		var getLastVersion = function() {
	var version = '';

	var currentVersion = engine.varCache.currentVersion;
	var lastVersion = engine.liteStorage.get('lastVersion', '');
	if (!currentVersion || !lastVersion) {
	 return version;
	}

	try {
	 if (mono.isNewVersion(currentVersion, lastVersion)) {
		version = lastVersion;
	 }
	} catch (err) {
	 mono.debug('isNewVersion', err);
	}

	return version;
		};

		var details = {
	language: engine.language,
	preferences: engine.preferences,
	version: engine.varCache.currentVersion,
	lastVersion: getLastVersion(),
	helperName: engine.varCache.helperName
		};

		return response(details);
	},
	getLanguageAndPreference: function(message, response) {
		engine.actionList.getLanguage(message, function(language) {
	engine.actionList.getPreference(message, function(preference) {
	 response({
		getLanguage: language,
		getPreference: preference
	 });
	});
		});
	},
	getLanguage: function(message, cb) {
		cb(engine.language);
	},
	getPreference: function(message, cb) {
		var preferences = engine.preferences;
		if (mono.isSafari || mono.isGM) {
	preferences = mono.extend({}, engine.preferences);
		}

		cb(preferences);

		engine.userTrack();

		engine.sendInGa.pull();
	},
	updateOption: function(message) {
		var key = message.key;
		var value = message.value;
		var oldValue = engine.preferences[key];
		engine.preferences[key] = value;

		var obj = {};
		obj[key] = value;
		mono.storage.set(obj);

		if (engine.onOptionChange[key]) {
	engine.onOptionChange[key](value, oldValue);
		}
	},
	downloadFromCurrentPage: function() {
		var url = 'http://savefrom.net/';
		mono.getActiveTabUrl(function(cUrl) {
	var args = mono.param({
	 url: cUrl,
	 utm_source: engine.preferences.sfHelperName,
	 utm_medium: 'extensions',
	 utm_campaign: 'bookmarklet'
	});
	mono.openTab(url + '?' + args, true);
		});
	},
	openPoll: function() {
		if (['en', 'uk', 'ru'].indexOf(engine.language.lang) === -1) {
	return;
		}
		var url = 'http://' + engine.language.lang + '.savefrom.net/helper-form.php';
		mono.getActiveTabUrl(function(cUrl) {
	var domain = mono.getDomain(cUrl) || '';

	var args = '?' + mono.param({
	 version: engine.varCache.currentVersion,
	 helper: engine.preferences.sfHelperName,
	 url: domain
	});
	mono.openTab(url + args, true);
		});
	},
	reportBug: function() {
		var url = 'http://savefrom.userecho.com/forum/20869-/';
		if (engine.language.lang === 'ru') {
	url = 'http://savefrom.userecho.com/forum/19523-/';
		}
		mono.openTab(url);
	},
	viaMenu_updateLinks: function() {
		mono.sendMessageToActiveTab({
	action: 'updateLinks'
		});
	},
	viaMenu_downloadMP3Files: function() {
		mono.sendMessageToActiveTab({
	action: 'downloadMP3Files'
		});
	},
	viaMenu_downloadPlaylist: function() {
		mono.sendMessageToActiveTab({
	action: 'downloadPlaylist'
		});
	},
	viaMenu_downloadPhotos: function() {
		mono.sendMessageToActiveTab({
	action: 'downloadPhotos'
		});
	},
	viaMenu_changeState: function(msg) {
		engine.actionList.updateOption({
	key: msg.prefKey,
	value: msg.state
		});

		if (msg.state && msg.moduleName === 'lm' && msg.needInclude) {
	if (mono.isChrome || mono.isFirefox) {
	 return engine.tabListener.injectLmInActiveTab();
	}
		}

		mono.sendMessageToActiveTab({
	action: 'changeState',
	moduleName: msg.moduleName,
	state: msg.state
		});
	},
	showOptions: function() {
		if (mono.isGM) {
	return _options.show();
		}
		var url = 'options.html';
		if (mono.isSafari) {
	url = safari.extension.baseURI + url;
		}
		mono.openTab(url, true);
	},
	getActiveTabModuleInfo: function(msg, cb) {
		mono.sendMessageToActiveTab({
	action: 'getModuleInfo',
	url: msg.url
		}, function(moduleInfo) {
	cb(moduleInfo);
		});
		return true;
	},
	getActiveTabUrl: function(message, cb) {
		mono.getActiveTabUrl(function(url) {
	return cb(url);
		});
		return true;
	},
	getActiveTabInfo: function(msg, cb) {
		var preferences = engine.preferences;
		mono.getActiveTabUrl(function(url) {
	if (url.indexOf('http') !== 0) {
	 return cb();
	}

	var hostList = {
	 dailymotion: ['*://*.dailymotion.*/*'],
	 facebook: ['*://*.facebook.com/*'],
	 mailru: ['*://my.mail.ru/*'],
	 odnoklassniki: ['*://*.ok.ru/*', '*://*.odnoklassniki.ru/*'],
	 savefrom: ['*://*.savefrom.net/*'],
	 soundcloud: ['*://*.soundcloud.com/*'],
	 vimeo: ['*://*.vimeo.com/*'],
	 vk: ['*://*.vk.com/*', '*://*.vkontakte.ru/*'],
	 youtube: ['*://*.youtube.com/*'],
	 instagram: ['*://*.instagram.com/*'],
	 rutube: ['*://*.rutube.ru/*']
	};

	var moduleName = 'lm';
	var prefKey = 'lmMediaHosting';
	var state = preferences.lmMediaHosting;

	for (var key in hostList) {
	 var regList = hostList[key];
	 var re = regList.map(function(pattern) {
		return mono.urlPatternToStrRe(pattern);
	 }).join('|');
	 re = new RegExp(re);

	 if (re.test(url)) {
		moduleName = key;
		prefKey = engine.preferenceMap[moduleName];
		state = preferences[prefKey];
		break;
	 }
	}

	return cb({
	 moduleName: moduleName,
	 prefKey: prefKey,
	 url: url,
	 state: state
	});
		});
		return true;
	},
	userjsDetected: function() {
		if (!engine.liteStorage.get('userjsDetected', false)) {
	engine.liteStorage.set('userjsDetected', true);

	var uuid = engine.getUuid();
	return mono.request({
	 url: 'https://www.google-analytics.com/collect',
	 type: 'POST',
	 data: 'v=1&tid=UA-7055055-1&cid=' + uuid + '&t=pageview&dh=savefrom.net&dp=%2Fextension%2Fuserjs_installed.ext'
	});
		}
	},
	hideDownloadWarning: function(message, cb) {
		if (message.set !== undefined) {
	return mono.storage.set({
	 hideDownloadWarning: message.set
	});
		}
		mono.storage.get('hideDownloadWarning', function(storage) {
	cb(storage.hideDownloadWarning);
		});
		return true;
	},
	track: function(message) {
		delete message.action;
		engine.track(message);
	},
	addToClipboard: function(message) {
		if (mono.isChrome || mono.isFirefox) {
	var text = message.text;
	var textArea;
	document.body.appendChild(textArea = mono.create('textarea', {
	 text: text
	}));
	textArea.select();
	mono.asyncCall(function() {
	 document.execCommand("copy", false, null);
	 textArea.parentNode.removeChild(textArea);
	});
		}
	},
	setIconBadge: function(msg) {
		"use strict";
		var text = String(msg.text);

		if (mono.isChrome || mono.isFirefox) {
	chrome.browserAction && chrome.browserAction.setBadgeText({
	 text: text
	});
		}
	},
	trackError: function(msg) {
		"use strict";
		try {
	var trackError = engine.actionList.trackError;
	if (!trackError.dDbl) {
	 trackError.dDbl = {};
	}

	var desc = msg.desc;

	if (msg.error) {
	 var error = msg.error;
	 desc = desc ? desc + ' ' : '';
	 if (error instanceof Error) {
		desc += String(error.message || error) || 'ERROR';
		if (error.stack) {
			desc += ' ' + msg.error.stack;
		}
	 } else {
		desc += error;
	 }
	}

	var exd = desc.substr(0, 150);

	if (trackError.dDbl[exd]) {
	 return;
	}
	trackError.dDbl[exd] = true;

	var params = {
	 t: 'exception',
	 exd: exd,
	 tid: 'UA-7055055-9'
	};

	engine.sendStatsInfo(params);
		} catch (e) {}
	},
	openTab: function(msg) {
		mono.openTab(msg.url);
	}
 };

 engine.onMessage = function(message, cb) {
	if (!message || typeof message !== 'object') {
		mono.debug('Skip message', message);
		return;
	}

	if (!engine.onMessage.ready) {
		engine.onMessage.stack.push(arguments);
		return;
	}

	var action = message.action;
	var func = engine.actionList[action];
	if (func) {
		return func.call(engine.actionList, message, cb);
	}

	for (var moduleName in engine.modules) {
		var module = engine.modules[moduleName];
		func = module[action];
		if (func) {
	return func.call(module, message, cb);
		}
	}

	func = engine.utils[action];
	if (func) {
		return func.call(engine.utils, message, cb);
	}

	mono.debug('Action is not found', message);
 };
 engine.onMessage.stack = [];
 engine.onMessage.ready = false;

 engine.loadSettings = function(cb) {
	var varCache = engine.varCache;
	var preferences = engine.preferences;
	var defaultPreferences = engine.defaultPreferences;

	if (mono.isGM) {
		defaultPreferences.button = 0;
		defaultPreferences.showUmmyBtn = 0;
	}

	var preload = {
		ummyDetected: function(value) {
	if (!value && value !== 0) {
	 value = preferences.showUmmyInfo ? 0 : 1;
	 mono.storage.set({
		ummyDetected: value
	 });
	}
	preferences.ummyDetected = value;
		}
	};

	var defPreferencesKeys = Object.keys(defaultPreferences);
	var preloadKeys = Object.keys(preload);
	return mono.storage.get(defPreferencesKeys.concat(preloadKeys), function(storage) {
		defPreferencesKeys.forEach(function(key) {
	var defaultValue = defaultPreferences[key];
	var value = storage[key];
	if (value === undefined) {
	 value = defaultValue;
	}
	preferences[key] = value;
		});

		preloadKeys.forEach(function(key) {
	preload[key](storage[key]);
		});

		if (varCache.isFirstrun) {
	var tutorialStorage = {
	 showUmmyLanding: preferences.showUmmyLanding = 1,
	 onceShowYtTooltip: preferences.onceShowYtTooltip = 1,
	 onceShowYtTutorial: preferences.onceShowYtTutorial = 1
	};
	mono.storage.set(tutorialStorage);
		}

		if (preferences.onceShowYtTutorial && !engine.hasMenuTutorial) {
	preferences.onceShowYtTutorial = 0;
		}

		if (mono.isChrome) {
	if (mono.isChromeVersion < 31) {
	 preferences.downloads = false;
	} else
	if (
	 (chrome.downloads && chrome.downloads.download) ||
	 (chrome.permissions && chrome.permissions.request)
	) {
	 preferences.downloads = true;
	}
		}

		if (mono.isGM) {
	preferences.downloads = false;
	var hasDownloadApi = typeof GM_download !== 'undefined';
	var browserApiDownloadMode = false;
	if (hasDownloadApi) {
	 browserApiDownloadMode = typeof GM_info !== 'undefined' && GM_info.downloadMode === 'browser';
	}
	if (hasDownloadApi && (preferences.gmNativeDownload || browserApiDownloadMode)) {
	 preferences.gmNativeDownload = 1;
	 preferences.downloads = true;
	}
		}

		if (mono.isFirefox) {
	preferences.downloads = true;
		}

		if (preferences.downloads) {
	preferences.moduleShowDownloadInfo = 0;
		}

		return cb();
	});
 };

 engine.exp = (function() {
	var varCache = engine.varCache;
	var preferences = engine.preferences;
	var meta = varCache.meta;
	var list = engine.expList = {};
	/**
	 * @param {string} type
	 * @returns {number}
	 */
	var getExpIndex = function(type) {
		var result = 0;
		var value = 0;

		if (type === 'firstrun') {
	value = mono.getRandomInt(0, 100);

	for (var index in list) {
	 var percent = list[index].percent;
	 if (value < percent) {
		result = parseInt(index);
		break;
	 } else {
		value -= percent;
	 }
	}

		} else
		if (type === 'upgrade') {

		}

		return result;
	};
	var selectExp = function() {
		var metaExp = meta.exp || {};
		Object.keys(list).forEach(function(index) {
	var expItem = metaExp[index];
	if (!expItem || !expItem.enable) {
	 delete list[index];
	} else {
	 var item = list[index];
	 if (item.isAvailable && !item.isAvailable(preferences, varCache)) {
		delete list[index];
	 } else {
		list[index].percent = expItem.percent || 0;
	 }
	}
		});

		var expIndex = preferences.expIndex;
		if (expIndex > 0 && !list[expIndex]) {
	expIndex = 0;
		}

		if (varCache.isFirstrun) {
	expIndex = getExpIndex('firstrun');
		} else
		if (varCache.isUpgrade && expIndex === 0) {
	expIndex = getExpIndex('upgrade');
		}

		setExpIndex(expIndex);
	};
	/**
	 * @param {number} index
	 */
	var setExpIndex = function(index) {
		if (preferences.expIndex !== index) {
	mono.debug('Set exp index', index);
	engine.actionList.updateOption({
	 key: 'expIndex',
	 value: index
	});
		}
	};
	var disable = function() {
		setExpIndex(0);
	};
	var cancel = function() {
		mono.debug('Cancel exp', preferences.expIndex);
		preferences.expIndex = 0;
	};
	/**
	 * @param {number} expIndex
	 */
	var load = function(expIndex) {
		mono.debug('Load exp', expIndex);
		try {
	list[expIndex](preferences, varCache);
		} catch (e) {
	mono.error('Load exp error!', expIndex, e);
		}
	};
	var run = function() {
		var expIndex = preferences.expIndex;
		if (expIndex > 0) {
	if (!list[expIndex]) {
	 setExpIndex(0);
	} else
	if (meta.exp && meta.exp[expIndex] && meta.exp[expIndex].cancel) {
	 cancel();
	} else {
	 load(expIndex);
	}
		}
	};
	var init = function() {
		if (typeof EXP_INDEX === 'number') {
	mono.debug('Force exp index', EXP_INDEX);
	preferences.expIndex = EXP_INDEX;
	run();
		} else
		if (!varCache.isFirstrun && !varCache.isUpgrade) {
	run();
		} else
		if (!meta.exp || mono.isEmptyObject(list)) {
	setExpIndex(0);
		} else {
	selectExp();
	run();
		}
	};
	return {
		init: init,
		disable: disable
	}
 })();

 engine.checkSovetnik = function() {
	"use strict";
	var preferences = engine.preferences;

	var langList = [
		'be', 'kk', 'ru', 'uk',
		'hy', 'ro', 'az', 'ka',
		'ky', 'uz', 'lv', 'lt',
		'et', 'tg', 'fi', 'tk'
	];

	var countryList = [
		'by', 'kz', 'ru', 'ua',
		'am', 'md', 'az', 'ge',
		'kg', 'uz', 'lv', 'lt',
		'ee', 'tj', 'fi', 'tm'
	];

	preferences.hasSovetnik = countryList.indexOf(preferences.country) !== -1;
	if (!preferences.hasSovetnik) {
		var navLanguage = engine.getNavLanguage().substr(0, 2).toLowerCase();
		preferences.hasSovetnik = langList.indexOf(navLanguage) !== -1;
	}

	if (preferences.hasSovetnik) {
		if (mono.isOpera || mono.isChrome || mono.isFirefox) {
	if (engine.sovetnikInit) {
	 mono.asyncCall(function() {
		engine.sovetnikInit && engine.sovetnikInit();
	 });
	}
		}
	}

	engine.tabListener.enable();
 };

 engine.prepare = function(cb) {
	"use strict";
	var varCache = engine.varCache;

	engine.loader.when('getHelperVersion', function() {
		engine.getCountry();
	});

	engine.loader.when('loadSettings', function() {
		if (engine.sovetnikIsAvailable()) {
	engine.checkSovetnik();
	engine.loader.when('getCountry', function() {
	 engine.checkSovetnik();
	});
		}
	});

	engine.loader.when(['loadLanguage', 'loadSettings', 'getHelperVersion'], function() {
		varCache.isUpgrade = !varCache.isFirstrun && engine.preferences.version !== varCache.currentVersion;

		engine.getMeta(function() {
	engine.exp.init();

	cb();
		});
	});

	engine.loadLanguage(function() {
		engine.loader.ready('loadLanguage');
	});

	engine.loadSettings(function() {
		engine.loader.ready('loadSettings');
	});

	var onGetVersion = function(version) {
		varCache.currentVersion = version || 'unknown';
		engine.loader.ready('getHelperVersion');
	};

	mono.safeFn(function(cb) {
		cb(mono.getVersion());
	}, onGetVersion, engine)(onGetVersion);
 };

 engine.initMessageListener = function() {
	if (!engine.initMessageListener.fired) {
		engine.initMessageListener.fired = true;
		mono.onMessage.addListener(function(message, response) {
	return engine.onMessage(message, response);
		}, {
	isBg: true
		});
	}
 };

 engine.init = function() {
	engine.initMessageListener();

	var varCache = engine.varCache;
	var preferences = engine.preferences;

	var load = function() {
		var _navigator = mono.getNavigator();

		varCache.helperName = engine.getHelperName();

		preferences.showUmmyItem = /^Win|^Mac/.test(_navigator.platform) ? 1 : 0;

		preferences.sfHelperName = engine.getSfHelperName();

		engine.loader.ready('init');

		engine.loader.when('beforePrepare', function() {
	engine.checkVersion();
		});

		engine.prepare(function() {
	engine.loader.ready('beforePrepare');

	engine.onMessage.ready = true;
	while (engine.onMessage.stack.length > 0) {
	 mono.asyncFn(engine.onMessage).apply(null, engine.onMessage.stack.shift());
	}

	engine.loader.ready('prepare');
		});
	};

	var keys = ['uuid', 'version', 'meta', 'country', 'aiid'];
	keys.push(engine.liteStorage.getStorageKey());

	return mono.storage.get(keys, function(storage) {
		engine.liteStorage.setStorage(storage);

		if (typeof storage.uuid === 'string' && storage.uuid.length === 36) {
	varCache.uuid = storage.uuid;
		}
		if (typeof storage.aiid === 'string') {
	varCache.aiid = storage.aiid.replace(/aiid/, '').replace(/#/g, '');
		}
		if (!storage.version) {
	varCache.isFirstrun = true;
		}
		if (storage.meta) {
	mono.extend(varCache.meta, storage.meta);
		}
		if (storage.country) {
	preferences.country = storage.country;
		}

		return load();
	});
 };

 engine.userTrack = function() {
	if (engine.liteStorage.isTimeout('trackTimeout')) {
		return;
	}
	engine.liteStorage.setTimeout('trackTimeout', 5 * 60);

	var next = function() {
		engine.track({
	t: 'screenview',
	cd: 'init',
	tid: 'UA-7055055-5'
		}, {
	id: 'init',
	onSuccess: function() {
	 "use strict";
	 engine.liteStorage.setTimeout('trackTimeout', 12 * 60 * 60);

	 if (engine.preferences.expIndex) {
		engine.track({
			t: 'screenview',
			cd: 'init',
			tid: 'UA-7055055-11'
		});
	 }

	 engine.events.emit('sendScreenView');
	}
		});
	};

	return engine.dblTrackCheck(next);
 };

 engine.trackValidate = function(params) {
	var isValidValue = function(value) {
		if (!value && value !== 0 && value !== false) {
	return false;
		}
		if (['object', 'function'].indexOf(typeof value) !== -1) {
	return false;
		}
		return true;
	};

	if (!params.tid) {
		return false;
	}

	if (!params.cid) {
		return false;
	}

	if (parseInt(params.v) !== 1) {
		return false;
	}

	if (!isValidValue(params.an)) {
		return false;
	}

	if (!params.t) {
		return false;
	} else
	if (params.t === 'event') {
		if (!isValidValue(params.ec) || !isValidValue(params.ea) || !isValidValue(params.el)) {
	return false;
		}
	} else
	if (params.t === 'screenview') {
		if (!isValidValue(params.cd)) {
	return false;
		}
	} else
	if (params.t === 'social') {
		if (!isValidValue(params.st) || !isValidValue(params.sa) || !isValidValue(params.sn)) {
	return false;
		}
	}

	return true;
 };

 engine.track = function(params, details) {
	return engine.sendStatsInfo(params, details);
 };

 /**
 * @param {Object} params
 * @param {Object} [details]
 * @param {String} [details.id]
 * @param {Function} [details.onSuccess]
 */
 engine.quickTrack = function(params, details) {
	if (!engine.trackValidate(params)) {
		mono.error('Invalid track params!', params);
	} else {
		mono.debug('Track', params);
		engine.sendInGa.push(params, details);
	}
 };

 engine.wrapBaseStatInfo = function(params) {
	var varCache = engine.varCache;

	var defaultParams = {
		v: 1,
		ul: mono.getNavigator().language,
		tid: 'UA-67738130-2',
		cid: engine.getUuid(),

		an: 'helper',
		aid: varCache.helperName,
		av: varCache.currentVersion
	};

	for (var key in defaultParams) {
		if (!params.hasOwnProperty(key)) {
	params[key] = defaultParams[key];
		}
	}

	return params;
 };

 engine.sendStatsInfo = function(params, details) {
	var preferences = engine.preferences;

	engine.wrapBaseStatInfo(params);

	if (engine.varCache.aiid) {
		params.aiid = engine.varCache.aiid;
	}

	if (!params.hasOwnProperty('cd3')) {
		params.cd3 = engine.language.lang;
	}

	if (preferences.expIndex) {
		params.cd1 = 'test_' + preferences.expIndex;
	}

	var fromId = engine.liteStorage.get('fromId', 0);
	if (fromId > 0) {
		params.cd2 = fromId;
	}

	if (preferences.hasSovetnik) {
		params.cd4 = preferences.sovetnikEnabled ? 'true' : 'false';
	}

	params.cd6 = preferences.ummyDetected ? 'true' : preferences.showUmmyItem ? 'false' : 'none';

	if (preferences.hasAviaBar) {
		params.cd7 = preferences.aviaBarEnabled ? 'true' : 'false';
	}

	engine.quickTrack(params, details);
 };

 engine.checkVersion = function() {
	var varCache = engine.varCache;
	var needSaveVersion = false;
	if (varCache.isFirstrun) {
		engine.loader.ready('firstrun');

		needSaveVersion = true;
	} else
	if (varCache.isUpgrade) {
		engine.loader.ready('upgrade');

		needSaveVersion = true;
	}

	if (needSaveVersion) {
		engine.actionList.updateOption({
	key: 'version',
	value: varCache.currentVersion
		});
	}
 };

 engine.loader.when('firstrun', function() {
	"use strict";
	if (mono.isGM) {
		return;
	}

	var url = 'http://savefrom.net/user.php?helper=' + engine.preferences.sfHelperName + ';firstrun';

	engine.utils.checkUrlsOfOpenTabs([
		/https?:\/\/([\w\-]+\.)?savefrom\.net\/(update-helper|userjs-setup)\.php/i
	], function(foundUrls) {
		if (foundUrls.length > 0) {
	return;
		}

		engine.utils.checkUrlsOfOpenTabs([
	/https?:\/\/legal\.yandex\.(ru|com\.tr)\//i
		], function(foundUrls) {
	var active = foundUrls.length === 0;
	return mono.openTab(url, active);
		});
	});
 });

 engine.loader.when('prepare', function() {
	"use strict";
	var preferences = engine.preferences;

	if (preferences.onceShowYtTutorial) {
		engine.actionList.setIconBadge({
	text: '?'
		});
	}

	if (preferences.showUmmyLanding) {
		mono.storage.get(['onceUmmyLandingHide'], function(storage) {
	if (storage.onceUmmyLandingHide > 2) {
	 mono.storage.set({
		showUmmyLanding: preferences.showUmmyLanding = 0
	 });
	}
		});
	}
 });

 engine.loader.when('prepare', function() {
	"use strict";
	if (!mono.isChrome && !mono.isFirefox) {
		return;
	}

	engine.tabListener.enable();
 });

 engine.loader.when('init', function() {
	"use strict";
	if ((!mono.isChrome && !mono.isFirefox) || !chrome.runtime.setUninstallURL) {
		return;
	}

	var varCache = engine.varCache;
	var preferences = engine.preferences;

	var updateUrl = function() {
		var uninstallUrl = 'http://savefrom.net/goodbye.php';

		var params = {
	version: varCache.currentVersion,
	language: engine.language.lang,
	appid: varCache.helperName,
	country: preferences.country
		};

		var url = (uninstallUrl + '?' + mono.param(params)).substr(0, 255);
		chrome.runtime.setUninstallURL(url);
	};

	updateUrl();

	engine.loader.when('prepare', function() {
		updateUrl();
	});

	engine.loader.when('getCountry', function() {
		updateUrl();
	});
 });

 engine.loader.when('prepare', function() {
	"use strict";
	if (!mono.isOpera) {
		return;
	}

	engine.operaShowButton(engine.preferences.button);
 });

 engine.liteStorage = (function() {
	var storageKey = 'liteStorage';
	var store = {};
	/**
	 * @param {*} value
	 */
	var cloneObj = function(value) {
		return JSON.parse(JSON.stringify({
	w: value
		})).w;
	};
	/**
	 * @param {Function} cb
	 */
	var save = function(cb) {
		var obj = {};
		obj[storageKey] = store;
		return mono.storage.set(obj, cb);
	};
	var debounceSave = function() {
		mono.error('liteStorage is not set!');
	};
	/**
	 * @param {String} key
	 * @param {*} value
	 */
	var setValue = function(key, value) {
		if (store[key] !== value) {
	store[key] = value;
	debounceSave();
		}
	};
	/**
	 * @param {string} key
	 * @param {*} defaultValue
	 * @returns {*}
	 */
	var getValue = function(key, defaultValue) {
		var value = store[key];
		if (value === undefined) {
	value = defaultValue;
		}
		return cloneObj(value);
	};
	/**
	 * @returns {string}
	 */
	var getStorageKey = function() {
		return storageKey;
	};
	/**
	 * @param {Object} storage
	 */
	var setStorage = function(storage) {
		store = storage[storageKey] || {};
		debounceSave = mono.debounce(save, 100);
	};
	/**
	 * @param {string} key
	 * @param {number} time
	 */
	var setExpire = function(key, time) {
		return setValue(key, mono.getTime() + time);
	};
	/**
	 * @param {string} key
	 * @returns {boolean}
	 */
	var isTimeout = function(key) {
		return getValue(key, 0) > mono.getTime();
	};
	/**
	 * @param {string} key
	 * @returns {boolean}
	 */
	var isExpire = function(key) {
		return getValue(key, 0) < mono.getTime();
	};
	return {
		getStorageKey: getStorageKey,
		setStorage: setStorage,
		set: setValue,
		get: getValue,
		isTimeout: isTimeout,
		setTimeout: setExpire,
		isExpire: isExpire,
		setExpire: setExpire
	};
 })();

 engine.menuCommands = {
	commands: [{
		id: 'downloadFromCurrentPage',
		command: function() {
	engine.actionList.downloadFromCurrentPage();
		}
	}, {
		id: 'updateLinks',
		notify: 'updateLinksNotification',
		modules: ['vk', 'odnoklassniki', 'facebook', 'lm',
	'youtube',
	'dailymotion', 'instagram', 'rutube'
		]
	}, {
		id: 'downloadMP3Files',
		modules: ['vk', 'odnoklassniki', 'mailru']
	}, {
		id: 'downloadPlaylist',
		modules: ['vk', 'odnoklassniki', 'mailru']
	}, {
		id: 'downloadPhotos',
		modules: ['vk']
	}, {
		id: 'showOptions',
		command: function() {
	_options.show();
		}
	}, {
		id: 'reportBug',
		command: function() {
	engine.actionList.reportBug();
		}
	}, {
		id: 'enableDisableModule',
		command: function() {
	engine.actionList.getActiveTabInfo(null, function(tabInfo) {
	 tabInfo = tabInfo || {};
	 var state = tabInfo.state ? 0 : 1;
	 engine.actionList.viaMenu_changeState({
		state: state,
		prefKey: tabInfo.prefKey,
		moduleName: tabInfo.moduleName
	 });
	 if (state) {
		if (engine.preferences.button === 1) {
			engine.gmShowButton(1);
		} else {
			engine.gmShowButton(0);
		}
	 } else {
		engine.gmShowButton(0);
	 }
	});
		}
	}, {
		id: 'showHideButton',
		command: function() {
	var hiddenBtn = _menu.menu === null;
	engine.actionList.updateOption({
	 action: 'updateOption',
	 key: 'button',
	 value: hiddenBtn ? 1 : 0
	});
		}
	}],
	registerModule: function(params) {
		if (typeof GM_registerMenuCommand === 'undefined') {
	return;
		}

		var strId = params.id;

		var name = engine.language[strId];

		if (params.command) {
	return GM_registerMenuCommand(name, params.command);
		}

		var fn = function() {
	engine.onMessage({
	 action: (params.modules !== undefined ? 'viaMenu_' : '') + params.id
	});

	if (params.notify && typeof GM_notification !== 'undefined') {
	 GM_notification(engine.language[params.notify], null, null, null, 3000);
	}
		};

		return GM_registerMenuCommand(name, fn);
	},
	register: function(moduleName) {
		var hasRmFunc = typeof GM_unregisterMenuCommand !== "undefined";
		for (var i = 0, item; item = this.commands[i]; i++) {
	if (hasRmFunc) {
	 if (item.gmId) {
		GM_unregisterMenuCommand(item.gmId);
	 }
	} else
	if (item.hasOwnProperty("gmId")) {
	 continue;
	}

	if (!item.modules || item.modules.indexOf(moduleName) > -1) {
	 item.gmId = this.registerModule(item);
	}
		}
	}
 };

 engine.loader.when('prepare', function() {
	"use strict";
	if (!mono.isIframe()) {
		engine.menuCommands.register(_moduleName);

		if (engine.preferences.button && engine.preferences[engine.preferenceMap[_moduleName]] || _moduleName === 'savefrom') {
	engine.gmShowButton(1);
		}
	}
 });
 engine.checkUpdate = function() {
	"use strict";
	var varCache = engine.varCache;
	var typeList = engine.checkUpdate.typeList;
	var type = (mono.isChrome && !engine.isOperaNext) ? 'chrome' : (mono.isFirefox && engine.firefoxNoStore) ? 'firefox' : mono.isGM ? 'gm' : mono.isSafari ? 'safari' : undefined;
	if (!typeList[type]) {
		return;
	}

	var requestVersion = function() {
		mono.request({
	type: 'GET',
	url: typeList[type].url,
	cache: false
		}, function(err, resp) {
	if (err) {
	 return;
	}

	var version = typeList[type].getVersion(resp.body);
	if (version) {
	 engine.liteStorage.set('lastVersion', version);
	 engine.liteStorage.setExpire('lastVersionExpire', 604800);
	}
		});
	};

	var isExpire = engine.liteStorage.isExpire('lastVersionExpire');
	if (varCache.isUpgrade) {
		isExpire = true;
	}

	if (isExpire) {
		engine.liteStorage.set('lastVersion', '');
		engine.liteStorage.setExpire('lastVersionExpire', 86400);

		requestVersion();
	}
 };

 engine.checkUpdate.typeList = {
	chrome: {
		url: 'http://download.sf-helper.com/chrome/updates-3.xml',
		getVersion: function(data) {
	var version = /updatecheck.+version=['"](.+)['"]/.exec(data);
	version = version && version[1];
	return version;
		}
	},
	firefox: {
		url: 'https://download.sf-helper.com/mozilla/updates.json',
		getVersion: function(data) {
	var updates = null;
	try {
	 var obj = JSON.parse(data);
	 updates = obj.addons[Object.keys(obj.addons)[0]].updates;
	} catch (e) {}
	if (!updates) return;

	var version = null;
	updates.some(function(item) {
	 return version = item.version;
	});

	return version;
		}
	},
	safari: {
		url: 'https://download.sf-helper.com/safari/update.plist',
		getVersion: function(data) {
	if (typeof data !== 'string') {
	 return;
	}

	var pos = data.indexOf('<key>CFBundleVersion</key>');
	if (pos === -1) {
	 return;
	}

	data = data.substr(pos);
	var version = /<string>(.+)<\/string>/.exec(data);
	version = version && version[1];
	return version;
		}
	},
	gm: {
		url: 'https://download.sf-helper.com/chrome/helper.meta.js',
		getVersion: function(data) {
	var version = /@version\s+(.+)\s*\r?\n/.exec(data);
	version = version && version[1];
	return version;
		}
	}
 };

 engine.loader.when('prepare', function() {
	"use strict";
	engine.checkUpdate();
 });
 engine.loader.when('init', function() {
	"use strict";
	if (mono.isFirefox && mono.isFirefoxMobile) {
		return;
	}
	if (mono.isChrome && mono.isChromeMobile) {
		return;
	}

	var preferences = engine.preferences;

	var isDeny = function() {
		if (!preferences.hasAviaBar || !preferences.aviaBarEnabled) {
	return true;
		}

		return false;
	};

	var updateState = function() {
		delete engine.tabListener.extendJsList.aviaBar;

		if (isDeny()) {
	return;
		}

		engine.tabListener.extendJsList.aviaBar = aviaBar;

		engine.tabListener.enable();

		travelBarInit && travelBarInit();
	};

	var aviaBar = {
		matchCache: /null/,
		includeList: [
	'*://*.ozon.travel/*',
	'*://*.onetwotrip.com/*',

	'*://*.skyscanner.*/*',

	'*://*.aeroflot.ru/*',
	'*://*.momondo.*/*',
	'*://*.anywayanyday.com/*',
	'*://*.svyaznoy.travel/*',
	'*://avia.tickets.ru/*',
	'*://*.s7.ru/*',
	'*://*.kupibilet.ru/*',
	'*://*.trip.ru/*',
	'*://*.sindbad.ru/*',
	'*://*.aviakassa.ru/*',
	'*://*.aviakassa.com/*',
	'*://*.biletix.ru/*',
	'*://*.utair.ru/*',

	'*://*.kayak.*/*',
	'*://*.orbitz.com/*',
	'*://*.travelocity.com/*',
	'*://*.travelocity.ca/*',
	'*://*.expedia.*/*',
	'*://*.expedia-cn.com/*',
	'*://*.priceline.com/*',

	'*://booking.airasia.com/*',
	'*://*.ryanair.com/*',

	'*://*.booking.*/*',
	'*://*.agoda.*/*',
	'*://*.hotels.com/*',
	'*://*.ostrovok.ru/*',
	'*://*.travel.ru/*',
	'*://*.oktogo.ru/*',
	'*://*.roomguru.ru/*',
	'*://*.tripadvisor.ru/*',
	'*://*.hilton.ru/*',
	'*://*.hilton.com/*',
	'*://*.marriott.com/*',
	'*://*.hostelworld.com/*',
	'*://*.tiket.com/*',
	'*://*.hotelsclick.com/*',
	'*://*.hotelscombined.com/*',

	'*://*.avis.*/*',
	'*://*.budget.com/*',
	'*://*.wizzair.com/*',
	'*://*.emirates.com/*',
	'*://*.delta.com/*',
	"*://*.hertz.com/*",
	"*://*.europcar.com/*"
		],
		getScriptList: function(url) {
	var list = [];

	if (isDeny()) {
	 updateState();
	 return list;
	}

	if (this.matchCache.test(url)) {
	 list.push('includes/components.js');
	 list.push('includes/aviaBar.js');
	}
	return list;
		}
	};

	aviaBar.matchCache = aviaBar.includeList.map(function(pattern) {
		return mono.urlPatternToStrRe(pattern);
	}).join('|');
	aviaBar.matchCache = new RegExp(aviaBar.matchCache);

	engine.onOptionChange.aviaBarEnabled = function() {
		updateState();
	};

	var onLoadSettings = function() {
		var onceTrackAviaBar;

		preferences.hasAviaBar = 1;

		mono.storage.get('onceTrackAviaBar', function(storage) {
	var value = storage.onceTrackAviaBar;
	if (!value && value !== 0) {
	 if (mono.getRandomInt(0, 100) < 10) {
		value = 1;
	 } else {
		value = 0;
	 }
	 mono.storage.set({
		onceTrackAviaBar: value
	 });
	}
	onceTrackAviaBar = value;
		});

		updateState();

		engine.events.on('sendScreenView', function() {
	if (onceTrackAviaBar) {
	 var params = {
		t: 'screenview',
		cd: 'init',
		tid: 'UA-70432435-1'
	 };

	 engine.wrapBaseStatInfo(params);
	 engine.quickTrack(params);
	}
		});
	};

	engine.loader.when('loadSettings', onLoadSettings);

	var travelBarInit = function() {
		travelBarInit = null;

		var browser = {
	onMessage: function(cb) {
	 return mono.onMessage.addListener(cb, {
		isBg: true
	 });
	},
	storage: {
	 get: function(data, cb) {
		return mono.storage.get(data, cb);
	 },
	 set: function(data, cb) {
		return mono.storage.set(data, cb);
	 }
	}
		};

		var travelBar = {};

		travelBar.appInfo = {
	id: 'sf.sfHelper',
	track: true,
	directTrack: true,
	useTemplates: true
		};

		travelBar.API = {
	setRemovedState: function(state) {
	 browser.storage.get('aviaBar', function(storage) {
		storage.aviaBar = storage.aviaBar || {};
		storage.aviaBar.removed = !!state;
		return browser.storage.set(storage);
	 });
	}
		};

		var track = function(details) {
	var tid = 'UA-70432435-1';
	var isError = ['cheapflightError', 'hotelError'].indexOf(details.ec) !== -1;
	if (isError) {
	 tid = 'UA-7055055-10';
	}

	details.tid = tid;
	engine.wrapBaseStatInfo(details);

	if (!isError) {
	 var detailsMirror = JSON.parse(JSON.stringify(details));
	 detailsMirror.tid = 'UA-70432435-6';

	 engine.quickTrack(details);
	 engine.quickTrack(detailsMirror);
	} else
	if (mono.isGM) {
	 engine.quickTrack(details);
	}
		};

		var directTrack = function(details) {
	if (details.tid) {
	 engine.wrapBaseStatInfo(details);
	 engine.quickTrack(details);
	}
		};

		var prepareStorage = function(storage) {
	var travelBar = storage.aviaBar;
	if (!travelBar || typeof travelBar !== 'object') {
	 travelBar = storage.aviaBar = {};
	}
	if (!Array.isArray(travelBar.blackList)) {
	 travelBar.blackList = [];
	}
	if (!travelBar.templates || typeof travelBar.templates !== 'object') {
	 travelBar.templates = {};
	}
		};

		browser.onMessage(function(msg, response) {
	if (msg && msg.action) {
	 switch (msg.action) {
		case "tbrGetInfo":
			response(travelBar.appInfo);
			break;
		case "tbrIsAllow":
			browser.storage.get('aviaBar', function(storage) {
		prepareStorage(storage);

		var removed = storage.aviaBar.removed;
		if (removed) {
		 return response(false);
		}

		var allow = true;

		var blackList = storage.aviaBar.blackList;

		var item = null;
		blackList.some(function(_item) {
		 if (_item.hostname === msg.hostname) {
			item = _item;
			return true;
		 }
		});

		if (item) {
		 var now = parseInt(Date.now() / 1000);
		 if (item.expire > now) {
			allow = false;
		 } else {
			var pos = blackList.indexOf(item);
			blackList.splice(pos, 1);

			browser.storage.set(storage);
		 }
		}

		return response(allow);
			});
			break;
		case "tbrCloseBar":
			browser.storage.get('aviaBar', function(storage) {
		prepareStorage(storage);

		var blackList = storage.aviaBar.blackList;

		var item = null;
		blackList.some(function(_item) {
		 if (_item.hostname === msg.hostname) {
			item = _item;
			return true;
		 }
		});

		if (!item) {
		 var now = parseInt(Date.now() / 1000);
		 blackList.push({
			hostname: msg.hostname,
			expire: now + 5 * 60 * 60
		 });

		 browser.storage.set(storage);
		}
			});
			break;
		case "tbrGetTemplate":
			browser.storage.get('aviaBar', function(storage) {
		prepareStorage(storage);

		var templates = storage.aviaBar.templates;
		var template = templates[msg.id];
		if (!template) {
		 templates[msg.id] = template = {};
		}

		var now = parseInt(Date.now() / 1000);
		if (template.expire > now) {
		 response(template.data);
		} else {
		 template.expire = now + 21600;

		 mono.request({
			url: 'https://travelbar.tools/static/scheme/' + msg.id + '.json',
			timeout: 60 * 1000,
			json: true
		 }, function(err, _response) {
			if (!err) {
				var data = _response.body;
				template.expire = now + 43200;
				template.data = data;

				Object.keys(templates).sort(function(a, b) {
			return templates[a].expire > templates[b].expire ? -1 : 1;
				}).slice(5).forEach(function(key) {
			delete templates[key];
				});
			}

			response(data);
			browser.storage.set(storage);
		 });
		}
			});
			break;
		case "tbrEvent":
			if (msg.type === 'track') {
		track(msg.data[0]);
			} else
			if (msg.type === 'directTrack') {
		directTrack(msg.data[0]);
			}
			break;
	 }
	}
		});
	};
 });
 engine.loader.when('init', function() {
	"use strict";
	if (mono.isFirefox && mono.isFirefoxMobile) {
		return;
	}

	if (mono.isChrome && mono.isChromeMobile) {
		return;
	}

	var preferences = engine.preferences;
	var meta = engine.varCache.meta;

	var isDeny = function() {
		if (!preferences.hasFsStat || !preferences.statEnabled) {
	return true;
		} else {
	return false;
		}
	};

	var updateState = function() {
		if (isDeny()) {
	delete engine.tabListener.extendJsList.stat;
		} else {
	engine.tabListener.extendJsList.stat = stat;
	engine.tabListener.enable();
		}
	};

	var stat = {
		noBlackList: true,
		getScriptList: function() {
	var list = [];

	if (isDeny()) {
	 updateState();
	} else {
	 list.push('includes/fsStat.js');
	}

	return list;
		}
	};

	var isAvailable = function() {
		if (meta.fsStat && (!meta.fsStat.enable || meta.fsStat.cancel)) {
	preferences.hasFsStat = 0;
		} else {
	preferences.hasFsStat = 1;
		}
	};

	engine.events.on('statEnabledChange', function() {
		updateState();
	});

	engine.loader.when('getMeta', function() {
		isAvailable();

		updateState();
	});

	engine.loader.when('prepare', function() {
		isAvailable();

		updateState();
	});
 });
 engine.loader.when('init', function() {
	"use strict";
	if (mono.isFirefox && mono.isFirefoxMobile) {
		return;
	}

	if (mono.isChrome && mono.isChromeMobile || /yabrowser|chameleon/.test(engine.varCache.helperName)) {
		return;
	}

	var preferences = engine.preferences;
	var meta = engine.varCache.meta;

	var isDeny = function() {
		if (!preferences.hasSwStat || !preferences.statEnabled) {
	return true;
		} else {
	return false;
		}
	};

	var updateState = function() {
		if (engine.extra.sw) {
	if (isDeny()) {
	 engine.extra.sw.disable();
	} else {
	 engine.extra.sw.enable();
	}
		}
	};

	var isAvailable = function() {
		if (meta.swStat && (!meta.swStat.enable || meta.swStat.cancel)) {
	preferences.hasSwStat = 0;
		} else {
	preferences.hasSwStat = 1;
		}
	};

	engine.events.on('statEnabledChange', function() {
		updateState();
	});

	engine.loader.when('getMeta', function() {
		isAvailable();

		updateState();
	});

	engine.loader.when('beforePrepare', function() {
		isAvailable();

		updateState();
	});
 });
 engine.loader.when('init', function() {
	var onceTrackStat = null;

	engine.events.on('sendScreenView', function() {
		if (!onceTrackStat) return;

		var preferences = engine.preferences;
		var varCache = engine.varCache;

		if (preferences.hasFsStat || preferences.hasSwStat) {
	var params = {
	 v: 1,
	 ul: mono.getNavigator().language,
	 tid: 'UA-67738130-7',
	 cid: engine.getUuid(),
	 an: 'helper',
	 aid: varCache.helperName,
	 av: varCache.currentVersion,
	 t: 'screenview',
	 cd: 'init',
	 cd1: preferences.hasFsStat ? preferences.statEnabled ? 'true' : 'false' : 'none',
	 cd2: preferences.hasSwStat ? preferences.statEnabled ? 'true' : 'false' : 'none'
	};

	engine.sendInGa.push(params);
		}
	});

	mono.storage.get({
		onceTrackStat: null
	}, function(storage) {
		onceTrackStat = storage.onceTrackStat;
		if (onceTrackStat === null) {
	if (mono.getRandomInt(0, 100) < 10) {
	 onceTrackStat = 1;
	} else {
	 onceTrackStat = 0;
	}
	mono.storage.set({
	 onceTrackStat: onceTrackStat
	});
		}
	});
 });
 engine.loader.when(['init', 'getHelperVersion'], function() {
	"use strict";

	var dblTrackCheck = function(cb) {
		"use strict";
		if (!mono.isGM) {
	return cb();
		}

		if (mono.isIframe()) {
	return;
		}

		var key = 'userjsDblTrack';
		var getObj = {};
		getObj[key] = '';
		return mono.storage.get(getObj, function(storage) {
	var now = Date.now();

	var dataList = storage[key].split(',');
	if (dataList[1] > now) {
	 return;
	}

	var uuid = engine.generateUuid();
	var expire = now + 60000;
	storage[key] = uuid + ',' + expire;
	mono.storage.set(storage);

	return setTimeout(function() {
	 return mono.storage.get(getObj, function(storage) {
		var dataList = storage[key].split(',');
		if (dataList[0] === uuid) {
			cb();
		}
	 });
	}, 5 * 1000);
		});
	};

	return mono.storage.getPromise({
		userjsIsInstaller: 0,
		userjsVideoId: '',
		userjsIsOpenVideo: 0
	}).then(function(options) {
		if (!options.userjsIsInstaller) {
	mono.debug('Skip', 'userjsIsInstaller', JSON.stringify(options));
	return;
		}

		if (!options.userjsIsOpenVideo && typeof options.userjsVideoId === 'string' && options.userjsVideoId) {
	dblTrackCheck(function() {
	 options.userjsIsOpenVideo = 1;
	 mono.storage.set(options, function() {
		var videoId = options.userjsVideoId.replace(/#/g, '');
		if (videoId !== 'videoId') {
			mono.openTab('https://www.youtube.com/watch?v=' + videoId);
		}
	 });
	});
		}
	});
 });
 //@insert

 mono.onReady(function() {
	if (mono.isGM) {
		engine.initMessageListener();
	} else {
		engine.init();
	}
 });
 var bg_utils = {
	getFileSize: function(message, cb) {
		"use strict";
		var url = message.url;
		var response = {
	fileSize: 0,
	fileType: '',
	status: 0,
	error: false
		};
		mono.request({
	url: url,
	type: 'HEAD'
		}, function(err, resp) {
	if (err) {
	 response.error = true;
	 return cb(response);
	}

	response.status = resp.statusCode;

	response.fileSize = parseInt(resp.headers['content-length']) || 0;

	var contentType = resp.headers['content-type'];
	if (contentType) {
	 response.fileType = contentType;
	}

	cb(response);
		});
		return true;
	},
	ChromeDl: function() {
		var inProgress = {};
		var isListen = false;
		var addListener = function() {
	if (isListen) return;
	isListen = true;
	chrome.downloads.onChanged.addListener(onChange);
		};
		var removeListener = function() {
	isListen = false;
	chrome.downloads.onChanged.removeListener(onChange);
		};
		var addDownloadId = function(id, details) {
	if (!inProgress[id]) {
	 inProgress[id] = details;
	}
		};
		var removeDownloadId = function(id) {
	if (inProgress[id]) {
	 delete inProgress[id];
	}
	if (Object.keys(inProgress).length === 0) {
	 removeListener();
	}
		};
		var canFixNetworkFiled = function(details, downloadDelta) {
	var time = parseInt(Date.now() / 1000);

	var resumeDownload = false;

	if (!details.lastFix) {
	 details.lastFix = 0;
	}

	if (details.lastFix + 5 < time) {
	 details.lastFix = time;
	 resumeDownload = true;
	}

	var isInterrupted = downloadDelta.state && downloadDelta.state.current === 'interrupted';
	var networkFailed = downloadDelta.error && downloadDelta.error.current === 'NETWORK_FAILED';
	var canResume = downloadDelta.canResume && downloadDelta.canResume.current;
	if (!isInterrupted || !networkFailed || !canResume) {
	 resumeDownload = false;
	}

	return resumeDownload;
		};
		var onChange = function(downloadDelta) {
	var details = inProgress[downloadDelta.id];
	if (!details) {
	 return;
	}

	var resumeDownload = false;
	if (details.fixNetworkFiled) {
	 resumeDownload = canFixNetworkFiled(details, downloadDelta);
	}
	if (resumeDownload) {
	 chrome.downloads.resume(downloadDelta.id);
	} else
	if (downloadDelta.state && ['interrupted', 'complete'].indexOf(downloadDelta.state.current) !== -1) {
	 removeDownloadId(downloadDelta.id);
	}
		};

		this.download = function(details) {
	var url = details.url;
	var filename = details.filename;

	details.fixNetworkFiled = /(vk\.me|userapi\.com)\/.+\.mp4/i.test(url);

	var options = {
	 url: url,
	 filename: filename
	};
	if (mono.isFirefox && engine.preferences.saveAsDialog) {
	 options.saveAs = true;
	}
	chrome.downloads.download(options, function(downloadId) {
	 if (details.fixNetworkFiled) {
		addDownloadId(downloadId, details);
		addListener();
	 }
	});
		};
	},
	chromeDownload: null,
	downloadFile: function(message) {
		"use strict";
		var _this = bg_utils;
		if (mono.isChrome || mono.isFirefox) {
	if (!_this.chromeDownload) {
	 _this.chromeDownload = new _this.ChromeDl();
	}
	var onHasPermission = function() {
	 return _this.chromeDownload.download(message.options);
	};
	if (chrome.downloads && chrome.downloads.download) {
	 return onHasPermission();
	} else
	if (chrome.permissions && chrome.permissions.request) {
	 chrome.permissions.request({
		permissions: ['downloads']
	 }, function(granted) {
		if (granted) {
			return onHasPermission();
		} else {
			mono.error('Permissions not granted!');
		}
	 });
	} else {
	 mono.error('Method in not supported!');
	}
		} else
		if (mono.isGM) {
	GM_download(message.options.url, message.options.filename);
		}
	},
	chromeListDownload: function(list, folder) {
		var waitDownloadId = null;

		list = list.map(function(item) {
	return {
	 url: item.url,
	 filename: folder + item.filename
	};
		});

		var addListener = function() {
	chrome.downloads.onChanged.addListener(onChange);
		};

		var removeListener = function() {
	chrome.downloads.onChanged.removeListener(onChange);
		};

		var onChange = function(downloadDelta) {
	if (downloadDelta.id !== waitDownloadId || !downloadDelta.state) {
	 return;
	}

	if (['interrupted', 'complete'].indexOf(downloadDelta.state.current) !== -1) {
	 waitDownloadId = null;
	 return onSuccess();
	}
		};

		addListener();

		var index = -1;
		var onSuccess = function() {
	index++;
	var item = list[index];
	if (!item) {
	 removeListener();
	 return;
	}

	return chrome.downloads.download({
	 url: item.url,
	 filename: item.filename
	}, function(downloadId) {
	 waitDownloadId = downloadId;
	});
		};

		return onSuccess();
	},
	downloadList: function(message) {
		"use strict";
		var _this = this;
		var list = message.fileList;
		var folder = message.folder;

		if ((mono.isChrome || mono.isFirefox) && chrome.downloads && chrome.downloads.download) {
	_this.chromeListDownload(list, folder);
		} else {
	list.forEach(function(item) {
	 _this.downloadFile({
		options: {
			url: item.url,
			filename: folder + item.filename
		}
	 });
	});
		}
	},
	getUmmyIcon: function(message, cb) {
		"use strict";
		var icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB90lEQVQ4EcVSy2oUURCtqm7HcYgmYDTiYxEEERdZGP0B0UVwEcSv8LHIb4gbQcjGlVtB40YhfkAWuhs0uFOIgjJomiEzztzue4+n7rTgH6SaoqpPnao6fW+LHLapC9hdPHMbKT1UTcsQWxDBnAAdFkuvQ6QR1cD0QAUVoF+0kKdXBoO32j959maK8V1LVDaBDXkwm9q32atz/hmRpIZb5STqPaDIjP/oFAS5Xu1l/MPCBZhxt09uSRykCn1QhmQr1MiSQ3TPGYdIMtwfZPh3MjkhlvOWOcuTrJQB5VJeR0g5HlzjMSSVpp7mtQGFBJjXwJp69AlqtlTW0bpQ6nNLbTdjSCIxNhkOqUBwBconZYWZr1G6RgXcRoI782k0rO681vVq15o6SGyCrFefbHVnS6eNkmcSyMlOvr48ernimjlf5WcUuP1zr7C7W090/twiMcjw+y95dWcjXRr7Sn6Ba8mmB1RQ/MwqOK2mg356FPFi4xGm4z8I40nOT434OanElDdWM2aH/eAtHOlz98XZRBch0uPnHPu4J9uPn+dNzNGTLho/Kj+D1gza12fl1RuEtlmaaWPiGkOK8k0mecB5Nnes8DZvdiwPgRVrmbAp19aI8Fe2ZSDN86aOk9OpkfiHqfKoap9JfMTWfcavvNXN+/H9G596uPYX83AWUVC6/FsAAAAASUVORK5CYII=';
		cb(icon);
	},
	getWarningIcon: function(message, cb) {
		"use strict";
		var icon;
		var color = message.color || '#c2c2c2';
		if (message.type === 'audio') {
	icon = '<svg width="21px" height="24px" viewBox="0 0 21 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M21,2.76923077 L21,17.6487288 C21,17.6487288 21,17.6487288 21,17.6487288 L21,18.4615385 L20.9068729,18.4615385 C20.723595,19.2712249 20.2716013,20.0865791 19.5669296,20.7680198 C17.9203537,22.360313 15.5176896,22.6184747 14.2004289,21.3446402 C12.8831682,20.0708056 13.1501309,17.7473503 14.7967068,16.1550571 C16.0602516,14.9331676 17.7690324,14.4969051 19.0909091,14.9356816 L19.0909091,14.9356816 L19.0909091,4.15384615 L7.63636364,6.92307692 L7.63636364,19.4948826 C7.63636364,19.4948826 7.63636364,19.4948826 7.63636364,19.4948826 L7.63636364,20.3076923 L7.5432365,20.3076923 C7.35995859,21.1173788 6.90796493,21.9327329 6.20329323,22.6141737 C4.55671732,24.2064669 2.15405328,24.4646286 0.836792552,23.190794 C-0.480468173,21.9169595 -0.213505501,19.5935041 1.43307041,18.0012109 C2.69661523,16.7793214 4.40539601,16.343059 5.72727273,16.7818354 L5.72727273,16.7818354 L5.72727273,6.46153846 L5.72727273,3.69230769 L21,0 L21,2.76923077 Z" fill="' + color + '"></path></svg>';
		} else
		if (message.type === 'playlist') {
	icon = '<svg width="24px" height="18px" viewBox="0 0 24 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M0,0 L0,3.6 L3.42857143,3.6 L3.42857143,0 L0,0 Z M0,7.2 L0,10.8 L3.42857143,10.8 L3.42857143,7.2 L0,7.2 Z M5.14285714,0 L5.14285714,3.6 L24,3.6 L24,0 L5.14285714,0 Z M5.14285714,7.2 L5.14285714,10.8 L20.5714286,10.8 L20.5714286,7.2 L5.14285714,7.2 Z M0,14.4 L0,18 L3.42857143,18 L3.42857143,14.4 L0,14.4 Z M5.14285714,14.4 L5.14285714,18 L22.2857143,18 L22.2857143,14.4 L5.14285714,14.4 Z" fill="' + color + '"></path></svg>';
		} else {
	// photo
	icon = '<svg width="24px" height="18px" viewBox="0 0 24 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M19.5,3 L21.0089096,3 C22.6582294,3 24,4.34288718 24,5.99942248 L24,15.0005775 C24,16.6556493 22.6608432,18 21.0089096,18 L2.99109042,18 C1.34177063,18 0,16.6571128 0,15.0005775 L0,5.99942248 C0,4.34435073 1.33915679,3 2.99109042,3 L7.5,3 C7.5,1.34651712 8.84187067,0 10.497152,0 L16.502848,0 C18.1583772,0 19.5,1.34314575 19.5,3 L19.5,3 Z M13.5,16.5 C16.8137087,16.5 19.5,13.8137087 19.5,10.5 C19.5,7.18629134 16.8137087,4.5 13.5,4.5 C10.1862913,4.5 7.5,7.18629134 7.5,10.5 C7.5,13.8137087 10.1862913,16.5 13.5,16.5 Z M13.5,15 C15.9852815,15 18,12.9852815 18,10.5 C18,8.0147185 15.9852815,6 13.5,6 C11.0147185,6 9,8.0147185 9,10.5 C9,12.9852815 11.0147185,15 13.5,15 Z" fill="' + color + '"></path></svg>';
		}
		cb('data:image/svg+xml;utf8,' + encodeURIComponent(icon));
	},
	checkUrlsOfOpenTabs: function(regExpList, callback) {
		"use strict";
		var getUrlList = mono.isGM ? function(cb) {
	cb([location.href]);
		} : mono.isChrome ? function(cb) {
	var urlList = [];
	chrome.tabs.query({}, function(tabs) {
	 tabs.forEach(function(tab) {
		urlList.push(tab.url);
	 });
	 cb(urlList);
	});
		} : mono.isFirefox ? function(cb) {
	var urlList = [];
	if (mono.isFirefoxMobile) {
	 return cb(urlList);
	}

	chrome.tabs.query({}, function(tabs) {
	 tabs.forEach(function(tab) {
		urlList.push(tab.url);
	 });
	 cb(urlList);
	});
		} : mono.isOpera ? function(cb) {
	var urlList = [];
	var oTabs = opera.extension.tabs.getAll();
	oTabs.forEach(function(tab) {
	 urlList.push(tab.url);
	});
	cb(urlList);
		} : mono.isSafari ? function(cb) {
	var urlList = [];

	safari.application &&
	 safari.application.activeBrowserWindow &&
	 safari.application.activeBrowserWindow.tabs &&
	 safari.application.activeBrowserWindow.tabs.forEach(function(tab) {
		if (!tab.url) {
			return 1;
		}
		urlList.push(tab.url);
	 });

	cb(urlList);
		} : function(cb) {
	cb([]);
		};

		getUrlList(function(urlList) {
	var foundUrlList = [];
	urlList.forEach(function(url) {
	 regExpList.forEach(function(regexp) {
		if (url.search(regexp) !== -1) {
			foundUrlList.push(url);
		}
	 });
	});
	callback(foundUrlList);
		});
	},
	getData: function(message, cb) {
		"use strict";
		var url = message.url;
		if (!url) {
	return cb();
		}

		mono.request({
	url: url
		}, function(err, resp, data) {
	if (err) {
	 return cb();
	}
	cb(data);
		});
		return true;
	}
 };

 engine.utils = bg_utils;
 (function() {
	var language = {};
	var preference = {};
	var varCache = {
		icons: {},
		activeTabInfo: {},
		helperName: ''
	};
	var menuContainer = undefined;

	var translatePage = function() {
		var elList = menuContainer.querySelectorAll('*[data-i18n]');
		for (var i = 0, len = elList.length; i < len; i++) {
	var el = elList[i];
	var key = el.dataset.i18n;
	el.textContent = language[key];
	if (el.classList.contains('label')) {
	 el.title = language[key];
	}
		}
	};

	var onModuleToggle = function() {
		var tabInfo = varCache.activeTabInfo;

		var state = varCache.moduleTrigger.classList.contains('disabled') ? 1 : 0;
		tabInfo.state = state;

		setCheckboxState(state, 1);

		preference[tabInfo.prefKey] = state;

		mono.sendMessage({
	action: 'viaMenu_' + 'changeState',
	state: state ? 1 : 0,
	prefKey: tabInfo.prefKey,
	moduleName: tabInfo.moduleName,
	needInclude: tabInfo.isNotResponse
		});

		tabInfo.isNotResponse = false;

		updateMenuItem(tabInfo);
	};

	var menuItemAction = function(event) {
		event.preventDefault();
		event.stopPropagation();
		var node = this;

		if (node.classList.contains('inactive')) {
	return;
		}

		var action = node.dataset.action;

		var isModule = node.classList.contains('module');

		if (action === 'enableModule') {
	return onModuleToggle();
		} else {
	mono.sendMessage({
	 action: (isModule ? 'viaMenu_' : '') + action
	});
		}

		if (mono.isGM) {
	_menu.hideMenuItems();
	return;
		}
		if (mono.isSafari) {
	safari.extension.popovers[0].hide();
	return;
		}
		window.close();
	};

	var setItemState = function(className, show) {
		var li = menuContainer.querySelectorAll('div.' + className);
		for (var i = 0; i < li.length; i++) {
	if (show) {
	 li[i].classList.remove('inactive');
	} else {
	 li[i].classList.add('inactive');
	}
		}
	};

	var setModuleItemState = function(moduleName, show) {
		if (!show) {
	return setItemState('module', false);
		}

		var li = menuContainer.querySelectorAll('div.module');
		for (var i = 0; i < li.length; i++) {
	if (li[i].classList.contains(moduleName)) {
	 li[i].classList.remove('inactive');
	} else {
	 li[i].classList.add('inactive');
	}
		}
	};

	var safariBlankLink = function(e) {
		e.preventDefault();

		mono.openTab(this.href, true);
	};

	var updateSafariLinks = function() {
		var links = menuContainer.querySelectorAll('a[href][target="_blank"]');
		for (var i = 0, len = links.length; i < len; i++) {
	links[i].removeEventListener('click', safariBlankLink);
	links[i].addEventListener('click', safariBlankLink);
		}
	};

	var bindSocialBtn = function() {
		var targetUrl = 'http://savefrom.net/user.php';
		var url = encodeURIComponent(targetUrl);
		var image = encodeURIComponent('http://savefrom.net/img/icon_100.png');
		var title = encodeURIComponent(language.extName);
		var desc = encodeURIComponent(language.socialDesc);
		var btnList = {
	vk: {
	 network: 'vkontakte',
	 title: language.shareIn.replace('%w', 'VK'),
	 href: 'http://vk.com/share.php?url=' + url + '&image=' + image + '&title=' + title + '&description=' + desc
	},
	ok: {
	 network: 'odnoklassniki',
	 title: language.shareIn.replace('%w', 'OK.ru'),
	 href: 'http://www.odnoklassniki.ru/dk?st.cmd=addShare&st.s=1&st._surl=' + url + '&st.comments=' + desc
	},
	mailru: {
	 network: 'mail.ru',
	 title: language.shareIn.replace('%w', 'Mail.ru'),
	 href: 'http://connect.mail.ru/share?url=' + url + '&title=' + title + '&description=' + desc + '&imageurl=' + image
	},
	tw: {
	 network: 'twitter',
	 title: language.shareIn.replace('%w', 'Twitter'),
	 href: 'https://twitter.com/intent/tweet?text=' + title + '&url=' + url
	},
	fb: {
	 network: 'facebook',
	 title: language.shareIn.replace('%w', 'Facebook'),
	 href: 'http://www.facebook.com/sharer.php?s=100&p[url]=' + url + '&p[title]=' + title + '&p[summary]=' + desc + '&p[images][0]=' + image
	},
	gp: {
	 network: 'google+',
	 title: language.shareIn.replace('%w', 'Google+'),
	 href: 'https://plus.google.com/share?url=' + url
	},
	lj: {
	 network: 'livejournal',
	 title: language.shareIn.replace('%w', 'Livejournal'),
	 href: 'http://www.livejournal.com/update.bml?subject=' + title + '&event=' + desc + ' ' + url
	}
		};
		for (var item in btnList) {
	var link = menuContainer.querySelector('.social-btn.' + item);
	if (!link) {
	 continue;
	}
	link.title = btnList[item].title;
	link.href = btnList[item].href;
	link.dataset.network = btnList[item].network;
		}
		link.parentNode.addEventListener('click', function(e) {
	var btn = e.target;
	if (!btn.classList.contains('social-btn')) {
	 return;
	}
	var network = btn.dataset.network;
	mono.sendMessage({
	 action: 'track',
	 t: 'social',
	 st: targetUrl,
	 sa: 'share',
	 sn: network
	});
		});
	};

	var setModuleBtnState = function(tabInfo) {
		if (['odnoklassniki'].indexOf(tabInfo.moduleName) !== -1) {
	setItemState('bookmarklet', false);
		} else {
	setItemState('bookmarklet', true);
		}

		setModuleItemState(tabInfo.moduleName, tabInfo.state);

		if (tabInfo.state && tabInfo.moduleName === 'youtube') {
	var url = tabInfo.url;

	var isPlaylist = /\/playlist\?|[?&]list=/.test(url);
	if (!isPlaylist) {
	 isPlaylist = /(user|channel|c|show)\/[^\/]+(\/feed|\/featured|\/videos|$)/i.test(url);
	}
	if (!isPlaylist) {
	 isPlaylist = /\/(feed)\/(trending|subscriptions|history)/i.test(url);
	}

	setItemState('plYoutube', !!isPlaylist);
		}
	};

	var setCheckBoxModuleState = function(tabInfo) {
		"use strict";
		var checkBoxIsEnable = false;
		var checkBoxState = false;
		if (['savefrom'].indexOf(tabInfo.moduleName) !== -1) {
	checkBoxState = 'force';
		} else
		if (tabInfo.moduleName) {
	checkBoxState = !!tabInfo.state;
	checkBoxIsEnable = true;
		}
		setItemState('enableModule', checkBoxIsEnable);
		setCheckboxState(checkBoxState);
	};

	var onGetTabInfo = function(tabInfo, force) {
		varCache.activeTabInfo = tabInfo = tabInfo || {};

		setCheckBoxModuleState(tabInfo);
		setModuleBtnState(tabInfo);

		if (!force) {
	var onResponse = function(moduleInfo) {
	 "use strict";
	 clearTimeout(timeout);
	 tabInfo.isNotResponse = !moduleInfo;

	 for (var key in moduleInfo) {
		tabInfo[key] = moduleInfo[key];
	 }

	 setCheckBoxModuleState(tabInfo);
	 setModuleBtnState(tabInfo);
	};
	var timeout = setTimeout(onResponse, 250);
	mono.sendMessage({
	 action: 'getActiveTabModuleInfo',
	 url: tabInfo.url
	}, onResponse);
		}
	};

	var updateMenuItem = function(tabInfo) {
		varCache.activeTabInfo = {};

		if (tabInfo) {
	onGetTabInfo(tabInfo, 1);
		} else {
	mono.sendMessage({
	 action: 'getActiveTabInfo'
	}, onGetTabInfo);
		}
	};

	var setIconState = function(el, state) {
		var path = el.querySelector('path');
		if (state === 'hover') {
	path.setAttribute('fill', '#ffffff');
		} else
		if (state === 'active') {
	path.setAttribute('fill', '#AAAAAA');
		} else {
	var type = el.getAttribute('data-type');
	if (type === 'downloadMP3Files') {
	 path.setAttribute('fill', '#00CCFF');
	} else
	if (type === 'downloadPlaylist') {
	 path.setAttribute('fill', '#77D1FA');
	} else
	if (type === 'downloadPhotos') {
	 path.setAttribute('fill', '#88cb66');
	} else
	if (type === 'showAboutPage') {
	 path.setAttribute('fill', '#ADE61B');
	} else
	if (type === 'updateLinks') {
	 path.setAttribute('fill', '#CB7FBD');
	} else
	if (type === 'downloadFromCurrentPage') {
	 path.setAttribute('fill', '#CB7FBD');
	}
		}
	};

	var getAboutPage = function() {
		"use strict";
		return mono.create(document.createDocumentFragment(), {
	append: [
	 mono.create('p', {
		text: language.aboutDescription
	 }),
	 mono.create('a', {
		href: 'http://savefrom.net/faq.php#supported_resourses',
		target: '_blank',
		text: language.aboutSupported,
		style: {
			display: 'block'
		}
	 }),
	 mono.create('a', {
		href: 'http://savefrom.net/user.php?helper=' + varCache.helperName,
		target: '_blank',
		text: language.homePage,
		style: {
			display: 'block'
		}
	 })
	]
		});
	};

	var updateDescription = function(action, label) {
		var desc = varCache.desc;
		var text = varCache.deskText;
		var title = varCache.descTitel;
		var more = varCache.descMore;
		desc.dataset.page = action;

		var icon = varCache.icons[action];
		var subIcon = desc.querySelector('.icon');
		if (icon) {
	var _icon = icon.cloneNode(true);
	setIconState(_icon, 'active');
	if (subIcon) {
	 subIcon.parentNode.replaceChild(_icon, subIcon);
	}
	subIcon.style.visibility = 'visible';
		} else {
	subIcon.style.visibility = 'hidden';
		}

		if (action === 'showAboutPage') {
	title.textContent = language.aboutTitle;
	text.textContent = '';
	text.appendChild(getAboutPage());
	more.style.display = 'none';
		} else {
	title.textContent = label;
	text.textContent = language['menu' + mono.capitalize(action)] || '';
	more.style.display = 'block';
		}

		if (mono.isSafari) {
	updateSafariLinks();
		}
	};

	var onReady = function() {
		translatePage();

		varCache.descMore.href = 'http://savefrom.net/user.php?helper=' + varCache.helperName;

		var btnList = menuContainer.querySelectorAll('div[data-action]');
		for (var i = 0; i < btnList.length; i++) {
	var icon = btnList[i].querySelector('svg');
	if (icon) {
	 varCache.icons[btnList[i].dataset.action] = icon;
	 setIconState(icon);
	}

	if (btnList[i].style.display === 'none') {
	 continue;
	}

	mono.create(btnList[i], {
	 on: [
		['click', menuItemAction],
		['mouseenter', function() {
			"use strict";
			var action = this.dataset.action;

			var icon = varCache.icons[action];
			icon && setIconState(icon, 'hover');

			var span = this.querySelector('span');
			var title = span && span.textContent || '';

			updateDescription(action, title);
		}],
		['mouseleave', function() {
			"use strict";
			var action = this.dataset.action;

			var icon = varCache.icons[action];
			icon && setIconState(icon);
		}]
	 ]
	});
		}

		bindSocialBtn();

		if (mono.isSafari) {
	updateSafariLinks();
		}

		updateDescription('showAboutPage');

		updateMenuItem();

		menuContainer.classList.remove('loading');
	};

	var setCheckboxState = function(state, byUser) {
		if (byUser) {
	varCache.moduleTrigger.classList.add('sf-transition');
		}
		if (state === 'force') {
	varCache.moduleTrigger.classList.add('enableForce');
		} else {
	varCache.moduleTrigger.classList.remove('enableForce');
		}
		if (state) {
	varCache.moduleTrigger.classList.remove('disabled');
	varCache.moduleTrigger.nextElementSibling.textContent = language.disableModule;
		} else {
	varCache.moduleTrigger.classList.add('disabled');
	varCache.moduleTrigger.nextElementSibling.textContent = language.enableModule;
		}
		if (varCache.desc.dataset.page !== 'showAboutPage') {
	updateDescription('enableModule', state ? language.disableModule : language.enableModule);
		}
	};

	var tutorial = {
		tutorialSlides: null,
		show: function() {
	"use strict";
	if (!preference.onceShowYtTutorial) {
	 return;
	}

	var onClose = function() {
	 mono.sendMessage({
		action: 'updateOption',
		key: 'onceShowYtTutorial',
		value: preference.onceShowYtTutorial = 0
	 });
	 mono.sendMessage({
		action: 'setIconBadge',
		text: ''
	 });
	};

	if (!tutorial.tutorialSlides) {
	 return onClose();
	}

	return tutorial.tutorialSlides.show({
	 container: menuContainer,
	 width: 482,
	 height: 404 + (mono.isGM ? 2 : 0),
	 padding: 4,
	 slideList: tutorial.tutorialSlides.getYtSlideList('black'),
	 onClose: onClose,
	 trackId: 'Menu',
	 boxStyle: {
		backgroundColor: 'transparent'
	 },
	 containerStyle: {
		borderRadius: '3px',
		backgroundColor: 'rgba(0, 104, 255, 0.9)',
		padding: 0,
		margin: '4px',
		boxShadow: 'none'
	 },
	 slideStyle: {
		backgroundColor: 'transparent',
		borderRadius: 0
	 },
	 leftBtnStyle: {
		top: '4px',
		left: '4px'
	 },
	 rightBtnStyle: {
		top: '4px',
		right: '4px'
	 },
	 closeBtnStyle: {
		backgroundColor: '#fff',
		color: 'rgba(0, 104, 255, 0.9)'
	 },
	 cssStyle: {
		' .sf-dots': {
			paddingTop: '2px'
		},
		' .sf-dot i': {
			backgroundColor: '#fff'
		},
		' .sf-dot.active i': {
			backgroundColor: 'transparent',
			borderRadius: '6px',
			margin: '-1px',
			width: '6px',
			height: '6px',
			border: '2px solid #fff'
		},
		' .sf-slider-conteiner span': {
			color: '#fff !important'
		},
		' .sf-slider-conteiner a': {
			color: '#fff !important'
		}
	 },
	 arrowColor: '#fff',
	 arrowColorActive: '#fff',
	 onResize: function(details) {
		details.box.style.position = 'absolute';
	 },
	 withOpacity: true,
	 withDelay: 250,
	 onShow: function() {
		mono.isSafari && updateSafariLinks();

		mono.sendMessage({
			action: 'setIconBadge',
			text: '?'
		});
	 }
	});
		}
	};

	var setVersion = function(version, lastVersion) {
		"use strict";
		var versionNode = varCache.desc.querySelector('.version');

		versionNode.textContent = '';

		versionNode.appendChild(mono.create('span', {
	text: language.aboutVersion + ' ' + version
		}));

		if (lastVersion && lastVersion !== version) {
	versionNode.appendChild(mono.create('a', {
	 text: language.updateTo.replace('%d', lastVersion),
	 href: 'http://savefrom.net/user.php?helper=' + varCache.helperName + '&update=' + version,
	 target: '_blank'
	}));
		}
	};

	var run = function(parent) {
		menuContainer = (parent || document).querySelector('.sf-menu-container');
		if (!menuContainer) {
	return;
		}

		setTimeout(function() {
	menuContainer.classList.remove('loading');
		}, 1000);

		varCache.list = menuContainer.querySelector('.sf-menu-list');
		varCache.desc = menuContainer.querySelector('.sf-menu-desc');
		varCache.moduleTrigger = menuContainer.querySelector('.sf-checkbox');
		varCache.descTitel = varCache.desc.querySelector('.title');
		varCache.deskText = varCache.desc.querySelector('.desc');
		varCache.descMore = varCache.desc.querySelector('.more');

		varCache.list.style.height = varCache.list.offsetHeight + 'px';

		mono.sendMessage({
	action: 'getMenuDetails'
		}, function(response) {
	language = response.language;
	preference = response.preferences;

	mono.global.language = language;
	mono.global.preference = preference;

	varCache.helperName = response.helperName;

	setVersion(response.version, response.lastVersion);

	if (['en', 'uk', 'ru'].indexOf(language.lang) === -1) {
	 menuContainer.classList.add('no-poll');
	}

	tutorial.show();

	onReady();
		});
	};

	mono.onReady(function() {
		if (mono.isGM) {
	_menu.initMenu = run;
		} else {
	run();
		}
	});

	//@insert
 })();
 var dailymotion_com_embed = {
	getDailymotionLinks: function(request, callback) {
		var _this = dailymotion_com_embed;
		var callback_links = function(data) {
	if (!data) {
	 data = {};
	}

	_this.addUmmyLinks(data.links, request.extVideoId);

	var response = {
	 action: request.action,
	 extVideoId: request.extVideoId,
	 links: data.links,
	 title: data.title,
	 duration: data.duration,
	 thumb: data.thumb
	};

	callback(response);
		};

		dailymotion_com_embed.getEmbedVideoInfo(request.extVideoId, request.metadata, callback_links);
		return true;
	},

	/**
	 * @private
	 */
	addUmmyLinks: function(links, videoId) {
		if (engine.preferences.showUmmyItem && links && links.length) {
	links.push({
	 url: 'ummy://www.dailymotion.com/video/' + videoId + '?sf_type=audio',
	 type: 'ummyAudio',
	 extra: 'ummy',
	 videoId: 'dm-' + videoId
	});
		}
	},

	readDmMetadata: function(metadata, noRead) {
		"use strict";
		if (!metadata.qualities) {
	return;
		}

		delete metadata.qualities.auto;

		var qualities = metadata.qualities;

		var links = [];
		var info = {
	title: metadata.title,
	duration: metadata.duration,
	thumb: metadata.poster_url,
	links: undefined
		};

		for (var size in qualities) {
	var linkList = qualities[size];
	if (!Array.isArray(linkList)) continue;

	for (var i = 0, item; item = linkList[i]; i++) {
	 if (!/mp4|flv/.test(item.type) || !item.url) {
		continue;
	 }
	 if (!noRead) {
		var linkObj = dailymotion_com_embed.readLink(item.url);
		linkObj && links.push(linkObj);
	 } else {
		links.push(item.url);
	 }
	}
		}

		if (links.length) {
	if (!noRead) {
	 links.sort(function(a, b) {
		return a.height < b.height;
	 });
	}
	info.links = links;
		}

		return info;
	},

	getPlayerV5Links: function(text, cb, noRead) {
		"use strict";
		var metadata = null;
		try {
	var scriptList = mono.getPageScript(text, /playerV5/);
	scriptList.some(function(script) {
	 "use strict";
	 var jsonList = mono.findJson(script);
	 return jsonList.some(function(json) {
		if (json && json.metadata) {
			metadata = json.metadata;
			return true;
		}
	 });
	});

	if (!metadata) {
	 throw 'Metadata is not found!';
	}
		} catch (e) {
	return cb();
		}

		var info = this.readDmMetadata(metadata, noRead);
		cb(info);
	},

	readLink: function(url) {
		url = url.replace(/\\\//g, '/');
		url = url.replace(/\@\@[\w\-]+$/, '');
		var size = '';
		var t = url.match(/\/cdn\/\w+\-(\d+x\d+)\//i);
		if (t && t.length > 1) {
	size = t[1];
		} else {
	t = url.match(/\D(\d+x\d+)\D/i);
	if (t && t.length > 1) {
	 size = t[1];
	}
		}

		var ext = 'FLV';
		t = url.match(/\.(\w{1,6})(?:$|\?)/);
		if (t && t.length > 1) {
	ext = t[1].toUpperCase();
		}

		if (size !== '80x60') {
	var height = parseInt(size.split('x').slice(-1)[0]);
	return {
	 url: url,
	 name: ext + ' ' + height,
	 ext: ext,
	 info_url: '',
	 height: height
	};
		}
	},

	getLinks: function(text, cb, noRead) {
		var about = {};
		var links = [];
		var info = text.match(/(?:var|,)\s*info\s*=\s*\{(.*?)\}\s*(?:;|,\s*\w+\s*=)/i);

		if (!info || info.length < 2) {
	return dailymotion_com_embed.getPlayerV5Links(text, cb, noRead);
		}
		info = info[1];
		try {
	info = JSON.parse('{' + info + '}');
	if (!info) {
	 return cb();
	}
	about.title = info.title;
	about.duration = info.duration;
	about.thumb = info.thumbnail_medium_url;
	for (var i in info) {
	 if (!info.hasOwnProperty(i)) {
		continue;
	 }
	 if (typeof info[i] !== 'string') {
		continue;
	 }
	 if (info[i].search(/^https?:\/\/[^\s\"]+\.(mp4|flv)(\?|$)/) > -1) {
		if (noRead) {
			links.push(info[i]);
		} else {
			var link = dailymotion_com_embed.readLink(info[i]);
			if (link !== undefined) {
		links.push(link);
			}
		}
	 }
	}
		} catch (e) {}
		if (links.length > 0) {
	if (!noRead) {
	 links.sort(function(a, b) {
		return a.height < b.height;
	 });
	}
	about.links = links;
		}
		cb(about);
	},

	getDailymotionEmbedVideoInfoMsg: function(message, cb) {
		mono.request({
	url: message.url
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	dailymotion_com_embed.getLinks(data, cb, true);
		});
		return true;
	},

	getEmbed: function(url, cb) {
		mono.request({
	url: url
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	dailymotion_com_embed.getLinks(data, cb);
		});
	},

	getEmbedVideoInfo: function(id, metadata, callback) {
		"use strict";
		if (metadata) {
	var info = this.readDmMetadata(metadata);
	if (info && info.links && info.links.length) {
	 return callback(info);
	}
		}

		var url = "http://www.dailymotion.com/embed/video/" + id;
		dailymotion_com_embed.getEmbed(url, callback);
	}
 };

 engine.modules.dailymotion = dailymotion_com_embed;
 var odnoklassniki_ru_embed = {
	getOdnoklassnikiLinks: function(request, callback) {
		function callback_links(links) {
	var response = {
	 action: request.action,
	 extVideoId: request.extVideoId,
	 links: links,
	 title: request.title
	};

	callback(response);
		}

		odnoklassniki_ru_embed._getOdnoklassnikiLinks(request.extVideoId, callback_links);
		return true;
	},

	getOdnoklassnikiAudioLinks: function(request, callback) {
		function callback_links(data) {
	var response = {
	 action: request.action,
	 trackId: request.trackId,
	 jsessionId: request.jsessionId,
	 data: data
	};

	callback(response);
		}

		odnoklassniki_ru_embed._getOdnoklassnikiAudioLinks(request.url, request.trackId, request.jsessionId, callback_links);
		return true;
	},

	_getOdnoklassnikiLinks: function(extVideoId, callback) {
		if (!extVideoId) {
	callback(null);
	return;
		}

		var url = 'http://in.video.mail.ru/cgi-bin/video/oklite?eid=' + extVideoId;

		mono.request({
	url: url
		}, function(err, resp, data) {
	if (err || !data) {
	 return callback(null);
	}

	var u = 'http://www.okcontent.video.mail.ru/media/';

	var host = data.match(/\$vcontentHost=([^\s"'<>]+)/i);
	if (host && host.length > 1) {
	 u = 'http://' + host[1] + '/media/';
	}

	u += extVideoId;

	var links = [];

	var quality = '';
	var qulityMatch = data.match(/\$height=([0-9]+)/);
	if (qulityMatch && qulityMatch.length > 1) {
	 quality = qulityMatch[1];
	}

	links.push({
	 url: u + '-v.mp4',
	 name: 'SD',
	 ext: 'FLV',
	 subname: quality
	});

	if (data.search(/\$HDexist=1/i) > -1) {
	 quality = '';
	 qulityMatch = data.match(/\$HDheight=([0-9]+)/);
	 if (qulityMatch && qulityMatch.length > 1) {
		quality = qulityMatch[1];
	 }

	 links.push({
		url: u + '-hv.mp4',
		name: 'HD',
		ext: 'MP4',
		subname: quality
	 });
	}

	if (links) {
	 callback(links);
	}
		});
	},

	_getOdnoklassnikiAudioLinks: function(pageUrl, trackId, jSessionId, cb) {
		if (!trackId || !jSessionId) {
	return cb(null);
		}

		mono.request({
	url: 'http://wmf1.ok.ru/play;jsessionid=' + jSessionId + '?tid=' + trackId,
	json: true
		}, function(err, resp, json) {
	if (err || !json) {
	 return cb(null);
	}

	cb(json);
		});
	},

	getOkAudioListLinks: function(msg, cb) {
		var responseList = [];
		var trackIdList = msg.trackIdArr;
		var jSessionId = msg.jsessionId;
		if (!Array.isArray(trackIdList) || typeof jSessionId !== 'string' || !trackIdList.length) {
	return cb(responseList);
		}

		var waitCount = trackIdList.length;
		var readyCount = 0;
		var onReady = function() {
	readyCount++;
	if (readyCount !== waitCount) {
	 return;
	}
	return cb(responseList);
		};
		var onGetData = function(data) {
	if (data) {
	 responseList.push(data);
	}
	onReady();
		};
		for (var i = 0, trackId; trackId = trackIdList[i]; i++) {
	this._getOdnoklassnikiAudioLinks(undefined, trackId, jSessionId, onGetData);
		}
		return true;
	},

	getClipyouLinks: function(id, hash, quality, title, cb) {
		mono.request({
	url: 'http://media.clipyou.ru/api/player/secure_link?record_id=' + id + '&type=mp4&resource_hash=' + hash,
	json: true
		}, function(err, resp, json) {
	if (err || !json || !Array.isArray(json.data) || !json.data.length) {
	 return cb();
	}

	var links = [];
	json.data.forEach(function(item) {
	 links.push({
		quality: quality,
		url: item,
		title: title
	 });
	});
	cb(links);
		});
	},

	getClipyouHash: function(id, cb) {
		mono.request({
	url: 'http://media.clipyou.ru/api/player_data.json?id=' + id
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	data = data.match('resource_hash".?:.?"([^"]*)"');
	if (!data || data.length < 2) {
	 return cb();
	}
	var hash = data[1];
	cb(hash);
		});
	},

	getPladformVideo: function(message, cb) {
		"use strict";
		var response = {
	action: message.action,
	extVideoId: message.extVideoId,
	links: [],
	title: message.title
		};

		var done = function() {
	cb(response);
		};

		var playerId = message.extVideoId.playerId;
		var videoId = message.extVideoId.videoId;

		mono.request({
	url: 'http://out.pladform.ru/getVideo?pl=' + playerId + '&videoid=' + videoId,
	xml: true
		}, function(err, resp, xml) {
	if (err || !xml) {
	 return done();
	}

	var srcList = xml.querySelectorAll('src');
	if (srcList.length === 0) {
	 return done();
	}

	var cover = xml.querySelector('cover') || undefined;
	if (cover && (cover = cover.textContent) && cover.substr(0, 2) === '//') {
	 cover = 'http:' + cover;
	}

	var time = xml.querySelector('time') || undefined;
	time = time && time.textContent;

	var title = xml.querySelector('title');
	title = title && title.textContent;
	if (title) {
	 response.title = title;
	}

	var firstLink = srcList[0];
	var type = firstLink.getAttribute('type');
	var id = firstLink.textContent || '';
	var quality = firstLink.getAttribute('quality');
	if (firstLink) {
	 if (type === 'clipyou') {
		return odnoklassniki_ru_embed.getClipyouHash(id, function(hash) {
			if (!hash) {
		return done();
			}

			odnoklassniki_ru_embed.getClipyouLinks(id, hash, quality, title, function(links) {
		response.links = links;

		done();
			});
		});
	 } else
	 if (type === 'rutube') {
		var externalEmbed = xml.querySelector('external_embed');
		externalEmbed = externalEmbed && externalEmbed.textContent;
		if (externalEmbed) {
			response.action = 'getRutubeLinks';
			response.links = [externalEmbed];
		}

		return done();
	 }
	}

	var qualityList = ['ld', 'sd'];
	var sizeList = ['360', '720'];

	for (var i = 0, src; src = srcList[i]; i++) {
	 id = src.textContent || '';
	 quality = src.getAttribute('quality');

	 if (/^\d+p$/.test(quality)) {
		quality = quality.match(/^(\d+)p$/)[1];
	 }

	 var qIndex = qualityList.indexOf(quality);
	 if (qIndex !== -1) {
		quality = sizeList[qIndex];
	 }

	 type = src.getAttribute('type');

	 if (type === 'video') {
		response.links.push({
			url: id,
			quality: quality,
			title: title,
			cover: cover,
			duration: time
		});
	 }
	}

	return done();
		});
		return true;
	},

	getOkMetadata: function(message, cb) {
		var url = message.url;
		if (!url) {
	return cb();
		}
		mono.request({
	method: 'POST',
	url: url,
	json: true
		}, function(err, resp, json) {
	if (err || !json) {
	 return cb();
	}

	cb(json);
		});
		return true;
	},

	getOkViaMobile: function(message, cb) {
		"use strict";
		var metadata = message.metadata;
		var params = {
	'st.cmd': 'movieLayer',
	'st.mvId': message.mvId
		};
		var url = 'http://m.ok.ru/dk?' + mono.param(params);

		var response = {
	action: message.action,
	links: null,
	title: metadata.movie.title
		};

		mono.request({
	url: url
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	var r = new RegExp('href="([^"]+st\\.cmd=moviePlaybackRedirect[^"]+st\\.mvid=' + message.mvId + '[^"]+)"');
	var moviePlaybackRedirect = data.match(r);
	moviePlaybackRedirect = moviePlaybackRedirect && moviePlaybackRedirect[1];
	if (!moviePlaybackRedirect) {
	 return cb();
	}

	moviePlaybackRedirect = mono.fileName.decodeSpecialChars(moviePlaybackRedirect);

	response.links = [{
	 url: moviePlaybackRedirect
	}];

	if (!/st.mq=\d+/.test(moviePlaybackRedirect)) {
	 return cb(response);
	}

	var videos = metadata.videos;
	if (!videos || !videos.length) {
	 return cb(response);
	}

	videos.forEach(function(item) {
	 if (!item.url) {
		return;
	 }

	 var params = mono.parseUrl(item.url);
	 if (!params.type) {
		return;
	 }

	 item.url = moviePlaybackRedirect.replace(/(st.mq=)\d+/, '$1' + params.type);
	});

	response.links = videos;

	cb(response);
		});
		return true;
	},

	okDirectOrMobile: function(message, cb) {
		"use strict";
		var metadata = message.metadata;
		var url = null;
		metadata.videos && metadata.videos.some(function(item) {
	if (item.url) {
	 url = item.url;
	 return true;
	}
		});

		var onAbort = function() {
	message.action = 'getOkViaMobile';
	odnoklassniki_ru_embed.getOkViaMobile(message, cb);
		};

		if (!url) {
	onAbort();
	return true;
		}

		mono.request({
	url: url,
	type: 'HEAD'
		}, function(err) {
	if (err) {
	 return onAbort();
	}

	message.action = 'getOkViaMobileNoWrap';
	message.links = metadata.videos;
	return cb(message);
		});
		return true;
	}
 };

 engine.modules.odnoklassniki = odnoklassniki_ru_embed;
 var soundcloud_com_embed = {
	getSoundcloudTrackInfo: function(request, callback) {
		function callback_links(data, isValid) {
	var response = {
	 action: request.action,
	 trackUrl: request.trackUrl,
	 client_id: request.client_id,
	 data: data,
	 checkLinks: isValid
	};

	callback(response);
		}

		soundcloud_com_embed._getSoundcloudTrackInfo(request.trackUrl, request.client_id, callback_links);
		return true;
	},

	_getSoundcloudTrackInfo: function(trackUrl, client_id, callback) {
		if (!trackUrl || !client_id) {
	callback(null);
	return;
		}

		var url = 'http://api.soundcloud.com/resolve.json?url=' + trackUrl +
	'&client_id=' + encodeURIComponent(client_id);

		mono.request({
	url: url,
	headers: {
	 Referer: url
	},
	json: true
		}, function(err, resp) {
	var json = resp.body;
	if (err || !json) {
	 return callback();
	}

	soundcloud_com_embed.checkSoundcloudLinks(json, client_id, callback);
		});
	},

	checkSoundcloudLinks: function(data, client_id, cb) {
		if (!data) {
	return cb();
		}
		var info = data;

		if (info.kind !== 'track' && info.tracks && info.tracks.length === 1) {
	info = info.tracks[0];
		}

		if (info.kind === 'track' && info.stream_url) {
	soundcloud_com_embed.validateSoundcloudTrackUrl(info.stream_url, client_id, function(isValid) {
	 cb(data, isValid);
	});
	return;
		}

		if (info.tracks && info.tracks[0]) {
	soundcloud_com_embed.validateSoundcloudTrackUrl(info.tracks[0].stream_url, client_id, function(isValid) {
	 cb(data, isValid);
	});
	return;
		}

		return cb(data);
	},

	validateSoundcloudTrackUrl: function(url, client_id, cb) {
		url += (url.indexOf('?') === -1) ? '?' : '&';
		url += 'client_id=' + client_id;
		mono.request({
	url: url,
	type: 'HEAD'
		}, function(err) {
	cb(!err);
		});
	}
 };

 engine.modules.soundcloud = soundcloud_com_embed;
 var vimeo_com_embed = {
	getVimeoLinks: function(request, callback) {
		"use strict";

		function callback_links(links, title, thumb) {
	var response = {
	 action: request.action,
	 extVideoId: request.extVideoId,
	 links: links,
	 title: title,
	 thumb: thumb
	};

	callback(response);
		}

		vimeo_com_embed._getVimeoLinks(request.extVideoId, request.url, callback_links);
		return true;
	},
	_getVimeoLinks: function(videoId, url, callback) {
		"use strict";
		vimeo_com_embed.getVimeoNoEmbedLinks(videoId, url, function(links, title, thumb) {
	if (links) {
	 return callback(links, title, thumb);
	}

	vimeo_com_embed.getVimeoEmbedLinks(videoId, callback);
		});
	},
	getVimeoEmbedLinks: function(videoId, callback) {
		"use strict";
		var abort = function() {
	return callback(null, '', '');
		};

		if (!videoId) {
	return abort();
		}

		mono.request({
	url: 'https://player.vimeo.com/video/' + videoId
		}, function(err, resp, data) {
	if (err || !data) {
	 return abort();
	}

	var jsonList = mono.findJson(data, [/"files":/]);
	var config = null;
	jsonList.some(function(obj) {
	 "use strict";
	 if (!obj.video || !obj.request || !obj.request.files) {
		return;
	 }

	 config = obj;
	 return true;
	});

	data = vimeo_com_embed.getLinksFromConfig(config);
	if (data) {
	 return callback(data.links, data.title, data.thumb);
	}

	return abort();
		});
	},
	getVimeoConfig: function(url, cb) {
		"use strict";
		var abort = function() {
	return cb(null, '', '');
		};

		mono.request({
	url: url
		}, function(err, resp, data) {
	if (err || !data) {
	 return abort();
	}

	data = vimeo_com_embed.getVimeoDataFromConfig(data);
	if (data) {
	 return cb(data.links, data.title, data.thumb);
	}

	return abort();
		});
	},
	getClipPageConfig: function(data, cb) {
		"use strict";
		var abort = function() {
	return cb(null, '', '');
		};

		var configUrl = null;
		var scriptList = mono.getPageScript(data, /['"]config_url['"]\s*:\s*/);
		scriptList.some(function(script) {
	var configList = mono.findJson(script, /['"]config_url['"]\s*:\s*/);
	return configList.some(function(config) {
	 if (config.player) {
		configUrl = config.player.config_url;
		if (configUrl) {
			return true;
		}
	 }
	});
		});

		if (configUrl) {
	return vimeo_com_embed.getVimeoConfig(configUrl, cb);
		}

		return abort();
	},
	getVimeoNoEmbedLinks: function(videoId, url, cb) {
		"use strict";
		var abort = function() {
	return cb(null, '', '');
		};

		if (videoId && url) {
	var isReview = /vimeo\.com\/[^\/]+\/review\/\d+/i.test(url);
	var isPrivate = /vimeo\.com\/\d+\/\w+/i.test(url);
	if (!isReview && !isPrivate) {
	 url = null;
	}
		}

		mono.request({
	url: url || 'https://vimeo.com/' + videoId
		}, function(err, resp, data) {
	if (err || !data) {
	 return abort();
	}

	var configUrl = data.match(/data-config-url=["']([^\s"'<>]+)/i);
	configUrl = configUrl && configUrl[1].replace(/&amp;/ig, '&');

	if (configUrl) {
	 return vimeo_com_embed.getVimeoConfig(configUrl, cb);
	} else {
	 return vimeo_com_embed.getClipPageConfig(data, cb);
	}
		});
	},
	getVimeoLinksFromConfig: function(msg, response) {
		"use strict";
		var data = vimeo_com_embed.getLinksFromConfig(msg.config);
		if (data) {
	return response(data);
		}

		return response(null);
	},
	getLinksFromConfig: function(config) {
		"use strict";
		if (!config || !config.video || !config.request || !config.request.files) {
	return null;
		}

		var video = config.video;
		var files = config.request.files;

		var data = {};

		data.title = video.title || '';

		var maxSize = null;
		for (var size in video.thumbs) {
	if (maxSize === null || maxSize < size) {
	 maxSize = size;
	 data.thumb = video.thumbs[size];
	}
		}

		data.links = [];

		for (var type in files) {
	if (!Array.isArray(files[type])) {
	 continue;
	}
	files[type].forEach(function(item) {
	 if (!item || !item.url || !item.mime) {
		return;
	 }

	 var ext = item.mime.split('/')[1];
	 if (!ext) {
		ext = item.url.match(/\.(\w{2,4})(?:\?|#|$)/i);
		ext = ext && ext[1] || 'mp4';
	 }

	 var extUp = ext.toUpperCase();

	 var height = item.quality;
	 if (/^\d+p$/.test(height)) {
		height = height.replace(/p$/, '');
	 }

	 var extQuality = extUp + ' ' + height;

	 data.links.push({
		url: item.url,
		name: extQuality,
		height: height,
		type: extUp,
		format: extUp,
		ext: ext
	 });
	});
		}

		if (!data.links.length) {
	data = null;
		}

		return data;
	},
	getVimeoDataFromConfig: function(config) {
		"use strict";
		config = config.replace(/(\{|,)\s*(\w+)\s*:/ig, '$1"$2":').
		replace(/(:\s+)\'/g, '$1"').replace(/\'([,\]\}])/g, '"$1');

		try {
	config = JSON.parse(config);
		} catch (err) {
	return null;
		}

		return this.getLinksFromConfig(config);
	}
 };

 engine.modules.vimeo = vimeo_com_embed;
 var vkontakte_ru_embed = {
	getVKLinks: function(request, callback) {
		function callback_links(vid, links, title, duration, thumb, data, embed) {
	if (embed) {
	 embed.origRequest = request;
	 engine.onMessage(embed, callback);
	 return;
	}

	var response = {
	 action: request.action,
	 extVideoId: vid ? vid : request.extVideoId,
	 links: links,
	 title: title,
	 duration: duration,
	 thumb: thumb,
	 data: data,
	 checkLinks: null
	};

	if (request.checkLinks && links && links.length > 0) {
	 vkontakte_ru_embed.checkVkLinks(links, function(checkUrl, isValid) {
		response.checkLinks = isValid;
		callback(response);
	 });
	 return;
	}

	callback(response);
		}

		vkontakte_ru_embed._getVKLinks(request.extVideoId, callback_links);
		return true;
	},

	preparePladformLinks: function(pladformLinks) {
		var links;
		var obj = {
	links: links = []
		};
		pladformLinks.forEach(function(item) {
	obj.title = item.title;
	obj.duration = item.duration;
	obj.thumb = item.cover;

	var ext = item.url.match(/[\w]+\.(mp4|flv)(?:\?|$)/i);
	if (!ext) {
	 ext = 'flv';
	} else {
	 ext = ext[1];
	}
	links.push({
	 url: item.url,
	 name: ext.toUpperCase(),
	 subname: item.quality.toUpperCase(),
	 type: ext.toLowerCase()
	});
		});
		return obj;
	},

	_getVKLinks: function(videoId, callback) {
		var _this = this;
		var links = [],
	title = videoId,
	data = null,
	embed = null,
	thumb = '',
	duration = '';

		var vid = videoId;

		var m = null;
		var oid = null;
		var id = null;

		var re = /^video(-?\d+)_(\d+)/i;

		if (!re.test(vid)) {
	var oidRe = /(?:^|&)oid=(-?\d+)/i;
	var idRe = /(?:^|&)id=(-?\d+)/i;

	m = vid.match(oidRe);
	oid = m && parseInt(m[1]);

	m = vid.match(idRe);
	id = m && parseInt(m[1]);

	vid = '';
	if (oid && id) {
	 vid = 'video' + oid + '_' + id;
	}
		} else {
	m = vid.match(re);
	oid = parseInt(m[1]);
	id = parseInt(m[2]);
		}

		if (!vid) {
	return callback(vid, links, title);
		}

		title = vid;

		return mono.request({
	url: 'https://vk.com/' + vid
		}, function(err, resp, response) {
	if (err || !response) {
	 return callback(vid, links, title, duration, thumb, data, embed);
	}

	var varsJson = null;

	mono.findJson(response, [/"vid":/, /"oid":/, /"md_title":/]).some(function(arr) {
	 var index = 4;
	 var obj = arr && arr[index] && arr[index].player && arr[index].player.params && arr[index].player.params[0];
	 if (obj && obj.vid === id && obj.oid === oid) {
		varsJson = obj;
		return true;
	 }
	});

	if (varsJson) {
	 var videoInfo = vkontakte_ru_embed.getVkLinksFromJson(varsJson);

	 links = videoInfo.links;
	 title = videoInfo.title;
	 thumb = videoInfo.thumb;
	 duration = videoInfo.duration;

	 return callback(vid, links, title, duration, thumb, varsJson, embed);
	}

	var embedPlayerUrl = null;
	mono.findJson(response, [/"player"/]).some(function(arr) {
	 var index = 4;
	 var embedUrl = arr && arr[4] && arr[4].player && arr[4].player.params && arr[4].player.params[0];
	 if (typeof embedUrl === 'string') {
		embedPlayerUrl = embedUrl;
		return true;
	 }
	});

	var foundEmbed = false;
	if (embedPlayerUrl) {
	 var m;
	 m = /dailymotion.com\/(?:swf\/)?video\/([\w\d]+)/i.exec(embedPlayerUrl);
	 if (m) {
		embed = {
			action: 'getDailymotionLinks',
			extVideoId: m[1]
		};
		foundEmbed = true;
	 }
	}

	if (foundEmbed) {
	 return callback(vid, links, title, duration, thumb, data, embed);
	}

	var async = false;
	mono.findJsonString(response, /video_box_wrap/).some(function(str) {
	 var m = null;
	 var frameBodyM = /<iframe([^>]+)>/i.exec(str);
	 if (frameBodyM) {
		var frameBody = frameBodyM[1];
		m = /youtube.com\\?\/embed\\?\/([\w\-]+)/i.exec(frameBody);
		if (m) {
			embed = {
		action: 'getYoutubeLinks',
		extVideoId: m[1]
			};
			return true;
		}
		m = /vimeo.com\\?\/video\\?\/(\d+)/i.exec(frameBody);
		if (m) {
			embed = {
		action: 'getVimeoLinks',
		extVideoId: m[1]
			};
			return true;
		}
		m = /src="([^"]*pladform\.ru[^"]+)"/i.exec(frameBody);
		if (m) {
			async = true;
			var params = mono.parseUrl(m[1]);
			engine.modules.odnoklassniki.getPladformVideo({
		extVideoId: {
		 playerId: params.pl,
		 videoId: params.videoid
		}
			}, function(response) {
		if (response && response.action === 'getRutubeLinks') {
		 response.links = null;
		}

		var pladformLinks = response && response.links;
		if (!Array.isArray(pladformLinks)) {
		 return callback(vid, links, title, duration, thumb, data, embed);
		}

		var videoInfo = vkontakte_ru_embed.preparePladformLinks(pladformLinks);

		return callback(vid, videoInfo.links, videoInfo.title, videoInfo.duration, videoInfo.thumb, data, embed);
			});
			return true;
		}
	 }

	 var videoSourceFound = mono.getHtmlTags('video', str).some(function(videoTag) {
		var doc = mono.parseHtmlFragment(videoTag, 'https://vk.com/');

		var isFoundLinks = false;
		[].slice.call(doc.querySelectorAll('source')).forEach(function(sourceTag) {
			var isMatchedUrl = false;
			var url = sourceTag.src;
			if (!isMatchedUrl) {
		var isInstagram = /^(.*cdninstagram\.com.+mp4)/i.exec(url);
		if (isInstagram) {
		 isMatchedUrl = true;
		 isFoundLinks = true;
		 links.push({
			url: url,
			subname: 'SD',
			name: 'MP4',
			type: 'mp4'
		 });
		}
			}
			if (!isMatchedUrl) {
		var isVkVideo = /\.(\d+)\.mp4(?:$|\?)/.exec(url);
		if (isVkVideo) {
		 isMatchedUrl = true;
		 isFoundLinks = true;
		 var vkUrl = url;
		 var quality = isVkVideo[1];
		 var pos = vkUrl.indexOf('?');
		 if (pos !== -1) {
			vkUrl = vkUrl.substr(0, pos);
		 }
		 mono.findJsonString(response, /mv_title/).some(function(str) {
			var m = /id="mv_title"[^>]*>([^<]+)/.exec(str);
			if (m) {
				title = mono.fileName.decodeSpecialChars(mono.decodeUnicodeEscapeSequence(m[1]));
				return true;
			}
		 });
		 links.push({
			url: vkUrl,
			subname: quality,
			name: 'MP4',
			type: 'mp4'
		 });
		}
			}
		});
		return isFoundLinks;
	 });
	 if (videoSourceFound) {
		return true;
	 }

	 if (/var\sopts\s*=\s*/.test(str)) {
		m = /url:\s*'(?:[^']+)dailymotion.com\/(?:swf\/)?video\/([\w\d]+)/.exec(str);
		if (m) {
			embed = {
		action: 'getDailymotionLinks',
		extVideoId: m[1]
			};
			return true;
		}
	 }
	});

	return !async && callback(vid, links, title, duration, thumb, data, embed);
		});
	},

	checkVkLinks: function(links, callback) {
		var checkUrl = '';

		if (links && links.length > 0) {
	if (links[0].type == 'mp4')
	 checkUrl = links[0].url;
	else if (links.length > 1)
	 checkUrl = links[1].url;
	else
	 checkUrl = links[0].url;
		}

		if (checkUrl) {
	mono.request({
	 url: checkUrl,
	 type: 'HEAD'
	}, function(err, resp) {
	 callback(checkUrl, !err);
	});
	return;
		}

		callback();
	},

	getVkLinksFromJsonMsg: function(msg, response) {
		var info = this.getVkLinksFromJson(msg.json);
		return response(info);
	},

	getVkLinksFromJson: function(json) {
		var links = [];
		var vid = json.vid;
		var title = json.md_title || json.vid;
		var thumb = '';

		if (json.thumb) {
	thumb = json.thumb;
		} else
		if (json.jpg) {
	thumb = json.jpg;
		}

		var flvRe = /\.flv(\?|$)]/;
		var videoUrlRegExp = /url([0-9]+)/;
		Object.keys(json).forEach(function(key) {
	var url = '';
	var type = 'mp4';
	var quality = null;
	if (key === 'extra_data' && json.extra === "99") {
	 url = '';
	 if (json.live_mp4) {
		url = json.live_mp4;
	 } else
	 if (json.postlive_mp4) {
		url = json.postlive_mp4;
	 }
	 if (url) {
		quality = json.hd ? 'HD' : 'SD';
		links.push({
			url: url,
			subname: quality,
			name: type.toUpperCase(),
			type: type
		});
	 }
	} else
	if (key === 'extra_data' && json.extra === "52") {
	 quality = json.hd ? 'HD' : 'SD';
	 url = json.extra_data;
	 if (flvRe.test(url)) {
		type = 'flv';
	 }
	 links.push({
		url: url,
		subname: quality,
		name: type.toUpperCase(),
		type: type
	 });
	} else {
	 quality = key.match(videoUrlRegExp);
	 quality = quality && quality[1];
	 if (quality !== null) {
		url = json[key];
		if (flvRe.test(url)) {
			type = 'flv';
		}

		links.push({
			url: url,
			subname: quality,
			name: type.toUpperCase(),
			type: type
		});
	 }
	}
		});

		var duration = json.duration;

		return {
	action: 'getVKLinks',
	extVideoId: vid,
	links: links,
	title: title,
	duration: duration,
	thumb: thumb,
	data: json,
	checkLinks: null
		};
	},

	getVkLinksFromData: function(request, cb) {
		var response = request.data;

		var varsJson = null;
		mono.findJson(response, [/"vid":/, /"oid":/, /"md_title":/]).some(function(obj) {
	var obj = obj.player && obj.player.params && obj.player.params[0];
	if (obj) {
	 varsJson = obj;
	 return true;
	}
		});

		if (!varsJson) {
	return cb();
		}

		var info = vkontakte_ru_embed.getVkLinksFromJson(varsJson);

		return cb(info);
	}
 };

 engine.modules.vkontakte = vkontakte_ru_embed;
 var youtube_com_embed = {
	/**
	 * @private
	 */
	lastSts: ["17655", [
		["reverse", null],
		["swap", 51],
		["reverse", null],
		["swap", 24],
		["splice", 3],
		["swap", 70],
		["reverse", null]
	]],
	_stsIsLoaded: false,
	_lastSignature: null,
	_dashMpdSigR: /(\/s\/([^\/]+))/,
	getYoutubeLinks: function(request, callback) {
		function callback_links(links, title, subtitles, duration) {
	youtube_com_embed.addUmmyLinks(links, request.extVideoId);

	var response = {
	 action: request.action,
	 extVideoId: request.extVideoId,
	 links: links,
	 title: title,
	 subtitles: subtitles,
	 duration: duration,
	 checkLinks: null
	};

	if (request.checkLinks && links) {
	 return youtube_com_embed.checkYoutubeLinks(links, function(checkUrl, isValid) {
		response.checkLinks = isValid;
		return callback(response);
	 });
	}

	return callback(response);
		}

		youtube_com_embed._prepareLastSts().then(function() {
	return youtube_com_embed._getYoutubeLinks(request.url, request.extVideoId, request.checkSubtitles, request.noDash).then(function(result) {
	 callback_links(result.links, result.title, result.subtitles, result.duration);
	}, function(err) {
	 mono.error('_getYoutubeLinks error', err);
	 callback_links(null, '', null, '');
	});
		});
		return true;
	},
	_getYoutubeLinks: function(eurl, id, checkSubtitles, noDash) {
		var self = this;

		if (!eurl) {
	eurl = 'https://www.youtube.com/watch?v=' + id;
		}

		return self._getVideoInfo(id, eurl).then(function(result) {
	var config = result.config;
	var signature = result.signature;
	return self.onGetConfig(id, checkSubtitles, noDash, config, signature);
		});
	},
	_getVideoInfo: function(id, eurl) {
		var self = this;
		var Promise = mono.Promise;
		var rp = self._rp;
		var getVideoInfo = function(domain, eurl, id, el, signature) {
	var headers = {};
	if (mono.isChromeMobile || mono.isFirefoxMobile) {
	 headers['User-Agent'] = mono.getDesktopUa();
	}
	return rp({
	 url: 'https://' + domain + '/get_video_info?' + mono.param({
		video_id: id,
		eurl: eurl,
		el: el,
		sts: signature.sts
	 }),
	 headers: headers
	}).then(function(config) {
	 return self._dataStrToObj(config);
	}).then(function(config) {
	 if (parseInt(config.errorcode, 10) === 2) {
		var err = new Error('Invalid parameters');
		err.code = 'INVALID_REQUEST';
		throw err;
	 }
	 if (parseInt(config.errorcode, 10) === 150) {
		var err = new Error('Video is unavailable');
		err.code = 'UNAVAILABLE';
		throw err;
	 }
	 if (!config.video_id) {
		throw new Error('Config videoId is empty!');
	 }

	 return {
		signature: signature,
		config: config
	 };
	});
		};
		var getAsJsonPage = function(id) {
	var headers = {};
	if (mono.isChromeMobile || mono.isFirefoxMobile) {
	 headers['User-Agent'] = mono.getDesktopUa();
	}
	return rp({
	 url: 'https://www.youtube.com/watch?' + mono.param({
		v: id,
		spf: 'navigate'
	 }),
	 headers: headers
	}).then(function(response) {
	 var json = JSON.parse(response);
	 var jsonList = null;
	 json.some(function(item) {
		if (item.data && item.data.swfcfg) {
			jsonList = item.data.swfcfg;
			return true;
		}
	 });
	 if (!jsonList) {
		throw new Error('swfcfg is not found');
	 }

	 if (!jsonList.args || typeof jsonList.args !== 'object') {
		throw new Error('jsonList args is not found!');
	 }

	 var config = self._decodeParams(jsonList.args);
	 if (!config.video_id) {
		throw new Error('Config videoId is empty!');
	 }

	 var sts = parseInt(jsonList.sts);
	 var playerUrl = jsonList.assets && jsonList.assets.js;

	 return getSignatureByConfig(config, playerUrl, sts).then(function(signature) {
		return {
			config: config,
			signature: signature
		}
	 });
	});
		};
		var getAsVideoPage = function(id) {
	var headers = {};
	if (mono.isChromeMobile || mono.isFirefoxMobile) {
	 headers['User-Agent'] = mono.getDesktopUa();
	}
	return rp({
	 url: 'https://www.youtube.com/watch?' + mono.param({
		v: id
	 }),
	 headers: headers
	}).then(function(response) {
	 var script = mono.getPageScript(response, /ytplayer\.config\s+=\s+/);
	 if (!script.length) {
		throw new Error('Video page script is not found!');
	 }
	 script = script[0];

	 var jsonList = mono.findJson(script, [/"video_id":/])[0];
	 if (!jsonList) {
		throw new Error('Video page jsonList is not found!');
	 }

	 if (!jsonList.args || typeof jsonList.args !== 'object') {
		throw new Error('jsonList args is not found!');
	 }

	 var config = self._decodeParams(jsonList.args);
	 if (!config.video_id) {
		throw new Error('Config videoId is empty!');
	 }

	 var sts = parseInt(jsonList.sts);
	 var playerUrl = jsonList.assets && jsonList.assets.js;

	 return getSignatureByConfig(config, playerUrl, sts).then(function(signature) {
		return {
			config: config,
			signature: signature
		}
	 });
	});
		};
		var getSignatureByConfig = function(config, playerUrl, sts) {
	var promise = Promise.resolve(null);

	if (self._getChipedItem(config)) {
	 promise = promise.then(function() {
		if (!playerUrl) {
			throw new Error('Player url is not found!');
		}
		return self._ytHtml5SigDecipher.dechip(playerUrl, sts);
	 });
	}

	return promise;
		};

		return self._getSignature().then(function(signature) {
	return getVideoInfo('www.youtube-nocookie.com', eurl, id, 'detailpage', signature).then(function(result) {
	 var config = result.config;
	 if (config.requires_purchase === '1' ||
		config.url_encoded_fmt_stream_map === '' ||
		config.fmt_url_map === '' ||
		config.adaptive_fmts === '' ||
		config.errorcode > 0) {
		throw new Error('Some config keys is bad');
	 }
	 return result;
	}, function(err) {
	 mono.debug('try 1', err);
	 throw err;
	}).catch(function() {
	 return getVideoInfo('www.youtube.com', eurl, id, 'detailpage', signature).catch(function(err) {
		mono.debug('try 2', err);
		throw err;
	 });
	}).catch(function() {
	 return getVideoInfo('www.youtube-nocookie.com', eurl, id, 'embedded', signature).then(function(result) {
		var config = result.config;
		if (config.requires_purchase === '1' ||
			config.url_encoded_fmt_stream_map === '' ||
			config.fmt_url_map === '' ||
			config.adaptive_fmts === '' ||
			config.errorcode > 0) {
			throw new Error('Some config keys is bad');
		}
		return result;
	 }, function(err) {
		mono.debug('try 3', err);
		throw err;
	 });
	}).catch(function() {
	 return getVideoInfo('www.youtube.com', eurl, id, 'embedded', signature).catch(function(err) {
		mono.debug('try 4', err);
		throw err;
	 });
	});
		}).catch(function(err) {
	mono.debug('_getSignature error', err);
	throw err;
		}).catch(function() {
	return getAsJsonPage(id).catch(function(err) {
	 mono.debug('try 5', err);
	 throw err;
	});
		}).catch(function() {
	return getAsVideoPage(id).catch(function(err) {
	 mono.debug('try 6', err);
	 throw err;
	});
		}).catch(function() {
	throw new Error('Get config error');
		}).then(function(result) {
	var config = result.config;
	var signature = result.signature;
	return self._testSignature(config, signature).then(function() {
	 return {
		config: config,
		signature: signature
	 };
	});
		});
	},
	_getSignature: function() {
		var self = this;
		var Promise = mono.Promise;
		var rp = self._rp;

		if (self._lastSignature && self._lastSignature.expire > parseInt(Date.now() / 1000)) {
	return Promise.resolve(self._lastSignature);
		}

		var headers = {};
		if (mono.isChromeMobile || mono.isFirefoxMobile) {
	headers['User-Agent'] = mono.getDesktopUa();
		}

		return rp({
	url: 'https://www.youtube.com/',
	headers: headers
		}).then(function(html) {
	var playerInfo = null;
	try {
	 playerInfo = self.getPlayerStsAndUrlFromHtml(html);
	} catch (err) {
	 playerInfo = self.getPlayerStsAndUrlFromAuthHtml(html);
	}
	return playerInfo;
		}).then(function(playerInfo) {
	if (!playerInfo.sts) {
	 return self._ytHtml5SigDecipher.dechip(playerInfo.playerUrl);
	} else {
	 return playerInfo;
	}
		}).then(function(signature) {
	signature.expire = parseInt(Date.now() / 1000) + 6 * 60 * 60;
	return self._lastSignature = signature;
		});
	},
	getPlayerStsAndUrlFromHtml: function(html) {
		var script = mono.getPageScript(html, /window\.ytplayer\s*=\s*/);
		if (!script.length) {
	throw new Error('Player config is not found');
		}
		script = script[0];

		var jsonList = mono.findJson(script, [/"PLAYER_JS_URL":/]);
		if (!jsonList.length) {
	throw new Error('Player url is not found!');
		}
		jsonList = jsonList[0];

		var playerUrl = jsonList.PLAYER_JS_URL;
		if (!playerUrl) {
	throw new Error('playerUrl is not found');
		}

		return {
	sts: null,
	playerUrl: playerUrl
		};
	},
	getPlayerStsAndUrlFromAuthHtml: function(html) {
		var script = mono.getPageScript(html, /ytplayer\.config\s+=\s+/);
		if (!script.length) {
	throw new Error('Player config script is not found');
		}
		script = script[0];

		var jsonList = mono.findJson(script, [/"assets":/, /"sts":\d+/]);
		if (!jsonList.length) {
	throw new Error('Player config json is not found!');
		}
		jsonList = jsonList[0];


		var playerUrl = jsonList.assets && jsonList.assets.js;
		if (!playerUrl) {
	throw new Error('playerUrl is not found');
		}

		var sts = parseInt(jsonList.sts);

		return {
	sts: sts,
	playerUrl: playerUrl
		};
	},
	_dataStrToObj: function(data) {
		return this._decodeParams(mono.parseUrl(data, {
	params: true
		}));
	},
	_decodeParams: function(data) {
		var self = this;
		['url_encoded_fmt_stream_map', 'adaptive_fmts', 'fmt_url_map'].forEach(function(key) {
	if (data[key]) {
	 data[key] = data[key].split(',').map(function(item) {
		return self._dataStrToObj(item);
	 });
	}
		});
		return data;
	},
	_testSignature: function(config, signature) {
		var self = this;
		var Promise = mono.Promise;
		var rp = self._rp;

		var chipedItem = self._getChipedItem(config);

		if (!chipedItem) {
	return Promise.resolve();
		}

		return Promise.resolve().then(function() {
	if (!signature.actionList) {
	 return self._ytHtml5SigDecipher.dechip(signature.playerUrl, signature.sts).then(function(sig) {
		signature.sts = sig.sts;
		signature.actionList = sig.actionList;
		return signature;
	 });
	} else {
	 return signature;
	}
		}).then(function(signature) {
	var url = null;
	var decodedSignature = self._ytHtml5SigDecipher.applyActions(signature.actionList, chipedItem.s);
	if (chipedItem.getUrl) {
	 url = chipedItem.getUrl(decodedSignature);
	} else {
	 url = chipedItem.url + '&signature=' + decodedSignature;
	}

	return rp({
	 method: 'HEAD',
	 url: url
	}).then(function() {
	 return self._ytHtml5SigDecipher.saveSignature(signature);
	}, function(err) {
	 if (/403/.test(err.message)) {
		throw err;
	 }
	});
		});
	},
	_getChipedItem: function(config) {
		var self = this;
		var chipedItem = null;

		['url_encoded_fmt_stream_map', 'adaptive_fmts', 'fmt_url_map'].some(function(key) {
	var item = config[key];
	if (!item) {
	 return false;
	}

	return item.some(function(subItem) {
	 if (subItem.s && subItem.url) {
		chipedItem = subItem;
		return true;
	 }
	});
		});

		var dashUrl = config.dashmpd;
		if (!chipedItem && dashUrl) {
	var s = self._dashMpdSigR.exec(dashUrl);
	if (s) {
	 chipedItem = {
		s: s[2],
		url: dashUrl.replace(s[1], ''),
		getUrl: function(url, s, signature) {
			return url.replace(s[1], '/signature/' + signature);
		}.bind(null, dashUrl, s)
	 };
	}
		}

		return chipedItem;
	},

	/**
	 * @private
	 */
	readFmt: function(links, fmt, signature, titleParam) {
		var self = this;
		var meta = links.meta;

		fmt.forEach(function(item) {
	if (item.stream) {
	 meta.hasStream = 1;
	 return;
	}

	var url = item.url;
	if (!url) return;

	if (!/([?&])s(ig(nature)?)?=/i.test(url)) {
	 if (item.sig) {
		url += '&signature=' + item.sig;
	 } else
	 if (item.signature) {
		url += '&signature=' + item.signature;
	 } else
	 if (item.s) {
		url += '&signature=' + self._ytHtml5SigDecipher.applyActions(signature.actionList, item.s);
	 }
	}
	url = url.replace(/([?&])sig=/i, '$1signature=').replace(/\\u0026/ig, '&');

	var itag = item.itag;
	if (!itag) {
	 var m = /(?:[?&])itag=(\d+)/i.exec(url);
	 if (m) {
		itag = m[1];
	 }
	}
	if (!itag) return;

	if (!/[?&]itag=/i.test(url)) {
	 url += '&itag=' + itag;
	}

	url = self._addRateByPass(url);

	var metaItem = meta[itag];
	if (!metaItem) {
	 metaItem = meta[itag] = {};
	}

	if (item.fps) {
	 metaItem.fps = item.fps;
	}

	if (item.size && /^\d+x\d+$/.test(item.size)) {
	 var wh = item.size.split('x');
	 metaItem.quality = youtube_com_embed.getDashQuality(wh[0], wh[1]);
	}

	if (item.bitrate) {
	 metaItem.bitrate = parseInt(item.bitrate);
	}

	if (item.type) {
	 metaItem.type = item.type;
	 var codecs = item.type.match(/codecs="([^"]+)"/);
	 if (codecs) {
		metaItem.codecs = codecs[1];
	 }
	}

	if (item.projection_type) {
	 metaItem.projectionType = parseInt(item.projection_type);
	}

	if (!links[itag]) {
	 links[itag] = url + titleParam;
	}
		});
	},

	/**
	 * @private
	 */
	onGetConfig: function(videoId, checkSubtitles, noDash, config, signature) {
		"use strict";
		var self = this;
		var Promise = mono.Promise;

		var links = {
	 meta: {}
	},
	title = '',
	subtitles = null,
	duration = '',
	dashUrl = null;

		return Promise.resolve().then(function() {
	duration = config.length_seconds || '';

	title = config.title || '';
	var titleParam = '';
	if (title) {
	 title = title.replace(/\+/g, ' ');
	 titleParam = '&title=' + encodeURIComponent(mono.fileName.modify(title));
	}

	var fmtMap = config.fmt_url_map || config.url_encoded_fmt_stream_map || [];
	var adaptiveFmts = config.adaptive_fmts || [];

	if (config.livestream || config.live_playback) {
	 links.meta.hasStream = 1;
	}

	fmtMap && self.readFmt(links, fmtMap, signature, titleParam);
	adaptiveFmts && self.readFmt(links, adaptiveFmts, signature, titleParam);

	dashUrl = config.dashmpd || '';
	if (dashUrl && dashUrl.indexOf('yt_live_broadcast') !== -1) {
	 dashUrl = null;
	}
		}).then(function() {
	var promise = Promise.resolve();

	if (checkSubtitles) {
	 promise = promise.then(function() {
		return new Promise(function(resolve) {
			self.getYoutubeSubtitles({
		extVideoId: videoId
			}, function(subs) {
		subtitles = subs || null;
		resolve();
			});
		}).catch(function(err) {
			mono.error('Get subtitles error', err);
		});
	 });
	}

	if (!noDash && dashUrl) {
	 promise = promise.then(function() {
		var dechipedDashUrl = dashUrl;

		var s = self._dashMpdSigR.exec(dashUrl);
		if (s) {
			var sig = self._ytHtml5SigDecipher.applyActions(signature.actionList, s[2]);
			dechipedDashUrl = dashUrl.replace(s[1], '/signature/' + sig);
		}

		dechipedDashUrl = dechipedDashUrl.replace('/sig/', '/signature/');

		return self.getYouTubeDashLinks(links, dechipedDashUrl).catch(function(err) {
			mono.error('Get dash error', err);
		});
	 });
	}

	return promise;
		}).then(function() {
	var len = Object.keys(links).length;
	if (links.meta && !links.meta.hasStream) {
	 len--;
	}
	if (!len) {
	 links = null;
	}

	return {
	 links: links,
	 title: title,
	 subtitles: subtitles,
	 duration: duration
	};
		});
	},

	/**
	 * @private
	 */
	addUmmyLinks: function(links, videoId) {
		if (!links || (links.meta && links.meta.hasStream)) {
	return;
		}

		if (engine.preferences.showUmmyItem) {
	var meta = links.meta;
	if (!meta) {
	 meta = links.meta = {};
	}
	meta.videoId = videoId;

	links['ummy'] = 'ummy://www.youtube.com/watch?v=' + videoId;
	links['ummyAudio'] = 'ummy://www.youtube.com/watch?v=' + videoId + '&sf_type=audio';
		}
	},

	/**
	 * @private
	 */
	checkYoutubeLinks: function(links, callback) {
		var checkItags = ['18', '34', '35'],
	checkUrl = '';
		for (var i = 0; i < checkItags.length; i++) {
	if (links[checkItags[i]]) {
	 checkUrl = links[checkItags[i]];
	 break;
	}
		}

		if (checkUrl) {
	mono.request({
	 type: 'HEAD',
	 url: checkUrl
	}, function(err, resp) {
	 callback(checkUrl, !err);
	});
		} else {
	callback();
		}
	},

	/**
	 * @private
	 */
	convertVtt2Srt: function(item, cb) {
		"use strict";
		mono.request({
	url: item.url
		}, function(err, resp, body) {
	if (err || !body) {
	 console.error('Request error!', err);
	 return cb();
	}

	var re = /(\d{2}:\d{2}:\d{2})\.(\d{3})/g;
	var validateRe = /^\d{2}:\d{2}:\d{2}\.\d{3}/;
	var arr = body.split('\n\n');

	if (!validateRe.test(arr[0])) {
	 arr.shift();
	}

	if (!validateRe.test(arr[arr.length - 1])) {
	 arr.pop();
	}

	var hasSkip = false;
	var srt = arr.filter(function(item) {
	 var r = validateRe.test(item);
	 if (!r) {
		hasSkip = true;
	 }
	 return r;
	}).map(function(item, index) {
	 item = item.replace(re, "$1,$2");
	 return (index + 1) + '\n' + item;
	});
	srt = srt.join('\n\n');

	if (hasSkip) {
	 return cb();
	}

	item.srt = srt;
	item.preprocess = 'srt2url';
	cb();
		});
	},

	/**
	 * @private
	 */
	getYoutubeSubtitles: function(message, cb) {
		var _this = this;
		var videoId = message.extVideoId;
		var baseUrl = 'http://video.google.com/timedtext';
		mono.request({
	url: baseUrl + '?hl=' + engine.language.lang + '&v=' + videoId + '&type=list&tlangs=1',
	xml: true
		}, function(err, resp, xml) {
	if (err || !xml) {
	 return cb();
	}

	var track = xml.querySelectorAll('track');
	var target = xml.querySelectorAll('target');
	var list = [];
	var trackList = {};
	var targetList = {};
	var origTrack = undefined;
	var langCode, param;
	for (var i = 0, item; item = track[i]; i++) {
	 langCode = item.getAttribute('lang_code');
	 param = {
		lang: langCode,
		v: videoId,
		fmt: 'vtt',
		name: item.getAttribute('name') || undefined
	 };
	 trackList[langCode] = {
		lang: item.getAttribute('lang_translated'),
		langCode: langCode,
		url: baseUrl + '?' + mono.param(param),
		name: param.name
	 };
	 list.push(trackList[langCode]);
	 if (!origTrack && item.getAttribute('cantran')) {
		origTrack = param;
	 }
	}

	if (origTrack) {
	 for (i = 0, item; item = target[i]; i++) {
		langCode = item.getAttribute('lang_code');
		param = {
			lang: origTrack.lang,
			v: videoId,
			tlang: langCode,
			fmt: 'vtt',
			name: origTrack.name
		};
		targetList[langCode] = {
			lang: item.getAttribute('lang_translated'),
			langCode: langCode,
			url: baseUrl + '?' + mono.param(param),
			isAuto: true
		};
	 }
	}

	langCode = mono.getNavigator().language.toLowerCase();
	if (langCode.indexOf('zh-hant') === 0) {
	 langCode = 'zh-Hant';
	} else
	if (langCode.indexOf('zh-hans') === 0) {
	 langCode = 'zh-Hans';
	}
	var localeList = [langCode];
	if (localeList[0] === 'uk') {
	 localeList.push('ru');
	}
	for (var i = 0, item; item = localeList[i]; i++) {
	 if (!trackList[item] && targetList[item]) {
		list.push(targetList[item]);
	 }
	}

	var waitCount = 0;
	var readyCount = 0;
	var onReady = function() {
	 readyCount++;
	 if (waitCount !== readyCount) {
		return;
	 }
	 return cb(list);
	};
	waitCount++;
	list.forEach(function(item) {
	 waitCount++;
	 _this.convertVtt2Srt(item, onReady);
	});
	onReady();
		});
	},

	/**
	 * @private
	 */
	getYouTubeDashLinks: function(links, dashUrl) {
		var self = this;
		var rp = self._rp;

		var headers = {};
		if (mono.isChromeMobile || mono.isFirefoxMobile) {
	headers['User-Agent'] = mono.getDesktopUa();
		}

		return rp({
	url: dashUrl,
	headers: headers,
	xml: true
		}).then(function(xml) {
	self.parseDash(xml, links);
		});
	},

	/**
	 * @private
	 */
	getDashQuality: function(a, b) {
		var qualityList = {
	144: 144,
	240: 240,
	360: 360,
	480: 480,
	720: 720,
	1080: 1080,
	1440: 1440,
	'4K': 2160,
	'5K': 2880,
	'8K': 4320
		};

		var quality;
		var g = Math.max(a, b);
		a = Math.min(a, b);
		for (var qualityName in qualityList) {
	var value = qualityList[qualityName];
	if (g >= Math.floor(16 * value / 9) || a >= value) {
	 quality = qualityName;
	} else {
	 return quality;
	}
		}
		return quality;
	},

	/**
	 * @private
	 */
	parseDash: function(xml, links) {
		var self = this;
		var elList = xml.querySelectorAll('Representation');

		var meta = links.meta = links.meta || {};

		for (var i = 0, el; el = elList[i]; i++) {
	var baseUrlNode = el.querySelector('BaseURL');
	var url = baseUrlNode.textContent;
	if (!url) {
	 continue;
	}

	var segmentUrlNode = baseUrlNode.parentNode.querySelector('SegmentURL');
	var segmentUrl = segmentUrlNode && segmentUrlNode.getAttribute('media');
	if (segmentUrl && segmentUrl.indexOf('sq/') === 0) {
	 continue;
	}

	var itag = el.getAttribute('id');
	var metaItem = meta[itag];
	if (!metaItem) {
	 metaItem = meta[itag] = {};
	}

	url = self._addRateByPass(url);

	var frameRate = el.getAttribute('frameRate');
	if (frameRate) {
	 metaItem.fps = frameRate;
	}

	var width = el.getAttribute('width');
	var height = el.getAttribute('height');

	if (width && height) {
	 metaItem.quality = youtube_com_embed.getDashQuality(width, height);
	}

	var codecs = el.getAttribute('codecs');
	if (codecs) {
	 metaItem.codecs = codecs;
	 var type = url.match(/mime=([^&]+)/);
	 type = type && type[1];
	 if (type) {
		metaItem.type = type;
	 }
	}

	if (!links[itag]) {
	 links[itag] = url;
	}
		}
	},

	getYoutubeIdListFromPlaylist: function(request, cb) {
		youtube_com_embed.getIdListFromList(request.baseUrl || 'https://www.youtube.com', request.listId, cb);
		return true;
	},

	/**
	 * @private
	 */
	getIdListFromList: (function() {
		var getNextPage = function(baseUrl, url, pageList, cb) {
	if (!pageList) {
	 pageList = [];
	}
	mono.request({
	 url: baseUrl + url,
	 json: true
	}, function(err, resp, data) {
	 if (err || !data) {
		return cb(pageList);
	 }

	 pageList.push(data.content_html);
	 var nextPageUrl = getNextPageUrl(data.load_more_widget_html);
	 if (nextPageUrl === undefined) {
		return cb(pageList);
	 }
	 getNextPage(baseUrl, nextPageUrl, pageList, cb);
	});
		};
		var getTitleFromPage = function(data) {
	var title = data.match(/<h1[^>]+>([^<]+)<\/h1>/);
	if (!title) {
	 return undefined;
	}
	return title[1].replace(/\r?\n/g, " ").trim();
		};
		var getNextPageUrl = function(data) {
	if (!data) {
	 return undefined;
	}
	var nextUrl = data.match(/data-uix-load-more-href="([^"]+)"/);
	if (nextUrl) {
	 nextUrl = nextUrl[1];
	}
	return nextUrl || undefined;
		};
		var readLinksFromPages = function(listId, pageList, cb) {
	var title = getTitleFromPage(pageList[0]);
	var idObj = {};
	var idList = [];
	var pattern = /href="\/watch\?([^"]+)"/g;
	var maxIndex = 0;
	for (var i = 0, len = pageList.length; i < len; i++) {
	 var content = pageList[i];
	 content.replace(pattern, function(string, args) {
		var url = mono.parseUrl(args, {
			params: true,
			sep: '&amp;'
		});
		if (url.list !== listId) {
			return;
		}
		url.index = parseInt(url.index);
		idObj[url.index] = url.v;
		if (url.index > maxIndex) {
			maxIndex = url.index;
		}
	 });
	}
	for (i = 0; i <= maxIndex; i++) {
	 if (idObj[i] === undefined) {
		continue;
	 }
	 if (idList.indexOf(idObj[i]) === -1) {
		idList.push(idObj[i]);
	 }
	}
	cb({
	 idList: idList,
	 title: title
	});
		};
		var getNewClientHeaders = function(data) {
	var headers = {};
	var params = null;
	var m = /ytcfg\.set\(({.+)/.exec(data);
	if (m) {
	 mono.findJsObject(m[1]).some(function(obj) {
		if (obj.INNERTUBE_CONTEXT_CLIENT_NAME) {
			params = obj;
			return true;
		}
	 });
	}
	if (params) {
	 headers['x-youtube-client-name'] = params.INNERTUBE_CONTEXT_CLIENT_NAME;
	 headers['x-youtube-client-version'] = params.INNERTUBE_CONTEXT_CLIENT_VERSION;
	 if (params.ID_TOKEN) {
		headers['x-youtube-identity-token'] = params.ID_TOKEN;
	 }
	 /*headers['x-youtube-page-cl'] = params.PAGE_CL;
	 headers['x-youtube-page-label'] = params.PAGE_BUILD_LABEL;
	 headers['x-youtube-variants-checksum'] = params.VARIANTS_CHECKSUM;*/
	}
	return headers;
		};
		var getNewTitle = function(data) {
	var title = 'unknown';
	var params = null;
	var m = data.match(/"playlistSidebarPrimaryInfoRenderer":({.+)/);
	if (m) {
	 mono.findJsObject(m[1]).some(function(obj) {
		if (obj.title && obj.title.runs) {
			params = obj;
			return true;
		}
	 });
	}
	if (params) {
	 try {
		params.title.runs.some(function(obj) {
			if (obj.text) {
		return title = obj.text;
			}
		});
	 } catch (err) {
		mono.debug('getNewTitle error', err);
	 }
	}
	return title;
		};
		var getNewNextPageUrl = function(data) {
	var path = null;
	var m = data.match(/"nextContinuationData":({[^}]+})/);
	if (m) {
	 try {
		var nextContinuationData = JSON.parse(m[1]);
		path = '/browse_ajax?' + mono.param({
			ctoken: nextContinuationData.continuation,
			itct: nextContinuationData.clickTrackingParams
		});
	 } catch (e) {
		mono.debug('getNewNextPageUrl error', e);
	 }
	}
	return path;
		};
		var getNewVideoList = function(data, idList) {
	var onGetContents = function(contents) {
	 if (Array.isArray(contents)) {
		contents.forEach(function(item) {
			var playlistVideoRenderer = item.playlistVideoRenderer;
			var videoId = playlistVideoRenderer && playlistVideoRenderer.videoId;
			if (videoId) {
		idList.push(videoId);
			}
		});
	 }
	};

	var pos = data.indexOf('{"playlistVideoListRenderer":{');
	if (pos !== -1) {
	 data = data.substr(pos);
	 mono.findJson(data).forEach(function(obj) {
		var playlistVideoListRenderer = obj.playlistVideoListRenderer;
		var contents = playlistVideoListRenderer && playlistVideoListRenderer.contents;
		onGetContents(contents);
	 });
	} else {
	 pos = data.indexOf('{"playlistVideoListContinuation":{');
	 if (pos !== -1) {
		data = data.substr(pos);
		mono.findJson(data).forEach(function(obj) {
			var playlistVideoListContinuation = obj.playlistVideoListContinuation;
			var contents = playlistVideoListContinuation && playlistVideoListContinuation.contents;
			onGetContents(contents);
		});
	 }
	}
		};
		var getNewNextPage = function(baseUrl, path, clientHeaders, dataList, cb) {
	mono.request({
	 url: baseUrl + path,
	 headers: clientHeaders,
	 json: true
	}, function(err, resp, data) {
	 if (err) {
		mono.error('YT next page request error!', err);
		return cb();
	 }

	 var dataStr = JSON.stringify(data);
	 var nextPageUrl = getNewNextPageUrl(dataStr);
	 getNewVideoList(dataStr, dataList);

	 if (nextPageUrl) {
		getNewNextPage(baseUrl, nextPageUrl, clientHeaders, dataList, cb);
	 } else {
		cb();
	 }
	});
		};
		return function getLinksFromList(baseUrl, listId, cb) {
	mono.request({
	 url: baseUrl + '/playlist?list=' + listId
	}, function(err, resp, data) {
	 if (err) {
		return cb();
	 }

	 var nextPageUrl = null;
	 if (/"playlistVideoListRenderer"/.test(data)) {
		var clientHeaders = getNewClientHeaders(data);
		var title = getNewTitle(data);
		var idList = [];

		getNewVideoList(data, idList);

		nextPageUrl = getNewNextPageUrl(data);
		if (!nextPageUrl) {
			cb({
		idList: idList,
		title: title
			});
		} else {
			getNewNextPage(baseUrl, nextPageUrl, clientHeaders, idList, function() {
		cb({
		 idList: idList,
		 title: title
		});
			});
		}
	 } else {
		nextPageUrl = getNextPageUrl(data);
		if (!nextPageUrl) {
			readLinksFromPages(listId, [data], cb);
		} else {
			getNextPage(baseUrl, nextPageUrl, [data], function(pageList) {
		readLinksFromPages(listId, pageList, cb);
			});
		}
	 }
	});
		};
	})(),

	getYoutubeLinksFromConfig: function(message, response) {
		"use strict";
		var self = this;
		var Promise = mono.Promise;
		var fallback = function(err) {
	mono.debug('Skip getYoutubeLinksFromConfig', err);
	youtube_com_embed.getYoutubeLinks(message, response);
		};

		this._prepareLastSts().then(function() {
	var jsonList = message.config;
	if (!jsonList ||
	 !jsonList.args ||
	 jsonList.args.video_id !== message.extVideoId ||
	 !jsonList.assets ||
	 !jsonList.assets.js ||
	 !(jsonList.args.url_encoded_fmt_stream_map || jsonList.args.adaptive_fmts || jsonList.args.fmt_url_map)
	) {
	 throw new Error('jsonList args is not found!');
	}

	var config = self._decodeParams(jsonList.args, 1);
	var sts = parseInt(jsonList.sts);
	var playerUrl = jsonList.assets && jsonList.assets.js;

	return Promise.resolve().then(function() {
	 if (self._getChipedItem(config)) {
		if (!playerUrl) {
			throw new Error('Player url is not found!');
		}
		return self._ytHtml5SigDecipher.dechip(playerUrl, sts);
	 } else {
		return null;
	 }
	}).then(function(signature) {
	 return self._testSignature(config, signature).then(function() {
		return {
			config: config,
			signature: signature
		};
	 });
	}).then(function(result) {
	 var config = result.config;
	 var signature = result.signature;
	 return self.onGetConfig(
		message.extVideoId,
		message.checkSubtitles,
		message.noDash,
		config,
		signature
	 ).then(function(result) {
		var links = result.links;
		var title = result.title;

		if (links) {
			youtube_com_embed.addUmmyLinks(links, message.extVideoId);
		}

		response({
			links: links,
			title: title,
			isQuick: 1
		});
	 });
	});
		}).catch(fallback);
		return true;
	},
	_addRateByPass: function(url) {
		if (!/ratebypass/.test(url)) {
	if (!/\?/.test(url)) {
	 if (!/\/$/.test(url)) {
		url += '/';
	 }
	 url += 'ratebypass/yes/'
	} else {
	 url += '&ratebypass=yes'
	}
		}
		return url;
	},

	_ytHtml5SigDecipher: {
		applyActions: function(actionList, sig) {
	var actions = {
	 slice: function(a, b) {
		a.slice(b)
	 },
	 splice: function(a, b) {
		a.splice(0, b)
	 },
	 reverse: function(a) {
		a.reverse()
	 },
	 swap: function(a, b) {
		var c = a[0];
		a[0] = a[b % a.length];
		a[b] = c
	 }
	};
	var parts = sig.split("");
	for (var i = 0, item; item = actionList[i]; i++) {
	 actions[item[0]](parts, item[1]);
	}
	return parts.join("");
		},
		readObfFunc: function(func, data) {
	var vList = func.match(/\[(\w+)\]/g);
	if (!vList) {
	 return;
	}
	for (var i = 0, v; v = vList[i]; i++) {
	 var vv = data.match(new RegExp('[, ]{1}' + v.slice(1, -1) + '="(\\w+)"'));
	 if (vv) {
		func = func.replace(v, '.' + vv[1]);
	 }
	}
	var arr = func.split(';');
	var actList = [];
	for (var i = 0, item; item = arr[i]; i++) {
	 if (item.indexOf('.split(') !== -1 || item.indexOf('.join(') !== -1) {
		continue;
	 }
	 if (item.indexOf('reverse') !== -1) {
		actList.push(['reverse', null]);
		continue;
	 }
	 var m = item.match(/splice\((\d+)\)/);
	 if (m) {
		m = parseInt(m[1]);
		if (isNaN(m)) return;
		actList.push(['splice', m]);
		continue;
	 }
	 var m = item.match(/slice\((\d+)\)/);
	 if (m) {
		m = parseInt(m[1]);
		if (isNaN(m)) return;
		actList.push(['slice', m]);
		continue;
	 }
	 var m = item.match(/\[(\d+)%\w+\.length/);
	 if (m) {
		m = parseInt(m[1]);
		if (isNaN(m)) return;
		actList.push(['swap', m]);
	 }
	}
	return actList;
		},
		getNewChip: function(data) {
	var getObjPropFn = function(objectName, propName) {
	 objectName = objectName.replace(/\$/g, '\\$');
	 var placeRe = new RegExp('(?:var |,)?' + objectName + '={');
	 var placePos = data.search(placeRe);
	 if (placePos === -1) {
		throw new Error('Place is not found');
	 }

	 var place = data.substr(placePos, 300);
	 propName = propName.replace(/\$/g, '\\$');
	 var re = new RegExp(propName + ':function\\(([$\\w,]+)\\){([^}]+)}');
	 var m = place.match(re);
	 if (!m) {
		throw new Error('Place function is not found!');
	 }

	 var args = m[1];
	 var statement = m[2];
	 return {
		args: args,
		statement: statement
	 };
	};
	var readAction = function(item) {
	 var m = /([\w$]+)(?:\.([\w$]+)|\[("[\w$]+")\])\([\w$]+,?([\w$]+)?\)/.exec(item);
	 if (!m) {
		throw new Error('readAction');
	 }

	 var objectName = m[1];
	 var propName = m[2] || m[3];
	 var arg = m[4];
	 var fn = getObjPropFn(objectName, propName);
	 if (/\.reverse/.test(fn.statement)) {
		return ['reverse', null];
	 } else {
		if (!/^[\d]+$/.test(arg)) {
			throw new Error('Arg is not number');
		}

		if (/\.splice/.test(fn.statement)) {
			return ['splice', parseInt(arg)];
		} else if (/\.slice/.test(fn.statement)) {
			return ['slice', parseInt(arg)];
		} else {
			return ['swap', parseInt(arg)];
		}
	 }
	};
	var readStatement = function(arg, statement) {
	 arg = arg.replace(/\$/g, '\\$');
	 var re = new RegExp('[\\w$]+(?:\\.[\\w$]+|\\["[\\w$]+"\\])\\(' + arg + '[^)]*\\)', 'g');
	 var actionList = statement.match(re);
	 if (!actionList) {
		throw new Error('readScope');
	 }

	 return actionList.map(function(item) {
		return readAction(item);
	 });
	};
	var findDecodeFn = function(name) {
	 name = name.replace(/\$/g, '\\$');
	 var re = new RegExp('(?:function ' + name + '|(?:var |,|;\n)' + name + '=function)\\(([\\w$]+)\\){([^}]*)}[;,]');
	 var m = re.exec(data);
	 if (!m) {
		throw new Error('findConvertFn');
	 }

	 var variable = m[1];
	 var statement = m[2];
	 return readStatement(variable, statement);
	};

	var stsM = /,sts:(\d+)/.exec(data);
	if (!stsM) {
	 throw new Error('Sts is not found');
	}
	var sts = parseInt(stsM[1]);

	var fnName = /[$_a-zA-Z0-9]+\.set\((?:[$_a-zA-Z0-9]+\.[$_a-zA-Z0-9]+\|\|)?"signature",([$_a-zA-Z0-9]+)\(/.exec(data);
	if (fnName) {
	 mono.debug('new chip');
	} else {
	 fnName = /(?:function ([$_a-zA-Z0-9]+)|(?:var |,|;\n)([$_a-zA-Z0-9]+)=function)\(([\w$]+)\){\3=\3\.split\([^}]+;return \3\.join\([^}]+}[;,]/.exec(data);
	 if (fnName) {
		mono.debug('alt chip');
		fnName = [fnName[0], fnName[1] || fnName[2]];
	 }
	}
	if (!fnName) {
	 throw new Error('Decode function name is not found!');
	}

	var actionList = findDecodeFn(fnName[1]);

	if (!actionList.length) {
	 throw new Error('actionList is empty');
	}

	return {
	 actionList: actionList,
	 sts: sts
	};
		},
		getChip: function(data) {
	var sts = data.match(/,sts:(\d+)/);
	sts = sts && sts[1];

	var actList = [];
	var funcName = data.match(/\.sig\|\|([$_a-zA-Z0-9]+)\(/);
	if (!funcName) {
	 return this.getNewChip(data);
	} else {
	 mono.debug('old chip');
	}
	funcName = funcName[1];
	funcName = funcName.replace(/\$/g, '\\$');
	var func = data.match(new RegExp("((?:function " + funcName + "|(?:var |,|;\n)" + funcName + "=function)\\(([\\w$]+)\\){[^}]*})[;,]"));
	if (!func) {
	 throw new Error('Func is not found!');
	}
	var vName = func[2];
	func = func[1];
	var regexp = new RegExp("[\\w$]+\\.[\\w$]+\\(" + vName + "[^)]*\\)", 'g');
	var sFuncList = func.match(regexp);
	if (!sFuncList) {
	 actList = this.readObfFunc(func, data);
	 if (actList && actList.length > 0) {
		return {
			actionList: actList,
			sts: sts
		};
	 }
	 throw new Error('readObfFunc actions is not found');
	}
	var objName = '';
	var objElList = [];
	for (var i = 0, item; item = sFuncList[i]; i++) {
	 var m = item.match(/([\w$]+)\.([\w$]+)\([\w$]+,?([\w$]+)?\)/);
	 if (m) {
		objName = m[1];
		objElList.push({
			name: m[2],
			arg: parseInt(m[3])
		});
	 }
	}
	var sPos = data.indexOf('var ' + objName + '={');
	if (sPos === -1) {
	 sPos = data.indexOf(',' + objName + '={');
	}
	if (sPos === -1) {
	 sPos = data.indexOf(objName + '={');
	}
	var place = data.substr(sPos, 300);
	for (i = 0, item; item = objElList[i]; i++) {
	 var vName = item.name;
	 regexp = new RegExp(vName + ":(function\\([$\\w,]+\\){[^}]+})");
	 var sF = place.match(regexp);
	 if (!sF) {
		throw new Error('Match fn error');
	 }
	 sF = sF[1];
	 if (sF.indexOf('splice') !== -1) {
		if (isNaN(item.arg)) {
			throw new Error('Match splice error');
		}
		actList.push(['splice', item.arg]);
	 } else
	 if (sF.indexOf('slice') !== -1) {
		if (isNaN(item.arg)) {
			throw new Error('Match slice error');
		}
		actList.push(['slice', item.arg]);
	 } else
	 if (sF.indexOf('reverse') !== -1) {
		item.arg = null;
		actList.push(['reverse', item.arg]);
	 } else {
		if (isNaN(item.arg)) {
			throw new Error('Match reverse error');
		}
		actList.push(['swap', item.arg]);
	 }
	}
	return {
	 actionList: actList,
	 sts: sts
	};
		},
		getPlayer: function(url) {
	var self = this;

	if (url.substr(0, 2) === '//') {
	 url = 'https:' + url;
	} else
	if (url[0] === '/') {
	 url = 'https://www.youtube.com' + url;
	}

	var headers = {};
	if (mono.isChromeMobile || mono.isFirefoxMobile) {
	 headers['User-Agent'] = mono.getDesktopUa();
	}

	var rp = youtube_com_embed._rp;

	return rp({
	 url: url,
	 headers: headers
	});
		},
		playerUrlSts: {},
		storage: {},
		saveSignature: function(signature) {
	var self = this;
	var Promise = mono.Promise;

	if (!self.playerUrlSts[signature.playerUrl]) {
	 self.playerUrlSts[signature.playerUrl] = signature.sts;
	}

	var existsActionList = self.storage[signature.sts];
	if (!existsActionList || JSON.stringify(existsActionList) !== JSON.stringify(signature.actionList)) {
	 Object.keys(self.storage).sort(function(a, b) {
		return a < b ? 1 : -1
	 }).slice(3).forEach(function(key) {
		delete self.storage[key];
	 });

	 if (signature.sts && signature.actionList) {
		self.storage[signature.sts] = signature.actionList;
	 }

	 return new Promise(function(resolve) {
		mono.storage.set({
			ytDechipList: self.storage
		}, resolve);
	 });
	}
		},
		isTrackError: {},
		/**
		 * @typedef {{}} YtSignature
		 * @property {number} sts
		 * @property {string} playerUrl
		 * @property {string} [actionList]
		 */
		/**
		 * @param {string} playerUrl
		 * @param {number} [sts]
		 * @return {Promise.<YtSignature>}
		 */
		dechip: function(playerUrl, sts) {
	var self = this;
	var Promise = mono.Promise;

	if (!sts) {
	 sts = self.playerUrlSts[playerUrl];
	}

	var cache = self.storage[sts];
	if (cache) {
	 return Promise.resolve({
		sts: sts,
		actionList: cache,
		playerUrl: playerUrl
	 });
	}

	return self.getPlayer(playerUrl).then(function(response) {
	 return self.getChip(response);
	}).then(function(result) {
	 return {
		sts: result.sts,
		actionList: result.actionList,
		playerUrl: playerUrl
	 };
	}).catch(function(err) {
	 var sigUrl = '';
	 var m = /\/(?:[^\/]+)?player-([^\/]+)\//.exec(playerUrl);
	 if (m) {
		sigUrl = m[1];
	 } else {
		sigUrl = 'unknownPlayerName';
	 }
	 if (!self.isTrackError[sigUrl]) {
		self.isTrackError[sigUrl] = 1;
		sigUrl && sts && engine.track({
			t: 'event',
			ec: 'youtube',
			ea: 'pError',
			el: sts + ' ' + sigUrl
		});
	 }
	 throw err;
	});
		}
	},

	/**
	 * @private
	 */
	_prepareLastSts: function() {
		var _this = this;
		var Promise = mono.Promise;
		if (_this._stsIsLoaded) {
	return Promise.resolve();
		}
		_this._stsIsLoaded = true;

		return new Promise(function(resolve) {
	mono.storage.get('ytDechipList', resolve);
		}).then(function(storage) {
	var stsObj = storage.ytDechipList || {};
	_this._ytHtml5SigDecipher.storage = stsObj;

	var inlineSts = _this.lastSts;
	var inlineStsNum = inlineSts[0];
	var inlineStsActions = inlineSts[1];
	stsObj[inlineStsNum] = inlineStsActions;

	var lastStsNum = Object.keys(stsObj).sort(function(a, b) {
	 return a < b ? 1 : -1
	}).shift();
	var lastStsActions = stsObj[lastStsNum];

	if (inlineStsNum < lastStsNum) {
	 _this.lastSts[0] = lastStsNum;
	 _this.lastSts[1] = lastStsActions;
	}
		});
	},
	_rp: function(options) {
		var Promise = mono.Promise;
		return new Promise(function(resolve, reject) {
	mono.request(options, function(err, resp, data) {
	 if (err && typeof err === 'string') {
		err = new Error(err);
	 }
	 err ? reject(err) : resolve(data);
	});
		});
	}
 };

 engine.modules.youtube = youtube_com_embed;
 var facebook_com_embed = {
	getFacebookLinks: function(request, callback) {
		var callback_links = function(links, title, thumb, duration) {
	var response = {
	 action: request.action,
	 extVideoId: request.extVideoId,
	 links: links || null,
	 title: title || '',
	 thumb: thumb || '',
	 duration: duration || ''
	};

	callback(response);
		};

		facebook_com_embed._getFacebookLinks(request.extVideoId, callback_links);
		return true;
	},

	getFacebookLinksFromData: function(request, cb) {
		var data = request.data;
		var videoId = request.extVideoId;

		return facebook_com_embed.getLinksFromData2(data, videoId, true, function(links, title, thumb, duration) {
	var response = {
	 action: 'getFacebookLinksFromData',
	 extVideoId: request.extVideoId,
	 links: links || null,
	 title: title || '',
	 thumb: thumb || '',
	 duration: duration || ''
	};

	cb(response);
		});
	},

	_getFacebookLinks: function(videoId, cb) {
		mono.request({
	type: 'GET',
	url: 'https://www.facebook.com/video.php?v=' + videoId,
	headers: {
	 'Cookie': ''
	}
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	facebook_com_embed.getLinksFromData(data, videoId, cb);
		});
	},
	getLinksFromData: function(data, videoId, cb) {
		"use strict";
		var match = data.match(/\["params","([^"]*)"\]/im);
		var title = '';

		if (!match) {
	return this.getLinksFromData2(data, videoId, false, cb);
		}
		var videoData = null;
		try {
	videoData = JSON.parse(decodeURIComponent(JSON.parse('"' + match[1] + '"'))).video_data;
	if (videoData.progressive) {
	 videoData = videoData.progressive;
	}
	if (!videoData) {
	 return cb();
	}
		} catch (e) {
	return cb();
		}

		var thumb = null;
		var duration = null;

		var links = [];
		var typeMap = {
	sd_src: 'SD',
	hd_src: 'HD'
		};
		if (!Array.isArray(videoData)) {
	videoData = [videoData];
		}
		for (var i = 0, item; item = videoData[i]; i++) {
	['sd_src', 'hd_src'].forEach(function(type) {
	 if (item.thumbnail_src) {
		thumb = item.thumbnail_src;
	 }

	 if (item.video_duration) {
		duration = item.video_duration;
	 }

	 if (!item[type]) {
		return;
	 }

	 var ext = facebook_com_embed.getFileExtension(item[type], 'mp4');
	 links.push({
		url: item[type],
		name: typeMap[type],
		type: ext,
		ext: ext.toUpperCase()
	 });
	});
		}

		cb(links, title, thumb, duration);
	},

	getLinksFromData2: function(data, videoId, noPage, cb) {
		"use strict";
		var title = '';

		var params = null;

		var onScript = function(data) {
	return data.split(/"?videoData"?:\[/).some(function(data) {
	 return mono.findJsObject(data).some(function(_params) {
		if ((_params.sd_src || _params.hd_src) && String(_params.video_id) === String(videoId)) {
			params = _params;
			return true;
		}
	 });
	});
		};

		if (noPage) {
	onScript(data);
		} else {
	mono.getPageScript(data, [/"?videoData"?:\[/]).some(onScript);
		}

		if (!params) {
	return cb();
		}

		var links = [];

		var ext;
		if (params.sd_src) {
	ext = facebook_com_embed.getFileExtension(params.sd_src, 'mp4');
	links.push({
	 url: params.sd_src,
	 name: 'SD',
	 type: ext,
	 ext: ext.toUpperCase()
	});
		}
		if (params.hd_src) {
	ext = facebook_com_embed.getFileExtension(params.hd_src, 'mp4');
	links.push({
	 url: params.hd_src,
	 name: 'HD',
	 type: ext,
	 ext: ext.toUpperCase()
	});
		}

		return cb(links, title, params.thumbnail_src, params.video_duration);
	},

	getFileExtension: function(str, def) {
		var ext = str.match(/\.([a-z0-9]{3,4})(\?|$)/i);
		if (ext) {
	ext = ext[1];
	return ext.toLowerCase();
		}

		return (def ? def : '');
	},

	getFacebookPhotoUrl: function(message, cb) {
		if (!message.fbid) {
	return cb();
		}
		mono.request({
	url: 'https://www.facebook.com/photo.php?fbid=' + message.fbid
		}, function(err, resp, data) {
	if (err || !data) {
	 return cb();
	}

	var m = data.match(/<a[^>]+fbPhotosPhotoActionsItem[^>]+href="([^">]+dl=1)"[^>]+>/i);
	if (m) {
	 var url = m[1].replace(/&amp;/g, '&');
	 return cb([url]);
	}

	var links = [];
	var sizeLinkMap = {};
	var m = data.match(/(<a[^>]+rel="theater"[^>]+>)/ig);
	m && m.forEach(function(linkHtml) {
	 var m = linkHtml.match(/data-pl[os]i="[^"]+"/ig);
	 m && m.forEach(function(item) {
		var pos = item.indexOf('=');
		if (pos !== -1) {
			var key = item.substr(0, pos);
			var value = item.substr(pos + 1);
			value = value.substr(1, value.length - 2).replace(/&amp;/g, '&');
			sizeLinkMap[key] = value;
			links.push(value);
		}
	 });
	});

	if (sizeLinkMap['data-ploi']) {
	 return cb([sizeLinkMap['data-ploi']]);
	}
	return cb(links);
		});
		return true;
	}
 };

 engine.modules.facebook = facebook_com_embed;
 var mail_ru_embed = {
	getMailruLinks: function(request, callback) {
		function callback_links(links, title, thumb, vid, duration) {
	var response = {
	 action: request.action,
	 extVideoId: vid || request.extVideoId,
	 links: links,
	 title: title,
	 thumb: thumb,
	 duration: duration
	};

	callback(response);
		}

		mail_ru_embed._getMailruLinks(request.extVideoId, callback_links);
		return true;
	},
	_getMailruLinks: function(pathname, callback) {
		"use strict";
		var metadataUrl, vPath = pathname;
		var videoId = pathname.match(/\/([^\/]+)\/([^\/]+)\/video\/(.+).html/);
		if (!videoId) {
	videoId = pathname.match(/embed\/([^\/]+)\/([^\/]+)\/(.+).html/);
		}
		if (videoId) {
	metadataUrl = 'http://api.video.mail.ru/videos/' + videoId[1] + '/' + videoId[2] + '/' + videoId[3] + '.json';
	vPath = videoId[1] + '/' + videoId[2] + '/video/' + videoId[3] + '.html';
		}
		if (metadataUrl) {
	return mail_ru_embed.onGetMailruMetadataUrl(metadataUrl, vPath, callback);
		}
		mono.request({
	url: 'http://my.mail.ru/' + pathname
		}, function(err, resp, data) {
	if (err || !data) {
	 return callback();
	}

	var jsonRe = /"metaUrl":/;
	var pageConfig = null;
	mono.getPageScript(data, jsonRe).some(function(script) {
	 return mono.findJson(script, jsonRe).some(function(json) {
		if (json.metaUrl) {
			pageConfig = json;
			return true;
		}
	 });
	});

	if (pageConfig) {
	 metadataUrl = pageConfig.metaUrl;
	 mail_ru_embed.onGetMailruMetadataUrl(metadataUrl, vPath, callback);
	 return;
	}

	data = data.match(/<meta\s+content="[^"]+(videoapi\.my\.mail[^&]+)&[^"]+"[^>]+\/>/);
	if (!data) {
	 return callback();
	}
	data = decodeURIComponent(data[1]);
	var vid = data.substr(data.lastIndexOf('/') + 1);
	metadataUrl = 'http://videoapi.my.mail.ru/videos/' + vid + '.json';
	mail_ru_embed.onGetMailruMetadataUrl(metadataUrl, vPath, callback);
		});
	},
	onGetMailruMetadataUrl: function(metadataUrl, vPath, callback) {
		mail_ru_embed.getMailruMetadata(metadataUrl, function(data) {
	if (!data || typeof data === 'string') {
	 return callback();
	}
	mail_ru_embed.readMailruMetadata(data, function(_links, title, thumb, duration) {
	 callback(mail_ru_embed.prepMailruLinks(_links), title, thumb, vPath, duration);
	});
		});
	},
	prepMailruLinks: function(_links) {
		if (!_links) {
	return;
		}
		var links = [];
		for (var i = 0, link; link = _links[i]; i++) {
	var url = link.url;
	var format = 'FLV';
	if (url.indexOf('.mp4') !== -1) {
	 format = 'MP4';
	}
	if (url.indexOf('.mov') !== -1) {
	 format = 'MOV';
	}
	if (url.indexOf('.mpg') !== -1) {
	 format = 'MPG';
	}
	if (!link.quality) {
	 link.quality = '-?-';
	}
	var quality = link.quality.toUpperCase();

	var qList = ['1080P', '720P', '480P', '360P', '272P'];
	var tList = ['1080', '720', '480', '360', '272'];

	var qPos = qList.indexOf(quality);
	if (qPos !== -1) {
	 quality = tList[qPos];
	}

	var ext = format.toLowerCase();
	links.push({
	 url: url,
	 subname: quality,
	 name: format,
	 ext: ext
	});
		}
		links.sort(function(a, b) {
	if (a.subname === 'HD') {
	 return 1;
	}
	return a.subname > b.subname;
		});
		return links;
	},
	getMailruMetadata: function(url, cb) {
		if (!url) {
	return cb();
		}
		mono.request({
	url: url,
	json: true
		}, function(err, resp, json) {
	if (err || !json) {
	 return cb();
	}

	cb(json);
		});
	},
	readMailruMetadata: function(metadata, cb) {
		var links = [],
	title;
		/**
		 * @namespace metadata.provider Object
		 * @namespace metadata.movie Object
		 * @namespace metadata.videos Object
		 * @namespace metadata.meta Object
		 * @namespace metadata.meta.poster Object
		 */
		var duration = undefined;
		var thumb = undefined;
		if (metadata.meta) {
	thumb = metadata.meta.poster;
	duration = metadata.meta.duration;
		}
		if (metadata.provider === 'UPLOADED') {
	title = metadata.movie ? metadata.movie.title : undefined;
	if (!metadata.videos) {
	 return cb();
	}
	metadata.videos.forEach(function(item) {
	 links.push({
		quality: item.name,
		url: item.url,
		title: title
	 });
	});
		} else
		if (metadata.provider === 'ugc') {
	title = metadata.meta ? metadata.meta.title : undefined;
	if (!metadata.videos) {
	 return cb();
	}
	metadata.videos.forEach(function(item) {
	 links.push({
		quality: item.key,
		url: item.url,
		title: title
	 });
	});
		} else
		if (metadata.provider === 'pladform') {
	title = metadata.meta ? metadata.meta.title : undefined;
	var okEmbed = engine.modules.odnoklassniki;
	okEmbed.getPladformVideo({
	 extVideoId: {
		playerId: metadata.meta.playerId,
		videoId: metadata.meta.videoId
	 }
	}, function(response) {
	 if (!response) {
		return cb();
	 }

	 if (response.action === 'getRutubeLinks') {
		response.links = null;
	 }

	 var links = response.links;
	 if (!links) {
		return cb();
	 }
	 links.forEach(function(item) {
		if (item.title === undefined) {
			item.title = title
		}
	 });
	 cb(links, title, thumb, duration);
	});
	return;
		}
		if (links.length === 0) {
	return cb();
		}
		return cb(links, title, thumb, duration);
	}
 };

 engine.modules.mail_ru = mail_ru_embed;
 var SaveFrom_Utils = {
	downloadParam: 'sfh--download',

	setStyle: function(node, style) {
		if (!node || !style)
	return;

		for (var i in style)
	node.style[i] = style[i];
	},


	getStyle: function(node, property) {
		return node && window.getComputedStyle && window.getComputedStyle(node, null).getPropertyValue(property);
	},

	addStyleRules: function(selector, rules, className) {
		var style = className ? document.querySelector('#savefrom-styles.' + className) : document.getElementById('savefrom-styles');
		if (!style) {
	style = document.createElement('style');
	style.id = 'savefrom-styles';
	if (className) {
	 style.classList.add(className);
	}
	// maybe need for safari
	//style.appendChild(document.createTextNode(""));
	var s = document.querySelector('head style');
	if (s)
	 // allow to override our styles
	 s.parentNode.insertBefore(style, s);
	else
	 document.querySelector('head').appendChild(style);
		}

		if (typeof(rules) == 'object') {
	var r = [];
	for (var i in rules)
	 r.push(i + ':' + rules[i]);

	rules = r.join(';');
		}

		style.textContent += selector + '{' + rules + '}';
	},

	getPosition: function(node, parent) {
		var box = node.getBoundingClientRect();

		if (parent) {
	var parent_pos = parent.getBoundingClientRect();
	return {
	 top: Math.round(box.top - parent_pos.top),
	 left: Math.round(box.left - parent_pos.left),
	 width: box.width,
	 height: box.height
	}
		}
		return {
	top: Math.round(box.top + window.pageYOffset),
	left: Math.round(box.left + window.pageXOffset),
	width: box.width,
	height: box.height
		}
	},

	getSize: function(node) {
		return {
	width: node.offsetWidth,
	height: node.offsetHeight
		};
	},

	getMatchFirst: function(str, re) {
		var m = str.match(re);
		if (m && m.length > 1)
	return m[1];

		return '';
	},

	getElementByIds: function(ids) {
		for (var i = 0; i < ids.length; i++) {
	var node = document.getElementById(ids[i]);
	if (node)
	 return node;
		}

		return null;
	},

	getParentByClass: function(node, name) {
		if (!node || name == '') {
	return false;
		}

		var parent;
		if (typeof name === 'object' && name.length > 0) {
	for (parent = node; parent; parent = parent.parentNode) {
	 if (parent.nodeType !== 1) {
		return null;
	 }
	 for (var i = 0; i < name.length; i++) {
		if (parent.classList.contains(name[i])) {
			return parent;
		}
	 }
	}
		} else {
	for (parent = node; parent; parent = parent.parentNode) {
	 if (parent.nodeType !== 1) {
		return null;
	 }
	 if (parent.classList.contains(name)) {
		return parent;
	 }
	}
		}

		return null;
	},

	getParentByTagName: function(node, tagName) {
		if (!node || !tagName) {
	return false;
		}

		for (var parent = node; parent; parent = parent.parentNode) {
	if (parent.nodeType !== 1) {
	 return null;
	}

	if (parent.tagName === tagName) {
	 return parent;
	}
		}

		return null;
	},

	getParentById: function(node, id) {
		for (var parent = node; parent; parent = parent.parentNode) {
	if (parent.nodeType !== 1) {
	 return null;
	}

	if (parent.id === id) {
	 return parent;
	}
		}

		return null;
	},

	hasChildrenTagName: function(node, tagName) {
		for (var i = 0, item; item = node.childNodes[i]; i++) {
	if (item.nodeType !== 1) {
	 continue;
	}
	if (item.tagName === tagName) {
	 return true;
	}
		}
		return false;
	},

	isParent: function(node, testParent) {
		if (!testParent || [1, 9, 11].indexOf(testParent.nodeType) === -1) {
	return false;
		}

		return testParent.contains(node);
	},


	emptyNode: function(node) {
		while (node.firstChild)
	node.removeChild(node.firstChild);
	},

	download: function(filename, url, requestOptions, callback) {
		if (!url)
	return false;

		filename = filename || this.getFileName(url);
		if (!filename)
	return false;

		if (!mono.global.preference.downloads) {
	return false;
		}

		var params = requestOptions || {};
		params.url = url;
		params.filename = filename;

		callback = callback || undefined;

		mono.sendMessage({
	action: 'downloadFile',
	options: params
		}, callback);

		return true;
	},

	downloadList: {
		showDownloadWarningPopup: function(onContinue, type) {
	var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();

	mono.sendMessage({
	 action: 'getWarningIcon',
	 type: type
	}, function(icon) {
	 template.icon.style.backgroundImage = 'url(' + icon + ')';
	});

	mono.create(template.textContainer, {
	 append: [
		mono.create('p', {
			text: mono.global.language.warningPopupTitle,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '13px'
			}
		}),
		mono.create('p', {
			text: mono.global.language.warningPopupDesc + ' ',
			style: {
		color: '#868686',
		fontSize: '14px',
		marginBottom: '13px',
		lineHeight: '24px',
		marginTop: '0px'
			},
			append: mono.create('a', {
		href: (mono.global.language.lang === 'ru' || mono.global.language.lang === 'uk') ? 'http://vk.com/page-55689929_49003549' : 'http://vk.com/page-55689929_49004259',
		text: mono.global.language.readMore,
		target: '_blank',
		style: {
		 color: '#4A90E2'
		}
			})
		}),
		mono.create('p', {
			style: {
		marginBottom: '13px'
			},
			append: [
		mono.create('label', {
		 style: {
			color: '#868686',
			cursor: 'pointer',
			fontSize: '14px',
			lineHeight: '19px'
		 },
		 append: [
			mono.create('input', {
				type: 'checkbox',
				style: {
			cssFloat: 'left',
			marginLeft: '0px'
				},
				on: ['click', function() {
			mono.sendMessage({
			 action: 'hideDownloadWarning',
			 set: this.checked ? 1 : 0
			});
				}]
			}),
			mono.global.language.noWarning
		 ]
		})
			]
		})
	 ]
	});

	var cancelBtn = undefined;
	var continueBtn = undefined;
	mono.create(template.buttonContainer, {
	 append: [
		cancelBtn = mono.create('button', {
			text: mono.global.language.cancel,
			style: {
		height: '27px',
		width: '118px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		margin: '12px',
		marginBottom: '11px',
		marginRight: '4px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer'
			}
		}),
		continueBtn = mono.create('button', {
			text: mono.global.language.continue,
			style: {
		height: '27px',
		width: '118px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		margin: '12px',
		marginBottom: '11px',
		marginRight: '8px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer'
			}
		})
	 ]
	});

	cancelBtn.addEventListener('click', function(e) {
	 var popup = template.body.parentNode;
	 mono.trigger(popup.lastChild, 'click');
	});

	continueBtn.addEventListener('click', function(e) {
	 e.preventDefault();
	 e.stopPropagation();

	 onContinue();
	 mono.trigger(cancelBtn, 'click');
	});

	SaveFrom_Utils.popupDiv(template.body, 'dl_warning_box_popup');
		},
		startChromeDownloadList: function(options) {
	var folderName = options.folderName;
	var linkList = options.list;


	if (folderName) {
	 folderName += '/';
	}

	return mono.sendMessage({
	 action: 'downloadList',
	 fileList: linkList,
	 folder: folderName
	});
		},
		startOldChromeDownloadList: function(options, hideDialog) {
	var folderName = options.folderName;
	var linkList = options.list;
	var dataType = options.type;

	if (folderName) {
	 folderName += '/';
	}

	var itemIndex = 0;
	var pause = false;
	var timeout = 500;

	var focusEl = document.body;

	focusEl.focus();

	if (!hideDialog) {
	 focusEl.onblur = function() {
		pause = true;
	 };
	}

	var nextOneFile = function() {
	 var item = linkList[itemIndex];
	 itemIndex++;

	 if (item === undefined) {
		return;
	 }

	 if (mono.global.preference.downloads) {
		SaveFrom_Utils.download(folderName + item.filename, item.url);
	 } else {
		mono.trigger(mono.create('a', {
			download: item.filename,
			href: item.url,
			on: ['click', function(e) {
		SaveFrom_Utils.downloadOnClick(e);
			}]
		}), 'click', {
			cancelable: true,
			altKey: true
		});
	 }

	 if (pause) {
		SaveFrom_Utils.downloadList.showDownloadWarningPopup(function() {
			pause = false;
			focusEl.focus();
			nextOneFile();
		}, dataType);
	 } else {
		if (itemIndex > 5 && timeout) {
			timeout = undefined;
			focusEl.onblur = undefined;
			pause = false;
			if (mono.global.preference.downloads) {
		mono.sendMessage({
		 action: 'downloadList',
		 fileList: linkList.slice(itemIndex),
		 folder: folderName
		});
		return;
			}
		}

		setTimeout(function() {
			nextOneFile();
		}, timeout);
	 }
	};

	nextOneFile();
		},
		startDownload: function(options) {
	options.list.forEach(function(item) {
	 item.filename = mono.fileName.modify(item.filename);
	});

	options.folderName = mono.fileName.modify(options.folderName);

	if ((mono.isChrome && mono.global.preference.downloads) || mono.isFirefox) {
	 return SaveFrom_Utils.downloadList.startChromeDownloadList(options);
	} else
	if (mono.isGM || mono.isSafari) {
	 return mono.sendMessage({
		action: 'hideDownloadWarning'
	 }, function(state) {
		SaveFrom_Utils.downloadList.startOldChromeDownloadList(options, state);
	 });
	}
		},
		showBeforeDownloadPopup: function(list, options) {
	options.list = list;
	var type = options.type;
	var folderName = options.folderName;
	var onContinue = options.onContinue || SaveFrom_Utils.downloadList.startDownload;
	var onShowList = options.onShowList || SaveFrom_Utils.playlist.popupFilelist;
	var count = options.count || list.length;
	var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();

	mono.sendMessage({
	 action: 'getWarningIcon',
	 color: '#00CCFF',
	 type: type
	}, function(icon) {
	 template.icon.style.backgroundImage = 'url(' + icon + ')';
	});

	var showListLink = [];
	if (onShowList) {
	 showListLink = [' (', mono.create('a', {
		href: '#',
		text: mono.global.language.vkListOfLinks.toLowerCase()
	 }), ')'];
	 showListLink[1].addEventListener('click', function(e) {
		e.preventDefault();
		e.stopPropagation();
		onShowList(options.list);
		mono.trigger(cancelBtn, 'click');
	 });
	}

	mono.create(template.textContainer, {
	 append: [
		mono.create('p', {
			text: folderName || mono.global.language.playlistTitle,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '13px'
			}
		}),
		mono.create('p', {
			text: mono.global.language.vkFoundFiles.replace('%d', count),
			style: {
		color: '#868686',
		fontSize: '14px',
		marginBottom: '13px',
		lineHeight: '24px',
		marginTop: '0px'
			},
			append: showListLink
		}),
		mono.create('p', {
			text: mono.global.language.beforeDownloadPopupWarn,
			style: {
		color: '#868686',
		fontSize: '14px',
		marginBottom: '13px',
		lineHeight: '24px',
		marginTop: '0px'
			}
		})
	 ]
	});

	var cancelBtn = undefined;
	var dlBtn = undefined;
	mono.create(template.buttonContainer, {
	 append: [
		cancelBtn = mono.create('button', {
			text: mono.global.language.cancel,
			style: {
		height: '27px',
		width: '118px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		margin: '12px',
		marginBottom: '11px',
		marginRight: '4px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer'
			}
		}),
		dlBtn = mono.create('button', {
			text: mono.global.language.continue,
			style: {
		height: '27px',
		width: '118px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		margin: '12px',
		marginBottom: '11px',
		marginRight: '8px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer'
			}
		})
	 ]
	});

	cancelBtn.addEventListener('click', function(e) {
	 var popup = template.body.parentNode;
	 mono.trigger(popup.lastChild, 'click');
	});

	dlBtn.addEventListener('click', function(e) {
	 e.preventDefault();
	 e.stopPropagation();

	 onContinue(options);
	 mono.trigger(cancelBtn, 'click');
	});

	SaveFrom_Utils.popupDiv(template.body, 'dl_confirm_box_popup');
		}
	},

	downloadLink: function(a, callback) {
		if (!a.href)
	return false;

		var filename = a.getAttribute('download');

		return this.download(filename, a.href, null, callback);
	},

	safariDlLink: function(e) {
		"use strict";
		if (e.button || e.ctrlKey || e.altKey || e.shitfKey) {
	return;
		}

		var me = null;

		var legacy = function(e) {
	var me = document.createEvent('MouseEvents');
	me.initMouseEvent('click', true, e.cancelable, window, 0,
	 e.screenX, e.screenY, e.clientX, e.clientY,
	 false, true, false, e.metaKey, e.button, e.relatedTarget);
	return me;
		};

		try {
	if (typeof MouseEvent !== 'function') {
	 throw 'legacy';
	}
	me = new MouseEvent('click', {
	 bubbles: true,
	 cancelable: e.cancelable,
	 screenX: e.screenX,
	 screenY: e.screenY,
	 clientX: e.clientX,
	 clientY: e.clientY,
	 ctrlKey: false,
	 altKey: true,
	 shiftKey: false,
	 metaKey: e.metaKey,
	 button: e.button,
	 relatedTarget: e.relatedTarget
	});
		} catch (err) {
	me = legacy(e);
		}

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

		this.dispatchEvent(me);
	},

	downloadOnClick: function(event, callback, options) {
		options = options || {};
		var _this = SaveFrom_Utils;

		var node = options.el || event.target;
		if (node.tagName !== 'A') {
	node = mono.getParent(node, 'A');
		}

		if (!node) {
	return;
		}

		if (mono.isSafari) {
	return _this.safariDlLink.call(node, event);
		}

		if (!mono.global.preference.downloads) {
	return;
		}

		if ((mono.isFirefox || mono.isGM) && /^blob:|^data:/.test(node.href)) {
	return;
		}

		if (event.button === 2) {
	return;
		}

		event.preventDefault();
		event.stopPropagation();

		_this.downloadLink(node, callback);
	},

	getQueryString: function(query, key_prefix, key_suffix) {
		if (!query || typeof(query) != 'object')
	return '';

		if (key_prefix === undefined)
	key_prefix = '';

		if (key_suffix === undefined)
	key_suffix = '';

		var str = '';
		for (var key in query) {
	if (str.length)
	 str += '&';

	if (query[key] instanceof Object) {
	 if (!key_prefix)
		key_prefix = '';

	 if (!key_suffix)
		key_suffix = '';

	 str += SaveFrom_Utils.getQueryString(query[key], key_prefix + key + "[", "]" + key_suffix);
	} else
	 str += key_prefix + escape(key) + key_suffix + '=' + escape(query[key]);
		}

		return str;
	},

	decodeUnicodeEscapeSequence: function(text) {
		return text.replace(/\\u([0-9a-f]{4})/g, function(s, m) {
	m = parseInt(m, 16);
	if (!isNaN(m)) {
	 return String.fromCharCode(m);
	}
		});
	},


	getFileExtension: function(str, def) {
		var ext = this.getMatchFirst(str, /\.([a-z0-9]{3,4})(\?|$)/i);
		if (ext)
	return ext.toLowerCase();

		return (def ? def : '');
	},


	getFileName: function(url) {
		var filename = this.getMatchFirst(url, /\/([^\?#\/]+\.[a-z\d]{2,6})(?:\?|#|$)/i);
		if (!filename)
	return filename;

		return mono.fileName.modify(filename);
	},


	getTopLevelDomain: function(domain) {
		if (!domain)
	return '';

		if (!domain.match(/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}/))
	return domain;

		var a = domain.split('.');
		var l = a.length;

		if (l == 2)
	return domain;

		return (a[l - 2] + '.' + a[l - 1]);
	},


	dateToObj: function(ts, leadingZero) {
		var d = (ts === null || ts === undefined) ? new Date() : new Date(ts);

		if (leadingZero === undefined)
	leadingZero = true;

		var res = {
	year: d.getFullYear(),
	month: (d.getMonth() + 1),
	day: d.getDate(),
	hour: d.getHours(),
	min: d.getMinutes(),
	sec: d.getSeconds()
		};

		if (leadingZero) {
	for (var i in res) {
	 if (res[i].toString().length == 1)
		res[i] = '0' + res[i];
	}
		}

		return res;
	},


	utf8Encode: function(str) {
		str = str.replace(/\r\n/g, "\n");
		var res = "";

		for (var n = 0; n < str.length; n++) {
	var c = str.charCodeAt(n);

	if (c < 128)
	 res += String.fromCharCode(c);
	else if ((c > 127) && (c < 2048)) {
	 res += String.fromCharCode((c >> 6) | 192);
	 res += String.fromCharCode((c & 63) | 128);
	} else {
	 res += String.fromCharCode((c >> 12) | 224);
	 res += String.fromCharCode(((c >> 6) & 63) | 128);
	 res += String.fromCharCode((c & 63) | 128);
	}

		}

		return res;
	},

	sizeHuman: function(size, round) {
		if (round == undefined || round == null)
	round = 2;

		var s = size,
	count = 0,
	sign = '',
	unite_spec = [
	 mono.global.language.vkFileSizeByte,
	 mono.global.language.vkFileSizeKByte,
	 mono.global.language.vkFileSizeMByte,
	 mono.global.language.vkFileSizeGByte,
	 mono.global.language.vkFileSizeTByte
	];

		if (s < 0) {
	sign = '-';
	s = Math.abs(s);
		}

		while (s >= 1000) {
	count++;
	s /= 1024;
		}

		if (round >= 0) {
	var m = round * 10;
	s = Math.round(s * m) / m;
		}

		if (count < unite_spec.length)
	return sign + s + ' ' + unite_spec[count];

		return size;
	},

	secondsToDuration: function(seconds) {
		if (!seconds || isNaN(seconds))
	return '';

		function zfill(time) {
	if (time < 10)
	 return '0' + time;

	return time.toString();
		}

		var hours = Math.floor(seconds / 3600);
		seconds %= 3600;

		var minutes = Math.floor(seconds / 60);
		seconds %= 60;

		if (hours > 0)
	return hours + ":" + zfill(minutes) + ":" + zfill(seconds);

		return minutes + ":" + zfill(seconds);
	},

	svg: {
		icon: {
	download: 'M 4,0 4,8 0,8 8,16 16,8 12,8 12,0 4,0 z',
	info: 'M 8,1.55 C 11.6,1.55 14.4,4.44 14.4,8 14.4,11.6 11.6,14.4 8,14.4 4.44,14.4 1.55,11.6 1.55,8 1.55,4.44 4.44,1.55 8,1.55 M 8,0 C 3.58,0 0,3.58 0,8 0,12.4 3.58,16 8,16 12.4,16 16,12.4 16,8 16,3.58 12.4,0 8,0 L 8,0 z M 9.16,12.3 H 6.92 V 7.01 H 9.16 V 12.3 z M 8.04,5.91 C 7.36,5.91 6.81,5.36 6.81,4.68 6.81,4 7.36,3.45 8.04,3.45 8.72,3.45 9.27,4 9.27,4.68 9.27,5.36 8.72,5.91 8.04,5.91 z',
	noSound: 'M 11.4,5.05 13,6.65 14.6,5.05 16,6.35 14.4,7.95 16,9.55 14.6,11 13,9.35 11.4,11 10,9.55 11.6,7.95 10,6.35 z M 8,1.75 8,14.3 4,10.5 l -4,0 0,-4.75 4,0 z'
		},

		cache: {},

		getSrc: function(icon, color) {
	if (!this.icon[icon])
	 return '';

	if (!this.cache[icon])
	 this.cache[icon] = {};

	if (!this.cache[icon][color]) {
	 this.cache[icon][color] = btoa(
		'<?xml version="1.0" encoding="UTF-8"?>' +
		'<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="16" height="16" viewBox="0 0 16 16" id="svg2" xml:space="preserve">' +
		'<path d="' + this.icon[icon] + '" fill="' + color + '" /></svg>'
	 );
	}

	if (this.cache[icon][color])
	 return 'data:image/svg+xml;base64,' + this.cache[icon][color];

	return '';
		},

		getSvg: function(icon, color, width, height) {
	var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
	var svgNS = svg.namespaceURI;
	svg.setAttribute('width', width || '16');
	svg.setAttribute('height', height || width || '16');
	svg.setAttribute('viewBox', '0 0 16 16');

	var path = document.createElementNS(svgNS, 'path');
	svg.appendChild(path);
	path.setAttribute('d', this.icon[icon]);

	if (color) {
	 path.setAttribute('fill', color);
	}

	return svg;
		}
	},

	appendDownloadInfo: function(parent, color, boxStyle, btnStyle) {
		if (!color)
	color = '#a0a0a0';

		var info = document.createElement('span');
		info.appendChild(document.createTextNode(mono.global.language.downloadTitle));
		this.setStyle(info, {
	display: 'inline-block',
	position: 'relative',
	border: '1px solid ' + color,
	borderRadius: '5px',
	fontSize: '13px',
	lineHeight: '17px',
	padding: '2px 19px 2px 5px',
	marginTop: '5px',
	opacity: 0.9
		});

		if (boxStyle)
	this.setStyle(info, boxStyle);

		var close = document.createElement('span');
		close.textContent = String.fromCharCode(215);
		this.setStyle(close, {
	color: color,
	width: '14px',
	height: '14px',
	fontSize: '14px',
	fontWeight: 'bold',
	lineHeight: '14px',
	position: 'absolute',
	top: 0,
	right: 0,
	overflow: 'hidden',
	cursor: 'pointer'
		});

		if (btnStyle)
	this.setStyle(close, btnStyle);

		close.addEventListener('click', function() {
	info.parentNode.removeChild(info);
	mono.sendMessage({
	 action: 'updateOption',
	 key: 'moduleShowDownloadInfo',
	 value: 0
	});
		}, false);

		info.appendChild(close);
		parent.appendChild(info);
	},

	/**
	 * @param {Object|null} ctrStyle
	 * @param {Object|null} btnStyle
	 * @param {Object|null} svgStyle
	 * @param {Object} details
	 * @param {Object} details.url
	 * @param {Element} [details.link]
	 * @param {Boolean} [details.brackets]
	 * @returns {{node: Element}}
	 */
	getFileSizeIcon: function(ctrStyle, btnStyle, svgStyle, details) {
		var _this = this;
		var language = mono.global.language;
		details = details || {};
		ctrStyle = ctrStyle || {};
		btnStyle = btnStyle || {};
		svgStyle = svgStyle || {};

		var defaultColor = '#333333';
		var zeroSizeColor = '#ffac00';
		var errorColor = '#ff0000';

		var getIconBtn = function(iconColor) {
	return mono.create('div', {
	 style: btnStyle,
	 append: [
		mono.create(SaveFrom_Utils.svg.getSvg('info', iconColor), {
			style: svgStyle
		})
	 ]
	});
		};

		var onIconClick = function(e) {
	e.stopPropagation();
	e.preventDefault();

	ctr.textContent = '...';
	var url = details.url;
	if (!url) {
	 url = details.link && details.link.href;
	}
	mono.sendMessagePromise({
	 action: 'getFileSize',
	 url: url
	}).then(function(response) {
	 if (response.error || !response.fileSize) {
		throw new Error(JSON.stringify(response));
	 }

	 var fileType = response.fileType || '';
	 var size = _this.sizeHuman(response.fileSize, 2);
	 var bitRate = '';
	 if (details.link && /^audio\//i.test(fileType)) {
		var seconds = parseInt(details.link.dataset.savefromHelperDuration);
		if (seconds > 0) {
			bitRate += Math.floor((response.fileSize / seconds) / 125);
			bitRate += ' ' + mono.global.language.kbps;
		}
	 }

	 var text = '';
	 if (bitRate) {
		text += size + ' ~ ' + bitRate;
	 } else {
		text += size;
	 }

	 if (details.brackets) {
		text = '(' + text + ')';
	 }

	 ctr.textContent = text;
	 ctr.title = fileType;
	}).catch(function(err) {
	 mono.error(err);

	 var icon;
	 if (err.message === 'ZERO') {
		icon = getIconBtn(zeroSizeColor);
		icon.title = language.getFileSizeTitle;
	 } else {
		icon = getIconBtn(errorColor);
		icon.title = language.getFileSizeFailTitle;
	 }
	 icon.addEventListener('click', onIconClick);

	 ctr.textContent = '';
	 ctr.appendChild(icon);
	});
		};

		var ctr = mono.create('div', {
	style: ctrStyle,
	append: [
	 mono.create(getIconBtn(defaultColor), {
		title: language.getFileSizeTitle,
		on: ['click', onIconClick]
	 })
	]
		});

		return {
	node: ctr
		};
	},

	appendFileSizeIcon: function(link, iconStyle, textStyle, error, noBrackets, container) {
		var language = mono.global.language;
		iconStyle = iconStyle || {};
		textStyle = textStyle || {};

		var iconColor = '#333333';
		if (error === '0') {
	iconColor = '#ffac00';
		} else
		if (error) {
	iconColor = '#ff0000';
		} else
		if (iconStyle.color) {
	iconColor = iconStyle.color;
		}

		var defIconStyle = {
	width: '14px',
	height: '14px',
	marginLeft: '3px',
	verticalAlign: 'middle',
	position: 'relative',
	top: '-1px',
	cursor: 'pointer'
		};
		mono.extend(defIconStyle, iconStyle);

		var defTextStyle = {
	fontSize: '75%',
	fontWeight: 'normal',
	marginLeft: '3px',
	whiteSpace: 'nowrap'
		};
		mono.extend(defTextStyle, textStyle);

		var fsBtn = mono.create('img', {
	src: SaveFrom_Utils.svg.getSrc('info', iconColor),
	title: error ? language.getFileSizeFailTitle : language.getFileSizeTitle,
	style: defIconStyle
		});

		var _this = this;

		if (container) {
	container.appendChild(fsBtn);
		} else
		if (link.nextSibling) {
	link.parentNode.insertBefore(fsBtn, link.nextSibling);
		} else {
	link.parentNode.appendChild(fsBtn);
		}

		fsBtn.addEventListener("click", function(event) {
	event.preventDefault();
	event.stopPropagation();

	var node = mono.create('span', {
	 text: '...',
	 style: defTextStyle
	});

	fsBtn.parentNode.replaceChild(node, fsBtn);

	return mono.sendMessage({
	 action: 'getFileSize',
	 url: link.href
	}, function(response) {
	 if (response.fileSize > 0) {
		var fileType = response.fileType || '';
		var size = _this.sizeHuman(response.fileSize, 2);
		var bitrate = '';
		if (/^audio\//i.test(fileType)) {
			var seconds = link.getAttribute('data-savefrom-helper-duration');
			seconds = seconds && parseInt(seconds);
			if (seconds > 0) {
		bitrate = Math.floor((response.fileSize / seconds) / 125);
		bitrate += ' ' + mono.global.language.kbps;
			}
		}
		var text = '';

		if (bitrate) {
			text = size + ' ~ ' + bitrate;
		} else {
			text = size;
		}

		if (!noBrackets) {
			text = '(' + text + ')';
		}

		node.textContent = text;
		node.title = fileType;
	 } else
	 if (response.error) {
		var errBtn = _this.appendFileSizeIcon(link, iconStyle, textStyle, true, noBrackets, document.createDocumentFragment());
		node.parentNode.replaceChild(errBtn, node);
	 } else {
		var zeroBtn = _this.appendFileSizeIcon(link, iconStyle, textStyle, '0', noBrackets, document.createDocumentFragment());
		node.parentNode.replaceChild(zeroBtn, node);
	 }
	});
		}, false);

		return fsBtn;
	},

	appendNoSoundIcon: function(link, iconStyle) {
		iconStyle = iconStyle || {};

		var noSoundIconColor = '#ff0000';
		if (iconStyle.color) {
	noSoundIconColor = iconStyle.color;
		}

		var defIconStyle = {
	width: '14px',
	height: '14px',
	marginLeft: '3px',
	verticalAlign: 'middle',
	position: 'relative',
	top: '-1px',
	cursor: 'pointer'
		};
		mono.extend(defIconStyle, iconStyle);

		var icon = mono.create('img', {
	src: SaveFrom_Utils.svg.getSrc('noSound', noSoundIconColor),
	title: mono.global.language.withoutAudio,
	style: defIconStyle
		});

		if (link.nextSibling) {
	link.parentNode.insertBefore(icon, link.nextSibling);
		} else
		if (link.parentNode) {
	link.parentNode.appendChild(icon);
		} else {
	link.appendChild(icon);
		}
	},

	video: {
		dataAttr: 'data-savefrom-video-visible',
		yt: {
	inited: false,

	show3D: false,
	showMP4NoAudio: false,

	showFormat: {
	 'FLV': true,
	 'MP4': true,
	 'WebM': false,
	 '3GP': false,
	 'Audio AAC': false,
	 'Audio Vorbis': false,
	 'Audio Opus': false
	},

	format: {
	 'FLV': {
		'5': {
			quality: '240'
		},
		'6': {
			quality: '270'
		},
		'34': {
			quality: '360'
		},
		'35': {
			quality: '480'
		}
	 },

	 'MP4': {
		'18': {
			quality: '360'
		},
		'22': {
			quality: '720'
		},
		'37': {
			quality: '1080'
		},
		'38': {
			quality: '8K'
		},
		'59': {
			quality: '480'
		},
		'78': {
			quality: '480'
		},
		'82': {
			quality: '360',
			'3d': true
		},
		'83': {
			quality: '240',
			'3d': true
		},
		'84': {
			quality: '720',
			'3d': true
		},
		'85': {
			quality: '1080',
			'3d': true
		},
		'160': {
			quality: '144',
			noAudio: true
		},
		'133': {
			quality: '240',
			noAudio: true
		},
		'134': {
			quality: '360',
			noAudio: true
		},
		'135': {
			quality: '480',
			noAudio: true
		},
		'136': {
			quality: '720',
			noAudio: true
		},
		'137': {
			quality: '1080',
			noAudio: true
		},
		'212': {
			quality: '480',
			noAudio: true
		},
		'213': {
			quality: '480',
			noAudio: true
		},
		'214': {
			quality: '720',
			noAudio: true
		},
		'215': {
			quality: '720',
			noAudio: true
		},
		'264': {
			quality: '1440',
			noAudio: true
		},
		'138': {
			quality: '8K',
			noAudio: true
		},
		'298': {
			quality: '720',
			noAudio: true,
			sFps: true
		},
		'299': {
			quality: '1080',
			noAudio: true,
			sFps: true
		},
		'266': {
			quality: '4K',
			noAudio: true
		}
	 },

	 'WebM': {
		'43': {
			quality: '360'
		},
		'44': {
			quality: '480'
		},
		'45': {
			quality: '720'
		},
		'46': {
			quality: '1080'
		},
		'167': {
			quality: '360',
			noAudio: true
		},
		'168': {
			quality: '480',
			noAudio: true
		},
		'169': {
			quality: '720',
			noAudio: true
		},
		'170': {
			quality: '1080',
			noAudio: true
		},
		'218': {
			quality: '480',
			noAudio: true
		},
		'219': {
			quality: '480',
			noAudio: true
		},
		'242': {
			quality: '240',
			noAudio: true
		},
		'243': {
			quality: '360',
			noAudio: true
		},
		'244': {
			quality: '480',
			noAudio: true
		},
		'245': {
			quality: '480',
			noAudio: true
		},
		'246': {
			quality: '480',
			noAudio: true
		},
		'247': {
			quality: '720',
			noAudio: true
		},
		'248': {
			quality: '1080',
			noAudio: true
		},
		'271': {
			quality: '1440',
			noAudio: true
		},
		'272': {
			quality: '8K',
			noAudio: true
		},
		'278': {
			quality: '144',
			noAudio: true
		},
		'100': {
			quality: '360',
			'3d': true
		},
		'101': {
			quality: '480',
			'3d': true
		},
		'102': {
			quality: '720',
			'3d': true
		},
		'302': {
			quality: '720',
			noAudio: true,
			sFps: true
		},
		'303': {
			quality: '1080',
			noAudio: true,
			sFps: true
		},
		'308': {
			quality: '1440',
			noAudio: true,
			sFps: true
		},
		'313': {
			quality: '4K',
			noAudio: true
		},
		'315': {
			quality: '4K',
			noAudio: true,
			sFps: true
		},
		'330': {
			quality: '144',
			noAudio: true,
			sFps: true
		},
		'331': {
			quality: '240',
			noAudio: true,
			sFps: true
		},
		'332': {
			quality: '360',
			noAudio: true,
			sFps: true
		},
		'333': {
			quality: '480',
			noAudio: true,
			sFps: true
		},
		'334': {
			quality: '720',
			noAudio: true,
			sFps: true
		},
		'335': {
			quality: '1080',
			noAudio: true,
			sFps: true
		},
		'336': {
			quality: '1440',
			noAudio: true,
			sFps: true
		},
		'337': {
			quality: '2160',
			noAudio: true,
			sFps: true
		}
	 },

	 '3GP': {
		'17': {
			quality: '144'
		},
		'36': {
			quality: '240'
		}
	 },

	 'Audio AAC': {
		'139': {
			quality: '48',
			ext: 'aac',
			noVideo: true
		},
		'140': {
			quality: '128',
			ext: 'aac',
			noVideo: true
		},
		'141': {
			quality: '256',
			ext: 'aac',
			noVideo: true
		},
		'256': {
			quality: '192',
			ext: 'aac',
			noVideo: true
		},
		'258': {
			quality: '384',
			ext: 'aac',
			noVideo: true
		}
	 },

	 'Audio Vorbis': {
		'171': {
			quality: '128',
			ext: 'webm',
			noVideo: true
		},
		'172': {
			quality: '192',
			ext: 'webm',
			noVideo: true
		}
	 },

	 'Audio Opus': {
		'249': {
			quality: '48',
			ext: 'opus',
			noVideo: true
		},
		'250': {
			quality: '128',
			ext: 'opus',
			noVideo: true
		},
		'251': {
			quality: '256',
			ext: 'opus',
			noVideo: true
		}
	 }
	},


	init: function() {
	 if (SaveFrom_Utils.video.yt.inited) {
		return;
	 }

	 ['Audio AAC', 'Audio Vorbis', 'Audio Opus'].forEach(function(item) {
		var formatType = SaveFrom_Utils.video.yt.format[item];
		for (var qualityValue in formatType) {
			formatType[qualityValue].quality += ' ' + mono.global.language.kbps;
		}
	 });

	 SaveFrom_Utils.video.yt.show3D = mono.global.preference.ytHide3D == '0';
	 SaveFrom_Utils.video.yt.showMP4NoAudio = mono.global.preference.ytHideMP4NoAudio == '0';

	 var show = false;
	 var showAudio = false;
	 for (var i in SaveFrom_Utils.video.yt.showFormat) {
		var prefName = 'ytHide' + i.replace(' ', '_');
		if (prefName === 'ytHideAudio_AAC') {
			prefName = 'ytHideAudio_MP4';
		}
		var value = mono.global.preference[prefName] == '0';
		if (i === 'Audio AAC') {
			showAudio = value;
		}
		SaveFrom_Utils.video.yt.showFormat[i] = value;
		if (value) {
			show = true;
		}
	 }

	 SaveFrom_Utils.video.yt.showFormat['Audio Vorbis'] = showAudio;
	 SaveFrom_Utils.video.yt.showFormat['Audio Opus'] = showAudio;

	 if (!show) {
		SaveFrom_Utils.video.yt.showFormat.FLV = true;
	 }

	 SaveFrom_Utils.video.yt.inited = true;
	},


	show: function(links, parent, showDownloadInfo, style, videoTitle) {
	 style = style || {};

	 var content = document.createElement('div');
	 SaveFrom_Utils.setStyle(content, {
		display: 'inline-block',
		margin: '0 auto'
	 });
	 parent.appendChild(content);

	 var box = document.createElement('div');
	 SaveFrom_Utils.setStyle(box, {
		display: 'inline-block',
		padding: '0 90px 0 0',
		position: 'relative'
	 });
	 content.appendChild(box);

	 var tbl = document.createElement('table');
	 SaveFrom_Utils.setStyle(tbl, {
		emptyCells: 'show',
		borderCollapse: 'collapse',
		margin: '0 auto',
		padding: '0',
		width: 'auto'
	 });
	 box.appendChild(tbl);

	 var hidden = false;

	 for (var i in SaveFrom_Utils.video.yt.format) {
		if (SaveFrom_Utils.video.yt.append(links, i,
		SaveFrom_Utils.video.yt.format[i], tbl, style, videoTitle)) {
			hidden = true;
		}
	 }

	 for (var i in links) {
		if (i === 'ummy' || i === 'ummyAudio' || i === 'meta') {
			continue;
		}
		if (SaveFrom_Utils.video.yt.append(links, '', null, tbl, style, videoTitle)) {
			hidden = true;
		}

		break;
	 }

	 if (!tbl.firstChild) {
		parent.textContent = mono.global.language.noLinksFound;
		return;
	 }

	 if (!hidden)
		return;

	 var more = document.createElement('span');
	 more.textContent = mono.global.language.more + ' ' + String.fromCharCode(187);
	 SaveFrom_Utils.setStyle(more, {
		color: '#555',
		border: '1px solid #a0a0a0',
		borderRadius: '3px',
		display: 'block',
		fontFamily: 'Arial',
		fontSize: '15px',
		lineHeight: '17px',
		padding: '1px 5px',
		position: 'absolute',
		bottom: '3px',
		right: '0',
		cursor: 'pointer'
	 });

	 if (style.btn && typeof(style.btn) == 'object')
		SaveFrom_Utils.setStyle(more, style.btn);

	 box.appendChild(more);

	 more.addEventListener('click', function(event) {
		event.preventDefault();
		event.stopPropagation();

		var e = parent.querySelectorAll('*[' + SaveFrom_Utils.video.dataAttr + ']');
		for (var i = 0; i < e.length; i++) {
			var visible = e[i].getAttribute(SaveFrom_Utils.video.dataAttr);
			var display = 'none',
		symbol = String.fromCharCode(187);
			if (visible == '0') {
		visible = '1';
		display = '';
		symbol = String.fromCharCode(171);
			} else
		visible = '0';

			e[i].style.display = display;
			e[i].setAttribute(SaveFrom_Utils.video.dataAttr, visible);
			this.textContent = mono.global.language.more + ' ' + symbol;
		}

		return false;
	 }, false);


	 if (showDownloadInfo === 1) {
		var color = '#a0a0a0',
			a = tbl.querySelector('td a');

		content.appendChild(document.createElement('br'));
		SaveFrom_Utils.appendDownloadInfo(content, color, null, {
			width: '16px',
			height: '16px',
			fontSize: '16px',
			lineHeight: '16px'
		});
	 }
	},


	append: function(links, title, format, parent, style, videoTitle) {
	 var hidden = false;

	 var aStyle = {
		whiteSpace: 'nowrap'
	 };

	 var sStyle = {
		fontSize: '75%',
		fontWeight: 'normal',
		marginLeft: '3px',
		whiteSpace: 'nowrap'
	 };

	 var tr = document.createElement('tr');

	 var td = document.createElement('td');
	 td.appendChild(document.createTextNode(title ? title : '???'));

	 if (!title || !SaveFrom_Utils.video.yt.showFormat[title]) {
		tr.setAttribute(SaveFrom_Utils.video.dataAttr, '0');
		tr.style.display = 'none';
		hidden = true;
	 }

	 SaveFrom_Utils.setStyle(td, {
		border: 'none',
		padding: '3px 15px 3px 0',
		textAlign: 'left',
		verticalAlign: 'middle'
	 });

	 tr.appendChild(td);

	 td = document.createElement('td');
	 SaveFrom_Utils.setStyle(td, {
		border: 'none',
		padding: '3px 0',
		textAlign: 'left',
		verticalAlign: 'middle',
		lineHeight: '17px'
	 });
	 tr.appendChild(td);

	 var meta = links.meta || {};

	 var sep = false;
	 if (format) {
		for (var i in format) {
			if (links[i]) {
		var quality = format[i].quality;
		if (sep) {
		 td.lastChild.style.marginRight = '15px';
		 td.appendChild(document.createTextNode(' '));
		}

		var span = document.createElement('span');
		span.style.whiteSpace = 'nowrap';

		var a = document.createElement('a');
		a.href = links[i];
		a.title = mono.global.language.downloadTitle;

		if (meta[i]) {
		 if (meta[i].quality) {
			quality = meta[i].quality;
		 }

		 if (format[i].sFps) {
			quality += ' ' + (meta[i].fps || 60);
		 }
		}

		if (format[i]['3d']) {
		 a.textContent = '3D';
		} else {
		 a.textContent = quality;
		}
		if (videoTitle) {
		 var ext = format[i]['ext'];
		 if (!ext)
			ext = title.toLowerCase();

		 a.setAttribute('download', mono.fileName.modify(videoTitle + '.' + ext));

		 a.addEventListener('click', function(event) {
			SaveFrom_Utils.downloadOnClick(event);
		 }, false);
		}
		SaveFrom_Utils.setStyle(a, aStyle);
		if (style.link && typeof(style.link) == 'object')
		 SaveFrom_Utils.setStyle(a, style.link);

		span.appendChild(a);
		SaveFrom_Utils.appendFileSizeIcon(a, style.fsIcon, style.fsText);

		if (format[i]['3d']) {
		 if (!SaveFrom_Utils.video.yt.show3D) {
			hidden = true;
			span.setAttribute(SaveFrom_Utils.video.dataAttr, '0');
			span.style.display = 'none';
		 }

		 var s = document.createElement('span');
		 s.textContent = quality;
		 SaveFrom_Utils.setStyle(s, sStyle);
		 if (style.text && typeof(style.text) == 'object')
			SaveFrom_Utils.setStyle(s, style.text);

		 a.appendChild(s);
		}

		if (format[i]['noAudio']) {
		 if (!SaveFrom_Utils.video.yt.showMP4NoAudio) {
			hidden = true;
			span.setAttribute(SaveFrom_Utils.video.dataAttr, '0');
			span.style.display = 'none';
		 }

		 SaveFrom_Utils.appendNoSoundIcon(a, style ? style.noSoundIcon : false);
		}

		td.appendChild(span);

		sep = true;

		delete links[i];
			}
		}
	 } else {
		for (var i in links) {
			if (sep) {
		td.lastChild.style.marginRight = '15px';
		td.appendChild(document.createTextNode(' '));
			}

			var span = document.createElement('span');
			span.style.whiteSpace = 'nowrap';

			var a = document.createElement('a');
			a.href = links[i];
			a.title = mono.global.language.downloadTitle;
			a.textContent = i;
			SaveFrom_Utils.setStyle(a, aStyle);
			if (style.link && typeof(style.link) == 'object')
		SaveFrom_Utils.setStyle(a, style.link);

			span.appendChild(a);
			SaveFrom_Utils.appendFileSizeIcon(a, style.fsIcon, style.fsText);
			td.appendChild(span);

			sep = true;

			delete links[i];
		}
	 }

	 if (sep === false) {
		return;
	 }
	 parent.appendChild(tr);

	 return hidden;
	}
		}
	}, // video


	playlist: {
		btnStyle: {
	display: 'block',
	fontWeight: 'bold',
	border: 'none',
	textDecoration: 'underline'
		},


		getFilelistHtml: function(links) {
	if (!links || links.length == 0)
	 return;

	var rows = 0;
	var list = '';

	for (var i = 0; i < links.length; i++) {
	 if (links[i].url) {
		list += links[i].url + '\r\n';
		rows++;
	 }
	}

	if (list) {
	 if (rows < 5) {
		rows = 5;
	 } else
	 if (rows > 14) {
		rows = 14;
	 }

	 var textareaNode;
	 return mono.create(document.createDocumentFragment(), {
		append: [
			mono.create('p', {
		text: mono.global.language.filelistTitle,
		style: {
		 color: '#0D0D0D',
		 fontSize: '20px',
		 marginBottom: '11px',
		 marginTop: '5px'
		}
			}),
			mono.create('p', {
		style: {
		 marginBottom: '11px'
		},
		append: mono.parseTemplate(mono.global.language.filelistInstruction)
			}),
			mono.create('p', {
		text: mono.global.language.vkFoundFiles.replace('%d', links.length),
		style: {
		 color: '#000',
		 marginBottom: '11px'
		},
		append: mono.create('a', {
		 text: mono.global.language.playlist,
		 href: '#',
		 class: 'sf__playlist',
		 style: {
			display: 'none',
			cssFloat: 'right'
		 }
		})
			}),
			textareaNode = mono.create('textarea', {
		text: list,
		rows: rows,
		cols: 60,
		style: {
		 width: '100%',
		 whiteSpace: (mono.isFirefox || mono.isGmOnly) ? 'normal' : 'nowrap'
		}
			}),
			(!mono.isChrome && !mono.isFirefox) ? undefined : mono.create('button', {
		text: mono.global.language.copy,
		style: {
		 height: '27px',
		 backgroundColor: '#ffffff',
		 border: '1px solid #9e9e9e',
		 marginTop: '6px',
		 paddingLeft: '10px',
		 paddingRight: '10px',
		 borderRadius: '5px',
		 fontSize: '14px',
		 cursor: 'pointer',
		 cssFloat: 'right'
		},
		on: ['click', function(e) {
		 var _this = this;
		 _this.disabled = true;
		 if (mono.isFirefox) {
			textareaNode.select();
			document.execCommand("copy");
		 } else {
			mono.sendMessage({
				action: 'addToClipboard',
				text: list
			});
		 }
		 setTimeout(function() {
			_this.disabled = false;
		 }, 1000);
		}],
		append: mono.create('style', {
		 text: mono.style2Text({
			'#savefrom_popup_box': {
				append: {
			'button:hover:not(:disabled)': {
			 backgroundColor: '#597A9E !important',
			 borderColor: '#597A9E !important',
			 color: '#fff'
			},
			'button:active': {
			 opacity: 0.9
			}
				}
			}
		 })
		})
			})
		]
	 });
	}
		},


		popupFilelist: function(links, title, playlist, id) {
	var content = SaveFrom_Utils.playlist.getFilelistHtml(links);
	if (!content)
	 return;

	var popup = SaveFrom_Utils.popupDiv(content, id);
	if (playlist) {
	 var a = popup.querySelector('a.sf__playlist');
	 if (a) {
		a.addEventListener('click', function(event) {
			setTimeout(function() {
		SaveFrom_Utils.playlist.popupPlaylist(links, title, true, id);
			}, 100);
			event.preventDefault();
			return false;
		}, false);

		SaveFrom_Utils.setStyle(a, SaveFrom_Utils.playlist.btnStyle);
	 }
	}
		},

		getInfoPopupTemplate: function() {
	var popupContainer = mono.create('div', {
	 class: 'sf-infoPopupTemplate',
	 style: {
		width: '400px',
		minHeight: '40px'
	 }
	});

	var mediaIcon = mono.create('div', {
	 style: {
		backgroundSize: '48px',
		backgroundRepeat: 'no-repeat',
		backgroundPosition: 'center top',
		display: 'inline-block',
		width: '60px',
		height: '60px',
		cssFloat: 'left',
		marginTop: '16px',
		marginRight: '10px'
	 }
	});

	var textContent = mono.create('div', {
	 style: {
		display: 'inline-block',
		width: '330px'
	 }
	});

	var buttonWrap = mono.create('div', {
	 style: {
		textAlign: 'right'
	 },
	 append: mono.create('style', {
		text: mono.style2Text({
			'.sf-infoPopupTemplate': {
		append: [{
		 'a.sf-button': {
			padding: '1px 6px',
			display: 'inline-block',
			textAlign: 'center',
			height: '23px',
			lineHeight: '23px',
			textDecoration: 'none'
		 }
		}, {
		 selector: ['button:hover', 'a.sf-button:hover'],
		 style: {
			backgroundColor: '#597A9E !important',
			borderColor: '#597A9E !important',
			color: '#fff'
		 }
		}]
			}
		})
	 })
	});

	popupContainer.appendChild(mediaIcon);
	popupContainer.appendChild(textContent);
	popupContainer.appendChild(buttonWrap);
	return {
	 icon: mediaIcon,
	 buttonContainer: buttonWrap,
	 textContainer: textContent,
	 body: popupContainer
	}
		},

		getM3U: function(links) {
	var text = '#EXTM3U\r\n';

	for (var i = 0; i < links.length; i++) {
	 if (!links[i].duration)
		links[i].duration = '-1';

	 if (links[i].title || links[i].duration) {
		text += '#EXTINF:' + links[i].duration + ',' +
			links[i].title + '\r\n';
	 }

	 text += links[i].url + '\r\n';
	}

	return text;
		},


		getPlaylistHtml: function(links, fileTitle) {
	if (!links || links.length == 0)
	 return;

	var links_len = links.length;

	var d = SaveFrom_Utils.dateToObj();
	var dateStr = d.year + '-' + d.month + '-' + d.day + ' ' +
	 d.hour + '-' + d.min;

	// M3U
	var m3uList = SaveFrom_Utils.playlist.getM3U(links);
	m3uList = m3uList.replace(/\r\n/g, '\n');

	var m3uUrl = mono.getDataUrl(m3uList, 'audio/x-mpegurl');

	var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();

	mono.sendMessage({
	 action: 'getWarningIcon',
	 color: '#00CCFF',
	 type: 'playlist'
	}, function(icon) {
	 template.icon.style.backgroundImage = 'url(' + icon + ')';
	});

	mono.create(template.textContainer, {
	 append: [
		mono.create('p', {
			text: fileTitle || mono.global.language.playlistTitle,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '13px'
			}
		}),
		mono.create('p', {
			text: mono.global.language.playlistInstruction,
			style: {
		color: '#868686',
		fontSize: '14px',
		marginBottom: '13px',
		lineHeight: '24px',
		marginTop: '0px'
			}
		}),
		mono.create('a', {
			text: mono.global.language.filelist + ' (' + links_len + ')',
			href: '#',
			class: 'sf__playlist',
			style: {
		display: 'none',
		fontSize: '14px',
		marginBottom: '13px',
		lineHeight: '24px',
		marginTop: '0px'
			}
		})
	 ]
	});

	if (!fileTitle) {
	 fileTitle = 'playlist';
	}
	fileTitle += ' ' + dateStr;

	mono.create(template.buttonContainer, {
	 append: [
		mono.create('a', {
			text: mono.global.language.download,
			href: m3uUrl,
			download: mono.fileName.modify(fileTitle + '.m3u'),
			class: 'sf-button',
			style: {
		width: '118px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		margin: '12px',
		marginBottom: '11px',
		marginRight: '8px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer'
			}
		})
	 ]
	});

	return template.body;
		},


		popupPlaylist: function(links, title, filelist, id) {
	var content = SaveFrom_Utils.playlist.getPlaylistHtml(links, title);
	if (!content)
	 return;

	var popup = SaveFrom_Utils.popupDiv(content, id);
	if (filelist) {
	 var a = popup.querySelector('a.sf__playlist');
	 if (a) {
		a.addEventListener('click', function(event) {
			setTimeout(function() {
		SaveFrom_Utils.playlist.popupFilelist(links, title, true, id);
			}, 100);
			event.preventDefault();
			return false;
		}, false);

		a.style.display = 'inline';
		a = null;
	 }
	}
	var dl_links = popup.querySelectorAll('a[download]');
	for (var i = 0, el; el = dl_links[i]; i++) {
	 el.addEventListener('click', SaveFrom_Utils.downloadOnClick, false);
	}
		}
	},

	popupCloseBtn: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAWUlEQVQ4y2NgGHHAH4j1sYjrQ+WIAvFA/B+I36MZpg8V+w9VQ9Al/5EwzDBkQ2AYr8uwaXiPQ0yfkKuwGUayIYQMI8kQqhlEFa9RLbCpFv1US5BUzSLDBAAARN9OlWGGF8kAAAAASUVORK5CYII=',

	popupDiv: function(content, id, maxWidth, maxHeight, onClose) {
		if (!id) {
	id = 'savefrom_popup_box';
		}

		if (!maxWidth)
	maxWidth = 580;

		if (!maxHeight)
	maxHeight = 520;

		var popupBody = document.getElementById(id);
		if (popupBody) {
	popupBody.parentNode.removeChild(popupBody);
		}

		popupBody = mono.create('div', {
	id: id,
	style: {
	 zIndex: '9999',
	 display: 'block',
	 cssFloat: 'none',
	 position: 'fixed',
	 margin: '0',
	 padding: '0',
	 visibility: 'hidden',
	 color: '#000',
	 background: '#fff',
	 border: '3px solid #c0cad5',
	 borderRadius: '7px',
	 overflow: 'auto'
	}
		});


		var cnt = mono.create('div', {
	style: {
	 display: 'block',
	 cssFloat: 'none',
	 position: 'relative',
	 overflow: 'auto',
	 margin: '0',
	 padding: '10px 15px'
	}
		});

		if (typeof content === 'function') {
	content(cnt);
		} else {
	cnt.appendChild(content);
		}

		var btn = mono.create('img', {
	src: SaveFrom_Utils.popupCloseBtn,
	alt: 'x',
	width: 18,
	height: 18,
	style: {
	 position: 'absolute',
	 top: '10px',
	 right: '15px',
	 opacity: '0.5',
	 cursor: 'pointer'
	},
	on: [
	 ['mouseenter', function() {
		"use strict";
		this.style.opacity = '0.9';
	 }],
	 ['mouseleave', function() {
		"use strict";
		this.style.opacity = '0.5';
	 }],
	 ['click', function() {
		"use strict";
		if (popupBody.parentNode) {
			popupBody.parentNode.removeChild(popupBody);
		}
		if (onClose) {
			onClose();
		}
		return false;
	 }]
	]
		});

		cnt.appendChild(btn);
		popupBody.appendChild(cnt);
		document.body.appendChild(popupBody);

		if (popupBody.offsetWidth > maxWidth) {
	popupBody.style.width = maxWidth + 'px';
		}

		if (popupBody.offsetHeight > maxHeight) {
	popupBody.style.height = maxHeight + 'px';
	popupBody.style.width = (maxWidth + 20) + 'px';
		}

		setTimeout(function() {
	var l = Math.floor((window.innerWidth - popupBody.offsetWidth) / 2.0);
	var t = Math.floor((window.innerHeight - popupBody.offsetHeight) / 2.0);
	if (t < 0) {
	 t = 0;
	}
	if (location.host.indexOf('youtu') !== -1 && t < 92) {
	 t = 92;
	 popupBody.style.height = (popupBody.offsetHeight - t - 10) + 'px';
	}
	if (l < 0) {
	 l = 0;
	}
	SaveFrom_Utils.setStyle(popupBody, {
	 top: t + 'px',
	 left: l + 'px',
	 visibility: 'visible'
	});
		});

		var onDocClose = function(event) {
	var node = event.target;
	if (node !== popupBody && !SaveFrom_Utils.isParent(node, popupBody)) {
	 if (popupBody.parentNode) {
		popupBody.parentNode.removeChild(popupBody);
	 }
	 document.removeEventListener('click', onDocClose, false);
	 if (onClose) {
		onClose();
	 }
	}
		};

		setTimeout(function() {
	document.addEventListener('click', onDocClose, false);
		}, 100);

		popupBody.addEventListener('close', function() {
	if (popupBody.parentNode) {
	 popupBody.parentNode.removeChild(popupBody);
	}
	document.removeEventListener('click', onDocClose, false);
	if (onClose) {
	 onClose();
	}
		});

		popupBody.addEventListener('kill', function() {
	if (popupBody.parentNode) {
	 popupBody.parentNode.removeChild(popupBody);
	}
	document.removeEventListener('click', onDocClose, false);
		});

		return popupBody;
	},

	popupDiv2: function(_details) {
		"use strict";
		var details = {
	id: 'savefrom_popup_box',
	containerStyle: null,
	bodyStyle: null,
	content: null,
	container: null,
	body: null
		};

		details._onClose = function() {
	document.removeEventListener('click', details._onClose);

	if (container.parentNode) {
	 container.parentNode.removeChild(container);
	}

	details.onClose && details.onClose();
		};

		mono.extend(details, _details);

		var container = details.container = mono.create('div', {
	id: details.id,
	style: {
	 zIndex: 9999,
	 display: 'block',
	 position: 'fixed',
	 background: '#fff',
	 border: '3px solid #c0cad5',
	 borderRadius: '7px'
	},
	append: [
	 mono.create('style', {
		text: mono.style2Text({
			selector: '#' + details.id,
			style: mono.styleReset
		})
	 })
	],
	on: [
	 ['click', function(e) {
		e.stopPropagation();
	 }]
	]
		});

		var closeBtn = mono.create('img', {
	src: SaveFrom_Utils.popupCloseBtn,
	alt: 'x',
	width: 18,
	height: 18,
	style: {
	 position: 'absolute',
	 top: '10px',
	 right: '15px',
	 opacity: '0.5',
	 cursor: 'pointer'
	},
	on: [
	 ['mouseenter', function() {
		"use strict";
		this.style.opacity = '0.9';
	 }],
	 ['mouseleave', function() {
		"use strict";
		this.style.opacity = '0.5';
	 }],
	 ['click', details._onClose]
	]
		});

		container.appendChild(closeBtn);

		var body = details.body = mono.create('div', {
	style: mono.extendPos({
	 display: 'block',
	 position: 'relative',
	 padding: '10px 15px',
	 overflow: 'auto'
	}, details.bodyStyle)
		});

		if (typeof details.content === 'function') {
	details.content(body);
		} else {
	body.appendChild(details.content);
		}

		container.appendChild(body);

		document.body.appendChild(container);
		document.addEventListener('click', details._onClose);

		return details;
	},

	// row - used for hide tooltip on mouseout
	// because node can dissaper from DOM before mouseout raised
	showTooltip: function(node, text, row, style) {
		if (!node)
	return;

		var tooltip = document.querySelector('.savefrom-tooltip');
		if (!tooltip) {
	tooltip = document.createElement('div');
	tooltip.className = 'savefrom-tooltip';
	SaveFrom_Utils.setStyle(tooltip, {
	 'position': 'absolute',
	 'opacity': 0,
	 'zIndex': -1
	});
	if (style) {
	 SaveFrom_Utils.setStyle(tooltip, style);
	}
		}

		tooltip.textContent = text;

		if (tooltip.lastNode && tooltip.lastNode === node) {
	fixPosition();
	return;
		}

		if (tooltip.lastNode) {
	mono.off(tooltip.lastNode, 'mouseleave', hide);
	mono.off(tooltip.lastNode, 'mousemove', fixPosition);
	tooltip.lastRow && mono.off(tooltip.lastRow, 'mouseleave', hide);
		}

		tooltip.lastNode = node;
		row && (tooltip.lastRow = row);

		mono.on(node, 'mouseleave', hide);
		mono.on(node, 'mousemove', fixPosition, false);
		row && mono.on(row, 'mouseleave', hide);

		document.body.appendChild(tooltip);
		fixPosition();

		function fixPosition(e) {
	if (e !== undefined) {
	 e.stopPropagation();
	}
	var p = SaveFrom_Utils.getPosition(node),
	 s = SaveFrom_Utils.getSize(tooltip);

	if (p.top == 0 && p.left == 0)
	 return;

	p.top = p.top - s.height - 10;
	p.left = p.left - s.width / 2 + SaveFrom_Utils.getSize(node).width / 2;

	p.left = Math.min(p.left, document.body.clientWidth + document.body.scrollLeft - s.width);
	if (p.top < document.body.scrollTop)
	 p.top = p.top + s.height + SaveFrom_Utils.getSize(node).height + 20;

	p.top += 'px';
	p.left += 'px';

	// show
	p.zIndex = 9999;
	p.opacity = 1;

	SaveFrom_Utils.setStyle(tooltip, p);
		}

		function hide() {
	if (tooltip.parentNode)
	 document.body.removeChild(tooltip);

	tooltip.lastNode = null;
	tooltip.lastRow = null;
	SaveFrom_Utils.setStyle(tooltip, {
	 zIndex: -1,
	 opacity: 0
	});
	mono.off(node, 'mouseleave', hide);
	mono.off(node, 'mousemove', fixPosition);
	row && mono.off(row, 'mouseleave', hide);
		}
	},

	embedDownloader: {
		dataAttr: 'data-savefrom-get-links',
		dataIdAttr: 'data-savefrom-container-id',
		containerClass: 'savefrom-links-container',
		linkClass: 'savefrom-link',
		panel: null,
		lastLink: null,
		style: null,

		hostings: {
	'youtube': {
	 re: [
		/^https?:\/\/(?:[a-z]+\.)?youtube\.com\/(?:#!?\/)?watch\?.*v=([\w\-]+)/i,
		/^https?:\/\/(?:[a-z0-9]+\.)?youtube\.com\/(?:embed|v)\/([\w\-]+)/i,
		/^https?:\/\/(?:[a-z]+\.)?youtu\.be\/([\w\-]+)/i
	 ],
	 action: 'getYoutubeLinks',
	 prepareLinks: function(links) {
		var ret = [];
		var sfUtilsYt = SaveFrom_Utils.video.yt;
		var format = sfUtilsYt.format;

		var meta = links.meta || {};

		for (var formatName in format) {
			for (var iTag in format[formatName]) {
		var metaTag = meta[iTag] || {};
		if (links[iTag]) {
		 var type = formatName;
		 if (format[formatName][iTag].ext) {
			type = format[formatName][iTag].ext;
		 }

		 var quality = format[formatName][iTag].quality;
		 if (metaTag.quality) {
			quality = metaTag.quality;
		 }

		 if (format[formatName][iTag].sFps) {
			quality += ' ' + (metaTag.fps || 60);
		 }

		 if (format[formatName][iTag]['3d']) {
			quality += ' (3d)';
		 }

		 ret.push({
			name: formatName + ' ' + quality,
			type: type,
			url: links[iTag],
			noSound: format[formatName][iTag].noAudio
		 });
		}
			}
		}

		return ret;
	 }
	},
	'vimeo': {
	 re: [
		/^https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/(?:\w+\#)?(\d+)/i,
		/^https?:\/\/player\.vimeo\.com\/video\/(\d+)/i,
		/^https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/channels\/(?:[^\/]+)\/(\d+)$/i,
		/^https?:\/\/vimeo\.com\/(?:.+)clip_id=(\d+)/i
	 ],
	 action: 'getVimeoLinks',
	 prepareLinks: function(links) {
		return links.map(function(link) {
			var ext = link.ext;
			if (!ext) {
		ext = 'MP4';
		if (link.url.search(/\.flv($|\?)/i) != -1)
		 ext = 'FLV';
			}

			link.name = link.name ? link.name : ext;
			link.type = link.type ? link.type : ext;
			link.ext = ext;

			return link;
		});
	 }
	},

	'vk': {
	 re: [
		/^https?:\/\/(?:[\w\-]+\.)?(?:vk\.com|vkontakte\.ru)\/(?:[^\/]+\/)*(?:[\w\-\.]+\?.*z=)?(video-?\d+_-?\d+\?list=[0-9a-z]+|video-?\d+_-?\d+)/i,
		/^https?:\/\/(?:[\w\-]+\.)?(?:vk\.com|vkontakte\.ru)\/video_ext\.php\?(.+)/i
	 ],
	 action: 'getVKLinks'
	},

	'dailymotion': {
	 re: [
		/^http:\/\/(?:www\.)?dai\.ly\/([a-z0-9]+)_?/i,
		/^https?:\/\/(?:[\w]+\.)?dailymotion\.com(?:\/embed|\/swf)?\/video\/([a-z0-9]+)_?/i
	 ],
	 action: 'getDailymotionLinks'
	},

	'facebook': {
	 re: [
		/^https?:\/\/(?:[\w]+\.)?facebook\.com(?:\/video)?\/video.php.*[?&]{1}v=([0-9]+).*/i,
		/^https?:\/\/(?:[\w]+\.)?facebook\.com\/.+\/videos(?:\/\w[^\/]+)?\/(\d+)/i
	 ],
	 action: 'getFacebookLinks'
	}
		},


		init: function(style) {
	this.style = style;

	if (this.panel) {
	 SaveFrom_Utils.popupMenu.removePanel();
	}

	this.panel = null;
	this.lastLink = null;

	var links = document.querySelectorAll('a[' + this.dataAttr + ']'),
	 i, l = links.length;

	for (i = 0; i < l; i++) {
	 if (['savefrom.net', 'sf-addon.com'].indexOf(
			SaveFrom_Utils.getTopLevelDomain(links[i].hostname)) > -1) {
		links[i].removeEventListener('click', this.onClick, false);
		links[i].addEventListener('click', this.onClick, false);
	 }
	}

	// hide menu on click outside them
	// process dinamically added links
	if (document.body) {
	 document.body.removeEventListener('click', this.onBodyClick, true);
	 document.body.addEventListener('click', this.onBodyClick, true);
	}
		},


		checkUrl: function(url) {
	for (var hosting in this.hostings) {
	 var params = this.hostings[hosting];

	 for (var i = 0, len = params.re.length; i < len; i++) {
		var match = url.match(params.re[i]);
		if (match) {
			return {
		hosting: hosting,
		action: params.action,
		extVideoId: match[1]
			};
		}
	 }
	}

	return null;
		},

		reMapHosting: function(action) {
	var map = {
	 'getYoutubeLinks': 'youtube',
	 'getVimeoLinks': 'vimeo',
	 'getDailymotionLinks': 'dailymotion',
	 'getFacebookLinks': 'facebook',
	 'getVKLinks': 'vk'
	};

	return map[action];
		},


		onClick: function(event, a) {
	var _this = SaveFrom_Utils.embedDownloader;

	if (!a) {
	 a = event.target;
	 while (a.parentNode) {
		if (a.nodeName === 'A')
			break;
		a = a.parentNode;
	 }

	 if (!a)
		return;
	}

	var href = a.getAttribute('data-savefrom-get-links');
	if (!href)
	 return;

	if (event.button !== 0 || event.ctrlKey || event.shiftKey)
	 return;

	if (_this.lastLink === a && _this.panel && _this.panel.style.display != 'none') {
	 _this.lastLink = null;
	 _this.panel.style.display = 'none';

	 event.preventDefault();
	 event.stopPropagation();
	 return;
	}

	_this.lastLink = a;
	var data = _this.checkUrl(href);
	if (!data)
	 return;

	event.preventDefault();
	event.stopPropagation();

	var request = {
	 action: data.action,
	 extVideoId: data.extVideoId
	};

	_this.showLinks(mono.global.language.download + ' ...', null, a);

	mono.sendMessage(request, function(response) {
	 var hosting = data.hosting;

	 if (response.action != request.action) {
		hosting = _this.reMapHosting(response.action);
	 }

	 if (response.links)
		_this.showLinks(response.links, response.title, a, hosting, true);
	 else
		_this.showLinks(mono.global.language.noLinksFound, null, a, undefined, true);
	});

	return false;
		},


		onBodyClick: function(event) {
	var _this = SaveFrom_Utils.embedDownloader;

	var node = event.target;

	if (!_this.panel || _this.panel.style.display == 'none') {
	 if (node.tagName !== 'A' && mono.matches(node, 'A ' + node.tagName)) {
		while (node.parentNode) {
			if (node.tagName === 'A') {
		break;
			}
			node = node.parentNode;
		}
	 }

	 if (node.nodeName !== 'A') {
		return;
	 }

	 // dinamic links
	 if (node.hasAttribute(_this.dataAttr) && ['savefrom.net', 'sf-addon.com'].indexOf(SaveFrom_Utils.getTopLevelDomain(node.hostname)) > -1) {
		return _this.onClick(event, node);
	 }

	 return;
	}

	if (_this.panel === node || _this.panel.contains(node)) {
	 return;
	}

	_this.lastLink = null;
	_this.panel.style.display = 'none';

	event.preventDefault();
	event.stopPropagation();
		},

		hidePanel: function() {
	if (this.panel) {
	 this.panel.style.display = 'none';
	}
		},

		createMenu: function(links, title, a, hname, update) {
	var menuLinks = mono.global.language.noLinksFound;
	if (typeof links === 'string') {
	 menuLinks = links;
	} else
	if (SaveFrom_Utils.popupMenu.prepareLinks[hname] !== undefined && links) {
	 menuLinks = SaveFrom_Utils.popupMenu.prepareLinks[hname](links, title);
	}
	var options = {
	 links: menuLinks,
	 button: a,
	 popupId: undefined,
	 showFileSize: true,
	 containerClass: this.containerClass,
	 linkClass: this.linkClass,
	 style: {
		popup: (this.style) ? this.style.container : undefined,
		item: (this.style) ? this.style.link : undefined
	 },
	 isUpdate: update
	};
	if (update && this.panel) {
	 SaveFrom_Utils.popupMenu.update(this.panel, options)
	} else {
	 this.panel = SaveFrom_Utils.popupMenu.create(options);
	}
		},

		showLinks: function(links, title, a, hname, update) {
	var panel, id = a.getAttribute(this.dataIdAttr);
	if (id)
	 panel = document.getElementById(id);

	if (!panel) {
	 this.createMenu(links, title, a, hname, update);

	 return;
	} else if (this.panel) {
	 this.panel.style.display = 'none';
	}

	if (typeof(links) == 'string') {
	 panel.textContent = links;
	} else if (!links || links.length == 0) {
	 panel.textContent = mono.global.language.noLinksFound;
	} else {
	 // append links
	 if (hname && this.hostings[hname] && this.hostings[hname].prepareLinks)
		links = this.hostings[hname].prepareLinks(links);

	 panel.textContent = '';

	 for (var i = 0; i < links.length; i++) {
		if (links[i].url && links[i].name) {
			var a = document.createElement('a');
			a.href = links[i].url;
			a.title = mono.global.language.downloadTitle;
			a.appendChild(document.createTextNode(links[i].name));
			var span = document.createElement('span');
			span.className = this.linkClass;

			span.appendChild(a);
			panel.appendChild(span);

			SaveFrom_Utils.appendFileSizeIcon(a);
			if (links[i].noSound)
		SaveFrom_Utils.appendNoSoundIcon(a);

			if (title && !links[i].noTitle && links[i].type) {
		a.setAttribute('download', mono.fileName.modify(
		 title + '.' + links[i].type.toLowerCase()));

		a.addEventListener('click', SaveFrom_Utils.downloadOnClick, false);
			}
		}
	 }
	}
		}
	},
	createUmmyInfo: function(details, targetNode) {
		"use strict";
		details = details || {};

		var params = mono.extend({
	vid: 111,
	utm_source: 'savefrom-helper',
	utm_medium: 'youtube-helper'
		}, details.params);

		if (!params.utm_campaign) {
	if (details.itemType === 'hd') {
	 params.utm_campaign = 'youtube-helper-hd';
	} else
	if (details.itemType === 'mp3') {
	 params.utm_campaign = 'youtube-helper-mp3';
	}
		}

		var ummyUrl;
		if (/^Mac/.test(navigator.platform) && /^yt-/.test(params.video)) {
	ummyUrl = 'http://videodownloader.ummy.net/save-from-youtube.html?' + mono.param({
	 vid: params.vid,
	 video: params.video,
	 utm_source: 'savefrom-helper',
	 utm_medium: 'youtube-helper',
	 utm_campaign: 'ummy',
	 utm_content: 'ummy_integration_h'
	});
		} else {
	ummyUrl = 'http://videodownloader.ummy.net/?' + mono.param(params);
		}


		var themeShadowArrowDirStyle, themeArrowDirStyle, themeInfoPopup, themeLinkColor;

		var shadowArrowDirStyle, arrowDirStyle, containerDirArrow;
		if (details.posLeft) {
	shadowArrowDirStyle = {
	 border: '8px solid transparent',
	 borderLeft: '10px solid rgb(192, 187, 187)',
	 borderRight: 0,
	 top: '8px',
	 right: '11px'
	};

	arrowDirStyle = mono.extend({}, shadowArrowDirStyle, {
	 right: '12px',
	 borderLeft: '10px solid #fff'
	});

	containerDirArrow = {
	 right: '21px'
	};

	if (details.darkTheme) {
	 themeShadowArrowDirStyle = {
		borderLeftColor: 'rgba(255, 255, 255, 0.4)'
	 };

	 themeArrowDirStyle = {
		borderLeftColor: 'rgba(28,28,28, 0.6)'
	 };
	}
		} else {
	shadowArrowDirStyle = {
	 border: '8px solid transparent',
	 borderRight: '10px solid rgb(192, 187, 187)',
	 borderLeft: 0,
	 top: '8px',
	 left: '11px'
	};

	arrowDirStyle = mono.extend({}, shadowArrowDirStyle, {
	 left: '12px',
	 borderRight: '10px solid #fff'
	});

	containerDirArrow = {
	 left: '21px'
	};

	if (details.darkTheme) {
	 themeShadowArrowDirStyle = {
		borderRightColor: '#fff'
	 };

	 themeArrowDirStyle = {
		borderRightColor: '#000'
	 };
	}
		}

		if (details.darkTheme) {
	themeLinkColor = {
	 'a': {
		color: '#eee'
	 }
	};
		} else {
	themeLinkColor = {};
		}

		if (details.darkTheme) {
	themeInfoPopup = {
	 backgroundColor: 'rgba(28,28,28,0.8)',
	 border: '1px solid rgba(255, 255, 255, 0.4)',
	 color: '#fff'
	};
		} else {
	themeInfoPopup = {
	 backgroundColor: '#fff',
	 border: '1px solid #ccc'
	};
		}


		var arrow = mono.create(document.createDocumentFragment(), {
	append: [
	 mono.create('span', {
		style: mono.extend({
			display: 'inline-block',
			width: 0,
			position: 'absolute'
		}, shadowArrowDirStyle, themeShadowArrowDirStyle)
	 }),
	 mono.create('span', {
		style: mono.extend({
			display: 'inline-block',
			width: 0,
			position: 'absolute',
			zIndex: 1
		}, arrowDirStyle, themeArrowDirStyle)
	 })
	]
		});

		var showUmmyInfoNode = null;
		var info = null;
		var infoContainer = mono.create('div', {
	class: 'sf-ummy-info-popup-container',
	style: {
	 position: 'absolute',
	 zIndex: 9999
	},
	append: [
	 arrow,
	 info = mono.create('div', {
		class: 'sf-ummy-info-popup',
		style: mono.extend({
			position: 'relative',
			backgroundColor: '#fff',
			border: '1px solid #ccc',
			padding: '6px 5px',
			textAlign: 'center',
			maxWidth: '240px',
			lineHeight: '16px',
			fontSize: '12px',
			fontFamily: 'arial, sans-serif',
			cursor: 'default'
		}, containerDirArrow, themeInfoPopup),
		append: [
			mono.parseTemplate(mono.global.language.ummyMenuInfo.replace(
		'{url}', ummyUrl
			)),
			mono.create('label', {
		style: {
		 verticalAlign: 'middle',
		 display: 'block'
		},
		append: [
		 showUmmyInfoNode = mono.create('input', {
			type: 'checkbox',
			name: 'showUmmyInfo',
			style: {
				verticalAlign: 'middle'
			}
		 }),
		 mono.global.language.tooltipHide
		]
			}),
			mono.create('style', {
		text: mono.style2Text({
		 '.sf-ummy-info-popup': {
			append: mono.extend({
				'> p': {
			margin: '0 0 .8em 0'
				},
				'> p.center': {
			textAlign: 'center'
				},
				'> p > .green-btn-2.arrow': {
			color: '#fff',
			background: '#84bd07',
			borderRadius: '5px',
			display: 'inline-block',
			position: 'relative',
			lineHeight: 1,
			padding: '8px 34px 8px 10px',
			textDecoration: 'none',
			fontSize: '12px'
				},
				'> p > .green-btn-2.arrow:hover': {
			color: '#fff',
			opacity: 0.8
				},
				'> p > .green-btn-2.arrow:after': {
			background: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAjklEQVQoke3RsRGCQBCF4YuJsQDoQMpjKMImtAjth9xMEj4DF4c5QDH3n7lk773b3XsJNzTpR9DglrwYcUG9w1iHdoTpgYkBJ5QrxkPcDXNDQm/JHR2KOF3UcvoUgnZL8KFBi2I+Yrk2YsZjsaIsBVQ4i08KxqhVu1OYBLji+E/hzTKFlV13pfAVGynkPAFtrlNTMRczMgAAAABJRU5ErkJggg==) 0 0 no-repeat',
			content: '""',
			display: 'block',
			position: 'absolute',
			width: '16px',
			height: '14px',
			top: '50%',
			right: '10px',
			marginTop: '-7px'
				},
				'input': {
			display: 'inline-block'
				}
			}, themeLinkColor)
		 }
		})
			})
		]
	 })
	],
	on: [
	 ['mouseclick', function(e) {
		e.stopPropagation();
	 }],
	 ['mousedown', function(e) {
		e.stopPropagation();
	 }]
	]
		});

		mono.sendMessage({
	action: 'getUmmyIcon'
		}, function(dataImg) {
	var icon = info.querySelector('img');
	if (!icon) {
	 return;
	}
	icon.src = dataImg;
	icon.style.verticalAlign = 'text-bottom';
		});

		showUmmyInfoNode.checked = !mono.global.preference.showUmmyInfo;
		showUmmyInfoNode.addEventListener('change', function(e) {
	e.preventDefault();
	e.stopPropagation();

	if (this.checked) {
	 mono.trigger(targetNode, 'sfRmInfoPopup');
	 mono.global.preference.showUmmyInfo = 0;
	} else {
	 mono.trigger(targetNode, 'sfAddInfoPopup');
	 mono.global.preference.showUmmyInfo = 1;
	}

	mono.sendMessage({
	 action: 'updateOption',
	 key: 'showUmmyInfo',
	 value: mono.global.preference.showUmmyInfo
	});
		});

		details.onCreateUmmyInfo && details.onCreateUmmyInfo(infoContainer);

		return infoContainer;
	},
	bindUmmyInfo: function(container, details) {
		"use strict";
		details = details || {};
		// menu
		if (!details.noUmmy && !mono.global.preference.showUmmyInfo) {
	return;
		}
		if (details.widthLimit && document.documentElement.offsetWidth < details.widthLimit) {
	return;
		}

		details.leftOffset = details.leftOffset || 21;

		var infoPopup = null;
		var infoPopupShowTimer = null;
		var positionTop = null;

		var popupArrowTop = 8;
		var popupArrow = null;
		var popupArrowShadow = null;

		var killTimer = null;
		var killTimerUpdate = function() {
	clearTimeout(killTimer);
	killTimer = setTimeout(function() {
	 if (infoPopup && infoPopup.parentNode) {
		if (infoPopup.style.display !== 'none') {
			return killTimerUpdate();
		}
		infoPopup.parentNode.removeChild(infoPopup);
	 }
	}, 30 * 1000);
		};

		var fixPosition = function() {
	setTimeout(function() {
	 var windowHeight = window.innerHeight;
	 var infoHeight = infoPopup.clientHeight;
	 var scrollY = window.scrollY;
	 if (infoHeight + positionTop > windowHeight + scrollY) {
		var newPositionTop = windowHeight - infoHeight + scrollY;
		if (newPositionTop < 0) {
			return;
		}

		if (positionTop === newPositionTop) {
			return;
		}

		infoPopup.style.top = newPositionTop + 'px';

		var raz = 8 - (windowHeight - (infoHeight + positionTop) + scrollY);
		if (popupArrowTop !== raz) {
			popupArrowTop = raz;
			popupArrow.style.top = popupArrowTop + 'px';
			popupArrowShadow.style.top = popupArrowTop + 'px';
		}
	 } else {
		if (popupArrowTop !== 8) {
			popupArrowTop = 8;
			popupArrow.style.top = popupArrowTop + 'px';
			popupArrowShadow.style.top = popupArrowTop + 'px';
		}
	 }
	});
		};

		var onMouseLeave = function() {
	clearTimeout(infoPopupShowTimer);
	infoPopupShowTimer = setTimeout(function() {
	 infoPopup && (infoPopup.style.display = 'none');
	}, 50);
		};

		var updateLeftPos = function(el) {
	var position = SaveFrom_Utils.getPosition(el);
	if (details.posLeft) {
	 infoPopup.style.right = (document.documentElement.clientWidth - position.left - details.leftOffset) + 'px';
	} else {
	 var size = SaveFrom_Utils.getSize(el);
	 infoPopup.style.left = (size.width + position.left - details.leftOffset) + 'px';
	}
		};

		var onMouseEnter = function() {
	if (!details.noUmmy && !mono.global.preference.showUmmyInfo) {
	 return;
	}

	clearTimeout(infoPopupShowTimer);

	var position = SaveFrom_Utils.getPosition(container);

	if (!infoPopup) {
	 if (details.expUmmyInfo) {
		infoPopup = details.expUmmyInfo(details.createUmmyInfoDetails, container);
	 } else {
		infoPopup = SaveFrom_Utils.createUmmyInfo(details.createUmmyInfoDetails, container);
	 }

	 popupArrow = infoPopup.firstChild;
	 popupArrowShadow = popupArrow.nextElementSibling;

	 positionTop = position.top - 4;

	 mono.on(infoPopup, 'mouseenter', function() {
		clearTimeout(infoPopupShowTimer);
	 });

	 mono.on(infoPopup, 'mouseleave', onMouseLeave);
	} else {
	 positionTop = position.top - 4;
	}

	infoPopup.style.top = positionTop + 'px';

	if (infoPopup.dataset.hide === '1') {
	 return;
	}

	updateLeftPos(container);

	if (!infoPopup.parentNode) {
	 infoPopup.style.display = 'none';
	 (details.container || document.body).appendChild(infoPopup);
	}

	if (infoPopup.style.display !== 'block') {
	 infoPopup.style.display = 'block';
	 mono.trigger(infoPopup, 'sfShowInfoPopup');
	}

	fixPosition();

	killTimerUpdate();
		};

		mono.on(container, 'mouseenter', onMouseEnter);
		mono.on(container, 'mouseleave', onMouseLeave);

		mono.on(container, 'sfRmInfoPopup', function() {
	mono.off(container, 'mouseenter', onMouseEnter);
		});

		mono.on(container, 'sfAddInfoPopup', function() {
	mono.on(container, 'mouseenter', onMouseEnter);
		});
	},

	popupMenu: {
		popupId: 'sf_popupMenu',
		popup: undefined,
		popupStyle: undefined,
		dataArrtVisible: 'data-isVisible',
		extStyleCache: undefined,
		ummyIcon: null,

		badgeQualityList: ['8K', '4K', '2160', '1440', '1080', '720', 'ummy'],
		createBadge: function(qulity, options) {
	var _this = this;
	options = options || {};
	var style = {
	 display: 'inline-block',
	 lineHeight: '18px',
	 width: '19px',
	 height: '17px',
	 color: '#fff',
	 fontSize: '12px',
	 borderRadius: '2px',
	 verticalAlign: 'middle',
	 textAlign: 'center',
	 paddingRight: '2px',
	 fontWeight: 'bold',
	 marginLeft: '3px'
	};
	for (var key in options.containerStyle) {
	 style[key] = options.containerStyle[key];
	}

	var container = mono.create('div', {
	 style: style
	});

	if (qulity === '1080' || qulity === '2160' || qulity === '1440' || qulity === '720') {
	 container.textContent = 'HD';
	 container.style.backgroundColor = '#505050';
	 container.style.paddingRight = '1px';
	} else
	if (qulity === '8K' || qulity === '4K') {
	 container.textContent = 'HD';
	 container.style.paddingRight = '1px';
	 container.style.backgroundColor = 'rgb(247, 180, 6)';
	} else
	if (qulity === 'mp3' || qulity === 'MP3') {
	 container.textContent = 'MP3';
	 container.style.width = '26px';
	 container.style.paddingRight = '1px';
	 container.style.backgroundColor = '#505050';
	} else
	if (qulity === 'ummy') {
	 if (this.ummyIcon) {
		container.style.background = 'url(' + this.ummyIcon + ') center center no-repeat';
	 } else {
		mono.sendMessage({
			action: 'getUmmyIcon'
		}, function(dataImg) {
			container.style.background = 'url(' + (_this.ummyIcon = dataImg) + ') center center no-repeat';
		});
	 }
	}
	return container;
		},

		getTitleNode: function(link) {
	"use strict";
	var _this = SaveFrom_Utils.popupMenu;

	var titleContainer = mono.create('span', {
	 style: {
		cssFloat: 'left'
	 }
	});

	if (link.extra === 'converter') {
	 var badge = document.createDocumentFragment();
	 if (['MP3', '8K', '4K', '1440', '1080', '720'].indexOf(link.format) !== -1) {
		badge.appendChild(_this.createBadge(link.format, {
			containerStyle: {
		marginLeft: 0
			}
		}));
	 } else {
		badge.appendChild(document.createTextNode(link.format));
	 }
	 mono.create(titleContainer, {
		append: [badge, ' ', link.quality]
	 });
	 badge = null;
	} else
	if (link.quality === 'ummy') {
	 // ummy hook
	 var badge = document.createDocumentFragment();
	 if (link.uQuality !== null) {
		if (['8K', '4K', '1440', '1080', '720'].indexOf(link.uQuality) !== -1) {
			badge.appendChild(document.createTextNode(link.uQuality));
		} else {
			badge.appendChild(_this.createBadge(link.uQuality, {
		containerStyle: {
		 marginLeft: 0
		}
			}));
		}
	 }
	 mono.create(titleContainer, {
		append: [badge, ' ', 'Ummy']
	 });
	 badge = null;
	} else
	if (link.itemText) {
	 titleContainer.textContent = link.itemText;
	} else {
	 var titleQuality = link.quality ? ' ' + link.quality : '';
	 var titleFormat = link.format ? link.format : '???';
	 var title3D = link['3d'] ? '3D ' : '';
	 var titleFps = '';
	 if (link.sFps) {
		titleFps += ' ' + (link.fps || 60);
	 }
	 titleContainer.textContent = title3D + titleFormat + titleQuality + titleFps;
	}

	if (_this.badgeQualityList.indexOf(String(link.quality)) !== -1) {
	 titleContainer.appendChild(_this.createBadge(String(link.quality)));
	}

	return titleContainer;
		},

		createPopupItem: function(listItem, options) {
	var _this = SaveFrom_Utils.popupMenu;

	var href;
	if (typeof listItem === 'string') {
	 href = listItem;
	} else {
	 href = listItem.href;
	}

	if (href === '-') {
	 var line = mono.create('div', {
		style: {
			display: 'block',
			margin: '1px 0',
			borderTop: '1px solid rgb(214, 214, 214)'
		}
	 });
	 return {
		el: line
	 };
	}

	var itemContainer = document.createElement((href === '-text-') ? 'div' : 'a');
	if (options.linkClass) {
	 itemContainer.classList.add(options.linkClass);
	}
	var itemContainerStyle = {
	 display: 'block',
	 padding: '0 5px',
	 textDecoration: 'none',
	 whiteSpace: 'nowrap',
	 overflow: 'hidden'
	};
	if (listItem.isHidden) {
	 itemContainer.setAttribute(_this.dataArrtVisible, '0');
	 itemContainerStyle.display = 'none';
	}
	SaveFrom_Utils.setStyle(itemContainer, itemContainerStyle);

	if (href === '-text-') {
	 itemContainer.style.lineHeight = '22px';
	 return {
		el: itemContainer
	 };
	}

	itemContainer.href = href;

	if (href === '#') {
	 return {
		el: itemContainer
	 };
	}

	if (mono.isGM || mono.isOpera || mono.isSafari) {
	 if (listItem.quality !== 'ummy' && !listItem.extra) {
		itemContainer.title = mono.global.language.downloadTitle;
	 }
	}

	if (listItem.forceDownload) {
	 var filename = '';
	 if (listItem.title) {
		var ext = (listItem.ext || listItem.format || '').toLowerCase();
		if (ext) {
			ext = '.' + ext;
		}
		filename = listItem.title + ext;
	 }
	 itemContainer.setAttribute('download', mono.fileName.modify(filename));
	 itemContainer.addEventListener('click', function(event) {
		SaveFrom_Utils.downloadOnClick(event, null, {
			el: this
		});
	 }, false);
	}

	var onItemClickList = [];
	if (listItem.func) {
	 if (Array.isArray(listItem.func)) {
		onItemClickList.push.apply(onItemClickList, listItem.func);
	 } else {
		onItemClickList.push(listItem.func);
	 }
	}
	if (options.onItemClick && onItemClickList.indexOf(options.onItemClick) === -1) {
	 onItemClickList.push(options.onItemClick);
	}

	if (onItemClickList.length) {
	 itemContainer.addEventListener('click', function(e) {
		var _this = this;
		onItemClickList.forEach(function(cb) {
			"use strict";
			return cb.call(_this, e, listItem);
		});
	 }, false);
	}

	if (listItem.isBlank) {
	 itemContainer.setAttribute('target', '_blank');
	}

	itemContainer.appendChild(_this.getTitleNode(listItem));

	var infoConteiner = mono.create('span', {
	 style: {
		cssFloat: 'right',
		lineHeight: '22px',
		height: '22px'
	 }
	});
	var sizeIconStyle = {
	 top: '5px',
	 verticalAlign: 'top'
	};
	for (var key in options.sizeIconStyle) {
	 sizeIconStyle[key] = options.sizeIconStyle[key];
	}
	var sizeIconTextStyle = {
	 marginLeft: 0
	};

	if (listItem.noAudio) {
	 SaveFrom_Utils.appendNoSoundIcon(infoConteiner, sizeIconStyle);
	}

	var sizeIconNode = null;
	if (!listItem.noSize) {
	 infoConteiner.addEventListener('click', function onClick(e) {
		if (infoConteiner.firstChild.tagName === 'IMG') {
			e.preventDefault();
			e.stopPropagation();
			mono.trigger(infoConteiner.firstChild, 'click', {
		cancelable: true
			});
		}
		this.removeEventListener('click', onClick);
	 });
	 sizeIconNode = SaveFrom_Utils.appendFileSizeIcon(itemContainer, sizeIconStyle, sizeIconTextStyle, undefined, true, infoConteiner, listItem);
	}

	itemContainer.appendChild(infoConteiner);

	if (listItem.quality === 'ummy') {
	 var bInfoDetails = mono.extend({}, options.bindUmmyInfoDetails);
	 var cInfoDetails = bInfoDetails.createUmmyInfoDetails = mono.extend({
		itemType: listItem.uIsAudio ? 'mp3' : 'hd'
	 }, bInfoDetails.createUmmyInfoDetails);
	 cInfoDetails.params = mono.extend({
		video: listItem.videoId,
		vid: listItem.vid
	 }, cInfoDetails.params);
	 SaveFrom_Utils.bindUmmyInfo(itemContainer, bInfoDetails);
	}

	return {
	 el: itemContainer,
	 sizeIcon: sizeIconNode,
	 prop: listItem
	};
		},

		sortMenuItems: function(list, options) {
	if (options === undefined) {
	 options = {};
	}
	var formatPriority = ['ummy', 'Audio Opus', 'Audio Vorbis', 'Audio AAC', '3GP', 'WebM', 'FLV', 'MP4'];
	var strQuality = {
	 Mobile: 280,
	 LD: 280,
	 SD: 360,
	 HD: 720,
	 ummy: 1
	};
	if (options.strQualityExtend) {
	 mono.extend(strQuality, options.strQualityExtend);
	}
	var sizePriority = {};
	var bitratePriority = [];
	var defList = [];
	var audioList = [];
	var subtitleList = [];
	var mute60List = [];
	var muteList = [];
	var _3dList = [];
	var unkList = [];

	list.forEach(function(item) {
	 var prop = item.prop;
	 if (options.noProp) {
		prop = item;
	 }
	 var sortOptions = prop.sort || {};
	 if (!prop.format) {
		unkList.push(item);
		return 1;
	 }
	 if (prop.isOther) {
		unkList.push(item);
	 } else
	 if (prop.isSubtitle) {
		subtitleList.push(item);
	 } else
	 if (prop.noVideo) {
		bitratePriority[prop.quality] = parseInt(prop.quality);
		audioList.push(item);
	 } else {
		var size = sortOptions.size || strQuality[prop.quality] || -1;
		if (size === -1) {
			if (String(prop.quality).substr(-1) === 'K') {
		size = parseInt(prop.quality) * 1000;
			} else {
		size = parseInt(prop.quality);
			}
		}
		if (options.maxSize && size > options.maxSize) {
			return 1;
		}
		if (options.minSize && size < options.minSize) {
			return 1;
		}
		sizePriority[prop.quality] = size;
		if (prop.noAudio) {
			if (prop.sFps) {
		mute60List.push(item);
			} else {
		muteList.push(item);
			}
		} else
		if (prop['3d']) {
			_3dList.push(item);
		} else {
			defList.push(item);
		}
	 }
	});
	var sizeCompare = function(a, b) {
	 return sizePriority[a.quality] > sizePriority[b.quality] ? -1 : sizePriority[a.quality] === sizePriority[b.quality] ? 0 : 1;
	};
	var bitrateCompare = function(a, b) {
	 return bitratePriority[a.quality] > bitratePriority[b.quality] ? -1 : (bitratePriority[a.quality] === bitratePriority[b.quality]) ? 0 : 1;
	};
	var formatCompare = function(a, b) {
	 if (a.noVideo && b.noVideo) {
		return bitrateCompare(a, b);
	 }
	 if (a.noVideo) {
		return 1;
	 }
	 if (b.noVideo) {
		return -1;
	 }
	 return formatPriority.indexOf(a.format) > formatPriority.indexOf(b.format) ? -1 : formatPriority.indexOf(a.format) === formatPriority.indexOf(b.format) ? 0 : 1;
	};

	var compare = function(aa, bb) {
	 var a = aa.prop;
	 var b = bb.prop;
	 if (options.noProp) {
		a = aa;
		b = bb;
	 }

	 var size = sizeCompare(a, b);
	 if (size !== 0) {
		return size;
	 }
	 return formatCompare(a, b);
	};
	defList.sort(compare);
	_3dList.sort(compare);
	audioList.sort(compare);
	mute60List.sort(compare);
	muteList.sort(compare);

	var resList = null;
	if (options.typeList) {
	 resList = [];
	 if (options.typeList.indexOf('video') !== -1) {
		resList = resList.concat(defList);
	 }
	 if (options.typeList.indexOf('3d') !== -1) {
		resList = resList.concat(_3dList);
	 }
	 if (options.typeList.indexOf('audio') !== -1) {
		resList = resList.concat(audioList);
	 }
	 if (options.typeList.indexOf('mute') !== -1) {
		resList = resList.concat(muteList);
	 }
	 if (options.typeList.indexOf('mute60') !== -1) {
		resList = resList.concat(mute60List);
	 }
	 if (options.typeList.indexOf('subtitles') !== -1) {
		resList = resList.concat(subtitleList);
	 }
	 if (options.typeList.indexOf('other') !== -1) {
		resList = resList.concat(unkList);
	 }
	} else {
	 resList = defList.concat(_3dList, audioList, subtitleList, mute60List, muteList, unkList);
	}
	if (options.groupCompare) {
	 resList.sort(compare);
	}
	return resList;
		},

		removePanel: function() {
	if (this.popup.parentNode !== null) {
	 this.popup.parentNode.removeChild(this.popup);
	}
	if (this.popupStyle !== undefined && this.popupStyle.parentNode !== null) {
	 this.popupStyle.parentNode.removeChild(this.popupStyle);
	}
	this.popup = undefined;
	this.popupStyle = undefined;
		},

		getHiddenList: function(hiddenList, options) {
	"use strict";
	var _this = this;
	var content = document.createDocumentFragment();
	var scrollListItemCount = 8;
	if (hiddenList.length < scrollListItemCount) {
	 mono.create(content, {
		append: hiddenList
	 });
	} else {
	 var scrollContainer = mono.create('div', {
		style: {
			maxHeight: (scrollListItemCount * 24) + 'px',
			overflowY: 'scroll',
			display: 'none'
		},
		on: [
			['wheel', function(e) {
		if (e.wheelDeltaY > 0 && this.scrollTop === 0) {
		 e.preventDefault();
		} else
		if (e.wheelDeltaY < 0 && this.scrollHeight - (this.offsetHeight + this.scrollTop) <= 0) {
		 e.preventDefault();
		}
			}],
			(function() {
		var hasTopShadow = false;
		return ['scroll', function() {
		 if (this.scrollTop !== 0) {
			if (hasTopShadow) {
				return;
			}
			hasTopShadow = true;
			this.style.boxShadow = 'rgba(0, 0, 0, 0.40) -2px 1px 2px 0px inset';
		 } else {
			if (!hasTopShadow) {
				return;
			}
			hasTopShadow = false;
			this.style.boxShadow = '';
		 }
		}];
			})()
		],
		append: hiddenList
	 });
	 scrollContainer.setAttribute(_this.dataArrtVisible, '0');

	 content.appendChild(scrollContainer);
	}

	var separator = _this.createPopupItem('-', options).el;
	content.appendChild(separator);

	var moreItem = _this.createPopupItem('#', options).el;
	mono.create(moreItem, {
	 text: mono.global.language.more + ' ' + String.fromCharCode(187), //171 //160 - space
	 data: {
		visible: '0'
	 },
	 on: ['click', function(e) {
		e.preventDefault();
		var state = this.dataset.visible;
		var symbol;
		if (state > 0) {
			state--;
			symbol = 187;
		} else {
			state++;
			symbol = 171;
		}
		this.textContent = mono.global.language.more + ' ' + String.fromCharCode(symbol);
		this.dataset.visible = state;
		var itemList = this.parentNode.querySelectorAll('*[' + _this.dataArrtVisible + ']');
		for (var i = 0, item; item = itemList[i]; i++) {
			if (state === 1) {
		item.style.display = 'block';
			} else {
		item.style.display = 'none';
			}
			item.setAttribute(_this.dataArrtVisible, state);
		}
	 }]
	});
	content.appendChild(moreItem);

	if (options.visibleCount === 0) {
	 mono.trigger(moreItem, 'click', {
		cancelable: true
	 });
	}

	return content;
		},

		getContent: function(options) {
	"use strict";
	var _this = this;
	var links = options.links;

	var content = document.createDocumentFragment();

	var sizeIconList = [];

	if (typeof(links) === 'string') {
	 var loadingItem = _this.createPopupItem('-text-', options).el;
	 loadingItem.textContent = links;
	 content.appendChild(loadingItem);
	} else
	if (links.length === 0) {
	 var emptyItem = _this.createPopupItem('-text-', options).el;
	 emptyItem.textContent = mono.global.language.noLinksFound;
	 content.appendChild(emptyItem);
	} else {
	 var items = [];
	 links.forEach(function(link) {
		items.push(_this.createPopupItem(link, options));
	 });

	 items = _this.sortMenuItems(items, options.sortDetails);

	 var hiddenList = [];

	 items.forEach(function(item) {
		if (item.prop.isHidden) {
			hiddenList.push(item.el);
			return 1;
		}

		content.appendChild(item.el);

		if (options.showFileSize && item.sizeIcon) {
			sizeIconList.push(item.sizeIcon);
		}
	 });

	 options.visibleCount = items.length - hiddenList.length;

	 if (hiddenList.length > 0) {
		if (options.getHiddenListFunc) {
			content.appendChild(options.getHiddenListFunc(hiddenList, options));
		} else {
			content.appendChild(_this.getHiddenList(hiddenList, options));
		}
	 }
	}

	return {
	 sizeIconList: sizeIconList,
	 content: content
	};
		},

		create: function(options) {
	var button = options.button;
	var _this = SaveFrom_Utils.popupMenu;

	options.linkClass = options.linkClass || 'sf-menu-item';

	options.offsetRight = options.offsetRight || 0;
	options.offsetTop = options.offsetTop || 0;

	options.parent = options.parent || document.body;

	if (options.isUpdate && (_this.popup === undefined || _this.popup.style.display === 'none')) {
	 return;
	}

	if (_this.popup) {
	 _this.removePanel();
	}

	var popupContainer = _this.popup = document.createElement('div');
	var containerSelector = '#' + _this.popupId;
	if (options.popupId) {
	 containerSelector = '#' + options.popupId;
	 popupContainer.id = options.popupId;
	} else
	if (options.containerClass) {
	 containerSelector = '.' + options.containerClass;
	 popupContainer.classList.add(options.containerClass);
	} else {
	 popupContainer.id = _this.popupId;
	}

	var popupContainerStyle = {
	 display: 'block',
	 position: 'absolute',
	 minHeight: '24px',
	 cursor: 'default',
	 textAlign: 'left',
	 whiteSpace: 'nowrap',
	 fontFamily: 'arial, sans-serif'
	};
	if (options.extStyle) {
	 delete popupContainerStyle.display;
	}

	var pos = SaveFrom_Utils.getPosition(button, options.parent),
	 size = SaveFrom_Utils.getSize(button);

	popupContainerStyle.top = (pos.top + options.offsetTop + size.height) + 'px';
	popupContainerStyle.left = (pos.left + options.offsetRight) + 'px';
	SaveFrom_Utils.setStyle(popupContainer, popupContainerStyle);

	var popupCustomContainerStyle = {
	 'background-color': '#fff',
	 'z-index': '9999',
	 'box-shadow': '0 2px 10px 0 rgba(0,0,0,0.2)',
	 border: '1px solid #ccc',
	 'border-radius': '3px',
	 'font-size': '12px',
	 'font-weight': 'bold',
	 'min-width': '190px'
	};

	if (options.style && options.style.popup) {
	 for (var key in options.style.popup) {
		var value = options.style.popup[key];
		popupCustomContainerStyle[key] = value;
	 }
	}

	SaveFrom_Utils.addStyleRules(containerSelector, popupCustomContainerStyle);

	var itemCustomStyle = {
	 'line-height': '24px',
	 color: '#3D3D3D'
	};

	if (options.style && options.style.item) {
	 for (var key in options.style.item) {
		var value = options.style.item[key];
		itemCustomStyle[key] = value;
	 }
	}

	SaveFrom_Utils.addStyleRules(containerSelector + ' .' + options.linkClass, itemCustomStyle);

	var stopPropagationFunc = function(e) {
	 e.stopPropagation()
	};
	mono.create(popupContainer, {
	 on: [
		['click', stopPropagationFunc],
		['mouseover', stopPropagationFunc],
		['mouseup', stopPropagationFunc],
		['mousedown', stopPropagationFunc],
		['mouseout', stopPropagationFunc]
	 ]
	});

	while (popupContainer.firstChild !== null) {
	 popupContainer.removeChild(popupContainer.firstChild);
	}

	var menuContent = _this.getContent.call(_this, options);
	var sizeIconList = menuContent.sizeIconList;
	menuContent = menuContent.content;
	popupContainer.appendChild(menuContent);


	var hoverBgColor = '#2F8AFF';
	var hoverTextColor = '#fff';
	if (options.style && options.style.hover) {
	 hoverBgColor = options.style.hover.backgroundColor || hoverBgColor;
	 hoverTextColor = options.style.hover.color || hoverTextColor;
	}
	var styleEl = _this.popupStyle = document.createElement('style');
	styleEl.textContent = mono.style2Text({
	 selector: containerSelector,
	 append: {
		'a:hover': {
			backgroundColor: hoverBgColor,
			color: hoverTextColor
		},
		'> a:first-child': {
			borderTopLeftRadius: '3px',
			borderTopRightRadius: '3px'
		},
		'> a:last-child': {
			borderBottomLeftRadius: '3px',
			borderBottomRightRadius: '3px'
		}
	 }
	});

	options.parent.appendChild(styleEl);
	options.parent.appendChild(popupContainer);
	if (options.extStyle) {
	 if (SaveFrom_Utils.popupMenu.extStyleCache !== undefined && SaveFrom_Utils.popupMenu.extStyleCache.parentNode !== null) {
		SaveFrom_Utils.popupMenu.extStyleCache.parentNode.removeChild(SaveFrom_Utils.popupMenu.extStyleCache);
	 }

	 var extElClassName = 'sf-extElStyle_' + containerSelector.substr(1);
	 var extBodyClassName = 'sf-extBodyStyle_' + containerSelector.substr(1);
	 var extBodyStyle = document.querySelector('style.' + extBodyClassName);
	 if (extBodyStyle === null) {
		document.body.appendChild(mono.create('style', {
			class: extBodyClassName,
			text: mono.style2Text({
		selector: containerSelector,
		style: {
		 display: 'none'
		}
			})
		}));
	 }
	 SaveFrom_Utils.popupMenu.extStyleCache = options.extStyle.appendChild(mono.create('style', {
		class: extElClassName,
		text: mono.style2Text({
			selector: 'body ' + containerSelector,
			style: {
		display: 'block'
			}
		})
	 }));
	}

	setTimeout(function() {
	 sizeIconList.forEach(function(icon) {
		mono.trigger(icon, 'click', {
			bubbles: false,
			cancelable: true
		});
	 });
	});

	return popupContainer;
		},

		update: function(popupContainer, options) {
	var _this = SaveFrom_Utils.popupMenu;

	while (popupContainer.firstChild !== null) {
	 popupContainer.removeChild(popupContainer.firstChild);
	}

	var menuContent = _this.getContent.call(_this, options);
	var sizeIconList = menuContent.sizeIconList;
	menuContent = menuContent.content;
	popupContainer.appendChild(menuContent);

	setTimeout(function() {
	 sizeIconList.forEach(function(icon) {
		mono.trigger(icon, 'click', {
			bubbles: false,
			cancelable: true
		});
	 });
	});
		},

		preprocessItem: {
	srt2url: function(item, popupLink) {
	 "use strict";
	 var srt = item.srt;
	 var blobUrl = mono.getDataUrl(srt, 'text/plain');

	 popupLink.ext = 'srt';
	 popupLink.format = 'SRT';
	 popupLink.href = blobUrl;
	 popupLink.noSize = true;
	 if (mono.isOpera) {
		popupLink.forceDownload = false;
	 }
	}
		},

		prepareLinks: {
	youtube: function(links, title, subtitles, details) {
	 details = details || {};
	 subtitles = subtitles || [];
	 links = mono.extend({}, links);
	 var sfUtilsYt = SaveFrom_Utils.video.yt;
	 sfUtilsYt.init();

	 var menuLinks = [];
	 var popupLink = null;
	 var meta = links.meta || {};

	 Object.keys(sfUtilsYt.format).forEach(function(format) {
		var formatObj = sfUtilsYt.format[format];
		return Object.keys(formatObj).forEach(function(itag) {
			var url = links[itag];
			if (!url) {
		return;
			}

			var isHidden = false;

			if (!sfUtilsYt.showFormat[format]) {
		isHidden = true;
			}

			var prop = formatObj[itag];

			if (prop['3d'] && !sfUtilsYt.show3D) {
		isHidden = true;
			}

			if (prop.noAudio && !sfUtilsYt.showMP4NoAudio) {
		isHidden = true;
			}

			popupLink = {
		href: url,
		isHidden: isHidden,
		title: title,
		format: format,
		itag: itag,
		forceDownload: true
			};

			mono.extend(popupLink, prop);

			var metaTag = meta[itag];
			if (metaTag) {
		if (metaTag.quality) {
		 popupLink.quality = metaTag.quality;
		}

		if (metaTag.fps) {
		 popupLink.fps = metaTag.fps;
		}
			}

			menuLinks.push(popupLink);
			delete links[itag];
		});
	 });


	 (links.ummy || links.ummyAudio) && (function() {
		var qualityBadge = null;
		var qualityIndex = -1;
		var badgeQualityList = SaveFrom_Utils.popupMenu.badgeQualityList;
		menuLinks.forEach(function(popupLink) {
			var qIndex = badgeQualityList.indexOf(popupLink.quality);
			if (qIndex !== -1 && (qualityIndex === -1 || qIndex < qualityIndex)) {
		qualityIndex = qIndex;
			}
		});
		if (qualityIndex !== -1) {
			qualityBadge = badgeQualityList[qualityIndex];
		}

		var videoId = meta.videoId;
		videoId && ['ummy', 'ummyAudio'].forEach(function(itag) {
			var url = links[itag];
			if (!url) {
		return;
			}

			popupLink = {
		href: url,
		quality: 'ummy',
		noSize: true,
		format: 'ummy',
		videoId: 'yt-' + videoId
			};

			if (itag === 'ummy') {
		popupLink.itag = 'ummy';
		popupLink.uQuality = qualityBadge;
			} else
			if (itag === 'ummyAudio') {
		popupLink.itag = 'ummyAudio';
		popupLink.uQuality = 'mp3';
		popupLink.uIsAudio = true;
			}

			if (details.ummyVid) {
		popupLink.vid = details.ummyVid;
			}

			menuLinks.push(popupLink);
			delete links[itag];
		});
	 })();

	 Object.keys(links).forEach(function(itag) {
		if (itag === 'meta') {
			return;
		}

		popupLink = {
			href: links[itag],
			isHidden: true,
			title: title,
			quality: itag,
			itag: itag,
			forceDownload: true
		};

		menuLinks.push(popupLink);
		delete links[itag];
	 });

	 subtitles.forEach(function(item) {
		"use strict";
		popupLink = {
			href: item.url,
			isHidden: true,
			quality: 'SRT' + (item.isAuto ? 'A' : ''),
			itemText: mono.global.language.subtitles + ' (' + item.lang + ')',
			title: title + '-' + item.langCode,
			ext: 'vtt',
			format: 'VTT',
			isSubtitle: true,
			langCode: item.langCode,
			forceDownload: true
		};

		if (item.preprocess === 'srt2url') {
			SaveFrom_Utils.popupMenu.preprocessItem.srt2url(item, popupLink);
		}

		menuLinks.push(popupLink);
	 });

	 meta.extra && meta.extra.forEach(function(item) {
		popupLink = {
			href: '#' + item.extra,
			noSize: true,
			isHidden: false
		};

		mono.extend(popupLink, item);

		if (item.itag) {
			Object.keys(sfUtilsYt.format).some(function(format) {
		var formatObj = sfUtilsYt.format[format];
		var prop = formatObj[item.itag];
		if (prop) {
		 mono.extend(popupLink, prop);
		 return true;
		}
			});
		}

		if (item.request) {
			popupLink.func = function(e) {
		e.preventDefault();
		return mono.sendMessage(item.request);
			};
		}

		popupLink.noAudio = false;

		menuLinks.push(popupLink);
	 });

	 return menuLinks;
	},
	vimeo: function(links, title) {
	 var menuLinks = [];
	 var popupLink;
	 links.forEach(function(link) {
		var ext = link.ext;
		if (!ext) {
			ext = 'mp4';
			if (link.url.search(/\.flv($|\?)/i) != -1) {
		ext = 'flv';
			}
		}
		var quality = link.height || '';
		var format = link.type;
		popupLink = {
			href: link.url,
			title: title,
			ext: ext,
			format: format,
			quality: quality,
			forceDownload: true
		};
		menuLinks.push(popupLink);
	 });
	 return menuLinks;
	},
	vk: function(links, title) {
	 var menuLinks = [];
	 var popupLink;
	 links.forEach(function(link) {
		var ext = link.name || link.ext;
		if (ext) {
			ext = ext.toLowerCase();
		}
		var format = ext && ext.toUpperCase() || '';
		var quality = link.subname || '';
		popupLink = {
			href: link.url,
			title: title,
			ext: ext,
			format: format,
			quality: quality,
			forceDownload: true
		};
		menuLinks.push(popupLink);
	 });
	 return menuLinks;
	},
	dailymotion: function(links, title) {
	 var menuLinks = [];

	 links.forEach(function(link) {
		var popupLink = null;
		if (link.extra === 'ummy') {
			popupLink = {
		href: link.url,
		quality: 'ummy',
		noSize: true,
		format: 'ummy',
		videoId: link.videoId,
		sort: {
		 size: 480
		}
			};

			if (link.type === 'ummyAudio') {
		popupLink.uQuality = 'mp3';
		popupLink.uIsAudio = true;
			}
		} else {
			popupLink = {
		href: link.url,
		title: title,
		ext: link.ext,
		format: link.ext,
		quality: link.height || '',
		forceDownload: true
			};

			if (mono.isOpera) {
		popupLink.noSize = true;
			}
		}

		menuLinks.push(popupLink);
	 });

	 return menuLinks;
	},
	facebook: function(links, title) {
	 var menuLinks = [];
	 var popupLink;
	 links.forEach(function(link) {
		var ext = link.ext;
		var format = (ext) ? ext.toUpperCase() : '';
		var quality = link.name;
		popupLink = {
			href: link.url,
			title: title,
			ext: ext,
			format: format,
			quality: quality,
			forceDownload: true
		};
		menuLinks.push(popupLink);
	 });
	 return menuLinks;
	},
	rutube: function(href) {
	 "use strict";
	 if (Array.isArray(href)) {
		href = href[0];
	 }
	 if (typeof href !== 'string') {
		return;
	 }
	 var links = [];

	 var videoId = href.match(/\/embed\/(\d+)/);
	 videoId = videoId && videoId[1] || undefined;

	 if (!videoId) {
		videoId = href.match(/\/video\/([0-9a-z]+)/);
		videoId = videoId && videoId[1] || undefined;
	 }

	 if (/\/\/video\./.test(href)) {
		href = href.replace(/\/\/video\./, '//');
		if (!videoId) {
			videoId = href.match(/\/(\d+)$/);
			videoId = videoId && videoId[1] || undefined;
		}
	 }

	 if (videoId) {
		videoId = 'rt-' + videoId;
	 }

	 var ummyUrl = href.replace(/^.*(\/\/.*)$/, 'ummy:$1');

	 var videoLink = {
		href: ummyUrl,
		quality: 'ummy',
		noSize: true,
		format: 'ummy',
		itag: 'ummy',
		uQuality: '720',
		vid: 114,
		videoId: videoId
	 };

	 var sep = '?';
	 if (ummyUrl.indexOf(sep) !== -1) {
		sep = '&';
	 }
	 ummyUrl += sep + 'sf_type=audio';

	 var audioLink = {
		href: ummyUrl,
		quality: 'ummy',
		noSize: true,
		format: 'ummy',
		itag: 'ummyAudio',
		uQuality: 'mp3',
		uIsAudio: true,
		vid: 114,
		videoId: videoId
	 };

	 links.push(videoLink);
	 links.push(audioLink);

	 return links;
	},
	mailru: function(links, title) {
	 var menuLinks = [];
	 var popupLink;
	 links.forEach(function(link) {
		var ext = link.ext;
		var format = link.name;
		var quality = link.subname;
		popupLink = {
			href: link.url,
			title: title,
			ext: ext,
			format: format,
			quality: quality,
			forceDownload: true
		};
		menuLinks.push(popupLink);
	 });
	 return menuLinks;
	}
		},

		/**
		 * @param {Node|Element} target
		 * @param {String|Array} links
		 * @param {String} id
		 * @param {Object} [_details]
		 * @returns {{isShow: boolean, el: Node|Element, hide: Function, update: Function}}
		 */
		quickInsert: function(target, links, id, _details) {
	_details = _details || {};
	var result = {};

	var hideMenu = function(e) {
	 if (e && (e.target === target || target.contains(e.target))) {
		return;
	 }

	 if (!result.isShow) {
		return;
	 }

	 menu.style.display = 'none';
	 mono.off(document, 'mousedown', hideMenu);
	 result.isShow = false;
	 _details.onHide && _details.onHide(menu);
	};

	var options = {
	 links: links,
	 button: target,
	 popupId: id,
	 showFileSize: true
	 /*
	 parent: args.parent,
	 extStyle: args.extStyle,
	 offsetRight: args.offsetRight,
	 offsetTop: args.offsetTop,
	 onItemClick: args.onItemClick
	 */
	};

	mono.extend(options, _details);

	var menu = SaveFrom_Utils.popupMenu.create(options);

	_details.onShow && _details.onShow(menu);

	mono.off(document, 'mousedown', hideMenu);
	mono.on(document, 'mousedown', hideMenu);

	return mono.extend(result, {
	 button: target,
	 isShow: true,
	 el: menu,
	 hide: hideMenu,
	 update: function(links) {
		options.links = links;
		SaveFrom_Utils.popupMenu.update(menu, options)
	 }
	});
		}
	},

	frameMenu: {
		getBtn: function(details) {
	"use strict";
	var containerStyle = {
	 verticalAlign: 'middle',
	 position: 'absolute',
	 zIndex: 999,
	 fontFamily: 'arial, sans-serif'
	};

	for (var key in details.containerStyle) {
	 containerStyle[key] = details.containerStyle[key];
	}

	var quickBtnStyle = details.quickBtnStyleObj || {
	 display: 'inline-block',
	 fontSize: 'inherit',
	 height: '22px',
	 border: '1px solid rgba(255, 255, 255, 0.4)',
	 borderRadius: '3px',
	 borderTopRightRadius: 0,
	 borderBottomRightRadius: 0,
	 paddingRight: 0,
	 paddingLeft: '28px',
	 cursor: 'pointer',
	 verticalAlign: 'middle',
	 position: 'relative',
	 lineHeight: '22px',
	 textDecoration: 'none',
	 zIndex: 1,
	 color: '#fff'
	};

	if (details.singleBtn && !details.quickBtnStyleObj) {
	 delete quickBtnStyle.borderTopRightRadius;
	 delete quickBtnStyle.borderBottomRightRadius;
	}

	var selectBtnStyle = {
	 position: 'relative',
	 display: 'inline-block',
	 fontSize: 'inherit',
	 height: '24px',
	 padding: 0,
	 paddingRight: '21px',
	 border: '1px solid rgba(255, 255, 255, 0.4)',
	 borderLeft: 0,
	 borderRadius: '3px',
	 borderTopLeftRadius: '0',
	 borderBottomLeftRadius: '0',
	 cursor: 'pointer',
	 color: '#fff',
	 zIndex: 0,
	 verticalAlign: 'middle',
	 marginLeft: 0,
	 boxSizing: 'border-box',
	 lineHeight: '22px'
	};

	for (var key in details.selectBtnStyle) {
	 selectBtnStyle[key] = details.selectBtnStyle[key];
	}

	var quickBtnIcon = details.quickBtnIcon || mono.create('i', {
	 style: {
		position: 'absolute',
		display: 'inline-block',
		left: '6px',
		top: '3px',
		backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#ffffff') + ')',
		backgroundSize: '12px',
		backgroundRepeat: 'no-repeat',
		backgroundPosition: 'center',
		width: '16px',
		height: '16px'
	 }
	});

	var selectBtnIcon = details.selectBtnIcon || mono.create('i', {
	 style: {
		position: 'absolute',
		display: 'inline-block',
		top: '9px',
		right: '6px',
		border: '5px solid #FFF',
		borderBottomColor: 'transparent',
		borderLeftColor: 'transparent',
		borderRightColor: 'transparent'
	 }
	});

	var quickBtn;

	var btnContainer = mono.create('div', {
	 id: details.btnId,
	 style: containerStyle,
	 on: details.on,
	 append: [
		quickBtn = mono.create('a', {
			class: 'sf-quick-btn',
			style: quickBtnStyle,
			href: '#',
			append: [
		quickBtnIcon
			]
		}),
		mono.create('style', {
			text: mono.style2Text({
		selector: '#' + details.btnId,
		style: details.nodeCssStyle || {
		 opacity: 0.8,
		 display: 'none'
		},
		append: [{
		 'button::-moz-focus-inner': {
			padding: 0,
			margin: 0
		 },
		 '.sf-quick-btn': details.quickBtnCssStyle || {
			backgroundColor: 'rgba(28,28,28,0.1)'
		 },
		 '.sf-select-btn': {
			backgroundColor: 'rgba(28,28,28,0.1)'
		 }
		}, {
		 selector: [':hover', '.sf-over'],
		 join: '',
		 style: {
			opacity: 1
		 },
		 append: {
			'.sf-quick-btn': details.quickBtnOverCssStyle || {
				backgroundColor: 'rgba(0, 163, 80, 0.5)'
			},
			'.sf-select-btn': {
				backgroundColor: 'rgba(60, 60, 60, 0.5)'
			}
		 }
		}, {
		 join: '',
		 '.sf-over': {
			append: {
				'.sf-select-btn': {
			backgroundColor: 'rgba(28,28,28,0.8)'
				}
			}
		 },
		 '.sf-show': {
			display: 'block'
		 }
		}]
			})
		})
	 ]
	});

	var selectBtn = null;
	var setQuality = null;
	if (!details.singleBtn) {
	 setQuality = function(text) {
		var node = typeof text === 'object' ? text : document.createTextNode(text);
		var first = selectBtn.firstChild;
		if (first === selectBtnIcon) {
			selectBtn.insertBefore(node, first);
		} else {
			selectBtn.replaceChild(node, first);
		}
	 };
	 selectBtn = mono.create('button', {
		class: 'sf-select-btn',
		style: selectBtnStyle,
		on: details.onSelectBtn,
		append: [
			selectBtnIcon
		]
	 });
	 btnContainer.appendChild(selectBtn);
	}

	return {
	 node: btnContainer,
	 setQuality: setQuality,
	 setLoadingState: function() {
		setQuality(mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('info', '#ffffff'),
			style: {
		width: '14px',
		height: '14px',
		marginLeft: '6px',
		verticalAlign: 'middle',
		top: '-1px',
		position: 'relative'
			}
		}));
	 },
	 selectBtn: selectBtn,
	 quickBtn: quickBtn
	};
		},

		getHiddenList: function(hiddenList, options) {
	"use strict";
	var popupMenu = SaveFrom_Utils.popupMenu;
	var moreBtn = popupMenu.createPopupItem('-text-', options).el;
	mono.create(moreBtn, {
	 text: mono.global.language.more + ' ' + String.fromCharCode(187),
	 style: {
		cursor: 'pointer'
	 },
	 on: ['click', function() {
		var content = this.parentNode;
		var itemList = content.querySelectorAll('*[' + popupMenu.dataArrtVisible + ']');
		for (var i = 0, item; item = itemList[i]; i++) {
			item.style.display = 'block';
			item.setAttribute(popupMenu.dataArrtVisible, 1);
		}
		this.parentNode.removeChild(this);
		/*content.replaceChild(mono.create('i', {
			class: 'sf-separator'
		}), this);*/
	 }]
	});

	var content = document.createDocumentFragment();
	content.appendChild(moreBtn);

	mono.create(content, {
	 append: hiddenList
	});

	if (options.visibleCount === 0) {
	 mono.trigger(moreBtn, 'click', {
		cancelable: true
	 });
	}

	return content;
		},

		getMenuContainer: function(options) {
	"use strict";
	var popupMenu = SaveFrom_Utils.popupMenu;
	var button = options.button;
	var popupId = options.popupId;

	var container = mono.create('div', {
	 style: {
		position: 'absolute',
		minHeight: '24px',
		cursor: 'default',
		textAlign: 'left',
		whiteSpace: 'nowrap',
		overflow: 'auto'
	 }
	});

	if (popupId[0] === '#') {
	 container.id = popupId.substr(1);
	} else {
	 container.classList.add(popupId);
	}

	var menuContent = popupMenu.getContent(options);
	container.appendChild(menuContent.content);

	setTimeout(function() {
	 menuContent.sizeIconList.forEach(function(icon) {
		mono.trigger(icon, 'click', {
			bubbles: false,
			cancelable: true
		});
	 });
	});

	var pos = SaveFrom_Utils.getPosition(button, options.parent);
	var size = SaveFrom_Utils.getSize(button);

	var stopPropagationFunc = function(e) {
	 e.stopPropagation()
	};

	var topOffset = pos.top + size.height;
	var menuStyle = {
	 top: topOffset + 'px',
	 maxHeight: (document.body.offsetHeight - topOffset - 40) + 'px'
	};

	if (options.leftMenuPos) {
	 menuStyle.left = pos.left + 'px';
	} else {
	 menuStyle.right = (document.body.offsetWidth - pos.left - size.width) + 'px';
	}

	mono.create(container, {
	 style: menuStyle,
	 on: [
		['click', stopPropagationFunc],
		['mouseover', stopPropagationFunc],
		['mouseup', stopPropagationFunc],
		['mousedown', stopPropagationFunc],
		['mouseout', stopPropagationFunc],
		['wheel', function(e) {
			if (e.wheelDeltaY > 0 && this.scrollTop === 0) {
		e.preventDefault();
			} else
			if (e.wheelDeltaY < 0 && this.scrollHeight - (this.offsetHeight + this.scrollTop) <= 0) {
		e.preventDefault();
			}
		}]
	 ],
	 append: [
		mono.create('style', {
			text: mono.style2Text({
		selector: (popupId[0] === '#' ? '' : '.') + popupId,
		style: {
		 display: 'none',
		 fontFamily: 'arial, sans-serif',

		 backgroundColor: 'rgba(28,28,28,0.8)',
		 zIndex: 9999,
		 borderRadius: '4px',
		 fontSize: '12px',
		 fontWeight: 'bold',
		 minWidth: '190px',
		 color: '#fff'
		},
		append: [{
		 join: '',
		 '.sf-show': {
			display: 'block'
		 },
		 '::-webkit-scrollbar-track': {
			backgroundColor: '#424242'
		 },
		 '::-webkit-scrollbar': {
			width: '10px',
			backgroundColor: '#424242'
		 },
		 '::-webkit-scrollbar-thumb': {
			backgroundColor: '#8e8e8e'
		 }
		}, {
		 '.sf-menu-item': {
			lineHeight: '24px',
			color: '#fff'
		 },
		 '.sf-menu-item:hover': {
			backgroundColor: '#1c1c1c'
		 }
		}]
			})
		})
	 ]
	});

	return container;
		},
		getMenu: function(target, links, id, _options) {
	"use strict";
	var options = {
	 links: links,
	 button: target,
	 popupId: id || '#sf-frame-menu',
	 showFileSize: true,
	 sizeIconStyle: {
		color: '#fff'
	 },
	 linkClass: 'sf-menu-item',
	 bindUmmyInfoDetails: {
		posLeft: true,
		widthLimit: 480,
		container: _options.container,
		createUmmyInfoDetails: {
			posLeft: true,
			darkTheme: true
		}
	 },
	 getHiddenListFunc: this.getHiddenList.bind(this)
	};

	for (var key in _options) {
	 options[key] = _options[key];
	}

	var menu = this.getMenuContainer(options);

	(options.container || document.body).appendChild(menu);

	var hideMenu = function() {
	 if (menu.parentNode) {
		menu.parentNode.removeChild(menu);
	 }
	 out.isShow = false;
	 options.onHide && options.onHide();
	};

	options.onShow && options.onShow(menu);

	mono.off(document, 'mousedown', hideMenu);
	mono.on(document, 'mousedown', hideMenu);

	var out = {
	 isShow: true,
	 el: menu,
	 hide: hideMenu,
	 update: function(links) {
		var popupMenu = SaveFrom_Utils.popupMenu;
		var style = menu.lastChild;
		menu.textContent = '';

		options.links = links;
		var menuContent = popupMenu.getContent(options);

		setTimeout(function() {
			menuContent.sizeIconList.forEach(function(icon) {
		mono.trigger(icon, 'click', {
		 bubbles: false,
		 cancelable: true
		});
			});
		});

		menu.appendChild(menuContent.content);
		menu.appendChild(style);
	 }
	};

	return out;
		}
	},

	mobileLightBox: {
		id: 'sf-lightbox',
		clear: function() {
	var el = document.getElementById(SaveFrom_Utils.mobileLightBox.id);
	if (el === null) {
	 return;
	}
	el.parentNode.removeChild(el);
		},
		getTitle: function(item) {
	var title = [];

	title.push(item.format || '???');
	if (item.quality) {
	 var quality = item.quality;

	 if (item.sFps) {
		quality += ' ' + (item.fps || 60);
	 }

	 title.push(quality);
	}
	if (item['3d']) {
	 title.push('3D');
	}
	if (item.noAudio) {
	 title.push(mono.global.language.withoutAudio);
	}

	return title.join(' ');
		},
		createItem: function(listItem) {
	var mobileLightBox = SaveFrom_Utils.mobileLightBox;

	var button = mono.create('a', {
	 style: {
		display: 'block',
		marginBottom: '6px',
		border: 'solid 1px #d3d3d3',
		lineHeight: '36px',
		minHeight: '36px',
		background: '#f8f8f8',
		verticalAlign: 'middle',
		fontSize: '15px',
		textAlign: 'center',
		color: '#333',
		borderRadius: '2px',
		overflow: 'hidden',
		position: 'relative'
	 }
	});

	var filename = '';
	if (listItem.title) {
	 var ext = (listItem.ext || listItem.format || '').toLowerCase();
	 if (ext) {
		ext = '.' + ext;
	 }
	 filename = mono.fileName.modify(listItem.title + ext);
	}

	if (typeof listItem === 'string') {
	 button.textContent = listItem;
	 return button;
	} else {
	 button.href = listItem.href;
	 button.download = filename;
	 button.textContent = mobileLightBox.getTitle(listItem);
	}

	if (listItem.forceDownload) {
	 button.addEventListener('click', function(e) {
		SaveFrom_Utils.downloadOnClick(e, null, {
			el: this
		});
	 });
	}

	if (listItem.isHidden) {
	 button.classList.add('isOptional');
	 button.style.display = 'none';
	}

	var ctrStyle = {
	 cssFloat: 'right',
	 lineHeight: '36px',
	 fontSize: '75%',
	 marginRight: '10px'
	};
	var svgStyle = {
	 width: '16px',
	 height: '16px'
	};
	var btnStyle = {
	 padding: '10px',
	 verticalAlign: 'middle',
	 lineHeight: 0
	};
	var sizeIcon = SaveFrom_Utils.getFileSizeIcon(ctrStyle, btnStyle, svgStyle, {
	 url: listItem.href
	});
	button.appendChild(sizeIcon.node);

	return button;
		},
		getItems: function(itemList) {
	var mobileLightBox = SaveFrom_Utils.mobileLightBox;

	if (typeof itemList === 'string') {
	 return {
		list: [mobileLightBox.createItem(itemList)],
		hiddenCount: 0
	 };
	}

	var list = [];
	for (var i = 0, item; item = itemList[i]; i++) {
	 if (item.quality === 'ummy') {
		continue;
	 }
	 if (item.extra) {
		continue;
	 }
	 list.push({
		el: mobileLightBox.createItem(item),
		prop: item
	 });
	}
	list = SaveFrom_Utils.popupMenu.sortMenuItems(list);
	var elList = [];
	var hiddenElList = [];
	for (i = 0, item; item = list[i]; i++) {
	 if (item.prop.isHidden) {
		hiddenElList.push(item.el);
	 } else {
		elList.push(item.el);
	 }
	}
	return {
	 list: elList.concat(hiddenElList),
	 hiddenCount: hiddenElList.length
	};
		},
		show: function(itemList) {
	var mobileLightBox = SaveFrom_Utils.mobileLightBox;

	var topOffset = window.pageYOffset;
	var winHeight = window.innerHeight;
	var mTop = parseInt(winHeight / 100 * 15);
	var btnBox = undefined;
	var moreBtn;

	var getBtnBoxSize = function(hasMore) {
	 "use strict";
	 var i = hasMore ? 2 : 1;
	 return winHeight - 46 * i - mTop * 2;
	};

	var setMoreBtnState = function(itemObj) {
	 "use strict";
	 if (itemObj.hiddenCount > 0) {
		btnBox.style.height = getBtnBoxSize(1) + 'px';
		moreBtn.style.display = 'block';
	 } else {
		moreBtn.style.display = 'none';
		btnBox.style.height = getBtnBoxSize(0) + 'px';
	 }

	 if (itemObj.hiddenCount === itemObj.list.length) {
		onMoreBtnClick(moreBtn);
	 }
	};

	var onMoreBtnClick = function(btnNode) {
	 var state = 'none';
	 var elList = btnNode.parentNode.querySelectorAll('.isOptional');
	 if (btnNode.dataset.state !== 'open') {
		btnNode.dataset.state = 'open';
		btnNode.textContent = mono.global.language.more + ' ' + String.fromCharCode(171);
		state = 'block';
	 } else {
		btnNode.dataset.state = 'close';
		btnNode.textContent = mono.global.language.more + ' ' + String.fromCharCode(187);
	 }
	 for (var i = 0, el; el = elList[i]; i++) {
		el.style.display = state;
	 }
	};

	var exLb = document.getElementById(mobileLightBox.id);
	if (exLb !== null) {
	 exLb.parentNode.removeChild(exLb);
	}


	var lbWidth = window.innerWidth;
	if (lbWidth <= 250) {
	 lbWidth = '90%';
	} else {
	 lbWidth = '70%';
	}

	if (!itemList || itemList.length === 0) {
	 itemList = mono.global.language.noLinksFound;
	}

	var itemObj = mobileLightBox.getItems(itemList);

	var lightbox = mono.create('div', {
	 id: mobileLightBox.id,
	 style: {
		position: 'absolute',
		top: 0,
		left: 0,
		width: '100%',
		zIndex: 9000,
		height: document.body.scrollHeight + 'px',
		background: 'rgba(0,0,0,0.85)',
		textAlign: 'center',
		boxSizing: 'content-box'
	 },
	 on: [
		['click', function(e) {
			e.preventDefault();
			close();
		}]
	 ],
	 append: mono.create('div', {
		style: {
			display: 'inline-block',
			width: lbWidth,
			backgroundColor: '#eee',
			height: (winHeight - mTop * 2) + 'px',
			marginTop: (mTop + topOffset) + 'px',
			borderRadius: '4px',
			padding: '8px',
			position: 'relative',
			boxSizing: 'content-box'
		},
		append: [
			btnBox = mono.create('div', {
		style: {
		 overflowY: 'auto',
		 marginBottom: '6px'
		},
		append: itemObj.list,
		on: ['touchmove', function(e) {
		 e.stopPropagation();
		}]
			}),
			moreBtn = mono.create(mobileLightBox.createItem(mono.global.language.more + ' ' + String.fromCharCode(187)), {
		href: '#',
		on: ['click', function(e) {
		 e.preventDefault();
		 onMoreBtnClick(this);
		}]
			}),
			mono.create(mobileLightBox.createItem(mono.global.language.close), {
		style: {
		 marginBottom: 0
		},
		on: ['click', function(e) {
		 e.preventDefault();
		 close();
		}]
			})
		],
		on: ['click', function(e) {
			e.stopPropagation();
		}]
	 })
	});

	setMoreBtnState(itemObj);

	document.body.appendChild(lightbox);

	var topPos = document.body.scrollTop;

	var result = {};

	var close = function() {
	 if (!result.isShow) {
		return;
	 }

	 document.body.scrollTop = topPos;
	 result.hide();
	};

	return mono.extend(result, {
	 isShow: true,
	 el: lightbox,
	 hide: function() {
		lightbox.parentNode && lightbox.parentNode.removeChild(lightbox);
		result.isShow = false;
	 },
	 close: close,
	 update: function(itemList) {
		if (lightbox.parentNode === null) {
			return;
		}

		if (!itemList || itemList.length === 0) {
			itemList = mono.global.language.noLinksFound;
		}

		btnBox.textContent = '';
		var itemObj = mobileLightBox.getItems(itemList);

		mono.create(btnBox, {
			append: itemObj.list
		});

		setMoreBtnState(itemObj);
	 }
	});
		}
	},

	/**
	 * @param {Object} details
	 * @param {Array} [details.args]
	 * @param {number} [details.timeout]
	 * @param {function} details.func
	 * @param {function} details.cb
	 */
	bridge: function(details) {
		"use strict";
		details.args = details.args || [];
		if (details.timeout === undefined) {
	details.timeout = 300;
		}
		var scriptId = 'sf-bridge-' + parseInt(Math.random() * 1000) + '-' + Date.now();

		var listener = function(e) {
	window.removeEventListener('sf-bridge-' + scriptId, listener);
	var data;
	if (!e.detail) {
	 data = undefined;
	} else {
	 data = JSON.parse(e.detail);
	}
	details.cb(data);
		};

		window.addEventListener('sf-bridge-' + scriptId, listener);

		var wrapFunc = '(' + (function(func, args, scriptId, timeout) {
	/* fix */
	var node = document.getElementById(scriptId);
	if (node) {
	 node.parentNode.removeChild(node);
	}

	var fired = false;
	var done = function(data) {
	 if (fired) {
		return;
	 }
	 fired = true;

	 var event = new CustomEvent('sf-bridge-' + scriptId, {
		detail: JSON.stringify(data)
	 });
	 window.dispatchEvent(event);
	};

	timeout && setTimeout(function() {
	 done();
	}, timeout);

	args.push(done);

	func.apply(null, args);
		}).toString() + ')(' + [
	details.func.toString(),
	JSON.stringify(details.args),
	JSON.stringify(scriptId),
	parseInt(details.timeout)
		].join(',') + ');';

		if (mono.isSafari) {
	var safariFix = function() {
	 if (typeof CustomEvent === 'undefined') {
		CustomEvent = function(event, params) {
			params = params || {
		bubbles: false,
		cancelable: false
			};
			var evt = document.createEvent('CustomEvent');
			evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
			return evt;
		};
		CustomEvent.prototype = window.Event.prototype;
	 }
	};
	wrapFunc = wrapFunc.replace('/* fix */', '(' + safariFix.toString() + ')();');
		} else
		if (mono.isOpera) {
	wrapFunc = wrapFunc.replace('/* fix */', 'var CustomEvent = window.CustomEvent;');
		}

		var script = mono.create('script', {
	id: scriptId,
	text: wrapFunc
		});
		document.body.appendChild(script);
	}
 };

 SaveFrom_Utils.TutorialTooltip = function(details) {
	"use strict";
	var _this = this;
	this.details = {
		btnTopOffset: -3,
		btnLeftOffset: 0
	};

	mono.extend(this.details, details);

	this.onResize = this.onResize.bind(this);
	this.onResizeDebouce = mono.debounce(this.onResize, 250);
	this.onClose = this.onClose.bind(this);

	this.target = details.target;

	if (this.target.dataset.sfHasTooltip === '1') {
		return;
	}
	this.target.dataset.sfHasTooltip = '1';


	this.tooltipNode = this.getNode();


	this.target.addEventListener('mouseup', this.onClose);

	this.target.addEventListener(mono.onRemoveEventName, function() {
		_this.onClose && _this.onClose(1);
	});

	window.addEventListener('resize', this.onResizeDebouce);

	this.onResize();

	(details.parent || document.body).appendChild(this.tooltipNode);
 };

 SaveFrom_Utils.TutorialTooltip.prototype.getNode = function() {
	"use strict";
	var _this = this;
	var language = mono.global.language;

	var zIndex = (function() {
		var zIndex = 1000;
		var top = document.getElementById('masthead-positioner');
		var styleList = top && window.getComputedStyle(top, null);
		if (styleList) {
	zIndex = parseInt(styleList.getPropertyValue('z-index')) + 1;
		}
		return zIndex;
	})();

	var box = mono.create('div', {
		class: 'sf-tooltip',
		on: ['mouseup', function(e) {
	e.stopPropagation();
		}],
		append: [
	mono.create('span', {
	 style: {
		display: 'inline-block',
		border: '8px solid transparent',
		borderRight: '10px solid #4D4D4D',
		borderLeft: 0,
		width: 0,
		top: '8px',
		left: '0px',
		position: 'absolute'
	 }
	}),
	mono.create('span', {
	 style: {
		display: 'inline-block',
		backgroundColor: '#4D4D4D',
		marginLeft: '10px',
		padding: '10px 10px',
		maxWidth: '220px',
		minWidth: '220px',
		lineHeight: '16px',
		fontSize: '14px',
		fontFamily: 'font-family: arial, sans-serif',
		color: '#fff'
	 },
	 append: [
		mono.create('p', {
			style: {
		margin: 0
			},
			append: mono.parseTemplate(language.tutorialTooltipText)
		}),
		mono.create('a', {
			class: 'sf-button',
			text: 'OK',
			style: {
		display: 'inline-block',
		textAlign: 'center',
		textDecoration: 'none',
		padding: '0 10px',
		cssFloat: 'right',

		marginTop: '5px',
		lineHeight: '20px',
		borderRadius: '3px',
		fontSize: '12px',
		color: '#fff',
		fontWeight: 'bolder',
		backgroundColor: '#167AC6',
		cursor: 'pointer'
			},
			on: ['click', function(e) {
		e.preventDefault();
		_this.onClose && _this.onClose();
			}]
		}),
		mono.create('style', {
			text: mono.style2Text({
		'.sf-tooltip': {
		 position: 'absolute',
		 zIndex: zIndex + 2,
		 append: {
			'.sf-button:hover': {
				backgroundColor: '#126db3 !important'
			},
			'.sf-button:active': {
				opacity: 0.9
			}
		 }
		}
			})
		})
	 ]
	})
		]
	});

	return box;
 };

 SaveFrom_Utils.TutorialTooltip.prototype.onClose = function(force) {
	"use strict";
	if (force && force.type === 'mouseup') {
		force = null;
	}

	if (this.tooltipNode) {
		this.tooltipNode.parentNode && this.tooltipNode.parentNode.removeChild(this.tooltipNode);
		this.tooltipNode = null;
	}

	window.removeEventListener('resize', this.onResizeDebouce);
	this.target.removeEventListener('mouseup', this.onClose);

	this.onClose = null;
	if (!force) {
		this.details.onClose && this.details.onClose();
	}
 };

 SaveFrom_Utils.TutorialTooltip.prototype.onResize = function() {
	"use strict";
	var btn = this.target;
	if (!btn.offsetParent || !btn.parentNode) {
		return this.onClose && this.onClose(1);
	}

	var btnPos = SaveFrom_Utils.getPosition(btn, this.details.parent);
	var top = btnPos.top + this.details.btnTopOffset;
	var left = btnPos.left + btnPos.width + this.details.btnLeftOffset;
	this.tooltipNode.style.top = top + 'px';
	this.tooltipNode.style.left = left + 'px';
 };

 SaveFrom_Utils.mutationWatcher = {
	/**
	 * @return {MutationObserver}
	 */
	getMutationObserver: function() {
		"use strict";
		var MutationObserverCtor = null;
		if (typeof MutationObserver !== 'undefined') {
	MutationObserverCtor = MutationObserver;
		} else
		if (typeof WebKitMutationObserver !== 'undefined') {
	MutationObserverCtor = WebKitMutationObserver;
		} else
		if (typeof MozMutationObserver !== 'undefined') {
	MutationObserverCtor = MozMutationObserver;
		} else
		if (typeof JsMutationObserver !== 'undefined') {
	MutationObserverCtor = JsMutationObserver;
		}
		return MutationObserverCtor;
	},
	isAvailable: function() {
		"use strict";
		return !!this.getMutationObserver();
	},
	disconnect: function(details) {
		"use strict";
		details.observer.disconnect();
	},
	connect: function(details) {
		"use strict";
		details.observer.observe(details.target, details.config);
	},
	joinMutations: function(mutations) {
		"use strict";
		var jMutations = [];
		var targetList = [];

		var jObj = {},
	obj, hasNodes;
		var mutation, i, node, tIndex;
		while (mutation = mutations.shift()) {
	tIndex = targetList.indexOf(mutation.target);

	if (tIndex === -1) {
	 tIndex = targetList.push(mutation.target) - 1;
	 jObj[tIndex] = {
		target: mutation.target,
		added: [],
		removed: []
	 };
	}

	obj = jObj[tIndex];
	hasNodes = undefined;

	for (i = 0; node = mutation.addedNodes[i]; i++) {
	 if (node.nodeType !== 1) {
		continue;
	 }

	 obj.added.push(node);
	 hasNodes = true;
	}

	for (i = 0; node = mutation.removedNodes[i]; i++) {
	 if (node.nodeType !== 1) {
		continue;
	 }

	 obj.removed.push(node);
	 hasNodes = true;
	}

	if (hasNodes !== undefined && obj.inList === undefined) {
	 obj.inList = true;
	 jMutations.push(obj);
	}
		}

		return jMutations;
	},
	isMatched: null,
	prepareMatched: function() {
		"use strict";
		if (this.isMatched) {
	return;
		}

		var el = document.createElement('div');

		if (typeof el.matches === 'function') {
	this.isMatched = function(node, selector) {
	 return node.matches(selector);
	};
		} else
		if (typeof el.matchesSelector === 'function') {
	this.isMatched = function(node, selector) {
	 return node.matchesSelector(selector);
	};
		} else
		if (typeof el.webkitMatchesSelector === 'function') {
	this.isMatched = function(node, selector) {
	 return node.webkitMatchesSelector(selector);
	};
		} else
		if (typeof el.mozMatchesSelector === 'function') {
	this.isMatched = function(node, selector) {
	 return node.mozMatchesSelector(selector);
	};
		} else
		if (typeof el.oMatchesSelector === 'function') {
	this.isMatched = function(node, selector) {
	 return node.oMatchesSelector(selector);
	};
		} else
		if (typeof el.msMatchesSelector === 'function') {
	this.isMatched = function(node, selector) {
	 return node.msMatchesSelector(selector);
	};
		}

		el = null;
	},
	match: function(details, summaryList, mutation) {
		"use strict";
		var _this = this;
		var node, i, query, n;
		var queries = details.queries;
		var hasChanges = false;
		['added', 'removed'].forEach(function(type) {
	var nodeList = mutation[type];
	for (n = 0; node = nodeList[n]; n++) {
	 for (i = 0; query = queries[i]; i++) {
		if (query.is !== undefined && query.is !== type) {
			continue;
		}
		var nodeArr = summaryList[i][type];
		if (_this.isMatched(node, query.css) === true) {
			nodeArr.push(node);
		} else {
			nodeArr.push.apply(nodeArr, node.querySelectorAll(query.css));
		}

		if (hasChanges === false) {
			hasChanges = nodeArr[0] !== undefined;
		}
	 }
	}
		});

		return hasChanges;
	},
	filterTarget: function(queries, node) {
		"use strict";
		var i, query;
		for (i = 0; query = queries[i]; i++) {
	if (this.isMatched(node, query.css) === true) {
	 return true;
	}
		}
		return false;
	},
	run: function(_details) {
		"use strict";
		var _this = this;
		var details = {
	config: {
	 childList: true,
	 subtree: true
	},
	target: document.body,
	filterTarget: []
		};
		mono.extend(details, _details);

		details._disconnect = this.disconnect.bind(this, details);
		details._connect = this.connect.bind(this, details);
		details._match = this.match.bind(this, details);

		var _summaryList = [];
		for (var i = 0; i < details.queries.length; i++) {
	_summaryList.push({
	 added: [],
	 removed: []
	});
		}
		_summaryList = JSON.stringify(_summaryList);

		this.prepareMatched();

		var mObserver = this.getMutationObserver();
		details.observer = new mObserver(function(mutations) {
	// console.time('o');
	var jMutations = _this.joinMutations(mutations);
	if (jMutations.length === 0) {
	 // console.timeEnd('o');
	 return;
	}

	var hasChanges = false;
	var mutation;
	var summaryList = JSON.parse(_summaryList);
	while (mutation = jMutations.shift()) {
	 // console.log('mutation', mutation);
	 /*console.log('mutationClone', {
		target: mutation.target.cloneNode(true),
		added: [].slice.call(mutation.added).map(function (node) {
			return node.cloneNode(true)
		}),
		removed: [].slice.call(mutation.removed).map(function (node) {
			return node.cloneNode(true)
		})
	 });*/
	 if (_this.filterTarget(details.filterTarget, mutation.target) === false) {
		if (details._match(summaryList, mutation) === true) {
			hasChanges = true;
		}
	 }
	}

	hasChanges === true && details.callback(summaryList);
	// console.timeEnd('o');
		});

		details.trigger = function(node) {
	var hasChanges = false;
	var summaryList = JSON.parse(_summaryList);

	var mutation = {
	 added: [node],
	 removed: []
	};
	if (details._match(summaryList, mutation)) {
	 hasChanges = true;
	}

	hasChanges === true && details.callback(summaryList);
		};

		details.start = function() {
	details._disconnect();
	details._connect();
	details.trigger(details.target);
		};

		details.stop = function() {
	details._disconnect();
		};

		details.start();

		return details;
	}
 };

 SaveFrom_Utils.mutationAttrWatcher = {
	isAvailable: function() {
		"use strict";
		return !!SaveFrom_Utils.mutationWatcher.getMutationObserver();
	},
	disconnect: function(details) {
		"use strict";
		details.observer.disconnect();
	},
	connect: function(details) {
		"use strict";
		details.observer.observe(details.target, details.config);
	},
	run: function(_details) {
		"use strict";
		var _this = this;

		var details = {
	config: {
	 attributes: true,
	 childList: false,
	 attributeOldValue: true
	},
	target: document.body
		};

		mono.extend(details, _details);

		if (!Array.isArray(details.attr)) {
	details.attr = [details.attr];
		}

		details.config.attributeFilter = details.attr;

		details._disconnect = this.disconnect.bind(this, details);
		details._connect = this.connect.bind(this, details);

		var _summaryList = [];
		for (var i = 0; i < details.attr.length; i++) {
	_summaryList.push({});
		}
		_summaryList = JSON.stringify(_summaryList);

		var mObserver = SaveFrom_Utils.mutationWatcher.getMutationObserver();
		details.observer = new mObserver(function(mutations) {
	var hasChanges = false;
	var mutation;
	var summaryList = JSON.parse(_summaryList);
	while (mutation = mutations.shift()) {
	 // console.log('mutation', mutation);
	 var index = details.attr.indexOf(mutation.attributeName);
	 if (index === -1) {
		continue;
	 }

	 var value = mutation.target.getAttribute(mutation.attributeName);
	 if (value === mutation.oldValue) {
		continue;
	 }

	 summaryList[index] = {
		value: value,
		oldValue: mutation.oldValue
	 };

	 hasChanges = true;
	}

	hasChanges === true && details.callback(summaryList);
		});

		details.start = function() {
	details._disconnect();
	details._connect();

	var hasChanges = false;
	var summaryList = JSON.parse(_summaryList);

	for (var i = 0, attributeName; attributeName = details.attr[i]; i++) {
	 var value = details.target.getAttribute(attributeName);
	 if (value === null) {
		continue;
	 }
	 summaryList[i] = {
		value: value,
		oldValue: null
	 };

	 hasChanges = true;
	}

	hasChanges === true && details.callback(summaryList);
		};

		details.stop = function() {
	details._disconnect();
		};

		setTimeout(function() {
	details.start();
		});

		return details;
	}
 };
 /**
 * @license
 * Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 * Code distributed by Google as part of the polymer project is also
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 */
 // @url https://github.com/webcomponents/webcomponentsjs
 // @version 0.7.15
 var WeakMap = (typeof WeakMap !== 'undefined') ? WeakMap : undefined;
 if (typeof WeakMap === "undefined") {
	(function() {
		var defineProperty = Object.defineProperty;
		var counter = Date.now() % 1e9;
		WeakMap = function() {
	this.name = "__st" + (Math.random() * 1e9 >>> 0) + (counter++ + "__");
		};
		WeakMap.prototype = {
	set: function(key, value) {
	 var entry = key[this.name];
	 if (entry && entry[0] === key) entry[1] = value;
	 else defineProperty(key, this.name, {
		value: [key, value],
		writable: true
	 });
	 return this;
	},
	get: function(key) {
	 var entry;
	 return (entry = key[this.name]) && entry[0] === key ? entry[1] : undefined;
	},
	"delete": function(key) {
	 var entry = key[this.name];
	 if (!entry || entry[0] !== key) return false;
	 entry[0] = entry[1] = undefined;
	 return true;
	},
	has: function(key) {
	 var entry = key[this.name];
	 if (!entry) return false;
	 return entry[0] === key;
	}
		};
	})();
 }

 var JsMutationObserver = (typeof JsMutationObserver !== 'undefined') ? JsMutationObserver : undefined;
 (function() {
	if (typeof JsMutationObserver !== 'undefined') {
		return;
	}
	var registrationsTable = new WeakMap();
	var setImmediate;
	if (typeof navigator !== 'undefined' && /Trident|Edge/.test(navigator.userAgent)) {
		setImmediate = setTimeout;
	} else if (window.setImmediate) {
		setImmediate = window.setImmediate;
	} else {
		var setImmediateQueue = [];
		var sentinel = String(Math.random());
		window.addEventListener("message", function(e) {
	if (e.data === sentinel) {
	 var queue = setImmediateQueue;
	 setImmediateQueue = [];
	 queue.forEach(function(func) {
		func();
	 });
	}
		});
		setImmediate = function(func) {
	setImmediateQueue.push(func);
	window.postMessage(sentinel, "*");
		};
	}
	var isScheduled = false;
	var scheduledObservers = [];

	function scheduleCallback(observer) {
		scheduledObservers.push(observer);
		if (!isScheduled) {
	isScheduled = true;
	setImmediate(dispatchCallbacks);
		}
	}

	function wrapIfNeeded(node) {
		return window.ShadowDOMPolyfill && window.ShadowDOMPolyfill.wrapIfNeeded(node) || node;
	}

	function dispatchCallbacks() {
		isScheduled = false;
		var observers = scheduledObservers;
		scheduledObservers = [];
		observers.sort(function(o1, o2) {
	return o1.uid_ - o2.uid_;
		});
		var anyNonEmpty = false;
		observers.forEach(function(observer) {
	var queue = observer.takeRecords();
	removeTransientObserversFor(observer);
	if (queue.length) {
	 observer.callback_(queue, observer);
	 anyNonEmpty = true;
	}
		});
		if (anyNonEmpty) dispatchCallbacks();
	}

	function removeTransientObserversFor(observer) {
		observer.nodes_.forEach(function(node) {
	var registrations = registrationsTable.get(node);
	if (!registrations) return;
	registrations.forEach(function(registration) {
	 if (registration.observer === observer) registration.removeTransientObservers();
	});
		});
	}

	function forEachAncestorAndObserverEnqueueRecord(target, callback) {
		for (var node = target; node; node = node.parentNode) {
	var registrations = registrationsTable.get(node);
	if (registrations) {
	 for (var j = 0; j < registrations.length; j++) {
		var registration = registrations[j];
		var options = registration.options;
		if (node !== target && !options.subtree) continue;
		var record = callback(options);
		if (record) registration.enqueue(record);
	 }
	}
		}
	}
	var uidCounter = 0;
	JsMutationObserver = function(callback) {
		this.callback_ = callback;
		this.nodes_ = [];
		this.records_ = [];
		this.uid_ = ++uidCounter;
	};
	JsMutationObserver.prototype = {
		observe: function(target, options) {
	target = wrapIfNeeded(target);
	if (!options.childList && !options.attributes && !options.characterData || options.attributeOldValue && !options.attributes || options.attributeFilter && options.attributeFilter.length && !options.attributes || options.characterDataOldValue && !options.characterData) {
	 throw new SyntaxError();
	}
	var registrations = registrationsTable.get(target);
	if (!registrations) registrationsTable.set(target, registrations = []);
	var registration;
	for (var i = 0; i < registrations.length; i++) {
	 if (registrations[i].observer === this) {
		registration = registrations[i];
		registration.removeListeners();
		registration.options = options;
		break;
	 }
	}
	if (!registration) {
	 registration = new Registration(this, target, options);
	 registrations.push(registration);
	 this.nodes_.push(target);
	}
	registration.addListeners();
		},
		disconnect: function() {
	this.nodes_.forEach(function(node) {
	 var registrations = registrationsTable.get(node);
	 for (var i = 0; i < registrations.length; i++) {
		var registration = registrations[i];
		if (registration.observer === this) {
			registration.removeListeners();
			registrations.splice(i, 1);
			break;
		}
	 }
	}, this);
	this.records_ = [];
		},
		takeRecords: function() {
	var copyOfRecords = this.records_;
	this.records_ = [];
	return copyOfRecords;
		}
	};

	function MutationRecord(type, target) {
		this.type = type;
		this.target = target;
		this.addedNodes = [];
		this.removedNodes = [];
		this.previousSibling = null;
		this.nextSibling = null;
		this.attributeName = null;
		this.attributeNamespace = null;
		this.oldValue = null;
	}

	function copyMutationRecord(original) {
		var record = new MutationRecord(original.type, original.target);
		record.addedNodes = original.addedNodes.slice();
		record.removedNodes = original.removedNodes.slice();
		record.previousSibling = original.previousSibling;
		record.nextSibling = original.nextSibling;
		record.attributeName = original.attributeName;
		record.attributeNamespace = original.attributeNamespace;
		record.oldValue = original.oldValue;
		return record;
	}
	var currentRecord, recordWithOldValue;

	function getRecord(type, target) {
		return currentRecord = new MutationRecord(type, target);
	}

	function getRecordWithOldValue(oldValue) {
		if (recordWithOldValue) return recordWithOldValue;
		recordWithOldValue = copyMutationRecord(currentRecord);
		recordWithOldValue.oldValue = oldValue;
		return recordWithOldValue;
	}

	function clearRecords() {
		currentRecord = recordWithOldValue = undefined;
	}

	function recordRepresentsCurrentMutation(record) {
		return record === recordWithOldValue || record === currentRecord;
	}

	function selectRecord(lastRecord, newRecord) {
		if (lastRecord === newRecord) return lastRecord;
		if (recordWithOldValue && recordRepresentsCurrentMutation(lastRecord)) return recordWithOldValue;
		return null;
	}

	function Registration(observer, target, options) {
		this.observer = observer;
		this.target = target;
		this.options = options;
		this.transientObservedNodes = [];
	}
	Registration.prototype = {
		enqueue: function(record) {
	var records = this.observer.records_;
	var length = records.length;
	if (records.length > 0) {
	 var lastRecord = records[length - 1];
	 var recordToReplaceLast = selectRecord(lastRecord, record);
	 if (recordToReplaceLast) {
		records[length - 1] = recordToReplaceLast;
		return;
	 }
	} else {
	 scheduleCallback(this.observer);
	}
	records[length] = record;
		},
		addListeners: function() {
	this.addListeners_(this.target);
		},
		addListeners_: function(node) {
	var options = this.options;
	if (options.attributes) node.addEventListener("DOMAttrModified", this, true);
	if (options.characterData) node.addEventListener("DOMCharacterDataModified", this, true);
	if (options.childList) node.addEventListener("DOMNodeInserted", this, true);
	if (options.childList || options.subtree) node.addEventListener("DOMNodeRemoved", this, true);
		},
		removeListeners: function() {
	this.removeListeners_(this.target);
		},
		removeListeners_: function(node) {
	var options = this.options;
	if (options.attributes) node.removeEventListener("DOMAttrModified", this, true);
	if (options.characterData) node.removeEventListener("DOMCharacterDataModified", this, true);
	if (options.childList) node.removeEventListener("DOMNodeInserted", this, true);
	if (options.childList || options.subtree) node.removeEventListener("DOMNodeRemoved", this, true);
		},
		addTransientObserver: function(node) {
	if (node === this.target) return;
	this.addListeners_(node);
	this.transientObservedNodes.push(node);
	var registrations = registrationsTable.get(node);
	if (!registrations) registrationsTable.set(node, registrations = []);
	registrations.push(this);
		},
		removeTransientObservers: function() {
	var transientObservedNodes = this.transientObservedNodes;
	this.transientObservedNodes = [];
	transientObservedNodes.forEach(function(node) {
	 this.removeListeners_(node);
	 var registrations = registrationsTable.get(node);
	 for (var i = 0; i < registrations.length; i++) {
		if (registrations[i] === this) {
			registrations.splice(i, 1);
			break;
		}
	 }
	}, this);
		},
		handleEvent: function(e) {
	e.stopImmediatePropagation();
	switch (e.type) {
	 case "DOMAttrModified":
		var name = e.attrName;
		var namespace = e.relatedNode.namespaceURI;
		var target = e.target;
		var record = new getRecord("attributes", target);
		record.attributeName = name;
		record.attributeNamespace = namespace;
		var oldValue = e.attrChange === MutationEvent.ADDITION ? null : e.prevValue;
		forEachAncestorAndObserverEnqueueRecord(target, function(options) {
			if (!options.attributes) return;
			if (options.attributeFilter && options.attributeFilter.length && options.attributeFilter.indexOf(name) === -1 && options.attributeFilter.indexOf(namespace) === -1) {
		return;
			}
			if (options.attributeOldValue) return getRecordWithOldValue(oldValue);
			return record;
		});
		break;

	 case "DOMCharacterDataModified":
		var target = e.target;
		var record = getRecord("characterData", target);
		var oldValue = e.prevValue;
		forEachAncestorAndObserverEnqueueRecord(target, function(options) {
			if (!options.characterData) return;
			if (options.characterDataOldValue) return getRecordWithOldValue(oldValue);
			return record;
		});
		break;

	 case "DOMNodeRemoved":
		this.addTransientObserver(e.target);

	 case "DOMNodeInserted":
		var changedNode = e.target;
		var addedNodes, removedNodes;
		if (e.type === "DOMNodeInserted") {
			addedNodes = [changedNode];
			removedNodes = [];
		} else {
			addedNodes = [];
			removedNodes = [changedNode];
		}
		var previousSibling = changedNode.previousSibling;
		var nextSibling = changedNode.nextSibling;
		var record = getRecord("childList", e.target.parentNode);
		record.addedNodes = addedNodes;
		record.removedNodes = removedNodes;
		record.previousSibling = previousSibling;
		record.nextSibling = nextSibling;
		forEachAncestorAndObserverEnqueueRecord(e.relatedNode, function(options) {
			if (!options.childList) return;
			return record;
		});
	}
	clearRecords();
		}
	};
 })();
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('dailymotion', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleDailymotion ? 1 : 0;
	var Promise = mono.Promise;

	var iframe = mono.isIframe() && /\/embed\/([\w\-]+)/i.test(document.location.href);

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return dailymotion.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	dailymotion.updateLinks();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	dailymotion.run();
		});
	}

	var dailymotion = {
		contextMenu: null,
		linkCache: {},
		embed: null,
		title: '',
		styleIndex: 0,
		btnId: 'sf__download_btn',
		result: null,
		popupIsShow: false,

		run: function() {
	moduleState = 1;
	if (iframe) {
	 dailymotion.appendIframeButtons();
	 return;
	}

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 return dailymotion.mutationMode.enable();
	}
		},
		changeState: function(state) {
	if (iframe) return;
	moduleState = state;
	dailymotion.rmBtn();
	dailymotion.mutationMode.stop();
	if (state) {
	 dailymotion.run();
	}
		},

		hideMenu: function() {
	if (dailymotion.contextMenu && dailymotion.contextMenu.isShow) {
	 dailymotion.contextMenu.hide();
	 dailymotion.contextMenu = null;
	}
		},

		updateLinks: function() {
	dailymotion.changeState(0);
	dailymotion.changeState(1);
		},

		appendIframeButtons: function() {
	var _this = this;
	var btnObj = SaveFrom_Utils.frameMenu.getBtn({
	 quickBtnStyleObj: {
		display: 'inline-block',
		cursor: 'pointer',
		position: 'relative',
		padding: '9px 10px'
	 },
	 quickBtnCssStyle: {
		backgroundColor: 'rgba(0,0,0,.75)'
	 },
	 singleBtn: true,
	 btnId: _this.btnId,
	 containerStyle: {
		right: '50px',
		top: '10px'
	 },
	 quickBtnIcon: mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
		style: {
			display: 'inline-block',
			width: '16px',
			height: '16px',
			verticalAlign: 'middle'
		}
	 }),
	 on: [
		['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			if (_this.contextMenu && _this.contextMenu.isShow) {
		_this.hideMenu();
		return;
			}

			var id = _this.getIdFromUrl();
			var links = language.download + ' ...';
			var fromCache = _this.linkCache[id];
			if (fromCache) {
		links = SaveFrom_Utils.popupMenu.prepareLinks.dailymotion(fromCache.links, fromCache.title);
			}

			var menu = _this.contextMenu = SaveFrom_Utils.frameMenu.getMenu(this, links, 'sf-frame-menu', {
		container: btnObj.container,
		onShow: function() {
		 btnObj.node.classList.add('sf-over');
		},
		onHide: function() {
		 _this.contextMenu = null;
		 btnObj.node.classList.remove('sf-over');
		}
			});

			if (!fromCache) {
		mono.sendMessage({
		 action: 'getDailymotionLinks',
		 extVideoId: id,
		 metadata: _this.getMetadata(id)
		}, function(response) {
		 var menuLinks = language.noLinksFound;
		 if (response.links) {
			_this.linkCache[id] = response;
			menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.dailymotion(response.links, response.title);
		 }
		 menu.update(menuLinks);
		});
			}
		}],
		['mousedown', function(e) {
			e.stopPropagation();
			if (e.button === 2) {
		mono.off(document.body, 'mousemove', onMouseMove);

		_this.hideMenu();

		if (btnObj.container.parentNode) {
		 btnObj.container.parentNode.removeChild(btnObj.container);
		}
			}
		}]
	 ]
	});

	btnObj.quickBtn.title = language.download;

	btnObj.container = mono.create('div', {
	 class: 'sf-btn-ctr',
	 append: btnObj.node
	});

	mono.on(btnObj.container, 'mouseenter', function() {
	 btnObj.lockHide = true;
	});

	mono.on(btnObj.container, 'mouseleave', function() {
	 btnObj.lockHide = false;
	});

	var hideTimeout = null;
	var hasClassName = false;
	var timeoutFn = function() {
	 if (!btnObj.lockHide) {
		btnObj.container.classList.add('sf-hide-ui');
		hasClassName = true;
	 }
	};
	var onMouseMove = function() {
	 if (hasClassName) {
		btnObj.container.classList.remove('sf-hide-ui');
		hasClassName = false;
	 }

	 clearTimeout(hideTimeout);
	 hideTimeout = setTimeout(timeoutFn, 3 * 1000);
	};
	mono.on(document.body, 'mousemove', onMouseMove);

	btnObj.node.appendChild(mono.create('style', {
	 text: mono.style2Text([{
		selector: [
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) #' + _this.btnId,
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) .sf-frame-menu'
		],
		style: {
			display: 'block'
		}
	 }])
	}));

	document.body.appendChild(btnObj.container);
		},

		getIdFromUrl: function(url) {
	url = url || location.href;
	var videoId = url.match(/\/embed\/video\/([a-z0-9]+)/);
	videoId = videoId && videoId[1];
	return videoId;
		},

		getMetadata: function(id) {
	"use strict";
	var metadata = null;
	var scriptList = mono.getPageScript(document.body.innerHTML, /playerV5/);
	scriptList.some(function(script) {
	 var jsonList = mono.findJson(script);
	 return jsonList.some(function(json) {
		if (json && json.metadata && json.metadata.id === id) {
			metadata = json.metadata;
			return true;
		}
	 });
	});
	return metadata;
		},

		rmBtn: function() {
	var btnList = document.querySelectorAll(['#' + dailymotion.btnId, '.sf-wrapper']);
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
	dailymotion.result = null;
	dailymotion.popupIsShow = false;
		},
		newGetVideoId: function() {
	var id = '';
	var m = /\/video\/([^\/]+)/.exec(location.href);
	if (m) {
	 id = m[1];
	}
	return id;
		},
		newGetMetadata: function(id) {
	return new Promise(function(resolve, reject) {
	 SaveFrom_Utils.bridge({
		func: function(cb) {
			try {
		cb(window.__PLAYER_CONFIG__.metadata);
			} catch (err) {
		cb(null);
			}
		},
		cb: function(result) {
			if (!result || result.id !== id) {
		reject(new Error('Metadata is not found'));
			} else {
		resolve(result);
			}
		}
	 });
	}).catch(function() {
	 return null;
	});
		},
		insertBtn: function(node) {
	if (node.querySelector('.sf-dl-btn')) {
	 return;
	}

	var btn = mono.create('a', {
	 id: dailymotion.btnId,
	 class: ['sf-dl-btn'],
	 href: '#',
	 title: language.download,
	 append: [
		mono.create('span', {
			append: [
		SaveFrom_Utils.svg.getSvg('download', '#000')
			]
		}),
		mono.create('style', {
			text: mono.style2Text({
		selector: '.sf-dl-btn',
		style: {
		 display: 'block',
		 flex: 1,
		 textAlign: 'center',
		 cursor: 'pointer'
		}
			})
		})
	 ],
	 on: ['click', function(e) {
		e.preventDefault();
		e.stopPropagation();

		if (dailymotion.contextMenu && dailymotion.contextMenu.isShow) {
			dailymotion.hideMenu();
		} else {
			var results = language.download + ' ...';
			var menu = dailymotion.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, results, 'sf-popupMenu');

			Promise.resolve().then(function() {
		var id = dailymotion.newGetVideoId();
		if (!id) {
		 throw new Error('Video is not found');
		}

		var cache = dailymotion.linkCache[id];
		if (cache) {
		 return cache;
		}

		return dailymotion.newGetMetadata(id).then(function(metadata) {
		 return mono.sendMessagePromise({
			action: 'getDailymotionLinks',
			extVideoId: id,
			metadata: metadata
		 }).then(function(response) {
			if (!response || !response.links) {
				throw new Error('Links is not found');
			}

			return dailymotion.linkCache[id] = response;
		 });
		});
			}).then(function(response) {
		var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.dailymotion(response.links, response.title);
		menu.update(menuLinks);
			}).catch(function(err) {
		mono.debug('Load links error', err);
		menu.update(language.noLinksFound);
			});
		}
	 }]
	});
	node.appendChild(btn);
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		dailymotion.insertBtn(node);
			}
		},
		queries: [{
			css: 'div[class^="QueueActions__content"]',
			is: 'added'
		}]
	 });
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.dailymotion.*/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		if (!/\/embed\/([\w\-]+)/i.test(document.location.href)) {
	return false;
		}

		try {
	if (location.hostname === window.parent.location.hostname) {
	 return false;
	}
		} catch (e) {}
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('facebook', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleFacebook ? 1 : 0;
	var Promise = mono.Promise;

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return fb.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	fb.changeState(0);
	fb.changeState(1);
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	fb.run();
		});
	}

	var fb = {
		contextMenu: null,
		className: 'savefrom_fb_download',
		isMutation: false,
		run: function() {
	moduleState = 1;

	videoFeed.addStyle();
	photo.injectStyle();

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 this.isMutation = true;
	 this.initEmbedDownloader();
	 this.mutationMode.enable();
	 return;
	}
		},

		changeState: function(state) {
	fb.hideMenu();
	moduleState = state;
	externalMedia.disable();
	photo.rmCurrentPhotoBtn();
	photo.rmDataAttrs();
	videoFeed.rmBtn();
	video.rmBtn();
	fb.mutationMode.stop();
	if (state) {
	 fb.run();
	}
		},

		initEmbedDownloader: function() {
	SaveFrom_Utils.addStyleRules('.' + SaveFrom_Utils.embedDownloader.linkClass + ' img', {
	 opacity: '.5'
	});

	SaveFrom_Utils.embedDownloader.init();
		},

		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	wrapVideoGetLinks: function(player) {
	 switch (player.tagName) {
		case 'EMBED':
			video.getLinksFromEmbed(player, function(links) {
		video.appendLinks(links);
			});
			break;
		case 'VIDEO':
			video.getLinksFromVideo(player, function(links) {
		video.appendLinks(links);
			});
			break;
	 }
	},
	wrapVideoFeedOnLinkHover: function() {
	 if (!moduleState) {
		return;
	 }
	 videoFeed.onLinkHover.apply(this);
	},
	wrapPhotoOnHover: function(context) {
	 if (!moduleState) {
		return;
	 }
	 photo.addCurrentDlBtn(this);
	},
	wrapExternalMediaMouseEnter: function() {
	 if (!moduleState) {
		return;
	 }
	 var link = this;
	 var attr = link.dataset[externalMedia.linkDataAttr];

	 if (attr) {
		clearTimeout(externalMedia.timer);
		return;
	 }

	 if (externalMedia.handle(link)) {
		if (externalMedia.lastLink && externalMedia.lastLink !== link) {
			externalMedia.removeBtn(externalMedia.lastLink);
		}

		SaveFrom_Utils.embedDownloader.hidePanel();
		externalMedia.lastLink = link;
	 } else {
		mono.off(this, 'mouseenter', fb.mutationMode.wrapExternalMediaMouseEnter);
		mono.off(this, 'mouseleave', fb.mutationMode.wrapExternalMediaMouseLeave);
	 }
	},
	wrapExternalMediaMouseLeave: function() {
	 if (!moduleState) {
		return;
	 }
	 var link = this;
	 var attr = link.dataset[externalMedia.linkDataAttr];

	 if (attr) {
		clearTimeout(externalMedia.timer);
		externalMedia.timer = setTimeout(function() {
			externalMedia.removeBtn(link);
		}, 1500);
	 }
	},
	wrapExternalMedia: function(node) {
	 mono.on(node, 'mouseenter', fb.mutationMode.wrapExternalMediaMouseEnter);
	 mono.on(node, 'mouseleave', fb.mutationMode.wrapExternalMediaMouseLeave);
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 var _this = this;

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			for (i = 0; i < 2; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 var isInsert = false;

		 if (mono.matches(node, '#fbxPhotoContentContainer .videoStage ' + node.tagName)) {
			isInsert = true;
		 }

		 if (isInsert) {
			_this.wrapVideoGetLinks(node);
		 } else {
			mono.one(node, 'mouseenter', _this.wrapVideoFeedOnLinkHover);
		 }
		}
			}

			summary = summaryList[2];
			for (n = 0; node = summary.added[n]; n++) {
		fb.hideMenu();

		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var container = SaveFrom_Utils.getParentByClass(node, 'stageWrapper');
		mono.one(container, 'mouseenter', _this.wrapPhotoOnHover);
			}

			summary = summaryList[3];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.sfSkip > 0) {
		 continue;
		}
		node.sfSkip = '1';

		_this.wrapExternalMedia(node);
			}

			summary = summaryList[4];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}

		},
		queries: [{
		css: 'embed',
		is: 'added'
			},
			{
		css: 'video',
		is: 'added'
			},
			{
		css: [
		 '.fbPhotoSnowliftContainer .stageWrapper .stage img.spotlight',
		 '.fbPhotoSnowliftContainer .stageWrapper .stage .videoStage video'
		],
		is: 'added'
			},
			{
		css: 'a',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		},
		hideMenu: function() {
	if (fb.contextMenu) {
	 fb.contextMenu.hide();
	 fb.contextMenu = null;
	}
		}
	};

	var externalMedia = {
		linkDataAttr: 'savefromEd',
		timer: 0,
		lastLink: null,

		re: [
	/https?:\/\/(?:[a-z]+\.)?youtube\.com\/(?:#!?\/)?watch\?[^\s\"\'\<\>]*v=([\w\-]+)/i,
	/https?:\/\/(?:[a-z0-9]+\.)?youtube\.com\/(?:embed|v)\/([\w\-]+)/i,
	/https?:\/\/(?:[a-z]+\.)?youtu\.be\/([\w\-]+)/i,
	/https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/(\d+)(?:\?|$)/i
		],

		thumbnail: {
	youtube: {
	 re: [/ytimg\.com(?:\/|%2F)vi(?:\/|%2F)([\w\-]+)(?:\/|%2F)/i],
	 url: 'http://www.youtube.com/watch?v={vid}'
	}
		},

		disable: function() {
	var panel = SaveFrom_Utils.embedDownloader.panel;
	if (panel) {
	 panel.style.display = 'none';
	}
		},

		removeBtn: function(link) {
	if (!link || typeof(link) != 'object')
	 return;

	var btn = link.querySelector('.' + fb.className);
	if (btn) {
	 btn.parentNode.removeAttribute(mono.dataAttr2Selector(externalMedia.linkDataAttr));
	 btn.parentNode.removeChild(btn);
	}

	link.removeAttribute(mono.dataAttr2Selector(externalMedia.linkDataAttr));

	if (link == this.lastLink)
	 this.lastLink = null;
		},


		checkUrl: function(url, retry) {
	if (!retry && url.search(/https?:\/\/([\w\-]+\.)?facebook\.com\/l\.php/i) > -1) {
	 return this.checkUrl(decodeURIComponent(url), true);
	}

	for (var i = 0, l = this.re.length; i < l; i++) {
	 var m = url.match(this.re[i]);
	 if (m && m.length > 0)
		return m[0];
	}
		},


		handle: function(link) {
	var img = link.querySelector('img');
	if (img) {
	 var parent = img.parentNode;
	 if (img.src && SaveFrom_Utils.getStyle(parent, 'position') == 'relative') {
		var ajaxify = link.getAttribute('ajaxify');

		if (ajaxify && ajaxify.search(/\/flash\/expand_inline/i) > -1) {
			var url = this.getThumbnailUrl(img.src);
			if (url) {
		return this.createButton(url, parent, link, {
		 display: 'block',
		 position: 'absolute',
		 bottom: '3px',
		 right: '3px',
		 zIndex: 9999,
		 margin: 0,
		 width: '16px',
		 height: '16px'
		}, {
		 display: 'block'
		});
			}
		} else if (this.checkUrl(link.href)) {
			return this.createButton(link.href, parent, link, {
		display: 'block',
		position: 'absolute',
		bottom: '3px',
		right: '3px',
		zIndex: 9999,
		margin: 0,
		width: '16px',
		height: '16px'
			}, {
		display: 'block'
			});
		}
	 }

	 return false;
	}

	return this.createButton(link.href, link, link);
		},


		getThumbnailUrl: function(url) {
	for (var i in this.thumbnail) {
	 for (var j = 0; j < this.thumbnail[i].re.length; j++) {
		var vid = SaveFrom_Utils.getMatchFirst(url, this.thumbnail[i].re[j]);
		if (vid)
			return this.thumbnail[i].url.replace(/\{vid\}/ig, vid);
	 }
	}

	return '';
		},


		createButton: function(url, parent, link, styleParent, styleIcon) {
	url = this.checkUrl(url);
	if (!url)
	 return false;

	var btn = document.createElement('a');
	btn.className = fb.className;
	btn.href = 'http://savefrom.net/?url=' + encodeURIComponent(url);
	btn.setAttribute(SaveFrom_Utils.embedDownloader.dataAttr, url);
	btn.title = language.download;

	SaveFrom_Utils.setStyle(btn, {
	 marginLeft: '7px',
	 verticalAlign: 'middle'
	});

	if (styleParent)
	 SaveFrom_Utils.setStyle(btn, styleParent);

	var icon = document.createElement('img');
	icon.className = 'icon';
	icon.src = SaveFrom_Utils.svg.getSrc('download', '#a2db16');
	SaveFrom_Utils.setStyle(icon, {
	 display: 'inline-block',
	 width: '16px',
	 height: '16px',
	 verticalAlign: 'middle',
	 //opacity: '0.9',
	 cursor: 'pointer'
	});

	if (styleIcon)
	 SaveFrom_Utils.setStyle(icon, styleIcon);

	btn.appendChild(icon);

	link.dataset[this.linkDataAttr] = 1;
	parent.appendChild(btn);

	return true;
		}
	};


	var video = {
		getLinksFromEmbed: function(embed, cb) {
	if (!embed) {
	 return cb(null);
	}

	var fv = embed.getAttribute('flashvars');
	if (fv === null) {
	 return cb(null);
	}

	var params = mono.parseUrl(fv).params;
	if (!params) {
	 return cb(null);
	}

	var videoData = null;
	try {
	 videoData = JSON.parse(params).video_data;
	} catch (e) {}

	if (!videoData) {
	 return cb(null);
	}

	if (videoData.progressive) {
	 videoData = videoData.progressive;
	}

	var links = {};
	var typeMap = {
	 sd_src: 'SD',
	 hd_src: 'HD'
	};
	if (!Array.isArray(videoData)) {
	 videoData = [videoData];
	}
	for (var i = 0, item; item = videoData[i]; i++) {
	 ['sd_src', 'hd_src'].forEach(function(type) {
		if (!item[type]) {
			return;
		}

		links[item[type]] = typeMap[type];
	 });
	}

	return cb(links);
		},

		requestLocalVideoLinks: function(videoid) {
	return new Promise(function(resolve, reject) {
	 SaveFrom_Utils.bridge({
		func: function(cb) {
			var err = null;
			var token = null;
			try {
		token = require('DTSGInitialData').token;
			} catch (_err) {
		err = _err.message;
			}
			cb([err, token]);
		},
		cb: function(args) {
			var err = null;
			var token = null;
			if (!args || args[0]) {
		err = new Error('Get token timeout');
			} else {
		token = args[1];
			}
			err ? reject(err) : resolve(token);
		}
	 });
	}).then(function(token) {
	 return new Promise(function(resolve, reject) {
		mono.request({
			method: 'POST',
			url: 'https://www.facebook.com/video/tahoe/async/' + videoid + '/?' + mono.param({
		chain: true,
		isvideo: true
			}),
			data: mono.param({
		__a: 1,
		fb_dtsg: token
			})
		}, function(err, resp, data) {
			if (err || !data) {
		reject(new Error('Request data error'));
			} else {
		resolve(data);
			}
		});
	 });
	}).then(function(data) {
	 return new Promise(function(resolve, reject) {
		mono.sendMessage({
			action: 'getFacebookLinksFromData',
			extVideoId: videoid,
			data: data
		}, function(response) {
			if (!response || !response.links) {
		reject(new Error('Get links from data error'));
			} else {
		resolve(response);
			}
		});
	 });
	}).catch(function(err) {
	 mono.error('get local links error', err);
	 throw err;
	});
		},

		requestBgVideoLinks: function(videoId) {
	return new Promise(function(resolve, reject) {
	 mono.sendMessage({
		action: 'getFacebookLinks',
		extVideoId: videoId
	 }, function(response) {
		if (!response || !response.links) {
			reject(new Error('Get links error'));
		} else {
			resolve(response);
		}
	 });
	}).catch(function(err) {
	 mono.error('get links error', err);
	 throw err;
	});
		},

		requestVideoLinksById: function(videoId) {
	return Promise.resolve().then(function() {
	 return video.requestLocalVideoLinks(videoId);
	}).catch(function() {
	 return video.requestBgVideoLinks(videoId);
	});
		},

		requestVideoLinks: function(videoId, cb) {
	return video.requestVideoLinksById(videoId).then(function(response) {
	 cb(response.links, response.title);
	}, function(err) {
	 cb();
	});
		},

		getLinksFromVideo: function(videoNode, cb) {
	if (!videoNode) {
	 return cb(null);
	}

	var links = {};

	var id = null;

	if (!id) {
	 // video in popup
	 var ctr = mono.getParent(videoNode, '.uiStreamStory[data-story-id]');
	 var storyId = /:(\d+)$/.exec(ctr && ctr.dataset.storyId);
	 storyId = storyId && storyId[1];
	 // mono.debug('uiStreamStory', storyId);
	 if (storyId) {
		id = storyId;
	 }
	}
	if (!id) {
	 // from wall
	 var parent = mono.getParentByClass(videoNode, 'fbUserContent');
	 // mono.debug('fbUserContent parent', !!parent);
	 if (parent) {
		var videoLink = parent.querySelector('a[data-video-id]');
		// mono.debug('fbUserContent videoLink', !!videoLink);
		if (videoLink) {
			var videoId = videoLink && videoLink.dataset.videoId;
			// mono.debug('fbUserContent videoId', videoId);
			if (videoId) {
		id = videoId;
			}
		}
	 }
	}
	if (!id) {
	 // from stream wall
	 var parent = mono.getParent(videoNode, '.userContentWrapper');
	 // mono.debug('userContentWrapper parent', !!parent);
	 if (parent) {
		var postLinkNode = parent.querySelector('div[id^="feed_subtitle_"] a[data-video-channel-id]');
		// mono.debug('userContentWrapper postLinkNode', !!postLinkNode);
		if (postLinkNode) {
			var m = /\/videos\/(\d+)/.exec(postLinkNode.href);
			var videoId = m && m[1];
			// mono.debug('userContentWrapper videoId', videoId);
			if (videoId) {
		id = videoId;
			}
		} else {
			var aTheater = parent.querySelectorAll('a.profileLink, a[rel="theater"], #fbPhotoSnowliftTimestamp > a[href]');
			// mono.debug('userContentWrapper aTheater', !!aTheater);
			var videoId = null;
			[].slice.call(aTheater).some(function(item) {
		var m = /\/videos\/(\d+)/.exec(item.href);
		return videoId = m && m[1];
			});
			// mono.debug('userContentWrapper videoId', videoId);
			if (videoId) {
		id = videoId;
			}
		}
	 }
	}
	if (!id) {
	 // facebook.com/username/videos/n/
	 var isPageLet = false;
	 var isOpenedVideo = false;
	 var streamPagelet = document.getElementById('stream_pagelet');
	 var prevElement = streamPagelet && streamPagelet.previousElementSibling;
	 if (prevElement && prevElement.contains(videoNode)) {
		isPageLet = true;
	 }
	 if (!isPageLet) {
		var uiStreamStory = document.querySelector('.uiStreamStory');
		var uiStreamStoryWrap = uiStreamStory && uiStreamStory.parentNode;
		uiStreamStoryWrap = uiStreamStoryWrap && uiStreamStoryWrap.parentNode;
		if (uiStreamStoryWrap && uiStreamStoryWrap.contains(videoNode)) {
			// is opened video
			isOpenedVideo = true;
		}
	 }
	 if (isPageLet || isOpenedVideo) {
		var videoId = video.getVideoIdFromUrl();
		// mono.debug('isPageLet || isOpenedVideo', videoId);
		if (videoId) {
			id = videoId;
		}
	 }
	}
	if (!id) {
	 var permalink = mono.getParent(videoNode, '#pagelet_group_permalink');
	 if (permalink) {
		var m = /video_id:"?([^,"]+)/.exec(document.body.innerHTML);
		var videoId = m && m[1];
		// mono.debug('pagelet_group_permalink videoId', videoId);
		if (videoId) {
			id = videoId;
		}
	 }
	}
	id && (links.id = id);

	if (videoNode.src && /^https?:/.test(videoNode.src)) {
	 var ext = SaveFrom_Utils.getFileExtension(videoNode.src, 'mp4');
	 links[videoNode.src] = ext.toUpperCase();
	}

	var src = videoNode.querySelectorAll('source');
	if (src && src.length > 0) {
	 for (var i = 0; i < src.length; i++) {
		var ext = SaveFrom_Utils.getFileExtension(src[i].src, 'mp4');
		links[src[i].src] = ext.toUpperCase();
	 }
	}

	// mono.debug('links', links);

	if (Object.keys(links).length) {
	 return cb(links);
	} else {
	 return cb(null);
	}
		},

		getVideoIdFromUrl: function() {
	var videoId = null;
	SaveFrom_Utils.embedDownloader.hostings.facebook.re.some(function(reg) {
	 var m = reg.exec(location.href);
	 if (m) {
		videoId = m[1];
		return true;
	 }
	});
	return videoId;
		},


		getFileName: function(url) {
	var name = SaveFrom_Utils.getFileName(url);
	if (name)
	 return name;

	var d = SaveFrom_Utils.dateToObj();
	var dateStr = d.year + '-' + d.month + '-' + d.day + '_' +
	 d.hour + '-' + d.min;

	return 'facebook_' + dateStr + '.' +
	 SaveFrom_Utils.getFileExtension(url, 'mp4');
		},

		prepareLinks: function(links, _title) {
	var menuLinks = [];
	for (var url in links) {
	 var title = this.getFileName(url);
	 var extPos = title.lastIndexOf('.');
	 var ext = title.substr(extPos + 1);
	 title = _title || title.substr(0, extPos);
	 var format = ext.toUpperCase();

	 var quality = links[url];
	 var popupLink = {
		href: url,
		title: title,
		format: format,
		quality: quality,
		forceDownload: true
	 };
	 menuLinks.push(popupLink);
	}
	if (menuLinks.length === 0) {
	 menuLinks = language.noLinksFound;
	}
	return menuLinks;
		},

		appendLinks: function(links) {
	if (!links) {
	 return;
	}

	var box = document.getElementById('fbPhotoPageMediaInfo');

	if (box === null) {
	 return;
	}

	var title = document.querySelector('h2.uiHeaderTitle');
	if (title) {
	 title = title.textContent;
	}

	if (!box || box.querySelector('.' + fb.className)) {
	 return;
	}

	var panel = document.createElement('div');
	panel.className = fb.className;

	var button = mono.create('div', {
	 title: language.download,
	 style: {
		display: 'inline-block',
		width: '16px',
		height: '16px',
		backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#a2db16') + ')',
		backgroundRepeat: 'no-repeat',
		backgroundPosition: 'center center',
		verticalAlign: 'middle',
		cursor: 'pointer'
	 }
	});

	panel.appendChild(button);


	var popupMenuLinks = null;

	button.addEventListener('click', function() {
	 if (fb.contextMenu && fb.contextMenu.isShow) {
		fb.hideMenu();
		return;
	 }

	 var menu = fb.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', fb.className + '_popup');

	 if (popupMenuLinks) {
		menu.update(popupMenuLinks);
	 } else
	 if (links.id) {
		var id = links.id;
		delete links.id;
		video.requestVideoLinks(id, function(_links, title) {
			if (_links) {
		popupMenuLinks = SaveFrom_Utils.popupMenu.prepareLinks.facebook(_links, title);
			} else {
		popupMenuLinks = video.prepareLinks(links);
			}
			menu.update(popupMenuLinks);
		});
	 } else {
		popupMenuLinks = video.prepareLinks(links, title);
		menu.update(popupMenuLinks);
	 }
	});

	box.appendChild(panel);

	box = null;
	panel = null;
	button = null;
		},
		rmBtn: function() {
	var btnList = document.querySelectorAll('.' + fb.className);
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		}
	};

	var photo = {
		style: null,
		getFilenameFromUrl: function(url) {
	return SaveFrom_Utils.getMatchFirst(url, /\/([^\/]+\.[a-z0-9]{3,4})(?:\?|$)/i);
		},
		getPhotoIdFromUrl: function() {
	var photoId = null;
	var params = mono.parseUrl(location.href);
	if (params.fbid) {
	 photoId = params.fbid;
	}
	return photoId;
		},
		prepPhotoUrl: function(url) {
	if (url) {
	 if (!/[?&]dl=1/.test(url)) {
		var sep = /\?/.test(url) ? '&' : '?';
		url += sep + 'dl=1'
	 }
	}
	return url;
		},
		rmCurrentPhotoBtn: function(insertContainer) {
	var exBtn = undefined;
	var imgList = document.querySelectorAll('.sf-dl-current-photo-btn');
	for (var i = 0, imgItem; imgItem = imgList[i]; i++) {
	 if (!insertContainer || !insertContainer.contains(imgItem)) {
		imgItem.parentNode.removeChild(imgItem);
	 } else {
		exBtn = imgItem;
	 }
	}
	return exBtn;
		},
		injectStyle: function() {
	if (this.style) {
	 if (!this.style.parentNode) {
		document.head.appendChild(this.style);
	 }
	 return;
	}

	this.style = mono.create('style', {
	 text: mono.style2Text({
		'div > .sf-dl-current-photo-btn': {
			display: 'none',
			position: 'absolute',
			top: '10px',
			left: '10px',
			width: '28px',
			height: '24px',
			border: 0,
			zIndex: 100,
			cursor: 'pointer',

			backgroundColor: '#000',
			padding: 0,
			borderRadius: '2px',
			opacity: 0.4,
			transition: 'opacity 100ms linear',

			lineHeight: 0
		},
		'div > .sf-dl-current-photo-btn svg': {
			margin: '4px'
		},
		'div > .sf-dl-current-photo-btn:hover': {
			opacity: 0.8
		},
		'div > .sf-dl-current-photo-btn:hover svg path': {
			fill: '#00B75A'
		},
		'body:not(.fullScreen) div:hover > .sf-dl-current-photo-btn': {
			display: 'block'
		}
	 })
	});

	document.head.appendChild(this.style);
		},
		getPhotoUrlFromCtr: function(container) {
	var result = [];
	var img = container.querySelector('img.spotlight') || container.querySelector('img.fbPhotoImage');
	if (img) {
	 result.push(img.src);
	}
	return result;
		},
		getVideoUrlFromPhotoCtr: function(imgContainer) {
	var videoId = null;
	var videoNode = imgContainer.querySelector('.stage .videoStage video');
	if (videoNode) {
	 var parent = mono.getParent(videoNode, '.fbPhotoSnowliftPopup');
	 if (parent) {
		var postLinkNode = parent.querySelector('div[id^="feed_subtitle_"] a[data-video-channel-id]');
		if (postLinkNode) {
			var m = /\/videos\/(\d+)/.exec(postLinkNode.href);
			if (m) {
		videoId = m[1];
			}
		}
	 }
	}
	return videoId;
		},
		getLinksFromPhotoCtr: function(imgContainer) {
	var isVideo = mono.matches(imgContainer, '.stageWrapper.showVideo');
	if (isVideo) {
	 return Promise.resolve().then(function() {
		var videoId = video.getVideoIdFromUrl();
		// mono.debug('getVideoIdFromUrl', videoId);
		if (!videoId) {
			videoId = photo.getVideoUrlFromPhotoCtr(imgContainer);
			// mono.debug('getVideoUrlFromPhotoCtr', videoId);
		}
		if (videoId) {
			return video.requestVideoLinksById(videoId);
		}
	 }).then(function( /**{links:string[], title:string}*/ response) {
		// mono.debug('requestVideoLinksById', response);
		return SaveFrom_Utils.popupMenu.prepareLinks.facebook(response.links, response.title);
	 });
	} else {
	 return Promise.resolve().then(function() {
		var fbid = photo.getPhotoIdFromUrl();
		// mono.debug('getPhotoIdFromUrl', fbid);
		if (fbid) {
			return new Promise(function(resolve, reject) {
		mono.sendMessage({
		 action: 'getFacebookPhotoUrl',
		 fbid: fbid
		}, function(urls) {
		 // mono.debug('getFacebookPhotoUrl', url);
		 if (!urls || !urls.length) {
			reject(new Error('getFacebookPhotoUrl can\'t get url'))
		 } else {
			resolve(urls);
		 }
		});
			});
		} else {
			throw new Error('Can\'t get photo id from url');
		}
	 }).catch(function(err) {
		var urls = photo.getPhotoUrlFromCtr(imgContainer);
		// mono.debug('getPhotoUrlFromCtr', url);
		return urls;
	 }).then(function(urls) {
		// mono.debug('urls', urls);
		if (!urls || !urls.length) {
			throw new Error('Photo url not found');
		}

		return urls.map(function(url) {
			var dlUrl = photo.prepPhotoUrl(url);
			var photoFileName = mono.fileName.modify(photo.getFilenameFromUrl(dlUrl));
			var m = /(.+)\.([^.]+)$/.exec(photoFileName);
			var photoExt = 'jpg';
			var photoTitle = photoFileName;
			if (m) {
		photoExt = m[1];
		photoTitle = m[2];
			}

			return {
		href: dlUrl,
		title: photoTitle,
		quality: language.download,
		format: ' ',
		ext: photoExt,
		isBlank: true
			};
		});
	 });
	}
		},
		addDlCurrentPhotoBtn: function(imgContainer) {
	var exBtn = this.rmCurrentPhotoBtn(imgContainer);
	if (exBtn) {
	 return;
	}

	var button = mono.create('a', {
	 class: 'sf-dl-current-photo-btn',
	 href: '#',
	 title: language.download,
	 append: [
		SaveFrom_Utils.svg.getSvg('download', '#FFF', 16)
	 ],
	 on: ['click', function(e) {
		e.stopPropagation();
		e.preventDefault();

		if (fb.contextMenu && fb.contextMenu.isShow) {
			fb.hideMenu();
			return;
		}

		var onKeyDown = function(e) {
			if (e.keyCode === 18 || e.keyCode === 17) return;
			menu.hide();
			document.removeEventListener('keydown', onKeyDown);
		};

		var menu = fb.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', "photoDlMenu", {
			parent: imgContainer,
			onShow: function() {
		if (fb.isMutation) {
		 return;
		}
		document.addEventListener('keydown', onKeyDown);
			},
			onHide: function() {
		if (fb.isMutation) {
		 return;
		}
		document.removeEventListener('keydown', onKeyDown);
			}
		});

		photo.getLinksFromPhotoCtr(imgContainer).then(function(links) {
			// mono.debug('getLinksFromPhotoCtr', links);
			links.forEach(function(link) {
		link.func = function() {
		 menu.hide();
		};
			});

			menu.update(links);
		}).catch(function(err) {
			mono.debug('Get photo links error', err);
			menu.update(language.noLinksFound);
		});
	 }]
	});

	imgContainer.appendChild(button);
		},
		addCurrentDlBtn: function(container) {
	if (container.dataset.sfSkip > 0) {
	 return;
	}
	container.dataset.sfSkip = '1';

	this.addDlCurrentPhotoBtn(container);
		},
		rmDataAttrs: function() {
	var dataAttr = mono.dataAttr2Selector('sfSkip');
	var dataAttrList = document.querySelectorAll('*[' + dataAttr + ']');
	for (var i = 0, item; item = dataAttrList[i]; i++) {
	 item.removeAttribute(dataAttr);
	}
		}
	};

	var videoFeed = {
		style: null,
		addStyle: function() {
	if (this.style) {
	 if (!this.style.parentNode) {
		document.head.appendChild(this.style);
	 }
	 return;
	}

	this.style = mono.create('style', {
	 class: 'sfFeedStyle',
	 text: mono.style2Text([{
		selector: '.' + fb.className + '-feed' + '.sf-feed',
		style: {
			display: 'none',
			width: '20px',
			height: '20px',
			padding: 0,
			position: 'absolute',
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#a2db16') + ') center no-repeat transparent',
			backgroundSize: '16px',
			top: '5px',
			left: '5px',
			zIndex: 1,
			cursor: 'pointer'
		}
	 }, {
		selector: 'div[role="dialog"] .' + fb.className + '-feed' + '.sf-feed',
		style: {
			top: '40px'
		}
	 }, {
		selector: 'body:not(.fullScreen) div:hover > .' + fb.className + '-feed' + '.sf-feed',
		style: {
			display: 'block'
		}
	 }, {
		selector: '.' + fb.className + '-feed' + '.sf-feed:active',
		style: {
			outline: 0
		}
	 }])
	});

	document.head.appendChild(this.style);
		},
		onDlBtnClick: function(e) {
	e.preventDefault();
	e.stopPropagation();

	if (fb.contextMenu && fb.contextMenu.isShow) {
	 fb.hideMenu();
	 return;
	}

	try {
	 var links = JSON.parse(this.dataset.sfDlLinks);
	} catch (e) {
	 return;
	}

	var menu = fb.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', fb.className + '_popup');

	if (links.id) {
	 var id = links.id;
	 delete links.id;
	 video.requestVideoLinks(id, function(_links, title) {
		var popupMenuLinks;
		if (_links) {
			popupMenuLinks = SaveFrom_Utils.popupMenu.prepareLinks.facebook(_links, title);
		} else {
			popupMenuLinks = video.prepareLinks(links);
		}
		menu.update(popupMenuLinks);
	 });
	} else {
	 var popupMenuLinks = video.prepareLinks(links);
	 menu.update(popupMenuLinks);
	}
		},
		addDownloadBtn: function(container, links) {
	var prevBtn = container.querySelector('.' + fb.className + '-feed');
	if (prevBtn) {
	 prevBtn.parentNode.removeChild(prevBtn);
	}

	container.appendChild(mono.create('a', {
	 data: {
		sfDlLinks: JSON.stringify(links)
	 },
	 title: language.download,
	 class: [fb.className + '-feed', 'sf-feed'],
	 href: '#',
	 on: ['click', videoFeed.onDlBtnClick]
	}));
		},
		onLinkHover: function() {
	var _this = this;
	if (this.dataset.hasSfFeedBtn > 1) {
	 return;
	}
	this.dataset.hasSfFeedBtn = '1';

	var onReady = function(links) {
	 if (!links) {
		_this.dataset.hasSfFeedBtn = 0;
		return;
	 }

	 if (mono.matches(_this, '.uiStreamStory ' + _this.tagName) ||
		mono.matches(_this, '.fbPhotoSnowliftContainer ' + _this.tagName)
	 ) {
		var videoInPhoto = mono.getParent(_this, '.fbPhotoSnowliftPopup .stageWrapper');
		if (!videoInPhoto) {
			videoFeed.addDownloadBtn(_this.parentNode, links);
		}
		return;
	 }

	 var timeLineMainColumn = document.getElementById('pagelet_timeline_main_column') ||
		document.getElementById('stream_pagelet') ||
		document.getElementById('mainContainer');


	 if (timeLineMainColumn && timeLineMainColumn.contains(_this)) {
		videoFeed.addDownloadBtn(_this.parentNode, links);
		return;
	 }

	 var streamPagelet = document.getElementById('stream_pagelet');
	 var prevElement = streamPagelet && streamPagelet.previousElementSibling;
	 if (prevElement && prevElement.contains(_this)) {
		videoFeed.addDownloadBtn(_this.parentNode, links);
		return;
	 }

	 var uiStreamStory = document.querySelector('.uiStreamStory');
	 var uiStreamStoryWrap = uiStreamStory && uiStreamStory.parentNode;
	 uiStreamStoryWrap = uiStreamStoryWrap && uiStreamStoryWrap.parentNode;
	 if (uiStreamStoryWrap && uiStreamStoryWrap.contains(_this)) {
		// is opened video
		videoFeed.addDownloadBtn(_this.parentNode, links);
		return;
	 }
	};

	var videoEl = this;
	if (videoEl.tagName === 'VIDEO') {
	 videoEl = videoEl.querySelector('embed') || this;
	}

	if (videoEl.tagName === 'EMBED') {
	 video.getLinksFromEmbed(videoEl, onReady);
	} else
	if (videoEl.tagName === 'VIDEO') {
	 video.getLinksFromVideo(videoEl, onReady);
	}
		},
		rmBtn: function() {
	var dataAttr = mono.dataAttr2Selector('hasSfFeedBtn');
	var dataAttrList = document.querySelectorAll('*[' + dataAttr + ']');
	for (var i = 0, item; item = dataAttrList[i]; i++) {
	 item.removeAttribute(dataAttr);
	}
	var btnList = document.querySelectorAll('.' + fb.className + '-feed');
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.facebook.com/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('lm', function(moduleName, initData) {
	"use strict";
	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.lmMediaHosting ? 1 : 0;

	if (preference.showUmmyInfo) {
		mono.asyncCall(function() {
	checkUmmyPage();
		});
	}

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return lm.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	lm.savefromLinkCount = -1;
	lm.run();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	lm.run();
		});
	}

	var checkUmmyPage = function() {
		if (typeof location === 'object' && /videodownloader\.ummy\.net/.test(location.href)) {
	if (/pozdravlyaem|congratulations|tebrikler/.test(location.href)) {
	 mono.sendMessage({
		action: 'updateOption',
		key: 'showUmmyInfo',
		value: 0
	 });
	 mono.sendMessage({
		action: 'updateOption',
		key: 'ummyDetected',
		value: 1
	 });
	}
		}
	};

	var lm = {
		htmlAfter: '',

		linkText: '',

		linkStyle: {
	'border': 'none',
	'textDecoration': 'none',
	'padding': '0',
	'position': 'relative'
		},

		imgStyle: {
	'border': 'none',
	'width': 'auto',
	'height': 'auto'
		},

		buttonSrc: 'data:image/gif;base64,R0lGODlhEAAQAOZ3APf39+Xl5fT09OPj4/Hx8evr6/3+/u7u7uDh4OPi497e3t7e3/z8/P79/X3GbuXl5ubl5eHg4WzFUfb39+Pj4lzGOV7LOPz7+/n6+vn5+ZTLj9/e387Ozt7f3/7+/vv7/ISbePn5+m/JV1nRKXmVbkCnKVrSLDqsCuDh4d/e3uDn3/z7/H6TdVeaV1uSW+bn5v39/eXm5eXm5kyHP/f39pzGmVy7J3yRd9/f3mLEKkXCHJbka2TVM5vaZn6Wdfn6+YG/c/r5+ZO/jeLi41aHTIeageLn4f39/vr6+kzNG2PVM5i+lomdf2CXYKHVmtzo2YXNeDqsBebl5uHh4HDKWN3g3kKqEH6WeZHTXIPKdnSPbv79/pfmbE7PHpe1l4O8dTO5DODg4VDLIlKUUtzo2J7SmEWsLlG4NJbFjkrJHP7+/VK5Nfz8+zmnC3KKa+Hg4OHh4Y63j/3+/eDg4Ojo6P///8DAwP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAHcALAAAAAAQABAAAAfWgHd2g4SFhYJzdYqLjIpzgx5bBgYwHg1Hk2oNDXKDFwwfDF5NLmMtcStsn4MhGT8YS04aGmU1QRhIGYMTADQAQlAODlloAMYTgwICRmRfVBISIkBPKsqDBAREZmcVFhYVayUz2IMHB1dWOmImI2lgUVrmgwUFLzdtXTxKSSduMfSD6Aik48MGlx05SAykM0gKhAAPAhTB0oNFABkPHg5KMIBCxzlMQFQZMGBIggSDpsCJgGDOmzkIUCAIM2dOhEEcNijQuQDHgg4KOqRYwMGOIENIB90JBAA7',

		sfref: '&utm_source={sfHelperName}&utm_medium=extensions&utm_campaign=link_modifier',

		pageUrl: 'http://savefrom.net/',
		anchorAttribute: 'savefrom_lm',
		anchorAttributeLink: 'savefrom_lm_is_link',
		anchorIndexAttribute: 'savefrom_lm_index',

		linkRegExp: null,

		savefromLinkCount: 0,

		re: {
	mediahosting: {
	 'youtube.com': [
		/^https?:\/\/([a-z]+\.)?youtube\.com\/(#!?\/)?watch\?.*v=/i,
		/^https?:\/\/([a-z0-9]+\.)?youtube\.com\/(embed|v)\/[\w\-]+/i
	 ],
	 'youtu.be': [/^https?:\/\/([a-z]+\.)?youtu\.be\/[\w\-]+/i],
	 'google.com': [/^http:\/\/video\.google\.com\/videoplay\?.*docid=/i],
	 'break.com': [/^http:\/\/(www\.)?break\.com\/(index|movies\w*|(\w+\-)+\w+)\/.+\.html$/i, /^http:\/\/view\.break\.com\/\d+/i],
	 'vimeo.com': [/^http:\/\/([\w\-]+\.)?vimeo\.com\/\d+$/i],
	 'sevenload.com': [/^http:\/\/([\w\-]+\.)?sevenload\.com\/videos\/[-\w\+\/=]+/i, /^http:\/\/([\w\-]+\.)?sevenload\.com\/shows\/.+/i],
	 'facebook.com': [/^https?:\/\/(?:www\.)facebook\.com\/([^\/]+\/)*video\.php\?([^&]+&)*v=\d+/i],
	 //'rutube.ru': [/^http:\/\/rutube\.ru\/tracks\/\d+\.html\?.*v=[a-f0-9]+/i],
	 'mail.ru': [/^http:\/\/([a-z0-9_-]+\.)?video\.mail\.ru\/(.+\/)+\d+\.html/i, /^http:\/\/r\.mail\.ru\/\w+\/video\.mail\.ru\/(.+\/)+\d+\.html/i],
	 'yandex.ru': [/^http:\/\/video\.yandex\.ru\/users\/[-\w,!\+]+\/view\/[-\w,!\+]+\/?/i],
	 'rambler.ru': [/^http:\/\/vision\.rambler\.ru\/users\/[^\/\s]+\/\d+\/[-\w_\+!]+\/?/i],
	 'smotri.com': [/^http:\/\/([a-z0-9_-]+\.)?smotri\.com\/video\/view\/\?.*id=v[0-9a-f]/i],
	 'tvigle.ru': [/^http:\/\/(www\.)?tvigle\.ru\/channel\/\d+\?.*vid_id=\d+/i, /^http:\/\/(www\.)tvigle\.ru\/prg\/\d+\/\d+/i],
	 '1tv.ru': [/^http:\/\/(www\.)?1tv\.ru(\:\d+)?\/newsvideo\/\d+/i, /^http:\/\/(www\.)?1tv\.ru(\:\d+)?\/news\/\w+\d+/i],
	 'ntv.ru': [/^http:\/\/news\.ntv\.ru\/(\w+\/)?\d+\/video\/?/i],
	 'vesti.ru': [/^http:\/\/(www\.)?vesti\.ru\/videos\?.*vid=\d+/i],
	 'mreporter.ru': [/^http:\/\/(www\.)?mreporter\.ru\/reportermessages\!viewreport\.do[^\?]*\?.*reportid=\d+/i],
	 'autoplustv.ru': [/^http:\/\/(www\.)?autoplustv\.ru\/494\/\?id=\d+/i],
	 'amik.ru': [/^http:\/\/(www\.)?amik\.ru\/video\/vid\d+\.html/i, /^http:\/\/(www\.)?amik\.ru\/video\/vcid\d+\.html/i],
	 'life.ru': [/^http:\/\/([\w+\-]+\.)?life\.ru\/video\/\d+/i]
	}
		},


		parseHref: function(href, search) {
	var res = [];
	res.push(href);

	var i = href.toLowerCase().indexOf('http://', 7);
	if (i > 7) {
	 res.push(href.substring(i));
	} else if (search) {
	 var h = search.match(/http%3a(%2f%2f|\/\/)[^\s\&\"\<\>]+/i);
	 if (h && h.length > 0) {
		res.push(decodeURIComponent(h[0]));
	 } else {
		var s = '';
		try {
			s = decodeURIComponent(search);
		} catch (err) {}

		if (s) {
			// facebook
			h = s.match(/((?:aHR0cDovL|aHR0cHM6Ly)[a-z0-9+\/=]+)/i);
			if (h && h.length > 1) {
		try {
		 h = atob(h[1]);
		} catch (e) {
		 h = '';
		}
		if (h.search(/^https?:\/\//i) != -1)
		 res.push(decodeURIComponent(h));
			}
		}
	 }
	}

	return res;
		},


		href: function(a) {
	return a.getAttribute('href') || '';
		},


		getElementIndex: function(e) {
	var html = e.innerHTML;
	if (!html || html == ' ')
	 return 1;

	var bg = e.style.backgroundImage;
	if (bg && bg != 'none')
	 return 1;

	var c = e.getElementsByTagName('*');
	for (var i = 0; i < c.length; i++) {
	 if (c[i].tagName == 'IMG')
		return 2;
	 else {
		bg = c[i].style.backgroundImage;
		if (bg && bg != 'none')
			return 1;
	 }
	}

	return 0;
		},


		run: function() {
	SaveFrom_Utils.embedDownloader.init();

	lm.sfref = lm.sfref.replace('{sfHelperName}', preference.sfHelperName);

	var prefMediaHosting = !!preference.lmMediaHosting;

	moduleState = 1;

	lm.linkRegExp = {};

	if (prefMediaHosting) {
	 for (var i in lm.re.mediahosting)
		lm.linkRegExp[i] = lm.re.mediahosting[i];
	}


	var a = document.getElementsByTagName('a');
	if (lm.savefromLinkCount != a.length) {
	 lm.savefromLinkCount = a.length;

	 var found = {},
		lastHref = '';

	 for (var i = 0, len = a.length; i < len; i++) {
		var href = handleAnchor(a[i]);
		if (href) {
			var index = 0;
			var attr = a[i].getAttribute(lm.anchorIndexAttribute);
			if (attr === 0 || attr)
		index = parseInt(attr);
			else {
		index = lm.getElementIndex(a[i]);
		a[i].setAttribute(lm.anchorIndexAttribute, index);
			}

			if (found[href]) {
		if (index < found[href].index) {
		 found[href].elements = [a[i]];
		 found[href].index = index;
		 lastHref = href;
		} else if (index == found[href].index && href != lastHref) {
		 found[href].elements.push(a[i]);
		 lastHref = href;
		}
			} else {
		found[href] = {
		 index: index,
		 elements: [a[i]]
		};

		lastHref = href;
			}
		}
	 }

	 var count = 0;
	 for (var i in found) {
		for (var j = 0, len = found[i].elements.length; j < len; j++) {
			var e = found[i].elements[j];
			count++;
			if (!e.getAttribute(lm.anchorAttribute))
		modifyLink(e, i);
		}
	 }
	}



	function checkLink(link, domain) {
	 if (!link)
		return false;

	 if (link == window.location.href)
		return false;

	 domain = SaveFrom_Utils.getTopLevelDomain(domain);
	 if (!domain || !lm.linkRegExp[domain])
		return false;

	 for (var i = 0; i < lm.linkRegExp[domain].length; i++) {
		if (link.search(lm.linkRegExp[domain][i]) != -1)
			return true;
	 }

	 return false;
	}


	function handleAnchor(obj) {
	 var href = obj.href;
	 if (typeof href === 'string' && href.search(/^https?:\/\/([\w\-]+\.)?savefrom\.net\//i) == -1) {
		var hrefArray = lm.parseHref(href, obj.search);

		if (hrefArray.length > 0) {
			if (lm.href(obj).indexOf('#') != 0 && checkLink(hrefArray[0], obj.hostname)) {
		return hrefArray[0];
			} else if (hrefArray.length > 1) {
		for (var j = 1; j < hrefArray.length; j++) {
		 var aTemp = document.createElement('a');
		 aTemp.href = hrefArray[j];
		 if (lm.href(aTemp).indexOf('#') != 0 && checkLink(hrefArray[j], aTemp.hostname)) {
			return hrefArray[j];
		 }
		}
			}
		}
	 }

	 return '';
	}

	function modifyLink(obj, link) {
	 if (!obj)
		return;

	 obj.setAttribute(lm.anchorAttribute, '1');

	 var box = document.createElement('span');
	 box.setAttribute('style', 'padding: 0; margin: 0; margin-left: 5px;');
	 box.addEventListener('click', function(e) {
		e.stopPropagation();
	 });

	 var parent = obj.parentNode;
	 if (!parent)
		return;

	 try {
		link = encodeURIComponent(link);
	 } catch (err) {
		return;
	 }

	 var href = lm.pageUrl + '?url=' + link;
	 if (lm.sfref)
		href += lm.sfref;

	 // add button
	 var a = document.createElement('a');
	 a.href = href;
	 a.target = '_blank';
	 a.title = language.lmButtonTitle;

	 a.style.backgroundImage = 'url(' + lm.buttonSrc + ')';
	 a.style.backgroundRepeat = 'no-repeat';
	 a.style.width = '16px';
	 a.style.height = '16px';
	 a.style.display = 'inline-block';

	 for (var i in lm.linkStyle)
		a.style[i] = lm.linkStyle[i];

	 if (obj.style.zIndex) {
		a.style.zIndex = obj.style.zIndex;
	 }

	 a.setAttribute(lm.anchorAttribute, '1');
	 a.setAttribute(lm.anchorAttributeLink, '1');
	 if (lm.linkText) {
		a.textContent = lm.linkText;
	 }

	 box.appendChild(a);

	 if (lm.htmlAfter)
		box.textContent += lm.htmlAfter;


	 if (obj.nextSibling)
		parent.insertBefore(box, obj.nextSibling);
	 else
		parent.appendChild(box);
	}
		},
		changeState: function(state) {
	preference.lmMediaHosting = state;
	moduleState = state;
	var btnList = document.querySelectorAll('a[' + lm.anchorAttributeLink + ']');
	for (var i = 0, item; item = btnList[i]; i++) {
	 item = item.parentNode;
	 item.parentNode.removeChild(item);
	}
	var dataAttrList = document.querySelectorAll(['*[' + lm.anchorAttribute + ']', '*[' + lm.anchorIndexAttribute + ']']);
	for (i = 0, item; item = dataAttrList[i]; i++) {
	 item.removeAttribute(lm.anchorAttribute);
	 item.removeAttribute(lm.anchorIndexAttribute);
	}
	lm.savefromLinkCount = -1;

	if (state) {
	 lm.run();
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (document.contentType && document.contentType !== 'text/html') {
		return false;
	}

	if (mono.isIframe()) {
		return false;
	}

	if (mono.isSafari || mono.isFF || mono.isGM) {
		var excludeList = [
	'ftp://*',
	'file://*',
	'*://*.google.*/*',
	'*://*.acidtests.org/*',

	'*://*.savefrom.net/*',
	'*://*.youtube.com/*',
	'*://*.vimeo.com/*',
	'*://*.dailymotion.*/*',
	'*://*.vk.com/*',
	'*://*.vkontakte.ru/*',
	'*://*.odnoklassniki.ru/*',
	'*://*.ok.ru/*',
	'*://my.mail.ru/*',
	'*://*.soundcloud.com/*',
	'*://*.facebook.com/*',
	'*://*.instagram.com/*',
	'*://*.rutube.ru/*'
		];

		var reStr = excludeList.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (/yandex\.com\/launcher/.test(location.href)) {
		return false;
	}

	return true;
 });

 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('odnoklassniki', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleOdnoklassniki ? 1 : 0;
	var allowDownloadMode = mono.isChrome || mono.isFirefox || (mono.isGM && mono.isTM);
	var Promise = mono.Promise;

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return odnoklassniki.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	updateLinks();
		}
		if (message.action === 'downloadMP3Files') {
	if (allowDownloadMode) {
	 audio.downloadMP3Files();
	} else {
	 audio.showListOfAudioFiles(false);
	}
		}
		if (message.action === 'downloadPlaylist') {
	audio.showListOfAudioFiles(true);
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	odnoklassniki.run();
		});
	}

	var odnoklassniki = {
		linkCache: {},
		contextMenu: null,
		videoToken: null,
		run: function() {
	moduleState = 1;

	audio.getJsSessionId();
	videoFeed.injectStyle();
	photo.injectStyle();

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 return odnoklassniki.mutationMode.enable();
	}
		},
		changeState: function(state) {
	moduleState = state;
	video.rmBtn();
	audio.disable();
	photo.rmCurrentPhotoBtn();
	videoFeed.disable();
	videoFeed.rmBtn();
	odnoklassniki.hideMenu();
	odnoklassniki.mutationMode.stop();
	odnoklassniki.clearCache();
	if (state) {
	 odnoklassniki.run();
	}
		},
		hideMenu: function() {
	if (odnoklassniki.contextMenu) {
	 odnoklassniki.contextMenu.hide();
	 odnoklassniki.contextMenu = null;
	}
		},
		clearCache: function() {
	var linkCache = odnoklassniki.linkCache;
	for (var key in linkCache) {
	 delete linkCache[key];
	}
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	wrapOnPhotoOver: function() {
	 if (!moduleState) {
		return;
	 }

	 photo.addCurrentDlBtn(this);
	},
	wrapVideoFeedOnImgOver: function() {
	 if (!moduleState) {
		return;
	 }

	 videoFeed.onImgOver.call(this);
	},
	wrapAudioOnMouseOver: function() {
	 if (!moduleState) {
		return;
	 }

	 audio.onMouseOver.apply(this, arguments);
	},
	wrapAudioOnMouseOut: function() {
	 if (!moduleState) {
		return;
	 }

	 audio.onMouseOut.apply(this, arguments);
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 var _this = this;

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			for (i = 0; i < 2; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.sfSkip > 0) {
			continue;
		 }
		 node.sfSkip = '1';

		 mono.on(node, 'mouseenter', _this.wrapAudioOnMouseOver);
		 mono.on(node, 'mouseleave', _this.wrapAudioOnMouseOut);
		}
			}

			if (!mono.isSafari && !mono.isOpera) {
		summary = summaryList[2];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 mono.one(node, 'mouseenter', _this.wrapOnPhotoOver);
		}
			}

			summary = summaryList[3];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var parent = mono.getParent(node, '.vp_video');
		if (!parent) {
		 return;
		}

		var info = video.getPlayerOptions(node);
		if (info) {
		 video.appendLinkUnderVideo(parent.parentNode, info);
		}
			}

			summary = summaryList[4];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		node = mono.getParentByClass(node, 'vid-card_cnt');
		if (node.dataset.sfSkip) {
		 return;
		}

		mono.one(node, 'mouseenter', _this.wrapVideoFeedOnImgOver);
			}

			for (i = 5; i < 8; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 mono.one(node, 'mouseenter', _this.wrapVideoFeedOnImgOver);
		}
			}

			summary = summaryList[8];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}

			summary = summaryList[9];
			for (n = 0; node = summary.added[n]; n++) {
		node.addEventListener('click', videoFeed.onBtnClick);
			}
		},
		queries: [{
		css: '.track.js-track',
		is: 'added'
			},
			{
		css: '.mus-tr_i',
		is: 'added'
			},
			{
		css: '#photo-layer_photo',
		is: 'added'
			},
			{
		css: '.vp_video .vid-card_cnt',
		is: 'added'
			},
			{
		css: '.vid-card_cnt .vid-card_cnt_w img',
		is: 'added'
			},
			{
		css: '.d_comment_text_w img',
		is: 'added'
			},
			{
		css: '.video-card .video-card_img-w img',
		is: 'added'
			},
			{
		css: '.vid-card_cnt img',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			},
			{
		css: '.sf-video-feed-bind-on-insert',
		is: 'added'
			}
		]
	 });
	}
		}
	};

	var downloadLinkClassName = 'savefrom_ok_download';

	var updateLinks = function() {
		odnoklassniki.clearCache();
		removeDownloadLinks();
		audio.getJsSessionId();
		video.catchPopup();
	};

	var createTextLink = function(href, text, blank) {
		if (blank == undefined)
	blank = true;

		var a = document.createElement('a');
		a.href = href;
		a.className = downloadLinkClassName;
		a.textContent = text;

		if (blank)
	a.setAttribute('target', '_blank');

		return a;
	};

	var removeDownloadLinks = function() {
		var selector = '.' + downloadLinkClassName;

		var e = document.querySelectorAll(selector);
		for (var i = e.length - 1; i >= 0; i--)
	e[i].parentNode.removeChild(e[i]);
	};

	///////////////////////////////////////////////////////////////////
	// AUDIO

	var audio = {
		downloadIdPrefix: 'savefrom_ok_audio_download_',
		infoIdPrefix: 'savefrom_ok_audio_info_',
		lastRow: null,
		lastRowCandidate: null,
		timer: 0,
		jsessionId: '',
		clientHashV: '',
		scriptNode: null,
		cache: {},
		ajaxTimer: {},


		showRowElements: function(row, show, force) {
	if (!row)
	 return;

	var node = row.querySelectorAll('div.' + downloadLinkClassName);

	if (show && !node.length) {
	 audio.showRowLinks(row);
	}

	node = row.querySelectorAll('div.' + downloadLinkClassName);
	for (var i = 0; i < node.length; i++) {
	 if (show) {
		node[i].style.display = '';
	 } else {
		node[i].style.display = 'none';
	 }
	}
		},


		getNodeTrackId: function(node) {
	var query = node.getAttribute('data-query');
	if (query) {
	 try {
		query = JSON.parse(query);
		if (query && query.trackId) {
			return query.trackId;
		}
	 } catch (e) {
		return null;
	 }
	}

	var span = node.querySelector('span.track_play[onclick]');
	if (span) {
	 var m = /(?:playMediatopic|playFeedTrack)\(['"]?(\d+)['"]?/.exec(span.getAttribute('onclick'));
	 return m && m[1];
	}

	return null;
		},


		getTrackId: function(parent) {
	var trackId = audio.getNodeTrackId(parent);
	if (trackId) {
	 var links = {};
	 links[trackId] = parent;
	 return links;
	}

	var id = parent.id;
	if (id) {
	 var hashPos = id.indexOf('#');
	 if (hashPos !== -1) {
		id = id.substr(hashPos + 1);
	 }
	 trackId = SaveFrom_Utils.getMatchFirst(id, /^\w+_(\d+)$/i);
	 if (!trackId) {
		if (id.indexOf('GROUP_FEED') !== -1) {
			trackId = id.substr(id.lastIndexOf('_') + 1);
		}
	 }
	 if (trackId) {
		var links = {};
		links[trackId] = parent;
		return links;
	 }
	}

	return null;
		},


		showRowLinks: function(row) {
	var links = audio.getTrackId(row);
	for (var i in links) {
	 if (audio.handleRow(i, links[i]))
		return true;
	}

	return false;
		},

		disable: function() {
	audio.lastRowCandidate = null;
	audio.lastRow = null;
	var dlBtn = document.querySelectorAll('.' + downloadLinkClassName);
	for (var i = 0, item; item = dlBtn[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		},

		getJsSessionId: function() {
	"use strict";
	var getSidViaApi = function() {
	 return new Promise(function(resolve, reject) {
		SaveFrom_Utils.bridge({
			func: function(cb) {
		require(['OK/music/sid'], function(sid) {
		 sid.get().then(function(sid) {
			cb([null, sid]);
		 }, function(err) {
			cb([err.message]);
		 })
		});
			},
			cb: function(args) {
		if (!args) {
		 reject(new Error('Get sid timeout'))
		} else {
		 var err = args[0];
		 var sid = args[1];
		 err ? reject(new Error('Get sid error')) : resolve(sid);
		}
			}
		});
	 });
	};

	var getSigViaReq = function() {
	 return new Promise(function(resolve, reject) {
		var url = location.protocol + '//' + location.host + '/web-api/music/conf';
		mono.request({
			type: 'POST',
			url: url,
			data: '_',
			json: true,
			localXHR: true
		}, function(err, resp, data) {
			if (err || !data || !data.sid) {
		reject(new Error('Get jsSessionId error!'));
			} else {
		resolve(data.sid);
			}
		});
	 });
	};

	return getSidViaApi().catch(function(err) {
	 return getSigViaReq();
	}).then(function(sid) {
	 audio.jsessionId = sid;
	}, function(err) {
	 mono.debug('getJsSessionId error', err);
	});
		},


		getLink: function(trackId) {
	if (!trackId || !audio.jsessionId)
	 return;

	audio.ajaxTimer[trackId] = window.setTimeout(function() {
	 delete audio.ajaxTimer[trackId];
	 audio.deleteLink(trackId);
	}, 30000);

	mono.sendMessage({
	 action: 'getOdnoklassnikiAudioLinks',
	 url: location.href,
	 trackId: trackId,
	 jsessionId: audio.jsessionId
	}, function(response) {
	 audio.setLink(response.trackId, response.data);
	});
		},


		onMouseOver: function() {
	if (!audio.jsessionId) {
	 return;
	}

	var node = this;
	if (node) {
	 audio.lastRowCandidate = node;
	 clearTimeout(audio.timer);

	 if (audio.lastRow !== node) {
		audio.timer = window.setTimeout(function() {
			audio.showRowElements(audio.lastRow, false);
			audio.lastRow = node;
			audio.lastRowCandidate = null;
			audio.showRowElements(audio.lastRow, true);
		}, 250);
	 }
	}
		},


		onMouseOut: function() {
	var node = this;
	if (
	 (audio.lastRow && audio.lastRow.contains(node)) ||
	 (audio.lastRowCandidate && audio.lastRowCandidate.contains(node))
	) {
	 clearTimeout(audio.timer);
	 audio.timer = window.setTimeout(function() {
		audio.showRowElements(audio.lastRow, false);
		audio.lastRow = null;
		audio.lastRowCandidate = null;
	 }, 1000);
	}
	node = null;
		},


		handleRow: function(trackId, row) {
	if (!trackId || !row)
	 return false;

	var parent = row;
	parent.style.position = 'relative';

	var duration = row.querySelector('.m_c_duration, .m_portal_duration');

	var box = document.createElement('div');
	box.className = downloadLinkClassName;

	var right = 40;
	var mmpcw = document.getElementById('mmpcw');
	if (mmpcw && mmpcw.contains(row)) {
	 right = 65;
	}

	SaveFrom_Utils.setStyle(box, {
	 color: '#fff',
	 background: '#46aa19',
	 border: '1px solid #337d12',
	 borderRadius: '3px',
	 padding: '1px 5px',
	 position: 'absolute',
	 right: right + 'px',
	 top: '50%',
	 lineHeight: '15px',
	 fontSize: '12px',
	 opacity: 0,
	 zIndex: 9999,
	 cursor: 'pointer'
	});

	box.addEventListener('click', audio.onBoxClick, false);

	box.addEventListener('mousedown', function(e) {
	 e.stopPropagation();
	}, false);

	var title = audio.getTitle(trackId, row);

	var link1 = createTextLink('#', '...');
	link1.id = audio.downloadIdPrefix + trackId;
	link1.title = language.downloadTitle;
	if (duration) {
	 link1.setAttribute('data-savefrom-helper-duration',
		audio.secondsFromDurationNode(duration));
	}

	if (title) {
	 title += '.mp3';
	 link1.setAttribute('download', mono.fileName.modify(title));
	}

	SaveFrom_Utils.setStyle(link1, {
	 color: '#fff',
	 fontWeight: 'normal'
	});

	link1.addEventListener('click', audio.onDownloadLinkClick, false);

	box.appendChild(link1);
	parent.appendChild(box);

	if (audio.cache[trackId])
	 audio.setLinkFromCache(trackId, link1);
	else
	 audio.getLink(trackId);

	box.style.marginTop = '-' + (box.offsetHeight / 2) + 'px';
	box.style.opacity = '1';

	var close = document.createElement('span');
	close.textContent = String.fromCharCode(215);
	close.title = language.close;
	SaveFrom_Utils.setStyle(close, {
	 color: '#fff',
	 fontFamily: 'Tahoma,Helvetica,sans-serif',
	 fontSize: '15px',
	 marginLeft: '7px',
	 opacity: '.7',
	 cursor: 'pointer'
	});
	close.addEventListener('click', audio.onCloseBtnClick, false);
	box.appendChild(close);

	return true;
		},


		onBoxClick: function(event) {
	event.preventDefault();
	event.stopPropagation();

	var a = this.querySelector('a.' + downloadLinkClassName);
	if (a) {
	 mono.trigger(a, 'click', {
		cancelable: true
	 });
	 return false;
	}

	this.style.display = 'none';
	return false;
		},


		onDownloadLinkClick: function(event) {
	if (event.button == 2)
	 return false;

	event.stopPropagation();

	if (this.href == '#') {
	 event.preventDefault();
	 return false;
	}

	SaveFrom_Utils.downloadOnClick(event);

	return false;
		},


		onCloseBtnClick: function(event) {
	if (event.button == 2)
	 return true;

	event.preventDefault();
	event.stopPropagation();

	var parent = mono.getParent(this, '.' + downloadLinkClassName);
	if (parent)
	 parent.style.display = 'none';

	return false;
		},


		deleteLink: function(trackId, node) {
	if (!node && trackId)
	 node = document.getElementById(audio.downloadIdPrefix + trackId);

	if (!node)
	 return;

	var box = node.parentNode;
	if (!box) {
	 return;
	}
	box.parentNode.removeChild(box);
		},


		getHash: function(src, magic) {
	if (!magic)
	 magic = [4, 3, 5, 6, 1, 2, 8, 7, 2, 9, 3, 5, 7, 1, 4, 8, 8, 3, 4, 3, 1, 7, 3, 5, 9, 8, 1, 4, 3, 7, 2, 8];

	var a = [];
	for (var i = 0; i < src.length; i++) {
	 a.push(parseInt('0x0' + src.charAt(i)));
	}

	src = a;

	var res = [];
	src = src.slice(0);
	src[32] = src[31];
	var sum = 0;
	var i = 32;
	while (i-- > 0)
	 sum += src[i];

	for (var x = 0; x < 32; x++)
	 res[x] = Math.abs(sum - src[x + 1] * src[x] * magic[x]);

	return res.join('');
		},


		setLinkFromCache: function(trackId, node) {
	if (!audio.cache[trackId])
	 return false;

	if (!node)
	 node = document.getElementById(audio.downloadIdPrefix + trackId);

	if (!node)
	 return;

	node.href = audio.cache[trackId].url;
	node.textContent = '';
	if (audio.cache[trackId].downloadAttr)
	 node.setAttribute('download', audio.cache[trackId].downloadAttr);

	var icon = mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
	 style: {
		display: 'inline-block',
		width: '16px',
		height: '16px',
		verticalAlign: 'middle',
		opacity: '0.9'
	 }
	});
	node.appendChild(icon);

	var info = document.createTextNode(audio.cache[trackId].info);

	if (node.nextSibling)
	 node.parentNode.insertBefore(info, node.nextSibling);
	else
	 node.parentNode.appendChild(info);

	return true;
		},

		getClientHash: function(url) {
	var md5fn = audio.md5;
	var getViaApi = function() {
	 if (audio.getClientHash.getViaApiError) {
		return Promise.reject(audio.getClientHash.getViaApiError);
	 }

	 return new Promise(function(resolve, reject) {
		SaveFrom_Utils.bridge({
			args: [url],
			func: function(url, cb) {
		require(['OK/music/garbage'], function(m) {
		 try {
			cb([null, /clientHash=([^&]+)/.exec(m.calc(url))[1]]);
		 } catch (err) {
			cb([err.message]);
		 }
		});
			},
			cb: function(args) {
		if (!args) {
		 reject(new Error('getClientHash timeout'));
		} else {
		 var err = args[0];
		 var result = args[1];
		 err ? reject(new Error('getClientHash error')) : resolve(result);
		}
			}
		});
	 }).catch(function(err) {
		audio.getClientHash.getViaApiError = err;
		throw err;
	 });
	};

	var getViaConfig = function() {
	 var getClientHash = function(n, k, e) {
		var r = /md5=(\w*)/g.exec(n)[1],
			q = k(r + "secret"),
			u = q.length,
			t = 0,
			p, v, s, o = "";
		for (p = 0; p < u; p += 1) {
			t += parseInt(q[p], 16);
		}
		for (p = 0; p < u; p += 1) {
			v = parseInt(q[p], 16);
			if (p === u - 1) {
		s = v;
			} else {
		s = parseInt(q[p + 1], 16);
			}
			o += Math.abs(t - (v * s * e[p]));
		}
		return o;
	 };
	 var getV = function() {
		if (audio.clientHashV) {
			return Promise.resolve(audio.clientHashV);
		}

		return new Promise(function(resolve, reject) {
			SaveFrom_Utils.bridge({
		func: function(cb) {
		 try {
			cb([null, require.s.contexts._.config.config['OK/music/garbage'].v]);
		 } catch (err) {
			cb([err.message]);
		 }
		},
		cb: function(args) {
		 if (!args) {
			reject(new Error('getClientHash timeout'))
		 } else {
			var err = args[0];
			var result = args[1];
			err ? reject(new Error('getClientHash error')) : resolve(result);
		 }
		}
			});
		}).then(function(v) {
			return audio.clientHashV = v;
		});
	 };
	 return getV().then(function(v) {
		return getClientHash(url, md5fn, v);
	 });
	};

	return getViaApi().catch(function(err) {
	 return getViaConfig();
	});
		},

		setLink: function(trackId, data, clientHash) {
	if (!trackId)
	 return;

	clearTimeout(audio.ajaxTimer[trackId]);

	var node = document.getElementById(audio.downloadIdPrefix + trackId);
	if (!node)
	 return;

	if (audio.setLinkFromCache(trackId, node))
	 return;

	if (!data || !data.play) {
	 audio.deleteLink(trackId, node);
	 node.textContent = '?';
	 return;
	}

	if (clientHash === undefined) {
	 return this.getClientHash(data.play).then(function(clientHash) {
		audio.setLink(trackId, data, clientHash);
	 }, function(err) {
		audio.deleteLink(trackId, node);
	 });
	}

	var size = data.track && data.track.size || -1;

	audio.cache[trackId] = {};
	audio.cache[trackId].url = data.play + (clientHash ? '&clientHash=' + clientHash : '');

	var info = ' (' + SaveFrom_Utils.sizeHuman(size, 2);

	var duration = node.getAttribute('data-savefrom-helper-duration');
	if (data.track) {
	 if (data.track.duration)
		duration = data.track.duration;

	 if (data.track.ensemble && data.track.name) {
		var title = data.track.ensemble + ' - ' + data.track.name;
		audio.cache[trackId].title = title;
		audio.cache[trackId].downloadAttr = mono.fileName.modify(title + '.mp3');
	 }
	}

	if (size && size > 0 && duration) {
	 duration = parseInt(duration);
	 if (isNaN(duration)) {
		delete audio.cache[trackId];
		return;
	 }

	 var bitrate = Math.floor((size / duration) / 125) + ' ' + language.kbps;
	 info += ' ~ ' + bitrate;
	}

	info += ')';
	audio.cache[trackId].info = info;

	audio.setLinkFromCache(trackId, node);
		},


		getTitle: function(id, row) {
	if (!id || !row)
	 return '';

	var name = '';

	var performer = row.querySelector('.m_c_artist, .mus-tr_artist, .m_portal_c_artist');
	var title = row.querySelector('.m_track_source, .mus-tr_song, .m_portla_track_name');

	if (performer) {
	 performer = performer.textContent;
	 if (performer)
		name += performer.trim();
	}

	if (title) {
	 title = title.textContent;
	 if (title) {
		if (name)
			name += ' - ';

		name += title.trim();
	 }
	}

	if (name)
	 return name.replace(/\<a\s+[^\>]+\>/ig, '').replace(/\<\/a\>/ig, '');

	return '';
		},


		secondsFromDurationNode: function(node) {
	if (!node)
	 return 0;

	var text = node.textContent;
	if (!text)
	 return 0;

	var m = text.match(/^(?:\s*(\d+)\s*\:)?\s*(\d+)\s*\:\s*(\d+)/);
	if (m && m.length > 3) {
	 if (!m[1])
		m[1] = 0;

	 return parseInt(m[1]) * 3600 + parseInt(m[2]) * 60 + parseInt(m[3]);
	}

	return 0;
		},

		getPlaylistName: function(container) {
	if (container === document) return;

	var title = container.querySelector('.mus_h2_tx');
	if (!title) return;
	return mono.fileName.modify(title.textContent) || undefined;
		},

		elIsHidden: function isHidden(el) {
	return (el.offsetParent === null)
		},

		getLayer: function() {
	var layer = document.getElementById('mmpcw');
	if (!layer) {
	 return;
	}
	if (layer.classList.contains('__hidden')) {
	 return;
	}
	layer = layer.querySelector('div.m_c_s[aria-hidden="false"]');
	if (!layer || audio.elIsHidden(layer)) {
	 return;
	}
	return layer;
		},

		getPopup: function(title, type, onClose) {
	var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();

	var progressEl;
	mono.create(template.textContainer, {
	 append: [!title ? undefined : mono.create('p', {
			text: title,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '13px'
			}
		}),
		progressEl = mono.create('p', {
			text: '',
			style: {
		color: '#868686',
		fontSize: '14px',
		lineHeight: '24px'
			}
		})
	 ]
	});

	var popupEl = SaveFrom_Utils.popupDiv(template.body, 'sf_progress_popup', undefined, undefined, onClose);

	var setState = function(state) {
	 if (setState.state === state) {
		return;
	 }
	 setState.state = state;

	 template.buttonContainer.style.display = 'none';
	 progressEl.style.display = 'none';
	 mono.sendMessage({
		action: 'getWarningIcon',
		type: type,
		color: '#77D1FA'
	 }, function(icon) {
		template.icon.style.backgroundImage = 'url(' + icon + ')';
	 });
	 if (state === 'progress') {
		progressEl.style.display = 'block';
	 }
	 if (state === 'error') {
		mono.sendMessage({
			action: 'getWarningIcon',
			type: type,
			color: '#AAAAAA'
		}, function(icon) {
			template.icon.style.backgroundImage = 'url(' + icon + ')';
		});
		progressEl.style.display = 'block';
	 }
	};

	return {
	 onPrepare: function(text) {
		setState('progress');
		progressEl.textContent = text;
	 },
	 onProgress: function(count, max) {
		progressEl.textContent = language.vkFoundFiles.replace('%d', count) + ' ' + language.vkFoundOf + ' ' + max;
	 },
	 onReady: function() {
		mono.trigger(popupEl, 'kill');
	 },
	 onError: function(text) {
		setState('error');
		progressEl.textContent = text;
	 }
	}
		},

		getAudioLinksViaAPI: function(trackIdList, onProgress, cb) {
	var abort = false;
	var trackList = [];
	var len = trackIdList.length;
	var next = function() {
	 if (abort) {
		return;
	 }
	 var trackIdArr = trackIdList.splice(0, 10);
	 if (trackIdArr.length === 0) {
		return cb(trackList);
	 }

	 mono.sendMessage({
		action: 'getOkAudioListLinks',
		trackIdArr: trackIdArr,
		jsessionId: audio.jsessionId
	 }, function(responseList) {
		var promise = Promise.resolve();
		if (Array.isArray(responseList)) {
			responseList.forEach(function(item) {
		promise = promise.then(function() {
		 if (typeof item.play !== 'string' || typeof item.track !== 'object') return;

		 var url = item.play;

		 var title;
		 if (item.track.name) {
			title = item.track.name;
		 }
		 if (item.track.ensemble) {
			title = item.track.ensemble + (title ? ' - ' + title : '');
		 }
		 if (!title) {
			title = 'noname';
		 }

		 return audio.getClientHash(url).then(function(clientHash) {
			url += '&clientHash=' + clientHash;

			trackList.push({
				url: url,
				duration: item.track.duration || 0,
				title: title,
				filename: mono.fileName.modify(title) + '.mp3'
			});
		 });
		}).catch(function(err) {
		 mono.debug('process item error', item, err);
		});
			});
		}
		promise.then(function() {
			onProgress(len - trackIdList.length, len);
			next();
		});
	 });
	};
	next();

	return {
	 abort: function() {
		abort = true;
	 }
	}
		},

		getAudioListLinksPopup: function(trackIdList, title, cb) {
	var process;
	var popup = this.getPopup(title, 'audio', function onClose() {
	 if (process) {
		process.abort();
	 }
	});
	var _cb = function(links) {
	 if (links.length === 0) {
		popup.onError(language.vkMp3LinksNotFound);
		return;
	 }
	 popup.onReady();

	 cb(links);
	};

	popup.onPrepare(language.download + ' ...');

	process = this.getAudioLinksViaAPI(trackIdList, popup.onProgress, _cb);
		},

		getAudioLinks: function(container, title, cb) {
	var rowList = container.querySelectorAll(['.m_portal_track', '.m_c_tr', '.mus-tr_i']);
	var trackIdList = [];
	for (var i = 0, row; row = rowList[i]; i++) {
	 var trackIdObj = audio.getTrackId(row);
	 for (var trackId in trackIdObj) {
		trackIdList.push(trackId);
	 }
	}
	this.getAudioListLinksPopup(trackIdList, title, cb);
		},

		downloadMP3Files: function() {
	var container = audio.getLayer() || document;
	var title = audio.getPlaylistName(container);
	audio.getAudioLinks(container, title, function(trackList) {
	 SaveFrom_Utils.downloadList.showBeforeDownloadPopup(trackList, {
		type: 'audio',
		folderName: title
	 });
	});
		},

		showListOfAudioFiles: function(showPlaylist) {
	var container = audio.getLayer() || document;
	var title = audio.getPlaylistName(container);
	audio.getAudioLinks(container, title, function(trackList) {
	 if (trackList.length) {
		if (showPlaylist) {
			SaveFrom_Utils.playlist.popupPlaylist(trackList, title, true);
		} else {
			SaveFrom_Utils.playlist.popupFilelist(trackList);
		}
		return;
	 }

	 alert(language.vkMp3LinksNotFound);
	});
		},

		md5: function(str) {
	// http://kevin.vanzonneveld.net
	// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
	// + namespaced by: Michael White (http://getsprink.com)
	// +	tweaked by: Jack
	// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// +		input by: Brett Zamir (http://brett-zamir.me)
	// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
	// -	depends on: utf8_encode
	// *	 example 1: md5('Kevin van Zonneveld');
	// *	 returns 1: '6e658d4bfcb59cc13f96c14450ac40b9'
	var xl;

	var rotateLeft = function(lValue, iShiftBits) {
	 return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
	};

	var addUnsigned = function(lX, lY) {
	 var lX4, lY4, lX8, lY8, lResult;
	 lX8 = (lX & 0x80000000);
	 lY8 = (lY & 0x80000000);
	 lX4 = (lX & 0x40000000);
	 lY4 = (lY & 0x40000000);
	 lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
	 if (lX4 & lY4) {
		return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
	 }
	 if (lX4 | lY4) {
		if (lResult & 0x40000000) {
			return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
		} else {
			return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
		}
	 } else {
		return (lResult ^ lX8 ^ lY8);
	 }
	};

	var _F = function(x, y, z) {
	 return (x & y) | ((~x) & z);
	};
	var _G = function(x, y, z) {
	 return (x & z) | (y & (~z));
	};
	var _H = function(x, y, z) {
	 return (x ^ y ^ z);
	};
	var _I = function(x, y, z) {
	 return (y ^ (x | (~z)));
	};

	var _FF = function(a, b, c, d, x, s, ac) {
	 a = addUnsigned(a, addUnsigned(addUnsigned(_F(b, c, d), x), ac));
	 return addUnsigned(rotateLeft(a, s), b);
	};

	var _GG = function(a, b, c, d, x, s, ac) {
	 a = addUnsigned(a, addUnsigned(addUnsigned(_G(b, c, d), x), ac));
	 return addUnsigned(rotateLeft(a, s), b);
	};

	var _HH = function(a, b, c, d, x, s, ac) {
	 a = addUnsigned(a, addUnsigned(addUnsigned(_H(b, c, d), x), ac));
	 return addUnsigned(rotateLeft(a, s), b);
	};

	var _II = function(a, b, c, d, x, s, ac) {
	 a = addUnsigned(a, addUnsigned(addUnsigned(_I(b, c, d), x), ac));
	 return addUnsigned(rotateLeft(a, s), b);
	};

	var convertToWordArray = function(str) {
	 var lWordCount;
	 var lMessageLength = str.length;
	 var lNumberOfWords_temp1 = lMessageLength + 8;
	 var lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64;
	 var lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16;
	 var lWordArray = new Array(lNumberOfWords - 1);
	 var lBytePosition = 0;
	 var lByteCount = 0;
	 while (lByteCount < lMessageLength) {
		lWordCount = (lByteCount - (lByteCount % 4)) / 4;
		lBytePosition = (lByteCount % 4) * 8;
		lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount) << lBytePosition));
		lByteCount++;
	 }
	 lWordCount = (lByteCount - (lByteCount % 4)) / 4;
	 lBytePosition = (lByteCount % 4) * 8;
	 lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
	 lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
	 lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
	 return lWordArray;
	};

	var wordToHex = function(lValue) {
	 var wordToHexValue = "",
		wordToHexValue_temp = "",
		lByte, lCount;
	 for (lCount = 0; lCount <= 3; lCount++) {
		lByte = (lValue >>> (lCount * 8)) & 255;
		wordToHexValue_temp = "0" + lByte.toString(16);
		wordToHexValue = wordToHexValue + wordToHexValue_temp.substr(wordToHexValue_temp.length - 2, 2);
	 }
	 return wordToHexValue;
	};

	var x = [],
	 k, AA, BB, CC, DD, a, b, c, d, S11 = 7,
	 S12 = 12,
	 S13 = 17,
	 S14 = 22,
	 S21 = 5,
	 S22 = 9,
	 S23 = 14,
	 S24 = 20,
	 S31 = 4,
	 S32 = 11,
	 S33 = 16,
	 S34 = 23,
	 S41 = 6,
	 S42 = 10,
	 S43 = 15,
	 S44 = 21;

	//str = this.utf8_encode(str);
	x = convertToWordArray(str);
	a = 0x67452301;
	b = 0xEFCDAB89;
	c = 0x98BADCFE;
	d = 0x10325476;

	xl = x.length;
	for (k = 0; k < xl; k += 16) {
	 AA = a;
	 BB = b;
	 CC = c;
	 DD = d;
	 a = _FF(a, b, c, d, x[k + 0], S11, 0xD76AA478);
	 d = _FF(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
	 c = _FF(c, d, a, b, x[k + 2], S13, 0x242070DB);
	 b = _FF(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
	 a = _FF(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
	 d = _FF(d, a, b, c, x[k + 5], S12, 0x4787C62A);
	 c = _FF(c, d, a, b, x[k + 6], S13, 0xA8304613);
	 b = _FF(b, c, d, a, x[k + 7], S14, 0xFD469501);
	 a = _FF(a, b, c, d, x[k + 8], S11, 0x698098D8);
	 d = _FF(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
	 c = _FF(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
	 b = _FF(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
	 a = _FF(a, b, c, d, x[k + 12], S11, 0x6B901122);
	 d = _FF(d, a, b, c, x[k + 13], S12, 0xFD987193);
	 c = _FF(c, d, a, b, x[k + 14], S13, 0xA679438E);
	 b = _FF(b, c, d, a, x[k + 15], S14, 0x49B40821);
	 a = _GG(a, b, c, d, x[k + 1], S21, 0xF61E2562);
	 d = _GG(d, a, b, c, x[k + 6], S22, 0xC040B340);
	 c = _GG(c, d, a, b, x[k + 11], S23, 0x265E5A51);
	 b = _GG(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
	 a = _GG(a, b, c, d, x[k + 5], S21, 0xD62F105D);
	 d = _GG(d, a, b, c, x[k + 10], S22, 0x2441453);
	 c = _GG(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
	 b = _GG(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
	 a = _GG(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
	 d = _GG(d, a, b, c, x[k + 14], S22, 0xC33707D6);
	 c = _GG(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
	 b = _GG(b, c, d, a, x[k + 8], S24, 0x455A14ED);
	 a = _GG(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
	 d = _GG(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
	 c = _GG(c, d, a, b, x[k + 7], S23, 0x676F02D9);
	 b = _GG(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
	 a = _HH(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
	 d = _HH(d, a, b, c, x[k + 8], S32, 0x8771F681);
	 c = _HH(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
	 b = _HH(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
	 a = _HH(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
	 d = _HH(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
	 c = _HH(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
	 b = _HH(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
	 a = _HH(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
	 d = _HH(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
	 c = _HH(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
	 b = _HH(b, c, d, a, x[k + 6], S34, 0x4881D05);
	 a = _HH(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
	 d = _HH(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
	 c = _HH(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
	 b = _HH(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
	 a = _II(a, b, c, d, x[k + 0], S41, 0xF4292244);
	 d = _II(d, a, b, c, x[k + 7], S42, 0x432AFF97);
	 c = _II(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
	 b = _II(b, c, d, a, x[k + 5], S44, 0xFC93A039);
	 a = _II(a, b, c, d, x[k + 12], S41, 0x655B59C3);
	 d = _II(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
	 c = _II(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
	 b = _II(b, c, d, a, x[k + 1], S44, 0x85845DD1);
	 a = _II(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
	 d = _II(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
	 c = _II(c, d, a, b, x[k + 6], S43, 0xA3014314);
	 b = _II(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
	 a = _II(a, b, c, d, x[k + 4], S41, 0xF7537E82);
	 d = _II(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
	 c = _II(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
	 b = _II(b, c, d, a, x[k + 9], S44, 0xEB86D391);
	 a = addUnsigned(a, AA);
	 b = addUnsigned(b, BB);
	 c = addUnsigned(c, CC);
	 d = addUnsigned(d, DD);
	}

	var temp = wordToHex(a) + wordToHex(b) + wordToHex(c) + wordToHex(d);

	return temp.toLowerCase();
		}
	};

	// /AUDIO
	///////////////////////////////////////////////////////////////////



	///////////////////////////////////////////////////////////////////
	// VIDEO

	var video = {
		requestMobileToken: function(response, cb) {
	var host = null;
	response.links.some(function(item) {
	 var hostname = item.url.match(/\/\/([^/]+)/);
	 hostname = hostname && hostname[1];
	 if (hostname) {
		host = hostname;
		return true;
	 }
	});
	SaveFrom_Utils.bridge({
	 timeout: 3000,
	 args: [host],
	 func: function(host, cb) {
		var cookie = document.cookie;
		var vdsig = cookie && cookie.match(/vdsig=([^;]+);/);
		vdsig = vdsig && vdsig[1];
		if (vdsig) {
			return cb({
		vtkn: vdsig
			});
		}

		host = host || 'vd4.mycdn.me';
		var xhr = new XMLHttpRequest();
		xhr.open('POST', location.protocol + '//' + host + '/usr_login', true);
		xhr.withCredentials = true;
		xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		xhr.onreadystatechange = function() {
			if (xhr.readyState === 4 && xhr.status === 200) {
		var data = null;
		try {
		 data = JSON.parse(xhr.responseText);
		} catch (e) {}

		return cb(data);
			}
		};
		xhr.send();
	 },
	 cb: function(data) {
		if (!data || !data.vtkn) {
			return cb();
		}

		if (data.ttl) {
			data.expire = Date.now() + data.ttl * 1000;
			odnoklassniki.videoToken = data;
		}

		response.vtkn = data.vtkn;

		cb(response);
	 }
	});
		},
		getMobileToken: function(response, cb) {
	if (response.vtkn) {
	 return cb(response);
	}

	var videoToken = odnoklassniki.videoToken;
	if (videoToken && videoToken.expire > Date.now()) {
	 response.vtkn = videoToken.vtkn;
	 return cb(response);
	}

	return video.requestMobileToken(response, cb);
		},
		wrapMobileLinks: function(response, cb) {
	video.getMobileToken(response, function(response) {
	 if (!response || !response.vtkn) {
		return cb();
	 }

	 response.action = 'getOkViaMobileWrapped';
	 response.links.forEach(function(link) {
		var sep = !/\?/.test(link.url) ? '?' : '&';
		link.url += sep + 'vdsig=' + response.vtkn;
	 });
	 cb(response);
	});
		},
		prepareResponse: function(response, cb) {
	var abort = function() {
	 cb(language.noLinksFound);
	};
	if (!response || !response.links) {
	 return abort();
	}

	if (!preference.showUmmyItem && response.action === 'getRutubeLinks') {
	 return abort();
	}

	if (response.action === 'getOkViaMobile') {
	 return video.wrapMobileLinks(response, function(response) {
		if (!response) {
			return abort();
		}

		video.prepareResponse(response, cb);
	 });
	}

	var prepareLinkType = null;
	if (response.action === 'getYoutubeLinks') {
	 prepareLinkType = 'youtube';
	} else
	if (response.action === 'getVimeoLinks') {
	 prepareLinkType = 'vimeo';
	} else
	if (response.action === 'getDailymotionLinks') {
	 prepareLinkType = 'dailymotion';
	} else
	if (response.action === 'getRutubeLinks') {
	 prepareLinkType = 'rutube';
	}

	var menuLinks = null;

	if (prepareLinkType) {
	 menuLinks = SaveFrom_Utils.popupMenu.prepareLinks[prepareLinkType](response.links, response.title);
	} else {
	 menuLinks = videoFeed.prepareLinks(response.links, response.title);
	}

	return cb(menuLinks);
		},

		matchOpenGraph: function(metadata) {
	if (!metadata || !metadata.movie || !metadata.movie.contentId) {
	 return;
	}

	var url = metadata.movie.contentId;

	if (url.indexOf('rutube.') !== -1 && preference.showUmmyItem) {
	 return {
		action: 'getRutubeLinks',
		links: [url]
	 }
	}

	if (url.indexOf('pladform') !== -1) {
	 var urlArgs = mono.parseUrl(url);
	 return {
		action: 'getPladformVideo',
		extVideoId: {
			playerId: urlArgs.pl,
			videoId: urlArgs.videoid
		}
	 };
	}

	var request = SaveFrom_Utils.embedDownloader.checkUrl(url);
	if (request) {
	 return request;
	}

	var poster = metadata.movie.poster;
	if (typeof poster === 'string') {
	 var redirectUrl = mono.parseUrl(poster).url || poster;
	 var ytId = redirectUrl.match(/ytimg\.com\/vi\/([^\/]+)\//);
	 ytId = ytId && ytId[1];
	 if (ytId) {
		return {
			action: 'getYoutubeLinks',
			extVideoId: ytId
		}
	 }
	}
		},

		switchMetadataProvider: function(metadata) {
	"use strict";
	if (!metadata || !metadata.provider || !metadata.movie) {
	 return;
	}

	switch (metadata.provider) {
	 case 'USER_YOUTUBE':
		if (metadata.movie.contentId) {
			return {
		request: {
		 action: 'getYoutubeLinks',
		 extVideoId: metadata.movie.contentId
		}
			};
		}
		break;
	 case 'OPEN_GRAPH':
		var request = this.matchOpenGraph(metadata);
		if (request) {
			return {
		request: request
			}
		}
		break;
	 case 'LIVE_TV_APP':
	 case 'LIVE_TV_ODKL':
	 case 'UPLOADED_ODKL':
	 case 'UPLOADED':
	 case 'PARTNER':
	 case 'YKL':
		if (metadata.videos && metadata.movie.title) {
			return {
		request: {
		 action: 'wrapMobileLinks',
		 title: metadata.movie.title,
		 links: metadata.videos
		}
			};
		}

		/* via mobile
		 return {
		 request: {
		 action: 'okDirectOrMobile',
		 mvId: metadata.movie.id,
		 metadata: metadata,
		 title: metadata.movie.title,
		 links: null
		 }
		 };
		 */

		/* odirect only
		 if (metadata.videos && metadata.movie.title) {
		 return {
		 links: videoFeed.prepareLinks(metadata.videos, metadata.movie.title)
		 };
		 }*/
		break;
	}
		},

		getPlayerMetadata: function(movieId, sid, cb, withSid) {
	var params = {
	 cmd: 'videoPlayerMetadata',
	 mid: movieId,
	 rnd: Date.now()
	};

	if (withSid) {
	 params.mtId = sid;
	}

	mono.request({
	 method: 'POST',
	 url: location.protocol + '//' + location.host + '/dk?' + mono.param(params),
	 json: true,
	 localXHR: true
	}, function(err, resp, metadata) {
	 if (err) {
		if (!withSid && sid) {
			video.getPlayerMetadata(movieId, sid, cb, 1);
			return;
		}

		return cb();
	 }

	 return cb(metadata);
	});
		},

		getEmbed: function(contentId, cb) {
	var params = mono.parseUrl(contentId);
	if (!params.id || !params.sig) {
	 return cb();
	}

	var url = 'http://cdn-ok.com/video/get/?' + mono.param({
	 id: params.id,
	 format: 1,
	 sig: params.sig,
	 sig2: 'oldRotator'
	});

	mono.sendMessage({
	 action: 'getData',
	 url: url
	}, function(data) {
	 if (!data) {
		return cb();
	 }

	 var jsonList = mono.findJson(data, [/"sourceType":/, /"sourceId":/]);

	 var isFound = jsonList.some(function(json) {
		if (json.sourceType === 'youtube' && json.sourceId) {
			cb({
		request: {
		 action: 'getYoutubeLinks',
		 extVideoId: json.sourceId
		}
			});
			return true;
		}
	 });

	 if (!isFound) {
		return cb();
	 }
	});
		},

		readMetadata: function(metadata, cb, skipPlayerMeta) {
	if (metadata.movie) {
	 if (/cdn-ok\.com\/embed/.test(metadata.movie.contentId)) {
		return this.getEmbed(metadata.movie.contentId, function(info) {
			if (!info || !info.request) {
		return cb();
			}

			mono.sendMessage(info.request, function(response) {
		video.prepareResponse(response, cb);
			});
		});
	 }
	}

	if (!skipPlayerMeta && metadata.movie && metadata.movie.movieId) {
	 var sid = metadata.movie.link && mono.parseUrl(metadata.movie.link, {
		sep: '&amp;'
	 })['st.vpl.sid'];
	 return this.getPlayerMetadata(metadata.movie.movieId, sid, function(_metadata) {
		video.readMetadata(_metadata || metadata, cb, 1);
	 });
	}

	var info = this.switchMetadataProvider(metadata);
	if (!info) {
	 return cb();
	}

	if (info.links) {
	 return cb(info.links);
	}

	if (info.request) {
	 if (info.request.action === 'getRutubeLinks') {
		video.prepareResponse(info.request, cb);
	 } else
	 if (info.request.action === 'wrapMobileLinks') {
		video.wrapMobileLinks(info.request, function(response) {
			video.prepareResponse(response, function(menuLinks) {
		cb(menuLinks, 1);
			});
		});
	 } else {
		mono.sendMessage(info.request, function(response) {
			video.prepareResponse(response, cb);
		});
	 }
	} else {
	 cb();
	}
		},

		loadLinks: function(info, menu) {
	var linkCache = odnoklassniki.linkCache;
	var cacheKey = JSON.stringify(info);
	var cache = linkCache[cacheKey];
	if (cache) {
	 return menu.update(cache);
	}

	var onGetMeta = function(metadata, skipPlayerMeta) {
	 var onFail = function() {
		menu.update(language.noLinksFound);
	 };

	 if (!metadata) {
		return onFail();
	 }

	 video.readMetadata(metadata, function(menuLinks, noCache) {
		if (!menuLinks) {
			return onFail();
		}

		if (Array.isArray(menuLinks) && !menuLinks.length) {
			noCache = 1;
		}

		if (!noCache) {
			linkCache[cacheKey] = menuLinks;
		}

		menu.update(menuLinks);
	 }, skipPlayerMeta);
	};

	if (info.metadata) {
	 onGetMeta(info.metadata);
	} else
	if (info.request) {
	 if (info.request.action === 'getOkMetadata') {
		mono.sendMessage(info.request, onGetMeta);
	 } else
	 if (info.request.action === 'getPlayerMetadata') {
		this.getPlayerMetadata(info.request.extVideoId, info.request.sid, function(metadata) {
			onGetMeta(metadata, 1);
		});
	 } else
	 if (info.request.action === 'getRutubeLinks') {
		video.prepareResponse(info.request, function(menuLinks) {
			menu.update(menuLinks);
		});
	 } else {
		mono.sendMessage(info.request, function(response) {
			video.prepareResponse(response, function(menuLinks) {
		menu.update(menuLinks);
			});
		});
	 }
	} else {
	 menu.update(language.noLinksFound);
	}
		},

		appendLinkUnderVideo: function(container, info) {
	"use strict";
	var insertContainer = container.querySelector('.vp-layer-info_cnt');
	if (!insertContainer) {
	 return;
	}

	var exButton = insertContainer.querySelector('.' + downloadLinkClassName);

	var button = mono.create('span', {
	 className: downloadLinkClassName,
	 style: {
		marginLeft: '12px'
	 },
	 on: [
		['click', function(e) {
			e.stopPropagation();
		}],
		['mousedown', function(e) {
			e.stopPropagation();
		}],
		['keydown', function(e) {
			e.stopPropagation();
		}]
	 ],
	 append: [
		mono.create('a', {
			href: '#',
			text: language.download,
			on: ['click', function(e) {
		e.preventDefault();

		mono.onRemoveEvent(button, odnoklassniki.hideMenu);

		if (odnoklassniki.contextMenu && odnoklassniki.contextMenu.isShow) {
		 if (odnoklassniki.contextMenu.button !== this) {
			odnoklassniki.hideMenu();
		 } else {
			odnoklassniki.hideMenu();
			return;
		 }
		}

		var menu = odnoklassniki.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + '...', 'sf-single-video-menu', {
		 parent: container
		});

		video.loadLinks(info, menu);
			}]
		})
	 ]
	});

	if (exButton && exButton.parentNode) {
	 exButton.parentNode.replaceChild(button, exButton);
	 exButton = null;
	} else {
	 insertContainer.appendChild(button);
	}
		},

		getPlayerOptions: function(node) {
	var optionsNode = mono.getParent(node, '[data-player-element-id][data-options]');

	var options = optionsNode && optionsNode.dataset.options;

	if (!options) {
	 return;
	}

	try {
	 options = JSON.parse(options);
	} catch (e) {}

	var flashvars = options.flashvars;

	if (!flashvars) {
	 return;
	}

	if (flashvars.metadata) {
	 var metadata = null;

	 try {
		metadata = JSON.parse(flashvars.metadata);
	 } catch (e) {}

	 if (metadata) {
		return {
			metadata: metadata
		}
	 }
	}

	if (flashvars.metadataUrl) {
	 return {
		request: {
			action: 'getOkMetadata',
			url: decodeURIComponent(flashvars.metadataUrl)
		}
	 }
	}

	var url = options.url;
	if (url) {
	 var request = SaveFrom_Utils.embedDownloader.checkUrl(url);
	 if (request) {
		return {
			request: request
		}
	 }

	 if (url.indexOf('rutube.') !== -1) {
		return {
			request: {
		action: 'getRutubeLinks',
		links: [url]
			}
		}
	 }
	}
		},

		catchPopup: function() {
	"use strict";
	var videoContainer = null;
	this.lastWaitEl && this.lastWaitEl.abort();

	this.lastWaitEl = this.waitEl(function() {
	 videoContainer = document.querySelector('.vp_video .vid-card_cnt');
	 if (!videoContainer) {
		return;
	 }

	 return videoContainer;
	}, function() {
	 var parent = mono.getParent(videoContainer, '.vp_video');
	 if (!parent) {
		return;
	 }

	 var info = video.getPlayerOptions(videoContainer);
	 if (info) {
		video.appendLinkUnderVideo(parent.parentNode, info);
	 }
	});
		},

		rmBtn: function() {
	var dlBtn = document.querySelectorAll('.' + downloadLinkClassName);
	for (var i = 0, item; item = dlBtn[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		},

		lastWaitEl: null,
		waitEl: function(func, cb, options) {
	var out;
	var capsule = mono.extend({
	 abort: function() {
		clearInterval(capsule.timeout);
		capsule.isAborted = true;
	 }
	}, {
	 delay: 500,
	 repeat: 12,
	 isAborted: false,
	 timeout: null
	}, options);

	if (out = func()) {
	 cb(out);
	 return capsule;
	}

	(function wait() {
	 capsule.repeat--;
	 capsule.timeout = setTimeout(function() {
		if (capsule.isAborted) {
			return;
		}

		if (out = func()) {
			return cb(out);
		}

		if (!capsule.isAborted && capsule.repeat) {
			wait();
		}
	 }, capsule.delay);
	})();

	return capsule;
		}
	};

	var videoFeed = {
		btnClassName: 'sf-feed-dl-btn',
		style: undefined,
		thumbClassName: 'vid-card_img',
		prepareLinks: function(links, title) {
	if (!links || !links.length) {
	 return language.noLinksFound;
	}

	if (typeof links === 'string') {
	 return links;
	}

	title = title || '';

	var menuLinks = [];
	var popupLink;
	var quality;
	var format;
	var url;
	for (var i = 0, len = links.length; i < len; i++) {
	 var link = links[i];
	 if (typeof(link) === 'object' && link.url) {
		url = link.url;
		var ext = link.ext;

		if (!ext) {
			ext = 'MP4';
			if (link.url.indexOf('.mp4') !== -1) {
		ext = 'MP4';
			}
			if (url.indexOf('.flv') !== -1) {
		ext = 'FLV';
			}
			if (link.url.indexOf('.mov') !== -1) {
		ext = 'MOV';
			}
			if (link.url.indexOf('.mpg') !== -1) {
		ext = 'MPG';
			}
		}

		ext = ext.toLowerCase();
		format = ext.toUpperCase();
		quality = link.subname || link.quality || link.name || ext;
	 } else {
		url = link;
		ext = 'MP4';
		if (url.indexOf('.mp4') !== -1) {
			ext = 'MP4';
		}
		if (url.indexOf('.flv') !== -1) {
			ext = 'FLV';
		}
		if (url.indexOf('.mov') !== -1) {
			ext = 'MOV';
		}
		if (url.indexOf('.mpg') !== -1) {
			ext = 'MPG';
		}

		ext = ext.toLowerCase();
		format = ext.toUpperCase();

		quality = ext;
		var qualityMath = SaveFrom_Utils.getMatchFirst(links[i], /\.(\d+)\.mp4/i);
		if (qualityMath) {
			quality = qualityMath;
		}
	 }

	 var trueName = [144, 240, 360, 480, 720, 1080, 1440, '4K'];
	 var origName = ['mobile', 'lowest', 'low', 'sd', 'hd', 'full', 'quad', 'ultra'];
	 var pos = origName.indexOf(quality);
	 if (pos !== -1) {
		quality = trueName[pos];
	 }

	 popupLink = {
		href: url,
		title: link.title ? link.title : title,
		ext: ext,
		format: format,
		quality: quality,
		forceDownload: true
	 };
	 menuLinks.push(popupLink);
	}
	return menuLinks;
		},
		getPosterData: function(node) {
	var infoNode = mono.getParent(node, '[hrefattrs]');

	var info = infoNode && infoNode.getAttribute('hrefattrs');

	if (!info) {
	 return;
	}

	var params = mono.parseUrl(info, {
	 params: true
	});

	var sid = params['st.vpl.sid'];
	var vid = params['st.vpl.id'];
	if (!vid) {
	 var vidNode = mono.getParent(node, '[data-id]');
	 vid = vidNode && vidNode.dataset.id;

	 if (vid && vid[0] === 'c') {
		return;
	 }
	}

	if (vid && vid.substr(0, 3) === 'OK_') {
	 vid = SaveFrom_Utils.getMatchFirst(vid, /OK_\d+_(\d+)/);
	}

	if (!vid) {
	 return;
	}

	return {
	 request: {
		sid: sid,
		action: 'getPlayerMetadata',
		extVideoId: vid
	 }
	};
		},
		onBtnClick: function(e) {
	e.preventDefault();
	e.stopPropagation();

	var info = JSON.parse(this.dataset.sfContext);
	mono.onRemoveEvent(this, odnoklassniki.hideMenu);

	if (odnoklassniki.contextMenu && odnoklassniki.contextMenu.isShow) {
	 if (odnoklassniki.contextMenu.button !== this) {
		odnoklassniki.hideMenu();
	 } else {
		odnoklassniki.hideMenu();
		return;
	 }
	}

	// post in popup
	var menuParent = document.querySelector('#mtLayer.__active #mtLayerMain > div');
	if (!menuParent) {
	 // video gaallery
	 menuParent = document.getElementById('vv_content');
	}
	if (!menuParent) {
	 // pm msgs
	 menuParent = document.getElementById('__messagesList__');
	 if (menuParent && !menuParent.offsetParent) {
		menuParent = null;
	 }
	}
	if (!menuParent && mono.matches(this, '#mainContent ' + this.tagName)) {
	 menuParent = mono.getParent(this, '#mainContent');
	}
	var menu = odnoklassniki.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', 'sf-popupMenu', {
	 parent: menuParent || undefined
	});

	video.loadLinks(info, menu);
		},
		onImgOver: function(e) {
	var _this = this;
	var isChat = mono.matches(this, '.mdialog_chat_window .d_comment_text_w ' + this.tagName);

	var container = null;
	if (isChat) {
	 container = mono.getParentByClass(this, 'd_comment_text_w');
	} else
	if (mono.matches(this, '.video-card > .video-card_img-w ' + this.tagName)) {
	 container = mono.getParentByClass(this, 'video-card_img-w');
	} else
	if (this.classList.contains('vid-card_cnt')) {
	 container = this;
	} else
	if (mono.matches(this, '.vid-card_cnt ' + this.tagName)) {
	 container = mono.getParentByClass(this, 'vid-card_cnt');
	}

	if (!container) {
	 return;
	}

	if (container.getElementsByClassName(videoFeed.btnClassName).length) {
	 return;
	}

	var isLive = !!container.querySelector('.vid-card_live.__active');
	if (isLive) {
	 return;
	}

	var isVideoVitrina = mono.matches(this, '.vid-card_img__link ' + this.tagName);

	var btnData = video.getPlayerOptions(this);
	if (!btnData) {
	 btnData = videoFeed.getPosterData(this);
	}
	if (!btnData) {
	 return;
	}

	btnData.isChat = isChat;

	var customStyle = {};

	if (isChat) {
	 mono.extend(customStyle, {
		left: '15px',
		top: '15px'
	 });
	}

	if (isVideoVitrina) {
	 mono.extend(customStyle, {
		backgroundColor: '#454648',
		borderColor: 'rgb(53, 53, 53)'
	 });
	}

	var btn = mono.create('i', {
	 class: [videoFeed.btnClassName, 'sf-video-feed-bind-on-insert'],
	 style: customStyle,
	 data: {
		sfContext: JSON.stringify(btnData)
	 },
	 title: language.download
	});

	btn.appendChild(mono.create(SaveFrom_Utils.svg.getSvg('download'), {
	 style: {
		width: '12px',
		height: '12px',
		margin: '4px'
	 }
	}));

	container.appendChild(btn);
		},
		injectStyle: function() {
	if (this.style) {
	 if (!this.style.parentNode) {
		document.head.appendChild(this.style);
	 }
	 return;
	}

	this.style = mono.create('style', {
	 text: mono.style2Text({
		'div > .sf-feed-dl-btn': {
			display: 'none',
			border: '1px solid #F8F8F8',
			width: '20px',
			height: '20px',
			padding: 0,
			position: 'absolute',
			top: 0,
			left: 0,
			zIndex: 1,
			cursor: 'pointer',
			backgroundColor: '#F8F8F8'
		},
		'div > .sf-feed-dl-btn svg path': {
			fill: '#eb722e'
		},
		'div > .sf-feed-dl-btn:hover svg path': {
			fill: '#00B75A'
		},
		'div > .sf-feed-dl-btn:active': {
			outline: 0,
			boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
		},
		'div:hover > .sf-feed-dl-btn': {
			display: 'block'
		}
	 })
	});

	document.head.appendChild(this.style);
		},
		disable: function() {
	if (this.style) {
	 this.style.parentNode && this.style.parentNode.removeChild(this.style);
	}
		},
		rmBtn: function() {
	var btnList = document.querySelectorAll('.sf-feed-dl-btn');
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
	var dataAttr = mono.dataAttr2Selector('sfSkip2');
	var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
	for (i = 0, item; item = dataAttrList[i]; i++) {
	 item.removeAttribute(dataAttr);
	}
		}
	};

	// /VIDEO
	///////////////////////////////////////////////////////////////////


	// PHOTO
	///////////////////////////////////////////////////////////////////

	var photo = {
		style: null,
		rmCurrentPhotoBtn: function(insertContainer) {
	var exBtn = undefined;
	var imgList = document.querySelectorAll('.sf-dl-current-photo-btn');
	for (var i = 0, imgItem; imgItem = imgList[i]; i++) {
	 if (!insertContainer || !insertContainer.contains(imgItem)) {
		imgItem.parentNode.removeChild(imgItem);
	 } else {
		exBtn = imgItem;
	 }
	}
	return exBtn;
		},
		addDlCurrentPhotoBtn: function(container) {
	var exBtn = this.rmCurrentPhotoBtn(container);
	if (exBtn) {
	 return;
	}

	var _this = odnoklassniki;

	container.appendChild(mono.create('a', {
	 class: 'sf-dl-current-photo-btn',
	 href: '#',
	 title: language.download,
	 on: ['click', function(e) {
		e.stopPropagation();
		e.preventDefault();

		if (_this.contextMenu && _this.contextMenu.isShow && _this.contextMenu.button === this) {
			if (_this.contextMenu.button !== this) {
		_this.hideMenu();
			} else {
		_this.hideMenu();
		return;
			}
		}

		var onKeyDown = function(e) {
			if (e.keyCode === 18 || e.keyCode === 17) return;
			menu.hide();
			document.removeEventListener('keydown', onKeyDown);
		};

		var menu = _this.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', "photoDlMenu", {
			parent: container,
			onShow: function() {
		document.addEventListener('keydown', onKeyDown);
			},
			onHide: function() {
		document.removeEventListener('keydown', onKeyDown);
			}
		});

		var linkList = [];

		var img = container.querySelector('img.photo-layer_img');
		if (img) {
			var url = img.dataset.fsSrc || img.dataset.nfsSrc || img.src;
			if (url) {
		linkList.push({
		 href: url,
		 title: 'photo_' + parseInt(Date.now() / 1000),
		 quality: language.download,
		 format: ' ',
		 ext: 'jpg',
		 forceDownload: true,
		 isBlank: true,
		 func: function() {
			menu.hide();
		 }
		});
			}
		}

		if (!img) {
			img = container.querySelector('div.gif[data-gifsrc]');
		}

		if (img) {
			var map = {
		webmsrc: 'webm',
		mp4src: 'mp4',
		gifsrc: 'gif'
			};
			Object.keys(map).forEach(function(type) {
		var url = img.dataset[type];
		if (!url) {
		 return;
		}

		var ext = map[type];

		linkList.push({
		 href: url,
		 title: 'gif_' + parseInt(Date.now() / 1000),
		 quality: language.download,
		 format: ext.toUpperCase(),
		 ext: ext,
		 forceDownload: true,
		 isBlank: true,
		 func: function() {
			menu.hide();
		 }
		});
			});
		}

		if (linkList.length === 0) {
			menu.update(language.noLinksFound);
			return;
		}

		menu.update(linkList);
	 }],
	 append: [
		mono.create(SaveFrom_Utils.svg.getSvg('download'), {
			style: {
		width: '12px',
		height: '12px',
		margin: '4px'
			}
		})
	 ]
	}));
		},
		injectStyle: function() {
	if (photo.style) {
	 if (!photo.style.parentNode) {
		document.head.appendChild(photo.style);
	 }
	 return;
	}

	photo.style = mono.create('style', {
	 text: mono.style2Text({
		'div > .sf-dl-current-photo-btn': {
			display: 'none',
			border: '1px solid #F8F8F8',
			width: '20px',
			height: '20px',
			padding: 0,
			position: 'absolute',
			backgroundColor: '#F8F8F8',
			top: '73px',
			left: '90px',
			zIndex: 100,
			cursor: 'pointer'
		},
		'div > .sf-dl-current-photo-btn svg path': {
			fill: '#eb722e'
		},
		'div > .sf-dl-current-photo-btn:hover svg path': {
			fill: '#00B75A'
		},
		'div > .sf-dl-current-photo-btn:active': {
			outline: 0,
			boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
		},
		'div:hover > .sf-dl-current-photo-btn': {
			display: 'block'
		}
	 })
	});

	document.head.appendChild(photo.style);
		},
		addCurrentDlBtn: function(container) {
	if (container.dataset.sfSkip2 === '1') {
	 return;
	}
	container.dataset.sfSkip2 = '1';

	var img = container.querySelector('img.photo-layer_img');
	if (img) {
	 var url = img.dataset.fsSrc || img.dataset.nfsSrc || img.src;
	 if (!url) {
		img = null;
	 }
	}

	if (!img) {
	 img = container.querySelector('div.gif[data-gifsrc]');
	}

	if (!img) {
	 return;
	}

	this.addDlCurrentPhotoBtn(container);
		}
	};

	// /PHOTO
	//////////////////////////////////////////////////////////////////
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.ok.ru/*',
	'*://*.odnoklassniki.ru/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('mailru', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleMailru ? 1 : 0;

	var allowDownloadMode = mono.isChrome || mono.isFirefox || (mono.isGM && mono.isTM);

	var Promise = mono.Promise;

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return mailru.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'downloadMP3Files') {
	if (allowDownloadMode) {
	 audio.downloadMP3Files();
	} else {
	 audio.showListOfAudioFiles(false);
	}
		}
		if (message.action === 'downloadPlaylist') {
	audio.showListOfAudioFiles(true);
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	mailru.run();
		});
	}

	var mailru = {
		contextMenu: null,
		run: function() {
	moduleState = 1;

	audio.injectStyle();
	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 this.mutationMode.enable();
	}
		},
		changeState: function(state) {
	moduleState = state;
	audio.rmBtn();
	video.rmBtn();
	this.mutationMode.stop();
	this.hideMenu();
	if (state) {
	 this.run();
	}
		},
		hideMenu: function() {
	if (mailru.contextMenu) {
	 mailru.contextMenu.hide();
	 mailru.contextMenu = null;
	}
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	wrapAudioOnMouseOver: function() {
	 if (!moduleState) {
		return;
	 }

	 if (this.dataset.sfSkip > 0) {
		return;
	 }
	 this.dataset.sfSkip = '1';

	 var context = JSON.parse(this.dataset.sfContext);
	 audio.onTrackOver(this, context.type);
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node, context;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		context = {};
		context.type = 0;
		node.dataset.sfContext = JSON.stringify(context);
		mono.one(node, 'mouseenter', mailru.mutationMode.wrapAudioOnMouseOver);
			}

			summary = summaryList[1];
			for (n = 0; node = summary.added[n]; n++) {
		context = {};
		context.type = 1;
		node.dataset.sfContext = JSON.stringify(context);
		mono.one(node, 'mouseenter', mailru.mutationMode.wrapAudioOnMouseOver);
			}

			var videoInfo, parent, info;

			for (i = 2; i < 4; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 videoInfo = video.getVideoId(node);
		 if (!videoInfo) {
			continue;
		 }

		 parent = mono.getParentByClass(node, 'b-video__left');
		 if (!parent) {
			continue;
		 }

		 info = parent.querySelector('.b-video__info-time');
		 if (!info) {
			continue;
		 }

		 video.insertBtnInPopup(videoInfo, info);
		}
			}

			for (i = 4; i < 6; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 videoInfo = video.getVideoId(node);
		 if (!videoInfo) {
			continue;
		 }

		 parent = mono.getParentByClass(node, 'sp-video__item-page');
		 if (!parent) {
			continue;
		 }

		 info = parent.querySelector('.sp-video__item-page__info__additional');
		 if (!info) {
			continue;
		 }

		 video.insertBtnInPage(videoInfo, info, 1);
		}
			}

			for (i = 6; i < 8; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 parent = mono.getParentByClass(node, 'sp-video__item-page-new__video-content');
		 info = parent && parent.querySelector('.sp-video__item-page-new__actions');
		 videoInfo = parent && video.getVideoContentVideoId(parent);
		 if (!info || !videoInfo) {
			continue;
		 }

		 video.insertBtnInPage(videoInfo, info, 2);
		}
			}

			summary = summaryList[0];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: '.jp__track',
		is: 'added'
			},
			{
		css: '.song-item',
		is: 'added'
			},
			{
		css: '.b-video__left .b-video__container object',
		is: 'added'
			},
			{
		css: '.b-video__left .b-video__container video',
		is: 'added'
			},
			{
		css: '.sp-video__item-page .sp-video__item-page__video-wrapper object',
		is: 'added'
			},
			{
		css: '.sp-video__item-page .sp-video__item-page__video-wrapper video',
		is: 'added'
			},
			{
		css: '.sp-video__item-page-new .sp-video__item-page-new__video-content video',
		is: 'added'
			},
			{
		css: '.sp-video__item-page-new .sp-video__item-page-new__video-content object',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		}
	};

	var tooltip = {
		tooltip: undefined,
		updatePos: function(button, options) {
	var btnPosition = SaveFrom_Utils.getPosition(button);
	var size = SaveFrom_Utils.getSize(this.tooltip);

	this.tooltip.style.top = (btnPosition.top + options.top - size.height) + 'px';

	var left = btnPosition.left + parseInt(options.width / 2) - parseInt(size.width / 2);
	var pageWidth = document.body.clientWidth + document.body.scrollLeft;
	if (pageWidth < left + size.width) {
	 left = pageWidth - size.width;
	}
	this.tooltip.style.left = left + 'px';
		},
		show: function(button, options) {
	var _this = this;
	if (this.tooltip !== undefined) {
	 this.hide();
	} else {
	 this.tooltip = mono.create('div', {
		class: 'sf-tooltip',
		style: mono.extend({
			position: 'absolute',
			display: 'none',
			zIndex: 9999,
			opacity: 0,
			transition: 'opacity 0.2s',
			whiteSpace: 'nowrap',
			fontSize: '12px',
			color: '#111',
			fontFamily: 'arial, verdana, sans-serif, Lucida Sans'
		}, options.style),
		on: ['mouseenter', function(e) {
			_this.hide();
		}]
	 });
	 document.body.appendChild(this.tooltip);
	}
	this.tooltip.style.display = 'block';

	setTimeout(function() {
	 _this.updatePos(button, options);
	 _this.tooltip.style.opacity = 1;
	});

	return this.tooltip;
		},
		hide: function() {
	this.tooltip.style.opacity = 0;
	this.tooltip.style.display = 'none';
		}
	};

	var getFolderName = function() {
		var folderName = document.title;
		var sep = folderName.indexOf('-');
		if (sep !== -1) {
	folderName = folderName.substr(0, sep - 1);
		}

		return mono.fileName.modify(folderName);
	};

	var audio = {
		className: 'sf-audio-panel',
		lastRow: null,
		style: undefined,
		secondsFromDuration: function(time) {
	var minSec = time.split(':').map(function(item) {
	 return parseInt(item);
	});
	return minSec[0] * 60 + minSec[1];
		},
		getTitle: function(row) {
	var title = row.querySelector('.jp__track-fullname');
	var artist = row.querySelector('.jp__track-performer');
	if (artist === null) {
	 artist = row.querySelector('.jp__track-name-text');
	 if (artist !== null) {
		artist = artist.querySelector('a:not(.jp__track-fullname)');
		if (artist !== null) {
			var tmp = title;
			title = artist;
			artist = tmp;
		}
	 }
	}
	if (artist !== null) {
	 artist = artist.textContent.trim();
	 if (!artist) {
		artist = 'noname';
	 }
	} else {
	 artist = '';
	}
	if (artist) {
	 artist = artist + ' - ';
	} else {
	 artist = '';
	}
	if (title === null) {
	 return;
	}
	title = title.textContent;
	var fullName = artist + title;
	fullName = fullName.replace(/[\r\n\t\s]+/img, ' ').replace(/\s+/g, ' ').trim();
	return fullName;
		},
		getTitle2: function(row) {
	var title = row.querySelector('.title');
	var name = row.querySelector('.name');
	var author = row.querySelector('.author');
	if (name) {
	 name = name.textContent;
	 if (!name.length) {
		name = 'noname';
	 }
	}
	if (author) {
	 author = author.textContent;
	}
	var fullName = '';
	if (name && author) {
	 fullName = author + ' - ' + name;
	} else {
	 fullName = title.textContent;
	}
	fullName = fullName.replace(/[\r\n\t\s]+/img, ' ').replace(/\s+/g, ' ').trim();
	return fullName;
		},
		getMp3UrlList: function(cb) {
	var type = 1;
	var rowList = document.querySelectorAll('.song-item');
	if (rowList.length === 0) {
	 rowList = document.querySelectorAll('.jp__track');
	 type = 0;
	}

	var urlList = [];
	var dblList = {};

	var next = function(row) {
	 promise = promise.then(function() {
		return new Promise(function(resolve) {
			audio.getUrl(row, type, resolve);
		});
	 }).then(function(url) {
		if (url && !dblList[url]) {
			dblList[url] = 1;
			var durationNode = '';
			var fullTitle = '';
			if (type === 0) {
		durationNode = row.querySelector('.jp__track-duration-total');
		fullTitle = audio.getTitle(row);
			} else {
		durationNode = row.querySelector('.time');
		fullTitle = audio.getTitle2(row);
			}
			if (fullTitle) {
		var filename = mono.fileName.modify(fullTitle) + '.mp3';
		var duration = durationNode && audio.secondsFromDuration(durationNode.textContent);
		urlList.push({
		 url: url,
		 filename: filename,
		 title: fullTitle,
		 duration: duration
		});
			}
		}
	 }).catch(function(err) {
		// console.error(err);
	 });
	};

	var promise = Promise.resolve();
	for (var row, i = 0; row = rowList[i]; i++) {
	 next(row);
	}
	promise.then(function() {
	 cb(urlList);
	});
		},
		showListOfAudioFiles: function(isPlaylist) {
	audio.getMp3UrlList(function(list) {
	 if (list.length === 0) {
		return;
	 }
	 if (isPlaylist) {
		SaveFrom_Utils.playlist.popupPlaylist(list, getFolderName(), true);
	 } else {
		SaveFrom_Utils.playlist.popupFilelist(list);
	 }
	});
		},
		downloadMP3Files: function() {
	audio.getMp3UrlList(function(list) {
	 if (list.length === 0) {
		return;
	 }
	 SaveFrom_Utils.downloadList.showBeforeDownloadPopup(list, {
		type: 'audio',
		folderName: getFolderName()
	 });
	});
		},
		onDlBtnOver: function(e) {
	if (mono.isOpera || mono.isSafari) {
	 return;
	}
	var duration = this.dataset.duration;
	if (e.type === 'mouseenter') {
	 var _this = this;
	 var options = undefined;
	 var ttp = tooltip.show(_this, options = {
		top: -14,
		width: 16,
		style: {
			backgroundColor: '#fff',
			border: '1px solid #ccc',
			color: 'rgb(48, 48, 48)'
		}
	 });

	 if (_this.dataset.bitrate) {
		ttp.style.padding = '2px 5px 3px';
		ttp.textContent = ' (' + _this.dataset.size + ' ~ ' + _this.dataset.bitrate + ')';
		return;
	 }

	 if (_this.dataset.size) {
		ttp.style.padding = '2px 5px 3px';
		ttp.textContent = ' (' + _this.dataset.size + ')';
		return;
	 }

	 ttp.style.padding = '2px 2px 0 2px';
	 ttp.textContent = '';
	 ttp.appendChild(mono.create('img', {
		src: '//my9.imgsmail.ru/r/my/preloader_circle_16.gif',
		height: 16,
		width: 16
	 }));

	 mono.sendMessage({
		action: 'getFileSize',
		url: _this.href
	 }, function(response) {
		ttp.style.padding = '2px 5px 3px';
		if (!response.fileSize) {
			ttp.textContent = language.getFileSizeFailTitle;
			tooltip.updatePos(_this, options);
			return;
		}
		var size = SaveFrom_Utils.sizeHuman(response.fileSize, 2);

		if (duration) {
			var bitrate = Math.floor((response.fileSize / duration) / 125) + ' ' + language.kbps;

			_this.dataset.bitrate = bitrate;
			_this.dataset.size = size;

			ttp.textContent = ' (' + size + ' ~ ' + bitrate + ')';
		} else {
			_this.dataset.size = size;

			ttp.textContent = ' (' + size + ')';
		}

		tooltip.updatePos(_this, options);
	 });
	 return;
	}
	// mouseleave
	tooltip.hide();
		},
		getUrlViaBridge: function(row, cb) {
	if (audio.getUrlViaBridge.index === undefined) {
	 audio.getUrlViaBridge.index = 0;
	}

	var className = 'sf-bridge-item-' + audio.getUrlViaBridge.index;
	audio.getUrlViaBridge.index++;
	row.classList.add(className);

	SaveFrom_Utils.bridge({
	 args: [{
		className: className
	 }],
	 func: function(data, cb) {
		var className = data.className;
		var el = document.getElementsByClassName(className)[0];
		el.classList.remove(className);

		var $data = jQuery(el).data();
		if ($data && $data.item) {
			cb($data.item.url);
		}
	 },
	 cb: function(data) {
		cb(data);
	 },
	 timeout: 300
	});
		},
		getUrlById: function(id) {
	var func = function(id, cb) {
	 var url = '';
	 var findUrl = function(items, id) {
		var url = '';
		items.some(function(item) {
			if (item._attr && item._attr.file === id) {
		url = item._attr.url;
		return true;
			}
		});
		return url;
	 };
	 try {
		url = findUrl(APP.activePage.collection.items, id);
	 } catch (err) {}
	 try {
		if (!url) {
			url = findUrl(APP.player.collection.items, id);
		}
	 } catch (err) {}
	 cb(url);
	};

	return new Promise(function(resolve) {
	 SaveFrom_Utils.bridge({
		args: [id],
		func: func,
		cb: resolve
	 });
	}).then(function(url) {
	 if (!url) {
		throw new Error('Url is not found');
	 }
	 if (/^\/\//.test(url)) {
		url = 'http:' + url;
	 }
	 return url;
	});
		},
		getUrl: function(row, rowType, cb) {
	var url = row.dataset.url;
	if (url) {
	 return cb(url);
	}
	if (rowType === 0) {
	 var urlLink = row.querySelector('a.jp__track-fullname-link');
	 if (urlLink === null) {
		return cb();
	 }
	 urlLink = urlLink.href;
	 var params = mono.parseUrl(urlLink);
	 if (!params.file || !params.uid) {
		return audio.getUrlViaBridge(row, function(url) {
			if (url) {
		row.dataset.url = url;
			}
			cb(url);
		});
	 }
	 return cb('http://music.my.mail.ru/file/' + params.file + '.mp3?u=' + encodeURIComponent(params.uid));
	} else
	if (rowType === 1) {
	 var id = row.dataset.file;
	 return audio.getUrlById(id).then(function(url) {
		cb(url);
	 }, function() {
		cb();
	 });
	}
		},
		onDlBtnClick: function(e) {
	e.stopPropagation();
	SaveFrom_Utils.downloadOnClick(e);
		},
		getDlLink: function(url, duration, fullTitle) {
	return mono.create('a', {
	 data: {
		duration: duration || ''
	 },
	 href: url,
	 style: {
		position: 'relative',
		display: 'inline-block',
		width: '16px',
		height: '16px',
		verticalAlign: 'middle'
	 },
	 download: mono.fileName.modify(fullTitle + '.mp3'),
	 on: [
		['mouseenter', audio.onDlBtnOver],
		['mouseleave', audio.onDlBtnOver],
		['click', this.onDlBtnClick]
	 ]
	});
		},
		addDownloadPanelNew: function(row, url) {
	if (!url) {
	 return;
	}
	var fullTitle = audio.getTitle2(row);
	if (!fullTitle) {
	 return;
	}
	var timeEl = row.querySelector('.time');
	var duration = timeEl && audio.secondsFromDuration(timeEl.textContent);

	var dlPanel = mono.create('div', {
	 class: [audio.className, 'type-2'],
	 append: [
		this.getDlLink(url, duration, fullTitle)
	 ]
	});

	var iconsEl = row.querySelector('.icons');
	if (iconsEl) {
	 iconsEl.appendChild(dlPanel);
	}
		},
		addDownloadPanel: function(row, url) {
	var duration = row.querySelector('.jp__track-duration-total');
	if (duration === null || url === undefined) {
	 return;
	}
	var fullTitle = audio.getTitle(row);
	if (!fullTitle) {
	 return;
	}
	duration = audio.secondsFromDuration(duration.textContent);

	var dlPanel = mono.create('div', {
	 class: [audio.className, 'type-0'],
	 append: [
		this.getDlLink(url, duration, fullTitle)
	 ]
	});

	var container = row.querySelector('.jp__track-management');
	if (!container) {
	 return;
	}

	if (container.firstChild) {
	 container.insertBefore(dlPanel, container.firstChild);
	} else {
	 container.appendChild(dlPanel);
	}
		},
		onTrackOver: function(row, rowType) {
	if (row.getElementsByClassName(audio.className).length !== 0) {
	 return;
	}

	audio.getUrl(row, rowType, function(url) {
	 if (rowType === 1) {
		audio.addDownloadPanelNew(row, url);
	 } else {
		audio.addDownloadPanel(row, url);
	 }
	});
		},
		injectStyle: function() {
	if (this.style) {
	 if (!this.style.parentNode) {
		document.head.appendChild(this.style);
	 }
	 return;
	}

	this.style = mono.create('style', {
	 text: mono.style2Text([{
		selector: '.' + this.className,
		style: {
			display: 'none',
			left: '22px',
			backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#168DE2') + ')',
			backgroundRepeat: 'no-repeat',
			backgroundPosition: 'center',
			backgroundSize: '16px'
		}
	 }, {
		selector: '.jp__track:hover .' + this.className,
		style: {
			display: 'block',
			opacity: 0.5
		}
	 }, {
		selector: '.jp__track:hover .' + this.className + '.type-0',
		style: {
			display: 'inline-block',
			marginLeft: '-16px',
			position: 'relative',
			left: '-2px'
		}
	 }, {
		selector: '.jp__track.jp__track-plays .' + this.className,
		style: {
			left: '-18px'
		}
	 }, {
		selector: '.' + this.className + ':hover',
		style: {
			opacity: '1 !important'
		}
	 }, {
		selector: '.' + this.className + '.type-2',
		style: {
			marginRight: '5px',
			marginLeft: '5px'
		}
	 }, {
		selector: '.song-item:hover .' + this.className + '.type-2',
		style: {
			display: 'inline-block',
			opacity: 0.5
		}
	 }])
	});

	document.head.appendChild(this.style);
		},
		rmBtn: function() {
	if (audio.style) {
	 audio.style.parentNode.removeChild(audio.style);
	 audio.style = undefined;
	}
	var btnList = document.querySelectorAll('.' + audio.className);
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		}
	};

	var video = {
		btnIndex: 0,
		domCache: {},
		className: 'sf-video-btn',

		prepareLinks: function(links) {
	var menuLinks = [];
	for (var i = 0, link; link = links[i]; i++) {
	 var url = link.url;
	 var format = 'FLV';
	 if (url.indexOf('.mp4') !== -1) {
		format = 'MP4';
	 }
	 if (url.indexOf('.mov') !== -1) {
		format = 'MOV';
	 }
	 if (url.indexOf('.mpg') !== -1) {
		format = 'MPG';
	 }
	 if (!link.quality) {
		link.quality = '-?-';
	 }
	 var quality = link.quality.toUpperCase();

	 var qList = ['1080P', '720P', '480P', '360P', '272P'];
	 var tList = ['1080', '720', '480', '360', '272'];

	 var qPos = qList.indexOf(quality);
	 if (qPos !== -1) {
		quality = tList[qPos];
	 }

	 var ext = format.toLowerCase();
	 var popupLink = {
		href: url,
		title: link.title,
		ext: ext,
		format: format,
		quality: quality,
		forceDownload: true
	 };
	 menuLinks.push(popupLink);
	}
	if (menuLinks.length === 0) {
	 return;
	}
	return menuLinks;
		},

		showLinkList: function(links, button, isUpdate) {
	if (!links) {
	 links = language.noLinksFound;
	}

	if (isUpdate) {
	 if (!mailru.contextMenu) {
		return;
	 }
	 mailru.contextMenu.update(links);
	 return;
	}

	if (mailru.contextMenu && mailru.contextMenu.isShow) {
	 mailru.hideMenu();
	 return;
	}
	mailru.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(button, links, 'video-links-popup', {
	 parent: mono.getParentByClass(button, 'b-video__main')
	});
		},

		appendPageBtn: function(container, btnIndex, styleIndex) {
	var exBtn = container.querySelector('.' + video.className);
	if (exBtn !== null) {
	 return;
	}

	var link = null;
	var btn = mono.create('span', {
	 class: video.className,
	 append: [
		link = mono.create('a', {
			data: {
		index: btnIndex
			},
			href: '#',
			on: ['click', function(e) {
		e.preventDefault();

		mono.onRemoveEvent(this, mailru.hideMenu);

		video.readDomCache(this.dataset.index, this);
			}]
		})
	 ]
	});

	if (styleIndex === 1) {
	 link.style.marginLeft = '15px';
	 link.textContent = language.download;
	} else
	if (styleIndex === 2) {
	 mono.create(link, {
		style: {
			fontSize: 0,
			lineHeight: 0,
			padding: '6px',
			boxShadow: 'inset 0 0 0 1px #ccc',
			borderRadius: '3px',
			display: 'inline-block'
		}
	 });
	 link.appendChild(SaveFrom_Utils.svg.getSvg('download', '#666', 18, 18));
	}

	if (styleIndex === 2) {
	 container.appendChild(btn);
	} else
	if (styleIndex === 1) {
	 var child = container.lastChild;
	 container.insertBefore(btn, child);
	 child = null;
	}
		},

		appendBtn: function(container, btnIndex) {
	var exBtn = container.querySelector('.' + video.className);
	if (exBtn !== null) {
	 return;
	}

	var child = undefined;
	var style = {};
	if (container.childNodes.length > 1) {
	 child = container.childNodes[1];
	} else {
	 child = container.lastChild;
	 style.marginRight = '5px';
	}

	var btn = mono.create('span', {
	 class: container.lastChild.getAttribute('class') + ' ' + video.className,
	 append: [
		mono.create('a', {
			data: {
		index: btnIndex
			},
			text: language.download,
			href: '#',
			on: ['click', function(e) {
		e.preventDefault();

		mono.onRemoveEvent(this, mailru.hideMenu);

		video.readDomCache(this.dataset.index, this);
			}],
			style: style
		})
	 ]
	});

	container.insertBefore(btn, child);
	child = null;
		},

		readDomCache: function(index, button) {
	video.showLinkList(language.download, button);

	var abort = function() {
	 video.showLinkList(undefined, button, 1)
	};

	var showLinks = function(response) {
	 if (response.action === 'getRutubeLinks') {
		if (!preference.showUmmyItem) {
			return abort();
		}
		video.showLinkList(SaveFrom_Utils.popupMenu.prepareLinks.rutube(response.links), button, 1);
	 } else
	 if (response.action === 'getMailruLinks') {
		video.showLinkList(SaveFrom_Utils.popupMenu.prepareLinks.mailru(response.links, response.title), button, 1);
	 } else {
		video.showLinkList(video.prepareLinks(response.links), button, 1);
	 }
	};

	var cacheItem = video.domCache[parseInt(index)];
	if (cacheItem.links) {
	 showLinks(cacheItem);
	} else
	if (cacheItem.metadataUrl) {
	 var metadataUrl = cacheItem.metadataUrl;
	 if (/^\/\//.test(metadataUrl)) {
		metadataUrl = 'http:' + metadataUrl;
	 }

	 var always = function(data) {
		if (!data || typeof data !== 'object') {
			abort();
		} else
			video.readMeta(data, function(response) {
		if (!response.links) {
		 abort();
		} else {
		 cacheItem.links = response.links;
		 cacheItem.action = response.action;

		 showLinks(cacheItem);
		}
			});
	 };

	 mono.request({
		url: metadataUrl,
		withCredentials: true,
		json: true,
		localXHR: true
	 }, function(err, resp) {
		always(err ? null : resp.body);
	 });
	} else
	if (cacheItem.url) {
	 mono.sendMessage({
		action: 'getMailruLinks',
		extVideoId: cacheItem.url
	 }, function(response) {
		if (!response.links) {
			abort();
		} else {
			cacheItem.title = response.title;
			cacheItem.links = response.links;
			cacheItem.action = response.action;

			showLinks(cacheItem);
		}
	 });
	} else {
	 abort();
	}
		},

		readMeta: function(metadata, cb) {
	var links = [],
	 title;
	if (metadata.provider === 'UPLOADED') {
	 title = metadata.movie ? metadata.movie.title : undefined;
	 if (!metadata.videos) {
		return cb();
	 }
	 metadata.videos.forEach(function(item) {
		links.push({
			quality: item.name,
			url: item.url,
			title: title
		});
	 });
	}
	if (metadata.provider === 'ugc') {
	 title = metadata.meta ? metadata.meta.title : undefined;
	 if (!metadata.videos) {
		return cb();
	 }
	 metadata.videos.forEach(function(item) {
		links.push({
			quality: item.key,
			url: item.url,
			title: title
		});
	 });
	}
	if (metadata.provider === 'pladform') {
	 title = metadata.meta ? metadata.meta.title : undefined;
	 mono.sendMessage({
		action: 'getPladformVideo',
		extVideoId: {
			playerId: metadata.meta.playerId,
			videoId: metadata.meta.videoId
		}
	 }, function(response) {
		if (!response) {
			return cb();
		}

		var links = response.links;
		if (!links) {
			return cb();
		}

		links.forEach(function(item) {
			if (typeof item !== 'object') {
		return;
			}

			if (item.title === undefined) {
		item.title = title
			}
		});

		cb(response);
	 });
	 return;
	}
	if (links.length === 0) {
	 return cb();
	}
	return cb({
	 links: links
	});
		},

		getFlashVars: function(videoObj) {
	if (!videoObj) {
	 return;
	}

	var flashvars = videoObj.querySelector('param[name="flashvars"]');
	if (!flashvars) {
	 return;
	}

	var value = flashvars.value;
	var url = mono.parseUrl(value, {
	 params: true
	});

	if (url.metadataUrl) {
	 return {
		metadataUrl: url.metadataUrl
	 };
	}
		},

		matchUrl: function(url) {
	var linkR = /\/([^\/]+)\/([^\/]+)\/video\/(.+).html/;
	var embedR = /embed\/([^\/]+)\/([^\/]+)\/(.+).html/;

	var r = url.match(linkR);
	if (!r) {
	 r = url.match(embedR);
	}

	return r;
		},

		getVideoId: function(player) {
	if (player.tagName !== 'OBJECT') {
	 player = player.querySelector('object[name="b-video-player"]');
	}

	var videoObj = this.getFlashVars(player);
	if (videoObj) {
	 return videoObj;
	}

	var albumJson = document.querySelector('[data-type="album-json"]');
	if (albumJson) {
	 try {
		albumJson = JSON.parse(albumJson.textContent);
		if (albumJson.signVideoUrl) {
			return {
		metadataUrl: albumJson.signVideoUrl
			};
		}
	 } catch (e) {}
	}

	var videoId = this.matchUrl(location.pathname);

	if (videoId) {
	 return {
		metadataUrl: 'http://api.video.mail.ru/videos/' + videoId[1] + '/' + videoId[2] + '/' + videoId[3] + '.json'
	 };
	}
		},

		getVideoContentVideoId: function(conteiner) {
	var videoId = null;

	var shareBtn = conteiner.querySelector('.sp-video__item-page-new__share__item[data-share-type][data-location]');
	if (shareBtn) {
	 videoId = {
		url: shareBtn.dataset.location
	 };
	}

	return videoId;
		},

		insertBtnInPage: function(videoInfo, info, styleIndex) {
	if (videoInfo.metadataUrl) {
	 videoInfo.metadataUrl = decodeURIComponent(videoInfo.metadataUrl);
	 video.domCache[video.btnIndex] = {
		metadataUrl: videoInfo.metadataUrl
	 };
	} else {
	 video.domCache[video.btnIndex] = mono.extend({}, videoInfo);
	}


	video.appendPageBtn(info, video.btnIndex, styleIndex);

	video.btnIndex++;
		},
		insertBtnInPopup: function(videoInfo, info) {
	if (videoInfo.metadataUrl) {
	 videoInfo.metadataUrl = decodeURIComponent(videoInfo.metadataUrl);
	 video.domCache[video.btnIndex] = {
		metadataUrl: videoInfo.metadataUrl
	 };
	}

	video.appendBtn(info, video.btnIndex);

	video.btnIndex++;
		},
		rmBtn: function() {
	var btnList = document.querySelectorAll('.' + video.className);
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://my.mail.ru/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('savefrom', function(moduleName, initData) {
	"use strict";
	var language = initData.getLanguage;
	var preference = initData.getPreference;

	mono.asyncCall(function() {
		savefrom.run();
	});

	var getRandomInt = function() {
		var now = Date.now();
		var rnd = now;
		while (now === rnd) {
	rnd = Date.now();
		}
		return rnd;
	};

	var savefrom = {
		name: moduleName,
		scriptId: 'savefrom__ext_script',
		dataAttr: 'data-extension-disabled',


		run: function() {
	savefrom.setExtParams();

	if (location.href.search(/\/(update-helper|userjs-setup)\.php/i) > -1) {
	 var btn = document.getElementById('js-not-remind');
	 if (btn) {
		btn.addEventListener('click', function(e) {
			if (e.button === 0) {
		mono.sendMessage({
		 action: 'hideUserjsMigrationInfo'
		});
			}
		});
	 }
	 return;
	}

	var form = document.getElementById('sf_form');
	if (!form)
	 return;

	form.addEventListener('submit', function(event) {
	 var url = form.sf_url.value;
	 if (!url)
		return;

	 if (form.getAttribute(savefrom.dataAttr) == '1')
		return;

	 var re = {
		getVKLinks: [
			/^https?:\/\/(?:[a-z]+\.)?(?:vk\.com|vkontakte\.ru)\/(video-?\d+_-?\d+)/i,
			/^https?:\/\/(?:[a-z]+\.)?(?:vk\.com|vkontakte\.ru)\/video_ext.php\?(.*oid=-?\d+.*)$/i,
			/^https?:\/\/(?:[a-z]+\.)?(?:vk\.com|vkontakte\.ru)\/[\w\-\.]+\?.*z=(video-?\d+_-?\d+)/i
		],
		getYoutubeLinks: [
			/^https?:\/\/(?:[a-z]+\.)?youtube\.com\/(?:#!?\/)?watch\?.*v=([\w\-]+)/i,
			/^https?:\/\/(?:[a-z0-9]+\.)?youtube\.com\/(?:embed|v)\/([\w\-]+)/i,
			/^https?:\/\/(?:[a-z]+\.)?youtu\.be\/([\w\-]+)/i
		],
		getVimeoLinks: [
			/^https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/(?:\w+\#)?(\d+)/i,
			/^https?:\/\/player\.vimeo\.com\/video\/(\d+)/i,
			/^https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/channels\/(?:[^\/]+)\/(\d+)$/i,
			/^https?:\/\/(?:[\w\-]+\.)?vimeo\.com\/[^\/]+\/review\/(\d+)\/(?:\d+)/i
		],

		getDailymotionLinks: [
			/^http:\/\/(?:www\.)?dai\.ly\/([a-z0-9]+)_?/i,
			/^https?:\/\/(?:[\w]+\.)?dailymotion\.com(?:\/embed|\/swf)?\/video\/([a-z0-9]+)_?/i
		],

		getFacebookLinks: [
			/^https?:\/\/(?:[\w]+\.)?facebook\.com(?:\/video)?\/video.php.*[?&]{1}v=([0-9]+).*/i,
			/^https?:\/\/(?:[\w]+\.)?facebook\.com\/.+\/videos(?:\/\w[^\/]+)?\/(\d+)/i
		],

		getMailruLinks: [
			/^https?:\/\/my\.mail\.ru\/([^\/]+\/[^\/]+\/[^\/]+\/[^\/]+\/[^\/]+\.html).*/i,
			/^https?:\/\/videoapi\.my\.mail\.ru\/videos\/(embed\/[^\/]+\/[^\/]+\/[^\/]+\/[^\/]+\.html).*/i
		]
	 };

	 for (var i in re) {
		for (var j = 0; j < re[i].length; j++) {
			var vid = url.match(re[i][j]);
			if (vid && vid.length > 1) {
		vid = vid[1];
		var playlist = SaveFrom_Utils.getMatchFirst(url, /list=([\w\-]+)/i);
		event.preventDefault();
		event.stopPropagation();

		var request = {
		 extVideoId: vid,
		 action: i,
		 checkSubtitles: true,
		 checkLinks: true
		};

		if (i === 'getVimeoLinks') {
		 request.url = url;
		}

		mono.sendMessage(request, function(r) {
		 savefrom.setLinks(r.action, r.extVideoId, r.links, r.title, null,
			r.subtitles, playlist, r.duration, r.thumb, r.checkLinks);
		});

		return false;
			}
		}
	 }
	}, false);


	document.body.addEventListener('click', function(event) {
	 var node = event.target;

	 if (node.tagName === 'I' && node.classList.contains('file-info-btn')) {
		savefrom.onInfoBtnClick.call(node, event);
		return;
	 }

	 if (node.tagName != 'A') {
		if (node.parentNode.tagName == 'A')
			node = node.parentNode;
		else
			return;
	 }

	 if ((mono.isChrome || mono.isFirefox) && node.classList.contains('link-download') && !node.classList.contains('disabled') && node.getAttribute('download')) {
		if (node.classList.contains('ga_track_events') && node.getAttribute('data-ga-event')) {
			mono.trigger(node, 'sendstats', {
		bubbles: true,
		cancelable: false
			});
		}
		return SaveFrom_Utils.downloadOnClick(event, null);
	 }

	 var vid = node.getAttribute('data-video-id');
	 if (!vid) {
		return;
	 }

	 if (node.getAttribute(savefrom.dataAttr) == '1')
		return;

	 var action = {
		vk: 'getVKLinks',
		yt: 'getYoutubeLinks'
	 };

	 vid = vid.split(':', 2);
	 if (vid.length != 2 || !action[vid[0]])
		return;

	 event.preventDefault();
	 event.stopPropagation();

	 node.style.display = 'none';

	 if (!node.id) {
		node.id = vid[0] + '_' + vid[1] + '_' + (Math.random() * 1000) +
			'_' + (new Date()).getTime();
	 }

	 var request = {
		extVideoId: vid[1],
		action: action[vid[0]],
		checkSubtitles: true,
		checkLinks: true
	 };

	 mono.sendMessage(request, function(r) {
		savefrom.setLinks(r.action, r.extVideoId, r.links, r.title, node,
			r.subtitles, null, r.duration, r.thumb, r.checkLinks);
	 });

	 return false;
	}, true);
		},

		onInfoBtnClick: function(e) {
	e.preventDefault();
	e.stopPropagation();

	if (this.classList.contains('sf-clicked')) {
	 return;
	}
	this.classList.add('sf-clicked');

	var className = 'sf-btn' + getRandomInt();
	this.classList.add(className);

	var rmOldScript = function() {
	 var exScript = document.getElementsByClassName('sf-script')[0];
	 if (exScript !== undefined) {
		exScript.parentNode.removeChild(exScript);
	 }
	};

	rmOldScript();
	document.body.appendChild(mono.create('script', {
	 class: 'sf-script',
	 text: '(' + function(btnClassName) {
		try {
			var btn = document.getElementsByClassName(btnClassName);
			var $btn = $(btn);

			$btn.unbind('click').removeAttr('onclick').addClass('active');

			if (btn.onclick) {
		btn.onclick = null;
			}

			var parent = $btn.closest('.result-box').find('.meta')[0];

			if (!parent) {
		return;
			}

			var boxId = 'file_info' + btnClassName;

			var box = sf.append(parent, 'div', {
		'id': boxId,
		'class': 'media-info'
			});

			sf.append(box, 'span', {
		id: boxId + '_busy'
			});

			sf.busy(boxId + '_busy', true);
		} catch (err) {}
	 }.toString() + ')(' + JSON.stringify(className) + ')'
	}));

	var url = this.nextElementSibling.href;
	var title = this.nextElementSibling.textContent;
	mono.sendMessage({
	 action: 'getFileSize',
	 url: url
	}, function(response) {
	 var size = response.fileSize;
	 var data = {
		size: {
			name: {
		trans: language.size
			},
			value: SaveFrom_Utils.sizeHuman(size)
		}
	 };
	 rmOldScript();
	 document.body.appendChild(mono.create('script', {
		class: 'sf-script',
		text: '(' + function(btnClassName, title, json) {
			try {
		var busy = document.getElementById('file_info' + btnClassName + '_busy');

		$(busy).slideUp();

		var btn = document.getElementsByClassName(btnClassName);

		sf.fileInfo.show(json, title, btn, busy.parentNode);
			} catch (err) {}
		}.toString() + ')(' + [
			JSON.stringify(className),
			JSON.stringify(title),
			JSON.stringify(data)
		].join(',') + ')'
	 }));
	});
		},

		setExtParams: function() {
	var script = mono.create('script', {
	 id: 'savefrom__ext_params',
	 type: 'text/javascript'
	});

	var params = {
	 id: preference.sfHelperName,
	 version: preference.version,
	 enable: 1
	};

	script.textContent = '(' + function(json) {
	 try {
		if (window.setBrowserExtension && typeof setBrowserExtension == "function") {
			setBrowserExtension(json);
		}
	 } catch (err) {}
	}.toString() + ')(' + JSON.stringify(params) + ')';

	document.body.appendChild(script);
		},


		setLinks: function(action, vid, links, title, btn, subtitles, playlist,
	duration, thumb, valid) {
	if (valid === false) {
	 savefrom.handleError(btn);
	 return;
	}

	switch (action) {
	 case 'getYoutubeLinks':
		savefrom.setYoutubeLinks(vid, links, title, btn, subtitles,
			playlist, duration, thumb);
		break;
	 case 'getVKLinks':
		savefrom.setVKLinks(vid, links, title, btn, duration, thumb);
		break;

	 case 'getVimeoLinks':
		savefrom.setVimeoLinks(vid, links, title, btn, duration, thumb);
		break;

	 case 'getDailymotionLinks':
		savefrom.setDailymotionLinks(vid, links, title, btn, duration, thumb);
		break;

	 case 'getFacebookLinks':
		savefrom.setFacebookLinks(vid, links, title, btn, duration, thumb);
		break;

	 case 'getMailruLinks':
		savefrom.setMailruLinks(vid, links, title, btn, duration, thumb);
		break;
	}
		},


		handleError: function(btn) {
	if (btn) {
	 if (btn) {
		btn.style.display = '';
		btn.setAttribute(savefrom.dataAttr, '1');
		btn.click();
	 }
	 return;
	}

	var form = document.getElementById('sf_form');
	if (!form)
	 return;

	form.setAttribute(savefrom.dataAttr, '1');
	form.submit();
	form.removeAttribute(savefrom.dataAttr);
		},

		showVideoResult: function(result, btn) {
	if (!result || !result.url || !result.url.length) {
	 savefrom.handleError(btn);
	 return;
	}

	var script = document.getElementById(savefrom.scriptId);
	if (script) {
	 script.parentNode.removeChild(script);
	}

	script = mono.create('script', {
	 id: savefrom.scriptId,
	 type: 'text/javascript'
	});

	var fn;
	if (btn) {
	 var btnId = btn.id;
	 fn = '(' + function(btnId, json) {
		try {
			var btn = document.getElementById(btnId);
			sf.result.replaceAjaxResult(json, true, true, btn);
		} catch (err) {}
	 }.toString() + ')("' + btnId + '",' + JSON.stringify(result) + ')';
	} else {
	 fn = '(' + function(json) {
		try {
			sf.finishRequest(true);
			sf.videoResult.show(json);
		} catch (err) {}
	 }.toString() + ')(' + JSON.stringify(result) + ')';
	}
	script.textContent = fn;

	document.body.appendChild(script);
		},


		setVKLinks: function(vid, links, title, btn, duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var result = {
	 id: vid,
	 url: links,
	 hosting: 'vk.com (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: "http://vk.com/" + vid,
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 }
	};

	if (thumb)
	 result.thumb = thumb;

	for (var i = 0; i < result.url.length; i++) {
	 result.url[i].info_url = '#';

	 if (!result.url[i].ext && result.url[i].type)
		result.url[i].ext = result.url[i].type;

	 if (!result.sd && !result.url[i].subname)
		result.sd = {
			url: result.url[i].url
		};
	 else if (!result.hd && result.url[i].subname && parseInt(result.url[i].subname) >= 720)
		result.hd = {
			url: result.url[i].url
		};
	}

	savefrom.showVideoResult(result, btn);
		},

		setYoutubeLinks: function(vid, links, title, btn, subtitles, playlist,
	duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var result = {
	 id: vid,
	 url: [],
	 hosting: '101 (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: (vid ? 'http://youtube.com/watch?v=' + vid : ''),
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 },
	 thumb: (vid ? 'http://i.ytimg.com/vi/' + vid + '/hqdefault.jpg' : '')
	};

	var sig = false;


	SaveFrom_Utils.video.yt.init();

	var meta = links.meta || {};

	for (var formatName in SaveFrom_Utils.video.yt.format) {
	 var f = SaveFrom_Utils.video.yt.format[formatName];
	 for (var iTag in f) {
		var metaTag = meta[iTag] || {};
		if (links[iTag]) {
			if (!sig && links[iTag].search(/(\?|&)sig(nature)?=/i) > -1) {
		sig = true;
			}

			var quality = f[iTag].quality;

			if (metaTag.quality) {
		quality = metaTag.quality;
			}

			var l = {
		url: links[iTag],
		name: formatName,
		subname: quality,
		info_url: '#',
		type: formatName,
		quality: quality,
		attr: {}
			};


			if (f[iTag].sFps) {
		l.subname += ' ' + (metaTag.fps || 60);
			}

			if (f[iTag]['3d']) {
		l.name = '3D ' + l.name;
		l.group = '3d';
		l['3d'] = true;
			} else if (f[iTag]['noAudio']) {
		l.group = 'MP4 ';
		l.attr['class'] = 'no-audio';
		// l.ext = 'mp4';
			} else if (formatName == 'Audio AAC') {
		l.type = 'AAC';
		l.ext = 'aac';
		l.attr.style = 'white-space: nowrap;';
			} else if (formatName == 'Audio Vorbis') {
		l.type = 'Vorbis';
		l.ext = 'webm';
		l.attr.style = 'white-space: nowrap;';
			} else if (formatName == 'Audio Opus') {
		l.type = 'Opus';
		l.ext = 'opus';
		l.attr.style = 'white-space: nowrap;';
			} else {
		if (formatName.toLowerCase() == 'flv' && !result.sd) {
		 result.sd = {
			url: links[iTag]
		 };
		}

		if (parseInt(quality) >= 720 && result.sd && !result.hd) {
		 result.hd = {
			url: links[iTag]
		 };
		}
			}

			if (l.ext === undefined && l.type) {
		l.ext = l.type.toLowerCase();
			}

			if (f[iTag].noVideo === undefined && f[iTag].noAudio === undefined) {
		l.no_download = true;
			}

			result.url.push(l);
			delete links[iTag];
		}
	 }
	}

	if (!sig) {
	 savefrom.handleError(btn);
	 return;
	}

	if (subtitles && subtitles.length > 0) {
	 var subsId = vid.replace(/[^\w]/, '_');
	 var btnId = 'yt_subs_btn_' + subsId;
	 subsId = 'yt_subs_' + subsId;

	 var subtToken = 'extension';
	 var subsTitle = result.meta.title ? btoa(SaveFrom_Utils.utf8Encode(result.meta.title)) : '';

	 result.action = [];
	 result.action.push({
		name: language.subtitles,
		attr: {
			id: btnId,
			href: '#'
		},

		bind: {
			click: {
		fn: 'sf.youtubeSubtitles("{vid}","{subsId}","{btnId}","{subtToken}","{subsTitle}")'
		 .replace('{vid}', vid)
		 .replace('{subsId}', subsId)
		 .replace('{btnId}', '#' + btnId)
		 .replace('{subtToken}', subtToken)
		 .replace('{subsTitle}', subsTitle)
			}
		}
	 });
	}

	if (playlist && false) {
	 playlist = 'http://www.youtube.com/playlist?list=' + playlist;
	 if (!result.action)
		result.action = [];
	 result.action.push({
		name: language.playlist,
		attr: {
			href: '#',
			class: 'tooltip',
			title: language.downloadWholePlaylist
		},

		bind: {
			click: {
		fn: 'sf.processLink("{playlist}");'
		 .replace('{playlist}', playlist)
			}
		}
	 });
	}

	if (preference.showUmmyItem) {
	 result.action = result.action || [];

	 result.action.push({
		name: 'HD / MP3',
		group: 'ummy',
		'second-btn': true,

		attr: {
			href: 'ummy://www.youtube.com/watch?v=' + vid,
			class: 'ummy-link ga_track_events',
			target: '_blank',
			'data-ga-event': 'send;event;vidacha-helper;youtube;ummy-helper-button-click'
		}
	 });

	 result.fn = result.fn || [];
	 result.fn.push("if(window.ga){ga('send','event','vidacha-helper','youtube','ummy-helper-button-show');}");
	}

	savefrom.showVideoResult(result, btn);
		},

		setVimeoLinks: function(vid, links, title, btn, duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var result = {
	 id: vid,
	 url: links,
	 hosting: 'vimeo.com (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: "http://vimeo.com/" + vid,
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 }
	};

	if (thumb)
	 result.thumb = thumb;

	for (var i = 0; i < result.url.length; i++) {
	 result.url[i].info_url = '#';

	 if (!result.url[i].ext && result.url[i].type)
		result.url[i].ext = result.url[i].type;

	 if (!result.sd && result.url[i].name == 'SD')
		result.sd = {
			url: result.url[i].url
		};
	 else if (!result.hd && result.url[i].name == 'HD')
		result.hd = {
			url: result.url[i].url
		};
	}

	savefrom.showVideoResult(result, btn);
		},

		setDailymotionLinks: function(vid, links, title, btn, duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var linkList = links.filter(function(item) {
	 if (!item.extra) {
		return true;
	 }
	});

	var result = {
	 id: vid,
	 url: linkList,
	 hosting: 'dailymotion.com (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: "http://dai.ly/" + vid,
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 }
	};

	if (thumb)
	 result.thumb = thumb;

	var hd_size = 0,
	 sd_size = 0;
	for (var i = 0, item; item = linkList[i]; i++) {
	 item.info_url = '#';

	 if (item.height >= 720) {
		if (hd_size < item.height) {
			result.hd = {
		url: item.url
			};
			hd_size = item.height;
		}
	 } else
	 if (sd_size < item.height) {
		result.sd = {
			url: item.url
		};
		sd_size = item.height;
	 }
	 delete item.height;
	}
	savefrom.showVideoResult(result, btn);
		},

		setFacebookLinks: function(vid, links, title, btn, duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var result = {
	 id: vid,
	 url: links,
	 hosting: 'facebook.com (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: "https://facebook.com/video.php?v=" + vid,
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 }
	};

	if (thumb) {
	 result.thumb = thumb;
	}

	for (var i = 0, item; item = links[i]; i++) {
	 item.info_url = '#';

	 if (item.name === "SD") {
		result.sd = {
			url: item.url
		};
	 } else
	 if (item.name === "HD") {
		result.hd = {
			url: item.url
		};
	 }

	 item.subname = item.name;
	 item.name = item.ext;
	}
	savefrom.showVideoResult(result, btn);
		},

		setMailruLinks: function(vid, links, title, btn, duration, thumb) {
	if (!vid || !links) {
	 savefrom.handleError(btn);
	 return;
	}

	var result = {
	 id: vid,
	 url: links,
	 hosting: 'mail.ru (h)',
	 meta: {
		title: (title ? mono.fileName.modify(title) : 'download'),
		source: "http://my.mail.ru/" + vid,
		duration: SaveFrom_Utils.secondsToDuration(duration)
	 }
	};

	if (thumb)
	 result.thumb = thumb;

	var maxSd = 0;
	for (var i = 0, item; item = result.url[i]; i++) {
	 item.info_url = '#';
	 if (!isNaN(parseInt(item.subname))) {
		if (maxSd < item.subname && item.subname < 720) {
			result.sd = {
		url: item.url
			};
			maxSd = item.subname;
		}
		if (!result.hd && item.subname >= '720') {
			result.hd = {
		url: item.url
			};
		}
	 } else {
		if (item.subname.toLowerCase() === 'sd') {
			result.sd = {
		url: item.url
			};
		} else if (item.subname.toLowerCase() === 'hd') {
			result.hd = {
		url: item.url
			};
		}
	 }
	}

	savefrom.showVideoResult(result, btn);
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.savefrom.net/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (!mono.isIframe()) {
		if (location.href.search(/savefrom\.net\/(index\d?\.php|user\.php|update-helper\.php|userjs-setup\.php|\d+-[^\/]+\/|articles\/.+)?(\?|#|$)/i) === -1) {
	return false;
		}
	} else {
		var allowFrame = false;

		if (mono.isGM) {
	allowFrame = location.href.indexOf('/tools/helper-check.html') !== -1;
		}

		if (!allowFrame) {
	return false;
		}
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('soundcloud', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleSoundcloud ? 1 : 0;

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return sc.changeState(message.state);
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	sc.run();
		});
	}

	var sc = {
		client_id: 'fDoItMDbsbZz8dY16ZzARCZmzgHBPotA',

		lastRow: null,
		timer: 0,
		btnClass: 'savefrom-helper--btn',

		nodeId: 0,

		tracks: {},
		audioElClassList: ['soundList__item', 'searchList__item', 'trackList__item', 'compactTrackList__item', 'soundBadgeList__item'],

		refreshClientId: function() {
	SaveFrom_Utils.bridge({
	 func: function(cb) {
		if (typeof webpackJsonp === 'undefined') {
			return;
		}
		webpackJsonp([], {
			0: function(e, t, n) {
		"use strict";
		var getObjList = function() {
		 var list = [];
		 for (var key in n) {
			if (!n.hasOwnProperty(key)) {
				continue;
			}
			var obj = n[key];
			if (typeof obj !== 'object') {
				continue;
			}
			var hasExports = false;
			for (var key2 in obj) {
				if (obj[key2].exports) {
			hasExports = true;
				}
				break;
			}
			if (!hasExports) {
				continue;
			}
			list.push(obj);
		 }

		 return list;
		};

		var clientId;
		getObjList().some(function(obj) {
		 for (var index in obj) {
			var item = obj[index];
			if (!item) {
				continue;
			}
			var exports = item.exports;
			if (!exports) {
				continue;
			}
			var _store = exports._store;
			if (!_store) {
				continue;
			}

			clientId = _store.client_id;
			if (clientId) {
				break;
			}
		 }

		 if (clientId) {
			return true;
		 }
		});

		cb({
		 client_id: clientId
		});
			}
		});
	 },
	 cb: function(data) {
		if (!data || !data.client_id) {
			return;
		}

		mono.storage.setExpire({
			scClientId: data.client_id
		}, 21600);

		sc.client_id = data.client_id;
	 }
	});
		},

		loadClientId: function(cb) {
	var _this = this;
	mono.storage.getExpire('scClientId', function(storage, isExpire) {
	 if (storage.scClientId) {
		_this.client_id = storage.scClientId;
	 }

	 if (isExpire) {
		_this.refreshClientId();
	 }

	 return cb();
	});
		},

		run: function() {
	moduleState = 1;
	var _this = this;
	return this.loadClientId(function() {
	 if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
		_this.mutationMode.enable();
	 }
	});
		},

		changeState: function(state) {
	moduleState = state;
	sc.mutationMode.stop();
	sc.rmBtn();
	if (state) {
	 sc.run();
	}
		},

		rmBtn: function() {
	['sfSkip', 'sfId', 'sfSingle'].forEach(function(attr) {
	 var dataAttr = mono.dataAttr2Selector(attr);
	 var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
	 for (var i = 0, item; item = dataAttrList[i]; i++) {
		item.removeAttribute(dataAttr);
	 }
	});
	var btnList = document.querySelectorAll('.' + sc.btnClass);
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		},

		handleRow: function(row, single) {
	var parent = row.querySelector('.soundActions .sc-button-group');
	if (!parent) {
	 // console.log('no parent!', row);
	 return;
	}

	var onGetTackInfo = function(info) {
	 if (info) {
		sc.appendButton(parent, row, info);
	 }
	};

	if (single) {
	 if (parent.getElementsByClassName(sc.btnClass).length === 0) {
		sc.getTrackInfo(window.location.href, row, onGetTackInfo);
	 }
	} else {
	 var a = row.querySelector('a.sound__coverArt[href], a.soundTitle__title[href], a.trackItemWithEdit__trackTitle[href], a.trackItem__trackTitle[href], .chartTrack__title>a[href]');
	 if (a !== null) {
		sc.getTrackInfo(a.href, row, onGetTackInfo);
	 }
	}
		},


		getTrackInfo: function(url, row, cb) {
	url = url.replace(/#.*$/i, '');

	if (url.search(/^\/\/(?:[\w-]+\.)?soundcloud\.com(?:\d+)?\//i) > -1) {
	 url = window.location.protocol + url;
	} else
	if (url.search(/https?:\/\//i) == -1) {
	 if (url.charAt(0) != '/') {
		url = '/' + url;
	 }

	 url = window.location.protocol + '//' + window.location.host + url;
	}

	var cache = sc.tracks;
	var cacheTrack = cache[url];

	if (cacheTrack) {
	 if (cacheTrack.cbList) {
		cacheTrack.cbList.push(cb);
	 } else {
		cb(cacheTrack);
	 }
	} else {
	 cacheTrack = cache[url] = {
		cbList: [cb]
	 };

	 var request = {
		action: 'getSoundcloudTrackInfo',
		trackUrl: url,
		client_id: sc.client_id
	 };

	 return mono.sendMessagePromise(request).then(function(response) {
		cache[url] = sc.setTrackInfo(response);
	 }, function(err) {
		mono.error('getSoundcloudTrackInfo', err);
		cache[url] = null;
	 }).then(function() {
		var cb;
		while (cb = cacheTrack.cbList.shift()) {
			cb(cache[url]);
		}
	 });
	}
		},

		setTrackInfo: function(data) {
	var url = data.trackUrl;
	var info = data.data;
	if (!url || !info) {
	 return;
	}

	var tInfo = {};

	if (info.kind != 'track' && info.tracks && info.tracks.length == 1) {
	 info = info.tracks[0];
	}

	if (info.kind == 'track' && info.stream_url) {
	 sc.setSingleTrackParams(tInfo, info);
	 tInfo.checkLinks = data.checkLinks;
	 return tInfo;
	} else
	if (info.tracks) {
	 var playlist = [];
	 for (var i = 0, len = info.tracks.length; i < len; i++) {
		var t = {};
		sc.setSingleTrackParams(t, info.tracks[i]);
		playlist.push(t);
	 }

	 if (playlist.length > 0) {
		if (info.title) {
			tInfo.title = info.title;
		}

		tInfo.playlist = playlist;
		tInfo.checkLinks = data.checkLinks;
		return tInfo;
	 }
	}
		},


		setSingleTrackParams: function(track, info) {
	var downloadUrl = info.stream_url;
	downloadUrl += (downloadUrl.indexOf('?') == -1) ? '?' : '&';
	downloadUrl += 'client_id=' + sc.client_id;
	track.url = downloadUrl;

	var param = ['id', 'title', 'duration'];
	for (var i = 0; i < param.length; i++) {
	 if (info[param[i]]) {
		track[param[i]] = info[param[i]];
	 }
	}

	if (track.title && info.user && info.user.username) {
	 track.title = info.user.username + ' - ' + track.title;
	}
		},

		onDlBtnClick: function(e) {
	SaveFrom_Utils.downloadOnClick(e);
		},

		appendButton: function(parent, row, info) {
	var track = info;

	// update parent after latency
	if (!document.body.contains(row)) {
	 return;
	} else
	if (!document.body.contains(parent)) {
	 parent = row.querySelector('.soundActions .sc-button-group');
	 if (!parent) {
		return;
	 }
	}

	if (row.dataset.sfSkip > 1) {
	 return;
	}
	row.dataset.sfSkip = '2';

	var btnClass = ['sc-button-small', 'sc-button-medium', 'sc-button-large'];
	for (var i = 0; i < btnClass.length; i++) {
	 if (parent.querySelector('.' + btnClass[i])) {
		btnClass = [btnClass[i]];
	 }
	}

	var a = document.createElement('a');

	var sfId = row.dataset.sfId;
	if (sfId) {
	 a.dataset.sfParentId = row.dataset.sfId;
	}

	a.className = sc.btnClass + ' sc-button sc-button-responsive ' + btnClass[0];
	a.style.position = 'relative';

	var icon = document.createElement('img');

	if (track.playlist) {
	 a.href = '#';
	 a.title = language.playlist;

	 var title = track.title ? mono.fileName.modify(track.title) : 'soundcloud';

	 a.addEventListener('click', function(event) {
		event.preventDefault();
		setTimeout(function() {
			SaveFrom_Utils.playlist.popupPlaylist(track.playlist, title, true);
		}, 100);
	 }, false);

	 if (track.checkLinks === false) {
		icon.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAJElEQVQoz2P4//8/A7mYgWqa6+vr/xPCtNE86udRP9PWz6RiANU4hUYGNDpOAAAAAElFTkSuQmCC';
	 } else {
		icon.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAMUlEQVR42mL8//8/A7mAiYECwILC82Uk7IzN/xmpYjPjqJ9H/UxTP1OkGQAAAP//AwDcahUV6UvyJwAAAABJRU5ErkJggg==';
	 }
	 icon.alt = language.playlist;
	} else
	if (track.checkLinks === false) {
	 a.href = '#';
	 a.title = language.noLinksFound;

	 a.addEventListener('click', function(e) {
		e.preventDefault();
		var style = {
			backgroundColor: '#fff',
			border: '1px solid #777',
			padding: '2px 5px 3px'
		};
		SaveFrom_Utils.showTooltip(this, language.noLinksFound, undefined, style);
	 });

	 icon.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAVklEQVQoz2P4//8/A7mYgSqa0UF9ff1/GEaXG0SagYrmI2vAg+djtZkIA+bjdTYeA+YT5WcsBswnNcDmY9NIlGaoAQnYxHEFGMHQxqe5gRDGqpnuGQMALmDKhkjc9oYAAAAASUVORK5CYII=';
	 icon.alt = 'noLinksFound'
	} else {
	 a.href = track.url;
	 a.title = language.download;

	 if (track.title) {
		a.setAttribute('download',
			mono.fileName.modify(track.title.trim() + '.mp3'));

		a.addEventListener('click', this.onDlBtnClick, false);
	 }

	 icon.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAPklEQVR42mNgGHTgvw/DfxgexJqBiuYja8CD55NrwHxyXTCfWP/OJ0sjFgPmkxvXCWRFDy6MT3MDITw40j8Ak46HYQ4gDfUAAAAASUVORK5CYII=';
	 icon.alt = 'download';
	}

	SaveFrom_Utils.setStyle(icon, {
	 width: '15px',
	 height: '15px',
	 position: 'absolute',
	 top: '50%',
	 left: '50%',
	 margin: '-7px 0 0 -7px'
	});
	a.appendChild(icon);

	parent.appendChild(a);

	icon = null;
	a = null;
	parent = null;
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (sc.mutationMode.observer) {
		sc.mutationMode.observer.stop();
	 }
	},
	enable: function() {
	 var _this = this;
	 if (sc.mutationMode.observer) {
		return sc.mutationMode.observer.start();
	 }

	 var onTrackOver = (function() {
		var lastTimer = null;
		return function() {
			var _this = this;
			clearTimeout(lastTimer);
			lastTimer = setTimeout(function() {
		mono.off(_this, 'mouseenter', onTrackOver);
		if (moduleState) {
		 sc.handleRow.call(sc, _this);
		}
			}, 250);
		};
	 })();

	 sc.mutationMode.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;
			for (i = 0; i < 6; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';
		 node.dataset.sfId = '' + sc.nodeId++;

		 mono.on(node, 'mouseenter', onTrackOver);
		}
			}
			for (i = 6; i < 8; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 var _node = null;
		 if (i === 6) {
			_node = mono.getParentByClass(node, 'listenEngagement');
		 } else {
			_node = mono.getParentByClass(node, 'visualSound');
		 }

		 if (_node.dataset.sfSingle > 0) {
			continue;
		 }
		 _node.dataset.sfSkip = '1';

		 if (_node.classList.contains('streamContext')) {
			return;
		 }

		 _node.dataset.sfSingle = '1';

		 sc.handleRow(_node, 1);
		}
			}

			summary = summaryList[8];
			for (n = 0; node = summary.removed[n]; n++) {
		node = document.querySelector('[' + mono.dataAttr2Selector('sfId') + '="' + node.dataset.sfParentId + '"]');

		if (!node) {
		 return;
		}

		if (node.dataset.sfSingle) {
		 return;
		}

		node.dataset.sfSkip = '1';

		if (document.body.contains(node)) {
		 sc.handleRow(node);
		}
			}
		},
		queries: [{
		css: 'li.trackList__item',
		is: 'added'
			},
			{
		css: 'li.compactTrackList__item',
		is: 'added'
			},
			{
		css: 'li.searchList__item',
		is: 'added'
			},
			{
		css: 'li.soundList__item',
		is: 'added'
			},
			{
		css: 'li.soundBadgeList__item',
		is: 'added'
			},
			{
		css: 'li.chartTracks__item',
		is: 'added'
			},
			{
		css: 'div.listenEngagement .soundActions .sc-button-group',
		is: 'added'
			},
			{
		css: 'div.visualSound .soundActions .sc-button-group',
		is: 'added'
			},
			{
		css: '.savefrom-helper--btn',
		is: 'removed'
			}
		],
		filterTarget: [{
		css: 'div.waveform__layer'
			},
			{
		css: 'a.commentPopover__username'
			},
			{
		css: '.commentPopover'
			},
			{
		css: 'div.playbackTimeline__timePassed'
			},
			{
		css: 'div.commentsList'
			},
			{
		css: 'div.commentsList__item'
			},
			{
		css: 'div.commentsList__body'
			}
		]
	 });
	}
		}
	};

 }, null, function syncIsActive() {
	"use strict";

	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.soundcloud.com/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('sovetnik', function(moduleName, initData) {
	"use strict";
	var preference = initData.getPreference;

	var onScriptStart = function() {
		var limit = 30;
		var slice = [].slice;
		var interval = setInterval(function() {
	var id = null;

	var found = slice.call(document.querySelectorAll('body style+div[id^=m]>div[class]:not([id])>img[src]')).some(function(node) {
	 if (!/^data:image\/png;/.test(node.src)) {
		return;
	 }

	 var parent = node.parentNode && node.parentNode.parentNode;
	 if (!parent || !/^m/.test(parent.id)) {
		return;
	 }

	 var btns = parent.querySelector('div>a[title][id^=m]~a[title][id^=m]~a[title][id^=m]');
	 if (btns) {
		id = '#' + parent.id;
		return true;
	 }
	});

	limit--;
	if (limit < 0 || found) {
	 clearInterval(interval);
	}

	found && addListener(id);
		}, 1000);

		var addListener = function(id) {
	var container = document.querySelector(id);
	if (!container) {
	 return;
	}

	mono.sendMessage({
	 action: 'track',
	 t: 'event',
	 ec: 'sovetnik',
	 ea: 'show',
	 el: 'bar',
	 tid: 'UA-67738130-3'
	});

	container.addEventListener('mousedown', function(e) {
	 if (e.button !== 0) {
		return;
	 }

	 var link = e.target;
	 while (link) {
		if (link.tagName === 'A') {
			break;
		}
		if (link.tagName === 'TR' && link.dataset.typeShop) {
			break;
		}

		link = link.parentNode;
		if (link && link === container) {
			link = null;
		}
	 }

	 if (!link) {
		return;
	 }

	 var type = null;
	 if (link.tagName === 'TR') {
		type = link.dataset.typeShop;
	 }
	 if (link.tagName === 'A') {
		var prev = link.previousElementSibling;
		var next = link.nextElementSibling;
		if (!next || next.tagName !== 'A' || !next.querySelector('span')) {
			next = null;
		}
		if (next && next.nextElementSibling) {
			next = null;
		}
		var parent = link.parentNode;
		if (parent && parent.parentNode !== container) {
			parent = null;
		}
		if (parent && parent.childElementCount !== 2) {
			parent = null;
		}
		if (parent && !prev && next) {
			type = 'Show button';
		}
	 }

	 if (!type) {
		return;
	 }

	 mono.sendMessage({
		action: 'track',
		t: 'event',
		ec: 'sovetnik',
		ea: 'click',
		el: type,
		tid: 'UA-67738130-3',
		ev: 2
	 });
	 mono.sendMessage({
		action: 'track',
		t: 'event',
		ec: 'sovetnik',
		ea: 'click',
		el: type,
		tid: 'UA-7055055-5',
		ev: 2
	 });
	});
		};
	};

	var domain = document.domain;

	if (/^www./.test(domain)) {
		domain = domain.slice(4);
	}

	var settings = {
		affId: 1020,
		clid: 2210496,
		applicationName: 'SaveFrom',
		aviaEnabled: true,
		offerEnabled: true
	};

	var getUrl = function() {
		var filePath = '//dl.metabar.ru/static/js/sovetnik.min.js';
		return filePath +
	'?mbr=true&settings=' + encodeURIComponent(JSON.stringify(settings));
	};

	var url = getUrl();

	var extStorage = {
		get: function(obj, cb) {
	return mono.storage.get(obj, cb);
		},
		set: function(obj, cb) {
	if (preference.sovetnikEnabled === 1) {
	 if (obj.sovetnikRemoved === true) {
		mono.sendMessage({
			action: 'updateOption',
			key: 'sovetnikEnabled',
			value: 0
		});
	 } else
	 if (obj.sovetnikOfferAccepted === false) {
		mono.sendMessage({
			action: 'updateOption',
			key: 'sovetnikEnabled',
			value: 0
		});
	 }
	}
	return mono.storage.set(obj, cb);
		},
		remove: function(obj, cb) {
	return mono.storage.remove(obj, cb);
		}
	};

	var injector = {
		/**
		 * inject script to the page
		 */
		inject: function() {
	if (!mono.isIframe()) {
	 var script = document.createElement('script');
	 script.async = 1;
	 script.src = url;
	 script.setAttribute('charset', 'UTF-8');
	 if (document.body) {
		document.body.appendChild(script);
		onScriptStart();
	 }
	}
		},

		/**
		 * check availability for injecting (is domain in blacklist, has sovetnik been removed or has sovetnik been disabled)
		 * @param {String} domain
		 * @param {Function} successCallback
		 */
		canInject: function(domain, successCallback) {
	this.listenScriptMessages();

	extStorage.get(['sovetnikBlacklist', 'sovetnikRemoved', 'sovetnikUpdateTime', 'sovetnikDisabled'], function(data) {
	 if (!((data.sovetnikBlacklist && data.sovetnikBlacklist[domain]) || data.sovetnikRemoved || data.sovetnikDisabled)) {
		successCallback();
	 }
	 data.sovetnikUpdateTime = data.sovetnikUpdateTime || 0;
	 if (Date.now() - data.sovetnikUpdateTime > 604800000) { // one week
		extStorage.set({
			sovetnikUpdateTime: Date.now(),
			sovetnikRemoved: false,
			sovetnikBlacklist: {}
		});
	 }
	});
		},

		/**
		 * add domain to the blacklist
		 * @param domain
		 */
		addToBlacklist: function(domain) {
	extStorage.get('sovetnikBlacklist', function(data) {
	 data.sovetnikBlacklist = data.sovetnikBlacklist || {};
	 data.sovetnikBlacklist[domain] = true;
	 extStorage.set(data);
	});
		},

		/**
		 *
		 * @param {Boolean} value
		 */
		setSovetnikRemovedState: function(value) {
	if (typeof value === "undefined") {
	 extStorage.remove('sovetnikRemoved');
	} else {
	 extStorage.set({
		sovetnikRemoved: value
	 });
	}
		},

		/**
		 *
		 * @param {Boolean} value
		 */
		setOfferState: function(value) {
	if (typeof value === "undefined") {
	 extStorage.remove('sovetnikOfferAccepted');
	} else {
	 extStorage.set({
		sovetnikOfferAccepted: value
	 });
	}
		},

		/**
		 * window.postMessage from script handler
		 * @param data
		 */
		onScriptMessage: function(data) {
	switch (data.command) {
	 case 'blacklist':
		{
			this.addToBlacklist(data.value);
			break;
		}
	 case 'removed':
		{
			this.setSovetnikRemovedState(data.value);
			break;
		}
	 case 'offerAccepted':
		this.setOfferState(data.value);
		break;
	}
		},

		/**
		 * window.postMessage handler
		 */
		listenScriptMessages: function() {
	window.addEventListener('message', function(message) {
	 if (message && message.data && message.data.type === 'MBR_ENVIRONMENT') {
		this.onScriptMessage(message.data);
	 }
	}.bind(this), false);
		}
	};

	injector.canInject(domain, injector.inject.bind(injector));
 }, function isActive(initData) {
	"use strict";
	if (mono.global.exAviaBar) {
		return false;
	}

	var preference = initData.getPreference;

	if (!preference.hasSovetnik) {
		return false;
	}

	if (!preference.sovetnikEnabled) {
		return false;
	}

	if (mono.global.ddblAdv) {
		return false;
	}
	mono.global.ddblAdv = true;

	return true;
 }, function syncIsAvailable() {
	"use strict";
	if (!document.domain) {
		return false;
	}

	if (mono.isIframe()) {
		return false;
	}

	if (mono.isSafari || mono.isGM) {
		var excludeList = [
	'ftp://*',
	'file://*',
	'*://*.google.*/*',
	'*://*.acidtests.org/*',

	'*://*.savefrom.net/*',
	'*://*.youtube.com/*',
	'*://*.vimeo.com/*',
	'*://*.dailymotion.*/*',
	'*://*.vk.com/*',
	'*://*.vkontakte.ru/*',
	'*://*.odnoklassniki.ru/*',
	'*://*.ok.ru/*',
	'*://my.mail.ru/*',
	'*://*.soundcloud.com/*',
	'*://*.facebook.com/*',
	'*://*.instagram.com/*',
	'*://*.rutube.ru/*'
		];

		var reStr = excludeList.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	var inBlackList = function() {
		var list = [
	"vk.com",
	"youtube.com",
	"odnoklassniki.ru",
	"ok.ru",
	"privet.ru",
	"facebook.com",
	"news.sportbox.ru",
	"play.google.com",
	"roem.ru",
	"linkedin.com",
	"ex.ua",
	"instagram.com",
	"rutube.ru",
	"e.mail.ru",
	"fotki.yandex.ru",
	"support.kaspersky.ru",
	"vimeo.com",
	"club.foto.ru",
	"garant.ru",
	"webmaster.yandex.ru",
	"support.kaspersky.ru",
	"fotki.yandex.ru",
	"mk.ru",
	"metrika.yandex.ru",
	"images.yandex.ru",
	"disk.yandex.ru",
	"maps.yandex.ru",
	"help.yandex.ru",
	"www.yaplakal.com",
	"www.facebook.com",
	"my.mail.ru",
	"aviasales.ru"
		];

		return mono.matchHost(location.hostname, list);
	};

	if (inBlackList()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('aviaBar', function(moduleName, initData) {
	"use strict";

	(function() {
		"use strict";
		var browser = {
	sendMessage: function(msg, cb) {
	 return mono.sendMessage(msg, cb);
	}
		};

		var initMessaging = function() {
	var origin = location.origin || location.protocol + "//" + location.hostname;
	var api = {
	 sendMessage: function(message) {
		window.postMessage(message, origin);
	 },
	 onMessage: function(cb) {
		window.addEventListener('message', function(e) {
			if (e.origin === origin) {
		cb(e.data);
			}
		}, true);
	 }
	};

	var bridgeMsgTools = {
	 idPrefix: Math.floor(Math.random() * 1000),
	 listenerArr: [],
	 mkResponse: function(cbId) {
		var _this = this;
		var fn = cbId && function(msg) {
			var message = {
		tbr: true,
		idPrefix: _this.idPrefix,
		data: msg,
		responseId: cbId
			};
			return api.sendMessage(message);
		};
		return function(msg) {
			if (fn) {
		fn(msg);
		fn = null;
			}
		};
	 },
	 listener: function(message) {
		var _this = bridgeMsgTools;
		if (message && message.tbr && message.idPrefix !== _this.idPrefix && !message.responseId) {
			var respFn = _this.mkResponse(message.callbackId);
			_this.listenerArr.forEach(function(fn) {
		return fn(message.data, respFn);
			});
		}
	 },
	 onMessage: function(cb) {
		this.listenerArr.push(cb);
		if (this.listenerArr.length === 1) {
			api.onMessage(this.listener);
		}
	 }
	};

	bridgeMsgTools.onMessage(function(msg, response) {
	 if (msg && msg.action) {
		browser.sendMessage(msg, response);
	 }
	});
		};

		var canInject = function() {
	if (document.body.parentNode.dataset.travelBar) {
	 return false;
	}

	if (document.defaultView.self !== document.defaultView.top) {
	 return false;
	}

	return true;
		};

		var insertScript = function() {
	var script = document.createElement('script');
	script.src = 'https://travelbar.tools/static/travelBar.lite.min.js#tbr=true';

	var _script = document.querySelector('script');
	if (_script) {
	 initMessaging();
	 _script.parentNode.insertBefore(script, _script);
	}
		};

		if (canInject()) {
	insertScript();
		}
	})();
 }, function isAvailable(initData) {
	"use strict";
	var preference = initData.getPreference;

	if (!preference.hasAviaBar) {
		return false;
	}

	if (!preference.aviaBarEnabled) {
		return false;
	}

	return true;
 }, function syncIsAvailable() {
	"use strict";
	if (mono.isIframe()) {
		return false;
	}

	var list = [
		'*://*.ozon.travel/*',
		'*://*.onetwotrip.com/*',

		'*://*.skyscanner.*/*',

		'*://*.aeroflot.ru/*',
		'*://*.momondo.*/*',
		'*://*.anywayanyday.com/*',
		'*://*.svyaznoy.travel/*',
		'*://avia.tickets.ru/*',
		'*://*.s7.ru/*',
		'*://*.kupibilet.ru/*',
		'*://*.trip.ru/*',
		'*://*.sindbad.ru/*',
		'*://*.aviakassa.ru/*',
		'*://*.aviakassa.com/*',
		'*://*.biletix.ru/*',
		'*://*.utair.ru/*',

		'*://*.kayak.*/*',
		'*://*.orbitz.com/*',
		'*://*.travelocity.com/*',
		'*://*.travelocity.ca/*',
		'*://*.expedia.*/*',
		'*://*.expedia-cn.com/*',
		'*://*.priceline.com/*',

		'*://booking.airasia.com/*',
		'*://*.ryanair.com/*',

		'*://*.booking.*/*',
		'*://*.agoda.*/*',
		'*://*.hotels.com/*',
		'*://*.ostrovok.ru/*',
		'*://*.travel.ru/*',
		'*://*.oktogo.ru/*',
		'*://*.roomguru.ru/*',
		'*://*.tripadvisor.ru/*',
		'*://*.hilton.ru/*',
		'*://*.hilton.com/*',
		'*://*.marriott.com/*',
		'*://*.hostelworld.com/*',
		'*://*.tiket.com/*',
		'*://*.hotelsclick.com/*',
		'*://*.hotelscombined.com/*',

		'*://*.avis.*/*',
		'*://*.budget.com/*',
		'*://*.wizzair.com/*',
		'*://*.emirates.com/*',
		'*://*.delta.com/*',
		"*://*.hertz.com/*",
		"*://*.europcar.com/*"
	];

	for (var i = 0, item; item = list[i]; i++) {
		var re = new RegExp(mono.urlPatternToStrRe(item));
		if (re.test(location.protocol + '//' + location.hostname)) {
	mono.global.aviaBarProfile = item;
	mono.global.exAviaBar = true;
	return true;
		}
	}

	return false;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('vimeo', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleVimeo ? 1 : 0;
	var iframe = mono.isIframe();

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return vimeo.changeState(message.state);
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	vimeo.run();
		});
	}

	var vimeo = {
		panelId: 'savefrom__vimeo_links',
		btnBox: null,
		clipId: null,
		timer: null,
		btnPrefix: 'sd_ld_bnt_',
		popupIsShow: false,
		dlBtnClassName: 'sf-dl-btn',
		currentMenu: null,
		linkCache: {},

		run: function() {
	moduleState = 1;
	if (iframe) {
	 vimeo.clipId = vimeo.getFrameClipId();
	 if (vimeo.clipId) {
		return vimeo.appendIframeButtons();
	 } else {
		iframe = false;
	 }
	}

	this.videoFeed.injectStyle();

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 this.mutationMode.enable();
	}
		},

		changeState: function(state) {
	if (iframe) {
	 return;
	}

	moduleState = state;

	vimeo.videoFeed.disable();

	vimeo.rmAllBtn();

	vimeo.mutationMode.stop();

	if (state) {
	 vimeo.run();
	}
		},

		hideMenu: function() {
	if (vimeo.currentMenu) {
	 vimeo.currentMenu.hide();
	 vimeo.currentMenu = null;
	}
		},

		getFrameClipId: function() {
	var frameClipId = document.location.href.match(/player\.vimeo\.com\/video\/([\w\-]+)/i);
	frameClipId = frameClipId && frameClipId[1];
	if (frameClipId) {
	 return frameClipId;
	}
		},

		getBrowserVideoData: function(container, id) {
	var btnParent = container.querySelector('.uploaded_on');
	if (!btnParent) {
	 btnParent = container.querySelector('#info .meta .stats');
	}
	if (!btnParent) {
	 return null;
	}
	if (id) {
	 id = id.match(/([0-9]+)$/);
	 id = id && id[1];
	}
	if (!id) {
	 var firstLink = container.querySelector('a.js-title') || container.querySelector('a');
	 if (!firstLink) {
		return;
	 }
	 var url = firstLink.getAttribute('href');
	 if (!url) {
		return;
	 }
	 id = url.match(/\/([0-9]+)$/);
	 id = id && id[1];
	}
	if (!id) {
	 return;
	}
	return {
	 id: id,
	 parent: btnParent,
	 style: 1
	};
		},

		getVideoId: function(container) {
	container = container || document;
	var id = null;
	var player;

	player = container.querySelector('.player[data-clip-id]');
	if (player) {
	 return player.dataset.clipId;
	}

	player = container.querySelector('.player[data-fallback-url]');
	if (player) {
	 var fallbackUrl = player.dataset.fallbackUrl || '';
	 fallbackUrl = fallbackUrl.match(/video\/([0-9]+)\//);
	 if (fallbackUrl) {
		return fallbackUrl[1];
	 }
	}

	player = container.querySelector('div.player_wrapper > div.faux_player[data-clip_id]');
	if (player) {
	 id = player.dataset.clip_id;
	 if (id) {
		return id;
	 }
	}
		},

		onBtnClick: function(videoData, e) {
	e.stopPropagation();
	e.preventDefault();
	var id = videoData.id;

	if (!id) {
	 var container = null;
	 if (videoData.playerContainer) {
		container = mono.getParent(videoData.parent, videoData.playerContainer);
	 }
	 id = vimeo.getVideoId(container);
	}

	if (vimeo.currentMenu && vimeo.currentMenu.isShow) {
	 vimeo.hideMenu();
	 return;
	}

	var links = language.download + ' ...';
	var details = {};
	if (videoData.style === 4) {
	 details.offsetTop = 20;
	}

	var isMobile = this.dataset.sfMobile > 0;

	var menu;
	if (!isMobile) {
	 menu = vimeo.currentMenu = SaveFrom_Utils.popupMenu.quickInsert(this, links, 'sf-popupMenu', details)
	} else {
	 menu = vimeo.currentMenu = SaveFrom_Utils.mobileLightBox.show(links)
	}

	var onResponse = function(response) {
	 if (response && response.links) {
		vimeo.linkCache[id] = response;
		var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.vimeo(response.links, response.title);
		menu.update(menuLinks);
	 } else {
		menu.update(language.noLinksFound);
	 }
	};

	var fromCache = vimeo.linkCache[id];
	if (fromCache) {
	 onResponse(fromCache);
	} else {
	 mono.sendMessagePromise({
		action: 'getVimeoLinks',
		extVideoId: id,
		url: location.href
	 }).catch(function(err) {
		mono.error(err);
	 }).then(onResponse);
	}
		},

		rmAllBtn: function() {
	['sfSkip'].forEach(function(attr) {
	 var dataAttr = mono.dataAttr2Selector(attr);
	 var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
	 for (var i = 0, item; item = dataAttrList[i]; i++) {
		item.removeAttribute(dataAttr);
	 }
	});

	var btnList = document.querySelectorAll('.' + vimeo.dlBtnClassName);
	for (var i = 0, item; item = btnList[i]; i++) {
	 if (item.dataset.sfType === '1' || item.dataset.sfType === '3') {
		item = item.parentNode;
	 }
	 item.parentNode.removeChild(item);
	}
	vimeo.videoFeed.rmBtn();

	vimeo.hideMenu();
		},

		appendBtn: function(videoData) {
	var box = videoData.parent;

	var exBtn = box.querySelector('.' + vimeo.dlBtnClassName);
	if (exBtn) {
	 if (!exBtn.dataset.sfId && videoData.style !== 6) {
		return;
	 }
	 exBtn.parentNode.removeChild(exBtn);
	 exBtn = null;
	}

	var btn;
	if (videoData.style === 1) {
	 btn = mono.create('a', {
		text: language.download,
		class: [vimeo.dlBtnClassName, 'sf-style-1'],
		style: {
			display: 'inline'
		},
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		},
		href: '#' + videoData.id
	 });
	} else
	if (videoData.style === 2) {
	 btn = mono.create('button', {
		text: language.download,
		class: [vimeo.dlBtnClassName, 'btn', 'iconify_down_b'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		}
	 });
	} else
	if (videoData.style === 5) {
	 btn = mono.create('button', {
		class: [vimeo.dlBtnClassName, 'sf-type-5'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		},
		append: [
			mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
		style: {
		 display: 'inline-block',
		 width: '16px',
		 height: '16px',
		 verticalAlign: 'text-bottom',
		 marginRight: '.625rem'
		}
			}),
			mono.create('span', {
		style: {
		 marginLeft: 0
		},
		text: language.download
			})
		]
	 });
	} else
	if (videoData.style === 7) {
	 btn = mono.create('button', {
		class: [vimeo.dlBtnClassName, 'sf-type-7'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		},
		append: [
			mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
		style: {
		 display: 'inline-block',
		 width: '16px',
		 height: '16px',
		 verticalAlign: 'text-bottom',
		 marginRight: '.625rem'
		}
			}),
			mono.create('span', {
		style: {
		 marginLeft: 0
		},
		text: language.download
			})
		]
	 });
	} else
	if (videoData.style === 3) {
	 btn = mono.create('button', {
		class: [vimeo.dlBtnClassName, 'iris_btn', 'iris_btn-switch'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		},
		append: [
			mono.create(SaveFrom_Utils.svg.getSvg('download', '#00adef'), {
		style: {
		 display: 'inline-block',
		 width: '16px',
		 height: '16px',
		 verticalAlign: 'text-bottom',
		 marginRight: '.625rem'
		}
			}),
			mono.create('span', {
		class: 'iris_btn-content',
		style: {
		 marginLeft: 0
		},
		text: language.download
			})
		]
	 });
	} else
	if (videoData.style === 4) {
	 btn = mono.create('i', {
		class: [vimeo.dlBtnClassName, 'sf-style-4'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style
		},
		style: {
			display: 'inline-block',
			border: '1px solid #F8F8F8',
			width: '20px',
			height: '20px',
			lineHeight: 0,
			cursor: 'pointer',
			marginLeft: '10px',
			verticalAlign: 'middle'
		},
		append: mono.create('style', {
			text: mono.style2Text([{
		selector: '.' + vimeo.dlBtnClassName + '.sf-style-4',
		style: {
		 background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#777777') + ') center no-repeat #F8F8F8',
		 backgroundSize: '12px'
		}
			}, {
		selector: '.' + vimeo.dlBtnClassName + '.sf-style-4:hover',
		style: {
		 background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #F8F8F8',
		 backgroundSize: '12px'
		}
			}, {
		selector: '.' + vimeo.dlBtnClassName + '.sf-style-4:active',
		style: {
		 outline: 0,
		 boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
		}
			}])
		})
	 });

	 if (mono.isOpera) {
		btn.style.background = '#F8F8F8';
		btn.appendChild(mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('download', '#777777'),
			style: {
		width: '12px',
		height: '12px',
		margin: '4px',
		backgroundColor: '#F8F8F8'
			}
		}));
	 }
	} else
	if (videoData.style === 6) {
	 btn = mono.create('button', {
		class: [vimeo.dlBtnClassName, 'btn', 'btn_sm', 'btn_blue_o'],
		data: {
			sfId: videoData.id,
			sfType: videoData.style,
			sfMobile: 1
		},
		style: {
			marginLeft: '8px'
		},
		append: [
			mono.create(SaveFrom_Utils.svg.getSvg('download', '#00adef'), {
		style: {
		 display: 'inline-block',
		 width: '12px',
		 height: '12px',
		 verticalAlign: 'text-bottom',
		 marginRight: '4px'
		}
			}),
			mono.create('span', {
		class: 'btn_text',
		style: {
		 marginLeft: 0
		},
		text: language.download
			})
		]
	 });
	}

	btn.addEventListener('click', vimeo.onBtnClick.bind(btn, videoData));

	if (videoData.style === 1) {
	 btn = mono.create('span', {
		append: [
			btn,
			' | '
		]
	 });
	}

	if (videoData.style === 3) {
	 btn = mono.create('div', {
		class: 'clip_info-user_actions',
		append: [
			btn
		]
	 });
	}

	if (videoData.style === 1 || videoData.style === 2 || videoData.style === 6) {
	 var firstChild = box.firstChild;
	 if (firstChild) {
		box.insertBefore(btn, firstChild);
	 } else {
		box.appendChild(btn);
	 }
	} else {
	 box.appendChild(btn);
	}
		},

		playerStateChangeObserver: null,
		observeVideoUi: function(btnObj, node) {
	var playerMenu = node;
	if (playerMenu) {
	 var hideBtnTimer = null;
	 var controlsRe = /(\s|^)with-controls(\s|$)/;
	 if (this.playerStateChangeObserver) {
		this.playerStateChangeObserver.stop();
	 }
	 this.playerStateChangeObserver = SaveFrom_Utils.mutationAttrWatcher.run({
		attr: 'class',
		target: playerMenu,
		callback: function(summaryList) {
			var summary = summaryList[0];
			var showBefore = !controlsRe.test(summary.oldValue);
			var showAfret = !controlsRe.test(summary.value);
			if (!showBefore && showAfret) {
		clearTimeout(hideBtnTimer);
		hideBtnTimer = setTimeout(function() {
		 if (!btnObj.lockHide) {
			btnObj.container.classList.add('sf-hide-ui');
		 }
		}, 100);
			} else
			if (showBefore && !showAfret) {
		clearTimeout(hideBtnTimer);
		btnObj.container.classList.remove('sf-hide-ui');
			}
		}
	 });
	}
		},

		appendIframeButtons: function() {
	var _this = this;
	var btnObj = SaveFrom_Utils.frameMenu.getBtn({
	 quickBtnStyleObj: {
		display: 'inline-block',
		border: 0,
		borderRadius: '.3em',
		cursor: 'pointer',
		position: 'relative',
		padding: '6px 8px'
	 },
	 quickBtnCssStyle: {
		backgroundColor: 'rgba(23,35,34,.75)'
	 },
	 quickBtnOverCssStyle: {
		backgroundColor: 'rgb(0, 173, 239)'
	 },
	 nodeCssStyle: {
		display: 'none'
	 },
	 singleBtn: true,
	 btnId: _this.panelId,
	 containerStyle: {
		left: '10px',
		top: '10px'
	 },
	 quickBtnIcon: mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
		style: {
			display: 'inline-block',
			width: '16px',
			height: '16px',
			verticalAlign: 'middle'
		}
	 }),
	 on: [
		['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			if (_this.currentMenu && _this.currentMenu.isShow) {
		_this.hideMenu();
		return;
			}

			var id = _this.clipId;
			var fromCache = _this.linkCache[id];
			var links = language.download + ' ...';
			if (fromCache) {
		links = SaveFrom_Utils.popupMenu.prepareLinks.vimeo(fromCache.links, fromCache.title);
			}

			var menu = _this.currentMenu = SaveFrom_Utils.frameMenu.getMenu(this, links, 'sf-frame-menu', {
		leftMenuPos: true,
		container: btnObj.container,
		onShow: function() {
		 btnObj.node.classList.add('sf-over');
		},
		onHide: function() {
		 _this.currentMenu = null;
		 btnObj.node.classList.remove('sf-over');
		}
			});

			if (!fromCache) {
		_this.getLinksFromPage(function(links, title) {
		 var menuLinks = language.noLinksFound;
		 if (links) {
			_this.linkCache[id] = {
				links: links,
				title: title
			};
			menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.vimeo(links, title);
		 }
		 menu.update(menuLinks);
		});
			}
		}],
		['mousedown', function(e) {
			e.stopPropagation();
			if (e.button === 2) {
		if (observer) {
		 observer.stop();
		 observer = null;
		}

		_this.hideMenu();

		if (btnObj.container.parentNode) {
		 btnObj.container.parentNode.removeChild(btnObj.container);
		}
			}
		}]
	 ]
	});

	btnObj.quickBtn.title = language.download;

	btnObj.container = mono.create('div', {
	 class: 'sf-btn-ctr',
	 append: btnObj.node
	});

	mono.on(btnObj.container, 'mouseenter', function() {
	 btnObj.lockHide = true;
	});

	mono.on(btnObj.container, 'mouseleave', function() {
	 btnObj.lockHide = false;
	});

	btnObj.node.appendChild(mono.create('style', {
	 text: mono.style2Text([{
		selector: [
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) #' + _this.panelId,
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) .sf-frame-menu'
		],
		style: {
			display: 'block'
		}
	 }])
	}));

	document.body.appendChild(btnObj.container);


	var observer = SaveFrom_Utils.mutationWatcher.run({
	 callback: function(summaryList) {
		var summary = summaryList[0];
		var node = summary.added[0];
		if (node) {
			_this.observeVideoUi(btnObj, node);
			setTimeout(function() {
		observer.stop();
		observer = null;
			}, 0);
		}
	 },
	 queries: [{
		css: '#player .captions[aria-live="assertive"]',
		is: 'added'
	 }]
	});
		},

		getLinksFromPage: function(cb) {
	var reList = [
	 /"video":{/,
	 /"request":{/,
	 /"files":/
	];
	var scriptList = mono.getPageScript(document.body.innerHTML, reList);

	var config = null;
	scriptList.some(function(script) {
	 var jsonList = mono.findJson(script, reList);
	 return jsonList.some(function(json) {
		if (json.video && json.request && json.request.files) {
			config = json;
			return true;
		}
	 });
	});
	var request = null;
	var links = null;
	var title = null;

	var onResponse = function(response) {
	 if (response) {
		links = response.links || null;
		title = response.title || null;
	 }

	 return cb(links, title);
	};

	if (config) {
	 request = {
		action: 'getVimeoLinksFromConfig',
		config: config
	 };

	 return mono.sendMessage(request, onResponse);
	} else {
	 request = {
		action: 'getVimeoLinks',
		extVideoId: vimeo.clipId
	 };

	 return mono.sendMessage(request, onResponse);
	}
		},

		videoFeed: {
	btnClassName: 'sf-feed-dl-btn',
	style: null,
	onClick: function(e) {
	 e.preventDefault();
	 e.stopPropagation();

	 var id = this.dataset.sfId;
	 var isCouchMode = this.dataset.sfCouchMode > 0;

	 if (vimeo.currentMenu && vimeo.currentMenu.isShow) {
		vimeo.hideMenu();
		return;
	 }

	 var fromCache = vimeo.linkCache[id];
	 var links = language.download + ' ...';
	 if (fromCache) {
		links = SaveFrom_Utils.popupMenu.prepareLinks.vimeo(fromCache.links, fromCache.title);
	 }

	 var menu = vimeo.currentMenu = SaveFrom_Utils.popupMenu.quickInsert(this, links, 'sf-popupMenu');

	 if (fromCache) {
		return;
	 }

	 var url = null;
	 if (/"url"/.test(id)) {
		url = JSON.parse(id).url;
		id = null;
	 }

	 mono.sendMessage({
		action: 'getVimeoLinks',
		extVideoId: id,
		url: url
	 }, function(response) {
		var menuLinks = null;
		if (response.links) {
			vimeo.linkCache[id] = response;
			menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.vimeo(response.links, response.title);
		} else {
			menuLinks = language.noLinksFound;
		}
		menu.update(menuLinks);
	 });
	},
	getBtn: function(details) {
	 var btn = mono.create('i', {
		class: details.classList,
		data: {
			sfId: details.id,
			sfCouchMode: details.isCouchMode ? 1 : 0
		},
		on: ['click', this.onClick]
	 });

	 if (mono.isOpera) {
		btn.style.background = '#F8F8F8';
		btn.appendChild(mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('download', '#777777'),
			style: {
		width: '12px',
		height: '12px',
		margin: '4px',
		backgroundColor: '#F8F8F8'
			}
		}));
	 }

	 return btn;
	},
	onImgOver2: function(e) {
	 var link = this.parentNode;

	 var parent;
	 var id;

	 if (link.tagName !== 'A') {
		return;
	 }

	 var href = link.getAttribute('href');
	 if (!href) {
		return;
	 }

	 id = href.match(/^\/(\d+)$/);
	 id = id && id[1];

	 if (!id) {
		return;
	 }

	 parent = link.parentNode;
	 if (!parent || !parent.classList.contains('contextclip-img')) {
		return;
	 }

	 if (parent.dataset.sfBtn > 0) {
		return;
	 }
	 parent.dataset.sfBtn = '1';

	 var _this = vimeo.videoFeed;

	 var classList = [_this.btnClassName, 'sf-type1-btn'];

	 link.appendChild(vimeo.videoFeed.getBtn({
		id: id,
		classList: classList
	 }));

	 link = null;
	 parent = null;
	},
	onImgOver: function(e) {
	 var link = this.parentNode;

	 var parent;
	 var id;

	 if (mono.matches(this, 'a.contextclip-img-thumb')) {
		parent = this;
		link = this;
		var m = /\/([0-9]+)/.exec(this.href);
		if (m) {
			id = m[1];
		}
	 }

	 if (!id) {
		if (mono.matches(this, 'div.iris_video-vital') || mono.matches(this, 'li.clip_thumbnail')) {
			link = this.querySelector('.iris_thumbnail');
			parent = this;
			var linkNode = this.querySelector('a.iris_link-box');
			if (linkNode) {
		var url = linkNode.href;
		id = url.match(/\/([0-9]+)/);
		id = id && id[1];

		if (!id && url) {
		 id = JSON.stringify({
			'url': url
		 });
		}
			}
		}
	 }

	 if (!id && link.tagName == 'LI') {
		id = link.dataset.resultId;
		if (id && id.substr(0, 5) === 'clip_') {
			id = id.substr(5);
			parent = link;
			link = this.querySelector('.thumbnail_wrapper');
		} else {
			return;
		}
	 }

	 if (!id) {
		if (link.tagName !== 'A') {
			return;
		}
		id = link.dataset.clipId;

		parent = link.parentNode;
		if (!parent) {
			return;
		}
	 }

	 var isCouchMode = false;
	 if (!id) {
		id = parent.id;
		isCouchMode = id.substr(0, 7) === 'item_id' && parent.classList.contains('clip');
		if (!isCouchMode && id.substr(0, 4) !== 'clip') {
			id = undefined;
		}
		if (!id && parent.tagName === 'ARTICLE' && parent.classList.contains('clip_item')) {
			id = link.getAttribute('href');
		}
		if (!id) {
			return;
		}

		id = id.match(/([0-9]+)$/);
		if (id) {
			id = id[1];
		}
	 }

	 var hasBtn = parent.dataset.sfBtn;
	 if (hasBtn) {
		return;
	 }
	 parent.dataset.sfBtn = '1';

	 var _this = vimeo.videoFeed;

	 var classList = [_this.btnClassName];
	 if (this.classList.contains('thumbnail_lg_wide')) {
		classList.push('sf-type1-btn');
	 }

	 if (this.classList.contains('contextclip-img-thumb')) {
		classList.push('sf-type4-btn');
	 }

	 if (this.classList.contains('clip_thumbnail') || this.classList.contains('iris_video-vital')) {
		classList.push('sf-type3-btn');
	 }

	 var ol = parent.parentNode;
	 if (ol && ol.id === 'clips') {
		classList.push('sf-type1-btn');
		// classList.push('sf-type2-btn');
	 }
	 ol = null;

	 if (isCouchMode) {
		classList.push('sf-type1-btn');
	 }

	 if (parent.classList.contains('promo_clip') && classList.length === 1) {
		classList.push('sf-type1-btn');
		// classList.push('sf-type2-btn');
	 }

	 link.appendChild(vimeo.videoFeed.getBtn({
		id: id,
		classList: classList,
		isCouchMode: isCouchMode
	 }));

	 link = null;
	 parent = null;
	},
	injectStyle: function() {
	 if (this.style) {
		!this.style.parentNode && document.head.appendChild(this.style);
		return;
	 }

	 this.style = mono.create('style', {
		text: mono.style2Text([{
			selector: [
		'.sf-dl-btn.sf-type-5',
		'.sf-dl-btn.sf-type-7'
			],
			style: {
		color: '#fff',
		borderColor: '#00adef',
		backgroundColor: '#00adef',
		minWidth: '4.25rem',
		minHeight: '2.1rem',
		padding: '0 .625rem',
		lineHeight: '2.14286',
		fontSize: '.875rem',
		width: 'auto',
		position: 'relative',
		margin: 0,
		fontWeight: 700,
		borderWidth: '.0625rem',
		borderStyle: 'solid',
		borderRadius: '.1875rem',
		letterSpacing: '.1px',
		transition: 'all .1s ease-in-out',
		cursor: 'pointer',
		marginLeft: '.5rem'
			}
		}, {
			selector: [
		'.sf-dl-btn.sf-type-5',
		'.sf-dl-btn.sf-type-7'
			],
			style: {
		color: '#fff',
		borderColor: '#00adef',
		backgroundColor: '#00adef',
		minWidth: '4.25rem',
		minHeight: '2.1rem',
		padding: '0 .625rem',
		lineHeight: '2.14286',
		fontSize: '13px',
		width: 'auto',
		position: 'relative',
		margin: 0,
		fontWeight: 700,
		borderWidth: '.0625rem',
		borderStyle: 'solid',
		borderRadius: '.1875rem',
		letterSpacing: '.1px',
		transition: 'all .1s ease-in-out',
		cursor: 'pointer',
		marginLeft: '.5rem'
			}
		}, {
			selector: [
		'.sf-dl-btn.sf-type-5:hover',
		'.sf-dl-btn.sf-type-7:hover'
			],
			style: {
		color: '#fff',
		borderColor: '#08c',
		backgroundColor: '#08c'
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn',
		'a .sf-feed-dl-btn.sf-type3-btn',
		'a > .sf-feed-dl-btn.sf-type4-btn'
			],
			style: {
		display: 'none',
		border: '1px solid #F8F8F8',
		width: '20px',
		height: '20px',
		padding: 0,
		position: 'absolute',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#777777') + ') center no-repeat #F8F8F8',
		backgroundSize: '12px',
		top: 'auto',
		left: 'auto',
		lineHeight: 0
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn.sf-type4-btn'
			],
			style: {
		top: 0,
		left: 0
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn.sf-type1-btn',
		'a > div > .sf-feed-dl-btn.sf-type3-btn'
			],
			style: {
		top: 0
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn.sf-type2-btn'
			],
			style: {
		opacity: 0.5
			}
		}, {
			selector: [
		'a > div > .sf-feed-dl-btn.sf-type3-btn'
			],
			style: {
		zIndex: 10
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn:hover',
		'a > div > .sf-feed-dl-btn.sf-type3-btn:hover'
			],
			style: {
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #F8F8F8',
		backgroundSize: '12px'
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn.sf-type2-btn:hover'
			],
			style: {
		opacity: 0.8
			}
		}, {
			selector: [
		'a > .sf-feed-dl-btn:active',
		'a > div > .sf-feed-dl-btn.sf-type3-btn:active'
			],
			style: {
		outline: 0,
		boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
			}
		}, {
			selector: [
		'a:hover > .sf-feed-dl-btn',
		'a:hover > div > .sf-feed-dl-btn.sf-type3-btn'
			],
			style: {
		display: 'block'
			}
		}])
	 });

	 document.head.appendChild(this.style);
	},
	disable: function() {
	 if (this.style) {
		this.style.parentNode && this.style.parentNode.removeChild(this.style);
	 }
	},
	rmBtn: function() {
	 var btnList = document.querySelectorAll('.sf-feed-dl-btn');
	 for (var i = 0, item; item = btnList[i]; i++) {
		item.parentNode.removeChild(item);
	 }

	 var dataAttr = mono.dataAttr2Selector('sfBtn');
	 var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
	 for (i = 0, item; item = dataAttrList[i]; i++) {
		item.removeAttribute(dataAttr);
	 }
	}
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }
	},
	wrapOnImgOver: function() {
	 if (!moduleState) {
		return;
	 }
	 vimeo.videoFeed.onImgOver.apply(this, arguments);
	},
	wrapOnImgOver2: function() {
	 if (!moduleState) {
		return;
	 }
	 vimeo.videoFeed.onImgOver2.apply(this, arguments);
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node, styleIndex, videoId;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		vimeo.hideMenu();
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		styleIndex = 2;
		vimeo.appendBtn({
		 id: '',
		 parent: node,
		 style: styleIndex,
		 playerContainer: '#clip'
		});
			}

			summary = summaryList[1];
			for (n = 0; node = summary.added[n]; n++) {
		vimeo.hideMenu();
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var stats = node.querySelector('.stats') || node.querySelector('.time');
		if (!stats) {
		 continue;
		}

		styleIndex = 1;
		vimeo.appendBtn({
		 id: '',
		 parent: stats,
		 style: styleIndex,
		 playerContainer: '#channel_clip_container'
		});
			}

			summary = summaryList[2];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		if (node.id.substr(0, 5) !== 'clip_') {
		 continue;
		}

		var videoData = vimeo.getBrowserVideoData(node, node.id);
		if (!videoData) {
		 continue;
		}

		vimeo.appendBtn(videoData);
			}

			summary = summaryList[3];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		mono.one(node, 'mouseenter', vimeo.mutationMode.wrapOnImgOver);
			}

			summary = summaryList[4];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var parent = mono.getParentByClass(node, 'clip_thumbnail');

		mono.one(parent, 'mouseenter', vimeo.mutationMode.wrapOnImgOver);
			}

			summary = summaryList[5];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var parent = mono.getParentByClass(node, 'iris_video-vital');

		mono.one(parent, 'mouseenter', vimeo.mutationMode.wrapOnImgOver);
			}

			summary = summaryList[6];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var parent = mono.getParentByClass(node, 'contextclip-img-thumb');

		mono.one(parent, 'mouseenter', vimeo.mutationMode.wrapOnImgOver);
			}

			summary = summaryList[7];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		vimeo.hideMenu();

		var wrapper = mono.getParent(node, '.clip_info-wrapper');
		if (!wrapper) {
		 continue;
		}

		var clipInfoActions = wrapper.querySelector('.clip_info-actions');
		if (!clipInfoActions) {
		 continue;
		}

		styleIndex = 3;
		vimeo.appendBtn({
		 id: '',
		 parent: clipInfoActions,
		 style: styleIndex,
		 playerContainer: '.clip_main'
		});
			}

			summary = summaryList[8];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		vimeo.hideMenu();

		var wrapper = mono.getParent(node, '.clip_main-content');
		if (!wrapper) {
		 continue;
		}

		styleIndex = 5;
		vimeo.appendBtn({
		 id: '',
		 parent: node,
		 style: styleIndex,
		 playerContainer: '.clip_main'
		});
			}

			summary = summaryList[9];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		mono.one(node, 'mouseenter', vimeo.mutationMode.wrapOnImgOver2);
			}

			summary = summaryList[10];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		styleIndex = 4;
		vimeo.appendBtn({
		 id: '',
		 parent: node,
		 style: styleIndex,
		 playerContainer: '.clip'
		});
			}

			summary = summaryList[11];
			for (n = 0; node = summary.added[n]; n++) {
		styleIndex = 6;
		vimeo.appendBtn({
		 id: '',
		 parent: node,
		 style: styleIndex,
		 playerContainer: '.clip_wrapper'
		});
			}

			summary = summaryList[12];
			for (n = 0; node = summary.added[n]; n++) {
		styleIndex = 7;

		vimeo.appendBtn({
		 id: '',
		 parent: node,
		 style: styleIndex,
		 playerContainer: '#main'
		});
			}
		},
		queries: [{
		css: '#clip #info #tools',
		is: 'added'
			},
			{
		css: '#channel_clip_container #info .meta',
		is: 'added'
			},
			{
		css: '#browse_content ol.browse_videos_videos > li',
		is: 'added'
			},
			{
		css: 'img.thumbnail',
		is: 'added'
			},
			{
		css: '.clip_thumbnail .iris_thumbnail img',
		is: 'added'
			},
			{
		css: '.iris_video-vital .iris_thumbnail img',
		is: 'added'
			},
			{
		css: '.contextclip-img-thumb img',
		is: 'added'
			},
			{
		css: '.clip_main .clip_info a.js-user_link.iris_link-header',
		is: 'added'
			},
			{
		css: '.clip_main .clip_main-content div.clip_info-subline--inline',
		is: 'added'
			},
			{
		css: '.contextclip-img img',
		is: 'added'
			},
			{
		css: '.client_wrapper .clip header h1',
		is: 'added'
			},
			{
		css: '.clip_page .clip .meta_section_subinfo',
		is: 'added'
			},
			{
		css: '#main .clip_info-subline--inline',
		is: 'added'
			}
		]
	 });
	}
		}
	};

 }, null, function syncIsActive() {
	"use strict";

	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.vimeo.com/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('vk', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleVkontakte ? 1 : 0;
	var allowDownloadMode = mono.isChrome || mono.isFirefox || (mono.isGM && mono.isTM);
	var Promise = mono.Promise;

	var iframe = mono.isIframe();
	var videoExt = false;

	if (iframe) {
		if (/\/video_ext\.php\?.+/.test(location.href)) {
	videoExt = true;
		} else
		if (/\/widget_comments\.php\?.+/.test(location.href)) {
	iframe = false;
		} else {
	return;
		}
	}

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return vk.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	updateLinks();
		}
		if (message.action === 'downloadMP3Files') {
	if (allowDownloadMode) {
	 audio.downloadMP3Files();
	} else {
	 audio.showListOfAudioFiles(false);
	}
		}
		if (message.action === 'downloadPlaylist') {
	audio.showListOfAudioFiles(true);
		}
		if (message.action === 'downloadPhotos') {
	photo.downloadPhoto();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	vk.run();
		});
	}

	var vk = {
		contextMenu: null,
		isMutation: false,
		run: function() {
	moduleState = 1;

	if (/m\.vk\.com/.test(location.hostname)) {
	 return mVk.run();
	}

	if (videoExt) {
	 video.addFrameBtn();
	 return;
	}

	photo.injectStyle();

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 vk.isMutation = true;
	 audio.addCustomStyle();
	 vk.mutationMode.enable();
	}
		},
		changeState: function(state) {
	if (iframe) {
	 return;
	}
	moduleState = state;
	removeDownloadLinks();
	audio.hideLinks();

	videoFeed.off();

	vk.hideMenu();

	photo.rmCurrentPhotoBtn();

	audio.rmBitrate();

	photo.rmPhotoAlbumDlBtn();

	vk.mutationMode.stop();

	if (state) {
	 vk.run();
	}
		},
		hideMenu: function() {
	if (vk.contextMenu) {
	 vk.contextMenu.hide();
	 vk.contextMenu = null;
	}
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	wrapNewAudioOnMouseOver: function() {
	 if (!moduleState) {
		return;
	 }
	 audio.onNewMouseOver.apply(this, arguments);
	},
	wrapVideoFeedOnMouseOver: function() {
	 if (!moduleState) {
		return;
	 }
	 videoFeed.onLinkHover.apply(this, arguments);
	},
	onVideoInsert: function(node) {
	 var layer = SaveFrom_Utils.getParentById(node, 'mv_box');
	 var player = video.getPlayerNode(layer);

	 if (player) {
		video.getLinksFromPlayer(layer, player, video.newAppendButton.bind(video));
	 } else {
		node.dataset.sfSkip = 0;
	 }
	},
	onVideoChange: function(node) {
	 var _this = this;
	 if (!/video_box_wrap-?\d+_-?\d+/.test(node.id)) {
		_this.onVideoInsert(node);
	 } else
	 if (!node.sfWatch) {
		node.sfWatch = true;
		var sfArrtMutationWatcher = SaveFrom_Utils.mutationAttrWatcher.run({
			attr: 'id',
			target: node,
			callback: function() {
		_this.onVideoInsert(node);
			}
		});
		mono.onRemoveEvent(node, function() {
			sfArrtMutationWatcher.stop();
			node.sfWatch = false;
			node.dataset.sfSkip = 0;
		});
	 } else {
		_this.onVideoInsert(node);
	 }
	},
	enable: function() {
	 var _this = this;
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		mono.one(node, 'mouseenter', vk.mutationMode.wrapVideoFeedOnMouseOver);
			}

			summary = summaryList[1];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		_this.onVideoChange(node);
			}

			summary = summaryList[2];
			for (n = 0; node = summary.added[n]; n++) {
		node = node.parentNode;
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		_this.onVideoChange(node);
			}

			summary = summaryList[3];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		photo.addNewPhotoAlbumDlBtn(node);
			}

			summary = summaryList[4];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		photo.addNewDlCurrentPhotoBtn(node);
			}

			for (i = 5; i < 8; i++) {
		summary = summaryList[i];
		for (n = 0; node = summary.added[n]; n++) {
		 if (node.dataset.sfSkip > 0) {
			continue;
		 }
		 node.dataset.sfSkip = '1';

		 mono.one(node, 'mouseenter', vk.mutationMode.wrapNewAudioOnMouseOver);
		}
			}

			summary = summaryList[8];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: '.post_video_desc a.lnk',
		is: 'added'
			},

			{
		css: '#mv_box #mv_player_box > .video_box_wrap',
		is: 'added'
			},
			{
		css: '#mv_box #mv_player_box > .video_box_wrap > #video_player',
		is: 'added'
			},

			{
		css: '#photos_all_block',
		is: 'added'
			},

			{
		css: '.pv_photo_wrap .pv_img_area_wrap',
		is: 'added'
			},

			{
		css: '.audio_row',
		is: 'added'
			},
			{
		css: '.top_audio_player .top_audio_player_title',
		is: 'added'
			},
			{
		css: '.audio_page_player .audio_page_player_title_performer',
		is: 'added'
			},

			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		}
	};

	var downloadLinkClassName = 'savefrom_vk_download';

	var updateLinks = function() {
		vk.changeState(0);
		vk.changeState(1);
	};

	var removeDownloadLinks = function() {
		var selector = 'a.' + downloadLinkClassName +
	',div.' + downloadLinkClassName +
	',span.' + downloadLinkClassName;

		audio.lastRow = null;

		var e = document.querySelectorAll(selector);
		for (var i = e.length - 1; i >= 0; i--) {
	if (audio.elIsHidden(e[i])) {
	 e[i].parentNode.removeChild(e[i]);
	}
		}
	};

	var getFolderName = function() {
		var folderName = document.title;
		var sep = folderName.indexOf('|');
		if (sep !== -1) {
	folderName = folderName.substr(0, sep - 1);
		}

		return mono.fileName.modify(folderName);
	};

	var parseVkResponse = function(data) {
		var intval = function(e) {
	return e === !0 ? 1 : parseInt(e) || 0
		};

		var floatval = function(e) {
	return e === !0 ? 1 : parseFloat(e) || 0
		};

		var e = data.split('<!>');
		for (var o = e.length - 1; o >= 0; --o) {
	var n = e[o];
	if ("<!" == n.substr(0, 2)) {
	 var a = n.indexOf(">"),
		i = n.substr(2, a - 2);
	 switch (n = n.substr(a + 1), i) {
		case "json":
			var json = null;

			try {
		json = JSON.parse(n);
			} catch (e) {}

			e[o] = json;
			break;
		case "int":
			e[o] = intval(n);
			break;
		case "float":
			e[o] = floatval(n);
			break;
		case "bool":
			e[o] = intval(n) ? !0 : !1;
			break;
		case "null":
			e[o] = null;
			break;
		case "pageview_candidate":
			e.pop();
			break;
		case "debug":
			e.pop()
	 }
	}
		}
		return e;
	};

	var rmEmTags = function(str) {
		var re = /<em>.*<\/em>/;
		if (re.test(str)) {
	str = str.replace(/<\/?em>/g, '');
		}
		return str;
	};


	///////////////////////////////////////////////////////////////////
	// AUDIO

	var audio = {
		audioElClassList: ['audio', 'audioRow', 'audioRowWall'],
		lastRow: null,
		className: downloadLinkClassName,
		cache: {},

		lastValidRequest: null,
		waitUntilUnblock: function(details) {
	var _this = this;
	var limit = 10;
	var interval = 15 * 1000;

	if (!_this.lastValidRequest) {
	 return Promise.reject(new Error('Last valid request is empty!'));
	}

	var getData = function() {
	 return new Promise(function(resolve) {
		setTimeout(resolve, interval);
	 }).then(function() {
		if (details.abort) {
			throw new Error('Abort');
		}

		return mono.requestPromise(_this.lastValidRequest).then(function(response) {
			limit--;
			var list = parseVkResponse(response.body)[5];
			if (!list) {
		if (limit > 0) {
		 return getData();
		} else {
		 throw new Error('Can\'t request data');
		}
			}
		});
	 });
	};
	return getData().then(function() {
	 return new Promise(function(resolve) {
		setTimeout(resolve, 250);
	 });
	});
		},

		needUnmask: function(idsArr) {
	var re = /audio_api_unavailable/;
	return idsArr.some(function(item) {
	 if (re.test(item[2])) {
		return true;
	 }
	});
		},

		unmaskUrlViaPlayer: (function() {
	var onReady = null;
	var findDecodeFn = function(modules) {
	 return new Promise(function(resolve) {
		SaveFrom_Utils.bridge({
			args: [modules],
			func: function(modulesStr, cb) {
		try {
		 var modules = new Function([], 'return ' + modulesStr)();
		 modulesStr = null;

		 var unmaskFnId = null;
		 for (var index in modules) {
			var fn = modules[index];
			if (/\.audioUnmaskSource=/.test(fn)) {
				unmaskFnId = index;
				break;
			}
		 }
		 if (unmaskFnId === null) {
			throw new Error('Unmask fn is no found!');
		 }

		 var installed = {};
		 var loadModule = function(id) {
			if (installed[id]) return installed[id].exports;
			var module = installed[id] = {
				exports: {},
				id: id,
				loaded: false
			};
			modules[id].call(module.exports, module, module.exports, loadModule);
			module.loaded = true;
			return module.exports
		 };

		 var result = loadModule(unmaskFnId);
		 if (!result.audioUnmaskSource) {
			throw new Error('Unmask fn is no exists!');
		 }

		 window.sfUnmaskUrl = result.audioUnmaskSource;

		 cb(true);
		} catch (err) {
		 cb(false);
		}
			},
			cb: resolve
		});
	 }).then(function(result) {
		if (!result) {
			throw new Error('Can\'t create unmask function!');
		}
	 });
	};
	var loadPlayerJs = function(url) {
	 return mono.requestPromise({
		url: url,
		localXHR: true
	 }).then(function(respones) {
		var body = respones.body;

		var modules = '';
		var pos = body.indexOf('([function');
		if (pos !== -1) {
			modules = body.substr(pos);
		}

		if (pos === -1) {
			pos = body.indexOf('({0:function');
			if (pos !== -1) {
		modules = body.substr(pos);
			}
		}

		if (!modules) {
			throw new Error('Modules is not found!');
		}

		return modules;
	 });
	};
	var getPlayerUrl = function() {
	 var isMobile = /m\.vk\.com/.test(location.hostname);
	 var scriptReList = null;
	 if (isMobile) {
		scriptReList = [/\/js\/s_c\.js\?\d+/];
	 } else {
		scriptReList = [
			/\/js\/al\/audioplayer\.js\?\d+/,
			/\/cmodules\/web\/audioplayer\.js\?\d+/
		];
	 }
	 var scriptUrl = '';
	 [].slice.call(document.querySelectorAll('script[src]')).some(function(node) {
		var url = node.src;
		return scriptReList.some(function(re) {
			if (re.test(url)) {
		scriptUrl = url;
		return true;
			}
		});
	 });
	 if (!scriptUrl) {
		return Promise.reject(new Error('Player url is not found!'));
	 } else {
		return Promise.resolve(scriptUrl);
	 }
	};
	return function(idsArr) {
	 var needUnmask = audio.needUnmask(idsArr);
	 if (!needUnmask) {
		return Promise.resolve(idsArr);
	 }

	 if (onReady === null) {
		onReady = getPlayerUrl().then(loadPlayerJs).then(findDecodeFn).then(function() {
			onReady = true;
		}).catch(function(err) {
			mono.debug('Unmask via player error!', err);
			onReady = false;
		});
	 }

	 if (typeof onReady === 'boolean') {
		return audio.unmaskUrl(idsArr);
	 } else {
		return onReady.then(function() {
			return audio.unmaskUrl(idsArr);
		});
	 }
	};
		})(),

		unmaskUrl: function(idsArr) {
	var needUnmask = audio.needUnmask(idsArr);
	var result;
	if (!needUnmask) {
	 result = Promise.resolve(idsArr);
	} else {
	 result = new Promise(function(resolve) {
		SaveFrom_Utils.bridge({
			args: [idsArr],
			func: function(idsArr, cb) {
		var aFail = false;
		var bFail = false;
		var unmaskUrl = function(url) {
		 var _url = '';
		 if (!aFail && window.sfUnmaskUrl) {
			try {
				_url = window.sfUnmaskUrl(url);
			} catch (err) {
				aFail = true;
			}
		 }
		 if (!bFail && !_url && window.AudioPlayerFlash) {
			try {
				var r = {};
				window.AudioPlayerFlash.prototype.setUrl.apply(r, [url]);
				_url = r._url;
			} catch (err) {
				bFail = true;
			}
		 }
		 if (typeof _url !== 'string') {
			_url = '';
		 }
		 return _url;
		};
		idsArr.forEach(function(item) {
		 var url = unmaskUrl(item[2]);
		 if (url) {
			item[2] = url;
		 }
		});
		cb(idsArr);
			},
			cb: resolve
		});
	 }).then(function(_idsArr) {
		return _idsArr || idsArr;
	 });
	}
	return result;
		},

		_getNewTrackListByIds: function(ids, details) {
	var _this = this;
	details = details || {};
	var limit = 10;
	var count = 0;
	var trackObj = {};
	var cache = _this.cache;

	var idsClone = ids.filter(function(id) {
	 if (cache[id]) {
		trackObj[id] = cache[id];
		count++;
		return false;
	 } else {
		return true;
	 }
	});

	var partIds = [];
	while (idsClone.length) {
	 partIds.push(idsClone.splice(0, limit));
	}
	var maxCount = ids.length;

	var promise = Promise.resolve();

	partIds.forEach(function(ids) {
	 var requestIds = function() {
		if (details.abort) {
			throw new Error('Abort');
		}

		var requestDetails;
		return mono.requestPromise(requestDetails = {
			type: 'POST',
			headers: {
		'Content-Type': 'application/x-www-form-urlencoded',
		'X-Requested-With': 'XMLHttpRequest'
			},
			data: mono.param({
		act: 'reload_audio',
		al: 1,
		ids: ids.join(',')
			}),
			url: '/al_audio.php',
			localXHR: true
		}).then(function(response) {
			var list = parseVkResponse(response.body)[5];
			if (!list) {
		throw new Error('Track list is not found!');
			} else {
		_this.lastValidRequest = requestDetails;

		list.forEach(function(track) {
		 var id = track[1] + '_' + track[0];
		 cache[id] = track;
		 trackObj[id] = track;
		 count++;
		});

		if (details.onProgress) {
		 details.onProgress(count, maxCount);
		}
			}

			return new Promise(function(resolve) {
		setTimeout(resolve, 250);
			});
		});
	 };

	 promise = promise.then(function() {
		return requestIds().catch(function(e) {
			if (e.message !== 'Track list is not found!') {
		throw e;
			}

			if (!details.withoutUnblock) {
		return _this.waitUntilUnblock(details).then(requestIds);
			}
		}).catch(function(e) {
			if (e.message !== 'Abort') {
		mono.debug('requestIds error!', e);
			}
		});
	 });
	});

	promise = promise.then(function() {
	 Object.keys(cache).slice(1000).forEach(function(id) {
		delete cache[id];
	 });

	 var idsArr = [];
	 ids.forEach(function(id) {
		var track = trackObj[id];
		track && idsArr.push(track);
	 });
	 return idsArr;
	});

	promise = promise.then(function(idsArr) {
	 return audio.unmaskUrlViaPlayer(idsArr);
	});

	return promise;
		},

		_getAlbumIdFromUrl: function(url) {
	var _this = this;
	var list = [url];
	var params = mono.parseUrl(url);
	if (params.z) {
	 list.unshift(params.z);
	}

	var result = null;
	list.some(function(url) {
	 result = _this._getAlbumId(url);
	 if (result) {
		return true;
	 }
	});
	return result;
		},

		_getAlbumId: function(url) {
	if (/[?&]q=/.test(url)) {
	 return null;
	}

	var albumInfo = {
	 url: '/al_audio.php',
	 data: {}
	};

	var playlistMatch = /audio_playlist(-?\d+)_(-?\d+)(?:\/(\w+))?/.exec(url);
	if (playlistMatch) {
	 albumInfo.data.access_hash = playlistMatch[3] || '';
	 albumInfo.data.act = 'load_section';
	 albumInfo.data.al = 1;
	 albumInfo.data.claim = 0;
	 // albumInfo.data.is_loading_all = 1;
	 albumInfo.data.owner_id = playlistMatch[1];
	 albumInfo.data.playlist_id = playlistMatch[2];
	 albumInfo.data.type = 'playlist';
	 albumInfo.data.offset = 0;
	}

	if (!albumInfo.data.act) {
	 var audiosMatch = /audios(-?\d+)/.exec(url);
	 if (audiosMatch) {
		var sectionMatch = /[?&]section=(\w+)/.exec(url);
		var sectionName = sectionMatch && sectionMatch[1];
		if (sectionName && ['playlists', 'all'].indexOf(sectionName) === -1) {
			return null;
		}

		albumInfo.data.access_hash = '';
		albumInfo.data.act = 'load_section';
		albumInfo.data.al = 1;
		albumInfo.data.claim = 0;
		albumInfo.data.owner_id = audiosMatch[1];
		albumInfo.data.playlist_id = -1;
		albumInfo.data.type = 'playlist';
		albumInfo.data.offset = 0;
	 }
	}

	if (!albumInfo.data.act) {
	 return null;
	} else {
	 return albumInfo;
	}
		},

		getNewNodeTrackInfo: function(node, cb) {
	var _this = this;
	var track = _this.readNewDataAudio(node.dataset.audio);

	var info = _this.getNewTrackInfo(track);
	if (!info) {
	 return cb("Track info is not found!");
	}

	if (info.url) {
	 return audio.unmaskUrlViaPlayer([
		[null, null, info.url]
	 ]).then(function(idArr) {
		info.url = idArr[0][2];
		cb(null, info);
	 });
	} else
	if (info.fullId) {
	 return cb(null, info);
	} else {
	 return cb("Track info is not found!");
	}
		},

		_getAlbumTrackViaApi: function(albumInfo, details) {
	var _this = this;
	if (!albumInfo.url) {
	 mono.debug('Page is not exists!', albumInfo);
	 throw new Error('Page is not exists!');
	}

	var postData = JSON.parse(JSON.stringify(albumInfo.data));

	var requestList = function() {
	 if (details.abort) {
		return Promise.reject(new Error('Abort'));
	 }

	 return mono.requestPromise({
		type: 'POST',
		headers: {
			'Content-Type': 'application/x-www-form-urlencoded',
			'X-Requested-With': 'XMLHttpRequest'
		},
		url: albumInfo.url,
		data: postData,
		timeout: 60 * 1000,
		localXHR: true
	 }).then(function(response) {
		var data = parseVkResponse(response.body);
		var album = data[5];
		if (!album) {
			throw new Error('Album data is empty!');
		}

		return new Promise(function(resolve) {
			setTimeout(resolve, 250);
		}).then(function() {
			return album;
		});
	 });
	};

	return requestList().then(function(album) {
	 var limit = 20;
	 var getOffset = function(offset) {
		if (!offset || limit < 0) {
			return album;
		}

		limit--;
		postData.offset = offset;
		return requestList().then(function(offsetAlbum) {
			if (offsetAlbum.list.length) {
		album.list.push.apply(album.list, offsetAlbum.list);
		return getOffset(offsetAlbum.nextOffset);
			} else {
		return album;
			}
		}, function(e) {
			if (e.message !== 'Abort') {
		mono.debug('getOffset error!', e);
			}
			return album;
		});
	 };
	 return getOffset(album.nextOffset);
	});
		},

		_getAllTrackViaDom: function(container, details) {
	var _this = this;
	details = details || {};

	var list = [];

	[].slice.call(container.querySelectorAll('.audio_row')).forEach(function(node) {
	 if (details.fromPage && _this.elIsHidden(node)) {
		return;
	 }

	 if (!details.grabReply && photo.isReply(node)) {
		return;
	 }

	 var track = null;
	 try {
		track = JSON.parse(node.dataset.audio);
	 } catch (e) {}

	 if (track) {
		list.push(track);
	 }
	});

	return {
	 list: list
	};
		},

		_getNewAudioLinks: function(container, details) {
	var _this = this;

	details = details || {};
	container = container || document;
	var fromPage = container === document;

	var popup = photo.getPopup('', 'audio', function onClose() {
	 details.abort = true;
	});

	popup.onPrepare(language.download + ' ...');

	var findAudioAlbumLinks = function() {
	 return Promise.resolve().then(function() {
		var albumInfo = _this._getAlbumIdFromUrl(location.href);
		if (!albumInfo) {
			throw new Error('Album is not found');
		}

		return _this._getAlbumTrackViaApi(albumInfo, details);
	 });
	};

	var findAudioAllLinks = function() {
	 return Promise.resolve().then(function() {
		return _this._getAllTrackViaDom(container, {
			fromPage: fromPage,
			grabReply: false
		})
	 });
	};

	details.onProgress = function(count, max) {
	 popup.onProgress(count, max);
	};

	var getTrackListUrl = function(album) {
	 var ids = [];

	 var title = '';
	 if (typeof album.title === 'string') {
		title = mono.fileName.modify(album.title);
	 }

	 album.list.forEach(function(track) {
		var id = track[1] + '_' + track[0];
		if (ids.indexOf(id) === -1) {
			ids.push(id);
		}
	 });

	 return _this._getNewTrackListByIds(ids, details).then(function(list) {
		var linkList = {};
		var trackList = [];

		list.forEach(function(track) {
			var info = _this.getNewTrackInfo(track);
			if (info && info.url) {
		var filename = _this.getNewAudioFilename(info);
		var fullTitle = _this.getNewAudioFullTitle(info);
		linkList[info.fullId] = info.url;
		trackList.push({
		 url: info.url,
		 title: fullTitle,
		 filename: filename
		});
			}
		});

		return {
			linkList: linkList,
			trackList: trackList,
			title: title
		};
	 });
	};

	var promise = Promise.resolve();
	if (fromPage) {
	 promise = promise.then(findAudioAlbumLinks).catch(function(e) {
		if (e.message !== 'Album is not found') {
			mono.debug('findAlbumLinks error!', e);
		}
		throw e;
	 }).catch(function() {
		return findAudioAllLinks();
	 });
	} else {
	 promise = promise.then(findAudioAllLinks);
	}

	promise = promise.then(function(album) {
	 var list = album.list;

	 if (!list.length) {
		throw new Error('Audio is not found');
	 }

	 popup.onProgress(0, list.length);
	 return album;
	});

	promise = promise.then(getTrackListUrl);

	promise = promise.then(function(result) {
	 popup.onReady();
	 return result;
	}, function(e) {
	 popup.onReady();
	 throw e;
	});

	return promise;
		},

		tooltip: {
	tooltip: undefined,
	updatePos: function(button, options) {
	 var btnPosition = SaveFrom_Utils.getPosition(button);
	 var size = SaveFrom_Utils.getSize(this.tooltip);

	 this.tooltip.style.top = (btnPosition.top + options.top - size.height) + 'px';

	 var left = btnPosition.left + parseInt(options.width / 2) - parseInt(size.width / 2);
	 var pageWidth = document.body.clientWidth + document.body.scrollLeft;
	 if (pageWidth < left + size.width) {
		left = pageWidth - size.width;
	 }
	 this.tooltip.style.left = left + 'px';
	},
	show: function(button, options) {
	 var _this = this;
	 if (this.tooltip !== undefined) {
		this.hide();
	 } else {
		this.tooltip = mono.create('div', {
			class: 'sf-tooltip',
			style: mono.extend({
		position: 'absolute',
		display: 'none',
		zIndex: 9999,
		opacity: 0,
		transition: 'opacity 0.2s',
		whiteSpace: 'nowrap'
			}, options.style),
			on: ['mouseenter', function(e) {
		_this.hide();
			}]
		});
		document.body.appendChild(this.tooltip);
	 }
	 this.tooltip.style.display = 'block';

	 setTimeout(function() {
		_this.updatePos(button, options);
		_this.tooltip.style.opacity = 1;
	 }, 0);

	 return this.tooltip;
	},
	hide: function() {
	 this.tooltip.style.opacity = 0;
	 this.tooltip.style.display = 'none';
	}
		},

		rmBitrate: function() {
	if (audio.rmBitrate.style === undefined) {
	 document.body.appendChild(audio.rmBitrate.style = mono.create('style', {
		text: '.sf-bitrate-value {display: none;}'
	 }));
	}
	var bitrateList = document.querySelectorAll('.sf-bitrate-value');
	for (var i = 0, item; item = bitrateList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		},

		insertNewBitrate: function(bitrate, audioRowInfo) {
	if (!bitrate || !audioRowInfo) {
	 return;
	}

	if (!audioRowInfo.classList.contains('audio_row__info')) {
	 return;
	}

	var durationContainer = audioRowInfo.querySelector('.audio_row__duration');
	if (!durationContainer) {
	 return;
	}

	if (audio.rmBitrate.style !== undefined) {
	 audio.rmBitrate.style.parentNode.removeChild(audio.rmBitrate.style);
	 audio.rmBitrate.style = undefined;
	}

	if (durationContainer.querySelector('.sf-bitrate-value')) {
	 return;
	}

	var style = {
	 position: 'absolute',
	 textAlign: 'right',
	 right: 0,
	 opacity: '0.8',
	 top: '14px',
	 fontSize: '11px',
	 whiteSpace: 'nowrap'
	};

	var el = mono.create('span', {
	 text: ' ' + bitrate,
	 class: 'sf-bitrate-value',
	 style: style
	});

	durationContainer.appendChild(el);
		},

		onDlBtnLeave: function() {
	var _this = audio;
	var currentTooltip = _this.tooltip;
	currentTooltip.hide();
		},

		onDlBtnOver: function() {
	var _this = audio;
	var currentTooltip = _this.tooltip;

	var node = this;
	var fullId = node.dataset.fullId;
	var audioRowInfo = node.parentNode && node.parentNode.parentNode;

	var topOffset = -6;
	if (node.dataset.bitrateOffsetTop) {
	 topOffset = parseInt(node.dataset.bitrateOffsetTop);
	}

	var options = {
	 top: topOffset,
	 width: 24,
	 style: {
		backgroundColor: '#fff',
		border: '1px solid #ccc',
		color: 'rgb(48, 48, 48)'
	 }
	};

	var ttp = currentTooltip.show(node, options);
	ttp.dataset.fullId = fullId;

	var setTtpLabel = function() {
	 var bitrate = node.dataset.bitrate;
	 var size = node.dataset.size;

	 var text = '';
	 if (!size) {
		text = language.getFileSizeFailTitle;
	 } else
	 if (bitrate) {
		_this.insertNewBitrate(bitrate, audioRowInfo);
		text = size + ' ~ ' + bitrate;
	 } else {
		text = size;
	 }

	 ttp.style.padding = '2px 5px 3px';
	 ttp.textContent = text;
	};

	if (node.dataset.size) {
	 setTtpLabel();
	} else {
	 ttp.style.padding = '2px 2px 0 2px';
	 ttp.textContent = '';
	 ttp.appendChild(mono.create('img', {
		src: '/images/upload.gif',
		height: 8,
		width: 32,
		style: {
			marginTop: '2px',
			marginBottom: '1px'
		}
	 }));

	 if (!node.dataset.preloadOver) {
		node.dataset.preloadOver = 1;

		_this._preloadNewTrackUrl(node).then(function(url) {
			node.dataset.preloadOver = 2;
			node.href = url;

			return _this._onOverInsertBitrate(node, audioRowInfo).then(function() {
		if (ttp.dataset.fullId === fullId) {
		 setTtpLabel();
		 currentTooltip.updatePos(node, options);
		}
			});
		}).catch(function(e) {
			node.dataset.preloadOver = '';

			if (ttp.dataset.fullId === fullId) {
		setTtpLabel();
		currentTooltip.updatePos(node, options);
			}
		});
	 }
	}
		},

		preloadIdPromiseMap: {},

		_preloadNewTrackUrl: function(node) {
	var _this = this;
	var idPromiseMap = _this.preloadIdPromiseMap;
	var fullId = node.dataset.fullId;
	var promise = idPromiseMap[fullId];
	if (!promise) {
	 promise = idPromiseMap[fullId] = _this._getNewTrackListByIds([fullId], {
		withoutUnblock: true
	 }).then(function(list) {
		delete idPromiseMap[fullId];

		var track = null;
		list.some(function(_track) {
			var id = _track[1] + '_' + _track[0];
			if (id === fullId) {
		track = _track;
		return true;
			}
		});

		var info = track && _this.getNewTrackInfo(track);
		if (!info || !info.url) {
			throw new Error('Track is not found');
		}

		return info.url;
	 }, function(e) {
		delete idPromiseMap[fullId];
		throw e;
	 });
	}
	return promise;
		},

		onNewDlBtnClick: function(e) {
	SaveFrom_Utils.downloadOnClick(e);

	var rowList = document.querySelectorAll('._audio_row_' + this.dataset.fullId);
	for (var i = 0, row; row = rowList[i]; i++) {
	 row.style.backgroundColor = '#f4f7fc';
	}
		},

		_onNewDlBtnClickWrapper: function(e) {
	var _this = audio;
	var node = this;
	e.stopPropagation();

	if (node.dataset.preloadOver > 1 || node.dataset.preloadBitrate > 1) {
	 node.dataset.preloadDl = 2;
	}

	if (!node.dataset.preloadDl) {
	 e.preventDefault();

	 node.dataset.preloadDl = 1;

	 _this._preloadNewTrackUrl(node).then(function(url) {
		node.dataset.preloadDl = 2;
		node.href = url;

		audio.onNewDlBtnClick.call(node, e);
	 }, function() {
		node.dataset.preloadDl = '';
	 });
	} else
	if (node.dataset.preloadDl > 1) {
	 // loading complete
	 audio.onNewDlBtnClick.call(node, e);
	} else {
	 // loading
	 e.preventDefault();
	}
		},

		getNewDlBtn: function(info, filename) {
	var args = {
	 href: info.url || '#sf-preload',
	 class: [audio.className, 'sf-audio-btn'],
	 download: mono.fileName.modify(filename) || '',
	 data: {
		duration: info.duration || '',
		fullId: info.fullId
	 },
	 style: {
		width: '16px',
		height: '16px'
	 },
	 on: [
		['mouseenter', this.onDlBtnOver],
		['mouseleave', this.onDlBtnLeave],
		['click', this._onNewDlBtnClickWrapper],
		['mousedown', function(e) {
			e.stopPropagation();
		}]
	 ]
	};

	if (mono.isGM || mono.isOpera || mono.isSafari) {
	 args.title = language.downloadTitle;
	}

	return mono.create('a', args);
		},

		preloadSizePromiseMap: {},

		_onOverInsertBitrate: function(node, audioRowInfo) {
	var _this = this;
	var sizePromiseMap = _this.preloadSizePromiseMap;
	var fullId = node.dataset.fullId;
	var promise = sizePromiseMap[fullId];
	if (!promise) {
	 promise = sizePromiseMap[fullId] = mono.sendMessagePromise({
		action: 'getFileSize',
		url: node.href
	 }).then(function(response) {
		delete sizePromiseMap[fullId];

		if (!response) {
			throw new Error('Response is empty');
		}

		if (!response.fileSize) {
			delete _this.cache[fullId];
			throw new Error('File size is empty');
		}

		var size = SaveFrom_Utils.sizeHuman(response.fileSize, 2);
		var bitrate = '';
		if (node.dataset.duration) {
			bitrate = Math.floor((response.fileSize / node.dataset.duration) / 125) + ' ' + language.kbps;
		}

		node.dataset.bitrate = bitrate;
		node.dataset.size = size;

		audio.insertNewBitrate(bitrate, audioRowInfo);
	 }, function(e) {
		delete sizePromiseMap[fullId];
		throw e;
	 });
	}
	return promise;
		},

		getNewAudioFullTitle: function(info) {
	var fnArr = [];

	if (info.title) {
	 fnArr.push(info.title);
	}

	if (info.performer) {
	 if (fnArr.length) {
		fnArr.unshift(' - ');
	 }

	 fnArr.unshift(info.performer);
	}

	return fnArr.join('');
		},

		getNewAudioFilename: function(info) {
	var fullTitle = this.getNewAudioFullTitle(info);

	if (fullTitle) {
	 fullTitle += '.mp3';
	}

	return fullTitle;
		},

		handleNewCurrentAudioRow: function(container, info, type) {
	var filename = this.getNewAudioFilename(info);
	var dlBtn = this.getNewDlBtn(info, filename);

	var arrowColor = '#6C8CAC';
	if (type === 1) {
	 arrowColor = '#C4D1DE';
	}

	dlBtn.classList.remove('sf-audio-btn');
	mono.create(dlBtn, {
	 style: {
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', arrowColor) + ') center no-repeat',
		backgroundSize: '12px',
		width: '12px',
		height: '12px',
		padding: 0,
		margin: 0,
		cssFloat: 'left',
		marginRight: '3px',
		marginTop: '1px',
		marginBottom: '-2px'
	 }
	});

	var attrObserver = null;

	mono.onRemoveEvent(dlBtn, function() {
	 mono.one(container, 'mouseenter', vk.mutationMode.wrapNewAudioOnMouseOver);
	 attrObserver && attrObserver.stop();
	});

	if (type === 2) {
	 var parent = mono.getParent(container, '.audio_page_player');
	 if (parent) {
		attrObserver = SaveFrom_Utils.mutationAttrWatcher.run({
			target: parent,
			attr: ['data-full-id'],
			callback: function() {
		dlBtn.parentNode && dlBtn.parentNode.removeChild(dlBtn);
		attrObserver && attrObserver.stop();
			}
		});
	 }
	}

	if (type === 1) {
	 dlBtn.dataset.bitrateOffsetTop = 1;
	}

	container.insertBefore(dlBtn, container.firstChild);
		},

		handleNewAudioRow: function(container, btnContainer, info) {
	var _this = this;
	var filename = this.getNewAudioFilename(info);
	var dlBtn = this.getNewDlBtn(info, filename);
	var audioRowInfo = btnContainer.parentNode;

	mono.create(dlBtn, {
	 class: ['audio_row__action'],
	 style: {
		width: '24px',
		height: '24px',
		cssFloat: 'left'
	 }
	});

	var firstChild = btnContainer.firstChild;
	if (firstChild) {
	 btnContainer.insertBefore(dlBtn, firstChild);
	} else {
	 btnContainer.appendChild(dlBtn);
	}

	if (preference.vkShowBitrate === 1) {
	 if (!dlBtn.dataset.preloadBitrate) {
		dlBtn.dataset.preloadBitrate = 1;

		_this._preloadNewTrackUrl(dlBtn).then(function(url) {
			dlBtn.dataset.preloadBitrate = 2;
			dlBtn.href = url;

			return _this._onOverInsertBitrate(dlBtn, audioRowInfo);
		}).catch(function(e) {

		});
	 }
	}
		},

		addNewDlTrackBtn: function(container) {
	var _this = this;

	var start = function() {
	 observer.observe(container, {
		childList: true,
		subtree: true
	 });
	};

	var stop = function() {
	 observer.disconnect();
	};

	var onGetTrackInfo = function(btnContainer, tackInfo) {
	 _this.handleNewAudioRow(container, btnContainer, tackInfo);
	};

	var trackInfoPromise = null;

	var onBtnContainer = function(btnContainer) {
	 if (!trackInfoPromise) {
		trackInfoPromise = new Promise(function(resolve, reject) {
			_this.getNewNodeTrackInfo(container, function(err, info) {
		if (err) {
		 reject(err);
		} else {
		 resolve(info);
		}
			});
		}).catch(function(err) {
			stop();
		});
	 }
	 trackInfoPromise.then(function(info) {
		info && onGetTrackInfo(btnContainer, info);
	 });
	};

	var mObserver = SaveFrom_Utils.mutationWatcher.getMutationObserver();
	var observer = new mObserver(function(mutations) {
	 var mutation = null,
		node = null,
		i = 0,
		len = 0;
	 while (mutation = mutations.shift()) {
		if (
			mutation.type === 'childList' &&
			mutation.addedNodes.length &&
			mutation.target.classList.contains('audio_row__info')
		) {
			for (i = 0, len = mutation.addedNodes; node = mutation.addedNodes[i]; i++) {
		if (node.classList.contains('audio_row__actions')) {
		 onBtnContainer(node);
		 return;
		}
			}
		}
	 }
	});
	start();

	var node = container.querySelector('.audio_row__actions');
	if (node) {
	 onBtnContainer(node);
	 node = null;
	}
		},

		getNewTrackInfo: function(track) {
	if (!track) {
	 return null;
	}

	var info = {};

	if (typeof track[2] === 'string') {
	 info.url = track[2];
	}

	info.title = track[3];
	if (info.title) {
	 info.title = mono.fileName.decodeSpecialChars(rmEmTags(info.title));
	}

	info.performer = track[4];
	if (info.performer) {
	 info.performer = mono.fileName.decodeSpecialChars(rmEmTags(info.performer));
	}

	info.duration = parseInt(track[5]);

	if (track[1] && track[0]) {
	 info.fullId = track[1] + '_' + track[0];
	}

	info.id = track[0];
	info.ownerId = track[1];
	return info;
		},

		readNewDataAudio: function(data) {
	try {
	 return JSON.parse(data);
	} catch (e) {
	 return null;
	}
		},

		addNewDlCurrentTrackBtn: function(container, type) {
	var _this = this;
	SaveFrom_Utils.bridge({
	 func: function(cb) {
		var track = null;

		if (!track && typeof ap !== 'undefined' && ap._currentAudio) {
			track = ap._currentAudio;
		}

		if (!track && typeof cur !== 'undefined' && cur.audioPage && cur.audioPage._readyAudio) {
			track = cur.audioPage._readyAudio;
		}

		if (!track) {
			try {
		track = JSON.parse(localStorage.audio_v9_track);
			} catch (e) {}
		}

		return cb(track);
	 },
	 cb: function(track) {
		if (!track) {
			var node = document.querySelector('.audio_page_player[data-audio]');
			track = node && _this.readNewDataAudio(node.dataset.audio);
		}

		var info = track && _this.getNewTrackInfo(track);
		if (info) {
			var onGetInfo = function(info) {
		_this.handleNewCurrentAudioRow(container, info, type);
			};

			if (!info.url) {
		if (info.fullId) {
		 onGetInfo(info);
		}
			} else {
		onGetInfo(info);
			}
		}
	 },
	 timeout: 300
	});
		},

		onNewMouseOver: function(e) {
	var _this = audio;
	var row = this;
	if (!row || row.querySelector('.' + audio.className)) {
	 return;
	}

	var currentTrackType = null;
	if (row.classList.contains('top_audio_player_title')) {
	 currentTrackType = 1;
	}
	if (row.classList.contains('audio_page_player_title_performer')) {
	 currentTrackType = 2;
	}

	if (currentTrackType) {
	 _this.addNewDlCurrentTrackBtn(row, currentTrackType);
	} else {
	 _this.addNewDlTrackBtn(row);
	}
		},

		addCustomStyle: function() {
	if (this.addCustomStyle.hasStyle === 1) {
	 return;
	}
	this.addCustomStyle.hasStyle = 1;

	var currentStyle = document.querySelector('#savefrom-styles.sf-audio');
	if (currentStyle) {
	 currentStyle.parentNode.removeChild(currentStyle);
	}

	SaveFrom_Utils.addStyleRules('.' + downloadLinkClassName + '.sf-audio-btn', {
	 'background': 'url(' + SaveFrom_Utils.svg.getSrc('download', '#5f7fa2') + ') center no-repeat !important',
	 'opacity': '0.4'
	}, 'sf-audio');
		},

		hideLinks: function() {
	if (this.addCustomStyle.hasStyle) {
	 this.addCustomStyle.hasStyle = 0;

	 var currentStyle = document.querySelector('#savefrom-styles.sf-audio');
	 if (currentStyle) {
		currentStyle.parentNode.removeChild(currentStyle);
	 }

	 SaveFrom_Utils.addStyleRules('.' + downloadLinkClassName, {
		'display': 'none'
	 }, 'sf-audio');
	}
	if (audio.tooltip.tooltip) {
	 audio.tooltip.tooltip.parentNode.removeChild(audio.tooltip.tooltip);
	 audio.tooltip.tooltip = undefined;
	}

	audio.cache = {};
		},

		elIsHidden: function isHidden(el) {
	return (el.offsetParent === null)
		},

		downloadMP3Files: function() {
	var container = photo.getLayer() || document;

	audio._getNewAudioLinks(container).then(function(result) {
	 var linkList = result.linkList;
	 var trackList = result.trackList;
	 var title = result.title || getFolderName();

	 var list = trackList;

	 if (list.length === 0) {
		return alert(language.vkMp3LinksNotFound);
	 }
	 // mp3
	 SaveFrom_Utils.downloadList.showBeforeDownloadPopup(list, {
		type: 'audio',
		folderName: title
	 });
	}, function(e) {
	 if (e.message !== 'Abort') {
		mono.debug('_getNewAudioLinks error!', e);
		alert(language.vkMp3LinksNotFound);
	 }
	});
		},

		showListOfAudioFiles: function(showPlaylist) {
	var container = photo.getLayer() || document;
	audio._getNewAudioLinks(container).then(function(result) {
	 var linkList = result.linkList;
	 var trackList = result.trackList;
	 var title = result.title || getFolderName();
	 var list = null;
	 if (showPlaylist) {
		list = trackList;

		if (list.length !== 0) {
			return SaveFrom_Utils.playlist.popupPlaylist(list, title, true);
		}
	 } else {
		list = [];
		for (var i in linkList) {
			list.push({
		url: linkList[i]
			});
		}

		if (list.length !== 0) {
			return SaveFrom_Utils.playlist.popupFilelist(list);
		}
	 }

	 alert(language.vkMp3LinksNotFound);
	}, function(e) {
	 if (e.message !== 'Abort') {
		mono.debug('_getNewAudioLinks error!', e);
		alert(language.vkMp3LinksNotFound);
	 }
	});
		}
	};

	// /AUDIO
	///////////////////////////////////////////////////////////////////



	///////////////////////////////////////////////////////////////////
	// VIDEO

	var video = {
		panelId: 'savefrom__vk_video_links',
		videoAttr: 'data-savefrom-video',
		hiddenAttr: 'data-savefrom-hidden',
		btnBoxId: 'sf-iframe-dl-btn',
		btnBox: null,
		style: {
	fontSize: '10pt',
	margin: '15px 0',
	padding: '0'
		},

		getLinksFormUrl: function(url) {
	if (!url) return;

	if (url.substr(0, 2) === '//') {
	 url = 'http:' + url;
	}

	if (preference.showUmmyItem && this.isRutubeLink(url)) {
	 return video.getRutubeLinks(url);
	}

	if (this.isPladformLink(url)) {
	 return video.getPladformLinks(url);
	}

	var request;
	var hostingList = SaveFrom_Utils.embedDownloader.hostings;
	for (var hostingName in hostingList) {
	 var hosting = hostingList[hostingName];
	 for (var i = 0, item; item = hosting.re[i]; i++) {
		var data = url.match(item);
		if (data) {
			request = {
		hosting: hostingName,
		action: hosting.action,
		extVideoId: data[1]
			};
			break;
		}
	 }
	 if (request) break;
	}

	if (!request) return;
	return {
	 request: request
	}
		},

		getLinksFromFlashVars: function(flashVars) {
	var params = mono.parseUrl(flashVars, {
	 params: true
	});
	var links = video.getLinksFromHtml5MetaData(params);
	return links;
		},

		getLinksFromHtml5MetaData: function(metaData) {
	if (!metaData) return;

	var title = metaData.md_title;

	if (title === undefined) return;

	var videoUrlRegExp = /url([0-9]+)/;
	var urlList = {};

	var hasLinks = false;
	for (var key in metaData) {
	 var quality = null;

	 if (key === 'extra_data' && metaData.extra === "52") {
		quality = metaData.hd ? 'HD' : 'SD';
		urlList[quality] = metaData[key];
		hasLinks = true;
		continue;
	 }

	 quality = key.match(videoUrlRegExp);
	 if (quality === null) continue;

	 var link = metaData[key];
	 var vPos = link.indexOf('?');
	 if (vPos !== -1) {
		link = link.substr(0, vPos);
	 }
	 hasLinks = true;
	 urlList[quality[1]] = link;
	}

	if (!hasLinks) {
	 return;
	}

	return {
	 title: title,
	 links: urlList
	}
		},

		getRutubeLinks: function(src) {
	if (!/rutube[^\/]+\/(?:play|video)\/embed\/(\d+)/.test(src) && !/video\.rutube\./.test(src)) {
	 return;
	}

	var links = SaveFrom_Utils.popupMenu.prepareLinks.rutube(src);

	return {
	 isUmmy: true,
	 links: links
	};
		},

		isRutubeLink: function(src) {
	return /\/\/.*rutube\..*/.test(src);
		},

		getPladformLinks: function(src) {
	if (!src) {
	 return;
	}

	var params = mono.parseUrl(src);
	return {
	 request: {
		action: 'getPladformVideo',
		extVideoId: {
			playerId: params.pl,
			videoId: params.videoid
		}
	 }
	};
		},

		isPladformLink: function(src) {
	return /\/\/.*pladform\..*/.test(src);
		},

		getLinksVideoEl: function(videoEl, layer) {
	"use strict";
	var title = layer.querySelector('.vv_summary');
	if (!title) {
	 return null;
	}
	title = title.textContent;

	var linkList = {};
	var hasLinks;
	var sourceList = videoEl.querySelectorAll('source');
	for (var i = 0, node; node = sourceList[i]; i++) {
	 var src = node.src || '';
	 var pos = src.indexOf('?');
	 if (pos !== -1) {
		src = src.substr(0, pos);
	 }
	 var m = src.match(/\.(\d+)\.[^\/]+$/);
	 if (m === null) {
		continue;
	 }
	 linkList[m[1]] = src;
	 hasLinks = true;
	}

	if (!hasLinks) {
	 return;
	}

	return {
	 title: title,
	 links: linkList
	}
		},

		getPlayerNode: function(layer) {
	var player = null;
	[
	 'iframe.video_yt_player',
	 '#html5_player',
	 '#flash_video_obj',
	 '#playerObj',
	 '#player',
	 '.video_box_wrap > #video_player'
	].some(function(selector) {
	 player = layer.querySelector(selector);
	 if (player) {
		return true;
	 }
	});
	return player;
		},

		getLinksFromMv: function(oid, vid, cb) {
	SaveFrom_Utils.bridge({
	 args: [vid, oid],
	 func: function(vid, oid, cb) {
		var mvcur = window.mvcur;
		if (!mvcur || !mvcur.player || !mvcur.player.vars) {
			return cb();
		}

		var vars = mvcur.player.vars;
		if (vars.vid !== vid || vars.oid !== oid) {
			return cb();
		}

		return cb({
			vars: mvcur.player.vars
		});
	 },
	 cb: function(data) {
		if (!data) {
			return cb();
		}

		var links = video.getLinksFromHtml5MetaData(data.vars);
		return cb(links);
	 },
	 timeout: 300
	});
		},

		getLinksFromFrame: function(callback) {
	var html = document.body.innerHTML;
	var params = mono.parseUrl(location.href);
	var oid = parseInt(params.oid);
	var id = parseInt(params.id);
	if (oid && id) {
	 var varsJson = null;
	 mono.findJson(html, [/"vid":/, /"oid":/, /"md_title":/]).some(function(obj) {
		varsJson = obj;
		return true;
	 });
	 if (varsJson && varsJson.vid === id && varsJson.oid === oid) {
		return callback(null, {
			request: {
		hosting: 'vk',
		action: 'getVkLinksFromJsonMsg',
		json: varsJson
			}
		});
	 } else {
		var layer = document.body;
		var player = video.getPlayerNode(layer);
		if (player) {
			return video.getLinksFromPlayer(layer, player, function(links) {
		if (links) {
		 callback(null, links);
		}
			});
		}
	 }
	}

	return callback('ERROR');
		},

		getLinksFromPlayer: function(layer, playerNode, cb) {
	if (!playerNode) return;

	var links, flashVars;
	if (playerNode.tagName === 'OBJECT') {
	 flashVars = playerNode.querySelector('param[name="flashvars"]');
	 if (flashVars) {
		flashVars = flashVars.getAttribute('value');
		links = video.getLinksFromFlashVars(flashVars);
	 }
	} else
	if (playerNode.tagName === 'IFRAME') {
	 var src = playerNode.getAttribute('src');
	 if (!links) {
		links = video.getLinksFormUrl(src);
	 }
	} else
	if (playerNode.tagName === 'EMBED') {
	 var url = playerNode.getAttribute('src');
	 if (!links) {
		flashVars = playerNode.getAttribute('flashvars');
		if (flashVars) {
			links = video.getLinksFromFlashVars(flashVars);
		}
	 }
	 if (!links) {
		links = video.getLinksFormUrl(url);
	 }
	}
	if (links) {
	 return cb(links, layer);
	}
	if (playerNode.tagName === 'DIV' && playerNode.id === 'video_player') {
	 var parentId = playerNode.parentNode.id;
	 var oidVid = parentId && parentId.match(/video_box_wrap(-?\d+)_(-?\d+)/);
	 if (oidVid) {
		oidVid.shift();
		oidVid = oidVid.map(function(id) {
			return parseInt(id);
		});
		return SaveFrom_Utils.bridge({
			args: oidVid,
			func: function(oid, vid, cb) {
		var mvcur = window.mvcur;
		if (!mvcur) {
		 return cb();
		}

		var path = 'video' + oid + '_' + vid;
		if (mvcur.listId) {
		 path += '?list=' + mvcur.listId;
		}

		return cb({
		 path: path
		});
			},
			cb: function(data) {
		if (!data) {
		 return;
		}

		return cb({
		 request: {
			hosting: 'vk',
			action: 'getVKLinks',
			extVideoId: data.path,
			oidVid: oidVid
		 }
		}, layer);
			},
			timeout: 300
		});
	 }
	}
	if (playerNode.id === 'html5_player') {
	 return SaveFrom_Utils.bridge({
		func: function(cb) {
			if (!window.html5video || !window.html5video.vars) {
		return cb();
			}
			cb(window.html5video.vars);
		},
		cb: function(data) {
			var links = video.getLinksFromHtml5MetaData(data);
			if (links) {
		return cb(links, layer);
			}
		},
		timeout: 300
	 });
	}
	if (playerNode.tagName === 'A') {
	 var href = playerNode.href;
	 var params = mono.parseUrl(href);
	 if (params.to) {
		links = SaveFrom_Utils.embedDownloader.checkUrl(params.to);
		if (links) {
			return cb({
		request: links
			}, layer);
		} else {
			return cb(null, layer);
		}
	 }
	}
	return cb(null, layer);
		},

		preparePladformLinks: function(response) {
	if (response && response.action === 'getRutubeLinks') {
	 response.links = null;
	}

	var links = response && response.links;
	var title = 'noname';
	var linkList = {};

	if (links) {
	 for (var i = 0, item; item = links[i]; i++) {
		title = item.title;
		if (linkList[item.quality]) {
			item.quality = +' ';
		}
		linkList[item.quality.toUpperCase()] = item.url;
	 }
	}

	return {
	 title: title,
	 links: linkList
	}
		},

		prepareLinks: function(links) {
	var title = links.title;

	var linkList = [];
	for (var quality in links.links) {
	 var item = links.links[quality];

	 var ext = item.match(/[\w]+\.(mp4|flv)(?:\?|$)/i);
	 if (!ext) {
		ext = 'flv';
	 } else {
		ext = ext[1];
	 }
	 var format = ext.toUpperCase();

	 linkList.push({
		href: item,
		quality: quality,
		title: title,
		ext: ext,
		format: format,
		forceDownload: true
	 });
	}

	return linkList;
		},

		getVideoLinksAsAjax: function(url) {
	var oidVid = /video(-?\d+_-?\d+)/.exec(url);
	oidVid = oidVid && oidVid[1];

	var list = mono.parseUrl(url).list;
	return photo._getModuleName().then(function(moduleName) {
	 return new Promise(function(resolve) {
		videoFeed.getLinkAsAjax([
			oidVid,
			list
		], function(response, hosting) {
			resolve({
		hosting: hosting,
		response: response
			});
		}, moduleName);
	 })
	});
		},

		newAppendButton: function(links, container) {
	var _this = this;
	if (!links) {
	 return;
	}

	var funcClassName = 'sf-under-video';

	var controls = container.querySelector('#mv_info');
	var actionBlock = controls && controls.querySelector('.mv_actions_block .like_cont .like_btns');
	controls = null;

	var topControls = container.querySelector('#mv_top_controls');

	if (actionBlock && audio.elIsHidden(actionBlock)) {
	 actionBlock = null;
	}

	if (!actionBlock && !topControls) {
	 return;
	}

	var isInTopControls = !!(!actionBlock && topControls);

	var oldBtnList = container.querySelectorAll('.' + downloadLinkClassName);
	for (var i = 0, node; node = oldBtnList[i]; i++) {
	 node.parentNode.removeChild(node);
	}
	node = null;
	oldBtnList = null;

	var dlBtn = mono.create('div', {
	 class: [downloadLinkClassName, funcClassName],
	 style: {
		cursor: 'pointer'
	 },
	 on: [
		['click', function(e) {
			e.stopPropagation();

			mono.onRemoveEvent(this, vk.hideMenu);

			if (vk.contextMenu && vk.contextMenu.isShow) {
		vk.hideMenu();
		return;
			}

			var menu = vk.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + '...', 'sf-single-video-menu', {
		parent: container
			});

			if (links.isUmmy) {
		menu.update(links.links);
		return;
			}

			if (links.request) {
		var onResponse = function(response) {
		 var mLinks = language.noLinksFound;
		 if (response) {
			if (links.request.action === 'getPladformVideo') {
				if (preference.showUmmyItem && response.action === 'getRutubeLinks') {
			mLinks = SaveFrom_Utils.popupMenu.prepareLinks.rutube(response.links);
				} else {
			mLinks = video.prepareLinks(video.preparePladformLinks(response));
				}
			} else
			if (response.links) {
				var hosting = SaveFrom_Utils.embedDownloader.reMapHosting(response.action);
				if (hosting) {
			mLinks = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
				}
			}
		 }
		 if (links.request.action === 'getVKLinks' && typeof mLinks === 'string' || mLinks.length === 0) {
			return _this.getVideoLinksAsAjax(links.request.extVideoId).then(function(data) {
				var hosting = data.hosting;
				var response = data.response;
				var mLinks = language.noLinksFound;
				if (response && response.links) {
			if (response.isUmmy) {
			 mLinks = response.links;
			} else {
			 mLinks = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
			}
				}
				menu.update(mLinks);
			});
		 } else {
			menu.update(mLinks);
		 }
		};

		var next = function() {
		 try {
			mono.sendMessage(links.request, onResponse);
		 } catch (e) {
			onResponse();
		 }
		};
		if (links.request.oidVid) {
		 video.getLinksFromMv(links.request.oidVid[0], links.request.oidVid[1], function(links) {
			if (links) {
				var mLinks = video.prepareLinks(links);
				menu.update(mLinks);
			} else {
				next();
			}
		 });
		} else {
		 next();
		}
		return;
			}

			var mLinks = video.prepareLinks(links);
			menu.update(mLinks);
		}],
		['mousedown', function(e) {
			e.stopPropagation();
		}],
		['keydown', function(e) {
			e.stopPropagation();
		}]
	 ]
	});

	if (actionBlock) {
	 mono.create(dlBtn, {
		class: ['like_btn'],
		append: [
			mono.create('div', {
		class: ['like_button_icon'],
		append: [
		 mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('download', '#828a99'),
			width: 16,
			height: 16,
			style: {
				margin: '4px'
			}
		 })
		]
			}),
			mono.create('div', {
		class: ['like_button_label'],
		text: language.download
			})
		]
	 });

	 var more = actionBlock.querySelector('.ui_actions_menu_wrap');
	 if (more) {
		more.parentNode.insertBefore(dlBtn, more);
	 } else {
		actionBlock.appendChild(dlBtn);
	 }
	} else
	if (isInTopControls) {
	 mono.create(dlBtn, {
		class: ['mv_top_button'],
		style: {
			textAlign: 'center'
		},
		append: [
			mono.create('img', {
		class: ['mv_small_close_icon'],
		style: {
		 backgroundImage: 'none',
		 width: '20px',
		 height: '20px'
		},
		src: SaveFrom_Utils.svg.getSrc('download', '#FFFFFF'),
		width: 20,
		height: 20
			})
		]
	 });

	 var childElement = topControls.firstChild;
	 if (!childElement) {
		topControls.appendChild(dlBtn);
	 } else
	 if (!audio.elIsHidden(topControls.lastChild)) {
		topControls.appendChild(dlBtn);
	 } else {
		while (childElement.nextElementSibling && !audio.elIsHidden(childElement.nextElementSibling)) {
			childElement = childElement.nextElementSibling;
		}
		childElement.parentNode.insertBefore(dlBtn, childElement);
	 }
	}
		},

		appendNewFrameBtn: function(links, container) {
	var _this = this;
	if (container.querySelector('.' + downloadLinkClassName)) {
	 return;
	}

	var firstClick = true;

	var btnObj = SaveFrom_Utils.frameMenu.getBtn({
	 singleBtn: true,
	 btnId: _this.btnBoxId,
	 containerStyle: {
		top: '10px',
		right: '10px'
	 },
	 on: [
		['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			if (vk.contextMenu && vk.contextMenu.isShow) {
		vk.hideMenu();
		return;
			}

			var menu = vk.contextMenu = SaveFrom_Utils.frameMenu.getMenu(this, language.download + '...', 'sf-frame-menu', {
		container: btnObj.container,
		onShow: function() {
		 btnObj.node.classList.add('sf-over');
		},
		onHide: function() {
		 vk.contextMenu = null;
		 btnObj.node.classList.remove('sf-over');
		}
			});

			if (links.request) {
		var onResponse = function(response) {
		 var mLinks = language.noLinksFound;
		 if (response && links.request.action === 'getPladformVideo') {
			if (preference.showUmmyItem && response.action === 'getRutubeLinks') {
				mLinks = SaveFrom_Utils.popupMenu.prepareLinks.rutube(response.links);
			} else {
				mLinks = _this.prepareLinks(_this.preparePladformLinks(response));
			}
		 } else
		 if (response && response.links) {
			var hosting = SaveFrom_Utils.embedDownloader.reMapHosting(response.action);
			if (hosting) {
				mLinks = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
			}
		 }
		 menu.update(mLinks);
		};

		try {
		 mono.sendMessage(links.request, onResponse);
		} catch (e) {
		 onResponse();
		}
			} else {
		menu.update(_this.prepareLinks(links));
			}

			firstClick = false;
		}],
		['mousedown', function(e) {
			e.stopPropagation();
			if (e.button === 2) {
		vk.hideMenu();

		if (btnObj.container.parentNode) {
		 btnObj.container.parentNode.removeChild(btnObj.container);
		}
			}
		}]
	 ]
	});

	btnObj.container = mono.create('div', {
	 class: 'sf-btn-ctr',
	 append: btnObj.node
	});

	btnObj.node.appendChild(mono.create('style', {
	 text: mono.style2Text([{
		selector: [
			'body:hover .sf-btn-ctr #' + _this.btnBoxId,
			'body:hover .sf-btn-ctr .sf-frame-menu'
		],
		style: {
			display: 'block'
		}
	 }])
	}));

	document.body.appendChild(btnObj.container);
		},

		addFrameBtn: function() {
	var _this = this;
	var container = document.getElementById('page_wrap');
	if (!container) {
	 return;
	}

	video.getLinksFromFrame(function(err, links) {
	 if (!err) {
		video.appendNewFrameBtn(links, container);
	 }
	});
		}
	};

	var videoFeed = {
		linkDataAttr: 'savefromHasBtn',
		getLinkAsAjaxRequest: function(_details, index) {
	"use strict";
	index = index || 0;

	var details = mono.extend({}, _details);

	var abort = function() {
	 if (index < 1) {
		return videoFeed.getLinkAsAjaxRequest(_details, ++index);
	 }

	 _details.error && _details.error();
	};

	var data = details.data;

	if (index === 0) {
	 data.act = 'show_inline';
	} else
	if (index === 1) {
	 data.act = 'show';
	}

	mono.request(details, function(err, res, data) {
	 if (err || !data) {
		return abort();
	 }

	 if (data.indexOf('href="/join"') !== -1) {
		return abort();
	 }

	 _details.success(data);
	});
		},
		getVideoDataFromLink: function(link) {
	var re = /showVideo\(['"]{1}([^'"]+)['"]{1},.?['"]{1}([^'"]+)['"]{1},.*\)/;
	var attrValue = link.getAttribute('onclick');
	var videoData = re.exec(attrValue);
	if (videoData) {
	 videoData.shift();
	}
	return videoData;
		},
		getLinkAsAjax: function(videoData, cb, moduleName) {
	videoFeed.getLinkAsAjaxRequest({
	 localXHR: 1,
	 type: 'POST',
	 url: '/al_video.php',
	 data: {
		list: videoData[1],
		video: videoData[0],
		act: 'show_inline',
		module: moduleName,
		al: 1
	 },
	 success: function(pageData) {
		if (!pageData) {
			return cb();
		}

		var frameSrc = pageData.match(/<iframe[^>]+src=['"]{1}([^'">]+)['"]{1}[^>]+>/i);
		if (!frameSrc) {
			// search dailymotion
			frameSrc = pageData.match(/var\s+opts\s+=\s+({[^}]*})/im);
			if (frameSrc) {
		frameSrc = frameSrc[1].match(/url:\s+['"]{1}([^'"]+)['"]{1}/i);
		if (frameSrc && frameSrc[1].indexOf('//') !== 0 && frameSrc[1].indexOf('http') !== 0) {
		 frameSrc = null;
		}
			}
		}

		if (!frameSrc) {
			mono.sendMessagePromise({
		action: 'getVkLinksFromData',
		data: pageData
			}).then(function(response) {
		return cb(response, 'vk');
			}).catch(function() {
		return cb({}, 'vk');
			});
			return;
		}

		var url = frameSrc[1];

		if (preference.showUmmyItem && video.isRutubeLink(url)) {
			return cb(video.getRutubeLinks(url));
		}

		if (url.indexOf('//') === 0) {
			url = 'http:' + url;
		}
		if (url.indexOf('http') !== 0) {
			return cb();
		}

		var data = SaveFrom_Utils.embedDownloader.checkUrl(url);
		if (!data) {
			return cb();
		}

		var request = {
			action: data.action,
			extVideoId: data.extVideoId
		};

		mono.sendMessage(request, function(response) {
			var hosting = data.hosting;

			if (response.action !== request.action) {
		hosting = SaveFrom_Utils.embedDownloader.reMapHosting(response.action);
			}

			return cb(response, hosting);
		});
	 },
	 error: function() {
		cb();
	 }
	});
		},
		addDownloadBtn: function(link) {
	var url = link.href;

	var dlBtn = mono.create('a', {
	 href: 'http://savefrom.net/?url=' + encodeURIComponent(url),
	 style: {
		display: 'inline-block',
		width: '16px',
		height: '16px',
		marginLeft: '5px',
		backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#78A2CC') + ')',
		backgroundRepeat: 'no-repeat',
		marginBottom: '-4px'
	 },
	 on: ['click', function(e) {
		var _this = this;
		e.preventDefault();
		// e.stopPropagation();

		mono.onRemoveEvent(btnWrapper, vk.hideMenu);

		if (vk.contextMenu && vk.contextMenu.isShow) {
			vk.hideMenu();
			return;
		}

		var parentEl = document.querySelector('#wk_box');
		if (!parentEl || !parentEl.contains(this)) {
			parentEl = null;
		}
		var args = {
			parent: parentEl
		};

		var url = this.getAttribute(SaveFrom_Utils.embedDownloader.dataAttr);
		var eRequest = SaveFrom_Utils.embedDownloader.checkUrl(url);
		if (!eRequest) {
			vk.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(dlBtn, language.noLinksFound, 'sf-popupMenu', args);
			return;
		}

		var request = {
			action: eRequest.action,
			extVideoId: eRequest.extVideoId
		};

		var menu = vk.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(dlBtn, language.download + ' ...', 'sf-popupMenu', args);

		var onResponse = function(response) {
			var hosting = eRequest.hosting;

			if (response.action != request.action) {
		hosting = SaveFrom_Utils.embedDownloader.reMapHosting(response.action);
			}

			if (Array.isArray(response.links) && response.links.length === 0) {
		response.links = undefined;
			}

			if (response.links) {
		var links = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
		menu.update(links);
			} else {
		var link = SaveFrom_Utils.getParentByTagName(_this.parentNode, 'A');
		var videoData = videoFeed.getVideoDataFromLink(link);
		photo.getModuleName(videoFeed.getLinkAsAjax.bind(videoFeed, videoData, function(response, hosting) {
		 if (response && response.links) {
			var links;
			if (response.isUmmy) {
				links = response.links;
			} else {
				links = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
			}
			menu.update(links);
			return;
		 }
		 menu.update(language.noLinksFound);
		}));
			}
		};

		try {
			mono.sendMessage(request, onResponse);
		} catch (e) {
			onResponse({});
		}

	 }]
	});

	dlBtn.setAttribute(SaveFrom_Utils.embedDownloader.dataAttr, url);

	var btnWrapper = mono.create('span', {
	 class: 'sf-video-feed-container',
	 on: ['click', function(e) {
		e.stopPropagation();
	 }],
	 append: [
		dlBtn
	 ]
	});

	var postVideoTitle = link.querySelector('.post_video_title');
	if (postVideoTitle) {
	 postVideoTitle.appendChild(btnWrapper);
	} else {
	 link.appendChild(btnWrapper);
	}
		},
		onLinkHover: function() {
	var link = this;

	if (link.tagName !== 'A') {
	 return;
	}

	var href = link.href || '';
	var id = link.id;
	if (id.indexOf('post_media_lnk') !== 0 || href.indexOf('/video') === -1) {
	 return;
	}

	if (vk.contextMenu && vk.contextMenu.isShow) {
	 vk.hideMenu();
	}

	if (link.dataset[videoFeed.linkDataAttr]) {
	 return;
	}
	link.dataset[videoFeed.linkDataAttr] = 1;

	videoFeed.addDownloadBtn(link);
		},
		off: function() {
	var btnList = document.querySelectorAll('.sf-video-feed-container');
	for (var i = 0, item; item = btnList[i]; i++) {
	 item.parentNode.removeChild(item);
	}
	var dataAttr = mono.dataAttr2Selector(videoFeed.linkDataAttr);
	var dataAttrList = document.querySelectorAll('*[' + dataAttr + ']');
	for (i = 0, item; item = dataAttrList[i]; i++) {
	 item.removeAttribute(dataAttr);
	}
		}
	};

	// /VIDEO
	///////////////////////////////////////////////////////////////////



	///////////////////////////////////////////////////////////////////
	// PHOTO

	var photo = {
		photoCache: {},
		getAlbumId: function(url) {
	if (/(\?|&|#)act=edit/i.test(url)) {
	 return;
	}

	var dataList = [];

	dataList.push(url);

	var params = mono.parseUrl(url);
	if (params.w) {
	 dataList.push(params.w);
	}
	if (params.z) {
	 dataList.push.apply(dataList, params.z.split('/'));
	}

	if (/#/.test(url)) {
	 dataList.push(url.substr(url.indexOf('#') + 1));
	 dataList.push(decodeURIComponent(url.substr(url.indexOf('#') + 1)));
	}

	dataList.reverse();

	var aid = null;
	var m = null;
	dataList.some(function(data) {
	 m = data.match(/(?:\/|#|=|^)(albums?|tag|photos|feed(?:\d+)?_|wall)(-?\d+)(?:_(\d+))?/i);
	 if (m) {
		if (m[3]) {
			if (/^(feed|wall)/.test(m[1])) {
		aid = m[1] + m[2] + '_' + m[3];
			} else {
		aid = 'album' + m[2] + '_' + m[3];
			}

		} else {
			if (m[1] == 'albums') {
		m[1] = 'photos';
			}

			aid = m[1] + m[2];
		}
		return true;
	 }
	});

	return aid;
		},
		getModuleName: function(cb) {
	var dataArg = 'sfModule';
	var script = mono.create('script', {
	 text: '(' + function() {
		if (window.cur && window.cur.module && typeof(window.cur.module) === 'string') {
			document.body.dataset['{dataArg}'] = window.cur.module;
		}
	 }.toString().replace('{dataArg}', dataArg) + ')();'
	});
	document.body.appendChild(script);
	setTimeout(function() {
	 script.parentNode.removeChild(script);
	 cb(document.body.dataset[dataArg]);
	}, 0);
		},
		isReply: function(el) {
	return mono.matches(el, '.replies ' + el.tagName) || mono.matches(el, '.wl_replies ' + el.tagName);
		},
		getWallPostContent: function() {
	var postId = location.href.match(/wall(-?\d+_\d+)/);
	postId = postId && postId[1];

	if (!postId) {
	 return;
	}

	return document.getElementById('post' + postId) || document.getElementById('wpt' + postId);
		},

		/**
		 * @param {string} title
		 * @param {string} type
		 * @param {Function} onClose
		 * @returns {{onPrepare: Function, onProgress: Function, onReady: Function, onError: Function}}
		 */
		getPopup: function(title, type, onClose) {
	var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();
	var progressEl;

	mono.create(template.textContainer, {
	 append: [
		mono.create('p', {
			text: title,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '13px'
			}
		}),
		progressEl = mono.create('p', {
			text: '',
			style: {
		color: '#868686',
		fontSize: '14px',
		lineHeight: '24px'
			}
		})
	 ]
	});

	var popupEl = SaveFrom_Utils.popupDiv(template.body, 'sf_progress_popup', undefined, undefined, onClose);

	var setState = function(state) {
	 if (setState.state === state) {
		return;
	 }
	 setState.state = state;

	 template.buttonContainer.style.display = 'none';
	 progressEl.style.display = 'none';
	 mono.sendMessage({
		action: 'getWarningIcon',
		type: type,
		color: '#77D1FA'
	 }, function(icon) {
		template.icon.style.backgroundImage = 'url(' + icon + ')';
	 });
	 if (state === 'progress') {
		progressEl.style.display = 'block';
	 }
	 if (state === 'error') {
		mono.sendMessage({
			action: 'getWarningIcon',
			type: type,
			color: '#AAAAAA'
		}, function(icon) {
			template.icon.style.backgroundImage = 'url(' + icon + ')';
		});
		progressEl.style.display = 'block';
	 }
	};

	return {
	 onPrepare: function(text) {
		setState('progress');
		progressEl.textContent = text;
	 },
	 onProgress: function(count, max) {
		progressEl.textContent = language.vkFoundFiles.replace('%d', count) + ' ' + language.vkFoundOf + ' ' + max;
	 },
	 onReady: function() {
		mono.trigger(popupEl, 'kill');
	 },
	 onError: function(text) {
		setState('error');
		progressEl.textContent = text;
	 }
	}
		},
		getLayer: function() {
	var layer = document.getElementById('layer_wrap');
	if (layer === null || layer.style.display === "none" || layer.textContent.length === 0) {
	 layer = null;
	}
	if (layer === null) {
	 layer = document.getElementById('wk_layer_wrap');
	 if (layer === null || layer.style.display === "none" || layer.textContent.length === 0) {
		layer = null;
	 }
	}
	return layer;
		},
		_getAlbumLinks: function(id, details) {
	var _this = this;
	var cache = _this.photoCache;
	var title = '';
	if (/albums|tags|photos/.test(location.href)) {
	 title = getFolderName();
	}

	var dblId = {};
	var albumPhotoList = [];
	var pagePhotoCount = 0;
	var albumPhotoCount = 0;
	var pageCount = 0;
	var pageIndex = 0;

	var requestPage = function(offset) {
	 if (details.abort) {
		return Promise.reject(new Error('Abort'));
	 }

	 var postData = {
		act: 'show',
		al: 1,
		list: id
	 };
	 if (offset) {
		postData.offset = offset;
	 }
	 return mono.requestPromise({
		type: 'POST',
		headers: {
			'Content-Type': 'application/x-www-form-urlencoded',
			'X-Requested-With': 'XMLHttpRequest'
		},
		url: '/al_photos.php',
		data: postData,
		localXHR: true,
		timeout: 60 * 1000
	 }).then(function(response) {
		var data = parseVkResponse(response.body);

		var count = data[6];
		var photos = data[8];

		if (!pagePhotoCount) {
			pagePhotoCount = photos.length;
		}

		albumPhotoCount = count;

		if (!pageCount) {
			pageCount = Math.ceil(count / pagePhotoCount);
		}

		var pageList = [];
		var pageTitle = '';

		photos.forEach(function(item) {
			if (!dblId[item.id]) {
		dblId[item.id] = 1;
		var photoInfo = _this.getMaxPhotoSize(item);
		if (photoInfo) {
		 if (!pageTitle && item.album) {
			pageTitle = mono.fileName.decodeSpecialChars(mono.decodeUnicodeEscapeSequence(item.album.replace(/<[^>]+>/g, '')));
		 }
		 photoInfo.id = item.id;
		 cache[item.id] = photoInfo;
		 pageList.push(photoInfo);
		}
			}
		});

		return new Promise(function(resolve) {
			setTimeout(resolve, 250);
		}).then(function() {
			return {
		title: pageTitle,
		list: pageList
			};
		});
	 });
	};
	var getPage = function() {
	 var offset = pageIndex * pagePhotoCount;
	 return requestPage(offset).then(function(result) {
		pageCount--;
		pageIndex++;

		albumPhotoList.push.apply(albumPhotoList, result.list);

		if (details.onProgress) {
			details.onProgress(albumPhotoList.length, albumPhotoCount);
		}

		if (!title) {
			title = result.title;
		}
		if (pageCount > 0) {
			return getPage();
		}
	 });
	};
	return getPage().then(function() {
	 Object.keys(cache).slice(1000).forEach(function(id) {
		delete cache[id];
	 });

	 if (!albumPhotoList.length) {
		throw new Error('Album is empty');
	 }

	 if (!title) {
		title = getFolderName();
	 }

	 return {
		title: title,
		list: albumPhotoList
	 }
	}, function(e) {
	 if (e.message !== 'Abort') {
		mono.debug('Get photo page error!', e);
	 }
	 throw e;
	});
		},
		_getPhotoLinks: function(photoId, listId, details) {
	var _this = this;
	var requestPhoto = function(curModule) {
	 if (details.abort) {
		return Promise.reject(new Error('Abort'));
	 }

	 var postData = {
		act: 'show',
		al: 1,
		list: listId,
		module: curModule,
		photo: photoId
	 };
	 return mono.requestPromise({
		type: 'POST',
		headers: {
			'Content-Type': 'application/x-www-form-urlencoded',
			'X-Requested-With': 'XMLHttpRequest'
		},
		url: '/al_photos.php',
		data: postData,
		localXHR: true,
		timeout: 60 * 1000
	 }).then(function(response) {
		var data = parseVkResponse(response.body);

		var photos = data[8];
		var photoInfo = null;

		photos.some(function(item) {
			if (item.id === photoId) {
		photoInfo = _this.getMaxPhotoSize(item);
		return true;
			}
		});

		if (!photoInfo) {
			throw new Error('Photo is is not found!');
		}

		return new Promise(function(resolve) {
			setTimeout(resolve, 250);
		}).then(function() {
			return photoInfo;
		});
	 });
	};

	return _this._getModuleName().then(function(curModule) {
	 return requestPhoto(curModule);
	}).catch(function(e) {
	 if (e.message !== 'Abort') {
		mono.debug('Get photo error!', e);
	 }
	 throw e;
	});
		},
		_getModuleName: function() {
	var dataArg = 'sfModule';
	return new Promise(function(resolve, reject) {
	 var script = mono.create('script', {
		text: '(' + function(dataArg) {
			if (window.cur && window.cur.module && typeof(window.cur.module) === 'string') {
		document.body.dataset[dataArg] = window.cur.module;
			}
		}.toString() + ')(' + JSON.stringify(dataArg) + ');'
	 });
	 document.body.appendChild(script);
	 setTimeout(function() {
		script.parentNode.removeChild(script);
		resolve(document.body.dataset[dataArg]);
	 }, 0);
	});
		},
		_getAlbumLinksViaDom: function(container, details) {
	var _this = this;
	var cache = _this.photoCache;

	if (details.abort) {
	 return Promise.reject(new Error('Abort'));
	}

	var paramsRe = /showPhoto\s*\(\s*["']([-\d_]+)["']\s*,\s*["']([\w\-]+)["']/i;
	var jsonRe = /\{["']?temp["']?\s*:\s*(\{.+?\})/i;
	var jsonReplaceRe = /(\{|,)\s*(\w+)\s*:/ig;
	var dDbl = {};
	var list = [];
	var getFullSizeSrc = function(_list) {
	 var promise = Promise.resolve();
	 var list = [];

	 var cloneList = _list.filter(function(item) {
		var photo = cache[item.id];
		if (photo) {
			list.push(photo);
			return false;
		} else {
			return true;
		}
	 });

	 if (details.onProgress) {
		details.onProgress(list.length, _list.length);
	 }

	 cloneList.forEach(function(_photoInfo) {
		promise = promise.then(function() {
			return _this._getPhotoLinks(_photoInfo.id, _photoInfo.listId, details).then(function(photoInfo) {
		cache[_photoInfo.id] = photoInfo;
		list.push(photoInfo);

		if (details.onProgress) {
		 details.onProgress(list.length, _list.length);
		}
			}, function(e) {
		if (e.message === 'Abort') {
		 throw e;
		}

		if (_photoInfo.url) {
		 list.push(_photoInfo);
		 if (details.onProgress) {
			details.onProgress(list.length, _list.length);
		 }
		 mono.debug('Photo link from dom', e);
		}
			});
		});
	 });
	 promise = promise.then(function() {
		Object.keys(cache).slice(1000).forEach(function(id) {
			delete cache[id];
		});

		if (!list.length) {
			throw new Error('Photos is not found');
		}

		return {
			list: list
		};
	 });
	 return promise;
	};
	var getPhotoIdFromLink = function(node) {
	 if (photo.isReply(node) || audio.elIsHidden(node)) {
		return;
	 }
	 var onClick = node.getAttribute('onclick');
	 var params = paramsRe.exec(onClick);
	 if (params) {
		var photoId = params[1];
		if (!dDbl[photoId]) {
			dDbl[photoId] = 1;
			var listId = params[2];
			var photoInfo = null;

			var json = jsonRe.exec(onClick);
			if (json) {
		json = json[1].replace(jsonReplaceRe, '$1"$2":');
		var obj = null;
		try {
		 obj = JSON.parse(json);
		} catch (err) {}

		photoInfo = obj && _this.getMaxPhotoSize(obj);
			}

			if (!photoInfo) {
		photoInfo = {};
			}

			photoInfo.id = photoId;
			photoInfo.listId = listId;

			list.push(photoInfo);
		}
	 }
	};
	[].slice.call(container.querySelectorAll('a[onclick]')).forEach(getPhotoIdFromLink);
	if (list.length === 0 && container !== document) {
	 var postContainer = _this.getWallPostContent();
	 if (postContainer) {
		[].slice.call(postContainer.querySelectorAll('a[onclick]')).forEach(getPhotoIdFromLink);
	 }
	}
	return getFullSizeSrc(list);
		},
		_getLinks: function(container, id) {
	var _this = this;
	var promise = Promise.resolve();
	var details = {};

	var popup = _this.getPopup(getFolderName(), 'photo', function onClose() {
	 details.abort = true;
	});

	details.onProgress = function(count, max) {
	 popup.onProgress(count, max);
	};

	popup.onPrepare(language.download + ' ...');

	var listToLinks = function(list) {
	 var links = [];
	 list.forEach(function(item) {
		var url = item.url;
		var filename = _this.getFilenameFromUrl(url);
		if (!filename) {
			filename = 'unknown.jpg';
		}
		links.push({
			filename: filename,
			url: url
		});
	 });

	 var zeroCount = String(links.length).length;
	 links.forEach(function(item, index) {
		var number = String(index + 1);
		while (number.length < zeroCount) {
			number = '0' + number;
		}
		item.filename = number + '-' + item.filename;
	 });

	 return links;
	};

	var findPhotoAlbumLinks = function() {
	 return _this._getAlbumLinks(id, details);
	};

	var findPhotoAllLinks = function() {
	 container = container || document;
	 if (container === document) {
		var layer = _this.getLayer();
		if (layer) {
			container = layer;
		}
	 }
	 return _this._getAlbumLinksViaDom(container, details);
	};

	if (id) {
	 promise = promise.then(findPhotoAlbumLinks).catch(function(e) {
		if (e.message !== 'Album is empty' && e.message !== 'Abort') {
			mono.debug('findAlbumLinks error', e);
		}
		throw e;
	 }).catch(function() {
		return findPhotoAllLinks();
	 });
	} else {
	 promise = promise.then(findPhotoAllLinks);
	}

	promise = promise.then(function(result) {
	 var title = result.title;
	 var list = result.list;

	 var links = listToLinks(list);

	 popup.onReady();

	 if (!title) {
		title = getFolderName();
	 }

	 if (!allowDownloadMode) {
		_this.showListOfLinks(title, links, true);
	 } else {
		SaveFrom_Utils.downloadList.showBeforeDownloadPopup(links, {
			count: links.length,
			folderName: title,
			type: 'photo',
			onShowList: function() {
		// show list on links
		_this.showListOfLinks(title, links, true);
			}
		});
	 }
	}, function(e) {
	 if (e.message !== 'Abort') {
		mono.debug('_getLinks error', e);
	 }

	 popup.onError(language.noLinksFound);
	});

	return promise;
		},
		rmPhotoAlbumDlBtn: function() {
	var dlAlbumBtn = document.querySelectorAll(['.sf-dl-ablum-btn-divide', '.sf-dl-ablum-btn']);
	for (var i = 0, item; item = dlAlbumBtn[i]; i++) {
	 item.parentNode.removeChild(item);
	}
		},
		addNewPhotoAlbumDlBtn: function(container) {
	var _this = this;

	var photosAlbumIntroInfo = container.querySelector('.photos_album_intro_info');
	var pageBlockHeaderExtra = container.querySelector('.page_block_header_extra');

	var body = photosAlbumIntroInfo || pageBlockHeaderExtra;

	if (!body || body.querySelector('.sf-dl-ablum-btn')) {
	 return;
	}

	var dlBtn = mono.create('a', {
	 text: language.vkDownloadPhotoAlbum,
	 href: '#',
	 class: 'sf-dl-ablum-btn',
	 on: ['click', function(e) {
		e.preventDefault();

		var id = photo.getAlbumId(location.href);
		_this._getLinks(container, id);

	 }]
	});

	var dlBtnWrapper = mono.create('span', {
	 append: dlBtn
	});

	if (photosAlbumIntroInfo) {
	 dlBtnWrapper.classList.add('photos_album_info');
	 dlBtnWrapper = mono.create(document.createDocumentFragment(), {
		append: [
			mono.create('span', {
		class: 'divide sf-dl-ablum-btn-divide',
		text: '|'
			}),
			dlBtnWrapper
		]
	 });
	} else
	if (pageBlockHeaderExtra) {
	 dlBtnWrapper.classList.add('photos_comments_link');
	 dlBtnWrapper.style.margin = '0 15px';
	}

	body.appendChild(dlBtnWrapper);
		},
		getContainer: function() {
	var container = document.getElementById('photos_albums_container');
	if (!container) {
	 container = document.getElementById('photos_container');
	}

	return container;
		},
		getFilenameFromUrl: function(url) {
	var fnRe = /\/([\w\-]+\.[a-z0-9]{3,4})(?:\?|$)/i;
	var fn = fnRe.exec(url);
	fn = fn && fn[1] || '';
	return fn;
		},
		rmCurrentPhotoBtn: function(insertContainer) {
	var exBtn = undefined;
	var imgList = document.querySelectorAll('.sf-dl-current-photo-btn');
	for (var i = 0, imgItem; imgItem = imgList[i]; i++) {
	 if (!insertContainer || !insertContainer.contains(imgItem)) {
		imgItem.parentNode.removeChild(imgItem);
	 } else {
		exBtn = imgItem;
	 }
	}
	return exBtn;
		},
		style: null,
		injectStyle: function() {
	if (this.style) {
	 if (!this.style.parentNode) {
		document.head.appendChild(this.style);
	 }
	 return;
	}

	this.style = mono.create('style', {
	 text: mono.style2Text({
		'div > .sf-dl-current-photo-btn': {
			display: 'none',
			border: '1px solid #F8F8F8',
			width: '20px',
			height: '20px',
			padding: 0,
			position: 'absolute',
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#777777') + ') center no-repeat #F8F8F8',
			backgroundSize: '12px',
			top: '20px',
			left: '30px',
			zIndex: 10,
			cursor: 'pointer'
		},
		'div > .sf-dl-current-photo-btn.sf-style-black': {
			border: 0,
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#FFF') + ') center no-repeat #000',
			backgroundSize: '14px',
			padding: '2px 4px',
			borderRadius: '2px',
			opacity: 0.4,
			transition: 'opacity 100ms linear'
		},
		'div > .sf-dl-current-photo-btn:hover': {
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #F8F8F8',
			backgroundSize: '12px',
			opacity: 0.8
		},
		'div > .sf-dl-current-photo-btn.sf-style-black:hover': {
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #000',
			backgroundSize: '14px'
		},
		'div > .sf-dl-current-photo-btn:active': {
			outline: 0,
			boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
		},
		'div:hover > .sf-dl-current-photo-btn': {
			display: 'block'
		}
	 })
	});

	document.head.appendChild(this.style);
		},
		getMaxPhotoSize: function(data) {
	var t = null,
	 r = null;
	["w", "z", "y", "x"].some(function(key) {
	 if (t = data[key + '_']) {
		return true;
	 }
	 if (r = data[key + '_src']) {
		return true;
	 }
	});

	if (!t) {
	 t = [r];
	}

	var genUrl = function(baseUrl, url) {
	 if (!url.match(/\.[a-z]{3}$/i)) {
		url += ".jpg";
	 }

	 if (url.match(/https?:\/\//i)) {
		return url;
	 }

	 return (baseUrl || '').replace(/\/[a-z0-9_:\.]*$/i, '') + '/' + url;
	};

	if (!t[0]) {
	 return null;
	}

	var info = {
	 url: genUrl(data.base, t[0]),
	 width: t[2] && t[1],
	 height: t[1] && t[2]
	};

	return info;
		},
		getNewCurrentPhotoLink: function(id, cb) {
	var _this = this;
	if (!id) {
	 return cb("ID is empty!");
	}

	return SaveFrom_Utils.bridge({
	 args: [id],
	 func: function(id, cb) {
		var data = {};

		if (typeof cur !== 'undefined' && cur.pvCurPhoto) {
			if (cur.pvCurPhoto.id === id) {
		data = cur.pvCurPhoto;
			}
		}

		return cb(data);
	 },
	 cb: function(data) {
		if (!data || !data.id) {
			return cb("ID is not found!");
		}

		var photoInfo = _this.getMaxPhotoSize(data);
		if (!photoInfo) {
			return cb('URL is not found!');
		}

		return cb(null, photoInfo);
	 },
	 timeout: 300
	});
		},
		addNewDlCurrentPhotoBtn: function(container) {
	var insertContainer = container;
	var exBtn = this.rmCurrentPhotoBtn(insertContainer);
	if (exBtn) {
	 return;
	}

	var _this = this;

	var pvPhoto = container.querySelector('a#pv_photo[href]');
	if (!pvPhoto) {
	 return;
	}

	var btn = mono.create('a', {
	 class: ['sf-dl-current-photo-btn', 'sf-style-black'],
	 href: '#',
	 title: language.download,
	 on: [
		['click', function(e) {
			e.stopPropagation();
			e.preventDefault();

			mono.onRemoveEvent(this, vk.hideMenu);

			if (vk.contextMenu && vk.contextMenu.isShow) {
		vk.hideMenu();
		return;
			}

			var menu = vk.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, language.download + ' ...', "photoDlMenu", {
		parent: insertContainer
			});

			var abort = function() {
		return menu.update(language.noLinksFound);
			};

			var id = pvPhoto.href.match(/photo(-?\d+_\d+)/);
			id = id && id[1];

			return _this.getNewCurrentPhotoLink(id, function(err, info) {
		if (err) {
		 return abort();
		}

		var photoFileName = mono.fileName.modify(_this.getFilenameFromUrl(info.url));
		var dotPos = photoFileName.lastIndexOf('.');
		var photoExt = photoFileName.substr(dotPos + 1);
		var photoTitle = photoFileName.substr(0, dotPos);

		var menuItems = [];

		menuItems.push({
		 href: info.url,
		 title: photoTitle,
		 quality: language.download,
		 format: ' ',
		 ext: photoExt,
		 forceDownload: true,
		 isOther: true,
		 isBlank: true,
		 func: function() {
			menu.hide();
		 }
		});

		menuItems.push({
		 href: '#getAlbum',
		 title: '',
		 quality: language.vkDownloadPhotoAlbum,
		 format: ' ',
		 ext: '',
		 noSize: true,
		 isOther: true,
		 func: function(e) {
			e.preventDefault();

			photo.downloadPhoto();
			menu.hide();
		 }
		});

		menu.update(menuItems);
			});
		}],
		['mousedown', function(e) {
			e.stopPropagation();
		}]
	 ]
	});

	SaveFrom_Utils.mutationAttrWatcher.run({
	 target: pvPhoto,
	 attr: ['href'],
	 callback: function() {
		if (vk.contextMenu && vk.contextMenu.isShow) {
			vk.hideMenu();
		}
	 }
	});

	insertContainer.appendChild(btn);
		},
		downloadPhoto: function() {
	var _this = this;
	var container = _this.getContainer();
	var id = _this.getAlbumId(location.href);
	if (!id) {
	 var link = document.querySelector('.pv_album_name a');
	 if (link && !audio.elIsHidden(link)) {
		id = _this.getAlbumId(link.href);
	 }
	}
	_this._getLinks(container, id);
		},
		showListOfPhotosContent: function(title, links) {
	var html = '<!DOCTYPE html><html>' + mono.create('html', {
	 append: [
		mono.create('head', {
			append: [
		mono.create('meta', {
		 attr: {
			charset: 'utf-8'
		 }
		}),
		mono.create('style', {
		 text: 'a,img{display:block;margin-bottom:5px;}p{width: 640px}'
		})
			]
		}),
		mono.create('body', {
			append: [
		title,
		mono.create('p', {
		 text: language.vkListOfPhotosInstruction
		}),
		mono.create('br'),
		mono.create('br'),
		(function() {
		 var nodeList = document.createDocumentFragment();
		 links.forEach(function(item) {
			var src = item.url;
			var fileName = item.filename || '';

			var img = mono.create('img', {
				src: src,
				alt: 'photo'
			});

			if (fileName) {
				img = mono.create('a', {
			href: src,
			download: fileName,
			append: img
				});
			}

			nodeList.appendChild(img);
		 });
		 return nodeList;
		})()
			]
		})
	 ]
	}).innerHTML + '</html>';

	return html;
		},
		showListOfLinks: function(title, links, showListOfPhotosLink) {
	var listOfPhotoLink;
	if (showListOfPhotosLink) {
	 listOfPhotoLink = mono.create(document.createDocumentFragment(), {
		append: [
			mono.create('p', {
		append: [
		 mono.create('a', {
			text: language.vkListOfPhotos,
			href: '#',
			class: 'sf__hidden',
			style: {
				fontWeight: 'bolder',
				border: 'none',
				textDecoration: 'underline'
			},
			on: ['click', function(e) {
				e.preventDefault();
				var html = photo.showListOfPhotosContent(title, links);
				var url = '';
				if (mono.isChrome || mono.isTM) {
			url = mono.getDataUrl(html, 'text/html', true);
			mono.sendMessage({
			 action: 'openTab',
			 url: url
			});
				} else {
			url = mono.getDataUrl(html, 'text/html');
			window.open(url, '_blank');
				}
			}]
		 })
		]
			})
		]
	 });
	} else {
	 listOfPhotoLink = '';
	}

	var textareaText = '';
	for (var i = 0, item; item = links[i]; i++) {
	 textareaText += item.url + '\r\n';
	}

	var textareaNode;
	var container = mono.create(document.createDocumentFragment(), {
	 append: [
		mono.create('p', {
			text: title,
			style: {
		color: '#0D0D0D',
		fontSize: '20px',
		marginBottom: '11px',
		marginTop: '5px'
			}
		}),
		mono.create('p', {
			append: mono.parseTemplate(language.vkListOfLinksInstruction)
		}),
		listOfPhotoLink,

		textareaNode = mono.create('textarea', {
			text: textareaText,
			cols: 60,
			rows: 10,
			style: {
		width: '100%'
			}
		}),
		(!mono.isChrome && !mono.isFirefox) ? undefined : mono.create('button', {
			text: language.copy,
			style: {
		height: '27px',
		backgroundColor: '#ffffff',
		border: '1px solid #9e9e9e',
		marginTop: '6px',
		paddingLeft: '10px',
		paddingRight: '10px',
		borderRadius: '5px',
		fontSize: '14px',
		cursor: 'pointer',
		cssFloat: 'right'
			},
			on: ['click', function(e) {
		var _this = this;
		_this.disabled = true;
		if (mono.isFirefox) {
		 textareaNode.select();
		 document.execCommand("copy");
		} else {
		 mono.sendMessage({
			action: 'addToClipboard',
			text: textareaText
		 });
		}
		setTimeout(function() {
		 _this.disabled = false;
		}, 1000);
			}],
			append: mono.create('style', {
		text: mono.style2Text({
		 '#savefrom_popup_box button:hover:not(:disabled)': {
			backgroundColor: '#597A9E !important',
			borderColor: '#597A9E !important',
			color: '#fff'
		 },
		 '#savefrom_popup_box button:active': {
			opacity: 0.9
		 }
		})
			})
		})
	 ]
	});

	SaveFrom_Utils.popupDiv(container);
		}
	};

	// /PHOTO
	///////////////////////////////////////////////////////////////////

	// do...

	var mVk = {
		mobileMenu: null,
		observer: null,
		styleEl: null,
		run: function() {
	"use strict";
	var _this = this;

	if (!SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 // console.error('MutationObserver is not supported!');
	 return;
	}

	if (_this.observer) {
	 return _this.observer.start();
	}

	_this.observer = SaveFrom_Utils.mutationWatcher.run({
	 callback: function(summaryList) {
		var summary, n, node;

		summary = summaryList[0];
		for (n = 0; node = summary.added[n]; n++) {
			if (node.dataset.sfSkip > 0) {
		continue;
			}
			node.dataset.sfSkip = '1';

			_this.insertAudioBtn(node);
		}

		summary = summaryList[1];
		for (n = 0; node = summary.added[n]; n++) {
			if (!node.parentNode.classList.contains('video_view')) {
		continue;
			}

			if (node.dataset.sfSkip > 0) {
		continue;
			}
			node.dataset.sfSkip = '1';

			_this.insertVideoBtn(node);
		}

		summary = summaryList[2];
		for (n = 0; node = summary.removed[n]; n++) {
			mono.onRemoveListener(node);
		}
	 },
	 queries: [{
			css: 'div.audio_item',
			is: 'added'
		},
		{
			css: 'div.vv_body',
			is: 'added'
		},
		{
			css: '.' + mono.onRemoveClassName,
			is: 'removed'
		}
	 ]
	});

	_this.insertStyle();
		},
		hideMenu: function() {
	if (mVk.mobileMenu) {
	 mVk.mobileMenu.hide();
	 mVk.mobileMenu = null;
	}
		},
		insertStyle: function() {
	"use strict";
	if (this.styleEl) {
	 if (!this.styleEl.parentNode) {
		document.head.appendChild(this.styleEl);
	 }
	 return;
	}

	this.styleEl = mono.create('style', {
	 class: 'sf-style',
	 text: mono.style2Text([{
		selector: '.' + downloadLinkClassName + '.sf-audio',
		style: {
			display: 'block',
			float: 'right',
			borderRadius: '3px',
			width: '22px',
			height: '22px',
			marginTop: '1px',
			marginLeft: '3px',
			marginRight: '3px',
			background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#ffffff') + ') center no-repeat',
			backgroundSize: '12px',
			backgroundColor: '#5E80AA'
		}
	 }, {
		selector: '.audio_item .' + downloadLinkClassName + '.sf-audio',
		style: {
			position: 'absolute',
			right: '32px',
			top: 0,
			bottom: 0,
			margin: 'auto'
		}
	 }, {
		selector: '.audio_item.ai_current .' + downloadLinkClassName + '.sf-audio',
		style: {
			bottom: 'auto',
			top: '6px'
		}
	 }])
	});
	document.head.appendChild(this.styleEl);
		},
		onAudioBtnClick: function(e) {
	"use strict";
	e.stopPropagation();

	SaveFrom_Utils.downloadOnClick(e);
		},
		getAudioDlBtnNode: function(title, url) {
	"use strict";
	return mono.create('a', {
	 class: [downloadLinkClassName, 'sf-audio'],
	 href: url,
	 download: mono.fileName.modify(title),
	 target: '_blank',
	 on: ['click', this.onAudioBtnClick],
	 title: language.download
	});
		},
		insertAudioBtn: function(node) {
	"use strict";
	var _this = this;
	var title = node.querySelector('.ai_label');
	title = title && title.textContent || '';
	if (title) {
	 title += '.mp3';
	}

	var url = node.querySelector('input');
	url = url && url.value;
	if (!url) {
	 return;
	}

	var aiDur = node.querySelector('.ai_dur');

	if (!aiDur) {
	 return;
	}

	var parent = aiDur.parentNode;

	audio.unmaskUrlViaPlayer([
	 [null, null, url]
	]).then(function(idsArr) {
	 url = idsArr[0][2];
	 var btn = _this.getAudioDlBtnNode(title, url);

	 var exBtn = parent.querySelector('.' + downloadLinkClassName);
	 if (exBtn) {
		exBtn.parentNode.replaceChild(btn, exBtn);
	 } else {
		var nextEl = aiDur.nextElementSibling;

		if (!nextEl) {
			return;
		}

		parent.insertBefore(btn, nextEl);
	 }
	});
		},
		onVideoBtnClick: function(links, e) {
	"use strict";
	e.preventDefault();
	e.stopPropagation();

	mVk.hideMenu();

	var lightBox = mVk.mobileMenu = SaveFrom_Utils.mobileLightBox.show(language.download + ' ...');

	var mLinks = language.noLinksFound;
	if (links.request) {
	 var onResponse = function(response) {
		if (response && links.request.action === 'getPladformVideo') {
			mLinks = video.prepareLinks(video.preparePladformLinks(response));
		} else {
			var hosting = SaveFrom_Utils.embedDownloader.reMapHosting(response.action);
			if (hosting && response && response.links) {
		mLinks = SaveFrom_Utils.popupMenu.prepareLinks[hosting](response.links, response.title);
			}
		}
		lightBox.update(mLinks);
	 };

	 try {
		mono.sendMessage(links.request, onResponse);
	 } catch (e) {
		onResponse();
	 }
	} else {
	 mLinks = video.prepareLinks(links);
	 lightBox.update(mLinks);
	}
		},
		appendVideoBtn: function(links, layer) {
	"use strict";
	var container = layer.querySelector('.mv_actions');
	var btn = mono.create('li', {
	 class: [downloadLinkClassName, 'sf-video-ctr'],
	 append: mono.create('a', {
		class: [downloadLinkClassName, 'mva_item'],
		text: language.download,
		on: ['click', this.onVideoBtnClick.bind(this, links)]
	 })
	});
	container && container.appendChild(btn);
	mono.onRemoveEvent(btn, mVk.hideMenu);
		},
		insertVideoBtn: function(node) {
	"use strict";
	var _this = this;
	var firstChild = node.querySelectorAll('iframe, video, a')[0];

	var ctr = mono.getParentByClass(node, 'video_view');

	var getLinksFromUrl = function() {
	 var m = /video(-?\d+)_(-?\d+)/.exec(location.href);
	 return m && {
		request: {
			hosting: 'vk',
			action: 'getVKLinks',
			extVideoId: 'video' + m[1] + '_' + m[2]
		}
	 };
	};

	if (!firstChild) {
	 if (node.querySelector('.vv_not_support')) {
		var links = getLinksFromUrl();
		if (links) {
			_this.appendVideoBtn(getLinksFromUrl(), ctr);
		}
	 }
	} else {
	 video.getLinksFromPlayer(ctr, firstChild, function(links, ctr) {
		if (!links) {
			links = getLinksFromUrl();
		}
		if (links) {
			_this.appendVideoBtn(links, ctr);
		}
	 });
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.vk.com/*',
	'*://*.vkontakte.ru/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('youtube', function(moduleName, initData) {
	"use strict";

	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleYoutube ? 1 : 0;
	var allowDownloadMode = mono.isChrome || mono.isFirefox || (mono.isGM && mono.isTM);
	var iframe = mono.isIframe();

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return youtube.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	var vId = youtube.getIdFromLocation();
	if (vId) {
	 getPlayerConfig(function(config) {
		if (config && config.args && config.args.video_id === vId) {
			var oldBtn = document.getElementById(youtube.buttonId);
			if (oldBtn !== null) {
		oldBtn.parentNode.removeChild(oldBtn);
			}

			ytUmmyBtn.rmBtn();

			youtube.responseCache = {};
			youtube.video_id = config.args.video_id;
			var container = document.getElementById('watch7-subscription-container');
			youtube.appendDownloadButton(container);
		}
	 });
	}
		}
		if (message.action === 'downloadPlaylist') {
	youtube.downloadPlaylist();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	youtube.run();
		});
	}

	var youtube = {
		swfargs: null,
		video_id: '',

		buttonId: 'savefrom__yt_btn',

		responseCache: {},
		isMobile: false,

		mobileMenu: null,
		currentMenu: null,
		currentTutorial: null,

		run: function() {
	moduleState = 1;
	if (iframe) {
	 var embedVideoId = location.href.match(/\/embed\/([\w\-]+)/i);
	 embedVideoId = embedVideoId && embedVideoId[1];

	 if (!embedVideoId) {
		iframe = false;
	 }
	}

	if (location.host.indexOf('m.') === 0) {
	 youtube.isMobile = true;

	 if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
		youtube.mobileMutationMode.enable();
	 }
	 return;
	}

	if (iframe) {
	 youtube.video_id = embedVideoId;

	 youtube.appendFrameButton();
	 return;
	}

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 youtube.videoFeed.injectStyle();
	 youtube.mutationMode.enable();
	}
		},

		changeState: function(state) {
	moduleState = state;
	if (iframe || youtube.isMobile) {
	 return;
	}

	youtube.tutorial.hide();

	youtube.hideCurrentMenu();

	youtube.videoFeed.rmBtn();

	youtube.mutationMode.stop();

	var btn = document.getElementById(youtube.buttonId);
	if (btn) {
	 btn.parentNode.removeChild(btn);
	}

	ytUmmyBtn.rmBtn();

	if (state) {
	 youtube.run();
	}
		},

		hideCurrentMenu: function() {
	if (youtube.currentMenu) {
	 youtube.currentMenu.hide();
	 youtube.currentMenu = null;
	}
		},

		hideMobileMenu: function() {
	if (youtube.mobileMenu) {
	 youtube.mobileMenu.hide();
	 youtube.mobileMenu = null;
	}
		},

		mutationMode: {
	observer: null,
	stop: function() {
	 if (youtube.mutationMode.observer) {
		youtube.mutationMode.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	wrapVideoFeedOnImgHover: function() {
	 if (!moduleState || !youtube.videoFeed.testUrl(location.href)) {
		return;
	 }

	 if (this.dataset.sfBtn > 0) {
		return;
	 }
	 this.dataset.sfBtn = '1';

	 var context = JSON.parse(this.dataset.sfContext);
	 this.appendChild(youtube.videoFeed.getBtnNode(context.id, context.styleIndex));
	},
	wrapNewVideoFeedOnThumbnailHover: function() {
	 if (!moduleState || !youtube.videoFeed.testUrl(location.href)) {
		return;
	 }

	 var _this = youtube.mutationMode;
	 var node = this;
	 var url = node.href;
	 var m = url && url.match(youtube.videoFeed.hrefIdPattern);
	 var id = m && m[1];
	 if (id) {
		if (!node.querySelector('.sf-feed-btn')) {
			var btnNode = youtube.videoFeed.getBtnNode(id, 2);
			mono.onRemoveEvent(btnNode, function() {
		if (!this.parentNode) {
		 mono.one(node, 'mouseenter', _this.wrapNewVideoFeedOnThumbnailHover);
		}
			});
			node.appendChild(btnNode);
		}
	 }
	 url = null;
	 m = null;
	 id = null;
	 btnNode = null;
	},
	enable: function() {
	 var _this = this;

	 if (_this.observer) {
		return _this.observer.start();
	 }

	 _this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, node, n, i, id, el, context;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		youtube.appendDownloadButton(node);
			}

			if (youtube.videoFeed.testUrl(location.href)) {
		for (i = 1; i < 3; i++) {
		 summary = summaryList[i];
		 for (n = 0; node = summary.added[n]; n++) {
			if (node.dataset.sfSkip > 0) {
				continue;
			}
			node.dataset.sfSkip = '1';

			id = node.dataset.videoIds;
			if (id) {
				el = node.parentNode;
				context = {};
				context.id = id;
				context.styleIndex = 1;
				el.dataset.sfContext = JSON.stringify(context);
				mono.one(el, 'mouseenter', _this.wrapVideoFeedOnImgHover);
			}
		 }
		}

		for (i = 3; i < 4; i++) {
		 summary = summaryList[i];
		 for (n = 0; node = summary.added[n]; n++) {
			if (node.dataset.sfSkip > 0) {
				continue;
			}
			node.dataset.sfSkip = '1';

			mono.one(mono.getParent(node, 'a'), 'mouseenter', _this.wrapNewVideoFeedOnThumbnailHover);
		 }
		}
			}

			summary = summaryList[4];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		youtube.appendNewDownloadButton(node);
			}

			summary = summaryList[5];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: '#watch7-subscription-container',
		is: 'added'
			},
			{
		css: 'button.addto-watch-later-button-sign-in',
		is: 'added'
			},
			{
		css: 'button.addto-watch-later-button',
		is: 'added'
			},
			{
		css: 'ytd-thumbnail a#thumbnail img',
		is: 'added'
			},
			{
		css: '.ytd-page-manager #info #top-level-buttons',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		},

		mobileMutationMode: {
	observer: null,
	stop: function() {
	 if (youtube.mutationMode.observer) {
		youtube.mutationMode.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	enable: function() {
	 var _this = this;

	 if (_this.observer) {
		return _this.observer.start();
	 }

	 _this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, node, n, i;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.id.indexOf('koya_elem_') !== 0) {
		 continue;
		}

		var ii = 0;
		while (ii < 4 && node) {
		 node = node.parentNode;
		 ii++;
		}

		if (!node || ii !== 4) {
		 continue;
		}

		var styleIndex = 1;

		// check is single video
		var firstChild = node.querySelector('div:nth-child(1)>h1:nth-child(1)');
		if (!firstChild) {
		 firstChild = null;
		}

		// check is playlist
		if (!firstChild) {
		 if (mono.matches(node, 'div > div > div[id^="koya_child_"]:last-child > div')) {
			firstChild = node;
			ii = 0;
			while (ii < 3 && firstChild) {
				firstChild = firstChild.parentNode;
				ii++;
			}
			if (!firstChild || !firstChild.querySelector('div:nth-child(1)>h2:nth-child(1)')) {
				firstChild = null;
			} else {
				node = node.parentNode;
				styleIndex = 2;
			}
		 }
		}

		if (!firstChild) {
		 continue;
		}

		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		youtube.appendMobileButton('', node, styleIndex);
			}

			summary = summaryList[1];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: 'div > div > div > a[onclick][href="#"] > span[id]',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		},

		_onSelectBtnClick: function(wrapBtnObj, e) {
	if (e.button > 0) {
	 return;
	}

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

	if (youtube.currentMenu && youtube.currentMenu.isShow) {
	 youtube.hideCurrentMenu();
	 return;
	}

	var btnObj = wrapBtnObj.btnObj;

	var videoId = btnObj.videoId;

	var menu = youtube.currentMenu = SaveFrom_Utils.popupMenu.quickInsert(btnObj.node, language.download + ' ...', 'sf-popupMenu', {
	 onShow: function() {
		mono.onRemoveEvent(btnObj.node, youtube.hideCurrentMenu);
	 },
	 onHide: function() {
		mono.offRemoveEvent(btnObj.node, youtube.hideCurrentMenu);
	 },
	 onItemClick: function(e, menuItem) {
		"use strict";
		var itag = menuItem.itag;
		if (itag === 'ummyAudio') {
			itag = 'ummy';
		}

		btnObj.lastItag !== itag && mono.storage.set({
			ytLastITag: itag
		}, function() {
			youtube.quickBtn.setValue(btnObj);
		});

		youtube.onMenuItemClick(menuItem, {
			isPageItem: 1,
			videoId: videoId
		});
	 },
	 offsetTop: btnObj.popupMenu && btnObj.popupMenu.offsetTop,
	 offsetRight: btnObj.popupMenu && btnObj.popupMenu.offsetRight,
	 parent: btnObj.popupMenu && btnObj.popupMenu.parent
	});

	var abort = function() {
	 menu.update(language.noLinksFound);
	};

	if (!videoId) {
	 return abort();
	}

	var onResponse = function(response) {
	 if (response.links) {
		var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.youtube(response.links, response.title, response.subtitles);
		menu.update(menuLinks);
		return;
	 }

	 abort();
	};

	var response = youtube.responseCache[videoId];
	if (response) {
	 return onResponse(response);
	}

	mono.sendMessage({
	 action: 'getYoutubeLinks',
	 extVideoId: videoId,
	 url: location.href,
	 checkSubtitles: true
	}, function(response) {
	 if (response.links) {
		youtube.responseCache[videoId] = response;
	 }

	 onResponse(response);
	});

	if (btnObj.isFirstMenuShow) {
	 btnObj.isFirstMenuShow = false;
	}
		},
		appendDownloadButton: function(parent) {
	var currentBtn = parent.querySelector('#' + youtube.buttonId);
	if (currentBtn) {
	 currentBtn.parentNode && currentBtn.parentNode.removeChild(currentBtn);
	 currentBtn = null;
	}


	var _this = this;

	var wrapBtnObj = {};
	var btnObj = _this.getButtonUnderVideo(youtube._onSelectBtnClick.bind(null, wrapBtnObj));
	wrapBtnObj.btnObj = btnObj;
	btnObj.isFirstMenuShow = true;

	btnObj.setLoadingState();

	parent.appendChild(btnObj.node);

	return getPlayerConfig(function(config) {
	 var videoId = null;

	 if (config && config.args && config.args.video_id) {
		videoId = config.args.video_id;
	 }

	 if (!videoId) {
		return;
	 }

	 btnObj.node.dataset.sfVideoId = videoId;

	 btnObj.ytConfig = config;
	 btnObj.videoId = videoId;
	 btnObj.isPage = 1;

	 var onGetLinksArr = [];

	 var insertTutorial = function() {
		insertTutorial = null;
		youtube.tutorial.show({
			target: btnObj.node
		});
	 };

	 onGetLinksArr.push(function() {
		return insertTutorial && insertTutorial();
	 });

	 var ummyBtn = ytUmmyBtn(language, preference, videoId);

	 if (ummyBtn) {
		onGetLinksArr.push(function(menuLinks) {
			var links = menuLinks.slice(0);
			links = SaveFrom_Utils.popupMenu.sortMenuItems(links, {
		typeList: ['video', '3d', 'mute', 'mute60'],
		groupCompare: true
			});
			if (links.length === 0) {
		return;
			}

			var best = links[0];
			var quality = best.prop && best.prop.quality;

			if (['8K', '4K', '1440', '1080'].indexOf(quality) === -1) {
		return;
			}

			mono.trigger(ummyBtn, 'changeValue', {
		detail: JSON.stringify(best.prop && best.prop.quality)
			});
		});

		parent.appendChild(ummyBtn);
	 }

	 btnObj.onGetLinksArr = onGetLinksArr;

	 _this.quickBtn.setValue(btnObj);
	});
		},

		appendNewDownloadButton: function(parent, isForce) {
	var _this = this;

	var currentBtn = parent.querySelector('#' + youtube.buttonId);
	if (currentBtn) {
	 if (isForce) {
		return;
	 }
	 currentBtn.classList.remove(mono.onRemoveClassName);
	 currentBtn.parentNode && currentBtn.parentNode.removeChild(currentBtn);
	 currentBtn = null;
	}

	var btnObj = new this.getNewButtonUnderVideo(youtube._onSelectBtnClick);
	btnObj.isFirstMenuShow = true;
	btnObj.setLoadingState();

	var secondBtn = parent.childNodes[2];
	if (!secondBtn) {
	 secondBtn = parent.firstChild;
	}
	if (secondBtn) {
	 parent.insertBefore(btnObj.node, secondBtn);
	} else {
	 parent.appendChild(btnObj.node);
	}

	mono.onRemoveEvent(btnObj.node, function() {
	 if (parent.parentNode) {
		youtube.appendNewDownloadButton(parent, true);
	 }
	});

	btnObj.popupMenu = {
	 offsetTop: -6,
	 offsetRight: -1,
	 parent: btnObj.menuContainer
	};

	return getNewPlayerConfig(function(config) {
	 var videoId = config && config.args && config.args.video_id;
	 if (!videoId) {
		mono.debug('videoId is not found!');
		return;
	 }

	 btnObj.node.dataset.sfVideoId = videoId;
	 btnObj.ytConfig = config;
	 btnObj.videoId = videoId;
	 btnObj.isPage = 1;

	 var onGetLinksArr = [];
	 btnObj.onGetLinksArr = onGetLinksArr;

	 onGetLinksArr.push(function() {
		youtube.tutorial.show({
			target: btnObj.node,
			parent: btnObj.menuContainer,
			btnLeftOffset: -6,
			btnTopOffset: 3
		});
	 });

	 _this.quickBtn.setValue(btnObj);
	});
		},


		getIdFromLocation: function(url) {
	if (!url) {
	 url = document.location.href;
	}

	var videoId = url.match(/\/watch\?(?:.+&)?v=([\w\-]+)/i);
	videoId = videoId && videoId[1];
	if (videoId) {
	 return videoId;
	}

	return null;
		},

		appendMobileButton: function(vid, container, styleIndex) {
	var style = {};

	if (styleIndex === 1) {
	 mono.extend(style, {
		display: 'inline-block',
		height: '28px',
		width: '18px',
		marginRight: '20px',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#757575') + ') center no-repeat',
		cssFloat: 'right'
	 });
	} else
	if (styleIndex === 2) {
	 mono.extend(style, {
		display: 'inline-block',
		height: '38px',
		width: '18px',
		marginRight: '12px',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#757575') + ') center no-repeat',
		backgroundSize: '20px',
		cssFloat: 'right'
	 });
	}

	var button = mono.create('div', {
	 data: {
		id: vid
	 },
	 style: style,
	 on: ['click', function() {
		var vid = this.dataset.id || youtube.getIdFromLocation();

		youtube.hideMobileMenu();

		var lightBox = youtube.mobileMenu = SaveFrom_Utils.mobileLightBox.show(language.download + ' ...');

		var onResponse = function(response) {
			var menuLinks = null;
			if (response && response.links) {
		menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.youtube(response.links, response.title || youtube.getTitleModify());
			}
			lightBox.update(menuLinks);
		};

		var fromCache = youtube.responseCache[vid];
		if (fromCache) {
			return onResponse(fromCache);
		}

		mono.sendMessage({
			action: 'getYoutubeLinks',
			extVideoId: vid,
			url: location.href
		}, function(response) {
			if (response.links) {
		youtube.responseCache[vid] = response;
			}
			onResponse(response);
		});
	 }]
	});

	mono.onRemoveEvent(button, youtube.hideMobileMenu);

	var btnSelector = 'div[id^="koya_child_"]:nth-child(1) > a[href="#"] > span[id^="koya_elem_"]';
	var lastNode = container.lastElementChild;
	if (!lastNode.querySelector(btnSelector)) {
	 lastNode = lastNode.previousElementSibling;
	 if (!lastNode.querySelector(btnSelector)) {
		lastNode = null;
	 }
	}

	if (lastNode) {
	 lastNode = lastNode.nextElementSibling;
	}

	if (lastNode) {
	 lastNode.parentNode.insertBefore(button, lastNode);
	} else {
	 container.appendChild(button);
	}
		},
		getNewButtonUnderVideo: function(onSelectBtnClick) {
	var _this = this;
	this.node = mono.create('div', {
	 id: youtube.buttonId,
	 style: {
		display: [
			'-ms-flexbox',
			'-webkit-flex',
			'flex'
		],
		marginLeft: '8px',
		MsFlexDirection: 'row',
		WebkitFlexDirection: 'row',
		flexDirection: 'row',
		MsFlexAlign: 'center',
		WebkitAlignItems: 'center',
		alignItems: 'center',
		MsFlexPack: 'center',
		WebkitJustifyContent: 'center',
		justifyContent: 'center',
		padding: '0 8px 0 0'
	 },
	 append: [
		this.menuContainer = mono.create('div', {
			style: {
		position: 'relative'
			}
		}),
		this.quickBtn = mono.create('a', {
			class: 'sf-quick-dl-btn',
			style: {
		display: 'inline-block',
		fontSize: 'inherit',
		height: '22px',
		border: '1px solid #00B75A',
		borderRadius: '3px',
		borderTopRightRadius: 0,
		borderBottomRightRadius: 0,
		paddingLeft: '28px',
		cursor: 'pointer',
		verticalAlign: 'middle',
		position: 'relative',
		lineHeight: '22px',
		textDecoration: 'none',
		zIndex: 1,
		color: '#fff'
			},
			href: '#',
			append: [
		mono.create('i', {
		 style: {
			position: 'absolute',
			display: 'inline-block',
			left: '6px',
			top: '3px',
			backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#ffffff') + ')',
			backgroundSize: '12px',
			backgroundRepeat: 'no-repeat',
			backgroundPosition: 'center',
			width: '16px',
			height: '16px'
		 }
		}),
		mono.create('span', {
		 class: 'sf-btn-name',
		 style: {
			paddingRight: '12px'
		 },
		 text: language.download
		})
			]
		}),
		this.quickBtnStyle = mono.create('style', {
			text: mono.style2Text({
		selector: '#' + youtube.buttonId,
		style: {
		 fontFamily: 'Roboto, Arial, sans-serif',
		 fontSize: '13px'
		},
		append: [{
		 'button::-moz-focus-inner': {
			padding: 0,
			margin: 0
		 },
		 '.sf-quick-dl-btn': {
			backgroundColor: '#00B75A'
		 },
		 '.sf-quick-dl-btn:hover': {
			backgroundColor: 'rgb(0, 163, 80)'
		 },
		 '.sf-quick-dl-btn:active': {
			backgroundColor: 'rgb(0, 151, 74)'
		 }
		}, {
		 media: '@media screen and (max-width: 1293px)',
		 append: {
			'.sf-quick-dl-btn .sf-btn-name': {
				display: 'none'
			}
		 }
		}]
			})
		}),
		this.selectBtn = mono.create('button', {
			style: {
		position: 'relative',
		display: 'inline-block',
		marginLeft: '-2px',
		fontSize: 'inherit',
		height: '24px',
		paddingRight: '21px',
		backgroundColor: '#F8F8F8',
		border: '1px solid #CCCCCC',
		borderRadius: '3px',
		borderTopLeftRadius: '0',
		borderBottomLeftRadius: '0',
		cursor: 'pointer',
		color: '#9B9B9B',
		zIndex: 0,
		verticalAlign: 'middle',
		boxSizing: 'border-box',
		lineHeight: '22px'
			},
			on: ['mousedown', function(e) {
		return onSelectBtnClick.call(this, {
		 btnObj: _this
		}, e);
			}],
			append: [
		this.selectBtnIcon = mono.create('i', {
		 style: {
			position: 'absolute',
			display: 'inline-block',
			top: '9px',
			right: '6px',
			border: '5px solid #868282',
			borderBottomColor: 'transparent',
			borderLeftColor: 'transparent',
			borderRightColor: 'transparent'
		 }
		})
			]
		})
	 ]
	});

	this.node.classList.add(mono.onRemoveClassName);

	var setQuality = function(text) {
	 var node = typeof text === 'object' ? text : document.createTextNode(text);
	 node.style.marginLeft = 0;
	 var first = _this.selectBtn.firstChild;
	 if (first === _this.selectBtnIcon) {
		_this.selectBtn.insertBefore(node, first);
	 } else {
		_this.selectBtn.replaceChild(node, first);
	 }
	};

	this.setQuality = setQuality;

	this.setLoadingState = function() {
	 setQuality(mono.create('img', {
		src: SaveFrom_Utils.svg.getSrc('info', '#333333'),
		style: {
			width: '14px',
			height: '14px',
			marginLeft: '6px',
			verticalAlign: 'middle',
			top: '-1px',
			position: 'relative'
		}
	 }));
	};
		},
		getButtonUnderVideo: function(onSelectBtnClick) {
	var selectBtnIcon = null;
	var quickBtn = null;
	var selectBtn = null;
	var node = mono.create('div', {
	 id: youtube.buttonId,
	 style: {
		display: 'inline-block',
		marginLeft: '10px',
		verticalAlign: 'middle'
	 },
	 append: [
		quickBtn = mono.create('a', {
			class: 'sf-quick-dl-btn',
			style: {
		display: 'inline-block',
		fontSize: 'inherit',
		height: '22px',
		border: '1px solid #00B75A',
		borderRadius: '3px',
		borderTopRightRadius: 0,
		borderBottomRightRadius: 0,
		paddingLeft: '28px',
		cursor: 'pointer',
		verticalAlign: 'middle',
		position: 'relative',
		lineHeight: '22px',
		textDecoration: 'none',
		zIndex: 1,
		color: '#fff'
			},
			href: '#',
			append: [
		mono.create('i', {
		 style: {
			position: 'absolute',
			display: 'inline-block',
			left: '6px',
			top: '3px',
			backgroundImage: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#ffffff') + ')',
			backgroundSize: '12px',
			backgroundRepeat: 'no-repeat',
			backgroundPosition: 'center',
			width: '16px',
			height: '16px'
		 }
		}),
		mono.create('span', {
		 class: 'sf-btn-name',
		 style: {
			paddingRight: '12px'
		 },
		 text: language.download
		})
			]
		}),
		mono.create('style', {
			text: mono.style2Text({
		selector: '#' + youtube.buttonId,
		append: [{
		 'button::-moz-focus-inner': {
			padding: 0,
			margin: 0
		 },
		 '.sf-quick-dl-btn': {
			backgroundColor: '#00B75A'
		 },
		 '.sf-quick-dl-btn:hover': {
			backgroundColor: 'rgb(0, 163, 80)'
		 },
		 '.sf-quick-dl-btn:active': {
			backgroundColor: 'rgb(0, 151, 74)'
		 }
		}, {
		 media: '@media screen and (max-width: 1293px)',
		 append: {
			'.sf-quick-dl-btn .sf-btn-name': {
				display: 'none'
			}
		 }
		}]
			})
		}),
		selectBtn = mono.create('button', {
			style: {
		position: 'relative',
		display: 'inline-block',
		marginLeft: '-2px',
		fontSize: 'inherit',
		height: '24px',
		paddingRight: '21px',
		backgroundColor: '#F8F8F8',
		border: '1px solid #CCCCCC',
		borderRadius: '3px',
		borderTopLeftRadius: '0',
		borderBottomLeftRadius: '0',
		cursor: 'pointer',
		color: '#9B9B9B',
		zIndex: 0,
		verticalAlign: 'middle',
		boxSizing: 'border-box',
		lineHeight: '22px'
			},
			on: ['mousedown', onSelectBtnClick],
			append: [
		selectBtnIcon = mono.create('i', {
		 style: {
			position: 'absolute',
			display: 'inline-block',
			top: '9px',
			right: '6px',
			border: '5px solid #868282',
			borderBottomColor: 'transparent',
			borderLeftColor: 'transparent',
			borderRightColor: 'transparent'
		 }
		})
			]
		})
	 ]
	});

	node.classList.add(mono.onRemoveClassName);

	var setQuality = function(text) {
	 var node = typeof text === 'object' ? text : document.createTextNode(text);
	 var first = selectBtn.firstChild;
	 if (first === selectBtnIcon) {
		selectBtn.insertBefore(node, first);
	 } else {
		selectBtn.replaceChild(node, first);
	 }
	};

	return {
	 node: node,
	 selectBtn: selectBtn,
	 quickBtn: quickBtn,
	 setQuality: setQuality,
	 setLoadingState: function() {
		setQuality(mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('info', '#333333'),
			style: {
		width: '14px',
		height: '14px',
		marginLeft: '6px',
		verticalAlign: 'middle',
		top: '-1px',
		position: 'relative'
			}
		}));
	 }
	};
		},
		titleChangeObserver: null,
		playerStateChangeObserver: null,
		observeFrameVideoChange: function(btnObj) {
	"use strict";
	var _this = this;

	if (!SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 return;
	}

	var playerTitle = document.querySelector('.ytp-title-link');
	if (playerTitle) {
	 if (this.titleChangeObserver) {
		this.titleChangeObserver.stop();
	 }
	 this.titleChangeObserver = SaveFrom_Utils.mutationAttrWatcher.run({
		attr: 'href',
		target: playerTitle,
		callback: function(summaryList) {
			var summary = summaryList[0];
			var oldVideoId = summary.oldValue && youtube.getIdFromLocation(summary.oldValue);
			var videoId = summary.value && youtube.getIdFromLocation(summary.value);
			if (oldVideoId !== videoId && youtube.video_id !== videoId) {
		youtube.video_id = videoId;

		if (youtube.frameQualityDetected) {
		 btnObj.link = null;
		 btnObj.setLoadingState();

		 _this.quickBtn.setValue(btnObj);
		}
			}
		}
	 });
	}

	var playerMenu = document.querySelector('.html5-video-player');
	if (playerMenu) {
	 var hideBtnTimer = null;
	 var ytpAutohideRe = /(\s|^)ytp-autohide(\s|$)/;
	 if (this.playerStateChangeObserver) {
		this.playerStateChangeObserver.stop();
	 }
	 this.playerStateChangeObserver = SaveFrom_Utils.mutationAttrWatcher.run({
		attr: 'class',
		target: playerMenu,
		callback: function(summaryList) {
			var summary = summaryList[0];
			var showBefore = ytpAutohideRe.test(summary.oldValue);
			var showAfret = ytpAutohideRe.test(summary.value);
			if (!showBefore && showAfret) {
		clearTimeout(hideBtnTimer);
		hideBtnTimer = setTimeout(function() {
		 if (!btnObj.lockHide) {
			btnObj.container.classList.add('sf-hide-ui');
		 }
		}, 100);
			} else
			if (showBefore && !showAfret) {
		clearTimeout(hideBtnTimer);
		btnObj.container.classList.remove('sf-hide-ui');
			}
		}
	 });
	}
		},

		appendFrameButton: function() {
	"use strict";
	if (document.body.clientWidth < 220 || document.body.clientHeight < 150) {
	 return;
	}

	var _this = youtube;

	var firstMenuShow = true;

	var btnObj = SaveFrom_Utils.frameMenu.getBtn({
	 btnId: 'sfYtFrameBtn',
	 containerStyle: {
		top: '40px',
		right: '20px'
	 },
	 on: [
		['mousedown', function(e) {
			e.stopPropagation();
			if (e.button !== 2) {
		return;
			}

			if (_this.onFrameMouseEnterBind) {
		mono.off(document.body, 'mouseenter', _this.onFrameMouseEnterBind);
		mono.off(document.body, 'mouseleave', _this.onFrameMouseLeaveBind);
		_this.onFrameMouseEnterBind = null;
		_this.onFrameMouseLeaveBind = null;
			}
			if (_this.titleChangeObserver) {
		_this.titleChangeObserver.stop();
		_this.titleChangeObserver = null;
			}
			if (_this.playerStateChangeObserver) {
		_this.playerStateChangeObserver.stop();
		_this.playerStateChangeObserver = null;
			}
			_this.hideCurrentMenu();
			if (btnObj.container.parentNode) {
		btnObj.container.parentNode.removeChild(btnObj.container);
			}
		}]
	 ],
	 onSelectBtn: ['mousedown', function(e) {
		if (e.button > 0) {
			return;
		}

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

		var vid = _this.video_id;

		if (_this.currentMenu) {
			youtube.hideCurrentMenu();
			return;
		}

		var menu = _this.currentMenu = SaveFrom_Utils.frameMenu.getMenu(this.parentNode, language.download + ' ...', 'sf-popupMenu', {
			container: btnObj.container,
			onShow: function() {
		btnObj.node.classList.add('sf-over');

		if (!firstMenuShow) {
		 firstMenuShow = false;
		}
			},
			onHide: function() {
		_this.currentMenu = null;
		btnObj.node.classList.remove('sf-over');
			},
			onItemClick: function(e, menuItem) {
		var itag = menuItem.itag;
		if (itag === 'ummyAudio') {
		 itag = 'ummy';
		}

		btnObj.lastItag !== itag && mono.storage.set({
		 ytLastITag: itag
		}, function() {
		 _this.quickBtn.setValue(btnObj);
		});

		youtube.onMenuItemClick(menuItem, {
		 isFrameItem: 1,
		 videoId: vid
		});
			}
		});

		var onResponse = function(response) {
			var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.youtube(response.links, response.title, response.subtitles, {
		ummyVid: 136
			});
			menu.update(menuLinks);
		};

		var response = _this.responseCache[vid];
		if (response) {
			return onResponse(response);
		}

		mono.sendMessage({
			action: 'getYoutubeLinks',
			extVideoId: vid,
			url: location.href,
			checkSubtitles: true
		}, function(response) {
			if (response.links) {
		_this.responseCache[vid] = response;
		return onResponse(response);
			}

			menu.update(language.noLinksFound);
		});
	 }]
	});

	btnObj.setLoadingState();

	btnObj.container = mono.create('div', {
	 class: 'sf-btn-ctr',
	 append: btnObj.node
	});
	document.body.appendChild(btnObj.container);

	var onBtnMouseEnter = function() {
	 mono.off(btnObj.node, 'mouseenter', onBtnMouseEnter);

	 if (_this.frameQualityDetected) {
		return;
	 }
	 _this.frameQualityDetected = true;

	 _this.quickBtn.setValue(btnObj);
	};

	mono.on(btnObj.node, 'mouseenter', onBtnMouseEnter);

	mono.one(document, 'mouseenter', function() {
	 _this.onFrameMouseEnter(btnObj);
	});

	mono.on(btnObj.container, 'mouseenter', function() {
	 btnObj.lockHide = true;
	});

	mono.on(btnObj.container, 'mouseleave', function() {
	 btnObj.lockHide = false;
	});

	btnObj.node.appendChild(mono.create('style', {
	 text: mono.style2Text([{
		selector: [
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) #sfYtFrameBtn',
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) .sf-popupMenu'
		],
		style: {
			display: 'block'
		}
	 }])
	}));

	if (this.onFrameMouseEnterBind) {
	 mono.off(document.body, 'mouseenter', this.onFrameMouseEnterBind);
	 mono.off(document.body, 'mouseleave', this.onFrameMouseLeaveBind);
	}

	this.onFrameMouseEnterBind = this.onFrameMouseEnter.bind(this, btnObj);
	this.onFrameMouseLeaveBind = this.onFrameMouseLeave.bind(this, btnObj);

	mono.on(document.body, 'mouseenter', this.onFrameMouseEnterBind);
	mono.on(document.body, 'mouseleave', this.onFrameMouseLeaveBind);

	this.observeFrameVideoChange(btnObj);
		},

		frameQualityDetected: false,
		frameQualityTimer: null,

		onFrameMouseEnterBind: null,
		onFrameMouseLeaveBind: null,

		onFrameMouseEnter: function(btnObj) {
	"use strict";
	var _this = this;
	if (!this.frameQualityDetected) {
	 clearTimeout(this.frameQualityTimer);
	 this.frameQualityTimer = setTimeout(function() {
		if (_this.frameQualityDetected) {
			return;
		}
		_this.frameQualityDetected = true;


		_this.quickBtn.setValue(btnObj);
	 }, 500);
	}
		},

		onFrameMouseLeave: function(btnObj) {
	"use strict";
	if (!this.frameQualityDetected) {
	 clearTimeout(this.frameQualityTimer);
	}
		},

		getTitle: function() {
	var t = document.getElementById('watch-headline-title');
	if (t)
	 return t.textContent;

	var meta = document.getElementsByTagName('meta');
	for (var i = 0; i < meta.length; i++) {
	 var name = meta[i].getAttribute('name');
	 if (name && name.toLowerCase() == 'title')
		return meta[i].getAttribute('content');
	}

	if (iframe || youtube.isMobile)
	 return document.title.replace(/ - YouTube$/, '');

	return '';
		},

		getTitleModify: function() {
	"use strict";
	var title = youtube.getTitle();
	if (title) {
	 title = modifyTitle(title);
	}

	return title;
		},

		onMenuItemClick: function(link, details) {
	"use strict";
	details = details || {};

	if (!link.format) {
	 mono.sendMessage({
		action: 'track',
		t: 'event',
		ec: 'youtube',
		ea: 'new_format',
		el: link.itag + ' ' + details.videoId
	 });
	}
		},

		onDlBtnClick: function(e, link, details) {
	details = details || {};
	if (!link) {
	 e.preventDefault();
	 e.stopPropagation();
	 mono.trigger(this.parentNode.lastChild, 'mousedown');
	 return;
	}

	youtube.onMenuItemClick(link, details);

	if (link.quality !== 'ummy') {
	 if (link.forceDownload) {
		SaveFrom_Utils.downloadOnClick(e);
	 }
	}
		},

		quickBtn: {
	prepMenuLinks: function(links, title) {
	 "use strict";
	 var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.youtube(links, title);
	 var ummyLinks = {};

	 var linkList = [];
	 for (var i = 0, item; item = menuLinks[i]; i++) {
		if (item.quality === 'ummy') {
			ummyLinks[item.itag] = item;
		}
		linkList.push({
			prop: item
		});
	 }
	 menuLinks = SaveFrom_Utils.popupMenu.sortMenuItems(linkList);

	 return {
		menuLinks: menuLinks,
		ummyLinks: ummyLinks
	 };
	},

	setValueInSelectBtn: function(details, text) {
	 "use strict";
	 if (typeof text !== 'object') {
		text = document.createTextNode(text);
	 }
	 var first = details.selectBtn.firstChild;
	 if (first === details.selectBtn.lastChild) {
		details.selectBtn.insertBefore(text, first)
	 } else {
		details.selectBtn.replaceChild(text, first);
	 }
	},

	getBestItem: function(menuLinks) {
	 var sList = [];
	 for (var i = 0, item; item = menuLinks[i]; i++) {
		if (item.prop.noAudio || item.prop.noVideo) {
			continue;
		}
		if (item.prop.format === 'ummy') {
			continue;
		}
		if (item.prop.extra) {
			continue;
		}
		if (!item.prop.isHidden) {
			sList.push(item.prop);
		}
	 }

	 if (!sList.length) {
		return;
	 }

	 return sList[0];
	},

	bindDlBtn: function(details) {
	 "use strict";
	 var quickBtn = details.quickBtn;
	 if (details.quickBtnEvent) {
		quickBtn.removeEventListener('click', details.quickBtnEvent);
	 }
	 quickBtn.addEventListener('click', details.quickBtnEvent = function(e) {
		e.stopPropagation();

		if (details.link && youtube.currentMenu) {
			youtube.hideCurrentMenu();
		}

		var dlBtnDetails = {
			videoId: details.videoId || youtube.video_id
		};
		if (details.isPage) {
			dlBtnDetails.isPage = 1;
		} else {
			dlBtnDetails.isFrame = 1;
		}

		youtube.onDlBtnClick.call(this, e, details.link, dlBtnDetails);
	 });
	},

	setBestValue: function(details, link) {
	 "use strict";
	 var quickBtn = details.quickBtn;

	 details.link = link;

	 var btnText = link.quality;
	 if (!link.noVideo) {
		btnText = parseInt(btnText);
	 }
	 if (link['3d']) {
		btnText = '3D ' + btnText;
	 }
	 if (link.sFps) {
		btnText += ' ' + (link.fps || 60);
	 }

	 var textContainer = mono.create('span', {
		text: btnText,
		style: {
			marginLeft: '6px',
			verticalAlign: 'bottom'
		}
	 });

	 if (details.setQuality) {
		details.setQuality(textContainer);
	 } else {
		this.setValueInSelectBtn(details, textContainer);
	 }

	 var title = [link.format, btnText];
	 if (link.noAudio) {
		title.push(language.withoutAudio);
	 }
	 title = title.join(' ');

	 quickBtn.title = title;
	 quickBtn.href = link.href;

	 if (link.title && link.format) {
		var ext = (link.ext || link.format || '').toLowerCase();
		if (ext) {
			ext = '.' + ext;
		}
		quickBtn.setAttribute('download', mono.fileName.modify(link.title + ext));
	 }
	},

	setUmmyBadge: function(details, link) {
	 "use strict";
	 var _this = this;
	 var quickBtn = details.quickBtn;

	 details.link = link;

	 mono.sendMessage({
		action: 'getUmmyIcon'
	 }, function(dataImg) {
		var ummyIcon = mono.create('span', {
			style: {
		width: '16px',
		height: '16px',
		backgroundImage: 'url(' + dataImg + ')',
		backgroundRepeat: 'no-repeat',
		backgroundPosition: 'center',
		marginLeft: '6px',
		display: 'inline-block',
		verticalAlign: 'top'
			}
		});
		_this.setValueInSelectBtn(details, ummyIcon);
	 });

	 quickBtn.title = mono.capitalize(link.quality);
	 quickBtn.href = link.href;

	 quickBtn.removeAttribute('download');
	},

	onGetLinks: function(details, links, title) {
	 "use strict";
	 var _this = this;
	 if (links) {
		var count = Object.keys(links).length;
		if (links.meta) {
			count--;
		}
	 }
	 if (!links || !count) {
		return this.setValueInSelectBtn(details, '');
	 }

	 mono.storage.get('ytLastITag', function(storage) {
		var lastItag = storage.ytLastITag;
		if (lastItag === 'ummyAudio') {
			lastItag = 'ummy';
		}
		details.lastItag = lastItag;

		var menuLinks = _this.prepMenuLinks(links, title || youtube.getTitleModify());

		details.onGetLinksArr && details.onGetLinksArr.forEach(function(fn) {
			return fn(menuLinks.menuLinks);
		});

		if (lastItag === 'ummy' && menuLinks.ummyLinks[lastItag]) {
			_this.setUmmyBadge(details, menuLinks.ummyLinks[lastItag]);
		} else {
			var link = _this.getBestItem(menuLinks.menuLinks);
			if (link) {
		_this.setBestValue(details, link);
			} else {
		_this.setValueInSelectBtn(details, '');
			}
		}
	 });
	},
	setValue: function(details) {
	 "use strict";
	 var _this = this;
	 this.bindDlBtn(details);

	 var vid = details.videoId || youtube.video_id;
	 if (!vid) {
		return this.onGetLinks(details);
	 }

	 var response = youtube.responseCache[vid];
	 if (response) {
		this.onGetLinks(details, response.links, response.title);
		response = null;
		return;
	 }

	 var onResponse = function(response) {
		response = response || {};

		if (response.isQuick) {
			details.quickBtn.dataset.isQuick = '1';
		}

		return _this.onGetLinks(details, response.links, response.title);
	 };

	 var request = {
		action: 'getYoutubeLinks',
		extVideoId: vid,
		url: location.href,
		noDash: true
	 };

	 if (details.ytConfig) {
		request.action = 'getYoutubeLinksFromConfig';
		request.config = details.ytConfig;
	 }

	 mono.sendMessage(request, onResponse);
	}
		},

		videoFeed: {
	style: null,
	hrefIdPattern: /[\?&]v=([^&]+)/,
	imgIdPattern: /vi[^\/]*\/([^\/]+)/,
	rList: [
	 /\/playlist\?/,
	 /(user|channel|c|show)\/[^\/]+(\/feed|\/featured|\/videos|$)/i,
	 /\/(feed)\/(trending|subscriptions|history)/i
	],
	testUrl: function(url) {
	 return this.rList.some(function(r) {
		return r.test(url);
	 });
	},
	injectStyle: function() {
	 if (this.style) {
		!this.style.parentNode && document.head.appendChild(this.style);
		return;
	 }

	 this.style = mono.create('style', {
		class: 'sf-feed-style',
		text: mono.style2Text([{
			selector: [
		'.contains-percent-duration-watched .sf-feed-btn'
			],
			style: {
		bottom: '6px'
			}
		}, {
			selector: [
		'a > .sf-feed-btn',
		'div > .sf-feed-btn',
		'span > .sf-feed-btn'
			],
			style: {
		display: 'none',
		border: '1px solid #d3d3d3',
		width: '20px',
		height: '20px',
		padding: 0,
		position: 'absolute',
		right: '26px',
		bottom: '2px',
		borderRadius: '2px',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#777777') + ') center no-repeat #F8F8F8',
		backgroundSize: '12px',
		cursor: 'pointer'
			}
		}, {
			selector: [
		'a > .sf-feed-btn.style-2'
			],
			style: {
		border: 0,
		top: 0,
		left: 0,
		margin: '4px',
		padding: '2px 4px',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#FFF') + ') center no-repeat #000',
		backgroundSize: '14px',
		opacity: '.8'
			}
		}, {
			selector: [
		'a > .sf-feed-btn:hover',
		'div > .sf-feed-btn:hover',
		'span > .sf-feed-btn:hover'
			],
			style: {
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #F8F8F8',
		backgroundSize: '12px'
			}
		}, {
			selector: [
		'a > .sf-feed-btn.style-2:hover'
			],
			style: {
		border: 0,
		top: 0,
		left: 0,
		margin: '4px',
		padding: '2px 4px',
		background: 'url(' + SaveFrom_Utils.svg.getSrc('download', '#00B75A') + ') center no-repeat #000',
		backgroundSize: '14px',
		opacity: '.8'
			}
		}, {
			selector: [
		'a > .sf-feed-btn:active',
		'div > .sf-feed-btn:active',
		'span > .sf-feed-btn:active'
			],
			style: {
		outline: 0,
		boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
			}
		}, {
			selector: [
		'a:hover > .sf-feed-btn',
		'div:hover > .sf-feed-btn',
		'span:hover > .sf-feed-btn'
			],
			style: {
		display: 'block'
			}
		}])
	 });

	 document.head.appendChild(this.style);
	},
	rmBtn: function() {
	 var nodeList = document.querySelectorAll('.sf-feed-btn');
	 for (var i = 0, item; item = nodeList[i]; i++) {
		item.parentNode.removeChild(item);
	 }

	 ['sfBtn', 'sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		nodeList = document.querySelectorAll('[' + dataAttr + ']');
		for (i = 0, item; item = nodeList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},

	getBtnNode: function(_vid, styleIndex) {
	 var className = ['sf-feed-btn'];
	 if (styleIndex) {
		className.push('style-' + styleIndex);
	 }
	 return mono.create('i', {
		class: className,
		append: [!mono.isOpera ? undefined : mono.create('img', {
			src: SaveFrom_Utils.svg.getSrc('download', '#777777'),
			style: {
		width: '12px',
		height: '12px',
		margin: '4px'
			}
		})],
		on: ['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			var _this = this;

			if (youtube.currentMenu && youtube.currentMenu.isShow) {
		youtube.hideCurrentMenu();
		return;
			}

			var vid = _vid;
			var parent = _this.parentNode;
			if (parent && parent.tagName === 'A' && parent.id === 'thumbnail') {
		var m = youtube.videoFeed.hrefIdPattern.exec(parent.href);
		if (m) {
		 vid = m[1];
		}
			}

			var hasHomePageSkeleton = document.querySelector('#home-page-skeleton.hidden');

			var menu = youtube.currentMenu = SaveFrom_Utils.popupMenu.quickInsert(_this, language.download + ' ...', 'sf-popupMenu', {
		offsetTop: hasHomePageSkeleton ? 54 : 0,
		onShow: function() {
		 mono.onRemoveEvent(_this, youtube.hideCurrentMenu);
		},
		onHide: function() {
		 mono.offRemoveEvent(_this, youtube.hideCurrentMenu);
		},
		onItemClick: function(e, menuItem) {
		 "use strict";
		 youtube.onMenuItemClick(menuItem, {
			isFeedItem: 1,
			videoId: vid
		 });
		}
			});

			mono.sendMessage({
		action: 'getYoutubeLinks',
		extVideoId: vid,
		url: location.href,
		checkSubtitles: true
			}, function(response) {
		if (response.links) {
		 var menuLinks = SaveFrom_Utils.popupMenu.prepareLinks.youtube(response.links, response.title, response.subtitles);
		 menu.update(menuLinks);
		 return;
		}

		menu.update(language.noLinksFound);
			});
		}]
	 });
	}
		},

		downloadPlaylist: function() {
	var getIdListFromPage = function(container) {
	 var idList = [];
	 var imgList = container.querySelectorAll('img[src]');
	 var pattern = youtube.videoFeed.imgIdPattern;
	 for (var i = 0, el; el = imgList[i]; i++) {
		var matched = el.src.match(pattern);
		if (!matched) {
			continue;
		}
		if (idList.indexOf(matched[1]) === -1) {
			idList.push(matched[1]);
		}
	 }

	 var dataElList = container.querySelectorAll('*[data-video-id]');
	 for (i = 0, el; el = dataElList[i]; i++) {
		var id = el.dataset.videoId;
		if (idList.indexOf(id) === -1) {
			idList.push(id);
		}
	 }
	 return idList;
	};
	var getIdLinks = function(cb) {
	 var container = document;
	 var url = mono.parseUrl(location.href);
	 if (url.list) {
		mono.sendMessage({
			action: 'getYoutubeIdListFromPlaylist',
			listId: url.list,
			baseUrl: location.protocol + '//' + location.host
		}, function(response) {
			if (!response) {
		return cb();
			}
			if (!response.idList || response.idList.length === 0) {
		var container = document.querySelector(".playlist-videos-container > .playlist-videos-list");
		if (container !== null) {
		 response.idList = getIdListFromPage(container);
		}
		if (!response.title) {
		 var title = document.querySelector(".playlist-info > .playlist-title");
		 if (title !== null) {
			response.title = title.textContent.replace(/\r?\n/g, " ").trim();
		 }
		}
			}
			cb(response.idList, response.title);
		});
	 } else {
		var idList = getIdListFromPage(container);
		cb(idList, youtube.getTitle());
	 }
	};
	var getVideoLink = function(vid, maxSize, typeList, cb) {
	 var useDash = typeList.indexOf('audio') !== -1;
	 mono.sendMessage({
		action: 'getYoutubeLinks',
		extVideoId: vid,
		noDash: useDash
	 }, function(response) {
		var links = undefined;
		if (response.links) {
			links = SaveFrom_Utils.popupMenu.prepareLinks.youtube(response.links, response.title);
			links = SaveFrom_Utils.popupMenu.sortMenuItems(links, {
		noProp: true,
		maxSize: maxSize,
		minSize: 2,
		typeList: typeList
			});
		}
		cb(links);
	 });
	};
	var getVideoLinks = function(idList, maxSize, onProgress, onReady) {
	 var abort = false;
	 var linkList = {};
	 var index = 0;
	 var inProgress = 0;
	 var listLen = idList.length;

	 var typeList = undefined;
	 if (maxSize === 'audio') {
		typeList = ['audio'];
		maxSize = undefined;
	 } else {
		typeList = ['video'];
		maxSize = parseInt(maxSize) || undefined;
	 }

	 var getNextOneId = function() {
		if (abort) {
			return;
		}
		var id = idList[index];
		if (id === undefined) {
			if (inProgress === 0) {
		return onReady(linkList);
			} else {
		return;
			}
		}
		index++;
		inProgress++;
		getVideoLink(id, maxSize, typeList, function(links) {
			var firstLink = links ? links[0] : undefined;
			if (firstLink) {
		var ext = (firstLink.ext || firstLink.format || '').toLowerCase();
		if (ext) {
		 ext = '.' + ext;
		}
		var filename = mono.fileName.modify(firstLink.title + ext);
		linkList[id] = {
		 url: firstLink.href,
		 title: firstLink.title,
		 filename: filename
		};
			}
			onProgress(index, listLen);
			inProgress--;
			getNextOneId();
		});
	 };
	 getNextOneId();
	 getNextOneId();
	 return {
		abort: function() {
			abort = true;
		}
	 }
	};
	var getPopup = function(onClose) {
	 var template = SaveFrom_Utils.playlist.getInfoPopupTemplate();
	 var progressEl;
	 var qualitySelectBox;
	 var qualitySelect;

	 mono.sendMessage({
		action: 'getWarningIcon',
		type: 'playlist',
		color: '#77D1FA'
	 }, function(icon) {
		template.icon.style.backgroundImage = 'url(' + icon + ')';
	 });

	 mono.create(template.textContainer, {
		append: [
			mono.create('p', {
		text: language.playlist,
		style: {
		 color: '#0D0D0D',
		 fontSize: '20px',
		 marginBottom: '11px',
		 marginTop: '13px'
		}
			}),
			qualitySelectBox = mono.create('div', {
		append: [
		 mono.create('p', {
			text: language.quality + ":",
			style: {
				color: '#000000',
				fontSize: '14px',
				marginBottom: '13px',
				lineHeight: '24px'
			},
			append: [
				qualitySelect = mono.create('select', {
			style: {
			 width: '75px',
			 marginLeft: '5px'
			},
			append: [
			 mono.create('option', {
				text: '720',
				value: '720'
			 }),
			 mono.create('option', {
				text: '480',
				value: '480'
			 }),
			 mono.create('option', {
				text: '360',
				value: '360'
			 }),
			 mono.create('option', {
				text: '240',
				value: '240'
			 }),
			 mono.create('option', {
				text: 'Audio',
				value: 'audio'
			 })
			]
				})
			]
		 }),
		 mono.create('p', {
			text: language.qualityNote,
			style: {
				color: '#868686',
				fontSize: '14px',
				lineHeight: '24px'
			}
		 })
		]
			}),
			progressEl = mono.create('p', {
		text: '',
		style: {
		 color: '#868686',
		 fontSize: '14px',
		 lineHeight: '24px'
		}
			})
		]
	 });

	 var continueBtn, cancelBtn;
	 mono.create(template.buttonContainer, {
		append: [
			cancelBtn = mono.create('button', {
		text: language.cancel,
		style: {
		 height: '27px',
		 width: '118px',
		 backgroundColor: '#ffffff',
		 border: '1px solid #9e9e9e',
		 margin: '12px',
		 marginBottom: '11px',
		 marginRight: '4px',
		 borderRadius: '5px',
		 fontSize: '14px',
		 cursor: 'pointer'
		}
			}),
			continueBtn = mono.create('button', {
		text: language.continue,
		style: {
		 height: '27px',
		 width: '118px',
		 backgroundColor: '#ffffff',
		 border: '1px solid #9e9e9e',
		 margin: '12px',
		 marginBottom: '11px',
		 marginRight: '8px',
		 borderRadius: '5px',
		 fontSize: '14px',
		 cursor: 'pointer'
		}
			})
		]
	 });

	 var popupEl = SaveFrom_Utils.popupDiv(template.body, 'pl_progress_popup', undefined, undefined, onClose);
	 return {
		qualitySelect: function(cb) {
			progressEl.style.display = 'none';
			template.buttonContainer.style.display = 'block';
			qualitySelectBox.style.display = 'block';
			continueBtn.addEventListener('click', function() {
		cb(qualitySelect.value);
			});
			cancelBtn.addEventListener('click', function() {
		mono.trigger(popupEl, 'kill');
			});
		},
		onPrepare: function(text) {
			progressEl.style.display = 'block';
			template.buttonContainer.style.display = 'none';
			qualitySelectBox.style.display = 'none';
			progressEl.textContent = text;
		},
		onProgress: function(count, max) {
			progressEl.textContent = language.vkFoundFiles.replace('%d', count) + ' ' + language.vkFoundOf + ' ' + max;
		},
		onReady: function(list, title) {
			mono.trigger(popupEl, 'kill');
			if (allowDownloadMode) {
		SaveFrom_Utils.downloadList.showBeforeDownloadPopup(list, {
		 type: 'playlist',
		 folderName: title
		});
			} else {
		SaveFrom_Utils.playlist.popupPlaylist(list, title, true, undefined, 'video');
			}
		},
		onError: function(text) {
			mono.sendMessage({
		action: 'getWarningIcon',
		type: 'playlist',
		color: '#AAAAAA'
			}, function(icon) {
		template.icon.style.backgroundImage = 'url(' + icon + ')';
			});

			progressEl.style.display = 'block';
			template.buttonContainer.style.display = 'none';
			qualitySelectBox.style.display = 'none';
			progressEl.textContent = text;
		}
	 }
	};
	return function() {
	 var abort = false;
	 var gettingLink = undefined;
	 var popup = getPopup(function onClose() {
		abort = true;
		if (gettingLink) {
			gettingLink.abort();
		}
	 });
	 popup.qualitySelect(function(maxSize) {
		popup.onPrepare(language.download + ' ...');
		getIdLinks(function(idList, title) {
			if (abort) {
		return;
			}
			if (!idList || idList.length === 0) {
		popup.onError(language.noLinksFound);
		return;
			}

			gettingLink = getVideoLinks(idList, maxSize, popup.onProgress, function onReady(linkList) {
		var links = [];
		for (var id in linkList) {
		 links.push(linkList[id]);
		}

		var folderName = mono.fileName.modify(title);
		popup.onReady(links, folderName);
			});
		});
	 });
	}
		}()
	};

	youtube.tutorial = {
		show: function(details) {
	"use strict";
	this.hide();

	if (!preference.onceShowYtTooltip) {
	 return;
	}

	details.onClose = function() {
	 attrWatcher && attrWatcher.stop();

	 mono.sendMessage({
		action: 'updateOption',
		key: 'onceShowYtTooltip',
		value: preference.onceShowYtTooltip = 0
	 });
	};

	youtube.currentTutorial = new SaveFrom_Utils.TutorialTooltip(details);

	var attrWatcher = null;
	var page = document.querySelector('#page.watch');
	if (page) {
	 attrWatcher = youtube.currentTutorial.attrWatcher = SaveFrom_Utils.mutationAttrWatcher.run({
		attr: 'class',
		target: page,
		callback: function() {
			var currentTutorial = youtube.currentTutorial;
			if (currentTutorial && currentTutorial.tooltipNode.parentNode) {
		currentTutorial.onResize();
			} else {
		attrWatcher.stop();
			}
		}
	 });
	}
		},
		hide: function() {
	var currentTutorial = youtube.currentTutorial;
	if (currentTutorial) {
	 currentTutorial.onClose && currentTutorial.onClose(1);
	 if (currentTutorial.attrWatcher) {
		currentTutorial.attrWatcher.stop();
	 }
	 youtube.currentTutorial = null;
	}
		}
	};

	var modifyTitle = function(t) {
		t = t.replace(/[\x2F\x5C\x3A\x7C]/g, '-');
		t = t.replace(/[\x2A\x3F]/g, '');
		t = t.replace(/\x22/g, '\'');
		t = t.replace(/\x3C/g, '(');
		t = t.replace(/\x3E/g, ')');
		t = t.replace(/(?:^\s+)|(?:\s+$)/g, '');
		return t;
	};

	var getPlayerConfig = function(cb) {
		return SaveFrom_Utils.bridge({
	func: function(cb) {
	 var ytPlayerConfig = window.ytplayer && window.ytplayer.config;
	 if (!ytPlayerConfig) {
		return cb();
	 }
	 cb({
		args: ytPlayerConfig.args,
		sts: ytPlayerConfig.sts,
		assets: ytPlayerConfig.assets
	 })
	},
	timeout: 300,
	cb: function(data) {
	 if (!data || !data.args || !data.args.video_id) {
		var metaVideoId = document.querySelector('#watch7-content meta[itemprop="videoId"]');
		var videoId = metaVideoId && metaVideoId.getAttribute('content');
		if (videoId) {
			data = data || {};
			data.args = data.args || {};
			data.args.video_id = videoId;
		}
	 }

	 cb(data);
	}
		});
	};

	var getNewPlayerConfig = function(cb) {
		return SaveFrom_Utils.bridge({
	func: function(cb) {
	 var player = document.querySelector('#movie_player');

	 if (!player || !player.getVideoData) {
		return cb();
	 }

	 if (!player.getUpdatedConfigurationData) {
		(function(fn) {
			if (fn && !fn.sf_apply) {
		fn.sf_apply = fn.apply;
		fn.apply = function(self, args) {
		 var playerArgs = args[0];
		 if (playerArgs && playerArgs.video_id) {
			var vars = JSON.parse(JSON.stringify(playerArgs));
			player.getSfPlayerVars = function() {
				return vars;
			};
		 }
		 return fn.sf_apply(self, args);
		}
			}
		})(player.loadVideoByPlayerVars);
	 }

	 var ytConfig = window.ytplayer && window.ytplayer.config;
	 var sts = ytConfig && ytConfig.sts;

	 var updatedConfig = player.getUpdatedConfigurationData && player.getUpdatedConfigurationData();

	 var assets = updatedConfig && updatedConfig.assets;
	 if (!assets || !assets.js) {
		assets = ytConfig && ytConfig.assets;
	 }

	 var args = updatedConfig && updatedConfig.args;
	 if (!args) {
		args = ytConfig && ytConfig.args;
	 }

	 var videoData = player.getVideoData() || {};
	 var sfConfig = player.getSfPlayerVars && player.getSfPlayerVars();

	 return cb({
		config: {
			sts: sts,
			assets: assets
		},
		video_id: videoData.video_id,
		sfConfig: sfConfig,
		ytConfig: args
	 });
	},
	timeout: 300,
	cb: function(data) {
	 if (!data || !data.config) {
		return cb(null);
	 }

	 var ytConfig = data.ytConfig || {};
	 var sfConfig = data.sfConfig || {};
	 var config = data.config || {};
	 var video_id = data.video_id;

	 if (!video_id) {
		video_id = youtube.getIdFromLocation();
	 }

	 if (sfConfig.video_id && (!video_id || video_id === sfConfig.video_id)) {
		config.args = sfConfig;
		cb(config);
	 } else
	 if (ytConfig.video_id && video_id === ytConfig.video_id) {
		config.args = ytConfig;
		cb(config);
	 } else
	 if (video_id) {
		cb({
			args: {
		video_id: video_id
			}
		});
	 } else {
		cb(null);
	 }
	}
		});
	};

	var ytUmmyBtn = function(language, preferences, videoId) {
		"use strict";
		ytUmmyBtn.rmBtn();

		if (!preferences.showUmmyItem) {
	return;
		}

		if (!preferences.showUmmyBtn) {
	return;
		}

		var url = 'ummy://www.youtube.com/watch?v=' + videoId;
		var vid = 130;

		var textNode = mono.create('span', {
	class: 'sf-btn-name',
	text: language.download + ' '
		});
		var qualityNode = mono.create('span', {
	text: 'HD'
		});

		var ummyLogo = mono.create('i', {
	style: {
	 position: 'absolute',
	 display: 'inline-block',
	 left: '6px',
	 top: '3px',
	 backgroundSize: '16px',
	 backgroundRepeat: 'no-repeat',
	 backgroundPosition: 'center',
	 width: '16px',
	 height: '16px'
	}
		});
		mono.sendMessage({
	action: 'getUmmyIcon'
		}, function(dataImg) {
	ummyLogo.style.backgroundImage = 'url(' + dataImg + ')';
		});
		var btn = mono.create('div', {
	id: ytUmmyBtn.id,
	style: {
	 display: 'inline-block',
	 marginLeft: '10px',
	 verticalAlign: 'middle'
	},
	on: ['changeValue', function(e) {
	 var value = JSON.parse(e.detail);
	 var newQualityNode = mono.create('span', {
		text: value
	 });
	 qualityNode.parentNode.replaceChild(newQualityNode, qualityNode);
	 qualityNode = newQualityNode;
	}],
	append: [
	 mono.create('a', {
		class: 'sf-quick-dl-btn',
		style: {
			display: 'inline-block',
			fontSize: 'inherit',
			height: '22px',
			border: '1px solid #CCCCCC',
			borderRadius: '3px',
			paddingRight: '8px',
			paddingLeft: '28px',
			cursor: 'pointer',
			verticalAlign: 'middle',
			position: 'relative',
			lineHeight: '22px',
			textDecoration: 'none',
			zIndex: 1,
			color: '#575757'
		},
		href: url,
		append: [
			ummyLogo,
			textNode,
			qualityNode
		],
		on: ['click', function() {
			if (/^ummy/.test(this.href)) {
		mono.sendMessage({
		 action: 'track',
		 t: 'event',
		 ec: 'youtube',
		 ea: 'download',
		 el: 'ummy hd'
		});
			}
		}]
	 }),
	 mono.create('style', {
		text: mono.style2Text([{
			selector: '#' + ytUmmyBtn.id,
			append: [{
		'.sf-quick-dl-btn': {
		 backgroundColor: '#F8F8F8'
		},
		'.sf-quick-dl-btn:hover': {
		 backgroundColor: '#EDEDED'
		},
		'.sf-quick-dl-btn:active': {
		 backgroundColor: '#F8F8F8'
		}
			}, {
		media: '@media screen and (max-width: 1293px)',
		append: {
		 '.sf-quick-dl-btn .sf-btn-name': {
			display: 'none'
		 }
		}
			}]
		}])
	 })
	]
		});
		ytUmmyDirect.bindUmmyBtn(btn, {
	video: 'yt-' + videoId,
	vid: vid
		});

		return btn;
	};
	ytUmmyBtn.id = 'sf-ummy-btn';
	ytUmmyBtn.rmBtn = function() {
		"use strict";
		var btnList = document.querySelectorAll('#' + ytUmmyBtn.id);
		for (var i = 0, el; el = btnList[i]; i++) {
	el.parentNode.removeChild(el);
		}
	};

	var ytUmmyDirect = {
		createInfoPopup: function(yesUrl, noUrl) {
	"use strict";
	var language = mono.global.language;

	var info;
	var infoContainer = mono.create('div', {
	 class: 'sf-ummy-info-confirm-popup-container',
	 style: {
		position: 'absolute',
		zIndex: 9999
	 },
	 append: [
		mono.create('span', {
			style: {
		display: 'inline-block',
		border: '8px solid transparent',
		borderRight: '10px solid rgb(192, 187, 187)',
		borderLeft: 0,
		width: 0,
		top: '8px',
		left: '11px',
		position: 'absolute'
			}
		}),
		mono.create('span', {
			style: {
		display: 'inline-block',
		border: '8px solid transparent',
		borderRight: '10px solid #fff',
		borderLeft: 0,
		width: 0,
		top: '8px',
		left: '12px',
		position: 'absolute'
			}
		}),
		info = mono.create('div', {
			class: 'sf-ummy-confirm-info-popup',
			style: {
		backgroundColor: '#fff',
		border: '1px solid #ccc',
		marginLeft: '21px',
		padding: '6px 5px',
		textAlign: 'center',
		maxWidth: '240px',
		lineHeight: '16px',
		fontSize: '12px',
		fontFamily: 'arial, sans-serif',
		cursor: 'default'
			},
			append: [
		mono.create('p', {
		 append: mono.parseTemplate(language.ummyTooltipConfirm),
		 onCreate: function(el) {
			var img = el.querySelector('img');
			img.style.verticalAlign = 'text-bottom';
			mono.sendMessage({
				action: 'getUmmyIcon'
			}, function(dataImg) {
				img.src = dataImg;
			});
		 }
		}),
		mono.create('p', {
		 style: {
			textAlign: 'right'
		 },
		 append: [
			mono.create('a', {
				class: 'sf-btn',
				style: {
			cssFloat: 'left'
				},
				text: language.yes,
				href: yesUrl,
				on: ['click', function(e) {
			setTimeout(function() {
			 mono.storage.get('onceUmmyLandingHide', function(storage) {
				if (!storage.onceUmmyLandingHide) {
					storage.onceUmmyLandingHide = 0;
				}

				mono.storage.set({
					onceUmmyLandingHide: ++storage.onceUmmyLandingHide
				});

				infoContainer.parentNode.removeChild(infoContainer);
				infoContainer.dataset.hide = '1';
			 });
			}, 250);
				}]
			}),
			mono.create('a', {
				class: 'sf-btn',
				text: language.no,
				href: noUrl,
				target: '_blank'
			})
		 ]
		}),
		mono.create('p', {
		 append: [
			mono.create('label', {
				append: [
			mono.create('input', {
			 type: 'checkbox',
			 on: ['change', function() {
				mono.storage.set({
					onceUmmyLandingHide: 3
				});

				infoContainer.parentNode.removeChild(infoContainer);
				infoContainer.dataset.hide = '1';
			 }]
			}),
			mono.create('span', {
			 text: language.tooltipHide
			})
				]
			})
		 ]
		}),
		mono.create('style', {
		 text: mono.style2Text({
			'.sf-ummy-confirm-info-popup > p': {
				margin: '5px 0'
			},
			'.sf-ummy-confirm-info-popup label > *': {
				verticalAlign: 'middle'
			},
			'.sf-ummy-confirm-info-popup .sf-btn': {
				display: 'inline-block',
				fontSize: 'inherit',
				height: '22px',
				border: '1px solid rgb(204, 204, 204)',
				borderRadius: '3px',
				cursor: 'pointer',
				verticalAlign: 'middle',
				position: 'relative',
				lineHeight: '22px',
				textDecoration: 'none',
				color: 'rgb(87, 87, 87)',
				textAlign: 'center',
				margin: '0 10px',
				width: '80px'
			},
			'.sf-ummy-confirm-info-popup .sf-btn:hover': {
				backgroundColor: '#ededed'
			}
		 })
		})
			]
		})
	 ],
	 on: [
		['mouseclick', function(e) {
			e.stopPropagation();
		}],
		['mousedown', function(e) {
			e.stopPropagation();
		}]
	 ]
	});

	return infoContainer;
		},
		bindUmmyBtn: function(container, params) {
	"use strict";
	if (!preference.showUmmyLanding) {
	 return;
	}

	var _this = this;

	var webUrl;
	if (/^Mac/.test(navigator.platform)) {
	 webUrl = 'http://videodownloader.ummy.net/save-from-youtube.html?' + mono.param({
		vid: params.vid,
		video: params.video,
		utm_source: 'savefrom-helper',
		utm_medium: 'youtube-helper',
		utm_campaign: 'ummy',
		utm_content: 'ummy_integration_h'
	 });
	} else {
	 webUrl = 'http://videodownloader.ummy.net/?' + mono.param({
		vid: 111,
		utm_source: 'savefrom-helper',
		utm_medium: 'youtube-helper',
		utm_campaign: 'youtube-helper-hd'
	 });
	}

	mono.storage.get(['onceUmmyLandingOpened', 'onceUmmyLandingHide'], function(storage) {
	 if (storage.onceUmmyLandingHide > 2) {
		return;
	 }

	 if (!storage.onceUmmyLandingHide) {
		storage.onceUmmyLandingHide = 0;
	 }

	 var btn = container.querySelector('a');
	 var ummyUrl = btn.href;

	 if (storage.onceUmmyLandingOpened) {
		return SaveFrom_Utils.bindUmmyInfo(container, {
			expUmmyInfo: _this.createInfoPopup.bind(_this, ummyUrl, webUrl),
			noUmmy: true
		});
	 }

	 btn.target = '_blank';
	 btn.href = webUrl;

	 var onceBtnOnClick = function() {
		setTimeout(function() {
			btn.removeEventListener('click', onceBtnOnClick);
			mono.storage.set({
		onceUmmyLandingOpened: 1
			});
			btn.target = '_self';
			btn.href = ummyUrl;
			SaveFrom_Utils.bindUmmyInfo(container, {
		expUmmyInfo: _this.createInfoPopup.bind(_this, ummyUrl, webUrl),
		noUmmy: true
			});
		}, 250);
	 };

	 btn.addEventListener('click', onceBtnOnClick);
	});
		}
	};

 }, null, function syncIsActive() {
	"use strict";

	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.youtube.com/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (/\/\/gaming\.youtube/.test(document.URL)) {
		return false;
	}

	return true;
 });

 //@insert
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('instagram', function(moduleName, initData) {
	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleInstagram ? 1 : 0;

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return instagram.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	return instagram.updateLinks();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	instagram.run();
		});
	}

	var instagram = {
		urlR: /\/\/[^\/]+\.[^\/]+\/p\//,
		lastWaitEl: null,
		dlBtnClassName: 'savefrom-helper--btn',
		styleEl: null,
		run: function() {
	moduleState = 1;

	this.insertStyle();

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 this.mutationMode.enable();
	}
		},
		rmStyle: function() {
	this.styleEl && this.styleEl.parentNode && this.styleEl.parentNode.removeChild(this.styleEl);
		},
		insertStyle: function() {
	if (this.styleEl) {
	 if (!this.styleEl.parentNode) {
		document.head.appendChild(this.styleEl);
	 }
	 return;
	}

	this.styleEl = mono.create('style', {
	 text: mono.style2Text([{
		selector: '.' + this.dlBtnClassName,
		style: {
			display: 'none',
			border: '1px solid #F8F8F8',
			top: '8px',
			left: '8px',
			padding: 0,
			position: 'absolute',
			backgroundColor: '#F8F8F8',
			cursor: 'pointer',
			lineHeight: 0
		}
	 }, {
		selector: '.' + this.dlBtnClassName + ' svg',
		style: {
			margin: '2px'
		}
	 }, {
		selector: '.' + this.dlBtnClassName + ' svg path',
		style: {
			fill: '#777777'
		}
	 }, {
		selector: '.Embed .' + this.dlBtnClassName,
		style: {
			border: '1px solid #B5B5B5',
			borderRadius: '4px',
			padding: '3px'
		}
	 }, {
		selector: '.' + this.dlBtnClassName + ':hover svg path',
		style: {
			fill: '#3f729b'
		}
	 }, {
		selector: '.' + this.dlBtnClassName + ':active',
		style: {
			outline: 0,
			boxShadow: 'inset 0 3px 5px rgba(0, 0, 0, 0.125)'
		}
	 }, {
		selector: [
			'*:hover > .' + this.dlBtnClassName,
			'*.sf-touch-show > .' + this.dlBtnClassName
		],
		style: {
			display: 'block'
		}
	 }, {
		selector: '*.sf-touch-hide > .' + this.dlBtnClassName,
		style: {
			display: 'none'
		}
	 }])
	});

	document.head.appendChild(this.styleEl);
		},
		updateLinks: function() {
	this.changeState(0);
	this.changeState(1);
		},
		changeState: function(state) {
	moduleState = state;
	this.rmDlBtn();
	this.rmStyle();
	this.mutationMode.stop();
	if (state) {
	 this.run();
	}
		},
		rmDlBtn: function() {
	var btnList = document.querySelectorAll('.' + this.dlBtnClassName);
	for (var i = 0, el; el = btnList[i]; i++) {
	 el.parentNode.removeChild(el);
	}
		},
		getVideoInfo: function(video) {
	// var poster = video.getAttribute('poster');
	var src = video.getAttribute('src');
	if (typeof src !== 'string') {
	 return;
	}

	var ext = 'mp4';
	if (src.indexOf('.flv') !== -1) {
	 ext = 'flv';
	}

	var filename = src.match(/\/([^\/?]+)(?:$|\?)/);
	filename = filename && filename[1];
	if (!filename) {
	 filename = 'noname.' + ext;
	}
	return {
	 filename: filename,
	 url: src
	}
		},
		getImageInfo: function(image) {
	var src = null;
	var srcset = image.getAttribute('srcset');
	if (typeof srcset === 'string') {
	 var urls = [];
	 srcset.split(',').map(function(value) {
		var p = value.split(' ');
		urls.push({
			url: p[0],
			size: p[1]
		});
	 });
	 urls.sort(function(a, b) {
		return a.size > b.size ? -1 : 1;
	 });
	 var best = urls.pop();
	 if (best) {
		src = best.url;
	 }
	}
	if (!src) {
	 src = image.getAttribute('src');
	}

	if (typeof src !== 'string') {
	 return;
	}

	var ext = 'jpg';
	if (src.indexOf('.png') !== -1) {
	 ext = 'png';
	}

	var filename = src.match(/\/([^\/?]+)(?:$|\?)/);
	filename = filename && filename[1];
	if (!filename) {
	 filename = 'noname.' + ext;
	}
	return {
	 filename: filename,
	 url: src
	}
		},
		getDbBtnEl: function(videoInfo) {
	return mono.create('a', {
	 class: [this.dlBtnClassName],
	 href: videoInfo.url,
	 download: videoInfo.filename,
	 title: language.download,
	 style: {
		position: 'absolute',
		zIndex: 100
	 },
	 on: ['click', function(e) {
		e.stopPropagation();
		SaveFrom_Utils.downloadOnClick(e, undefined, {
			el: this
		});
	 }],
	 append: [
		SaveFrom_Utils.svg.getSvg('download', null, 16, 16)
	 ]
	});
		},
		showOnTouch: function(ctr, btn) {
	var debug = false;

	if (ctr.dataset.sfTouch > 0) {
	 return;
	}

	debug && console.log('call!', ctr, btn);

	var isShow = false;
	var timer = null;

	var hide = function() {
	 if (isShow) {
		debug && console.log('hide');
		isShow = false;
		ctr.classList.remove('sf-touch-show');
		ctr.classList.add('sf-touch-hide');
	 }
	};

	var hideTimer = function() {
	 if (isShow) {
		debug && console.log('hideTimer');
		clearTimeout(timer);
		timer = setTimeout(function() {
			debug && console.log('call hideTimer');
			hide();
		}, 3000);
	 }
	};

	var show = function() {
	 if (!isShow) {
		isShow = true;
		debug && console.log('show');
		ctr.classList.remove('sf-touch-hide');
		ctr.classList.add('sf-touch-show');
	 }
	};

	mono.create(ctr, {
	 data: {
		sfTouch: '1'
	 },
	 on: [
		['touchstart', function(e) {
			debug && console.log('touchstart');
			show();
		}],
		['touchend', function(e) {
			debug && console.log('touchend');
			hideTimer();
		}]
	 ]
	});
		},
		addDlBtn: function(container, type, ctr) {
	var _this = this;
	var isEmbed = type === 'embed';
	var isEmbedSlider = false;
	if (container.querySelector('.EmbedSidecar')) {
	 isEmbedSlider = true;
	}

	var oldBtn = container.querySelector('.' + this.dlBtnClassName);
	if (oldBtn) {
	 oldBtn.classList.remove(mono.onRemoveClassName);
	 oldBtn.parentNode.removeChild(oldBtn);
	 oldBtn = null;
	}

	var iMedia = container;

	var mediaInfo = null;

	var video = container.querySelector(['div > div > video[src]']);
	if (video) {
	 mediaInfo = this.getVideoInfo(video);
	}

	if (!mediaInfo) {
	 var image;
	 if (isEmbed && !isEmbedSlider) {
		image = iMedia.querySelector('.EmbedFrame img.EmbeddedMediaImage');
		if (image) {
			mediaInfo = this.getImageInfo(image);
		}
	 } else {
		image = iMedia.querySelectorAll(['div > img[src]'])[0];
		if (image) {
			mediaInfo = this.getImageInfo(image);
		} else {
			image = iMedia.querySelectorAll(['div > img'])[0];
			if (image && (image.style.visibility === 'hidden' || !image.src)) {
		var waitSrc = SaveFrom_Utils.mutationAttrWatcher.run({
		 target: image,
		 attr: ['src'],
		 callback: function(summary) {
			var srcChanges = summary[0];
			if (srcChanges.value) {
				_this.addDlBtn(container, type, ctr);
				waitSrc.stop();
			}
		 }
		});
			}
		}
	 }
	}

	if (!mediaInfo) {
	 return;
	}

	var dlBtn = this.getDbBtnEl(mediaInfo);

	var targetType = '';
	var targetNode = null;
	if (image) {
	 targetType = 'image';
	 targetNode = image;
	} else {
	 targetType = 'video';
	 targetNode = video;
	}

	var srcChangeObserver = SaveFrom_Utils.mutationAttrWatcher.run({
	 target: targetNode,
	 attr: ['src'],
	 callback: function(summary) {
		var srcChanges = summary[0];
		if (srcChanges.value !== mediaInfo.url) {
			mediaInfo = null;
			if (targetType === 'image') {
		mediaInfo = _this.getImageInfo(targetNode);
			} else {
		mediaInfo = _this.getVideoInfo(targetNode);
			}
			if (mediaInfo && dlBtn.parentNode) {
		dlBtn.href = mediaInfo.url;
		dlBtn.download = mediaInfo.filename;
			} else {
		srcChangeObserver.stop();
			}
		}
	 }
	});
	mono.onRemoveEvent(targetNode, function() {
	 if (mono.contains(document.body, ctr)) {
		ctr.dataset.sfSkip = 0;
		instagram.mutationMode.observer.trigger(ctr);
	 }
	});

	if (isEmbedSlider) {
	 mono.create(dlBtn, {
		style: {
			top: '62px'
		}
	 });
	} else
	if (isEmbed) {
	 var embedFollowButton = document.querySelector('.Header');
	 if (embedFollowButton) {
		iMedia = embedFollowButton;
		mono.create(dlBtn, {
			style: {
		position: 'relative',
		zIndex: 100,
		display: 'block',
		left: 'auto',
		top: 'auto',
		marginLeft: '10px'
			}
		});
	 }
	}

	iMedia.appendChild(dlBtn);

	this.showOnTouch(iMedia, dlBtn);
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		instagram.addDlBtn(node.parentNode, '', node);
			}

			summary = summaryList[1];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.querySelector('ul > li')) continue;
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		instagram.addDlBtn(node.parentNode, '', node);
			}

			summary = summaryList[2];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		instagram.addDlBtn(node, 'embed', node);
			}

			summary = summaryList[3];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: [
		 'div > div > article > header + div > div > div:nth-child(1) ul > li > div:nth-child(1) > div:nth-child(1)'
		],
		is: 'added'
			},
			{
		css: [
		 'div > div > article > header + div > div > div:nth-child(1)'
		],
		is: 'added'
			},
			{
		css: [
		 '.Embed'
		],
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		}
	};
 }, null, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.instagram.com/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe() && !/\/\/[^\/]+\.[^\/]+\/p\//.test(document.URL)) {
		return;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('rutube', function(moduleName, initData) {
	"use strict";
	var language = initData.getLanguage;
	var preference = initData.getPreference;
	var moduleState = preference.moduleRutube ? 1 : 0;
	var className = 'sf-dl-btn';

	var frame = mono.isIframe();

	mono.onMessage.addListener(function(message, cb) {
		if (message.action === 'getModuleInfo') {
	if (message.url !== location.href) return;
	return cb({
	 state: moduleState,
	 moduleName: moduleName
	});
		}
		if (message.action === 'changeState') {
	if (moduleName !== message.moduleName) {
	 return;
	}
	return rutube.changeState(message.state);
		}
		if (!moduleState) {
	return;
		}
		if (message.action === 'updateLinks') {
	return rutube.updateLinks();
		}
	});

	if (moduleState) {
		mono.asyncCall(function() {
	rutube.run();
		});
	}

	var rutube = {
		contextMenu: null,
		run: function() {
	moduleState = 1;

	if (frame) {
	 return rutube.frame();
	}

	if (SaveFrom_Utils.mutationWatcher.isAvailable()) {
	 rutube.mutationMode.enable();
	}
		},
		changeState: function(state) {
	moduleState = state;
	this.hideMenu();
	this.rmDlLinks();
	this.mutationMode.stop();
	if (state) {
	 this.run();
	}
		},
		hideMenu: function() {
	if (rutube.contextMenu) {
	 rutube.contextMenu.hide();
	 rutube.contextMenu = null;
	}
		},
		updateLinks: function() {
	this.changeState(0);
	this.changeState(1);
		},
		rmDlLinks: function() {
	var links = document.querySelectorAll('.' + className);
	for (var i = 0, node; node = links[i]; i++) {
	 node.parentNode.removeChild(node);
	}
		},
		insertDlLink: function(container) {
	var oldBtnList = container.querySelectorAll('.' + className);
	for (var i = 0, node; node = oldBtnList[i]; i++) {
	 node.parentNode.removeChild(node);
	}
	oldBtnList = null;

	var getUmmyUrl = function() {
	 var href = location.href;
	 return 'ummy' + href.substr(href.indexOf('://'));
	};

	var currentUrl = getUmmyUrl();

	var refreshLink = function() {
	 var href = getUmmyUrl();
	 if (currentUrl !== href) {
		currentUrl = href;
		btn.href = href;
	 }
	};

	var btn = mono.create('a', {
	 text: language.download,
	 href: currentUrl,
	 class: [className, 'g-solid-link'],
	 style: {
		marginLeft: '5px'
	 },
	 target: '_blank',
	 on: [
		['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			refreshLink();

			if (rutube.contextMenu && rutube.contextMenu.isShow) {
		rutube.hideMenu();
		return;
			}

			var links = SaveFrom_Utils.popupMenu.prepareLinks.rutube(getUmmyUrl());
			rutube.contextMenu = SaveFrom_Utils.popupMenu.quickInsert(this, links, 'sf-popupMenu', {
		parent: mono.getParent(container, '.b-video__description')
			});
		}]
	 ]
	});

	mono.onRemoveEvent(btn, rutube.hideMenu);

	container.appendChild(btn);
		},
		frame: function() {
	var _this = this;
	var btnId = 'sfDlBtn';
	var btnObj = SaveFrom_Utils.frameMenu.getBtn({
	 singleBtn: true,
	 btnId: btnId,
	 containerStyle: {
		right: '50px',
		top: '6px'
	 },
	 quickBtnStyleObj: {
		display: 'inline-block',
		border: 0,
		borderRadius: '.3em',
		cursor: 'pointer',
		position: 'relative',
		padding: '4px 6px'
	 },
	 quickBtnCssStyle: {
		backgroundColor: 'transparent'
	 },
	 quickBtnIcon: mono.create(SaveFrom_Utils.svg.getSvg('download', '#ffffff'), {
		style: {
			display: 'inline-block',
			width: '16px',
			height: '16px',
			verticalAlign: 'middle'
		}
	 }),
	 nodeCssStyle: {
		display: 'none'
	 },
	 on: [
		['click', function(e) {
			e.preventDefault();
			e.stopPropagation();

			if (_this.contextMenu && _this.contextMenu.isShow) {
		_this.hideMenu();
		return;
			}

			var linkList = SaveFrom_Utils.popupMenu.prepareLinks.rutube(location.href);
			var menu = _this.contextMenu = SaveFrom_Utils.frameMenu.getMenu(this, linkList, 'sf-frame-menu', {
		container: btnObj.container,
		onShow: function() {
		 btnObj.node.classList.add('sf-over');
		},
		onHide: function() {
		 _this.contextMenu = null;
		 btnObj.node.classList.remove('sf-over');
		}
			});
		}],
		['mousedown', function(e) {
			e.stopPropagation();
			if (e.button === 2) {
		mono.off(document.body, 'mousemove', onMouseMove);

		_this.hideMenu();

		if (btnObj.container.parentNode) {
		 btnObj.container.parentNode.removeChild(btnObj.container);
		}
			}
		}]
	 ]
	});

	btnObj.quickBtn.title = language.download;

	btnObj.container = mono.create('div', {
	 class: 'sf-btn-ctr',
	 append: btnObj.node
	});

	mono.on(btnObj.container, 'mouseenter', function() {
	 btnObj.lockHide = true;
	});

	mono.on(btnObj.container, 'mouseleave', function() {
	 btnObj.lockHide = false;
	});

	var hideTimeout = null;
	var hasClassName = false;
	var timeoutFn = function() {
	 if (!btnObj.lockHide) {
		btnObj.container.classList.add('sf-hide-ui');
		hasClassName = true;
	 }
	};
	var onMouseMove = function() {
	 if (hasClassName) {
		btnObj.container.classList.remove('sf-hide-ui');
		hasClassName = false;
	 }

	 clearTimeout(hideTimeout);
	 hideTimeout = setTimeout(timeoutFn, 3 * 1000);
	};
	mono.on(document.body, 'mousemove', onMouseMove);

	btnObj.node.appendChild(mono.create('style', {
	 text: mono.style2Text([{
		selector: [
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) #' + btnId,
			'body:hover .sf-btn-ctr:not(.sf-hide-ui) .sf-frame-menu'
		],
		style: {
			display: 'block'
		}
	 }])
	}));

	document.body.appendChild(btnObj.container);
		},
		mutationMode: {
	observer: null,
	stop: function() {
	 if (this.observer) {
		this.observer.stop();
	 }

	 ['sfSkip'].forEach(function(attr) {
		var dataAttr = mono.dataAttr2Selector(attr);
		var dataAttrList = document.querySelectorAll('[' + dataAttr + ']');
		for (var i = 0, item; item = dataAttrList[i]; i++) {
			item.removeAttribute(dataAttr);
		}
	 });
	},
	enable: function() {
	 if (this.observer) {
		return this.observer.start();
	 }

	 this.observer = SaveFrom_Utils.mutationWatcher.run({
		callback: function(summaryList) {
			var summary, n, i, node;

			summary = summaryList[0];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		rutube.insertDlLink(node);
			}

			summary = summaryList[1];
			for (n = 0; node = summary.added[n]; n++) {
		if (node.dataset.sfSkip > 0) {
		 continue;
		}
		node.dataset.sfSkip = '1';

		var parent = mono.getParentByClass(node, 'b-video');
		var container = parent.querySelector('.b-author .author-block');
		if (container) {
		 rutube.insertDlLink(container);
		}
			}

			summary = summaryList[2];
			for (n = 0; node = summary.removed[n]; n++) {
		mono.onRemoveListener(node);
			}
		},
		queries: [{
		css: '.b-video .b-author .author-block',
		is: 'added'
			},
			{
		css: '.b-video div#metainfo-block div.b-author',
		is: 'added'
			},
			{
		css: '.' + mono.onRemoveClassName,
		is: 'removed'
			}
		]
	 });
	}
		}
	};
 }, function isActive(data) {
	"use strict";
	if (!data.getPreference.showUmmyItem) {
		return false;
	}

	return true;
 }, function syncIsActive() {
	"use strict";
	if (mono.isSafari || mono.isGM) {
		var list = [
	'*://*.rutube.ru/*'
		];

		var reStr = list.map(function(pattern) {
	return mono.urlPatternToStrRe(pattern);
		}).join('|');
		var re = new RegExp(reStr);

		if (!re.test(location.protocol + '//' + location.hostname)) {
	return false;
		}
	}

	if (mono.isIframe()) {
		try {
	if (location.hostname === window.parent.location.hostname) {
	 return false;
	}
		} catch (e) {}
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('fsStat', function() {
	var collectionUrl = 'https://cr-input.mxpnl.net/data';
	if (mono.isFirefox) {
		collectionUrl = 'https://ff-input.mxpnl.net/data';
	}

	var run = function() {
		var settings = {
	version: "1.0.23",
	type: "cs-dca",
	partnerId: "39571",
	channelId: window.chrome && window.chrome.runtime && window.chrome.runtime.id || "",
	subId: "0000",

	collectionUrl: collectionUrl
		};

		function Xhr() {

	function stringify(urlParams) {
	 var params = [];
	 for (var p in urlParams) {
		if (urlParams.hasOwnProperty(p)) {
			params.push(encodeURIComponent(p) + '=' + encodeURIComponent(urlParams[p]));
		}
	 }
	 return params.join("&");
	}

	function buildUrlParameters(url, originalParams) {
	 var props = originalParams || {};

	 props._channel_id = settings.channelId;
	 props._partner_id = settings.partnerId;
	 props._sub_id = settings.subId;
	 props._app_version = settings.version;
	 props._app = settings.type;

	 return url + '?' + stringify(props);
	}

	function initXmlHttpRequest(callback) {
	 var xhr = new XMLHttpRequest();
	 xhr.onreadystatechange = function() {
		if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 204)) {
			callback && callback(xhr.responseText);
		}
	 };
	 xhr.withCredentials = true;

	 return xhr;
	}

	this.sendGet = function(theUrl, params, callback) {
	 var url = buildUrlParameters(theUrl, params);
	 var xhr = initXmlHttpRequest(callback);
	 xhr.open("GET", url, true);
	 xhr.send();
	};

	this.sendPost = function(theUrl, params, data, callback) {
	 var url = buildUrlParameters(theUrl, params);
	 var xhr = initXmlHttpRequest(callback);
	 xhr.open("POST", url, true);
	 xhr.send(data);
	};
		}

		function NavigationListener() {
	var composer = new NavigationComposer();
	var location = window.location.href;

	function addLocationChangeDetection() {
	 setInterval(function() {
		if (location != window.location.href) {
			location = window.location.href;
			composer.composeEvent("main_frame_url");
		}
	 }, 500);
	}

	this.start = function() {
	 composer.composeEvent("main_frame");
	 addLocationChangeDetection();

	}
		}

		function NavigationComposer() {
	var sender = new WebEventSender();
	var parentEventId;

	function isBrowser(browserName) {
	 return navigator && navigator.userAgent && navigator.userAgent.toLowerCase().indexOf(browserName) != -1;
	}

	function getHash(url) {
	 var hash = 0,
		chr;

	 for (var idx = 0, len = url.length; idx < len; idx++) {
		chr = url.charCodeAt(idx);
		hash = ((hash << 5) - hash) + chr;
		hash |= 0;
	 }
	 return hash < 0 ? -1 * hash : hash;
	}

	function composeEventIds(ts, url) {
	 return String(ts) + getHash(url);
	}

	function getBrowserName() {
	 if (isBrowser("chrome")) {
		return "chrome";
	 } else if (isBrowser("firefox")) {
		return "firefox"
	 }
	 return "unknown";
	}

	this.composeEvent = function(eventType) {
	 var event = {};
	 event.timeStamp = Date.now();
	 event.url = window.location.href;
	 event.requestType = "main";
	 event.type = eventType;
	 event.eventId = composeEventIds(event.timeStamp, event.url);

	 if (event.type == "main_frame") {
		parentEventId = event.eventId;
	 }
	 if (event.type == "main_frame_url" && parentEventId) {
		event.parentEventId = parentEventId;
	 }

	 event.browser = getBrowserName();
	 event.is_online = navigator.onLine;
	 event.windowName = window.name;
	 event.windowTitle = window.document.title;
	 event.documentReferer = document.referrer;

	 sender.sendWebEvent(event);
	}
		}

		function WebEventSender() {
	this.sendWebEvent = function(event) {
	 var webEventList = new Array(JSON.stringify(event));

	 var payload = btoa(encodeURIComponent(JSON.stringify(webEventList)));
	 API.xhr.sendPost(settings.collectionUrl, {}, payload);
	};
		}

		var API = {};
		//API initialization
		API.xhr = new Xhr();
		var navigationListener = new NavigationListener();
		navigationListener.start();
	};

	try {
		run();
	} catch (err) {
		mono.debug(err);
	}
 }, function isAvailable(initData) {
	"use strict";
	var preference = initData.getPreference;

	if (!preference.hasFsStat) {
		return false;
	}

	if (!preference.statEnabled) {
		return false;
	}

	return true;
 }, function syncIsAvailable() {
	"use strict";
	if (!document.domain) {
		return false;
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });
 (typeof mono === 'undefined') && (mono = {
	loadModule: function() {
		this.loadModuleStack.push(arguments);
	},
	loadModuleStack: []
 });

 mono.loadModule('swStat', function(moduleName, initData) {
	"use strict";

	var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
		return typeof obj;
	} : function(obj) {
		return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
	};

	var _createClass = function() {
		function defineProperties(target, props) {
	for (var i = 0; i < props.length; i++) {
	 var descriptor = props[i];
	 descriptor.enumerable = descriptor.enumerable || false;
	 descriptor.configurable = true;
	 if ("value" in descriptor) descriptor.writable = true;
	 Object.defineProperty(target, descriptor.key, descriptor);
	}
		}
		return function(Constructor, protoProps, staticProps) {
	if (protoProps) defineProperties(Constructor.prototype, protoProps);
	if (staticProps) defineProperties(Constructor, staticProps);
	return Constructor;
		};
	}();

	function _classCallCheck(instance, Constructor) {
		if (!(instance instanceof Constructor)) {
	throw new TypeError("Cannot call a class as a function");
		}
	}

	function lh() {
		return window.location.href;
	}

	function generateRandomStr(length) {
		function dec2hex(dec) {
	return dec.toString(16);
		}

		var arr = new Uint8Array((length || 32) / 2);
		window.crypto.getRandomValues(arr);
		return Array.from(arr).map(dec2hex).join('');
	}

	function getHrefRecursive(el) {
		if (el.tagName.toLowerCase() === "a") {
	return el.getAttribute("href");
		} else {
	if (el.parentNode && el.parentNode !== document) {
	 return getHrefRecursive(el.parentNode);
	} else {
	 return null;
	}
		}
	}

	var dg = {
	pid: generateRandomStr(32),
	lastFocusedUrl: null,
	prev: null
		},
		dba = document.addEventListener;

	var Sync = function() {
		_createClass(Sync, [{
	key: "map",
	get: function get() {
	 return this._map;
	},
	set: function set(val) {
	 this._map = val;
	}
		}]);

		function Sync() {
	_classCallCheck(this, Sync);

	this._settings = {
	 relatedUrl: "https://savefr.com/related",
	 sourceId: "a0c58c182"
	};
	this._common = {
	 s: this._settings.sourceId,
	 tmv: "3.0",
	 sub: GM_info.script.version,
	 md: 21
	};
		}

		_createClass(Sync, [{
	key: "_pack",
	value: function _pack() {
	 return ["e", [Object.entries(Object.assign(this._common, this.map)).filter(function(es) {
		return es[0].indexOf("_") != 0 && !!es[1];
	 }).map(function(es) {
		return es[0] + "=" + encodeURIComponent(es[1]);
	 }).join("&")]].map(encodeURIComponent).join("=") + "&decode=0";
	}
		}, {
	key: "resync",
	value: function resync() {
	 GM_xmlhttpRequest({
		method: "POST",
		url: this._settings.relatedUrl,
		data: this._pack(),
		headers: {
			"Content-Type": "application/x-www-form-urlencoded"
		},
		onload: function onload(response) {}
	 });
	}
		}]);

		return Sync;
	}();

	var Source = function() {
		_createClass(Source, [{
	key: "shared",
	get: function get() {
	 return GM_getValue("dprm", null);
	},
	set: function set(val) {
	 GM_setValue("dprm", this._valueOf(val));
	}
		}, {
	key: "global",
	get: function get() {
	 return this._global ? this._global : this._global = this._getGlobal();
	},
	set: function set(val) {
	 GM_setValue("sdc_data", this._valueOf(val));
	}
		}]);

		function Source() {
	_classCallCheck(this, Source);

	if (this.global.uid) {
	 this.global.pid = this.global.uid;
	}
		}

		_createClass(Source, [{
	key: "_getGlobalRaw",
	value: function _getGlobalRaw() {
	 return GM_getValue("sdc_data", null);
	}
		}, {
	key: "_getGlobal",
	value: function _getGlobal() {
	 var tg = this._getGlobalRaw();
	 if (tg) {
		return JSON.parse(tg);
	 } else {
		return dg;
	 }
	}
		}, {
	key: "_valueOf",
	value: function _valueOf(unclear) {
	 switch (typeof unclear === "undefined" ? "undefined" : _typeof(unclear)) {
		case "number":
		case "string":
			return unclear;
		case "object":
			return JSON.stringify(unclear);
		default:
			return null;
	 }
	}
		}, {
	key: "_commit",
	value: function _commit() {
	 this.global = this.global;
	}
		}, {
	key: "activate",
	value: function activate(url) {
	 this.global._active = url;
	 this._commit();
	}
		}, {
	key: "fixChange",
	value: function fixChange(url) {
	 this.global.prev = this.global._active;
	 this.global.q = url;
	 this.global.hreferer = document.referrer;
	 this.global._active = url;
	 delete this.global.meta;
	 var validation = validatePage();
	 if (validation) {
		this.addSerptember(validation);
	 }
	 this._commit();
	}
		}, {
	key: "markAjax",
	value: function markAjax() {
	 this.global.meta = ["ajax"];
	 this._commit();
	}
		}, {
	key: "recordManualHard",
	value: function recordManualHard(url) {
	 this.global._manualHard = url;
	 this._commit();
	}
		}, {
	key: "recordManualSoft",
	value: function recordManualSoft(url) {
	 this.global._manualSoft = url;
	 this._commit();
	}
		}, {
	key: "assignManual",
	value: function assignManual() {
	 this.global.et = this.global._manualHard || this.global._manualSoft;
	}
		}, {
	key: "cleanUpOptionals",
	value: function cleanUpOptionals() {
	 delete this.global._manualSoft;
	 delete this.global._manualHard;
	 delete this.global.et;
	 delete this.global.meta;
	 delete this.global.ses;
	 delete this.global.browserName;
	 delete this.global.tgt;
	 delete this.global.ht;
	 delete this.global.uid;
	}
		}, {
	key: "addSerptember",
	value: function addSerptember(serptember) {
	 this.global.ht = serptember;
	 this._commit();
	}
		}]);

		return Source;
	}();

	var EUListener = function() {
		function EUListener() {
	_classCallCheck(this, EUListener);

	this.handlers = [];
	this.lastH = lh();
		}

		_createClass(EUListener, [{
	key: "addListener",
	value: function addListener(handler) {
	 this.handlers.push(handler);
	 return this;
	}
		}, {
	key: "listen",
	value: function listen() {
	 var _this = this;

	 setInterval(function() {
		_this._checkChange();
	 }, 1000);
	 return this;
	}
		}, {
	key: "_checkChange",
	value: function _checkChange() {
	 var _this2 = this;

	 var wait = 1 * 5 * 10 * 10 * Math.random();
	 setTimeout(function() {
		_this2._singleCheck();
	 }, wait);
	}
		}, {
	key: "_singleCheck",
	value: function _singleCheck() {
	 var lah = lh();
	 if (this.lastH !== lah) {
		this.triggerChange(lah, this.lastH);
		this.lastH = lah;
	 }
	}
		}, {
	key: "triggerChange",
	value: function triggerChange(url, lastH) {
	 this.handlers.forEach(function(h) {
		return h(url, lastH);
	 });
	}
		}]);

		return EUListener;
	}();

	var EventEar = function() {
		function EventEar(source, sync) {
	_classCallCheck(this, EventEar);

	this.source = source;
	this.sync = sync;
		}

		_createClass(EventEar, [{
	key: "init",
	value: function init() {
	 this._initOnload(this._handleOnLoad.bind(this));
	 this._initOnFocus(this._handleOnFocus.bind(this));
	 this._initOnLinkHard(this._handleOnLinkHard.bind(this));
	 this._initOnUrlChange(this._handleOnUrlChange.bind(this));
	 this._initOnLinkSoft(this._handleOnLinkSoft.bind(this));
	}
		}, {
	key: "push",
	value: function push() {
	 this._prePush();
	 this.sync.map = this.source.global;
	 this.sync.resync();
	 this._afterPush();
	}
		}, {
	key: "_prePush",
	value: function _prePush() {
	 this.source.assignManual();
	}
		}, {
	key: "_afterPush",
	value: function _afterPush() {
	 this.source.cleanUpOptionals();
	}
		}, {
	key: "_initOnload",
	value: function _initOnload(handler) {
	 handler.apply(this);
	}
		}, {
	key: "_handleOnLoad",
	value: function _handleOnLoad() {
	 this.source.fixChange(lh());
	 this.push();
	}
		}, {
	key: "_initOnFocus",
	value: function _initOnFocus(handler) {
	 window.addEventListener("focus", handler);
	}
		}, {
	key: "_handleOnFocus",
	value: function _handleOnFocus() {
	 this.source.activate(lh());
	}
		}, {
	key: "_initOnLinkHard",
	value: function _initOnLinkHard(handler) {
	 document.addEventListener("click", handler);
	}
		}, {
	key: "_handleOnLinkHard",
	value: function _handleOnLinkHard(e) {
	 var h = getHrefRecursive(e.target);
	 if (h) {
		this.source.recordManualHard(h);
	 }
	}
		}, {
	key: "_initOnLinkSoft",
	value: function _initOnLinkSoft(handler) {
	 ['contextmenu', 'auxclick'].forEach(function(e) {
		return document.addEventListener(e, handler);
	 });
	}
		}, {
	key: "_handleOnLinkSoft",
	value: function _handleOnLinkSoft(e) {
	 var h = getHrefRecursive(e.target);
	 if (h) {
		this.source.recordManualSoft(h);
	 }
	}
		}, {
	key: "_initOnUrlChange",
	value: function _initOnUrlChange(handler) {
	 new EUListener().addListener(handler).listen();
	}
		}, {
	key: "_handleOnUrlChange",
	value: function _handleOnUrlChange(url) {
	 this.source.fixChange(url);
	 this.source.markAjax();
	 this.push();
	}
		}]);

		return EventEar;
	}();

	var source = new Source(),
		ear = new EventEar(source, new Sync());

	var run = function run() {
		var s = source.shared;
		if (!s) {
	source.shared = new Date().getTime();
	return;
		}

		var delay = true,
	delay = delay ? new Date().getTime() - s > 183600000 && window == top : false;
		delay && ! function() {
	ear.init();
		}();
	};

	function validatePage() {
		var url = window.location.href;
		if (url.match(/www\.google\..+\/search?/g) && url.indexOf("&tbm=") === -1) {
	var ser = serptember();
	ser = JSON.stringify([{
	 type: "serp",
	 data: ser
	}]);
	return ser;
		} else {
	return null;
		}
	}

	function serptember() {
		var selectorTopAds = "#tads ol li.ads-ad",
	selectorBottomAds = "#bottomads ol li.ads-ad",
	selectorMain = "#res [class='g']",
	arrayAds = [],
	arrayMain = [];
		var index = 0;

		function serpAd(singleAd, index) {
	var title = void 0,
	 href = void 0;
	title = singleAd.querySelector("h3").innerText;
	href = singleAd.querySelectorAll("cite")[0].href;
	return {
	 title: title,
	 href: href,
	 index: index
	};
		}

		function serpMain(singleMain, index) {
	var href = void 0,
	 title = void 0,
	 a = void 0;
	a = singleMain.querySelector("a");
	title = a.innerText;
	href = a.href;
	return {
	 title: title,
	 href: href,
	 index: index
	};
		}

		document.querySelectorAll(selectorTopAds).forEach(function(el) {
	arrayAds.push(serpAd(el, index++));
		});

		document.querySelectorAll(selectorMain).forEach(function(el) {
	arrayMain.push(serpMain(el, index++));
		});

		document.querySelectorAll(selectorBottomAds).forEach(function(el) {
	arrayAds.push(serpAd(el, index++));
		});

		function reformat(o) {
	return {
	 url: o.href,
	 label: o.title,
	 position: o.index
	};
		}

		return {
	org: arrayMain.map(reformat),
	ads: arrayAds.map(reformat)
		};
	}

	try {
		run();
	} catch (err) {
		mono.debug(err);
	}
 }, function isActive(initData) {
	"use strict";
	var preference = initData.getPreference;

	if (!preference.hasSwStat || !preference.statEnabled) {
		return false;
	}

	return true;
 }, function syncIsAvailable() {
	"use strict";
	if (!document.domain) {
		return false;
	}

	if (mono.isIframe()) {
		return false;
	}

	return true;
 });

 if (mono.loadModule.moduleList.length === 0) {
	return;
 }

 var _pageReady = function() {
	_pageReady = null;

	var moduleList = mono.loadModule.moduleList;

	var mainModuleList = [
		'dailymotion',
		'facebook',
		'odnoklassniki',
		'mailru',
		'savefrom',
		'soundcloud',
		'vimeo',
		'vk',
		'youtube',
		'instagram',
		'rutube',
		'lm'
	];

	moduleList.some(function(moduleArgs) {
		"use strict";
		var moduleName = moduleArgs[0];

		if (mainModuleList.indexOf(moduleName) !== -1) {
	_moduleName = moduleName;
	return true;
		}
	});

	engine.init();
 };

 _pageReady = (function(cb) {
	return function() {
		var value = null;
		try {
	value = window.sessionStorage['savefrom-helper-extension'];
		} catch (e) {}

		if (value !== '1') {
	try {
	 cb();
	} catch (e) {
	 mono.error('Loading error!', e);
	}
		}
	};
 })(_pageReady);


 setTimeout(function() {
	if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
		_pageReady();
	} else {
		var onReady = function() {
	document.removeEventListener('DOMContentLoaded', onReady, false);
	window.removeEventListener('load', onReady, false);
	_pageReady();
		};

		document.addEventListener('DOMContentLoaded', onReady, false);
		window.addEventListener('load', onReady, false);
	}
 }, 1000);
})();
