Greasy Fork is available in English.

VK Mod

Add some function on site "vk.com"

// ==UserScript==
// @name			VK Mod
// @name:ru			Мод для Вконтакте
// @namespace		SoThUg
// @version			0.11
// @description		Add some function on site "vk.com"
// @description:ru	Добавляет разные функции на сайт вконтакте
// @author			SoThUg
// @icon			https://github.com/sothug/vk-mod/blob/master/icon.png?raw=true
// @match			*://vk.com/*
// @match			*://*.vk.com/*
// @start-at		document-start
// @grant			none
// ==/UserScript==

"use strict"

const script_name = "VK Mod"

const storageName = "vk_mod_preference"
const DNTName = "vk_mod_do_not_type"
const DNRName = "vk_mod_do_not_read"
const default_storage = {
	ads_left: true,
	ads_block: true,
	marked_as_ads: true,
	story: false,
	recom_friend: true,
	recom_app: true,
	popul_clips: true,
	round: true,
	audio_ads: true,
	dnr: true,
	dnr_audio: true,
	dnt: true,
	dnt_audio: true,
	getDeep: 100
}


// vars on window
var vk = {id: 0},			// window.vk
ap,							// window.ap - аудиоплеер
cur = {peer: 0},			// window.cur

vkMod,						// window.vkMod
w,							// window

uiActionsMenu,				// window.uiActionsMenu
TopMenu						// window.TopMenu

var get = {
	varOnWindow(id, callback = null, deep = 1) {
		if (!isrtype(id, "", "get.varOnWindow")) {
			return false
		}

		if (!!w[id]) {
			console.debug(`get.varOnWindow: ${id} успешно получен`)
			callback.call(this, id, w[id])
			return true
		}

		if (deep > get.deep) {
			console.error(`get.varOnWindow: ${id} - превышен лимит ожидания(${get.deep})`)
			return false
		}

		w.setTimeout(get.varOnWindow, 100, id, callback, deep + 1)
		console.debug(`get.varOnWindow: ${id} - ожидание(${deep})`)
	}, 
	elementOnDOM(id, callback = null, deep = 1) {
		if (!isrtype(id, "", "get.elementOnDOM")) {
			return false
		}

		if (document.querySelectorAll(id).length) {
			console.debug(`get.elementOnDOM: "${id}" успешно получен`)
			callback.call(this, id, document.querySelectorAll(id))
			return true
		}

		if (deep > get.deep) {
			console.error(`get.elementOnDOM: "${id}" - превышен лимит ожидания(${get.deep})`)
			return false
		}

		w.setTimeout(get.elementOnDOM, 100, id, callback, deep + 1)
		console.debug(`get.elementOnDOM: "${id}" - ожидание(${deep})`)
	}
}
Object.defineProperty(get, "deep", {
	get: () => {return getStorageForId(storageName, vk.id)["getDeep"]}, 
	set: (val) => {setStorageForId(storageName, "getDeep", val, vk.id)}, 
	enumerable: true, 
	configurable: true
})

var menu = {
	html: `
<div id="vk_mod_menu">
	<div id="vk_mod_menu_bg" onclick="vkMod.menu.close()"></div>
	<div id="vk_mod_menu_wrap">
		<div class="popup_box_container" style="width: fit-content;height: auto;margin-top: 15vh;z-index: 10500;position: relative;" tabindex="0">
			<div class="box_layout">
				<div class="vk_mod_menu_box_title box_title_wrap" style="">
					<div class="box_x_button" aria-label="Закрыть" tabindex="0" role="button" onclick="vkMod.menu.close()"></div>
					<div class="box_title_controls" style="display: none;">Здесь что-то может быть</div>
					<div class="box_title">Настройки VK Mod</div>
				</div>
				<div class="vk_mod_menu_box_body box_body" style="display: block;padding: 1vh 2vh 1vh 2vh">
					<div class="settings_line">
						<div class="settings_label">Реклама</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_ads_left" type="checkbox" class="vk_mod_input _checkbox input_ads_left" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'ads_left', document.getElementsByClassName('input_ads_left')[0].checked, vk.id);
									vkMod.styles.updateStyles('ads_left')
									vkMod.menu.updateInputs('ads_left')
								">
								<label for="vk_mod_settigns_ads_left">Выключить рекламу слева</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_ads_block" type="checkbox" class="vk_mod_input _checkbox input_ads_block" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'ads_block', document.getElementsByClassName('input_ads_block')[0].checked, vk.id);
									vkMod.styles.updateStyles('ads_block')
									vkMod.menu.updateInputs('ads_block')
								">
								<label for="vk_mod_settigns_ads_block">Выключить блок "Рекламная запись"</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_marked_as_ads" type="checkbox" class="vk_mod_input _checkbox input_marked_as_ads" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'marked_as_ads', document.getElementsByClassName('input_marked_as_ads')[0].checked, vk.id);
									vkMod.styles.updateStyles('marked_as_ads')
									vkMod.menu.updateInputs('marked_as_ads')
								">
								<label for="vk_mod_settigns_marked_as_ads">Выключить посты с проплаченной рекламой</label>
							</div>
						</div>
					</div>
					<div class="settings_line">
						<div class="settings_label">Лента новостей</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_recom_friend" type="checkbox" class="vk_mod_input _checkbox input_recom_friend" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'recom_friend', document.getElementsByClassName('input_recom_friend')[0].checked, vk.id);
									vkMod.styles.updateStyles('recom_friend')
									vkMod.menu.updateInputs('recom_friend')
								">
								<label for="vk_mod_settigns_recom_friend">Включить "Рекомендованные друзья"</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_recom_app" type="checkbox" class="vk_mod_input _checkbox input_recom_app" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'recom_app', document.getElementsByClassName('input_recom_app')[0].checked, vk.id);
									vkMod.styles.updateStyles('recom_app')
									vkMod.menu.updateInputs('recom_app')
								">
								<label for="vk_mod_settigns_recom_app">Включить блок "Рекомендованные приложения"</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_popul_clips" type="checkbox" class="vk_mod_input _checkbox input_popul_clips" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'popul_clips', document.getElementsByClassName('input_popul_clips')[0].checked, vk.id);
									vkMod.styles.updateStyles('popul_clips')
									vkMod.menu.updateInputs('popul_clips')
								">
								<label for="vk_mod_settigns_popul_clips">Включить блок "Популярные клипы"</label>
							</div>
						</div>
					</div>
					<div class="settings_line">
						<div class="settings_label">Интерфейс</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_story" type="checkbox" class="vk_mod_input _checkbox input_story" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'story', document.getElementsByClassName('input_story')[0].checked, vk.id);
									vkMod.styles.updateStyles('story')
									vkMod.menu.updateInputs('story')
								">
								<label for="vk_mod_settigns_story">Выключить блок историй</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_round" type="checkbox" class="vk_mod_input _checkbox input_round" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'round', document.getElementsByClassName('input_round')[0].checked, vk.id);
									vkMod.styles.updateStyles('round')
									vkMod.menu.updateInputs('round')
								">
								<label for="vk_mod_settigns_round">Включить закругления</label>
							</div>
						</div>
					</div>
					<div class="settings_line">
						<div class="settings_label">Аудио</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_audio_ads" type="checkbox" class="vk_mod_input _checkbox input_audio_ads" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'audio_ads', document.getElementsByClassName('input_audio_ads')[0].checked, vk.id);
									vkMod.menu.updateInputs('audio_ads')
								">
								<label for="vk_mod_settigns_audio_ads">Выключить рекламу в аудиоплеере</label>
							</div>
						</div>
					</div>
					<div class="settings_line">
						<div class="settings_label">Активность</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_dnr" type="checkbox" class="vk_mod_input _checkbox input_dnr" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'dnr', document.getElementsByClassName('input_dnr')[0].checked, vk.id);
									vkMod.menu.updateInputs('dnr')
								">
								<label for="vk_mod_settigns_dnr" class="label_for_checkbox">Нечиталка</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_dnr_audio" type="checkbox" class="vk_mod_input _checkbox input_dnr_audio" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'dnr_audio', document.getElementsByClassName('input_dnr_audio')[0].checked, vk.id);
									vkMod.menu.updateInputs('dnr_audio')
								">
								<label for="vk_mod_settigns_dnr_audio" class="label_for_checkbox">Нечиталка голосовых</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_dnt" type="checkbox" class="vk_mod_input _checkbox input_dnt" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'dnt', document.getElementsByClassName('input_dnt')[0].checked, vk.id);
									vkMod.menu.updateInputs('dnt')
								">
								<label for="vk_mod_settigns_dnt">Неписалка</label>
							</div>
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_dnt_audio" type="checkbox" class="vk_mod_input _checkbox input_dnt_audio" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'dnt_audio', document.getElementsByClassName('input_dnt_audio')[0].checked, vk.id);
									vkMod.menu.updateInputs('dnt_audio')
								">
								<label for="vk_mod_settigns_dnt_audio">Неписалка голосовых</label>
							</div>
						</div>
					</div>
					<div class="settings_line">
						<div class="settings_label">Системные</div>
						<div class="settings_labeled_text">
							<div class="settings_labeled_row">
								<input id="vk_mod_settigns_getDeep" type="number" class="vk_mod_input _number input_getDeep dark" onchange="
									vkMod.setStorageForId('vk_mod_preference', 'getDeep', document.getElementsByClassName('input_getDeep')[0].value, vk.id);
									vkMod.menu.updateInputs('getDeep')
								">
								<label for="vk_mod_settigns_getDeep">Глубина ожидания переменной</label>
							</div>
							<div class="settings_labeled_row">
								<button id="vk_mod_settigns_updateInputs" class="vk_mod_button button_updateInputs flat_button dark" onclick="vkMod.menu.updateInputs()">
									Обновить поля ввода в меню
								</button>
							</div>
							<div class="settings_labeled_row">
								<button id="vk_mod_settigns_updateStyles" class="vk_mod_button button_updateStyles flat_button dark" onclick="vkMod.styles.updateStyles()">
									Обновить стили
								</button>
							</div>
						</div>
					</div>
				</div>
				<div class="box_controls_wrap">
					<div class="box_controls">
						<table cellspacing="0" cellpadding="0" class="fl_r">
							<tbody>
								<tr><td>
									<button class="flat_button secondary has_tooltip" onclick="vkMod.setStorage('vk_mod_preference', vk.id, vkMod.default_storage); vkMod.menu.updateInputs(); vkMod.styles.updateStyles()">
										Настройки по умолчанию
										<span class="tooltiptext">Устанавливает настройки по умолчанию</span>
									</button>
									<button class="flat_button has_tooltip" onclick="location.href='https://vk.me/join/AJQ1d94uiBiCO4Ymc0eeuD_f'">
										Перейти в беседу
										<span class="tooltiptext">Откроет беседу обсуждения данного мода</span>
									</button>
								</td></tr>
							</tbody>
						</table>
						<div class="box_controls_text _box_controls_text">VK Mod 0.11</div>
					</div>
					<div></div>
				</div>
			</div>
		</div>
	</div>
</div>
	`,
	updateInputs(id = undefined) {
		if (id) {
			let el = document.getElementsByClassName(`input_${id}`)[0]
			if (getStorageForId(storageName, vk.id)[id] == undefined) {
				setStorageForId(storageName, id, default_storage[id], vk.id)
			}

			console.debug("updateInputs:", id, getStorageForId(storageName, vk.id)[id])

			if (el.type == "number") {
				el.value = getStorageForId(storageName, vk.id)[id]
			}
			if (el.type == "checkbox") {
				el.checked = getStorageForId(storageName, vk.id)[id]
			}
		}
		else {
			let inputs_class = ["ads_left", "ads_block", "marked_as_ads", "story", "recom_friend", "recom_app", "popul_clips", "round", "audio_ads", "dnr", "dnr_audio", "dnt", "dnt_audio", "getDeep"]

			inputs_class.forEach((e, i, a) => {
				let el = document.getElementsByClassName(`input_${e}`)[0]
				if (getStorageForId(storageName, vk.id)[e] == undefined) {
					setStorageForId(storageName, e, default_storage[e], vk.id)
				}

				console.debug("updateInputs:", e, getStorageForId(storageName, vk.id)[e])

				if (el.type == "number") {
					el.value = getStorageForId(storageName, vk.id)[e]
				}
				if (el.type == "checkbox") {
					el.checked = getStorageForId(storageName, vk.id)[e]
				}
			})
		}
	},
	show() {
		if (!document.getElementById("vk_mod_menu")) {
			document.body.insertAdjacentHTML("afterBegin", menu.html)
		}
		if (!document.getElementById("vk_mod_style_menu")) {
			document.head.insertAdjacentHTML("beforeEnd", styles.menu)
		}

		menu.updateInputs()
	},
	close() {
		if (document.getElementById("vk_mod_menu")) {
			document.getElementById("vk_mod_menu").remove()
		}
		if (document.getElementById("vk_mod_style_menu")) {
			document.getElementById("vk_mod_style_menu").remove()
		}
	}
}

var styles = {
	menu: `
<style id="vk_mod_style_menu">
	[dir] #vk_mod_menu_bg {
		background: rgba(0, 0, 0, 0.7);
	}
	[dir=ltr] #vk_mod_menu,
	#vk_mod_menu_bg {
		left: 0;
	}
	#vk_mod_menu,
	#vk_mod_menu_bg {
		position: fixed;
		width: 100%;
		height: 100%;
		z-index: 10000;
		top: 0;
	}
	.vk_mod_menu_box_body {
		max-height: 50vmin;
		overflow-y: auto;
	}
	.vk_mod_input._number {
		width: 10vmax;
	}

	[dir] .settings_line {
		margin: 0;
		padding: 15px 0 14px;
		border-bottom: 1px solid var(--steel_gray_80);
	}
	[dir=ltr] .settings_label {
		padding: 6px 10px 6px 0;
		float: left;
	}
	.settings_label {
		color: var(--gray_600);
		width: 145px;
		line-height: 16px;
	}
	[dir=ltr] .settings_labeled_text {
		margin: 0 0 8px 155px;
	}
	[dir] .settings_labeled_text {
		padding-top: 6px;
	}
	.settings_labeled_row {
		margin-bottom: 5px;
	}

	.has_tooltip {
		display: inline-block !important;
	}
	.has_tooltip ._underlined {
		border-bottom: 1px dotted black;
	}
	.has_tooltip .tooltiptext {
		visibility: hidden !important;
		opacity: 0;
		transition: 0.5s;

		background-color: black;
		color: #fff;
		text-align: center;
		border-radius: 6px;
		padding: 5px;

		position: absolute !important;
		z-index: 150000;
	}
	.has_tooltip:hover .tooltiptext {
		visibility: visible !important;
		opacity: 1;
		transition: 0.5s 0.5s;

		z-index: 150000;
	}
</style>
	`,
	ads_left: `
<style id="vk_mod_style_ads_left">
	#ads_left {
		display: none !important;
	}
</style>
	`,
	ads_block: `
<style id="vk_mod_style_ads_block">
	._ads_block_data_w {
		display: none !important;
	}
</style>
	`,
	marked_as_ads: `
<style id="vk_mod_style_marked_as_ads">
	.marked_as_ads {
		display: none !important;
	}
</style>
	`,
	story: `
<style id="vk_mod_style_story">
	.stories_feed_wrap,
	.page_story_photo {
		display: none !important;
	}
</style>
	`,
	recom_friend: `
<style id="vk_mod_style_recom_friend">
	.feed_friends_recomm {
		display: none !important;
	}
</style>
	`,
	recom_app: `
<style id="vk_mod_style_recom_app">
	.apps_feedRightAppsBlock {
		display: none !important;
	}
</style>
	`,
	popul_clips: `
<style id="vk_mod_style_popul_clips">
	.ShortVideoFeedBlock {
		display: none !important;
	}
</style>
	`,
	round: `
<style id="vk_mod_style_round">
	.flat_button.profile_btn_cut_right {
		border-radius: 0px 10px 10px 0px !important;
	}
	.flat_button.profile_btn_cut_left {
		border-radius: 10px 0px 0px 10px !important;
	}
	.page_block,
	.eltt,
	#top_profile_menu,
	.ts_cont_wrap,
	.flat_button,
	.token,
	.left_count_wrap,
	.ms_items_more,
	.BaseModal__content,
	.ChatSettings,
	.ui_actions_menu,
	.page_actions_wrap,
	.submit_post_field,
	.box_no_title.box_no_buttons {
		border-radius: 10px !important;
	}
	.ui_search_input_block {
		border-radius: 8px !important;
	}
	.page_block_header,
	.page_block_h2,
	.ui_tabs_header,
	.box_title_wrap,
	.page_cover,
	.PopupHeader,
	.box_no_title,
	.ui_tabs,
	.ui_tabs_box {
		border-radius: 10px 10px 0px 0px !important;
	}
	.box_controls,
	.box_no_buttons,
	.ChatSettings__content {
		border-radius: 0px 0px 10px 10px !important;
	}
</style>
	`,
	updateStyles(id = undefined) {
		if (id) {
			let el = document.getElementById(`vk_mod_style_${id}`)
			if (getStorageForId(storageName, vk.id)[id] == undefined) {
				setStorageForId(storageName, id, default_storage[id], vk.id)
			}

			console.debug("updateStyles:", id, getStorageForId(storageName, vk.id)[id])

			if (getStorageForId(storageName, vk.id)[id] && !el) {
				document.head.insertAdjacentHTML("beforeEnd", styles[id])
			}
			if (!getStorageForId(storageName, vk.id)[id] && el) {
				el.remove()
			}
		}
		else {
			let styles_list = ["ads_left", "ads_block", "marked_as_ads", "story", "recom_friend", "recom_app", "popul_clips", "round"]
			
			styles_list.forEach((e, i, a) => {
				let el = document.getElementById(`vk_mod_style_${e}`)
				if (getStorageForId(storageName, vk.id)[e] == undefined) {
					setStorageForId(storageName, e, default_storage[e], vk.id)
				}

				console.debug("updateStyles:", e, getStorageForId(storageName, vk.id)[e])

				if (getStorageForId(storageName, vk.id)[e] && !el) {
					document.head.insertAdjacentHTML("beforeEnd", styles[e])
				}
				if (!getStorageForId(storageName, vk.id)[e] && el) {
					el.remove()
				}
			})
		}
	}
}

function getStorage(name = storageName) {
	let storage = JSON.parse(localStorage.getItem(name))
	if ((name == storageName) && !storage) {
		return default_storage
	}
	return storage || {}
}

function getStorageForId(name, id) {
	let storage = JSON.parse(localStorage.getItem(name))
	if ((name == storageName) && !storage) {
		return default_storage
	}
	return storage[id] || {}
}

function setStorage(name, key, value) {
	console.debug("setStorage(", name, ',', key, ',', value, ')')
	let storage = getStorage(name)

	storage[key] = value

	localStorage.setItem(name, JSON.stringify(storage))
}

function setStorageForId(name, key, value, id) {
	console.debug("setStorageForId(", name, ',', key, ',', value, ',', id, ')')
	let storage = getStorage(name),
	storage_for_id = storage[id] || {}

	storage_for_id[key] = value
	storage[id] = storage_for_id

	localStorage.setItem(name, JSON.stringify(storage))
}

function getDNTStatus(id) {
	return !(getStorageForId(storageName, vk.id)["dnt_preference"] || '').match(`.${id}.`)
}

function setDNTStatus(id, val = true) {
	console.debug("setDNTStatus(", id, ",", val, ")")
	let DNT = (getStorageForId(storageName, vk.id)["dnt_preference"] || '')

	if (val && DNT.match(`.${id}.`)) {
		setStorageForId(storageName, "dnt_preference", DNT.replace(`.${id}.`, ""), vk.id)
	}

	if (!val && !DNT.match(`.${id}.`)) {
		setStorageForId(storageName, "dnt_preference", DNT + `.${id}.`, vk.id)
	}
}

function getDNRStatus(id) {
	return !(getStorageForId(storageName, vk.id)["dnr_preference"] || '').match(`.${id}.`)
}

function setDNRStatus(id, val = true) {
	console.debug("setDNRStatus(", id, ",", val, ")")
	let DNR = (getStorageForId(storageName, vk.id)["dnr_preference"] || '')

	if (val && DNR.match(`.${id}.`)) {
		setStorageForId(storageName, "dnr_preference", DNR.replace(`.${id}.`, ""), vk.id)
	}

	if (!val && !DNR.match(`.${id}.`)) {
		setStorageForId(storageName, "dnr_preference", DNR + `.${id}.`, vk.id)
	}
}

function postsAds(id, oldVal, val) {
	switch (id) {
		case "cur":
			console.log(val.wallPage || val.rowsCont)
			if (getStorageForId(storageName, vk.id)["marked_as_ads"] == undefined) {
				setStorageForId(storageName, "marked_as_ads", default_storage["marked_as_ads"], vk.id)
			}
			let post_page = (val.wallPage || val.rowsCont)
			if (getStorageForId(storageName, vk.id)["marked_as_ads"] && post_page) {
				post_page.getElementsByClassName("wall_marked_as_ads").forEach((e, i, a) => {
					e.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.classList.add("marked_as_ads")
				})
				if (post_page == val.wallPage) {
					get.varOnWindow("wall", (id, val) => {
						let {showMore} = val

						val.showMore = function() {
							let result = showMore.apply(this, Array.prototype.slice.call(arguments))

							post_page.getElementsByClassName("wall_marked_as_ads").forEach((e, i, a) => {
								e.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.classList.add("marked_as_ads")
							})

							return result
						}
					})
				}
				if (post_page == val.rowsCont) {
					get.varOnWindow("feed", (id, val) => {
						let {showMore} = val

						val.showMore = function() {
							let result = showMore.apply(this, Array.prototype.slice.call(arguments))

							post_page.getElementsByClassName("wall_marked_as_ads").forEach((e, i, a) => {
								e.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.classList.add("marked_as_ads")
							})

							return result
						}
					})
				}
			}
			return val
		default:
			return val
	}
}

function setUI(id, val) {
	if (!isrtype(id, "", "setUI")) {
		return undefined
	}

	switch(id) {
		case "uiActionsMenu":
			let {show} = val

			val.show = function(t, e, i) {
				show.apply(this, Array.prototype.slice.call(arguments))

				let el = (document.getElementsByClassName("im-action_mute")[0] || document.getElementsByClassName("im-action_unmute")[0]);
				if (!el) {
					return !1
				}

				if (!document.getElementsByClassName("im-action_dnr").length && !document.getElementsByClassName("im-action_dnt").length) {
					el.insertAdjacentHTML("beforeBegin", 
						`<style type="text/css">
							[dir]
							.ui_actions_menu_item.im-action.im-action_dnr.true::before,
							.ui_actions_menu_item.im-action.im-action_dnt.true::before {
								background-position: 5px -405px;
							}
							[dir]
							.ui_actions_menu_item.im-action.im-action_dnr.false::before,
							.ui_actions_menu_item.im-action.im-action_dnt.false::before {
								background-position: 5px -305px;
							}
						</style>`)
					el.insertAdjacentHTML("beforeBegin", `<a 
															id="im_dnr" 
															tabindex="0" 
															role="link" 
															class="ui_actions_menu_item _im_action im-action im-action_dnr" 
															onclick="
																window.cur && !!cur.peer && vkMod.setDNRStatus(cur.peer, !vkMod.getDNRStatus(cur.peer));
																this.text = vkMod.getDNRStatus(cur.peer) ? ' Выключить нечиталку' : ' Включить нечиталку';
																this.classList.add((vkMod.getDNRStatus(cur.peer)).toString());
																this.classList.remove((!vkMod.getDNRStatus(cur.peer)).toString())
															">
																 ${vkMod.getDNRStatus(cur.peer) ? "Выключить нечиталку" : "Включить нечиталку"}
															</a>`)
					el.insertAdjacentHTML("beforeBegin", `<a 
															id="im_dnt" 
															tabindex="0" 
															role="link" 
															class="ui_actions_menu_item _im_action im-action im-action_dnt" 
															onclick="
																window.cur && !!cur.peer && vkMod.setDNTStatus(cur.peer, !vkMod.getDNTStatus(cur.peer));
																this.text = vkMod.getDNTStatus(cur.peer) ? ' Выключить неписалку' : ' Включить неписалку';
																this.classList.add((vkMod.getDNTStatus(cur.peer)).toString());
																this.classList.remove((!vkMod.getDNTStatus(cur.peer)).toString())
															">
																 ${vkMod.getDNTStatus(cur.peer) ? "Выключить неписалку" : "Включить неписалку"}
															</a>`)
					el.insertAdjacentHTML("beforeBegin", `<div class="ui_actions_menu_sep"></div>`)
					document.getElementById("im_dnr").classList.add((vkMod.getDNRStatus(cur.peer)).toString())
					document.getElementById("im_dnr").classList.remove((!vkMod.getDNRStatus(cur.peer)).toString())
					document.getElementById("im_dnt").classList.add((vkMod.getDNTStatus(cur.peer)).toString())
					document.getElementById("im_dnt").classList.remove((!vkMod.getDNTStatus(cur.peer)).toString())
				}
			}
			break;
		case "#top_profile_menu":
			let el = document.getElementById("top_profile_menu")

			if (!document.getElementById("top_vk_mod_menu_link")) {
				el.lastElementChild.insertAdjacentHTML("beforeBegin", `<a 
																		class="top_profile_mrow" 
																		id="top_vk_mod_menu_link" 
																		onclick="vkMod && vkMod.menu.show()" 
																		style="">
																			Настройки VK Mod
																		</a>`)
				el.lastElementChild.insertAdjacentHTML("beforeBegin", `<div class="top_profile_sep"></div>`)
			}
			break
		default:
			console.error(`setUI has no id: ${id}`)
			return () => {}
	}
}

function isrtype(id, rid, name) {
	if (typeof id !== typeof "") {
		console.error(`${name}: id must be a ${typeof rid}`)
		return false
	}
	return true
}

function XHRListener() {
	const {send} = XMLHttpRequest.prototype

	XMLHttpRequest.prototype.send = function (data) {
		if (/type=typing/.test(data) && getStorageForId(storageName, vk.id)["dnt"] && getDNTStatus(data.match(/peer=([0-9]+)/)[1])) {
			return this.abort()
		}
		if (/type=audiomessage/.test(data) && getStorageForId(storageName, vk.id)["dnt_audio"] && getDNTStatus(data.match(/peer=([0-9]+)/)[1])) {
			return this.abort()
		}

		if (/act=a_mark_read/.test(data) && getStorageForId(storageName, vk.id)["dnr"] && getDNRStatus(data.match(/peer=([0-9]+)/)[1])) {
			return this.abort()
		}
		if (/act=a_mard_listened/.test(data) && getStorageForId(storageName, vk.id)["dnr_audio"] && getDNTStatus(data.match(/peer=([0-9]+)/)[1])) {
			return this.abort()
		}

		return send.apply(this, Array.prototype.slice.call(arguments))
	}
}

function watcher(id, oldVal, val) {
	console.debug("Watcher:", id, oldVal, val);
	switch(id) {
		case "_adman":
			if (getStorageForId(storageName, vk.id)["audio_ads"] && val != null) {
				let {start} = val;
				val.start = function(a) {start.apply(this, Array.prototype.slice.call(arguments)); setTimeout(function() {val.skip()}, 100)};
			}
			return val
		default:
			return val
	}
}

function objWatch() {
	/*
	 * object.watch polyfill
	 *
	 * 2012-04-03
	 *
	 * By Eli Grey, http://eligrey.com
	 * Public Domain.
	 * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
	 */


	// object.watch
	if (!w.Object.prototype.watchh) {
		w.Object.defineProperty(w.Object.prototype, "watchh", {
			enumerable: false
			, configurable: true
			, writable: false
			, value: function (prop, handler) {
				var
				oldval = this[prop]
				, newval = oldval
				, getter = function () {
					return newval;
				}
				, setter = function (val) {
					oldval = newval;
					return newval = handler.call(this, prop, oldval, val);
				}
				;

				if (delete this[prop]) { // can't watch constants
					w.Object.defineProperty(this, prop, {
						get: getter
						, set: setter
						, enumerable: true
						, configurable: true
					});
				}
			}
		});
	}

	// object.unwatch
	if (!w.Object.prototype.unwatchh) {
		w.Object.defineProperty(w.Object.prototype, "unwatchh", {
			enumerable: false
			, configurable: true
			, writable: false
			, value: function (prop) {
				var val = this[prop];
				delete this[prop]; // remove accessors
				this[prop] = val;
			}
		});
	}
}

function setWindow(w) {
	w.vkMod = {}
	vkMod = w.vkMod

	vkMod.default_storage = default_storage

	vkMod.getStorage = getStorage
	vkMod.getStorageForId = getStorageForId
	vkMod.setStorage = setStorage
	vkMod.setStorageForId = setStorageForId

	vkMod.getDNTStatus = getDNTStatus
	vkMod.setDNTStatus = setDNTStatus
	vkMod.getDNRStatus = getDNRStatus
	vkMod.setDNRStatus = setDNRStatus

	vkMod.get = get

	vkMod.menu = menu

	vkMod.styles = styles
}

(function(window, undefined) {
	w = window
	if (w.self != w.top)
		return

	console.info(script_name)

	objWatch()

	XHRListener()
	setWindow(window)

	get.varOnWindow("uiActionsMenu", (id, val) => {uiActionsMenu = val; setUI(id, val)})
	get.elementOnDOM("#top_profile_menu", (id, val) => {setUI(id, val)})
	get.varOnWindow("vk", (id, val) => {vk = val; styles.updateStyles()})
	get.varOnWindow("ap", (id, val) => {ap = val; ap.ads.watchh(`_adman`, watcher)})
	get.varOnWindow("cur", (id, val) => {
		cur = val
		w.watchh(`cur`, postsAds)
		postsAds("cur", null, cur)
	})

	w.addEventListener('load', function() {
	})
})(window)