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

Юристы зарабатывают огромные деньги и славу, оперируя хорошим знанием законов. Правила форума — простой путь к успешному общению.

№1720125-12-2024 18:33:40

Duche
Участник
 
Группа: Members
Зарегистрирован: 07-02-2016
Сообщений: 218
UA: Firefox 105.0

Re: Custom Buttons

Добрый день . Есть необходимость в кнопке или на худой конец код для CSS FG_userChrome "Запрета любого масштабирования на определённом сайте" если есть в закромах, буду весьма признателен.

Отредактировано Duche (28-12-2024 15:20:07)

Отсутствует

 

№1720225-12-2024 19:22:07

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 6
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Опять забыл ,хочу поменять Firefox на Netscape Navigator
Вспомнил !

Отредактировано green25 (25-12-2024 19:49:31)

Отсутствует

 

№1720326-12-2024 22:46:26

beggrr
Участник
 
Группа: Members
Зарегистрирован: 04-02-2014
Сообщений: 129
UA: Firefox 102.0

Re: Custom Buttons

У меня веб консоль открыта в многострочном режиме. Если я выделяю в левой половине  фрагмент кода, можно сделать, чтобы выполнился только этот выделенный фрагмент?

Отсутствует

 

№1720408-01-2025 14:02:30

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 6
UA: Firefox 136.0

Re: Custom Buttons

новой CB не предвидеться ? для 128 к примеру. и скрипты отвалились к тому же...

Отсутствует

 

№1720508-01-2025 14:52:16

vending_machine
Участник
 
Группа: Members
Зарегистрирован: 10-01-2020
Сообщений: 558
UA: Firefox 115.0

Re: Custom Buttons

green25 пишет

новой CB не предвидеться ? для 128 к примеру. и скрипты отвалились к тому же...

https://forum.mozilla-russia.org/viewto … 27#p811227

Отсутствует

 

№1720608-01-2025 15:37:38

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 6
UA: Firefox 136.0

Re: Custom Buttons

vending_machine

vending_machine пишет

https://forum.mozilla-russia.org/viewto … 27#p811227

Это да, но кнопок не видать..Еще что-то надо ?

Отсутствует

 

№1720708-01-2025 16:31:04

vending_machine
Участник
 
Группа: Members
Зарегистрирован: 10-01-2020
Сообщений: 558
UA: Firefox 115.0

Re: Custom Buttons

green25 пишет

Еще что-то надо ?

При закрытом Firefox, удалить папку startupCache из Локального каталога профиля.
Но у вас 136 [firefox]. Видимо надо откатится на версию поменьше.

Отсутствует

 

№1720808-01-2025 16:55:53

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 6
UA: Firefox 136.0

Re: Custom Buttons

vending_machine

vending_machine пишет

Но у вас 136

не это user agent / а так 128 и startupCache  удалял...бесполезно
Так понимаю выше 115 об СВ можно забыть ?

Отредактировано green25 (15-01-2025 13:50:22)

Отсутствует

 

№1720921-01-2025 20:56:04

Ki_rrrilll
Участник
 
Группа: Members
Зарегистрирован: 22-11-2013
Сообщений: 129
UA: Firefox 115.0

Re: Custom Buttons

У кого может есть ссылка на paxmod?
Не знаю какая именно версия CB нужна для ФФ 115

Отсутствует

 

№1721021-01-2025 21:25:01

kokoss
Участник
 
Группа: Members
Зарегистрирован: 15-02-2018
Сообщений: 1773
UA: Firefox 115.0

Re: Custom Buttons

Ki_rrrilll пишет

Не знаю какая именно версия CB нужна для ФФ 115

https://forum.mozilla-russia.org/viewto … 37#p811737


Win7

Отсутствует

 

№1721121-01-2025 23:10:10

Ki_rrrilll
Участник
 
Группа: Members
Зарегистрирован: 22-11-2013
Сообщений: 129
UA: Firefox 115.0

Re: Custom Buttons

kokoss
Большое спасибо! :)

Отсутствует

 

№1721201-02-2025 04:38:49

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

Re: Custom Buttons

Приветствую!
Вопрос: как отображать цифру на любой кнопке ? и каждый час обновлять ?
Например, на своей кнопке или unified-extensions-button выводить справа сверху цифру аналогично AdBlock, но на синем фоне ?


Dumby - сделал подсчёт дней от смены пароля, через nsILoginManager не получилось...

Выделить код

Код:

(async ()=>{ // код выполнять в окне
	var AlLogins = await Services.logins.getAllLogins();
	var PassInfo = urluser => AlLogins.filter(function(item){
		if(item.origin +' '+ item.username == urluser) return item;
	})[0];
	try {var dayPass = PassInfo("https://accounts.google.com USER@gmail.com").timePasswordChanged;
	} catch {dayPass = new Date()}

	var diffDays = Math.ceil(Math.abs(new Date().getTime() - new Date(dayPass).getTime())/86400000);
	console.log(diffDays); //прошло дней от смены пароля
})();

Отредактировано Dobrov (01-02-2025 04:44:29)

Отсутствует

 

№1721301-02-2025 09:32:24

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

Re: Custom Buttons

Dobrov пишет

Вопрос: как отображать цифру на любой кнопке ? и каждый час обновлять ?
Например, на своей кнопке или unified-extensions-button выводить справа сверху цифру аналогично AdBlock, но на синем фоне ?

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

Выделить код

Код:

(id => {
	var btn = document.getElementById(id);

	btn.setAttribute("badged", true);
	btn.setAttribute("badgeStyle", "color: white; background-color: blue");

	btn.textContent = "";
	btn.render();

	var tick = num => {
		btn.setAttribute("badge", String(num).padStart(3, "0"));
		++num > 100 || setTimeout(tick, 250, num);
	}
	tick(0);

})("unified-extensions-button");


На своей — пере-рендер не требуется.


А на любой (чужой) — надо смотреть, нет ли в ней
чего-то полезного, типа <menupopup>, и если есть,
тогда удалять только добро, добавленное кустомэлементщиной.

сделал подсчёт дней от смены пароля, через nsILoginManager не получилось...

А через что же тогда?
Ну-ка запусти alert(Services.logins); Что видно?


Подсчётом дней от смены пароля он не занимается,
но сами логины ты взял именно у него.


А ceil — вроде как-то немного непонятно,
может лучше floor или, в крайнем случае, round


Да и сам подсчёт у тебя какой-то слишком странный. Вот, прогон всех

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

Выделить код

Код:

(async () => {

	var logins = await Services.logins.getAllLogins();
	var formatter = new Intl.RelativeTimeFormat("ru", {style: "long"});

	var now = Date.now();
	var day = 24 * 60 * 60 * 1000;

	var result = logins.map(login => {

		var info = login.username + " " + login.origin + "\n";

		var changed = login.timePasswordChanged;
		var changedDate = new Date(changed).toLocaleDateString("ru");

		var diff = Math.floor((now - changed) / day);

		return info + changedDate + " \u2014 " + formatter.format(-diff, "day");
	});

	console.log("\n" + result.join("\n\n") + "\n ");

})();

Отсутствует

 

№1721404-02-2025 00:49:54

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

Re: Custom Buttons

Dumby - спасибо! (исчо другой вопрос в теме UCF задал)
На кнопке не получается показать 4 символа, видно первые три. Это ограничение Firefox ?


Делаю показ дней от смены/обновления пароля выбранной учётки или до смены пароля (если указан срок жизни пароля при регулярной замене)

Отсутствует

 

№1721504-02-2025 16:55:20

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

Re: Custom Buttons

Dobrov пишет

Это ограничение Firefox ?

Да. В chrome://global/skin/toolbarbutton.css написано:


.toolbarbutton-badge {
  .....
  max-width: 20px;
  .....
}


Можно поднять (вписать в badgeStyle).

Отсутствует

 

№1721604-02-2025 22:11:12

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

Re: Custom Buttons

Custom Buttons 0.0.7.0.0.35


Версия без использования «on…» атрибутов в 136+, надеюсь.

Отсутствует

 

№1721705-02-2025 16:38:41

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 326
UA: Google 2.1

Re: Custom Buttons

Dumby, здравствуете.
В связи с прилётом Bug 1881888 - Remove support for JSMs, пытаюсь поднять bootstrap расширения, но столкнулся с необходимостью перезагрузки всего [firefox]. Подскажите пожалуйста, может есть какое решение для перезагрузки/выгрузки ESModule`й в [firefox]
ЗЫ: В случае с DTA, выкидывает ошибку:

can't access dead object


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1721805-02-2025 21:42:54

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

Re: Custom Buttons

Farby пишет

может есть какое решение для перезагрузки/выгрузки ESModule`й в [firefox]

Нет, они не выгружаются.
Чтобы не быть голословным, вот


Bug 1836480 - Convert toolkit/mozapps/extensions/internal/XPI* to ES modules


Там прямо сходу сказано: «ES modules cannot be unloaded».
Поэтому, запилили какой-то жуткий wrapper XPIExports,
где новые экземпляры модулей обретаются посредством импорта
с добавлением к урлу модуля суффикса «?…».


А вообще, кстати да, я как-то связязан только с не restartless расширениями,
поэтому даже не задумывался, а ведь для настоящих bootstrap расширений
это, действительно, может быть проблемой.

Отредактировано Dumby (05-02-2025 22:02:58)

Отсутствует

 

№1721905-02-2025 23:33:52

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 326
UA: Google 2.1

Re: Custom Buttons

Dumby пишет

ведь для настоящих bootstrap расширений
это, действительно, может быть проблемой.

Спасибо за ответ, Вы для меня были последней инстанцией, конечно я запилил рестарт если моодуль загружен или отказ от обслуживания...
ЗЫ: и да я проштудировал что смог найти и там нет выгрузки перезагрузки и etc... Спасибо за подтверждение.
PPS: А ещё вопрос Вы будете поддерживать DOMi или мне пора разбираться с Browser Toolbox?...
Ну я всё-таки начинал с Netscape Navigator, хотя если удаться отконсолить и поймать ошибки, но пока это для меня сложно.


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1722006-02-2025 00:33:45

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 326
UA: Google 2.1

Re: Custom Buttons

Dumby пишет

это, действительно, может быть проблемой.

Это не проблема, это опа и присели :)


Жизнь иногда такое выкидывает, что хочется подобрать...

На форуме

 

№1722106-02-2025 17:56:03

pnm4
Участник
 
Группа: Members
Зарегистрирован: 11-12-2022
Сообщений: 3
UA: Firefox 136.0

Re: Custom Buttons

Проблемы со средним щелчком? Firefox 136 + 0.0.7.0.0.35
this.Click = function(event) {alert(event.button);}
Больше не выводит "1"

Отсутствует

 

№1722206-02-2025 18:32:37

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

Re: Custom Buttons

Farby пишет

ещё вопрос Вы будете поддерживать DOMi или мне пора разбираться с Browser Toolbox?...

Не готов согласться с этим вот «или», поскольку DOMi
Browser Toolbox'у даже близко вообще ни разу не замена.


Встроенная машинерия — это мощь, шок и трепет.
Навык обращения с ней представляет безотносительную ценность.


А DOMi — это так, просто выцветший отголосок-отражения чего-то былого.
Впрочем, иногда, сугубо для простецких задач, вполне себе ностальгически-симпатичен.


Вобщем, собственно ответ, можно сформулировать как «если смогу».
У меня сейчас такой.


pnm4 пишет

Firefox 136 + 0.0.7.0.0.35
this.Click = function(event) {alert(event.button);}
Больше не выводит "1"

Ага, Firefox 28.0   0.0.5.8.2
this.Click = function(event) {alert(event.button);}
тоже не выводит "1"

Отсутствует

 

№1722306-02-2025 19:44:50

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

Re: Custom Buttons

Dumby
Раскрыл 136b2. Перестали рабтать кнопки:
1. Экспорт в HTML файл в контекстном меню закладок

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

Выделить код

Код:

//-------------------- Экспорт в HTML файл в контекстном меню закладок
(popup => addEventListener("popupshowing", {
    handleEvent() {
        if (this.shouldHide()) return;
        var before = document.getElementById("placesContext_openSeparator");
        var menuitem = popup.insertBefore(document.createXULElement("menuitem"), before);
        menuitem.setAttribute("label", "Экспорт папки в HTML");
        menuitem.setAttribute("oncommand", "exportFolder();");
        menuitem.exportFolder = this.pick.bind(this);
        addDestructor(() => menuitem.remove());
        (this.handleEvent = () => menuitem.hidden = menuitem.disabled = this.shouldHide())();
    },
    shouldHide() {
        var node = popup.triggerNode._placesNode;
        var hide = !node || node.type != node.RESULT_TYPE_FOLDER;
        if (!hide) this.guid = node.bookmarkGuid, this.title = node.title;
        return hide;
    },
    pick() {
        var fp = makeFilePicker();
        fp.init(window.browsingContext, PlacesUIUtils.promptLocalization.formatValueSync("places-bookmarks-export"), fp.modeSave);
        fp.defaultString = (this.title ? DownloadPaths.sanitize(this.title) : "untitled") + ".html";
        fp.open(res => res == fp.returnCancel || this.export(fp.file.path));
    },
    async export(path) {
        var tree = await PlacesUtils.promiseBookmarksTree(
            this.guid, {includeItemIds: true}
        );
        var bookmarks = {children: [
            {root: "toolbarFolder"},
            {root: "unfiledBookmarksFolder"},
            {root: "bookmarksMenuFolder", children: [tree], guid: PlacesUtils.bookmarks.menuGuid}
        ]};
        new this.nsvo.BookmarkExporter(bookmarks).exportToFile(path);
    },
    get nsvo() {
        delete this.nsvo;
        return this.nsvo = Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm", {});
    }
}, false, popup))(document.getElementById("placesContext") || 1);

2.Дополнительные пункты в контекстном меню кнопки

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

Выделить код

Код:

/*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,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACDUlEQVR42nWT30sUURTHz91Zd9FkVBRrX0oScTMI8weSIYSU/QMFbb5oGAn+AQo9B/UH9BAF+lIr+OLrSETQQ4GhEVRrUFkP/SLRNcvUnXv8nrmz7XVcB86cy7nn+5kz556ryHrueKxIYcFkPMmbxXHgzA6NDyguatQewBzHkKMMQDnWPgDsIwYh8/hATP8HQCRJEFIc5iA1DpljZIGogJU/9WXp39DR1pj99QBwe45dZb6UhCXE2Hx6m4xtQZQ/8SDni+DdSNopVYtysUiFpVbDqp4vPL4uCWc6zt+H+wvbAOBrCBCRFgh0AtIA6BboBVADc6eeZLMCGOq/kkF8Hcs8AO8tgDwav5M0AI/bw87Xw9dml+bvSUamtfsGYmuIrQDwKgIoQirkF3pR9uCbz28vx+tTMWo6VRdsL79eLax80yePtc3MTlwYKwMIT8HTvYufFkc+bq4NOjWN9Kf6cIVsHNr4sePnf9LxytqH06Od1w6oICGA9uZGOnvEpb7tgkrefLl8UXZvdTV5CYe3vv9Wzy6dVnfL9KAKfkcALeihaSKT+yj3Imji1XRPBvFyTTSn4LHMi5Y5SCnrGFc3c5MCqKtMD5c5RnsOisfILlmD1NxA5yThwy96GhkkRtlxrP3oIO0d5ZKXfSSbUYaJ1/tGef9lovAyUeQyAaKCO4nLpPRBABWJlwCWt6vYBccZ9A793wmLAAAAAElFTkSuQmCC";
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(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "6");
let f1IDs = ["custombuttons-contextpopup-saveButtonImage",
            "custombuttons-contextpopup-saveButtonImage-sub"];
addMenuItem(f1IDs, cIDs, "Сохранить изображение кнопки", saveImg8,
            "document.getElementById('" + this.id
          + "').saveImageURI(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "I");
let f2IDs = ["custombuttons-contextpopup-copyButtonsCodeText",
            "custombuttons-contextpopup-copyButtonsCodeText-sub"];
addMenuItem(f2IDs, b2IDs, "Копировать код кнопки как текст", saveImg2,
            "document.getElementById('" + this.id
          + "').copyButtonsCodeText(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "T");                    
let f3IDs = ["custombuttons-contextpopup-copyAsHTML",
            "custombuttons-contextpopup-copyAsHTML-sub"];
addMenuItem(f3IDs, b2IDs, "Копировать код кнопки как HTML ссылку", saveImg3,
            "document.getElementById('" + this.id
          + "').copyToHTMLCode(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "M");
let f4Ds = ["custombuttons-contextpopup-copyToBBCode",
            "custombuttons-contextpopup-copyToBBCode-sub"];
addMenuItem(f4Ds, b2IDs, "Копировать код кнопки как BBcode сылку", saveImg4,
            "document.getElementById('" + this.id
          + "').copyToBBCode(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "B");
let f5Ds = ["custombuttons-contextpopup-saveAsHTML",
            "custombuttons-contextpopup-saveAsHTML-sub"];
addMenuItem(f5Ds, bIDs, "Сохранить код кнопки в HTML файл", saveImg5,
            "document.getElementById('" + this.id
          + "').saveToHTMLCode(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "H");
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(('triggerNode' in this.parentNode) "
          + "? this.parentNode.triggerNode "
          + ": document.popupNode);", "D");
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 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(
      !("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
      ? window.browsingContext
      : 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();
             }
         });
};

/////////////////////////////////////////////////////////////////////////////
////////////////////////////Остальные функци ////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


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

this.idMIonclick = function idMIonclick(aStrI) {
  var btn = aStrI.id;
  var box = custombuttons.confirmBox("Копировать в буфер", btn, "Да", "Нет");
  if (box) {
    custombuttons.cbService.writeToClipboard(btn);
    custombuttons.alertSlide(btn, "Скопирована в буфер");
} }
function  mostRecentWindow(windowType) {
  return Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow(windowType);
}

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

this.copyImageURI = function copyImageURI(aStrA) {
    var btn = aStrA;
    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(aStrT) {
  var btn = aStrT;
  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(aStrM) {
  var btn = aStrM;
  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(aStrBB) {
  var btn = aStrBB;
  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);
}

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

this.saveImageURI = function saveImageURI(aStrU) {
 
  var remove = gBrowser.removeCurrentTab.bind(gBrowser);
    var promiseTargetFile = async (...args) => {
        var res = await window.promiseTargetFile(...args);
        setTimeout(remove, 0);
        return res;
    }
    var internalSave = eval(`(${window.internalSave})`);
    var save = eval(`(${window.saveDocument})`);
    var saveBrowser = eval(`(${window.saveBrowser})`)
     
     var btn = aStrU;
  if (!btn) return;
  
  (saveButtonImage = btn => {
        if (btn.image != "") {
            var tab = gBrowser.selectedTab;
            gBrowser.selectedTab = gBrowser.addTab(btn.image, {
                triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
            });
            setTimeout(function() {
              if (!!window.saveBrowser) {
                saveBrowser(gBrowser.selectedBrowser, true);
              } else {
                window.content.document.title = btn.name;
                save(window.content.document);
              }
            }, 2000);
        } else
            custombuttons.alertBox("Эта кнопка не имееет изображения!");
    })(btn);
 
}



/////////////////////////////// Сохранить кнопку в 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.xul"){' + 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(
    !("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
    ? window.browsingContext
    : 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(aStrH) {
  var btn = aStrH;
  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();

3. Autocopy

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

Выделить код

Код:

/*Initialization Code*/
(ps => {


    var s = "ucf.autocopy.enabled";
 
    this._handleClick = () =>  cbu.setPrefs(s, cbu.getPrefs(s) == true ? false: true);
var toggleImage = val => {
        
self.image = cbu.getPrefs(s)
           ? 'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgMBAAQIAAcEBwAIBAcACAQHAAgEBwAIBAcACAQHAAgEBwAIBAcACAMEAQEAAAAAAAAAAAAAAAACAwADAAAAABUnAB9cqgC3a7wB4Gq5Ad1qugHearoB3mq6Ad5qugHearoB3mi4AN1qugHgYrMAxR43AC8AAAAAAAEAAAECAAQAAAQAV6AAprP9Vv/W/qn80/+f/9T/ov/U/6L/1P+i/9T/ov/U/6L/1/+n/9X+pfy3/WL/Y7QAvwEAAQAAAAAAFSgAH1ehAKlyzwD1htgf/YzcJ/2K2yP9i9sk/YvbJf2L2yX9i9sm/YnaIv2b4kP92/21/Nf+qv9quwHdBQkACAAAAQBeqwCzr/tR/8X0j/u+8X//vvJ//77ygP++8oD/vvKA/77yf/+98n7/wvSH/4zcKv+e4kv93v+0/2i5AN0DBwAIBQkACGu8AdzV/af/4v/B/d//u//h/7//4f+//+H/v//h/7//4f+//9/+u//n/8n/w/GK/4zaK/3g/7r/aroC3gMHAAgEBwAIarkC3dX/pf/g/sD93v67/9/+vv/g/r//4P6//+D+v//f/r7/3f66/+T/xv/B8Yb/j9st/eT/w/9qugPeAwcACAQHAAhqugLe2v+w/+j/z/3l/8r/5//N/+f/zv/n/87/5//O/+f/zf/l/sj/7P/W/8Xyj/+Q2y/96f/N/2q6A94DBwAIBAcACGq6At7f/7n/7v/c/ev/1v/t/9n/7f/a/+3/2v/t/9r/7f/Z/+r+1f/y/+P/yPKW/5DbMf3s/9X/aroE3gMHAAgEBwAIaroC3uP/wf/z/+j98P/h//L/5P/z/+X/8//l//P/5f/y/+T/8P7g//j/7v/L8p3/kdsy/fD/3P9rugTeAwcACAQHAAhqugLe5v/J//j/8v31/+r/9v/t//f/7v/3/+//9//u//b/7f/0/un//f/4/87yo/+R2zL98f/f/2q5Bd0DBwAIBAcACGq6At7p/8///P/6/fj/8f/6//T/+v/1//r/9f/6//X/+v/0//f+8P//////0fGo/5PbNf30/+f/a7wE3AQJAAgEBwAIabkC3er/0f/+//79+v/0//v/9//8//j//P/4//z/+P/7//f/+f70///////T8qz/i9go+8P9ef9dqwCzAAACAAUJAAhquwHd7f7a//////z+//39/////f////3////9/////f////39/vz9/////dzzvv5v0AD1VqECqRUnAB8AAAAAAQACAGK0AL/J/Yf/8v7k/O3/1//u/9n/7v/Z/+7/2f/u/9n/7v/Z/+3/1//x/eP8vfxu/1WgAKYAAAUAAQIABAABAAAAAAAAHjcALmGzAMVquwLgarkC3Wq6At5qugLearoC3mq6At5qugLearkC3Wu8AeBbqgC3FScAHwAAAAACAwADAAAAAAAAAAAAAAAAAwQCAQQIAAgEBwAIBAcACAQHAAgEBwAIBAcACAQHAAgEBwAIBAgABwMDAgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='

   : 

'data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEDAAQACAcEAAcIBAAHCAQABwgEAAcIBAAHCAQABwgEAAcIBAAHCAMBBAEAAAAAAAAAAAAAAAACAAMDAAAAABUAJx9cAKq3awG84GoBud1qAbreagG63moBut5qAbreagG63mgAuN1qAbrgYgCzxR4ANy8AAAAAAAABAAEAAgQABAAAVwCgprNW/f/Wqf7805///9Si///Uov//1KL//9Si///Uov//16f//9Wl/vy3Yv3/YwC0vwEBAAAAAAAAFQAoH1cAoalyAM/1hh/Y/Ywn3P2KI9v9iyTb/Ysl2/2LJdv9iybb/Yki2v2bQ+L927X9/Neq/v9qAbvdBQAJCAABAABeAKuzr1H7/8WP9Pu+f/H/vn/y/76A8v++gPL/voDy/75/8v+9fvL/wof0/4wq3P+eS+L93rT//2gAud0DAAcIBQAJCGsBvNzVp/3/4sH//d+7///hv///4b///+G////hv///4b///9+7/v/nyf//w4rx/4wr2v3guv//agK63gMABwgEAAcIagK53dWl///gwP793rv+/9++/v/gv/7/4L/+/+C//v/fvv7/3br+/+TG///BhvH/jy3b/eTD//9qA7reAwAHCAQABwhqArre2rD//+jP//3lyv//583//+fO///nzv//587//+fN///lyP7/7Nb//8WP8v+QL9v96c3//2oDut4DAAcIBAAHCGoCut7fuf//7tz//evW///t2f//7dr//+3a///t2v//7dn//+rV/v/y4///yJby/5Ax2/3s1f//agS63gMABwgEAAcIagK63uPB///z6P/98OH///Lk///z5f//8+X///Pl///y5P//8OD+//ju///LnfL/kTLb/fDc//9rBLreAwAHCAQABwhqArre5sn///jy//316v//9u3///fu///37///9+7///bt///06f7//fj//86j8v+RMtv98d///2oFud0DAAcIBAAHCGoCut7pz////Pr//fjx///69P//+vX///r1///69f//+vT///fw/v//////0ajx/5M12/305///awS83AQACQgEAAcIaQK53erR///+/v/9+vT///v3///8+P///Pj///z4///79///+fT+///////TrPL/iyjY+8N5/f9dAKuzAAIAAAUACQhqAbvd7dr+//////z+/f/9/////f////3////9/////f////39/P79/////dy+8/5vAND1VgKhqRUAJx8AAAAAAQIAAGIAtL/Jh/3/8uT+/O3X///u2f//7tn//+7Z///u2f//7tn//+3X///x4/38vW78/1UAoKYABQAAAQACBAAAAQAAAAAAHgA3LmEAs8VqArvgagK53WoCut5qArreagK63moCut5qArreagK53WsBvOBbAKq3FQAnHwAAAAACAAMDAAAAAAAAAAAAAAAAAwIEAQQACAgEAAcIBAAHCAQABwgEAAcIBAAHCAQABwgEAAcIBAAIBwMCAwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
this.tooltipText = cbu.getPrefs(s) ? 'Autocopy' : 'Stop Autocopy'; 

}
    toggleImage();
    ps.addObserver(s, toggleImage, false);
    addDestructor(() => ps.removeObserver(s, toggleImage));
})(Services.prefs);

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

Будет время, посмотри пожалуйста.

Отсутствует

 

№1722407-02-2025 08:18:38

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

Re: Custom Buttons

Garalf пишет

1. Экспорт в HTML файл в контекстном меню закладок

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

Выделить код

Код:

/*
        menuitem.setAttribute("oncommand", "exportFolder();");
        menuitem.exportFolder = this.pick.bind(this);
*/
        addEventListener("command", () => this.pick(), false, menuitem);
Выделить код

Код:

/*
        new this.nsvo.BookmarkExporter(bookmarks).exportToFile(path);
    },
    get nsvo() {
        delete this.nsvo;
        return this.nsvo = Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm", {});
*/
        new this.exporter(bookmarks).exportToFile(path);
    },
    get exporter() {
        var m = ChromeUtils.importESModule("resource://gre/modules/BookmarkHTMLUtils.sys.mjs");
        delete this.exporter;
        return this.exporter = cbu.dbg.ref("BookmarkExporter", m.BookmarkHTMLUtils.exportToFile);

2.Дополнительные пункты в контекстном меню кнопки

Жуть какая.
Желание в этом копаться пропадает с первого же взгляда.


Возможно, хватит таких формальных правок, ну или нет.

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

Выделить код

Код:

/*
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 re = /\)\.(.+?)\(/;
var listener = e => self[e.target.linkedMehtod](custombuttons.popupNode);

function addMenuItem(ids, nodeIds, label, image, code) {
    var num = 0;
    for(var id of ids) {
        $(id)?.remove();
        var menuitem = document.createXULElement("menuitem");
        menuitem.id = id;
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("image", image);
        menuitem.setAttribute("label", label);

        menuitem.linkedMehtod = code.match(re)[1];
        addEventListener("command", listener, false, menuitem);

        num || menuitem.setAttribute("observes", "custombuttons-contextbroadcaster-primary");

        var node = $(nodeIds[num++]);
        node?.parentNode.insertBefore(menuitem, node.nextSibling);
    }
}
Выделить код

Код:

/*
        moveMIEl.setAttribute("onclick", "document.getElementById(\"" +
            self.id + "\").MoveToolbarButtons.MoveOnClick(event);");
*/
        moveMIEl.onclick = e => this.MoveOnClick(e);

3. Autocopy

Здесь не вижу никакого отвала.
Кнопка всего лишь (и успешно) переключает настройку
ucf.autocopy.enabled

Отсутствует

 

№1722507-02-2025 13:55:04

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

Re: Custom Buttons

Dumby
Большое спасибо!
И еще. Не знаю, как сразу не заметил
Toggle Restartless Add-ons

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

Выделить код

Код:

// 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", "plugin"],
	// 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", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
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;
	if(Components.interfaces.nsIWebTransportHash) { // random, Fx 123+
		var func = function() {
			func = false;
		}
		var thread = Services.tm.currentThread;
		var meth = newDis ? "disable" : "enable";
		addon[meth]({allowSystemAddons: true}).finally(func);
		while(func) thread.processNextEvent(true);
	}
	else 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");
	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 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));
	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 || window.BrowserAddonUI.openAddonsMgr // 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;
}// 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", "plugin"],
	// 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", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
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;
	if(Components.interfaces.nsIWebTransportHash) { // random, Fx 123+
		var func = function() {
			func = false;
		}
		var thread = Services.tm.currentThread;
		var meth = newDis ? "disable" : "enable";
		addon[meth]({allowSystemAddons: true}).finally(func);
		while(func) thread.processNextEvent(true);
	}
	else 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");
	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 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));
	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 || window.BrowserAddonUI.openAddonsMgr // 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;
}

Кнопка не срабатывает

Отсутствует

 

Board footer

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