Полезная информация

Пользователи не любят читать документацию. Станьте оригинальным, будьте не как все. Ознакомьтесь с нашей базой знаний.

№1532604-04-2021 02:26:56

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 68.0

Re: Custom Buttons

Dumby пишет

Может так подойдёт

Ооо, супер, спасибо
Блин, забыл еще, можете добавить? чтобы вкладки контейнера после добавления закрывались все

Отсутствует

 

№1532704-04-2021 04:18:55

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 472
UA: Firefox 84.0

Re: Custom Buttons

как получить текущее значение userAgent из JS-кода, если ключ general.useragent.override сброшен ?
Например, у меня это: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:84.0) Gecko/20100101 Firefox/84.0"

Выделить код

Код:

// В этом примере не работает получение ЮзерАгента, а также показ строки статуса
var agent = navigator.userAgent;
StatusPanel._label = agent;
setTimeout(()=> StatusPanel._label = '',3000);

Отсутствует

 

№1532804-04-2021 09:38:57

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2247
UA: Firefox 68.0

Re: Custom Buttons

ВВП пишет

А tooltipText нельзя на значок ?

скрытый текст
                if (lab) {
                    if (td) doc.l10n.setAttributes(item, `${
                        active ? "dis" : "en"
                    }able-addon-button`);
                    else item.textContent = "Проверить обновление";
                }
                else if (!td) item.setAttribute("title", "Tooltip Text");

Stkvsky пишет

чтобы вкладки контейнера после добавления закрывались все

скрытый текст

Выделить код

Код:

(async id => {
	var menuitem = document.createXULElement("menuitem");
	document.getElementById(id).after(menuitem);
	typeof addDestructor == "function"
		&& addDestructor(() => menuitem.remove());
	menuitem.render = function() {
		this.id = "context_bookmarkContainer";
		this.label = "Добавить контейнер в закладки";
		this.setAttribute("oncommand", "bookmark()");

		var bm = PlacesUtils.bookmarks, attr = "usercontextid";
		var {toolbarGuid: parentGuid, TYPE_FOLDER: type} = bm;
		this.bookmark = async () => {
			var tab = TabContextMenu.contextTab;
			var title = tab.label, id = tab.getAttribute(attr);
			var {guid} = await bm.insert({title, parentGuid, type});
			var tabs = [];
			for(tab of gBrowser.visibleTabs)
				tab.getAttribute(attr) == id
				&& tabs.unshift(tab) && await bm.insert({
					parentGuid: guid, title: tab.label,
					url: tab.linkedBrowser.currentURI.spec
				});
			gBrowser.removeTabs(tabs);
		}
		var raf = () => menuitem.hidden =
			!TabContextMenu.contextTab.hasAttribute(attr);
		var {render} = this.constructor.prototype;
		(this.render = () => {
			requestAnimationFrame(raf);
			render.call(menuitem);
		})();
	}
})("context_reopenInContainer");

Dobrov пишет

как получить текущее значение userAgent из JS-кода, если ключ general.useragent.override сброшен ?

скрытый текст

Выделить код

Код:

Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).userAgent

Отсутствует

 

№1532904-04-2021 11:33:54

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 336
UA: Firefox 87.0

Re: Custom Buttons

Dumby
От теперь класс! Благодарю!
jgolzk27.jpg

Отсутствует

 

№1533004-04-2021 12:41:39

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 472
UA: Firefox 84.0

Re: Custom Buttons

Dumby проверь мой код QuickToggleAboutConfig со строки 144,
добавил переключение UserAgent, но параметр не меняется, почему-то тип general.useragent.override всегда логический, а не строковый…

Выделить код

Код:

// Quick Toggle https://forum.mozilla-russia.org/viewtopic.php?pid=784139#p784139
// https://forum.mozilla-russia.org/viewtopic.php?pid=784165#p784165
// Быстрое переключение параметров about:config
// Ctrl+Click или правый клик - сброс параметра по-умолчанию
// стиль иконки: #QuickToggleAboutConfigSettings .toolbarbutton-icon{ padding: 2px !important;}

(async (name, id, func) => {
	if (name == "Object") return CustomizableUI.createWidget(func());
	var win = name == "Window", g = Components.utils.import("resource://gre/modules/Services.jsm", {});
	if (g[id]) {if (win) return;} else g[id] = func();
	if (win) return CustomizableUI.createWidget(g[id]);
	addDestructor(r => r[5] == "e" && delete g[id]);
	g[id].onCreated(this);
})(this.constructor.name, "QuickToggleAboutConfigSettings", () => { // BEGIN (this.constructor… {код кнопки});

	var {prefs} = Services, db = prefs.getDefaultBranch("");
	var pv = parseInt(Services.appinfo.platformVersion);
	var xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
	var icon_vpn = "hue-rotate(270deg) brightness(95%)";
	function Notify(title, text, time){ Components.classes['@mozilla.org/alerts-service;1'].getService(Components.interfaces.nsIAlertsService).showAlertNotification(null, title, text, false, '', null, time);
	}
	function Antizapret(trg){
		if (Services.prefs.getIntPref('network.proxy.type') == 2) { // выключить
			Services.prefs.setIntPref('network.proxy.type', 0);
			Services.prefs.setStringPref("network.proxy.autoconfig_url", "127.0.0.1");
			trg.style.filter = '';
			Notify('Proxy', 'Режим по-умолчанию отключен', 2000);
		} else {
			Services.prefs.setIntPref('network.proxy.type', 2);
			Services.prefs.setStringPref("network.proxy.autoconfig_url", "https://antizapret.prostovpn.org/proxy.pac");
			trg.style.filter = icon_vpn;
			Notify('Proxy', 'Работаем через VPN Антизапрет', 2000);
		}
	}

	var useragent = Components.classes["@mozilla.org/network/protocol;1?name=http"].getService(Components.interfaces.nsIHttpProtocolHandler).userAgent; // текущий юзерагент

//=====================================================================================

	// refresh:
	//	false - reload current tab
	//	true - reload current tab skip cache
	//
	// restart:
	//	false - restart browser
	//	true - restart browser with confirm

//=====================================================================================

	var secondary = [{

			pref: ["network.proxy.autoconfig_url", "Прокси (VPN) URL"],
			userChoice: 0, userAlt: 1, refresh: true,
			values: [
				["127.0.0.1", "отключен…", "0", "", `Services.prefs.setIntPref('network.proxy.type', 0); node.parentNode.parentNode.style.filter = '';`],
				["https://antizapret.prostovpn.org/proxy.pac", "АнтиЗапрет", "1", "Надёжный доступ на заблокированные сайты\n\n«Режим прокси» меняется на 2", `Services.prefs.setIntPref('network.proxy.type', 2); node.parentNode.parentNode.style.filter = icon_vpn;`],
				[prefs.getStringPref("user.pacfile", "file:///etc/proxy.pac"), "user .pac файл", "2"],
				["https://git.io/ac-anticensority-pac", "ac-anticensority", "3"],
				["localhost", "Tor Browser", "4"]
	]},{
			pref: ["network.proxy.type", "Режим прокси"],
			userChoice: 0, userAlt: 2, refresh: true,
			values: [
				[0, "Без прокси", "0", "по-умолчанию"],
				[5, "Системные (из IE)", "5"],
				[2, "Автонастройка", "2", "about:config — user.pacfile"],
				[1, "Ручная настройка", "1", "вторая строка"],
				[4, "Автоопределение", "4"]
	]},{
			pref: ["network.proxy.share_proxy_settings", "Прокси для всех протоколов"],
			userChoice: true, refresh: true,
			values: [[true, "Да", "", "Прокси для всех протоколов при ручной настройке"], [false, "Нет"]]
	},{
			pref: ["network.trr.mode", "DNS через HTTPS"],
			userChoice: 2, userAlt: 0, refresh: true,
			values: [
				[0, "Выключен", "0"], [2, "TRR + мой", "2"], [3, "только TRR", "3"]
	]},null,{
			pref: ["permissions.default.image", "Загрузка изображений"],
			userChoice: 1, userAlt: 3, refresh: true,
			values: [[1, "Разрешена"], [3, "Только с сайта"], [2, "Отключить"]]
	},{
			pref: ["image.animation_mode", "Анимация изображений"],
			userChoice: "none", refresh: true,
			values: [["none", "Выключена"], ["normal", "По циклу"], ["once", "Единожды"]]
	},{
			pref: ["browser.display.document_color_use", "Использовать цвета сайтов"],
			userChoice: 0,
			values: [[0, "Авто", "0"], [1, "Всегда", "1"], [2, "Никогда", "2"]]
	},{
			pref: ["browser.display.use_document_fonts", "Загружать web-шрифты"],
			userChoice: 1, refresh: true,
			values: [[1, "Да"], [0, "Нет"]]
	},null,{
			pref: ["media.autoplay.default", "Авто-play аудио/видео"],
			userChoice: 5, refresh: true,
			values: [
				[0, "Разрешить", "0"],
				[1, "Запретить", "1"],
				[2, "Спрашивать", "2"],
				[5, "Блокировать", "5"]
	]},{
			pref: ["media.autoplay.blocking_policy", "Автозапуск (политика)"],
			userChoice: 1, userAlt: 2, refresh: true,
			values: [
				[1, "Временная", "1"],
				[2, "По действию", "2"],
				[0, "Постоянная", "0"]
	]},{
			pref: ["plugin.state.flash", "Flash-plugin"],
			userChoice: 2, refresh: true,
			values: [
				[2, "Всегда включать", "2"],
				[1, "Включать по запросу", "1"],
				[0, "Никогда не включать", "0"]
	]},{
			pref: ["gfx.webrender.all", "WebRender для всего"],
			userChoice: false, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},null,{
			pref: ["javascript.enabled", "Выполнять скрипты Java"],
			userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["network.cookie.cookieBehavior", "Cookies"],
			userChoice: 1, userAlt: 3, refresh: false,
			values: [
				[1, "Не принимать сторонние"], [3, "Не принимать с не посещенных"], [4, "Не принимать от трекеров"],
				[2, "Не принимать со всех"], [0, "Принимать со всех"]
	]},{
			pref: ["dom.enable_performance", "Статус загрузки страницы"],
			userChoice: false
	},{
			pref: ["dom.storage.enabled", "Локальное хранилище"],
			userChoice: true
	},{
			pref: ["network.http.sendRefererHeader", "Referer - для чего"],
			userChoice: 1,
			values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки и изобр.", "2"]]
	},{
			pref: ["media.peerconnection.enabled", "WebRTC утечка IP"],
			userChoice: false
	},{
			pref: ["general.useragent.override", "User Agent"],
			userChoice: true, refresh: true,
			values: [
				[useragent, "По-умолчанию", "", "", `Services.prefs.setStringPref('general.useragent.override', '')`],
				["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56"],
				["Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56 Linux"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:68.0) Gecko/20100101 Firefox/68.0", "Firefox 68 MacOSX"],
				["Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", "MSIE 6.0 Windows"],
				["Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", "Chrome61 Android7"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30", "Safari 6 MacOSX"],
				["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
				["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36", "Chrome57 W7"],
				["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", "Chrome61 W10"],
				["Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", "Samsung Galaxy S6"],
				["Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)", "Playstation 4"],
				["Xbox (Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Xbox One (mobile)"],
				["Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Microsoft Lumia 950"],
				["Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350)", "Windows Phone"],
				["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"]
	]}
	];

	return {
		label: "Quick Toggle Settings",
		id: "QuickToggleAboutConfigSettings",
		tooltiptext:
`Quick Toggle Settings

ЛКМ	Боковая панель: Журнал
ПКМ	Меню основных настроек
Long	Антизапрет proxy`,
		localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAdeSURBVHjaACcA2P8AbW1r2J6UnfEBAQEAAH9/feRepVj/4Zcn/wCCgX/k7+/v//+7KP8AAAD//wAnANj/AIuKifGIiIbxAAAAAACyv7f//////xUVFG8A7Ozs/+vIe/+vezrUAAAA//8ASwC0/wCQiI7kUp9P/2G5U//2/fb/s7S0/w8QEBIAgYB/5PHx8f/jzKn/6caJ///oev9NKiKFAIqIhuTu7e3/7+/v//WzPf/Dom7/ZzEJwAAAAP//JMk/CwFxAMfhL0mSRRbvglImsxfgFVjMFn9uMIkwyiqyobwBVpnuFoOyooT86uTOcOlj8KyPBv2euxjN3U637TYb9ZukkCSptbawLiWqqwqO4zCdTJAkAZTPeQD29gbgH0EQ8PY8vo85fIY4/RqZVFjyfR9jDNf7C7YF2OXIpqMJ+Z6HMU9OR5vlbAyHIvFYJPljO/5dEgjjAA5/Cxtu8aIcCm5yE6JZlMbm/oegpYZqiBbr9AjCOUIQG6q1tqAabGrVhhYhXOqt9N77lcLFGfZpcuvZHrHtAwXQem6TvhNW6wUmtKe5vDjHNM20TDSqDbXymKPqblDv7eO8rrPWytMddBn9jOi5GoBMZn5HRETK9qECCMOQWueUZvcBgGQMwUsNngp89kMsy9oWERGnYqs4jvF9nyAIcPUXnWYJkhJEJ/z2tyDZhMEeY3WMOJWyGg6HeJ6H1pooinh7/+D7qgg3Obhfhts82cU5J7tgpOQ/s4bMpKanjMnPdovXnaMlRET+yKKfkCbjOI7j37ooo0PQn0NUCFIUo2J2iKhD1govQZcg+uMKo6QiCG9BROlYNbTA/iAWiSnqwQwJZJsu8WKCWUSWmlmxoK1nz+Oe32/PmsHju4PkCA+fw+fw+lw+EgzWEqyrtVsan9odjZ32nfAtOxQK2vX1YXw+3+kla79zOQC8TaWU9Aufp2YAyGazOI5D+f59j/8DM6kvnO84Q/nL7TwwaqhqryQYvw5AV1cnA/E4gUCgZxFEpqNcThyi1bzJ3cQlqqcr8L9aOO/b9wQAkb4+FkE+lwfgaPdBjn/cy8jYKG7exZzN4r4+yfhQL7FYtAAcxwEXeid6aJp4SFb/ASAxOQ7uNcaeHOHN23cFoLUmk8mglCL5y2Lu6zPsT/ew3zcAj0jdLqP7fjMbRIov+teJaK0wDAPLslBKMzoYwTWvMK8bmDdugK4GfQHyIX60HZ4UpQrAsiycXJ7hgRfw4RTEvRDdymysghO+VWfrKktFlFKkUinS6TSmaZLROaYiYXi+GSLbYGgPsatliEjxzk0rRJLJn1imuRDLxLAUc4PnGGmtoqW5DYYPoNt3IyL+krVFRUueXyYiKz3LV//rW9Z7dtB/jF1rpMa70SN/+Sj/mKjrMI5/AQPa6MeSFj9a2SjMRckdd5QKlBA3bC63yg006pqxNpv2Q1tuAUdYY1iN0Y9lM1nM3TAdSrLiEA9CJAFjpQdywV0aHeD9kO9x3Pd+H6/+AG5erJ7t89dnn+f1ed7P834EjabKsjweAMFAkN7pXo6av6X52nf0T14g6Avy7zAajdRoqsmWZWsSEhJib+FFh0ZTZfF5vZGH3X39FGufomwyi303VFS07mRw+DIAYV8YAK/Xw+joCHa7nXAoTItWy5aSkq6kpKT0pbSxEUDToaOWc+ZzZHWnUWR4ALVpAwct5RyeOUCz9UM+nalg9+TTFBseJL/nca65ri8Nv4TodOK4OYvVZgNAr9eTmppaEVVBY32DZWJ6nErd++w/sYdXju1EfX4bH0y9wL4/t1Gme55d2l3sbXmT2k4NUkhalGjsd8aOqTEcKccyuQjt1HWQnLz6rRUS3dqDC8P9bGp+gvX9KazrTGNL07OMmyai9LdOzfDXyHFwvsOcLouR1moA+vr6SEm57+0oQE1NtUWSFn8lSRKBQCAqmdsygfniSa6eP45xQMdwTxej+haC9kbgEzC9hOnL9VjGBhg0GHkoI2O3IAhCfOIqobTo/sUl6XK58Hg8OBwORFFEFEWcc3OIznmuDukxDzQQ9jYCdeCrBV89eBrB9xW4G/Fd/hhrayVXvnmP3sqXp069puo6sjX75Beb0w9EAex2Ow6HI2I7URSZn3djv+niysDP/D1Yx4KtHmY/A8t+FkxlYChkYUgGv6yDXzPhDyW+IVXwh4ObzpQWpBVHAJLkwWaz4XA4IjZdhrjm3dywO/mt5wSzHTvgJzm0ZUL7o/DjY9CtZEGfR0ftczz58JqqxFWCcO/d8YJalb7UA7e0NN9e/H4/fr+fQDBIMBQiHA4TAqzTU1g73oVTa0EnY/r0dsa/fxVJXwiXcuGSCuOhbHKS47SCIMgS42OTczOTbhMK8vMalMqcswqFvE2pyFlxcuTyNoVCcfr17QVn6/bmD71YIu/Jzd14ZnNhUXvBM4VtSuXG9s/35Ju5uBV6i2nakXL9rjihVLn2TuHrNzL+w97/EzExK3fRHbfHCLI1q+9pUG/46HD5Iy3K9Li85ft/BgCgAJvUrkqRFQAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

			btn.btn = true;
			btn.domParent = null;
			btn.popups = new btn.ownerGlobal.Array();
			this.createPopup(doc, btn, "secondary", secondary);
			this.createCloseMenusOption(doc, btn);

			if (Services.prefs.getIntPref('network.proxy.type') == 2)
				btn.style.filter = icon_vpn; // btn.style.cssText = "background-image: -moz-linear-gradient(#c0c8c0, #c0c8c0, #c0c8c0) !important";

			btn.linkedObject = this;
			for(var type of ["command", "contextmenu", "mousedown", "auxclick"])
				btn.setAttribute("on" + type, `linkedObject.${type}(event)`);

		},
		createPopup(doc, btn, name, data) {
			var popup = doc.createElementNS(xul_ns, "menupopup");
			var prop = name + "Popup";
			btn.popups.push(btn[prop] = popup);
			popup.id = this.id + "-" + prop;
			for (var type of ["popupshowing", "click"])
				popup.setAttribute("on" + type, `parentNode.linkedObject.${type}(event)`);
			for(var obj of data) popup.append(this.createElement(doc, obj));
			btn.append(popup);
		},
		map: {b: "Bool", n: "Int", s: "String"},
		createElement(doc, obj) {
			if (!obj) return doc.createElementNS(xul_ns, "menuseparator");
			var pref = doc.ownerGlobal.Object.create(null), node, img, bool;
			for(var [key, val] of Object.entries(obj)) {
				if (key == "pref") {
					var [apref, lab, akey, ttt] = val;
					pref.pref = apref; pref.lab = lab || apref;
					if (ttt) pref.ttt = ttt;
				}
				else if (key == "image") img = val, pref.img = true;
				else if (key != "values") pref[key] = val;
				else pref.hasVals = true;
			}
			var type = prefs.getPrefType(pref.pref);
			var str = this.map[type == prefs.PREF_INVALID
				? obj.values ? (typeof obj.values[0][0])[0] : "b"
				: type == prefs.PREF_BOOL ? "b" : type == prefs.PREF_INT ? "n" : "s"
			];
			pref.get = prefs[`get${str}Pref`];
			var map, set = prefs[`set${str}Pref`];
			if (pref.hasVals) {
				for(var [val, , , , code] of obj.values)
					code && (map || (map = new Map())).set(val, code);
				if (map) pref.set = (key, val) => {
					set(key, val);
					map.has(val) && eval(map.get(val)); // выполнить код
				}
			}
			if (!map) pref.set = set;

			node = doc.createElementNS(xul_ns, "menu");
			node.className = "menu-iconic";
			node.setAttribute("closemenu", "none");
			img && node.setAttribute("image", img);
			akey && node.setAttribute("accesskey", akey);
			(node.pref = pref).vals = doc.ownerGlobal.Object.create(null);
			this.createRadios(doc,
				str.startsWith("B") && !pref.hasVals ? [[true, "true"], [false, "false"]] : obj.values,
				node.appendChild(doc.createElementNS(xul_ns, "menupopup"))
			);
			if ("userChoice" in obj) pref.noAlt = !("userAlt" in obj);
			return node;
		},
		createCloseMenusOption(doc, btn) {
			var pn = this.closePref = "QuickToggleAboutConfigSettings.closeMenus";
			var data = [null, {
				pref: [pn, "Закрывать меню этой кнопки"], values: [[true, "Да"], [false, "Нет"]]
			}];
			var setCloseMenus = e => {
				e.stopPropagation();
				var trg = e.target, {pref, val} = trg, updPopup = true, clear;
				switch(e.type) {
					case "command": pref = (trg = trg.closest("menu")).pref; updPopup = false; break;
					case "click": if (e.button) return; break;
					case "contextmenu": e.preventDefault(); clear = pref;
				}
				if (!pref) return;
				if (clear) prefs.clearUserPref(pn);
				else if (!updPopup && val === pref.val) return;
				else pref.set(pn, val !== undefined ? val : !pref.val);
				this.upd(trg);
				updPopup && this.popupshowing(null, trg.querySelector("menupopup"));
			}
			(this.createCloseMenusOption = (doc, btn) => {
				for(var obj of data)
					btn.secondaryPopup.append(this.createElement(doc, obj));
				var m = btn.secondaryPopup.lastChild;
				m.style.cssText = "fill: lightblue !important; list-style-image: url(chrome://browser/skin/menu.svg) !important;";
				m.setAttribute("oncommand", "setCloseMenus(event)");
				m.onclick = m.oncontextmenu = m.setCloseMenus = setCloseMenus;
			})(doc, btn);
		},
		UserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACRUlEQVR42qWTS2gTQRjH/zPJZo2BDfgkB62vYg5CjQdvRUE8WB8HKyahYotIQQOeFKGCF7EgvQlBqOCjWFKlevBRDyL4uHloLXhYqWJroaEqlETSptndGb+ZPDH0ojN8O8vA98vv+/Itw38u1nSRYRE6UhKyg4FF1R292/Q+xhhLi4TIrgjgIzwe4MbgmR2nrZMtxxEN63zYORuPZp7gzpehvCu9XjfuPmwCUHIiEtw4fK89zVvD2+FHAD56quXBpV3CVO4ret6nxHzxZxdBRuqADCKmz7RHD9y2tlmbYbBVfwE8DXBkEd/y39H5+mze8ZyoSIpsFXC9u/VE3/ldpyg5AAMEoJMTQi2hANKBgyKdy0h/eoChqcf9MimvVAHjd/fdiG0NRwgQpDAp1agBpDZwdHKJLKZzc+h5e3kCSexh5c5j8fmhW0HGpNZXFkqfsQpAKgcFKGmAlAxHXp5bkkmsrhosjh4cCIJ5pG5qfQXgtMslqO1qgEMWTPrR+eriEhoA4wN7L8TWh0KVXzfgY2RQAUjVBenWLH4UCrj04Wa9BNXEo5va+w63xCiJ6WRVP2MVgNQIgjgEA17MTODZ7Lt+AtSaGDG4377a1m2FAkKrc11/dUwIID1dSKHEcW3yft4RbpR6kK1PYgaJtaY1nNp5jIeMsjKNbs1AWRUcjvTnp2Kh9LtLJGTDINUhcTIZ3L+hzdq9ZgvWmSF9/Wu5gI8L03gzP5l3hddL/3/zKDdA9MdE0UERrdzaFGMUaap75Y/pX9Yfap76EQYaCeEAAAAASUVORK5CYII=",
		notUserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACNUlEQVR42qXTT2jTUBwH8G9e0ta2ks5ND0Ww/qX1oGyevEwQnOBA6EHXwLwoUh3eRGRs501RPE6xgp6KrexQECZY8DBPXmbRS4Z/K0gRtmmDa2yb5PlLX7p2Gwpi4EfCy8sn3/dPwn9e0saGnCRF6XYFnA9DkhKtRs51ep6jmtEcp/JHIM9Yivl9mf0XNDV2Ngn1YLzVxdB1lJ8U8P5hzuCWnR6xrPwmIM8kLRjdkT326BaLHNgLKAFA9ouXdhOw6qi++4j589cd89vSaMqyc2sAPUXlgF8fyt9WI3t2Av4w4NsCMJ8AHAKav6hqqH76iuLINcNuNhOawyttYCp+7tRE/6XT9GFQAG4CpniATQkIaNQIMVG6/xSL2WfTGueTbWDh5L2rA9t29QGBrQQEPUDuAuoEmFSr+P5lGc8v33mtAUfaQO3M4/GgDOoYCIkUin8D0BDDoBQ2ZMxqN0wCQh0gMxaUnYb4uzt+dwIZ8wBHTGQbYAHMpu+uAxaGxpMDvSr3AHcFaAIlD+BtoN6ag5WqhOLNwrohTMWPH5roP7pdLIwb3wW6EziWGAZhpVdLWHzxdpqAyc4yKrJ+4uKg2oMV+lgRK9CdwAVsG1WpF8UH84Zt2QkCKmsbiRAtFAllB5OHWQ9fpr81OwlcgBL9kPrwsvDGMQ1zNMV5ZyN1ISlZYZl9/TE1tjuMSFgA1VWO8uef+FAqG47tpFMcm7dyFyIOEzBMlfCadao5qhk39l9P479evwFbiOcRSXKueAAAAABJRU5ErkJggg==",
		UserAltImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIfSURBVDhPpVM9b9NQFD02cSKKFVGKIFFREFsmFiCA1AEQQrQjC0gssLCkC0jJj0iFWFoGdiT4Ay0SEmJBhPI5FBokikL4SCC0Te2UxInt13Mdu1CVrVc6713fe86x/T6w09DCeTNKJ7Q0pzyUmoCmZYOiUhXms8RMcd6vB7UwthhM5fTLWix2L3PmYjKdOwdz9DDFGtr1KuovnqD29JGlfO9Goew9DCV/DaZy2pXE8PD9Y/mbupk6xE6cVWPQhEujHo2+4fXdO76ztnq1UPYfSCcwKB1HWjeMyunCraSZOsDqbkBPsBOTNoMGvkOTLto/f+F56bble262OK/qesiYzIydTJojFLjrgEe4bcIOEdXWYe6LITN2KgmFSRFGBuPpo0eAngX0SexTJHD5LOgLpEZTcgKuUuMijAyy5l7+r0NSb21g5LQI5gGYSy2AjYBLjQyRAUniTnJXsBoayBzm3RBBndwwIoOK3fgNdIREQYCV/0DqLdiNZqCRITKYayx85RfQuUNiZ3k7IgO+vfGeXGpkiAyma++WLHtZhZ8o5H/FEVpos1V7u8TFwLQId8nw+Afa51Oq2vyycmlk9KCW0GXFZeu63IUO8z88Bx5s28SbuUXfddzrxVcobxpI0GTh7H734/dPzQt9d0/CGBpCPB7nbhmwbAPVxR4+PPtsuU7/WuElth/lKORUcsoTE8TgMg0WbJaY4Zu3XKYdBrABARUk+ls4DfQAAAAASUVORK5CYII=",
		upd(node) {
			var {pref} = node, def = false, user = false, val;
			if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
				var pn = pref.pref;
				try {val = pref.defVal = db[pref.get.name](pn); def = true}
				catch(ex) {def = false;}
				var user = prefs.prefHasUserValue(pn);
				if (user) try {val = pref.get(pn, undefined);} catch(ex) {}
			}
			if (val == pref.val && def == pref.def && user == pref.user) return;
			pref.val = val; pref.def = def; pref.user = user;
			var exists = def || user;

			var ttt = exists ? val : "Этого префа не существует";
			if (ttt === "") ttt = "[ empty_string ]";
			ttt += "\n" + pref.pref;
			if (pref.ttt) ttt += "\n" + pref.ttt;
			node.tooltipText = ttt;

			var img, alt = "userAlt" in pref && val == pref.userAlt;
			if (alt) img = this.UserAltImg;
			if ("userChoice" in pref)
				if (val == pref.userChoice)
					//node.style.removeProperty("color"),
					img = this.UserChoiceImg;
				else {
					//node.style.setProperty("color", "maroon", "important");
					if (!alt) img = this.notUserChoiceImg;
				}
			if (!pref.img) img
				? node.setAttribute("image", img)
				: node.removeAttribute("image");
			user
				? node.style.setProperty("font-style", "italic", "important")
				: node.style.removeProperty("font-style");

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "Другое" : "По умолчанию";
				lab += ` — "${sfx}"`;
			}
			node.setAttribute("label", lab);
		},
		createRadios(doc, vals, popup) {
			for(var arr of vals) {
				if (!arr) {
					popup.append(doc.createElementNS(xul_ns, "menuseparator"));
					continue;
				}
				var [val, lab, key, ttt] = arr;
				var menuitem = doc.createElementNS(xul_ns, "menuitem");
				menuitem.setAttribute("type", "radio");
				menuitem.setAttribute("closemenu", "none");
				menuitem.style.setProperty("font-style", "italic", "important"),
				menuitem.setAttribute("label", popup.parentNode.pref.vals[val] = lab);
				key && menuitem.setAttribute("accesskey", key);
				var tip = menuitem.val = val;
				if (ttt) tip += "\n" + ttt;
				menuitem.tooltipText = tip;
				popup.append(menuitem);
			}
		},
		openPopup(popup) {
			var btn = popup.parentNode;
			if (btn.domParent != btn.parentNode) {
				btn.domParent = btn.parentNode;
				var pos;
				if (btn.matches(".widget-overflow-list > :scope"))
					pos = "after_start";
				else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
					btn.closest("toolbar").getBoundingClientRect(), pos = width > height
						? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
						: `${win.innerWidth - right > left ? "end" : "start"}_before`;
				for(var p of btn.popups) p.setAttribute("position", pos);
			}
			popup.openPopup(btn);
		},
		maybeRestart(node, conf) {
			var msgRest = "Перезапустить браузер?", msgAbort = "Запрос на выход отменен.";
			if (pv >= 77) {
				var title = node.closest("toolbarbutton").label;
				var pp = domWin => Services.prompt.wrappedJSObject.pickPrompter({
					domWin, modalType: Ci.nsIPrompt.MODAL_TYPE_WINDOW
				});
				var confirm = win => pp(win).confirm(title, msgRest);
				var alert = win => pp(win).alert(title, msgAbort);
			} else {
				var confirm = win => win.confirm(msgRest);
				var alert = win => win.alert(msgAbort);
			}
			return (this.mayBeRestart = (node, conf) => {
				var win = node.ownerGlobal;
				if (conf && !confirm(win)) return;
				if (win.BrowserUtils.restartApplication() === false) alert(win);
				else return true;
			})(node, conf);
		},
		regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
		maybeRe(node, fe) {
			var {pref} = node;
			if ("restart" in pref) {
				if (this.maybeRestart(node, pref.restart)) return;
			}
			else this.popupshowing(fe, node.parentNode);
			if ("refresh" in pref) {
				var win = node.ownerGlobal;
				if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
					? win.BrowserReloadSkipCache() : win.BrowserReload();
			}
		},
		maybeClosePopup(e, trg) {
			!e.ctrlKey && prefs.getBoolPref(this.closePref, undefined)
				&& trg.parentNode.hidePopup();
		},
		command(e) {
			var trg = e.target; 			// if (trg.btn) return this.openPopup(trg.primaryPopup);
			if (trg.btn) {
				var doc = e.target.ownerDocument; // Переключить боковую панель
				var win = doc.defaultView;
				var bar = doc.querySelector("#add-additional-vertical-bar");
				if (bar)
					win.setToolbarVisibility(bar, bar.collapsed);
				bar.collapsed ? win.SidebarUI.hide('viewHistorySidebar') : win.SidebarUI.show('viewHistorySidebar');
				return;
			}
			var menu = trg.closest("menu"), newVal = trg.val;
			this.maybeClosePopup(e, menu);
			if (newVal != menu.pref.val)
				menu.pref.set(menu.pref.pref, newVal),
				this.maybeRe(menu, true);
		},
		popupshowing(e, trg = e.target) {
			if (trg.state == "closed") return;
			if (trg.id) {
				for(var node of trg.children) {
					if (node.nodeName.endsWith("r")) continue;
					this.upd(node);
					!e && node.open && this.popupshowing(null, node.querySelector("menupopup"));
				}
				return;
			}
			var {pref} = trg.closest("menu"), findChecked = true;

			var findDef = "defVal" in pref;
			var checked = trg.querySelector("[checked]");
			if (checked) {
				if (checked.val == pref.val) {
					if (findDef) findChecked = false;
					else return;
				}
				else checked.removeAttribute("checked");
			}
			if (findDef) {
				var def = trg.querySelector("menuitem:not([style*=font-style]");
				if (def)
					if (def.val == pref.defVal) {
						if (findChecked) findDef = false;
						else return;
					}
					else def.style.setProperty("font-style", "italic", "important");
			}
			for(var node of trg.children) if ("val" in node) {
				if (findChecked && node.val == pref.val) {
					node.setAttribute("checked", true);
					if (findDef) findChecked = false;
					else break;
				}
				if (findDef && node.val == pref.defVal) {
					node.style.removeProperty("font-style");
					if (findChecked) findDef = false;
					else break;
				}
			}
		},
		contextmenu(e) {
			var trg = e.target;
			if (trg.btn) {
				if (e.ctrlKey || e.shiftKey) return;
				if (e.detail == 2) return trg.secondaryPopup.hidePopup();
				this.openPopup(trg.secondaryPopup);
			}
			else if ("pref" in trg) {
				this.maybeClosePopup(e, trg);
				if (trg.pref.user)
					prefs.clearUserPref(trg.pref.pref),
					this.maybeRe(trg);
			}
			e.preventDefault();
		},
		click(e) {
			if (e.button) return;
			var trg = e.target, {pref} = trg;
			if (!pref) return;
		},
		auxclick(e) {
			if (e.button != 1 || !e.target.btn) return;
			e.view.alert("MMB");
			// PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar');
		},
		mousedown(e) {
			var reset = e => e.target.linkedObject = this;
			var id, lo = {command: reset, mousedown: reset};

			var lin = /macos|linux/.test(e.view.AppConstants.platform);
			var stop = e => reset(e) && e.preventDefault();

			lo.contextmenu = lin
				? e => e.ctrlKey || e.shiftKey ? dsp(e) : stop(e) : stop;
			var context = lin
				? e => e.button == 2 && e.type.endsWith("p") && this.contextmenu(e) : () => {};

			var dsp = (e, timeout) => {
				var trg = e.target;
				trg.onmouseup = trg.onmouseleave = null;
				if (timeout) return this.londPress(e);
				e.view.clearTimeout(id);
				reset(e);
				context(e);
			}
			(this.mousedown = e => {
				var trg = e.target;
				if (!trg.btn) return;
				trg.linkedObject = lo;
				trg.onmouseup = trg.onmouseleave = dsp;
				id = e.view.setTimeout(dsp, 500, e, true);
			})(e);
		},
		londPress(e) { // удержание кнопки мыши, выбирать команды, отводящие мышь от кнопки
			var msg = "QuickSettings\nLONG PRESS: e.button = " + e.button;
			// Components.utils.reportError(msg);
			// StatusPanel._label = 'text'; setTimeout(()=> StatusPanel._label = '',3000);
			// e.view.alert(msg);
			Antizapret(e.target);
		}
	};
}); // END код кнопки

Отсутствует

 

№1533104-04-2021 12:47:28

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 68.0

Re: Custom Buttons

Dumby
Класс, спасибо огромное

Отсутствует

 

№1533204-04-2021 13:07:05

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2247
UA: Firefox 68.0

Re: Custom Buttons

Dobrov пишет

параметр не меняется, почему-то тип general.useragent.override всегда логический, а не строковый…

Не вижу такого.


Но могло раньше (как-то) прописаться как логическое,
тогда следует сбросить (удалить) настройку и перезапуститься.
После этого должно быть нормально.

Отсутствует

 

№1533304-04-2021 15:49:46

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 336
UA: Firefox 87.0

Re: Custom Buttons

Dumby
Посмотрите как спец https://forum.mozilla-russia.org/viewto … 32#p789732
А то там тишина...

Отсутствует

 

№1533404-04-2021 17:25:12

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 472
UA: Firefox 84.0

Re: Custom Buttons

Dumby - Да, после удаления-перезапуска ЮзерАгент меняется.
А как сделать, чтобы строка меню стала зелёной, если "преф не существует"?

Выделить код

Код:

pref: ["general.useragent.override", "User Agent"], userChoice: true, refresh: true,
	values: [
	[useragent, "По-умолчанию", "", "", `Services.prefs.setStringPref('general.useragent.override', '')`],
	["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56"],………

Второй вопрос: как выполнить команду меню: "Показать все закладки"
PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar') // это не работает…

Отсутствует

 

№1533504-04-2021 21:39:34

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2247
UA: Firefox 68.0

Re: Custom Buttons

ВВП пишет

А то там тишина...

Тоже особо сказать нечего.
Для многострочности вкладок мне известен только этот проект.
Ещё Tab Mix Plus, но там (пока?) не готово.


Dobrov пишет

А как сделать, чтобы строка меню стала зелёной, если "преф не существует"?

Можно в upd() дописать, если цвет внешним стилем не приколочен
(без if (...) если не только для этой настройки, а для всех)

скрытый текст

Выделить код

Код:

...
			if (pref.pref == "general.useragent.override") exists
				? node.style.removeProperty("color")
				: node.style.setProperty("color", "green", "important");

Dobrov пишет

Второй вопрос: как выполнить команду меню: "Показать все закладки"
PlacesCommandHook.showPlacesOrganizer('BookmarksToolbar') // это не работает…

Работает. И в 84.0.2, и в 89.0a1
А команда пункта меню "Показать все закладки" запускает
PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');

Отсутствует

 

№1533605-04-2021 01:56:49

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 472
UA: Firefox 84.0

Re: Custom Buttons

Dumby - спасибо, сделал имя меню в скобках, если преф сброшен.
Есть баг: если изменить цвет текста меню, то он не меняется при наведении мыши и плохо различим.
Как вернуть "белый" цвет для строк, подсвеченых курсором?

Выделить код

Код:

if ("userChoice" in pref)
	if (val == pref.userChoice)
		node.style.removeProperty("color"),
		img = this.UserChoiceImg;
	else {
		node.style.setProperty("color", "maroon", "important");
		if (!alt) img = this.notUserChoiceImg;……

FF-QT.jpg


2) У меня команда открытия "Библиотеки" не работает, ничего не происходит…
log: Uncaught ReferenceError: PlacesCommandHook is not defined Windows x86, Firefox 84.0.2

Выделить код

Код:

auxclick(e) {
	if (e.button != 1 || !e.target.btn) return;
	e.view.alert("MMB"); // эта строка работает нормально
	PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks'); // эта строка ничего не открывает

3) И всё таки, как выбрать "галочкой" пункт под-меню «По-умолчанию», если преф не существует? (хотя-бы в под-меню выбора ЮзерАгента)

Выделить код

Код:

pref: ["general.useragent.override", "User Agent"],
			userChoice: true, refresh: true,
			values: [
				[useragent, "По-умолчанию", "", "", `Services.prefs.setStringPref('general.useragent.override', '')`],……

Весь код кнопки:

Выделить код

Код:

// Quick Toggle https://forum.mozilla-russia.org/viewtopic.php?pid=784139#p784139
// https://forum.mozilla-russia.org/viewtopic.php?pid=784165#p784165
// Быстрое переключение параметров about:config
// Ctrl+Click или правый клик - сброс параметра по-умолчанию
// стиль иконки: #QuickToggleAboutConfigSettings .toolbarbutton-icon{ padding: 2px !important;}

(async (name, id, func) => {
	if (name == "Object") return CustomizableUI.createWidget(func());
	var win = name == "Window", g = Components.utils.import("resource://gre/modules/Services.jsm", {});
	if (g[id]) {if (win) return;} else g[id] = func();
	if (win) return CustomizableUI.createWidget(g[id]);
	addDestructor(r => r[5] == "e" && delete g[id]);
	g[id].onCreated(this);
})(this.constructor.name, "QuickToggleAboutConfigSettings", () => { // BEGIN (this.constructor… {код кнопки});

	var {prefs} = Services, db = prefs.getDefaultBranch("");
	var pv = parseInt(Services.appinfo.platformVersion);
	var xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
	var icon_vpn = "hue-rotate(270deg) brightness(95%)";
	function Notify(title, text, time){ Components.classes['@mozilla.org/alerts-service;1'].getService(Components.interfaces.nsIAlertsService).showAlertNotification(null, title, text, false, '', null, time);
	}
	function Antizapret(trg){
		if (Services.prefs.getIntPref('network.proxy.type') == 2) { // выключить
			Services.prefs.setIntPref('network.proxy.type', 0);
			Services.prefs.setStringPref("network.proxy.autoconfig_url", "127.0.0.1");
			trg.style.filter = '';
			Notify('Proxy', 'Режим по-умолчанию отключен', 2000);
		} else {
			Services.prefs.setIntPref('network.proxy.type', 2);
			Services.prefs.setStringPref("network.proxy.autoconfig_url", "https://antizapret.prostovpn.org/proxy.pac");
			trg.style.filter = icon_vpn;
			Notify('Proxy', 'Работаем через VPN Антизапрет', 2000);
		}
	}
	var useragent = Components.classes["@mozilla.org/network/protocol;1?name=http"].getService(Components.interfaces.nsIHttpProtocolHandler).userAgent; // текущий юзерагент

//=====================================================================================

	// refresh:
	//	false - reload current tab
	//	true - reload current tab skip cache
	//
	// restart:
	//	false - restart browser
	//	true - restart browser with confirm

//=====================================================================================

	var secondary = [{

			pref: ["network.proxy.autoconfig_url", "Прокси (VPN) URL"],
			userChoice: 0, userAlt: 1, refresh: true,
			values: [
				["127.0.0.1", "отключен…", "0", "", `Services.prefs.setIntPref('network.proxy.type', 0); node.parentNode.parentNode.style.filter = '';`],
				["https://antizapret.prostovpn.org/proxy.pac", "АнтиЗапрет", "1", "Надёжный доступ на заблокированные сайты\n\n«Режим прокси» меняется на 2", `Services.prefs.setIntPref('network.proxy.type', 2); node.parentNode.parentNode.style.filter = icon_vpn;`],
				[prefs.getStringPref("user.pacfile", "file:///etc/proxy.pac"), "user .pac файл", "2"],
				["https://git.io/ac-anticensority-pac", "ac-anticensority", "3"],
				["localhost", "Tor Browser", "4"]
	]},{
			pref: ["network.proxy.type", "Режим прокси"],
			userChoice: 0, userAlt: 2, refresh: true,
			values: [
				[0, "Без прокси", "0", "по-умолчанию"],
				[5, "Системные (из IE)", "5"],
				[2, "Автонастройка", "2", "about:config — user.pacfile"],
				[1, "Ручная настройка", "1", "вторая строка"],
				[4, "Автоопределение", "4"]
	]},{
			pref: ["network.proxy.share_proxy_settings", "Прокси для всех протоколов"],
			userChoice: true, refresh: true,
			values: [[true, "Да", "", "Прокси для всех протоколов при ручной настройке"], [false, "Нет"]]
	},{
			pref: ["network.trr.mode", "DNS через HTTPS"],
			userChoice: 2, userAlt: 0, refresh: true,
			values: [
				[0, "Выключен", "0"], [2, "TRR + мой", "2"], [3, "только TRR", "3"]
	]},null,{
			pref: ["permissions.default.image", "Загрузка изображений"],
			userChoice: 1, userAlt: 3, refresh: true,
			values: [[1, "Разрешена"], [3, "Только с сайта"], [2, "Отключить"]]
	},{
			pref: ["image.animation_mode", "Анимация изображений"],
			userChoice: "none", refresh: true,
			values: [["none", "Выключена"], ["normal", "По циклу"], ["once", "Единожды"]]
	},{
			pref: ["browser.display.document_color_use", "Использовать цвета сайтов"],
			userChoice: 0,
			values: [[0, "Авто", "0"], [1, "Всегда", "1"], [2, "Никогда", "2"]]
	},{
			pref: ["browser.display.use_document_fonts", "Загружать web-шрифты"],
			userChoice: 1, refresh: true,
			values: [[1, "Да"], [0, "Нет"]]
	},null,{
			pref: ["media.autoplay.default", "Авто-play аудио/видео"],
			userChoice: 5, refresh: true,
			values: [
				[0, "Разрешить", "0"],
				[1, "Запретить", "1"],
				[2, "Спрашивать", "2"],
				[5, "Блокировать", "5"]
	]},{
			pref: ["media.autoplay.blocking_policy", "Автозапуск (политика)"],
			userChoice: 1, userAlt: 2, refresh: true,
			values: [
				[1, "Временная", "1"],
				[2, "По действию", "2"],
				[0, "Постоянная", "0"]
	]},{
			pref: ["plugin.state.flash", "Flash-plugin"],
			userChoice: 2, refresh: true,
			values: [
				[2, "Всегда включать", "2"],
				[1, "Включать по запросу", "1"],
				[0, "Никогда не включать", "0"]
	]},{
			pref: ["gfx.webrender.all", "WebRender для всего"],
			userChoice: false, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},null,{
			pref: ["javascript.enabled", "Выполнять скрипты Java"],
			userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["network.cookie.cookieBehavior", "Cookies"],
			userChoice: 1, userAlt: 3, refresh: false,
			values: [
				[1, "Не принимать сторонние"], [3, "Не принимать с не посещенных"], [4, "Не принимать от трекеров"],
				[2, "Не принимать со всех"], [0, "Принимать со всех"]
	]},{
			pref: ["dom.enable_performance", "Статус загрузки страницы"],
			userChoice: false
	},{
			pref: ["dom.storage.enabled", "Локальное хранилище"],
			userChoice: true
	},{
			pref: ["network.http.sendRefererHeader", "Referer - для чего"],
			userChoice: 1,
			values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки и изобр.", "2"]]
	},{
			pref: ["media.peerconnection.enabled", "WebRTC утечка IP"],
			userChoice: false
	},{
			pref: ["general.useragent.override", "User Agent"],
			userChoice: true, refresh: true,
			values: [
				[useragent, "По-умолчанию", "", "", `Services.prefs.setStringPref('general.useragent.override', '')`],
				["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56"],
				["Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56 Linux"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:68.0) Gecko/20100101 Firefox/68.0", "Firefox 68 MacOSX"],
				["Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", "MSIE 6.0 Windows"],
				["Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", "Chrome61 Android7"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30", "Safari 6 MacOSX"],
				["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
				["Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36", "Chrome57 W7"],
				["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", "Chrome61 W10"],
				["Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", "Samsung Galaxy S6"],
				["Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)", "Playstation 4"],
				["Xbox (Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Xbox One (mobile)"],
				["Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Microsoft Lumia 950"],
				["Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350)", "Windows Phone"],
				["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"]
	]}
	];

	return {
		label: "Quick Toggle Settings",
		id: "QuickToggleAboutConfigSettings",
		tooltiptext:
`Quick Toggle Settings

ЛКМ	Боковая панель: Журнал
ПКМ	Меню основных настроек
Long	Антизапрет proxy`,
		localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAdeSURBVHjaACcA2P8AbW1r2J6UnfEBAQEAAH9/feRepVj/4Zcn/wCCgX/k7+/v//+7KP8AAAD//wAnANj/AIuKifGIiIbxAAAAAACyv7f//////xUVFG8A7Ozs/+vIe/+vezrUAAAA//8ASwC0/wCQiI7kUp9P/2G5U//2/fb/s7S0/w8QEBIAgYB/5PHx8f/jzKn/6caJ///oev9NKiKFAIqIhuTu7e3/7+/v//WzPf/Dom7/ZzEJwAAAAP//JMk/CwFxAMfhL0mSRRbvglImsxfgFVjMFn9uMIkwyiqyobwBVpnuFoOyooT86uTOcOlj8KyPBv2euxjN3U637TYb9ZukkCSptbawLiWqqwqO4zCdTJAkAZTPeQD29gbgH0EQ8PY8vo85fIY4/RqZVFjyfR9jDNf7C7YF2OXIpqMJ+Z6HMU9OR5vlbAyHIvFYJPljO/5dEgjjAA5/Cxtu8aIcCm5yE6JZlMbm/oegpYZqiBbr9AjCOUIQG6q1tqAabGrVhhYhXOqt9N77lcLFGfZpcuvZHrHtAwXQem6TvhNW6wUmtKe5vDjHNM20TDSqDbXymKPqblDv7eO8rrPWytMddBn9jOi5GoBMZn5HRETK9qECCMOQWueUZvcBgGQMwUsNngp89kMsy9oWERGnYqs4jvF9nyAIcPUXnWYJkhJEJ/z2tyDZhMEeY3WMOJWyGg6HeJ6H1pooinh7/+D7qgg3Obhfhts82cU5J7tgpOQ/s4bMpKanjMnPdovXnaMlRET+yKKfkCbjOI7j37ooo0PQn0NUCFIUo2J2iKhD1govQZcg+uMKo6QiCG9BROlYNbTA/iAWiSnqwQwJZJsu8WKCWUSWmlmxoK1nz+Oe32/PmsHju4PkCA+fw+fw+lw+EgzWEqyrtVsan9odjZ32nfAtOxQK2vX1YXw+3+kla79zOQC8TaWU9Aufp2YAyGazOI5D+f59j/8DM6kvnO84Q/nL7TwwaqhqryQYvw5AV1cnA/E4gUCgZxFEpqNcThyi1bzJ3cQlqqcr8L9aOO/b9wQAkb4+FkE+lwfgaPdBjn/cy8jYKG7exZzN4r4+yfhQL7FYtAAcxwEXeid6aJp4SFb/ASAxOQ7uNcaeHOHN23cFoLUmk8mglCL5y2Lu6zPsT/ew3zcAj0jdLqP7fjMbRIov+teJaK0wDAPLslBKMzoYwTWvMK8bmDdugK4GfQHyIX60HZ4UpQrAsiycXJ7hgRfw4RTEvRDdymysghO+VWfrKktFlFKkUinS6TSmaZLROaYiYXi+GSLbYGgPsatliEjxzk0rRJLJn1imuRDLxLAUc4PnGGmtoqW5DYYPoNt3IyL+krVFRUueXyYiKz3LV//rW9Z7dtB/jF1rpMa70SN/+Sj/mKjrMI5/AQPa6MeSFj9a2SjMRckdd5QKlBA3bC63yg006pqxNpv2Q1tuAUdYY1iN0Y9lM1nM3TAdSrLiEA9CJAFjpQdywV0aHeD9kO9x3Pd+H6/+AG5erJ7t89dnn+f1ed7P834EjabKsjweAMFAkN7pXo6av6X52nf0T14g6Avy7zAajdRoqsmWZWsSEhJib+FFh0ZTZfF5vZGH3X39FGufomwyi303VFS07mRw+DIAYV8YAK/Xw+joCHa7nXAoTItWy5aSkq6kpKT0pbSxEUDToaOWc+ZzZHWnUWR4ALVpAwct5RyeOUCz9UM+nalg9+TTFBseJL/nca65ri8Nv4TodOK4OYvVZgNAr9eTmppaEVVBY32DZWJ6nErd++w/sYdXju1EfX4bH0y9wL4/t1Gme55d2l3sbXmT2k4NUkhalGjsd8aOqTEcKccyuQjt1HWQnLz6rRUS3dqDC8P9bGp+gvX9KazrTGNL07OMmyai9LdOzfDXyHFwvsOcLouR1moA+vr6SEm57+0oQE1NtUWSFn8lSRKBQCAqmdsygfniSa6eP45xQMdwTxej+haC9kbgEzC9hOnL9VjGBhg0GHkoI2O3IAhCfOIqobTo/sUl6XK58Hg8OBwORFFEFEWcc3OIznmuDukxDzQQ9jYCdeCrBV89eBrB9xW4G/Fd/hhrayVXvnmP3sqXp069puo6sjX75Beb0w9EAex2Ow6HI2I7URSZn3djv+niysDP/D1Yx4KtHmY/A8t+FkxlYChkYUgGv6yDXzPhDyW+IVXwh4ObzpQWpBVHAJLkwWaz4XA4IjZdhrjm3dywO/mt5wSzHTvgJzm0ZUL7o/DjY9CtZEGfR0ftczz58JqqxFWCcO/d8YJalb7UA7e0NN9e/H4/fr+fQDBIMBQiHA4TAqzTU1g73oVTa0EnY/r0dsa/fxVJXwiXcuGSCuOhbHKS47SCIMgS42OTczOTbhMK8vMalMqcswqFvE2pyFlxcuTyNoVCcfr17QVn6/bmD71YIu/Jzd14ZnNhUXvBM4VtSuXG9s/35Ju5uBV6i2nakXL9rjihVLn2TuHrNzL+w97/EzExK3fRHbfHCLI1q+9pUG/46HD5Iy3K9Li85ft/BgCgAJvUrkqRFQAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

			btn.btn = true;
			btn.domParent = null;
			btn.popups = new btn.ownerGlobal.Array();
			this.createPopup(doc, btn, "secondary", secondary);
			this.createCloseMenusOption(doc, btn);

			if (Services.prefs.getIntPref('network.proxy.type') == 2)
				btn.style.filter = icon_vpn; // btn.style.cssText = "background-image: -moz-linear-gradient(#c0c8c0, #c0c8c0, #c0c8c0) !important";

			btn.linkedObject = this;
			for(var type of ["command", "contextmenu", "mousedown", "auxclick"])
				btn.setAttribute("on" + type, `linkedObject.${type}(event)`);

		},
		createPopup(doc, btn, name, data) {
			var popup = doc.createElementNS(xul_ns, "menupopup");
			var prop = name + "Popup";
			btn.popups.push(btn[prop] = popup);
			popup.id = this.id + "-" + prop;
			for (var type of ["popupshowing", "click"])
				popup.setAttribute("on" + type, `parentNode.linkedObject.${type}(event)`);
			for(var obj of data) popup.append(this.createElement(doc, obj));
			btn.append(popup);
		},
		map: {b: "Bool", n: "Int", s: "String"},
		createElement(doc, obj) {
			if (!obj) return doc.createElementNS(xul_ns, "menuseparator");
			var pref = doc.ownerGlobal.Object.create(null), node, img, bool;
			for(var [key, val] of Object.entries(obj)) {
				if (key == "pref") {
					var [apref, lab, akey, ttt] = val;
					pref.pref = apref; pref.lab = lab || apref;
					if (ttt) pref.ttt = ttt;
				}
				else if (key == "image") img = val, pref.img = true;
				else if (key != "values") pref[key] = val;
				else pref.hasVals = true;
			}
			var type = prefs.getPrefType(pref.pref);
			var str = this.map[type == prefs.PREF_INVALID
				? obj.values ? (typeof obj.values[0][0])[0] : "b"
				: type == prefs.PREF_BOOL ? "b" : type == prefs.PREF_INT ? "n" : "s"
			];
			pref.get = prefs[`get${str}Pref`];
			var map, set = prefs[`set${str}Pref`];
			if (pref.hasVals) {
				for(var [val, , , , code] of obj.values)
					code && (map || (map = new Map())).set(val, code);
				if (map) pref.set = (key, val) => {
					set(key, val);
					map.has(val) && eval(map.get(val)); // выполнить код
				}
			}
			if (!map) pref.set = set;

			node = doc.createElementNS(xul_ns, "menu");
			node.className = "menu-iconic";
			node.setAttribute("closemenu", "none");
			img && node.setAttribute("image", img);
			akey && node.setAttribute("accesskey", akey);
			(node.pref = pref).vals = doc.ownerGlobal.Object.create(null);
			this.createRadios(doc,
				str.startsWith("B") && !pref.hasVals ? [[true, "true"], [false, "false"]] : obj.values,
				node.appendChild(doc.createElementNS(xul_ns, "menupopup"))
			);
			if ("userChoice" in obj) pref.noAlt = !("userAlt" in obj);
			return node;
		},
		createCloseMenusOption(doc, btn) {
			var pn = this.closePref = "QuickToggleAboutConfigSettings.closeMenus";
			var data = [null, {
				pref: [pn, "Закрывать меню этой кнопки"], values: [[true, "Да"], [false, "Нет"]]
			}];
			var setCloseMenus = e => {
				e.stopPropagation();
				var trg = e.target, {pref, val} = trg, updPopup = true, clear;
				switch(e.type) {
					case "command": pref = (trg = trg.closest("menu")).pref; updPopup = false; break;
					case "click": if (e.button) return; break;
					case "contextmenu": e.preventDefault(); clear = pref;
				}
				if (!pref) return;
				if (clear) prefs.clearUserPref(pn);
				else if (!updPopup && val === pref.val) return;
				else pref.set(pn, val !== undefined ? val : !pref.val);
				this.upd(trg);
				updPopup && this.popupshowing(null, trg.querySelector("menupopup"));
			}
			(this.createCloseMenusOption = (doc, btn) => {
				for(var obj of data)
					btn.secondaryPopup.append(this.createElement(doc, obj));
				var m = btn.secondaryPopup.lastChild;
				m.style.cssText = "fill: lightblue !important; list-style-image: url(chrome://browser/skin/menu.svg) !important;";
				m.setAttribute("oncommand", "setCloseMenus(event)");
				m.onclick = m.oncontextmenu = m.setCloseMenus = setCloseMenus;
			})(doc, btn);
		},
		UserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACRUlEQVR42qWTS2gTQRjH/zPJZo2BDfgkB62vYg5CjQdvRUE8WB8HKyahYotIQQOeFKGCF7EgvQlBqOCjWFKlevBRDyL4uHloLXhYqWJroaEqlETSptndGb+ZPDH0ojN8O8vA98vv+/Itw38u1nSRYRE6UhKyg4FF1R292/Q+xhhLi4TIrgjgIzwe4MbgmR2nrZMtxxEN63zYORuPZp7gzpehvCu9XjfuPmwCUHIiEtw4fK89zVvD2+FHAD56quXBpV3CVO4ret6nxHzxZxdBRuqADCKmz7RHD9y2tlmbYbBVfwE8DXBkEd/y39H5+mze8ZyoSIpsFXC9u/VE3/ldpyg5AAMEoJMTQi2hANKBgyKdy0h/eoChqcf9MimvVAHjd/fdiG0NRwgQpDAp1agBpDZwdHKJLKZzc+h5e3kCSexh5c5j8fmhW0HGpNZXFkqfsQpAKgcFKGmAlAxHXp5bkkmsrhosjh4cCIJ5pG5qfQXgtMslqO1qgEMWTPrR+eriEhoA4wN7L8TWh0KVXzfgY2RQAUjVBenWLH4UCrj04Wa9BNXEo5va+w63xCiJ6WRVP2MVgNQIgjgEA17MTODZ7Lt+AtSaGDG4377a1m2FAkKrc11/dUwIID1dSKHEcW3yft4RbpR6kK1PYgaJtaY1nNp5jIeMsjKNbs1AWRUcjvTnp2Kh9LtLJGTDINUhcTIZ3L+hzdq9ZgvWmSF9/Wu5gI8L03gzP5l3hddL/3/zKDdA9MdE0UERrdzaFGMUaap75Y/pX9Yfap76EQYaCeEAAAAASUVORK5CYII=",
		notUserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACNUlEQVR42qXTT2jTUBwH8G9e0ta2ks5ND0Ww/qX1oGyevEwQnOBA6EHXwLwoUh3eRGRs501RPE6xgp6KrexQECZY8DBPXmbRS4Z/K0gRtmmDa2yb5PlLX7p2Gwpi4EfCy8sn3/dPwn9e0saGnCRF6XYFnA9DkhKtRs51ep6jmtEcp/JHIM9Yivl9mf0XNDV2Ngn1YLzVxdB1lJ8U8P5hzuCWnR6xrPwmIM8kLRjdkT326BaLHNgLKAFA9ouXdhOw6qi++4j589cd89vSaMqyc2sAPUXlgF8fyt9WI3t2Av4w4NsCMJ8AHAKav6hqqH76iuLINcNuNhOawyttYCp+7tRE/6XT9GFQAG4CpniATQkIaNQIMVG6/xSL2WfTGueTbWDh5L2rA9t29QGBrQQEPUDuAuoEmFSr+P5lGc8v33mtAUfaQO3M4/GgDOoYCIkUin8D0BDDoBQ2ZMxqN0wCQh0gMxaUnYb4uzt+dwIZ8wBHTGQbYAHMpu+uAxaGxpMDvSr3AHcFaAIlD+BtoN6ag5WqhOLNwrohTMWPH5roP7pdLIwb3wW6EziWGAZhpVdLWHzxdpqAyc4yKrJ+4uKg2oMV+lgRK9CdwAVsG1WpF8UH84Zt2QkCKmsbiRAtFAllB5OHWQ9fpr81OwlcgBL9kPrwsvDGMQ1zNMV5ZyN1ISlZYZl9/TE1tjuMSFgA1VWO8uef+FAqG47tpFMcm7dyFyIOEzBMlfCadao5qhk39l9P479evwFbiOcRSXKueAAAAABJRU5ErkJggg==",
		UserAltImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIfSURBVDhPpVM9b9NQFD02cSKKFVGKIFFREFsmFiCA1AEQQrQjC0gssLCkC0jJj0iFWFoGdiT4Ay0SEmJBhPI5FBokikL4SCC0Te2UxInt13Mdu1CVrVc6713fe86x/T6w09DCeTNKJ7Q0pzyUmoCmZYOiUhXms8RMcd6vB7UwthhM5fTLWix2L3PmYjKdOwdz9DDFGtr1KuovnqD29JGlfO9Goew9DCV/DaZy2pXE8PD9Y/mbupk6xE6cVWPQhEujHo2+4fXdO76ztnq1UPYfSCcwKB1HWjeMyunCraSZOsDqbkBPsBOTNoMGvkOTLto/f+F56bble262OK/qesiYzIydTJojFLjrgEe4bcIOEdXWYe6LITN2KgmFSRFGBuPpo0eAngX0SexTJHD5LOgLpEZTcgKuUuMijAyy5l7+r0NSb21g5LQI5gGYSy2AjYBLjQyRAUniTnJXsBoayBzm3RBBndwwIoOK3fgNdIREQYCV/0DqLdiNZqCRITKYayx85RfQuUNiZ3k7IgO+vfGeXGpkiAyma++WLHtZhZ8o5H/FEVpos1V7u8TFwLQId8nw+Afa51Oq2vyycmlk9KCW0GXFZeu63IUO8z88Bx5s28SbuUXfddzrxVcobxpI0GTh7H734/dPzQt9d0/CGBpCPB7nbhmwbAPVxR4+PPtsuU7/WuElth/lKORUcsoTE8TgMg0WbJaY4Zu3XKYdBrABARUk+ls4DfQAAAAASUVORK5CYII=",
		upd(node) {
			var {pref} = node, def = false, user = false, val;
			if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
				var pn = pref.pref;
				try {val = pref.defVal = db[pref.get.name](pn); def = true}
				catch(ex) {def = false;}
				var user = prefs.prefHasUserValue(pn);
				if (user) try {val = pref.get(pn, undefined);} catch(ex) {}
			}
			if (val == pref.val && def == pref.def && user == pref.user) return;
			pref.val = val; pref.def = def; pref.user = user;
			var exists = def || user;

			var ttt = exists ? val : "Этого префа не существует";
			if (ttt === "") ttt = "[ empty_string ]";
			ttt += "\n" + pref.pref;
			if (pref.ttt) ttt += "\n" + pref.ttt;
			node.tooltipText = ttt;

			var img, alt = "userAlt" in pref && val == pref.userAlt;
			if (alt) img = this.UserAltImg;
			if ("userChoice" in pref)
				if (val == pref.userChoice)
					node.style.removeProperty("color"),
					img = this.UserChoiceImg;
				else {
					node.style.setProperty("color", "maroon", "important");
					if (!alt) img = this.notUserChoiceImg;
				}

			if (!pref.img) img
				? node.setAttribute("image", img)
				: node.removeAttribute("image");
			user
				? node.style.setProperty("font-style", "italic", "important")
				: node.style.removeProperty("font-style");

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "Другое" : "По умолчанию";
				lab += ` — "${sfx}"`;
			}

			lab = exists ? lab : '['+ lab +']'; // имя = [имя] если преф не существует
			node.setAttribute("label", lab);
		},
		createRadios(doc, vals, popup) {
			for(var arr of vals) {
				if (!arr) {
					popup.append(doc.createElementNS(xul_ns, "menuseparator"));
					continue;
				}
				var [val, lab, key, ttt] = arr;
				var menuitem = doc.createElementNS(xul_ns, "menuitem");
				menuitem.setAttribute("type", "radio");
				menuitem.setAttribute("closemenu", "none");
				menuitem.style.setProperty("font-style", "italic", "important"),
				menuitem.setAttribute("label", popup.parentNode.pref.vals[val] = lab);
				key && menuitem.setAttribute("accesskey", key);
				var tip = menuitem.val = val;
				if (ttt) tip += "\n" + ttt;
				menuitem.tooltipText = tip;
				popup.append(menuitem);
			}
		},
		openPopup(popup) {
			var btn = popup.parentNode;
			if (btn.domParent != btn.parentNode) {
				btn.domParent = btn.parentNode;
				var pos;
				if (btn.matches(".widget-overflow-list > :scope"))
					pos = "after_start";
				else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
					btn.closest("toolbar").getBoundingClientRect(), pos = width > height
						? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
						: `${win.innerWidth - right > left ? "end" : "start"}_before`;
				for(var p of btn.popups) p.setAttribute("position", pos);
			}
			popup.openPopup(btn);
		},
		maybeRestart(node, conf) {
			var msgRest = "Перезапустить браузер?", msgAbort = "Запрос на выход отменен.";
			if (pv >= 77) {
				var title = node.closest("toolbarbutton").label;
				var pp = domWin => Services.prompt.wrappedJSObject.pickPrompter({
					domWin, modalType: Ci.nsIPrompt.MODAL_TYPE_WINDOW
				});
				var confirm = win => pp(win).confirm(title, msgRest);
				var alert = win => pp(win).alert(title, msgAbort);
			} else {
				var confirm = win => win.confirm(msgRest);
				var alert = win => win.alert(msgAbort);
			}
			return (this.mayBeRestart = (node, conf) => {
				var win = node.ownerGlobal;
				if (conf && !confirm(win)) return;
				if (win.BrowserUtils.restartApplication() === false) alert(win);
				else return true;
			})(node, conf);
		},
		regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
		maybeRe(node, fe) {
			var {pref} = node;
			if ("restart" in pref) {
				if (this.maybeRestart(node, pref.restart)) return;
			}
			else this.popupshowing(fe, node.parentNode);
			if ("refresh" in pref) {
				var win = node.ownerGlobal;
				if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
					? win.BrowserReloadSkipCache() : win.BrowserReload();
			}
		},
		maybeClosePopup(e, trg) {
			!e.ctrlKey && prefs.getBoolPref(this.closePref, undefined)
				&& trg.parentNode.hidePopup();
		},
		command(e) {
			var trg = e.target; 			// if (trg.btn) return this.openPopup(trg.primaryPopup);
			if (trg.btn) {
				var doc = e.target.ownerDocument; // Переключить боковую панель
				var win = doc.defaultView;
				var bar = doc.querySelector("#add-additional-vertical-bar");
				if (bar)
					win.setToolbarVisibility(bar, bar.collapsed);
				bar.collapsed ? win.SidebarUI.hide('viewHistorySidebar') : win.SidebarUI.show('viewHistorySidebar');
				return;
			}
			var menu = trg.closest("menu"), newVal = trg.val;
			this.maybeClosePopup(e, menu);
			if (newVal != menu.pref.val)
				menu.pref.set(menu.pref.pref, newVal),
				this.maybeRe(menu, true);
		},
		popupshowing(e, trg = e.target) {
			if (trg.state == "closed") return;
			if (trg.id) {
				for(var node of trg.children) {
					if (node.nodeName.endsWith("r")) continue;
					this.upd(node);
					!e && node.open && this.popupshowing(null, node.querySelector("menupopup"));
				}
				return;
			}
			var {pref} = trg.closest("menu"), findChecked = true;

			var findDef = "defVal" in pref;
			var checked = trg.querySelector("[checked]");
			if (checked) {
				if (checked.val == pref.val) {
					if (findDef) findChecked = false;
					else return;
				}
				else checked.removeAttribute("checked");
			}
			if (findDef) {
				var def = trg.querySelector("menuitem:not([style*=font-style]");
				if (def)
					if (def.val == pref.defVal) {
						if (findChecked) findDef = false;
						else return;
					}
					else def.style.setProperty("font-style", "italic", "important");
			}
			for(var node of trg.children) if ("val" in node) {
				if (findChecked && node.val == pref.val) {
					node.setAttribute("checked", true);
					if (findDef) findChecked = false;
					else break;
				}
				if (findDef && node.val == pref.defVal) {
					node.style.removeProperty("font-style");
					if (findChecked) findDef = false;
					else break;
				}
			}
		},
		contextmenu(e) {
			var trg = e.target;
			if (trg.btn) {
				if (e.ctrlKey || e.shiftKey) return;
				if (e.detail == 2) return trg.secondaryPopup.hidePopup();
				this.openPopup(trg.secondaryPopup);
			}
			else if ("pref" in trg) {
				this.maybeClosePopup(e, trg);
				if (trg.pref.user)
					prefs.clearUserPref(trg.pref.pref),
					this.maybeRe(trg);
			}
			e.preventDefault();
		},
		click(e) {
			if (e.button) return;
			var trg = e.target, {pref} = trg;
			if (!pref) return;
		},
		auxclick(e) {
			if (e.button != 1 || !e.target.btn) return;
			PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');
		},
		mousedown(e) {
			var reset = e => e.target.linkedObject = this;
			var id, lo = {command: reset, mousedown: reset};

			var lin = /macos|linux/.test(e.view.AppConstants.platform);
			var stop = e => reset(e) && e.preventDefault();

			lo.contextmenu = lin
				? e => e.ctrlKey || e.shiftKey ? dsp(e) : stop(e) : stop;
			var context = lin
				? e => e.button == 2 && e.type.endsWith("p") && this.contextmenu(e) : () => {};

			var dsp = (e, timeout) => {
				var trg = e.target;
				trg.onmouseup = trg.onmouseleave = null;
				if (timeout) return this.londPress(e);
				e.view.clearTimeout(id);
				reset(e);
				context(e);
			}
			(this.mousedown = e => {
				var trg = e.target;
				if (!trg.btn) return;
				trg.linkedObject = lo;
				trg.onmouseup = trg.onmouseleave = dsp;
				id = e.view.setTimeout(dsp, 500, e, true);
			})(e);
		},
		londPress(e) { // удержание кнопки мыши, выбирать команды, отводящие мышь от кнопки
			var msg = "QuickSettings\nLONG PRESS: e.button = " + e.button;
			if (e.button == 0) Antizapret(e.target);
		}
	};
}); // END код кнопки

Отредактировано Dobrov (05-04-2021 15:11:00)

Отсутствует

 

№1533705-04-2021 11:48:41

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 336
UA: Firefox 87.0

Re: Custom Buttons

Dumby
На это никто и ничего

скрытый текст
Что делать, этот код бьет по https://hdrezka-ag.com/
@-moz-document url("about:newtab") {
    body:empty {
        background-color: #222 !important;
    }
}

@-moz-document url("about:blank") {
    body:empty {
        background-color: #222 !important;
    }
}


Пришлось кнопку делать с гор.клавишей... гемморно,а что делать?
скрытый текст

Выделить код

Код:

/*CODE*/
var s = "browser.display.background_color";
cbu.setPrefs(s, cbu.getPrefs(s) == "#fff" ? "#222" : "#fff"); 

function toggleImage() {
   var val = (cbu.getPrefs(s) == "#222");
   
   var {icon} = self;
   icon.src = val ? self.image : "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAy0lEQVR42t2TWw6DIBBFL1L/mrACH/Gx/wUpPj+bpivQ6QC2NaZppfGrN4GQyXC4wIwgoiuAM48TnAReWq/fSjDgFoahSpIE0zRBSmliJFjrRA7BhOZ5RhAEaNvW5ltAWZaqqiqzgfacapSmKbquc4Asy1TTNGvbX0FRFGEcRwcoikLVde3lwFy573sHyPNcaa29AHEcYxiGgxz8AcDzEW0OA+g4B5tC2qXlG+mXUrY5XMr0LGVuDmWIj0b5uHtpNLZPtsF4unh53+gONs64twYOr4EAAAAASUVORK5CYII="; 
   

   
};
toggleImage();
this.oncontextmenu =e=> { e.button && !e.ctrlKey && e.preventDefault() };
Services.prefs.addObserver(s, toggleImage, false);
addDestructor(()=> Services.prefs.removeObserver(s, toggleImage));

Отсутствует

 

№1533805-04-2021 14:38:23

toxa
Участник
 
Группа: Members
Зарегистрирован: 11-04-2012
Сообщений: 261
UA: Firefox 85.0

Re: Custom Buttons

В кнопке в инициализации есть строчка

Выделить код

Код:

gBrowser.addEventListener('mousedown', function(){....})

Если после инициализации я открою кнопку подредактировать, а потом закрою, то листнер дублируется. Помогает только перезапуск браузера.


Можно как то сделать, чтоб если листнер уже есть, новый не добавлялся?


Заодно хочу спросить. Вот эта строчка

Выделить код

Код:

const x = (await (await fetch(url)).text());

в консоли работает нормально. А в кнопке дает ошибку. Или если написать это в редакторе кода, скажем, АСЕ - тоже пишет ошибку. Что там ему не нравится?

Отредактировано toxa (05-04-2021 17:30:40)

Отсутствует

 

№1533905-04-2021 22:08:07

ВВП
Участник
 
Группа: Members
Зарегистрирован: 13-03-2021
Сообщений: 336
UA: Firefox 87.0

Re: Custom Buttons

Dumby
Ну, что делать, как background задать на новую и пустую без кода этого?
может в tabbrowser.js  типа:
const FAVICON_DEFAULTS = {
    "about:newtab": "chrome://global/skin/icons/Portrait.png",
    "about:home": "chrome://global/skin/icons/Portrait.png",
    "about:blank": "chrome://global/skin/icons/Portrait.png",
};   
Ну, и background тоже?

Отсутствует

 

№1534006-04-2021 09:07:40

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2247
UA: Firefox 68.0

Re: Custom Buttons

Dobrov пишет

Как вернуть "белый" цвет для строк, подсвеченых курсором?

Наверно можно стилем.
Дописать вызов регистрации стиля в onCreated(), и, далее, сам метод.
Как-то так

скрытый текст

Выделить код

Код:

...
		onCreated(btn) {
			...

			this.addSheet(btn);
		},
		addSheet(btn) {
			var cb = Array.isArray(btn._destructors);
			var id = cb ? btn.id : "QuickToggleAboutConfigSettings";
			var css = `#${id} menu[_moz-menuactive] {
				color: unset /*white*/ !important;
			}`;
			var args = [
				"data:text/css;charset=utf-8," + encodeURIComponent(css),
				Ci.nsIDOMWindowUtils.USER_SHEET
			];
			if (cb) var destructor = function() {
				this.removeSheetUsingURIString(...args);
			}
			var add = b => b.ownerGlobal.windowUtils.loadSheetUsingURIString(...args);
			(this.addSheet = !cb ? add : btn => {
				add(btn);
				btn._destructors.push({destructor, context: btn.ownerGlobal.windowUtils});
			})(btn);
		},

Dobrov пишет

PlacesCommandHook is not defined

e.view.PlacesCommandHook должен быть defined


Dobrov пишет

3) И всё таки, как выбрать "галочкой" пункт под-меню «По-умолчанию», если преф не существует? (хотя-бы в под-меню выбора ЮзерАгента)
[useragent, "По-умолчанию", "", "", `Services.prefs.setStringPref('general.useragent.override', '')`],

Это что угодно, но только не "По-умолчанию".
Если прям так уж надо поперёк концепции, то можно так попробовать

скрытый текст

Выделить код

Код:

...
			pref: ["general.useragent.override", "User Agent"],
			userChoice: true, refresh: true,
			values: [				
				(arr => {
					var pref = "general.useragent.override";
					var has = Services.prefs.prefHasUserValue(pref);
					if (has) {
						var val = Services.prefs.getStringPref(pref);
						Services.prefs.clearUserPref(pref);
					}
					var ua = Cc["@mozilla.org/network/protocol;1?name=http"]
						.getService(Ci.nsIHttpProtocolHandler).userAgent;
					has && Services.prefs.setStringPref(pref, val);

					var find = node => node.pref && node.pref.pref == pref;
					var redef = (doc, ttt) => {
						var popup = doc.getElementById("QuickToggleAboutConfigSettings-secondaryPopup");
						var menuitem = Array.from(popup.children).find(find).menupopup.firstChild;
						menuitem.tooltipText = ttt ? ua + "\n" + ttt : ua;
						menuitem.setAttribute("oncommand",
							`event.stopPropagation();
							this.closest("toolbarbutton").linkedObject.contextmenu({
								preventDefault: Boolean,
								target: this.parentNode.parentNode
							});`
						);
					}
					Object.defineProperty(arr, "0", {enumerable: true, get() {
						if (Components.stack.formattedStack.includes("createRadios")) {
							var win = Services.wm.getMostRecentWindow("navigator:browser");
							win.setTimeout(redef, 0, win.document, this[3]);
						}
						else return "";
					}});
					return arr;
				})([null, "По-умолчанию"]),

toxa пишет

Если после инициализации я открою кнопку подредактировать, а потом закрою, то листнер дублируется. Помогает только перезапуск браузера.
Можно как то сделать, чтоб если листнер уже есть, новый не добавлялся?

Ну флаг можно поставить куда-нибудь,
в окно или иное подобное надёжное место.
Если флаг есть, то не добавляем листенер, иначе добавляем.


Редактирование кода самого листенера без перезапуска браузера
тогда всё ещё идёт лесом, но, каков вопрос...


toxa пишет

А в кнопке дает ошибку.

Оператор await валиден только
в асинхронных функциях и асинхронных генераторах
(ну, ещё и это будет).

скрытый текст

Выделить код

Код:

var url = "data:,757";
(async () => {
	const x = (await (await fetch(url)).text());
	alert(x);
})();

ВВП пишет

Ну, что делать, как background задать на новую и пустую без кода этого?

Даже близко не представляю
как этот код может быть связан с hdrezka-ag.com

Отсутствует

 

№1534106-04-2021 12:01:50

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 68.0

Re: Custom Buttons

Dumby
Здравсвуйте, по поводу контейнеров
Вы бы не могли пожалуйста еще сделать кнопку в контекстном меню папки закладок "Открыть всё в контейнере"

Отсутствует

 

№1534206-04-2021 16:30:47

xrun1
Участник
 
Группа: Members
Зарегистрирован: 12-12-2013
Сообщений: 1224
UA: Firefox 87.0

Re: Custom Buttons

Давно хотел задать вопрос тем, кто заливает картинки на directupload.net . У меня по клику открывается https и не работает. Если в адресной строке потом заменить на http  и обновить, то всё открывается. Может, кто подскажет решение или какая у меня проблема?

Отсутствует

 

№1534306-04-2021 17:59:59

bezuma
Участник
 
Группа: Members
Откуда: Москва
Зарегистрирован: 26-01-2014
Сообщений: 256
UA: Chrome 86.0

Re: Custom Buttons

xrun1
Есть там аккаунт - все ОК по https, проблемы были месяца три назад, сейчас все ровно, проверял на ФФ 87 и Центе. А тогда так бесило, что хотел акк удалить - картинки не открывались

Отредактировано bezuma (06-04-2021 18:06:39)

Отсутствует

 

№1534406-04-2021 22:26:27

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 484
UA: Firefox 87.0

Re: Custom Buttons

Dumby приветствую, давно не был в теме, не слежу давно, что происходит, извини за это. Подскажи, что подправить, все связано с боковой панелью, не отображаются в боковой панели:
1.

скрытый текст

Выделить код

Код:

/*Initialization Code*/
gCBClipboardViewer = this; // global obj
this.label="Clipboard Viewer";



((g, name, id) => {
    var obj = g[name] || (g[name] = ({
        topic: "quit-application-granted",
        init() {
            var pv = parseInt(Services.appinfo.platformVersion);
         var url = `data:application/${pv >= 73 ? "xhtm" : "vnd.mozilla.xu"}l+xml,${encodeURIComponent(self.Help)}`; 
            if (pv >= 69 && Services.appinfo.browserTabsRemoteAutostart) {
            this.url = `chrome://custombuttons/content/cbdialog${Date.now()}.xul`;
            this.helper = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup).registerChrome(
                    Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile)), [["override", this.url, url]]
                );
                url = this.url;
               } 
            (this.obs = Services.obs).addObserver(this, this.topic, false);
            return this;
        },
        observe() {
            delete g[name];
            this.obs.removeObserver(this, this.topic);
            this.helper.destruct();
        }
    }).init());

 var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                   .getService(Components.interfaces.nsIWindowWatcher);
 var em = ww.getWindowEnumerator();
 var winName = "clipview";
 var index = 1;
 while (em.hasMoreElements()) {
  let win = em.getNext();
  if(win.name == winName) {
    win.focus();
    return;
  }
  index++
 }

    this.onclick = e => {
       if (e.button == 0  )  {
         var pv = parseInt(Services.appinfo.platformVersion);
         var url = `data:application/${pv >= 73 ? "xhtm" : "vnd.mozilla.xu"}l+xml,${encodeURIComponent(self.Help)}`;  
        if (pv >= 69 && Services.appinfo.browserTabsRemoteAutostart) {
        var chromeURL = `chrome://custombuttons/content/cbdialog${Date.now()}.xul`;
        Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup).registerChrome(
        Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile)), [["override", chromeURL, url]]
    );
    url = chromeURL;
}
        window.openDialog(url, winName, "chrome, centerscreen, minimizable, resizable");
        }
       if (e.button == 1 && e.ctrlKey)  return gBrowser.selectedTab = gBrowser.addTrustedTab(obj.url);
       if (e.button == 1) SidebarUI.toggle(id);
          }

    addDestructor(reason => reason[5] == "e" && name in g && g[name].observe());
    for(var tab of gBrowser.tabs) {
        if (!tab.linkedPanel || tab.closing) continue;
        var br = tab.linkedBrowser;
        !br.isRemoteBrowser && br.currentURI.spec == obj.url &&
        br.contentDocument.documentURI.startsWith("about:neterror?e=fileNotFound") && br.reload();
    }

    var label = "Clipboard Viewer";
    var url = "chrome://browser/content/webext-panels.xhtml?" + id;
    var icon = this.image;
    var defaultURL = obj.url; 

     var e = (name, attrs, node, append) => {
        var elm = document.createXULElement(name);
        for(var a in attrs) elm.setAttribute(a, attrs[a]);
        append ? node.append(elm) : node.before(elm);
        return elm;
    }
    var menuitem = e("menuitem", {
        label,
        type: "checkbox",
        id: "menu_CBClipboardLoader",
        oncommand: `SidebarUI.toggle("${id}");`,
    }, document.getElementById("viewSidebarMenu"), true);

    var btn = e("toolbarbutton", {
        label,
        type: "checkbox",
        oncommand: "handleCommand();",
        id: "sidebar-switcher-CBClipboardLoader",
        class: "subviewbutton subviewbutton-iconic"
    }, document.querySelector('toolbarbutton[id^="sidebar-switcher-"] + toolbarseparator'));

    SidebarUI.sidebars.set(id, {
        url,
        title: label,
        buttonId: btn.id,
        menuId: menuitem.id,
    });
    var css = `\
        #${btn.id} > .toolbarbutton-icon,
        #sidebar-box[sidebarcommand="${id}"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
            width: 16px;
            height: 16px;
            opacity: 0.8;
            fill: currentColor;
            -moz-context-properties: fill;
            list-style-image: url(${icon});
        }`;
    var str = "data:text/css," + encodeURIComponent(css), type = windowUtils.USER_SHEET;
    windowUtils.loadSheetUsingURIString(str, type);

    addDestructor(() => {
        btn.remove(); menuitem.remove();
        SidebarUI.sidebars.delete(id);
        windowUtils.removeSheetUsingURIString(str, type);
    });
    var isActive = () => SidebarUI.isOpen && SidebarUI.currentID == id;

    if (isActive()) {
        SidebarUI.selectMenuItem(id);
        var doc = SidebarUI.browser.contentDocument;
        if (doc.readyState != "complete") return;
        var br = doc.getElementById("webext-panels-browser");
        if (br) defaultURL = br.currentURI.spec;
    }
    btn.handleCommand = () => {
        if (!btn.hasAttribute("checked")) {
            SidebarUI._switcherPanel.hidePopup();
            btn.setAttribute("checked", true);
        }
        loadURL(gBrowser.currentURI.spec);
    }

    addEventListener("load", e =>
        e.target.documentURI == url && load(defaultURL)
    , true, SidebarUI.browser);

    var loadURL = url => {
        defaultURL = defaultURL;
        isActive() ? load(url) : SidebarUI.show(id);  
    }

    var principal = {triggeringPrincipal: document.nodePrincipal};
    var config = {browserStyle: false, extension: {remote: false}};

        var e10sFox69 = Services.appinfo.browserTabsRemoteAutostart
        && parseInt(Services.appinfo.platformVersion) >= 69;
    var load = async url => {
        if (e10sFox69) {
            config.uri = url;
            config.extension.remote = E10SUtils.getRemoteTypeForURI(url, true) != E10SUtils.NOT_REMOTE;
        }
        var win = SidebarUI.browser.contentWindow;
        var br = win.document.getElementById("webext-panels-browser");
        if (br) {
            if (br.currentURI.spec === url) return;
            br.parentNode.remove();
        }
        var br = await win.getBrowser(config);
        win.onunload = () => defaultURL = br.currentURI.spec;
        br.loadURI(url, principal);
    }
                 // Обработчик следит за изменениями табов и меняет название и иконку нужного таба ......................................................
gBrowser.tabContainer.addEventListener("TabAttrModified", function(event) {
         // очистить адресную строку если это Clipboard Viewer ....
    var defaultURL1 = "chrome://custombuttons/content/some-unique-name.xhtml";
         if ( gBrowser.currentURI.spec == defaultURL1) gURLBar.value = "Clipboard Viewer"; 
}, true);

})(Cu.import("resource://gre/modules/Services.jsm", {}), "some-unique-name", "viewCBClipboardLoader");



this.info = "ЛКМ: открыть в окне\n\n\
СКМ: открыть в Sidebar\n\n\
Ctrl+СКМ: открыть в новой вкладке";


this.checkForCBWindow = function() {
  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                     .getService(Components.interfaces.nsIWindowWatcher);
  var em = ww.getWindowEnumerator();
  var winName = "clipview";
  var index = 1;
  while (em.hasMoreElements()) {
    let win = em.getNext();
    if(win.name == winName) {
      win.focus();
      return true;
    }
    index++
  }
  return false;
};

(css => {
    this.setAttribute("tooltip", "_child");
    var tooltip = this.appendChild(document.createXULElement("tooltip"));
    this.onmouseover = () => tooltip.label = (gClipboard.read() || this.info);
    css = `#${_id} > tooltip {${css.replace(/;/g, " !important;")}}`;
    var args = ["data:text/css;charset=utf-8," + encodeURIComponent(css), windowUtils.AGENT_SHEET];
    windowUtils.loadSheetUsingURIString(...args);
    addDestructor(() => windowUtils.removeSheetUsingURIString(...args));
})(`
    -moz-appearance: none;
    border: 1px solid black;
    max-width: none;
    background: #ebf1f9;
    color: black;
    font-family: monospace;
    opacity: 0.9;
    border-radius: 5px;
    font-size: 16px;
    padding: 4px 8px;
`);


скрытый текст
Справка

Выделить код

Код:

<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://global/skin/menu.css" type="text/css"?>
<?xml-stylesheet href="chrome://global/skin/popup.css" type="text/css"?>
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
        id="clipboard-viewer"
        width="800" height="600"
        title="Clipboard viewer"
        buttons="extra1, extra2, cancel"
        buttonlabelextra1="Правка"
        buttonlabelextra2="Очистить"
        buttonlabelcancel="Закрыть"
        buttonaccesskeyextra1="E"
        buttonaccesskeyextra2="r"
        buttonaccesskeycancel="C"
        onfocus="loadFromClipboard();">

  <keyset><key keycode="VK_F5" oncommand="loadFromClipboard();" /></keyset>
  <script src="chrome://global/content/globalOverlay.js"/>
  <script src="chrome://global/content/editMenuOverlay.js"/>
  <html:textarea  id="textbox"   contentEditable="true"  style="height: 600pt; "/>
  <script type="application/x-javascript"><![CDATA[

  var dialogEvents = Object.entries({dialogextra1: edit, dialogextra2: clearClipboard, dialogcancel: closeDialog});
for(let args of dialogEvents) document.addEventListener(...args);
addEventListener("unload", () => dialogEvents.forEach(args => document.removeEventListener(...args)), {once: true});
const Cc = Components.classes;
const Ci = Components.interfaces;

const gTextbox = document.getElementById("textbox");

function getMainwin() {
  if (window.frameElement) {
    return window.frameElement.ownerDocument.defaultView;
  } else if (window.opener) {
    return window.opener;
  } else {
    return Cc["@mozilla.org/appshell/window-mediator;1"].
           getService().QueryInterface(Ci.nsIWindowMediator).
           getMostRecentWindow("navigator:browser")
  }
}

function readFromClipboard() {
  var string;
  try {
      var clipboard = Cc["@mozilla.org/widget/clipboard;1"].
                      getService(Ci.nsIClipboard);
      var trans = Cc["@mozilla.org/widget/transferable;1"].
                  createInstance(Ci.nsITransferable);
      trans.addDataFlavor("text/unicode");
      if (clipboard.supportsSelectionClipboard()) {
        clipboard.getData(trans, clipboard.kSelectionClipboard);
      } else {
        clipboard.getData(trans, clipboard.kGlobalClipboard);
      }
      var data = {};
      var dataLen = {};
      trans.getTransferData("text/unicode", data, dataLen);
      if (data) {
        data = data.value.QueryInterface(Ci.nsISupportsString);
        string = trans.getTransferData.length == 2
          ? data.data : data.data.substring(0, dataLen.value / 2);
      }
  } catch (ex) {
  }
  return string;
}

function loadFromClipboard() {
  var string = readFromClipboard();
  if (gTextbox.value != string) {
    if (!string) {
      gTextbox.value = "";
    } else {
      gTextbox.value = string;
    }
  }
  gTextbox.selectionStart = 0;
  gTextbox.selectionEnd = 0;
}

function copyToClipboard(aString) {
  let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].
                        getService(Ci.nsIClipboardHelper);
  clipboardHelper.copyString(aString);
}

function clearClipboard() {
  copyToClipboard("");
  gTextbox.value = "";
}

function edit() {
  edittarget(gTextbox);
}

function closeDialog() {
  getMainwin().gCBClipboardViewer.opened = false;
  if (window.frameElement) {
    switch (window.frameElement.id) {
    case "sidebar":
      getMainwin().gCBClipboardViewer.toggleSidebar();
      break;
    default:
      getMainwin().gCBClipboardViewer.togglePanel();
    }
  } else {
    window.close();
  }
}

function popupShowing(aEvent) {
  var children = aEvent.target.childNodes;
  for (var i = 0; i < children.length; i++) {
    var command = children[i].getAttribute("cmd");
    if (command) {
      var controller = document.commandDispatcher
                               .getControllerForCommand(command);
      var enabled = controller.isCommandEnabled(command);
      if (enabled) {
        children[i].removeAttribute("disabled");
      } else {
        children[i].setAttribute("disabled", "true");
      }
    }
  }
}

/////////////////////////////////////////////////////////////////////////////
////////////////////////////// External Editor //////////////////////////////
/////////////////////////////////////////////////////////////////////////////

var _tmpdir=null,_dir_separator,_os;
var _ext,_encode,_target=[];

function editinit() {
  if (window.navigator.platform.toLowerCase().indexOf("win") != -1) {
    // Windows OS
    _dir_separator = "\\";
    _os = "win";
  } else {
    // UNIX/Linux OS
    _dir_separator = "/";
    _os = "unix";
  }

  _ext = "txt";
  _encode = "UTF-8";
  _target = [];

  window.addEventListener("unload", edituninit, false);
  window.addEventListener("unload", function() {
    document.removeEventListener("focus", checkfocus_window, true);
  }, false);
}

function getEditor() {
  var pref = Cc["@mozilla.org/preferences-service;1"].
             getService(Ci.nsIPrefService).
             getBranch("custombuttons.ClipboardViewer.");
  var editor = null;
  try {
    editor = pref.getCharPref("external_editor");
  } catch(ex) {
    var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                  getService(Ci.nsIPromptService);

    var ask = prompts.confirm(null, "Clipboard Viewer",
                              "Вы должны сначала выбрать текстовый редактор.\nНажмите OK для продолжения.");
    if (!ask) return false;

    var nsIFilePicker = Ci.nsIFilePicker;
    var filePicker = Cc["@mozilla.org/filepicker;1"].
                     createInstance(nsIFilePicker);
    filePicker.init(window, "Select editor", nsIFilePicker.modeOpen);
    filePicker.appendFilters(nsIFilePicker.filterApplication);
    filePicker.appendFilters(nsIFilePicker.filterAll);
    filePicker.open(res => {
                   if (res == nsIFilePicker.returnOK) 
      if (filePicker.file.exists() && filePicker.file.isExecutable()) {
        pref.setCharPref("external_editor", filePicker.file.path);
        editor = filePicker.file.path;
      }
      });
    }
  return editor;
}

function edituninit() {
  if (_tmpdir == null) return;
  var windowType = "navigator:browser";
  var windowManager = Cc["@mozilla.org/appshell/window-mediator;1"].
                      getService();
  var windowManagerInterface = windowManager.
                               QueryInterface(Ci.nsIWindowMediator);
  var enumerator = windowManagerInterface.getEnumerator(windowType);
  if (enumerator.hasMoreElements()) {
    return;
  }

  var file = Cc["@mozilla.org/file/local;1"].
             createInstance(Ci.nsIFile);
  file.initWithPath(_tmpdir);
  var entries = file.directoryEntries;
  while (entries.hasMoreElements()) {
    var entry = entries.getNext().QueryInterface(Ci.nsIFile);
    if (/^custombuttons\./i.test(entry.leafName)) {
      try {
        entry.remove(false);
      } catch(e) {
      }
    }
  }

  try {
    if (file.exists() == true ) {
      file.remove(false);
    }
  } catch(e) {
  }

  _tmpdir = null;
}

function checkfocus_window() {
  var target, filename, timestamp, encode,
      file, inst, sstream, utf, textBoxText;

  if (_target.length<=0) return;

  file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  istr = Cc["@mozilla.org/network/file-input-stream;1"].
         createInstance(Ci.nsIFileInputStream);

  // FileInputStream's read is [noscript].
  sstream = Cc["@mozilla.org/scriptableinputstream;1"].
            createInstance(Ci.nsIScriptableInputStream);
  utf = Cc["@mozilla.org/intl/utf8converterservice;1"].
        createInstance(Ci.nsIUTF8ConverterService);

  for (var i=0; i < _target.length;i++) {
    target = _target[i];
    if (!target.hasAttribute("filename")) continue;
    filename = target.getAttribute("filename");
    timestamp = target.getAttribute("timestamp");
    file.initWithPath(filename);
    if (!file.exists() || !file.isReadable()) continue;
    if (file.lastModifiedTime <= timestamp) continue;

    target.setAttribute("timestamp", file.lastModifiedTime);

    istr.init(file, 1, 0x400, false);
    sstream.init(istr);

    textBoxText  = sstream.read(sstream.available());
    encode = target.getAttribute("encode");
    if (textBoxText.length) {
      copyToClipboard(utf.convertStringToUTF8(textBoxText, encode, true, false));
      target.value = utf.convertStringToUTF8(textBoxText, encode, true, false);
    } else {
      clearClipboard();
      target.value = "";
    }
    sstream.close();
    istr.close();
    try {
      file.remove(false);
    } catch(e) {
    }
  }
}

function editfile(target,filename) {
  // Figure out what editor to use.
  var editor = getEditor();
  //var editor = "C:\\Program Files\\AkelPad\\AkelPad.exe";
  if (!editor) return false;

  var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  file.initWithPath(editor);
  if (!file.exists()) {
    alert("Error_invalid_Editor_file");
    return false;
  }
  if (!file.isExecutable()) {
    alert("Error_Editor_not_executable");
    return false;
  }
  target.setAttribute("filename", filename);
  target.setAttribute("timestamp", file.lastModifiedTime);

  // Run the editor.
  var process = Cc["@mozilla.org/process/util;1"].
                createInstance(Ci.nsIProcess);
  process.init(file);
  var args = [filename];
  process.run(false, args, args.length);  // don't block
  document.addEventListener("focus", checkfocus_window, true);
  return true;
}

function edittarget(target) {
  var textBoxText = target.value;
  // Get filename.
  var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
  if (target.hasAttribute("filename")) {
    var filename = target.getAttribute("filename");
    file.initWithPath(filename);
    try {
      if(file.exists()) file.remove(false);
    } catch(e) {
    }
  } else {
    var filename = TmpFilenameTextarea();
  }
  file.initWithPath(filename);    
  file.create(file.NORMAL_FILE_TYPE, 0x180);

  // Write the data to the file.
  var ostr = Cc["@mozilla.org/network/file-output-stream;1"].
             createInstance(Ci.nsIFileOutputStream);
  ostr.init(file, 2, 0x200, false);

  if(navigator.platform == "Win32") {
    // Convert Unix newlines to standard network newlines
    textBoxText = textBoxText.replace(/\n/g, "\r\n");
  }
  var conv = Components.classes['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  try {
        conv.charset = 'utf-8';
	textBoxText = conv.ConvertFromUnicode(textBoxText);
    
  } catch(e) {
    textBoxText = "";
  }
  ostr.write(textBoxText, textBoxText.length);

  ostr.flush();
  ostr.close();

  // setup target info
  target.setAttribute("encode", _encode);

  // Edit the file.
  if (editfile(target,file.path)) {
    _target.push(target);  // Editting target array
  }
}

//Compose temporary filename out of
function TmpFilenameTextarea() {
  var TmpFilename;
  _tmpdir = gettmpDir();
  do {
    TmpFilename = _tmpdir + _dir_separator + "clipboard." +
                  Math.floor(Math.random() * 100000) + "." + _ext;
  } while (!ExistsFile(TmpFilename))
    return TmpFilename;
}

//Function returns true if given filename exists
function ExistsFile(filename) {
  try {
    var file = Cc["@mozilla.org/file/local;1"].
               createInstance(Ci.nsIFile);
    file.initWithPath(filename);
    return true;
  } catch(e) {
    return false;
  }
}

/**
* Returns the directory where we put files to edit.
* @returns nsILocalFile The location where we should write editable files.
*/
function gettmpDir() {
  /* Where is the directory that we use. */
  var fobj = Cc["@mozilla.org/file/directory_service;1"].
             getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);
  fobj.append("Clipboard_Viewer");
  if (!fobj.exists()) {
    fobj.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0700", 8));
  }
  if (!fobj.isDirectory()) {
    alert("Возникли проблемы с поиском или создать каталог: "+ fobj.path);
  }
  return fobj.path;
}

//////////////////////////////////////////////

function onLoad() {
  getMainwin().gCBClipboardViewer.opened = true;
  editinit();
  gTextbox.focus();
}

window.addEventListener("load", onLoad, false);
window.removeEventListener("unload", onLoad, false);

  ]]></script>

</dialog>


2.
скрытый текст

Выделить код

Код:

// Добавить новый пункт "Открыть в боковой панели" в контекстное меню вкладки ........................................................................................
( {
    var label = "CB Site Loader";
    var url = "chrome://browser/content/webext-panels.xhtml?" + id;
    var icon = "chrome://devtools/skin/images/dock-side-left.svg";
    var defaultURL = "data:text/html;charset=utf-8,<center><h1>Заглушка</h1></center>";
    var currentURL;

    var e = (name, attrs, node, append) => {
        var elm = document.createXULElement(name);
        for(var a in attrs) elm.setAttribute(a, attrs[a]);
        append ? node.append(elm) : node.before(elm);
        return elm;
    }
    var menuitem = e("menuitem", {
        label,
        type: "checkbox",
        id: "menu_CBSiteLoader",
        oncommand: "handleCommand1();",
    }, document.getElementById("viewSidebarMenu"), true);


        handleCommand1 = () => loadURL(
        TabContextMenu.contextTab.linkedBrowser.currentURI.spec
    );

    var btn = e("toolbarbutton", {
        label,
        type: "checkbox",
        oncommand: "handleCommand();",
        id: "sidebar-switcher-CBSidebarLoader",
        class: "subviewbutton subviewbutton-iconic"
    }, document.querySelector('toolbarbutton[id^="sidebar-switcher-"] + toolbarseparator'));

    SidebarUI.sidebars.set(id, {
        url,
        title: label,
        buttonId: btn.id,
        menuId: menuitem.id,
    });
    var css = `\
        #${btn.id} > .toolbarbutton-icon,
        #sidebar-box[sidebarcommand="${id}"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
            width: 16px;
            height: 16px;
            opacity: 0.8;
            fill: currentColor;
            -moz-context-properties: fill;
            list-style-image: url(${icon});
        }`;
    var str = "data:text/css," + encodeURIComponent(css), type = windowUtils.USER_SHEET;
    windowUtils.loadSheetUsingURIString(str, type);

    addDestructor(() => {
        btn.remove(); menuitem.remove();
        SidebarUI.sidebars.delete(id);
        windowUtils.removeSheetUsingURIString(str, type);
    });
    var isActive = () => SidebarUI.isOpen && SidebarUI.currentID == id;

    if (isActive()) {
        SidebarUI.selectMenuItem(id);
        var doc = SidebarUI.browser.contentDocument;
        if (doc.readyState != "complete") return;
        var br = doc.getElementById("webext-panels-browser");
        if (br) currentURL = br.currentURI.spec;
    }
    btn.handleCommand = () => {
        if (!btn.hasAttribute("checked")) {
            SidebarUI._switcherPanel.hidePopup();
            btn.setAttribute("checked", true);
        }
        loadURL(gBrowser.currentURI.spec);
    }
    
    if ( document.getElementById("TabCBSite") ) return;
 
  var tabContext = document.getElementById("tabContextMenu");
 
   var mItem = document.createXULElement("menuitem");                 
   mItem.setAttribute("id", "TabCBSite");
   mItem.setAttribute("label", "Открыть в боковой панели");
   mItem.setAttribute("oncommand", "handleCommand1()" );
   mItem.setAttribute("class", "menuitem-iconic");
   mItem.setAttribute("image", icon);
   tabContext.insertBefore( mItem, tabContext.firstChild.nextSibling ); // как первый пункт

 
    addEventListener("load", e =>
        e.target.documentURI == url && load(currentURL || defaultURL)
    , true, SidebarUI.browser);

    var loadURL = url => {
        currentURL = url;
        isActive() ? load(url) : SidebarUI.show(id);    
    }
    var principal = {triggeringPrincipal: document.nodePrincipal};
    var config = {browserStyle: false, extension: {remote: false}};
    //var load = async url => {
        var e10sFox69 = Services.appinfo.browserTabsRemoteAutostart
        && parseInt(Services.appinfo.platformVersion) >= 69;
    var load = async url => {
        if (e10sFox69) {
            config.uri = url;
            config.extension.remote = E10SUtils.getRemoteTypeForURI(url, true) != E10SUtils.NOT_REMOTE;
        }
        var win = SidebarUI.browser.contentWindow;
        var br = win.document.getElementById("webext-panels-browser");
        if (br) {
            if (br.currentURI.spec === url) return;
            br.parentNode.remove();
        }
        var br = await win.getBrowser(config);
        win.onunload = () => currentURL = br.currentURI.spec;
        br.loadURI(url, principal);
    }
})("viewCBSiteLoader");

Отредактировано Andrey_Krropotkin (06-04-2021 22:31:34)

Отсутствует

 

№1534507-04-2021 00:39:53

toxa
Участник
 
Группа: Members
Зарегистрирован: 11-04-2012
Сообщений: 261
UA: Firefox 85.0

Re: Custom Buttons

Dumby пишет
toxa пишет

Если после инициализации я открою кнопку подредактировать, а потом закрою, то листнер дублируется. Помогает только перезапуск браузера.
Можно как то сделать, чтоб если листнер уже есть, новый не добавлялся?

Ну флаг можно поставить куда-нибудь,
в окно или иное подобное надёжное место.
Если флаг есть, то не добавляем листенер, иначе добавляем.

А как поставить флаг в окно? И какие есть еще места, куда можно поставить флаг?

Отсутствует

 

№1534607-04-2021 02:18:49

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 472
UA: Firefox 84.0

Re: Custom Buttons

Dumby спасибо за кнопку QuickToggleAboutSettings!
Дополнил кнопку: выбор шрифтов, подсказки, умолчания, Zoom, графика, Hotkeys, запоминание путей сохранения файлов и картинок и прочее…

Выделить код

Код:

// Quick Toggle Быстрое переключение параметров about:config для custom_script.js

(async (name, id, func) => { // https://forum.mozilla-russia.org/viewtopic.php?pid=789824#p789824
	if (name == "Object") return CustomizableUI.createWidget(func());
	var win = name == "Window", g = Cu.import("resource://gre/modules/Services.jsm", {});
	if (g[id]) {if (win) return;} else g[id] = func(); if (win) return CustomizableUI.createWidget(g[id]);
	addDestructor(r => r[5] == "e" && delete g[id]); g[id].onCreated(this);	// BEGIN QuickToggle…
})(this.constructor.name, "ToggleAboutConfig", () => { var help =

`ПКМ	Меню быстрых настроек 
։нажать	Краткая справка ✍
…+Alt	Опции about:config\n
ЛКМ	Боковая панель: Журнал
։нажать	Антизапрет proxy
…+Alt	Пипетка: захват цвета
…Shift	★ Библиотека закладок\n
СКМ	± Zoom Текст/Страница
։нажать	Консоль браузера\n
- тире ⟳ Обновить ↯ Перезапуск
СКМ ролик мыши, :нажать ≥ 1 сек`, // :нажать - удержание кнопки мыши около секунды. свободные hotkeys: ЛКМ+Alt+⇧

help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762'],

// Ctrl+Click или правый клик - сброс параметра по-умолчанию
// клик по параметру с Shift блокирует авто-закрытие меню
// строки с userAlt имеют шрифт italic
//	refresh: false - reload current tab,	true - reload current tab skip cache
//	restart: false - restart browser,		true - restart browser with confirm
// Разделитель: Имя меню "—,⟳,↯" Опция, ⟳ обновить страницу, ↯ перезапуск браузера
// иконки равны ключам: userChoice:зелёный, userAlt:жёлтый, userPro:серый, нет userChoice:серый, ни один:красный

	{prefs, dirsvc} = Services, db = prefs.getDefaultBranch(""), my_vpn = "https://antizapret.prostovpn.org/proxy.pac", icon_vpn = "hue-rotate(270deg) brightness(95%)", menuactive = (AppConstants.platform == "macosx") ? '#e8e8e8' : '#124', // текст, подсвеченный курсором
	xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
	fonts = ["Arial","Cantarell","DejaVu Sans","Roboto","PT Serif","Segoe UI","Ubuntu","Cambria","Fira Sans","Georgia","Noto Sans","Calibri","Times","системный"],
	font_pref = (font) => { return font.map(function(name) { // массив с вложениями
		return (name == font[font.length -1]) ? ["", name,,, `prefs.setIntPref("browser.display.use_document_fonts", 1)`] : [name, name,,, `prefs.setIntPref("browser.display.use_document_fonts", 0)`]; });
	},
	fontserif = font_pref(fonts), fontsans = [["PT Sans","PT Sans"], ...fontserif], dw; // путь загрузки
	try {dw = prefs.getComplexValue("browser.download.dir",Ci.nsIFile);} catch {dw = dirsvc.get("DfltDwnld", Ci.nsIFile);};

	var secondary = [{ // pref … [apref, lab, akey, hint, js-code]
			pref: ["dom.disable_open_during_load", "Блокировать всплывающие окна"], userChoice: 2, userAlt: true,
	},{
			pref: ["browser.safebrowsing.downloads.remote.block_dangerous", "Опасные файлы, сайты",,"browser.safebrowsing.downloads.remote.block_dangerous_host"], userChoice: true, userAlt: false,
			values: [[true, "Запретить",,,`prefs.setBoolPref('browser.safebrowsing.downloads.remote.block_dangerous_host',true)`], [false, "Загружать",,,`prefs.setBoolPref('browser.safebrowsing.downloads.remote.block_dangerous_host',false)`]]
	},{
			pref: ["permissions.default.image", "Загрузка графики"], userChoice: 1, userAlt: 3, refresh: true,
			values: [[1, "Разрешена"], [3, "Только с сайта"], [2, "Отключить"]]
	},{
			pref: ["ucf_save.dirs", `Сайт|Графика`,,`\nПути сохранения страниц | графики\n[Загрузки] — папка по-умолчанию${dw ? ":\n"+ dw.path : ""}`], userChoice: "_Сайты||_Картинки|1", userAlt: "", userPro: "_Web||_Images|1",
			values: [
				["", "папка [Загрузки]"], // subdir: пусто | 0 заголовок | 1 домен
				[`_Сайты||_Картинки|1`, "_Сайты|_Картинки/имя"], // _Web/host|_Pics/title
				[`_Web||_Images|`, "_Web|_Images"],
				[`_Web||_Images|1`, "_Web|_Images/имя"],
				[`_Web||_Pics|1`, "_Web|_Pics/имя"],
				[`_Web|1|_Pics|0`, "_Web/сайт|_Pics/имя"],
				[`_Web|1|_Pics|`, "_Web/сайт|_Pics"],
				[`_Web|1|_Images|0`, "ввести свои пути"]] // здесь нужно открыть about:config
	},null,{
			pref: ["network.proxy.autoconfig_url", "Прокси (VPN) URL", "п"],
			userChoice: my_vpn, userAlt: "127.0.0.1", userPro: "", refresh: true,
			values: [
				["", "сброшен", ""],
				[my_vpn, "АнтиЗапрет", "1", "\nНадёжный доступ на заблокированные сайты\n«Режим прокси» меняется на 2", `prefs.setIntPref('network.proxy.type', 2); node.parentNode.parentNode.style.filter = icon_vpn;`],
				["https://git.io/ac-anticensority-pac", "ac-anticensority", "2"],
				// ["localhost", "Tor Browser", "4", "Только для Linux, MacOS\nУстановите сервис: «tor»"],
				[prefs.getStringPref("user.pacfile", "file:///etc/proxy.pac"), "user .pac файл", "3"], // нужен диалог выбора pac-файла
				["127.0.0.1", "local host", "0",, `prefs.setIntPref('network.proxy.type', 0); node.parentNode.parentNode.style.filter = '';`]]
	},{
			pref: ["network.proxy.type", "Режим прокси", "р"], userChoice: 0, userAlt: 2, refresh: true,
			values: [
				[0, "Без прокси", "0", "по-умолчанию"],
				[5, "Системные (из IE)", "5"],
				[2, "Автонастройка", "2", "about:config - user.pacfile"],
				[1, "Ручная настройка", "1", "Используется network.proxy.autoconfig_url"],
				[4, "Автоопределение", "4"] ]
	},{
			pref: ["network.proxy.share_proxy_settings", "Все протоколы через прокси"], userAlt: true, refresh: true,
			values: [[true, "Да", "", "Прокси для всех протоколов при ручной настройке"], [false, "Нет"]]
	},{
			pref: ["network.trr.mode", "DNS поверх HTTPS",, "\nШифрование DNS-трафика для\nзащиты персональных данных"], userChoice: 1, userAlt: 2, userPro: 5, refresh: true,
			values: [
				[0, "по-умолчанию", "0"], [1, "автоматически", "1", "используется DNS или DoH, в зависимости от того, что быстрее"], [2, "DoH, затем DNS", "2"], [3, "только DoH", "3"], [4, "DNS и DoH", "4"], [5, "отключить DoH", "5"] ]
	},null,{
			pref: ["browser.zoom.full", "Масштабировать"], userChoice: false, userAlt: true,
			values: [[true, "всю страницу"], [false, "только текст"]]
	},{
			pref: ["font.name.sans-serif.x-cyrillic", "Шрифт без засечек ",,"\nТакже влияет на всплывающие подсказки\nСистемный: загрузка шрифтов документа"], userAlt: "", values: fontsans
	},{
			pref: ["font.name.serif.x-cyrillic", "Шрифт с засечками"], userAlt: "", values: fontserif
	},{
			pref: ["image.animation_mode", "Анимация изображений"], userChoice: "none", userAlt: "normal", refresh: true,
			values: [["none", "Выключена"], ["normal", "По циклу"], ["once", "Единожды"]]
	},null,{
			pref: ["media.autoplay.default", "Авто-play аудио/видео"], userChoice: 0, userAlt: 2, userPro: 5, refresh: true,
			values: [
				[0, "Разрешить", "0"], [2, "Спрашивать", "2"], [1, "Запретить", "1"], [5, "Блокировать", "5"]]
	},{
			pref: ["media.autoplay.blocking_policy", "Автозапуск (политика)"], userChoice: 1, userAlt: 2, refresh: true,
			values: [[1, "Временная", "1"], [2, "По действию", "2"], [0, "Постоянная", "0"]]
	},{
			pref: ["gfx.webrender.all", "Аппаратное ускорение графики"], userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["gfx.webrender.force-disabled", "Web render disabled", , "gfx.webrender.compositor.force-enabled\n\nАппаратная отрисовка страниц видеокартой.\nотключите при разных проблемах с графикой"],
			userChoice: false, restart: true, values: [
			[true, "Да",,, `prefs.setBoolPref("gfx.webrender.compositor.force-enabled", false)`],
			[false, "Нет",,, `prefs.setBoolPref("gfx.webrender.compositor.force-enabled", true)`]]
	},null,{
			pref: ["network.cookie.cookieBehavior", "Получать куки",, "\nПерсональные настройки посещённых сайтов"], userChoice: 3, userAlt: 0, userPro: 4, refresh: false,
			values: [[0, "со всех сайтов"], [3, "кроме не посещённых"], [4, "кроме трекеров"], [1, "кроме сторонних"], [2, "никогда"]]
	},{
			pref: ["network.http.sendRefererHeader", "Referer: для чего"], userChoice: 2, userAlt: 1,
			values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки, графика", "2"]]
	},{
			pref: ["dom.storage.enabled", "Локальное хранилище",, "\nСохранение персональных данных, по\nкоторым вас можно идентифицировать"],
			userChoice: false, userAlt: true,
			values: [[true, "Разрешить"], [false, "Запретить"]]
	},{
			pref: ["privacy.resistFingerprinting", "Изоляция Firstparty-Fingerprint", ,"privacy.firstparty.isolate\n\nЗащита данных пользователя также\nзапрещает запоминать размер окна"], userChoice: false,
			values: [[true, "Да", , "Защита от слежки",`prefs.setBoolPref('privacy.firstparty.isolate', true);`], [false, "Нет", , "Защита от слежки",`prefs.setBoolPref('privacy.firstparty.isolate', false);`]]
	},{
			pref: ["media.peerconnection.enabled", "WebRTC ваш реальный IP"], userChoice: false,
			values: [[true, "Выдать"], [false, "Скрыть"]]
	},null,{
			pref: ["browser.tabs.remote.force-enable", "Многопоточный режим вкладок"], userChoice: null, userAlt: true, userPro: false,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["javascript.enabled", "Выполнять скрипты Java",,"\nПоддержка интерактивных сайтов (и рекламы)"], userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["browser.cache.disk.capacity", "Кэш браузера",,"browser.cache.memory.enable"], userChoice: 1048576, userAlt: 0,
			values: [
			[1048576, "Диск и Память",,, `prefs.setBoolPref("browser.cache.memory.enable", true); prefs.setBoolPref("browser.cache.disk.enable", true)`],
			[0, "только Память",,, 		`prefs.setBoolPref("browser.cache.memory.enable", true); prefs.setBoolPref("browser.cache.disk.enable", false)`],
			[2097152, "только Диск",,, `prefs.setBoolPref("browser.cache.memory.enable", false); prefs.setBoolPref("browser.cache.disk.enable", true)`]]
	},{
			pref: ["dom.enable_performance", "Статус загрузки страницы",,"\nПередача данных разрешит определять\nфакт использования прокси-сервера"], userAlt: true
	},{
			pref: ["general.useragent.override", "User Agent"],
			userChoice: null, userAlt: "Mozilla/5.0 (Android 9; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0", userPro: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:68.0) Gecko/20100101 Firefox/68.0", refresh: true,
			values: [
				(arr => {
					var pref = "general.useragent.override";
					var has = prefs.prefHasUserValue(pref);
					if (has) {
						var val = prefs.getStringPref(pref);
						prefs.clearUserPref(pref);
					}
					var ua = Cc["@mozilla.org/network/protocol;1?name=http"]
						.getService(Ci.nsIHttpProtocolHandler).userAgent; // текущий юзерагент
					has && prefs.setStringPref(pref, val);

					var find = node => node.pref && node.pref.pref == pref;
					var redef = (doc, hint) => {
						var popup = doc.getElementById("ToggleAboutConfig-secondaryPopup");
						var menuitem = Array.from(popup.children).find(find).menupopup.firstChild;
						menuitem.tooltipText = hint ? ua + "\n" + hint : ua;
						menuitem.setAttribute("oncommand",
							`event.stopPropagation();
							this.closest("toolbarbutton").linkedObject.contextmenu({
								preventDefault: Boolean,
								target: this.parentNode.parentNode
							});`
						);
					}
					Object.defineProperty(arr, "0", {enumerable: true, get() {
						if (Components.stack.formattedStack.includes("createRadios")) {
							var win = Services.wm.getMostRecentWindow("navigator:browser");
							win.setTimeout(redef, 0, win.document, this[3]);
						}
						else return "";
					}});
					return arr;
				})([null, "По-умолчанию"]),
				["Mozilla/5.0 (Android 9; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0", "Firefox Android9"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:68.0) Gecko/20100101 Firefox/68.0", "Firefox 68 MacOSX"],
				["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", "Chrome61 Win10"],
				["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56"],
				["Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56 Linux"],
				["Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", "MSIE 6.0 Windows"],
				["Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", "Chrome61 Android7"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30", "Safari 6 MacOSX"],
				["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
				["Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", "Samsung Galaxy S6"],
				["Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)", "Playstation 4"],
				["Xbox (Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Xbox One (mobile)"],
				["Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Microsoft Lumia 950"],
				["Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350)", "Windows Phone"],
				["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"]]
	},{
			pref: ["browser.sessionstore.restore_on_demand", "Загружать неактивные вкладки", , "\nПри запуске загружаются все вкладки,\nэто может замедлить работу браузера."], userAlt: false,
			values: [[false, "Да"], [true, "Нет"]]
	}];
	return {
		label: "Quick Toggle Settings",
		id: "ToggleAboutConfig",
		tooltiptext: help, localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAADhUlEQVR4AaWUIYzsNhCG91WVelKfqoWrpwMLFxQYGhoGBgYGBgYGGhoGBhoGGhoaBhoaGhqcdIFhf+dGt2n09rpq1ZE+2bHl+eyxd0993yVjNIwxq9F6/WjtaNd5nBnq85g2PMeMo8EwDEUI0Z7+jm/EY2it87ZtuAf1EUqAzZZZcuCxnyOlBFoLkuhnkgdBjBG36YqzP+EaTqgmSckyjrGuK8paWGythVLKfv/9t/O/OkEIC6pZoskCfanQuQYLSTm2/ZQkjSil8Pc8z6iqyp/P58tn2l92wWjGnEuG9gO6uUVtaygnMOQaU+nR+hotjfeugwmaHNv9pJzYOYcQAp+kbVv3cBJrbA45QIQLVLyizRI6t5R8gC0aY+nQZYWK5lT4E3nNn2XaiJX5PAmLXl9fuweBz54X97mCoYR21SwYc8+yjsbrKKGCvAu4TDnne3IO7z2oTP1RwCVKJXGJ+rlHaxu0ocZQahLWaO4lmjtov5eI7yDZFnFqWXQUPL1kFzwaV3GpmqQo+cMlc1li9MBCyacbUnC74MePy3NBiAukFRDLBTd/QWUVUk44BJclx5lMPVYvEJ3e7+CZYN+dSw5TmpiQA1/oPTYip4SV5gANRIloa5Ku/LKeCqhljrHmhLzMSIFYPGLw3N/KCMAAuUYeBUkXLDE9vCIW0K45MbXYoW/aFZdiyxYoM9Y0oiwGaxyBjcAElAHFCETTw40T6tvVXP749eUfBe/v78xdlJaAvNwTGmo1YYiRIME6YosGxWnEaUDQbXFt5adazKO6DEcBX97b29tRAq4tl0XzpZKAkzJFEx31iY1bYuANIBtkW6engl1CRD6JAYrh5Mg90QBRAYsgbkAkksS2VJvT0jXqUj0R7HzOrViCQ1kGILZAUAQl9AxLVl/BDTI24tyK1+/8n/SVYOexVDMlaQAvAHdjOHmQLPS6grxe9ZeviDgKuM/jzIZM38kbbE4ekguGBV7CDwLXy1k/vKJtPf4ODmAPFhbf74mLa5DmFltQwCKJCskIiPNpprTi42+bRcaYTIv3Z/oVhcgpUrIOm1NYbIvZWtjJIliSLgpIFdZZohMvgQRqFyilRimFF0I4+QXik7ZR3vRqqSsRpJSO1jEf/bFXGUsNhAq2uWS63eb0X+NjN/uuforb9UXYXjqEBkkLyMupP85/I/53fDzLsZV6am8zCdR9/C+4MZTbZ2zc/gAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

			btn.btn = true;
			btn.domParent = null;
			btn.popups = new btn.ownerGlobal.Array();
			this.createPopup(doc, btn, "secondary", secondary);
			this.createCloseMenusOption(doc, btn);

			if (prefs.getIntPref('network.proxy.type') == 2)
				btn.style.filter = icon_vpn; // btn.style.cssText = "background-image: -moz-linear-gradient(#c0c8c0, #c0c8c0, #c0c8c0) !important";

			btn.linkedObject = this;
			for(var type of ["command", "contextmenu", "mousedown", "auxclick"]) // события
				btn.setAttribute("on" + type, `linkedObject.${type}(event)`);
			this.addSheet(btn);
		},
		addSheet(btn) {
			var cb = Array.isArray(btn._destructors);
			var id = cb ? btn.id : "ToggleAboutConfig";
			var css = `#${id} menu[_moz-menuactive] {
				color: ${menuactive} !important;
			}`;
			var args = [
				"data:text/css;charset=utf-8," + encodeURIComponent(css),
				Ci.nsIDOMWindowUtils.USER_SHEET
			];
			if (cb) var destructor = function() {
				this.removeSheetUsingURIString(...args);
			}
			var add = b => b.ownerGlobal.windowUtils.loadSheetUsingURIString(...args);
			(this.addSheet = !cb ? add : btn => {
				add(btn);
				btn._destructors.push({destructor, context: btn.ownerGlobal.windowUtils});
			})(btn);
		},
		createPopup(doc, btn, name, data) {
			var popup = doc.createElementNS(xul_ns, "menupopup");
			var prop = name + "Popup";
			btn.popups.push(btn[prop] = popup);
			popup.id = this.id + "-" + prop;
			for (var type of ["popupshowing", "click"])
				popup.setAttribute("on" + type, `parentNode.linkedObject.${type}(event)`);
			for(var obj of data) popup.append(this.createElement(doc, obj));
			btn.append(popup);
		},
		map: {b: "Bool", n: "Int", s: "String"},
		createElement(doc, obj) {
			if (!obj) return doc.createElementNS(xul_ns, "menuseparator");
			var pref = doc.ownerGlobal.Object.create(null), node, img, bool;
			for(var [key, val] of Object.entries(obj)) {
				if (key == "pref") {
					var [apref, lab, akey, hint] = val;
					pref.pref = apref; pref.lab = lab || apref;
					if (hint) pref.hint = hint;
				}
				else if (key == "image") img = val, pref.img = true;
				else if (key != "values") pref[key] = val;
				else pref.hasVals = true;
			}
			var type = prefs.getPrefType(pref.pref);
			var str = this.map[type == prefs.PREF_INVALID
				? obj.values ? (typeof obj.values[0][0])[0] : "b"
				: type == prefs.PREF_BOOL ? "b" : type == prefs.PREF_INT ? "n" : "s"
			];
			pref.get = prefs[`get${str}Pref`];
			var map, set = prefs[`set${str}Pref`];
			if (pref.hasVals) {
				for(var [val, , , , code] of obj.values)
					code && (map || (map = new Map())).set(val, code);
				if (map) pref.set = (key, val) => {
					set(key, val);
					map.has(val) && eval(map.get(val)); // выполнить код
				}
			}
			if (!map) pref.set = set;

			node = doc.createElementNS(xul_ns, "menu");
			node.className = "menu-iconic";
			node.setAttribute("closemenu", "none");
			img && node.setAttribute("image", img);
			akey && node.setAttribute("accesskey", akey);
			(node.pref = pref).vals = doc.ownerGlobal.Object.create(null);
			this.createRadios(doc,
				str.startsWith("B") && !pref.hasVals ? [[true, "true"], [false, "false"]] : obj.values,
				node.appendChild(doc.createElementNS(xul_ns, "menupopup"))
			);
			if ("userChoice" in obj) pref.noAlt = !("userAlt" in obj);
			return node;
		},
		createCloseMenusOption(doc, btn) {
			var pn = this.closePref = "ToggleAboutConfig.closeMenus";
			var data = [null, {
				pref: [pn, "Закрывать меню этой кнопки"], values: [[true, "Да"], [false, "Нет"]]
			}];
			var setCloseMenus = (e, trg = e.target) => {
				e.stopPropagation();
				var {pref, val} = trg, updPopup = true, clear;
				switch(e.type) {
					case "command": pref = (trg = trg.closest("menu")).pref; updPopup = false; break;
					case "click": if (e.button) return; break;
					case "contextmenu": e.preventDefault(); clear = pref;
				}
				if (!pref) return;
				if (clear) prefs.clearUserPref(pn);
				else if (!updPopup && val === pref.val) return;
				else pref.set(pn, val !== undefined ? val : !pref.val);
				this.upd(trg);
				updPopup && this.popupshowing(null, trg.querySelector("menupopup"));
			}
			(this.createCloseMenusOption = (doc, btn) => {
				for(var obj of data)
					btn.secondaryPopup.append(this.createElement(doc, obj));
				var m = btn.secondaryPopup.lastChild;
				m.style.cssText = "fill: lightblue !important; list-style-image: url(chrome://browser/skin/menu.svg) !important;";
				m.setAttribute("oncommand", "setCloseMenus(event)");
				m.onclick = m.oncontextmenu = m.setCloseMenus = setCloseMenus;
			})(doc, btn);
		},
		UserImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAe1BMVEUAAAB1dXV1dXV1dXV1dXV1dXV1dXV1dXV8fHyRkZGfn5+oqKipqamqqqqysrKzs7O6urq8vLy9vb3ExMTFxcXHx8fNzc3Ozs7Pz8/Q0NDY2NjZ2dna2tre3t7g4ODi4uLj4+Pk5OTl5eXn5+fs7Ozv7+/w8PDx8fHy8vLK4aRZAAAACHRSTlMAGFiAiNDw+LBTincAAACRSURBVBgZBcFBTsMADAAwJ20pqjgM+P8TgQua2Nok2AAAAiIzdfcgkOv2/ub357yaIF8+jwgz969nS7He9pnumf22hrSse1d1V/W+LlIc3d1V3d1HCPsHkWjj+5FUTVdVTdcQtmMNkXrMdT/Dur0KYYy/8wqx7QugHucE+bIFmPPZArkuS+qqqwmIzNTdAwAA/zI6Uy+Vzu5KAAAAAElFTkSuQmCC", // серый
		UserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABGElEQVR4AWKgPgAQSmbgEsNQFB53GHz43GGcioPF4mCxGCwWi8FiMBgMBoPFYOzOPWf2/cv3Z/3P7Wo2W2VQolIJ59zb/grvlfLnO1lWJ7lmwDn2cEbnY3jciF0XSTUqSfKZRCLP4NB9c9tlyrOE4iXUwMAd2OMZHLiPj2O0uUZcWcQXp3jKsQTAuSeODlxm7grEIRmxZZal2EuhOxjEGR24yNwXqHOe5MQsdtVCq72He+DiIfNQYMxGwJRHhdIzPLs4zwViGw4ypF5MGsiYIBLML/t04D49gja7kz4eyEB6yiQCnhG4zDx/xsa30sUL3RMtgXP5jO9+JApt2L+DZ3C+/o2sbv9l5xsNtYBz7B0HS8qUZSZqAwCSaKMMRS9J3wAAAABJRU5ErkJggg==", // зелёный
		notUserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABDElEQVR4Aa2TK3CEQBBEt6JwdxKJRJ48GRkZiURGxqt4dV5FnkQikSdXIpHIweE6+8KKyf+7Va92qqe770v493MOoUw8JGJizcSslV+Fm3NRWLxrZUMnzaOAGY0dng/DXVnK+kdpGhKXFI4CZjR2ePC+edu0W3eSYieNPQEPGjvhwUuG7FXuuK+b691uX4Sw2obBmkHbwIOXjC+4qY61D+d5Bq89g5eML6hpxvQd8JLxBXk5uxsM3p05vmBcJhfmnrkzbmaHl4wv6KfL5IKuZHbFecZLxhecxiEuiwVX4nBhPHjJvP0j7QtZe5TaWmqqdG8wo7HD8+m/MWHxUMluDym0wYzGDs/fHqb/Pk9WVkDcHStz1AAAAABJRU5ErkJggg==", // красный
		UserAltImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAklEQVR4Aa2TKVDEQBREx3sRiUQi8Wpl5MqVSCQyMhIZGYlciVy5cmQkEhk5runHNQf3kapX9ev36w97hX9/xk3ozGCiScDMjuyr8va6D+syd9LdIOkgYGZHhvNhed4FyxdSmswsaQ/M7MiEg9uWO65r6aV1MCOFCnZkOLh0ygPjMgXpvjc7i5f50HORHRkOLp3yQNSR131uYZMPFbAjw8GlUx5IWk5kLJwiGeQMOzIcoFMfiEEFz+IZMHsHOW8PRO0dHMyxIEK9w8FtX8IYBwe3gPQeOcel0377Vt1w/XNwcOm8/SJtLcxI70OG8+m3kevxKihNucjMjgznbz+m/34eAEg4sZItNtjUAAAAAElFTkSuQmCC", // жёлтый
		regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
		upd(node) {
			var {pref} = node, def = false, user = false, val;
			if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
				var pn = pref.pref;
				try {val = pref.defVal = db[pref.get.name](pn); def = true}
				catch(ex) {def = false;}
				var user = prefs.prefHasUserValue(pn);
				if (user) try {val = pref.get(pn, undefined);} catch(ex) {}
			}
			if (val == pref.val && def == pref.def && user == pref.user) return;
			pref.val = val; pref.def = def; pref.user = user;
			var exists = def || user;

			var hint = exists ? val : "Эта опция не указана";
			if (hint === "") hint = "[ пустая строка ]";
			hint += "\n" + pref.pref;
			if (pref.hint) hint += "\n" + pref.hint;
			node.tooltipText = hint;

			var img, alt = "userAlt" in pref && val == pref.userAlt, pro = "userPro" in pref && val == pref.userPro;
			if (alt) img = this.UserAltImg;
			if (pro) img = this.UserImg;
			if ("userChoice" in pref)
				if (val == pref.userChoice)
					node.style.removeProperty("color"),
					img = this.UserChoiceImg;
				else {
					node.style.setProperty("color", "#804040", "important");
					if (!alt && !pro) img = this.notUserChoiceImg;
				}
			node.nextSibling && node.setAttribute("image", img || this.UserImg); // серый значок, если нет userChoice
			user
				? node.style.setProperty("font-style", "italic", "important")
				: node.style.removeProperty("font-style");

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "другое" : "стандарт";
				lab += ` ${"restart" in pref ? "↯-" : "refresh" in pref ? "-⟳" : "—"} ${sfx}`;
			}
			lab = exists ? lab : '['+ lab +']'; // имя = [имя] если преф не существует
			node.setAttribute("label", lab);
		},
		createRadios(doc, vals, popup) {
			for(var arr of vals) {
				if (!arr) {
					popup.append(doc.createElementNS(xul_ns, "menuseparator"));
					continue;
				}
				var [val, lab, key, hint] = arr;
				var menuitem = doc.createElementNS(xul_ns, "menuitem");
				menuitem.setAttribute("type", "radio");
				menuitem.setAttribute("closemenu", "none");
				menuitem.style.setProperty("font-style", "italic", "important"),
				menuitem.setAttribute("label", popup.parentNode.pref.vals[val] = lab);
				key && menuitem.setAttribute("accesskey", key);
				var tip = menuitem.val = val;
				if (hint) tip += "\n" + hint;
				menuitem.tooltipText = tip;
				popup.append(menuitem);
			}
		},
		openPopup(popup) {
			var btn = popup.parentNode;
			if (btn.domParent != btn.parentNode) {
				btn.domParent = btn.parentNode;
				if (btn.matches(".widget-overflow-list > :scope"))
					var pos = "after_start";
				else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
					btn.closest("toolbar").getBoundingClientRect(), pos = width > height
						? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
						: `${win.innerWidth - right > left ? "end" : "start"}_before`;
				for(var p of btn.popups) p.setAttribute("position", pos);
			}
			popup.openPopup(btn);
		},
		maybeRestart(node, conf) {
			if (conf && !Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) return;
			var cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
			Services.obs.notifyObservers(cancel, "quit-application-requested", "restart");
			return cancel.data ? Services.prompt.alert(null, this.label, "Запрос на выход отменён.") : this.restart();
		},
		async restart() {
			var meth = Services.appinfo.inSafeMode ? "restartInSafeMode" : "quit";
			Services.startup[meth](Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
		},
		maybeRe(node, fe) {
			var {pref} = node;
			if ("restart" in pref) {
				if (this.maybeRestart(node, pref.restart)) return;
			}
			else this.popupshowing(fe, node.parentNode);
			if ("refresh" in pref) {
				var win = node.ownerGlobal;
				if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
					? win.BrowserReloadSkipCache() : win.BrowserReload();
			}
		},
		maybeClosePopup(e, trg) {
			!e.shiftKey && prefs.getBoolPref(this.closePref, undefined)
				&& trg.parentNode.hidePopup();
		},
		eyedropper(trg) { // Пипетка - захват цвета
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm")
				.require("devtools/client/menus").menuitems
				.find(menuitem => menuitem.id == "menu_eyedropper");
			(this.eyedropper = target => obj.oncommand({target}))(trg);
		},
		auxclick(e) { // CKM
			if (e.button != 1 || !e.target.btn) return;
			e.view.ZoomManager.toggleZoom();
		},
		command(e) { // нажатия левой кнопки мыши
			var trg = e.target, win = e.view;
			if (trg.btn) { // LMB
				if (e.shiftKey) e.altKey
					? e.view.alert("Press Alt+Shift") // Alt+Shift
					: e.view.PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar"); // Shift Библиотека Панель закладок
				else if (e.altKey)
					this.eyedropper(trg); // Alt Пипетка
				else {  // LMB Click
					var bar = trg.ownerDocument.getElementById("add-additional-vertical-bar");
					if (bar) {
						win.setToolbarVisibility(bar, bar.collapsed);
						bar.collapsed ? win.SidebarUI.hide() : win.SidebarUI.show("viewHistorySidebar");
					} else
						win.SidebarUI.toggle("viewHistorySidebar");
				}
				return;
			}
			var menu = trg.closest("menu"), newVal = trg.val;
			this.maybeClosePopup(e, menu);
			if (newVal != menu.pref.val)
				menu.pref.set(menu.pref.pref, newVal),
				this.maybeRe(menu, true);
		},
		popupshowing(e, trg = e.target) {
			if (trg.state == "closed") return;
			if (trg.id) {
				for(var node of trg.children) {
					if (node.nodeName.endsWith("r")) continue;
					this.upd(node);
					!e && node.open && this.popupshowing(null, node.querySelector("menupopup"));
				}
				return;
			}
			var {pref} = trg.closest("menu"), findChecked = true;

			var findDef = "defVal" in pref;
			var checked = trg.querySelector("[checked]");
			if (checked) {
				if (checked.val == pref.val) {
					if (findDef) findChecked = false;
					else return;
				}
				else checked.removeAttribute("checked");
			}
			if (findDef) {
				var def = trg.querySelector("menuitem:not([style*=font-style]");
				if (def)
					if (def.val == pref.defVal) {
						if (findChecked) findDef = false;
						else return;
					}
					else def.style.setProperty("font-style", "italic", "important");
			}
			for(var node of trg.children) if ("val" in node) {
				if (findChecked && node.val == pref.val) {
					node.setAttribute("checked", true);
					if (findDef) findChecked = false;
					else break;
				}
				if (findDef && node.val == pref.defVal) {
					node.style.removeProperty("font-style");
					if (findChecked) findDef = false;
					else break;
				}
			}
		},
		contextmenu(e) { // RMB
			var trg = e.target, win = e.view;
			if (trg.btn) {
				if (e.ctrlKey || e.shiftKey) return;
				if (e.detail == 2) return trg.secondaryPopup.hidePopup(); // меню быстрых настроек
				! e.altKey ? this.openPopup(trg.secondaryPopup) : this.switchToTab("about:config", e);
			}
			else if ("pref" in trg) {
				this.maybeClosePopup(e, trg);
				if (trg.pref.user)
					prefs.clearUserPref(trg.pref.pref),
					this.maybeRe(trg);
			}
			e.preventDefault();
		},
		click(e) {
			if (e.button) return;
			var trg = e.target, {pref} = trg;
			if (!pref) return;
		},
		mousedown(e) {
			var reset = e => e.target.linkedObject = this;
			var id, lo = {command: reset, mousedown: reset, auxclick: e => e.button != 1 || reset(e)};
			var lin = /macos|linux/.test(e.view.AppConstants.platform);
			var stop = e => reset(e) && e.preventDefault();
			lo.contextmenu = lin
				? e => e.ctrlKey || e.shiftKey ? dsp(e) : stop(e) : stop;
			var context = lin
				? e => e.button == 2 && e.type.endsWith("p") && this.contextmenu(e) : () => {};
			var dsp = (e, timeout) => {
				var trg = e.target;
				trg.onmouseup = trg.onmouseleave = null;
				if (timeout) return this.londPress(e);
				e.view.clearTimeout(id);
				reset(e);
				context(e);
			}
			(this.mousedown = e => { var trg = e.target;
				if (!trg.btn) return;
				trg.linkedObject = lo;
				trg.onmouseup = trg.onmouseleave = dsp;
				id = e.view.setTimeout(dsp, 500, e, true);
			})(e);
		},
		showInStatusPanel(info, ms = 5000) {
			var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
			if (StatusPanel.update.tid)
				clearTimeout(StatusPanel.update.tid)
			else {
				var {update} = StatusPanel;
				StatusPanel.update = () => {};
				StatusPanel.update.ret = () => {
					StatusPanel.update = update;
					StatusPanel.update();
				}
			}
			StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, ms);
			StatusPanel._label = info;
		},
		Notify(title, text, ms = 3000){
			Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService).showAlertNotification(null, title, text, false, '', null, ms);
		},
		switchToTab(url, e = this) { // открыть вкладку | закрыть, если открыта
			for(var tab of e.view.gBrowser.tabs)
				if ( tab.linkedBrowser.currentURI.spec == url ) {e.view.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть
			e.view.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
		},
		switchProxy(e, pac) {
			if (prefs.getIntPref('network.proxy.type') == 2) { // выключить
				prefs.setIntPref('network.proxy.type', 0);
				prefs.setStringPref("network.proxy.autoconfig_url", "127.0.0.1");
				e.target.style.removeProperty("filter");
				this.showInStatusPanel("\u{1F6A6} Настройки сети - работа без прокси"); // символ Светофор
			} else {
				prefs.setIntPref('network.proxy.type', 2);
				prefs.setStringPref("network.proxy.autoconfig_url", pac);
				e.target.style.setProperty("filter", icon_vpn, "important");
				this.showInStatusPanel("\u{1F6A6} Заблокированные сайты через «АнтиЗапрет»");
				// this.Notify('Proxy', 'Работаем через VPN Антизапрет');
			}
		},
		londPress(e) { // удержание кнопки мыши. на второй долгий клик при отпускании сработает действие на обычный клик этой кнопки
			var trg = e.target, win = e.view;
			if (e.button == 0) this.switchProxy(e, my_vpn);
			if (e.button == 1) trg.ownerDocument.getElementById("key_browserConsole").doCommand(); // Консоль браузера
			if (e.button == 2) { // RMB Long
				var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
				(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(help_ucf[0], e) : this.switchToTab(help_ucf[1], e);
			}
		}
	};
}); // END ToggleAboutConfig

Отредактировано Dobrov (23-05-2021 17:38:22)

Отсутствует

 

№1534707-04-2021 21:50:41

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2247
UA: Firefox 68.0

Re: Custom Buttons

Stkvsky пишет

в контекстном меню папки закладок "Открыть всё в контейнере"

Для custom_script.js

скрытый текст

Выделить код

Код:

(async (sel, self) => ({
	init(topic) {
		Services.obs.addObserver(self = this, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(mm, topic);
			Services.obs.removeObserver(quit, t);
		}, "quit-application-granted");
	},
	observe(doc) {
		var list = doc.querySelectorAll(sel);
		list.length && new this.listener(Array.from(list));
	},
	listener: class {
		constructor(list) {
			var popup = this.popup = (this.list = list)[0].parentNode;
			popup.addEventListener("popupshowing", this);
			popup.ownerGlobal.addEventListener("unload",
				() => popup.removeEventListener("popupshowing", this)
			, {once: true});
		}
		get shouldHide() {
			return (this.ind = this.list.findIndex(this.find)) == -1;
		}
		find(node) {
			return !node.hidden && !node.disabled;
		}
		handleEvent() {
			if (this.shouldHide) return;
			var doc = this.popup.ownerDocument;
			var menu = this.menu = doc.createXULElement("menu");
			menu.setAttribute("label", "Открыть всё в контейнере");
			var popup = menu.appendChild(doc.createXULElement("menupopup"));
			popup.setAttribute("oncommand", "lst.cmd(event)");
			(this.sub = popup).lst = this;
			(this.list[1] || this.list[0]).after(menu);
			/\/browser\.x(?:u|htm)l$/.test(doc.documentURI)
				|| self.stylify(doc.ownerGlobal.windowUtils);
			this.handleEvent = self.popupshowing;
		}
		cmd(e) {
			self.redef(e.target.getAttribute("data-usercontextid"), e.view);
			this.list[this.ind].doCommand();
		}
	},
	redef(id, w) {
		var gbw = Cu.import("resource:///modules/PlacesUIUtils.jsm", {}).getBrowserWindow;
		(this.redef = (id, w) => {
			var gb = gbw(w).gBrowser, lt = gb.loadTabs;
			gb.loadTabs = (urls, opts) => {
				opts.userContextId = id;
				(gb.loadTabs = lt).call(gb, urls, opts);
			}
		})(id, w);
	},
	popupshowing(e) {
		if (e.target == this.popup) this.menu.hidden = this.shouldHide;
		else if (e.target == this.sub)
			e.stopImmediatePropagation(),
			e.view.createUserContextMenu(e, {isContextMenu: true});
	},
	stylify(wu) {
		var url = "chrome://browser/content/usercontext/usercontext.css";
		var css = Cu.readUTF8URI(Services.io.newURI(url));
		url = "data:text/css;charset=utf-8," + encodeURIComponent(
			css.replace(/list-style-image.+?\)/, "$& !important")
		);
		(this.stylify = wu => wu.loadSheetUsingURIString(url, wu.USER_SHEET))(wu);
	}
}).init("chrome-document-loaded"))(
	"#placesContext_openBookmarkContainer\\:tabs,#placesContext_openContainer\\:tabs"
);

Andrey_Krropotkin пишет

все связано с боковой панелью, не отображаются в боковой панели

«1.» там наворочено какой-то жути.
Вот, совсем не многим лучше, но это к тому, что webext-panels.xhtml там не нужен.

скрытый текст

Выделить код

Код:

((g, id) => {
	var name = id + "Helper";
	var obj = g[name] || (g[name] = ({
		wids: new Set(),
		url: `chrome://custombuttons/content/${id}.xhtml`,
		reg(code) {
			var muri = Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile));
			var ams = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
			(this.reg = code => {
				this.ts = Cu.now();
				var url = "data:application/xhtml+xml;charset=utf-8," + encodeURIComponent(this.code = code);
				this.helper = ams.registerChrome(muri, [["override", this.url, url]]);
			})(code);
		},
		init(btn) {
			this.reg(btn.Help);
			this.init = btn => {
				if (Cu.now() - this.ts < 500) return;
				var code = btn.Help;
				if (this.code == code) return;
				this.helper.destruct();
				Services.obs.notifyObservers(null, "chrome-flush-caches");
				this.reg(code);
			}
		}
	}));
	obj.init(this);

	window.gCBClipboardViewer = {toggleSidebar: () => SidebarUI.toggle(id)};

	var {url} = obj, winName = "clipview";
	var find = win => win.name == winName;

	this.onclick = e => {
		if (e.button == 1) return e.ctrlKey
			? gBrowser.selectedTab = gBrowser.addTrustedTab(url) : SidebarUI.toggle(id);
		if (e.button) return;
		var win = Array.from(Services.wm.getEnumerator(null)).find(find);
		if (win) return win.focus();
		openDialog(url, winName, "chrome,centerscreen,minimizable,resizable");
	}

	var label = this.label = "Clipboard Viewer";
	var icon = this.image;

	var e = (name, attrs, node, append) => {
		var elm = document.createXULElement(name);
		for(var a in attrs) elm.setAttribute(a, attrs[a]);
		append ? node.append(elm) : node.before(elm);
		return elm;
	}
	var menuitem = e("menuitem", {
		label,
		type: "checkbox",
		id: "menu_CBClipboardLoader",
		oncommand: `SidebarUI.toggle("${id}");`,
	}, document.getElementById("viewSidebarMenu"), true);

	var btn = e("toolbarbutton", {
		label,
		type: "checkbox",
		oncommand: `SidebarUI.show("${id}")`,
		id: "sidebar-switcher-CBClipboardLoader",
		class: "subviewbutton subviewbutton-iconic"
	}, document.querySelector('toolbarbutton[id^="sidebar-switcher-"] + toolbarseparator'));
	SidebarUI.isOpen && SidebarUI.currentID == id && btn.setAttribute("checked", true);

	SidebarUI.sidebars.set(id, {
		url,
		title: label,
		buttonId: btn.id,
		menuId: menuitem.id,
	});

	var info = [
		"ЛКМ: открыть в окне",
		"СКМ: открыть в Sidebar",
		"Ctrl+СКМ: открыть в новой вкладке"
	].join("\n\n");

	this.setAttribute("tooltip", "_child");
	var tooltip = this.appendChild(document.createXULElement("tooltip"));
	this.onmouseover = () => tooltip.label = gClipboard.read() || info;

	var css = `\
		#${_id} > tooltip {
			-moz-appearance: none;
			border: 1px solid black;
			max-width: none;
			background: #ebf1f9;
			color: black;
			font-family: monospace;
			opacity: 0.9;
			border-radius: 5px;
			font-size: 16px;
			padding: 4px 8px;
		}
		#${btn.id} > .toolbarbutton-icon,
		#sidebar-box[sidebarcommand="${id}"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
			width: 16px;
			height: 16px;
			opacity: 0.8;
			fill: currentColor;
			-moz-context-properties: fill;
			list-style-image: url(${icon});
		}`
		.replace(/;$/gm, " !important;");

	var str = "data:text/css," + encodeURIComponent(css), type = windowUtils.USER_SHEET;
	windowUtils.loadSheetUsingURIString(str, type);

	addDestructor(reason => {
		btn.remove(); menuitem.remove();
		SidebarUI.sidebars.delete(id);
		windowUtils.removeSheetUsingURIString(str, type);
		if (reason[5] == "e") obj.code = "";
	});

	var wid = window.docShell.outerWindowID;
	if (obj.wids.has(wid)) return;

	obj.wids.add(wid);
	var maybeReload = br => !br.isRemoteBrowser && br.currentURI.spec == url &&
		br.contentDocument.documentURI.startsWith("about:neterror?e=fileNotFound") && br.reload();
	for(var tab of gBrowser.tabs)
		tab.linkedPanel && !tab.closing && maybeReload(tab.linkedBrowser);
	var su = SidebarUI;
	su._box && su.lastOpenedId == id && (!su.isOpen || !su.browser.src) && su.show(id);

})(Cu.import("resource://gre/modules/Services.jsm", {}), "viewCBClipboardLoader");


А в «2.» — вторая строка битая, и код #TabCBSite
режет своим return'ом поперёк, неужели в конце разместить сложно было.


А по сути отвала, консоль ведь подсказывает, что не хватает
policy.browsingContextGroupId
Можно попробовать от CB подсунуть, если paxmod, иначе вписать id
от другого заведомо установленного и включённого WebExtensions.

скрытый текст

Выделить код

Код:

/*
    var config = {browserStyle: false, extension: {remote: false}};
*/
    var config = {browserStyle: false, extension: {remote: false, policy: {
        browsingContextGroupId: WebExtensionPolicy
            .getByID("custombuttons@xsms.org").browsingContextGroupId
    }}};

toxa пишет

А как поставить флаг в окно? И какие есть еще места, куда можно поставить флаг?

С помощью оператора присваивания, очевидно же.
Место, ну, например, объект custombuttons, чтоб окно не засорять.
А вообще листенеры удалять принято.

Отсутствует

 

№1534808-04-2021 03:36:51

Пострел
Участник
 
Группа: Members
Зарегистрирован: 08-04-2021
Сообщений: 51
UA: Firefox 87.0

Re: Custom Buttons

Здравствуйте. Можно ли адаптировать кнопку
"При повторном открытии боковой панели закладок, все открытые папки автоматически сворачиваются"
для 87 версии Fierfox?

скрытый текст

Выделить код

Код:

gBrowser.currentURI == "about:customizing" || (() => {

    var func = PlacesTreeView.prototype.toggleOpenState;
    func = eval("(" + String.replace(func, /\s+if \(!this._c([\s\S]+)}\s+}/, "") + ")");

    addEventListener("pageshow", e => {
        if (e.target.location != "chrome://browser/content/bookmarks/bookmarksPanel.xul") return;
        var view = e.target.getElementById("bookmarks-view").view;
        view.toggleOpenState = func.bind(view);

    }, false, document.getElementById("sidebar"));
})();

Отсутствует

 

№1534908-04-2021 04:50:55

Stkvsky
Участник
 
Группа: Members
Зарегистрирован: 26-06-2012
Сообщений: 1700
UA: Firefox 68.0

Re: Custom Buttons

Dumby пишет

Для custom_script.js

Прекрасно работает, спасибо
Блин извиняюсь, а можно две поправки внести?
Чтобы контейнер не выбирать из списка, а создавался новый?
И можно ли указать чтобы вкладки не загружались?

Отредактировано Stkvsky (08-04-2021 07:49:28)

Отсутствует

 

№1535008-04-2021 09:25:33

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 315
UA: Firefox 88.0

Re: Custom Buttons

Dumby
В 88 по-моему опять не работает кнопка по правому клику
https://forum.mozilla-russia.org/viewtopic.php?pid=789677#p789677
Посмотрите, пожалуйста.

Отредактировано Garalf (08-04-2021 18:37:49)

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]