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

Список ответов на каверзные вопросы можно получить в FAQ-разделе форума.

№1687620-05-2023 02:22:56

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

Re: Custom Buttons

Dumby
просьба доработать антиподписячий код – с ним браузер LibreWolf 110 не запускается, но LibreWolf 97 работает нормально.

Выделить код

Код:

JavaScript error: resource://gre/modules/AddonManager.jsm, line 20: InternalError: module record has unexpected status: Evaluating
JavaScript error: resource://gre/modules/AsyncShutdown.sys.mjs, line 727: Error: Phase "profile-before-change" is finished, it is too late to register completion condition "OS.File: flush I/O queued before profileBeforeChange"

Отсутствует

 

№1687720-05-2023 09:36:34

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

Re: Custom Buttons

Dobrov пишет

браузер LibreWolf 110

Где я его возьму? По ссылке другая версия,
а гитлаб мне фигу показывает, а не «all versions and release notes».
Ладно, скачал 112.0.2-1

просьба доработать антиподписячий код

Антиподписячий код, по сути, меняет AppConstants.MOZ_REQUIRE_SIGNING
чтобы релиз или бета были как DE, ESR, Unbraindead.


Насколько я вижу, в LibreWolf 112.0.2-1, его значение уже выставлено в false
То есть, антиподписячий код просто не требуется, достаточно настроек
xpinstall.signatures.required
extensions.experiments.enabled


А дурацкие надписи на about:addons скрыл так: layout.css.has-selector.enabled
и в userContent.css

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

Выделить код

Код:

@-moz-document url(about:addons) {
	.addon-card-message:has([data-l10n-id=details-notification-unsigned]) {
		display: none !important;
	}
}


Так ли всё это для LibreWolf 110 — проверь сам.

Отредактировано Dumby (20-05-2023 09:39:51)

Отсутствует

 

№1687820-05-2023 11:11:03

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

Re: Custom Buttons

Dumby дурацкие надписи не могу скрыть – до них не доходит! :)
LibreWolf версий 110-113 не запускаются под виндой и Маком с таким антиподписячим config.js
Просьба сделать так, чтобы ошибки выполнения в этом коде не мешали запускаться браузеру !

Выделить код

Код:

//
try {(jsval => { // Firefox 78+, FIX 100+ https://forum.mozilla-russia.org/viewtopic.php?pid=802045#p802045
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	var MRS = "MOZ_REQUIRE_SIGNING", AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;

	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true; genv();

		dbg.onEnterFrame = frame => {
			var {script} = frame;
			try {if (!script.url.startsWith(uac)) return;} catch {return;}
			dbg.onEnterFrame = undefined;

			if (script.isModule) { // ESM, Fx 108+
				var env = frame.environment;
				frame.onPop = () => env.setVariable(AC, gref.makeDebuggeeValue(freeze(
					o.assign(new o(), env.getVariable(AC).unsafeDereference(), {[MRS]: false})
				)));
			} else { // JSM
				var nsvo = frame.this.unsafeDereference();
				nsvo.Object = {freeze(ac) {
					ac[MRS] = false;
					delete nsvo.Object;
					return freeze(ac);
				}};
			}
		}
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj[MRS] = false;

		if ((disleg = "MOZ_ALLOW_ADDON_SIDELOAD" in obj)) lexp();
		else
			obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
			lockPref("extensions.legacy.enabled", true);

		return (o.freeze = freeze)(obj);
	}
	lockPref("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

	var useDbg = true, xpii = "resource://gre/modules/addons/XPIInstall.";
	if (Ci.nsINativeFileWatcherService) { // Fx < 100
		jsval = Cu.import(xpii + "jsm", {});
		var shouldVerify = jsval.shouldVerifySignedState;
		if (shouldVerify.length == 1)
			useDbg = false,
			jsval.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);
	}
	if (useDbg) { // Fx 99+
		try {var exp = ChromeUtils.importESModule(xpii + "sys.mjs");}
		catch {exp = g.ChromeUtils.import(xpii + "jsm");}
		jsval = o.assign({}, exp);

		var env = genv(jsval.XPIInstall.installTemporaryAddon);
		var ref = name => {try {return env.find(name).getVariable(name).unsafeDereference();} catch {}}
		jsval.XPIDatabase = (ref("lazy") || {}).XPIDatabase || ref("XPIDatabase");

		var proto = ref("Package").prototype;
		var verify = proto.verifySignedState;
		proto.verifySignedState = function(id) {
			return id ? {cert: null, signedState: undefined} : verify.apply(this, arguments);
		}
		dbg.removeAllDebuggees();
	}
	if (disleg) jsval.XPIDatabase.isDisabledLegacy = () => false;
})(
	"permitCPOWsInScope" in Cu ? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu
);}
catch(ex) {Cu.reportError(ex);}

Отсутствует

 

№1687920-05-2023 17:02:10

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

Re: Custom Buttons

Dobrov
Я что-то непонятное написал?
Есть непреодлимое желание устанавливать в false то,
что уже и так стоит в false?


Хорошо, есть у меня код, который делался под const,
а не под var, как сейчас.
Вроде устанавливает, и ошибок не вызывает.
Такая правка:

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

Выделить код

Код:

/*
				var env = frame.environment;
				frame.onPop = () => env.setVariable(AC, gref.makeDebuggeeValue(freeze(
					o.assign(new o(), env.getVariable(AC).unsafeDereference(), {[MRS]: false})
				)));
*/
				var line = script.source.text
					.split("\n").findIndex(str => str.includes(MRS)) + 1;

				var max = script.getPossibleBreakpoints().at(-1).offset;
				for(var offset = 0; offset < max; offset++) try {
					if (script.getOffsetMetadata(offset).lineNumber > line) break;
				} catch {}

				var cls = {class: "Object"};
				var find = o => o.getProperty(MRS).return !== undefined;
				frame.onStep = () => {
					var ac = frame.offset > offset && dbg.findObjects(cls).find(find);
					if (ac) frame.onStep = undefined, ac.unsafeDereference()[MRS] = false;
				}

Отсутствует

 

№1688021-05-2023 13:22:03

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

Re: Custom Buttons

Dumby - спасибо!, код нужен был просто для тестирования.

Второй вопрос: в новых FF перестала мигать кнопка Загрузок, как исправить?

Выделить код

Код:

async function dflash(){
	var d = await Downloads.createDownload({source: "about:blank", target: FileUtils.File("/")});
	(await Downloads.getList(Downloads.ALL)).add(d);
	d.refresh(d.succeeded = true);
}
dflash();

Отсутствует

 

№1688121-05-2023 17:23:38

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

Re: Custom Buttons

Dobrov пишет

в новых FF перестала мигать кнопка Загрузок

Может из-за этого?
Что у тебя показывает, если запустить с Консоли браузера?


matchMedia("(prefers-reduced-motion)").matches;

Отсутствует

 

№1688221-05-2023 17:27:00

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

Re: Custom Buttons

Dumby пишет

matchMedia("(prefers-reduced-motion)").matches;

выдаёт «true»
системную анимацию не хочу включать, при этом на FF97 кнопка Загрузки мигает.

Отредактировано Dobrov (21-05-2023 17:34:57)

Отсутствует

 

№1688321-05-2023 19:25:32

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

Re: Custom Buttons

Dobrov пишет

при этом на FF97 кнопка Загрузки мигает

Баг то хоть глазами пробежал?
Написано же: «Target Milestone: --- → 111 Branch».

системную анимацию не хочу включать

Системную? Ты, наверно, хотел написать «в браузере»?
Можно ведь создать числовую настройку ui.prefersReducedMotion
со значением ноль.


Если это не устраивает, то можно, например, переключать её в коде.


То есть, перед строкой с d.refresh — устанавливаем, а после — сбрасываем.
Только не забудь тогда в самой строке добавить await перед d.refresh
(можно её ещё и в try catch завернуть, чтобы гарантировать сброс).

Отсутствует

 

№1688422-05-2023 05:15:21

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

Re: Custom Buttons

Dumby пишет

Системную? Ты, наверно, хотел написать «в браузере»?

насколько я понял, браузер учитывает анимацию системы:

In Windows 10 Settings, in Display, set "Show animations in Windows" to Off.
Or on macOS , in the Accessibility system preferences, under "Display" tick the "Reduce motion" checkbox.

Фиктивное завершение загрузки файла переделал так:

Выделить код

Код:

async function dflash(){ // фиктивное завершение загрузки
	var {prefs} = Services;
	var d = await Downloads.createDownload({source: "about:blank", target: FileUtils.File("/")});
	(await Downloads.getList(Downloads.ALL)).add(d);
	if (matchMedia("(prefers-reduced-motion)").matches)
		prefs.setIntPref('ui.prefersReducedMotion', 0);
	await d.refresh(d.succeeded = true);
	prefs.clearUserPref('ui.prefersReducedMotion');
}
dflash();

Отредактировано Dobrov (22-05-2023 05:44:12)

Отсутствует

 

№1688522-05-2023 16:13:07

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

Re: Custom Buttons

Dobrov пишет

переделал так

А зачем matchMedia("(prefers-reduced-motion)").matches проверяется?
Мы ведь уже выяснили, что у тебя там будет true.
И настройку предложил сбрасывать, поскольку не хочешь завести её на постоянно,
то есть под конкретный расклад.


А если проверка matches, подразумевается как бы под более общий случай,
тогда и пост-d.refresh() действие должно производиться только если matches был true.


И, в таком случае, настройку следует сбрасывать только если её не существовало,
иначе — вернуть какая была.

Отсутствует

 

№1688623-05-2023 04:27:13

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

Re: Custom Buttons

Dumby пишет

А зачем matchMedia("(prefers-reduced-motion)").matches проверяется?
Мы ведь уже выяснили, что у тебя там будет true.

Вот 2 вариант. Не знаю, как не создавать настройку, если её не было. У меня в неё пишется значение по-умолчанию…

Выделить код

Код:

async function dflash(){ // фиктивное завершение загрузки
	var {prefs} = Services, p = 'ui.prefersReducedMotion', t = prefs.getIntPref(p, 0);
	var d = await Downloads.createDownload({source: "about:blank", target: FileUtils.File("/")});
	(await Downloads.getList(Downloads.ALL)).add(d);
	prefs.setIntPref(p, 0); await d.refresh(d.succeeded = true); prefs.setIntPref(p, t);
}
dflash();

Ещё подскажите, как посмотреть объект Java-Script (например Свойства для кнопки) без расширения DOM-Inspector ?
Может, возможно доработать кнопку Attributes Inspector для просмотра Java-Script свойств ?
DOM-inspector.png

Отредактировано Dobrov (23-05-2023 09:47:15)

Отсутствует

 

№1688725-05-2023 00:49:57

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

Re: Custom Buttons

Dobrov пишет

если её не было

Обычно, существование настройки проверяется
вызовом метода Services.prefs.getPrefType()


Для несуществующей настройки
результатом вызова будет Services.prefs.PREF_INVALID


Однако, поскольку ui.prefersReducedMotion дефолтно не существует,
можно просто проверять Services.prefs.prefHasUserValue()


Вобщем, я типа о чём-то таком

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

Выделить код

Код:

//...
	var p = "ui.prefersReducedMotion";
	var {matches} = matchMedia("(prefers-reduced-motion)");
	if (matches) {
		var has = prefs.prefHasUserValue(p);
		if (has) var was = prefs.getIntPref(p);
		prefs.setIntPref(p, 0);
	}
	await d.refresh(d.succeeded = true);

	if (matches) has ? prefs.setIntPref(p, was) : prefs.clearUserPref(p);


Разумеется, кейс, что настройка существует, но создана
неправильно (например, как логическая) здесь не поддерживается.

как посмотреть объект Java-Script (например Свойства для кнопки) без расширения DOM-Inspector ?

Эээ, в консоли.
console.log(button);


Если с Инспектора, то ПКМ —> «Использовать в Консоли» —> Enter


И гораздо круче, в DOMi только лишь for-in-перечисляемые свойства,
а в консоли — вообще все.
Но, для функций не предусмотрено отображение как toString(),
что слегка снижает информативность.

Отсутствует

 

№1688825-05-2023 10:41:54

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

Re: Custom Buttons

Dumby — спасибо за уроки! :beer:


Ещё проблема: «Инструменты браузера: Пипетка» при вызове из кода ничего не происходит.
Но если открыть «Консоль браузера», то потом Пипетка из кода открывается.


Как сделать, чтобы сразу из скрипта работало ?
document.getElementById("menu_eyedropper").click();

Отсутствует

 

№1688926-05-2023 12:48:25

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

Re: Custom Buttons

Dobrov пишет

«Инструменты браузера: Пипетка»

Хмм, у меня код для пипетки, неожиданно, даже сохранился.
Но это код для CB, надеюсь распорядишься правильно.

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

Выделить код

Код:

this._handleClick = () => {

	var sub = parseInt(Services.appinfo.platformVersion) >= 96 ? "loader/" : "";
	var url = `resource://devtools/shared/${sub}Loader.`;

	try {var exp = ChromeUtils.importESModule(url + "sys.mjs");}
	catch {exp = ChromeUtils.import(url + "jsm");}

	var obj = exp.require("devtools/client/menus").menuitems
		.find(menuitem => menuitem.id == "menu_eyedropper");

	(this._handleClick = obj.oncommand.bind(null, {target: this}))();
}

Отсутствует

 

№1689027-05-2023 15:28:58

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

Re: Custom Buttons

Dumby - menu_eyedropper работает, Спасибо!


Вопрос по элементу "menupopup" на примере старой кнопки Quick Toggle меню (строка 458 в этом коде)
Как определить состояние меню: Открыто, Не открыто? Какой флаг это показывает ?
В моём профиле при наведении на кнопку есть подсказка в строке статуса. Если сделать правый клик, то откроется меню кнопки ToggleButton.
При наведении мыши по строкам меню опять показывается эта же подсказка, хотя id менюшки «ToggleButton-Popup», а в коде подсказка прописана для id «ToggleButton».


При открытом меню "ToggleButton-Popup" его id.state всегда "closed" и других флагов в DOM Inspector я не нашёл для этой менюшки.
Нужен код, чтобы получить состояние менюшки открыта или нет (чтобы не выводить лишние подсказки).

Отсутствует

 

№1689129-05-2023 22:29:55

Ferguss114
Участник
 
Группа: Members
Зарегистрирован: 31-03-2012
Сообщений: 207
UA: Chrome 113.0

Re: Custom Buttons

Как сделать программно клик на пункте контекстного меню страницы?
Если в атрибуте oncommand этого пункта нет строки "gContextMenu", то все нормально.
Но если "gContextMenu" есть, то это не работает.
Я вызываю меню document.getElementById("contentAreaContextMenu").openPopup()
Меню открывается, но клика не происходит. В консоли ошибка this.ownerDoc is undefined а при закрывании меню появляется еще и gContextMenu is null

Отсутствует

 

№1689201-06-2023 05:57:08

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

Re: Custom Buttons

Подскажите, у кого-нибудь есть рабочая кнопка Дополнительные пункты в контекстном меню кнопки для актуальной версии

Отсутствует

 

№1689301-06-2023 11:14:38

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

Re: Custom Buttons

Garalf эта?

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

Выделить код

Код:

/*Initialization Code*/ 

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////// Создание меню ///////////////////////////////
/////////////////////////////////////////////////////////////////////////////

function $(aId) {
  return document.getElementById(aId);
};

function addMenuItem(aNewIDs, aNodeIDs, aLabel, aIcon, aCommand) {
  for (var i = 0; i < aNewIDs.length; i++) {
    
    if ($(aNewIDs[i])) $(aNewIDs[i]).parentNode.removeChild($(aNewIDs[i]));

  var mi = document.createXULElement("menuitem");
           mi.setAttribute("id", aNewIDs[i]);
           mi.setAttribute("class", "menuitem-iconic");
           mi.setAttribute("image", aIcon);
           mi.setAttribute("label", aLabel);
           mi.setAttribute("oncommand", aCommand);
        
    if (i == 0)
      mi.setAttribute("observes", "custombuttons-contextbroadcaster-primary");

    if ($(aNodeIDs[i])) {
      if ($(aNodeIDs[i]).nextSibling) {
        $(aNodeIDs[i]).parentNode.insertBefore(mi, $(aNodeIDs[i]).nextSibling);
      } else {
        $(aNodeIDs[i]).parentNode.appendChild(mi);
      }
    }
  }
};

var saveImg1 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAnUlEQVQ4jc3RPQ4BURTF8Z+PgrAPtUJnIxZgBZagkolVqDWWIliARCFR6GSq0Twyw2SemcpJTnPzzv/edy//ohOymj7mARl6NRoOQ6YAiGmNFAm6TQBpePdAJwYYYIF+rpaE8CoGaGEbarsw7qfaVYCl4rY3mOOGaQwwU326O8Zhyi/AJPwxdv8rRmWAyw/hl89lgCZ+69AgvPcXegKfOWlGgoA/rgAAAABJRU5ErkJggg==";
var saveImg2 = "data:application/file;base64,AAABAAEAEBACAAEAAQCwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAADbAAADpcAAA//AAANVwAAD/8AAAKsAAAD/AAAP//AAAP8AAAD/AAAD/8AAAwDAAAP/wAAA/wAAAP8AAA";
var saveImg3 = "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAAAAAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAAAAAAAP8AAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAA/wAAAAAAAAD/AAAA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQf//rEH4j6xB8IesQeHDrEHD4axBh/CsQYf4rEGH8KxBw+GsQeHDrEHwh6xB+I+sQf//rEH//6xB//+sQQ==";
var saveImg4 = "data:application/file;base64,AAABAAEAEBACAAEAAQCwAAAAFgAAACgAAAAQAAAAIAAAAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAD/AAAD/8AAA//AAAP/wAAD/8AAA//AAAP/wAAD/8AAA//AAAP/wAAD/8AAA//AAAP/wAAA/wAAAP8AAA";
var saveImg5 = "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1FQr1FRT7BRUU+wUVFPsFFRT7BRUU+wUVFPsFFRT7BRUU+wUVFPsFFRT7BRUU+wU1FQrwAAAAAAAAAAAAAAAHd0cpz//////////////////////////////////////////////////////////3d0cpwAAAAAAAAAAAAAAAB4d3Sc/////+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4ODg/+Dg4P/g4OD/4ODg//////94d3ScAAAAAAAAAAAAAAAAenh3nP/////i4uL/4uLi/5yiz/9HYcT/PWrR/1qQ3P+nuNr/4uLi/+Li4v//////enh3nAAAAAAAAAAAAAAAAH18eJz/////5OTk/2FluP8IJ6r/D1TU/wli4v8FZeb/D3He/4y+5f/k5OT/+/v7/318eJwAAAAAAAAAAAAAAACBfXyc/////5Wc0v8JKq//E2Pd/yFTrv9EKTn/LjVZ/xaF2/8OdNP/zNnm//z8/P+BfXycAAAAAAAAAAAAAAAAhIGBnP////89Xc7/Dk7N/wpQ0/8zVp//TGCK/4U2Cf9DW2b/Eqj7/5C+5//8/Pz/hIGBnAAAAAAAAAAAAAAAAIaEgZz/////Kmnf/wlFzf8gPZ7/uXcl/8OAK/+UTBL/b1Az/zfG9f9srdX/+fn5/4aEgZwAAAAAAAAAAAAAAACJhoac/////0iN6P8HQtH/EVzd/2mCqf/dqkz/0pY6/6FnKv9Nvt7/kMfs//X19f+JhoacAAAAAAAAAAAAAAAAiomJnP////+Mr+r/FFnU/xVRyv+LorL/8Oqh//nXbP+5omH/Urvf/8bj7v/s7Oz/iomJnAAAAAAAAAAAAAAAAI6Kipz/////2+n3/5aSm/+hcEn/4uXC////2P/25KD/rqSD/7HR5//o6Oj/4eHh/46KipwAAAAAAAAAAAAAAACPjo6c//////j4+P/4+Pj/3s/F/9i/n//p27n/5NG0/+7r6f/r6+v/3t7e/9LS0v+Pjo6cAAAAAAAAAAAAAAAAk4+PnP/////6+vr/+vr6//r6+v/6+vr/+vr6//j4+P/19fX/r66t/62sq/ObmprrkI2NpgAAAAAAAAAAAAAAAJOTk5z//////Pz8//z8/P/8/Pz/+/v7//r6+v/4+Pj/9fX1/66trfHs7Ozrp6emp4aGhhMAAAAAAAAAAAAAAACWlpOc/////////////////v7+//39/f/6+vr/9/f3//Pz8/+bmprrp6enp3NzcxYAAAAAAAAAAAAAAAAAAAAAmJiWnJiYlpyYmJacmJiWnJeTk52SkZGemJiWnJeTk52SkZGej4+LpIaGhhMAAAAAAAAAAAAAAAAAAAAAgAMAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAIADAACABwAAgA8AAA==";
var saveImg6 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABKklEQVR42mNkQAMNDAxCQMoTiB2BWAEq/ACI9wPxdqD8O2T1jGiao4HUEgb8IAaobimGAUDBGiDVzEAcqAWqb4EbQKTNWF3CCPXzW5ho/f//YDqHkZFhCpQNA7lAMV4gzY4QEmZEtx1mgCtQ8W4o2xLIPg5lJwDZUghDYkAGzAEyktENMAQqPA9l6wDZEkB6D6Yhc0EG7AGynfEZYAxkawHpxVC+A5CvC6RFGBj2Em2AIZCeg8Q3BtLSUAMIegGkwQBIz8U0YC7OQEQ2wAjIPoekGRQTUC/E4IxGkMKzaNEI8jso4chDbGdgA0UjekJ6A8S3gRikFWQyKOF/higGR50UFLPBEhLM9AZoUv4F1cAA1QTi/2SAJFkQnxdCoyZlJEPIz0xIhpCUnQFx83abgfUZOQAAAABJRU5ErkJggg==";
var saveImg7 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAASUlEQVR42mNkoBAw0s6ABob/aHxGkg1I8zEGM2dtOTskDED3MxCgGIBpASOGATANhACyi6hrAGVeoDgQB6UB2PxMvAFEAooNAAC7izYR2pQ0nAAAAABJRU5ErkJggg==";
var saveImg8 = "data:application/file;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAD///8A////AESqAABHpgAASKAAAEukAQxMpQMYR6MAAEepAQ9MpgOLRKgACUenAABHpgAARKoAAP///wD///8A////AP///wBEqgAARaIAAFS2FVBj0jS3Y9AzyVG0EUROrwt8TKcDs0ikAANInwAAR6YAAESqAAD///8A////AP///wD///8ARKQAAVvJKJtj0jX/SaAJ/0abCv9UtSH/U7QX8lW5IMddwyfQW8Iii0mqBQ1DqQAA////AP///wD///8A////AEWlAAFYwB6YXsYc5F7GFt9bwA+vVbgWvU6rAKBRrgf0RZgA/02oDP9ezCzJR6UBA////wD///8A////AP///wBFrAAASKIAADiGNQAeYIIpF1iJHVa4GKEve0tFLHl1Pla7J51exRnUWL8VjEetAAP///8A////AP///wD///8AC0WuAAk/tAAANdpIAFD6/wA+7f8gd6jqBlPX+QBE9PIGS8uDLnZLADqNJwAzjDMA////AP///wD///8A////AAAw0QAALc0AAEPdegBI4f8AK8P/ADHX/wAwzf8AL8b/AEno/gAz2zYALtEQAC3VAP///wD///8A////AP///wAAMssAADjSMwBA2pIATen/ADG9/wBe1v8ATcz/ADHK/wBN5/4ASeH/AEvl6wA10DP///8A////AP///wD///8AADnTSQBO5/8ANc7/ADnR/xC+9/8C0f//AMj//wCP8P8ALcX/ACrC/wBE3f8ASeOf////AP///wD///8A////AABI4Z4ARN3/ACvG/wJAxP8Z5v//AMT//wDE//8Axv7/AETM/wA51P8AUervADjSPP///wD///8A////AP///wAAOdRiAFry+QBS6/QATOP/H+P8/xDe//8J1///E9H4/wFE2f8AReC6ADTOEwAyywD///8A////AP///wD///8AADDNAAAyzCcARN7WACvG/wVfzf8d0vf/HM30/whf0/8AI7n/ADzV+AA40kUAMswA////AP///wD///8A////AAAwywAAPNdTAEfg/wAsw/8AOdb/ADLP/wAswv8AReP/AC7H/wA40f8AR+GfADDKAP///wD///8A////AP///wAAMswAADfRQgBY8f8AT+j/AE7o7AA+1/8AK8T/AE7o/wBN5f8AVe7/AD/ZbAAwywD///8A////AP///wD///8AADPMAAAyywAANM4vADjSUQA0zikAU+z3AEff/wBJ4sMAN9EtADjROQAyywAAM8wA////AP///wD///8A////AAAzzAAAM8wAADHLAAAwywAAMcsAAD/YXQBK4pYAN9AxAC/KAAAwygAAM8wAADPMAP///wD///8A+R8AAPAfAADABwAAwAMAAPgDAADwHwAA8AcAAOADAADAAwAAwAMAAMAHAADgBwAA4AcAAOAHAADwDwAA/j8AAA==";
var saveImg9 = "data:application/file;base64,AAABAAEAEBAAAAAAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8BAAAAFwAAAGkAAABzAAAAdQAAAHUAAAB1AAAAdQAAAHUAAAB1AAAAdQAAAHUAAAB1AAAAdQAAADkAAAAP////AQAAAIdgZmj/YWlt/2FobP9haGz/YGhr/2Boa/9fZ2v/X2dr/15mav9dZWn/XGRo/0ZNUP8AAACdAAAAFf///wEAAACr2tzc/9ve4P/a3t//1dnZ/7S2tf+foJz/m5ya/6apqP/O0tP/09fZ/9DV1v+hqq//AAAAuQAAABX///8BAAAAq+3u7//e4eL/ub29/2hnXv9oVUX/U0As/zgxGf83Lx3/YWBX/7a5uv/S1tj/o6yx/wAAALkAAAAV////AQAAAKv29/f/19na/1dUQf9jXDv/dmtJ/4FoSP9VQiL/V0Ek/008Iv9HQTP/yc3P/6Wus/8AAAC5AAAAFf///wEAAACr+Pj4/5uamP9tY0L/g31b/6GLa/+McVH/eFY5/4xwUv9yXkD/RTki/4uMiv+nsLT/AAAAuQAAABX///8BAAAAq/n6+v+FfXL/waSM/8qznf/DrZP/ooFi/7WfhP+qh2//blk9/1A+Iv9aWlH/pK2x/wAAALkAAAAV////AQAAAKv6+/v/d3Rr/9zCsP/RxbH/z8Wu/9fJt//Qvab/qItv/5iOb/9tYUH/VVJK/6Wtsf8AAAC5AAAAFf///wEAAACr+/z8/4mHff+3pI//3NK//+HXxf/m3Mz/5trJ/9rMuf+bgWT/d14//2hnYP+osbX/AAAAuQAAABX///8BAAAAq/z9/f/FxL7/j4l+//Xw5f/29ez/8/Dl/+DMuv/VuaP/poZn/2dFKv+srav/oamt/wAAALkAAAAV////AQAAAKv+/v7/+/z8/5iZjf+5uqr/6+PW/+3i1P/kzL3/vZR+/4NhSf+Qh3z/z9HS/4qQkv8AAAC1AAAAFf///wEAAACr/v7+//7+/v/u7u3/tbiv/5WSgP+DfGj/e25Z/29gTv+sppz/vr6+/5aYmP90eHr/AAAApwAAABP///8BAAAAq/////////////////7+/v/9/f3//f39//v8/P/5+fn/1dXV/2pqav9TU1P/QUFB/wEBAYkAAAAJ////AQAAAKv7+/v//////////////////v7+//7+/v/+/v7/+vr6/9fY2P/V1tb/7Ozs/4KCgv8EBAQrAAAAA////wEAAACFlJSU/6ioqP+qqqr/qqqq/6qqqv+qqqr/qKio/6anp/2kpaX9o6Oj/4qKitUZGRk9////Af///wH///8BAAAAFQAAAFUAAABVAAAAVQAAAFUAAABVAAAAVQAAAFUAAABTBAQEUx8fH1dfX18z////Af///wH///8BAAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//w==";
var loadImg = "data:image/x-icon;base64,AAABAAEAEBAAAAAAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///8B////Af///wH///8B////AQAAAB+yg2l71ZRt+daTa//Wk2v/1pNr/9aTa//Wk2v/v4NlywYEAycAAAAX////Af///wH///8B////Af///wHdo4M53Zx3/+Sgef/innb/4p52/+Kedv/lo3r/5aR6/+mqgfvPj27N////Af///wH///8B////Af///wH///8B4KSD8+mmff/opH3/56N6/+Whef/no3r/66qB/8KBXf/Fg1//0Y5s78+Pbqn///8B////Af///wH///8B////AeGlg//trIX/7ayE/+6thP/urIX/9bWL/8eFYf/GhF//yYZh/9SPa/nkn3j/////Af///wH///8B////Af///wHkn3j/76+G/+6uh//vrob/76+G//W3jP/GhF//y4di/9ONZ//XkWr/5J94/////wH///8B////Af///wH///8B5J94/++wif/wsIj/8LGJ//CxiP/0toz/y4di/8+KZf/vroP/766D/+SfeP////8B////Af///wH///8B////AeSfeP/xs4v/8bOL//Cziv/ws4v/87WL/9CNa+3QjWvt3p96/96fev/en3r/////Af///wH///8B////Af///wHkn3j/8rWN//K2jf/ytY3/8rWN//O2jvfdo4Lj0I5sJdCObCXQjmwl0I5sJf///wH///8B////Af///wH///8B5J94//O3j//zuI//87iP//O4j//zuZDx3aOD7////wH///8B////Af///wHdo4M/5J94/+SfeP/kn3j/5J94/++2j//zupH/9LqR//O6kv/0upL/87qR/+SfeP/kn3j/5J94/+SfeP////8B////Af///wHkoXr/9cCY//W8k//0vJT/9L2T//S8lP/0vZT/9LyU//S8lP/1vJT/9cCY/+Shev////8B////Af///wH///8B9b+WC+SjfP/2yqP/9b+W//a/lf/2v5X/9b6V//W+lv/1vpX/9sqj/+SjfP////8B////Af///wH///8B////Af///wH2wZgX5aZ+//fTrP/2wJj/9sCY//bAmP/2wZj/99Os/+Smf/////8B////Af///wH///8B////Af///wH///8B////Af///wHkqYH/99ax//fCmv/3wpn/99aw/+Sogf////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////AeWrhP/317L/99ey/+WrhP////8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B5a2F/+Wthf////8B////Af///wH///8B////Af///wH///8BAAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//w==";
var moveToMenu = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABKElEQVR42o3TMUvDQBQH8P+7tLlEwaW4ugl+iU6CCA4i0tbdqavgZ/ErCE79DsVBkYpKLR2kuApOatG297zLJWkqXnIPwrsk937cveQIJXHZBZt8cgFyzaEqYGuTvl7feM2FVAIHR8Bj341UAu1T4PPdjXgBJgzyPKDFy4SDIkJZo1yRASZ+FDB5Itzfct7YBGjuBBBh6oT6ed2OazGjsV3wQ2Cu64bXUKM7FgZJgPaufinTSVFhrDPFheVIe01nwLgfq9HNVCRAaw8Q6USWS4B05mgVIH2/qG+ABh+46im7heP9EELOLRDZpVqAQev8ZwUS/DBLivMtlDWxc7Ycs6yBhyov9vqMGcBBDB5/rxT7/Qfntjn/FXsDrmIvoHUonMVegMllx/kXlvSRMQ0GPE4AAAAASUVORK5CYII=";
var removeFromToolbar = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACMElEQVR42p3Sz2/SUBwA8G9pgQJd6QYZQQkOjUFdXMCLMn9B4l3myRgXhydPZgcTY2KyLfGiF/wT9GTmhfIXjMQ4NP5YD5qZTGdVdEx+9FVKSwttrTV4QD3AN3mH9837fr7v+/Kww1e4ic2HiSYMRGxufcHjxs87nQ5GN0zodnXQNLVomvCAL6ZR/xxmAS9VsX1umz1pJ/dfKGeD4+58OERNYQQO7Y4BWs8E3XCAqvRAEkWkdpTcNptm+4DpMVUOtboZl4tY2reXWmTGvbDTVKFnALicTrvYsFqbpgEOBw6tpgiigOb4Yoa1gTMzY1B+XUexKM1gLid8a3QgQHtBklQQBJm3RuAxDBiPl0z4aMq+eu1rFYmiFLOBowd8QJEEiLIBlbpV7PdArdZCApJX3j9O3e/Pe/Di0zTpJgvB8CSjSG1o7O7mbCA04QIHjoGsGuAfI6FRk0D8ISe3VlPc4OPGL61nab+/QDE0VD9/+T1CKOCCtqoDQRBAe0n4yH/nZUVLVtjTCP4R8cvPN8LRSKL6qVKygUjIC0rXAAMw6+EI6GkmoGaDM0DP8KuzfyHx+WfLe6KRpdpOlbMB+E9oWof78OhEcjB/5OqLbHAyUEB1oYTBCDFz7VWaYZg11BTYkYBj17lFyu3Lo5aYGwk4fuNNHgd8QVHk2EjA7K3NDVM3iuV708tDA2dXtqb0rr5mrWT57jQaGjh1+13eKio+uXOo9Gs/NJC6+TZhdf7zQ38Ceg0HgF1MCP0AAAAASUVORK5CYII=";
var iconADD = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAASCAMAAABl5a5YAAAAkFBMVEWsFBSfERGYExOPDw8jEBCwFBRJICDXICBCGRnELCwUDg5BDg6XDg7AFhbAGBjVKyvHHBzIKyuJGBiDGRm7FxeiOTmMMDASDg5PExM1Dg4TDg4xEBBaIyMvExMsExMbDw9GDg4AAAC5Dw/JDg7MDg7QDw++Dg60Dg7DDg6sDg61FRWNDg7YGRmwDg7WKyvEGhojAnMyAAAAInRSTlP7+/TzTfst/k/9J4T85N/16PFfWgljXR15BRU7HQ8MMIsAJl/uYAAAAINJREFUeF6Nz8cSgzAMBFDSGx1Cc4wdSy6Q9v9/F4Y2Hk7sTe+iXee5yFqokzhOqAWPr5QtsyFAOMxQ0voYIDokTcsestALTwjK8/2ih/tbSkQwSjd8BEDobjFB9hPCGNVuXHeAipBzpMR2z1g+vaWR1rvc7nFpPq+rXb3g/FatXrvIH9eGIuNzLuIwAAAAAElFTkSuQmCC";
var customBtn = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjVJREFUeNqcUkuIUmEUPl4fML6bBsFRacRF2EaCcRO48bFLEIJcBIJLxb2b2UWLkIEgQQjauVFaKmgYuclUTMUHKoaKE4GPxnxh5r3ezh0QHJFmmA/OPZfDfx7fdw7AfojQ7qGx4QawdgMymeyRyWR6IZVKucFg8N10Ov0Ot4VGozmNRCLx5XL5F/HHbrefYZh7m1yWWq0+TafTn2kEdqXr9TrtcDje3JR4xRHH1QYCgXOz2Wzs9/sQj8dBIpEATtGrVqu9yWSywmdLNGq3AAeNsFgsz202mxk7QzKZBK1WCzqdjtHjKUmSx41Go4L+slQqpYvF4keKoibXpnA6nWfY+VehUKBSqRTJ0Oh2u3Qul6MHgwE9Ho9pLMD4H16v95zD4RxuU6BbrdYlPh5g8tfFYvET9XiIiRylUsnQgPl8DtgVhsOheL1eS6LRaGy1Wg02FAA5VmKxWB1/yUQioWOxWHKXy2XEYsDQEggEgKJCJpOBcrnMbOliW4MNSOaDCbVKpVISCoVGHBnQQygUgnK1AkeH9z+Ew+HX2xoQezZDjUaj3ygDiMViaLfbV8kK+TGw2WysJzzafry3QK1W+9Lr9RpIDbLZLEhE4vedTsdvMBieuN3ulwRBPLh2B7uYzWYXzWZzjBoc5PP5MJ70K1xfEsVc4cpNeDcnuLFvzM3978gILpcrRy/w+XybGE+v1z/z+/2frFarG9d5AHeBSqUyeDyetwqF4jHcFXw+/4TH44n+CTAA3Ccog288LRAAAAAASUVORK5CYII=";
var saveAll = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACOElEQVR42mNkwAJ+7xf7//3tKwZeIUELRqf3JxnwAEZkzrtlwnv/MzE6/WfkWCwS/iTu7RLO/wzMPAwc/z4Lc0f/eIfTgP/7BM0PXDY5ofB9N8M/EB8k+BcoyQw1mEWd4SurLIO97lkUFzX9b/oPNmBznc7/////MbAzfIdoBoI/P77Abfnzn4XhH9A0kOLA7icQV88HKn0EdcH6Ss3/yM76/f0jg2vTXYZ/f39j9fdUgQkMdTp1DNFXoiEGrCzFNMCj6SrD/79/MDQzsbAzCEfyMTBwADkbGBjBBiwrRDXgF9AAz6ZLWF1QIlHCsFRnKdj2pYxLIQYsytPAMMC75SLYBVMkpzDkPM+By0lFSTEwCDAwvJ79eouoqKgv2IB5WWgGfPvI4N95kUEyQQwcJVXbq8CGeEp6MlzUuQi2fcKbCQgDZqWhG/CJIaDrLIPMf0kGBjeggAoDw5YNWxh8rHwYGMQZGB5PfszAysq6RUJCAmLAtCR1DAMCu08zzJSZxtDm0cbA8AQoyAxJGyDbWx+2MrCwsGyRkZGBGDApDpsBx8FhME95HkObfhtYM4MqA8OdnjtgNSAXyMvLQwzoi8JmwGF4NGoc12Bg6IHYXnuzFm6AsrIyxIAJ+UHXfj27oomIhc9AA/ajpAOdPToMV1yuwPnMzMxbNDU14QaEAqnGH28fqYGywt/fP5i8q5czYUtIMABygY6ODsSAJUuWOKMrUFHT2PPvzy90YZhX/79//36dt7d3KACPfPJHOU7GxAAAAABJRU5ErkJggg==";

let cIDs = ["custombuttons-contextpopup-exportXML",
            "custombuttons-contextpopup-exportXML-sub"];
let bIDs = ["custombuttons-contextpopup-bookmarkButton",
            "custombuttons-contextpopup-bookmarkButton-sub"];
addMenuItem(cIDs, bIDs, "Сохранить код кнопки в XML файл", saveImg9,
            "document.getElementById('" + this.id
          + "').saveXML(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode.URI "
          + ": document.popupNode.URI);", "X");
let xIDs = ["custombuttons-contextpopup-importnewbutton",
            "custombuttons-contextpopup-importnewbutton-sub"];
let aIDs = ["custombuttons-contextpopup-addnewbutton",
            "custombuttons-contextpopup-addnewbutton-sub"];
addMenuItem(xIDs, aIDs, "Добавить кнопку из XML файла\u2026", loadImg,
            "document.getElementById('" + this.id +
            "').loadXML();");          
let fIDs = ["custombuttons-contextpopup-copyImageURI",
            "custombuttons-contextpopup-copyImageURI-sub"];
let b2IDs = ["custombuttons-contextpopup-copyURI",
            "custombuttons-contextpopup-copyURI-sub"];            
addMenuItem(fIDs, b2IDs, "Копировать изображение кнопки в формате base64", saveImg1,
            "document.getElementById('" + this.id
          + "').copyImageURI();");
let f1IDs = ["custombuttons-contextpopup-saveButtonImage",
            "custombuttons-contextpopup-saveButtonImage-sub"];
addMenuItem(f1IDs, cIDs, "Сохранить изображение кнопки", saveImg8,
            "document.getElementById('" + this.id
          + "').saveImageURI();");  
let f2IDs = ["custombuttons-contextpopup-copyButtonsCodeText",
            "custombuttons-contextpopup-copyButtonsCodeText-sub"];
addMenuItem(f2IDs, b2IDs, "Копировать код кнопки как текст", saveImg2,
            "document.getElementById('" + this.id
          + "').copyButtonsCodeText();");                    
let f3IDs = ["custombuttons-contextpopup-copyAsHTML",
            "custombuttons-contextpopup-copyAsHTML-sub"];
addMenuItem(f3IDs, b2IDs, "Копировать код кнопки как HTML ссылку", saveImg3,
            "document.getElementById('" + this.id
          + "').copyToHTMLCode();");
let f4Ds = ["custombuttons-contextpopup-copyToBBCode",
            "custombuttons-contextpopup-copyToBBCode-sub"];
addMenuItem(f4Ds, b2IDs, "Копировать код кнопки как BBcode сылку", saveImg4,
            "document.getElementById('" + this.id
          + "').copyToBBCode();");          
let f5Ds = ["custombuttons-contextpopup-saveAsHTML",
            "custombuttons-contextpopup-saveAsHTML-sub"];
addMenuItem(f5Ds, bIDs, "Сохранить код кнопки в HTML файл", saveImg5,
            "document.getElementById('" + this.id
          + "').saveToHTMLCode();");
let f8Ds = ["custombuttons-contextpopup-saveAsHTMLAll",
            "custombuttons-contextpopup-AsHTMLAll-sub"];
addMenuItem(f8Ds, f5Ds, "Сохранить все кнопки в HTML файл", saveAll,
            "document.getElementById('" + this.id
          + "').saveToHTMLALLCode()");          
let f6Ds = ["custombuttons-contextpopup-getButtonId",
            "custombuttons-contextpopup-getButtonId-sub"];
let b1IDs = ["custombuttons-contextpopup-remove",
            "custombuttons-contextpopup-remove-sub"];            
addMenuItem(f6Ds, b1IDs, "Показать Id кнопки", saveImg6,
            "document.getElementById('" + this.id
          + "').idMIonclick(window.document);");
let f7Ds = ["custombuttons-contextpopup-addNextButton",
             "custombuttons-contextpopup-addNextButton-sub"];
   
                                        
var addMI = document.getElementById('custombuttons-contextpopup-addnewbutton');
addMI.setAttribute('image', iconADD);
var addMI1 = document.getElementById('custombuttons-contextpopup-addnewbutton-sub');
addMI1.setAttribute('image', iconADD);
var addMI2 = document.getElementById('custombuttons-contextpopup-move-moveToPanel');
addMI2.setAttribute('image', moveToMenu);
var addMI3 = document.getElementById('custombuttons-contextpopup-move-removeFromToolbar');
addMI3.setAttribute('image', removeFromToolbar);
var addMI4 = document.getElementById('custombuttons-contextpopup-customize');
addMI4.setAttribute('image', customBtn);
//var addMI5 = document.getElementById('custombuttons-contextpopup-remove');
//addMI5.setAttribute('label', 'Удалить...');

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////// Общие функци ////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

var options1 = {year: "numeric"};
var options2 = {day: "numeric", month: "long"};
var cDate = new Date().toLocaleDateString("ru-RU", options1);
var dDate = new Date().toLocaleDateString("ru-RU", options2);
var aDate = cDate + "г" + " " + dDate;

var options3 = {weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false};
var bDate = new Date().toLocaleDateString("ru-RU", options3);

var saveToFile = function (fileContent, fileName) {
	var uc = Components.classes['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
	uc.charset = 'utf-8';
	fileContent = uc.ConvertFromUnicode(fileContent);

	var nsIFilePicker = Components.interfaces.nsIFilePicker;
	var fp = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
	fp.init(window, '', fp.modeSave);
	fp.defaultString = fileName;
	fp.appendFilters(fp.filterHTML);
	fp.appendFilters(fp.filterAll);
        fp.open(function (rv) {
        if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
              var stream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream);
              stream.init(fp.file, 0x02|0x20|0x08, 0666, 0);
              stream.write(fileContent, fileContent.length);
             stream.close();
             }
         });
};

function  mostRecentWindow(windowType) {
  return Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow(windowType);
};
/////////////////////////////////////////////////////////////////////////////
////////////////////////////Остальные функци ////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////// Показать Id кнопки ///////////////////////////////

this.idMIonclick = function idMIonclick() {
  var btn = custombuttons.popupNode.id;
  var box = custombuttons.confirmBox("Копировать в буфер", btn, "Да", "Нет");
  if (box) {
    custombuttons.cbService.writeToClipboard(btn);
    custombuttons.alertSlide(btn, "Скопирована в буфер");
} }


/////////////////////////////// Копировать изображение кнопки в формате base64 ///////////////////////////////

this.copyImageURI = function copyImageURI() {
	var btn = custombuttons.popupNode;
	if (!btn) return;
	cbu.gClipboard.write(btn.image);
  var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
  as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "Изображение кнопки скопировано в буфер", false, "", null);
}

/////////////////////////////// Копировать код кнопки как текст ///////////////////////////////

this.copyButtonsCodeText = function copyButtonsCodeText() {
  var btn = custombuttons.popupNode;
  if (!btn) return;
  var code = ((btn.cbCommand == "") || (btn.Command == "/*CODE*/"))
              ? ""
              : ("\n/*CODE*/\n" + btn.cbCommand + "\n");
	var init = ((btn.cbInitCode == "") || (btn.cbInitCode == "/*Initialization Code*/"))
              ? ""
              : ("\n/*Initialization Code*/\n" + btn.cbInitCode);
	cbu.gClipboard.write(code + init);
	//custombuttons.alertSlide(btn.name, "Код скопирван в буфер");
	var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
        as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "Код скопирван в буфер", false, "", null);

}

/////////////////////////////// Копировать код кнопки как HTML ссылку ///////////////////////////////

this.copyToHTMLCode = function copyToHTMLCode() {
  var btn = custombuttons.popupNode;
  if (!btn) return;
  var code = "<p><div id=\"install\" style=\"background: transparent -moz-linear-gradient(center top , rgb(224, 102, 255) 30%, rgb(125, 38, 205) 55%); text-shadow: 0pt -1px 0pt rgb(122, 55, 139); border: 1px outset rgb(85, 26, 139); border-radius: 1em; padding: 0; width: 240px; text-align: center;\"><a href=\"" + btn.URI + "\" style=\"display: block; padding: 1em; color: #ffffff; text-decoration: none;\" title=\"Click here to install " + btn.name + "\" rel=\"nofollow\"><img src=\"" + btn.image + "\" alt=\"" + btn.name + "\" style=\"vertical-align: middle; float: left;\"/>" + btn.name + "</a></div></p>";
  cbu.gClipboard.write(code);
  var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
  as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "HTML кнопки скопирован в буфер", false, "", null);

}

/////////////////////////////// Копировать код кнопки как BBcode сылку ///////////////////////////////

this.copyToBBCode = function copyToBBCode() {
  var btn = custombuttons.popupNode;
  if (!btn) return;
  var code = "[url=" + btn.URI + "][B]" + btn.name + "[/B][/url]";
  cbu.gClipboard.write(code); //.toXMLString());
  var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
  as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "BBCode кнопки скопирован в буфер", false, "", null);
}

/////////////////////////////// Сохранить изображение кнопки custombutton ///////////////////////////////

this.saveImageURI = function saveImageURI() {
	var gb = gBrowser;
	var remove = gb.removeCurrentTab.bind(gb);

	var promiseTargetFile = async (...args) => {
		var info = args[0].fileInfo;
		info.fileName = `${gb.selectedTab.label}.${info.fileExt}`;

		var res = await window.promiseTargetFile(...args);
		setTimeout(remove, 0);
		return res;
	}
	var internalSave = eval(`(${window.internalSave})`);
	var saveBrowser = eval(`(${window.saveBrowser})`);
	var save = (tab, name) => {
		(gb.selectedTab = tab).label = name;
		saveBrowser(tab.linkedBrowser);
	}
	(self.saveImageURI = () => {
		var btn = custombuttons.popupNode;
		if (!btn) return;
		if (btn.image == "") return custombuttons.alertBox("Эта кнопка не имеет изображения!");
		
		var tab = gb.selectedTab = gb.addTrustedTab(btn.image);
		setTimeout(save, 2e3, tab, btn.name || "image");
	})();
}



/////////////////////////////// Сохранить кнопку в XML файл ///////////////////////////////

this.saveXML = function saveXML(aStrURI) {
  var cbURI = (aStrURI != undefined) ? aStrURI : readFromClipboard();
  if (!cbURI || !/^custombutton\:\/\//.test(cbURI)) {
    custombuttons.uChelpButton(this);
    return;
  }
var topicURL = "http://forum.mozilla-russia.org/viewforum.php?id=34"
  var cbXML = cbURI.replace(/^custombutton\:\/\//, "");
  var decodeXML = unescape(cbXML);
  var btnName = decodeXML.match(/\<name\/?.+/).toString();
  var name = "untitled";
  if (!/\<name\/\>/.test(btnName)) {
    name = btnName.replace(/\<\/?\w+\>/g, "").toString();
  }
  var image = decodeXML.match(/\<image\/?.+/).toString();
  var icon = "";
  if (!/\<\image.*\[\].*\>$/.test(image)) {
    icon = image.match(/[^\[\]]+/g)[2].toString()
                .replace(/custombuttons\-stdicon\-\d/, "").toString();
  }

  function htmlEntities(str) {
      return str.replace(/&/g, "&amp;").replace(/</g, "&lt;")
                .replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  }

  var xmlTemplate = "custombuttons/\"\n\
              xmlns:html=\"http://www.w3.org/1999/xhtml\">\n\
  <html:head>\n\
    <html:title><![CDATA[" + name + "]]></html:title>\n\
    <html:link rel=\"shortcut icon\" href=\"" + icon + "\"/>\n\
    <html:style type=\"text/css\"><![CDATA[\n\
body { font-size: medium; margin: 0; }\n\
body, code:before, help:before, initcode:before {\n\
  font-family: \"Verdana\", sans-serif;\n\
}\n\
#wrapper { position: fixed; top: 1em; right: 1em; text-align: center; }\n\
p { font-size: small; text-align: center; }\n\
#button {\n\
  background-color: rgb(85, 168, 2);\n\
  background-image: linear-gradient(to bottom, rgb(147, 200, 94),\
 rgb(85, 168, 2));\n\
  background-image: -moz-linear-gradient(top, rgb(147, 200, 94),\
 rgb(85, 168, 2));\n\
  background-image: -o-linear-gradient(top, rgb(147, 200, 94),\
 rgb(85, 168, 2));\n\
  background-image: -webkit-linear-gradient(top, rgb(147, 200, 94),\
 rgb(85, 168, 2));\n\
  border: 1px solid rgb(58, 116, 4);\n\
  border-radius: .5em;\n\
  -moz-border-radius: .5em;\n\
  -webkit-border-radius: .5em;\n\
  padding: 0;\n\
  margin-bottom: 1em;\n\
  box-shadow: 1px 2px 3px rgba(0, 0, 0, .25);\n\
  -moz-box-shadow: 1px 2px 3px rgba(0, 0, 0, .25);\n\
  -o-box-shadow: 1px 2px 3px rgba(0, 0, 0, .25);\n\
  -webkit-box-shadow: 1px 2px 3px rgba(0, 0, 0, .25);\n\
}\n\
#button a {\n\
  color: #000;\n\
  text-shadow: -1pt -1px 0pt rgba(255, 255, 255, .5);\n\
  padding: 1em;\n\
  text-decoration: none;\n\
}\n\
:-moz-any-link:focus {\n\
  color: white;\n\
  outline-color: transparent;\n\
  text-decoration: none;\n\
}\n\
#button a, code, code:before, initcode, initcode:before, help, help:before {\
\n  display: block;\n\
}\n\
#credits { position: fixed; bottom: 1em; right: 1em; font-size: small; }\n\
custombutton { background-color: rgb(171, 171, 171); margin: 1em; }\n\
date, image, mode, accelkey { display: none; }\n\
name { font-weight: bold; font-size: x-large; }\n\
code:before, help:before, initcode:before {\n\
  font-weight: bold;\n\
  font-size: large;\n\
  margin: 0 0 1em;\n\
  padding: .5em;\n\
}\n\
code:before { content: \"Код\"; }\n\
help:before { content: \"Справка\"; }\n\
initcode:before { content: \"Инициализация\"; }\n\
code, initcode, help {\n\
  background-color: rgb(255, 255, 255);\n\
  border: 1px inset rgb(170, 170, 170);\n\
  font: medium monospace;\n\
  margin: 1em 1em 2em 0;\n\
  padding: 1em;\n\
  text-align: left;\n\
  width: 840px;\n\
  white-space: pre-wrap;\n\
  word-wrap: break-word;\n\
}\n\
.clear { clear: both; }\n\
]]></html:style>\n\
  </html:head>\n\
  <html:body>\n\
    <html:div id=\"wrapper\">\n\
      <html:div id=\"button\">\n\
        <html:a href=\"" + cbURI + "\" rel=\"nofollow\" title=\"Установить " +
        htmlEntities(name, "ENT_COMPAT") +"\">\n\
        <![CDATA[Установить кнопку]]>\n\
        </html:a>\n\
      </html:div>\n\
      <html:div id=\"credits\">\n\
        <html:a href=\"" + topicURL +"\">\n\
          <![CDATA[Другие кнопки]]><html:br/>\
<![CDATA[на форуме Mozilla Россия]]>\n\
        </html:a>\n\
      </html:div>\n\
    </html:div>\n\
  </html:body>";

  decodeXML = decodeXML.replace(/custombuttons\/\"\>/, xmlTemplate);

  name += ".xml";
  saveToFile(decodeXML, name);
 var btn = document.popupNode;
 var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
 as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "сохранена в XML файл", false, "", null);
}
var mrw = mostRecentWindow('navigator:browser');
var css = '@-moz-document url("chrome://browser/content/browser.xhtml"){' + this.Help + '}';
var uri = makeURI('data:text/css,' + encodeURIComponent(css));
var sss = Components.classes['@mozilla.org/content/style-sheet-service;1'].getService(Components.interfaces.nsIStyleSheetService);
if (!sss.sheetRegistered(uri, sss.USER_SHEET)) sss.loadAndRegisterSheet(uri, sss.USER_SHEET);

/////////////////////////////// Добавить кнопку из XML файл ///////////////////////////////

this.loadXML = function loadXML() {
  var nsIFilePicker = Ci.nsIFilePicker;
  var fp = window.makeFilePicker();
  fp.init(window, "Установить кнопку из XML файла",
          nsIFilePicker.modeOpen);
  fp.appendFilters(fp.filterXML);
  fp.appendFilter("Все файлы", "*.*");
  fp.open(re=> { 
      if ( re == fp.returnOK ) gBrowser.selectedTab = gBrowser.addTrustedTab(fp.file.path);
   })

}


/////////////////////////////// Сохранить все кнопки в HTML файл ///////////////////////////////

this.saveToHTMLALLCode = function saveToHTMLALLCode() {
 var visibleCBbuttons = [...document.querySelectorAll('[cb-mode]')];
   var paletteCBbuttons = [...custombuttons.palette.querySelectorAll('[cb-mode]')];
   var allCBbuttons = visibleCBbuttons.concat(paletteCBbuttons);

var gn = btn => btn.getAttribute("label") || "Без названия";
allCBbuttons.sort((a, b) => gn(a).localeCompare(gn(b)));

var array = [];
   allCBbuttons.forEach(but=> {
      var uri = but.URI ? but.URI : getPaleteButtonsURI(but);
      var name1 = but.getAttribute("label") || "Без названия";
      var image = but.getAttribute("image") || "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACNFBMVEXDRgDweQDnbwC0NgDCRQC7PQDtpTu+QQD78q3PUwDCRAD//8vbYQDLTgDocAD0iQX1jQbGSgD7iAD4gwDVaw7vdwDyegD+igDweQDlawDyhAXveADmZwDzmBbtcwDwkhHveADmhBTkgxbweADkfRH+igD7hADXZQX0gADyjA/tfgvveQDiawDQVADRXgfnbwD1jAb9iQD9z0PVZAbxpinykRHtdgD5mRbERQDDRgDOYArCRAD9iQD3ewDxegD3dwDFRgDAQAD+hgD4fQDucgDtgQftfwTISQDCRQDvdgD5lxb1kRb2qh7wnynkagD766LDRQDDRwDDRQDspjbtpzbuqDbvqzv//8DUZAbCRQD//83pjhvveQbvggW+QQDfcxLlmUb//8veYADMTwDHSADVVwC8PgDwixb78q3oZgDUWADRVADCRQD9iQDERwDBQQDYVQC+PgC0NgDbYQDWWgDUWADSVQDFSAC+QAC7PAC0NgD+wRL+/kD+/zb+7i3+8Cv+/DX+wBL/+DD/rwz+qgz+rwz/tAz+sQzfbAT+3iP/+jD//Dz1jwP+nwf5iwf7pBz2tRr4lwb/ogfsdQT+2R7/9ET+2iT8yyLmgBL5iQL2egLokhP7zzT60zv1zUr0gAL1ewP50yn9+UL++EXooyngaAH41EPzowzyySj+xxzlhhHvvyruwjXxvTnUag3+zxrwigP+swz86U/ZXwD75lHqegD6xzb//kn+uhPTXAG3OAD/+UC2vFeJAAAAe3RSTlMAAAAAAAAAAAAAAAAAAAAAAAAAAACfnwAIaelpAAA2+wD7UQAAa3oArKzRzMysnwgAvPQA/gAAw3prw0oALr16vS4AACnF6bspAAAA/mIAADQAAAAAAI8AAEwA0vv7kd2yAuuvr+vKABL23U+8Sk/d9vUDIIAQAAAQgCAfvupHAAABB0lEQVR4Xi3IU3fDABgA0C+u26FdZ9u2bdu2vZSzbds2/tyac3IfLxjpGyA6MimqY6gHTNgiiNwkwRM1E7EhTrYyTfMzzsh0ZEPiH+D6+u5mV1jEhpe5haVCYW1T4suEvUOkk7PLpVJ5WFnl7hEUDN5LPvsHJ6cazdn13f3DYxu0B35+/Y2qVSr17NzGeUgohIVHLK/PMDa39qKieRATGxd/9cS4uU1MwnmQktqY/qGlaVr7/ZOVDTmQm5df8PwyNj4xOTVdjOM4kCRZOr+wWFa+srpWQVEU8PnV2zu7NbV19UfHDQRBAIfTdNHc0soVdnR2dfdgGAgEvW99/QNc4eDQ8O8Ihv0D77NPgbVLZ6kAAAAASUVORK5CYII=";
      
      array.push("<li>\n<img src=" + image + ">&nbsp\<a href=" + uri + ">" + name1 +"</a><br>\n");
   });
   
   var before = "<html>\n<head>\n<title>Custom Buttons</title>\n<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\n";
   var before1 = "<style type='text/css'>\nbody {background: beige;} a:link {color: black; text-decoration: none;} img {border: 0; margin: 0px 10px;}\n</style>\n";
   var before2 = "</head>\n<body>\nCustom Buttons\n<p>\n";
   var info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
   var before3 = info.vendor + " " + info.name + " " + info.version + " (build " + info.appBuildID + ")\n";
   var after = "\n</ol>\n</body>\n</html>";
	
   var text = before + before1 + before2 + before3 + "<p>\n" + bDate + "<p>\n" + "<ol>" + array.join("") + after;
   var name = "CB buttons " + aDate + ".html"
   saveToFile(text, name);

   var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
   alertsService.showAlertNotification(saveImg5, "Экспорт в HTML", "Экспортировал все CB кнопки как HTML");
   setTimeout(()=> alertsService.closeAlert(), 4000);
};


function getPaleteButtonsURI(but) {

   var uri = "chrome://custombuttons/content/nbftemplate.xml";
   var stream = NetUtil.newChannel({uri, loadUsingSystemPrincipal: true}).open();
   var doc = new DOMParser().parseFromStream(stream, null, stream.available(), "application/xml");
   stream.close();
 
["help,Help", "name,label", "image,image", "mode,cb-mode", "initcode,cb-init", "accelkey,cb-accelkey", "code,cb-oncommand"]
   .forEach(str=> {
      var arr = str.split(',');
      var value = but.getAttribute(arr[1]), name = arr[0];
      custombutton.buttonSetText(doc, name, value, true);
   });

   var ser = new XMLSerializer();
   return "custombutton://" + escape(ser.serializeToString(doc));
};


/////////////////////////////// Сохранить кнопку в HTML файл ///////////////////////////////

this.saveToHTMLCode = function saveToHTMLCode() {
  var btn = custombuttons.popupNode;
  var xml = '<html xmlns="' + xhtmlns + '">\n';
      xml += '<head>\n';
      xml += '<meta http-equiv = "Content-Type" content = "text/html; charset=utf-8"/>\n';
      xml += '<title> ' + btn.name + '  для Custom Buttons </title>\n';
      xml += '<link rel="icon" type="image/vnd.microsoft.icon" href = "'+ btn.image +'" />\n';
      xml += '<style type="text/css">\n';
      xml += '.button a{ \n';
      xml += 'background-color: rgb(85, 168, 2); \n';
      xml += 'background-image: linear-gradient(to bottom, rgb(147, 200, 94), rgb(85, 168, 2)); \n';
      xml += 'background-image: -moz-linear-gradient(top, rgb(147, 200, 94), rgb(85, 168, 2)); \n';
      xml += 'border: 1px solid rgb(58, 116, 4); \n';
      xml += 'border-radius: .5em; \n';
      xml += ' -webkit-border-radius: .5em; \n';
      xml += 'padding: 0; \n';
      xml += 'margin-bottom: 1em; \n';
      xml += 'box-shadow: 1px 2px 3px rgba(0, 0, 0, .25); \n';
      xml += ' -o-box-shadow: 1px 2px 3px rgba(0, 0, 0, .25); \n';
      xml += ' -webkit-box-shadow: 1px 2px 3px rgba(0, 0, 0, .25); \n';
      xml += ' color: #000; \n';
      xml += ' text-shadow: -1pt -1px 0pt rgba(255, 255, 255, .5); \n';
      xml += ' padding: 0.5em; \n';
      xml += ' text-decoration: none; \n';
      xml += '} ';
      xml += 'pre { border: 1px inset rgb(170, 170, 170); \n';
      xml += 'background-color: rgb(255, 255, 255);} \n';
      xml += 'body { background-color: rgb(245, 245, 220);} \n';
      xml += '</style> \n';
      xml += '</head>\n';
      xml += '<body>\n';
      xml += '<section id="install"><h1> ' + btn.name + ' </h1> \n';
      xml += '</section>\n';
      xml += '<div class="button"><a href = "' + btn.URI + '">Установить кнопку</a></div> \n';
      xml += '<section id="init"><h2>Инициализация</h2><pre>' + e4xConv_encodeHTML(btn.cbInitCode) + '</pre></section>\n';
      xml += '<section id="code"><h2>Код</h2><pre>' + e4xConv_encodeHTML(btn.cbCommand) + '</pre></section> \n';
      xml += '<section id="help"><h2>Справка</h2><pre>' + e4xConv_encodeHTML(btn.Help) + '</pre></section> \n';
      xml += '</body> \n';
      xml += '</html> ';
  var html = '<!DOCTYPE html>\n' + xml;
  var name = btn.name + ".HTML";
  saveToFile(html, name);
  var as = Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService);
  as.showAlertNotification(btn.image, "Кнопка: " + btn.name, "сохранена в HTML файл", false, "", null);
}

function e4xConv_encodeHTML(s, isAttr) {
	s = String(s)
		.replace(/&/g, "&amp;")
		.replace(/</g, "&lt;")
		.replace(/>/g, "&gt;")
		.replace(/"/g, "&quot;");
	if(isAttr) {
		s = s
			.replace(/\t/g, "&#x9;")
			.replace(/\n/g, "&#xA;")
			.replace(/\r/g, "&#xD;");
	}
	return s;
};

/////////////////////////////// Переместить кнопку ///////////////////////////////

this.MoveToolbarButtons = MTB = {
	// Start editable preferences
	MoveButtonMICBContext: true, // Add "Move button" menu item to Custom Buttons context menu?
	MoveButtonMITBarContext: false, // Add "Move button" menu item to toolbars context menu?
	ShowMoveAlert: false, // Show the alert with instructions when moving buttons?
	MoveBtnImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAYklEQVQ4jWNgGJTgaAXHf2LEcGrGZQBBQ45WcPz/fZ0XpwG45FAUEDIAqzyyJLIiZIxNHqdmYvHRCo7/DLhsIEYziivQDcHnBawxgqyQUCzgjQlC6YCotEB2QkJWTIzY4AAAezv/caeCLKcAAAAASUVORK5CYII=",
	// End editable preferences

	_UID: "__cb_move_toolbar_buttons_" + custombuttons.getNumber(self.id),
	WindowIDs: ["main-window", "messengerWindow", "msgcomposeWindow"],
	ToolbarContexIDs: ["toolbar-context-menu", "aios-toolbar-contextmenu", "aios-sbhtoolbar-contextmenu"],
	Pref: "extensions.custombuttons.MoveToolbarButtons.Id" + custombuttons.getNumber(self.id) + ".MoveButtonID",
	SP: Services.prefs,

	Init: function() {
		if (!(MTB._UID in window)) {
			window[MTB._UID] = {
				Initialized: false
			};
		}

		if (!window[MTB._UID]["Initialized"]) {
			window[MTB._UID]["Initialized"] = true;
			custombuttons.isPref(MTB.Pref, "");

			if ("CustomizableUI" in window) {
				var originalFunction = custombuttons.persistCurrentSets.toString();
				eval("custombuttons.persistCurrentSets=" + originalFunction
					.replace(", newButtonId)", ", newButtonId, aToRight)")
					.replace("pos + 1", "aToRight ? pos : pos + 1"));
			}

			if (MTB.MoveButtonMICBContext) {
				var contextPops = document.getElementsByTagName("menupopup");
				let p = 0,
					pLen = contextPops.length;
				for (; p < pLen; p++) {
					var cPopID = contextPops[p].id;
					if (cPopID.substr(0, 26) === "custombuttons-contextpopup") {
						var cPopEl = document.getElementById(cPopID);
						var moveMIID = "custombuttons-contextpopup-moveButton" + cPopID.slice(26);
						var moveMIEl = document.getElementById(moveMIID);
						if (!moveMIEl)
							MTB.CreateMoveMI(moveMIID, cPopEl);
					}
				}
			}

			if (MTB.MoveButtonMITBarContext) {
				Array.prototype.slice.call(MTB.ToolbarContexIDs).forEach(function(aTBCtxID, aIndex) {
					let Ctx = document.getElementById(aTBCtxID);
					let tBarCtxMIID = MTB._UID + "moveButton" + aIndex;
					if (!document.getElementById(tBarCtxMIID)) {
						if (Ctx) {
							MTB.CreateMoveMI(tBarCtxMIID, Ctx);
							Ctx.addEventListener("popupshowing", function(aE) {
								document.getElementById(tBarCtxMIID).hidden = (document.popupNode.tagName !== "toolbarbutton");
							}, false);
						}
					}
				});
			}

			addDestructor(function(aReason) {
				if (aReason === "delete") {
					SP.resetUserPrefs(MTB.Pref);
					delete window[_UID];
				}
				if (aReason === "delete" || aReason === "update") {
					window[MTB._UID]["Initialized"] = false;
				}
			});
		}
	},

	getPopupNode: function(aEl) {
		if (custombuttons.popupNode)
			return custombuttons.popupNode;
		var popupNode = aEl;
		while (popupNode.tagName.toLowerCase() !== "toolbarbutton") {
			popupNode = popupNode.parentNode;
		}
		return popupNode;
	},

	CreateMoveMI: function(aMItemID, aPopupEl) {
		let moveMIEl = aPopupEl.appendChild(document.createElementNS(xulns, "menuitem"));
		moveMIEl.setAttribute("id", aMItemID);
		moveMIEl.setAttribute("label", "Переместить кнопку...");
		moveMIEl.setAttribute("class", "menuitem-iconic");
		moveMIEl.setAttribute("image", MTB.MoveBtnImage);
		moveMIEl.setAttribute("tooltip", MTB._UID + "moveButtonMI_tooltip");
		moveMIEl.setAttribute("onclick", "document.getElementById(\"" +
			self.id + "\").MoveToolbarButtons.MoveOnClick(event);");
	},

	MoveOnClick: function(aE) {
		var popupNode = MTB.getPopupNode(aE.target);
		if (!popupNode)
			return;
		aE.stopPropagation();
		aE.preventDefault();
		if (aE.button === 0 && !aE.shiftKey && !aE.ctrlKey && !aE.altKey) {
			MTB.SP.setCharPref(MTB.Pref, popupNode.id);
			window.addEventListener('click', MTB.MoveListener, true);
			window.addEventListener('mouseup', MTB.DefaultPrevention, true);
			window.addEventListener('mousedown', MTB.DefaultPrevention, true);
			window.addEventListener('contextmenu', MTB.DefaultPrevention, true);
			MTB.HidePopup(aE.target);
			MTB.ShowMoveAlert && Services.prompt.alert(null, self.name,
				"Now click ANY toolbarbutton element inside ANY toolbar.\n" +
				"Left click will position the button to the left of the target.\n" +
				"Right click will position the button to the right of the target.\n\n" +
				"To cancel the movement, just click ANY element inside the browser that IS NOT a toolbarbutton.\n");
		}
	},

	MoveListener: function(aE) {
		MTB.DefaultPrevention(aE);
		window.removeEventListener('click', MTB.MoveListener, true);
		window.removeEventListener('mouseup', MTB.DefaultPrevention, true);
		window.removeEventListener('mousedown', MTB.DefaultPrevention, true);
		window.removeEventListener('contextmenu', MTB.DefaultPrevention, true);
		var anchor = aE.target;
		if (anchor.tagName !== "toolbarbutton")
			return;
		var toolbar = anchor.parentNode;
		var BtnToMove = document.getElementById(MTB.SP.getCharPref(MTB.Pref));
		if (aE.button === 0 && !aE.shiftKey && !aE.ctrlKey && !aE.altKey)
			toolbar.insertBefore(BtnToMove, anchor);
		else if (aE.button === 2 && !aE.shiftKey && !aE.ctrlKey && !aE.altKey)
			toolbar.insertBefore(BtnToMove, anchor.nextSibling);
		if ("CustomizableUI" in window)
			custombuttons.persistCurrentSets(toolbar.id, anchor.id,
				BtnToMove.id || BtnToMove.getAttribute("id"), !(aE.button === 2));
		else {
			toolbar.setAttribute("currentset", toolbar.currentSet);
			document.persist(toolbar.id, "currentset");
		}
		MTB.SP.setCharPref(MTB.Pref, "");
	},

	DefaultPrevention: function(aE) {
		aE.preventDefault();
		aE.stopImmediatePropagation && aE.stopImmediatePropagation();
		aE.stopPropagation();
	},

	Tooltips: function(aRem) {
		var popSetID = MTB._UID + "popupset";
		var popSetEl = document.getElementById(popSetID);
		if (popSetEl)
			popSetEl.parentNode.removeChild(popSetEl);

		if (aRem)
			return;

		if (!popSetEl) {
			popSetEl = document.createElementNS(xulns, "popupset");
			popSetEl.setAttribute("id", popSetID);
		}

		popSetEl.appendChild(MTB.parseXML("<tooltip xmlns=\"" + xulns + "\" xmlns:html=\"" + xhtmlns +
			"\" id=\"" + MTB._UID + "moveButtonMI_tooltip" + "\">" +
			"<description><html:b>Instructions</html:b> : After clicking this menu item, you can " +
			"click ANY toolbarbutton element inside the application to place the currently " +
			"selected button to the left (with Left click) or to the right (with Right click) " +
			"of the targeted toolbarbutton.</description>" +
			"<separator/>" +
			"<description><html:b>Note</html:b> : It can be ANY toolbarbutton, not just " +
			"other Custom Buttons.</description>" +
			"</tooltip>"));

		setTimeout(function() {
			Array.prototype.slice.call(MTB.WindowIDs).forEach(function(aWinID) {
				let win = document.getElementById(aWinID);
				if (win)
					!document.getElementById(popSetID) && win.appendChild(popSetEl);
			});
		}, 100);
	},

	parseXML: function(aXML) { // Return parsed XML
		aXML = aXML.replace(/>\s+</g, "><"); // Linearize XML
		return (new DOMParser).parseFromString(aXML, "application/xml").documentElement;
	},

	HidePopup: function(aEl) {
		try {
			aEl.hidePopup();
		} catch (aError) {
			try {
				aEl.parentNode.hidePopup();
			} catch (aError) {
				try {
					aEl.parentNode.parentNode.hidePopup();
				} catch (aError) {
					try {
						aEl.parentNode.parentNode.parentNode.hidePopup();
					} catch (aError) {}
				}
			}
		}
	}
};

this.MoveToolbarButtons.Init();

старая, но еще живая, ее бы по хорошему переписать бы пора

Отсутствует

 

№1689401-06-2023 15:05:07

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

Re: Custom Buttons

Да, работает на 114. Премного благодарен.

Добавлено 01-06-2023 15:22:42
Andrey_Krropotkin
Если можно, еще один вопрос.
Нужна рабочая версия [CB]Toggle Restartless Add-ons

Отредактировано Garalf (01-06-2023 15:24:22)

Отсутствует

 

№1689501-06-2023 18:39:44

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

Re: Custom Buttons

Garalf у меня на 113 такая

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

Выделить код

Код:

// http://infocatcher.ucoz.net/js/cb/toggleRestartlessAddons.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57948
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Toggle_Restartless_Add-ons

// Toggle Restartless Add-ons button for Custom Buttons
// (code for "initialization" section)
// Also the code can be used from main window context (as Mouse Gestures code, for example)

// Also you can check for add-ons updates using right-click:
// copy all code from
// https://github.com/Infocatcher/Custom_Buttons/blob/master/Check_for_Addons_Updates/checkForAddonsUpdates.js
// after "//== Check for Addons Updates begin"

// See "var style = " to modify styles for specific add-ons

// (c) Infocatcher 2013-2019
// version 0.1.3pre4 - 2020-01-01

var options = {
	addonTypes: ["extension"],
	// Possible values: "extension", "plugin"
	// From extensions: "userstyle" (Stylish), "greasemonkey-user-script" (Greasemonkey), "userscript" (Scriptish)
	// (swap to reorder in the menu)
	showVersions: 1,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "this.handleEvent(event);");
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

var tb = this.parentNode;
if(tb && tb.getAttribute("orient") == "vertical") {
	// https://addons.mozilla.org/firefox/addon/vertical-toolbar/
	var isRight = tb.parentNode.getAttribute("placement") == "right";
	mp.setAttribute("position", isRight ? "start_before" : "end_before");
}

var cleanupTimer = 0;
mp.updateMenu = function() {
	clearTimeout(cleanupTimer);
	addStyle();
	getRestartlessAddons(options.addonTypes, function(addons) {
		var df = document.createDocumentFragment();
		var prevType;
		function sortPosition(addon) {
			if("STATE_ASK_TO_ACTIVATE" in AddonManager && addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE)
				return options.sort.clickToPlay;
			if(addon.isActive)
				return options.sort.enabled;
			return options.sort.disabled;
		}
		function key(addon) {
			return options.addonTypes.indexOf(addon.type)
				+ "\n" + sortPosition(addon)
				+ "\n" + addon.name.toLowerCase();
		}
		addons.sort(function(a, b) {
			var ka = key(a);
			var kb = key(b);
			return ka == kb ? 0 : ka < kb ? -1 : 1;
		}).forEach(function(addon) {
			var type = addon.type;
			if(prevType && type != prevType)
				df.appendChild(document.createElementNS(xulns, "menuseparator"));
			prevType = type;
			var icon = addon.iconURL || addon.icon64URL;
			var mi = document.createElementNS(xulns, "menuitem");
			mi.className = "menuitem-iconic";
			var label = addon.name;
			if(options.showVersions == 1)
				label += " " + addon.version;
			else if(options.showVersions == 2)
				mi.setAttribute("acceltext", addon.version);
			mi.setAttribute("label", label);
			mi.setAttribute("image", icon || mp.icons[type] || "");
			if(!icon && mp.icons.useSVG)
				mi.style.fill = "#15c";
			var tip = addon.description || "";
			var delay = "delayedStartupAddons" in Services
				&& Services.delayedStartupAddons[addon.id] || null;
			var isDelayed = delay !== null;
			mi.classList.toggle("toggleRestartlessAddons-isDelayed", isDelayed);
			if(isDelayed)
				tip = "[Delayed Startup: " + delay.toLocaleString() + "]" + (tip ? "\n" + tip : "");
			tip && mi.setAttribute("tooltiptext", tip);
			mi.classList.toggle("toggleRestartlessAddons-isHidden", addon.hidden || false);
			setDisabled(mi, addon.userDisabled);
			mi._cbAddon = addon;
			df.appendChild(mi);
		});
		mp.textContent = "";
		mp.appendChild(df);
	});
};
mp.handleEvent = function(e) {
	var mi = e.target;
	if(!("_cbAddon" in mi))
		return;
	var addon = mi._cbAddon;
	if(e.type == "mousedown") {
		var closeMenu = isAskToActivateAddon(addon)
			? options.closeMenuClickToPlay
			: options.closeMenu;
		if(e.shiftKey)
			closeMenu = !closeMenu;
		mi.setAttribute("closemenu", closeMenu ? "auto" : "none");
		return;
	}
	var hasMdf = hasModifier(e);
	if(e.type == "command" && (!hasMdf || e.shiftKey)) {
		let newDis = setNewDisabled(addon);
		setDisabled(mi, newDis);
	}
	else if(e.type == "command" && hasMdf || e.type == "click" && e.button == 1) {
		openAddonPage(addon);
		closeMenus(mi);
	}
	else if(e.type == "click" && e.button == 2) {
		if(openAddonOptions(addon))
			closeMenus(mi);
	}
};
mp.destroyMenu = function() {
	removeStyle();
	clearTimeout(cleanupTimer);
	cleanupTimer = setTimeout(function() {
		mp.textContent = "";
	}, 5000);
};
mp.icons = {
	get platformVersion() {
		delete this.platformVersion;
		return this.platformVersion = parseFloat(Services.appinfo.platformVersion);
	},
	get useSVG() {
		delete this.useSVG;
		return this.useSVG = Services.appinfo.name == "Firefox" && this.platformVersion >= 57;
	},
	get plugin() {
		delete this.plugin;
		return this.plugin = this.useSVG
			? this.platformVersion >= 65
				? "chrome://global/skin/plugins/pluginGeneric.svg"
				: "chrome://mozapps/skin/plugins/pluginGeneric.svg"
			: "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},
	get extension() {
		delete this.extension;
		return this.extension = this.useSVG
			? this.platformVersion >= 76
				? "chrome://mozapps/skin/extensions/extensionGeneric.svg" // Or chrome://mozapps/skin/extensions/extension.svg
				: "chrome://mozapps/skin/extensions/extensionGeneric-16.svg"
			: "chrome://mozapps/skin/extensions/extensionGeneric-16.png";
	}
};
function isAskToActivateAddon(addon) {
	return addon.type == "plugin"
		&& "STATE_ASK_TO_ACTIVATE" in AddonManager
		&& Services.prefs.getBoolPref("plugins.click_to_play", true);
}
function setNewDisabled(addon) {
	var newDis = getNewDisabled(addon);
	var oldDis = addon.userDisabled;
	try {
		addon.userDisabled = newDis;
	}
	catch(e) { // Error: Cannot disable hidden add-on firefox@getpocket.com
		_log("Can't set addon.userDisabled to " + newDis + ", error:\n" + e);
		if(addon.hidden)
			setNewDisabledRaw(addon, newDis);
	}
	var realDis = addon.userDisabled;
	if(realDis != newDis && addon.type == "extension") { // Firefox 62+? Weird things happens
		setNewDisabledRaw(addon, newDis);
		realDis = addon.userDisabled;
	}
	if(realDis != newDis) { // We can't enable vulnerable plugins
		let err = "Can't set addon.userDisabled to " + newDis + ", real value: " + realDis;
		if(newDis) {
			_log(err + "\nSTATE_ASK_TO_ACTIVATE not supported?");
			newDis = false;
		}
		else {
			_log(err + "\nVulnerable plugin?");
			if(oldDis == AddonManager.STATE_ASK_TO_ACTIVATE)
				newDis = true;
			else
				newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		}
		addon.userDisabled = newDis;
	}
	ensureSpecialDisabled(addon, newDis);
	return addon.userDisabled;
}
function getNewDisabled(addon) {
	// disabled -> STATE_ASK_TO_ACTIVATE -> enabled -> ...
	var curDis = addon.userDisabled;
	var newDis;
	if("STATE_ASK_TO_ACTIVATE" in AddonManager && curDis == AddonManager.STATE_ASK_TO_ACTIVATE)
		newDis = false;
	else if(!curDis)
		newDis = true;
	else {
		if(isAskToActivateAddon(addon))
			newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		else
			newDis = false;
	}
	return newDis;
}
function setNewDisabledRaw(addon, newDis) {
	_log("Let's try set addon.userDisabled using raw hack");
	let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	if("lazy" in g) g = g.lazy;

	if("XPIDatabase" in g && "updateAddonDisabledState" in g.XPIDatabase) { // Firefox 61+
		let rawAddon = g.XPIDatabase.getAddons().find(function(rawAddon) {
			return rawAddon.id == addon.id;
		});
		g.XPIDatabase.updateAddonDisabledState(
			rawAddon,
			g.XPIDatabase.updateAddonDisabledState.length == 1 // Firefox 74+
				? { userDisabled: newDis }
				: newDis
		);
	}
	else if("eval" in g) { // See "set userDisabled(val)"
		let addonFor = g.eval("addonFor");
		let rawAddon = addonFor(addon);
		//rawAddon.userDisabled = newDis;
		g.XPIProvider.updateAddonDisabledState(rawAddon, newDis);
	}
	else { // Firefox 57+? See https://forum.mozilla-russia.org/viewtopic.php?pid=745272#p745272
		updateAddonDisabledState(addon, newDis);
	}
}
function updateAddonDisabledState(addon, newDis) {
	var nsvo = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	var key = "_cbToggleRestartlessAddonsData";
	var url = URL.createObjectURL(new Blob([
		"XPIProvider.updateAddonDisabledState(addonFor(this." + key + "[0]), this." + key + "[1]); delete this." + key + ";"
	]));
	addDestructor(function() {
		URL.revokeObjectURL(url);
	});
	(updateAddonDisabledState = function(addon, newDis) {
		nsvo[key] = [addon, newDis];
		Services.scriptloader.loadSubScript(url, nsvo);
	})(addon, newDis);
}
function setDisabled(mi, disabled) {
	var askToActivate = "STATE_ASK_TO_ACTIVATE" in AddonManager && disabled == AddonManager.STATE_ASK_TO_ACTIVATE;
	var cl = mi.classList;
	cl.toggle("toggleRestartlessAddons-askToActivate", askToActivate);
	cl.toggle("toggleRestartlessAddons-disabled", disabled && !askToActivate);
}
function ensureSpecialDisabled(addon, newDis) {
	if(addon.id == "screenshots@mozilla.org")
		Services.prefs.setBoolPref("extensions.screenshots.disabled", newDis);
}

if(
	this instanceof XULElement // Custom Buttons
	&& typeof event == "object"
	&& !("type" in event) && typeof _phase == "string" && _phase == "init" // Initialization
) {
	this.type = "menu";
	this.orient = "horizontal";
	this.appendChild(mp);

	this.onmouseover = function(e) {
		if(e.target != this)
			return;
		Array.prototype.some.call(
			this.parentNode.getElementsByTagName("*"),
			function(node) {
				if(
					node != this
					&& node.namespaceURI == xulns
					// See https://github.com/Infocatcher/Custom_Buttons/issues/28
					//&& node.boxObject
					//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
					&& "open" in node
					&& node.open
					&& node.getElementsByTagName("menupopup").length
				) {
					node.open = false;
					this.open = true;
					return true;
				}
				return false;
			},
			this
		);
	};
	this.onmousedown = function(e) {
		if(e.target == this && e.button == 0 && hasModifier(e))
			e.preventDefault();
	};
	this.oncontextmenu = function(e) {
		if(e.target == this && !hasModifier(e) && hasUpdater())
			e.preventDefault();
	};
	this.onclick = function(e) {
		if(e.target != this)
			return;
		if(e.button == 0 && hasModifier(e) || e.button == 1)
			openAddonsManager();
		else if(e.button == 2 && !hasModifier(e) && hasUpdater())
			checkForAddonsUpdates.call(this);
	};
}
else { // Mouse gestures or something other...
	let e;
	if(typeof event == "object" && event instanceof Event && "screenX" in event) // FireGestures
		e = event;
	else if(
		this instanceof Components.interfaces.nsIDOMChromeWindow
		&& "mgGestureState" in window && "endEvent" in mgGestureState // Mouse Gestures Redox
	)
		e = mgGestureState.endEvent;
	else {
		let anchor = this instanceof XULElement && this
			|| window.gBrowser && gBrowser.selectedBrowser
			|| document.documentElement;
		if("boxObject" in anchor) {
			let bo = anchor.boxObject;
			e = {
				screenX: bo.screenX,
				screenY: bo.screenY
			};
			if(this instanceof XULElement)
				e.screenY += bo.height;
		}
	}
	if(!e || !("screenX" in e))
		throw new Error("[Toggle Restartless Add-ons]: Can't get event object");
	document.documentElement.appendChild(mp);
	mp.addEventListener("popuphidden", function destroy(e) {
		mp.removeEventListener(e.type, destroy, false);
		setTimeout(function() {
			mp.destroyMenu();
			mp.parentNode.removeChild(mp);
		}, 0);
	}, false);
	mp.openPopupAtScreen(e.screenX, e.screenY);
}

function getRestartlessAddons(addonTypes, callback, context) {
	if(!("AddonManager" in window))
		Components.utils.import("resource://gre/modules/AddonManager.jsm");
	if(!("Services" in window))
		Components.utils.import("resource://gre/modules/Services.jsm");
	var then, promise = AddonManager.getAddonsByTypes(addonTypes, then = function(addons) {
		callback.call(context, addons.filter(function(addon) {
			var ops = addon.operationsRequiringRestart;
			return !addon.appDisabled
				&& !(ops & AddonManager.OP_NEEDS_RESTART_ENABLE || ops & AddonManager.OP_NEEDS_RESTART_DISABLE)
				&& (
					!addon.hidden
					|| options.showHidden > 0
					|| options.showHidden == -1 && !addon.userDisabled
				)
				&& (addon.iconURL || "").substr(0, 29) != "resource://search-extensions/";
		}));
	});
	promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+
}
function openAddonOptions(addon) {
	// Based on code from chrome://mozapps/content/extensions/extensions.js
	// Firefox 21.0a1 (2013-01-27)
	var optionsURL = addon.optionsURL;
	if(!addon.isActive || !optionsURL)
		return false;
	if(addon.type == "plugin") // No options for now!
		return false;
	if(
		addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_INFO || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_BROWSER || NaN)
	)
		openAddonPage(addon, true);
	else if(addon.optionsType == AddonManager.OPTIONS_TYPE_TAB && "switchToTabHavingURI" in window)
		switchToTabHavingURI(optionsURL, true);
	else {
		let windows = Services.wm.getEnumerator(null);
		while(windows.hasMoreElements()) {
			let win = windows.getNext();
			if(win.document.documentURI == optionsURL) {
				win.focus();
				return true;
			}
		}
		// Note: original code checks browser.preferences.instantApply and may open modal windows
		window.openDialog(optionsURL, "", "chrome,titlebar,toolbar,centerscreen,dialog=no");
	}
	return true;
}
function openAddonsManager(view) {
	var openAddonsMgr = window.BrowserOpenAddonsMgr // Firefox
		|| window.openAddonsMgr // Thunderbird
		|| window.toEM; // SeaMonkey
	openAddonsMgr(view);
}
function openAddonPage(addon, scrollToPreferences) {
	var platformVersion = parseFloat(
		Services.appinfo.name == "Pale Moon"
			? Services.appinfo.version
			: Services.appinfo.platformVersion
	);
	scrollToPreferences = scrollToPreferences && platformVersion >= 12
		? "/preferences"
		: "";
	openAddonsManager("addons://detail/" + encodeURIComponent(addon.id) + scrollToPreferences);
}

function hasModifier(e) {
	return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;
}

function addStyle() {
	if(addStyle.hasOwnProperty("_style"))
		return;
	var style = '\
		.toggleRestartlessAddons-isDelayed > .menu-iconic-text {\n\
			opacity: 0.75;\n\
			color: #070;\n\
		}\n\
		.toggleRestartlessAddons-isHidden > .menu-iconic-text {\n\
			color: #609;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-left {\n\
			opacity: 0.4;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-text,\n\
		.toggleRestartlessAddons-disabled > .menu-accel-container {\n\
			opacity: 0.5;\n\
		}\n\
		.toggleRestartlessAddons-askToActivate {\n\
			color: -moz-nativehyperlinktext;\n\
		}';
	addStyle._style = document.insertBefore(
		document.createProcessingInstruction(
			"xml-stylesheet",
			'href="' + "data:text/css,"
				+ encodeURIComponent(style) + '" type="text/css"'
		),
		document.documentElement
	);
}
function removeStyle() {
	if(!addStyle.hasOwnProperty("_style"))
		return;
	var s = addStyle._style;
	s.parentNode.removeChild(s);
	delete addStyle._style;
}
function closeMenus(node) {
	// Based on function closeMenus from chrome://browser/content/utilityOverlay.js
	for(; node && "tagName" in node; node = node.parentNode) {
		if(
			node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
			&& (node.localName == "menupopup" || node.localName == "popup")
		)
			node.hidePopup();
	}
}
function _log(s) {
	if(typeof LOG == "function") // Custom Buttons
		LOG(s);
	else // Or something else
		Services.console.logStringMessage("Toggle Restartless Add-ons: " + s);
}

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
	
	
	
	
	
	
	
	
	
	function checkForAddonsUpdates() {
// http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57958
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates

// Check for Addons Updates button for Custom Buttons
// (code for "code" section)

// (c) Infocatcher 2012-2021
// version 0.1.6pre4 - 2021-03-28

// Button just open hidden tab with about:addons and trigger built-in "Check for Updates" function.
// And show tab, if found updates.

(function() {
var btn = this instanceof XULElement
	? this
	: { // Launched not from custom button
		image: "", // Base64-encoded icon (if empty, will be used "imgLoading")
		label: "Check for Addons Updates",
		tooltipText: ""
	};
if("_cb_disabled" in btn)
	return;
btn._cb_disabled = true;

if(!("Services" in window))
	Components.utils.import("resource://gre/modules/Services.jsm");
var app = Services.appinfo.name;
var pv = parseFloat(Services.appinfo.platformVersion);

var ADDONS_URL = "about:addons";

var progressIcon = new ProgressIcon(btn);
var image = btn.image || progressIcon.imgLoading;
var tip = btn.tooltipText;
btn.tooltipText = "Open " + ADDONS_URL + "…";

var tab, browser, gBrowser;
var tbTabInfo, tbTab;

var trgWindow = Services.wm.getMostRecentWindow("navigator:browser")
	|| app == "Thunderbird" && Services.wm.getMostRecentWindow("mail:3pane")
	|| window;
var trgDocument = trgWindow.document;
var tabmail = trgDocument.getElementById("tabmail");

if(tabmail && app == "Thunderbird") { // Note: SeaMonkey doesn't support content tabs in mail window
	let addonsWin;
	let receivePong = function(subject, topic, data) {
		addonsWin = subject;
	};
	Services.obs.addObserver(receivePong, "EM-pong", false);
	Services.obs.notifyObservers(null, "EM-ping", "");
	Services.obs.removeObserver(receivePong, "EM-pong");
	if(addonsWin) {
		let rootWindow = addonsWin
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIWebNavigation)
			.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
			.rootTreeItem
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIDOMWindow);
		tabmail = rootWindow.document.getElementById("tabmail");
		tbTabInfo = tabmail.getBrowserForDocument(addonsWin);
		tbTab = tab = tbTabInfo.tabNode;
		processAddonsTab(addonsWin);
	}
	else {
		Services.obs.addObserver(function observer(subject, topic, data) {
			Services.obs.removeObserver(observer, topic);
			if(subject.document.readyState == "complete")
				processAddonsTab(subject);
			else {
				subject.addEventListener("load", function onLoad(e) {
					subject.removeEventListener(e.type, onLoad, false);
					processAddonsTab(subject);
				}, false);
			}
		}, "EM-loaded", false);
		// See openAddonsMgr() -> openContentTab()
		tbTabInfo = tabmail.openTab("contentTab", {
			contentPage: ADDONS_URL,
			clickHandler: "specialTabs.siteClickHandler(event, /addons\.mozilla\.org/);",
			background: true
		});
		tbTab = tab = tbTabInfo.tabNode;
		tbTab.collapsed = true;
		// Note: dontSelectHiddenTab() not implemented
	}
}
else if("gBrowser" in trgWindow && trgWindow.gBrowser.tabs) {
	let isPending = false;
	let ws = Services.wm.getEnumerator("navigator:browser");
	windowsLoop:
	while(ws.hasMoreElements()) {
		let w = ws.getNext();
		let tabs = w.gBrowser.tabs;
		for(let i = 0, l = tabs.length; i < l; ++i) {
			let t = tabs[i];
			if(
				!t.closing
				&& t.linkedBrowser
				&& t.linkedBrowser.currentURI.spec == ADDONS_URL
			) {
				tab = t;
				break windowsLoop;
			}
		}
	}

	gBrowser = trgWindow.gBrowser;
	if(!tab) {
		tab = gBrowser.addTab(ADDONS_URL, {
			triggeringPrincipal: "Services" in window // Firefox 63+
				&& Services.scriptSecurityManager
				&& Services.scriptSecurityManager.getSystemPrincipal()
		});
		tab.collapsed = true;
		tab.closing = true; // See "visibleTabs" getter in chrome://browser/content/tabbrowser.xml
		trgWindow.addEventListener("TabSelect", dontSelectHiddenTab, false);
	}
	else if(
		tab.getAttribute("pending") == "true" // Gecko >= 9.0
		|| tab.linkedBrowser.contentDocument.readyState == "uninitialized"
		// || tab.linkedBrowser.__SS_restoreState == 1
	)
		isPending = true;

	browser = tab.linkedBrowser;
	if(
		isPending
		|| browser.webProgress.isLoadingDocument
		|| browser.currentURI.spec == "about:blank" // Firefox 79+
	) {
		browser.addEventListener("load", processAddonsTab, true);
		if(isPending) {
			if(pv >= 41) {
				// Workaround to correctly restore pending tab
				// See https://github.com/Infocatcher/Custom_Buttons/issues/39
				let selTab = gBrowser.selectedTab;
				gBrowser.selectedTab = tab;
				gBrowser.selectedTab = selTab;
			}
			else {
				browser.reload();
			}
		}
	}
	else {
		processAddonsTab();
	}
}
else {
	progressIcon.restore();
	btn.tooltipText = tip;
	delete btn._cb_disabled;
	Services.prompt.alert(window, btn.label, "Error: Can't find supported window!");
	return;
}

function processAddonsTab(e, again) {
	var doc;
	if(e && e instanceof Components.interfaces.nsIDOMWindow) {
		doc = e.document;
	}
	else if(e) {
		doc = e.target;
		if(doc.location != ADDONS_URL)
			return;
		browser.removeEventListener(e.type, processAddonsTab, true);
	}
	else {
		doc = browser.contentDocument;
	}

	btn.tooltipText = "Process " + ADDONS_URL + "…";
	progressIcon.loading();

	var origAttr = "_cb_checkForAddonsUpdates_origImage";
	if(!tab.hasAttribute(origAttr)) {
		var link = doc.querySelector('link[rel="shortcut icon"]'); // Not loaded yet?
		tab.setAttribute(origAttr, link && link.href || tab.image);
	}
	tab.image = image;

	var fu = $("cmd_findAllUpdates");
	if(!fu) { // Firefox 72+
		var win = doc.defaultView;
		var vb = doc.getElementById("html-view-browser");
		if(!vb) {
			if(!HTMLHtmlElement.isInstance(doc.documentElement)) { // Firefox 87+
				win.setTimeout(processAddonsTab, 20, win);
				return;
			}
			vb = browser;
		}
		if(!again) { // Strange errors happens
			// chrome://mozapps/content/extensions/aboutaddons.js
			// getTelemetryViewName() -> el.closest(...) is null
			win.setTimeout(processAddonsTab, 20, win, true);
			return;
		}
		var vbDoc = vb.contentDocument;
		fu = vbDoc.querySelector('[action="check-for-updates"]');
		var um = vbDoc.getElementById("updates-message");
	}

	var notFound = $("updates-noneFound") || {
		get hidden() { return um.getAttribute("state") != "none-found"; }
	};
	var updated = $("updates-installed") || {
		get hidden() { return um.getAttribute("state") != "installed"; }
	};
	// Avoid getting false results from the past update check (may not be required for "noneFound")
	if(um) { // Firefox 72+
		um.hidden = true;
		um.removeAttribute("state");
	}
	else {
		notFound.hidden = updated.hidden = true;
	}

	//fu.doCommand();
	fu.click();

	function localize(node, key, callback) {
		if(um) { // Firefox 72+
			doc.l10n.formatValue(key).then(function(s) {
				callback(s || key);
			}, Components.utils.reportError);
			return;
		}
		callback(node.getAttribute("value") || key);
	}

	var inProgress = $("updates-progress") || {
		get hidden() { return um.getAttribute("state") != "updating"; }
	};
	localize(inProgress, "addon-updates-updating", function(s) {
		btn.tooltipText = s;
	});

	var waitTimer = setInterval(function() {
		if(!doc.defaultView || doc.defaultView.closed) {
			stopWait();
			notify("Tab with add-ons manager was closed!");
			return;
		}
		if(!inProgress.hidden)
			return;
		var autoUpdate = $("utils-autoUpdateDefault")
			|| vbDoc.querySelector('[action="set-update-automatically"]');
		var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"
			|| autoUpdate.checked;

		var found = $("updates-manualUpdatesFound-btn") || {
			get hidden() { return um.getAttribute("state") != "manual-updates-found"; }
		};
		if(
			autoUpdateChecked
				? notFound.hidden && updated.hidden
				: notFound.hidden && found.hidden
		) // Too early?
			return;

		stopWait();
		if(!tbTab)
			tab.closing = false;
		function removeTab() {
			if(!tab.collapsed)
				return;
			if(tbTab) {
				tabmail.closeTab(tbTabInfo, true /*aNoUndo*/);
				return;
			}
			gBrowser.removeTab(tab);
			(function forgetClosedTab(isSecondTry) {
				var ss = "nsISessionStore" in Components.interfaces
					? (
						Components.classes["@mozilla.org/browser/sessionstore;1"]
						|| Components.classes["@mozilla.org/suite/sessionstore;1"]
					).getService(Components.interfaces.nsISessionStore)
					: trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
				if(!("forgetClosedTab" in ss))
					return;
				var closedTabs = JSON.parse(ss.getClosedTabData(window));
				for(let i = 0, l = closedTabs.length; i < l; ++i) {
					let closedTab = closedTabs[i];
					let state = closedTab.state;
					if(state.entries[state.index - 1].url == ADDONS_URL) {
						ss.forgetClosedTab(window, i);
						return;
					}
				}
				if(!isSecondTry) // May be needed in SeaMonkey
					setTimeout(forgetClosedTab, 0, true);
			})();
		}

		if(!notFound.hidden) {
			removeTab();
			localize(notFound, "addon-updates-none-found", function(s) {
				notify(s);
			});
			return;
		}
		if(autoUpdateChecked) {
			removeTab();
			localize(updated, "addon-updates-installed", function(s) {
				notify(s);
			});
			return;
		}

		tab.collapsed = false;

		var cats = $("categories");
		var upds = $("category-availableUpdates");
		if(cats && upds) {
			if(vb && cats.selectedItem == upds) // Only for Firefox 72+
				cats.selectedItem = $("category-extension"); // Trick to force update
			cats.selectedItem = upds;
		}
		else { // Firefox 76+ ?
			vbDoc.querySelector('.category[name="available-updates"]').click();
		}

		var tabWin = tab.ownerDocument.defaultView;
		if(tbTab)
			tabmail.switchToTab(tbTabInfo);
		else
			tabWin.gBrowser.selectedTab = tab;
		setTimeout(function() {
			tabWin.focus();
			doc.defaultView.focus();
			var al = $("addon-list") || vb;
			al.focus();
		}, 0);
	}, 50);
	function $(id) {
		return doc.getElementById(id);
	}
	function stopWait() {
		clearInterval(waitTimer);
		progressIcon.restore();
		btn.tooltipText = tip;
		if(tab.image == image)
			tab.image = tab.getAttribute(origAttr);
		tab.removeAttribute(origAttr);
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() {
			delete btn._cb_disabled;
		}, 500);
	}
	function notify(msg) {
		Components.classes["@mozilla.org/alerts-service;1"]
			.getService(Components.interfaces.nsIAlertsService)
			.showAlertNotification(
				app == "Firefox" && pv >= 57
					? "chrome://mozapps/skin/extensions/extensionGeneric.svg"
					: "chrome://mozapps/skin/extensions/extensionGeneric.png",
				btn.label,
				msg, false, "", null
			);
	}
}
function dontSelectHiddenTab(e) {
	// <tab /><tab collapsed="true" />
	// Close first tab: collapsed tab becomes selected
	var trgTab = e.originalTarget || e.target;
	if(trgTab != tab)
		return;

	if(/\n(?:BrowserOpenAddonsMgr|toEM)@chrome:\/\//.test(new Error().stack)) {
		// User open Add-ons Manager, show tab
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() { // Hidden tab can't be selected, so select it manually...
			tab.collapsed = tab.closing = false;
			gBrowser.selectedTab = tab;
		}, 0);
	}

	function done(t) {
		if(!t.hidden && !t.closing) {
			e.preventDefault();
			e.stopPropagation();
			return gBrowser.selectedTab = t;
		}
		return false;
	}
	for(var t = tab.nextSibling; t; t = t.nextSibling)
		if(done(t))
			return;
	for(var t = tab.previousSibling; t; t = t.previousSibling)
		if(done(t))
			return;
}
function ProgressIcon(btn) {
	var app = Services.appinfo.name;
	var pv = parseFloat(Services.appinfo.platformVersion);
	if(app == "SeaMonkey")
		this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif";
	else if(app == "Thunderbird") {
		this.imgConnecting = "chrome://messenger/skin/icons/connecting.png";
		this.imgLoading = "chrome://messenger/skin/icons/loading.png";
	}
	else {
		this.imgConnecting = app == "Firefox" && pv >= 58
			? "chrome://browser/skin/tabbrowser/tab-connecting.png"
			: "chrome://browser/skin/tabbrowser/connecting.png";
		this.imgLoading = app == "Firefox" && pv >= 48
			? "chrome://global/skin/icons/loading.png"
			: "chrome://browser/skin/tabbrowser/loading.png";
	}
	if(!(btn instanceof XULElement)) {
		this.loading = this.restore = function() {};
		return;
	}
	var useAnimation = app == "Firefox" && pv >= 32 && pv < 48;
	var btnIcon = btn.icon
		|| btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon");
	var origIcon = btnIcon.src;
	btnIcon.src = this.imgConnecting;
	if(useAnimation) {
		let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null);
		let s = btnIcon.style;
		s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" ");
		s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" ");
		s.width = cs.width;
		s.height = cs.height;
		s.boxShadow = "none";
		s.borderColor = s.background = "transparent";
		btnIcon.setAttribute("fadein", "true");
		btnIcon.setAttribute("busy", "true");
		btnIcon.classList.add("tab-throbber");
		btnIcon._restore = function() {
			delete btnIcon._restore;
			btnIcon.removeAttribute("busy");
			btnIcon.removeAttribute("progress");
			setTimeout(function() {
				btnIcon.classList.remove("tab-throbber");
				btnIcon.removeAttribute("style");
				btnIcon.removeAttribute("fadein");
			}, 0);
		};
	}
	this.loading = function() {
		btnIcon.src = this.imgLoading;
		if(useAnimation)
			btnIcon.setAttribute("progress", "true");
	};
	this.restore = function() {
		btnIcon.src = origIcon;
		if(useAnimation)
			btnIcon._restore();
	};
}
}).call(this);
//== Check for Addons Updates end
}


this.tooltipText = "Переключатель джетпаков" 
                   + "\n\nУправление:\nЛКМ – открыть меню" 
                   + "\nПКМ – проверить обновления"
                   + "\nСКМ – открыть страницу дополнений"
                   + "\nShift+ПКМ – меню кнопки"
                   + "\n\nВ меню: \nЛКМ – включить/выключить дополнение"
                   + "\nShift+ЛКМ – включить/выключить дополнение без закрытия меню"   
                   + "\nСКМ – открыть страницу дополнения в управлении дополнениями"                    
                   + "\nПКМ – открыть настройки дополнения (если есть)";

Отсутствует

 

№1689602-06-2023 13:27:08

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

Re: Custom Buttons

Andrey_Krropotkin
Большое спасибо!

Отсутствует

 

№1689705-06-2023 04:36:50

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

Re: Custom Buttons

Dumby - вопрос по JS-синтаксису:
есть ли способ объединить две последние записи в одну? (_531906d3-e22f-4a6c-a102-8057b88a1a63_)

Выделить код

Код:

var tooltips = {
	get "stop-button"() {
		return GetDynamicShortcutTooltipText("stop-button") +"\n"+ hmap.get("wheel-stop");
},
"_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action": hmap.get("#").split('|')[11], // SingleSave
"_531906d3-e22f-4a6c-a102-8057b88a1a63_-BAP": hmap.get("#").split('|')[11]
}

Ещё можешь пояснить что-нибудь по вопросу: как определить, что меню открыто?

Отсутствует

 

№1689805-06-2023 16:05:15

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

Re: Custom Buttons

Dobrov пишет

вопрос по JS-синтаксису:
есть ли способ объединить две последние записи в одну?

Разве что уже после определения объекта tooltips написать
tooltips["…-BAP"] = tooltips["…-browser-action"] = hmap.get("#").split('|')[11];

Ещё можешь пояснить что-нибудь по вопросу: как определить, что меню открыто?

Нет, не могу.
id менюшки «ToggleButton-Popup» я не смог увидеть, но есть "ToggleButton-secondaryPopup".


И, геттер XULPopupElement.prototype.state существует, и, у меня, работает.
Добавил в get "ToggleButton"() {…}


Services.console.logStringMessage(
    window.event.target.closest("#ToggleButton").secondaryPopup.state
);
и мне показывает "open" или "closed".

Отсутствует

 

№1689906-06-2023 05:32:27

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

Re: Custom Buttons

Dumby - ясно, будем искать…
я уже встречал этот глюк в MacOS, что .state всегда closed…

Отсутствует

 

№1690008-06-2023 04:07:14

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

Re: Custom Buttons

Dumby
сделал secondaryPopup.state, это был не глюк MacOS.


Не получается после сохранения сайта изменить стиль имени текущей вкладки.
Первая стока работает, а как изменить стиль шрифта вкладки?
Желательно одновременно overline и underline ? Или отдельно на italic ?


gBrowser.selectedTab.style.filter = "blur(0.5px) drop-shadow(0px 1px 0px #808)";
gBrowser.selectedTab.style.textDecoration = "overline";

Отредактировано Dobrov (08-06-2023 04:13:10)

Отсутствует

 

Board footer

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