Объявление

В связи с наплывом спама и ботов на форуме, регистрация новых пользователей будет приостановлена. О восстановлении регистрации будет сообщено дополнительно

Administrator

№197601-05-2025 01:01:00

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

Re: UCF - ваши кнопки, скрипты…

Да еще может кто нибудь сделать тултип вот таким

image.png

т.е. Ladle один цвет, URL другой цвет

Отсутствует

 

№197701-05-2025 02:15:05

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

Re: UCF - ваши кнопки, скрипты…

Andrey_Krropotkin
А где старый стиль?

Отсутствует

 

№197801-05-2025 02:55:31

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

Re: UCF - ваши кнопки, скрипты…

dinn, Andrey_Krropotkin
Да, спасибо. У меня попутно ещё одна кнопка отвалилась "Память/минимизировать в адресной строке".

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

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=789048#p789048
// https://forum.mozilla-russia.org/viewtopic.php?pid=789076#p789076
// https://forum.mozilla-russia.org/viewtopic.php?pid=791818#p791818
// https://forum.mozilla-russia.org/viewtopic.php?pid=795879#p795879
// здесь трындёж на 2-3 страницы https://forum.mozilla-russia.org/viewtopic.php?id=9591&p=612
(async id => ({

  delay: 2e3,

  val: "",
  init(topic, mm) {
    Services.obs.addObserver(mm = this, topic);
    Services.obs.addObserver(function quit(s, t) {
      this.timer?.cancel();
      Services.obs.removeObserver(mm, topic);
      Services.obs.removeObserver(quit, t);
    }, "quit-application-granted");
  },
  observe(win) {
    var df = win.MozXULElement.parseXULToFragment(
/*
      `<hbox id="${id}"><label id="${id += "-label"}"/></hbox>`
*/
      `<hbox id="${id}" tooltiptext="${
        "ЛКМ: Минимизировать потребление памяти&#xA;ПКМ: about:process&#xA;Ctrl+ПКМ: about:debugging#/runtime/this-firefox"
      }" onclick="event.button || ${
        "memoryMinimizationButton.doMinimize(event)"
      }"><label id="${id += "-label"}"/></hbox>`
    );
    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    (this.observe = async win => {
      this.timer.cancel();
      await new Promise(ChromeUtils.idleDispatch);
/*
      win.document.getElementById("star-button-box")
        .after(win.document.importNode(df, true));
      this.notify();
    })(win);
  },
*/
      var clone = win.document.importNode(df, true);
      clone.firstChild.oncontextmenu = this.about;
      win.document.getElementById("star-button-box").after(clone);
      this.notify();
    })(win);
  },
  about(e) {
    var gb = e.view.gBrowser;
    gb.selectedTab = gb.addTrustedTab(`about:${
      e.ctrlKey ? "debugging#/runtime/this-firefox" : "processes"
    }`);
  },
  async notify() {
    var info = await ChromeUtils.requestProcInfo();
    var bytes = info.memory;
    for(var child of info.children) bytes += child.memory;
    this.timer.initWithCallback(this, this.delay, this.timer.TYPE_ONE_SHOT);

    var prev = this.val;
    if ((this.val = this.mgb(bytes)) != prev)
      for(var win of CustomizableUI.windows) {
        var lab = win.document.getElementById(id);
        if (lab) lab.value = this.val;
      }
  },
  mgb: bytes => bytes < 1073741824
    ? Math.round(bytes / 1048576) + "MB"
    : (bytes / 1073741824).toFixed(2) + "GB"
}).init("browser-delayed-startup-finished"))("ucf-mem-indicator");


Там функция из скрипта по ЛКМ не запускалась. С параметром security.browser_xhtml_csp.enabled --> false заработало.
Andrey_Krropotkin
Ты про эту кнопку? Вроде, работает. Просто одну команду попробовал, для теста.
Тултипы для закладок нужны? У меня точно нет, есть подобное для урлов... hover_links

Отсутствует

 

№197901-05-2025 08:56:57

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

Re: UCF - ваши кнопки, скрипты…

_zt - это не стиль, это делается только скриптом, мозиловский блокируется и заменяется новым. Сделал скрин из глючного зарубежного скрипта undoCloseTab вот отсюда, которым не пользуюсь и в том скрипте намешано так, что ничего не поймешь. Пользуюсь undoCloseTab от Виталия. Там у этого человека  еще в 2 скриптах такое же, но  я не разобрался и поэтому спросил, может кто сварганит именно на тултипы, где есть ссылки - типа пунктов Журнала, Закладок
А если ты про боковую панель, то в Настройки- Основные есть пункт: Показать боковую панель, я именно про это. Переключает вид боковой панели


xrun1 - да именно про этот скрипт в этом посте, и повторюсь при переключении на новый вид боковой панели - не работает, при старом виде - работает

Отредактировано Andrey_Krropotkin (01-05-2025 09:02:14)

Отсутствует

 

№198001-05-2025 10:13:33

dinn
Участник
 
Группа: Members
Зарегистрирован: 28-09-2024
Сообщений: 78
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

Andrey_Krropotkin пишет

но 1 и 3 нет

может так

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

Выделить код

Код:

btn.addEventListener(type, this);
..
handleEvent(e) {
	this[e.type](e);
},
..
popup.addEventListener(type, this);

Отсутствует

 

№198101-05-2025 13:23:35

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

Re: UCF - ваши кнопки, скрипты…

Andrey_Krropotkin
Ясно, я сначала не понял что это для закладок. Думал TooltipOverLink с каким то стилем внутри.

Отсутствует

 

№198206-05-2025 23:07:07

dinn
Участник
 
Группа: Members
Зарегистрирован: 28-09-2024
Сообщений: 78
UA: Firefox 138.0

Re: UCF - ваши кнопки, скрипты…

xrun1 пишет

У меня попутно ещё одна кнопка отвалилась "Память/минимизировать в адресной строке"
Там функция из скрипта по ЛКМ не запускалась. С параметром security.browser_xhtml_csp.enabled --> false заработало

Плохой совет дал, если речь об "on" атрибутах. Думаю, никому не составит сложности переделать под addEventListener.
Совет для quick_toggle_about_config_button.js тоже дал, но никто даже не попытался попробовать

Отсутствует

 

№198307-05-2025 00:12:05

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

Re: UCF - ваши кнопки, скрипты…

dinn пишет

Совет для quick_toggle_about_config_button.js тоже дал, но никто даже не попытался попробовать

Это №1980? Я, например, не понял. Мне надо написать, что закомментировать и что и куда вставить

Выделить код

Код:

/*
    старый код закомментировать или удалить
*/
    сюды-вставить-новый-код

Код своих кнопок выкладывал выше, №1970 и №1978.
Мне UCF без чужой помощи, как от дохлого осла уши. Те 5 кнопок, что сваял сам, однокомандные и без них смогу обойтись. Сам сложное не напишу и готовое не исправлю. Это продукт для тех, кто разбирается в кишках Firefox. А я простой пользователь, а не потрошитель.:) Мне готовенькое надо.
Так что, пока сработало с security.browser_xhtml_csp.enabled в false - буду пользоваться своими кнопками. Когда отвалятся - буду искать варианты на замену.
Или предлагай вариант по шаблону выше.;)

Отсутствует

 

№198407-05-2025 18:01:15

dinn
Участник
 
Группа: Members
Зарегистрирован: 28-09-2024
Сообщений: 78
UA: Firefox 138.0

Re: UCF - ваши кнопки, скрипты…

security.browser_xhtml_csp.* // default values
Если оба поставить в true, то блокировок для скриптов не будет, а только ошибки в консоли


quick_toggle_about_config_button.js 138+

Выделить код

Код:

//  btn.linkedObject = this;
..
//  btn.setAttribute("on" + type, `linkedObject.${type}(event)`);
    btn.addEventListener(type, this);
..
//  popup.setAttribute("on" + type, `parentNode.linkedObject.${type}(event)`);
    popup.addEventListener(type, this);
..
//  m.setAttribute("oncommand", "setCloseMenus(event)");
    m.addEventListener("command", setCloseMenus);
//  m.onclick = m.oncontextmenu = m.setCloseMenus = setCloseMenus;
..
    handleEvent(e) {
      this[e.type](e);
    },


На счет последнего коммента так и не понял в каких случаях эта строка нужна. handleEvent можно вставить, например, перед command(e)

Отсутствует

 

№198508-05-2025 12:17:06

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

Re: UCF - ваши кнопки, скрипты…

dinn
Работает, спасибо.
А что с этой?

memory_used_in_urlbar.js

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=789048#p789048
// https://forum.mozilla-russia.org/viewtopic.php?pid=789076#p789076
// https://forum.mozilla-russia.org/viewtopic.php?pid=791818#p791818
// https://forum.mozilla-russia.org/viewtopic.php?pid=795879#p795879
// здесь трындёж на 2-3 страницы https://forum.mozilla-russia.org/viewtopic.php?id=9591&p=612
(async id => ({

  delay: 2e3,

  val: "",
  init(topic, mm) {
    Services.obs.addObserver(mm = this, topic);
    Services.obs.addObserver(function quit(s, t) {
      this.timer?.cancel();
      Services.obs.removeObserver(mm, topic);
      Services.obs.removeObserver(quit, t);
    }, "quit-application-granted");
  },
  observe(win) {
    var df = win.MozXULElement.parseXULToFragment(
/*
      `<hbox id="${id}"><label id="${id += "-label"}"/></hbox>`
*/
      `<hbox id="${id}" tooltiptext="${
        "ЛКМ: Минимизировать потребление памяти&#xA;ПКМ: about:process&#xA;Ctrl+ПКМ: about:debugging#/runtime/this-firefox"
      }" onclick="event.button || ${
        "memoryMinimizationButton.doMinimize(event)"
      }"><label id="${id += "-label"}"/></hbox>`
    );
    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    (this.observe = async win => {
      this.timer.cancel();
      await new Promise(ChromeUtils.idleDispatch);
/*
      win.document.getElementById("star-button-box")
        .after(win.document.importNode(df, true));
      this.notify();
    })(win);
  },
*/
      var clone = win.document.importNode(df, true);
      clone.firstChild.oncontextmenu = this.about;
      win.document.getElementById("star-button-box").after(clone);
      this.notify();
    })(win);
  },
  about(e) {
    var gb = e.view.gBrowser;
    gb.selectedTab = gb.addTrustedTab(`about:${
      e.ctrlKey ? "debugging#/runtime/this-firefox" : "processes"
    }`);
  },
  async notify() {
    var info = await ChromeUtils.requestProcInfo();
    var bytes = info.memory;
    for(var child of info.children) bytes += child.memory;
    this.timer.initWithCallback(this, this.delay, this.timer.TYPE_ONE_SHOT);

    var prev = this.val;
    if ((this.val = this.mgb(bytes)) != prev)
      for(var win of CustomizableUI.windows) {
        var lab = win.document.getElementById(id);
        if (lab) lab.value = this.val;
      }
  },
  mgb: bytes => bytes < 1073741824
    ? Math.round(bytes / 1048576) + "MB"
    : (bytes / 1073741824).toFixed(2) + "GB"
}).init("browser-delayed-startup-finished"))("ucf-mem-indicator");


Меню есть, но скрипт не выполняется. Поправить можно?
Чтобы тебе было проще, вот UCF с кнопками и скриптом. Первую поправил, вторая не рабочая.

Отсутствует

 

№198608-05-2025 14:47:27

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 662
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

В 139 версии у меня отвалилось несколько скриптов. Но в первую очередь хотелось бы исправить два скрипта.
Первый в консоль пишет: "Content-Security-Policy: Параметры страницы заблокировали выполнение JavaScript eval (script-src), поскольку он нарушает следующую директиву: «script-src chrome: moz-src: resource: 'report-sample'» (Отсутствует 'unsafe-eval')"
Второй в консоль пишет: "EvalError: call to Function() blocked by CSP"


Я знаю, что можно поиграться параметрами about:config - security.browser_xhtml_csp.* и в 139 - security.allow_unsafe_dangerous_privileged_evil_eval, но думаю, что это временное решение и рано или поздно эти параметры выпилят и все равно придется править скрипты.


Просьба к знатокам, помогите исправить эти два скрипта.

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

Выделить код

Код:

//
// Сохранять изображение без запроса в указанную папку, из контекстного меню .....
//
try {
(this.contextsaveimg = {
    path: "E:\\Download",
    init(that) {
        var contextMenu = this.contextMenu = document.querySelector("#contentAreaContextMenu");
        if (!contextMenu) return;
        contextMenu.addEventListener("popupshowing", this);
        that.setUnloadMap("contextsaveimg", this.destructor, this);
    },
    destructor() {
        this.contextMenu.removeEventListener("popupshowing", this);
    },
    handleEvent(e) {
        if (!gContextMenu.onImage || gContextMenu.webExtBrowserType === "popup") return;
        var menuitem = document.createXULElement("menuitem");
        menuitem.setAttribute("id", "ucf_SaveImg");
        menuitem.setAttribute("label", "Сохранить изображение в папку: " + this.path);
        menuitem.addEventListener("click", function(e) { saveImg(); });
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABAnAAAQJwAAAAAAAAAAAAAAAAEGAQECKAABATsAAAAoAAAAFAAAAAkAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjRRXUVtk8U+Y4TGKUBYrRgmNpcOFR91BQgMWAECBD8AAQEqAAAAGAAAAAwAAAAGAAAAAQAAAAAAAAAAAAAAAEVrp7Nws9//fcr6/3W+9f5wten5ZKHQ7lWGsedDaI7QL0hjtR4sQKERGSaBBgkOXgAAACoAAAAEAAAAAAAAAABKdrK+aqjY/4XO7P+AyfL/fcXy/4HL+/97xPT/eMHy/3e+8f9trt77YpvI8Ux0m98TGymTAAAAJQAAAAAAAAAAVIS7yGWh0/8yTVv/UniH/0htff+L0e//WIab/0x2i/9XiKL/RGp+/zNRYv9dkrP+S26S2QQGClwAAAAGAAAAAGeYxtFxrtr/IDA7/01mbf8sPkP/SWZw/zlSWv88Vl//LkRL/3Wsv/83UVr/XYqe/3+51PQaJDCWAAAAGwAAAAB5qtHajMXk/yY1Pf9fa23/GB8g/2yGjP8mMDL/Sl9k/0RYXP9wmKL/dZ+r/4i6y/+n4PD+TmV5yQECA0UAAAACh7bX46zh8/9tjZj/gpen/4GMk//G4Oj/kJ6f/52clf+svbz/f5id/3yXnv+gx9L/wez1/5u0xuwPExt+AAAAEJTA3ey/7vr/ve35/7Hf8v+w0ub/ubvE/8a4uf/NwLv/083I/9Pl6//T7ff/0O32/9fw9v/a6vL8P0ZYtAAAAC+iy+P2zvX8/831/P/K6e3/y7Kg/+GNZf/vlWr/6Zx3/9qegv/Ino7/t8TQ/7HM4/+itc3yu8ja52BtirQAAAEkqs/l9dv8/f/b/P3/1dzU/9qXcf/slmb/+aFr//+td//+rXb/+aRu/9+1k/+/2uL/RldrtiIrPU0ZITEoAAAAA46017zX8Pf85/7+/+X8+//g7+3+yryw+O6odPz/wYj/9bJ7/+KpgP/nqn3/0tPJ/kRTY6QAAQEYAAAAAAAAAAA6V3Yuboyqj6C81My50ubgo7zR1I92bLbzt3/3/tGZ/8aTZttfY2+PbX2Sn3GFm7EiLDtNAAAABQAAAAAAAAAAAgMEAAUHCgYYJDETIS4+LjMwNU+3e029/NGZ/vHHlPRrSSx/BQUFEAMFBwcEBQcJAQECAgAAAAAAAAAAAAAAAAAAAAAAAAAAEQgAA1wvElSsaz3B9LZ8+fC7gu6WZ0COFg0GGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcTAQrWbS+y7pdZ8eOVWMqUWy91IRIJFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AACf/wAAgH8AAIAfAACADwAAgA8AAIAHAACABwAAgA8AAIAPAADOHwAA/v8AAP//AAD//wAA//8AAA==");
        (this.contextMenu.querySelector("#context-sendimage") || this.contextMenu.lastElementChild).after(menuitem);
        this.handleEvent = () => menuitem.hidden = (!gContextMenu.onImage || gContextMenu.webExtBrowserType === "popup");

        function saveImg() {
            var p = Services.prefs;
            var data = Object.assign(Object.create(null), {
                "browser.download.folderList": { type: "Int", set: 2 },
                "browser.download.useDownloadDir": { type: "Bool", set: true },
                "browser.download.dir": { type: "String", set: this.path }
            });
            var lazy = {PrivateBrowsingUtils};
            var save = eval(`(function ${gContextMenu.saveMedia})`.replace("\n        false, // don't", "\n        true, //"));
            (menuitem.saveImg = () => {
                for(var pref in data) {
                    var obj = data[pref], meth = `et${obj.type}Pref`;
                    obj.val = p.prefHasUserValue(pref) ? p["g" + meth](pref) : null;
                    p["s" + meth](pref, obj.set);
                }
                try {save.call(gContextMenu);} finally {
                    for(var pref in data) data[pref].val === null
                        ? p.clearUserPref(pref)
                        : p[`set${data[pref].type}Pref`](pref, data[pref].val);
                }
            })();
        }
    }
}).init(this);
} catch(e) { Cu.reportError(e); }

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

Выделить код

Код:

//
// Сохранить как PNG .....
//
(async func => CustomizableUI.createWidget({
    id: "ucf_SaveAsPNG",
    label: "Сохранить как PNG",
    tooltiptext: "Сохранить как PNG",
    localized: false,
    // defaultArea: CustomizableUI.AREA_NAVBAR,
    onCreated(btn) {
        var win = btn.ownerGlobal;
        new win.Function("_id, xhtmlns, addDestructor", func.toString().slice(7, -1)).call(
            btn, this.id, "http://www.w3.org/1999/xhtml",
            destructor => win.addEventListener("unload", destructor, {once: true})
        );
        btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGwSURBVHjarJPPaxNBGIafmd1km23saqtRaLB6kFYl2ELpRayCVL20RvCmJwk9ePNWvPsXtCA9eNCDeO0hLemPo96LYAuKkRhtsBViEtI0rrt+GyFQ1oMkfjAf883M+8w7w4zyfZ9uwsxms10RzCBNTd3sSLy2lvsDCCKzWMCKGHgotOTAlpamJPm+jCmFL00bmmbD5WlmsKXTdBltB3bcxowaLWJwsTpi4bvNVu1JMmVn5cmcqTB6/DAg3hcjGpUFskjZjlgFKdmvVIiISKlfUhlyQMVPywgDjvX1YCjpOBZ2fZ+9+TuUpxcZuz7Ebl7ArkfM0dgDcOAFCvfwHTi9URJJi2RCqEuPmRxPUXn9imodLo1D6orGjMHW6govZqfDDkYviDXBlZ4vsFsrk8vXqZRWeDY8hz55lvvpGU7sFEn0RhhTtTBg/tETCvlNdj68papsUoND3Lh6iyPOOYrvv/JufYNrqcssbW8zkjwdBnz7/JKL50/x4F4at9GgsFckX/2IefQHZyb6mbl9l2LpE8ff1PnufWkD/t87yC1v/pvi4V8AwZvuNFS33/m3AAMAhEeAiqLmty4AAAAASUVORK5CYII=");
    }
}))(() => {
((main, parts) => this._handleClick = () => {
    var df = MozXULElement.parseXULToFragment(`
        <menupopup>
            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
                label="Сохранить всю страницу как PNG"
                value="all"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKaSURBVHjaYpx94sPD44//CDG9+/SDp8bsBw9AADEmrX7zmQEIGF+//fD/w8dPDAABxNix6/nbcA0GIQYoYGJiZGSQFBdmOHnqLAMrKwsDQAAxJq958/n/f4jsm2//GBifP3v+/8cfiMDDj/8ZmHi5ORl4gPjPn18M///9ZWDh5uVm4GJkYfj56zcD9z9GBoAAYmwH2aL8UwimDRk8+fyfgYWRgZFBgI+b4TsWBR+BtoNcxcDDwwW2R1iQn2HvgcMMX799Y/j6/TvDzz+/GVj+/fvPvO0BC8N/oGpGhv8ML3m0GS59EwWbcP/9XwaAAGSRwQ3CMAxFfxq3TgrqiSMswARMwSJMxZEdGAXBAEiQquICJMFuekDqwTlY38/Wi9kf78OqNTO8kndretBh+1puuvl+R8Dpwm9iruG8xPP/tIGzhULc1OB6voJJXxGjQpumGpsKCc8wEqISwGJfA1P1IYCEmCegkTmqLIELT/8KC+/x+cbxBpEPOl8zbn0q5voWXdcipQQrgZiC/QlQORmzNBAEUfjt7N4ichBUDIKCWNsJhiBiZ2mZf5XC0v/gT7C10lqwNMVBkBC9kKAmt7e+uQtJxBS6cNwy7L2b981bTrz/fnmIxnHTYN081i1F8PQacddDzm0M+UfAYPw/gfyTlqMNVfOJVxIGpfxNgMcZRwoYpkVpe1ezLn/jplc7B2wWb+1gwwfuvxg31lwi7MAimOU4E+IevuXoXt+gILTE1aQ1VBJnwNYR9ltXFKCweM9RWFpYCnjWiukU7dYJLs7PUMyKuglNAZt6GVncZ7SgIU2ZJ8M/bLqVQEFYN9hJPQ5201pgvhztjnk2ZhOYh+esd/vY39ZPZYWBXlURqZ4Qwg8uZXU3gM7p3vAbsXveewxHZWUAAAAASUVORK5CYII="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL/SURBVHjaYijccvd25ZHvv5n4RSUVQs2vswAEECOIxwACy+8v/3/30Zv/AAHE0Hb6y+//UPDv4///LD/+MjJ8+PqF4cvnnww/vgkxAAQQY8Xhb78YgQCsj5mFgQGm/P+v///fvvj/n+n/fwaGe//vMexg2Mrw7BsDA8vXHwwMPN/5GdQ/mzG8BbIBAoiRfdqvX8xf/rBCDAEaAWL8ByOgmZwMLBXGvxnrzLgYVj9cyfCL+TfDj9+/Gd7//sAQw5vK8PEXAwMTRBsD0Li3DNKikgwC4twM3NIMDF9/fGf4CVTA8u0fM+ODjwwM7qJZDH8/MTBI/GFgUAba/QYo+fEnAwNAADKoXQVhIAjOJYfgsxIR/AwRi/yBlY3gz9j7FUI+wt7CwkbEzs7CNIo2Yl53t3duElDBhYXZZXaWGbHYKW0M4b+cex7WG9FaxmxGfNcfYNH3T+pr/LcSzu1h3Wx11EU2eOkYYRSi7jVgrQeb1TDtzFHkLgu13GQIBgGkJ6GFxva8h6XqX0kgRlF+AfkEw4S7ujEGbKnAzDa6GNEYLvOh2N6wOUGaVXHKayqQsHeiHgyfqKJ51sbxjvAWoG1qWWkYiKJnZhKTpgapK1tEslRw50+4camIGz/ObfcuBP0HBREEn12I+MBUadPM5HomqbWgi8kJydzHOfdctXmU29Vhbhqq8leNfxQyLP6lWgjjdxds9Yxs95ZRVAo+h8yFqEau2fHEKjpKqgojRDg+H0iw1gb212NY2oC/MCxz5DxGaYgPsKrGsnRYUEusnvKb4MNqnBlN5aZD8q54Hb2gP+gj62bohOyKdVxgobTBk3vE7cMzDhYPkVA0NW0xaBrlk6BZtRN20I16SMIWPt0CJlLQowqJStE2YwqnQRbE2ZwazrYURDquL5zcnCINU4xdiYo7QXXo4TesSMaIGGRTJ6jnqBxvkJNwfhFa2Ovu1jlrwXjD2gZ9wITeKVhImEFXgXcIgrvri/udqySztpDZemJurTzK70h/3h1xY3xpvwGLI42vrCwxmAAAAABJRU5ErkJggg=="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menupopup>
    `);
    var popup = df.firstChild;
    popup.setAttribute("context", "");

    popup.addEventListener("command", e => popup.handleCommand(e));

    popup.handleCommand = e => {
        var name = _id + ":DataURLReady";
        main = main.replace("%MESSAGE_NAME%", name);

        var urls = {}, configurable = true, enumerable = true;
        Object.entries(parts).forEach(([key, part]) => Object.defineProperty(urls, key, {
            configurable, enumerable, get() {
                var value = `data:;charset=utf-8,({${
                    encodeURIComponent(main + part)
                }%0A}).init("${key}")`;
                Object.defineProperty(urls, key, {configurable, enumerable, value});
                return value;
            }
        }));

        var getTabLabel = () => {
            var label = gBrowser.selectedTab.label;
            var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
            return label.substring(0, 50);
        }

        var listener = msg => {
            var fp = makeFilePicker();
            fp.init(
                !("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
                    ? window.browsingContext
                    : window
                    , "Сохранить как…", fp.modeSave);
            fp.appendFilter("", "*.png");

            var fileName = getTabLabel();
            fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '').replace(/\s+/g, '_').slice(0, 100).replace(/^\s+|\s+$/g, '');
            var fileDate = (function () {
                var d = new Date(), z = function(n){return (n < 10 ? '0' : '') + n};
                return '[' + z(d.getFullYear()) + '_' + z(d.getMonth()+1) + '_' + z(d.getDate()) + '\u2014' + z(d.getHours()) + '_' + z(d.getMinutes()) + '_' + z(d.getSeconds()) + ']';
            })();

            fp.defaultString = fileName + "_" + fileDate + ".png";
            fp.open(res => res == fp.returnCancel || !fp.file || makeWebBrowserPersist().saveURI(
                Services.io.newURI(msg.data), document.nodePrincipal,
                null, null, null, null, null, fp.file, null, null
            ));
        }
        messageManager.addMessageListener(name, listener);
        addDestructor(() => messageManager.removeMessageListener(name, listener));

        (popup.handleCommand = e => gBrowser.selectedBrowser.messageManager
            .loadFrameScript(urls[e.target.value], false)
        )(e);
    }
    this.append(df);
    (this._handleClick = () => popup.openPopup(this, "after_start"))();
})(`
    init(cmd) {
        cmd.startsWith("c")
            ? this[cmd].init(this[cmd].parent = this)
            : this[cmd]();
    },
    capture(win, x, y, width, height) {
        var canvas = win.document.createElementNS("${xhtmlns}", "canvas");
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        var tryDraw = ind => {
            try {ctx.drawWindow(win, x, y, canvas.width, canvas.height, "white")}
            catch(ex) {canvas.height = ind * canvas.width; tryDraw(--ind);}
        }
        tryDraw(17);
        sendAsyncMessage("%MESSAGE_NAME%", canvas.toDataURL("image/png"));
    },
    `, {

        all: `all() {
            var win = content;
            this.capture(win, 0, 0, win.innerWidth + win.scrollMaxX, win.innerHeight + win.scrollMaxY);
        }`,
        page: `page() {
            var win = content, doc = win.document, body = doc.body, html = doc.documentElement;
            var scrX = (body.scrollLeft || html.scrollLeft) - html.clientLeft;
            var scrY = (body.scrollTop || html.scrollTop) - html.clientTop;
            this.capture(win, scrX, scrY, win.innerWidth, win.innerHeight);
        }`,
        clipping: `clipping: {
            handleEvent(e) {
                if (e.button) return false;
                e.preventDefault();
                e.stopPropagation();
                switch(e.type) {
                    case "mousedown":
                        this.downX = e.pageX;
                        this.downY = e.pageY;
                        this.bs.left = this.downX + "px";
                        this.bs.top = this.downY + "px";
                        this.body.appendChild(this.box);
                        this.flag = true;
                        break;
                    case "mousemove":
                        if (!this.flag) return;
                        this.moveX = e.pageX;
                        this.moveY = e.pageY;
                        if (this.downX > this.moveX) this.bs.left = this.moveX + "px";
                        if (this.downY > this.moveY) this.bs.top  = this.moveY + "px";
                        this.bs.width = Math.abs(this.moveX - this.downX) + "px";
                        this.bs.height = Math.abs(this.moveY - this.downY) + "px";
                        break;
                    case "mouseup":
                        this.uninit();
                        break;
                }
            },
            init() {
                var win = {};
                Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
                    .getFocusedElementForWindow(content, true, win);
                this.win = win.value;

                this.doc = this.win.document;
                this.body = this.doc.body;
                if (!HTMLBodyElement.isInstance(this.body)) {
                    Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
                        .showAlertNotification("${self.image}", ${JSON.stringify(self.label)}, "Не удается захватить!");
                    return false;
                }
                this.flag = null;
                this.box = this.doc.createElement("div");
                this.bs = this.box.style;
                this.bs.border = "red dashed 1px";
                this.bs.position = "absolute";
                this.bs.zIndex = "2147483647";
                this.defaultCursor = this.win.getComputedStyle(this.body, "").cursor;
                this.body.style.cursor = "crosshair";
                ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.addEventListener(type, this, true));
            },
            uninit() {
                var pos = [this.win, parseInt(this.bs.left), parseInt(this.bs.top), parseInt(this.bs.width), parseInt(this.bs.height)];
                this.body.style.cursor = this.defaultCursor;
                this.body.removeChild(this.box);
                this.parent.capture.apply(this, pos);
                ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.removeEventListener(type, this, true));
            }
        }`,
        click: `click: {
            getPosition() {
                var html = this.doc.documentElement;
                var body = this.doc.body;
                var rect = this.target.getBoundingClientRect();
                return [
                    this.win,
                    Math.round(rect.left) + (body.scrollLeft || html.scrollLeft) - html.clientLeft,
                    Math.round(rect.top) + (body.scrollTop || html.scrollTop) - html.clientTop,
                    parseInt(rect.width),
                    parseInt(rect.height)
                ];
            },
            highlight() {
                this.orgStyle = this.target.hasAttribute("style") ? this.target.style.cssText : false;
                this.target.style.cssText += "outline: red 1px solid; outline-offset: 1px; -moz-outline-radius: 2px;";
            },
            lowlight() {
                if (this.orgStyle) this.target.style.cssText = this.orgStyle;
                else this.target.removeAttribute("style");
            },
            handleEvent(e) {
                switch(e.type) {
                    case "click":
                        if (e.button) return;
                        e.preventDefault();
                        e.stopPropagation();
                        this.lowlight();
                        this.parent.capture.apply(this, this.getPosition());
                        this.uninit();
                        break;
                    case "mouseover":
                        if (this.target) this.lowlight();
                        this.target = e.target;
                        this.highlight();
                        break;
                }
            },
            init() {
                this.win = content;
                this.doc = content.document;
                ["click", "mouseover"].forEach(type=> this.doc.addEventListener(type, this, true));
            },
            uninit() {
                this.target = false;
                ["click", "mouseover"].forEach(type=> this.doc.removeEventListener(type, this, true));
            }
        }`
    });
});


«The Truth Is Out There»

Отсутствует

 

№198708-05-2025 16:43:38

dinn
Участник
 
Группа: Members
Зарегистрирован: 28-09-2024
Сообщений: 78
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

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

memory_used_in_urlbar.js 138+

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=789048#p789048
// https://forum.mozilla-russia.org/viewtopic.php?pid=789076#p789076
// https://forum.mozilla-russia.org/viewtopic.php?pid=791818#p791818
// https://forum.mozilla-russia.org/viewtopic.php?pid=795879#p795879
// здесь трындёж на 2-3 страницы https://forum.mozilla-russia.org/viewtopic.php?id=9591&p=612
(async id => ({

  delay: 2e3,

  val: "",
  init(topic, mm) {
    Services.obs.addObserver(mm = this, topic);
    Services.obs.addObserver(function quit(s, t) {
      this.timer?.cancel();
      Services.obs.removeObserver(mm, topic);
      Services.obs.removeObserver(quit, t);
    }, "quit-application-granted");
  },
  observe(win) {
    var df = win.MozXULElement.parseXULToFragment(
/*
      `<hbox id="${id}"><label id="${id += "-label"}"/></hbox>`
*/
      `<hbox id="${id}" tooltiptext="${
        "ЛКМ: Минимизировать потребление памяти&#xA;ПКМ: about:process&#xA;Ctrl+ПКМ: about:debugging#/runtime/this-firefox"
      }"><label id="${id += "-label"}"/></hbox>`
    );
    this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
    (this.observe = async win => {
      this.timer.cancel();
      await new Promise(ChromeUtils.idleDispatch);
/*
      win.document.getElementById("star-button-box")
        .after(win.document.importNode(df, true));
      this.notify();
    })(win);
  },
*/
      var clone = win.document.importNode(df, true);
      clone.firstChild.addEventListener("click", this.memind);
      win.document.getElementById("star-button-box").after(clone);
      this.notify();
    })(win);
  },
  memind(e) {
    if (e.button) {
      var gb = e.view.gBrowser;
      gb.selectedTab = gb.addTrustedTab(`about:${
        e.ctrlKey ? "debugging#/runtime/this-firefox" : "processes"
      }`);
    } else e.view.memoryMinimizationButton.doMinimize(e);
  },
  async notify() {
    var info = await ChromeUtils.requestProcInfo();
    var bytes = info.memory;
    for(var child of info.children) bytes += child.memory;
    this.timer.initWithCallback(this, this.delay, this.timer.TYPE_ONE_SHOT);

    var prev = this.val;
    if ((this.val = this.mgb(bytes)) != prev)
      for(var win of CustomizableUI.windows) {
        var lab = win.document.getElementById(id);
        if (lab) lab.value = this.val;
      }
  },
  mgb: bytes => bytes < 1073741824
    ? Math.round(bytes / 1048576) + "MB"
    : (bytes / 1073741824).toFixed(2) + "GB"
}).init("browser-delayed-startup-finished"))("ucf-mem-indicator");

Отредактировано dinn (08-05-2025 16:46:15)

Отсутствует

 

№198808-05-2025 23:24:31

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

Re: UCF - ваши кнопки, скрипты…

Save_Button

Выделить код

Код:

// Кнопка Save - Основной скрипт в Save_Script.js
// Правка Vitaliy V. 2505
// Предыдущая версия
// https://forum.mozilla-russia.org/viewtopic.php?pid=809376#p809376

(async () => CustomizableUI.createWidget({
	id: "ucf-cbbtn-Save",
	tooltiptext: "Сохранить страницу\n/ часть / выбранное",
	nameSaveScript: "Save_Script.js",
	localized: false,
	cbu: {
		types: {
			128: "Bool", boolean: "Bool",
			64: "Int", number: "Int",
			32: "String", string: "String"
		},
		getPrefs(pref) {
			try {
				return Services.prefs[`get${
					this.types[Services.prefs.getPrefType(pref)]
				}Pref`](pref);
			}
			catch {return null;}
		},
		setPrefs(pref, val) {
			Services.prefs[`set${this.types[typeof val]}Pref`](pref, val);
		}
	},
	gClipboard: {
		get ch() {
			delete this.ch;
			return this.ch = Cc["@mozilla.org/widget/clipboardhelper;1"]
				.getService(Ci.nsIClipboardHelper);
		},
		write(str) {
			this.ch.copyStringToClipboard(str, Services.clipboard.kGlobalClipboard);
		}
	},
	custombuttonsUtils: {
		writeFile(path, data) {
			try {
				if (path.includes(":\\")) path = path.replace(/\//g, "\\");
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				file.exists() && file.remove(false);

				var strm = Cc["@mozilla.org/network/file-output-stream;1"]
					.createInstance(Ci.nsIFileOutputStream);
				strm.init(file, 0x04 | 0x08, 420, 0);
				strm.write(data, data.length);
				strm.flush();
				strm.close();
			} catch(ex) {
				Cu.reportError("Custom Buttons: " + [path, "---", ex, ex.stack].join("\n"));
			}
		}
	},
	addDestructor(destructor, context) {
		this._destructors.push({destructor, context});
	},
	addEventListener(...args) {
		var trg = args[3];
		if (!trg) trg = args[3] = this.ownerGlobal;
		trg.addEventListener(...args);
		this._handlers.push(args);
	},

	onCreated(btn) {
		var win = btn.ownerGlobal;
		btn._handlers = new win.Array();
		btn._destructors = new win.Array();
		win.addEventListener("unload", this, {once: true});
		btn.self = btn;
		btn._id = this.id;
		btn.cbu = this.cbu;
		btn.xhtmlns = "http://www.w3.org/1999/xhtml";
		btn.addDestructor = this.addDestructor.bind(btn);
		btn.addEventListener = this.addEventListener.bind(btn);
		btn.gClipboard = this.gClipboard;
		btn.custombuttonsUtils = this.custombuttonsUtils;
		Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/custom_js/${this.nameSaveScript}`, btn);
		btn.setAttribute("image", this.image);
	},
	get image() {
		var img = `${this.id.toLowerCase()}-img`;
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(img, Services.io.newURI("data:image/svg+xml;base64,77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjYgMjYiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiI+DQogIDxwYXRoIGQ9Ik0xOCA5TDE1IDlMMTUgMkwxOCAyIFogTSAyNiA2TDI2IDIzQzI2IDI0LjY1NjI1IDI0LjY1NjI1IDI2IDIzIDI2TDMgMjZDMS4zNDM3NSAyNiAwIDI0LjY1NjI1IDAgMjNMMCAzQzAgMS4zNDM3NSAxLjM0Mzc1IDAgMyAwTDIwIDBDMjAuODI4MTI1IDAgMjEuMjg1MTU2IDAuMDQyOTY4OCAyMy42MjEwOTQgMi4zNzg5MDZDMjUuOTU3MDMxIDQuNzE0ODQ0IDI2IDUuMTcxODc1IDI2IDYgWiBNIDUgOUM1IDkuNTUwNzgxIDUuNDQ5MjE5IDEwIDYgMTBMMTkgMTBDMTkuNTUwNzgxIDEwIDIwIDkuNTUwNzgxIDIwIDlMMjAgMkMyMCAxLjQ0OTIxOSAxOS41NTA3ODEgMSAxOSAxTDYgMUM1LjQ0OTIxOSAxIDUgMS40NDkyMTkgNSAyIFogTSAyMyAxNEMyMyAxMy40NDkyMTkgMjIuNTUwNzgxIDEzIDIyIDEzTDQgMTNDMy40NDkyMTkgMTMgMyAxMy40NDkyMTkgMyAxNEwzIDI0QzMgMjQuNTUwNzgxIDMuNDQ5MjE5IDI1IDQgMjVMMjIgMjVDMjIuNTUwNzgxIDI1IDIzIDI0LjU1MDc4MSAyMyAyNFoiIGZpbGw9IiM4RThFOTgiIC8+DQo8L3N2Zz4="));
			delete this.image;
			return this.image = `resource://${img}`;
	},
	handleEvent(e) {
		var btn = e.target.getElementById(this.id);
		for(var args of btn._handlers)
			args.pop().removeEventListener(...args);
		delete btn._handlers;
		for(var {destructor, context} of btn._destructors)
			try {destructor.call(context, "destructor");}
			catch(ex) {Cu.reportError(ex);}
		delete btn._destructors;
	}
}))();

Save_Script

Выделить код

Код:

// Скрипт Save
// Правка Vitaliy V. 2505
// Предыдущая версия
// https://forum.mozilla-russia.org/viewtopic.php?pid=781458#p781458
// https://forum.mozilla-russia.org/viewtopic.php?pid=799602#p799602
// https://forum.mozilla-russia.org/viewtopic.php?pid=799639#p799639
// https://forum.mozilla-russia.org/viewtopic.php?pid=805016#p805016

// Кнопка-виджет в Save_Button.js

self.label = "Save";
self.type = "menu";

// Создать меню для кнопки ...
var array = [
    { label: "Сохранить favicon сайта", func: () => saveFavicon(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAADUElEQVRYCe2WS0gVYRiGZ8pQ8oJZYXbBWkZEBFEUlQlBEi0zwWWLtoEg1ULRRSCRXQgM3VSbCIJAcNMiRGgVRVZSq1DpImam0U1Nz/S80//JOJ6bYUTg4X2+9/u//zL/XM6Z43lLn398BfyFHD8IgjzGV0IVyDfh0hvCA7gP3b7vT+KLJw5cAq3wBTJJYy4waNWi7ICFauETmH66ZAK/61BOGlif8lFCzR9vgsk+NEMCpP4gCK6C9IOwxxYn3we2iSvkgyBpbiOJb2OzdiY1gamNJBdegnQhvhDFVpD6CHnQAaaG+Pi0bWbVQAKk0xpMshekKUKpalGorQO7BbvVR7sOJK11XLWMMLoExkBqtQk0LoLUabW409kFUov10dAtwQI9R6usbp5jScTPkRfDMDxmZjUuVSnAeKRGc47GXOsoY564/BE+AmvhLJyBWc15OJikDY3SWwR/Q+MsuobfiRk8lA4YJi5U4jr4NP4cApBWEraCJvbi6bSTzmXwCr6D5BN2QDEchG6YL65AC0i3o70UjoD0OlpPljNoAKTD0X4Kd0A6H61rp9F2uWv0OTfTVVE+ppAB3UIN0dnKjRcusWOEzfgGysKq5w05N8t3yTfn6eyr6yxwbmZrbrCCPL4B3SvVA4UIqeqRIbNpqrHxNcMJ8Q28Dauet965Waqzsv6oF7qGzXFNb6NL3jkPLb4BvVbVsU0hwmeXr3aezmzMeGyQrTkYrfvRBo+oviI91BIwCHbZcsl171Qf8NJ/ttDtg850EpfULifRCR/gd+AheSh1hIkCG8jBR8GeetJFla5K6h8idjbNJto5ZD0MwUmYBukSYTtch3uQTNUUT0Ev1IO0gnADSqGdY8zgqcUGCmAIpJue+9C4CFKnK80zOpO9jG5Rl94T8udNSlZgYLLX8R7qUqrXcRmd9jrepXVp14GU/etYEwWzmsDURpILz0C6rDFRKF4D6SkhDzrA1BAdm1XOTB+aQbvHgv4gCC6BNEXYbwuRVwTB7P9B/TMapC0lCI0w52G3eVk5k2vhI5jsMk9QSPendIT+E1kdJNMgFioC3ZIPeCYNM0BnXZhpXfUv6NKw8HImHfI87xhUwGbv96cf64Eu6PF9fwZf0v9xBX4BW7VJooPQ4WAAAAAASUVORK5CYII="},
    { label: "Копировать favicon сайта в base64", func: () => copyFaviconData(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAC6UlEQVRYCe2VS2hTQRSGJzFFMFRtEUNBaRptpVUEFdyoCxcuRESkBVcGEYRCEQUXQXBpoQjik25EkSxEfCDiSkEUbNGCC1204KOoIBqQplRplRpz/f5wpwwxbZ6ii5b/m/OYM2cmNzdTY+b//vETCFS7v+d5IXrsgD3QBmFIwQDcCAQCH7B/R2zeBW9gNmWYSEKk5ieg6UnI1wiJXsjXexIdNTsEzQ6D1YDvZLEbtQn2Jkh6Oik58M7zvGWarwqaROEHSKcZekB6ahsTbAcpzRADe4grtsbaoHXKsMepXQhTkIaDIP1io4Qg2AIeNEAPDIEUZz4mpyJYHIIxqEYJd/OQG5TgR40xjZCBOyB1MgThAUyA1WacZhiBYVgNG2ATVCY+9laQPtoOBHr0GK/V5mRJXAap14/3K4Anii0h65RoJ/26MI3sJ8mS0xNYT24xvtVy34mQV60uKaW+a6gIGjVABqpRv7u5Tu7Gc/pcq+MUPAPpJ4NiWVyjT6bY4ikJ3+ArWOldsX75lo++Cs6Dftsd2LMgJW03gpWQ9WnCXgXpLUPxr50iNbiIHYdpmIALUO9ssoS4CzpBSjOENY89BtJLhjMg6UA7NV8QKvSyJLDDMJt0AyaZXGub4AdhN9wH/R9own4GKaPB54RdU9BS9BpKlRrfonib24x4DdwGV1MEh9y6gj5Fo1CJXrHoCCyyjfFPwRhcghabn9NSWOkBWJqTvh79nzBE+loWuBuSa4HrcBfWuXM5n2S1B6BFTjPNieogDs/B1WBuU4biPwmKylDuLmCnqDGmGw4YYyKQr082EbAOi0bxY1Cp0ix8bIwJwi6og3xNk3gI+7jUdEHh+tIBwNUggS6ZIWy1ytLgGjT72/1pmNQ7oJ/MOfw2t4K4FfrgC5Sreyxod/sV9Ck6CisKTvpJ5uuhG15AMele2esvrb1h93boA13BmBnpKcWJ9C7UfuP8jmzUCP2Qgkee5y3Nr5mP/+sn8Bs4IBR11QNlKgAAAABJRU5ErkJggg=="},
    { separator: ''},
    { label: "Сохранить ярлык страницы как …", func: () => saveShortcuts(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAACsklEQVRYCe2Xy29MYRjGe6Z0U6UXl2RiTCoV0UU3EguxlBaR7qjoiKWyROzUyr9g0YuNS9jwD4iSSCxQS0NqEjuShhKNykzm83uGd/rN6BzmnDMrTp7f977f7XnPbU4ybW3/+hG04gY45zbiOwy9UILHQRC8JbZWFA7gEnwBX2U692Bry84A83a4CWEqMLk58ZPAVMVvE03vSc7AIByGR2C6k+gJ4FpfPM9Y2i9CX49mzv08yoSMPx8rx+w6mH4rbuYsOASmkzYeK+J22q0eDYurCMuGwDShsVjgpNv6higt0mwPM2T+HJiOhK39qzmcdoPpsjbR6YQO5T6MZZ1zOkmCW6Lp9Ocj5ZgcAOk5TTecgBV4BevMlDzrnCuA6bzNxYq4DcA89MIYFEFapumSOTHraotP0U/m6ysj6INjYMW/k4/+Kt5P7l/5DP2U5ppGG2EI9kLGDMhPQQmkbzTDoMcxSVwC0wxJ5OI5Ni+A6QWJbnuOWAJJxUdINK53grSqa2TNF9cm0GZCVc/IeiAHJZBUXB+ZPjrzYFogydndaiqyMQWzYPpEotvaTRyHEkiV4jKnE8AuOAh7NBYJNtcXLzDWLzPiKOhFI7gijV5AXXmg+dhgqKuYJppUPCtjBrpgGaQizXHQM39J3Kk1scHoApiqxWXM4HrIwwqMgd4Fe+H2a00sMNwAn0FapKlcuW/KWAdUPqPEK271GPDXRcrxOgqms2EmLMrAR5DyYWv/NJfyFuzw8ideXpNSMc3AA+gB6aqa2GA8DqaRtQyZTMNrMM2utS7SGI5Z51wZpIc0NT8t+vXFbzHWHqlYo00Y3gXTnHNOX7hB4gR8AFPyxXVSuG+BAoTpBpPJXrmKG5hvg/tQL/1ELzJY82hsX9TY0IxC+g7sw3gTvIOn/L36SvyvRO/AD94bS0nNXI/tAAAAAElFTkSuQmCC"},
    { separator: ''},
    { label: "Сохранить страницу как PDF", func: () => savePageToPDF(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAACt0lEQVRYCb2XS2sTURTHz8T0aQ21tVHRLtxYqI+F4EY3UVCLi8YHSKG40KWK+Akq6kcQv4Da+liIpbrwRWtF0ZUENyq468JSC8VqtaYl/g5k0pvJTHJ7Mxr+v3vOvXPuOSczk0kiEvIqFAptcA0+w0+w0XeCHsK2kJT2SyTogBy46hsb99pW9IKBbL7N2iCoXjE8gjmopg4ODkELqOYZsp7njWPtRfFNsASqOwwVDUZlI1bjMSX9wuuLivfXE75TtLuwa0B1g3dQUMeS6UBcM/MHNHEEG6lgA61G5Izh27iLIUEtrI3SxGFsqIINhAbVudjEfm0iIyGv/9GAlvUvx26dmMTZwLKZOMRfz5reqA3YkpIlr37nHikyItIIpjYy2QqqXgZ9RrzBVoqbJQu+eioj3FZIeB58Zc0sTpeATPq0fImdhA1mwgj/ecS6ODVAsuPQDZ3gPzVxVy/XBhYo1QrTkAJnuTYwTsU07IMv4CynBnhEf6XiGDTBa3CWUwPFai+K1uYmLIZWmnoa6CfdPNzkk9CMdZJTAxTcTLWMiJyCdTDCmtNDzWkTBS/AR3gCR+EdjNHEb2wvqCZE5CL3yyI2Uqs+AxRJke0c5OAWTMIS7IBjkIApOAOHoKo0uGqAeZDi+tPrOmvt0A9/4DSkYTtchjzshAkR0TODsRQFQr8LWE/CJZgF1RBDyjKtENsDvsq+C5K1krCrkZjHsAf0nT/jul7Fj0U1G6DKQTgAH0BP+VlsbLJp4C3VRsGDE7z7KWxsStbKRME5Yk7CP1EikHXBmHcZfr1u2kjww/DLXW64LnD6Y1KeaWVGPg/ugyrP0LlyVKTsEnC6Zwi4S8AgDMAW5k+xeXBRA5v6YD+ohqkxq04kFNSfWzls3HpPwvbIwuYBAtvA/3u+jO8q3fuJzVdgrVnD9/8CvFxMGTTWer0AAAAASUVORK5CYII="},
    { label: "Печать страницы / печать в PDF", func: () => document.querySelector("#mainCommandSet > command#cmd_print").doCommand(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAC6ElEQVRYCd2WzW8NURjGZ6hqKFckNIidhUY/fCysmm7KxkaIhAVNETtJcxf+AURISxMbEZvGBgtBokU3NrpxW410JxVarCiVNKQtx+/Re2TmzseZe29Xmud3zpz3fd53zkzn3ju+52X/M8Ycwn0V3kMBRkDzG9/3Dcdlyy+ngg2M498BpfpOYBS0GbupiSybyrwBTr6LE+gkTN4kwyZYAUn6RkKbsRTY0FtiIZWzgV4q87AAW0AnaGbeA7tBs9YrOU7SVxLBu/Qk0wa4+uUUToGueoArOcBxRPh0R5pI2A1pbmVdB3EqxAUjMRrvA6ujEUNKgKIaaIEuuA7DMA+SnqmU6mIKZz9IMwyriuGKJuo3wAJIF5xNcNXDLEi3nAUOA03OgJUe7PQKnMfBqj3d7c7SaBCkCbcbB84BkKYYlhGqWNTn4CdIfc5GuBrAPjCXnQUOA72Cd7PNYfc8CvJg1egscBho9BikdwzurwBMIyBl+7ymbIAm62EOpN4U62IKVxNYdS9GKx9pdBKs9jo74TwPkj6zm50FDgONBkGaZMh0+x9ilJ46ejvTNGkA+zBfCRWQaIW1oSALYo1wCbaxrEr06AarllAzondBn817zB3gvj2hDu4FPV+BNBpxEx2GoF6yOA2rI+YKAvRpBqt8pAWZIYjTD4J/70qkqIwAPS6C9ItB7xHhaoI3wKUXGE5A0u96uGlxhb8WPoE0VAyHJzJtkFVfMF6D7eEu8St8x8DqSLyLKA49iEyZ9RvnKUoTRd6HMZA+MuhtKeK3v25dZB5AVvkYN0KaDpPU6xiT18Nr3LwOUmGX++EZZFF/UjOK14Gumsnom68uyRsbp2on3IY5SNI0ifrSBorBc7A6WOrJvKbDVuiBGYjTfYI5NWTW/7yDeRysbipXNXTLwTn4AKWaJTBdhOmf7nBUU/XJgw1oWAudxpjXkKTPJM6CH6xd0mM1h3ZjTB+MgU76iLnTGLNmSU/23zf7A6yAD944M8WnAAAAAElFTkSuQmCC"},
    { label: "Сохранить страницу / выбор как HTML", func: () => savePageToHTML(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAADCUlEQVRYCb2XTUhUURTH37zU8mORNtpEJTQIQlCrgqiFrkoImk2EYS2jhW3ah5TRqpULoVZtkrJFZbmoGBDsaxXZrhEiamdWZJZW6ky/I+8OZ968N+/Om0j+v3vOvfe8c8+7780dx3EC/gqFQgtchln4CTb6TtB92BWQ0n6IBG3wBuLqMxfut10x4Q/k4puMDYDoKc0kfINKamNyCBpBtEiTSSQSU1h7sXgKVkF0i6aswLBsxEo8pqhlvL6weDPuGseze7AbQDTKHRTEsWTOF7eJ/l2KOIINlb+AJhU5r3wb93dAUCNjExRxGBsofwGBQTUObuR6KaLXCfj7HwXIsuZx7JWO5l8WsKYTB/itjMmLWo8tqq7o1e6Mk6LXcZwG0NpKZweIdtPIGfECWy5elgwYdZdHxBsh4SAYZXSWWI+ATC5MwjikdcIQPxsy7rhhE5XGOR/yzD+EE/CMIpLYWIpVgLfSrGe3YQchlmopIKVWbGUX5LFUvRO1FNCvCsjhyxH+ikLS+NZyrSMJJPkQpEG+LY8xZHQQ5wp0wghYq9pz4CuZn0MHiOZo5HN+CmtUzRdY1Z+CHlZJgdm5e/hToPVId6J8kygqzmHbzxJ0HIwWcWS7P2K1lnUnynejAtT8sPLz+KfhAxyFP2B0kWK3mE6UdaMCZJ6E8q4kxfe4xmE0gb8PkpCF9yDqpLkKVrIqgMVWyfYOjKY9R17Ic/gXYAGMBii6wXQqWbfSpG/uuurvFJ/C8jCKPwODYCRnglVuqyAv6wg2C6Iz3KH8kyG+QxEFnC4wesDYL9OxtiSt+HXMfBOMgegxTRc0w0mYB9EMjTkn1tem3w1GmfVBr6lmB+ROl7gzOQV7uH4BXsIPuAFv4TwcIOYT1kp1vqgl1W/Hz0GZWGCaQQFjpQ4VJQWrrnLZo3aI9cNEpSlxyZeAOyBaoSk5I0p2gDuT53ibDLLN/djtXPAEuwJxVM9FfXAIRGOs8UWcUFiw1h+npAjUa0Y3hy6sJwhsAfPzfA0/ruTaHBdfgma9hvH/Ag7EeepKNPfrAAAAAElFTkSuQmCC"},
    { label: "Сохранить URL, страницу / выбор как TXT", func: () => saveSelectionToTxt(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAACKElEQVRYCe2XP0scQRjGd2WjpRAQi4ipvOvSCpIujX8gEkIasVA5CxG0igGLhFj4MfQ7CPkGp5Upki5nYQKiiCAnCpp4cP6eeIOb2ZlZMeddocfzm3nned+dnZudO70osl71ej2GWfgKF/C/0hzbTFKybuUeUvgZ7kuf7LvGaYO7PmP8M4qiBD7CITRDvUyyAjXoj+P4gD4rFjACRsVsxd0cJiyC0XB6lo70gLgT7ltd6RvYC0jnWhK3fQE6bMF3yoN7ScE0SIscoDM8Pcd3MnJYp74cqklCyUauQD8D0geaM3gBxiP0apNMGbxKvJmbxAlhBaSaGjgG4xF6pWu9yUyCrR0Ho2Km4I4GE6Y/huPpaTrSg3bEuY+A1b9iYQsgTXKoTvHeMJiKwq85avfDJddfuXk1zyl4DVIXzSkMgPEInVpyupaZuwPU6+/BFr10qQb2wHiETp073ZDJ1j4ewswGsStjmMsgjXKwTvAmGMxDnlap/xIqSkLJRq6XfgikJ2qgD4xH6JWu9SaVuM0CflG4AdJvNbADxiP0Std6k5kEW/t4CF278hZzFaRBDlWVnSoxeA8hDVO7GypQLlGTQzf5Akim/ikD4xE61el0LdNMaNn/DCuM1kC6UAPfwXiETlWdbshkax/eIWz7/wP2Av6EHlGTcubL7O909iHU4aqRka8fqEfEzVBPYxLN/a0RuzsOYkt/nGZWwQJi0Lv/Qd8saa5S5mYYV1bQVfz1oDyqAAAAAElFTkSuQmCC"},
    { separator: ''},
    { label: "Копировать изображение / текст в base64", func: () => copyFileToBase(), image: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAAEEfUpiAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAM/SURBVEhL7ZdfZNZRGMfPuzFGIyJGdBExRaTxXqQRXY0ouxgjIiKi7hsxIrqLXUVEKV1EjGU0pYgxIpaIXYwRJbqIkbfP95zn+fn9eX/vu3d7I+nL933Ov+d5zu+c5zznvOGPoNVqvbNiRKPUMGUygc5JKybVsrowoJ9cxyv9UB+DTZUb1qABD+FMo9FoUl+m/B0Ow3ow8LAVu4CRs+amggE6DiA35Ts1lSBNpzX5pBNKHXfgCJyBcZG0lPuQz+GgfaIUNqBcv4ftYQPTStbgnsndIW6FgMsniIPwM9RWjDPny8juQPmtz7kTNKY8zr9xEG7ReRKOwgc2+HjqTk4kyxvmBrbgEJ1v4Cbli7E1hFP6QXkBEbfB6jKe9kk/Ag3al0dwKDaEcAaFHypIIbZUIWe9Iz+DfwD5RdT5fQF9ERdZxJtWroVnDZ0YRZ+Unxr3w+3BVrVrJLaDUsJeK9832RaMmzBHhUykA38aeStVC8gCSZCypEejQ2twIhVj1lDyvRprISyZlPKoFedMZpCBj6kY5rG+AVcor6WmDApxeV/A2KQYW4EMaPWFKyYF9yjvOqna4sexIYRZY4Tn9tcIbaGS/wjUMf6Ax0v0KXUdhatQht24cvG5GAcUdGw1YAJKeUHKSEExIqg9mxnIl7cPZlSImTiD3WAnBrQm/UnpfwWydNAOLLaHhO/YF/iJHdbd2BdUJoBThY3O5rHY0B6rTCIf+P0Bzm97mEFdluetqwDaOz9+ekA+I+t4+hnXdTXFV/5K1d6BPUWqH8IlbM1buQBPBWOIfD7VVaAcom04Ar/BdbiMIZ34jsCenPmrYAUdz5AV+AQuIHrZU6WaSmoVsKX4UY4XFLCyXQtPBHtMCj9RapZJ+1n1xRFsFY4qLy/ariHc+VfoT5xa+ATiw8kwjKHKCWAS8QimWoS2LQM6M4jpVAu6yabRiTFEnwe2WHjReDbXW9fvC+EuA8etHEFdX5yfmB5sEWbU91lO9Zchu047oZAHMKTr5AZUAqqDVuI6DvQQjkDvGaLX6yXeZ5VE5MCoLshDUIaV+dZR0DO2Z2Ar/+SZw062erW3kZzBNfgSKpp35LwbdnId/kcfEcJvJ7yZGHuzI6wAAAAASUVORK5CYII="},
    { separator: ''},
    { label: "(Меню ПКМ) Сохр./добавить текст в файл", value: "Save.SelectionToFile" },
    { label: "(Меню ПКМ) Открыть текст в редакторе", value: "Save.TextToEditor"},
];

var menuPopup = self.appendChild(document.createXULElement("menupopup"));
array.forEach((m,i)=> {
    if ("separator" in m) { menuPopup.appendChild(document.createXULElement("menuseparator")); return };
    var mItem = menuPopup.appendChild(document.createXULElement("menuitem"));
    mItem.setAttribute("label", m.label);
    mItem.setAttribute("class", "menuitem-iconic");
    if ("image" in m) mItem.setAttribute("image", m.image || array[i-1].image);
    if ("value" in m) {
        mItem.setAttribute('type', 'checkbox');
        mItem.setAttribute('checked', cbu.getPrefs(m.value) );
        mItem.onclick =()=> cbu.setPrefs(m.value, !cbu.getPrefs(m.value));
        }
    if ("func" in m) mItem.addEventListener("command", m.func);
});
menuPopup.onclick = e => e.stopPropagation();


function aDate() {
    var t=new Date();
    var y=1900+t.getYear();
    var min=t.getMinutes(); if (min<10){min="0"+min};
    var h=t.getHours();
    var m=t.getMonth();switch(m){case 0: m="января";break;case 1: m="февраля";break;case 2: m="марта";break;case 3: m="апреля";break;case 4: m="мая";break;case 5: m="июня";break;case 6: m="июля";break;case 7: m="августа";break;case 8: m="сентября";break;case 9: m="октября";break;case 10: m="ноября";break;default: m="декабря";}
    var d=t.getDate();
    var curdate=d+" "+m+" "+y+" "+"г";
    var myfilename=curdate;
    return myfilename;
}


// Копировать favicon сайта в base64
function FaviconToBase(image) {
    var canvas = document.createElementNS(xhtmlns, 'canvas');
    canvas.width = image.naturalWidth;
    canvas.height = image.naturalHeight;
    var ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    var base64 = canvas.toDataURL();
    gClipboard.write(base64);

    var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
    var alertName = "FaviconToBase";

    as.showAlertNotification(
        base64,
        "Скрипт Save - FaviconToBase",
        "Значок скопирован как base64",
        false,
        "",
        null,
        alertName
    );

    setTimeout(as.closeAlert, 5e3, alertName);
};

// Сохранить страницу / выбор как HTML
function savePageToHTML() {
var vert = String.raw`javascript:(function(){var getSelWin=function(w){if(w.getSelection().toString())return w;for(var i=0,f,r;f=w.frames[i];i++){try{if(r=getSelWin(f))return r}catch(e){}}};var selWin=getSelWin(window),win=selWin||window,doc=win.document,loc=win.location;var qualifyURL=function(url,base){if(!url||/^([a-z]+:|%23)/.test(url))return url;var a=doc.createElement('a');if(base){a.href=base;a.href=a.protocol+(url.charAt(0)=='/'%3F(url.charAt(1)=='/'%3F'':'//'+a.host):'//'+a.host+a.pathname.slice(0,(url.charAt(0)!='%3F'&&a.pathname.lastIndexOf('/')+1)||a.pathname.length))+url}else{a.href=url};return a.href};var encodeImg=function(src,obj){var canvas,img,ret=src;if(/^https%3F:%5C/%5C//.test(src)){canvas=doc.createElement('canvas');if(!obj||obj.nodeName.toLowerCase()!='img'){img=doc.createElement('img');img.src=src}else{img=obj};if(img.complete)try{canvas.width=img.width;canvas.height=img.height;canvas.getContext('2d').drawImage(img,0,0);ret=canvas.toDataURL((/%5C.jpe%3Fg/i.test(src)%3F'image/jpeg':'image/png'))}catch(e){};if(img!=obj)img.src='about:blank'};return ret};var toSrc=function(obj){var strToSrc=function(str){var chr,ret='',i=0,meta={'%5Cb':'%5C%5Cb','%5Ct':'%5C%5Ct','%5Cn':'%5C%5Cn','%5Cf':'%5C%5Cf','%5Cr':'%5C%5Cr','%5Cx22':'%5C%5C%5Cx22','%5C%5C':'%5C%5C%5C%5C'};while(chr=str.charAt(i++)){ret+=meta[chr]||chr};return'%5Cx22'+ret+'%5Cx22'},arrToSrc=function(arr){var ret=[];for(var i=0;i<arr.length;i++){ret[i]=toSrc(arr[i])||'null'};return'['+ret.join(',')+']'},objToSrc=function(obj){var val,ret=[];for(var prop in obj){if(Object.prototype.hasOwnProperty.call(obj,prop)&&(val=toSrc(obj[prop])))ret.push(strToSrc(prop)+': '+val)};return'{'+ret.join(',')+'}'};switch(Object.prototype.toString.call(obj).slice(8,-1)){case'Array':return arrToSrc(obj);case'Boolean':case'Function':case'RegExp':return obj.toString();case'Date':return'new Date('+obj.getTime()+')';case'Math':return'Math';case'Number':return isFinite(obj)%3FString(obj):'null';case'Object':return objToSrc(obj);case'String':return strToSrc(obj);default:return obj%3F(obj.nodeType==1&&obj.id%3F'document.getElementById('+strToSrc(obj.id)+')':'{}'):'null'}};var ele,pEle,clone,reUrl=/(url%5C(%5Cx22%3F)(.+%3F)(%5Cx22%3F%5C))/g;if(selWin){var rng=win.getSelection().getRangeAt(0);pEle=rng.commonAncestorContainer;ele=rng.cloneContents()}else{pEle=doc.documentElement;ele=(doc.body||doc.getElementsByTagName('body')[0]).cloneNode(true)};while(pEle){if(pEle.nodeType==1){clone=pEle.cloneNode(false);clone.appendChild(ele);ele=clone};pEle=pEle.parentNode};var sel=doc.createElement('div');sel.appendChild(ele);for(var el,all=sel.getElementsByTagName('*'),i=all.length;i--;){el=all[i];if(el.style&&el.style.backgroundImage)el.style.backgroundImage=el.style.backgroundImage.replace(reUrl,function(a,b,c,d){return b+encodeImg(qualifyURL(c))+d});switch(el.nodeName.toLowerCase()){case'link':case'style':case'script':el.parentNode.removeChild(el);break;case'a':case'area':if(el.hasAttribute('href')&&el.getAttribute('href').charAt(0)!='%23')el.href=el.href;break;case'img':case'input':if(el.hasAttribute('src'))el.src=encodeImg(el.src,el);break;case'audio':case'video':case'embed':case'frame':case'iframe':if(el.hasAttribute('src'))el.src=el.src;break;case'object':if(el.hasAttribute('data'))el.data=el.data;break;case'form':if(el.hasAttribute('action'))el.action=el.action;break}};var head=ele.insertBefore(doc.createElement('head'),ele.firstChild);var meta=doc.createElement('meta');meta.httpEquiv='content-type';meta.content='text/html; charset=utf-8';head.appendChild(meta);var title=doc.getElementsByTagName('title')[0];if(title)head.appendChild(title.cloneNode(true));head.copyScript=function(){if('$'in win)return;var f=doc.createElement('iframe');f.src='about:blank';f.style.cssText ='position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;';doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in win){if(name in f.contentWindow||!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name))continue;try{str=toSrc(win[name]);if(!/%5C{%5Cs*%5C[native code%5C]%5Cs*%5C}/.test(str)){script.appendChild(doc.createTextNode('var '+name+' = '+str.replace(/<%5C/(script>)/ig,'<%5C%5C/$1')+';%5Cn'))}}catch(e){}};f.parentNode.removeChild(f);if(script.childNodes.length)this.nextSibling.appendChild(script)};head.copyScript();head.copyStyle=function(s){if(!s)return;var style=doc.createElement('style');style.type='text/css';if(s.media&&s.media.mediaText)style.media=s.media.mediaText;try{for(var i=0,rule;rule=s.cssRules[i];i++){if(rule.type!=3){if((!rule.selectorText||rule.selectorText.indexOf(':')!=-1)||(!sel.querySelector||sel.querySelector(rule.selectorText))){style.appendChild(doc.createTextNode(rule.cssText.replace(reUrl,function(a,b,c,d){var url=qualifyURL(c,s.href);if(rule.type==1&&rule.style&&rule.style.backgroundImage)url=encodeImg(url);return b+url+d})+'%5Cn'))}}else{this.copyStyle(rule.styleSheet)}}}catch(e){if(s.ownerNode)style=s.ownerNode.cloneNode(false)};this.appendChild(style)};var sheets=doc.styleSheets;for(var j=0;j<sheets.length;j++)head.copyStyle(sheets[j]);head.appendChild(doc.createTextNode('%5Cn'));var doctype='',dt=doc.doctype;if(dt&&dt.name){doctype+='<!DOCTYPE '+dt.name;if(dt.publicId)doctype+=' PUBLIC %5Cx22'+dt.publicId+'%5Cx22';if(dt.systemId)doctype+=' %5Cx22'+dt.systemId+'%5Cx22';doctype+='>%5Cn'};var href = 'data:text/html;charset=utf-8,' + encodeURIComponent(doctype + sel.innerHTML + '\n<!-- This document saved from ' + (loc.protocol != 'data:' ? loc.href : 'data:uri') + ' -->');var a = document.documentElement.appendChild(document.createElement("a"));a.setAttribute("href", href);var name = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());name=name.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, '');name += (function () {var d = new Date(), z=function(n){return '_' + (n < 10 ? '0' : '') + n};return z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds());})();a.setAttribute("download", name + ".html");a.click();a.remove();})();`;
gBrowser.fixupAndLoadURIString(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};

// Получить имя файла
async function pick(fileName) {
    try {
        var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
        await IOUtils.makeDirectory(file.path);
    } catch {
        file = Services.dirsvc.get("Desk", Ci.nsIFile);
    }
    var fp = makeFilePicker();
    fp.init(window.browsingContext, "", fp.modeSave);
    fp.displayDirectory = file;
    fp.defaultString = fileName;
    return await new Promise(fp.open) != fp.returnCancel && fp.file;
}

// Сохранить favicon сайта
function saveFavicon() {
    var dn = "favicon";
    var re = /^data:(image\/[^;,]+)/i;
    var ms = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
    (saveFavicon = async () => {
        var url = gBrowser.selectedTab.image;
        if (!url) return;
        if (re.test(url)) {
            try {var name = gBrowser.currentURI.host || dn;} catch {name = dn;}
            name += "." + ms.getPrimaryExtension(RegExp.$1, "ico");
        } else
            var name = Services.io.newURI(url).QueryInterface(Ci.nsIURL).fileName;

        var file = await pick(name);
        file && IOUtils.write(file.path, new Uint8Array(await (await fetch(url)).arrayBuffer()));
    })();
}

// Сохранить ярлык страницы как …
function saveShortcuts() {
    var img = self.getAttribute("image");
    var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
    (saveShortcuts = async () => {
        var file = await pick(getTabLabel() + ".url");
        if (file)
            await IOUtils.writeUTF8(file.path, `[InternetShortcut]\r\nURL=${gBrowser.currentURI.spec}\r\n`),
            as.showAlertNotification(
                gBrowser.selectedTab.image || img, file.leafName, "Сохранил в: " + file.parent.path
            );
    })();
}

// Копировать изображение или текстовой файл в base64 ...
function copyFileToBase(){
var fp = window.makeFilePicker();
fp.init(window.browsingContext, "Открыть файл", fp.modeOpen);
fp.appendFilter(
"Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml;\
                    *.xhtml; *.json; *.jpe; *.jpg; *.jpeg; *.gif; *.png; *.bmp; *.ico; *.svg;\
                    *.svgz; *.tif; *.tiff; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw; *.webp");
    fp.open(re=> {
    if ( re != fp.returnOK ) return;
    var file = fp.file;
    var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
    inputStream.init(file, 0x01, 0o600, 0);
    var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
    stream.setInputStream(inputStream);
    var encoded = btoa(stream.readBytes(stream.available()));
    var contentType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService).getTypeFromFile(file);
    var dataURI = "data:" + contentType + ";charset=utf-8;base64," + encoded;
    gClipboard.write(dataURI);

    var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
    var alertName = "FileToBase";

    as.showAlertNotification(
        dataURI,
        "Скрипт Save - FileToBase",
        "Файл скопирован как base64",
        false,
        "",
        null,
        alertName
    );

    setTimeout(as.closeAlert, 5e3, alertName);
    });
};

// Сохранить страницу как PDF, скриптом ...
function savePageToPDF() {
    var ps = Ci.nsIPrintSettings, cfg = {

        paperWidth: 8.5,
        paperHeight: 11,
        paperSizeUnit: ps.kPaperSizeInches, // kPaperSizeMillimeters

        marginLeft: .2,
        marginRight: .2,
        marginTop: .2,
        marginBottom: .2,

        edgeLeft: .1,
        edgeRight: .1,
        edgeTop: 0,
        edgeBottom: 0,

        headerStrLeft: "&T",
        headerStrCenter: "",
        headerStrRight: "&U",

        footerStrLeft: "&PT",
        footerStrCenter: "",
        footerStrRight: "&D",

        printBGColors: true,
        printBGImages: false,

        scaling: 1,
        shrinkToFit: true, // overrides scaling
        orientation: ps.kPortraitOrientation, // kLandscapeOrientation

        printerName: "",
        printSilent: true,
        printToFile: true,
        showPrintProgress: false,
        isInitializedFromPrefs: false,
        isInitializedFromPrinter: false,
        outputFormat: ps.kOutputFormatPDF,
        outputDestination: ps.kOutputDestinationFile,
    };
    ps = Cc["@mozilla.org/gfx/printsettings-service;1"]
        .getService(Ci.nsIPrintSettingsService).createNewPrintSettings();
    for(var key in cfg) if (key in ps) ps[key] = cfg[key];
    (savePageToPDF = async () => {
        try {
            var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
            await IOUtils.makeDirectory(file.path);
        } catch {
            file = Services.dirsvc.get("Desk", Ci.nsIFile);
        }
        file.append(`Snap ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")}.pdf`);
        ps.toFileName = file.path;
        await gBrowser.selectedBrowser.browsingContext.print(ps);
        //file.launch();
    })();
};


// Копировать favicon сайта в base64 ...
function copyFaviconData() {
    var img = new Image();
    img.src = gBrowser.selectedTab.image;
    FaviconToBase(img);
};


// Сохранить URL, страницу / выбор как TXT ...
function saveSelectionToTxt() {
    // [129+] l11 = length >= 11; https://forum.mozilla-russia.org/profile.php?id=71856
    var {length} = saveURL, splice = length > 9, l11 = length >= 11;
    var msgName = _id + ":Save:GetSelection";
    var receiver = msg => {
        var args = [
            "data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\r\n\r\n" + msg.data),
            getTabLabel() + '   ' + aDate().replace(/:/g, ".") + ".txt",
            null, false, false, null, window.document
        ];
        splice && args.splice(5, 0, null) && l11 && args.splice(1, 0, null);
        saveURL(...args);
    }
    messageManager.addMessageListener(msgName, receiver);
    addDestructor(() => messageManager.removeMessageListener(msgName, receiver));

    var func = fm => {
        var res, fed, win = {};
        var fe = fm.getFocusedElementForWindow(content, true, win);
        var sel = (win = win.value).getSelection();
        if (sel.isCollapsed) {
            var ed = fe && fe.editor;
            if (ed && ed instanceof Ci.nsIEditor)
                sel = ed.selection, fed = fe;
        }
        if (sel.isCollapsed)
            fed && fed.blur(),
            docShell.doCommand("cmd_selectAll"),
            res = win.getSelection().toString(),
            docShell.doCommand("cmd_selectNone"),
            fed && fed.focus();

        res = res || sel.toString();
        /\S/.test(res) && sendAsyncMessage("NAME", res);
    }
    var url = "data:charset=utf-8," + encodeURIComponent(`(${func})`.replace("NAME", msgName))
        + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
    (saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
}


// Добавляем в контекстного меню страницы новые пункты ...
((contextMenu, el)=> {

    // в контекстное меню выделенного текста ...
    var saveItem = contextMenu.insertBefore(document.createXULElement("menuitem"), el);
    saveItem.id = "content-saveItem";
    saveItem.setAttribute("label", "Сохр./добавить текст в файл");
    saveItem.setAttribute("class", "menuitem-iconic");
    saveItem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAADAgBEDRIXnwxQjKQNWp6pDFWXqAxXm6gMV5moDFeaqAxXmqgMV5qoDFebqAxVlqgNW5+pCkyIogwSFqgDAgBHDQoFhyszOv8hheP+IJH7/x+L8v8fjfb/H433/x+N9v8fjfb/H432/x+N9/8fi/L/IJH7/yGF5P0kLTTvDAcDgwgICIQ8Ojf/0czA+Oji1fzh18r85NzO/OTbz/zj287849vO/OPbzvzk3M/84dfK++ji1f3Sy8D5NDIvywYGB3kKCgqFQ0A8/+XXw/v979f/9uTO//rp0f/66NH/+ujR//rn0f/66NH/+ujR//bkzv/979f/5tfD/UZBPv8KCwqEDQwMhUVDQP/f08X7+OrZ/+zf0P/v5NP/8OPT/+/j0//v4tP/8OPT/+/j0//s39D/+OrZ/+DTxfxEQj//DAwMhA8PD4VKR0T/4dXG+/rr2v/v4tH/9OXU//Ll1P/z5dT/8+XU//Pl1P/05NT/7+DR//rr2v/i1cX7SkhE/w8PD4USEhKFT0xI/+XXxfv97tr/9ePR//no1P/459T/+OfU//jn1P/459T/+OfU//Xk0f/97tr/5dfF+09MSf8SEhGFFRQUhVNQTv/j2cv7+u/g//Hm2P/169v/9Orb//Tq2//06tv/9erb//br3P/x5tf/+e/g/+PZzPtTUU7/FBQUhRgXF4VXU1D/2828+/Lk0f/q2sf/7d3K/+3dyv/t3cr/7N3K/+rayP/r28n/69vI//Ll0v/azbv7VlNP/xgXF4UfHh6FTktJ/1JOTPtZVFL/Uk5L/1FNSv9RTUr/UU1K/1JPTP9YVVD/VVJP/09NSv9WUk//UU1L+05LSf8fHh2FIR8fhVVTUP9FQkD7UlBM/6Wlj/+4uJ7/sLCX/7S0mv+xsJn/oKCQ/6+vmv+hoYv/TEtH/0NCQPtVUk//IR8fhSMhIIVcWVb/SEVF+19dVv/f3sP////e//X10v///93/2di8/1lYWP+eno//5+fG/19dV/9JRkb7W1hV/yMhIYUkJCOFXltZ/0tJSPtdW1f/0NC4/+/u1P/h4cj/8PDV/7++q/8vLC7/e3lw/9fWv/9eXVf/TElJ+15bWf8lJCKEJSQjhF9cWf9LSUf5XVtX/tbVwf/5+OL/6enV//j54v/GxrX/QD0+/42Kgv/d3cr/YF5a/k5LSvlhXlv/JSUjhCkoKIZpZWT/VVJR/WNhXP/V1cT//f3s/+3t3v/8/Or/zc2//01LSf+VlIz/4eDS/2hmYv9YVVT8aWVj/ycmJoIaGRlYSEVE1DYzM8NKSUfP0dHG9/X16P/n59v+7e3g/+jo3f/X2M3+6uve/9bWzPdOTUvNOjg3y0RBQLwPDw8lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==");
    saveItem.onclick =()=> saveSelectionToFile();

    var editorItem = contextMenu.insertBefore(document.createXULElement("menuitem"), el);
    editorItem.id = "content-editorItem";
    editorItem.setAttribute("label", "Открыть текст в редакторе");
    editorItem.setAttribute("class", "menuitem-iconic");
    editorItem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcXFz/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9aWlr/Wlpa/1paWv9cXFz/AAAAAAAAAAAAAAAAXFxc//n7+f39/v3+/f79/v7+/v7+/v7+/v7+/f7+/v3+/v78/v7+/P////v////2XV1d/wAAAAAAAAAAAAAAAF9fX//7/fv/FVcX/wBIAv8ASAL/AEgC/wBIAv8ASAL/AEgC/wBIAv8VVxf//f78/2BgYP8AAAAAAAAAAAAAAABhYWH/+/78/zRtNf8otUX/KLVF/yi1Rf8otUX/ZLal/ypBSf9Upcv/NG01//z+/P9kZGT/AAAAAAAAAAAAAAAAZGRk//v+/P9Yh1n/MtB1/zLQdf8y0HX/MtB1/3PZrv9Tpcz/AXfF/2WYiP/8/v3/Z2dn/wAAAAAAAAAAAAAAAGZmZv/8/v3/gKSB/zvdof873aH/O92h/zvdof873aH/Fsj4/wSo6/8Ldr3//f7+/2tra/8AAAAAAAAAAAAAAABpaWn//P/+/6fAqP9C4bn/QuG5/0Lhuf9C4bn/QuG5/1vkxf8GwP7/AXfH/5HD4v9ubm7/AAAAAAAAAAAAAAAAa2tr//z//v/L2sz/lt7S/5be0v+W3tL/lt7S/5be0v+W3tL/Ms34/wSv8P8Abr7/cnJy/wAAAAAAAAAAAAAAAG5ubv/9/v7/6vDq/2Xo3f9l6N3/Zejd/2Xo3f9l6N3/Zejd/2Xo3f8GwP7/AXvJ/1uPsP8AAAAAAAAAAAAAAABwcHD//v////Hu5//l4NT/5eDU/+Xg1P/l4NT/5eDU/+Xg1P/l4NT/XNT5/wS39/8Abr//5PH2GgAAAAAAAAAAc3Nz/////////////v79//7+/f/+/v3//v79//7+/f/+/v3//v79//////8Gwf7/AYPN/xh+wukAAAAAAAAAAHV1df//////7Onh/+Xg1P/l4NT/5eDU/+Xg1P/l4NT/gYGB/4CAgP+AgID/U67L/wS2+P8AbcD/4ezzIAAAAAB4eHj///////39/P///////////////////////Pv6/4CAgP/9/f3//f39/4GBgf4Nwv3+UI+q/4x+d9IAAAAAenp6//////+Wlpf/9/X0/5OTlP/18/L/k5OU//Lv7f+AgID//f39/4GBgf6EhIRd8OXhU8yplP9yWU7/zsjCXH19ff+3t7f/////KLq6uv////8ourq6/////yi6urr/gICA/4GBgf6EhIRdAAAAAAAAAACxqMC5HhmE/wAAdP+AgID/gICA/56entOBgYH/n5+f1oGBgf+fn5/WgYGB/4GBgf+EhIRJAAAAAAAAAAAAAAAA5Of3EAcKj/8AAHP/AAesQQAHrEEAB6xBAAesQQAHrEEAB6xBAAesQQAHrEEAB6xBAAOsQQADrEEAAaxBAAGsQQAArEEAGKxBADisQQ==");
    editorItem.onclick =()=> textToEditor();


// устанавливаем где и при каких настройках показывать новые пункты ....
    addEventListener('popupshowing', e=> {
        if (e.target != e.currentTarget) return;
        var sel = gContextMenu.isTextSelected;
        saveItem.hidden = !sel || !cbu.getPrefs("Save.SelectionToFile");
        editorItem.hidden = !sel || !cbu.getPrefs("Save.TextToEditor");
        }, false, contextMenu);

    // удалять новые пункти при изменениях ....
    addDestructor(()=> {
        saveItem.remove(); editorItem.remove();
    });
})(document.getElementById("contentAreaContextMenu"), document.getElementById("context-sep-open"));


// Сохранить или добавить выделенный текст в файл в папке загрузок, если назначена,
// иначе на Рабочий стол .............
function saveSelectionToFile() {
    var line = ".".repeat(62) + "\n";
    var hint = "Нажмите чтобы открыть файл";
    var prfx = "Выбранный текст сохранен в файл ";

    var img = self.getAttribute("image");
    var desk = Services.dirsvc.get("Desk", Ci.nsIFile);
    var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);

    (saveSelectionToFile = async () => {
        var time = aDate(), url = gBrowser.currentURI.displaySpec;
        var text = `${line}${getTabLabel()} - ${time}\n${url}\n\n${
            gContextMenu.contentData.selectionInfo.fullText
        }\n\n\n`;
        try {
            var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
            var msg = prfx + "в папку " + file.leafName;
            await IOUtils.makeDirectory(file.path);
        } catch(ex) {
            file && Cu.reportError(ex);
            file = desk.clone();
            var msg = prfx + "на рабочий стол";
        }
        file.append(`Save - ${time}.txt`);
        await IOUtils.writeUTF8(file.path, text, {mode: file.exists() ? "append" : "create"});

        var name = "sstf-" + Cu.now();
        as.showAlertNotification(
            gBrowser.selectedTab.image || img, msg, hint, true, "",
            (s, t) => t == "alertclickcallback" && file.launch(), name
        );
        setTimeout(as.closeAlert, 8e3, name);
    })();
};


// Создать текстовой файл с выбранным текстом в папке загрузок, если назначена,
// иначе на Рабочий стол, и открыть в редакторе ...
function textToEditor() {
let browserMM = gBrowser.selectedBrowser.messageManager;
browserMM.addMessageListener('getSelect', function listener(message) {
    // создать текст для записи
    var text = convertFromUnicode("UTF-8", message.data);
    try {var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {file = Services.dirsvc.get("Desk", Ci.nsIFile);}
    file.append("TextToEditor.txt");
    custombuttonsUtils.writeFile(file.path, text);
    file.launch();

    browserMM.removeMessageListener('getSelect', listener, true);
});
    browserMM.loadFrameScript('data:,sendAsyncMessage("getSelect", content.document.getSelection().toString())', false);
};


// Конвертировать текст в юникод ...
function convertFromUnicode(charset, str) {
     var converter = Cc['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
     converter.charset = charset;
     str = converter.ConvertFromUnicode(str);
     return str + converter.Finish();
};


// Получить название вкладки без не сохраняемых символов и лишних пробелов ...
function getTabLabel() {
    var label = gBrowser.selectedTab.label;
    var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
    return label.substring(0, 50);
};
((main, parts) => this.onmousedown = e => {
    if (e.button) return;
    this.onmousedown = null;

    var df = MozXULElement.parseXULToFragment(`
        <menugroup orient="vertical">
            <menuseparator/>
            <menuitem class="menuitem-iconic"
                image="data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAACpElEQVRYCe2XT4hNURzHz8UwwxRGwxTKZPGksCGLWXg2ZmbjlVJqZKMUlpZKycZaycpGZFiIUiQZJcpCYsdi1jSjZIZJM6/n8y3nde6f887p3sbbeH0/5/zO7/zO7/7euefe3jOm4NNqtfrhCnyGnxCjHwQ9hOGClPEuEgzAByirWRYeiL1ikg1k8W18EyC9onkM36GTBpi8BH0gzdE0kiSZoo8XFx+CJZDu0uQK9GUjVvF0bS1gjfnirX+FNf72e+hXgnSdb9CSEcnXTFwv4wcUMUrvVbaAtU7kjGPHmL8LgvrwPaKII/SFWlXodZwsHmI4CdshJ3ZpZ86ZdqxhqCLGiX1pMp9gAcSr+kP0VdTLYt2OOkV8xG4rewvaE7EGO2RzNANrNjKvg9pD31bMDjwj+gWshyLtMMZMwz2oG2NWg6stDLaBtJtG74g39HnxbRpgVctHlPOQ8DxYNdwsdvtc33LYz31J/1UBvuub4Blg3/ayehyka5xiveEOMqib8OcJ8alTn10SLIAF++EqSDdpFkCPpfUx9Eovs44FdP0WpEpnu5frKaiR26orT0Hqi7qD4Bmg7BEWnALpAodqHt8og2MQ0i3iX3cKChbA4hqcAekizTzsA+vD9OotM5UL0K+baRJJTTWgX0jWx9ArrfVO5ibY2v+HsGhXDuM8C9JpDtUcO3WUwUkI6QbxU52CYg7hMAmOg3SORvd1F731YXr1lJnKBcyS5B1IS2rgC1gfplda653MTbC1XT+Ev5yqBh27qrnZSaD3iDN0THZgEEr9MXHSpEzyJXAfpEWaTW5A6hByYmcImCRgAk7AVsb6TbiIXUY9LBqDEZDucI1vMrxwwap/TklRqPd4N3gv7E4Q2A/273kTu6y09hOLL8M69xrW/gOmqWkAQxEbGgAAAABJRU5ErkJggg=="
                label="Сохранить всю страницу как PNG"
                value="all"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAC1klEQVRYCb2XO2gUQRjHd+OeMZpCTxIFAz5ATwVtfKDEIpUGES6oiBDUQiWFvU1QEBEbO7HSRohERYIBKxEU9bQSuULC2YiFYIjiKzHKGdffd2SO2b3Z3dnNmfD/zXwz871u5y7HOY7hz/f9drgIb2EKbPQdp/uw1pDSfosEeShDVn0icIdtRTfsSPAQe/0gesbwAL5CnPIcnoc2EP1gKLqu+5jZXhRfCX9ANMzQ0GBUNnzFn6muaazeKH+136KM2XkL8wIQXeMV+GJYMh7yW8R6hCb2MUcq3MBizXNCs23M3wanNvZGaWIvs1HhBoxOc9xsJV6a6HEMf/PRgJRV17FVFjrNbGBGT2ywl7Enb9Qcc11e3YoxuEMJWhfj8p6zO9DjOM5C0LWCRReINjPI/4gXzI2iUBGUCsqDjQLEqVv5mmYCz4BSUfdp0Rf/0X4UldvqCgj+AH0QpbGog6R9qwb4hzRJolFouqwa4PLkY7QtZfVXNP4rKcaqAZKshueQRhtxrkCs5utNGNmEF3kSPJDP+Z7gVuJKYhKdrBqYvctSYrYMDqmugDdjDk7DFdiUoV5DiNUT0KKGsA/DNzhGE2t4OtPYmWX9BCi2nSpH4CycgE7YAHOSdQNU2QWiGwxdIPpIY61wGWrfB8wH4CYMikMSXpKDdp7D9kF0kqHM4x+n0HXsU3AI+yXzcXgHJUgnEhi/DSULZztBVJIB+mAA/sITEFUZBsGTGAXrAigFvg2VT23GI7IBceD8KYjkx8obMeAeeHAQ1otfGPYjGwh0Gg7U1yTZz3o3jMEkfIZLMMxV+MwjkFrhBn5qGTqwK6D0BeMqnKPgFHMadWrO0ry21ExeZQdk+mGipQmY5HPhLoiqDMt1h8AT4JVN4HAbh344CqtYP2SuQhblCOqFbhDdooZcndhmKJiHMjRbr0m41Fw1tItjO6if5zPYWSWxFYIvwJJQmdryH5Bn0+cd0zmPAAAAAElFTkSuQmCC"
                label="Сохранить видимую часть как PNG"
                value="page"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAACe0lEQVRYCc2Xv2sUQRTHZ2M0RgNq9M5gVLAxENBOIaRJmhiri2ChpLCy8h+wEsTGf8BK0EZJVBAF04hwoMFGSBDSxP9AohBMTJR4rJ8HzuXt7uwys3scHt/vvDdv3q/dmbu9NcbxieN4AN6DX+BP6IMfOL2CZxwp/U0kGISfYVl8I/CCb8Uo7UjwE2yzUPCB4Q1ch0UYZPEO7IeCDYZGFEVNpD8oPgT/QMEcQ6bBvGz4ij+ijW206Tx/a++xyj95DrkHCh5wBbEonvya8tvP/CVNXELmIt3AAeW5pnQf9bfDqR/ba5qYQjqRbsDpVNHYR7w0MWEcn240IGXtdpyXiWYnG2jpxA79CDY5qHuRbfS2terKM1JMGGP2QY3jTE5CwSiD/EZ8RGbBYWlAi5GsRzkLCW9Bi4bO0skt0HnT+ru0wc671YCtl5GVG+C+Xof1TGZPQ+UGqPMILtHEGDIYnWhAnhfDVG7GcXwVGQTvryHJD5H5LDwFT8BjsAZtjj70efxu8gx5jO4FG+zjvIDTOCyCPMge0sQmTbwocrRrIVsgT0obVySliftFDnotpIElHVigb7F2A3ohZAtmyHgaDsE6tGfgNrpcNcJsM1zh9i8iw8HeBf8UE/MLCjYYJl1VsY9Ai4b2CdkCHaf1FpMVeJErbyKD0Bvk7Xa+jPkTxeX2o4ahcgMUfh9WMundk5x2f/bfNbCl7kFN6VXVukqwqfSkyvekBku9mCQz7c7IF8HnULDDcHR31ZjEIeRAreEwj8MsvAaHmb9F7sAykD+g0wSOQ8FTanwXJZcUrPpySgonlrEezi2sF3AcgPb1vIVeFhK7SvBdeFDXsPpfv6YmkcwUIdAAAAAASUVORK5CYII="
                label="Сохранить выбранный элемент как PNG"
                value="click"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAABrklEQVRYCe2Xu0oDQRSGZ71HU2gkKmhjJQjaWdmk0nRpBR/BdxDEZ/ANFC+FKFiJYCHYiqW+gSSCeEViWL8DzmYyOyvLbkg14f/n/HP2zDkns0uyo5TjE4ZhEe7CR/gB0+CVoDM470iZ3kWCEryHWdFg4UraioEdyOJ9fJtQcMNwAV/gfyhxcRsWoOCNoRYEwTU2PSg+A3+g4JAh1mBSNmIlHhPhC1VNitf+Pi3+7BK2Hwr2+AahiJR8suJGmJ/SxDo2EXYDo0Zk3dBp5LcjqIDvnCbWsE7YDTiDcjqHWS9NVJTj04sGpKy+HcsyMdnNBlpmYoeewCcP6iA2wkCk8otjUlSUUkPQxDSTOShYZJDfiFtsHDwsNaixEI/I5iHhFtSomVm6eQvMvLa+sh163qsGdL2Y9Q34HfA74HfA74DfAb8D9g58Gv+XZUPnlVNGgndDd0peWcow08GkM1N7Rr4AnkBBk2GyfVWpjndCDiJ1Ao4IkKPZBnaW+SW2CbNAXkCrLFyFggNqPItIJAXzHk5J4cQd3vHEwuYFAotQH89b6KyQtQ8s3oFjZg2tfwH5w9EYtWehDwAAAABJRU5ErkJggg=="
                label="Сохранить выбранную область как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.handleCommand = e => {
        var name = _id + ":DataURLReady";
        main = main.replace("%MESSAGE_NAME%", name);

        var urls = {}, configurable = true, enumerable = true;
        Object.entries(parts).forEach(([key, part]) => Object.defineProperty(urls, key, {
            configurable, enumerable, get() {
                var value = `data:;charset=utf-8,({${
                    encodeURIComponent(main + part)
                }%0A}).init("${key}")`;
                Object.defineProperty(urls, key, {configurable, enumerable, value});
                return value;
        }}));
        var getTabLabel = () => {
            var label = gBrowser.selectedTab.label;
            var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
            return label.substring(0, 50);
        }
        var listener = msg => {
            var fp = makeFilePicker();
            fp.init(window.browsingContext, "Сохранить как…", fp.modeSave);
            fp.appendFilter("", "*.png");
            fp.defaultString = getTabLabel() + ".png";
            fp.open(res => {
                if (res == fp.returnCancel || !fp.file) return;
                var wbp = makeWebBrowserPersist(), args = [
                    Services.io.newURI(msg.data), document.nodePrincipal,
                    null, null, null, null, fp.file, null
                ];
                //wbp.saveURI.length == 9 && splice(args);
                var {length} = wbp.saveURI;
                length >= 9 && splice(args);
                length == 10 && args.splice(3, 0, null);
                wbp.saveURI(...args);
            });
        }
        var splice = arr => {
            var fox74 = parseInt(Services.appinfo.platformVersion) >= 74;
            var args = [fox74 ? 7 : 2, 0, fox74 ? Ci.nsIContentPolicy.TYPE_IMAGE : null];
            (splice = arr => arr.splice(...args))(arr);
        }
        messageManager.addMessageListener(name, listener);
        addDestructor(() => messageManager.removeMessageListener(name, listener));

        (menugroup.handleCommand = e => gBrowser.selectedBrowser.messageManager
            .loadFrameScript(urls[e.target.value], false)
        )(e);
    }
    menugroup.addEventListener("command", e => menugroup.handleCommand(e));
    menuPopup.querySelector('menuitem[label*="ярлык"]').after(df);
})(`
    init(cmd) {
        cmd.startsWith("c")
            ? this[cmd].init(this[cmd].parent = this)
            : this[cmd]();
    },
    capture(win, x, y, width, height) {
        var canvas = win.document.createElementNS("${xhtmlns}", "canvas");
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        var tryDraw = ind => {
            try {ctx.drawWindow(win, x, y, canvas.width, canvas.height, "white")}
            catch(ex) {canvas.height = ind * canvas.width; tryDraw(--ind);}
        }
        tryDraw(17);
        sendAsyncMessage("%MESSAGE_NAME%", canvas.toDataURL("image/png"));
    },
    `, {

    all: `all() {
        var win = content;
        this.capture(win, 0, 0, win.innerWidth + win.scrollMaxX, win.innerHeight + win.scrollMaxY);
    }`,
    page: `page() {
        var win = content, doc = win.document, body = doc.body, html = doc.documentElement;
        var scrX = (body.scrollLeft || html.scrollLeft) - html.clientLeft;
        var scrY = (body.scrollTop || html.scrollTop) - html.clientTop;
        this.capture(win, scrX, scrY, win.innerWidth, win.innerHeight);
    }`,
    clipping: `clipping: {
        handleEvent(e) {
            if (e.button) return false;
            e.preventDefault();
            e.stopPropagation();
            switch(e.type) {
                case "mousedown":
                    this.downX = e.pageX;
                    this.downY = e.pageY;
                    this.bs.left = this.downX + "px";
                    this.bs.top = this.downY + "px";
                    this.body.appendChild(this.box);
                    this.flag = true;
                    break;
                case "mousemove":
                    if (!this.flag) return;
                    this.moveX = e.pageX;
                    this.moveY = e.pageY;
                    if (this.downX > this.moveX) this.bs.left = this.moveX + "px";
                    if (this.downY > this.moveY) this.bs.top    = this.moveY + "px";
                    this.bs.width = Math.abs(this.moveX - this.downX) + "px";
                    this.bs.height = Math.abs(this.moveY - this.downY) + "px";
                    break;
                case "mouseup":
                    this.uninit();
                    break;
            }
        },
        init() {
            var win = {};
            Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
                .getFocusedElementForWindow(content, true, win);
            this.win = win.value;

            this.doc = this.win.document;
            this.body = this.doc.body;
            if (!HTMLBodyElement.isInstance(this.body)) {
                Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
                    .showAlertNotification("${self.image}", ${JSON.stringify(self.label)}, "Не удается захватить!");
                return false;
            }
            this.flag = null;
            this.box = this.doc.createElement("div");
            this.bs = this.box.style;
            this.bs.border = "#0f0 dashed 2px";
            this.bs.position = "absolute";
            this.bs.zIndex = "2147483647";
            this.defaultCursor = this.win.getComputedStyle(this.body, "").cursor;
            this.body.style.cursor = "crosshair";
            ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.addEventListener(type, this, true));
        },
        uninit() {
            var pos = [this.win, parseInt(this.bs.left), parseInt(this.bs.top), parseInt(this.bs.width), parseInt(this.bs.height)];
            this.body.style.cursor = this.defaultCursor;
            this.body.removeChild(this.box);
            this.parent.capture.apply(this, pos);
            ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.removeEventListener(type, this, true));
        }
    }`,
    click: `click: {
        getPosition() {
            var html = this.doc.documentElement;
            var body = this.doc.body;
            var rect = this.target.getBoundingClientRect();
            return [
                this.win,
                Math.round(rect.left) + (body.scrollLeft || html.scrollLeft) - html.clientLeft,
                Math.round(rect.top) + (body.scrollTop || html.scrollTop) - html.clientTop,
                parseInt(rect.width),
                parseInt(rect.height)
            ];
        },
        highlight() {
            this.orgStyle = this.target.hasAttribute("style") ? this.target.style.cssText : false;
            this.target.style.cssText += "outline: red 2px solid; outline-offset: 2px; -moz-outline-radius: 2px;";
        },
        lowlight() {
            if (this.orgStyle) this.target.style.cssText = this.orgStyle;
            else this.target.removeAttribute("style");
        },
        handleEvent(e) {
            switch(e.type){
                case "click":
                    if (e.button) return;
                    e.preventDefault();
                    e.stopPropagation();
                    this.lowlight();
                    this.parent.capture.apply(this, this.getPosition());
                    this.uninit();
                    break;
                case "mouseover":
                    if (this.target) this.lowlight();
                    this.target = e.target;
                    this.highlight();
                    break;
            }
        },
        init() {
            this.win = content;
            this.doc = content.document;
            ["click", "mouseover"].forEach(type=> this.doc.addEventListener(type, this, true));
        },
        uninit() {
            this.target = false;
            ["click", "mouseover"].forEach(type=> this.doc.removeEventListener(type, this, true));
        }
    }`
});

Добавлено 08-05-2025 23:33:33

Экспорт папки в HTML

Выделить код

Код:

// Пункт конт.меню закладок "Экспорт папки в HTML" - от Dumby
// https://forum.mozilla-russia.org/viewtopic.php?pid=803127#p803127
// + правка от VitalyV под 139+

(async topic => {
	var imp = m => ChromeUtils.importESModule(`resource://gre/modules/${m}.sys.mjs`)[m];
	var exporter = {
		get dps() {
			delete this.dps;
			return this.dps = imp("DownloadPaths");
		},
		get exporter() {
			delete this.exporter;
			return this.exporter = UcfPrefs.dbg.ref("BookmarkExporter", imp("BookmarkHTMLUtils").exportToFile);
		},
		async export(popup) {
			var win = popup.ownerGlobal, tn = popup.triggerNode;
			var node, pu = win.PlacesUtils, bm = pu.bookmarks;

			if (tn.nodeName == "treechildren") node = popup._view.selectedNode;
			else if (tn.id == "OtherBookmarks")
				node = {bookmarkGuid: bm.unfiledGuid, title: tn.getAttribute("label")};
			else node = tn._placesNode || popup._view.result.root;

			var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);

			fp.init(win.browsingContext, win.PlacesUIUtils.promptLocalization.formatValueSync("places-bookmarks-export"), fp.modeSave);
			fp.appendFilters(fp.filterHTML);
			fp.defaultString = (node.title ? this.dps.sanitize(node.title) : "untitled") + ".html";

			if (await new Promise(fp.open) == fp.returnCancel) return;

			var tree = await pu.promiseBookmarksTree(pu.getConcreteItemGuid(node), {includeItemIds: true});
			tree.title = bm.getLocalizedTitle(tree);
			var bookmarks = {children: [
				{root: "toolbarFolder"},
				{root: "unfiledBookmarksFolder"},
				{root: "bookmarksMenuFolder", children: [tree], guid: bm.menuGuid}
			]};
			new this.exporter(bookmarks).exportToFile(fp.file.path);
		},
		observe(doc) {
			var popup = doc.querySelector("menupopup#placesContext");
			if (!popup) return;

			var menuitem = doc.createXULElement("menuitem");
			for(let args of Object.entries({
				label: "Экспорт папки в HTML",
				selection: "folder",
				"node-type": "folder",
				"selection-type": "single|none",
				id: "placesContext_exportFolder",
			}))
				menuitem.setAttribute(...args);
			menuitem.addEventListener("command", e => exporter.export(e.currentTarget.parentElement));
			menuitem.exporter = this;
			doc.getElementById("placesContext_openSeparator").before(menuitem);
		}
	};
	Services.obs.addObserver(exporter, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(exporter, topic);
	}, "quit-application-granted");
})("chrome-document-loaded");

Добавлено 08-05-2025 23:36:01
To be continued... Как напишете чего нибудь.

Отредактировано _zt (08-05-2025 23:36:01)

Отсутствует

 

№198909-05-2025 00:43:10

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

Re: UCF - ваши кнопки, скрипты…

_zt
"Экспорт папки в HTML" для 136+ мне правил Farby здесь.

Отсутствует

 

№199009-05-2025 05:33:44

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

Re: UCF - ваши кнопки, скрипты…

QuickToggleAboutConfigSettings

Выделить код

Код:

// Quick Toggle от Damby - Быстрое переключение параметров about:config
// от https://forum.mozilla-russia.org/viewtopic.php?pid=784165#p784165
// и до https://forum.mozilla-russia.org/viewtopic.php?pid=789795#p789795
// + перепись скрипта от VitalyV 250425

(async (
    id = "QuickToggleAboutConfigSettings",
    label = "Quick Toggle Settings",
    tooltiptext = "Quick Toggle Settings\n\nЛКМ    Первое меню\nПКМ    Второе меню",
    image = "chrome://user_chrome_files/content/custom_styles/svg/QuickToggle.svg",
//=============================================================================
    {prefs} = Services, db = prefs.getDefaultBranch(""),
    pv = parseInt(Services.appinfo.platformVersion),
    xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
//=============================================================================
    // ЛКМ по пункту - переключает параметр на userChoice
    // или между userChoice и userAlt (если второй есть)
    //
    // Ctrl+ЛКМ или ПКМ по пункту - сброс параметра по-умолчанию FF
    // или его удаление, если параметр добавлен польз-лем\расширением\скриптом
    //
    // Первый символ в названии
    //    ! Маловероятно, но может сломать что-то на сайтах
    //    ? Баги Firefox
    //    • Связан с последующим
    //    ^ Связан с предыдущим
    //
    // Следующий символ после длинного дефиса, в названии:
    //    "молния"            - перезагрузка,
    //    "песочные часы"        - обновление страницы,
    //    "короткий дефис"    - отсутствие действия
    //
    // userChoice:
    //    ваше значение (зеленая метка)
    //
    // userAlt:
    //    альтернативное ваше значение (желтая метка)
    //
    // Иконки назначаются в в секции UserChoiceImg и ниже
    //
    // refresh:
    //    false    - перезагрука текущей вкладки
    //    true    - перезагрука текущей вкладки минуя кэш
    //
    // restart:
    //    false    - перезапуск браузера
    //    true    - перезапуск браузера с подтверждением (отмена - отменяет только
    //    перезапуск браузера, преф переключится, но может не применится)

    // Структуру заполнения смотрите в примерах.
    // Третий блок в значениях - это памятка в подменю, он не обязателен,
    // тем более что значение есть в подсказке (тултипе).

    primary = [
        {
            pref: ["network.proxy.type", "• Настройки прокси"],
            userChoice: 5, userAlt: 1, refresh: true,
            values: [
                [0, "Не проксировать", "0"],
                [5, "Системные (IE)", "5"],
                [2, "Авто (proxi.pac)", "2"],
                [1, "Прописанные", "1"],
                [4, "Автоопределение", "4"]
    ]},
            null,
    {
            pref: ["browser.zoom.full", "Масштабировать"],
            userChoice: false, userAlt: true, refresh: true,
            values: [[true, "Весь контент"], [false, "Только текст"]]
    },
            null,
    {
            pref: ["permissions.default.image", "Загружать web-графику"],
            userChoice: 1, userAlt: 2, refresh: true,
            values: [
                [1, "Да", "1"],
                [3, "С сайта", "3"],
                [2, "Нет", "2"]
    ]}
    ],

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

    secondary = [
        {
            pref: ["network.trr.mode", "• DoH DNS (режим trr)"],
            userChoice: 3, userAlt: 0, refresh: true,
            values: [
                [3, "Строгий"],
                [2, "DoH первым"],
                [5, "Явно отключен"],
                [0, "Отключен"]
    ]},{
            pref: ["network.trr.uri", "^ DoH DNS провайдер"],
            userChoice: "https://firefox.dns.nextdns.io/",
            userAlt: "https://doh.dns.sb/dns-query",
            refresh: true,
            values: [
                ["https://cloudflare-dns.com/dns-query", "Cloudflare"],
                ["https://mozilla.cloudflare-dns.com/dns-query", "Fx Cloudflare"],
                ["https://dns.comss.one/dns-query", "Comss DNS"],
                ["https://dns.google/dns-query", "Google DNS"],
                ["https://firefox.dns.nextdns.io/", "Fx NextDNS"]
    ]},{
            pref: ["network.trr.exclude-etc-hosts", "^ DoH учитывает HOSTS"],
            userChoice: true, userAlt: false, restart: true,
            values: [[true, "Да"], [false, "Нет"]]
    },
            null,
    {
            pref: ["dom.security.https_only_mode", "Режим Только HTTPS"],
            userChoice: false, refresh: true,
            values: [[true, "Скучно без проблем"], [false, "Нет"]]
    }
    ],

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

) => CustomizableUI.createWidget({
        id,
        label,
        tooltiptext,
        localized: false,
        onCreated(btn) {
            btn.setAttribute("image", image);
            var doc = btn.ownerDocument;
            btn.btn = true;
            btn.domParent = null;
            btn.popups = new btn.ownerGlobal.Array();
            this.createPopup(doc, btn, "primary", primary);
            this.createPopup(doc, btn, "secondary", secondary);
            this.createCloseMenusOption(doc, btn);
            for(var type of ["command", "contextmenu"])
                btn.addEventListener(type, this);
        },
        handleEvent(e) {
            this[e.type](e);
        },
        createPopup(doc, btn, name, data) {
            var popup = doc.createElementNS(xul_ns, "menupopup");
            var prop = name + "Popup";
            btn.popups.push(btn[prop] = popup);
            popup.id = `${id}-${prop}`;
            for (var type of ["popupshowing", "click"])
                popup.addEventListener(type, this);
            for(var obj of data) popup.append(this.createElement(doc, obj));
            btn.append(popup);
        },
        map: {b: "Bool", n: "Int", s: "String"},
        createElement(doc, obj) {
            if (!obj) return doc.createElementNS(xul_ns, "menuseparator");
            var pref = doc.ownerGlobal.Object.create(null), node, img, bool;
            for(var [key, val] of Object.entries(obj)) {
                if (key == "pref") {
                    var [apref, lab, akey, ttt] = val;
                    pref.pref = apref; pref.lab = lab || apref;
                    if (ttt) pref.ttt = ttt;
                }
                else if (key == "image") img = val, pref.img = true;
                else if (key != "values") pref[key] = val;
                else pref.hasVals = true;
            }
            var type = prefs.getPrefType(pref.pref);
            var str = this.map[type == prefs.PREF_INVALID
                ? obj.values ? (typeof obj.values[0][0])[0] : "b"
                : type == prefs.PREF_BOOL ? "b" : type == prefs.PREF_INT ? "n" : "s"
            ];
            pref.get = prefs[`get${str}Pref`];
            pref.set = prefs[`set${str}Pref`];

            node = doc.createElementNS(xul_ns, "menu");
            node.className = "menu-iconic";
            node.setAttribute("closemenu", "none");
            img && node.setAttribute("image", img);
            akey && node.setAttribute("accesskey", akey);
            (node.pref = pref).vals = doc.ownerGlobal.Object.create(null);
            this.createRadios(doc,
                str.startsWith("B") && !pref.hasVals ? [[true, "true"], [false, "false"]] : obj.values,
                node.appendChild(doc.createElementNS(xul_ns, "menupopup"))
            );
            if ("userChoice" in obj) pref.noAlt = !("userAlt" in obj);
            return node;
        },
        createCloseMenusOption(doc, btn) {
            var pn = this.closePref = `${id}.closeMenus`;
            var data = [null, {
                pref: [pn, "Автозакрытие этого меню"], values: [[true, "Да"], [false, "Нет"]]
            }];
            var setCloseMenus = e => {
                e.stopPropagation();
                var trg = e.target, {pref, val} = trg, updPopup = true, clear;
                switch(e.type) {
                    case "click": if (e.button) return; break;
                    case "contextmenu": e.preventDefault(); clear = pref;
                }
                if (!pref) return;
                if (clear) prefs.clearUserPref(pn);
                else if (!updPopup && val === pref.val) return;
                else pref.set(pn, val !== undefined ? val : !pref.val);
                this.upd(trg);
                updPopup && this.popupshowing(null, trg.querySelector("menupopup"));
            };
            (this.createCloseMenusOption = (doc, btn) => {
                for(var obj of data)
                    btn.secondaryPopup.append(this.createElement(doc, obj));
                var m = btn.secondaryPopup.lastChild;
                m.style.cssText = "fill: lightblue !important; list-style-image: url(chrome://browser/skin/menu.svg) !important;";
                m.onclick = m.oncontextmenu = m.setCloseMenus = setCloseMenus;
            })(doc, btn);
        },
        UserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHqSURBVDhPpVJBTxNBGH1LLakxVEJQQIkhlZCAJDYR/BGScNCDwZMcuEji2ZP+BD14MCGciDEaEw8e1At3QGLlwAUOFtm6tut2uwtb2t3p8GbZ0iXlVF/y5ttkv+/Nmzej5XI5/A+6otoxTh1ks9mwhniJHvRiBAHXLjRIEy7yeIqjqAPNuXYHS5jGVby7PTT27eHo7Or9zL3Va/19Xyj5FsuYjLpOcdbBG9xFGh+fZRaGJ5O3tB7tMrcXcGQZP+pb8tXuyjpdPcA89HYHynYazxdvzg5nkgOapFsPNleXP+uY6L6hzYxMT+EC5vCVa4SWAM880Ju4cyVxUXNkERUeuyJLIR1+O6wTqcEEUngEA5eiqZgAAxtMpfur0sIBLA7+hS2NkK5UGVpsqgEJjLMkT4biAkzbE27D5u7/ZAFWSB1l0iRtUokyEj+aCBEXMHcOA70Y/IElfsMUeyiJPIpBHiZZEvv4Vcsrge9Mi1ZO0BJQ98yw9w4tWT4yYNV1ki78AuyggHLNwLZjC1Txnpl60VRMQD2SBl5sGsHaT88XJd+DEzioCAd6rYpN1xf7Jj5T4BMe00eE9pf4GteZ8Ry6mbYKDDyzxAblP4TDT1BUbc2585+yumd1Vc201ZmV7djObQKdopVBRwCOAVyl9qcQtNEaAAAAAElFTkSuQmCC",
        notUserChoiceImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHqSURBVDhPpVJNSxtRFD1PJ9GMMVpaKyWhRGwRFy7Utv+gVFyUgoWif8Ct+APMsvQX+BuUWhdBcOvSDwi1XVhK0WgmTr4mmphoJvPRe5NnxhLdpAcO9828d869794nXNfF/6BLxo7RqiAcDjciYx3oDwFRBxikT4ey5CtAcg64aZ4ANE1rxDaDLeB176Aae/zx/ZR/NDqEuu1UD39oxsZ2wnIRmwV+8rl7DeLAGzXg+/pidSWivBwVCPQDvH91AfPot/tn6fOedW3NzTT0TYNWD7hsVfWtjH1ZjCjPggImFV25AKpE+xr+6CMxsvzpVXePmE8AipR5Bnzn4bcT0yJoCZSyQDkPXOrEDDHXYG8k0B18/nSBdvukzDPghqlB/xOUi4BBgiIJDUleF0lmVaEo7ngd8EnZP2N0uiolBwXKmksDegrISubovvyPWauR3kPLgBb5q+O8hnMyOD8jJoknTaZPKZLRSQp2+eaA6q9JmWcwTHPWv2cT0E0XGcqos4lkhgz4X7Jql3O1NZNaK2WewSQ9EtqIHcWPd5FybWQNoECNNApkdgmcwv61k47TbDbf0VykrP0hfaPlADAfCmFBERiHQN2sY79UwTqLP1BX+NyDL5HBc+ZR3Xab78xl383cZtAp7o6xAwB/AT+Y7GFJS/NjAAAAAElFTkSuQmCC",
        UserAltImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAH7SURBVDhPpVJLa1NBFP5mJolNcrWhtiq9Betjk0VFfNS1O7ciXbQguBDXxR9g3Lnown8g3SqCi24K/gCxVbqogiBaxdyY5nFrkt5H5j7GmZvxphDdxA8+Dgzn+86Zcw4RQuB/QHUcG2kHpmkmUWHtEo6DY55mUCIhYkHQOlXA97vv4esUWJaVxBGDp2Vcz54klakbN69kZ8wZRHHsVT9b7e2tHdFH5eEuPqi8vxqsLWDxmIGX5+8/mMufPkdA8zIBiMMuvNo3sbf+bCt2cWd1V+kHBukMVNuFE3h08d7SXHF6glC4oHFXGnTBmA9j1iBnb9+6xvJYfryIjJYdGaL889TlC1cLkyFhQQek3wThjYSUt8GCX9Iky/JniitGF0WtGhpQgdLEJJvO8AMpUgIbtK/JbflmI0cc2Y0oy/JZLRsaEIKYcCemXgvUlVXdOpj/c0Bnf0BXdhXxQEsSpAZqVU7NtkRPJh5aoAmrCYnzA9SpIbDr4G74Lsihr2VDA7Xn5kdvx2mGgnr7soNaSubXQb0GelUv8lt4Pj8rJ6yRGqgjERyVT68P3rarJIo6HojrJOS2h8Yejb6+OdyIe3i1tIlIy0YP6ckCTCOH5WIJKxmGsnwKeB/bbgcvlHj1Cxoq75+XqKD2rFb1Z9rqz6rto5VHDMZFOoPxAPwGQ077rbKVfhEAAAAASUVORK5CYII=",
        upd(node) {
            var {pref} = node, def = false, user = false, val;
            if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
                var pn = pref.pref;
                try {val = pref.defVal = db[pref.get.name](pn); def = true}
                catch(ex) {def = false;}
                var user = prefs.prefHasUserValue(pn);
                if (user) try {val = pref.get(pn, undefined);} catch(ex) {}
            }
            if (val == pref.val && def == pref.def && user == pref.user) return;
            pref.val = val; pref.def = def; pref.user = user;
            var exists = def || user;

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

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

            var {lab} = pref;
            if (exists && pref.hasVals) {
                if (val in pref.vals) var sfx = pref.vals[val] || val;
                else var sfx = user ? "Другое" : "По умолчанию";
                lab += ` ${"restart" in pref ? "— ↯" : "refresh" in pref ? "— ⧖" : "— -"} "${sfx}"`;
            }
            node.setAttribute("label", lab);
        },
        createRadios(doc, vals, popup) {
            for(var arr of vals) {
                if (!arr) {
                    popup.append(doc.createElementNS(xul_ns, "menuseparator"));
                    continue;
                }
                var [val, lab, key, ttt] = arr;
                var menuitem = doc.createElementNS(xul_ns, "menuitem");
                menuitem.setAttribute("type", "radio");
                menuitem.setAttribute("closemenu", "none");
                menuitem.style.setProperty("font-style", "italic", "important"),
                menuitem.setAttribute("label", popup.parentNode.pref.vals[val] = lab);
                key && menuitem.setAttribute("accesskey", key);
                var tip = menuitem.val = val;
                if (ttt) tip += "\n" + ttt;
                menuitem.tooltipText = tip;
                popup.append(menuitem);
            }
        },
        openPopup(popup) {
            var btn = popup.parentNode;
            if (btn.domParent != btn.parentNode) {
                btn.domParent = btn.parentNode;
                var pos;
                if (btn.matches(".widget-overflow-list > :scope"))
                    pos = "after_start";
                else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
                    btn.closest("toolbar").getBoundingClientRect(), pos = width > height
                        ? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
                        : `${win.innerWidth - right > left ? "end" : "start"}_before`;
                for(var p of btn.popups) p.setAttribute("position", pos);
            }
            popup.openPopup(btn);
        },
        maybeRestart(node, conf) {
            if (conf) {
                node.ownerGlobal.event.preventDefault();
                if (!Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) {
                    var popup = node.closest("menupopup");
                    return popup.openPopup(popup.parentNode);
                }
            }

            var cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
            Services.obs.notifyObservers(cancel, "quit-application-requested", "restart");
            return cancel.data ? Services.prompt.alert(null, this.label, "Запрос на выход отменен.") : this.restart();
        },
        async restart() {
            var meth = Services.appinfo.inSafeMode ? "restartInSafeMode" : "quit";
            Services.startup[meth](Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
        },
        regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
        maybeRe(node, fe) {
            var {pref} = node;
            if ("restart" in pref) {
                if (this.maybeRestart(node, pref.restart)) return;
            }
            else this.popupshowing(fe, node.parentNode);
            if ("refresh" in pref) {
                var win = node.ownerGlobal;
                if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
                    ? win.BrowserCommands.reloadSkipCache() : win.BrowserCommands.reload();
            }
        },
        maybeClosePopup(e, trg) {
            !e.ctrlKey && prefs.getBoolPref(this.closePref, undefined)
                && trg.parentNode.hidePopup();
        },
        command(e) {
            var trg = e.target;
            if (trg.btn) return trg.secondaryPopup.state == "closed" && this.openPopup(trg.primaryPopup);

            var menu = trg.closest("menu"), newVal = trg.val;
            this.maybeClosePopup(e, menu);
            if (newVal != menu.pref.val)
                menu.pref.set(menu.pref.pref, newVal),
                this.maybeRe(menu, true);
        },
        popupshowing(e, trg = e.target) {
            if (trg.state == "closed") return;
            if (trg.id) {
                for(var node of trg.children) {
                    if (node.nodeName.endsWith("r")) continue;
                    this.upd(node);
                    !e && node.open && this.popupshowing(null, node.querySelector("menupopup"));
                }
                return;
            }
            var {pref} = trg.closest("menu"), findChecked = true;

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

            this.maybeClosePopup(e, trg);
            if (!("noAlt" in pref)) return;

            if (pref.val == pref.userChoice)
                if (pref.noAlt) return;
                else  pref.set(pref.pref, pref.userAlt);
            else
                pref.set(pref.pref, pref.userChoice);
            this.maybeRe(trg);
        },
}))();

Добавлено 09-05-2025 05:41:58

Mem_Indicator

Выделить код

Код:

// Mem_Indicator от Dumby
// https://forum.mozilla-russia.org/viewtopic.php?pid=789048#p789048
// + правка VitalyV под 140
// Показывает суммарный жор памяти, (как в about:processes)
// Для custom_script.js в user_chrome_files

(async id => ({

    delay: 2e3,

    val: "",
    init(topic, mm) {
        Services.obs.addObserver(mm = this, topic);
        Services.obs.addObserver(function quit(s, t) {
            this.timer?.cancel();
            Services.obs.removeObserver(mm, topic);
            Services.obs.removeObserver(quit, t);
        }, "quit-application-granted");
    },
    observe(win) {
        var df = win.MozXULElement.parseXULToFragment(
// ЛКМ на кнопке - вызов ucf_MemoryMinimizationButton.uc.js
            `<hbox id="${id}" tooltiptext=
                "         ЛКМ   Минимизировать память&#xA;         ПКМ   Диспетчер процессов&#xA;Ctrl+ПКМ   Открыть about:memory"
            ><label id="${id += "-label"}"/></hbox>`
        );
        this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

        (this.observe = async win => {
            this.timer.cancel();
            await new Promise(ChromeUtils.idleDispatch);
// ПКМ, Ctrl+ПКМ
            var ind = win.document.importNode(df, true);
            var {firstChild} = ind;
            firstChild.oncontextmenu = this.about;
            firstChild.onclick = e => e.button || win.memoryMinimizationButton?.doMinimize();
            win.document.getElementById("star-button-box").after(ind);
            this.notify();
        })(win);
    },
    about(e) {
        var gb = e.view.gBrowser;
        gb.selectedTab = gb.addTrustedTab(`about:${
            e.ctrlKey ? "memory" : "processes"
        }`);
    },
//************************//
    async notify() {
        var info = await ChromeUtils.requestProcInfo();
        var bytes = info.memory;
        for(var child of info.children) bytes += child.memory;
        this.timer.initWithCallback(this, this.delay, this.timer.TYPE_ONE_SHOT);

        var prev = this.val;
        if ((this.val = this.mgb(bytes)) != prev)
            for(var win of CustomizableUI.windows) {
                var lab = win.document.getElementById(id);
                if (lab) lab.value = this.val;
            }
    },
    mgb: bytes => bytes < 1073741824
        ? Math.round(bytes / 1048576) + "MB"
        : (bytes / 1073741824).toFixed(2) + "GB"
}).init("browser-delayed-startup-finished"))("ucf-mem-indicator");

Memory_Minimization_Button.uc.js

Выделить код

Код:

// Vitaliy V. - 250502
// Подключать в двух местах
//
//  UcfStylesScripts.scriptschrome.load
//      { path: "memoryMinimizationButton.js", },
//
//  UcfStylesScriptsChild.scriptscontent.pageshow
//      { path: "memoryMinimizationButton.js", urlregxp: /about:processes\?memoryMinimizationButton/, },

var memoryMinimizationButton = {

  doMinimize: function(event) {
    function doGlobalGC()  {
       Services.obs.notifyObservers(null, "child-gc-request");
       Cu.forceGC();
    }

    function doCC()  {
      Services.obs.notifyObservers(null, "child-cc-request");
      if (typeof window.windowUtils != "undefined")
        window.windowUtils.cycleCollect();
      else
      window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
            .getInterface(Components.interfaces.nsIDOMWindowUtils).cycleCollect();
    }

    function doMemMinimize(event) {
      memoryMinimizationButton.kill();
      Services.obs.notifyObservers(null, "child-mmu-request");
    }

    doGlobalGC();
    doCC();
    // Всплывашка
    setTimeout((event)=> {doMemMinimize(event);}, 1000, event);
    var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
    alertsService.showAlertNotification("chrome://user_chrome_files/content/custom_styles/svg/memory_slot.svg", "Скрипт Memory Minimization", "Память освобождена успешно!");
    setTimeout(() => alertsService.closeAlert(), 2000);
  },

  kill: function() {
    var tab = gBrowser.addTrustedTab("about:processes?memoryMinimizationButton");
    gBrowser.hideTab(tab);
    setTimeout(() => {
      gBrowser.removeTab(tab);
    }, 8000);
  }
}

if (location.href == "about:processes?memoryMinimizationButton") {
  Services.console.logStringMessage("killing start");
  setTimeout(() => {
    let closeButtons = document.querySelectorAll("tr.process > td.close-icon");
    for(let closeButton of closeButtons) {
      let row = closeButton.parentNode;
      let canKill = true;
      if (row.querySelector("favicon")?.getAttribute("style")?.includes("link.svg")) {
        canKill = false;
        break;
      }
      for (let childRow = row.nextSibling;
           childRow && !childRow.classList.contains("process");
           childRow = childRow.nextSibling ) {
        let win = childRow.win;
        if (win?.tab?.tab?.selected) {
          canKill = false;
          break;
        }
      }
      if (canKill)
        Control._handleKill(closeButton);
    }
  }, 5000);
}

Добавлено 09-05-2025 05:48:45

reloadskipcache

Выделить код

Код:

// 126+ Функция Ctrl+Shift+R по ПКМ на стандартной кнопке обновления страницы
// https://forum.mozilla-russia.org/viewtopic.php?pid=784200#p784200
// 13x+ скрипт переписан VitalyV - 250509
(async (
    id = Symbol("reloadskipcache"),
    description1 = "ЛКМ Обновить страницу Ctrl+R",
    description2 = "СКМ Дублировать вкладку Ctrl+ЛКМ",
    description3 = "ПКМ Обновить минуя кэш Ctrl+Shift+R",
) => (this[id] = {
    init() {
        var node = this.node = CustomizableUI.getWidget("stop-reload-button").forWindow(window).node.querySelector("#reload-button");
        if (!node) return;
        var tooltipid = "ucf-reload-button-shortcut-tooltip";
        document.querySelector("#dynamic-shortcut-tooltip")?.after(MozXULElement.parseXULToFragment(`
            <tooltip id="${tooltipid}">
                <description class="tooltip-label">${description1}</description>
                <description class="tooltip-label">${description2}</description>
                <description class="tooltip-label">${description3}</description>
            </tooltip>
        `));
        node.tooltip = tooltipid;
        node.setAttribute("context", "");
        node.addEventListener("click", this);
        setUnloadMap(id, this.destructor, this);
    },
    handleEvent(e) {
        if (e.button != 2) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        BrowserCommands.reloadSkipCache();
    },
    destructor() {
        this.node.removeEventListener("click", this);
    },
}).init())();

Добавлено 09-05-2025 05:58:07

Bookmarks Star Tooltip Helper

Выделить код

Код:

// Bookmarks Star Tooltip Helper от Dumby (fx112)
// https://forum.mozilla-russia.org/viewtopic.php?pid=789303#p789303
// https://forum.mozilla-russia.org/viewtopic.php?pid=804649#p804649
// 13x+ Замена unloadlisteners
// Показывает информативную подсказку при наведении на звездочку в адресной строке
// Для custom_script_win.js в user_chrome_files

(async (id, sel) => {
	var g = Cu.getGlobalForObject(Cu), stt = g[id];
	if (!stt) {
		var {obs, prefs} = Services, {bookmarks: bm, observers: pobs} = PlacesUtils;
		stt = g[id] = {
			bm,
			pref: `ucf.${id}Guid`,
			async init() {
				this.args = [b => this.bguids.add(b.parentGuid), {concurrent: true}];
				this.pobsArgs = [
					["bookmark-added", "bookmark-moved"],
					this.record = this.record.bind(this)
				];
				pobs.addListener(...this.pobsArgs);
				obs.addObserver(this, "quit-application-granted");

				var guid = prefs.getCharPref(this.pref, "");
				if (!guid) try {var [guid] = await PlacesUtils.metadata.get(
					PlacesUIUtils.LAST_USED_FOLDERS_META_KEY, []
				)} catch {}
				this.guids.push(guid || await PlacesUIUtils.defaultParentGuid || bm.unfiledGuid);
			},
			observe() {
				pobs.removeListener(...this.pobsArgs);
				obs.removeObserver(this, "quit-application-granted");
				prefs.setCharPref(this.pref, this.guids[0]);
			},
			record(events) {
				for(var e of events) if (
					e.itemType == bm.TYPE_BOOKMARK && e.source == bm.SOURCES.DEFAULT
					&& !(e.type == "bookmark-moved" && e.parentGuid == e.oldParentGuid)
				)
					this.guids[0] = e.parentGuid;
			},
			bguids: new g.Set(),
			guids: new g.Array(),
			fetch(win) {
				this.bguids.clear();
				return bm.fetch({url: win.gBrowser.currentURI.spec}, ...this.args);
			},
			tt(de) {
				var kids = InspectorUtils.getChildrenForNode;
				return (this.tt = kids.length == 2
					? de => {
						var list = kids(de, true);
						return list.item(list.length - 1);
					}
					: de => kids(de, true, false).at(-1)
				)(de);
			}
		};
		stt.init();

		var func = id => this[id].handleEvent = async function(e) {
			var win = e.view;
			var star = e.target;
			star.tooltipText = "\u3164";
			var starred = star.hasAttribute("starred");
			starred && await this.fetch(win);
			var result = [];
			for(var guid of (starred ? this.bguids : this.guids)) {
				var arr = [], num = 50;
				while(--num) {
					if (!star.matches(":hover")) return;
					var res = await this.bm.fetch(guid);
					if (!res) break;
					if ((guid = res.parentGuid) == this.bm.rootGuid) {
						arr.unshift(this.bm.getLocalizedTitle(res));
						break;
					}
					arr.unshift(res.title || "[Безымянная папка]");
				}
				arr.length && result.push(arr.join("\\"));
			}
			if (!star.matches(":hover")) return;
			if (!result.length) return win.document.l10n.translateElements([star]);
			var text = result.join("\n");
			if (starred) {
				var m = result.length > 1;
// две строки
				text = `Заклад${m ? "ки" : "ка"} в:\n${text}`;
			} else
				text = "Последняя в:\n" + text;
			star.defaultTT.state == "open"
				? star.defaultTT.label = text : star.tooltipText = text;
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})("${id}")`);
		g.ChromeUtils.compileScript(url).then(ps => ps.executeInGlobal(g));
	}
	await delayedStartupPromise;
	var tt = stt.tt(document.documentElement);
	var stars = Array.from(document.querySelectorAll(sel));
	for(var star of stars) star.defaultTT = tt, star.addEventListener("mouseenter", stt);

	var destructor = () => {
		for(var star of stars) star.removeEventListener("mouseenter", stt);
	}
	var ucf = window.ucf_custom_scripts_win;
	if (ucf) ucf.setUnloadMap(id, destructor, this);
	else window.addEventListener("unload", destructor, {once: true});

})("ucfBookmarksStarFTooltipHelper", "#star-button, #context-bookmarkpage");

viewcookieswithrightclick

Выделить код

Код:

// Открытие менеджера куков по ПКМ на звездочке в адресной строке
// https://forum.mozilla-russia.org/viewtopic.php?pid=792269#p792269
// При открытии окна обновляется один раз, если не закрывая окна вызвать
// повторно, данные обновятся и будут далее обновляться с интервалом 5с
// Реинкарнация от Dumby, цель - исправление бага с размером окна
// https://forum.mozilla-russia.org/viewtopic.php?pid=802777#p802777
// 13x+ Замена unloadlisteners
(this.viewcookieswithrightclick = {
            init(that) {
                var star = this.star = document.querySelector("#star-button-box");
                if (!star)
                    return;
                star.addEventListener("contextmenu", this, true);
                star.addEventListener("click", this, true);
                //that.unloadlisteners.push("viewcookieswithrightclick");
                setUnloadMap(Symbol("viewcookieswithrightclick"), this.destructor, this);
            },
            handleEvent(e) {
                if (e.button != 2)
                    return;
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();
                if (e.type != "click")
                    return;
                this.viewCookies();
            },
            getETDL(uri) {
                var eTLD = "";
                try {
                    eTLD = Services.eTLD.getBaseDomain(uri);
                } catch (e) {
                    try {
                        eTLD = uri.asciiHost;
                    } catch (e) {}
                }
                return eTLD;
            },
            async viewCookies() {
                var sds = "chrome://ucfsdswnd/content/sds.xhtml";
                var type = "Browser:SiteDataSettings", g = Cu.getGlobalForObject(Cu);
                if (!Object.hasOwn(g, sds)) {
                    var xhtml = (await (await fetch("chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml")).text())
                        .replace(/(persist=".+)"/, `$1 screenX screenY sizemode" windowtype="${type}"`);
                    g[sds] = Cc["@mozilla.org/addons/addon-manager-startup;1"]
                        .getService(Ci.amIAddonManagerStartup).registerChrome(
                            Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile)),
                            [["override", sds, "data:application/xhtml+xml," + encodeURIComponent(xhtml)]]
                        );
                }
                (this.viewCookies = async () => {
                    var uri = gBrowser.selectedBrowser.currentURI;
                    try {
                        let _uri = ReaderMode.getOriginalUrl(uri.spec);
                        if (_uri)
                            uri = Services.io.newURI(_uri);
                    } catch(e) {}
                    uri = this.getETDL(uri);
                    var _win = Services.wm.getMostRecentWindow(type);
                    await SiteDataManager.updateSites();
                    if (!_win)
                        await new Promise(resolve =>
                            (_win = openDialog(sds, type, "chrome,dialog=no,resizable"))
                                .addEventListener("DOMContentLoaded", resolve, {once: true})
                        );
                    else if ("_gSiteDataSettings" in _win)
                         _win._gSiteDataSettings();
                    else {
                        Services.scriptloader.loadSubScript("data:," + encodeURIComponent(`
                            var _gSiteDataSettings = gSiteDataSettings._gSiteDataSettings = (function() {
                                SiteDataManager.getSites().then(sites => {
                                    this._sites = sites;
                                    var sortCol = document.querySelector("treecol[data-isCurrentSortCol=true]");
                                    this._sortSites(this._sites, sortCol);
                                    this._buildSitesList(this._sites);
                                });
                            }).bind(gSiteDataSettings);
                            _gSiteDataSettings();
                            var updateSetInterval = setInterval(async () => {
                                await SiteDataManager.updateSites();
                                _gSiteDataSettings();
                            }, 5000);
                            let removeBtns = document.querySelectorAll("#removeSelected, #removeAll");
                            var updateClearInterval = () => {
                                clearInterval(updateSetInterval);
                                for (let btn of removeBtns)
                                    btn.removeEventListener("command", updateClearInterval);
                                updateClearInterval = null;
                            };
                            for (let btn of removeBtns)
                                btn.addEventListener("command", updateClearInterval);
                        `), _win);
                         _win.addEventListener("unload", () => {
                            _win.updateClearInterval?.();
                        }, { once: true });
                    }
                    _win.focus();
                    var filter = _win.document.getElementById("searchBox");
                    if (!filter) return;
                    filter.value = uri;
                    filter.focus();
                    filter.dispatchEvent(new _win.Event("input", { bubbles: true }));
                })();
            },
            destructor() {
                this.star.removeEventListener("contextmenu", this, true);
                this.star.removeEventListener("click", this, true);
            },
        }).init(this);

Добавлено 09-05-2025 06:01:46

openpermissionswithrightclick

Выделить код

Код:

// Открытие окна "информации о странице" на вкладке разрешений,
// по клику ПКМ в адресной строке на боксе с иконками разрешений
// (и сайта, если добавлено скриптом FavIcon in URL-bar).
// https://forum.mozilla-russia.org/viewtopic.php?pid=783003#p783003
// Открываемую вкладку можно изменить ниже.
// 13x+ Замена unloadlisteners
(this.openpermissionswithrightclick = {
    init(that) {
        var identitybox = this.identitybox = document.querySelector("#identity-box");
        if (!identitybox)
            return;
        identitybox.addEventListener("contextmenu", this, true);
        identitybox.addEventListener("click", this, true);
        //that.unloadlisteners.push("openpermissionswithrightclick");
        setUnloadMap(Symbol("openpermissionswithrightclick"), this.destructor, this);
    },
    handleEvent(e) {
        if (e.button != 2)
            return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        if (e.type != "click")
            return;
        this.browserpageinfo(null, "permTab");
    },
    browserpageinfo() {
        this.browserpageinfo = ("BrowserCommands" in window)
        ? () => { BrowserCommands.pageInfo(...arguments) }
        : () => { BrowserPageInfo(...arguments) }
        this.browserpageinfo(...arguments);
    },
    destructor() {
        this.identitybox.removeEventListener("contextmenu", this, true);
        this.identitybox.removeEventListener("click", this, true);
    }
}).init(this);

Отредактировано _zt (09-05-2025 18:16:22)

Отсутствует

 

№199109-05-2025 06:10:21

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

Re: UCF - ваши кнопки, скрипты…

C простыми unloadlisteners я думаю понятно.
//that.unloadlisteners.push("xxxxxxxxxxxxxx");
setUnloadMap(Symbol("xxxxxxxxxxxxxx"), this.destructor, this);
и, в подключении - , ucfobj: true, }, если еще не так.

Добавлено 09-05-2025 06:13:16
Ну и до кучи:
cleardownloadsbutton.js
menusrestartitems.js

Добавлено 09-05-2025 06:16:56

Открытие окна "Инструменты браузера" по ПКМ на иконке developer-button

Выделить код

Код:

// Открытие окна "Инструменты браузера"
// по ПКМ на иконке developer-button
// VitalyV 250428
(async (
    id = Symbol("openbrowsertoolbox"),
    tooltipText = "ЛКМ	Меню инструментов\nПКМ	Инструменты браузера",
) => (this[id] = {
    async init() {
        await window.delayedStartupPromise;
        var btn = this.btn = CustomizableUI.getWidget("developer-button")?.forWindow(window).node;
        if (!btn) return;
        btn.setAttribute("context", "");
        btn.tooltipText = tooltipText;
        btn.addEventListener("click", this);
        setUnloadMap(id, this.destructor, this);
    },
    handleEvent(e) {
        if (e.button != 2) return;
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        var {prefs} = Services, chr = "devtools.chrome.enabled", rem = "devtools.debugger.remote-enabled";
        if (!prefs.getBoolPref(chr) || !prefs.getBoolPref(rem)) {
            prefs.setBoolPref(chr, true);
            prefs.setBoolPref(rem, true);
        }
        document.querySelector("#devtoolsKeyset > key#key_browserToolbox")?.doCommand();
    },
    destructor() {
        this.btn.removeEventListener("click", this);
    },
}).init())();

Добавлено 09-05-2025 06:25:48

tabCounter

Выделить код

Код:

// Счетчик вкладок в адресной строке
// https://forum.mozilla-russia.org/viewtopic.php?pid=797189#p797189
// 13x+ Замена unloadlisteners от VitalyV

(async style => {
	await document.documentReadyForIdle;

	var tabCounter = document.createXULElement("button");
	tabCounter.id = "tabCounter";
	tabCounter.setAttribute("tooltiptext", "Количество открытых вкладок");
	var urlbarIcons = document.getElementById("page-action-buttons");
	urlbarIcons.insertBefore(tabCounter, urlbarIcons.lastChild);

	var flag = "--reg-flag-ex";
	if (!windowUtils.getVisitedDependentComputedStyle(tabCounter, "", flag)) {
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		var uri = Services.io.newURI("data:text/css," + encodeURIComponent(
			`@-moz-document url(${location}) {${
				style.replace("{", `{\n\t\t${flag}: 1;`).replace(/;$/gm, " !important;")
			}}`
		));
		sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
	}
	var tid, lab = () => {
		tid = null;
		tabCounter.label = gBrowser.tabs.length;
	}
	var count = () => {
		tid && clearTimeout(tid);
		tid = setTimeout(lab, 150);
	}
	lab();

	var tc = gBrowser.tabContainer;
	tc.addEventListener("TabOpen", count);
	tc.addEventListener("TabClose", count);

	ucf_custom_scripts_win.setUnloadMap(Symbol(), () => {
		tc.removeEventListener("TabOpen", count);
		tc.removeEventListener("TabClose", count);
	}, this);
})(`
	#tabCounter {
		appearance: none;
		min-width: 22px;
		min-height: 22px;
		max-height: 22px;
		text-align: center;
		color: silver;
		background-color: inherit;
		border: none;
		padding: 0;
		margin: 0;
	}
`);

, ucfobj: false

Отредактировано _zt (09-05-2025 06:35:41)

Отсутствует

 

№199209-05-2025 07:02:02

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

Re: UCF - ваши кнопки, скрипты…

dinn
Работает. Grazie mille!

Отсутствует

 

№199309-05-2025 13:18:36

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 32
UA: Firefox 138.0

Re: UCF - ваши кнопки, скрипты…

_zt а нет у вас кнопки ToggleRestartlessAddons с правками для 139+?

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

Выделить код

Код:

// 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");
addEventListener("popupshowing", () => mp.updateMenu(), false, mp);
addEventListener("command", mp.onmousedown = mp.onclick = e => {
	if (!e.button || e.type.endsWith("k")) mp.handleEvent(e);
}, false, mp);
mp.toggleAttribute("context");
addEventListener("popuphidden", () => mp.destroyMenu(), false, mp);

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 = "currentColor";
			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;
		var v = this.useSVG && this.platformVersion;
		return this.plugin =
			v >= 88 && "chrome://global/skin/icons/plugin.svg" ||
			v >= 81 && "chrome://global/skin/plugins/plugin.svg" ||
			v >= 65 && "chrome://global/skin/plugins/pluginGeneric.svg" ||
			v >= 57 && "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;
}
function checkForAddonsUpdates() {
//== Check for Addons Updates begin
// http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57958
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates

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

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

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

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


var app = Services.appinfo.name;
var pv = parseFloat(Services.appinfo.platformVersion);

var ADDONS_URL = "about:addons";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		tab.collapsed = false;

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

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

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

	function done(t) {
		if(!t.hidden && !t.closing) {
			e.preventDefault();
			e.stopPropagation();
			return gBrowser.selectedTab = t;
		}
		return false;
	}
	for(var t = tab.nextSibling; t; t = t.nextSibling)
		if(done(t))
			return;
	for(var t = tab.previousSibling; t; t = t.previousSibling)
		if(done(t))
			return;
}
function ProgressIcon(btn) {
	var {icon} = btn, origIcon = icon.src;

	var box = btn.ownerDocument.createXULElement("hbox");
	box.toggleAttribute("busy");
	box.toggleAttribute("fadein");
	box.className = "tab-throbber";

	var s = btn.style, r = btn.getBoundingClientRect();
	s.setProperty("min-width", r.width + "px", "important");
	s.setProperty("min-height", r.height + "px", "important");
	Object.defineProperty(btn, "open", {configurable: true});

	icon.replaceWith(box);
	icon.src = "chrome://global/skin/icons/loading.svg";

	this.loading = () => {
		box.replaceWith(icon);
		s.removeProperty("min-width");
		s.removeProperty("min-height");
	}
	this.restore = () => {
		icon.src = origIcon;
		delete btn.open;
	}
}
}).call(this);
//== Check for Addons Updates end
}              

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

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	onCreated(btn) {
		var icon = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(39, 174, 129);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.9 15.3H3.2c-.88 0-1.6-.6-1.6-1.4v-2.7c0-.4.33-.6.74-.6h1.72c.7 0 1.25-.64 1.25-1.2 0-.64-.55-1.15-1.25-1.15H2.34c-.41 0-.74-.32-.74-.68V5.84c0-.81.72-1.48 1.6-1.48h2.36V3.13c0-1.21.93-2.297 2.21-2.419C9.23.57 10.5 1.62 10.5 2.98v1.38h2.4c.9 0 1.5.67 1.5 1.48v8.06c0 .8-.6 1.4-1.5 1.4z'/></svg>";
		var subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		icon = "resource://" + subst;

		var e = Object.create(null);
		var ael = (t, l, c, trg) => trg.addEventListener(t, l, c);
		var code = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/ucf-cbbtn-ToggleRestartlessAddons.js"
		));

		(this.onCreated = async btn => {
			btn.image = icon;
			var win = btn.ownerGlobal;
			await new Promise(win.setTimeout);
			new win.Function("self,event,_phase,addEventListener", code)
				.call(btn, btn, e, "init", ael);
		})(btn);
	}
}))();

Отсутствует

 

№199409-05-2025 14:06:10

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 662
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

unter_officer пишет

В 139 версии у меня отвалилось несколько скриптов. Но в первую очередь хотелось бы исправить два скрипта.
Первый в консоль пишет: "Content-Security-Policy: Параметры страницы заблокировали выполнение JavaScript eval (script-src), поскольку он нарушает следующую директиву: «script-src chrome: moz-src: resource: 'report-sample'» (Отсутствует 'unsafe-eval')"
Второй в консоль пишет: "EvalError: call to Function() blocked by CSP"


Я знаю, что можно поиграться параметрами about:config - security.browser_xhtml_csp.* и в 139 - security.allow_unsafe_dangerous_privileged_evil_eval, но думаю, что это временное решение и рано или поздно эти параметры выпилят и все равно придется править скрипты.


Просьба к знатокам, помогите исправить эти два скрипта.

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

Выделить код

Код:

//
// Сохранять изображение без запроса в указанную папку, из контекстного меню .....
//
try {
(this.contextsaveimg = {
    path: "E:\\Download",
    init(that) {
        var contextMenu = this.contextMenu = document.querySelector("#contentAreaContextMenu");
        if (!contextMenu) return;
        contextMenu.addEventListener("popupshowing", this);
        that.setUnloadMap("contextsaveimg", this.destructor, this);
    },
    destructor() {
        this.contextMenu.removeEventListener("popupshowing", this);
    },
    handleEvent(e) {
        if (!gContextMenu.onImage || gContextMenu.webExtBrowserType === "popup") return;
        var menuitem = document.createXULElement("menuitem");
        menuitem.setAttribute("id", "ucf_SaveImg");
        menuitem.setAttribute("label", "Сохранить изображение в папку: " + this.path);
        menuitem.addEventListener("click", function(e) { saveImg(); });
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAABAnAAAQJwAAAAAAAAAAAAAAAAEGAQECKAABATsAAAAoAAAAFAAAAAkAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjRRXUVtk8U+Y4TGKUBYrRgmNpcOFR91BQgMWAECBD8AAQEqAAAAGAAAAAwAAAAGAAAAAQAAAAAAAAAAAAAAAEVrp7Nws9//fcr6/3W+9f5wten5ZKHQ7lWGsedDaI7QL0hjtR4sQKERGSaBBgkOXgAAACoAAAAEAAAAAAAAAABKdrK+aqjY/4XO7P+AyfL/fcXy/4HL+/97xPT/eMHy/3e+8f9trt77YpvI8Ux0m98TGymTAAAAJQAAAAAAAAAAVIS7yGWh0/8yTVv/UniH/0htff+L0e//WIab/0x2i/9XiKL/RGp+/zNRYv9dkrP+S26S2QQGClwAAAAGAAAAAGeYxtFxrtr/IDA7/01mbf8sPkP/SWZw/zlSWv88Vl//LkRL/3Wsv/83UVr/XYqe/3+51PQaJDCWAAAAGwAAAAB5qtHajMXk/yY1Pf9fa23/GB8g/2yGjP8mMDL/Sl9k/0RYXP9wmKL/dZ+r/4i6y/+n4PD+TmV5yQECA0UAAAACh7bX46zh8/9tjZj/gpen/4GMk//G4Oj/kJ6f/52clf+svbz/f5id/3yXnv+gx9L/wez1/5u0xuwPExt+AAAAEJTA3ey/7vr/ve35/7Hf8v+w0ub/ubvE/8a4uf/NwLv/083I/9Pl6//T7ff/0O32/9fw9v/a6vL8P0ZYtAAAAC+iy+P2zvX8/831/P/K6e3/y7Kg/+GNZf/vlWr/6Zx3/9qegv/Ino7/t8TQ/7HM4/+itc3yu8ja52BtirQAAAEkqs/l9dv8/f/b/P3/1dzU/9qXcf/slmb/+aFr//+td//+rXb/+aRu/9+1k/+/2uL/RldrtiIrPU0ZITEoAAAAA46017zX8Pf85/7+/+X8+//g7+3+yryw+O6odPz/wYj/9bJ7/+KpgP/nqn3/0tPJ/kRTY6QAAQEYAAAAAAAAAAA6V3Yuboyqj6C81My50ubgo7zR1I92bLbzt3/3/tGZ/8aTZttfY2+PbX2Sn3GFm7EiLDtNAAAABQAAAAAAAAAAAgMEAAUHCgYYJDETIS4+LjMwNU+3e029/NGZ/vHHlPRrSSx/BQUFEAMFBwcEBQcJAQECAgAAAAAAAAAAAAAAAAAAAAAAAAAAEQgAA1wvElSsaz3B9LZ8+fC7gu6WZ0COFg0GGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcTAQrWbS+y7pdZ8eOVWMqUWy91IRIJFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AACf/wAAgH8AAIAfAACADwAAgA8AAIAHAACABwAAgA8AAIAPAADOHwAA/v8AAP//AAD//wAA//8AAA==");
        (this.contextMenu.querySelector("#context-sendimage") || this.contextMenu.lastElementChild).after(menuitem);
        this.handleEvent = () => menuitem.hidden = (!gContextMenu.onImage || gContextMenu.webExtBrowserType === "popup");

        function saveImg() {
            var p = Services.prefs;
            var data = Object.assign(Object.create(null), {
                "browser.download.folderList": { type: "Int", set: 2 },
                "browser.download.useDownloadDir": { type: "Bool", set: true },
                "browser.download.dir": { type: "String", set: this.path }
            });
            var lazy = {PrivateBrowsingUtils};
            var save = eval(`(function ${gContextMenu.saveMedia})`.replace("\n        false, // don't", "\n        true, //"));
            (menuitem.saveImg = () => {
                for(var pref in data) {
                    var obj = data[pref], meth = `et${obj.type}Pref`;
                    obj.val = p.prefHasUserValue(pref) ? p["g" + meth](pref) : null;
                    p["s" + meth](pref, obj.set);
                }
                try {save.call(gContextMenu);} finally {
                    for(var pref in data) data[pref].val === null
                        ? p.clearUserPref(pref)
                        : p[`set${data[pref].type}Pref`](pref, data[pref].val);
                }
            })();
        }
    }
}).init(this);
} catch(e) { Cu.reportError(e); }

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

Выделить код

Код:

//
// Сохранить как PNG .....
//
(async func => CustomizableUI.createWidget({
    id: "ucf_SaveAsPNG",
    label: "Сохранить как PNG",
    tooltiptext: "Сохранить как PNG",
    localized: false,
    // defaultArea: CustomizableUI.AREA_NAVBAR,
    onCreated(btn) {
        var win = btn.ownerGlobal;
        new win.Function("_id, xhtmlns, addDestructor", func.toString().slice(7, -1)).call(
            btn, this.id, "http://www.w3.org/1999/xhtml",
            destructor => win.addEventListener("unload", destructor, {once: true})
        );
        btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGwSURBVHjarJPPaxNBGIafmd1km23saqtRaLB6kFYl2ELpRayCVL20RvCmJwk9ePNWvPsXtCA9eNCDeO0hLemPo96LYAuKkRhtsBViEtI0rrt+GyFQ1oMkfjAf883M+8w7w4zyfZ9uwsxms10RzCBNTd3sSLy2lvsDCCKzWMCKGHgotOTAlpamJPm+jCmFL00bmmbD5WlmsKXTdBltB3bcxowaLWJwsTpi4bvNVu1JMmVn5cmcqTB6/DAg3hcjGpUFskjZjlgFKdmvVIiISKlfUhlyQMVPywgDjvX1YCjpOBZ2fZ+9+TuUpxcZuz7Ebl7ArkfM0dgDcOAFCvfwHTi9URJJi2RCqEuPmRxPUXn9imodLo1D6orGjMHW6govZqfDDkYviDXBlZ4vsFsrk8vXqZRWeDY8hz55lvvpGU7sFEn0RhhTtTBg/tETCvlNdj68papsUoND3Lh6iyPOOYrvv/JufYNrqcssbW8zkjwdBnz7/JKL50/x4F4at9GgsFckX/2IefQHZyb6mbl9l2LpE8ff1PnufWkD/t87yC1v/pvi4V8AwZvuNFS33/m3AAMAhEeAiqLmty4AAAAASUVORK5CYII=");
    }
}))(() => {
((main, parts) => this._handleClick = () => {
    var df = MozXULElement.parseXULToFragment(`
        <menupopup>
            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
                label="Сохранить всю страницу как PNG"
                value="all"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKaSURBVHjaYpx94sPD44//CDG9+/SDp8bsBw9AADEmrX7zmQEIGF+//fD/w8dPDAABxNix6/nbcA0GIQYoYGJiZGSQFBdmOHnqLAMrKwsDQAAxJq958/n/f4jsm2//GBifP3v+/8cfiMDDj/8ZmHi5ORl4gPjPn18M///9ZWDh5uVm4GJkYfj56zcD9z9GBoAAYmwH2aL8UwimDRk8+fyfgYWRgZFBgI+b4TsWBR+BtoNcxcDDwwW2R1iQn2HvgcMMX799Y/j6/TvDzz+/GVj+/fvPvO0BC8N/oGpGhv8ML3m0GS59EwWbcP/9XwaAAGSRwQ3CMAxFfxq3TgrqiSMswARMwSJMxZEdGAXBAEiQquICJMFuekDqwTlY38/Wi9kf78OqNTO8kndretBh+1puuvl+R8Dpwm9iruG8xPP/tIGzhULc1OB6voJJXxGjQpumGpsKCc8wEqISwGJfA1P1IYCEmCegkTmqLIELT/8KC+/x+cbxBpEPOl8zbn0q5voWXdcipQQrgZiC/QlQORmzNBAEUfjt7N4ichBUDIKCWNsJhiBiZ2mZf5XC0v/gT7C10lqwNMVBkBC9kKAmt7e+uQtJxBS6cNwy7L2b981bTrz/fnmIxnHTYN081i1F8PQacddDzm0M+UfAYPw/gfyTlqMNVfOJVxIGpfxNgMcZRwoYpkVpe1ezLn/jplc7B2wWb+1gwwfuvxg31lwi7MAimOU4E+IevuXoXt+gILTE1aQ1VBJnwNYR9ltXFKCweM9RWFpYCnjWiukU7dYJLs7PUMyKuglNAZt6GVncZ7SgIU2ZJ8M/bLqVQEFYN9hJPQ5201pgvhztjnk2ZhOYh+esd/vY39ZPZYWBXlURqZ4Qwg8uZXU3gM7p3vAbsXveewxHZWUAAAAASUVORK5CYII="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>

            <menuitem class="menuitem-iconic"
                image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL/SURBVHjaYijccvd25ZHvv5n4RSUVQs2vswAEECOIxwACy+8v/3/30Zv/AAHE0Hb6y+//UPDv4///LD/+MjJ8+PqF4cvnnww/vgkxAAQQY8Xhb78YgQCsj5mFgQGm/P+v///fvvj/n+n/fwaGe//vMexg2Mrw7BsDA8vXHwwMPN/5GdQ/mzG8BbIBAoiRfdqvX8xf/rBCDAEaAWL8ByOgmZwMLBXGvxnrzLgYVj9cyfCL+TfDj9+/Gd7//sAQw5vK8PEXAwMTRBsD0Li3DNKikgwC4twM3NIMDF9/fGf4CVTA8u0fM+ODjwwM7qJZDH8/MTBI/GFgUAba/QYo+fEnAwNAADKoXQVhIAjOJYfgsxIR/AwRi/yBlY3gz9j7FUI+wt7CwkbEzs7CNIo2Yl53t3duElDBhYXZZXaWGbHYKW0M4b+cex7WG9FaxmxGfNcfYNH3T+pr/LcSzu1h3Wx11EU2eOkYYRSi7jVgrQeb1TDtzFHkLgu13GQIBgGkJ6GFxva8h6XqX0kgRlF+AfkEw4S7ujEGbKnAzDa6GNEYLvOh2N6wOUGaVXHKayqQsHeiHgyfqKJ51sbxjvAWoG1qWWkYiKJnZhKTpgapK1tEslRw50+4camIGz/ObfcuBP0HBREEn12I+MBUadPM5HomqbWgi8kJydzHOfdctXmU29Vhbhqq8leNfxQyLP6lWgjjdxds9Yxs95ZRVAo+h8yFqEau2fHEKjpKqgojRDg+H0iw1gb212NY2oC/MCxz5DxGaYgPsKrGsnRYUEusnvKb4MNqnBlN5aZD8q54Hb2gP+gj62bohOyKdVxgobTBk3vE7cMzDhYPkVA0NW0xaBrlk6BZtRN20I16SMIWPt0CJlLQowqJStE2YwqnQRbE2ZwazrYURDquL5zcnCINU4xdiYo7QXXo4TesSMaIGGRTJ6jnqBxvkJNwfhFa2Ovu1jlrwXjD2gZ9wITeKVhImEFXgXcIgrvri/udqySztpDZemJurTzK70h/3h1xY3xpvwGLI42vrCwxmAAAAABJRU5ErkJggg=="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menupopup>
    `);
    var popup = df.firstChild;
    popup.setAttribute("context", "");

    popup.addEventListener("command", e => popup.handleCommand(e));

    popup.handleCommand = e => {
        var name = _id + ":DataURLReady";
        main = main.replace("%MESSAGE_NAME%", name);

        var urls = {}, configurable = true, enumerable = true;
        Object.entries(parts).forEach(([key, part]) => Object.defineProperty(urls, key, {
            configurable, enumerable, get() {
                var value = `data:;charset=utf-8,({${
                    encodeURIComponent(main + part)
                }%0A}).init("${key}")`;
                Object.defineProperty(urls, key, {configurable, enumerable, value});
                return value;
            }
        }));

        var getTabLabel = () => {
            var label = gBrowser.selectedTab.label;
            var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
            return label.substring(0, 50);
        }

        var listener = msg => {
            var fp = makeFilePicker();
            fp.init(
                !("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
                    ? window.browsingContext
                    : window
                    , "Сохранить как…", fp.modeSave);
            fp.appendFilter("", "*.png");

            var fileName = getTabLabel();
            fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '').replace(/\s+/g, '_').slice(0, 100).replace(/^\s+|\s+$/g, '');
            var fileDate = (function () {
                var d = new Date(), z = function(n){return (n < 10 ? '0' : '') + n};
                return '[' + z(d.getFullYear()) + '_' + z(d.getMonth()+1) + '_' + z(d.getDate()) + '\u2014' + z(d.getHours()) + '_' + z(d.getMinutes()) + '_' + z(d.getSeconds()) + ']';
            })();

            fp.defaultString = fileName + "_" + fileDate + ".png";
            fp.open(res => res == fp.returnCancel || !fp.file || makeWebBrowserPersist().saveURI(
                Services.io.newURI(msg.data), document.nodePrincipal,
                null, null, null, null, null, fp.file, null, null
            ));
        }
        messageManager.addMessageListener(name, listener);
        addDestructor(() => messageManager.removeMessageListener(name, listener));

        (popup.handleCommand = e => gBrowser.selectedBrowser.messageManager
            .loadFrameScript(urls[e.target.value], false)
        )(e);
    }
    this.append(df);
    (this._handleClick = () => popup.openPopup(this, "after_start"))();
})(`
    init(cmd) {
        cmd.startsWith("c")
            ? this[cmd].init(this[cmd].parent = this)
            : this[cmd]();
    },
    capture(win, x, y, width, height) {
        var canvas = win.document.createElementNS("${xhtmlns}", "canvas");
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        var tryDraw = ind => {
            try {ctx.drawWindow(win, x, y, canvas.width, canvas.height, "white")}
            catch(ex) {canvas.height = ind * canvas.width; tryDraw(--ind);}
        }
        tryDraw(17);
        sendAsyncMessage("%MESSAGE_NAME%", canvas.toDataURL("image/png"));
    },
    `, {

        all: `all() {
            var win = content;
            this.capture(win, 0, 0, win.innerWidth + win.scrollMaxX, win.innerHeight + win.scrollMaxY);
        }`,
        page: `page() {
            var win = content, doc = win.document, body = doc.body, html = doc.documentElement;
            var scrX = (body.scrollLeft || html.scrollLeft) - html.clientLeft;
            var scrY = (body.scrollTop || html.scrollTop) - html.clientTop;
            this.capture(win, scrX, scrY, win.innerWidth, win.innerHeight);
        }`,
        clipping: `clipping: {
            handleEvent(e) {
                if (e.button) return false;
                e.preventDefault();
                e.stopPropagation();
                switch(e.type) {
                    case "mousedown":
                        this.downX = e.pageX;
                        this.downY = e.pageY;
                        this.bs.left = this.downX + "px";
                        this.bs.top = this.downY + "px";
                        this.body.appendChild(this.box);
                        this.flag = true;
                        break;
                    case "mousemove":
                        if (!this.flag) return;
                        this.moveX = e.pageX;
                        this.moveY = e.pageY;
                        if (this.downX > this.moveX) this.bs.left = this.moveX + "px";
                        if (this.downY > this.moveY) this.bs.top  = this.moveY + "px";
                        this.bs.width = Math.abs(this.moveX - this.downX) + "px";
                        this.bs.height = Math.abs(this.moveY - this.downY) + "px";
                        break;
                    case "mouseup":
                        this.uninit();
                        break;
                }
            },
            init() {
                var win = {};
                Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
                    .getFocusedElementForWindow(content, true, win);
                this.win = win.value;

                this.doc = this.win.document;
                this.body = this.doc.body;
                if (!HTMLBodyElement.isInstance(this.body)) {
                    Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
                        .showAlertNotification("${self.image}", ${JSON.stringify(self.label)}, "Не удается захватить!");
                    return false;
                }
                this.flag = null;
                this.box = this.doc.createElement("div");
                this.bs = this.box.style;
                this.bs.border = "red dashed 1px";
                this.bs.position = "absolute";
                this.bs.zIndex = "2147483647";
                this.defaultCursor = this.win.getComputedStyle(this.body, "").cursor;
                this.body.style.cursor = "crosshair";
                ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.addEventListener(type, this, true));
            },
            uninit() {
                var pos = [this.win, parseInt(this.bs.left), parseInt(this.bs.top), parseInt(this.bs.width), parseInt(this.bs.height)];
                this.body.style.cursor = this.defaultCursor;
                this.body.removeChild(this.box);
                this.parent.capture.apply(this, pos);
                ["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.removeEventListener(type, this, true));
            }
        }`,
        click: `click: {
            getPosition() {
                var html = this.doc.documentElement;
                var body = this.doc.body;
                var rect = this.target.getBoundingClientRect();
                return [
                    this.win,
                    Math.round(rect.left) + (body.scrollLeft || html.scrollLeft) - html.clientLeft,
                    Math.round(rect.top) + (body.scrollTop || html.scrollTop) - html.clientTop,
                    parseInt(rect.width),
                    parseInt(rect.height)
                ];
            },
            highlight() {
                this.orgStyle = this.target.hasAttribute("style") ? this.target.style.cssText : false;
                this.target.style.cssText += "outline: red 1px solid; outline-offset: 1px; -moz-outline-radius: 2px;";
            },
            lowlight() {
                if (this.orgStyle) this.target.style.cssText = this.orgStyle;
                else this.target.removeAttribute("style");
            },
            handleEvent(e) {
                switch(e.type) {
                    case "click":
                        if (e.button) return;
                        e.preventDefault();
                        e.stopPropagation();
                        this.lowlight();
                        this.parent.capture.apply(this, this.getPosition());
                        this.uninit();
                        break;
                    case "mouseover":
                        if (this.target) this.lowlight();
                        this.target = e.target;
                        this.highlight();
                        break;
                }
            },
            init() {
                this.win = content;
                this.doc = content.document;
                ["click", "mouseover"].forEach(type=> this.doc.addEventListener(type, this, true));
            },
            uninit() {
                this.target = false;
                ["click", "mouseover"].forEach(type=> this.doc.removeEventListener(type, this, true));
            }
        }`
    });
});

Похоже, что про скрипты потихоньку можно забывать.
Судя по всему, после ухода с форума Dumby и Виталия, других альтруистов, среди знатоков по скриптам, нет и помощи больше ждать не от кого. :(

Отредактировано unter_officer (09-05-2025 14:08:27)


«The Truth Is Out There»

Отсутствует

 

№199509-05-2025 18:20:49

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

Re: UCF - ваши кнопки, скрипты…

reloadskipcache обновил.
   
unter_officer
Save чем не устраивает? Там только PNG без запроса нет.

Отсутствует

 

№199609-05-2025 18:30:21

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 662
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

_zt пишет

Save чем не устраивает?

Ставить такой крутой комбайн ради четырех функций? Как-то.... scratch_one-s_head.gif


add: Разве что попробовать убрать из Save всё для меня лишнее.

Отредактировано unter_officer (09-05-2025 23:14:16)


«The Truth Is Out There»

Отсутствует

 

№199709-05-2025 19:20:33

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

Re: UCF - ваши кнопки, скрипты…

unter_officer
Там пол скрипта как раз о PNG.

Отсутствует

 

№199809-05-2025 19:27:27

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 662
UA: Firefox 137.0

Re: UCF - ваши кнопки, скрипты…

_zt
Дело в том, что когда-то Dumby кто-то попросил вырезать из Save только эти четыре функции для PNG.
А потом уже я просил Dumby переделать этот отделенный от Save скрипт из СВ в UCF.
Просто не всем нужен такой навороченный комбайн как Save.

Отредактировано unter_officer (09-05-2025 19:31:34)


«The Truth Is Out There»

Отсутствует

 

№199909-05-2025 20:24:14

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 32
UA: Firefox 138.0

Re: UCF - ваши кнопки, скрипты…

unter_officer пишет

Похоже, что про скрипты потихоньку можно забывать.Судя по всему, после ухода с форума Dumby и Виталия, других альтруистов, среди знатоков по скриптам, нет и помощи больше ждать не от кого.

их понять можно.один из-за того что в основном тут росияне а второй "тащить весь форум на себе" не захотел
а сечас в [firefox] скриншотер неплохой.

Отсутствует

 

№200010-05-2025 16:50:31

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

Re: UCF - ваши кнопки, скрипты…

Нашёл у себя ещё 2 отвалившихся в 138 скрипта.
custom_script_win.js

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

Выделить код

Код:

// Пункт для контекстного меню адресной строки, подставляющий модификаторы поиска
(this.searchmodifiers = {
    init(that) {
        var urlbar = this.urlbar = gURLBar.textbox;
        if (!urlbar) return;
        urlbar.addEventListener("popupshowing", this);
//        that.unloadlisteners.push("searchmodifiers");
        setUnloadMap(Symbol("searchmodifiers"), this.destructor, this);
    },
    handleEvent(e) {
        if (e.originalTarget != this.popup) return;
        this.urlbar.removeEventListener("popupshowing", this);
        this.urlbar = null;
        var sep = this.popup.querySelector("menuseparator.menuseparator-add-engine") || this.popup.lastElementChild;
        var menu = this.append("menu", sep, {label: "Вставить ^ * + % ~ # @"}, "before");
        this.append("menuseparator", menu, null, "before");
        var popup = this.append("menupopup", menu, {oncommand: "insert(event);"}, "append");
        popup.addEventListener("popupshowing", this, { once: true });
        this.handleEvent = e => {
            var df = document.createDocumentFragment();
            for (let label of [
                "^ История (журнал)",
                "* Закладки",
                "+ Страницы с метками",
                "% Вкладки (текущие, открытые)",
                "~ Набранные",
                "# Названия",
                "@ Действия(по доступным тегам)"
            ])
                this.append("menuitem", df, {label}, "append");
            popup.append(df);
            var ed = gURLBar.inputField.editor
                .QueryInterface(Ci.nsIEditor || Ci.nsIPlaintextEditor);
            popup.insert = e => {
                var str = e.target.label[0] + " ";
// если раскомментировать строки ниже, добавляет пробел в конец (не пустого) url
//                var val = gURLBar.inputField.value;
//                if (val && !val.endsWith(" ")) str = " " + str;
//                ed.endOfDocument();
                ed.insertText(str);
            };
        };
    },
    get popup() {
        delete this.popup;
        return this.popup = gURLBar.inputField.parentNode.menupopup;
    },
    append(name, parent, attrs, func) {
        var elm = document.createXULElement(name);
        if (attrs) for (let a in attrs) elm.setAttribute(a, attrs[a]);
        parent[func](elm);
        return elm;
    },
    destructor() {
        this.urlbar?.removeEventListener("popupshowing", this);
    },
}).init(this);


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

Выделить код

Код:

// Добавить подменю "Поиск изображения в" в контекстном меню изображений
(this.searchimagecontextmenu = {
    handleEvent(evt) {
        if (evt.target != this.contextMenu || !gContextMenu?.imageInfo?.currentSrc) return;
        var array = [
            ['Google', 'https://www.google.lv/favicon.ico', 'https://www.google.com/searchbyimage?&image_url='],
            ['Яндекс', 'https://yastatic.net/morda-logo/i/favicon_islands.ico', 'https://yandex.ru/images/search?rpt=imageview&url='],
            ['Bing', 'https://www.bing.com/s/a/bing_p.ico', 'https://www.bing.com/images/search?view=detailv2&iss=sbi&form=SBIHMP&sbisrc=UrlPaste&q=imgurl:'],
            ['Tineye', 'https://tineye.com/favicon.ico', 'https://tineye.com/search?pluginver=bookmark_1.0&url='],
        ];
        var menu = document.createXULElement("menu");
        menu.setAttribute("label", "Поиск изображения в ...");
        menu.setAttribute("class", "menu-iconic");
        menu.setAttribute("image", array[1][1]);
        menu.setAttribute("onclick", "_searcclick(event);");
        menu._searcclick = function(e) {
            if (e.target != this) return;
            gBrowser.selectedTab = gBrowser.addTrustedTab(this._searcharg[2] + encodeURIComponent(gContextMenu.imageInfo.currentSrc), { index: gBrowser.selectedTab._tPos + 1 } );
            this.parentNode.hidePopup();
        }
        menu._searcharg = array[0];
        var menuPopup = document.createXULElement("menupopup");
        menu.append(menuPopup);
        array.forEach(m=> {
            var mItem = document.createXULElement("menuitem");
            mItem.setAttribute("label", m[0]);
            mItem.setAttribute("image", m[1]);
            mItem.setAttribute("class", "menuitem-iconic");
            mItem.setAttribute("oncommand", "gBrowser.selectedTab = gBrowser.addTrustedTab(_searcharg[2] + encodeURIComponent(gContextMenu.imageInfo.currentSrc), { index: gBrowser.selectedTab._tPos + 1 } );");
            mItem._searcharg = m;
            menuPopup.append(mItem);
        });
        var mItem = document.createXULElement("menuitem");
        mItem.setAttribute("label", 'Искать во всех поисковиках');
        mItem.setAttribute("oncommand", "_searcharg.forEach(m => { gBrowser.selectedTab = gBrowser.addTrustedTab(m[2] + encodeURIComponent(gContextMenu.imageInfo.currentSrc), { index: gBrowser.selectedTab._tPos + 1 } );});");
        mItem._searcharg = array;
        menuPopup.append(mItem);
        this.contextMenu.querySelector("#context-copyimage-contents")?.before(menu);
        this.popupshowing = e => {
            if (e.target != this.contextMenu) return;
            menu.hidden = !gContextMenu?.imageInfo?.currentSrc;
        };
        this.popuphiding = e => {
            if (e.target != this.contextMenu) return;
            menu.hidden = true;
        };
        this.contextMenu.addEventListener("popuphiding", this);
        this.handleEvent = e => {
            this[e.type](e);
        };
    },
    init(that) {
        var contextMenu = this.contextMenu = document.querySelector("#contentAreaContextMenu");
        if (!contextMenu) return;
        contextMenu.addEventListener("popupshowing", this);
//        that.unloadlisteners.push("searchimagecontextmenu");
        setUnloadMap(Symbol("searchimagecontextmenu"), this.destructor, this);
    }, на яндексе и бинге.
    destructor() {
        this.contextMenu.removeEventListener("popupshowing", this);
        this.contextMenu.removeEventListener("popuphiding", this);
    },
}).init(this);


Поиск хоть кривенько, но работал на яндексе и бинге.
Ещё pageinfo в контекстном меню страницы отвалился, но это фигня. А вот поиск по картинке жалко.

Отсутствует

 

Board footer

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