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

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

№1557616-05-2021 08:08:26

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

Re: Custom Buttons

rubel - строка 82:

Выделить код

Код:

var savedir = PathUtils.join(await Downloads.getPreferredDownloadsDirectory(), "_Web", FatMs(Title(-1))); // каталог Загрузки + домен

Отсутствует

 

№1557716-05-2021 08:50:48

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

Dobrov
Исправил -- var savedir = PathUtils.join(await Downloads.getPreferredDownloadsDirectory(), );
Теперь сохраняет просто в "D:\Downloads" . Но вот с именами файлов беда. Например вот с этой страницы сохраняет название файла:
Mozilla Firefox 87.0 08.02.2021 _ Cento8 Название программы_ Firefox Версия программы_ 87.0 Разработ_16.05.2021-09։41։18 Чушь какая-то. Как сделать чтоб было нормально, с выбором названия файла ?

Отсутствует

 

№1557816-05-2021 09:47:20

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

Re: Custom Buttons

rubel пишет

Как сделать чтоб было нормально, с выбором названия файла ?

Удобство этого скрипта именно в том, что нет навязчивого запроса имени файла! Имя файла равно заголовку вкладки, это задано в последних строках.
Что-то ты не так исправил, у меня с этой ссылки сохраняет как: Mozilla Firefox 87.0 _ effect ∞_16.05.2021-14։42։47.html

Отсутствует

 

№1557916-05-2021 11:00:48

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

Dobrov пишет

Удобство этого скрипта именно в том, что нет навязчивого запроса имени файла!

Ну кому как, а мне удобно присваивать имена файлов как я хочу.... Все равно спасибо.
Уважаемые гуру Dobrov Andrey_Krropotkin Dumby Vitaliy V.
сделайте, пожалуйста, кнопку Save snapshot to html для UCF.

Отсутствует

 

№1558016-05-2021 20:32:56

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

Re: Custom Buttons

Dumby
Как бы на "отмена" код  SidebarUI.hide(); сработал ?

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

Выделить код

Код:

/*Initialization Code*/

this.onclick = this.oncontextmenu = function(event) {
if (event.button == 1) {
SidebarUI.toggle('viewHistorySidebar'); 
 if (custombuttons.confirmBox(null, "Вниманиее ! Очистка Истории!", "Да", "Отмена") ) {

PlacesUtils.history.clear();
SidebarUI.hide();

}
}

 if(event.button == 0 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){
SidebarUI.toggle('viewHistorySidebar');
}
     
if(event.button == 2 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){
Sanitizer.showUI(window);   
CustomizableUI.setToolbarVisibility("PersonalToolbar", document.querySelector("#PersonalToolbar").closed);
 var s = "browser.zoom.full";
      cbu.setPrefs(s, cbu.getPrefs(s) == true ? true : true); 
 var s = "intl.accept_languages";
 cbu.setPrefs(s, cbu.getPrefs(s) == "ru" ? "ru": "ru");

  var s = "media.autoplay.default";
 cbu.setPrefs(s, cbu.getPrefs(s) == 5 ? 5: 5);
SidebarUI.hide();
    var s = "extensions.long_left_click.timeContent";
     cbu.setPrefs(s, cbu.getPrefs(s) == 350 ? 350: 350);
document.querySelector(
    "#mainPopupSet > tooltip[onpopupshowing*=undoCloseTabsList]"
)?.undoCloseTabsList.updUI();
             

}
};

this.oncontextmenu =e=> { e.button && !e.ctrlKey && e.preventDefault() };

this.tooltipText = "ЛКМ: Боковая история\nСКМ: Очистка Истории \nПКМ: Окно очистки всего";

Отсутствует

 

№1558116-05-2021 22:09:52

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

Re: Custom Buttons

Dobrov пишет

и тогда брать заголовок из label-тэга текущей страницы

Жаль, что слова «в другом процессе» — пустой звук.
Если title страницы Text Linky Tool не ценность, то можно так попробовать

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

Выделить код

Код:

/*
				this.drag("add");
		},
*/
				this.drag("add"),
				this.checkTLT();
		},
		checkTLT() {
			var tltHost = WebExtensionPolicy.getByID(
				"{3efee51a-7d3b-4412-a889-addc6ff6276b}"
			)?.mozExtensionHostname;
			tltHost && (this.checkTLT = () =>
				gBrowser.selectedTab.label.startsWith(tltHost) &&
				gBrowser.selectedBrowser.messageManager.loadFrameScript(this.tltFs, false)
			)();
		},
		get tltFs() {
			delete this.tltFs;
			return this.tltFs = "data:;charset=utf-8,(" + encodeURIComponent(doc => {
				var tc = doc.getElementById("lblFrom").textContent;
				doc.title = tc.slice(0, tc.lastIndexOf("("));
			}) + ")(content.document)";
		},

Первый работает в custom_script_win.js , а Второй почему-то только в кнопке CustomButton.
Нужно устранить три недостатка

Потому что первый сделан под ucf, а второй под CB.
И не «устранить три недостатка», а «добавить три хотелки».

UPD: bugzil.la/1706479 FF90+

Выделить код

Код:

(async (id, sel) => {
	var g = Cu.getGlobalForObject(Cu), stt = g[id];
	if (!stt) {
		var {obs, prefs} = Services, {bookmarks: bm, observers: pobs} = PlacesUtils;
		stt = g[id] = {
			bm,
			pref: `ucf.${id}Guid`,
			events: ["bookmark-added"],
			async init() {
				this.handleEvent = e => this[e.type](e);

				if ((this.pbm = typeof PlacesBookmarkMoved == "function"))
					this.events.push("bookmark-moved");
				else
					this.QueryInterface = g.ChromeUtils.generateQI([Ci.nsINavBookmarkObserver]),
					bm.addObserver(this);

				pobs.addListener(this.events, this.added = events => {
					for(var e of events) e.isTagging || this[e.constructor.name](e);
				});
				obs.addObserver(this, "quit-application-granted");
				this.args = [b => this.bguids.add(b.parentGuid), {concurrent: true}];
				var guid = prefs.getStringPref(this.pref, "");
				if (!guid) try {var [guid] = await PlacesUtils.metadata.get(
					PlacesUIUtils.LAST_USED_FOLDERS_META_KEY, []
				)} catch {}
				this.guids.push(guid || await PlacesUIUtils.defaultParentGuid || bm.unfiledGuid);
			},
			observe() {
				this.pbm || bm.removeObserver(this);
				pobs.removeListener(this.events, this.added);
				obs.removeObserver(this, "quit-application-granted");
				prefs.setStringPref(this.pref, this.guids[0]);
			},
			skipTags: true,
			bguids: new g.Set(),
			guids: new g.Array(),
			PlacesBookmarkAddition(e) {
				if (e.itemType == bm.TYPE_BOOKMARK && e.source == bm.SOURCES.DEFAULT)
					this.guids[0] = e.parentGuid;
			},
			PlacesBookmarkMoved(e) {
				e.parentGuid != e.oldParentGuid && this.PlacesBookmarkAddition(e);
			},
			onItemMoved(a, b, c, d, e, itemType, f, oldParentGuid, parentGuid, source) {
				this.PlacesBookmarkMoved({itemType, source, oldParentGuid, parentGuid});
			},
			fetch(win) {
				this.bguids.clear();
				return bm.fetch({url: win.gBrowser.currentURI.spec}, ...this.args);
			},
			tt(win) {
				var list = win.InspectorUtils
					.getChildrenForNode(win.document.documentElement, true);
				return list.item(list.length - 1);
			},
			find: obj => obj.name == "tooltiptext",
			opts: {
				relatedToCurrent: true,
				triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
			},
			loadURI_ex(win, url, newtab) {newtab
				? win.switchToTabHavingURI(url, true, this.opts)
				: win.gBrowser.loadURI(url, this.opts);
			},
			auxclick(e) {
				if (e.button != 1) return;
				var win = e.view;
				if (e.shiftKey) this.loadURI_ex(win, "https://www.google.ru", true);
				else if (e.altKey) this.loadURI_ex(win, "about:newtab");
				else win.undoCloseTab();
			}	
		};
		var ps = ["onBeginUpdateBatch", "onEndUpdateBatch", "onItemChanged", "onItemVisited"];
		var noop = () => {}; for(var p of ps) stt[p] = noop; stt.init();

		var func = id => this[id].mouseenter = async function(e) {
			var win = e.view;
			var star = e.target;
			star.tooltipText = "\u3164";
			var starred = win.BookmarkingUI.status == win.BookmarkingUI.STATUS_STARRED;
			starred && await this.fetch(win);
			var result = [];
			for(var guid of (starred ? this.bguids : this.guids)) {
				var arr = [], num = 50;
				while(--num) {
					if (!star.matches(":hover")) return;
					var res = await this.bm.fetch(guid);
					if (!res) break;
					if ((guid = res.parentGuid) == this.bm.rootGuid) {
						arr.unshift(this.bm.getLocalizedTitle(res));
						break;
					}
					arr.unshift(res.title || "[Безымянная папка]");
				}
				arr.length && result.push(arr.join("\\"));
			}
			if (!star.matches(":hover")) return;

			var text = (await win.document.l10n.formatMessages([{
				id: star.getAttribute("data-l10n-id"),
				args: JSON.parse(star.getAttribute("data-l10n-args"))
			}]))[0].attributes.find(this.find).value;
	
			if (result.length) {
				var txt = result.join("\n");
				if (starred) {
					var m = result.length > 1;
					txt = `Адрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${txt}`;
				} else
					txt = "Недавняя папка:\n" + txt;
				text += "\n\n" + txt;
			}
			win.document.tooltipNode == star
				? this.tt(win).label = text : star.tooltipText = text;
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})("${id}")`);
		g.ChromeUtils.compileScript(url).then(ps => ps.executeInGlobal(g));
	}
	await delayedStartupPromise;
	var stars = Array.from(document.querySelectorAll(sel));
	for(var star of stars)
		star.addEventListener("auxclick", stt),
		star.addEventListener("mouseenter", stt);
		
	var destructor = () => {
		for(var star of stars)
			star.removeEventListener("auxclick", stt),
			star.removeEventListener("mouseenter", stt);
	}
	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	if (ucf)
		ucf[id] = {destructor},
		ucf.unloadlisteners.push(id);
	else
		window.addEventListener("unload", destructor, {once: true});
})("ucfBookmarksStarFTooltipHelper", ":is(#star-button, #star-button-box)[role], #context-bookmarkpage");

rubel пишет

сделайте, пожалуйста, кнопку Save snapshot to html для UCF.

Попробую JSM'кой. Создать AppMenuTbbSaveHTMLChild.jsm
и подключть в custom_script.js (путь свой).

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

Выделить код

Код:

(async url => ChromeUtils.import(url))(
	"chrome://user_chrome_files/content/custom_scripts/Actors/AppMenuTbbSaveHTMLChild.jsm"
);


AppMenuTbbSaveHTMLChild.jsm

Выделить код

Код:

var self, name = "AppMenuTbbSaveHTML", EXPORTED_SYMBOLS = [name + "Child"];
var {io, focus, obs} = globalThis.Services ||
	ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

class AppMenuTbbSaveHTMLChild extends JSWindowActorChild {
	receiveMessage() {
		return htmlAndName(this.contentWindow);
	}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {moduleURI: __URI__},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
	},
	observe(win) {
		win.document.getElementById("appMenu-popup")
			.addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.className = "subviewbutton subviewbutton-iconic";
		btn.setAttribute("label", "Сохранить страницу или выбранное как HTML");
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII=");
		btn.setAttribute("oncommand", "saveHTML();");
		btn.saveHTML = this.saveHTML;
		popup.querySelector('toolbarbutton[id^="appMenu-print-button"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup")
			.removeEventListener("popupshowing", this);
	},
	async saveHTML() {
		var win = this.ownerGlobal;
		var br = win.gBrowser.selectedBrowser;
		var bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;

		var actor = bc?.currentWindowGlobal?.getActor(name);
		actor && self.save(win, ...await actor.sendQuery(""));
	},
	async save(win, fileContent, fileName) {
		var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
		fp.init(win, "", fp.modeSave);
		fp.defaultString = fileName;
		fp.appendFilters(fp.filterHTML);
		fp.appendFilters(fp.filterAll);
		var res = await new Promise(fp.open);
		if (res == fp.returnOK || res == fp.returnReplace)
			this.write(fp.file.path, fileContent);
	},
	write(path, html) {
		if (typeof IOUtils != "object") {
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
		}
		(this.write = IOUtils.writeUTF8 || IOUtils.writeAtomicUTF8)(path, html);
	}
	
}).init("browser-delayed-startup-finished");

var htmlAndName = async mainWin => {

	var resolveURL = function (url, base) {
		try {
			return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	};

	var getSelWin = function (w) {
		if (w.getSelection().toString()) return w;
		for (var i = 0, f, r; f = w.frames[i]; i++) {
			try {
				if (r = getSelWin(f)) return r;
			} catch(e) {}
		}
	};

	var encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else {
				img = obj;
			};
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	};
	var toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};

		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	};

	var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location;
	var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g;

	if (selWin) {
		var rng = win.getSelection().getRangeAt(0);
		pEle = rng.commonAncestorContainer;
		ele = rng.cloneContents();
	} else {
		pEle = doc.documentElement;
		ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true);
	};
	while (pEle) {
		if (pEle.nodeType == 1) {
			clone = pEle.cloneNode(false);
			clone.appendChild(ele);
			ele = clone;
		};
		pEle = pEle.parentNode
	};
	var sel = doc.createElement('div');
	sel.appendChild(ele);

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a': 
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};

	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild);
	var meta = doc.createElement('meta');
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	var title = doc.getElementsByTagName('title')[0];
	if (title) head.appendChild(title.cloneNode(true));

	head.copyScript = function (unsafeWin) {
		if ('$' in unsafeWin) return;
		var f = doc.createElement('iframe');
		f.src = 'about:blank';
		f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');
		doc.documentElement.appendChild(f);
		var str, script = doc.createElement('script');
		script.type = 'text/javascript';
		for (var name in unsafeWin) {
			if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue;
			try {
				str = toSrc(unsafeWin[name]);
				if (!/\{\s*\[native code\]\s*\}/.test(str)) {
					script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n'));
				}
			} catch (e) {};
		};
		f.parentNode.removeChild(f);
		if (script.childNodes.length) this.nextSibling.appendChild(script);
	};
	head.copyScript(win.wrappedJSObject || win);

	head.copyStyle = function (s) {
		if (!s) return;
		var style = doc.createElement('style');
		style.type = 'text/css';
		if (s.media && s.media.mediaText) style.media = s.media.mediaText;
		try {
			for (var i = 0, rule; rule = s.cssRules[i]; i++) {
				if (rule.type != 3) {
					if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) {
						var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) {
							if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href);
							if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url);
							return prev + url + next;
						});
						style.appendChild(doc.createTextNode(css + '\n'));
					}
				} else {
					this.copyStyle(rule.styleSheet);
				}
			}
		} catch(e) {
			if (s.ownerNode) style = s.ownerNode.cloneNode(false);
		};
		this.appendChild(style);
	};
	var sheets = doc.styleSheets;
	for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]);
	head.appendChild(doc.createTextNode('\n'));

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, '');
	fileName += (function () {
		var d = new Date(), z = function(n){return '_' + (n < 10 ? '0' : '') + n};
		return z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds());
	})();
	if(!/\.html?$/.test(fileName))fileName += '.html';

	return [doctype + sel.innerHTML + '\n<!-- This document saved from ' + (loc.protocol != 'data:' ? loc.href : 'data:uri') + ' -->', fileName];
}

ВВП пишет

Как бы на "отмена" код  SidebarUI.hide(); сработал ?

Скобку «}», которая идёт после SidebarUI.hide();
переставить так, чтобы она шла перед SidebarUI.hide();

Отредактировано Dumby (23-05-2021 17:53:01)

Отсутствует

 

№1558216-05-2021 22:51:23

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

Re: Custom Buttons

Dumby

Dumby пишет

Скобку «}», которая идёт после SidebarUI.hide();
переставить так, чтобы она шла перед SidebarUI.hide();

Класс!

Отсутствует

 

№1558317-05-2021 04:51:13

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

Dumby пишет

Попробую JSM'кой. Создать AppMenuTbbSaveHTMLChild.jsm
и подключть в custom_script.js (путь свой).

Все сделал по вашей методе, но меню  не появилось.

Отсутствует

 

№1558417-05-2021 06:02:37

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

Re: Custom Buttons

Dumby Спасибо! Скрипты для Звёздочки и Загрузки картинки отличные! :beer: (я лишь добавил обрезку имён вкладок)


Ещё просьба [финальная :)]: скрипт Загрузки картинки перетаскивание вправо с изменениями не всегда может сохранить картинку.
Некоторые изображения нельзя тащить, например Cover Гости из прошлого и там же фотки актрис.
Убрал стилем мешающий сохранению div на данном сайте, клик по обложке откроет Слайд-шоу на всё окно, но перетаскивание на картинке не работает.


Dumby - нельзя ли в скрипт "Сохранить картинку перетаскиванием" добавить ещё сохранение картинки по событию двойной click или click+Ctrl или клик колёсиком мыши (это в строке events: ["dragover", "drop", "dragend"],) ???
А лучше сделать отдельный скрипт, который может по кликам сохранить картинку или скрытую некликабельную картинку, которая под курсором.

Выделить код

Код:

@-moz-document domain(doramatv.live), domain(mose.live) { /* позволяет сохранять картинки */
  .fotorama__fullscreen-icon { max-height: 300px !important; max-width: 300px !important;}
}
rubel пишет

Все сделал по вашей методе, но меню  не появилось.

AppMenuTbbSaveHTMLChild.jsm - Всё работает, строка "Сохранить страницу или выбранное как HTML" есть в меню, которое на кнопке Firefox.

Отредактировано Dobrov (17-05-2021 08:13:59)

Отсутствует

 

№1558517-05-2021 08:44:51

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

Dobrov, Dumby
Помогите, пожалуйста.
Вот мои действия:
1.Создаю файл AppMenuTbbSaveHTMLChild.jsm
2.помещаю его в папку custom_scripts
3.в файл custom_script.js в самый верх добавляю

Выделить код

Код:

(async url => ChromeUtils.import(url))(
	"chrome://user_chrome_files/content/custom_scripts/AppMenuTbbSaveHTMLChild.jsm"
);

Получается вот так:

Выделить код

Код:

(async url => ChromeUtils.import(url))(
	"chrome://user_chrome_files/content/custom_scripts/AppMenuTbbSaveHTMLChild.jsm"
);

(() => {
    var loadscript = name => {
        try {
            Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`, globalThis, "UTF-8");
        } catch(e) {}
    };
    loadscript("cs/Icons in Sidebar.js");
    loadscript("cs/extension_manager_button.js");
	loadscript("cs/closeothertabs.uc.js");
	loadscript("cs/BBCode-Multi.js");
	loadscript("cs/undo_tab.js");
	loadscript("cs/download_and_switch_proxy_buttons.js");
	loadscript("cs/Ram.js");
	loadscript("cs/Nightly.js");
	
    // и т. д.
})();

Удаляю полностью папку startupCache, запускаю браузер и ничего не появилось в меню, которое на кнопке Firefox.
Что у меня не так ?

Отредактировано rubel (17-05-2021 13:41:10)

Отсутствует

 

№1558617-05-2021 12:19:15

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

Re: Custom Buttons

del

Отредактировано ВВП (17-05-2021 17:15:24)

Отсутствует

 

№1558717-05-2021 15:14:33

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

Re: Custom Buttons

rubel пишет

и ничего не появилось в меню

Аналогично, у меня тоже "не взлетело".

Отсутствует

 

№1558817-05-2021 15:48:51

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

Re: Custom Buttons

rubel, xrun1 винды у меня нет уже 4 года, проверял только на Linux и MacOS. в Firefox 87 всё работает.
Может кэш удалить правым кликом по штатной UCF-кнопке "Перезапустить приложение"?
Или нужен config.js, в котором есть bootstrap поддержка для CustomButtons.

custom_script.js

Выделить код

Код:

// Этот скрипт можно использовать для создания кнопок с помощью CustomizableUI.createWidget

(() => { // загрузка внешних скриптов
	var loadscript = name => {
		try {
			Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`,globalThis,"UTF-8");
			return true;
		} catch(e) {}
	};
	loadscript("ucf_FavDir.js");

})();

(async url => ChromeUtils.import(url))(
	"chrome://user_chrome_files/content/custom_scripts/AppMenuTbbSaveHTMLChild.jsm"
);

Отредактировано Dobrov (17-05-2021 16:40:29)

Отсутствует

 

№1558917-05-2021 16:47:54

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

Dobrov
Нет не работает и с вашим config.js, в котором есть bootstrap. Windows 10.

Отсутствует

 

№1559017-05-2021 17:51:23

sandro79
Участник
 
Группа: Members
Откуда: Ставрополье
Зарегистрирован: 15-11-2017
Сообщений: 1512
UA: Firefox 78.0

Re: Custom Buttons

xrun1 пишет

Аналогично, у меня тоже "не взлетело"

А у меня вспорхнуло :D added: Меню шире стало, но это поправимо.

скрытый текст
a0dff1fc72c6.png

rubel пишет

3.в файл custom_script.js в самый верх добавляю

Только я вниз добавил. Путь такой же как у вас. Сохранение работает.
Подключал к самой адекватной в 8-ой линейке [firefox] 86. ОС [windows] 7, но вряд ли это критично. config.js обычный из user_chrome_files, без bootstrap.

скрытый текст
8cb17e39669b.png
В 78 не пошло, но видимо и не должно было.


В 90 [nightly] тоже работает

скрытый текст
4912a8d95de4.png

Отредактировано sandro79 (17-05-2021 18:56:34)

Отсутствует

 

№1559117-05-2021 18:56:43

rubel
Участник
 
Группа: Members
Откуда: г.Самара
Зарегистрирован: 10-05-2005
Сообщений: 489
UA: Firefox 86.0

Re: Custom Buttons

sandro79
Спасибо за помощь, все сделал по-твоему, поместил в самый низ ну и кодировка была ANSI у фйла AppMenuTbbSaveHTMLChild.jsm.
Теперь все работает как положено, все сохраняет и стили и картинки. :)
Dumby
Спасибо за вашу работу. :beer:

Отсутствует

 

№1559218-05-2021 00:02:22

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

Re: Custom Buttons

sandro79
Я ca8123620e2d9c2d1190a1f42dc829ef.gif! Искал в меню по ПКМ на странице.

Отсутствует

 

№1559318-05-2021 14:57:07

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

Re: Custom Buttons

Dumby
Имею код , но если боковуха уже открыта , то при ЛКМ она сходу закрывается...Как бы избежать?

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

Выделить код

Код:

/*Initialization Code*/

this.onclick = this.oncontextmenu = function(event) {
if (event.button == 0) {
SidebarUI.toggle('viewHistorySidebar'); 
 if (custombuttons.confirmBox(null, "Вниманиее ! Очистка Истории!", "Да", "Отмена") ) {
SidebarUI.toggle('viewHistorySidebar');
PlacesUtils.history.clear();
}
SidebarUI.hide();

}

 if(event.button == 1 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){
SidebarUI.toggle('viewHistorySidebar');
}
     
if(event.button == 2 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){
Sanitizer.showUI(window);   
CustomizableUI.setToolbarVisibility("PersonalToolbar", document.querySelector("#PersonalToolbar").closed);
 var s = "browser.zoom.full";
      cbu.setPrefs(s, cbu.getPrefs(s) == true ? true : true); 
 var s = "intl.accept_languages";
 cbu.setPrefs(s, cbu.getPrefs(s) == "ru" ? "ru": "ru");

  var s = "media.autoplay.default";
 cbu.setPrefs(s, cbu.getPrefs(s) == 5 ? 5: 5);
SidebarUI.hide();
    var s = "extensions.long_left_click.timeContent";
     cbu.setPrefs(s, cbu.getPrefs(s) == 350 ? 350: 350);
document.querySelector(
    "#mainPopupSet > tooltip[onpopupshowing*=undoCloseTabsList]"
)?.undoCloseTabsList.updUI();
             

}
};

this.oncontextmenu =e=> { e.button && !e.ctrlKey && e.preventDefault() };

this.tooltipText = "ЛКМ: Очистка Истории\nСКМ: Боковая история \nПКМ: Окно очистки всего";

Отсутствует

 

№1559418-05-2021 17:31:38

kazarin
Участник
 
Группа: Members
Зарегистрирован: 23-11-2016
Сообщений: 24
UA: unknown 0.0

Re: Custom Buttons

Dobrov пишет

Не в интересе дело, а в том, что расширение Image Picka не подгружает картинки, по которым не кликали, то есть оно бесполезно!

Список url-ов же. Меняете p на o (в ссылках типа https://static.doramatv.live/uploads/pics/04/88/836_p.jpg) и кидаете в любой даунлоадер.

Отсутствует

 

№1559518-05-2021 17:37:50

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

Re: Custom Buttons

Dobrov пишет

нельзя ли в скрипт "Сохранить картинку перетаскиванием" добавить ещё сохранение картинки по событию двойной click

Нет, добавить нельзя. Объяснял же — нету в этом процессе
никакой картинки и никакого события клика на ней.


Ладно, уговорил попробовать переписать с двойным click'ом. Тоже JSM'ка,
назвать как-нибудь, например, MouseImgSaver.jsm и импортировать из custom_script.js
Предыдущий код отключить не забудь.

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

Выделить код

Код:

var EXPORTED_SYMBOLS = ["MouseImgSaverChild", "MouseImgSaverParent"];

var u = {get it() {
		delete this.it;
		return this.it = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
}};
for(let name of ["E10SUtils", "PrivateBrowsingUtils"])
	ChromeUtils.defineModuleGetter(u, name, `resource://gre/modules/${name}.jsm`);

class MouseImgSaverChild extends JSWindowActorChild {
	handleEvent(e) {
		if (e.button) return;
		var trg = e.explicitOriginalTarget;
		trg.nodeType == Node.ELEMENT_NODE
		&& trg instanceof Ci.nsIImageLoadingContent
		&& this[e.type](trg, e);
	}
	handleDragEvent(e) {
		this[e.type](e);
	}
	dragstart(trg, e) {
		this.trg = trg;
		this.x = e.screenX;
		this.y = e.screenY;
		this.drag("add");
		this.handleEvent = this.handleDragEvent;
		this.checkTextLinkyTool(trg.ownerDocument);
	}
	events = ["dragover", "drop", "dragend"];
	drag(meth = (delete this.handleEvent, delete this.trg, "remove")) {
		meth += "EventListener";
		var win = this.contentWindow;
		for(var type of this.events) win[meth](type, this, true);
	}
	drop() {
		this.drag();
	}
	dragover(e) {
		var {x, y} = this,
			cx = e.screenX, cy = e.screenY,
			dx = cx - x,
			ax = Math.abs(dx), ay = Math.abs(cy - y);

		if (ax < 10 && ay < 10) return;
		if (dx < 0 || ax < ay) return this.drag();
		this.x = cx; this.y = cy;
	}
	dragend(e) {
		var dt = e.dataTransfer, {trg} = this;
		this.drag();
		dt.mozUserCancelled || this.send(trg, e.screenX);
	}
	dblclick(trg) {
		trg.matches(":any-link :scope") || this.send(trg);
	}
	send(trg, sx) {
		var uri = trg.currentURI;
		if (!uri) return;

		var doc = trg.ownerDocument;
		var cookieJarSettings = u.E10SUtils
			.serializeCookieJarSettings(doc.cookieJarSettings);

		var referrerInfo = Cc["@mozilla.org/referrer-info;1"]
			.createInstance(Ci.nsIReferrerInfo);
		referrerInfo.initWithElement(trg);
		referrerInfo = u.E10SUtils.serializeReferrerInfo(referrerInfo);

		var contentType = null, contentDisposition = null;
		try {
			var props = u.it.getImgCacheForDocument(doc).findEntryProperties(uri, doc);
			var cs = Ci.nsISupportsCString;
			try {contentType = props.get("type", cs).data;} catch {}
			try {contentDisposition = props.get("content-disposition", cs).data;} catch {}
		} catch {}

		this.sendAsyncMessage("", {
			url: (trg.currentRequestFinalURI || uri).spec,
			contentType, referrerInfo, cookieJarSettings, contentDisposition, sx,
			isPrivate: u.PrivateBrowsingUtils.isContentWindowPrivate(trg.ownerGlobal)
		});
	}
	checkTextLinkyTool(doc) {
		if (doc.title || !doc.documentURI.startsWith("moz-extension:")) return;
		var lab = doc.querySelector("body > label#lblFrom:first-child")?.textContent;
		if (lab) doc.title = lab.slice(0, lab.lastIndexOf("("));
	}
}
if (!ChromeUtils.domProcessChild.childID) {
	ChromeUtils.registerWindowActor("MouseImgSaver", {
		allFrames: true,
		parent: {moduleURI: __URI__},
		messageManagerGroups: ["browsers"],
		child: {moduleURI: __URI__, events: {dblclick: {capture: true}, dragstart: {capture: true}}}
	});
	var wref, data = Object.assign(Object.create(null), {
		"browser.download.dir": {type: "String", get set() {

			var dir = wref.get().Services.dirsvc.get("DfltDwnld", Ci.nsIFile);
			dir.append("_Images");
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать папку, если не существует…
			var {path} = dir; // папка [Загрузки]/_Images

			Object.defineProperty(this, "set", {get() {
				var win = wref.get();
				var title = win.gBrowser.selectedTab.label.slice(0, 100);
				return PathUtils.join(path, win.DownloadPaths.sanitize(title));
			}});
			return this.set;
		}},
		"browser.download.folderList": {type: "Int", set: 2},
		"browser.download.useDownloadDir": {type: "Bool", set: true}
	});
	var MouseImgSaverParent = class extends JSWindowActorParent {
		receiveMessage(msg) {
			var {url, contentType, contentDisposition, sx,
				isPrivate, referrerInfo, cookieJarSettings} = msg.data;

			var win = msg.target.browsingContext.topChromeWindow;
			if (sx && sx > win.mozInnerScreenX + win.innerWidth) return;

			wref = Cu.getWeakReference(win);
			var p = win.Services.prefs;

			for(var pref in data) {
				var obj = data[pref], meth = `et${obj.type}Pref`;
				obj.val = p.prefHasUserValue(pref) ? p["g" + meth](pref) : null;
				p["s" + meth](pref, obj.set);
			}
			try {win.internalSave(
				url,
				null, // document
				null, // file name
				contentDisposition,
				contentType,
				false, // do not bypass the cache
				null, // filepicker title key
				null, // chosen data
				u.E10SUtils.deserializeReferrerInfo(referrerInfo),
				u.E10SUtils.deserializeCookieJarSettings(cookieJarSettings),
				win.document, // initiating doc
				true, // skip prompt for where to save
				null, // cache key
				isPrivate,
				win.document.nodePrincipal
			);}
			finally {
				for(var pref in data) data[pref].val === null
					? p.clearUserPref(pref)
					: p[`set${data[pref].type}Pref`](pref, data[pref].val);
			}
		}
	}
}

ВВП пишет

если боковуха уже открыта , то при ЛКМ она сходу закрывается...Как бы избежать?

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

Выделить код

Код:

this.onclick = event => {
	if (event.button == 0) {
		var id = "viewHistorySidebar";
		SidebarUI.isOpen && SidebarUI.currentID == id || SidebarUI.show(id);
		custombuttons.confirmBox(null, "Вниманиее ! Очистка Истории!", "Да", "Отмена")
			&& PlacesUtils.history.clear();
		SidebarUI.hide();
	}
	.....
	.....
	.....
}

Отредактировано Dumby (18-05-2021 20:48:16)

Отсутствует

 

№1559618-05-2021 19:39:25

_zt
Участник
 
Группа: Members
Зарегистрирован: 10-11-2014
Сообщений: 905
UA: Firefox 78.0

Re: Custom Buttons

Dumby
Этот код в 78 должен работать?


Fx 91 esr

Отсутствует

 

№1559718-05-2021 20:39:22

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

Re: Custom Buttons

_zt пишет

Этот код в 78 должен работать?

Думаю да. Добавил в 78 в custom_script_win.js — выглядит рабочим.
Только исключение тэгов для "bookmark-added" потерялось. Можно вписать.

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

Выделить код

Код:

/*
					for(var e of events) this.record(e.itemType, e.source, e.parentGuid);
*/
					for(var e of events) e.isTagging || this.record(e.itemType, e.source, e.parentGuid);

Отсутствует

 

№1559818-05-2021 23:13:48

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

Re: Custom Buttons

Dumby

Dumby пишет

если боковуха уже открыта , то при ЛКМ она сходу закрывается...Как бы избежать?

Тонкий ход. Благодарю.

Отсутствует

 

№1559919-05-2021 03:08:18

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

Re: Custom Buttons

Dumby пишет

сохранение картинки … с двойным click'ом.

Спасибо, отличная работа!
Но есть вопрос по этому скрипту MouseImgSaver.jsm - как картинку сохранять по клику колёсиком? (вместо dblclick)
Я добавлял события mousedown, click, auxclick - все они не реагируют на клик средней кнопки мыши!
строка 100: child: {moduleURI: __URI__, events: {mousedown: {capture: true}…


Dumby Вторая просьба доработать кнопку переключения параметров Quick Toggle about:config.
Плюс этой кнопки в том, что по иконкам меню сразу видно значение и "безопасность" опций.
Нужно добавить подменю в Quick Toggle about:config, чтобы строки переключения опций можно было объединять по категориям в одном под-меню (т.к. меня этих опций более тридцати, многих из которых нет в настройках браузера).


Ещё в кнопке пункты меню без иконок, если не указаны userChoice или userAlt. Я добавил для таких строк иконку, но есть баг: строка "Закрывать меню этой кнопки" свою иконку меняет только после переключения опций!
код новой иконки: 367 строка. Нужно исправить баг: при первом открытии последняя строка меню не с родной иконкой.

Выделить код

Код:

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

(async (name, id, func) => { // https://forum.mozilla-russia.org/viewtopic.php?pid=784165#p784165
	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);	// (this.constructor… {код кнопки}); BEGIN QuickToggle…
})(this.constructor.name, "ToggleAboutConfig", () => { var description =

`Quick Toggle Settings

ПКМ	Меню быстрых настроек
…+Alt	Отладка дополнений
…Long	✍ about:config
ЛКМ	Боковая панель: Журнал
…Long	Антизапрет proxy ⬄
…+Shift	★ Библиотека закладок
…+Alt	Пипетка: захват цвета
СКМ	± Zoom Текст/Страница
…Long	Консоль браузера

— тире ⟳ Обновить ↯ Перезапуск`, // ЛКМ+Alt+⇧ этот hotkey свободен

// 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
// Разделитель: Имя меню "—,⟳,↯" Опция, ⟳ обновить страницу, ↯ перезапуск браузера
// стиль иконки: #ToggleAboutConfig .toolbarbutton-icon{ padding: 2px !important;}

	icon_vpn = "hue-rotate(270deg) brightness(95%)", menuactive = '#e8e8e8', // текст, подсвеченный курсором
	{prefs} = Services, db = prefs.getDefaultBranch(""), pv = parseInt(Services.appinfo.platformVersion),
	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] : [name, name]; });
	},
	fontserif = font_pref(fonts), fontsans = [["PT Sans","PT Sans"], ...fontserif],

	secondary = [{ // pref … [apref, lab, akey, hint, js-code]
			pref: ["permissions.default.image", "Загрузка графики"], userChoice: 1, userAlt: 3, refresh: true,
			values: [[1, "Разрешена"], [3, "Только с сайта"], [2, "Отключить"]]
	},{
			pref: ["browser.safebrowsing.downloads.remote.block_dangerous", "Опасные файлы, сайты"], 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: ["browser.display.use_document_fonts", "Загружать Web-шрифты"], userChoice: 1, refresh: true,
			values: [[1, "Да"], [0, "Нет"]]
	},{
			pref: ["browser.sessionstore.restore_on_demand", "Загружать неактивные вкладки", , "При запуске загружаются все вкладки.\nМожет замедлить работу браузера."], userAlt: false, // userChoice: true, userAlt: false,
			values: [[false, "Да"], [true, "Нет"]]
	},{
			pref: ["javascript.enabled", "Выполнять скрипты Java"], userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},null,{
			pref: ["network.proxy.autoconfig_url", "Прокси (VPN) URL", "п"],
			userChoice: "127.0.0.1", userAlt: "https://antizapret.prostovpn.org/proxy.pac", refresh: true,
			values: [
				["127.0.0.1", "отключен…", "0", "", `prefs.setIntPref('network.proxy.type', 0); node.parentNode.parentNode.style.filter = '';`],
				["https://antizapret.prostovpn.org/proxy.pac", "АнтиЗапрет", "1", "Надёжный доступ на заблокированные сайты\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"] ]
	},{
			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", "Все протоколы через прокси"], userChoice: true, refresh: true,
			values: [[true, "Да", "", "Прокси для всех протоколов при ручной настройке"], [false, "Нет"]]
	},{
			pref: ["network.trr.mode", "DNS поверх HTTPS", , "Шифрование DNS-трафика для\nзащиты персональных данных"], userChoice: 1, userAlt: 2, refresh: true,
			values: [
				[0, "по-умолчанию", "0"], [1, "автоматически", "1", "используется DNS или DoH, в зависимости от того, что быстрее"], [2, "DoH, затем DNS", "2"], [3, "только DoH", "3"], [4, "DNS и DoH", "4"], [5, "отключить DoH", "5"] ]
	},{
			pref: ["network.trr.uri", "Провайдер DNS", , "↯ нужен перезапуск браузера"],
			userChoice: "https://mozilla.cloudflare-dns.com/dns-query", userAlt: "https://firefox.dns.nextdns.io/", restart: true,
			values: [
				["https://mozilla.cloudflare-dns.com/dns-query", "Cloudflare"], ["https://firefox.dns.nextdns.io/", "NextDNS"], ["https://doh.opendns.com/dns-query", "OpenDNS"], ["", "по-умолчанию"]]
	},null,{
			pref: ["browser.zoom.full", "Масштабировать"], userChoice: false, userAlt: true,
			values: [[true, "всю страницу"], [false, "только текст"]]
	},{
			pref: ["browser.display.document_color_use", "Использовать цвета сайтов"], userChoice: 0, userAlt: 1,
			values: [[0, "Авто", "0"], [1, "Всегда", "1"], [2, "Никогда", "2"]]
	},{
			pref: ["font.name.sans-serif.x-cyrillic", "Шрифт без засечек "], userChoice: "Arial", userAlt: "PT Sans",
			values: fontsans
	},{
			pref: ["font.name.serif.x-cyrillic", "Шрифт с засечками"], userChoice: "Cambria", userAlt: "Times",
			values: fontserif
	},{
			pref: ["image.animation_mode", "Анимация изображений"], userChoice: "none", userAlt: "normal", refresh: true,
			values: [["none", "Выключена"], ["normal", "По циклу"], ["once", "Единожды"]]
	},{
			pref: ["ui.prefersReducedMotion", "Анимация интерфейса", , "↯ перезапуск браузера"], userChoice: true, userAlt: 0, restart: true,
			values: [[1, "Отключена"], [0, "Включена"]]
	},null,{
			pref: ["media.autoplay.default", "Авто-play аудио/видео"], userChoice: 5, userAlt: 2, 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: 0, userAlt: 1, refresh: true, values: [
				[2, "Всегда включать", "2"], [1, "Включать по запросу", "1"], [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отключите при разных проблемах с графикой"],
			userChoice: false, restart: true, values: [
			[true, "Да", "", "", `prefs.setBoolPref("gfx.webrender.compositor.force-enabled", false)`],
			[true, "Нет", "", "", `prefs.setBoolPref("gfx.webrender.compositor.force-enabled", true)`] ]
	},null,{
			pref: ["dom.disable_open_during_load", "Блокировать всплывающие окна"], userChoice: 2, userAlt: true,
	},{
			pref: ["network.cookie.cookieBehavior", "Получать куки"], userChoice: 1, userAlt: 3, refresh: false,
			values: [
				[1, "кроме сторонних"], [3, "кроме не посещённых"], [4, "кроме трекеров"], [2, "никогда"], [0, "со всех сайтов"] ]
	},{
			pref: ["network.http.sendRefererHeader", "Referer: для чего"], userChoice: 2, userAlt: 1,
			values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки, изобр.", "2"]]
	},{
			pref: ["dom.storage.enabled", "Локальное хранилище", , "Сохранение персональных данных, по\nкоторым вас можно идентифицировать"],
			userChoice: false, userAlt: true,
			values: [[true, "Разрешить"], [false, "Запретить"]]
	},{
			pref: ["privacy.resistFingerprinting", "Изоляция Firstparty-Fingerprint", ,"privacy.firstparty.isolate\nЗащита данных пользователя также\nзапрещает запоминать размер окна"], userChoice: true,
			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: true
	},{
			pref: ["dom.enable_performance", "Статус загрузки страницы",,"Передача данных разрешит определять\nфакт использования прокси-сервера"], userChoice: false
	},{
			pref: ["browser.cache.disk.enable", "Дисковый кэш"], userChoice: true,
			values: [[true, "Включен"], [false, "Выключен", "", "Включается кэш в оперативной памяти", `prefs.setBoolPref("browser.cache.memory.enable", true)`]]
	},{
			pref: ["browser.cache.memory.enable", "Кэш в оперативной памяти"], userChoice: true,
			values: [[true, "Да"], [false, "Запрет"]]
	},{
			pref: ["general.useragent.override", "User Agent"],
			userChoice: null, userAlt: "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.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 (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 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: "ToggleAboutConfig",
		tooltiptext: description,
		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; //  = this.UserImg
			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", "#804040", "important");
					if (!alt) img = this.notUserChoiceImg;
				}

			if (!pref.img) img
				? node.setAttribute("image", img)
				:	/этой кнопки/.test(node.getAttribute("label")) ? node.setAttribute("image", "chrome://browser/skin/menu.svg") : node.setAttribute("image", this.UserImg);
				// : 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 += ` ${"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;
			var win = Services.wm.getMostRecentWindow("navigator:browser");
			win.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:debugging#/runtime/this-firefox", e); // ПКМ + Alt отладка дополнений
			}
			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};

			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);
		},
		Notify(title, text, time = 2000){ Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService).showAlertNotification(null, title, text, false, '', null, time);
		},
		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()});
		},
		Antizapret(trg) {
			if (prefs.getIntPref('network.proxy.type') == 2) { // выключить
				prefs.setIntPref('network.proxy.type', 0);
				prefs.setStringPref("network.proxy.autoconfig_url", "127.0.0.1");
				trg.style.removeProperty("filter");
			} else {
				prefs.setIntPref('network.proxy.type', 2);
				prefs.setStringPref("network.proxy.autoconfig_url", "https://antizapret.prostovpn.org/proxy.pac");
				trg.style.setProperty("filter", icon_vpn, "important");
				// this.Notify('Proxy', 'Работаем через VPN Антизапрет');
			}
		},
		londPress(e) { // удержание кнопки мыши. на второй долгий клик при отпускании сработает действие на обычный клик этой кнопки
			var trg = e.target, win = e.view;
			if (e.button == 0) this.Antizapret(e.target);
			if (e.button == 1) trg.ownerDocument.getElementById("key_browserConsole").doCommand(); // Консоль браузера
			if (e.button == 2) this.switchToTab("about:config", e); // RMB Long
		}
	};
}); // END ToggleAboutConfig

Отредактировано Dobrov (19-05-2021 08:17:48)

Отсутствует

 

№1560019-05-2021 16:22:00

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

Re: Custom Buttons

Dumby
Опять не могу сделать. Окно по ПКМ выскакивает и другие коды сработают при отмене. Как бы запретить ?

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

Выделить код

Код:

if(event.button == 2 && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey){
Sanitizer.showUI(window);   

CustomizableUI.setToolbarVisibility("PersonalToolbar", document.querySelector("#PersonalToolbar").closed);
 var s = "browser.zoom.full";
      cbu.setPrefs(s, cbu.getPrefs(s) == true ? true : true); 
 var s = "intl.accept_languages";
 cbu.setPrefs(s, cbu.getPrefs(s) == "ru" ? "ru": "ru");

  var s = "media.autoplay.default";
 cbu.setPrefs(s, cbu.getPrefs(s) == 5 ? 5: 5);
SidebarUI.hide();
    var s = "extensions.long_left_click.timeContent";
     cbu.setPrefs(s, cbu.getPrefs(s) == 350 ? 350: 350);
document.querySelector(
    "#mainPopupSet > tooltip[onpopupshowing*=undoCloseTabsList]"
)?.undoCloseTabsList.updUI();
gClipboard.clear();  
          
var file = Services.dirsvc.get('ProfD', Ci.nsIFile);
         file.initWithPath(file.path + "\\memory\\start.vbs"); 
         file.launch(); 
}
};

Отсутствует

 

Board footer

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