UserChromeFiles 2024 загрузчик скриптов/стилей расширяет возможности Firefox 115+ через функцию autoconfig. © Vitaliy V.
Установка: содержимое папки Firefox поместить в путь его установки, папку chrome в ваш профиль. После изменения скриптов нажать в about:support «Очистить кэш запуска». В окне UCF включить нужные панели и настройки. UCF old для Firefox 78+


Demo-Профиль 9.5.23 для Librewolf 84+ UCF-скрипты добавляют кнопки, меню, клики, стили: градиент загрузки, автоскрытие панели вкладок, яркость, быстрая закладка, восстановить удалённые закладки, сохранить картинку кликом, запись страниц/выделенного текста даже из режима чтения в единый HTML, перевод сайта/выдел. текста, url в подсказке ссылок, играть/скачать видео-ссылку из контекст-меню, переключение скрытых настроек, жесты мыши, 2 режима кнопок в опциях UCF, стили в зависимости от OS, режим Прокси меняет цвет фона кнопки ≡ Меню и многое другое…  Лёгкость настройки - всё в ucf_hookClicks.js
Подсказки на кнопках и встроенная Справка вам в помощь - жмите кнопку «Печать» секунду.

Скрипты/кнопки, установленные в Демо-профиле
Кнопки — расширяемые кликами кнопок мыши и колёсика, сочетания клавиш и жесты:
новые 14 — Журнал и меню опций, История, Закладки, Папки, Пароли, Просмотр части страницы или Запись в Html, Менеджер сессий, Восстановить удалённые закладки или fav-иконки, Ссылки в подсказках, Дополнения, Инспектор атрибутов, UCF…
стандартные 7 — Загрузки, ≡ Меню, Печать, Звёздочка, Щит защиты, Замок, Обновить/Стоп
расширения 2 — SingleFile (сохранение по Alt+Shift+S), Reader View.
User.js скрипт пользователя: запуск по Alt+X, ucf_aom-button.js управление дополнениями
ucf_hookClicks.js глобальные функции, меню, подсказки, перехват нажатий мыши и клавиатуры
ucf_QuickToggle.js открытие боковой панели, меню скрытых системных настроек
attrsInspector.js Attributes Inspector, auto_hide_sidebar.js скрытие боковой панели
menubarVisibilityChance.js, ucf_autohidetabstoolbar.js автоскрытие панели с одной вкладкой
ucf-loads-favicons.js восстановить иконки закладок
ucf_BookmarkDir.js быстрая закладка, перевод сайта/текста
ucf_contextsearch.js поиск в контекстном меню, ucf_findbarclose.js автоскрытие поиска
ucf_cooks-pass.js пароли и куки, ucf_UrlTooltip.js ссылки в подсказках
ucf_LocationBarEnhancer.js градиент загрузки страниц
ucf_pauseResume.js кнопка Пауза для скачиваемых файлов
ucf_downloads_clear.js кнопка "Очистить загрузки" на DownloadsPanel
ucf_session.js сохранение | восстановление сессий
ucf_tab-update.js автообновление вкладок по таймеру
ucf_undo-tab.js история закрытых вкладок/окон
ucf_win_contextmenuopenwith.js можно прописать свои приложения открытия ссылок
ucf_win_mousedrag.js жесты мыши для ссылок и выделенного текста
AppMenuTbbSaveHTMLChild.jsm сохранение страниц в единый Html
ClickPicSave.jsm сохранение картинок, в том числе и недоступных для сохранения обычным способом
UCFTitleChangedChild.jsm авто-коррекция имён (заголовков) вкладок
PageReadMode.user.js выбор части страницы курсорными клавишами после нажатия Alt+R
undoBookmarksContextMenu.uc.js восстановить удалённые Закладки в панели закладок
Возможности дополняются пользовательскими скриптами, например Vk Media Downloader

Обновляемые кнопки на github: например: Дополнения, Восстановить вкладки/Окна, Пауза/Продолжить в загрузках, Обновление вкладок по таймеру, Контекстный поиск, Simple Session Manager


    Скрипты для докум. окна браузера [ChromeOnly]
hookClicks — перехват кликов позволяет «разгрузить» кнопки, исключив код кликов
UCF drag and go жесты мыши
Перезапустить приложение в основном и классическом меню
Автоскрытие панели вкладок
Добавление прокси из контекстного меню
Скрытие панели поиска после клика на странице
Фавикон в адресной строке
Информация о странице, вкладка разрешения
Sidebar Tabs
Добавить кнопку "Очистить загрузки" на DownloadsPanel
Кнопка "Меню ссылок" (авт. Dumby)l


    Скрипты В фоне [System Principal]:
Восстановить фавиконки закладок
Автоматически добавлять выделенный текст в SearchBar (авт. Dumby)
Тултипы с URL (авт. Dumby)
Кнопка Прокси
Замена фавиконок для сайтов
Кнопка «Сохранить» комбинированная, конвертировать в base64

del

Vitaliy V.

Удаляйте все загрузчики скриптов из custom_script_all_win.js
добавляйте этот

У меня там только

стандартный загрузчик

Выделить код

Код:

load() {
        if (this.initialized)
            return;
        this.initialized = true;
        /* ************************************************ */

        // Здесь может быть ваш код который сработает по событию "load" не раньше

        /* ************************************************ */
    },


Его нужно заменить на тот, что по ссылке?

harryk
Загрузчики можно добавить где комментарий // Здесь может быть ваш код который сработает по событию "load" не раньше
но добавлять необязательно это для тех кто хочет чтобы были отдельные файлы.
Что касается custom_script_all_win.js у вас же там ничего нет значит и добавлять ничего не надо

Т.е. тот загрузчик нужно вставить вместо или рядом с комментарием?

Да


to all
Добавляйте здесь ссылки на скрипты или сами скрипты кто чем пользуется...

Vitaliy V.
Да, у меня там ничего нет. Пока нет, но я над этим работаю :)
Т.е. тот загрузчик нужно вставить вместо или рядом с комментарием?

Vitaliy V.
Огромное Спасибо за обновлённые загрузчики, скрипты и кнопки.
Еще добавлю здесь ссылку на дропмакер в адресной строке urlbarhistorydropmarker.
В 91+ тоже работает исправно, только иконка там поменялась на arrow-down.svg, сменил у себя в [nightly].
Ещё добавлю переработанные по Вашим советам скрипты классического окна загрузок для custom_script_all_win.js и русифицированные к тому же

ucjsDownloadsManager.uc.js

Выделить код

Код:

if (location.href == "chrome://browser/content/browser.xhtml") {
  Cu.import("resource://gre/modules/Services.jsm");

  window.ucjs_downloadManager = {
    _summary: null,
    _list: null,

    createElement: function(localName, arryAttribute) {
      let elm = document.createXULElement(localName);
      for(let i = 0; i < arryAttribute.length; i++) {
        elm.setAttribute(arryAttribute[i].attr, arryAttribute[i].value);
      }
      return elm;
    },

    init: function() {
      window.addEventListener("unload", this, false);

      let ref = document.getElementById("menu_openDownloads");
      let menu = ref.parentNode.insertBefore(
        this.createElement("menuitem",
          [{attr: "label", value:"Менеджер загрузок"},

           {attr : "oncommand", value: "ucjs_downloadManager.openDownloadManager(true);"}
          ]), ref);

      XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                "resource://gre/modules/Downloads.jsm");
      // Ensure that the DownloadSummary object will be created asynchronously.
      if (!this._summary) {
        this.Downloads.getSummary(this.Downloads.ALL).then(summary => {
          this._summary = summary;
          return this._summary.addView(this);
        }).then(null, Cu.reportError);
      }

      if (!this._list) {
        this.Downloads.getList(this.Downloads.ALL).then(list => {
          this._list = list;
          return this._list.addView(this);
        }).then(null, Cu.reportError);
      }
    },

    uninit: function() {
      window.removeEventListener("unload", this, false);

      if (this._summary) {
        this._summary.removeView(this);
      }
      if (this._list) {
        this._list.removeView(this);
      }
    },

    handleEvent: function(event) {
      switch (event.type) {
        case "unload":
          this.uninit();
          break;
      }
    },

    openDownloadManager: function ucjs_openDownloadManager(aForceFocus) {
      var enumerator = Services.wm.getEnumerator(null);
      while(enumerator.hasMoreElements()) {
        var win = enumerator.getNext();
        if (win.location == "chrome://browser/content/downloads/contentAreaDownloadsView.xhtml"
          && PrivateBrowsingUtils.isWindowPrivate(window) ==
             PrivateBrowsingUtils.isWindowPrivate(win)) {
          if (aForceFocus)
            win.focus();
          return;
        }
      }

      try {
        var height = Math.max(100,Services.prefs.getIntPref("browser.download.manager.size.height"));
        var width  = Math.max(300,Services.prefs.getIntPref("browser.download.manager.size.width"));
        var screenX = Math.min(Math.max(0,Services.prefs.getIntPref("browser.download.manager.size.screenX")), screen.availWidth - width);
        var screenY = Math.min(Math.max(0,Services.prefs.getIntPref("browser.download.manager.size.screenY")), screen.availHeight - height);
      } catch(r){
        height = 300;
        width  = 480;
        screenX = 5;
        screenY = 5;
      }
      var win = window.open("chrome://browser/content/downloads/contentAreaDownloadsView.xhtml",
                            "Download" +
                              (PrivateBrowsingUtils.isWindowPrivate(window) ? " - Private Window"
                                                                            : ""),
                            "outerWidth=" + width + ",outerHeight=" + height +
                            ",left=" + screenX + ",top=" + screenY +
                            ",chrome,toolbar=yes,dialog=no,resizable");
    },

    closeDownloadManager: function ucjs_closeDownloadManager() {
      var enumerator = Services.wm.getEnumerator(null);
      while(enumerator.hasMoreElements()) {
        var win = enumerator.getNext();
        if (win.location == "chrome://browser/content/downloads/contentAreaDownloadsView.xhtml") {
          win.close();
          return;
        }
      }
    },

    onDownloadAdded: function (aDownload) {
      var showWhenStarting = true;
      try {
        showWhenStarting = Services.prefs.getBoolPref("browser.download.manager.showWhenStarting");
      } catch(e) {}
      var numDls = 0;
      if (showWhenStarting) {
        if (this._list) {
          this._list.getAll().then(downloads => {
            for (let download of downloads) {
              if (!download.stopped)
                numDls++;
            }
            if (numDls > 0)
              this.openDownloadManager(false);
          }).then(null, Cu.reportError);
        }
      }
    },

    onDownloadChanged: function (aDownload) {
      if (!this._list)
        return;
      this._list.getAll().then(downloads => {
        var num = 0;
        for (let download of downloads) {
          if (!download.succeeded)
            num++;
        }
        if (num == 0) {
          var closeWhenDone = true;
          try {
            closeWhenDone = Services.prefs.getBoolPref("browser.download.manager.closeWhenDone");
          } catch(e) {}
          if (closeWhenDone) {
            this.closeDownloadManager();
          }
        }
      }).then(null, Cu.reportError);
    }
  };
  ucjs_downloadManager.init();
}

ucjsDownloadsManager2.uc.js

Выделить код

Код:

Cu.import("resource://gre/modules/Services.jsm");
  Cu.import("resource://gre/modules/DownloadIntegration.jsm");

  window.ucjs_downloadManagerMain = {
    originalTitle:"",
    _summary: null,
    _list: null,
    _wait:false,

    createElement: function(localName, arryAttribute) {
      let elm = document.createXULElement(localName);
      for(let i = 0; i < arryAttribute.length; i++) {
        elm.setAttribute(arryAttribute[i].attr, arryAttribute[i].value);
      }
      return elm;
    },

    createElementNS: function(NS, localName, arryAttribute) {
      let elm = document.createElementNS(NS, localName);
      for(let i = 0; i < arryAttribute.length; i++) {
        elm.setAttribute(arryAttribute[i].attr, arryAttribute[i].value);
      }
      return elm;
    },

    init: function() {
      window.addEventListener("unload", this, false);

      // xxx remove in-content css
      var elements = document.childNodes;
      for (var i = 0; i <= elements.length; i++) {
        var element = elements[i];
        if (element.nodeValue.indexOf("chrome://browser/skin/downloads/contentAreaDownloadsView.css") > -1) {
          document.removeChild(element);
          break;
        }
      }

      document.getElementById("downloadsListEmptyDescription").setAttribute("flex", "1");
      let ref = document.documentElement;
      ref = ref.appendChild(this.createElement("hbox", []));
      ref.appendChild(this.createElement("button",
        [{attr: "id", value: "ucjs_clearListButton"},
         {attr: "label", value: "Очистить загрузки"},

        ]));
      ref.appendChild(this.createElement("spacer",
        [{attr: "flex", value: "1"}]));
      ref.appendChild(this.createElementNS("http://www.w3.org/1999/xhtml", "input",
        [{attr: "id", value: "ucjs_downloadManagerMain_input"},
         {attr: "clickSelectsAll", value: "true"},
         {attr: "type", value: "search"},
         {attr: "placeholder", value: "Поиск в загрузках"},
         {attr: "aria-autocomplete", value: "list"}
        ]));

        document.getElementById("ucjs_clearListButton").addEventListener("command", function(event) {
            ucjs_downloadManagerMain.clearDownloads();
          });
        document.getElementById("ucjs_downloadManagerMain_input")
                .addEventListener("input", function(event) {
            ucjs_downloadManagerMain.doSearch(event.target.value);
          });

      this.originalTitle = document.title +
                           (PrivateBrowsingUtils.isWindowPrivate(window) ? " - Private Window"
                                                                         : "");

/*
      // xxx Bug 1279329 "Copy Download Link" of context menu in Library is grayed out
      var listBox = document.getElementById("downloadsRichListBox");
      var placesView = listBox._placesView;
      var place = placesView.place;
      placesView.place= null;
      placesView.place = place;
*/

      setTimeout(function(){this._wait = true}.bind(this), 0);

      // Ensure that the DownloadSummary object will be created asynchronously.
      if (!this._summary) {
        Downloads.getSummary(Downloads.ALL).then(summary => {
          this._summary = summary;
          return this._summary.addView(this);
        }).then(null, Cu.reportError);
      }

      if (!this._list) {
        Downloads.getList(Downloads.ALL).then(list => {
          this._list = list;
          return this._list.addView(this);
        }).then(null, Cu.reportError);
      }

      try {
        var showProgressInTaskButton = Services.prefs.getBoolPref("browser.download.manager.showProgressInTaskButton")
      } catch(ex) {
        showProgressInTaskButton = true; //default
      }
      if (showProgressInTaskButton)
        setTimeout(function() {
          try {
            let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                                  .getInterface(Ci.nsIWebNavigation)
                                  .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
                                  .QueryInterface(Ci.nsIInterfaceRequestor)
                                  .getInterface(Ci.nsIXULWindow).docShell;
            let gWinTaskbar = Components.classes["@mozilla.org/windows-taskbar;1"]
                                      .getService(Components.interfaces.nsIWinTaskbar);
            this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
          } catch(ex) {
            this._taskbarProgress = null;
          }
        }.bind(this), 10);
    },

    uninit: function() {
      window.removeEventListener("unload", this, false);

      this._taskbarProgress = null;
      if (this._wait)
        this.saveSizePosition();

      if (this._summary) {
        this._summary.removeView(this);
      }
      if (this._list) {
        this._list.removeView(this);
      }
    },

    handleEvent: function(event) {
      switch (event.type) {
        case "unload":
          this.uninit();
          break;
      }
    },

    saveSizePosition: function() {
      if (window.windowState == 3) {
        Services.prefs.setIntPref("browser.download.manager.size.height", window.outerHeight);
        Services.prefs.setIntPref("browser.download.manager.size.width", window.outerWidth);
        Services.prefs.setIntPref("browser.download.manager.size.screenX", window.screenX);
        Services.prefs.setIntPref("browser.download.manager.size.screenY", window.screenY);
      }
    },

    onSummaryChanged: function () {
      if (!this._summary)
        return;
      if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
        document.title = this.originalTitle;
        if (this._taskbarProgress) {
          this._taskbarProgress.setProgressState(
                                     Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
        }
        Cu.import("resource://gre/modules/Services.jsm");
        var enumerator = Services.wm.getEnumerator("navigator:browser");
        while(enumerator.hasMoreElements()) {
          return;
        }

        var closeWhenDone = true;
        try {
          closeWhenDone = Services.prefs.getBoolPref("browser.download.manager.closeWhenDone");
        } catch(e) {}
        if (closeWhenDone) {
          DownloadIntegration._store.save();
          window.close();
        }

      } else {

        // If the last browser window has been closed, we have no indicator any more.
        if (this._taskbarProgress) {
          if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
            this._taskbarProgress.setProgressState(
                                     Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
          } else {
            // For a brief moment before completion, some download components may
            // report more transferred bytes than the total number of bytes.  Thus,
            // ensure that we never break the expectations of the progress indicator.
            let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
                                                this._summary.progressCurrentBytes);
            this._taskbarProgress.setProgressState(
                                     Ci.nsITaskbarProgress.STATE_NORMAL,
                                     progressCurrentBytes,
                                     this._summary.progressTotalBytes);
          }
        }

        // Update window title
        var numDls = 0;
        if (!this._list)
          return;
        this._list.getAll().then(downloads => {
          for (let download of downloads) {
            if (download.hasProgress && !download.succeeded)
              numDls++;
          }

          let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
                                            this._summary.progressCurrentBytes);
          let percent = Math.floor(progressCurrentBytes / this._summary.progressTotalBytes * 100);
          let text = percent + "% из " + numDls + (numDls < 2 ? " файла - " : " файлов - ") ;
          document.title = text + this.originalTitle;
        }).then(null, Cu.reportError);
      }
    },

    clearDownloads: function ucjs_clearDownloads() {
      var DO_NOT_DELETE_HISTORY = true; /* custmizable true or false */
      var richListBox = document.getElementById("downloadsRichListBox");

      var places = [];
      function addPlace(aURI, aTitle, aVisitDate) {
        places.push({
          uri: aURI,
          title: aTitle,
          visits: [{
            visitDate: (aVisitDate || Date.now()) * 1000,
            transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
          }]
        });
      }
      function moveDownloads2History(d) {
        if (DO_NOT_DELETE_HISTORY &&
            !PrivateBrowsingUtils.isWindowPrivate(window)) {
          for (let element of richListBox.childNodes) {
            let download = element._shell.download;
            let aURI = makeURI(download.source.url);
            // let aTitle = document.getAnonymousElementByAttribute(element, "class", "downloadTarget").value
            let aTitle = download.target.path;
            aTitle = aTitle.match( /[^\\]+$/i )[0];
            aTitle = aTitle.match( /[^/]+$/i )[0];

            let aVisitDate = download.endTime || download.startTime;
            addPlace(aURI, aTitle, aVisitDate)
          }
        }

        // Clear List
        richListBox._placesView.doCommand('downloadsCmd_clearDownloads');

        if (DO_NOT_DELETE_HISTORY &&
            !PrivateBrowsingUtils.isWindowPrivate(window)) {
          if (places.length > 0) {
            var asyncHistory = Components.classes["@mozilla.org/browser/history;1"]
                     .getService(Components.interfaces.mozIAsyncHistory);
              asyncHistory.updatePlaces(places);
          }
        }
      }
      var btn = document.getElementById("ucjs_clearListButton");
      moveDownloads2History(0);
    },

    doSearch: function ucjs_doSearch(filterString) {
      var richListBox = document.getElementById("downloadsRichListBox");
      richListBox._placesView.searchTerm = filterString;
    }
  };
  ucjs_downloadManagerMain.init();

Added: Скрипт со стилием и иконками для добавления кнопки паузы в окна загрузок.
Небольшие косметические улучшения
Image_001.png

Выделить код

Код:

@-moz-document url(chrome://browser/content/downloads/contentAreaDownloadsView.xhtml) {
#downloadsListEmptyDescription {
    margin: 0 !important;
    padding: 1px 0 0 6px !important;
    background-color: white !important;
}
#contentAreaDownloadsView > hbox {
    background-color: -moz-Dialog !important;
    padding-top: 2px !important;
    border: 1px solid gainsboro !important;	
}
#ucjs_clearListButton {
    margin: 1px 2px 2px !important;
}
}

Скрипты авторства Dumby, которые использую:
Переключение вкладок наведением указателя на вкладку, и там же, как бы костыль, добавления закладки звёздочкой в адресной строке в меню закладок, а через меню на странице - в "Другие закладки" https://forum.mozilla-russia.org/viewto … 27#p786627
Действие двойного клика на панели вкладок - открыть новую вкладку вместо развернуть окно https://forum.mozilla-russia.org/viewto … 49#p782149
Кнопка "Закрыть другие вкладки" https://forum.mozilla-russia.org/viewto … 81#p788681
Скрипт возврата в адресную строку значка "Копировать ссылку" https://forum.mozilla-russia.org/viewto … 96#p790496
Скрипт возврата пункта "Информация о странице" https://forum.mozilla-russia.org/viewto … 92#p792092
Другой вариант возврата пункта "Информация о странице" с открытием вкладки "Разрешения", код в самом низу поста https://forum.mozilla-russia.org/viewto … 04#p792104
Ещё кнопка авторства Vitaliy V. вкл/откл звука на вкладке/вкладках https://forum.mozilla-russia.org/viewto … 10#p787710, тоже сильно выручает.
Через файл user_chrome.manifest комплекта user_chrome_files можно легко поменять значки интерфейса и так же на служебных страницах, что я и сделал в 89+, использовав значки из [firefox] 78 и свои кое-где

user_chrome.manifest firefox 90
Image_001.png

Выделить код

Код:

content user_chrome_files ./
# Не уверены, не редактируйте этот файл!

override chrome://global/skin/icons/defaultFavicon.svg chrome://user_chrome_files/content/custom_styles/svg/defaultFavicon.svg

override chrome://branding/content/identity-icons-brand.svg chrome://user_chrome_files/content/custom_styles/svg/defaultFavicon.svg

override chrome://mozapps/skin/extensions/extension.svg chrome://user_chrome_files/content/custom_styles/svg/extension.svg

override chrome://global/skin/icons/settings.svg chrome://user_chrome_files/content/custom_styles/svg/settings.svg

override chrome://mozapps/skin/extensions/extensionGeneric.svg chrome://user_chrome_files/content/custom_styles/svg/extension.svg

override chrome://global/skin/icons/help.svg chrome://user_chrome_files/content/custom_styles/svg/help.svg

override chrome://global/skin/icons/plugin.svg chrome://user_chrome_files/content/custom_styles/svg/pluginGeneric.svg

override chrome://mozapps/skin/extensions/category-discover.svg chrome://user_chrome_files/content/custom_styles/svg/category-discover.svg

override chrome://mozapps/skin/extensions/category-extensions.svg chrome://user_chrome_files/content/custom_styles/svg/category-extensions.svg

override chrome://mozapps/skin/extensions/category-themes.svg chrome://user_chrome_files/content/custom_styles/svg/category-themes.svg

override chrome://mozapps/skin/extensions/category-plugins.svg chrome://user_chrome_files/content/custom_styles/svg/category-plugins.svg

override chrome://browser/skin/preferences/category-general.svg chrome://user_chrome_files/content/custom_styles/svg/settings.svg

override chrome://browser/skin/home.svg chrome://user_chrome_files/content/custom_styles/svg/home.svg

override chrome://browser/skin/preferences/category-search.svg chrome://user_chrome_files/content/custom_styles/svg/category-search.svg

override chrome://browser/skin/preferences/category-privacy-security.svg chrome://user_chrome_files/content/custom_styles/svg/category-privacy-security.svg

override chrome://browser/skin/preferences/category-experiments.svg chrome://user_chrome_files/content/custom_styles/svg/category-experiments.svg

override chrome://global/skin/icons/more.svg chrome://user_chrome_files/content/custom_styles/svg/more.svg

override chrome://global/skin/icons/folder.svg chrome://user_chrome_files/content/custom_styles/svg/folder.svg

override chrome://browser/skin/places/folder.svg chrome://user_chrome_files/content/custom_styles/svg/folder.svg

override chrome://global/skin/icons/arrow-dropdown-16.svg chrome://user_chrome_files/content/custom_styles/svg/arrow-dropdown-16.svg

override chrome://browser/skin/history.svg chrome://user_chrome_files/content/custom_styles/svg/history.svg

override chrome://global/skin/icons/chevron.svg chrome://user_chrome_files/content/custom_styles/svg/chevron.svg

override chrome://browser/skin/menu.svg chrome://user_chrome_files/content/custom_styles/svg/menu.svg

override chrome://global/skin/icons/page-portrait.svg chrome://global/skin/icons/info.svg

override chrome://branding/content/icon16.png chrome://branding/content/identity-icons-brand.svg

override chrome://browser/skin/controlcenter/tracking-protection.svg chrome://user_chrome_files/content/custom_styles/svg/tracking-protection.svg

Частично менял стилем там, где не нужно все скопом менять или другие причины
скрытый текст

Выделить код

Код:

/* иконка щита "трекеры не обнаружены" */
#urlbar-input-container[pageproxystate="valid"] > #tracking-protection-icon-container > #tracking-protection-icon-box > #tracking-protection-icon {
	list-style-image: url(./svg/tracking-protection.svg) !important;
	fill-opacity: 0.6 !important;
}
/* иконка щита "блокируются трекеры" */
#urlbar-input-container[pageproxystate="valid"] > #tracking-protection-icon-container > #tracking-protection-icon-box:not([hasException])[active] > #tracking-protection-icon {
	display: block !important;
	background-image: url(./svg/tracking-protection-active.svg) !important;
	transform: translateX(-256px) !important;
	width: 272px !important;
	background-size: auto !important;
	height: 16px !important;
	min-height: 16px !important;
	-moz-context-properties: fill, fill-opacity !important;
}
/* зелёный замок */
#identity-box[pageproxystate="valid"].verifiedDomain #identity-icon, #identity-box[pageproxystate="valid"].mixedActiveBlocked #identity-icon {
	list-style-image:  url(./svg/security.svg) !important;
        fill: #12BC00 !important;
}
/* зелёный замок в "информация о сайте" */
#identity-popup[connection^="secure"] .identity-popup-security-connection {
	background-image:  url(./svg/security.svg) !important;
        fill: #12BC00 !important;
}
/* чёрный замок смешанного содержимого */
#identity-box[pageproxystate="valid"].weakCipher #identity-icon, #identity-box[pageproxystate="valid"].mixedDisplayContent #identity-icon, #identity-box[pageproxystate="valid"].mixedDisplayContentLoadedActiveBlocked #identity-icon, #identity-box[pageproxystate="valid"].certUserOverridden #identity-icon, #identity-box[pageproxystate="valid"].certErrorPage #identity-icon {
	list-style-image: url(./svg/security-warning.svg) !important;
	fill-opacity: 0.6 !important;
}
/* чёрный замок смешанного содержимого в "информация о сайте" */
#identity-popup[ciphers="weak"] .identity-popup-security-connection, #identity-popup[mixedcontent~="passive-loaded"][isbroken] .identity-popup-security-connection {
	background-image: url(./svg/security-warning.svg) !important;
}
/* зачёркнутый замок незащещённого содержимого */
#identity-box[pageproxystate="valid"].notSecure #identity-icon, #identity-box[pageproxystate="valid"].mixedActiveContent #identity-icon, #identity-box[pageproxystate="valid"].httpsOnlyErrorPage #identity-icon {
	list-style-image: url(./svg/security-broken.svg) !important;
	fill-opacity: 0.6 !important;
}
/* зачёркнутый замок незащещённого содержимого в "информация о сайте" */
.identity-popup-security-connection {
	background-image: url(./svg/security-broken.svg) !important;
}
/* значок дополнительных разрешений */
#permissions-granted-icon {
	list-style-image: url(./svg/permissions.svg) !important;
	fill-opacity: 0.6 !important;
}
/* значок запрета автовоспроизведения */
.autoplay-media-icon.blocked-permission-icon {
	list-style-image: url(./svg/autoplay-media-blocked.svg) !important;
	fill-opacity: 0.6 !important;
}
/* значок разрешённого автовоспроизведения */
.autoplay-media-icon {
	list-style-image: url(./svg/autoplay-media.svg) !important;
	fill-opacity: 0.6 !important;
}
/* кнопка "назад" */
#back-button {
	list-style-image: url("./svg/back.svg") !important;
}
/* кнопка "вперёд" */
#forward-button {
	list-style-image: url("./svg/forward.svg") !important;
}
/* значок "v" "показать историю" */
#urlbar .urlbar-history-dropmarker {
	/* list-style-image: url(./svg/arrow-dropdown-16.svg) !important; */
	fill-opacity: 0.6 !important;
}
/* звёздочка в адресной строке */
#pageAction-panel-bookmark, #star-button {
	list-style-image: url("./svg/bookmark-hollow.svg") !important;
	fill-opacity: 0.6 !important;
}
#pageAction-panel-bookmark[starred], #star-button[starred] {
	list-style-image: url("./svg/bookmark.svg") !important;
	fill-opacity: 1 !important;
}
/* прозрачность значка "копироать ссылку" */
#pageAction-urlbar-ucf-copyURL .urlbar-icon {
	fill-opacity: .6 !important;
}

Папка со значками.

Скрипты для custom_script.js:
URL tooltip: https://forum.mozilla-russia.org/viewto … 55#p783755

скрипт

Выделить код

Код:

(async () => {
    var id = "ucf-toggle-tooltip-url",
    label = "Тултипы с URL",
    tooltiptext = "Переключить тултипы",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' style='fill:context-fill rgb(142, 142, 152);'><path d='M9.618 6.721a2.483 2.483 0 0 0-.39-.317l-.735.734A1.486 1.486 0 0 1 8.91 9.55l-2.12 2.122a1.486 1.486 0 0 1-2.122 0 1.486 1.486 0 0 1 0-2.121l.605-.605a3.53 3.53 0 0 1-.206-1.209L3.961 8.843a2.506 2.506 0 0 0 0 3.535 2.506 2.506 0 0 0 3.535 0l2.122-2.121a2.506 2.506 0 0 0 0-3.536z'/><path d='M6.79 9.55c.12.121.25.226.389.317l.734-.734a1.486 1.486 0 0 1-.417-2.411L9.618 4.6a1.486 1.486 0 0 1 2.121 0 1.486 1.486 0 0 1 0 2.121l-.605.605c.137.391.211.798.206 1.209l1.106-1.107a2.506 2.506 0 0 0 0-3.535 2.506 2.506 0 0 0-3.535 0L6.789 6.014a2.506 2.506 0 0 0 0 3.536z'/><circle style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round' cx='8' cy='8' r='7.4'/></svg>",
    imgcolordisable = "color-mix(in srgb, currentColor 20%, #e31b5d)";

    var branch = "extensions.ucf.", pref = "tooltip_url_enable";
    var tpurl = {
        initialised: false,
        tooltip_url_enable: true,
        get ext_branch() {
            delete this.ext_branch;
            return this.ext_branch = Services.prefs.getBranch(branch);
        },
        init() {
            if (this.initialised) return;
            Services.prefs.getDefaultBranch(branch).setBoolPref(pref, true);
            Services.prefs.addObserver(`${branch}${pref}`, this);
            if (this.tooltip_url_enable = this.ext_branch.getBoolPref(pref))
                this.registerActor();
            this.initialised = true;
        },
        observe(subject, topic, data) {
            var fill = "";
            if ((this.tooltip_url_enable = this.ext_branch.getBoolPref(pref)) === true)
                this.registerActor();
            else {
                fill = imgcolordisable;
                this.unregisterActor();
            }
            this.callWithEachWindow(id, {fill: fill});
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
        },
        registerActor() {
            ChromeUtils.registerWindowActor("UcfTooltipUrl", {
                child: {
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/Ucf_TooltipUrlChild.jsm",
                    events: {
                        mouseover: { capture: true },
                    },
                },
                allFrames: true,
                matches: ["<all_urls>"],
                messageManagerGroups: ["browsers"],
            });
        },
        unregisterActor() {
            ChromeUtils.unregisterWindowActor("UcfTooltipUrl");
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            tpurl.init();
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
            if (!tpurl.tooltip_url_enable)
                btn.style.setProperty("fill", imgcolordisable);
        },
        onCommand(e) {
            tpurl.ext_branch.setBoolPref(pref, !tpurl.ext_branch.getBoolPref(pref));
        },
    });
})();

+
ucf_TooltipUrlChild.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfTooltipUrlChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
class UcfTooltipUrlChild extends JSWindowActorChild {
    handleEvent(e) {
        timer.cancel();
        timer.initWithCallback(() => {
            var elm = e.isTrusted && e.composedTarget, el, titl;
            if (!elm) return;
            do {
                if (!elm.matches) continue;
                if (elm.matches(":any-link")) {
                    if (elm.matches("[href='#'], [href^='javascript']"))
                        return;
                    el = elm;
                    if (elm.matches("[title]"))
                        titl = elm;
                    else
                        while (elm = elm.flattenedTreeParentNode) {
                            if (!elm.matches) continue;
                            if (elm.matches("[title]")) {
                                titl = elm;
                                break;
                            }
                        }
                    break;
                }
                if (elm.matches("[title]")) {
                    titl = elm;
                    while (elm = elm.flattenedTreeParentNode) {
                        if (!elm.matches) continue;
                        if (elm.matches(":any-link")) {
                            if (elm.matches("[href='#'], [href^='javascript']"))
                                return;
                            el = elm;
                            break;
                        }
                    }
                    break;
                }
            } while (elm = elm.flattenedTreeParentNode);
            if (!el) return;
            var href = el.href;
            if (titl) el = titl;
            titl = (el.title || "");
            var title = titl.trim(), pre = "", path = "";
            try {
                href = Services.io.newURI(href);
                pre = href.displayPrePath;
                path = `\n${href.pathQueryRef}`;
                if (path === "\n/") path = "";
                href = `${pre}${path}`;
            } catch (e) {}
            try {
                href = decodeURIComponent(href);
            } catch (e) {}
            el.title = title = `${href}${title === "" ? "" : `\nTitle: ${title}`}`;
            this.contentWindow.addEventListener("mouseout", () => {
                try {
                    if (!el || title !== el.title) return;
                    if (titl !== "")
                        el.title = titl;
                    else
                        el.removeAttribute("title");
                } catch (e) {}
            }, { once: true });
        }, 400, Ci.nsITimer.TYPE_ONE_SHOT);
    }
    didDestroy() {
        timer.cancel();
    }
}


Proxy: https://forum.mozilla-russia.org/viewto … 94#p782794;


Скрипты для custom_script_win.js:
Скрыть ненужные папки в боковой панели: https://forum.mozilla-russia.org/viewto … 25#p777225;
AutoPopup: https://forum.mozilla-russia.org/viewto … 64#p789264
Скрипты: search_image_contextmenu ... https://forum.mozilla-russia.org/viewto … 29#p788229

UndoBookmarksContextMenu - пункты контекстного меню закладок "вернуть\повторить удаление" (закладки)
Для custom_script_win.js или custom_script_all_win.js, в зависимости где должно работать -
панель закладок, сайдбар, библиотека в окне или библиотека во вкладке.
Строка загрузки - loadscript("subfolder/script_file_name.uc.js", win); или loadscript("script_file_name.uc.js", win);

скрытый текст
// ==UserScript==
// @name          s_UndoBookmarksContextMenu
// @namespace     http://space.geocities.yahoo.co.jp/gl/alice0775
// @include       *
// @compatibility Firefox 60
// @author        alice0775
// @version       2019/11/20 23:00 fix redeclaration error
// @version       2019/07/10 10:00 fix 70 Bug 1558914 - Disable Array generics in Nightly
// @version       2018/10/04 20:00 remove conflict shortcuts key for main window
// @version       2018/10/04 60+
// ==/UserScript==
if (typeof window.undobookmarksmenu == "undefined") {
  window.undobookmarksmenu = {
    popup: null,

    handleEvent: function(event) {
      switch (event.type) {
        case 'unload':
          this.uninit();
          break;
        case 'popupshown':
          this.popupshown(event);
          break;
      }
    },

    init: function() {
      window.addEventListener('unload', this, false);
      this.popup = document.getElementById("placesContext");
      if (!this.popup)
        return;
      this.popup.addEventListener('popupshown', this, false);
      let template = (location.href == "chrome://browser/content/browser.xul")  ?
                [
                  ["menuitem", {id: "undobookmarksmenuUndo",
                                disabled: "true",
                                label: "Вернуть удаленное",
                                key: "key_undo",
                                oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
                                selection: "any"
                  }],
                  ["menuitem", {id:"undobookmarksmenuRedo",
                                disabled: "true",
                                label: "Повторить удаление",
                                key: "key_redo",
                                oncommand: "PlacesTransactions.redo().catch(Cu.reportError);",
                                selection: "any"
                  }]
                ] : [
                  ["menuitem", {id: "undobookmarksmenuUndo",
                                disabled: "true",
                                label: "Вернуть удаленное",
                                key: "key_undo",
                                oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
                                acceltext: "Ctrl+Z",
                                selection: "any"
                  }],
                  ["menuitem", {id:"undobookmarksmenuRedo",
                                disabled: "true",
                                label: "Повторить удаление",
                                key: "key_redo",
                                oncommand: "PlacesTransactions.redo().catch(Cu.reportError);",
                                acceltext: "Ctrl+Y",
                                selection: "any"
                  }]
                ];

      let ref = document.getElementById("placesContext_deleteSeparator");
      ref.parentNode.insertBefore(this.jsonToDOM(template, document, {}), ref);
    },

    uninit: function() {
      window.removeEventListener('unload', this, false);
      if (!this.popup)
        return;
      this.popup.removeEventListener('popupshown', this, false);
    },

    popupshown: function(event){
      var menuitem = document.getElementById("undobookmarksmenuUndo");
      if (menuitem)
        menuitem.setAttribute('disabled', PlacesTransactions.topUndoEntry == null);
      menuitem = document.getElementById("undobookmarksmenuRedo");
      if (menuitem)
        menuitem.setAttribute('disabled', PlacesTransactions.topRedoEntry == null);
    },

    jsonToDOM: function(jsonTemplate, doc, nodes) {
      jsonToDOM.namespaces = {
      html: "http://www.w3.org/1999/xhtml",
      xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
      };
      jsonToDOM.defaultNamespace = jsonToDOM.namespaces.xul;
      function jsonToDOM(jsonTemplate, doc, nodes) {
        function namespace(name) {
            var reElemNameParts = /^(?:(.*):)?(.*)$/.exec(name);
            return { namespace: jsonToDOM.namespaces[reElemNameParts[1]], shortName: reElemNameParts[2] };
        }

        // Note that 'elemNameOrArray' is: either the full element name (eg. [html:]div) or an array of elements in JSON notation
        function tag(elemNameOrArray, elemAttr) {
          // Array of elements?  Parse each one...
          if (Array.isArray(elemNameOrArray)) {
            var frag = doc.createDocumentFragment();
            Array.prototype.forEach.call(arguments, function(thisElem) {
              frag.appendChild(tag.apply(null, thisElem));
            });
            return frag;
          }

          // Single element? Parse element namespace prefix (if none exists, default to defaultNamespace), and create element
          var elemNs = namespace(elemNameOrArray);
          var elem = doc.createElementNS(elemNs.namespace || jsonToDOM.defaultNamespace, elemNs.shortName);

          // Set element's attributes and/or callback functions (eg. onclick)
          for (var key in elemAttr) {
            var val = elemAttr[key];
            if (nodes && key == "key") {
                nodes[val] = elem;
                continue;
            }

            var attrNs = namespace(key);
            if (typeof val == "function") {
              // Special case for function attributes; don't just add them as 'on...' attributes, but as events, using addEventListener
              elem.addEventListener(key.replace(/^on/, ""), val, false);
            } else {
              // Note that the default namespace for XML attributes is, and should be, blank (ie. they're not in any namespace)
              elem.setAttributeNS(attrNs.namespace || "", attrNs.shortName, val);
            }
          }

          // Create and append this element's children
          var childElems = Array.prototype.slice.call(arguments, 2);
          childElems.forEach(function(childElem) {
            if (childElem != null) {
              elem.appendChild(
                  childElem instanceof doc.defaultView.Node ? childElem :
                      Array.isArray(childElem) ? tag.apply(null, childElem) :
                          doc.createTextNode(childElem));
            }
          });
          return elem;
        }
        return tag.apply(null, jsonTemplate);
      }

      return jsonToDOM(jsonTemplate, doc, nodes);
    }
  }


  window.undobookmarksmenu.init();
}

To be continued... Потом еще десяток выложу.

Кнопка для Attributes_Inspector от Dumby
https://forum.mozilla-russia.org/viewto … 07#p789007
Путь к attrsInspector.js прописать свой.
Сам attrsInspector.js - https://github.com/Infocatcher/Custom_B … _Inspector
Например, chrome://user_chrome_files/content/custom_scripts/custom_js/attrsInspector.js =
пути ФС - .\chrome\user_chrome_files\custom_scripts\custom_js\attrsInspector.js
Для custom_script.js в user_chrome_files
 
Открытие окна "Инструменты браузера" по ПКМ на иконке Attributes Inspector
Создано по шаблонам от Vitaliy V.

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

Выделить код

Код:

// Открытие окна "Инструменты браузера"
// по ПКМ на иконке Attributes Inspector
// Создано по шаблонам, где то здесь -
// https://forum.mozilla-russia.org/viewtopic.php?pid=791976#p791976
(this.opendevtoolsrclick3 = {
    async init(that) {
        await window.delayedStartupPromise;
        var btn = CustomizableUI.getWidget("AttributesInspector")?.forWindow(window).node;
        if (!btn) return;
        btn.setAttribute("context", "");
        btn.tooltipText = `ЛКМ: Attributes Inspector\nПКМ: Инструменты браузера`;
        var listener = e => {
            if (e.button != 2) return;
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            var pref = Services.prefs, chr = "devtools.chrome.enabled", rem = "devtools.debugger.remote-enabled";
                if (!pref.getBoolPref(chr) || !pref.getBoolPref(rem)) {
                    pref.setBoolPref(chr, true);
                    pref.setBoolPref(rem, true);
            }
            var { BrowserToolboxLauncher } = ChromeUtils.import("resource://devtools/client/framework/browser-toolbox/Launcher.jsm");
            BrowserToolboxLauncher.init();
        };
        btn.addEventListener("click", listener);
        that.unloadlisteners.push("opendevtoolsrclick3");
        this.destructor = () => {
            btn.removeEventListener("click", listener);
        };
    }
}).init(this);

Для custom_script_win.js
Строка загрузки - loadscript("subfolder/script_file_name.uc.js", this); или loadscript("script_file_name.uc.js", this);

Vitaliy V.
Спрошу в этой теме, возможно это можно сделать тоже только скриптом. Вы мне делали скрипт смены иконки поисковика.
Стилем, по этому шаблону, я сменил ещё иконку на старую на закладке.
Старую иконку взял по этому адресу https://yandex.ru/favicon.ico, странно что сейчас везде отображается не она а буква Я в красном круге, но не суть важно.

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

Выделить код

Код:

.bookmark-item:not([container])[image^='page-icon:https://yandex.ru/'] image {
    object-position: 16px 0px !important;
    list-style-image: none !important;
    background: url("./svg/yandex.ico") transparent center no-repeat !important;
}

Хотел вас спросить, может можно сменить фавикон на вкладке для заданного адреса, т.е. там где он изначально формируется, чтоб на вкладке и в адресной строке была одна и та же иконка.
Я использую этот скрипт для отображения фавикона в строке адреса, но наверно он там же считывает иконку, где и Ваш faviconinurlbar. Может можно что-то придумать.
Ещё поменял на NNM сразу на трёх адресах, там у них вообще странно, если масштаб страницы 100%, то отображается одна бабочка(нормальная), повёрнутая вправо, а если ставлю больше масштаб(133% использую почти везде), то уже другая.
Мелочи это всё конечно, но как-то это нелогично, в slimjet с бабочкой такой проблемы нет, а в [firefox] давненько уже.
скрытый текст
______.PNG
Войдите или зарегистрируйтесь, чтобы увидеть скрытый текст.

Выделить код

Код:

.bookmark-item:not([container]):is([image^='page-icon:https://nnm-club.me/'], [image^='page-icon:https://nnmclub.ro/'], [image^='page-icon:https://nnmclub.to/']) image {
    object-position: 16px 0px !important;
    list-style-image: none !important;
    background: url("./svg/nnmclub.ico") transparent center no-repeat !important;
}
sandro79 пишет

возможно это можно сделать тоже только скриптом

Это скрипты для сайтов, для этого есть Greasemonkey и т.д.

sandro79 пишет

там где он изначально формируется

Естественно со страницы сайта по ссылке например
<link rel="icon" href="favicon.ico">
либо из кеша


Ладно попробую но только потому что в Greasemonkey не сработают некоторые события.
Размеры например sizes="32x32" не учитываются, для всех одна иконка
custom_script.js

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

Выделить код

Код:

ChromeUtils.registerWindowActor("LinkWinActor", {
    child: {
        moduleURI: "chrome://user_chrome_files/content/custom_scripts/LinkWinActorChild.jsm",
        events: {
            DOMLinkAdded: { capture: true },
            DOMLinkChanged: {},
            DOMHeadElementParsed: {},
            pageshow: {},
        },
    },
    messageManagerGroups: ["browsers"],
    matches: [ // адреса где работает скрипт
        "https://yandex.ru/",
        "https://yandex.ru/?*",
        "https://yandex.ru/search/*",
        "https://passport.yandex.ru/*",
        "https://nnmclub.to/*",
        "about:config",
        "about:user-chrome-files",
    ],
});


LinkWinActorChild.jsm
скрытый текст

Выделить код

Код:

var EXPORTED_SYMBOLS = ["LinkWinActorChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
const LINK_SELECTOR = "link[href]:is([rel~='icon'],[rel~='apple-touch-icon'],[rel~='apple-touch-icon-precomposed'],[rel~='fluid-icon'],[rel~='mask-icon'])";
const ICONS = { // "домен, или адрес для about|chrome|resource": "иконка",
    "yandex.ru": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><rect rx='2' ry='2' width='16' height='16' style='fill:rgb(231, 43, 90);'/><path d='M 8.56,2 C 6.31,2 4.49,3.5 4.49,5.99 4.49,7.49 5.31,8.48 6.97,9.57 L 4,13.5 V 14 H 5.76 L 8.53,9.57 H 9.47 V 14 H 11 V 2 Z M 9.47,8.48 H 8.67 C 7.36,8.48 6.05,7.98 6.05,5.99 6.05,4 7.26,3 8.47,3 H 9.47 Z' style='fill:white;'/></g></svg>",
    "nnmclub.to": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><path d='M 0.887,0 C 0.54,0.14 0.289,0.279 0.135,0.711 0.051,0.851 0.015,0.991 0,1.27 -0.009,1.71 0.061,2.16 0.325,3.05 0.564,3.88 0.612,4.12 0.612,4.37 0.612,4.48 0.612,4.56 0.588,4.67 0.457,5.43 0.457,6.07 0.588,6.56 0.672,6.87 0.779,7.09 0.947,7.28 1.21,7.57 1.49,7.73 2.05,7.87 L 2.27,7.91 2.11,7.96 C 1.87,8.01 1.71,8.08 1.55,8.19 1.33,8.31 1.22,8.41 1.09,8.59 0.947,8.65 0.887,8.83 0.839,8.99 0.803,9.17 0.792,9.41 0.827,9.65 0.851,9.93 0.851,9.93 0.827,10 0.827,10.1 0.767,10.3 0.708,10.5 0.588,10.9 0.564,11.1 0.564,11.2 0.564,11.5 0.612,11.6 0.875,11.8 1.15,12.2 1.18,12.3 1.15,12.6 1.1,13.2 1.18,13.6 1.39,13.7 1.49,13.7 1.6,13.9 1.75,14 1.93,14 2.05,14.1 2.28,14.4 2.48,14.5 2.55,14.5 2.64,14.5 2.73,14.7 2.73,14.7 2.87,14.7 3.04,14.7 3.04,14.7 3.47,14.5 4.03,14 4.19,13.9 4.44,13.9 4.55,13.7 4.6,13.7 4.68,13.7 4.88,13.6 5.11,13 5.35,11.9 5.39,11.8 5.43,11.6 5.47,11.5 5.65,10.9 5.89,10.4 6.12,10 6.24,9.83 6.48,9.59 6.49,9.59 6.49,9.59 6.49,9.75 6.48,10.1 6.44,10.3 6.43,10.5 6.28,11.1 6.12,11.5 6.08,11.7 6.05,12 6.03,12.4 6.11,12.6 6.21,12.9 6.32,13 6.43,13 6.57,13 6.71,12.9 6.81,12.6 6.91,12.4 7.07,12 7.11,11.7 7.11,11 7.11,10.7 7.13,10.4 7.27,10 7.35,9.75 7.32,9.75 7.37,9.83 7.52,10.1 7.64,10.7 7.69,11.3 7.72,11.6 7.72,12.2 7.69,12.4 7.64,13 7.61,13.6 7.68,14 7.72,14.4 7.76,14.5 8.01,14.5 8.15,14.7 8.31,14.8 8.67,15.5 8.92,15.9 9.03,15.9 9.11,16 9.17,16 9.17,16 9.33,16 9.52,16 9.52,16 9.77,15.9 10.1,15.7 10.1,15.7 10.4,15.7 10.7,15.7 10.7,15.7 10.7,15.7 11,15.6 11.1,15.5 11.4,14.9 11.4,14.9 11.4,14.8 11.5,14.7 11.5,14.5 11.6,14.4 11.7,14.1 12.2,13.9 12.2,13.9 12.2,13.7 12.4,13.6 12.4,13.6 12.4,13 12.4,12.6 12.5,12.4 12.5,12.3 12.5,12.3 12.5,12.2 12.7,12 12.8,11.7 12.8,11.6 12.8,11.5 12.8,11.2 12.8,11.1 12.8,11.1 12.7,10.7 12.4,10.3 12,9.93 11.9,9.83 11.9,9.83 11.9,9.83 12.2,9.93 12.7,9.93 12.9,9.93 13.7,9.75 14,9.08 14.1,7.84 14.1,7.76 14.3,7.59 14.3,7.49 14.3,7.27 14.3,7.19 14.5,7.01 14.5,6.83 14.8,6.6 15.1,6.19 15.7,5.24 15.9,4.85 16,4.41 16,4.31 16,4.25 16,4.03 16,3.84 16,3.76 16,3.69 15.7,3.03 15.2,2.68 14.3,2.75 13.7,2.76 13,2.97 12.2,3.32 12,3.4 11.6,3.63 11.4,3.81 10.2,4.49 9.11,5.55 8.08,6.85 7.95,7 7.85,7.11 7.85,7.11 7.85,7.11 7.84,7.05 7.84,7.04 7.76,6.93 7.72,6.87 7.61,6.81 L 7.53,6.73 7.64,6.47 C 7.92,5.92 8.13,5.51 8.31,5.23 8.35,5.12 8.51,4.91 8.76,4.63 9.43,3.73 9.68,3.47 9.77,3.35 10,3.21 10,3.2 10,3.15 10.1,3.05 10,2.92 10,2.87 9.95,2.87 9.95,2.91 9.84,2.97 9.84,3.11 9.52,3.44 9.11,4.03 8.92,4.19 8.76,4.37 8.76,4.45 8.35,4.95 7.95,5.69 7.61,6.47 7.53,6.6 7.49,6.73 7.47,6.73 7.47,6.73 7.43,6.71 7.37,6.71 L 7.28,6.68 V 6.59 C 7.2,5.69 7.08,5.08 6.91,4.49 6.83,4.16 6.8,4 6.59,3.44 6.33,2.75 6.2,2.32 6.2,2.21 6.17,2.09 6.05,2.08 6,2.2 5.97,2.31 5.99,2.39 6.11,2.55 6.21,2.75 6.32,2.97 6.73,4.15 7,4.96 7.07,5.13 7.2,6.29 7.23,6.68 7.23,6.68 7.2,6.68 7.13,6.68 6.97,6.75 6.89,6.83 6.83,6.87 6.81,6.89 6.81,6.89 6.81,6.89 6.75,6.73 6.68,6.56 6.53,6.07 6.4,5.77 6.2,5.27 5.36,3.43 4.39,2.01 3.36,1.13 2.64,0.571 2.01,0.14 1.49,0 1.32,0 1.02,0 0.887,0 Z' style='fill:rgb(0, 140, 255);stroke:black;stroke-width:0.6;stroke-linejoin:round;stroke-linecap:round;'/></g></svg>",
    "about:config": "chrome://global/skin/icons/settings.svg",
    "about:user-chrome-files": "chrome://global/skin/icons/settings.svg",
};

class LinkWinActorChild extends JSWindowActorChild {
    actorCreated() {
        var docURI = this.document.documentURIObject, host;
        if (!/^(?:about|chrome|resource)$/.test(docURI.scheme))
            try {
                let baseDomain = Services.eTLD.getBaseDomain(docURI);
                host = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService)
                .convertToDisplayIDN(baseDomain, {});
            } catch (e) {
                try {
                    host = docURI.displayHost;
                } catch (e) {
                    host = docURI.specIgnoringRef;
                }
            }
        else
            host = docURI.specIgnoringRef;
        var icon = ICONS[host];
        if (!icon) {
            this.handleEvent = e => {};
            return;
        }
        this._icon = icon;
        this.onHeadParsed(this.document.head);
    }
    onHeadParsed(target) {
        for (let link of target.querySelectorAll(LINK_SELECTOR))
            link.remove();
        var link = this.document.createElement("link");
        link.setAttribute("rel", "icon");
        link.setAttribute("sizes", "any");
        link.setAttribute("href", this._icon);
        target.append(link);
    }
    onLinkEvent(link) {
        if (!link.matches(LINK_SELECTOR) || link.href == this._icon) return;
        link.href = this._icon;
        link.setAttribute("rel", "icon");
        link.setAttribute("sizes", "any");
        if (link.hasAttribute("type"))
            link.removeAttribute("type");
    }
    handleEvent(e) {
        switch (e.type) {
            case "DOMLinkAdded":
            case "DOMLinkChanged":
                this.onLinkEvent(e.target);
                break;
            case "pageshow":
                this.onHeadParsed(e.target.head);
                break;
        }
    }
}


Или вариант поддомен + домен, для тех кому надо устанавливать разные иконки для поддоменов

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

Выделить код

Код:

var EXPORTED_SYMBOLS = ["LinkWinActorChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
const LINK_SELECTOR = "link[href]:is([rel~='icon'],[rel~='apple-touch-icon'],[rel~='apple-touch-icon-precomposed'],[rel~='fluid-icon'],[rel~='mask-icon'])";
const ICONS = { // "поддомен + домен, или адрес для about|chrome|resource": "иконка",
    "yandex.ru": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><rect rx='2' ry='2' width='16' height='16' style='fill:rgb(231, 43, 90);'/><path d='M 8.56,2 C 6.31,2 4.49,3.5 4.49,5.99 4.49,7.49 5.31,8.48 6.97,9.57 L 4,13.5 V 14 H 5.76 L 8.53,9.57 H 9.47 V 14 H 11 V 2 Z M 9.47,8.48 H 8.67 C 7.36,8.48 6.05,7.98 6.05,5.99 6.05,4 7.26,3 8.47,3 H 9.47 Z' style='fill:white;'/></g></svg>",
    get "passport.yandex.ru"() { return this["yandex.ru"]; },
    "nnmclub.to": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><path d='M 0.887,0 C 0.54,0.14 0.289,0.279 0.135,0.711 0.051,0.851 0.015,0.991 0,1.27 -0.009,1.71 0.061,2.16 0.325,3.05 0.564,3.88 0.612,4.12 0.612,4.37 0.612,4.48 0.612,4.56 0.588,4.67 0.457,5.43 0.457,6.07 0.588,6.56 0.672,6.87 0.779,7.09 0.947,7.28 1.21,7.57 1.49,7.73 2.05,7.87 L 2.27,7.91 2.11,7.96 C 1.87,8.01 1.71,8.08 1.55,8.19 1.33,8.31 1.22,8.41 1.09,8.59 0.947,8.65 0.887,8.83 0.839,8.99 0.803,9.17 0.792,9.41 0.827,9.65 0.851,9.93 0.851,9.93 0.827,10 0.827,10.1 0.767,10.3 0.708,10.5 0.588,10.9 0.564,11.1 0.564,11.2 0.564,11.5 0.612,11.6 0.875,11.8 1.15,12.2 1.18,12.3 1.15,12.6 1.1,13.2 1.18,13.6 1.39,13.7 1.49,13.7 1.6,13.9 1.75,14 1.93,14 2.05,14.1 2.28,14.4 2.48,14.5 2.55,14.5 2.64,14.5 2.73,14.7 2.73,14.7 2.87,14.7 3.04,14.7 3.04,14.7 3.47,14.5 4.03,14 4.19,13.9 4.44,13.9 4.55,13.7 4.6,13.7 4.68,13.7 4.88,13.6 5.11,13 5.35,11.9 5.39,11.8 5.43,11.6 5.47,11.5 5.65,10.9 5.89,10.4 6.12,10 6.24,9.83 6.48,9.59 6.49,9.59 6.49,9.59 6.49,9.75 6.48,10.1 6.44,10.3 6.43,10.5 6.28,11.1 6.12,11.5 6.08,11.7 6.05,12 6.03,12.4 6.11,12.6 6.21,12.9 6.32,13 6.43,13 6.57,13 6.71,12.9 6.81,12.6 6.91,12.4 7.07,12 7.11,11.7 7.11,11 7.11,10.7 7.13,10.4 7.27,10 7.35,9.75 7.32,9.75 7.37,9.83 7.52,10.1 7.64,10.7 7.69,11.3 7.72,11.6 7.72,12.2 7.69,12.4 7.64,13 7.61,13.6 7.68,14 7.72,14.4 7.76,14.5 8.01,14.5 8.15,14.7 8.31,14.8 8.67,15.5 8.92,15.9 9.03,15.9 9.11,16 9.17,16 9.17,16 9.33,16 9.52,16 9.52,16 9.77,15.9 10.1,15.7 10.1,15.7 10.4,15.7 10.7,15.7 10.7,15.7 10.7,15.7 11,15.6 11.1,15.5 11.4,14.9 11.4,14.9 11.4,14.8 11.5,14.7 11.5,14.5 11.6,14.4 11.7,14.1 12.2,13.9 12.2,13.9 12.2,13.7 12.4,13.6 12.4,13.6 12.4,13 12.4,12.6 12.5,12.4 12.5,12.3 12.5,12.3 12.5,12.2 12.7,12 12.8,11.7 12.8,11.6 12.8,11.5 12.8,11.2 12.8,11.1 12.8,11.1 12.7,10.7 12.4,10.3 12,9.93 11.9,9.83 11.9,9.83 11.9,9.83 12.2,9.93 12.7,9.93 12.9,9.93 13.7,9.75 14,9.08 14.1,7.84 14.1,7.76 14.3,7.59 14.3,7.49 14.3,7.27 14.3,7.19 14.5,7.01 14.5,6.83 14.8,6.6 15.1,6.19 15.7,5.24 15.9,4.85 16,4.41 16,4.31 16,4.25 16,4.03 16,3.84 16,3.76 16,3.69 15.7,3.03 15.2,2.68 14.3,2.75 13.7,2.76 13,2.97 12.2,3.32 12,3.4 11.6,3.63 11.4,3.81 10.2,4.49 9.11,5.55 8.08,6.85 7.95,7 7.85,7.11 7.85,7.11 7.85,7.11 7.84,7.05 7.84,7.04 7.76,6.93 7.72,6.87 7.61,6.81 L 7.53,6.73 7.64,6.47 C 7.92,5.92 8.13,5.51 8.31,5.23 8.35,5.12 8.51,4.91 8.76,4.63 9.43,3.73 9.68,3.47 9.77,3.35 10,3.21 10,3.2 10,3.15 10.1,3.05 10,2.92 10,2.87 9.95,2.87 9.95,2.91 9.84,2.97 9.84,3.11 9.52,3.44 9.11,4.03 8.92,4.19 8.76,4.37 8.76,4.45 8.35,4.95 7.95,5.69 7.61,6.47 7.53,6.6 7.49,6.73 7.47,6.73 7.47,6.73 7.43,6.71 7.37,6.71 L 7.28,6.68 V 6.59 C 7.2,5.69 7.08,5.08 6.91,4.49 6.83,4.16 6.8,4 6.59,3.44 6.33,2.75 6.2,2.32 6.2,2.21 6.17,2.09 6.05,2.08 6,2.2 5.97,2.31 5.99,2.39 6.11,2.55 6.21,2.75 6.32,2.97 6.73,4.15 7,4.96 7.07,5.13 7.2,6.29 7.23,6.68 7.23,6.68 7.2,6.68 7.13,6.68 6.97,6.75 6.89,6.83 6.83,6.87 6.81,6.89 6.81,6.89 6.81,6.89 6.75,6.73 6.68,6.56 6.53,6.07 6.4,5.77 6.2,5.27 5.36,3.43 4.39,2.01 3.36,1.13 2.64,0.571 2.01,0.14 1.49,0 1.32,0 1.02,0 0.887,0 Z' style='fill:rgb(0, 140, 255);stroke:black;stroke-width:0.6;stroke-linejoin:round;stroke-linecap:round;'/></g></svg>",
    "about:config": "chrome://global/skin/icons/settings.svg",
    "about:user-chrome-files": "chrome://global/skin/icons/settings.svg",
};

class LinkWinActorChild extends JSWindowActorChild {
    actorCreated() {
        var docURI = this.document.documentURIObject, host;
        if (!/^(?:about|chrome|resource)$/.test(docURI.scheme))
            try {
                host = docURI.displayHost;
            } catch (e) {
                host = docURI.specIgnoringRef;
            }
        else
            host = docURI.specIgnoringRef;
        var icon = ICONS[host];
        if (!icon) {
            this.handleEvent = e => {};
            return;
        }
        this._icon = icon;
        this.onHeadParsed(this.document.head);
    }
    onHeadParsed(target) {
        for (let link of target.querySelectorAll(LINK_SELECTOR))
            link.remove();
        var link = this.document.createElement("link");
        link.setAttribute("rel", "icon");
        link.setAttribute("sizes", "any");
        link.setAttribute("href", this._icon);
        target.append(link);
    }
    onLinkEvent(link) {
        if (!link.matches(LINK_SELECTOR) || link.href == this._icon) return;
        link.href = this._icon;
        link.setAttribute("rel", "icon");
        link.setAttribute("sizes", "any");
        if (link.hasAttribute("type"))
            link.removeAttribute("type");
    }
    handleEvent(e) {
        switch (e.type) {
            case "DOMLinkAdded":
            case "DOMLinkChanged":
                this.onLinkEvent(e.target);
                break;
            case "pageshow":
                this.onHeadParsed(e.target.head);
                break;
        }
    }
}


sandro79 пишет

Я использую этот скрипт для отображения фавикона в строке адреса

Этот скрипт ужасен, что вы в нем нашли?

Vitaliy V. пишет

Ладно попробую но только потому что в Greasemonkey не сработают некоторые события.
Размеры например sizes="32x32" не учитываются, для всех одна иконка

Виталий, ну Вы Мастер!!! Я думал там попроще будет, как с иконкой поисковика примерно.
Огромное Вам Спасибо! Все с ходу заработало! Прописал остальные адреса, всё отлично!
Правда была проблема когда адреса добавлял, запятые забыл проставить и никак иконки не появлялись, но взял себя в руки и решил эту задачу.

скрытый текст
______2.PNG

Этот скрипт ужасен, что вы в нем нашли?

Да, я вижу его недостатки, скорее всего поверхностно, но плюс в  том, что он мне подходит тем, что на месте скрытой стилем лупы в строке адреса, он ставит значок [firefox] на странице about:newtab, также расположение фавикона мне привычней (ну этим можно и пожертвовать), с тех пор как пользуюсь этим скриптом, с 60 какой-то версии. И плюс ещё, что фавикон в строке адреса при навигации в пределах страницы, обновлении страницы - всегда отображается и не исчезает. Если бы в Вашем скрипте фавикон не менялся на identity-icon пока находишься на странице - обновляешь, по ссылкам ходишь, я бы его использовал конечно, но постоянное обновление фавикона, ну мне не подходит. Я вижу недостатки в используемом мной скрипте, насколько фавикон жёстко привязан к высоте адресной строки, чуть размер строки другой, растягивается или сжимается, подбирать вручную нужно правкой скрипта. Но я настроил под свой режим и пойдёт пока.

Vitaliy V.
Разреши попросить прояснить насчёт
событий "DOMHeadElementParsed" и "pageshow".


Если я правильно понимаю, то лисий LinkHandlerChild
слушает их на предмет того, что если соответствующий <link>
не нарисовался, то запросить дефолтный http://сайт/favicon.ico,
и для предотвращения сохранения фавиконок,
изменённых js-манипуляциями скриптами сайта.


А LinkWinActorChild зачем их слушает?

Dumby
DOMLinkAdded срабатывает не всегда при использовании matches: [ // адреса где работает скрипт ...
Это видно с яндексом если перейти со страницы поиска на главную нажав логотип Яндекс в слева от поиска
А DOMHeadElementParsed да возможно лишний, вариант скрипта не окончательный, есть ещё проблемка с ним

sandro79 пишет

Если бы в Вашем скрипте фавикон не менялся на identity-icon пока находишься на странице - обновляешь, по ссылкам ходишь, я бы его использовал конечно, но постоянное обновление фавикона, ну мне не подходит

Это не постоянное обновление, если говорить об этом то ваш скрипт обновляет её намного чаще, когда это и не нужно.
Это скрытие иконки сделано чтобы при переходе например на другой сайт она не отображалась некоторое время с адресом от другого сайта ,
и вообще то это можно изменить удалив например css селектор , #identity-faviconinurlbar[favbusy="true"]

sandro79 пишет

что на месте скрытой стилем лупы в строке адреса, он ставит значок [firefox] на странице about:newtab

что требуется удалить для этого
#identity-faviconinurlbar[faviconchrome="true"],
,.localResource
, #urlbar:not(.searchButton) #identity-box[pageproxystate="invalid"] #identity-faviconinurlbar
или добавить в конце css кода

Выделить код

Код:

#urlbar:not(.searchButton) #identity-box[pageproxystate="invalid"].localResource #identity-faviconinurlbar[faviconchrome="true"] {
                        display: -moz-inline-box !important;
                    }
Vitaliy V. пишет

DOMLinkAdded срабатывает не всегда при использовании matches

О как! Принято, тогда действительно стоит прогонять ещё раз, на всякий случай.
Я-то вообще был бы склонен к переопределению LinkHandlerParent.prototype.setIconFromLink

sandro79 пишет

но плюс в  том, что он мне подходит тем, что на месте скрытой стилем лупы в строке адреса, он ставит значок [firefox] на странице about:newtab,

Можно не скрывая подставить другую иконку, например так:

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

Выделить код

Код:

#urlbar:not(.searchButton) > #urlbar-input-container > #identity-box[pageproxystate="invalid"] #identity-icon {
    list-style-image: url("chrome://branding/content/about-logo.svg") !important;
}

Vitaliy V. пишет

и вообще то это можно изменить удалив например css селектор , #identity-faviconinurlbar[favbusy="true"]

Да, это сработало.

что требуется удалить для этого

#identity-faviconinurlbar[faviconchrome="true"],

А это не сработало. Добавление в конце css кода тоже нет. Да я может не совсем правильно поставил задачу, не знаю даже. Скрины выложу. Вот на about:newtab у него как и на служебных страницах тоже добавляются значки, может можно так сделать, а так теперь фавикон не мелькает

скрытый текст
______.PNG______2.PNG
Сам скрипт, что я правил, может где ошибся
скрытый текст

Выделить код

Код:

(this.faviconinurlbar = {
            init(that) {
                var identity = document.querySelector("#identity-icon");
                if (!identity)
                    return;
                var iconDefault = "chrome://branding/content/identity-icons-brand.svg"; // или свою иконку
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #identity-faviconinurlbar {
                        --v-faviconinurlbar-default: url("${iconDefault}");

                        list-style-image: var(--v-faviconinurlbar, none) !important;
                        pointer-events: none !important;
                        height: 16px !important;
                        width: auto !important;
                        margin-inline-start: 4px !important;
                        -moz-context-properties: fill, fill-opacity;
                        fill: currentColor;
                        fill-opacity: var(--urlbar-icon-fill-opacity, 1);
                    }
                    #identity-faviconinurlbar:not([faviconinurlbar="true"]) {
                        --v-faviconinurlbar: var(--v-faviconinurlbar-default) !important;
                    }
                    #urlbar[actiontype="extension"] #identity-faviconinurlbar,
                    #identity-box:is(.extensionPage,.chromeUI,.localResource) #identity-faviconinurlbar {
                        display: none !important;
                    }
                `);
                windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
                var faviconinurlbar = document.createXULElement("image");
                faviconinurlbar.id = "identity-faviconinurlbar";
                identity.after(faviconinurlbar);
                gBrowser.tabContainer.addEventListener("TabAttrModified", this);
                gBrowser.addProgressListener(this);
                that.unloadlisteners.push("faviconinurlbar");
                var {STATE_START, STATE_STOP, STATE_IS_NETWORK} = Ci.nsIWebProgressListener;
                var updatefavicon = image => {
                    if (image) {
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", `url("${image}")`);
                        faviconinurlbar.setAttribute("faviconinurlbar", "true");
                        faviconinurlbar.setAttribute("faviconchrome", `${image.startsWith("chrome:")}`);
                    } else {
                        faviconinurlbar.setAttribute("faviconinurlbar", "false");
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", "");
                    }
                };
                this.handleEvent = e => {
                    var tab = e.target, changed;
                    if (!tab.selected || !((changed = e.detail.changed).includes("image") || changed.includes("selected"))) return;
                    updatefavicon(tab.image);
                };
                this.onStateChange = (aWebProgress, aRequest, aStateFlags, aStatus) => {
                    if (aStateFlags & STATE_IS_NETWORK && aWebProgress?.isTopLevel) {
                        if (aStateFlags & STATE_START)
                            faviconinurlbar.setAttribute("favbusy", "true");
                        else if (aStateFlags & STATE_STOP) {
                            faviconinurlbar.setAttribute("favbusy", "false");
                            updatefavicon(gBrowser.selectedTab.image);
                        }
                    }
                };
            },
            destructor() {
                gBrowser.tabContainer.removeEventListener("TabAttrModified", this);
                gBrowser.removeProgressListener(this);
            }
        }).init(this);

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

kokoss пишет

Можно не скрывая подставить другую иконку, например так:

Да, спасибо. Я знаю как подобные иконки менять :D Но как вариант да, сойдет.

sandro79 пишет

Да, спасибо. Я знаю как подобные иконки менять

Я и не сомневался:D, но может кому нибудь ещё пригодится :)

sandro79 пишет

может где ошибся

я ещё писал про ,.localResource
но как я понял вообще скрывать нигде не нужно тогда удалите целиком правило относящееся к display: none !important;

Vitaliy V. пишет

я ещё писал про ,.localResource

Вот же ж, всё-таки недоглядел я, запутался.

но как я понял вообще скрывать нигде не нужно тогда удалите целиком правило относящееся к display: none !important;

Да, я неправильно сформулировал задачу, не упомянув о служебных страницах, извиняюсь.
Да, и это теперь сработало на служебных страницах. Но чтоб сработало на about:newtab, нужно удалить из кода правило, скрывающее лупу

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

Выделить код

Код:

#urlbar:not(.searchButton) > #urlbar-input-container > #identity-box[pageproxystate="invalid"] > #identity-icon-box {
  display: none !important;
}

И получается так. А если скрыть лупу, то лого [firefox] тоже пропадает. Если использовать этот код появляется два лого [firefox].
Ну я понял, тут дело в том как, к чему привязан что ли вафикон в разных скриптах, технически не знаю как сформулировать. Ну тут похоже надо больше переделывать.

kokoss пишет

но может кому нибудь ещё пригодится

Конечно, любой рабочий код будет полезен.

sandro79 пишет

А если скрыть лупу, то лого [firefox] тоже пропадает ... Ну я понял, тут дело в том как, к чему привязан что ли вафикон в разных скриптах

Нет дело не в этом, просто нужно скрывать #identity-icon а не контейнер

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

Выделить код

Код:

#urlbar:not(.searchButton) > #urlbar-input-container > #identity-box[pageproxystate="invalid"] #identity-icon {
  display: none !important;
}

Vitaliy V. пишет

Нет дело не в этом, просто нужно скрывать #identity-icon а не контейнер

Огромное Вам спасибо! Теперь всё отлично!

скрытый текст
______.PNG______2.PNG______3.PNG
Оставил скрипт пока в [nightly], на ней привыкать буду некоторое время к расположению фавикона сайта. У Ариса он перед замком, у Вас после него.
Здесь оставлю коды скрипта и стиля, как резерв
скрытый текст

Выделить код

Код:

(this.faviconinurlbar = {
            init(that) {
                var identity = document.querySelector("#identity-icon");
                if (!identity)
                    return;
                var iconDefault = "chrome://global/skin/icons/info.svg"; // или свою иконку
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #identity-faviconinurlbar {
                        --v-faviconinurlbar-default: url("${iconDefault}");

                        list-style-image: var(--v-faviconinurlbar, none) !important;
                        pointer-events: none !important;
                        height: 16px !important;
                        width: auto !important;
                        margin-inline-start: 4px !important;
                        -moz-context-properties: fill, fill-opacity;
                        fill: currentColor;
                        fill-opacity: var(--urlbar-icon-fill-opacity, 1);
                    }
                    #identity-faviconinurlbar:not([faviconinurlbar="true"]) {
                        --v-faviconinurlbar: var(--v-faviconinurlbar-default) !important;
                    }
                `);
                windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
                var faviconinurlbar = document.createXULElement("image");
                faviconinurlbar.id = "identity-faviconinurlbar";
                identity.after(faviconinurlbar);
                gBrowser.tabContainer.addEventListener("TabAttrModified", this);
                gBrowser.addProgressListener(this);
                that.unloadlisteners.push("faviconinurlbar");
                var {STATE_START, STATE_STOP, STATE_IS_NETWORK} = Ci.nsIWebProgressListener;
                var updatefavicon = image => {
                    if (image) {
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", `url("${image}")`);
                        faviconinurlbar.setAttribute("faviconinurlbar", "true");
                        faviconinurlbar.setAttribute("faviconchrome", `${image.startsWith("chrome:")}`);
                    } else {
                        faviconinurlbar.setAttribute("faviconinurlbar", "false");
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", "");
                    }
                };
                this.handleEvent = e => {
                    var tab = e.target, changed;
                    if (!tab.selected || !((changed = e.detail.changed).includes("image") || changed.includes("selected"))) return;
                    updatefavicon(tab.image);
                };
                this.onStateChange = (aWebProgress, aRequest, aStateFlags, aStatus) => {
                    if (aStateFlags & STATE_IS_NETWORK && aWebProgress?.isTopLevel) {
                        if (aStateFlags & STATE_START)
                            faviconinurlbar.setAttribute("favbusy", "true");
                        else if (aStateFlags & STATE_STOP) {
                            faviconinurlbar.setAttribute("favbusy", "false");
                            updatefavicon(gBrowser.selectedTab.image);
                        }
                    }
                };
            },
            destructor() {
                gBrowser.tabContainer.removeEventListener("TabAttrModified", this);
                gBrowser.removeProgressListener(this);
            }
        }).init(this);

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

Выделить код

Код:

#urlbar:not(.searchButton) > #urlbar-input-container > #identity-box[pageproxystate="invalid"] #identity-icon {
  display: none !important;
}

sandro79 пишет

У Ариса он перед замком, у Вас после него

Не думаете же вы что это сложно изменить, вот даже стилем можно
заменить
margin-inline-start: 4px !important;
на
-moz-box-ordinal-group: 0 !important;
margin-inline: 0 4px !important;


ну или вместо -moz-box-ordinal-group: 0 !important;
//identity.after(faviconinurlbar);
identity.before(faviconinurlbar);

Vitaliy V. пишет

Не думаете же вы что это сложно изменить, вот даже стилем можно

Да, я собирался вообще попробовать, но что-то подумал, что там жёстко у Вас фавикон привязан, и вообще решил не трогать, оставить оригинальное расположение.
Да, сработало стилем. Спасибо за подсказку! Теперь вообще супер!

ну или вместо -moz-box-ordinal-group: 0 !important; //identity.after(faviconinurlbar); identity.before(faviconinurlbar);

А так вообще великолепно! Спасибо! Только замок от фавикона чуть отодвину стилем, это я уже сам.

скрытый текст
______.PNG

Выделить код

Код:

#identity-icon {
    margin-inline-start: 4px !important;
}

Vitaliy V.
Ну всё, настроил я всё окончательно, теперь визуально один в один с Ариса скриптом.
В скрипте ещё для селектора #identity-faviconinurlbar сменил margin-inline-start: 2px !important;
Ниже скрины - верхняя панель 91 с Ариса скриптом, нижняя 92 [nightly] с этим

скрытый текст
Image_001.png
Теперь можно переходить на этот вариант окончательно. Ещё раз Большое Спасибо :beer:


Окончательный вариант скрипта, добавил в скрипт css-коды с предыдущих сообщений

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

Выделить код

Код:

(this.faviconinurlbar = {
            init(that) {
                var identity = document.querySelector("#identity-icon");
                if (!identity)
                    return;
                var iconDefault = "chrome://global/skin/icons/defaultFavicon.svg"; // или свою иконку
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #identity-faviconinurlbar {
                        --v-faviconinurlbar-default: url("${iconDefault}");

                        list-style-image: var(--v-faviconinurlbar, none) !important;
                        pointer-events: none !important;
                        height: 16px !important;
                        width: 16px !important;
                        margin-inline-start: 3px !important;
                        -moz-context-properties: fill, fill-opacity;
                        fill: currentColor;
                        fill-opacity: var(--urlbar-icon-fill-opacity, 1);
                    }
                    #identity-faviconinurlbar:not([faviconinurlbar="true"]) {
                        --v-faviconinurlbar: var(--v-faviconinurlbar-default) !important;
                    }
                    #identity-icon {
                        margin-inline-start: 4px !important;
                    }
                    .identity-box-button {
                        padding-inline: 2px !important;
                    }         
                    #urlbar[actiontype="extension"] #identity-faviconinurlbar,
                    #identity-box:is(.extensionPage,.chromeUI,.unknownIdentity:not(.mixedDisplayContent,.mixedDisplayContentLoadedActiveBlocked)) #identity-faviconinurlbar {
                        display: none !important;
                    }
                    #urlbar:not(.searchButton) > #urlbar-input-container > #identity-box[pageproxystate="invalid"] #identity-icon {
                        display: none !important;
                    }
                `);
                windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
                var faviconinurlbar = document.createXULElement("image");
                faviconinurlbar.id = "identity-faviconinurlbar";
                identity.before(faviconinurlbar);
                gBrowser.tabContainer.addEventListener("TabAttrModified", this);
                gBrowser.addProgressListener(this);
                that.unloadlisteners.push("faviconinurlbar");
                var {STATE_START, STATE_STOP, STATE_IS_NETWORK} = Ci.nsIWebProgressListener;
                var updatefavicon = image => {
                    if (image) {
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", `url("${image}")`);
                        faviconinurlbar.setAttribute("faviconinurlbar", "true");
                        faviconinurlbar.setAttribute("faviconchrome", `${image.startsWith("chrome:")}`);
                    } else {
                        faviconinurlbar.setAttribute("faviconinurlbar", "false");
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", "");
                    }
                };
                this.handleEvent = e => {
                    var tab = e.target, changed;
                    if (!tab.selected || !((changed = e.detail.changed).includes("image") || changed.includes("selected"))) return;
                    updatefavicon(tab.image);
                };
                this.onStateChange = (aWebProgress, aRequest, aStateFlags, aStatus) => {
                    if (aStateFlags & STATE_IS_NETWORK && aWebProgress?.isTopLevel) {
                        if (aStateFlags & STATE_START)
                            faviconinurlbar.setAttribute("favbusy", "true");
                        else if (aStateFlags & STATE_STOP) {
                            faviconinurlbar.setAttribute("favbusy", "false");
                            updatefavicon(gBrowser.selectedTab.image);
                        }
                    }
                };
            },
            destructor() {
                gBrowser.tabContainer.removeEventListener("TabAttrModified", this);
                gBrowser.removeProgressListener(this);
            }
        }).init(this);

скрытый текст
______.PNG

Обновил Замена фавиконок для сайтов
Добавил замену на служебных страницах и там где вообще отсутствует иконка, кстати DOMHeadElementParsed нужен для этого


P.S. Насчет иконок, в идеале лучше использовать svg, если др. форматов то размером от 32x32 px и более

Vitaliy V. пишет

кстати DOMHeadElementParsed нужен для этого

Но в LinkWinActorChild.jsm его нет.
UPD: А, понял, по нему может сам actor создаваться.

Vitaliy V. пишет

Обновил Замена фавиконок для сайтов
Добавил замену на служебных страницах и там где вообще отсутствует иконка, кстати DOMHeadElementParsed нужен для этого


P.S. Насчет иконок, в идеале лучше использовать svg, если др. форматов то размером от 32x32 px и более

Огромное Спасибо за обнову!!! Обновился, всё везде сработало, на закладках иконки тоже обновились, и в топе сайтов.
В about:config и about:user-chrome-files тоже иконка настроек появилась. Всё отлично!

скрытый текст
______.png
Image_001.png

Обновил Добавить кнопку Пауза/Продолжить в загрузки https://forum.mozilla-russia.org/viewto … 50#p776150

Vitaliy V. пишет

Обновил Добавить кнопку Пауза/Продолжить в загрузки https://forum.mozilla-russia.org/viewto … 50#p776150

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

скрытый текст
Image_002.png
Только у меню кнопки круглые, правила из твиков для каждого окна:
скрытый текст

Выделить код

Код:

@-moz-document url("chrome://browser/content/places/places.xhtml") {
.downloadButton > .button-box {
	border-radius: 16px !important;
}
}
Выделить код

Код:

@-moz-document url(chrome://browser/content/downloads/contentAreaDownloadsView.xhtml) {
.downloadButton > .button-box {
	border-radius: 16px !important;
}
}

Блин, можно же добавить перед последней скобкой в стиль правило(без адреса) из кода выше и кнопки везде будут круглыми.

Не знаю, может кому пригодится.
Разбирался сегодня с конвертацией иконок в .svg формат, для подобных скриптов и стилей. С векторным редактором Inkscape не смог разобраться.
Не сразу удалось найти нормальный онлайн-конвертер, попадались все конвертирующие в base64 что ли в обвёртке svg, но не работали в упомянутых кодах, добавлял data:image/svg+xml;utf8, перед третьей строкой, строки 1 и 2 удалял, двойные кавычки менял на одинарные - ошибка синтаксиса и не работает

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

Выделить код

Код:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">  <image id="image0" width="16" height="16" x="0" y="0"
    xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABiVBMVEUAAAAAAQoAF6wAChMA
AREAwN0AAAAAAQoAZXYADC0AEagAUtkAAAAAAAAALnAABCoATcgBDGoBAwUVXdkAKLAAABcAHn4A
U+kABAkAAAAAChwACTsAH3EAfY8AQHYAC0EAN7cEAQIAABcABjQAAEQAAlYANY8AoPMARqIAABMA
F2EAG3gKGVUAu/4AUZAACA4ACBAAHH0ABiAAAAAAAAB/9vswXFYAjY4AgagAAg4AAAEAicsPW1oA
AAAAAgQAlOwASnYAhM8As/gAXbcBQowAAgkAAAAAJ0IABRQAAQIAN+MABHoAfewAh/QAH9IAovUA
Tf4ALtoAwPsAjvcN8/8A+/8cms8+n8wO2v8AIcsAOOMgvP8O/v8BUdEABHQAH84Akf8Ahf8AH+QA
rvcCqvoA3P8By/8AVn8FAzkAGZ8AEJMAJewAP9EAffYAk/oAoP0Auv8AnNsAMJ0Ao/8BqvgLU+EA
uf8A7P8AQaMCVdsA+P8o/f8A/P8U0f5V+v8A8P8Aqf0Aof/////gm21KAAAASnRSTlMASfMnMfQI
Krpm+v0GAZdk8qlA/fJM1/wUDkGDrMSrZuXybnfO+OX+vETNxer+vRhC334UF/6Tq8EwOPyXAxL9
lfD76bYaGl0yB0c23wgAAAABYktHRIKLs/9EAAAAB3RJTUUH5QcYFgUmGgEtiwAAAK5JREFUGNNj
YGBkYmZAASxerGwoAuzePhwoApxcvtw8SHxePj//AH5kAYHAoGBBZC1CwiGhIkh8UbGw8AhxCQZe
SSlpGVk5eQaFyKjomFhFJWUV1bj4hMQkBrXklNS09IzMrOyc3Dx1DU0GrfyCQm2douKS0rJyXT19
BgaDCkMjY5PKqmpTM3MLS5CpxkBsZV1TW2dji3CfnX19Q6MDkt22jk7OLq7IrnNz9/CEsACxmSFY
BCmxVAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMS0wNy0yNFQyMjowNTozOCswMzowMJlJCfEAAAAl
dEVYdGRhdGU6bW9kaWZ5ADIwMjEtMDctMjRUMjI6MDU6MzgrMDM6MDDoFLFNAAAAAElFTkSuQmCC" />
</svg>

Ресурс нашёл, ссылка, можно выбрать качество, выше качество - больше размер кода/файла. Размер и качество конечно уступают base64 и другим форматам.
Сконвертировал бабочку из оригинальной .ico-иконки. Ещё, как я понял в ходе экспериментов, чтоб иконка отработала в упомянутых в ссылках выше кодах, нужно заменить все двойные кавычки на одинарные. Получился готовый для использования код с максимальным качеством(Detailed)
14,6 КБ кода

Выделить код

Код:

data:image/svg+xml;utf8,<svg width='16' height='16' version='1.1' xmlns='http://www.w3.org/2000/svg'><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0' d='M 0.5 0 L 1 3.5 L 0 3.5 L 0.5 0 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0' d='M 4.5 0 L 16 0 L 16 7 L 8.5 7 L 7.5 8 L 6 6.5 L 6 3.5 L 4 0.5 L 4.5 0 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0' d='M 0.5 6 L 0.5 7 L 0.5 6 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0' d='M 15.5 10 L 16 16 L 10 15.5 L 11 13 Q 14.25 13.5 15.5 10 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0' d='M 0.5 12 Q 1.5 14 2.5 12 Q 6.25 10.75 5 14.5 L 5.5 16 L 0 16 L 0.5 12 Z '></path><path fill='rgb(0,6,17)' stroke='rgb(0,6,17)' stroke-width='1' opacity='0.17647058823529413' d='M 3.5 0 L 3.5 1 L 3.5 0 Z '></path><path fill='rgb(0,6,17)' stroke='rgb(0,6,17)' stroke-width='1' opacity='0.17647058823529413' d='M 1.5 1 L 2 2.5 L 1 2.5 L 1.5 1 Z '></path><path fill='rgb(0,6,17)' stroke='rgb(0,6,17)' stroke-width='1' opacity='0.17647058823529413' d='M 13.5 11 L 13.5 12 L 13.5 11 Z '></path><path fill='rgb(0,6,17)' stroke='rgb(0,6,17)' stroke-width='1' opacity='0.17647058823529413' d='M 8.5 15 L 8.5 16 L 8.5 15 Z '></path><path fill='rgb(0,131,215)' stroke='rgb(0,131,215)' stroke-width='1' opacity='0.9764705882352941' d='M 6.5 12 L 6.5 13 L 6.5 12 Z '></path><path fill='rgb(0,131,215)' stroke='rgb(0,131,215)' stroke-width='1' opacity='0.9764705882352941' d='M 6.5 14 L 6.5 15 L 6.5 14 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.00392156862745098' d='M 0.5 4 L 1 5.5 L 0 5.5 L 0.5 4 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.00392156862745098' d='M 8.5 7 L 8.5 8 L 8.5 7 Z '></path><path fill='rgb(0,51,115)' stroke='rgb(0,51,115)' stroke-width='1' opacity='0.615686274509804' d='M 1.5 4 L 1.5 5 L 1.5 4 Z '></path><path fill='rgb(0,51,115)' stroke='rgb(0,51,115)' stroke-width='1' opacity='0.615686274509804' d='M 12.5 7 L 12.5 8 L 12.5 7 Z '></path><path fill='rgb(0,51,115)' stroke='rgb(0,51,115)' stroke-width='1' opacity='0.615686274509804' d='M 14.5 7 L 14.5 8 L 14.5 7 Z '></path><path fill='rgb(0,51,115)' stroke='rgb(0,51,115)' stroke-width='1' opacity='0.615686274509804' d='M 10.5 13 L 10.5 14 L 10.5 13 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 3.5 4 L 3.5 5 L 3.5 4 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 13.5 9 L 13.5 10 L 13.5 9 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 1.5 10 L 1.5 11 L 1.5 10 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 3.5 10 L 3.5 11 L 3.5 10 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 6.5 13 L 6.5 14 L 6.5 13 Z '></path><path fill='rgb(0,159,243)' stroke='rgb(0,159,243)' stroke-width='1' opacity='0.996078431372549' d='M 9.5 13 L 9.5 14 L 9.5 13 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 1.5 3 L 1.5 4 L 1.5 3 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 5.5 4 L 5.5 5 L 5.5 4 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 11.5 7 L 11.5 8 L 11.5 7 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 15.5 7 L 15.5 8 L 15.5 7 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 8.5 8 L 8.5 9 L 8.5 8 Z '></path><path fill='rgb(0,14,55)' stroke='rgb(0,14,55)' stroke-width='1' opacity='0.4196078431372549' d='M 7.5 15 L 7.5 16 L 7.5 15 Z '></path><path fill='rgb(1,2,7)' stroke='rgb(1,2,7)' stroke-width='1' opacity='0.25098039215686274' d='M 1.5 0 L 1.5 1 L 1.5 0 Z '></path><path fill='rgb(1,2,7)' stroke='rgb(1,2,7)' stroke-width='1' opacity='0.25098039215686274' d='M 1.5 6 L 1.5 7 L 1.5 6 Z '></path><path fill='rgb(1,2,7)' stroke='rgb(1,2,7)' stroke-width='1' opacity='0.25098039215686274' d='M 5.5 12 L 5.5 13 L 5.5 12 Z '></path><path fill='rgb(23,250,255)' stroke='rgb(23,250,255)' stroke-width='1' opacity='1' d='M 4.5 8 L 4.5 9 L 4.5 8 Z '></path><path fill='rgb(23,250,255)' stroke='rgb(23,250,255)' stroke-width='1' opacity='1' d='M 8.5 11 L 8.5 12 L 8.5 11 Z '></path><path fill='rgb(45,156,205)' stroke='rgb(45,156,205)' stroke-width='1' opacity='1' d='M 2.5 7 L 4 7.5 L 2.5 8 L 2.5 7 Z '></path><path fill='rgb(4,82,221)' stroke='rgb(4,82,221)' stroke-width='1' opacity='0.9882352941176471' d='M 4.5 3 L 5 4.5 L 4 4.5 L 4.5 3 Z '></path><path fill='rgb(4,82,221)' stroke='rgb(4,82,221)' stroke-width='1' opacity='0.9882352941176471' d='M 1.5 5 Q 4 6 2.5 7 Q 0 6 1.5 5 Z '></path><path fill='rgb(4,82,221)' stroke='rgb(4,82,221)' stroke-width='1' opacity='0.9882352941176471' d='M 5.5 7 L 6 8.5 L 5 8.5 L 5.5 7 Z '></path><path fill='rgb(4,82,221)' stroke='rgb(4,82,221)' stroke-width='1' opacity='0.9882352941176471' d='M 9.5 10 L 9.5 11 L 9.5 10 Z '></path><path fill='rgb(4,82,221)' stroke='rgb(4,82,221)' stroke-width='1' opacity='0.9882352941176471' d='M 6.5 11 L 6.5 12 L 6.5 11 Z '></path><path fill='rgb(0,11,25)' stroke='rgb(0,11,25)' stroke-width='1' opacity='0.2549019607843137' d='M 10.5 7 L 10.5 8 L 10.5 7 Z '></path><path fill='rgb(0,11,25)' stroke='rgb(0,11,25)' stroke-width='1' opacity='0.2549019607843137' d='M 0.5 11 L 0.5 12 L 0.5 11 Z '></path><path fill='rgb(0,15,98)' stroke='rgb(0,15,98)' stroke-width='1' opacity='0.7607843137254902' d='M 5.5 5 L 5.5 6 L 5.5 5 Z '></path><path fill='rgb(0,15,98)' stroke='rgb(0,15,98)' stroke-width='1' opacity='0.7607843137254902' d='M 9.5 8 L 9.5 9 L 9.5 8 Z '></path><path fill='rgb(0,15,98)' stroke='rgb(0,15,98)' stroke-width='1' opacity='0.7607843137254902' d='M 0.5 10 L 0.5 11 L 0.5 10 Z '></path><path fill='rgb(0,15,98)' stroke='rgb(0,15,98)' stroke-width='1' opacity='0.7607843137254902' d='M 4.5 10 L 4.5 11 L 4.5 10 Z '></path><path fill='rgb(0,87,137)' stroke='rgb(0,87,137)' stroke-width='1' opacity='0.7098039215686275' d='M 4.5 2 L 4.5 3 L 4.5 2 Z '></path><path fill='rgb(0,87,137)' stroke='rgb(0,87,137)' stroke-width='1' opacity='0.7098039215686275' d='M 14.5 9 Q 16 10 13.5 11 Q 12 10 14.5 9 Z '></path><path fill='rgb(0,87,137)' stroke='rgb(0,87,137)' stroke-width='1' opacity='0.7098039215686275' d='M 11.5 11 L 11.5 12 L 11.5 11 Z '></path><path fill='rgb(0,87,137)' stroke='rgb(0,87,137)' stroke-width='1' opacity='0.7098039215686275' d='M 9.5 14 L 9.5 15 L 9.5 14 Z '></path><path fill='rgb(0,47,180)' stroke='rgb(0,47,180)' stroke-width='1' opacity='0.9215686274509803' d='M 5.5 6 L 5.5 7 L 5.5 6 Z '></path><path fill='rgb(0,47,180)' stroke='rgb(0,47,180)' stroke-width='1' opacity='0.9215686274509803' d='M 0.5 8 L 0.5 9 L 0.5 8 Z '></path><path fill='rgb(0,47,180)' stroke='rgb(0,47,180)' stroke-width='1' opacity='0.9215686274509803' d='M 8.5 14 L 8.5 15 L 8.5 14 Z '></path><path fill='rgb(0,37,131)' stroke='rgb(0,37,131)' stroke-width='1' opacity='0.8705882352941177' d='M 1.5 7 L 1.5 8 L 1.5 7 Z '></path><path fill='rgb(0,37,131)' stroke='rgb(0,37,131)' stroke-width='1' opacity='0.8705882352941177' d='M 0.5 9 L 0.5 10 L 0.5 9 Z '></path><path fill='rgb(0,37,131)' stroke='rgb(0,37,131)' stroke-width='1' opacity='0.8705882352941177' d='M 2.5 11 L 2.5 12 L 2.5 11 Z '></path><path fill='rgb(0,170,250)' stroke='rgb(0,170,250)' stroke-width='1' opacity='0.996078431372549' d='M 1.5 9 L 3 9.5 L 1.5 10 L 1.5 9 Z '></path><path fill='rgb(0,170,250)' stroke='rgb(0,170,250)' stroke-width='1' opacity='0.996078431372549' d='M 7.5 10 L 9 10.5 L 7.5 11 L 7.5 10 Z '></path><path fill='rgb(0,170,250)' stroke='rgb(0,170,250)' stroke-width='1' opacity='0.996078431372549' d='M 8.5 13 Q 10 14 7.5 15 Q 6 14 8.5 13 Z '></path><path fill='rgb(5,213,255)' stroke='rgb(5,213,255)' stroke-width='1' opacity='1' d='M 4.5 7 L 4.5 8 L 4.5 7 Z '></path><path fill='rgb(5,213,255)' stroke='rgb(5,213,255)' stroke-width='1' opacity='1' d='M 3.5 9 L 5 9.5 L 3.5 10 L 3.5 9 Z '></path><path fill='rgb(0,86,127)' stroke='rgb(0,86,127)' stroke-width='1' opacity='1' d='M 5.5 9 L 5.5 10 L 5.5 9 Z '></path><path fill='rgb(0,20,161)' stroke='rgb(0,20,161)' stroke-width='1' opacity='0.9803921568627451' d='M 2.5 0 L 2.5 1 L 2.5 0 Z '></path><path fill='rgb(0,20,161)' stroke='rgb(0,20,161)' stroke-width='1' opacity='0.9803921568627451' d='M 2.5 3 L 2.5 4 L 2.5 3 Z '></path><path fill='rgb(0,20,161)' stroke='rgb(0,20,161)' stroke-width='1' opacity='0.9803921568627451' d='M 7.5 9 L 9 9.5 L 7.5 10 L 7.5 9 Z '></path><path fill='rgb(3,6,77)' stroke='rgb(3,6,77)' stroke-width='1' opacity='0.9725490196078431' d='M 2.5 2 L 2.5 3 L 2.5 2 Z '></path><path fill='rgb(3,6,77)' stroke='rgb(3,6,77)' stroke-width='1' opacity='0.9725490196078431' d='M 6.5 8 L 7 9.5 Q 6 12 5 10.5 L 6.5 8 Z '></path><path fill='rgb(3,6,77)' stroke='rgb(3,6,77)' stroke-width='1' opacity='0.9725490196078431' d='M 10.5 8 L 12 8.5 L 10.5 9 L 10.5 8 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 2.5 1 L 2.5 2 L 2.5 1 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 2.5 4 L 3 5.5 L 2 5.5 L 2.5 4 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 1.5 8 L 3 8.5 L 1.5 9 L 1.5 8 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 12.5 8 L 12.5 9 L 12.5 8 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 15.5 8 L 15.5 9 L 15.5 8 Z '></path><path fill='rgb(0,42,218)' stroke='rgb(0,42,218)' stroke-width='1' opacity='1' d='M 9.5 9 L 11 9.5 L 9.5 10 L 9.5 9 Z '></path><path fill='rgb(0,139,247)' stroke='rgb(0,139,247)' stroke-width='1' opacity='0.996078431372549' d='M 3.5 2 L 4 3.5 L 3 3.5 L 3.5 2 Z '></path><path fill='rgb(0,139,247)' stroke='rgb(0,139,247)' stroke-width='1' opacity='0.996078431372549' d='M 4.5 5 L 4.5 6 L 4.5 5 Z '></path><path fill='rgb(0,139,247)' stroke='rgb(0,139,247)' stroke-width='1' opacity='0.996078431372549' d='M 13.5 8 L 15 8.5 L 11.5 10 L 11.5 9 L 13.5 8 Z '></path><path fill='rgb(0,187,253)' stroke='rgb(0,187,253)' stroke-width='1' opacity='0.996078431372549' d='M 3.5 5 L 3.5 6 L 3.5 5 Z '></path><path fill='rgb(0,187,253)' stroke='rgb(0,187,253)' stroke-width='1' opacity='0.996078431372549' d='M 2.5 10 L 2.5 11 L 2.5 10 Z '></path><path fill='rgb(0,187,253)' stroke='rgb(0,187,253)' stroke-width='1' opacity='0.996078431372549' d='M 10.5 10 L 10.5 11 L 10.5 10 Z '></path><path fill='rgb(0,187,253)' stroke='rgb(0,187,253)' stroke-width='1' opacity='0.996078431372549' d='M 12.5 10 L 12.5 11 L 12.5 10 Z '></path><path fill='rgb(0,10,21)' stroke='rgb(0,10,21)' stroke-width='1' opacity='0.09411764705882353' d='M 14.5 10 L 14.5 11 L 14.5 10 Z '></path><path fill='rgb(0,0,22)' stroke='rgb(0,0,22)' stroke-width='1' opacity='0.32941176470588235' d='M 0.5 7 L 0.5 8 L 0.5 7 Z '></path><path fill='rgb(0,0,22)' stroke='rgb(0,0,22)' stroke-width='1' opacity='0.32941176470588235' d='M 7.5 8 L 7.5 9 L 7.5 8 Z '></path><path fill='rgb(0,0,22)' stroke='rgb(0,0,22)' stroke-width='1' opacity='0.32941176470588235' d='M 15.5 9 L 15.5 10 L 15.5 9 Z '></path><path fill='rgb(0,56,160)' stroke='rgb(0,56,160)' stroke-width='1' opacity='1' d='M 6.5 10 L 6.5 11 L 6.5 10 Z '></path><path fill='rgb(0,56,160)' stroke='rgb(0,56,160)' stroke-width='1' opacity='1' d='M 1.5 11 L 1.5 12 L 1.5 11 Z '></path><path fill='rgb(0,6,32)' stroke='rgb(0,6,32)' stroke-width='1' opacity='0.49411764705882355' d='M 3.5 11 L 3.5 12 L 3.5 11 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.08627450980392157' d='M 4.5 11 L 6 11.5 L 4.5 12 L 4.5 11 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.08627450980392157' d='M 1.5 12 L 1.5 13 L 1.5 12 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.08627450980392157' d='M 6.5 15 L 6.5 16 L 6.5 15 Z '></path><path fill='rgb(1,245,255)' stroke='rgb(1,245,255)' stroke-width='1' opacity='1' d='M 3.5 6 L 5 6.5 L 3.5 7 L 3.5 6 Z '></path><path fill='rgb(1,245,255)' stroke='rgb(1,245,255)' stroke-width='1' opacity='1' d='M 11.5 10 L 11.5 11 L 11.5 10 Z '></path><path fill='rgb(1,245,255)' stroke='rgb(1,245,255)' stroke-width='1' opacity='1' d='M 7.5 11 L 8 13.5 L 7 13.5 L 7.5 11 Z '></path><path fill='rgb(106,248,253)' stroke='rgb(106,248,253)' stroke-width='1' opacity='0.996078431372549' d='M 9.5 11 L 10 12.5 L 9 12.5 L 9.5 11 Z '></path><path fill='rgb(26,198,254)' stroke='rgb(26,198,254)' stroke-width='1' opacity='1' d='M 3.5 8 L 3.5 9 L 3.5 8 Z '></path><path fill='rgb(26,198,254)' stroke='rgb(26,198,254)' stroke-width='1' opacity='1' d='M 8.5 12 L 8.5 13 L 8.5 12 Z '></path><path fill='rgb(31,92,88)' stroke='rgb(31,92,88)' stroke-width='1' opacity='0.5843137254901961' d='M 10.5 11 L 11 12.5 L 10 12.5 L 10.5 11 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.03137254901960784' d='M 4.5 1 L 4.5 2 L 4.5 1 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.03137254901960784' d='M 5.5 3 L 5.5 4 L 5.5 3 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.03137254901960784' d='M 9.5 7 L 9.5 8 L 9.5 7 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.03137254901960784' d='M 12.5 12 L 12.5 13 L 12.5 12 Z '></path><path fill='rgb(0,25,25)' stroke='rgb(0,25,25)' stroke-width='1' opacity='0.047058823529411764' d='M 9.5 15 L 9.5 16 L 9.5 15 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.011764705882352941' d='M 11.5 12 L 11.5 13 L 11.5 12 Z '></path><path fill='rgb(0,0,0)' stroke='rgb(0,0,0)' stroke-width='1' opacity='0.011764705882352941' d='M 5.5 14 L 5.5 15 L 5.5 14 Z '></path><path fill='rgb(0,192,221)' stroke='rgb(0,192,221)' stroke-width='1' opacity='0.9568627450980393' d='M 3.5 1 L 3.5 2 L 3.5 1 Z '></path><path fill='rgb(0,116,165)' stroke='rgb(0,116,165)' stroke-width='1' opacity='0.8117647058823529' d='M 13.5 7 L 13.5 8 L 13.5 7 Z '></path><path fill='rgb(0,116,165)' stroke='rgb(0,116,165)' stroke-width='1' opacity='0.8117647058823529' d='M 12.5 11 L 12.5 12 L 12.5 11 Z '></path><path fill='rgb(0,10,10)' stroke='rgb(0,10,10)' stroke-width='1' opacity='0.09019607843137255' d='M 6.5 7 L 6.5 8 L 6.5 7 Z '></path><path fill='rgb(0,10,10)' stroke='rgb(0,10,10)' stroke-width='1' opacity='0.09019607843137255' d='M 5.5 13 L 5.5 14 L 5.5 13 Z '></path><path fill='rgb(0,10,10)' stroke='rgb(0,10,10)' stroke-width='1' opacity='0.09019607843137255' d='M 10.5 14 L 10.5 15 L 10.5 14 Z '></path></svg>

В общем буду использовать иконки в base64.


Ага, у них есть ещё оптимизатор, прогнал через него и файл уменьшился до 3,5КБ, а качество вроде бы и не пострадало. Это уже внушает оптимизм, можно дальше экспериментировать.


Нет, оптимизированный код выдал ошибку

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

Выделить код

Код:

data:image/svg+xml;utf8,<svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'><path fill='#050919' stroke='#050919' opacity='.176' d='M3.5 0v1-1zM1.5 1L2 2.5H1L1.5 1zM13.5 11v1-1zM8.5 15v1-1z'/><path fill='#367BDC' stroke='#367BDC' opacity='.969' d='M6.5 14v1-1z'/><path stroke='#000' opacity='.004' d='M.5 4L1 5.5H0L.5 4zM8.5 7v1-1z'/><path fill='#1B3672' stroke='#1B3672' opacity='.627' d='M1.5 4v1-1zM12.5 7v1-1zM14.5 7v1-1zM10.5 13v1-1z'/><path fill='#3F9CF8' stroke='#3F9CF8' opacity='.996' d='M3.5 4v1-1zM13.5 9v1-1zM1.5 10v1-1zM7.5 10v1-1zM9.5 13v1-1z'/><path fill='#080E3A' stroke='#080E3A' opacity='.42' d='M1.5 3v1-1zM5.5 4v1-1zM11.5 7v1-1zM15.5 7v1-1zM8.5 8v1-1zM3.5 11v1-1zM7.5 15v1-1z'/><path fill='#02030A' stroke='#02030A' opacity='.251' d='M1.5 0v1-1zM1.5 6v1-1zM5.5 12v1-1z'/><path fill='#4DEFFE' stroke='#4DEFFE' d='M3.5 6v1-1zM11.5 10v1-1zM7.5 13v1-1z'/><path fill='#3F94CE' stroke='#3F94CE' opacity='.996' d='M2.5 7l1.5.5-1.5.5V7zM3.5 10v1-1zM6.5 12v1-1z'/><path fill='#3349DC' stroke='#3349DC' opacity='.988' d='M4.5 3L5 4.5H4L4.5 3zM1.5 5q2.5 1 1 2-2.5-1-1-2zM5.5 7L6 8.5H5L5.5 7zM10.5 9q1.5 1-1 2-1.5-1 1-2zM6.5 11v1-1z'/><path fill='#02051F' stroke='#02051F' opacity='.325' d='M.5 7v1-1zM7.5 8v1-1z'/><path fill='#150B62' stroke='#150B62' opacity='.761' d='M5.5 5v1-1zM9.5 8v1-1zM.5 10v1-1zM4.5 10v1-1z'/><path fill='#245A8B' stroke='#245A8B' opacity='.718' d='M4.5 2v1-1zM14.5 9q1.5 1-1 2-1.5-1 1-2zM11.5 11v1-1zM9.5 14v1-1z'/><path fill='#2726B1' stroke='#2726B1' opacity='.922' d='M5.5 6v1-1zM.5 8v1-1zM8.5 14v1-1z'/><path fill='#1D2182' stroke='#1D2182' opacity='.871' d='M1.5 7v1-1zM.5 9v1-1zM2.5 11v1-1z'/><path fill='#41A6F9' stroke='#41A6F9' opacity='.996' d='M1.5 9l1.5.5-1.5.5V9zM8.5 10v1-1zM8.5 13q1.5 1-1 2-1.5-1 1-2z'/><path fill='#4AD9FE' stroke='#4AD9FE' d='M4.5 7v1-1zM3.5 9v1-1z'/><path fill='#21557E' stroke='#21557E' d='M5.5 9v1-1z'/><path fill='#2600B2' stroke='#2600B2' opacity='.988' d='M2.5 0v1-1zM2.5 3L3 4.5H2L2.5 3zM1.5 8v1-1zM12.5 8v1-1zM7.5 9l1.5.5-1.5.5V9z'/><path fill='#15055D' stroke='#15055D' opacity='.976' d='M2.5 2v1-1zM10.5 8l1.5.5-1.5.5V8zM6.5 9q1.5 1-1 2-1.5-1 1-2z'/><path fill='#301ADF' stroke='#301ADF' d='M2.5 1v1-1zM2.5 5v1-1zM2.5 8v1-1zM15.5 8v1-1zM9.5 9v1-1z'/><path fill='#3C85F6' stroke='#3C85F6' opacity='.996' d='M3.5 2L4 3.5H3L3.5 2zM4.5 5v1-1zM13.5 8l1.5.5-3.5 1.5V9l2-1zM6.5 13v1-1z'/><path fill='#45B8FD' stroke='#45B8FD' opacity='.996' d='M3.5 5v1-1zM3.5 8v1-1zM2.5 10v1-1zM10.5 10v1-1zM12.5 10v1-1z'/><path fill='#051C21' stroke='#051C21' opacity='.059' d='M14.5 10v1-1zM9.5 15v1-1z'/><path fill='#030718' stroke='#030718' opacity='.263' d='M10.5 7v1-1zM15.5 9v1-1zM.5 11v1-1z'/><path fill='#24349E' stroke='#24349E' d='M6.5 10v1-1zM1.5 11v1-1z'/><path fill='#050414' stroke='#050414' opacity='.722' d='M6.5 8v1-1z'/><path stroke='#000' opacity='.086' d='M4.5 11l1.5.5-1.5.5v-1zM1.5 12v1-1zM6.5 15v1-1z'/><path fill='#51FCFE' stroke='#51FCFE' d='M4.5 6v1-1zM4.5 8v1-1zM7 11l2 .5q-1 2.5-2 1V11z'/><path fill='#7DF8FC' stroke='#7DF8FC' opacity='.996' d='M9.5 11l.5 1.5H9l.5-1.5z'/><path fill='#49CBFD' stroke='#49CBFD' d='M4.5 9v1-1zM8.5 12v1-1z'/><path fill='#2E5C58' stroke='#2E5C58' opacity='.584' d='M10.5 11l.5 1.5h-1l.5-1.5z'/><path stroke='#000' opacity='.031' d='M4.5 1v1-1zM5.5 3v1-1zM9.5 7v1-1zM12.5 12v1-1z'/><path fill='#0D0D13' stroke='#0D0D13' opacity='.075' d='M6.5 7v1-1zM5.5 13v1-1z'/><path stroke='#000' opacity='.012' d='M11.5 12v1-1zM5.5 14v1-1z'/><path fill='#40BEDB' stroke='#40BEDB' opacity='.957' d='M3.5 1v1-1z'/><path fill='#2B71A2' stroke='#2B71A2' opacity='.812' d='M13.5 7v1-1zM12.5 11v1-1z'/><path fill='#090913' stroke='#090913' opacity='.102' d='M10.5 14v1-1z'/></svg>

sandro79 пишет

Нет, оптимизированный код выдал ошибку

Так у вас повтор здесь ... </svg><svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'> ...
и цвета нужно перевести в rgb(a) или заменить # на %23

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

Vitaliy V. пишет

Так у вас повтор здесь ... ...
и цвета нужно перевести в rgb или заменить # на %23

Ну это, тот, что под первым спойлером в оптимизированном варианте, бабочка ихняя. В rgb(a) пока не пробовал, достаточно трудоёмкая для меня задача, заменил пакетно # на %23, всё-равно ошибку даёт. Может после %23 пробел должен быть, тоже пробовал, не прошло.

А вообще конечно всякие там конверторы из растра в SVG ерунда полная, да и зачем иконок SVG тоже полно

Да это верно, но хоть что-то, если не знаешь сам как и что делать. Да хотелось именно оригинальную иконку бабочки перегнать в svg. Зациклило меня на решении этой задачи.

В крайнем случае можно конвертнуть из др. вектора в SVG

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

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

Выделить код

Код:

<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0" d="M 0.5 0 L 1 3.5 L 0 3.5 L 0.5 0 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0" d="M 4.5 0 L 16 0 L 16 7 L 8.5 7 L 7.5 8 L 6 6.5 L 6 3.5 L 4 0.5 L 4.5 0 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0" d="M 0.5 6 L 0.5 7 L 0.5 6 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0" d="M 15.5 10 L 16 16 L 10 15.5 L 11 13 Q 14.25 13.5 15.5 10 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0" d="M 0.5 12 Q 1.5 14 2.5 12 Q 6.25 10.75 5 14.5 L 5.5 16 L 0 16 L 0.5 12 Z "></path><path fill="rgb(5,9,25)" stroke="rgb(5,9,25)" stroke-width="1" opacity="0.17647058823529413" d="M 3.5 0 L 3.5 1 L 3.5 0 Z "></path><path fill="rgb(5,9,25)" stroke="rgb(5,9,25)" stroke-width="1" opacity="0.17647058823529413" d="M 1.5 1 L 2 2.5 L 1 2.5 L 1.5 1 Z "></path><path fill="rgb(5,9,25)" stroke="rgb(5,9,25)" stroke-width="1" opacity="0.17647058823529413" d="M 13.5 11 L 13.5 12 L 13.5 11 Z "></path><path fill="rgb(5,9,25)" stroke="rgb(5,9,25)" stroke-width="1" opacity="0.17647058823529413" d="M 8.5 15 L 8.5 16 L 8.5 15 Z "></path><path fill="rgb(54,123,220)" stroke="rgb(54,123,220)" stroke-width="1" opacity="0.9686274509803922" d="M 6.5 14 L 6.5 15 L 6.5 14 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.00392156862745098" d="M 0.5 4 L 1 5.5 L 0 5.5 L 0.5 4 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.00392156862745098" d="M 8.5 7 L 8.5 8 L 8.5 7 Z "></path><path fill="rgb(27,54,114)" stroke="rgb(27,54,114)" stroke-width="1" opacity="0.6274509803921569" d="M 1.5 4 L 1.5 5 L 1.5 4 Z "></path><path fill="rgb(27,54,114)" stroke="rgb(27,54,114)" stroke-width="1" opacity="0.6274509803921569" d="M 12.5 7 L 12.5 8 L 12.5 7 Z "></path><path fill="rgb(27,54,114)" stroke="rgb(27,54,114)" stroke-width="1" opacity="0.6274509803921569" d="M 14.5 7 L 14.5 8 L 14.5 7 Z "></path><path fill="rgb(27,54,114)" stroke="rgb(27,54,114)" stroke-width="1" opacity="0.6274509803921569" d="M 10.5 13 L 10.5 14 L 10.5 13 Z "></path><path fill="rgb(63,156,248)" stroke="rgb(63,156,248)" stroke-width="1" opacity="0.996078431372549" d="M 3.5 4 L 3.5 5 L 3.5 4 Z "></path><path fill="rgb(63,156,248)" stroke="rgb(63,156,248)" stroke-width="1" opacity="0.996078431372549" d="M 13.5 9 L 13.5 10 L 13.5 9 Z "></path><path fill="rgb(63,156,248)" stroke="rgb(63,156,248)" stroke-width="1" opacity="0.996078431372549" d="M 1.5 10 L 1.5 11 L 1.5 10 Z "></path><path fill="rgb(63,156,248)" stroke="rgb(63,156,248)" stroke-width="1" opacity="0.996078431372549" d="M 7.5 10 L 7.5 11 L 7.5 10 Z "></path><path fill="rgb(63,156,248)" stroke="rgb(63,156,248)" stroke-width="1" opacity="0.996078431372549" d="M 9.5 13 L 9.5 14 L 9.5 13 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 1.5 3 L 1.5 4 L 1.5 3 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 5.5 4 L 5.5 5 L 5.5 4 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 11.5 7 L 11.5 8 L 11.5 7 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 15.5 7 L 15.5 8 L 15.5 7 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 8.5 8 L 8.5 9 L 8.5 8 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 3.5 11 L 3.5 12 L 3.5 11 Z "></path><path fill="rgb(8,14,58)" stroke="rgb(8,14,58)" stroke-width="1" opacity="0.4196078431372549" d="M 7.5 15 L 7.5 16 L 7.5 15 Z "></path><path fill="rgb(2,3,10)" stroke="rgb(2,3,10)" stroke-width="1" opacity="0.25098039215686274" d="M 1.5 0 L 1.5 1 L 1.5 0 Z "></path><path fill="rgb(2,3,10)" stroke="rgb(2,3,10)" stroke-width="1" opacity="0.25098039215686274" d="M 1.5 6 L 1.5 7 L 1.5 6 Z "></path><path fill="rgb(2,3,10)" stroke="rgb(2,3,10)" stroke-width="1" opacity="0.25098039215686274" d="M 5.5 12 L 5.5 13 L 5.5 12 Z "></path><path fill="rgb(77,239,254)" stroke="rgb(77,239,254)" stroke-width="1" opacity="1" d="M 3.5 6 L 3.5 7 L 3.5 6 Z "></path><path fill="rgb(77,239,254)" stroke="rgb(77,239,254)" stroke-width="1" opacity="1" d="M 11.5 10 L 11.5 11 L 11.5 10 Z "></path><path fill="rgb(77,239,254)" stroke="rgb(77,239,254)" stroke-width="1" opacity="1" d="M 7.5 13 L 7.5 14 L 7.5 13 Z "></path><path fill="rgb(63,148,206)" stroke="rgb(63,148,206)" stroke-width="1" opacity="0.996078431372549" d="M 2.5 7 L 4 7.5 L 2.5 8 L 2.5 7 Z "></path><path fill="rgb(63,148,206)" stroke="rgb(63,148,206)" stroke-width="1" opacity="0.996078431372549" d="M 3.5 10 L 3.5 11 L 3.5 10 Z "></path><path fill="rgb(63,148,206)" stroke="rgb(63,148,206)" stroke-width="1" opacity="0.996078431372549" d="M 6.5 12 L 6.5 13 L 6.5 12 Z "></path><path fill="rgb(51,73,220)" stroke="rgb(51,73,220)" stroke-width="1" opacity="0.9882352941176471" d="M 4.5 3 L 5 4.5 L 4 4.5 L 4.5 3 Z "></path><path fill="rgb(51,73,220)" stroke="rgb(51,73,220)" stroke-width="1" opacity="0.9882352941176471" d="M 1.5 5 Q 4 6 2.5 7 Q 0 6 1.5 5 Z "></path><path fill="rgb(51,73,220)" stroke="rgb(51,73,220)" stroke-width="1" opacity="0.9882352941176471" d="M 5.5 7 L 6 8.5 L 5 8.5 L 5.5 7 Z "></path><path fill="rgb(51,73,220)" stroke="rgb(51,73,220)" stroke-width="1" opacity="0.9882352941176471" d="M 10.5 9 Q 12 10 9.5 11 Q 8 10 10.5 9 Z "></path><path fill="rgb(51,73,220)" stroke="rgb(51,73,220)" stroke-width="1" opacity="0.9882352941176471" d="M 6.5 11 L 6.5 12 L 6.5 11 Z "></path><path fill="rgb(2,5,31)" stroke="rgb(2,5,31)" stroke-width="1" opacity="0.3254901960784314" d="M 0.5 7 L 0.5 8 L 0.5 7 Z "></path><path fill="rgb(2,5,31)" stroke="rgb(2,5,31)" stroke-width="1" opacity="0.3254901960784314" d="M 7.5 8 L 7.5 9 L 7.5 8 Z "></path><path fill="rgb(21,11,98)" stroke="rgb(21,11,98)" stroke-width="1" opacity="0.7607843137254902" d="M 5.5 5 L 5.5 6 L 5.5 5 Z "></path><path fill="rgb(21,11,98)" stroke="rgb(21,11,98)" stroke-width="1" opacity="0.7607843137254902" d="M 9.5 8 L 9.5 9 L 9.5 8 Z "></path><path fill="rgb(21,11,98)" stroke="rgb(21,11,98)" stroke-width="1" opacity="0.7607843137254902" d="M 0.5 10 L 0.5 11 L 0.5 10 Z "></path><path fill="rgb(21,11,98)" stroke="rgb(21,11,98)" stroke-width="1" opacity="0.7607843137254902" d="M 4.5 10 L 4.5 11 L 4.5 10 Z "></path><path fill="rgb(36,90,139)" stroke="rgb(36,90,139)" stroke-width="1" opacity="0.7176470588235294" d="M 4.5 2 L 4.5 3 L 4.5 2 Z "></path><path fill="rgb(36,90,139)" stroke="rgb(36,90,139)" stroke-width="1" opacity="0.7176470588235294" d="M 14.5 9 Q 16 10 13.5 11 Q 12 10 14.5 9 Z "></path><path fill="rgb(36,90,139)" stroke="rgb(36,90,139)" stroke-width="1" opacity="0.7176470588235294" d="M 11.5 11 L 11.5 12 L 11.5 11 Z "></path><path fill="rgb(36,90,139)" stroke="rgb(36,90,139)" stroke-width="1" opacity="0.7176470588235294" d="M 9.5 14 L 9.5 15 L 9.5 14 Z "></path><path fill="rgb(39,38,177)" stroke="rgb(39,38,177)" stroke-width="1" opacity="0.9215686274509803" d="M 5.5 6 L 5.5 7 L 5.5 6 Z "></path><path fill="rgb(39,38,177)" stroke="rgb(39,38,177)" stroke-width="1" opacity="0.9215686274509803" d="M 0.5 8 L 0.5 9 L 0.5 8 Z "></path><path fill="rgb(39,38,177)" stroke="rgb(39,38,177)" stroke-width="1" opacity="0.9215686274509803" d="M 8.5 14 L 8.5 15 L 8.5 14 Z "></path><path fill="rgb(29,33,130)" stroke="rgb(29,33,130)" stroke-width="1" opacity="0.8705882352941177" d="M 1.5 7 L 1.5 8 L 1.5 7 Z "></path><path fill="rgb(29,33,130)" stroke="rgb(29,33,130)" stroke-width="1" opacity="0.8705882352941177" d="M 0.5 9 L 0.5 10 L 0.5 9 Z "></path><path fill="rgb(29,33,130)" stroke="rgb(29,33,130)" stroke-width="1" opacity="0.8705882352941177" d="M 2.5 11 L 2.5 12 L 2.5 11 Z "></path><path fill="rgb(65,166,249)" stroke="rgb(65,166,249)" stroke-width="1" opacity="0.996078431372549" d="M 1.5 9 L 3 9.5 L 1.5 10 L 1.5 9 Z "></path><path fill="rgb(65,166,249)" stroke="rgb(65,166,249)" stroke-width="1" opacity="0.996078431372549" d="M 8.5 10 L 8.5 11 L 8.5 10 Z "></path><path fill="rgb(65,166,249)" stroke="rgb(65,166,249)" stroke-width="1" opacity="0.996078431372549" d="M 8.5 13 Q 10 14 7.5 15 Q 6 14 8.5 13 Z "></path><path fill="rgb(74,217,254)" stroke="rgb(74,217,254)" stroke-width="1" opacity="1" d="M 4.5 7 L 4.5 8 L 4.5 7 Z "></path><path fill="rgb(74,217,254)" stroke="rgb(74,217,254)" stroke-width="1" opacity="1" d="M 3.5 9 L 3.5 10 L 3.5 9 Z "></path><path fill="rgb(33,85,126)" stroke="rgb(33,85,126)" stroke-width="1" opacity="1" d="M 5.5 9 L 5.5 10 L 5.5 9 Z "></path><path fill="rgb(38,0,178)" stroke="rgb(38,0,178)" stroke-width="1" opacity="0.9882352941176471" d="M 2.5 0 L 2.5 1 L 2.5 0 Z "></path><path fill="rgb(38,0,178)" stroke="rgb(38,0,178)" stroke-width="1" opacity="0.9882352941176471" d="M 2.5 3 L 3 4.5 L 2 4.5 L 2.5 3 Z "></path><path fill="rgb(38,0,178)" stroke="rgb(38,0,178)" stroke-width="1" opacity="0.9882352941176471" d="M 1.5 8 L 1.5 9 L 1.5 8 Z "></path><path fill="rgb(38,0,178)" stroke="rgb(38,0,178)" stroke-width="1" opacity="0.9882352941176471" d="M 12.5 8 L 12.5 9 L 12.5 8 Z "></path><path fill="rgb(38,0,178)" stroke="rgb(38,0,178)" stroke-width="1" opacity="0.9882352941176471" d="M 7.5 9 L 9 9.5 L 7.5 10 L 7.5 9 Z "></path><path fill="rgb(21,5,93)" stroke="rgb(21,5,93)" stroke-width="1" opacity="0.9764705882352941" d="M 2.5 2 L 2.5 3 L 2.5 2 Z "></path><path fill="rgb(21,5,93)" stroke="rgb(21,5,93)" stroke-width="1" opacity="0.9764705882352941" d="M 10.5 8 L 12 8.5 L 10.5 9 L 10.5 8 Z "></path><path fill="rgb(21,5,93)" stroke="rgb(21,5,93)" stroke-width="1" opacity="0.9764705882352941" d="M 6.5 9 Q 8 10 5.5 11 Q 4 10 6.5 9 Z "></path><path fill="rgb(48,26,223)" stroke="rgb(48,26,223)" stroke-width="1" opacity="1" d="M 2.5 1 L 2.5 2 L 2.5 1 Z "></path><path fill="rgb(48,26,223)" stroke="rgb(48,26,223)" stroke-width="1" opacity="1" d="M 2.5 5 L 2.5 6 L 2.5 5 Z "></path><path fill="rgb(48,26,223)" stroke="rgb(48,26,223)" stroke-width="1" opacity="1" d="M 2.5 8 L 2.5 9 L 2.5 8 Z "></path><path fill="rgb(48,26,223)" stroke="rgb(48,26,223)" stroke-width="1" opacity="1" d="M 15.5 8 L 15.5 9 L 15.5 8 Z "></path><path fill="rgb(48,26,223)" stroke="rgb(48,26,223)" stroke-width="1" opacity="1" d="M 9.5 9 L 9.5 10 L 9.5 9 Z "></path><path fill="rgb(60,133,246)" stroke="rgb(60,133,246)" stroke-width="1" opacity="0.996078431372549" d="M 3.5 2 L 4 3.5 L 3 3.5 L 3.5 2 Z "></path><path fill="rgb(60,133,246)" stroke="rgb(60,133,246)" stroke-width="1" opacity="0.996078431372549" d="M 4.5 5 L 4.5 6 L 4.5 5 Z "></path><path fill="rgb(60,133,246)" stroke="rgb(60,133,246)" stroke-width="1" opacity="0.996078431372549" d="M 13.5 8 L 15 8.5 L 11.5 10 L 11.5 9 L 13.5 8 Z "></path><path fill="rgb(60,133,246)" stroke="rgb(60,133,246)" stroke-width="1" opacity="0.996078431372549" d="M 6.5 13 L 6.5 14 L 6.5 13 Z "></path><path fill="rgb(69,184,253)" stroke="rgb(69,184,253)" stroke-width="1" opacity="0.996078431372549" d="M 3.5 5 L 3.5 6 L 3.5 5 Z "></path><path fill="rgb(69,184,253)" stroke="rgb(69,184,253)" stroke-width="1" opacity="0.996078431372549" d="M 3.5 8 L 3.5 9 L 3.5 8 Z "></path><path fill="rgb(69,184,253)" stroke="rgb(69,184,253)" stroke-width="1" opacity="0.996078431372549" d="M 2.5 10 L 2.5 11 L 2.5 10 Z "></path><path fill="rgb(69,184,253)" stroke="rgb(69,184,253)" stroke-width="1" opacity="0.996078431372549" d="M 10.5 10 L 10.5 11 L 10.5 10 Z "></path><path fill="rgb(69,184,253)" stroke="rgb(69,184,253)" stroke-width="1" opacity="0.996078431372549" d="M 12.5 10 L 12.5 11 L 12.5 10 Z "></path><path fill="rgb(5,28,33)" stroke="rgb(5,28,33)" stroke-width="1" opacity="0.058823529411764705" d="M 14.5 10 L 14.5 11 L 14.5 10 Z "></path><path fill="rgb(5,28,33)" stroke="rgb(5,28,33)" stroke-width="1" opacity="0.058823529411764705" d="M 9.5 15 L 9.5 16 L 9.5 15 Z "></path><path fill="rgb(3,7,24)" stroke="rgb(3,7,24)" stroke-width="1" opacity="0.2627450980392157" d="M 10.5 7 L 10.5 8 L 10.5 7 Z "></path><path fill="rgb(3,7,24)" stroke="rgb(3,7,24)" stroke-width="1" opacity="0.2627450980392157" d="M 15.5 9 L 15.5 10 L 15.5 9 Z "></path><path fill="rgb(3,7,24)" stroke="rgb(3,7,24)" stroke-width="1" opacity="0.2627450980392157" d="M 0.5 11 L 0.5 12 L 0.5 11 Z "></path><path fill="rgb(36,52,158)" stroke="rgb(36,52,158)" stroke-width="1" opacity="1" d="M 6.5 10 L 6.5 11 L 6.5 10 Z "></path><path fill="rgb(36,52,158)" stroke="rgb(36,52,158)" stroke-width="1" opacity="1" d="M 1.5 11 L 1.5 12 L 1.5 11 Z "></path><path fill="rgb(5,4,20)" stroke="rgb(5,4,20)" stroke-width="1" opacity="0.7215686274509804" d="M 6.5 8 L 6.5 9 L 6.5 8 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.08627450980392157" d="M 4.5 11 L 6 11.5 L 4.5 12 L 4.5 11 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.08627450980392157" d="M 1.5 12 L 1.5 13 L 1.5 12 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.08627450980392157" d="M 6.5 15 L 6.5 16 L 6.5 15 Z "></path><path fill="rgb(81,252,254)" stroke="rgb(81,252,254)" stroke-width="1" opacity="1" d="M 4.5 6 L 4.5 7 L 4.5 6 Z "></path><path fill="rgb(81,252,254)" stroke="rgb(81,252,254)" stroke-width="1" opacity="1" d="M 4.5 8 L 4.5 9 L 4.5 8 Z "></path><path fill="rgb(81,252,254)" stroke="rgb(81,252,254)" stroke-width="1" opacity="1" d="M 7 11 L 9 11.5 Q 8 14 7 12.5 L 7 11 Z "></path><path fill="rgb(125,248,252)" stroke="rgb(125,248,252)" stroke-width="1" opacity="0.996078431372549" d="M 9.5 11 L 10 12.5 L 9 12.5 L 9.5 11 Z "></path><path fill="rgb(73,203,253)" stroke="rgb(73,203,253)" stroke-width="1" opacity="1" d="M 4.5 9 L 4.5 10 L 4.5 9 Z "></path><path fill="rgb(73,203,253)" stroke="rgb(73,203,253)" stroke-width="1" opacity="1" d="M 8.5 12 L 8.5 13 L 8.5 12 Z "></path><path fill="rgb(46,92,88)" stroke="rgb(46,92,88)" stroke-width="1" opacity="0.5843137254901961" d="M 10.5 11 L 11 12.5 L 10 12.5 L 10.5 11 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.03137254901960784" d="M 4.5 1 L 4.5 2 L 4.5 1 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.03137254901960784" d="M 5.5 3 L 5.5 4 L 5.5 3 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.03137254901960784" d="M 9.5 7 L 9.5 8 L 9.5 7 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.03137254901960784" d="M 12.5 12 L 12.5 13 L 12.5 12 Z "></path><path fill="rgb(13,13,19)" stroke="rgb(13,13,19)" stroke-width="1" opacity="0.07450980392156863" d="M 6.5 7 L 6.5 8 L 6.5 7 Z "></path><path fill="rgb(13,13,19)" stroke="rgb(13,13,19)" stroke-width="1" opacity="0.07450980392156863" d="M 5.5 13 L 5.5 14 L 5.5 13 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.011764705882352941" d="M 11.5 12 L 11.5 13 L 11.5 12 Z "></path><path fill="rgb(0,0,0)" stroke="rgb(0,0,0)" stroke-width="1" opacity="0.011764705882352941" d="M 5.5 14 L 5.5 15 L 5.5 14 Z "></path><path fill="rgb(64,190,219)" stroke="rgb(64,190,219)" stroke-width="1" opacity="0.9568627450980393" d="M 3.5 1 L 3.5 2 L 3.5 1 Z "></path><path fill="rgb(43,113,162)" stroke="rgb(43,113,162)" stroke-width="1" opacity="0.8117647058823529" d="M 13.5 7 L 13.5 8 L 13.5 7 Z "></path><path fill="rgb(43,113,162)" stroke="rgb(43,113,162)" stroke-width="1" opacity="0.8117647058823529" d="M 12.5 11 L 12.5 12 L 12.5 11 Z "></path><path fill="rgb(9,9,19)" stroke="rgb(9,9,19)" stroke-width="1" opacity="0.10196078431372549" d="M 10.5 14 L 10.5 15 L 10.5 14 Z "></path></svg>

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

Выделить код

Код:

<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path fill="#050919" stroke="#050919" opacity=".176" d="M3.5 0v1-1zM1.5 1L2 2.5H1L1.5 1zM13.5 11v1-1zM8.5 15v1-1z"/><path fill="#367BDC" stroke="#367BDC" opacity=".969" d="M6.5 14v1-1z"/><path stroke="#000" opacity=".004" d="M.5 4L1 5.5H0L.5 4zM8.5 7v1-1z"/><path fill="#1B3672" stroke="#1B3672" opacity=".627" d="M1.5 4v1-1zM12.5 7v1-1zM14.5 7v1-1zM10.5 13v1-1z"/><path fill="#3F9CF8" stroke="#3F9CF8" opacity=".996" d="M3.5 4v1-1zM13.5 9v1-1zM1.5 10v1-1zM7.5 10v1-1zM9.5 13v1-1z"/><path fill="#080E3A" stroke="#080E3A" opacity=".42" d="M1.5 3v1-1zM5.5 4v1-1zM11.5 7v1-1zM15.5 7v1-1zM8.5 8v1-1zM3.5 11v1-1zM7.5 15v1-1z"/><path fill="#02030A" stroke="#02030A" opacity=".251" d="M1.5 0v1-1zM1.5 6v1-1zM5.5 12v1-1z"/><path fill="#4DEFFE" stroke="#4DEFFE" d="M3.5 6v1-1zM11.5 10v1-1zM7.5 13v1-1z"/><path fill="#3F94CE" stroke="#3F94CE" opacity=".996" d="M2.5 7l1.5.5-1.5.5V7zM3.5 10v1-1zM6.5 12v1-1z"/><path fill="#3349DC" stroke="#3349DC" opacity=".988" d="M4.5 3L5 4.5H4L4.5 3zM1.5 5q2.5 1 1 2-2.5-1-1-2zM5.5 7L6 8.5H5L5.5 7zM10.5 9q1.5 1-1 2-1.5-1 1-2zM6.5 11v1-1z"/><path fill="#02051F" stroke="#02051F" opacity=".325" d="M.5 7v1-1zM7.5 8v1-1z"/><path fill="#150B62" stroke="#150B62" opacity=".761" d="M5.5 5v1-1zM9.5 8v1-1zM.5 10v1-1zM4.5 10v1-1z"/><path fill="#245A8B" stroke="#245A8B" opacity=".718" d="M4.5 2v1-1zM14.5 9q1.5 1-1 2-1.5-1 1-2zM11.5 11v1-1zM9.5 14v1-1z"/><path fill="#2726B1" stroke="#2726B1" opacity=".922" d="M5.5 6v1-1zM.5 8v1-1zM8.5 14v1-1z"/><path fill="#1D2182" stroke="#1D2182" opacity=".871" d="M1.5 7v1-1zM.5 9v1-1zM2.5 11v1-1z"/><path fill="#41A6F9" stroke="#41A6F9" opacity=".996" d="M1.5 9l1.5.5-1.5.5V9zM8.5 10v1-1zM8.5 13q1.5 1-1 2-1.5-1 1-2z"/><path fill="#4AD9FE" stroke="#4AD9FE" d="M4.5 7v1-1zM3.5 9v1-1z"/><path fill="#21557E" stroke="#21557E" d="M5.5 9v1-1z"/><path fill="#2600B2" stroke="#2600B2" opacity=".988" d="M2.5 0v1-1zM2.5 3L3 4.5H2L2.5 3zM1.5 8v1-1zM12.5 8v1-1zM7.5 9l1.5.5-1.5.5V9z"/><path fill="#15055D" stroke="#15055D" opacity=".976" d="M2.5 2v1-1zM10.5 8l1.5.5-1.5.5V8zM6.5 9q1.5 1-1 2-1.5-1 1-2z"/><path fill="#301ADF" stroke="#301ADF" d="M2.5 1v1-1zM2.5 5v1-1zM2.5 8v1-1zM15.5 8v1-1zM9.5 9v1-1z"/><path fill="#3C85F6" stroke="#3C85F6" opacity=".996" d="M3.5 2L4 3.5H3L3.5 2zM4.5 5v1-1zM13.5 8l1.5.5-3.5 1.5V9l2-1zM6.5 13v1-1z"/><path fill="#45B8FD" stroke="#45B8FD" opacity=".996" d="M3.5 5v1-1zM3.5 8v1-1zM2.5 10v1-1zM10.5 10v1-1zM12.5 10v1-1z"/><path fill="#051C21" stroke="#051C21" opacity=".059" d="M14.5 10v1-1zM9.5 15v1-1z"/><path fill="#030718" stroke="#030718" opacity=".263" d="M10.5 7v1-1zM15.5 9v1-1zM.5 11v1-1z"/><path fill="#24349E" stroke="#24349E" d="M6.5 10v1-1zM1.5 11v1-1z"/><path fill="#050414" stroke="#050414" opacity=".722" d="M6.5 8v1-1z"/><path stroke="#000" opacity=".086" d="M4.5 11l1.5.5-1.5.5v-1zM1.5 12v1-1zM6.5 15v1-1z"/><path fill="#51FCFE" stroke="#51FCFE" d="M4.5 6v1-1zM4.5 8v1-1zM7 11l2 .5q-1 2.5-2 1V11z"/><path fill="#7DF8FC" stroke="#7DF8FC" opacity=".996" d="M9.5 11l.5 1.5H9l.5-1.5z"/><path fill="#49CBFD" stroke="#49CBFD" d="M4.5 9v1-1zM8.5 12v1-1z"/><path fill="#2E5C58" stroke="#2E5C58" opacity=".584" d="M10.5 11l.5 1.5h-1l.5-1.5z"/><path stroke="#000" opacity=".031" d="M4.5 1v1-1zM5.5 3v1-1zM9.5 7v1-1zM12.5 12v1-1z"/><path fill="#0D0D13" stroke="#0D0D13" opacity=".075" d="M6.5 7v1-1zM5.5 13v1-1z"/><path stroke="#000" opacity=".012" d="M11.5 12v1-1zM5.5 14v1-1z"/><path fill="#40BEDB" stroke="#40BEDB" opacity=".957" d="M3.5 1v1-1z"/><path fill="#2B71A2" stroke="#2B71A2" opacity=".812" d="M13.5 7v1-1zM12.5 11v1-1z"/><path fill="#090913" stroke="#090913" opacity=".102" d="M10.5 14v1-1z"/></svg>

sandro79 пишет

Да хотелось именно оригинальную иконку бабочки перегнать в svg. Зациклило меня на решении этой задачи.

Не помню уже откуда взял, но завалялась у меня эта бабочка в PNG, размером 170px на 170px.
Я попробовал поиграться с конвертором, однако без потери качества перегнать не удалось.
Попробуйте, может у вас что-то получится.
nnm-club-02.png

sandro79 пишет

Ну это, тот, что под первым спойлером в оптимизированном варианте

Под первым у вас вообще то png в обертке SVG, а там где 14,6 КБ кода, нет повтора как в последнем
Вот ещё раз, здесь заканчивается svg тег
...<path fill='#000A0A' stroke='#000A0A' opacity='.09' d='M6.5 7v1-1zM5.5 13v1-1zM10.5 14v1-1z'/></svg>
а дальше опять начинается
<svg width='16' height='16' xmlns='http://www.w3.org/2000/svg'>...
ну это видимо вы вставили два раза в этот пост?

sandro79 пишет

Вы имеете в виду из первого большого кода конвертнуть в нормальном неонлайн-редакторе?

Нет никаким редактором не получится нормально конвертировать растровое изображение в векторное SVG
разве что потом вручную подправлять и то если они простые и не слишком фотографические.
Почитайте в чем отличие вектора от растра, в векторе нельзя создать фото, зато его можно масштабировать как угодно без потери качества.
Я имел ввиду можно без проблем перевести векторные изображения (они кроме SVG бывают и в других форматах - EPS, AI и т.д.) в SVG

unter_officer пишет

Попробуйте, может у вас что-то получится.

Спасибо! Тоже пригодится, буду экспериментировать.

Vitaliy V. пишет

Под первым у вас вообще то png в обертке SVG, а там где 14,6 КБ кода, нет повтора как в последнем

Да-да, имелся в виду 14,6 КБ. Про тот забыл уже.

Вот ещё раз, здесь заканчивается svg тег... а дальше опять начинается

Да, точно, вот же ж, два раза вставил.

Нет никаким редактором не получится нормально конвертировать растровое изображение в векторное SVG...

Да-да, я так уже поверхностно понимаю суть, тут ещё вы говорили. Надо будет так, хоть в общем, ознакомится с этой темой. Огромное Спасибо за разъяснение.


Подправил тот код под третьим спойлером, убрал дубляж, хоть и не работает, но так, для порядка.

Vitaliy V.
Ну проясните пожалуйста один момент. Решил обновить кнопку вкл/откл звука на вкладке/вкладках на вариант из add_toolbar_buttons. Взял код этой кнопки из parent.js, внёс правки, добавил код иконки из sound.svg, всё работает, но только иконка не меняет цвет на белый на тёмном фоне, как в add_toolbar_buttons. Нужно внести ещё какие-то правки, но вот какие и возможно ли это в скрипте, не знаю. Можно использовать конечно старую иконку, она всегда белая. Вот как бы эту чёрную заставить менять цвет на тёмном фоне?

Новый код кнопки с новой иконкой
Image_001.png

Выделить код

Код:

try {
	CustomizableUI.createWidget({
		id: "b-sound-muted-all-tabs",
		type: "custom",
		label: "Переключить звук",
		tooltiptext: "ЛКМ: Переключить звук в выделенных вкладках\nСКМ: Закрыть другие вкладки с источником звука\nПКМ: Переключить звук во всех вкладках",
		defaultArea: CustomizableUI.AREA_NAVBAR,
		localized: false,
		onBuild(doc) {
			var trbn = doc.createXULElement("toolbarbutton"),
			win = doc.defaultView;
			trbn.id = "b-sound-muted-all-tabs";
			trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
			trbn.setAttribute("label", "Переключить звук");
			trbn.setAttribute("tooltiptext", "ЛКМ: Переключить звук в выделенных вкладках\nСКМ: Закрыть другие вкладки с источником звука\nПКМ: Переключить звук во всех вкладках");
			trbn.setAttribute("context", false);
			trbn.setAttribute("image", "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 48 48'><g style='fill:context-fill;fill-opacity:context-fill-opacity;'><path d='M 22.5,4.49 15,12 H 10.5 C 6,12 3,16.5 3,21 L 3,27 C 3,31.5 5.99,36 10.5,36 H 15 L 22.5,43.5 C 24.9,45.9 27,45 27,43.5 V 4.49 C 27,2.99 24.9,2.09 22.5,4.49 Z'/><path d='M 39,24 C 39,19 35,15 30,15 28,15 28,18 30,18 33.3,18 36,20.7 36,24 36,27.3 33.3,30 30,30 28,30 28,33 30,33 35,33 39,29 39,24 Z'/><path d='M 30,9 C 28,9 28,12 30,12 36.6,12 42,17.4 42,24 42,30.6 36.6,36 30,36 28,36 28,39 30,39 38.4,39 45,32.4 45,24 45,15.6 38.4,9 30,9 Z'/></g></svg>");
			trbn.addEventListener("click", e => {
				if (e.button == 0) {
					win.gBrowser.toggleMuteAudioOnMultiSelectedTabs(win.gBrowser.selectedTab);
				} else if (e.button == 1) {
					for (let tab of win.gBrowser.visibleTabs.filter(tab => !tab.selected && (tab.muted || tab.soundPlaying)))
						win.gBrowser.removeTab(tab);
				} else if (e.button == 2) {
					e.preventDefault();
					e.stopPropagation();
					let tabsToToggle;
					if (win.gBrowser.selectedTab.activeMediaBlocked) {
						tabsToToggle = win.gBrowser.visibleTabs.filter(tab => tab.activeMediaBlocked || tab.linkedBrowser.audioMuted);
					} else {
						let tabMuted = win.gBrowser.selectedTab.linkedBrowser.audioMuted;
						tabsToToggle = win.gBrowser.visibleTabs.filter(tab => (tab.linkedBrowser.audioMuted == tabMuted && !tab.activeMediaBlocked) || (tab.activeMediaBlocked && tabMuted));
					}
					for (let tab of tabsToToggle)
						tab.toggleMuteAudio();
				}
			});
			return trbn;
		},
	});
} catch(e) {}

Вообще думаю использовать иконку, находящуюся по адресу chrome://global/skin/media/audio.svg
скрытый текст

Выделить код

Код:

data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16' fill='context-fill' fill-opacity='context-fill-opacity'><path d='M7.245 1.35 4.117 5 2 5a2 2 0 0 0-2 2l0 2a2 2 0 0 0 2 2l2.117 0 3.128 3.65C7.848 15.353 9 14.927 9 14L9 2c0-.927-1.152-1.353-1.755-.65z'/><path d='M11.764 15a.623.623 0 0 1-.32-1.162 6.783 6.783 0 0 0 3.306-5.805 6.767 6.767 0 0 0-3.409-5.864.624.624 0 1 1 .619-1.085A8.015 8.015 0 0 1 16 8.033a8.038 8.038 0 0 1-3.918 6.879c-.1.06-.21.088-.318.088z'/><path d='M11.434 11.85A4.982 4.982 0 0 0 13.25 8a4.982 4.982 0 0 0-1.819-3.852l-.431 0 0 7.702.434 0z'/></svg>

sandro79 пишет

Вот как бы эту чёрную заставить менять цвет на тёмном фоне?

для иконок загруженных по data: -moz-context-properties не работает
если не включить svg.context-properties.content.enabled - true

sandro79 пишет

Вообще думаю использовать иконку, находящуюся по адресу chrome://global/skin/media/audio.svg

Ну так и впишите этот адрес вместо data: ...
Или как вариант добавить файл .svg в user_chrome_files
chrome://user_chrome_files/content/далее путь к иконке

Vitaliy V. пишет

если не включить svg.context-properties.content.enabled - true

Да, это пробовал. Но почему-то, как и с вариантом chrome://user_chrome_files/content/далее путь к иконке, иконка краснеет.
Ага, всё, разобрался, нашёл причину, так тоже работает нормально.

Ну так и впишите этот адрес вместо data: ...

Да, вот это сработало как надо. Да надо было сразу так сделать и проверить, зациклило меня, чтоб внутрь скрипта иконку встроить.
Спасибо за подсказку, это самый оптимальный вариант. В данном варианте svg.context-properties.content.enabled, как и с вариантом chrome://user_chrome_files/content/ - можно не включать

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

Выделить код

Код:

try {
	CustomizableUI.createWidget({
		id: "b-sound-muted-all-tabs",
		type: "custom",
		label: "Переключить звук",
		tooltiptext: "ЛКМ: Переключить звук в выделенных вкладках\nСКМ: Закрыть другие вкладки с источником звука\nПКМ: Переключить звук во всех вкладках",
		defaultArea: CustomizableUI.AREA_NAVBAR,
		localized: false,
		onBuild(doc) {
			var trbn = doc.createXULElement("toolbarbutton"),
			win = doc.defaultView;
			trbn.id = "b-sound-muted-all-tabs";
			trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
			trbn.setAttribute("label", "Переключить звук");
			trbn.setAttribute("tooltiptext", "ЛКМ: Переключить звук в выделенных вкладках\nСКМ: Закрыть другие вкладки с источником звука\nПКМ: Переключить звук во всех вкладках");
			trbn.setAttribute("context", false);
			trbn.setAttribute("image", "chrome://global/skin/media/audio.svg");
			trbn.addEventListener("click", e => {
				if (e.button == 0) {
					win.gBrowser.toggleMuteAudioOnMultiSelectedTabs(win.gBrowser.selectedTab);
				} else if (e.button == 1) {
					for (let tab of win.gBrowser.visibleTabs.filter(tab => !tab.selected && (tab.muted || tab.soundPlaying)))
						win.gBrowser.removeTab(tab);
				} else if (e.button == 2) {
					e.preventDefault();
					e.stopPropagation();
					let tabsToToggle;
					if (win.gBrowser.selectedTab.activeMediaBlocked) {
						tabsToToggle = win.gBrowser.visibleTabs.filter(tab => tab.activeMediaBlocked || tab.linkedBrowser.audioMuted);
					} else {
						let tabMuted = win.gBrowser.selectedTab.linkedBrowser.audioMuted;
						tabsToToggle = win.gBrowser.visibleTabs.filter(tab => (tab.linkedBrowser.audioMuted == tabMuted && !tab.activeMediaBlocked) || (tab.activeMediaBlocked && tabMuted));
					}
					for (let tab of tabsToToggle)
						tab.toggleMuteAudio();
				}
			});
			return trbn;
		},
	});
} catch(e) {}


Наткнулся недавно на вроде бы полезный скрипт. Исправляет контекстное меню журнала.
Можно использовать с 11-ой строки, подключив в custom_script_win.js

Было|Cтало
Image_001.pngImage_002.png

ПАРОЛИ/КУКИ
https://forum.mozilla-russia.org/viewto … 86#p788786

FindBar для custom_script_all_win.js в секцию load
https://forum.mozilla-russia.org/viewto … 27#p777227

Код для CB, но отлично работает в ucf и в новых версиях FF
Если раскомментировать /* */ , то искомое будет на всех вкладках, а не только на исходной.

Туда же горячая клавиша для FindBar

Выделить код

Код:

addEventListener('keydown', e=> {
  if(e.ctrlKey&e.code=="KeyF"&&!gFindBar.hidden) {
    e.preventDefault()+gFindBar.close()
  }
});

Dumby
Поправь, пожалуйста, кнопку HTTP Request Logger для UCF.

rubel

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

Выделить код

Код:

(async self => CustomizableUI.createWidget(({
	label: "Unnamed",
	tooltiptext: "Unnamed",
	fileName: "http-request-log.txt",
	images: {
		true: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ==",
		false: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQQAArEEAAKxBAACsQQ=="
	},
	id: "ucf-httpRequestLogger",
	localized: false,
	init(pref) {
		var topic = "http-on-modify-request";
		this.toggle = () => Services.prefs.setBoolPref(pref, !this.active);
		var prefObs = () => {
			var val = Services.prefs.getBoolPref(pref, false);
			if (this.active ^ (this.active = val))
				Services.obs[`${val ? "add" : "remove"}Observer`](this, topic);
			this.setBtnsState();
		}
		prefObs();
		Services.prefs.addObserver(pref, prefObs);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.prefs.removeObserver(pref, prefObs);
			self.active && Services.obs.removeObserver(self, topic);
		}, "quit-application-granted");
		return self = this;
	},
	onCreated(btn) {
		btn._handleClick = this.toggle;
		btn.setAttribute("image", this.images[this.active]);
	},
	setBtnsState() {this.setBtnsState = () => {
		var img = this.images[this.active];
		var widget = CustomizableUI.getWidget(this.id);
		for(var win of CustomizableUI.windows)
			widget.forWindow(win).node?.setAttribute("image", img);
	}},
	log: "",
	observe(channel) {
		if (!(channel instanceof Ci.nsIHttpChannel)) return;
		this.log += `${
			channel.referrerInfo?.originalReferrer?.spec || "(none)"
		} ${
			channel.requestMethod 
		} ${
			channel.URI.spec
		}\r\n`;

		this.busy || this.write();
	},
	write() {
		var file = Services.dirsvc.get("Desk", Ci.nsIFile);
		file.append(this.fileName);
		var {path} = file;

		var {IOUtils} = Cu.getGlobalForObject(Cu);
		var modes = [{mode: "create"}, {mode: "append"}];
		var unbusy = () => {
			this.busy = false;
			this.log && this.write();
		}
		(this.write = () => {
			this.busy = true;
			var {log} = this;
			this.log = "";
			IOUtils.writeUTF8(path, log, modes[+file.exists()])
				.finally(unbusy);
		})();
	}
}).init("ucf.httpRequestLogger.enabled")))();

Dumby пишет

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

Выделить код

Код:

(async self => CustomizableUI.createWidget(({
	label: "Unnamed",
	tooltiptext: "Unnamed",
	fileName: "http-request-log.txt",
	images: {
		true: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ==",
		false: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQQAArEEAAKxBAACsQQ=="
	},
	id: "ucf-httpRequestLogger",
	localized: false,
	init(pref) {
		var topic = "http-on-modify-request";
		this.toggle = () => Services.prefs.setBoolPref(pref, !this.active);
		var prefObs = () => {
			var val = Services.prefs.getBoolPref(pref, false);
			if (this.active ^ (this.active = val))
				Services.obs[`${val ? "add" : "remove"}Observer`](this, topic);
			this.setBtnsState();
		}
		prefObs();
		Services.prefs.addObserver(pref, prefObs);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.prefs.removeObserver(pref, prefObs);
			self.active && Services.obs.removeObserver(self, topic);
		}, "quit-application-granted");
		return self = this;
	},
	onCreated(btn) {
		btn._handleClick = this.toggle;
		btn.setAttribute("image", this.images[this.active]);
	},
	setBtnsState() {this.setBtnsState = () => {
		var img = this.images[this.active];
		var widget = CustomizableUI.getWidget(this.id);
		for(var win of CustomizableUI.windows)
			widget.forWindow(win).node?.setAttribute("image", img);
	}},
	log: "",
	observe(channel) {
		if (!(channel instanceof Ci.nsIHttpChannel)) return;
		this.log += `${
			channel.referrerInfo?.originalReferrer?.spec || "(none)"
		} ${
			channel.requestMethod 
		} ${
			channel.URI.spec
		}\r\n`;

		this.busy || this.write();
	},
	write() {
		var file = Services.dirsvc.get("Desk", Ci.nsIFile);
		file.append(this.fileName);
		var {path} = file;

		var {IOUtils} = Cu.getGlobalForObject(Cu);
		var modes = [{mode: "create"}, {mode: "append"}];
		var unbusy = () => {
			this.busy = false;
			this.log && this.write();
		}
		(this.write = () => {
			this.busy = true;
			var {log} = this;
			this.log = "";
			IOUtils.writeUTF8(path, log, modes[+file.exists()])
				.finally(unbusy);
		})();
	}
}).init("ucf.httpRequestLogger.enabled")))();

Dumby, большое спасибо.

Dumby
Спасибо, прекрасно работает. :)

unter_officer, rubel
Парни, в какой custom_script, win или all_win его прописывать? И название должно быть ucf_httpRequestLogger?

добавлено   чорд возьми, нашел его в персонализации :dumb:

Новая версия UserChromeFiles со многими изменениями в коде, готова к использованию насколько могу судить,
но обновляться нужно осторожно и прочитав обновленную инструкцию,
из известных проблем, слетят ранее добавленные кнопки с панелей UserChromeFiles, если кому лень как мне лезть в персонализацию,
можно попробовать в консоле браузера выполнить код

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

Выделить код

Код:

var uiCustomization = "browser.uiCustomization.state";
Services.prefs.setCharPref(uiCustomization, Services.prefs.getCharPref(uiCustomization, "").replace(/"add-/g, "\"ucf-"));


врядли это затронет другие кнопки хоть и вероятность есть, или при закрытом браузере отредактируйте
настройку "browser.uiCustomization.state" в prefs.js заменив "add- на "ucf-


PS: Да и коды для загрузки доп. файлов скриптов теперь не требуются ...

Vitaliy V.
А с этим как... https://forum.mozilla-russia.org/viewto … 01#p788301 ?

kokoss
Для окна браузера - Включить скрипты: чекбокс "Для докум. окна браузера [ChromeOnly]"
и в CustomStylesScripts.jsm добавить в массив load: [ // По событию load    или domload: [ // По событию DOMContentLoaded
нужные вам скрипты, как видите здесь уже добавленны в закомментированном виде Special Widgets и Auto Hide Sidebar

скрытый текст
    scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию DOMContentLoaded

        ],
        load: [ // По событию load
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
        ],
    },

Да и параметр ucfobj должен быть true для моих скриптов (если не указано), и false или отсутствовать для других (если автором скрипта не указано обратное)

PS: custom_script_win.js и custom_script_all_win.js не нужно подключать в CustomStylesScripts.jsm
только в интерфейсе настроек включить "Для докум. окна браузера [ChromeOnly]" или "Для докум. всех окон [ChromeOnly]" соответственно
эти файлы не переименовывать и не удалять.
А custom_script.js можно раскомментировать в CustomStylesScripts.jsm ну или удалить или добавить с другим именем.


И кстати коды загрузчиков скриптов для custom_script_win.js custom_script_all_win.js также будут работать можете использовать их если нравится

Vitaliy V. пишет

Для окна браузера - Включить скрипты: чекбокс "Для докум. окна браузера [ChromeOnly]"
и в CustomStylesScripts.jsm добавить в массив load: [ // По событию load    или domload: [ // По событию DOMContentLoaded
нужные вам скрипты, как видите здесь уже добавленны в закомментированном виде Special Widgets и Auto Hide Sidebar
скрытый текст

Да и параметр ucfobj должен быть true для моих скриптов (если не указано), и false или отсутствовать для других (если автором скрипта не указано обратное)

PS: custom_script_win.js и custom_script_all_win.js не нужно подключать в CustomStylesScripts.jsm
только в интерфейсе настроек включить "Для докум. окна браузера [ChromeOnly]" или "Для докум. всех окон [ChromeOnly]" соответственно
эти файлы не переименовывать и не удалять.
А custom_script.js можно раскомментировать в CustomStylesScripts.jsm ну или удалить или добавить с другим именем

Пока не ясно как это работает, но надеюсь разберусь :)


Vitaliy V. пишет

И кстати коды загрузчиков скриптов для custom_script_win.js custom_script_all_win.js также будут работать можете использовать их если нравится

Спасибо!

kokoss пишет

Пока не ясно как это работает

А что не ясно, как работает (алгоритм, непонятно объяснил) или это не работает для вас?

Vitaliy V. пишет

или это не работает для вас?

Я же не сказал что не работает, просто многовато изменений.

непонятно объяснил)

да вроде понятно, если что спрошу

kokoss пишет

просто многовато изменений

Возможностей тоже прибавилось и потому что для более новых версий [firefox], 78+ теперь
было для 52+

Vitaliy V.
Спасибо за обновлённый комплект. Почти всё сразу заработало на подопытной 92.
Возникли только проблемы со скриптами для всех окон. ucf_wheretoopenlink.js и скрипты окна загрузок у меня не получилось запустить. В настройках включены все стили и скрипты. startupCache чистил постоянно.
Прописывал в CustomStylesScripts.jsm, поочерёдно в секции: domload: [ // По событию DOMContentLoaded, в load: [ // По событию load, по аналогии { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, }, с другими именами. Запустить удалось только по-старинке с добавлением загрузчика в custom_script_all_win.js
Не знаю может чего недопонял, неправильно сделал, да вроде и вариантов не много. Хорошо бы без загрузчиков запустить, как я понял из вашего поста, это возможно. Полный код CustomStylesScripts.jsm, как изначально, ну примерно так, прописывал незапустившиеся скрипты. ucjsDownloadsManager.uc.js прописывал тоже выше ucjsDownloadsManager2.uc.js

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

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfStylesScripts"];
var UcfStylesScripts = {
    /* ************************▼ Настройки ▼************************ */
    /**
    * Настройки стилей:
    *   path: путь к файлу от папки custom_styles
    *   type: права стиля AGENT_SHEET,  AUTHOR_SHEET или USER_SHEET
    */
    styleschrome: [ // Для докум. всех окон [ChromeOnly]
        // { path: "custom_styles_chrome_author.css", type: "AUTHOR_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "custom_styles_chrome_user.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "special_widget.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Special Widgets
        { path: "auto_hide_sidebar.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Auto Hide Sidebar
    ],
    stylesall: [ // Для всех документов
        // { path: "custom_styles_all_agent.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
        // { path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
    ],
    /**
    * Настройки скриптов:
    *   path: путь к скрипту от папки custom_scripts
    *   urlregxp: Адрес где работает скрипт в регулярном выражении, только Для докум. всех окон [ChromeOnly]
    *   ucfobj: true - загружать скрипт в специально созданный объект либо в window, для скриптов В фоне [System Principal] не используется
    */
    scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию DOMContentLoaded

        ],
        load: [ // По событию load
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "scripts3/favicon_in_urlbar.js", ucfobj: true, },
            { path: "scripts3/restart_item_in_menu.js", ucfobj: true, },
            { path: "scripts3/urlbarhistorydropmarker.js", ucfobj: true, },
            { path: "scripts3/contextmenuopenwith.js", ucfobj: true, }, 
            { path: "scripts3/add_bookmark_to_bookmarks_menu.js", ucfobj: false, },
            { path: "scripts3/pageInfo.js", ucfobj: false, },
            { path: "scripts3/places_addBookmarks.js", ucfobj: false, },
            { path: "scripts3/search_engine_icon.js", ucfobj: false, },
            { path: "scripts3/tabs_focus.js", ucfobj: false, },
            { path: "scripts3/tabstoolbar_doubleclick_opennewtab.js", ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager.uc.js", ucfobj: false, },
        ],
    },
    scriptsallchrome: { // Для докум. всех окон [ChromeOnly]
        domload: [ // По событию DOMContentLoaded
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager2.uc.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
        ],
        load: [ // По событию load
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
        ],
    },
    scriptsbackground: [ // В фоне [System Principal]
        { path: "custom_script.js", },
    ],
};
    /* ************************▲ Настройки ▲************************ */

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var UcfSSS = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
var preloadSheet = (obj, func) => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        let preload = UcfSSS.preloadSheet(uri, type);
        (obj.sheet = f => {
            try {
                f(preload, type);
            } catch (e) {}
        })(func);
    } catch (e) {
        obj.sheet = () => {};
    }
};
var registerSheet = async obj => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        if (!UcfSSS.sheetRegistered(uri, type))
            UcfSSS.loadAndRegisterSheet(uri, type);
    } catch (e) {}
};

Окно загрузок работает частично при добавлении ucjsDownloadsManager.uc.js в секцию scriptschrome: { // Для докум. окна браузера [ChromeOnly], ну это и понятно.
Добавление второй части скрипта в эту же секцию результата не дало, ну это и было ожидаемо.

sandro79 пишет

ucf_wheretoopenlink.js

Ок добавил ещё параметр где можно указать функцию которая выполнится при загрузке скрипта
func: Функция в виде строки которая выполнится при загрузке скрипта ...


вот так это должно выглядеть для окна библиотеки

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

Выделить код

Код:

load: [ // По событию load
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.places();" },
        ],


и для CustomStylesScriptsChild.jsm если нужно в контенте
скрытый текст

Выделить код

Код:

pageshow: [ // По событию pageshow
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, func: "ucf_where_to_open_link.places();" },
        ],

sandro79 пишет

может чего недопонял, неправильно сделал

Да с чего вы взяли что ваш ucjsDownloadsManager это окно библиотеки? Там все вместе и заклади и загрузки...
Но и я накосячил с для события load, забыл передать url документа в фукцию
Однако по событию DOMContentLoaded у вас бы сработало с этим адресом

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

Выделить код

Код:

/chrome:\/\/browser\/content\/downloads\/contentAreaDownloadsView\.xhtml/

Vitaliy V. пишет

Ок добавил ещё параметр где можно указать функцию которая выполнится при загрузке скрипта

Я так понял, что это было добавлено в нижнюю секцию "Настройки", обновил в обоих файлах и обновил user_chrome.js. В окне библиотеке сработало, для CustomStylesScriptsChild.jsm не добавлял, в контенте не нужно. Но вот почему не работает из боковой панели, значка журнала на панели, журнала из панели меню, ну то есть, как я понимаю в окне браузера "Для докум. окна браузера"? Вот это самое главное, о чём я забыл упомянуть выше, что вообще нигде не работает, кроме как если добавить загрузчик в custom_script_all_win.js, хотя можно и в custom_script_win.js.

Да с чего вы взяли что ваш ucjsDownloadsManager это окно библиотеки?

Да, в ходе экспериментов я это понял, когда оставил в адресе в регулярном выражении только chrome:/ - так кажется и серипт заработал. Сейчас с вашим адресом тоже работает. Только вот с ucf_wheretoopenlink.js не могу до конца разобраться.
Код CustomStylesScripts.jsm на данный момент ниже. Добавил  { path: "scripts2/ucf_wheretoopenlink.js", ucfobj: false,  }, в секцию Для докум. окна браузера [ChromeOnly], не работает, это добавлял func: "ucf_where_to_open_link.places();", ни так ни так не идёт

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

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfStylesScripts"];
var UcfStylesScripts = {
    /* ************************▼ Настройки ▼************************ */
    /**
    * Настройки стилей:
    *   path: путь к файлу от папки custom_styles
    *   type: права стиля AGENT_SHEET,  AUTHOR_SHEET или USER_SHEET
    */
    styleschrome: [ // Для докум. всех окон [ChromeOnly]
        // { path: "custom_styles_chrome_author.css", type: "AUTHOR_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "custom_styles_chrome_user.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "special_widget.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Special Widgets
        { path: "auto_hide_sidebar.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Auto Hide Sidebar
    ],
    stylesall: [ // Для всех документов
        { path: "custom_styles_all_agent.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
        // { path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
    ],
    /**
    * Настройки скриптов:
    *   path: путь к скрипту от папки custom_scripts
    *   urlregxp: Адрес где работает скрипт в регулярном выражении, только Для докум. всех окон [ChromeOnly]
    *   ucfobj: true - загружать скрипт в специально созданный объект либо в window, для скриптов В фоне [System Principal] не используется
    *   func: Функция в виде строки которая выполнится при загрузке скрипта, только Для докум. всех окон [ChromeOnly]
    */
    scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию DOMContentLoaded

        ],
        load: [ // По событию load
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "scripts3/favicon_in_urlbar.js", ucfobj: true, },
            { path: "scripts3/restart_item_in_menu.js", ucfobj: true, },
            { path: "scripts3/urlbarhistorydropmarker.js", ucfobj: true, },
            { path: "scripts3/contextmenuopenwith.js", ucfobj: true, }, 
            { path: "scripts3/add_bookmark_to_bookmarks_menu.js", ucfobj: false, },
            { path: "scripts3/pageInfo.js", ucfobj: false, },
            { path: "scripts3/places_addBookmarks.js", ucfobj: false, },
            { path: "scripts3/search_engine_icon.js", ucfobj: false, },
            { path: "scripts3/tabs_focus.js", ucfobj: false, },
            { path: "scripts3/tabstoolbar_doubleclick_opennewtab.js", ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager.uc.js", ucfobj: false, },
            { path: "scripts2/ucf_wheretoopenlink.js", ucfobj: false,  },
        ],
    },
    scriptsallchrome: { // Для докум. всех окон [ChromeOnly]
        domload: [ // По событию DOMContentLoaded
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager2.uc.js", urlregxp: /chrome:\/\/browser\/content\/downloads\/contentAreaDownloadsView\.xhtml/, ucfobj: false, },
        ],
        load: [ // По событию load
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.places();" },
        ],
    },
    scriptsbackground: [ // В фоне [System Principal]
        { path: "custom_script.js", },
    ],
};
    /* ************************▲ Настройки ▲************************ */

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var UcfSSS = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
var preloadSheet = (obj, func) => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        let preload = UcfSSS.preloadSheet(uri, type);
        (obj.sheet = f => {
            try {
                f(preload, type);
            } catch (e) {}
        })(func);
    } catch (e) {
        obj.sheet = () => {};
    }
};
var registerSheet = async obj => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        if (!UcfSSS.sheetRegistered(uri, type))
            UcfSSS.loadAndRegisterSheet(uri, type);
    } catch (e) {}
};

sandro79
Вот так для браузера, но прежде обновите user_chrome.js я там поправил ещё

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

Выделить код

Код:

{ path: "scripts2/ucf_wheretoopenlink.js", ucfobj: false, func: "ucf_where_to_open_link.browser();" },


А это для других докум.
скрытый текст

Выделить код

Код:

{ path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/bookmarksSidebar\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.bookmarksSidebar();" },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/historySidebar\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.historySidebar();" },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.places();" },

Дело в том что нужно разные функции добавлять, а это "ucf_where_to_open_link.places();" только для библиотеки
Иначе этот скрипт пришлось бы делить на 4 части, или в скрипте снова проверять url, а так в каждом документе выполняется разный код при вызове этих функций

Vitaliy V.
Обновил user_chrome.js, пути с функциями прописал, заработало везде. Ну теперь всё в принципе настроено. Для контента тоже добавил, раз есть возможность лишним не будет.
custom_script.js с кнопками и скриптами LinkWinActor и UCFNewTabPage, сменой иконки поиковика, пока по-старинке с загрузчиком оставил. Вечером наверно тоже переподключу по-новому. Огромное Спасибо, буду тестировать.

CustomStylesScripts.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfStylesScripts"];
var UcfStylesScripts = {
    /* ************************▼ Настройки ▼************************ */
    /**
    * Настройки стилей:
    *   path: путь к файлу от папки custom_styles
    *   type: права стиля AGENT_SHEET,  AUTHOR_SHEET или USER_SHEET
    */
    styleschrome: [ // Для докум. всех окон [ChromeOnly]
        // { path: "custom_styles_chrome_author.css", type: "AUTHOR_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "custom_styles_chrome_user.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "special_widget.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Special Widgets
        { path: "auto_hide_sidebar.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Auto Hide Sidebar
    ],
    stylesall: [ // Для всех документов
        { path: "custom_styles_all_agent.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
        // { path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
    ],
    /**
    * Настройки скриптов:
    *   path: путь к скрипту от папки custom_scripts
    *   urlregxp: Адрес где работает скрипт в регулярном выражении, только Для докум. всех окон [ChromeOnly]
    *   ucfobj: true - загружать скрипт в специально созданный объект либо в window, для скриптов В фоне [System Principal] не используется
    *   func: Функция в виде строки которая выполнится при загрузке скрипта, только Для докум. всех окон [ChromeOnly]
    */
    scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию DOMContentLoaded

        ],
        load: [ // По событию load
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "scripts3/favicon_in_urlbar.js", ucfobj: true, },
            { path: "scripts3/restart_item_in_menu.js", ucfobj: true, },
            { path: "scripts3/urlbarhistorydropmarker.js", ucfobj: true, },
            { path: "scripts3/contextmenuopenwith.js", ucfobj: true, }, 
            { path: "scripts3/add_bookmark_to_bookmarks_menu.js", ucfobj: false, },
            { path: "scripts3/pageInfo.js", ucfobj: false, },
            { path: "scripts3/places_addBookmarks.js", ucfobj: false, },
            { path: "scripts3/search_engine_icon.js", ucfobj: false, },
            { path: "scripts3/tabs_focus.js", ucfobj: false, },
            { path: "scripts3/tabstoolbar_doubleclick_opennewtab.js", ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager.uc.js", ucfobj: false, },
            { path: "scripts2/ucf_wheretoopenlink.js", ucfobj: false, func: "ucf_where_to_open_link.browser();" },
        ],
    },
    scriptsallchrome: { // Для докум. всех окон [ChromeOnly]
        domload: [ // По событию DOMContentLoaded
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucjsDownloadsManager2.uc.js", urlregxp: /chrome:\/\/browser\/content\/downloads\/contentAreaDownloadsView\.xhtml/, ucfobj: false, },
        ],
        load: [ // По событию load
            // { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.places();" },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/bookmarksSidebar\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.bookmarksSidebar();" },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/historySidebar\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.historySidebar();" },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, func: "ucf_where_to_open_link.places();" },
        ],
    },
    scriptsbackground: [ // В фоне [System Principal]
        { path: "scripts/add-sound-realtek-app.js", },
        { path: "scripts/Close-Tabs-button.js", },
        { path: "scripts/downloadPauseResumeButton.js", },
        { path: "scripts/ExtensionOptionsMenu.js", },
        { path: "scripts/LinkWinActor.js", },
        { path: "scripts/PotPlayer.js", },
        { path: "scripts/To_switch_proxy.js", },
        { path: "scripts/ucf-copyURL.js", },
        { path: "scripts/UCFNewTabPage.js", },
        { path: "scripts/undo_closetab_button.js", },
        { path: "scripts/yandex@search.js", },
    ],
};
    /* ************************▲ Настройки ▲************************ */

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var UcfSSS = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
var preloadSheet = (obj, func) => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        let preload = UcfSSS.preloadSheet(uri, type);
        (obj.sheet = f => {
            try {
                f(preload, type);
            } catch (e) {}
        })(func);
    } catch (e) {
        obj.sheet = () => {};
    }
};
var registerSheet = async obj => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        if (!UcfSSS.sheetRegistered(uri, type))
            UcfSSS.loadAndRegisterSheet(uri, type);
    } catch (e) {}
};

Переподключил скрипты scriptsbackground: [ // В фоне [System Principal] Всё подхватилось и работает. Код под спойлером обновил.


CustomStylesScriptsChild.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfCustomStylesScriptsChild"];
var UcfStylesScripts = {
    /* ************************▼ Настройки ▼************************ */
    /**
    * Настройки стилей:
    *   path: путь к файлу от папки custom_styles
    *   type: права стиля AGENT_SHEET,  AUTHOR_SHEET или USER_SHEET
    */
    stylescontent: [
        // { path: "custom_styles_content_author.css", type: "AUTHOR_SHEET", sheet(f) { preloadSheet(this, f); }, },
        { path: "custom_styles_content_user.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },
    ],
    /**
    * Настройки скриптов:
    *   path: путь к скрипту от папки custom_scripts
    *   urlregxp: Адрес где работает скрипт, в регулярном выражении
    *   func: Функция в виде строки которая выполнится при загрузке скрипта
    */
    scriptscontent: {
        DOMWindowCreated: [ // По событию DOMWindowCreated
            // { path: "example_all_about.js", urlregxp: /about:.*/, },
        ],
        pageshow: [ // По событию pageshow
            // { path: "example_downloads.js", urlregxp: /about:downloads/, },
            { path: "scripts2/ucf_wheretoopenlink.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, func: "ucf_where_to_open_link.places();" },
            { path: "scripts2/ucjsDownloadsManager2.uc.js",  urlregxp: /about:downloads/, },
        ],
    },
};
    /* ************************▲ Настройки ▲************************ */
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var UcfSSS = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
var preloadSheet = async (obj, func) => {
    try {
        let uri = Services.io.newURI(`chrome://user_chrome_files/content/custom_styles/${obj.path}`);
        let type = UcfSSS[obj.type];
        let preload = await UcfSSS.preloadSheetAsync(uri, type);
        (obj.sheet = f => {
            try {
                f(preload, type);
            } catch (e) {}
        })(func);
    } catch (e) {
        obj.sheet = () => {};
    }
};
class UcfCustomStylesScriptsChild extends JSWindowActorChild {
    actorCreated() {
        var win = this.contentWindow;
        var href = this.href = win?.location.href;
        if (!href) return;
        var { addSheet } = win.windowUtils;
        for (let s of UcfStylesScripts.stylescontent)
            s.sheet(addSheet);
    }
    handleEvent(e) {
        var href = this.href;
        if (!href || href === "about:blank") return;
        for (let s of UcfStylesScripts.scriptscontent[e.type]) {
            try {
                if (s.urlregxp.test(href)) {
                    let win = this.contentWindow;
                    Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${s.path}`, win, "UTF-8");
                    if (s.func)
                        new win.Function(s.func).apply(win, null);
                }
            } catch (e) {}
        }
    }
}

Vitaliy V.
Спасибо за обновление. Уведомления пропали у этого скрипта, как их вернуть? Сейчас подключен в CustomStylesScripts.jsm, в scriptsbackground.

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

Выделить код

Код:

try {
    ((img, preventClearThumbs) => {
        CustomizableUI.createWidget({
            id: "bt-clear-part-history",
            label: "Очистить историю",
            tooltiptext: "Очистить историю",
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onCreated: function(bt) {
                bt.image = img;
            },
            onCommand: function(event) {
                var win = event.target.ownerDocument.defaultView;
                var itemsToClear = [
                    "cookies",
                    "history",
                    "formdata",
                    "sessions",
                    "cache",
                 // "downloads",
                 // "offlineApps",
                 // "openWindows",
                    "pluginData",
                 // "siteSettings",
                ];
                var range = win.Sanitizer.getClearRange(0); // Диапазон очистки, 0 = все, 1,2,3 = часы, 4 = сегодня
                win.Sanitizer.sanitize(itemsToClear, {
                    ignoreTimespan: !range,
                    range,
                }).then(() => {
                    var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
                    alertsService.showAlertNotification(img, "Данные очищены!", "", false);
                    win.setTimeout(()=> alertsService.closeAlert(), 2000);
                });
            }
        });
    })("data:image/png;base64,i...", null);
} catch(e) {}


В CustomStylesScripts.jsm присутстуют раскомментированные строки:
            { path: "translatetextorpage.js", ucfobj: true, },
            { path: "contextproxy.js", ucfobj: true, },
но таких скриптов в комплекте нет.

_zt пишет

Уведомления пропали у этого скрипта, как их вернуть? ... "data:image/png;base64,i..."

Иконки то нет поэтому и не показывает нет не поэтому, значит у вас ошибка возникает во время очистки, консоль смотрите что пишет

_zt пишет

В CustomStylesScripts.jsm присутстуют раскомментированные строки:

Это я из FullTheme забыл убрать прежде чем отправить


Обновил в FullTheme файлы: common.css, CustomStylesScripts.jsm, CustomStylesScriptsChild.jsm
и в UserChromeFiles файлы: user_chrome.js, CustomStylesScripts.jsm, CustomStylesScriptsChild.jsm

Vitaliy V.

Error: Could not get children of file(...\LocalAppData\Temp\thumbnails) because it does not exist PromiseWorker.jsm:106

На старом то ucf этот скрипт уведомление выводит. Иконку я здесь из кода удалил так как много места занимает.
   
Проверил на старом ucf, уведомление есть. thumbnails отключаю уже много лет и уведомлениям это никогда не мешало, да и с удаленным browser.pagethumbnails.capturing_disabled, в этой версии ucf, уведомлений нет, а папка все равно по тому пути не создается.

_zt пишет

Error: Could not get children of file(...\LocalAppData\Temp\thumbnails) because it does not exist PromiseWorker.jsm:106

На старом то ucf этот скрипт уведомление выводит.

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

Vitaliy V. обновите пожалуйста вот эту кнопку

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

Выделить код

Код:

//Восстановить фавиконки закладок
try {
    (() => {
        var id = "ucf-loads-favicons",
        label = "Восстановить фавиконки",
        tooltiptext = "Восстановить фавиконки закладок",
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='32' width='16' viewBox='0 0 48 96'><g><path d='M 2.438,0 C 1.087,0 0,1.088 0,2.438 V 45.56 C 0,46.91 1.087,48 2.438,48 H 45.56 C 46.91,48 48,46.91 48,45.56 V 2.438 C 48,1.088 46.91,0 45.56,0 Z' style='fill:rgb(243, 135, 37);fill-opacity:1;' /><path style='opacity:0.25;fill:black;' d='M 14,45 17,48 H 45.5 C 47,47.9 47.9,47 48,45.5 V 17.6 L 33.9,3.5 Z'/><path style='fill:white;' d='M 15,3 C 14.4,3 14,3.4 14,4 V 45 L 24,35 34,45 V 4 C 34,3.4 33.6,3 33,3 Z' /><path d='M 2.44,48 C 1.09,48 0,49.1 0,50.4 V 93.6 C 0,94.9 1.09,96 2.44,96 H 45.6 C 46.9,96 48,94.9 48,93.6 V 50.4 C 48,49.1 46.9,48 45.6,48 Z' style='fill:rgb(209, 8, 3);fill-opacity:1;' /><path style='opacity:0.25;fill:black;' d='M 14,93 17,96 H 45.5 C 47,95.9 47.9,95 48,93.5 V 65.6 L 33.9,51.5 Z'/><path style='fill:white;' d='M 15,51 C 14.4,51 14,51.4 14,52 V 93 L 24,83 34,93 V 52 C 34,51.4 33.6,51 33,51 Z' /></g></svg>",
        alertimg = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='48' width='48' viewBox='0 0 48 48'><g><path d='M 2.438,0 C 1.087,0 0,1.088 0,2.438 V 45.56 C 0,46.91 1.087,48 2.438,48 H 45.56 C 46.91,48 48,46.91 48,45.56 V 2.438 C 48,1.088 46.91,0 45.56,0 Z' style='fill:rgb(243, 135, 37);fill-opacity:1;' /><path style='opacity:0.25;fill:black;' d='M 14,45 17,48 H 45.5 C 47,47.9 47.9,47 48,45.5 V 17.6 L 33.9,3.5 Z'/><path style='fill:white;' d='M 15,3 C 14.4,3 14,3.4 14,4 V 45 L 24,35 34,45 V 4 C 34,3.4 33.6,3 33,3 Z' /></g></svg>",
        maxrequests = 50, // Максимальное количество параллельных запросов
        maxtimeout = 30, // Длительность до прерывания запроса в секундах
        alertnotification = true; // Уведомление о завершении поиска фавиконок для закладок

        if (!("PlacesUtils" in this))
            ChromeUtils.defineModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
        var favicons = {
            running: false,
            async search() {
                if (this.running) return;
                this.running = true;
                for(let {node} of CustomizableUI.getWidget(id).instances)
                    node.style.setProperty("-moz-image-region", "rect(16px, 16px, 32px, 0px)", "important");
                var urlsList = [];
                var root = await PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid);
                var convert = (node, url) => {
                    if (node.children)
                        node.children.map(child => convert(child));
                    else if ((url = node.uri) && /^(?:http|ftp|file)s?:/.test(url))
                        urlsList.push(url);
                }
                convert(root);
                Promise.all(urlsList.map(this.getFaviconForPage)).then(results => this.sliceResults(results.filter(url => url !== null)));
            },
            get AlertsService() {
                delete this.AlertsService;
                return this.AlertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
            },
            sliceResults(results) {
                var maxlength = results.length;
                this.favmaxtimeout = maxtimeout * 1000;
                var forPageFavicon = (start, end) => {
                    var endlength = maxlength - end, startend;
                    if (endlength > 0)
                        startend = results.slice(start, end);
                    else
                        startend = results.slice(start, maxlength);
                    Promise.all(startend.map(this.getPageFavicon, this)).then(() => {
                        if (endlength > 0)
                            forPageFavicon(end, end + maxrequests);
                        else {
                            for(let {node} of CustomizableUI.getWidget(id).instances)
                                node.style.setProperty("-moz-image-region", "rect(0px, 16px, 16px, 0px)", "important");
                            if (alertnotification) {
                                try {
                                    this.AlertsService.showAlertNotification(alertimg, "Поиск фавиконок", "Завершено!", false);
                                } catch(e) {}
                            }
                            this.running = false;
                        }
                    });
                };
                forPageFavicon(0, maxrequests);
            },
            getFaviconForPage(siteURI) {
                return new Promise(resolve => {
                    try {
                        siteURI = Services.io.newURI(siteURI);
                    } catch(e) {
                        resolve(null);
                    }
                    PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => {
                        if (uri === null)
                            resolve(siteURI);
                        else
                            resolve(null);
                    });
                });
            },
            getPageFavicon(siteURI) {
                return new Promise(resolve => {
                    var req = new XMLHttpRequest();
                    if (!req) {
                        resolve();
                        return;
                    }
                    req.mozBackgroundRequest = true;
                    req.open("GET", siteURI.spec, true);
                    req.responseType = "document";
                    req.overrideMimeType("text/html");
                    req.timeout = this.favmaxtimeout;
                    req.onload = () => {
                        resolve();
                        var favURI = `${siteURI.prePath}/favicon.ico`, doc = req.responseXML;
                        if (doc !== null) {
                            let link = doc.querySelector("head link[href][rel~='icon']");
                            if (link !== null)
                                favURI = link.href;
                        }
                        try {
                            PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, Services.io.newURI(favURI), false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null, Services.scriptSecurityManager.getSystemPrincipal());
                        } catch(e) {}
                    };
                    req.onabort = () => {
                        resolve();
                    };
                    req.onerror = () => {
                        resolve();
                        req.abort();
                    };
                    req.ontimeout = () => {
                        resolve();
                        req.abort();
                    };
                    req.send(null);
                });
            },
        };
        CustomizableUI.createWidget({
            id: id,
            label: label,
            tooltiptext: tooltiptext,
            localized: false,
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onCreated: function(btn) {
                btn.style.setProperty("-moz-image-region", !favicons.running ? "rect(0px, 16px, 16px, 0px)" : "rect(16px, 16px, 32px, 0px)", "important");
                btn.style.setProperty("list-style-image", `url("${img}")`, "important");
            },
            onCommand: function(e) {
                favicons.search();
            },
        });
    })();
} catch(e) {}

Vitaliy V.
Профиль один и тот же, я только его собрал и тут вы со своим обновлением. :) (шутка) Я его пересобрал, на тех же скриптах, стилях, расширениях и с теми же конфигами, за исключением вашего config.js. Отвалился стиль для адресной строки из userChrome.css, пришлось его перенести в CustomStylesScripts.jsm в секцию styleschrome с правами USER_SHEET, никак по другому он не заработал. Скрипты из custom_script.js я все переподключил в CustomStylesScripts.jsm в секцию scriptsbackground, в том числе и обсуждаемый. Скрипты custom_script_win.js оставил на месте, так как решил не заморачиваться. Скриптов custom_script_all_win.js у меня не оказалось. Т.е. две проблемы стиль и этот скрипт. Ясен пень, что ucf виноват.
   
Но оказалось все проще, смотрите мультики:

Находим виновника

01.1632267720.gif

И отправляем его в ссылку

02.1632267763.gif
А вообще вы молодец, а виноват во всем я. Я же его выпросил. :)

Vitaliy V.
А можно как-то вернуть в новый UCF возможность использования стилей для веб-страниц через файл custom_styles_content_user.css?
Может опцией, если надо может, чтоб включить можно было в CustomStylesScriptsChild.jsm. У меня в старом комплекте в custom_style_user.css добавлено несколько кодов для страниц, в частности, очень нужный для меня :root { text-decoration-skip-ink: none;}, для этого форума стиль.
Можно конечно включить toolkit.legacyUserProfileCustomizations.stylesheets, добавить в папку chrome файл userContent.css и по-старинке использовать, но это не работает в безопасном режиме, да и вообще... Вообще удобно в старом UCF то, что стили и для интерфейса и для страниц можно использовать/подгонять в одном файле css.
Этот код, только с regexp, тоже использую для веб-страниц

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

Выделить код

Код:

@-moz-document regexp("(?:https?|ftp|file):\/\/.*") {
input, textarea {
    /* border: 1px solid rgba(131,137,150,.5); */
    outline: 1px solid transparent;
}
}

В новом комплекте непонятно как всё вышеперечисленное запустить...

sandro79 пишет

в старом комплекте в custom_style_user.css

А в новом в custom_styles_all_user.css

Dumby пишет

А в новом в custom_styles_all_user.css

Да, работает там, в трёх соснах заблудился. Спасибо за подсказку :beer: Вот надо же, так облажаться :dumb:


И chrome и content работает, как и в старом. Всё отлично.

egorsemenov06
У вас столько кнопок Add Toolbar Buttons
не проще его и использовать

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

Выделить код

Код:

(async () => {
    var id = "ucf-loads-favicons",
    label = "Восстановить фавиконки",
    tooltiptext = "Восстановить фавиконки закладок",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M3.6.6v14.8L8 11l4.4 4.4V.6z'/></svg>",
    maxrequests = 50, // Максимальное количество параллельных запросов
    maxtimeout = 30, // Длительность до прерывания запроса в секундах
    alertnotification = true; // Уведомление о завершении поиска фавиконок для закладок

    var favicons = {
        _favrunning: false,
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        showAlert(title, val) {
            try {
                this.alertsService.showAlertNotification(img, title, val, false);
            } catch(e) {}
        },
        favSearchStart() {
            if (this._favrunning) return;
            this._favrunning = true;
            this.callWithEachWindow(id, {fill: "color-mix(in srgb, currentColor 20%, #e31b5d)"});
            PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid).then(root => {
                var urlsList = [];
                var convert = (node, url) => {
                    if (node.children)
                        node.children.map(convert);
                    else if ((url = node.uri) && /^(?:https?|ftp|file):/.test(url))
                        urlsList.push(url);
                };
                convert(root);
                var favForPage = siteURI => {
                    return new Promise(resolve => {
                        try {
                            siteURI = Services.io.newURI(siteURI);
                        } catch(e) {
                            resolve(null);
                        }
                        PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => {
                            if (uri === null)
                                resolve(siteURI);
                            else
                                resolve(null);
                        });
                    });
                };
                Promise.all(urlsList.map(favForPage)).then(results => this.favSearchResults(results.filter(url => url !== null)));
            });
        },
        favComplete(favsuccesslength, favmaxlength) {
            this._favrunning = false;
            this.callWithEachWindow(id, {fill: ""});
            if (alertnotification)
                this.showAlert("Поиск фавиконок", `Успешно обработано - ${favsuccesslength}, не удалось обработать - ${favmaxlength - favsuccesslength}`);
        },
        favSearchResults(results) {
            var favmaxlength = results.length;
            var favsuccesslength = 0;
            if (!favmaxlength) {
                this.favComplete(0, 0);
                return;
            }
            var favmaxtimeout = maxtimeout * 1000;
            var _favmaxlength = favmaxlength;
            var splice = results.splice(0, maxrequests);
            var favSearchPage = siteURI => {
                (new Promise(resolve => {
                    try {
                        let req = new XMLHttpRequest();
                        req.mozBackgroundRequest = true;
                        req.open("GET", siteURI.spec, true);
                        req.responseType = "document";
                        req.overrideMimeType("text/html");
                        req.timeout = favmaxtimeout;
                        req.onload = () => {console.log(req)
                            try {
                                let doc = req.responseXML, favURI;
                                if (doc) {
                                    let links = doc.querySelectorAll("head link[href][rel~='icon']"), lastlink, is16, is32, isany;
                                    for (let link of links) {
                                        if (link.sizes.length === 1) {
                                            let size = link.sizes[0];
                                            if (/any/i.test(size))
                                                isany = link;
                                            else if (/32x32/i.test(size))
                                                is32 = link;
                                            else if (/16x16/i.test(size))
                                                is16 = link;
                                        }
                                        lastlink = link;
                                    }
                                    links = isany || is32 || is16 || lastlink;
                                    if (links)
                                        favURI = links.href;
                                }
                                if (!favURI)
                                    favURI = `${req.responseURL ? Services.io.newURI(req.responseURL).prePath : siteURI.prePath}/favicon.ico`;
                                let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
                                let request = PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, Services.io.newURI(favURI), false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
                                    onComplete() {
                                        ++favsuccesslength;
                                        resolve();
                                        timer.cancel();
                                        timer = null;
                                        request = null;
                                    },
                                }, Services.scriptSecurityManager.getSystemPrincipal());
                                if (!request) {
                                    resolve();
                                    timer = null;
                                    return;
                                }
                                timer.initWithCallback(() => {
                                    resolve();
                                    try {
                                        request.cancel();
                                    } catch(e) {}
                                    timer = null;
                                    request = null;
                                }, favmaxtimeout, timer.TYPE_ONE_SHOT);
                            } catch(e) {
                                resolve();
                            }
                        };
                        req.onabort = () => {
                            resolve();
                        };
                        req.onerror = req.ontimeout = () => {
                            resolve();
                            req.abort();
                        };
                        req.send(null);
                    } catch(e) {
                        resolve();
                    }
                })).then(() => {
                    if (!(--_favmaxlength)) {
                        this.favComplete(favsuccesslength, favmaxlength);
                        return;
                    }
                    if (!results.length) return;
                    favSearchPage(results.shift());
                });
            };
            splice.map(favSearchPage);
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
            if (favicons._favrunning)
                btn.style.setProperty("fill", "color-mix(in srgb, currentColor 20%, #e31b5d)");
        },
        onCommand(e) {
            favicons.favSearchStart();
        },
    });
})();

_zt пишет

А вообще вы молодец, а виноват во всем я. Я же его выпросил

Это вы молодец нашли баг
Я предполагал что DOMContentLoaded позновато для некоторых стилей (это касается только Для докум. всех окон [ChromeOnly]) однако проблем не обнаружил.
С тем же алертом у меня он появлялся, но как я заметил позже клики на нем не работают.


Вот теперь верните стиль в main_window.css
и обновите файлы: config.js, user_chrome.js, CustomStylesScripts.jsm, CustomStylesScriptsChild.jsm, custom_script_win.js
чтобы заменить DOMContentLoaded на MozBeforeInitialXULLayout

Vitaliy V. пишет

egorsemenov06
У вас столько кнопок Add Toolbar Buttons
не проще его и использовать

У меня их только три штуки из АТВ.Огромное Вам Спасибо!!!!!

Vitaliy V.
Починилось.
   
Нашел еще несколько проблем, давайте по порядку, так как возможно они взаимосвязаны:
2021.1632357561.png
Вот я просто слов без мата не нахожу, что бы описать этот цвет, именно в этом диалоге оно где, в common.css ?

@media (-moz-toolbar-prefers-color-scheme: dark) {
:root, dialog, menu-button, login-filter, login-item, login-intro, login-list, fxaccounts-button, remove-logins-dialog,
    import-error-dialog, import-summary-dialog, confirmation-dialog, info-item, message-bar, addon-updates-message {
>>
    --in-content-primary-button-background: var(--blue-50) !important;

   
Если да, то тогда оно не работает, цвет не меняется. В --blue-50 тоже.
   
После переноса в userChrome.css правило работает.

_zt
Поделись, пожалуйста этой закладкой Старый about:config
У меня даже нет такой папки chrome://user_chrome_files/content/aboutconfig/

rubel
https://forum.mozilla-russia.org/viewto … 75#p789675


Add,

скрин
e0101f473944.png

kokoss
OK, спасибо. все получилось.

_zt пишет

оно где, в common.css ?

Нет в common.css только контент.
В common_win.css я некоторые окна добавил, это туда же url("chrome://browser/content/places/bookmarkProperties.xhtml")
Ну я потом добавлю другие окна где есть <dialog>


Вроде готово, обновил FullTheme
и UserChromeFiles ещё раз обновил надеюсь пока больше не буду трогать если только баг всплывет.


upd: обновил Sidebar Tabs и Тултипы с URL https://forum.mozilla-russia.org/viewto … 02#p792702

Vitaliy V.
Починилось.Спасибо.
   
Можете объяснить?

Прошлая версия
02.1632481807.gif

_zt пишет

Можете объяснить?

А что тут объяснять
Отключили переменную в chrome://global/skin/in-content/common.css
для темной темы
@media (-moz-toolbar-prefers-color-scheme: dark)
Но переменная для светлой темы осталась именно она и дает этот синий цвет
а не от FullTheme. Ну или это вы там что-то наподключали, но такого цвета #2b71e4 в моей теме нет.
Впрочем gif же у вас цвет искажен скорее всего

Vitaliy V.
Искажен. В общем, ничего не понял, но спасибо что исправили. Я там ничего не менял в пространствах имен. В общем, спасибо.

Dumby
Помогите пожалуйста. Использую Ваш скрипт закрытия всех вкладок кроме активной, только со своей иконкой

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

Выделить код

Код:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACNUlEQVQ4jY2TT08aQRjG59LL7mk/gxdvXmw/QD+MX6AePNhk0jThMGljEzLpxio0arQWMGpqomXBP7TQ3YpQ5J8CQf65C7vUKK61Hp4eFhMwBPskc5iZzO9987zPENIV9WyOTkxMPCGPaJIxcdq7NdJ3SJcj4+/jdns+WvMPg1AqC+5vujKjdhrUEx4lhBDyciE0NqPZbU8G8BWAz9pgyCRj4oKqK0t5wJsF5MPr+rS8NkKmOJfc301tMQesl4BQFdhJ9UMolYX1pK5slYGNEuAvAF7VCFJKBUIIIS7OpYW4pX4pA/t14KgJRLMOhDEm7mZ1JaYDB3UgXAXWkkaQUlnoa9HFubSRMrXoOXBsAuVLIFGoBuJFXcn/BtIWkGgBoUxP5YdycS7tnVhqvg3UO0D7D9CygUbHAaoFI0hlefDje3HOpWTZ1Jo2cHMH2HeAeQMkS0Mq94oxJv4qG4p5A1z9ddb5NRDL1XyP5oTKshA71ZXiBVC5dFpv2kDlCshYwHZqCIQxJm4f64pmOGalLUAt6ErqzFKLF0CyBUQagO9wAIRSWViN68rOGRCqOeNSum5zzqUfRVP7aQDhGrBZAuaiPZBJxsS5mK6snDgB2SgBgUT/nDnnUjBnqV8rQKAIfMwCM3tdyOul/Wfuo9vObNqJqEcd7LaLc8mfNLVPp8CHNPBWta0X8qbzH+jq4fM32m3n3cGAhD2AzGuWyuO2RZcj432XU4u7T/9nzi7OpVcrobH7/T9AEdt1RBP8kgAAAABJRU5ErkJggg==

Недавно решил заменить её на svg из допротоновских версий chrome://browser/skin/panel-icon-cancel.svg
Закинул в папку svg комплекта эту иконку, чтоб белой была на тёмном фоне без надобности включения svg.context-properties.content.enabled.
Прописал путь к иконке chrome://user_chrome_files/content/custom_styles/svg/panel-icon-cancel.svg, иконка появилась, всё работает нормально
скрытый текст

Выделить код

Код:

CustomizableUI.createWidget({
	id: "Close-Tabs-button",
	label: "Закрыть другие вкладки",
	tooltiptext: "Закрыть другие вкладки",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn._handleClick = this.close;
		btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/panel-icon-cancel.svg");
	},
	close() {
		var gb = this.ownerGlobal.gBrowser;
		gb.removeAllTabsBut(gb.selectedTab);
	}
});

Но вот только маленькая она стала, меньше чем та что была. Я её чуть увеличил стилем
скрытый текст
Image_001.png

Выделить код

Код:

#Close-Tabs-button > image {
    padding: 2px !important;
}

Может можно добавить как-то тоже стиль выше в код скрипта, чтоб всё в одном месте было?

sandro79
У меня снова предложение чуть поперёк просьбы.
Там <svg> 32x32, а <path> 20x20 по-центру,
то есть как бы отступ 6px со всех сторон (поэтому выглядит маленькой).


Но можно во viewBox подогнать x, y, width и height.
Допустим, максимально. Меняем в самой svg'ке viewBox="0 0 32 32"
на viewBox="6 6 20 20" и отступа не будет совсем, тогда будет выглядеть крупнее.
Если положительного результата не получится, дай знать, полезу в js-код.

Dumby пишет

Допустим, максимально. Меняем в самой svg'ке viewBox="0 0 32 32"
на viewBox="6 6 20 20" и отступа не будет совсем, тогда будет выглядеть крупнее

6 6 20 20 всё-же крупновато получается. Но попробовал подрегулировать до viewBox="5 5 22 22", почти как с оригинальной svg и стилем, но чуть всё-равно крупнее. А 6 6 23 23 попробовал - размер визуально вроде не отличается, но сдвигается вверх и влево. Никак не получается один к одному подогнать.
Dumby
Ну если можно, добавьте пожалуйста css в js-код, ну почти подогнал как с оригинальной и стилем, но крупновато всё же с viewBox="5 5 22 22".
А за метод правки и полезную информацию Большое Спасибо, а то я пытался только цифры 32 менять в коде svg, теперь буду хоть это знать.

sandro79 пишет

сдвигается вверх и влево

Вот все центрированные варианты

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

Выделить код

Код:

viewBox="0 0 32 32"
viewBox="1 1 30 30"
viewBox="2 2 28 28"
viewBox="3 3 26 26"
viewBox="4 4 24 24"
viewBox="5 5 22 22"
viewBox="6 6 20 20"


Хотя, может можно не целые числа, не проверял.

добавьте пожалуйста css в js-код

Допустим, в атрибут style. Может не сработать,
если user или agent стилями приколочено, тогда снова дай знать.

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

Выделить код

Код:

CustomizableUI.createWidget({
	id: "Close-Tabs-button",
	label: "Закрыть другие вкладки",
	tooltiptext: "Закрыть другие вкладки",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn.render = this.render;
		btn._handleClick = this.close;
		btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/panel-icon-cancel.svg");
	},
	render() {
		delete this.render;
		this.render();
		this.icon.style.setProperty("padding", "2px", "important");
	},
	close() {
		var gb = this.ownerGlobal.gBrowser;
		gb.removeAllTabsBut(gb.selectedTab);
	}
});

Dumby пишет

Вот все центрированные варианты

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

Допустим, в атрибут style. Может не сработать, если user или agent стилями приколочено, тогда снова дай знать

Отлично всё, сработало! Теперь один в один. Огромное Вам Спасибо за помощь :beer:

sandro79
Всё таки проверил дробные числа. Похоже работает.
Вот с шагом в одну десятую. Ну, это я уже просто так, на интерес.

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

Выделить код

Код:

viewBox="0 0 32 32"
viewBox="0.1 0.1 31.8 31.8"
viewBox="0.2 0.2 31.6 31.6"
viewBox="0.3 0.3 31.4 31.4"
viewBox="0.4 0.4 31.2 31.2"
viewBox="0.5 0.5 31 31"
viewBox="0.6 0.6 30.8 30.8"
viewBox="0.7 0.7 30.6 30.6"
viewBox="0.8 0.8 30.4 30.4"
viewBox="0.9 0.9 30.2 30.2"
viewBox="1 1 30 30"
viewBox="1.1 1.1 29.8 29.8"
viewBox="1.2 1.2 29.6 29.6"
viewBox="1.3 1.3 29.4 29.4"
viewBox="1.4 1.4 29.2 29.2"
viewBox="1.5 1.5 29 29"
viewBox="1.6 1.6 28.8 28.8"
viewBox="1.7 1.7 28.6 28.6"
viewBox="1.8 1.8 28.4 28.4"
viewBox="1.9 1.9 28.2 28.2"
viewBox="2 2 28 28"
viewBox="2.1 2.1 27.8 27.8"
viewBox="2.2 2.2 27.6 27.6"
viewBox="2.3 2.3 27.4 27.4"
viewBox="2.4 2.4 27.2 27.2"
viewBox="2.5 2.5 27 27"
viewBox="2.6 2.6 26.8 26.8"
viewBox="2.7 2.7 26.6 26.6"
viewBox="2.8 2.8 26.4 26.4"
viewBox="2.9 2.9 26.2 26.2"
viewBox="3 3 26 26"
viewBox="3.1 3.1 25.8 25.8"
viewBox="3.2 3.2 25.6 25.6"
viewBox="3.3 3.3 25.4 25.4"
viewBox="3.4 3.4 25.2 25.2"
viewBox="3.5 3.5 25 25"
viewBox="3.6 3.6 24.8 24.8"
viewBox="3.7 3.7 24.6 24.6"
viewBox="3.8 3.8 24.4 24.4"
viewBox="3.9 3.9 24.2 24.2"
viewBox="4 4 24 24"
viewBox="4.1 4.1 23.8 23.8"
viewBox="4.2 4.2 23.6 23.6"
viewBox="4.3 4.3 23.4 23.4"
viewBox="4.4 4.4 23.2 23.2"
viewBox="4.5 4.5 23 23"
viewBox="4.6 4.6 22.8 22.8"
viewBox="4.7 4.7 22.6 22.6"
viewBox="4.8 4.8 22.4 22.4"
viewBox="4.9 4.9 22.2 22.2"
viewBox="5 5 22 22"
viewBox="5.1 5.1 21.8 21.8"
viewBox="5.2 5.2 21.6 21.6"
viewBox="5.3 5.3 21.4 21.4"
viewBox="5.4 5.4 21.2 21.2"
viewBox="5.5 5.5 21 21"
viewBox="5.6 5.6 20.8 20.8"
viewBox="5.7 5.7 20.6 20.6"
viewBox="5.8 5.8 20.4 20.4"
viewBox="5.9 5.9 20.2 20.2"
viewBox="6 6 20 20"

Dumby пишет

Всё таки проверил дробные числа. Похоже работает. Вот с шагом в одну десятую. Ну, это я уже просто так, на интерес

Да, это работает. viewBox="4.7 4.7 22.6 22.6" подошло идеально, ну я не увидел визуально разницы. Спасибо Большое за таблицу. Тоже пригодится в дальнейшем.
Я ещё вчера заменил кнопки для окна загрузок и соответственно библиотеки на эту же(panel-icon-cancel.svg) и panel-icon-retry.svg из 78 [firefox], и там они тоже мелковаты получились, да и в 78 такие же были. panel-icon-cancel.svg для нового скрипта сделаю дубликат с др. именем, а для окон загрузок подберу уже из дробных. Правда, чуть раньше уже пробовал с целыми, с кнопкой отмены нормально всё, а вот кнопку panel-icon-retry.svg начинает коробить - круглая стрелка квадратной становится. Ну буду экспериментировать, не получится, ну там и не столь важно в принципе. Спасибо за помощь!


Почти получилось с кнопкой повтора с viewBox="4.7 4.7 22.6 22.6", округлости чуть снизу не хватает, но попробую ещё с другими числами.


Да так и оставлю в окнах загрузок, нормально.

Vitaliy V.
А как теперь тултипы подключить №10124?

voqabuhe
раскомментируйте эту строку https://github.com/VitaliyVstyle/Vitali … ts.jsm#L16
стиль добавляйте соответственно в custom_styles_all_agent.css

Vitaliy V.
Спасибо, подключились.

Vitaliy V.
Спасибо за обновлённый вариант urlbarhistorydropmarker. Тоже его забрал.
В старом варианте пропал фон наведения в 92+ и значок сдвинулся чуть вправо(но я его вернул на место правда), а тут всё с этим в порядке.
Но скрипт для значка "Копировать ссылку" оставлю, привык к нему за три с лишним года.
Виталий, а нельзя ли ещё добавить в urlbarhistorydropmarker действие "Обновить текущую страницу" по СКМ? Вообще было бы супер - три в одном!

sandro79
Сомнительное удобство обновлять страницу колесом, но добавил
в принципе можно на другую функцию заменить здесь
                    if (e.button === 1) {
                        BrowserReload();
                        return;
                    }

Vitaliy V. пишет

Сомнительное удобство обновлять страницу колесом, но добавил...

Ну да. Но ничего так, вполне удобно. Колесо у меня работает отлично.
Да и хочется чего-то необычного испробовать. Всё работает отлично. Огромное Спасибо :beer:


Добавлю себе в пост, чтоб не потерять, три варианта скрипта: старый с правленным мной под Протон адресом для иконки, новый без обновления по СКМ, и новый с обновлением страницы по СКМ

скрытый текст
browser.urlbar.suggest.history - true
browser.urlbar.suggest.topsites - false
скрытый текст
Допротоновский адрес для иконки chrome://global/skin/icons/arrow-dropdown-16.svg В версиях 92+ этот скрипт лучше не использовать

Выделить код

Код:

(this.urlbarhistorydropmarker = {
            dropmarker: null,
            provider: null,
            get style() {
                delete this.style;
                return this.style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #urlbar .urlbar-history-dropmarker {
                        list-style-image: url("chrome://global/skin/icons/arrow-down.svg");
                        transition: opacity 0.15s ease;
                    }
                    #urlbar[switchingtabs] > #urlbar-input-container > .urlbar-history-dropmarker {
                        transition: none;
                    }
                    #urlbar[usertyping] > #urlbar-input-container > .urlbar-history-dropmarker {
                        display: none;
                    }
                    #nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container:not(:hover) > #urlbar:not([focused]) > #urlbar-input-container > .urlbar-history-dropmarker {
                        opacity: 0;
                    }
                `);
            },
            init(that) {
                Services.prefs.addObserver("browser.urlbar.suggest.history", this);
                Services.prefs.addObserver("browser.urlbar.suggest.topsites", this);
                that.unloadlisteners.push("urlbarhistorydropmarker");
                var {UrlbarProviderTopSites: provider} = {UrlbarProviderTopSites: this.provider} = ChromeUtils.import("resource:///modules/UrlbarProviderTopSites.jsm");
                if (!provider.orig_PRIORITY) {
                    provider.orig_PRIORITY = provider.ucf_PRIORITY = provider.PRIORITY;
                    delete provider.constructor.prototype.PRIORITY;
                    Object.defineProperty(provider.constructor.prototype, "PRIORITY", {
                        enumerable: true,
                        get() {
                            var priory = this.ucf_PRIORITY;
                            this.ucf_PRIORITY = this.orig_PRIORITY;
                            return priory;
                        },
                        set(val) {
                            this.ucf_PRIORITY = val;
                        },
                    });
                }
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true))
                   this.createDropmarker();
            },
            createDropmarker() {
                var fragment = MozXULElement.parseXULToFragment(`<image class="urlbar-history-dropmarker urlbar-icon chromeclass-toolbar-additional" role="button" tooltiptext="Показать историю"/>`);
                var dropmarker = this.dropmarker = fragment.firstElementChild;
                document.querySelector("#urlbar #page-action-buttons").before(fragment);
                dropmarker.addEventListener("mousedown", this);
                windowUtils.loadSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            removeDropmarker() {
                this.dropmarker.removeEventListener("mousedown", this);
                this.dropmarker.remove();
                this.dropmarker = null;
                windowUtils.removeSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            destructor() {
                if (this.dropmarker)
                    this.dropmarker.removeEventListener("mousedown", this);
                Services.prefs.removeObserver("browser.urlbar.suggest.history", this);
                Services.prefs.removeObserver("browser.urlbar.suggest.topsites", this);
            },
            observe() {
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true) && !this.dropmarker)
                    this.createDropmarker();
                else if (this.dropmarker)
                    this.removeDropmarker();
            },
            handleEvent(event) {
                event.preventDefault();
                event.stopPropagation();
                if (gURLBar.view.isOpen)
                    gURLBar.view.close();
                else {
                    this.provider.PRIORITY = 0;
                    gURLBar.focus();
                    gURLBar.startQuery({
                        allowAutofill: false
                    });
                }
            }
        }).init(this);

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

Выделить код

Код:

(this.urlbarhistorydropmarker = {
            // -- Настройки -->
            hidewhenusertyping: false, // скрывать dropmarker при вводе
            copyurlpightclick: true, // копирование URL  по ПКМ
            currentURIlabel: "Адрес текущей страницы в буфере обмена!",
            valueIsTypedlabel: "Содержимое адресной строки в буфере обмена!",
            ЛКМtooltiptext: "Показать историю",
            ПКМtooltiptext: "Копировать URL в буфер обмена",
            // <-- Настройки --

            dropmarker: null,
            provider: null,
            get style() {
                delete this.style;
                return this.style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #urlbar .urlbar-history-dropmarker {
                        list-style-image: url("chrome://global/skin/icons/arrow-down.svg") !important;
                        transition: opacity 0.15s ease;
                    }
                    #urlbar[switchingtabs] > #urlbar-input-container > .urlbar-history-dropmarker {
                        transition: none;
                    }
                    ${this.hidewhenusertyping ? `#urlbar[usertyping] > #urlbar-input-container > .urlbar-history-dropmarker {
                        display: none;
                    }` : ""}
                    #nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container:not(:hover) > #urlbar:not([focused]) > #urlbar-input-container > .urlbar-history-dropmarker {
                        opacity: 0;
                    }
                `);
            },
            init(that) {
                Services.prefs.addObserver("browser.urlbar.suggest.history", this);
                Services.prefs.addObserver("browser.urlbar.suggest.topsites", this);
                that.unloadlisteners?.push("urlbarhistorydropmarker");
                var { UrlbarProviderTopSites: provider } = { UrlbarProviderTopSites: this.provider } = ChromeUtils.import("resource:///modules/UrlbarProviderTopSites.jsm");
                if (!provider.orig_PRIORITY) {
                    provider.orig_PRIORITY = provider.ucf_PRIORITY = provider.PRIORITY;
                    delete provider.constructor.prototype.PRIORITY;
                    Object.defineProperty(provider.constructor.prototype, "PRIORITY", {
                        enumerable: true,
                        get() {
                            var priory = this.ucf_PRIORITY;
                            this.ucf_PRIORITY = this.orig_PRIORITY;
                            return priory;
                        },
                        set(val) {
                            this.ucf_PRIORITY = val;
                        },
                    });
                }
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true))
                   this.createDropmarker();
            },
            createDropmarker() {
                var fragment = MozXULElement.parseXULToFragment(`<image class="urlbar-page-action urlbar-history-dropmarker urlbar-icon" tooltiptext="${
                    !this.copyurlpightclick ? `${this.ЛКМtooltiptext}` : `ЛКМ: ${this.ЛКМtooltiptext}&#10;ПКМ: ${this.ПКМtooltiptext}`
                }"/>`);
                var dropmarker = this.dropmarker = fragment.firstElementChild;
                document.querySelector("#urlbar #urlbar-go-button").after(fragment);
                dropmarker.addEventListener("mousedown", this);
                if (this.copyurlpightclick)
                    dropmarker.addEventListener("click", this);
                windowUtils.loadSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            removeDropmarker() {
                this.removeListeners();
                this.dropmarker.remove();
                this.dropmarker = null;
                windowUtils.removeSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            removeListeners() {
                this.dropmarker.removeEventListener("mousedown", this);
                if (this.copyurlpightclick)
                    this.dropmarker.removeEventListener("click", this);
            },
            destructor() {
                if (this.dropmarker)
                    this.removeListeners();
                Services.prefs.removeObserver("browser.urlbar.suggest.history", this);
                Services.prefs.removeObserver("browser.urlbar.suggest.topsites", this);
            },
            observe() {
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true) && !this.dropmarker)
                    this.createDropmarker();
                else if (this.dropmarker)
                    this.removeDropmarker();
            },
            mousedown(e) {
                if (e.button !== 0) return;
                e.preventDefault();
                e.stopPropagation();
                if (gURLBar.view.isOpen)
                    gURLBar.view.close();
                else {
                    this.provider.PRIORITY = 0;
                    gURLBar.focus();
                    gURLBar.startQuery({
                        allowAutofill: false
                    });
                }
            },
            click(e) {
                if (e.button !== 2) return;
                var currentURI = this.currentURIlabel, valueIsTyped = this.valueIsTypedlabel;
                var gBrowserBundle = {
                    GetStringFromName(str) {
                        return ({
                            "confirmationHint.currentURI.label": currentURI,
                            "confirmationHint.valueIsTyped.label": valueIsTyped,
                        })[str];
                    }
                };
                var show = eval(`(function ${e.view.ConfirmationHint.show})`);
                var helper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
                (this.click = e => {
                    if (e.button !== 2) return;
                    e.preventDefault();
                    e.stopPropagation();
                    var url, mesId;
                    if (!gURLBar.valueIsTyped) {
                        url = gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec;
                        mesId = "currentURI";
                    } else {
                        url = gURLBar.untrimmedValue;
                        mesId = "valueIsTyped";
                    }
                    helper.copyString(url);
                    show.call(ConfirmationHint, this.dropmarker, mesId, { hideArrow: true });
                })(e);
            },
            handleEvent(e) {
                this[e.type](e);
            },
        }).init(this);

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

Выделить код

Код:

(this.urlbarhistorydropmarker = {
            // -- Настройки -->
            hidewhenusertyping: false, // скрывать dropmarker при вводе
            reloadpage: true, // обновить текущую страницу по СКМ
            copyurlrightclick: true, // копирование URL по ПКМ
            copyvalueistyped: true, // при вводе копировать содержимое адресной строки вместо URL
            currentURIlabel: "Адрес текущей страницы в буфере обмена!",
            valueIsTypedlabel: "Содержимое адресной строки в буфере обмена!",
            Ltooltiptext: "Показать историю",
            Mtooltiptext: "Обновить текущую страницу",
            Rtooltiptext: "Копировать URL в буфер обмена",
            // <-- Настройки --

            dropmarker: null,
            provider: null,
            get style() {
                delete this.style;
                return this.style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #urlbar .urlbar-history-dropmarker {
                        list-style-image: url("chrome://global/skin/icons/arrow-down.svg") !important;
                        transition: opacity 0.15s ease;
                    }
                    #urlbar[switchingtabs] > #urlbar-input-container > .urlbar-history-dropmarker {
                        transition: none;
                    }
                    ${this.hidewhenusertyping ? `#urlbar[usertyping] > #urlbar-input-container > .urlbar-history-dropmarker {
                        display: none;
                    }` : ""}
                    #nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container:not(:hover) > #urlbar:not([focused]) > #urlbar-input-container > .urlbar-history-dropmarker {
                        opacity: 0;
                    }
                `);
            },
            init(that) {
                Services.prefs.addObserver("browser.urlbar.suggest.history", this);
                Services.prefs.addObserver("browser.urlbar.suggest.topsites", this);
                that.unloadlisteners?.push("urlbarhistorydropmarker");
                var { UrlbarProviderTopSites: provider } = { UrlbarProviderTopSites: this.provider } = ChromeUtils.import("resource:///modules/UrlbarProviderTopSites.jsm");
                if (!provider.orig_PRIORITY) {
                    provider.orig_PRIORITY = provider.ucf_PRIORITY = provider.PRIORITY;
                    delete provider.constructor.prototype.PRIORITY;
                    Object.defineProperty(provider.constructor.prototype, "PRIORITY", {
                        enumerable: true,
                        get() {
                            var priory = this.ucf_PRIORITY;
                            this.ucf_PRIORITY = this.orig_PRIORITY;
                            return priory;
                        },
                        set(val) {
                            this.ucf_PRIORITY = val;
                        },
                    });
                }
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true))
                   this.createDropmarker();
            },
            get tooltipText() {
                delete this.tooltipText;
                var l = this.Ltooltiptext, m = "", r = "";
                if (this.reloadpage) l = `ЛКМ: ${this.Ltooltiptext}`, m = `&#10;СКМ: ${this.Mtooltiptext}`;
                if (this.copyurlrightclick) l = `ЛКМ: ${this.Ltooltiptext}`, r = `&#10;ПКМ: ${this.Rtooltiptext}`;
                return this.tooltipText = `${l}${m}${r}`;
            },
            createDropmarker() {
                var fragment = MozXULElement.parseXULToFragment(`<image class="urlbar-page-action urlbar-history-dropmarker urlbar-icon" tooltiptext="${this.tooltipText}"/>`);
                var dropmarker = this.dropmarker = fragment.firstElementChild;
                document.querySelector("#urlbar #urlbar-go-button").after(fragment);
                dropmarker.addEventListener("mousedown", this);
                if (this.copyurlrightclick || this.reloadpage)
                    dropmarker.addEventListener("click", this);
                windowUtils.loadSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            removeDropmarker() {
                this.removeListeners();
                this.dropmarker.remove();
                this.dropmarker = null;
                windowUtils.removeSheetUsingURIString(this.style, windowUtils.USER_SHEET);
            },
            removeListeners() {
                this.dropmarker.removeEventListener("mousedown", this);
                if (this.copyurlrightclick || this.reloadpage)
                    this.dropmarker.removeEventListener("click", this);
            },
            destructor() {
                if (this.dropmarker)
                    this.removeListeners();
                Services.prefs.removeObserver("browser.urlbar.suggest.history", this);
                Services.prefs.removeObserver("browser.urlbar.suggest.topsites", this);
            },
            observe() {
                if (Services.prefs.getBoolPref("browser.urlbar.suggest.history", false) && !Services.prefs.getBoolPref("browser.urlbar.suggest.topsites", true) && !this.dropmarker)
                    this.createDropmarker();
                else if (this.dropmarker)
                    this.removeDropmarker();
            },
            mousedown(e) {
                if (e.button !== 0) return;
                e.preventDefault();
                e.stopPropagation();
                if (gURLBar.view.isOpen)
                    gURLBar.view.close();
                else {
                    this.provider.PRIORITY = 0;
                    gURLBar.focus();
                    gURLBar.startQuery({
                        allowAutofill: false,
                    });
                }
            },
            click(e) {
                if (e.button === 0) return;
                var currentURI = this.currentURIlabel, valueIsTyped = this.valueIsTypedlabel;
                var gBrowserBundle = {
                    GetStringFromName(str) {
                        return ({
                            "confirmationHint.currentURI.label": currentURI,
                            "confirmationHint.valueIsTyped.label": valueIsTyped,
                        })[str];
                    }
                };
                var show = eval(`(function ${e.view.ConfirmationHint.show})`);
                var helper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
                (this.click = e => {
                    if (e.button === 0) return;
                    e.preventDefault();
                    e.stopPropagation();
                    if (e.button === 1) {
                        BrowserReload();
                        return;
                    }
                    var url, mesId;
                    if (!gURLBar.valueIsTyped || !this.copyvalueistyped) url = gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec, mesId = "currentURI";
                    else url = gURLBar.untrimmedValue, mesId = "valueIsTyped";
                    helper.copyString(url);
                    show.call(ConfirmationHint, this.dropmarker, mesId, { hideArrow: true });
                })(e);
            },
            handleEvent(e) {
                this[e.type](e);
            },
        }).init(this);

Кто-нибудь может помоч мне с этой проблемой?
Дополню: такая же проблема имеется с этим вариантом скрипта и с кастомной кнопкой с аналогичным функционалом.

Kot DaVinci
Можно после строки, содержащей node.setAttribute("label", label);
добавить строку, содержащую node.setAttribute("closemenu", "none");


Лучше во второй код, потому что там "таблетка от сепараторов" прописана,
ну, если фича "исключения скрытых" не мешает, конечно.

Dumby, спасибо. Работает как надо.

Dumby Поправьте пожалуйста эти 2 кнопки для [firefox] 93.0

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

Выделить код

Код:

// Switch Keyboard Layout
try {(keybUtils => CustomizableUI.createWidget({
    type: "custom",
    id: "SwitchKeyboardLayout",
    onBuild(doc) {
        var btn = doc.createXULElement("toolbarbutton");
        btn.id = this.id;
        btn.label = btn.tooltipText = "Switch Keyboard Layout";
        btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";

        btn.setAttribute("oncommand", "linkedObj.switch(document);");
        btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
        btn.linkedObj = this;
        return btn;
    },
    switch(doc) {
        var br = doc.activeElement;
        br && br.localName == "browser" && br.isRemoteBrowser
            ? br.messageManager.loadFrameScript(this.url, false)
            : this.keybUtils.switchSelKeybLayout();
    },
    get url() {
        delete this.url;
        return this.url = `data:;charset=utf-8,(${
            encodeURIComponent(keybUtils)
        }).switchSelKeybLayout()`;
    },
    get keybUtils() {
        delete this.keybUtils;
        var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
        var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
        Services.scriptloader.loadSubScript(url, this);
        var {id} = this;
        this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
            var window = Services.focus.activeWindow, {document} = window;
            var button = document.getElementById(id);
            if(
                !_focusFixed
                && "closeMenus" in window
                && document.commandDispatcher.focusedElement == button
            ) {
                window.closeMenus(button);
                window.setTimeout(function(_this) {
                    _this.switchSelKeybLayout(_subCall, true);
                }, 0, this);
                return;
            }
            return document.commandDispatcher.focusedElement;
        }
        return this.keybUtils;
    }
}))(`{
    //== Options
    noSelBehavior: { // Shift+Home
        ctrlKey:  false,
        altKey:   false,
        shiftKey: true,
        metaKey:  false,
        keyCode:  KeyEvent.DOM_VK_HOME,
        charCode: 0
    },
    // 0 - do nothing
    // 1 - convert all text
    // Or use object like following to simulate "keypress" event:

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            if(c in this.convTableForward)
                return true;
            if(c in this.convTableBackward)
                return false;
        }
        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(node instanceof Element) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
        var e = this.noSelBehavior;
        if(!e || typeof e != "object")
            return;
        var evt = node.ownerDocument.createEvent("KeyboardEvent");
        evt.initKeyEvent(
            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
            e.keyCode, e.charCode
        );
        node.dispatchEvent(evt);
    },
    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`)} catch(ex) {Cu.reportError(ex);}


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

Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}

egorsemenov06
Речь про initKeyEvent? Ну, конструктор нам рекоммендуют.

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

Выделить код

Код:

//        var evt = node.ownerDocument.createEvent("KeyboardEvent");
//        evt.initKeyEvent(
//            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//            e.keyCode, e.charCode
//        );

        var evt = new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        );


Во второй ничего не смог заметить.

Dumby пишет

egorsemenov06
Речь про initKeyEvent? Ну, конструктор нам рекоммендуют.

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

Выделить код

Код:

//        var evt = node.ownerDocument.createEvent("KeyboardEvent");
//        evt.initKeyEvent(
//            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//            e.keyCode, e.charCode
//        );

        var evt = new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        );


Во второй ничего не смог заметить.

Спасибо большое.Вторая чудесным образом сама аработала!

Vitaliy V.
Сделайте пожалуйста, что бы набранный текст удалялся автоматически после скрытия панели поиска.


Add, и если возможно, то объединить с этим скриптом: https://forum.mozilla-russia.org/viewto … 72#p782672

kokoss

скрытый текст
                           // if (!this.findbar.hidden)
                               // this.findbar.close();
                            if (this.findbar.hidden) return;
                            this.findbar.clear();
                            this.findbar.close();

скрытый текст
                        gFindBar.clear();
                        gFindBar.close();

Vitaliy V. пишет

скрытый текст
                           // if (!this.findbar.hidden)
                               // this.findbar.close();
                            if (this.findbar.hidden) return;
                            this.findbar.clear();
                            this.findbar.close();

Благодарю :beer:
А этот код:

gFindBar.clear();
gFindBar.close();

куда...?

kokoss пишет

куда...?

там ниже функция keydown(e) {...
для комбинаций клавиш Ctrl + F
перед gFindBar.close(); добавить gFindBar.clear();

Vitaliy V. пишет

там ниже функция keydown(e) {...
для комбинаций клавиш Ctrl + F
перед gFindBar.close(); добавить gFindBar.clear();

Я так понимаю что если для этого не использую клави..., то можно удалить этот код:

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

Выделить код

Код:

keydown(e) {
                if (e.ctrlKey && e.code == "KeyF" && !e.altKey && !e.shiftKey) {
                    if (this.timer != null) {
                        e.preventDefault();
                        return;
                    }
                    this.timer = setTimeout(() => {
                        this.timer = null;
                    }, 1000);
                    if (window.gFindBarInitialized && !gFindBar.hidden) {
                        e.preventDefault();
			gFindBar.clear();
                        gFindBar.close();
                    }
                }
            },

kokoss пишет

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

Нет, один этот код удалять нельзя, только вместе со слушателем, и удалять код не обязательно, достаточно отключить слушатель.
// window.addEventListener("keydown", this, true);
// window.removeEventListener("keydown", this, true);

Vitaliy V. пишет

Нет, один этот код удалять нельзя.

Я это уже понял...


Vitaliy V. пишет

и удалять код не обязательно, достаточно отключить слушатель.
// window.addEventListener("keydown", this, true);
// window.removeEventListener("keydown", this, true);

Спасибо!

Скачал комплект UserChromeFiles - 2021-9-23. Обновил/перезаписал все файлы по нужным папкам.
Скрипты подключились и заработали сразу, а вот стили ни один не подключился.
Ранее в файле custom_style_user.css было прописано следующее содержимое и всё работало:

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

Выделить код

Код:

/* Этот файл для правил CSS с правами USER_SHEET */
/* значки папок закладок желтого цвета */
@import url("./css/Colored-folders-91.css");

/* скрыть элементы меню закладок */
@import url("./css/hide_bookmarks_elements.css");

/* скрыть элементы контекстного меню на странице */
@import url("./css/hide_context_elements.css");

/* убрать history-dropmarker из адресной строки */
@import url("./css/history-dropmarker.css");

/* компактная панель поиска сверху справа */
@import url("./css/findbar_compakt.css");

/* панель быстрого поиска такая хе, как и полного поиска */
@import url("./css/findbar_show_full_quickfindbar.css");

/* в приложении GISMETEO - белый шрифт значка погоды */
@import url("./css/gismeteo.css");

/* изменение высоты панели вкладок, компактное меню (Proton) */
@import url("./css/bar_compact_proton.css");


теперь появились три файла: custom_styles_all_user.css , custom_styles_chrome_user.css , custom_styles_content_user.css
пробовал помещать код в них - безрезультатно
Не пойму куда вообще копать?

Inko7
Так все стили по умолчанию отключены в CustomStylesScripts.jsm (зачем включать пустые файлы, они там только для примера)
можете изменить это в styleschrome (стили подключенные в styleschrome работают там же где и userChrome.css)
ну или в stylesall: [ // Для всех документов
И вместо @import можно просто подключить ваши файлы там же в CustomStylesScripts.jsm, например
{ path: "css/Colored-folders-91.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },

Vitaliy V.
вот теперь все стили заработали, спасибо!

получается, раз скрипты заработали сразу, то файлы custom_script.js / custom_script_all_win.js / custom_script_win.js обрабатываются изначально и их прописывать дополнительно не нужно?

Inko7 пишет

файлы custom_script.js / custom_script_all_win.js / custom_script_win.js обрабатываются изначально и их прописывать дополнительно не нужно?

custom_script.js добавлен в CustomStylesScripts.jsm, но его можно удалить, переименовать,
а custom_script_all_win.js / custom_script_win.js обрабатываются изначально и их прописывать, удалять, переименовывать нельзя

Vitaliy V.
Вы можете написать скрипт отключающий отображение пунктов контекстного меню, с определенным в скрипте ID, для разных контекстов. С перечислением исключений для каждого добавленного пользователем ID. Что-то типа:

"#context-copy" this.hidden = gContextMenu.onLink || gContextMenu.onMailtoLink || gContextMenu.onImage || gContextMenu.onCanvas;
"#other-addon" this.hidden = gContextMenu.onTextInput || gContextMenu.isContentSelected;

Например, #context-copy появляется везде при выделенном на странице тексте, но главное здесь расширения, очень часто они добавляют свои пункты без учета контекста.
Второй пример, сепараторы, при переупорядочивании меню некоторые сепараторы надо удалить только для определенных контекстов.
   
И, если будете делать, добавьте примеры, в том числе для контекстов: фрейм, страница, вкладка, адресная строка и textarea (если такой есть отдельно от .onTextInput).
   
ps^ и есть ли контексты в закладках панели - папка, отдельныя закладка? Видел расширение которое добавляло свой пункт и туда и туда, а нужно было только для папок.

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


если не заметили недавно обновил ваши скрипты
https://forum.mozilla-russia.org/viewto … 24#p784824
https://forum.mozilla-russia.org/viewto … 55#p783755

Vitaliy V.
SidebarTabs обновлял, там беда со сплиттером
2021.1634372082.png
оставил так и выкинул after

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

Выделить код

Код:

#st_splitter {
                -moz-appearance: none !important;
                appearance: none !important;
                background-color: var(--chrome-content-separator-color, rgba(127,127,127,.5)) !important;
                background-clip: content-box !important;
                border-inline: 1px solid transparent !important;
                min-width: 3px !important;
                margin-inline: -1px !important;
                position: relative !important;
                z-index: 2 !important;
                -moz-box-ordinal-group: ${this.ST_RIGHT ? "100" : "0"} !important;
                -moz-box-orient: vertical !important;

2021.1634372264.png
   
Тултипы сейчас обновил.
Я тут подумал, что отображение заголовка перед адресом более удобно, как их местами поменять? У меня не получается, без заголовка остается пустая строка перед адресом.
   
ps^ А как вы svg преобразовываете, например вот это как в скрипт засунуть?
скрытый текст

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" width="16" height="16"><path d="M4 1C1.804688 1 0 2.800781 0 5L0 15C0 17.195313 1.804688 19 4 19L6.3125 19L8.0625 23.375C8.207031 23.765625 8.582031 24.027344 9 24.027344C9.417969 24.027344 9.792969 23.765625 9.9375 23.375L11.6875 19L22 19C24.195313 19 26 17.195313 26 15L26 5C26 2.800781 24.195313 1 22 1 Z M 4 3L22 3C23.117188 3 24 3.882813 24 5L24 15C24 16.113281 23.113281 17 22 17L11 17C10.589844 16.996094 10.214844 17.242188 10.0625 17.625L9 20.28125L7.9375 17.625C7.785156 17.242188 7.410156 16.996094 7 17L4 17C2.886719 17 2 16.113281 2 15L2 5C2 3.882813 2.882813 3 4 3Z" fill="#D0D0D0" /></svg>

_zt пишет

оставил так и выкинул after

надо только добавил
border: none !important;
background: none !important;

_zt пишет

отображение заголовка перед адресом более удобно, как их местами поменять?

// el.title = title = `${href}${title === "" ? "" : `\nTitle: ${title}`}`;
el.title = title = `${title === "" ? "" : `Title: ${title}\nUrl: `}${href}`;
Но сначала обновите все полностью, я там изменил немного

Vitaliy V.
Теперь нормально. В своем svg заменил fill= на style= из вашего и все заработало.

Dumby Не могли бы пожалуйста поправить вот этот скрипт для [firefox] 94.0

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

Выделить код

Код:

(async id => ({

	delay: 2e3,

	get limit() {
		var mb = 1024 * 1024;
		delete this.limit;
		return this.limit = this.notMulti
			? 770 * mb // not multiprocess, 770MB
			: 1.50 * 1024 * mb; // multiprocess, 1.50GB
	},
	xul: `
		<hbox
			id="${id}"
			value="ram"
			tooltiptext="ЛКМ: Очистить Память"
		>
			<label id="${id += "-label"}"/>
		</hbox>
	`,
	css: `
		min-height: 17px !important;
		height: 17px !important;
		border-radius: 3px !important;
		padding: 2px 5px 0px !important;
		font-family: Tahoma !important;
		color: SaddleBrown !important;
		font-size: 15px !important;
		margin-bottom: 2px !important;
		margin-right: 1px !important;
		margin-left: 3px !important;
	`,
	launch() {
		var file = Services.dirsvc.get("ProfD", Ci.nsIFile);
		["memreduct", "start.vbs"].forEach(file.append);
		(this.launch = file.launch)();
	},
	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(this.xul);
		this.click = e => e.button || this.launch();
		this.notMulti = !Services.appinfo.browserTabsRemoteAutostart;
		this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

		(this.observe = async win => {
			this.timer.cancel();
			await new Promise(win.requestAnimationFrame);
			var clone = win.document.importNode(df, true);
			var hbox = clone.firstChild;
			win.document.getElementById("page-action-buttons").append(clone);
			hbox.onclick = this.click;
			hbox.style.cssText = this.css;
			hbox.firstChild.style.setProperty("margin", "0", "important");
			this.notify();
		})(win);
	},
	async notify() {
		var info = await ChromeUtils.requestProcInfo();
		var bytes = info.residentSetSize;
		for(var child of info.children) bytes += child.residentUniqueSize;
		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)
				win.document.getElementById(id).value = this.val;

		//this.notMulti &&
			bytes > this.limit && this.launch();
	},
	mgb: bytes => bytes < 1073741824
		? String(Math.round(bytes / 1048576))
		: (bytes / 1073741824).toFixed(2)
}).init("browser-delayed-startup-finished"))("ucf-mem-indicator");

egorsemenov06 пишет

поправить вот этот скрипт для [firefox] 94.0

Заменить residentSetSize и residentUniqueSize на memory


Dobrov пишет

как добавить двойной клик на UCF-кнопку, чтобы вместе с ним не срабатывало событие "click" ?

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

Выделить код

Код:

try {	CustomizableUI.createWidget({
		id: "add-additional-personaltoolbar-button", type: "custom", localized: false, label:
"Панели, Папки", tooltiptext:
`ЛКМ:	★ Закладки\n…+ Alt	Домашняя папка
ПКМ:	⟳ История\n…+ Alt	Папка установки
СКМ:	Папка профиля\n…+ Alt	user_chrome_files`,
		onBuild(doc) {
			var trbn = doc.createXULElement("toolbarbutton");
			trbn.id = this.id;
			trbn.tooltipText = this.tooltiptext;
			trbn.label = this.label;
			trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
			trbn.setAttribute("context", false);
			trbn.style.setProperty("list-style-image", `url("chrome://user_chrome_files/content/vertical_top_bottom_bar/svg/bookmark-16.svg")`, "important");
			trbn.addEventListener("dblclick", function(e) {
				e.view.alert("DBL Click");
			}, false);
			trbn.explorer =(dir, subdir = undefined)=> {
				var dirs = Services.dirsvc.get(dir, Ci.nsIFile);
				if (subdir) dirs.append(subdir);
				if (dirs.exists()) dirs.launch();
			};
			trbn.bar =(bar)=> {
				var win = Services.wm.getMostRecentWindow("navigator:browser");
				if ("SidebarUI" in win)
					win.SidebarUI.toggle(bar);
				else if ("toggleSidebar" in win)
					win.toggleSidebar(bar);
			};
			trbn.addEventListener("click", function(e) {
				if (e.button == 0)
					e.altKey ? trbn.explorer("Home") : trbn.bar("viewBookmarksSidebar")
				else if (e.button == 1)
					e.altKey ? trbn.explorer("UChrm", "user_chrome_files") : trbn.explorer("ProfD")
				else if (e.button == 2)
					e.altKey ? trbn.explorer("GreD") : trbn.bar("viewHistorySidebar")
			}, false);
			return trbn;
		},
	});
} catch(e) {}

С помощью таймаута наверно, как же ещё.

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

Выделить код

Код:

(async (bar, exp, tid, self) => CustomizableUI.createWidget(self = {
	label: "Панели, Папки",
	tooltiptext: [
		"ЛКМ:	★ Закладки\n…+ Alt	Домашняя папка",
		"ПКМ:	⟳ История\n…+ Alt	Папка установки",
		"СКМ:	Папка профиля\n…+ Alt	user_chrome_files"
	].join("\n"),
	id: "add-additional-personaltoolbar-button",
	localized: false,
	onCreated(btn) {
		btn.onclick = this.click;
		btn.style.setProperty("list-style-image", "url(chrome://user_chrome_files/content/vertical_top_bottom_bar/svg/bookmark-16.svg)", "important");
	},
	exec(num, win) {
		tid = null;
		self[num](win);
	},
	context: win => win.document.getElementById(self.id)
		.dispatchEvent(new win.MouseEvent("contextmenu", self.a)),
	a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

	click(e) {
		if (e.detail > 2) return;
		var n2 = e.button != 2;
		var dbl = e.detail == 2;
		var num = 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

		if (!self[num]) {
			if (n2) return;
			num = "context";
			for(var p in self.a) self.a[p] = e[p];
		}
		n2 || e.preventDefault();

		var win = e.view;
		if (dbl) tid &&= win.clearTimeout(tid), self[num](win);
		else tid = win.setTimeout(self.exec, 300, num, win);
	},
	0:   w => bar(w, "viewBookmarksSidebar"), // ЛКМ
	2:  () => exp("Home"), // Alt+ЛКМ
	16: () => exp("ProfD"), // СКМ
	18: () => exp("UChrm", "user_chrome_files"), // Alt+СКМ
	32:  w => bar(w, "viewHistorySidebar"), // ПКМ
	34: () => exp("GreD"), // Alt+ПКМ

	1(win) { // Double Left Click
		win.alert("DBL Click");
	},
	33(win) { // Double Right Click
		win.alert("DBL Right Click");
	},
	29(win) { // Ctrl + Shift + Double Middle Click
		win.alert("Ctrl + Shift + DBL Middle Click");
	},
}))(
	(win, bar) => win.SidebarUI.toggle(bar),
	(dir, sub) => {
		dir = Services.dirsvc.get(dir, Ci.nsIFile);
		sub && dir.append(sub);
		dir.exists() && dir.launch();
	}
);

Dumby пишет

Заменить residentSetSize и residentUniqueSize на memory

Большое Вам Спасибо!!!

Vitaliy V.
Подскажите пожалуйста, как в этом скрипте запретить AutoPopup на некоторых кнопках? И спасибо за скрипт!


Add, вроде получилось что хотел, удалил из скрипта часть кода: this.ExtensionParent.WebExtensionPolicy.getByID(id).extension . Правильно ли я это сделал? Нужно было отключить AutoPopup значков расширений.


Add, и ешё вопрос, для чего нужен этот скрипт?

Dumby - Спасибо, скрипт очень компактный и крутой, но в двойной клик на UCF-кнопке правый клик мыши работает неверно.
Вместе с правым кликом в MacOS и вероятно Linux открывается контекстное меню панели, пробовал отключить и не получилось:

Выделить код

Код:

var reset = e => e.target.linkedObject = this;
		var id, lo = {click: e => n2 || reset(e)};
		var lin = /macos|linux/.test(e.view.AppConstants.platform);
		var stop = e => reset(e) && e.preventDefault();
		lo.contextmenu = lin
			? e => e.ctrlKey || e.shiftKey ? 0 : stop(e) : stop;
		n2 || reset(e) && e.preventDefault();
……

Dumby - ещё просьба по твоему скрипту перехвата кликов на кнопках > добавить Двойной клик  (у меня не удалось):
Конкретно - добавить двойной клик на "PanelUI-menu-button" (строка 133) и по возможности на "downloads-button"…


Второе (если это возможно) > изменить способ перехвата кнопок со стандартного addEventListener на такой же «продвинутый», как и в новом скрипте,
то есть способом: var num = 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

ucf_hookClicks.js (скрипт перехвата кликов на downloads-button, PanelUI-menu)

Выделить код

Код:

(async (id, func) => { // дополнительные клики на downloads-button, PanelUI-menu для custom_script_win.js
	await window.delayedStartupPromise;
	var btn = document.getElementById("downloads-button"), pui = document.getElementById("PanelUI-menu-button");
	if (!btn) return; btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) +`

ПКМ:	Сохранить как единый html
	всё | выделенное на странице
…+ Shift	Обзор папки [Загрузки]\n
Ролик:	Сохранить как файл .txt
…+ Shift	Сайт: графика Вкл/Выкл\n
Колёсико на рисунке: ➜ Сохранить
Двойной клик: найти Похожие фото`,
																PanelUI_help =
`Браузер Firefox, версия ${Services.appinfo.platformVersion}\n
Колёсико:	Развернуть | окно
…+ Alt		Полный экран
Правый клик	⇲ Свернуть
…+ Shift		Закрыть ✕
…+ Alt		Персонализация`;

	var addDestructor = nextDestructor => {
		var {destructor} = ucf[id];
		ucf[id].destructor = () => {
			try {destructor();} catch(ex) {Cu.reportError(ex);}
			nextDestructor();
		}
	},
	showInStatusPanel = (info, time = 5000) => {
		var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
		if (StatusPanel.update.tid)
			clearTimeout(StatusPanel.update.tid)
		else {
			var {update} = StatusPanel;
			StatusPanel.update = () => {};
			StatusPanel.update.ret = () => {
				StatusPanel.update = update;
				StatusPanel.update();
			}
		}
		StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time);
		StatusPanel._label = info;
	},
	saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt
		var splice = saveURL.length == 10;
		var msgName = id + ":Save:GetSelection";
		var receiver = msg => {
			var title = document.title || gBrowser.selectedTab.label;
			var args = [
				"data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data),
				title.replace(/[:\\\/<>?*|"]+/g,'_').replace(/\s+/g,' ').slice(0, 100).trim() + '_' + new Date().toLocaleString('ru').replace(', ','-').replace(/:/g, '։') + '.txt',
				null, false, true, null, window.document
			];
			splice && args.splice(5, 0, null);
			saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60));
		}
		messageManager.addMessageListener(msgName, receiver);
		addDestructor(() => messageManager.removeMessageListener(msgName, receiver));
		var func = fm => {
			var res, fed, win = {}, 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("saveSelectionToTxt", res);
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
		(saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end

	save = async () => { // автор: Лекс, правка: Dumby, Dobrov (пути сохранения HTML)
		var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML";
		if (typeof IOUtils != "object") { // Firefox 78 ESR
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			var PathUtils = {join: (...args) => OS.Path.join(...args)};
			var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
		}
		var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8";

		var Title = (type) => { // получить заголовок (без обрезки, если type не указан) или домен (type <0)
			var title = (document.title || gBrowser.selectedTab.label);
			if ( !type ) return title; // заголовок
			if ( type > 0 ) return title.slice(0, type).replace(/ \| Форум Mozilla Россия$| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); // ограничить длину имени
			var host = (/^file:\/\//.test(gURLBar.value)) ? '' : gURLBar.value.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,'');
			return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns');
		}
		var msgListener = async msg => {
			var [fileContent, fileName] = msg.data, dir;
			try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
			var arr = prefs.getStringPref("ucf_save.dirs", "_Web||_Images|0").split('|').slice(0, 2); // Dir/subdir: пусто|0 title|1 домен
			arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен
			arr.forEach(dir.append); // для ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/имя вкладки
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать папку, если не существует…
			var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			file.initWithPath(dir.path);
			dir.append(fileName);
			await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60));
			var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download
			(await Downloads.getList(Downloads.ALL)).add(d);
			d.refresh(d.succeeded = true); // кнопка Загрузки мигает
		}
		messageManager.addMessageListener(msgName, msgListener);
		addDestructor(() => messageManager.removeMessageListener(msgName, msgListener));

		var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services';
		var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName));
		(save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end save

	listener = e => { var trg = e.target; // Downloads Clicks
		if (e.button == 1) {
			if (e.shiftKey) { // СКМ + Shift
				if ( prefs.getIntPref("permissions.default.image", 1) == 1)
					prefs.setIntPref("permissions.default.image", 2), trg.style.filter = "hue-rotate(180deg) brightness(95%)"
				else
					prefs.setIntPref("permissions.default.image", 1), trg.style.filter = "";
				BrowserReload();
			} else	// СКМ Click
				saveSelectionToTxt(); // сохранить .txt
		} else if (e.button == 2) {
			if (e.shiftKey)
				Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
			else	// ПКМ Click
				save(); // Single HTML
		}
	},
	listener_pui = e => { // PanelUI-menu Clicks
		if (e.button == 1) {
			if (e.altKey)
				window.BrowserFullScreen()
			else
			if( window.windowState != window.STATE_MAXIMIZED )
				window.maximize()
			else
				window.restore();
		} else
			if (e.button == 2) if (e.altKey) return
		else {
			e.stopPropagation();
			(e.shiftKey) ? window.close() : window.minimize();
		}
	}, // end Clicks
	keydown_win = e => { // нажатие клавиш
		if (!(e.keyCode == 83 && e.shiftKey && e.altKey)) return;
		var singlesave = document.getElementById(save_ex); // SingleSave
		singlesave ? singlesave.click() : save(); // имитировать клик по кнопке, используя её ID
	},
	{prefs, dirsvc} = Services, tmax = btn.tooltipText.split("\n")[0].length, save_ex = "_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action";
	btn.setAttribute("context", "event.stopPropagation()");
	prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки

	(async () => { // SingleSave - дополнить подсказку
		setTimeout((but = document.getElementById(save_ex))=> {
			if (but)
				btn.tooltipText = btn.tooltipText + '\n\nAlt⇧S	нажатие SingleSave';
			if (!/Закрыть/.test(pui.tooltipText))
				pui.tooltipText = PanelUI_help;
		}, 9000); // после запуска ждать от 3 сек
	})();

	btn.addEventListener("click", listener), pui.addEventListener("click", listener_pui);
		window.addEventListener("keydown", keydown_win);
	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		btn.removeEventListener("click", listener), pui.removeEventListener("click", listener_pui);
		window.removeEventListener("keydown", keydown_win);
	}};
	ucf.unloadlisteners.push(id);

})("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	mainWin = {};
	focus.getFocusedElementForWindow(content, true, mainWin);
	mainWin = mainWin.value;

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0];
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	if (title) head.appendChild(title.cloneNode(true));

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};

	var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).trim();
	fileName += "_" + new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։");
	if (!/\.html?$/.test(fileName)) fileName += '.html';

	sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', fileName]);

}); // END hookClicks

В скрипт Quick Toggle about:config тоже добавить Двойной клик на кнопке (в коде есть ещё Долгое нажатие)

ucf_QuickToggle.js (скрипт из моего профиля FF в шапке темы)

Выделить код

Код:

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

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

`клик+Alt	Библиотека закладок
…+ Shift	± zoom Текст/страница
։нажать	Антизапрет ⇆ proxy\n
ПКМ	Меню быстрых настроек
։нажать	© Краткая справка
…+ Alt	Опции about:config\n
СКМ	Вкладка «Топ сайтов»
…+ Alt	Восстановить вкладку
…+ Shift	● Захват цвета, Zoom
։нажать	Консоль браузера\n
⟳ Обновить ↯ Перезапуск
ЛкМ: Левый клик, СкМ: Колёсико
нажать: долгий клик мыши ≥1 сек`, // ЛКМ+Alt+Shift: настройки User Chrome Files. свободные hotkeys: СКМ+Alt ЛКМ+Alt+⇧ Нет в подсказке: ЛКМ	панель Журнала, СКМ+Shift – Пипетка

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

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

// пути сохранения Html/Pics в [Загрузки]/dir/subdir заданы в ucf_save.dirs="_Html dir|subdir|_Pics dir|subdir" : пусто|0 заголовок|1 домен
// ucf_save.dirs="_Web||_Pics|1" save Html: [Загрузки]/_Web/имя страницы.html, save Pic: [Загрузки]/_Pics/имя вкладки/имя картинки

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

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

					var find = node => node.pref && node.pref.pref == pref;
					var redef = (doc, hint) => {
						var popup = doc.getElementById("ToggleAboutConfig-secondaryPopup");
						var menuitem = Array.from(popup.children).find(find).menupopup.firstChild;
						menuitem.tooltipText = hint ? ua + "\n" + hint : ua;
						menuitem.setAttribute("oncommand",
							`event.stopPropagation();
							this.closest("toolbarbutton").linkedObject.contextmenu({
								preventDefault: Boolean,
								target: this.parentNode.parentNode
							});`
						);
					}
					Object.defineProperty(arr, "0", {enumerable: true, get() {
						if (Components.stack.formattedStack.includes("createRadios")) {
							var win = Services.wm.getMostRecentWindow("navigator:browser");
							win.setTimeout(redef, 0, win.document, this[3]);
						}
						else return "";
					}});
					return arr;
				})([null, "По-умолчанию"]),
				["Mozilla/5.0 (Android 9; Mobile; rv:68.0) Gecko/68.0 Firefox/68.0", "Firefox Android9"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:68.0) Gecko/20100101 Firefox/68.0", "Firefox 68 MacOSX"],
				["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", "Chrome61 Win10"],
				["Mozilla/5.0 (Windows NT 6.1; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56"],
				["Mozilla/5.0 (X11; Linux x86_64; rv:56.0) Gecko/20100101 Firefox/56.0", "Firefox 56 Linux"],
				["Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", "MSIE 6.0 Windows"],
				["Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", "Chrome61 Android7"],
				["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30", "Safari 6 MacOSX"],
				["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
				["Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", "Samsung Galaxy S6"],
				["Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)", "Playstation 4"],
				["Xbox (Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Xbox One (mobile)"],
				["Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", "Microsoft Lumia 950"],
				["Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350)", "Windows Phone"],
				["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"]]
	},{
			pref: ["browser.cache.disk.free_space_soft_limit", "Неактивные вкладки",, "\nПри запуске загружаются все вкладки,\nэто может замедлить работу браузера.\nОптимально - Выгружать старые табы."], userChoice: 5121, userAlt: 5120, userPro: 5122,
			values: [
			[5120, "Загружать",,, `prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); prefs.setBoolPref("browser.tabs.unloadOnLowMemory", false)`],
			[5121, "Оптимально",, "Выгружать старые вкладки, экономя память", `prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); prefs.setBoolPref("browser.tabs.unloadOnLowMemory", true)`],
			[5122, "Не загружать",,, `prefs.setBoolPref("browser.sessionstore.restore_on_demand", false); prefs.setBoolPref("browser.tabs.unloadOnLowMemory", true)`]]
	}];

	return {
		label: "Quick Toggle Settings",
		id: "ToggleAboutConfig",
		tooltiptext: help, localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAADhUlEQVR4AaWUIYzsNhCG91WVelKfqoWrpwMLFxQYGhoGBgYGBgYGGhoGBhoGGhoaBhoaGhqcdIFhf+dGt2n09rpq1ZE+2bHl+eyxd0993yVjNIwxq9F6/WjtaNd5nBnq85g2PMeMo8EwDEUI0Z7+jm/EY2it87ZtuAf1EUqAzZZZcuCxnyOlBFoLkuhnkgdBjBG36YqzP+EaTqgmSckyjrGuK8paWGythVLKfv/9t/O/OkEIC6pZoskCfanQuQYLSTm2/ZQkjSil8Pc8z6iqyp/P58tn2l92wWjGnEuG9gO6uUVtaygnMOQaU+nR+hotjfeugwmaHNv9pJzYOYcQAp+kbVv3cBJrbA45QIQLVLyizRI6t5R8gC0aY+nQZYWK5lT4E3nNn2XaiJX5PAmLXl9fuweBz54X97mCoYR21SwYc8+yjsbrKKGCvAu4TDnne3IO7z2oTP1RwCVKJXGJ+rlHaxu0ocZQahLWaO4lmjtov5eI7yDZFnFqWXQUPL1kFzwaV3GpmqQo+cMlc1li9MBCyacbUnC74MePy3NBiAukFRDLBTd/QWUVUk44BJclx5lMPVYvEJ3e7+CZYN+dSw5TmpiQA1/oPTYip4SV5gANRIloa5Ku/LKeCqhljrHmhLzMSIFYPGLw3N/KCMAAuUYeBUkXLDE9vCIW0K45MbXYoW/aFZdiyxYoM9Y0oiwGaxyBjcAElAHFCETTw40T6tvVXP749eUfBe/v78xdlJaAvNwTGmo1YYiRIME6YosGxWnEaUDQbXFt5adazKO6DEcBX97b29tRAq4tl0XzpZKAkzJFEx31iY1bYuANIBtkW6engl1CRD6JAYrh5Mg90QBRAYsgbkAkksS2VJvT0jXqUj0R7HzOrViCQ1kGILZAUAQl9AxLVl/BDTI24tyK1+/8n/SVYOexVDMlaQAvAHdjOHmQLPS6grxe9ZeviDgKuM/jzIZM38kbbE4ekguGBV7CDwLXy1k/vKJtPf4ODmAPFhbf74mLa5DmFltQwCKJCskIiPNpprTi42+bRcaYTIv3Z/oVhcgpUrIOm1NYbIvZWtjJIliSLgpIFdZZohMvgQRqFyilRimFF0I4+QXik7ZR3vRqqSsRpJSO1jEf/bFXGUsNhAq2uWS63eb0X+NjN/uuforb9UXYXjqEBkkLyMupP85/I/53fDzLsZV6am8zCdR9/C+4MZTbZ2zc/gAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

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

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

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

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

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

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

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "другое" : "стандарт";
				lab += ` ${"restart" in pref ? "↯-" : "refresh" in pref ? "-⟳" : "—"} ${sfx}`;
			}
			lab = exists ? lab : '['+ lab +']'; // имя = [имя] если преф не существует
			node.setAttribute("label", lab);
		},
		createRadios(doc, vals, popup) {
			for(var arr of vals) {
				if (!arr) {
					popup.append(doc.createElementNS(xul_ns, "menuseparator"));
					continue;
				}
				var [val, lab, key, hint] = arr;
				var menuitem = doc.createElementNS(xul_ns, "menuitem");
				menuitem.setAttribute("type", "radio");
				menuitem.setAttribute("closemenu", "none");
				menuitem.style.setProperty("font-style", "italic", "important"),
				menuitem.setAttribute("label", popup.parentNode.pref.vals[val] = lab);
				key && menuitem.setAttribute("accesskey", key);
				var tip = menuitem.val = val;
				if (hint) tip += "\n" + hint;
				menuitem.tooltipText = tip;
				popup.append(menuitem);
			}
		},
		openPopup(popup) {
			var btn = popup.parentNode;
			if (btn.domParent != btn.parentNode) {
				btn.domParent = btn.parentNode;
				if (btn.matches(".widget-overflow-list > :scope"))
					var pos = "after_start";
				else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
					btn.closest("toolbar").getBoundingClientRect(), pos = width > height
						? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
						: `${win.innerWidth - right > left ? "end" : "start"}_before`;
				for(var p of btn.popups) p.setAttribute("position", pos);
			}
			popup.openPopup(btn);
		},
		maybeRestart(node, conf) {
			if (conf && !Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) return;
			var cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
			Services.obs.notifyObservers(cancel, "quit-application-requested", "restart");
			return cancel.data ? Services.prompt.alert(null, this.label, "Запрос на выход отменён.") : this.restart();
		},
		async restart() {
			var meth = Services.appinfo.inSafeMode ? "restartInSafeMode" : "quit";
			Services.startup[meth](Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
		},
		maybeRe(node, fe) {
			var {pref} = node;
			if ("restart" in pref) {
				if (this.maybeRestart(node, pref.restart)) return;
			}
			else this.popupshowing(fe, node.parentNode);
			if ("refresh" in pref) {
				var win = node.ownerGlobal;
				if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
					? win.BrowserReloadSkipCache() : win.BrowserReload();
			}
		},
		maybeClosePopup(e, trg) {
			!e.shiftKey && prefs.getBoolPref(this.closePref, undefined)
				&& trg.parentNode.hidePopup();
		},
		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;
				}
			}
		},
		click(e) {
			if (e.button) return;
			var trg = e.target, {pref} = trg;
			if (!pref) return;
		},
		command(e) { // нажатия левой кнопки мыши
			var trg = e.target, win = e.view;
			if (trg.btn) { // LMB
				if (e.shiftKey) e.altKey
					? win.openDialog("chrome://user_chrome_files/content/options/prefs_win.xhtml", "user_chrome_prefs:window", "centerscreen,resizable,dialog=no") // Alt+Shift
					: e.view.ZoomManager.toggleZoom(); // Alt Пипетка
				else if (e.altKey)
					e.view.PlacesCommandHook.showPlacesOrganizer("BookmarksToolbar"); // Shift Библиотека Панель закладок
				else {  // LMB Click
					var bar = e.target.ownerDocument.querySelector("#ucf-additional-vertical-bar")
					if (bar) {
						win.setToolbarVisibility(bar, bar.collapsed);
						bar.collapsed ? win.SidebarUI.hide() : win.SidebarUI.show("viewHistorySidebar");
					} else
						win.SidebarUI.toggle("viewHistorySidebar");
				}
				return;
			}
			var menu = trg.closest("menu"), newVal = trg.val;
			this.maybeClosePopup(e, menu);
			if (newVal != menu.pref.val)
				menu.pref.set(menu.pref.pref, newVal),
				this.maybeRe(menu, true);
		},
		auxclick(e) { // CKM
			if (e.button != 1 || !e.target.btn) return;
			if (e.altKey) e.shiftKey
				? this.switchToTab(e, "ya.ru") // Alt+Shift
				: e.view.undoCloseTab()
			else if (e.shiftKey)
				this.eyedropper(e.target)
			else
				this.switchToTab(e, "about:newtab");
		},
		contextmenu(e) { // RMB
			var trg = e.target, win = e.view;
			if (trg.btn) {
				if (e.ctrlKey || e.shiftKey) return;
				if (e.detail == 2) return trg.secondaryPopup.hidePopup(); // меню быстрых настроек
				! e.altKey ? this.openPopup(trg.secondaryPopup) : this.switchToTab(e, "about:config");
			}
			else if ("pref" in trg) {
				this.maybeClosePopup(e, trg);
				if (trg.pref.user)
					prefs.clearUserPref(trg.pref.pref),
					this.maybeRe(trg);
			}
			e.preventDefault();
		},
		mousedown(e) {
			var reset = e => e.target.linkedObject = this;
			var id, lo = {command: reset, mousedown: reset, auxclick: e => e.button != 1 || reset(e)};
			var lin = /macos|linux/.test(e.view.AppConstants.platform);
			var stop = e => reset(e) && e.preventDefault();
			lo.contextmenu = lin
				? e => e.ctrlKey || e.shiftKey ? dsp(e) : stop(e) : stop;
			var context = lin
				? e => e.button == 2 && e.type.endsWith("p") && this.contextmenu(e) : () => {};
			var dsp = (e, timeout) => {
				var trg = e.target;
				trg.onmouseup = trg.onmouseleave = null;
				if (timeout) return this.londPress(e);
				e.view.clearTimeout(id);
				reset(e);
				context(e);
			}
			(this.mousedown = e => {
				var trg = e.target;
				if (!trg.btn) return;
				trg.linkedObject = lo;
				trg.onmouseup = trg.onmouseleave = dsp;
				id = e.view.setTimeout(dsp, 500, e, true);
			})(e);
		},
		londPress(e) { // удержание кнопки мыши. на второй долгий клик при отпускании сработает действие на обычный клик этой кнопки
			var trg = e.target, win = e.view;
			if (e.button == 0) this.switchProxy(e, my_vpn);
			if (e.button == 1) trg.ownerDocument.getElementById("key_browserConsole").doCommand(); // Консоль браузера
			if (e.button == 2) { // RMB Long
				var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
				(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(e, help_ucf[0]) : this.switchToTab(e, help_ucf[1]);
			}
		},
		switchProxy(e, pac) {
			if (prefs.getIntPref('network.proxy.type') == 2) { // выключить
				prefs.setIntPref('network.proxy.type', 0);
				prefs.setStringPref("network.proxy.autoconfig_url", "127.0.0.1");
				e.target.style.removeProperty("filter");
				this.showInStatusPanel("\u{1F6A6} Настройки сети - работа без прокси"); // символ Светофор
			} else {
				prefs.setIntPref('network.proxy.type', 2);
				prefs.setStringPref("network.proxy.autoconfig_url", pac);
				e.target.style.setProperty("filter", icon_vpn, "important");
				this.showInStatusPanel("\u{1F6A6} Заблокированные сайты через «АнтиЗапрет»");
				// this.Notify('Proxy', 'Работаем через VPN Антизапрет');
			}
		},
		showInStatusPanel(info, ms = 5000) {
			var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
			if (StatusPanel.update.tid)
				clearTimeout(StatusPanel.update.tid)
			else {
				var {update} = StatusPanel;
				StatusPanel.update = () => {};
				StatusPanel.update.ret = () => {
					StatusPanel.update = update;
					StatusPanel.update();
				}
			}
			StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, ms);
			StatusPanel._label = info;
		},
		eyedropper(trg) { // Пипетка - захват цвета
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm")
				.require("devtools/client/menus").menuitems
				.find(menuitem => menuitem.id == "menu_eyedropper");
			(this.eyedropper = target => obj.oncommand({target}))(trg);
		},
		Notify(title, text, ms = 3000){
			Cc['@mozilla.org/alerts-service;1'].getService(Ci.nsIAlertsService).showAlertNotification(null, title, text, false, '', null, ms);
		},
		switchToTab(e = this, url) { // открыть вкладку | закрыть, если открыта
			for(var tab of e.view.gBrowser.tabs)
				if ( tab.linkedBrowser.currentURI.spec == url ) {e.view.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть
			e.view.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
		}
	};
}); // END ToggleAboutConfig

Dobrov пишет

Вместе с правым кликом в MacOS и вероятно Linux открывается контекстное меню панели

Посмотрел на Mint, и точно, так и есть.
Тогда можно попробовать манипуляцию атрибутом "context".

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

Выделить код

Код:

(async (bar, exp, tid, self) => CustomizableUI.createWidget(self = {
	label: "Панели, Папки",
	tooltiptext: [
		"ЛКМ:	★ Закладки\n…+ Alt	Домашняя папка",
		"ПКМ:	⟳ История\n…+ Alt	Папка установки",
		"СКМ:	Папка профиля\n…+ Alt	user_chrome_files"
	].join("\n"),
	id: "add-additional-personaltoolbar-button",
	localized: false,
	onCreated(btn) {
		btn.onclick = this.click;
		btn.toggleAttribute("context");
		btn.style.setProperty("list-style-image", "url(chrome://user_chrome_files/content/vertical_top_bottom_bar/svg/bookmark-16.svg)", "important");
	},
	exec(num, win) {
		tid = null;
		self[num](win);
	},
	context(win) {
		var btn = win.document.getElementById(this.id);
		btn.removeAttribute("context");
		btn.dispatchEvent(new win.MouseEvent("contextmenu", this.a));
		btn.toggleAttribute("context");
	},
	a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

	click(e) {
		if (e.detail > 2) return;
		var n2 = e.button != 2;
		var dbl = e.detail == 2;
		var num = 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

		if (!self[num]) {
			if (n2) return;
			num = "context";
			for(var p in self.a) self.a[p] = e[p];
		}

		var win = e.view;
		if (dbl) tid &&= win.clearTimeout(tid), self[num](win);
		else tid = win.setTimeout(self.exec, 300, num, win);
	},
	0:   w => bar(w, "viewBookmarksSidebar"), // ЛКМ
	2:  () => exp("Home"), // Alt+ЛКМ
	16: () => exp("ProfD"), // СКМ
	18: () => exp("UChrm", "user_chrome_files"), // Alt+СКМ
	32:  w => bar(w, "viewHistorySidebar"), // ПКМ
	34: () => exp("GreD"), // Alt+ПКМ

	1(win) { // Double Left Click
		win.alert("DBL Click");
	},
	33(win) { // Double Right Click
		win.alert("DBL Right Click");
	},
	29(win) { // Ctrl + Shift + Double Middle Click
		win.alert("Ctrl + Shift + DBL Middle Click");
	},
}))(
	(win, bar) => win.SidebarUI.toggle(bar),
	(dir, sub) => {
		dir = Services.dirsvc.get(dir, Ci.nsIFile);
		sub && dir.append(sub);
		dir.exists() && dir.launch();
	}
);


А ещё у меня там не работают альт-клики.
Насколько я понял, mousedown занят системой:
альт-левый — перетаскивание окна,
альт-средний — менюшка как по Alt+Пробел,
альт-правый — ресайз окна.
Но у тебя, видимо, такого нет.


Dobrov пишет

скрипт перехвата кликов на downloads-button, PanelUI-menu

Там полную раскликовку сделать сложнее,
потому что панельки из них вываливаются по mousedown.

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

Выделить код

Код:

/*
	listener = e => { var trg = e.target; // Downloads Clicks
		if (e.button == 1) {
			if (e.shiftKey) { // СКМ + Shift
				if ( prefs.getIntPref("permissions.default.image", 1) == 1)
					prefs.setIntPref("permissions.default.image", 2), trg.style.filter = "hue-rotate(180deg) brightness(95%)"
				else
					prefs.setIntPref("permissions.default.image", 1), trg.style.filter = "";
				BrowserReload();
			} else	// СКМ Click
				saveSelectionToTxt(); // сохранить .txt
		} else if (e.button == 2) {
			if (e.shiftKey)
				Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
			else	// ПКМ Click
				save(); // Single HTML
		}
	},
	listener_pui = e => { // PanelUI-menu Clicks
		if (e.button == 1) {
			if (e.altKey)
				window.BrowserFullScreen()
			else
			if( window.windowState != window.STATE_MAXIMIZED )
				window.maximize()
			else
				window.restore();
		} else
			if (e.button == 2) if (e.altKey) return
		else {
			e.stopPropagation();
			(e.shiftKey) ? window.close() : window.minimize();
		}
	}, // end Clicks
*/
	tid, allowMousedown, listener = {
		handleEvent(e) {
			if (e.detail > 2) return;
			var btn = e.target;
			var dbl = e.detail == 2;
			var num = (btn == pui && 100) +
				16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

			if (!this[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "mousedown";
			}
			if (dbl) tid &&= clearTimeout(tid), this[num](btn);
			else tid = setTimeout(this.exec, 300, num, btn, this);
		},
		exec(num, btn, self) {
			tid = null;
			self[num](btn);
		},
		mousedown(btn) {
			allowMousedown = true;
			btn.dispatchEvent(new MouseEvent("mousedown", {}));
			allowMousedown = false;
		},
		context(btn) {
			btn.removeAttribute("context");
			btn.dispatchEvent(new MouseEvent("contextmenu", this.a));
			btn.toggleAttribute("context");
		},
		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

		/*** ======= Downloads Clicks ======= ***/

		16: saveSelectionToTxt, // СКМ Click (сохранить .txt)
		20(btn) { // СКМ + Shift
			var pref = "permissions.default.image";
			var one = prefs.getIntPref(pref) == 1;
			prefs.setIntPref(pref, one ? 2 : 1);
			btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : "";
			BrowserReload();
		},
		32: save, // ПКМ Click (Single HTML)
		36() { // Shift + ПКМ
			Downloads.getSystemDownloadsDirectory()
				.then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
		},

		1() { // Double Left Click
			alert("Double Left Click");
		},
		41() { // Ctrl + Double Right Click
			alert("Ctrl + Double Right Click");
		},
		4() { // Shift + ЛКМ
			alert("Shift + ЛКМ");
		},
		
		/*** ======= PanelUI-menu Clicks ======= ***/

		118: BrowserFullScreen, // Alt + СКМ
		116() { // СКМ
			windowState != STATE_MAXIMIZED ? maximize() : restore();
		},
		132() { // ПКМ
			minimize();
		},
		136: BrowserTryToCloseWindow, // Shift + ПКМ
		134() { // Alt + ПКМ
			gCustomizeMode.enter();
		},

		101() { // Double Left Click
			alert("Double Left Click");
		},
		141() { // Ctrl + Double Right Click
			alert("Ctrl + Double Right Click");
		},
		104() { // Shift + ЛКМ
			alert("Shift + ЛКМ");
		},

	}, // end Clicks
Выделить код

Код:

//	btn.setAttribute("context", "event.stopPropagation()");
Выделить код

Код:

/*
	btn.addEventListener("click", listener), pui.addEventListener("click", listener_pui);
		window.addEventListener("keydown", keydown_win);
	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		btn.removeEventListener("click", listener), pui.removeEventListener("click", listener_pui);
		window.removeEventListener("keydown", keydown_win);
	}};
*/
	var mouseenter = function(e) {
		this.parentNode.addEventListener("mousedown", stop, true);
		this.addEventListener("mouseleave", mouseleave, {once: true});
	}
	var mouseleave = function(e) {
		this.parentNode.removeEventListener("mousedown", stop, true);
	}
	var stop = e => {
		e.button || allowMousedown || e.stopImmediatePropagation();
	}
	var btns = [btn, pui];
	for(var b of btns) {
		b.toggleAttribute("context"),
		b.addEventListener("click", listener, true),
		b.addEventListener("mouseenter", mouseenter);
	}
	window.addEventListener("keydown", keydown_win);

	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		for(var b of btns) {
			b.removeEventListener("click", listener, true);
			b.removeEventListener("mouseenter", mouseenter);
			if (b.matches(":hover"))
				b.removeEventListener("mouseleave", mouseleave),
				b.parentNode.removeEventListener("mousedown", stop, true);
		}
		window.removeEventListener("keydown", keydown_win);
	}};

Dobrov пишет

В скрипт Quick Toggle

Уволь.

Dumby присоеденяюсь к просьбе ВВП https://forum.mozilla-russia.org/viewto … 45#p795945 на счет скрипта памяти

Dumby - спасибо, я немного переделал код и обновил профиль-сборку скриптов.

Dumby пишет

А ещё у меня там не работают альт-клики. Насколько я понял, mousedown занят системой

Не системой, а демоном горячих клавиш, которые можно изменить в Настройках из меню.
перетаскивание/Resize окна вместе с Alt - это стандарт, но мета-клавишу можно в настройках поменять на Ctrl или Alt или Win…

egorsemenov06 пишет

присоеденяюсь к просьбе

Какой просьбе? Нет там никакой просьбы.

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

Есть сетование на трудности рихтовки и констатация
отсутствия реакции индикатора на очистку памяти фишкой.


Но индикатор и не должен напрямую реагировать ни на какие фишки.
Пара десятков миллисекунд на получение данных о памяти,
затем delay (две секунды) пауза, и так по кругу, вот и вся деятельность.
Дополнительный пинок только при открытии нового окна браузера.


И код рабочий, так что, если фишка действительно чистит память,
то индикатор это покажет, не прям сразу, но не позднее delay.
И да, если вдруг надо поставить (для теста) второй экземпляр кода,
то следует изменить id (в конце, "ucf-mem-indicator"), а то будут накладки.


И ещё там есть вопрос, можно ли сделать подобный индикатор,
но на другом принципе — использовать nsIMemoryReporterManager.


Когда-то давно я уже отвечал на это, целый трактат написал.
Суть — можно, но тогда результат будет включть только память DOM-процессов,
то есть выпадут процессы gpu, socket, rdd, может ещё какие-то подобные.

Dumby пишет
egorsemenov06 пишет

присоеденяюсь к просьбе

Какой просьбе? Нет там никакой просьбы.

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

Есть сетование на трудности рихтовки и констатация
отсутствия реакции индикатора на очистку памяти фишкой.


Но индикатор и не должен напрямую реагировать ни на какие фишки.
Пара десятков миллисекунд на получение данных о памяти,
затем delay (две секунды) пауза, и так по кругу, вот и вся деятельность.
Дополнительный пинок только при открытии нового окна браузера.


И код рабочий, так что, если фишка действительно чистит память,
то индикатор это покажет, не прям сразу, но не позднее delay.
И да, если вдруг надо поставить (для теста) второй экземпляр кода,
то следует изменить id (в конце, "ucf-mem-indicator"), а то будут накладки.


И ещё там есть вопрос, можно ли сделать подобный индикатор,
но на другом принципе — использовать nsIMemoryReporterManager.


Когда-то давно я уже отвечал на это, целый трактат написал.
Суть — можно, но тогда результат будет включть только память DOM-процессов,
то есть выпадут процессы gpu, socket, rdd, может ещё какие-то подобные.

Спасибо! понятно что ни чего не понятно.

Dumby - Помоги исправить код перехода в "Адаптивный дизайн".


код в сложной кнопке QuickToggle.js работает, но если несколько раз включить/выключить Адаптивный дизайн через кнопку, то это переключение перестаёт работать.
и не работает в простой кнопке, в которую ты мне клики добавил:

Выделить код

Код:

responsiveUI(trg) {
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_responsiveUI");
			(this.responsiveUI = target => obj.oncommand({target}))(trg);
		},

строка 155 - ucf_hookClicks.js

Выделить код

Код:

(async (id, func) => { // дополнительные клики на downloads-button, PanelUI-menu для custom_script_win.js
	await window.delayedStartupPromise;
	var btn = document.getElementById("downloads-button"), pui = document.getElementById("PanelUI-menu-button");
	if (!btn) return; btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) +`

Двойной клик:  открыть [Загрузки]
…на картинке: ⧉ найти Похожие\n
Правый клик (Alt+S): Сохранить
   в единый html всё / выделенное
…дважды  Картинки вкл/выкл\n
Ролик:	 Сохранить как файл .txt
Колёсико на рисунке: ➜ Сохранить`,	PanelUI_help =

`Клик мыши:	меню Firefox ${Services.appinfo.platformVersion}
…+ дважды	⊠ закрыть браузер
…+ Alt		Обновить без кэша
…+ Shift		⚑ Краткая справка\n
Правый клик	⇲ Свернуть
…+ Alt		Персонализация
Колёсико:	Развернуть | окно
…+ Alt		Полный экран`;

	var addDestructor = nextDestructor => {
		var {destructor} = ucf[id];
		ucf[id].destructor = () => {
			try {destructor();} catch(ex) {Cu.reportError(ex);}
			nextDestructor();
		}
	},
	showInStatusPanel = (info, time = 5000) => {
		var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
		if (StatusPanel.update.tid)
			clearTimeout(StatusPanel.update.tid)
		else {
			var {update} = StatusPanel;
			StatusPanel.update = () => {};
			StatusPanel.update.ret = () => {
				StatusPanel.update = update;
				StatusPanel.update();
			}
		}
		StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time);
		StatusPanel._label = info;
	},
	saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt
		var splice = saveURL.length == 10;
		var msgName = id + ":Save:GetSelection";
		var receiver = msg => {
			var title = document.title || gBrowser.selectedTab.label;
			var args = [
				"data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data),
				title.replace(/[:\\\/<>?*|"]+/g,'_').replace(/\s+/g,' ').slice(0, 100).trim() + '_' + new Date().toLocaleString('ru').replace(', ','-').replace(/:/g, '։') + '.txt',
				null, false, true, null, window.document
			];
			splice && args.splice(5, 0, null);
			saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60));
		}
		messageManager.addMessageListener(msgName, receiver);
		addDestructor(() => messageManager.removeMessageListener(msgName, receiver));
		var func = fm => {
			var res, fed, win = {}, 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("saveSelectionToTxt", res);
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
		(saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end

	save = async () => { // автор: Лекс, правка: Dumby, Dobrov (пути сохранения HTML)
		var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML";
		if (typeof IOUtils != "object") { // Firefox 78 ESR
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			var PathUtils = {join: (...args) => OS.Path.join(...args)};
			var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
		}
		var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8";

		var Title = (type) => { // получить заголовок (без обрезки, если type не указан) или домен (type <0)
			var title = (document.title || gBrowser.selectedTab.label);
			if ( !type ) return title; // заголовок
			if ( type > 0 ) return title.slice(0, type).replace(/ \| Форум Mozilla Россия$| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); // ограничить длину имени
			var host = (/^file:\/\//.test(gURLBar.value)) ? '' : gURLBar.value.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,'');
			return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns');
		}
		var msgListener = async msg => {
			var [fileContent, fileName] = msg.data, dir;
			try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
			var arr = prefs.getStringPref("ucf_save.dirs", "_Web||_Images|0").split('|').slice(0, 2); // Dir/subdir: пусто|0 title|1 домен
			arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен
			arr.forEach(dir.append); // для ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/имя вкладки
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать папку, если не существует…
			var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			file.initWithPath(dir.path);
			dir.append(fileName);
			await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60));
			var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download
			(await Downloads.getList(Downloads.ALL)).add(d);
			d.refresh(d.succeeded = true); // кнопка Загрузки мигает
		}
		messageManager.addMessageListener(msgName, msgListener);
		addDestructor(() => messageManager.removeMessageListener(msgName, msgListener));

		var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services';
		var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName));
		(save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end save

	tid, allowMousedown, listener = {
		handleEvent(e) {
			if (e.detail > 2) return;
			var btn = e.target;
			var dbl = e.detail == 2;
			var num = (btn == pui && 100) + 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

			if (!this[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "mousedown";
			}
			if (dbl) tid &&= clearTimeout(tid), this[num](btn);
			else tid = setTimeout(this.exec, 300, num, btn, this);
		},
		exec(num, btn, self) {
			tid = null;
			self[num](btn);
		},
		mousedown(btn) {
			allowMousedown = true;
			btn.dispatchEvent(new MouseEvent("mousedown", {}));
			allowMousedown = false;
		},
		context(btn) {
			btn.removeAttribute("context");
			btn.dispatchEvent(new MouseEvent("contextmenu", this.a));
			btn.toggleAttribute("context");
		},
		switchToTab(but, url) { // открыть вкладку | закрыть, если открыта
			for(var tab of but.ownerGlobal.gBrowser.tabs)
				if ( tab.linkedBrowser.currentURI.spec == url ) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть
			but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
		},
		responsiveUI(trg) {
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_responsiveUI");
			(this.responsiveUI = target => obj.oncommand({target}))(trg);
		},

		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

/*** ======= Downloads Clicks ======= ***/

		16: saveSelectionToTxt, // СКМ Click (сохранить .txt)
		1() { // Double Left Click - Обзор папки «Загрузки»
			Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
		},
		32: save, // ПКМ Click (Single HTML)
		33(btn) { // Double Right Click
			var pref = "permissions.default.image";
			var one = prefs.getIntPref(pref) == 1;
			prefs.setIntPref(pref, one ? 2 : 1);
			btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : "";
			BrowserReload();
		},
		2() { // Alt + ЛКМ
		},

/*** ======= PanelUI-menu Clicks ======= ***/

		102: BrowserReloadSkipCache, // Alt + Click
		104() { // Shift + ЛКМ
			var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762'];
			var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
			(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(pui, help_ucf[0]) : this.switchToTab(pui, help_ucf[1]);
		},
		118: BrowserFullScreen, // Alt + СКМ
		116() { // СКМ
			windowState != STATE_MAXIMIZED ? maximize() : restore();
		},
		132() { // ПКМ
			minimize();
		},
		134() { // Alt + ПКМ
			gCustomizeMode.enter();
		},
		101: BrowserTryToCloseWindow, // Double Left Click
		133(pui) { // Double Right Click
			responsiveUI(pui);
		},

	}, // end Clicks

	keydown_win = e => { // нажатие клавиш
		if (e.keyCode == 83 && e.altKey) e.shiftKey // Alt+S [+Shift]
		? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID
	},
	{prefs, dirsvc} = Services, linux = /macos|linux/.test(AppConstants.platform),
	save_ex = "_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action", singlesave;
	prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки

	(async () => { setTimeout(()=> { // дополнить подсказки
			singlesave = document.getElementById(save_ex);
			singlesave ? btn.tooltipText = btn.tooltipText + '\n\nAlt⇧S	 ⌨ нажатие SingleSave' : 0;
			if (!/Закрыть/.test(pui.tooltipText)) pui.tooltipText = PanelUI_help;
		}, linux ? 3000 : 9000); // ожидание для Windows больше
	})();
	var mouseenter = function(e) {
		this.parentNode.addEventListener("mousedown", stop, true);
		this.addEventListener("mouseleave", mouseleave, {once: true});
	}
	var mouseleave = function(e) {
		this.parentNode.removeEventListener("mousedown", stop, true);
	}
	var stop = e => {
		e.button || allowMousedown || e.stopImmediatePropagation();
	}
	var btns = [btn, pui];
	for(var b of btns) {
		b.toggleAttribute("context"),
		b.addEventListener("click", listener, true),
		b.addEventListener("mouseenter", mouseenter);
	}
	window.addEventListener("keydown", keydown_win);

	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		for(var b of btns) {
			b.removeEventListener("click", listener, true);
			b.removeEventListener("mouseenter", mouseenter);
			if (b.matches(":hover"))
				b.removeEventListener("mouseleave", mouseleave),
				b.parentNode.removeEventListener("mousedown", stop, true);
		}
		window.removeEventListener("keydown", keydown_win);
	}};

	ucf.unloadlisteners.push(id);

})("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	mainWin = {};
	focus.getFocusedElementForWindow(content, true, mainWin);
	mainWin = mainWin.value;

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0];
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	if (title) head.appendChild(title.cloneNode(true));

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};

	var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).trim();
	fileName += "_" + new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։");
	if (!/\.html?$/.test(fileName)) fileName += '.html';

	sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', fileName]);

}); // END hookClicks

Ещё вопрос - как имитировать нажатие клавиш, например "послать" переключения в режим "Адаптивный дизайн" - у меня это Option+Command+M (Win+Alt+M)
пробовал, но не пашет:  window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).sendKeyEvent("keypress", 0, "j".charCodeAt(0), 0x08)

Dobrov пишет

не работает
responsiveUI(pui);

this.responsiveUI(pui);


Или (для понимания) без промежуточности

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

Выделить код

Код:

.....
		133(pui) {
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_responsiveUI");
			(this[133] = target => obj.oncommand({target}))(pui);
		},

не пашет
window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).sendKeyEvent

На FF87 у window нет метода QueryInterface(),
и у nsIDOMWindowUtils нет метода sendKeyEvent().

Dumby - спасибо, исправил и доработал код.


Вопрос - как сделать перехват кликов на кнопках расширений, которые динамически появляются?
То есть, при старте браузера кнопки Reader View в строке адреса нет, она появляется, когда открывается сайт.
Будет удобнее, если у кнопки режима чтения появится ещё действие - например альтернативный режим просмотра.
То есть, нужно на кнопке Reader View по клику колёсиком включать/выключать режим "Адаптивный дизайн".

Выделить код

Код:

win.document.getElementById("pageAction-panel-_2495d258-41e7-4cd5-bc7d-ac15981f064e_").click()
trg.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн
id="pageAction-panel-_2495d258-41e7-4cd5-bc7d-ac15981f064e_" Reader View в меню Действия на странице
id="pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_" в строке адреса
actionid="_2495d258-41e7-4cd5-bc7d-ac15981f064e_"

Желательно добавить перехват кликов в тот же UCF-скрипт, который расширяет возможности кнопок Меню и Загрузки:

ucf_hookClicks.js

Выделить код

Код:

(async (id, func) => { // дополнительные клики на downloads-button, PanelUI-menu для custom_script_win.js
	await window.delayedStartupPromise;
	var btn = document.getElementById("downloads-button"), pui = document.getElementById("PanelUI-menu-button");
	if (!btn) return; btn_help =`

Двойной клик:  открыть [Загрузки]
…на картинке: ⧉ найти Похожие\n
Правый клик (Alt+S):  Сохранить
   в единый html всё / выделенное
…дважды  Картинки вкл/выкл\n
Ролик:	 Сохранить как файл .txt
Колёсико на рисунке: ➜ Сохранить`,	PanelUI_help =

`Клик мыши:	меню Firefox ${Services.appinfo.platformVersion}
…+ Shift		⚑ Краткая справка
…+ Alt		Персонализация
Клик дважды	⊠ закрыть браузер \n
Правый клик	⇲ Свернуть
…+ дважды	⤾ Вернуть вкладку
…+ Alt		Диспетчер задач
…+ Shift		Адаптивный дизайн\n
Колёсико:	Развернуть | окно
…+ Alt		Полный экран
…+ дважды	Обновить без кэша`, singlesave_help = `\nAlt⇧S	 ⌨ нажатие SingleSave`;

	var addDestructor = nextDestructor => {
		var {destructor} = ucf[id];
		ucf[id].destructor = () => {
			try {destructor();} catch(ex) {Cu.reportError(ex);}
			nextDestructor();
		}
	},
	showInStatusPanel = (info, time = 5000) => {
		var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
		if (StatusPanel.update.tid)
			clearTimeout(StatusPanel.update.tid)
		else {
			var {update} = StatusPanel;
			StatusPanel.update = () => {};
			StatusPanel.update.ret = () => {
				StatusPanel.update = update;
				StatusPanel.update();
			}
		}
		StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time);
		StatusPanel._label = info;
	},
	saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt
		var splice = saveURL.length == 10;
		var msgName = id + ":Save:GetSelection";
		var receiver = msg => {
			var title = document.title || gBrowser.selectedTab.label;
			var args = [
				"data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data),
				title.replace(/[:\\\/<>?*|"]+/g,'_').replace(/\s+/g,' ').slice(0, 100).trim() + '_' + new Date().toLocaleString('ru').replace(', ','-').replace(/:/g, '։') + '.txt',
				null, false, true, null, window.document
			];
			splice && args.splice(5, 0, null);
			saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60));
		}
		messageManager.addMessageListener(msgName, receiver);
		addDestructor(() => messageManager.removeMessageListener(msgName, receiver));
		var func = fm => {
			var res, fed, win = {}, 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("saveSelectionToTxt", res);
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
		(saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end

	save = async () => { // автор: Лекс, правка: Dumby, Dobrov (пути сохранения HTML)
		var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML";
		if (typeof IOUtils != "object") { // Firefox 78 ESR
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			var PathUtils = {join: (...args) => OS.Path.join(...args)};
			var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
		}
		var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8";

		var Title = (type) => { // получить заголовок (без обрезки, если type не указан) или домен (type <0)
			var title = (document.title || gBrowser.selectedTab.label);
			if ( !type ) return title; // заголовок
			if ( type > 0 ) return title.slice(0, type).replace(/ \| Форум Mozilla Россия$| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); // ограничить длину имени
			var host = (/^file:\/\//.test(gURLBar.value)) ? '' : gURLBar.value.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,'');
			return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns');
		}
		var msgListener = async msg => {
			var [fileContent, fileName] = msg.data, dir;
			try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
			var arr = prefs.getStringPref("ucf_save.dirs", "_Web||_Images|0").split('|').slice(0, 2); // Dir/subdir: пусто|0 title|1 домен
			arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен
			arr.forEach(dir.append); // для ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/имя вкладки
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать папку, если не существует…
			var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			file.initWithPath(dir.path);
			dir.append(fileName);
			await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60));
			var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download
			(await Downloads.getList(Downloads.ALL)).add(d);
			d.refresh(d.succeeded = true); // кнопка Загрузки мигает
		}
		messageManager.addMessageListener(msgName, msgListener);
		addDestructor(() => messageManager.removeMessageListener(msgName, msgListener));

		var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services';
		var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName));
		(save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	}, // end save

	tid, allowMousedown, listener = {
		handleEvent(e) {
			if (e.detail > 2) return;
			var btn = e.target;
			var dbl = e.detail == 2;
			var num = (btn == pui && 100) + 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;

			if (!this[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "mousedown";
			}
			if (dbl) tid &&= clearTimeout(tid), this[num](btn);
			else tid = setTimeout(this.exec, 300, num, btn, this);
		},
		exec(num, btn, self) {
			tid = null;
			self[num](btn);
		},
		mousedown(btn) {
			allowMousedown = true;
			btn.dispatchEvent(new MouseEvent("mousedown", {}));
			allowMousedown = false;
		},
		context(btn) {
			btn.removeAttribute("context");
			btn.dispatchEvent(new MouseEvent("contextmenu", this.a));
			btn.toggleAttribute("context");
		},
		switchToTab(but, url) { // открыть вкладку | закрыть, если открыта
			for(var tab of but.ownerGlobal.gBrowser.tabs)
				if ( tab.linkedBrowser.currentURI.spec == url ) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть
			but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
		},

		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

/*** ======= Downloads Clicks ======= ***/

		16: saveSelectionToTxt, // СКМ Click (сохранить .txt)
		1() { // Double Left Click - Обзор папки «Загрузки»
			Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
		},
		32: save, // ПКМ Click (Single HTML)
		33(btn) { // Double Right Click
			var pref = "permissions.default.image";
			var one = prefs.getIntPref(pref) == 1;
			prefs.setIntPref(pref, one ? 2 : 1);
			btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : "";
			BrowserReload();
		},
		2() { // Alt + ЛКМ
		},

/*** ======= PanelUI-menu Clicks ======= ***/

		102() { gCustomizeMode.enter(); // ЛКМ + Alt Персонализация
		},
		104() { // Shift + ЛКМ
			var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762'];
			var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
			(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(pui, help_ucf[0]) : this.switchToTab(pui, help_ucf[1]);
		},
		118: BrowserFullScreen, // Alt + СКМ
		116() { // СКМ
			windowState != STATE_MAXIMIZED ? maximize() : restore();
		},
		117: BrowserReloadSkipCache,  // СКМ Double
		132() { // ПКМ
			minimize();
		},
		134(pui) { // Alt + ПКМ
			this.switchToTab(pui, 'about:performance');
		},
		136(pui) { // Shift + ПКМ
			pui.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey
		},
		138(pui) { // Shift + Alt + ПКМ
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging");
			(this[136] = target => obj.oncommand({target}))(pui); // запуск пункта меню, у которого нет HotKey
		},
		101: BrowserTryToCloseWindow, // Double Left Click
		133(pui) { // ПКМ Double Right Click
			pui.ownerGlobal.undoCloseTab();
		},
	}, // end Clicks

	keydown_win = e => { // нажатие клавиш
		if (e.keyCode == 83 && e.altKey) e.shiftKey // Alt+S [+Shift]
		? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID
	},
	{prefs, dirsvc} = Services, linux = /macos|linux/.test(AppConstants.platform), singlesave;
	prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки

	var mouseenter = function(e) {
		this.parentNode.addEventListener("mousedown", stop, true);
		this.addEventListener("mouseleave", mouseleave, {once: true});

		if ((e.target == pui) && (!/справка/.test(pui.tooltipText)))
			pui.tooltipText = PanelUI_help; // обновить подсказки кнопок
		if (e.target == btn) {
			var singlesave = document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action"), dw;
			try {dw = prefs.getComplexValue("browser.download.dir",Ci.nsIFile)} catch {dw = dirsvc.get("DfltDwnld", Ci.nsIFile)};
		// if (!/Двойной/.test(btn.tooltipText))
			btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) + btn_help; // обновлять подсказку при наведении мыши
		if (singlesave){
			if (!/SingleSave/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + singlesave_help;
		} else
			btn.tooltipText = btn.tooltipText.replace(singlesave_help,'');
		if (!/выбранная/.test(btn.tooltipText))
			btn.tooltipText = btn.tooltipText + `${dw ? "\n\n[Загрузки] — выбранная папка:\n"+ dw.path.substring(0,33) + `${dw.path.length > 32 ? `…\n…${dw.path.substring(dw.path.length -31, dw.path.length)}`: ""}` : ""}`; // сократить/разбить длинную строку
		}
	}
	var mouseleave = function(e) {
		this.parentNode.removeEventListener("mousedown", stop, true);
	}
	var stop = e => {
		e.button || allowMousedown || e.stopImmediatePropagation();
	}
	var btns = [btn, pui];
	for(var b of btns) {
		b.toggleAttribute("context"),
		b.addEventListener("click", listener, true),
		b.addEventListener("mouseenter", mouseenter);
	}
	window.addEventListener("keydown", keydown_win);

	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		for(var b of btns) {
			b.removeEventListener("click", listener, true);
			b.removeEventListener("mouseenter", mouseenter);
			if (b.matches(":hover"))
				b.removeEventListener("mouseleave", mouseleave),
				b.parentNode.removeEventListener("mousedown", stop, true);
		}
		window.removeEventListener("keydown", keydown_win);
	}};

	ucf.unloadlisteners.push(id);

})("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	mainWin = {};
	focus.getFocusedElementForWindow(content, true, mainWin);
	mainWin = mainWin.value;

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0];
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	if (title) head.appendChild(title.cloneNode(true));

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};

	var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).trim();
	fileName += "_" + new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։");
	if (!/\.html?$/.test(fileName)) fileName += '.html';

	sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', fileName]);

}); // END hookClicks

Dumby
Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?

unter_officer пишет

Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?

пример уже есть в этом топике

Выделить код

Код:

(async (bar, exp, tid, self) => CustomizableUI.createWidget(self = { id: "test-button", localized: false, label: 
"Test Button", tooltiptext:
`ЛКМ:	Design View
CKM: folder UCF`,

	onCreated(btn) {
		btn.onclick = this.click;
		btn.toggleAttribute("context");
		btn.setAttribute("image", "chrome://browser/skin/preferences/application.png");
	},
	exec(num, win) {
		tid = null;
		self[num](win);
	},
	context(win) {
		var btn = win.document.getElementById(this.id);
		btn.removeAttribute("context");
		btn.dispatchEvent(new win.MouseEvent("contextmenu", this.a));
		btn.toggleAttribute("context");
	},
	a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

	click(e) {
		if (e.detail > 2) return;
		var n2 = e.button != 2;
		var dbl = e.detail == 2;
		var num = 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl;
		if (!self[num]) {
			if (n2) return;
			num = "context";
			for(var p in self.a) self.a[p] = e[p];
		}
		var win = e.view;
		if (dbl) tid &&= win.clearTimeout(tid), self[num](win);
		else tid = win.setTimeout(self.exec, 300, num, win);
	},
	0(win) { // ЛКМ
		var btn = win.document.getElementById(this.id);
		btn.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey
	},
	1(win) { // Double Left Click
		win.alert("DBL Click");
	},
	16: () => exp("UChrm", "user_chrome_files"), // СКМ
}))(
	(win, bar) => win.SidebarUI.toggle(bar),
	(dir, sub) => {
		dir = Services.dirsvc.get(dir, Ci.nsIFile);
		sub && dir.append(sub); dir.exists() && dir.launch();
	}
);

Dumby - ещё просьба вдобавок к первой:
подскажи код, чтобы выполнять скрипт из файла. То есть, кликом на кнопке выполняем внешний …/user_chrome_files/custom_scripts/user.js, в который должны быть переданы все текущие переменные, функции и т.п. той кнопки, из которой вызван внешний JS.


Может, это пригодиться для отладки скриптов без перезапуска браузера. А может есть более простой способ отладки части кода кнопки?
Наверное, код внешнего JS не должен попадать в startupCache, это верно?

Dobrov
Как пример вызова функции из другого скрипта можно посмотреть здесь, выше и ниже. Скрипт с функцией тоже должен быть загружен. Не знаю, это ли было нужно.
https://forum.mozilla-russia.org/viewtopic.php?pid=796057#p796057

xrun1 - не то. Цитата - Выполняем внешний скрипт. И никаких подгружаемых функций.

Dobrov пишет

при старте браузера кнопки Reader View в строке адреса нет, она появляется, когда открывается сайт

Значит слушать клики на родительском контейнере и проверять id кликнутого.

кликом на кнопке выполняем внешний …/user_chrome_files/custom_scripts/user.js, в который должны быть переданы все текущие переменные, функции и т.п. той кнопки, из которой вызван внешний JS
Наверное, код внешнего JS не должен попадать в startupCache, это верно?

Если scriptloader'ом по протоколу chrome:, то будет кэшироваться.
Можно использовать loadSubScriptWithOptions(),
но «все текущие переменные, функции» идут лесом,
разве что только в объект пробросить.

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

Выделить код

Код:

Services.scriptloader.loadSubScriptWithOptions(
	"chrome://user_chrome_files/content/custom_scripts/user.js",
	{ignoreCache: true, target: {myVariable1, myVariable2, myFunction}}
);


(new Function())(); тоже не подходит:
«Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope.»
Остаётся только direct eval().

То есть, нужно на кнопке Reader View по клику колёсиком включать/выключать режим "Адаптивный дизайн".
Желательно добавить перехват кликов в тот же UCF-скрипт, который расширяет возможности кнопок Меню и Загрузки

Вот, сводный пример. Добавляем после ucf.unloadlisteners.push(id);

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

Выделить код

Код:

.....
	var box = document.getElementById("page-action-buttons");
	var key = document.getElementById("key_responsiveDesignMode");
	var uri = Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/user.js");
	var boxLst = e => eval(Cu.readUTF8URI(uri));
	box.addEventListener("auxclick", boxLst, true);
	addDestructor(() => box.removeEventListener("auxclick", boxLst, true));


user.js

Выделить код

Код:

if (
	e.button == 1 &&
	e.target.id == "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_"
)
	e.stopImmediatePropagation(),
	key.doCommand();

this[136]

this[138], наверно.


unter_officer пишет

Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?

А в чём конкретно затруднение?
Там же просто получаем <key> и вызываем doCommand();

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

Выделить код

Код:

(async id => CustomizableUI.createWidget({
	get label() {
		var l10n = new (ChromeUtils.import("resource://devtools/shared/Loader.jsm")
			.require("devtools/shared/l10n")).MultiLocalizationHelper(
				"devtools/client/locales/startup.properties",
				"devtools/client/locales/menus.properties"
			);
		this.tooltiptext = l10n.getFormatStr(
			"toolbox.buttons.responsive",
			Services.appinfo.OS == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"
		);
		delete this.label;
		return this.label = l10n.getStr("responsiveDesignMode.label");
	},
	id: "ucf-responsiveDesignMode-btn",
	localized: false,
	onCreated(btn) {
		btn._handleClick = this.click;
		btn.style.cssText = `
			fill-opacity: 0 !important;
			-moz-context-properties: fill, fill-opacity !important;
			list-style-image: url(chrome://devtools/skin/images/command-responsivemode.svg) !important;
		`;
	},
	click() {
		this.ownerDocument.getElementById(id).doCommand();
	}
}))("key_responsiveDesignMode");

Dumby пишет

А в чём конкретно затруднение?
Там же просто получаем <key> и вызываем doCommand();

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

Выделить код

Код:

(async id => CustomizableUI.createWidget({
	get label() {
		var l10n = new (ChromeUtils.import("resource://devtools/shared/Loader.jsm")
			.require("devtools/shared/l10n")).MultiLocalizationHelper(
				"devtools/client/locales/startup.properties",
				"devtools/client/locales/menus.properties"
			);
		this.tooltiptext = l10n.getFormatStr(
			"toolbox.buttons.responsive",
			Services.appinfo.OS == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M"
		);
		delete this.label;
		return this.label = l10n.getStr("responsiveDesignMode.label");
	},
	id: "ucf-responsiveDesignMode-btn",
	localized: false,
	onCreated(btn) {
		btn._handleClick = this.click;
		btn.style.cssText = `
			fill-opacity: 0 !important;
			-moz-context-properties: fill, fill-opacity !important;
			list-style-image: url(chrome://devtools/skin/images/command-responsivemode.svg) !important;
		`;
	},
	click() {
		this.ownerDocument.getElementById(id).doCommand();
	}
}))("key_responsiveDesignMode");

Для меня это не так просто. scratch_one-s_head.gif


Dumby, большое спасибо.

Dumby - Спасибо! Ещё проблема: при возвращении из режима "Адаптивный дизайн" в обычный просмотр многие страницы остаются обрезанными, нужно F5 жать.
Как при возвращении в обычный просмотр ещё и обновить страницу? (но не обновлять её для Адаптивного дизайна)


Dumby - а как обновить подсказку для "Reader View" в панели адреса?
У меня только грубый вариант: зарегистрировать mouseenter и постоянно делать: ReaderView.tooltipText = подсказка……

Выделить код

Код:

var box = document.getElementById("page-action-buttons"); // кнопки панели адреса
var boxLst = e => {
	if (e.button == 1 && e.target.id == "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_") // Reader View
		e.stopImmediatePropagation(),	document.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн
}
box.addEventListener("auxclick", boxLst, true);
addDestructor(() => box.removeEventListener("auxclick", boxLst, true));

Dumby - Вопрос: в custom_script.js функция loadscript не грузит одну .JSM-ку, для которой нужна отдельная строка запуска :
ChromeUtils.import(`${scripts}/UCFTitleChangedChild.jsm`, {}).registerUCFTitleChanged(); // исправление заголовка вкладки

Выделить код

Код:

var EXPORTED_SYMBOLS = ["registerUCFTitleChanged", "UCFTitleChangedChild"];
function registerUCFTitleChanged() { // исправление заголовка вкладки …………

Попробуй исправить loadscript, чтобы он был более универсальный, а второй параметр мог быть именем выполняемой функции:
loadscript("UCFTitleChangedChild.jsm", registerUCFTitleChanged()); // так не работает!

Выделить код

Код:

const scripts = 'chrome://user_chrome_files/content/custom_scripts'; (async () => { // загрузка внешних js или jsm-скриптов
	var loadscript = (name, function_register) => {
		try { name.split('.').pop().split("?")[0].split("#")[0].toLowerCase() == "jsm"
			? ChromeUtils.import(`${scripts}/${name}`, {}).function_register
			: Services.scriptloader.loadSubScript(`${scripts}/${name}`,globalThis,"UTF-8");
			return true;
		} catch(e) {}
	};
	loadscript("ucf_eom-button.js"); // нижеследующая строка не работает:
	// loadscript("UCFTitleChangedChild.jsm", registerUCFTitleChanged()); 
})();
Dobrov пишет

но не обновлять её для Адаптивного дизайна

Можно проверять gBrowser.selectedBrowser.browsingContext.inRDMPane

как обновить подсказку для "Reader View" в панели адреса?

Если в смысле установить свою как для аддона, то вот вариант (в custom_script.js).

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

Выделить код

Код:

(async url => {
	var tooltip = "Test Tooltip";

	var m = "2495d258-41e7-4cd5-bc7d-ac15981f064e", id = `{${m}}`, aid = `_${m}_`;
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady);
	var onReady = (e, addon) => {
		if (addon.id != id) return;
		manager.off("ready", onReady);
		addon.once("shutdown", wait);
		manager.global.PageActions.actionForID(aid).setTooltip(tooltip);
	}
	manager.on("ready", onReady);

})("resource://gre/modules/ExtensionParent.jsm");

исправить loadscript

Что-то мне не слишком понятны код и задача, может так сойдёт?

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

Выделить код

Код:

(async scripts => {
	var re = /\.jsm$/i;
	var loadscript = name => {
		try {
			var {href, pathname} = new URL(scripts + name);
			if (re.test(pathname))
				return ChromeUtils.import(href);
			Services.scriptloader.loadSubScript(href);
			return true;
		}
		catch(ex) {Cu.reportError(ex);}
	}

	loadscript("ucf_eom-button.js");
	loadscript("UCFTitleChangedChild.jsm")?.registerUCFTitleChanged?.();

})("chrome://user_chrome_files/content/custom_scripts/");


Добавлено: Хотя нет, сам вызов же может завершиться с ошибкой.
Тогда затащим внутрь, как написано, второй параметр — имя выполняемой функции.
скрытый текст

Выделить код

Код:

(async scripts => {
	var re = /\.jsm$/i;
	var loadscript = (name, funcName) => {
		try {
			var {href, pathname} = new URL(scripts + name);
			if (re.test(pathname)) {
				var obj = ChromeUtils.import(href);
				funcName && obj[funcName]();
			} else
				Services.scriptloader.loadSubScript(href);
			return true;
		}
		catch(ex) {Cu.reportError(ex);}
	}

	loadscript("ucf_eom-button.js");
	loadscript("UCFTitleChangedChild.jsm", "registerUCFTitleChanged");

})("chrome://user_chrome_files/content/custom_scripts/");

Dumby - Спасибо! обновление подсказки кнопки также работает из custom_script_win.js.


Dumby - проверь мой новый загрузчик: (сократил, чтобы не повторять строки с loadscript)
Переделал через список массива  js-jsm скриптов. Путь к скриптам используется ещё для подключения [CB]-кодов, поэтому константа.

Выделить код

Код:

const scripts = 'chrome://user_chrome_files/content/custom_scripts/'; (async () => { // ваши скрипты
	[['ucf_QuickToggle.js'], ['UCFTitleChangedChild.jsm', 'registerUCFTitleChanged'], ['Test.jsm']]
	.forEach(function(name) { try { if (/\.jsm$/i.test(name[0])) { // [скрипт js или jsm, инициализация]
				var obj = ChromeUtils.import(scripts + name[0]);
				name[1] && obj[name[1]]();
			} else Services.scriptloader.loadSubScript(scripts + name[0]);
		} catch(ex) {Cu.reportError(ex);}
	});
})();
Dobrov пишет

проверь мой новый загрузчик: (сократил, чтобы не повторять строки с loadscript)

Ну, выглядит нормально.
Но, замысел целиком мне же неизвестен.
Вот зачем тогда функция что-то возвращает, раз это не используется.


Или, в исходнике, name.split('.').pop().split("?")[0].split("#")[0].toLowerCase()
наводило на мысль, что будут присутствовать имена типа "SomeModule.JsM?q=lol#bla",
но ничего подобного пока не видно, хотя, может потом добавятся, а если нет, то зря new URL() создаётся.
Короче — ничего серьёзного.

Dumby - вопрос по коду обновления ToolTip кнопки расширения.
Почему-то подсказка для Video DownloadHelper не обновляется! И как переделать код для замены Tooltip на нескольких кнопках расширений?

Выделить код

Код:

var view_id = "2495d258-41e7-4cd5-bc7d-ac15981f064e"; // Reader View
var vdh_id = "b9db16a4-6edc-47ec-a1f4-b86292ed211d"; // Video DownloadHelper
var manager = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm").ExtensionParent.apiManager,
wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady),
onReady = (e, addon) => {
	// if (addon.id != `{${view_id}}`) return;
	if (addon.id == `{${view_id}}`) {
		manager.off("ready", onReady), addon.once("shutdown", wait);
		manager.global.PageActions.actionForID(`_${view_id}_`).setTooltip(`Reader View	${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n\nКлик мыши	Режим для чтения\nКолёсико	Адаптивный дизайн`); // изменить подсказку
	}
	if (addon.id == `{${vdh_id}}`) {
		manager.off("ready", onReady), addon.once("shutdown", wait);
		manager.global.PageActions.actionForID(`_${vdh_id}_`).setTooltip(`Video DownloadHelper\nСкачивание проигрываемого видео`);
	}
};	manager.on("ready", onReady);
Dobrov пишет

Video DownloadHelper

Совсем разные вещи. У RV pageAction, а у VDH browserAction.

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

Выделить код

Код:

(async url => {
	// Reader View
	var rv = "2495d258-41e7-4cd5-bc7d-ac15981f064e";
	var rv_id = `{${rv}}`, rv_aid = `_${rv}_`;
	var rv_ttt = `Reader View	${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n\nКлик мыши	Режим для чтения\nКолёсико	Адаптивный дизайн`;

	// Video DownloadHelper
	var vdh_id = "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}";
	var vdh_ttt = "Video DownloadHelper\nСкачивание проигрываемого видео";

	var count = 0;
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var wait = (e, isAppShutdown) => isAppShutdown || !--count || manager.on("ready", onReady);
	var onReady = (e, addon) => {
		if (addon.id == rv_id)
			manager.global.PageActions.actionForID(rv_aid).setTooltip(rv_ttt);
		else if (addon.id == vdh_id)
			setVDHTooltip(addon);
		else return;

		++count == 2 && manager.off("ready", onReady);
		addon.once("shutdown", wait);
	}
	manager.on("ready", onReady);

	var setVDHTooltip = addon => {
		var vdh_wid = `_${vdh_id.slice(1, -1)}_-browser-action`;
		var {gPalette} = Cu.import("resource:///modules/CustomizableUI.jsm", {});

		var upd = manager.global.browserAction.prototype.updateButton;
		var asgn = eval(`({${upd}})`.replace(/\n^.+"tooltiptext".+$/m, ""));

		(setVDHTooltip = addon => {
			var widget = gPalette.get(vdh_wid);
			widget.tooltiptext = vdh_ttt;

			var {action} = manager.global.browserActionFor(addon);
			Object.assign(action.buttonDelegate, asgn);
			for(var [, node] of widget.instances)
				node.setAttribute("tooltiptext", vdh_ttt);
		})(addon);
	}
})("resource://gre/modules/ExtensionParent.jsm");

Dumby - ты делал перехват кликов для кнопок в панели адреса в скрипте ucf_hookClicks.js.
Получилась обработка кликов двумя дублирующими способами: первый до ucf.unloadlisteners, затем для кнопок на панели адреса.
Второй способ перехватывает клики всех кнопок page-action-buttons, а при обработке проверяется id кнопки.


Возможно ли доработать код, чтобы сразу перехватывать клики кнопок "nav-bar-customization-target" основной панели и "page-action-buttons" панели адреса ?
Выгода этого способа в том, что проще в одном скрипте прописать дополнительные клики нужных кнопок, а не делать кучу скриптов, где каждая кнопка обрабатывается персонально. Также прошу по возможности добавить действие на долгое нажатие кнопки, так как у меня перестал обрабатываться долгий клик в скрипте ToggleAboutConfig, когда я добавил addEventListener("mouseenter" для nav-bar-customization-target в скрипт ucf_hookClicks.


Ещё хотелка - добавить перехват "wheel". Ожидаемый итог работы кода: перехват событий кнопок для двух панелей, разбор такой же, как в твоём коде перехвата кликов: 512: saveSelectionToTxt, // СКМ Click (сохранить .txt) цифра содержит сумму событий: id кнопки, клавиш мыши, мета-клавиш, тип кликов, скролл над кнопками тулбара…
Удобнее сделать изменение яркости скролом над панелью безопасности "identity-box", чем над Звёздочкой. А скролл над кнопками основной панели определять отдельно для каждой, то есть добавить флаги e.scroll+ и e.scroll- так же, как сделано для dbl (дубль-клик).

ucf_hookClicks.js - поменял подсчёт кнопок и клавиш - строка 136

Выделить код

Код:

(async (id, func) => { // для custom_script_win.js: дополнительные клики и подсказки кнопок
	await window.delayedStartupPromise; var
	box = document.getElementById("page-action-buttons"), // кнопки панели адреса
	nav = document.getElementById("nav-bar-customization-target"), // кнопки панели
	btn = document.getElementById("downloads-button"), // 0 Загрузки
	pui = document.getElementById("PanelUI-menu-button"), // 1 меню
	fav = document.getElementById("star-button"), // 2
	prn = document.getElementById("print-button"), // 3
	rv = "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_", // 4 Reader View
	sgs = "_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action", // SingleSave button
	vdh = "_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action"; /*Video DownloadHelper*/ if (!btn) return; btn_help =`

Двойной клик: ⬇︎ открыть [Загрузки]
…на картинке: ⧉ найти Похожие\n
Правый клик (Alt+S):  Сохранить
   в единый html всё / выделенное
…дважды  Картинки вкл/выкл\n
Ролик:	 Сохранить как файл .txt
Колёсико на рисунке: ➜ Сохранить`,	PanelUI_help =

`Клик мыши:	меню Firefox ${Services.appinfo.platformVersion}
…+ Shift		⚑ Краткая справка
…+ Alt		Персонализация
Клик дважды	⊠ закрыть браузер \n
Правый клик	⇲ Свернуть
…+ дважды	⤾ Вернуть вкладку
…+ Alt		Диспетчер задач
…+ Shift		Адаптивный дизайн\n
Колёсико:	Развернуть | окно
…+ Alt		Полный экран
…+ дважды	Обновить без кэша`, sgs_help = `\nAlt⇧S	 ⌨ нажатие SingleSave`, rv_help =

`Reader View	${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n
Клик мыши	Режим для чтения
Колёсико	Адаптивный дизайн`; // vdh_help =`Video DownloadHelper\nСкачивание проигрываемого видео`;

	var addDestructor = nextDestructor => {
		var {destructor} = ucf[id];
		ucf[id].destructor = () => {
			try {destructor();} catch(ex) {Cu.reportError(ex);}
			nextDestructor();
		}
	},
	showInStatusPanel = (info, time = 5000) => {
		var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel;
		if (StatusPanel.update.tid)
			clearTimeout(StatusPanel.update.tid)
		else {
			var {update} = StatusPanel;
			StatusPanel.update = () => {};
			StatusPanel.update.ret = () => {
				StatusPanel.update = update;
				StatusPanel.update();
			}
		}
		StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time);
		StatusPanel._label = info;
	},
	Title = (max, title) => { // получить заголовок. без обрезки: max не указан, домен: max <0, + дата: max=0
		if (!title) var title = document.title || gBrowser.selectedTab.label;
		if (max == undefined) return title; // заголовок как есть или ограничить длину, убрать служебные символы
		title = title.replace(/[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim();
		if ( max > 0 ) return title.slice(0, max);
		if ( max == 0) return title.slice(0, 100) +"_"+ new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։");
		var host = decodeURIComponent(gURLBar.value); // max < 0
		if (!/^file:\/\//.test(host)) host = host.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,'');
		return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns');
	},
	saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt
		var splice = saveURL.length == 10;
		var msgName = id + ":Save:GetSelection";
		var receiver = msg => {
			var title = Title(0);
			var args = [
				"data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data),
				title + '.txt', null, false, true, null, window.document];
			splice && args.splice(5, 0, null);
			saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60));
		}
		messageManager.addMessageListener(msgName, receiver);
		addDestructor(() => messageManager.removeMessageListener(msgName, receiver));
		var func = fm => {
			var res, fed, win = {}, 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("saveSelectionToTxt", res);
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
		(saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	},
	save = async () => { // SingleHtml by Лекс, правка: Dumby, Dobrov
		var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML";
		if (typeof IOUtils != "object") { // Firefox 78 ESR
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			var PathUtils = {join: (...args) => OS.Path.join(...args)};
			var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
		}
		var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8";
		var msgListener = async msg => {
			var [fileContent, fileName] = msg.data, dir; // fileName: выделенный текст или null
			try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
			var arr = prefs.getStringPref("ucf_save.dirs","_Web||_Images|0").split('|').slice(0,2); //subdir: title|host
			arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен
			arr.forEach(dir.append); // ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/label
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать, если не существует…
			var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			file.initWithPath(dir.path);
			if (!fileName) fileName = Title(100); // убрать служебные символы
			dir.append(Title(0, fileName) +'.html');
			await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60));
			var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download
			(await Downloads.getList(Downloads.ALL)).add(d);
			d.refresh(d.succeeded = true); // кнопка Загрузки мигает
		}
		messageManager.addMessageListener(msgName, msgListener);
		addDestructor(() => messageManager.removeMessageListener(msgName, msgListener));

		var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services';
		var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName));
		(save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	},
	tid, allowMousedown, listener = { // доп.события для 15 кнопок
		handleEvent(e) {
			if (e.detail > 2) return;
			var btn = e.target;
			var dbl = e.detail == 2;
			var num = e.button *512 + e.metaKey *256 + e.ctrlKey *128 + e.shiftKey *64 + e.altKey *32 + dbl *16 +
			(btn == document.getElementById(rv) && 4) +
			(btn == prn && 3) + (btn == fav && 2) + (btn == pui && 1);

			if (!this[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "mousedown";
			}
			if (dbl) tid &&= clearTimeout(tid), this[num](btn);
			else tid = setTimeout(this.exec, 300, num, btn, this);
		},
		exec(num, btn, self) {
			tid = null;
			self[num](btn);
		},
		mousedown(btn) {
			allowMousedown = true;
			btn.dispatchEvent(new MouseEvent("mousedown", {}));
			allowMousedown = false;
		},
		context(btn) {
			btn.removeAttribute("context");
			btn.dispatchEvent(new MouseEvent("contextmenu", this.a));
			btn.toggleAttribute("context");
		},
		switchToTab(but, url) { // открыть вкладку | закрыть, если открыта
			for(var tab of but.ownerGlobal.gBrowser.tabs)
				if ( tab.linkedBrowser.currentURI.spec == url ) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть
			but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
		},

		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0},

/*** ======= Downloads Clicks ======= ***/

		512: saveSelectionToTxt, // СКМ Click (сохранить .txt)
		16() { // Double Left Click - Обзор папки «Загрузки»
			Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки»
		},
		1024: save, // ПКМ Click (Single HTML)
		1040(btn) { // Double Right Click
			var pref = "permissions.default.image";
			var one = prefs.getIntPref(pref) == 1;
			prefs.setIntPref(pref, one ? 2 : 1);
			btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : "";
			BrowserReload();
		},

/*** ======= PanelUI-menu Clicks ======= 100*pui 32*e.button 8*e.ctrlKey 4*e.shiftKey 2*e.altKey dbl btn ***/

		33() { gCustomizeMode.enter(); // ЛКМ + Alt Персонализация
		},
		65() { // Shift + ЛКМ
			var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762'];
			var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
			(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(pui, help_ucf[0]) : this.switchToTab(pui, help_ucf[1]);
		},
		545: BrowserFullScreen, // Alt + СКМ
		513() { // СКМ
			windowState != STATE_MAXIMIZED ? maximize() : restore();
		},
		529: BrowserReloadSkipCache,  // СКМ Double
		1025() { // ПКМ
			minimize();
		},
		1057(pui) { // Alt + ПКМ
			this.switchToTab(pui, 'about:performance');
		},
		1089(pui) { // Shift + ПКМ
			pui.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey
			if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload();
		},
		1153(pui) { // Shift + Alt + ПКМ
			var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging");
			(this[138] = target => obj.oncommand({target}))(pui); // запуск пункта меню, у которого нет HotKey
		},
		17: BrowserTryToCloseWindow, // Double Left Click
		1041(pui) { // ПКМ Double Right Click
			pui.ownerGlobal.undoCloseTab();
		},
	}, // end Clicks

	keydown_win = e => { // нажатие клавиш
		if (e.keyCode == 83 && e.altKey) e.shiftKey // Alt+S [+Shift]
		? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID
		if (e.keyCode == 68 && e.altKey){ // Alt+D отладка - запуск внешнего JS
			// e.target.ownerDocument.getElementById("key_browserConsole").doCommand();
			eval(Cu.readUTF8URI(Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/User.js")));
			console.log("END User.js " + Math.random());
		}
	},
	{prefs, dirsvc} = Services, linux = /macos|linux/.test(AppConstants.platform), singlesave;
	prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки

	var hint_upd = function(btn, text, find) { // обновить подсказку
		return;
	}
	var mouseenter = function(e) {
		this.parentNode.addEventListener("mousedown", stop, true);
		this.addEventListener("mouseleave", mouseleave, {once: true});

		var sgs_btn = document.getElementById(sgs), dw;
		try {dw = prefs.getComplexValue("browser.download.dir",Ci.nsIFile)}
			catch {dw = dirsvc.get("DfltDwnld", Ci.nsIFile)};
		var rv_btn = document.getElementById(rv); // Reader View
		if (e.target == rv_btn) {
			rv_btn.tooltipText = rv_help;
		}
		// var vdh_btn = document.getElementById(vdh); // Video DownloadHelper
		// if (e.target == vdh_btn) {
		// 	vdh_btn.tooltipText = vdh_help;
		// }
		if ((e.target == pui) && (!/справка/.test(pui.tooltipText)))
			pui.tooltipText = PanelUI_help; // обновить подсказки кнопок
		if (e.target == btn) {
		// if (!/Двойной/.test(btn.tooltipText))
			btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) + btn_help; // обновлять подсказку при наведении мыши
		if (sgs_btn){
			if (!/SingleSave/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + sgs_help;
		} else
			btn.tooltipText = btn.tooltipText.replace(sgs_help,'');
		if (!/выбранная/.test(btn.tooltipText))
			btn.tooltipText = btn.tooltipText + `${dw ? "\n\n[Загрузки] — выбранная папка:\n"+ dw.path.substring(0,33) + `${dw.path.length > 32 ? `…\n…${dw.path.substring(dw.path.length -31, dw.path.length)}`: ""}` : ""}`; // сократить/разбить длинную строку
		}
	}
	var mouseleave = function(e) {
		this.parentNode.removeEventListener("mousedown", stop, true);
	}
	var stop = e => {
		e.button || allowMousedown || e.stopImmediatePropagation();
	}
	var btns = [btn, pui];
	for(var b of btns) {
		b.toggleAttribute("context"),
		b.addEventListener("click", listener, true),
		b.addEventListener("mouseenter", mouseenter);
	}

	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	ucf[id] = {destructor() {
		for(var b of btns) {
			b.removeEventListener("click", listener, true);
			b.removeEventListener("mouseenter", mouseenter);
			if (b.matches(":hover"))
				b.removeEventListener("mouseleave", mouseleave),
				b.parentNode.removeEventListener("mousedown", stop, true);
		}
	}};
	ucf.unloadlisteners.push(id);

	var boxLst = e => {
		console.log('@: '+ e.button);
		if (e.button == 1 && e.target.id == `pageAction-urlbar-_${rv}_`) { // Reader View Button
			e.stopImmediatePropagation(),	document.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн
			if (gBrowser.selectedBrowser.browsingContext.inRDMPane)
				BrowserReload();
		}
	}
	box.addEventListener("auxclick", boxLst, true);
	box.addEventListener("mouseenter", mouseenter, true);
	window.addEventListener("keydown", keydown_win);
	addDestructor(() => {
		box.removeEventListener("auxclick", boxLst, true);
		box.removeEventListener("mouseenter", mouseenter, true);
		window.removeEventListener("keydown", keydown_win);
	});

})("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	mainWin = {};
	focus.getFocusedElementForWindow(content, true, mainWin);
	mainWin = mainWin.value;

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0];
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	if (title) head.appendChild(title.cloneNode(true));

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

	head.copyStyle = function (s) {
		if (!s) return;
		var style = doc.createElement('style');
		style.type = 'text/css';
		if (s.media && s.media.mediaText) style.media = s.media.mediaText;
		try {
			for (var i = 0, rule; rule = s.cssRules[i]; i++) {
				if (rule.type != 3) {
					if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) {
						var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) {
							if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href);
							if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url);
							return prev + url + next;
						});
						style.appendChild(doc.createTextNode(css + '\n'));
					}
				} else {
					this.copyStyle(rule.styleSheet);
				}
			}
		} catch(e) {
			if (s.ownerNode) style = s.ownerNode.cloneNode(false);
		};
		this.appendChild(style);
	};
	for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]);
	head.appendChild(doc.createTextNode('\n'));
	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var selText = selWin ? win.getSelection().toString().slice(0, 200) : undefined;
	sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', selText]); // выделенный текст

}); // END hookClicks

ucf_BookmarkDir.js - только как пример: яркость прокруткой над ★

Выделить код

Код:

(async (id, sel) => { // Клики на Звёздочке, ToolTip: расположение закладки в Избранном, Недавняя папка
	var g = Cu.getGlobalForObject(Cu), stt = g[id]; // https://forum.mozilla-russia.org/viewtopic.php?pid=790890#p790890
	if (!stt) { var {obs, prefs} = Services, {bookmarks: bm, observers: pobs} = PlacesUtils;
		stt = g[id] = { bm, help_star: `

Правый клик:	⤾ Вернуть вкладку
…+ Alt 	Перевод выдел.текст | Сайт 
…+ Shift	Гугл Перевод или поиск\n
Колесико ±	Яркость страниц
…+ клик 	Полная яркость`,

			pref: `ucf.${id}Guid`,
			events: ["bookmark-added"],
			async init() {
				this.handleEvent = e => this[e.type](e);

				if ((this.pbm = typeof PlacesBookmarkMoved == "function"))
					this.events.push("bookmark-moved");
				else
					this.QueryInterface = g.ChromeUtils.generateQI([Ci.nsINavBookmarkObserver]),
					bm.addObserver(this);
				pobs.addListener(this.events, this.added = events => {
					for(var e of events) e.isTagging || this[e.constructor.name](e);
				});
				obs.addObserver(this, "quit-application-granted");
				this.args = [b => this.bguids.add(b.parentGuid), {concurrent: true}];
				var guid = prefs.getStringPref(this.pref, "");
				if (!guid) try {var [guid] = await PlacesUtils.metadata.get(
					PlacesUIUtils.LAST_USED_FOLDERS_META_KEY, []
				)} catch {}
				this.guids.push(guid || await PlacesUIUtils.defaultParentGuid || bm.unfiledGuid);

				var pref = "ucf.tabbrowser-tabpanels.opacity"; // яркость страницы
				var getPref = () => Services.prefs.getIntPref(pref, 100);
				var css = `@-moz-document url(chrome://browser/content/browser.xhtml) {
					:is(${sel})[rst] {filter: grayscale(1%) !important;}
					:root:not([chromehidden*=toolbar]) #tabbrowser-tabbox {background-color: black !important;}
					:root:not([chromehidden*=toolbar]) #tabbrowser-tabpanels {opacity:${getPref()/100} !important;}}`;
				var subst = "ucf-tabbrowser-tabpanels-opacity-style", url = `resource://${subst}/`;
				Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler)
					.setSubstitution(subst, Services.io.newURI("data:text/css," + encodeURIComponent(css)));
				var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
				sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET);

				var st = InspectorUtils.getAllStyleSheets(document).find(s => s.href == url).cssRules[0].cssRules[2].style;

				this.setPref = (e, val = 100) => {
					Services.prefs.setIntPref(pref, val);
					e.target.toggleAttribute("rst");
				}
				this.wheel = e => {
					var val = getPref() + (e.deltaY < 0 ? 5 : -5); // шаг
					val < 25 || val > 100 || this.setPref(e, val);
				}
				var observer = () => st.setProperty("opacity", getPref() / 100, "important");
				Services.prefs.addObserver(pref, observer);
				this.removePrefObs = () => Services.prefs.removeObserver(pref, observer);
			},
			observe() {
				this.pbm || bm.removeObserver(this);
				pobs.removeListener(this.events, this.added);
				obs.removeObserver(this, "quit-application-granted");
				prefs.setStringPref(this.pref, this.guids[0]);
				this.removePrefObs();
			},
			bguids: new g.Set(), guids: new g.Array(),
			skipTags: true,
			tt(win) {
				var list = win.InspectorUtils
					.getChildrenForNode(win.document.documentElement, true);
				return list.item(list.length - 1);
			},
			PlacesBookmarkAddition(e) {
				if (e.itemType == bm.TYPE_BOOKMARK && e.source == bm.SOURCES.DEFAULT)
					this.guids[0] = e.parentGuid;
			},
			PlacesBookmarkMoved(e) {
				e.parentGuid != e.oldParentGuid && this.PlacesBookmarkAddition(e);
			},
			onItemMoved(a, b, c, d, e, itemType, f, oldParentGuid, parentGuid, source) {
				this.PlacesBookmarkMoved({itemType, source, oldParentGuid, parentGuid});
			},
			fetch(win) {
				this.bguids.clear();
				return bm.fetch({url: win.gBrowser.currentURI.spec}, ...this.args);
			},
			addTab: function(win, url, add, params = {relatedToCurrent: true}) { // открыть адрес [add: в новой вкладке]
				params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
				return (add) ? win.gBrowser.addTab(url, params) : win.gBrowser.loadURI(url, params);
			},
			translate(browserMM, win, e, go) { // Google-перевод сайта | выделенного текста (go) поиск выдел. текста в Яндекс
				browserMM.addMessageListener('getSelect', function listener(msg) {
					var url = (msg.data) ? (go)
						? "https://yandex.ru/search/?text="+ msg.data +"&src=suggest_Pers&lang=ru" // поиск текста в Яндекс
						: "https://translate.google.com/#view=home&op=translate&sl=auto&tl=ru&text="+ msg.data // Гугл перевод
						: "http://translate.google.com/translate?u="+ gURLBar.value +"&hl=ru&ie=UTF-8&sl=auto&tl=ru"; // Перевод сайта
					if (go && !msg.data) // Перевод сайти в Яндекс. ничего не выделено + go не пуст
						gBrowser.selectedTab = e.addTab(win, "https://translate.yandex.com/translate?url=" + gURLBar.value + "&dir=&ui=ru&lang=auto-ru", 1)
					else
						gBrowser.selectedTab = e.addTab(win, url, 1);
				browserMM.removeMessageListener('getSelect', listener, true);
				});
				browserMM.loadFrameScript('data:,sendAsyncMessage("getSelect", content.document.getSelection().toString())', false);
			},
			auxclick(e) {
				if (e.button == 2) {
					var win = e.view;
					if (e.altKey)
						this.translate(gBrowser.selectedBrowser.messageManager, win, this, 1);
					else if (e.shiftKey)
						this.translate(gBrowser.selectedBrowser.messageManager, win, this);
					else
						win.undoCloseTab();
				} else
					this.setPref(e);
			},
			find: obj => obj.name == "tooltiptext"
		};
		var ps = ["onBeginUpdateBatch", "onEndUpdateBatch", "onItemChanged", "onItemVisited"];
		var noop = () => {}; for(var p of ps) stt[p] = noop; stt.init();

		var func = id => this[id].mouseenter = async function(e) {
			var win = e.view, star = e.target, result = [], starred = star.hasAttribute("starred");
			starred && await this.fetch(win);
			this.help_star = this.help_star.replace(/Яркость страниц.*/, `Яркость страниц ${win.Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity", 100)}%`);

			for(var guid of (starred ? this.bguids : this.guids)) {
				var arr = [], num = 50;
				while(--num) {
					if (!star.matches(":hover")) return;
					var res = await this.bm.fetch(guid);
					if (!res) break;
					if ((guid = res.parentGuid) == this.bm.rootGuid) {
						arr.unshift(this.bm.getLocalizedTitle(res));
						break;
					}
					arr.unshift(res.title || "[Безымянная папка]");
				}
				arr.length && result.push(arr.join("\\"));
			}
			if (!star.matches(":hover")) return;

			var text = (await win.document.l10n.formatMessages([{ // стандартная подсказка
				id: star.getAttribute("data-l10n-id"),
				args: JSON.parse(star.getAttribute("data-l10n-args"))
			}]))[0].attributes.find(this.find).value, txt;

			if (result.length) {
				txt = result.join("\n");
				txt = starred ? `\n\n★ ${result.length > 1 ? "Данные закладки добавлены" : "Данная закладка добавлена"} в:\n${txt}` : "\n\n★ Недавно добавленная папка:\n" + txt;
			}
			win.document.tooltipNode == star ? this.tt(win).label = text + this.help_star + txt : star.tooltipText = text + this.help_star + txt;
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})("${id}")`);
		g.ChromeUtils.compileScript(url).then(ps => ps.executeInGlobal(g));
	}
	await delayedStartupPromise;

	var types = ["auxclick", "mouseenter", "wheel"];
	var stars = Array.from(document.querySelectorAll(sel));

	for(var star of stars) for(var type of types) star.addEventListener(type, stt);
	star.setAttribute("context", "event.stopPropagation()");

	var destructor = () => {
		for(var star of stars) for(var type of types) star.removeEventListener(type, stt);
	}
	var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win;
	if (ucf)
		ucf[id] = {destructor}, ucf.unloadlisteners.push(id);
	else
		window.addEventListener("unload", destructor, {once: true});
})("ucfBookmarksStarFTooltipHelper", "#star-button, #context-bookmarkpage");

ucf_QuickToggle.js - здесь код LongPress

Dobrov пишет

доработать код, чтобы сразу перехватывать клики кнопок "nav-bar-customization-target" основной панели и "page-action-buttons"

Так PanelUI-menu-button же торчит в коде,
nav-bar-customization-target ей не родитель. Общим будет nav-bar.

identity-box

Раз предполагаются элементы с видимыми для мыши
дочерними элементами, придётся использовать перебор и closest().
Кстати, с FF90+ таковы pageAction's (в hbox иконки завернули).

добавить действие на долгое нажатие кнопки
добавить перехват "wheel"

И ты думаешь я смогу это всё нормально записать?
Весьма сомнительно. Попробую, так, отдельно, в консоль.

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

Выделить код

Код:

(() => {
	var c = msg => Services.console.logStringMessage("[HC] " + msg);
	var data = {
		"#downloads-button": {
			mousedownTarget: true,

			128() { // СКМ Click 
				c("Downloads Button Middle Click");
			},
			4() { // Double Left Click
				c("Downloads Button Double Left Click");
			},
			256() { // ПКМ Click
				c("Downloads Button Right Click");
			},
			260(btn) { // Double Right Click
				c("Downloads Button Double Right Click");
			},

			1() { // Left Long Press
				c("Downloads Button Left Long Press");
			},
		},
		"#PanelUI-menu-button": {
			mousedownTarget: true,

			8() { // ЛКМ + Alt
				c("PUI Button Alt + Left Click");
			},
			16(btn) { // Shift + ЛКМ
				c("PUI Button Shift + Left Click");
			},
			136(btn) { // Alt + СКМ
				c("PUI Button Alt + Middle Click");
			},
			128() { // СКМ
				c("PUI Button Middle Click");
			},
			132() { // СКМ Double
				c("PUI Button Double Middle Click");
			},
			256() { // ПКМ
				c("PUI Button Right Click");
			},
			264(btn) { // Alt + ПКМ
				c("PUI Button Alt + Right Click");
			},
			272() { // Shift + ПКМ
				c("PUI Button Shift + Right Click");
			},
			280(btn) { // Shift + Alt + ПКМ
				c("PUI Button Shift + Alt + Right Click");
			},
			4() { // Double Left Click
				c("PUI Button Double Left Click");
			},
			260() { // ПКМ Double Right Click
				c("PUI Button Double Right Click");
			},

			145() { // Shift + Middle Long Press
				c("PUI Button Shift + Middle Long Press");
			},
		},
		"#pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": { // Reader View Button
			128() { // Middle Click
				c("Reader View Middle Click");
			},

			289() { // Ctrl + Right Long Press
				c("Reader View Ctrl + Right Long Press");
			},
			2(trg, forward) { // wheel
				c("Reader View Wheel " + (forward ? "forward" : "backward"));
			},
		},
		"#star-button-box": {
			1() { // Left Long Press
				c("Star Left Long Press");
			},
			4() { // Double Left Click
				c("Star Double Left Click");
			},
			129() { // Middle Long Press
				c("Star Middle Long Press");
			},
		},

		"#identity-permission-box": {
			2(trg, forward) { // wheel
				c("Identity Permisson Box Wheel " + (forward ? "forward" : "backward"));
			}
		},
		"#identity-box": {
			2(trg, forward) { // wheel
				c("Identity Box Wheel " + (forward ? "forward" : "backward"));
			},
			34(trg, forward) { // Ctrl + wheel
				c("Identity Box Ctrl + Wheel " + (forward ? "forward" : "backward"));
			},
		},
		"#identity-icon-box": {
			16() { // Shift + Left Click
				c("Identity Icon Box Shift + Left Click");
			},
		},
	};

	var listener = {
		filter(sel) {
			return this.closest(sel);
		},
		find(sel) {
			return data[sel][this] || data[sel][this + 1];
		},
		handleEvent(e) {
			if (this.skip || e.detail > 2) return;

			var trg = e.target;
			var sels = this.selectors.filter(this.filter, trg);
			var {length} = sels;
			if (!length) return;

			var dbl = e.detail == 2;
			var wh = e.type.startsWith("w");

			var num = e.metaKey *64 + e.ctrlKey *32 + e.shiftKey *16 + e.altKey *8
				+ (wh ? 2 : e.button *128 + dbl *4);

			var obj = data[
				length > 1 && sels.find(this.find, num) || sels[0]
			];

			// wheel
			if (wh) return obj[num]?.(trg, e.deltaY < 0);

			// mousedown
			if (e.type.startsWith("m")) {
				obj.mousedownTarget && this.stop(e);
				if (dbl) return;

				this.longPress = false;
				if (++num in obj)
					this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num);
				if (e.button == 2)
					this.ctx = trg.getAttribute("context"),
					trg.setAttribute("context", "");
				return;
			}

			// click
			obj.mousedownTarget || this.stop(e);
			if (this.longPress) return this.longPress = false;
			dbl
				? this.clickTID &&= clearTimeout(this.clickTID)
				: this.mousedownTID &&= clearTimeout(this.mousedownTID);

			if (!obj[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "dispatch",
					this.mdt = obj.mousedownTarget;
				obj = this;
			}
			dbl
				? obj[num](trg)
				: this.clickTID = setTimeout(this.exec, 300, trg, obj, num);
		},
		get selectors() {
			this.exec = (trg, obj, num) => {
				this.clickTID = null;
				obj[num](trg);
			}
			this.onLongPress = (trg, obj, num) => {
				this.mousedownTID = null;
				this.longPress = true;
				obj[num](trg);
			}
			delete this.selectors;
			return this.selectors = Object.keys(data);
		},
		get mdEvent() {
			delete this.mdEvent;
			return this.mdEvent = new MouseEvent("mousedown", {bubbles: true});
		},
		context(trg) {
			this.ctx
				? trg.setAttribute("context", this.ctx)
				: trg.removeAttribute("context");
			trg.dispatchEvent(new MouseEvent("contextmenu", this.a));
		},
		dispatch(trg) {
			this.skip = true;
			this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click();
			this.skip = false;
		},
		stop: e => {
			e.preventDefault();
			e.stopImmediatePropagation();
		},
		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}
	};

	var root = document.getElementById("nav-bar");
	var events = ["click", "mousedown", "wheel"];
	for(var type of events) root.addEventListener(type, listener, true);
	var id = "test-hookClicks";
	ucf_custom_script_win.unloadlisteners.push(id);
	ucf_custom_script_win[id] = {destructor() {
		for(var type of events) root.removeEventListener(type, listener, true);
	}};
})();

Dumby спасибо за отличный код дополнительных кликов. :)
Подключил оба кода в custom_script_win.js, попробую вернуть функции Save HTML и прочие…


Только не понял, как в код обновления tooltips добавить removeEventListener и нужен ли он.

Выделить код

Код:

(() => { // update Tooltips
	………
	addDestructor(() => {
		………
	});
})();

Ещё не обновляются подсказки для 1) tracking-protection-icon-container "На этой странице не обнаружено ни одного известного Firefox трекера" и 2) identity-icon-box "Подтверждено: Let's Encrypt".
Я подключил на них яркость страниц, код работает. А как к этим кнопкам с динамической подсказкой добавить свой текст?
`Колесико ±    Яркость страниц\n…+ клик     Полная яркость
Яркость страниц ${win.Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity", 100)}%`
?

Dobrov пишет

как в код обновления tooltips добавить removeEventListener и нужен ли он

Да так же как и в коде для кликов.
А вот нужен ли он — это я и сам хотел бы знать.
Необходимость вызывает сомнение, но так принято (было),
поэтому, по возможности, оно так и продолжается.


Допустим, тултипский код рядом, сразу после var listener = {.....};
и добавление обработчиков и деструктора в самом конце.

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

Выделить код

Код:

.......
	var str_cut = s => s;

	var dsym = Symbol();
	var j = (...args) => args.join("\n");
	var tooltips = {
		get "PanelUI-menu-button"() {
			delete this["PanelUI-menu-button"];
			return j(
				`Клик мыши:	меню Firefox ${Services.appinfo.platformVersion}`,
				"…+ Shift		⚑ Краткая справка",
				"…+ Alt		Персонализация",
				"Клик дважды	⊠ закрыть браузер\n",

				"Правый клик	⇲ Свернуть",
				"…+ дважды	⤾ Вернуть вкладку",
				"…+ Alt		Диспетчер задач",
				"…+ Shift		Адаптивный дизайн\n",

				"Колёсико:	Развернуть | окно",
				"…+ Alt		Полный экран",
				"…+ дважды	Обновить без кэша"
			);
		},

		"pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": j(
			`Reader View	${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n`,

			"Клик мыши	Режим для чтения",
			"Колёсико	Адаптивный дизайн"
		),

		"_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action": j(
			"Video DownloadHelper",
			"Скачивание проигрываемого видео"
		),

		[dsym]: j(
			GetDynamicShortcutTooltipText("downloads-button"),

			"\nДвойной клик: ⬇︎ открыть [Загрузки]",
			"…на картинке: ⧉ найти Похожие\n",

			"Правый клик (Alt+S):  Сохранить",
			"   в единый html всё / выделенное",
			"…дважды  Картинки вкл/выкл\n",

			"Ролик:	 Сохранить как файл .txt",
			"Колёсико на рисунке: ➜ Сохранить"
		),
		get "downloads-button"() {
			var hint = this[dsym];
			if (document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action"))
				hint += "\nAlt⇧S	 ⌨ нажатие SingleSave";
			try {var dw = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);}
			catch {dw = Services.dirsvc.get("DfltDwnld", Ci.nsIFile);}
			if (dw) hint += "\n\n[Загрузки] — выбранная папка:\n" + str_cut(dw.path, 33);
			return hint;
		},

		get "identity-icon-box"() {
			var ttt = "";
			var trg = window.event.target;
			if (!trg.id.endsWith("x")) {
				if (trg.hasAttribute("tooltiptext"))
					ttt = trg.ttt = trg.tooltipText;
				else
					ttt = trg.ttt;
				if (ttt) ttt += "\n\n";
				trg.removeAttribute("tooltiptext");
			}
			return ttt + "Свой текст";
		},

		get "tracking-protection-icon-container"() {
			var trg = window.event?.target;
			return trg.id.endsWith("r") &&
				trg.textContent + "\n\nСвой текст";
		}
	};

	document.getElementById("tracking-protection-icon-container")
		.removeAttribute("tooltip");

	var onMouseenter = e => {
		var trg = e.target;
		var hint = tooltips[trg.id] || tooltips[(trg = trg.parentNode).id];
		if (hint) trg.tooltipText = hint;
	}
	var root = document.getElementById("nav-bar");
	var events = ["click", "mousedown", "wheel"];

	root.addEventListener("mouseenter", onMouseenter, true);
	for(var type of events) root.addEventListener(type, listener, true);

	var id = "hookClicks-and-tooltips";
	ucf_custom_script_win.unloadlisteners.push(id);
	ucf_custom_script_win[id] = {destructor() {
		root.removeEventListener("mouseenter", onMouseenter, true);
		for(var type of events) root.removeEventListener(type, listener, true);
	}};
})();

Dumby - спасибо! Скрипт hookClicks пригодится многим, он позволит прописывать обработку кликов в одном скрипте и позволит «разгрузить» другие кнопки, не добавлять в них код обработки кликов.
Сделал финальный демо-скрипт, расширяющий возможности нескольких кнопок. Dumby, проверь, может я где-то накосячил или что-то можно сделать проще! :)

hookClicks дополнительные клики и подсказки кнопок

Выделить код

Код:

(async (id, func) => { // для custom_script_win.js: дополнительные клики и подсказки кнопок © Dumby, mod Dobrov
	var dsym = Symbol(), j = (...args) => args.join("\n"),
	br_val = () => { return ` ${Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity",100)}%`;}, br_txt =

		`Клик ролика	сброс яркости\nКрутить ±	Яркость страниц`, tooltips = {
	get "PanelUI-menu-button"() {	/* delete this["PanelUI-menu-button"]; */ return j(
		`Клик мыши:	меню Firefox ${Services.appinfo.platformVersion}`,
		`… держать	⚑ Краткая справка`,
		`…+ Alt		Персонализация`,
		`Клик дважды	⊠ закрыть браузер\n`,
		`Правый клик	⇲ Свернуть`,
		`…+ дважды	⤾ Вернуть вкладку`,
		`…+ Alt		Диспетчер задач\n`,
		`Колёсико:	Развернуть | окно`,
		`…+ Alt		Полный экран`,
		`…+ дважды	Обновить без кэша`
	);},
	[dsym]: j(GetDynamicShortcutTooltipText("downloads-button"),
		`\nДвойной клик: ⬇︎ открыть [Загрузки]`,
		`…на картинке: ⧉ найти Похожие\n`,
		`Правый клик (Alt+S):  Сохранить`,
		`   в единый html всё / выделенное`,
		`…дважды  Картинки вкл/выкл\n`,
		`Ролик:	 Сохранить как файл .txt`,
		`Колёсико на рисунке: ➜ Сохранить`
	),
	get "titlebar-button titlebar-close"() { return j(
		`Закрыть Firefox ${AppConstants.MOZ_APP_VERSION.replace(/-.*/,'')}\n`,
		`◉ колёсико	вернуть вкладку`,
		`◧ держать	краткая Справка`,
		`◨ пр. клик	⇲ Свернуть`);
	},
	get "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_"() { return j(
		`Reader View	${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n`,
		`Клик мыши	Режим для чтения`, `Колёсико	Адаптивный дизайн\nКолесико ±	Яркость сайта` + br_val());
	},
	"_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action":
		`Сохранить страницу с помощью SingleFile (Alt+S)`
	,
	"_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action":
		`Video DownloadHelper\nСкачивание проигрываемого видео`
	,
	get "downloads-button"() {
		var hint = this[dsym];
		if (document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action"))
			hint += "\nAlt⇧S	 ⌨ нажатие SingleSave";  //убрать/добавить
		try {var dw = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);}
			catch {dw = Services.dirsvc.get("DfltDwnld", Ci.nsIFile);} //отличается от ⇧
		if (dw) hint += "\n\n[Загрузки] — выбранная папка:\n" + str_cut(dw.path, 33);
		return hint;
	},
	get "identity-icon-box"() {
		var trg = window.event.target, ttt = "";
		if (!trg.id.endsWith("x")) {
			if (trg.hasAttribute("tooltiptext"))
				ttt = trg.ttt = trg.tooltipText;
			else
				ttt = trg.ttt;
			if (ttt) ttt += "\n\n";
			trg.removeAttribute("tooltiptext");
		}
		return ttt +`Правый клик	Копировать адрес в буфер\n`+ br_txt + br_val();
	},
	get "tracking-protection-icon-container"() {
		var trg = window.event?.target;
		return trg.id.endsWith("r") && trg.textContent + "\n\n" + br_txt + br_val();
	}
	}; /* end tooltips */ document.getElementById("tracking-protection-icon-container").removeAttribute("tooltip");

	var listener = { // дополнительные клики кнопок и перехват существующих
		filter(sel) {
			return this.closest(sel);
		},
		find(sel) {
			return data[sel][this] || data[sel][this + 1];
		},
		handleEvent(e) {
			if (this.skip || e.detail > 2) return;

			var trg = e.target;
			var sels = this.selectors.filter(this.filter, trg);
			var {length} = sels;
			if (!length) return;

			var dbl = e.detail == 2;
			var wh = e.type.startsWith("w");

			var num = e.metaKey *64 + e.ctrlKey *32 + e.shiftKey *16 + e.altKey *8 + (wh ? 2 : e.button *128 + dbl *4);

			var obj = data[
				length > 1 && sels.find(this.find, num) || sels[0]
			];
// wheel
			if (wh) return obj[num]?.(trg, e.deltaY < 0);
// mousedown
			if (e.type.startsWith("m")) {
				obj.mousedownTarget && this.stop(e);
				if (dbl) return;

				this.longPress = false;
				if (++num in obj)
					this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num);
				if (e.button == 2)
					this.ctx = trg.getAttribute("context"),
					trg.setAttribute("context", "");
				return;
			}
// click
			obj.mousedownTarget || this.stop(e);
			if (this.longPress) return this.longPress = false;
			dbl
				? this.clickTID &&= clearTimeout(this.clickTID)
				: this.mousedownTID &&= clearTimeout(this.mousedownTID);

			if (!obj[num]) {
				if (e.button == 1) return;
				if (e.button) {
					num = "context";
					for(var p in this.a) this.a[p] = e[p];
				} else
					num = "dispatch",
					this.mdt = obj.mousedownTarget;
				obj = this;
			}
			dbl
				? obj[num](trg)
				: this.clickTID = setTimeout(this.exec, 300, trg, obj, num);
		},
		get selectors() {
			this.exec = (trg, obj, num) => {
				this.clickTID = null;
				obj[num](trg);
			}
			this.onLongPress = (trg, obj, num) => {
				this.mousedownTID = null;
				this.longPress = true;
				obj[num](trg);
			}
			delete this.selectors;
			return this.selectors = Object.keys(data);
		},
		get mdEvent() {
			delete this.mdEvent;
			return this.mdEvent = new MouseEvent("mousedown", {bubbles: true});
		},
		context(trg) {
			this.ctx
				? trg.setAttribute("context", this.ctx)
				: trg.removeAttribute("context");
			trg.dispatchEvent(new MouseEvent("contextmenu", this.a));
		},
		dispatch(trg) {
			this.skip = true;
			this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click();
			this.skip = false;
		},
		stop: e => {
			e.preventDefault();
			e.stopImmediatePropagation();
		},
		a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}
	};
	var onMouseenter = e => {
		var trg = e.target, id = trg.id || trg.className;
		console.log('id= «'+ id + '» '+ Math.random());
		var hint = tooltips[id] || tooltips[(trg = trg.parentNode).id];
		if (hint) trg.tooltipText = hint;
	}
	var keydown_win = e => { // нажатие клавиш
		if (e.keyCode == 83 && e.altKey) { // Alt+S [+Shift]
			var singlesave = document.getElementById('_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action');
			e.shiftKey ? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID
		}
		if (e.keyCode == 88 && e.altKey){ // Alt+X отладка внешнего JS-кода
			// e.target.ownerDocument.getElementById("key_browserConsole").doCommand();
			eval(Cu.readUTF8URI(Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/User.js")));
			console.log("[END] User.js " + Math.random());
		}
	}
	var root = document.getElementById("navigator-toolbox");
	var events = ["click", "mousedown", "wheel"];
	root.addEventListener("mouseenter", onMouseenter, true);
	for(var type of events) root.addEventListener(type, listener, true);
	window.addEventListener("keydown", keydown_win);
	ucf_custom_script_win.unloadlisteners.push(id);

	ucf_custom_script_win[id] = {destructor() {
		root.removeEventListener("mouseenter", onMouseenter, true);
		for(var type of events) root.removeEventListener(type, listener, true);
		window.removeEventListener("keydown", keydown_win);
	}};
	addDestructor = nextDestructor => {
		var {destructor} = ucf_custom_script_win[id];
		ucf_custom_script_win[id].destructor = () => {
			try {destructor();} catch(ex) {Cu.reportError(ex);}
			nextDestructor();
		}
	}; // end Hooks

	var {prefs, dirsvc} = Services, getIntPref = (p) => prefs.getIntPref(p, 100),
	c = msg => Services.console.logStringMessage("[HC] "+ msg), // отладка
	sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
	my_br = "ucf.tabbrowser-tabpanels.opacity", // яркость страниц
	css = `@-moz-document url(chrome://browser/content/browser.xhtml) {
		:is(${id})[rst] {filter: grayscale(1%) !important;}
		:root:not([chromehidden*=toolbar]) #tabbrowser-tabbox {background-color: black !important;}
		:root:not([chromehidden*=toolbar]) #tabbrowser-tabpanels {opacity:${getIntPref(my_br)/100} !important;}}`,
	subst = "ucf-tabbrowser-tabpanels-opacity-style", url = `resource://${subst}/`;
	Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler)
		.setSubstitution(subst, Services.io.newURI("data:text/css," + encodeURIComponent(css)));
	sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET);
	var st = InspectorUtils.getAllStyleSheets(document).find(s => s.href == url).cssRules[0].cssRules[2].style;
	var observer = () => st.setProperty("opacity", getIntPref(my_br)/100, "important");
	prefs.addObserver(my_br, observer);
	this.removePrefObs = () => prefs.removeObserver(my_br, observer); // end яркость

	if (typeof IOUtils != "object") { // Firefox 78 ESR
		var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
		var PathUtils = {join: (...args) => OS.Path.join(...args)};
		var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))};
	};
	prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки

	str_cut = (s, cut = 33) => { // сократить/разбить строку
		return s.substring(0,cut) + `${s.length > cut - 1 ? `…\n…${s.substring(s.length -cut + 2, s.length)}`: ''}`;
	},
	url_color = (color = "rgba(240,176,0,0.5)", ms = 300) => { // строка адреса мигает
		var u_alert = document.getElementById("urlbar-input-container");
		u_alert.style.background = color; setTimeout(() => u_alert.style.background = "", ms);
	},
	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);}
	},
	switchToTab = (url, but = window) => { // открыть вкладку | закрыть, если открыта
		for(var tab of but.ownerGlobal.gBrowser.tabs)
			if (tab.linkedBrowser.currentURI.spec == url) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // закрыть
		but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
	},
	showInStatusPanel = (info, time = 5000) => {
		StatusPanel = window.StatusPanel;
		if (StatusPanel.update.tid)
			clearTimeout(StatusPanel.update.tid)
		else {
			var {update} = StatusPanel;
			StatusPanel.update = () => {};
			StatusPanel.update.ret = () => {
				StatusPanel.update = update;
				StatusPanel.update();
			}
		}
		StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time);
		StatusPanel._label = info;
	},
	Title = (max, title) => { // получить заголовок. без обрезки: max не указан, домен: max <0, + дата: max=0
		if (!title) var title = document.title || gBrowser.selectedTab.label;
		if (max == undefined) return title; // заголовок как есть или ограничить длину, убрать служебные символы
		title = title.replace(/[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim();
		if ( max > 0 ) return title.slice(0, max);
		if ( max == 0) return title.slice(0, 100) +"_"+ new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։");
		var host = decodeURIComponent(gURLBar.value); // max < 0
		if (!/^file:\/\//.test(host)) host = host.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,'');
		return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns');
	},
	saveSelectionToTxt = async () => { // сохранить выделенный/весь текст страницы как .txt
		var msgName = id + ":Save:GetSelection", splice = saveURL.length == 10;
		var receiver = msg => {
			var args = ["data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data),
				Title(0) + '.txt', null, false, true, null, window.document];
			splice && args.splice(5, 0, null);
			saveURL(...args); showInStatusPanel("√ текст сохранён: "+ Title(0).slice(0, 60));
		}
		messageManager.addMessageListener(msgName, receiver);
		addDestructor(() => messageManager.removeMessageListener(msgName, receiver));
		var func = fm => {
			var res, fed, win = {}, 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("saveSelectionToTxt", res);
		}
		var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));';
		(saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	},
	save = async () => { // SingleHtml by Лекс, правка: Dumby, Dobrov
		var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML";
		var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8";
		var msgListener = async msg => {
			var [fileContent, fileName] = msg.data, dir; // fileName: выделенный текст или null
			try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
			var arr = prefs.getStringPref("ucf_save.dirs","_Web||_Images|0").split('|').slice(0,2); //subdir: title|host
			arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен
			arr.forEach(dir.append); // ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/label
			dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать, если не существует…
			var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			file.initWithPath(dir.path);
			if (!fileName) fileName = Title(100); // убрать служебные символы
			dir.append(Title(0, fileName) +'.html');
			await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60));
			var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download
			(await Downloads.getList(Downloads.ALL)).add(d);
			d.refresh(d.succeeded = true); // кнопка Загрузки мигает
		}
		messageManager.addMessageListener(msgName, msgListener);
		addDestructor(() => messageManager.removeMessageListener(msgName, msgListener));

		var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services';
		var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName));
		(save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))();
	},
	bright = (trg, forward, val) => { // wheel
		if (!val) var val = getIntPref(my_br) + (forward ? 5 : -5);
		val = val > 100 ? 100 : val < 20 ? 20 : val;
		prefs.setIntPref(my_br, val), trg.toggleAttribute("rst"), showInStatusPanel("☀ Яркость страниц: "+ val +"%");
	},
	help = (btn) => { // встроенная справка
		var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762'];
		var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:///
		(newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? switchToTab(help_ucf[0]) : switchToTab(help_ucf[1]);
	},
	GetSelection = (mM = gBrowser.selectedBrowser.messageManager) => {
		mM.addMessageListener('getSelect', function sel_listener(msg) {
			window.seltxt = msg.data;
			mM.removeMessageListener('getSelect', sel_listener, true);
		});
		mM.loadFrameScript('data:,sendAsyncMessage("getSelect",content.document.getSelection().toString())',false);
	},
	data = {
		"#downloads-button": { mousedownTarget: true,

			4() { // Double Left Click - Обзор папки «Загрузки»
				c("DW Double Left Click");
				Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError);
			},
			128() { saveSelectionToTxt();}, // СКМ Click (сохранить .txt)
			256() { save();}, // ПКМ Click (Single HTML)
			260(btn) { // Double ПКМ Click
				var pref = "permissions.default.image";
				var one = prefs.getIntPref(pref) == 1;
				prefs.setIntPref(pref, one ? 2 : 1);
				btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : "";
				BrowserReload();
			},
		},
		"#PanelUI-menu-button": { mousedownTarget: true,

			1(btn) { help(btn);}, // Long Press
			8() { gCustomizeMode.enter();}, //ЛКМ + Alt Персонализация
			16(btn) { help(btn);}, // Shift + ЛКМ
			4() { goQuitApplication();}, // Double Left Click
			128() { windowState != STATE_MAXIMIZED ? maximize() : restore();}, // СКМ
			136(btn) { BrowserFullScreen();}, // Alt + СКМ
			132() { BrowserReloadSkipCache();}, // СКМ Double
			256() { minimize();}, // ПКМ
			260(btn) { btn.ownerGlobal.undoCloseTab();}, // ПКМ Double Right Click
			264(btn) { switchToTab('about:performance');}, // Alt + ПКМ
			280(btn) { // Shift + Alt + ПКМ
				var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging");
				(this[280] = target => obj.oncommand({target}))(btn); // запуск пункта меню, у которого нет HotKey
			},
		},
		"#pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": { // Reader View Button
			128(btn) { // СКМ
				btn.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey
				if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload();
			},
			2(trg, forward) { bright(trg, forward);}, // яркость по wheel ±
			264(btn) { // Alt + ПКМ
				translate(gBrowser.selectedBrowser.messageManager, 1);
			},
			1(btn) { // Shift + ПКМ
				translate(gBrowser.selectedBrowser.messageManager);
			},
		},
		"#star-button-box": {
			1() { // Left Long Press
				c("Star Left Long Press");
			},
			2(trg, forward) { bright(trg, forward);}, // яркость по wheel ±
			// 128(btn) { // СКМ
			// 	switchToTab('about:config');
			// },
			256() { // ПКМ
				window.undoCloseTab();
			},
		},
		"#identity-box": { // Замок
			2(trg, forward) { bright(trg, forward);}, // яркость по wheel ±
			128(trg, forward) { bright(trg, forward, 100);}, // СКМ
			256(btn) { // ПКМ
				gClipboard.write(gURLBar.value);
				url_color(), showInStatusPanel("в буфере: "+ gURLBar.value.slice(0, 80));
			},
		},
		"#tracking-protection-icon-container": { // Защита
			2(trg, forward) { bright(trg, forward);}, // яркость по wheel ±
			128(trg, forward) { bright(trg, forward, 100);}, // СКМ
		},
		"#identity-permission-box": {
			2(trg, forward) { // wheel
				c("Identity Permisson Box Wheel " + (forward ? "forward" : "backward"));
			}
		},
		"#identity-icon-box": {
			16() { // Shift + Left Click
				c("Identity Icon Box Shift + Left Click");
			},
		},
	}; // end Clicks, HotKeys ==================================================


})("hookClicks-and-tooltips", ({io, focus}) => { // SingleHTML не сохраняет svg графику

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	mainWin = {};
	focus.getFocusedElementForWindow(content, true, mainWin);
	mainWin = mainWin.value;

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0];
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	if (title) head.appendChild(title.cloneNode(true));

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

	head.copyStyle = function (s) {
		if (!s) return;
		var style = doc.createElement('style');
		style.type = 'text/css';
		if (s.media && s.media.mediaText) style.media = s.media.mediaText;
		try {
			for (var i = 0, rule; rule = s.cssRules[i]; i++) {
				if (rule.type != 3) {
					if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) {
						var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) {
							if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href);
							if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url);
							return prev + url + next;
						});
						style.appendChild(doc.createTextNode(css + '\n'));
					}
				} else {
					this.copyStyle(rule.styleSheet);
				}
			}
		} catch(e) {
			if (s.ownerNode) style = s.ownerNode.cloneNode(false);
		};
		this.appendChild(style);
	};
	for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]);
	head.appendChild(doc.createTextNode('\n'));
	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', selWin ? win.getSelection().toString().slice(0, 200) : undefined]); // выделенный текст
}); // END hookClicks

Dumby посмотрите пожалуйста кнопку toggleRestartlessAddons в ней при ПКМ не появляется сообщение в правом нижнем углу а в консоле появляеться ошибка
Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data
    forgetClosedTab chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:794
    removeTab chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:805
    waitTimer chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:809

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

Выделить код

Код:

// 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: 0,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 1,
	// 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:    0
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

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

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

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

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

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

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

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

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

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

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
function checkForAddonsUpdates() {
//== 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;

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

var ADDONS_URL = "about:addons";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		tab.collapsed = false;

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

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

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

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

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

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
		if(!isContextOpened())
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& 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;
				return true;
			}
			return false;
		}
	);
}
function isContextOpened() {
	return inBtn(document.popupNode);
}
function inBtn(node) {
	for(; node; node = node.parentNode)
		if(node == self)
			return true;
	return false;
}

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

Выделить код

Код:

try {CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js"
		));
	},
	onCreated(btn) {
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC");
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
});} catch(ex) {Cu.reportError(ex);}

[firefox] 95.0

egorsemenov06 пишет

JSON.parse

/*JSON.parse*/

Dumby пишет
egorsemenov06 пишет

JSON.parse

/*JSON.parse*/

Огромное Спасибо!!!!!

воможно ли с помощью скрипта https://forum.mozilla-russia.org/viewto … 54#p782454 открывать ссылки и страницы в tor browser? после последнего обновления тор открывается через скрипт, но соединения нет

Dumby
Хочу на базе этого скрипта сделать ещё один, но чтоб он закрывал текущую вкладку, заменить им хочу расширение.
Подскажите пожалуйста, может где подправить можно по мелочи?
Сейчас мой код закрытия "других вкладок" стал немного другим, иконку ещё более подходящую нашёл

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

Выделить код

Код:

CustomizableUI.createWidget({
	id: "Close-Tabs-button",
	label: "Закрыть другие вкладки",
	tooltiptext: "Закрыть другие вкладки",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn.render = this.render;
		btn._handleClick = this.close;
		btn.setAttribute("image", "chrome://devtools/skin/images/close.svg");
	},
	render() {
		delete this.render;
		this.render();
		this.icon.style.setProperty("padding", "3px", "important");
	},
	close() {
		var gb = this.ownerGlobal.gBrowser;
		gb.removeAllTabsBut(gb.selectedTab);
	}
});

По-моему, строку gb.removeAllTabsBut(gb.selectedTab); нужно изменить, но вот как...

sandro79 пишет

По-моему, строку gb.removeAllTabsBut(gb.selectedTab); нужно изменить, но вот как...

Верно, эту строку. Как? Ну, обычно,
следует просто посмотреть как это делает сам браузер, и срисовать себе.
Если как пункт контекстного меню вкладок «Закрыть (N) вклад(ку|ки|ок)»,
то получится что-то типа

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

Выделить код

Код:

...
		//gb.removeAllTabsBut(gb.selectedTab);
		var tab = gb.selectedTab;
		tab.multiselected
			? gb.removeMultiSelectedTabs()
			: gb.removeTab(tab, {animate: true});


Но там учитывается вариант несколько-выделенных вкладок.
Если нужно это игнорировать, то есть понимать вопрос
строго буквально «чтоб он закрывал текущую вкладку» и никак иначе,
то, в простейшем случае, можно заменить на
gb.removeTab(gb.selectedTab, {animate: true});

Dumby пишет

следует просто посмотреть как это делает сам браузер, и срисовать себе

С этим я пока не разобрался, хотя не помешало бы такую мелочь самому вычислить.

строго буквально «чтоб он закрывал текущую вкладку» и никак иначе

Да, как раз так и хотел, пункт "Выбрать все вкладки" не использую, и он у меня скрыт стилем. Скрипт собрал, всё работает

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

Выделить код

Код:

CustomizableUI.createWidget({
	id: "Close-Tab",
	label: "Закрыть вкладку",
	tooltiptext: "Закрыть вкладку",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn.render = this.render;
		btn._handleClick = this.close;
		btn.setAttribute("image", "chrome://devtools/skin/images/close.svg");
	},
	render() {
		delete this.render;
		this.render();
		this.icon.style.setProperty("padding", "3px", "important");
	},
	close() {
		var gb = this.ownerGlobal.gBrowser;
		gb.removeTab(gb.selectedTab, {animate: true});
	}
});

Благодарю за помощь! :beer:

Dumby
Кнопку  "Закрыть другие вкладки" №82 куда лучше подключить, а то она у меня на [firefox] 95 никак не хочет работать?

У меня кнопка "Закрыть вкладки..." такая, может кому пригодится. Кто-то помогал сделать, закреплённые вкладки, когда удаление слева или другие не удаляет. Я бы сам так не осилил. Работает, ucf у меня старый.

custom_script.js

Выделить код

Код:

// Этот скрипт можно использовать для создания кнопок с помощью CustomizableUI.createWidget
(() => {
    var loadscript = name => {
        try {
            Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`, globalThis, "UTF-8");
        } catch(e) {}
    };
    loadscript("my_buttons.js");
})();


my_buttons.js

Выделить код

Код:

var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
try {
    CustomizableUI.createWidget({
        id: "add-select-close-tabs-app",
        type: "custom",
        tooltiptext: [
            "ЛКМ: Закрыть все вкладки",
            "Shift+ЛКМ: Закрыть другие вкладки",
            "Ctrl+ЛКМ: Закрыть слева",
            "Alt+ЛКМ: Закрыть справа",
        ].join("\n"),
        onBuild: function(document) {
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");
            toolbarbutton_0.id = this.id;
            toolbarbutton_0.tooltipText = this.tooltiptext;
            toolbarbutton_0.label = "Закрыть все вкладки";
            toolbarbutton_0.setAttribute("context", false);
            toolbarbutton_0.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPYxg8wD5wy1y7gK1/7AO3/ncI2nq7uOnk3cq20//BuOP07b1Hnt09eurl/6OnXvwB0rOh2hAAphmGU0uOQDRD8ZrN94CaQQZADIFqQwBkzSAcnLTnVkLewWMgnFZy6Ep9z9kr3dMuHoNhqDYEQDeAEIZqQ4Dd937/JwVDtSEANkX4MFQbAmBThA9DtSEANkXIeM/t7/8vz1v9/1590//rE2f8/+pkIQ3VCgHYNMEwSPOLtOT/X1ws/n9xNvv/xRVIO5l9+OxsoQvVjt+AS/PXgDX/vXnt/4/q4v9/b1z9/y0tGmiY6S6odvwGgJwNshmk+f+fP/9/790JcQnQFVDt+A0A+RnsAqDNIM3/Prz//y0lEmTAZah2/OCrvZkM0LnvvqXFgG3+lgzUDAoHZ9N4qBLC4LOriQ7Qxt0gZwMNuQLRzMAAAAEG9BCOlZEaAAAAAElFTkSuQmCC");
            toolbarbutton_0.addEventListener("click", function(event) {
                var win = event.target.ownerDocument.defaultView;
                if (event.button == 0) {
                    if (event.shiftKey) {
                        win.gBrowser.removeAllTabsBut(win.gBrowser.selectedTab);
                    } else if (event.ctrlKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice(0, (index != -1) ? index : tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    } else if (event.altKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice((index != -1) ? (index + 1) : 0, tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    }
                else {
                    win.gBrowser.selectAllTabs();
                    win.gBrowser.removeMultiSelectedTabs();
                    }
                }
            }, false);
            toolbarbutton_0.classList.add("toolbarbutton-1");
            toolbarbutton_0.classList.add("chromeclass-toolbar-additional");
            return toolbarbutton_0;
        }
    });
} catch(e) {}


И ещё мне 2 кнопки сделал Vitaliy V. здесь.

voqabuhe пишет

куда лучше подключить

Что значит лучше?
Либо добавить в custom_script.js или подобный, либо отдельным файлом.
Придумываешь название, создаёшь, и прописываешь в CustomStylesScripts.jsm
(это если использовать встроенный загрузчик)

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

Выделить код

Код:

var UcfStylesScripts = {
    /** ************************▼ Настройки ▼************************ */

    .......

    scriptsbackground: [ // В фоне [System Principal]
        .......

        { path: "closeOtherTabs.js" },
    ],
    /** ************************▲ Настройки ▲************************ */
};

никак не хочет работать

Что значит не хочет работать?
Открывает вкладки вместо того, чтобы закрывать?


id'шник проверь, чтоб уникальный был.
Иконки, кстати, такой как там у тебя может не быть, пропиши свою.
Кэш ещё может залипнуть, закрой браузер и удали папку startupCache руками.

xrun1 пишет

my_buttons.js

Выделить код

Код:

var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
try {
    CustomizableUI.createWidget({
        id: "add-select-close-tabs-app",
        type: "custom",
        tooltiptext: [
            "ЛКМ: Закрыть все вкладки",
            "Shift+ЛКМ: Закрыть другие вкладки",
            "Ctrl+ЛКМ: Закрыть слева",
            "Alt+ЛКМ: Закрыть справа",
        ].join("\n"),
        onBuild: function(document) {
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");
            toolbarbutton_0.id = this.id;
            toolbarbutton_0.tooltipText = this.tooltiptext;
            toolbarbutton_0.label = "Закрыть все вкладки";
            toolbarbutton_0.setAttribute("context", false);
            toolbarbutton_0.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPYxg8wD5wy1y7gK1/7AO3/ncI2nq7uOnk3cq20//BuOP07b1Hnt09eurl/6OnXvwB0rOh2hAAphmGU0uOQDRD8ZrN94CaQQZADIFqQwBkzSAcnLTnVkLewWMgnFZy6Ep9z9kr3dMuHoNhqDYEQDeAEIZqQ4Dd937/JwVDtSEANkX4MFQbAmBThA9DtSEANkXIeM/t7/8vz1v9/1590//rE2f8/+pkIQ3VCgHYNMEwSPOLtOT/X1ws/n9xNvv/xRVIO5l9+OxsoQvVjt+AS/PXgDX/vXnt/4/q4v9/b1z9/y0tGmiY6S6odvwGgJwNshmk+f+fP/9/790JcQnQFVDt+A0A+RnsAqDNIM3/Prz//y0lEmTAZah2/OCrvZkM0LnvvqXFgG3+lgzUDAoHZ9N4qBLC4LOriQ7Qxt0gZwMNuQLRzMAAAAEG9BCOlZEaAAAAAElFTkSuQmCC");
            toolbarbutton_0.addEventListener("click", function(event) {
                var win = event.target.ownerDocument.defaultView;
                if (event.button == 0) {
                    if (event.shiftKey) {
                        win.gBrowser.removeAllTabsBut(win.gBrowser.selectedTab);
                    } else if (event.ctrlKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice(0, (index != -1) ? index : tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    } else if (event.altKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice((index != -1) ? (index + 1) : 0, tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    }
                else {
                    win.gBrowser.selectAllTabs();
                    win.gBrowser.removeMultiSelectedTabs();
                    }
                }
            }, false);
            toolbarbutton_0.classList.add("toolbarbutton-1");
            toolbarbutton_0.classList.add("chromeclass-toolbar-additional");
            return toolbarbutton_0;
        }
    });
} catch(e) {}

Интересная кнопочка.
Только немешало бы в неё добавить "защиту от дурака" на все действия - подтверждение на закрытие вкладок.


Что-то типа такого:

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

untitled-4.png
untitled-2.png
untitled-3.png

xrun1
Спасибо за кнопку"показа/скрытия панели закладок и доп.панели".

Dumby
Посмотрите пожалуйста, я правильно отредактировал?

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

Выделить код

Код:

try {
    CustomizableUI.createWidget({
        id: "additional-toolbars-button",
        type: "custom",
        label: "Доп. панели",
        tooltiptext: [
            "ЛКМ: Переключить верт. панель",
            "ПКМ: Переключить доп. панель"
        ].join("\n"),
        localized: false,
        onBuild(doc) {
            var trbn = doc.createXULElement("toolbarbutton");
            trbn.id = this.id;
            trbn.tooltipText = this.tooltiptext;
            trbn.label = this.label;
            trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
            trbn.setAttribute("context", false);
            trbn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/layer-visible-off.svg");
            trbn.addEventListener("click", function(e) {
                var pref = "browser.add.toolbars.visibility";
                if (e.button == 0) {
                    e.preventDefault();
                    e.stopPropagation();
                    CustomizableUI.setToolbarVisibility("ucf-additional-vertical-bar", doc.querySelector("#ucf-additional-vertical-bar").collapsed);
                } else if (e.button == 2) {
                    e.preventDefault();
                    e.stopPropagation();
                    CustomizableUI.setToolbarVisibility("ucf-additional-top-bar", doc.querySelector("#ucf-additional-top-bar").collapsed);
                }
            }, false);
            return trbn;
        },
    });
} catch(e) {}

unter_officer
Для меня такое сделать нереально. :) Не представляю, как в кнопку прикрутить confirm()... Да и не нужно. Кнопка "Восстановить" из расширения ATB Vitaliy V. восстанавливает все закрытые вкладки моей кнопкой. Если сразу озаботиться, конечно.
kokoss
Я ещё для боковой панели менял кнопку "Закладки" для экономии места, вдруг пригодится.

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

Заменить в user_chrome.js кнопку с id: "add-view-bookmarks-sidebar-button". Ещё раз повторюсь, ucf у меня старый.

Выделить код

Код:

try {
                CustomizableUI.createWidget({
                    id: "add-view-bookmarks-sidebar-button",
                    type: "custom",
                    label: "Закладки Библиотека История",
                    tooltiptext: "ЛКМ: Показать / Скрыть Закладки\nСКМ: Открыть Библиотеки в табе\nПКМ: Показать / Скрыть Историю",
                    localized: false,
                    onBuild: function(doc) {
                        var win = doc.defaultView;
                        var trbn_0 = doc.createElementNS(ns_xul, "toolbarbutton");
                        trbn_0.id = "add-view-bookmarks-sidebar-button";
                        trbn_0.className = "toolbarbutton-1 chromeclass-toolbar-additional";
                        trbn_0.setAttribute("label", "Закладки Библиотека История");
                        trbn_0.setAttribute("context", "false");
                        trbn_0.setAttribute("tooltiptext", "ЛКМ: Показать / Скрыть Закладки\nСКМ: Открыть Библиотеки в табе\nПКМ: Показать / Скрыть Историю");
                        trbn_0.addEventListener("click", function(e) {
                            if (e.button == 0) {
                                if ("SidebarUI" in win)
                                    win.SidebarUI.toggle("viewBookmarksSidebar");
                                else if ("toggleSidebar" in win)
                                    win.toggleSidebar("viewBookmarksSidebar");
                            }
                            else if (e.button == 1) {
                                var url="chrome://browser/content/places/places.xhtml";
                                win.SidebarUI.hide();
                                /* Для CB, открывает "История" в окне "Библиотека"
                                PlacesCommandHook.showPlacesOrganizer('History'); */
                                win.gBrowser.selectedTab = win.gBrowser.addTrustedTab(url);
                            }
                            else if (e.button == 2) {
                                if ("SidebarUI" in win)
                                    win.SidebarUI.toggle("viewHistorySidebar");
                                else if ("toggleSidebar" in win)
                                    win.toggleSidebar("viewHistorySidebar");
                            }
                        });
                        return trbn_0;
                    }
                });
            } catch(e) {}

_zt пишет

Посмотрите пожалуйста, я правильно отредактировал?

Не люблю я вопросы про «правильно», откуда мне знать что есть правильно.
Если работает, и страшных косяков нет (а их нет) — значит правильно.

скрытый текст
И, потом, не всегда понятен замысел, вот смотришь и думаешь,
что здесь имелось в виду? Если судить по себе, то далеко не всё пишется
с каким-то смыслом, иногда просто от балды, первое что в голову придёт.


Вот, например, в коде определяется переменная pref, но нигде не используется.
Или зачем e.preventDefault(); e.stopPropagation(); я же не знаю.
Превент может использоваться для предотвращения появления контекстного
меню при ПКМ (Windows), но там уже решено, что этого контекстного меню
не будет совсем никогда, поскольку установлен атрибут "context".


Или tooltiptext массивом. Когда там целая батарея подсказочных строк, то имеет смысл.
А парочку вполне можно и одной строкой записать. Это не вопрос правильности,
а вопрос предпочтения. Вот там widget создаётся как type: "custom", не знаю,
может это как-то внутренне оптимальнее, но чего не сделаешь, чтоб записать попроще, типа

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	label: "Доп. панели",
	tooltiptext: "ЛКМ: Переключить верт. панель\nПКМ: Переключить доп. панель",

	id: "additional-toolbars-button",
	localized: false,
	onCreated(btn) {
		btn.toggleAttribute("context");
		btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/layer-visible-off.svg");
	},
	0: "ucf-additional-vertical-bar",
	2: "ucf-additional-top-bar",
	onClick(e) {
		var id = this[e.button];
		id && CustomizableUI.setToolbarVisibility(id, e.view.document.getElementById(id).collapsed);
	}
}))();

Dumby
По мне судить не надо, судите по Vitaliy V., это его код.
Я в скриптах разбираюсь на уровне  - что нибудь добавить/удалить по доступному примеру. (Сколько раз это повторить надо?) Поэтому и спросил. Спасибо за готовый код, это то что в итоге мне нужно было.

xrun1 пишет

my_buttons.js

Выделить код

Код:

var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
try {
    CustomizableUI.createWidget({
        id: "add-select-close-tabs-app",
        type: "custom",
        tooltiptext: [
            "ЛКМ: Закрыть все вкладки",
            "Shift+ЛКМ: Закрыть другие вкладки",
            "Ctrl+ЛКМ: Закрыть слева",
            "Alt+ЛКМ: Закрыть справа",
        ].join("\n"),
        onBuild: function(document) {
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");
            toolbarbutton_0.id = this.id;
            toolbarbutton_0.tooltipText = this.tooltiptext;
            toolbarbutton_0.label = "Закрыть все вкладки";
            toolbarbutton_0.setAttribute("context", false);
            toolbarbutton_0.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPYxg8wD5wy1y7gK1/7AO3/ncI2nq7uOnk3cq20//BuOP07b1Hnt09eurl/6OnXvwB0rOh2hAAphmGU0uOQDRD8ZrN94CaQQZADIFqQwBkzSAcnLTnVkLewWMgnFZy6Ep9z9kr3dMuHoNhqDYEQDeAEIZqQ4Dd937/JwVDtSEANkX4MFQbAmBThA9DtSEANkXIeM/t7/8vz1v9/1590//rE2f8/+pkIQ3VCgHYNMEwSPOLtOT/X1ws/n9xNvv/xRVIO5l9+OxsoQvVjt+AS/PXgDX/vXnt/4/q4v9/b1z9/y0tGmiY6S6odvwGgJwNshmk+f+fP/9/790JcQnQFVDt+A0A+RnsAqDNIM3/Prz//y0lEmTAZah2/OCrvZkM0LnvvqXFgG3+lgzUDAoHZ9N4qBLC4LOriQ7Qxt0gZwMNuQLRzMAAAAEG9BCOlZEaAAAAAElFTkSuQmCC");
            toolbarbutton_0.addEventListener("click", function(event) {
                var win = event.target.ownerDocument.defaultView;
                if (event.button == 0) {
                    if (event.shiftKey) {
                        win.gBrowser.removeAllTabsBut(win.gBrowser.selectedTab);
                    } else if (event.ctrlKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice(0, (index != -1) ? index : tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    } else if (event.altKey) {
                        var ctab = win.gBrowser.selectedTab, tabs;
                        if (ctab.multiselected)
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned);
                        else
                            tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned);
                        var index = tabs.indexOf(ctab);
                        tabs = tabs.slice((index != -1) ? (index + 1) : 0, tabs.length);
                        tabs.forEach((tab) => {
                            win.gBrowser.removeTab(tab);
                        });
                    }
                else {
                    win.gBrowser.selectAllTabs();
                    win.gBrowser.removeMultiSelectedTabs();
                    }
                }
            }, false);
            toolbarbutton_0.classList.add("toolbarbutton-1");
            toolbarbutton_0.classList.add("chromeclass-toolbar-additional");
            return toolbarbutton_0;
        }
    });
} catch(e) {}

Dumby
Возможно ли в эту кнопку, на все действия, прикрутить горячие клавиши для клавиатуры, например Shift+1, Shift+2, Shift+3, Shift+4 ?

Dumby пишет

Что значит лучше?
Либо добавить в custom_script.js или подобный, либо отдельным файлом.
Придумываешь название, создаёшь, и прописываешь в CustomStylesScripts.jsm
(это если использовать встроенный загрузчик)

Типа про это как раз и спрашивал, в какой из них лучше, в чём разница?

Что значит не хочет работать?
Открывает вкладки вместо того, чтобы закрывать?

В смысле, что она ваще не появлялась.

id'шник проверь, чтоб уникальный был.

Ага, это и было причиной, видно с чем-то совпало, после изменения id, кнопка появилась и работает. Спасибо.

xrun1 пишет

Я ещё для боковой панели менял кнопку "Закладки" для экономии места, вдруг пригодится.
скрытый текст

И как это работает? А то у меня в [firefox] 95 не работает!

unter_officer пишет

Возможно ли в эту кнопку, на все действия, прикрутить горячие клавиши для клавиатуры, например Shift+1, Shift+2, Shift+3, Shift+4 ?

Обязательно в кнопку?
Это же надо вмешиваться внутрь или цепляться снаружи.
Может сойдёт какой-нибудь аналог отдельным независимым кодом, например
(это для custom_script_win.js, и, я надеюсь, вопрос был не о Firefox 52)

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

Выделить код

Код:

(async anim => {
	var re = /^(?:Digit|Numpad)(1|2|3|4)$/;
	var funcs = {
		1: () => {
			gBrowser.selectAllTabs();
			gBrowser.removeMultiSelectedTabs();
		},
		2: t => gBrowser.removeAllTabsBut(t),
		3: t => gBrowser.removeTabsToTheStartFrom(t, anim),
		4: t => gBrowser.removeTabsToTheEndFrom(t, anim),
	};
	var args = ["keydown", e => {
		if (
			e.ctrlKey || e.altKey || !re.test(e.code) ||
			e.repeat || docShell.isCommandEnabled("cmd_insertText")
		)
			return;

		var num = RegExp.$1;
		if (e.shiftKey || e.code.startsWith("N") && e.getModifierState("NumLock") && e.key != num)
			e.preventDefault(),
			funcs[num](gBrowser.selectedTab);

	}, true];
	addEventListener(...args);
	var id = Symbol(), ucf = ucf_custom_script_win;
	ucf.unloadlisteners.push(id);
	ucf[id] = {destructor: () => removeEventListener(...args)};
})({animate: true});

kokoss пишет

у меня в [firefox] 95 не работает!

Там ns_xul не определён, вот и не работает.
И иконки нет, но это уже мелочь.

Dumby пишет

Обязательно в кнопку?
Это же надо вмешиваться внутрь или цепляться снаружи.
Может сойдёт какой-нибудь аналог отдельным независимым кодом, например
(это для custom_script_win.js, и, я надеюсь, вопрос был не о Firefox 52)

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

Выделить код

Код:

(async anim => {
	var re = /^(?:Digit|Numpad)(1|2|3|4)$/;
	var funcs = {
		1: () => {
			gBrowser.selectAllTabs();
			gBrowser.removeMultiSelectedTabs();
		},
		2: t => gBrowser.removeAllTabsBut(t),
		3: t => gBrowser.removeTabsToTheStartFrom(t, anim),
		4: t => gBrowser.removeTabsToTheEndFrom(t, anim),
	};
	var args = ["keydown", e => {
		if (
			e.ctrlKey || e.altKey || !re.test(e.code) ||
			e.repeat || docShell.isCommandEnabled("cmd_insertText")
		)
			return;

		var num = RegExp.$1;
		if (e.shiftKey || e.code.startsWith("N") && e.getModifierState("NumLock") && e.key != num)
			e.preventDefault(),
			funcs[num](gBrowser.selectedTab);

	}, true];
	addEventListener(...args);
	var id = Symbol(), ucf = ucf_custom_script_win;
	ucf.unloadlisteners.push(id);
	ucf[id] = {destructor: () => removeEventListener(...args)};
})({animate: true});

Dumby, большое спасибо!


P.S. Вопрос конечно же был не о Firefox 52. :)

Dumby пишет

Там ns_xul не определён, вот и не работает.
И иконки нет, но это уже мелочь.

Иконку то я прикрутил, только не мог понять почему у него работает, а у меня нет, теперь понятно. Спасибо за подсказку!

kokoss
Это была не отдельная кнопка, я правил файл user_chrome.js. А для него иконки уже прописаны в vertical_top_bottom_bar\vertical_top_bottom_bar.css. Из-за этого, наверное, получилась неразбериха.

_zt пишет

По мне судить не надо, судите по Vitaliy V., это его код.

Не, это мой код, он там двумя постами выше. Кнопку делал, когда только-только Vitaliy V. сделал ucf. Делал по аналогии c кнопками, которые уже были в ucf. Поэтому там и type: "custom". А e.preventDefault(); e.stopPropagation(); у меня затесались по старой памяти от CB.:)

Может кому пригодится:

кнопка для custom_script.js

Выделить код

Код:

try {
    CustomizableUI.createWidget({
        id: "add-personalization-button-app",
        type: "custom",
        tooltiptext: [
            "ЛКМ: Персонализация",
            "СКМ: about:about",
            "ПКМ: about:support"
        ].join("\n"),
        onBuild: function(document) {
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");

            toolbarbutton_0.id = this.id;
            toolbarbutton_0.tooltipText = this.tooltiptext;
            toolbarbutton_0.label = "Персонализация about:about about:support";
            toolbarbutton_0.image = "chrome://browser/content/robot.ico";

            toolbarbutton_0.setAttribute("context", false);
            toolbarbutton_0.addEventListener("click", function(event) {
                var win = event.target.ownerDocument.defaultView;
                win.SidebarUI.hide();
                if (event.button == 0) {
                    win.gCustomizeMode.enter();
                }
                if (event.button == 1) {
                    win.gBrowser.selectedTab = win.gBrowser.addTrustedTab('about:about');
                }
                if (event.button == 2) {
		    win.gBrowser.selectedTab = win.gBrowser.addTrustedTab('about:support');
                }
            }, false);
            toolbarbutton_0.classList.add("toolbarbutton-1");
            toolbarbutton_0.classList.add("chromeclass-toolbar-additional");
            return toolbarbutton_0;
        }
    });
} catch(e) {}


в [firefox] 95 вроде работает, немного изменил... эту кнопку

Dumby
Если не сложно, не могли бы вы сделать пример-заготовку для подобной кнопки?
untitled-2.png


Что-то вроде нескольких кнопок в одной.


для FF 91.

Dumby
Уже порядочно долго использую Ваш скрипт для открытия новой вкладки двойным кликом на панели вкладок.
А можно ещё сделать скрипт, чтоб не происходил захват и перетаскивание окна при одиночном клике на панели вкладок?
А то бывает, как-то дрогнет что ли рука и происходит захват и сдвиг окна. Ну такая опция была и сейчас присутствует в новом Tab Mix Plus.
Очень хотелось бы вернуть эту функцию в виде скрипта

скрытый текст
Tab Mix Plus поставил, посмотрел, ну три небольших скрипта и два дополнения она мне компенсировала.
Но в обычном [firefox] нужен ещё скрипт для работы дополнения и bootstrapLoader.xpi по-любому ставить. В общем отказался, в [nightly] только пока оставил


______.PNG

sandro79 пишет

А можно ещё сделать скрипт, чтоб не происходил захват и перетаскивание окна при одиночном клике на панели вкладок?

Возможно я ошибаюсь, но вроде это можно реализовать стилем. Что-то типа такого:
ID_ПАНЕЛИ { -moz-window-dragging: no-drag !important; }

unter_officer пишет

Возможно я ошибаюсь, но вроде это можно реализовать стилем. Что-то типа такого:

Нет, Вы не ошибаетесь, действительно можно стилем. Спасибо за подсказку :beer:
Сделал стилем, добавил ещё панель закладок, работает прекрасно, вопрос закрыт

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

Выделить код

Код:

#TabsToolbar, #PlacesToolbar {
  -moz-window-dragging: no-drag !important;
}

unter_officer пишет

Если не сложно, не могли бы вы сделать пример-заготовку для подобной кнопки?

Ещё как сложно. Вопрос слишком общего характера.
Почти ничего не дано. Какой нужен уровень абстракции, какие там задачи,
что именно не получается, только гадать остаётся. Ладно, набрал простенький вариант.
Насколько далеко, и с какой стороны, это будет от желаемого — неизвестно.
Короче, лучше бы побольше конкретики.

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	label: "Some Label",
	tooltiptext: "Some Tooltip Text",

	id: "_some_unique_identifier_",
	localized: false,
	onCreated(btn) {
		btn.setAttribute("type", "menu");
		btn.setAttribute("image", "chrome://global/skin/narrate/headphone-active.svg");

		var doc = btn.ownerDocument;
		var popup = doc.createXULElement("menupopup");
		popup.creator = this;
		popup.toggleAttribute("context");
		popup.setAttribute("oncommand", "creator.cmd(event);");
		popup.setAttribute("oncontextmenu", "hidePopup(); creator.cmd(event);");
		
		for(var item of this.data) {
			if (!item) {
				popup.append(doc.createXULElement("menuseparator"));
				continue;
			}
			var menuitem = popup.appendChild(doc.createXULElement("menuitem"));
			menuitem.linkedItem = item;
			menuitem.setAttribute("label", item.lab || "");
			item.ttt && menuitem.setAttribute("tooltiptext", item.ttt);
			if (item.img)
				menuitem.className = "menuitem-iconic",
				menuitem.setAttribute("image", item.img);
		}
		btn.prepend(popup);
	},
	cmd(e) {
		var it = e.target.linkedItem;
		it && this[it.fnc](e, it.val);
	},
	data: [
		{
			lab: "Пункт 1",
			ttt: "Бла",
			img: "chrome://devtools/skin/images/fox-smiling.svg",
			fnc: "sayBla",
		},
		{
			lab: "Пункт 2",
			ttt: "Трижды Бла",
			img: "chrome://devtools/skin/images/fox-smiling.svg",
			fnc: "sayBla",
			val: 3,
		},
		null, // <= separator
		{
			lab: "Пункт 3",
			fnc: "alertLabel",
		},
		{
			lab: "Пункт 4",
			fnc: "viewImgSource",
			img: "chrome://browser/skin/protections/resolved-breach.svg",
		},
		null,
		{
			lab: "Пункт 5",
			ttt: "Default",
			img: "chrome://browser/skin/preferences/face-sad.svg",
		},
	],
	sayBla(e, val = 1) {
		Services.prompt.alert(null, null, "Бла ".repeat(val));
	},
	alertLabel(e) {
		e.view.alert(e.target.label);
	},
	viewImgSource(e) {
		var gb = e.view.gBrowser;
		gb.selectedTab = gb.addTrustedTab(
			"view-source:" + e.target.image,
			{index: gb.selectedTab._tPos + 1}
		);
	},
	undefined(e) {
		Services.prompt.alert(null, "Method missing!", "event.button = " + e.button);
	}
}))();

Dumby пишет

Короче, лучше бы побольше конкретики.

На многих кнопках вешаются разные действия на клик мыши с модификаторами (Shift+ЛКМ, Ctrl+ПКМ и тому подобное).
Мне тяжеловато запоминать все эти варианты, особенно когда кнопок много, а с моим неважным зрением постоянно вглядываться во всплывающие подсказки тоже тяжело.


Мне проще сделать одну кнопку и добавлять в неё по мере необходимости нужные действия.
Абстрактный пример: "Пункт 1 - Закрыть все вкладки", "Пункт 2 - Восстановить закрытую вкладку", "Пункт 3 - Открыть консоль браузера" и т.д. и т.п.




P.S. Dumby, большое спасибо за кнопку.

Случайно поднял глаза и заметил. ;) В кнопке для адресной строки "Копировать ссылку" в [firefox] 95 сменился значок. Теперь такой iconURL: "chrome://global/skin/icons/link.svg"

Это глюк гугла или что-то в скрипте? Автоматический перевод страницы Ru --> En не переводит. En --> Ru работает. №8287.
Перевод выделенного в окне работает правильно.

скрытый текст
kIqRWWe.png
xJ9T6om.png
LUlmMTi.png

xrun1, здравствуйте.
   
Нужно эту строчку:

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

Выделить код

Код:

var url = "http://translate.google.com/translate?u="+encodeURIComponent(urlt)+"&hl="+lng+"&langpair="+dir+"&tbb=1";
   
заменить на:
   
var url = "https://translate.google.com/translate?sl=auto&tl=ru&u="+encodeURIComponent(urlt)+"&hl="+lng+"&langpair="+dir+"&tbb=1";

Пострел, приветствую!
Попробовал, но у меня без изменений. Английский на русский переводит. Русский на английский не переводит: русский определяется автоматически, а второй язык тоже русский.

Хотел на халяву проскочить. Не получилось, пришлось самому подумать.:D
Пострел, спасибо, что подсказал, в какую сторону копать. Теперь работает, добавил параметр языка '&tl=' + l[1]

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

Выделить код

Код:

var url = "http://translate.google.com/translate?u="+encodeURIComponent(urlt)+"&hl="+lng+"&langpair="+dir+"&tbb=1";
   
заменить на:

var url = "http://translate.google.com/translate?u=" + encodeURIComponent(urlt) + '&tl=' + l[1] + "&hl=" + lng + "&langpair=" + dir + "&tbb=1";

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

Dumby
Не подскажите, как в моём примере добавить возможность вставлять "menuseparator" в тех местах, где мне это понадобится?
Что-то не могу сообразить, как это сделать.

Пример

Выделить код

Код:

try { (this.contextmenubookmark = {
	init(that) {
		var contextMenu = this.contextMenu = document.querySelector("#placesContext");
		if (!contextMenu) return;
		contextMenu.addEventListener("popupshowing", this);
		that.unloadlisteners.push("contextmenubookmark");
		var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
			/* Здесь какой-то стиль ..... */
		`);
		try { windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET); } catch (e) {}
	},
	destructor() {
		this.contextMenu.removeEventListener("popupshowing", this);
	},
	handleEvent(e) {
		var array = [
			["ucf_ID_1", "label_1", "func_1", "data:image/png;base64,....."],
			["ucf_ID_2", "label_2", "func_2", "data:image/png;base64,....."],
			// ["separator"],
			["ucf_ID_3", "label_3", "func_3", "data:image/png;base64,....."],
			// ["separator"],
			["ucf_ID_4", "label_4", "func_4", "data:image/png;base64,....."],
			["ucf_ID_5", "label_5", "func_5", "data:image/png;base64,....."],
			["ucf_ID_6", "label_6", "func_6", "data:image/png;base64,....."],
			// ["separator"],
			["ucf_ID_7", "label_7", "func_7", "data:image/png;base64,....."],
			// ["separator"],
			["ucf_ID_8", "label_8", "func_8", "data:image/png;base64,....."],
		];
		array.forEach(m=> {
			// if (m[0] == "separator") { document.createXULElement("menuseparator"); return };
			var menuitem = document.createXULElement("menuitem");
			menuitem.setAttribute("id", m[0]);
			menuitem.setAttribute("label", m[1]);
			menuitem.className = "menuitem-iconic";
			menuitem.setAttribute("image", m[3]);
			menuitem.setAttribute("oncommand", m[2]);
			(this.contextMenu.lastElementChild).after(menuitem);
			this.handleEvent = () => menuitem.hidden;
		});
	},
}).init(this);
} catch(ex) { Cu.reportError(ex); }

unter_officer пишет

вставлять "menuseparator"

Чтобы вставлять "menuseparator" его нужно... «вставлять»,
а не просто создать и... всё.

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

Выделить код

Код:

…
			// if (m[0] == "separator") { document.createXULElement("menuseparator"); return };
			if (m[0] == "separator") { e.target.append(document.createXULElement("menuseparator")); return; };

Dumby пишет

Чтобы вставлять "menuseparator" его нужно... «вставлять»,
а не просто создать и... всё.

Спасибо за помощь.

Dumby
Помогите пожалуйста по моей коллекционной сборке 69 [firefox]. По мне, так 69 - самая удачная в 60-ой линейке, поэтому решил оставить для коллекции.
Проблема в том, что не получается заставить работать на ней скрипт "Кнопка Дополнения".
Ну в этом наверно нет ничего странного, т.к. скрипт делался уже в бытность 70-ых версий, а в них, кажется с 72, менеджер дополнений был переработан.
Мне пришлось в 69 использовать скрипт extensionOptionsMenu.uc.js от xiaoxiaoflood, он работает как в 69, так и в актуальных версиях, но средствами user_chrome_files запустить его не удалось, поэтому был использован метод загрузки от автора этого скрипта - папка utils и этот код в config.js параллельно с user_chrome_files.
Может можно как-то изменить скрипт авторства Vitaliy V., чтоб он заработал на 69 версии, что предпочтительней, т.к. этот скрипт гораздо лучше, или, в крайнем случае, изменить скрипт от xiaoxiaoflood, чтоб его можно было запустить в user_chrome_files?
Я залил на Яндекс-диск свою настроенную портативку 69, без дополнений, темы, закладок и т.п.
Оба скрипта присутствуют и подключены и были немного изменены: пути до иконки, русификация скрипта xiaoxiaoflood, чекбоксы в скрипте Виталия не цветные, а как были в первом его скрипте, но он точно не будет его подгонять под неактуальную версию, поэтому прошу Вас посмотреть, может можно что-то сделать.
И еще просьба, если не очень сложно, может можно изменить скрипты "Замена фавиконок для сайтов" для работы в 69 [firefox], а то дополнение Favicon Switcher работает хуже чем эти скрипты - заменяет фавикон не сразу, и на Яндексе работает не совсем корректно.

sandro79 пишет

не получается заставить работать на ней скрипт "Кнопка Дополнения"
Может можно как-то изменить скрипт авторства Vitaliy V., чтоб он заработал на 69 версии, что предпочтительней

Что-то у меня там нет ChromeUtils в сандбоксе, наверно версия UCF совсем старая.
Ладно, будем исходить из того что нет. Возьмём из глобального объекта JSM'ок.
Вобщем, так, формально, почистил от операторов опцинольной последовательности,
и импорт модулей записал иначе, и, вроде, завелось.
Но там же дофига всего, так что нужно тестировать.

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

Выделить код

Код:

(async id => {
	label = "Дополнения",
	tooltiptext = "ЛКМ: Меню дополнений\nСКМ: Отладка дополнений\nПКМ: Открыть менеджер дополнений",
	img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 24,4.5 18,12 3,23.7 12,32.7 3.9,44.1 7.8,48 H 45 C 46.7,48 48,46.7 48,45 V 26.1 L 34.8,12.9 31.8,12.3 Z'/><path style='fill:white;' d='M 19.88,3 C 16.93,3 14.55,4.662 14.55,6.701 14.63,7.474 15.11,8.438 15.37,8.762 16.59,10.41 16.59,11.44 16.29,12.06 H 6.299 C 4.476,12.06 3,13.53 3,15.35 V 23.68 C 3.625,24 4.65,24 6.299,22.77 6.625,22.52 7.587,22.02 8.363,21.94 10.4,21.94 12.06,24.35 12.06,27.29 12.06,30.24 10.4,32.65 8.363,32.65 7.725,32.63 6.774,32.07 6.299,31.82 4.65,30.59 3.625,30.59 3,30.91 V 41.71 C 3,43.53 4.476,45 6.299,45 H 19.58 C 19.88,44.38 19.88,43.35 18.65,41.71 18.4,41.38 17.91,40.42 17.82,39.65 17.82,37.6 20.23,35.94 23.18,35.94 26.14,35.94 28.55,37.6 28.55,39.65 28.53,40.28 27.97,41.23 27.71,41.71 26.47,43.35 26.47,44.38 26.79,45 H 32.65 C 34.47,45 35.96,43.53 35.96,41.71 V 32.55 C 36.56,32.23 37.59,32.23 39.23,33.47 39.72,33.73 40.68,34.29 41.29,34.29 43.35,34.29 45,31.91 45,28.94 45,25.99 43.35,23.59 41.29,23.59 40.54,23.67 39.58,24.17 39.23,24.41 37.59,25.65 36.56,25.65 35.96,25.33 V 15.35 C 35.96,13.53 34.47,12.06 32.65,12.06 H 23.49 C 23.19,11.44 23.19,10.41 24.41,8.762 24.66,8.287 25.22,7.337 25.23,6.713 25.23,4.662 22.85,3 19.88,3' /></g></svg>",
	checked = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><path d='M 3,7 7,11 13,5' style='fill:none;stroke:white;stroke-width:1;'/></svg>",
	show_version = true,
	show_description = true,
	user_permissions = true,
	show_hidden = true,
	show_disabled = true,
	enabled_first = true,
	exceptions_listset = new Set([

	]),
	exceptions_type_listset = new Set([

	]);

	var imp = Cu.getGlobalForObject(Cu).ChromeUtils.import;
	var {AddonManager} = imp("resource://gre/modules/AddonManager.jsm");
	var {GlobalManager} = imp("resource://gre/modules/ExtensionParent.jsm").ExtensionParent;

	var extensionOptionsMenu = {
		get alertsService() {
			delete this.alertsService;
			return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
		},
		get clipboardHelp() {
			delete this.clipboardHelp;
			return this.clipboardHelp = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		},
		get exceptions_type_listarr() {
			delete this.exceptions_type_listarr;
			var arr = ["extension", "theme", "locale", "dictionary"];
			if (!exceptions_type_listset.size)
				return this.exceptions_type_listarr = arr;
			return this.exceptions_type_listarr = arr.filter(type => !exceptions_type_listset.has(type));
		},
		async populateMenu(e) {
			var popup = e.target, doc = e.view.document;
			var addons = await AddonManager.getAddonsByTypes(this.exceptions_type_listarr);
			var addonsMap = new WeakMap(),
			setAttributesMenu = (mi, addon, extension) => {
				var permissions, uuid,
				props = {
					label: `${addon.name}${show_version ? ` ${addon.version}` : ""}`,
					class: "menuitem-iconic",

					tooltiptext: `${
						show_description && addon.description ? addon.description + "\n" : ""
					}ID: ${
						addon.id
					}${
						addon.isActive && (uuid = extension && extension.uuid) ? `\nUUID: ${uuid}` : ""
					}${
						user_permissions && (
							permissions = addon.userPermissions && addon.userPermissions.permissions || ""
						).length ? `\nРазрешения: ${permissions.join(", ")}` : ""
					}\n${
						addon.optionsURL ? "\nЛКМ: Настройки" : ""
					}\nCtrl+ЛКМ: Копировать ID${
						uuid ? "\nShift+ЛКМ: Копировать UUID" : ""
					}${
						addon.creator && addon.creator.url ? "\nCtrl+Shift+ЛКМ: Автор" : ""
					}${
						addon.homepageURL ? "\nСКМ: Домашняя страница" : ""
					}${
						!addon.isBuiltin ? "\nCtrl+СКМ: Просмотр источника" : ""
					}\nShift+СКМ: Просмотр источника во вкладке\nПКМ: Включить/Отключить${
						!addon.isSystem && !addon.isBuiltin ? "\nCtrl+ПКМ: Удалить" : ""
					}`
				};
				for (let p in props)
					mi.setAttribute(p, props[p]);
				if (addon.iconURL)
					mi.setAttribute("image", addon.iconURL);
				var cls = mi.classList;
				addon.isActive ? cls.remove("ucf-disabled") : cls.add("ucf-disabled");
				addon.optionsURL ? cls.remove("ucf-notoptions") : cls.add("ucf-notoptions");
				addon.isSystem ? cls.add("ucf-system") : cls.remove("ucf-system");
				cls.add(`ucf-type-${addon.type}`);
			};
			addons.filter(a => !(a.iconURL || "").startsWith("resource://search-extensions/")).sort((a, b) => {
				var ka = `${(enabled_first ? a.isActive ? "0" : "1" : "")}${a.type || ""}${a.name.toLowerCase()}`;
				var kb = `${(enabled_first ? b.isActive ? "0" : "1" : "")}${b.type || ""}${b.name.toLowerCase()}`;
				return (ka < kb) ? -1 : 1;
			}).forEach(addon => {
				if (!exceptions_listset.has(addon.id) &&
					(!addon.hidden || show_hidden) &&
					(!addon.userDisabled || show_disabled)) {
					let extension = GlobalManager.extensionMap.get(addon.id),
					mi = doc.createXULElement("menuitem");
					setAttributesMenu(mi, addon, extension);
					mi._Addon = addon;
					mi._Extension = extension;
					popup.append(mi);
					addonsMap.set(addon, mi);
				}
			});
			var click = e => {
				e.preventDefault();
				e.stopPropagation();
				this.handleClick(e);
			};
			popup.addEventListener("click", click);
			var listener = {
				onEnabled: addon => {
					var mi = addonsMap.get(addon);
					if (mi)
						setAttributesMenu(mi, addon, mi._Extension);
				},
				onDisabled: addon => {
					listener.onEnabled(addon);
				},
				onInstalled: addon => {
					var extension = GlobalManager.extensionMap.get(addon.id),
					mi = doc.createXULElement("menuitem");
					setAttributesMenu(mi, addon, extension);
					mi._Addon = addon;
					mi._Extension = extension;
					popup.prepend(mi);
					addonsMap.set(addon, mi);
				},
				onUninstalled: addon => {
					var mi = addonsMap.get(addon);
					if (mi) {
						mi.remove();
						addonsMap.delete(addon);
					}
				},
			};
			AddonManager.addAddonListener(listener);
			popup.addEventListener("popuphiding", () => {
				AddonManager.removeAddonListener(listener);
				popup.removeEventListener("click", click);
				addonsMap = null;
				for (let item of popup.querySelectorAll("menuitem"))
					item.remove();
			}, { once: true });
		},
		handleClick(e) {
			var win = e.view, mi = e.target;
			if (!("_Addon" in mi) || !("_Extension" in mi))
				return;
			var addon = mi._Addon, extension = mi._Extension;
			switch (e.button) {
				case 0:
					if (e.ctrlKey && e.shiftKey) {
						if (addon.creator && addon.creator.url)
							win.gBrowser.selectedTab = this.addTab(win, addon.creator.url);
					} else if (e.ctrlKey) {
						this.clipboardHelp.copyString(addon.id);
						win.setTimeout(() => {
							this.alertsService.showAlertNotification(`${img}`, "ID в буфере обмена!", addon.id, false);
						}, 100);
					} else if (e.shiftKey) {
						if (extension && extension.uuid) {
							this.clipboardHelp.copyString(extension.uuid);
							win.setTimeout(() => {
								this.alertsService.showAlertNotification(`${img}`, "UUID в буфере обмена!", extension.uuid, false);
							}, 100);
						}
					} else if (addon.isActive && addon.optionsURL)
						this.openAddonOptions(addon, win);
					win.closeMenus(mi);
					break;
				case 1:
					if (e.ctrlKey) {
						if (!addon.isBuiltin)
							this.browseDir(addon);
					} else if (e.shiftKey)
						this.browseDir(addon, win);
					else if (addon.homepageURL)
						win.gBrowser.selectedTab = this.addTab(win, addon.homepageURL);
					win.closeMenus(mi);
					break;
				case 2:
					if (!e.ctrlKey) {
						let endis = addon.userDisabled ? "enable" : "disable";
						if (addon.id == "screenshots@mozilla.org")
							Services.prefs.setBoolPref("extensions.screenshots.disabled", !addon.userDisabled);
						else if (addon.id == "webcompat-reporter@mozilla.org")
							Services.prefs.setBoolPref("extensions.webcompat-reporter.enabled", addon.userDisabled);
						addon[endis]({ allowSystemAddons: true });
					} else if (!addon.isSystem && !addon.isBuiltin) {
						win.closeMenus(mi);
						if (Services.prompt.confirm(win, null, `Удалить ${addon.name}?`))
							addon.uninstall();
					}
				break;
			}
		},
		openAddonOptions(addon, win) {
			switch (addon.optionsType) {
				case 5:
					win.BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(addon.id)}/preferences`);
					break;
				case 3:
					win.switchToTabHavingURI(addon.optionsURL, true);
					break;
			}
		},
		browseDir(addon, win) {
			try {
				if (!win) {
					let file = Services.io.getProtocolHandler("file")
					.QueryInterface(Ci.nsIFileProtocolHandler)
					.getFileFromURLSpec(addon.getResourceURI().QueryInterface(Ci.nsIJARURI).JARFile.spec);
					if (file.exists())
						file.launch();
				} else
					win.gBrowser.selectedTab = this.addTab(win, addon.getResourceURI().spec);
			} catch (e) {}
		},
		addTab(win, url, params = {}) {
			params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
			params.relatedToCurrent = true;
			return win.gBrowser.addTab(url, params);
		},
	};
	CustomizableUI.createWidget({
		type: "custom",
		id, label, tooltiptext,
		localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onBuild(doc) {
			var btn = doc.createXULElement("toolbarbutton"), win = doc.defaultView,
			props = {
				context: "",
				type: "menu",
				id, label, tooltiptext,
				class: "toolbarbutton-1 chromeclass-toolbar-additional",
			};
			for (let p in props)
				btn.setAttribute(p, props[p]);
			btn.addEventListener("click", e => {
				if (e.button == 1)
					e.view.switchToTabHavingURI("about:debugging#/runtime/this-firefox", true, { ignoreFragment: "whenComparing", triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), });
				else if (e.button == 2)
					e.view.BrowserOpenAddonsMgr("addons://list/extension");
			});
			var mp = doc.createXULElement("menupopup");
			mp.id = `${id}-popup`;
			mp.addEventListener("contextmenu", e => {
				e.preventDefault();
				e.stopPropagation();
			});
			mp.addEventListener("popupshowing", e => {
				extensionOptionsMenu.populateMenu(e);
			});
			btn.append(mp);
			var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
				#${id}, #${id}-popup menuitem {
					list-style-image: url("${img}") !important;
				}
				#${id}-popup menuitem::after {
					display: -moz-box !important;
					content: "" !important;
					height: 16px !important;
					width: 16px !important;
					padding: 0 !important;
					border: 1px solid rgb(0, 116, 232) !important;
					border-radius: 0 !important;
					background-repeat: no-repeat !important;
					background-position: center !important;
					background-size: 16px !important;
					background-color: rgb(0, 116, 232) !important;
					background-image: url("${checked}") !important;
					opacity: 1 !important;
				}
				#${id}-popup menuitem.ucf-disabled::after {
					border-color: currentColor !important;
					background-color: transparent !important;
					background-image: none !important;
					opacity: .6 !important;
				}
				#${id}-popup menuitem.ucf-disabled > label,
				#${id}-popup menuitem.ucf-notoptions > label {
					opacity: .6 !important;
				}
				#${id}-popup menuitem.ucf-system > label {
					text-decoration: underline !important;
					text-decoration-style: dotted !important;
				}
				#${id}-popup menuitem > label {
					margin-inline-end: 0 !important;
				}
				#${id}-popup menuitem > .menu-accel-container {
					display: -moz-box !important;
					padding: 4px !important;
					margin: 0 !important;
					opacity: 1 !important;
				}
				#${id}-popup menuitem > .menu-accel-container .menu-iconic-accel {
					display: -moz-box !important;
					margin: 0 !important;
					height: 8px !important;
					width: 8px !important;
					border-radius: 4px !important;
					background-color: transparent !important;
					opacity: 1 !important;
					font-size: 0 !important;
				}
				#${id}-popup menuitem.ucf-type-dictionary > .menu-accel-container .menu-iconic-accel {
					background-color: rgb(227, 27, 93) !important;
				}
				#${id}-popup menuitem.ucf-type-locale > .menu-accel-container .menu-iconic-accel {
					background-color: rgb(48, 172, 55) !important;
				}
				#${id}-popup menuitem.ucf-type-theme > .menu-accel-container .menu-iconic-accel {
					background-color: rgb(219, 106, 0) !important;
				}
			`);
			try {
				win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
			} catch (e) {}
			return btn;
		},
	});
})("ucf-aom-button");

Я залил

яд.иск :usch:. Это ты залил для всех, кроме меня.

может можно изменить скрипты "Замена фавиконок для сайтов" для работы в 69

Похоже, в 69 у JSWindowActorChild нет callback'а actorCreated().
И about:config там ещё древесный, и :is(), понятное дело, отсутствует.


Ладно, попробуем. Итак, в первом скрипте меняем первую строку на
Cu.getGlobalForObject(Cu).ChromeUtils.registerWindowActor("LinkWinActor", {


Остальные двое:

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

Выделить код

Код:

const ICONS = { // "домен, или адрес для about|chrome|resource": "иконка",

	"yandex.ru": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><rect rx='2' ry='2' width='16' height='16' style='fill:rgb(231, 43, 90);'/><path d='M 8.56,2 C 6.31,2 4.49,3.5 4.49,5.99 4.49,7.49 5.31,8.48 6.97,9.57 L 4,13.5 V 14 H 5.76 L 8.53,9.57 H 9.47 V 14 H 11 V 2 Z M 9.47,8.48 H 8.67 C 7.36,8.48 6.05,7.98 6.05,5.99 6.05,4 7.26,3 8.47,3 H 9.47 Z' style='fill:white;'/></g></svg>",
	"nnmclub.to": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><path d='M 0.887,0 C 0.54,0.14 0.289,0.279 0.135,0.711 0.051,0.851 0.015,0.991 0,1.27 -0.009,1.71 0.061,2.16 0.325,3.05 0.564,3.88 0.612,4.12 0.612,4.37 0.612,4.48 0.612,4.56 0.588,4.67 0.457,5.43 0.457,6.07 0.588,6.56 0.672,6.87 0.779,7.09 0.947,7.28 1.21,7.57 1.49,7.73 2.05,7.87 L 2.27,7.91 2.11,7.96 C 1.87,8.01 1.71,8.08 1.55,8.19 1.33,8.31 1.22,8.41 1.09,8.59 0.947,8.65 0.887,8.83 0.839,8.99 0.803,9.17 0.792,9.41 0.827,9.65 0.851,9.93 0.851,9.93 0.827,10 0.827,10.1 0.767,10.3 0.708,10.5 0.588,10.9 0.564,11.1 0.564,11.2 0.564,11.5 0.612,11.6 0.875,11.8 1.15,12.2 1.18,12.3 1.15,12.6 1.1,13.2 1.18,13.6 1.39,13.7 1.49,13.7 1.6,13.9 1.75,14 1.93,14 2.05,14.1 2.28,14.4 2.48,14.5 2.55,14.5 2.64,14.5 2.73,14.7 2.73,14.7 2.87,14.7 3.04,14.7 3.04,14.7 3.47,14.5 4.03,14 4.19,13.9 4.44,13.9 4.55,13.7 4.6,13.7 4.68,13.7 4.88,13.6 5.11,13 5.35,11.9 5.39,11.8 5.43,11.6 5.47,11.5 5.65,10.9 5.89,10.4 6.12,10 6.24,9.83 6.48,9.59 6.49,9.59 6.49,9.59 6.49,9.75 6.48,10.1 6.44,10.3 6.43,10.5 6.28,11.1 6.12,11.5 6.08,11.7 6.05,12 6.03,12.4 6.11,12.6 6.21,12.9 6.32,13 6.43,13 6.57,13 6.71,12.9 6.81,12.6 6.91,12.4 7.07,12 7.11,11.7 7.11,11 7.11,10.7 7.13,10.4 7.27,10 7.35,9.75 7.32,9.75 7.37,9.83 7.52,10.1 7.64,10.7 7.69,11.3 7.72,11.6 7.72,12.2 7.69,12.4 7.64,13 7.61,13.6 7.68,14 7.72,14.4 7.76,14.5 8.01,14.5 8.15,14.7 8.31,14.8 8.67,15.5 8.92,15.9 9.03,15.9 9.11,16 9.17,16 9.17,16 9.33,16 9.52,16 9.52,16 9.77,15.9 10.1,15.7 10.1,15.7 10.4,15.7 10.7,15.7 10.7,15.7 10.7,15.7 11,15.6 11.1,15.5 11.4,14.9 11.4,14.9 11.4,14.8 11.5,14.7 11.5,14.5 11.6,14.4 11.7,14.1 12.2,13.9 12.2,13.9 12.2,13.7 12.4,13.6 12.4,13.6 12.4,13 12.4,12.6 12.5,12.4 12.5,12.3 12.5,12.3 12.5,12.2 12.7,12 12.8,11.7 12.8,11.6 12.8,11.5 12.8,11.2 12.8,11.1 12.8,11.1 12.7,10.7 12.4,10.3 12,9.93 11.9,9.83 11.9,9.83 11.9,9.83 12.2,9.93 12.7,9.93 12.9,9.93 13.7,9.75 14,9.08 14.1,7.84 14.1,7.76 14.3,7.59 14.3,7.49 14.3,7.27 14.3,7.19 14.5,7.01 14.5,6.83 14.8,6.6 15.1,6.19 15.7,5.24 15.9,4.85 16,4.41 16,4.31 16,4.25 16,4.03 16,3.84 16,3.76 16,3.69 15.7,3.03 15.2,2.68 14.3,2.75 13.7,2.76 13,2.97 12.2,3.32 12,3.4 11.6,3.63 11.4,3.81 10.2,4.49 9.11,5.55 8.08,6.85 7.95,7 7.85,7.11 7.85,7.11 7.85,7.11 7.84,7.05 7.84,7.04 7.76,6.93 7.72,6.87 7.61,6.81 L 7.53,6.73 7.64,6.47 C 7.92,5.92 8.13,5.51 8.31,5.23 8.35,5.12 8.51,4.91 8.76,4.63 9.43,3.73 9.68,3.47 9.77,3.35 10,3.21 10,3.2 10,3.15 10.1,3.05 10,2.92 10,2.87 9.95,2.87 9.95,2.91 9.84,2.97 9.84,3.11 9.52,3.44 9.11,4.03 8.92,4.19 8.76,4.37 8.76,4.45 8.35,4.95 7.95,5.69 7.61,6.47 7.53,6.6 7.49,6.73 7.47,6.73 7.47,6.73 7.43,6.71 7.37,6.71 L 7.28,6.68 V 6.59 C 7.2,5.69 7.08,5.08 6.91,4.49 6.83,4.16 6.8,4 6.59,3.44 6.33,2.75 6.2,2.32 6.2,2.21 6.17,2.09 6.05,2.08 6,2.2 5.97,2.31 5.99,2.39 6.11,2.55 6.21,2.75 6.32,2.97 6.73,4.15 7,4.96 7.07,5.13 7.2,6.29 7.23,6.68 7.23,6.68 7.2,6.68 7.13,6.68 6.97,6.75 6.89,6.83 6.83,6.87 6.81,6.89 6.81,6.89 6.81,6.89 6.75,6.73 6.68,6.56 6.53,6.07 6.4,5.77 6.2,5.27 5.36,3.43 4.39,2.01 3.36,1.13 2.64,0.571 2.01,0.14 1.49,0 1.32,0 1.02,0 0.887,0 Z' style='fill:rgb(0, 140, 255);stroke:black;stroke-width:0.6;stroke-linejoin:round;stroke-linecap:round;'/></g></svg>",
	
	"about:config": "resource://normandy/skin/shared/heartbeat-icon.svg",
	"about:user-chrome-files": "chrome://browser/skin/accessibility.svg",
};

var EXPORTED_SYMBOLS = ["LinkWinActorChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
const LINK_SELECTOR = "link[href]:-moz-any([rel~='icon'],[rel~='apple-touch-icon'],[rel~='apple-touch-icon-precomposed'],[rel~='fluid-icon'],[rel~='mask-icon'])";

var noop = () => {};
var re = /^(?:about|chrome|resource)$/;
var idn = Cc["@mozilla.org/network/idn-service;1"].getService(Ci.nsIIDNService);

var once = function() {
	delete this.handleEvent;
	var doc = this.document;
	var docURI = doc.documentURIObject, host;
		if (re.test(docURI.scheme)) host = docURI.specIgnoringRef;
		else try {
			host = idn.convertToDisplayIDN(Services.eTLD.getBaseDomain(docURI), {});
		} catch {
			try {host = docURI.displayHost;}
			catch {host = docURI.specIgnoringRef;}
		}
		var icon = ICONS[host];
		if (!icon) return this.handleEvent = noop;

		this._icon = icon;
		this.onHeadParsed(doc.head || doc.documentElement);
}

class LinkWinActorChild extends JSWindowActorChild {
	handleEvent = once;
	onHeadParsed(target) {
		for (let link of target.querySelectorAll(LINK_SELECTOR))
			link.remove();
		var link = this.document.createElementNS("http://www.w3.org/1999/xhtml", "link");
		link.setAttribute("rel", "icon");
		link.setAttribute("sizes", "any");
		link.setAttribute("href", this._icon);
		target.append(link);
	}
	onLinkEvent(link) {
		if (!link.matches(LINK_SELECTOR) || link.href == this._icon) return;
		link.href = this._icon;
		link.setAttribute("rel", "icon");
		link.setAttribute("sizes", "any");
		if (link.hasAttribute("type"))
			link.removeAttribute("type");
	}
	handleEvent(e) {
		switch (e.type) {
			case "DOMLinkAdded":
			case "DOMLinkChanged":
				this.onLinkEvent(e.target);
				break;
			case "pageshow":
				this.onHeadParsed(e.target.head || e.target.documentElement);
				break;
		}
	}
}


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

Выделить код

Код:

const ICONS = { // "поддомен + домен, или адрес для about|chrome|resource": "иконка"

	"yandex.ru": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><rect rx='2' ry='2' width='16' height='16' style='fill:rgb(231, 43, 90);'/><path d='M 8.56,2 C 6.31,2 4.49,3.5 4.49,5.99 4.49,7.49 5.31,8.48 6.97,9.57 L 4,13.5 V 14 H 5.76 L 8.53,9.57 H 9.47 V 14 H 11 V 2 Z M 9.47,8.48 H 8.67 C 7.36,8.48 6.05,7.98 6.05,5.99 6.05,4 7.26,3 8.47,3 H 9.47 Z' style='fill:white;'/></g></svg>",
	get "passport.yandex.ru"() {
		return this["yandex.ru"];
	},
	"nnmclub.to": "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><g><path d='M 0.887,0 C 0.54,0.14 0.289,0.279 0.135,0.711 0.051,0.851 0.015,0.991 0,1.27 -0.009,1.71 0.061,2.16 0.325,3.05 0.564,3.88 0.612,4.12 0.612,4.37 0.612,4.48 0.612,4.56 0.588,4.67 0.457,5.43 0.457,6.07 0.588,6.56 0.672,6.87 0.779,7.09 0.947,7.28 1.21,7.57 1.49,7.73 2.05,7.87 L 2.27,7.91 2.11,7.96 C 1.87,8.01 1.71,8.08 1.55,8.19 1.33,8.31 1.22,8.41 1.09,8.59 0.947,8.65 0.887,8.83 0.839,8.99 0.803,9.17 0.792,9.41 0.827,9.65 0.851,9.93 0.851,9.93 0.827,10 0.827,10.1 0.767,10.3 0.708,10.5 0.588,10.9 0.564,11.1 0.564,11.2 0.564,11.5 0.612,11.6 0.875,11.8 1.15,12.2 1.18,12.3 1.15,12.6 1.1,13.2 1.18,13.6 1.39,13.7 1.49,13.7 1.6,13.9 1.75,14 1.93,14 2.05,14.1 2.28,14.4 2.48,14.5 2.55,14.5 2.64,14.5 2.73,14.7 2.73,14.7 2.87,14.7 3.04,14.7 3.04,14.7 3.47,14.5 4.03,14 4.19,13.9 4.44,13.9 4.55,13.7 4.6,13.7 4.68,13.7 4.88,13.6 5.11,13 5.35,11.9 5.39,11.8 5.43,11.6 5.47,11.5 5.65,10.9 5.89,10.4 6.12,10 6.24,9.83 6.48,9.59 6.49,9.59 6.49,9.59 6.49,9.75 6.48,10.1 6.44,10.3 6.43,10.5 6.28,11.1 6.12,11.5 6.08,11.7 6.05,12 6.03,12.4 6.11,12.6 6.21,12.9 6.32,13 6.43,13 6.57,13 6.71,12.9 6.81,12.6 6.91,12.4 7.07,12 7.11,11.7 7.11,11 7.11,10.7 7.13,10.4 7.27,10 7.35,9.75 7.32,9.75 7.37,9.83 7.52,10.1 7.64,10.7 7.69,11.3 7.72,11.6 7.72,12.2 7.69,12.4 7.64,13 7.61,13.6 7.68,14 7.72,14.4 7.76,14.5 8.01,14.5 8.15,14.7 8.31,14.8 8.67,15.5 8.92,15.9 9.03,15.9 9.11,16 9.17,16 9.17,16 9.33,16 9.52,16 9.52,16 9.77,15.9 10.1,15.7 10.1,15.7 10.4,15.7 10.7,15.7 10.7,15.7 10.7,15.7 11,15.6 11.1,15.5 11.4,14.9 11.4,14.9 11.4,14.8 11.5,14.7 11.5,14.5 11.6,14.4 11.7,14.1 12.2,13.9 12.2,13.9 12.2,13.7 12.4,13.6 12.4,13.6 12.4,13 12.4,12.6 12.5,12.4 12.5,12.3 12.5,12.3 12.5,12.2 12.7,12 12.8,11.7 12.8,11.6 12.8,11.5 12.8,11.2 12.8,11.1 12.8,11.1 12.7,10.7 12.4,10.3 12,9.93 11.9,9.83 11.9,9.83 11.9,9.83 12.2,9.93 12.7,9.93 12.9,9.93 13.7,9.75 14,9.08 14.1,7.84 14.1,7.76 14.3,7.59 14.3,7.49 14.3,7.27 14.3,7.19 14.5,7.01 14.5,6.83 14.8,6.6 15.1,6.19 15.7,5.24 15.9,4.85 16,4.41 16,4.31 16,4.25 16,4.03 16,3.84 16,3.76 16,3.69 15.7,3.03 15.2,2.68 14.3,2.75 13.7,2.76 13,2.97 12.2,3.32 12,3.4 11.6,3.63 11.4,3.81 10.2,4.49 9.11,5.55 8.08,6.85 7.95,7 7.85,7.11 7.85,7.11 7.85,7.11 7.84,7.05 7.84,7.04 7.76,6.93 7.72,6.87 7.61,6.81 L 7.53,6.73 7.64,6.47 C 7.92,5.92 8.13,5.51 8.31,5.23 8.35,5.12 8.51,4.91 8.76,4.63 9.43,3.73 9.68,3.47 9.77,3.35 10,3.21 10,3.2 10,3.15 10.1,3.05 10,2.92 10,2.87 9.95,2.87 9.95,2.91 9.84,2.97 9.84,3.11 9.52,3.44 9.11,4.03 8.92,4.19 8.76,4.37 8.76,4.45 8.35,4.95 7.95,5.69 7.61,6.47 7.53,6.6 7.49,6.73 7.47,6.73 7.47,6.73 7.43,6.71 7.37,6.71 L 7.28,6.68 V 6.59 C 7.2,5.69 7.08,5.08 6.91,4.49 6.83,4.16 6.8,4 6.59,3.44 6.33,2.75 6.2,2.32 6.2,2.21 6.17,2.09 6.05,2.08 6,2.2 5.97,2.31 5.99,2.39 6.11,2.55 6.21,2.75 6.32,2.97 6.73,4.15 7,4.96 7.07,5.13 7.2,6.29 7.23,6.68 7.23,6.68 7.2,6.68 7.13,6.68 6.97,6.75 6.89,6.83 6.83,6.87 6.81,6.89 6.81,6.89 6.81,6.89 6.75,6.73 6.68,6.56 6.53,6.07 6.4,5.77 6.2,5.27 5.36,3.43 4.39,2.01 3.36,1.13 2.64,0.571 2.01,0.14 1.49,0 1.32,0 1.02,0 0.887,0 Z' style='fill:rgb(0, 140, 255);stroke:black;stroke-width:0.6;stroke-linejoin:round;stroke-linecap:round;'/></g></svg>",
	
	"about:config": "resource://normandy/skin/shared/heartbeat-icon.svg",
	"about:user-chrome-files": "chrome://browser/skin/accessibility.svg",
};

var EXPORTED_SYMBOLS = ["LinkWinActorChild"];
const LINK_SELECTOR = "link[href]:-moz-any([rel~='icon'],[rel~='apple-touch-icon'],[rel~='apple-touch-icon-precomposed'],[rel~='fluid-icon'],[rel~='mask-icon'])";

var noop = () => {};
var re = /^(?:about|chrome|resource)$/;

var once = function() {
	delete this.handleEvent;
	var doc = this.document;
	var docURI = doc.documentURIObject, host;
		if (re.test(docURI.scheme)) host = docURI.specIgnoringRef;
		else 
			try {host = docURI.displayHost;}
			catch {host = docURI.specIgnoringRef;}

		var icon = ICONS[host];
		if (!icon) return this.handleEvent = noop;

		this._icon = icon;
		this.onHeadParsed(doc.head || doc.documentElement);
}

class LinkWinActorChild extends JSWindowActorChild {
	handleEvent = once;
	onHeadParsed(target) {
		for (let link of target.querySelectorAll(LINK_SELECTOR))
			link.remove();
		var link = this.document.createElementNS("http://www.w3.org/1999/xhtml", "link");
		link.setAttribute("rel", "icon");
		link.setAttribute("sizes", "any");
		link.setAttribute("href", this._icon);
		target.append(link);
	}
	onLinkEvent(link) {
		if (!link.matches(LINK_SELECTOR) || link.href == this._icon) return;
		link.href = this._icon;
		link.setAttribute("rel", "icon");
		link.setAttribute("sizes", "any");
		if (link.hasAttribute("type"))
			link.removeAttribute("type");
	}
	handleEvent(e) {
		switch (e.type) {
			case "DOMLinkAdded":
			case "DOMLinkChanged":
				this.onLinkEvent(e.target);
				break;
			case "pageshow":
				this.onHeadParsed(e.target.head || e.target.documentElement);
				break;
		}
	}
}

Dumby пишет

и импорт модулей записал иначе, и, вроде, завелось.
Но там же дофига всего, так что нужно тестировать

Да, отлично, завелось, работает! Вроде всё в норме.

яд.иск :usch:. Это ты залил для всех, кроме меня

Понял. Тогда буду, как и скриншоты, заливать на https://www.upload.ee/

Ладно, попробуем. Итак, в первом скрипте меняем первую строку на
Cu.getGlobalForObject(Cu).ChromeUtils.registerWindowActor("LinkWinActor", {

Да, тоже всё завелось и отлично работает! Как и в актуальных версиях.
Огромное Вам Спасибо! Всё получилось великолепно!

отчёт :beer:
Image_001.png

Здесь https://forum.mozilla-russia.org/viewto … 08#p792708 скрипт ucjsDownloadsManager.uc.js. Никак не могу добиться его работы через UserChromeFiles от VitaliyV, его user_chrome_files установлен, из него работают дополнительные панели инструментов, их настраивать не пришлось, они заработали сразу. Возможно, не туда прописываю или неправильно прописываю. Пробовал запустить  другие скрипты, тоже не получилось.

shadow_user пишет

Никак не могу добиться его работы через UserChromeFiles от VitaliyV, его user_chrome_files установлен

Вот тут уже рабочий пример подключения скриптов.
Так до сих пор, с несущественными правками, всё и работает вплоть до 98 [nightly]. Единственное ucf_wheretoopenlink.js отвалился с 95 [firefox]
scripts, scripts2, scripts3 в путях - это папки в которые я  положил скрипты.
https://forum.mozilla-russia.org/viewto … 76#p794876
Читать надо тему, а так методом тыка ничего не получится конечно. Этот комплект не этот, куда достаточно просто скрипт закинуть, ничего не прописывая, и даже startupCache не надо чистить.

sandro79 пишет

Вот тут уже рабочий пример подключения скриптов.

Не получилось ничего. Ясное дело, тему читал, startupCache чистил кнопкой в настройках UCF. В умолчальных скриптах разкомментирую скрипт автоскрытия тулбара, и тоже не работает. Менял на ваш CustomStylesScripts.jsm, рядом с ним в папку scripts2 ложил ucjsDownloadsManager2.uc.js, и увы.

shadow_user
Скачайте мой настроенный комплект и разбирайтесь в чём у вас проблема.
startupCache надёжнее чистить вручную, т.к. очень часто ни кнопкой комплекта, ни кнопкой в about:support он почему-то не чистится.

sandro79 пишет

Скачайте мой настроенный комплект и разбирайтесь в чём у вас проблема.

Комплект заработал, вижу новые иконки папок и некоторых кнопок, но... кроме прогресса загрузки :D , упрямо не появляется. Может, и еще что не заработало. В about:profiles вижу два профиля, один default - "по умолчанию, сейчас используется и не может быть удален", второй default -esr - "сейчас используется другим приложением и не может быть удален", что-то тут не так, я сам еще один профиль не создавал.
Попробую с чистым профилем или сделаю новый портабельный.

shadow_user пишет

кроме прогресса загрузки

Вот эти два чекбокса должны быть включены.
Вы наверно в настройки комплекта даже не заходили, раз кроме изменений иконок средствами user_chrome.manifest ничего не заметили.
about:user-chrome-files - открыть настройки во вкладке.

sandro79 пишет

Вот эти два чекбокса должны быть включены.

Искренне благодарен за терпение и помощь! По прочтению темы я помню, что один или два д.б. включены, включал по одному, не помогло. Создал новый профиль, включил два, наконец-то прогресс загрузки показался! Возникла пара вопросов:
1. В наборе два скрипта, ucjsDownloadsManager.uc.js и ucjsDownloadsManager2.uc.js, зачем два и в чем разница? Какой из них у меня срабатывает? Теоретически я не большой спец, мне, может, и одного хватило бы.
2. Как отключить лишний, если он действительно лишний, и какой? Или их однозначно должно быть два?
3. А вот это более важно для меня - окно прогресса закрывается по окончании загрузки, а я привык, чтоб оно оставалось на экране, типа чтобы успеть репу почесать, как это сделать?
4. Табы стали переключаться наведением мышки, но я консерватор. Это работа, видимо, судя по названию, скрипта tabs_focus.js в файле. Для отключения достаточно его удалить\переименовать, или можно где-то закомментировать команду вызова?
5. Какой скрипт или стиль показывает фавиконку в строке адреса?

shadow_user пишет

В наборе два скрипта, ucjsDownloadsManager.uc.js и ucjsDownloadsManager2.uc.js, зачем два и в чем разница?

Это один скрипт разделён на два под особенности комплекта user_chrome_files, нужны оба скрипта и они оба подключены.
Чтоб окно прогресса не закрывалось, нужно в обоих скриптах найти строку var closeWhenDone = true; и вместо true вписать false, и следите чтоб кодировка скрипта оставалась UTF8 без BOM после правки и сохранения.

скрипта tabs_focus.js в файле. Для отключения достаточно его удалить\переименовать, или можно где-то закомментировать команду вызова?

Его можно удалить, ну и строку { path: "scripts3/tabs_focus.js", ucfobj: false, }, в CustomStylesScripts.jsm удалить или // закомментировать.
Так и с другим всем ненужным, я просто всё скинул вам с 91-ой версии, лень было по-новой возится или удалять почти всё.

Какой скрипт или стиль показывает фавиконку в строке адреса?

Это скрипт авторства Vitaliy V., только немного изменённый под себя по советам автора. Обсуждение начиная с первой страницы этой темы.
Скрипт исправно работает вплоть до 98 [nightly], но только в user_chrome_files, в классических комплектах его запустить не удалось.

sandro79 пишет

Это один скрипт разделён на два

Все понял и исправил под свои пожелания, кроме: не смог идентифицировать скрипт (или стиль?) для фавиконки, просьба нацелить. Дело в том, что у меня есть скрипт, который показывает фавиконку в размере 22х22, а не 16х16, не скажу, что это лучше или хуже, но мне нужно выбрать один. Так же вроде логично, чтобы фавиконка была не перед замком, а перед урлом, вроде и вы в какой-то теме об этом писали
78.png

shadow_user пишет

не смог идентифицировать скрипт (или стиль?) для фавиконки, просьба нацелить

Скрипт favicon_in_urlbar.js

который показывает фавиконку в размере 22х22, а не 16х16

Ну это тоже можно настроить, см. height и width.
У Виталия в скрипте width: auto !important; прописано, но мне как-то попался

Войдите или зарегистрируйтесь, чтобы увидеть скрытый текст.
со здоровенной иконкой и она не отображалась пока auto не заменил на значение в пикселях как у height.

вроде и вы в какой-то теме об этом писали

Да не, я наоборот у Виталия спрашивал как её перед замком поставить. У Ариса когда-то так было, привык.
identity.before(faviconinurlbar); замените на identity.after(faviconinurlbar);
Вам наверно лучше будет использовать оригинальный авторский скрипт, а не мой правленный.

sandro79 пишет

Скрипт favicon_in_urlbar.js

Ну конечно, нужно быть слепым, чтобы не заметить. На сей момент со всем разобрался, спасибо! :beer:
Почему два скрипта, favicon_in_urlbar.js и favicon_in_urlbar2.js?
Включил остальные птички в UCF, теперь вижу множество кнопок. "Закрыть другие вкладки" это скрипт Close-Tabs-button.js? И какой скрипт кнопки "Показать загрузки"? Просто мечтал об этой кнопке.

shadow_user пишет

Почему два скрипта, favicon_in_urlbar.js и favicon_in_urlbar2.js?

Ну favicon_in_urlbar2.js это Арисовский скрипт, ну просто оставил для потомков :D Он не подключен.

"Закрыть другие вкладки" это скрипт Close-Tabs-button.js?

Ну да. Там два в одном, ещё закрыть текущую.

И какой скрипт кнопки "Показать загрузки"?

To_switch_proxy.js Ну там две кнопки в одном скрипте - переключить прокси и показать загрузки когда-то Виталий по моей просьбе делал.
Если хотите их разделить, то как разделить их я не знаю.
Сейчас в кнопке прокси уже нет кнопки загрузок. Я пока старой, два в одном, пользуюсь.

Dumby
Приветствую! В [firefox] 96 не работает этот скрипт: https://forum.mozilla-russia.org/viewto … 56#p790256, или может что то не так сделал ?

kokoss пишет

В [firefox] 96 не работает этот скрипт

Проверил на 96 — вроде работает.

или может что то не так сделал ?

Да, разумеется, «что то не так сделать» можно всегда.
Но, чтобы попробовать понять что именно не так,
нужно знать что именно сделал, впрочем, об этом и не спрашивалось.

Dumby пишет

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

Добавил скрипт в папку custom_scripts и прописал путь до скрипта в custom_script.js так:

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

Выделить код

Код:

(() => {
    var loadscript = relpath => {
        try {
            Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${relpath}`, globalThis, "UTF-8");
        } catch(e) {}
    };
	loadscript("/ucf-cbbtn-BBCode-Multi.js");
	loadscript("/AutoCopyChild.jsm");
    // loadscript("/Undo_Close_Tabs.js");
    // loadscript("/QuickToggle_AboutConfig.js");
    // и т. д.
})();

kokoss пишет

loadscript("/AutoCopyChild.jsm");

Не-не, JSM'ки scriptloder'ом не грузят.
Их импортируют через ChromeUtils.import();


Можно в CustomStylesScripts.jsm прописать

скриншот

Выделить код

Код:

data:image/png;base64,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

Dumby
А с этим кодом работает:

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

Выделить код

Код:

(async () => { // загрузка внешних js или jsm-скриптов
	var loadscript = name => {
		try { name.split('.').pop().split("?")[0].split("#")[0].toLowerCase() != "jsm"
			? Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`,globalThis,"UTF-8")
			: ChromeUtils.import(`chrome://user_chrome_files/content/custom_scripts/${name}`); return true;
		} catch(e) {}
	};
	loadscript("/AutoCopyChild.jsm");
	loadscript("/ucf-cbbtn-BBCode-Multi.js");
})();

код взял у dobrov

kokoss пишет

А с этим кодом работает:

Ну да, можно и так.
Странное, правда, препарирование name,
и третий аргумент в loadSubScript() выпилен аж в Firefox 66


Но это мелочи, удивительно другое:
вот у тебя имена начинаются с ненужного слэша,
таким образом, в адресе получается двойной слэш,
но всё работает.


То есть, например, адрес вида
chrome://user_chrome_files/content/custom_scripts//////////custom_script.js
прекрасно открывается во вкладке.
Ни за что бы не подумал.

shadow_user пишет

Искренне благодарен за терпение и помощь! По прочтению темы я помню, что один или два д.б. включены, включал по одному, не помогло.

А первый пост видели? Именно для начинающих в начале темы есть готовый Демо-профиль, в котором все нужные опции включены и имеется минимально-необходимый набор скриптов.
Подробности в  firefox_profile_dobrov.html.


kokoss пишет

А с этим кодом работает:

kokoss — Вот более новый вариант загрузчика js/jsm скриптов для custom_script_win.js:

Выделить код

Код:

var loadscript = (js, win = this, init) => { try {
		if (/\.jsm$/i.test(js)) { // скрипт js или jsm [инициализация]
				var obj = ChromeUtils.import('chrome://user_chrome_files/content/custom_scripts/'+ js, win);
				init && obj[init]();
			} else
				Services.scriptloader.loadSubScript('chrome://user_chrome_files/content/custom_scripts/'+ js, win);
			return true;
		} catch(e) {} return false;
	}

………………
	// подключить внешние скрипты - сначала глобальные функции
	var jscripts = [["ucf_global_win.js", globalThis], ["ucf_mousedrag.js"], ["ucf_BookmarkDir.js"], ["ucf_hookClicks.js"], ["ucf_autohidetabstoolbar.js"], ["ucf_LocationBarEnhancer.js"], ["ucf_contextsearch.js"], ["ClickPicSave.jsm"], ["UCFTitleChangedChild.jsm", this, "registerUCFTitleChanged"]];
	for (i = 0; i < jscripts.length; i++) loadscript(jscripts[i][0], jscripts[i][1], jscripts[i][2]);

Dobrov
Даже просто наличие этого загрузчика в custom_script_win.js, с закомментированным импортом, как минимум ломает стили окна.

sandro79
Ваш комплект, безусловно, великолепен. Скажите, пожалуйста, какой стиль или скрипт добавляет эти два пункта и как их удалить? Поиском по открыть страницу не нашел. Edge у меня нет, IE не использую.
cont.png
Еще, вроде, если бы кнопка звука имела индикацию нажатия в виде перечеркивания красной линией, или становилась бы красной, было бы визуально весьма привлекательно.

shadow_user пишет

какой стиль или скрипт добавляет эти два пункта и как их удалить?

Это скрипт contextmenuopenwith.js

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

Да, я тоже уже об этом задумывался, но своими силами добавить индикацию конечно не смогу. Не знаю, может Dumby попросить добавить, если можно.
Dumby
А нельзя ли добавить в этот скрипт индикацию активности кнопки? Если конечно не сильно это муторно.
Может перечёркнутый значок chrome://global/skin/media/audio-muted.svg использовать.
Этот скрипт я собирал из кода из add_toolbar_buttons, надо было там id наверно сменить на другой.

sandro79 пишет

индикацию активности кнопки

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

Dumby пишет

индикацию активности кнопки

Слева отжата, звук включен, справа нажата, звук выключен, или вместо перечеркивания иконка меняет цвет на красный. Так будет визуально видно положение кнопки отжата\нажата.
on-off.png

shadow_user пишет

Слева отжата, звук включен, справа нажата, звук выключен

Звук в(ы)ключен где?

будет визуально видно положение кнопки отжата\нажата

То есть будет видно, нажата кнопка чётное количество раз, или нечётное.
Это понятно, но причём здесь звук? И причём здесь «активность кнопки»,
у кнопки нет никакой активности, она просто переключает по клику некий
на тот момент расклад, вот и всё.

Dumby пишет

Это понятно, но причём здесь звук?

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

shadow_user пишет

Кнопка управляет звуком активной вкладки

Управляет, но не монопольно же. Звук может быть переключён как угодно,
от через контекстное меню вкладки, и до любым другим сторонним кодом.
То есть звук и кнопка не имеют жёсткой связи.


Ладно, вместо «добавить в этот скрипт индикацию активности кнопки»,
сформулируем так: вывести на кнопку индикацию muted-состояния активной вкладки,
если muted, то иконка перечёркнутая, иначе неперечёркнутая. Это можно попробовать.

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

Выделить код

Код:

(async self => CustomizableUI.createWidget(self = {
	label: "Переключить звук",
	tooltiptext:
		"ЛКМ: Переключить звук в выделенных вкладках\n" +
		//"СКМ: Закрыть другие вкладки с источником звука\n" +
		"СКМ: Закрыть другие вкладки «ВОСПРОИЗВОДИТСЯ» и «БЕЗ ЗВУКА»\n" +
		"ПКМ: Переключить звук во всех вкладках",
	imgs: [
		"url(chrome://global/skin/media/audio.svg)",
		"url(chrome://global/skin/media/audio-muted.svg)"
	],
	id: "b-sound-muted-all-tabs",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn.onclick = this.click;
		btn.toggleAttribute("context");

		var win = btn.ownerGlobal, gb = win.gBrowser;
		btn.muted = gb.selectedTab.muted;
		this.setImg(btn);

		var tc = gb.tabContainer;
		var args = ["TabAttrModified", e => this.tam(e, btn)];

		tc.addEventListener(...args);
		win.addEventListener("unload",
			() => tc.removeEventListener(...args)
		, {once: true});
	},
	setImg: btn => btn.style.setProperty(
		"list-style-image", self.imgs[+btn.muted], "important"
	),
	tam(e, btn) {
		if (e.target.selected) {
			var arr = e.detail.changed;
			if (arr.includes("selected") || arr.includes("muted"))
				btn.muted ^ (btn.muted = e.target.muted) && this.setImg(btn);
		}
	},
	click(e) {
		var gb = this.ownerGlobal.gBrowser;
		if (e.button == 0)
			gb.toggleMuteAudioOnMultiSelectedTabs(gb.selectedTab);
		else if (e.button == 1)
			gb.visibleTabs.filter(self.f1).forEach(gb.removeTab, gb);
		else if (e.button == 2)
			for(var tab of gb.selectedTab.activeMediaBlocked
				? gb.visibleTabs.filter(self.f2)
				: gb.visibleTabs.filter(self.f3, gb.selectedTab.linkedBrowser.audioMuted)
			)
				tab.toggleMuteAudio();
	},
	f1: tab => !tab.selected && (tab.muted || tab.soundPlaying),
	f2: tab => tab.activeMediaBlocked || tab.linkedBrowser.audioMuted,
	f3(tab) {
		return tab.linkedBrowser.audioMuted == this && !tab.activeMediaBlocked
			|| tab.activeMediaBlocked && this
	}
}))();

Как в скрипте Контекстный поиск переместить меню «Искать в…» выше? (например перед строкой «Добавить в Заладки»)


Firefox.png


_zt пишет

Даже просто наличие этого загрузчика в custom_script_win.js, с закомментированным импортом, как минимум ломает стили окна.

Как это происходит? Мне не удалось воспроизвести: «как минимум ломает стили окна», но всё-же поправил код  в своём сообщении.

Dobrov пишет

переместить меню «Искать в…» выше? (например перед строкой «Добавить в Заладки»)

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

Выделить код

Код:

…
                //searchSelect.before(menu);
                document.getElementById("context-bookmarklink").before(menu);

Как это происходит? всё-же поправил код

Уж не знаю что поправил, но запятая в конце первой части кода всё ещё торчит.
А происходит это так: парсер видит, что после этой запятой идёт совсем не то,
что ожидалось, выдаёт «SyntaxError: missing variable name», и весь код встаёт враскоряку.

Dumby пишет

сформулируем так: вывести на кнопку индикацию muted-состояния активной вкладки,
если muted, то иконка перечёркнутая, иначе неперечёркнутая. Это можно попробовать.

Спасибо, самое оно!

Dobrov пишет

Как это происходит?

Будет явно заметно с VitaliyVstyle full_theme + Windows 7, но уверен, что и на 10-11 имеется смещение рамки окна. Что  приведет к ошибкам при правке стилей.
   
"Поправка" ничего не исправила.

Dumby - спасибо за помощь с меню поиска!

_zt - не знаю, на каком custom_script_win.js вы проверяете, но я брал загрузчик от Виталия, где фрагмент кода var loadscript также завершался запятой!

Выделить код

Код:

// Загрузчик для custom_script_win.js https://forum.mozilla-russia.org/viewtopic.php?pid=788301#p788301

	var loadscript = (js, win = this, init) => { try {
		if (/\.jsm$/i.test(js)) { // скрипт js или jsm [инициализация]
				var obj = ChromeUtils.import('chrome://user_chrome_files/content/custom_scripts/'+ js, win);
				init && obj[init]();
			} else
				Services.scriptloader.loadSubScript('chrome://user_chrome_files/content/custom_scripts/'+ js, win);
			return true;
		} catch(e) {} return false;
	},

	load_scripts_by_url = {
		browser: win => {
			//>>>>>>>>>>| Этот блок требуется для боковой панели и др., очистите строку ниже если он нужен |>>>>>>>>>>
			/*
			var box = document.querySelector("#browser") || window;
			var listener = e => {
				var doc = e.target || ({});
				load_scripts_by_url[doc.documentURI]?.(doc.defaultView);
			};
			box.addEventListener("pageshow", listener);
			this.loadscriptswinandsidebar = {
				destructor() {
					box.removeEventListener("pageshow", listener);
				}
			};
			this.unloadlisteners.push("loadscriptswinandsidebar");
			/* <<<<<<<<<<<<<<<<<<<< */

			setTimeout(() => { //>>>>>>>>>>| Загрузка скриптов для browser.xhtml |>>>>>>>>>>

			// подключить внешние скрипты - сначала глобальные функции
			var jscripts = [["ucf_global_win.js", globalThis], ["ucf_win_contextmenuopenwith.js"], ["ucf_mousedrag.js"], ["ucf_QuickToggle.js"], ["ucf_BookmarkDir.js"], ["ucf_hookClicks.js"], ["ucf_autohidetabstoolbar.js"], ["ucf_LocationBarEnhancer.js"], ["ucf_contextsearch.js"], ["ucf_findbarclose.js"], ["ucf_tab-update.js"], ["ucf_cooks-pass.js"], ["ClickPicSave.jsm"], ["UCFTitleChangedChild.jsm", this, "registerUCFTitleChanged"]]; // auto_hide_sidebar.js
			for (i = 0; i < jscripts.length; i++)
				loadscript(jscripts[i][0], jscripts[i][1], jscripts[i][2]);

			//<<<<<<<<<<<<<<<<<<<<
			}, 0);

		},
		//>>>>>>>>>>| Загрузка скриптов для др. документов |>>>>>>>>>>
		"chrome://browser/content/places/bookmarksSidebar.xhtml": win => {
			// боковая панель закладок
		},
		"chrome://browser/content/places/historySidebar.xhtml": win => {
			// боковая панель истории

		},
		//<<<<<<<<<<<<<<<<<<<<
	};
	load_scripts_by_url.browser(window);

// END Загрузчик для custom_script_win.js

Dobrov

Dobrov пишет

не знаю, на каком custom_script_win.js вы проверяете

На актуальном.
   

Dobrov пишет

завершался запятой!

Я ничего не говорил про запятые.
   

Dobrov пишет

брал загрузчик от Виталия

Однако его загрузчик работает без подобных ошибок. Хотя, у меня давно уже весь импорт в CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm, но его загрузчик до сих пор в custom_script_win.js присутствовал.
   
ps^ Теперь нормально. Правда проверил только на одном скрипте.
ps2^ Нефига не нормально, точки ошибку вызывают. Зачем вы их туда добавили? И запятая, если ее вернуть, тоже окно перекашивает.

Dobrov пишет

фрагмент кода var loadscript также завершался запятой!

Нет. Фрагмент кода начинается инструкцией var, которая завершается точкой с запятой;
А фрагмент loadscript завершается скобкой } которой заканчивается стрелочная функция.
И уже только затем идёт запятая, которая означает, что далее будет определена ещё одна переменная,
в данном случае load_scripts_by_url


_zt пишет

точки ошибку вызывают. Зачем вы их туда добавили?

Это не точки, а троеточия ("\u2026", HORIZONTAL ELLIPSIS).
И если, например, увидеть, что запятая в код попала по недоразумению,
вполне себе может представляться затруднительным, то


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

Dumby
Вот эта очевидность и ломает стили окна.

Есть кнопка для СВ, отображающая расход оперативной памяти, индикатор расположен в адресной строке (код во вкладке "Инициализация")

Memory Indicator

Выделить код

Код:

(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}" align="center"><label id="${id += "-label"}"/></hbox>`
		);
		this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

		// Bug 1665318 - In about:processes refresh, ResidentUniqueDistinguishedAmount is slow (Firefox 94+)
		// https://bugzilla.mozilla.org/show_bug.cgi?id=1665318
		parseInt(Services.appinfo.platformVersion) < 94 && Object.assign(this, eval(
			`({${this.notify}})`.replace("memory", "residentSetSize").replace("memory", "residentUniqueSize")
		));

		(this.observe = async win => {
			this.timer.cancel();
			await new Promise(ChromeUtils.idleDispatch);
			var ind = win.document.importNode(df, true);
			win.document.getElementById("star-button-box").after(ind);
			this.notify();
		})(win);
	},
	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");


Работает крайне нестабильно. Индикатор то отображается, то пропадает после перезапуска браузера, через несколько сессий опять появляется...
Dumby, если можно, перепишите, пожалуйста, под UCF. Спасибо большое :beer:

Помогите, пожалуйста, сделать пункты «открыть ссылку в {другой программе}».

Скачал user_chrome_files.
Распаковал, что и куда там сказано.
Нашёл contextmenuopenwith.js, но нашёл только здесь, я не знаю, правильно ли. Но других вариантов не нашёл.
В общем, тот код вставил в contextmenuopenwith.js, кинул файл в профиль\chrome\user_chrome_files\custom_scripts.
В CustomStylesScripts.jsm добавил  { path: "contextmenuopenwith.js", ucfobj, true, }, в секцию     scriptschrome: { load: [
Зашёл в настройки about:user-chrome-files, включил там стили и скрипты, проставил все галочки в стилях-скриптах.
Перезапустил лиса с очисткой startup cache.

Сами UCF работают, например, есть три ненужных мне панели, да и в about:user-chrome-files иначе было бы не зайти. Но меню открытия в других программах нету.

Wave
Я вам все расписал уже.
   
Если не работает, значит что-то сделали не правильно.
   
Скрипт вы нашли правильный.

_zt, я пошагово написал, что и как я делаю. Полностью совпадает с вашей инструкцией.

Wave
UCF для тех кто хотя бы может правильно отредактировать JavaScript и CSS,
а не с ошибками синтаксиса как у вас

Wave пишет

В CustomStylesScripts.jsm добавил  { path: "contextmenuopenwith.js", ucfobj, true, },

ucfobj: true

Vitaliy V., спасибо, заработало.

Vitaliy V. пишет

UCF для тех кто хотя бы может правильно отредактировать JavaScript и CSS

В теме, в которой мне посоветовали UCF, я писал:

Окей, по поводу UCF перехожу в ту тему. К слову, мне не надо «очень много полезного», я ищу одну конкретную функцию открытия ссылок в других браузерах или видеоплеере. В XUL-фоксе это можно было сделать одним аддоном, в первых WE-лисах двумя или тремя файликами, брошенными в /chrome, но тогда я не выдержал и откатился на XUL, а сейчас то решение не работает, не знаю почему. Может, что-то потерял. А вот это вот UCF-решение, оно громоздкое, избыточное (распаковывать файлы не только в chrome, но и в firefox, редактировать их и так далее), — так ещё и пока что не получилось заставить его работать.
Либо ставить расширение, ставить питон, ставить питоновский скрипт. Тоже избыточно, блин.

Я же не виноват, что простейшее и конкретное действие в квантуме можно организовать только будучи изрядно красноглазым. Дайте мне простой аддон, который можно поставить, залезть в его настройки и всё — и я не буду пользоваться UCF.

Wave пишет

А вот это вот UCF-решение, оно громоздкое, избыточное

Я не согласен с этим, что там такого громоздкого, а то что лишнее отключается в настройках и на работу браузера никак не влияет.

Распаковывать файлы по разным местам, редактировать конфиги вручную, вручную добавлять скрипты, которые или где-то находить, или писать самому, чистить startup cache, что там ещё. Вы сами сказали — это не для всех.
И вот я всё это худо-бедно сделал. Окей. Через год, когда придёт пора обновлять esr, надо будет либо вспоминать, что это я делал, либо искать (опять) все эти инструкции. Хорошо, если в бэкапе оно всё будет и легко на него наткнусь. Но ведь несколько лет назад, когда только вышел квантум, я попробовал на него перейти, неудачно — откатился, тогда тоже нашлось простенькое решение из двух или трёх файлов, которое надо было распаковать в каталог chrome, и оно работало — я ж это решение тоже тогда закатал в бэкап. Ну так с тех пор у меня ноут сменился, и то решение больше не работает, а я не знаю, почему, то ли потому, что девяностый огнелис этого уже не позволяет, то ли я что-то пропустил, потерял где-то. А сами те файлики в сети уже тю-тю, 404.
Я очень люблю программы-комбайны, которые можно как угодно настраивать, но очень не люблю для какой-то простейшей функции ставить комбайн, у которого ничего кроме этой функции не использовать.

Wave пишет

но очень не люблю для какой-то простейшей функции ставить комбайн

Ну так это не такая уж простейшая функция вызов внешних приложений, в расширениях этого теперь точно не будет
без ещё куда более громоздкого и не универсального решения в виде установки приложения для ОС и оно ещё должно в фоне постоянно работать.

Vitaliy V. пишет

Wave пишетно очень не люблю для какой-то простейшей функции ставить комбайнНу так это не такая уж простейшая функция вызов внешних приложений, в расширениях этого теперь точно не будет без ещё куда более громоздкого и не универсального решения в виде установки приложения для ОС и оно ещё должно в фоне постоянно работать.

async_run_applications.2021.9.7.xpi умеет вызывать внешние программы и передавать им текущий адрес или адрес из буфера обмена. Правда, тоже требует некоторых телодвижений в каталоге установленного фаерфокса.
https://addons.mozilla.org/ru/firefox/addon/potplayer-youtube-shortcut/ — вот это, почему-то, вызывает внешнее приложение, правда, всего одно — PotPlayer. Не требует установки в систему чего-то типа питона. Находится на AMO. Работает в [firefox] 91 esr. Не требует даже отключать проверку подписей расширений.
Как так?
.
К слову, примерно на втором месте после скрапбука мне из xul-расширений жаль It'sAllText, которое передавало содержимое textarea во внешний редактор, а по сохранению файла обновляло содержимое textarea. WE-аналоги нынче требуют сервер, оформленный в виде плагина для вима, саблайма или любого другого редактора, и чтобы точно так же он был запущен. Почему нельзя сделать по аналогии с potplayer-youtube-shortcut? Или хотя бы как скрипт к UCF (но это надо чтобы кто-то сделал, сам я вряд ли смогу)?

Wave пишет

async_run_applications.2021.9.7.xpi умеет вызывать внешние программы и передавать им текущий адрес или адрес из буфера обмена

Ну да мое расширение может но оно WebExtensions Experiments, для ознакомления https://firefox-source-docs.mozilla.org … xperiments
И его нельзя подписать или выложить на АМО и конечно такое апи не добавят в [firefox]

Wave пишет

https://addons.mozilla.org/ru/firefox/addon/potplayer-youtube-shortcut/ — вот это, почему-то, вызывает внешнее приложение, правда, всего одно — PotPlayer

Не пользуюсь [windows] и PotPlayer, но судя по коду расширения оно добавляет ссылку в виде
potplayer://ссылка, т.е. видимо сам PotPlayer регистрирует новый протокол potplayer: в [windows]
Короче опять же не универсальное решение.

Wave
"Открыть страницу в..." у меня заработало без дополнительных правок, на предыдущей странице я спрашивал, как его убрать https://forum.mozilla-russia.org/viewto … 68#p797568

Очевидно, что ты скачал где-то в другом месте набор, в котором этот скрипт уже был и был подключен. Я же сначала нагуглил скрипт, потом по цепочке выяснил, что подключить его можно через UCF, потом сам пакет UCF, потом выяснил, где и как в нём этот скрипт включить. И шёл я от темы про userChrome.css, потому что несколько лет назад было выяснил, что данную функцию можно задействовать через chrome. И что такое UCF вообще, не сразу выяснил.

Wave
Любезно поделился https://forum.mozilla-russia.org/viewto … 31#p797431 уважаемый мастер sandro79

Viatcheslav пишет

Есть кнопка для СВ

Это что ещё за фантазии?
Приведённый код как раз именно для UCF (для custom_script.js).

Dumby пишет

Приведённый код как раз именно для UCF (для custom_script.js)

Благодарю за просветление :sick: Вроде, нашёл его в теме для СВ, ну да ладно :blush:

Fx 91.4.1 ESR

Можно ли с помощью UCF переместить findbar в нижнюю панель (#browser-bottombox) и
сделать так, чтобы он занял её полностью и отображался постоянно, даже после перезапуска браузера?
 
Дело в том, что если:
privacy.resistFingerprinting;true
privacy.resistFingerprinting.letterboxing;true
то, при появлении findbar, уменьшается viewport — появляются серые полосы сверху и снизу.

viewport.1642889108.png
Для уведомлений помогает стиль: display:block и position:fixed,
а с findbar’ом не получается. Т.е., он отображается поверх содержимого страницы,
но серые полосы всё-равно появляются.
Если подвинуть findbar с помощью margin, то нижнюю серую полосу удаётся убрать, но верхняя полоса остаётся.
 
P.S. Из «Настройки внешнего вида…» меня изгнали, стало быть нужен именно скрипт.

negodnik пишет

переместить findbar

Ты так говоришь, как будто он один на всё окно,
а не для каждой вкладки свой, отдельный.

уменьшается viewport — появляются серые полосы

Если не нравятся серые полосы зачем тогда включать letterboxing :/
Вот этот стиль не образует ресайз, вдруг подойдёт.

изгнали, стало быть нужен именно скрипт

Не слишком ли надумано?
Написано «UCF», а для него есть отдельная тема.
Вот и всё, ничего более.

Ищу скрипты для custom_script.js- «Показать весь журнал» и «открыть about:config»

doud пишет

«Показать весь журнал» и «открыть about:config»

user_chrome_files

kokoss
Разве там есть такие кнопки?

Dumby
> Ты так говоришь, как будто он один на всё окно
Это просто моя хотелка. Готов умерить аппетиты. Просто переместить его в нижнюю панель.
Пускай отображается не постоянно и не занимает всю панель. Есть же кнопка.
Если она не сможет работать в нижней панели — ничего страшного.
> Если не нравятся серые полосы зачем тогда включать letterboxing
Надо.

Стиль видел.
Спасибо.

voqabuhe

скрин
09cf7513771d.png

kokoss,СКМ открывать не удобно, надо чтобы открывала по ЛКМ

doud пишет

Ищу скрипты для custom_script.js- «Показать весь журнал» и «открыть about:config»

Два в одном подойдёт?

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

Выделить код

Код:

try {
CustomizableUI.createWidget({
	id: "ucf_ShowHistory_AboutConfig",
	type: "custom",
	label: "Показать журнал / about:config",
	tooltiptext: [
		"ЛКМ: Показать журнал",
		"ПКМ: about:config"
	].join("\n"),
	// defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onBuild(doc) {
		var win = doc.defaultView;
		var trbn = doc.createXULElement("toolbarbutton");
		trbn.id = this.id;
		trbn.tooltipText = this.tooltiptext;
		trbn.label = this.label;
		trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
		trbn.setAttribute("context", false);
		trbn.setAttribute("image", "chrome://browser/skin/history.svg");
		trbn.addEventListener("click", function(e) {
			if (e.button == 0) {
					e.preventDefault();
					e.stopPropagation();
					win.SidebarUI.toggle("viewHistorySidebar");
			}
			else if (e.button == 2) {
				win.switchToTabHavingURI("about:config", true, {
					relatedToCurrent: true,
					triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
				});;
			}
		}, false);
		return trbn;
	},
});
} catch(e) {}

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

doud пишет

хотелось бы в новом окне или в новой вкладке

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


А пока попробуйте в коде заменить строку:
win.SidebarUI.toggle("viewHistorySidebar");

на строку:
win.PlacesCommandHook.showPlacesOrganizer("History");


Возможно, что вам такой вариант подойдёт.

Спасибо, отлично получилось открывает в новом окне

negodnik пишет

его

их


Вообще, вроде можно там findbar-склад устроить.
Надо смотреть, не заглючит ли чего.
Код для custom_script_win.js

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

Выделить код

Код:

(async uriStr => {
	var attr = "current";
	await delayedStartupPromise;
	gBrowser.browserBottomBox = document.getElementById("browser-bottombox");
	Object.assign(gBrowser, eval(`({${gBrowser._createFindBar}})`
		.replace(/\/\/.+?\);/s, "this.browserBottomBox.append(findBar);")
		.replace("return f", `aTab.selected && findBar.toggleAttribute("${attr}");\n      $&`)
	));
	windowUtils.loadSheetUsingURIString(uriStr.replace("A", attr), windowUtils.USER_SHEET);

	var arr = [["TabSelect", e => {
		e.target._findBar?.toggleAttribute(attr);
		e.detail.previousTab._findBar?.removeAttribute(attr);
	}], ["TabClose", e => e.target._findBar?.remove()]];

	var tc = gBrowser.tabContainer;
	for(var args of arr) tc.addEventListener(...args);
	var id = Symbol(), ucf = ucf_custom_script_win;
	ucf.unloadlisteners.push(id);
	ucf[id] = {destructor: () => arr.forEach(args => tc.removeEventListener(...args))};

})("data:text/css,%23browser-bottombox>findbar:not([A]){display:none!important;}");

findbar-склад устроился, но и полосы не пропали. Странный этот letterboxing.
Полосы появляются, даже если изменить высоту панелей, и не пропадают после перезапуска.
И с вышеупомянутым стилем Vitaliy V. не пропадают, даже если оставить в userChrome только этот стиль.
В общем, если нужную высоту панелей не угадаю, буду жить с полосами.
Спасибо за помощь.

del

Farby пишет

После подклучкния user_chrome_files у меня пропала способность открывать Menu bar путем нажатия клавиши Alt, можно ли включить это обратно?

Если бы это было так то уже бы другие пользователи об этом сообщили, и у мненя на [linux] это работает. Скорее всего проблема у вас в другом стиле или скрипте.

Farby пишет

передачи URL из контекстного меню в приложение путём расширение async_run_applications

Да я собирался сделать, хотя мне не нужны пункты для вызова приложений в контекстном меню особенно когда их много,
обычно когда мне нужно скачать по ссылке я копирую ее адрес в буфер обмена
и вызываю пункт меню кнопки async_run_applications для вызова с аргументом %OpenClipboardURI из буфера обмена.
Ну или есть скрипт https://forum.mozilla-russia.org/viewto … 54#p782454

del.

Vitaliy V. или Dumby - проблема с скриптом Контекстный поиск - при клике или выборе строк ничего не происходит.


Не работает на версии Firefox 91.5 и выше на МакОС, при этом на Linux скрипт работает без проблем на версиях от 80 до новейшей.
На Firefox 84.0.2 под МакОС работает, на версии Firefox 91.5 в контекстном меню создаётся подменю поиска, но при клике на любой из строк ничего не происходит. В консоли также никаких ошибок не появляется, но страница поиска не открывается. Подключал или в custom_script_win.js или в CustomStylesScripts.jsm, меню поиска в контекстном меню создаётся, но пункты меню не работают:

Выделить код

Код:

scriptschrome: { // Для докум. окна браузера [ChromeOnly]
		domload: [ // По событию "DOMContentLoaded"
      { path: "ucf_contextsearch.js", ucfobj: true, },

Как исправить работу скрипта Контекстный поиск для Firefox 90+ ??? (т. к. некоторые скрипты Dumby делал только под новый Firefox)

Dobrov
https://forum.mozilla-russia.org/viewto … 54#p782454

Dobrov
Там же по умолчанию включено нативное контекстное меню, отключи
widget.macos.native-context-menus - false
так хоть стиль для меню можно использовать, мой кстати работает на первый взгляд.
А иначе только на ...addEventListener("command", ... реагирует и только на пункты в подменю menuitem
на menu не срабатывает там где дефолтный поиск

Кстати, заметил что там небольшой кусочек замысла
потерялся (возвращаемое значение), пустяк конечно, но всё же.

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

Выделить код

Код:

/*
                this.handler = ev => {
                    if (ev.target != popup) return;
                    menu.hidden = searchSelect.hidden;
                };
                this.handlerRebuild = () => this.handler(e) || this.rebuild(menu);
*/
                this.handler = e => e.target != popup || (menu.hidden = searchSelect.hidden);
                this.handlerRebuild = e => this.handler(e) || this.rebuild(menu);


И ещё случайно наткнулся на такой момент:
searchSelect.collapsed = true; — скрывает пункт,
но в клавиатурной навигации он продолжает участвовать.


То есть, когда searchSelect не hidden, и стрелками клавиатуры
перемещаешься по пунктам #contentAreaContextMenu вверх-вниз,
то оно на нём как-бы запинается, пробуксовывает.


Если написать searchSelect.style.setProperty("display", "none", "important");
то нормально.

Dumby пишет

кусочек замысла
потерялся (возвращаемое значение), пустяк конечно, но всё же.

Ок, поправил это похоже после последней правки упустил когда проверку e.target != popup добавлял
которой кстати нет в первоначальном варианте

Dumby - доработал твой код, исправил неудобство кнопки «Быстрое переключение параметров about:config», которое есть во всех примерах форума:
Флажок строки под-меню не выбирается, если параметр сброшен Правым кликом по строке меню (отсутствует в настройках).


Теперь поведение выбора строк подменю одинаковое для параметров по-умолчанию, независимо от того, есть они в about:config или нет (сброшены). Как пример, в коде «Автовыбор значений по-умолчанию» включен в опциях: Загрузки, Многопоточный режим вкладок, User Agent.
Например, к строке ЮзерАгент добавляется всего лишь такой код: [ua, "встроенный"].


Скрипт брать из демо-профиля шапки темы, так как код скрипта сокращён и зависит от двух других: win_global.js и ucf_hookClicks.js.
ранее Dumby делал «Автовыбор значений по-умолчанию», но способ тащил за собой костыль в 30 строк кода на каждый пункт меню.

Dobrov пишет

Флажок строки под-меню не выбирается, если параметр сброшен Правым кликом по строке меню

Всё вроде выбирается. Код кнопки у меня отсюда.
+ изменения раз, два.

xrun1 пишет

Всё вроде выбирается. Код кнопки у меня отсюда.
+ изменения раз, два.

Такая же фигня на 91 esr.
 
Dobrov
Просто вы понакрутили дров в своем скрипте, так что он работает не так как ожидалось. Или использовали на 78esr те фиксы которые для версий выше предназначались.
Оригинальный же, от Dumby, даже с правками, так себя не ведет. Ссылки на использованные фиксы искать лень, но что-то брал и из вашего с ним обсуждения.

Vitaliy V. — насколько я понял, в CustomStylesScripts.jsm нет возможности подключать jsm-скрипты ?
Сделайте в новой версии UserChromeFiles подключение не только js но и jsm-скриптов. Пока поправил user_chrome.js так:

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
		{ path: "custom_script.js", },
………………
		{ path: "ClickPicSave.jsm", },

if (s.path) // правка в user_chrome.js
	if (/\.jsm$/i.test(s.path))
		ChromeUtils.import(`chrome://user_chrome_files/content/custom_scripts/${s.path}`)
	else
		Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${s.path}`, scope);
_zt пишет

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

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


xrun1 пишет

Всё вроде выбирается.

_zt и xrun1 - Неверно! Я тоже тестировал все версии кода! Вот код xrun1 со всеми правками:

но сброшенная опция "Многопоточный режим вкладок" не выбирается!

Выделить код

Код:

// Быстрое переключение параметров about:config
(async (name, id, func) => {
	if (name == "Object") return CustomizableUI.createWidget(func());
	var win = name == "Window", g = Components.utils.import("resource://gre/modules/Services.jsm", {});
	if (g[id]) {if (win) return;} else g[id] = func();
	if (win) return CustomizableUI.createWidget(g[id]);
	addDestructor(r => r[5] == "e" && delete g[id]);
	g[id].onCreated(this);
})(this.constructor.name, "QuickToggleAboutConfigSettings", () => {

	var {prefs} = Services, db = prefs.getDefaultBranch("");
	var pv = parseInt(Services.appinfo.platformVersion);
	var xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

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

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

	var primary = [{

			pref: ["network.proxy.type", "Настройки прокси"],
			userChoice: 5, userAlt: 1, refresh: true,
			values: [
				[0, "Не проксировать", "0"], [5, "Системные (из IE)", "5"], [2, "Авто (pacfile)", "2"],
				[1, "Прописанные", "1"], [4, "Автоопределение", "4"]
	]},
			null,
	{
			pref: ["permissions.default.image", "Загружать графику"],
			userChoice: 1, refresh: true,
			values: [[1, "Да"], [3, "С сайта"], [2, "Нет"]]
	},{
			pref: ["browser.display.use_document_fonts", "Загружать web-шрифты"],
			userChoice: 1, refresh: true,
			values: [[1, "Да"], [0, "Нет"]]
	},{
			pref: ["javascript.enabled", "Выполнять скрипты Java"],
			userChoice: true, refresh: true,
			values: [[true, "Да"], [false, "Нет"]]
	},{
			pref: ["media.autoplay.default", "Автозапуск медиа"],
			userChoice: 5, refresh: true,
			values: [
				[5, "Блокировать все", "5"],
				[1, "Блокировать не приглушенное", "1"],
				[0, "Разрешить все", "0"]
	]},{
			pref: ["media.autoplay.blocking_policy", "Автозапуск (политика)"],
			userChoice: 1, userAlt: 2, refresh: true,
			values: [
				[1, "Временная", "1"],
				[2, "По действию", "2"],
				[0, "Постоянная", "0"]
	]},{
			pref: ["network.cookie.cookieBehavior", "Cookies"],
			userChoice: 1, userAlt: 3, refresh: false,
			values: [
				[1, "Не принимать сторонние"], [3, "Не принимать с не посещенных"], [4, "Не принимать от трекеров"],
				[2, "Не принимать со всех"], [0, "Принимать со всех"]
	]},
			null,
	{
			pref: ["dom.storage.enabled", "Локальное хранилище"],
			userChoice: true
	},{
			pref: ["browser.tabs.remote.force-enable", "Многопоточный режим вкладок"],
			userChoice: true, userAlt: false,
			values: [[true, "Да"], [false, "Нет"]]
	}
];

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

	var secondary = [{

			pref: ["dom.serviceWorkers.enabled", "Видео dom.serviceWorkers"],
			userChoice: false
	},{
			pref: ["dom.enable_performance", "Статус загрузки страницы"],
			userChoice: false
	},
			null,
	{
			pref: ["browser.cache.memory.enable", "Кэш в оперативной памяти"],
			userChoice: true
	},
			null,
	{
			pref: ["intl.accept_languages", "Язык для веб-страниц"],
			userChoice: "en-US, en",
			values: [["en-US, en", "en-US, en"], ["en-US, en, ru-RU, ru", "en-US, en, ru-RU, ru"]]
	},{
			pref: ["browser.display.document_color_use", "Использовать цвета сайтов"],
			userChoice: 0,
			values: [[0, "Авто", "0"], [1, "Всегда", "1"], [2, "Никогда", "2"]]
	},
			null,
	{
			pref: ["network.http.sendRefererHeader", "Referer - для чего"],
			userChoice: 1,
			values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки и изобр.", "2"]]
	},{
			pref: ["network.http.referer.trimmingPolicy", "Referer - что"],
			userChoice: 0,
			values: [[0, "Полный URL", "0"], [1, "scheme+host+port+path", "1"], [2, "scheme+host+port", "2"]]
	},{
			pref: ["network.http.referer.XOriginPolicy", "RefererXO - когда"],
			userChoice: 0,
			values: [[0, "В любом случае", "0"], [1, "При совп. баз. домена", "1"], [2, "При совпадении адреса", "2"]]
	},{
			pref: ["network.http.referer.XOriginTrimmingPolicy", "RefererXO - что"],
			userChoice: 0,
			values: [[0, "Полный URL", "0"], [1, "scheme+host+port+path", "1"], [2, "scheme+host+port", "2"]]
	},{
			pref: ["network.http.referer.spoofSource", "Referer - корень сайта"],
			userChoice: false
	},
			null,
	{
			pref: ["media.peerconnection.enabled", "WebRTC утечка IP"],
			userChoice: false
	}
	];

	return {
		label: "Quick toggle",
		id: "QuickToggleAboutConfigSettings",
		localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFPSURBVDhPnVPNSsNAEJ5tamnBk+A7BMEcikohp/gi9i18nRafQoT00kK16tnn8CJo4vp9m5m41eSgH0zmd3d+diJ/gfdedvl0QW7k1PcLwem+3TjowZagK+Nn6yc3gNCJ+LCCh9LzzTMdqeoNbkQSFZmpJLGCW5Gx2hZaQahMbZ52VuDSfHpnAVJXOb4FKzianbzCXiLrHLYl5C3t5NTRwtxaKJxUTZ/JcOTr6q2Rxwf0aVb2/hLs4KqL3Isckz/mmSdRRksjyg+z7IM6LzDCBfuvAEOJmIKBOqAWmo1YodxLlQNsyO0rsIV/AZM+JGc2a4GgzhZ2p9mFmrpboCMqNYBDdMlwompoTWN6F2nFT5hBXb0PEpmo/Gk+oH+RrqNZINPeIlnZViV1jQuLFJQuWGB0gJdwgShvqdPXZv6J+F/QS7oXqQ9xBTFZ5oa8fAGs9fed5YhPSwAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

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

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

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

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

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

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

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

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

			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);
		}
	};
});

Dobrov пишет

насколько я понял, в CustomStylesScripts.jsm нет возможности подключать jsm-скрипты ?

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
		{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/xxxxxxxx.jsm");', },
Dobrov пишет

но сброшенная опция "Многопоточный режим вкладок" не выбирается!

Что я понимаю не так? [firefox] 97.0 Добавил опцию в свой файл, т.к. не пользуюсь и сделал видео (лежит на ЯД). ЛКМ - красный, ПКМ - синий.

Перелопатил форум на предмет скрита auto_hide_sidebar.uc.js, оказывается для userChromeJS его не так-то просто подключить.
Собрал до кучи из примеров, теперь он автономен и можно просто положит в папку crome с именем auto_hide_sidebar.uc.js или подключить в user_chrome_files, auto_hide_sidebar.css больше отдельно не нужен.

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

Выделить код

Код:

data:application/x-javascript;base64,Ly8gPT1Vc2VyU2NyaXB0PT0KLy8gQG5hbWUJQXV0byBIaWRlIFNpZGViYXIKLy8gQGF1dGhvcglWaXRhbGl5IFYuCi8vIEBpbmNsdWRlCW1haW4KLy8gQHNodXRkb3duCXRoaXMuYXV0b2hpZGVzaWRlYmFyLmRlc3RydWN0b3IoKTsKLy8gQG5vdGUJaHR0cHM6Ly9naXRodWIuY29tL1ZpdGFsaXlWc3R5bGUvVml0YWxpeVZzdHlsZS5naXRodWIuaW8vdHJlZS9tYXN0ZXIvc3R5bGVzZmYvdXNlcl9jaHJvbWVfZmlsZXMvCi8vIEBvbmx5b25jZQovLyA9PS9Vc2VyU2NyaXB0PT0KCih0aGlzLmF1dG9oaWRlc2lkZWJhciA9IHsKCXNpZGViYXI6IG51bGwsCgl0aW1lcjogbnVsbCwKCWRlbGF5OiBudWxsLAoJbGlzdGVuZXI6IG51bGwsCglldmVudHM6IFsiZHJhZ2VudGVyIiwgImRyb3AiLCAiZHJhZ2V4aXQiLCAiTW96TGF5ZXJUcmVlUmVhZHkiLCAibW91c2VlbnRlciIsICJtb3VzZWxlYXZlIl0sCglnZXQgdW5sb2FkbGlzdGVuZXJzKCkgewoJCWRlbGV0ZSB0aGlzLnVubG9hZGxpc3RlbmVyczsKCQl3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigidW5sb2FkIiwgdGhpcywgeyBvbmNlOiB0cnVlIH0pOwoJCXJldHVybiB0aGlzLnVubG9hZGxpc3RlbmVycyA9IFtdOwoJfSwKCWluaXQoKSB7CgkJdmFyIHNpZGViYXIgPSB0aGlzLnNpZGViYXIgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCIjc2lkZWJhci1ib3giKTsKCQlpZiAoIXNpZGViYXIpIHJldHVybjsKCQlmb3IgKGxldCB0eXBlIG9mIHRoaXMuZXZlbnRzKQoJCQlzaWRlYmFyLmFkZEV2ZW50TGlzdGVuZXIodHlwZSwgdGhpcyk7CgkJdGhpcy51bmxvYWRsaXN0ZW5lcnMucHVzaCgiYXV0b2hpZGVzaWRlYmFyIik7CgkJdmFyIHBvcHVwID0gdGhpcy5wb3B1cCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoIiNzaWRlYmFyTWVudS1wb3B1cCIpOwoJCWlmICghcG9wdXApIHJldHVybjsKCQlwb3B1cC5hZGRFdmVudExpc3RlbmVyKCJwb3B1cHNob3dpbmciLCB0aGlzKTsKCQlsZXQgc3R5bGUgPSB0aGlzLnN0eWxlCgkJdmFyIHNzcyA9IENjWydAbW96aWxsYS5vcmcvY29udGVudC9zdHlsZS1zaGVldC1zZXJ2aWNlOzEnXS5nZXRTZXJ2aWNlKENpLm5zSVN0eWxlU2hlZXRTZXJ2aWNlKTsKCQlsZXQgbmV3VVJJUGFyYW0gPSB7CgkJCWFVUkw6ICdkYXRhOnRleHQvY3NzLCcgKyBlbmNvZGVVUklDb21wb25lbnQoc3R5bGUpLAoJCQlhT3JpZ2luQ2hhcnNldDogbnVsbCwKCQkJYUJhc2VVUkk6IG51bGwKCQl9CgkJbGV0IGNzc1VyaSA9IFNlcnZpY2VzLmlvLm5ld1VSSShuZXdVUklQYXJhbS5hVVJMLAoJCQkJCQluZXdVUklQYXJhbS5hT3JpZ2luQ2hhcnNldCwKCQkJCQkJbmV3VVJJUGFyYW0uYUJhc2VVUkkpOwoJCXNzcy5sb2FkQW5kUmVnaXN0ZXJTaGVldChjc3NVcmksIHNzcy5BR0VOVF9TSEVFVCk7Cgl9LAoJZGVzdHJ1Y3RvcigpIHsKCQl2YXIgc2lkZWJhciA9IHRoaXMuc2lkZWJhcjsKCQlmb3IgKGxldCB0eXBlIG9mIHRoaXMuZXZlbnRzKQoJCQlzaWRlYmFyLnJlbW92ZUV2ZW50TGlzdGVuZXIodHlwZSwgdGhpcyk7CgkJaWYgKCF0aGlzLnBvcHVwKSByZXR1cm47CgkJdGhpcy5wb3B1cC5yZW1vdmVFdmVudExpc3RlbmVyKCJwb3B1cHNob3dpbmciLCB0aGlzKTsKCX0sCgloYW5kbGVFdmVudChlKSB7CgkJdGhpc1tlLnR5cGVdKGUpOwoJfSwKCU1vekxheWVyVHJlZVJlYWR5KGUpIHsKCQlpZiAoZS5vcmlnaW5hbFRhcmdldD8uaWQgPT0gIndlYmV4dC1wYW5lbHMtYnJvd3NlciIgJiYgIXRoaXMuc2lkZWJhci5oYXNBdHRyaWJ1dGUoInNpZGViYXJkcmFnIikpIHsKCQkJd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlZG93biIsICgpID0+IHsKCQkJCXRoaXMuZHJvcCgpOwoJCQl9LCB7IG9uY2U6IHRydWUgfSk7CgkJCXRoaXMuZHJhZ2VudGVyKCk7CgkJfQoJfSwKCWNsaWNrOiBmdW5jdGlvbihldmVudCkgewoJCXZhciBzaWRlYmFyID0gdGhpcy5zaWRlYmFyOwoJCXZhciBib3hPYmogPSBzaWRlYmFyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLCBib3hTY3JuID0gIXNpZGViYXIuYm94T2JqZWN0ID8gc2lkZWJhciA6IHNpZGViYXIuYm94T2JqZWN0OwoJCWlmIChldmVudC5zY3JlZW5ZIDwgYm94U2Nybi5zY3JlZW5ZIHx8IGV2ZW50LnNjcmVlblk+IGJveFNjcm4uc2NyZWVuWSArIGJveE9iai5oZWlnaHQgfHwgZXZlbnQuc2NyZWVuWCA8IGJveFNjcm4uc2NyZWVuWHx8IGV2ZW50LnNjcmVlblggPiBib3hTY3JuLnNjcmVlblggKyBib3hPYmoud2lkdGgpIHsKCQkJd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdGhpcywgZmFsc2UpOwoJCQl0aGlzLmxpc3RlbmVyID0gZmFsc2U7CgkJCWlmIChzaWRlYmFyLmhhc0F0dHJpYnV0ZSgic2lkZWJhcmRyYWciKSkKCQkJCXNpZGViYXIucmVtb3ZlQXR0cmlidXRlKCJzaWRlYmFyZHJhZyIpOwoJCX0KCX0sCgltb3VzZWVudGVyOiBmdW5jdGlvbigpIHsKCQl2YXIgc2lkZWJhciA9IHRoaXMuc2lkZWJhcjsKCQlpZiAoIXNpZGViYXIuaGFzQXR0cmlidXRlKCJzaWRlYmFyZHJhZyIpICYmICF0aGlzLmxpc3RlbmVyKSB7CgkJCWNsZWFyVGltZW91dCh0aGlzLnRpbWVyKTsKCQkJdmFyIGRlbGF5ID0gdGhpcy5kZWxheSB8fCAodGhpcy5kZWxheSA9ICtnZXRDb21wdXRlZFN0eWxlKHNpZGViYXIpLmdldFByb3BlcnR5VmFsdWUoInRyYW5zaXRpb24tZGVsYXkiKS5yZXBsYWNlKC9bXjAtOVwuXS9nLCAiIikgKiAxMDAwKTsKCQkJdGhpcy50aW1lciA9IHNldFRpbWVvdXQoKCkgPT4gewoJCQkJc2lkZWJhci5zZXRBdHRyaWJ1dGUoInNpZGViYXJkcmFnIiwgInRydWUiKTsKCQkJCXRoaXMubGlzdGVuZXIgPSB0cnVlOwoJCQkJd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgdGhpcywgZmFsc2UpOwoJCQl9LCBkZWxheSk7CgkJfQoJfSwKCW1vdXNlbGVhdmU6IGZ1bmN0aW9uKCkgewoJCWNsZWFyVGltZW91dCh0aGlzLnRpbWVyKTsKCX0sCglwb3B1cHNob3dpbmcoKSB7CgkJdGhpcy5wb3B1cC5hZGRFdmVudExpc3RlbmVyKCJwb3B1cGhpZGRlbiIsICgpID0+IHsKCQkJdGhpcy5kcm9wKCk7CgkJfSwgeyBvbmNlOiB0cnVlIH0pOwoJCXRoaXMuZHJhZ2VudGVyKCk7Cgl9LAoJZHJhZ2VudGVyKCkgewoJCWlmICghdGhpcy5zaWRlYmFyLmhhc0F0dHJpYnV0ZSgic2lkZWJhcmRyYWciKSkKCQkJdGhpcy5zaWRlYmFyLnNldEF0dHJpYnV0ZSgic2lkZWJhcmRyYWciLCAidHJ1ZSIpOwoJfSwKCWRyb3AoKSB7CgkJaWYgKHRoaXMuc2lkZWJhci5oYXNBdHRyaWJ1dGUoInNpZGViYXJkcmFnIikpCgkJCXRoaXMuc2lkZWJhci5yZW1vdmVBdHRyaWJ1dGUoInNpZGViYXJkcmFnIik7Cgl9LAoJZHJhZ2V4aXQoZSkgewoJCXZhciBzaWRlYmFyID0gdGhpcy5zaWRlYmFyOwoJCXZhciBib3hPYmogPSBzaWRlYmFyLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLCBib3hTY3JuID0gIXNpZGViYXIuYm94T2JqZWN0ID8gc2lkZWJhciA6IHNpZGViYXIuYm94T2JqZWN0OwoJCWlmICgoIWUucmVsYXRlZFRhcmdldCB8fCBlLnNjcmVlblkgPD0gKGJveFNjcm4uc2NyZWVuWSArIDUpIHx8IGUuc2NyZWVuWT49IChib3hTY3JuLnNjcmVlblkgKyBib3hPYmouaGVpZ2h0IC0gNSkKCQkJfHwgZS5zY3JlZW5YIDw9IChib3hTY3JuLnNjcmVlblggKyA1KSB8fCBlLnNjcmVlblggPj0gKGJveFNjcm4uc2NyZWVuWCArIGJveE9iai53aWR0aCAtIDUpKQoJCQkmJiBzaWRlYmFyLmhhc0F0dHJpYnV0ZSgic2lkZWJhcmRyYWciKSkKCQkJc2lkZWJhci5yZW1vdmVBdHRyaWJ1dGUoInNpZGViYXJkcmFnIik7Cgl9LAoJc3R5bGU6IGBALW1vei1kb2N1bWVudCB1cmwoImNocm9tZTovL2Jyb3dzZXIvY29udGVudC9icm93c2VyLnhodG1sIikgewoJCQkjc2lkZWJhci1ib3ggewoJCQkJLS12LXNpZGViYXItbWluLXdpZHRoOiAycHg7CgkJCQktLXYtc2lkZWJhci1taW4td2lkdGgtbm9ybWFsOiA1cHg7CgkJCQktLXYtc2lkZWJhci1tYXgtd2lkdGg6IDMwZW07CgkJCQktLXYtc2lkZWJhci10cmFuc2l0aW9uLWRlbGF5LXNob3c6IC4zczsKCQkJCS0tdi1zaWRlYmFyLXRyYW5zaXRpb24tZGVsYXktaGlkZTogLjZzOwoJCQkJLS12LXNpZGViYXItdHJhbnNpdGlvbi1kdXJhdGlvbjogLjNzOwoJCQkgLyogKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAqLwoKCQkJIHBvc2l0aW9uOiByZWxhdGl2ZSAhaW1wb3J0YW50OwoJCQkgei1pbmRleDogMiAhaW1wb3J0YW50OwoJCQkgbWluLXdpZHRoOiB2YXIoLS12LXNpZGViYXItbWF4LXdpZHRoKSAhaW1wb3J0YW50OwoJCQkgd2lkdGg6IHZhcigtLXYtc2lkZWJhci1tYXgtd2lkdGgpICFpbXBvcnRhbnQ7CgkJCSBtYXgtd2lkdGg6IHZhcigtLXYtc2lkZWJhci1tYXgtd2lkdGgpICFpbXBvcnRhbnQ7CgkJCSBvdmVyZmxvdzogaGlkZGVuICFpbXBvcnRhbnQ7CgkJCSBvcGFjaXR5OiAwICFpbXBvcnRhbnQ7CgkJCSAtLXYtc2lkZWJhci1tYXJnaW4tbWF4LXdpZHRoOiBjYWxjKC0xICogdmFyKC0tdi1zaWRlYmFyLW1heC13aWR0aCkpOwoJCQkgLS12LXNpZGViYXItdHJhbnNmb3JtLWxvY2FsZS1kaXI6IC0xOwoJCQkgLS12LXNpZGViYXItdHJhbnNmb3JtLWxvY2FsZS1kaXItdmlzaWJsZTogMTsKCQkJIG1hcmdpbi1pbmxpbmUtc3RhcnQ6IDAgIWltcG9ydGFudDsKCQkJIG1hcmdpbi1pbmxpbmUtZW5kOiB2YXIoLS12LXNpZGViYXItbWFyZ2luLW1heC13aWR0aCkgIWltcG9ydGFudDsKCQkJIGJvcmRlcjogbm9uZSAhaW1wb3J0YW50OwoJCQkgYm9yZGVyLWlubGluZS1lbmQ6IDFweCBzb2xpZCB2YXIoLS1zaWRlYmFyLWJvcmRlci1jb2xvciwgVGhyZWVEU2hhZG93KSAhaW1wb3J0YW50OwoJCQkgdHJhbnNmb3JtOiB0cmFuc2xhdGVYKGNhbGModmFyKC0tdi1zaWRlYmFyLXRyYW5zZm9ybS1sb2NhbGUtZGlyKSAqICh2YXIoLS12LXNpZGViYXItbWF4LXdpZHRoKSAtIHZhcigtLXYtc2lkZWJhci1taW4td2lkdGgpKSkpICFpbXBvcnRhbnQ7CgkJCSB0cmFuc2l0aW9uLXRpbWluZy1mdW5jdGlvbjogbGluZWFyLCBzdGVwLXN0YXJ0ICFpbXBvcnRhbnQ7CgkJCSB0cmFuc2l0aW9uLWRlbGF5OiB2YXIoLS12LXNpZGViYXItdHJhbnNpdGlvbi1kZWxheS1oaWRlKSwgY2FsYyh2YXIoLS12LXNpZGViYXItdHJhbnNpdGlvbi1kZWxheS1oaWRlKSArIHZhcigtLXYtc2lkZWJhci10cmFuc2l0aW9uLWR1cmF0aW9uKSkgIWltcG9ydGFudDsKCQkJIHRyYW5zaXRpb24tZHVyYXRpb246IHZhcigtLXYtc2lkZWJhci10cmFuc2l0aW9uLWR1cmF0aW9uKSwgMHMgIWltcG9ydGFudDsKCQkJIHRyYW5zaXRpb24tcHJvcGVydHk6IHRyYW5zZm9ybSwgb3BhY2l0eSAhaW1wb3J0YW50OwoJCSB9CgkJICNzaWRlYmFyLWJveFtwb3NpdGlvbmVuZD0idHJ1ZSJdIHsKCQkJIG1hcmdpbi1pbmxpbmUtc3RhcnQ6IHZhcigtLXYtc2lkZWJhci1tYXJnaW4tbWF4LXdpZHRoKSAhaW1wb3J0YW50OwoJCQkgbWFyZ2luLWlubGluZS1lbmQ6IDAgIWltcG9ydGFudDsKCQkJIGJvcmRlci1pbmxpbmUtc3RhcnQ6IDFweCBzb2xpZCB2YXIoLS1zaWRlYmFyLWJvcmRlci1jb2xvciwgVGhyZWVEU2hhZG93KSAhaW1wb3J0YW50OwoJCQkgYm9yZGVyLWlubGluZS1lbmQ6IG5vbmUgIWltcG9ydGFudDsKCQkgfQoJCSAjc2lkZWJhci1ib3hbcG9zaXRpb25lbmQ9InRydWUiXTotbW96LWxvY2FsZS1kaXIobHRyKSwKCQkgI3NpZGViYXItYm94Om5vdChbcG9zaXRpb25lbmQ9InRydWUiXSk6LW1vei1sb2NhbGUtZGlyKHJ0bCkgewoJCQkgLS12LXNpZGViYXItdHJhbnNmb3JtLWxvY2FsZS1kaXI6IDE7CgkJCSAtLXYtc2lkZWJhci10cmFuc2Zvcm0tbG9jYWxlLWRpci12aXNpYmxlOiAtMTsKCQkgfQoJCSA6cm9vdFt2X3ZlcnRpY2FsX2Jhcl9hdXRvaGlkZV1bdl92ZXJ0aWNhbF9iYXJfc2lkZWJhcj0idHJ1ZSJdICNzaWRlYmFyLWJveDpub3QoOmhvdmVyLFtzaWRlYmFyZHJhZ10pLAoJCSA6cm9vdFt2X3ZlcnRpY2FsX2Jhcl9hdXRvaGlkZV06bm90KFt2X3ZlcnRpY2FsX2Jhcl9zaWRlYmFyXSkgI3NpZGViYXItYm94Om5vdCg6aG92ZXIsW3NpZGViYXJkcmFnXSkgewoJCQkgdHJhbnNpdGlvbi1kZWxheTogMHMsIHZhcigtLXYtc2lkZWJhci10cmFuc2l0aW9uLWR1cmF0aW9uKSAhaW1wb3J0YW50OwoJCSB9CgkJIDpyb290W3NpemVtb2RlPSJub3JtYWwiXSAjc2lkZWJhci1ib3ggewoJCQkgLS12LXNpZGViYXItbWluLXdpZHRoOiB2YXIoLS12LXNpZGViYXItbWluLXdpZHRoLW5vcm1hbCkgIWltcG9ydGFudDsKCQkgfQoJCSAjc2lkZWJhci1ib3g6aG92ZXIsCgkJICNzaWRlYmFyLWJveFtzaWRlYmFyZHJhZ10gewoJCQkJCQl0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoMHB4KSAhaW1wb3J0YW50OwoJCQkgb3BhY2l0eTogMSAhaW1wb3J0YW50OwoJCQkgdHJhbnNpdGlvbi1kZWxheTogdmFyKC0tdi1zaWRlYmFyLXRyYW5zaXRpb24tZGVsYXktc2hvdykgIWltcG9ydGFudDsKCQkgfQoJCSA6cm9vdFt2X3ZlcnRpY2FsX2Jhcl92aXNpYmxlXVt2X3ZlcnRpY2FsX2Jhcl9zdGFydD0idHJ1ZSJdICNzaWRlYmFyLWJveDpub3QoW3Bvc2l0aW9uZW5kPSJ0cnVlIl0pLAoJCSA6cm9vdFt2X3ZlcnRpY2FsX2Jhcl92aXNpYmxlXVt2X3ZlcnRpY2FsX2Jhcl9zdGFydD0iZmFsc2UiXSAjc2lkZWJhci1ib3hbcG9zaXRpb25lbmQ9InRydWUiXSB7CgkJCSB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoY2FsYyh2YXIoLS12LXNpZGViYXItdHJhbnNmb3JtLWxvY2FsZS1kaXItdmlzaWJsZSkgKiB2YXIoLS12LXZlcnRpY2FsX2Jhcl93aWR0aCwgMHB4KSkpICFpbXBvcnRhbnQ7CgkJCSBvcGFjaXR5OiAxICFpbXBvcnRhbnQ7CgkJCSB0cmFuc2l0aW9uLWRlbGF5OiAwcyAhaW1wb3J0YW50OwoJCSB9CgkJICNicm93c2VyID4gI3NpZGViYXItc3BsaXR0ZXIgewoJCQkgZGlzcGxheTogbm9uZSAhaW1wb3J0YW50OwoJCSB9CgkJICNzaWRlYmFyLWJveCA+ICNzaWRlYmFyIHsKCQkJIG1pbi13aWR0aDogMCAhaW1wb3J0YW50OwoJCQkgd2lkdGg6IGF1dG8gIWltcG9ydGFudDsKCQkJIG1heC13aWR0aDogbm9uZSAhaW1wb3J0YW50OwoJCQkgLW1vei1ib3gtZmxleDogMSAhaW1wb3J0YW50OwoJCSB9CgkJIH1gCn0pLmluaXQodGhpcyk7Cg==


ЗЫ: может кому сгодиться, тестил на [firefox] 91esr и [firefox] 97

Farby пишет

для userChromeJS

скрипт ваш неправильный во первых он скорее всего не работает в таком виде в userChromeJS разве что там есть массив unloadlisteners, я не курсе
ну и loadAndRegisterSheet не для использования в оконных скриптах тем более без проверки загружен уже стиль или нет, лучше заменить хотя бы на windowUtils.loadSheetUsingURIString(string, type);
понятно что в ваших .uc.js скриптах loadAndRegisterSheet часто присутствует но это не значит что надо тоже такое городить.

Farby пишет

или подключить в user_chrome_files

вот это совсем лишнее учитывая что он там уже присутствует по умолчанию и с нормальной (кешированной) загрузкой стиля

17-02-2022 20:17:21

Dobrov пишет

насколько я понял, в CustomStylesScripts.jsm нет возможности подключать jsm-скрипты ?

Как вариант добавить перед строкой var UcfStylesScripts = {
эту
var jsmImport = path => `ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/${path}.jsm")`;
и далее добавлять названия скриптов или путь/название
scriptsbackground: [ // В фоне [System Principal]

        { func: jsmImport("path/scriptName"), },

Dobrov
Ну тут бабка надвое сказала, мне например, ваши комбайны, с кучей кликов, совсем не кажутся удобными и жесты мне неудобны. Но это не значит, что я против, я вижу что многим жесты нравятся. А вот массив кликов, ну это как в играх, например в Devil May Cry я ни при каком раскладе играть не буду, а ваши решения напоминают боевку этой игры. :)
 

Dobrov пишет

насколько я понял, в CustomStylesScripts.jsm нет возможности подключать jsm-скрипты

Dumby же давал загрузчик:

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

Выделить код

Код:

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


Добавляем его в стандартный импорт CustomStylesScripts.jsm:
скрытый текст

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
 ...
        { path: "custom_js/name.js", },

Vitaliy V. пишет

Как вариант добавить var jsmImport = path =>

Не работает на скрипте "Замена текста в имени вкладки", у которого надо грузить функцию, я попробовал перенести код, но не получилось:
Как исправить код, чтобы он грузил обычный jsm и jsm с функцией, которая выполнится при загрузке скрипта ?

Выделить код

Код:

var jsmImport = (name, funcName) => ` // подключить jsm [выполнить функцию]
	var {href, pathname} = new URL("chrome://user_chrome_files/content/custom_scripts/${name}");
	var obj = ChromeUtils.import(href); 
	funcName && obj[funcName]();
`;
{ func: jsmImport("UCFTitleChangedChild.jsm", "registerUCFTitleChanged"), },

UCFTitleChangedChild.jsm — Замена текста в имени вкладки

Выделить код

Код:

var EXPORTED_SYMBOLS = ["registerUCFTitleChanged", "UCFTitleChangedChild"];

var reg = /-\sПоиск\sв\sGoogle$| \| Форум Mozilla Россия$|^Смотреть дораму |^Смотреть бесплатно дораму |^Сериал \| Фильм | - DoramaTV/;
var hosts = ["https://www.google.com/search?*", "https://www.google.ru/search?*", "https://doramatv.live/*", "https://forum.mozilla-russia.org"];

function registerUCFTitleChanged() { // исправление заголовка вкладки
	ChromeUtils.registerWindowActor("UCFTitleChanged", {
		child: {
			moduleURI: "chrome://user_chrome_files/content/custom_scripts/UCFTitleChangedChild.jsm",
			events: {
				DOMTitleChanged: { capture: true },
			},
		},
			matches: hosts,
			messageManagerGroups: ["browsers"],
	});
}
class UCFTitleChangedChild extends JSWindowActorChild {
	handleEvent(e) {
		if (reg.test(this.document.title))
			this.document.title = this.document.title.replace(reg, "");
	}
}

Но работает в custom_script_win.js (а желательно подключать в CustomStylesScripts.jsm?)

Выделить код

Код:

var loadscript = (name, funcName) => {
  try { var {href, pathname} = new URL(`chrome://user_chrome_files/content/custom_scripts/${name}`);
    if (/\.jsm$/i.test(pathname)) {
      var obj = ChromeUtils.import(href);
      funcName && obj[funcName]();
    } else
      Services.scriptloader.loadSubScript(href);
    return true;
  }
  catch(ex) {Cu.reportError(ex);}
}
loadscript("ucf_aom-button.js");
loadscript("UCFTitleChangedChild.jsm", "registerUCFTitleChanged");

Dumby - Просьба убрать longPress из скрипта перехвата кликов - я пробовал, но срабатывает и обычный и двойной клик сразу…
После длительного юзания неудобство в том, что при нажатии кнопок действие происходит с задержкой в пол-секунды. Можно ли переделать код на стандартные события click, doubleclick, wheel — чтобы действие при нажатии кнопок выполнялось сразу?
Преимущество твоего кода в том, что легко можно прописать клики для любых кнопок панели инструментов, а в более простом примере на форуме нет разбора по #id нажатых кнопок и нет скролла…


Можешь набросать только часть кода для перехвата кликов/$id кнопок ? (я сам переделаю скрипт по ссылке)
Нужно обычный и двойной клик кнопок мыши, колёсико и разбор событий по кнопка, как в вышеуказанном коде:

Выделить код

Код:

data = {
	"#downloads-button": { mousedownTarget: true,
		2(trg, forward) {
			bright(trg, forward); // яркость по wheel ±
		},
		128() { saveSelectionToTxt();}, // СКМ Click
		260(btn) { // Double ПКМ Click
Dobrov пишет

Не работает на скрипте "Замена текста в имени вкладки", у которого надо грузить функцию

и в чем проблема, не надо там ничего переделывать с функцией jsmImport
{ func: `${jsmImport("UCFTitleChangedChild")}.registerUCFTitleChanged();`, },

18-02-2022 13:55:47

Dobrov пишет

Но работает в custom_script_win.js

это же опечатка или действительно в win запускаешь?

Vitaliy V. пишет

это же опечатка или действительно в win запускаешь?

Спасибо! Но в будущих версиях UCF возможно сделать подключение jsm-скриптов попроще? Без всяких `` кавычек, так, как для js сделано ?


Было так и имена вкладок менялись, но сейчас прописал правильно в CustomStylesScripts.jsm:
scriptsbackground: [ // В фоне [System Principal]

del

Dumby - В скрипте "Simple Session Manager" одна иконка прописана 2 раза. Как сократить код и передать её как переменную в regStyle() ?

Выделить код

Код:

onCreated(btn) {
…………
	btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8…………
regStyle() {
…………
	#${pid} > menu {
	list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8……………

Кроме того, восстановление сессий в новые вкладки с кликом средней кнопки + Ctrl не очень удобно, добавил для этого пункт меню: "Восстановить в новых вкладках"
А вообще для восстановления удобнее сразу кликать по имени сохранённой сесии (и восстанавливать в новых вкладках при клике с нажатым Ctrl), а в подменю оставить только: Переименовать и Удалить.

Simple Session Manager mod

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=798085#p798085
(async (pid, mp) => CustomizableUI.createWidget(({ id: "797321",
	label: "Simple Session Manager", tooltiptext: "Управление сессиями", localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = () => this.saveSession() || this.save();
		Services.obs.addObserver(this, "quit-application");
		var {openMenu} = this;
		this.render = function() {
			this.openMenu = openMenu;
			this.constructor.prototype.render.call(this);
		}
		delete this.init;
		return this;
	},
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn._handleClick = this.click;
		// btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/download-resume.svg");
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC");
		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;
		popup.setAttribute("onpopupshowing", "event.defaultPrevented || filler.fill(event)");
		btn.prepend(popup);

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
		btn.ownerGlobal.addEventListener("unload", () => {
			btn.removeEventListener("mousedown", this);
			popup.removeEventListener("command", this);
			if (popup.filler != this)
				popup.removeEventListener("dragstart", this),
				popup.removeEventListener("popuphidden", this);
		}, {once: true});
	},
	openMenu(arg) {
		var pos;
		if (this.matches(".widget-overflow-list > :scope"))
			pos = "after_start";
		else var win = this.ownerGlobal, {width, height, top, bottom, left, right} =
			this.closest("toolbar").getBoundingClientRect(), pos = width > height
				? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
				: `${win.innerWidth - right > left ? "end" : "start"}_before`;
		this.firstChild.setAttribute("position", pos);
		delete this.openMenu;
		this.openMenu(arg);
	},

	mousedown(e) {
		if (e.button) return;
		var trg = e.target;
		if (trg.nodeName.startsWith("t")) {
			trg.mdTimestamp = Cu.now();
			trg.tid = e.view.setTimeout(this.onTimeout, 500);
			return e.preventDefault();
		}
		e.detail == 2 && trg.nodeName == "menu" && this.boot(trg);
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.dblMD = true;
	},
	click() {
		var win = this.ownerGlobal;
		if (win.event.target != this) return;
		win.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r")) {
			arg = trg.parentNode.parentNode.label;
			if (cmd.startsWith("res"))
				return this[cmd](arg, (trg.label.indexOf("вклад")>0) && e.view); // Восстановить в новых вкладках
		}
		this[cmd](arg) || this.save();
	},

	dragstart(e) {
		var trg = e.target;
		if (trg.nodeName != "menu") return;

		var popup = trg.parentNode;
		this.dragData = {trg, mouse: true};
		trg.menupopup.hidePopup();

		var win = trg.ownerGlobal;
		win.setCursor("grabbing");
		var {width} = trg.getBoundingClientRect();
		trg.setAttribute("maxwidth", width);

		win.addEventListener("mouseup", this);
		popup.addEventListener("mousemove", this);
	},
	mousemove(e) {
		var trg = e.target, dtrg = this.dragData.trg;
		if (trg == dtrg || trg.nodeName != "menu") return;

		e.movementY > 0
			? trg.nextSibling != dtrg && trg.after(dtrg)
			: trg.previousSibling != dtrg && trg.before(dtrg);
	},
	mouseup(arg) {
		if (arg.constructor.isInstance?.(arg)) {
			arg.preventDefault();
			var {trg} = this.dragData;
			this.dragData.mouse = false;
		}
		else var trg = arg;

		trg.removeAttribute("maxwidth");
		trg.ownerGlobal.setCursor("auto");
		trg.ownerGlobal.removeEventListener("mouseup", this);
		trg.parentNode.removeEventListener("mousemove", this);
	},

	popuphidden(e) {
		if (!e.target.id) return;
		e.view.removeEventListener("keydown", this, true);
		var popup = e.target;
		popup.parentNode.phTimestamp = Cu.now();
		if (!this.dragData && !popup.dblMD) return;

		var save;
		if (this.dragData) {
			var {trg, mouse} = this.dragData;
			mouse && this.mouseup(trg);

			delete this.dragData;
			var order = Array.from(popup.getElementsByTagName("menu"), m => m.label);
			if (order.toString() != this.meta.order.toString()) {
				var hasBoot = this.meta.boot != null;
				if (hasBoot) var bootName = this.meta.order[this.meta.boot];
				this.meta.order = order;
				if (hasBoot) this.meta.boot = this.meta.order.indexOf(bootName);
				save = true;
			}
		}
		if (popup.dblMD) {
			delete popup.dblMD;
			var {boot} = this.meta;
			var bootNode = e.target.querySelector("[boot]");
			var ind = bootNode && this.meta.order.indexOf(bootNode.label);
			if (ind != boot)
				this.meta.boot = ind,
				save = true;
		}
		save && this.save(e.view);
	},

	sku: `#${pid} > menu[maxwidth]`,
	skd: `#${pid} > menu:is([maxwidth],[_moz-menuactive]):not([open])`,
	keydown(e) {
		if (e.repeat && e.key == "Shift" || !e.shiftKey) return;
		var func = this.keyHandlers[e.key];
		if (!func) return;

		var menu = e.view.windowRoot
			.ownerGlobal.document.querySelector(this.skd);
		if (menu)
			e.stopImmediatePropagation(),
			func.call(this, menu);
	},
	keyup(e) {
		if (e.key != "Shift") return;
		var win = e.view.windowRoot.ownerGlobal;
		win.removeEventListener("keyup", this, true);
		win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
	},
	keyHandlers: {
		Enter(menu) {
			this.boot(menu);
		},
		ArrowDown(menu) {
			var ns = menu.nextSibling;
			if (ns) ns.after(menu), this.arrow(menu);
		},
		ArrowUp(menu) {
			var ps = menu.previousSibling;
			if (ps.nodeName == "menu") ps.before(menu), this.arrow(menu);
		}
	},
	arrow(menu) {
		if (menu.hasAttribute("maxwidth")) return;
		menu.setAttribute("maxwidth", menu.getBoundingClientRect().width);
		menu.ownerGlobal.addEventListener("keyup", this, true);
		this.dragData = {trg: menu};
	},

	fill(e) {
		var mxe = e.view.MozXULElement;

		var initFrag = mxe.parseXULToFragment(`
			<menuitem value="saveSession" class="menuitem-iconic" label="Сохранить сессию"/>
			<menuitem value="deleteAllSessions" class="menuitem-iconic" label="Удалить все сессии"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<menuitem label="Восстановить"
				class="menuitem-iconic" value="restoreSession"/>
			<menuitem label="Восстановить в новых вкладках"
				class="menuitem-iconic" value="restoreSession"/>
			<menuitem label="Переименовать"
				class="menuitem-iconic" value="renameSession"/>
			<menuitem label="Удалить"
				class="menuitem-iconic" value="removeSession"/>
		</menupopup></menu>`);

		this.regStyle();

		var filler = {fill: e => e.target.id
			? e.view.addEventListener("keydown", this, true)
				|| e.target.fillFlag || this.fillSessions(e.target)
			: this.dragData?.mouse && e.preventDefault()
		};

		(this.fill = e => {
			var trg = e.target;
			trg.setAttribute("context", "");
			trg.append(trg.ownerDocument.importNode(initFrag, true));
			(trg.filler = filler).fill(e);
			trg.addEventListener("dragstart", this);
			trg.addEventListener("popuphidden", this);
		})(e);
	},
	fillSessions(popup) {
		while(popup.lastChild.nodeName == "menu") popup.lastChild.remove();
		var ind = 0, {boot} = this.meta;
		for(var name of this.meta.order) {
			var df = popup.ownerDocument.importNode(this.menuFrag, true);
			df.firstChild.setAttribute("label", name);
			if (ind++ == boot) df.firstChild.toggleAttribute("boot");
			popup.append(df);
		}
		popup.fillFlag = true;
	},
	regStyle() {
		delete this.regStyle;
		var subst = "ucf-ssm-style-resurl";
		Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(
			subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(`
			@-moz-document url-prefix(chrome://browser/content/browser.xhtml) {
				#${pid} > menu {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC");
				}
				#${pid} > [value=saveSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKVSURBVHjajFNNTBNREJ6+7na73bLbQn8I0BKNCYWKGr0oYoiJPxeNh6oHozcOxgSVi4le1JNHQRPigRsewURNPKiJMQicSISUACelRVJ2tz/bdktpy9adBzT8xTjJ5L35ezNv5hsLHEAv7noEntguChx7EuV8oTydKRhfno8kCnt9LTuFZ7f87mAz/8rnl86GOoOBBq+LQX1S1ioLs0txWdEmsulS38Phlcy+BwZ6W0L+JsfolcjpsFDnBKNcgQ193bRUwSrwQFgr6Dkd3o9NzilqMdI/FF+sPYCZ245I4zdunw9DtQr6LxlK6bVdpdrcPAiHfGBYLDD69ltUXsmfw0oIGoPNjkHMjMHa3J9asDyRpoyEOrQR0+dq5MxR0c29Rj3Bhvn8YjeWjZmN9cpWcApSkzpkptYg8T1FdWjTfysg1Ang9YtdT+80OogTbJc7OlsD+OftzOqsBrMflqHe6QKXIEL00zLV0UpSBbM/G9DeEQy6OXKJsfPsCbdXYjb0Yu2/nmMS9LSGYGlYpXLPkxCwkq1mR98Gn8Q4XexxYhhgYKf3TPTfRDaPqklkvViaSSpaxSpw/x1v5e0UG8W10k+iW8qfF2ZiccIydFQ7ScsXKO8ep4NiYj4ai6k5+Eoev1F1Wcn8QJDgnAnHbGYRWTh800cZ77Ryzmr6eCGXzYGiZCcQ2vQ32XT5PiIMTJBI4Waw1ZtZzLu7XaSMd1s9b9paKJA+vpuKqols3y4ov7wXaPN67GPXIl1hnDOOansyVicHhGHMzHkzeDKqqPnr/UOJxX3LNNDb5BJd3KC3Uexu7wgEGxq3limRqcxH47FVWRtPreYePBpJagdu4zYhwjx1cMFus59CuVgqTmPDDlrnvwIMAGS8IFCLagHpAAAAAElFTkSuQmCC");
				}
				#${pid} [value=restoreSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACzElEQVQ4jX2QPWwbZRjHf+/5vfPdObZjO3Ga2pVpGtxCP4QIDKgqDCDUNQwgJCS2qFI3hBCRCvXSCcTAAu0GY0Fi4kuq+IjUCpWAgCTq4IpS3Di2G8c+x/bZ57t7mSJFMfBf/3p+z+95BPvy3pufxqKmcVY3ZFFKmQtFGIReuNnrDze6lv5TqfSyx4EIgA/e+fzkZML6MDmVnD80mz1kxW3DMAxQCs/z6Hb6g3q1Xt1ttdfa7dbFN668vrkHkADJhPntufNnc0LTDi7AjFkkUknzcGF2LvCDuZVvfiwCj+/1GoAmhL3x6x2cZhsVqjFIGIZs15ts/LJOJCLt/Z0EiE3Y9eLpR1ONaoMH9zbHAGiC9FSKkwunWF1Z3RoDABhRg/zRPPmj+XHA/0QDaLectFLj6gcT+AHdTm9mzMC27e766lrWtCwy2Qz2hIUeNVDAaDCku9ujWW/ij3wsO9oaMzCiunf66TMU5gt4wyGbf1Upr5W5u15mq1JDhYqZQo6HIuqtueLw4vLXzwC89NZXeQFw/ePv7zx5buHEf6nf3qgMVipOGE0ko6EKhoQi3NnZ+c51BwsaQLOxM9N1uv86vNtz+aHSDn0pH9S2tl6r/Pn3s/VG/WYunzs/NZ2OSID0dKpRrzZS98v3kYbEsi1004BQcbtc9Q07bdYb229/dvmFL5aWVvVts9mq1Wo/e54vJIAQqGOPzQHgj3zcvos39BAIBshRqJQYjYZ/AFy79tQIeHVx+ctiIDRTA+g43Xud9q4PIHVJPBknk82QzqY5VZg2UUrEYhOX9s5aXL5RnMykb8Rj9kcC4OrSVZ1Hpt+PJ5PPz+SzR5KpyYRpGgRKMei7fHLr7lBmZ1XHcX4LgsCxLPt4JBLRm82Hr4j9DyuVSprdO/ZELGafkXpkXtPA94OK03Vv/R4/csEw5XOGbuieN6y6bv/S9XdfvPkPTgcoDlpQJpwAAAAASUVORK5CYII=");
				}
				#${pid} [value=renameSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPFJREFUeNrEU0sOgkAMbc1IIHoQT+KeY7hkBW5ccwLdsOICxI2X8peIGhggI21gggpCwsImM01f+tpOO0WlFIwR0efgeR5nsG0boihizPd91A5UwZDjuq5qwycwUkQYhttSr4Y4l76fDdtBEAQqz3OVZdnPQ09o2sQhrpBSQgnAen/qzLxZzljHcawxy7KAuCJNUyiKAm53SS0FBGQNlUW3lFO2iVCLYRhAXJEkCQPn65NGAswpL/4fWAebt1ZGXB3geHlw9maXsKqjS94CHNxF7xRM0/wKgI7jDBojvbnZAz3Gv/9E7NvGvmXCsev8EmAAWocA9ofpaRIAAAAASUVORK5CYII=");
				}
				#${pid} :is([value=removeSession], [value=deleteAllSessions]) {
					list-style-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABt0lEQVQ4ja2RT2sTURTFz8tk0nntFAQrVLBQxIUg/tkkMoLyIEVECHZhNrrxI+hOP5Mb6eBCcEipYIjZ2LpvhFasUtBSkwzOve+6mE6YSScuxLt67753fudyLvA/a7T+qCHmiTfrfWCMd3j9diPfq2SHX+uPnzJx71gP33w3bX9avBcE2j+SkK30Di7ffJ71VebMxD1hC2GGEEfH41prpftynIm92A0t2aYQQYgBThrndz/2KwCgf9Z2LNtImFOAtc356ijcCwJdJrZEUWzPfJpMAAAStPWR/h2Ktc10CoIQRUKEabFNvNbKfndcAKSjtrVfHW5YorWTzxDKYAxLtOWM+P7yt51hIYPpsDTNTyB/EwNAtWxdMuV8GCfbgxjxIviUYSV/SQOrbVjitbx4N8alBeXcdZR+3Tl3xS8FlIkt0dbnWH3xlbPgKgUo3HEq+tX7C4EuAAbGeOmqCuLIJt69s3PeQ1epKGfapCQJO6vGmwAWf/C1Wau6td8dV123BaAAUfHw6gSwtP3ugxA/y6X9INszAOQgbwFAIC/MQb9/Kv2vF2/UByejlVVn1Xiby/X6rPd/qj/1ak71UYKuwQAAAABJRU5ErkJggg==");
				}
				#${pid} > menuseparator:last-child,
				#${pid} > menu[maxwidth] > .menu-right,
				#${pid} > [value=deleteAllSessions]:nth-last-child(2) {
					display: none;
				}
				#${pid} > menu[boot] {
					color: red;
					font-weight: bold;
				}
				#${pid} > menu[maxwidth] {
					color: blue;
					font-weight: bold;
					outline-offset: -2px;
					outline: 2px solid orangered;
				}
			}
		`.replace(/;$/gm, " !important;"))));
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		sss.loadAndRegisterSheet(Services.io.newURI("resource://" + subst), sss.USER_SHEET);
	},

	get gs() {
		delete this.gs;
		return this.gs = Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {});
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	splice(name, newName) {
		var ind = this.meta.order.indexOf(name);
		if (ind == -1) return;
		var args = [ind, 1];

		if (1 in arguments) args.push(newName);
		else {
			if (ind == this.meta.boot) this.meta.boot = null;
			else if (ind < this.meta.boot) this.meta.boot--;
		}
		this.meta.order.splice(...args);
	},

	get meta() {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("simple_session_manager.json");
		this.path = file.path;
		try {
			this.data = JSON.parse(Cu.readUTF8File(file));
		} catch {
			this.pp = file.parent.path;
			this.data = Object.create(null);
		}
		var meta = this.data[mp];
		if (!meta) {
			var order = Object.keys(this.data);
			meta = this.data[mp] = {order, boot: null};
		}
		delete this.meta;
		return this.meta = meta;
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils;
		if (this.pp)
			await io.makeDirectory(this.pp), delete this.pp;
		(this.save = excWin => {
			this.meta.order.length
				? io.writeJSON(this.path, this.data)
				: io.remove(this.path, {ignoreAbsent: true});
			for(var win of CustomizableUI.windows) {
				if (win == excWin) continue;
				var popup = win.document.getElementById(pid);
				if (popup) popup.fillFlag = false;
			}
		})(excWin);
	},

	get prompter() {
		var {prompt} = Services;
		var p = {}, args = [null, null, "UCF Simple Session Manager"];
		p.alert = prompt.alert.bind(...args);
		p.confirm = prompt.confirm.bind(...args);
		var pr = prompt.prompt.bind(...args);
		p.prompt = (msg, value) => {
			var res = {value};
			return pr(msg, res, null, {}) ? res.value : null;
		}
		delete this.prompter;
		return this.prompter = p;
	},
	observe(s, t, data) {
		Services.obs.removeObserver(this, "quit-application");
		if (data.includes("restart")) return;

		var {boot} = this.meta;
		if (boot == null) return;

		var state = this.data[this.meta.order[boot]];
		//this.gs.SessionStoreInternal.getCurrentState = () => state;
		var ssi = this.gs.SessionStoreInternal;
		ssi.getCurrentState = () => state;
		Services.obs.removeObserver(ssi, "browser:purge-session-history");

		Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
	},

	get bwt() {
		delete this.bwt;
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		return this.bwt = ChromeUtils.import(url).BrowserWindowTracker;
	},
	getName(state) {
		var wl = state.windows.length, tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		} ${wl}/${tl} [${
			new Date().toLocaleString("mn").replace(" ", "-")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	saveSession(state = this.getState(), name = this.getName(state)) {
		var name = this.prompter.prompt("Сохранить:", name);
		if (name == null) return true;

		if (this.exists(name)) return this.saveSession(state, name);

		this.data[name] = state;

		this.meta.order.push(name);
		//this.meta.order.unshift(name);
		//if (this.meta.boot != null) this.meta.boot++;
	},
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
	renameSession(name, newName = name) {
		var newName = this.prompter.prompt(`Переименовать "${name}" в:`, newName);
		if (newName == null || newName == name) return true;

		if (this.exists(newName)) return this.renameSession(name, newName);

		var {data} = this;
		this.splice(name, newName);
		data[newName] = data[name];
		delete data[name];
	},
	removeSession(name) {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить ${name} ?`))
			return true;
		delete this.data[name];
		this.splice(name);
	},
	deleteAllSessions() {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить все сессии?`))
			return true;
		delete this.dragData;
		delete this.bwt.getTopWindow().document.getElementById(pid).dblMD;
		this.meta = (this.data = Object.create(null))[mp] = {order: [], boot: null};
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");

Dobrov пишет

одна иконка прописана 2 раза. Как сократить код

Например, задать её как свойство объекта, и далее использовать, типа this.image

А вообще для восстановления удобнее сразу кликать по имени сохранённой сесии (и восстанавливать в новых вкладках при клике с нажатым Ctrl), а в подменю оставить только: Переименовать и Удалить.

Там же двойной левый mousedown устанавливает
или снимает загрузочную сессию. С этим что делать?
Ладно, допустим перенесём на клик ПКМ.

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

Выделить код

Код:

(async (pid, mp, self) => CustomizableUI.createWidget((self = { id: "797321",
	label: "Simple Session Manager", tooltiptext: "Управление сессиями", localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = () => this.saveSession() || this.save();
		Services.obs.addObserver(this, "quit-application");
		var {openMenu} = this;
		this.render = function() {
			this.openMenu = openMenu;
			this.constructor.prototype.render.call(this);
		}
		delete this.init;
		return this;
	},
	image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC",
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn.onclick = this.click;
		// btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/download-resume.svg");
		btn.setAttribute("image", this.image);
		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;
		popup.setAttribute("onpopupshowing", "event.defaultPrevented || filler.fill(event)");
		btn.prepend(popup);

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
		btn.ownerGlobal.addEventListener("unload", () => {
			btn.removeEventListener("mousedown", this);
			popup.removeEventListener("command", this);
			if (popup.filler != this)
				popup.removeEventListener("dragstart", this),
				popup.removeEventListener("popuphidden", this);
		}, {once: true});
	},
	openMenu(arg) {
		var pos;
		if (this.matches(".widget-overflow-list > :scope"))
			pos = "after_start";
		else var win = this.ownerGlobal, {width, height, top, bottom, left, right} =
			this.closest("toolbar").getBoundingClientRect(), pos = width > height
				? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
				: `${win.innerWidth - right > left ? "end" : "start"}_before`;
		this.firstChild.setAttribute("position", pos);
		delete this.openMenu;
		this.openMenu(arg);
	},

	mousedown(e) {
		if (e.button) return;
		var trg = e.target;
		if (trg.nodeName.startsWith("t"))
			trg.mdTimestamp = Cu.now(),
			trg.tid = e.view.setTimeout(this.onTimeout, 500),
			e.preventDefault();
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.bootChanged = true;
	},
	muTimestamp: 0,
	click(e) {
		var trg = e.target;
		if (trg.nodeName == "menu") {
			if (e.button > 1) self.boot(trg);
			else if (Cu.now() - self.muTimestamp > 50)
				e.view.closeMenus(trg.menupopup),
				self.restoreSession(trg.label, (e.button || e.ctrlKey) && e.view);
		}
		else if (trg != this || e.button) return;
		e.view.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r"))
			arg = trg.parentNode.parentNode.label;
		this[cmd](arg) || this.save();
	},

	dragstart(e) {
		var trg = e.target;
		if (trg.nodeName != "menu") return;

		var popup = trg.parentNode;
		this.dragData = {trg, mouse: true};
		trg.menupopup.hidePopup();

		var win = trg.ownerGlobal;
		win.setCursor("grabbing");
		var {width} = trg.getBoundingClientRect();
		trg.setAttribute("maxwidth", width);

		win.addEventListener("mouseup", this);
		popup.addEventListener("mousemove", this);
	},
	mousemove(e) {
		var trg = e.target, dtrg = this.dragData.trg;
		if (trg == dtrg || trg.nodeName != "menu") return;

		e.movementY > 0
			? trg.nextSibling != dtrg && trg.after(dtrg)
			: trg.previousSibling != dtrg && trg.before(dtrg);
	},
	mouseup(arg) {
		if (arg.constructor.isInstance?.(arg)) {
			arg.preventDefault();
			var {trg} = this.dragData;
			this.dragData.mouse = false;
			this.muTimestamp = Cu.now();
		}
		else var trg = arg;

		trg.removeAttribute("maxwidth");
		trg.ownerGlobal.setCursor("auto");
		trg.ownerGlobal.removeEventListener("mouseup", this);
		trg.parentNode.removeEventListener("mousemove", this);
	},

	popuphidden(e) {
		if (!e.target.id) return;
		e.view.removeEventListener("keydown", this, true);
		var popup = e.target;
		popup.parentNode.phTimestamp = Cu.now();

		var save;
		if (this.dragData) {
			var {trg, mouse} = this.dragData;
			mouse && this.mouseup(trg);

			delete this.dragData;
			var order = Array.from(popup.getElementsByTagName("menu"), m => m.label);
			if (order.toString() != this.meta.order.toString()) {
				var hasBoot = this.meta.boot != null;
				if (hasBoot) var bootName = this.meta.order[this.meta.boot];
				this.meta.order = order;
				if (hasBoot) this.meta.boot = this.meta.order.indexOf(bootName);
				save = true;
			}
		}
		if (popup.bootChanged) {
			delete popup.bootChanged;
			var {boot} = this.meta;
			var bootNode = e.target.querySelector("[boot]");
			var ind = bootNode && this.meta.order.indexOf(bootNode.label);
			if (ind != boot)
				this.meta.boot = ind,
				save = true;
		}
		save && this.save(e.view);
	},

	sku: `#${pid} > menu[maxwidth]`,
	skd: `#${pid} > menu:is([maxwidth],[_moz-menuactive]):not([open])`,
	keydown(e) {
		if (e.repeat && e.key == "Shift" || !e.shiftKey && e.key != " ") return;
		var func = this.keyHandlers[e.key];
		if (!func) return;

		var menu = e.view.windowRoot
			.ownerGlobal.document.querySelector(this.skd);
		if (menu)
			e.stopImmediatePropagation(),
			func.call(this, menu, e);
	},
	keyup(e) {
		if (e.key != "Shift") return;
		var win = e.view.windowRoot.ownerGlobal;
		win.removeEventListener("keyup", this, true);
		win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
	},
	keyHandlers: {
		Enter(menu) {
			this.boot(menu);
		},
		ArrowDown(menu) {
			var ns = menu.nextSibling;
			if (ns) ns.after(menu), this.arrow(menu);
		},
		ArrowUp(menu) {
			var ps = menu.previousSibling;
			if (ps.nodeName == "menu") ps.before(menu), this.arrow(menu);
		},
		" "(menu, e) {
			e.preventDefault();
			menu.ownerGlobal.closeMenus(menu.parentNode);
			this.restoreSession(menu.label, e.ctrlKey && menu.ownerGlobal);
		}
	},
	arrow(menu) {
		if (menu.hasAttribute("maxwidth")) return;
		menu.setAttribute("maxwidth", menu.getBoundingClientRect().width);
		menu.ownerGlobal.addEventListener("keyup", this, true);
		this.dragData = {trg: menu};
	},

	fill(e) {
		var mxe = e.view.MozXULElement;

		var initFrag = mxe.parseXULToFragment(`
			<menuitem value="saveSession" class="menuitem-iconic" label="Сохранить сессию"/>
			<menuitem value="deleteAllSessions" class="menuitem-iconic" label="Удалить все сессии"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<menuitem label="Переименовать"
				class="menuitem-iconic" value="renameSession"/>
			<menuitem label="Удалить"
				class="menuitem-iconic" value="removeSession"/>
		</menupopup></menu>`);

		this.regStyle();

		var filler = {fill: e => e.target.id
			? e.view.addEventListener("keydown", this, true)
				|| e.target.fillFlag || this.fillSessions(e.target)
			: this.dragData?.mouse && e.preventDefault()
		};

		(this.fill = e => {
			var trg = e.target;
			trg.setAttribute("context", "");
			trg.append(trg.ownerDocument.importNode(initFrag, true));
			(trg.filler = filler).fill(e);
			trg.addEventListener("dragstart", this);
			trg.addEventListener("popuphidden", this);
		})(e);
	},
	fillSessions(popup) {
		while(popup.lastChild.nodeName == "menu") popup.lastChild.remove();
		var ind = 0, {boot} = this.meta;
		for(var name of this.meta.order) {
			var df = popup.ownerDocument.importNode(this.menuFrag, true);
			df.firstChild.setAttribute("label", name);
			if (ind++ == boot) df.firstChild.toggleAttribute("boot");
			popup.append(df);
		}
		popup.fillFlag = true;
	},
	regStyle() {
		delete this.regStyle;
		var subst = "ucf-ssm-style-resurl";
		Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(
			subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(`
			@-moz-document url-prefix(chrome://browser/content/browser.xhtml) {
				#${pid} > menu {
					list-style-image: url("${this.image}");
				}
				#${pid} > [value=saveSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKVSURBVHjajFNNTBNREJ6+7na73bLbQn8I0BKNCYWKGr0oYoiJPxeNh6oHozcOxgSVi4le1JNHQRPigRsewURNPKiJMQicSISUACelRVJ2tz/bdktpy9adBzT8xTjJ5L35ezNv5hsLHEAv7noEntguChx7EuV8oTydKRhfno8kCnt9LTuFZ7f87mAz/8rnl86GOoOBBq+LQX1S1ioLs0txWdEmsulS38Phlcy+BwZ6W0L+JsfolcjpsFDnBKNcgQ193bRUwSrwQFgr6Dkd3o9NzilqMdI/FF+sPYCZ245I4zdunw9DtQr6LxlK6bVdpdrcPAiHfGBYLDD69ltUXsmfw0oIGoPNjkHMjMHa3J9asDyRpoyEOrQR0+dq5MxR0c29Rj3Bhvn8YjeWjZmN9cpWcApSkzpkptYg8T1FdWjTfysg1Ang9YtdT+80OogTbJc7OlsD+OftzOqsBrMflqHe6QKXIEL00zLV0UpSBbM/G9DeEQy6OXKJsfPsCbdXYjb0Yu2/nmMS9LSGYGlYpXLPkxCwkq1mR98Gn8Q4XexxYhhgYKf3TPTfRDaPqklkvViaSSpaxSpw/x1v5e0UG8W10k+iW8qfF2ZiccIydFQ7ScsXKO8ep4NiYj4ai6k5+Eoev1F1Wcn8QJDgnAnHbGYRWTh800cZ77Ryzmr6eCGXzYGiZCcQ2vQ32XT5PiIMTJBI4Waw1ZtZzLu7XaSMd1s9b9paKJA+vpuKqols3y4ov7wXaPN67GPXIl1hnDOOansyVicHhGHMzHkzeDKqqPnr/UOJxX3LNNDb5BJd3KC3Uexu7wgEGxq3limRqcxH47FVWRtPreYePBpJagdu4zYhwjx1cMFus59CuVgqTmPDDlrnvwIMAGS8IFCLagHpAAAAAElFTkSuQmCC");
				}
				#${pid} [value=restoreSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACzElEQVQ4jX2QPWwbZRjHf+/5vfPdObZjO3Ga2pVpGtxCP4QIDKgqDCDUNQwgJCS2qFI3hBCRCvXSCcTAAu0GY0Fi4kuq+IjUCpWAgCTq4IpS3Di2G8c+x/bZ57t7mSJFMfBf/3p+z+95BPvy3pufxqKmcVY3ZFFKmQtFGIReuNnrDze6lv5TqfSyx4EIgA/e+fzkZML6MDmVnD80mz1kxW3DMAxQCs/z6Hb6g3q1Xt1ttdfa7dbFN668vrkHkADJhPntufNnc0LTDi7AjFkkUknzcGF2LvCDuZVvfiwCj+/1GoAmhL3x6x2cZhsVqjFIGIZs15ts/LJOJCLt/Z0EiE3Y9eLpR1ONaoMH9zbHAGiC9FSKkwunWF1Z3RoDABhRg/zRPPmj+XHA/0QDaLectFLj6gcT+AHdTm9mzMC27e766lrWtCwy2Qz2hIUeNVDAaDCku9ujWW/ij3wsO9oaMzCiunf66TMU5gt4wyGbf1Upr5W5u15mq1JDhYqZQo6HIuqtueLw4vLXzwC89NZXeQFw/ePv7zx5buHEf6nf3qgMVipOGE0ko6EKhoQi3NnZ+c51BwsaQLOxM9N1uv86vNtz+aHSDn0pH9S2tl6r/Pn3s/VG/WYunzs/NZ2OSID0dKpRrzZS98v3kYbEsi1004BQcbtc9Q07bdYb229/dvmFL5aWVvVts9mq1Wo/e54vJIAQqGOPzQHgj3zcvos39BAIBshRqJQYjYZ/AFy79tQIeHVx+ctiIDRTA+g43Xud9q4PIHVJPBknk82QzqY5VZg2UUrEYhOX9s5aXL5RnMykb8Rj9kcC4OrSVZ1Hpt+PJ5PPz+SzR5KpyYRpGgRKMei7fHLr7lBmZ1XHcX4LgsCxLPt4JBLRm82Hr4j9DyuVSprdO/ZELGafkXpkXtPA94OK03Vv/R4/csEw5XOGbuieN6y6bv/S9XdfvPkPTgcoDlpQJpwAAAAASUVORK5CYII=");
				}
				#${pid} [value=renameSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPFJREFUeNrEU0sOgkAMbc1IIHoQT+KeY7hkBW5ccwLdsOICxI2X8peIGhggI21gggpCwsImM01f+tpOO0WlFIwR0efgeR5nsG0boihizPd91A5UwZDjuq5qwycwUkQYhttSr4Y4l76fDdtBEAQqz3OVZdnPQ09o2sQhrpBSQgnAen/qzLxZzljHcawxy7KAuCJNUyiKAm53SS0FBGQNlUW3lFO2iVCLYRhAXJEkCQPn65NGAswpL/4fWAebt1ZGXB3geHlw9maXsKqjS94CHNxF7xRM0/wKgI7jDBojvbnZAz3Gv/9E7NvGvmXCsev8EmAAWocA9ofpaRIAAAAASUVORK5CYII=");
				}
				#${pid} :is([value=removeSession], [value=deleteAllSessions]) {
					list-style-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABt0lEQVQ4ja2RT2sTURTFz8tk0nntFAQrVLBQxIUg/tkkMoLyIEVECHZhNrrxI+hOP5Mb6eBCcEipYIjZ2LpvhFasUtBSkwzOve+6mE6YSScuxLt67753fudyLvA/a7T+qCHmiTfrfWCMd3j9diPfq2SHX+uPnzJx71gP33w3bX9avBcE2j+SkK30Di7ffJ71VebMxD1hC2GGEEfH41prpftynIm92A0t2aYQQYgBThrndz/2KwCgf9Z2LNtImFOAtc356ijcCwJdJrZEUWzPfJpMAAAStPWR/h2Ktc10CoIQRUKEabFNvNbKfndcAKSjtrVfHW5YorWTzxDKYAxLtOWM+P7yt51hIYPpsDTNTyB/EwNAtWxdMuV8GCfbgxjxIviUYSV/SQOrbVjitbx4N8alBeXcdZR+3Tl3xS8FlIkt0dbnWH3xlbPgKgUo3HEq+tX7C4EuAAbGeOmqCuLIJt69s3PeQ1epKGfapCQJO6vGmwAWf/C1Wau6td8dV123BaAAUfHw6gSwtP3ugxA/y6X9INszAOQgbwFAIC/MQb9/Kv2vF2/UByejlVVn1Xiby/X6rPd/qj/1ak71UYKuwQAAAABJRU5ErkJggg==");
				}
				#${pid} > menuseparator:last-child,
				#${pid} > menu[maxwidth] > .menu-right,
				#${pid} > [value=deleteAllSessions]:nth-last-child(2) {
					display: none;
				}
				#${pid} > menu[boot] {
					color: red;
					font-weight: bold;
				}
				#${pid} > menu[maxwidth] {
					color: blue;
					font-weight: bold;
					outline-offset: -2px;
					outline: 2px solid orangered;
				}
			}
		`.replace(/;$/gm, " !important;"))));
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		sss.loadAndRegisterSheet(Services.io.newURI("resource://" + subst), sss.USER_SHEET);
	},

	get gs() {
		delete this.gs;
		return this.gs = Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {});
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	splice(name, newName) {
		var ind = this.meta.order.indexOf(name);
		if (ind == -1) return;
		var args = [ind, 1];

		if (1 in arguments) args.push(newName);
		else {
			if (ind == this.meta.boot) this.meta.boot = null;
			else if (ind < this.meta.boot) this.meta.boot--;
		}
		this.meta.order.splice(...args);
	},

	get meta() {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("simple_session_manager.json");
		this.path = file.path;
		try {
			this.data = JSON.parse(Cu.readUTF8File(file));
		} catch {
			this.pp = file.parent.path;
			this.data = Object.create(null);
		}
		var meta = this.data[mp];
		if (!meta) {
			var order = Object.keys(this.data);
			meta = this.data[mp] = {order, boot: null};
		}
		delete this.meta;
		return this.meta = meta;
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils;
		if (this.pp)
			await io.makeDirectory(this.pp), delete this.pp;
		(this.save = excWin => {
			this.meta.order.length
				? io.writeJSON(this.path, this.data)
				: io.remove(this.path, {ignoreAbsent: true});
			for(var win of CustomizableUI.windows) {
				if (win == excWin) continue;
				var popup = win.document.getElementById(pid);
				if (popup) popup.fillFlag = false;
			}
		})(excWin);
	},

	get prompter() {
		var {prompt} = Services;
		var p = {}, args = [null, null, "UCF Simple Session Manager"];
		p.alert = prompt.alert.bind(...args);
		p.confirm = prompt.confirm.bind(...args);
		var pr = prompt.prompt.bind(...args);
		p.prompt = (msg, value) => {
			var res = {value};
			return pr(msg, res, null, {}) ? res.value : null;
		}
		delete this.prompter;
		return this.prompter = p;
	},
	observe(s, t, data) {
		Services.obs.removeObserver(this, "quit-application");
		if (data.includes("restart")) return;

		var {boot} = this.meta;
		if (boot == null) return;

		var state = this.data[this.meta.order[boot]];
		//this.gs.SessionStoreInternal.getCurrentState = () => state;
		var ssi = this.gs.SessionStoreInternal;
		ssi.getCurrentState = () => state;
		Services.obs.removeObserver(ssi, "browser:purge-session-history");

		Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
	},

	get bwt() {
		delete this.bwt;
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		return this.bwt = ChromeUtils.import(url).BrowserWindowTracker;
	},
	getName(state) {
		var wl = state.windows.length, tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		} ${wl}/${tl} [${
			new Date().toLocaleString("mn").replace(" ", "-")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	saveSession(state = this.getState(), name = this.getName(state)) {
		var name = this.prompter.prompt("Сохранить:", name);
		if (name == null) return true;

		if (this.exists(name)) return this.saveSession(state, name);

		this.data[name] = state;

		this.meta.order.push(name);
		//this.meta.order.unshift(name);
		//if (this.meta.boot != null) this.meta.boot++;
	},
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
	renameSession(name, newName = name) {
		var newName = this.prompter.prompt(`Переименовать "${name}" в:`, newName);
		if (newName == null || newName == name) return true;

		if (this.exists(newName)) return this.renameSession(name, newName);

		var {data} = this;
		this.splice(name, newName);
		data[newName] = data[name];
		delete data[name];
	},
	removeSession(name) {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить ${name} ?`))
			return true;
		delete this.data[name];
		this.splice(name);
	},
	deleteAllSessions() {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить все сессии?`))
			return true;
		delete this.dragData;
		delete this.bwt.getTopWindow().document.getElementById(pid).bootChanged;
		this.meta = (this.data = Object.create(null))[mp] = {order: [], boot: null};
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");

Vitaliy V.
Dumby
Что необходимо изменить или добавить в этом скрипте, что бы всплывающее окно автоматически закрывалось после перемещения курсора за пределы окна ?

Dumby пишет

Там же двойной левый mousedown устанавливает или снимает загрузочную сессию.

Вот этого в предыдущем коде не понял. Желательно в подсказку добавлять справку по действиям, как в большинстве примеров:
Долгое нажатие Сохранить сессию, Колёсико или Клик + Ctrl Открыть сессию в новых вкладках, Правый клик Выделить и Открывать при запуске???


Ещё проблема - сохраняю пару сессий по две или три вкладки. При клике по имени сохранённой сессии (не boot) восстанавливается только последняя вкладка, первые пустые. (Firefox 97.0.1)


А что должно происходить, если имя сессии правым кликом выделено Красным? Оставляю одну пустую вкладку, при перезапуске браузера выделенная сессия не загружается…
Раскомментировал эту строку - тоже ничего не автозагружается: this.gs.SessionStoreInternal.getCurrentState = () => state;

О, а скажите, можно ли посредством сабжа организовать такую штуку?
В доквантумном фоксе было расширение itsalltext. Становишься в textarea, жмёшь хоткей или кликаешь иконку (всплывала в уголке textarea) — и содержимое оной сохранялось в файле на диске и открывалось в текстовом редакторе, в каком настроено. А когда в редакторе сохранялся файл, содержимое textarea обновлялось. Можно было закрыть огнелиса, закрыть текстовый редактор, потом запустить их обратно и продолжить редактировать текст.
В квантуме же расширение больше не работает. Альтернативы требуют предварительно запустить текстовый редактор вручную, а в нём запустить плагин-сервер (есть для вима, для саблайма, ещё чего-то), и только тогда textarea обменивается текстом с редактором. Схема не намного удобней, чем просто запустить редактор и копипастить. Я б даже сказал, вообще шило на мыло.
А вот через UCF можно запускать сторонние приложения и значит — можно теоретически реализовать поведение старого аддона.

Wave пишет

Становишься в textarea, жмёшь хоткей или кликаешь иконку (всплывала в уголке textarea) — и содержимое оной сохранялось в файле на диске и открывалось в текстовом редакторе

В шапке темы скрипт hookClicks сохраняет выделенный текст или всю страницу в текстовый файл – правый клик по кнопке Загрузки. К имени файла добавляется заголовок вкладки и дата, но имя можно фиксированное прописать…

Dobrov пишет
Wave пишет

Становишься в textarea, жмёшь хоткей или кликаешь иконку (всплывала в уголке textarea) — и содержимое оной сохранялось в файле на диске и открывалось в текстовом редакторе

В шапке темы скрипт hookClicks сохраняет выделенный текст или всю страницу в текстовый файл – правый клик по кнопке Загрузки. К имени файла добавляется заголовок вкладки и дата, но имя можно фиксированное прописать…

Остаётся чтобы этот скрипт а) автоматически запускал текстовый редактор с этим файлом, б) по сохранению файла «снаружи» загружал его и обновлял содержимое страницы.

Wave
С внешним редактором не знаю, а вот для ucf в [firefox] есть такая кнопка. Что она делает - смотрите там мою просьбу выше.
Для Вашего пожелания можно было бы сделать следующее:
1. По ПКМ в textarea в меню появляется строчка, например, "Открыть Notepad". Текст выделяется, копируется в буфер, в [firefox] открывается вкладка "Notepad" и текст вставляется.
2. При закрытии вкладки текст выделяется, копируется в буфер и во вкладке, из которой был открыт в  textarea вставляется.
Дело за малым - устраивает это Вас? И если устраивает найти того, кто это напишет. Кнопку сделал Dumby.
UPD: можно и без строки меню, хоткеем. Невнимательно прочитал Ваш пост.

27-02-2022 14:54:56
UPD2: Можно поставить расширение "Управление историей форм". Будет сохраняться история редактирования.

xrun1
Вы распакуйте 52 версию, какую нить портативку, да и установите расширение, что б понимать о чем речь. Расширение шикарное, тоже его очень не хватает. То что вы предлагаете не совсем то... я через буфер обмена быстрее и проще все сделаю.

_zt
Если длинный пост всегда сначала пишу в редакторе, чтобы проверить орфографию. Поэтому расширением никогда не пользовался по причине ненужности лично для меня.
У меня портабельная есть 56-я, но ставить расширение лень.;) Предложил исходя из формулировки вопроса, что можно придумать на сей день. Нет так нет.

xrun1 пишет

UPD2: Можно поставить расширение "Управление историей форм". Будет сохраняться история редактирования.

У меня аналогичное расширение стоит, Textarea Cache. Оно, конечно, спасает от случайного перехода на другую страницу, закрытия вкладки и прочих неприятностей (на самом деле нет, потому что за всё время, что оно у меня стоит, так ни разу и не пригодилось).

xrun1 пишет

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

Ну вот как поступаете вы. Придя куда-то, где нужно написать длинный пост, вы, вероятно, копируете в буфер обмена содержимое textarea (например, если там уже есть цитаты), запускаете редактор, вставляете из буфера обмена текст, пишете пост, копируете в буфер обмена текст, закрываете редактор, предварительно сказав «не сохранять» или введя имя файла и выбрав путь, и вставляете текст туда, куда писали.
.
Как поступал я, когда сидел на фф56-. Придя куда-то, где нужно писать длинный пост, нажимал хоткей, автоматом запускался редактор, где уже было то, что  до этого было в textarea, писал пост, жал Ctrl-S, Alt-F4, переключался в браузер (если он не на переднем плане после закрытия редактора) и жал кнопку «отправить», т.к. в textarea текст уже тот, что я редактировал. Всё. Меньше действий — отсутствует ручной запуск редактора, два копирования-вставки в буфер, вопрос про то, что текст несохранён, с каким именем вы хотите его сохранить или вообще не хотите. Удобней (по крайней мере, мне).
.
Зы. Сами файлы расширение чистило по прошествии какого-то времени.

kokoss пишет

Что необходимо изменить или добавить в этом скрипте, что бы всплывающее окно автоматически закрывалось после перемещения курсора за пределы окна ?

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

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

Выделить код

Код:

(async bmrk => {
	await delayedStartupPromise;

	var popupshown = e => {
		var trg = e.target;
		if (trg.nodeName.startsWith("t")) return;

		var {curid, curbut} = autopopup;
		if (curid && trg.id == curid || curbut && (
			curbut.className == "bookmark-item" && trg.matches(bmrk) ||
			curbut.open && curbut.contains(trg.anchorNode || trg)
				&& (curbut.type != "menu" || curbut.menupopup)
		))
			trg.addEventListener("mouseleave", mouseleave),
			trg.addEventListener("popuphidden", popuphidden);
	}
	var popuphidden = function(e) {
		if (e.target == this)
			this.removeEventListener("mouseleave", mouseleave),
			this.removeEventListener("popuphidden", popuphidden);
	}
	var tid;
	var mouseleave = e => {
		tid && clearTimeout(tid);
		tid = setTimeout(check, 350, e.target);
	}
	var check = popup => {
		tid = null;
		popup.closest(":is(menupopup,panel):hover") || autopopup.curbut?.matches(":hover")
			|| (popup.nodeName.startsWith("m") ? closeMenus(popup) : popup.hidePopup());
	}
	var autopopup = ucf_custom_script_win.mouseoveropentoolbarbutton;
	var {destructor} = autopopup;
	autopopup.destructor = () => {
		destructor.call(autopopup);
		removeEventListener("popupshown", popupshown);
	}
	addEventListener("popupshown", popupshown);
})("toolbarbutton.bookmark-item :scope");


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

Вот этого в предыдущем коде не понял.

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

Желательно в подсказку добавлять

Вот и займись этим. Если туда еще добавить про двиганье мышью,
и все клавиатурные аналоги мышиных действий, будет капитально.

при перезапуске браузера выделенная сессия не загружается

И не должна, перезапуск есть перезапуск, это действие определённого характера.
А вот если закрыть Firefox, то затем, при запуске, должна загружаться.

восстанавливается только последняя вкладка, первые пустые

Прямо в реальном времени это не работает.
Нужно подождать, а не сохранять сразу после открытия вкладок.


Если очень надо, то можно попробовать так:
задать небольшое значение настройки browser.sessionstore.interval
подождать секунду, вернуть настройку, и тогда сохранять.
Вроде работает, на первый взгляд.

Выделить код

Код:

(async (pid, mp, self) => CustomizableUI.createWidget((self = { id: "797321",
	label: "Simple Session Manager", tooltiptext: "Управление сессиями", localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = async () => await this.saveSession() || this.save();
		Services.obs.addObserver(this, "quit-application");
		var {openMenu} = this;
		this.render = function() {
			this.openMenu = openMenu;
			this.constructor.prototype.render.call(this);
		}
		delete this.init;
		return this;
	},
	image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC",
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn.onclick = this.click;
		// btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/download-resume.svg");
		btn.setAttribute("image", this.image);
		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;
		popup.setAttribute("onpopupshowing", "event.defaultPrevented || filler.fill(event)");
		btn.prepend(popup);

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
		btn.ownerGlobal.addEventListener("unload", () => {
			btn.removeEventListener("mousedown", this);
			popup.removeEventListener("command", this);
			if (popup.filler != this)
				popup.removeEventListener("dragstart", this),
				popup.removeEventListener("popuphidden", this);
		}, {once: true});
	},
	openMenu(arg) {
		var pos;
		if (this.matches(".widget-overflow-list > :scope"))
			pos = "after_start";
		else var win = this.ownerGlobal, {width, height, top, bottom, left, right} =
			this.closest("toolbar").getBoundingClientRect(), pos = width > height
				? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
				: `${win.innerWidth - right > left ? "end" : "start"}_before`;
		this.firstChild.setAttribute("position", pos);
		delete this.openMenu;
		this.openMenu(arg);
	},

	mousedown(e) {
		if (e.button) return;
		var trg = e.target;
		if (trg.nodeName.startsWith("t"))
			trg.mdTimestamp = Cu.now(),
			trg.tid = e.view.setTimeout(this.onTimeout, 500),
			e.preventDefault();
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.bootChanged = true;
	},
	muTimestamp: 0,
	click(e) {
		var trg = e.target;
		if (trg.nodeName == "menu") {
			if (e.button > 1) self.boot(trg);
			else if (Cu.now() - self.muTimestamp > 50)
				e.view.closeMenus(trg.menupopup),
				self.restoreSession(trg.label, (e.button || e.ctrlKey) && e.view);
		}
		else if (trg != this || e.button) return;
		e.view.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	async command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r"))
			arg = trg.parentNode.parentNode.label;
		await this[cmd](arg) || this.save();
	},

	dragstart(e) {
		var trg = e.target;
		if (trg.nodeName != "menu") return;

		var popup = trg.parentNode;
		this.dragData = {trg, mouse: true};
		trg.menupopup.hidePopup();

		var win = trg.ownerGlobal;
		win.setCursor("grabbing");
		var {width} = trg.getBoundingClientRect();
		trg.setAttribute("maxwidth", width);

		win.addEventListener("mouseup", this);
		popup.addEventListener("mousemove", this);
	},
	mousemove(e) {
		var trg = e.target, dtrg = this.dragData.trg;
		if (trg == dtrg || trg.nodeName != "menu") return;

		e.movementY > 0
			? trg.nextSibling != dtrg && trg.after(dtrg)
			: trg.previousSibling != dtrg && trg.before(dtrg);
	},
	mouseup(arg) {
		if (arg.constructor.isInstance?.(arg)) {
			arg.preventDefault();
			var {trg} = this.dragData;
			this.dragData.mouse = false;
			this.muTimestamp = Cu.now();
		}
		else var trg = arg;

		trg.removeAttribute("maxwidth");
		trg.ownerGlobal.setCursor("auto");
		trg.ownerGlobal.removeEventListener("mouseup", this);
		trg.parentNode.removeEventListener("mousemove", this);
	},

	popuphidden(e) {
		if (!e.target.id) return;
		e.view.removeEventListener("keydown", this, true);
		var popup = e.target;
		popup.parentNode.phTimestamp = Cu.now();

		var save;
		if (this.dragData) {
			var {trg, mouse} = this.dragData;
			mouse && this.mouseup(trg);

			delete this.dragData;
			var order = Array.from(popup.getElementsByTagName("menu"), m => m.label);
			if (order.toString() != this.meta.order.toString()) {
				var hasBoot = this.meta.boot != null;
				if (hasBoot) var bootName = this.meta.order[this.meta.boot];
				this.meta.order = order;
				if (hasBoot) this.meta.boot = this.meta.order.indexOf(bootName);
				save = true;
			}
		}
		if (popup.bootChanged) {
			delete popup.bootChanged;
			var {boot} = this.meta;
			var bootNode = e.target.querySelector("[boot]");
			var ind = bootNode && this.meta.order.indexOf(bootNode.label);
			if (ind != boot)
				this.meta.boot = ind,
				save = true;
		}
		save && this.save(e.view);
	},

	sku: `#${pid} > menu[maxwidth]`,
	skd: `#${pid} > menu:is([maxwidth],[_moz-menuactive]):not([open])`,
	keydown(e) {
		if (e.repeat && e.key == "Shift" || !e.shiftKey && e.key != " ") return;
		var func = this.keyHandlers[e.key];
		if (!func) return;

		var menu = e.view.windowRoot
			.ownerGlobal.document.querySelector(this.skd);
		if (menu)
			e.stopImmediatePropagation(),
			func.call(this, menu, e);
	},
	keyup(e) {
		if (e.key != "Shift") return;
		var win = e.view.windowRoot.ownerGlobal;
		win.removeEventListener("keyup", this, true);
		win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
	},
	keyHandlers: {
		Enter(menu) {
			this.boot(menu);
		},
		ArrowDown(menu) {
			var ns = menu.nextSibling;
			if (ns) ns.after(menu), this.arrow(menu);
		},
		ArrowUp(menu) {
			var ps = menu.previousSibling;
			if (ps.nodeName == "menu") ps.before(menu), this.arrow(menu);
		},
		" "(menu, e) {
			e.preventDefault();
			menu.ownerGlobal.closeMenus(menu.parentNode);
			this.restoreSession(menu.label, e.ctrlKey && menu.ownerGlobal);
		}
	},
	arrow(menu) {
		if (menu.hasAttribute("maxwidth")) return;
		menu.setAttribute("maxwidth", menu.getBoundingClientRect().width);
		menu.ownerGlobal.addEventListener("keyup", this, true);
		this.dragData = {trg: menu};
	},

	fill(e) {
		var mxe = e.view.MozXULElement;

		var initFrag = mxe.parseXULToFragment(`
			<menuitem value="saveSession" class="menuitem-iconic" label="Сохранить сессию"/>
			<menuitem value="deleteAllSessions" class="menuitem-iconic" label="Удалить все сессии"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<menuitem label="Переименовать"
				class="menuitem-iconic" value="renameSession"/>
			<menuitem label="Удалить"
				class="menuitem-iconic" value="removeSession"/>
		</menupopup></menu>`);

		this.regStyle();

		var filler = {fill: e => e.target.id
			? e.view.addEventListener("keydown", this, true)
				|| e.target.fillFlag || this.fillSessions(e.target)
			: this.dragData?.mouse && e.preventDefault()
		};

		(this.fill = e => {
			var trg = e.target;
			trg.setAttribute("context", "");
			trg.append(trg.ownerDocument.importNode(initFrag, true));
			(trg.filler = filler).fill(e);
			trg.addEventListener("dragstart", this);
			trg.addEventListener("popuphidden", this);
		})(e);
	},
	fillSessions(popup) {
		while(popup.lastChild.nodeName == "menu") popup.lastChild.remove();
		var ind = 0, {boot} = this.meta;
		for(var name of this.meta.order) {
			var df = popup.ownerDocument.importNode(this.menuFrag, true);
			df.firstChild.setAttribute("label", name);
			if (ind++ == boot) df.firstChild.toggleAttribute("boot");
			popup.append(df);
		}
		popup.fillFlag = true;
	},
	regStyle() {
		delete this.regStyle;
		var subst = "ucf-ssm-style-resurl";
		Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(
			subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(`
			@-moz-document url-prefix(chrome://browser/content/browser.xhtml) {
				#${pid} > menu {
					list-style-image: url("${this.image}");
				}
				#${pid} > [value=saveSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKVSURBVHjajFNNTBNREJ6+7na73bLbQn8I0BKNCYWKGr0oYoiJPxeNh6oHozcOxgSVi4le1JNHQRPigRsewURNPKiJMQicSISUACelRVJ2tz/bdktpy9adBzT8xTjJ5L35ezNv5hsLHEAv7noEntguChx7EuV8oTydKRhfno8kCnt9LTuFZ7f87mAz/8rnl86GOoOBBq+LQX1S1ioLs0txWdEmsulS38Phlcy+BwZ6W0L+JsfolcjpsFDnBKNcgQ193bRUwSrwQFgr6Dkd3o9NzilqMdI/FF+sPYCZ245I4zdunw9DtQr6LxlK6bVdpdrcPAiHfGBYLDD69ltUXsmfw0oIGoPNjkHMjMHa3J9asDyRpoyEOrQR0+dq5MxR0c29Rj3Bhvn8YjeWjZmN9cpWcApSkzpkptYg8T1FdWjTfysg1Ang9YtdT+80OogTbJc7OlsD+OftzOqsBrMflqHe6QKXIEL00zLV0UpSBbM/G9DeEQy6OXKJsfPsCbdXYjb0Yu2/nmMS9LSGYGlYpXLPkxCwkq1mR98Gn8Q4XexxYhhgYKf3TPTfRDaPqklkvViaSSpaxSpw/x1v5e0UG8W10k+iW8qfF2ZiccIydFQ7ScsXKO8ep4NiYj4ai6k5+Eoev1F1Wcn8QJDgnAnHbGYRWTh800cZ77Ryzmr6eCGXzYGiZCcQ2vQ32XT5PiIMTJBI4Waw1ZtZzLu7XaSMd1s9b9paKJA+vpuKqols3y4ov7wXaPN67GPXIl1hnDOOansyVicHhGHMzHkzeDKqqPnr/UOJxX3LNNDb5BJd3KC3Uexu7wgEGxq3limRqcxH47FVWRtPreYePBpJagdu4zYhwjx1cMFus59CuVgqTmPDDlrnvwIMAGS8IFCLagHpAAAAAElFTkSuQmCC");
				}
				#${pid} [value=restoreSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACzElEQVQ4jX2QPWwbZRjHf+/5vfPdObZjO3Ga2pVpGtxCP4QIDKgqDCDUNQwgJCS2qFI3hBCRCvXSCcTAAu0GY0Fi4kuq+IjUCpWAgCTq4IpS3Di2G8c+x/bZ57t7mSJFMfBf/3p+z+95BPvy3pufxqKmcVY3ZFFKmQtFGIReuNnrDze6lv5TqfSyx4EIgA/e+fzkZML6MDmVnD80mz1kxW3DMAxQCs/z6Hb6g3q1Xt1ttdfa7dbFN668vrkHkADJhPntufNnc0LTDi7AjFkkUknzcGF2LvCDuZVvfiwCj+/1GoAmhL3x6x2cZhsVqjFIGIZs15ts/LJOJCLt/Z0EiE3Y9eLpR1ONaoMH9zbHAGiC9FSKkwunWF1Z3RoDABhRg/zRPPmj+XHA/0QDaLectFLj6gcT+AHdTm9mzMC27e766lrWtCwy2Qz2hIUeNVDAaDCku9ujWW/ij3wsO9oaMzCiunf66TMU5gt4wyGbf1Upr5W5u15mq1JDhYqZQo6HIuqtueLw4vLXzwC89NZXeQFw/ePv7zx5buHEf6nf3qgMVipOGE0ko6EKhoQi3NnZ+c51BwsaQLOxM9N1uv86vNtz+aHSDn0pH9S2tl6r/Pn3s/VG/WYunzs/NZ2OSID0dKpRrzZS98v3kYbEsi1004BQcbtc9Q07bdYb229/dvmFL5aWVvVts9mq1Wo/e54vJIAQqGOPzQHgj3zcvos39BAIBshRqJQYjYZ/AFy79tQIeHVx+ctiIDRTA+g43Xud9q4PIHVJPBknk82QzqY5VZg2UUrEYhOX9s5aXL5RnMykb8Rj9kcC4OrSVZ1Hpt+PJ5PPz+SzR5KpyYRpGgRKMei7fHLr7lBmZ1XHcX4LgsCxLPt4JBLRm82Hr4j9DyuVSprdO/ZELGafkXpkXtPA94OK03Vv/R4/csEw5XOGbuieN6y6bv/S9XdfvPkPTgcoDlpQJpwAAAAASUVORK5CYII=");
				}
				#${pid} [value=renameSession] {
					list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPFJREFUeNrEU0sOgkAMbc1IIHoQT+KeY7hkBW5ccwLdsOICxI2X8peIGhggI21gggpCwsImM01f+tpOO0WlFIwR0efgeR5nsG0boihizPd91A5UwZDjuq5qwycwUkQYhttSr4Y4l76fDdtBEAQqz3OVZdnPQ09o2sQhrpBSQgnAen/qzLxZzljHcawxy7KAuCJNUyiKAm53SS0FBGQNlUW3lFO2iVCLYRhAXJEkCQPn65NGAswpL/4fWAebt1ZGXB3geHlw9maXsKqjS94CHNxF7xRM0/wKgI7jDBojvbnZAz3Gv/9E7NvGvmXCsev8EmAAWocA9ofpaRIAAAAASUVORK5CYII=");
				}
				#${pid} :is([value=removeSession], [value=deleteAllSessions]) {
					list-style-image: url("data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABt0lEQVQ4ja2RT2sTURTFz8tk0nntFAQrVLBQxIUg/tkkMoLyIEVECHZhNrrxI+hOP5Mb6eBCcEipYIjZ2LpvhFasUtBSkwzOve+6mE6YSScuxLt67753fudyLvA/a7T+qCHmiTfrfWCMd3j9diPfq2SHX+uPnzJx71gP33w3bX9avBcE2j+SkK30Di7ffJ71VebMxD1hC2GGEEfH41prpftynIm92A0t2aYQQYgBThrndz/2KwCgf9Z2LNtImFOAtc356ijcCwJdJrZEUWzPfJpMAAAStPWR/h2Ktc10CoIQRUKEabFNvNbKfndcAKSjtrVfHW5YorWTzxDKYAxLtOWM+P7yt51hIYPpsDTNTyB/EwNAtWxdMuV8GCfbgxjxIviUYSV/SQOrbVjitbx4N8alBeXcdZR+3Tl3xS8FlIkt0dbnWH3xlbPgKgUo3HEq+tX7C4EuAAbGeOmqCuLIJt69s3PeQ1epKGfapCQJO6vGmwAWf/C1Wau6td8dV123BaAAUfHw6gSwtP3ugxA/y6X9INszAOQgbwFAIC/MQb9/Kv2vF2/UByejlVVn1Xiby/X6rPd/qj/1ak71UYKuwQAAAABJRU5ErkJggg==");
				}
				#${pid} > menuseparator:last-child,
				#${pid} > menu[maxwidth] > .menu-right,
				#${pid} > [value=deleteAllSessions]:nth-last-child(2) {
					display: none;
				}
				#${pid} > menu[boot] {
					color: red;
					font-weight: bold;
				}
				#${pid} > menu[maxwidth] {
					color: blue;
					font-weight: bold;
					outline-offset: -2px;
					outline: 2px solid orangered;
				}
			}
		`.replace(/;$/gm, " !important;"))));
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		sss.loadAndRegisterSheet(Services.io.newURI("resource://" + subst), sss.USER_SHEET);
	},

	get gs() {
		delete this.gs;
		return this.gs = Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {});
	},
	splice(name, newName) {
		var ind = this.meta.order.indexOf(name);
		if (ind == -1) return;
		var args = [ind, 1];

		if (1 in arguments) args.push(newName);
		else {
			if (ind == this.meta.boot) this.meta.boot = null;
			else if (ind < this.meta.boot) this.meta.boot--;
		}
		this.meta.order.splice(...args);
	},

	get meta() {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("simple_session_manager.json");
		this.path = file.path;
		try {
			this.data = JSON.parse(Cu.readUTF8File(file));
		} catch {
			this.pp = file.parent.path;
			this.data = Object.create(null);
		}
		var meta = this.data[mp];
		if (!meta) {
			var order = Object.keys(this.data);
			meta = this.data[mp] = {order, boot: null};
		}
		delete this.meta;
		return this.meta = meta;
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils;
		if (this.pp)
			await io.makeDirectory(this.pp), delete this.pp;
		(this.save = excWin => {
			this.meta.order.length
				? io.writeJSON(this.path, this.data)
				: io.remove(this.path, {ignoreAbsent: true});
			for(var win of CustomizableUI.windows) {
				if (win == excWin) continue;
				var popup = win.document.getElementById(pid);
				if (popup) popup.fillFlag = false;
			}
		})(excWin);
	},

	get prompter() {
		var {prompt} = Services;
		var p = {}, args = [null, null, "UCF Simple Session Manager"];
		p.alert = prompt.alert.bind(...args);
		p.confirm = prompt.confirm.bind(...args);
		var pr = prompt.prompt.bind(...args);
		p.prompt = (msg, value) => {
			var res = {value};
			return pr(msg, res, null, {}) ? res.value : null;
		}
		delete this.prompter;
		return this.prompter = p;
	},
	observe(s, t, data) {
		Services.obs.removeObserver(this, "quit-application");
		if (data.includes("restart")) return;

		var {boot} = this.meta;
		if (boot == null) return;

		var state = this.data[this.meta.order[boot]];
		var ssi = this.gs.SessionStoreInternal;
		ssi.getCurrentState = () => state;
		Services.obs.removeObserver(ssi, "browser:purge-session-history");

		Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
	},

	get bwt() {
		delete this.bwt;
		var url = "resource:///modules/BrowserWindowTracker.jsm";
		return this.bwt = ChromeUtils.import(url).BrowserWindowTracker;
	},
	getName(state) {
		var wl = state.windows.length, tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		} ${wl}/${tl} [${
			new Date().toLocaleString("mn").replace(" ", "-")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	get spref() {
		var pref = "browser.sessionstore.interval";
		var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
		var wait = cb => timer.initWithCallback(cb, 1e3, timer.TYPE_ONE_SHOT);
		delete this.spref;
		return this.spref = async cb => {
			var val = Services.prefs.getIntPref(pref);
			Services.prefs.setIntPref(pref, 100);
			await new Promise(wait);
			Services.prefs.setIntPref(pref, val);
		}
	},
	async saveSession(name = this.getName(this.getState())) {
		var name = this.prompter.prompt("Сохранить:", name);
		if (name == null) return true;

		if (this.exists(name)) return this.saveSession(name);

		await this.spref();
		this.data[name] = this.getState();

		this.meta.order.push(name);
		//this.meta.order.unshift(name);
		//if (this.meta.boot != null) this.meta.boot++;
	},
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
	renameSession(name, newName = name) {
		var newName = this.prompter.prompt(`Переименовать "${name}" в:`, newName);
		if (newName == null || newName == name) return true;

		if (this.exists(newName)) return this.renameSession(name, newName);

		var {data} = this;
		this.splice(name, newName);
		data[newName] = data[name];
		delete data[name];
	},
	removeSession(name) {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить ${name} ?`))
			return true;
		delete this.data[name];
		this.splice(name);
	},
	deleteAllSessions() {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить все сессии?`))
			return true;
		delete this.dragData;
		delete this.bwt.getTopWindow().document.getElementById(pid).bootChanged;
		this.meta = (this.data = Object.create(null))[mp] = {order: [], boot: null};
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");

Dumby пишет

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

Спасибо!

Dumby пишет

Вот и займись этим. Если туда еще добавить про двиганье мышью,
и все клавиатурные аналоги мышиных действий, будет капитально.

Доработал код Менеджера сессий, добавил описание: (про двиганье мышью записал только сортирровку перетаскиванием)
Сделал обновление времени browser.sessionstore.interval в подсказке и нашёл такие возможности, может что-то пропустил:

Выделить код

Код:

tooltiptext: `Менеджер сессий: Браузер каждые 0.25 мин
сохраняет сессии, это снижает ресурс SSD\n
Правый клик на Имени сессии:
	Выделить и Открывать при запуске
Колёсико или Клик + Ctrl:
	Открыть сессию в новых вкладках
Сортировка: тащите строки мышью
или курсором, удерживая Shift`,
………
		btn.addEventListener("mouseenter", this);
………
			btn.removeEventListener("mouseenter", this);
………
	mouseenter(e) { // обновить время сохранения сессии браузера
		e.view.document.getElementById(self.id).tooltipText = self.tooltiptext.replace(new RegExp(/ые .* ми/,''),'ые '+ Services.prefs.getIntPref('browser.sessionstore.interval',15e3)/60e3 + ' ми');
	},

Всем привет! Значительно переработал расширенный профиль Firefox (в шапке темы), добавил скриптов и сократил объём архива до 3,3 Мб:


Визуальное представление: 4 режима Прокси разным фоном ≡ Меню, запрет графики фоном кнопки ⤓ «Загрузки», различные сообщения статуса при наведении на кнопки, например показ пути к папке Загрузок, масштаб шрифта, переключение прокси, предупреждение о включенном HTTPS и прочее…


На любых кнопках разрешены все клики мыши ⦺, градиентный Прогресс загрузки страниц, Четыре режима чтения, Меню переключения скрытых настроек (долгое нажатие 0.5 сек на пункте меню откроет эту опцию в about:config), Управления дополнениями, расширенный Инспектор атрибутов, Добавление закладки без запроса, Перевод сайта/текста, Проиграть/скачать видео или ссылку программой из контекстного меню, Поиск похожих фото, Сохранение картинок, которые нельзя сохранить обычным способом, Жесты мыши например перетаскивание ссылки вправо копирует её в буфер, Расположение страниц в Закладках показывается в подсказке Звёздочки, Авто-коррекция имён вкладок, Яркость колёсиком ± на Замке, показ Ссылок и прочее… — читайте подсказки кнопкок при наведении мыши и встроенную Справку (долгий клик по кнопке «Печать»).

Возможности кнопок вкратце: ◧ левая кнопка мыши, ◨ правая, ⦿ средняя
1) ⤓ кнопка Загрузки добавлены клики мыши, в списке есть Пауза\Старт
    Правый клик: сохранить Страницу | Выделен. текст в единый .html
    ◨ клик + Shift Графика вкл/откл, Левый клик + Alt папка Загрузки
    Колёсико ⦿ клик мыши: Сохранить | Выделенный текст как файл .txt
    Alt+⇧+S сохранить Страницу в единый html расширением SingleFile
    единый html - файл, содержащий все данные: графику, текст, стили…

2) ≡ стандартная кнопка Меню — вне курсора составной значок
    ◧ Левый+Alt или Колёсико: Развернуть | Восстановить окно
    ⩉ Ролик вверх: Полный экран, ◧ Держать кнопку: Закрыть Firefox
    ◨ правый Свернуть, + Shift Вернуть вкладку, ◨ + Alt Диспетчер задач
    В меню ≡ Диалог сохранения "Страница / Выбранное в единый HTML"

3) Избранное + боковая панель с кнопками, Колёсико ± Масштаб
    Клик мыши: открыть слева Журнал, ◧ + Shift - zoom Текст/страница
    ◧ левый клик мыши +Alt: Заладки, ◧ держать: Вкл/Выкл Антизапрет
    ⦿ Колёсико – «Топ сайтов», ◨ правый клик Меню настроек

4) Менеджер сессий — сохранить вкладки и положение страниц в базу
    ◨ клик на имени: Выделить и Открывать эту сессию при запуске
    ⦿ колёсико или Клик + Ctrl: Открыть сессию в новых вкладках

удалите папку startupCache перед запуском, рекомендуется Firefox 90+
Установите Demo-профиль согласно структуре папок. Запуск firefox -P user

Dumby пишет

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

А можно ещё сделать что бы работал на кнопках CB
Add, и ещё кнопку для перезапуска [firefox] с удалением папки startupCache

kokoss пишет

А можно ещё сделать что бы работал на кнопках CB

Не требуется, «скрытый текст» и так работает на кнопках CB.

ещё кнопку для перезапуска [firefox] с удалением папки startupCache

Ууу, сам бы от такой не отказался.
Папка в лисьем использовании, и её удаление, на первый взгляд, не представляется возможным.

Dumby пишет

Не требуется, ... и так работает на кнопках CB.

Проверял в предыдущей версии UCF. В актуальной версии UCF у меня тоже не работает, походу не туда запихнул, пока не до конца разобрался куда добавлять...

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

Надеюсь такая возможность со временем появится!

kokoss пишет

и ещё кнопку для перезапуска [firefox] с удалением папки startupCache

Есть перезапуск для меню или горячая клавиша Ctrl+Alt+Q. В меню гамбургера не показывает иконку. https://forum.mozilla-russia.org/viewto … 07#p785107

xrun1 пишет

В меню гамбургера не показывает иконку. https://forum.mozilla-russia.org/viewto … 07#p785107

Это не то, таких кнопок у меня несколько


Add, просто что бы заработали некоторые скрипты, приходится удалять эту папку

Dumby пишет

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

Я сейчас проверил. Удаляется. Там ещё файл появляется .startup-incomplete, но он или само-удаляется при удалении папки, либо просто исчезает через некоторое время после запуска [firefox].

xrun1 пишет

Есть перезапуск для меню или горячая клавиша Ctrl+Alt+Q. В меню гамбургера не показывает иконку.

С иконкой

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

Выделить код

Код:

//Кнопка перезагрузки
(this.menusrestartitems = {
            init(that) {
                var btnClass = "ucf-appmenu-restart-button", muimID = "ucf_menu_FileRestartItem",
                ucf_script = (window.ucf_custom_script_win == that) ? "ucf_custom_script_win" : "ucf_custom_script_all_win";
                var abtns = document.querySelector("template#appMenu-viewCache")?.content.querySelectorAll("#appMenu-quit-button, #appMenu-quit-button2")
                    || document.querySelectorAll("#appMenu-quit-button");
                for (let abtn of abtns) {
                    let frag = MozXULElement.parseXULToFragment(`<toolbarbutton/>`);
                    let btn = frag.firstElementChild;
                    btn.id = btnClass;
                    btn.className = "subviewbutton subviewbutton-iconic";
                    btn.setAttribute("label", "Перезапуск");
                    btn.setAttribute("tooltiptext", "ЛКМ: Перезапустить приложение\nСКМ: Перезапустить без дополнений\nПКМ: Перезапустить и заново создать кэш быстрого запуска");
                    btn.setAttribute("shortcut", "Ctrl+Alt+Q");
                    btn.setAttribute("onclick", `${ucf_script}.menusrestartitems.restart_mozilla(event)`);
                    abtn.before(frag);
                }
                var aftermuim = document.querySelector("#menu_FilePopup #menu_FileQuitItem");
                if (aftermuim) {
                    let muim = document.createXULElement("menuitem");
                    muim.id = muimID;
                    muim.className = "menuitem-iconic";
                    muim.setAttribute("label", "Перезапуск");
                    muim.setAttribute("tooltiptext", "ЛКМ: Перезапустить приложение\nСКМ: Перезапустить без дополнений\nПКМ: Перезапустить и заново создать кэш быстрого запуска");
                    muim.setAttribute("acceltext", "Ctrl+Alt+Q");
                    muim.setAttribute("context", "");
                    muim.setAttribute("onclick", `${ucf_script}.menusrestartitems.restart_mozilla(event)`);
                    aftermuim.before(muim);
                }
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #${btnClass}.subviewbutton-iconic, #${muimID} {
                        list-style-image: url("chrome://global/skin/icons/reload.svg") !important;
                    }
                    #${btnClass}.subviewbutton-iconic .toolbarbutton-icon,
                    #${muimID} .menu-iconic-icon {
                        -moz-context-properties: fill !important;
                        fill: #e31b5d !important;
                    }
                `);
                try {
                    windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
                } catch (e) {}
                window.addEventListener("keydown", this);
                that.unloadlisteners.push("menusrestartitems");
            },
            restart_mozilla(e) {
                if (e.button == 0)
                    this._restart_mozilla();
                else if (e.button == 1)
                    e.view.safeModeRestart();
                else if (e.button == 2)
                    this._restart_mozilla(true);
            },
            _restart_mozilla(nocache = false) {
                var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
                if (cancelQuit.data)
                    return false;
                if (nocache)
                    Services.appinfo.invalidateCachesOnRestart();
                var restart = Services.startup;
                restart.quit(restart.eAttemptQuit | restart.eRestart);
            },
            handleEvent(e) {
                if (e.code == "KeyQ" && e.ctrlKey && e.altKey)
                    this._restart_mozilla();
            },
            destructor() {
                window.removeEventListener("keydown", this);
            }
        }).init(this);

xrun1 пишет

проверил

Увы, это не то, что можно «проверить».


Services.appinfo.invalidateCachesOnRestart(), разумеется, в основном, работает.
Иначе был бы соответствующий баг, STR, и всё такое.


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


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

egorsemenov06
Спасибо. А то был непорядок.
Dumby
Можно удалить ведь и системной командой cmd /c rd /s /q "путь к папке\startupCache" > nul 2>&1 перед перезапуском. Не элегантное решение (окно cmd мелькает), зато результативное.
Но Вам виднее.

DEL.

Dumby
В full_theme есть скрипт setattributechromemargin.js, он отвечает за смещение области chrome относительно рамки окна ОС, в том числе с учетом Aero в Win 7.
На [firefox] 100 скрипт перестал работать правильно, как минимум для Win 7.
Нижние углы окна [firefox] 100 :
1ae053102af052fbabdcbc6ff8506d92.png 279614fa4ac6aecfafd962f9885d72fb.png
левая и верхняя кромки в порядке.
   
Вы можете поправить это? Изменение значений результата не дает.
full_theme_220116.zip  user_chrome_files_211113.zip

_zt пишет

На [firefox] 100 скрипт перестал работать правильно

Надеюсь это только
Bug 1754547 - Provide a @media query to target major platform/toolkits (Firefox 99+)
То есть, поменяли -moz-os-version на -moz-platform

Вы можете поправить это?

Тут бы хорошо что-то более персонализированное говорить.


Если не собираешься использовать скрипт на других осях
(где он проведёт проверку, увидит, что это не Win7(8), и ничего не сделает),
то и проверка не нужна, просто удали её (третья строка в setattributechromemargin.js).


Иначе, если обратная совместимость не нужна,
то просто замени -moz-os-version на -moz-platform (в двух местах).


Иначе пишем какую-нибудь дополнительную проверку.
Вот, например, более отвязный вариант скрипта, не как рекомендация,
а просто посмотреть вариант проверки (вторая, третья и четвёртая строки).

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

Выделить код

Код:

(async tit => {
	if (AppConstants.platform != "win") return;
	var key = parseInt(Services.appinfo.platformVersion) >= 99 ? "platform" : "os-version";
	if (!matchMedia(`(-moz-${key}: windows-win7), (-moz-${key}: windows-win8)`).matches) return;

	Object.assign(tit, eval(`({${tit._update}})`.replace('"0,2,2,2"', "this.margin")));
	var glass = matchMedia("(-moz-windows-glass)");
	(glass.onchange = () => {
		tit.margin = glass.matches ? "0,7,7,7" : "0,0,0,0";
		tit.enabled && document.documentElement.setAttribute("chromemargin", tit.margin);
	})();
})(window.TabsInTitlebar);

Dumby
Спасибо. И второй и третий варианты на Win 7 работают.

Dumby
Вы можете этот скрипт изменить, что бы он открывал библиотеку в текущей вкладке, если вызван из библиотеки открытой во вкладке?
userChrome.js/openLibraryContextMenu.uc.js · alice0775 · GitHub
   
Что бы скрипт заработал во вкладке, надо удалить в нем строку
if (location.href == "chrome://browser/content/places/places.xhtml") return;
или поставить ! после скобки.
Автору бестолку писать.
   
Вообще, хорошо бы еще и пункт скрывать при пустой строке поиска.
А в идеале, написать новый скрипт, что бы он работал везде и переходил в папку в том документе из которого вызван: библиотека, библиотека во вкладке,
сайдбар, Sidebar Tabs.

_zt
Так есть же. Разве нет?


Или сугубо под 91?
Вроде нашёлся какой-то древний код (для custom_script.js), может подойдёт?
Если нет, то подкинь ещё подробностей.
Это я к тому, что сейчас времени нет, но если появится, то могут пригодиться.

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

Выделить код

Код:

try {({
	run(func) {
		var topics = ["quit-application-granted", "chrome-document-loaded"];
		var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
		for(var t of topics) obs.addObserver(this, t, false);
		this.observe = (subj, topic) => this[topic[0]](subj);
		this.q = () => topics.forEach(t => obs.removeObserver(this, t));

		this.run = async doc => {
			var code = `(${func})(document.getElementById("placesContext_editSeparator"));`;
			var ps = await doc.ownerGlobal.ChromeUtils
				.compileScript("data:charset=utf-8," + encodeURIComponent(code));
			(this.run = ps.executeInGlobal.bind(ps))(doc);
		}
		var re = /\/(?:places|bookmarksSidebar)\.xhtml$/;
		this.c = doc => re.test(doc.documentURI) && this.run(doc);
	}
}).run(sep => {
	var label = "\u041F\u0430\u043F\u043A\u0430 \u0437\u0430\u043A\u043B\u0430\u0434\u043A\u0438";
	var popup = sep.parentNode, listener = {
		handleEvent() {
			if (this.shouldHide) return;

			var menuitem = document.createXULElement("menuitem");
			menuitem.setAttribute("label", label);
			menuitem.setAttribute("oncommand", "creator.goParentFolder();");
			menuitem.creator = this;
			sep.before(menuitem);

			this.handleEvent = e => {
				if (e.target != popup) return;
				var sh = this.shouldHide;
				if (Boolean(menuitem.clientHeight) ^ sh) return;
				if ((menuitem.hidden = sh)) return;
				menuitem.disabled = false;
			}
		},
		get shouldHide() {
			var node = popup._view.selectedNodes.length == 1
				&& popup._view.selectedNode;
			return !(node && PlacesUtils.nodeIsBookmark(node)
				&& node.parent.type == node.RESULT_TYPE_QUERY);
		},
		get goParentFolder() {
			var tree = popup._view;
			if (tree.id.startsWith("b")) {
				delete this.library;
				var func = () => this.sidebar(tree);
			} else {
				delete this.sidebar;
				var list = document.getElementById("placesList");
				var func = () => this.library(popup._view, list);
			}
			delete this.goParentFolder;
			return this.goParentFolder = func;
		},
		sidebar(tree) {
			var {bookmarkGuid} = tree.selectedNode;
			if (tree.result.root.uri.startsWith("place:terms="))
				tree.place = tree.place;
			tree.selectItems([bookmarkGuid]);
			this.scroll(tree);
		},
		async library(tree, list) {
			var {bookmarkGuid} = tree.selectedNode;
			var {parentGuid} = await PlacesUtils.bookmarks.fetch(bookmarkGuid);

			if (PlacesUtils.getConcreteItemGuid(list.selectedNode) == parentGuid)
				list.selectItems([PlacesUtils.virtualAllBookmarksGuid]);
			else {
				var rows = list.view._rows, lastRow = rows[rows.length - 1];
				if (lastRow.bookmarkGuid == PlacesUtils.virtualAllBookmarksGuid)
					lastRow.containerOpen = true;
			}
			list.selectItems([parentGuid]);
			this.scroll(list);

			tree.selectItems([bookmarkGuid]);
			await new Promise(requestAnimationFrame);
			this.scroll(tree);
		},
		scroll(tree) {
			var pos = .4, visibleRows = tree.getPageLength();
			var ind = tree.view.selection.currentIndex;
			var first = tree.getFirstVisibleRow();
			var newFirst = ind - pos*visibleRows + 1;
			tree.scrollByLines(Math.round(newFirst - first));
		}
	};
	popup.addEventListener("popupshowing", listener);
	addEventListener("unload", () =>
		popup.removeEventListener("popupshowing", listener)
	, {once: true});
});} catch(ex) {Cu.reportError(ex);}

Dumby
Получается только для 91 нужен, им еще долго пользоваться. В 100 функция работает идеально, открывает папку там где нажат пункт.
Тот код что вы выложили только в окне работает. В окне у меня работает и тот код что по ссылке, да и вообще везде, вот только, вместо перехода на месте, из вкладки переходит в окно, а из Sidebar Tabs в нативный сайдбар.
В общем, как минимум, нужен код для Sidebar Tabs, как максимум аналог функции из [firefox]100.

_zt пишет

код что вы выложили только в окне работает

Быть этого не может.

скришотцы с 91

Выделить код

Код:

data:image/png;base64,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

Dumby
Да, работает. Спасибо. Или с кодировкой что-то не то было или очки пора купить. :) Вопрос закрыт.

Dumby
Посмотрите кнопку Save.
Что хотелось бы пофиксить:
1. Через меню кнопки сохраняет pdf и html не в папку загрузок назначенную в браузере, а в папку загрузок в системном профиле пользователя.
2. Через меню кнопки не сохраняет выбранный текст в файл txt.
3. Через контекстное меню сохраняет выбранный текст в файл txt на рабочий стол.
Хотелось бы пути сохранения перенаправить в назначенную папку, а п.2 починить.
   
У меня ссылки на мод не сохранилось. Последнюю правку делал вроде по этим постам.

js

Выделить код

Код:

// var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
// var {console} = Cu.import("resource://gre/modules/Console.jsm", {});

try {CustomizableUI.createWidget({
	id: "ucf-cbbtn-Save",
	tooltiptext: "Сохранить страницу\n/ часть / выбранное",
	localized: false,
	get initCode() {
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_js/Save_Script.jsm"
		));
	},
	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});
		new win.Function(
			"self,_id,cbu,xhtmlns,addDestructor,addEventListener,gClipboard,custombuttonsUtils",
			this.initCode
		).call(
			btn, btn, this.id, this.cbu,
			"http://www.w3.org/1999/xhtml",
			this.addDestructor.bind(btn),
			this.addEventListener.bind(btn),
			this.gClipboard, this.custombuttonsUtils
		);
	},
	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;
	}
});} catch(ex) {Cu.reportError(ex);}

jsm

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABGklEQVQ4EaWSvQ4BQRSFd/3EUouGksIDiNBLvMAqtVoaiShUHkItIRGd1iN4A0ql2l8wvruxw67FJuR87p05d85OFsP482PKeaVUjWpDDKpwgyAd2GyZprmiPkXAWFbUIvSld2EdgQ5UYApzKLl+xG2+VAuvDjZcoAldQspUI0yAzMXlS+D6e6qEtKlvAVc2o/CqI4sJTMF5Z4Qc6OU2hrw0eq01XZLrjah+ldhYgEeeAJIVbg9CyxMgp3j6kFqAIK15SP/V8ARwOI95ZqhBfRP+QCmVx9+4pv9XiGNc4JPEkxnt+wO0EbbRAVwtwyELfsl6zDpzOoBVFlLwSzIjs86c88cgscbKhgSkYQtByrG5gxPMeJlL6n+6A9I+WoE5cj6LAAAAAElFTkSuQmCC";


var folderpath="E:\Download";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить favicon сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Копировать favicon в base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Копировать изображение / текст в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Сохранить страницу / выбор как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить выбранный текст как TXT", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function savePageToHTML() {
var vert=`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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как PDF файл через сервис 'pdfmyurl.com' .............
function savePageToPDF() {
      var loc = gBrowser.currentURI.spec;
   var vert = "http://pdfmyurl.com?url=" + loc;
  
   gBrowser. loadURI(vert, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
   });
}; 

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

// Сохранить иконку текущего сайта с диалогом сохранения .............
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {

let browserMM = gBrowser.selectedBrowser.messageManager;
        browserMM.addMessageListener('getSelection', function listener(message) {
        var sel = message.data;
       !sel && document.getElementById("cmd_selectAll").doCommand(); 
     
   // создать название файла из заголовка страницы и текущего времени и сохранить текст ....
   var fileTitle = getTabLabel() + '  ' + aDate().replace(/:/g, ".");
   saveURL("data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + ("\r\n\r\n" + sel)), 
                                fileTitle + ".txt", null, false, false, null, window.document);
   !sel && goDoCommand("cmd_selectNone"); 
 browserMM.removeMessageListener('getSelection', listener, true);
});
        browserMM.loadFrameScript('data:,sendAsyncMessage("getSelection", content.document.getSelection().toString())', 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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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() {

 let browserMM = gBrowser.selectedBrowser.messageManager;
 browserMM.addMessageListener('getSelect', function listener(message) {
   // создать текст для записи
   var url = gBrowser.currentURI.spec;
   if (/\.рф/.test(url.host)) url = convertFromUnicode("UTF-8", url);
   
   var time = convertFromUnicode("UTF-8", aDate().replace(/:/g, "."));
   var text = convertFromUnicode("UTF-8", message.data); 
   var title = convertFromUnicode("UTF-8", getTabLabel());
   
   var text = "..............................................................\n"
            + title + " - " + time + "\n" + url + "\n\n" + text + "\n\n\n";
   var text = text.replace(/\u000A/g, "\u000D\u000A").replace(/\u000D\u000D\u000A/g, "\u000D\u000A");

   // путь к файлу и название файла
   var file = Services.dirsvc.get("Desk", Ci.nsIFile); 
   file.append("Save - " + (aDate().replace(/:/g, ".")) + ".txt");
          
   // создать файл с текстом или добавлять текст в файл
   var foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
   file.exists() ? foStream.init(file, 0x02 | 0x10, 0664, 0) : foStream.init(file, 0x02|0x08|0x20, 0666, 0);
   foStream.write(text, text.length);
   foStream.close();
    // всплывающая подсказка дает возможность открыть файл если кликнуть на подсказке
       var notificat = 'Сохранил выделенный текст в файл на рабочий стол'; 
   var image = gBrowser.selectedTab.image || self.image;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
    .showAlertNotification(image, notificat, "Кликни чтобы открыть файл", true, "", (s, t)=> { 
      if (t == 'alertclickcallback') file.launch();
   }, "");
 browserMM.removeMessageListener('getSelect', listener, true);
});
        browserMM.loadFrameScript('data:,sendAsyncMessage("getSelect", content.document.getSelection().toString())', false);

};

// Создать текстовой файл с выделенным текстом в папке профиля и открыть в редакторе .............
function textToEditor() {


 let browserMM = gBrowser.selectedBrowser.messageManager;
 browserMM.addMessageListener('getSelect', function listener(message) {
   // создать текст для записи
    var text = convertFromUnicode("UTF-8", message.data); 
    var file = Services.dirsvc.get('ProfD', 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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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);
    }
    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));
        }
    }`
});

JS подключен в CustomStylesScripts.jsm в секции для custom_js. [firefox] 91, 100.

Dumby
А можно ли сделать скрипт для открытия закладок в контейнере?
Как то есть начиная с сотой версии, сделать для старших версий, в частности для 91 хотелось бы добавить такую возможность.
Знаю, есть для этого дополнение, но я от него отказался по неск. причинам.
Через скрипт было бы неплохо, и чтоб пункт меню был расположен так же, как и в 100+, сразу после "Открыть в новой вкладке"

скрытый текст
Пунт переименован
Image_001.png
скрытый текст
Чувствую я, не придётся мне переходить на 102 ESR. Для меня больше минусов чем плюсов, единственное - плавающие полосы прокрутки впечатлили.
Начиная с 99, указатель переходит в режим захвата для изменения размера окна, ещё не успев дойти до внутреннего края окна, что очень неудобно при использовании кнопок панели меню и автоскрываемой боковой панели, постоянно приходится прицеливаться.
И, как я понял, из-за неотключаемого WebRender, с 92+ у меня пропадают кнопки управления окном в Win7 при использовании инструментов веб-разработчика, если используется нестандартная тема в браузере.
Да ещё и в Ютуб, когда плейлист играет, если открыть сайдбар, когда затемняется страница, то кнопки тоже пропадают
скрытый текст
Image_002.png

sandro79 пишет

Чувствую я, не придётся мне переходить на 102 ESR.

аналогично. вот прям в точку. я никак ночнушку 102, которая будет ЕСР, не могу привести в чувство. вроде и внешний вид сделал аналогичным, и кнопки важные работают, а куча мелочей не поддаются. какой-то дискомфорт необъяснимый. совсем не зашло.

_zt пишет

jsm

Почему jsm? Нет, расширение здесь, наверно,
может быть любым, в том числе и jsm, но это довольно странно.

1. Через меню кнопки сохраняет pdf и html не в папку загрузок назначенную в браузере, а в папку загрузок в системном профиле пользователя.

pdf там через какой-то сторонний сайт, так что это без меня.


А html — я не вижу такого.
Назначил в браузере папку, и сохраняет в эту папку.


Кстати, хорошо бы в savePageToHTML() в строку var vert=`javascript:(function(){…
добавить String.raw, а то там слэши экранирующие. То есть так:
var vert = String.raw`javascript:(function(){…

2. Через меню кнопки не сохраняет выбранный текст в файл txt.

Да, у saveURL() аргументов, определённо, больше.

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

Выделить код

Код:

/*
   saveURL("data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + ("\r\n\r\n" + sel)), 
                                fileTitle + ".txt", null, false, false, null, window.document);
*/
	saveURL(
		"data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\r\n\r\n" + sel),
		fileTitle + ".txt",
		null, false, false, null, null, null,
		gBrowser.selectedBrowser.browsingContext.originAttributes.privateBrowsingId > 0,
		document.nodePrincipal
	);

3. Через контекстное меню сохраняет выбранный текст в файл txt на рабочий стол.

Ну да, так там и задумано, и даже прокомментировано.
Можно заменить var file = Services.dirsvc.get("Desk", Ci.nsIFile); на
try {var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {file = Services.dirsvc.get("Desk", Ci.nsIFile);}


sandro79 пишет

Как то есть начиная с сотой версии, сделать для старших версий, в частности для 91

То есть с этого бага перерисовать?
Хорошо, попробую. Код для custom_script.js

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

Выделить код

Код:

Services.prefs.getBoolPref("browser.privatebrowsing.autostart", false) || (async css => {
	var obs = doc => {
		var win = doc.ownerGlobal;
		if (win.browsingContext.usePrivateBrowsing) return;
		var menuitem = doc.getElementById("placesContext_open:newtab");
		if (!menuitem) return;

		var df = win.MozXULElement.parseXULToFragment(
			`<menu
				id="placesContext_open:newcontainertab"
				label="Открыть в контейнере"
				accesskey="й"
				nodetype="link" node-type="link"
				selectiontype="single" selection-type="single"
			>
				<menupopup
					oncommand="openInContainerTab(event);"
					onpopupshowing="return createUserContextMenu(event, {isContextMenu: true});"/>
			</menu>`
		);
		df.firstChild.firstChild.openInContainerTab = open;
		menuitem.after(df);
		win.location != "chrome://browser/content/browser.xhtml" &&
			win.windowUtils.loadSheetUsingURIString(css, win.windowUtils.AUTHOR_SHEET);
	}
	var open = e => {
		var win = e.view, pui = win.PlacesUIUtils;
		var tn = pui.lastContextMenuTriggerNode;

		if (tn.closest("#managed-bookmarks"))
			var url = tn.link, arg = {};
		else {
			var node = pui.getViewForNode(tn).selectedNode;
			if (!pui.checkURLSecurity(node, win)) return;

			var url = node.uri;
			win.PlacesUtils.nodeIsBookmark(node)
				? pui.markPageAsFollowedBookmark(url)
				: pui.markPageAsTyped(url);

			var js = url.startsWith("javascript:");
			var arg = {
				allowPopups: js,
				allowInheritPrincipal: js,
				inBackground: pui.loadBookmarksInBackground
			};
		}
		arg.userContextId = +e.target.dataset.usercontextid;
		win.openTrustedLinkIn(url, "tab", arg);
	}
	var topic = "chrome-document-loaded";
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(obs, topic);
		Services.obs.removeObserver(quit, t);
	}, "quit-application-granted");
})("chrome://browser/content/usercontext/usercontext.css");

Dumby пишет

То есть с этого бага перерисовать?

Да-да, с него. Новый баг добавлен и отлично работает! Огромное Спасибо! :beer:

Dumby пишет

Почему jsm?

Возможно я сам это добавил, что бы сразу видеть зависимость.
   

Dumby пишет

pdf там через какой-то сторонний сайт, так что это без меня.

Сохраняет то не сайт, а скрипт или браузер, но если бы был браузер, то он сохранял бы стандартно.
   

Dumby пишет

А html — я не вижу такого.

Да, я забыл настроить папку на одном из профилей.
   

Dumby пишет

Да, у saveURL() аргументов, определённо, больше.

C этим все в порядке. Спасибо.
   

Dumby пишет

try {var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {file = Services.dirsvc.get("Desk", Ci.nsIFile);}

1. Так сохраняет в профиль. [firefox] 91, 100
2. И не сохраняет текст из textarea, можно это изменить?

Dumby
Просьба, сделать запускатор для кнопок ATB.

_zt пишет

Сохраняет то не сайт, а скрипт

Скрипт ничего не сохраняет.
Просто открывает урл в текущей вкладке. И всё, больше ничего.


Мне вот стало любопытно, а сохранялка в pdf,
которая у лисы на борту, сохраняет хуже чем веб-сервис?
Если нет, то можно было бы обсудить перевод на лисий код.
Сравнить можно сопоставлением с результатами сохранения,
например, этим WebExtensions (поднастроить его ещё).
Ну, если есть желание, разумеется.

1. Так сохраняет в профиль.

Путь к папке сохранения должен браться из настройки browser.download.dir
То есть в «папку загрузок назначенную в браузере» (если не получилось взять, тогда на рабочий стол).
Если там путь, ведущий в профиль, то всё правильно.

2. И не сохраняет текст из textarea

Даже и не предусмотрено. Как и работа в (i)frame'ах.

можно это изменить?

Вроде не должно быть сложностей, это же контекстное меню,
туда выделенный текст уже проброшен самим браузером.
Функция saveSelectionToFile() на замену

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

Выделить код

Код:

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);
	})();
}

kokoss пишет

Просьба, сделать запускатор для кнопок ATB.

В смысле чтобы кнопки в окне появлялись раньше?
Можно так попробовать (код для custom_script.js).

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

Выделить код

Код:

(async url => {
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var onReady = (e, addon) => {
		if (addon.id == "add_toolbar_buttons@vitaliy.ru")
			onAddon(addon), manager.off("ready", onReady);
	}
	var onAddon = async addon => {
		var mgr = addon.experimentAPIManager;
		var loaded = mgr.getModule("addToolbarButtons").asyncLoaded;
		loaded ? await loaded : mgr.getAPI("addToolbarButtons", addon);

		mgr.global.baseUri = (mgr.global.contExt = addon).baseURL;
		var atb = mgr.global.add_toolbar_buttons;
		style(atb, addon);
		atb.init();
	}
	var style = (atb, addon) => {
		var noop = () => {};
		var sss = atb.styleSS;
		var subst = "v-add-toolbar-buttons-style";
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var shutdown = (e, isAppShutdown) => {
			if (isAppShutdown) return;
			sss.unregisterSheet(...args),
			rph.setSubstitution(subst, null);
			manager.on("ready", onReady);
		}
		(style = async (atb, addon) => {
			atb.loadButtonStyle = atb.removeButtonStyle = noop;
			var css = `@-moz-document url(chrome://browser/content/browser.xhtml) {\n${
				await (await fetch(addon.baseURI.resolve("button.css"))).text()
			}\n}`;
			rph.setSubstitution(subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(css)));
			sss.loadAndRegisterSheet(...args);
			addon.once("shutdown", shutdown);
		})(atb, addon);
	}
	manager.on("ready", onReady);
})("resource://gre/modules/ExtensionParent.jsm");

Dumby пишет

Путь к папке сохранения должен браться из настройки browser.download.dir

Ага, только вы написали менять var file = Services.dirsvc.get("Desk", Ci.nsIFile);, ну я его и заменил, а это совсем не там, менять нужно было var file = Services.dirsvc.get('ProfD', Ci.nsIFile); :)
   

Dumby пишет

Функция saveSelectionToFile() на замену

В конце ";" не нужен?
   

Dumby пишет

Мне вот стало любопытно, а сохранялка в pdf,
которая у лисы на борту, сохраняет хуже чем веб-сервис?

Лучше, так как там хоть немного можно настроить вывод. У меня и без расширений пункт Печать позволяет сохранить PDF. Конечно надо заменить этот атавизм (сервис) на встроенную функцию. Вот только как, с выводом окна настроек или с чтением уже имеющихся настроек и моментальным сохранением? Я за второй вариант, если он возможен (настройки печати браузер сохраняет).

Dumby пишет

Можно так попробовать (код для custom_script.js).
скрытый текст

Благодарю, в актуальной версии UCF работает, а можно сделать что бы работало и в этой версии UCF ?

_zt пишет

менять нужно было var file = Services.dirsvc.get('ProfD', Ci.nsIFile);

Скриншот с форума.

В конце ";" не нужен?

Вообще, после функций не принято, и, за редким исключением, не нужно.
Но если есть сомнение, то можно поставить, хуже не будет.

чтением уже имеющихся настроек и моментальным сохранением

Я так предполагал: ты настроишь тот WebExtensions так, как нужно,
затем выложишь настройки, а я попробую вписать их в код.

вариант публикации

Идём в отладочную консоль аддона, например, открываем вкладку с адресом
about:devtools-toolbox?id=%7B9ab38051-cd73-4e46-b7bd-dc147f6f6b29%7D&type=extension&tool=webconsole


Там запускаем код. Должна открыться вкладка с настройками.
Копируем настройки в пост под спойлер.

Выделить код

Код:

(async () => {
	var data = JSON.stringify(
		await browser.storage.local.get(), null, "\t"
	);
	var url = URL.createObjectURL(
		new Blob([data], {type: "text/plain;charset=utf-8"})
	);
	browser.tabs.create({url})
		.finally(() => URL.revokeObjectURL(url));
})();


Останется определиться с тем, откуда брать
путь к папке для сохранения, и как формировать имя файла.


kokoss пишет

а можно сделать что бы работало и в этой версии UCF ?

Даже не знаю, может так попробуй

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

Выделить код

Код:

…
	//manager.on("ready", onReady);
	var policy = Cu.getGlobalForObject(Cu)
		.WebExtensionPolicy.getByID("add_toolbar_buttons@vitaliy.ru");
	policy ? onAddon(policy.extension) : manager.on("ready", onReady);

Dumby пишет

может так попробуй
скрытый текст

К сожалению у меня не работает. А если для файла config.js ?

заработало

Выделить код

Код:

(async url => {
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var onReady = (e, addon) => {
		if (addon.id == "add_toolbar_buttons@vitaliy.ru")
			onAddon(addon), manager.off("ready", onReady);
	}
	var onAddon = async addon => {
		var mgr = addon.experimentAPIManager;
		var loaded = mgr.getModule("addToolbarButtons").asyncLoaded;
		loaded ? await loaded : mgr.getAPI("addToolbarButtons", addon);

		mgr.global.baseUri = (mgr.global.contExt = addon).baseURL;
		var atb = mgr.global.add_toolbar_buttons;
		style(atb, addon);
		atb.init();
	}
	var style = (atb, addon) => {
		var noop = () => {};
		var sss = atb.styleSS;
		var subst = "v-add-toolbar-buttons-style";
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var shutdown = (e, isAppShutdown) => {
			if (isAppShutdown) return;
			sss.unregisterSheet(...args),
			rph.setSubstitution(subst, null);
			manager.on("ready", onReady);
		}
		(style = async (atb, addon) => {
			atb.loadButtonStyle = atb.removeButtonStyle = noop;
			var css = `@-moz-document url(chrome://browser/content/browser.xhtml) {\n${
				await (await fetch(addon.baseURI.resolve("button.css"))).text()
			}\n}`;
			rph.setSubstitution(subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(css)));
			sss.loadAndRegisterSheet(...args);
			addon.once("shutdown", shutdown);
		})(atb, addon);
	}
    manager.on("ready", onReady);
	var policy = Cu.getGlobalForObject(Cu)
		.WebExtensionPolicy.getByID("add_toolbar_buttons@vitaliy.ru");
	policy ? onAddon(policy.extension) : manager.on("ready", onReady);
})("resource://gre/modules/ExtensionParent.jsm");


thank-you.gif

Dumby пишет

Скриншот с форума.

Ну да, и оно сохраняло в профиль, пока не изменил var file = Services.dirsvc.get('ProfD', Ci.nsIFile);, да еще и папку левую там создавало, с названием как папка загрузок браузера.
Теперь все работает и ладно.
   
Ну пусть так будет, вывел все опции. Если оно в скрипте будет человекочитаемым то всегда изменить можно.

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

Выделить код

Код:

{
	"footerCenter": "",
	"footerLeft": "&PT",
	"footerRight": "&D",
	"headerCenter": "",
	"headerLeft": "&T",
	"headerRight": "&U",
	"marginBottom": 0.2,
	"marginLeft": 0.2,
	"marginRight": 0.2,
	"marginTop": 0.2,
	"orientation": 0,
	"paperHeight": 11,
	"paperSizeUnit": 1,
	"paperWidth": 8.5,
	"scaling": 1,
	"showBackgroundColors": true,
	"showBackgroundImages": false,
	"shrinkToFit": true,
}

Сохранять стандартно, в папку загрузок браузера.
   
И подскажите как добавить в кнопку пункт "Печать" браузера (шоб имя свое задать можно было), сразу после нынешнего (и будущего) "Сохранить страницу в PDF". Преднастройки это хорошо, но хотелось бы иметь возможность настройки из UI.

_zt пишет

    "paperHeight": 11,
    "paperSizeUnit": 1,
    "paperWidth": 8.5,

Странно, paperSizeUnit единица.
1 — это nsIPrintSettings.kPaperSizeMillimeters (миллиметры).


Наверно имелся в виду ноль.
0 — это nsIPrintSettings.kPaperSizeInches (дюймы).
А то получается размер с ноготь мизинца.


Вобщем, пока такой набросок замены функции savePageToPDF()
И, на всякий случай, idl'ки: release и esr91,
вдруг захочется с оставшимися настройками повозиться.

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

Выделить код

Код:

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).newPrintSettings;
	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();
	})();
}

как добавить в кнопку пункт "Печать" браузера (шоб имя свое задать можно было)

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

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

Выделить код

Код:

{ label: "Союзпечать", func: document.getElementById(document.getElementById("appMenu-viewCache").content.querySelector("[key=printKb]").getAttribute("command")).getAttribute("oncommand"), image: "chrome://global/skin/icons/print.svg"},

Dumby пишет

paperSizeUnit

Забыл вернуть, я же пытался все настройки вывести.
В общем, шикарно получилось. Спасибо. И главное без стороннего сервиса и с возможностью настройки.
   
А для открытия списков URL и сохранения списка адресов, выбранных или всех текущих вкладок, есть скрипт? Из буфера обмена / файла, в буфер обмена / файл / закладки.

_zt пишет

В общем, шикарно получилось.

И что получилось, можно итоговый вариант?

voqabuhe

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABGklEQVQ4EaWSvQ4BQRSFd/3EUouGksIDiNBLvMAqtVoaiShUHkItIRGd1iN4A0ql2l8wvruxw67FJuR87p05d85OFsP482PKeaVUjWpDDKpwgyAd2GyZprmiPkXAWFbUIvSld2EdgQ5UYApzKLl+xG2+VAuvDjZcoAldQspUI0yAzMXlS+D6e6qEtKlvAVc2o/CqI4sJTMF5Z4Qc6OU2hrw0eq01XZLrjah+ldhYgEeeAJIVbg9CyxMgp3j6kFqAIK15SP/V8ARwOI95ZqhBfRP+QCmVx9+4pv9XiGNc4JPEkxnt+wO0EbbRAVwtwyELfsl6zDpzOoBVFlLwSzIjs86c88cgscbKhgSkYQtByrG5gxPMeJlL6n+6A9I+WoE5cj6LAAAAAElFTkSuQmCC";


var folderpath="E:\Download";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить favicon сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Копировать favicon в base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Копировать изображение / текст в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Печать / печать в PDF", func: document.getElementById(document.getElementById("appMenu-viewCache").content.querySelector("[key=printKb]").getAttribute("command")).getAttribute("oncommand"), image: "chrome://global/skin/icons/print.svg"},
   { label: "Сохранить страницу / выбор как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить URL вкладки / выбор как TXT", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function 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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как 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).newPrintSettings;
    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();
    })();
};

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
    ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
    : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

// Сохранить иконку текущего сайта с диалогом сохранения .............
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {

let browserMM = gBrowser.selectedBrowser.messageManager;
        browserMM.addMessageListener('getSelection', function listener(message) {
        var sel = message.data;
       !sel && document.getElementById("cmd_selectAll").doCommand(); 
     
   // создать название файла из заголовка страницы и текущего времени и сохранить текст ....
   var fileTitle = getTabLabel() + '  ' + aDate().replace(/:/g, ".");
    saveURL(
        "data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\r\n\r\n" + sel),
        fileTitle + ".txt",
        null, false, false, null, null, null,
        gBrowser.selectedBrowser.browsingContext.originAttributes.privateBrowsingId > 0,
        document.nodePrincipal
    );
   !sel && goDoCommand("cmd_selectNone"); 
 browserMM.removeMessageListener('getSelection', listener, true);
});
        browserMM.loadFrameScript('data:,sendAsyncMessage("getSelection", content.document.getSelection().toString())', 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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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);
    }
    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));
        }
    }`
});

Dumby Хотя... еще бы папку для сохранения иконок и ярлыков сайтов привязать к папке загрузок браузера, иначе - рабочий стол. И вообще там непорядок - нажимаешь сохранить ярлык, он открывает окно и выводит уведомление, что типа уже сохранил, но он еще ничего не сохранял.

_zt
Спасибо.

Dumby
Кнопка Save не сохраняет полностью длинные страницы на этом форуме, если выбрать "Сохранить всю страницу как PNG", низ обрезается. Если возможно, поправь плиз.

_zt пишет

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

Набросок: три фрагмента (если оседание фавиконов в загрузках не ценность).
Первый и второй удалить, третий добавить.

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

Выделить код

Код:

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
    ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
    : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

// Сохранить иконку текущего сайта с диалогом сохранения .............
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};
Выделить код

Код:

function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};
Выделить код

Код:

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, "", fp.modeSave);
	fp.displayDirectory = file;
	fp.defaultString = fileName;
	return await new Promise(fp.open) != fp.returnCancel && fp.file;
}
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
			);
	})();
}

voqabuhe пишет

Если возможно

Говорят что нет.

Dumby
А сохранение скриптом в .pdf без разбивки на страницы тоже никак?

Dumby
Фавиконки не ценность, даже отключены в about:config.
Спасибо, все работает как надо.
   
А для открытия списков URL и сохранения списка URL - адресов выбранных или всех текущих вкладок, есть скрипт? Из буфера обмена / файла, в буфер обмена / файл. А то приходится сохранять папку закладок, потом копировать ее и вставлять в файл, для восстановления делать все в обратном порядке. И хотелось бы еще заголовок видеть перед каждым адресом.

voqabuhe пишет

А сохранение скриптом в .pdf без разбивки на страницы тоже никак?

У меня не получилось какой-нибудь browser.js или about:license.
Поднимаешь paperHeight — сначала качество съезжает, а ещё больше — вообще пустота.


Кстати, imgIEncoder способен собрать PNG большего размера чем canvas,
настолько, что даже сам браузер не сможет его отобразить, только подходящий вьювер.
Так что, для любителей экстремальных вещей, можно его попробовать.
Вот, например, автономный код для запуска в окне браузера, типа с консоли, или вкладка Код в CB.
Следует понимать, что на больши́х страницах может случиться ошибка (или даже краш) out of memory.

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

Выделить код

Код:

(async cid => {
	var fp = makeFilePicker();
	fp.init(window, "Сохранить как…", fp.modeSave);
	fp.appendFilter("", "*.png");
	var lab = DownloadPaths.sanitize(gBrowser.selectedTab.label);
	fp.defaultString = lab.slice(0, 50).trimRight() + ".png";
	var res = await new Promise(fp.open);
	if (res == fp.returnCancel || !fp.file) return;
	var {file} = fp;

	var br = gBrowser.selectedBrowser;
	var name = "extrem-encoder:de-xy-dpr-info";
	var url = "data:,(" + encodeURIComponent((de, name) => sendAsyncMessage(
		name, [de.scrollWidth, de.scrollHeight, content.devicePixelRatio]
	)) + `)(content.document.documentElement, "${name}")`;

	var mm = br.messageManager;
	mm.loadFrameScript(url, false);
	var res = await new Promise(r => {
		var lst = msg => mm.removeMessageListener(name, lst, r(msg.data));
		mm.addMessageListener(name, lst);
	});
	var [width, height, k] = res;
	var rectWidth = width;

	width = Math.floor(width * k);
	height = Math.floor(height * k);

	var step = 2000;
	var rectHeight = step / k;

	var canvas = document.createElement("canvas");
	canvas.width = width;
	canvas.height = step;
	var ctx = canvas.getContext("2d");

	var stride = width * 4;
	var encoder = Cc[cid].createInstance(Ci.imgIEncoder);
	var RGBA = encoder.INPUT_FORMAT_RGBA;
	encoder.startImageEncode(width, height, RGBA, "");

	var cwg = br.browsingContext.currentWindowGlobal;
	for(var y = 0; y < height; y += step) {

		var rect = new DOMRect(0, y / k, rectWidth, rectHeight);
		ctx.drawImage(await cwg.drawSnapshot(rect, k, "white"), 0, 0);

		var {data} = ctx.getImageData(0, 0, width, step);
		encoder.addImageFrame(data, data.length, width, step, stride, RGBA, "");
	}
	encoder.endImageEncode();

 	var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
 	stream.setInputStream(encoder.QueryInterface(Ci.nsIInputStream));
 	var bytes = stream.readByteArray(stream.available());

	await IOUtils.write(file.path, new Uint8Array(bytes));
 	file.reveal();

})("@mozilla.org/image/encoder;2?type=image/png");

_zt пишет

есть скрипт?

Увы, я не знаток существования скриптов.

открытия списков URL и сохранения списка URL

Если формат списка внутренний (только для этого скрипта),
то можно тяп-ляп что-нибудь набросать, например виджет

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	id: "799775",
	label: "799775",
	tooltiptext: "799775",

	localized: false,
	marker: "[ucf_tabs_linkset]\n\n",
	onCreated(btn) {
		btn.setAttribute("image", "resource://usercontext-content/pet.svg");
		btn.setAttribute("type", "menu");
		btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment(
			`<menupopup oncommand="creator.cmd(event);">
				<menuitem label="Сохранить адреса вкладок" value="1"/>
				<menuitem label="Сохранить адреса всех вкладок" value="2"/>
				<menuseparator/>
				<menuitem label="Копировать адреса вкладок" value="3"/>
				<menuitem label="Копировать адреса всех вкладок" value="4"/>
				<menuseparator/>
				<menuitem label="Открыть из файла" value="5"/>
				<menuitem label="Открыть из буфера" value="6"/>
			</menupopup>`
		));
		btn.firstChild.creator = this;
	},
	async cmd(e) {
		var win = e.view, num = e.target.value;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await win.IOUtils.readUTF8(file.path);
		}
		if (!text?.startsWith(this.marker)) return;

		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		var arr = text.split("\n");
		for(var ind = arr.length - 1; ind > 2; ind -= 3)
			var tab = gb.addTab(arr[ind], arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, 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 = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		return await new Promise(fp.open) != fp.returnCancel && fp.file;
	}
}))();

Dumby
Ну может уже делал.
   
Почти то что нужно.
Только вот список URL для открытия может быть создан не скриптом или скопирован откуда угодно и может быть в любом формате, и с любым текстовым мусором помимо адресов.
А вот формат сохранения идеален.
И еще, вот с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере.

_zt пишет

откуда угодно … в любом …  с любым

Я же совершенно не разбираюсь в урлах и их списках.
Конкретного бы чего-нибудь дал, лекцию прочёл.
Ладно, написал какую-то лажу, просто чтобы не ничего.

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

Выделить код

Код:

/*
		if (!text?.startsWith(this.marker)) return;

		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		var arr = text.split("\n");
		for(var ind = arr.length - 1; ind > 2; ind -= 3)
			var tab = gb.addTab(arr[ind], arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
*/
		if (!text) return;
		var own = text.startsWith(this.marker);
		if (own) var arr = text.split("\n");
		else {
			var urls = this.parse(text);
			if (!urls?.length) return;
		}
		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		if (own)
			for(var ind = arr.length - 1; ind > 2; ind -= 3)
				var tab = gb.addTab(arr[ind], arg);
		else 
			for(var url of urls)
				var tab = gb.addTab(url, arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(str);
			else {
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(Boolean);
					for(var sub of arr)
						url = this.url(sub),
						url && result.add(url);
				}
			}
		}
		var {size} = result;
		if (size) {
			result = Array.from(result);
			if (size > 1) result.reverse();
			return result;
		}
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [",", ";", '"', "'", /[\[\]]/, /()/],
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str).spec;
		} catch {}
	},

с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере

Да, мой косяк. Конечно же await win.IOUtils.makeDirectory(file.path);
Это на тот случай, что в настройке путь до несуществующей папки,
что маловероятно, может и правильно, что удалил эту строку.

Dumby
Да вроде нормально. Только вот что заметил:
1. место открытия списков - надо бы что бы всегда открывались после последней вкладки
2. ссылку с запятой в конце адреса открывает вместе с запятой
3. markdown не открывает
4. BB-code с заголовком не открывает
   
5. очень желательно вывести в меню опцию "заменять одиночные пробелы процентами" - "%20", но если после пробела идет любой спецсимвол, то обрезать ссылку до этого пробела.
Так как, многие поисковики отдают ссылки на страницы с поисковыми запросами с пробелами, да и вообще пробелы частенько встречаются, в ссылках на файлы, на самодельных сайтах и т.п.
Это не часто нужно и как правило будет мешать получению правильных ссылок, поэтому лучше сделать это как опцию, если такое возможно.
   

Пример списка с перечисленными проблемами

Выделить код

Код:

Создание гиперссылок - Изучение веб-разработки | MDN
https://developer.mozilla.org/ru/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks // afgaga

Управление дополнениями
about:addons

Ресурс
resource://usercontext-content/pet.svg


HTML
<a href="https://en.wikipedia.org/wiki/Percent-encoding">Percent-encoding - Wikipedia</a>

BB-code без заголовка
[url]https://forum.mozilla-russia.org/index.php[/url]


/* Проблемные ссылки */

Ссылка с запятой в конце адреса (открывает вместе с запятой)
https://github.com/Aris-t2/CustomCSSforFx/blob/master/classic/css/tabs/tabs_multiple_lines.css,

BB-code с заголовком (не открывает)
[url=https://forum.mozilla-russia.org/viewtopic.php?id=76642&p=14]UCF-скрипты на этом форуме | Форум Mozilla Россия[/url]

markdown 1 (не открывает)
[Ссылка на корень сайта](https://planshet-info.ru/kompjutery/ssylka-na-koren-sajta)

markdown 2 (не открывает)
![Image and Preview Themes on the toolbar](https://markdownmonster.west-wind.com/docs/images/EditorPreviewThemeUi.png) 

Ссылка с пробелами (обрезает по первому пробелу)
https://yandex.ru/yandsearch?text=команда на копирование файлов и папок в bat&lr=213


ps^ Исправил пост, сам пытался парсер править и сломал BB-code без заголовка.

_zt
Хорошо, попробую учесть.

если после пробела идет любой спецсимвол

А что за спецсимволы? Написал так: singleSpace: / (?![\s"])/g,
то есть только «\s» и «"», не знаю что ещё.

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

Выделить код

Код:

//.......
	onCreated(btn) {
		btn.setAttribute("image", "resource://usercontext-content/pet.svg");
		btn.setAttribute("type", "menu");
		btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment(
			`<menupopup oncommand="creator.cmd(event);" onpopupshowing="creator.check(this.lastChild);">
				<menuitem label="Сохранить адреса вкладок" value="1"/>
				<menuitem label="Сохранить адреса всех вкладок" value="2"/>
				<menuseparator/>
				<menuitem label="Копировать адреса вкладок" value="3"/>
				<menuitem label="Копировать адреса всех вкладок" value="4"/>
				<menuseparator/>
				<menuitem label="Открыть из файла" value="5"/>
				<menuitem label="Открыть из буфера" value="6"/>
				<menuitem label="Заменять одиночные пробелы процентами" closemenu="none" type="checkbox" value="7"/>
			</menupopup>`
		));
		btn.firstChild.creator = this;
	},
	check(item) {
		var pref = "ucf.linkset-widget.replspace";
		this.checked = Services.prefs.getBoolPref(pref, false);
		this.setPref = Services.prefs.setBoolPref.bind(null, pref);
		(this.check = item => {
			this.checked ? item.setAttribute("checked", true) : item.removeAttribute("checked");
		})(item);
	},
	singleSpace: / (?![\s"])/g,
	async cmd(e) {
		var num = e.target.value;
		if (num == 7)
			return this.setPref(this.checked = e.target.hasAttribute("checked"));
		var win = e.view;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await win.IOUtils.readUTF8(file.path);
		}
		if (!text) return;
		// if (!text || text.length > 500_000) return; // limit?

		var own = text.startsWith(this.marker);
		if (this.checked && !own) text = text.replace(this.singleSpace, "%20");

		var gb = win.gBrowser, tl = gb.visibleTabs.length;
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(arr[ind]);
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(url);
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(str);
			else {
				var bb = true;
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(this.colon);
					for(var sub of arr) {
						if (bb) { // []
							var ind = sub.indexOf("=");
							if (ind != -1 && ind < sub.indexOf(":"))
								sub = sub.slice(ind + 1);
						}
						url = this.url(sub);
						url && result.add(url);
					}
					bb = false;
				}
			}
		}
		// console log instead of open
		//if (true) return Services.console.logStringMessage(Array.from(result).join("\n"));

		//var {size} = result;
		//if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit?

		return result;
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"],

	unwanedEnds: /,$/,
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str.replace(this.unwanedEnds, "")).spec;
		} catch {}
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, fileName) {
		try {
			var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
			await win.IOUtils.makeDirectory(file.path);
		} catch {
			file = Services.dirsvc.get("Desk", Ci.nsIFile);
		}
		var fp = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		return await new Promise(fp.open) != fp.returnCancel && fp.file;
	}
}))();

Dumby
Да почти любой спецсимвол, кроме, пожалуй, скобок (и точка под вопросом). Так как есть нормально, потом всегда можно будет изменить.
   
На мой взгляд осталось два момента...
1. Не открывает ANSI файлы, а это стандартная кодировка Windows.
2. Неверно открывает URL с запятой в конце. При копировании со страниц адресов оформленных тегами (адрес-адрес) они копируются с прилипшей запятой:

Выделить код

Код:

<a href="https://en.wikipedia.org/">https://en.wikipedia.org/</a>,
[https://en.wikipedia.org/](https://en.wikipedia.org/),
[url=https://en.wikipedia.org/]https://en.wikipedia.org/[/url],

Всегда получается "https://en.wikipedia.org/," при копировании.
   
Кстати, там может быть и точка с запятой. Может по аналогии с singleSpace сделать, строку с перечислением отсекающих символов?
Даже не символов, а группы символов, например: ,\s ;\s
   
А в singleSpace: / (?![\s"... экранировать символы нужно и какие, или вообще можно все экранировать?

_zt пишет

Неверно открывает URL с запятой в конце.

Да, есть опечатки в коде.
Про копирование не понял. Поскольку код не занимается
копированием со страниц, то, видимо, это объяснение откуда что берётся.
Точку с запятой добавил в unwantedEnds: /[,;]$/,

Не открывает ANSI файлы, а это стандартная кодировка Windows.

Насколько мне известно, в Firefox нет API для определения кодировки.
Самое простое так: пытаемся прочитать как UTF-8, если ошибка,
тогда читаем бинарную строку и конвертируем в Windows-1251.

или

Есть ещё такой жуткий вариант: загружаем добро в невидимое окно как документ,
где Firefox сам определит кодировку по содержимому, и забираем результат.
В многопроцессном режиме, естественно, грузится не захочет, поэтому ещё
и свой протокол надо регистрировать. Ну и в консоли будет спам, что мол
кодировка не указана, что ожидаемо. Вобщем, только на крайний случай,
если первый вариант не подойдёт.

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

Выделить код

Код:

//.......
	async cmd(e) {
		var num = e.target.value;
		if (num == 7)
			return this.setPref(this.checked = e.target.hasAttribute("checked"));
		var win = e.view;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await this.read(file);
		}
		if (!text) return;
		// if (!text || text.length > 500_000) return; // limit?

		var own = text.startsWith(this.marker);
		if (this.checked && !own) text = text.replace(this.singleSpace, "%20");

		var gb = win.gBrowser, tl = gb.visibleTabs.length;
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(arr[ind]);
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(url);
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
	read(file) {
		var read1251 = async file => {
			var suc = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
				.createInstance(Ci.nsIScriptableUnicodeConverter);
			suc.charset = "windows-1251";
			var reader = new FileReader();

			return (read1251 = async file => {
				reader.readAsBinaryString(file);
				await new Promise(resolve => reader.onloadend = resolve);
				return suc.ConvertToUnicode(reader.result);
			})(file);
		}
		var {IOUtils} = Cu.getGlobalForObject(Cu);
		return (this.read = async file => {
			try {return await IOUtils.readUTF8(file.path);}
			catch {return read1251(file);}
		})(file);
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(url);
			else {
				var bb = true;
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(this.colon);
					for(var sub of arr) {
						if (bb) { // []
							var ind = sub.indexOf("=");
							if (ind != -1 && ind < sub.indexOf(":"))
								sub = sub.slice(ind + 1);
						}
						url = this.url(sub);
						url && result.add(url);
					}
					bb = false;
				}
			}
		}
		// console log instead of open
		//if (true) return Services.console.logStringMessage(Array.from(result).join("\n"));

		//var {size} = result;
		//if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit?

		return result;
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"],

	unwantedEnds: /[,;]$/,
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str.replace(this.unwantedEnds, "")).spec;
		} catch {}
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, fileName) {
		try {
			var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
			await win.IOUtils.makeDirectory(file.path);
		} catch {
			file = Services.dirsvc.get("Desk", Ci.nsIFile);
		}
		var fp = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		if (await new Promise(fp.open) == fp.returnCancel) return;
		if (fileName) var file = fp.file;
		else {
			var file = fp.domFileOrDirectory;
			file.path = fp.file.path;
		}
		return file;
	}
}))();

экранировать символы нужно и какие, или вообще можно все экранировать?

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


А можно экранировать, наоборот, те, которые при экранировании не образуют другой смысл.
Например s соответствует букве «эс», а \s соответствует любому юникодному символу-разделителю,
но z соответствует букве «зэт», и \z тоже соответствует букве «зэт».

Dumby пишет

Про копирование не понял.

Это не про скрипт, это про список, а он может быть откуда угодно, в том числе скопирован со страницы.
   

Dumby пишет

Есть ещё такой жуткий вариант

Как то сложно все. Не надо наверное, я потестирую то что есть.
   
Не нравится мне как это работает - "if (url) result.add(url);" , окно группировки одновременно открытых вкладок TST растягивается во все окно браузера по ширине. Причем, все равно что открываешь, ansi или utf8. В предыдущем варианте текст url не шифровался и окно не растягивало. Речь про адреса с utf8 символами, например, приведенный выше яндекс. Можно это поправить? Может обратную дешифровку сделать при "выводе" из скрипта?

_zt пишет

Может обратную дешифровку сделать при "выводе" из скрипта?

Если именно при открытии вкладок, то можно так попробовать
(это если и для своего, и для сторонних форматов).

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

Выделить код

Код:

/*
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(arr[ind]);
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(url);
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
*/
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(this.decode(arr[ind]));
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(this.decode(url));
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
	decode: url => url.startsWith("data:") ? url : decodeURI(url),


А так-то, было бы неплохо самому Автору дополнения рассмотреть
возможность ограничения ширины списка адресов, а то у меня тут
попался data:… адрес, так окно расколбасило не просто «во все окно браузера по ширине»,
а далеко за пределы экрана, овер шестнадцать тысяч (!) пикселей.
Ну или пользовательский смирительный стиль какой-нибудь запилить.

Dumby
Спасибо, теперь нормально.
   

Dumby пишет

смирительный стиль

Ну так как то.

userContent.css

Выделить код

Код:

@-moz-document url-prefix(moz-extension://uuid) {
/* Не позволять растягивать дополнительные окна больше заданного размера */
.rich-confirm-dialog {
    max-width: 800px !important;
}
}

Dumby
[firefox] 101 в расширение add_toolbar_buttons.2021.9.5.xpi  от Vitaliy V. перестала полноценно работать кнопка Дополнения, не открывается список. Можешь поправить? Или хотя бы скрипт починить из этого расширения №9229?

voqabuhe пишет

перестала полноценно работать кнопка Дополнения, не открывается список

ChromeUtils.import(…, null) сто лет как считался "не по понятиям".
Может замени в parent.js

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

Выделить код

Код:

/*
XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => {
    const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
    return GlobalManager;
});
*/
XPCOMUtils.defineLazyGetter(this, "GlobalManager", () =>
    ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm").ExtensionParent.GlobalManager
);

Dumby

Dumby пишет

ChromeUtils.import(…, null) сто лет как считался "не по понятиям".
Может замени в parent.js

Гениально! Спасибо!

Dumby пишет

Может замени в parent.js
скрытый текст

Благодарю :beer:

Dumby
На [firefox] 102 превьюшки TST улетели в край экрана, можете поправить.

_zt пишет

можете поправить

Следуем за ними.

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

Выделить код

Код:

/*
			var z = win.windowUtils.screenPixelsPerCSSPixel;
*/
			var z = win.devicePixelRatio;

Dumby
Спасибо. Трудно самому такое найти, особенно когда не знаешь чего искать.
   
Этот скрипт не позволяет запустить [firefox] 102, если в scriptschrome: >> load:, а если в  scriptsallchrome: >> load: - крашит [firefox] при вызове соответствующего окна / панели (places\/bookmarksSidebar\.xhtml или places\/places\.xhtml).

_zt пишет

крашит

Ну да, я упоминал. Если грузить скрипт снаружи в объект
глобального системного объекта, то, на ранних бетах и найтли,
разрабы валят пользователю браузер.


Это не касается доживающих последние времена NSVO.
Но Cu.import() больше таковые не возвращает.


Пока сам Cu.import() ещё с нами, можно так попробовать

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

Выделить код

Код:

/*
	if (!g[key]) {
		Services.scriptloader.loadSubScript(
			`data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
		);
		var raws = g[key];
*/
	var raws = g.TransactionsHistory?.proxifiedToRaw;
	if (raws) g = raws;
	if (!g[key]) {
		if (!raws) {
			Services.scriptloader.loadSubScript(
				`data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
			);
			raws = g[key];
		}

Dumby
Работает, спасибо. Эта правка до esr доживет или неизвестно?

_zt пишет

до esr доживет или неизвестно?

Так 102 всё уже, готова. Почти весь отвал делается в Nightly.
Бывают, конечно, исключения, но это редкость.

В [firefox] 101 (может и раньше) отвалилась кнопка "Дополнения" из расширения Add Toolbar Buttons.
Была такая отдельно от 18.09.2020

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

Выделить код

Код:

try {(() => {
    var id = "ucf-aom-button",
    label = "Дополнения",
    tooltiptext = "ЛКМ: Меню дополнений\nShift+ЛКМ: Меню дополнений + открыть менеджер\nСКМ: Открыть менеджер дополнений",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 24,4.5 18,12 3,23.7 12,32.7 3.9,44.1 7.8,48 H 45 C 46.7,48 48,46.7 48,45 V 26.1 L 34.8,12.9 31.8,12.3 Z'/><path style='fill:white;' d='M 19.88,3 C 16.93,3 14.55,4.662 14.55,6.701 14.63,7.474 15.11,8.438 15.37,8.762 16.59,10.41 16.59,11.44 16.29,12.06 H 6.299 C 4.476,12.06 3,13.53 3,15.35 V 23.68 C 3.625,24 4.65,24 6.299,22.77 6.625,22.52 7.587,22.02 8.363,21.94 10.4,21.94 12.06,24.35 12.06,27.29 12.06,30.24 10.4,32.65 8.363,32.65 7.725,32.63 6.774,32.07 6.299,31.82 4.65,30.59 3.625,30.59 3,30.91 V 41.71 C 3,43.53 4.476,45 6.299,45 H 19.58 C 19.88,44.38 19.88,43.35 18.65,41.71 18.4,41.38 17.91,40.42 17.82,39.65 17.82,37.6 20.23,35.94 23.18,35.94 26.14,35.94 28.55,37.6 28.55,39.65 28.53,40.28 27.97,41.23 27.71,41.71 26.47,43.35 26.47,44.38 26.79,45 H 32.65 C 34.47,45 35.96,43.53 35.96,41.71 V 32.55 C 36.56,32.23 37.59,32.23 39.23,33.47 39.72,33.73 40.68,34.29 41.29,34.29 43.35,34.29 45,31.91 45,28.94 45,25.99 43.35,23.59 41.29,23.59 40.54,23.67 39.58,24.17 39.23,24.41 37.59,25.65 36.56,25.65 35.96,25.33 V 15.35 C 35.96,13.53 34.47,12.06 32.65,12.06 H 23.49 C 23.19,11.44 23.19,10.41 24.41,8.762 24.66,8.287 25.22,7.337 25.23,6.713 25.23,4.662 22.85,3 19.88,3' /></g></svg>",
    checked = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><path d='M 4,5 7.5,8.5 12,4 V 8 L 8,12 H 7 L 4,9 Z' style='fill:white'/></svg>",
    show_version = true,
    show_description = true,
    user_permissions = true,
    show_hidden = true,
    show_disabled = true,
    enabled_first = true,
    exceptions_listset = new Set([

    ]);
    exceptions_type_listset = new Set([

    ]);
    if (!("AddonManager" in this))
        ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
    if (!("GlobalManager" in this))
        XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => {
            const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
            return GlobalManager;
        });
    var extensionOptionsMenu = {
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        get clipboardHelp() {
            delete this.clipboardHelp;
            return this.clipboardHelp = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
        },
        get exceptions_type_listarr() {
            delete this.exceptions_type_listarr;
            var arr = ["extension", "theme", "locale", "dictionary"];
            if (!exceptions_type_listset.size)
                return this.exceptions_type_listarr = arr;
            return this.exceptions_type_listarr = arr.filter(type => !exceptions_type_listset.has(type));
        },
        populateMenu: async function(e) {
            var popup = e.target, doc = e.view.document;
            var addons = await AddonManager.getAddonsByTypes(this.exceptions_type_listarr);
            var addonsMap = new WeakMap(),
            setAttributesMenu = (mi, addon, extension) => {
                var permissions, uuid,
                props = {
                    label: `${addon.name}${show_version ? ` ${addon.version}` : ""}`,
                    class: "menuitem-iconic",
                    tooltiptext: `${(show_description && addon.description) ? `${addon.description}\n` : ""}ID: ${addon.id}${addon.isActive && (uuid = extension?.uuid) ? `\nUUID: ${uuid}` : ""}${(user_permissions && (permissions = addon.userPermissions?.permissions)?.length) ? `\nРазрешения: ${permissions.join(", ")}` : ""}\n${addon.optionsURL ? "\nЛКМ: Настройки" : ""}\nCtrl+ЛКМ: Копировать ID${uuid ? "\nShift+ЛКМ: Копировать UUID" : ""}${addon.creator?.url ? "\nCtrl+Shift+ЛКМ: Автор" : ""}${addon.homepageURL ? "\nСКМ: Домашняя страница" : ""}${!addon.isBuiltin ? "\nCtrl+СКМ: Просмотр источника" : ""}\nShift+СКМ: Просмотр источника во вкладке\nПКМ: Включить/Отключить${(!addon.isSystem && !addon.isBuiltin) ? "\nCtrl+ПКМ: Удалить" : ""}`,
                };
                for (let p in props)
                    mi.setAttribute(p, props[p]);
                if (addon.iconURL)
                    mi.setAttribute("image", addon.iconURL);
                var cls = mi.classList;
                addon.isActive ? cls.remove("ucf-disabled") : cls.add("ucf-disabled");
                addon.optionsURL ? cls.remove("ucf-notoptions") : cls.add("ucf-notoptions");
                addon.isSystem ? cls.add("ucf-system") : cls.remove("ucf-system");
                cls.add(`ucf-type-${addon.type}`);
            };
            addons.filter(a => !(a.iconURL || "").startsWith("resource://search-extensions/")).sort((a, b) => {
                var ka = `${(enabled_first ? a.isActive ? "0" : "1" : "")}${a.type || ""}${a.name.toLowerCase()}`;
                var kb = `${(enabled_first ? b.isActive ? "0" : "1" : "")}${b.type || ""}${b.name.toLowerCase()}`;
                return (ka < kb) ? -1 : 1;
            }).forEach(addon => {
                if (!exceptions_listset.has(addon.id) &&
                    (!addon.hidden || show_hidden) &&
                    (!addon.userDisabled || show_disabled)) {
                    let extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.append(mi);
                    addonsMap.set(addon, mi);
                }
            });
            var click = (e) => {
                this.handleClick(e);
            };
            popup.addEventListener("click", click);
            var listener = {
                onEnabled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi)
                        setAttributesMenu(mi, addon, mi._Extension);
                },
                onDisabled: addon => {
                    listener.onEnabled(addon);
                },
                onInstalled: addon => {
                    var extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.prepend(mi);
                    addonsMap.set(addon, mi);
                },
                onUninstalled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi) {
                        mi.remove();
                        addonsMap.delete(addon);
                    }
                },
            };
            AddonManager.addAddonListener(listener);
            popup.addEventListener("popuphiding", (e) => {
                AddonManager.removeAddonListener(listener);
                popup.removeEventListener("click", click);
                addonsMap = null;
                while (popup.hasChildNodes())
                    popup.firstChild.remove();
            }, { once: true });
        },
        handleClick: function(e) {
            var win = e.view, mi = e.target;
            if (!("_Addon" in mi) || !("_Extension" in mi))
                return;
            var addon = mi._Addon, extension = mi._Extension;
            switch (e.button) {
                case 0:
                    if (e.ctrlKey && e.shiftKey) {
                        if (addon.creator?.url)
                            win.gBrowser.selectedTab = this.addTab(win, addon.creator.url);
                    } else if (e.ctrlKey) {
                        this.clipboardHelp.copyString(addon.id);
                        try {
                            this.alertsService.showAlertNotification(`${img}`, "ID в буфере обмена!", addon.id, false);
                        } catch(e) {}
                    } else if (e.shiftKey) {
                        if (extension?.uuid) {
                            this.clipboardHelp.copyString(extension.uuid);
                            try {
                                this.alertsService.showAlertNotification(`${img}`, "UUID в буфере обмена!", extension.uuid, false);
                            } catch(e) {}
                        }
                    } else if (addon.isActive && addon.optionsURL)
                        this.openAddonOptions(addon, win);
                    win.closeMenus(mi);
                    break;
                case 1:
                    if (e.ctrlKey) {
                        if (!addon.isBuiltin)
                            this.browseDir(addon);
                    } else if (e.shiftKey)
                        this.browseDir(addon, win);
                    else if (addon.homepageURL)
                        win.gBrowser.selectedTab = this.addTab(win, addon.homepageURL);
                    win.closeMenus(mi);
                    break;
                case 2:
                    if (!e.ctrlKey) {
                        let endis = addon.userDisabled ? "enable" : "disable";
                        if (addon.id == "screenshots@mozilla.org")
                            Services.prefs.setBoolPref("extensions.screenshots.disabled", !addon.userDisabled);
                        else if (addon.id == "webcompat-reporter@mozilla.org")
                            Services.prefs.setBoolPref("extensions.webcompat-reporter.enabled", addon.userDisabled);
                        addon[endis]({ allowSystemAddons: true });
                    } else if (!addon.isSystem && !addon.isBuiltin && Services.prompt.confirm(win, null, `Удалить ${addon.name}?`))
                        addon.uninstall();
                break;
            }
        },
        openAddonOptions: function(addon, win) {
            switch (addon.optionsType) {
                case 5:
                    win.BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(addon.id)}/preferences`);
                    break;
                case 3:
                    win.switchToTabHavingURI(addon.optionsURL, true);
                    break;
            }
        },
        browseDir: function(addon, win) {
            try {
                if (!win) {
                    let file = Services.io.getProtocolHandler("file")
                    .QueryInterface(Ci.nsIFileProtocolHandler)
                    .getFileFromURLSpec(addon.getResourceURI().QueryInterface(Ci.nsIJARURI).JARFile.spec);
                    if (file.exists())
                        file.launch();
                } else
                    win.gBrowser.selectedTab = this.addTab(win, addon.getResourceURI().spec);
            } catch (e) {}
        },
        addTab: function(win, url, params = {}) {
            params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
            params.relatedToCurrent = true;
            return win.gBrowser.addTab(url, params);
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild: function(doc) {
            var btn = doc.createXULElement("toolbarbutton"), win = doc.defaultView,
            props = {
                id: id,
                label: label,
                tooltiptext: tooltiptext,
                type: "menu",
                class: "toolbarbutton-1 chromeclass-toolbar-additional",
            };
            for (let p in props)
                btn.setAttribute(p, props[p]);
            btn.addEventListener("click", (e) => {
                if (e.button == 0) {
                    if (e.shiftKey)
                        win.BrowserOpenAddonsMgr();
                } else if (e.button == 1)
                    win.BrowserOpenAddonsMgr();
            });
            var mp = doc.createXULElement("menupopup");
            mp.id = `${id}-popup`;
            mp.addEventListener("click", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });
            mp.addEventListener("contextmenu", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });
            mp.addEventListener("popupshowing", (e) => {
                extensionOptionsMenu.populateMenu(e);
            });
            btn.append(mp);
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id}, #${id}-popup menuitem {
                    list-style-image: url("${img}") !important;
                }
                #${id}-popup menuitem::after {
                    display: -moz-box !important;
                    content: "" !important;
                    height: 16px !important;
                    width: 16px !important;
                    padding: 0 !important;
                    border: 1px solid rgb(0, 116, 232) !important;
                    border-radius: 0 !important;
                    background-repeat: no-repeat !important;
                    background-position: center !important;
                    background-size: 16px !important;
                    background-color: rgb(0, 116, 232) !important;
                    background-image: url("${checked}") !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem.ucf-disabled::after {
                    border-color: currentColor !important;
                    background-color: transparent !important;
                    background-image: none !important;
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-disabled > label,
                #${id}-popup menuitem.ucf-notoptions > label {
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-system > label {
                    text-decoration: underline !important;
                    text-decoration-style: dotted !important;
                }
                #${id}-popup menuitem > label {
                    margin-inline-end: 0 !important;
                }
                #${id}-popup menuitem > .menu-accel-container {
                    display: -moz-box !important;
                    padding: 4px !important;
                    margin: 0 !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem > .menu-accel-container .menu-iconic-accel {
                    display: -moz-box !important;
                    margin: 0 !important;
                    height: 8px !important;
                    width: 8px !important;
                    border-radius: 4px !important;
                    background-color: transparent !important;
                    opacity: 1 !important;
                    font-size: 0 !important;
                }
                #${id}-popup menuitem.ucf-type-dictionary > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(227, 27, 93) !important;
                }
                #${id}-popup menuitem.ucf-type-locale > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(48, 172, 55) !important;
                }
                #${id}-popup menuitem.ucf-type-theme > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(219, 106, 0) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            return btn;
        },
    });
})();} catch (e) {}


Есть новее?

xrun1 пишет

В [firefox] 101 (может и раньше) отвалилась кнопка "Дополнения" из расширения Add Toolbar Buttons

Я здесь добавил обновлённый вариант.
Скрипт с под первого спойлера отсюда тоже пока работает.

sandro79 пишет

Я здесь добавил обновлённый вариант.

Ваш пост не увидел. :) Спасибо.

Dumby
На 102 отвалился add_toolbar_buttons, его можно вернуть к жизни?
add_toolbar_buttons.2021.9.5.xpi

_zt
Да, враги народа не дремлют. Сделали так и так.
Не просто сделали, а ещё и засунули это в бету 102.


Таким образом, трюк с переопределением Object.freeze() больше не работает.
Это я про антиподписячий код говорю. Пока такой вариант
(в смысле если напишут export var …, а если напишут export const …, тогда ...).

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

Выделить код

Код:

//
try {(jsval => {
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;

	var AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true;
		var env, def = g.ChromeUtils.defineModuleGetter;
		g.ChromeUtils.defineModuleGetter = (...args) => {
			try {
				genv();
				dbg.addDebuggee(globalThis);
				var e = dbg.getNewestFrame().older.environment;
				var obj = e.parent.type == "object" && e.parent.object;
				if (obj && obj.class.startsWith("N")) // JSM, NSVO
					obj.unsafeDereference().Object = {
						freeze: ac => (ac.MOZ_REQUIRE_SIGNING = false) || freeze(ac)
					};
				else env = e; // ESM, Lexy "var"(?)
			}
			catch(ex) {Cu.reportError(ex);}
			(g.ChromeUtils.defineModuleGetter = def)(...args);
		}
		ChromeUtils.import(uac + "jsm");
		// (?)
		env && env.setVariable(AC, gref.makeDebuggeeValue(freeze(o.assign(
			new o(), env.getVariable(AC).unsafeDereference(), {MOZ_REQUIRE_SIGNING: false}
		))));
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;

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

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

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

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

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


Однако напомню, что в 91ESR антиподписячий код не обязателен,
можно обойтись только настройками. Есть вероятность того, что это будет верно и для 102ESR.
А дурацкие надписи на about:addons можно убрать скриптом, или, в крайнем случае, стилем.

Dumby
Спасибо.
   
Посмотрим, что в esr будет, ведь atb на ранних бетах работал, а потом вот чего придумали. Да и совместимость с последующими версиями хотелось бы иметь.

Dumby
Вы делали скрипт - кнопка включения стиля
А как бы этот стиль инициализировать при старте браузера, т.е. сделать кнопку не "включения", а наоборот "отключения стиля".

_zt пишет

Вы делали скрипт - кнопка включения стиля

Не совсем так. Это, скорее, proof of concept, что стиль будет перечитан с диска.


Можно после строки, где функция reg определяется, добавить строку с её вызовом: reg();
Это имеется в виду, что кнопка вытащена на тулбар.

Dumby
Концепт так концепт, главное что работает. В меню расширений "другие инструменты" тоже работает. Спасибо.

Dumby посмотрите пожалуйста эти две кнопки.При их использовании в [firefox] 102.0 падает вкладка

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

Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}

вторую не смог вставить не принимает форум поэтому ссылку на скрипт оставлю https://disk.yandex.ru/d/tT_A1SgQ9IZWgA

egorsemenov06 пишет

не смог вставить не принимает форум

В base64 всё примет.

в [firefox] 102.0 падает вкладка

Да, вижу. Увы, ничего лучше не придумал

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

Выделить код

Код:

/*
        var evt = new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        );
        node.dispatchEvent(evt);
    },
*/
        if(ChromeUtils.domProcessChild.childID) {
            var cmd = this.beh2cmd[e.ctrlKey + "_" + e.shiftKey + "_" + e.keyCode];
            cmd && docShell.doCommand(cmd);
        }
        else node.dispatchEvent(new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        ));
    },
    beh2cmd: { // Ctrl_Shift_VK
        false_true_36: "cmd_selectLinePrevious", // Shift+Home
    },

Dumby пишет
egorsemenov06 пишет

не смог вставить не принимает форум

В base64 всё примет.

Спасибо Большое !работает да и ладно.А как это в base64

egorsemenov06 пишет

А как это в base64

Ух ты! Хотел написать

Ну, типа текстовая ссылка для скормления адресной строке.
И спойлер [например]

Но получил: «500 Internal Server Error».
Риторический вопрос: чё за дела?

Dumby пишет
egorsemenov06 пишет

А как это в base64

Ух ты! Хотел написать

Ну, типа текстовая ссылка для скормления адресной строке.
И спойлер [например]

Но получил: «500 Internal Server Error».
Риторический вопрос: чё за дела?

и у меня такая же ошибка выходила

Dumby
egorsemenov06
спасибо за поднятие проблемы и правки кода
один вопросик всегда беспокоил по этой кнопке: почему когда переключаешь раскладку для слов начинающихся на букву Б, код срабатывает некорректно? Это поправимо?

Dumby пишет

_zt
Да, враги народа не дремлют. Сделали так и так.
Не просто сделали, а ещё и засунули это в бету 102.


Таким образом, трюк с переопределением Object.freeze() больше не работает.
Это я про антиподписячий код говорю. Пока такой вариант
(в смысле если напишут export var …, а если напишут export const …, тогда ...).

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

Выделить код

Код:

//
try {(jsval => {
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;

	var AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true;
		var env, def = g.ChromeUtils.defineModuleGetter;
		g.ChromeUtils.defineModuleGetter = (...args) => {
			try {
				genv();
				dbg.addDebuggee(globalThis);
				var e = dbg.getNewestFrame().older.environment;
				var obj = e.parent.type == "object" && e.parent.object;
				if (obj && obj.class.startsWith("N")) // JSM, NSVO
					obj.unsafeDereference().Object = {
						freeze: ac => (ac.MOZ_REQUIRE_SIGNING = false) || freeze(ac)
					};
				else env = e; // ESM, Lexy "var"(?)
			}
			catch(ex) {Cu.reportError(ex);}
			(g.ChromeUtils.defineModuleGetter = def)(...args);
		}
		ChromeUtils.import(uac + "jsm");
		// (?)
		env && env.setVariable(AC, gref.makeDebuggeeValue(freeze(o.assign(
			new o(), env.getVariable(AC).unsafeDereference(), {MOZ_REQUIRE_SIGNING: false}
		))));
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;

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

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

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

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

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


Однако напомню, что в 91ESR антиподписячий код не обязателен,
можно обойтись только настройками. Есть вероятность того, что это будет верно и для 102ESR.
А дурацкие надписи на about:addons можно убрать скриптом, или, в крайнем случае, стилем.

Подскажите, как вернуть к жизни add_toolbar_buttons.2021.9.5.xpi на 102 (Final или ESR), очень уж удобный плагин :)!

antialt пишет

Подскажите, как вернуть к жизни add_toolbar_buttons.2021.9.5.xpi на 102 (Final или ESR), очень уж удобный плагин :)!

Вы же процитировали решение, просто обновите код для отключения... на тот что под спойлером!

Извиняюсь, вставлял код на профиль после запуска со старой "антиподпиской", не сработал. Сейчас вытащил из бэкапа профиль от 101 версии, накатил обнову и новый код. Всё отлично, спасибо :)!

Dumby
Можете посмотреть расширение Open Link with New Tab? На 91.11 и 102 начался периодический отвал, проявляется в том что иногда ссылки по Ctrl+ЛКМ начинают открываться в новом окне, перезапуск расширения лечит это.

Dumby
с последним обновлением ff отваливается такой скрипт, не везде срабатывает: в строке адреса, в строке поиска на стартовом экране - не работает совсем, а на большинстве форум в окнах ввода срабатывает. Странно...

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

Выделить код

Код:

// MiddleMouse.Paste с заменой выделенного текста (порт с СВ)

try {((id, code, gmon) => {

    var d = "data:,", ref = "globalThis." + id, dref = d + ref;
    var psi = `${dref} = ${encodeURIComponent(code)};`;
    var psd = `${d}delete ${ref};`;

    var e10s = Services.appinfo.browserTabsRemoteAutostart;
    if (e10s) var
        fsi = `${dref}.listen(this, "add");`,
        fsd = `${dref}.listen(this);`;

    var g = Cu.getGlobalForObject(Cu), pref = "middlemouse.paste";
    var obs = {
        pref: Services.prefs.getBoolPref.bind(null, pref),
        startup() {
            Services.prefs.addObserver(pref, this);
            Services.obs.addObserver(this, "quit-application-granted", false);
            this.pref() && this.init();
            this.gmon();
        },
        async gmon() {
            Cu.importGlobalProperties(["fetch"]);
            var url = "chrome://custombuttons/content/editExternal.js";
            try {var src = await (await fetch(url)).text();} catch(ex) {return;}
            src = src.replace(/function gmon_edit_mouseclick[^}]+?}/, gmon);
            var arr = [["override", url, "data:," + encodeURIComponent(src)]];
            url = Services.io.getProtocolHandler("resource").getSubstitution("gre");
            this.gmonHelper = Cc["@mozilla.org/addons/addon-manager-startup;1"]
                .getService(Ci.amIAddonManagerStartup).registerChrome(url, arr);
        },
        shutdown() {
            this.pref() && this.destroy();
            e10s && Services.ppmm.removeDelayedProcessScript(psi),
            Services.ppmm.loadProcessScript(psd, false);
            Services.prefs.removeObserver(pref, this);
            Services.obs.removeObserver(this, "quit-application-granted");
            this.gmonHelper && this.gmonHelper.destruct();
        },
        init() {
            e10s && Services.mm.loadFrameScript(fsi, true);
            Services.obs.addObserver(this, "widget-first-paint", false);
            this.wins("add");
        },
        destroy() {
            if (e10s)
                Services.mm.removeDelayedFrameScript(fsi),
                Services.mm.loadFrameScript(fsd, false);
            Services.obs.removeObserver(this, "widget-first-paint");
            this.wins();
        },
        observe(subj, topic) {
            var char = topic[0];
            if (char == "w") return this.call(subj, "add");
            if (char == "q") return this.shutdown();
            this[this.pref() ? "init" : "destroy"]();
        },
        wins(arg) {
            for(var win of Services.wm.getEnumerator(null)) this.call(win, arg);
        },
        listen() {}
    };
    Services.ppmm.loadProcessScript(psi, e10s);
    g[id].call = g[id].listen;
    Object.assign(g[id], obs).startup();

})("ucf_custom_script_js_MiddleMousePaste", `{

    listen(trg, prfx = "remove") {
        var meth = prfx + "EventListener";
        trg[meth]("auxclick", this, true);
        trg[meth]("unload", this);
    },
    handleEvent(e) {this[e.type](e);},
    unload(e) {this.listen(e.target);},
    sn: Ci.nsISelectionController.SELECTION_NORMAL,
    inRect: (r, x, y) => y > r.top && y < r.bottom && x < r.right && x > r.left,
    auxclick(e) {
        if (e.button != 1) return;
        var trg = (e.originalTarget || e.target).closest(
            "input:not([disabled]),textarea:not([disabled])"
        );
        if (!trg) return;
        var ed = trg.editor;
        if (!ed || ed.selection.isCollapsed || !ed.canPaste(this.sn)) return;

        var x = e.clientX, y = e.clientY, rng = ed.selection.getRangeAt(0);
        if (!this.inRect(rng.getBoundingClientRect(), x, y)) return;

        var list = rng.getClientRects();
        if (list.length == 1 || Array.from(list).some(
            rect => this.inRect(rect, x, y)
        ))
            ed.paste(this.sn, e.preventDefault());
    }
}`, `\
function gmon_edit_mouseclick(e) {
    var mmp = Cc["@mozilla.org/preferences-service;1"]
        .getService(Ci.nsIPrefBranch).getBoolPref.bind(null, "middlemouse.paste");
    (gmon_edit_mouseclick = e => e.button != 1 || mmp() || edittarget(e.target))(e);
}`);
} catch(ex) {Cu.reportError(ex);}


((bu, bm, {star} = bu) => addEventListener("mouseenter", {
    async handleEvent() {
        if (!this.starred) return;
        star.tooltipText = "\u3164";
        var result = [];
        await this.fetch();
        for(var guid of this.guids) {
            var arr = [];
            while(true) {
                if (!this.hover) return;
                var res = await bm.fetch(guid);
                if ((guid = res.parentGuid) == bm.rootGuid) {
                    arr.unshift(bm.getLocalizedTitle(res));
                    break;
                }
                arr.unshift(res.title || "[Безымянная папка]");
            }
            result.push(arr.join("\\"));
        }
        this.hover && this.setTooltip(result);
    },
    get fetch() {
        addDestructor(() => this.starred && document.l10n.translateElements([star]));
        var set = this.guids = new Set();
        var args = [b => set.add(b.parentGuid), {concurrent: true}];
        delete this.fetch; return this.fetch = () => set.clear()
            || bm.fetch({url: gBrowser.currentURI.spec}, ...args);
    },
    setTooltip(arr) {
        var m = arr.length > 1;
        var text = `Адрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${arr.join("\n")}`;
        document.tooltipNode == star ? this.tt.label = text : star.tooltipText = text;
    },
    get tt() {
        var list = InspectorUtils.getChildrenForNode(document.documentElement, true);
        delete this.tt; return this.tt = list.item(list.length - 1);
    },
    get starred() {return bu._itemGuids.size;},
    get hover() {return star.matches(":hover");}
}, false, star || 1))(BookmarkingUI, PlacesUtils.bookmarks);


когда-то вы его уже приводили в чувство, теперь снова сломался...


и вопрос по тому же Switch Keyboard Layout button: переключаешь раскладку для слов начинающихся на букву Б, Ю, Ж - код срабатывает некорректно

Dumby
Сделайте пожалуйста, что бы скрипт: Очистить панель адреса или поиска прокруткой колёсиком мыши на панели работал во всех панелях поиска, включая контент!

_zt пишет

расширение

Это не расширение, а WebExtensions.

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

иногда

И где же мне взять это «иногда»? Не́где.

Можете посмотреть

Смотреть там особо неначто.
Регистрируется контентский скрипт, который,


запрашивает пользовательскую аддонскую настройку,
и если она не равна нулю (типа, установлена, и как 1 или 2)


тогда идёт перебор всех элементов <a>, которые присутствуют в DOM-дереве документа
(на момент исполнения скрипта, добавленные [как-то] позже идут лесом; и те, которые в Shadow DOM тоже, наверно, в пролёте),
и, если элемент (собственно ссылка) имеет href, тогда ему уставливается атрибут "target" со значением "_blank".
(ну, там в зависимости от 1 или 2, как-бы same-site или нет, неважно).


Вот и всё. Атрибут либо уставливается, либо нет.
Если атрибут должен устанавливаться, но не устанавливается,
тогда не знаю почему. Может storage.sync глючит (?).
В таком случае можно попробовать заменить все browser.storage.sync на browser.storage.local
и заново посетить настройки аддона. Предположение, разумеется, ни на чём не основано.


А так-то, допустим, если уже установлена какая-либо обезьяна,
то скриптов для неё, делающих нечто подобное, должно быть (ну, я так предполагаю) навалом.
И не просто подобное, а гораздо более ультимативное.
Может попробуй смигрировать в эту сторону.

Inko7 пишет

такой скрипт

Это ты мне что-то совсем левое древнее впариваешь.
Ещё и какой-то star-тултипский код в конце прицеплен, непонятно зачем.


Последний вариант вроде как здесь.


Но, железная поступь проекта «JSM-геноцид»,
рано или поздно, сметёт не только это, а вообще всё.
Если нет возможности вникнуть в это сейчас (пока никто не торопит)
то лучше отказаться от всего подобного сразу, нечего не дожидаясь.

для слов начинающихся на букву Б, Ю, Ж - код срабатывает некорректно

Да видел я пост про «Б». Ин-валидно.
Нет никаких проблем с «Б» (как и с «Ю»).


С «Ж», да, есть проблемы.
Так же, как есть проблемы и с «б», «ю», «ж», «Э».


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

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

Выделить код

Код:

/*
            if(c in this.convTableForward)
                return true;
            if(c in this.convTableBackward)
                return false;
*/
            var primary = c in this.convTableForward;
            if(primary ^ c in this.convTableBackward)
                return primary;

kokoss пишет

включая контент

Увы, делам контентским, вебским, я весьма посторонен.
Могу только извиниться за это.

Dumby пишет

Последний вариант вроде как здесь.

Как-то пропустил его, но он все равно не работает в строке адреса и в строке поиска на стартовой странице FF. Может оно и не особо критично...

Dumby пишет

Возможно, чуть лучше будет с такой правкой

изменений пока не заметил

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

Dumby пишет

Ещё и какой-то star-тултипский код в конце прицеплен, непонятно зачем.

Точно! Думаю, где же он спрятался)))
У меня есть два варианта кода, кто из них новее/лучше не знаю. Но у обоих есть глюк - открываем закладку, с нее переходим по внутр. ссылкам куда-либо, звездочка соответственно теперь пустая в адресной строке, а тултип все равно показывается с путём к изначально открытой закладке. Такое очень часто.

скрытый текст
этот наверное новее

Выделить код

Код:

// Показать адрес существующей закладки при наведении на звездочку

try {((bu, bm, {star} = bu) => {
    var listener = {
        async handleEvent() {
            if (!bu._itemGuids.size) return;
            star.tooltipText = "\u3164";
            var result = [];
            await this.fetch();
            for(var guid of this.guids) {
                var arr = [];
                while(true) {
                    if (!this.hover) return;
                    var res = await bm.fetch(guid);
                    if ((guid = res.parentGuid) == bm.rootGuid) {
                        arr.unshift(bm.getLocalizedTitle(res));
                        break;
                    }
                    arr.unshift(res.title || "[Безымянная папка]");
                }
                result.push(arr.join("\\"));
            }
            this.hover && this.setTooltip(result);
        },
        get fetch() {
            var set = this.guids = new Set();
            var args = [b => set.add(b.parentGuid), {concurrent: true}];
            delete this.fetch; return this.fetch = () => set.clear()
                || bm.fetch({url: gBrowser.currentURI.spec}, ...args);
        },
        setTooltip(arr) {
            var m = arr.length > 1;
            var text = `Адрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${arr.join("\n")}`;
            document.tooltipNode == star ? this.tt.label = text : star.tooltipText = text;
        },
        get tt() {
            var list = InspectorUtils.getChildrenForNode(document.documentElement, true);
            delete this.tt; return this.tt = list.item(list.length - 1);
        },
        get hover() {return star.matches(":hover");}
    };
    star.addEventListener("mouseenter", listener);
    addEventListener("unload",
        () => star.removeEventListener("mouseenter", listener)
    , {once: true});
})(BookmarkingUI, PlacesUtils.bookmarks);} catch(ex) {Cu.reportError(ex);}


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

Выделить код

Код:

// Показать адрес существующей закладки при наведении на звездочку

((bu, bm, {star} = bu) => addEventListener("mouseenter", {
    async handleEvent() {
        if (!this.starred) return;
        star.tooltipText = "\u3164";
        var result = [];
        await this.fetch();
        for(var guid of this.guids) {
            var arr = [];
            while(true) {
                if (!this.hover) return;
                var res = await bm.fetch(guid);
                if ((guid = res.parentGuid) == bm.rootGuid) {
                    arr.unshift(bm.getLocalizedTitle(res));
                    break;
                }
                arr.unshift(res.title || "[Безымянная папка]");
            }
            result.push(arr.join("\\"));
        }
        this.hover && this.setTooltip(result);
    },
    get fetch() {
        addDestructor(() => this.starred && document.l10n.translateElements([star]));
        var set = this.guids = new Set();
        var args = [b => set.add(b.parentGuid), {concurrent: true}];
        delete this.fetch; return this.fetch = () => set.clear()
            || bm.fetch({url: gBrowser.currentURI.spec}, ...args);
    },
    setTooltip(arr) {
        var m = arr.length > 1;
        var text = `Адрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${arr.join("\n")}`;
        document.tooltipNode == star ? this.tt.label = text : star.tooltipText = text;
    },
    get tt() {
        var list = InspectorUtils.getChildrenForNode(document.documentElement, true);
        delete this.tt; return this.tt = list.item(list.length - 1);
    },
    get starred() {return bu._itemGuids.size;},
    get hover() {return star.matches(":hover");}
}, false, star || 1))(BookmarkingUI, PlacesUtils.bookmarks);

03-07-2022 21:46:08

Dumby пишет

Ну, значит у кого-то из нас (двоих) руки кривые.

тут спору нет :)
о каком конкретно скрипте речь?

Inko7 пишет

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

:/

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

Выделить код

Код:

/*
            if (!bu._itemGuids.size) return;
*/
            if (!bu._itemGuids.size) return star.removeAttribute("tooltiptext");

о каком конкретно скрипте речь?

Об обоих двух.

Dumby пишет

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

У меня везде всё вставляет, без проблем. :)

voqabuhe пишет

У меня везде всё вставляет, без проблем.

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

Dumby
Понял в чем было дело. По инструкции для MMPaste.jsm нужно было:

можно добавив в конфигурационный CustomStylesScripts.jsm
в массив UcfStylesScripts.scriptsbackground объект вида
{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/MMPaste.jsm");' },

а у меня скрипты загружались уже через сам файл custom_script.js

в нем прописан вот такой код:

Выделить код

Код:

(() => {
    var loadscript = name => {
        try {
            Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`, globalThis, "UTF-8");
        } catch(e) {}
    };

    loadscript("./js/kbd_layout.js");
    loadscript("./js/MiddleMouse_Paste.js");

})();


но так же они тоже подгружались, просто как-то не совсем корректно?

Inko7 пишет

"./js/kbd_layout.js"

Это что ещё за дот-слэш в начале?
В данном случае — совершенно неуместно.
Работает, видимо, только потому, что где-то стоит защита от подобных выкрутасов.

но так же они тоже подгружались, просто как-то не совсем корректно?

kbd_layout.js — это просто скрипт.
Можно грузить так, как у тебя, можно инлайн,
можно любым другим аналогичным способом, так что здесь всё верно.


А Middle Mouse Paste — это модуль (jsm'ка, пока ещё).
Модули не грузят как скрипт. Их импортируют.
Так что здесь неверно, следует импортировать так, как сказано.

Dumby
Тепрь всё понятно и всё заработало как надо. Star-tooltip тоже отлично работает.
Спасибо.

Dumby

А так-то, допустим, если уже установлена какая-либо обезьяна, то скриптов для неё, делающих нечто подобное, должно быть (ну, я так предполагаю) навалом

Да, есть маленько.

В таком случае можно попробовать заменить все browser.storage.sync на browser.storage.local и заново посетить настройки аддона.

Уже не хочу, так как найденный скрипт работает лучше, например на github внешние ссылки корректно открывает в новой вкладке, ну и т.п.
   
Только вот, можете этот скрипт переделать под ucf, с настройками в самом файле.

Dumby пишет

Middle Mouse Paste — это модуль (jsm'ка, пока ещё).

Раз jsm-ки скоро отвалится, то весь UCF и CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm тоже перестанут работать?

если поддержку jsm уберут, просьба переделать в JS модуль сохранения картинок ClickPicSave.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["MouseImgSaverChild", "MouseImgSaverParent"]; // by Dumby сохранить картинку колёсиком или перетащив вправо; DBL поиск похожих

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

class MouseImgSaverChild extends JSWindowActorChild {
	handleEvent(e) { // клики мышью
		if (e.button > 1) return; // только ЛКМ, СКМ
		var trg = e.explicitOriginalTarget; // dragstart
		trg.nodeType == Node.ELEMENT_NODE
			&& trg instanceof Ci.nsIImageLoadingContent
			&& this[e.type](trg, e);
	}
	handleDragEvent(e) {
		this[e.type](e);
	}
	dragstart(trg, e) {
		this.trg = trg;
		this.x = e.screenX; this.y = e.screenY;
		this.drag("add");
		this.handleEvent = this.handleDragEvent;
		this.checkTextLinkyTool(trg.ownerDocument);
	}
	events = ["dragover", "drop", "dragend"];
	drag(meth = (delete this.handleEvent, delete this.trg, "remove")) {
		meth += "EventListener";
		var win = this.contentWindow;
		for(var type of this.events) win[meth](type, this, true);
	}
	drop() {
		this.drag();
	}
	dragover(e) {
		var {x, y} = this,
			cx = e.screenX, cy = e.screenY,
			dx = cx - x,
			ax = Math.abs(dx), ay = Math.abs(cy - y);

		if (ax < 10 && ay < 10) return;
		if (dx < 0 || ax < ay) return this.drag();
		this.x = cx; this.y = cy;
	}
	dragend(e) { // перетаскивание рисунка
		var dt = e.dataTransfer, {trg} = this;
		this.drag();
		dt.mozUserCancelled || this.send(trg, e.screenX); // сохранить
		// dt.mozUserCancelled || this.sendAsyncMessage("dragend", (trg.currentRequestFinalURI || uri).spec);
	}
	auxclick(trg) { // клик СКМ
		trg.matches(":any-link :scope") || this.send(trg);
	}
	dblclick(trg) { // ЛКМ
		trg.matches(":any-link :scope")
			|| this.sendAsyncMessage("dblclick", (trg.currentRequestFinalURI || uri).spec);
	}
	send(trg, sx) {
		var uri = trg.currentURI;
		if (!uri) return;

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

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

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

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

			var win = wref.get(), {prefs, dirsvc} = win.Services
			var {DownloadPaths, FileUtils} = win;
			var map = val => DownloadPaths.sanitize(val);

			win.Downloads.getList(win.Downloads.ALL).then(list => list.addView({
				onDownloadChanged(download) {
					if (!download.stopped) return;
					var {url} = download.source, title = titles[url];
					if (!title) return;
					delete titles[url];
					if (!download.succeeded) return;

					var file = FileUtils.File(download.target.path), {leafName} = file;
					var ext = leafName.slice(leafName.lastIndexOf("."));
					var newName = map(title) + ext, {parent} = file;
					var newFile = parent.clone();
					newFile.append(newName);
					try {
						newFile.createUnique(file.NORMAL_FILE_TYPE, file.permissions);
						file.renameTo(parent, newFile.leafName);
						download.target.path = newFile.path;
						download.refresh();
					} catch {}
				}
			}));
			Object.defineProperty(this, "set", {get() {
				try {var dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {var dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
				var arr = prefs.getStringPref("ucf.savedirs", "_Web||_Images|0").split('|').slice(2, 4);
				// подпапки в [Загрузках]: нет | папка графики | имя вкладки | домен
				arr[1] = (arr[1]) ? wref.get().gBrowser.selectedTab.label.slice(0, 64).replace(/ \| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim() : ""; // имя вкладки
				arr.map(map).forEach(dir.append); dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777);
				return dir.path;
			}});
			return this.set;
		}},
		"browser.download.folderList": {type: "Int", set: 2},
		"browser.download.useDownloadDir": {type: "Bool", set: true}
	});
	var MouseImgSaverParent = class extends JSWindowActorParent {
		receiveMessage(msg) {
			var win = msg.target.browsingContext.topChromeWindow, {name} = msg;
			if (name) return this[name](win, msg.data);

			var {url, contentType, contentDisposition, sx, title,
				isPrivate, referrerInfo, cookieJarSettings} = msg.data;
			if (sx && sx > win.mozInnerScreenX + win.innerWidth) return;

			if (title) titles[url] = title;

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

			for(var pref in data) {
				var obj = data[pref], meth = `et${obj.type}Pref`;
				obj.val = p.prefHasUserValue(pref) ? p["g" + meth](pref) : null;
				p["s" + meth](pref, obj.set);
			}
			try {win.internalSave(
				url,
				null, // document
				null, // file name
				contentDisposition,
				contentType,
				false, // do not bypass the cache
				null, // filepicker title key
				null, // chosen data
				u.E10SUtils.deserializeReferrerInfo(referrerInfo),
				u.E10SUtils.deserializeCookieJarSettings(cookieJarSettings),
				win.document, // initiating doc
				true, // skip prompt for where to save
				null, // cache key
				isPrivate,
				win.document.nodePrincipal
			);}
			finally {
				for(var pref in data) data[pref].val === null
					? p.clearUserPref(pref)
					: p[`set${data[pref].type}Pref`](pref, data[pref].val);
			}
		}
		dblclick(win, imgURL) {
			var gb = win.gBrowser, index = gb.selectedTab._tPos + 1;
			gb.selectedTab = gb.addTrustedTab('https://yandex.ru/images/search?rpt=imageview&url=' + imgURL, {index});
		}
	}
}

Dobrov пишет

Раз jsm-ки скоро отвалится, то весь UCF и CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm тоже перестанут работать?

ESM модули вместо них, несложная конвертация модулей jsm --> mjs, у меня уже работает на FF 103

_zt пишет

с настройками в самом файле

Что-то мне в голову не влезает то, что там навёрнуто,
не смог понять как это должно работать, сколько ни пытался.


Могу попробовать просто процитировать, без осознания,
но вряд ли из этого что-то выйдет.

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

Выделить код

Код:

var cfg = {
	rootzone: false,
	parent: true,
	neighbor: true,
	host: true,
	child: true,

	background: false,
	insert: true,
	setParent: true
};
var name = "ExternalLinkNewtaber";
if (ChromeUtils.domProcessChild.childID) {
	var empty = new RegExp("^$");

	var click = function(e) {
		e.preventDefault();
		e.stopImmediatePropagation();
		this.ownerGlobal.windowGlobalChild
			.getActor(name).sendAsyncMessage("", this.href);
	}
	var EXPORTED_SYMBOLS = [name + "Child"];
	var ExternalLinkNewtaberChild = class extends JSWindowActorChild {
		actorCreated() {
			this.pp = this.ph = empty;
			var host = this.contentWindow.location.hostname;

			// quot ===========================================================================

			let parent = host.replace(/^[^.]*\./, '').replace(/\./g, '\\\.');
			host = host.replace(/\./g, '\\\.');

			if (cfg.parent){this.pp = new RegExp(`^${parent}$`);}    // abc.x               => ^abc\.x$
			if (cfg.neighbor){
				const flat = host.replace(/\..*/, '');
				if (cfg.parent){
					this.pp = new RegExp(`[^(${flat}\.)]?${parent}$`);     // abc.x + *.abc.x     => [^(w\.)]?abc\.x$
				}
				else {this.pp = new RegExp(`[^(${flat})]?\.${parent}$`);}  // *.abc.x             => [^(w)]?\.abc\.x$
			}
			if (!cfg.rootzone && parent.search(/\..+\./) == -1){this.pp = empty;}
			if (cfg.host){this.ph = new RegExp(`^${host}$`);}    // w.abc.x             => ^w\.abc\.x$
			if (cfg.child){
				if (cfg.host){
					this.ph = new RegExp(`(.+\.)?${host}$`);                  // w.abc.x + *.w.abc.x => (.+\.)?w\.abc\.x$
				}
				else {this.ph = new RegExp(`.+\.${host}$`);}                  // *.w.abc.x           => .+\.w\.abc\.x$
			}

			// =========================================================================== quot
		}
		handleEvent(e) {
			for(var a of this.document.getElementsByTagName("a")) if (a.hasAttribute("href")) {
				var {host} = a;
				!host || empty.test(host) || this.pp.test(host) || this.ph.test(host)
					|| a.addEventListener("click", click);
			}
		}
	}
} else {
	var {background, insert, setParent} = cfg;
	var arg = insert || setParent, fg = !background;
	var EXPORTED_SYMBOLS = [name + "Parent"];
	var ExternalLinkNewtaberParent = class extends JSWindowActorParent {
		receiveMessage(msg) {
			var opts, gb = this.browsingContext.topChromeWindow.gBrowser;
			if (arg) {
				opts = {};
				var st = gb.selectedTab;
				if (insert) opts.index = st._tPos + 1;
				if (setParent) opts.ownerTab = st;
			}
			var tab = gb.addTrustedTab(msg.data, opts);
			if (fg) gb.selectedTab = tab;
		}
	}
	ChromeUtils.registerWindowActor(name, {
		allFrames: true,
		matches: ["*://*/*"],
		messageManagerGroups: ["browsers"],
		parent: {moduleURI: __URI__},
		child: {moduleURI: __URI__, events: {load: {capture: true}}}
	});
}

Dobrov пишет

просьба переделать в JS модуль сохранения картинок ClickPicSave.jsm

Сейчас пока не очень удобно, поскольку лисьи jsm'ки ещё не сконвертированы.


А вообще это не очень сложно (и, нужен Firefox 103+).
Сам модуль импортируется не ChromeUtils.import() а ChromeUtils.importESModule()
При регистрации ChromeUtils.register{Window, Process}Actor() указываем не moduleURI а esModuleURI
Свойства __URI__ в этих модулях нет, подойдёт Components.stack.filename
Вместо var EXPORTED_SYMBOLS = []; используем инструкцию export
Ну, вобщем, как-то так:

ClickPicSave.mjs

Выделить код

Код:

export {MouseImgSaverChild, MouseImgSaverParent};

var u = {get it() {
	delete this.it;
	return this.it = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
}};
["E10SUtils", "PrivateBrowsingUtils"].forEach(name => Object.defineProperty(u, name, {
	configurable: true, enumerable: true, get() {
		var url = `resource://gre/modules/${name}.`;
		try {var exp = ChromeUtils.importESModule(url + "sys.mjs");}
		catch {exp = ChromeUtils.import(url + "jsm");}
		delete this[name];
		return this[name] = exp[name];
	}
}));

class MouseImgSaverChild extends JSWindowActorChild {
	handleEvent(e) { // клики мышью
		if (e.button > 1) return; // только ЛКМ, СКМ
		var trg = e.explicitOriginalTarget; // dragstart
		trg.nodeType == Node.ELEMENT_NODE
			&& trg instanceof Ci.nsIImageLoadingContent
			&& this[e.type](trg, e);
	}
	handleDragEvent(e) {
		this[e.type](e);
	}
	dragstart(trg, e) {
		this.trg = trg;
		this.x = e.screenX; this.y = e.screenY;
		this.drag("add");
		this.handleEvent = this.handleDragEvent;
		this.checkTextLinkyTool(trg.ownerDocument);
	}
	events = ["dragover", "drop", "dragend"];
	drag(meth = (delete this.handleEvent, delete this.trg, "remove")) {
		meth += "EventListener";
		var win = this.contentWindow;
		for(var type of this.events) win[meth](type, this, true);
	}
	drop() {
		this.drag();
	}
	dragover(e) {
		var {x, y} = this,
			cx = e.screenX, cy = e.screenY,
			dx = cx - x,
			ax = Math.abs(dx), ay = Math.abs(cy - y);

		if (ax < 10 && ay < 10) return;
		if (dx < 0 || ax < ay) return this.drag();
		this.x = cx; this.y = cy;
	}
	dragend(e) { // перетаскивание рисунка
		var dt = e.dataTransfer, {trg} = this;
		this.drag();
		dt.mozUserCancelled || this.send(trg, e.screenX); // сохранить
		// dt.mozUserCancelled || this.sendAsyncMessage("dragend", (trg.currentRequestFinalURI || uri).spec);
	}
	auxclick(trg) { // клик СКМ
		trg.matches(":any-link :scope") || this.send(trg);
	}
	dblclick(trg) { // ЛКМ
		trg.matches(":any-link :scope")
			|| this.sendAsyncMessage("dblclick", (trg.currentRequestFinalURI || uri).spec);
	}
	send(trg, sx) {
		var uri = trg.currentURI;
		if (!uri) return;

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

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

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

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

			var win = wref.get(), {prefs, dirsvc} = win.Services
			var {DownloadPaths, FileUtils} = win;
			var map = val => DownloadPaths.sanitize(val);

			win.Downloads.getList(win.Downloads.ALL).then(list => list.addView({
				onDownloadChanged(download) {
					if (!download.stopped) return;
					var {url} = download.source, title = titles[url];
					if (!title) return;
					delete titles[url];
					if (!download.succeeded) return;

					var file = FileUtils.File(download.target.path), {leafName} = file;
					var ext = leafName.slice(leafName.lastIndexOf("."));
					var newName = map(title) + ext, {parent} = file;
					var newFile = parent.clone();
					newFile.append(newName);
					try {
						newFile.createUnique(file.NORMAL_FILE_TYPE, file.permissions);
						file.renameTo(parent, newFile.leafName);
						download.target.path = newFile.path;
						download.refresh();
					} catch {}
				}
			}));
			Object.defineProperty(this, "set", {get() {
				try {var dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {var dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
				var arr = prefs.getStringPref("ucf.savedirs", "_Web||_Images|0").split('|').slice(2, 4);
				// подпапки в [Загрузках]: нет | папка графики | имя вкладки | домен
				arr[1] = (arr[1]) ? wref.get().gBrowser.selectedTab.label.slice(0, 64).replace(/ \| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim() : ""; // имя вкладки
				arr.map(map).forEach(dir.append); dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777);
				return dir.path;
			}});
			return this.set;
		}},
		"browser.download.folderList": {type: "Int", set: 2},
		"browser.download.useDownloadDir": {type: "Bool", set: true}
	});
	var MouseImgSaverParent = class extends JSWindowActorParent {
		receiveMessage(msg) {
			var win = msg.target.browsingContext.topChromeWindow, {name} = msg;
			if (name) return this[name](win, msg.data);

			var {url, contentType, contentDisposition, sx, title,
				isPrivate, referrerInfo, cookieJarSettings} = msg.data;
			if (sx && sx > win.mozInnerScreenX + win.innerWidth) return;

			if (title) titles[url] = title;

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

			for(var pref in data) {
				var obj = data[pref], meth = `et${obj.type}Pref`;
				obj.val = p.prefHasUserValue(pref) ? p["g" + meth](pref) : null;
				p["s" + meth](pref, obj.set);
			}
			try {win.internalSave(
				url,
				null, // original url
				null, // document
				null, // file name
				contentDisposition,
				contentType,
				false, // do not bypass the cache
				null, // filepicker title key
				null, // chosen data
				u.E10SUtils.deserializeReferrerInfo(referrerInfo),
				u.E10SUtils.deserializeCookieJarSettings(cookieJarSettings),
				win.document, // initiating doc
				true, // skip prompt for where to save
				null, // cache key
				isPrivate,
				win.document.nodePrincipal
			);}
			finally {
				for(var pref in data) data[pref].val === null
					? p.clearUserPref(pref)
					: p[`set${data[pref].type}Pref`](pref, data[pref].val);
			}
		}
		dblclick(win, imgURL) {
			var gb = win.gBrowser, index = gb.selectedTab._tPos + 1;
			gb.selectedTab = gb.addTrustedTab('https://yandex.ru/images/search?rpt=imageview&url=' + imgURL, {index});
		}
	}
}

Dumby
К сожалению не работает, или я не знаю как его прописать.

_zt пишет

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

Да обычная jsm'ка, как и другие. То есть ChromeUtils.import("урл");
Чтоб совсем уж не работало это вряд ли. Откровенно "внешние" ссылки
должны открываться в новой вкладке, во всяком случае те, которые в есть в html сразу.
А вот конфигурация — вообще непонятно, ну кроме background, insert, setParent.

Dumby
Точно, забыл ее в лоадере проверить. Работает не хуже чем в обезьяне, на первый взгляд. Спасибо.
   
Я не знаю чего он там учесть пытался, без parent: true host: true она работает плохо, все подряд открывает в новой, в том числе и когда здесь по имени щелкаешь. Возможно последовательно опции добавлял, да так и оставил, у меня все как по умолчанию.

Привет мастерам!
Ребят, пару кнопок из шапки задействовал, работают прекрасно, спасибо большое создателям. Единственное, что поменял бы на свой вкус - сами иконки кнопок. Подскажите как можно это сделать, пожалуйста!

ez7pac
Свою иконку конвертируете в формат base64. Онлайн-конвертеров в сети полно.
Ищете в коде кнопки что-то типа такого

Выделить код

Код:

"chrome://browser/skin/search-engine-placeholder.png"

Это иконка из [firefox], можно взять то, что между двойными кавычками, вставить в адресную строку [firefox] и посмотреть. Заменяете на свой код.
Или смотрите уже сконвертированные иконки в коде типа

Выделить код

Код:

"data:image/что-то_непонятное"

Механизм такой же - заменяете на свой код то, что между двойными кавычками.

xrun1
Ага, понял, спасибо!
Такого нет:

Выделить код

Код:

"chrome://browser/skin/***.png"

Т.е. иконка не дефолтная, а своя.
Вот такое есть

Выделить код

Код:

"data:image/что-то_непонятное"

С конвертацией и заменой разберусь, думаю. Но тогда другой вопрос возник - а нельзя вместо кода иконки, вот этого "непонятного", воткнуть в код кнопки дефолтную иконку? Я там (chrome://browser/skin/browser.css) глянул - подходящие для меня есть.
Если можно, то как это сделать, чтобы не накосячить?

ez7pac
Вместо содержимого в двойных кавычках вставляете ссылку. Важно то, что данные в base64 или ссылка должны быть заключены в двойные кавычки.

ez7pac
Ещё можно так:

скрытый текст
варианты:

Выделить код

Код:

#ID кнопки> .toolbarbutton-icon {
    list-style-image: url("chrome://browser/skin/history.svg");
}
Выделить код

Код:

#ID кнопки {
    list-style-image: url("./image/своя иконка.png") !important;
}

:offtopic:

xrun1
Спасибо за консультацию. Воткнул дефолтную иконку вместо авторской - кнопка работает, все нормально.
kokoss
Спасибо, но это для меня слишком сложно, я дуб в этих делах. Заморачиваюсь максимум раз в два, а то и три года, когда приходится переходить на более свежую версию браузера. То, что помнил после прошлого перехода, давно вылетело из головы :D
+++
Ребят, теперь еще вопрос возник. Я за эти кнопки зацепился потому, что Add Toolbars Buttons на 102-й версии работает не полноценно, а к трем кнопкам оттуда привык, не хочется их терять. Две нашел в этой теме - выпадающее меню расширений и перезапуск браузера. Осталась кнопка менеджера паролей, но тут ее не вижу. Может плохо искал? Не поможете с ней?
Или, возможно, автор таки собирается обновить расширение, так мне намного проще, честно сказать. Он, по моему, тоже в этой теме бывает. Виталий, если не ошибаюсь.
+++
ЗЫ. Еще забыл. Вот кнопка меню расширений. Как можно переназначить клавиши? Т.е. , в ней менеджер дополнений открывается СКМ, а мне было бы привычней ПКМ.

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

Выделить код

Код:

try {(() => {
    var id = "ucf-aom-button",
    label = "Дополнения",
    tooltiptext = "ЛКМ: Меню дополнений\nShift+ЛКМ: Меню дополнений + открыть менеджер\nСКМ: Открыть менеджер дополнений",
    img = "chrome://mozapps/skin/extensions/extension.svg",
    show_version = true,
    show_description = true,
    user_permissions = true,
    show_hidden = true,
    show_disabled = true,
    enabled_first = true,
    exceptions_listset = new Set([

    ]);
    exceptions_type_listset = new Set([

    ]);
    if (!("AddonManager" in this))
        ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
    if (!("GlobalManager" in this))
        XPCOMUtils.defineLazyGetter(this, "GlobalManager", () =>
    ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm").ExtensionParent.GlobalManager);
    var extensionOptionsMenu = {
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        get clipboardHelp() {
            delete this.clipboardHelp;
            return this.clipboardHelp = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
        },
        get exceptions_type_listarr() {
            delete this.exceptions_type_listarr;
            var arr = ["extension", "theme", "locale", "dictionary"];
            if (!exceptions_type_listset.size)
                return this.exceptions_type_listarr = arr;
            return this.exceptions_type_listarr = arr.filter(type => !exceptions_type_listset.has(type));
        },
        populateMenu: async function(e) {
            var popup = e.target, doc = e.view.document;
            var addons = await AddonManager.getAddonsByTypes(this.exceptions_type_listarr);
            var addonsMap = new WeakMap(),
            setAttributesMenu = (mi, addon, extension) => {
                var permissions, uuid,
                props = {
                    label: `${addon.name}${show_version ? ` ${addon.version}` : ""}`,
                    class: "menuitem-iconic",
                    tooltiptext: `${(show_description && addon.description) ? `${addon.description}\n` : ""}ID: ${addon.id}${addon.isActive && (uuid = extension?.uuid) ? `\nUUID: ${uuid}` : ""}${(user_permissions && (permissions = addon.userPermissions?.permissions)?.length) ? `\nРазрешения: ${permissions.join(", ")}` : ""}\n${addon.optionsURL ? "\nЛКМ: Настройки" : ""}\nCtrl+ЛКМ: Копировать ID${uuid ? "\nShift+ЛКМ: Копировать UUID" : ""}${addon.creator?.url ? "\nCtrl+Shift+ЛКМ: Автор" : ""}${addon.homepageURL ? "\nСКМ: Домашняя страница" : ""}${!addon.isBuiltin ? "\nCtrl+СКМ: Просмотр источника" : ""}\nShift+СКМ: Просмотр источника во вкладке\nПКМ: Включить/Отключить${(!addon.isSystem && !addon.isBuiltin) ? "\nCtrl+ПКМ: Удалить" : ""}`,
                };
                for (let p in props)
                    mi.setAttribute(p, props[p]);
                if (addon.iconURL)
                    mi.setAttribute("image", addon.iconURL);
                var cls = mi.classList;
                addon.isActive ? cls.remove("ucf-disabled") : cls.add("ucf-disabled");
                addon.optionsURL ? cls.remove("ucf-notoptions") : cls.add("ucf-notoptions");
                addon.isSystem ? cls.add("ucf-system") : cls.remove("ucf-system");
                cls.add(`ucf-type-${addon.type}`);
            };
            addons.filter(a => !(a.iconURL || "").startsWith("resource://search-extensions/")).sort((a, b) => {
                var ka = `${(enabled_first ? a.isActive ? "0" : "1" : "")}${a.type || ""}${a.name.toLowerCase()}`;
                var kb = `${(enabled_first ? b.isActive ? "0" : "1" : "")}${b.type || ""}${b.name.toLowerCase()}`;
                return (ka < kb) ? -1 : 1;
            }).forEach(addon => {
                if (!exceptions_listset.has(addon.id) &&
                    (!addon.hidden || show_hidden) &&
                    (!addon.userDisabled || show_disabled)) {
                    let extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.append(mi);
                    addonsMap.set(addon, mi);
                }
            });
            var click = (e) => {
                this.handleClick(e);
            };
            popup.addEventListener("click", click);
            var listener = {
                onEnabled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi)
                        setAttributesMenu(mi, addon, mi._Extension);
                },
                onDisabled: addon => {
                    listener.onEnabled(addon);
                },
                onInstalled: addon => {
                    var extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.prepend(mi);
                    addonsMap.set(addon, mi);
                },
                onUninstalled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi) {
                        mi.remove();
                        addonsMap.delete(addon);
                    }
                },
            };
            AddonManager.addAddonListener(listener);
            popup.addEventListener("popuphiding", (e) => {
                AddonManager.removeAddonListener(listener);
                popup.removeEventListener("click", click);
                addonsMap = null;
                while (popup.hasChildNodes())
                    popup.firstChild.remove();
            }, { once: true });
        },
        handleClick: function(e) {
            var win = e.view, mi = e.target;
            if (!("_Addon" in mi) || !("_Extension" in mi))
                return;
            var addon = mi._Addon, extension = mi._Extension;
            switch (e.button) {
                case 0:
                    if (e.ctrlKey && e.shiftKey) {
                        if (addon.creator?.url)
                            win.gBrowser.selectedTab = this.addTab(win, addon.creator.url);
                    } else if (e.ctrlKey) {
                        this.clipboardHelp.copyString(addon.id);
                        try {
                            this.alertsService.showAlertNotification(`${img}`, "ID в буфере обмена!", addon.id, false);
                        } catch(e) {}
                    } else if (e.shiftKey) {
                        if (extension?.uuid) {
                            this.clipboardHelp.copyString(extension.uuid);
                            try {
                                this.alertsService.showAlertNotification(`${img}`, "UUID в буфере обмена!", extension.uuid, false);
                            } catch(e) {}
                        }
                    } else if (addon.isActive && addon.optionsURL)
                        this.openAddonOptions(addon, win);
                    win.closeMenus(mi);
                    break;
                case 1:
                    if (e.ctrlKey) {
                        if (!addon.isBuiltin)
                            this.browseDir(addon);
                    } else if (e.shiftKey)
                        this.browseDir(addon, win);
                    else if (addon.homepageURL)
                        win.gBrowser.selectedTab = this.addTab(win, addon.homepageURL);
                    win.closeMenus(mi);
                    break;
                case 2:
                    if (!e.ctrlKey) {
                        let endis = addon.userDisabled ? "enable" : "disable";
                        if (addon.id == "screenshots@mozilla.org")
                            Services.prefs.setBoolPref("extensions.screenshots.disabled", !addon.userDisabled);
                        else if (addon.id == "webcompat-reporter@mozilla.org")
                            Services.prefs.setBoolPref("extensions.webcompat-reporter.enabled", addon.userDisabled);
                        addon[endis]({ allowSystemAddons: true });
                    } else if (!addon.isSystem && !addon.isBuiltin && Services.prompt.confirm(win, null, `Удалить ${addon.name}?`))
                        addon.uninstall();
                break;
            }
        },
        openAddonOptions: function(addon, win) {
            switch (addon.optionsType) {
                case 5:
                    win.BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(addon.id)}/preferences`);
                    break;
                case 3:
                    win.switchToTabHavingURI(addon.optionsURL, true);
                    break;
            }
        },
        browseDir: function(addon, win) {
            try {
                if (!win) {
                    let file = Services.io.getProtocolHandler("file")
                    .QueryInterface(Ci.nsIFileProtocolHandler)
                    .getFileFromURLSpec(addon.getResourceURI().QueryInterface(Ci.nsIJARURI).JARFile.spec);
                    if (file.exists())
                        file.launch();
                } else
                    win.gBrowser.selectedTab = this.addTab(win, addon.getResourceURI().spec);
            } catch (e) {}
        },
        addTab: function(win, url, params = {}) {
            params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
            params.relatedToCurrent = true;
            return win.gBrowser.addTab(url, params);
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild: function(doc) {
            var btn = doc.createXULElement("toolbarbutton"), win = doc.defaultView,
            props = {
                id: id,
                label: label,
                tooltiptext: tooltiptext,
                type: "menu",
                class: "toolbarbutton-1 chromeclass-toolbar-additional",
            };
            for (let p in props)
                btn.setAttribute(p, props[p]);
            btn.addEventListener("click", (e) => {
                if (e.button == 0) {
                    if (e.shiftKey)
                        win.BrowserOpenAddonsMgr();
                } else if (e.button == 1)
                    win.BrowserOpenAddonsMgr();
            });
            var mp = doc.createXULElement("menupopup");
            mp.id = `${id}-popup`;
            mp.addEventListener("click", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });
            mp.addEventListener("contextmenu", (e) => {
                e.preventDefault();
                e.stopPropagation();
            });
            mp.addEventListener("popupshowing", (e) => {
                extensionOptionsMenu.populateMenu(e);
            });
            btn.append(mp);
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id}, #${id}-popup menuitem {
                    list-style-image: url("${img}") !important;
                }
                #${id}-popup menuitem::after {
                    display: -moz-box !important;
                    content: "" !important;
                    height: 16px !important;
                    width: 16px !important;
                    padding: 0 !important;
                    border: 1px solid rgb(0, 116, 232) !important;
                    border-radius: 0 !important;
                    background-repeat: no-repeat !important;
                    background-position: center !important;
                    background-size: 16px !important;
                    background-color: rgb(0, 116, 232) !important;
                    background-image: url("${checked}") !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem.ucf-disabled::after {
                    border-color: currentColor !important;
                    background-color: transparent !important;
                    background-image: none !important;
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-disabled > label,
                #${id}-popup menuitem.ucf-notoptions > label {
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-system > label {
                    text-decoration: underline !important;
                    text-decoration-style: dotted !important;
                }
                #${id}-popup menuitem > label {
                    margin-inline-end: 0 !important;
                }
                #${id}-popup menuitem > .menu-accel-container {
                    display: -moz-box !important;
                    padding: 4px !important;
                    margin: 0 !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem > .menu-accel-container .menu-iconic-accel {
                    display: -moz-box !important;
                    margin: 0 !important;
                    height: 8px !important;
                    width: 8px !important;
                    border-radius: 4px !important;
                    background-color: transparent !important;
                    opacity: 1 !important;
                    font-size: 0 !important;
                }
                #${id}-popup menuitem.ucf-type-dictionary > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(227, 27, 93) !important;
                }
                #${id}-popup menuitem.ucf-type-locale > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(48, 172, 55) !important;
                }
                #${id}-popup menuitem.ucf-type-theme > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(219, 106, 0) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            return btn;
        },
    });
})();} catch (e) {}

ez7pac пишет

Я за эти кнопки зацепился потому, что Add Toolbars Buttons на 102-й версии работает не полноценно,

скрытый текст
Да вроде работает, просто обновите код для отключения проверки... -> https://forum.mozilla-russia.org/viewto … 59#p800159, там ещё была проблема с кнопкой "Дополнения", здесь решение -> https://forum.mozilla-russia.org/viewto … 83#p799883

ez7pac пишет

Add Toolbars Buttons на 102-й версии работает не полноценно, а к трем кнопкам оттуда привык, не хочется их терять.

Всё работает, спасибо Dumby, №340

kokoss
voqabuhe
Ай, спасибо! Значит, не нашел. А из расширения перехожу на домашнюю страницу - 404.
+++
Все, разобрался, отредактировал, расширение заработало как положено.
Спасибо за помощь и консультации еще раз, парни! :beer:
+++
Опять 25
С кодом для отключения проверки не разберусь никак! Ткните носом, плиз, где он прячется.

ez7pac пишет

С кодом для отключения проверки не разберусь никак! Ткните носом, плиз, где он прячется.

В папке установки [firefox], в файле config.js.

kokoss пишет

В папке установки , в файле config.js.

В папке браузера? И добавлял этот код - https://forum.mozilla-russia.org/viewtopic.php?pid=800159#p800159, и заменял то, что там было - пофиг

скрытый текст
screenshot2022-07-13001.1657735664.png

ez7pac пишет

В папке браузера? И добавлял этот код - https://forum.mozilla-russia.org/viewtopic.php?pid=800159#p800159, и заменял то, что там было - пофиг

Инструкция, с описанием куда добавить: https://forum.mozilla-russia.org/viewtopic.php?id=70326

kokoss
Оба файла присутствуют по дефолту. Портабл родом отсюда - https://github.com/wvxwxvw/LibPortablePlus
И config.js, и config-prefs.js (код там правильный) есть и лежат на своих местах. Так дописывал код в config.js к имеющемуся, и заменял - эффекта нет - "работа дополнения не была проверена...". Хотя само расширение работает нормально. В 91-й версии никаких предупреждений нет. Вот и чешу репу - что я делаю не так...

ez7pac пишет

Осталась кнопка менеджера паролей, но тут ее не вижу. Может плохо искал? Не поможете с ней?

ссылки в коде

Выделить код

Код:

/* https://forum.mozilla-russia.org/viewtopic.php?pid=788786#p788786
  есть для окна куков другой код, запоминает положение окна https://forum.mozilla-russia.org/viewtopic.php?pid=788799#p788799 */
try {CustomizableUI.createWidget({
	label: "ПАРОЛИ/КУКИ",
	tooltiptext: "ЛКМ: См. ПАРОЛИ\nПКМ: См. КУКИ",
	id: "ucf-logins-sitedata",
	localized: false,
	onCreated(btn) {
		btn._handleClick = btn.oncontextmenu = e => this.view(e, btn.ownerGlobal);
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAADo0lEQVR42pWUf0idVRzGP+d4vVfbFFeu/SErV1bUBiI6ivajUcEiiiBoaTEEo7Lsj2W1P6Q2xFiL0Qp0q/7pB2GLijZGG/aDSPuBxBbkGm3O7IJuRJmod+t6r+89p+d9b6TWhu17uefe97zn+5zn+3yfcwznCz9e7c2S5fr3i8Ec5yLCzHtq96txdGNZSoE+a8mxgRG2cx/t5uTFAd7u17GOPrbl9oDdibGjdPgrtOIlwd7TtI2aN8zCbGcBn/dJJb7LdtP2n1XP+Q8Z+a2St5bVzVPmlPInBVKHnw/Y7tdr7OE7yjhksnM2sTxrHJ/7Sr0booIbaDSDeC9pjec8EQH6TdRzgofNALddqBR/HWNU84B5n0+j533EWcYKKZwyjZyZz/BJ36XxVkrpZPDnOMUJy4xmCmOSsxDSZwPKrtylhjXwqtnvR3mGczxBwOXEbIaU+4YMLWYNSUPTcA9pt1GLFQEUFek3fBCQE6oNp204NUXvWIUfrGsmyS6mNV/bo+F6bfg0HP/gNHWsMmwaDCKLhGGtwwks5mweKcS2OawrYOJchz9Y08ExsjgxTweOm/pNhhvD3YPE+B0JPv5kh2HzcEogi0XOEQtfCcxGdKeEWRqRDfHHf7/L71hjuYSDpFVFThQvXeGpOkqaJa6YPsPXt/xqaBhWoitRWi4PmFH2oja6K3fyYLILm20hq6ljRzb6vfUZyvmS6bi2KTBk0lpaDtVbHGO9hhOf/WFoTKaEIoaiFdO6YMZhF9fyzvIBGpJ3knOHKEy8RnfFY35AJnexVn1d1FAn/81MezUklC3Bn7wSAk4JpYRpARa5nMovJBv7iKnE/ZS508RzX/B2ZYP/kRcFslXsQq3lQSeHSPrY338X5Yaoyq6eZZgVYFyAQUbtLe7lK1fP0p+2cvTuVn9SzNK05hsVWslGWNhgMqqqwB0W10fN1fLkPxo6aRjHRQydPUV31bWRgb+3ezT/ePiGbNwSm56Uo/qY4GW1bUgbeXMvo7PG3jw8KcDSqMs2ZBgaMPYm+65qkoFfV+IjODVBFlUFlkTwgrmGNi4QAkz6yHuhP0JtstYwemaV79xQBdkDcnq+UiffpYNmbuY9Y0jNOZQS0My5HJpGugTYooLziamz8EP/Wn/4oRpm4p1IAbl4QjrtNytp+n/XV/PYSpV5GaYge2RvSXHtt/JaGVsk9O6o1BKekuC7FwKbfx/++3Y5oINvdFBzGsvpN+uj07tg/AUtF37/2LRsMgAAAABJRU5ErkJggg==");
	},
	view(e, win) {
		if (e && (e.ctrlKey || e.shiftKey)) return;

		var uri = win.gBrowser.selectedBrowser.currentURI;
		try {
			var url = win.ReaderMode.getOriginalUrl(uri.spec);
			if (url) uri = Services.io.newURI(url);
		} catch {}

		try {var tld = Services.eTLD.getBaseDomain(uri);}
		catch {var tld = uri.asciiHost;}
		e ? this.viewCookies(tld, win) : this.viewPasswords(tld, uri, win);
		return false;
	},
	viewPasswords(tld, uri, win) {
		try {
			tld = Services.io.newURI(`${uri.scheme}://${tld}`).displayHost;
		} catch {}

		var params = new win.URLSearchParams({...(tld && {filter: tld})});
		var gb = win.gBrowser;
		var separator = params.toString() ? "?" : "";
		var tabToSelect, url = `about:logins${separator}${params}`;

		for (var tab of gb.visibleTabs) {
			var {spec} = tab.linkedBrowser.currentURI;
			if (!spec.startsWith("about:logins")) continue;

			if (spec != url) {
				var pending = tab.hasAttribute("pending");
				if (pending) gb.selectedTab = tab;
				tab.linkedBrowser.loadURI(
					url, {triggeringPrincipal: tab.nodePrincipal}
				);
				if (pending) return;
			}
			tabToSelect = tab;
			break;
		}
		gb.selectedTab = tabToSelect || gb.addTrustedTab(url);
	},
	async viewCookies(tld, window) {
		var notFound, wt = "Browser:SiteDataSettings";
		var url = "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml";
		var win = Services.wm.getMostRecentWindow(wt);

		if (!win) {
			notFound = true;
			await window.SiteDataManager.updateSites();
			win = window.openDialog(url, wt, "chrome,dialog=no,centerscreen,resizable");

			var e = await new Promise(resolve =>
				win.windowRoot.addEventListener("DOMContentLoaded", resolve, {once: true})
			);
			win = e.target.ownerGlobal;
		}
		var doc = win.document, de = doc.documentElement;
		de.setAttribute("persist", "screenX screenY width height");
		if (notFound) {
			de.setAttribute("windowtype", wt);
			var xs = Services.xulStore, {id} = de;

			var x = xs.getValue(url, id, "screenX");
			var y = xs.getValue(url, id, "screenY");
			x && de.setAttribute("screenX", x);
			y && de.setAttribute("screenY", y);
		}
		var sb = doc.querySelector("#searchBox");
		sb.inputField.setUserInput(tld);
		setTimeout(() => sb.editor.selection.collapseToEnd(), 50);
		notFound || win.focus();
	}
});} catch(ex) {Cu.reportError(ex);}

13-07-2022 22:02:50
А какие кнопки не работают в расширении и что с ним не так?
Сообщения на форуме видел, но у меня работает. Может, не теми кнопками пользуюсь, вот и прошу конкретики.:)

xrun1, спасибо.

А какие кнопки не работают в расширении и что с ним не так?

Не работало одно только выпадающее меню с расширениями, с другими кнопками все было нормально. Но теперь уже подсказали как поправить само расширение, теперь все работает без вопросов, так что коды кнопок уже не нужны. С ATB мне гораздо проще. Пользуюсь обычно четырьмя кнопками - восстановить закрытую вкладку, пароли, перезагрузка браузера и меню расширений. Иногда прокси вытаскиваю.

ez7pac пишет

и заменял - эффекта нет

А перезапустить с очисткой кэша запуска...


Add. если проблема только в окошке с предупреждением...

скрыть

Выделить код

Код:

@-moz-document url-prefix("chrome://mozapps/content/extensions/aboutaddons.html"), url("about:addons"){
div.container {
   display: none !important;
}
}

добавить в userContent.css

kokoss

А перезапустить с очисткой кэша запуска...

Само собой.

Add. если проблема только в окошке с предупреждением...

Так пропало предупреждение. Спасибо :beer:
Не то, чтобы оно сильно мешало, но на мозги немного капало :D

ez7pac
Странно что у вас в [firefox] 102 появилось это окошко с предупреждением..., у меня в [firefox] 102 такого окошка с предупреждением... нет! Оно попадалось мне только в ESR ветке при использовании настроек в конфиге:

Выделить код

Код:

extensions.experiments.enabled = true
xpinstall.signatures.required = false

отключающие проверку...

kokoss
У меня именно ESR и именно эти настройки в конфиге. При том, что их я не трогал точно. Обычно настраиваю только general.smoothScroll.mouseWheel.duration, мин-макс по 500 выставляю и browser.tabs.loadBookmarksInTabs - true. Все, больше ничего не трогаю.
Да и настройки эти неактивны, в смысле - я их не могу изменить. Скорее всего, автор сборки что-то там подкрутил. Ну, или она не дружит со 102-й версией. Я использовал архив для 91-й.

ez7pac пишет

У меня именно ESR и именно эти настройки в конфиге.

С этого и надо было начать, тогда код для отключения... не нужен, достаточно переключить настройки в конфиге, которые я привёл выше + стиль для скрытия..., а не активны они потому что автор сборки видимо закинул их в файл config.js.

kokoss, дружище! Странный глюк вылез вот с этим кодом в userContent.css:

Выделить код

Код:

div.container {
   display: none !important;
}

Заметил не сразу, потому что он ломает только некоторые сайты. Вот хороший пример - http://tundra-japonamat.ru/index.html
По самому верху страницы ссылки навигации по сайту. Так вот при добавлении этого кода эти ссылки тупо пропадают. И еще счетчик от Хотлог в низу страницы. Ну, это мелочь. Перерыл все расширения и т.п., искал виновника, и вышел таки именно на этот код. Удаляю его из userContent - сайт начинает отображаться правильно, возвращаю - ломается опять...
Может там у меня со стилями что-то не то? Глянь, пожалуйста, если не в лом. Мой userContent.css без вот этого кода для скрытия предупреждений:

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

Выделить код

Код:

@namespace url(http://www.w3.org/1999/xhtml);

@-moz-document domain("www.google.ru")
{
#newsbox span.tl>a, #newsbox span.tl>a.l {
    font-size: 13px;
    text-decoration: underline;
}
#res h3 {
    font-size: medium;
    text-decoration: underline;
}
#tads a, #tadsb a, #res a, #rhs a, #taw a {
    text-decoration: underline ;
}
div.nrg-title>a, div.irg-title>a {
    font-size: medium;
}
span.tl>a {
    font-size: 13px;
}
.rgsep, .g-blk, #extrares {
    display: none !important;
}
div#pushdown, div.pdp-psy, div.pmoabs {
    display: none !important;
}
div.rhsvw.kno-ftr {
    display: none !important;
}
#res h3 {
    font-size: medium !important;
}
.spell {
    font-size: 15px !important;
}
#_L8b {
    display: none !important;
}
div._eNq span {
    font-size: 15px !important;
    text-decoration: underline !important;
}
div.gb_g.gb_Lc.gb_Xc.gb_Wc > .gb_ha {
    display: none !important;
}
.mslg .l {
    font-size: 15px !important;
}
._dTj {
    font-size: 14px !important;
}
._vSj {
    line-height: 16px !important;
}
.xIqs0b {
    font-size: 15px !important;
}
html, body {
text-shadow: 0px 0px 0px !important;
}
}

@-moz-document domain("www.google.com")
{
#botstuff {
    display: none !important;
}
#brs {
    display: none !important;
}
div.vk_c.rhsvw.vk_rhsc.kp-blk {
    display: none !important;
}
h3.r {
    font-size: 15px;
    text-decoration: underline;
}
h3.r a {
    font-size: 15px;
}
#newsbox span.tl > a {
    font-size: 13px;
    text-decoration: underline;
}
div.nrg-title._Hb>a, div.irg-title._Hb>a, div.nrg-title._zb>a, div._qi._zb>a, a._ef.nrg-footer, a._ef.irg-footer {
    font-size: 15px ;
    text-decoration: underline;
}
span.spell.ng,a.spell b i  {
    font-size: 15px;
}
._Dk, ._wI, ._wI a {
    font-size: 15px !important;
}
#res h3 {
    font-size: medium !important;
}
.spell {
    font-size: 15px !important;
}
#_L8b {
    display: none !important;
}
div._eNq span {
    font-size: 15px !important;
    text-decoration: underline !important;
}
div.gb_g.gb_Lc.gb_Xc.gb_Wc > .gb_ha {
    display: none !important;
}
.mslg .l {
    font-size: 15px !important;
}
._dTj {
    font-size: 14px !important;
}
._vSj {
    line-height: 16px !important;
}
.xIqs0b {
    font-size: 15px !important;
}
html, body {
text-shadow: 0px 0px 0px !important;
}
}

@-moz-document domain("yandex.ru")
{
.b-advertizing-and-wizards,.b-serp-item_last_yes {
    display: none !important;
}
.b-body-items_type_main .b-serp-item:nth-child(11), .b-body-items_type_main .b-serp-item:nth-child(12), .b-body-items_type_main .b-serp-item:nth-child(13), .b-body-items_type_main .b-serp-item:nth-child(14), .b-body-items_type_main .b-serp-item:nth-child(15), .b-body-items_type_main .b-serp-item:nth-child(16), .b-body-items_type_main .b-serp-item:nth-child(17) {
    display: none !important;
}
a.b-spec-adv__direct {
    display: none !important;
}
div.b-person-results.g-gap-vertical.g-gap-horizontal {
    display: none !important;
}
div.b-related.g-gap-vertical.g-gap-horizontal {
    display: none !important;
}
div.b-spec-adv.b-spec-adv_serp-margin_yes.g-gap-vertical.b-spec-adv.b-spec-adv_type_highlighted.i-bem {
    background-color: #fff;
    font-size: 0;
    height: 0;
    margin-left: -1200px;
    padding: 0;
    width: 0;
}
div.z-audio__flash {
    display: none !important;
}
li.z-audio.b-serp-item.i-bem.z-audio_js_inited {
    display: none !important;
}
p.b-filters__intents.b-filters__intents_level_1 {
    display: none !important;
}
.logo, .b-logo {
    opacity: 0.3 !important;
}
.logo:hover {
    opacity: 1 !important;
}
.serp-item__title {
    font-size: medium !important;
    font-weight: normal !important;
    text-decoration: underline;
}

a.link.serp-item__title-link {
    font-size: 15px !important;
    text-decoration: underline !important;
}

a.link.distro__link.distro__item, div.distro__item {
    display: none !important;
}

div.distro.scroll-spy, div.z-default-search {
    display: none !important;
}

div.footer-stripe, div.footer-stripe__content {
    display: none !important;
}
}

@-moz-document domain("ya.ru")
{
.suggest2-form__input {
    background-color: #eee !important;
}
.input__control {
	color: #444 !important;
}
.button_theme_websearch  {
    background-color: #eee !important;
	color: #aaa !important;
}
}

@-moz-document domain("new-rutor.org")
{
body {
    background-image: none !important;
    padding: 0 !important;
}
#pluso-panel, #all, div#ws {
    width: 100% !important;
}
div#ws div#content {
    width: 75% !important;
}
.sideblock2 {
    display: none !important;
}
}

@-moz-document domain("bing.com")
{
h2, h3, .b_no h1 {
    font-size: medium !important;
}
}

@-moz-document domain("duckduckgo.com")
{
a.large, a.result__a  {
    font-size: medium !important;
}
.add-to-browser-badge, div.foot-home.js-foot-home, div.tag-home.tag-home--slide.no-js__hide.js-tag-home, div.results--powered  {
    display: none !important;
}
div.header-wrap--home.js-header-wrap {
    opacity: .5 !important;
}
div.logo-wrap--home, span.header__logo, img#logo {
    opacity: .3 !important;
}
div.logo-wrap--home:hover, span.header__logo:hover, img#logo:hover {
    opacity: 1 !important;
}
.logo_homepage {
    background-size: 130px 100px !important;
    height: 100px !important;
    width: 130px !important;
}

a.badge-link__wrap.js-badge-main-msg,
div.badge-link.badge-link--top.js-badge-link,
div.tag-home,
div.onboarding-bottom-bathroomguy-slide,
div.onboarding-bottom.onboarding-bottom-bathroomguy {
    display: none !important;
}
div.tag-home__wrapper,div.hide--screen-xs {
    display: none !important;
}
div.onboarding-ed__slide,div.onboarding-ed,div.badge-link {
    display: none !important;
}
}

@-moz-document domain("1tv.ru")
{
body {
    background-color: #000 !important;
}
.navbar,
.menu {
    background-color: #eee !important;
}
article.live,
.theme-main {
    background-color: #f4f4f4 !important;
}
header#page-title {
    color: #b4b4b4 !important;
}
.theme-main > .live,
.theme-main > .live > .player-wrapper {
    background-color: #000 !important;
}
.footer {
    background-color: #000 !important;
	color: #f4f4f4 !important;
}

#mainalert,
#popupContainer,
#yandex_ad,
.itv-push-suggest,
.welcome-popup,
.promo-place,
.branding_promo_place,
.header-promo-place,
.side-promo-place-right,
.footer-promo-place,
.social-buttons-compact,
.social-widgets,
.small-social-buttons,
.footer-menu,
.yashare-auto-init.b-share_theme_counter,
.links > li:nth-of-type(7) {
    display: none !important;
}
}

@-moz-document domain("ntv.ru")
{
div.intop {
    display: none !important;
}
#newplayer {
    top: 0 !important;
}
div#push_alert,
div.wrap_top_flash,
input.check_rez,
ul.likeBtm.custom,
div.outtop,
#x8831 a img {
    display: none !important;
}
}

input:not([type="submit"]):not([type="reset"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]),
textarea {
    background: #ffff33 !important;
}


Собирал давно, таскал из версии в версию. Вполне возможно, что там куча лишнего или даже вредного. Может из-за этого и сайты ломаются, а твой код только катализатором выступает :D ...

ez7pac
Спасибо за подсказку, не подумал что может что то скрывать на сайтах. Тогда так:

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

Выделить код

Код:

@-moz-document url-prefix("chrome://mozapps/content/extensions/aboutaddons.html"), url("about:addons"){
div.container {
   display: none !important;
}
}

kokoss
Да, с этим кодом все шоколадно. Спасибо еще раз за помощь :beer:

Парни, а Simple Session Manager из шапки - куда код кидать?

ez7pac пишет

Парни, а Simple Session Manager из шапки - куда код кидать?

смотрите в шапке Демо-профиль, в нём множество скриптов/стилей уже подключено - файл CustomStylesScripts.jsm

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
	{ path: "custom_script.js", },
	{ path: "ucf_SessionManager.js", },

Dobrov, спасибо, но мне этого мало. В том смысле, что я в этих вопросах почти полный валенок. Так, вершков некоторых нахватался...
Скачал этот профиль, посмотрел, почитал - слишком много разных фишек, многие мне ни о чем не говорят, другие мне просто не нужны. Я хотел попробовать именно менеджер сессий. Сейчас юзаю Tab Session Manager, в принципе, терпимо, но убивает когда он при восстановлении сессии тусует вкладки как карточную колоду. Еще один похожий есть, с похожим интерфейсом, но там точно такая же ерунда. Вот и хотел пощупать этот, но что, куда и как - не понимаю. UserChromeFiles в наличии, а вот куда там добавить код этого менеджера - не понимаю.
Я не спец от слова совсем, подсказки на лету не поймаю, увы... Мне разжевывать надо :D

ez7pac
Вроде не тасует, если стоит галка "Восстановить расположение окон Восстановить размеры и расположение окон при открытии сессии."

ez7pac пишет

UserChromeFiles в наличии, а вот куда там добавить код этого менеджера - не понимаю.

повторюсь - файл CustomStylesScripts.jsm – добавить строку в секцию scriptsbackground: [ // В фоне [System Principal]
В этой теме в различных вариантах расписан порядок установки, всё есть в шапке и справке Демо-профиля. Ссылки снабжены всплывающими подсказками, например наведите мышь на startupCache в последней строке встроенной справки
Если вам описание установки в шапке не ясно, тогда пользуйтесь обычными дополнениями…

Dobrov
Не все пользуются версией с CustomStylesScripts.jsm и прочими scriptsbackground: [ // В фоне [System Principal]. Мне эта версия UCF непонятна, лично я на старой остался.

xrun1 пишет

Не все пользуются версией с CustomStylesScripts.jsm. Мне эта версия UCF непонятна, лично я на старой остался.

Дополнил шапку, добавил custom_script.js - скрипты добавлять здесь: (async () => { ["ucf_SessionManager.js"]. Процитирую скрипт из шапки:

Выделить код

Код:

// Этот скрипт можно использовать для создания кнопок с помощью CustomizableUI.createWidget
(async (scripts) => { // подключить внешние скрипты
  [['ucf_QuickToggle.js'], ['UCFTitleChangedChild.jsm', 'registerUCFTitleChanged'], ['Test.jsm']]
  .forEach(function(js) { try { if (/\.jsm$/i.test(js[0])) { // [скрипт js или jsm, инициализация]
        var obj = ChromeUtils.import(scripts + js[0]);
        js[1] && obj[js[1]]();
      } else Services.scriptloader.loadSubScript(scripts + js[0]);
    } catch(ex) {Cu.reportError(ex);}
  });
})('chrome://user_chrome_files/content/custom_scripts/');

/* вариант с отдельными скриптами:
(async () => { var loadscript = (name, funcName) => {
  try { var {href, pathname} = new URL(`chrome://user_chrome_files/content/custom_scripts/${name}`);
    if (/\.jsm$/i.test(pathname)) {
      var obj = ChromeUtils.import(href);
      funcName && obj[funcName]();
    } else
      Services.scriptloader.loadSubScript(href);
  }
  catch(ex) {Cu.reportError(ex);}
}
loadscript("ucf_aom-button.js");
loadscript("UCFTitleChangedChild.jsm", "registerUCFTitleChanged");
})();
*/
ez7pac пишет

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

Какие фишки непонятны? Описание есть в спойлере шапки и встроенной справке (спойлер "Полный список▼").
Сообщите, какие конкретно скрипты непонятны и я дополню описание.
.


Ещё можно убрать ненужные скрипты из CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm, оставив базовый ucf_hookClicks.js и нужный вам ucf_SessionManager.js. Впрочем дополнительные скрипты требуют значительно меньше памяти, чем дополнения.

ez7pac
xrun1
Этот скрипт... вроде работает в версии UCF 2021-6-5 -> https://forum.mozilla-russia.org/viewto … 17#p797517, код под первым спойлером.


Add, добавить в user_chrome_files/custom_scripts/custom_script.js

Dobrov пишет

Сообщите, какие конкретно скрипты непонятны и я дополню описание..

Да не то, чтобы непонятны, просто я мельком пробежался, сильно не вникал. Я ставлю обычно только то, что мне нужно и полезно на данный момент. Ставить пак, пусть и хороший, "шоб було" :D, ради одной фишки - не мое. Вот сейчас из всего списка меня заинтересовал только менеджер сессий. А перебирать пак, удаляя ненужное мне - это косяки, глюки, нервотрепка и т.п. Элементарно не хватит знаний.

kokoss пишет

Этот скрипт... вроде работает в версии UCF 2021-6-5 -> https://forum.mozilla-russia.org/viewto … 17#p797517, код под первым спойлером.

Спасибо. Но код - это хорошо, но еще бы знать куда его впихнуть и не накосячить при этом... :D
Я в принцип работы UCF не въезжаю вообще. userChrome, userContent - еще так, с пятое на десятое, и то нужно носом ткнуть - "вот этот код добавить вот сюда и не забудь вот эту запятую поставить". А UCF - это уже другой уровень.

kokoss пишет

Add, добавить в user_chrome_files/custom_scripts/custom_script.js

Только сейчас увидел ответ. И снова спасибо! Добавил, кнопка появилась, работает менеджер. Я извиняюсь, конечно, но возникли пара вопросов.
1. Работает только ручное сохранение или можно выставить в авто? Если можно, то как включить и как настроить периодичность?
2. Где хранятся сессии?

ez7pac пишет

1. Работает только ручное сохранение или можно выставить в авто? Если можно, то как включить и как настроить периодичность?
2. Где хранятся сессии?

Сесии храняться в «Профиль Firefox//chrome/simple_session_manager.json»
С авто-сохранением и таймером для него надо обращаться к Dumby, он этот скрипт делал.

Dobrov, понял, спасибо.

Уважаемые гуру, подскажите, пожалуйста. Есть вот такой скрипт, прекрасно работает на 78. А на 101 не грузится меню с подключёнными стилями (остальное работает). Это реально поправить?

kazarin пишет

Пробуй, проверял на [firefox] 103, в консоли ошибок нет...

UserCSSLoader.uc.js

Выделить код

Код:

// ==UserScript==
// @name           UserCSSLoader
// @description    Stylish みたいなもの
// @namespace      http://d.hatena.ne.jp/Griever/
// @author         Griever
// @include        main
// @license        MIT License
// @compatibility  Firefox 4
// @charset        UTF-8
// @version        0.0.4改
// @startup        UCL.init();
// @note           0.0.4 Remove E4X
// @note           CSSEntry クラスを作った
// @note           スタイルのテスト機能を作り直した
// @note           ファイルが削除された場合 rebuild 時に CSS を解除しメニューを消すようにした
// @note           uc で読み込まれた .uc.css の再読み込みに仮対応
// ==/UserScript==

/****** 使い方 ******

chrome フォルダに CSS フォルダが作成されるのでそこに .css をぶち込むだけ。
ファイル名が "xul-" で始まる物、".as.css" で終わる物は AGENT_SHEET で、それ以外は USER_SHEET で読み込む。
ファイルの内容はチェックしないので @namespace 忘れに注意。

メニューバーに CSS メニューが追加される
メニューを左クリックすると ON/OFF
					中クリックするとメニューを閉じずに ON/OFF
					右クリックするとエディタで開く

エディタは "view_source.editor.path" に指定されているものを使う
フォルダは "UserCSSLoader.FOLDER" にパスを入れれば変更可能

 **** 説明終わり ****/

(function(){

let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
if (!window.Services)
	Cu.import("resource://gre/modules/Services.jsm");
// 起動時に他の窓がある(2窓目の)場合は抜ける
let list = Services.wm.getEnumerator("navigator:browser");
while(list.hasMoreElements()){ if(list.getNext() != window) return; }

if (window.UCL) {
	window.UCL.destroy();
	delete window.UCL;
}
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

window.UCL = {
	IN_TOOLMENU: false,	// true: ツールメニュー内、 false:メインメニューのヘルプの前
	USE_UC: "UC" in window,
	AGENT_SHEET: Ci.nsIStyleSheetService.AGENT_SHEET,
	USER_SHEET : Ci.nsIStyleSheetService.USER_SHEET,
	readCSS    : {},
	get disabled_list() {
		let obj = [];
		try {
			obj = decodeURIComponent(this.prefs.getCharPref("disabled_list")).split("|");
		} catch(e) {}
		delete this.disabled_list;
		return this.disabled_list = obj;
	},
	get prefs() {
		delete this.prefs;
		return this.prefs = Services.prefs.getBranch("UserCSSLoader.")
	},
	get styleSheetServices(){
		delete this.styleSheetServices;
		return this.styleSheetServices = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
	},
	get FOLDER() {
		let aFolder;
		try {
			// UserCSSLoader.FOLDER があればそれを使う
			let folderPath = this.prefs.getCharPref("FOLDER");
			aFolder = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
			aFolder.initWithPath(folderPath);
		} catch (e) {
			aFolder = Services.dirsvc.get("UChrm", Ci.nsIFile);
			aFolder.appendRelativePath("CSS");
		}
		if (!aFolder.exists() || !aFolder.isDirectory()) {
			aFolder.create(Ci.nsIFile.DIRECTORY_TYPE, 0664);
		}
		delete this.FOLDER;
		return this.FOLDER = aFolder;
	},
	getFocusedWindow: function() {
		let win = document.commandDispatcher.focusedWindow;
		if (!win || win == window) win = content;
		return win;
	},
	init: function() {
		const cssmenu = $C("menu", {
			id: "usercssloader-menu",
			label: "CSS",
			accesskey: "C"
		});
		const menupopup = $C("menupopup", {
			id: "usercssloader-menupopup"
		});
		cssmenu.appendChild(menupopup);

		let menu = $C("menu", {
			label: "メニュ━━━(゚∀゚)━━━!!",
			accesskey: "C"
		});
		menupopup.appendChild(menu);
		let mp = $C("menupopup", { id: "usercssloader-submenupopup" });
		menu.appendChild(mp);
		mp.appendChild($C("menuitem", {
			label: "Rebuld",
			accesskey: "R",
			acceltext: "Alt + R",
			oncommand: "UCL.rebuild();"
		}));
		mp.appendChild($C("menuseparator"));
		mp.appendChild($C("menuitem", {
			label: "新規作成",
			accesskey: "N",
			oncommand: "UCL.create();"
		}));
		mp.appendChild($C("menuitem", {
			label: "CSS フォルダを開く",
			accesskey: "O",
			oncommand: "UCL.openFolder();"
		}));
		mp.appendChild($C("menuitem", {
			label: "userChrome.css を編集",
//			hidden: true,
			oncommand: "UCL.editUserCSS(\'userChrome.css\');"
		}));
		mp.appendChild($C("menuitem", {
			label: "userContent.css を編集",
//			hidden: true,
			oncommand: "UCL.editUserCSS(\'userContent.css\');"
		}));
		mp.appendChild($C("menuseparator"));
		mp.appendChild($C("menuitem", {
			label: "スタイルのテスト (Chrome)",
			id: "usercssloader-test-chrome",
			hidden: true,
			accesskey: "C",
			oncommand: "UCL.styleTest(window);"
		}));
		mp.appendChild($C("menuitem", {
			label: "スタイルのテスト (Webページ)",
			id: "usercssloader-test-content",
			hidden: true,
			accesskey: "W",
			oncommand: "UCL.styleTest();"
		}));
		mp.appendChild($C("menuitem", {
			label: "userstyles.org でスタイルを検索",
			accesskey: "S",
			oncommand: "UCL.searchStyle();"
		}));

		menu = $C("menu", {
			label: ".uc.css",
			accesskey: "U",
			hidden: !UCL.USE_UC
		});
		menupopup.appendChild(menu);
		mp = $C("menupopup", { id: "usercssloader-ucmenupopup" });
		menu.appendChild(mp);
		mp.appendChild($C("menuitem", {
			label: "Rebuild(.uc.js)",
			oncommand: "UCL.UCrebuild();"
		}));
		mp.appendChild($C("menuseparator", { id: "usercssloader-ucsepalator" }));

		if (this.IN_TOOLMENU) {
			$('menu_ToolsPopup').insertBefore(cssmenu, $('menu_preferences'));
		} else {
			$('main-menubar').insertBefore(cssmenu, $('helpMenu'));
		}

		$("mainKeyset").appendChild($C("key", {
			id: "usercssloader-rebuild-key",
			oncommand: "UCL.rebuild();",
			key: "R",
			modifiers: "alt",
		}));

		this.rebuild();
		this.initialized = true;
		if (UCL.USE_UC) {
			setTimeout(function() {
				UCL.UCcreateMenuitem();
			}, 1000);
		}
		window.addEventListener("unload", this, false);
	},
	uninit: function() {
		const dis = [];
		for (let x of Object.keys(this.readCSS)) {
			if (!this.readCSS[x].enabled)
				dis.push(x);
		}
		this.prefs.setCharPref("disabled_list", encodeURIComponent(dis.join("|")));
		window.removeEventListener("unload", this, false);
	},
	destroy: function() {
		var i = document.getElementById("usercssloader-menu");
		if (i) i.parentNode.removeChild(i);
		var i = document.getElementById("usercssloader-rebuild-key");
		if (i) i.parentNode.removeChild(i);
		this.uninit();
	},
	handleEvent: function(event) {
		switch(event.type){
			case "unload": this.uninit(); break;
		}
	},
	rebuild: function() {
		let ext = /\.css$/i;
		let not = /\.uc\.css/i;
		let files = this.FOLDER.directoryEntries.QueryInterface(Ci.nsISimpleEnumerator);

		while (files.hasMoreElements()) {
			let file = files.getNext().QueryInterface(Ci.nsIFile);
			if (!ext.test(file.leafName) || not.test(file.leafName)) continue;
			let CSS = this.loadCSS(file);
			CSS.flag = true;
		}
		for (let leafName of Object.keys(this.readCSS)) {
			const CSS = this.readCSS[leafName];
			if (!CSS.flag) {
				CSS.enabled = false;
				delete this.readCSS[leafName];
			}
			delete CSS.flag;
			this.rebuildMenu(leafName);
		}
		if (this.initialized) {
			if (typeof(StatusPanel) !== "undefined")
				StatusPanel._label = "Rebuild しました";
			else
				XULBrowserWindow.statusTextField.label = "Rebuild しました";
		}
	},
	loadCSS: function(aFile) {
		var CSS = this.readCSS[aFile.leafName];
		if (!CSS) {
			CSS = this.readCSS[aFile.leafName] = new CSSEntry(aFile);
			if (this.disabled_list.indexOf(CSS.leafName) === -1) {
				CSS.enabled = true;
			}
		} else if (CSS.enabled) {
			CSS.enabled = true;
		}
		return CSS;
	},
	rebuildMenu: function(aLeafName) {
		var CSS = this.readCSS[aLeafName];
		var menuitem = document.getElementById("usercssloader-" + aLeafName);
		if (!CSS) {
			if (menuitem)
				menuitem.parentNode.removeChild(menuitem);
			return;
		}

		if (!menuitem) {
			menuitem = $C("menuitem", {
				label		: aLeafName,
				id			: "usercssloader-" + aLeafName,
				class		: "usercssloader-item " + (CSS.SHEET == this.AGENT_SHEET? "AGENT_SHEET" : "USER_SHEET"),
				type		: "checkbox",
				autocheck	: "false",
				oncommand	: "UCL.toggle('"+ aLeafName +"');",
				onclick		: "UCL.itemClick(event);"
			});
			document.getElementById("usercssloader-menupopup").appendChild(menuitem);
		}
		menuitem.setAttribute("checked", CSS.enabled);
	},
	toggle: function(aLeafName) {
		var CSS = this.readCSS[aLeafName];
		if (!CSS) return;
		CSS.enabled = !CSS.enabled;
		this.rebuildMenu(aLeafName);
	},
	itemClick: function(event) {
		if (event.button == 0) return;

		event.preventDefault();
		event.stopPropagation();
		let label = event.currentTarget.getAttribute("label");

		if (event.button == 1) {
			this.toggle(label);
		}
		else if (event.button == 2) {
			closeMenus(event.target);
			this.edit(this.getFileFromLeafName(label));
		}
	},
	getFileFromLeafName: function(aLeafName) {
		let f = this.FOLDER.clone();
		f.QueryInterface(Ci.nsIFile); // use appendRelativePath
		f.appendRelativePath(aLeafName);
		return f;
	},
	styleTest: function(aWindow) {
		aWindow || (aWindow = this.getFocusedWindow());
		new CSSTester(aWindow, function(tester){
			if (tester.saved)
				UCL.rebuild();
		});
	},
	searchStyle: function() {
		let word;
		try {
			word = gBrowser.currentURI.host;
		} catch {
			word = gBrowser.currentURI.spec;
		}
		let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
		openLinkIn("https://userstyles.org/styles/search/" + word, "tab", {
								private: false,
								inBackground: false,
								relatedToCurrent: true,
								triggeringPrincipal: systemPrincipal,
								});
	},
	openFolder: function() {
		this.FOLDER.launch();
	},
	editUserCSS: function(aLeafName) {
		let file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.appendRelativePath(aLeafName);
		this.edit(file);
	},
	edit: function(aFile) {
		var editor = Services.prefs.getCharPref("view_source.editor.path");
		if (!editor) return alert("エディタのパスが未設定です。\n view_source.editor.path を設定してください");
		try {
			var UI = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
			UI.charset = window.navigator.platform.toLowerCase().indexOf("win") >= 0? "Shift_JIS": "UTF-8";
			var path = UI.ConvertFromUnicode(aFile.path);
			var app = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
			app.initWithPath(editor);
			var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
			process.init(app);
			process.run(false, [path], 1);
		} catch (e) {}
	},
	create: function(aLeafName) {
		if (!aLeafName) aLeafName = prompt("ファイル名を入力してください", dateFormat(new Date(), "%Y_%m%d_%H%M%S"));
		if (aLeafName) aLeafName = aLeafName.replace(/\s+/g, " ").replace(/[\\/:*?\"<>|]/g, "");
		if (!aLeafName || !/\S/.test(aLeafName)) return;
		if (!/\.css$/.test(aLeafName)) aLeafName += ".css";
		let file = this.getFileFromLeafName(aLeafName);
		this.edit(file);
	},
	UCrebuild: function() {
		let re = /^file:.*\.uc\.css(?:\?\d+)?$/i;
		let query = "?" + new Date().getTime();
		Array.prototype.slice(document.styleSheets).forEach(function(css){
			if (!re.test(css.href)) return;
			if (css.ownerNode) {
				css.ownerNode.parentNode.removeChild(css.ownerNode);
			}
			let pi = document.createProcessingInstruction('xml-stylesheet','type="text/css" href="'+ css.href.replace(/\?.*/, '') + query +'"');
			document.insertBefore(pi, document.documentElement);
		});
		UCL.UCcreateMenuitem();
	},
	UCcreateMenuitem: function() {
		let sep = $("usercssloader-ucsepalator");
		let popup = sep.parentNode;
		if (sep.nextSibling) {
			let range = document.createRange();
			range.setStartAfter(sep);
			range.setEndAfter(popup.lastChild);
			range.deleteContents();
			range.detach();
		}

		let re = /^file:.*\.uc\.css(?:\?\d+)?$/i;
		Array.prototype.slice(document.styleSheets).forEach(function(css) {
			if (!re.test(css.href)) return;
			let fileURL = decodeURIComponent(css.href).split("?")[0];
			let aLeafName = fileURL.split("/").pop();
			let m = $C("menuitem", {
				label		: aLeafName,
				tooltiptext	: fileURL,
				id			: "usercssloader-" + aLeafName,
				type		: "checkbox",
				autocheck	: "false",
				checked		: "true",
				oncommand	: "this.setAttribute('checked', !(this.css.disabled = !this.css.disabled));",
				onclick		: "UCL.UCItemClick(event);"
			});
			m.css = css;
			popup.appendChild(m);
		});
	},
	UCItemClick: function(event) {
		if (event.button == 0) return;
		event.preventDefault();
		event.stopPropagation();

		if (event.button == 1) {
			event.target.doCommand();
		}
		else if (event.button == 2) {
			closeMenus(event.target);
			let fileURL = event.currentTarget.getAttribute("tooltiptext");
			let file = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler).getFileFromURLSpec(fileURL);
			this.edit(file);
		}
	},
};

function CSSEntry(aFile) {
	this.path = aFile.path;
	this.leafName = aFile.leafName;
	this.lastModifiedTime = 1;
	this.SHEET = /^xul-|\.as\.css$/i.test(this.leafName) ? 
		Ci.nsIStyleSheetService.AGENT_SHEET: 
		Ci.nsIStyleSheetService.USER_SHEET;
}
CSSEntry.prototype = {
	sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
	_enabled: false,
	get enabled() {
		return this._enabled;
	},
	set enabled(isEnable) {
		var aFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
		aFile.initWithPath(this.path);
	
		var isExists = aFile.exists(); // ファイルが存在したら true
		var lastModifiedTime = isExists ? aFile.lastModifiedTime : 0;
		var isForced = this.lastModifiedTime != lastModifiedTime; // ファイルに変更があれば true

		var fileURL;
			try {
					fileURL = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromFile(aFile);
			} catch {
					fileURL = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromActualFile(aFile);
			}

		var uri = Services.io.newURI(fileURL, null, null);

		if (this.sss.sheetRegistered(uri, this.SHEET)) {
			// すでにこのファイルが読み込まれている場合
			if (!isEnable || !isExists) {
				this.sss.unregisterSheet(uri, this.SHEET);
			}
			else if (isForced) {
				// 解除後に登録し直す
				this.sss.unregisterSheet(uri, this.SHEET);
				this.sss.loadAndRegisterSheet(uri, this.SHEET);
			}
		} else {
			// このファイルは読み込まれていない
			if (isEnable && isExists) {
				this.sss.loadAndRegisterSheet(uri, this.SHEET);
			}
		}
		if (this.lastModifiedTime !== 1 && isEnable && isForced) {
			log(this.leafName + " の更新を確認しました。");
		}
		this.lastModifiedTime = lastModifiedTime;
		return this._enabled = isEnable;
	},
};

function CSSTester(aWindow, aCallback) {
	this.win = aWindow || window;
	this.doc = this.win.document;
	this.callback = aCallback;
	this.init();
}
CSSTester.prototype = {
	sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
	preview_code: "",
	saved: false,
	init: function() {
		this.dialog = openDialog(
			"data:text/html;charset=utf8,"+encodeURIComponent('<!DOCTYPE HTML><html lang="ja"><head><title>CSSTester</title></head><body></body></html>'),
			"",
			"width=550,height=400,dialog=no");
		this.dialog.addEventListener("load", this, false);
	},
	destroy: function() {
		this.preview_end();
		this.dialog.removeEventListener("unload", this, false);
		this.previewButton.removeEventListener("click", this, false);
		this.saveButton.removeEventListener("click", this, false);
		this.closeButton.removeEventListener("click", this, false);
	},
	handleEvent: function(event) {
		switch(event.type) {
			case "click":
				if (event.button != 0) return;
				if (this.previewButton == event.currentTarget) {
					this.preview();
				}
				else if (this.saveButton == event.currentTarget) {
					this.save();
				}
				else if (this.closeButton == event.currentTarget) {
					this.dialog.close();
				}
				break;
			case "load":
				var doc = this.dialog.document;
				doc.body.innerHTML = '\
					<style type="text/css">\
						:not(input):not(select) { padding: 0px; margin: 0px; }\
						table { border-spacing: 0px; }\
						body, html, #main, #textarea { width: 100%; height: 100%; }\
						#textarea { font-family: monospace; }\
					</style>\
					<table id="main">\
						<tr height="100%">\
							<td colspan="4"><textarea id="textarea"></textarea></td>\
						</tr>\
						<tr height="40">\
							<td><input type="button" value="Preview" /></td>\
							<td><input type="button" value="Save" /></td>\
							<td width="80%"><span class="log"></span></td>\
							<td><input type="button" value="Close" /></td>\
						</tr>\
					</table>\
				';
				this.textbox = doc.querySelector("textarea");
				this.previewButton = doc.querySelector('input[value="Preview"]');
				this.saveButton = doc.querySelector('input[value="Save"]');
				this.closeButton = doc.querySelector('input[value="Close"]');
				this.logField = doc.querySelector('.log');

				var code = "@namespace url(" + this.doc.documentElement.namespaceURI + ");\n";
				code += this.win.location.protocol.indexOf("http") === 0?
					"@-moz-document domain(" + this.win.location.host + ") {\n\n\n\n}":
					"@-moz-document url(" + this.win.location.href + ") {\n\n\n\n}";
				this.textbox.value = code;
				this.dialog.addEventListener("unload", this, false);
				this.previewButton.addEventListener("click", this, false);
				this.saveButton.addEventListener("click", this, false);
				this.closeButton.addEventListener("click", this, false);

				this.textbox.focus();
				let p = this.textbox.value.length - 3;
				this.textbox.setSelectionRange(p, p);

				break;
			case "unload":
				this.destroy();
				this.callback(this);
				break;
		}
	},
	preview: function() {
		var code = this.textbox.value;
		if (!code || !/\:/.test(code))
			return;
		code = "data:text/css;charset=utf-8," + encodeURIComponent(this.textbox.value);
		if (code == this.preview_code)
			return;
		this.preview_end();
		var uri = Services.io.newURI(code, null, null);
		this.sss.loadAndRegisterSheet(uri, Ci.nsIStyleSheetService.AGENT_SHEET);
		this.preview_code = code;
		this.log("Preview");
	},
	preview_end: function() {
		if (this.preview_code) {
			let uri = Services.io.newURI(this.preview_code, null, null);
			this.sss.unregisterSheet(uri, Ci.nsIStyleSheetService.AGENT_SHEET);
			this.preview_code = "";
		}
	},
	save: function() {
		var data = this.textbox.value;
		if (!data) return;

		var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
		fp.init(window, "", Ci.nsIFilePicker.modeSave);
		fp.appendFilter("CSS Files","*.css");
		fp.defaultExtension = "css";
		if (window.UCL)
			fp.displayDirectory = UCL.FOLDER;
		var res = fp.show();
		if (res != fp.returnOK && res != fp.returnReplace) return;

		var suConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
		suConverter.charset = "UTF-8";
		data = suConverter.ConvertFromUnicode(data);
		var foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
		foStream.init(fp.file, 0x02 | 0x08 | 0x20, 0664, 0);
		foStream.write(data, data.length);
		foStream.close();
		this.saved = true;
	},
	log: function() {
		this.logField.textContent = dateFormat(new Date(), "%H:%M:%S") + ": " + $A(arguments);
	}
};

UCL.init();

function $(id) { return document.getElementById(id); }
function $A(arr) { return Array.prototype.slice(arr); }
function $C(name, attr) {
	const el = document.createElementNS(XULNS, name);
	if (attr) Object.keys(attr).forEach(function(n) { el.setAttribute(n, attr[n]) });
	return el;
}
function dateFormat(date, format) {
	format = format.replace("%Y", ("000" + date.getFullYear()).substr(-4));
	format = format.replace("%m", ("0" + (date.getMonth()+1)).substr(-2));
	format = format.replace("%d", ("0" + date.getDay()).substr(-2));
	format = format.replace("%H", ("0" + date.getHours()).substr(-2));
	format = format.replace("%M", ("0" + date.getMinutes()).substr(-2));
	format = format.replace("%S", ("0" + date.getSeconds()).substr(-2));
	return format;
}

function log() { Application.console.log(Array.prototype.slice(arguments)); }

})();

Farby
Меню появилось, но стили не отключаются и не обновляются. Проверял на чистой 102 ESR.


UPD Скрипт заработал! Правда, запускается он каким-то извращённым способом: во-первых, ему нужен пустой (ну или запиканный звёздочками) userChrome.css. В принципе, он его и так игнорит, но ни в 78, ни в доквантумных версиях наличие записей там ему не мешало. А во-вторых, после редактирования userChrome.css обновить надо через кнопку Reload user{Chrome, Content}.css. Тогда он видит изменения и потом уже начинает нормально работать.

Спасибо, что починили, очень уж удобный скрипт, приходилось держать вместо него три кнопки, которые не охватывали полностью его функций:)

Dumby посмотрите пожалуйста кнопку Save.В ней не работает функция "Сохранить значок веб-сайта"

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.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==";

var folderpath="C:\\Users\\Firepox\\Desktop";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить значок веб-сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Запомнить значок веб-сайта как base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Кодировать изображение(текст.файл) в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить всю страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Сохранить всю страницу или выбранное как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить выделенный текст как txt файл", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { separator: ''},
   { label: "Запомнить изображение как base64, в контекстном меню", value: "Save.WebScreenShotOnImage"},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function 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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как PDF файл через сервис 'pdfmyurl.com' .............
function savePageToPDF() {
      var loc = gBrowser.currentURI.spec;
   var vert = "http://pdfmyurl.com?url=" + loc;
  
   gBrowser. loadURI(vert, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
   });
};

// Сохранить иконку текущего сайта с диалогом сохранения .............
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};

//Добавыть в контекстное меню страницы пункт "Запомнить изображение как base64"..........................................................................................
(popup => addEventListener("popupshowing", {
    handleEvent(e) {
        if (this.shouldHide) return;

        var menuitem = document.createXULElement("menuitem");
        menuitem.id = "content-baseItem";
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("oncommand", "copyImageAsBase64()");
        menuitem.setAttribute("label", "Запомнить изображение как base64");
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
        popup.append(menuitem);
        addDestructor(() => menuitem.remove());

        menuitem.copyImageAsBase64 = () => {
            var {osPid} = gContextMenu.actor.manager.browsingContext.currentWindowGlobal;
            if (osPid == -1) osPid = Services.appinfo.processID;
            for(var ind = 0, len = Services.ppmm.childCount; ind < len; ind++) {
                var pmm = Services.ppmm.getChildAt(ind);
                if (pmm.osPid == osPid) break;
            }
            pmm.loadProcessScript("data:;charset=utf-8," + encodeURIComponent(this.code()), false);
        }
        this.handleEvent = () => menuitem.hidden = this.shouldHide;
    },
    get shouldHide() {
        return !(gContextMenu.onImage && Services.prefs.getBoolPref("Save.WebScreenShotOnImage", false));
    },
    code: () => `(targetIdentifier => {

        var image = ChromeUtils.import("resource://gre/modules/ContentDOMReference.jsm")
            .ContentDOMReference.resolve(targetIdentifier);

        var canvas = image.ownerDocument.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();

        Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)
            .copyStringToClipboard(base64, Ci.nsIClipboard.kGlobalClipboard);

        Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
            .showAlertNotification(base64, "${self.label}", "Запомнил изображение как base64");
    })(${
        JSON.stringify(gContextMenu.targetIdentifier)
    })`
}, false, popup || 1))(document.getElementById("contentAreaContextMenu"));


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {
	var splice = saveURL.length == 10;
	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);
		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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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
                ];
                var {length} = wbp.saveURI;
                length >= 9 && splice(args);
                length == 10 && args.splice(3, 0, null);
                wbp.saveURI(...args);
				
		setTimeout(async lp => {
			var d = await Downloads.createDownload({
				source: "about:blank", target: fp.file
			});
			(await lp).add(d);
			d.refresh(d.succeeded = true);
		}, 777, Downloads.getList(Downloads.ALL));				
            });
        }
        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);
    }
    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));
        }
    }`
});

egorsemenov06 пишет

посмотрите пожалуйста кнопку Save.В ней не работает функция "Сохранить значок веб-сайта"

Опять изменили аргументы в internalSave()

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

Выделить код

Код:

/*
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
*/
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 16
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: internalSave.length == 15
		? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
		: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

Dumby пишет
egorsemenov06 пишет

посмотрите пожалуйста кнопку Save.В ней не работает функция "Сохранить значок веб-сайта"

Опять изменили аргументы в internalSave()

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

Выделить код

Код:

/*
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
*/
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 16
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: internalSave.length == 15
		? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
		: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

Сасибо Большое!!!

Заметил, что если включить стили в настройках расширения - перестают работать клавиши вызова боковой панели (ctrl+B, ctrl+H). Это можно как-то починить? (на всякий случай выложил папку https://disk.yandex.ru/d/SCyAqLNnyxbTRw)

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


upd: виновник этого безобразия - auto_hide_sidebar.css

Dumby посмотрите пожалуйста кнопку Save.В ней не работает функция "Сохранить выделеный текст как txt фаил"

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.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==";

var folderpath="C:\\Users\\Firepox\\Desktop";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить значок веб-сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Запомнить значок веб-сайта как base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Кодировать изображение(текст.файл) в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить всю страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Сохранить всю страницу или выбранное как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить выделенный текст как txt файл", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { separator: ''},
   { label: "Запомнить изображение как base64, в контекстном меню", value: "Save.WebScreenShotOnImage"},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function 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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как PDF файл через сервис 'pdfmyurl.com' .............
function savePageToPDF() {
      var loc = gBrowser.currentURI.spec;
   var vert = "http://pdfmyurl.com?url=" + loc;
  
   gBrowser. loadURI(vert, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
   });
};

// Сохранить иконку текущего сайта с диалогом сохранения .............
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 16
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: internalSave.length == 15
		? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
		: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};

//Добавыть в контекстное меню страницы пункт "Запомнить изображение как base64"..........................................................................................
(popup => addEventListener("popupshowing", {
    handleEvent(e) {
        if (this.shouldHide) return;

        var menuitem = document.createXULElement("menuitem");
        menuitem.id = "content-baseItem";
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("oncommand", "copyImageAsBase64()");
        menuitem.setAttribute("label", "Запомнить изображение как base64");
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
        popup.append(menuitem);
        addDestructor(() => menuitem.remove());

        menuitem.copyImageAsBase64 = () => {
            var {osPid} = gContextMenu.actor.manager.browsingContext.currentWindowGlobal;
            if (osPid == -1) osPid = Services.appinfo.processID;
            for(var ind = 0, len = Services.ppmm.childCount; ind < len; ind++) {
                var pmm = Services.ppmm.getChildAt(ind);
                if (pmm.osPid == osPid) break;
            }
            pmm.loadProcessScript("data:;charset=utf-8," + encodeURIComponent(this.code()), false);
        }
        this.handleEvent = () => menuitem.hidden = this.shouldHide;
    },
    get shouldHide() {
        return !(gContextMenu.onImage && Services.prefs.getBoolPref("Save.WebScreenShotOnImage", false));
    },
    code: () => `(targetIdentifier => {

        var image = ChromeUtils.import("resource://gre/modules/ContentDOMReference.jsm")
            .ContentDOMReference.resolve(targetIdentifier);

        var canvas = image.ownerDocument.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();

        Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)
            .copyStringToClipboard(base64, Ci.nsIClipboard.kGlobalClipboard);

        Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
            .showAlertNotification(base64, "${self.label}", "Запомнил изображение как base64");
    })(${
        JSON.stringify(gContextMenu.targetIdentifier)
    })`
}, false, popup || 1))(document.getElementById("contentAreaContextMenu"));


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {
	var splice = saveURL.length == 10;
	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);
		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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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
                ];
                var {length} = wbp.saveURI;
                length >= 9 && splice(args);
                length == 10 && args.splice(3, 0, null);
                wbp.saveURI(...args);
				
		setTimeout(async lp => {
			var d = await Downloads.createDownload({
				source: "about:blank", target: fp.file
			});
			(await lp).add(d);
			d.refresh(d.succeeded = true);
		}, 777, Downloads.getList(Downloads.ALL));				
            });
        }
        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);
    }
    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));
        }
    }`
});

[firefox] 103.0.1

egorsemenov06 пишет

не работает функция "Сохранить выделеный текст как txt фаил"

А, saveURL() тоже

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

Выделить код

Код:

…
	//var splice = saveURL.length == 10;
	var {length} = saveURL, splice = length > 9, l11 = length == 11;

…

		//splice && args.splice(5, 0, null);
		splice && args.splice(5, 0, null) && l11 && args.splice(1, 0, null);

Dumby - получается, что теперь нужны два скрипта?
А может лучше проверять на версию FF, как infocatcher делает?
Тогда бы один скрипт работал на прежнем и новом Firefox…

Dobrov пишет

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

О чём речь, позволь поинтересоваться?

о проверке версий:
например AppConstants.MOZ_APP_VERSION больше 102 -> выполняем один код, меньше -> другой прежний.


Это наверное поможет, если Опять изменили аргументы в internalSave()

Dobrov
То есть, речь об этой правке.
Тогда скажи на какой версии Firefox эта правка
сломала обратную совместимость, попробую посмотреть.

Dumby пишет

Тогда скажи на какой версии Firefox эта правка
сломала обратную совместимость, попробую посмотреть.

Не знаю, если новая правка работает на всех Firefox, начиная с 90, тогда ладно.


Я откатился на FF97, когда страницы стали непонятно тормозить и долго загружаться на FF102.

Dumby пишет

А, saveURL() тоже

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

Dumby посмотрите пожалуйста кнопку toggleRestartlessAddons в неи не вкл. и не откл. дополнения при клики ЛКМ выдает такую ошибку
Uncaught ReferenceError: addDestructor is not defined
    updateAddonDisabledState chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:268
    setNewDisabledRaw chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:259
    setNewDisabled chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:201
    handleEvent chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:138
    oncommand chrome://browser/content/browser.xhtml:1

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

Выделить код

Код:

// 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: 0,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

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

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

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

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

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

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

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

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

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

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
function checkForAddonsUpdates() {
//== 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;

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

var ADDONS_URL = "about:addons";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		tab.collapsed = false;

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

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

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

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

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

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
		if(!isContextOpened())
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& 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;
				return true;
			}
			return false;
		}
	);
}
function isContextOpened() {
	return inBtn(document.popupNode);
}
function inBtn(node) {
	for(; node; node = node.parentNode)
		if(node == self)
			return true;
	return false;
}

[firefox] 103.0.1

egorsemenov06 пишет

addDestructor is not defined

Это значило бы, что надо загрузчик смотреть, если бы дело было в этом.


Но дело в другом (см. также).
Пока, можно добавить в код одну строку про "lazy".

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

Выделить код

Код:

…
	let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	if("lazy" in g) g = g.lazy;

Dumby пишет
egorsemenov06 пишет

addDestructor is not defined

Это значило бы, что надо загрузчик смотреть, если бы дело было в этом.


Но дело в другом (см. также).
Пока, можно добавить в код одну строку про "lazy".

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

Выделить код

Код:

…
	let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	if("lazy" in g) g = g.lazy;

Загрузчик это вот его

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

Выделить код

Код:

try {CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js"
		));
	},
	onCreated(btn) {
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC");
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
});} catch(ex) {Cu.reportError(ex);}

Dobrov, уважаемый, можно вопрос по вашей кнопке Quick toggle for about:config preferences (Dobrov mod)?

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

Выделить код

Код:

window.SetDownloadDir = function (path, path_old) {
	path = window.convertFromUnicode("UTF-8", path); path_old = window.convertFromUnicode("UTF-8", path_old) || '';
	if ( path != path_old ) {
		cbu.setPrefs("browser.download.folderList", 2);
		cbu.setPrefs("browser.download.useDownloadDir", true);
		cbu.setPrefs("browser.download.dir", path);
	} return path;
};

window.PathToDownloadDir = function (change) { // null возврат папки загрузки true тоже + открыть папку загрузки 2 изменить папку загрузки - обзор. возврат папки загрузки раньше диалога выбора
// 	try { var path = Services.prefs.getComplexValue("browser.download.dir", Ci.nsISupportsString).data }
	try { var path = Services.downloads.userDownloadsDirectory.path }
	catch(e) { path = Services.downloads.defaultDownloadsDirectory.path };
	if ( change == true ) {
		var file = Services.dirsvc.get('ProfD', Ci.nsIFile);
		file.initWithPath(path);
		file.launch();
	}
	if (typeof change == 'string') path = window.SetDownloadDir(change, path); // изменить путь загрузок
	if (change == 2) {
		var fp = window.makeFilePicker();
		fp.init(window, "Выберите папку для загрузок!", 2);
		fp.open(re=> { if ( re == fp.returnOK ) return window.SetDownloadDir(fp.file.path, path) });
	} return path;
};

window.PathToDownloadFolder = function (change) { // Получить\Открыть (change=false)\Изменить путь к папке загрузки (change=true или текст)
	try { var path = Services.downloads.userDownloadsDirectory.path }
	catch(e) { path = Services.downloads.defaultDownloadsDirectory.path };
	if ( change == false ) {
		var file = Services.dirsvc.get('ProfD', Ci.nsIFile);
		file.initWithPath(path);
		file.launch();
	} if ( !change ) return path; // arg == undefined || arg == false
	var fp = window.makeFilePicker();
	fp.init(window, "Выберите папку для загрузок!", 2);
	fp.open(re=> {
		if ( re != fp.returnOK ) return;
		cbu.setPrefs("browser.download.folderList", 2);
		cbu.setPrefs("browser.download.useDownloadDir", true);
		cbu.setPrefs("browser.download.dir", window.convertFromUnicode("UTF-8", fp.file.path));
	})
};

window.Title = function (type) { // заголовок (без обрезки, если type не указан), домен (type <0)
	var title = (content.document.title || gBrowser.mCurrentTab.label);
	var host = (/about:/.test(gURLBar.value)) ? // ReaderView или страница about:…
		decodeURIComponent(gURLBar.value).replace(/^.*url=/,'').replace(/^https?:\/\//,'').replace(/\/.*/,'') :
		gBrowser.currentURI.host;
	if (/^file:\/\//.test(gBrowser.currentURI.spec)) host = ''; // открыт локальный файл
	if ( !type ) return title; // заголовок
	if ( type > 0 ) return title.replace(/[:\\\/<>?*|"]+/g,' ').replace(/\s+/g,' ').replace(/  /g,' ').substr(0, type).trim(); // ограничить длину имени
	if ( type < 0 ) return host.replace(/^www\./,'').replace(/^ru\./,'').replace(/^m\./,'').replace(/^forum\./,'').replace(/^club\.dns/,'dns');
};


// Быстрое переключение параметров about:config от 24.07.2016 [FIX Dobrov]
// Изменить иконку при несоответствие любого параметра пользовательскому предпочтению (см. ниже)
// Иконка меняется только при изменении параметров через меню кнопки, либо после его открытия.

var s='CB.hasNotUserChoice', str = ' Быстрые настройки';
// str = 'profile: '+ OS.Constants.Path.profileDir;
str = str +'\n Правый клик: опции браузера';
str = str +'\n Держать: открыть about:config';
str = str +'\n\n Proxy(VPN): …порт: '+ cbu.getPrefs("network.proxy.http_port");
str = str +'\n '+ cbu.getPrefs("network.proxy.http");
this.tooltipText = str;
// str = str +'\n'+ OS.Constants.Path.libxul;
// str = str +'\n'+ OS.Constants.Path.homeDir;
// str = str +'\n'+ OS.Constants.Path.desktopDir;
// str = str +'\n'+ OS.Constants.Path.winAppDataDir; // undefined
// str = str +'\n'+ OS.Constants.Path.winLocalAppDataDir; // undefined
// str = str +'\n'+ OS.Constants.Path.winStartMenuProgsDir; // undefined
// str = str +'\n'+ OS.Constants.Path.winStartMenuProgsDir; // undefined
// str = str +'\n'+ OS.Constants.Path.macUserLibDir; // undefined
// str = str +'\n'+ OS.Constants.Path.macLocalApplicationsDir; // undefined

function toggleImage(){
	custombuttons.getPrefs(s) ? self.style.cssText = '' : self.style.cssText = 'filter: grayscale(100%)';
};
toggleImage();Services.prefs.addObserver(s,toggleImage,false);addDestructor(()=>Services.prefs.removeObserver(s,toggleImage));

var menuPopup = self.appendChild(document.createXULElement("menupopup"));
menuPopup.id='quick-aboutconfig-menupopup'; // для стиля в userChrome.css
var menuContext = self.appendChild(document.createXULElement("menupopup"));

// для действия Сохранить как… Web-страница будет в кодировке Win1251. на FF ниже 60 в GIT не видно выпадающего списка версий
var useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:78.0) Gecko/20100101 Firefox/78.0"; //,,,Firefox 78/MacOSX|||\
var useragent_name = "Firefox 78/MacOSX";

[
{nodeName:"menu", name:"Прокси (VPN)", pref:"network.proxy.type", refresh:"", image:"chrome://browser/skin/privatebrowsing-mask.png", userChoice:2, strValues:"0,,,отключен,,,0|||2,,,Автонастройка,,,2|||4,,,…для текущей сети,,,4|||5,,,Системный,,,5"},

{nodeName:"menu", name:"URL автонастройки", pref:"network.proxy.autoconfig_url", refresh:"", userChoice:"https://antizapret.prostovpn.org/proxy.pac", strValues:"https://antizapret.prostovpn.org/proxy.pac,,,АнтиЗапрет,,,1|||file:///etc/proxy.pac,,,.pac файл,,,2|||https://git.io/ac-anticensority-pac,,,ac-anticensority,,,3|||127.0.0.1,,,Отключен,,,0"},	//https://rebrand.ly/ac-anticensority
{nodeName:"checkbox", name:"режим 'Без прокси' при выходе", pref:"CB.Proxy.reset", userChoice:"false"},
{nodeName:"menuseparator"},
{nodeName:"menu", name:"Загружать шрифты Web", pref:"browser.display.use_document_fonts", strValues:"0,,,Выкл,,,|||1,,,Вкл,,,"},
{nodeName:"checkbox", name:"Выполнять скрипты Java", pref:"javascript.enabled", key:'j', userChoice:"true"},
{nodeName:"checkbox", name:"Сообщить о загрузке страницы", pref:"dom.enable_performance", userChoice:"false"},
{nodeName:"menuseparator"},
{nodeName:"checkbox", name:"автопроигрывание мультимедиа", pref:"media.autoplay.enabled", key:'m', userChoice:"true"},
{nodeName:"menuseparator"},
{nodeName:"menu", name:"User Agent", pref:"general.useragent.override", key:'u', refresh:"", userChoice: useragent, strValues: "\
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:100.0.1) Gecko/20100101 Firefox/101.0.1.0,,,Firefox 101.0.1/MacOSX|||\
Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727),,,MSIE 6.0/Windows|||\
Mozilla/5.0 (Linux; Android 7.0; PLUS Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36,,,Chrome61/Android7|||\
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30,,,Safari 6/MacOSX|||\
Mozilla/5.0 (Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36,,,Samsung Galaxy S6|||\
Mozilla/5.0 (PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko),,,Playstation 4|||\
Xbox (Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586,,,Xbox One (mobile)|||\
Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586,,,Microsoft Lumia 950|||\
Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350),,,Windows Phone|||\
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html),,,GoogleBot|||\
,,,Пустое значение"},
// Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots),,,YandexBot|||\
// Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm),,,BingBot|||\
// Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp),,,YahooBot|||\
// Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html),,,BaiduspiderBot|||\
// ia_archiver (+http://www.alexa.com/site/help/webmasters; crawler@alexa.com),,,AlexaCrawlerBot|||\
// DuckDuck bot/1.0; (+http://duckduckgo.com/duckduckbot.html),,,DuckDuckBot|||\
].forEach( (m)=> {
	CreateMenu(m, menuPopup); // какое меню создавать
});


[
{nodeName:"menu", name:"Куки", pref:"network.cookie.cookieBehavior", refresh:"", userChoice:"0", strValues:"0,,,Разрешить все сайты,,,|||2,,,Запретить все сайты,,,|||3,,,Сторонние посещённые сайты,,,|||1,,,Не принимать со сторонних сайтов,,,"},
{nodeName:"menu", name:"Загружать графику", pref:"permissions.default.image", refresh:"", userChoice:1, strValues:"1,,,Да|||3,,,Site only|||2,,,Нет"},
// 1,2,3; 1 разрешить изображения, 2 отключить; 3 изображения с основного веб-сайта, блокировать со сторонних (third-party) серверов
{nodeName:"menu", name:"Анимация изображений", pref:"image.animation_mode", key:'i', refresh:"", userChoice:"normal", strValues:"normal,,,вкл.,,,|||none,,,выкл.,,,"},
{nodeName:"checkbox", name:"OnLine видео  'dom.workers'", pref:"dom.workers.enabled", key:'w', userChoice:"true"},
{nodeName:"menuseparator"},
{nodeName:"checkbox", name:"Откл. дискового кэша", pref:"browser.cache.disk.enable", userChoice:"false"},
{nodeName:"checkbox", name:"Откл. кэша в оперативной памяти", pref:"browser.cache.memory.enable", userChoice:"false"},
{nodeName:"checkbox", name:"Локальное хранилище indexedDB", pref:"dom.indexedDB.enabled", key:'d', userChoice:"true"},
{nodeName:"checkbox", name:"Откл. локального хранилища", pref:"dom.storage.enabled", key:'s'},
{nodeName:"menuseparator"},
// {nodeName:"menu", name:"Back-story-cash [Tessssttt]", pref:"browser.sessionhistory.max_total_viewers", strValues:"0,,,0"},
// {nodeName:"menuseparator"},
{nodeName:"menu", name:"useragent.locale", pref:"general.useragent.locale", key:'l', restart:"", strValues:"en-US,,,English,,,e|||ru-RU,,,Рус (ru-RU),,,r|||ru,,,русский (ru),,,"},
{nodeName:"menu", name:"language", pref:"intl.accept_languages", strValues:"en-US, en;q=0.5,,,en-US, en;q=0.5,,,e|||en-US, en, ru-RU, ru,,,en-US, en, ru-RU, ru,,,r"},
{nodeName:"menu", name:"document_color_use", pref:"browser.display.document_color_use", key:'c', userChoice:"0", strValues:"0,,,Auto,,,0|||1,,,Always,,,1|||2,,,Never,,,2"},
{nodeName:"menuseparator"},
{nodeName:"checkbox", name:"В качестве реферера корень сайта", pref:"network.http.referer.spoofSource", userChoice:"true"},
{nodeName:"menu", name:"Вкл/Выкл Referer", pref:"network.http.sendRefererHeader", userChoice:"2", strValues:"0,,,0|||2,,,2"},
{nodeName:"menu", name:"referer.trimmingPolicy", pref:"network.http.referer.trimmingPolicy", strValues:"0,,,0|||2,,,2"},
{nodeName:"menuseparator"},
{nodeName:"checkbox", pref:"media.mediasource.enabled", userChoice:"true"},
{nodeName:"checkbox", pref:"media.peerconnection.enabled"},		//WebRTC false=off!
{nodeName:"checkbox", name:"Многопоточность вкладок (CPU)", pref:"browser.tabs.remote.autostart", restart:"", userChoice:"false"},	//about:support=Multiprocess Windows|forum.ru-board.com/topic.cgi?forum=5&topic=49695&start=0&limit=1&m=9#1
{nodeName:"menuseparator"},
// {nodeName:"checkbox", name:"On/Off useragentS", pref:"general.useragent.site_specific_overrides"},
// useragent +",,,"+ useragent_name +"|||\
].forEach( (m)=> {
	CreateMenu(m, menuContext); // контекстное меню
});


function CreateMenu(m, menuNew) {

	if (m.nodeName==="checkbox" || m.nodeName==="radio")
		var mItem = document.createXULElement('menuitem') // для checkbox, radio
	else
		var mItem = document.createXULElement(m.nodeName);

	var addCommand=''; // создать элементы меню

	if ("refresh" in m)	addCommand = ' BrowserReload();';
	if ("restart" in m)	addCommand = ' if (custombuttons.confirmBox(null, "Restart?", "Yes", "Cancel")) Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);';


	if ("image" in m) mItem.setAttribute('image', m.image);
	if ("name" in m) mItem.setAttribute('name', m.name);
	if ("pref" in m) {
		mItem.setAttribute('closemenu', 'none');
		mItem.setAttribute('oncontextmenu', 'event.preventDefault();custombuttons.clearPrefs("'+m.pref+'");'+ addCommand);
	}
	if ("key" in m) mItem.setAttribute('accesskey', m.key);

// тип radio не добавлен !!!
	if (m.nodeName==="checkbox") {

		mItem.setAttribute('type', 'checkbox');
		mItem.setAttribute('oncommand','custombuttons.setPrefs("'+m.pref+'",!custombuttons.getPrefs("'+m.pref+'"));if (event.shiftKey && event.keyCode==event.DOM_VK_RETURN){event.preventDefault();custombuttons.clearPrefs("'+m.pref+'")};'+addCommand);
	}
	// if (m.nodeName==="radio") {
	// 	if ("name" in m) mItem.setAttribute('label', m.name);
	// 	mItem.setAttribute('type', 'radio');
	// 	mItem.setAttribute('oncommand','custombuttons.setPrefs("'+m.pref+'",!custombuttons.getPrefs("'+m.pref+'"));'+addCommand);
	// }

	if (m.nodeName === "menu") {
		mItem.setAttribute('class', 'menu-iconic');
		var subMenu = mItem.appendChild(document.createXULElement("menupopup"));
	 for (var value of m.strValues.split('|||')) {
		var submItem = document.createXULElement("menuitem");
		var smVal = value.split(',,,')[0];
		var smValConv = convertFromUnicode("UTF-8", smVal);
		var smName = value.split(',,,')[1];
		var key = value.split(',,,')[2];
		key && submItem.setAttribute('accesskey', key);
		submItem.setAttribute('type', 'radio');
		submItem.setAttribute('label', smName);
		submItem.setAttribute('tooltiptext', smVal);
		submItem.setAttribute('closemenu', 'none');
		submItem.setAttribute('oncommand', 'try{custombuttons.setPrefs("'+ m.pref +'","'+ smValConv.replace(/\\/g,'\\\\')+'")}catch(e){Services.prefs.setIntPref("'+ m.pref +'","'+ smValConv +'")};'+ addCommand);
		subMenu.appendChild(submItem);
	 }
	}

	menuNew.appendChild(mItem);


// Листенеры отслеживания переключения параметров и устанавка соответствующих названий и чекбоксов для пунктов меню при открытии меню и кликах
 for (var type of ['command', 'popupshowing', 'contextmenu']) {
	addEventListener(type, (e)=> {
	 setTimeout(()=> {
		if ("pref" in m) {
			var val, def;
			def = Services.prefs.prefHasUserValue(m.pref);

			try {
				val = Services.prefs.getComplexValue(m.pref, Ci.nsISupportsString).data;
			} catch(e) {
				if (Services.prefs.getPrefType(m.pref) == 64) val = custombuttons.getPrefs(m.pref).toString();
				else val = custombuttons.getPrefs(m.pref);
			}
			def ? mItem.style.setProperty('font-weight', 'bold', 'important') : mItem.style.removeProperty('font-weight');
		}

		if (m.nodeName === 'checkbox') {
			mItem.setAttribute('checked', val);
			mItem.label = ( mItem.hasAttribute('name') ? mItem.getAttribute('name') : m.pref );

			if ("userChoice" in m) { // отображение значения +' - "'+val+'"';
				try {var usrChc = (val.toString() === m.userChoice)} catch(e) {usrChc = false};
				mItem.setAttribute('user-choice', usrChc);
				usrChc ? mItem.style.removeProperty('color') : mItem.style.setProperty('color', 'orangered', 'important');
			}
		}

		if (subMenu) {
			for (var smitem of subMenu.getElementsByTagName('menuitem')) {
				var smval = smitem.getAttribute('tooltiptext');
				smitem.setAttribute('checked', (val === smval) ? true : false);
			}
		}

		if (m.nodeName === "menu") {
			var vname;

			try {
				vname = subMenu.getElementsByAttribute('checked', 'true')[0].getAttribute('label');
			} catch(e) {
				if (!Services.prefs.prefHasUserValue(m.pref)) vname = 'Default';
				else vname = 'Other';
			}

			mItem.setAttribute('label', ( mItem.hasAttribute('name') ? mItem.getAttribute('name') : m.pref ) +' - "'+ vname +'"');
			mItem.setAttribute('tooltiptext', val || 'This preferences has null value or does not exist.');

			if ("userChoice" in m) {
				var smUsrChc = (val === m.userChoice.toString());

				mItem.setAttribute('user-choice', smUsrChc);
				smUsrChc ? mItem.style.removeProperty('color') : mItem.style.setProperty('color', 'orangered', 'important');
			}
		}

		if ("userChoice" in m) {
			var hasNotUserChoice = menuNew.getElementsByAttribute('user-choice', 'false')[0];

			custombuttons.setPrefs(s, hasNotUserChoice ? true : false);
		}
	 }, 0)
	}, false, menuNew)
 }

}; // End CreateMenu



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

var switchOffProxy = { // Переключать на режим 'Без прокси' при закрытии браузера если это разрешено в 'about:config'
	observe: function(subject, topic, data) {
	if ( data == "shutdown" && cbu.getPrefs("CB.Proxy.reset") ) cbu.setPrefs("network.proxy.type",0);
	}
};
Services.obs.addObserver(switchOffProxy, "quit-application", false);

// nodeName: checkbox - для логических(boolean) параметров, menu - для целых(integer) и строковых(string). menuseparator - для разделителя.
// pref - параметр about:config.
// Параметры имеющие значения отличные от дефолтных - выделены жирным стилем текста.
// restart (задавать с пустым значением. т.е., restart: "") - перезапуск браузера (с подтверждением в диалоговом окне) после изменения параметра.
// key - задает accesskey - клавиши для быстрой навигации по меню.
// userChoice - задает предпочитаемое значение и если текущее значение с ним не совпадает, пункт меню/название меню помечаются красным цветом.
// Также можно установить предупреждающую иконку для таких пунктов. См. стиль в посте ккнопки. А также меняется иконка самой кнопки (см. выше).
// strValues - значения и отображаемое в меню название значения. Задавать для целых(integer) и строковых(string) параметров.
// Задается в виде: значение,,,название,,,accesskey|||значение2,,,название2,,,accesskey2|||значение3,,,название3 и т.д. (accesskey - задается опционально)
// Полное значение отображается в подсказках, при наведении на название подменю/пункт подменю.
// Для логических(boolean) - отображается сразу после самого параметра (значение true - также ставит галочку для него).
// ЛКМ по пунктам меню - переключает значения для логических(boolean) параметров,
// любая кнопка по пунктам в субменю - задает это значение для целых(integer) и строковых(string) параметров.
// Клавиатура: Enter - переключение параметра.
// Alt + M - открыть меню кнопки. (Сочетание можно сменить на свое. См. в конце кода)
// ПКМ по пунктам меню и названию субменю - сбрасывает значение параметра в дефолтное (сейчас перехватывается кликом по кнопке).
// Спецклавиша вызова контекстного меню / Shift+Enter - сброс в дефолтное значение (убрал это действие).

// Настройка функций кликов мыши для кнопки, учитывая долгое нажатие
	var longPress = false; // долгое нажатие
function handleEvent(event) {
  switch(event.type)
  {	case"mouseover": // можно обновлять tooltip кнопки
	  0
		// this.setAttribute('Popup.state', menuPopup.state); // состояние меню: открыто/скрыто
		// this.setAttribute('menuContext.state', menuContext.state); // состояние меню: открыто/скрыто
		break;
	case"mousedown":
		self.timer = setTimeout(()=> { // удержание
			if ( event.target.localName == "menuitem" ) return;
			longPress = true; // блокировка обычных кликов
			if ( event.button == 0 ) { // ЛКм Long
				window.switch_tab_url('about:config');
			} else if ( event.button == 2 ) { // ПКм Long
// 				setPathToDownloadFolder();
				window.switch_tab_url('about:preferences#content');
			} else if ( event.button == 1 ) { // СКм Long
				0
			}
		}, 500 );
		break;
	case"mouseup": // отжатие кнопки
		clearTimeout(self.timer); // сброс таймера долгих нажатий
		if ( longPress ) { longPress = false } // выполнялась команда долгих нажатий, сброс флага и выход
		else if ( event.button == 0 ) { // ЛКм
			if ( /open|showing/.test(this.getAttribute('Popup.state')) || /open|showing/.test(this.getAttribute('menuContext.state')) ) {// показать/скрыть меню, замена функции this._handleClick
				menuPopup.hidePopup();
				menuContext.hidePopup()
			} else
				menuPopup.openPopup(this,'after_start');
			if ( !/open|showing/.test(this.getAttribute('menuContext.state')) ) // показать/скрыть меню, замена функции this._handleClick
				this.setAttribute('Popup.state', menuPopup.state); // видимость Popup. выполнить также на "mouseover" ( или один раз глобально в конце handleEvent)
			 	this.setAttribute('menuContext.state', menuContext.state); // видимость Popup. выполнить также на "mouseover" ( или один раз глобально в конце handleEvent)
		} else if ( event.button == 2 && !event.altKey && !event.metaKey) { // ПКм
			if ( event.ctrlKey && event.shiftKey ) { // +Ctrl +Shift
				var host = gURLBar.inputField;
				var str = 'host ' + host + "\n" +
				decodeURIComponent(gBrowser.currentURI.spec) + "\n" +
				'+1 '+ window.Title(33) + "\n" +
				'=0 '+ window.Title(0) + "\n" +
				'-1 '+ decodeURIComponent(window.Title(-1)) + "\n";
// 				URLBarInput.value + '/n';
// 				gURLBar.mInputField;
				// window.show_tooltip(0, '', str, 5000);
			}
			if ( event.ctrlKey) { // +Ctrl
// 				window.SavSnapshot();
				var info = custombuttons.cbService.readFromClipboard();
// 				info = document.getElementById('tabbrowser-tabs').clientWidth;
//				info = gBrowser.tabs.clientWidth;
				window.statusTextField(info);
			} else if ( event.shiftKey) { // +Shift
				window.PathToDownloadFolder("/home");
			} else {
// 				window.switch_tab_url('about:config');
			if (/open|showing/.test(this.getAttribute('menuContext.state'))) // показать/скрыть меню, замена функции this._handleClick
				menuContext.hidePopup()
			else
				menuContext.openPopup(this,'after_start');
		 	this.setAttribute('menuContext.state', menuContext.state); // видимость Popup. выполнить также на "mouseover" ( или один раз глобально в конце handleEvent)
			}
			event.preventDefault();
			event.stopPropagation();
		} else if ( event.button == 1 ) { // СКм
			custombuttons.editButton(this); // Редактировать кнопку…
		}
		break;
	case"DOMMouseScroll": // ролик мыши над кнопкой
		event.detail > 0 ? FullZoom.reduce() : FullZoom.enlarge();
		break;
	case"contextmenu": // ПКм+Alt или Meta откроет контекстное меню
		menuContext.hidePopup();
		if(!event.altKey && !event.metaKey) event.preventDefault();	// event.stopPropagation();
		// break;
  }
// команды при выполнении любых событий
};
// var this_event = ["mousedown", "mouseup", "mouseover", "contextmenu", "draggesture", "DOMMouseScroll"]
var this_event = ["mousedown", "mouseup", "mouseover", "draggesture", "DOMMouseScroll"]
	.forEach((type)=> this.addEventListener(type, handleEvent, false));

// Горячие клавиши: действия. коды: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value
addEventListener("keyup", (e)=>{
	if (e.altKey && !e.shiftKey && !e.ctrlKey && e.keyCode==77){ // сочетание клавиш Alt+M
		e.preventDefault();e.stopPropagation();
		menuPopup.showPopup(this,-1,-1,"popup","bottomleft","topleft");
	}
}, false,window);

// Блокировать контекстное меню при клике ПКМ, +Ctrl, +Shift
this.oncontextmenu = function(e) { if(e.button == 2 && !e.altKey && !e.metaKey) { e.preventDefault(); e.stopPropagation(); } };

// Листенер позволяющий сброс параметров с субменю по Shift + Enter // За код спасибо Dumby
// addEventListener("popupshown", {
// 	handleEvent: function(e) {
// 		this[e.type](e);
// 	},
// 	popupshown: function(e) {
// 		if (e.target != menuPopup) return;
// 		menuPopup.addEventListener("popuphidden", this, false);
// 		window.addEventListener("keydown", this, true);
// 	},
// 	popuphidden: function(e) {
// 		if (e.target != menuPopup) return;
// 		menuPopup.removeEventListener("popuphidden", this, false);
// 		window.removeEventListener("keydown", this, true);
// 	},
// 	popupshowing: function(e) {
// 		e.target.parentNode.removeEventListener("popupshowing", this, false);
// 		e.preventDefault();
// 	},
// 	get old() {
// 		delete this.old;
// 		this.e = new MouseEvent("contextmenu", {});
// 		return this.old = parseInt(Services.appinfo.platformVersion) < 25;
// 	},
// 	get prop() {
// 		delete this.prop;
// 		if ("key" in KeyboardEvent.prototype) this.prop = "key", this.val = "Enter";
// 		else this.prop = "keyCode", this.val = KeyboardEvent.DOM_VK_RETURN;
// 		return this.prop;
// 	},
// 	keydown: function(e) {
// 		if (!e.shiftKey || e.ctrlKey || e.altKey || e[this.prop] != this.val) return;
// 		var target = menuPopup.querySelector("menu[_moz-menuactive]:not([open])");
// 		if (!target) return;

// 		this.old ? target.addEventListener("popupshowing", this, false) : e.stopPropagation();
// 		target.dispatchEvent(this.e);
// 		menuPopup.dispatchEvent(this.e);
// 	}
// }, false, menuPopup);




// pref:"network.proxy.autoconfig_url", refresh:"", userChoice:"https://antizapret.prostovpn.org/proxy.pac", strValues:"https://antizapret.prostovpn.org/proxy.pac,,,АнтиЗапрет,,,1|||file:///etc/proxy.pac,,,.pac файл,,,2|||https://git.io/ac-anticensority-pac,,,ac-anticensority,,,3"},	//https://rebrand.ly/ac-anticensority

// {nodeName:"checkbox", pref:"xpinstall.signatures.required"},		//Check is compatibility
// {nodeName:"checkbox", pref:"browser.bookmarks.autoExportHTML"},		//BookmarksHtml [false=places.sqlite]
// {nodeName: "menu", pref: "CB.TEST", key: 't', userChoice: "C:\\Downloads\\TEST1", strValues: "C:\\Downloads\\TEST1,,,TEST1,,,1|||C:\\Downloads\\TEST2,,,TEST2,,,2"},

// var mPrefs = "network.proxy.autoconfig_url";


// }, 100);

// window.statusTextField(gBrowser.docShell.charset +' '+ m.value +'_'+ decodeURIComponent(window.hostname()));

// 			tmp = (self.image == imgFlashToPlayer || self.image == imgFlashMinimize ||  self.image == imgFlashMaximize);
// 			if(m.value.substring(0,9)=='videotopl')
// 				self.image = tmp ? imgFlashToPlayer : imgHTML5ToPlayer;



// 	Menu_n_TooltipTxts.forEach((m) => {
// 	// window.show_tooltip(0, '', m, 5000);

// 	var mItem = document.createXULElement("menuitem");
// 		if("radio" in m) {
// 			// 0
// 			// l = l + ( cbu.getPrefs(mPrefs) == m.value ) + '\n';
// 			mItem.setAttribute('checked', cbu.getPrefs(mPrefs) == m.value);
// 		}
// //		menuContext.appendChild(mItem);
// 	});


// mItem.getAttribute('name')
// mItem.setAttribute("type", "radio");

// mItem.setAttribute('label', ( mItem.hasAttribute('name') ? mItem.getAttribute('name') : m.pref ) +' - "'+ vname +'"');
// mItem.setAttribute('tooltiptext', val || 'This preferences has null value or does not exist.');

// {nodeName:"checkbox", name:"Загружать графику", radio:"", pref:"permissions.default.image", refresh:"", userChoice:1, strValues:"1,,,On"},
// {nodeName:"checkbox", name:"не Загружать графику", radio:"", pref:"permissions.default.image", refresh:"", strValues:"2,,,Off"},


Код привёл, потому что тут на форуме было много вариаций, а мне нравится именно эта. Обычно у меня всегда включен по умолчанию Автонастройка - Антизапрет, и до этого никакие сайты на это не ругались. Но сегодня (вчера ещё всё было в порядке) forum.ru-board.com выдал: "Прокси-сервер отказывается принимать соединения". Поскольку ничего другогоу меня подключено не было, я отключил антизапрет, и форум загрузился. Почему он стал реагировать на кнопку, и можно ли это как-то поправить? Захожу часто, постоянно включать-отключать не радует.

kazarin - Антизапрет переодически не работает на некоторых сайтах - флибуста и прочие.
Для Антизапрета нужно установить два пункта меню: 1) Прокси - Антизапрет 2) Режим прокси - Автонастройка.
С виду код вроде рабочий, но советую обновить кнопку. Приведённый код сильно устарел - актуальный от апреля 2022 (требуется 2 скрипта: ucf_QuickToggle.js + глобальный ucf_hookClicks.js).


в моём профиле (в шапке темы) переключение прокси делается проще: долгий клик по Замку или кнопке Quick toggle.

Dobrov
Спасибо за ответ!
Поторопился я писать, сегодня уже всё грузится - наверно, на стороне форума были косяки.
Код рабочий и работает отлично, пользуюсь им уже давно и он очень нравится мне в том виде, что есть)) Посмотрел новую версию, это вообще совсем другой скрипт, с другими функциями, он её не заменит.

Dumby а можно из этого дополнения https://forum.mozilla-russia.org/viewto … 20#p801220 сделать скрипт для UCF

egorsemenov06
Тяп-ляп — папка long_left_click
Загружать из неё в сандбокс скрипт startup.js
Например

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

Выделить код

Код:

…
    scriptsbackground: [ // В фоне [System Principal]
        …
        { path: "long_left_click/startup.js" },


Настройки — about:llc
скрытый текст

Выделить код

Код:

data:application/x-zip-compressed;base64,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

Dumby пишет

egorsemenov06
Тяп-ляп — папка long_left_click
Загружать из неё в сандбокс скрипт startup.js
Например

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

Dumby пишет

Тяп-ляп — папка long_left_click
Загружать из неё в сандбокс скрипт startup.js
Например
скрытый текст

Настройки — about:llc
скрытый текст

Интересный вариант, запихнуть расширение в UCF :beer:

Dumby в этот скрипт можно добавить стиль что бы иконкии были все на одном уровне

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

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.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==";

var folderpath="C:\\Users\\Firepox\\Desktop";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить значок веб-сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Запомнить значок веб-сайта как base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Кодировать изображение(текст.файл) в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить всю страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Сохранить всю страницу или выбранное как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить выделенный текст как txt файл", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { separator: ''},
   { label: "Запомнить изображение как base64, в контекстном меню", value: "Save.WebScreenShotOnImage"},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function 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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как PDF файл через сервис 'pdfmyurl.com' .............
function savePageToPDF() {
      var loc = gBrowser.currentURI.spec;
   var vert = "http://pdfmyurl.com?url=" + loc;
  
   gBrowser. loadURI(vert, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
   });
};

// Сохранить иконку текущего сайта с диалогом сохранения .............
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 16
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: internalSave.length == 15
		? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
		: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};

//Добавыть в контекстное меню страницы пункт "Запомнить изображение как base64"..........................................................................................
(popup => addEventListener("popupshowing", {
    handleEvent(e) {
        if (this.shouldHide) return;

        var menuitem = document.createXULElement("menuitem");
        menuitem.id = "content-baseItem";
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("oncommand", "copyImageAsBase64()");
        menuitem.setAttribute("label", "Запомнить изображение как base64");
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
        popup.append(menuitem);
        addDestructor(() => menuitem.remove());

        menuitem.copyImageAsBase64 = () => {
            var {osPid} = gContextMenu.actor.manager.browsingContext.currentWindowGlobal;
            if (osPid == -1) osPid = Services.appinfo.processID;
            for(var ind = 0, len = Services.ppmm.childCount; ind < len; ind++) {
                var pmm = Services.ppmm.getChildAt(ind);
                if (pmm.osPid == osPid) break;
            }
            pmm.loadProcessScript("data:;charset=utf-8," + encodeURIComponent(this.code()), false);
        }
        this.handleEvent = () => menuitem.hidden = this.shouldHide;
    },
    get shouldHide() {
        return !(gContextMenu.onImage && Services.prefs.getBoolPref("Save.WebScreenShotOnImage", false));
    },
    code: () => `(targetIdentifier => {

        var image = ChromeUtils.import("resource://gre/modules/ContentDOMReference.jsm")
            .ContentDOMReference.resolve(targetIdentifier);

        var canvas = image.ownerDocument.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();

        Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)
            .copyStringToClipboard(base64, Ci.nsIClipboard.kGlobalClipboard);

        Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
            .showAlertNotification(base64, "${self.label}", "Запомнил изображение как base64");
    })(${
        JSON.stringify(gContextMenu.targetIdentifier)
    })`
}, false, popup || 1))(document.getElementById("contentAreaContextMenu"));


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {
    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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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
                ];
                var {length} = wbp.saveURI;
                length >= 9 && splice(args);
                length == 10 && args.splice(3, 0, null);
                wbp.saveURI(...args);
				
		setTimeout(async lp => {
			var d = await Downloads.createDownload({
				source: "about:blank", target: fp.file
			});
			(await lp).add(d);
			d.refresh(d.succeeded = true);
		}, 777, Downloads.getList(Downloads.ALL));				
            });
        }
        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);
    }
    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));
        }
    }`
});

для менюшек использую такой стиль
скрытый текст

Выделить код

Код:

menupopup > menuitem,
menupopup > menu {
    padding-block: 2px !important;
    padding-inline-start: 12px !important;
}
menupopup {
    --menuitem-hover-background-color: #91C9F7 !important;
    --menu-background-color: #F2F2F2 !important;
    --menu-color: #000000 !important;
    --menuitem-disabled-hover-background-color: rgba(224, 224, 230, 0.6) !important;
    --menu-disabled-color: rgba(21, 20, 26, 0.5) !important;
    --menu-border-color: #919191 !important;
    --menu-icon-opacity: 0.7 !important;
}


@-moz-document url("chrome://browser/content/browser.xhtml"),
    url("chrome://browser/content/places/places.xhtml"),
    url("chrome://browser/content/places/historySidebar.xhtml"),
    url("chrome://browser/content/places/bookmarksSidebar.xhtml"),
    url("chrome://browser/content/syncedtabs/sidebar.xhtml") {
:root {
	--v-icons-text-padding-inline-start: 5px;
}
menu:not(.menu-iconic)::before, menuitem:not(.menuitem-iconic)::before {
    width: 16px;
    height: 16px;
    display: -moz-inline-box;
    margin-inline-start: 0;
    margin-inline-end: 8px;
    position: relative;
    background-color: transparent;
    background-position: center;
    background-size: 16px;
    background-repeat: no-repeat;
	
}	
menupopup menuitem:is([type="checkbox"],[type="radio"]):not([checked="true"]) > .menu-iconic-left > .menu-iconic-icon {
    -moz-context-properties: fill, fill-opacity, stroke !important;
    fill: currentColor;
    fill-opacity: var(--v-icons-fill-opacity);
}
}

и везде нормально а в этой кнопке нет. [firefox] 104.0 windows 10 ltsc. B windows 7 было хорошо

egorsemenov06 пишет

в этот скрипт можно добавить стиль что бы иконкии были все на одном уровне

В скрипт? Это принципиально?
Просто может лучше добавить в стиль для менюшек первой строкой:
#ID menuitem,
где ID — id кнопки Save.

Dumby пишет
egorsemenov06 пишет

в этот скрипт можно добавить стиль что бы иконкии были все на одном уровне

В скрипт? Это принципиально?
Просто может лучше добавить в стиль для менюшек первой строкой:
#ID menuitem,
где ID — id кнопки Save.

Cпасибо Вам Большое!!!!!теперь отлично

Уважаемые форумчане, опять я с вопросом.
Поместил кастомную кнопку в скрипт (один готовый код копипейстнул в другой). И вроде как работает, но по клику выскакивает вот такое.
P2TaM43t.gif
Увы, моих знаний не хватает понять, что надо править. Помогите, пожалуйста.
Вот код скрипта:

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

Выделить код

Код:

try {
CustomizableUI.createWidget({
        id: "FFTools",
        type: "custom",
        tooltiptext: [
            "FFTools"
        ].join("\n"),
	
				
        onBuild: function(document) {
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");

            toolbarbutton_0.id = this.id;
            toolbarbutton_0.tooltipText = this.tooltiptext;
            toolbarbutton_0.label = "FFTools";
            toolbarbutton_0.setAttribute("image", "data:image/png;base64,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");

            toolbarbutton_0.addEventListener("click", function(event) {
(n => {
	var inBackground = false;
	var data = [{
		lab: "about:config",
		url: "about:config",
		img: "chrome://browser/skin/developer.svg"
	},{
		lab: "about:config old",
		url: "chrome://global/content/config.xhtml",
		img: "chrome://browser/skin/developer.svg"
	},{
		lab: "about:support",
		url: "about:support",
		img: "chrome://global/skin/icons/info.svg"
	},{
		lab: "about:performance",
		url: "about:performance",
		img: "chrome://global/skin/icons/performance.svg"
	},{
		lab: "about:memory",
		url: "about:memory",
		img: "chrome://global/skin/icons/warning.svg"
	},{
		lab: "about:profiling",
		url: "about:profiling",
		img: "chrome://devtools/skin/images/profiler-stopwatch.svg"
	},,{
		lab: "about:debugging",
		url: "about:debugging#/runtime/this-firefox",
		img: "chrome://browser/skin/developer.svg"
	},
	null, {
		lab: "Библиотека",
		url: "chrome://browser/content/places/places.xhtml",
		img: "chrome://browser/skin/library.svg"
	}, {
		lab: "О сборочной конфигурации",
		url: "about:buildconfig",
		img: "chrome://devtools/skin/images/settings.svg"
	}];

	this.type = "menu";
	var popup = n("menupopup"), dummy = n("menuitem");
	popup.toggleAttribute("context");
	dummy.render = () => {
		dummy.remove();
		data.forEach((o, ind) => {
			if (!o) return popup.append(n("menuseparator"));
			var menuitem = n("menuitem");
			menuitem.setAttribute("label", o.lab || o.url);
			if (o.img)
				menuitem.className = "menuitem-iconic",
				menuitem.setAttribute("image", o.img);
			menuitem.ind = ind;
			popup.append(menuitem);
		});
		popup.setAttribute("oncommand", "tab(event.target.ind);");
		popup.tab = ind => {
			var {url, params} = data[ind];
			var tab = gBrowser.addTrustedTab(url, params);
			if (!(params?.inBackground || inBackground)) gBrowser.selectedTab = tab;
		}
	}
	popup.append(dummy);
	this.prepend(popup);

	//this.onmouseover = () => this.open = true;

	this.removeAttribute("tooltiptext");
	var tt = this.appendChild(n("box")).appendChild(n("tooltip"));
	tt.setAttribute("onpopupshowing", "return !(parentNode.parentNode.open = true);");
	this.setAttribute("tooltip", tt.id = _id + "-tooltip");

})(nn => document.createXULElement(nn));
                
                
            }, false);
            toolbarbutton_0.classList.add("toolbarbutton-1");
            toolbarbutton_0.classList.add("chromeclass-toolbar-additional");
            return toolbarbutton_0;
        }
    });
} catch(e) {}

kazarin пишет

toolbarbutton_0.addEventListener("click"

Это-то зачем, type = "menu" вполне достаточно.
Неиспользуемое долой.

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

Выделить код

Код:

(async data => CustomizableUI.createWidget({
	id: "FFTools",
	label: "FFTools",
	localized: false,
	onCreated(btn) {
		var doc = btn.ownerDocument;
		var n = nn => doc.createXULElement(nn);

		btn.type = "menu";
		btn.setAttribute("image", "data:image/png;base64,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");

		var popup = n("menupopup"), dummy = n("menuitem");
		popup.toggleAttribute("context");
		dummy.n = n;
		dummy.render = this.render;

		popup.append(dummy);
		btn.prepend(popup);

		var tt = btn.appendChild(n("box")).appendChild(n("tooltip"));
		tt.btn = dummy.btn = btn;
		tt.setAttribute("onpopupshowing", "return !(this.btn.open = true);");
		btn.setAttribute("tooltip", tt.id = btn.id + "-tooltip");
	},
	render() {
		var {n, btn, parentNode: popup} = this;
		this.remove();

		for(var o of data) {
			if (!o) {
				popup.append(n("menuseparator"));
				continue;
			}
			var {lab, url, img} = o;
			var menuitem = n("menuitem");
			
			menuitem.setAttribute("label", lab || url);
			if (img)
				menuitem.className = "menuitem-iconic",
				menuitem.setAttribute("image", img);
			menuitem.url = url;
			popup.append(menuitem);
		}
		popup.setAttribute(
			"oncommand", 
			"gBrowser.selectedTab = gBrowser.addTrustedTab(event.target.url);"
		);
	}
}))([{
	url: "about:config",
	img: "chrome://browser/skin/developer.svg"
},{
	lab: "about:config old",
	url: "chrome://global/content/config.xhtml",
	img: "chrome://browser/skin/developer.svg"
},{
	url: "about:support",
	img: "chrome://global/skin/icons/info.svg"
},{
	url: "about:performance",
	img: "chrome://global/skin/icons/performance.svg"
},{
	url: "about:memory",
	img: "chrome://global/skin/icons/warning.svg"
},{
	url: "about:profiling",
	img: "chrome://devtools/skin/images/profiler-stopwatch.svg"
},{
	lab: "about:debugging",
	url: "about:debugging#/runtime/this-firefox",
	img: "chrome://browser/skin/developer.svg"
},
null,{
	lab: "Библиотека",
	url: "chrome://browser/content/places/places.xhtml",
	img: "chrome://browser/skin/library.svg"
},{
	lab: "О сборочной конфигурации",
	url: "about:buildconfig",
	img: "chrome://devtools/skin/images/settings.svg"
}]);


P.S. Если в UA поста вопроса гуглятина,
то хорошая практика — указывать о какой версии Firefox речь.

Dumby
Так откуда ж мне знать, что оно неиспользуемое:(
Большое спасибо! Прекрасно работает.
За юзерагент извиняюсь, проверял кое-что и забыл сменить. Обычно стараюсь ставить на актуальный. когда что-то спрашиваю. Пользуюсь обычно 78 и 101. Хотя вообще у меня их много.
А можно ещё спросить, как сделать такое же, но для папок виндовс? Вариантов кнопок, открывающих одну папку, много, а хотелось бы одну кнопку на всех.

Опять... агент сменил, а страницу не обновил:dumb:

kazarin пишет

А можно ещё спросить, как сделать такое же, но для папок виндовс?

Что-то не слишком подробный вопрос. Может типа как-то так

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

Выделить код

Код:

(async data => CustomizableUI.createWidget({
	id: "WinFolders",
	label: "Some Label",
	localized: false,
	onCreated(btn) {
		var doc = btn.ownerDocument;
		var n = nn => doc.createXULElement(nn);

		btn.type = "menu";
		btn.setAttribute("image", this.image);

		var popup = n("menupopup"), dummy = n("menuitem");
		popup.toggleAttribute("context");
		dummy.n = n;
		dummy.render = this.render;

		popup.append(dummy);
		btn.prepend(popup);

		var tt = btn.appendChild(n("box")).appendChild(n("tooltip"));
		tt.setAttribute("onpopupshowing", "return !(this.btn.open = true);");
		btn.setAttribute("tooltip", tt.id = (tt.btn = btn).id + "-tooltip");
	},
	render() {
		var {n, parentNode: popup} = this;
		this.remove();

		for(var o of data) {
			if (!o) {
				popup.append(n("menuseparator"));
				continue;
			}
			var {lab, file, img} = o;
			var menuitem = n("menuitem");

			menuitem.setAttribute("label", lab || file.leafName);
			menuitem.className = "menuitem-iconic";
			menuitem.setAttribute("image", img || "moz-icon://file:///" + file.path);
			menuitem.tooltipText = file.path;

			menuitem.file = file;
			popup.append(menuitem);
		}
		popup.setAttribute(
			"oncommand", 
			'var {file} = event.target; file.exists() ? file.reveal() : Services.prompt.alert(null, "Not found!", file.path);'
		);
	},
	get image() {
		var sym = Symbol.iterator;
		data[sym] = () => {
			delete data[sym];
			var it = data[sym](), {next} = it;
			var f = Components.Constructor("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
			it.next = () => {
				var res = next.call(it), obj = res.value;
				if (obj) {
					var {file} = obj;
					//if (typeof file == "function") obj.file = obj.file(f);
					if (typeof file == "string") {
						var ind = file.indexOf("\\"), has = ind > 0;
						var root = has ? file.slice(0, ind) : file;
						if (root.length > 2)
							file = Services.dirsvc.get(root, Ci.nsIFile).path
								+ (has ? file.slice(ind) : "");
						obj.file = f(file);
					}
				}
				return res;
			}
			return it;
		}
		delete this.image;
		return this.image = "chrome://browser/skin/save.svg";
	}
}))([{

	file: "WinD"
},{
	lab: "Автозагрузка",
	file: "Progs\\Startup"
},{
	file: "SysD\\drivers\\etc"
},
null, {
	lab: "Fox",
	file: "GreD",
	img: "chrome://branding/content/icon16.png"
},{
	file: "UChrm\\user_chrome_files\\custom_scripts",
	img: "data:image/svg+xml, <svg xmlns='http://www.w3.org/2000/svg' fill='%23fee082' stroke='%23b28501'><path d='M 7.7500004,3.5 H 13.5 a 2,2 45 0 1 2,2 v 7 a 2,2 135 0 1 -2,2 h -11 a 2,2 45 0 1 -2,-2 v -9 a 2,2 135 0 1 2,-2 H 5.0000004 A 1.3150997,1.3150997 20.81677 0 1 5.8737051,1.8321819 L 7.7500004,3.5 5.8737051,5.1678181 A 1.3150997,1.3150997 159.18323 0 1 5.0000004,5.5 H 0.5'/></svg>"
},{
	file: "D:\\Folder\\SubFolder"
}]);

Dumby
Да-да, я именно это имел в виду! Огромнейшее спасибо! Ужасно не хватало такой кнопки.

Dumby пишет

Настройки — about:llc

Как переместить эту закладку на панель навигации ?

kokoss пишет

Как переместить эту закладку на панель навигации ?

как вариант добавить в эту кнопку код:

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

Выделить код

Код:

},{
	lab: "LLC Config",
	url: "about:llc",
	img: "resource://long_left_click/svg/long-click.svg"

kokoss пишет

Как переместить эту закладку на панель навигации ?

Никак. Закладка — это же всего лишь запись в places.sqlite,
её нельзя переместить на панель навигации.

Farby пишет

как вариант добавить в эту кнопку код:
скрытый текст

Мне для этого достаточно и дефолтной кнопки "Закладки", но это не то.

Dumby пишет

Никак.

Ясненько.

Dumby
Извините, что снова о том же, но что надо изменить в скриптах-кнопочках, чтоб они завелись на 68? Замечательно работают на 78 и выше, но хотелось бы, чтоб и там тоже.
(почему именно на 68, это форк квантумной лисы, работающей на XP, но на ней работают и все те же кнопки и скрипты, что и на обычном 68, только один скрипт за всё время не завёлся, видимо, обращается именно к изменённой части)

kazarin пишет

68, это форк квантумной лисы

Уж не знаю что за форк, а на квантумной лисе 68
<toolbarbutton>-добро построено на XBL, значит такая правка

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

Выделить код

Код:

…
		//btn.type = "menu";
		btn.setAttribute("type", "menu");

Dumby
Одна-единственная строчка разницы... поразительно.
Огромное вам спасибо ещё раз! Всё работает.

Dumby
Сделайте пожалуйста что бы этот скрипт отрывал историю и закладки рядом с текущей вкладкой!
Add, и что бы работал в UCF/2021-6-5. И желательно что бы был выбор, открывать рядом с текущей или в новой!

kokoss пишет

Сделайте пожалуйста что бы этот скрипт отрывал историю и закладки рядом с текущей вкладкой!

Дело в том, что «этот скрипт» ничего не открывает,
он просто меняет метод BrowserUtils.whereToOpenLink().
Можно попробовать "TabOpen" послушать чтобы вкладку подвинуть,
но это как-то нестрого, нужно тестировать.

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

Выделить код

Код:

(async url => {
	try {var exp = ChromeUtils.importESModule(url + "sys.mjs");}
	catch {exp = ChromeUtils.import(url + "jsm");}
	var bu = exp.BrowserUtils, {whereToOpenLink} = bu;

	var ts, ind;
	var args = ["TabOpen", e => Cu.now() - ts < 100
		&& e.target.ownerGlobal.gBrowser.moveTabTo(e.target, ind)
	, {once: true}];

	var rn = Ci.nsINavHistoryResultNode, notRn = o => !(o instanceof rn);

	bu.whereToOpenLink = function(e) {
		var beg, res = whereToOpenLink.apply(bu, arguments);
		if (!Event.isInstance(e) || "ws".includes(beg = res[0])) return res;
		try {
			var trg = e.composedTarget, win = trg.ownerGlobal;
			var node = trg._placesNode;
			if (notRn(node)) {
				node = trg.closest("tree")?.selectedNode;
				if (notRn(node)) return res;
				win = win.BrowserWindowTracker.getTopWindow();
			}
			//if ((e.button == 1 || e.ctrlKey) && res == "tab") return "current";

			var gb = win.gBrowser;
			if (beg == "c") {
				if (gb.selectedTab.isEmpty || node.uri.startsWith("javascript:"))
					return res;
				res = "tab";
			}
			if (res.startsWith("t")) {
				ts = Cu.now();
				ind = gb.selectedTab._tPos + 1;
				gb.tabContainer.addEventListener(...args);
			}
			return res;
		}
		catch(ex) {return res;}
	}
})("resource://gre/modules/BrowserUtils.");

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

Прости великодушно, но я слишком глуп чтобы такие загадки разгадывать.
Это ещё более(!) непонятно, чем «переместить закладку на панель навигации».

Dumby пишет

Можно попробовать "TabOpen" послушать чтобы вкладку подвинуть,
но это как-то нестрого, нужно тестировать.
скрытый текст

Благодарю :beer:, работает, а если нужно что бы открывало в новой вкладке, то изменяем "TabOpen" на "whereToOpenLink".

Dumby возможно ли сделать из этого расширения https://www.upload.ee/files/14493725/do … s.xpi.html кнопку для UCF что бы закачки передавались в портативный Download Master

Тут потребовалось, а оказалось, что у меня не работает.
1. Вопрос-подтверждение "Удалить закладку/папку?" Сам скрипт выше.

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

Выделить код

Код:

(async repl => {
  var obj = `{\n  ${
    (await (await fetch("chrome://browser/content/places/controller.js")).text())
      .match(/async _removeRange\(.+?\n\ +}(?=,\n)/s)[0]
      .replace("// This is a common bookmark item.", repl)
  }\n}`
  var ps = await ChromeUtils.compileScript("data:,(" + encodeURIComponent(`${obj => {
    var patch = async ctor => {
      var proto = ctor.prototype, meth = proto?._removeRange;
      meth && Object.assign(proto, obj);
    }
    var key = "PlacesController";
    var desc = Object.getOwnPropertyDescriptor(window, key);
    if (!desc) return;

    var {get} = desc;
    if (get)
      desc.get = () => {
        var val = get();
        patch(val);
        return val;
      },
      Object.defineProperty(window, key, desc);
    else
      patch(desc.value);
  }})(${obj});`));

  var obs = doc => "PlacesController" in doc.ownerGlobal && ps.executeInGlobal(doc);
  var topic = "chrome-document-loaded";
  Services.obs.addObserver(obs, topic);
  Services.obs.addObserver(function quit(s, t) {
    Services.obs.removeObserver(quit, t);
    Services.obs.removeObserver(obs, topic);
  }, "quit-application-granted");
})(
    `$&
        if (!removedFolders.ignore) {
/*
          let info = await PlacesUtils.bookmarks.fetch(node.bookmarkGuid);
*/
          let args = [node.bookmarkGuid];
          let isBookmark = PlacesUtils.nodeIsBookmark(node);

          isBookmark && args.push(null, {includePath: true});
          let info = await PlacesUtils.bookmarks.fetch(...args);
          if (isBookmark) info.parentGuid = info.path[0].guid;
          if (
            info?.parentGuid == "${PlacesUtils.bookmarks.toolbarGuid}" &&
            !(removedFolders.ignore ??= Services.prompt.confirm(
              null, null, "Удалить закладку/папку?"
            ))
          ) {
            totalItems--;
            continue;
          }
        }`
);


2. "Восстановить удалённое". Ссылки в коде.
скрытый текст

Выделить код

Код:

// Исходная версия https://github.com/alice0775/userChrome.js/blob/master/undoBookmarksContextMenu.uc.js
// более свежая https://github.com/alice0775/userChrome.js/blob/master/91/undoBookmarksContextMenu.uc.js
// Версия _zt https://forum.mozilla-russia.org/viewtopic.php?pid=792868#p792868
// Версия ВВП https://forum.mozilla-russia.org/viewtopic.php?pid=798662#p798662
// Это вариант от Dumby https://forum.mozilla-russia.org/viewtopic.php?pid=798678#p798678
// иконка моя
(async sep => {
  if (!sep) return;

  var key = "hasRemoveTransaction";
  var g = Cu.import("resource://gre/modules/PlacesTransactions.jsm", {});
  if (!g[key]) {
    Services.scriptloader.loadSubScript(
      `data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
    );
    var raws = g[key];
    g[key] = entry => {
      for(var tr of entry)
        if (raws.get(tr) instanceof PlacesTransactions.Remove)
          return true;
    }
  }
  var menuitem = document.createXULElement("menuitem");
  for(var args of Object.entries({
    closemenu: "single",
    class: "menuitem-iconic",
    id: "placesCmd_undoRemove",
    label: "Восстановить удалённое",
    oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
    image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAEd0lEQVRIx53Ve2yW5RkG8N/7ft8Hxa/9CgjSSDnMA4xVTUMQWzadhzhnjSCC0y0jSjom+JdGp844ZhSjHLJsowaNZoCCHOWglXGoQbo2fBW3obDNUBfXKpsUVhxn6OHdH8+31GXGmL3/3M913ffzPNed93qeBxvmI8umCNjyPFEDSRJw40qKa9jyHSbdzY77Ar+9FTvZ+nHAb9T7P76YcwlOktlBdJia2SSthfRvaR/Mync5tp85Q2mbEHJ/X0bqRTpOBJyehog3C3j1HV9RQP91xG9T8z2SofyphMELWH4zW5s40s7kQ/T0cnYfXfPD1OgM0TlGzSN1DZkzxG1EdURjaa9HCfddEupfuSkIfHXJ5wVENA8Iw9QcRnbzXj31++g8w+WVZHMhXzaKhu2MGMtd36f5CTrn8WAJyf001KCYpA6dDBtLlOfoYuI6kgp6XuLsbhQRfw39ovDPx7/JgYS1b3NkCxcvoPgTevIULyFVTm4jyinp4uRJiirYt5BMLc15kvE0lnCsgZaE2wdx6LVCo9/ghQn4LhdFxEOonELv8ogVV9Iyjd9fx8T1FE2nq5XSQWQuJbsmrJH9JclMsldhFsW3EU0iuxsb6f9zTA3dHj1J1yPsKaPrIOtnkn6KDY2cvYF/vkfmINGuiPdnUD6Pd/7Mur/x0RCqXqTfj+m5glyO1B8ovR1pijeil6KvBw8V1QaB/QYWzNgSYu+TmMm5GVhKT47OBWR28kgnvxvKS/OjvuMGuTSr72FtBR/WctNe4l+H3GUt7DxB2TKe+CmnF5IZRzSZzFmS4wy9n2QGY2eR3Mx193DqAao+4vh04lZS2/EkQyqo7cWux4Kjwf4gKF3N5hEs+QsLlwVuVRPLP2FeTSh95hZSpSx6OOAkwWCWLSEqYWVz4JtLqd3GiuX0/oS6XKjd/BQbF6SRJynij4eJKpl7WSg8cCdzxjHjH3QuIsaorQx7NCx8QQdxCYM2kzxL423BbGN6SP+I6tmcO86hahqu4MA2To3mX+eTxbA2er6VJjcA/Yl2kOzlrutJxrDraaI1XH09j3fQVk7pcG5sCgIOttLVzejpmMPrnxJvYnIZXelwxGHTCOaX0PorsmUMOC/wpeNofg4fjg9EW3+k6CzcYJ99M7h6fSfxFFJrMZzFm0I+fzXxlTQ1EHf3eSlJyI3mlYGs/yEPx4F7/NkQ11zEymFhnDoY9RnwcHmIZ34Q1CcTkBD9FSM58gu8SmUHbmVuJbYRt2AiE/cwZT/tG3h+Gh29lH3A+VPZ+zMq1rHwcq6tY1U31ReQXPgFt3M+/9+xJRvM9f5S3NnX6btLqfqYt1p44xQrFnH0aX6zmBXIj+Hae0N95qG+ea/PJjrB7tV47csE7CzEJqKXyTcW8CquaibfwTOX0FjPLSfYk+G8XWyb+rlTUcndVUQjWXOK+FiBn/QfQen/FVBVVdhoc4FIkczqM5WBvPMWp4fQMpzFHeEN2NCFCcyNSH2b1AiSl3nhVnrnkjpNkqP9jrBvdzX8G2rIlxvMK3a7AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==",
  }))
    menuitem.setAttribute(...args);

  var desc = Object.getOwnPropertyDescriptor(XULElement.prototype, "hidden");
  var {set} = desc;
  desc.set = () => {
    var entry = PlacesTransactions.topUndoEntry;
    var vis = entry && g[key](entry);
    vis && menuitem.removeAttribute("disabled");
    set.call(menuitem, !vis);
  }
  Object.defineProperty(menuitem, "hidden", desc);
  sep.before(menuitem);
})(document.getElementById("placesContext_deleteSeparator"));


Поделитесь, кто пользуется и у кого работает.

xrun1 пишет

Поделитесь, кто пользуется и у кого работает.

Вопрос на удаление работал, но зачем нужен? Только время отнимает.

2. второй очень полезный: пережал иконку в Восстановить закладки, сократив на 800 байт.

Dobrov пишет

Только время отнимает.

1. Меня заставляет ещё раз подумать. Код работает в боковой панели, а хотелось бы ещё в панели закладок. Помнится, раньше и там работал.
2. Пережали иконку - спасибо, но мне это не интересно: в посте выше в коде моя иконка и вопрос был в том, что этот код не работает у меня на [firefox] v104.0 и работает ли вообще?
UPD: обновлённый код от alice0775 пока не проверял.

egorsemenov06 пишет

возможно ли сделать из этого расширения https://www.upload.ee/files/14493725/do … s.xpi.html кнопку для UCF

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

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

Разве что можно попытаться забрать у него browser.runtime.sendNativeMessage() себе,
и попробовать построить симуляцию на аргументах командной строки.
Затея так себе, но раз всё равно не работает...
Создать .mjs'ку, прописать там путь до dmaster.exe. Импортировать, например, так

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

Выделить код

Код:

var UcfStylesScripts = {
    /** ************************▼ Настройки ▼************************ */
    .......

    scriptsbackground: [ // В фоне [System Principal]
        .......

        { func: 'ChromeUtils.importESModule("chrome://user_chrome_files/content/custom_scripts/DownloadMaster.mjs");' },
    ],
    /** ************************▲ Настройки ▲************************ */
};


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

Выделить код

Код:

if (!ChromeUtils.domProcessChild.childID) {

	var path = "C:\\Portable Program Files\\Download Master Portable\\dmaster.exe";

	var urlsListURL = "download_master.urls"; // Relative to "chrome" folder, use "/"


	var U = Services.io.newFileURI, CC = Components.Constructor;
	var F = CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");

	var file = F(path);
	if (!file.exists()) throw "Download Master Portable Not Found!\n" + path;
	var proc = CC("@mozilla.org/process/util;1", "nsIProcess", "init").bind(null, file);

	var urlsListPath = Services.io.newURI(
		U(Services.dirsvc.get("UChrm", Ci.nsIFile)).resolve(urlsListURL)
	).QueryInterface(Ci.nsIFileURL).file.path;

	var esModuleURI = Components.stack.filename;
	ChromeUtils.registerWindowActor("DownloadMaster", {
		parent: {esModuleURI},
		remoteTypes: ["extension"],
		child: {esModuleURI, events: {DOMDocElementInserted: {}}},
		matches: ["moz-extension://*/_generated_background_page.html"]
	});
	var DownloadMasterParent = class extends JSWindowActorParent {
		async receiveMessage(msg) {
			var id = msg.name, d = msg.data;
			if (id == "D") return file.launch(); // openDownloadMaster

			var args = [];
			if (id == "A") var arg = d.url; // openAddURLWindow
			else {
				var push, url, arg = "", dwnld = id == "l";

				if (dwnld) url = d.url; // downloadFile
				else { // addUrls
					if (d.urls.length == 1) url = d.urls[0];
					else await IOUtils.writeUTF8(
						push = url = urlsListPath, d.urls.join("\n")
					);
				}
				arg += `<url>${url}</url><referer>${d.referrer}</referer>`;

				var str = "<cookies>"
				for(var {name, value} of d.cookies) str += `${name}=${value}; `;
				arg += str + "</cookies>";

				if (dwnld) { // downloadFile
					var uri = U(F(d.filename)).QueryInterface(Ci.nsIFileURL);

					if (d.isNameSelected)
						arg += `<savepath>${uri.file.parent.path}</savepath>`
							+ `<filename>${uri.fileName}</filename>`;

					var ext = uri.fileExtension;
					if (ext) arg += `<fileExtension>.${ext}</fileExtension>`
				}
				else if (d.urlsdata?.title) // addUrls [1]
					arg += `<filename>${d.urlsdata.title}</filename>`;

				args.push(push || "-addurl");
			}
			args.push(arg);
			proc().runwAsync(args, args.length);
		}
	}
}
export {DownloadMasterParent};

export class DownloadMasterChild extends JSWindowActorChild {
	handleEvent(e) {
		var win = this.contentWindow;
		if (WebExtensionPolicy.getByHostname(win.location.host).id != "dm@westbyte.com")
			return;

		win = win.wrappedJSObject;
		var o = win.Object, res = new o();
		res.downloadState = "DOWNLOAD_ACCEPTED";

		var desc = new o();
		desc.configurable = true;
		Cu.exportFunction(() => {
			delete res.protocolVersion;
			return res.protocolVersion = win.BRDEP.protocolVersion;
		}, desc, {defineAs: "get"});
		o.defineProperty(res, "protocolVersion", desc);
		
		var dummy = win.Promise.resolve(res);
		var sendMethods = new Set([
			"downloadFile", "addUrls", "openAddURLWindow", "openDownloadMaster"
		]);
		win.browser.runtime.sendNativeMessage = (s, data) => {
			if (sendMethods.has(data.method)) {
				var id = data.method[4];
				delete data.method;
				this.sendAsyncMessage(id, data);
			}
			return dummy;
		}
	}
}

xrun1 пишет

1. Вопрос-подтверждение "Удалить закладку/папку?"
Код работает в боковой панели

Видимо, симилярно этому. Заведи уже наконец "новый" UCF.

2. "Восстановить удалённое"

Тоже вроде обсуждалось. Но, наверно, без lazy.
Вобщем, пока Cu.import() еще с нами, такой вариант

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

Выделить код

Код:

(async sep => {
	if (!sep) return;

	var key = "hasRemoveTransaction";
	var g = Cu.import("resource://gre/modules/PlacesTransactions.jsm", {});

	var raws = (g.lazy || g).TransactionsHistory?.proxifiedToRaw;
	if (raws) g = raws;

	if (!g[key]) {
		if (!raws) {
			Services.scriptloader.loadSubScript(
				`data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
			);
			raws = g[key];
		}
		g[key] = entry => {
			for(var tr of entry)
				if (raws.get(tr) instanceof PlacesTransactions.Remove)
					return true;
		}
	}
	var menuitem = document.createXULElement("menuitem");
	for(var args of Object.entries({
		closemenu: "single",
		class: "menuitem-iconic",
		id: "placesCmd_undoRemove",
		label: "Восстановить удалённое",
		oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAEd0lEQVRIx53Ve2yW5RkG8N/7ft8Hxa/9CgjSSDnMA4xVTUMQWzadhzhnjSCC0y0jSjom+JdGp844ZhSjHLJsowaNZoCCHOWglXGoQbo2fBW3obDNUBfXKpsUVhxn6OHdH8+31GXGmL3/3M913ffzPNed93qeBxvmI8umCNjyPFEDSRJw40qKa9jyHSbdzY77Ar+9FTvZ+nHAb9T7P76YcwlOktlBdJia2SSthfRvaR/Mync5tp85Q2mbEHJ/X0bqRTpOBJyehog3C3j1HV9RQP91xG9T8z2SofyphMELWH4zW5s40s7kQ/T0cnYfXfPD1OgM0TlGzSN1DZkzxG1EdURjaa9HCfddEupfuSkIfHXJ5wVENA8Iw9QcRnbzXj31++g8w+WVZHMhXzaKhu2MGMtd36f5CTrn8WAJyf001KCYpA6dDBtLlOfoYuI6kgp6XuLsbhQRfw39ovDPx7/JgYS1b3NkCxcvoPgTevIULyFVTm4jyinp4uRJiirYt5BMLc15kvE0lnCsgZaE2wdx6LVCo9/ghQn4LhdFxEOonELv8ogVV9Iyjd9fx8T1FE2nq5XSQWQuJbsmrJH9JclMsldhFsW3EU0iuxsb6f9zTA3dHj1J1yPsKaPrIOtnkn6KDY2cvYF/vkfmINGuiPdnUD6Pd/7Mur/x0RCqXqTfj+m5glyO1B8ovR1pijeil6KvBw8V1QaB/QYWzNgSYu+TmMm5GVhKT47OBWR28kgnvxvKS/OjvuMGuTSr72FtBR/WctNe4l+H3GUt7DxB2TKe+CmnF5IZRzSZzFmS4wy9n2QGY2eR3Mx193DqAao+4vh04lZS2/EkQyqo7cWux4Kjwf4gKF3N5hEs+QsLlwVuVRPLP2FeTSh95hZSpSx6OOAkwWCWLSEqYWVz4JtLqd3GiuX0/oS6XKjd/BQbF6SRJynij4eJKpl7WSg8cCdzxjHjH3QuIsaorQx7NCx8QQdxCYM2kzxL423BbGN6SP+I6tmcO86hahqu4MA2To3mX+eTxbA2er6VJjcA/Yl2kOzlrutJxrDraaI1XH09j3fQVk7pcG5sCgIOttLVzejpmMPrnxJvYnIZXelwxGHTCOaX0PorsmUMOC/wpeNofg4fjg9EW3+k6CzcYJ99M7h6fSfxFFJrMZzFm0I+fzXxlTQ1EHf3eSlJyI3mlYGs/yEPx4F7/NkQ11zEymFhnDoY9RnwcHmIZ34Q1CcTkBD9FSM58gu8SmUHbmVuJbYRt2AiE/cwZT/tG3h+Gh29lH3A+VPZ+zMq1rHwcq6tY1U31ReQXPgFt3M+/9+xJRvM9f5S3NnX6btLqfqYt1p44xQrFnH0aX6zmBXIj+Hae0N95qG+ea/PJjrB7tV47csE7CzEJqKXyTcW8CquaibfwTOX0FjPLSfYk+G8XWyb+rlTUcndVUQjWXOK+FiBn/QfQen/FVBVVdhoc4FIkczqM5WBvPMWp4fQMpzFHeEN2NCFCcyNSH2b1AiSl3nhVnrnkjpNkqP9jrBvdzX8G2rIlxvMK3a7AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==",
	}))
		menuitem.setAttribute(...args);

	var desc = Object.getOwnPropertyDescriptor(XULElement.prototype, "hidden");
	var {set} = desc;
	desc.set = () => {
		var entry = PlacesTransactions.topUndoEntry;
		var vis = entry && g[key](entry);
		vis && menuitem.removeAttribute("disabled");
		set.call(menuitem, !vis);
	}
	Object.defineProperty(menuitem, "hidden", desc);
	sep.before(menuitem);
})(document.getElementById("placesContext_deleteSeparator"));

Dumby пишет

Затея так себе

Спасибо но у меня так и не заработало!Наверно лыжи не едут :)

Dumby пишет

Заведи уже наконец "новый" UCF.

Нет, я не трус, но я боюсь. Боюсь, смогу ли я, способен ли?
2. Работает, спасибо.:beer:

xrun1 пишет

2. Работает

Не понял. А 1. что, не работает что ли?

Нет, я не трус, но я боюсь. Боюсь, смогу ли я, способен ли?

Так склонируй браузер, и там пробуй разобраться.
Никто не торопит, ни чем не рискуешь.
В отношении скриптов в "новом" вообще ничего не поменялось.
Не нравится встроенный загрузчик — не используй, делай как привык.
Со стилями да, наверно придётся почесать репу.

Dumby пишет

Не понял. А 1. что, не работает что ли?

Работает в боковой панели. На панели закладок не спрашивает.
Я почему об этом вспомнил? Случайно удалил закладку, потом вспомнил, что там пост обновляемый. Пришлось лезть на форум, вспоминать тему, формулировать запрос, искать... А с подсказкой подумал бы второй раз, стоит ли удалять.
Что касается "нового" UCF. Там слишком много для меня слов, смысл которых не понимаю. И файлы непонятные со страшными словами CustomStylesScripts.jsm, CustomStylesScriptsChild.jsm. Что куда пихать не понял тогда, не понимаю и сейчас. Даже пробовать боюсь, да и зачем, если всё работает.

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

P.S. Например, у меня нет стилей AGENT_SHEET,  AUTHOR_SHEET или USER_SHEET. У меня просто стили, которые работают. Как-то так...

xrun1 пишет

1. Вопрос-подтверждение "Удалить закладку/папку?"
скрытый текст

А у меня наоборот, этот скрипт на панели закладок работает, а в боковой нет.

Dumby пишет

Вобщем, пока Cu.import() еще с нами, такой вариант

В панели закладок вернуть удалённое работает. А как насчёт окна "Управление закладками"?


Dumby - возможно добавить восстановление удалённых закладок было и в окне Библиотеки?


Ещё вопрос по стилям UCF в CustomStylesScripts.jsm - подскажите, как подключать стили в зависимости от операционной системы?
Хотелка такая - подключать общие AGENT_SHEET и USER_SHEET стили как обычно, но добавить ещё пару CSS в зависимости от OS.
То есть чтобы на macos подключались ещё пара своих AGENT_SHEET и USER_SHEET стилей, на linux пара своих, на windows тоже.

Выделить код

Код:

var UcfStylesScripts = { /** ************************▼ Настройки ▼************************ */
……………………………
	stylesall: [ // Для всех документов - ЗДЕСЬ разделить стили по типу операционной системы
		{ path: "custom_styles_all_agent.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
		{ path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
	],

Dobrov
Это можно в самих стилях указать

Пример

Выделить код

Код:

@media (-moz-platform: windows) {
  /* some rules targeting windows OS  */
}
@media (-moz-platform: osx) {
  /* some rules targeting mac OS  */
}
@media (-moz-platform: linux) {
  /* some rules targeting linux (by which I think we normally mean gtk in practice?  */
}

xrun1 пишет

Работает в боковой панели. На панели закладок не спрашивает.

kokoss пишет

А у меня наоборот, этот скрипт на панели закладок работает, а в боковой нет.

У меня и в боковой, и на панели закладок работает. Dumby, спасибо.

kokoss пишет

А у меня наоборот, этот скрипт на панели закладок работает, а в боковой нет.

Хех, у меня аналогично, в боковой панели спасает Ctrl+Z

bezuma
А ты его куда пристроил?

xrun1 пишет

Работает в боковой панели. На панели закладок не спрашивает.

Это я понял ещё из изначального вопроса.
На что была дадена ссылка, где сказано какую строку куда добавить.
Вот и осталось непонятно, помогла строка или нет.


kokoss пишет

А у меня наоборот, этот скрипт на панели закладок работает, а в боковой нет.

Этого не может быть. Наверно ты не понял что скрипт делает.
И неудивительно: человек когда-то что-то спрашивает, затем кто-то другой
спрашивает мод, затем третий, зачем-то, тащит это к себе, да ещё и переименовывает
"Удалять с панели закладок?" в "Удалить закладку/папку?". Короче — испорченный телефон.


Подтверждение появляется если:
1. Удаляется закладка из панели закладок (включая закладки из подпапок).
2. Удаляется папка из панели закладок (только из самой панели закладок, исключая вложенные папки).
А удаляется где — неважно (панель закладок, main-menubar, сайдбар, Библиотека, и.т.д).


Dobrov пишет

возможно добавить восстановление удалённых закладок было и в окне Библиотеки?

Оно там есть. В смысле должно работать Ctrl+Z.
Но можешь попробовать загрузить скрипт в это окно,
в CustomStylesScripts.jsm, для Библиотеки, есть даже пример-комментарий.

чтобы на macos подключались ещё пара своих AGENT_SHEET и USER_SHEET стилей, на linux пара своих, на windows тоже.

Так а в чём, собственно, затруднение?
Я не знаю как тебе видится это организовать, но, допустим, например:


В начале скрипта импортируем лисий модуль AppConstants,
и определяем функцию типа var os = name => `${name}_${AppConstants.platform}.css`;
Тогда, там где path, вместо строки, пишем вызов этой функции path: os("style1"),
получается, как если бы, path: "style1_macosx.css" (или "style1_linux.css", или "style1_win.css").
Не проверял.


Farby пишет

osx

Не osx, а macos. К тому же, Firefox 99+, а не 97.

bezuma пишет

Хех, у меня аналогично

У меня просто старая версия UCF, видимо поэтому у меня так работает.

kokoss
Так и у меня старая, и не собираюсь переходить - бесцветные иконки категорически отказываюсь лицезреть )

bezuma пишет

бесцветные иконки категорически отказываюсь лицезреть

Если только из за этого, то просто замените в новом UCF, папку svg и файл icons_in_menu на свои.
Add, забыл. там ещё понадобится комплект full_theme, где и находятся папка svg и файл icons_in_menu.

kokoss
Да пробовал свои воткнуть, с наскока не вышло, а потом запутался вовсе и плюнул, без сторонней помощи не осилю

Dumby пишет

В начале скрипта импортируем лисий модуль AppConstants, и определяем функцию типа var os = name => `${name}_${AppConstants.platform}.css`;

с @media (-moz-platform не совсем подходит, так как хочу, чтобы работало на "быстрых" старых версиях, как UCF для FF78+. Так нормально?

Выделить код

Код:

const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
var os = name => `${name}_${AppConstants.platform}.css`;

var UcfStylesScripts = { /** ************************▼ Настройки ▼ + доп. подключение слилей для вашей ОС */
………………………
	stylesall: [ // Для всех документов
		{ path: "custom_styles_all_agent.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
		{ path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
	// добавить стиль для вашей операционной системы: "*_macosx.css" "*_linux.css" "*_win.css"
		{ path: os("custom_styles_all_agent"), type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
		{ path: os("custom_styles_all_user"), type: "AGENT_SHEET", sheet() { registerSheet(this); }, },
	],
Dumby пишет

На что была дадена ссылка, где сказано какую строку куда добавить.
Вот и осталось непонятно, помогла строка или нет.

Ссылка помогла после повторного прочтения. Меня смутило слово "симилярно".
Что касается перехода на "новый" UCF. Привык делать, если чётко понимаю "что" и "зачем". Второй пункт спорный: если всё работает, то зачем? Оптимизация?
Первый пункт непонятен. Догадываюсь, что CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm некие загрузчики, но чем они отличаются и, в зависимости от этого, что куда прописывать. Виталий этого не объяснил, или я невнимательно читал. Когда "ucfobj" считать объектом? Методом "тыка" делать не хочу, да и долго это будет.
P.S. А я и не переходил на icons_in_menu от Виталия. У меня остался свой с цветными иконками, за исключением системных.:P
P.P.S. Опять переделывать user_chrome.js лениво, сейчас так.

скрытый текст
Image002_2022-09-15_12-09.pngImage003_2022-09-15_12-09.png

bezuma пишет

без сторонней помощи не осилю

Пробуйте.

voqabuhe пишет

bezumaА ты его куда пристроил?

Никуда - тупо жмакаю клаву

kokoss
Благодарю - поковыряюсь )

Вот пара моих скриптов, надеюсь кому пригодятся.
Они сделаны под загрузчик by alice0775; будут ли работать с описанным в начале этой темы загрузчиком, не знаю.
 
Quick Mark
Переработанное меню добавления закладки. Можно выбрать папку по умолчанию, переименовать папку, создать новую. Высота окошка настраивается (потянуть мышкой). Внутри скрипта есть несколько настроек, которые вы можете изменить по вкусу (думаю, в последних версиях FF не все они уже работают).

Скриншот
0kwPfoB.png

Код

Выделить код

Код:

// QUICK BOOKMARK
// Replacement for QuickMark extension for Firefox 57+
	// https://addons.mozilla.org/firefox/addon/quickmark/
	// https://github.com/TarekJor/Firefox-52-ESR-legacy-addon/blob/master/xpi/quickmark-1.2.11-fx.xpi
// Required Firefox version: min 54, max tested 70a

// Changelog
// 2017.11.14 - initial release for FF 57
// 2018.08.28 - edits for FF 62-63
// 2019.08.18 - edits for FF 68-70:
	// Some operations with folderTree variable moved from initialization code into panel.popupshowing listener as node #editBMPanel_folderTree is no longer present in DOM until popup initialized and toggleFolderTreeVisibility() called.
	// createElement() replaced with createXULElement() (used in makeResizer() and makeFolderContextMenu()).
// 2020.05.08 - edits for FF 76
	// Bookmarks panel is now accessed via StarUI.panel as getElementById(...) returns null in FF 76
// 2020.06.02 - edits for FF 78 or 79
	// Popup hint is now accessed via ConfirmationHint._panel as getElementById(...) returns null since FF 78 or 79
// 2022.06.19 - edits for FF ≈101
	// Small fixes

let settings = {

	// If false, you can set any folder as default via its context menu
	useLastSelectedFolderAsDefault: false,

	// Comment out any element to unhide it
	menuElementsToHide: [
		// '.panel-header',
		'#editBookmarkPanelImage', // FF62+
		'#editBookmarkPanelFaviconContainer', // FF62+
		// '#editBMPanel_nameRow',
		// '#editBMPanel_folderRow',
		'#editBMPanel_folderRow > hbox', // ≈FF101
		'#editBMPanel_newFolderBox',
		// '#editBMPanel_tagsRow',
		'#editBookmarkPanel_showForNewBookmarks', // FF63+
		'#editBookmarkHeaderSeparator', // ≈FF101
	],

	makeMenuNarrower: true,
	makeButtonsSmaller: false,
	hideBookmarksToolbarIfEmpty: true,
	hideThatStupidGreenTooltipOnSaving: true, // FF62+

	useOneClickMode: true,
};


let strings = {
	'en': {
		'setAsDefault': 'Set as default folder',
		'rename': 'Rename',
		'addNewFolder': 'New folder...',
	},
	'ru': {
		'setAsDefault': 'Сделать папкой по умолчанию',
		'rename': 'Переименовать',
		'addNewFolder': 'Новая папка...',
	},
};
strings['en-US'] = strings['en'];
strings['ru-RU'] = strings['ru'];


if (Services.vc.compare(Services.appinfo.platformVersion, '54.*') == -1) {
	let path = Components.stack.filename;
	path = path.substring(path.lastIndexOf('///') + 3);
	alert('“Quick Bookmark” userscript will not work with this version of Firefox. \nMinimal supported version is 54. \nPlease delete the script to avoid such alerts and possible malfunctioning. \n' + path);
}


let storage = Services.prefs.getBranch('extensions.userScripts.quickBookmark.');
let starButton = document.getElementById('star-button-box');

// let panel = document.getElementById('editBookmarkPanel'); // Null since FF76 because of “lazy load” (see browser-places.js)
let panel = StarUI.panel; // This creates the panel in FF76 if missed or just gets it in older versions

let folderTree = panel.querySelector('#editBMPanel_folderTree'); // Null in FF 68-70
let folderTreeRow = panel.querySelector('#editBMPanel_folderTreeRow');

let defaultFolderGuid = storage.getCharPref('defaultFolderGuid', PlacesUtils.bookmarks.unfiledGuid);
let lastSelectedFolderGuid = defaultFolderGuid; // Used to distinguish if user really selected the folder or just expanded/collapsed it
let shouldCloseOnClick = true;

makeResizer();
makeFolderContextMenu();

let folderTreeHeight = storage.getCharPref('folderTreeHeight', '300px');
folderTreeRow.style.height = folderTreeHeight;

for (let sel of settings.menuElementsToHide) {
	if (sel == '#editBookmarkPanel_showForNewBookmarks') continue; // This must be done later
	let el = panel.querySelector(sel);
	if (el)
		el.style.display = 'none';
	if (sel == '#editBMPanel_folderRow > hbox' && el)
		folderTreeRow.style.padding = '0px';
}

if (settings.makeMenuNarrower) {
	if (folderTree)
		folderTree.style.minWidth = '260px';

	// Fix for ~FF62b18, probably will not be needed in latest builds
	panel.querySelector('#editBookmarkPanelBottomButtons').removeAttribute('style');
}
if (settings.makeButtonsSmaller) {
	panel.querySelector('#editBookmarkPanelDoneButton').style.padding = '5px 2px 7px';
	panel.querySelector('#editBookmarkPanelRemoveButton').style.padding = '5px 2px 7px';
}
if (settings.hideThatStupidGreenTooltipOnSaving) {
	// document.querySelector('#confirmation-hint').style.display = 'none'; // Null since FF78 or 79 because of “lazy load” (see browser.js)
	let hint = document.querySelector('#confirmation-hint') || ConfirmationHint._panel; // This creates the hint in FF78-79 if missed or just gets it in older versions
	hint.style.display = 'none';
}

// let tagsSelectorCollapsed = storage.getBoolPref('tagsSelectorCollapsed', true);



// Add/remove bookmark silently when middle-clicking on the star button or pressing Ctrl+Alt+D
// This may interfere with typographic keyboard layouts; possible solution - use Ctrl+Shift+D
starButton.addEventListener('click', function(e) {
	if (e.button == 1 || (e.button == 0 && (e.ctrlKey || e.shiftKey))) {
		e.stopPropagation();
		toggleBookmark();
	}
}, true);
document.addEventListener('keydown', function(e) {
	if (e.ctrlKey && e.altKey && e.code == 'KeyD') {
		e.preventDefault();
		toggleBookmark();
	}

	// In FF63a1, if typographic layout is used and we are pressing Ctrl+Alt+D, both e.ctrlKey and e.altKey are false
	// This is a temporary workaround
	else if (e.key == '°' && e.code == 'KeyD') {
		e.preventDefault();
		toggleBookmark();
	}
});

panel.addEventListener('popupshowing', function() {
	gEditItemOverlay.toggleFolderTreeVisibility();

	if (!folderTree)
		folderTree = panel.querySelector('#editBMPanel_folderTree');

	if (settings.makeMenuNarrower)
		folderTree.style.minWidth = '260px';

	folderTree.contextMenu = 'editBMPanel_folderTree_contextMenu';

	if (settings.menuElementsToHide.includes('#editBookmarkPanel_showForNewBookmarks')) {
		let el = panel.querySelector('#editBookmarkPanel_showForNewBookmarks');
		if (el)
			el.style.display = (el.checked) ? 'none' : '';
	}

	// if (!settings.menuElementsToHide.includes('#editBMPanel_tagsRow') && !tagsSelectorCollapsed)
		// gEditItemOverlay.toggleTagsSelector();
});

panel.addEventListener('popupshown', onPopupShown);
panel.addEventListener('popuphidden', onPopupHidden);

function onPopupShown() {
	folderTree.focus();

	if (StarUI._isNewBookmark)
		gEditItemOverlay._folderTree.selectItems([defaultFolderGuid]);

	lastSelectedFolderGuid = gEditItemOverlay.selectedFolderGuid;

	// Reset var
	shouldCloseOnClick = true;

	if (settings.useOneClickMode) {
		folderTree.addEventListener('select', onFolderTreeSelect);
		folderTreeRow.addEventListener('click', onFolderTreeClick);
		folderTreeRow.addEventListener('keydown', onFolderTreeClick, true);

		// NB: keydown listener must be attached to folderTreeRow, not folderTree,
		// to be able to prevent default folderTree action (expand/collapse folder) when pressing Enter
	}
};

function onFolderTreeSelect(e) {
	// Stop undesirable calls before the panel is shown
	if (!panel.hasAttribute('panelopen')) return;

	if (gEditItemOverlay.selectedFolderGuid == lastSelectedFolderGuid)
		// 'Select' event fired, but folder not changed? Seems user expanded/collapsed some folder, so panel shouldn’t be closed
		shouldCloseOnClick = false;
	else
		lastSelectedFolderGuid = gEditItemOverlay.selectedFolderGuid;
};

function onFolderTreeClick(e) {
	// Cancel one-click mode when Alt pressed, e.g. user can rename folder with Alt+DoubleClick
	if (e.altKey) return;

	// Rename folder with F2
	if (e.code == 'F2') {
		let g = gEditItemOverlay;
		g._folderTree.startEditing(g._folderTree.view.selection.currentIndex, g._folderTree.columns.getFirstColumn());
	}

	// Close menu with Enter or left click
	else if (e.code == 'Enter' || e.button == 0) {
		// Prevent folder from expand/collapse when pressing Enter
		e.stopPropagation();

		if (shouldCloseOnClick || e.code == 'Enter') {
			setTimeout(function() {
				StarUI.panel.hidePopup();
			}, 100);
		}

		shouldCloseOnClick = true;
	}
};

function onPopupHidden() {
	// Hide bookmarks toolbar if empty
	if (settings.hideBookmarksToolbarIfEmpty) {
		setTimeout(function() {
			let toolbarFolder = PlacesUtils.getFolderContents(PlacesUtils.bookmarks.toolbarGuid).root;
			let toolbarNode = document.getElementById('PersonalToolbar');
			if (toolbarNode && !toolbarNode.collapsed && !toolbarFolder.hasChildren)
				BookmarkingUI.toggleBookmarksToolbar();
		}, 500);
	}

	// Save panel height
	let height = folderTreeRow.style.height;
	if (height && height != '0' && height != '0px' && height != folderTreeHeight) {
		storage.setCharPref('folderTreeHeight', height);
		folderTreeHeight = height;
	}

	// Save default folder guid
	if (settings.useLastSelectedFolderAsDefault && gEditItemOverlay.selectedFolderGuid && gEditItemOverlay.selectedFolderGuid != defaultFolderGuid) {
		storage.setCharPref('defaultFolderGuid', gEditItemOverlay.selectedFolderGuid);
		defaultFolderGuid = gEditItemOverlay.selectedFolderGuid;
	}

	// Save tags selector state
	/*
	let collapsed = panel.querySelector('#editBMPanel_tagsSelectorRow').collapsed;
	if (collapsed != tagsSelectorCollapsed) {
		storage.setBoolPref('tagsSelectorCollapsed', collapsed);
		tagsSelectorCollapsed = collapsed;
	}
	*/
};

// Visible resizer above OK/Cancel buttons
/*
function makeResizer() {
	let el = document.createXULElement('resizer');
	el.setAttribute('dir', 'bottom');
	el.setAttribute('element', 'editBMPanel_folderTreeRow');
	el.style.MozAppearance = 'none';
	el.style.backgroundSize = 'auto';
	el.style.backgroundPosition = 'bottom right';
	el.style.backgroundImage = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABmJLR0QAUABQAFDGhwvwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gcVDwIBDNJ+vQAAADdJREFUKM9jYCARMDIwMDD8//+fOMWMjAxMhBQdOHDg/4EDB+AmMg2Mk5CdMYBOwuUM+juJZAAAHy0zBdN01HIAAAAASUVORK5CYII=)';
	el.style.marginTop = '-16px';
	// panel.querySelector('#editBookmarkPanelRows').style.paddingBottom = "0px";
	panel.querySelector('#editBookmarkPanelContent').appendChild(el);
};
*/

// Invisible resizer at the bottom edge of panel
function makeResizer() {
	let el = document.createXULElement('resizer');
	el.setAttribute('dir', 'bottom');
	el.setAttribute('element', 'editBMPanel_folderTreeRow');
	el.style.MozAppearance = 'none';
	el.style.background = 'none';
	el.style.height = '6px';
	el.style.margin = '-2px 0 -4px';
	panel.appendChild(el);
};

function makeFolderContextMenu() {
	let menu = document.createXULElement('menupopup');
	menu.id = 'editBMPanel_folderTree_contextMenu';

	let items = ['rename', 'addNewFolder'];
	if (!settings.useLastSelectedFolderAsDefault)
		items.unshift('setAsDefault');

	for (let item of items) {
		let z = document.createXULElement('menuitem');
		z.setAttribute('label', lang(item));
		z.setAttribute('action', item);
		menu.appendChild(z);
	}

	document.querySelector('#mainPopupSet').appendChild(menu);
	// folderTree.contextMenu = 'editBMPanel_folderTree_contextMenu';

	if (!settings.useLastSelectedFolderAsDefault) {
		menu.firstChild.setAttribute('type', 'checkbox');
		menu.addEventListener('popupshowing', function(e) {
			menu.firstChild.setAttribute('checked', (defaultFolderGuid == gEditItemOverlay.selectedFolderGuid));
		});
	}

	menu.addEventListener('command', onFolderContextMenuCommand);
};

function onFolderContextMenuCommand(e) {
	let g = gEditItemOverlay;
	switch (e.target.getAttribute('action')) {
		case 'setAsDefault':
			storage.setCharPref('defaultFolderGuid', g.selectedFolderGuid);
			defaultFolderGuid = g.selectedFolderGuid;
		break;
		case 'rename':
			g._folderTree.startEditing(g._folderTree.view.selection.currentIndex, g._folderTree.columns.getFirstColumn());
		break;
		case 'addNewFolder':
			g.newFolder();
	}
};

function lang(string) {
	let l = strings[navigator.language];
	return (l && l[string]) ? l[string] : strings['en'][string] ? strings['en'][string] : string;
}

function toggleBookmark() {
	if (BookmarkingUI.status == BookmarkingUI.STATUS_STARRED) {
		let uri = gBrowser.currentURI.spec;
		try { uri = decodeURIComponent(uri); } catch {}
		PlacesUtils.bookmarks.search(uri).then((foundItems) => {
			if (foundItems.length)
				PlacesUtils.bookmarks.remove(foundItems);
		});
	}
	else
		PlacesUtils.bookmarks.insert({url: gBrowser.currentURI.spec, title: gBrowser.contentTitle, parentGuid: defaultFolderGuid});
}


 
Tab Wheel Scroll
Переключение между вкладками вращением колеса мыши.
Код

Выделить код

Код:

(function() {

// FF65-
// let tabbox = document.getElementById('tabbrowser-tabs').arrowScrollbox._scrollbox;

// FF66+
let tabbox = document.getElementById('tabbrowser-tabs').arrowScrollbox.scrollbox;

tabbox.addEventListener('wheel', wheelHandler);

function wheelHandler(event) {
	// Preserve original behaviour if meta (Windows) key is held
	if (event.metaKey) return;

	if (event.deltaY < 0) {
		gBrowser.tabContainer.advanceSelectedTab(-1, false);
	}
	else {
		gBrowser.tabContainer.advanceSelectedTab(1, false);
	}    
	event.stopPropagation();
	event.preventDefault();
}

})();


 
Input Language Assistant
Это аналог старого расширения с таким же названием, и работает точно так же. Поставили курсор в адресную строку - раскладка переключилась на английскую, убрали оттуда курсор - раскладка вернулась к прежней (русской). Работает только на Windows.
Код

Выделить код

Код:

window.inputLanguageAssistant = {

	init: function() {

		let urlbar = document.getElementById('urlbar-input'); // Since FF 68-70 (I don't know exact version)
		if (!urlbar)
			urlbar = document.getElementById('urlbar');
		if (!urlbar)
			return;

		try {
			Components.utils.import("resource://gre/modules/ctypes.jsm");
			this.lib = ctypes.open("user32.dll");
			this.ActivateKeyboardLayout = this.lib.declare("ActivateKeyboardLayout",
														   ctypes.winapi_abi,
														   ctypes.voidptr_t,  // return HKL
														   ctypes.voidptr_t,  // HKL hkl
														   ctypes.uint32_t);  // UINT Flags
			this.KLF_SETFORPROCESS = 0x00000100;
			this.HKL_ENGLISH = ctypes.voidptr_t(0x00000409); // United States (US)
		}
		catch (err) {
			// console.log(err);
		}

		urlbar.addEventListener('focus', function(e) {
			window.inputLanguageAssistant.focus();
		});

		urlbar.addEventListener('blur', function(e) {
			window.inputLanguageAssistant.blur();
		});

	},

	focus: function() {
		try {
			if (this.ActivateKeyboardLayout) {
				this.hkl = this.ActivateKeyboardLayout(this.HKL_ENGLISH, this.KLF_SETFORPROCESS);
			}
		}
		catch (err) {
			// console.log(err);
		}
	},

	blur: function() {
		try {
			if (this.ActivateKeyboardLayout && this.hkl) {
				this.ActivateKeyboardLayout(this.hkl, this.KLF_SETFORPROCESS);
			}
		}
		catch (err) {
			// console.log(err);
		}
	}
};

window.inputLanguageAssistant.init();

Yeesha, спасибо!
А у вас подключено через userChrome.js или rebuild_userChrome.uc.js?
У меня на 68 есть загрузчик через userChrome.js, через него не работает. А на основных браузерах параллельно с UCF стоит загрузчик от xiaoxiaoflood, закладки и язык подхватились и работают, проверял на 68, 78 и 102. Жаль, закладки конфликтуют с кастомной кнопкой, изменяющей размер окошка, не знаю, кого из них оставить, и ваш хорош, и тот. А за Input Language Assistant особо спасибо, очень полезная фича. Хотя раньше у меня её не было, были две кнопки в доквантумном браузере, из которых я сделал одну - указание цветом в поле, какой язык сейчас включён, и индикатор на тулбаре. У вас случайно какой-нибудь из них нет под квантум?
Код для вкладок не проверял, мне такая фича неудобна.

kazarin, через userChrome.js, про rebuild не знаю

У вас случайно какой-нибудь из них нет под квантум?

Увы, нет

Yeesha
Через rebuild_userChrome.uc.js это у Ксяо, нужен этот файл и папка utils в chrome (я третий метод использую), и код в config.js. Скрипты просто кладутся рядом и сами подхватываются.

Попробовал поставить "новый" UCF. До кнопок пока не добрался, попробовал стили. Мои все заработали, кроме одного. В нём можно было регулировать ширину боковой и высоту доп.панелей. С "новым" UCF не работает или я его не туда воткнул.

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

Выделить код

Код:

/* Сжать доп.панели https://forum.mozilla-russia.org/viewtopic.php?pid=775867#p775867 */
#add-additional-top-bar,
#add-additional-bottom-bar {
    --toolbarbutton-outer-padding: 2px !important; /* было 0px */
    --toolbarbutton-inner-padding: 2px !important;
    min-height: 20px !important;
}
:-moz-any(#add-additional-top-bar,#add-additional-bottom-bar) .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}
#add-additional-bottom-closebutton {
    padding: 0 !important;
}
#add-additional-vertical-bar {
    --toolbarbutton-outer-padding: 2px !important; /* было 0px */
    --toolbarbutton-inner-padding: 3px !important;
    min-width: 20px !important;
}
#add-additional-vertical-bar .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}


Как ширину/высоту панелей отрегулировать в "новом" UCF?

xrun1
С нижней я сделал так:

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

Выделить код

Код:

#ucf-additional-bottom-bar {
      margin-bottom: -4px !important;
 }


Вертикальная меня устраивает (она прозрачная и автоскрывается), но, наверно, можно попробовать margin-left.

Опять в Firefox 103+ перестал работать скрипт от Виталия - нужно скрывать панель вкладок, если открыта одна вкладка.
Dumby - может починишь код menubarVisibilityChance.js или переделаешь, чтобы работало автоскрытие панели вкладок на FF90+ ?

Выделить код

Код:

var menubarvisibilitychance = {
	buttons: null,
	buttonsfullscreen: null,
	init(that) {
		var menubar = this.menubar = document.querySelector("#toolbar-menubar");
		if (!menubar) return;
		this.autohidechange = new MutationObserver(() => {
			this.settoolbarvisibility();
		});
		this.autohidechange.observe(menubar, {
			attributeFilter: ["autohide", "inactive"],
			attributes: true,
		});
		this.sizemodechange = new MutationObserver(() => {
			this.setbuttonboxwidth();
		});
		this.sizemodechange.observe(document.documentElement, {
			attributeFilter: ["sizemode"],
			attributes: true,
		});
		that.unloadlisteners?.push("menubarvisibilitychance");
		this.settoolbarvisibility();
	},
	settoolbarvisibility() {
		var docElm = document.documentElement;
		if (this.menubar.getAttribute("autohide") == "true" && this.menubar.getAttribute("inactive") == "true") {
			docElm.setAttribute("v_menubar_autohide", true);
			this.setbuttonboxwidth();
		} else
			docElm.setAttribute("v_menubar_autohide", false);
	},
	width(outerRect, innerRect) {
		if (!window.RTL_UI)
			this.width = (outerRect, innerRect) => outerRect.right - innerRect.left;
		else
			this.width = (outerRect, innerRect) => innerRect.right - outerRect.left;
		return this.width(outerRect, innerRect);
	},
	setbuttonboxwidth() {
		var buttons, docElm = document.documentElement;
		if (docElm.getAttribute("sizemode") != "fullscreen")
			buttons = (this.buttons || (this.buttons = this.menubar.querySelector(".titlebar-buttonbox-container")));
		else
			buttons = (this.buttonsfullscreen || (this.buttonsfullscreen = document.querySelector("#window-controls")));
		var innerRect = buttons.getBoundingClientRect();
		if (innerRect.width < 1) {
			docElm.style.setProperty("--v-titlebar-buttonbox-container-width", "0px");
			return;
		}
		var outerRect = docElm.getBoundingClientRect();
		docElm.style.setProperty("--v-titlebar-buttonbox-container-width", `${this.width(outerRect, innerRect)}px`);
	},
	destructor() {
		this.autohidechange.disconnect();
		this.sizemodechange.disconnect();
		this.autohidechange = null;
		this.sizemodechange = null;
	}
};
menubarvisibilitychance.init(this);
Dobrov пишет

Так нормально?

В комментарий прокралась опечатка: «слилей».

может починишь код menubarVisibilityChance.js или переделаешь, чтобы работало автоскрытие панели вкладок

Не понял просьбу. menubar это не панель вкладок.


xrun1 пишет

Как ширину/высоту панелей отрегулировать в "новом" UCF?

kazarin верно намекает, что в "новом" UCF
id тулбаров префиксятся с ucf- а не с add-

Dumby пишет

Не понял просьбу. menubar это не панель вкладок.

Ну, именно этот этот код автоскрывает панель вкладок, но на FF103+ не пашет.

Dobrov пишет

именно этот этот код автоскрывает панель вкладок

Нет, он этого не делает. Он вообще ничего не автоскрывает.


Код следит за атрибутами "autohide" и "inactive" toolbar-menubar'а,
и за атрибутом "sizemode" корневого элемента документа (<html>).


И, при изменении этих атрибутов, устанавливает для <html>
значение атрибута "v_menubar_autohide" и в его атрибуте "style"
устанавливает часть именуемую "--v-titlebar-buttonbox-container-width".


Вот и всё. Если у тебя какой-то стиль повешен на эти атрибуты, то где стиль?

Dumby пишет

при изменении этих атрибутов, устанавливает для <html>
значение атрибута "v_menubar_autohide" и в его атрибуте "style"
устанавливает часть именуемую "--v-titlebar-buttonbox-container-width".

Вот и всё. Если у тебя какой-то стиль повешен на эти атрибуты, то где стиль?

Так как этот стиль связан с кодом, привожу стиль от Виталия здесь:

Выделить код

Код:

@-moz-document url("chrome://browser/content/browser.xhtml") {
:root { /* вкладки снизу: стиль плюс скрипты https://forum.mozilla-russia.org/viewtopic.php?pid=784310#p784310 */
    --v-toolbar-menubar-height: 28px; /* высота панели меню, только чётные числа не меньше 20px для macos = 0
    если изменяете эту переменную то и в --v-toolbar-menubar-height-content - атрибут height= нужно установить ровно в два раза больше*/
    --v-toolbar-menubar-height-content: url("data:image/svg+xml,<svg width='28' height='56' xmlns='http://www.w3.org/2000/svg'><rect x='0' y='0' width='100%' height='100%' style='fill:transparent;'/></svg>");  /* height='2 * --v-toolbar-menubar-height' */
    --tab-border-radius: 4px !important;

    /* **************************************** */
    --proton-tab-block-margin: 0px !important;
    --tab-block-margin: 0px !important;
    --tabs-navbar-shadow-size: 0px !important;
}
#navigator-toolbox {
    border-block: none !important;
    box-shadow: none !important;
    padding-top: 0 !important;
    -moz-appearance: none !important;
    appearance: none !important;
}
:root:not([inFullscreen])[tabsintitlebar] #navigator-toolbox {
    position: relative !important;
}
#navigator-toolbox > toolbar {
    -moz-box-ordinal-group: 10 !important;
}
#navigator-toolbox > #nav-bar {
    margin-block: 0 !important;
    box-shadow: none !important;
    -moz-box-ordinal-group: 0 !important;
    padding-inline: 0 !important;
}
:root:not([inFullscreen]) #navigator-toolbox > #nav-bar {
    margin-top: var(--v-toolbar-menubar-height) !important;
}
#navigator-toolbox > #PersonalToolbar {
    -moz-box-ordinal-group: 1 !important;
}
:root:not([inFullscreen])[tabsintitlebar] > *|body::before {
    content: var(--v-toolbar-menubar-height-content) !important;
    display: -moz-box !important;
    -moz-box-flex: 0 !important;
    -moz-box-orient: vertical !important;
    -moz-box-pack: start !important;
    -moz-box-align: stretch !important;
    -moz-box-ordinal-group: 0 !important;
    margin-bottom: calc(-2 * var(--v-toolbar-menubar-height)) !important;
    box-sizing: content-box !important;
}
:root:not([inFullscreen])[tabsintitlebar][sizemode="normal"] > *|body::before {
    -moz-appearance: -moz-window-titlebar !important;
    appearance: -moz-window-titlebar !important;
}
:root:not([inFullscreen])[tabsintitlebar][sizemode="maximized"] > *|body::before {
    -moz-appearance: -moz-window-titlebar-maximized !important;
    appearance: -moz-window-titlebar-maximized !important;
}
:root:not([inFullscreen])[tabsintitlebar]:-moz-lwtheme > *|body::before {
    visibility: hidden !important;
}
@media not all and (-moz-os-version: windows-win7) {
    @media not all and (-moz-os-version: windows-win8) {
:root:-moz-lwtheme {
    background-color: var(--lwt-accent-color, -moz-Dialog) !important;
}
:root:-moz-window-inactive:-moz-lwtheme {
    background-color: var(--lwt-accent-color-inactive, var(--lwt-accent-color, -moz-Dialog)) !important;
}
    }
}
#navigator-toolbox > #titlebar {
    -moz-appearance: none !important;
    appearance: none !important;
    -moz-box-ordinal-group: 100 !important;
    position: static !important;
}
#toolbar-menubar {
    padding-block: 0 !important;
    margin-block: 0 !important;
    border: none !important;
    background: none !important;
    --toolbarbutton-outer-padding: 0px !important;
    --toolbarbutton-inner-padding: calc((var(--v-toolbar-menubar-height) - 16px) / 2) !important;
    -moz-appearance: none !important;
    appearance: none !important;
}
:root:not(:is([inFullscreen],[chromehidden~="menubar"])) #toolbar-menubar {
    position: absolute !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    display: flex !important;
    flex-wrap: nowrap !important;
    flex-direction: row !important;
    align-items: stretch !important;
    justify-content: flex-start !important;
    min-height: 0 !important;
    height: var(--v-toolbar-menubar-height) !important;
    overflow: hidden !important;
}
#toolbar-menubar .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}
:root:not([inFullscreen]) #toolbar-menubar > :is(toolbaritem,toolbarbutton) {
    align-self: center !important;
}
:root:not([inFullscreen]) #toolbar-menubar > :is(#menubar-items,#wrapper-menubar-items,.titlebar-buttonbox-container) {
    align-self: flex-start !important;
}
:root:not([inFullscreen]) #toolbar-menubar > * {
    padding-block: 0 !important;
    margin-block: 0 !important;
}
:root:not([inFullscreen]) #toolbar-menubar[autohide="true"][inactive="true"]:not([customizing="true"]) > *:not(.titlebar-buttonbox-container) {
    opacity: 0 !important;
    pointer-events: none !important;
}
:root[inFullscreen] #toolbar-menubar {
    visibility: collapse !important;
}
:root:not([inFullscreen]) #toolbar-menubar > :is(*[style*="-moz-box-ordinal-group: 1000;"],.titlebar-buttonbox-container) {
    order: 1000 !important;
}
:root:not([inFullscreen]) #toolbar-menubar > :is(toolbarspring,spacer,[id^="wrapper-customizableui-special-spring"]) {
    flex-grow: 1 !important;
}
:root:not([inFullscreen]) #toolbar-menubar > :is(#search-container,#wrapper-search-container) {
    flex-grow: 100 !important;
}
#toolbar-menubar #search-container {
    padding-block: 0 !important;
}
#toolbar-menubar #searchbar {
    min-height: calc(var(--v-toolbar-menubar-height) - 2px) !important;
}
#TabsToolbar {
    -moz-appearance: none !important;
    appearance: none !important;
    padding-block: 0 !important;
    margin-block: 0 !important;
    box-shadow: 0 -1px 0 var(--tabs-border-color, rgba(0,0,0,.3)) inset !important;
    position: static !important;
    background-color: var(--toolbar-bgcolor, -moz-dialog) !important;
    background-image: var(--toolbar-bgimage, none) !important;
    color: var(--toolbar-color, -moz-dialogtext) !important;
    --lwt-toolbarbutton-icon-fill: inherit !important;
}
#tabbrowser-tabs {
    padding-bottom: 0 !important;
    margin-bottom: 0 !important;
}
.tabbrowser-tab {
    background-color: transparent !important;
    border-top: none !important;
}
.tab-background {
    border-end-end-radius: 0 !important;
    border-end-start-radius: 0 !important;
    margin-block-start: 1px !important;
}
:root:not(:-moz-lwtheme) .tab-background {
    --toolbar-bgimage: none;
    --toolbar-non-lwt-bgimage: none;
}
#TabsToolbar > .toolbar-items {
    padding-top: 0 !important;
    margin-top: 0 !important;
}
.tabbrowser-tab[usercontextid] .tab-context-line {
    margin-block: 0 !important;
    margin-inline: calc(var(--tab-border-radius) / 2) !important;
}
#TabsToolbar .titlebar-buttonbox-container,
#navigator-toolbox::after,
#TabsToolbar::after,
#TabsToolbar .titlebar-spacer:is([type="pre-tabs"],[type="post-tabs"]) {
    display: none !important;
}
#scrollbutton-up, #scrollbutton-down {
    border-block: none !important;
    border-end-end-radius: 0 !important;
    border-end-start-radius: 0 !important;
}
:root[inFullscreen] #window-controls {
    position: absolute !important;
    display: flex !important;
    align-items: start !important;
    top: 0 !important;
    inset-inline-start: auto !important;
    inset-inline-end: 0 !important;
    margin: 0 !important;
}
:root[inFullscreen] #navigator-toolbox > #nav-bar {
    margin-inline-end: var(--v-titlebar-buttonbox-container-width, 108px) !important;
}
:root:not([inFullscreen])[v_menubar_autohide="true"] #navigator-toolbox > #nav-bar {
    margin-top: 0 !important;
    margin-inline-end: var(--v-titlebar-buttonbox-container-width, 108px) !important;
}
:root:not([inFullscreen])[v_menubar_autohide="true"] #toolbar-menubar {
    pointer-events: none !important;
}
:root:not([inFullscreen])[v_menubar_autohide="true"] #toolbar-menubar .titlebar-buttonbox-container {
    pointer-events: auto !important;
}
@media (-moz-os-version: windows-win7), (-moz-os-version: windows-win8) {
:root[sizemode="normal"] #TabsToolbar {
    border-inline: 1px solid hsla(240,5%,5%,0.3) !important;
    background-clip: padding-box !important;
}
    @media (-moz-windows-classic: 0) {
:root:not([inFullscreen])[v_menubar_autohide="true"][tabsintitlebar][sizemode="normal"] #navigator-toolbox > #nav-bar {
    margin-top: 1px !important;
}
:root:not([inFullscreen])[sizemode="normal"] #toolbar-menubar:not([autohide="true"]) > #menubar-items {
    margin-top: 1px !important;
}
    }
}
@media (-moz-windows-classic) {
:root:not([inFullscreen])[tabsintitlebar][sizemode="normal"] #navigator-toolbox::before {
    content: "" !important;
    display: -moz-box !important;
    height: 4px !important;
    -moz-box-ordinal-group: 0 !important;
    visibility: visible !important;
}
:root:not([inFullscreen])[tabsintitlebar][sizemode="normal"]:-moz-lwtheme #navigator-toolbox::before {
    background-image: linear-gradient(to bottom, ThreeDLightShadow 0, ThreeDLightShadow 1px, ThreeDHighlight 1px, ThreeDHighlight 2px, ActiveBorder 2px, ActiveBorder 4px, transparent 4px) !important;
}
:root:not([inFullscreen])[tabsintitlebar][sizemode="normal"] #toolbar-menubar {
    margin-top: 4px !important;
}
}
:root[data-l10n-id="browser-main-window-mac"] {
    --v-toolbar-menubar-height: 0px !important;
    --v-toolbar-menubar-height-content: none !important;
}
:root:not([inFullscreen])[tabsintitlebar][data-l10n-id="browser-main-window-mac"] #nav-bar {
    margin-inline-start: calc(var(--toolbarbutton-outer-padding, 2px) + var(--v-titlebar-button-horizont-padding, 6px) * 6 + var(--v-titlebar-button-image-width, 12px) * 3) !important;
}
:root:not([inFullscreen])[tabsintitlebar][data-l10n-id="browser-main-window-mac"] #TabsToolbar .titlebar-buttonbox-container {
    visibility: visible !important;
    display: -moz-box !important;
    position: absolute !important;
    display: flex !important;
    top: 0 !important;
}
}
Dobrov пишет

привожу стиль

Ну и где там хоть что-то про автоскрытие панели вкладок (TabsToolbar)?

Dumby -  Я не уверен, но автоскрытие панели вкладок от Виталия есть в профиле profile_ucf_dobrov, но перестало работать в новых FF.

Выделить код

Код:

:root:not([inFullscreen])[v_menubar_autohide="true"] #navigator-toolbox > #nav-bar {
    margin-top: 0 !important;
    margin-inline-end: var(--v-titlebar-buttonbox-container-width, 108px) !important;
}

Dobrov
autohide_tabstoolbar
Оффтоп

Dobrov пишет

есть в профиле profile_ucf_dobrov

Если ты про этот код, то его подкосил баг 1767802.

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

Выделить код

Код:

/*
			.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabs-newtab-button {
*/
			.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabs-newtab-button,
			.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button {

Dumby - спасибо, заработало! :beer: Исправил также этот ucf_autohidetabstoolbar.js в шапке.

Dumby, подскажите, что можно изменить в скрипте Simple Session Manager, чтобы заработало в FF68 и 78..?
В 68 после замены btn.type = "menu"; на btn.setAttribute("type", "menu"); и правки @-moz-document url-prefix(chrome://browser/content/browser.xul) кнопка появляется, по щелчку ЛКМ предлагает сохранить сессию, но не сохраняет. В 78 кнопка появляется без ковыряния в коде, но тоже не работает.

LGS
Скрипты у Dobrov-а  работают с перехватом кликов hookClicks, его нужно добавить в CustomStylesScripts.jsm, иначе ни один из этих скриптов работать не будет.
Это для 78. А в 68 новые UCF, к сожалению, не работают.

kazarin
Здесь, вроде, родной от Dumby, но все равно не работает.

LGS пишет

В 68 после замены btn.type = "menu"; на btn.setAttribute("type", "menu"); и правки @-moz-document url-prefix(chrome://browser/content/browser.xul) кнопка появляется, по щелчку ЛКМ предлагает сохранить сессию, но не сохраняет.

Что-то я не понял, там же четыре «?.» оператора, которые надо выгребать,
а об этом ни слова. Ладно, будем считать что сделал, но не написал.


Патч на сохранение. Только это, я не буду там всё тестировать,
а если ещё что-то всплывёт, то пиши доходчивый STR, попробую посмотреть.

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

Выделить код

Код:

/*
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils;
*/
	io: {
		get OS() {
			delete this.OS;
			Cu.import("resource://gre/modules/osfile.jsm", this);
			return this.OS;
		},
		makeDirectory(path) {
			return (this.makeDirectory = this.OS.File.makeDir)(path);
		},
		writeJSON(path, obj) {
			var wa = this.OS.File.writeAtomic;
			return (this.writeJSON = (path, obj) => wa(path, JSON.stringify(obj)))(path, obj);
		}
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils || this.io;

LGS
Оказывается, SSM не работает и в профиле Dobrov-а (тестовый профиль для [firefox] 78 от 12.04.2022). Есть кнопка, но ничего не сохраняется, а через клик выскакивает сообщение:

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

(окно одно, в профиле всё as is, ничего не менялось)
Проверил отдельный скрипт из шапки. Отлично работает на 102. На 68 на старых UCF ожидаемо не завёлся. На 78 - есть кнопка, подхватывает .json от CB, но не сохраняет, не переименовывает и не удаляет сессии. Покопался немного по форуму и нашёл следующее.
То есть этот скрипт, по-видимому, ниже 91 работать и не будет, а более ранние версии существуют только в виде кастомной кнопки.


Dumby
На 78 фикс не помог, по-прежнему не сохраняет.

kazarin пишет

фикс не помог

Точно. Это я глупость написал. Вторая попытка.

Dumby

Dumby пишет

Патч на сохранение

На 78 (esr и Вин10 x64) заработало все: сохранение, восстановление, удаление, переименование и даже иконки у менюшек.
На 68 (форке и ВинХР) заработало сохранение и восстановление. Спасибо!

68esr и Вин10 x64 кнопка не создается, наверное что-то локальное у меня.

Dumby пишет

Только это, я не буду там всё тестировать,
а если ещё что-то всплывёт, то пиши доходчивый STR, попробую посмотреть.

Конечно, сам погоняю, посмотрю что всплывет. Только мне стыдно спросить - что такое STR..?

Add: Оказывается, работало (попытка 1) до перезагрузки компа. Теперь проверяю "попытку 2".

kazarin

kazarin пишет

этот скрипт, по-видимому, ниже 91 работать и не будет

На 78 попытка 2 тоже работает, как и первая. Только не знаю как будет после перезагрузки компьютера.
На 68esr кнопка не создается, на Еноте68 работает сохранение и восстановление.

Dumby
"Вторая попытка" работает и на 78, и на 68! Большое спасибо!))

LGS
В 68 нет иконки на функции удаления, потому что самой такой картинки в браузере нет, вы там пропишите своё что-нибудь))
Нет, есть, но почему-то не отображается.
От перезагрузки компа кнопки не зависят, только от очистки кэша скриптов.
А вот на 68 esr не проверял, счаз гляну.

Да, у меня тоже не появляется в 68. А в Еноте есть))

kazarin

kazarin пишет

От перезагрузки компа кнопки не зависят, только от очистки кэша скриптов

Значит кэш не очистился. После правки "попытки 1" браузер (78) однозначно перезагружался с кнопки очистки кэша и все работало. После перезагрузки компа (нужно было на ВинХР енот проверить) все слетело.

LGS пишет

На 68esr кнопка не создается, на Еноте68 работает

Конечно не создаётся. Оператор «?.» это не какая-то там ошибка в работе кода,
это сразу SyntaxError, весь код встанет враскоряку.


Уж не знаю что за Енот68, но значит там поддержка этого оператора есть.

Только мне стыдно спросить - что такое STR..?

Steps To Reproduce (шаги по воспроизводству),
типа делай раз, делай два, делай три. Багзильский такой формат.


Пример. Допустим, обсуждаемый код работает.


STR: Запускаем Firefox 78. Инициируем в кнопке сохранение сессии.
В открывшемся окошке ввода имени вызываем контекстное меню (ПКМ) на строке «Сохранить:».
Жмём пункт «Выделить всё».


AR: Выделение не происходит.
ER: Выделение происходит.


Ну, это баг такой в лисе, можно и в простом alert'е увидеть.
Воспроизводится и в 106.0a1

Dumby

Dumby пишет

Steps To Reproduce (шаги по воспроизводству)

Благодарю за науку и помощь со скриптами!

Dumby пишет

Уж не знаю что за Енот68

Это MyPal, человек его на базе 68esr делает для ХР и уже, вроде, кое-что от 78 туда впихнул. Поэтому скрипт и работает.

LGS
Ну, то есть, в изначальном вопросе «В 68 после замены» «и правки» это был енот.
У меня нет никакого енота, а сохранение надо было смотреть, поэтому,
на скорую руку, чисто формально, запихал код в Firefox 68 так

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

Выделить код

Код:

....
		//if (old != trg) old?.removeAttribute("boot");
		if (old != trg) old && old.removeAttribute("boot");
....
		//if (arg.constructor.isInstance?.(arg)) {
		if (arg.constructor.isInstance && arg.constructor.isInstance(arg)) {
....
		//win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
		var menu = win.document.querySelector(this.skd);
		menu && menu.removeAttribute("maxwidth");
....
			//: this.dragData?.mouse && e.preventDefault()
			: this.dragData && this.dragData.mouse && e.preventDefault()

Dumby
Проверил, в 68 работает. Спасибо!:)

LGS
Исправил иконку так

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

Выделить код

Код:

#${pid} [value=removeSession] {
					list-style-image: url("resource://usercontext-content/cart.svg");
				} 
				
				#${pid} [value=deleteAllSessions] {
					list-style-image: url("resource://usercontext-content/cart.svg");
				}


Разнёс на две, 68, видимо, не понимает перечисления, что обычный esr, что енот.

Dumby

Dumby пишет

Ну, то есть, в изначальном вопросе «В 68 после замены» «и правки» это был енот.

Да, извиняюсь, не совсем точно сформулировал. Думал, что ESR не так важно. А про енота вы и так не знали.

kazarin пишет

на скорую руку, чисто формально, запихал код в Firefox 68

На 68esr заработало: сохраняет, восстанавливает, переименовывает. Только не удаляет.
Благодарю!

18-09-2022 00:47:20
kazarin

kazarin пишет

Исправил иконку так

Спасибо, теперь совсем красиво.

Dumby - спасибо за адаптацию ucf_SessionManager!
Как сделать прокрутку вкладок колёсиком мыши ? (аналогично Ctrl+Page Up|Down)
твой код работает, только если его запустить из консоли. Разные способы запуска из CustomStylesScripts.jsm не пашут:

Выделить код

Код:

(async (tc) => addEventListener("wheel", e => {
	if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)
		return;
	e.stopPropagation(); e.preventDefault();
	tc.advanceSelectedTab(e.deltaY < 0 ? 1 : -1, true);
}, true, tc || 1))(gBrowser.tabContainer);
kazarin пишет

Скрипты у Dobrov-а  работают с перехватом кликов hookClicks, его нужно добавить в CustomStylesScripts.jsm, иначе ни один "из этих" скриптов работать не будет.

LGS спрашивал про ucf_SessionManager.js, которому не нужен ucf_hookClicks. "Из этих" зависят только attrsInspector.js, ucf_mousedrag.js, ucf_QuickToggle.js.
Но глобальный скрипт значительно упрощает код других кнопок и добавляет много функций, подсказки, клики, перехват при наведении мыши на кнопки/панели. Например, расширяет возможности кнопок Загрузки, Печать, PanelUI-menu, Замок и многих других, в том числе можно добавить горячие клавиши и расширить клики/подсказки любых дополнений, как это сделано для Single Save, VideoDownloadHelper, ReaderView.

Dobrov пишет

Разные способы запуска из CustomStylesScripts.jsm не пашут

Что там может не пахать

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

Выделить код

Код:

(async tc => {
	var args = ["wheel", e => {
		if (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey)
			return;
		e.stopPropagation(); e.preventDefault();
		tc.advanceSelectedTab(e.deltaY < 0 ? 1 : -1, true);
	}, true];
	tc.addEventListener(...args);
	var id = Symbol();
	this.unloadlisteners.push(id);
	this[id] = {destructor: () => tc.removeEventListener(...args)};
})(gBrowser.tabContainer);
Выделить код

Код:

var UcfStylesScripts = {
    .......
    scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию "DOMContentLoaded"

        ],
        load: [ // По событию "load"
            .......
            { path: "advanceSelectedTab.js", ucfobj: true },
        ],
    },

Dobrov пишет

LGS спрашивал про ucf_SessionManager.js, которому не нужен ucf_hookClicks

Да мы уже разобрались)) Просто моей первой мыслью, раз иконка есть, а скрипт не пашет, было, что взят скрипт, который работает с перехватом.

Dobrov пишет

добавляет много функций, подсказки, клики, перехват при наведении мыши на кнопки/панели

А неужели так много там экономится, чтоб он был так уж нужен?
Мне кажется, таки hookClicks это для мастеров. Которые сами умеют писать скрипты, и им так удобнее. Мне вот как простому юзеру hookClicks потенциально не нравится тем, что если вдруг UCF перестанут работать на FF, то их уже по-другому не подключишь (hookClicks работает только с UCF, я проверял). Скрипты для UCF не всегда дружат с другими загрузчиками, но большая часть - да.
Ну и порой бывает, что сам скрипт может работать на более низкой версии FF со старым UCF, а без hookClicks он не заработает.
Кстати, а вы не можете пояснить, почему на 78 выскакивает вот такое сообщение? В профиле ничего не менял, просто запустил. На самом свежем профиле тоже выскакивает.

kazarin пишет

неужели так много там экономится, чтоб "ucf_hookClicks.js" был так уж нужен?

Да, например отдельно в кнопке меню ucf_QuickToggle.js такое очень сложно сделать, а используя hookClicks всё намного проще:
Спасибо Dumby - код обработки предельно прост: 2(trg, forward) { bright(trg, forward);}, // яркость роликом мыши на кнопке ±
1) клики трёх кнопок мыши, б) их долгое нажатие с) прокрутка колёсика мыши д) + комбинации с Ctrl, Alt, Shift, Win
2) различные действия, в зависимости, где событие мыши: на самой кнопке или в её меню опций
3) различные подсказки, обновляемые при наведении курсора мыши на кнопку или разные пункты меню и подменю
4) сообщения в строке статуса при наведении на кнопку или при изменении режимов работы браузера
5) цвет кнопки в зависимости от режима прокси и многое другое (скрипт подробно комментирован)
6) всё работает на любых панелях и кнопках, не нужно в каждую кнопку прописывать клики, подсказки и прочее...
в обычном скрипте для обработки клика левой кнопки мыши нужна не одна строка, а больше. Если добавить правую кнопку и колёсико, код значительно усложняется.


kazarin пишет

почему на 78 выскакивает вот такое сообщение? В профиле ничего не менял, просто запустил. На самом свежем профиле тоже выскакивает.

проверил, получается, нужен Firefox 84+, на нём всё ОК, на FF78 получил ошибку создания массива:
hmap = new Map([ ["downloads-button", // тексты: кнопка «Загрузки» (в firefox_profile_ucf_dobrov.html указано: рекомендуется Firefox 90+)

Dobrov, спасибо, понятно. Ну в общем это облегчает работу для скриптодела))

Dobrov пишет

рекомендуется Firefox 90+

Да это понятно, интересна была работа на 78.

Подскажите, кто знает... или как думает, можно ли этот скрипт, или этот, или вот такой скрипт+стиль впихнуть в заголовок фокса..?  Ну, т.е. чтобы версия, дата и время отображались в заголовке, а не в панели меню. Или такое технически невозможно..?
Вопрос актуален для всех версий ФФ с 68 по текущую релизную.

Dumby
Сделайте пожалуйста скрипт для авто... открытия панели загрузок при старте закачки.

kokoss
В config.js
lockPref("browser.download.panel.shown", false);
и в about:config
browser.download.alwaysOpenPanel    = true
уже не работает?

Dumby
Нарисовалась хотелка для этого скрипта, тот что под спойлером.
Пишу для истории - скрипт при поиске создает пункт "Показать в папке" в контекстном меню закладок, и в библиотеке, и в закладках сайдбара.
Но проблема в том, что после срабатывания и последующего переоткрытия сайдбара (закрыть и снова открыть) положение поиска сбрасывается. Тоже самое и с нативным пунктом, вот здесь об этом.
   
А вот у этого скрипта такой проблемы нет. Но к сожалению он не работает в библиотеке и не прячет свой пункт когда нет поиска, т.е. пункт торчит всегда.
   
Не могли бы вы свой скрипт изменить так, что бы он вел себя в сайдбаре аналогично скрипту alice0775, т.е. не сбрасывал положение поиска после переоткрытия сайдбара?

kokoss пишет

Сделайте пожалуйста скрипт для авто... открытия панели загрузок при старте закачки.

Хорошо, попробую. Это в custom_script.js

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

Выделить код

Код:

Services.obs.addObserver(function brw(win, topic) {
	Services.obs.removeObserver(brw, topic);

	var {Downloads: d, BrowserWindowTracker: bwt} = win;
	var show = function(download) {
		download.newDownloadNotified ||
		bwt.getTopWindow(this)?.DownloadsPanel.showPanel();
	};
	["PUBLIC", "PRIVATE"].forEach(async (type, ind) => {
		var view = Object.create(null);
		view.private = Boolean(ind);
		view.onDownloadChanged = show;
		(await d.getList(d[type])).addView(view);
	});
}, "browser-delayed-startup-finished");

_zt пишет

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

«положение поиска» это какие папки развёрнуты?
Если так, то можно попробовать запоминать принудительно.

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

Выделить код

Код:

/*
			tree.scrollByLines(Math.round(newFirst - first));
		}
*/
			tree.scrollByLines(Math.round(newFirst - first));
			this.persist(tree.view);
		},
		persist(view) {
			var du = document.documentURI, xs = Services.xulStore;
			var obf = PlacesUIUtils.obfuscateUrlForXulStore, set = obf
				? u => xs.setValue(du, obf(u), "open", "true")
				: u => xs.setValue(du, u, "open", "true");
			(this.persist = view => {
				for(var node of view._rows) node.containerOpen && set(node.uri)
			})(view);
		}

_zt, voqabuhe
По совету умных товарищей "переполз" со "старого" UCF на "новый". Вроде, все стили и кнопки подключились, но, как оказалось, рано радовался - скрипт SidebarBookmarkSearchOpenFolder.uc.js в этом "новом" у меня не работает. Судя по постам, скрипт лучше штатной возможности "Показать в папке" (#placesContext_showInFolder).
Скрипт у меня в "старом" варианте подключался по какой-то хитрой схеме. Куда его втыкать теперь? В CustomStylesScripts.jsm этот вариант не работает.

Выделить код

Код:

load: [ // По событию "load"
            { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "cs_win/SidebarBookmarkSearchOpenFolder.uc.js", ucfobj: false, },
_zt пишет

В config.js
lockPref("browser.download.panel.shown", false);

Уже нет, при чём эта настройка уже не помню с какой версии перестала работать, как и настройка > browser.download.improvements_to_download_panel = false, отвалилась после обновления на [firefox] 105.
add, этот способ тоже не работает > lockPref("browser.download.panel.shown", false); + browser.download.alwaysOpenPanel  = true


Dumby пишет

Это в custom_script.js
скрытый текст

Благодарю!!!

xrun1 пишет

Special Widgets

Special Widgets грузится в объект ucf_custom_script_win
окна документа браузера (browser.xhtml).


А закладки в сайдбаре — это другое окно (bookmarksSidebar.xhtml).
Надо прописывать в UcfStylesScripts.scriptsallchrome


Опусти глаза чуть пониже. Пример для Библиотеки видишь?
// { path: "example_places.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/, ucfobj: false, },


Вот туда. Типа
{ path: "cs_win/SidebarBookmarkSearchOpenFolder.uc.js", urlregxp: /chrome:\/\/browser\/content\/places\/bookmarksSidebar\.xhtml/ },

Dumby
Спасибо, все ок.
   
xrun1
В         load: [ // По событию "load"
            { path: "cs_win/SidebarBookmarkSearchOpenFolder.uc.js", urlregxp: /chrome:\/\/browser\/content\/places\/bookmarksSidebar\.xhtml/, ucfobj: false, },
на перезагрузку с очисткой кэша особо не надейтесь. Делайте ее, закрывайте браузер и проверяйте отсутствие / удаляйте startupCache.
   
Только вот зачем вам этот обрубок? Только что же обсуждали.

ShowBookmarkFolder_ucf.js

Выделить код

Код:

// Пункт контекстного меню закладок "Показать в папке", от Dumby,
// работает во всех документах - окно, вкладка, сайдбар.
// Появляется при не пустой строке поиска.
// https://forum.mozilla-russia.org/viewtopic.php?pid=799551#p799551
// + https://forum.mozilla-russia.org/viewtopic.php?pid=801732#p801732

try {({
	run(func) {
		var topics = ["quit-application-granted", "chrome-document-loaded"];
		var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
		for(var t of topics) obs.addObserver(this, t, false);
		this.observe = (subj, topic) => this[topic[0]](subj);
		this.q = () => topics.forEach(t => obs.removeObserver(this, t));

		this.run = async doc => {
			var code = `(${func})(document.getElementById("placesContext_editSeparator"));`;
			var ps = await doc.ownerGlobal.ChromeUtils
				.compileScript("data:charset=utf-8," + encodeURIComponent(code));
			(this.run = ps.executeInGlobal.bind(ps))(doc);
		}
		var re = /\/(?:places|bookmarksSidebar)\.xhtml$/;
		this.c = doc => re.test(doc.documentURI) && this.run(doc);
	}
}).run(sep => {
	var label = "Показать в папке";
	var popup = sep.parentNode, listener = {
		handleEvent() {
			if (this.shouldHide) return;

			var menuitem = document.createXULElement("menuitem");
			menuitem.setAttribute("label", label);
			menuitem.setAttribute("oncommand", "creator.goParentFolder();");
			menuitem.creator = this;
			sep.before(menuitem);

			this.handleEvent = e => {
				if (e.target != popup) return;
				var sh = this.shouldHide;
				if (Boolean(menuitem.clientHeight) ^ sh) return;
				if ((menuitem.hidden = sh)) return;
				menuitem.disabled = false;
			}
		},
		get shouldHide() {
			var node = popup._view.selectedNodes.length == 1
				&& popup._view.selectedNode;
			return !(node && PlacesUtils.nodeIsBookmark(node)
				&& node.parent.type == node.RESULT_TYPE_QUERY);
		},
		get goParentFolder() {
			var tree = popup._view;
			if (tree.id.startsWith("b")) {
				delete this.library;
				var func = () => this.sidebar(tree);
			} else {
				delete this.sidebar;
				var list = document.getElementById("placesList");
				var func = () => this.library(popup._view, list);
			}
			delete this.goParentFolder;
			return this.goParentFolder = func;
		},
		sidebar(tree) {
			var {bookmarkGuid} = tree.selectedNode;
			if (tree.result.root.uri.startsWith("place:terms="))
				tree.place = tree.place;
			tree.selectItems([bookmarkGuid]);
			this.scroll(tree);
		},
		async library(tree, list) {
			var {bookmarkGuid} = tree.selectedNode;
			var {parentGuid} = await PlacesUtils.bookmarks.fetch(bookmarkGuid);

			if (PlacesUtils.getConcreteItemGuid(list.selectedNode) == parentGuid)
				list.selectItems([PlacesUtils.virtualAllBookmarksGuid]);
			else {
				var rows = list.view._rows, lastRow = rows[rows.length - 1];
				if (lastRow.bookmarkGuid == PlacesUtils.virtualAllBookmarksGuid)
					lastRow.containerOpen = true;
			}
			list.selectItems([parentGuid]);
			this.scroll(list);

			tree.selectItems([bookmarkGuid]);
			await new Promise(requestAnimationFrame);
			this.scroll(tree);
		},
		scroll(tree) {
			var pos = .4, visibleRows = tree.getPageLength();
			var ind = tree.view.selection.currentIndex;
			var first = tree.getFirstVisibleRow();
			var newFirst = ind - pos*visibleRows + 1;
//			tree.scrollByLines(Math.round(newFirst - first));
//		}
			tree.scrollByLines(Math.round(newFirst - first));
			this.persist(tree.view);
		},
		persist(view) {
			var du = document.documentURI, xs = Services.xulStore;
			var obf = PlacesUIUtils.obfuscateUrlForXulStore, set = obf
				? u => xs.setValue(du, obf(u), "open", "true")
				: u => xs.setValue(du, u, "open", "true");
			(this.persist = view => {
				for(var node of view._rows) node.containerOpen && set(node.uri)
			})(view);
		}
// <<<
	};
	popup.addEventListener("popupshowing", listener);
	addEventListener("unload", () =>
		popup.removeEventListener("popupshowing", listener)
	, {once: true});
});} catch(ex) {Cu.reportError(ex);}


Подключается или в custom_script или в CustomStylesScripts.jsm в секцию scriptsbackground: [ // В фоне [System Principal]
        { path: "cs/ShowBookmarkFolder_ucf.js", },
   
   
25-09-2022 16:54:17
Dumby
А можете еще переместить пункт в самый верх меню и добавить после него сепаратор?

Подскажите по такому вопросу:
    при запуске FF открывается домашняя страница, а курсор (фокус) устанавливается в адресную строку и она выделяется голубенькой рамкой
    лично по мне логично было бы установить его в строку поиска, которая посередине экрана и именно она бы подсвечивалась

скрытый текст
fffnew.jpg

Как подсказывают, стилем нельзя.
    Как это сделать с помощью скрипта?

Dumby
Спасибо, за разъяснение и работает.

_zt пишет

Только вот зачем вам этот обрубок?

Если бы загрузки тоже отображались в Sidebar'е, окно библиотек мне вообще не нужно.

xrun1 пишет

Если бы загрузки тоже отображались в Sidebar'е, окно библиотек мне вообще не нужно.

Может ещё работает в актуальной версии > https://forum.mozilla-russia.org/viewto … 24#p784824

_zt пишет

А можете еще переместить пункт в самый верх меню и добавить после него сепаратор?

Не исключено. Замена начала кода

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

Выделить код

Код:

try {({
	run(func) {
		var topics = ["quit-application-granted", "chrome-document-loaded"];
		var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
		for(var t of topics) obs.addObserver(this, t, false);
		this.observe = (subj, topic) => this[topic[0]](subj);
		this.q = () => topics.forEach(t => obs.removeObserver(this, t));

		this.run = async doc => {
			var code = `(${func})(document.getElementById("placesContext"));`;
			var ps = await doc.ownerGlobal.ChromeUtils
				.compileScript("data:charset=utf-8," + encodeURIComponent(code));
			(this.run = ps.executeInGlobal.bind(ps))(doc);
		}
		var re = /\/(?:places|bookmarksSidebar)\.xhtml$/;
		this.c = doc => re.test(doc.documentURI) && this.run(doc);
	}
}).run(popup => {
	var label = "Показать в папке", listener = {
		handleEvent() {
			if (this.shouldHide) return;

			var menuitem = document.createXULElement("menuitem");
			menuitem.setAttribute("label", label);
			menuitem.setAttribute("oncommand", "creator.goParentFolder();");
			menuitem.creator = this;

			var sep = document.createXULElement("menuseparator");
			popup.prepend(menuitem, sep);

			this.handleEvent = e => {
				if (e.target != popup) return;
				var sh = this.shouldHide;
				if (!(
					Boolean(menuitem.clientHeight) ^ sh
					|| (menuitem.hidden = sep.hidden = sh)
				))
					menuitem.disabled = false;
			}
		},

Inko7 пишет

Как подсказывают, стилем нельзя.
    Как это сделать с помощью скрипта?

Ясен пень нельзя. Стили не занимаются отслеживанием запуска и перемещением фокуса.
Вот, весьма криво, но, возможно, сойдёт (это код для custom_script.js).

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

Выделить код

Код:

Services.obs.addObserver(function wr(win, topic) {
	Services.obs.removeObserver(wr, topic);
	var re = /^about:(?:newtab|home)$/;
	var fs = 'data:,content.document.getElementById("newtab-search-text").focus();';
	Array.from(CustomizableUI.windows).forEach(async win => {
		await win.gBrowserInit.firstContentWindowPaintPromise;
		var br = win.gBrowser.selectedBrowser;
		if (re.test(br.currentURI.spec))
			br.focus(),
			br.messageManager.loadFrameScript(fs, false);
	});
}, "sessionstore-windows-restored");

kokoss
Я помню эту штуку, мне не понравилось.

Dumby
Спасибо. Теперь вообще замечательно.
   

kokoss пишет

этот способ тоже не работает > lockPref("browser.download.panel.shown", false); + browser.download.alwaysOpenPanel  = true

Работает, только что проверил на 105.
   

kokoss пишет

Может ещё работает в актуальной версии > https://forum.mozilla-russia.org/viewtopic.php?pid=784824#p784824

Работает.

_zt пишет

Работает, только что проверил на 105.

А у меня не работает! Работает, если в настройках включена опция"Сохранять файлы", что для меня не вариант, о чём я сообщил здесь > https://forum.mozilla-russia.org/viewtopic.php?pid=801691#p801691

Интересно, что Sidebar Tabs у меня не установлен, о чём сказал выше. А вот кнопка для него стоит, как раз для загрузок (спойлер в п.2). Запасливый, однако!:D

kokoss
Вы правы.
lockPref("browser.download.panel.shown", false); + browser.download.alwaysOpenPanel = true
это "Показывать панель загрузок при окончании загрузки", а вам надо "при старте".

Dumby

Dumby пишет

Вот, весьма криво, но, возможно, сойдёт (это код для custom_script.js).

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

Inko7 пишет

но

Это что ещё за «но»?

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

Выделить код

Код:

data:image/png;base64,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

Dumby
понял, осознал :)
возможно ли расширить функционал кода для вариантов открытия новой вкладки (плюсиком) или домашней страницы (домиком) ?

Inko7 пишет

возможно ли расширить функционал кода для вариантов открытия новой вкладки (плюсиком) или домашней страницы (домиком) ?

Даже не знаю, может подойдёт вариант "вообще всегда"?

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

Выделить код

Код:

(async fsu => {
	var beg = "resource:///actors/AboutNewTabParent.";
	try {var exp = ChromeUtils.importESModule(beg + "sys.mjs");}
	catch {exp = ChromeUtils.import(beg + "jsm");}
	var proto = exp.AboutNewTabParent.prototype;

	var rm = proto.receiveMessage;
	Object.assign(proto, {async receiveMessage(message) {
		if (message.name == "AboutNewTabVisible") {
			var br = this.browsingContext.top.embedderElement;
			br.focus();
			br.messageManager.loadFrameScript(fsu, false);
		}
		return rm.call(this, message);
	}});
})("data:,content.document.getElementById('newtab-search-text').focus();");

Dumby, скажите, пожалуйста, это осуществимо или нет..?

Dumby пишет

Даже не знаю, может подойдёт вариант "вообще всегда"?

Отлично! Спасибо!

Dumby, посоветовали ваш скрипт для удаления "Панели закладок", "Меню закладок" и "Другие закладки" в боковой панели. Срабатывает отлично на версиях с 68 по 105 с небольшим побочным эффектом: на разделителях между папками появляется разрыв:
Sidebar.1664377176.png
Можно это как-то исправить, кроме как удаления разделителей..? И можно ли придумать что-то подобное вашему скрипту для удаления вышеуказанного в "Библиотеке"..?

LGS пишет

скажите, пожалуйста, это осуществимо или нет..?

Да особо сказать нечего.
Заголовок это не часть документа, элемент туда не поместить.


Можно разве что текст подменить.
Например, переопределить метод gBrowser.updateTitlebar()
чтобы он устанавливал document.title со своим вставленным добром.


Ещё можно какой-нибудь <panel> поверх заголовка отобразить,
но я не знаю как по-простому сделать, чтобы за панельку можно было таскать окно,
плюс, заголовского контекстного меню у неё не будет.
Ну и настраивать код, конечно, придётся. То есть что-то типа

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

Выделить код

Код:

(async (n, id) => {
	var css = `
		#${id} {
			margin-bottom: 2px;
			margin-right: 102px;

			border: 0;
			height: 18px;
			padding: 0 2px;
			-moz-appearance: none;
			background-color: transparent;
		}
		:root[sizemode=normal] #${id} {
			margin-bottom: 3px;
		}
		:root[sizemode=fullscreen] #${id} {
			visibility: collapse;
		}
		#${id} > hbox {
			color: white;
			height: 18px;
			font-weight: bold;
			background-color: red;
		}
		#${id} > hbox > box {
			padding: 0 4px;
		}
	`;
	var ucs = {
		esr: "ESR",
		beta: "Beta",
		release: "Release",
		nightly: "Nightly",
		aurora: "DevEdition",
		default: "Unbraindead", // ?
	};
	var format = {weekday: "long", day: "numeric", month: "long", year: "numeric"};
	var attrs = {
		id, noautohide: true, noautofocus: true,
		position: "before_end", consumeoutsideclicks: "never"
	};


	css = css.replace(/;/g, " !important;");
	var url = "data:text/css;charset=utf-8," + encodeURIComponent(css);
	windowUtils.loadSheetUsingURIString(url, windowUtils.USER_SHEET);

	var panel = n("panel");
	var hbox = panel.appendChild(n("hbox"));
	for(var args of Object.entries(attrs)) panel.setAttribute(...args);

	document.getElementById("mainPopupSet").append(panel);
	var num = 3; while(num--) hbox.appendChild(n("box")).append("");
	var [vers, time, date] = Array.from(hbox.children, b => b.firstChild);

	var arr = [
		Services.appinfo.name,
		"v" + AppConstants.MOZ_APP_VERSION_DISPLAY,
		`(${Services.appinfo.is64Bit ? 64 : 32}-bit)`
	];
	var c = AppConstants.MOZ_UPDATE_CHANNEL, uc = ucs[c];
	if (uc) {
		if (c == "esr") arr[1] = arr[1].slice(0, -3);
		arr.splice(1, 0, uc);
	}
	vers.data = arr.join(" ");

	var cd;
	var tick = () => {
		var dt = new Date(), d = dt.getDate();
		if (d != cd) cd = d, upd(dt);
		time.data = dt.toLocaleTimeString("mn");
	}
	var upd = d => date.data = d.toLocaleDateString("ru", format);

	tick();
	window.setInterval(tick, 1e3);
	panel.ondblclick = e => {
		if (e.button) return;
		panel.collapsed = true;
		windowState == STATE_MAXIMIZED ? restore() : maximize();
		panel.collapsed = false;
	}
	panel.openPopup(document.documentElement);

	var sr = panel.shadowRoot;
	if (sr) sr.firstChild.removeAttribute("part");

})(nn => document.createXULElement(nn), "vtd-info-panel");

скрипт для удаления

Скрипт ничего не удаляет.
Он меняет корневую ноду places-дерева с «Все закладки» на «Меню закладок».

Можно это как-то исправить, кроме как удаления разделителей..?

Не знаю. Ничего хорошего не придумал.
Так-то, вот этой "чёрточки слева" просто не должно быть.


Если пишу
#bookmarks-view > treechildren::-moz-tree-twisty(separator) {
    padding: 0 !important;
}
то разрыв пропадает, но это сдвигает чуть влево и серараторы в субпапках.


А если пишу
#bookmarks-view > treechildren::-moz-tree-separator {
    margin-right: 16px !important;
}
то слева сепараторы выглядят как надо, но теряют в продолжительности справа.

И можно ли придумать что-то подобное вашему скрипту для удаления вышеуказанного в "Библиотеке"..?

Да я пробовал раньше. И не раз. И ничего не получилось.

Dumby

Dumby пишет

Ну и настраивать код, конечно, придётся.

На Win10 почти идеально выглядит без правок:
Titlebar.1664463242.png
и текст не подменяется. Реально круто, спасибо!

Dumby пишет

если пишу
#bookmarks-view > treechildren::-moz-tree-separator {
    margin-right: 16px !important;
}

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

Dumby пишет

ничего не получилось

Разве такое может быть..?

В [firefox] 106 отвалился "Пункт для контекстного меню адресной строки, подставляющий модификаторы поиска".
Кто-нибудь знает, что не так?

xrun1 пишет

"подставляющий модификаторы поиска"

попробовал запустить код через userCromeJS от Ксяо на [firefox] 106.0.1, не заработала только очистка мышкой #searchbar. брал из второго спойлера с пометкой Update, может в этом проблема...

xrun1
Сходил по ссылке, вставил код в 108.0a1, пункт есть.

Dumby
Работает, даже не знаю, что у меня было.:beer:

Подскажите код, который осуществляет перезапуск браузера с текущими аргументами командной строки. Взятый отсюда https://forum.mozilla-russia.org/viewto … 43#p798943 перезапускает без аргументов.

Обновил UserChromeFiles и Demo-ПРОФИЛЬ для Firefox 84+
Изменения в основном для совместимости с новыми версиями Firefox, в Демо-профиле изменено 555 файлов.


Если оформление браузера «неправильное», скачайте aris-t2 стиль, соответствующий вашей версии Firefox.
    current (Firefox 110+), legacy/fx101-108, legacy/fx91-100, legacy/fx60-90
удалите папки «config, css, image» из «Ваш-профиль/chrome/user_chrome_files/custom_styles/aris-t2» и скопируйте такие же для вашего Firefox, например из fx91-100.

Dumby, можно заставить работать эти скрипты FirefoxTaskManager part1, part2 (не знаю, почему автор разбил на две части) и aboutconfig_menu.uc.js в 68ESR..? Размеры, конечно, не хилые, но, может, посмотрите, что можно сделать..?

LGS пишет

и aboutconfig_menu.uc.js в 68ESR

Сначала убрать, на всякий случай, строку console.log("aboutconfig_menu.uc.js");
Затем заменить btn.type = 'menu'; на btn.setAttribute("type", "menu");


И должно работать. Ну, иконку ещё заменить, в 68 нет никакого «ion.svg».
Расположить, разумеется, в custom_script.js, а не в окно.


А FirefoxTaskManager — вот тут не знаю.
Для меня всё упирается в CSS. Если JS ещё могу поправить,
то как спозиционировать то, что скрипт суёт во вкладки, так, как в 102, я совершенно без понятия.

Dumby

Dumby пишет

должно работать

Спасибо, работает.

Dumby пишет

что скрипт суёт во вкладки, так, как в 102, я совершенно без понятия

А если упростить задачу: вот этот адаптировать под 68. На 78 проверял, там, вроде, только выпадающий список без "вторжения" во вкладки.
Судя по потрохам скрипта, задачу это не упрощает. Отбой, извиняюсь за отнятое время.

LGS пишет

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

Вторжение есть, div.tabBars добавляется в табский vbox.tab-background
Но да, что-то никакого видимого тултипа это не образует.
Так что попробуем вернуться к предыдущему варианту.


Насчёт позиционирования, оно работает, если .tab-content'у назначить position: relative
Как бы это чего-нибудь не сломало, надо приглядеться.


Кстати вот про «не знаю, почему автор разбил на две части»,
надо полагать, это потому, что скрипты сделаны под «xiaoxiaoflood's uc loader»,
и у второго (part2) присутствует директива @onlyonce, то есть он должен исполняться только один раз.


Итак, part2.
Убираем строку console.log("taskmonitor_part2.js");
Меняем btn.type = 'menu'; на btn.setAttribute("type", "menu");
В стиль (он там в конце) добавляем
tab.tabbrowser-tab .tab-content {position: relative !important;}
И перемещаем скрипт в custom_script.js, если он ещё не там.


part1
Меняем (все) document.body на просто document
то есть убираем «.body», в 68 у документа нет никакого body
И меняем эти четыре строки

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

Выделить код

Код:

/*
        var insertNode = tabNode.getElementsByClassName("tab-content")[0];
*/
        var insertNode = document.getAnonymousElementByAttribute(tabNode, "class", "tab-content");


/*
        var close_button = tabNode.getElementsByClassName("tab-content")[0].getElementsByClassName("tab-close-button")[0];
*/
        var close_button = insertNode.querySelector(":scope > .tab-close-button");


/*
            if ( _btnNode ) btnNode = _btnNode.getElementsByClassName("toolbarbutton-badge-stack")[0];
*/
            if (_btnNode) btnNode =  document.getAnonymousElementByAttribute(_btnNode, "class", "toolbarbutton-badge-stack");


/*
            contParent = document.createXULElement("div");
*/
            contParent = document.createXULElement("box");

Dumby, круто, как всегда, огромное спасибо:

скрытый текст
Taskbar-4.1667918402.png   Taskbar-5.1667918423.png   Taskbar-6.1667918439.png

Только пришлось помимо четырех строк еще три заменить, а то консоль ошибки выдавала и по щелчку ЛКМ в окошке с голубым фоном значения не выводились:
скрытый текст
// var menu_task_obj = win.documen.getElementsByClassName( "fftm_widget_task" )[i;
    var menu_task_obj = document.getElementById( "fftm_widget_task_"+i );

// var fftm_widget = win.documen.getElementsByClassName("fftm_widget_class")[0];
   var fftm_widget = document.getElementById("fftm_widget");

// var _btnNode = win.documen.getElementsByAttribute("data-extensionid",addonId)[0];
    var _btnNode = document.getElementsByAttribute("data-extensionid",addonId


Хорошо, что у автора они прописаны были выше под комментариями (стр. 700, к примеру)... наугад попробовал строку с ошибкой закомментировать, авторскую активировать - получилось.

LGS пишет

пришлось помимо четырех строк еще три заменить

Видимо реплейс «.body» прошёл кривовато.
В приведённых строках везде «win.documen»
без буквы «t» на конце слова документ.

Dumby пишет

В приведённых строках везде «win.documen»
без буквы «t» на конце слова документ.

Мой косяк, когда в редакторе замену делал document.body на document букву t потерял. В оригинале все нормально. Странно, что вообще сработала замена трех строк.

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

Случайно заметил, что обновился скрипт от Alice0775 memoryMinimizationButton.uc.js. Убрал 3 сообщения слева снизу и заменил "всплывашкой". Кнопка у меня в боковой панели, добавил отступ - в [firefox] v.107 прижалась влево к границе окна. Если кто пользуется, в CustomStylesScripts.jsm секция load: [ // По событию "load"
{ path: "memoryMinimizationButton.uc.js", ucfobj: false, },

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

Выделить код

Код:

// ==UserScript==
// https://raw.githubusercontent.com/alice0775/userChrome.js/master/108/memoryMinimizationButton.uc.js
// @name           memoryMinimizationButton.uc.js
// @namespace      http://space.geocities.yahoo.co.jp/gl/alice0775
// @description    memory minimization button
// @charset        utf-8
// @include        main
// @include        about:processes?memoryMinimizationButton
// @compatibility  Firefox 108
// @author         Alice0775
// @version        2022/10/18 10:00 fix Bug 1790616
// @version        2022/06/18 00:00 kil process
// @version        2018/10/09 00:00 fix CSS
// @version        2018/09/07 23:00 fix initial visual status
// ==/UserScript==
var memoryMinimizationButton = {
  get memoryMinimizationButton(){
    return document.getElementById("memoryMinimizationButton");
  },

  get statusinfo() {
    if ("StatusPanel" in window) {
      // fx61+
      return StatusPanel._labelElement.value;
    } else {
      return XULBrowserWindow.statusTextField.label;
    }
  },

  set statusinfo(val) {
    if ("StatusPanel" in window) {
      // fx61+
      StatusPanel._label = val;
    } else {
      XULBrowserWindow.statusTextField.label = val;
    }
    if(this._statusinfotimer)
      clearTimeout(this._statusinfotimer);
    this._statusinfotimer = setTimeout(() => {this.hideStatusInfo();}, 2000);
    this._laststatusinfo = val;
    return val;
  },

  init: function() {
    let style = `
      #memoryMinimizationButton {
          width: 16px;
          height: 16px;
          margin-left: 5px; /* со 107-й сломалось, добавил */
        list-style-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjEuMWMqnEsAAAFdSURBVFhH7VbLbsMwDMsef5dc8z/53F13GXZYm5kaZciqnKro2hxaAkRkmkqI2gI6KNZ1vStPEJluyS6WZVlB3xDpWc3q/Ewf6ASClwicLkhojc5PxaDvgQIU4dWSPjEa/Z1y1ed5fqPUeCmJVhAGUC9YNzz4ghNEelYDrI5aAozjeNyD5QBqgF34DJAN8GN4oGb3cJ527fftumEqgAVurdk7Um680zR92bXWEdMBWB+s14+TegFf93hpgF5dgylK/cFSPSGvOgKA9afWgP4y+lR/xKuOwMN6AdwFXff4H0eAqfhWnU9Mhdx+rrtMBSjER3TUtLYaqOMYjaldN8wGuBmfAWqAcmMPe1ACYF6lIBUZXXFO29I3/5IVoFMpcLoA2iX96gVLiBb0bb6gINSy/WLsgb4HDgBD0BzqXsv0i8nDm+/JCC8lKW5zY4SGvT9LBdbVS0g/64q4fxh+AZvdJHHKcZdFAAAAAElFTkSuQmCC');
      }
      @media (min-resolution: 1.1dppx) {
        #memoryMinimizationButton {
          width: 32px;
          height: 32px;
        }
      }
     `.replace(/\s+/g, " ");

    let sss = Components.classes['@mozilla.org/content/style-sheet-service;1']
                .getService(Components.interfaces.nsIStyleSheetService);
    let newURIParam = {
        aURL: 'data:text/css,' + encodeURIComponent(style),
        aOriginCharset: null,
        aBaseURI: null
    }
    let cssUri = Services.io.newURI(newURIParam.aURL, newURIParam.aOriginCharset, newURIParam.aBaseURI);
    if (!sss.sheetRegistered(cssUri, sss.AUTHOR_SHEET))
      sss.loadAndRegisterSheet(cssUri, sss.AUTHOR_SHEET);

    if (this.memoryMinimizationButton)
      return;

    ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
    try {
      CustomizableUI.createWidget({ //must run createWidget before windowListener.register because the register function needs the button added first
        id: 'memoryMinimizationButton',
        type:  'custom',
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild: function(aDocument) {
          var toolbaritem = aDocument.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'toolbarbutton');
          var props = {
            id: "memoryMinimizationButton",
            class: "toolbarbutton-1 chromeclass-toolbar-additional",
            tooltiptext: "Memory minimization(shift+click; kill other tabs)",
            oncommand: "memoryMinimizationButton.doMinimize(event);",
            type: CustomizableUI.TYPE_TOOLBAR,
            label: "Memory minimization",
            removable: "true"
          };
          for (var p in props) {
            toolbaritem.setAttribute(p, props[p]);
          }
          
          return toolbaritem;
        },
      });
    } catch(ee) {}
  },

  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) {
      if (event.button == 1 || event.shiftKey || event.altKey || event.ctrlKey)
        memoryMinimizationButton.kill();
      Services.obs.notifyObservers(null, "child-mmu-request");
      var gMgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
//      gMgr.minimizeMemoryUsage(() => {if (!(event.button == 1 || event.shiftKey || event.altKey)) memoryMinimizationButton.displayStatus("Memory minimization done")});
      gMgr.minimizeMemoryUsage(() => {if (!(event.button == 1 || event.shiftKey || event.altKey)) memoryMinimizationButton.displayStatus("")});
    }
    function sendHeapMinNotifications()  {
      function runSoon(f) {
        var tm = Cc["@mozilla.org/thread-manager;1"]
                  .getService(Ci.nsIThreadManager);

        tm.mainThread.dispatch({ run: f }, Ci.nsIThread.DISPATCH_NORMAL);
      }

      function sendHeapMinNotificationsInner() {
        var os = Cc["@mozilla.org/observer-service;1"]
                 .getService(Ci.nsIObserverService);
        os.notifyObservers(null, "memory-pressure", "heap-minimize");

        if (++j < 3)
          runSoon(sendHeapMinNotificationsInner);
      }

      var j = 0;
      sendHeapMinNotificationsInner();
    }

//    this.displayStatus("Memory minimization start")
    doGlobalGC();
    doCC();
    //sendHeapMinNotifications();
    // Добавил всплывашку
    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/icons/information-16.png", "Memory", "Потребление памяти минимизировано!");
    setTimeout(() => alertsService.closeAlert(), 2000);
  },
  
  _statusinfotimer: null,
  _laststatusinfo: null,
  displayStatus: function(val) {
    this.statusinfo = val;
  },
  hideStatusInfo: function() {
    if(this._statusinfotimer)
      clearTimeout(this._statusinfotimer);
    this._statusinfotimer = null;
    if (this._laststatusinfo == this.statusinfo)
      this.statusinfo = "";
  },

  kill: function() {
    this.browser = document.createXULElement("browser");
    this.browser.src = "about:processes?memoryMinimizationButton";
    document.documentElement.appendChild(this.browser);
    setTimeout(() => {
      this.browser.src = "about:blank";
      document.documentElement.removeChild(this.browser);
      delete this.browser;
//      Services.console.logStringMessage("killing done");
//      this.displayStatus("Memory minimization done")
    }, 8000);
  }
}

if (location.href == "chrome://browser/content/browser.xhtml") {
  memoryMinimizationButton.init();
} else 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;
      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);
    }
    return;
    /*
    let closeButtons = document.querySelectorAll("tr.process > td.close-icon");
    for(let closeButton of closeButtons) {
      closeButton.click();
    }
    */
  }, 5000);
}


И кнопка в панель адреса, вроде от Dumby, ссылки нет.
scriptsbackground: [ // В фоне [System Principal]
скрытый текст

Выделить код

Код:

(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}" tooltiptext="${
        "ЛКМ: Минимизировать потребление памяти&#xA;ПКМ: about:performance&#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);
      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" : "performance"
    }`);
  },
  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");


Если что напутал, поправьте.
UPD: Скрипт заменил на 108+

xrun1
Кнопка в панель адреса
+ та и следующая страница обсуждения, если на кнопку в строке адреса надо повесить функцию очистки по клику и ...
   
А секция sendHeapMinNotifications() разве не к уведомлениям в статусе относится?

_zt пишет

секция sendHeapMinNotifications() разве не к уведомлениям в статусе относится?

Это функция и вызов её закомментарил, там всего одно место как раз перед моей "всплывашкой". Правильно это или что-то ф-ция делает ещё - не знаю...
За ссылку на разговор о кнопке спасибо. Я помню, что-то было на пару страниц, читал, но ссылку у себя не сохранил.
Что касается очистки по клику, так это есть. Скрипт создаёт свою кнопку, но я ей не пользуюсь, она сидит в боковой панели. А по клику ЛКМ на кнопке в строке адреса как раз и вызывается функция из скрипта memoryMinimizationButton.doMinimize(event). Я кнопку компоновал из нескольких сообщений, в том числе из Вашего.

xrun1
Понятно. Только непонятно, нужна ли мне последняя секция?

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

Выделить код

Код:

var memoryMinimizationButton = {
  get memoryMinimizationButton(){
    return document.getElementById("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");
      var gMgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
    }

    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/icons/information-16.png", "Memory Minimization", "Минимизация памяти выполнена!");
    setTimeout(() => alertsService.closeAlert(), 2000);
  },

  kill: function() {
    this.browser = document.createXULElement("browser");
    this.browser.src = "about:processes?memoryMinimizationButton";
    document.documentElement.appendChild(this.browser);
    setTimeout(() => {
      this.browser.src = "about:blank";
      document.documentElement.removeChild(this.browser);
      delete this.browser;
    }, 8000);
  }
}

if (location.href == "about:processes?memoryMinimizationButton") {
  setTimeout(() => {
    let closeButtons = document.querySelectorAll("tr.process > td.close-icon");
    for(let closeButton of closeButtons) {
      closeButton.click();
    }
  }, 5000);
}

_zt пишет

нужна ли мне последняя секция?

Думаю, нет. Как я понял, по задумке автора по shift+click должны закрываться другие табы. У меня не закрываются. Может потому, что у меня нет кнопки закрытия вкладки, а в этой секции скрипт кликает по ней.))

https://hg.mozilla.org/mozilla-central/rev/49cefc94b9bd

Dumby посмотрите пожалуйста кнопку Куки а то там такая фигня по ПКМ

скрытый текст
8874acab75388702e75a1f26300be490.png

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

Выделить код

Код:

//Переключить Куки
try {(() => {
    var id = "ucf-cookie-toggle",
    label = "Переключить Куки",
    tooltiptext = "ЛКМ: Переключить Куки\nСКМ: Удалить куки домена текущей страницы\nПКМ: Управление куками",
    gpref = "network.cookie.cookieBehavior",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";

    var tbarbtns = {
        initialised: false,
        get network_cookie_cookieBehavior() {
            delete this.network_cookie_cookieBehavior;
            try {
                return this.network_cookie_cookieBehavior = Services.prefs.getIntPref(gpref);
            } catch(e) { }
            return this.network_cookie_cookieBehavior = null;
        },
        init() {
            if (this.initialised) return;
            this.initialised = true;
            Services.prefs.addObserver(gpref, this);
        },
        prefToggleNumber(pref, next) {
            Services.prefs.setIntPref(pref, next[Services.prefs.getIntPref(pref)]);
        },
        getETDL(uri) {
            var eTLD = "";
            try {
                eTLD = Services.eTLD.getBaseDomain(uri);
            } catch (e) {
                try {
                    eTLD = uri.asciiHost;
                } catch (e) {}
            }
            return eTLD;
        },
        async viewCookies(win) {
            var uri = win.gBrowser.selectedBrowser.currentURI;
            try {
                let _uri = win.ReaderMode.getOriginalUrl(uri.spec);
                if (_uri)
                    uri = Services.io.newURI(_uri);
            } catch(e) {}
            uri = this.getETDL(uri);
            var type = "Browser:SiteDataSettings", id = "SiteDataSettingsDialog";
            var _win = Services.wm.getMostRecentWindow(type);
            if (!_win) {
                await win.SiteDataManager.updateSites();
                let url = "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml", xs = Services.xulStore;
                let sx = xs.getValue(url, id, "screenX");
                let sy = xs.getValue(url, id, "screenY");
                let wh = xs.getValue(url, id, "width");
                let ht = xs.getValue(url, id, "height");
                let sm = xs.getValue(url, id, "sizemode");
                let features = `chrome,dialog=no,resizable,${sx && sy ? `screenX=${sx !== "0" ? sx : "1"},screenY=${sy !== "0" ? sy : "1"}` : "centerscreen"}${wh && ht ? `,width=${wh},height=${ht}` : ""}`;
                _win = win.openDialog(url, type, features);
                await new Promise(resolve => {
                    _win.windowRoot.addEventListener("DOMContentLoaded", () => {
                        _win.windowRoot.addEventListener("MozUpdateWindowPos", () => {
                            if (sm === "maximized")
                                _win.maximize();
                        }, { once: true, capture: true });
                        resolve();
                    }, { once: true });
                });
            }
            var doc = _win.document;
            var docEl = doc.documentElement;
            docEl.setAttribute("windowtype", type);
            docEl.id = id;
            docEl.setAttribute("persist", "screenX screenY width height sizemode");
            _win.focus();
            var filter = doc.querySelector("#searchBox");
            if (!filter) return;
            filter.value = uri;
            filter.focus();
            filter.dispatchEvent(new _win.Event("input", { bubbles: true }));
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
        },
        observe(subject, topic, pref) {
            if (pref == gpref) {
                delete this.network_cookie_cookieBehavior;
                let network_cookie_cookieBehavior = this.network_cookie_cookieBehavior = Services.prefs.getIntPref(pref);
                this.callWithEachWindow(id, {badge: network_cookie_cookieBehavior, badgeStyle: `background: ${network_cookie_cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`});
            }
        },
        uninit() {
            if (!this.initialised) return;
            Services.prefs.removeObserver(gpref, this);
            this.initialised = false;
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild(document) {
            var win = document.defaultView, trbn = document.createXULElement("toolbarbutton");
            trbn.id = id;
            trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional badged-button";
            trbn.setAttribute("badged", "true");
            trbn.setAttribute("constrain-size", "true");
            trbn.setAttribute("label", label);
            trbn.setAttribute("context", "false");
            trbn.setAttribute("tooltiptext", tooltiptext);
            var cookieBehavior = tbarbtns.network_cookie_cookieBehavior;
            if (cookieBehavior !== null) {
                trbn.setAttribute("badge", cookieBehavior);
                trbn.setAttribute("badgeStyle", `background: ${cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`);
                trbn.addEventListener("click", e => {
                    if (e.button == 0)
                        tbarbtns.prefToggleNumber(gpref, [1,2,3,4,5,0]);
                    else if (e.button == 1) {
                        if (!win.gIdentityHandler?._uriHasHost || win.gIdentityHandler._pageExtensionPolicy)
                            return;
                        let baseDomain = win.SiteDataManager.getBaseDomainFromHost(win.gIdentityHandler._uri.host);
                        win.SiteDataManager.hasSiteData(baseDomain).then(hasData => {
                            if (hasData && win.SiteDataManager.promptSiteDataRemoval(win, [baseDomain]))
                                win.SiteDataManager.remove(baseDomain);
                        });
                    } else if (e.button == 2) {
                        e.preventDefault();
                        e.stopPropagation();
                        tbarbtns.viewCookies(win);
                    }
                });
            }
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
                #${id}[badge="2"] {
                    fill: color-mix(in srgb, currentColor 20%, #e31b5d) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            tbarbtns.init();
            return trbn;
        },
        onDestroyed(doc) {
            tbarbtns.uninit();
        },
    });
})();} catch(e) {}

[firefox] 107.0.1

egorsemenov06 пишет

Dumby посмотрите пожалуйста кнопку Куки а то там такая фигня по ПКМ

Посмотрел. Такой фигни не вижу.

Dumby пишет
egorsemenov06 пишет

Dumby посмотрите пожалуйста кнопку Куки а то там такая фигня по ПКМ

Посмотрел. Такой фигни не вижу.

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

egorsemenov06 пишет

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

У меня окно кук в [firefox] 107.0.1 выглядит так:

скрытый текст
Untitled-3.jpg
Я могу потянуть окно за низ и добиться нужного размера.
Но после перезапуска [firefox] окно опять принимает первоначальный вид, т.е. размеры окна не сохраняются.

unter_officer пишет

Я могу потянуть окно за низ и добиться нужного размера.
Но после перезапуска [firefox] окно опять принимает первоначальный вид, т.е. размеры окна не сохраняются.

решилось созданием нового профиля

Dumby
на ff 107 заметил, что перестал работать скрипт SwitchKeyboardLayout

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

Выделить код

Код:

try {(keybUtils => CustomizableUI.createWidget({
    type: "custom",
    id: "SwitchKeyboardLayout",
    onBuild(doc) {
        var btn = doc.createXULElement("toolbarbutton");
        btn.id = this.id;
        btn.label = btn.tooltipText = "Switch Keyboard Layout";
        btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";

        btn.setAttribute("oncommand", "linkedObj.switch(document);");
        btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
        btn.linkedObj = this;
        return btn;
    },
    switch(doc) {
        var br = doc.activeElement;
        br && br.localName == "browser" && br.isRemoteBrowser
            ? br.messageManager.loadFrameScript(this.url, false)
            : this.keybUtils.switchSelKeybLayout();
    },
    get url() {
        delete this.url;
        return this.url = `data:;charset=utf-8,(${
            encodeURIComponent(keybUtils)
        }).switchSelKeybLayout()`;
    },
    get keybUtils() {
        delete this.keybUtils;
        var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
        var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
        Services.scriptloader.loadSubScript(url, this);
        var {id} = this;
        this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
            var window = Services.focus.activeWindow, {document} = window;
            var button = document.getElementById(id);
            if(
                !_focusFixed
                && "closeMenus" in window
                && document.commandDispatcher.focusedElement == button
            ) {
                window.closeMenus(button);
                window.setTimeout(function(_this) {
                    _this.switchSelKeybLayout(_subCall, true);
                }, 0, this);
                return;
            }
            return document.commandDispatcher.focusedElement;
        }
        return this.keybUtils;
    }
}))(`{
    //== Options
    noSelBehavior: { // Shift+Home
        ctrlKey:  false,
        altKey:   false,
        shiftKey: true,
        metaKey:  false,
        keyCode:  KeyEvent.DOM_VK_HOME,
        charCode: 0
    },
    // 0 - do nothing
    // 1 - convert all text
    // Or use object like following to simulate "keypress" event:

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
/*
            if(c in this.convTableForward)
                return true;
            if(c in this.convTableBackward)
                return false;
*/
            var primary = c in this.convTableForward;
            if(primary ^ c in this.convTableBackward)
                return primary;        }

        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(node instanceof Element) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
        var e = this.noSelBehavior;
        if(!e || typeof e != "object")
            return;

/*
        var evt = new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        );

        node.dispatchEvent(evt);
    },
*/

	if(ChromeUtils.domProcessChild.childID) {
            var cmd = this.beh2cmd[e.ctrlKey + "_" + e.shiftKey + "_" + e.keyCode];
            cmd && docShell.doCommand(cmd);
        }
        else node.dispatchEvent(new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        ));
    },
    beh2cmd: { // Ctrl_Shift_VK
        false_true_36: "cmd_selectLinePrevious", // Shift+Home
    },

    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`)} catch(ex) {Cu.reportError(ex);}


гляньте пожалуйста, возможно ли починить?

Inko7 пишет

на ff 107 заметил, что перестал работать скрипт

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


Но, x instanceof *Element лучше заменить на *Element.isInstance(x)
Там три вхождения (поиск по «instanceof»).

Dumby
После правок всё заработало! Спасибо.

не долго радовался((
скопировал правленый файл скрипта, принес с работы домой, заменил им текущий нерабочий, перегрузил FF с очисткой кэша - не заработало!
начал разбираться:
оказывается, если тыцкать саму кнопку, то раскладка слов исправляется! не работает код на горячую клавишу F8

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

Выделить код

Код:

// Назначить клавишу F8 для исправления раскладки введенного текста
// код SwitchKeyboardLayout в файле custom_script.js

try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}


странно, но на работе ж каким-то чудным образом заработало...

Dumby
извиняюсь, что не верно и не разобравшись озвучил суть проблемы
почини пожалуйста эту переключалку по F8

del

Inko7 пишет

почини пожалуйста эту переключалку по F8

Затруднительно починить то, что не сломано.


Добавил код в custom_script_win.js, топаю по адресу
data:text/html;charset=utf-8,<textarea spellcheck="false">qwerty</textarea>
фосус в конец поля, жму F8, и вижу переключение qwerty <—> йцукен


перегрузил FF с очисткой кэша

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

Dumby пишет

Чтобы быть уверенным, следует, при закрытом браузере,
удалить папку startupCache собственноручно.

помогло
раньше как-то до этого не доходило
спасибо

Dumby пишет

собственноручно

Кнопку + батник для себя не делали?
   
По поводу окна кук:
У меня окно кук в [firefox] 102 растет по вертикали после каждого запуска с удалением, в итоге после нескольких запусков кнопки удаления уезжают под панель задач.
Скрипт вызова здесь или:

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

Выделить код

Код:

(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");
            },
            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 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 type = "Browser:SiteDataSettings", id = "SiteDataSettingsDialog";
                var _win = Services.wm.getMostRecentWindow(type);
                await SiteDataManager.updateSites();
                if (!_win) {
                    let url = "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml", xs = Services.xulStore;
                    let sx = xs.getValue(url, id, "screenX");
                    let sy = xs.getValue(url, id, "screenY");
                    let wh = xs.getValue(url, id, "width");
                    let ht = xs.getValue(url, id, "height");
                    let sm = xs.getValue(url, id, "sizemode");
                    let features = `chrome,dialog=no,resizable,${sx && sy ? `screenX=${sx !== "0" ? sx : "1"},screenY=${sy !== "0" ? sy : "1"}` : "centerscreen"}${wh && ht ? `,width=${wh},height=${ht}` : ""}`;
                    _win = openDialog(url, type, features);
                    await new Promise(resolve => {
                        _win.windowRoot.addEventListener("DOMContentLoaded", () => {
                            _win.windowRoot.addEventListener("MozUpdateWindowPos", () => {
                                if (sm === "maximized")
                                    _win.maximize();
                            }, { once: true, capture: true });
                            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, "UTF-8");
                     _win.addEventListener("unload", () => {
                        _win.updateClearInterval?.();
                    }, { once: true });
                }
                var doc = _win.document;
                var docEl = doc.documentElement;
                docEl.setAttribute("windowtype", type);
                docEl.id = id;
                docEl.setAttribute("persist", "screenX screenY width height sizemode");
                _win.focus();
                var filter = doc.querySelector("#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);

egorsemenov06 пишет

решилось созданием нового профиля

Сомневаюсь. Просто не запустили нужное кол-во раз.

_zt пишет

У меня окно кук в [firefox] 102 растет по вертикали после каждого запуска с удалением, в итоге после нескольких запусков кнопки удаления уезжают под панель задач.

Я в 108 бетке "приколотил высоту гвоздями". Вроде пока нормально.
В userChrome.css:

Выделить код

Код:

@-moz-document url-prefix("chrome://browser/content/preferences/dialogs/siteDataSettings.x") {
	#SiteDataSettingsDialog {
		max-height: 465px !important;
	}
	#sitesList {
		height: 25em !important;
	}
}
@-moz-document url-prefix("chrome://browser/content/preferences/dialogs/siteDataRemoveSelected.x") {
	#SiteDataRemoveSelectedDialog {
		max-height: 450px !important;
	}
	#removalList {
		height: 25em !important;
	}
}

unter_officer
Спасибо. Добавил, пока не увеличивается, посмотрим.
   
xrun1
Спасибо. Работает вроде.
   

Dumby пишет

динамическая хром-регистрация

Спасибо.

_zt пишет

Кнопку + батник для себя не делали?

Нет, не делал.

У меня окно кук в [firefox] 102 растет по вертикали после каждого запуска с удалением

Да, есть такое.
Можно скопировать xhtml'ку в UCF, дописать persist'а и windowtype,
и загружать уже этот адрес. Ну и почистить код, соответственно.


Или динамическая хром-регистрация, viewCookies() типа

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

Выделить код

Код:

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 }));
                })();
            },

Dumby а что надо исправить в этой кнопке Переключить куки что бы окно не росло по вертикали в [firefox] 107.0.1?

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

Выделить код

Код:

//Переключить Куки
try {(() => {
    var id = "ucf-cookie-toggle",
    label = "Переключить Куки",
    tooltiptext = "ЛКМ: Переключить Куки\nСКМ: Удалить куки домена текущей страницы\nПКМ: Управление куками",
    gpref = "network.cookie.cookieBehavior",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";

    var tbarbtns = {
        initialised: false,
        get network_cookie_cookieBehavior() {
            delete this.network_cookie_cookieBehavior;
            try {
                return this.network_cookie_cookieBehavior = Services.prefs.getIntPref(gpref);
            } catch(e) { }
            return this.network_cookie_cookieBehavior = null;
        },
        init() {
            if (this.initialised) return;
            this.initialised = true;
            Services.prefs.addObserver(gpref, this);
        },
        prefToggleNumber(pref, next) {
            Services.prefs.setIntPref(pref, next[Services.prefs.getIntPref(pref)]);
        },
        getETDL(uri) {
            var eTLD = "";
            try {
                eTLD = Services.eTLD.getBaseDomain(uri);
            } catch (e) {
                try {
                    eTLD = uri.asciiHost;
                } catch (e) {}
            }
            return eTLD;
        },
        async viewCookies(win) {
            var uri = win.gBrowser.selectedBrowser.currentURI;
            try {
                let _uri = win.ReaderMode.getOriginalUrl(uri.spec);
                if (_uri)
                    uri = Services.io.newURI(_uri);
            } catch(e) {}
            uri = this.getETDL(uri);
            var type = "Browser:SiteDataSettings", id = "SiteDataSettingsDialog";
            var _win = Services.wm.getMostRecentWindow(type);
            if (!_win) {
                await win.SiteDataManager.updateSites();
                let url = "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml", xs = Services.xulStore;
                let sx = xs.getValue(url, id, "screenX");
                let sy = xs.getValue(url, id, "screenY");
                let wh = xs.getValue(url, id, "width");
                let ht = xs.getValue(url, id, "height");
                let sm = xs.getValue(url, id, "sizemode");
                let features = `chrome,dialog=no,resizable,${sx && sy ? `screenX=${sx !== "0" ? sx : "1"},screenY=${sy !== "0" ? sy : "1"}` : "centerscreen"}${wh && ht ? `,width=${wh},height=${ht}` : ""}`;
                _win = win.openDialog(url, type, features);
                await new Promise(resolve => {
                    _win.windowRoot.addEventListener("DOMContentLoaded", () => {
                        _win.windowRoot.addEventListener("MozUpdateWindowPos", () => {
                            if (sm === "maximized")
                                _win.maximize();
                        }, { once: true, capture: true });
                        resolve();
                    }, { once: true });
                });
            }
            var doc = _win.document;
            var docEl = doc.documentElement;
            docEl.setAttribute("windowtype", type);
            docEl.id = id;
            docEl.setAttribute("persist", "screenX screenY width height sizemode");
            _win.focus();
            var filter = doc.querySelector("#searchBox");
            if (!filter) return;
            filter.value = uri;
            filter.focus();
            filter.dispatchEvent(new _win.Event("input", { bubbles: true }));
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
        },
        observe(subject, topic, pref) {
            if (pref == gpref) {
                delete this.network_cookie_cookieBehavior;
                let network_cookie_cookieBehavior = this.network_cookie_cookieBehavior = Services.prefs.getIntPref(pref);
                this.callWithEachWindow(id, {badge: network_cookie_cookieBehavior, badgeStyle: `background: ${network_cookie_cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`});
            }
        },
        uninit() {
            if (!this.initialised) return;
            Services.prefs.removeObserver(gpref, this);
            this.initialised = false;
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild(document) {
            var win = document.defaultView, trbn = document.createXULElement("toolbarbutton");
            trbn.id = id;
            trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional badged-button";
            trbn.setAttribute("badged", "true");
            trbn.setAttribute("constrain-size", "true");
            trbn.setAttribute("label", label);
            trbn.setAttribute("context", "false");
            trbn.setAttribute("tooltiptext", tooltiptext);
            var cookieBehavior = tbarbtns.network_cookie_cookieBehavior;
            if (cookieBehavior !== null) {
                trbn.setAttribute("badge", cookieBehavior);
                trbn.setAttribute("badgeStyle", `background: ${cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`);
                trbn.addEventListener("click", e => {
                    if (e.button == 0)
                        tbarbtns.prefToggleNumber(gpref, [1,2,3,4,5,0]);
                    else if (e.button == 1) {
                        if (!win.gIdentityHandler?._uriHasHost || win.gIdentityHandler._pageExtensionPolicy)
                            return;
                        let baseDomain = win.SiteDataManager.getBaseDomainFromHost(win.gIdentityHandler._uri.host);
                        win.SiteDataManager.hasSiteData(baseDomain).then(hasData => {
                            if (hasData && win.SiteDataManager.promptSiteDataRemoval(win, [baseDomain]))
                                win.SiteDataManager.remove(baseDomain);
                        });
                    } else if (e.button == 2) {
                        e.preventDefault();
                        e.stopPropagation();
                        tbarbtns.viewCookies(win);
                    }
                });
            }
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
                #${id}[badge="2"] {
                    fill: color-mix(in srgb, currentColor 20%, #e31b5d) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            tbarbtns.init();
            return trbn;
        },
        onDestroyed(doc) {
            tbarbtns.uninit();
        },
    });
})();} catch(e) {}

_zt Вы  правы

egorsemenov06 пишет

а что надо исправить в этой кнопке Переключить куки что бы окно не росло по вертикали в [firefox] 107.0.1?

Ну тоже что-то сделать с методом viewCookies().
Например, заменить на такой

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

Выделить код

Код:

//
        async viewCookies(win) {
            var func = async sds => {
                await document.documentReadyForIdle;
                var upd, box = document.getElementById("searchBox");
                (upd = site => box.setUserInput(site, box.focus()))(window.arguments[0]);
                var attr = "data-isCurrentSortCol", sel = `treecol[${attr}=true]`;
                window.updSearch = async site => {
                    var sites = sds._sites = await SiteDataManager.getSites();
                    var col = document.querySelector(sel);
                    col.removeAttribute(attr);
                    sds._sortSites(sites, col);
                    sds._buildSitesList(sites);
                    window.focus(upd(site));
                }
            }
            var type = "Browser:SDS";
            var xhtml = (await (await win.fetch("chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml")).text())
                .replace(/(persist=".+)"/, `$1 screenX screenY sizemode" windowtype="${type}"`)
                .replace(/<script .+>/, `$&\n  <script>(${func})(gSiteDataSettings);\n  </script>\n${
                    ["globalOverlay", "editMenuOverlay"]
                        .map(n => `  <script src="chrome://global/content/${n}.js"/>`).join("\n")
                }`);

            var sds = "chrome://ucfsdswnd/content/sds.xhtml";
            this.cr = 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 win => {
                var uri = win.gBrowser.selectedBrowser.currentURI;
                try {
                    var u = win.ReaderMode.getOriginalUrl(uri.spec);
                    if (u) uri = Services.io.newURI(u);
                } catch {}
                uri = this.getETDL(uri);

                await win.SiteDataManager.updateSites();
                var w = Services.wm.getMostRecentWindow(type);
                w ? w.updSearch(uri) : win.openDialog(sds, type, "chrome,dialog=no,resizable", uri);
            })(win);
        },

Dumby пишет

Ну тоже что-то сделать с методом viewCookies().
Например, заменить на такой

Спасибо Огромное!!!

Dumby здесь вопрос под первым спойлером, а здесь ответ.
Посмотрите, пожалуйста, можно ли заставить работать на 68 ESR. xhtml на xul заменил, результат отрицательный. Что интересно, на Mypal68 (форке 68esr для ХР) скрипт срабатывает. В Вин10 и 78esr, 91esr, 102esr, 107 - тоже без проблем.

LGS пишет

xhtml на xul заменил, результат отрицательный

Этого недостаточно. Надо ещё от этого оператора избавиться,
его поддержка добавлена в Firefox только с версии 74 (в Mypal68, видимо, она есть).


Правки можно по-разному записать. Допустим, такой вариант

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

Выделить код

Код:

/*
            var listener = e => {
                var doc = e.target || ({});
                load_scripts_by_url[doc.documentURI]?.(doc.defaultView);
            };
*/
            var listener = e => load_scripts_by_url.x(e.target.ownerGlobal);
Выделить код

Код:

/*
            if (win.arguments?.find(f => f === "Downloads" || f === "History" || f === "Tags")) return;
*/
            if (/^(?:Downloads|History|Tags)(?:,|$)/.test(String(win.arguments))) return;
Выделить код

Код:

/*
    };
    load_scripts_by_url[location.href]?.(window);
*/
        x(win) {
            var func = this[win.location.href];
            func && func(win);
        }
    };
    load_scripts_by_url.x(window);

Dumby пишет

Надо ещё от этого оператора избавиться

Да, именно на "?." консоль ругалась, а как правильно сделать, естественно, не знал, поэтому и обратился к профессионалу. Тупое удаление оператора вопрос не решало, писать про это не стал, чтобы еще глупее не выглядеть.

Dumby пишет

Допустим, такой вариант

Отличный вариант, спасибо.

Dumby пишет

https://hg.mozilla.org/mozilla-central/rev/49cefc94b9bd

https://github.com/xiaoxiaoflood/firefox-scripts/commit/33c69b5b959b84d3982eb6f72aefd1cca14e3ad3#diff-c8669a0b709ab73f8751d7e079d6c3b98ac1c464b5a9e1592f28e91ab7a63de7

Я дико извиняюсь, на чистоустановленного лиса, залил архив из первого сообщения этой темы https://forum.mozilla-russia.org/viewto … 26#p791126, все по полочкам, пытаюсь прикрутить к нему скрипт про ненужные папки https://forum.mozilla-russia.org/viewto … 25#p777225 , а у меня, к сожалению, ничего не выходит.... Если кому не сложно скиньте свой файл заведомо рабочий, для такого криворукого идиота я:)
З.Ы : лис 108

Не пойму может я идиот, но у меня все равно не работает:(

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

Выделить код

Код:

// Скрипт для документа окна браузера [ChromeOnly]
var ucf_custom_script_win = {
    initialized: false,
    get unloadlisteners() {
        delete this.unloadlisteners;
        window.addEventListener("unload", this, { once: true });
        return this.unloadlisteners = [];
    },
    load() {
        if (this.initialized)
            return;
        this.initialized = true;
        /* ************************************************ */

        // Здесь может быть ваш код который сработает по событию "load"

((type, listener) => {
    addEventListener(type, listener);
    addEventListener("unload", () => removeEventListener(type, listener), {once: true});
})("MozBeforeInitialXULLayout", {
    handleEvent(e) {
        e.target.documentURI.endsWith(this.ends) &&
        Object.defineProperty(e.target.getElementById("bookmarks-view"), "place", this);
    },
    get ends() {
        delete this.ends;
        return this.ends = `/bookmarksSidebar.x${
            parseInt(Services.appinfo.platformVersion) >= 73 ? "htm" : "u"
        }l`;
    },
    configurable: true,
    set() {
        delete this.place;
        this.place = "place:parent=menu________";
    }
});

        /* ************************************************ */
    },
    handleEvent(e) {
        this[e.type](e);
    },
    unload() {
        this.unloadlisteners.forEach(str => {
            try {
                this[str].destructor();
            } catch (e) {}
        });
    },
};
/* ************************************************ */

// Здесь может быть ваш код который сработает по событию "DOMContentLoaded"


Пробовал в разных вариантах, не работает, все равно :( Может я что в настройках не включил? 3й день на лисе 108 до этого сидел на 68 все работало изумительно.... ни папки меню закладок, ни других тебе закладок....

mfrost пишет

Может я что в настройках не включил?

Кстати, вполне возможно. Типа эту галку


Включить скрипты:
[  ] В фоне [System Principal]
[✔] Для докум. окна браузера [ChromeOnly] ◄◄◄◄◄◄◄
[  ] Для докум. всех окон [ChromeOnly]

Dumby, посмотрите, пожалуйста, можно ли "оживить" этот древний скрипт, а еще лучше адаптировать dav_LinkifiesLocationBar.uc.js для 68esr. Для первого "зачистка" по вашей рекомендации у меня не помогает, для второго замены в стр.14 chrome://browser/content/browser.xhtml на xul явно недостаточно.

LGS пишет

а еще лучше адаптировать dav_LinkifiesLocationBar.uc.js для 68esr

Ууу, копаться в этом радости мало.

замены в стр.14 chrome://browser/content/browser.xhtml на xul явно недостаточно

Если код в custom_script_win.js, то «стр.14» не нужна как таковая, убери её.


Чтобы как-то завелось, можно попробовать заменить пару строк

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

Выделить код

Код:

/*
		var urlBarInput = getWindow().document.querySelector("#urlbar-input").value;
*/
		var urlBarInput = gURLBar.value;


/*
	var urlbarInput = getWindow().document.querySelector("#urlbar-input");
*/
	var urlbarInput = gURLBar.inputField;


И, там ещё есть текст стиля в var stylexul = `…`;
Так вот, в нём, заменить все (три) решётки (#) на точки (.),
то есть, с идентификатора на класс.

Dumby пишет

Ууу, копаться в этом радости мало

Извиняюсь за отнятое время.

Dumby пишет

Чтобы как-то завелось, можно попробовать заменить пару строк

Завелось и работает, благодарю!

Dumby пишет
mfrost пишет

Может я что в настройках не включил?

Кстати, вполне возможно. Типа эту галку


Включить скрипты:
[  ] В фоне [System Principal]
[✔] Для докум. окна браузера [ChromeOnly] ◄◄◄◄◄◄◄
[  ] Для докум. всех окон [ChromeOnly]

Товарищи, подскажите где это включить в настройках не нашел:(

mfrost пишет

Товарищи, подскажите где это включить в настройках не нашел

Видимо ни в тех настройках искали:
2022-12-26_202921.png

kokoss пишет
mfrost пишет

Товарищи, подскажите где это включить в настройках не нашел

Видимо ни в тех настройках искали:

Вижу кнопку выделенную вами, вижу окошко где описано как открыть данное меню, но, к сожалению, описанным способом в моей версии браузера, данное меню не открывается
Стоит 108 лис обновленный с 68, залит архив из первого сообщения данной темы, так же натянут данный стиль
При клике правой кнопкой мыши вот такое меню
123.jpg

mfrost

kokoss пишет

Огромное спасибо! Залил ваш архив и все заработало:)

Посмотрите, пожалйуста, здесь:
    https://forum.mozilla-russia.org/viewtopic.php?pid=803055#p803055
    Можно ли сделать?

LZAA
Первая секция первого поста темы - добавит, помимо прочего, нижнюю панель. Работает ли это в 68 спрашивайте в теме, небольшой архив версий у меня есть, ранняя версия 200519.

_zt
     Уточните, пожалуйста, 'Первая секция первого поста темы' - это 'UserChromeFiles загрузчик скриптов/стилей'? Если да, то такой вопрос имеется. Если 'UserChromeFiles' сделан Vitaliy V, то не проще ли мне задать ему мой вопрос? Дело в том, что мне нужно только добавить эту панель.
    Но для начала нужно выяснить, есть ли эта панель в коде FF 68 в приципе. Что Вам об этом известно?

LZAA
1. Да. Разве там есть др. первая секция? )
2. Конечно попробуйте. Только вначале посмотрите время его последнего поста.
3. Отсюда и далее по теме.
   
Подождите, может кто др. скрипт предложит. Только вот какой в этом смысл, загрузчик то все равно ставить придется, не ucf так другой, а для ucf скриптов больше, да и не ucf скрипты через него как правило запускаются.

_zt

  Я вчера 'UserChromeFiles загрузчик скриптов/стилей' смотрел и даже пытался установить в FF 68. Но никакого результата не получил. То есть интерфейс не изменился вовсе.
   Про 'загрузчик'. Я в этом плохо смыслю. Но я вижу два метода. Это - 'css' и 'js'.
   Файл стиля работает без всякого загрузчика. Может быть, что для имплеминтации 'js' загрузчик как раз и нужен. Но мне до сих пор неизвестно, 'есть ли эта панель в коде FF 68 в приципе'. А если есть, то каким образом её можно активировать - стилем или сценарием. Поэтому и спрашиваю.

LZAA пишет

Я вчера 'UserChromeFiles загрузчик скриптов/стилей' смотрел и даже пытался установить в FF 68. Но никакого результата не получил. То есть интерфейс не изменился вовсе.

Попробуйте

kokoss

Спасибо за файл. Это работает.
Провёл некоторую 'чистку'. Сейчас у меня в директории 'user_chrome_files' имеется только один файл - 'user_chrome.manifest'. Но возможность активировать ещё 3 панели осталась. Это каким-то хитрым образом делает 'config.js' в директории 'Firefox'. Прямых указаний на 'toolbar' в сценарии нет. Из этого факта я делаю вывод о том, что панели таки есть в коде FF 68, а сценарий их активирует.
   Можно ли уменьшить высоту панели приложений (нижней) стилем?

Провёл некоторую 'чистку'. Сейчас у меня в директории 'user_chrome_files' имеется только один файл - 'user_chrome.manifest'. Но возможность активировать ещё 3 панели осталась.

Можно подумать: «что за бред сивой кобылы?».
Однако, я смог воспроизвести это!


Одна укфская кнопка потерялась, а у остальных пропали иконки
(кроме как у кнопки скрытия нижнего тулбара). Но сами тулбары остались!

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

Выделить код

Код:

data:image/png;base64,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


Весьма наглядный пример того, что такое startupCache.

LZAA пишет

Можно ли уменьшить высоту панели приложений (нижней) стилем?

Попробуйте так:

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

Выделить код

Код:

#add-additional-bottom-bar {
    --toolbarbutton-outer-padding: 1px !important;
    --toolbarbutton-inner-padding: 2px !important;
}

#add-additional-bottom-closebutton {
    padding: 0px !important;
}


оффтоп

Dumby
Забавно. Кэш запуска вы, конечно, очистили?
   
LZAA
Давайте использовать имена собственные. Панель приложений была в [firefox] 52-, а здесь bottom-bar или нижняя панель.
   
Даже официально это был browser-bottombox.

_zt пишет

Кэш запуска вы, конечно, очистили?

Ну, под конец, конечно, очистил. Чтобы посмотреть как тулбары исчезнут.


А сначала, напротив, даже не дышал на него.
Ведь суть эксперимента была именно в том, выдержит ли startupCache
столь эпическое обескровливание UCF, или это просто прогон. Оказалось правда.

_zt

   Всё правильно. Названия разные. Беда лишь в том, что я это только сейчас от Вас узнал.

   Про 'startupCache'.
   Действительно, если его очистить, то настройка пропадает. Теперь поиски 'источника' панели буду совмещать с такой очисткой.

kokoss

  PM.

LZAA
Полный источник кода kokoss здесь. Вдруг пригодится, я ещё и боковую подобным образом сжал.

xrun1 пишет

Полный источник кода

кочу спросить, кто вам дал право публиковать ссылку...

Farby

Я тоже 'кочу' спросить, у кого здесь следует испрашивать милостивого разрешения на те или иные 'телодвижения'? Не у Вас ли?

31-12-2022 19:31:27
Экспериментальным путём установил, что в директории 'user_chrome_files' по-минимому следует оставить следующее.
   1. Папку 'vertical_top_bottom_bar' с содержимым.
   2. Файлы: 'user_chrome.js' и 'user_chrome.manifest'.
   Панели работают и без 'startupCache'.

Farby пишет

кочу спросить, кто вам дал право публиковать ссылку...

Обоснуйте.

xrun1 пишет

Обоснуйте

Извиняюсь не туда посмотрел

Dumby, что можно подправить в вашем коде для СВ, чтобы срабатывало в боковой панели и библиотеке..?  Загружаю с помощью UCF, поэтому спрашиваю здесь.
Забыл сказать, что поправил document.createElement на document.createXULElement - работает вплоть до 108-ой, только почему-то пункт "Экспорт папки в HTML" размножается:
Export.1672905372.png

LGS пишет

работает вплоть до 108-ой

Вот когда Cu.import() удалят — всё,
BookmarkExporter только дебаггером можно будет достать.

почему-то пункт "Экспорт папки в HTML" размножается

Ну так потому, что код для CB, а не для UCF.
В CB, addEventListener() — это не window.addEventListener()
Лучше воздерживаться от дословного переноса кодов.


А размножается, наверно, потому, что листенер не может
переопределить свой метод handleEvent(),
поскольку перед этим идёт вызов функции addDestructor(), которой в UCF нет.
Соответственно, ошибка и обрыв дальнейшего исполнения.

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

Нее, «подправить» тут не выйдет.
Ведь эти адреса могут быть и во вкладке загружены,
плюс, вроде, "старый" и "новый" UCF по-разному сайдбар видят.


Вот, попробовал перерисовать под custom_script.js

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

Выделить код

Код:

(async topic => {
	var imp = (m, n = m) => Cu.import(`resource://gre/modules/${m}.jsm`, {})[n];
	var exporter = {
		get dps() {
			delete this.dps;
			return this.dps = imp("DownloadPaths");
		},
		get exporter() {
			delete this.exporter;
			return this.exporter = imp("BookmarkHTMLUtils", "BookmarkExporter");
		},
		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, win.PlacesUIUtils.getString("EnterExport"), 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 || doc.createElement).call(doc, "menuitem");
			for(var args of Object.entries({
				label: "Экспорт папки в HTML",
				selection: "folder",
				"node-type": "folder",
				"selection-type": "single|none",
				id: "placesContext_exportFolder",
				oncommand: "exporter.export(parentNode);"
			}))
				menuitem.setAttribute(...args);

			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");

Dumby пишет

Вот, попробовал перерисовать под custom_script.js

Работает и не размножается, включая библиотеку во вкладке. Большое новогоднее "спасибо".

Dumby, не посмотрите этот скриптик:

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

Выделить код

Код:

//
// Копировать ссылку в адресной строке ..........
// https://forum.mozilla-russia.org/viewtopic.php?pid=793188#p793188 .....
//
(async (url, pa = ChromeUtils.import(url).PageActions) => pa.addAction(new pa.Action({

	title: "Копировать ссылку",
	tooltip: "Копировать ссылку",
	iconURL: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAACiSURBVHjapFOBDcQgCNRPN2pnkpl0JpyJFpszvv1K+5KYAHJwgHoRcTPyeQvYNhI91aEMnp51DcIsRVRX32JVa+0Y45HEOaLsTAZ9NeghcK1e5jcD/tmC0gbVQy++lE7KOadrn9aQ4Mddz8AE920MEyCgCRqCLwkQzMzS2nfg2xYwbQtcE/wL/lojXhhWRXSukDn60Wtdmv0/AvTiZ7/zLsAASyfDH6YOUVUAAAAASUVORK5CYII=",

	id: "ucf_copyURL",
	pinnedToUrlbar: true,
	onCommand(e) {
		var gBrowserBundle = {
			GetStringFromName: () => "Скопировано в буфер обмена!"
		};
		var show = eval(`(function ${e.view.ConfirmationHint.show})`);
		var helper = Cc["@mozilla.org/widget/clipboardhelper;1"]
			.getService(Ci.nsIClipboardHelper);

		(this.onCommand = e => {
			var win = e.view;
			var uri = win.gBrowser.selectedBrowser.currentURI;
			helper.copyString(win.gURLBar.makeURIReadable(uri).displaySpec);

			var anchor = win.BrowserPageActions.panelAnchorNodeForAction(this, e);
			show.call(win.ConfirmationHint, anchor, "", {event: e, hideArrow: true});
		})(e);
	},
	onPlacedInUrlbar: node => node.style.setProperty("--urlbar-icon-fill-opacity", ".6", "important")
})))("resource:///modules/PageActions.jsm");

В [firefox] 109 пропала подсказка "Скопировано в буфер обмена!" и ругается в консоли: Uncaught ReferenceError: MozXULElement is not defined.

unter_officer
Да, вижу. Можно вместо (или вместе с)

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

Выделить код

Код:

//
		var gBrowserBundle = {
			GetStringFromName: () => "Скопировано в буфер обмена!"
		};


подкинуть подобного же фейка, типа
скрытый текст

Выделить код

Код:

//
		var MozXULElement = {insertFTLIfNeeded() {}};
		var document = {l10n: {setAttributes: msg => msg.textContent = "Скопировано в буфер обмена!"}};


Ну, или править уже сам код для eval()

Dumby, большое спасибо!

Dumby посмотрите пожалуйста эти 2 кнопки что то они не работают в [firefox] 109.0

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

Выделить код

Код:

// Switch Keyboard Layout
try {(keybUtils => CustomizableUI.createWidget({
    type: "custom",
    id: "SwitchKeyboardLayout",
    onBuild(doc) {
        var btn = doc.createXULElement("toolbarbutton");
        btn.id = this.id;
        btn.label = btn.tooltipText = "Switch Keyboard Layout";
        btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";

        btn.setAttribute("oncommand", "linkedObj.switch(document);");
        btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
        btn.linkedObj = this;
        return btn;
    },
    switch(doc) {
        var br = doc.activeElement;
        br && br.localName == "browser" && br.isRemoteBrowser
            ? br.messageManager.loadFrameScript(this.url, false)
            : this.keybUtils.switchSelKeybLayout();
    },
    get url() {
        delete this.url;
        return this.url = `data:;charset=utf-8,(${
            encodeURIComponent(keybUtils)
        }).switchSelKeybLayout()`;
    },
    get keybUtils() {
        delete this.keybUtils;
        var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
        var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
        Services.scriptloader.loadSubScript(url, this);
        var {id} = this;
        this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
            var window = Services.focus.activeWindow, {document} = window;
            var button = document.getElementById(id);
            if(
                !_focusFixed
                && "closeMenus" in window
                && document.commandDispatcher.focusedElement == button
            ) {
                window.closeMenus(button);
                window.setTimeout(function(_this) {
                    _this.switchSelKeybLayout(_subCall, true);
                }, 0, this);
                return;
            }
            return document.commandDispatcher.focusedElement;
        }
        return this.keybUtils;
    }
}))(`{
    //== Options
    noSelBehavior: { // Shift+Home
        ctrlKey:  false,
        altKey:   false,
        shiftKey: true,
        metaKey:  false,
        keyCode:  KeyEvent.DOM_VK_HOME,
        charCode: 0
    },
    // 0 - do nothing
    // 1 - convert all text
    // Or use object like following to simulate "keypress" event:

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            var primary = c in this.convTableForward;
            if(primary ^ c in this.convTableBackward)
                return primary;
        }
        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(node instanceof Element.isInstance(x)) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
        var e = this.noSelBehavior;
        if(!e || typeof e != "object")
            return;
		
        if(ChromeUtils.domProcessChild.childID) {
            var cmd = this.beh2cmd[e.ctrlKey + "_" + e.shiftKey + "_" + e.keyCode];
            cmd && docShell.doCommand(cmd);
        }
        else node.dispatchEvent(new node.ownerGlobal.KeyboardEvent(
            "keypress", {bubbles: true, cancelable: true, ...e}
        ));
    },
    beh2cmd: { // Ctrl_Shift_VK
        false_true_36: "cmd_selectLinePrevious", // Shift+Home
    },
    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`)} catch(ex) {Cu.reportError(ex);}

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

Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}

egorsemenov06

Dumby пишет

x instanceof *Element лучше заменить на *Element.isInstance(x)
Там три вхождения (поиск по «instanceof»).

Сделано не только неполно (только третье), да ещё и неправильно.

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

Выделить код

Код:

/*
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
*/
        if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {


		
/*
                if(node instanceof Element.isInstance(x)) {
*/
                if(Element.isInstance(node)) {

Dumby пишет

egorsemenov06

Dumby пишет

x instanceof *Element лучше заменить на *Element.isInstance(x)
Там три вхождения (поиск по «instanceof»).

Сделано не только неполно (только третье), да ещё и неправильно.

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

Выделить код

Код:

/*
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
*/
        if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {


		
/*
                if(node instanceof Element.isInstance(x)) {
*/
                if(Element.isInstance(node)) {

Спасибо большое!!!А что неправильно где не полно я так и не понял заработало и слава Dumby

Dumby, а вы не можете придумать способ, который позволял бы открывать закладки и историю в новой вкладке, когда открыта одна вкладка about:newtab..?  Т.е, если поподробнее, то сейчас, когда запускаю фокс, то открыта только вкладка about:newtab, и , если жмякнуть по закладке, например, в боковой панели, то она откроется в текущей вкладке, а не в новой. То же самое и с историей. Меня почему-то это сильно напрягает... нужно, чтобы about:newtab (она же домашняя с плитками)  не "затиралась", а оставалась "нетронутой". Привычка, может и вредная.

20-01-2023 16:14:11
Под способом, естественно, скрипт подразумевается, а не какие-нибудь клавиши и СКМ.

LGS
https://forum.mozilla-russia.org/viewto … 05#p797605

kokoss
Я это пробовал, и это - у меня не работает. Именно после запуска ФФ, когда еще ни одна закладка не открывалась и ничего не посещалось. В этом случае закладка или история откроются в текущей about:newtab вкладке. Если вернуться обратно на about:newtab, то уже  будет открываться в новых вкладках. Насколько я понял - нужна хотя бы одна посещенная вкладка, чтобы фокс начал открывать в новой вкладке. Может, Dumby что-нибудь придумает типа такого как здесь.

Это тоже не сработало.

LGS пишет

а вы не можете придумать способ, который позволял бы открывать закладки и историю в новой вкладке, когда открыта одна вкладка about:newtab..?  Т.е, если поподробнее, то сейчас, когда запускаю фокс, то открыта только вкладка about:newtab, и , если жмякнуть по закладке, например, в боковой панели, то она откроется в текущей вкладке, а не в новой. То же самое и с историей. Меня почему-то это сильно напрягает...

Этот -> https://forum.mozilla-russia.org/viewto … 05#p797605 скрипт так и работает, открывает( кроме дилов, в хотелке  про открытие дилов в новой вкладке нет) закладки и историю в новой вкладке + user_pref("browser.tabs.loadBookmarksInTabs", true);

20-01-2023 21:32:15

LGS пишет

у меня не работает. Именно после запуска ФФ, когда еще ни одна закладка не открывалась и ничего не посещалось. В этом случае закладка или история откроются в текущей about:newtab вкладке.

есть такое, тоже напрягает...

kokoss пишет

Эти скрипты так и работают, открывают закладки и историю в новой вкладке + user_pref("browser.tabs.loadBookmarksInTabs", true);

Работают, если открыта хотя-бы одна вкладка. Если открыта только about:newtab (сразу после запуска фокса), то закладка или история откроются в текущей вкладке (в моем случае это about:newtab).
Вообщем, если не лень, то можно  воспроизвести:
1. Устанавливаем скрипт.
2. Запускаем ФФ.
3. Устанавливаем домашней страницей about:newtab.
4. Убираем (если включено) галку «Открыть предыдущие окна и вкладки» (или, в более старых версиях, «Восстанавливать предыдущую сессию».
5. Закрываем ФФ, запускаем снова. Фокс откроется с одной вкладкой (about:newtab).
6. Жмем, к примеру, закладку в боковой панели - она откроется в текущей вкладке, а не в новой.

20-01-2023 21:41:56

kokoss пишет

есть такое, тоже напрягает...

Ага, значит кое-как смог объяснить, что мне нужно...

LGS пишет

когда запускаю фокс, то открыта только вкладка about:newtab

То есть, ты хочешь забрать эту вкладку себе,
чтобы в неё places-добро не грузилось?


Можно так попробовать, вроде не должно грузиться
пока в эту вкладку не будет загружено что-то другим образом.
Код для custom_script_win.js

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

Выделить код

Код:

(async place => {
	await gBrowserInit.idleTasksFinishedPromise;

	var tabs = gBrowser.visibleTabs;
	if (tabs.length > 1) return;

	var re = /^about:(?:newtab|home)$/;
	var [tab] = tabs, br = tab.linkedBrowser;

	if (re.test(br.currentURI.spec)) br.loadURI = function(url, params) {
		if (
			re.test(this.currentURI.spec) &&
			gBrowser.selectedBrowser == this &&
			Components.stack.formattedStack.includes(place)
		) {
			var e = window.event ||
				Services.focus.focusedWindow.event ||
				Services.wm.getMostRecentWindow("Places:Organizer")?.event;

			if (e) {
				var node, trg = e.target;

				if (trg.nodeName == "treechildren") node = trg.parentNode.selectedNode;
				else if (trg.id == "placesCmd_open") {
					var popup = trg.ownerDocument.getElementById("placesContext");
					node = popup._view.selectedNode || popup.triggerNode._placesNode;
				}
				else node = trg._placesNode;

				if (node) {
					PlacesUIUtils._openNodeIn(node, "tab", window);
					//gBrowser.selectedTab = tab; // force in background
					return;
				}
			}
		}
		delete this.loadURI;
		this.loadURI(url, params);
	}
})("_openNodeIn@resource:///modules/PlacesUIUtils.");

можно  воспроизвести

Не написано про галку «Открыть предыдущие окна и вкладки»
(или, в более старых версиях, «Восстанавливать предыдущую сессию»).


Кстати, можно прицепить что-нибудь к адресу, типа about:newtab#
тогда, вкладка с таким адресом, наверно, не будет рассматриваться как c «blank page url».
Не то же самое, что скрипт, но, при определённом раскладе, может представлять интерес.

Dumby пишет

Кстати, можно прицепить что-нибудь к адресу, типа about:newtab#
тогда, вкладка с таким адресом, наверно, не будет рассматриваться как c «blank page url».
Не то же самое, что скрипт, но, при определённом раскладе, может представлять интерес.

Скрипт почему-то не зашел, а вот ваша мысль про about:newtab# пришлась очень кстати. Назначил домашней about:newtab# - теперь открывает как мне надо в новой вкладке. Единственное, пришлось стили подправить, которые завязаны были на about:newtab, но это мелочи.
Большое спасибо!

Скрипт заработал... из custom_script_all_win.js.

Dumby
Возможно ли сделать кнопочку - удалить (очистить) сессию.

unter_officer

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

Выделить код

Код:

(async ss => {
	ss = ChromeUtils.importESModule(ss).SessionStore;
	await ss.promiseAllWindowsRestored;
	CustomizableUI.createWidget({
		id: "803529",
		localized: false,
		onCreated(btn) {
			var vals, keys = [, "label", "tooltiptext"];
			var disable = () => {
				keys[0] = "disabled";
				vals = [true, "No Last Session", "No last session, nothing to clear"];
			}
			var setState = btn => {
				for(var ind in keys) btn.setAttribute(keys[ind], vals[ind]);
			}
			if (ss.canRestoreLastSession) {
				keys[0] = "oncommand";
				var cmd = `SessionStore.canRestoreLastSession = false;`;
				vals = [cmd, "Clear Last Session", "Clear last session"];

				var qt = "quit-application-granted";
				var ct = "sessionstore-last-session-cleared";

				var destroy = () => {
					Services.obs.removeObserver(obs, ct);
					Services.obs.removeObserver(destroy, qt);
				}
				var obs = () => {
					destroy();
					disable();
					var widget = CustomizableUI.getWidget(this.id);
					for(var win of CustomizableUI.windows) {
						var {node} = widget.forWindow(win);
						setState(node);
						node.removeAttribute("oncommand");
					}
				}
				Services.obs.addObserver(obs, ct);
				Services.obs.addObserver(destroy, qt);
			}
			else disable();

			(this.onCreated = btn => {
				setState(btn);
				btn.setAttribute("image", "chrome://global/skin/icons/close-fill.svg");
			})(btn);
		}
	});
})("resource:///modules/sessionstore/SessionStore.sys.mjs");

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

Dumby
Подскажите пожалуйста.


Понадобился мне [firefox] 78 ESR. Стал подключать UCF-кнопки и столкнулся с такой проблемкой.
Не получается подключить кнопки, которые сделаны в виде JSM'ок.
Пытаюсь подключать так:

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
	{ func: 'ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/Script.jsm");' },
],

Версия UCF крайняя (2021-9-23).
Консоль ничего не говорит.
Это особенность 78 версии или у меня лыжи не едут?

unter_officer пишет

Не получается подключить кнопки, которые сделаны в виде JSM'ок

У меня тоже лыжи не смазанные... правда, версия UCF старая 2021-2-14 и подключал в custom_script.js, как здесь (спойлеры внизу).
Консоль что-то говорит про:
ChromeUtils.domProcessChild.childID || ({
Это на 78esr. На 91esr, 102esr и последней релизной 109 нормально подхватывается.

unter_officer пишет

Не получается подключить кнопки, которые сделаны в виде JSM'ок.

Испёк портабл 78.15.0esr + UCF 2021-9-23
Проставил галку «Включить скрипты:»
[✔] В фоне [System Principal]


Создал в папке custom_scripts файл Script.jsm

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

Выделить код

Код:

var EXPORTED_SYMBOLS = [];
ChromeUtils.import("resource:///modules/CustomizableUI.jsm").CustomizableUI.createWidget({
	id: "is_78esr_special_test",
	label: "TEST: is 78ESR Special?",
	tooltiptext: "TEST: is 78ESR Special?",
	localized: false,
	onCreated(btn) {
		btn._handleClick = function() {
			this.ownerGlobal.Services.prompt.alert(null, null, "Success!");
		}
		btn.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ==");
	}
});


Подключил как у тебя написано.
Результат: кнопка создалась и кликается.


Таким образом, ответ на вопрос «Это особенность 78 версии или у меня лыжи не едут?»
будет такой: нет, 78-я версия ничем здесь не особенная.


LGS пишет

Консоль что-то говорит про:
ChromeUtils.domProcessChild.childID

Правильно говорит, в 78 у ChromeUtils нет свойства domProcessChild
Наверно, можно заменить ChromeUtils.domProcessChild.childID
на ChromeUtils.contentChild

Dumby пишет

Таким образом, ответ на вопрос «Это особенность 78 версии или у меня лыжи не едут?»
будет такой: нет, 78-я версия ничем здесь не особенная.

Оказалось, что дело в самих кнопках.
По вашему совету заменил ChromeUtils.domProcessChild.childID на ChromeUtils.contentChild.
Кнопка LongLeftClick заработала без ошибок.
Кнопка Reload user{Chrome, Content}.css работает, но при нажатии ПКМ все же выдает в консоль TypeError: ChromeUtils.getAllDOMProcesses is not a function.
Кнопка Сохранить страницу или выбранное как HTML появилась, но не работает. Консоль пишет TypeError: cwg.domProcess is undefined.
В Старом about:config я ничего не редактировал. Просто не работает. Консоль пишет: TypeError: L10nRegistry.registerSources is not a function.

Dumby пишет

Наверно, можно заменить ChromeUtils.domProcessChild.childID
на ChromeUtils.contentChild

Подхватился и работает... лыжи едут. Спасибо!

unter_officer пишет

Кнопка Reload user{Chrome, Content}.css работает, но при нажатии ПКМ все же выдает в консоль TypeError: ChromeUtils.getAllDOMProcesses is not a function.

Да, метод добавлен только c Firefox 80+
Без него, разве что process script'ом пытаться пнуть.

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

Выделить код

Код:

var name = "UCFUserCReloader", EXPORTED_SYMBOLS = [name + "Parent", name + "Child"];

var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

var find = function(sheet) {
	return sheet.href == this;
}
var getSheet = (doc, href) =>
	InspectorUtils.getAllStyleSheets(doc).find(find, href);

if (!ChromeUtils.contentChild) {
	var data, url;
	var noop = () => {};

	var getURI = sub => {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append(`userC${sub}.css`);
		return Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL);
	}
	var oncontextmenu = e => e.ctrlKey || e.shiftKey || e.detail != 1 || contextmenu(e);

	var contextmenu = e => {
		var {file, spec} = getURI("ontent");
		var wb = Services.appShell.createWindowlessBrowser();
		var contentSheet = getSheet(wb.document, spec);
		wb.close();

		if (!contentSheet) return oncontextmenu = contextmenu = noop;

		url = spec;
		var child = {moduleURI: __URI__};
		ChromeUtils.registerProcessActor(name, {child, parent: child});
		var ps = `data:,ChromeUtils.contentChild?.getActor("${name}").query()`;

		(contextmenu = async e => {
			if (!file.exists()) return;
			e.preventDefault();
			data = await reload(contentSheet, Object.create(null));
			if (data) for(var p in data) {
				Services.ppmm.loadProcessScript(ps, false);
				return e.view.setTimeout(restyle, 150);
			}
		})(e);
	}
	ChromeUtils.import("resource:///modules/CustomizableUI.jsm").CustomizableUI.createWidget({
		label: "Reload user{Chrome, Content}.css",
		tooltiptext: "L: Reload userChrome.css\nR: Reload userContent.css",

		id: "798278",
		localized: false,
		onCreated(btn) {
			btn._handleClick = this.click;
			btn.oncontextmenu = oncontextmenu;
			btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIpwAACKcBMsYCAwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAMwSURBVEiJ5dbNb5RVFMfxz3mmrYgYE0IN4IaKb4n4UqOwMCYu2JCoQVDiwoUrQ4j0xao7ExITQxRoodEFK1cmRkSE8AeYYKpEFF0YY4SiUSMRQW0sLe0818U8nekwHe3U7vhtnnNOzj3f89znPvderjVFK8npaSWrPSjpElahQzhvyrF4y++t1GqbF7DHOvQLj6Ozrt2EDutYRHAasMK0vXgWWZO0SX/7DlJf6Skp3UWcFeVRV4zG235tCZx6rDPtKLqqAN4XPhROm3DJEiG3LA6aKkZ14jUSKaOd1Gsco6Qz8vRiDDvTFJz63S13AjdVAg7JvBxDzs2RfrE2MP9ijolZijvJXo/h8pmmb5z6LZf7qICWJQNxwP5mM1OnzNdy03V1U0oi2x77y+/Wp16t3CDWgvDKvKGIQZepfO+iWBIpKG9s7HGW0k7dKgsJjsSQffOF1or4srAuS04U9jOp14amYGGgiF2Re6llaEWnMSbPNyl5DtNFQzvnBKdd2oRNhXt4ZvW1rCwfkbInY9jHMeiscBiEJ9Lz2mfSaovgovXC8sI7siAoYsgnMy8JkmPYhhtdbwOV6a9NdVhT69qphYIblPl0FuO2WrimVVVrcu7dZkFqd75q525uBCelqj0tXzRwvSYbwWZ1doOVi4Yad0vVDj81gjPfV+2y9YsGLnlgFrj6p9TA405irPAemwmn/raNqc/D/wO9uXj+bMhXDeDihDledLYt9VmTektb5flxebYgcOpx+yzw0aic3vXgSqY9hdUuOUx6T+WWcX/LUELYh3ZMyqq1G8FxwCl8ULjdVFd6d6tgPQbMfLIwHIPONgVXWs1OFm3Mjt6Rdlg2X2bq0yfsLtzP/OHVq3PqzuPUm/Vj9xz9ZDrcg5F/Bb6gS8mbkq1F6EdlW+IdE03Baae1pEeJb1SuO0vrMvPsPvKRArDaChf8qWRal/BQccBsxnXFiBFTtjS7czW93qYdVurQJZW6SLeK+DaGyofSLh0uGUNHk6F/SfZI3ojh2k41b3DThnrdS+1/nAnjcxzR5mDsdeG/6szrXl2nJX4w4RFJJ3LhF7lzMey3lmtdU/oHaoj4Y/PDRWgAAAAASUVORK5CYII=");
		},
		get click() {
			var {file, spec} = getURI("hrome");
			var chromeSheet = getSheet(Services.wm.getMostRecentWindow(null).document, spec);
			delete this.click;
			return this.click = !chromeSheet ? noop : function() {
				var win = this.ownerGlobal;
				if (win.event?.detail < 2 && file.exists())
					reload(chromeSheet),
					win.setTimeout(restyle, 50);
			}
		}
	});

	var restyle = () => {
		var subst = "u_css_reloader_restyle_substitution";
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		rph.setSubstitution(subst, Services.io.newURI("data:text/css,:root{}"));

		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		(restyle = () => {
			sss.loadAndRegisterSheet(...args);
			sss.unregisterSheet(...args);
		})();
	}
	var reload = async (sheet, obj) => {
		try {var style = await (await fetch(sheet.href)).text();}
		catch {return obj;}

		InspectorUtils.parseStyleSheet(sheet, style);
		if (obj) obj[sheet.href] = style;
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& await reload(rule.styleSheet, obj);
		}
		return obj;
	}
	var UCFUserCReloaderParent = class extends JSProcessActorParent {
		receiveMessage(msg) {
			return msg.name ? url : data;
		}
	}
}
else var UCFUserCReloaderChild = class extends JSProcessActorChild {
	async query() {
		var {sheet} = this;
		if (!sheet) {
			var en = Services.ww.getWindowEnumerator(null);
			if (en.hasMoreElements()) sheet =
				this.sheet = getSheet(en.getNext().document, await this.sendQuery("u"));
		}
		sheet && this.parse(sheet, await this.sendQuery(""));
	}
	parse(sheet, data) {
		var style = data[sheet.href];
		if (!style) return;

		InspectorUtils.parseStyleSheet(sheet, style);
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& this.parse(rule.styleSheet, data);
		}
	}
}

Кнопка Сохранить страницу или выбранное как HTML появилась, но не работает. Консоль пишет TypeError: cwg.domProcess is undefined.

Если бы только это.
Ещё нет nsIFocusManager.focusedContentBrowsingContext
и напрочь отсутствует IOUtils как таковой.


Ладно, можно попробовать что-нибудь подпаять.
Вот первая часть. Остальное, var snap = mainWin => {…} такое же.

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

Выделить код

Код:

var name = "UCFSaveSnapshotToHTML", EXPORTED_SYMBOLS = [name + "Child"];

var fw = win => {
	var gfe = Cc["@mozilla.org/focus-manager;1"]
		.getService(Ci.nsIFocusManager).getFocusedElementForWindow;
	return (fw = win => {
		var res = {};
		var fe = gfe(win, true, res);
		return [res.value, fe];
	})(win);
}

if (!ChromeUtils.contentChild) {
	var click = async function() {
		var win = this.ownerGlobal;

		var bc = win.gBrowser.selectedBrowser.browsingContext, cwg = bc.currentWindowGlobal;
		var fp = picker(win, "", Ci.nsIFilePicker.modeSave);

		if (cwg.isInProcess) {
			var [fwin, fe] = fw(bc.window);
			if (fe?.matches("browser[remote=true]"))
				cwg = fe.browsingContext.currentWindowGlobal;
		}
		var [fileContent, fileName] = cwg.isInProcess
			? snap(fwin)
			: await cwg.contentParent.getActor(name).sendQuery("", cwg.innerWindowId);

		fp.defaultString = fileName;
		fp.appendFilters(fp.filterHTML);
		fp.appendFilters(fp.filterAll);

		await new Promise(fp.open) != fp.returnCancel &&
			win.OS.File.writeAtomic(fp.file.path, new win.TextEncoder().encode(fileContent));	
	}
	ChromeUtils.import("resource:///modules/CustomizableUI.jsm").CustomizableUI.createWidget({
		label: "796961",
		tooltiptext: "796961",

		id: "796961",
		localized: false,
		onCreated(btn) {
			btn._handleClick = click;
			btn.setAttribute("image", "chrome://devtools/skin/images/tool-application.svg");
		}
	});

	var picker = (...args) => {
		ChromeUtils.registerProcessActor(name, {child: {moduleURI: __URI__}});
		return (picker = Components.Constructor(
			"@mozilla.org/filepicker;1", "nsIFilePicker", "init"
		))(...args);
	}
}
else var UCFSaveSnapshotToHTMLChild = class extends JSProcessActorChild {
	receiveMessage(msg) {
		return snap(fw(WindowGlobalChild.getByInnerWindowId(msg.data).browsingContext.window)[0]);
	}
}

В Старом about:config я ничего не редактировал. Просто не работает. Консоль пишет: TypeError: L10nRegistry.registerSources is not a function.

Странно, там же есть старый about:config.
chrome://global/content/config.xhtml


Но если, зачем-то, надо, то можно после строки, содержащей
.import("resource://gre/modules/L10nRegistry.jsm"); дописать

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

Выделить код

Код:

//
		if (!L10nRegistry.registerSources) L10nRegistry = Object.assign(
			Object.create(L10nRegistry),
			{registerSources([s]) {this.registerSource(s);}, removeSources([s]) {this.removeSource(s);}}
		);

Dumby
Кнопочки "Reload user{Chrome, Content}.css" и "Сохранить страницу или выбранное как HTML" теперь отлично работают.
Про то, что в [firefox] 78 в старый about:config можно попасть по адресу chrome://global/content/config.xhtml, я за давностью забыл напрочь.


Огромное спасибо за помощь!

Dumby
Если не сложно, переделайте под [firefox] 78 вот этот скриптик:

HTTP Request Logger

Выделить код

Код:

//
// HTTP Request Logger ..........
// https://forum.mozilla-russia.org/viewtopic.php?pid=794053#p794053 .....
//
(async self => CustomizableUI.createWidget(({
  label: "HTTP Request Logger",
  tooltiptext: "HTTP Request Logger",
  fileName: "http-request-log.txt",
  images: {
    true: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/wAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/Zv+Z/2b/mf9m/5n/AAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ==",
    false: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/AwMD/AAAA/wAAAP/AwMD/AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wMDA/wAAAP8AAAD/wMDA/wAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8DAwP8AAAD/AAAA/8DAwP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP/AwMD/AAAA/wAAAP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/8DAwP/AwMD/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AACsQQAArEEAAKxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQR/4rEEf+KxBH/isQQAArEEAAKxBAACsQQ=="
  },
  id: "ucf-httpRequestLogger",
  localized: false,
  init(pref) {
    var topic = "http-on-modify-request";
    this.toggle = () => Services.prefs.setBoolPref(pref, !this.active);
    var prefObs = () => {
      var val = Services.prefs.getBoolPref(pref, false);
      if (this.active ^ (this.active = val))
        Services.obs[`${val ? "add" : "remove"}Observer`](this, topic);
      this.setBtnsState();
    }
    prefObs();
    Services.prefs.addObserver(pref, prefObs);
    Services.obs.addObserver(function quit(s, t) {
      Services.obs.removeObserver(quit, t);
      Services.prefs.removeObserver(pref, prefObs);
      self.active && Services.obs.removeObserver(self, topic);
    }, "quit-application-granted");
    return self = this;
  },
  onCreated(btn) {
    btn._handleClick = this.toggle;
    btn.setAttribute("image", this.images[this.active]);
  },
  setBtnsState() {this.setBtnsState = () => {
    var img = this.images[this.active];
    var widget = CustomizableUI.getWidget(this.id);
    for(var win of CustomizableUI.windows)
      widget.forWindow(win).node?.setAttribute("image", img);
  }},
  log: "",
  observe(channel) {
    if (!(channel instanceof Ci.nsIHttpChannel)) return;
    this.log += `${
      channel.referrerInfo?.originalReferrer?.spec || "(none)"
    } ${
      channel.requestMethod
    } ${
      channel.URI.spec
    }\r\n`;

    this.busy || this.write();
  },
  write() {
    var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
    file.append(this.fileName);
    var {path} = file;

    var {IOUtils} = Cu.getGlobalForObject(Cu);
    var modes = [{mode: "create"}, {mode: "append"}];
    var unbusy = () => {
      this.busy = false;
      this.log && this.write();
    }
    (this.write = () => {
      this.busy = true;
      var {log} = this;
      this.log = "";
      IOUtils.writeUTF8(path, log, modes[+file.exists()])
        .finally(unbusy);
    })();
  }
}).init("ucf.httpRequestLogger.enabled")))();

unter_officer
Попробуй такой write()

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

Выделить код

Код:

//
  write() {
    var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
    file.append(this.fileName);

    var flags = 0x02 | 0x08 | 0x10; // MODE_WRONLY | MODE_CREATE | MODE_APPEND
    var args = ["@mozilla.org/network/file-output-stream;1", "nsIFileOutputStream", "init"];
    var fos = Components.Constructor(...args).bind(null, file, flags, 0o644, 0);

    var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
    var copier = Cc["@mozilla.org/network/async-stream-copier;1"].createInstance(Ci.nsIAsyncStreamCopier2);

    var ro = {onStartRequest() {}};
    ro.onStopRequest = () => {
      this.busy = false;
      this.log ? this.write() : sis.setData("", 0);
    };
    (this.write = () => {
      this.busy = true;
      sis.setUTF8Data(this.log);
      this.log = "";
      try {
        copier.init(sis, fos(), null, 0, true, true);
        copier.asyncCopy(ro, null);
      } catch(ex) {
        this.toggle();
        ro.onStopRequest();
        Services.prompt.alert(null, this.label, ex.message);
      }
    })();
  }

Dumby
Большое спасибо! Всё отлично работает.

Dumby Посмотрите пожалуйста кнопку Переключить Куки на ней не отображаеться иконка в [firefox] 110.0

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

Выделить код

Код:

//Переключить Куки
try {(() => {
    var id = "ucf-cookie-toggle",
    label = "Переключить Куки",
    tooltiptext = "ЛКМ: Переключить Куки\nСКМ: Удалить куки домена текущей страницы\nПКМ: Управление куками",
    gpref = "network.cookie.cookieBehavior",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";

    var tbarbtns = {
        initialised: false,
        get network_cookie_cookieBehavior() {
            delete this.network_cookie_cookieBehavior;
            try {
                return this.network_cookie_cookieBehavior = Services.prefs.getIntPref(gpref);
            } catch(e) { }
            return this.network_cookie_cookieBehavior = null;
        },
        init() {
            if (this.initialised) return;
            this.initialised = true;
            Services.prefs.addObserver(gpref, this);
        },
        prefToggleNumber(pref, next) {
            Services.prefs.setIntPref(pref, next[Services.prefs.getIntPref(pref)]);
        },
        getETDL(uri) {
            var eTLD = "";
            try {
                eTLD = Services.eTLD.getBaseDomain(uri);
            } catch (e) {
                try {
                    eTLD = uri.asciiHost;
                } catch (e) {}
            }
            return eTLD;
        },
//
        async viewCookies(win) {
            var func = async sds => {
                await document.documentReadyForIdle;
                var upd, box = document.getElementById("searchBox");
                (upd = site => box.setUserInput(site, box.focus()))(window.arguments[0]);
                var attr = "data-isCurrentSortCol", sel = `treecol[${attr}=true]`;
                window.updSearch = async site => {
                    var sites = sds._sites = await SiteDataManager.getSites();
                    var col = document.querySelector(sel);
                    col.removeAttribute(attr);
                    sds._sortSites(sites, col);
                    sds._buildSitesList(sites);
                    window.focus(upd(site));
                }
            }
            var type = "Browser:SDS";
            var xhtml = (await (await win.fetch("chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml")).text())
                .replace(/(persist=".+)"/, `$1 screenX screenY sizemode" windowtype="${type}"`)
                .replace(/<script .+>/, `$&\n  <script>(${func})(gSiteDataSettings);\n  </script>\n${
                    ["globalOverlay", "editMenuOverlay"]
                        .map(n => `  <script src="chrome://global/content/${n}.js"/>`).join("\n")
                }`);

            var sds = "chrome://ucfsdswnd/content/sds.xhtml";
            this.cr = 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 win => {
                var uri = win.gBrowser.selectedBrowser.currentURI;
                try {
                    var u = win.ReaderMode.getOriginalUrl(uri.spec);
                    if (u) uri = Services.io.newURI(u);
                } catch {}
                uri = this.getETDL(uri);

                await win.SiteDataManager.updateSites();
                var w = Services.wm.getMostRecentWindow(type);
                w ? w.updSearch(uri) : win.openDialog(sds, type, "chrome,dialog=no,resizable", uri);
            })(win);
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
        },
        observe(subject, topic, pref) {
            if (pref == gpref) {
                delete this.network_cookie_cookieBehavior;
                let network_cookie_cookieBehavior = this.network_cookie_cookieBehavior = Services.prefs.getIntPref(pref);
                this.callWithEachWindow(id, {badge: network_cookie_cookieBehavior, badgeStyle: `background: ${network_cookie_cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`});
            }
        },
        uninit() {
            if (!this.initialised) return;
            Services.prefs.removeObserver(gpref, this);
            this.initialised = false;
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild(document) {
            var win = document.defaultView, trbn = document.createXULElement("toolbarbutton");
            trbn.id = id;
            trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional badged-button";
            trbn.setAttribute("badged", "true");
            trbn.setAttribute("constrain-size", "true");
            trbn.setAttribute("label", label);
            trbn.setAttribute("context", "false");
            trbn.setAttribute("tooltiptext", tooltiptext);
            var cookieBehavior = tbarbtns.network_cookie_cookieBehavior;
            if (cookieBehavior !== null) {
                trbn.setAttribute("badge", cookieBehavior);
                trbn.setAttribute("badgeStyle", `background: ${cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`);
                trbn.addEventListener("click", e => {
                    if (e.button == 0)
                        tbarbtns.prefToggleNumber(gpref, [1,2,3,4,5,0]);
                    else if (e.button == 1) {
                        if (!win.gIdentityHandler?._uriHasHost || win.gIdentityHandler._pageExtensionPolicy)
                            return;
                        let baseDomain = win.SiteDataManager.getBaseDomainFromHost(win.gIdentityHandler._uri.host);
                        win.SiteDataManager.hasSiteData(baseDomain).then(hasData => {
                            if (hasData && win.SiteDataManager.promptSiteDataRemoval(win, [baseDomain]))
                                win.SiteDataManager.remove(baseDomain);
                        });
                    } else if (e.button == 2) {
                        e.preventDefault();
                        e.stopPropagation();
                        tbarbtns.viewCookies(win);
                    }
                });
            }
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
                #${id}[badge="2"] {
                    fill: color-mix(in srgb, currentColor 20%, #e31b5d) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            tbarbtns.init();
            return trbn;
        },
        onDestroyed(doc) {
            tbarbtns.uninit();
        },
    });
})();} catch(e) {}

egorsemenov06
Можно сменить протокол svg'шки.
Создать, например, cookie-toggle.svg
допустим, в папке user_chrome_files\custom_scripts\imgs


Содержимое файла взять из data-адреса от «<svg» и до «/svg>» включительно.
Тогда в коде меняем

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

Выделить код

Код:

/*
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";
*/
    img = "chrome://user_chrome_files/content/custom_scripts/imgs/cookie-toggle.svg";


Можно и динамическую хром-регистрацию в код вписать, если очень надо.
Смотрю, и как resource-адрес тоже работает
скрытый текст

Выделить код

Код:

/*
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";
*/
    img = (rph => {
        var subst = "ucf-cookie-toggle-btn-img";
        rph.setSubstitution(subst, Services.io.newURI(
            "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>"
        ));
        return `resource://${subst}/`;
    })(Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler));


Ну, или включить настройку svg.context-properties.content.enabled
Но, я не знаю почему она дефолтно выключена,
может тому есть какие-то веские причины.

Dumby пишет

Ну, или включить настройку svg.context-properties.content.enabled Но, я не знаю почему она дефолтно выключена,может тому есть какие-то веские причины.

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

egorsemenov06
Опаньки!
Bug 1817071 - Remove -moz-image-region


Попытка наброска реплейсмента, если 16px:


1. После многочисленных trbn.setAttribute(…) — ещё один
trbn.setAttribute("image", img);


2. Правка стиля

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

Выделить код

Код:

/*
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
*/
                #${id} image {
                    --h: 16px;
                    height: var(--h); !important;
                    object-fit: cover !important;
                    object-position: top !important;
                }
                #${id}[badge="0"] image {
                    object-position: 0 calc(-1 * var(--h)) !important;
                }

Dumby пишет

egorsemenov06Опаньки!Bug 1817071 - Remove -moz-image-regionПопытка наброска реплейсмента, если 16px:1. После многочисленных trbn.setAttribute(…) — ещё одинtrbn.setAttribute("image", img);2. Правка стиляскрытый текстВыделить кодКод:/*
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
*/
                #${id} image {
                    --h: 16px;
                    height: var(--h); !important;
                    object-fit: cover !important;
                    object-position: top !important;
                }
                #${id}[badge="0"] image {
                    object-position: 0 calc(-1 * var(--h)) !important;
                }

я включил насройку svg.context-properties.content.enabled и она нормально стала отображаться.Сейчас попробовал правку стиля и иконка опять пропала.Но всеравно Спасибо Вам может в будущем опять такое же получиться а так работает и мать с ней.

Dumby пишет

1. После многочисленных trbn.setAttribute(…) — ещё один
trbn.setAttribute("image", img);


2. Правка стиля

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

Выделить код

Код:

/*
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
*/
                #${id} image {
                    --h: 16px;
                    height: var(--h); !important;
                    object-fit: cover !important;
                    object-position: top !important;
                }
                #${id}[badge="0"] image {
                    object-position: 0 calc(-1 * var(--h)) !important;
                }

У меня в этой кнопке иконка не пропадала. Видимо из-за включенного svg.context-properties.content.enabled. Но на всякий случай фикс все же накатил.
После применения фикса все ОК. Спасибо.

Dumby, скажите, пожалуйста, а нельзя ли с помощью скрипта вернуть пункт в контекстном меню адресной строки "Вставить и перейти" в версиях 91 и выше..?
Как было в старых версиях:
PastGo.1676980660.png
Без него как-то не совсем комфортно... чего-то удобного из функционала не хватает.
Что интересно, в новых версиях ФФ в строке поиска (не адреса) пункт "Вставить и искать" оставили, а адресную почему-то обделили...

LGS пишет

нельзя ли с помощью скрипта вернуть пункт в контекстном меню адресной строки "Вставить и перейти"

Нет. Нельзя «вернуть» то, что не пропадало.

Dumby пишет

Нельзя «вернуть» то, что не пропадало.

Понятно.

Dumby, вы, вроде как, .uc.js машинерию не особо жалуете, но, может посмотрите этот скрипт, чтобы на 110 заработал.
Он показывает закладки(папки) из кнопки избранного, меню, гамбургера в боковой панели. На 102esr еще срабатывал, на 110 уже нет.
Консоль пишет ошибку Uncaught TypeError: PlacesUtils.bookmarks.getFolderIdForItem is not a function в строке  let aFolderItemId = PlacesUtils.bookmarks.getFolderIdForItem(node.itemId);.

LGS пишет

вы, вроде как, .uc.js машинерию не особо жалуете

С чего бы мне её не жаловать? Просто у меня её нет.
И, они разные бывают. Если что-то простое, типа скрит в окно и всё, то ещё ничего.

Но если там своих API понатыкано, то машинерию разворачивать надо,
а это, сам понимаешь, радости мало.

PlacesUtils.bookmarks.getFolderIdForItem is not a function

Может просто так

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

Выделить код

Код:

/*
      let aFolderItemId = PlacesUtils.bookmarks.getFolderIdForItem(node.itemId);
      if (aFolderItemId) {
*/
      if (node.parent.type == node.RESULT_TYPE_FOLDER) {

Dumby пишет

а это, сам понимаешь, радости мало

Поэтому и уточнил сначала... вдруг, желания связываться не будет.

Dumby пишет

Может просто так

Сработало! Только из гамбургера не подхватывает. Но я закладками оттуда не пользуюсь, поэтому и так хорошо.
Спасибо.

Dumby - просьба исправить скрипт, скрывающий панель вкладок с одной вкладкой для FF-110 (на FF97 работало)
плюс используется стиль «Вкладки снизу»: от aris-t2 @import ./css/tabs/tabs_below_navigation_toolbar.css

Выделить код

Код:

// ucf_autohidetabstoolbar.js https://forum.mozilla-russia.org/viewtopic.php?pid=790733#p790733
(this.autohidetabstoolbar = {
	init(that) {
		var newtab = this.newtab = document.querySelector("#tabs-newtab-button");
		if (!newtab) return;
		newtab.addEventListener("animationstart", this);
		that.unloadlisteners.push("autohidetabstoolbar");
		var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
			:root[ucfautohidetabstoolbar="true"] #TabsToolbar:not([customizing]) {
				visibility: collapse !important;
			}
			#main-window[ucfautohidetabstoolbar="true"]:not([customizing]) box > #navigator-toolbox {
				padding-bottom: 0 !important;
			}
			#tabs-newtab-button {
				opacity: 1;
				animation-name: toolbar_visible !important;
				animation-timing-function: step-start !important;
				animation-duration: .1s !important;
				animation-iteration-count: 1 !important;
				animation-delay: 0s !important;
			}
			.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabs-newtab-button,
			.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button {
				opacity: 0;
				animation-name: toolbar_hide !important;
			}
			@keyframes toolbar_visible {
				from { opacity: 0;}
				to {opacity: 1;}
			}
			@keyframes toolbar_hide {
				from {opacity: 1;}
				to {opacity: 0;}
			}
		`);
		windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
	},
	handleEvent(e) {
		this[e.animationName]?.();
	},
	toolbar_visible() {
		document.documentElement.setAttribute("ucfautohidetabstoolbar", "false");
	},
	toolbar_hide() {
		document.documentElement.setAttribute("ucfautohidetabstoolbar", "true");
	},
	destructor() {
		this.newtab.removeEventListener("animationstart", this);
	}
}).init(this);
Dobrov пишет

просьба исправить

Не способен.


То есть, атрибуты {first, last}-visible-tab выпилены,
однако, можно записать как-то так

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

Выделить код

Код:

/*
					.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabs-newtab-button,
					.tabbrowser-tab[first-visible-tab="true"][last-visible-tab="true"] ~ #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button {
						opacity: 0;
						animation-name: toolbar_hide !important;
					}
*/
					#tabs-newtab-button {
						animation-name: toolbar_hide !important;
					}
					.tabbrowser-tab:not([hidden]) ~ .tabbrowser-tab:not([hidden]) ~ #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button {
						animation-name: toolbar_visible !important;
					}


и будет срабатывать.
Но что дальше с этим делать — я без понятия.

Dumby
Помогите решить задачку.
При выделении текста на странице, на ряде сайтов, и последующем копировании этого текста (Ctrl+C), в начале текста, или в конце текста, или и там и там, также копируются пробелы.
Надо, что в буфер обмена попал текст без этих пробелов.


Я сам пытался что-то придумать. Вроде бы даже получился рабочий код:

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

Выделить код

Код:

try {
(this.ucf_SelectedTextToClipboard = {
	init(that) {
		document.addEventListener("keydown", this);
	},
	destructor() {
		this.document.removeEventListener("keydown", this);
	},
	handleEvent(e) {
		if (e.ctrlKey && e.code == "KeyC") {

			// выделенный текст из страницы .....
			var selection = document.commandDispatcher.focusedWindow.getSelection();

			// выделенный текст из текстового поля .....
			if ( selection.isCollapsed ) {
				var theBox = document.commandDispatcher.focusedElement;
				if (theBox && (theBox.type == "text" || theBox.type == "textarea" || theBox.type == "textbox" || theBox.type == "input" || theBox.type == "search")) {
					var startPos = theBox.selectionStart;
					var endPos = theBox.selectionEnd;
					selection = theBox.value.substring(startPos, endPos);
				}
			}

			if ( selection !== '' ) {
				setTimeout(()=> selection = gClipboard.read(), 100); // читаем из буфера обмена .....
				setTimeout(()=> selection = selection.toString().trim(), 100); // убираем пробелы .....
				setTimeout(()=> gClipboard.write(selection), 100); // записываем в буфер обмена .....
			}
			else { return; }
		}
	},
}).init(this);
} catch(e) {}

Но мне в этом коде не нравиться цепочка: копировать в буфер -> читать из буфера -> обрезать пробелы -> и снова копировать в буфер. А по другому сделать у меня не получается.
Помогите пожалуйста с кодом.

unter_officer
Прости великодушно, но код — эпический ппц.

Выделить код

Код:

//
	init(that) {
		document.addEventListener("keydown", this);
	},
	destructor() {
		this.document.removeEventListener("keydown", this);
	},

Что ещё за «this.document»? Просто document
И, если имелось в виду, что удалить листенер по выгрузке окна,
то это так не работает. Следует явно указать в init() имя объекта с методом destructor()
that.unloadlisteners.push("ucf_SelectedTextToClipboard");

Выделить код

Код:

//
			// выделенный текст из страницы .....
			var selection = document.commandDispatcher.focusedWindow.getSelection();

			// выделенный текст из текстового поля .....
			if ( selection.isCollapsed ) {

Какой ещё страницы? Какого ещё текстового поля?
Всё это добро находится в другом(!) процессе.
Нету больше прямого доступа к содержимому веб-страниц. И уже давно.

не нравиться цепочка: копировать в буфер -> читать из буфера -> обрезать пробелы -> и снова копировать в буфер.

Ну «копировать в буфер» уже произошло, ведь слушается Ctrl+C,
а чем не нравится «читать из буфера -> обрезать пробелы -> и снова копировать в буфер»
я не понял, разве есть другие варианты?


И, код, вроде как, под UCF, но используется объект gClipboard,
а его добавляет в окно CB, то есть, если неиметь/удалить/выключить CB,
то код просто не будет работать.

Dumby пишет

Прости великодушно, но код — эпический ппц.

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


Dumby пишет

И, код, вроде как, под UCF, но используется объект gClipboard

Вот, меня терзали смутные сомнения, что в UCF вроде как не должно быть gClipboard.
Но вспомнить, почему не должно быть, я так и не смог.



Не знаю, правильно или нет..... в общем, получился у меня вот такой код:

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

Выделить код

Код:

try {
(this.ucf_SelectedTextToClipboard = {
	init(that) {
		document.addEventListener("keydown", this);
	},
	handleEvent(e) {
		if (e.ctrlKey && e.code == "KeyC") {

			var write, read = () => {
				var cb = Services.clipboard, gc = cb.kGlobalClipboard, fl = "text/plain";
				var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
				write = str => ch.copyStringToClipboard(str, gc);

				var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
				trans.init(null);

				return (read = () => {
					trans.addDataFlavor(fl);
					cb.getData(trans, gc);
					var res = {};
					trans.getTransferData(fl, res);
					res = res.value?.QueryInterface(Ci.nsISupportsString).data;
					trans.removeDataFlavor(fl);
					return res;
				})();
			}

			var selection = '';
			setTimeout(()=> selection = read(), 100); // читаем из буфера обмена .....
			setTimeout(()=> selection = selection.toString().trim(), 100); // убираем пробелы с обоих концов текста .....
			setTimeout(()=> write(selection), 100); // записываем в буфер обмена .....
		}
	},
}).init(this);
} catch(e) {}

В нем я использовал вашу функцию из другой кнопки. Вроде работает.
На большее, в данном вопросе, я пожалуй не способен.

Dumby
В кнопке ATB 2021.9.5 перестал работать переход по адресу из буфера обмена, исправьте пожалуйста!

kokoss пишет

В кнопке ATB 2021.9.5 перестал работать переход по адресу из буфера обмена, исправьте пожалуйста!

И ещё в кнопке поиск на странице не работает СКМ: Поиск на этой странице из буфера обмена.

kokoss
Попробуй, например, такую правку parent.js

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

Выделить код

Код:

/*
    readFromClipboard(win) {
        var url = "";
        try {
            var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
            var contxt = ("docShell" in win) ? win.docShell.QueryInterface(Ci.nsILoadContext) : win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsILoadContext);
            trans.init(contxt);
            trans.addDataFlavor("text/unicode");
            var clipboard = Services.clipboard;
            clipboard.getData(trans, clipboard.kGlobalClipboard);
            var data = {};
            trans.getTransferData("text/unicode", data, {});
            if (data.value) {
                data = data.value.QueryInterface(Ci.nsISupportsString);
                url = data.data;
            }
        } catch (ex) {}
        return url;
    },
*/
    readFromClipboard() {
        var {getData, kGlobalClipboard: kGK} = Services.clipboard;
        var flavor = `text/${parseInt(Services.appinfo.platformVersion) >= 111 ? "plain" : "unicode"}`;
        var transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
        transferable.init(null);
        transferable.addDataFlavor(flavor);

        return (this.readFromClipboard = () => {
            try {
                getData(transferable, kGK);
                var out = {};
                transferable.getTransferData(flavor, out);
                var url = out.value.QueryInterface(Ci.nsISupportsString).data;
            }
            finally {
                transferable.setTransferData(flavor, null);
                return url || "";
            }
        })();
    },

Dumby пишет

Попробуй, например, такую правку parent.js
скрытый текст

Благодарю!


voqabuhe пишет

И ещё в кнопке поиск на странице не работает СКМ: Поиск на этой странице из буфера обмена.

В этой кнопке работает только ЛКМ.

kokoss пишет

В этой кнопке работает только ЛКМ.

Эта кнопка? https://imgsh.net/a/LYEkjcn.png
У меня все пункты работают.

Dumby пишет

Попробуй, например, такую правку parent.js

Спасибо.

kokoss пишет

В этой кнопке работает только ЛКМ.

Но теперь то всё работает.

xrun1 пишет

У меня все пункты работают.

В смысле после правки от Dumby работают, или без неё работают?

voqabuhe пишет

Но теперь то всё работает.

Да, после правки вроде всё заработало.

voqabuhe пишет

В смысле после правки от Dumby работают, или без неё работают?

Наверное, после. Я не успел проверить до, сразу сделал правку. :lol:

Dumby
В 113 Nightly перестала работать кнопка LongPress.jsm https://forum.mozilla-russia.org/viewto … 64#p797864, если возможно, исправьте пожалуйста!

darex пишет

LongPress.jsm

lpa и в 111`м не работает, но можно попробовать так

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

Выделить код

Код:

'
				//.embedderElement.ownerGlobal.gBrowser.loadOneTab(link, {
				.embedderElement.ownerGlobal.openLinkIn(link, "tab", {

Farby пишет

но можно попробовать так

Благодарю, так работает.


У кнопки toggle-proxy сломались значки, это возможно исправить?

darex пишет

У кнопки toggle-proxy сломались значки, это возможно исправить?

Вариант

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

Выделить код

Код:

/*
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>",
        imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><circle cy='24' cx='24' style='fill:rgb(146, 69, 101);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /><circle cy='72' cx='24' style='fill:rgb(209, 8, 3);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,89.8 22.3,79.1 36.7,65.9 44,73.2 C 43.5,78.6 41,85.7 33,89.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,67 H 13 L 24,78 35,67' /><circle cy='120' cx='24' style='fill:rgb(243, 135, 37);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,138 22,127 36.7,114 44,121 C 43.5,127 40.9,134 32.8,138 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 34.9,115 H 13.1 L 24,126 34.9,115' /><circle cy='168' cx='24' style='fill:rgb(21, 161, 99);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.9,186 22,175 36.7,162 44,169 C 43.5,175 40.9,182 32.9,186 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,163 H 13 L 24,174 35,163' /><circle cy='216' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,234 22,223 36.7,210 44,217 C 43.5,223 40.9,230 32.8,234 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,211 H 13 L 24,222 35,211' /></g></svg>";
*/
        img = "<rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(RGB);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9'/>",
        imgmenu = "<circle cy='24' cx='24' style='fill:rgb(RGB);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19'/>";
Выделить код

Код:

/*
            get btnstyle() {
                delete this.btnstyle;
                return this.btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                    }
                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                    }
                    #${id}[activated="1"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                    }
                    #${id}[activated="2"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(32px, 16px, 48px, 0px) !important;
                    }
                    #${id}[activated="4"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(48px, 16px, 64px, 0px) !important;
                    }
                    #${id}[activated="5"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(64px, 16px, 80px, 0px) !important;
                    }
*/
            get btnstyle() {
                var rgb = ["146,69,101", "209,8,3", "243,135,37", null, "21,161,99", "0,120,173"];

                var beg = 'list-style-image: url("data:image/svg+xml;charset=utf-8,'
                    + "<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g>";
                    
                var lsi = (ind, isMenu) => `${beg}${(isMenu ? imgmenu : img).replace("RGB", rgb[ind])}</g></svg>") !important;`;

                delete this.btnstyle;
                return this.btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #${id}-button {
                        ${lsi(0)}
                    }
                    #${id}-button-menu {
                        ${lsi(0, true)}
                    }\n${
                        (ws => {
                            var n, arr = [];
                            var r = m => `${ws}#${id}[activated="${n}"] > #${id}-button${m} {\n${ws}    ${lsi(n, m)}\n${ws}}`;
                            for(n of "1245") arr.push(r(""), r("-menu"));
                            return arr.join("\n");
                        })(" ".repeat(20))
                    }

Dumby пишет

Вариант

Большое спасибо, всё исправилось.

Dumby Посмотрите пожалуйста кнопку about_config в ней не ищет выделеное по СКМ в [firefox] 111.0

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

Выделить код

Код:

//about_config..........................................
try {
    ((id, label, tooltiptext, about_config) => {
        CustomizableUI.createWidget({
            id: id,
            type: "custom",
            label: label,
            tooltiptext: tooltiptext,
            localized: false,
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onBuild: function(document) {
                var win = document.defaultView;
                var toolbarbutton = document.createXULElement("toolbarbutton");
                toolbarbutton.id = id;
                toolbarbutton.className = "toolbarbutton-1 chromeclass-toolbar-additional";
                toolbarbutton.setAttribute("label", label);
                toolbarbutton.setAttribute("context", "false");
                toolbarbutton.setAttribute("tooltiptext", tooltiptext);
                toolbarbutton.setAttribute("image", "data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGltYWdlIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgaHJlZj0iZGF0YTppbWFnZS9wbmc7YmFzZTY0LGlWQk9SdzBLR2dvQUFBQU5TVWhFVWdBQUFCQUFBQUFRQ0FNQUFBQW9MUTlUQUFBQUJHZEJUVUVBQUxHUEMveGhCUUFBQUNCalNGSk4gQUFCNkpnQUFnSVFBQVBvQUFBQ0E2QUFBZFRBQUFPcGdBQUE2bUFBQUYzQ2N1bEU4QUFBQzRsQk1WRVVhR2hvaUlpSWVIaDRqSXlNciBLeXVIaFliRnhNUXpMekFrSXlNUkVCQnBaMmVtcEtUdzhQQ3RxcXFJaFlablpHYWJtWm9jSEJ5bW82TnJaMmVYbFpiMjl2WUFBQUNtIG9xSnJiWEdrb3FNZkh4ODdORFpBYnBlOHU3dENPejFGVEVpMnVMbTN0N2k2dTd6Mjl2Y2ZIeDRxWTQ5L05WTzR0TE84dDdiZERRM2MgUHpEZVNEcmtMeDdpT0NjQUFBTGtNQ0FlSEJvOFdJbTlFQkhtTGl2eDdlZ2FXNVFQT0Y4ckFBQXRLaWI4L1B5VWs1TXlNREYwY25PYSBtWm5QejgvZDNOek15OHVSa1pIdjcrOWxaR1JMUjBoVVUxTitmSDIvdnI2SGhvYU1pWW1Wa3BOTlRVMnFxS202dDdlenNMQjJjM1czIHRiYTF0TFNOaTR1bXBLU1lscGYzOS9jSkNBbWxvYUswc2JHZm5KMTFjbkhMeTh5OHU3eXdyNitVa3BNaUlpSStPRHFvcEtXMXNySmQgWFYvTXhzR1BqbzZ0cTZ5cXFLa0FBQUJFUFQrbG9hTyt0ck1QVTZ1MHFhU1psWmE3dUxpL3ZyN0F2NzgvT1R0RE96MmNwS2liamFjaiBXOEF0VjN1YnBhYzBYb0ErYlpSdlpZZVdQbEtjS2ltemQzYTVzSzhBQUFBeFpwQThkNlVxYnJ6QUtDRGVjblhETUN6RUJ3TFlFZ3ZiIE95d2ZIeDhBQUFBelpvMUpmYnBpVG9HbUVRK2lEUTNMSEJiZ0xSdjI5L2NmSHg4NmJxb2FXY01zZHRBbVQ0Nm1CUVdsR1JuaGIyK1kgRGczUExTSGdPaXoxOXZjU09Xd1JUTGt5Yzg0VFZwNmpGUlRrZUhpYURnN1hJaDgzVzRrTVE3QVpWYVFnTWxHSUV4R3ZFaEtiRUJEaCBkbmFtRUJEY0t5ckcyUFFZVDZrZFQ1a1VUNlFFRHhrQUFBRnpBZ0tkRUJEZmJHdlhMaTZJbGJFS1BKRTBiclVSUG93SEZDQUVBZ0VGIEF3SUlDQWdJQndjQUJ3Y0VCQVFxQlFWb0R3L2RjSENGTVRFTUdqVVVLbFVVRWhFV0V3NFdGeGdYR0JnWEZ4Y1pGeGNYRmhZT0ZoWU4gRmhaR0ZSVTJGeGNKRXhQdDdPeSt2YjM3Ky92cjYrdkJ3TUNPalkvaDRPQjJqN3FHTkZYdmVYVFpSajFWSzJFTE9xRWFacy9aWUdIQiBRa01TUDZiRFFrSVFRcTJtSUNERVJFUWFXc2N3ZE5PcUlpSXllZUd4SHgvRFBqNnJIQnovLy84RVlVVjJBQUFBMlhSU1RsTUFBQUFBIEFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFCb2lnSUIgUHJUbWJBSTZpUVlDTmZoK0VCUXk1R0lGQkkvK2RNSnFBeDdrWVFVRXN2ZmREd1FmNUdFUzVmeEZBd01lNW1PdythbDdLd0lESDhUTSA5VWdDQVFPdnB3Y0hBUUlCQkxQNi92NXlBUUlCQVFPdnlkZTlJUU1CQXFiOTZoY3crdnZ0T1FNRGxQbitSbjMxN1NoODltOEZBUU82IDlzd0xTTkt6bFFJQkRMbjNoRXZYemNBSERBNE5EUThKQ0p6eVAyYVJPa0pOVFUxTlRrVXdkMUVCN0hFdHV3QUFBQUZpUzBkRTlVWFMgRzlzQUFBQUpjRWhaY3dBQUN4SUFBQXNTQWRMZGZ2d0FBQUFIZEVsTlJRZmtBeFlYSmdKSGF0WmFBQUFCRzBsRVFWUVkwd0VRQWUvKyBBRGs2QURzQkFnTUVQQVU5UGo5QUJrRUFRdGxEQjBRSUNVVUtSa2ZhU0VsS0N3QU1TMHhORFU0T1R3OVEyMUZTVTFRUUFGVVJWbGRZIEVsbGFFMXZjWE4xZFhoUUFGVjhXWUdGaUZ4aGpaTjVsMzJZWlp3QVZHbWdiYVdwckhHemdiVzV2Y0IxeEFCVWFGbkllYzNSMTRYWjMgSDNnZ0lTSUFJeVFXZVhvbGUrTGpmQ1o5Zm44bktBQWpKSUNCZ29Qa2hJV0doeW1JaVNvckFDT0tpNHlONWVhT2orZm9rSkVza2kwQSBrNVF1bGVtV2w1aVptcHZxbkowdm5nQ2ZNS0Ryb2FLak1US2s3S1h0cHFjekFEU283cW52cWpXcnJLMnU4Sy9xc0xFQXNyTzA4YlUyIHRoYTNON2k1OHJyenV3Qzh2YjYvd01IQ3c4VEZ4c2ZJOU1uS0FEakx6TTNPejlEUjBkTFQxTlhXMTlqMkVuZHVKeExhcXdBQUFDVjAgUlZoMFpHRjBaVHBqY21WaGRHVUFNakF5TUMwd015MHlNbFF5TXpvek9Eb3dNaXN3TURvd01MMVl5a0FBQUFBbGRFVllkR1JoZEdVNiBiVzlrYVdaNUFESXdNakF0TURNdE1qSlVNak02TXpnNk1ESXJNREE2TURETUJYTDhBQUFBQUVsRlRrU3VRbUNDIi8+PC9zdmc+");
                toolbarbutton.addEventListener("click", function(event) {
                    if (event.button == 0)
                        win.gBrowser.selectedTab = about_config.addTab(win, "about:config",  {
                            relatedToCurrent: true,
                        });
                    else if (event.button == 1)
                        about_config.openSelectedConfigTab(win);
                    else if (event.button == 2) {
                        event.preventDefault();
                        event.stopPropagation();
                        win.gBrowser.selectedTab = about_config.addTab(win, "about:about",  {
                            relatedToCurrent: true,
                        });
                    }
                });
                return toolbarbutton;
            }
        });
    })(
        "ucf-open-about-config",
        "about:config",
        "ЛКМ: Открыть about:config\nСКМ: Искать выделенное в about:config\nПКМ: Открыть about:about",
        {
            get ClipboardHelper() {
                delete this.ClipboardHelper;
                return this.ClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
            },
            openClipboardConfigTab: function(win, clip = "", copy) {
                var filter = this.readFromClipboard(win), nowarn = false, pref = "browser.aboutConfig.showWarning";
                if (copy && clip != filter)
                    this.ClipboardHelper.copyString(clip);
                if (Services.prefs.getBoolPref(pref, false)) {
                    Services.prefs.setBoolPref(pref, false);
                    nowarn = true;
                }
                var browser = win.gBrowser.getBrowserForTab(win.gBrowser.selectedTab = this.addTab(win, "about:config", {
                    relatedToCurrent: true,
                }));
                browser.addEventListener("pageshow", (e) => {
                    var doc = e.currentTarget.contentDocument, input = (doc && doc.querySelector("input#about-config-search"));
                    if (input && filter) {
                        input.value = filter;
                        input.dispatchEvent(new doc.defaultView.Event("input", { bubbles: true }));
                    }
                    if (nowarn)
                        setTimeout(() => {
                            Services.prefs.setBoolPref(pref, true);
                        }, 200);
                }, { once: true });
            },
            openSelectedConfigTab: function(win) {
                var clip = this.readFromClipboard(win);
                win.goDoCommand("cmd_copy");
                setTimeout(() => {
                    this.openClipboardConfigTab(win, clip, true);
                }, 100);
            },
            addTab: function(win, url, params = {}) {
                params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
                return win.gBrowser.addTab(url, params);
            },
            readFromClipboard: function(win) {
                var url = "";
                try {
                    var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
                    var contxt = ("docShell" in win) ? win.docShell.QueryInterface(Ci.nsILoadContext) : win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation).QueryInterface(Ci.nsILoadContext);
                    trans.init(contxt);
                    trans.addDataFlavor("text/unicode");
                    var clipboard = Services.clipboard;
                    clipboard.getData(trans, clipboard.kGlobalClipboard);
                    var data = {};
                    trans.getTransferData("text/unicode", data, {});
                    if (data.value) {
                        data = data.value.QueryInterface(Ci.nsISupportsString);
                        url = data.data;
                    }
                } catch (ex) { }
                return url;
            },
        }
    );
} catch(e) {}

egorsemenov06 пишет

Посмотрите пожалуйста кнопку

Не буду.


Про readFromClipboard() обсуждалось отсюда,
но у тебя в коде торчат "text/unicode".
Хоть бы unicode на plain поменял.

Dumby пишет

egorsemenov06 пишетПосмотрите пожалуйста кнопкуНе буду.Про readFromClipboard() обсуждалось отсюда,но у тебя в коде торчат "text/unicode".Хоть бы unicode на plain поменял.

Спасибо что ткнули носом!!!

Здравствуйте, можно ли сделать что-то подобное. Как на сайте translit .cc
В реальном времени вводишь латиницей, а он переводит сразу же в кириллицу в формах ввода. Можно с окном, типа блокнота. Если сложно, то ладно.

17-03-2023 13:05:32
Есть у кого user_chrome_files последней версий?

b0ttle пишет

Есть у кого user_chrome_files последней версий?

https://disk.yandex.ru/d/B5q-UMpu8x2H6Q

Кто знает, как изменить размер vertical-toolbar, какой-то он широкий. Или в каком файле эти настройки хранятся. Последняя user-chrome-files которая в шапке. Кстати, мне кажется, или там есть проблемы с auto_hide_sidebar и special_widgets. Ну при них, кнопка не работает открытия закладки. При этом не всегда реагирует на открытие, не октрывает автоматом. В общем, убрал их. Только мешаются. Наверно в новых версиях их нужно править.

b0ttle
Примерно так, стилем. Родной файл vertical_top_bottom_bar.css лучше не трогать.

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

Выделить код

Код:

/* Сжать доп.панели https://forum.mozilla-russia.org/viewtopic.php?pid=775867#p775867 */
#ucf-additional-top-bar,
#ucf-additional-bottom-bar {
    --toolbarbutton-outer-padding: 2px !important; /* было 0px */
    --toolbarbutton-inner-padding: 2px !important;
    min-height: 18px !important;
}
:-moz-any(#ucf-additional-top-bar,#ucf-additional-bottom-bar) .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}
#ucf-additional-bottom-closebutton {
    padding: 0 !important;
}
#ucf-additional-vertical-bar {
    --toolbarbutton-outer-padding: 2px !important; /* было 0px */
    --toolbarbutton-inner-padding: 3px !important;
    min-width: 18px !important;
}
#ucf-additional-vertical-bar .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}


P.S. По ссылке исходное сообщение. Обратите внимание, название панелей изменились.
P.P.S. special_widgets работают, auto_hide_sidebar не пользуюсь, не знаю. И вообще никаких проблем с открытием закладки.

xrun1
Я тут пока осваиваюсь, непонятно в какой файл и какой код добавлять. Только дошло, что надо редактировать в CustomStylesScripts.jsm, и еще один файл. Смотрел по примеру. До этого делал иначе. Возможно дело в этом. А у вас в special_widgets есть "разделитель" и "свободное пространство"? У меня лишь "растягивающийся интервал". Других почему-то нет.

b0ttle пишет

А у вас в special_widgets есть "разделитель" и "свободное пространство"? У меня лишь "растягивающийся интервал". Других почему-то нет.

Что бы появились в файле CustomStylesScripts.jsm включите пункты;

// { path: "special_widget.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Special Widgets
// { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets

и в настройках user_chrome_files эти настройки;

Стили -> Для докум. всех окон [ChromeOnly]
Скрипты -> Для докум. окна браузера [ChromeOnly]

и  после этого перезапустить браузер с очисткой кэша запуска.

Спасибо за стиль с доп.панелями, помогло. С интервалами тоже решилось.
Не понимаю, что значит по событию. Так же, для всех или одного окна, и [ChromeOnly]. Пример есть, но все равно запутался. Раньше я ложил код, получается в load. А в примере, код обычно в другое ложат. И вот тут и путаница) Можно немного подробнее, если не затруднит.

18-03-2023 20:39:02
kokoss
Нашел ссылку на ваш пример. У вас пустой custom_script.js. В примере dobrov-а, там какой-то код. Если добавлять через CustomStyleScripts.jsm, то получается эти файлы не сильно важны? Для чего тогда они?

b0ttle
В файл custom_script.js добавляются коды кнопок, если не нужны, то можете отключить в файле CustomStylesScripts.jsm  пункт -> { path: "custom_script.js", },

В расширении Async Run Applications 2021.9.7 правка parent.js тоже сработала.
Запуск приложений с аргументом %OpenCurrentURI открывал текущую страницу, а из clipboard с %OpenClipboardURI нет.
Dumby ещё раз спасибо.

Dumby
Я задавал вопрос в теме по CSS, но судя по тому, что там я ответа не получил, скорее всего стилями этого сделать не получится.
Суть вот в чём.
Сейчас новая кнопка "Расширения" прибита намертво.
Возможно ли с помощью скрипта как-то её открепить, чтобы была возможность переместить на любую другую панель?


P.S. Если на любую другую панель очень сложно, то лично меня устроил бы перенос этой кнопки на "Дополнительную панель" из UCF.

unter_officer пишет

Возможно ли с помощью скрипта как-то её открепить, чтобы была возможность переместить на любую другую панель?

Не располагаю сейчас временем задуматься.
Может так сойдёт, надеюсь панель не колапснутая.


custom_script_win.js, "DOMContentLoaded" (не "load").

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

Выделить код

Код:

(async id => {
	if (!Services.prefs.getStringPref("browser.uiCustomization.state").includes(`"${id}"`))
		await delayedStartupPromise;
	var btn = document.getElementById(id);
	btn.setAttribute("removable", true);
	document.getElementById("nav-bar-customization-target").append(btn);
})("unified-extensions-button");

Dumby
Супер! Огромное спасибо!

Можно ли сделать код, который бы открывал ссылки правым кликом? Может есть у кого?
И еще. Кто знает, что делает menubarVisibilityChance.js. Может он не работает?

b0ttle
А расширение не устроит? https://addons.mozilla.org/ru/firefox/a … ght-links/

Dumby, возможно ли адаптировать код, который делает кнопку съемной, под config.js?

xrun1
Он у меня как раз стоит, думал может попроще сделать. Так как пользуюсь лишь этим кликом. По идее, не так важно. Просто хотелка)
А за что отвечают файлы из примера в шапке, не знайте? Один связан с каким-то поиском, другой не ясно. Так и не разобрался.

b0ttle
Там много скриптов. Себе собирал по мере чтения темы и нужности лично мне.

6e73epo пишет

код, который делает кнопку съемной

Не совсем так.
Вопрос был в том, чтобы переместить её в хорошее место.


Она, вроде как, задумана использоваться не только
чтобы в неё мышью тыкать, но ещё и как анкор для каких-то нотификаций.
Поэтому её и скрывают не через display: none.
Ну, это всего лишь я так предполагаю.


Вобщем, не нашёл сходу подходящего топика, поэтому "chrome-document-interactive".

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

Выделить код

Код:

//
(async topic => {
	var id = "unified-extensions-button", qid = `"${id}"`;
	var obs = async doc => {
		var btn = doc.getElementById(id);
		if (!btn) return;
		if (!Services.prefs.getStringPref("browser.uiCustomization.state").includes(qid))
			await doc.ownerGlobal.delayedStartupPromise;
		btn.setAttribute("removable", true);
		doc.getElementById("nav-bar-customization-target").append(btn);
	}
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");

Dumby, а если бы речь шла только о том, чтобы сделать кнопку съемной, например alltabs-button, то в только что приведенном вами коде не достаточно было бы просто сменить id на "alltabs-button" и присутствует что-то лишнее? Если мне не изменяет память, то раньше эта кнопка была съемной, а по какой причине эту возможность убрали, мне не ясно

6e73epo пишет

например alltabs-button, то в только что приведенном вами коде не достаточно было бы просто сменить id на "alltabs-button" и присутствует что-то лишнее?

Ну, почему не достаточно, достаточно.


И да, лишнее, для alltabs-button, определённо, присутствует.
Собственно даже всё, кроме установки атибута "removable".
Иначе говоря
var obs = doc => doc.getElementById("alltabs-button")?.setAttribute("removable", true);

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

6e73epo
У меня что-то не открепляет, после перезапуска возвращает на место. Создал файл, вписал в scriptschrome(не load). А он не слушает. Вы же про кнопку Расширения?

Dumby пишет

Иначе говоря
var obs = doc => doc.getElementById("alltabs-button")?.setAttribute("removable", true);

А как использовать, (async () => { ... })(); , или типа того, нужен?

_zt пишет

А как использовать, (async () => { ... })(); , или типа того, нужен?

Искуство быть непонятным?
Мной такая конструкция используется вместо try {…} catch(…) {…}

Dumby
Для ucf используется

Выделить код

Код:

(async () => {
код
})();

для unified-extensions-button

Выделить код

Код:

(async topic => {
код
})("chrome-document-interactive");

А для alltabs-button этого не надо? Просто строку данную выше вписываем?

_zt
Что-то понятнее не стало.


Была просьба перерисовать код для unified-extensions-button
с custom_script_win.js под config.js


Поэтому обсёрвер, и даже async так же используется
по прямому назначению, поскольку внутри есть await


Затем, вдогонку, вопрос, как бы это могло выглядеть уже для alltabs-button
Соответственно, даден ответ, что чуть попроще

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

Выделить код

Код:

(async topic => {
	var obs = doc => doc.getElementById("alltabs-button")?.setAttribute("removable", true);
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");


А если для alltabs-button под custom_script_win.js
то хватит и одной строки
document.getElementById("alltabs-button").setAttribute("removable", true);
Тоже "DOMContentLoaded" (не "load").

Dumby
Я config.js имел ввиду. Спасибо за ответ, исчерпывающе.

Dumby
В [firefox] 112 такой код отказался работать:

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

Выделить код

Код:

try {
(this.ucf_DblclickTabUpPage = {
	init(that) {
		gBrowser.tabContainer.addEventListener("dblclick", this);
	},
	handleEvent(e) {
		if ( e.button == 0 && e.target.matches("tab :scope:not(.tab-close-button):not(.tab-icon-sound), tab") ) {
			e.preventDefault();
			var vert = `javascript:(function(d,scrT){scrT=d.documentElement.scrollTop||d.body.scrollTop;if(scrT>window.innerHeight){localStorage['bmk_'+d.location.href]=scrT;scrollTo(0,0)}else{scrollTo(0,localStorage['bmk_'+d.location.href]||0)}})(document)`;
			gBrowser.loadURI(vert, { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal() });
		}
	},
}).init(this);
} catch(e) {}

В консоли пишет: Uncaught TypeError: 'uri' member of CancelContentJSOptions is not an object.
Не поможете реанимировать.

unter_officer пишет

'uri' member of CancelContentJSOptions is not an object.

Это Bug 1810141 - Loading URIs should be done with URI objects by default, rather than strings, to help avoid unnecessary parsing / fixup / allocations


То есть, либо
gBrowser.loadURI(Services.io.newURI(vert),
либо
gBrowser.fixupAndLoadURIString(vert,

Dumby
Большое спасибо.

Можно реанимировать?

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

Выделить код

Код:

// https://web.archive.org/web/20211130053336/https://forum.mozilla-russia.org/viewtopic.php?pid=785023#p785023 ...
try {(() => {
    var id = "ucf-aom-button",
    label = "Дополнения",
    tooltiptext = "ЛКМ: Меню дополнений\nСКМ: Отладка дополнений\nПКМ: Открыть менеджер дополнений",
    img="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC",
    //img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 24,4.5 18,12 3,23.7 12,32.7 3.9,44.1 7.8,48 H 45 C 46.7,48 48,46.7 48,45 V 26.1 L 34.8,12.9 31.8,12.3 Z'/><path style='fill:white;' d='M 19.88,3 C 16.93,3 14.55,4.662 14.55,6.701 14.63,7.474 15.11,8.438 15.37,8.762 16.59,10.41 16.59,11.44 16.29,12.06 H 6.299 C 4.476,12.06 3,13.53 3,15.35 V 23.68 C 3.625,24 4.65,24 6.299,22.77 6.625,22.52 7.587,22.02 8.363,21.94 10.4,21.94 12.06,24.35 12.06,27.29 12.06,30.24 10.4,32.65 8.363,32.65 7.725,32.63 6.774,32.07 6.299,31.82 4.65,30.59 3.625,30.59 3,30.91 V 41.71 C 3,43.53 4.476,45 6.299,45 H 19.58 C 19.88,44.38 19.88,43.35 18.65,41.71 18.4,41.38 17.91,40.42 17.82,39.65 17.82,37.6 20.23,35.94 23.18,35.94 26.14,35.94 28.55,37.6 28.55,39.65 28.53,40.28 27.97,41.23 27.71,41.71 26.47,43.35 26.47,44.38 26.79,45 H 32.65 C 34.47,45 35.96,43.53 35.96,41.71 V 32.55 C 36.56,32.23 37.59,32.23 39.23,33.47 39.72,33.73 40.68,34.29 41.29,34.29 43.35,34.29 45,31.91 45,28.94 45,25.99 43.35,23.59 41.29,23.59 40.54,23.67 39.58,24.17 39.23,24.41 37.59,25.65 36.56,25.65 35.96,25.33 V 15.35 C 35.96,13.53 34.47,12.06 32.65,12.06 H 23.49 C 23.19,11.44 23.19,10.41 24.41,8.762 24.66,8.287 25.22,7.337 25.23,6.713 25.23,4.662 22.85,3 19.88,3' /></g></svg>",
    checked = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><path d='M 3,7 7,11 13,5' style='fill:none;stroke:white;stroke-width:1;'/></svg>",
    show_version = false,
    show_description = true,
    user_permissions = true,
    show_hidden = true,
    show_disabled = true,
    enabled_first = true,
    exceptions_listset = new Set([

    ]),
    exceptions_type_listset = new Set([

    ]);
    if (!("AddonManager" in this))
        ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
    if (!("GlobalManager" in this))
        XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => {
            const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", null);
            return GlobalManager;
        });
    var extensionOptionsMenu = {
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        get clipboardHelp() {
            delete this.clipboardHelp;
            return this.clipboardHelp = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
        },
        get exceptions_type_listarr() {
            delete this.exceptions_type_listarr;
            var arr = ["extension", "theme", "locale", "dictionary"];
            if (!exceptions_type_listset.size)
                return this.exceptions_type_listarr = arr;
            return this.exceptions_type_listarr = arr.filter(type => !exceptions_type_listset.has(type));
        },
        async populateMenu(e) {
            var popup = e.target, doc = e.view.document;
            var addons = await AddonManager.getAddonsByTypes(this.exceptions_type_listarr);
            var addonsMap = new WeakMap(),
            setAttributesMenu = (mi, addon, extension) => {
                var permissions, uuid,
                props = {
                    label: `${addon.name}${show_version ? ` ${addon.version}` : ""}`,
                    class: "menuitem-iconic",
                    tooltiptext: `${(show_description && addon.description) ? `${addon.description}\n` : ""}ID: ${addon.id}${addon.isActive && (uuid = extension?.uuid) ? `\nUUID: ${uuid}` : ""}${(user_permissions && (permissions = addon.userPermissions?.permissions)?.length) ? `\nРазрешения: ${permissions.join(", ")}` : ""}\n${addon.optionsURL ? "\nЛКМ: Настройки" : ""}\nCtrl+ЛКМ: Копировать ID${uuid ? "\nShift+ЛКМ: Копировать UUID" : ""}${addon.creator?.url ? "\nCtrl+Shift+ЛКМ: Автор" : ""}${addon.homepageURL ? "\nСКМ: Домашняя страница" : ""}${!addon.isBuiltin ? "\nCtrl+СКМ: Просмотр источника" : ""}\nShift+СКМ: Просмотр источника во вкладке\nПКМ: Включить/Отключить${(!addon.isSystem && !addon.isBuiltin) ? "\nCtrl+ПКМ: Удалить" : ""}`,
                };
                for (let p in props)
                    mi.setAttribute(p, props[p]);
                if (addon.iconURL)
                    mi.setAttribute("image", addon.iconURL);
                var cls = mi.classList;
                addon.isActive ? cls.remove("ucf-disabled") : cls.add("ucf-disabled");
                addon.optionsURL ? cls.remove("ucf-notoptions") : cls.add("ucf-notoptions");
                addon.isSystem ? cls.add("ucf-system") : cls.remove("ucf-system");
                cls.add(`ucf-type-${addon.type}`);
            };
            addons.filter(a => !(a.iconURL || "").startsWith("resource://search-extensions/")).sort((a, b) => {
                var ka = `${(enabled_first ? a.isActive ? "0" : "1" : "")}${a.type || ""}${a.name.toLowerCase()}`;
                var kb = `${(enabled_first ? b.isActive ? "0" : "1" : "")}${b.type || ""}${b.name.toLowerCase()}`;
                return (ka < kb) ? -1 : 1;
            }).forEach(addon => {
                if (!exceptions_listset.has(addon.id) &&
                    (!addon.hidden || show_hidden) &&
                    (!addon.userDisabled || show_disabled)) {
                    let extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.append(mi);
                    addonsMap.set(addon, mi);
                }
            });
            var click = e => {
                e.preventDefault();
                e.stopPropagation();
                this.handleClick(e);
            };
            popup.addEventListener("click", click);
            var listener = {
                onEnabled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi)
                        setAttributesMenu(mi, addon, mi._Extension);
                },
                onDisabled: addon => {
                    listener.onEnabled(addon);
                },
                onInstalled: addon => {
                    var extension = GlobalManager.extensionMap.get(addon.id),
                    mi = doc.createXULElement("menuitem");
                    setAttributesMenu(mi, addon, extension);
                    mi._Addon = addon;
                    mi._Extension = extension;
                    popup.prepend(mi);
                    addonsMap.set(addon, mi);
                },
                onUninstalled: addon => {
                    var mi = addonsMap.get(addon);
                    if (mi) {
                        mi.remove();
                        addonsMap.delete(addon);
                    }
                },
            };
            AddonManager.addAddonListener(listener);
            popup.addEventListener("popuphiding", () => {
                AddonManager.removeAddonListener(listener);
                popup.removeEventListener("click", click);
                addonsMap = null;
                for (let item of popup.querySelectorAll("menuitem"))
                    item.remove();
            }, { once: true });
        },
        handleClick(e) {
            var win = e.view, mi = e.target;
            if (!("_Addon" in mi) || !("_Extension" in mi))
                return;
            var addon = mi._Addon, extension = mi._Extension;
            switch (e.button) {
                case 0:
                    if (e.ctrlKey && e.shiftKey) {
                        if (addon.creator?.url)
                            win.gBrowser.selectedTab = this.addTab(win, addon.creator.url);
                    } else if (e.ctrlKey) {
                        this.clipboardHelp.copyString(addon.id);
                        win.setTimeout(() => {
                            this.alertsService.showAlertNotification(`${img}`, "ID в буфере обмена!", addon.id, false);
                        }, 100);
                    } else if (e.shiftKey) {
                        if (extension?.uuid) {
                            this.clipboardHelp.copyString(extension.uuid);
                            win.setTimeout(() => {
                                this.alertsService.showAlertNotification(`${img}`, "UUID в буфере обмена!", extension.uuid, false);
                            }, 100);
                        }
                    } else if (addon.isActive && addon.optionsURL)
                        this.openAddonOptions(addon, win);
                    win.closeMenus(mi);
                    break;
                case 1:
                    if (e.ctrlKey) {
                        if (!addon.isBuiltin)
                            this.browseDir(addon);
                    } else if (e.shiftKey)
                        this.browseDir(addon, win);
                    else if (addon.homepageURL)
                        win.gBrowser.selectedTab = this.addTab(win, addon.homepageURL);
                    win.closeMenus(mi);
                    break;
                case 2:
                    if (!e.ctrlKey) {
                        let endis = addon.userDisabled ? "enable" : "disable";
                        if (addon.id == "screenshots@mozilla.org")
                            Services.prefs.setBoolPref("extensions.screenshots.disabled", !addon.userDisabled);
                        else if (addon.id == "webcompat-reporter@mozilla.org")
                            Services.prefs.setBoolPref("extensions.webcompat-reporter.enabled", addon.userDisabled);
                        addon[endis]({ allowSystemAddons: true });
                    } else if (!addon.isSystem && !addon.isBuiltin) {
                        win.closeMenus(mi);
                        if (Services.prompt.confirm(win, null, `Удалить ${addon.name}?`))
                            addon.uninstall();
                    }
                break;
            }
        },
        openAddonOptions(addon, win) {
            switch (addon.optionsType) {
                case 5:
                    win.BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(addon.id)}/preferences`);
                    break;
                case 3:
                    win.switchToTabHavingURI(addon.optionsURL, true);
                    break;
            }
        },
        browseDir(addon, win) {
            try {
                if (!win) {
                    let file = Services.io.getProtocolHandler("file")
                    .QueryInterface(Ci.nsIFileProtocolHandler)
                    .getFileFromURLSpec(addon.getResourceURI().QueryInterface(Ci.nsIJARURI).JARFile.spec);
                    if (file.exists())
                        file.launch();
                } else
                    win.gBrowser.selectedTab = this.addTab(win, addon.getResourceURI().spec);
            } catch (e) {}
        },
        addTab(win, url, params = {}) {
            params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
            params.relatedToCurrent = true;
            return win.gBrowser.addTab(url, params);
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild(doc) {
            var btn = doc.createXULElement("toolbarbutton"), win = doc.defaultView,
            props = {
                id: id,
                label: label,
                context: "",
                tooltiptext: tooltiptext,
                type: "menu",
                class: "toolbarbutton-1 chromeclass-toolbar-additional",
            };
            for (let p in props)
                btn.setAttribute(p, props[p]);
            btn.addEventListener("click", e => {
                if (e.button == 0)
                    e.view.switchToTabHavingURI("about:debugging#/runtime/this-firefox", true, { ignoreFragment: "whenComparing", triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), });
                else if (e.button == 2)
                    e.view.BrowserOpenAddonsMgr("addons://list/extension");
            });
            var mp = doc.createXULElement("menupopup");
            mp.id = `${id}-popup`;
            mp.addEventListener("contextmenu", e => {
                e.preventDefault();
                e.stopPropagation();
            });
            mp.addEventListener("popupshowing", e => {
                extensionOptionsMenu.populateMenu(e);
            });
            btn.append(mp);
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id}, #${id}-popup menuitem {
                    list-style-image: url("${img}") !important;
                }
                #${id}-popup menuitem::after {
                    display: -moz-box !important;
                    content: "" !important;
                    height: 16px !important;
                    width: 16px !important;
                    padding: 0 !important;
                    border: 1px solid rgb(0, 116, 232) !important;
                    border-radius: 0 !important;
                    background-repeat: no-repeat !important;
                    background-position: center !important;
                    background-size: 16px !important;
                    background-color: rgb(0, 116, 232) !important;
                    background-image: url("${checked}") !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem.ucf-disabled::after {
                    border-color: currentColor !important;
                    background-color: transparent !important;
                    background-image: none !important;
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-disabled > label,
                #${id}-popup menuitem.ucf-notoptions > label {
                    opacity: .6 !important;
                }
                #${id}-popup menuitem.ucf-system > label {
                    text-decoration: underline !important;
                    text-decoration-style: dotted !important;
                }
                #${id}-popup menuitem > label {
                    margin-inline-end: 0 !important;
                }
                #${id}-popup menuitem > .menu-accel-container {
                    display: -moz-box !important;
                    padding: 4px !important;
                    margin: 0 !important;
                    opacity: 1 !important;
                }
                #${id}-popup menuitem > .menu-accel-container .menu-iconic-accel {
                    display: -moz-box !important;
                    margin: 0 !important;
                    height: 8px !important;
                    width: 8px !important;
                    border-radius: 4px !important;
                    background-color: transparent !important;
                    opacity: 1 !important;
                    font-size: 0 !important;
                }
                #${id}-popup menuitem.ucf-type-dictionary > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(227, 27, 93) !important;
                }
                #${id}-popup menuitem.ucf-type-locale > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(48, 172, 55) !important;
                }
                #${id}-popup menuitem.ucf-type-theme > .menu-accel-container .menu-iconic-accel {
                    background-color: rgb(219, 106, 0) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            return btn;
        },
    });
})();} catch (e) {}

b0ttle
https://forum.mozilla-russia.org/viewto … 24#p785024

LGS
Похоже не работает, у меня не подхвотил с "//В фоне [System Principal]". Отдельно в файл, как в примере в шапке. И что-то вообще глухо, даже иконка не появилась. По мне, я что-то не то делаю. Даже старый код перестал показывать иконку. Странно. У вас работает?

del

b0ttle
В моём профиле работает и называется extension_manager_button.js.

xrun1
Во, спасибо. Просто еще до вашего профиля не дошел. Свое еще не перебрал, много чего уже естественно не работает у меня. Давно заходил.
Ваша работает, только как бы решить проблему с этим extensions(значок который не убирается). Можно в стилях убрать, по идее. Ну ладно, с этим сам разберусь наверно. Одной проблемой меньше. Теперь одним кликом вкл/выкл дополнение.

b0ttle пишет

как бы решить проблему с этим extensions(значок который не убирается)

№694 и дальше (решения от ув. Dumby).
P.S. Кнопки, стили, где есть русский язык, сохраняйте обязательно в кодировке 65001 (UTF-8) без BOM. Проблема неработоспособности может быть в этом.

Dumby, вы не могли бы кнопку about:config из Add Toolbar Buttons 2021.9.5 сделать отдельной для UCF..?

LGS
https://forum.mozilla-russia.org/viewto … 55#p795555

_zt, отлично, спасибо. В итоге, ваш вариант сейчас "причесываю".

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

_zt, а причесать то не получается. Теперь не знаю как about:cfg заменить на chrome://user_chrome_files/content/aboutconfig/config.xhtml. Замена в строке aboutoldurl = "about:cfg" ничего не дает. Мне, в принципе, кнопка и нужна была для старого about:config.
В Add Toolbar Buttons адрес легко правится в parent.js. А здесь уперся.

Added: причесал, работает.

LGS
about:cfg это и есть chrome://user_chrome_files/content/aboutconfig/config.xhtml
   
Там для 107+ нужна правка в config.js

Выделить код

Код:

document.getElementById("configDeck").setAttribute("selectedIndex", 0);
вместо
document.getElementById("configDeck").selectedIndex = 0;

Ссылку не сохранил, где то в этой теме, вроде.

_zt пишет

Ссылку не сохранил, где то в этой теме, вроде.

https://forum.mozilla-russia.org/viewtopic.php?pid=802633#p802633

_zt пишет

about:cfg это и есть chrome://user_chrome_files/content/aboutconfig/config.xhtml

Нет, без замены about:cfg на chrome://user_chrome_files/content/aboutconfig/config.xhtml вскрипте от Vitaly V. в вашей редакции (2-й спойлер) не срабатывает. Может это у меня что-то особенное:
Config_old_1.1680353214.png
После замены - открывает старый about:config:
Config_old.1680350187.png

_zt
Давно вижу, а понять не могу. Если ввожу в адресной строке about:cfg, должна открыться старая панель? У Вас это работает?

LGS
xrun1
В config.js два раза заменить надо, и в config.css еще - https://forum.mozilla-russia.org/viewto … 33#p802633 . После изменений кэш загрузки чистить обязательно, для уверенности - вручную.
   
Если нет, то просите Dumby исправить. У меня на 102 работает и из кнопки и из адресной строки/закладки.
   
aboutconfig то точно последний?
aboutconfigOLDv2.zip - в архиве без правок.

_zt пишет

У меня на 102 работает и из кнопки и из адресной строки/закладки

На 102 с пол-пинка завелось... на 111 тоже about:cfg заработал. Что мешало - не пойму, наверное что-то локальное.

_zt
Спасибо, разобрался. У меня стоял старый ucf, когда было это сообщение. Сейчас поправил.

Dumby
Посмотри пожалуйста Add Toolbar Buttons, а то в [firefox] 112 некоторые иконки раздвояйюца.

voqabuhe пишет

Посмотри пожалуйста Add Toolbar Buttons

я, думал что это скрипт, но...

voqabuhe пишет

Посмотри пожалуйста Add Toolbar Buttons, а то в [firefox] 112 некоторые иконки раздвояйюца.

Да, как-то пробовал нарезать тамошние SVG'шки вручную.
Даже собрал комплект для самостоятельной правки.
Текстовая ссылка на страницу загрузки комплекта:

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

Выделить код

Код:

data:text/html;charset=utf-8,<!DOCTYPE html>%0A<html>%0A%09<head>%0A%09%09<title>ATB Bug 1817071</title>%0A%09%09<meta http-equiv="Content-Type" content="text/html; charset=utf-8">%0A%09</head>%0A%09<body style="padding-top: 16vh;">%0A%09%09<center><h1 style="font-family: Consolas, Verdana;">%0A%09%09%09<a%0A%09%09%09%09download="ATB_Bug_1817071.zip"%0A%09%09%09%09href="data:application/x-zip-compressed;base64,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"%0A%09%09%09>%0A%09%09%09%09ATB_Bug_1817071.zip</a>%0A%09%09</h1></center>%0A%09</body>%0A</html>

Можно ли создать скрипт для создания кнопки расширений, как в старых версиях Firefox? Нажал кнопку левой кнопкой мыши -открылась страница управление дополнениями.А больше ничего и не надо..

Dumby пишет

Да, как-то пробовал нарезать тамошние SVG'шки вручную.
Даже собрал комплект для самостоятельной правки.
Текстовая ссылка на страницу загрузки комплекта:
скрытый текст

Благодарю :beer:

doud
Как вариант, использовать кнопку "Дополнения" из Add Toolbar Buttons

doud пишет

Можно ли создать скрипт для создания кнопки

Да, пока что можно, откуда сомнение (?).
Примеров на форуме — [censored] ешь.

Нажал кнопку левой кнопкой мыши -открылась страница управление дополнениями

Это просьба?
Опиcание краткое, но вполне достаточное.
Однако, хорошей практикой было бы, дополнительно,
принести с собой ещё и её id'шник, имя, тултип, и иконку.
Иначе, можешь получить что-то типа

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	id: "804471",
	label: "???",
	tooltiptext: "??? ???",
	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		btn._handleClick = this.click;
		btn.image = "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAATAAAAEwAAABMAAAATAAAAAAAAAAAAAAAAAAAAEwAAABMAAAATAAAAEwAAAAAAAAAAAAAAAAAAAL8AAADEAAAAxgAAAMYAAADGAAAAEwAAAAAAAAAAAAAAvwAAAMQAAADGAAAAxgAAABMAAAAAAAAAAAAAAAAAAAC/wP/M/wD/AP8A/wD/AAAAxgAAABMAAAATAAAAEwAAAMQA/wD/J/9U/wAAAMYAAAATAAAAAAAAAAAAAAAAAAAAv6b/wP+m/8D/VP+G/wAAAMYAAADGAAAAxgAAAMYAAADGAP8A/wD/Lf8AAADGAAAAEwAAABMAAAATAAAAAAAAAL8AAAC/AAAAvwDzAP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AAAAxgAAAMYAAADGAAAAEwAAAAAAAAAAAAAAAAAAAL8A4AD/APMA/wDzAP8A8wD/APMA/wDzAP8A8wD/AP8A/wD/AP8A/wD/AAAAxgAAABMAAAAAAAAAAAAAAAAAAAC/AMwA/wDgAP8A4AD/AOAA/wDgAP8A4AD/AOAA/wDgAP8A4AD/AP8A/wAAAMYAAAATAAAAAAAAAAAAAAATAAAAxACzAP8AzAD/AMwA/wDMAP8AzAD/AMwA/wDMAP8A/wD/AP8A/wD/AP8AAADEAAAADwAAAAAAAAC/AAAAxAAAAMYAoAD/ALMA/wCzAP8AswD/ALMA/wCzAP8AswD/AP8A/wAAAMYAAADEAAAAkQAAAAAAAAAAAAAAvwD/R/8A/0f/AP9H/wCgAP8AoAD/AKAA/wCgAP8AoAD/AKAA/wD/AP8AAADGAAAAEwAAAAAAAAAAAAAAAAAAAL8A/zT/AIYA/wCGAP8AhgD/AIYA/wCGAP8AhgD/AIYA/wCGAP8A7AD/AAAAxgAAABMAAAAAAAAAAAAAAAAAAAC/AP8g/wD/IP8A/yD/AP8g/wD/IP8AegD/AP8g/wD/IP8A/yD/AP8A/wAAAMQAAAATAAAAAAAAAAAAAAAAAAAAvwAAAL8AAAC/AAAAvwAAAL8A/w3/AGcA/wD/Df8AAADGAAAAxAAAAL8AAAC/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/APMA/wDzAP8A8wD/AAAAxAAAABMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQAAAL8AAAC/AAAAvwAAAL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwcOsQYGDrEGAA6xBgACsQYAArEHgAKxB4ACsQcAArEGAAaxBgAOsQYADrEGAA6xBgAesQfgfrEH4P6xB//+sQQ==";
	},
	click() {
		this.ownerGlobal.openTrustedLinkIn("about:addons", "current");
	}
}))();

Dumby пишет

Да, как-то пробовал нарезать тамошние SVG'шки вручную.
Даже собрал комплект для самостоятельной правки.
Текстовая ссылка на страницу загрузки комплекта:

Замечательно. Большое спасибо.

Dumby, Спасибо!Работает без всяких id'шников, имен, тултипов.Только как сделать, чтобы страница дополнений открывалась в новой вкладке?

doud пишет

Работает без всяких id'шников, имен, тултипов.

Ясно, я был непонят, абсолютно вообще.

как сделать, чтобы страница дополнений открывалась в новой вкладке?

Ну, если так ставишь вопрос, то можно заменить в коде current на tab
При правке и отсутствии изменений, следет вспомнить о ручной очистке startupCache

Dumby пишет

Да, как-то пробовал нарезать тамошние SVG'шки вручную.Даже собрал комплект для самостоятельной правки.Текстовая ссылка на страницу загрузки комплекта

А как в этой кнопке поправить иконку?Подскажите пожалуйста

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

Выделить код

Код:

//Переключить Куки
try {(() => {
    var id = "ucf-cookie-toggle",
    label = "Переключить Куки",
    tooltiptext = "ЛКМ: Переключить Куки\nСКМ: Удалить куки домена текущей страницы\nПКМ: Управление куками",
    gpref = "network.cookie.cookieBehavior",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='32'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.5 10.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1h-1v-1M8 .6C8 5 11 8 15.4 8c0 4-3.4 7.4-7.4 7.4S.6 12 .6 8 4 .6 8 .6M12.5 26.5v-1h-1v1h1m-4-4h1v1h-1v-1m0 7h1v-1h-1v1m-6-7h1v1h-1v-1m4 4v-1h-1v1h1m-3 3v-1h-1m3-9h1v1m2-3.5v.5h1V17m2 2.5v1h1v-1h-1m3.5 3h-.5v1h.5m-8.5-3h-1v-1M8 16.6c4 0 7.4 3.4 7.4 7.4S12 31.4 8 31.4.6 28 .6 24 4 16.6 8 16.6'/></svg>";

    var tbarbtns = {
        initialised: false,
        get network_cookie_cookieBehavior() {
            delete this.network_cookie_cookieBehavior;
            try {
                return this.network_cookie_cookieBehavior = Services.prefs.getIntPref(gpref);
            } catch(e) { }
            return this.network_cookie_cookieBehavior = null;
        },
        init() {
            if (this.initialised) return;
            this.initialised = true;
            Services.prefs.addObserver(gpref, this);
        },
        prefToggleNumber(pref, next) {
            Services.prefs.setIntPref(pref, next[Services.prefs.getIntPref(pref)]);
        },
        getETDL(uri) {
            var eTLD = "";
            try {
                eTLD = Services.eTLD.getBaseDomain(uri);
            } catch (e) {
                try {
                    eTLD = uri.asciiHost;
                } catch (e) {}
            }
            return eTLD;
        },
//
        async viewCookies(win) {
            var func = async sds => {
                await document.documentReadyForIdle;
                var upd, box = document.getElementById("searchBox");
                (upd = site => box.setUserInput(site, box.focus()))(window.arguments[0]);
                var attr = "data-isCurrentSortCol", sel = `treecol[${attr}=true]`;
                window.updSearch = async site => {
                    var sites = sds._sites = await SiteDataManager.getSites();
                    var col = document.querySelector(sel);
                    col.removeAttribute(attr);
                    sds._sortSites(sites, col);
                    sds._buildSitesList(sites);
                    window.focus(upd(site));
                }
            }
            var type = "Browser:SDS";
            var xhtml = (await (await win.fetch("chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml")).text())
                .replace(/(persist=".+)"/, `$1 screenX screenY sizemode" windowtype="${type}"`)
                .replace(/<script .+>/, `$&\n  <script>(${func})(gSiteDataSettings);\n  </script>\n${
                    ["globalOverlay", "editMenuOverlay"]
                        .map(n => `  <script src="chrome://global/content/${n}.js"/>`).join("\n")
                }`);

            var sds = "chrome://ucfsdswnd/content/sds.xhtml";
            this.cr = 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 win => {
                var uri = win.gBrowser.selectedBrowser.currentURI;
                try {
                    var u = win.ReaderMode.getOriginalUrl(uri.spec);
                    if (u) uri = Services.io.newURI(u);
                } catch {}
                uri = this.getETDL(uri);

                await win.SiteDataManager.updateSites();
                var w = Services.wm.getMostRecentWindow(type);
                w ? w.updSearch(uri) : win.openDialog(sds, type, "chrome,dialog=no,resizable", uri);
            })(win);
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.setAttribute(a, atr[a]);
                }
        },
        observe(subject, topic, pref) {
            if (pref == gpref) {
                delete this.network_cookie_cookieBehavior;
                let network_cookie_cookieBehavior = this.network_cookie_cookieBehavior = Services.prefs.getIntPref(pref);
                this.callWithEachWindow(id, {badge: network_cookie_cookieBehavior, badgeStyle: `background: ${network_cookie_cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`});
            }
        },
        uninit() {
            if (!this.initialised) return;
            Services.prefs.removeObserver(gpref, this);
            this.initialised = false;
        },
    };
    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild(document) {
            var win = document.defaultView, trbn = document.createXULElement("toolbarbutton");
            trbn.id = id;
            trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional badged-button";
            trbn.setAttribute("badged", "true");
            trbn.setAttribute("constrain-size", "true");
            trbn.setAttribute("label", label);
            trbn.setAttribute("context", "false");
            trbn.setAttribute("tooltiptext", tooltiptext);
            var cookieBehavior = tbarbtns.network_cookie_cookieBehavior;
            if (cookieBehavior !== null) {
                trbn.setAttribute("badge", cookieBehavior);
                trbn.setAttribute("badgeStyle", `background: ${cookieBehavior !== 2 ? "#0074e8" : "#e31b5d"}; color: #ffffff; font-size: 10px; line-height: 10px; box-shadow: none; text-shadow: none; padding-block: 0 1px !important; padding-inline: 2px !important; min-width: 0 !important;`);
                trbn.addEventListener("click", e => {
                    if (e.button == 0)
                        tbarbtns.prefToggleNumber(gpref, [1,2,3,4,5,0]);
                    else if (e.button == 1) {
                        if (!win.gIdentityHandler?._uriHasHost || win.gIdentityHandler._pageExtensionPolicy)
                            return;
                        let baseDomain = win.SiteDataManager.getBaseDomainFromHost(win.gIdentityHandler._uri.host);
                        win.SiteDataManager.hasSiteData(baseDomain).then(hasData => {
                            if (hasData && win.SiteDataManager.promptSiteDataRemoval(win, [baseDomain]))
                                win.SiteDataManager.remove(baseDomain);
                        });
                    } else if (e.button == 2) {
                        e.preventDefault();
                        e.stopPropagation();
                        tbarbtns.viewCookies(win);
                    }
                });
            }
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id} {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                }
                #${id}[badge="0"] {
                    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                }
                #${id}[badge="2"] {
                    fill: color-mix(in srgb, currentColor 20%, #e31b5d) !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            tbarbtns.init();
            return trbn;
        },
        onDestroyed(doc) {
            tbarbtns.uninit();
        },
    });
})();} catch(e) {}

egorsemenov06 пишет

А как в этой кнопке поправить иконку?Подскажите пожалуйста

https://forum.mozilla-russia.org/viewto … 78#p803778

Dumby пишет

Да, как-то пробовал нарезать тамошние SVG'шки вручную.

Когда иконки разделены на отдельные файлы то при первом нажатии иконка не загруженная в память может появлятся с задержкой.
Да и потом зачем переделывать координаты когда это делается проще, пример с
autoplay.svg

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

Выделить код

Код:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<view id="5" viewBox="0 16 16 16"/>
<view id="1" viewBox="0 32 16 16"/>
<g style="fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;">
<path d="M4.4 5.4V1.6H.6m3.4.2C3.1 2.1.6 4 .6 8S4 15.4 8 15.4 15.4 12 15.4 8s-2.9-6-3.8-6.5M4.4 21.4v-3.8H.6m3.4.2C3.1 18.1.6 20 .6 24S4 31.4 8 31.4s7.4-3.4 7.4-7.4-2.9-6-3.8-6.5M4.4 37.4v-3.8H.6m3.4.2C3.1 34.1.6 36 .6 40S4 47.4 8 47.4s7.4-3.4 7.4-7.4-2.9-6-3.8-6.5"/>
<path d="M6.6 4.6 10 8l-3.4 3.4ZM6.6 27.4v-6.8m3 0v6.8M6.6 43.4v-6.8M9.6 36.6 13 40l-3.4 3.4Z"/>
</g>
</svg>

button.css

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

Выделить код

Код:

#b-autoplay {
    list-style-image: url("resource://add_toolbar_buttons/svg/autoplay.svg") !important;
}
#b-autoplay[activated="false"],
#b-autoplay[activated="5"] {
    list-style-image: url("resource://add_toolbar_buttons/svg/autoplay.svg#5") !important;
}
#b-autoplay[activated="1"] {
    list-style-image: url("resource://add_toolbar_buttons/svg/autoplay.svg#1") !important;
}

unter_officer пишет

egorsemenov06 пишетА как в этой кнопке поправить иконку?Подскажите пожалуйстаhttps://forum.mozilla-russia.org/viewto … 78#p803778

у меня после этой правки даже на чистом профиле иконка пропадает а без этой правки двоиться

egorsemenov06 пишет

у меня после этой правки даже на чистом профиле иконка пропадает

Что бы не пропадала, необходимо применить 1. пункт по ссылке что вам дали.

Dumby посмотрите пожалуйста кнопку Save в ней не работают "Сохранить всю страницу как PDF" и "Сохранить всю страницу или выбранное как HTML"

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.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==";

var folderpath="C:\\Users\\Firepox\\Desktop";

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить значок веб-сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Запомнить значок веб-сайта как base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Кодировать изображение(текст.файл) в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить всю страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Сохранить всю страницу или выбранное как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить выделенный текст как txt файл", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { separator: ''},
   { label: "Запомнить изображение как base64, в контекстном меню", value: "Save.WebScreenShotOnImage"},
   { 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", ()=> eval(m.func.toString()));
});
menuPopup.setAttribute("onclick", "event.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;
}
 

function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


function 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.setAttribute('style','position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');doc.documentElement.appendChild(f);var str,script=doc.createElement('script');script.type='text/javascript';for(var name in 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. loadURI(vert, {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
};


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  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, 0600, 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);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

// Сохранить страницу как PDF файл через сервис 'pdfmyurl.com' .............
function savePageToPDF() {
      var loc = gBrowser.currentURI.spec;
   var vert = "http://pdfmyurl.com?url=" + loc;
  
   gBrowser. loadURI(vert, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
   });
};

// Сохранить иконку текущего сайта с диалогом сохранения .............
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 16
	? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
		internalSave(url, null, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
	: internalSave.length == 15
		? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
		: (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
			internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};

//Добавыть в контекстное меню страницы пункт "Запомнить изображение как base64"..........................................................................................
(popup => addEventListener("popupshowing", {
    handleEvent(e) {
        if (this.shouldHide) return;

        var menuitem = document.createXULElement("menuitem");
        menuitem.id = "content-baseItem";
        menuitem.className = "menuitem-iconic";
        menuitem.setAttribute("oncommand", "copyImageAsBase64()");
        menuitem.setAttribute("label", "Запомнить изображение как base64");
        menuitem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ==");
        popup.append(menuitem);
        addDestructor(() => menuitem.remove());

        menuitem.copyImageAsBase64 = () => {
            var {osPid} = gContextMenu.actor.manager.browsingContext.currentWindowGlobal;
            if (osPid == -1) osPid = Services.appinfo.processID;
            for(var ind = 0, len = Services.ppmm.childCount; ind < len; ind++) {
                var pmm = Services.ppmm.getChildAt(ind);
                if (pmm.osPid == osPid) break;
            }
            pmm.loadProcessScript("data:;charset=utf-8," + encodeURIComponent(this.code()), false);
        }
        this.handleEvent = () => menuitem.hidden = this.shouldHide;
    },
    get shouldHide() {
        return !(gContextMenu.onImage && Services.prefs.getBoolPref("Save.WebScreenShotOnImage", false));
    },
    code: () => `(targetIdentifier => {

        var image = ChromeUtils.import("resource://gre/modules/ContentDOMReference.jsm")
            .ContentDOMReference.resolve(targetIdentifier);

        var canvas = image.ownerDocument.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();

        Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper)
            .copyStringToClipboard(base64, Ci.nsIClipboard.kGlobalClipboard);

        Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
            .showAlertNotification(base64, "${self.label}", "Запомнил изображение как base64");
    })(${
        JSON.stringify(gContextMenu.targetIdentifier)
    })`
}, false, popup || 1))(document.getElementById("contentAreaContextMenu"));


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {
    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,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   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/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть страницы как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент страницы как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область страницы как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    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, "Сохранить как…", 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
                ];
                var {length} = wbp.saveURI;
                length >= 9 && splice(args);
                length == 10 && args.splice(3, 0, null);
                wbp.saveURI(...args);
				
		setTimeout(async lp => {
			var d = await Downloads.createDownload({
				source: "about:blank", target: fp.file
			});
			(await lp).add(d);
			d.refresh(d.succeeded = true);
		}, 777, Downloads.getList(Downloads.ALL));				
            });
        }
        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);
    }
    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));
        }
    }`
});

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

Выделить код

Код:

try {CustomizableUI.createWidget({
	id: "ucf-cbbtn-Save",
	tooltiptext: "Сохранить",
	localized: false,
	get initCode() {
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/Save.js"
		));
	},
	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});
		new win.Function(
			"self,_id,cbu,xhtmlns,addDestructor,addEventListener,gClipboard,custombuttonsUtils",
			this.initCode
		).call(
			btn, btn, this.id, this.cbu,
			"http://www.w3.org/1999/xhtml",
			this.addDestructor.bind(btn),
			this.addEventListener.bind(btn),
			this.gClipboard, this.custombuttonsUtils
		);
	},
	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;
	}
});} catch(ex) {Cu.reportError(ex);}

kokoss пишет
egorsemenov06 пишет

у меня после этой правки даже на чистом профиле иконка пропадает

Что бы не пропадала, необходимо применить 1. пункт по ссылке что вам дали.

Сасибо ВСЕМ!!!разобрался

Vitaliy V. пишет

это делается проще

Ух-ты, круть-то какая!
Я даже проверять это сейчас не готов, однако, на первый взгляд,


насколько понятно, сперва меняем рутский width-height,
и его viewBox, в формате {x, y, w, h}, как если бы для одиночного,


затем, добавляем ему неких дочерних <view> со своим viewBox'ом,
и присваиваем им id'шник, который, затем, ref-линкуется в стиле как url("…#id")


P.S. Отче, мы осиротевши без тебя, видишь же, без присмотра, творится бардак.
Разреши попросить хотя-бы рассмотреть возможность вернуться к нам, это был бы праздник.


egorsemenov06 пишет

посмотрите пожалуйста кнопку Save в ней не работают "Сохранить всю страницу как PDF" и "Сохранить всю страницу или выбранное как HTML"

Нет, не буду.
Чтобы посмотреть, сперва должно быть это сделано,
в двух местах, в данном случае. Поиск по loadURI

Dumby пишет
egorsemenov06 пишет

посмотрите пожалуйста кнопку Save в ней не работают "Сохранить всю страницу как PDF" и "Сохранить всю страницу или выбранное как HTML"

Нет, не буду.
Чтобы посмотреть, сперва должно быть это сделано,
в двух местах, в данном случае. Поиск по loadURI

Спасибо большое!!!Заработало!

Dumby
В [firefox] 112 не отображается иконка в кнопке cookiesPermissions. Можно туда хоть какую-нибудь повесить?

xrun1
С добрым утром. :)

Dumby
Видел я это, но ведь это было впереди паровоза релиза... Забыл, извиняюсь.

Dumby, а мне в каком направлении двигаться, если у меня Add Toolbar Buttons 2021.9.5, но с svg(цветными) и button.css от АТВ 2020.8.14..? В 112 иконки стали узкими, некоторые на разноцветные столбики похожие:
svg.1681369165.png

LGS пишет

svg(цветными) и button.css от АТВ 2020.8.14.

выложите на посмотреть

Farby пишет

выложите на посмотреть

https://www.upload.ee/files/15113333/AT … d.rar.html
Там внутри svg и button.css от АТВ 2020 и, на всякий случай, само расширение целиком.

LGS пишет

мне в каком направлении двигаться

Как в каком?
Сам отец-основатель посетил нас,
и рассказал как это по-уму делается.


Например, для «Переключить прокси»

world-network.svg & buttons-for-toolbars-menu.svg

Выделить код

Код:

<!--
<svg xmlns="http://www.w3.org/2000/svg" height="80" width="16" viewBox="0 0 48 240">
-->
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 48 48">
    <view id="1" viewBox="0 48 48 48"/>
    <view id="2" viewBox="0 96 48 48"/>
    <view id="4" viewBox="0 144 48 48"/>
    <view id="5" viewBox="0 192 48 48"/>


button.css

Выделить код

Код:

/************
#b-toggle-proxy {
    list-style-image: url("svg/world-network.svg") !important;
    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
    margin-inline-end: 0 !important;
}

#b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg") !important;
    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
    margin-inline-start: 0px !important;
}

#toolbaritem-b-toggle-proxy[activated="1"] :-moz-any(#b-toggle-proxy,#b-toggle-proxy-menu) {
    -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
}

#toolbaritem-b-toggle-proxy[activated="2"] :-moz-any(#b-toggle-proxy,#b-toggle-proxy-menu) {
    -moz-image-region: rect(32px, 16px, 48px, 0px) !important;
}

#toolbaritem-b-toggle-proxy[activated="4"] :-moz-any(#b-toggle-proxy,#b-toggle-proxy-menu) {
    -moz-image-region: rect(48px, 16px, 64px, 0px) !important;
}

#toolbaritem-b-toggle-proxy[activated="5"] :-moz-any(#b-toggle-proxy,#b-toggle-proxy-menu) {
    -moz-image-region: rect(64px, 16px, 80px, 0px) !important;
}
************/

#b-toggle-proxy {
    list-style-image: url("svg/world-network.svg") !important;
    margin-inline-end: 0 !important;
}
#toolbaritem-b-toggle-proxy[activated="1"] > #b-toggle-proxy {
    list-style-image: url("svg/world-network.svg#1") !important;
}
#toolbaritem-b-toggle-proxy[activated="2"] > #b-toggle-proxy {
    list-style-image: url("svg/world-network.svg#2") !important;
}
#toolbaritem-b-toggle-proxy[activated="4"] > #b-toggle-proxy {
    list-style-image: url("svg/world-network.svg#4") !important;
}
#toolbaritem-b-toggle-proxy[activated="5"] > #b-toggle-proxy {
    list-style-image: url("svg/world-network.svg#5") !important;
}

#b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg") !important;
    margin-inline-start: 0px !important;
}
#toolbaritem-b-toggle-proxy[activated="1"] > #b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg#1") !important;
}
#toolbaritem-b-toggle-proxy[activated="2"] > #b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg#2") !important;
}
#toolbaritem-b-toggle-proxy[activated="4"] > #b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg#4") !important;
}
#toolbaritem-b-toggle-proxy[activated="5"] > #b-toggle-proxy-menu {
    list-style-image: url("svg/buttons-for-toolbars-menu.svg#5") !important;
}

Dumby пишет

Сам отец-основатель посетил нас,
и рассказал как это по-уму делается.

Жаль, неведомо отче, что скуден ум мой науку эту постичь. Живем тем, что люд добрый подскажет, да направит на путь истинный.

Dumby пишет

для «Переключить прокси»

Что-то не подхватываются иконки. В world-network.svg и buttons-for-toolbars-menu.svg прописывал:

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

Выделить код

Код:

<?xml version="1.0" encoding="UTF-8"?>
 <svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 48 48">
    <view id="1" viewBox="0 48 48 48"/>
    <view id="2" viewBox="0 96 48 48"/>
    <view id="4" viewBox="0 144 48 48"/>
    <view id="5" viewBox="0 192 48 48"/>


и с  </svg> в конце тоже не работает.
В buttons.css тяжело что-то напутать.
Правил и вживую в установленном расширении, и в распакованном с последующей заменой. startupCache выжигал каленым железом - не помогло.
Кнопки есть и работают, но без значков:
svg-1.1681391941.png

Добавлено: разобрался, благодаря Farby, как правильно код в svg-ку прописывать. Теперь заработало.

Add, лучше так -> Add Toolbar Buttons 2021.9.5 с правками от Dumby, для firefox 112+

LGS пишет

svg и button.css

или файлы на замену, как Сам отец-основатель рекомендовал

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

Выделить код

Код:

data:application/zip;base64,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

Farby пишет

файлы на замену, как Сам отец-основатель рекомендовал

Снизошла благодать господня, спасибо вам, добрый человек! Просто воткнул содержимое архива в расширение с заменой - заработало:
svg-2.1681394109.png
Даже startupCache чистить не пришлось.
В технологию пока не вникал, кроме того, что закомментировать -moz-image-region: в buttons.css и id(#) в нужных иконках проставить.
А вот как с svg работать до меня не дошло, там явно не метода отца-основателя...

LGS пишет

А вот как с svg работать до меня не дошло, там явно не метода отца-основателя...

Да именно там метода от отца-основателя. Это как, например docx ковырять без word, так и svg без векторного редактора ковырять в блокноте...

Dumby
А для чего нужен файл changes.html ?

После выхода Firefox 110 перестал работать скрипт URL tooltip
https://forum.mozilla-russia.org/viewtopic.php?pid=783755#p783755
Что надо исправить?

doud пишет

После выхода Firefox 110 перестал работать скрипт URL tooltip
https://forum.mozilla-russia.org/viewtopic.php?pid=783755#p783755
Что надо исправить?

У меня в [firefox] 112 работает -> https://forum.mozilla-russia.org/viewtopic.php?pid=792745#p792745

kokos, этот пробовал, тоже не работает...

doud пишет

kokoss, этот пробовал, тоже не работает...

Какую версию UCF используйте ?

А хрен его знает, я в этом не особенно разбираюсь.Года 2021 наверно...
Все остальные скрипты работают без проблем.

kokoss пишет

А для чего нужен файл changes.html ?

Это файл diff, если про linux, но если про вообще это файл в котором отображен лог изменения одной версии и другой.

doud пишет

А хрен его знает, я в этом не особенно разбираюсь.Года 2021 наверно...

Там же есть текстовый файл с названием -> version. У меня в последней версии 2021-9-23 работает.

Farby пишет

но если про вообще это файл в котором отображен лог изменения одной версии и другой.

ясненько...

Версия-2021-6-5

doud пишет

Версия-2021-6-5

Тоже до недавнего времени сидел на этой версии, вламы было обновляться, но пришлось, что и вам советую, скорее всего причина в этом!

скрытый текст
https://www.mediafire.com/file/a4tm1zgcczefr8p/VitaliyVstyle.github.io-master.zip/file

doud пишет

Версия-2021-6-5

Попробуйте код прописать в custom_script_win.js или custom_script_all_win.js. У меня старый UCF только на 78 фоксе, там проверил - работает.
А так, с новым UCF скрипт работает вплоть до 112 версии.

kokoss пишет

https://www.mediafire.com/file/a4tm1zgcczefr8p/VitaliyVstyle.github.io-master.zip/file

на данный момент это уже устарело, слишком много изменений была...

Farby пишет

на данный момент это уже устарело, слишком много изменений был

Это последняя версия комплекта от автора, а что касаймо user_chrome_files, то там вроде незначительные изменения...

Farby пишет

на данный момент это уже устарело, слишком много изменений была...

Farby пишет

опа(,) кочешь опустить до своего уровня, а потом разделить [задавить] своим интеллектом?

Да вроде и стараться особо не надо. )
UCF, о котором шла речь, не устарел, тем более для 102 esr, а именно эта версия у того кому предназначалась ссылка.
Webextensions и темы тоже работают, а extensions изначально предназначены для версий 56-.

_zt пишет

была...

ну наконец нашелся владелец русского языка, который исправит ошибки

_zt пишет

Да вроде и стараться

больше не будет....

16-04-2023 15:21:14

_zt пишет

опа(,) к

программа максимум!!!!!!!!!!!!?????????????????

16-04-2023 15:22:51

Farby пишет

Отредактировано Farby (Сегодня 15:21:14)

в который раз не редактировал................................................................

_zt пишет

Да вроде и стараться особо не надо. )

а так нужно было..???

doud
Скрипт работает в Вашей версии, проверил. Только не быстро. После наведения на ссылку, надо сдвинуть мышку на пару пикселей и подсказка появляется. Он и в новой версии UCF так работает.
P.S. Надо 400 заменить на 100 в UcfTooltipUrlChild.jsm, будет быстрее.

xrun1 пишет

надо сдвинуть мышку на пару пикселей и подсказка появляется

Не работает даже в новом UCF

doud
config.js меняли в папке [firefox]?

xrun1 пишет

UcfTooltipUrlChild.jsm

У меня этого файла нет. скрипт добавлялся в custom_script.js. config.js не менял.

Dumby
было когда-то вот такое творение:

ЕСЛИ ТЕКУЩАЯ ВКЛАДКА ПУСТАЯ, ТО ОТКРЫВАТЬ ЗАКЛАДКИ (ПАПКИ) ПО СКМ В ТЕКУЩЕЙ ВКЛАДКЕ

Выделить код

Код:

// ЕСЛИ ТЕКУЩАЯ ВКЛАДКА ПУСТАЯ, ТО ОТКРЫВАТЬ ЗАКЛАДКИ (ПАПКИ) ПО СКМ В ТЕКУЩЕЙ ВКЛАДКЕ
// FindBar, листание результатов поиска колесиком мыши (Mouse Scroll find)

try {({
	init(tabpanels) {
		var dsp = e => this[e.type](e);
		addEventListener("click", dsp, true);
		tabpanels.addEventListener("wheel", dsp);
		addEventListener("unload", () => {
			removeEventListener("click", dsp, true);
			tabpanels.removeEventListener("wheel", dsp);
		}, {once: true});
	},
	e: {ctrlKey: true, shiftKey: true},
	p: {triggeringPrincipal: document.nodePrincipal},
	s: "menu.bookmark-item,toolbarbutton.bookmark-item[type=menu]",
	k: (k => k in PlacesUIUtils ? k : "_" + k)("openTabset"),
	click(e) {
		if (
			e.button == 1 && isBlankPageURL(gBrowser.currentURI.spec)
			&& !e.ctrlKey && !e.shiftKey && !e.altKey
			&& e.target.matches(this.s) && !gBrowser.webProgress.isLoadingDocument
		) {
			var trg = e.target, pn = trg._placesNode;
			if (!pn) return;
			e.stopPropagation();

			var urls = PlacesUtils.getURLsForContainerNode(pn);
			PlacesUIUtils.openInTabClosesMenu && trg.tagName == "menu" && closeMenus(trg);

			if (urls.length && OpenInTabsUtils.confirmOpenInTabs(urls.length, window))
				gBrowser.loadURI(urls.shift().uri, this.p),
				urls.length && PlacesUIUtils[this.k](urls, this.e, window);
		}
	},
	wheel(e) {
		var findbar = gBrowser.selectedTab._findBar;
		findbar?.matches(":hover") && e.deltaY
			&& findbar.onFindAgainCommand(e.deltaY < 0);
	}
}).init(document.getElementById("tabbrowser-tabpanels"))}
catch(ex) {Cu.reportError(ex);}

тут два кода в одном, просто я их нормально разделить не могу


в FF112 заметил некорректную работу: если при старте браузера открыта начальная (домашняя) страница или я нахожусь на ней при других открытых вкладках, то СКМ по папке вообще ничего не открывает. Но если вместо пустой вкладки активна вкладка уже с чем-то, то всё ок.
Можно пофиксить?

doud
Пробуйте. В архиве 2 версии, старая и новая. Новая слегка правленная (user_chrome.js, vertical_top_bottom_bar.js).

Попробовал все версии, так и не заработало...

doud, а в custom_script_win.js или custom_script_all_win.js..?

doud
Тогда я пас.

LGS пишет

custom_script_win.js или custom_script_all_win.js

Пробовал и туда и сюда...

Inko7 пишет

в FF112 заметил некорректную работу

Снова это, надо полагать.

Уважаемые xrun1, kokoss и LGS! Спасибо за помощь! Методом тыка выяснилось, что скрипт работает только при установки browser.chrome.toolbar_tips в true!
Однако при этом возникают ненужные всплывающие подсказки при наведении на кнопки Firefox, что мне не нужно...Как их убрать?

Dumby пишет
Inko7 пишет

в FF112 заметил некорректную работу

Снова это, надо полагать.

Вроде помогло, спасибо. Потом назад не нужно будет исправлять?

xrun1 пишет

надо сдвинуть мышку на пару пикселей и подсказка появляется. Он и в новой версии UCF так работает.

Странно, у меня появляется без перемещения мышки.

kokoss
У меня появляется (изменил в UcfTooltipUrlChild.jsm задержку с 400 на 100) или не появляется сразу (тогда и надо чуть-чуть дёрнуть мышкой по ссылке). С чем связано не понимаю. Может какие-то скрипты конфликтуют.

скрытый текст
Вообще, чисто визуально, мне больше нравится javascript с моими правками
"Hover Links (v2)"

Выделить код

Код:

// ==UserScript==
// @name          Hover Links (v2)
// @version       2023.04.17
// @namespace     http://userscripts-mirror.org/scripts/show/30002
// @description   A tooltip pops up when hovering over links.
// @include       *
// ==/UserScript==

// define
const popTTDelay = 0; // было 300 amount of time(in ms) after mouseover before popup (0 for immediatly)
var bg_color = "#ffffe0";
var border_color = "#ffd700";
var font_color = "#4682b4";
var font_face = "verdana";
var font_size = "10px"; // sorry, change slyles manually in function create_window   :-)
const offSBRRight = 18; // было 24 horizontal constant. if the popup makes horizontal scrollbars appear, increase this constant until it doesn't (default:18)
const offSBRTop = 18; // было 24 vertical constant. if the popup makes vertical scrollbars appear, increase this constant until it doesn't (default:18)
// variables
var ttH, ttW;

(function() {
  function locate(event)
  {
    var ttWin = find_window();
    if (ttWin) {
      var tempLeft=(event.clientX + window.pageXOffset)+13; // было 9
      var tempTop=(event.clientY + window.pageYOffset)+17; // было 10

    if ((tempLeft+ttW) > (window.innerWidth+window.pageXOffset-offSBRRight)) { //if its too far right
      tempLeft= (window.innerWidth+window.pageXOffset-offSBRRight-ttW-10);
    }

    if((tempTop+ttH) > (window.innerHeight+window.pageYOffset-offSBRTop)) { //if its too far down
      tempTop-=(ttH+offSBRTop);
    }

    if (tempLeft < window.pageXOffset) { //if it is too far left, i.e. it is a super-wide box
      tempLeft=window.pageXOffset+1; //set it all the way to the left
    }
      ttWin.style.top = tempTop + "px";
      ttWin.style.left = tempLeft + "px";
    }
  }

  function find_window()
  {
    return document.getElementById("link_tt");
  }

  function create_window(id, ttTitle)
  {
    var tt_err ='';
    var tt_div = document.createElement("div");
    tt_div.setAttribute("id", "link_tt");
    tt_div.setAttribute("style", "text-align: left; background: " + bg_color + "; border: 1px solid " + border_color + "; padding: 2px; color: " +
                                font_color + "; font-family: " + font_face + "; font-size: " + font_size + "; position: absolute; z-index:1000000; " +
                'padding-left: 8px; padding-right: 8px; padding-top: 3px; padding-bottom: 3px; ' +
                'max-width: 35% !important; line-height: 1.2 !important; width: auto !important; ' +
                '-moz-border-radius: 0.7em !important;' );

    try {var decodedUrl=decodeURIComponent(id.href); }
    catch (err) { tt_err += '<br><font style="font-size: 8px; color: grey;">' + ' bad url )' + '</font>';
            decodedUrl=id.href; } // ?? :)

    var ttUrlHost = decodedUrl.split('/'); ttUrlHost = ttUrlHost[2];
//    var favIco = '';
    // изменил if (ttUrlHost) {favIco="<img style='display: none;' onLoad=this.style.display=''; src=http://"+ttUrlHost+"/favicon.ico>  ";}
//    if (ttUrlHost) {favIco='';} // Закомментировал if, вроде, без иконки не нужно
//    else {ttUrlHost='';}{ttUrlHost='';}
    var dUrlLeng = decodedUrl.length;
    var urlPage = decodedUrl.slice(decodedUrl.indexOf(ttUrlHost) + ttUrlHost.length,decodedUrl.length);
    if (urlPage == '/') { urlPage = ''; } else { urlPage = '<br>' + urlPage; }

    if (ttTitle) { ttTitle = '<font style = "font-size: 13px; color: #8b0000;">' + ttTitle + '</font><br>'; }
    else { ttTitle = ''; }
    // изменил tt_div.innerHTML = ttTitle+favIco +
    tt_div.innerHTML = ttTitle + '<font style = "font-size: 12px; color: #007700;">' + ttUrlHost + '</font>' + urlPage + tt_err; // цвет добавил для домена

    document.body.appendChild(tt_div);
    ttH = tt_div.offsetHeight;
    ttW = tt_div.offsetWidth;
//    if (popTTDelay)
//      {  find_window().style.display='none';
//        window.setTimeout('show_windowTT()', popTTDelay);
//      }
  }

  function show_windowTT()
    {  if (find_window()) { find_window().style.display = ''; }
  }

  function kill_window()
  {
    if (find_window()) find_window().parentNode.removeChild(find_window());
  }

  function create_event(id)
  {
    if (id.title)
    {
      var ttTitle = id.title;
      id.addEventListener("mouseover", function() { create_window(id, ttTitle); }, false);
      id.title=''; // Прибивает всплывашку у ссылки на сайте и делает всплывашку заголовком
    }
    else {id.addEventListener("mouseover", function() { create_window(id); }, false);
    }
    id.addEventListener("mouseout", function() { kill_window(); }, false);
    id.addEventListener("click", function() { kill_window(); }, false); // добавил действие на клик, чтобы окно убивалось
    id.addEventListener("mousemove", function(event) { locate(event); }, true);
  }

  unsafeWindow.show_windowTT = show_windowTT;
  var link = document.getElementsByTagName("a");
  var i;
  for (i = 0; i < link.length; i++)
  {
    create_event(link[i]);
  }
})();


Скрипт ловит ссылки по тегу "a" и отлавливает не все, даже с тегом. И не работает на youtube. У Виталия ловится больше ссылок по matches. В скрипте надо менять var link = document.getElementsByTagName("a");, но пока не представляю как.
Хотел попробовать скрестить (т.е. добавить цветное окно вместо просто "всплывашки" у ссылки в UcfTooltipUrlChild.jsm), но вообще не представляю, как добавить функцию окна и стиль в jsm-ку. Идеи есть, знаний не хватает. Вот и сижу со скриптом, а кнопка от Виталия на всякий случай.

Dumby посмотрите пожалуйста этот крипт

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

Выделить код

Код:

//Google переводчик
try {((label, ...args) => CustomizableUI.createWidget({
    label,
    localized: false,
    tooltiptext: label,
    id: "gtBookmarklet",
    onCommand: e => e.view.gBrowser.loadURI(...args),
    onCreated: btn => btn.image = "data:image/png;base64,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"
}))(
    "Google переводчик",
    `javascript:{var d, b, o, v, p; b = (d = document).body; o = d.createElement('script'); o.setAttribute('src', 'https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit'); o.setAttribute('type', 'text/javascript'); b.appendChild(o); v = b.insertBefore(d.createElement('div'), b.firstChild); v.id = 'google_translate_element'; v.style.display = 'none'; p = d.createElement('script'); p.text = 'function googleTranslateElementInit(){new google.translate.TranslateElement({pageLanguage:""},"google_translate_element");}'; p.setAttribute('type', 'text/javascript'); b.appendChild(p)};void(0);`,
    {triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()}
)} catch(ex) {Cu.reportError(ex);}

я пробывал добавить вот это https://forum.mozilla-russia.org/viewto … 78#p804278 ну ничего у меня не получилось [firefox] 112.0.1

egorsemenov06 пишет

пробывал добавить вот это https://forum.mozilla-russia.org/viewto … 78#p804278 ну ничего у меня не получилось

Хмм, интересно, что там может не получиться?
Нужно, всего-то, например, заменить loadURI на fixupAndLoadURIString


Посмотреть прямо полностью в работе, разумеется, не могу,
это сетевые дела, да ещё и гуглячьи. Но, скажем так:


судя по коду букмарклета, он создаёт и суёт на страницу в элемент <body>
элемент <div> в начало, и два элемента <script> в конец.
После замены — это и происходит (видно в инспекторе).

Dumby пишет

egorsemenov06 пишетпробывал добавить вот это https://forum.mozilla-russia.org/viewto … 78#p804278 ну ничего у меня не получилосьХмм, интересно, что там может не получиться?Нужно, всего-то, например, заменить loadURI на fixupAndLoadURIStringПосмотреть прямо полностью в работе, разумеется, не могу,это сетевые дела, да ещё и гуглячьи. Но, скажем так:судя по коду букмарклета, он создаёт и суёт на страницу в элемент <body>элемент <div> в начало, и два элемента <script> в конец.После замены — это и происходит (видно в инспекторе).
                    Отредактировано Dumby (Сегодня 12:10:14)

я добавлял все вместе с (vert, ну тупенький я
Спасибо Вам заработало!!!

xrun1 пишет

С чем связано не понимаю. Может какие-то скрипты конфликтуют.

На чистом профиле только с этим скриптом без расширений также ?

xrun1
"-moz-border-radius" в Hover Links работает? Вроде border-radius надо.

kokoss
На чистом нормально. Предполагаю, что может гадить один java-скрипт, но, как писал выше, для меня не особенно критично и разбираться с этим не хочется. Убью кучу времени...
PsS. Но со скриптом из поста выше, окно подсказки появляется мгновенно, а здесь с небольшой задержкой.

xrun1 пишет

Хотел попробовать скрестить (т.е. добавить цветное окно вместо просто "всплывашки" у ссылки в UcfTooltipUrlChild.jsm), но вообще не представляю, как добавить функцию окна и стиль в jsm-ку. Идеи есть, знаний не хватает

Если не принципиально в jsm-ке, то можно в scrollbars_tooltips.css немного изменить стиль автора...

тултипы

Выделить код

Код:

/* Тултипы */
tooltip {
    -moz-appearance: none !important;
    appearance: none !important;
}
tooltip[hasbeenopened="true"] {
    color: InfoText !important;
    background: InfoBackground !important;
    border: 1px solid color-mix(in srgb, currentColor 20%, transparent) !important;
    padding: 2px !important;
}
@media (prefers-color-scheme: light), (-moz-toolbar-prefers-color-scheme: light) {
tooltip[hasbeenopened="true"] {
    color: rgb(12, 12, 22) !important;
    background: rgb(245, 245, 255) !important;
    border: 1px solid rgb(204, 204, 214) !important;
}
}
@media (prefers-color-scheme: dark), (-moz-toolbar-prefers-color-scheme: dark) {
tooltip[hasbeenopened="true"] {
    color: rgb(0, 0, 0) !important;
    background-image: linear-gradient(#FFFFDB, yellow) !important; 
    border-color: rgb(64, 64, 75) !important;
    border-radius: 1px !important;
    font-size: 15px !important;
}
}
tooltip[hasbeenopened="true"] :is(description,label) {
    color: inherit !important;
}

kokoss
Спасибо. Я видел подобный код на форуме, но у себя не сохранил.

xrun1 пишет

Я видел подобный код на форуме, но у себя не сохранил.

Он из комплекта "full_theme".

kokoss
Я это понял, но у меня своя тема. Лёгких путей не ищем!

Dumby, имеется такой скрипт от немецких камрадов для версии 112, показывает количество закладок/папок (можно наоборот) в меню и кнопке избранного,  здесь вариант для версий ниже 112. Можно на его основе что-то придумать, чтобы подобное работало  в боковой панели..?

Открытие "about:addons" по ПКМ на кнопке нового меню дополнений 111+

Выделить код

Код:

// Открытие "about:addons" по ПКМ на кнопке нового меню дополнений 111+
(this.openaboutaddonsthisfirefox = {
    async init(that) {
        await window.delayedStartupPromise;
        var btn = CustomizableUI.getWidget("unified-extensions-button")?.forWindow(window).node;
        if (!btn) return;
        btn.setAttribute("context", "");
        btn.tooltipText = `	 ЛКМ : Меню дополнений\n	 ПКМ : Менеджер\nCtrl+ПКМ : Отладка`;
        var listener = e => {
            if (e.button != 2) return;
            e.preventDefault();
            e.stopPropagation();
            e.stopImmediatePropagation();
            var gb = e.view.gBrowser;
            gb.selectedTab = gb.addTrustedTab(`about:${
                e.ctrlKey ? "debugging#/runtime/this-firefox" :"addons"
            }`);
        };
        btn.addEventListener("click", listener);
        that.unloadlisteners.push("openaboutaddonsthisfirefox");
        this.destructor = () => {
            btn.removeEventListener("click", listener);
        };
    }
}).init(this);


В scriptschrome >> load
   
+стиль

Выделить код

Код:

/* Настройка меню кнопки расширений 111+
 * https://forum.mozilla-russia.org/viewtopic.php?pid=804620#p804620 ***/
#unified-extensions-view {
/*    background: transparent !important; /* прозрачность меню, если настроено глобально */
    --uei-icon-size: 18px; /* значки */
}
/* бейджи - ~ три символа, например, у uBlock 1000+ пишется как >1k */
#unified-extensions-view .toolbarbutton-badge {
    margin: -3px 0 0 0 !important;
    margin-inline-end: -4px !important;
    font-size: 10px !important;
    line-height: 14px !important;
    max-width: 4em !important;
    padding: 0 !important;

}
/* нижняя часть списка - дополнения без кнопок на панели - на выбор */
#unified-extensions-view .unified-extensions-list, /* целиком */
/*#unified-extensions-view .unified-extensions-list .unified-extensions-item:not([extension-id="например-полный-ID-@Imagus"],[extension-id^="например-начало-ID-select-after-closing"]), /* или с исключениями */
#unified-extensions-view .panel-header /* заголовок меню */ {
    display: none !important;
}
#unified-extensions-view toolbaritem,
#unified-extensions-view unified-extensions-item {
    height: 29px !important;
    padding: 0 0 0 2px !important;
/*    margin: 0 4px 0 0 !important; /* если шестеренки скрыты */
}
#unified-extensions-view toolbarbutton {
    height: 29px !important;
    padding: 0 0 0 4px !important;

}
#unified-extensions-view .unified-extensions-item {
    height: 30px !important;
}
#unified-extensions-view .unified-extensions-item-menu-button {
/*    display: none !important; /* скрыть шестеренки */
    padding: 0 !important;
    margin: 0 4px 0 0 !important;
}
#unified-extensions-view .unified-extensions-item-name,
#unified-extensions-view .unified-extensions-item-message {
    height: 1.2em !important;
    width: 15em !important; /* длина заголовка и сообщения */
    padding-right: 0 !important;
    margin-right: 0 !important;
    padding-inline: 0 !important;
    margin-inline: 0 !important;
}
#unified-extensions-view .unified-extensions-item-message {
/*    display: none !important; /* скрыть сообщения */
    font-size: 12px !important;
}
/* нижняя кнопка */
#unified-extensions-manage-extensions {
    background: rgba(50,50,50,0.5) !important;
    min-height: 28px !important;
    padding: 2px 8px 4px 8px !important;
    margin: -6px 0 0 0 !important;
}
#unified-extensions-manage-extensions:hover {
    background: rgba(70,70,70,0.8) !important;
}
#unified-extensions-manage-extensions label {
    margin-block: 0;
    margin-inline: 10px !important;
}

/* Изменить, убрать пункты контекстного меню */
menuitem.unified-extensions-context-menu-pin-to-toolbar[label="Закрепить на панели инструментов"] > .menu-iconic-text {
    display: none !important;
}
menuitem.unified-extensions-context-menu-pin-to-toolbar[label="Закрепить на панели инструментов"] > .menu-accel-container::before {
    content: "Закрепить на панели";
    display: inline-block;
    -moz-box-flex: 1;
}
menuitem.unified-extensions-context-menu-report-extension {
    display: none !important;
}


Компактное меню новой кнопки "Расширения", скрывает - заголовок, дополнения без кнопок на панели (на выбор - целиком или со списком исключений), пункт контекстного меню "пожаловаться", сообщения (по желанию). И еще, по мелочи.

Dumby
Ctrl+Shift+C в Attributes Inspector работает, на 112?
   
И еще, на 112, в этом скрипте некорректно работает щелчок ЛКМ в списке вызываемом по ПКМ. Приводит к открытию первого меню поверх второго. Он у меня сейчас так выглядит.

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

Выделить код

Код:

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

	var {prefs} = Services, db = prefs.getDefaultBranch("");
	var pv = parseInt(Services.appinfo.platformVersion);
	var xul_ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

	var primary = [
//
	];
	var secondary = [
//
	];
	return {
		label: "Quick toggle",
		id: "QuickToggleAboutConfigSettings",
		localized: false,
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFPSURBVDhPnVPNSsNAEJ5tamnBk+A7BMEcikohp/gi9i18nRafQoT00kK16tnn8CJo4vp9m5m41eSgH0zmd3d+diJ/gfdedvl0QW7k1PcLwem+3TjowZagK+Nn6yc3gNCJ+LCCh9LzzTMdqeoNbkQSFZmpJLGCW5Gx2hZaQahMbZ52VuDSfHpnAVJXOb4FKzianbzCXiLrHLYl5C3t5NTRwtxaKJxUTZ/JcOTr6q2Rxwf0aVb2/hLs4KqL3Isckz/mmSdRRksjyg+z7IM6LzDCBfuvAEOJmIKBOqAWmo1YodxLlQNsyO0rsIV/AZM+JGc2a4GgzhZ2p9mFmrpboCMqNYBDdMlwompoTWN6F2nFT5hBXb0PEpmo/Gk+oH+RrqNZINPeIlnZViV1jQuLFJQuWGB0gJdwgShvqdPXZv6J+F/QS7oXqQ9xBTFZ5oa8fAGs9fed5YhPSwAAAABJRU5ErkJggg==",
		onCreated(btn) {
			btn.setAttribute("image", this.image);
			var doc = btn.ownerDocument;

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

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

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

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

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "Другое" : "По умолчанию";
				lab += ` ${"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 && !Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) return;

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

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

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

			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);
		}
	};
});

Можете поправить?
   
xrun1
А туда то я не заглянул. ) Спасибо.

_zt пишет

Ctrl+Shift+C в Attributes Inspector работает, на 112?

Работает с правкой в Firefox 111+, поломали Ctrl+Shift+C копирование с тултипа

LGS пишет

избранного

???

Можно на его основе что-то придумать, чтобы подобное работало  в боковой панели..?

Смотря что считать за «на основе».
В нём же про менюшки, а в боковой панели — дерево.


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

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

Выделить код

Код:

//...
    scriptsallchrome: { // Для докум. всех окон [ChromeOnly]
        domload: [ // По событию "DOMContentLoaded"
            // ...
            { path: "places_sidebar_folders_statistics.js", urlregxp: /chrome:\/\/browser\/content\/places\/(bookmarks|history)Sidebar.xhtml/ },
        ],


и places_sidebar_folders_statistics.js
скрытый текст

Выделить код

Код:

(async () => {
	var addFolderStats = node => {
		var title = PlacesUIUtils.getBestTitle(node, true);

		if (PlacesUtils.nodeIsContainer(node)) {
			var wasClosed = !node.containerOpen;
			if (wasClosed) node.containerOpen = true;

			var urls = 0, containers = 0;
			for(var ind = 0, max = node.childCount; ind < max; ind++) {
				var child = node.getChild(ind);
				if (PlacesUtils.nodeIsURI(child)) urls++;
				else if (PlacesUtils.nodeIsContainer(child)) containers++;
			}
			if (wasClosed) node.containerOpen = false;
		
			title += ` (${urls}/${containers})`;
		}
		return title;
	}
	var proto = PlacesTreeView.prototype;
	Object.assign(proto, eval(`({getCellText: ${proto.getCellText}})`.replace(
		"PlacesUIUtils.getBestTitle(node, true)", "addFolderStats(node)"
	)));
})();

_zt пишет

Приводит к открытию первого меню поверх второго.

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

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

Выделить код

Код:

.....
			//if (trg.btn) return this.openPopup(trg.primaryPopup);
			if (trg.btn) return trg.secondaryPopup.state == "closed" && this.openPopup(trg.primaryPopup);

Dumby пишет

Может просто прямо так и записать

Спасибо, очень важная кнопочка.

Dumby пишет

???

Так еще со школы помню (хоть и давно это было) стараться избегать повтора слов в предложении и применять, по-возможности, синонимы. Вот я и применил). То, что "избранное" будет понято правильно, я как-то не сомневался.

Dumby пишет

Допустм, подключение через встроенный загрузчик

Отлично, спасибо!
На скринах: первый - закладки, второй - история
sidebar.1681978758.png   sidebar-1.1681979968.png
Порядок отображения закладки/папки менять не стал, по-умолчанию устраивает.

_zt
Посмотрите PM.

Dumby пишет

custom_script_win.js, "DOMContentLoaded" (не "load").
скрытый текст

У меня если дополнительная панель скрыта, то блокирует всплывающее окно для установки расширения с АМО. В чём может быть причина ?

Dumby
Да, теперь все ок.
   
Посмотрите, пожалуйста, еще пару ваших скриптов, тоже не работают.
// этот вообще не видно
https://forum.mozilla-russia.org/viewto … 78#p798678
// а этот пустой тултип показывает
https://forum.mozilla-russia.org/viewto … 03#p789303
   
Еще одно вылезло:
Я выше поделку выложил, делал ее по шаблонам - раз, два и т.п., в моем профиле она показывает тултип, а в "чистом" - нет.
Не могу понять в чем дело.

kokoss пишет

У меня если дополнительная панель скрыта, то блокирует всплывающее окно для установки расширения с АМО. В чём может быть причина ?

Понятия не имею.
Но если anchor панельки находится где-то на коллапснутом тулбаре,
то хорошего не жди.


Можно попробовать, на такой случай,
подсунуть какой-нибудь другой anchor'ский элемент.
Код для custom_script_win.js

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

Выделить код

Код:

(async id => {
	var anchor = "notification-popup-box"; // urlbar
	//var anchor = "PanelUI-menu-button"; // hamburger

	var btn = document.getElementById(id);
	var orig = gUnifiedExtensions.getPopupAnchorID;
	var custom = eval(`(function ${orig})`.replace(id, anchor));
	gUnifiedExtensions.getPopupAnchorID = (...args) =>
		(btn.matches("toolbar[collapsed=true] :scope") ? custom : orig)(...args);
})("unified-extensions-button");

_zt пишет

этот вообще не видно

Может здесь посмотри (в самом конце сообщения).


а этот пустой тултип показывает

Ну да, nsINavBookmarkObserver — всё, удалили.
Хорошо, попробую записать только под PlacesObservers

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

Выделить код

Код:

(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 ? "а" : ""} заклад${m ? "ок" : "ки"}:\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_script_win;
	if (ucf) ucf[id] = {destructor}, ucf.unloadlisteners.push(id);
	else window.addEventListener("unload", destructor, {once: true});

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

в моем профиле она показывает тултип, а в "чистом" - нет.

У, это надо чистую сборку собирать с чистым UCF,
только чтобы проверить. Может быть соберусь, а может нет.

Dumby пишет

Можно попробовать, на такой случай,
подсунуть какой-нибудь другой anchor'ский элемент.
Код для custom_script_win.js
скрытый текст

Спасибо, теперь ОК!


Dumby пишет

Понятия не имею.
Но если anchor панельки находится где-то на коллапснутом тулбаре,
то хорошего не жди.

Использую дополнительную панель из UCF

_zt
А вообще, заморочно, но не так уж и долго.
Собрал 112.0.1, подключил скрипт как написано.
Результат — тултип есть.

скриншот

Выделить код

Код:

data:image/png;base64,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

Dumby

Ну да, nsINavBookmarkObserver — всё, удалили.
Хорошо, попробую записать только под PlacesObservers

Поискал у себя в кнопках текст nsINavBookmarkObserver, нашёл одну. Если правильно понял, это аналог кнопки из-под цитаты выше.
Вот хотелка, а вот реализация (2-й спойлер). И работает невзирая на nsINavBookmarkObserver.
Клики на звёздочке, лично мне, не нужны. А вот идея с "Недавняя папка:" симпатичная.

tooltip скриншот

Выделить код

Код:

data:image/png;charset=utf-8;base64,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


Или подскажите, куда эту новую вместо моей вставлять. Старая работает так, новая нет.
скрытый текст

Выделить код

Код:

scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        domload: [ // По событию "DOMContentLoaded"

        ],
        load: [ // По событию "load"
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "cs_win/bookmark-added.js", ucfobj: false, }, // для звёздочки показывать папку https://forum.mozilla-russia.org/viewtopic.php?pid=790890#p790890


Или это вообще не о том... :(

xrun1 пишет

И работает невзирая на nsINavBookmarkObserver.

Это потому, что соответствующий стафф находится
в блоке else, соответственно, давно уже не исполняется.

Старая работает так, новая нет.

Да, мой косяк.
Возился из-под 114, а на 112 не проверил.
Подправил, надеюсь.


Но, это версия без заголовка в тултипе,
то есть сразу «Адрес(а) заклад(ки/ок): ...»,
или последняя папка, куда добавлялась закладка.


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

Dumby
Новый UndoBookmarks работает.
BookmarksStarTooltipHelper, заработал, только последний.
   
Тултипу кнопки "расширения" мешал этот код.
Почему этого не происходило в старом профиле, мне неведомо.
Этот код работает и вроде не мешает.
   
Где то упоминалось, что в следующих версиях эту кнопку сделают перемещаемой. Это случилось в 114?
   
Спасибо.
   
21-04-2023 10:32:38
xrun1
Или так. )

Выделить код

Код:

// Убираем строку перед адресом закладки
//				text = `Адрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${text}`;
				text = `${text}`;
_zt пишет

Где то упоминалось, что в следующих версиях эту кнопку сделают перемещаемой.

Bug 1820743 - Allow to move unified extensions button on the navbar
То есть, насколько понимаю, да. Но только в пределах панели навигации.

Это случилось в 114?

Как видно из бага, случилось уже в 113.
Могло случиться и в 112, uplift в бету был реквестирован, но не поддержали.

Dumby пишет

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

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

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

Выделить код

Код:

text = `Редактировать эту закладку (Ctrl+D)\n\nАдрес${m ? "а" : ""} заклад${m ? "ок" : "ки"}:\n${text}`;
      } else
        text = "Добавить страницу в закладки (Ctrl+D)\n\nНедавняя папка:\n" + text;

Dumby
Вы когда то делали фрагмент для Save

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

Выделить код

Код:

// Сохранить страницу как 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).newPrintSettings;
    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();
    })();
};

Чет не работает. loadURI менял у себя по инструкции.

Dumby, можно попросить сочинить или поправить текущий еще и для  библиотеки..?
Эксперимента ради попробовал через встроенный загрузчик places_sidebar_folders_statistics.js подтянуть:
       

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

Выделить код

Код:

scriptsallchrome: { // Для докум. всех окон [ChromeOnly]
        domload: [ // По событию "DOMContentLoaded"
               { path: "custom_js_win/places_sidebar_folders_statistics.js", urlregxp: /chrome:\/\/browser\/content\/places\/places\.xhtml/ },


- счетчик появляется, но не цепляет закладки/историю и папки отображаются без названия:

Places-1.1682169183.png   Places-2.1682170280.png

_zt пишет

Чет не работает.

Хмм, судя по этому багу, в таком виде,
уже в 102 должно было перестать работать.

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

Выделить код

Код:

/*
        .getService(Ci.nsIPrintSettingsService).newPrintSettings;
*/
        .getService(Ci.nsIPrintSettingsService).createNewPrintSettings();

LGS пишет

или поправить текущий еще и для  библиотеки..?

Может так

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

Выделить код

Код:

/*
			var wasClosed = !node.containerOpen;
*/
			var wasClosed = !node.QueryInterface(Ci.nsINavHistoryContainerResultNode).containerOpen;

Dumby пишет

Может так

Почти идеально, единственное в "Журнале" в левой колонке (поле) количество url-ов не показывается, а в правом поле их число отображается:
Places-4.1682176815.png  Places-3.1682176955.png
Порядок изменил на папки/закладки.
Если это муторно и времени отнимает много, то можно и оставить как есть.
За сделанное - огромное спасибо!

Dumby пишет

уже в 102 должно было перестать работать.

Ну да, не работает. Не приходилось пользоваться, видимо. Другие варианты компактнее и удобнее, если печать не нужна, а она редко нужна. На 112 просто системно перепроверяю все.

_zt пишет

Ну да, не работает. Не приходилось пользоваться, видимо

С правкой Dumby заработал, но у меня сохраняет без запроса и на рабочий стол. Как директорию для сохранения pdf-ок поменять не знаете..?

LGS
Я говорю, что проверил в 102 и там не работал код без правки. С правкой, понятное дело, работает.
   

Выделить код

Код:

var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);

Т.е browser.download.dir должен быть назначен, иначе (по умолчанию) его нет, а следовательно и сохранять будет, наверное, в зависимости от параметра browser.download.folderList - 0=desktop, 1=downloads (default), 2=last used.

LGS пишет

в "Журнале" в левой колонке (поле) количество url-ов не показывается

В левой колонке вообще нигде количество url-ов не показывается.
Это потому, что там дерево чисто "папочное". Нужно пересчитывать.


А ещё, наткнулся на такой феномен: если навести мышь
на название закрытого контейнера, то начинается жор процессора и памяти.


Видимо, из-за открытия контейнера, образуется некая инвалидация,
и getCellText() вызывается снова, где опять открывает контейнер, и так далее, то есть петля.
Почему только при наведении мышью — непонятно.


Вобщем, такой вариант

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

Выделить код

Код:

(async (CRN, QRN) => {
	var addFolderStats = node => {
		var title = PlacesUIUtils.getBestTitle(node, true);

		if (PlacesUtils.nodeIsContainer(node)) {
			node.QueryInterface(CRN).QueryInterface(QRN);

			if (node.queryOptions.excludeItems) {
				var opts = node.queryOptions.clone();
				opts.excludeItems = false;
			}
			if (opts || !node.containerOpen) {
				node = PlacesUtils.history
					.executeQuery(node.query, opts || node.queryOptions).root;
				node.containerOpen = true;
			}
			var urls = 0, containers = 0;
			for(var ind = 0, max = node.childCount; ind < max; ind++) {
				var child = node.getChild(ind);
				if (PlacesUtils.nodeIsURI(child)) urls++;
				else if (PlacesUtils.nodeIsContainer(child)) containers++;
			}
			title += ` (${containers}/${urls})`;
		}
		return title;
	}
	var proto = PlacesTreeView.prototype;
	Object.assign(proto, eval(`({getCellText: ${proto.getCellText}})`.replace(
		"PlacesUIUtils.getBestTitle(node, true)", "addFolderStats(node)"
	)));
})(Ci.nsINavHistoryContainerResultNode, Ci.nsINavHistoryQueryResultNode);

Dumby пишет

А ещё, наткнулся на такой феномен: если навести мышь
на название закрытого контейнера, то начинается жор процессора и памяти.

У себя такого эффекта не наблюдаю, проверил для чистоты эксперимента на версиях 112, 102, 91 и 78. Везде ровно, без признаков отжора. Наверное, от железа и настроек каких-нибудь зависит, я в about:config редко лазаю, и user.js не пользуюсь, если что и наворочено, то по-минимуму.

_zt пишет

и сохранять будет, наверное, в зависимости от параметра browser.download.folderList - 0=desktop, 1=downloads (default), 2=last used

Не реагирует ни на какие значения, как кидал на рабочий стол, так и кидает.

_zt пишет

browser.download.dir должен быть назначен, иначе (по умолчанию) его нет

А какой синтаксис будет правильный: browser.download.dir=, или как?

LGS
browser.download.dir это параметр about:config, так же как и browser.download.folderList
Синтаксис там обычный. ) Т.е., если вы здесь ничего не трогали
2023.1682261559.jpgто параметр отсутствует и соответственно скрипт его считать не может.
   

У себя такого эффекта не наблюдаю

А я наблюдаю по CPU, у меня виджет на панель задач выведен, а вот по памяти было ровно. Теперь все нормально, если не считать того, что в библиотеку не подключается, но там уже работает пара скриптов, так что скорее всего конфликт.

_zt

_zt пишет

browser.download.dir это параметр about:config, так же как и browser.download.folderList

Вообщем, как ни крути - все равно результата нет, решил пока встроенной сохранялкой в PDF пользоваться, которая через "Печать" вызывается, Могу ошибаться, но по-моему вы как раз и обсуждали с Dumby этот вариант:

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

Выделить код

Код:

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
    ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
    : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

плюс значок и пункт меню в "Save":
     
скрытый текст

Выделить код

Код:

{ label: "Печать / печать в PDF", func: document.getElementById(document.getElementById("appMenu-viewCache").content.querySelector("[key=printKb]").getAttribute("command")).getAttribute("oncommand"), image: "chrome://global/skin/icons/print.svg"},
   { label: "Сохранить страницу / выбор как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},


Там и диалог сохранения имеется, и качество на выходе неплохое...

LGS
Я изменил свое сообщение, проверьте параметр.
Да, когда то обсуждали эти два пункта.

_zt пишет

Я изменил свое сообщение, проверьте параметр

Оказывается, дело было в том, что у меня папка "Загрузки" находится не на С: , а перенесена на другой диск-помойку. Если выбрать любую другую папку, то пожалуйста - сохраняет в указанную.
А в "Загрузки" не на системном разделе - не хочет. Скрин помог, спасибо, разобрались.

Вернул, ради проверки, путь для сохранения "Загрузки" (у меня там путь: "F:\Windows 7\Documents\Downloads") - скрипт начал отрабатывать как положено, грузить в "Загрузки".
Downloads.1682268324.png

LGS
Возможно с правами напутано на целевом диске или в целевой папке. У меня сохраняет куда укажу в настройках, на любой диск и по любым путям (и с пробелами и с кириллицей).
   
Второй параметр browser.download.folderList если равен = 2, должен по идее вообще запретить рабочий стол использовать.

Dumby, посмотрите, пожалуйста, эту кнопку, последняя функция внизу "Общее количество закладок". Не работает в 112, консоль пишет:
Uncaught TypeError: Cc['@mozilla.org/embedcomp/prompt-service;1'] is undefined.

LGS пишет

Uncaught TypeError: Cc['@mozilla.org/embedcomp/prompt-service;1'] is undefined.

Dumby про это писал здесь

LGS
Я у себя заменил:

Выделить код

Код:

var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);

На:

Выделить код

Код:

var prompts = Cc["@mozilla.org/prompter;1"].getService(Ci.nsIPromptService);

Пока работает.

unter_officer пишет

var prompts = Cc["@mozilla.org/prompter;1"].getService(Ci.nsIPromptService);

Спасибо, работает.

Dumby, можно придумать скрипт, чтобы показывал версию отключенных расширений на странице about:addons..? Стиль от Aris. Посмотрел скрипты ("Дополнения", extensionOptionsMenu) - они у себя версию у отключенных дополнений отображают. Может, для about:addons что-то получится сделать..?

LGS
Кнопка показывает https://forum.mozilla-russia.org/viewto … 24#p785024. Если не обязательно на about:addons...

xrun1 пишет

Кнопка показывает https://forum.mozilla-russia.org/viewto … 24#p785024. Если не обязательно на about:addons

Я про кнопки упоминал в посте в скобках... Хотелка именно именно для about:addons.

LGS пишет

показывал версию отключенных расширений на странице about:addons

Скорее интереснее, куда она девается.
Я посмотрел на 112, и что-то не вижу, чтобы это было сделано специально.
Похоже, что её просто document.l10n.setAttributes() куда-то выбрасывает.

можно придумать скрипт

Ой, сейчас немного неохота, и некогда.
Может у тебя есть какие-нибудь скрипты, которые уже что-то делают на about:addons ?
Я в том смысле, что тогда можно было бы попробовать дописать туда ещё кусок кода.

Dumby пишет

Может у тебя есть какие-нибудь скрипты, которые уже что-то делают на about:addons ?
Я в том смысле, что тогда можно было бы попробовать дописать туда ещё кусок кода.

Только этот:

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

Выделить код

Код:

(async (css, self) => ({

	//===[ Buttons ]===================================================================

	vertical: true,
	btnActions: ["preferences", "toggle-disabled", "remove", "install-update"],

	cn: "ucf-cloned-button",
	update(e) {
		var trg = e.target;
		trg.nodeName == "ADDON-CARD" && trg.addon.type != "theme" && this.onCard(trg);
	},
	onCard(card, again) {
		var btnsParent = card.querySelector("addon-options");
		if (!btnsParent) return again || card.ownerGlobal
			.requestAnimationFrame(() => this.onCard(card, true));

		var doc = card.ownerDocument;
		var [span] = card.getElementsByClassName(this.ccn);
		if (span) span.textContent = "";
		else
			card.querySelector("button.more-options-button")
				.before(span = doc.createElement("span")),
			span.className = this.ccn;

		var item, num = 0;
		for(var sel of this.btnActions) {
			if (num++ == this.tInd) {
				if (!card.querySelector(this.ts)) continue;
				item = this.createPanelItem(doc);
				item.setAttribute("action", "toggle-disabled");
				doc.l10n.setAttributes(item, `${
					btnsParent.parentNode.getAttribute("active") == "true" ? "dis" : "en"
				}able-addon-button`);
			} else {
				item = btnsParent.querySelector(sel);
				if (!item) continue;
				item = this.clone(item);
			}
			span.append(item);
			item.shadowRoot.querySelector("button").classList.add(this.cn);
		}
	},

	//===[ Popup ]=====================================================================

	items: {
		"Копировать имя": [
			addon => self.copy(addon.name),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAByUlEQVQ4jY2TTUhUURiGn5tD0Sp00iAwXM1iFu4kWkUZg/9MoZQhdoUCV7ooItq4atFORRGkuLMwoc0kNBGKDLSwFpEIQgspqGUUNsjI3HvPX4s7I3Nn7oW+1fn5vvd8z3vOsYiIgXuPneFMr21ZVmh9+m7GisoPxcTsM2d46on5c1gy9bHgvDVR+afqJzNzy870xIgNUJGGkqf4XVEAuJ4XeWCiNpibX3MGr/XYPd0pADxlOK0MQgf7Xw++sfJq0zRiJQAWcgWndHRkr28UWcrlAfCVwa8KrK4XOPxbYqz/MsnWcyfFi7lCIPD9x0/70YNRykKz+eELQkny74sIJbnQmqQ71UVhe+cES2hoP9uC63mBgO/7dF7s4NexZPRmBqlBaMPHnc+kL7UTh1VxqwKuGxjkaUP+zRZCSVqwKJeP2dvdj8SqGZuod9hXhr6hGwgNL5ZfEoc1PnaL2sFNAlvvthFKxmJJbfCUqXbghhHSyTOkJwcBmHr4vAnLVwKhJEPZbLgDXwh678yG7rfjfLIJS2qD0IEP9R6kiq8XrzS+sOz9p5+asKodXB0YCQkcNBbHYUXlxP6u67dnIj9PY8QJpIC2/6iX/wDxeTOAfNDrywAAAABJRU5ErkJggg==",
		],
		"Копировать ID": [
			addon => self.copy(addon.id),
			"Копировать имя"
		],
		"Копировать версию": [
			addon => self.copy(addon.version),
			"Копировать имя",
			addon => !addon.version
		],
		"Копировать имя и версию": [
			addon => self.copy(addon.name + " " + addon.version),
			"Копировать имя",
			addon => !addon.version
		],
		"Копировать URL кнопки": [
			(addon, win) => {
				var btn = Object.assign({
					parameters: {},
					get initcode() {return this.initCode;},
					setText(doc, name, t, cds) {
						win.custombutton.buttonSetText(doc, name, this[name], cds);
					}
				}, win.custombuttons.cbService.getButtonParameters(addon.buttonLink));
				self.copy(win.custombutton.buttonGetURI(btn));
			},
			"Копировать имя",
			addon => addon.type != "custombuttons"
		],
		"Домашняя страница": [
			(addon, win) => win.openURL(addon.homepageURL || addon.reviewURL.replace(/\/reviews\/.*$/, "/")),
			"data:image/png;base64,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",
			addon => !addon.homepageURL && !addon.reviewURL
		],
		"Поиск на АМО": [
			(addon, win) => win.openURL(
				addon.homepageURL || ("https://addons.mozilla.org/search/?q=" + encodeURIComponent(addon.name))
			),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJGSURBVDjLjdJLSNRBHMDx78yqLZaKS75DPdgDDaFDbdJmde5QlhCJGxgpRJfqEEKnIsJLB7skQYQKZaSmdLaopPCgEvSCShCMzR5a7oq7/3l12RVtjfzBMA/4fWZ+MyOccwBM3g8HEbIdfCEhfAFnLVapOa28Uevpjrqz/WOsERJgsu9Uq5CZQzgqrJfo9BajNd5irEYn4p3OUiFExtCLmw2tawFi4l5zUMjMIau9u7K+qxeoAcoAA0wDb2OPwmfA16LiiaOHLj1edRLpkO3WmIis7+oBDgJbgQ2AH6gC6jY19N62RkcctKeVIJAhp9QgUA3kJXdONZVcq9JxPSgQoXRAyIDRth8oAXQyKdWnoCKrTD9CBv4GMqx1WGNZkeRWJKbG2hiD1Cb9FbTnzWFdY/LCdLKlgNQ84gyNKqHm0gDjqVHnxDHgA/B9RQkpaB6YklkZl62np9KBhOqwjpKFgeY2YAz4BESBWHI8Hhs6PVVSvc3v98ye4fP7T676B845nt040ip98qpWJmI9PWiU6bfWgXGN2YHcKwU7tsuc4kpUPMbU0+f8+vKt+Pitl7PLAMDI9cNBoB0hQwICzjqUp6MZvsy8yvp95BRuQUjJ75mPvH4wYo1NlJ64Mza7DPwrhi8cCOeXl/aUB4P4c/NJxKLMvpngycCrzxVFG2v/CwAMnguF80oLe8p27cQh+fnpPV/fTc95S6piXQDAw7a9YbWkezZXFbAwMx/xPFXb1D3+Y90AQF/L7kAsri9mZ4lrTd0TcYA/Kakr+x2JSPUAAAAASUVORK5CYII=",
			["custombuttons", "theme", "plugin"]
		],
		"Папка установки": [
			addon => self.getFile(addon).reveal(),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAklEQVR42mNkoBAwAjEXEAcCMTsW+U9AvB6I/+I0gJ2Noe3QJoZKASEgjwkq+h+C795lYPCKZMgC8mZg0fsfbICIIMOcF6cZkrGZ/v8fA0PndIa/7z8BXfAPqgVKv//A8GPeZoYERmE+hjmP1+AwAOgiTjOIP7HZLyjLMJNRmB/ogjMMyYzsUC8wIhQwsgCFWHAHoKAy0AARIYY5r69idwEhIKgBMgAYBq8vkGmALsyAU2QaYAwz4CiZBliADBAAGnCATANsYQbsJtMAJ5ABwGh8uZE8A4R9gAYwMzHkT8hl6Bfgxp5ecIH3Xxj+F05hyIdpUmXAnpnwge9AfJckW7EBAC/gSzisxsnmAAAAAElFTkSuQmCC",
			["custombuttons", "theme", "plugin"]
		],
		"Файл установки": [
			addon => self.getFile(addon).launch(),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA51AAAOdQG5Y1EyAAAAhklEQVQ4y6WT0QnAIAxE78Bt2v1cod3PznP9kVLTRKUGBD+Sd5eQUBJWggCWCAkAJPGXOqlUPyVKkrSPQIoCQJGE6AF4HFyGuBmrR3cGQX8Wmp2cL8Cqv5JPD5Zm1D0nFZjTjHpQ7DuI1L3iBjBS9/pvhtjr21OukVkXiX/ug6SWj4mr53wDMz1vldHlLJkAAAAASUVORK5CYII=",
			["custombuttons", "theme", "plugin"]
		],
		"Проверить обновления": [
			Cr.NS_ERROR_NET_TIMEOUT_EXTERNAL // Fx 87+
				? (addon, win) => win.content.checkForUpdate(addon)
				: (addon, win) => win.content.frames[0].checkForUpdate(addon),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACVUlEQVQ4jXWT30vTYRTGP3Mb+ZbiXmm6DGUO0pkQGQR2URBdKZWWIHRlFHYhXnQRdVUEBUn/gBddFZERaalUoAUhYUglVIjO5TKbjMDtq2vDQ9t8u0jnr3Xgvfuch/M87zk2rTX/K+u05QROUMBVlrmkn+lvWxlHzsbjVj4uzlHFFec15/7UYCrFU6xcrF0ptbn5jNVADS9o47yvxed24yZRnchLz6dbpUSUlMhHNavSa3zeNskMIZzYOQChPyGmolNIXGz+Tn/lvu59d6jhtdVkZX1vm0AFVFSKpZfPNHKQ3SSBPsxCfMEWi8Rwtbkq5L3UqnH1OCtgNVleqRaf+MUjXrH0Sx2TUuklQAt+XPRykyAzFHNIfgkUUyV58k4FVOifhVLe0sUnGhknnyoAPaAjzHGUbkYxjOlHup0R7iPATqCI9vUMHHhwAR7SGCJrdnS/DjPOMd2nhwD4zS2CZEgCBRxZF8gQJgrkYwcqNmaiJ3QGMADMMMsyMeyAE/e6QII3TEP54XIbhXSufqfXarEebEwcH7swFBIDUqSyIYpPFrFzIV4ZhwLqBDGskOE6XUxwUsqkRwWVyF6xscw0P3lOgodqSgXRWqO1tnGZVwxiiGAYw3CbpWbTbDpMh+EGH2jDvcpuetk9EK8ME6UBQwkOoJUdc445RodHqb9YXxaeDJ8StwyogIrnXGUVUEkplx7mKWKJWn7gTH1PwQqEv4bBQRHzLKlJNbJRwJbrGq2zlps9fKEOD5MIEZ6Q5K7u1xNb2ZzXiEFYxDBEF8I9PaBDOTngLwkg8PzzO00cAAAAAElFTkSuQmCC",
			addon => !addon.applyBackgroundUpdates || addon.isBuiltin
		],
	},
	listContainerId: "ucf-aa-extra-items-container",
	showing(e) {
		var card = e.target.closest("addon-card");
		if (!card) return;

		this.labs = [];
		var imgs = new Map();
		var set = (key, val) => imgs.set(key, imgs.has(key) ? imgs.get(key).concat(val) : [val]);
		var entries = Object.entries(this.items);

		entries.forEach(([lab, [func, img, hideOn]], ind) => {
			this.labs.push(lab);
			(this[lab] = func).hideOn = hideOn;
			img && set(this.items[img]?.[1] ? entries.findIndex(a => a[0] == img): ind, ind);
		});
		if (imgs.size) {
			var cspRe = /^(?:chrome|file|jar|resource|moz-extension|https?):/;
			var [s, p, o] = this.vers >= 110
				? ["::part(button)", "background-image", "AUTHO"] : ["", "--icon", "USE"];

			var reg = [], push = (ind, icon) => {
				var chromeImg = "chrome://user_chrome_files/content/aaepiimg_" + ind;
				reg.push(["override", chromeImg, icon]);
				return chromeImg;
			}
			var rules = [];
			for(var [ind, nums] of imgs) {
				var sel = [], img = entries[ind][1][1];
				for(var num of nums) sel.push(
					`\t#${this.listContainerId} > panel-item:nth-child(${num + 1})${s}`
				);
				rules.push(`${sel.join(",\t\n")} {\n\t\t${p}: url(${
					cspRe.test(img) ? img : push(ind, img)
				}) !important;\n\t}`);
			}
			if (reg.length) {
				var ams = Cc["@mozilla.org/addons/addon-manager-startup;1"]
					.getService(Ci.amIAddonManagerStartup);
				var mUri = Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile));
				this.chromeReg = ams.registerChrome(mUri, reg);
			}
			this.regSheet(`\n${rules.join("\n")}\n}`, o + "R_SHEET");
		}
		delete this.items;

		self = this;
		this.sym = Symbol.for(this.listContainerId);
		(this.showing = e => {
			var card = e.target.closest("addon-card");
			card && this.onListShowind(card.addon, e.target);
		})(e);
		this.onListShowind(card.addon, e.target);
	},
	async onListShowind(addon, list) {
		var doc = list.ownerDocument, win = doc.ownerGlobal;
		var container = doc[this.sym];
		if (!container) {
			container = doc[this.sym] = doc.createElement("div");
			container.onclick = this.cclick;
			container.id = this.listContainerId;
			for(var lab of this.labs)
				container.appendChild(this.createPanelItem(doc)).append(lab);

			var mo = new win.MutationObserver(this.mut);
			(container.mo = mo).container = container;
		}
		for(var item of container.children) {
			var h = this[item.textContent].hideOn;
			item.hidden = h && (h.call ? h(addon) : h.includes(addon.type));
		}
		var {mo} = container;
		mo.disconnect();
		list.contains(container) || list.prepend(container);
		mo.count = 0;
		mo.ts = Date.now();
		mo.observe(list, {childList: true});
	},
	mut(muts, mo) {
		if (++mo.count > 10 || Date.now() - mo.ts > 100)
			return mo.disconnect();
		var list = muts[0].target, {container} = mo;
		if (list.firstElementChild != container)
			mo.disconnect(),
			list.prepend(container),
			mo.observe(list, {childList: true});
	},
	cclick(e) {
		e.stopImmediatePropagation();
		this.parentNode.hide();
		self[e.target.textContent](
			e.target.closest("addon-card").addon,
			e.view.windowRoot.ownerGlobal
		);
	},
	copy: str => (self.copy =
		Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper).copyString
	)(str),
	getFile(addon) {
		var file, uri = addon.getResourceURI();
		if (uri instanceof Ci.nsIJARURI) uri = uri.JARFile;
		if (uri instanceof Ci.nsIFileURL) file = uri.file;
		return file;
	},

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

	init(topic, quit) {
		Services.obs.addObserver(this, topic);
		Services.obs.addObserver(quit = (s, t) => {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(this, topic);
		}, "quit-application-granted");
	},
	isTargetDoc: doc => doc.ownerGlobal.docShell
		.currentDocumentChannel.name.endsWith("/aboutaddons.html"),
	observe(doc) {
		if (!this.isTargetDoc(doc)) return;

		var vers = this.vers = parseInt(Services.appinfo.platformVersion);
		this.ts = `${vers >= 111 ? "moz-toggle" : "input"}[action="toggle-disabled"]`;

		css = css.replace("%TS%", this.ts)
			.replace(/%CN%/g, this.cn)
			.replace(/;$/gm, " !important;")
			.replace("%FD%", this.vertical ? "column" : "row");

		this.regSheet(css, "USER_SHEET");

		var unload = e => {
			e.target.removeEventListener("update", this, true);
			e.target.removeEventListener("showing", this, true);
		}
		var load = doc => {
			doc.addEventListener("update", this, true);
			doc.addEventListener("showing", this, true);
			doc.ownerGlobal.addEventListener("unload", unload, {once: true});
		}
		this.handleEvent = e => this[e.type](e);
		this.observe = doc => this.isTargetDoc(doc) && load(doc);

		this.ccn = this.cn + "s-container";
		this.tInd = this.btnActions.findIndex(s => s == "toggle-disabled");

		this.btnActions = this.btnActions.map(
			action => `panel-list > panel-item[action="${action}"]`
		);
		this.createPanelItem = vers == 110
			? doc => new (doc.ownerGlobal.customElements.get("panel-item"))
			: doc => doc.createElement("panel-item");

		if (vers >= 89) this.clone = item => item.cloneNode(true);
		else {
			var cf = function(e) {
				var win = e.view;
				win.InspectorUtils.removeContentState(this, 4, true);
				Services.focus.clearFocus(win);
			}
			this.clone = item => {
				var clone = item.cloneNode(true);
				clone.onclick = cf;
				return clone;
			}
		}
		load(doc);
	},
	regSheet(...args) {
		var prfx = "data:text/css;charset=utf8,@-moz-document url(about:addons),%0A"
			+ "url(chrome://mozapps/content/extensions/aboutaddons.html) {";
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
			.getService(Ci.nsIStyleSheetService);
		(this.regSheet = (code, origin) => sss.loadAndRegisterSheet(
			Services.io.newURI(prfx + encodeURIComponent(code)), sss[origin]
		))(...args);
	}
}).init("chrome-document-loaded"))(`\

	span.%CN%s-container {
		display: flex;
		flex-direction: %FD%;
		row-gap: 1px;
	}
	addon-card[expanded] span.%CN%s-container {
		flex-direction: row;
	}
	button.%CN% {
		-moz-appearance: none;

		margin: 0 1px;
		padding: 1px 6px 3px 6px;
		background-image: none;
		background-color: rgba(174, 236, 235, 0.9);

		border-radius: 0;
		border: 1px solid #bbb;

		font-size: 13px;
		white-space: nowrap;
		font-family: Segoe UI;
	}
	button.%CN%:hover {
		background-color: gold;
	}
	button.%CN%:after, %TS% {
		display: none;
	}
}`);

Addons.1683375076.png

В основном стилями обходился.

Dumby пишет

Ой, сейчас немного неохота, и некогда.

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

LGS пишет

Только этот

OK. Возвращаю модифицированный.
Вписал результат неких экспериментов на предмет просьбы,
которые, в 113, чисто с веб-консоли проводились.

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

Выделить код

Код:

(async (css, self) => ({

	//===[ Buttons ]===================================================================

	vertical: true,
	btnActions: ["preferences", "toggle-disabled", "remove", "install-update"],

	cn: "ucf-cloned-button",
	update(e) {
		var trg = e.target;
		trg.nodeName == "ADDON-CARD" && trg.addon.type != "theme" && this.onCard(trg);
	},
	onCard(card, again) {
		var btnsParent = card.querySelector("addon-options");
		if (!btnsParent) return again || card.ownerGlobal
			.requestAnimationFrame(() => this.onCard(card, true));

		var doc = card.ownerDocument;
		var [span] = card.getElementsByClassName(this.ccn);
		if (span) span.textContent = "";
		else
			card.querySelector("button.more-options-button")
				.before(span = doc.createElement("span")),
			span.className = this.ccn;

		var item, num = 0;
		for(var sel of this.btnActions) {
			if (num++ == this.tInd) {
				if (!card.querySelector(this.ts)) continue;
				item = this.createPanelItem(doc);
				item.setAttribute("action", "toggle-disabled");
				doc.l10n.setAttributes(item, `${
					btnsParent.parentNode.getAttribute("active") == "true" ? "dis" : "en"
				}able-addon-button`);
			} else {
				item = btnsParent.querySelector(sel);
				if (!item) continue;
				item = this.clone(item);
			}
			span.append(item);
			item.shadowRoot.querySelector("button").classList.add(this.cn);
		}
	},

	//===[ Popup ]=====================================================================

	items: {
		"Копировать имя": [
			addon => self.copy(addon.name),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAByUlEQVQ4jY2TTUhUURiGn5tD0Sp00iAwXM1iFu4kWkUZg/9MoZQhdoUCV7ooItq4atFORRGkuLMwoc0kNBGKDLSwFpEIQgspqGUUNsjI3HvPX4s7I3Nn7oW+1fn5vvd8z3vOsYiIgXuPneFMr21ZVmh9+m7GisoPxcTsM2d46on5c1gy9bHgvDVR+afqJzNzy870xIgNUJGGkqf4XVEAuJ4XeWCiNpibX3MGr/XYPd0pADxlOK0MQgf7Xw++sfJq0zRiJQAWcgWndHRkr28UWcrlAfCVwa8KrK4XOPxbYqz/MsnWcyfFi7lCIPD9x0/70YNRykKz+eELQkny74sIJbnQmqQ71UVhe+cES2hoP9uC63mBgO/7dF7s4NexZPRmBqlBaMPHnc+kL7UTh1VxqwKuGxjkaUP+zRZCSVqwKJeP2dvdj8SqGZuod9hXhr6hGwgNL5ZfEoc1PnaL2sFNAlvvthFKxmJJbfCUqXbghhHSyTOkJwcBmHr4vAnLVwKhJEPZbLgDXwh678yG7rfjfLIJS2qD0IEP9R6kiq8XrzS+sOz9p5+asKodXB0YCQkcNBbHYUXlxP6u67dnIj9PY8QJpIC2/6iX/wDxeTOAfNDrywAAAABJRU5ErkJggg==",
		],
		"Копировать ID": [
			addon => self.copy(addon.id),
			"Копировать имя"
		],
		"Копировать версию": [
			addon => self.copy(addon.version),
			"Копировать имя",
			addon => !addon.version
		],
		"Копировать имя и версию": [
			addon => self.copy(addon.name + " " + addon.version),
			"Копировать имя",
			addon => !addon.version
		],
		"Копировать URL кнопки": [
			(addon, win) => {
				var btn = Object.assign({
					parameters: {},
					get initcode() {return this.initCode;},
					setText(doc, name, t, cds) {
						win.custombutton.buttonSetText(doc, name, this[name], cds);
					}
				}, win.custombuttons.cbService.getButtonParameters(addon.buttonLink));
				self.copy(win.custombutton.buttonGetURI(btn));
			},
			"Копировать имя",
			addon => addon.type != "custombuttons"
		],
		"Домашняя страница": [
			(addon, win) => win.openURL(addon.homepageURL || addon.reviewURL.replace(/\/reviews\/.*$/, "/")),
			"data:image/png;base64,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",
			addon => !addon.homepageURL && !addon.reviewURL
		],
		"Поиск на АМО": [
			(addon, win) => win.openURL(
				addon.homepageURL || ("https://addons.mozilla.org/search/?q=" + encodeURIComponent(addon.name))
			),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJGSURBVDjLjdJLSNRBHMDx78yqLZaKS75DPdgDDaFDbdJmde5QlhCJGxgpRJfqEEKnIsJLB7skQYQKZaSmdLaopPCgEvSCShCMzR5a7oq7/3l12RVtjfzBMA/4fWZ+MyOccwBM3g8HEbIdfCEhfAFnLVapOa28Uevpjrqz/WOsERJgsu9Uq5CZQzgqrJfo9BajNd5irEYn4p3OUiFExtCLmw2tawFi4l5zUMjMIau9u7K+qxeoAcoAA0wDb2OPwmfA16LiiaOHLj1edRLpkO3WmIis7+oBDgJbgQ2AH6gC6jY19N62RkcctKeVIJAhp9QgUA3kJXdONZVcq9JxPSgQoXRAyIDRth8oAXQyKdWnoCKrTD9CBv4GMqx1WGNZkeRWJKbG2hiD1Cb9FbTnzWFdY/LCdLKlgNQ84gyNKqHm0gDjqVHnxDHgA/B9RQkpaB6YklkZl62np9KBhOqwjpKFgeY2YAz4BESBWHI8Hhs6PVVSvc3v98ye4fP7T676B845nt040ip98qpWJmI9PWiU6bfWgXGN2YHcKwU7tsuc4kpUPMbU0+f8+vKt+Pitl7PLAMDI9cNBoB0hQwICzjqUp6MZvsy8yvp95BRuQUjJ75mPvH4wYo1NlJ64Mza7DPwrhi8cCOeXl/aUB4P4c/NJxKLMvpngycCrzxVFG2v/CwAMnguF80oLe8p27cQh+fnpPV/fTc95S6piXQDAw7a9YbWkezZXFbAwMx/xPFXb1D3+Y90AQF/L7kAsri9mZ4lrTd0TcYA/Kakr+x2JSPUAAAAASUVORK5CYII=",
			["custombuttons", "theme", "plugin"]
		],
		"Папка установки": [
			addon => self.getFile(addon).reveal(),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAklEQVR42mNkoBAwAjEXEAcCMTsW+U9AvB6I/+I0gJ2Noe3QJoZKASEgjwkq+h+C795lYPCKZMgC8mZg0fsfbICIIMOcF6cZkrGZ/v8fA0PndIa/7z8BXfAPqgVKv//A8GPeZoYERmE+hjmP1+AwAOgiTjOIP7HZLyjLMJNRmB/ogjMMyYzsUC8wIhQwsgCFWHAHoKAy0AARIYY5r69idwEhIKgBMgAYBq8vkGmALsyAU2QaYAwz4CiZBliADBAAGnCATANsYQbsJtMAJ5ABwGh8uZE8A4R9gAYwMzHkT8hl6Bfgxp5ecIH3Xxj+F05hyIdpUmXAnpnwge9AfJckW7EBAC/gSzisxsnmAAAAAElFTkSuQmCC",
			["custombuttons", "theme", "plugin"]
		],
		"Файл установки": [
			addon => self.getFile(addon).launch(),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA51AAAOdQG5Y1EyAAAAhklEQVQ4y6WT0QnAIAxE78Bt2v1cod3PznP9kVLTRKUGBD+Sd5eQUBJWggCWCAkAJPGXOqlUPyVKkrSPQIoCQJGE6AF4HFyGuBmrR3cGQX8Wmp2cL8Cqv5JPD5Zm1D0nFZjTjHpQ7DuI1L3iBjBS9/pvhtjr21OukVkXiX/ug6SWj4mr53wDMz1vldHlLJkAAAAASUVORK5CYII=",
			["custombuttons", "theme", "plugin"]
		],
		"Проверить обновления": [
			Cr.NS_ERROR_NET_TIMEOUT_EXTERNAL // Fx 87+
				? (addon, win) => win.content.checkForUpdate(addon)
				: (addon, win) => win.content.frames[0].checkForUpdate(addon),
			"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAACVUlEQVQ4jXWT30vTYRTGP3Mb+ZbiXmm6DGUO0pkQGQR2URBdKZWWIHRlFHYhXnQRdVUEBUn/gBddFZERaalUoAUhYUglVIjO5TKbjMDtq2vDQ9t8u0jnr3Xgvfuch/M87zk2rTX/K+u05QROUMBVlrmkn+lvWxlHzsbjVj4uzlHFFec15/7UYCrFU6xcrF0ptbn5jNVADS9o47yvxed24yZRnchLz6dbpUSUlMhHNavSa3zeNskMIZzYOQChPyGmolNIXGz+Tn/lvu59d6jhtdVkZX1vm0AFVFSKpZfPNHKQ3SSBPsxCfMEWi8Rwtbkq5L3UqnH1OCtgNVleqRaf+MUjXrH0Sx2TUuklQAt+XPRykyAzFHNIfgkUUyV58k4FVOifhVLe0sUnGhknnyoAPaAjzHGUbkYxjOlHup0R7iPATqCI9vUMHHhwAR7SGCJrdnS/DjPOMd2nhwD4zS2CZEgCBRxZF8gQJgrkYwcqNmaiJ3QGMADMMMsyMeyAE/e6QII3TEP54XIbhXSufqfXarEebEwcH7swFBIDUqSyIYpPFrFzIV4ZhwLqBDGskOE6XUxwUsqkRwWVyF6xscw0P3lOgodqSgXRWqO1tnGZVwxiiGAYw3CbpWbTbDpMh+EGH2jDvcpuetk9EK8ME6UBQwkOoJUdc445RodHqb9YXxaeDJ8StwyogIrnXGUVUEkplx7mKWKJWn7gTH1PwQqEv4bBQRHzLKlJNbJRwJbrGq2zlps9fKEOD5MIEZ6Q5K7u1xNb2ZzXiEFYxDBEF8I9PaBDOTngLwkg8PzzO00cAAAAAElFTkSuQmCC",
			addon => !addon.applyBackgroundUpdates || addon.isBuiltin
		],
	},
	listContainerId: "ucf-aa-extra-items-container",
	showing(e) {
		var card = e.target.closest("addon-card");
		if (!card) return;

		this.labs = [];
		var imgs = new Map();
		var set = (key, val) => imgs.set(key, imgs.has(key) ? imgs.get(key).concat(val) : [val]);
		var entries = Object.entries(this.items);

		entries.forEach(([lab, [func, img, hideOn]], ind) => {
			this.labs.push(lab);
			(this[lab] = func).hideOn = hideOn;
			img && set(this.items[img]?.[1] ? entries.findIndex(a => a[0] == img): ind, ind);
		});
		if (imgs.size) {
			var cspRe = /^(?:chrome|file|jar|resource|moz-extension|https?):/;
			var [s, p, o] = this.vers >= 110
				? ["::part(button)", "background-image", "AUTHO"] : ["", "--icon", "USE"];

			var reg = [], push = (ind, icon) => {
				var chromeImg = "chrome://user_chrome_files/content/aaepiimg_" + ind;
				reg.push(["override", chromeImg, icon]);
				return chromeImg;
			}
			var rules = [];
			for(var [ind, nums] of imgs) {
				var sel = [], img = entries[ind][1][1];
				for(var num of nums) sel.push(
					`\t#${this.listContainerId} > panel-item:nth-child(${num + 1})${s}`
				);
				rules.push(`${sel.join(",\t\n")} {\n\t\t${p}: url(${
					cspRe.test(img) ? img : push(ind, img)
				}) !important;\n\t}`);
			}
			if (reg.length) {
				var ams = Cc["@mozilla.org/addons/addon-manager-startup;1"]
					.getService(Ci.amIAddonManagerStartup);
				var mUri = Services.io.newFileURI(Services.dirsvc.get("ProfD", Ci.nsIFile));
				this.chromeReg = ams.registerChrome(mUri, reg);
			}
			this.regSheet(`\n${rules.join("\n")}\n}`, o + "R_SHEET");
		}
		delete this.items;

		self = this;
		this.sym = Symbol.for(this.listContainerId);
		(this.showing = e => {
			var card = e.target.closest("addon-card");
			card && this.onListShowind(card.addon, e.target);
		})(e);
		this.onListShowind(card.addon, e.target);
	},
	async onListShowind(addon, list) {
		var doc = list.ownerDocument, win = doc.ownerGlobal;
		var container = doc[this.sym];
		if (!container) {
			container = doc[this.sym] = doc.createElement("div");
			container.onclick = this.cclick;
			container.id = this.listContainerId;
			for(var lab of this.labs)
				container.appendChild(this.createPanelItem(doc)).append(lab);

			var mo = new win.MutationObserver(this.mut);
			(container.mo = mo).container = container;
		}
		for(var item of container.children) {
			var h = this[item.textContent].hideOn;
			item.hidden = h && (h.call ? h(addon) : h.includes(addon.type));
		}
		var {mo} = container;
		mo.disconnect();
		list.contains(container) || list.prepend(container);
		mo.count = 0;
		mo.ts = Date.now();
		mo.observe(list, {childList: true});
	},
	mut(muts, mo) {
		if (++mo.count > 10 || Date.now() - mo.ts > 100)
			return mo.disconnect();
		var list = muts[0].target, {container} = mo;
		if (list.firstElementChild != container)
			mo.disconnect(),
			list.prepend(container),
			mo.observe(list, {childList: true});
	},
	cclick(e) {
		e.stopImmediatePropagation();
		this.parentNode.hide();
		self[e.target.textContent](
			e.target.closest("addon-card").addon,
			e.view.windowRoot.ownerGlobal
		);
	},
	copy: str => (self.copy =
		Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper).copyString
	)(str),
	getFile(addon) {
		var file, uri = addon.getResourceURI();
		if (uri instanceof Ci.nsIJARURI) uri = uri.JARFile;
		if (uri instanceof Ci.nsIFileURL) file = uri.file;
		return file;
	},

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

	init(topic, quit) {
		Services.obs.addObserver(this, topic);
		Services.obs.addObserver(quit = (s, t) => {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(this, topic);
		}, "quit-application-granted");
	},
	isTargetDoc: doc => doc.ownerGlobal.docShell
		.currentDocumentChannel.name.endsWith("/aboutaddons.html"),
	observe(doc) {
		if (!this.isTargetDoc(doc)) return;

		var vers = this.vers = parseInt(Services.appinfo.platformVersion);
		this.ts = `${vers >= 111 ? "moz-toggle" : "input"}[action="toggle-disabled"]`;

		css = css.replace("%TS%", this.ts)
			.replace(/%CN%/g, this.cn)
			.replace(/;$/gm, " !important;")
			.replace("%FD%", this.vertical ? "column" : "row");

		this.regSheet(css, "USER_SHEET");

		var unload = e => {
			e.target.removeEventListener("update", this, true);
			e.target.removeEventListener("showing", this, true);
		}
		var load = doc => {
			doc.addEventListener("update", this, true);
			doc.addEventListener("showing", this, true);
			var win = doc.ownerGlobal;
			win.addEventListener("unload", unload, {once: true});
			this.inactiveAddonsVersion(win);
		}
		this.handleEvent = e => this[e.type](e);
		this.observe = doc => this.isTargetDoc(doc) && load(doc);

		this.ccn = this.cn + "s-container";
		this.tInd = this.btnActions.findIndex(s => s == "toggle-disabled");

		this.btnActions = this.btnActions.map(
			action => `panel-list > panel-item[action="${action}"]`
		);
		this.createPanelItem = vers == 110
			? doc => new (doc.ownerGlobal.customElements.get("panel-item"))
			: doc => doc.createElement("panel-item");

		if (vers >= 89) this.clone = item => item.cloneNode(true);
		else {
			var cf = function(e) {
				var win = e.view;
				win.InspectorUtils.removeContentState(this, 4, true);
				Services.focus.clearFocus(win);
			}
			this.clone = item => {
				var clone = item.cloneNode(true);
				clone.onclick = cf;
				return clone;
			}
		}
		load(doc);
	},
	inactiveAddonsVersion(win) {
		var desc = win.Object.getOwnPropertyDescriptor(win.HTMLElement.prototype, "title");
		var {set} = desc, cfg = {attributes: true, attributeFilter: ["title"]};

		var handleMuts = function(m, {trg, val}) {
			this.disconnect();
			var txt = trg.firstChild;
			if (txt) txt.data = txt.data.replace(trg.closest("addon-card").addon.name, val);
		}
		desc.set = function(val) {
			set.call(this, val);
			if (this.getAttribute("data-l10n-id") != "addon-name-disabled") return;

			var mo = new win.MutationObserver(handleMuts);
			mo.val = val;
			mo.observe(mo.trg = this, cfg);
		}
		for(var Elm of [win.HTMLAnchorElement, win.HTMLHeadingElement])
			win.Object.defineProperty(Elm.prototype, "title", desc);
	},
	regSheet(...args) {
		var prfx = "data:text/css;charset=utf8,@-moz-document url(about:addons),%0A"
			+ "url(chrome://mozapps/content/extensions/aboutaddons.html) {";
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
			.getService(Ci.nsIStyleSheetService);
		(this.regSheet = (code, origin) => sss.loadAndRegisterSheet(
			Services.io.newURI(prfx + encodeURIComponent(code)), sss[origin]
		))(...args);
	}
}).init("chrome-document-loaded"))(`\

	span.%CN%s-container {
		display: flex;
		flex-direction: %FD%;
		row-gap: 1px;
	}
	addon-card[expanded] span.%CN%s-container {
		flex-direction: row;
	}
	button.%CN% {
		-moz-appearance: none;

		margin: 0 1px;
		padding: 1px 6px 3px 6px;
		background-image: none;
		background-color: rgba(174, 236, 235, 0.9);

		border-radius: 0;
		border: 1px solid #bbb;

		font-size: 13px;
		white-space: nowrap;
		font-family: Segoe UI;
	}
	button.%CN%:hover {
		background-color: gold;
	}
	button.%CN%:after, %TS% {
		display: none;
	}
}`);

Dumby пишет

Возвращаю модифицированный.

Шедеврально! Большое спасибо!
Addons-1.1683397339.png
Работает также в 78, 91, 102.

Dumby
Вы копирование uuid можете добавить? Иногда надо в расширении что то подправить и приходится в отладку идти, и там искать, выделять, копировать.

Попытался скрестить этот стиль со скриптом и максимум что у меня получилось:
   
ae46ed52c168278c12a7105a9795536d.jpeg  68311a36b936a3ddb129f4e4a52aec4b.jpeg 
Не знаю как flex-ы разделить, что бы меню скрипта оставалось как меню, а не встраивалось в таблицу, если это вообще возможно.

_zt пишет

Иногда надо в расширении что то подправить и приходится в отладку идти, и там искать, выделять, копировать

Далее, ожидалось «, затем …», но найдено «.»

Вы копирование uuid можете добавить?

Не исключено

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

Выделить код

Код:

//...
		"копирование uuid": [
			addon => self.copy(addon.isActive
				? Cu.getGlobalForObject(Cu).WebExtensionPolicy.getByID(addon.id).mozExtensionHostname
				: JSON.parse(Services.prefs.getCharPref("extensions.webextensions.uuids", "{}"))[addon.id]
			),
			"Копировать имя",
			addon => addon.type != "extension" || !(addon.isWebExtension ?? true)
		],

Попытался скрестить этот стиль со скриптом и максимум что у меня получилось:

Ожидался код стиля попытки получившегося максимума, но найдено ничего.

Dumby пишет

Далее, ожидалось «, затем …», но найдено «.»

:) Затем userContent.css

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

Выделить код

Код:

@-moz-document url-prefix(moz-extension://uuid/) {
…
}

а далее Reload userChrome/userContent
   

Dumby пишет

Ожидался код стиля попытки получившегося максимума, но найдено ничего.

Так это просто заготовка. Я ни в чем не уверен и тестировал только на full_theme от VitalyV.

скрипт

Выделить код

Код:

удалено

стиль

Выделить код

Код:

удалено

Dumby пишет

Не исключено

Спасибо, работает.
   
И по поводу скрипта, у меня не везде срабатывал поиск на AMO, вместо этого пытался открыть домашнюю страницу, поэтому изменил пункт. Не знаю, верно или нет.

_zt пишет

userContent.css

Ааа, вот оно что.
Я-то прочитал «в расширении что то подправить»,
как «влезьть внутрь .xpi и там похозяйничать».

скрипт

Ух, неслабо ты его покрамсал.


Я так понимаю, предпочтение отдано
стилем-выведенным кнопкам, а не скриптом-добавленным кнопкам.
Тогда я тоже не знаю что здесь можно сделать.


Если бы наоборот, то хоть что-то можно было бы попробовать.
Я вот в стиле закомментировал этот кусок

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

Выделить код

Код:

addon-card panel-list {
    position: static !important;
    display: block !important;
    background: none !important;
    border: none !important;
    border-radius: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    box-shadow: none !important;
    min-width: 0 !important;
}


а в скрипте поменял
скрытый текст

Выделить код

Код:

/*
	vertical: true,
	btnActions: ["toggle-disabled"],
*/
	vertical: false,
	btnActions: ["remove", "install-update", "preferences"],
Выделить код

Код:

/*
	onCard(card, again) {
		var btnsParent = card.querySelector("addon-options");
		if (!btnsParent) return again || card.ownerGlobal
			.requestAnimationFrame(() => this.onCard(card, true));

		var doc = card.ownerDocument;
		var [span] = card.getElementsByClassName(this.ccn);
		if (span) span.textContent = "";
		else
			card.querySelector("button.more-options-button")
				.before(span = doc.createElement("span")),
			span.className = this.ccn;
	},
*/
	onCard(card, again) {
		var btnsParent = card.querySelector("addon-options");
		if (!btnsParent) return again || card.ownerGlobal
			.requestAnimationFrame(() => this.onCard(card, true));

		var doc = card.ownerDocument;
		var [span] = card.getElementsByClassName(this.ccn);

		var moreOptionsButton = card.querySelector("button.more-options-button");

		if (span) span.textContent = "";
		else
			moreOptionsButton.before(span = doc.createElement("span")),
			span.className = this.ccn;

		for(var sel of this.btnActions) {
			var item = btnsParent.querySelector(sel);
			if (item)
				span.append(item = this.clone(item)),
				item.shadowRoot.querySelector("button").classList.add(this.cn);
		}
		span.querySelector("[action=remove]").before(moreOptionsButton);
	},


и, у меня, в 113, выглядит так
(отображение добра в <panel-list>'е, конечно, надо править)
скрытый текст

Выделить код

Код:

data:image/png;base64,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

изменил пункт. Не знаю, верно или нет.

Ну, не то, чтобы прямо совсем неверно, но выражение
("https://addons.mozilla.org/search/?q=" + encodeURIComponent(addon.name)) || addon.homepageURL
это типа true || something
Результатом такого выражения никогда не будет something.
То есть, можно оставить только то, что в скобках.

Dumby
Отлично, спасибо. Видимо я недотыркал. :)
   
Последняя проблема, при наличии обновлений тултип кнопки с меню обрезается по высоте. На первой анимации видно.

gif-ки
BrmPUqvM.gif
   
e66uHz7s.gif
скрипт

Выделить код

Код:

(async (css, self) => ({

	//===[ Buttons ]===================================================================

	vertical: false,
	btnActions: ["remove", "install-update", "preferences"],

	cn: "ucf-cloned-button",
	update(e) {
		var trg = e.target;
		trg.nodeName == "ADDON-CARD" && trg.addon.type != "theme" && this.onCard(trg);
	},
	onCard(card, again) {
		var btnsParent = card.querySelector("addon-options");
		if (!btnsParent) return again || card.ownerGlobal
			.requestAnimationFrame(() => this.onCard(card, true));

		var doc = card.ownerDocument;
		var [span] = card.getElementsByClassName(this.ccn);
		if (span) span.textContent = "";
		else
			card.querySelector("button.more-options-button")
				.before(span = doc.createElement("span")),
			span.className = this.ccn;

		var item, num = 0;
		for(var sel of this.btnActions) {
			if (num++ == this.tInd) {
				if (!card.querySelector(this.ts)) continue;
				item = this.createPanelItem(doc);
				item.setAttribute("action", "toggle-disabled");
				doc.l10n.setAttributes(item, `${
					btnsParent.parentNode.getAttribute("active") == "true" ? "dis" : "en"
				}able-addon-button`);
			} else {
				item = btnsParent.querySelector(sel);
				if (!item) continue;
				item = this.clone(item);
			}
			span.append(item);
			item.shadowRoot.querySelector("button").classList.add(this.cn);
		}
	},

	//===[ Popup ]=====================================================================

	items: {
		"Копировать имя": [
			addon => self.copy(addon.name),
		],
		"Копировать ID": [
			addon => self.copy(addon.id),
			"Копировать имя"
		],
		"Копировать UUID": [
			addon => self.copy(addon.isActive
				? Cu.getGlobalForObject(Cu).WebExtensionPolicy.getByID(addon.id).mozExtensionHostname
				: JSON.parse(Services.prefs.getCharPref("extensions.webextensions.uuids", "{}"))[addon.id]
			),
			"Копировать имя",
			addon => addon.type != "extension" || !(addon.isWebExtension ?? true)
		],
//		"Копировать версию": [
//			addon => self.copy(addon.version),
//			"Копировать имя",
//			addon => !addon.version
//		],
//		"Копировать имя и версию": [
//			addon => self.copy(addon.name + " " + addon.version),
//			"Копировать имя",
//			addon => !addon.version
//		],
		"Коп. URL кнопки": [
			(addon, win) => {
				var btn = Object.assign({
					parameters: {},
					get initcode() {return this.initCode;},
					setText(doc, name, t, cds) {
						win.custombutton.buttonSetText(doc, name, this[name], cds);
					}
				}, win.custombuttons.cbService.getButtonParameters(addon.buttonLink));
				self.copy(win.custombutton.buttonGetURI(btn));
			},
			"Копировать имя",
			addon => addon.type != "custombuttons"
		],
		"Домашняя страница": [
			(addon, win) => win.openURL(addon.homepageURL || addon.reviewURL.replace(/\/reviews\/.*$/, "/")),
			addon => !addon.homepageURL && !addon.reviewURL
		],
		"Поиск на АМО": [
			(addon, win) => win.openURL(
				("https://addons.mozilla.org/search/?q=" + encodeURIComponent(addon.name))
			),
			["custombuttons", "theme", "plugin"]
		],
		"Папка установки": [
			addon => self.getFile(addon).reveal(),
			["custombuttons", "theme", "plugin"]
		],
		"Файл установки": [
			addon => self.getFile(addon).launch(),
			["custombuttons", "theme", "plugin"]
		],
		"Проверить обновления": [
			Cr.NS_ERROR_NET_TIMEOUT_EXTERNAL // Fx 87+
				? (addon, win) => win.content.checkForUpdate(addon)
				: (addon, win) => win.content.frames[0].checkForUpdate(addon),
			addon => !addon.applyBackgroundUpdates || addon.isBuiltin
		],
	},
	listContainerId: "ucf-aa-extra-items-container",
	showing(e) {
		var card = e.target.closest("addon-card");
		if (!card) return;

		this.labs = [];
		var imgs = new Map();
		var set = (key, val) => imgs.set(key, imgs.has(key) ? imgs.get(key).concat(val) : [val]);
		var entries = Object.entries(this.items);

		entries.forEach(([lab, [func, img, hideOn]], ind) => {
			this.labs.push(lab);
			(this[lab] = func).hideOn = hideOn;
			img && set(this.items[img]?.[1] ? entries.findIndex(a => a[0] == img): ind, ind);
		});

		self = this;
		this.sym = Symbol.for(this.listContainerId);
		(this.showing = e => {
			var card = e.target.closest("addon-card");
			card && this.onListShowind(card.addon, e.target);
		})(e);
		this.onListShowind(card.addon, e.target);
	},
	async onListShowind(addon, list) {
		var doc = list.ownerDocument, win = doc.ownerGlobal;
		var container = doc[this.sym];
		if (!container) {
			container = doc[this.sym] = doc.createElement("div");
			container.onclick = this.cclick;
			container.id = this.listContainerId;
			for(var lab of this.labs)
				container.appendChild(this.createPanelItem(doc)).append(lab);

			var mo = new win.MutationObserver(this.mut);
			(container.mo = mo).container = container;
		}
		for(var item of container.children) {
			var h = this[item.textContent].hideOn;
			item.hidden = h && (h.call ? h(addon) : h.includes(addon.type));
		}
		var {mo} = container;
		mo.disconnect();
		list.contains(container) || list.prepend(container);
		mo.count = 0;
		mo.ts = Date.now();
		mo.observe(list, {childList: true});
	},
	mut(muts, mo) {
		if (++mo.count > 10 || Date.now() - mo.ts > 100)
			return mo.disconnect();
		var list = muts[0].target, {container} = mo;
		if (list.firstElementChild != container)
			mo.disconnect(),
			list.prepend(container),
			mo.observe(list, {childList: true});
	},
	cclick(e) {
		e.stopImmediatePropagation();
		this.parentNode.hide();
		self[e.target.textContent](
			e.target.closest("addon-card").addon,
			e.view.windowRoot.ownerGlobal
		);
	},
	copy: str => (self.copy =
		Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper).copyString
	)(str),
	getFile(addon) {
		var file, uri = addon.getResourceURI();
		if (uri instanceof Ci.nsIJARURI) uri = uri.JARFile;
		if (uri instanceof Ci.nsIFileURL) file = uri.file;
		return file;
	},

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

	init(topic, quit) {
		Services.obs.addObserver(this, topic);
		Services.obs.addObserver(quit = (s, t) => {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(this, topic);
		}, "quit-application-granted");
	},
	isTargetDoc: doc => doc.ownerGlobal.docShell
		.currentDocumentChannel.name.endsWith("/aboutaddons.html"),
	observe(doc) {
		if (!this.isTargetDoc(doc)) return;

		var vers = this.vers = parseInt(Services.appinfo.platformVersion);
		this.ts = `${vers >= 111 ? "moz-toggle" : "input"}[action="toggle-disabled"]`;

		css = css.replace("%TS%", this.ts)
			.replace(/%CN%/g, this.cn)
			.replace(/;$/gm, " !important;")
			.replace("%FD%", this.vertical ? "column" : "row");

		this.regSheet(css, "USER_SHEET");

		var unload = e => {
			e.target.removeEventListener("update", this, true);
			e.target.removeEventListener("showing", this, true);
		}
		var load = doc => {
			doc.addEventListener("update", this, true);
			doc.addEventListener("showing", this, true);
			var win = doc.ownerGlobal;
			win.addEventListener("unload", unload, {once: true});
			this.inactiveAddonsVersion(win);
		}
		this.handleEvent = e => this[e.type](e);
		this.observe = doc => this.isTargetDoc(doc) && load(doc);

		this.ccn = this.cn + "s-container";
		this.tInd = this.btnActions.findIndex(s => s == "toggle-disabled");

		this.btnActions = this.btnActions.map(
			action => `panel-list > panel-item[action="${action}"]`
		);
		this.createPanelItem = vers == 110
			? doc => new (doc.ownerGlobal.customElements.get("panel-item"))
			: doc => doc.createElement("panel-item");

		if (vers >= 89) this.clone = item => item.cloneNode(true);
		else {
			var cf = function(e) {
				var win = e.view;
				win.InspectorUtils.removeContentState(this, 4, true);
				Services.focus.clearFocus(win);
			}
			this.clone = item => {
				var clone = item.cloneNode(true);
				clone.onclick = cf;
				return clone;
			}
		}
		load(doc);
	},
	inactiveAddonsVersion(win) {
		var desc = win.Object.getOwnPropertyDescriptor(win.HTMLElement.prototype, "title");
		var {set} = desc, cfg = {attributes: true, attributeFilter: ["title"]};

		var handleMuts = function(m, {trg, val}) {
			this.disconnect();
			var txt = trg.firstChild;
			if (txt) txt.data = txt.data.replace(trg.closest("addon-card").addon.name, val);
		}
		desc.set = function(val) {
			set.call(this, val);
			if (this.getAttribute("data-l10n-id") != "addon-name-disabled") return;

			var mo = new win.MutationObserver(handleMuts);
			mo.val = val;
			mo.observe(mo.trg = this, cfg);
		}
		for(var Elm of [win.HTMLAnchorElement, win.HTMLHeadingElement])
			win.Object.defineProperty(Elm.prototype, "title", desc);
	},
	regSheet(...args) {
		var prfx = "data:text/css;charset=utf8,@-moz-document url(about:addons),%0A"
			+ "url(chrome://mozapps/content/extensions/aboutaddons.html) {";
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"]
			.getService(Ci.nsIStyleSheetService);
		(this.regSheet = (code, origin) => sss.loadAndRegisterSheet(
			Services.io.newURI(prfx + encodeURIComponent(code)), sss[origin]
		))(...args);
	}
}).init("chrome-document-loaded"))(`\

	span.%CN%s-container {
		display: flex;
		flex-direction: %FD%;
		row-gap: 1px;
	}
	addon-card[expanded] span.%CN%s-container {
		flex-direction: row;
	}
}`);

стиль

Выделить код

Код:

@media (prefers-color-scheme: dark) {
#details-deck .inline-options-stack {
    background-color: rgba(43,42,51,.8) !important;
  }
}
#categories button.category {
    border: none !important;
}
/********* Показывает версии дополнений ***************************************/
@-moz-document  url("about:addons"), url("chrome://mozapps/content/extensions/aboutaddons.html") {
*|*[current-view="list"] .addon-name-link[title] {
    font-size: 0 !important;
    display: flex !important;
    flex-direction: row !important;
  }
*|*[current-view="list"] .addon-name-link[title]::after {
    display: block !important;
    content: attr(title) !important;
    font-size: 1rem !important;
    line-height: 1em !important;
  }
/* hide version number on updates category */
div[current-view="updates"] .addon-name-link[title]::after {
    display: none !important;
    visibility: collapse !important;
    opacity: 0 !important;
  }
}
/* Добавляет кнопки вместо меню: включить, отключить, удалить, настройки... ***/
/* https://forum.mozilla-russia.org/viewtopic.php?pid=793823#p793823
 * Чтобы вернуть текст кнопкам, то в #main:
 * --v-buttons-font-size: Xrem;
 * Чтобы убрать текст для кнопок определенного типа там есть пример
 * addon-card panel-item[action="report"]
 * Убрать иконки: --v-buttons-icon-size: 0px;                                 */
@-moz-document  url("about:addons"), url("chrome://mozapps/content/extensions/aboutaddons.html") {
:root {
    --v-content-button-border-color: var(--in-content-box-border-color, rgba(12, 12, 13, 0.3));
    --v-content-button-border-color-hover: var(--in-content-border-hover, rgba(12, 12, 13, 0.5));
    --v-content-button-background: var(--in-content-button-background, rgba(12, 12, 13, 0.1));
    --v-content-button-background-hover: var(--in-content-button-background-hover, rgba(12, 12, 13, 0.15));
    --v-content-button-background-active: var(--in-content-button-background-active, rgba(12, 12, 13, 0.2));
    --v-content-button-color: var(--in-content-button-text-color, #0c0c0d);
    --v-content-button-color-hover: var(--in-content-button-text-color-hover, #0c0c0d);
    --v-content-button-color-active: var(--in-content-button-text-color-hover, #0c0c0d);
    --v-buttons-tooltip-color: var(--in-content-page-color, #0c0c0d);
    --v-buttons-tooltip-background: var(--in-content-box-background, #ffffff);
    --v-buttons-tooltip-border-color: var(--in-content-box-border-color, rgba(12, 12, 13, 0.3));
    --v-main-max-width: 1000px;
    --card-padding: 8px !important;
    --section-width: 664px !important;
    --addon-icon-size: 32px !important;
}
#main {
    --v-buttons-font-size: 0.8rem; // 0;
    --v-buttons-font-weight: bold;
    --v-buttons-border-radius: 4px;
    --v-buttons-border-width: 1px;
    --v-buttons-min-height: 26px;
    --v-buttons-tooltip-font-size: 0.8rem;
    --v-buttons-tooltip-border-radius: 2px;
    --v-buttons-tooltip-delay: .5s;
    --v-buttons-tooltip-translate-x: -132px;
    --v-buttons-tooltip-translate-y: 15px;
    --v-buttons-width: auto;
    --v-list-display-flex: flex;
}
@media (prefers-color-scheme: dark) {
:root {
    --v-content-button-border-color: var(--in-content-box-border-color, rgba(200, 200, 210, 0.2));
    --v-content-button-border-color-hover: var(--in-content-border-hover, rgba(249, 249, 250, 0.3));
    --v-content-button-background: var(--in-content-button-background, rgba(249, 249, 250, 0.1));
    --v-content-button-background-hover: var(--in-content-button-background-hover, rgba(249, 249, 250, 0.15));
    --v-content-button-background-active: var(--in-content-button-background-active, rgba(249, 249, 250, 0.2));
    --v-content-button-color: var(--in-content-button-text-color, #f9f9fa);
    --v-content-button-color-hover: var(--in-content-button-text-color-hover, #f9f9fa);
    --v-content-button-color-active: var(--in-content-button-text-color-hover, #f9f9fa);
    --v-buttons-tooltip-color: var(--in-content-page-color, rgba(200, 200, 210, 0.5));
    --v-buttons-tooltip-background: rgba(40, 40, 50, 0.85);
    --v-buttons-tooltip-border-color: rgba(120, 120, 120, 0.3);
}
}
#moz-toggle-button.toggle-button,
addon-card .theme-enable-button {
    --v-buttons-font-size: 0;
    --v-buttons-toggle-button-content: "Включить";
    --v-buttons-tooltip-content: "Включить дополнение";
    --v-buttons-tooltip-display: block;
    --icon: url("chrome://global/skin/icons/check-partial.svg");
}
#moz-toggle-button.toggle-button[aria-pressed="true"],
addon-card .card[active="true"] .theme-enable-button {
    --v-buttons-toggle-button-content: "Отключить";
    --v-buttons-tooltip-content: "Отключить дополнение";
    --v-buttons-tooltip-display: block;
    --icon: url("chrome://global/skin/icons/check.svg");
    --v-content-button-background: rgba(85,255,85, 0.2);
}
addon-card panel-item[action="remove"] {
    --v-buttons-font-size: 0;
    --v-buttons-tooltip-content: "Удалить дополнение";
    --v-buttons-tooltip-display: block;
    --icon: url("chrome://global/skin/icons/delete.svg");
    --v-content-button-background: rgba(255,85,85, 0.3);
    --v-content-button-background-hover: rgba(255,85,85, 0.8);
}
addon-card panel-item[action="install-update"] {
    --v-buttons-font-size: 0;
    --v-buttons-tooltip-content: "Обновить дополнение";
    --v-buttons-tooltip-display: block;
    --icon: url("chrome://global/skin/icons/update-icon.svg");
    --v-content-button-background: rgba(49,140,231, 0.3);
    --v-content-button-background-hover: rgba(49,140,231, 0.8);
}
addon-card panel-item[action="preferences"] {
    --v-buttons-font-size: 0;
    --v-buttons-tooltip-content: "Настройки дополнения";
    --v-buttons-tooltip-display: block;
    --icon: url("chrome://global/skin/icons/settings.svg");
    --v-content-button-background: rgba(85,85,255, 0.3);
    --v-content-button-background-hover: rgba(85,85,255, 0.8);
}
/* Скрыть "Пожаловаться на дополнение" */
addon-card panel-item[action="report"] {
    display: none !important;
}
addon-list[type="plugin"] addon-card panel-item:not([checked]) {
    --icon: url("chrome://global/skin/icons/check-partial.svg");
}
addon-card button.more-options-button {
    --v-buttons-tooltip-content: "Дополнительные действия";
    --v-buttons-tooltip-display: block;
    --v-buttons-tooltip-translate-x: 0px !important;
    width: 26px !important;
    height: 26px !important;
    margin: 0 0 0 2px !important;
    padding: 0 0 0 0 !important;
    border-radius: 4px !important;
}
body {
    margin: 0 !important;
}
#main {
    max-width: var(--v-main-max-width) !important;
    margin-inline-start: 16px !important;
    margin-inline-end: 16px !important;
}
addon-card message-bar {
    --card-padding: inherit !important;
}
addon-card .card-contents {
    width: auto !important;
    overflow-x: hidden !important;
    flex-grow: 1 !important;
}
addon-card .card {
    display: grid !important;
    grid-template-columns: 1fr auto !important;
}
addon-card .card > * {
    grid-column-start: 1 !important;
    grid-column-end: 3 !important;
}
addon-card .card > .addon-card-collapsed {
    grid-column-start: 1 !important;
    grid-column-end: 2 !important;
    grid-row-start: 1 !important;
    overflow-x: hidden !important;
}
addon-card .card > addon-options {
    grid-column-start: 2 !important;
    grid-column-end: 3 !important;
    grid-row-start: 1 !important;
    align-self: start !important;
    min-width: 0 !important;
    overflow-x: hidden !important;
}
img.card-heading-image {
    margin: calc(var(--card-padding) * -1) calc(var(--card-padding) * -1) var(--card-padding) !important;
    width: calc(var(--card-padding) * 2 + 100%) !important;
    height: auto !important;
    object-fit: cover !important;
}
addon-card .card > img.card-heading-image:not([hidden]) {
    display: inline !important;
}
addon-card .card > img.card-heading-image:not([hidden]) + .addon-card-collapsed,
addon-card .card > img.card-heading-image:not([hidden]) + .addon-card-collapsed ~ addon-options {
    grid-row-start: 2 !important;
}
addon-card .addon-name-container {
    margin-top: 0 !important;
}
div.arrow.top ~ div.list {
    display: var(--v-list-display-flex, block) !important;
    justify-content: end !important;
}
#ucf-aa-extra-items-container panel-item {
    --v-buttons-font-size: 0.8rem;
    --v-buttons-font-weight: normal;
    --v-buttons-border-radius: 2px;
    --v-buttons-border-width: 1px;
    --v-buttons-min-height: 16px;
    --v-buttons-width: 100%;
}
addon-card panel-list {
    border: 1px solid var(--v-buttons-tooltip-border-color) !important;
    padding: 1px 0 !important;
    margin: 1px !important;
    opacity: 0.9 !important; /**/
}
addon-card panel-list panel-item[data-l10n-id="remove-addon-button"],
addon-card panel-list panel-item[data-l10n-id="install-update-button"],
addon-card panel-list panel-item[data-l10n-id="preferences-addon-button"],
addon-card panel-list panel-item[data-l10n-id="report-addon-button"],
addon-card panel-list panel-item[data-l10n-id="manage-addon-button"] {
    display: none !important;
}
addon-card panel-list panel-item {
    margin: 1px !important;
}
addon-card panel-item:where(:not([hidden])) {
    padding: 0 !important;
    margin: 0 !important;
    display: flex !important;
    align-items: center !important;
}
style + div.container {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}
addon-card panel-item[action="expand"],
div.arrow.top, div.arrow.bottom,
addon-card panel-item-separator {
    display: none !important;
}
#moz-toggle-button.toggle-button,
addon-card button.theme-enable-button,
link[href$="panel-item.css"] ~ button {
    display: flex !important;
    align-items: center !important;
    width: var(--v-buttons-width, 100%) !important;
    min-height: var(--v-buttons-min-height, 26px) !important;
    height: auto !important;
    line-height: 1.25em !important;
    padding: 0px calc(4px + .25em) !important;
    margin: 0 2px !important;
    background-image: var(--icon, none) !important;
    background-repeat: no-repeat !important;
    background-size: var(--v-buttons-icon-size, 16px) !important;
    background-position-y: center !important;
    background-position-x: left 4px !important;
    padding-inline-start: calc(4px + .25em + var(--v-buttons-icon-size, 16px)) !important;
    background-color: var(--v-content-button-background) !important;
    border-color: var(--v-content-button-border-color) !important;
    border-style: solid !important;
    border-width: var(--v-buttons-border-width, 0px) !important;
    color: var(--v-content-button-color) !important;
    border-radius: var(--v-buttons-border-radius, 0) !important;
    font-weight: var(--v-buttons-font-weight, inherit) !important;
    font-size: var(--v-buttons-font-size, inherit) !important;
    appearance: none !important;
    box-shadow: none !important;
    outline: none !important;
    -moz-context-properties: fill;
    fill: currentColor;
}
#moz-toggle-button.toggle-button,
addon-card button.theme-enable-button {
    padding-inline-start: 0 !important;
}
#moz-toggle-button.toggle-button {
    border-color: var(--v-content-button-border-color) !important;
}
#moz-toggle-button.toggle-button:enabled:hover,
addon-card button.theme-enable-button:enabled:hover,
addon-card button.more-options-button:enabled:hover,
link[href$="panel-item.css"] ~ button:enabled:hover {
    background-color: var(--v-content-button-background-hover) !important;
    color: var(--v-content-button-color-hover) !important;
    border-color: var(--v-content-button-border-color-hover) !important;
}
#moz-toggle-button.toggle-button:hover:active,
addon-card button.theme-enable-button:enabled:hover:active,
link[href$="panel-item.css"] ~ button:enabled:hover:active {
    background-color: var(--v-content-button-background-active) !important;
    color: var(--v-content-button-color-active) !important;
}
#moz-toggle-button.toggle-button::-moz-focus-inner,
addon-card button.theme-enable-button::-moz-focus-inner,
link[href$="panel-item.css"] ~ button::-moz-focus-inner {
    border: none !important;
}
link[href$="panel-item.css"] ~ button::after {
    inset: 0px !important;
    bottom: auto !important;
    height: 3px !important;
    width: auto !important;
    border-radius: 0 !important;
}
#moz-toggle-button.toggle-button::before {
    all: unset !important;
    display: inline-block !important;
    content: var(--v-buttons-toggle-button-content, none) !important;
    white-space: nowrap !important;
    margin-inline-start: calc(4px + .25em + var(--v-buttons-icon-size, 16px)) !important;
}
#moz-toggle-button.toggle-button:dir(rtl),
addon-card button.theme-enable-button:dir(rtl),
addon-card button.more-options-button:dir(rtl),
link[href$="panel-item.css"] ~ button:dir(rtl) {
    background-position-x: right 4px !important;
}
link[href$="panel-item.css"] ~ button > label {
    padding: 0 !important;
    margin: 0 !important;
}
addon-card .more-options-menu {
    flex-grow: 0 !important;
    position: static !important;
    margin: 0 !important;
    align-self: start !important;
}
addon-card .addon-name,
addon-card .addon-description {
    white-space: nowrap !important;
    overflow-x: hidden !important;
    text-overflow: ellipsis !important;
}
#moz-toggle-button.toggle-button::after,
addon-card button.theme-enable-button::after,
addon-card button.more-options-button::after,
addon-card panel-item::after {
    display: var(--v-buttons-tooltip-display, none) !important;
    content: "";
    max-width: 0;
    pointer-events: none !important;
}
#moz-toggle-button.toggle-button:hover::after,
addon-card button.theme-enable-button:hover::after,
addon-card button.more-options-button:hover::after,
addon-card panel-item:hover::after {
    animation-name: tooltip_delay !important;
    animation-timing-function: step-end !important;
    animation-duration: 0s !important;
    animation-iteration-count: 1 !important;
    animation-fill-mode: forwards !important;
    animation-delay: var(--v-buttons-tooltip-delay, .5s) !important;
    --v-buttons-tooltip-transform: translate(calc(1px + var(--v-buttons-tooltip-translate-x)), calc(100% + var(--v-buttons-tooltip-translate-y)));
}
@keyframes tooltip_delay {
    from {
        max-width: 0;
    }
    to {
        content: var(--v-buttons-tooltip-content, "");
        align-self: flex-end;
        position: absolute;
        color: var(--v-buttons-tooltip-color);
        background: var(--v-buttons-tooltip-background);
        border: 1px solid var(--v-buttons-tooltip-border-color);
        border-radius: var(--v-buttons-tooltip-border-radius, 0);
        padding: 2px 0px 4px 0px;
        min-width: 15em;
        max-width: 50em;
        line-height: 1.25em;
        font-size: 0.7rem;
        font-weight: normal;
        text-align: center;
        z-index: var(--z-index-popup, 10);
        overflow: hidden;
        text-overflow: ellipsis;
        transform: var(--v-buttons-tooltip-transform, none);
    }
}
addon-card .toggle-button:hover::after,
addon-card button.theme-enable-button:hover::after {
    --v-buttons-tooltip-transform: translate(calc(var(--v-buttons-tooltip-translate-x) - 1px), calc(100% + 1px + var(--v-buttons-tooltip-translate-y)));
}
addon-card button.theme-enable-button::before {
    content: "";
    display: inline-block;
    width: calc(4px + .25em + var(--v-buttons-icon-size, 16px));
  }
}
/* custom_css_for_fx - recentupdates_category_always_visible ******************/
/* Github: https://github.com/aris-t2/customcssforfx **************************/
@-moz-document url-prefix(about:addons),
    url-prefix(chrome://mozapps/content/extensions/aboutaddons.html) {
    #categories .category[name="recent-updates"][disabled],
    #category-recentUpdates[disabled] {
      overflow: visible !important;
      height: 48px !important;
      min-height: 48px !important;
      opacity: 1 !important;
      transition-property: unset !important;
      transition-duration: unset !important;
      -moz-user-input: unset !important;
    }
    #categories .category[name="recent-updates"][hidden],
    #category-recentUpdates[hidden] {
      opacity: 1 !important;
      min-height: 48px !important;
      height: 48px !important;
      display: inherit !important;
      transition: unset !important;
    }
}

В стиле заодно поправил отображение заголовка в представлениях обновлений, а то скрывался вместе с версией.

_zt пишет

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

Увы, не получилось воспроизвести.


Может добавить в @keyframes, в to {…}, какой-нибудь min-height
Так, пальцем в небо.

Dumby
Пробовал. Потом еще раз посмотрю, когда с сюрпризами 113 разберусь - стили менюшек развалились.

Dumby, в 113 [firefox] вертикальная панель отвалилась. Есть уже решение?

voqabuhe пишет

в 113  вертикальная панель отвалилась. Есть уже решение?

Заменить в vertical_top_bottom_bar.css:


display: -moz-box --> display: flex
-moz-box-flex --> flex-grow
-moz-box-orient: vertical --> flex-direction: column
-moz-box-align --> align-items
-moz-box-pack --> justify-content

voqabuhe
Я вот так написал в vertical_top_bottom_bar.css

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

Выделить код

Код:

/*
#ucf-additional-vertical-container {
    display: -moz-box !important;
}
*/
@supports (display: -moz-box) {
    #ucf-additional-vertical-container {
        display: -moz-box !important;
    }
}
@supports not (display: -moz-box) {
    #ucf-additional-vertical-container {
        display: flex !important;
    }
    #ucf-additional-vertical-bar {
        flex-grow: 1 !important;
    }
}

Dumby пишет

Я вот так написал в vertical_top_bottom_bar.css

Во, спасибо, появилась.

unter_officer пишет

Заменить в vertical_top_bottom_bar.css:

Спасибо. Но вариант от Dumby требует меньше движений.

voqabuhe пишет

Спасибо. Но вариант от Dumby требует меньше движений.

Пожалуйста.
Только мой вариант не совсем мой. Он от автора UCF. ;)

Dumby, а посмотри ещё пожайлуста, чего-то Findbar вниз уехал.

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

Выделить код

Код:

@-moz-document url("chrome://browser/content/browser.xhtml") {
:root {
    --v-findbar-flex-direction: row; /* row-reverse - findbar справа */
    --v-findbar-container-start: 4px; /* 100%; */ /* 1px - findbar справа */
    --v-findbar-container-end: 1px; /* 100% - findbar справа */
    --v-findbar-margin-right: 17px; /* отступ для скроллбара */
    --v-findbar-duration: 150ms; /* длительность анимации */
    --v-findbar-border-radius: 6px; /* радиус скругления findbar */
    --v-findbar-textbox-radius: 3px; /* радиус скругления поля ввода */
    --v-findbar-textbox-width: 14em; /* длина поля ввода */
    --v-findbar-toolbarbutton-border-radius: 3px; /* радиус скругления кнопок */
    --v-findbar-toolbarbutton-inner-padding: 3px; /* влияет на размер кнопок и поля ввода */
    --v-findbar-toolbarbutton-icon-display: -moz-box; /* none - скрыть иконки на кнопках */
    --v-findbar-toolbarbutton-text-display: none; /* -moz-box - показать текст на кнопках */
}
.browserContainer > findbar {
    -moz-box-ordinal-group: 0 !important;
    height: 0 !important;
    min-height: 0 !important;
    max-height: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    margin-right: var(--v-findbar-margin-right) !important;
    border: none !important;
    display: flex !important;
    flex-direction: var(--v-findbar-flex-direction) !important;
    transition: none !important;
    visibility: visible !important;
    opacity: 1 !important;
    color: var(--toolbar-color, -moz-DialogText) !important;
    box-shadow: none !important;
    overflow: visible !important;
    --toolbarbutton-inner-padding: var(--v-findbar-toolbarbutton-inner-padding);
    --toolbarbutton-border-radius: var(--v-findbar-toolbarbutton-border-radius);
}
.browserContainer > findbar > .findbar-container {
    position: fixed !important;
    display: flex !important;
    align-items: center !important;
    flex-direction: var(--v-findbar-flex-direction) !important;
    inset: auto !important;
    margin: -1px 0 0 0 !important;
    height: auto !important;
    min-width: 0 !important;
    padding: 1px !important;
    padding-inline-start: min(calc(2 * var(--toolbarbutton-inner-padding) + 19px), var(--v-findbar-container-start)) !important;
    padding-inline-end: min(calc(2 * var(--toolbarbutton-inner-padding) + 19px), var(--v-findbar-container-end)) !important;
    overflow: hidden !important;
    border: 1px solid var(--chrome-content-separator-color, rgba(0,0,0,.25)) !important;
    border-top-width: 0 !important;
    box-shadow: none !important;
    z-index: 2 !important;
    border-radius: 0 0 var(--v-findbar-border-radius) var(--v-findbar-border-radius) !important;
    background-color: var(--toolbar-bgcolor, -moz-Dialog) !important;
    background-image: var(--toolbar-bgimage, linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15))) !important;
    opacity: 1;
    animation-name: findbar-animation, findbar-animation-opacity !important;
    animation-delay: 0s !important;
    animation-duration: var(--v-findbar-duration) !important;
    animation-timing-function: linear, ease-out !important;
    animation-iteration-count: 1 !important;
    animation-fill-mode: none !important;
}
.browserContainer > findbar > #findbar-close-container,
.browserContainer > findbar > toolbarbutton.findbar-closebutton {
    margin: 1px 3px !important;
    padding: 0 !important;
    border: none !important;
    border-radius: var(--v-findbar-toolbarbutton-border-radius) !important;
    -moz-appearance: none !important;
    appearance: none !important;
    position: fixed !important;
    /* display: flex !important; */  display: none !important;
    z-index: 2 !important;
    height: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
    width: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
    opacity: 1;
    animation-name: findbar-animation, findbar-animation-opacity !important;
    animation-delay: 0s !important;
    animation-duration: var(--v-findbar-duration) !important;
    animation-timing-function: linear, ease-out !important;
    animation-iteration-count: 1 !important;
    animation-fill-mode: none !important;
}
.browserContainer > findbar > #findbar-close-container > toolbarbutton.findbar-closebutton {
    appearance: none !important;
    margin: 0 !important;
    padding: 0 !important;
    height: 100% !important;
    width: 100% !important;
    border: none !important;
    border-radius: var(--v-findbar-toolbarbutton-border-radius) !important;
}
.browserContainer > findbar toolbarbutton.findbar-closebutton > .toolbarbutton-icon {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    height: 16px !important;
    width: 16px !important;
    border-radius: var(--v-findbar-toolbarbutton-border-radius) !important;
}
.browserContainer > findbar[hidden="true"] > .findbar-container,
.browserContainer > findbar[hidden="true"] > #findbar-close-container,
.browserContainer > findbar[hidden="true"] > toolbarbutton.findbar-closebutton {
    transform: translateY(-100%);
    animation-name: findbar-animation-hide, findbar-animation-opacity-hide !important;
    animation-timing-function: linear, ease-in !important;
    pointer-events: none !important;
    opacity: 0;
}
@keyframes findbar-animation {
    from {
        transform: translateY(-100%);
        pointer-events: none;
    }
    to {
        transform: translateY(0%);
        pointer-events: none;
    }
}
@keyframes findbar-animation-opacity {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}
@keyframes findbar-animation-hide {
    from {
        transform: translateY(0%);
    }
    to {
        transform: translateY(-100%);
    }
}
@keyframes findbar-animation-opacity-hide {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}
.browserContainer > findbar:-moz-lwtheme {
    color: var(--toolbar-color, inherit) !important;
}
.browserContainer > findbar:-moz-lwtheme > .findbar-container {
    background-color: var(--lwt-accent-color, white) !important;
    background-image: linear-gradient(var(--toolbar-bgcolor, rgba(255,255,255,.4)), var(--toolbar-bgcolor, rgba(255,255,255,.4))) !important;
}
.browserContainer > findbar:-moz-lwtheme-brighttext > .findbar-container {
    background-color: var(--lwt-accent-color, black) !important;
}
:root[lwtheme-image] .browserContainer > findbar:-moz-lwtheme > .findbar-container {
    background-repeat: repeat-y !important;
    background-size: auto auto !important;
    background-attachment: fixed !important;
    background-position: right top !important;
    background-image: linear-gradient(var(--toolbar-bgcolor, rgba(255,255,255,.4)), var(--toolbar-bgcolor, rgba(255,255,255,.4))), var(--lwt-header-image, none) !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton) {
    align-self: normal !important;
}
.browserContainer > findbar > .findbar-container > * {
    margin: 1px !important;
    outline: none !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] {
    display: flex !important;
    flex-direction: row !important;
    align-items: normal !important;
}
.browserContainer > findbar > .findbar-container > description:empty {
    margin: 0 !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable,
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > .findbar-textbox {
    padding-block: 0 !important;
    margin-inline: 0 !important;
    min-height: 22px !important;
    box-sizing: border-box !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.findbar-find-previous {
    margin-inline: 2px !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable {
    min-width: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
    border: none !important;
    border-radius: var(--toolbarbutton-border-radius) !important;
    background-color: transparent !important;
    padding-inline: var(--toolbarbutton-inner-padding) !important;
    outline: none !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > .findbar-textbox {
    border-radius: var(--v-findbar-textbox-radius) !important;
    min-height: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
    width: var(--v-findbar-textbox-width) !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > .findbar-textbox,
.browserContainer > findbar > .findbar-container > description,
.browserContainer > findbar > .findbar-container > label {
    padding-bottom: 1px !important;
}
.browserContainer > findbar > .findbar-container > checkbox > .checkbox-label-box > .checkbox-icon,
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable > .toolbarbutton-text,
.browserContainer > findbar > .findbar-container > .find-status-icon {
    display: none !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton) > :is(.checkbox-label-box,.toolbarbutton-text) {
    display: var(--v-findbar-toolbarbutton-text-display) !important;
    margin-inline: 2px !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable > .toolbarbutton-icon,
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton) > :is(.checkbox-check,.toolbarbutton-icon) {
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
    height: 16px !important;
    width: 16px !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton) > :is(.checkbox-check,.toolbarbutton-icon) {
    display: var(--v-findbar-toolbarbutton-icon-display) !important;
    appearance: none !important;
    background: none !important;
    color: inherit !important;
    filter: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='0'><filter id='fill'><feComposite in='FillPaint' in2='SourceGraphic' operator='in'/></filter></svg>#fill");
    fill: currentColor;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton) {
    appearance: none !important;
    padding: var(--toolbarbutton-inner-padding) !important;
    border-radius: var(--toolbarbutton-border-radius) !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable:not([disabled="true"]):hover,
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton):not([disabled="true"]):hover {
    background-color: var(--toolbarbutton-hover-background, color-mix(in srgb, currentColor 17%, transparent)) !important;
}
.browserContainer > findbar > .findbar-container > hbox[anonid="findbar-textbox-wrapper"] > toolbarbutton.tabbable:not([disabled="true"]):is([checked="true"],:hover:active),
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton):not([disabled="true"]):is([checked="true"],:hover:active) {
    background-color: var(--toolbarbutton-active-background, color-mix(in srgb, currentColor 30%, transparent)) !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton).findbar-highlight {
    list-style-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:rgb(142, 142, 152);' d='M 2,3 V 4.2 H 6 V 12 h 1.2 l 0,-7.8 h 4 L 11.2,3 Z M 12.5,6 C 11.8,8.39 10,10.3 10,11.6 10,12.9 11.1,14 12.5,14 13.9,14 15,12.9 15,11.6 15,10.3 13.2,8.39 12.5,6 Z m 0,6.8 c -1,0 -1.3,-0.8 -1.3,-1.2 0,-1.1 0.8,-2.1 1.3,-3 0.5,0.9 1.3,1.9 1.3,3 0,0.4 -0.3,1.2 -1.3,1.2 z' /></svg>") !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton).findbar-case-sensitive {
    list-style-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:rgb(142, 142, 152);' d='m 6,3 -5,9 h 1.2 l 1.1,-2 h 2.5 v 2 H 7 V 3 Z M 5.8,5.5 5.8,9 H 3.9 Z M 13,6 V 6.77 C 12.5,6.28 11.7,6 11,6 9.34,6 8,7.34 8,9 c 0,1.7 1.34,3 3,3 0.7,0 1.5,-0.3 2,-0.8 V 12 h 1 V 6 Z m -2,1 c 1.1,0 2,0.9 2,2 0,1.1 -0.9,2 -2,2 C 9.9,11 9,10.1 9,9 9,7.9 9.9,7 11,7 Z' /></svg>") !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton).findbar-match-diacritics {
    list-style-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:rgb(142, 142, 152);' d='M 5,3 3,0 H 0 M 12,5 10,2 H 7 M 6,3 1,12 h 1.2 l 1.1,-2 h 2.5 v 2 H 7 V 3 M 5.8,5.5 V 9 H 3.9 Z M 13,6 V 6.77 C 12.5,6.28 11.7,6 11,6 9.34,6 8,7.34 8,9 c 0,1.7 1.34,3 3,3 0.7,0 1.5,-0.3 2,-0.8 V 12 h 1 V 6 Z m -2,1 c 1.1,0 2,0.9 2,2 0,1.1 -0.9,2 -2,2 C 9.9,11 9,10.1 9,9 9,7.9 9.9,7 11,7 Z'/></svg>") !important;
}
.browserContainer > findbar > .findbar-container > :is(checkbox,toolbarbutton).findbar-entire-word {
    list-style-image: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:rgb(142, 142, 152);' d='M 15,13.8 V 15 H 1 V 13.8 M 15,2.2 15,1 H 1 V 2.2 M 6,3 1,12 h 1.2 l 1.1,-2 h 2.5 v 2 H 7 V 3 M 5.8,5.5 V 9 H 3.9 Z M 13,6 V 6.77 C 12.5,6.28 11.7,6 11,6 9.34,6 8,7.34 8,9 c 0,1.7 1.34,3 3,3 0.7,0 1.5,-0.3 2,-0.8 V 12 h 1 V 6 Z m -2,1 c 1.1,0 2,0.9 2,2 0,1.1 -0.9,2 -2,2 C 9.9,11 9,10.1 9,9 9,7.9 9.9,7 11,7 Z' /></svg>") !important;
}
}

09-05-2023 12:30:33

unter_officer пишет

Пожалуйста.
Только мой вариант не совсем мой. Он от автора UCF.

Да, где ты его нашёл?

voqabuhe пишет

Да, где ты его нашёл?

Здесь, через личку.
И хочу ещё раз, уже публично, поблагодарить Виталия за помощь!

unter_officer
Ну вот попробовал вариант от Виталия. С ним у меня вертикальная панель стала по высоте  короче примерно на  2/3, как бы поднялась вся вверх.

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

voqabuhe
Может где-то ошиблись, когда производили замену?
У меня с первого раза все завелось и прекрасно работает.

unter_officer
А как там ошибёшься, скопировал - нашёл, скопировал - заменил. У вас были кнопки внизу панели? У меня четыре последние кнопки на скрине были в самом низу, а теперь поднялись вверх. С вариантом от Dumby всё нормально. Ну попробую ещё раз, позже перепроверить.

Untitled-3.png


Мой vertical_top_bottom_bar.css:

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

Выделить код

Код:

toolbarpaletteitem[place="palette"] > toolbaritem.ucf-additional-springs {
    background: white url("./svg/spring.svg") center no-repeat;
    border: none;
    outline: 1px solid currentColor;
    outline-offset: -1px;
    min-height: 37px;
    opacity: 0.3;
}
toolbarpaletteitem[place="toolbar"] > toolbaritem.ucf-additional-springs,
toolbarpaletteitem[place="panel"] > toolbaritem.ucf-additional-springs,
toolbarpaletteitem[place="menu-panel"] > toolbaritem.ucf-additional-springs {
    background: white url("./svg/spring.svg") center no-repeat;
    border: none;
    min-width: 34px;
    min-height: 14px;
    outline: 1px solid currentColor;
    outline-offset: -1px;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3;
}
toolbarpaletteitem[place="palette"][id*="ucf-additional-top-spring"],
toolbarpaletteitem[place="palette"][id*="ucf-additional-vertical-spring"],
toolbarpaletteitem[place="palette"][id*="ucf-additional-bottom-spring"] {
    vertical-align: top;
}
toolbarpaletteitem[place] > toolbaritem.ucf-additional-springs {
    -moz-window-dragging: no-drag !important;
}
#ucf-restart-app {
    list-style-image: url("./svg/restart-app-16.svg") !important;
    fill: #f38725 !important;
}
#ucf-additional-vertical-toggle-button {
    list-style-image: url("./svg/vert-toolbar.svg") !important;
}
#ucf-additional-top-toggle-button {
    list-style-image: url("./svg/top-toolbar.svg") !important;
}
#ucf-additional-bottom-toggle-button {
    list-style-image: url("./svg/bottom-toolbar.svg") !important;
}
#ucf-view-history-sidebar-button {
    list-style-image: url("./svg/history-16.svg") !important;
}
#ucf-view-bookmarks-sidebar-button {
    list-style-image: url("./svg/bookmark-16.svg") !important;
}
#ucf-open-directories-button {
    list-style-image: url("./svg/user-home-16.svg") !important;
}
#browser-bottombox:not([lwthemefooter="true"]):-moz-lwtheme {
    background-color: transparent !important;
}
#ucf-additional-vertical-box {
    background: none !important;
    border: none !important;
    box-shadow: none !important;
    position: relative !important;
    z-index: 3 !important;
    margin: -1px 0 !important;
}
#ucf-additional-top-bar,
#ucf-additional-vertical-bar,
#ucf-additional-bottom-bar {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    border-block: none !important;
    overflow: -moz-hidden-unscrollable;
    overflow: clip;
}
#ucf-additional-bottom-bar {
    border-top: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-bottom-bar #ucf-additional-bottom-closebutton {
    margin: 0 var(--toolbarbutton-outer-padding, 1px) !important;
    padding: var(--toolbarbutton-inner-padding, 4px) !important;
}
#ucf-additional-bottom-bar #ucf-additional-bottom-closebutton .toolbarbutton-icon {
    margin: 0 !important;
    padding: 0 !important;
    height: 16px !important;
    width: 16px !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar,
#ucf-additional-bottom-bar {
    -moz-window-dragging: no-drag !important;
    background-clip: border-box !important;
    background-origin: border-box !important;
    background-color: var(--toolbar-bgcolor, -moz-Dialog) !important;
    background-image: var(--toolbar-bgimage, linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15))) !important;
    color: var(--toolbar-color, -moz-DialogText) !important;
    border-inline: none !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar {
    padding: 1px 0 !important;
    margin: 0 !important;
    font-size: 1rem !important;
    min-width: 20px !important;
    width: auto !important;
    flex-grow: 1 !important;
    align-items: stretch !important;
    justify-content: start !important;
    flex-direction: column !important;
}
:root:-moz-lwtheme[style*="--lwt-additional-images"] #navigator-toolbox {
    background-attachment: fixed !important;
}
#ucf-additional-vertical-box:-moz-lwtheme > #ucf-additional-vertical-bar,
#ucf-additional-bottom-bar:-moz-lwtheme {
    color: var(--toolbar-color, inherit) !important;
    background-repeat: no-repeat, var(--lwt-background-tiling, repeat-y) !important;
    background-size: auto auto !important;
    background-attachment: fixed !important;
    background-position: right top, var(--lwt-background-alignment, right top) !important;
    background-color: transparent !important;
    background-image: linear-gradient(var(--toolbar-bgcolor, rgba(255,255,255,.4)), var(--toolbar-bgcolor, rgba(255,255,255,.4))), var(--lwt-header-image, var(--lwt-additional-images, none)) !important;
}
:root[lwtheme-image="true"] #ucf-additional-vertical-box:-moz-lwtheme > #ucf-additional-vertical-bar,
:root[lwtheme-image="true"] #ucf-additional-bottom-bar:-moz-lwtheme {
    background-repeat: no-repeat, repeat-y !important;
}
:root #browser-bottombox[lwthemefooter="true"] #ucf-additional-bottom-bar:-moz-lwtheme {
    background-repeat: initial !important;
    background-attachment: initial !important;
    background-position: initial !important;
    background-color: var(--toolbar-bgcolor, rgba(255,255,255,.4)) !important;
    background-image: none !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-lwtheme > #ucf-additional-vertical-bar {
    background-color: var(--lwt-accent-color, white) !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-lwtheme-brighttext > #ucf-additional-vertical-bar {
    background-color: var(--lwt-accent-color, black) !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    border-inline-end: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-vertical-box[v_vertical_bar_start="false"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    border-inline-end: none !important;
    border-inline-start: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar[collapsed="true"] {
    padding: 0 !important;
    border: none !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons {
    flex-direction: column !important;
    margin-inline: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons > toolbarbutton.toolbarbutton-combined > .toolbarbutton-text {
    padding-inline: 1px !important;
    margin-inline: 0 !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons separator {
    display: none !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarspring {
	min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarseparator {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    margin: 2px !important;
    margin-top: 4px !important;
    margin-bottom: 0 !important;
    border: none !important;
    border-top: 1px solid currentColor !important;
    width: auto !important;
    max-width: none !important;
    height: 5px !important;
    min-height: 5px !important;
    max-height: 5px !important;
    opacity: 0.3 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarspacer {
	height: 15px !important;
}
#ucf-additional-vertical-container {
    display: flex !important;
}
#ucf-additional-vertical-container[vertautohide="true"] {
    position: relative !important;
    min-width: 100px !important;
    width: 100px !important;
    max-width: 100px !important;
    overflow: visible !important;
    margin-inline-start: 0 !important;
    margin-inline-end: -100px !important;
    pointer-events: none !important;
    visibility: hidden !important;
}
#ucf-additional-vertical-container[vertautohide="true"][v_vertical_bar_start="false"] {
    margin-inline-start: -100px !important;
    margin-inline-end: 0 !important;
}
#ucf-additional-vertical-box[vertautohide="true"] {
    position: absolute !important;
    display: block !important;
    top: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    right: auto !important;
    font-size: 0px !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"],
#ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl) {
    left: auto !important;
    right: 0 !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl) {
    left: 0 !important;
    right: auto !important;
}
#ucf-additional-vertical-box[vertautohide="true"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: calc(-1 * (100% - 5px));
    right: auto;
    opacity: 0;
    animation-name: toolbar-hide !important;
    animation-timing-function: linear !important;
    animation-duration: 0.2s !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-property: opacity !important;
    transition-timing-function: step-start !important;
    transition-duration: 0s !important;
    transition-delay: 0.2s !important;
    pointer-events: auto !important;
    height: 100% !important;
    visibility: visible !important;
    position: relative !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl) > #ucf-additional-vertical-bar:not([collapsed="true"]),
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: auto;
    right: calc(-1 * (100% - 5px));
    animation-name: toolbar-hide-rtl !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl) > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: calc(-1 * (100% - 5px));
    right: auto;
    animation-name: toolbar-hide !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: 0px;
    right: auto;
    opacity: 1;
    animation-name: toolbar-visible !important;
    transition-delay: 0s !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl)[v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]),
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"][v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: auto;
    right: 0px;
    animation-name: toolbar-visible-rtl !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl)[v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: 0px;
    right: auto;
    animation-name: toolbar-visible !important;
}
@keyframes toolbar-hide {
    from {
        left: 0px;
    }
    to {
        left: calc(-1 * (100% - 5px));
    }
}
@keyframes toolbar-hide-rtl {
    from {
        right: 0px;
    }
    to {
        right: calc(-1 * (100% - 5px));
    }
}
@keyframes toolbar-visible {
    from {
        left: calc(-1 * (100% - 5px));
    }
    to {
        left: 0px;
    }
}
@keyframes toolbar-visible-rtl {
    from {
        right: calc(-1 * (100% - 5px));
    }
    to {
        right: 0px;
    }
}
:root[inDOMFullscreen] #ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]),
:root[inFullscreen]:not([OSXLionFullscreen]) #ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]):not([fullscreentoolbar="true"]),
#ucf-additional-bottom-bar[collapsed="true"][customizable="true"][customizing="true"] {
    visibility: collapse !important;
    padding: 0 !important;
    border: none !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > #personal-bookmarks {
    width: 0 !important;
    flex-grow: 0 !important;
    margin: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > #search-container {
    width: 0 !important;
    max-height: 0 !important;
    overflow-y: visible !important;
    flex-grow: 0 !important;
    min-width: 80px !important;
    margin: 0 !important;
}
:root[chromehidden~="location"][chromehidden~="toolbar"] #ucf-additional-vertical-container {
    display: none !important;
}
@supports (fill: color-mix(in srgb, currentColor 20%, transparent)) {
    #ucf-restart-app {
        fill: color-mix(in srgb, currentColor 20%, #f38725) !important;
    }
}

voqabuhe пишет

Findbar вниз уехал

Рядом с (или вместо) -moz-box-ordinal-group: 0 !important;


order: -1 !important;

Dumby пишет

Рядом с (или вместо) -moz-box-ordinal-group: 0 !important;

order: -1 !important;

Вот спасибо большое. Теперь всё на месте.

09-05-2023 13:29:38

unter_officer пишет

Мой vertical_top_bottom_bar.css:

Спасибо, с вашим действительно всё нормально. Таки значит я где-то ошибся. :dumb: Теперь два рабочих варианта.

voqabuhe пишет

Спасибо, с вашим действительно всё нормально.

Вот и ладушки. :)
А спасибо не мне, а Виталию.

voqabuhe пишет

Теперь два рабочих варианта.

Это не одно и то же.
Я просто вернуть вертикальный тулбар написал.


А то, что выложил unter_officer — это памятка типа такой,
и, соответственно, стиль, в котором сделаны все правки.


Там же не только тулбар, но ещё и чтобы на нём "дропмаркер"
кнопок типа ATB «Переключить прокси» был снизу, а не справа,
чтобы виджет «Элементы панели закладок» там не флексился,
и ещё что-то про сёрчбар, хоть я и не представляю, чтобы его туда
кто-то захотел поместить.

unter_officer пишет

А спасибо не мне, а Виталию.

Ну да, я и говорю, спасибо Виталию в вашем лице, как его представителю. :)

Dumby пишет

Это не одно и то же.

Ну теперь понятно, спасибо за разъяснения.

Dumby пишет

order: -1 !important;

Спасибо!


unter_officer пишет

display: -moz-box --> display: flex
-moz-box-flex --> flex-grow
-moz-box-orient: vertical --> flex-direction: column
-moz-box-align --> align-items
-moz-box-pack --> justify-content

Спасибо!


Add, и всех С Днём Победы!!!

Dumby
А кнопку Показать адрес существующей закладки при наведении на звездочку №15326 вы уже правили, есть рабочая? А то я как то пропустил, а она уже и на предыдущей версии FF не работала.

voqabuhe
https://forum.mozilla-russia.org/viewto … 49#p804649

_zt пишет

https://forum.mozilla-russia.org/viewtopic.php?pid=804649#p804649

Спасибо. Блин, ещё 20.04.23 оказывается починили, а парюсь. :dumb:  Dumby, спасибо за полезную кнопочку.

voqabuhe
Заголовок, если нужен https://forum.mozilla-russia.org/viewto … 65#p804665.

Dumby
Добавьте пожалуйста в этот скрипт функцию -> "Обновить вкладку" на ЛКМ, а "Обновить вкладку минуя кеш" перенести на ПКМ.

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

Выделить код

Код:

try {
    CustomizableUI.createWidget({
        id: "reload-skip-cache-ToolBarButton",
        type: "custom",
        onBuild: function(aDocument) {
            let toolbaritem = aDocument.createXULElement("toolbarbutton");
            let props = {
                id: "reload-skip-cache-ToolBarButton",
                class: "toolbarbutton-1 chromeclass-toolbar-additional",
                label: "Tab neu laden",
                tooltiptext: "Обновить вкладку минуя кеш",
                style: "list-style-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAOOSURBVHjabNPNb5RVFMfx77n3eZlnpjMMDNIOSCsvKoTgAmO6cuNKFP4ENrIBElyIoRtcqVg1JlZimrjGhQYTA8aNijsljUaIgCWATQTaQgCZlpl55nmee4+LTisY7/58cs49vyND732BFjm9u7ex5QpxY5DO7LVqsr65RWx51ETBNkHwWT7tfXcqnZu7ljS3Lrpum6BaJ7s3O6i9zMujkIlLRKvrr9hK5WBp/YY9SXMjQa0KQLHwkHT+L9K52W+Lh+1Jzf03EkYjJo7OFK3W2RUovT1biweHjlWf3XGkvGGz0dTjeimoBwAx2DhGkoDOzeu+M3P1M0ywozH60ov3fj57QobGv8SnHUDerz03ejSurcZ12iAgIoAsQSiqCgqmlFCkbUQMQZxw99wPEzL4zuf4tLu3vGn710lzo/GdNhiDIPi8h7rCAWCstVGMouAVCUJQBZQHv09NSO3Au+XS+uGv6jtHX9a0A/0uevdutx7OXDmdzt84hxHKzaeeT57ctCdeO7hOMID2J7a0pn+bkDVHPtmVNIenkuaI1bwHxtK7f2dhYfr8fjHmFICJS5iotMHEpe+qT+/cLs4/Bi1evzgha8cmD1RGtk6aIAYU7wpa0xdOum57X1ipYssDqCu2m1Ll5MDmbbtwOZrn/c5BjGFx5soJaRz9dLwyvHUMVRDB91L39/mfDosxkyapIDYEeCOqNz4yYZRqnq0gy8/10relcWRiPBkaHltajuCL3LUu/nIYmDSlBIzFhFEUDtSsFrliLbK8SRFQRb3Ppf76hwdKawYnTRj15xbat/48WbQX95m4jGYpJk6wUYzvdQlqdawN8F5R7xAbLA1TO3R8VxiXp8Ja3eI9GIv3bqFza2a/2OCU5hmqiolKiLVPxGvXBaYo7vgsdS5NwQZLcRl47a2yjeJTpfqa3ar9TQQRHlrZwoPTRev+OWAxWNVoRLVVGLiiRXZJ83zOZb1c3VLMZNXB46grXrWBPR2UymYFMxZsgISRAzqaZ/O44pJ69ytwAfjD5/kt9a4LgtQPjaOuwGe98SCKx0wYLEek/6ErF5IBN4DLCBd9Xlx2Lv9ejJ0HliC8o+h2qqDHbBC8KTYw/9Y/5jmFlnfFeV8UH4ixP5ooylB9DFou2w16UET2mv8crVdFVc+IyMeKnBUxmCjifyExBl/kA4JsQeQF4Jm+dhXVKUWv2zBc9F55FPpnAAjpv6gFuxLzAAAAAElFTkSuQmCC)",
                oncommand: "BrowserReloadSkipCache();"
            };
            for (let p in props)
                toolbaritem.setAttribute(p, props[p]);
            return toolbaritem;
        }
    });
} catch(e) {}

kokoss
https://forum.mozilla-russia.org/viewtopic.php?pid=784200#p784200

kokoss

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	id: "reload-skip-cache-ToolBarButton",
	label: "Tab neu laden",
	tooltiptext:
		"ЛКМ: Обновить вкладку\n" +
		"ПКМ: Обновить вкладку минуя кеш",

	localized: false,
	onCreated(btn) {
		btn.oncontextmenu = this.context;
		btn.setAttribute("oncommand", "BrowserReload()");
		btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAOOSURBVHjabNPNb5RVFMfx77n3eZlnpjMMDNIOSCsvKoTgAmO6cuNKFP4ENrIBElyIoRtcqVg1JlZimrjGhQYTA8aNijsljUaIgCWATQTaQgCZlpl55nmee4+LTisY7/58cs49vyND732BFjm9u7ex5QpxY5DO7LVqsr65RWx51ETBNkHwWT7tfXcqnZu7ljS3Lrpum6BaJ7s3O6i9zMujkIlLRKvrr9hK5WBp/YY9SXMjQa0KQLHwkHT+L9K52W+Lh+1Jzf03EkYjJo7OFK3W2RUovT1biweHjlWf3XGkvGGz0dTjeimoBwAx2DhGkoDOzeu+M3P1M0ywozH60ov3fj57QobGv8SnHUDerz03ejSurcZ12iAgIoAsQSiqCgqmlFCkbUQMQZxw99wPEzL4zuf4tLu3vGn710lzo/GdNhiDIPi8h7rCAWCstVGMouAVCUJQBZQHv09NSO3Au+XS+uGv6jtHX9a0A/0uevdutx7OXDmdzt84hxHKzaeeT57ctCdeO7hOMID2J7a0pn+bkDVHPtmVNIenkuaI1bwHxtK7f2dhYfr8fjHmFICJS5iotMHEpe+qT+/cLs4/Bi1evzgha8cmD1RGtk6aIAYU7wpa0xdOum57X1ipYssDqCu2m1Ll5MDmbbtwOZrn/c5BjGFx5soJaRz9dLwyvHUMVRDB91L39/mfDosxkyapIDYEeCOqNz4yYZRqnq0gy8/10relcWRiPBkaHltajuCL3LUu/nIYmDSlBIzFhFEUDtSsFrliLbK8SRFQRb3Ppf76hwdKawYnTRj15xbat/48WbQX95m4jGYpJk6wUYzvdQlqdawN8F5R7xAbLA1TO3R8VxiXp8Ja3eI9GIv3bqFza2a/2OCU5hmqiolKiLVPxGvXBaYo7vgsdS5NwQZLcRl47a2yjeJTpfqa3ar9TQQRHlrZwoPTRev+OWAxWNVoRLVVGLiiRXZJ83zOZb1c3VLMZNXB46grXrWBPR2UymYFMxZsgISRAzqaZ/O44pJ69ytwAfjD5/kt9a4LgtQPjaOuwGe98SCKx0wYLEek/6ErF5IBN4DLCBd9Xlx2Lv9ejJ0HliC8o+h2qqDHbBC8KTYw/9Y/5jmFlnfFeV8UH4ixP5ooylB9DFou2w16UET2mv8crVdFVc+IyMeKnBUxmCjifyExBl/kA4JsQeQF4Jm+dhXVKUWv2zBc9F55FPpnAAjpv6gFuxLzAAAAAElFTkSuQmCC"
	},
	context(e) {
		return e.ctrlKey || e.shiftKey || Boolean(e.view.BrowserReloadSkipCache());
	}
}))();

Dumby пишет

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

Благодарю!!!


_zt пишет

https://forum.mozilla-russia.org/viewtopic.php?pid=784200#p784200

Интересный вариант, а как отключить анимацию(кнопку СТОП) кнопки при обновлении вкладки ?

kokoss
Как то так, один из вариантов, просто как например

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

Выделить код

Код:

#stop-button {
	list-style-image: url("chrome://global/skin/icons/reload.svg") !important
}

Не понимаю, зачем отключать анимацию, что бы потом добавить невменяемо работающий индикатор? (А вменяемо работающих я не видел) В адресную строку или еще куда. Это же не анимация, это кнопка СТОП.

Dumby
Можно ускорить скрытие уведомлений в этих скриптах?

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

Выделить код

Код:

// Копировать значок сайта в base64
function WebScreenShotonImage(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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
    var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
    sss.loadAndRegisterSheet(uri, 0);

    Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "\nЗначок скопирован как base64", false, "", (s, t)=> {
        if (t == 'alertfinished')
            sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
    }, "");
};
Выделить код

Код:

// Копировать изображение или текстовой файл в base64 ...
function copyFileToBase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
    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, 0600, 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 sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
    var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
    sss.loadAndRegisterSheet(uri, 0);

    Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Файл скопирован как base64", false, "", (s, t)=> {
        if (t == 'alertfinished')
            sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
        }, "");
    });
};

_zt
Ну, nsIAlertsService имеет метод closeAlert();
Можно вызвать его с таймаутом.

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

Выделить код

Код:

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

as.showAlertNotification(
	"about:logo",
	"Title",
	"Message",
	false,
	"",
	(subject, topic) => console.log(topic),
	alertName
);

setTimeout(as.closeAlert, 2e3, alertName);

_zt
Сделайте общий стиль для "всплывашек". Поправил для 113-й, чей - не помню.

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

Выделить код

Код:

#alertNotification {
    padding: 5px !important;
}
#alertImage {
    max-height: 24px !important;
    max-width: 24px !important;
    margin: 4px 7px !important;
    padding: unset !important;
}
#alertTextBox {
    flex-direction: row !important;
    align-items: center !important;
    justify-content: center !important;
}
#alertBox {
    padding-block-end: 7px !important;
    box-shadow: rgba(84, 83, 87, 0.32) 0 0 5px !important;
}

Dumby

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

Выделить код

Код:

as.showAlertNotification(
        base64,
        "Скрипт Save - FaviconToBase",
        "Значок скопирован как base64",
        false,
        "",
        (subject, topic) => console.log(topic),
        alertName
    );

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

:rock:
А удаление стиля как прикрутить? Оно нужно вообще? Если его из оригинала вставить после "", то таймаут перестает работать.
   
xrun1
Да, что то я не подумал об этом.
скрытый текст

Выделить код

Код:

@-moz-document url("chrome://global/content/alerts/alert.xhtml") {
#alertTitleBox {
    padding: 0 10px !important;
    font-size: 14px !important;
}
#alertImage {
    max-height: 36px !important;
    max-width: 36px !important;
    margin: 4px 10px !important;
    padding: unset !important;
}
#alertTextBox {
    min-width: 300px !important;
    font-weight: bold;
    flex-direction: row !important;
    align-items: center !important;
    justify-content: center !important;
}
#alertBox {
    box-shadow: rgba(10, 10, 12, 0.32) 0 0 5px !important;
}
@media (prefers-color-scheme: dark) {
#alertBox {
    border-color: rgb(0, 116, 232) !important;
    background-color: var(--menu-background-color, var(--arrowpanel-background, Field)) !important;
    color: var(--menu-color, var(--arrowpanel-color, FieldText)) !important;
}
}
}

Здравствуйте всем. Кто знает, они работают до сих пор? Просто, второй вроде отвалился. Есть какой-то аналог, или посвежее?
https://forum.mozilla-russia.org/viewto … 17#p786917
https://forum.mozilla-russia.org/viewto … 19#p787019

Dumby пишет

Я вот так написал в vertical_top_bottom_bar.css

Отлично! Панелька появилась, спасибо!!)))

_zt пишет

А удаление стиля как прикрутить? Оно нужно вообще?

Раз стилизировал алерт снаружи, то, наверно,
регистрация стиля в коде уже не нужна, можно убрать.
А в showAlertNotification() шестым аргументом тогда сделать просто null


b0ttle пишет

Кто знает, они работают до сих пор?

Что-то мешает самому проверить?
p786917 — вроде работает, только showBrowserPageActionFeedback() нету.
Если очень надо, можно попробовать чем-нибудь другим заменить.

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

Выделить код

Код:

/*
		showBrowserPageActionFeedback(PageActions.actionForID("copyURL"));
	},
*/
		this.hint();
	},
	hint() {
		var anchor = window.document.getElementById("identity-icon");
		var document = {l10n: {setAttributes(lab) {
			lab.textContent = "Скопировано в буфер обмена!";
		}}};
		(this.hint = eval(`(function ${ConfirmationHint.show})`)
			.bind(ConfirmationHint, anchor, "")
		)();
	},

второй вроде отвалился

Что второй? По ссылке — лишь фрагмент кода.
И, отваливаться в нём особо нечему.
Другое дело, что на Services.appinfo.invalidateCachesOnRestart()
нельзя надёжно положиться.

Dumby
Только начал разбираться, и у меня первый не работал. Второй еще не проверял, он от Vitaliy V.
Просто, смотреть работает или нет, без знания кода. Такое себе, нужно методом тыка. Времени много занимает.
Извините, если что. Понимаю, каждому объяснять и поправлять код, любой устанет) Как и Vitaliy V. Собственно, поэтому нас и покинул, как мне кажется.
Второй код Vitaliy V.

А тут моя каша, когда-то сделанная из двух примеров. Не мог решить, что использовать. Так и осталось, пару лет не возвращался.
Там бардак, осторожно! Сам не могу разобраться)

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

Выделить код

Код:

/* https://forum.mozilla-russia.org/viewtopic.php?pid=786917#p786917 ||000|001Alt|101Ctrl+Alt|100Ctrl|110Ctrl+Shift
   {Vit.V.786906#p786906||786926#p786926 https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values 
   Console> document.addEventListener("keydown",(e)=>console.log(e.code));||e.preventDefault(); (prevent default!..)
   https://forum.mozilla-russia.org/viewtopic.php?pid=787019#p787019
   }*/
// https://support.mozilla.org/ru/kb/sochetaniya-klavish#w_okna-i-vkladki || https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly
//============================================================
//forum.mozilla-russia.org/viewtopic.php?pid=777255#p777255||forum.ru-board.com/topic.cgi?forum=5&topic=50187&start=1640#21
//addEventListener('keydown',e=>{if(e.ctrlKey&&!e.altKey&&!e.shiftKey&&(e.keyCode==70)&&!gFindBar.hidden){e.preventDefault();gFindBar.close();}});
                   //"KeyF":()=>{if(e.ctrlKey&&!e.altKey&&!e.shiftKey&&window.gFindBarInitialized&&!gFindBar.hidden){e.preventDefault();gFindBar.close?.();}},
             	   //LS_Vit//"KeyF":{true_false_false=>{if(!window.gFindBarInitialized||gFindBar.hidden)return;e.preventDefault();gFindBar.close?.();}},
				   //LS_Vit//"KeyV":{"true_false_false":(e)=>{if(this.skip)return;this.ch.copyString(gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec);showBrowserPageActionFeedback(PageActions.actionForID("copyURL"));}},
//addEventListener("keydown",e=>e.ctrlKey&&e.code=="KeyQ"&&!e.shiftKey&&!e.altKey&&e.preventDefault()+RunQT());
//============================================================
(this.keyboardshortcuts={
get skip(){return docShell.isCommandEnabled("cmd_insertText");},
get ch(){delete this.ch;return this.ch=Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);},
init(that){var keydown={

/*test*/"Backquote":{"false_false_false":(e)=>{this.skip||e.preventDefault();gBrowser.loadURI("javascript:(function(bookmarklets)%7Bfor(var%20i=0;i%3Cbookmarklets.length;i++)%7Bvar%20code=bookmarklets%5Bi%5D.url;if(code.indexOf(%22javascript:%22)!=-1)%7Bcode=code.replace(%22javascript:%22,%22%22);eval(code)%7Delse%7Bcode=code.replace(/%5Es+%7Cs+$/g,%22%22);if(code.length%3E0)%7Bwindow.open(code)%7D%7D%7D%7D)(%5B%7Btitle:%22%E7%A0%B4%E9%99%A4%E5%8F%B3%E9%94%AE%E8%8F%9C%E5%8D%95%E9%99%90%E5%88%B6%22,url:%22javascript:function%20applyWin(a)%7Bif(typeof%20a.__nnANTImm__===%5Cx22undefined%5Cx22)%7Ba.__nnANTImm__=%7B%7D;a.__nnANTImm__.evts=%5B%5Cx22mousedown%5Cx22,%5Cx22mousemove%5Cx22,%5Cx22copy%5Cx22,%5Cx22contextmenu%5Cx22%5D;a.__nnANTImm__.initANTI=function()%7Ba.__nnantiflag__=true;a.__nnANTImm__.evts.forEach(function(c,b,d)%7Ba.addEventListener(c,this.fnANTI,true)%7D,a.__nnANTImm__)%7D;a.__nnANTImm__.clearANTI=function()%7Bdelete%20a.__nnantiflag__;a.__nnANTImm__.evts.forEach(function(c,b,d)%7Ba.removeEventListener(c,this.fnANTI,true)%7D,a.__nnANTImm__);delete%20a.__nnANTImm__%7D;a.__nnANTImm__.fnANTI=function(b)%7Bb.stopPropagation();return%20true%7D;a.addEventListener(%5Cx22unload%5Cx22,function(b)%7Ba.removeEventListener(%5Cx22unload%5Cx22,arguments.callee,false);if(a.__nnantiflag__===true)%7Ba.__nnANTImm__.clearANTI()%7D%7D,false)%7Da.__nnantiflag__===true?a.__nnANTImm__.clearANTI():a.__nnANTImm__.initANTI()%7DapplyWin(top);var%20fs=top.document.querySelectorAll(%5Cx22frame,%20iframe%5Cx22);for(var%20i=0,len=fs.length;i%3Clen;i++)%7Bvar%20win=fs%5Bi%5D.contentWindow;try%7Bwin.document%7Dcatch(ex)%7Bcontinue%7DapplyWin(fs%5Bi%5D.contentWindow)%7D;void%200;%22%7D,%7Btitle:%22%E7%A0%B4%E9%99%A4%E9%80%89%E6%8B%A9%E5%A4%8D%E5%88%B6%E9%99%90%E5%88%B6%22,url:%22javascript:(function()%7Bvar%20doc=document;var%20bd=doc.body;bd.onselectstart=bd.oncopy=bd.onpaste=bd.onkeydown=bd.oncontextmenu=bd.onmousemove=bd.onselectstart=bd.ondragstart=doc.onselectstart=doc.oncopy=doc.onpaste=doc.onkeydown=doc.oncontextmenu=null;doc.onselectstart=doc.oncontextmenu=doc.onmousedown=doc.onkeydown=function%20()%7Breturn%20true;%7D;with(document.wrappedJSObject%7C%7Cdocument)%7Bonmouseup=null;onmousedown=null;oncontextmenu=null;%7Dvar%20arAllElements=document.getElementsByTagName(%5Cx27*%5Cx27);for(var%20i=arAllElements.length-1;i%3E=0;i--)%7Bvar%20elmOne=arAllElements;with(elmOne.wrappedJSObject%7C%7CelmOne)%7Bonmouseup=null;onmousedown=null;%7D%7Dvar%20head=document.getElementsByTagName(%5Cx27head%5Cx27)%5B0%5D;if(head)%7Bvar%20style=document.createElement(%5Cx27style%5Cx27);style.type=%5Cx27text/css%5Cx27;style.innerHTML=%5Cx22html,*%7B-moz-user-select:auto!important;%7D%5Cx22;head.appendChild(style);%7Dvoid(0);%7D)();%22%7D%5D)",{triggeringPrincipal:gBrowser.contentPrincipal});}},//Remove the right-click menu restriction
"Digit1":{"false_false_false":(e)=>{this.skip||openPreferences();}},
"Digit2":{"false_false_false":(e)=>{this.skip||BrowserPageInfo();}},
"Digit3":{"false_false_false":(e)=>{this.skip||duplicateTabIn(gBrowser.selectedTab,'tab');}},
"Digit4":{"false_false_false":(e)=>{this.skip||gBrowser.loadURI("javascript:(function(){var%20night=function(w){(function(d){var%20css='html{opacity:0.7!important;background:black!important;}body{background:white!important;}';var%20s=d.getElementsByTagName('style');for(var%20i=0,si;si=s[i];i++){if(si.innerHTML==css){si.parentNode.removeChild(si);return}};var%20heads=d.getElementsByTagName('head');if(heads.length){var%20node=d.createElement('style');node.type='text/css';node.appendChild(d.createTextNode(css));heads[0].appendChild(node)}})(w.document);%20for(var%20i=0,f;f=w.frames[i];i++){try{arguments.callee(f)}catch(e){}}};night(window)})();",{triggeringPrincipal:gBrowser.contentPrincipal});}},//Night mode
"Digit5":{"false_false_false":(e)=>{this.skip||gBrowser.loadURI("javascript:(function(){var EnRuT=[['щ','shh'],['Щ','Shh'],['Щ','SHH'],['х','hh'],['Х','Hh'],['Х','HH'],['ж','zh'],['Ж','Zh'],['Ж','ZH'],['ц','cz'],['Ц','Cz'],['Ц','CZ'],['ю','yu'],['Ю','Yu'],['Ю','YU'],['ё','yo'],['Ё','Yo'],['Ё','YO'],['я','ya'],['Я','Ya'],['Я','YA'],['ч','ch'],['Ч','Ch'],['Ч','CH'],['ш','sh'],['Ш','Sh'],['Ш','SH'],['э','e`'],['Э','E`'],['ы','y'],['Ы','Y'],['ъ','``'],['ь','`'],['р','r'],['т','t'],['у','u'],['и','i'],['о','o'],['п','p'],['а','a'],['с','s'],['д','d'],['ф','f'],['г','g'],['й','j'],['к','k'],['л','l'],['з','z'],['х','x'],['ц','c'],['в','v'],['б','b'],['н','n'],['м','m'],['Р','R'],['Т','T'],['У','U'],['И','I'],['О','O'],['П','P'],['А','A'],['С','S'],['Д','D'],['Ф','F'],['Г','G'],['Й','J'],['К','K'],['Л','L'],['З','Z'],['Х','X'],['Ц','C'],['В','V'],['Б','B'],['Н','N'],['М','M'],['е','e'],['Е','E']],A=document.activeElement;A.onkeyup=function ftr(){for(var s=A.value,i=0;i<EnRuT.length;i++){s=s.replace(RegExp(EnRuT[i][1],'g'),EnRuT[i][0])};A.value=s}})()",{triggeringPrincipal:gBrowser.contentPrincipal});}},//AutoTranslit- https://forum.ru-board.com/topic.cgi?forum=5&topic=46779&start=320#15
	"F3":{"false_false_false":(e)=>{e.preventDefault();var s=prompt('Google_ site:.. ..','');if(s.length>0)gBrowser.addTrustedTab('https://www.google.com/search?q=site:'+encodeURIComponent(gBrowser.currentURI.host)+' '+encodeURIComponent(s));}},
	"F2":{"false_false_false":(e)=>{gBrowser.loadURI("javascript:{d=document;b=d.body;o=d.createElement('scri'+'pt');o.setAttribute('src','https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit');o.setAttribute('type','text/javascript');b.appendChild(o);v=b.insertBefore(d.createElement('div'),b.firstChild);v.id='google_translate_element';v.style.display='none';p=d.createElement('scri'+'pt');p.text='function%20googleTranslateElementInit(){new%20google.translate.TranslateElement({pageLanguage:%22%22},%22google_translate_element%22);}';p.setAttribute('type','text/javascript');b.appendChild(p);}void%200",{triggeringPrincipal:gBrowser.contentPrincipal});}},
  "KeyP":{"false_true_false" :(e)=>{OpenBrowserWindow({private:true});}},
  "KeyS":{"true_true_false"  :(e)=>{FileUtils.getFile('UChrm',['user_chrome_files','_Local','.Ev','Everything.exe']).launch();}},
  "KeyU":{"true_true_false"  :(e)=>{FileUtils.getFile('UChrm',['user_chrome_files','_Local','_ultrasurf.exe']).launch();}},
  "KeyV":{"false_false_false":(e)=>{if(this.skip)return;this.ch.copyString(gURLBar.makeURIReadable(gBrowser.currentURI).displaySpec);showBrowserPageActionFeedback(PageActions.actionForID("copyURL"));}},       
  "KeyB":{"true_false_false" :(e)=>{e.preventDefault();gBrowser.toggleMuteAudioOnMultiSelectedTabs(gBrowser.selectedTab);}},
  "KeyF":{"true_true_false"  :(e)=>{FileUtils.File('D:\\.L\\_Soft\\-Port\\_Photo\\ShareX\\ShareX.exe').launch();},
  		  "true_false_false"(e){if(!gFindBar.hidden){e.preventDefault();gFindBar.close();}}},
  "KeyQ":{"true_false_false" :(e)=>{FileUtils.getFile('UChrm',['user_chrome_files','_Local','.QT','QTranslate.exe']).launch();},//Ctrl+Q
		  "true_true_false"(e,nocache=false)//Ctrl+Alt+Q
									{var cancelQuit=Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
									Services.obs.notifyObservers(cancelQuit,"quit-application-requested","restart");
									if(cancelQuit.data)return false;
									if(nocache)Services.appinfo.invalidateCachesOnRestart();
									var restart=Services.startup;
									restart.quit(restart.eAttemptQuit|restart.eRestart);},
		  "true_true_true"(e){this["true_true_false"](e,true);}},//Ctrl+Alt+Shift+Q
		//"true_false_true"(e){if(AppConstants.platform=="linux")this["true_true_false"](e,true);}},//Ctrl+Shift+Q для linux


//(this.keyboardshortcuts={
//init(that){var keydown={
handleEvent(e){if(AppConstants.platform !="macosx"){
 (this.handleEvent=e=>{this[e.code]?.[`${e.ctrlKey}_${e.altKey}_${e.shiftKey}`]?.(e);})(e);return;}
 (this.handleEvent=e=>{this[e.code]?.[`${e.metaKey}_${e.altKey}_${e.shiftKey}`]?.(e);})(e);},};
//                          "KeyQ":{"true_false_false":(e)=>{},
//                    		"000Digit2"(e){},
//this.handleEvent=e=>this[String(+e.ctrlKey) + +e.shiftKey + +e.altKey + e.code]?.(e);}, 					"000Digit2"(e){this.skip||BrowserPageInfo();},.. }
					   document.addEventListener("keydown",keydown,true);
this.destructor=()=>document.removeEventListener("keydown",keydown,true);
that.unloadlisteners.push("keyboardshortcuts");},
}).init(this);
/*(ucf.keyboardshortcuts={			||Dumby||
init(){  			     windowRoot.addEventListener("keydown",this,true);
  ucf.unloadlisteners.push("keyboardshortcuts");
  this.destructor=()=>windowRoot.removeEventListener("keydown",this,true);
  this.handleEvent=e=>this[String(+e.ctrlKey) + +e.shiftKey + +e.altKey + e.code]?.(e);}, "000Digit2"(e){this.skip||BrowserPageInfo();},.. }).init(); _//_// */


Заработал первый и второй код, ошибка была в пути к файлу.

Dumby пишет

Раз стилизировал алерт снаружи, то, наверно,
регистрация стиля в коде уже не нужна, можно убрать

Я потом уже понял что не нужна. Поспешил. Почему то подумал, что он в принципе нужен и без него значок не выведется.

Dumby пишет

шестым аргументом тогда сделать просто null

Я так понял, что это вместо (subject, topic)... , так как если перед, то таймаут опять не работает.

_zt
В дополнение к ссылке Dumby. nsIAlertsService

xrun1
Я попытаюсь разобрать, но ничего не обещаю. :)

Помогите пожалуйста запустить нижнюю панель начинающему, ткните мордой в инструкцию)
Версия Фаерфокс 112, переносная, чистая.
Распаковал у по директориям user_chrome_files.
Сделал разрешения в about:config
Запустились:
Дополнительная панель
Вертикальная панель
а Нижнюю панель не удается запустить.
При нажатии на кнопки скрыть показать дополнытельную и вертикальная реагируют.
В настройках  user_chrome_files в окне нижняя панель отмечена галочкой.
Перезагрузки лкм, скам и пкм делал по несколько раз.

miirrr пишет

а Нижнюю панель не удается запустить.

https://forum.mozilla-russia.org/viewtopic.php?pid=802991#p802991

miirrr пишет

а Нижнюю панель не удается запустить.

Я вчера скинул вам архив со всеми внесёнными правками. Вы его установили или какой-то другой вариант UCF?


P.S. Да, я вчера написал, что этот архив для Firefox версии 113. Просто у меня вчера не было возможности проверить на предыдущей версии.
Сегодня проверил на 112 - все нормально работает, все панели на своих местах.

kokoss, unter_officer огромное спасибо. Скачал сейчас 113, сразу запустилось все.
Попробовал еще раз сейчас и на 112, на той что не получалось часами, тоже сразу запустилось все.
Еще раз большое спасибо!

Еще один вопрос, возможно ли на нижнюю или боковую ставить закладки и вкладки?

miirrr
А что вам мешает попробовать? Закладки можно, вкладки нет, так как вкладки это панель, а закладки это элемент.

_zt
Разумеется я пытался, перед тем как спрашивать.
Подскажите как, у меня не получается(

miirrr
Как и все остальное. Настройки панелей (бывшая персонализация) >> Хватаете 2023.1684417323.jpg и тащите куда вам надо. После нажатия кнопки "Готово", значок превратится в элементы панели закладок (или в кнопку с меню, если перетащите на боковую панель).

miirrr пишет

возможно ли на нижнюю или боковую ставить закладки и вкладки?

Закладки и вкладки нельзя перетащить на доп.панели. Только кнопки.

xrun1 пишет

Закладки и вкладки нельзя перетащить на доп.панели. Только кнопки.

Закладки можно, только скопом.

xrun1 пишет

Закладки и вкладки нельзя перетащить на доп.панели.

Да ну!? Не знал. Видимо именно поэтому у меня закладки уже несколько лет живут на дополнительной панели.
А вот здесь я что объяснял по вашему?

kokoss пишет

Закладки можно, только скопом.

И как я этот скоп закладок могу выделить?

_zt пишет

у меня закладки уже несколько лет живут на дополнительной панели

В обычной жизни ничего на доп.панели не добавляется, кроме кнопок. Проверял на чистом профиле на крайней портабельной. miirrr спросил, я ответил. Если у Вас есть личные хаки - в студию.

xrun1
Я уже все написал, и даже ссылку вам дал на пост, больше мне добавить нечего.

_zt пишет

Настройки панелей (бывшая персонализация) >> Хватаете 2023.1684417323.jpg и тащите куда вам надо

Это кнопка. Вопрос был про отдельно взятую закладку, как на панели закладок: папки и закладки. На доп.панелях так сделать невозможно.

18-05-2023 20:29:41
kokoss
Скоп - это Вы имели ввиду кнопку?

xrun1 пишет

Скоп - это Вы имели ввиду кнопку?

Это все элементы панели закладок разом, а miirrr видимо нужно было переместить некоторые закладки, а это я не в курсе как сделать!

kokoss пишет

а miirrr видимо нужно было переместить некоторые закладки

Давайте не будем гадать. :)

miirrr пишет

Еще один вопрос, возможно ли на нижнюю или боковую ставить закладки и вкладки?

Какой вопрос, такой ответ и этот ответ был:

_zt пишет

Закладки можно, вкладки нет, так как вкладки это панель, а закладки это элемент

xrun1 пишет

Скоп - это Вы имели ввиду кнопку?

Это не кнопка, это "Элементы панели закладок", т.е. все закладки панели закладок.

_zt пишет

Давайте не будем гадать.

Давайте :)

del

Сделал я эти панели, и нижнюю и боковую в надежде,
что там можно разместить отдельные, вкладки и закладки.
Ну и несколько кнопок.
И хорошо что нельзя, потому что нашел другое решение,
которое имеет больше возможностей и удобнее для этого.
Сделал в виндовсе выезжающие панели и на на них разместил
вкладки и закладки.

Ребята,  вы помогали и советами и разными дополнениями, много за эти дни переделал.
Благодаря вашей работе стало значительно комфортнее, так что большое спасибо.
И счетчики поставил на закладки, и шрифты увеличил и в конетстном меню и в адресной, поиске,
на вкладках и закладках.

Уменьшил расстояние контекстных менюшее и увеличил шрифт.
переместил вниз вкладки, под закладки,
цвет закладок при наведении на них меняется в цвете, а в выпадающем другой цвет,
Верх вообще убрал, даже кноки, свернуть, закрыть, все... и все меню)
Столько место освободилось)
То есть у меня в самом верху
Адресная строка и поиск
ниже Закладки
и внизу вкладки.

Еще убрать бы надпись в Адресной строке "Ищите с помощью...
Убрать бы из поиска знак "лупа" или как его там?)

miirrr пишет

Еще убрать бы надпись в Адресной строке "Ищите с помощью...

Попробуйте такой стиль (в userChrome.css):

Выделить код

Код:

*|input#urlbar-input::placeholder {
  opacity: 0 !important;
}
miirrr пишет

Еще убрать бы надпись в Адресной строке "Ищите с помощью...
Убрать бы из поиска знак "лупа" или как его там?)

С этим в профильную тему -> https://forum.mozilla-russia.org/viewto … 46#p805146

Обновил UserChromeFiles и Demo-ПРОФИЛЬ для Firefox 84+
Изменения в основном для совместимости с новыми версиями Firefox, в Демо-профиле изменено 555 файлов.


Если оформление браузера «неправильное», скачайте aris-t2 стиль, соответствующий вашей версии Firefox.
    current (Firefox 110+), legacy/fx101-108, legacy/fx91-100, legacy/fx60-90
удалите папки «config, css, image» из «Ваш-профиль/chrome/user_chrome_files/custom_styles/aris-t2» и скопируйте такие же для вашего Firefox, например из fx91-100.


Дополнил диалог UCF-настроек, добавил опции: 1 Простой режим и подсказки кнопок
2 Включить кнопки управления окном (вид как в Windows 10 или скрыть кнопки окна)
3 Отладка — логи некоторых команд в консоль (например значения нажатий клавиш и кнопок)

Dumby - проблема с подсказками в твоём оригинальном скрипте перехвата кликов
Добавил изменение подсказок для элементов без .id, взял .className. На Windows 7 c Firefox 113 не пашет, подсказка не меняется.
На МакОС и Линукс на ФФ113 всё ОК: перехват кликов и изменение Tooltips по ".titlebar-button.titlebar-close" для кнопки «Закрыть»
Как исправить, чтобы менялись подсказки на Windows ? Сейчас в этом посте такие правки:

Выделить код

Код:

get "titlebar-button titlebar-close"() { return j(
		`Закрыть Firefox`,
		`◨ пр. клик	⇲ Свернуть`);
	},
……………
	var root = document.getElementById("navigator-toolbox");
……………
	var onMouseenter = e => {
		var trg = e.target, id = trg.id || trg.className;
		console.log('id= «'+ id + '» '+ Math.random());
		var hint = tooltips[id] || tooltips[(trg = trg.parentNode).id];

клики добавлять не стал, они в моём расширеном скрипте ucf_hookClicks.js

Был такой cкрипт для скрытия в Библиотеке, из списка в левой части, строчек "Журнал", "Загрузки", "Метки", "Все закладки"
https://forum.mozilla-russia.org/viewtopic.php?pid=786946#p786946
Как его переделать, чтобы удалить только "Загрузки"и "Метки"?

Dumby скрипт сохранения картинки колёсиком на Firefox 113 выдаёт ошибку:
(NS_ERROR_ILLEGAL_VALUE) [nsIIOService.newFileURI]

Посмотрите, может исправите!

Dobrov пишет

На Windows 7 c Firefox 113 не пашет, подсказка не меняется.

У меня и в каком-нибудь Firefox 97 подсказка не меняется.
Показывается нативный тултип от операционной системы.
Менять атрибут "tooltiptext" бесполезно.

скрипт сохранения картинки колёсиком на Firefox 113 выдаёт ошибку

Это потому, что в internalSave() снова изменились аргументы.
Всобачили вторым аргументом aOriginalURL
Так что, тоже вклей туда что-нибудь, url или null


doud пишет

для скрытия
чтобы удалить

Ты совсем не понимаешь что скрипт делает.
Он ничего не скрывает, и ничего не удаляет, он — подменяет.


Теоретически, можно было бы создать папку,
куда скопировать Журнал, Панель закладок, Меню закладок, Другие закладки.
Затем вычислить guid этой папки, и скормить в код:


//.replace("RESULTS_AS_LEFT_PANE_QUERY", "RESULTS_AS_ROOTS_QUERY"));
.replace(/(`place:).+?RESULTS_AS_LEFT_PANE_QUERY}/, "$1parent=guid"));


Но, безотносительно кода, насколько я вижу, само наличие такой папки — есть мина!
При некоторых операциях с такой папкой (или с папкой содержащей эту папку),
например, таких как перетаскивание или копирование, браузер просто зависнет. Увы.

Dumby пишет

У меня и в каком-нибудь Firefox 97 подсказка не меняется.
Показывается нативный тултип от операционной системы.
Менять атрибут "tooltiptext" бесполезно.

На Линуксе и Маке подсказки меняются без проблем, может есть какой-то трюк и для Windows ?


Dumby пишет

Это потому, что в internalSave() снова изменились аргументы.
Всобачили вторым аргументом aOriginalURL

ClickPicSave cделал, как у тебя для saveURL(...args), картинку сохраняет. Dumby – проверь, может неправильно ?

Выделить код

Код:

var args = [url,
null, // document
……………
win.document.nodePrincipal];
var {length} = win.internalSave, lfix = length > 15;
lfix && args.splice(1, 0, null);

Dumby
Есть ваш код для переключения стиля https://forum.mozilla-russia.org/viewto … 42#p795542, я его использую для переключения стиля скрытия панели вкладок.
Использую с reg();, т.е. с инициализацией при старте браузера.
При открытии некоторых дополнительных окон, скрипт самопроизвольно переключает стиль. Открылось новое окно, например, "новое приватное" или окно NoScript (NoScript запросы в окнах открывает) и стиль переключился - вкладки появились, открыл еще одно окно и стиль переключился - вкладки пропали (причем во всех окнах имеющих панель табов). На другие окна, например, менеджера кук или информации о странице, скрипт так не реагирует.
Если стиль использовать не из скрипта, то он себя так не ведет.
Можете это поправить?

_zt пишет

Использую с reg();, т.е. с инициализацией при старте браузера.

Мне предлагается угадать,
куда именно вызов reg() был вписан?


Угадатель из меня никакой.
Хотелось бы думать, что сразу после var reg = …;
но, видимо, это не так.

Dumby
Не, после btn._handleClick = reg;
   
Спасибо, теперь нормально.

Dumby можно вас попросить сделать кнопку для UCF для переключения прокси в ручная настройка прокси и без прокси.что б эта кнопка была в строке адреса,когда вкл ручную настройку прокси что б запускалось приложение  opera-proxy.windows-386.ехе а при повторном нажатии кнопки переклчалась на без прокси и завершался процесс opera-proxy.windows-386.ехе

egorsemenov06
Что-то я не представляю как такое сделать.


Если бы иметь уверенность, что exe'шник будет
запускаться только этим кодом, тогда ещё можно попробовать.


Но ведь он может быть запущен в любой момент иным способом,
и, наверно, получается, что это бесполезная писанина для custom_script.js

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

Выделить код

Код:

(async url => {

	var path = ".......\\opera-proxy.windows-386.exe";

	var icons = [
		'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect fill="limegreen" x="0" y="0" width="16" height="16"/></svg>',
		'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect fill="red" x="0" y="0" width="16" height="16"/></svg>'
	];
	var labels = [
		"Active",
		"Not Active"
	];
	var tooltips = [
		"Active",
		"Not Active"
	];

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

	exp.PageActions.addAction(new exp.PageActions.Action({
		title: labels[1],
		iconURL: icons[1],
		pinnedToUrlbar: true,
		id: "ucf-opera-proxy",
		onPlacedInUrlbar(node) {
			var pref = Services.prefs.getIntPref(type) == 1, proc, active;
			var upd = () => {
				var state = pref && Boolean(proc);
				if (state == active) return;

				var ind = +!(active = state);
				this.setIconURL(icons[ind]);
				this.setTitle(labels[ind]);
				this.setTooltip(tooltips[ind]);
			}
			this["nsPref:changed"] = () => upd(
				pref = Services.prefs.getIntPref(type) == 1
			);
			this["process-finished"] = this["process-failed"] = () => upd(proc = null);

			this["quit-application-granted"] = t => {
				Services.obs.removeObserver(this, t);
				Services.prefs.removeObserver(type, this);
				proc?.kill();
			}
			this.observe = (s, topic) => this[topic](topic);
			Services.prefs.addObserver(type, this);
			Services.obs.addObserver(this, "quit-application-granted");

			var {id} = node;
			var style = `#${id} {display: flex !important;}\n` +
				`@media (max-width: 680px) {#${id} {visibility: collapse !important;}}`;

			(this.onPlacedInUrlbar = this._onPlacedInUrlbar = node => {
				var sheet = new node.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				node.ownerDocument.adoptedStyleSheets.push(sheet);
			})(node);

			var run = () => {
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				(run = () => {
					proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
					//proc.startHidden = true;
					try {proc.init(file); proc.runwAsync([], 0, this);}
					catch {proc = null;}
					upd();
				})();
			}
			this._onCommand = () => {
				if (active) Services.prefs.setIntPref(type, 0), proc.kill();
				else pref || Services.prefs.setIntPref(type, 1), proc || run();
			}
		}
	}));
})("resource:///modules/PageActions.");

Dumby пишет

egorsemenov06
Что-то я не представляю как такое сделать.


Если бы иметь уверенность, что exe'шник будет
запускаться только этим кодом, тогда ещё можно попробовать.


Но ведь он может быть запущен в любой момент иным способом,
и, наверно, получается, что это бесполезная писанина для custom_script.js

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

Выделить код

Код:

(async url => {

	var path = ".......\\opera-proxy.windows-386.exe";

	var icons = [
		'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect fill="limegreen" x="0" y="0" width="16" height="16"/></svg>',
		'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"><rect fill="red" x="0" y="0" width="16" height="16"/></svg>'
	];
	var labels = [
		"Active",
		"Not Active"
	];
	var tooltips = [
		"Active",
		"Not Active"
	];

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

	exp.PageActions.addAction(new exp.PageActions.Action({
		title: labels[1],
		iconURL: icons[1],
		pinnedToUrlbar: true,
		id: "ucf-opera-proxy",
		onPlacedInUrlbar(node) {
			var pref = Services.prefs.getIntPref(type) == 1, proc, active;
			var upd = () => {
				var state = pref && Boolean(proc);
				if (state == active) return;

				var ind = +!(active = state);
				this.setIconURL(icons[ind]);
				this.setTitle(labels[ind]);
				this.setTooltip(tooltips[ind]);
			}
			this["nsPref:changed"] = () => upd(
				pref = Services.prefs.getIntPref(type) == 1
			);
			this["process-finished"] = this["process-failed"] = () => upd(proc = null);

			this["quit-application-granted"] = t => {
				Services.obs.removeObserver(this, t);
				Services.prefs.removeObserver(type, this);
				proc?.kill();
			}
			this.observe = (s, topic) => this[topic](topic);
			Services.prefs.addObserver(type, this);
			Services.obs.addObserver(this, "quit-application-granted");

			var {id} = node;
			var style = `#${id} {display: flex !important;}\n` +
				`@media (max-width: 680px) {#${id} {visibility: collapse !important;}}`;

			(this.onPlacedInUrlbar = this._onPlacedInUrlbar = node => {
				var sheet = new node.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				node.ownerDocument.adoptedStyleSheets.push(sheet);
			})(node);

			var run = () => {
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				(run = () => {
					proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
					//proc.startHidden = true;
					try {proc.init(file); proc.runwAsync([], 0, this);}
					catch {proc = null;}
					upd();
				})();
			}
			this._onCommand = () => {
				if (active) Services.prefs.setIntPref(type, 0), proc.kill();
				else pref || Services.prefs.setIntPref(type, 1), proc || run();
			}
		}
	}));
})("resource:///modules/PageActions.");

Спасибо Большое!!!!!Просто шикарно!!!

Dumby а можно добавить в эту кнопку opera-proxy что бы автоматом обновлялась текущая страница при включении и выключении этой кнопки с небольшой задержой (спасибо _zt) и чтобы при выходе из firefox переключалась  на без прокси а при перезагрузке не закрывалось приложение opera-proxy.windows-386.exe

egorsemenov06 пишет

Dumby а можно добавить в эту кнопку opera-proxy что бы автоматом обновлялась текущая страница при включении и выключении этой кнопки

С некоторой задежкой.

_zt пишет

С некоторой задежкой.

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

Выделить код

Код:

/*
			this._onCommand = () => {
*/
			var timeout = 1500;

			var tid, oldState, reload = (gb, tab, state) => {
				tid = null;
				active != state && gb.reloadTab(tab);
			}
			this._onCommand = e => {
				tid ? clearTimeout(tid) : oldState = active;
				var gb = e.view.gBrowser;
				tid = setTimeout(reload, timeout, gb, gb.selectedTab, oldState);

Dumby пишет
_zt пишет

С некоторой задежкой.

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

Выделить код

Код:

/*
			this._onCommand = () => {
*/
			var timeout = 1500;

			var tid, oldState, reload = (gb, tab, state) => {
				tid = null;
				active != state && gb.reloadTab(tab);
			}
			this._onCommand = e => {
				tid ? clearTimeout(tid) : oldState = active;
				var gb = e.view.gBrowser;
				tid = setTimeout(reload, timeout, gb, gb.selectedTab, oldState);

Большое Спасибо!!!!!!при выходе и перезапуске из firefox что бы переключалась на без прокси невозможно сделать?а то когда закрываю или перезапускаю с вкл.прокси, то  opera-proxy.windows-386 закрываеться, а потом запускаю  браузер и в настроках сети остаеться ручная настройка прокси, хотя иконка на кнопке красная "Not Active"

egorsemenov06 пишет

остаеться ручная настройка прокси, хотя иконка на кнопке красная "Not Active"

Всё правильно.
Active — это считается когда и настройка, и запущенный кодом exe'шник.


Например, когда Active, если снаружи переключить только настройку,
или только завершить процесс, тогда это уже считается как Not Active.

при выходе и перезапуске из firefox что бы переключалась на без прокси невозможно сделать?

Ну почему же, вполне возможно.
Перед proc?.kill(); дописать active && Services.prefs.setIntPref(type, 0);

Dumby пишет

Перед proc?.kill(); дописать active && Services.prefs.setIntPref(type, 0);

Вы просто Волшебник!!!Спасибо ВАМ еще раз от души!!!!

Dumby
Спасибо.
   
egorsemenov06
Это можно просто в user.js прописать - network.proxy.type - при включении, после любой перезагрузки, будет восстанавливаться заданное значение.

Друзья, поделитесь, пожалуйста, настроенным комплектом UCF версии от 2021-9-23
с примерами подключенных скриптов и стилей.
У меня в состоянии UCF "из коробки" на [firefox] 114.0 дополнительная и вертикальная панель не появляются... :(

Viatcheslav
Примеры там по умолчанию есть, в описании все написано.
https://m.freespeech.club/cQjSgTzr.7z
Версию я сам менял, так как она была перевыложена в ноябре с какими то изменениями сделанными в октябре.

Как сюда добавить хоткей? Не хочется лишний раз дублировать код. Там что-то такое:
if (e.code == "KeyQ" && e.ctrlKey && e.altKey), не знаю как подставить.

ucf_hookClicks.js

Выделить код

Код:

128(){ //Clean Cache
		    var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
			Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
			if (cancelQuit.data) return false;
			Services.appinfo.invalidateCachesOnRestart();
			var restart = Services.startup;
			restart.quit(restart.eAttemptQuit|restart.eRestart);
		},

[upd:] Получилось, спасибо.

b0ttle пишет

Как сюда добавить хоткей? Не хочется лишний раз дублировать код. Там что-то такое:if (e.code == "KeyQ" && e.ctrlKey && e.altKey), не знаю как

Если подразумевается ucf_hookClicks.js в шапке, то в нём подробные комментарии, добавляйте перехват клавиш по аналогии с двумя существующими:

Выделить код

Код:

var keydown_win = e => { // нажатие клавиш
	if (e.keyCode == 83 && e.altKey) { console.log("Alt+S" + Math.random());
	}
	if (e.keyCode == 88 && e.altKey){ console.log("Alt+X");
	}
}

Содержание файла config.js (для отключения проверки цифровых подписей дополнений) для [firefox] версии 102 и выше такое:

config.js для Firefox версии 102 и выше

Выделить код

Код:

//
try {(jsval => {
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;

	var AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true;
		var env, def = g.ChromeUtils.defineModuleGetter;
		g.ChromeUtils.defineModuleGetter = (...args) => {
			try {
				genv();
				dbg.addDebuggee(globalThis);
				var e = dbg.getNewestFrame().older.environment;
				var obj = e.parent.type == "object" && e.parent.object;
				if (obj && obj.class.startsWith("N")) // JSM, NSVO
					obj.unsafeDereference().Object = {
						freeze: ac => (ac.MOZ_REQUIRE_SIGNING = false) || freeze(ac)
					};
				else env = e; // ESM, Lexy "var"(?)
			}
			catch(ex) {Cu.reportError(ex);}
			(g.ChromeUtils.defineModuleGetter = def)(...args);
		}
		ChromeUtils.import(uac + "jsm");
		// (?)
		env && env.setVariable(AC, gref.makeDebuggeeValue(freeze(o.assign(
			new o(), env.getVariable(AC).unsafeDereference(), {MOZ_REQUIRE_SIGNING: false}
		))));
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;

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

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

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

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

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


А для работы комплекта UCF такое:
config.js для UCF

Выделить код

Код:

//
(async () => {
    var sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Cu.evalInSandbox(`
        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
        var user_chrome_files_sandbox = {
            init() {
                Services.obs.addObserver(this, "domwindowopened");
                Services.obs.addObserver(this, "profile-after-change");
            },
            observe(aWindow, aTopic, aData) {
                Services.obs.removeObserver(this, "profile-after-change");
                this.observe = (window, topic, data) => {
                    if (!(window instanceof Ci.nsIDOMChromeWindow)) return;
                    var docElementInserted = e => {
                        var win = e.target.defaultView;
                        if (win instanceof Ci.nsIDOMChromeWindow)
                            user_chrome.initWindow(win);
                    };
                    window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
                    window.addEventListener("load", e => {
                        window.addEventListener("unload", e => {
                            window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
                        }, { once: true });
                    }, { once: true });
                };
                var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
                file.append("user_chrome_files");
                file.append("user_chrome.manifest");
                if (!file.exists() || !file.isFile()) {
                    this.removeObs();
                    return;
                }
                try {
                    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
                    .autoRegister(file);

                    Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
                } catch(ex) {
                    this.removeObs();
                    return;
                }
                if (aTopic === "domwindowopened")
                    this.observe(aWindow, aTopic, aData);
            },
            removeObs() {
                Services.obs.removeObserver(this, "domwindowopened");
            },
        };
        user_chrome_files_sandbox.init();
    `, sandbox);
})();

// lockPref("xpinstall.signatures.required", false);
// lockPref("extensions.experiments.enabled", true);
// lockPref("extensions.langpacks.signatures.required", false);


Можно ли совместить содержимое и использовать одновременно?
Или... как правильно? Спасибо за помощь :beer:

Viatcheslav пишет

Можно ли совместить содержимое и использовать одновременно?

а разве так можно???!!!

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

Выделить код

Код:

//
try {(jsval => {
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;

	var AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true;
		var env, def = g.ChromeUtils.defineModuleGetter;
		g.ChromeUtils.defineModuleGetter = (...args) => {
			try {
				genv();
				dbg.addDebuggee(globalThis);
				var e = dbg.getNewestFrame().older.environment;
				var obj = e.parent.type == "object" && e.parent.object;
				if (obj && obj.class.startsWith("N")) // JSM, NSVO
					obj.unsafeDereference().Object = {
						freeze: ac => (ac.MOZ_REQUIRE_SIGNING = false) || freeze(ac)
					};
				else env = e; // ESM, Lexy "var"(?)
			}
			catch(ex) {Cu.reportError(ex);}
			(g.ChromeUtils.defineModuleGetter = def)(...args);
		}
		ChromeUtils.import(uac + "jsm");
		// (?)
		env && env.setVariable(AC, gref.makeDebuggeeValue(freeze(o.assign(
			new o(), env.getVariable(AC).unsafeDereference(), {MOZ_REQUIRE_SIGNING: false}
		))));
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;

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

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

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

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

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


//
(async () => {
    var sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Cu.evalInSandbox(`
        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
        var user_chrome_files_sandbox = {
            init() {
                Services.obs.addObserver(this, "domwindowopened");
                Services.obs.addObserver(this, "profile-after-change");
            },
            observe(aWindow, aTopic, aData) {
                Services.obs.removeObserver(this, "profile-after-change");
                this.observe = (window, topic, data) => {
                    if (!(window instanceof Ci.nsIDOMChromeWindow)) return;
                    var docElementInserted = e => {
                        var win = e.target.defaultView;
                        if (win instanceof Ci.nsIDOMChromeWindow)
                            user_chrome.initWindow(win);
                    };
                    window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
                    window.addEventListener("load", e => {
                        window.addEventListener("unload", e => {
                            window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
                        }, { once: true });
                    }, { once: true });
                };
                var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
                file.append("user_chrome_files");
                file.append("user_chrome.manifest");
                if (!file.exists() || !file.isFile()) {
                    this.removeObs();
                    return;
                }
                try {
                    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
                    .autoRegister(file);

                    Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
                } catch(ex) {
                    this.removeObs();
                    return;
                }
                if (aTopic === "domwindowopened")
                    this.observe(aWindow, aTopic, aData);
            },
            removeObs() {
                Services.obs.removeObserver(this, "domwindowopened");
            },
        };
        user_chrome_files_sandbox.init();
    `, sandbox);
})();

// lockPref("xpinstall.signatures.required", false);
// lockPref("extensions.experiments.enabled", true);
// lockPref("extensions.langpacks.signatures.required", false);

Viatcheslav пишет

Содержание файла config.js (для отключения проверки цифровых подписей дополнений) для [firefox] версии 102 и выше такое:

А для чего отключать проверку цифровых подписей дополнений? Для CB? Им еще пользуются?
Порой проще скинуть скрипт ИИ и подрихтовать, чем дожидаться пока на тебя обратят внимание.
Правда не всегда что надо выдает, приходится долго и мучительно добиваться своего, но все же, это по мне легче.
[upd:] Извините, не хотел показаться грубым насчет CB. Просто я давно его забросил, не спорю что удобен. Думал, что сейчас мало кто пользуется, раньше да. Без поддержки любая тема может погибнуть, а бесплатно, это лишь хобби до пары до времени. Как вариант, не обновляться.

b0ttle пишет

А для чего отключать проверку цифровых подписей дополнений? Для CB? Им еще пользуются?

Для возможности установки неподписанных расширений, а также не из АМО.
И да - Custom Buttons пользуются, и таких немало...

b0ttle пишет

А для чего отключать проверку цифровых подписей дополнений?

Гениальный вопрос.
   

b0ttle пишет

Для CB? Им еще пользуются?

А вы в соответствующую тему зайдите и там ляпните это. :)

_zt
У Вас был скрипт "Открытие "about:addons" по ПКМ на кнопке нового меню дополнений 111+"
https://forum.mozilla-russia.org/viewtopic.php?pid=804620#p804620
Как в этом скрипте поменять кнопки мыши т.е. ЛКМ- открыть "about:addons" а ПКМ- показать меню дополнений?

doud
Cкрипт добавляет действие по ПКМ, вместо контекстного меню. А вам надо скрипт переназначения действия, это будет уже другой скрипт, лучше Dumby об этом попросить.

doud пишет

Как в этом скрипте поменять кнопки мыши т.е. ЛКМ- открыть "about:addons" а ПКМ- показать меню дополнений?

Хмм, left mousedown и right click сойдёт?

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

Выделить код

Код:

(async code => {
	await delayedStartupPromise;

	var btn = document.getElementById("unified-extensions-button");
	btn.setAttribute("onmousedown", code);
	var toggle = gUnifiedExtensions.togglePanel.bind(gUnifiedExtensions, new MouseEvent("mousedown"));
	btn.oncontextmenu = () => !toggle();

	var ttt = btn.tooltipText = [
		"Left Mousedown: about:addons",
		"Ctrl + Left Mousedown: about:debugging#/runtime/this-firefox",
		"Contextmenu: Unified Extensions Panel"
	].join("\n");

	var args = ["aftercustomization", () => btn.tooltipText = ttt];
	gNavToolbox.addEventListener(...args);
	addEventListener("unload", () => gNavToolbox.removeEventListener(...args), {once: true});

})(`if (!event.button) gBrowser.selectedTab = gBrowser.addTrustedTab(
	"about:" + (event.ctrlKey ? "debugging#/runtime/this-firefox" : "addons")
);`);

Dumby
Спасибо! То что надо:beer:

Добрый день!

Прошу помощи.
В версиях 116 и 117 перестал работать UCF

В 116-117 нет даже значка UCF.

vassemm пишет

В версиях 116 и 117 перестал работать UCF

Попробовал запустить [firefox] 116 Beta 1. Действительно не работает.


Консоль ругается на файл config.js.
TypeError: invalid 'instanceof' operand Ci.nsIDOMChromeWindow

unter_officer пишет

invalid 'instanceof' operand Ci.nsIDOMChromeWindow

Это Bug 1522052 - Remove nsIDOMChromeWindow


Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»


На Win7, я Firefox 117 уже только распаковкой смог поставить.
Праздник кончается, передаём Наследие в заботливые руки
обладателей других операционных систем.

Друзья, поделитесь, пожалуйста, рабочим в [firefox] 114
скриптом для UCF размещения фавиконки сайта в адресной строке :whiteflag:

Dumby пишет

Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»

Благодарю.
На первый беглый взгляд все заработало. WIN11  FF116 FF117
:beer:

Dumby пишет

Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»

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



06-07-2023 21:16:32

Viatcheslav пишет

скриптом для UCF размещения фавиконки сайта в адресной строке

Если ничего не путаю, то вот: https://forum.mozilla-russia.org/viewto … 52#p793152

unter_officer пишет

Если ничего не путаю, то вот: https://forum.mozilla-russia.org/viewto … 52#p793152

Спасибо большое, но... Всё время показывает дефолтную иконку.
А вот этот работает - https://github.com/Aris-t2/CustomJSforFx/blob/master/scripts/favicon_in_urlbar.uc.js

Viatcheslav
Вот это рабочий, если нет, то проблема у вас.
Иконку дефолтную свою вписать или положить по прописанному пути.

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

Выделить код

Код:

// FavIcon in URL-bar - Иконка сайта в url-баре
// https://forum.mozilla-russia.org/viewtopic.php?pid=789469#p789469
(this.faviconinurlbar = {
            init(that) {
                var identity = document.querySelector("#identity-icon");
                if (!identity)
                    return;
                var iconDefault = "chrome://user_chrome_files/content/custom_styles/png/globe-16.png"; // или свою иконку
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #identity-faviconinurlbar {
                        --v-faviconinurlbar-default: url("${iconDefault}");

                        list-style-image: var(--v-faviconinurlbar, none) !important;
                        pointer-events: none !important;
                        height: 16px !important;
                        width: auto !important;
                        margin-inline-start: 4px !important;
                        -moz-context-properties: fill, fill-opacity;
                        fill: currentColor;
                        fill-opacity: var(--urlbar-icon-fill-opacity, 1);
                    }
                    #identity-faviconinurlbar:not([faviconinurlbar="true"]),
                    #identity-faviconinurlbar[favbusy="true"] {
                        --v-faviconinurlbar: var(--v-faviconinurlbar-default) !important;
                    }
                    #identity-faviconinurlbar[faviconchrome="true"],
                    #urlbar[actiontype="extension"] #identity-faviconinurlbar,
                    #identity-box:is(.extensionPage,.chromeUI,.localResource) #identity-faviconinurlbar,
                    #urlbar:not(.searchButton) #identity-box[pageproxystate="invalid"] #identity-faviconinurlbar {
                        display: none !important;
                    }
                `);
                windowUtils.loadSheetUsingURIString(style, windowUtils.USER_SHEET);
                var faviconinurlbar = document.createXULElement("image");
                faviconinurlbar.id = "identity-faviconinurlbar";
                identity.after(faviconinurlbar);
                gBrowser.tabContainer.addEventListener("TabAttrModified", this);
                gBrowser.addProgressListener(this);
                that.unloadlisteners.push("faviconinurlbar");
                var {STATE_START, STATE_STOP, STATE_IS_NETWORK} = Ci.nsIWebProgressListener;
                var updatefavicon = image => {
                    if (image) {
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", `url("${image}")`);
                        faviconinurlbar.setAttribute("faviconinurlbar", "true");
                        faviconinurlbar.setAttribute("faviconchrome", `${image.startsWith("chrome:")}`);
                    } else {
                        faviconinurlbar.setAttribute("faviconinurlbar", "false");
                        faviconinurlbar.style.setProperty("--v-faviconinurlbar", "");
                    }
                };
                this.handleEvent = e => {
                    var tab = e.target, changed;
                    if (!tab.selected || !((changed = e.detail.changed).includes("image") || changed.includes("selected"))) return;
                    updatefavicon(tab.image);
                };
                this.onStateChange = (aWebProgress, aRequest, aStateFlags, aStatus) => {
                    if (aStateFlags & STATE_IS_NETWORK && aWebProgress?.isTopLevel) {
                        if (aStateFlags & STATE_START)
                            faviconinurlbar.setAttribute("favbusy", "true");
                        else if (aStateFlags & STATE_STOP) {
                            faviconinurlbar.setAttribute("favbusy", "false");
                            updatefavicon(gBrowser.selectedTab.image);
                        }
                    }
                };
            },
            destructor() {
                gBrowser.tabContainer.removeEventListener("TabAttrModified", this);
                gBrowser.removeProgressListener(this);
            }
        }).init(this);

_zt пишет

Вот это рабочий, если нет, то проблема у вас

Наверное... :(
А каким способом подключали?
У меня вот этот - https://github.com/Aris-t2/CustomJSforFx/blob/master/scripts/favicon_in_urlbar.uc.js
подключен к UCF в custom_script_win.js по событию "DOMContentLoaded".
Только не зря его Виталий назвал "ужасным... :usch:

Viatcheslav Они разные, может и подключать по разному надо, у меня в CustomStylesScripts.jsm

scriptschrome: { // Для докум. окна браузера [ChromeOnly]
        load: [ // По событию "load"

Это аналог custom_script_win.js после секции ...

Выделить код

Код:

load() {
        if (this.initialized)
            return;
        this.initialized = true;
        /* ************************************************ */
тут коды или импорт

Импорт раньше у меня такой был ...

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

Выделить код

Код:

// Для скриптов отдельными файлами (см. примеры)
        // С уточнением в каком "документе" работать
        // https://forum.mozilla-russia.org/viewtopic.php?pid=788301#p788301
        (async () => {
            var loadscript = (relpath, obj) => {
                try {
                    Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${relpath}`, obj, "UTF-8");
                    return true;
                } catch(e) { }
                return false;
            },
            load_scripts_by_url = {
                browser: win => {
                    //>>>>>>>>>>| Блок требуется для боковой панели и т.п. |>>>>>>>>>>
                    var box = document.querySelector("#browser") || window;
                    var listener = e => {
                        var doc = e.target || ({});
                        load_scripts_by_url[doc.documentURI]?.(doc.defaultView);
                    };
                    box.addEventListener("pageshow", listener);
                    this.loadscriptswinandsidebar = {
                        destructor() {
                            box.removeEventListener("pageshow", listener);
                        }
                    };
                    this.unloadlisteners.push("loadscriptswinandsidebar");
                    /* <<<<<<<<<<<<<<<<<<<< */
                    
                    //>>>>>>>>>>| Загрузка скриптов для browser.xhtml |>>>>>>>>>>
                        // Здесь скрипты для основного окна
                        loadscript("custom_js_win/ucf_Bookmarks_Star_Tooltip_Helper.uc.js", win);
                        loadscript("custom_js_win/ucf_Context_Menu_Open_With.us.js", this) && this.unloadlisteners.push("contextmenuopenwith");
                        loadscript("custom_js_win/ucf_FavIcon_In_URL-bar.uc.js", this);
                        loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.browser();
                        loadscript("custom_js_win/ucf_SidebarTabs.us.js", this) && this.unloadlisteners.push("sidebar_tabs");
                        // и т.д.

                    //<<<<<<<<<<<<<<<<<<<<
                },
                //>>>>>>>>>>| Загрузка скриптов для др. документов |>>>>>>>>>>
                "chrome://browser/content/places/bookmarksSidebar.xhtml": win => {
                    // Здесь скрипты для боковой панели закладок
                    loadscript("custom_js_win/s_AutoCloseBookMarkFolder_Fx37.uc.js", win);
                    loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.bookmarksSidebar();
                    // и т.д.

                },
                "chrome://browser/content/places/historySidebar.xhtml": win => {
                    // Здесь скрипты для боковой панели истории
                    loadscript("custom_js_win/s_AutoCloseHistoryFolder_Fx37.uc.js", win);
                    loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.historySidebar();
                    // и т.д.

                },
                //<<<<<<<<<<<<<<<<<<<<
            };
            load_scripts_by_url.browser(window);
        })();


Понятия не имею бкдет ли оно сейчас работать.

_zt пишет

Импорт раньше у меня такой был ...

Это для старой версии UCF
Спасибо за помощь :beer:

Dumby
Есть ваш скриптик "Восстановление удалённых закладок".

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

Выделить код

Код:

//
// Dumby: https://forum.mozilla-russia.org/viewtopic.php?pid=801497#p801497
//
(async sep => {
	if (!sep) return;

	var key = "hasRemoveTransaction";
	var g = Cu.import("resource://gre/modules/PlacesTransactions.jsm", {});

	var raws = (g.lazy || g).TransactionsHistory?.proxifiedToRaw;
	if (raws) g = raws;

	if (!g[key]) {
		if (!raws) {
			Services.scriptloader.loadSubScript(
				`data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
			);
			raws = g[key];
		}
		g[key] = entry => {
			for(var tr of entry)
				if (raws.get(tr) instanceof PlacesTransactions.Remove)
					return true;
		}
	}
	var menuitem = document.createXULElement("menuitem");
	for(var args of Object.entries({
		closemenu: "single",
		class: "menuitem-iconic",
		id: "placesCmd_undoRemove",
		label: "Восстановить удалённое",
		oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAEd0lEQVRIx53Ve2yW5RkG8N/7ft8Hxa/9CgjSSDnMA4xVTUMQWzadhzhnjSCC0y0jSjom+JdGp844ZhSjHLJsowaNZoCCHOWglXGoQbo2fBW3obDNUBfXKpsUVhxn6OHdH8+31GXGmL3/3M913ffzPNed93qeBxvmI8umCNjyPFEDSRJw40qKa9jyHSbdzY77Ar+9FTvZ+nHAb9T7P76YcwlOktlBdJia2SSthfRvaR/Mync5tp85Q2mbEHJ/X0bqRTpOBJyehog3C3j1HV9RQP91xG9T8z2SofyphMELWH4zW5s40s7kQ/T0cnYfXfPD1OgM0TlGzSN1DZkzxG1EdURjaa9HCfddEupfuSkIfHXJ5wVENA8Iw9QcRnbzXj31++g8w+WVZHMhXzaKhu2MGMtd36f5CTrn8WAJyf001KCYpA6dDBtLlOfoYuI6kgp6XuLsbhQRfw39ovDPx7/JgYS1b3NkCxcvoPgTevIULyFVTm4jyinp4uRJiirYt5BMLc15kvE0lnCsgZaE2wdx6LVCo9/ghQn4LhdFxEOonELv8ogVV9Iyjd9fx8T1FE2nq5XSQWQuJbsmrJH9JclMsldhFsW3EU0iuxsb6f9zTA3dHj1J1yPsKaPrIOtnkn6KDY2cvYF/vkfmINGuiPdnUD6Pd/7Mur/x0RCqXqTfj+m5glyO1B8ovR1pijeil6KvBw8V1QaB/QYWzNgSYu+TmMm5GVhKT47OBWR28kgnvxvKS/OjvuMGuTSr72FtBR/WctNe4l+H3GUt7DxB2TKe+CmnF5IZRzSZzFmS4wy9n2QGY2eR3Mx193DqAao+4vh04lZS2/EkQyqo7cWux4Kjwf4gKF3N5hEs+QsLlwVuVRPLP2FeTSh95hZSpSx6OOAkwWCWLSEqYWVz4JtLqd3GiuX0/oS6XKjd/BQbF6SRJynij4eJKpl7WSg8cCdzxjHjH3QuIsaorQx7NCx8QQdxCYM2kzxL423BbGN6SP+I6tmcO86hahqu4MA2To3mX+eTxbA2er6VJjcA/Yl2kOzlrutJxrDraaI1XH09j3fQVk7pcG5sCgIOttLVzejpmMPrnxJvYnIZXelwxGHTCOaX0PorsmUMOC/wpeNofg4fjg9EW3+k6CzcYJ99M7h6fSfxFFJrMZzFm0I+fzXxlTQ1EHf3eSlJyI3mlYGs/yEPx4F7/NkQ11zEymFhnDoY9RnwcHmIZ34Q1CcTkBD9FSM58gu8SmUHbmVuJbYRt2AiE/cwZT/tG3h+Gh29lH3A+VPZ+zMq1rHwcq6tY1U31ReQXPgFt3M+/9+xJRvM9f5S3NnX6btLqfqYt1p44xQrFnH0aX6zmBXIj+Hae0N95qG+ea/PJjrB7tV47csE7CzEJqKXyTcW8CquaibfwTOX0FjPLSfYk+G8XWyb+rlTUcndVUQjWXOK+FiBn/QfQen/FVBVVdhoc4FIkczqM5WBvPMWp4fQMpzFHeEN2NCFCcyNSH2b1AiSl3nhVnrnkjpNkqP9jrBvdzX8G2rIlxvMK3a7AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==",
	}))
		menuitem.setAttribute(...args);

	var desc = Object.getOwnPropertyDescriptor(XULElement.prototype, "hidden");
	var {set} = desc;
	desc.set = () => {
		var entry = PlacesTransactions.topUndoEntry;
		var vis = entry && g[key](entry);
		vis && menuitem.removeAttribute("disabled");
		set.call(menuitem, !vis);
	}
	Object.defineProperty(menuitem, "hidden", desc);
	sep.after(menuitem);
})(document.getElementById("placesContext_deleteSeparator"));

Заметил в FF115 такой момент. Если вызвать контекстное меню на свободном месте панели закладок, то пункт "Восстановить удалённое" остаётся затенённым.
Untitled-2.png
В предыдущих версиях [firefox] всё работает нормально.
Возможно поправить?

ucf_hookClicks.js некорректно работает в 115.0.1, где-то нет тултипов, отвалилась подсветка и вся система алертов при копирований адресной строки через identity-box, и еще что-то. Все остальное работает, пока не разобрался что еще отвалилось.
Кстати, как в консоли высматривать ошибки? Тот который Ctrl+Shift+J.

unter_officer пишет

В предыдущих версиях [firefox] всё работает нормально.

Да, вижу. Слегка поменяли код.
Раньше disabled ставился перед hidden,
а теперь получилось наоборот, сначала hidden.


Можно попробовать вообще просто поставить на disabled заглушку

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

Выделить код

Код:

/*
		var vis = entry && g[key](entry);
		vis && menuitem.removeAttribute("disabled");
		set.call(menuitem, !vis);
	}
*/
		set.call(menuitem, !entry || !g[key](entry));
	}
	Object.defineProperty(menuitem, "disabled", {});


Bug 1780695 - «Remove Services.jsm» (Firefox 117+)
See also: Bug 1667455 - «Expose a "Services" property on all privileged JS scopes (like Cu/Cc/Ci)» (Firefox 104+)

Dumby пишет

Можно попробовать вообще просто поставить на disabled заглушку

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

Тут тултипы работают в hookClicks, но при запуске firefox почему-то пишет, что файл не запущен, хотя по виду все работает. Странно.
Проверил. В коде по ссылке, там тултипы работают, а функций нет, при кликах ничего не происходит.
Хотел свою версию сюда(код то не мой, не умею кодить. просто чуть-чуть под себя подогнал), но там какие-то символы мешают, сайт не принимает, а в base64txt, не умею.
Смог сделать в base64txt, но теперь из-за размера не пропустил, что-то про (64 КБ).
В общем, https://pastebin.com/fn2WwQVS Судя по длине кода, наверно никто не захочет в ней возиться.

Dumby Возможно ли запускать перевод выделеного текста горячими клавишами (двойнм Ctrl) в этом скрипте?

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

Выделить код

Код:

// Google Translate в контекстном меню.......
(this.googletranslate = {
            init(that) {
                var lc = navigator.lastClick = {}, w = null, xhtmlns = 'http://www.w3.org/1999/xhtml';
                var mouseUp = (e) => {
                    if (e.button) return;
                    lc.X = e.screenX - mozInnerScreenX;
                    lc.Y = e.screenY - mozInnerScreenY;
                };
                gBrowser.tabpanels.addEventListener('mouseup', mouseUp, false);
                this.destructor = () => {
                    gBrowser.tabpanels.removeEventListener('mouseup', mouseUp, false);
                    if (w)
                        w.closeWin();
                };
                that.unloadlisteners.push("googletranslate");
                var createWindow = function(text, status, title, id, pos, size) {
                    var win = window, doc = win.document, wId = 'ujs_window'+(id || '');
                    w = doc.getElementById(wId);
                    var keyDown = function(e) {if (!e.shiftKey && !e.ctrlKey && !e.altKey && e.keyCode == 27)doc.getElementById(wId).closeWin();};
                    var mouseDown = function() {doc.getElementById(wId).closeWin();};

                    if (w)
                        w.closeWin();
                    w = doc.createElementNS(xhtmlns, 'div');
                    w.setAttribute('style', 'position:fixed;display:block;visibility:hidden;left:0;top:0;width:auto;height:auto;border:1px solid gray;padding:2px;margin:0;z-index:99999;overflow:hidden;cursor:move;'+(typeof w.style.borderRadius === 'string' ? 'background-color:#eaeaea;padding-top:0px;border-radius:4px;box-shadow:0 0 15px rgba(0,0,0,.4);' : 'background:-o-skin("Window Skin");'));
                    w.id = wId;
                    w.closeWin = function() {
                        doc.removeEventListener('keydown', keyDown, false);
                        gBrowser.tabpanels.removeEventListener('mousedown', mouseDown, false);
                        this.parentNode.removeChild(this);
                        w = null;
                    };
                    w.addEle = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'div');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
                    w.addEle1 = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'textarea');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
                    var img = doc.createElementNS(xhtmlns, 'div');
                    img.setAttribute('style', 'display:block;float:right;width:16px;height:16px;padding:0;margin-top:2px;margin-right:1px;border:none;cursor:pointer;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACQUlEQVR4Xm2SPUhbURTHfzfNJ4nU0Axx7CJCN2kQtIgVCtq6OdQuBUftWIQqFNsKDo6iHTqIqN3a0kklUKTS2AZrcOjioFtRjFWUfH+803cS9GHwB4dzcu75/+8L9xgaSEDbLXhaASzAyXAMn5/DHxwwjeJgKLRxd3Q0atxuLhGgWqmQmp9PJ7LZJ5Ow3WhQFweDG20LC1FfRwc3kUsm+T48fPIzl3s8VTdxxLt+/2FhdlZkc1MkmdR8Pba2ajlrz3z1+9OvIOaIfb7DwsSEyNKSyM6OSLksEo/rbw2ttadntV7Gnv3k86VfqskvmDzv7hZ580Zkelrk4kJqFIsii4satVrRs+rYmBQHBuRvS4u8g/fuCmDOz+HgAIWpKRgfh3AYhoZQ8Hqxjo4o9PdT3d1FcQEl8KoBnJ7C/j6KZkZGYG4OIhEUFed7erD29q69jGrd1qVBqcQVKgwEuMSEQhjtXRk4JvW/kM1i2WGMga4uzMoKBIOIfbNiolEC6+uU+/qQRALFA1QBlwVYxmBcLmhuxqytQSgEKu7t1dBavwKvbeINh/Has2IMqnXpem4bky6qWLdvZgaOj2FwEHNyoqG19vRMZ8jZs1+NOfsNcZS3EFvzeNL51laRe/dEYrFadsLpZeyZDx7P6QN4Btwx1OE1xO4HAquP2tsjAZ+Pm8gWi3xMpc6W8/kXPyAO/DM4oOsZa2pafdjZGXF7PAgOpXKZL4nE2XImcyUGMDSg63kbhgvgtYCq8+akIL4J3y7Fyn+DokZOnLlMyQAAAABJRU5ErkJggg==");background:-o-skin("Caption Close Button Skin");');
                    img.title = (win.navigator.language.indexOf('ru') == 0) ? '\u0417\u0430\u043A\u0440\u044B\u0442\u044C' : 'Close';
                    img.addEventListener('click', function() {this.parentNode.closeWin();}, false);
                    w.appendChild(img);
                    var title = w.addEle(title, 'display:table;color:#000;font:17px Times New Roman;width:auto;height:auto;padding:0;margin:0 2px;cursor:text;');
                    title.onclick = e => {
                        e.preventDefault();
                        var url = e.target.href;
                        // Здесь открываем url как хотим.
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                        doc.getElementById(wId).closeWin();
                    };
                    var cnt = w.addEle1(text, 'display:block;border:1px solid #aaa;padding-bottom:3px;padding-left:3px;background-color:#fafcfe;color:#000;font:16px Times New Roman;width:310px;height:160px;overflow:auto;cursor:text;-moz-user-focus:normal;-moz-user-select:text;');
                    cnt.contentEditable="true";
                    cnt.context="contentAreaContextMenu";
                    w.addEle(status, 'display:table;font:12px Times New Roman;font-weight:bold;color:blue;width:auto;height:auto;padding-top:2px;margin:0 3px;cursor:pointer;');
                    w.addEventListener('mousedown', function(e) {
                        if (e.target == w) {
                            e.preventDefault();
                            var grabX = e.clientX, grabY = e.clientY, origX = parseInt(w.style.left), origY = parseInt(w.style.top);
                            var mouseMove = function(ev) {
                                w.style.left = origX+ev.clientX-grabX+'px';
                                w.style.top = origY+ev.clientY-grabY+'px';
                            };
                            doc.addEventListener('mousemove', mouseMove, false);
                            doc.addEventListener('mouseup', function() {doc.removeEventListener('mousemove', mouseMove, false);}, false);
                        }
                    }, false);
                    doc.documentElement.appendChild(w);

                    if (size) {
                        cnt.style.height = size.height;
                        cnt.style.width = size.width;
                    } else {
                        for (var i = 3; i < 10; i++) {
                            if (cnt.scrollHeight > cnt.offsetHeight || cnt.scrollWidth > cnt.offsetWidth) {
                                cnt.style.height = 80*i+'px';
                                cnt.style.width = 160*i+'px';
                            } else
                                break;
                        }
                    }

                    var docEle = (doc.compatMode == 'CSS1Compat' && win.postMessage) ? doc.documentElement : doc.body;
                    var mX = docEle.clientWidth-w.offsetWidth, mY = docEle.clientHeight-w.offsetHeight;
                    if (mX < 0) {cnt.style.width = parseInt(cnt.style.width)+mX+'px'; mX = 0;}
                    if (mY < 0) {cnt.style.height = parseInt(cnt.style.height)+mY+'px'; mY =0;}
                    var hW = parseInt(w.offsetWidth/2);
                    w.style.left = (pos && pos.X < mX+hW ? (pos.X > hW ? pos.X-hW : 0) : mX)+'px';
                    w.style.top = (pos && pos.Y+10 < mY ? pos.Y+10 : mY)+'px';
                    w.style.visibility = 'visible';
                    doc.addEventListener('keydown', keyDown, false);
                    gBrowser.tabpanels.addEventListener('mousedown', mouseDown, false);
                    return w;
                };

                var getHash = function (txt) {
                    TKK=eval('((function(){var a\x3d817046147;var b\x3d-335196159;return 410049+\x27.\x27+(a+b)})())');
                    function sM(a) {
                        var b;
                        if (null !== yr)
                            b = yr;
                        else {
                            b = wr(String.fromCharCode(84));
                            var c = wr(String.fromCharCode(75));
                            b = [b(), b()];
                            b[1] = c();
                            b = (yr = window[b.join(c())] || "") || "";
                        }
                        var d = wr(String.fromCharCode(116)), c = wr(String.fromCharCode(107)), d = [d(), d()];
                        d[1] = c();
                        c = "&" + d.join("") + "=";
                        d = b.split(".");
                        b = Number(d[0]) || 0;
                        for (var e = [], f = 0, g = 0; g < a.length; g++) {
                            var l = a.charCodeAt(g);
                            128 > l ? e[f++] = l : (2048 > l ? e[f++] = l >> 6 | 192 : (55296 == (l & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (l = 65536 + ((l & 1023) << 10) + (a.charCodeAt(++g) & 1023),
                            e[f++] = l >> 18 | 240,
                            e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224,
                            e[f++] = l >> 6 & 63 | 128),
                            e[f++] = l & 63 | 128);
                        }
                        a = b;
                        for (f = 0; f < e.length; f++)
                            a += e[f],
                        a = xr(a, "+-a^+6");
                        a = xr(a, "+-3^+b+-f");
                        a ^= Number(d[1]) || 0;
                        0 > a && (a = (a & 2147483647) + 2147483648);
                        a %= 1E6;
                        return c + (a.toString() + "." + (a ^ b));
                    }

                    var yr = null;
                    var wr = function(a) {
                        return function() {
                            return a;
                        };
                    }, xr = function(a, b) {
                        for (var c = 0; c < b.length - 2; c += 3) {
                            var d = b.charAt(c + 2), d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d), d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
                            a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d;
                        }
                        return a;
                    };
                    return sM(txt);
                };

                var ujs_google_translate = function (dir) {
                    var lng = window.navigator.language.slice(0, 2), txt = gContextMenu.selectionInfo.fullText, l = dir.split('|');
                    var encTxt = encodeURIComponent(txt);
                    var winWait = function(lng) {createWindow('', (lng == 'ru' ? 'Подождите идет перевод' : 'Wait, is going Translating')+'\u2026', 'Google Translate', '_gt', window.navigator.lastClick);};
                    if (txt) {
                        winWait(lng);
                        var xhr = new XMLHttpRequest();
                        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + l[0] + '&tl=' + l[1] + '&hl=' + lng + '&eotf=0&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t' + getHash(txt);
                        var urlt = "http://translate.google.com/translate_t?text="+encTxt+"&sl='  + langFrom_google_text + '&tl=' + langTo_google_text +'&hl=' + lng + '&eotf=0&ujs=gtt";
                        xhr.open('POST', url, true);
                        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
                        xhr.onreadystatechange = function() {
                            try {
                                if (xhr.readyState == 4 && xhr.status == 200) {
                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
                                    for (var i = 0, n; n = tmp[0][i]; i++) {
                                        if (n[0])result += n[0].toString();
                                    };
                                   status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                                   createWindow(result, status, '<a href="'+urlt.replace(/&/g,'&amp;')+'" target="_blank" style="display:inline;padding:0;margin:0;text-decoration:none;border:none;color:#009;font:16px Times New Roman;">Google Translate</a>', '_gt', window.navigator.lastClick);
                                }
                            } catch(e) {};
                        };
                        xhr.send('q=' + encodeURIComponent(txt));
                    } else {
                        var urlt = gBrowser.currentURI.spec;
                        var url = "http://translate.google.com/translate?u="+encodeURIComponent(urlt)+'&tl='+l[1]+"&hl="+lng+"&langpair="+dir+"&tbb=1";
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                    };
                };
                var contextMenu = document.getElementById("contentAreaContextMenu");
                var nextEleMenu = document.getElementById("context-inspect");

                var menuItem = document.createXULElement("menuitem");
                menuItem.setAttribute("id", "context-ru-google-translate");
                menuItem.setAttribute("label", "Перевести на русский");
                menuItem.setAttribute("class", "menuitem-iconic");
                menuItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABBUlEQVR4Ac2RMU4DMRREB2MqihQpAkUaqnAduuQw4Sx03CGcJYIiDYqQ0lCsZ77535ZFcgAkRuPvV7zd/dLiz3N1PFb8hmRckrfELZrF9ONQ1B6Yz0MyM7S0O6zGkVprh/3+Kw/JzkHSpRpJKZkpr9fYbOx0cjVsCbWG31oHBM9mtt0q73ZcrdLhAMlYQKkUI22aqsNo8HKZAMQhvVaKSHWj2Q2aPU3mJQFY7nuHyvYFduP83WF3AJRfcPNYnr/Lp1G1uK4m9sno1LaUbnX/htf8BNzoneUD5NhjvLCMhURQSQ93QCZwXYjFwg3I0NZKrvoknQPMIHkt/jRAQKMeG2yX89/mB4EJbKbZxIhFAAAAAElFTkSuQmCC");
                menuItem.addEventListener("command", function() {ujs_google_translate('auto|ru');}, false);
                contextMenu.insertBefore(menuItem, nextEleMenu);
				
                contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);
            }
        }).init(this);

b0ttle пишет

Тут тултипы работают в hookClicks, но при запуске firefox почему-то пишет, что файл не запущен, хотя по виду все работает.

на FF114 всё работало. На 115 перехват кликов не работает, базовый hookClicks выдаёт много ошибок в консоли.


Dumby - проверьте на Firefox 115 ваш базовый hookClicks, на его основе я делал все доработки для профиля в шапке.

Dumby - просьба доработать скрипт, чтобы его запускать по перехвату кликов из любых кнопок:

AppMenuTbbSaveHTMLChild.jsm

Выделить код

Код:

/* для UCF CustomStylesScripts.jsm
	scriptsbackground: [ // В фоне [System Principal]
		{ func: jsmImport("AppMenuTbbSaveHTMLChild.jsm"), }, */

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

class AppMenuTbbSaveHTMLChild extends JSWindowActorChild {
	receiveMessage() {
		return htmlAndName(this.contentWindow);
	}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {moduleURI: __URI__},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
	},
	observe(win) {
		win.document.getElementById("appMenu-popup")
			.addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.setAttribute("label", "Страница | выбранное в единый HTML");
		var before = "appMenu-save-file-button2", subviewbutton = "subviewbutton";
		if ( parseInt(popup.ownerGlobal.Services.appinfo.version) < 89 ) {
			subviewbutton = "subviewbutton subviewbutton-iconic", before = "appMenu-print-button";
			btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAACNJREFUCNdjYH/AgBX9/89Q/4/B/g+D/A8G/g8gEeYDDIwNAIB7EDCcKCcMAAAAAElFTkSuQmCC");
		}
		btn.className = subviewbutton;
		btn.setAttribute("oncommand", "saveHTML();");
		btn.saveHTML = this.saveHTML;
		popup.querySelector('toolbarbutton[id^="'+ before +'"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup").removeEventListener("popupshowing", this);
	},
	async saveHTML() {
		var win = this.ownerGlobal;
		var br = win.gBrowser.selectedBrowser;
		var bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;

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

var htmlAndName = async mainWin => {

	var resolveURL = function (url, base) {
		try {
			return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	};
	var getSelWin = function (w) {
		if (w.getSelection().toString()) return w;
		for (var i = 0, f, r; f = w.frames[i]; i++) {
			try {
				if (r = getSelWin(f)) return r;
			} catch(e) {}
		}
	};
	var encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else {
				img = obj;
			};
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	};
	var toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	};

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild);
	var meta = doc.createElement('meta');
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	var title = doc.getElementsByTagName('title')[0];
	if (title) head.appendChild(title.cloneNode(true));

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

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

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

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

Этот скрипт спрашивает, куда сохранять страницу в единый HTML. Второй подобный код ты делал для скрипта ucf_hookClicks.js – сохранять страницу без запроса (я его доработал для сохранения в папку с имёнем сайта и прочими условиями).


Просьба такая - сделать единый скрипт, который работает в зависимости от переданных опций, то есть либо сохраняет в единый HTML с запросом "Куда?", либо сохраняет сразу без запроса в "Загрузки".
То есть, чтобы скрипт можно было вызывать из разных кнопок (например, по перехвату кликов из ucf_hookClicks) с разными опциями – например "Спросить", куда сохранять или "Не спрашивать".

Не знаю в какую тему написать... В FX115 отвалилась кнопка восстановления вкладок после закрытия (не показывает список закрытых вкладок), вроде она относится к расширению add_toolbar_buttons@vitaliy.ru. Можно как-то ее починить?

https://dropmefiles.com/XaeKh - расширение


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

Northtech
https://forum.mozilla-russia.org/viewtopic.php?pid=804061#p804061


Add, надеюсь оно, я просто эту кнопку не использую.

kokoss
нет, к сожалению не то. Эти изменения я вносил уже...

egorsemenov06 пишет

Возможно ли запускать перевод горячими клавишами (двойнм Ctrl) в этом скрипте?

Это как-то неудобно. Скрипт завязан на контекстное меню,
и берёт выделенный текст из его машинерии. А если клавишей, то взять негде.


Разве что через буфер обмена. Ну попробуй дописать после строки
contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);

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

Выделить код

Код:

//
                var translate = async () => {
                    var br = gBrowser.selectedBrowser;
                    var fw = Services.focus.focusedWindow;
                    if (fw == window) {
                        if (document.activeElement != br) return;
                    }
                    else if (fw.browsingContext.top != br.browsingContext) return;

                    var cb = navigator.clipboard;
                    var was = await cb.readText();
                    if (was) await cb.writeText("");

                    docShell.doCommand("cmd_copy");
                    await new Promise(r => setTimeout(r, 100));
                    var txt = await cb.readText();

                    if (txt || was) cb.writeText(was);
                    if (!txt && !br.currentURI.scheme.startsWith("http")) return;
                    window.gContextMenu = {selectionInfo: {
                        get fullText() {
                            window.gContextMenu = null;
                            return txt;
                        }
                    }};
                    ujs_google_translate("auto|ru");
                }
                var ts = 0, destr = this.destructor, args = ["keyup", e =>
                    e.key == "Control" && ts - (ts = Cu.now()) > -300
                    && !e.shiftKey && !e.altKey && translate(ts = 0)
                ];
                addEventListener(...args);
                this.destructor = () => destr(removeEventListener(...args));

Dobrov пишет

ваш базовый hookClicks

Нет у меня никакого hookClicks, не сочиняй.
Мы там что-то когда-то обсуждали, и ты его родил.

проверьте на Firefox 115

Ну я убрал кусок кода if (typeof IOUtils != "object") {…};
и, на первый взгляд, вроде нормализовалось.
Так, потыкал немного, там же невозможно всё проверить,
нужно что-то поконкретнее.


В этом куске всё тот же косяк с непониманием работы var.
Вчера вот Services.jsm удалили, так у меня всё рухнуло в основном из-за этого.
Вобщем, тоже удали, или перепиши кусок правильно.


Да, кстати, потерялся var перед str_cut = …
Без него, в окно добавятся ненужные там
str_cut, url_color, switchToTab, showInStatusPanel, Title,
saveSelectionToTxt, save, bright, help, GetSelection, data,


и gClipboard конечно, который может перезаписать
тот, что от Custom Buttons, а это уже совсем нехорошо.

чтобы скрипт можно было вызывать

Скрипт нельзя «вызвать», только загрузить. Вызвать можно функцию.


Это надо что-то в SystemGlobal добавить, чтобы с других мест вызывать.
Попробую расписать, чисто для примера. Следи за мыслью

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

Сначала в init() дописываем
globalThis[Symbol.for(name)] = this.saveHTML;


Затем, из saveHTML() убираем var win = this.ownerGlobal;
а в саму функцию добавляем наш аргумент, и вторым аргументом окно
async saveHTML(arg, win = this.ownerGlobal) {


и, в последней строке функции, наш аргумент пробрасываем
actor && self.save(win, ...await actor.sendQuery(""), arg);


Далее вписываем его и в save()
async save(win, fileContent, fileName, arg) {


Таким образом, внутри save() теперь можно его использовать
if (arg) {
    win.alert(arg); // test
    // Здесь делаем что-то одно, типа вычисляем путь для файла
} else {
    // Здесь делаем что-то другое, типа открываем файл-пикер и забираем путь с него
}
// Здесь записываем файл


Всё. Теперь можно вызывать снаружи, например, с консоли браузера:

(() => {
    var save = Cu.getGlobalForObject(Cu)[Symbol.for("AppMenuTbbSaveHTML")];
    save("TesT", window);
})();

Northtech пишет

dropmefiles.com

javascript:void(0); — нет уж, разрешите отказаться.


Но наверняка дело в getClosedTabCount
Можно заменить в parent.js все два на getClosedTabCountForWindow


Если нужна обратная совместимость, то два
sessionStore.getClosedTabCount
заменить на
(sessionStore.getClosedTabCountForWindow || sessionStore.getClosedTabCount)

Dumby пишет
egorsemenov06 пишет

Возможно ли запускать перевод горячими клавишами (двойнм Ctrl) в этом скрипте?

Это как-то неудобно. Скрипт завязан на контекстное меню,
и берёт выделенный текст из его машинерии. А если клавишей, то взять негде.


Разве что через буфер обмена. Ну попробуй дописать после строки
contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);

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

Выделить код

Код:

//
                var translate = async () => {
                    var br = gBrowser.selectedBrowser;
                    var fw = Services.focus.focusedWindow;
                    if (fw == window) {
                        if (document.activeElement != br) return;
                    }
                    else if (fw.browsingContext.top != br.browsingContext) return;

                    var cb = navigator.clipboard;
                    var was = await cb.readText();
                    if (was) await cb.writeText("");

                    docShell.doCommand("cmd_copy");
                    await new Promise(r => setTimeout(r, 100));
                    var txt = await cb.readText();

                    if (txt || was) cb.writeText(was);
                    if (!txt && !br.currentURI.scheme.startsWith("http")) return;
                    window.gContextMenu = {selectionInfo: {
                        get fullText() {
                            window.gContextMenu = null;
                            return txt;
                        }
                    }};
                    ujs_google_translate("auto|ru");
                }
                var ts = 0, destr = this.destructor, args = ["keyup", e =>
                    e.key == "Control" && ts - (ts = Cu.now()) > -300
                    && !e.shiftKey && !e.altKey && translate(ts = 0)
                ];
                addEventListener(...args);
                this.destructor = () => destr(removeEventListener(...args));

Большое Спасибо!!!!то что доктор прописал!

Dumby

javascript:void(0); — нет уж, разрешите отказаться.

Да я этого и не видел никогда, у меня ublock. :)

getClosedTabCountForWindow

сработало, спасибо.

В Firefox 117 не открывается старый about:config. Достаточно ли закомментировать строку с импортом Services.jsm?

После какого-то из недавних обновлений перестал работать скрипт (contextsearch.uc.js), вот он: https://forum.mozilla-russia.org/viewto … 83#p780283 , переключающий поисковые системы в контекстном меню выделенного текста.
В данный момент стоит [firefox] 115.02 (Windows 7).
В скриптах я, к сожалению, совсем не разбираюсь. Подскажите пожалуйста, как теперь его снова заставить работать?

Dumby пишет

Попробую расписать, чисто для примера. Следи за мыслью…

Спасибо за подсказки! Доработал скрипт AppMenuTbbSaveHTMLChild, проверь, может я накосячил где-то…
1) теперь запускается так: Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](true, window);
2) можно сохранять страницу автоматически или с запросом "Куда"
3) может сохранять в разные папки в зависимости от опции ucf.savedirs: Загрузки[домен]имя вкладки
или указать любой путь сохранения, например: Загрузки/Web/домен/имя …for("SingleHTML")]("Web|1", window)

SingleHTMLChild.jsm

Выделить код

Код:

/* SingleHtml by Лекс, правка: Dumby, mod Dobrov
для UCF CustomStylesScripts.jsm
	scriptsbackground: [ // В фоне [System Principal]
		{ func: jsmImport("SingleHTMLChild.jsm"), },
вызов: Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](arg, window)
	если arg False, то диалог выбора пути сохранения */

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

class SingleHTMLChild extends JSWindowActorChild { //класс = name + Child
	receiveMessage() { return htmlAndName(this.contentWindow);}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {moduleURI: __URI__},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
		globalThis[Symbol.for(name)] = this.saveHTML;
	},
	observe(win) {
		win.document.getElementById("appMenu-popup")
			.addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.setAttribute("label", "Страница | выбранное в единый HTML");
		var before = "appMenu-save-file-button2", subviewbutton = "subviewbutton";
		btn.className = subviewbutton;
		btn.setAttribute("oncommand", "saveHTML();");
		btn.saveHTML = this.saveHTML;
		popup.querySelector('toolbarbutton[id^="'+ before +'"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup").removeEventListener("popupshowing", this);
	},
	async saveHTML(arg, win = this.ownerGlobal) {
		var br = win.gBrowser.selectedBrowser;
		var bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;
		var actor = bc?.currentWindowGlobal?.getActor(name);
		actor && self.save(win, ...await actor.sendQuery(""), arg); // htmlAndName
	},
	async save(win, data, fname, host, arg, d = prefs.getStringPref("ucf.savedirs","_Web||_Pic|0")) {
		if (/.*\|/.test(arg)) d = arg; //Dir/Subdir|[пусто|0 title|1 host]
		if (!/.*\|/.test(d)) d += '|'; d = d.split('|').slice(0,2); //Загрузки[домен]имя
		fname = fname.replace(/\s+/g,' ').replace(/[\\\/?*\"'`]+/g,'').replace(/[|<>]+/g,'_').replace(/:/g,'։').slice(0,100).trim();
		d[1] = (d[1] == "0") ? fname.slice(0,48).trim() : (d[1] == "1") ? host.replace(/\/.*/,'') : "";
		try {var dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {var dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
		d.forEach(dir.append); dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777);
		fname += (function() {var d = new Date(), z = function(n){return '։' + (n < 10 ? '0' : '') + n}; return '_'+ d.getHours() + z(d.getMinutes()) + z(d.getSeconds());})();
		dir.append(fname +'.html'); var path = dir.path; //назначить путь сохранения

		if (!arg) { // диалог выбора папки
			var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
			fp.init(win, "", fp.modeSave);
			fp.defaultString = path.split(/.*[\/|\\]/)[1];
			fp.appendFilters(fp.filterHTML); fp.appendFilters(fp.filterAll);
			var res = await new Promise(fp.open);
			if (res == fp.returnOK || res == fp.returnReplace)
				path = fp.file.path
			else return;
		}
		this.write(path, data);
		d = await win.Downloads.createDownload({source: "about:blank",target: win.FileUtils.File(path)}); (await win.Downloads.getList(win.Downloads.ALL)).add(d); await d.refresh(d.succeeded = true); //flash DWButton
	},
	write(path, html) {
		if (typeof IOUtils == "object")
			var write = IOUtils.writeUTF8 || IOUtils.writeAtomicUTF8; // Fx 85+ || 82-84
		if (!write) { // Fx 79-81
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			write = (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt));
		}
		(this.write = write)(path, html);
	}
}).init("browser-delayed-startup-finished");

var htmlAndName = async mainWin => {

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try {
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location,
	ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g;

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets;
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	var title = doc.getElementsByTagName('title')[0];
	if (title) head.appendChild(title.cloneNode(true));

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var onlyName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	return [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', onlyName, loc.hostname];
}

Dumby - ещё нужна помощь!
хочу добавить в SystemGlobal вторую функцию, в ней нужны адрес и имя текущей вкладки.
gURLBar и gBrowser не работают, а win.location.hostname и doc.getElementsByTagName('title')[0] работают только в var htmlAndName = async mainWin => {
не знаю, как получить адрес и имя текущей вкладки без receiveMessage()


как в JSM-ке SingleHTMLChild получить адрес и имя текущей вкладки ?

odd74RUS пишет

стоит [firefox] 115.02

Нет такой версии.
Но есть 115.0.2 и на ней скрипт работает.


А вот в 116, да, отвалится:
Move handling of search engine one-off hidden from preferences into the search settings.


Можно будет так поправить

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

Выделить код

Код:

/*
        var pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
        var hiddenList = pref ? pref.split(",") : [];
        var engines = await Services.search.getVisibleEngines();
        for (let engine of engines.filter(e => !hiddenList.includes(e.name))) {
*/
        var engines = await Services.search.getVisibleEngines();
        for(let engine of engines.filter(e => !e.hideOneOffButton)) {


Или, если нужна какая-то обратная совместимость, например, так
скрытый текст

Выделить код

Код:

/*
    async rebuild(menu) {
        var de = Services.search.defaultEngine;
        de = de.wrappedJSObject || de;
        this.setAttrs(menu, de, `Искать в ${de.name} или в ...`);
        menu.ePopup.textContent = "";
        var pref = Services.prefs.getStringPref("browser.search.hiddenOneOffs");
        var hiddenList = pref ? pref.split(",") : [];
        var engines = await Services.search.getVisibleEngines();
        for (let engine of engines.filter(e => !hiddenList.includes(e.name))) {
*/
    getEngines() {
        var args = "hideOneOffButton" in Services.search.defaultEngine
            ? [e => !e.hideOneOffButton]
            : Object.defineProperty(
                [function(e) {return !this.includes(e.name);}], "1", {
                    get: () => Services.prefs.getStringPref(this.hide)?.split(",") || []
                }
            );
        return (this.getEngines = async () =>
            (await Services.search.getVisibleEngines()).filter(...args)
        )();
    },
    async rebuild(menu) {
        var de = Services.search.defaultEngine;
        de = de.wrappedJSObject || de;
        this.setAttrs(menu, de, `Искать в ${de.name} или в ...`);
        menu.ePopup.textContent = "";
        for(let engine of await this.getEngines()) {


И да, я понимаю, что разговоры про 116+ для Win7 не слишком актуальны,
так что это просто так, на всякий случай.


Dobrov пишет

gURLBar и gBrowser не работают

Что-то я не понимаю твоего затруднения.
gURLBar и gBrowser — это свойства окна браузера, а не свойства SystemGlobal.


Есть ссылка на окно — есть и его свойства,
через оператор доступа к свойству объекта «.» (точка),
то есть: окно.gURLBar и окно.gBrowser


У тебя же самого, например, написано win.Downloads и win.FileUtils
Эти, конечно, можно получить и импортом модулей, но зачем, когда они уже есть в окне.


И gBrowser в модуле используется, смотри первую строку в saveHTML()

Dumby пишет

Что-то я не понимаю твоего затруднения.

Я тоже! :) Накосячил - разные функции с одним именем прописал в globalThis[Symbol.for(…
В итоге доработал 3 скрипта, в них сокращён код, используются 2 общие функции:
ucf_hookClicks.js ClickPicSave.jsm SingleHTMLChild.jsm


:( Ещё не получилось добавить setTimeout(… в скрипты scriptsbackground: [ // В фоне [System Principal]

Dobrov
Круто, ждал) Все работает.

Dobrov пишет

не получилось добавить setTimeout(… в скрипты scriptsbackground

Не понял.
Скрипты scriptsbackground исполняются в специально созданном Sandbox'е
и setTimeout() туда уже добавлен самим UCF посредством импорта модуля
resource://gre/modules/Timer.jsm (или не .jsm, а .sys.mjs, зависит от версии FF).


Даже попробовал проверить: в scriptsbackground — { path: "test_timeout.js" },
в test_timeout.js — setTimeout(Services.prompt.alert, 7777, null, "title", "message");
Запускаю браузер, и таймаут, определённо, есть.

Dumby пишет

Но есть 115.0.2 и на ней скрипт работает.

Работает, но почему то на mail.ru, bing.com, может ещё где не срабатывает, в чём может быть причина ?

Dumby пишет

Скрипты scriptsbackground исполняются в специально созданном Sandbox'е и setTimeout() туда уже добавлен самим UCF

в JSM это не работает. Есть другой способ JSM подключать в CustomStylesScripts.jsm? Как добавить setTimeout в JSM?

Выделить код

Код:

var jsmImport = name => `ChromeUtils.import("chrome://user_chrome_files/content/custom_scripts/${name}")`;
……………
scriptsbackground: [ // В фоне [System Principal]
	{ path: "test_timeout.jsm" }, // только здесь setTimeout работает
	{ func: jsmImport("test_timeout.jsm"), }, // не пашет ✕✖✘

Dumby ещё вопрос: А возможно сделать доступными из начала скрипта несколько функций, прописанных в блоке (async (id) => {}) ?
Чисто для лучшей читабельности хочу в начало скрипта код нажатий кнопок keydown_win = e => {, который в конце…
key_data = e => log(e.keyCode); // не работает log(…), так как он расположен в (async (id… перед keydown_win…
Однако функции, прописанные после keydown_win…, доступны, если их прописать в начало скрипта.

Блок-схема скрипта hookClicks.js (ChromeOnly)

Выделить код

Код:

var hmap = new Map([["downloads-button", `колёсико ⬇︎ папка [Загрузки]`]]);
// +++ код >>>

data = { // клавиши и события мыши в начале скрипта для лучшего восприятия
	"#downloads-button": { 1() {save()},
// +++ код >>>
}};

key_data = e => { // перехват клавиш в начале скрипта для лучшего восприятия
	log(e.keyCode); // ✕✖✘ недоступны функции из блока (async (id, func) => {
};
// +++ код >>>

(async (id, func) => { // +++ код основные функции >>>

var dsym = Symbol(), tooltips = {
	[dsym]: GetDynamicShortcutTooltipText("downloads-button") + "\n" + hmap.get("downloads-button"),
// +++ код >>> get "downloads-button"() {
}

var log = (msg) => {
	Services.console.logStringMessage(msg);
},
keydown_win = e => { // перехват клавиш
	key_data(e); // не работает ✕✖✘
// +++ код >>>
}
window.addEventListener("keydown", keydown_win);
// +++ код >>>

})("hookClicks-and-tooltips");

Упростил скрипт перехвата кнопок/ролика мыши и нажатий клавиш ucf_hookClicks.js
Блок обработки клавиш теперь в начале скрипта – аналогично подсказкам и действиям мыши.
Настраивать стало удобнее, код выглядит примерно так:
    var keys = { // перехват клавиш Meta 8 Ctrl 4 Shift 2 Alt 1
        KeyS_1(e) { save()}, // S+Alt
        KeyX_5(e) { userjs(e)}, // X+Alt+Ctrl
    }

Если включена отладка, в консоли видно нажатия мыши и сочетания клавиш.
Флаг отладки есть в диалоге настройки UserChromeFiles моего профиля

Dobrov
Вопрос, могу ли я перенести все отсюда, сюда в var keys = {}. По идее можно, просто думаю, а если в полях ввода будет мешаться код. С дефолтными клавищами, можно также e.preventDefault(); впереди? Просто не совсем представляю, как должно все выглядеть. А для чего userj(e), а где-то просто ()? За что отвечает (e). Да знаю, совсем ноль в этом. Просто потребитель) Спасибо за полезности, и Dumby за помощь.

b0ttle - userj(e) - внешний скрипт пользователя

b0ttle пишет

Вопрос, могу ли я перенести все отсюда, сюда в var keys = {}.

нет, переноси код ручками - видно же, что синтаксис разный:
у тебя: "KeyG":{"true_false_false" ……
у меня: KeyS_1(e) { save()}, // S+Alt

Dobrov
Вопрос больше про "this.skip". Он не будет выполнять код в полях ввода, адресной строке? Или, можно отдельно в начало кода добавить, что-то наподобие e.preventDefault(); Чтобы, лишнего кода не добавлять. Еще вопрос, про (е)/() в чем разница, это клик event? Переносить также с (е)?.
Перенес так, но некоторые срабатывают в полях ввода. Пришлось пока закомментить.

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

Выделить код

Код:

keyboard = { // нажатия клавиш Meta 8 Ctrl4 Shift2 Alt1
	KeyS_1(e) { save()}, // S+Alt
	KeyS_3(e) { // S+Alt+Shift
		var single = document.getElementById(hmap.get("#").split('|')[10]);
		single ? single.click() : save(); // имитировать клик кнопки
	},
	KeyS_5(e) { saveSelectionToTxt();}, // S+Alt+Ctrl
	KeyX_1(e) { userjs(e)}, // X+Alt внешний скрипт
	KeyQ_5(e) { // Q+Alt+Ctrl CleanCache - Перезапустить, удалив кэш
			var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
			Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
			if (cancelQuit.data) return false;
			Services.appinfo.invalidateCachesOnRestart();
			var restart = Services.startup;
			restart.quit(restart.eAttemptQuit | restart.eRestart);},
	//============
  get skip(){return docShell.isCommandEnabled("cmd_insertText");},
 Backquote(){this.skip||PlacesCommandHook.showPlacesOrganizer("BookmarksMenu").click();},
	Digit1(){this.skip||document.getElementById("unified-extensions-button").click();},
	Digit2(){this.skip||BrowserOpenAddonsMgr();},
	Digit3(){this.skip||openPreferences();},
	Digit4(){this.skip||duplicateTabIn(gBrowser.selectedTab,'tab');},
	Digit5(){if(this.skip)return;var s=prompt('Google_ site:.. ..','');if(s.length>0)gBrowser.addTrustedTab('https://www.google.com/search?q=site:'+encodeURIComponent(gBrowser.currentURI.host)+' '+encodeURIComponent(s));},
	Digit6(){if(this.skip)return;var p=Services.dirsvc.get('GreD',Ci.nsIFile);p.initWithPath(p.path+"\\..\\..\\..\\_Photo\\ShareX\\ShareX.exe");p.launch();},
	Digit7(){if(this.skip)return;gBrowser.fixupAndLoadURIString("javascript:(function(){var EnRuT=[['щ','shh'],['Щ','Shh'],['Щ','SHH'],['х','hh'],['Х','Hh'],['Х','HH'],['ж','zh'],['Ж','Zh'],['Ж','ZH'],['ц','cz'],['Ц','Cz'],['Ц','CZ'],['ю','yu'],['Ю','Yu'],['Ю','YU'],['ё','yo'],['Ё','Yo'],['Ё','YO'],['я','ya'],['Я','Ya'],['Я','YA'],['ч','ch'],['Ч','Ch'],['Ч','CH'],['ш','sh'],['Ш','Sh'],['Ш','SH'],['э','e`'],['Э','E`'],['ы','y'],['Ы','Y'],['ъ','``'],['ь','`'],['р','r'],['т','t'],['у','u'],['и','i'],['о','o'],['п','p'],['а','a'],['с','s'],['д','d'],['ф','f'],['г','g'],['й','j'],['к','k'],['л','l'],['з','z'],['х','x'],['ц','c'],['в','v'],['б','b'],['н','n'],['м','m'],['Р','R'],['Т','T'],['У','U'],['И','I'],['О','O'],['П','P'],['А','A'],['С','S'],['Д','D'],['Ф','F'],['Г','G'],['Й','J'],['К','K'],['Л','L'],['З','Z'],['Х','X'],['Ц','C'],['В','V'],['Б','B'],['Н','N'],['М','M'],['е','e'],['Е','E']],A=document.activeElement;A.onkeyup=function ftr(){for(var s=A.value,i=0;i<EnRuT.length;i++){s=s.replace(RegExp(EnRuT[i][1],'g'),EnRuT[i][0])};A.value=s}})()",{triggeringPrincipal:gBrowser.contentPrincipal});
			 },//AutoTranslit- https://forum.ru-board.com/topic.cgi?forum=5&topic=46779&start=320#15
		F1(){FileUtils.getFile('SysD',['sndvol.exe']).launch();},
		F2(e){e.preventDefault();gBrowser.fixupAndLoadURIString("javascript:{d=document;b=d.body;o=d.createElement('scri'+'pt');o.setAttribute('src','https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit');o.setAttribute('type','text/javascript');b.appendChild(o);v=b.insertBefore(d.createElement('div'),b.firstChild);v.id='google_translate_element';v.style.display='none';p=d.createElement('scri'+'pt');p.text='function googleTranslateElementInit(){new google.translate.TranslateElement({pageLanguage:%22%22},%22google_translate_element%22);}';p.setAttribute('type','text/javascript');b.appendChild(p);}void 0",{triggeringPrincipal:gBrowser.contentPrincipal});
			 },//Enable translation(https://forum.mozilla-russia.org/viewtopic.php?pid=805422#p805422 ||805427#p805427)
		F3(e){e.preventDefault();gBrowser.fixupAndLoadURIString("javascript:(function(){var night=function(w){(function(d){var css='html{opacity:0.6!important;background:black!important;}body{background:white!important;}';var s=d.getElementsByTagName('style');for(var i=0,si;si=s[i];i++){if(si.innerHTML==css){si.parentNode.removeChild(si);return}};var heads=d.getElementsByTagName('head');if(heads.length){var node=d.createElement('style');node.type='text/css';node.appendChild(d.createTextNode(css));heads[0].appendChild(node)}})(w.document);for(var i=0,f;f=w.frames[i];i++){try{arguments.callee(f)}catch(e){}}};night(window)})();",{triggeringPrincipal:gBrowser.contentPrincipal});
			 },//Night mode(Focus mode)
		F4(){var vert=`javascript:{const o=["contextmenu","selectstart","select","mousedown","mouseup","cut","copy"],t=["-webkit","-moz","-ms","-khtml",""],e=window.jQuery;[document,document.body,document.documentElement].forEach(n=>{for(const t of o)n["on"+t]=null,e&&e(n).off(t);if(n.style)for(const o of t){const t=[o,"user-select"].join("-");n.style[t]="initial"}})
			 };void(0);`;gBrowser.loadURI(Services.io.newURI(vert),{triggeringPrincipal:Services.scriptSecurityManager.getSystemPrincipal()});
			 },//Re-enable selecting & copying text [maple3142] (Bookmarklet)
	KeyG_4(e){e.preventDefault();var bar=document.getElementById("ucf-additional-vertical-bar");setToolbarVisibility(bar,bar.collapsed);},
	KeyW_1(){gBrowser.removeAllTabsBut(gBrowser.selectedTab);},
	  KeyV(){if(this.skip)return;gClipboard.write(gURLBar.value);glob.flash_bg_text('urlbar-input-container',0,'rgba(240,176,0,0.5)',300);},//300,"CopyURL: "+gURLBar.value.slice(0,80)) 
	KeyV_1(){let url=readFromClipboard();try{switchToTabHavingURI(url,true);}catch(ex){
			 var reg=/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/;
			 if(!reg.test(url)){url='https://www.google.com/search?q='+encodeURIComponent(url);
			 }else{if(url.substring(4,0).toLowerCase()=="http"){url=encodeURIComponent(url);
			 }else{url='http://'+encodeURIComponent(url);}}switchToTabHavingURI(url,true);}},//Open clipboard address
	KeyU_5(){FileUtils.getFile('UChrm',['user_chrome_files','_','u.exe']).launch();},
	KeyA_5(){FileUtils.getFile('UChrm',['user_chrome_files','_','.Ev','Everything.exe']).launch();},
	KeyQ_4(){FileUtils.getFile('UChrm',['user_chrome_files','_','.QT','QTranslate.exe']).launch();},
	//============
}

kokoss пишет

Работает, но почему то на mail.ru, bing.com, может ещё где не срабатывает, в чём может быть причина ?

Без понятия.
Даже если бы было расписано что значит «не срабатывает»,
и с цитатами из консоли, и подробнейшим STR, то это было бы
бесполезно,
поскольку воспроизвести это вот «на mail.ru, bing.com»,
для меня, не представляется возможным.


Но интересно, если закомментировать
searchSelect.style.setProperty("display", "none", "important");
чтобы встроенный пункт не скрывался,
то будет ли этот встроенный пункт срабатывать, там же.


Dobrov пишет

Как добавить setTimeout в JSM?

Ну так тоже импортируй модуль Timer.
var {setTimeout} = …

key_data = e => { // перехват клавиш в начале скрипта для лучшего восприятия
    log(e.keyCode); // ✕✖✘ недоступны функции из блока (async (id, func) => {

Конечно недоступны, log определяется как var log = … внутри функции.
Снаружи функции этот log не видно, и не должно быть видно.


Если нужно использовать log в key_data, но чтобы он задавался в (async (id, func) => {
тогда можно написать просто var log; (без ничего) рядом с key_data
а внутри (async (id, func) => { тогда написать log = … без var


Ну, это в общем случае. Блок-схема не предполагает, что сам (весь) код
как то завёрнут, то есть, что key_data, что var key_data, что window.key_data,
всё это одно и то же — мусор в окне. Тогда можно var log; и не писать.
Но лучше всё-таки код заворачивать.


b0ttle пишет

Вопрос больше про "this.skip". Он не будет выполнять код в полях ввода, адресной строке?

skip() тоже можно перенести, тогда не будет.

Еще вопрос, про (е)/() в чем разница, это клик event? Переносить также с (е)?

Какой ещё «клик event»? keydown event же.
А вписывать (e) необходимо, только если он используется внутри,
иначе можно оставить просто ()


Вобщем как-то так

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

Выделить код

Код:

....... = { // перехват клавиш Meta 8 Ctrl 4 Shift 2 Alt 1

	get skip() {return docShell.isCommandEnabled("cmd_insertText");},

	Backquote() {this.skip || PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");},
	Digit1() {this.skip || document.getElementById("unified-extensions-button").click();},

Dumby пишет

Но интересно, если закомментировать
searchSelect.style.setProperty("display", "none", "important");
чтобы встроенный пункт не скрывался,
то будет ли этот встроенный пункт срабатывать, там же.

Не срабатывает, ладно, всё равно я эти поисковики редко использую.

Dumby
Блогадарю, поправил. Только интересно, что клавиши совсем не работают в полях ввода. Это не связанно с this.skip, до этого еще заметил, но забыл написать. Функций работают, а сам ввод `,1,2,3,4,5.. нет.
Насчет get skip(){return docShell.isCommandEnabled("cmd_insertText");}, мне казалось что нужно переносить, но хотел куда-то в keydown_win=e=>{...}, боялся что напортачу с синтаксисом или еще с чем-то, и будет ли он оттуда работать. А так, он будто не на своем месте. Вопрос, может в keydown_win=e=>{...} уже реализован функционал с this.skip? Просто не разбираюсь, что там написано.

b0ttle пишет

клавиши совсем не работают в полях ввода.

в поле быстрого ответа этого форума перехват нажатий клавиш работает.
Про this.skip не понял. keydown_win не нужно править, а skip так работает: ret = keys.skip; // keys = { get skip(){…


Dumby пишет

Блок-схема не предполагает, что сам (весь) код как то завёрнут, ……Но лучше всё-таки код заворачивать

А как правильно завернуть код на блок-схеме или скрипте ucf_hookClicks.js, чтобы он не мусорил в окно?
Если всё завернуть в (async (id) => { …… })("hookClicks-and-tooltips"); пропадут общие функции для других скриптах, что неправильно…
Пробовал делать в одном объекте несколько общих функций для других скриптов, но не работает…

dobrov
Не совсем понял, про ret = keys.skip; // keys = { get skip(){…
Пробовал так, по разному, но ничего не выходит.
Digit3(){keys.skip;openPreferences();},
Digit3(){get skip();openPreferences();},
Digit3(){get skip(){openPreferences();}},
Что значит ret=, первый раз вижу такое значение.

В общем, проблема в том, что `1234567, в полях ввода не пишутся, при рабочих Digit1,2,3,5,6,7. Функций работают, но текст нет. А при this.skip||, текст не работает и функций, в полях ввода, ничего не происходит. Отдельно функций работают. Как-то так.
Остальные хоткей, особо-то и не нужны ведь в полях ввода. Такие как F1,F2, там естественно проблем таких нет.

b0ttle пишет

Не совсем понял, про ret = keys.skip; // keys = { get skip(){…

Это пример, что функция keys.skip возвращает значение в переменную ret. (то есть, код работает)
А зачем перехват ввода цифр? Делай нормальные сочетания с комбинацией управляющих клавиш, например Ctrl+Atl+S, а кнопки без управляющих клавиш нафига перехватывать?
А всякие this.skip|| это из другого кода, я его не знаю…

Dobrov пишет

Если всё завернуть в (async (id) => { …… })("hookClicks-and-tooltips"); пропадут общие функции для других скриптах

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

Пробовал делать в одном объекте несколько общих функций для других скриптов, но не работает

Конечно не работает.
globalThis в оконном скрипте ссылается на окно.


А Cu.getGlobalForObject(Cu) возвращает SystemGlobal
(так было не всегда, где-то с самого конца шестидесятых),
то есть глобальный объект всех модулей, JSM и ESM,
он один на весь процесс. Когда-то давно, такой был у каждой JSM'ки.
Кстати, название так и не утвердили.


Иначе говоря, ты добавил GlobShare в одно место,
а запрашиваешь его для использования — в другом месте.


Вобщем, заворачиваешь код,
а внутри пишешь window.GlobShare = {bla: 777};
Тогда в другом скрипте console.log(GlobShare.bla) должен показать 777.


Ну это если такое присваивание будет исполнено сперва,
а не после вызова console.log(), так что следует продумать порядок загрузки скриптов,
и, возможность влияния всякого асинхрона, тут надо смотреть конкретно по ситуации.

А зачем перехват ввода цифр? Делай нормальные сочетания с комбинацией управляющих клавиш, например Ctrl+Atl+S

Сравнил тоже. Одну клавишу нажимать или три.
Не у всех такие ловкие пальцы как у тебя.


b0ttle пишет

проблема в том, что `1234567, в полях ввода не пишутся, при рабочих Digit1,2,3,5,6,7.

Такого быть не должно.
Надо искать причину. Может ты раскомментировал e.preventDefault();
в keydown_win = (e) => {…} и забыл, проверь.

Dumby пишет

пишешь window.GlobShare = {bla: 777};

Я ждал совета покруче! Но старый известный способ объявлять данные через window. наверное самый простой!
Ещё посоветуй, каким способом сэкономить ресурсы браузера для нескольких window.Функций:
1) прописать нужные функции отдельно как window.ShareFunctions…
2) или вписать нужные функции в объект и объявить только его?

Dobrov пишет

известный способ объявлять данные через window. наверное самый простой

Ищешь каких-то сложностей?
Я всего лишь к тому, что в окно лучше добавлять только нужное, а не всё подряд.

Ещё посоветуй, каким способом сэкономить ресурсы браузера для нескольких window.Функций:
1) прописать нужные функции отдельно как window.ShareFunctions…
2) или вписать нужные функции в объект и объявить только его?

Ну как, смотри сам,
если добавлять отдельно, то возрастает вероятность конфликта
с именами браузерных и пользовательских свойств,
и нахлобучка для стороннего наблюдателя, типа «откуда здесь это взялось»,
зато в скриптах можно сразу писать ShareFunction1(); ShareFunction2();


А если собрать в один объект, то вероятность конфликта меньше,
и как-бы стройнее и логичнее, но обращаться тогда придётся ShareObject.ShareFunction()
или дополнительно писать var {ShareFunction1, ShareFunction2} = ShareObject;

Dumby
Ура заработало, до этого я лишь правил изменения которые выходили. С последними изменениями, там настолько много чего поменялось в файле, что решил просто скопировать и потом уже менять. Так что, скорее всего с моей стороны были ошибки. Извините, что так долго мозг парил) Решилось.
Похоже проблема и правда была в //e.preventDefault(); в новом файле он закомментиван, я и подумать не мог, что он мог вызвать проблему. Думал, как удобно, не нужно везде его добавлять отдельно, он из коробки уже) Оказывается, мог вызывать столько проблем. С одельными комбинациями, и правда бывает удобно, что он из коробки. Но не в плане одних кнопок, как было у меня.
Dobrov
Насчет, "нафига". Удобно же, чем занимать какие-то комбинаций, которые еще запомни. Тут легче, тем более, они кроме как в полях ввода не используются.

Dumby пишет

прикрутить горячие клавиши для клавиатуры, например Shift+1

вопрос по старому коду перехвата клавиш: Для чего и что делает строка var num = RegExp.$1 ?
и почему используется animate? (async anim => { код })({animate: true});

Код для теста: перехват нажатий клавиш

Выделить код

Код:

(async anim => { // горячие клавиши https://forum.mozilla-russia.org/viewtopic.php?pid=796907#p796907
	// var re = /^(?:Digit|Numpad)(1|2|3|4)$/;
	var funcs = {
		1: () => {
			true;
		},
		2: tab => gBrowser.removeAllTabsBut(tab),
	};

	var args = ["keydown", e => {
		if (e.repeat || docShell.isCommandEnabled("cmd_insertText")) return;
		var m = (e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey), n = m ? "_"+ m.toString() : "";

		var num = RegExp.$1; // всегда 21
		// if ( e.shiftKey || e.code.startsWith("N") && e.getModifierState("NumLock") && e.key != num )
			// e.preventDefault(), funcs[num](gBrowser.selectedTab);
			e.preventDefault();
				if (e.keyCode > 31)
					console.log('@ '+ e.key +' '+ e.code +' '+ e.keyCode +' '+ num +' '+ Math.random());
				if (num in funcs)
					funcs[num](gBrowser.selectedTab);
	}, true];

	addEventListener(...args);
	var id = Symbol(), ucf = ucf_custom_script_win;
	ucf.unloadlisteners.push(id);
	ucf[id] = {destructor: () => removeEventListener(...args)};
})({animate: true});

del

Dobrov пишет

что делает строка var num = RegExp.$1 ?

В приведённом коде — не делает ничего осмысленного,
поскольку регулярные выражения в коде не используются.


А в оригинале — в num пишется цифра клавиши.
Хотя да, нехорошо, фича deprecated. Короче, здесь почитай.

и почему используется animate? (async anim => { код })({animate: true});

Ну вот, например, пункт контекстного меню вкладки «Закрыть вкладки слева»
имеет атрибут "oncommand"
gBrowser.removeTabsToTheStartFrom(TabContextMenu.contextTab, {animate: true});
так что это просто цитата.

Переделал перехват нажатий клавиш в скрипте ucf_hookClicks.js, возможности:
Простой синтаксис, различаются нажатия в полях ввода, в зависимости от имени (свойства объекта) можно игнорировать preventDefault().
Если в имени ключа первая буква строчная, нажатия передаются браузеру, preventDefault() не выполняется. Из двух равных ключей keyA и KeyA выбирается ключ с прописной буквы KeyA. Если нужно перехватить нажатия в полях ввода, добавьте символ «I»: KeyA_I

Выделить код

Код:

var klaBa = { // Пример блока обработки, нажатия клавиш Meta 8 Ctrl 4 Shift 2 Alt 1 Input 'I' Skip
	KeyA(e) {
		console.log('KeyA');
		return 'KeyA'; // обычное нажатие клавиши
	},
	keyA(e) { this.KeyA(e); // выполнить код обычного нажатия, не запускать preventDefault()
		return 'keyA Input'}, // нажатие в поле ввода
	KeyA_I() {0}, // в поле ввода (+ управляющие клавиши: KeyA_5I)
	KeyA_5() { console.log('нажатие A + Ctrl + Alt')}
}

Как сделать, чтобы F3 работал везде. С учетом preventDefault. То как сделал, он работает везде, за исключием одного момента, вне поля ввода, preventDefault не срабатывает. Может неправильно вписал?

Выделить код

Код:

F3_I(){openPreferences();},

Просто как пример. Когда нужно, чтобы хоткей работал везде. Тут хоткей не работает в поле ввода, но вызывается стандартный хоткей "поиск". Вне поля ввода, ctrl+g начинает работать нормально.

Выделить код

Код:

KeyG_4(){var bar=document.getElementById("ucf-additional-vertical-bar");setToolbarVisibility(bar,bar.collapsed);},

Тут добавил I, работает везде, но тогда вне поля ввода, он не перехватывает preventDefault.
В старом варианте кода, было легче, где надо добавить e.preventDefault, и конфликт не возникал. Может я что-то не так делаю?

Выделить код

Код:

KeyG_4I(){var bar=document.getElementById("ucf-additional-vertical-bar");setToolbarVisibility(bar,bar.collapsed);},
b0ttle пишет

Тут добавил I, работает везде, но тогда вне поля ввода, он не перехватывает preventDefault.
В старом варианте кода, было легче, где надо добавить e.preventDefault, и конфликт не возникал.

Вообще-то, я подробно всё расписал, может у тебя старая версия ? ucf_hookClicks.js часто обновляю.
Брал за основу код Dumby, только упростил: вместо this.skip || команда достаточно добавить «I»
Можно убрать проверку на поля ввода, но поведение при нажатиях на разных страницах будет неадекватным.

Пример блока обработки нажатий

Выделить код

Код:

F3(){ console.log('F3 exec');
	openPreferences();
},
F3_I(){this.F3()}, // F3 в поле ввода
KeyG_4(){ // Ctrl+G
	var bar = document.getElementById("ucf-additional-vertical-bar"); setToolbarVisibility(bar,bar.collapsed);
},
KeyG_4I(){this.KeyG_4()}, // Ctrl+G в поле ввода

Dobrov
То что надо. Ничего теперь не конфликтует, но как-то не практично. Раньше все комбинаций работали где угодно, за исключением this.skip||. Старый варинт как-то практичнее в этом плане. Вот `1234567V, сейчас с ними все отлично, но из-за них теперь страдают и другие комбинаций. Как-то так) Все же, старый варинт кастомизаций, где ты сам выбирал, что и как будет работать, просто подставив впереди нужный коммент. Более удобен.
Просто как пример, Alt+Ctrl+S, что-то написал в поле ввода, захотел, выделил и сохранил. Теперь такой возможности нет. Ну есть, но как-то не очень удобно.

Сейчас, когда KeyG_4I, он конфликтует из-за preventDefault, в полях ввода он перехватывает стандартные комбинаций пойска. Но вне поля ввода, он этого не делает. В этом и проблема. Это в случае со стандартными хоткеями. Есть тот же F3, где тоже стандартная комбинация пойск, на F2, там расширение у меня, его тоже надо перехватывать с preventDefault. Не знаю, как еще объяснить. В общем, из-за того что все из коробки, не очень удобно и что-то может конфкликтовать, и конфликтует. Как было до этого с preventDefault, в полях ввода, когда `1234567 отказывались вообще работать.
Даже так не работает, конфликтует из-за I. И e.preventDefault(); не срабатывает.

Выделить код

Код:

keyG_4I(e){e.preventDefault();var bar=document.getElementById("ucf-additional-vertical-bar");setToolbarVisibility(bar,bar.collapsed);},

Так то что надо. Нигде ничего не конфликтует.

Выделить код

Код:

KeyG_4(){var bar=document.getElementById("ucf-additional-vertical-bar");setToolbarVisibility(bar,bar.collapsed);},
KeyG_4I(){this.KeyG_4()},

Dumby - проверь плиз, код перехвата клавиш. b0ttle - Да, мой код глючит! :| (UPDATE пост обновил)
Проблема такая: если в поле ввода (ответа этого форума) нажать 1, вместе с запуском кода в поле ввода попадает вводимая клавиша.
Даже с preventDefault в поле ввода попадает символ, хотя на страницу ввод не передаётся, как это и задумано. (stopImmediatePropagation не помог)
Мне надо запретить передачу в поле ввода клавиши, если в блоке обработки прописано сочетание клавиш с нажатием этой клавиши в поле ввода - то есть Digit1_I.


В примере нажатия на странице и в поле ввода различаются (для поля ввода к имени нужно добавить "I").

Для Digit1_I (цифра 1 в поле ввода) выполняется preventDefault, но почему-то в поле ввода попадает символ.

Выделить код

Код:

(async anim => { // Для докум. окна браузера [ChromeOnly]
	var keyboard = { // нажатия клавиш Meta 8 Ctrl 4 Shift 2 Alt 1 Input 'I' Skip первая буква строчная
		Digit1() {
			console.log("■ клавиша 1 "+ Math.random());
		}, // Alt+X
		Digit1_I() { this.Digit1();
		},
		KeyX_1(e) {
			console.log("■ Alt+X "+ Math.random());
		},
		KeyX_1I(e) { this.KeyX_1(e)},  // Alt+X в поле ввода
	}
	var args = ["keydown", e => {
		if (e.repeat) return; //e.getModifierState("CapsLock")*16
		var k, m = (e.metaKey*8 +e.ctrlKey*4 +e.shiftKey*2 +e.altKey +"I".slice(!docShell.isCommandEnabled("cmd_insertText"))).replace(/^0/,'');
		m = e.code + ("_"+ m).slice(0, 5*Boolean(m)); k = m[0].toLowerCase() + m.slice(1);
		if (k in keyboard) m = k, k = 0; // не держать ввод
		if (e.keyCode > 31)
			if (m in keyboard) {
				keyboard[m](gBrowser.selectedTab);
				!k && e.preventDefault();
			}
		console.log("■ "+ m +' '+ Math.random());
	}, true];

	addEventListener(...args);
	var id = Symbol(), ucf = ucf_custom_script_win;
	ucf.unloadlisteners.push(id);
	ucf[id] = {destructor: () => removeEventListener(...args)};

})({animate: true});

Dobrov пишет

Для Digit1_I (цифра 1 в поле ввода) выполняется preventDefault

Это каким же образом?


Перед if-проверками в переменной k
находится строка с LowerCase'нутой первой буквой, то есть digit1_I


Первую if-проверку k не проходит, поскольку digit1_I в keyboard нет,
таким образом k не меняется, остаётся всё той же строкой digit1_I


Так и остаётся далее, где подходим к строке !k && e.preventDefault();
Отрицание непустой строки (!k) даёт false


Выражение вида false && something
сразу возвращает false
something, при этом, даже не рассматривается и не вычисляется.


Иными словами, e.preventDefault(), вопреки утверждению, не выполняется.

скрытый текст
Кстати, я тут возился с ucf_hookClicks.js типа как бы
избежать этого slice-replace сумбура в keydown_win()
Ну, просто так, на интерес. Вот вариант.


Пропускаем объект klaBa через некий парсер (без валидации, конечно)

Выделить код

Код:

((obj, re) => {
	for(var p in klaBa) {
		var func = klaBa[p];
		if (typeof func == "string") func = klaBa[func];

		var [key, mod] = p.split("_");
		mod = mod || "";

		var first = key[0];
		var upper = first.toUpperCase();
		var prevent = first == upper;

		var [, m, i] = mod.match(re);
		m = m || 0; // modifiers bitmap

		var arr = [func, prevent, i ? i == "I" ? 1 : 0 : -1]; // textfields flag

		var prop = prevent ? key : upper + key.slice(1);
		var o = obj[prop] || (obj[prop] = Object.create(null));

		o[m] ? o[m].push(arr) : o[m] = [arr];
	}
	klaBa = obj;
})(Object.create(null), /(\d+)?(i)?/i);

Тогда keydown_win() становится стройнее

Выделить код

Код:

keydown_win = e => { // перехват клавиш, учитывая поля ввода

	if (e.repeat) return;

	var data = klaBa[e.code]?.[e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey];
	if (data) {
		var cmd = docShell.isCommandEnabled("cmd_insertText");

		for(var [func, p, i] of data) if (i ^ cmd)
			p && e.preventDefault(), func(e, gBrowser.selectedTab);
	}
},

И примечания к синтаксису свойств klaBa:


Если начинается с маленькой буквы — не вызывать e.preventDefault().
Если заканчивается на «I» — не запускать в полях ввода.
Если заканчивается на «i» — запускать только в полях ввода.


При наличии модификаторов и/или «iI»-флага — отделять от кода клавиши символом «_».


Проброс одинакового — вместо функции вписываем соответствующую строку
digit5(e, t) {console.log("Digit 5!"); console.log(t.label);},
numpad5: "digit5",


—————


Но так получается, что в klaBa нельзя вписывать что-то постороннее,
только клавишные сочетания. Незнаю насколько это плохо.


Ну и вообще, под парсер как концепцию,
можно запилить какой-нибудь менее птичий синтаксис, наверно.

Dumby - спасибо за помощь, изучу твой код и исправлю свои ошибки! :beer:
Всё круто, но зачем отдельный Объект-парсер, может в одном keydown_win будет компактней?
Варианты: 1) запускать везде 2) не вызывать e.preventDefault 3) не запускать в полях ввода 4) только в полях ввода.


Не смог запустить сочетание клавиш Alt+X. По одной клавише пашет, а так нет:     KeyX_1(e) {    console.log("Alt+X");

нельзя вписывать что-то постороннее, только клавишные сочетания.

Может, ещё надумаешь вариант покруче? Как я понял, теперь нельзя использовать что-то вроде
    klaBa = {
        gClipboard: {
            write(str, ch = Cc["@mozilla.org/widget/clipboardhelper;1"]……

Первую if-проверку k не проходит, поскольку digit1_I в keyboard нет, таким образом k не меняется…

Ты проверял старый код, я вчера его улучшил, всё должно работать правильно

Выделить код

Код:

keydown_win = e => { if (e.repeat) return;
	var k, m = (e.metaKey*8 +e.ctrlKey*4 +e.shiftKey*2 +e.altKey +"I".slice(!docShell.isCommandEnabled("cmd_insertText"))).replace(/^0/,'');
	m = e.code + ("_"+ m).slice(0, 5*Boolean(m)); k = m[0].toLowerCase() + m.slice(1);
	// m = Digit1 k = digit1
	if (m in klaBa) // если прописаны одинаковые клавиши (Digit1 и digit1), приоритет имеют имена с Заглавной
		k = m, m = 0; // m = 0 – выполнять preventDefault
	if (k in klaBa) { // если Digit1 не найдено, запускаем digit1 и не выполняем preventDefault
		!m && e.preventDefault(); // пропуск, если имя с прописной буквы. Отрицание непустой строки !m = !Digit1 = false
		klaBa[k](e); // execute
	}
},

Dobrov пишет

зачем отдельный Объект-парсер, может в одном keydown_win будет компактней?

Парсер это не объект, а отдельный фрагмент кода.
Определяется функция и сразу вызывается.
В функцию завёрнуто просто чтобы внутри всякий свой var писать
не опасаясь, что он чего-нибудь испортит, ucf_hookClicks всё таки большой.


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


А сделать keydown_win() lazy-функцией не получится, она сразу регистрируется
как обработчик события, и после этого менять её бесполезно.


Разве что только переделать её в объект с методом handleEvent()
При первом вызове исполняется подготовительный код,
затем переопределяется handleEvent(), ну вызывается сразу. Да, так можно.

Может, ещё надумаешь вариант покруче? Как я понял, теперь нельзя использовать что-то вроде
klaBa = {
    gClipboard: {
        write(str, ch = Cc["@mozilla.org/widget/clipboardhelper;1"]……

Наверно можно, но не знаю нужно ли.
Допустим, выбрать из klaBa только клавишные свойства, и собрать в отдельную карту.
Попробовал — код получился жутковатый.

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

Выделить код

Код:

var keyMap = Object.create(null);
(re => {
    var parse = (str, test) => {
        if (name.length < 2 || str.endsWith("_")) return null;

        var first = str[0];
        var upper = first.toUpperCase();
        var prevent = first == upper;
        upper = prevent ? str : upper + str.slice(1);

        if (test) return re.test(upper);

        var match = upper.match(re);
        if (match) {
            var [, kode, mod, i] = match;
            return [kode, str, prevent, i ? i == "I" ? 1 : 0 : -1, mod || 0];
        }
    }
    for(var name in klaBa) {
        var arr = parse(name);
        if (!arr) continue;

        var desc = Object.getOwnPropertyDescriptor(klaBa, name);
        if ("value" in desc) {
            var val = klaBa[name];
            if (typeof val == "string") {

                if (klaBa.hasOwnProperty(val) && parse(val, true)) arr[1] = val;
                else continue; // skip not alias strings
            }
            else if (typeof val != "function") continue; // skip not functions
        }
        else if (!desc.get) continue; // skip only setters

        var kode = arr.shift(), mod = arr.pop();

        var o = keyMap[kode] || (keyMap[kode] = Object.create(null));
        o[mod] ? o[mod].push(arr) : o[mod] = [arr];
    }

})(new RegExp(`^(${[
    // https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values

    "Key[A-Z]|F(?:1?\\d|2[0-4])|Digit\\d",
    "Numpad(?:\\d|Enter|Add|Comma|Subtract|Decimal|Divide|Multiply|Equal)",
    "(?:Bracket|Arrow|Control|Shift|Alt|OS)(?:Left|Right)|(?:Arrow|Page)(?:Up|Down)",
    "Space|Quote|Equal|Comma|Minus|Period|Slash|Semicolon|Backslash|CapsLock|ContextMenu",
    "Backquote|Help|Backspace|Tab|Enter|Escape|End|Home|Insert|Delete|PrintScreen|NumLock",
    "Again|Convert|Copy|Cut|Eject|Find|IntlBackslash|IntlRo|IntlYen|KanaMode|NonConvert",
    "Open|Paste|Pause|Power|Props|ScrollLock|Select|Sleep|Undo|WakeUp|Lang[1-2]",
    "Media(?:PlayPause|Select|Stop|TrackNext|TrackPrevious)|Volume(?:Up|Down|Mute)",
    "Browser(?:Back|Favorites|Forward|Home|Refresh|Search|Stop)|Launch(?:App1|App2|Mail)"

].join("|")})(?:_(\\d\\d?)?([iI])?)?$`));
Выделить код

Код:

keydown_win = e => { // перехват клавиш, учитывая поля ввода

    if (e.repeat) return;

    var data = keyMap[e.code]?.[e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey];
    if (data) {
        var cmd = docShell.isCommandEnabled("cmd_insertText");

        for(var [name, p, i] of data) if (i ^ cmd)
            p && e.preventDefault(), klaBa[name](e, gBrowser.selectedTab);
    }
},

Ты проверял старый код

Какой был предоставлен, такой и проверял.

Add Toolbar Buttons уже починяли? Если нет, Dumby, посмотри пожалуйста, из кнопки Восстановить закрытые вкладки пропали.

voqabuhe
Northtech мне дал решение в личке.

В самом расширении есть файл parent.js
все getClosedTabCount заменить на getClosedTabCountForWindow

xrun1
Спасибо, работает. А с другими кнопками всё нормально, не заметил чего?

voqabuhe
Из тех, которыми пользуюсь - работают.

Dumby - придумал вариант списка нажатий клавиш проще прежнего. Код доделаю нескоро, может у тебя получится?


Формат: получаем имя нажатой кнопки вместе с управляющими клавишами: Digit1_1 равно Alt+1 (учитывая LED-индикаторы, если в объекте вместо функции Digit1_1 есть строка "Digit1_1").
Но управляющие клавиши можно отделить от имени, если это упростит код (как у тебя сделано для перехвата нажатий мыши).

Пример объекта - Если вместо функции строка, то учитывем LED-индикаторы
1) Если в объекте есть функция Digit1_1 (без учёта LED), то все режимы разрешены, то есть выполняем её всегда.
2) Если нет функции Digit1_1 и строки-объекта "Digit1_1", то добавляем состояние LED-индикаторов (Digit1_17, если CapsLock включен) и ищем такую функцию или строку-объект.
3) Если вместо функции имеется строка-объект, то "Digit1_1" или "Digit1_17" содержит одну или несколько функций, имена которых равны сумме флагов.
Разрешающий флаг как в предыдущих кодах, включает: preventDefault*4 + Input*2 + MainPage
Преимущество в том, что одно и тоже сочетание клавиш (Alt+1) может работать по разному в зависимости от LED-индикаторов.
Можно добавить фильтр по операционной системе, тогда сначала ищем это же имя + ваша OS: "Digit1_1_macosx", потом обычное имя Digit1_1.

Выделить код

Код:

var keyData = { // имя объекта: Клавиша_Shift+Alt+Ctrl[LED]
	Digit1_1(e) { // 1+Alt если Имя = Функция, считаем, что все режимы разрешены
			console.log("Digit1 везде");
	},
	"Digit1_1": { // 1+Alt
		7(e){ // preventDefault*4 + Input*2 + MainPage
			console.log("Digit1 везде, игнорируя LED-индикаторы");
		},
		2(){}, // только в полях ввода, Skip preventDefault
		5(){}, // кроме полей ввода
	},
	"Digit1_1_macosx": { // массив для переопределения отдельных сочетаний клавиш для вашей OS
		16(e){ // только если CapsLock включен
			keyData["Digit1_1"][0]("Digit1_1_macosx"); // повторяем код из общего объекта
		}
	},
}

Dobrov пишет

Пример объекта - Если вместо функции строка, …

Что-то не вижу никакой строки вместо функции.


И, при такой записи, "Digit1_1" затрёт Digit1_1
то есть, у объекта keyData будет только два (не три) свойства,
Digit1_1 как объект (не фунция, функция затёрта объектом) и Digit1_1_macosx

Dumby пишет

Что-то не вижу никакой строки вместо функции.
И, при такой записи, "Digit1_1" затрёт Digit1_1 то есть, у объекта keyData будет только два (не три) свойства,

В объекте все варианты для примера, а так должен быть только один из них. Далее разбор в зависисмости от режима e.getModifierState("CapsLock") и прочих LED…


То есть, эти две записи нельзя различить по типам, учитывая, что в объекте должна быть только одна из них?
KeyData = {   Digit1_1(e) {},   "Digit1_1": {}   }

Dobrov пишет

То есть, эти две записи нельзя различить по типам, учитывая, что в объекте должна быть только одна из них?
KeyData = {   Digit1_1(e) {},   "Digit1_1": {}   }

Не понял.
В одном объекте, даже не «должна», а «может»
быть только одна такая запись, то есть — различать просто не с чем.


А если имеется в виду, что различить такую же запись
но в другом объекте, то, разумеется, можно, это очевидно.

Dumby - я ранее расписал примерный алгоритм обработки нажатий. Различать по типу - если ключ это функция, то её запускаем, не проверяя режимы ввода. В примере это первый Digit1_1.


А в строковом ключе (имя свойства объекта) будут имена функций тех режимов, на которых надо запускать код для этого сочетания клавиш.
    "Digit1_1": { // 1+Alt
        7(e){}, // preventDefault*4 + Input*2 + MainPage
        2(){}, // только в полях ввода, Skip preventDefault
        16(e){} // только если CapsLock включен e.getModifierState("CapsLock")*16

Dumby
Тут недавно говорили
https://forum.mozilla-russia.org/viewto … 64#p804264
и после последнего обновления ESR оно престало работать.

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

Выделить код

Код:

(async style => {
	var uri = Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(style));
	var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
	sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
})(`
...
`);


То ли лыжи не едут...

_zt
Прости великодушно, но я слишком глуп,
чтобы соотнести то, о чём «недавно говорили»,
с кодом регистрации стиля из-под спойлера.


Но да, на 115, вроде вполне достаточно будет просто только

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

Выделить код

Код:

(async ids => {
	for(var id of ids)
		document.getElementById(id)?.setAttribute("removable", true);
})(["unified-extensions-button", "alltabs-button"]);

Dumby, подскажите, в чем разница между этими двумя записями?


Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('UChrm', Ci.nsIFile);
Services.dirsvc.get("UChrm", Ci.nsIFile);

6e73epo пишет

в чем разница между этими двумя записями?

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


Но, если интересно копнуть вглубь веков относительно сахара,
то Cc и Ci определены с Firefox 60, а Services определён с Firefox 104.

Dumby, благодарю, а чуть выше вы дали понять что в 115 записи для "unified-extensions-button" и "alltabs-button" могут быть эквивалентны, т.е. для config.js можно так

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

Выделить код

Код:

// сделать кнопку All Tabs (alltabs-button) съемной
(async topic => {
	var obs = doc => doc.getElementById("alltabs-button")?.setAttribute("removable", true);
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");

// сделать кнопку unified-extensions-button съемной
(async topic => {
	var obs = doc => doc.getElementById("unified-extensions-button")?.setAttribute("removable", true);
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");

Как бы это дело подсократить, чтобы дублирующие строки не писать или оставить как есть?

6e73epo пишет

Как бы это дело подсократить

Да обычный мерж, только и всего.
Не стал for of писать, чтобы не потерять наглядность.

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

Выделить код

Код:

// установить для кнопок #alltabs-button и #unified-extensions-button
// атрибут "removable" как "true"
(async topic => {
	var obs = doc => {
		doc.getElementById("alltabs-button")?.setAttribute("removable", true);
		doc.getElementById("unified-extensions-button")?.setAttribute("removable", true);
	}
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");

6e73epo
Что за кнопка alltabs-button, что-то понять не могу? Разобрался, а что, удобно.

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


Логика такая: прописаны две одинаковых горячих клавиши, но у одной постфикс (или префикс, если это проще сделать в коде) с именем нужной ОС, для которой нужно выполнять код.
Если ОС, на которой запущен браузер, совпадает с именем сочетания клавиш Digit1_I_win с префиксом (постфиксом), то код из обычного имени Digit1_I игнорируется и выполняется Digit1_I_win (или Digit1_I_macosx, если браузер запущен на МакОС)
Тогда можно в один список включить как обычные сочетания клавиш, которые выполняются всегда, так и некоторые с постфиксом, которые будут действовать только на конкретной ОС (например, запуск Быстрых Заметок)
Вообще, синтаксис на твоё усмотрение, необязательно Клавиша_МОД Digit1_I, может вариант удобнее придумаешь ?

Выделить код

Код:

var KlaBa = {
keyX_1(e) { // обычное сочетание клавиш работает всегда
	console.log("Alt+X skip preventDefault");
},
Digit1_I_macosx(e, t) { // приоритет для вашей OS
	shell_RunwA("/usr/bin/open", ["-n","-b","com.apple.Stickies"]);
},
Digit1_I_win(e, t) { // приоритет для вашей OS
	shell_RunwA("C:\\Windows\\system32\\StikyNot.exe","");
},
Digit1_I(e, t) { // приоритет низкий
	run Notes
},
Digit1: "Digit1_I" //ссылка на функцию
};

Dobrov
Во загадал загадку!
Что-то ничего умнее не смог придумать, чем так

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

Выделить код

Код:

((obj, re) => {
	var del = new Set();
	var platformRe = /_(?:win|linux|macosx)$/;
	var {platform} = AppConstants, num = -platform.length - 1;

	for(var p in klaBa) platformRe.test(p) && del.add(
		p.endsWith(platform) ? p.slice(0, num) : p
	);
	for(p in klaBa) del.has(klaBa[p]) && del.add(p);
	for(var d of del) delete klaBa[d];

Dumby пишет

Во загадал загадку!

Спасибо! Но имена функций остаются в постфиксом ОС: Digit1_I_macosx(e, t)……
Перехват клавиш может не сработать ??? если нет нажатия управляющих клавиш: Digit1_macosx

пробовал убрать, создав копию свойства объекта, но не работает:

Выделить код

Код:

var klaBa = {
	keyX_1(e) {
		console.log("Alt+X skip preventDefault");
	},
	Digit1_I_macosx(e, t) { // приоритет для вашей OS
		shell_RunwA("/usr/bin/open", ["-n","-b","com.apple.Stickies"]);
	},
	Digit1_I_win(e, t) { // приоритет для вашей OS
		shell_RunwA("C:\\Windows\\system32\\StikyNot.exe","");
	},
	Digit1_I(e, t) { // приоритет низкий
		0;
	},
	Digit1: "Digit1_I" //ссылка на функцию
};

((obj, re) => {

	var del = new Set();
	var platformRe = /_(?:win|linux|macosx)$/;
	var {platform} = AppConstants, num = -platform.length - 1;

	for(var p in klaBa) platformRe.test(p) && del.add(
		p.endsWith(platform) ? p.slice(0, num) : p
	);
	for(var p in klaBa) del.has(klaBa[p]) && del.add(p);
	for(var d of del)
		delete klaBa[d]; //есть Key_OS ? удалить имена-клоны
	for(var p in klaBa)
		if (platformRe.test(p)) { //убрать имя вашей ОС из свойства
			klaBa[p.replace(platformRe,'')] = klaBa[p];
			delete klaBa[p];
	}
	for(var p in klaBa) {

		var func = klaBa[p];
		if (typeof func == "string") func = klaBa[func]; //ссылка на функцию

		var [key, mod] = p.split("_");
		mod = mod || "";

		var upper = key[0].toUpperCase();
		var prevent = key[0] == upper;

		var [, m, i] = mod.match(re);
		m = m || 0; // modifiers bitmap

		var arr = [func, prevent, i ? i == "I" ? 1 : 0 : -1]; // textfields flag

		var prop = prevent ? key : upper + key.slice(1); //имя клавиши без модификаторов

		var o = obj[prop] || (obj[prop] = Object.create(null));

		o[m] ? o[m].push(arr) : o[m] = [arr];
	}

	klaBa = obj;
})(Object.create(null), /(\d+)?([a-z]+)?/i);

for(p in klaBa)
	console.log(klaBa[p]);

Dobrov пишет

имена функций остаются в постфиксом ОС: Digit1_I_macosx(e, t)……

Имена функций значения не имеют, их вызов идёт без участия имени.
Функция перекочёвывает в массив,
а когда e.code и сумма модификаторов совпадает с нажатыми клавишами,
извлекается деструктурирующим присваиванием for(var [func, p, i] of data)

Перехват клавиш может не сработать ??? если нет нажатия управляющих клавиш: Digit1_macosx

Если отказаться от удаления ОС из свойств объекта, то будет работать неправильно.
Из-за того, что re был изменён с /(\d+)?(i)?/i на /(\d+)?([a-z]+)?/i
флаг полей ввода пропишется как ноль, то есть — только в полях ввода,
а должен быть минус единица, то есть — всегда.

пробовал убрать, создав копию свойства объекта, но не работает

Да вроде работает как написано, свойство Digit1_I_win успешно заменяется на Digit1_I
но имя самой функции, на которую ссылается klaBa.Digit1_I
при этом, конечно же, не меняется, остаётся Digit1_I_win

После установки 116-й версии в очередной раз исчезла нижняя панель. :( Специалисты, подскажите как опять её вернуть-то? Пробовал вот это, но не помогло. Может надо версию обновить? У меня в version.txt прописано: "версия, дата г-м-д: 2021-9-23".

Black_Monk пишет

Пробовал вот это, но не помогло

проверяйте

Выделить код

Код:

toolbarpaletteitem[place="palette"] > toolbaritem.ucf-additional-springs {
    background: white url("./svg/spring.svg") center no-repeat;
    border: none;
    outline: 1px solid currentColor;
    outline-offset: -1px;
    min-height: 37px;
    opacity: 0.3;
}
toolbarpaletteitem[place="toolbar"] > toolbaritem.ucf-additional-springs,
toolbarpaletteitem[place="panel"] > toolbaritem.ucf-additional-springs,
toolbarpaletteitem[place="menu-panel"] > toolbaritem.ucf-additional-springs {
    background: white url("./svg/spring.svg") center no-repeat;
    border: none;
    min-width: 34px;
    min-height: 14px;
    outline: 1px solid currentColor;
    outline-offset: -1px;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3;
}
toolbarpaletteitem[place="palette"][id*="ucf-additional-top-spring"],
toolbarpaletteitem[place="palette"][id*="ucf-additional-vertical-spring"],
toolbarpaletteitem[place="palette"][id*="ucf-additional-bottom-spring"] {
    vertical-align: top;
}
toolbarpaletteitem[place] > toolbaritem.ucf-additional-springs {
    -moz-window-dragging: no-drag !important;
}
#ucf-restart-app {
    list-style-image: url("./svg/restart-app-16.svg") !important;
    fill: #f38725 !important;
}
#ucf-additional-vertical-toggle-button {
    list-style-image: url("./svg/vert-toolbar.svg") !important;
}
#ucf-additional-top-toggle-button {
    list-style-image: url("./svg/top-toolbar.svg") !important;
}
#ucf-additional-bottom-toggle-button {
    list-style-image: url("./svg/bottom-toolbar.svg") !important;
}
#ucf-view-history-sidebar-button {
    list-style-image: url("./svg/history-16.svg") !important;
}
#ucf-view-bookmarks-sidebar-button {
    list-style-image: url("./svg/bookmark-16.svg") !important;
}
#ucf-open-directories-button {
    list-style-image: url("./svg/user-home-16.svg") !important;
}
#browser-bottombox:not([lwthemefooter="true"]):-moz-lwtheme {
    background-color: transparent !important;
}
#ucf-additional-vertical-box {
    background: none !important;
    border: none !important;
    box-shadow: none !important;
    position: relative !important;
    z-index: 3 !important;
    margin: -1px 0 !important;
}
#ucf-additional-top-bar,
#ucf-additional-vertical-bar,
#ucf-additional-bottom-bar {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    border-block: none !important;
    overflow: -moz-hidden-unscrollable;
    overflow: clip;
}
#ucf-additional-bottom-bar {
    border-top: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-bottom-bar #ucf-additional-bottom-closebutton {
    margin: 0 var(--toolbarbutton-outer-padding, 1px) !important;
    padding: var(--toolbarbutton-inner-padding, 4px) !important;
}
#ucf-additional-bottom-bar #ucf-additional-bottom-closebutton .toolbarbutton-icon {
    margin: 0 !important;
    padding: 0 !important;
    height: 16px !important;
    width: 16px !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar,
#ucf-additional-bottom-bar {
    -moz-window-dragging: no-drag !important;
    background-clip: border-box !important;
    background-origin: border-box !important;
    background-color: var(--toolbar-bgcolor, -moz-Dialog) !important;
    background-image: var(--toolbar-bgimage, linear-gradient(rgba(255,255,255,.15), rgba(255,255,255,.15))) !important;
    color: var(--toolbar-color, -moz-DialogText) !important;
    border-inline: none !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar {
    padding: 1px 0 !important;
    margin: 0 !important;
    font-size: 1rem !important;
    min-width: 20px !important;
    width: auto !important;
    flex-grow: 1 !important;
    align-items: stretch !important;
    justify-content: start !important;
    flex-direction: column !important;
}
:root:-moz-lwtheme[style*="--lwt-additional-images"] #navigator-toolbox {
    background-attachment: fixed !important;
}
#ucf-additional-vertical-box:-moz-lwtheme > #ucf-additional-vertical-bar,
#ucf-additional-bottom-bar:-moz-lwtheme {
    color: var(--toolbar-color, inherit) !important;
    background-repeat: no-repeat, var(--lwt-background-tiling, repeat-y) !important;
    background-size: auto auto !important;
    background-attachment: fixed !important;
    background-position: right top, var(--lwt-background-alignment, right top) !important;
    background-color: transparent !important;
    background-image: linear-gradient(var(--toolbar-bgcolor, rgba(255,255,255,.4)), var(--toolbar-bgcolor, rgba(255,255,255,.4))), var(--lwt-header-image, var(--lwt-additional-images, none)) !important;
}
:root[lwtheme-image="true"] #ucf-additional-vertical-box:-moz-lwtheme > #ucf-additional-vertical-bar,
:root[lwtheme-image="true"] #ucf-additional-bottom-bar:-moz-lwtheme {
    background-repeat: no-repeat, repeat-y !important;
}
:root #browser-bottombox[lwthemefooter="true"] #ucf-additional-bottom-bar:-moz-lwtheme {
    background-repeat: initial !important;
    background-attachment: initial !important;
    background-position: initial !important;
    background-color: var(--toolbar-bgcolor, rgba(255,255,255,.4)) !important;
    background-image: none !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-lwtheme > #ucf-additional-vertical-bar {
    background-color: var(--lwt-accent-color, white) !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-lwtheme-brighttext > #ucf-additional-vertical-bar {
    background-color: var(--lwt-accent-color, black) !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    border-inline-end: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-vertical-box[v_vertical_bar_start="false"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    border-inline-end: none !important;
    border-inline-start: 1px solid var(--chrome-content-separator-color, var(--toolbox-border-bottom-color, rgba(0,0,0,0.1))) !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar[collapsed="true"] {
    padding: 0 !important;
    border: none !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons {
    flex-direction: column !important;
    margin-inline: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons > toolbarbutton.toolbarbutton-combined > .toolbarbutton-text {
    padding-inline: 1px !important;
    margin-inline: 0 !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbaritem.toolbaritem-combined-buttons separator {
    display: none !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarspring {
	min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarseparator {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    margin: 2px !important;
    margin-top: 4px !important;
    margin-bottom: 0 !important;
    border: none !important;
    border-top: 1px solid currentColor !important;
    width: auto !important;
    max-width: none !important;
    height: 5px !important;
    min-height: 5px !important;
    max-height: 5px !important;
    opacity: 0.3 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > toolbarspacer {
	height: 15px !important;
}
#ucf-additional-vertical-container {
    display: flex !important;
}
#ucf-additional-vertical-container[vertautohide="true"] {
    position: relative !important;
    min-width: 100px !important;
    width: 100px !important;
    max-width: 100px !important;
    overflow: visible !important;
    margin-inline-start: 0 !important;
    margin-inline-end: -100px !important;
    pointer-events: none !important;
    visibility: hidden !important;
}
#ucf-additional-vertical-container[vertautohide="true"][v_vertical_bar_start="false"] {
    margin-inline-start: -100px !important;
    margin-inline-end: 0 !important;
}
#ucf-additional-vertical-box[vertautohide="true"] {
    position: absolute !important;
    display: block !important;
    top: 0 !important;
    bottom: 0 !important;
    left: 0 !important;
    right: auto !important;
    font-size: 0px !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"],
#ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl) {
    left: auto !important;
    right: 0 !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl) {
    left: 0 !important;
    right: auto !important;
}
#ucf-additional-vertical-box[vertautohide="true"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: calc(-1 * (100% - 5px));
    right: auto;
    opacity: 0;
    animation-name: toolbar-hide !important;
    animation-timing-function: linear !important;
    animation-duration: 0.2s !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-property: opacity !important;
    transition-timing-function: step-start !important;
    transition-duration: 0s !important;
    transition-delay: 0.2s !important;
    pointer-events: auto !important;
    height: 100% !important;
    visibility: visible !important;
    position: relative !important;
}
#ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl) > #ucf-additional-vertical-bar:not([collapsed="true"]),
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: auto;
    right: calc(-1 * (100% - 5px));
    animation-name: toolbar-hide-rtl !important;
}
#ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl) > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: calc(-1 * (100% - 5px));
    right: auto;
    animation-name: toolbar-hide !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: 0px;
    right: auto;
    opacity: 1;
    animation-name: toolbar-visible !important;
    transition-delay: 0s !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"]:-moz-locale-dir(rtl)[v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]),
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"][v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: auto;
    right: 0px;
    animation-name: toolbar-visible-rtl !important;
}
#ucf-additional-vertical-container > #ucf-additional-vertical-box[vertautohide="true"][v_vertical_bar_start="false"]:-moz-locale-dir(rtl)[v_vertical_bar_visible] > #ucf-additional-vertical-bar:not([collapsed="true"]) {
    left: 0px;
    right: auto;
    animation-name: toolbar-visible !important;
}
@keyframes toolbar-hide {
    from {
        left: 0px;
    }
    to {
        left: calc(-1 * (100% - 5px));
    }
}
@keyframes toolbar-hide-rtl {
    from {
        right: 0px;
    }
    to {
        right: calc(-1 * (100% - 5px));
    }
}
@keyframes toolbar-visible {
    from {
        left: calc(-1 * (100% - 5px));
    }
    to {
        left: 0px;
    }
}
@keyframes toolbar-visible-rtl {
    from {
        right: calc(-1 * (100% - 5px));
    }
    to {
        right: 0px;
    }
}
:root[inDOMFullscreen] #ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]),
:root[inFullscreen]:not([OSXLionFullscreen]) #ucf-additional-vertical-box > #ucf-additional-vertical-bar:not([collapsed="true"]):not([fullscreentoolbar="true"]),
#ucf-additional-bottom-bar[collapsed="true"][customizable="true"][customizing="true"] {
    visibility: collapse !important;
    padding: 0 !important;
    border: none !important;
    min-width: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > #personal-bookmarks {
    width: 0 !important;
    flex-grow: 0 !important;
    margin: 0 !important;
}
#ucf-additional-vertical-box > #ucf-additional-vertical-bar > #search-container {
    width: 0 !important;
    max-height: 0 !important;
    overflow-y: visible !important;
    flex-grow: 0 !important;
    min-width: 80px !important;
    margin: 0 !important;
}
:root[chromehidden~="location"][chromehidden~="toolbar"] #ucf-additional-vertical-container {
    display: none !important;
}
@supports (fill: color-mix(in srgb, currentColor 20%, transparent)) {
    #ucf-restart-app {
        fill: color-mix(in srgb, currentColor 20%, #f38725) !important;
    }
}

kokoss, у меня vertical_top_bottom_bar.css аналогичный, но нижней панели нет. :|

Black_Monk
Эту правку -> https://forum.mozilla-russia.org/viewto … 91#p802991 делали?
2023-08-02_123214.png

kokoss пишет

Black_Monk
Эту правку -> https://forum.mozilla-russia.org/viewto … 91#p802991 делали?

Этот блок у меня в vertical_top_bottom_bar.js уже есть...

kokoss
Теоретически, не исключён вариант, что Black_Monk
в UCF пользуется только нижним тулбаром и ничем другим,
поэтому про него и говорит, то есть как-бы ложный акцент.


Если предположение верно, тогда мог просто накрыться сам UCF,
и тулбар, соответственно, вместе с ним.
В таком случае, можно предложить почитать отсюда.


Ну, и ещё, напомнить, что лучше заранее удалить
импорт модуля Services.jsm а то в 117 опять будет проблема.

Dumby пишет

Теоретически, не исключён вариант, что Black_Monk
в UCF пользуется только нижним тулбаром и ничем другим,

Да, так и есть...

Dumby пишет

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

Да помогло, огромное спасибо. И vertical_top_bottom_bar.css не пришлось править...

Dumby пишет

лучше заранее удалить
импорт модуля Services.jsm а то в 117 опять будет проблема.

А это как сделать? Я честно говоря, даже не понял о чём вы... :)

Dumby

Dumby пишет

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

У меня без этой правки не работал UCF, а у него только нижняя панель, поэтому даже не подумал про эту правку!

Black_Monk пишет

А это как сделать? Я честно говоря, даже не понял о чём вы...

Видимо про это -> var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

kokoss пишет

Видимо про это -> var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

А если так: ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
Тоже удалять?

unter_officer пишет

А если так: ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
Тоже удалять?

Откуда ж мне знать, у меня нет [firefox] 117!

unter_officer пишет

А если так: ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
Тоже удалять?

Конечно удалять. По этому адресу теперь больше ничего нет.
Обратишься к такому геттеру — получишь ошибку NS_ERROR_FILE_NOT_FOUND


Если сомнение в том, а как же совместимость кода с лисицами древнее чем 104,
тогда можно задвинуть импорт какой-нибудь проверкой, например, globalThis.Services


Ещё возможен кейс, где импорт идёт через Cu.import() и не по назначению, а для получения NSVO
Это невозможно уже со 102-ой, но сам код мог ещё как-то, неправильно, но работать.
А теперь вообще откажет. В этом случае, простое удаление не поможет, нужно искать замену,
например, перенести в SystemGlobal

Dumby пишет

Конечно удалять. По этому адресу теперь больше ничего нет.
Обратишься к такому геттеру — получишь ошибку NS_ERROR_FILE_NOT_FOUND

Dumby, спасибо.


Правда я пока не планирую переходить на Windows 10+, поэтому FF117+ мне не грозит.
Но взял на заметку. Мало ли в будущем все-таки придется пересесть на Windows 10+.

Dumby, kokoss, спасибо за помощь...

to_Alls свёл в один скрипт перехват нажатий клавиш от Dumby (немного изменил), может пригодиться!

синтаксис: Имя(_mod)(_OS) справка в коде

Выделить код

Код:

(async anim => { // Для докум. окна браузера [ChromeOnly]

var klaBa = { /* блок перехвата нажатиий клавиш
синтаксис: Имя[_mod][_OS](e,t){код} e = Event, t = gBrowser.selectedTab

Откройте боковую панель и смотрите коды нажатий клавиш в консоли
имя со строчной буквы: передавать нажатия окну, запрет preventDefault()
mod содержит «I» — запуск только в полях ввода, «i» кроме полей ввода
отделять «_» от кода клавиши при наличии модификаторов и/или «iI»-флага
OS только для указанной системы: KeyA_1i_win(e,t){… //Alt+A для Windows
Ссылка: "имя нужной функции": digit1() {код}, numpad1: "digit1"
*/
	Digit1() {
		console.log("Digit1 Page+Input");
	},
	digit2() {
		console.log("digit2 skip preventDefault");
	},
	Digit3_I() {
		console.log("Digit3 Only Input");
	},
	Digit4_i() {
		console.log("Digit4 Skip Input");
	},
	Digit5: "Digit1", //ссылка на функцию
	Digit6_win(e, t) {
		console.log("Digit6_I Windows Only");
	},
	Digit6_linux(e, t) {
		console.log("Digit6_I Linux Only");
	},
};
((obj, re, reos, del) => { // парсинг блока клавиш ускоряет обработку нажатий
	var {platform} = AppConstants, num = -platform.length - 1;
	for(var p in klaBa) reos.test(p) && del.add( p.endsWith(platform) ? p.slice(0, num) : p);
	for(var p in klaBa) del.has(klaBa[p]) && del.add(p);
	for(var d of del) delete klaBa[d]; //есть Key_OS ? удалить имена-клоны
	for(var p in klaBa)
		if (reos.test(p)) { //убрать имя вашей ОС из свойства
			klaBa[p.replace(reos,'')] = klaBa[p]; delete klaBa[p];}

	for(var p in klaBa) { // парсинг функция(){…}, bool, num
		var func = klaBa[p];
		if (typeof func == "string") func = klaBa[func]; //ссылка на функцию
		var [key, mod] = p.split("_"); mod = mod || "";
		var upper = key[0].toUpperCase();
		var prevent = key[0] == upper; // bool: True если имя с Заглавной. если false: Skip preventDefault
		var [, m, i] = mod.match(re); m = m || 0; // modifiers bitmap
		var arr = [func, prevent, i ? i == "I" ? 0 : 1 : -1]; // textfields flag
		var prop = prevent ? key : upper + key.slice(1); //имя клавиши без модификаторов
		var o = obj[prop] || (obj[prop] = Object.create(null));
		o[m] ? o[m].push(arr) : o[m] = [arr];
	}
	klaBa = obj; // нет Ii -1 везде, Имя_I = 0 в полях ввода, Имя_i 1 кроме полей ввода
})(Object.create(null), /(\d+)?(i)?/i, /_(?:win|linux|macosx)$/, new Set());

var Debug = (id = "sidebar-box") => !document.getElementById(id).hidden,
keydown_w = e => { // перехват клавиш
	if (e.repeat) return; // повтор: выход
	var mod = e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey;
	if (e.keyCode > 31 && Debug())
		console.log(`■ key ${mod ? e.code +"_"+ mod : e.code}`); //показ клавиш
	var data = klaBa[e.code]?.[mod]; if (!data) return;
	for(var [func, p, i] of data)
		if (i ^ docShell.isCommandEnabled("cmd_insertText"))
			p && e.preventDefault(), func(e, gBrowser.selectedTab); //запуск по сочетанию
};
var args = ["keydown", e => { keydown_w(e)}, true];

window.addEventListener(...args);
var id = Symbol(), ucf = ucf_custom_script_win;
ucf.unloadlisteners.push(id);
ucf[id] = {destructor: () => window.removeEventListener(...args)};

})({animate: true});

Dumby
Убрал, в config.js тоже, но у меня теперь отвалились хоткей, которые похоже зависят от него.

Выделить код

Код:

var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Выделить код

Код:

FileUtils.getFile('UChrm','...']).launch();

Что делать? Если уберут в 117, придется обходиться таким?

Выделить код

Код:

var p=Services.dirsvc.get('UChrm',Ci.nsIFile);p.initWithPath(p.path+"...");p.launch();
Dobrov пишет

смотрите коды нажатий клавиш в консоли

Вот это мне не нравится.
Из-за этого, вычисление модификаторов вытащено из того места, где оно было,
и поднято выше, таким образом, теряется существенная часть смысла предварительной
обработки klaBa — «если e.code не совпадает, то модификаторы даже не вычисляются».


Даже если вернуть на место и повторить под Debug()-условием, то всё равно,
это лишние проверки, а из-за e.keyCode > 31 не показывает какие-нибудь там
«Backspace», «Enter», «Pause».


Лучше бы что-нибудь другое для просмотра кода клавиш придумать.
Захотелось попробовать спец-панельку, типа жмешь, например, Ctrl+Shift+0,
выскакивает панелька, теперь нажимешь клавиши, и на ней смотришь.


Набросок кода для добавления в предоставленный, перед последней строкой.

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

Выделить код

Код:

// Ctrl+Shift+0
(klaBa.Digit0 = klaBa.Digit0 || (klaBa.Digit0 = Object.create(null)))[6] = [[e => {

	var df = MozXULElement.parseXULToFragment(`
		<panel onpopuphidden="destroy()" onpopupshown="pos()" oncommand="cmd(event)">
			<hbox style="font-size: 20px; padding: .5em;">
				<label style="min-width: 14em; border: 1px solid gray;
					display: flex; align-items: center; justify-content: center;"/>
				<button label="P" tooltiptext="Toggle skip preventDefailt()" style="min-width: 2em;"/>
				<button label="i" tooltiptext="Ignore textfields" style="min-width: 2em;"/>
				<button label="I" tooltiptext="Only textfields" style="min-width: 2em;"/>
				<box style="width: 1em;"/>
				<button label="W" tooltiptext="Windows" style="min-width: 2em;"/>
				<button label="L" tooltiptext="Linux" style="min-width: 2em;"/>
				<button label="M" tooltiptext="Mac" style="min-width: 2em;"/>
				<box style="width: 1em;"/>
				<button label="Copy"/>
			</hbox>
		</panel>
	`);

	var code, mods, os;

	var panel = df.firstChild, label = df.querySelector("label");
	panel.remove();
	var setLabel = () => label.value = [code, mods, os].filter(Boolean).join("_");

	var kw = keydown_w;
	panel.destroy = () => panel.remove(keydown_w = kw);

	panel.pos = () => {
		var {width, height} = panel.getOuterScreenRect();
		var aw = screen.availWidth - width;
		panel.moveTo(aw/2, screen.availHeight/3);
		panel.style.removeProperty("opacity");
	}
	panel.cmd = e => {
		var lab = e.target.label;
		if (lab == "Copy")
			navigator.clipboard.writeText(label.value),
			panel.hidePopup();
		else
			commands[lab](), setLabel();
	}
	var re = /i/i, commands = {
		P() {
			var first = code[0];
			var upper = first.toUpperCase();
			first = first == upper ? first.toLowerCase() : upper;
			code = first + code.slice(1);
		},
		i: (flag = "i") => re.test(mods)
			? mods = mods.replace(re, mods.includes(flag) ? "" : flag)
			: mods += flag,

		I: () => commands.i("I"),

		W: (pl = "win") => os = os == pl ? "" : pl,
		L: () => commands.W("linux"),
		M: () => commands.W("macosx")
	};

	var handleKeydown = e => {
		e.preventDefault();
		e.stopImmediatePropagation();

		if (e.repeat) return;

		os = "";
		code = e.code || "";
		mods = e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey || "";

		//if (!mods && code == "Escape") return panel.hidePopup();
		setLabel();
	}
	// end of lazy stuff

	(klaBa.Digit0[6][0][0] = () => {
		panel.style.setProperty("opacity", "0", "important");
		mainPopupSet.append(panel);
		panel.openPopupAtScreen();
		(keydown_w = handleKeydown)(e);
	})();

}, false, -1]];

b0ttle пишет

Убрал, в config.js тоже, но у меня теперь отвалились хоткей, которые похоже зависят от него.

Нисколько не похоже.
Вообще никак не связанные вещи.


Эту плюшку просто выбросили в 116
Bug 920187 - Deprecate and get rid of FileUtils.getFile()

Здравствуйте, возможно ли на страницу about:addons вернуть поиск - фильтр установленных дополнений, как когда-то давно в Firefox по дефолту было?

Dumby
Как можно поправить? Не работает из-за FileUtils.getFile. При выходе, удалялись ненужные файлы в профиле.

Выделить код

Код:

try{var closer={observe:(s,topic,data)=>{if(topic=="quit-application"){
//const clipboard=Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);clipboard.emptyClipboard(clipboard.kGlobalClipboard);//Clear clipboard
FileUtils.getFile("UChrm",["user_chrome_files","_","sqlite[Fx].vbs"]).launch();//Run external script
}}};Services.obs.addObserver(closer,"quit-application",false);}catch(ex){}

По ссылке на баг, прочитал такое "IOUtils and PathUtils offers async alternatives", но дуб дубом в этом. Это что-то вроде замены FileUtils.getFile()?

В последних версиях пропали интервалы из add_toolbar_buttons@vitaliy.ru.xpi. Остался только растягивающийся интервал. Можно ли это как то поправить?

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

special_widget.css:

Выделить код

Код:

@-moz-document url("chrome://browser/content/browser.xhtml") {
toolbarpaletteitem[place="palette"] > toolbarspring[id^="customizableui-special-spring"] {
    background: white url("./svg/spring.svg") center no-repeat !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 37px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="toolbar"] > toolbarspring[id^="customizableui-special-spring"] {
    background: white url("./svg/spring.svg") center no-repeat !important;
    border: none !important;
    min-width: 34px !important;
    min-height: 14px !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="palette"] > toolbarspacer[id^="customizableui-special-spacer"] {
    background: white !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 37px !important;
    width: 15px !important;
    max-width: 15px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="toolbar"] > toolbarspacer[id^="customizableui-special-spacer"] {
    background: white !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 14px !important;
    width: 15px !important;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3 !important;
}
toolbarseparator[id^="customizableui-special-separator"] {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    margin: 2px !important;
    margin-inline-start: 4px !important;
    margin-inline-end: 0 !important;
    border: none !important;
    border-inline-start: 1px solid currentColor !important;
    min-width: 5px !important;
    width: 5px !important;
    max-width: 5px !important;
    opacity: 0.3 !important;
}
toolbar[orient="vertical"] toolbarseparator[id^="customizableui-special-separator"] {
    margin-inline-start: 2px !important;
    margin-inline-end: 2px !important;
    margin-top: 4px !important;
    margin-bottom: 0 !important;
    border-top: 1px solid currentColor !important;
    border-inline-start: none !important;
    width: auto !important;
    max-width: none !important;
    height: 5px !important;
    min-height: 5px !important;
    max-height: 5px !important;
}
toolbarpaletteitem[place="palette"] > toolbarseparator[id^="customizableui-special-separator"] {
    margin: 0 !important;
    min-height: 37px !important;
}
toolbarpaletteitem[place] > toolbarspring[id^="customizableui-special-spring"],
toolbarpaletteitem[place] > toolbarspacer[id^="customizableui-special-spacer"],
toolbarpaletteitem[place] > toolbarseparator[id^="customizableui-special-separator"] {
    -moz-window-dragging: no-drag !important;
}
toolbar:not(#nav-bar) toolbarpaletteitem[place="toolbar"][id^="wrapper-customizableui-special-spring"],
toolbar:not(#nav-bar) toolbarspring {
    max-width: none !important;
}
toolbarpaletteitem[place="palette"][id^="wrapper-customizableui-special-spacer"],
toolbarpaletteitem[place="palette"][id^="wrapper-customizableui-special-separator"] {
    -moz-box-align: center !important;
    box-align: center !important;
    align-items: center !important;
    text-align: center !important;
}
}

Northtech
У меня так, вроде не сильно отличается.

special_widget.css

Выделить код

Код:

@-moz-document url("chrome://browser/content/browser.xhtml") {
toolbarpaletteitem[place="palette"] > toolbarspring[id^="customizableui-special-spring"] {
    background: white url("../vertical_top_bottom_bar/svg/spring.svg") center no-repeat !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 37px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="toolbar"] > toolbarspring[id^="customizableui-special-spring"] {
    background: white url("./svg/spring.svg") center no-repeat !important;
    border: none !important;
    min-width: 34px !important;
    min-height: 14px !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="palette"] > toolbarspacer[id^="customizableui-special-spacer"] {
    background: white !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 37px !important;
    width: 15px !important;
    max-width: 15px !important;
    opacity: 0.3 !important;
}
toolbarpaletteitem[place="toolbar"] > toolbarspacer[id^="customizableui-special-spacer"] {
    background: white !important;
    border: none !important;
    outline: 1px solid currentColor !important;
    outline-offset: -1px !important;
    min-height: 14px !important;
    width: 15px !important;
    margin-block: 2px !important;
    margin-inline: 1px !important;
    opacity: 0.3 !important;
}
toolbarseparator[id^="customizableui-special-separator"] {
    -moz-appearance: none !important;
    appearance: none !important;
    padding: 0 !important;
    margin: 2px !important;
    margin-inline-start: 4px !important;
    margin-inline-end: 0 !important;
    border: none !important;
    border-inline-start: 1px solid currentColor !important;
    min-width: 5px !important;
    width: 5px !important;
    max-width: 5px !important;
    opacity: 0.3 !important;
}
toolbar[orient="vertical"] toolbarseparator[id^="customizableui-special-separator"] {
    margin-inline-start: 2px !important;
    margin-inline-end: 2px !important;
    margin-top: 4px !important;
    margin-bottom: 0 !important;
    border-top: 1px solid currentColor !important;
    border-inline-start: none !important;
    width: auto !important;
    max-width: none !important;
    height: 5px !important;
    min-height: 5px !important;
    max-height: 5px !important;
}
toolbarpaletteitem[place="palette"] > toolbarseparator[id^="customizableui-special-separator"] {
    margin: 0 !important;
    min-height: 37px !important;
}
toolbarpaletteitem[place] > toolbarspring[id^="customizableui-special-spring"],
toolbarpaletteitem[place] > toolbarspacer[id^="customizableui-special-spacer"],
toolbarpaletteitem[place] > toolbarseparator[id^="customizableui-special-separator"] {
    -moz-window-dragging: no-drag !important;
}
toolbar:not(#nav-bar) toolbarpaletteitem[place="toolbar"][id^="wrapper-customizableui-special-spring"],
toolbar:not(#nav-bar) toolbarspring {
    max-width: none !important;
}
toolbarpaletteitem[place="palette"][id^="wrapper-customizableui-special-spacer"],
toolbarpaletteitem[place="palette"][id^="wrapper-customizableui-special-separator"] {
    align-items: center !important;
}
}


Это стиль, еще есть скрипт который добавляет сами интервалы. Если без стиля, они переносятся на панели, просто без обертки в виде стиля будут невидимы. Собственно, у меня все интервалы есть на 116, через ucf.

Dumby пишет

Набросок кода для добавления в предоставленный, перед последней строкой.

Это всё конечно хорошо, но многовато дублирующего кода - в однократном парсинге и коде просмотра клавиш одинаковая обработка модификаторов и постфиксов на ОС. А если использовать предпросмотр нажатий, то код панельки пригодится вынести отдельно - в ней и нажатия мыши  удобно просматривать и вывод отладки или сообщения об ошибках…


Ещё вот вариант простого синтаксиса клавиш, сложнее в восприятии, но проще для кода парсинга:
Dumby — посмотри, как с этим вариантом переделать keydown_win = e => ……
Digit1() { console.log("Digit1 Page+Input Only Apple");}, true, -1, "macosx", (ОС можно пропустить:  …, true, -1,,)

Переделал обработчик нажатий клавиш, проверь, так лучше?

Выделить код

Код:

var keydown_win = e => { // перехват клавиш, учитывая поля ввода
	if (e.repeat || !prefs.getBoolPref('javascript.enabled',1)) return; // повтор|скрипты OFF: выход
	var data = klaBa[e.code]?.[e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey];
	if (data)
		for(var [func, p, i] of data)
			if (i ^ docShell.isCommandEnabled("cmd_insertText"))
				p && e.preventDefault(), func(e, gBrowser.selectedTab); //запуск по сочетанию
	if (!Debug()) return; // отладки нет
	var mod = e.metaKey*8 + e.ctrlKey*4 + e.shiftKey*2 + e.altKey;
	console.log(`■ key ${mod ? e.code +"_"+ mod : e.code}`); //показ клавиш
}

Поделитесь, пожалуйста, рабочим вариантом
для автоматической очистки поля поиска
после ввода запроса.
Скрипт https://github.com/Aris-t2/CustomJSforFx/blob/master/scripts/alternative_searchbar.uc.js
на [firefox] 114 не работает... :angry: (в строке 40 настройку false на true менял)

Northtech пишет

Остался только растягивающийся интервал

У меня всегда было их два, "Растягивающийся пробел" первый и последний в списке. Остальные тоже на месте. https://imgsh.net/a/rqWJYGc.png
special_widget.css этот файл не трогал, как есть. В Вашем на 3 строки снизу больше

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

Выделить код

Код:

box-align: center !important;
    align-items: center !important;
    text-align: center !important;


Вот мой add_toolbar_buttons@vitaliy.ru.xpi

Viatcheslav
https://forum.mozilla-russia.org/viewto … 60#p795260
xrun1
Замените в файле special_widget.css -> -moz-box-align: center !important;  на  align-items: center !important;

kokoss пишет

https://forum.mozilla-russia.org/viewtopic.php?pid=795260#p795260

Спасибо, но мне не для панели поиска Findbar (поиск на странице),
а для поисковой строки Searchbar (поиск в сети Интернет) :blush:

Viatcheslav
Ну тогда как вариант с помощью кнопки из Add Toolbar Buttons!
2023-08-04_113956.png

kokoss
Спасибо, двоение вылечилось.:beer:
Viatcheslav
Может, подойдёт очистка поля прокруткой колёсика мышки?

Спасибо, но там много лишнего... :/

Viatcheslav пишет

Спасибо, но там много лишнего...

Достаточно этого:

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

Выделить код

Код:

// Очистить панель адреса или поиска прокруткой колёсиком мыши на панели
// https://forum.mozilla-russia.org/viewtopic.php?pid=788229#p788229
(this.clearsearchurlbar = {
            init(that) {
                for (let el of (this.urlsearcbar = document.querySelectorAll("#urlbar,#searchbar")))
                    el.addEventListener("wheel", this);
                that.unloadlisteners.push("clearsearchurlbar");
            },
            handleEvent(e) {
                e.target.value = "";
            },
            destructor() {
                for (let el of this.urlsearcbar)
                    el.removeEventListener("wheel", this);
            },
        }).init(this);

kokoss пишет

Достаточно этого:

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

Выделить код

Код:

// Очистить панель адреса или поиска прокруткой колёсиком мыши на панели
// https://forum.mozilla-russia.org/viewtopic.php?pid=788229#p788229
(this.clearsearchurlbar = {
            init(that) {
                for (let el of (this.urlsearcbar = document.querySelectorAll("#urlbar,#searchbar")))
                    el.addEventListener("wheel", this);
                that.unloadlisteners.push("clearsearchurlbar");
            },
            handleEvent(e) {
                e.target.value = "";
            },
            destructor() {
                for (let el of this.urlsearcbar)
                    el.removeEventListener("wheel", this);
            },
        }).init(this);

Не знаю,  в чём дело, но не работает :angry:

04-08-2023 19:58:52

kokoss пишет

Ну тогда как вариант с помощью кнопки из Add Toolbar Buttons!
https://www.upload.ee/image/15536116/2023-08-04_113956.png

Слава Богу, хоть это сработало :beer:

Viatcheslav пишет

Не знаю,  в чём дело, но не работает

У меня при добавлении в custom_script_win.js  тоже не работает в строке поиска, только в адресной строке, поэтому предложил ATB.

b0ttle пишет

По ссылке на баг, прочитал такое "IOUtils and PathUtils offers async alternatives", но дуб дубом в этом. Это что-то вроде замены FileUtils.getFile()?

Здесь, наверно, упор скорее на «async»,
и, «alternatives» — это скорее «alternatives» для них, а не для нас,
им-то "UChrm" нафик не сдался.


Вобщем, как-то так

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

Выделить код

Код:

(async parts => Services.obs.addObserver(function quit(s, topic, data) {
	Services.obs.removeObserver(quit, topic);
	//if (data == "restart") return;
	var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
	parts.forEach(file.append);
	file.launch();
}, "quit-application"))(["user_chrome_files", "_", "sqlite[Fx].vbs"]);

Dobrov пишет

Переделал обработчик нажатий клавиш, проверь, так лучше?

Да, так чуть лучше.
Плюс, три три дебаг-строки рядом, удобно закомментировать, если что.
Зачем return при не 'javascript.enabled' понять не смог.

Dumby — посмотри, как с этим вариантом переделать keydown_win = e => ……
Digit1() { console.log("Digit1 Page+Input Only Apple");}, true, -1, "macosx"

Четыре сущности разделённые запятой. Функция и три примитива.
Синтаксис функции такой, какой возможен, вроде как, только в объектах и в классах.
Так что неясно, где бы такая запись могла бы быть расположена.


kokoss пишет

У меня при добавлении в custom_script_win.js  тоже не работает в строке поиска, только в адресной строке

Если под «при добавлении» имеется в виду добавление в конец,
то ничего удивительного что работает лишь частично,
ведь custom_script_win.js исполняется по событию "DOMContentLoaded"
а к этому моменту #searchbar'а ещё нет.


Код предназначен быть добавленным после строки
«// Здесь может быть ваш код который сработает по событию "load"»


Об этом как-бы намекает констукция вида (this.something = {…}).init(this);
Если в терминах встроенного в CustomStylesScripts.jsm загрузчика,
это — scriptschrome: { …, load: [ …, { path: "…", ucfobj: true, }, ] },

Есть скрипт который нормально работает на странице about:addons с консоли
Как сделать чтобы он автоматом запускался при открытии about:addons?
пробовал создать .js но что-то оно селекторы не считывает и походу не задаёт( может есть нюанс какой-то?

OmTatSat пишет

Как сделать чтобы он автоматом запускался при открытии about:addons?

Допустим, создаём в папке custom_scripts
файл about_addons_test.js в котором прописываем своё js-добро.


Тогда, в CustomStylesScriptsChild.jsm
в массив UcfStylesScripts.scriptscontent.pageshow
добавляем
{ path: "about_addons_test.js", urlregxp: /about:addons/ },


Теперь, убеждаемся, что в настройках UCF проставлена галка
[✔] Включить стили и скрипты для контента [about:, chrome:, moz-extension:]


Всё. Рестарт с очисткой кэша, и должно работать.


Если не работает, то на Firefox, древнее чем 115,
следует удалить папку startupCache вручную.

пробовал создать .js но что-то оно

Это предложение угадать его содержимое?

Dumby пишет

Зачем return при не 'javascript.enabled' понять не смог.

Для временного отключения кода перехвата клавиш - для отладки или если в системе такие же есть.
2023-08-05-17-20-31.png

Dumby пишет

Код предназначен быть добавленным после строки
«// Здесь может быть ваш код который сработает по событию "load"»

Туда и добавлял, сейчас проверил -> работает, дааааа :angry:

Dumby пишет

Вобщем, как-то так

Классно, отрабатывает лучше чем было.

Dobrov пишет

Для временного отключения кода перехвата клавиш - для отладки

Почему тогда именно эта настройка,
а не какая-нибудь другая, или вообще своя.

или если в системе такие же есть

Это какая-то разновидность дискриминации?
Типа: «
        Запретил исполняться контентским скриптам?
        Ну тогда и с этого скрипта получишь шиш.
».
Или просто связывание несвязанных вещей?

Dumby пишет
OmTatSat пишет

Как сделать чтобы он автоматом запускался при открытии about:addons?

Допустим, создаём в папке custom_scripts
файл about_addons_test.js в котором прописываем своё js-добро.


Тогда, в CustomStylesScriptsChild.jsm
в массив UcfStylesScripts.scriptscontent.pageshow
добавляем
{ path: "about_addons_test.js", urlregxp: /about:addons/ },


Теперь, убеждаемся, что в настройках UCF проставлена галка
[✔] Включить стили и скрипты для контента [about:, chrome:, moz-extension:]


Всё. Рестарт с очисткой кэша, и должно работать.


Если не работает, то на Firefox, древнее чем 115,
следует удалить папку startupCache вручную.

пробовал создать .js но что-то оно

Это предложение угадать его содержимое?

Спасибо огромное!) Голову сломал, никак не получалось)
Правда пришлось перейти на новый загрузчик, и пока восстановить работу всего зверинца не удаётся, зато поиск-фильтр уже есть в about:addons как давным давно было по дефолту !)
Например у меня работал CustomButtons, после смены загрузчика кнопка в сайдбаре дополнений пропала, хотя само приложение есть, странно( может из-за отвалившегося disable-add-on-signing.js ? хотя я и пробовал его аж в двух местах прописать
Потом был удобные менеджер включенных отключенных скриптов https://github.com/xiaoxiaoflood/firefox-scripts#instructions ссылка utils → I'm interested in both scripts and extensions https://raw.githubusercontent.com/xiaoxiaoflood/firefox-scripts/master/utils.zip
и styloaix.uc.js тоже не ясно куда его прописать, в какой подпункт?
может есть мануал, как определять какой скрипт и стиль куда прописывать?

Dumby пишет

Почему тогда именно эта настройка, а не какая-нибудь другая, или вообще своя.

3 уже добавлял, но в итоге в UCF только одну оставил. К javascript.enabled привязал просто как пример, кому надо, уберут!

или если в системе такие же есть

Да, на макос с Meta+Shift+Буква много системных сочетаний, поэтому и просил постфикс операционки добавить в перехват нажатий клавиш.

OmTatSat
Тут не все так просто, из-за этого и не получится такой удобный менеджер скриптов прикрутить, наверно придется всю ucf тогда переделывать. Мне тоже у него понравился тот менеджер, очень удобный. Но, возможно, многое не прикрутишь, как в ucf. Потому и остановился на ucf. Здесь к тому же что-то подскажут, что-то обновляется. У каждого свой плюсы и минусы, и свой особенности.

OmTatSat пишет

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

Не понял. Что значит «пришлось» ?


UCF либо развёрнут, либо нет.
Если развёрнут, значит и загрузчик есть, на него не надо «переходить».
Ты либо используешь его, либо нет.


Остальное, иные uc-экосистемы, или CB, могут и должны
работать независимо и параллельно, и, если есть положительный опыт взаимодействия с ними,
то, без особой необходимости, и уверености в том, что «я могу перейти», ничего с ними делать не следует.


Dobrov пишет

extensions.user_chrome_files.debug

Другое дело.

b0ttle пишет

OmTatSat
Тут не все так просто, из-за этого и не получится такой удобный менеджер скриптов прикрутить, наверно придется всю ucf тогда переделывать. Мне тоже у него понравился тот менеджер, очень удобный. Но, возможно, многое не прикрутишь, как в ucf. Потому и остановился на ucf. Здесь к тому же что-то подскажут, что-то обновляется. У каждого свой плюсы и минусы, и свой особенности.

кстати оказалось, они оба вместе живут себе)) просто конфиг config.js который в папке Firefox я сначала заменил на UCF а потом решил попробовать объединить, и волшебным образом завелось)

скрытый текст
https-forum-mozilla-russia-org-post-php-tid-76642-qid-806409-Mozilla-Firefox-08-05-23-21-25-19.png

Выделить код

Код:

// UserChromeFiles Vitaliy V. http://forum.mozilla-russia.org/viewtopic.php?id=76642
lockPref('xpinstall.signatures.required', false);
lockPref('extensions.install_origins.enabled', false);
try {
  let cmanifest = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('UChrm', Ci.nsIFile);
  cmanifest.append('utils');
  cmanifest.append('chrome.manifest');
  Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest);

  Cu.import('chrome://userchromejs/content/BootstrapLoader.jsm');
} catch (ex) {};

try {
  Cu.import('chrome://userchromejs/content/userChrome.jsm');
} catch (ex) {};

(async () => { Cu.evalInSandbox(`
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"), user_chrome_files_sandbox = {
	init() {
		Services.obs.addObserver(this, "domwindowopened");
		Services.obs.addObserver(this, "profile-after-change");
	},
	observe(aWindow, aTopic, aData) {
		Services.obs.removeObserver(this, "profile-after-change");
		this.observe = (window, topic, data) => {
			if (!(window instanceof Ci.nsIDOMChromeWindow)) return;
			var docElementInserted = e => {
				var win = e.target.defaultView;
				if (win instanceof Ci.nsIDOMChromeWindow)
					user_chrome.initWindow(win);
			};
			window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
			window.addEventListener("load", e => {
				window.addEventListener("unload", e => {
					window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
				}, { once: true });
			}, { once: true });
		};
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("user_chrome_files");
		file.append("user_chrome.manifest");
		if (!file.exists() || !file.isFile()) {
			this.removeObs();
			return;
		}
		try {
			Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(file);
			Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
		} catch(ex) {
			this.removeObs();
			return;
		}
		if (aTopic === "domwindowopened")
			this.observe(aWindow, aTopic, aData);
	},
	removeObs() {
		Services.obs.removeObserver(this, "domwindowopened");
	},
};
user_chrome_files_sandbox.init();`, Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), { wantComponents: true, sandboxName: "UserChromeFiles", wantGlobalProperties: ["ChromeUtils"],}));
})();

05-08-2023 21:43:17

Dumby пишет
OmTatSat пишет

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

Не понял. Что значит «пришлось» ?


UCF либо развёрнут, либо нет.
Если развёрнут, значит и загрузчик есть, на него не надо «переходить».
Ты либо используешь его, либо нет.


Остальное, иные uc-экосистемы, или CB, могут и должны
работать независимо и параллельно, и, если есть положительный опыт взаимодействия с ними,
то, без особой необходимости, и уверености в том, что «я могу перейти», ничего с ними делать не следует.

Я скриптами разбираюсь на аматорском уровне, может и не правильно термин применил)
Пришлось, значит, чтобы получить желаемое, пришлось рискнуть своим налаженным пространством :D
Но конечно с бекапами всё это дело с легкостью на душе происходит.
Тем более что удалось их оба задействовать.
Спасибо за помощь.

05-08-2023 22:00:24
Вот как сам поиск - фильтр в about:addons получился
не идеально, но дело делает, может кто-то ещё допилит, буду благодарен)
https://i.ibb.co/DQbztvq/Mozilla-Firefo … -49-20.png
https://i.ibb.co/nP9wSH5/Mozilla-Firefo … -49-12.png
https://i.ibb.co/D1fsk5T/Mozilla-Firefo … -49-00.png

Выделить код

Код:

// Создание элементов

const input = document.createElement('input');

input.id = 'search';

input.type = 'text';

const button = document.createElement('button');

button.id = 'filter';

button.innerText = 'Найти';

const search = document.querySelector('.main-search > search-addons:nth-child(2)');

// Прикрепление к нужному элементу

search.appendChild(input);

search.appendChild(button);

// Обработчики событий

button.addEventListener('click', () => {

const searchTerm = input.value;

filterElements("");

filterElements(searchTerm);

});

input.style.position = 'relative';

input.style.zIndex = '999';

input.style.left = '300px';

input.style.top = '-37px';

input.style.maxheight = '13px';

input.style.height = '15px';

button.style.position = 'relative';

button.style.zIndex = '999';

button.style.left = '300px';

button.style.top = '-35px';

button.style.height = '20px';

function filterElements(searchTerm) {

const names = document.querySelectorAll('h3.addon-name a.addon-name-link');

const descs = document.querySelectorAll('.addon-description');

descs.forEach((desc, i) => {

console.log('Проверяем элемент', desc);

const textElem = desc.firstChild;

if (!textElem) {

const text = "emp";

foundDesc = false;

} else {

text = textElem.textContent;

foundDesc = text.includes(searchTerm);

;console.log('описание:', text , 'foundDesc' , foundDesc);

}

const name = names[i];

if (!name ) {

return

}

const nametext = name.textContent;

let foundName = name.textContent.toLowerCase().includes(searchTerm);

; console.log('название:', nametext , 'foundName' , foundName , 'описание:', text , 'foundDesc' , foundDesc);

if (!foundName && !foundDesc) {

name.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.style.display = 'none';

console.log('Скрываем название:', nametext , 'foundName' , foundName , 'описание:', text , 'foundDesc' , foundDesc);

} else {

name.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.style.display = 'unset';

console.log('Показываем название:', nametext , 'foundName' , foundName , 'описание:', text , 'foundDesc' , foundDesc);

}

});

}

OmTatSat
Мне кажется, или у вас скриншоты странные?) А так, да, CB тоже работает вместе с ucf, а вот про xiaoxiaoflood, не знал.
Мне казалось, что это как иметь 4 антивируса, или как tampermonkey и greasemonkey, главное, чтобы не конфликтовало и не потеряться во всем этом. А так, почему нет.
add: Маленькие какие-то, ничего не видно. Или только у меня так? Те которые в ссылках, в спойлере нормально отображается.

b0ttle пишет

OmTatSat
Мне кажется, или у вас скриншоты странные?) А так, да, CB тоже работает вместе с ucf, а вот про xiaoxiaoflood, не знал.
Мне казалось, что это как иметь 4 антивируса, или как tampermonkey и greasemonkey, главное, чтобы не конфликтовало и не потеряться во всем этом. А так, почему нет.

Хз) смотря что вы считаете странностью :D
Согласен, как минимум просмотреть чтобы дублей не было

Подскажите, как вернуть к жизни Add Toolbar Buttons в FF 117 :)?

@Dumby
Большая просьба исправить скрипт разделителей special_widgets.js, чтобы загружался вне ucf старым методом от Aris-t2 или Endor8.

Dumby пишет

Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»

А где это надо менять?

stvol пишет

А где это надо менять?

https://forum.mozilla-russia.org/viewto … 67#p805867

fuchsfan пишет

@Dumby
Большая просьба исправить скрипт разделителей special_widgets.js, чтобы загружался вне ucf старым методом от Aris-t2 или Endor8.

Что значит «исправить»?
Исправить можно баг или поломку,
а «чтобы загружался …» — это не называется «исправить».


Вобщем, меняем первую и последнюю (не пустую) строку на такие
(async url => location != url || await delayedStartupPromise || ({
}).init("customizationready"))("chrome://browser/content/browser.xhtml");


И, вместо методов init() и destructor() один только init(), такой:

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

Выделить код

Код:

//
    init(type) {
        window.addEventListener(type, this);
        window.addEventListener("unload",
            () => window.removeEventListener(type, this)
        , {once: true});
    },


И про special_widget.css не забываем.

antialt пишет

Подскажите, как вернуть к жизни Add Toolbar Buttons в FF 117 :)?

Спасибо за ответ Dumby в личке

Убираем из кода расширения строки,
где импортируется или определяется геттер модуля
(короче, где есть этот адрес)
"resource://gre/modules/Services.jsm"

фикс для ff 117+
https://www.upload.ee/files/15546719/add_toolbar_buttons_vitaliy.ru_Dumby_fix117_.xpi.html

Dumby пишет

Вобщем, меняем первую и последнюю (не пустую) строку на такие

В навбаре все нормально, а в вертикальном аддонбаре https://github.com/Aris-t2/CustomJSforF … ical.uc.js линии-разделители видны, а промежутки не видны.

fuchsfan
Скрипт не отвечает за то, где и как видны «промежутки».
Только за их наличие там, откуда их можно на тулбар вытаскивать.


Чтобы «промежутки» было видно на этом вертикальном аддонбаре,
это надо куда-нибудь в стили пристроить что-то типа


toolbar#addonbar_v > toolbarspacer {
    width: auto !important;
    height: 15px !important;
}

Dumby пишет

Чтобы «промежутки» было видно на этом вертикальном аддонбаре,
это надо куда-нибудь в стили пристроить что-то типа

Пристроил, наконец-то все пошло, благодарю.

08-08-2023 15:43:30
@Dumby
Если отцентрируете положение подписей под элементами, будет отлично, или обойдемся так, спасибо.
text.png

fuchsfan пишет

Если отцентрируете положение подписей под элементами, будет отлично

https://forum.mozilla-russia.org/viewto … 66#p806366

kokoss пишет

https://forum.mozilla-russia.org/viewto … 66#p806366

Сработало, все как надо, благодарю за помощь. Вопрос закрыт.

Обновил ucf_hookClicks.js - изменений много, вот некоторые:


1) проще редактировать свои сочетания клавиш, добавлены клики мыши на вкладках: Закрыть все слева/справа.
2) режим работы Простой/Эксперт не требует перезапуска браузера – подсказки кнопок и действия клавиш/мыши меняются в зависимости от режима, который переключается в диалоге настроек UCF или кликом колёсика на кнопке "Печать" или строке меню.

Verevkin пишет

У меня нету таких вхождений в config.js, поделись, плиз, готовым файлом, я протестирую.

У меня он такой

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

Выделить код

Код:

//config.js для user_chrome_files (UCF) 
(async () => {
    var sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Cu.evalInSandbox(`
        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
        var user_chrome_files_sandbox = {
            init() {
                Services.obs.addObserver(this, "domwindowopened");
                Services.obs.addObserver(this, "profile-after-change");
            },
            observe(aWindow, aTopic, aData) {
                Services.obs.removeObserver(this, "profile-after-change");
                this.observe = (window, topic, data) => {
                    if (!(window instanceof Ci.nsIDOMChromeWindow)) return;
                    var docElementInserted = e => {
                        var win = e.target.defaultView;
                        if (win instanceof Ci.nsIDOMChromeWindow)
                            user_chrome.initWindow(win);
                    };
                    window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
                    window.addEventListener("load", e => {
                        window.addEventListener("unload", e => {
                            window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
                        }, { once: true });
                    }, { once: true });
                };
                var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
                file.append("user_chrome_files");
                file.append("user_chrome.manifest");
                if (!file.exists() || !file.isFile()) {
                    this.removeObs();
                    return;
                }
                try {
                    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
                    .autoRegister(file);

                    Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
                } catch(ex) {
                    this.removeObs();
                    return;
                }
                if (aTopic === "domwindowopened")
                    this.observe(aWindow, aTopic, aData);
            },
            removeObs() {
                Services.obs.removeObserver(this, "domwindowopened");
            },
        };
        user_chrome_files_sandbox.init();
    `, sandbox);
})();

fuchsfan
Обсуждали уже, что такой файл в 116 и 117 не работает без правок.
https://forum.mozilla-russia.org/viewto … 66#p805866
https://forum.mozilla-russia.org/viewto … 24#p806324
У Verevkin, похоже, ещё старый UCF (до "версия, дата г-м-д: 2021-9-23").

xrun1 пишет

Обсуждали уже, что такой файл в 116 и 117 не работает без правок.

То был файл из v115. А вот этот из v116, с ним у меня работает ucf с панелью, а какой он свжести фиг его знает.

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

Выделить код

Код:

// config.js для user_chrome_files (UCF) (код уже исправлен для работы в v117 - ниже закомментирована строка #9 импорта модуля Services.jsm, ее можно всю удалить) 
(async () => {
    var sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Cu.evalInSandbox(`
//        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
        var user_chrome_files_sandbox = {
            init() {
                Services.obs.addObserver(this, "domwindowopened");
                Services.obs.addObserver(this, "profile-after-change");
            },
            observe(aWindow, aTopic, aData) {
                Services.obs.removeObserver(this, "profile-after-change");
                this.observe = (window, topic, data) => {
                    if (!(window .isChromeWindow)) return;
                    var docElementInserted = e => {
                        var win = e.target.defaultView;
                        if (win .isChromeWindow)
                            user_chrome.initWindow(win);
                    };
                    window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
                    window.addEventListener("load", e => {
                        window.addEventListener("unload", e => {
                            window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
                        }, { once: true });
                    }, { once: true });
                };
                var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
                file.append("user_chrome_files");
                file.append("user_chrome.manifest");
                if (!file.exists() || !file.isFile()) {
                    this.removeObs();
                    return;
                }
                try {
                    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
                    .autoRegister(file);

                    Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
                } catch(ex) {
                    this.removeObs();
                    return;
                }
                if (aTopic === "domwindowopened")
                    this.observe(aWindow, aTopic, aData);
            },
            removeObs() {
                Services.obs.removeObserver(this, "domwindowopened");
            },
        };
        user_chrome_files_sandbox.init();
    `, sandbox);
})();

fuchsfan пишет

а какой он свжести фиг его знает.

Версию можно узнать в user_chrome_files/текстовый файл -> version

fuchsfan
Этот правильный. :)

Спасибо, мужики. Буду пробовать, о результатах сообщу.
Взамен вот вам набор, который у меня проработал вплоть до 116.0.3 включительно. Вдруг кому интересно.
https://disk.yandex.ru/d/k6hlE4juVVCTiA

fuchsfan пишет

xrun1 пишетОбсуждали уже, что такой файл в 116 и 117 не работает без правок.То был файл из v115. А вот этот из v116, с ним у меня работает ucf с панелью, а какой он свжести фиг его знает.скрытый текстВыделить кодКод:// config.js для user_chrome_files (UCF) (код уже исправлен для работы в v117 - ниже закомментирована строка #9 импорта модуля Services.jsm, ее можно всю удалить)
(async () => {
    var sandbox = Cu.Sandbox(Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Cu.evalInSandbox(`
//        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
        var user_chrome_files_sandbox = {
            init() {
                Services.obs.addObserver(this, "domwindowopened");
                Services.obs.addObserver(this, "profile-after-change");
            },
            observe(aWindow, aTopic, aData) {
                Services.obs.removeObserver(this, "profile-after-change");
                this.observe = (window, topic, data) => {
                    if (!(window .isChromeWindow)) return;
                    var docElementInserted = e => {
                        var win = e.target.defaultView;
                        if (win .isChromeWindow)
                            user_chrome.initWindow(win);
                    };
                    window.windowRoot.addEventListener("DOMDocElementInserted", docElementInserted, true);
                    window.addEventListener("load", e => {
                        window.addEventListener("unload", e => {
                            window.windowRoot.removeEventListener("DOMDocElementInserted", docElementInserted, true);
                        }, { once: true });
                    }, { once: true });
                };
                var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
                file.append("user_chrome_files");
                file.append("user_chrome.manifest");
                if (!file.exists() || !file.isFile()) {
                    this.removeObs();
                    return;
                }
                try {
                    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
                    .autoRegister(file);

                    Services.scriptloader.loadSubScript("chrome://user_chrome_files/content/user_chrome.js", globalThis, "UTF-8");
                } catch(ex) {
                    this.removeObs();
                    return;
                }
                if (aTopic === "domwindowopened")
                    this.observe(aWindow, aTopic, aData);
            },
            removeObs() {
                Services.obs.removeObserver(this, "domwindowopened");
            },
        };
        user_chrome_files_sandbox.init();
    `, sandbox);
})();

Спасибо, вроде работает.
Но есть 1 нюанс: панель СВЕРХУ. Как её переместить вниз?
002760.png

Verevkin пишет

Но есть 1 нюанс: панель СВЕРХУ. Как её переместить вниз?

Зачем перемещать дополнительную панель вниз, если можно использовать нижнюю панель!
Add, и заверните цитируемый текст в тег -> Quote, а код под -> Spoiler

kokoss пишет

Зачем перемещать дополнительную панель вниз, если можно использовать нижнюю панель!

А как её создать?

002762.png

kokoss пишет

Зачем перемещать дополнительную панель вниз, если можно использовать нижнюю панель!

Обнаружил кнопку вызова окна настройки, но рано обрадовался: чекбоксы включаются, но перезапуск не срабатывает.
ЧЯДНТ?

2023-08-25-21-35.png

Как вручную подправить конфиг, чтобы тупо появилась нижняя панель? Кроме неё мне ничего не надо.

Verevkin пишет

А как её создать?

Её не надо создавать, её необходимо включить в настройках UCF + перезапустить браузер...

Обнаружил кнопку вызова окна настройки, но рано обрадовался: чекбоксы включаются, но перезапуск не срабатывает.
ЧЯДНТ?

Может потому что используйте актуальный код в старом комплекте UCF

Господа!

Прошу помощи в возвращении к жизни UCF в 117 версии.

В 116 все работает норм.(после изменений которые были вот в этом посте https://forum.mozilla-russia.org/viewto … 69#p805869)

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

Пробовал комментировать
//        var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
как сказано выше, но результата не принесло.

==================================================
РЕШЕНО!
Путем удаления этих строк во всех файлах UCF

vassemm пишет

В 117 окно настроек открывается с пустыми настройками, крыжики ставятся, но перезагрузка не работает и состояние не запоминает.

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

26-08-2023 09:11:57
UCF сильная вещь, но как уважаемый Dumby перестанет ее поддерживать, так и кранты?

26-08-2023 09:17:07

Verevkin пишет

Но есть 1 нюанс: панель СВЕРХУ. Как её переместить вниз?

Просто к сведению, у Aris-t2 есть горизонтальные панели, верхняя и нижняя, нижняя это https://github.com/Aris-t2/CustomJSforF … nbar.uc.js Подключается легко, работает.

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

Выделить код

Код:

data:image/png;base64,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/W1QsGOW86RUrmPrwltvH93pe8JVTQk0VsXz5z/ba62efJLZeOqpdvrq4ouzdg5UNtBYOlga65577rE+3H777dBYNKlorPu+//2bb7rp3/79N6M/OnrI4EHDhg4ebomswYOKRfLG7t1nfvyMhx5++LavfMXf39dYturoaOxZP26J/zLxQM2wmsN/37j/wdcl3gdb3ng6zDfiWLet2K8oJmKVtTOkdcKl6xjkHHONWP9671umXpgeLsVQY02aNKlQKOzbt+/dd9/92Mc+9tZbb+3YsWP48OEXXHBBwfmBeldXV0hjOcqEFj8CjfXRZ257gDgSx1Y4r09hFJW7j62HhBrLOval8x11xFj29rCNOd9xR5Us3/PiBbf7Aspy4fyXJJ7sXlUqJrAQqpoSaKzKZt06cv315I9/ZDZed529AOuDH8zaOVDxmI4Pb7399on977XiX/va13yNNX369E9S81TJYl3RoLE8jbXxhU2nnnqKpa6GDxtiYYXG0lh7/3vviJNOeuTRR+fMnu3vH6zHCuefbJo6e6oX8ZrD0yjElyo6GiswIp+VZEoPZ9GcfFWV3GA4nUb45Ba3KagjvaW0YySWxrrsssus8G7ZssWSWVbnfuWVV/bv319VVTVy5EgvWCGNtXtV6xvnX/BiJyV9uByVl0+idc/rU24f3Rns46gYwhxlp5hGlbQXba2UjfIK4mQZkWTR7C+clNStlKusJ4GUKh0lqJoSaKxK5ehRcvfdxBrZqZWIZNgwe8H7rbdm7RzoI2B80OGrX/3qokWLrA933HEH1rzTpKKxfvL445/59Kffeff9IYMHjzjpw5bAGjx4sCUCDh48eOjQoffee+/Xv/nNF//6r/39qblCh3AWR5TXcURWJ5lSygb5qokWW/xcoZ7GovNJlk6jFR6XlOIMdjZ2TClltjjN5+58x7YqzklZuSZ5rMsvv9wK74EDB1566SXiKP2hQ4deeOGF7mcS1lhTXn/ojcktn/wPRuuEVY5QY6m3+EeF81s+SWksNtPmHSWsmhKMoRXJ739vP7393/+d2XjeeaSjg4wbl7VzoO+A8UGH5cuXX3HFFdaHH/7whzfffPOECROy9simz2os60q/97//+4LzL3ht164RI0764IknDho06OjRo5bAsq79z2/cOOKkk86nHgPIa6zwxJtQczgSipTSPa4cYiYNfTvh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

UCF для FF 115-116 с правками от Dumby

Dumby Посмотрите кнопку toggleRestartlessAddons она не работает после удаления в ней Components.utils.import("resource://gre/modules/Services.jsm");

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

Выделить код

Код:

// 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: 0,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

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

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

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

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

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

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

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

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

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

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
function checkForAddonsUpdates() {
//== 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;

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

var ADDONS_URL = "about:addons";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		stopWait();
		if(!tbTab)
			tab.closing = false;
		function removeTab() {
			if(!tab.collapsed)
				return;
			if(tbTab) {
				tabmail.closeTab(tbTabInfo, true /*aNoUndo*/);
				return;
			}
			gBrowser.removeTab(tab);
			(function forgetClosedTab(isSecondTry) {
				var ss = "nsISessionStore" in Components.interfaces
					? (
						Components.classes["@mozilla.org/browser/sessionstore;1"]
						|| Components.classes["@mozilla.org/suite/sessionstore;1"]
					).getService(Components.interfaces.nsISessionStore)
					: trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
				if(!("forgetClosedTab" in ss))
					return;
				var closedTabs = (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 app = Services.appinfo.name;
	var pv = parseFloat(Services.appinfo.platformVersion);
	if(app == "SeaMonkey")
		this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif";
	else if(app == "Thunderbird") {
		this.imgConnecting = "chrome://messenger/skin/icons/connecting.png";
		this.imgLoading = "chrome://messenger/skin/icons/loading.png";
	}
	else {
		this.imgConnecting = app == "Firefox" && pv >= 58
			? "chrome://browser/skin/tabbrowser/tab-connecting.png"
			: "chrome://browser/skin/tabbrowser/connecting.png";
		this.imgLoading = app == "Firefox" && pv >= 48
			? "chrome://global/skin/icons/loading.png"
			: "chrome://browser/skin/tabbrowser/loading.png";
	}
	if(!(btn instanceof XULElement)) {
		this.loading = this.restore = function() {};
		return;
	}
	var useAnimation = app == "Firefox" && pv >= 32 && pv < 48;
	var btnIcon = btn.icon
		|| btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon");
	var origIcon = btnIcon.src;
	btnIcon.src = this.imgConnecting;
	if(useAnimation) {
		let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null);
		let s = btnIcon.style;
		s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" ");
		s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" ");
		s.width = cs.width;
		s.height = cs.height;
		s.boxShadow = "none";
		s.borderColor = s.background = "transparent";
		btnIcon.setAttribute("fadein", "true");
		btnIcon.setAttribute("busy", "true");
		btnIcon.classList.add("tab-throbber");
		btnIcon._restore = function() {
			delete btnIcon._restore;
			btnIcon.removeAttribute("busy");
			btnIcon.removeAttribute("progress");
			setTimeout(function() {
				btnIcon.classList.remove("tab-throbber");
				btnIcon.removeAttribute("style");
				btnIcon.removeAttribute("fadein");
			}, 0);
		};
	}
	this.loading = function() {
		btnIcon.src = this.imgLoading;
		if(useAnimation)
			btnIcon.setAttribute("progress", "true");
	};
	this.restore = function() {
		btnIcon.src = origIcon;
		if(useAnimation)
			btnIcon._restore();
	};
}
}).call(this);
//== Check for Addons Updates end
}              

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

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
		if(!isContextOpened())
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& 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;
				return true;
			}
			return false;
		}
	);
}
function isContextOpened() {
	return inBtn(document.popupNode);
}
function inBtn(node) {
	for(; node; node = node.parentNode)
		if(node == self)
			return true;
	return false;
}

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

Выделить код

Код:

try {CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js"
		));
	},
	onCreated(btn) {
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC");
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
});} catch(ex) {Cu.reportError(ex);}

kokoss пишет

Может потому что используйте актуальный код в старом комплекте UCF

Ну, я ж не шарю во всём этом добре. Дайте просто набор файлов для 117+, готовых к употреблению, пожалуйста.
Или скажите, что нужно сделать со старым набором, взятым тут: https://disk.yandex.ru/d/qz1Ci_0OOrjb9w

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

sticker.webp

Verevkin пишет

Ну, я ж не шарю во всём этом добре. Дайте просто набор файлов для 117+, готовых к употреблению, пожалуйста.

На предыдущей странице скачайте комплект UCF для [firefox] 116 и сделайте правки для [firefox] 117 из этого поста, затем очистите папку startupCache

Всем здравствовать. Граждане, подскажите плиз рабочий вариант кода для установки не подписанных расширений для FF 102.9 esr

egorsemenov06 пишет

Dumby Посмотрите кнопку toggleRestartlessAddons она не работает после удаления в ней Components.utils.import("resource://gre/modules/Services.jsm");

У вас же вроде [firefox] 116, а эта правка для [firefox] 117+

kokoss пишет

На предыдущей странице скачайте комплект UCF для [firefox] 116

Скачал, извлёк из rar-архива.

kokoss пишет

сделайте правки для [firefox] 117 из этого поста

В посту картинка в base64 - скриншот поиска текста "Services.jsm" в файлах.

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

Я нашёл 4 файла с таким вхождением. Что дальше? Как и где править?

26-08-2023 12:57:13

kokoss пишет

У вас же вроде [firefox] 116, а эта правка для [firefox] 117+

У меня 5 компов, на 2 из них Linux. Именно на них FF обновился до 117 2 дня назад и вылезла текущая проблема.
Пишу я с компа на винде, где FF ещё не обновился, но обновится в ближайшее время. Экспериментирую в виртуалке с линупсом.

Verevkin пишет

Я нашёл 4 файла с таким вхождением. Что дальше? Как и где править?

Просто удалите эти строки -> var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); в этих 4 файлах, и после не забудьте очистить папку startupCache

kokoss пишет

Просто удалите эти строки -> var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); в этих 4 файлах, и после не забудьте очистить папку startupCache

Спасибо, получилось.
----
Скажите, а цвет фона панелей и их габариты можно как-то отрегулировать?

2023-08-26-13-31.png

egorsemenov06 пишет

не работает после удаления в ней Components.utils.import("resource://gre/modules/Services.jsm");

Оно там под условием if(!("Services" in window))
которое в предыдущей строке, так что надо удалить ещё и её.


Иначе это условие перейдёт на код,
следующий за удалённой импорт-строкой, и всё встанет враскоряку.


JKT пишет

рабочий вариант кода для установки

Да нынешний вроде должен работать.

не подписанных расширений

Неподписанное расширение — это "масляное масло".
Подписанным или неподписанным может быть WebExtensions, но не расширение.


Расширения сто лет как не подписывают,
а которые были подписаны ещё тогда, уже давно отвалились.

для FF 102.9 esr

Для ESR 102 код не требуется, достаточно настроек
xpinstall.signatures.required
extensions.experiments.enabled

Dumby пишет

Оно там под условием if(!("Services" in window))которое в предыдущей строке, так что надо удалить ещё и её.Иначе это условие перейдёт на код,следующий за удалённой импорт-строкой, и всё встанет враскоряку.

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

Уф.
У меня на 117 заработало.
Удалил строку
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
во всех файлах.

Странно ночью несколько раз делал тоже самое плюс очистка кэша. И ничего не работало.

Тем не менее всем спасибо!

Verevkin пишет

а цвет фона панелей и их габариты можно как-то отрегулировать?

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

Выделить код

Код:

#ucf-additional-bottom-bar {
    --toolbarbutton-outer-padding: 1px;
    --toolbarbutton-inner-padding: 2px;
    --toolbar-bgcolor: red;
}

Verevkin
Дополню пост kokoss своим со ссылкой на оригинал.

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

Выделить код

Код:

/* Сжать доп.панели https://forum.mozilla-russia.org/viewtopic.php?pid=775867#p775867 */
#ucf-additional-top-bar,
#ucf-additional-bottom-bar {
    --toolbarbutton-outer-padding: 2px !important; /* было 0px */
    --toolbarbutton-inner-padding: 2px !important;
    min-height: 18px !important;
}
:is(#ucf-additional-top-bar,#ucf-additional-bottom-bar) .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}
#ucf-additional-bottom-closebutton {
    padding: 0 !important;
}
#ucf-additional-vertical-bar {
    --toolbarbutton-outer-padding: 0px !important; /* это и есть ширина боковой панели; комбинируется с пар. ниже */
    --toolbarbutton-inner-padding: 3px !important; / расстояние по вертикали между кнопками; комбинируется с предыдущим, лучше 3 или 5 */
    min-width: 18px !important;
}
#ucf-additional-vertical-bar .toolbarbutton-badge {
    margin-inline-end: calc(-1 * (var(--toolbarbutton-outer-padding) + var(--toolbarbutton-inner-padding))) !important;
}

Verevkin пишет

цвет фона панелей

Нашлось такое решение. В стиле: "user_chrome_files\vertical_top_bottom_bar\vertical_top_bottom_bar.css" меняем строку на строку.

Выделить код

Код:

background-color: var(--toolbar-bgcolor, -moz-Dialog) !important;
Выделить код

Код:

background-color: blue !important;

Dumby посмотрите пожалуйста вот этот скрипт на 117.0 перестал работать

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

Выделить код

Код:

(function() {
    if ("OpenBrowserWindow" in window) {
        var str = OpenBrowserWindow.toString(), regx = /defaultArgs\s*=\s*\"about\:privatebrowsing\"\s*\;/g;
        if (!regx.test(str))
            return;
        eval(`OpenBrowserWindow = ${str.replace(regx, 'console.log("load page: " + defaultArgs);')}`);
    }
})();

Кто-нибудь пользуется скроллбаром Aris-t2?
Как настраивать его размеры?

m_nikolay
Этим? Я - нет.
У него был старый, в котором всё настраивалось: ширина, цвет и т.д., но в какой-то момент ([firefox] 113?) сломалось. Можно было поправить, но Aris-t2 этого делать не стал. Убрал старый и выложил новый. Пользуюсь теперь стилем.

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

Выделить код

Код:

/* изменения с 113-й; перестал работать скрипт custom_scrollbars.uc.js
  Здесь стиль https://forum.mozilla-russia.org/viewtopic.php?pid=804939#p804939 */

@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace html url("http://www.w3.org/1999/xhtml");

/* Ширина и наличие кнопок перемотки регулируется через about:config :
 * По умолчанию - 0, MacOs - 1 (без кнопок), GTK - 2 (без кнопок),
 * Android - 3 (без кнопок), Windows 10 - 4, Windows 11 - 5 ***//*
user_pref("widget.non-native-theme.scrollbar.style", 2); себе поставил 3
// Наложенный скролбар + скрытие, через ~3сек неактивности (с метками поиска)
user_pref("ui.useOverlayScrollbars", 1); /* себе параметр не создавал */

scrollbar, scrollcorner, html|select {
    --v-scrollbar-background-color: #E48080 !important; /* rgba(100,100,100,.4) */
    --v-scrollbar-background-color-hover: #FF0000 !important; /* rgba(100,100,100,.5) */
    --v-scrollbar-background-color-active: #0000BB !important; /* rgba(100,100,100,.6) */
    --v-scrollbar-border-radius: 5px !important;
    --v-scrollbar-min-size: 7px !important; /* влияет только на размер кнопок */
    --v-scrollbar-dn-image: url(".././svg/arrow-dn.svg");
    --v-scrollbar-up-image: url(".././svg/arrow-up.svg");
    --v-scrollbar-rit-image: url(".././svg/arrow-rit.svg");
    --v-scrollbar-lft-image: url(".././svg/arrow-lft.svg");
    --v-scrollbar-image-size: 5px !important; /* значок кнопок перемотки, 0 что б скрыть */
}
/* ************************************************ */

/* Скроллбар, dropmarker */
scrollbar {
/*    -moz-appearance: none !important;
    appearance: none !important; /**/
    background: none !important;
    border: none !important;
    padding: 0 !important;
    margin: 0 !important;
}
scrollbar[root="true"] {
    position: relative !important;
    z-index: 2147483647 !important;
}
scrollbar[root="true"][orient="vertical"] {
    margin-left: calc(-1 * var(--v-scrollbar-min-size)) !important;
}
scrollbar[root="true"][orient="horizontal"] {
    margin-top: calc(-1 * var(--v-scrollbar-min-size)) !important;
}
scrollcorner {
    -moz-appearance: none !important;
    appearance: none !important;
    width: var(--v-scrollbar-min-size) !important;
    height: var(--v-scrollbar-min-size) !important;
    background: none !important;
    border: none !important;
}
scrollbar > slider {
    -moz-appearance: none !important;
    appearance: none !important;
    background: #44944A !important; /* Арлекин; было none */
    border: none !important;
}
scrollbar > slider > thumb,
scrollbar > scrollbarbutton,
html|*:not(html|select) > scrollbar > slider > thumb,
html|select > html|button {
    -moz-appearance: none !important;
    appearance: none !important;
    border: none !important;
    border-radius: var(--v-scrollbar-border-radius) !important;
    background-color: var(--v-scrollbar-background-color) !important;
    background-image: none !important;
    background-repeat: no-repeat !important;
    background-position: center !important;
    background-size: var(--v-scrollbar-image-size) !important;
}
scrollbar[orient="vertical"],
scrollbar[orient="vertical"] > slider,
scrollbar[orient="vertical"] > slider > thumb {
    min-width: var(--v-scrollbar-min-size) !important;
    max-width: var(--v-scrollbar-min-size) !important;
}
scrollbar[orient="horizontal"],
scrollbar[orient="horizontal"] > slider,
scrollbar[orient="horizontal"] > slider > thumb {
    min-height: var(--v-scrollbar-min-size) !important;
    max-height: var(--v-scrollbar-min-size) !important;
}
scrollbar > scrollbarbutton {
    min-width: var(--v-scrollbar-min-size) !important;
    min-height: var(--v-scrollbar-min-size) !important;
}
scrollbar > slider > thumb:hover,
scrollbar > scrollbarbutton:not([disabled="true"]):hover {
    background-color: var(--v-scrollbar-background-color-hover) !important;
}
scrollbar > slider > thumb:active,
scrollbar > scrollbarbutton:not([disabled="true"]):active,
html|select > html|button:active {
    background-color: var(--v-scrollbar-background-color-active) !important;
}
html|select > html|button {
    background-image: var(--v-scrollbar-dn-image) !important;
}
scrollbar > scrollbarbutton[type="increment"],
html|select > html|button[orientation="right"] {
    background-image: var(--v-scrollbar-rit-image) !important;
}
scrollbar[orient="vertical"] > scrollbarbutton[type="increment"] {
    background-image: var(--v-scrollbar-dn-image) !important;
}
scrollbar > scrollbarbutton[type="decrement"],
html|select > html|button[orientation="left"] {
    background-image: var(--v-scrollbar-lft-image) !important;
}
scrollbar[orient="vertical"] > scrollbarbutton[type="decrement"] {
    background-image: var(--v-scrollbar-up-image) !important;
}
scrollbar > scrollbarbutton[disabled="true"] {
    opacity: 0.5 !important;
}


P.S. Размеры у Aris-t2, наверное, тоже регулируются параметром widget.non-native-theme.scrollbar.style
И у меня Win 10. Как на других OS не помню.

xrun1
Это кажется css код? Как его прицепить? Импортировать в userChrome или куда?

xrun1 пишет

Размеры у Aris-t2, наверное, тоже регулируются параметром widget.non-native-theme.scrollbar.style

Вот это нужная подсказка, спасибо.

kokoss пишет

очистить папку startupCache

Всё сделал по рекомендациям, но не могу найти папку startupCache.
Где её искать? Без её чистки правки у меня не работают.

kokoss пишет

Просто удалите эти строки -> var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

И ещё: где кроме как в config.js искать эту строку?

stvol пишет

Всё сделал по рекомендациям, но не могу найти папку startupCache.
Где её искать?

В профиле [firefox]

И ещё: где кроме как в config.js искать эту строку?

https://forum.mozilla-russia.org/viewto … 15#p806615

Dumby
А нельзя ли сделать правки для ФФ 117*

29-08-2023 19:50:11

kokoss пишет

В профиле [firefox]

Нет такой.
По второму вопросу вы имеете в виду- "во всех файлах UCF"?


Всё ПОЛУЧИЛОСЬ!!
Строку var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); нашёл только в config.js
а кэш чистил через about:support

m_nikolay пишет

Это кажется css код? Как его прицепить? Импортировать в userChrome или куда?

\chrome\user_chrome_files\custom_scripts\CustomStylesScripts.jsm
вставить в секцию
    stylesall: [ // Для всех документов
строку
{ path: "custom_scrollbars.css", type: "AGENT_SHEET", sheet() { registerSheet(this); }, }, // <-- Скроллбар
Соответственно стиль в кодировке UTF-8 без BOM должен быть, обзываться custom_scrollbars.css и лежать в папке \chrome\user_chrome_files\custom_styles\
И в настройках UCF настройка стилей "Для всех документов" должна стоять.
UPD: Посмотрел скрипт Aris, работает так же, только цвета другие. :)

stvol пишет

По второму вопросу вы имеете в виду- "во всех файлах UCF"?

Нет не во всех, а только в четырёх; помимо файла config.js, в -> options/prefs.js, в CustomStylesScripts.jsm и CustomStylesScriptsChild.jsm

egorsemenov06 пишет

посмотрите пожалуйста вот этот скрипт на 117.0 перестал работать

Да, это Bug 713713 - Clean up OpenBrowserWindow
Может так сойдёт? Код в custom_script.js, не в окно.

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

Выделить код

Код:

(async (bwt, lazy) => {
	var TelemetryStopwatch = lazy = Object.create(null);
	lazy.start = lazy.finish = () => {};
	ChromeUtils.defineLazyGetter(lazy, "BrowserHandler",
		() => Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler)
	);
	ChromeUtils.defineESModuleGetters(lazy, {
		HomePage: "resource:///modules/HomePage.sys.mjs",
		PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
	});
	var bwt = ChromeUtils.importESModule(bwt).BrowserWindowTracker;
	Object.assign(bwt, eval(`({${bwt.openWindow}})`.replace(/\n +if \(!args &.+?}/s, "")));
})("resource:///modules/BrowserWindowTracker.sys.mjs");

Dumby пишет

egorsemenov06 пишетпосмотрите пожалуйста вот этот скрипт на 117.0 перестал работатьДа, это Bug 713713 - Clean up OpenBrowserWindowМожет так сойдёт? Код в custom_script.js, не в окно.скрытый текстВыделить кодКод:(async (bwt, lazy) => {
    var TelemetryStopwatch = lazy = Object.create(null);
    lazy.start = lazy.finish = () => {};
    ChromeUtils.defineLazyGetter(lazy, "BrowserHandler",
        () => Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler)
    );
    ChromeUtils.defineESModuleGetters(lazy, {
        HomePage: "resource:///modules/HomePage.sys.mjs",
        PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs"
    });
    var bwt = ChromeUtils.importESModule(bwt).BrowserWindowTracker;
    Object.assign(bwt, eval(`({${bwt.openWindow}})`.replace(/\n +if \(!args &.+?}/s, "")));
})("resource:///modules/BrowserWindowTracker.sys.mjs");

Огромое Спасибо!!!!!Так хорошо.Заработало

Упс, не туда.

В [firefox] 117 отвалился старый about:config
Там в config.js есть строка

Выделить код

Код:

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

Dumby, что с этим делать?
Add Toolbar Buttons тоже отвалился. Не знаю, в какую тему написать, поэтому сюда. С этим тоже можно что-то сделать?

xrun1
Спасибо за помощь, теперь работают оба варианта :)

xrun1 пишет

Add Toolbar Buttons тоже отвалился. Не знаю, в какую тему написать, поэтому сюда. С этим тоже можно что-то сделать?

Тоже с этим столкнулся. Само расширение есть, а кнопок нет... :(

xrun1 пишет

что с этим делать?

можно так

Выделить код

Код:

var Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

ладно вот add_toolbar_buttons-112.2023.08.23.xpi, надеюсь ничего не упустил...
+

Выделить код

Код:

// br.loadURI(url
br.loadURI(Services.io.newURI(url)

add_toolbar_buttons-112.2023.08.30.xpi

Farby
Ещё я здесь спросил.
Спасибо.

Farby пишет

ладно вот add_toolbar_buttons-112.2023.08.23.xpi, надеюсь ничего не упустил...

Ну вот и кнопки вернулись. Сенькаю! :cool:

p.s. Вот только кнопка about:config при нажатии ЛКМ не срабатывает (все остальные кнопки мыши срабатывают).:(

Black_Monk пишет

Вот только кнопка about:config при нажатии ЛКМ не срабатывает

Можно заменить в parent.js


br.loadURI(url
на
br.fixupAndLoadURIString(url
или на
br.loadURI(Services.io.newURI(url)

Dumby пишет

Можно заменить в parent.js

Выделить код

Код:

// br.loadURI(url
br.loadURI(Services.io.newURI(url)

Спасибо, забрал. Почему-то в консоли ошибок не было или я опять ступил...

С ATB разобрались. А что с этим делать?

Выделить код

Код:

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

aboutconfig.zip

xrun1 пишет

const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

Может так -> const Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
Add, источник...

kokoss
Попробовал, не получилось.
Мне новый вид (относительно уже) не нравится, привык к старому виду about:config.

xrun1 пишет

не получилось

Я тут пробовал сконвертировать jsm в mjs,
возможность импорта jsm'ок ведь выпилят, рано или поздно.
Вот, может это у тебя заведётся.


Там без файла favicon (наверняка уже свой стоит),
и без папки locale (а то на форум не влезет, использовать имеющуюся).


Регистрация about:cfg — соответственно
{ func: 'ChromeUtils.importESModule("chrome://user_chrome_files/content/aboutconfig/UCFAboutConfigFluent.mjs");' },


Текстовая ссылка на страницу загрузки

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

Выделить код

Код:

data:text/html;charset=utf-8,<!DOCTYPE html>%0A<html>%0A%09<head>%0A%09%09<title>aboutconfig-mjs</title>%0A%09%09<meta http-equiv="Content-Type" content="text/html; charset=utf-8">%0A%09</head>%0A%09<body> %0A%09%09<center style="font-family: Verdana; margin-top: 25vh;"><h1><a%0A%09%09%09download="aboutconfig-mjs.zip"%0A%09%09%09href="data:application/x-zip-compressed;base64,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"%0A%09%09>aboutconfig-mjs.zip</a></h1></center>%0A%09</body>%0A</html>

Dumby пишет

Там без файла favicon (наверняка уже свой стоит)

Нет, зачем менять. Зелёная шестерёнка устраивает. :) Спасибо, всё завелось и работает. :beer:

огромная просьба вернуть к жизни расширение на фф 117.0 https://www.upload.ee/files/15636250/lo … 8.xpi.html

sachka
Кажется зто работает

Farby
да, работает. благодарю, выручил

Dumby пишет

возможность импорта jsm'ок ведь выпилят, рано или поздно.

А как jsm в mjs переделывать? Например скрипт сохранения страницы SingleSaveHTML.jsm ?
Чём модуль ES mjs и скрипт jsm отличаются? Нашёл только эту справку.
Скрипт mjs в FireFox 97 будет работать?

Dobrov пишет

А как jsm в mjs переделывать?

Ооо, следуй за ними.

Например скрипт сохранения страницы SingleSaveHTML.jsm ?

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

Выделить код

Код:

export class SingleHTMLChild extends JSWindowActorChild { //класс = name + Child
	receiveMessage() { return htmlAndName(this.contentWindow);}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {esModuleURI: Components.stack.filename},

Чём модуль ES mjs и скрипт jsm отличаются? Нашёл только эту справку.

Ну, ES типа вебский модуль, только без top-level await'а.

Скрипт mjs в FireFox 97 будет работать?

Конечно нет. Даже рядом не стояло.

Раз уж тут jsm-ками озадачились, добавлю, что не работает (последний спойлер AppMenuTbbSaveHTMLChild.jsm).
Подобное работает у меня в кнопке Save, но хочется разнообразия. :)

xrun1
а вот и MJS`ка

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

Выделить код

Код:

// в custom_script.js (async url => ChromeUtils.importESModule(url))( "chrome://user_chrome_files/content/custom_scripts/Actors/AppMenuTbbSaveHTMLChild.mjs");

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

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

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

var htmlAndName = async mainWin => {

	var resolveURL = function (url, base) {
		try {
			return io.newURI(url, null, io.newURI(base)).spec;
		} catch {}
	};
	var getSelWin = function (w) {
		if (w.getSelection().toString()) return w;
		for (var i = 0, f, r; f = w.frames[i]; i++) {
			try {
				if (r = getSelWin(f)) return r;
			} catch(e) {}
		}
	};
	var encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else {
				img = obj;
			};
			if (img.complete) try{
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	};
	var toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	};

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

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild);
	var meta = doc.createElement('meta');
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	var title = doc.getElementsByTagName('title')[0];
	if (title) head.appendChild(title.cloneNode(true));

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

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

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

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

Dumby
Можно ли сделать так, чтобы кнопка about:config из ATB при нажатии ЛКМ открывала страницу about:config в новой вкладке?

qwerty1956 пишет

Можно ли сделать так, чтобы кнопка about:config из ATB при нажатии ЛКМ открывала страницу about:config в новой вкладке?

Откройте настройки ATB,  включайте  -> Открывать в новой вкладке кнопки и ссылки, и жмёте на "Сохранить настройки".

Farby
Не работает - пропал пункт меню в гамбургере.

скрытый текст
Image003_2023-09-01_13-35.png

xrun1 пишет

Не работает

у меня так

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

импорт правильный?
custom_script.js

Выделить код

Код:

// в custom_script.js (async url => ChromeUtils.importESModule(url))( "chrome://user_chrome_files/content/custom_scripts/Actors/AppMenuTbbSaveHTMLChild.mjs");

01-09-2023 14:05:44
или как выше писал:

Dumby пишет

Регистрация about:cfg — соответственно
{ func: 'ChromeUtils.importESModule("chrome://user_chrome_files/content/aboutconfig/UCFAboutConfigFluent.mjs");' },

Farby
Извиняюсь. Просто заменил модуль, а надо было ещё поменять в custom_script.js
ChromeUtils.import
на
ChromeUtils.importESModule
Спасибо.:beer:

Farby пишет

импорт правильный? custom_script.js

custom_script.js лишний этап, можно грузить напрямую с авто-определением JSM<>MJS в CustomStylesScripts.jsm

Выделить код

Код:

jsmImport = (s, e = /\.jsm$/i.test(s) ? "" : "ESModule") => `ChromeUtils.import${e}("chrome://user_chrome_files/content/custom_scripts/${s}")`;
………
	scriptsbackground: [ // В фоне [System Principal]
		{ func: jsmImport("SingleHTML.mjs"), },

Ещё не работает "Экспорт папки в HTML".
Что-то я разошёлся ошибки искать в своём профиле. Больше не буду. :(

kokoss пишет
qwerty1956 пишет

Можно ли сделать так, чтобы кнопка about:config из ATB при нажатии ЛКМ открывала страницу about:config в новой вкладке?

Откройте настройки ATB,  включайте  -> Открывать в новой вкладке кнопки и ссылки, и жмёте на "Сохранить настройки".

Блин, забыл про настройки. Спасибо

xrun1
Наверное можно так...

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

Выделить код

Код:

.
			// fp.init(win, win.PlacesUIUtils.getString("EnterExport"), fp.modeSave);
			fp.init(win, win.PlacesUIUtils.promptLocalization.formatValueSync("places-bookmarks-export"), fp.modeSave);

Farby пишет

Наверное можно так...

Появилась такая фича, как будто в код встроен счетчик количества запусков "Управления закладками", один раз открыл - один пункт, три раза открыл - три пункта.
err.png

fuchsfan

fuchsfan пишет

Появилась такая фича

Это не фича. Здесь, третий абзац объясняется почему.
Проверил у себя с правкой Farby - работает, не двоит и не троит...

LGS пишет

Это не фича. Здесь, третий абзац объясняется почему.

В боковой панели не двоит, двоит только в "управлении закладками". Пробую не в UCF, а c загрузчиком Aris-t2.

fuchsfan пишет

Пробую не в UCF, а c загрузчиком Aris-t2

Возможно, из-за этого и размножается. Если есть желание, проверьте в UCF и custom_script.js. У меня в "Управлении закладками" все нормально.

LGS пишет

проверьте в UCF и custom_script.js. У меня в "Управлении закладками" все нормально.

При активации в CustomStylesScripts.jsm не двоит, при добавлении кода в custom_script.js двоит. При активации в Aris-t2 двоит.

fuchsfan пишет

При активации в CustomStylesScripts.jsm не двоит, при добавлении кода в custom_script.js двоит. При активации в Aris-t2 двоит.

Скажете пожалуйста, а до моего предложения у Вас хоть раз встречалась "Экспорт папки в HTML"?

Farby пишет

Скажете пожалуйста, а до моего предложения у Вас хоть раз встречалась "Экспорт папки в HTML"?

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

А починить можно такую jsm-ку, первый спойлер..?  Или сконвертировать в mjs..? ЛКМ для userChrome.css работает, правая для userContent.css - нет.
Пробовал сам переделать, как здесь Dumby объяснял - результат нулевой.

LGS пишет

А починить можно такую jsm-ку

MJS`ка

Reload user{Chrome, Content}.css

Выделить код

Код:

// в custom_script.js (async url => ChromeUtils.importESModule(url))( "chrome://user_chrome_files/content/custom_scripts/Actors/userContentReloader.mjs");

var name = "UCF_userContentReloader"; /*, EXPORTED_SYMBOLS = [name + "Child"]; */ export {UCF_userContentReloaderChild};
if (typeof Services != "object")
	/* eslint-disable-next-line no-var */
	var Services = globalThis.Services;

var find = function(sheet) {
	return sheet.href == this;
}
var getSheet = (doc, href) =>
	InspectorUtils.getAllStyleSheets(doc).find(find, href);

if (!ChromeUtils.domProcessChild.childID) {
	var noop = () => {};
	ChromeUtils.importESModule("resource:///modules/CustomizableUI.sys.mjs").CustomizableUI.createWidget({
		label: "Reload user{Chrome, Content}.css",
		tooltiptext: "L: Reload userChrome.css\nR: Reload userContent.css",

		id: "ucf-userContentReloader",
		localized: false,
		onCreated(btn) {
			btn._handleClick = this.click;
			btn.oncontextmenu = oncontextmenu;
			btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIpwAACKcBMsYCAwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAMwSURBVEiJ5dbNb5RVFMfxz3mmrYgYE0IN4IaKb4n4UqOwMCYu2JCoQVDiwoUrQ4j0xao7ExITQxRoodEFK1cmRkSE8AeYYKpEFF0YY4SiUSMRQW0sLe0818U8nekwHe3U7vhtnnNOzj3f89znPvderjVFK8npaSWrPSjpElahQzhvyrF4y++t1GqbF7DHOvQLj6Ozrt2EDutYRHAasMK0vXgWWZO0SX/7DlJf6Skp3UWcFeVRV4zG235tCZx6rDPtKLqqAN4XPhROm3DJEiG3LA6aKkZ14jUSKaOd1Gsco6Qz8vRiDDvTFJz63S13AjdVAg7JvBxDzs2RfrE2MP9ijolZijvJXo/h8pmmb5z6LZf7qICWJQNxwP5mM1OnzNdy03V1U0oi2x77y+/Wp16t3CDWgvDKvKGIQZepfO+iWBIpKG9s7HGW0k7dKgsJjsSQffOF1or4srAuS04U9jOp14amYGGgiF2Re6llaEWnMSbPNyl5DtNFQzvnBKdd2oRNhXt4ZvW1rCwfkbInY9jHMeiscBiEJ9Lz2mfSaovgovXC8sI7siAoYsgnMy8JkmPYhhtdbwOV6a9NdVhT69qphYIblPl0FuO2WrimVVVrcu7dZkFqd75q525uBCelqj0tXzRwvSYbwWZ1doOVi4Yad0vVDj81gjPfV+2y9YsGLnlgFrj6p9TA405irPAemwmn/raNqc/D/wO9uXj+bMhXDeDihDledLYt9VmTektb5flxebYgcOpx+yzw0aic3vXgSqY9hdUuOUx6T+WWcX/LUELYh3ZMyqq1G8FxwCl8ULjdVFd6d6tgPQbMfLIwHIPONgVXWs1OFm3Mjt6Rdlg2X2bq0yfsLtzP/OHVq3PqzuPUm/Vj9xz9ZDrcg5F/Bb6gS8mbkq1F6EdlW+IdE03Baae1pEeJb1SuO0vrMvPsPvKRArDaChf8qWRal/BQccBsxnXFiBFTtjS7czW93qYdVurQJZW6SLeK+DaGyofSLh0uGUNHk6F/SfZI3ojh2k41b3DThnrdS+1/nAnjcxzR5mDsdeG/6szrXl2nJX4w4RFJJ3LhF7lzMey3lmtdU/oHaoj4Y/PDRWgAAAAASUVORK5CYII=");
		},
		get click() {
			var {file, spec} = getURI("hrome");
			var chromeSheet = getSheet(Services.wm.getMostRecentWindow(null).document, spec);
			delete this.click;
			return this.click = !chromeSheet ? noop : function() {
				var win = this.ownerGlobal;
				if (win.event?.detail < 2 && file.exists())
					reload(chromeSheet),
					win.setTimeout(restyle, 50);
			}
		}
	});
	var getURI = sub => {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append(`userC${sub}.css`);
		return Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL);
	}
	var oncontextmenu = e => e.ctrlKey || e.shiftKey || e.detail != 1 || contextmenu(e);

	var contextmenu = e => {
		var {file, spec} = getURI("ontent");
		var wb = Services.appShell.createWindowlessBrowser();
		var contentSheet = getSheet(wb.document, spec);
		wb.close();

		if (!contentSheet) return oncontextmenu = contextmenu = noop;

		ChromeUtils.registerProcessActor(name, {child: {esModuleURI: Components.stack.filename}});
		(contextmenu = async e => {
			if (!file.exists()) return;
			e.preventDefault();
			var data = await reload(contentSheet, Object.create(null));
			if (data) for(var p in data) {
				for(var dp of ChromeUtils.getAllDOMProcesses())
					dp.remoteType && await dp.getActor(name).sendQuery(spec, data);
				restyle();
				return;
			}
		})(e);
	}

	var restyle = () => {
		var subst = "u_css_reloader_restyle_substitution";
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		rph.setSubstitution(subst, Services.io.newURI("data:text/css,:root{}"));

		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		(restyle = () => {
			sss.loadAndRegisterSheet(...args);
			sss.unregisterSheet(...args);
		})();
	}
	var reload = async (sheet, obj) => {
		try {var style = await (await fetch(sheet.href)).text();}
		catch {return obj;}

		InspectorUtils.parseStyleSheet(sheet, style);
		if (obj) obj[sheet.href] = style;
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& await reload(rule.styleSheet, obj);
		}
		return obj;
	}
}
else var UCF_userContentReloaderChild = class extends JSProcessActorChild {
	receiveMessage(msg) {
		var {sheet} = this;
		if (!sheet) {
			var en = Services.ww.getWindowEnumerator(null);
			if (en.hasMoreElements()) sheet =
				this.sheet = getSheet(en.getNext().document, msg.name);
		}
		sheet && this.parse(sheet, msg.data);
	}
	parse(sheet, data) {
		var style = data[sheet.href];
		if (!style) return;

		InspectorUtils.parseStyleSheet(sheet, style);
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& this.parse(rule.styleSheet, data);
		}
	}
}

Farby, благодарю, заработало. Вспомнил, что не на все правки в userContent.css "на лету" ПКМ реагирует, все равно приходится перезагружать. А я как раз на таком стиле и проверял. Догадался на других проверить - срабатывает.

Farby пишет

Наверное можно так...

Спасибо, работает. :beer:

Dumby, вы не могли бы написать скрипт, который убирает фокус из строки поиска на страницах about:config и about:cfg..?
Нечто подобное вы делали здесь, только там нужно было убрать фокус из адресной строки и поместить его в строку поиска about:home, а мне надо убрать фокус из строки поиска about:config, about:cfg аналогично щелчку мыши вне строки поиска.

LGS пишет

скрипт, который убирает фокус из строки поиска на страницах about:config и about:cfg..?

Хмм, убирает когда?
Если при загрузке, то, может быть, создать blur.js

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

Выделить код

Код:

location.pathname == "config"
	? document.getElementById("about-config-search").blur()
	: textbox.addEventListener("focus", e => e.target.blur(), {once: true});


И прописать в CustomStylesScriptsChild.jsm, в массив DOMContentLoaded
{ path: "blur.js", urlregxp: /^about:c(?:f|onfi)g(?:\?.+)?$/ },


Ну, и убедиться, что в настройках UCF стоит галка
[✔] Включить стили и скрипты для контента [about:, chrome:, moz-extension:]

Dumby пишет

Ну, и убедиться, что в настройках UCF стоит галка
[✔] Включить стили и скрипты для контента [about:, chrome:, moz-extension:]

Ценное замечание, так бы я долго проверял почему не работает. Но теперь фоновая картинка бесит: custom_styles\png\background.png. Как ее культурно отключить, кроме переименования/удаления..?
Вроде все внутренности UCF проверил, но что-то ничего подходящего не нашел. Подозреваю, что в самом CustomStylesScriptsChild.jsm прописано как картинка подтягивается, но где именно - не пойму.

Кажется разобрался, все просто. Надо в CustomStylesScriptsChild.jsm закомментировать строку { path: "common.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, },

на Firefox 117 диалог настроек UCF не запоминает ничего - можно установить флажки, закрыть настройки UCF.
Снова открываем диалог, где все флажки сняты. Cтавил со всеми нужными правками, проверял на Windows 10 на пустом профиле.
Не работают все команды этого диалога: "Перезапустить*" и прочие… Может для чистого профиля с UCF что-то в about:config надо добавлять?
"toolkit.legacyUserProfileCustomizations.stylesheets" установлен в true.


Вот архив изменённого UCF – у кого работает UCF на FF 117, подскажите, где ошибка в правленных файлах???

Dobrov пишет

у кого работает UCF на FF 117, подскажите

у меня его нет, но можно начать с

chrome\user_chrome_files\options\prefs.js

Выделить код

Код:

// var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");

Farby - спасибо! убрал забытый var {Services}… в user_chrome_files/options/prefs.js, всё заработало.


to_Alls сделал версию UCF, совместимую с Firefox 78-116, папки Firefox, chrome/user_chrome_files


не знаю, как исправить для Firefox 117 — в нём не подключаются стили и скрипты…

Оказывается все итак работает. По сообщению выше, думал есть какие-то проблемы в 117.

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

rubel
Эта работает

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

Выделить код

Код:

// Скрипт создает подмножество кнопок для запуска приложений, открытия папок и файлов
// Кнопка 1 (Открыть F:\\Firefox Backup)
try {
    CustomizableUI.createWidget({
        id: "add-openfolder1-app",
        label: "Открыть папку 1",
        tooltiptext: "Открыть F:\\Firefox Backup",
        defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated: btn => btn.image = "file:///D:/Portable Files/Firefox Portable/Profiles/Chrome/icons/folder1.png",
        onCommand: function(event) {
            var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
            file.initWithPath("F:\\Firefox Backup");
            if (file.exists()) file.launch();
        }
    });
} catch(e) {}

// Кнопка 2 (повторяем код с другим путем)

// Следующая кнопка...

fuchsfan
Как Вы её подключаете ?

rubel пишет

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

Да вроде работает, какая версия UCF ?

kokoss
UCF для FF 115-116 с правками от Dumby.
Как и куда подключать ?

rubel пишет

Как Вы её подключаете ?

Никаких особенностей, в userChrome.js, если Aris-t2, или в CustomStylesScripts.jsm, если UCF.

fuchsfan
Создал чистый профиль с чистым user_chrome_files из VitaliyVstyle.github.io-master версия, дата г-м-д: 2021-9-23.
Создал файл WinFolders.js с Вашим кодом, поместил в chrome\user_chrome_files\custom_scripts\WinFolders.js
В Файле CustomStylesScripts.jsm прописал так

Выделить код

Код:

scriptsbackground: [ // В фоне [System Principal]
	    { path: "WinFolders.js", },
            { path: "custom_script.js", },
    ],

Удалил startupCache, перезапускаю и никаких кнопок нигде не появляется, где моя ошибка ?

rubel пишет

В Файле CustomStylesScripts.jsm прописал так

Практически все сторонние скрипты прописаны в первой сверху секции load

Выделить код

Код:

load: [ // По событию "load"
            { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
         // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
            { path: "favicon_in_urlbar.js", ucfobj: true, },
            { path: "ucf-mem-indicator_simple.uc.js", ucfobj: false, },

fuchsfan
Сделал

Выделить код

Код:

load: [ // По событию "load"
            // { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
			 { path: "WinFolders.js", ucfobj: true, },
			 { path: "favicon_in_urlbar.js", ucfobj: true, },
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
        ],

Все равно кнопки нет, а вот иконка сайта в адресной строке появилась.
Может дадите папку chrome, попробую заменить.

В настройках панели инструментов смотрели? В гл. окне браузера мышкой медленно поводите там, где могла бы быть кнопка, может, кнопка есть, но без иконки, под мышкой это проявится. Я отошел от ucf, но смогу проверить ваш WinFolders.js на другом компе, если выложите код.

rubel пишет

Как и куда подключать ?

Я добавляю в файл -> custom_script.js
2023-09-22_102030.png

rubel пишет

UCF для FF 115-116 с правками от Dumby

Я спрашивал про версию UCF, а не про сделанные правки, кстати про правку для [firefox] 116 я в курсе, а что за правка для [firefox] 115 ?

fuchsfan
Да, нашлась кнопка, но без иконки её не видно совсем.
57ad6731ba2b9982587c75fcbdeed577.png
Первая директория работает открывается, а вот вторая не появляется совсем.
Вот мой WinFolders.js

Выделить код

Код:

// Скрипт создает подмножество кнопок для запуска приложений, открытия папок и файлов
// Кнопка 1 (Открыть F:\\Firefox Backup)
try {
    CustomizableUI.createWidget({
        id: "add-openfolder1-app",
        label: "Открыть папку 1",
        tooltiptext: "Открыть D:\\1",
        defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated: btn => btn.image = "file:///D:/Portable Files/Firefox Portable/Profiles/Chrome/icons/folder1.png",
        onCommand: function(event) {
            var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
            file.initWithPath("D:\\1");
            if (file.exists()) file.launch();
        }
    });
} catch(e) {}

// Кнопка 2 (повторяем код с другим путем)
try {
    CustomizableUI.createWidget({
        id: "add-openfolder1-app",
        label: "Открыть папку 2",
        tooltiptext: "Открыть D:\\Downloads",
        defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated: btn => btn.image = "file:///D:/Portable Files/Firefox Portable/Profiles/Chrome/icons/folder1.png",
        onCommand: function(event) {
            var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
            file.initWithPath("D:\\Downloads");
            if (file.exists()) file.launch();
        }
    });
} catch(e) {}
// Следующая кнопка...

Что не правильно ?

rubel
Просто к сведению, существует расширение Async Run Applications 2021.9.7 by VitaliyV аналогичного функционала, создает кнопку с выпадающим меню, в котором пользовательские кнопки. Давненько не использовал.

22-09-2023 09:56:48
Ага, увидел, проверю.

22-09-2023 10:01:00

Выделить код

Код:

onCreated: btn => btn.image = "file:///D:/Portable Files/Firefox Portable/Profiles/Chrome/icons/folder1.png",

Измените путь на свой, в папке Chrome создайте папку icons, в нее положите иконку 16х16, 24х24 пикселя png, jpg с любым именем, в коде исправьте имя на свое. И иконка появится.

22-09-2023 10:03:41
Кнопки не должны иметь одинаковый ID, во втором коде измените ID кнопки на другой произвольный, хотя бы цифру 2 вместо 1

Выделить код

Код:

id: "add-openfolder1-app",

22-09-2023 10:11:12

rubel пишет

tooltiptext: "Открыть D:\\Downloads",

В этой строке измените всплывающую подсказку на свою

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

22-09-2023 10:40:40
kokoss
Спасибо за скриншот. :beer: Нашел я эту кнопку невзрачную в настройках панели инструментов.
2a82117721666a6600498da51b4051f8.png 
Всем спасибо за внимание.

rubel пишет

кнопку невзрачную

Так можете заменить на любую свою взрачную, тем более. что эта ассоциируется с "сохранить" (стрелка вовнутрь), а не с "открыть" (стрелка наружу).

fuchsfan
Эта кнопка была невзрачная от автора и она затерялась  в настройках панели инструментов. А теперь конечно могу её сделать на свой вкус. :)

fuchsfan, а чтобы кнопка открывала папку chrome в профиле, какой путь указать? Без всяких проверок на существование папки

Dumby
Вроде на форуме нет темы по скриптам, подключаемых по методам
метод Aris-t2   метод xiaoxiaoflood   метод Endor8
Может нужно создать тему по этим методам.
Поэтому попрошу Вас здесь посмотреть скрипт ucf-mem-indicator.js
Он перестал работать в 115. Применяю метод Endor8 для загрузки скриптов.

скрытый текст
ucf-mem-indicator.js

Выделить код

Код:

(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}" tooltiptext="${
        "ЛКМ: Минимизировать потребление памяти&#xA;ПКМ: about:performance&#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);
      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" : "performance"
    }`);
  },
  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");

rubel пишет

Вроде на форуме нет темы по скриптам, подключаемых по методам Aris-t2

Насколько я понимаю, метод Aris-t2 не позволяет подключать скрипты «В фоне [System Principal]», поэтому UCF удобней и полезней…
кроме того, в подключении скриптов методом Aris-t2 нет ничего сложного.

6e73epo пишет

а чтобы кнопка открывала папку chrome в профиле, какой путь указать?

Указать реальный путь к папке Chrome.

Выделить код

Код:

file.initWithPath("D:\\Portable Files\\Firefox Portable\\Profiles\\Chrome");
6e73epo пишет

Без всяких проверок на существование папки

Если браузер перемещен в другую директорию, чтобы скрипт сам определял путь к папке Chrome, такого не знаю, но есть кнопка для папки Profiles, а там останется один клик. И есть скрипт сложнее и побольше, который путь ко всем папкам Firefox и файлам userChrome.css, user.js определяет сам.

fuchsfan, с пробелами не тестировал, у меня путь без пробелов. Завелось так

Выделить код

Код:

onCommand: function(event) {
	Services.dirsvc.get("UChrm", Ci.nsIFile).launch();
}
rubel пишет

метод Aris-t2   метод xiaoxiaoflood   метод Endor8
Может нужно создать тему по этим методам.

Зато есть тема под названием userChrome.js https://forum.mozilla-russia.org/viewtopic.php?id=77354 и вашему посту как раз в ней место, а не в теме по ucf.

Dobrov пишет

кроме того, в подключении скриптов методом Aris-t2 нет ничего сложного.

Да и методом UCF тоже ничего сложного, когда его освоишь.
Дело только в том, что не все скрипты, работающие в UCF, работают методом Aris-t2.
Вот и хотелось бы их подправить по возможности. :/
fuchsfan
Запостил в той теме свой вопрос.

rubel пишет

UCF для FF 115-116 с правками от Dumby. Как и куда подключать ?

ответ Всем… По этой инструкции. или читаем Шапку.
исправил версию UCF, совместимую с Firefox от 78 до 117, всё в папках Firefox, chrome/user_chrome_files

Может кому пригодится, спасибо dobrov. Кстати, тут есть и хоткей, и кнопки-папки, да много чего. Удобная штука, советую) По желанию, можно подвести под личные потребности. Не пропускает в тексте, скорее всего из-за каких-то символов... пришлось так. Вставить в адресную строку.
А да, настроен немного под себя. Если что поменяйте.

Выделить код

Код:

data:text/plain;charset=UTF-8;base64,KGFzeW5jKEZmLHRFeHApPT57IC8vIGhvb2sgTW91c2VLZXlzIMKpIER1bWJ5LCBtb2QgMi4wIERvYnJvdiDQutC90L7Qv9C60Lgg0LrQvtC80LDQvdC00Ysg0YHQutC40L3igKYKVGFnPXsKCiJkb3dubG9hZHMtYnV0dG9uIjoneycrIC8v0L3Rg9C20LXQvSBTaW5nbGVIVE1MLmpzbSwg0LfQsNCy0LjRgdC40LzRi9C5IHVjZl9RdWlja1RvZ2dsZQpg4peJ77yaICAgIOKsh++4jmZvbGRlciBbRG93bmxvYWRzXQril6jvvJogICAgICAgICAgIOKenFNhdmUgUGFnZQril4nvvJogICAgICAgICAgIG9uIFBob3RvIOKenFNhdmXvuLBgKyAvL9C00LXQudGB0YLQstC40Y8g0LrQu9Cw0LLQuNGIfNC80YvRiNC4LCDQv9C+0LTRgdC60LDQt9C60LgKYOKXpy1sb25n77yaIOKsh++4jmZvbGRlciBbRG93bmxvYWRzXVxuCuKXqO+8miAgICAgICAgICAgICAgIChBbHQrUykg4p6cU2F2ZQogICAgICAgICAgICAgICB0byBTaW5nbGVIVE1MIGFsbCAvaGlnaGxpZ2h0ZWQK4peJ77yaICAgICAgICAgICAgICAgJHtGZi5DdHIoIlN1cGVyIiwiQ3RybCtTaGlmdCIpfStTIOKenGFzIFRleHRcbgril6ctZG91Ymxl77yaIG9uIFBob3RvOiBmaW5kIFNpbWlsYXIK4penK1NoaWZ077yaICAgIEdyYXBoaWNzIG9uL29mZgril6crQWx077yaICAgICAgIFNlbGVjdGlvbiBkaWFsb2cgb24vb2ZmfWAsCgoiUGFuZWxVSS1tZW51LWJ1dHRvbiI6IC8v0YLQtdC60YHRgiDQn9GA0L7RgdGC0L7QuSDRgNC10LbQuNC877iw0K3QutGB0L/QtdGA0YIKYOKXp++8miAgICAgICAgICAgICAgICBtZW51IEZpcmVmb3ggJHtGZi52ZXJ9e1xu77iwCuKXpytTaGlmdO+8miAgICAg4p6/aGVscFxufQril6ctbG9uZ++8miAgICAgICDinJUgQ2xvc2UgYnJvd3NlclxuYCArYOKXpytBbHTvvJril4nvvJog4p2QTWF4aW1pemXilIJSZXN0b3JlCgril6jvvJogICAgICAgICAgICAgICAg4oeyTWluaW1pemUge++4sGAgLyrRjdC60YHQv9C10YDRgiovKyBgCuKXqCtBbHTvvJogICAgICAgICBhYm91dDpzdXBwb3J0fQrDmC1zY3JvbGzvvJog4oaRICDipL4gUmV0dXJuIHRhYmAsCgoiem9vbXBhZ2Utd2VfZHctZGV2LWJyb3dzZXItYWN0aW9uIjoKYFxuXG7il4krQWx077yaICAgIGNsb3NlIHRhYnMgb24gdGhlIHJpZ2h0CuKXiStTaGlmdO+8miBjbG9zZSBsZWZ0YCwKCiJ0YWJzLW5ld3RhYi1idXR0b24iOgpg4peo77yaICAgICDQstC10YDQvdGD0YLRjCDQstC60LvQsNC00LrRgwril6crQWx077yaINC+0YHRgtCw0LLQuNGC0YwgMSDRgtC10LrRg9GJ0YPRjmAsCgoiaWRlbnRpdHktYm94IjogLy9kZWJ1ZyDimJEg0LrQvtC80LDQvdC00Yt80LTQtdC50YHRgtCy0LjRjyDQsiDQutC+0L3RgdC+0LvRjApg4peo77yaICAgICAgICAgICAgICAgIENvcHkgYWRkcmVzcyB0byBjbGlwYm9hcmQK4penK0FsdO+8muKXie+8miBQcm94eSBzZXR0aW5ncwp777iwXG7il6crU2hpZnTvvJogICAgIG1lZGlhVGFifQrDmC1zY3JvbGzvvJogICAgIEJyaWdodG5lc3M6IGAsCgoiZmF2ZGlycy1idXR0b24iOiAvL9Cx0L7QutC+0LLQsNGPINC/0LDQvdC10LvRjCDQvtGC0LrRgNGL0YLQsDogZGVidWcg0JLQutC7CmDil6fvvJogICAgICAgICAgICAg4piFQm9va21hcmtzCuKXqO+8miAgICAgICAgICAgICDin7NIaXN0b3J5CuKXpytBbHTvvJogICAgICBGRgril6grQWx077yaICAgICAgSG9tZSBmb2xkZXJcbgril4nvvJogICAgICAgICAgICAg4qyH77iORG93bmxvYWRzCuKXiStBbHTvvJogICAgICBVc2VyQ2hyb21lRmlsZXNcbgrDmC1zY3JvbGzvvJogICDihpEgdXNlcl9jaHJvbWVfZmlsZXMKICAgICAgICAgICAgICAgICAgICDipL4gY3VzdG9tX3NjcmlwdHMK4penK1NoaWZ077yaICAgJUgudHh0CuKXqCtTaGlmdO+8miAgIHVjZl9ob29rQ2xpY2tzLmpzXG5cbgpNYWduaWZ5JkVtb2ppVzEwKFdpbisuKXxjaGFybWFwLG9zayxzbmR2b2xcbkFsdCtEICBBbHQr4oaQ4oaSL8OYXG5DdHJsK0IvSCAgICAgICAgICAgICAgLUJvb2ttYXJrcy9IICAoQWx0K0IvUylcbkN0cmwrRSAgICAgICAgICAgICAgICAgICAtU2VhcmNoXG5DdHJsK1NoaWZ0K1EvVyAtQ2xvc2VGRlxuQ3RybCtVICAgICAgICAgICAgICAgICAgLVBhZ2VTb3VyY2VcbkN0cmwrTyAgICAgICAgICAgICAgICAgIC1PcGVuIGEgZmlsZVxuQ3RybCtTaGlmdCtTICAgICAgICAtU2NyZWVuc2hvdFxuQ3RybCtBbHQrU2hpZnQrSSAtQnJvd3NlclRvb2xib3hcblxuQ3RybCtTaGlmdCtPL0ggQ3RybCtCL0ggQWx0K0IvU1xuQ3RybCtTaGlmdCtK4pWRQ3RybCtTaGlmdCtEZWzilZFDdHJsK1NoaWZ0K0RcbkN0cmwrU2hpZnQrQuKVkUN0cmwrSuKVkVNoaWZ0K8OY4pWRQ3RybCtTaGlmdCtFIEYxMmAsCgoiVG9nZ2xlQnV0dG9uIjoKYOKXpy1sb25n77yaICAg0LIg0YHRgtGA0L7QutC1INC80LXQvdGOLWFib3V0OmNvbmZpZwril6grQWx077yaICAgICDQv9C+0YHQuy4g0LfQsNC60LvQsNC00LrQsCDQvNC10L3Rjgril6fvvJogICAgICAgICAgICAtwqvQltGD0YDQvdCw0LvCu1xuCuKXqO+8miAgICAgICAgICAgINCc0LXQvdGOINCx0YvRgdGC0YDRi9GFINC90LDRgdGC0YDQvtC10LoK4peJ77yaICAgICAgICAgICAgLeKKntCd0L7QstCw0Y8g0LLQutC70LDQtNC60LAgKCR7RmYuQ3RyKCl9VClcbgril6crQWx077yaICAgICAt0JHQuNCx0LvQuNC+0YLQtdC60LAsINCW0YPRgNC90LDQuyB777iwCuKXpy1sb25n77yaICAg0J/QuNC/0LXRgtC60LAg0YbQstC10YLQsCwg0LvQuNC90LfQsFxuCuKXpytTaGlmdO+8miAg0LzQsNGB0YjRgtCw0LEg0KLQtdC60YHRgiAvINCS0YHRkX0Kw5gtc2Nyb2xs77yaICDQvNCw0YHRiNGC0LDQsSDQodGC0YDQsNC90LjRhtGLIMKxYCwKCiJSZWFkZXJWaWV3IjoKYNCa0LvQuNC6INC80YvRiNC4CdCn0YLQtdC90LjQtSDQsiBSZWFkZXJWaWV3CtCa0L7Qu9GR0YHQuNC60L4J0J/RgNC+0YHRgtC+0Lkg0YDQtdC20LjQvCDRh9GC0LXQvdC40Y9cbmAsCiJyZWFkZXItbW9kZS1idXR0b24iOgpg4peo77yaICAgICAgICAgINCQ0LTQsNC/0YLQuNCy0L3Ri9C5INC00LjQt9Cw0LnQvQril6grQWx077yaICDQktGL0LHQvtGAINGH0LDRgdGC0Lgg0YHRgtGA0LDQvdC40YbRiyBcbgrDmC1zY3LvvJogICDQr9GA0LrQvtGB0YLRjCDRgdGC0YDQsNC90LjRhjogYCwKCiJwcmludC1idXR0b24iOgpg0KDQsNGB0L/QtdGH0LDRgtCw0YLRjCDRgdGC0YDQsNC90LjRhtGDICgke0ZmLkN0cigpfVApXG4K4peo77yaICAgICAgICAgICAg0LHRi9GB0YLRgNCw0Y8g0J/QtdGH0LDRgtGMCuKXie+8miAgICAgICAgICAgINGA0LXQttC40Lwg0LrQvdC+0L/QvtC6CuKXpy1sb25n77yaICDinr/QutGA0LDRgtC60LDRjyDRgdC/0YDQsNCy0LrQsGAsCgoicmVsb2FkLWJ1dHRvbiI6CmDil6fvvJogICAgICAgICAgICDin7Mgbm8gY2FjaGUK4penLWxvbmfvvJogIOKtriBhbGwgdGFic2AsCgoidHJhY2tpbmctcHJvdGVjdGlvbi1pY29uLWNvbnRhaW5lciI6CmDil6fvvJogIFNpdGUgc2VjdXJpdHkgZGV0YWlscwril6jvvJogIExvZ2lucyBhbmQgUGFzc3dvcmRzCuKXie+8miAgYWJvdXQ6cHJvY2Vzc2VzCnvvuLBcbuKXpy1sb25n77yaICDih4YgV2ViLWZvbnRzCuKXpytTaGlmdO+8miAgYWJvdXQ6c2VydmljZXdvcmtlcnN9YCwKCiJ3aGVlbC1zdG9wIjoKYOKXie+8miAgICAgICAgICAgIOKclSBhbGwgcmVsb2Fkcwp777iw4peoLWxvbmfvvJogICBBbnRpemFwcmV0IOKHhiBObyBwcm94eX1cbmAsCgoidGl0bGViYXItYnV0dG9uLnRpdGxlYmFyLWNsb3NlIjogLy/QutGA0L7QvNC1IFdpbmRvd3MKYNCX0LDQutGA0YvRgtGMIEZpcmVmb3ggJHtGZi52ZXJ9XG4K4peJ77yaINCy0LXRgNC90YPRgtGMINCy0LrQu9Cw0LTQutGDCuKXpy1sb25n77yaIOKev9C60YDQsNGC0LrQsNGPINGB0L/RgNCw0LLQutCwCuKXqO+8miDih7LQodCy0LXRgNC90YPRgtGMYCwKCiJBdHRyaWJ1dGVzLUluc3BlY3RvciI6IC8vYXR0cnNJbnNwZWN0b3IuanMgfOKXqO+8miAgICAgICAgICAgINCd0LDRgdGC0YDQvtC50LrQuCBVc2VyQ2hyb21lRmlsZXN8QWx0ICsgeCAgICAgICDQt9Cw0L/Rg9GB0YLQuNGC0Ywg0YHQutGA0LjQv9GCIFVzZXIuanMKYOKXp++8miAgICAgICAgICAgIEF0dHJpYnV0ZXMtSW5zcGVjdG9yCuKXpy1sb25n77yaICDRgdC80LXQvdCwINGA0LXQttC40LzQsCDQutC90L7Qv9C+0LoK4penK0FsdO+8miAgICDQmNC90YHRgtGA0YPQvNC10L3RgtGLINCx0YDQsNGD0LfQtdGA0LAK4penK1NoaWZ077yaINCd0LDRgdGC0YDQvtC50LrQuCDQv9GA0L7RhNCw0LnQu9C10YDQsGAsCgoiQCI6IC8v0YLQtdC60YHRgtGLCmDQntGH0LjRgdGC0LjRgtGMINC/0LDQvdC10LvRjCDQutC+0LvRkdGB0LjQutC+0Lwg0LzRi9GI0Lh80JfQsNC/0YDQtdGJ0ZHQvdC90YvQtSDRgdCw0LnRgtGLINGH0LXRgNC10Lcg0JDQvdGC0LjQl9Cw0L/RgNC10YJ84piAIEJyaWdodG5lc3M6IHzwn5K+INC60Y3RiCwg0LTQsNC90L3Ri9C1INGB0LDQudGC0L7Qsiwg0LrRg9C60Lgg0LfQsNC90LjQvNCw0Y7RgiB80JfQsNGF0LLQsNGCINGG0LLQtdGC0LAg0LIg0JHRg9GE0LXRgCDQvtCx0LzQtdC90LAgKNC60YPRgNGB0L7RgCDQtNCy0LjQs9Cw0LXRgiDQvdCwIDEg0YLQvtGH0LrRgyl84pqh77iPINCX0LDQv9GA0LXRidC10L3QviDRgdC+0YXRgNCw0L3Rj9GC0Ywg0LvQvtCz0LjQvdGLINC4INC/0LDRgNC+0LvQuHzil6cg0LTQtdGA0LbQsNGC0Yw6IGFib3V0OmNvbmZpZywg4peoINC/0YAuINC60LvQuNC6INCh0LHRgNC+0YEsIOKfsyDQntCx0L3QvtCy0LjRgtGMLCDihq8g0J/QtdGA0LXQt9Cw0L/Rg9GB0Lp80JTQvtC70LPQuNC5INC60LvQuNC6INCyINGB0YLRgNC+0LrQtSDQvNC10L3Rjjog0J/RgNCw0LLQuNGC0Ywg0L7Qv9GG0LjRjiDilIIg0JrQvtC70ZHRgdC40LrQvjog0KHQtdGA0LLQuNGB0Yt80J7RiNC40LHQutCwINGB0LrRgNC40L/RgtCwIHzihq8g0J3QtSDQt9Cw0L/QvtC80LjQvdCw0YLRjCDQuNGB0YLQvtGA0LjRjiDQv9C+0YHQtdGJ0LXQvdC40Ll84oavINCj0LTQsNC70Y/RgtGMINC40YHRgtC+0YDQuNGOINC/0L7RgdC10YnQtdC90LjQuSwg0LfQsNC60YDRi9Cy0LDRjyDQsdGA0LDRg9C30LXRgHzQv9C10YDQuNC+0LQg0YHQvtGF0YDQsNC90LXQvdC40Y8g0YHQtdGB0YHQuNC5INC80LXQvdGP0LXRgtGB0Y8g0LIg0LzQtdC90Y4g0LrQvdC+0L/QutC4IMKr0JbRg9GA0L3QsNC7wrt8U2luZ2xlRmlsZSAoQWx0K0N0cmwrUylcbtCh0L7RhdGA0LDQvdC40YLRjCDRgdGC0YDQsNC90LjRhtGDINCyINC10LTQuNC90YvQuSBIdG1sfFZpZGVvIERvd25sb2FkSGVscGVyXG7QodC60LDRh9C40LLQsNC90LjQtSDQv9GA0L7QuNCz0YDRi9Cy0LDQtdC80L7Qs9C+INCy0LjQtNC10L58XG5cbuKXqO+8miDQndCw0YHRgtGA0L7QudC60LggVXNlckNocm9tZUZpbGVzXG7il4nvvJog0J/QtdGA0LXQt9Cw0L/Rg9GB0YLQuNGC0YwsINGD0LTQsNC70LjQsiDQutGN0YhcbkFsdCArIHgJ0LfQsNC/0YPRgdGC0LjRgtGMINGB0LrRgNC40L/RgiBVc2VyLmpzYH07IHZhciBUID0gVGFnWyJAIl0uc3BsaXQoJ3wnKSwgQiA9IFsuLi5PYmplY3Qua2V5cyhUYWcpLCJ2aWV3SGlzdG9yeVNpZGViYXIiXTsKCihhc3luYyAoaWQpID0+IEN1c3RvbWl6YWJsZVVJLmNyZWF0ZVdpZGdldCh7bGFiZWw6YNCf0LDQvdC10LvQuCwg0J/QsNC/0LrQuGAsaWQ6IGlkLHRvb2x0aXB0ZXh0OiBUYWdbaWRdLApvbkNyZWF0ZWQoYnRuKXtidG4uc3R5bGUuc2V0UHJvcGVydHkoImxpc3Qtc3R5bGUtaW1hZ2UiLCJ1cmwoY2hyb21lOi8vZGV2dG9vbHMvc2tpbi9pbWFnZXMvZm9sZGVyLnN2ZykiKTt9fSkKKShCWzVdKTsgLy8g0LrQvdC+0L/QutC4LdC/0L7QtNGB0LrQsNC30LrQuC3QutC70LjQutC4CgprbGFCYSA9IHsgLy/Qv9C10YDQtdGF0LLQsNGCLdC60LvQsNCy0LjRiCBLZXlBW19tb2RdW19PU10oZSx0KXvQutC+0LR9INC4IEtleUI6ICJLZXlBIgoJS2V5WF8xKGUsdCkge3VzZXJqcyhlKX0sICAgICAgLy8gQWx0K1gg0LfQsNC/0YPRgdC6INCy0L3QtdGI0L3QtdCz0L4gSlMt0LrQvtC00LAKCUtleVNfNigpICAgIHtzYXZlU2VsVG9UeHQoKX0sIC8qIEN0cmwrU2hpZnQrUyAqLyBLZXlTXzE1OiAiS2V5U182IiwKCUtleVNfMShlLHQpIHtzYXZlKCl9LCAgICAgICAgIC8vQWx0K1MgfCBlOiBFdmVudCwgdDogZ0Jyb3dzZXIuc2VsZWN0ZWRUYWIKLyoKCW1vZCA9IGUubWV0YUtleSo4ICsgZS5jdHJsS2V5KjQgKyBlLnNoaWZ0S2V5KjIgKyBlLmFsdEtleQoJbW9kICsgSSDQsiDQutC+0L3RhtC1OiDRgtC+0LvRjNC60L4g0LIg0L/QvtC70Y/RhSDQstCy0L7QtNCwLCDCq2nCuyDQutGA0L7QvNC1INC/0L7Qu9C10Lkg0LLQstC+0LTQsAoJMdGPINCx0YPQutCy0LAg0YHRgtGA0L7Rh9C90LDRjzog0L/QtdGA0LXQtNCw0LLQsNGC0Ywg0L3QsNC20LDRgtC40Y8sINC30LDQv9GA0LXRgiBwcmV2ZW50RGVmYXVsdAoJ0L7RgtC00LXQu9GP0YLRjCDCq1/CuyDQvtGCINC60L7QtNCwLCDQtdGB0LvQuCDQtdGB0YLRjCDQvNC+0LTQuNGE0LjQutCw0YLQvtGA0Ysg0Lgv0LjQu9C4IMKraUnCuy3RhNC70LDQswoJ0YLQvtC70YzQutC+INCyINGD0LrQsNC30LDQvdC90L7QuSBPUzogS2V5QV8xaV93aW4oZSx0KXvigKYgQWx0K0Eg0LTQu9GPINCy0LjQvdC00YsgKi8KCS8vPT09PT09PT09PT09CiBCYWNrcXVvdGVfaSgpe1BsYWNlc0NvbW1hbmRIb29rLnNob3dQbGFjZXNPcmdhbml6ZXIoIkJvb2ttYXJrc01lbnUiKS5jbGljaygpO30sCglEaWdpdDFfaSgpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ1bmlmaWVkLWV4dGVuc2lvbnMtYnV0dG9uIikuY2xpY2soKTt9LAoJRGlnaXQyX2koKXtCcm93c2VyT3BlbkFkZG9uc01ncigpO30sCglEaWdpdDNfaSgpe29wZW5QcmVmZXJlbmNlcygpO30sCglEaWdpdDRfaSgpe2R1cGxpY2F0ZVRhYkluKGdCcm93c2VyLnNlbGVjdGVkVGFiLCd0YWInKTt9LAoJRGlnaXQ1X2koKXt2YXIgcz1wcm9tcHQoJ0dvb2dsZV8gc2l0ZTouLiAuLicsJycpO2lmKHMubGVuZ3RoPjApZ0Jyb3dzZXIuYWRkVHJ1c3RlZFRhYignaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9zZWFyY2g/cT1zaXRlOicrZW5jb2RlVVJJQ29tcG9uZW50KGdCcm93c2VyLmN1cnJlbnRVUkkuaG9zdCkrJyAnK2VuY29kZVVSSUNvbXBvbmVudChzKSk7fSwKCURpZ2l0Nl9pKCl7dmFyIHA9U2VydmljZXMuZGlyc3ZjLmdldCgnR3JlRCcsQ2kubnNJRmlsZSk7cC5pbml0V2l0aFBhdGgocC5wYXRoKyJcXC4uXFwuLlxcLi5cXF9QaG90b1xcU2hhcmVYXFxTaGFyZVguZXhlIik7cC5sYXVuY2goKTt9LAovKglEaWdpdDdfaSgpe2dCcm93c2VyLmZpeHVwQW5kTG9hZFVSSVN0cmluZygiamF2YXNjcmlwdDooZnVuY3Rpb24oKXt2YXIgRW5SdVQ9W1sn0YknLCdzaGgnXSxbJ9CpJywnU2hoJ10sWyfQqScsJ1NISCddLFsn0YUnLCdoaCddLFsn0KUnLCdIaCddLFsn0KUnLCdISCddLFsn0LYnLCd6aCddLFsn0JYnLCdaaCddLFsn0JYnLCdaSCddLFsn0YYnLCdjeiddLFsn0KYnLCdDeiddLFsn0KYnLCdDWiddLFsn0Y4nLCd5dSddLFsn0K4nLCdZdSddLFsn0K4nLCdZVSddLFsn0ZEnLCd5byddLFsn0IEnLCdZbyddLFsn0IEnLCdZTyddLFsn0Y8nLCd5YSddLFsn0K8nLCdZYSddLFsn0K8nLCdZQSddLFsn0YcnLCdjaCddLFsn0KcnLCdDaCddLFsn0KcnLCdDSCddLFsn0YgnLCdzaCddLFsn0KgnLCdTaCddLFsn0KgnLCdTSCddLFsn0Y0nLCdlYCddLFsn0K0nLCdFYCddLFsn0YsnLCd5J10sWyfQqycsJ1knXSxbJ9GKJywnYGAnXSxbJ9GMJywnYCddLFsn0YAnLCdyJ10sWyfRgicsJ3QnXSxbJ9GDJywndSddLFsn0LgnLCdpJ10sWyfQvicsJ28nXSxbJ9C/JywncCddLFsn0LAnLCdhJ10sWyfRgScsJ3MnXSxbJ9C0JywnZCddLFsn0YQnLCdmJ10sWyfQsycsJ2cnXSxbJ9C5JywnaiddLFsn0LonLCdrJ10sWyfQuycsJ2wnXSxbJ9C3JywneiddLFsn0YUnLCd4J10sWyfRhicsJ2MnXSxbJ9CyJywndiddLFsn0LEnLCdiJ10sWyfQvScsJ24nXSxbJ9C8JywnbSddLFsn0KAnLCdSJ10sWyfQoicsJ1QnXSxbJ9CjJywnVSddLFsn0JgnLCdJJ10sWyfQnicsJ08nXSxbJ9CfJywnUCddLFsn0JAnLCdBJ10sWyfQoScsJ1MnXSxbJ9CUJywnRCddLFsn0KQnLCdGJ10sWyfQkycsJ0cnXSxbJ9CZJywnSiddLFsn0JonLCdLJ10sWyfQmycsJ0wnXSxbJ9CXJywnWiddLFsn0KUnLCdYJ10sWyfQpicsJ0MnXSxbJ9CSJywnViddLFsn0JEnLCdCJ10sWyfQnScsJ04nXSxbJ9CcJywnTSddLFsn0LUnLCdlJ10sWyfQlScsJ0UnXV0sQT1kb2N1bWVudC5hY3RpdmVFbGVtZW50O0Eub25rZXl1cD1mdW5jdGlvbiBmdHIoKXtmb3IodmFyIHM9QS52YWx1ZSxpPTA7aTxFblJ1VC5sZW5ndGg7aSsrKXtzPXMucmVwbGFjZShSZWdFeHAoRW5SdVRbaV1bMV0sJ2cnKSxFblJ1VFtpXVswXSl9O0EudmFsdWU9c319KSgpIix7dHJpZ2dlcmluZ1ByaW5jaXBhbDpnQnJvd3Nlci5jb250ZW50UHJpbmNpcGFsfSk7CgkJCSB9LC8vQXV0b1RyYW5zbGl0LSBodHRwczovL2ZvcnVtLnJ1LWJvYXJkLmNvbS90b3BpYy5jZ2k/Zm9ydW09NSZ0b3BpYz00Njc3OSZzdGFydD0zMjAjMTUgKi8KLyoJRGlnaXQ4X2koKXt2YXIgcD1TZXJ2aWNlcy5kaXJzdmMuZ2V0KCdIb21lJyxDaS5uc0lGaWxlKTtwLmluaXRXaXRoUGF0aChwLnBhdGgrIlxcLi5cXC4uXFwuLlxcX1Bob3RvXFxTaGFyZVhcXFNoYXJlWC5leGUiKTsKCQkJIGxldCBhcmd1bWVudHM9WyItbm8tcmVtb3RlIiwiLVAiLCJYWFgiLCAiLWZvcmVncm91bmQiXTsgLy/QktGL0LHQvtGAINC/0YDQvtGE0LjQu9GPLiDQktC80LXRgdGC0L4gVGVzdC1Vc2VyINCy0LXQtNC40YLQtSDQuNC80Y8g0L/RgNC+0YTQuNC70Y8g0YLQsNC6LCDQutCw0Log0L7QvdC+INC+0YLQvtCx0YDQsNC20LDQtdGC0YHRjyDQsiBhYm91dDpwcm9maWxlcwoJCQkgbGV0IHByb2Nlc3M9Q2NbIkBtb3ppbGxhLm9yZy9wcm9jZXNzL3V0aWw7MSJdLmNyZWF0ZUluc3RhbmNlKENpLm5zSVByb2Nlc3MpOwoJCQkgcHJvY2Vzcy5pbml0KHApO3Byb2Nlc3MucnVuKGZhbHNlLCBhcmd1bWVudHMsIGFyZ3VtZW50cy5sZW5ndGgpOyAvL9Cf0YDQvtGE0LjQu9GMINC/0L7QtNGC0LLQtdGA0LbQtNC10L0KCQkJIH0sLy8gaHR0cHM6Ly9mb3J1bS5ydS1ib2FyZC5jb20vdG9waWMuY2dpP2ZvcnVtPTUmdG9waWM9NTEyMDIjNCAqLwogICAJCUYxKCl7dmFyIHA9U2VydmljZXMuZGlyc3ZjLmdldCgnU3lzRCcsQ2kubnNJRmlsZSk7cC5pbml0V2l0aFBhdGgocC5wYXRoKyJcXHNuZHZvbC5leGUiKTtwLmxhdW5jaCgpO30sCgkJRjIoKXtnQnJvd3Nlci5maXh1cEFuZExvYWRVUklTdHJpbmcoImphdmFzY3JpcHQ6e2Q9ZG9jdW1lbnQ7Yj1kLmJvZHk7bz1kLmNyZWF0ZUVsZW1lbnQoJ3NjcmknKydwdCcpO28uc2V0QXR0cmlidXRlKCdzcmMnLCdodHRwczovL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV9hL2VsZW1lbnQuanM/Y2I9Z29vZ2xlVHJhbnNsYXRlRWxlbWVudEluaXQnKTtvLnNldEF0dHJpYnV0ZSgndHlwZScsJ3RleHQvamF2YXNjcmlwdCcpO2IuYXBwZW5kQ2hpbGQobyk7dj1iLmluc2VydEJlZm9yZShkLmNyZWF0ZUVsZW1lbnQoJ2RpdicpLGIuZmlyc3RDaGlsZCk7di5pZD0nZ29vZ2xlX3RyYW5zbGF0ZV9lbGVtZW50Jzt2LnN0eWxlLmRpc3BsYXk9J25vbmUnO3A9ZC5jcmVhdGVFbGVtZW50KCdzY3JpJysncHQnKTtwLnRleHQ9J2Z1bmN0aW9uIGdvb2dsZVRyYW5zbGF0ZUVsZW1lbnRJbml0KCl7bmV3IGdvb2dsZS50cmFuc2xhdGUuVHJhbnNsYXRlRWxlbWVudCh7cGFnZUxhbmd1YWdlOiUyMiUyMn0sJTIyZ29vZ2xlX3RyYW5zbGF0ZV9lbGVtZW50JTIyKTt9JztwLnNldEF0dHJpYnV0ZSgndHlwZScsJ3RleHQvamF2YXNjcmlwdCcpO2IuYXBwZW5kQ2hpbGQocCk7fXZvaWQgMCIse3RyaWdnZXJpbmdQcmluY2lwYWw6Z0Jyb3dzZXIuY29udGVudFByaW5jaXBhbH0pOwoJCQkgfSwvL0VuYWJsZSB0cmFuc2xhdGlvbihodHRwczovL2ZvcnVtLm1vemlsbGEtcnVzc2lhLm9yZy92aWV3dG9waWMucGhwP3BpZD04MDU0MjIjcDgwNTQyMiB8fDgwNTQyNyNwODA1NDI3KQoJCUYzKCl7Z0Jyb3dzZXIuZml4dXBBbmRMb2FkVVJJU3RyaW5nKCJqYXZhc2NyaXB0OihmdW5jdGlvbigpe3ZhciBuaWdodD1mdW5jdGlvbih3KXsoZnVuY3Rpb24oZCl7dmFyIGNzcz0naHRtbHtvcGFjaXR5OjAuNiFpbXBvcnRhbnQ7YmFja2dyb3VuZDpibGFjayFpbXBvcnRhbnQ7fWJvZHl7YmFja2dyb3VuZDp3aGl0ZSFpbXBvcnRhbnQ7fSc7dmFyIHM9ZC5nZXRFbGVtZW50c0J5VGFnTmFtZSgnc3R5bGUnKTtmb3IodmFyIGk9MCxzaTtzaT1zW2ldO2krKyl7aWYoc2kuaW5uZXJIVE1MPT1jc3Mpe3NpLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoc2kpO3JldHVybn19O3ZhciBoZWFkcz1kLmdldEVsZW1lbnRzQnlUYWdOYW1lKCdoZWFkJyk7aWYoaGVhZHMubGVuZ3RoKXt2YXIgbm9kZT1kLmNyZWF0ZUVsZW1lbnQoJ3N0eWxlJyk7bm9kZS50eXBlPSd0ZXh0L2Nzcyc7bm9kZS5hcHBlbmRDaGlsZChkLmNyZWF0ZVRleHROb2RlKGNzcykpO2hlYWRzWzBdLmFwcGVuZENoaWxkKG5vZGUpfX0pKHcuZG9jdW1lbnQpO2Zvcih2YXIgaT0wLGY7Zj13LmZyYW1lc1tpXTtpKyspe3RyeXthcmd1bWVudHMuY2FsbGVlKGYpfWNhdGNoKGUpe319fTtuaWdodCh3aW5kb3cpfSkoKTsiLHt0cmlnZ2VyaW5nUHJpbmNpcGFsOmdCcm93c2VyLmNvbnRlbnRQcmluY2lwYWx9KTsKCQkJIH0sLy9OaWdodCBtb2RlKEZvY3VzIG1vZGUpCgkJRjQoKXt2YXIgdmVydD1gamF2YXNjcmlwdDp7Y29uc3Qgbz1bImNvbnRleHRtZW51Iiwic2VsZWN0c3RhcnQiLCJzZWxlY3QiLCJtb3VzZWRvd24iLCJtb3VzZXVwIiwiY3V0IiwiY29weSJdLHQ9WyItd2Via2l0IiwiLW1veiIsIi1tcyIsIi1raHRtbCIsIiJdLGU9d2luZG93LmpRdWVyeTtbZG9jdW1lbnQsZG9jdW1lbnQuYm9keSxkb2N1bWVudC5kb2N1bWVudEVsZW1lbnRdLmZvckVhY2gobj0+e2Zvcihjb25zdCB0IG9mIG8pblsib24iK3RdPW51bGwsZSYmZShuKS5vZmYodCk7aWYobi5zdHlsZSlmb3IoY29uc3QgbyBvZiB0KXtjb25zdCB0PVtvLCJ1c2VyLXNlbGVjdCJdLmpvaW4oIi0iKTtuLnN0eWxlW3RdPSJpbml0aWFsIn19KQoJCQkgfTt2b2lkKDApO2A7Z0Jyb3dzZXIubG9hZFVSSShTZXJ2aWNlcy5pby5uZXdVUkkodmVydCkse3RyaWdnZXJpbmdQcmluY2lwYWw6U2VydmljZXMuc2NyaXB0U2VjdXJpdHlNYW5hZ2VyLmdldFN5c3RlbVByaW5jaXBhbCgpfSk7CgkJCSB9LC8vUmUtZW5hYmxlIHNlbGVjdGluZyAmIGNvcHlpbmcgdGV4dCBbbWFwbGUzMTQyXSAoQm9va21hcmtsZXQpCglLZXlHXzQoKXt2YXIgYmFyPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ1Y2YtYWRkaXRpb25hbC12ZXJ0aWNhbC1iYXIiKTtzZXRUb29sYmFyVmlzaWJpbGl0eShiYXIsYmFyLmNvbGxhcHNlZCk7fSwKCUtleVdfMSgpe2dCcm93c2VyLnJlbW92ZUFsbFRhYnNCdXQoZ0Jyb3dzZXIuc2VsZWN0ZWRUYWIpO30sCglLZXlWX2koKXtnQ2xpcGJvYXJkLndyaXRlKGdVUkxCYXIudmFsdWUpO2dsb2IuZmxhc2goMCwwLCdyZ2JhKDI0MCwxNzYsMCwwLjUpJywzMDApO30sLy8zMDAsIkNvcHlVUkw6ICIrZ1VSTEJhci52YWx1ZS5zbGljZSgwLDgwKSkKCUtleVZfMSgpe2xldCB1cmw9cmVhZEZyb21DbGlwYm9hcmQoKTt0cnl7c3dpdGNoVG9UYWJIYXZpbmdVUkkodXJsLHRydWUpO31jYXRjaChleCl7CgkJCSB2YXIgcmVnPS9bYS16QS1aMC05XVstYS16QS1aMC05XXswLDYyfShcLlthLXpBLVowLTldWy1hLXpBLVowLTldezAsNjJ9KStcLj8vOwoJCQkgaWYoIXJlZy50ZXN0KHVybCkpe3VybD0naHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS9zZWFyY2g/cT0nK2VuY29kZVVSSUNvbXBvbmVudCh1cmwpOwoJCQkgfWVsc2V7aWYodXJsLnN1YnN0cmluZyg0LDApLnRvTG93ZXJDYXNlKCk9PSJodHRwIil7dXJsPWVuY29kZVVSSUNvbXBvbmVudCh1cmwpOwoJCQkgfWVsc2V7dXJsPSdodHRwOi8vJytlbmNvZGVVUklDb21wb25lbnQodXJsKTt9fXN3aXRjaFRvVGFiSGF2aW5nVVJJKHVybCx0cnVlKTt9fSwvL09wZW4gY2xpcGJvYXJkIGFkZHJlc3MKCUtleVFfNSgpe01vdXNlW0JbMTRdXVsxMjhdKCk7fSwKCUtleVVfNSgpe3ZhciBwPVNlcnZpY2VzLmRpcnN2Yy5nZXQoJ1VDaHJtJyxDaS5uc0lGaWxlKTtwLmluaXRXaXRoUGF0aChwLnBhdGgrIlxcdXNlcl9jaHJvbWVfZmlsZXNcXF9cXHUuZXhlIik7cC5sYXVuY2goKTt9LAoJS2V5QV81KCl7dmFyIHA9U2VydmljZXMuZGlyc3ZjLmdldCgnVUNocm0nLENpLm5zSUZpbGUpO3AuaW5pdFdpdGhQYXRoKHAucGF0aCsiXFx1c2VyX2Nocm9tZV9maWxlc1xcX1xcLkV2XFxFdmVyeXRoaW5nLmV4ZSIpO3AubGF1bmNoKCk7fSwKCUtleVFfNCgpe3ZhciBwPVNlcnZpY2VzLmRpcnN2Yy5nZXQoJ1VDaHJtJyxDaS5uc0lGaWxlKTtwLmluaXRXaXRoUGF0aChwLnBhdGgrIlxcdXNlcl9jaHJvbWVfZmlsZXNcXF9cXC5RVFxcUVRyYW5zbGF0ZS5leGUiKTtwLmxhdW5jaCgpO30sCgkvLz09PT09PT09PT09PQp9LApNb3VzZT17IC8vTWV0YSo2NCBDdHJsKjMyINCo0LjRhNGCKjE2IEFsdCo4IChXaCA/IDIgOiBCdXQqMTI4KSBsb25nKjEKCSJ1cmxiYXItaW5wdXQiOnsgLy9DYXBzTG9jayBPbjogc2tpcCBhY3Rpb24gbW91c2V8a2V5Ym9hcmQKCQkyKHRyZyxmb3J3YXJkKXt0cmcudmFsdWU9IiJ9fSwgLy/QvtGH0LjRgdGC0LjRgtGMCgkidGFiYnJvd3Nlci10YWJzIjp7IC8vPD4g0LLQutC70LDQtNC60Lgg0LrQvtC70ZHRgdC40LrQvtC8CgkJMih0cmcsZm9yd2FyZCl7Z0Jyb3dzZXIudGFiQ29udGFpbmVyLmFkdmFuY2VTZWxlY3RlZFRhYihmb3J3YXJkID8gLTEgOiAxLHRydWUpO30sCgkJOCgpe30sMTYoKXt9LDY0KCl7fSwgLy/QstGL0LHQvtGACgkJMTI4KGJ0bil7Z0Jyb3dzZXIucmVtb3ZlVGFiKFRhYkFjdChidG4pKX0sIC8v0JwgICAgICAgLSDQstC60LvQsNC00LrQsCDQv9C+0LQg0LzRi9GI0YzRjgoJCTEzNihidG4pe1RhYnNEZWwoMSxUYWJBY3QoYnRuKSl9LCAgICAgICAgICAvL9CcK0FsdCAgIC0g0LfQsNC60YDRi9GC0Ywg0LLQutC70LDQtNC60Lgg0YHQv9GA0LDQstCwCgkJMTQ0KGJ0bil7VGFic0RlbCgwLFRhYkFjdChidG4pKX19LCAgICAgICAgIC8v0JwrU2hpZnQgLSDigKYg0YHQu9C10LLQsAoJInN0b3AtYnV0dG9uIjp7CgkJMTI4KCl7Zm9yKHZhciBpPTA7aTxnQnJvd3Nlci50YWJzLmxlbmd0aDtpKyspIGdCcm93c2VyLmdldEJyb3dzZXJBdEluZGV4KGkpLnN0b3AoKX0sIC8vTQoJCTI1Nygpe3N3aXRjaFByb3h5KCl9fSwgICAgICAgICAgICAvL1ItbG9uZwoJInN0YXItYnV0dG9uLWJveCI6ewoJCTEoKXt0cmFuc2xhdGUoKX0sICAgICAgICAgICAgICAgICAvL0xvbmcKCQk4KCl7d2luZG93LlBsYWNlc0NvbW1hbmRIb29rLnNob3dQbGFjZXNPcmdhbml6ZXIoIkJvb2ttYXJrc1Rvb2xiYXIiKX0sIC8vTCtBbHQgLSDQsdC40LHQu9C40L7RgtC10LrQsAoJCTEyOCgpe3N3aXRjaFRhYihGYXZJdGVtKCkpfSwgICAgICAvL00KCQkxMjkoKXtzd2l0Y2hUYWIoRmF2SXRlbShmYWxzZSkpfSwgLy9NLWxvbmcKCQkyNTYoKXt0b0ZhdigpfX0sICAgICAgICAgICAgICAgICAgLy9SCgkiYXBwTWVudS1wcmludC1idXR0b24yIjp7IC8v0JzQtdC90Y46INCf0LXRh9Cw0YLRjOKApgoJCTEoKXtIZWxwKCl9LAoJCTEyOChidG4pe0V4cGVydCgpfSwKCQkyNTYoKXtNb3VzZVtCWzldXVsyNTZdKCl9fSwKCSJwYWdlQWN0aW9uLXVybGJhci1fMjQ5NWQyNTgtNDFlNy00Y2Q1LWJjN2QtYWMxNTk4MWYwNjRlXyI6eyAvL1JlYWRlclZpZXcKCQkyKHRyZyxmb3J3YXJkKXticmlnaHQodHJnLGZvcndhcmQsNSl9LCAvL9GP0YDQutC+0YHRgtGMINC/0L4gd2hlZWwgwrEKCQkxMjgoYnRuKXtidG4ub3duZXJEb2N1bWVudC5nZXRFbGVtZW50QnlJZCgia2V5X3RvZ2dsZVJlYWRlck1vZGUiKS5kb0NvbW1hbmQoKX0sIC8v0YjRgtCw0YLQvdGL0Lkg0KDQtdC20LjQvCDRh9GC0LXQvdC40Y8KCQkyNTYoYnRuKXtNb3VzZVtCWzhdXVsyNTZdKGJ0bil9fSwKCSJ1bmlmaWVkLWV4dGVuc2lvbnMtYnV0dG9uIjp7bW91c2Vkb3duVGFyZ2V0OnRydWUsIAoJCTEoKXtFeHBlcnQoKX0sCgkJMTI4KCl7TW91c2VbQlsxNF1dWzEyOF0oKX0sCgkJMjU2KCl7b3BlbkRpYWwoKX19LAoJW0JbMF1dOnttb3VzZWRvd25UYXJnZXQ6dHJ1ZSwgLy/QvdC1INC/0LXRgNC10LTQsNCy0LDRgtGMINC90LDQttCw0YLQuNGPINC00LDQu9GM0YjQtQoJCTEoKXtEb3dubG9hZHMuZ2V0U3lzdGVtRG93bmxvYWRzRGlyZWN0b3J5KCkudGhlbihwYXRoID0+IEZpbGVVdGlscy5GaWxlKHBhdGgpLmxhdW5jaCgpLEN1LnJlcG9ydEVycm9yKX0sIC8vTG9uZwoJCTgoKXt2YXIgcD0iYnJvd3Nlci5kb3dubG9hZC5pbXByb3ZlbWVudHNfdG9fZG93bmxvYWRfcGFuZWwiLG49Z2xvYi5wcmVmKHApOwoJCQlnbG9iLnByZWYocCwgIW4pOwoJCQlnbG9iLnRvU3RhdHVzKGDQn9C+0LTRgtCy0LXRgNC20LTQtdC90LjQtSDQt9Cw0LPRgNGD0LfQutC4ICR7biA/ICLiiJog0YDQsNC30YDQtdGIIiA6ICLinJgg0LfQsNC/0YDQtdGJIn3QtdC90L5gLDNlMyk7fSwgLy9MK0FsdAoJCTE2KCl7dmFyIG49Z2xvYi5wcmVmKEZmLmkpID09IDI7Z2xvYi5wcmVmKEZmLmksIG4gPyAxIDogMik7CgkJCWdsb2IubW9kZV9za2luKCk7IEJyb3dzZXJSZWxvYWQoKTsKCQkJZ2xvYi50b1N0YXR1cyhg0JfQsNCz0YDRg9C30LrQsCDQuNC30L7QsdGA0LDQttC10L3QuNC5ICR7biA/ICLiiJog0YDQsNC30YDQtdGIIiA6ICLinJgg0LfQsNC/0YDQtdGJIn3QtdC90L5gLDNlMyk7fSwgICAvL0wrU2hpZnQKCQkxMjgoKXtGZi5FeHAoKSAvL9GA0LXQttC40Lwg0J/RgNC+0YHRgtC+0Lkv0K3QutGB0L/QtdGA0YI6INGA0LDQt9C90YvQtSDQtNC10LnRgdGC0LLQuNGPCgkJCT8gc2F2ZVNlbFRvVHh0KCkgOiAvL9GB0L7RhdGA0LDQvdC40YLRjHzQstGL0LTQtdC70LXQvS4g0LrQsNC6IC50eHQKCQkJRG93bmxvYWRzLmdldFN5c3RlbURvd25sb2Fkc0RpcmVjdG9yeSgpLnRoZW4ocGF0aCA9PiBGaWxlVXRpbHMuRmlsZShwYXRoKS5sYXVuY2goKSxDdS5yZXBvcnRFcnJvcil9LAoJCTI1Nigpe3NhdmUoKX19LCAvL3dlYgoJW0JbMV1dOnttb3VzZWRvd25UYXJnZXQ6dHJ1ZSwKCQkxKGJ0bil7Z29RdWl0QXBwbGljYXRpb24oYnRuKX0sIC8vTG9uZwoJCTIodHJnLGZvcndhcmQpe2ZvcndhcmQgPyB0cmcub3duZXJHbG9iYWwudW5kb0Nsb3NlVGFiKCkgOiB0cmcub3duZXJHbG9iYWwuQnJvd3NlckNsb3NlVGFiT3JXaW5kb3coKX0sIC8v0LrRgNGD0YLQuNGC0YwgK9CX0LDQutGA0YvRgtGMINCy0LrQu9Cw0LTQutGDIC3QktC+0YHRgdGC0LDQvdC+0LLQuNGC0YwKCQk4KCl7d2luZG93U3RhdGUgIT0gU1RBVEVfTUFYSU1JWkVEID8gbWF4aW1pemUoKSA6IHJlc3RvcmUoKX0sICAgLy9MK0FsdAoJCTE2KCl7SGVscCgpfSwgICAgICAgICAgICAgICAgICAgLy9MK1NoaWZ0CgkJMTI4KCl7d2luZG93U3RhdGUgIT0gU1RBVEVfTUFYSU1JWkVEID8gbWF4aW1pemUoKSA6IHJlc3RvcmUoKX0sIC8vTQoJCTEyOSgpe0Jyb3dzZXJGdWxsU2NyZWVuKCl9LCAgICAgLy9NLWxvbmcKCQkxMzYoKXt0aGlzWzEyOV0oKX0sICAgICAgICAgICAgIC8vTStBbHQKCQkyNTYoKXttaW5pbWl6ZSgpfSwgICAgICAgICAgICAgIC8vUgoJCTI2NCgpe3N3aXRjaFRhYignYWJvdXQ6c3VwcG9ydCcpfSwgLy9SK0FsdAoJCTI3MihidG4pe2J0bi5vd25lckdsb2JhbC5QbGFjZXNDb21tYW5kSG9vay5zaG93UGxhY2VzT3JnYW5pemVyKCJIaXN0b3J5Iil9fSwgLy9SK1NoaWZ0CglbQlsyXV06ezIodHJnLGZvcndhcmQpe3pvb20oZm9yd2FyZCl9fSwgLy93aGVlbAoJW0JbM11dOnsKCQk4KCl7Z0Jyb3dzZXIucmVtb3ZlQWxsVGFic0J1dChnQnJvd3Nlci5zZWxlY3RlZFRhYil9LAoJCTI1NihidG4pe2J0bi5vd25lckdsb2JhbC51bmRvQ2xvc2VUYWIoKX19LAoJW0JbNF1dOnsgLy/Qt9Cw0LzQvtC6CgkJMih0cmcsZm9yd2FyZCl7YnJpZ2h0KHRyZyxmb3J3YXJkLDUpfSwgLy/Rj9GA0LrQvtGB0YLRjAoJCTgoKXtvcGVuUHJveHlXaW4oKX0sIC8vTCtBbHQKCQkxMCh0cmcsZm9yd2FyZCl7YnJpZ2h0KHRyZyxmb3J3YXJkKX0sCgkJMTYoYnRuKXtCcm93c2VyUGFnZUluZm8oYnRuLCJtZWRpYVRhYiIpfSwgLy9MK1NoaWZ0IC0gZmVlZFRhYiBwZXJtVGFiIHNlY3VyaXR5VGFiCgkJMTI4KCl7dGhpc1s4XSgpfSwKCQkyNTYoKXtnQ2xpcGJvYXJkLndyaXRlKGdVUkxCYXIudmFsdWUpO2dsb2IuZmxhc2goMCwwLCdyZ2JhKDI0MCwxNzYsMCwwLjUpJywzMDAsItCyINCx0YPRhNC10YDQtTogIitnVVJMQmFyLnZhbHVlLnNsaWNlKDAsODApKTt9fSwKCVtCWzVdXTp7IC8vQ3VzdG9taXphYmxlVUkg0LIg0YHQutGA0LjQv9GC0LUKCQkwKGJ0bil7YnRuLm93bmVyR2xvYmFsLlNpZGViYXJVSS50b2dnbGUoInZpZXdCb29rbWFya3NTaWRlYmFyIil9LCAvL0wKCQkyKHRyZyxmb3J3YXJkKXtmb3J3YXJkID8gZ2xvYi5kaXJzdmNnZXQoIlVDaHJtIiwidXNlcl9jaHJvbWVfZmlsZXMiKSA6IGdsb2IuZGlyc3ZjZ2V0KCJVQ2hybSIsInVzZXJfY2hyb21lX2ZpbGVzIiwiY3VzdG9tX3NjcmlwdHMiKX0sCgkJOCgpe2dsb2IuZGlyc3ZjZ2V0KCJIb21lIil9LCAgICAgICAgICAgICAgICAgICAgICAgICAgLy9MK0FsdAoJCTE2KCl7Z2xvYi5kaXJzdmNnZXQoIlVDaHJtIiwidXNlcl9jaHJvbWVfZmlsZXMiLCJjdXN0b21fc2NyaXB0cyIsIkNCIiwiJUgudHh0Iil9LCAvL0wrU2hpZnQKCQkxMjgoYnRuKXtidG4ub3duZXJHbG9iYWwuUGxhY2VzQ29tbWFuZEhvb2suc2hvd1BsYWNlc09yZ2FuaXplcigiRG93bmxvYWRzIil9LCAvL00gLSDQkdC40LHQu9C40L7RgtC10LrQsCDQl9Cw0LPRgNGD0LfQutC4CgkJMTM2KCl7Z2xvYi5kaXJzdmNnZXQoIlVDaHJtIiwidXNlcl9jaHJvbWVfZmlsZXMiKX0sICAgLy9NK0FsdAoJCTI1NihidG4pe2J0bi5vd25lckdsb2JhbC5TaWRlYmFyVUkudG9nZ2xlKEIuYXQoLTEpKX0sIC8vUgoJCTI2NCgpe2dsb2IuZGlyc3ZjZ2V0KCJHcmVEIil9LCAgICAgICAgICAgICAgICAgICAgICAgIC8vUitBbHQKCQkyNzIoKXtnbG9iLmRpcnN2Y2dldCgiVUNocm0iLCJ1c2VyX2Nocm9tZV9maWxlcyIsImN1c3RvbV9zY3JpcHRzIiwiZG9icm92IiwidWNmX2hvb2tDbGlja3MuanMiKX19LCAvL0wrU2hpZnQKCVtCWzZdXTp7bW91c2Vkb3duVGFyZ2V0OnRydWUsCgkJMChidG4pe2lmIChidG4uaWQgPT0gQls2XSkgewoJCQkJICAgdmFyIGJhciA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJ1Y2YtYWRkaXRpb25hbC12ZXJ0aWNhbC1iYXIiKTsKCQkJCSAgIGlmIChiYXIpIHdpbmRvdy5zZXRUb29sYmFyVmlzaWJpbGl0eShiYXIsZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInNpZGViYXItYm94IikuaGlkZGVuKTsKCQkJCSAgIHdpbmRvdy5TaWRlYmFyVUkudG9nZ2xlKEIuYXQoLTEpKTsKCQkJICAgfSBlbHNlIGdsb2IubW9kZV9za2luKCk7fSwgLy9MIC0g0LzQtdC90Y4g0LrQvdC+0L/QutC4CgkJMShidG4peyAvL0xvbmcKCQkJaWYgKGJ0bi5pZCA9PSBCWzZdKXsgLy/Qu9C40L3Qt9CwCgkJCQlnbG9iLmZsYXNoKDAsMCwncmdiYSgxMDAsMCwyMjUsMC4xKScsNTAwLCBUWzRdKTsKCQkJCXZhciB1cmwgPSBgcmVzb3VyY2U6Ly9kZXZ0b29scy9zaGFyZWQvJHtwYXJzZUludChGZi52ZXIpID4gOTUgPyAibG9hZGVyLyIgOiAiIn1Mb2FkZXIuYDsKCQkJCXRyeSB7dmFyIGV4cCA9IENocm9tZVV0aWxzLmltcG9ydEVTTW9kdWxlKHVybCArICJzeXMubWpzIik7fSBjYXRjaCB7ZXhwID0gQ2hyb21lVXRpbHMuaW1wb3J0KHVybCArICJqc20iKTt9CgkJCQl2YXIgb2JqID0gZXhwLnJlcXVpcmUoImRldnRvb2xzL2NsaWVudC9tZW51cyIpLm1lbnVpdGVtcy5maW5kKG1lbnVpdGVtID0+IG1lbnVpdGVtLmlkID09ICJtZW51X2V5ZWRyb3BwZXIiKTsKCQkJCSh0ZXN0ID0gb2JqLm9uY29tbWFuZC5iaW5kKG51bGwsIHt0YXJnZXQ6YnRufSkpKCk7CgkJCX0gZWxzZSAvL9C60LvQuNC6INCyINC80LXQvdGOINCx0YvRgdGC0YDRi9GFINC90LDRgdGC0YDQvtC10LoKCQkJCWdsb2IuYWJvdXRfY29uZmlnKGJ0bi5wcmVmLnByZWYpLCAvL2dvINC/0LDRgNCw0LzQtdGC0YAgYWJvdXQ6Y29uZmlnCgkJCQlidG4ucGFyZW50Tm9kZS5wYXJlbnROb2RlLnNlY29uZGFyeVBvcHVwLmhpZGVQb3B1cCgpO30sCgkJMih0cmcsZm9yd2FyZCl7em9vbShmb3J3YXJkKX0sIC8vd2hlZWwKCQk4KCl7c3dpdGNoVGFiKCdjaHJvbWU6Ly9icm93c2VyL2NvbnRlbnQvcGxhY2VzL3BsYWNlcy54aHRtbCcpfSwgLy9MK0FsdAoJCTE2KGJ0bil7aWYgKGJ0bi5pZCA9PSBCWzZdKSB6b29tKDAsMSl9LCAgICAgICAgICAgICAgICAgICAgICAgICAvL0wrU2hpZnQKCQkxMjgoYnRuKXsgLy9NCgkJCWlmIChidG4uaWQgPT0gQls2XSkKCQkJCXN3aXRjaFRhYigiYWJvdXQ6bmV3dGFiIiwgdHJ1ZSkKCQkJZWxzZSAvL9C80LXQvdGOINCx0YvRgdGC0YDRi9GFINC90LDRgdGC0YDQvtC10LoKCQkJCXN3aXRjaFRhYigpLAoJCQkJYnRuLnBhcmVudE5vZGUucGFyZW50Tm9kZS5zZWNvbmRhcnlQb3B1cC5oaWRlUG9wdXAoKTt9LAoJCTEyOShidG4pe3VzZXJqcyhidG4sIiIpfSwgICAgICAgICAgICAgICAvL00tbG9uZyAtINC60L7QvdGB0L7Qu9GMCgkJMTM2KCl7dmFyIG49ImJyb3dzZXIuZGlzcGxheS51c2VfZG9jdW1lbnRfZm9udHMiLGY9Z2xvYi5wcmVmKG4pID8gMCA6IDE7CgkJCSAgZ2xvYi5wcmVmKG4sZik7em9vbSgwLDAsMCwoZj4wKSA/ICIgKyBXZWIt0YjRgNC40YTRgtGLIiA6ICIiKTtCcm93c2VyUmVsb2FkKCk7fSwgLy9NK0FsdAoJCTI1Nigpe30sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvL1IgLSBsb2NrCgkJMjY0KCl7c3dpdGNoVGFiKEZhdkl0ZW0oZmFsc2UpKX19LCAgICAgICAvL1IrQWx0CgkgIC8vMjcyKGJ0bil7aWYgKGJ0bi5pZD1CWzZdKSBvcGVuRGlhbCgpfX0sIC8vTCtTaGlmdCAtIFVDRiBQcmVmcwoJW0JbOF1dOnsgLy/inr8KCQkyKHRyZyxmb3J3YXJkKXticmlnaHQodHJnLGZvcndhcmQsNSl9LCAgLy/Rj9GA0LrQvtGB0YLRjAoJCTI1NihidG4pe2J0bi5vd25lckRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJrZXlfcmVzcG9uc2l2ZURlc2lnbk1vZGUiKS5kb0NvbW1hbmQoKTsKCQkJCSBpZihnQnJvd3Nlci5zZWxlY3RlZEJyb3dzZXIuYnJvd3NpbmdDb250ZXh0LmluUkRNUGFuZSlCcm93c2VyUmVsb2FkKCk7fX0sIC8vUiAtIE1vYmlsZSBWaWV3ICjQv9GD0L3QutGCINC80LXQvdGOINGBIEhvdEtleSkKCVtCWzldXTp7CgkJMSgpe0hlbHAoKX0sCgkJMTI4KCl7RXhwZXJ0KCl9LAoJCTI1Nigpe2RvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJtZW51X3ByaW50IikuZG9Db21tYW5kKCl9fSwKCVtCWzEwXV06ewoJCTEoKXt3aXRoIChnQnJvd3Nlcikgc2VsZWN0QWxsVGFicygpLHJlbG9hZE11bHRpU2VsZWN0ZWRUYWJzKCksY2xlYXJNdWx0aVNlbGVjdGVkVGFicygpO30sCgkJMTI4KCl7Zm9yICh2YXIgaT0wO2k8Z0Jyb3dzZXIudGFicy5sZW5ndGg7aSsrKSBnQnJvd3Nlci5nZXRCcm93c2VyQXRJbmRleChpKS5zdG9wKCl9LAoJCTI1Nigpe0Jyb3dzZXJSZWxvYWRTa2lwQ2FjaGUoKX0sCgkJMjU3KCl7c3dpdGNoUHJveHkoKX19LAoJW0JbMTFdXTp7CgkJMSgpe01vdXNlW0JbNl1dWzEzNl0oKX0sIC8vTG9uZyAtZm9udHMKCQkyKHRyZyxmb3J3YXJkKXticmlnaHQodHJnLGZvcndhcmQpfSwKCQkxNigpe3N3aXRjaFRhYigpfSwKCQkxMjgoKXtzd2l0Y2hUYWIoJ2Fib3V0OnBlcmZvcm1hbmNlJyl9LAoJCTI1NihidG4pe3ZhciBsb2dpbnM9YnRuLm93bmVyRG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInVjZi1sb2dpbnMtc2l0ZWRhdGEiKTtsb2dpbnMgPyBsb2dpbnMuY2xpY2soKSA6IHN3aXRjaFRhYignYWJvdXQ6bG9naW5zJyk7fX0sCglbQlsxM11dOnsKCQkxKCl7SGVscCgpfSwKCQkxMjgoYnRuKXtidG4ub3duZXJHbG9iYWwudW5kb0Nsb3NlVGFiKCl9LAoJCTI1Nigpe21pbmltaXplKCl9fSwKCVtCWzE0XV06eyAvL0F0dHJWaWV3CgkJMSgpe0V4cGVydCgpfSwKCQk4KCl7dmFyIGNocj0iZGV2dG9vbHMuY2hyb21lLmVuYWJsZWQiLHJlbT0iZGV2dG9vbHMuZGVidWdnZXIucmVtb3RlLWVuYWJsZWQiOwoJCQlpZiAoIWdsb2IucHJlZihjaHIpIHx8ICFnbG9iLnByZWYocmVtKSkge2dsb2IucHJlZihjaHIsdHJ1ZSk7IGdsb2IucHJlZihyZW0sdHJ1ZSk7fQoJCQl2YXIge0Jyb3dzZXJUb29sYm94TGF1bmNoZXJ9PUNocm9tZVV0aWxzLmltcG9ydCgicmVzb3VyY2U6Ly9kZXZ0b29scy9jbGllbnQvZnJhbWV3b3JrL2Jyb3dzZXItdG9vbGJveC9MYXVuY2hlci5qc20iKTsKCQkJQnJvd3NlclRvb2xib3hMYXVuY2hlci5pbml0KCk7fSwgLy/Rg9C0LtC+0YLQu9Cw0LTQutCwCgkJMTYoKXtzd2l0Y2hUYWIoJ2Fib3V0OnByb2ZpbGluZycpfSwKCQkxMjgoKXsgLy9NLCBRK0FsdCtDdHJsIENsZWFuQ2FjaGUgLSDQn9C10YDQtdC30LDQv9GD0YHRgtC40YLRjCwg0YPQtNCw0LvQuNCyINC60Y3RiAoJCQl2YXIgY2FuY2VsUXVpdCA9IENjWyJAbW96aWxsYS5vcmcvc3VwcG9ydHMtUFJCb29sOzEiXS5jcmVhdGVJbnN0YW5jZShDaS5uc0lTdXBwb3J0c1BSQm9vbCk7CgkJCVNlcnZpY2VzLm9icy5ub3RpZnlPYnNlcnZlcnMoY2FuY2VsUXVpdCwicXVpdC1hcHBsaWNhdGlvbi1yZXF1ZXN0ZWQiLCJyZXN0YXJ0Iik7CgkJCWlmIChjYW5jZWxRdWl0LmRhdGEpIHJldHVybiBmYWxzZTsKCQkJU2VydmljZXMuYXBwaW5mby5pbnZhbGlkYXRlQ2FjaGVzT25SZXN0YXJ0KCk7CgkJCXZhciByZXN0YXJ0ID0gU2VydmljZXMuc3RhcnR1cDsKCQkJcmVzdGFydC5xdWl0KHJlc3RhcnQuZUF0dGVtcHRRdWl0IHwgcmVzdGFydC5lUmVzdGFydCk7fSwKCQkyNTYoKXtvcGVuRGlhbCgpfSAvL1VDRnByZWZzCgl9fTsgdmFyIE0gPSBPYmplY3Qua2V5cyhNb3VzZSk7CgooYXN5bmMgKGlkKSA9PiBDdXN0b21pemFibGVVSS5jcmVhdGVXaWRnZXQoe2xhYmVsOmlkLnJlcGxhY2UoJy0nLCcgJyksIGlkOmlkLAoJb25DcmVhdGVkKGJ0bikgewoJCWJ0bi5zZXRBdHRyaWJ1dGUoImltYWdlIiwiZGF0YTppbWFnZS93ZWJwO2Jhc2U2NCxVa2xHUmp3QUFBQlhSVUpRVmxBNFRDOEFBQUF2RDhBREFBb0diU001T3Y2azc3NFhDUEZQLzAvMDMvOEpHUHh6cm9JenVPVzA2SWg2MEdlbm4xUy9nSGUrQmdBPSIpOwoJCWJ0bi5vbm1vdXNlZW50ZXIgPSBidG4ub25tb3VzZWxlYXZlID0gdGhpcy5vbm1vdXNlOwoJCWJ0bi5zZXRBdHRyaWJ1dGUoIm9uY29tbWFuZCIsImhhbmRsZUNvbW1hbmQodGhpcykiKTsgYnRuLmhhbmRsZUNvbW1hbmQgPSB0aGlzLmhhbmRsZUNvbW1hbmQ7Cgl9LAoJb25tb3VzZTogZSA9PiBlLnRhcmdldC5mb2N1c2VkV2luZG93ID0gZS50eXBlLmVuZHNXaXRoKCJyIikgJiYgU2VydmljZXMud20uZ2V0TW9zdFJlY2VudFdpbmRvdyhudWxsKSwKCWdldCBoYW5kbGVDb21tYW5kKCkge2RlbGV0ZSB0aGlzLmhhbmRsZUNvbW1hbmQ7CgkJcmV0dXJuIHRoaXMuaGFuZGxlQ29tbWFuZCA9IGJ0biA9PiB7KGJ0bi5oYW5kbGVDb21tYW5kID0gbmV3IGJ0bi5vd25lckdsb2JhbC5GdW5jdGlvbih0aGlzLmNvZGUpLmJpbmQoYnRuKSkoKTt9Cgl9LAoJZ2V0IGNvZGUoKSB7ZGVsZXRlIHRoaXMuY29kZTsgdmFyIHMgPSBGZi5jICsiY3VzdG9tX3NjcmlwdHMvIisgQlsxNF0gKyIuanMiOwoJCXRyeSB7aWQgPSAndGhpcy5mb2N1c2VkV2luZG93ICYmIHRoaXMuZm9jdXNlZFdpbmRvdy5mb2N1cygpO1xuJysKCQkJQ3UucmVhZFVURjhVUkkoU2VydmljZXMuaW8ubmV3VVJJKHMpKX0gY2F0Y2gge2lkID0gYGdsb2IudG9TdGF0dXMoIiR7VFs4XX0ke3N9Iiw3ZTMpYH0KCQlyZXR1cm4gdGhpcy5jb2RlID0gaWQ7fQp9KSkoQlsxNF0pOwoKdmFyIHtwcmVmc30gPSBTZXJ2aWNlcyxPdmVyID0geyAvL21vZGlmeSBUb29sdGlwcyDQv9C+0LQg0LzRi9GI0YzRjgpnZXQgW0JbMV1dKCkgeyAvLyBkZWxldGUgdGhpc1vigKZdOwoJZ2xvYi5tb2RlX3NraW4oKTsgaWYgKGdsb2IucHJlZigic2lnbm9uLnJlbWVtYmVyU2lnbm9ucyIpKQoJCVNlcnZpY2VzLmNhY2hlMi5hc3luY0dldERpc2tDb25zdW1wdGlvbih7b25OZXR3b3JrQ2FjaGVEaXNrQ29uc3VtcHRpb24oYnl0ZXMpIHsKCQkJZ2xvYi50b1N0YXR1cyhUWzNdICsgZ2xvYi5mb3JtYXRCeXRlcyhieXRlcyksM2UzKSAvLyDQstGL0LLQvtC0INC+0LHRitGR0LzQsCDQutGN0YjQsAoJCX0sIFF1ZXJ5SW50ZXJmYWNlOiBDaHJvbWVVdGlscy5nZW5lcmF0ZVFJKFsibnNJU3VwcG9ydHNXZWFrUmVmZXJlbmNlIiwibnNJQ2FjaGVTdG9yYWdlQ29uc3VtcHRpb25PYnNlcnZlciJdKX0pCgllbHNlIGdsb2IudG9TdGF0dXMoVFs1XSwyZTMpOyAvL9C90LUg0YXRgNCw0L3QuNGC0Ywg0L/QsNGA0L7Qu9C4CglyZXR1cm4gdEV4cChCWzFdKTsKfSwKZ2V0IFtCWzNdXSgpIHsKCXJldHVybiBHZXREeW5hbWljU2hvcnRjdXRUb29sdGlwVGV4dChCWzNdKSArIFRhZ1tCWzNdXTsKfSwKZ2V0IFtCWzBdXSgpIHt2YXIgZHcgPSBnbG9iLmRpcnN2Y2dldCgiIik7CglpZiAoZHcpIGdsb2IubW9kZV9za2luKGAke2dsb2IucHJlZihGZi5pKSA+IDEgPyAiXHV7MjZBMX0g0JPRgNCw0YTQuNC60LAg0L7RgtC60LvRjtGH0LXQvdCwLCIgOiAi8J+SviDQv9Cw0L/QutCwIn0gW9CX0LDQs9GA0YPQt9C60LhdIGArIGdsb2IuY3JvcChkdywgOTYsJycpKTsKCXJldHVybiBHZXREeW5hbWljU2hvcnRjdXRUb29sdGlwVGV4dChCWzBdKSArIlxuIisgdEV4cChCWzBdKTsKfSwKZ2V0ICJ0YWJicm93c2VyLXRhYiIoKSB7dmFyIHRyZyA9IHdpbmRvdy5ldmVudD8udGFyZ2V0OwoJdHJnLnRvb2x0aXBUZXh0ID0gdHJnLmxhYmVsICsgVGFnW0JbMl1dOwp9LApnZXQgW0JbMTBdXSgpIHtnbG9iLm1vZGVfc2tpbignJyk7CglyZXR1cm4gR2V0RHluYW1pY1Nob3J0Y3V0VG9vbHRpcFRleHQoQlsxMF0pICsiXG5cbiIrIFRhZ1tCWzEwXV0gKyJcbiIrIHRFeHAoQlsxMl0pOwp9LApnZXQgW01bMl1dKCkge3JldHVybiBHZXREeW5hbWljU2hvcnRjdXRUb29sdGlwVGV4dChbTVsyXV0pICsiXG4iKyB0RXhwKEJbMTJdKTsKfSwKZ2V0IFtNWzBdXSgpIHtnbG9iLnRvU3RhdHVzKFRbMF0sMjUwMCk7Cn0sCltCWzldXTogVGFnW0JbOV1dLApbTVs0XV06IFRhZ1tCWzldXSwKInRpdGxlYmFyLWJ1dHRvbiB0aXRsZWJhci1jbG9zZSI6IFRhZ1tCWzEzXV0sCmdldCBbTVszXV0oKSB7Cgl2YXIgdHh0ID0gYCR7Z2xvYi5wcmVmKCJkb20uZGlzYWJsZV9vcGVuX2R1cmluZ19sb2FkIikgPyAi0JfQsNC/0YDQtdGCIiA6ICLihq8g0KDQsNC30YDQtdGIIn3QuNGC0Ywg0LLRgdC/0LvRi9Cy0LDRjtGJ0LjQtSDQvtC60L3QsGA7CglpZiAoIWdsb2IucHJlZigicGxhY2VzLmhpc3RvcnkuZW5hYmxlZCIpKSB0eHQgPSBUWzldOwoJaWYgKGdsb2IucHJlZigicHJpdmFjeS5zYW5pdGl6ZS5zYW5pdGl6ZU9uU2h1dGRvd24iKSkgdHh0ID0gVFsxMF07CglnbG9iLnRvU3RhdHVzKHR4dCwzZTMpOwp9LApnZXQgW0JbMl1dKCkgeyAvL2N1c3RvbSBoaW50CglyZXR1cm4gdG9vbHRpcF94KHdpbmRvdy5ldmVudC50YXJnZXQsIuKpiSDQoNC+0LvQuNC6IMKxCdCY0LfQvNC10L3QuNGC0Ywg0LzQsNGB0YjRgtCw0LEiKTsKfSwKZ2V0IFtCWzE0XV0oKSB7cmV0dXJuIFRhZ1tCWzE0XV0gKyBUWzE0XTt9LApnZXQgImlkZW50aXR5LWljb24tYm94IigpIHsKCXJldHVybiB0b29sdGlwX3god2luZG93LmV2ZW50LnRhcmdldCwgdEV4cChCWzRdKSArIGJyX3ZhbCgpKTsKfSwKZ2V0IFtCWzRdXSgpIHtnbG9iLnRvU3RhdHVzKHRoaXMuYnJfZXhwKCksMjUwMCk7IC8v0YDQtdC20LjQvCDQutC90L7Qv9C+0LoKCXJldHVybiB0b29sdGlwX3god2luZG93LmV2ZW50LnRhcmdldCwgdEV4cChCWzRdKSArIGJyX3ZhbCgpKTsKfSwKZ2V0IFtCWzExXV0oKSB7Z2xvYi50b1N0YXR1cyh0aGlzLmJyX2V4cCgpLDI1MDApOwoJdmFyIHRyZyA9IHdpbmRvdy5ldmVudD8udGFyZ2V0OyAvL2N1c3RvbSBoaW50IDIKCXJldHVybiB0cmcuaWQuZW5kc1dpdGgoInIiKSAmJiB0cmcudGV4dENvbnRlbnQgKydcbicrIHRFeHAoQlsxMV0pOwp9LApnZXQgW0JbNl1dKCkgeyAvL0Zhdk1lbnUKCXZhciB0cmcgPSB3aW5kb3cuZXZlbnQ/LnRhcmdldDsKCWlmICh0cmcuaWQgPT0gQls2XSkgewoJCXRyeSB7dHJnLm1zdGF0ZSA9IHRyZy5zZWNvbmRhcnlQb3B1cC5zdGF0ZTt9IGNhdGNoe30gLy/QtNC70Y8gdWNmX1F1aWNrVG9nZ2xlLmpzCgkJem9vbSgwLDAsMCxgLCAke2dsb2IucHJlZigiYnJvd3Nlci50YWJzLmxvYWRJbkJhY2tncm91bmQiKSA/ICLQndC1INCy0YvQsdC40YDQsNGC0YwiIDogItCf0LXRgNC10LrQu9GO0YfQsNGC0YzRgdGPINCyIn0g0L3QvtCy0YvQtSDQstC60LvQsNC00LrQuGApOwoJfSBlbHNlIHsKCQl0cmcubXN0YXRlID0gdHJnLnN0YXRlOwoJCWdsb2IudG9TdGF0dXMoVFs2XSw5ZTMpOwoJfQoJaWYgKHRyZy5tc3RhdGUgIT0gIm9wZW4iKQoJCXJldHVybiB0RXhwKEJbNl0pCgllbHNlIHRyZy50b29sdGlwVGV4dCA9ICIiOwp9LApnZXQgW0JbOF1dKCkgeyAvL2dldCDQvNC+0LbQtdGCINCy0YvQv9C+0LvQvdGP0YLRjCDQutC+0LQKCXJldHVybiBHZXREeW5hbWljU2hvcnRjdXRUb29sdGlwVGV4dChCWzhdKSArIlxuIisgVGFnW0JbOF1dICsgYnJfdmFsKCk7Cn0sCmdldCBbTVs1XV0oKSB7CglyZXR1cm4gVGFnW0JbN11dICsgVGFnW0JbOF1dICsgYnJfdmFsKCk7Cn0sCmdldCAidWNmX1Nlc3Npb25NYW5hZ2VyIigpIHtnbG9iLnRvU3RhdHVzKFRbMTFdKTt9LApnZXQgInVuaWZpZWQtZXh0ZW5zaW9ucy1idXR0b24iKCl7cmV0dXJuICLQoNCw0YHRiNC40YDQtdC90LjRjyIrIFRbMTRdfSwKIl81MzE5MDZkMy1lMjJmLTRhNmMtYTEwMi04MDU3Yjg4YTFhNjNfLWJyb3dzZXItYWN0aW9uIjogVFsxMl0sIC8vU2luZ2xlRmlsZQoiXzUzMTkwNmQzLWUyMmYtNGE2Yy1hMTAyLTgwNTdiODhhMWE2M18tQkFQIjogVFsxMl0sCiJfYjlkYjE2YTQtNmVkYy00N2VjLWExZjQtYjg2MjkyZWQyMTFkXy1icm93c2VyLWFjdGlvbiI6IFRbMTNdLCAvL1ZESAoiX2I5ZGIxNmE0LTZlZGMtNDdlYy1hMWY0LWI4NjI5MmVkMjExZF8tQkFQIjogVFsxNF0sCmJyX2V4cCh0ID0gVFsyXSArIGJyX3ZhbCgpKXsKCXJldHVybiB0ICtgICR7RmYuRXhwKCkgPyAi0K3QutGB0L/QtdGA0YLQvdGL0LkiIDogItCf0YDQvtGB0YLQvtC5In0g0YDQtdC20LjQvCDQutC90L7Qv9C+0LpgfQp9OwoKd2luZG93Lmdsb2IgPSB7IC8vYWxsIFtDaHJvbWVPbmx5XS1zY3JpcHRzCglwcmVmKGtleSxzZXQsIHB0ID0ge2I6IkJvb2wiLG46IkludCIsczoiU3RyaW5nIn0pIHsgLy/QuNC70Lgga2V5ID0gW2tleSxkZWZhdWx0XQoJCWlmICh0eXBlb2Yga2V5ICE9ICJvYmplY3QiKSBrZXkgPSBba2V5XTsKCQl2YXIgdCA9IFNlcnZpY2VzLnByZWZzLmdldFByZWZUeXBlKGtleVswXSk7CgkJdCA9IHB0W3QgPT0gMTI4ID8gImIiIDogdCA9PSA2NCA/ICJuIiA6IHQgPT0gMzIgPyAicyIgOiAiIl07CgkJaWYgKCF0KSB0ID0gcHRbc2V0ICE9IHVuZGVmaW5lZCA/ICh0eXBlb2Ygc2V0KVswXSA6ICh0eXBlb2Yga2V5WzFdKVswXV07CgkJaWYgKCF0KSByZXR1cm47IGlmIChzZXQgIT0gdW5kZWZpbmVkKQoJCQlTZXJ2aWNlcy5wcmVmc1tgc2V0JHt0fVByZWZgXShrZXlbMF0sc2V0KQoJCWVsc2Ugc2V0ID0gU2VydmljZXMucHJlZnNbYGdldCR7dH1QcmVmYF0oLi4ua2V5KTsgcmV0dXJuIHNldDsKCX0sCgl1YShyZWFsID0gZmFsc2UsdWFfbXkgPSAiZ2VuZXJhbC51c2VyYWdlbnQub3ZlcnJpZGUiKSB7IC8v0YLQtdC60YPRidC40Lkg0LjQu9C4INCy0YjQuNGC0YvQuSDQrtC30LXRgNCQ0LPQtdC90YIKCQl0dHQgPSB0aGlzLnByZWYodWFfbXkpOyBTZXJ2aWNlcy5wcmVmcy5jbGVhclVzZXJQcmVmKHVhX215KTsKCQl1YSA9IENjWyJAbW96aWxsYS5vcmcvbmV0d29yay9wcm90b2NvbDsxP25hbWU9aHR0cCJdLmdldFNlcnZpY2UoQ2kubnNJSHR0cFByb3RvY29sSGFuZGxlcikudXNlckFnZW50OyAvLyDQutC+0YHRgtGL0LvRjAoJCXR0dCAmJiB0aGlzLnByZWYodWFfbXksdHR0KTsgaWYgKCF0dHQpIHR0dCA9IHVhOyBpZiAocmVhbCkgdHR0ID0gdWE7IHJldHVybiB0dHQ7Cgl9LAoJZGlyc3ZjZ2V0KCkgeyAvL9C60L7QvdGB0YLQsNC90YLQsCDQv9GD0YLQuCArIHN1YmRpcnMsINC10YHQu9C4INC/0L7RgdC7LiDQvtC/0YbQuNGPID0gIiIg0LLQtdGA0L3Rg9GC0Ywg0L/Rg9GC0YwsINC40L3QsNGH0LUg0L7RgtC60YDRi9GC0YwKCQl2YXIgZiwgZCA9IFsuLi5hcmd1bWVudHNdLCByID0gKGQuYXQoLTEpID09ICIiKTsgKHIpICYmIGQucG9wKCk7CgkJdHJ5IHtmID0gU2VydmljZXMuZGlyc3ZjLmdldChkWzBdIHx8ICJEZmx0RHdubGQiLENpLm5zSUZpbGUpO30gY2F0Y2gge2YgPSBTZXJ2aWNlcy5wcmVmcy5nZXRDb21wbGV4VmFsdWUoImJyb3dzZXIuZG93bmxvYWQuZGlyIixDaS5uc0lGaWxlKX0KCQlkLnNsaWNlKDEsIGQubGVuZ3RoKS5mb3JFYWNoKChjKT0+Zi5hcHBlbmQoYykpOwoJCWlmIChyKSByZXR1cm4gZi5wYXRoOyBmLmV4aXN0cygpICYmIGYubGF1bmNoKCk7Cgl9LAoJY3JvcChzLGN1dCA9IDMzLGNoID0gJ+KAplxuJykgeyAvL9GB0L7QutGA0LDRgtC40YLRjC/RgNCw0LfQsdC40YLRjCDRgdGC0YDQvtC60YMKCQlyZXR1cm4gcy5zdWJzdHJpbmcoMCxjdXQpICtgJHtzLmxlbmd0aCA+IGN1dCAtIDEgPyBgJHtjaH3igKYke3Muc3Vic3RyaW5nKHMubGVuZ3RoIC0gY3V0ICsgY2gubGVuZ3RoLHMubGVuZ3RoKX1gOiAnJ31gOwoJfSwKCWZvcm1hdEJ5dGVzKGIsZCA9IDEpIHsgLy/QvtCx0YrRkdC8INCx0LDQudGC4oCm0KLQsQoJCWxldCBpID0gTWF0aC5sb2cyKGIpLzEwfDA7IHJldHVybiBwYXJzZUZsb2F0KChiLzEwMjQqKihpPWk8PTA/MDppKSkudG9GaXhlZChkKSkrYCR7aT4wPydLTUdUJ1tpLTFdOicnfWJgOwoJfSwKCWFib3V0X2NvbmZpZyhmaWx0ZXIpIHsgLy/QvdCwINC+0L/RhtC40Y4KCQl2YXIgc2V0RmlsdGVyID0gKGUsaW5wdXQgPSAoZT8udGFyZ2V0IHx8IHdpbmRvdy5jb250ZW50LmRvY3VtZW50KS5nZXRFbGVtZW50QnlJZCgiYWJvdXQtY29uZmlnLXNlYXJjaCIpKSA9PiB7CgkJCWlmIChlIHx8IGlucHV0LnZhbHVlICE9IGZpbHRlcikgaW5wdXQuc2V0VXNlcklucHV0KGZpbHRlcik7fSwKCQlmb3VuZCA9IHdpbmRvdy5zd2l0Y2hUb1RhYkhhdmluZ1VSSSgiYWJvdXQ6Y29uZmlnIix0cnVlLCB7cmVsYXRlZFRvQ3VycmVudDogdHJ1ZSwKCQkJdHJpZ2dlcmluZ1ByaW5jaXBhbDogU2VydmljZXMuc2NyaXB0U2VjdXJpdHlNYW5hZ2VyLmdldFN5c3RlbVByaW5jaXBhbCgpfSk7CgkJaWYgKGZvdW5kKSBzZXRGaWx0ZXIobnVsbCx3aW5kb3cpOwoJCWVsc2UgZ0Jyb3dzZXIuc2VsZWN0ZWRCcm93c2VyLmFkZEV2ZW50TGlzdGVuZXIoInBhZ2VzaG93IixzZXRGaWx0ZXIsIHtvbmNlOiB0cnVlfSk7Cgl9LAoJdG9TdGF0dXModHh0LHRpbWUgPSA1ZTMsU3RhdHVzUGFuZWwgPSB3aW5kb3cuU3RhdHVzUGFuZWwpIHsKCQlpZiAoU3RhdHVzUGFuZWwudXBkYXRlLnRpZCkKCQkJY2xlYXJUaW1lb3V0KFN0YXR1c1BhbmVsLnVwZGF0ZS50aWQpCgkJZWxzZSB7CgkJCXZhciB7dXBkYXRlfSA9IFN0YXR1c1BhbmVsOwoJCQlTdGF0dXNQYW5lbC51cGRhdGUgPSAoKSA9PiB7fTsKCQkJU3RhdHVzUGFuZWwudXBkYXRlLnJldCA9ICgpID0+IHsKCQkJCVN0YXR1c1BhbmVsLnVwZGF0ZSA9IHVwZGF0ZSxTdGF0dXNQYW5lbC51cGRhdGUoKTsKCQl9fQoJCVN0YXR1c1BhbmVsLnVwZGF0ZS50aWQgPSBzZXRUaW1lb3V0KFN0YXR1c1BhbmVsLnVwZGF0ZS5yZXQsdGltZSk7CgkJU3RhdHVzUGFuZWwuX2xhYmVsID0gdHh0OwoJfSwKCWZsYXNoKGlkLHN0eWxlLGNvbG9yLG1zLHRleHQsdGltZSA9IDVlMykgeyAvL9GB0YLQsNGC0YPRgSwg0LzQuNCz0LDQvdC40LUKCQlpZCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGlkIHx8ICd1cmxiYXItaW5wdXQtY29udGFpbmVyJyk7IGlmICghaWQpIHJldHVybjsKCQlpZiAoc3R5bGUpIGlkLnN0eWxlLmZpbHRlciA9IHN0eWxlOyBpZiAoY29sb3IpIGlkLnN0eWxlLmJhY2tncm91bmQgPSBjb2xvcjsKCQlpZiAobXMpIHNldFRpbWVvdXQoKCkgPT4gewoJCQlpZC5zdHlsZS5yZW1vdmVQcm9wZXJ0eSgnZmlsdGVyJyksaWQuc3R5bGUucmVtb3ZlUHJvcGVydHkoJ2JhY2tncm91bmQtY29sb3InKTt9LG1zKTsKCQlpZiAodGV4dCkgZ2xvYi50b1N0YXR1cyh0ZXh0LHRpbWUpOwoJfSwKCW1vZGVfc2tpbih0ZXh0LHAgPSB0aGlzLnByZWYoJ25ldHdvcmsucHJveHkudHlwZScpLHQscyA9ICd1bnNldCcsbyA9ICcnLHopIHt3aXRoKGdsb2IpewoJCWlmIChwcmVmKCJkb20uc2VjdXJpdHkuaHR0cHNfb25seV9tb2RlIikpCgkJCWZsYXNoKEJbMTBdLCJkcm9wLXNoYWRvdygwcHggMC41cHggMHB4ICNGOEYpIiksbyA9ICcsINGC0L7Qu9GM0LrQviBIVFRQUycKCQllbHNlIGZsYXNoKEJbMTBdLCJub25lIik7CgkJaWYgKHVhKCkgJiYgKHVhKCkgIT0gdWEodHJ1ZSkpKSBvID0gbyArJywg0YfRg9C20L7QuSDQrtC30LXRgNCQ0LPQtdC90YInOwoJCXogPSBwcmVmKCJuZXR3b3JrLnByb3h5Lm5vX3Byb3hpZXNfb24iKSA9PSAiIiA/ICIiIDogIiwg0JXRgdGC0Ywg0YHQsNC50YLRiy3QuNGB0LrQu9GO0YfQtdC90LjRjyI7CgkJaWYgKHAgPT0gMSkgdCA9IFsnc2VwaWEoMTAwJSkgc2F0dXJhdGUoMTUwJSkgYnJpZ2h0bmVzcygwLjkpJywgJ9Cg0YPRh9C90LDRjyDQvdCw0YHRgtGA0L7QudC60LAg0L/RgNC+0LrRgdC4Jysgel07CgkJZWxzZSBpZiAocCA9PSAyKSB0ID0gWydodWUtcm90YXRlKDEyMGRlZykgc2F0dXJhdGUoNzAlKScsVFsxXSArIHpdLHMgPSAnaHVlLXJvdGF0ZSgyNzBkZWcpIGJyaWdodG5lc3MoOTUlKSc7CgkJZWxzZSBpZiAocCA9PSA0KSB0ID0gWydodWUtcm90YXRlKDI1MGRlZykgYnJpZ2h0bmVzcygwLjk1KSBzYXR1cmF0ZSgxNTAlKScsJ9Ch0LXRgtGMIC0g0LDQstGC0L7QvdCw0YHRgtGA0L7QudC60LAg0L/RgNC+0LrRgdC4Jysgel07CgkJZWxzZSBpZiAocCA9PSAwKSB0ID0gWydzYXR1cmF0ZSgwJSkgYnJpZ2h0bmVzcygwLjk1KScsJ9Cd0LDRgdGC0YDQvtC50LrQuCDRgdC10YLQuCAtINGB0LjRgdGC0LXQvNC90YvQtScrIHpdCgkJZWxzZSB0ID0gW3MsJ9Ch0LXRgtGMINGA0LDQsdC+0YLQsNC10YIg0LHQtdC3INC/0YDQvtC60YHQuCddOyAvLyDRgdC10YDRi9C5INGE0L7QvSDQutC90L7Qv9C60LgKCQlmbGFzaChCWzBdLHByZWYoRmYuaSkgPiAxID8gImh1ZS1yb3RhdGUoMTgwZGVnKSBkcm9wLXNoYWRvdygwcHggMC41cHggMHB4ICNGNjgpIiA6ICJub25lIik7CgkJZmxhc2goQls2XSxzKTsgZmxhc2goQlsxXSx0WzBdKTsKCQl6ID0gdHlwZW9mKHRleHQpOyBpZiAoeiA9PSAnc3RyaW5nJykKCQkJdG9TdGF0dXModGV4dCA/IHRleHQgOiAiXHV7MjZBMX0iKyB0WzFdICsgbyw1ZTMpOyAvLyDRgdC40LzQstC+0Lsg0JLQvdC40LzQsNC90LjQtQoJfX0KfTsKKChvYmosZGVsLHJlLHJlb3MpID0+IHsgLy8g0L/QsNGA0YHQuNC90LMg0LHQu9C+0LrQsCDQutC70LDQstC40Ygg0YPRgdC60L7RgNGP0LXRgiDQvtCx0YDQsNCx0L7RgtC60YMg0L3QsNC20LDRgtC40LkKCXZhciBudW0gPSAtRmYub3MubGVuZ3RoIC0gMTsKCWZvcih2YXIgcCBpbiBrbGFCYSkgcmVvcy50ZXN0KHApICYmIGRlbC5hZGQoIHAuZW5kc1dpdGgocGxhdGZvcm0pID8gcC5zbGljZSgwLG51bSkgOiBwKTsKCWZvcih2YXIgcCBpbiBrbGFCYSkgZGVsLmhhcyhrbGFCYVtwXSkgJiYgZGVsLmFkZChwKTsKCWZvcih2YXIgZCBvZiBkZWwpIGRlbGV0ZSBrbGFCYVtkXTsgLy/QtdGB0YLRjCBLZXlfT1MgPyDRg9C00LDQu9C40YLRjCDQuNC80LXQvdCwLdC60LvQvtC90YsKCWZvcih2YXIgcCBpbiBrbGFCYSkgaWYgKHJlb3MudGVzdChwKSkKCQkJa2xhQmFbcC5yZXBsYWNlKHJlb3MsJycpXSA9IGtsYUJhW3BdLCBkZWxldGUga2xhQmFbcF07IC8v0YPQsdGA0LDRgtGMINC40LzRjyDQstCw0YjQtdC5INCe0KEg0LjQtyDRgdCy0L7QudGB0YLQstCwCglmb3IodmFyIHAgaW4ga2xhQmEpIHt2YXIgZnVuYyA9IGtsYUJhW3BdOyAvLygpe+KApn0sIGJvb2wsbnVtCgkJaWYgKHR5cGVvZiBmdW5jID09ICJzdHJpbmciKSBmdW5jID0ga2xhQmFbZnVuY107IC8v0YHRgdGL0LvQutCwINC90LAg0YTRg9C90LrRhtC40Y4KCQl2YXIgW2tleSxtb2RdID0gcC5zcGxpdCgiXyIpOyBtb2QgPSBtb2QgfHwgIiI7CgkJdmFyIHVwcGVyID0ga2V5WzBdLnRvVXBwZXJDYXNlKCk7IHZhciBwcmV2ZW50ID0ga2V5WzBdID09IHVwcGVyOwoJCXZhciBbLCBtLGldID0gbW9kLm1hdGNoKHJlKTsgbSA9IG0gfHwgMDsgLy9tb2RpZmllcnMgYml0bWFwCgkJdmFyIGFyciA9IFtmdW5jLHByZXZlbnQsIGkgPyBpID09ICJJIiA/IDAgOiAxIDogLTFdOyAvL3RleHRmaWVsZHMgZmxhZyDQmNC80Y9faSAxINC60YDQvtC80LUg0L/QvtC70LXQuSDQstCy0L7QtNCwCgkJdmFyIHByb3AgPSBwcmV2ZW50ID8ga2V5IDogdXBwZXIgKyBrZXkuc2xpY2UoMSk7IC8v0LjQvNGPINCx0LXQtyBtb2QKCQl2YXIgbyA9IG9ialtwcm9wXSB8fCAob2JqW3Byb3BdID0gT2JqZWN0LmNyZWF0ZShudWxsKSk7CgkJb1ttXSA/IG9bbV0ucHVzaChhcnIpIDogb1ttXSA9IFthcnJdOyAvL9C40LzRjyDRgdC+INGB0YLRgNC+0YfQvdC+0Lk6IFNraXAgcHJldmVudERlZmF1bHQKCX07IGtsYUJhID0gb2JqOyB9KShPYmplY3QuY3JlYXRlKG51bGwpLG5ldyBTZXQoKSwvKFxkKyk/KGkpPy9pLC9fKD86d2lufGxpbnV4fG1hY29zeCkkLyk7CmRhdGEgPSB7fTsgTS5mb3JFYWNoKChrKSA9PntkYXRhWyIjIisga10gPSBkYXRhWyIuIisga10gPSBNb3VzZVtrXX0pOwoKdmFyIERlYnVnID0gKGUsaWQgPSAic2lkZWJhci1ib3giKSA9PiB7CglpZiAoU2VydmljZXMucHJlZnMuZ2V0Qm9vbFByZWYoRmYucCArJ2RlYnVnJyxmYWxzZSkpIHJldHVybiB0cnVlOwoJcmV0dXJuICFkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCkuaGlkZGVuOwp9LAprZXlkb3duX3dpbiA9IGUgPT4geyAvL9C/0LXRgNC10YXQstCw0YIg0LrQu9Cw0LLQuNGILCDRg9GH0LjRgtGL0LLQsNGPINC/0L7Qu9GPINCy0LLQvtC00LAKCWlmIChlLnJlcGVhdCkgcmV0dXJuOyAvLyDQstGL0LrQu9GO0YfQuNGC0YwgZS5nZXRNb2RpZmllclN0YXRlKCJDYXBzTG9jayIpCgl2YXIgZGF0YSA9IGtsYUJhW2UuY29kZV0/LltlLm1ldGFLZXkqOCArIGUuY3RybEtleSo0ICsgZS5zaGlmdEtleSoyICsgZS5hbHRLZXldOwoJaWYgKGRhdGEpIC8v0LXRgdGC0YwgSG90S2V5CgkJZm9yKHZhciBbZnVuYyxwLGldIG9mIGRhdGEpCgkJCWlmIChpIF4gZG9jU2hlbGwuaXNDb21tYW5kRW5hYmxlZCgiY21kX2luc2VydFRleHQiKSkKCQkJCXAgJiYgZS5wcmV2ZW50RGVmYXVsdCgpLCBmdW5jKGUsIGdCcm93c2VyLnNlbGVjdGVkVGFiKTsgLy/Qt9Cw0L/Rg9GB0Log0L/QviDRgdC+0YfQtdGC0LDQvdC40Y4KCWlmICghRGVidWcoKSkgcmV0dXJuOyAvL9C/0L7QutCw0Lcg0LrQu9Cw0LLQuNGICgljb25zb2xlLmxvZygn4pagIGtleSAnKyBlLmNvZGUgKyAoJ18nKyAoZS5tZXRhS2V5KjggKyBlLmN0cmxLZXkqNCArIGUuc2hpZnRLZXkqMiArIGUuYWx0S2V5KSkucmVwbGFjZSgnXzAnLCcnKSk7Cn07Cmxpc3RlbmVyID0geyAvL9C00LXQudGB0YLQstC40Y8g0LzRi9GI0LgsINC/0LXRgNC10YXQstCw0YIg0YHRg9GJ0LXRgdGC0LLRg9GO0YnQuNGFCgloYW5kbGVFdmVudChlKSB7CgkJaWYgKHRoaXMuc2tpcCB8fCBlLmRldGFpbCA+IDEpIHJldHVybjsKCQl2YXIgdHJnID0gZS50YXJnZXQsIGlkID0gdHJnLmlkIHx8IHRyZy5jbGFzc05hbWUgfHwgdHJnLnRhZ05hbWU7CgkJaWYgKGUudHlwZSA9PSAibW91c2VlbnRlciIpIHsKCQkJdmFyIGhpbnQgPSBPdmVyW2lkXSB8fCBPdmVyWyh0cmcgPSB0cmcucGFyZW50Tm9kZSkuaWRdOwoJCQlpZiAoaGludCkgdHJnLnRvb2x0aXBUZXh0ID0gaGludDsgcmV0dXJuOyAvL9C+0LHQvdC+0LLQuNGC0Ywg0L/QvtC00YHQutCw0LfQutGDCgkJfQoJCXZhciBzZWxzID0gdGhpcy5zZWxlY3RvcnMuZmlsdGVyKHRoaXMuZmlsdGVyLCB0cmcpOyAvLyNpZAoJCXZhciB7bGVuZ3RofSA9IHNlbHM7IGlmICghbGVuZ3RoKSByZXR1cm47CgkJdmFyIHdoZWVsID0gZS50eXBlLnN0YXJ0c1dpdGgoInciKTsKCQl2YXIgbnVtID0gZS5tZXRhS2V5KjY0ICsgZS5jdHJsS2V5KjMyICsgZS5zaGlmdEtleSoxNiArIGUuYWx0S2V5KjggKyAod2hlZWwgPyAyIDogZS5idXR0b24qMTI4KTsgLy9kYmwqNAoJCXZhciBvYmogPSBkYXRhWwoJCQlsZW5ndGggPiAxICYmIHNlbHMuZmluZCh0aGlzLmZpbmQsbnVtKSB8fCBzZWxzWzBdCgkJXTsKCQlEZWJ1ZygpICYmIGNvbnNvbGUubG9nKCfilqAgYnV0IMKrJysgaWQgKyfCuyBrZXkgJysgbnVtKTsgLy93aGVlbCDQtNCy0LDQttC00YsKCQlpZiAod2hlZWwpIHJldHVybiBvYmpbbnVtXT8uKHRyZywgZS5kZWx0YVkgPCAwKTsKLy8gbW91c2Vkb3duCgkJaWYgKGUudHlwZS5zdGFydHNXaXRoKCJtIikpIHsKCQkJb2JqLm1vdXNlZG93blRhcmdldCAmJiB0aGlzLnN0b3AoZSk7CgkJCXRoaXMubG9uZ1ByZXNzID0gZmFsc2U7IC8vKyDQt9Cw0LTQtdGA0LbQutCwINC/0YDQuCDQvtCx0YvRh9C90L7QvCDQutC70LjQutC1CgkJCWlmICgrK251bSBpbiBvYmopCgkJCQl0aGlzLm1vdXNlZG93blRJRCA9IHNldFRpbWVvdXQodGhpcy5vbkxvbmdQcmVzcyw2NDAsIHRyZyxvYmosbnVtKTsKCQkJaWYgKGUuYnV0dG9uID09IDIpCgkJCQl0aGlzLmN0eCA9IHRyZy5nZXRBdHRyaWJ1dGUoImNvbnRleHQiKSwgdHJnLnNldEF0dHJpYnV0ZSgiY29udGV4dCIsIiIpOwoJCQlyZXR1cm47CgkJfQoJCW9iai5tb3VzZWRvd25UYXJnZXQgfHwgdGhpcy5zdG9wKGUpOwkvL2NsaWNrCgkJaWYgKHRoaXMubG9uZ1ByZXNzKSByZXR1cm4gdGhpcy5sb25nUHJlc3MgPSBmYWxzZTsKCQl0aGlzLm1vdXNlZG93blRJRCAmJj0gY2xlYXJUaW1lb3V0KHRoaXMubW91c2Vkb3duVElEKTsKCQlpZiAoIW9ialtudW1dKSB7CgkJCWlmIChlLmJ1dHRvbiA9PSAxKSByZXR1cm47CgkJCWlmIChlLmJ1dHRvbikgewoJCQkJbnVtID0gImNvbnRleHQiOwoJCQkJZm9yKHZhciBwIGluIHRoaXMuYSkgdGhpcy5hW3BdID0gZVtwXTsKCQkJfSBlbHNlCgkJCQludW0gPSAiZGlzcGF0Y2giLCB0aGlzLm1kdCA9IG9iai5tb3VzZWRvd25UYXJnZXQ7CgkJCW9iaiA9IHRoaXM7CgkJfQoJCW9ialtudW1dKHRyZyk7IC8vcnVuCgl9LAoJZmluZChzZWwpIHsgLy/Rg9GB0LvQvtCy0LjRjyDQt9Cw0L/Rg9GB0LrQsCA/CgkJcmV0dXJuIGRhdGFbc2VsXVt0aGlzXSB8fCBkYXRhW3NlbF1bdGhpcyArIDFdOwoJfSwKCWZpbHRlcihzZWwpIHtyZXR1cm4gdGhpcy5jbG9zZXN0KHNlbCk7Cgl9LAoJZ2V0IHNlbGVjdG9ycygpIHsKCQl0aGlzLm9uTG9uZ1ByZXNzID0gKHRyZyxvYmosbnVtKSA9PiB7CgkJCXRoaXMubW91c2Vkb3duVElEID0gbnVsbDsKCQkJdGhpcy5sb25nUHJlc3MgPSB0cnVlOwoJCQlvYmpbbnVtXSh0cmcpOwoJCX0KCQlkZWxldGUgdGhpcy5zZWxlY3RvcnM7CgkJcmV0dXJuIHRoaXMuc2VsZWN0b3JzID0gT2JqZWN0LmtleXMoZGF0YSk7Cgl9LAoJZ2V0IG1kRXZlbnQoKSB7CgkJZGVsZXRlIHRoaXMubWRFdmVudDsKCQlyZXR1cm4gdGhpcy5tZEV2ZW50ID0gbmV3IE1vdXNlRXZlbnQoIm1vdXNlZG93biIsIHtidWJibGVzOiB0cnVlfSk7Cgl9LAoJY29udGV4dCh0cmcpIHsKCQl0aGlzLmN0eCA/IHRyZy5zZXRBdHRyaWJ1dGUoImNvbnRleHQiLHRoaXMuY3R4KSA6IHRyZy5yZW1vdmVBdHRyaWJ1dGUoImNvbnRleHQiKTsKCQl0cmcuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgiY29udGV4dG1lbnUiLHRoaXMuYSkpOwoJfSwKCWRpc3BhdGNoKHRyZykgewoJCXRoaXMuc2tpcCA9IHRydWU7CgkJdGhpcy5tZHQgPyB0cmcuZGlzcGF0Y2hFdmVudCh0aGlzLm1kRXZlbnQpIDogdHJnLmNsaWNrKCk7CgkJdGhpcy5za2lwID0gZmFsc2U7Cgl9LAoJc3RvcDogZSA9PiB7CgkJZS5wcmV2ZW50RGVmYXVsdCgpOyBlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpOwoJfSwKCWE6IHtfX3Byb3RvX186IG51bGwsYnViYmxlczogdHJ1ZSxzY3JlZW5YOiAwLHNjcmVlblk6IDB9Cn0sCmlkID0gInVjZl9ob29rRXhwZXJ0IixldmVudHMgPSBbImNsaWNrIiwibW91c2Vkb3duIiwid2hlZWwiLCJtb3VzZWVudGVyIl0sCmVscyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIiNuYXZpZ2F0b3ItdG9vbGJveCwjdWNmLWFkZGl0aW9uYWwtdmVydGljYWwtYmFyLCNhcHBNZW51LXBvcHVwLCN3aWRnZXQtb3ZlcmZsb3ctbWFpblZpZXciKTsKZm9yKHZhciBlbCBvZiBlbHMpIGZvcih2YXIgdHlwZSBvZiBldmVudHMpCgkJZWwuYWRkRXZlbnRMaXN0ZW5lcih0eXBlLGxpc3RlbmVyLHRydWUpOwp3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcigia2V5ZG93biIsa2V5ZG93bl93aW4sdHJ1ZSk7CnVjZl9jdXN0b21fc2NyaXB0X3dpbi51bmxvYWRsaXN0ZW5lcnMucHVzaChpZCk7CnVjZl9jdXN0b21fc2NyaXB0X3dpbltpZF0gPSB7ZGVzdHJ1Y3RvcigpIHsKCXdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKCJrZXlkb3duIixrZXlkb3duX3dpbik7Cglmb3IodmFyIGVsIG9mIGVscykgZm9yKHZhciB0eXBlIG9mIGV2ZW50cykKCQllbC5yZW1vdmVFdmVudExpc3RlbmVyKHR5cGUsbGlzdGVuZXIsdHJ1ZSk7Cn19Owp2YXIgYWRkRGVzdHJ1Y3RvciA9IG5leHREZXN0cnVjdG9yID0+IHsgLy/QtNC70Y8gc2F2ZVNlbFRvVHh0Cgl2YXIge2Rlc3RydWN0b3J9ID0gdWNmX2N1c3RvbV9zY3JpcHRfd2luW2lkXTsKCXVjZl9jdXN0b21fc2NyaXB0X3dpbltpZF0uZGVzdHJ1Y3RvciA9ICgpID0+IHsKCQl0cnkge2Rlc3RydWN0b3IoKTt9IGNhdGNoKGV4KSB7Q3UucmVwb3J0RXJyb3IoZXgpO30KCQluZXh0RGVzdHJ1Y3RvcigpOwp9fTsKd2l0aCAoZG9jdW1lbnQpIGdldEVsZW1lbnRCeUlkKEJbMTFdKS5yZW1vdmVBdHRyaWJ1dGUoInRvb2x0aXAiKSwKCWdldEVsZW1lbnRCeUlkKCJuYXYtYmFyIikudG9vbHRpcCA9IGlkOyAvL9GE0LvQsNCzINGD0YHQv9C10YjQvdC+0Lkg0LfQsNCz0YDRg9C30LrQuAoKZ2xvYi5tb2RlX3NraW4oKTsgLy/Qv9C+0LTRgdCy0LXRgtC60LAg0LrQvdC+0L/QvtC6INC4INC/0L7QtNGB0LrQsNC30LrQuCDQvtGC0L7QsdGA0LDQttCw0Y7RgiDQvdCw0YHRgtGA0L7QudC60Lgg0LHRgNCw0YPQt9C10YDQsApnbG9iLnByZWYoJ3VpLnByZWZlcnNSZWR1Y2VkTW90aW9uJywwKTsJLy9Eb3dubG9hZEJ1dHRvbiBhbmltYXRpb24gRklYCnZhciB0YWJyID0gRmYucCArIm9wYWNpdHkiLHVybCA9IGByZXNvdXJjZTovLyR7dGFicn0vYCwgLy9icmlnaHQgdGFicwpnZXRJbnRQcmVmID0gKHApID0+IFNlcnZpY2VzLnByZWZzLmdldEludFByZWYocCwxMDApLApzc3MgPSBDY1siQG1vemlsbGEub3JnL2NvbnRlbnQvc3R5bGUtc2hlZXQtc2VydmljZTsxIl0uZ2V0U2VydmljZShDaS5uc0lTdHlsZVNoZWV0U2VydmljZSksCmNzcyA9IGBALW1vei1kb2N1bWVudCB1cmwoY2hyb21lOi8vYnJvd3Nlci9jb250ZW50L2Jyb3dzZXIueGh0bWwpIHsKCTppcygke2lkfSlbcnN0XSB7ZmlsdGVyOiBncmF5c2NhbGUoMSUpICFpbXBvcnRhbnR9Cgk6cm9vdDpub3QoW2Nocm9tZWhpZGRlbio9dG9vbGJhcl0pICN0YWJicm93c2VyLXRhYmJveCB7YmFja2dyb3VuZC1jb2xvcjogYmxhY2sgIWltcG9ydGFudH0KCTpyb290Om5vdChbY2hyb21laGlkZGVuKj10b29sYmFyXSkgI3RhYmJyb3dzZXItdGFicGFuZWxzIHtvcGFjaXR5OiR7Z2V0SW50UHJlZih0YWJyKS8xMDB9ICFpbXBvcnRhbnR9fWA7ClNlcnZpY2VzLmlvLmdldFByb3RvY29sSGFuZGxlcigicmVzb3VyY2UiKS5RdWVyeUludGVyZmFjZShDaS5uc0lSZXNQcm90b2NvbEhhbmRsZXIpCgkuc2V0U3Vic3RpdHV0aW9uKHRhYnIsU2VydmljZXMuaW8ubmV3VVJJKCJkYXRhOnRleHQvY3NzLCIrIGVuY29kZVVSSUNvbXBvbmVudChjc3MpKSk7CnNzcy5sb2FkQW5kUmVnaXN0ZXJTaGVldChTZXJ2aWNlcy5pby5uZXdVUkkodXJsKSxzc3MuVVNFUl9TSEVFVCk7CnZhciBzdCA9IEluc3BlY3RvclV0aWxzLmdldEFsbFN0eWxlU2hlZXRzKGRvY3VtZW50KS5maW5kKHMgPT4gcy5ocmVmID09IHVybCkuY3NzUnVsZXNbMF0uY3NzUnVsZXNbMl0uc3R5bGU7CnZhciBvYnNlcnZlciA9ICgpID0+IHN0LnNldFByb3BlcnR5KCJvcGFjaXR5IiwgZ2V0SW50UHJlZih0YWJyKS8xMDAsImltcG9ydGFudCIpOwpwcmVmcy5hZGRPYnNlcnZlcih0YWJyLG9ic2VydmVyKTsKdGhpcy5yZW1vdmVQcmVmT2JzID0gKCkgPT4gcHJlZnMucmVtb3ZlT2JzZXJ2ZXIodGFicixvYnNlcnZlcik7IC8vZW5kIGJyaWdodAoKdmFyIGNzc19VU0VSID0gKGNzcykgPT4geyAvL9C70L7QutCw0LvRjNC90YvQtSDRhNGD0L3QutGG0LjQuAoJdmFyIHN0eWxlID0gRmlsZUV4aXN0cyhjc3MpID8gU2VydmljZXMuaW8ubmV3VVJJKGNzcykgOiBtYWtlVVJJKCdkYXRhOnRleHQvY3NzO2NoYXJzZXQ9dXRmLTgsJysgZW5jb2RlVVJJQ29tcG9uZW50KGNzcykpOwoJdmFyIGFyZ3MgPSBbc3R5bGUsc3NzLlVTRVJfU0hFRVRdOyAvLyDRgdGC0LjQu9GMOiDRhNCw0LnQuyDQuNC70LggQ1NTCgkodGhpcy5jc3MgPSAhdGhpcy5jc3MpID8gc3NzLmxvYWRBbmRSZWdpc3RlclNoZWV0KC4uLmFyZ3MpIDogc3NzLnVucmVnaXN0ZXJTaGVldCguLi5hcmdzKTsKfSwKZ0NsaXBib2FyZCA9IHt3cml0ZShzdHIsY2ggPSBDY1siQG1vemlsbGEub3JnL3dpZGdldC9jbGlwYm9hcmRoZWxwZXI7MSJdLmdldFNlcnZpY2UoQ2kubnNJQ2xpcGJvYXJkSGVscGVyKSkgewoJCSh0aGlzLndyaXRlID0gc3RyID0+IGNoLmNvcHlTdHJpbmdUb0NsaXBib2FyZChzdHIsU2VydmljZXMuY2xpcGJvYXJkLmtHbG9iYWxDbGlwYm9hcmQpKShzdHIpO30KfSwKVGFiQWN0ID0gKGUpID0+IHtyZXR1cm4gZS5jbG9zZXN0KCIudGFiYnJvd3Nlci10YWIiKTsKfSwKc3dpdGNoVGFiID0gKHVybCA9ICdhYm91dDpzZXJ2aWNld29ya2VycycsZ28gPSBmYWxzZSkgPT4geyAvL9C+0YLQutGA0YvRgtGMINCy0LrQu9Cw0LTQutGDIHwg0LfQsNC60YDRi9GC0YwsINC10YHQu9C4INC+0YLQutGA0YvRgtCwIHwg0LLRi9Cx0YDQsNGC0YwKCWZvcih2YXIgdGFiIG9mIGdCcm93c2VyLnZpc2libGVUYWJzKQoJCWlmICh0YWIubGlua2VkQnJvd3Nlci5jdXJyZW50VVJJLnNwZWMgPT0gdXJsKQoJCQl7Z28gPyBnQnJvd3Nlci5zZWxlY3RlZFRhYiA9IHRhYiA6IGdCcm93c2VyLnJlbW92ZVRhYih0YWIpOyByZXR1cm47fQoJZ0Jyb3dzZXIuYWRkVHJ1c3RlZFRhYih1cmwpOyBnQnJvd3Nlci5zZWxlY3RlZFRhYiA9IGdCcm93c2VyLnZpc2libGVUYWJzW2dCcm93c2VyLnZpc2libGVUYWJzLmxlbmd0aCAtMV07Cn0sClRhYnNEZWwgPSAocmlnaHQgPSAwLGN1cnIgPSBnQnJvd3Nlci5zZWxlY3RlZFRhYikgPT4geyAvLyDQt9Cw0LrRgNGL0YLRjCDQstC60LvQsNC00LrQuCDRgdC70LXQstCwL9GB0L/RgNCw0LLQsCDQvtGCINCw0LrRgtC40LLQvdC+0LkKCXZhciB0YWJzID0gZ0Jyb3dzZXIudmlzaWJsZVRhYnMuZmlsdGVyKHRhYiA9PiAhdGFiLnBpbm5lZCksIGkgPSB0YWJzLmluZGV4T2YoY3Vycik7Cgl2YXIgYSA9IChpICE9IC0xKSwgYiA9IChhID8gaSArIHJpZ2h0IDogIXJpZ2h0ICogdGFicy5sZW5ndGgpOwoJYXJncyA9IHJpZ2h0ID8gW2IsIHRhYnMubGVuZ3RoXSA6IFswLGJdOwoJdGFicy5zbGljZSguLi5hcmdzKS5mb3JFYWNoKChpKSA9PiB7Z0Jyb3dzZXIucmVtb3ZlVGFiKGkpfSk7Cn0sCnN3aXRjaFByb3h5ID0gKHBhYyA9ICdodHRwczovL2FudGl6YXByZXQucHJvc3RvdnBuLm9yZy9wcm94eS5wYWMnKSA9PiB7Cgl2YXIgcG4gPSAnbmV0d29yay5wcm94eS50eXBlJyxwID0gJ25ldHdvcmsucHJveHkuYXV0b2NvbmZpZ191cmwnOwoJaWYgKGdsb2IucHJlZihwbikgIT0gMikgLy8g0LLRi9C60LvRjtGH0LjRgtGMCgkJZ2xvYi5wcmVmKHBuLDIpLCBnbG9iLnByZWYocCxwYWMpCgllbHNlCgkJZ2xvYi5wcmVmKHBuLDUpLCBnbG9iLnByZWYocCwibG9jYWxob3N0Iik7CglnbG9iLm1vZGVfc2tpbigpOyAvL9GA0LDQt9C90YvQuSDRhNC+0L0g0LfQsNC80LrQsCDQtNC70Y8g0J/RgNC+0LrRgdC4CglCcm93c2VyUmVsb2FkKCk7Cn0sClRpdGxlID0gKG1heCx0aXRsZSkgPT4geyAvL9C30LDQs9C+0LvQvtCy0L7Qui4g0LHQtdC3INC+0LHRgNC10LfQutC4OiBtYXgg0L3QtSDRg9C60LDQt9Cw0L0sINC00L7QvNC10L06IG1heCA8MCwgKyDQtNCw0YLQsDogbWF4PTAKCWlmICghdGl0bGUpIHZhciB0aXRsZSA9IGRvY3VtZW50LnRpdGxlIHx8IGdCcm93c2VyLnNlbGVjdGVkVGFiLmxhYmVsOwoJaWYgKG1heCA9PSB1bmRlZmluZWQpIHJldHVybiB0aXRsZTsgLy/QvtCz0YDQsNC90LjRh9C40YLRjCDQtNC70LjQvdGDLCDRg9Cx0YDQsNGC0Ywg0YHQu9GD0LbQtdCx0L3Ri9C1INGB0LjQvNCy0L7Qu9GLCgl0aXRsZSA9IHRpdGxlLnJlcGxhY2UoL1tcXFwvPypcIidgXSsvZywnJykucmVwbGFjZSgvXHMrL2csJyAnKS5yZXBsYWNlKC9bfDw+XSsvZywnXycpLnJlcGxhY2UoLzovZywn1oknKS50cmltKCk7CglpZiAoIG1heCA+IDAgKSByZXR1cm4gdGl0bGUuc2xpY2UoMCxtYXgpOwoJaWYgKCBtYXggPT0gMCkgcmV0dXJuIHRpdGxlLnNsaWNlKDAsMTAwKSArIl8iKyBuZXcgRGF0ZSgpLnRvTG9jYWxlRGF0ZVN0cmluZygncnUnLCB7ZGF5OidudW1lcmljJyxtb250aDonbnVtZXJpYycseWVhcjonMi1kaWdpdCd9KSArJy0nKyBuZXcgRGF0ZSgpLnRvTG9jYWxlVGltZVN0cmluZygnZW4tR0InKS5yZXBsYWNlKC86L2csItaJIik7IC8v0LTQsNGC0LAt0YfQsNGB0YsKCXZhciBob3N0ID0gZGVjb2RlVVJJQ29tcG9uZW50KGdVUkxCYXIudmFsdWUpOyAvL21heCA8IDAKCWlmICghL15maWxlOlwvXC8vLnRlc3QoaG9zdCkpIGhvc3QgPSBob3N0LnJlcGxhY2UoL14uKnVybD18aHR0cHM/OlwvXC98d3d3XC58XC8uKi9nLCcnKS5yZXBsYWNlKC9ecnVcLnxebVwufGZvcnVtXC4vLCcnKS5yZXBsYWNlKC9eY2x1YlwuZG5zLywnZG5zJyk7CglyZXR1cm4gaG9zdDsKfSwKc2F2ZSA9ICgpID0+IHsgLy/RhNGD0L3QutGG0LjRjyDQuNC3IFNpbmdsZUhUTUwuanNtCgl2YXIgYXJncyA9IFtnbG9iLmNyb3AoIuKImiDRgdGC0YDQsNC90LjRhtCwINC30LDQv9C40YHQsNC90LA6ICIrIFRpdGxlKDApLDQ4LCcnKSw3ZTNdOwoJdHJ5IHtDdS5nZXRHbG9iYWxGb3JPYmplY3QoQ3UpW1N5bWJvbC5mb3IoIlNpbmdsZUhUTUwiKV0odHJ1ZSx3aW5kb3cpOwoJCWdCcm93c2VyLnNlbGVjdGVkVGFiLnRleHRMYWJlbC5zdHlsZS50ZXh0RGVjb3JhdGlvbiA9ICJvdmVybGluZSI7IC8vIF7Qv9C+0LTRh9GR0YDQutC40LLQsNC90LjQtQoJfSBjYXRjaCB7YXJncyA9IFsn4pi5INCe0YjQuNCx0LrQsCDRhNGD0L3QutGG0LjQuCBTaW5nbGVIVE1MJywxZTRdfQoJZ2xvYi50b1N0YXR1cyguLi5hcmdzKTsKfSwKc2F2ZVNlbFRvVHh0ID0gYXN5bmMgKCkgPT4geyAvL9CyIC50eHQg0JLRgdGRINC40LvQuCDQktGL0LHRgNCw0L3QvdC+0LUKCXZhciB7bGVuZ3RofSA9IHNhdmVVUkwsIHNwbGljZSA9IGxlbmd0aCA+IDksIGwxMSA9IGxlbmd0aCA9PSAxMSwgbXNnTmFtZSA9IGlkICsgIjpTYXZlOkdldFNlbGVjdGlvbiI7IC8vRklYIEZGMTAzKwoJdmFyIHJlY2VpdmVyID0gbXNnID0+IHt2YXIgdHh0ID0gImRhdGE6dGV4dC9wbGFpbiwiKyBlbmNvZGVVUklDb21wb25lbnQoZ0Jyb3dzZXIuY3VycmVudFVSSS5zcGVjICsiXG5cbiIrIG1zZy5kYXRhKTsKCQl2YXIgYXJncyA9IFt0eHQsVGl0bGUoMCkgKycudHh0JyxudWxsLGZhbHNlLHRydWUsbnVsbCx3aW5kb3cuZG9jdW1lbnRdOwoJCXNwbGljZSAmJiBhcmdzLnNwbGljZSg1LDAsbnVsbCkgJiYgbDExICYmIGFyZ3Muc3BsaWNlKDEsMCxudWxsKTsKCQlzYXZlVVJMKC4uLmFyZ3MpOwoJCWdsb2IudG9TdGF0dXMoZ2xvYi5jcm9wKCLiiJog0YLQtdC60YHRgiDRgdC+0YXRgNCw0L3RkdC9OiAiKyBUaXRsZSgwKSw5NiwnJykpOwoJfQoJbWVzc2FnZU1hbmFnZXIuYWRkTWVzc2FnZUxpc3RlbmVyKG1zZ05hbWUscmVjZWl2ZXIpOwoJYWRkRGVzdHJ1Y3RvcigoKSA9PiBtZXNzYWdlTWFuYWdlci5yZW1vdmVNZXNzYWdlTGlzdGVuZXIobXNnTmFtZSxyZWNlaXZlcikpOwoJdmFyIHNmdW5jID0gZm0gPT4gewoJCXZhciByZXMsZmVkLHdpbiA9IHt9LGZlID0gZm0uZ2V0Rm9jdXNlZEVsZW1lbnRGb3JXaW5kb3coY29udGVudCx0cnVlLHdpbik7CgkJdmFyIHNlbCA9ICh3aW4gPSB3aW4udmFsdWUpLmdldFNlbGVjdGlvbigpOwoJCWlmIChzZWwuaXNDb2xsYXBzZWQpIHsKCQkJdmFyIGVkID0gZmUgJiYgZmUuZWRpdG9yOwoJCQlpZiAoZWQgJiYgZWQgaW5zdGFuY2VvZiBDaS5uc0lFZGl0b3IpCgkJCQlzZWwgPSBlZC5zZWxlY3Rpb24sIGZlZCA9IGZlOwoJCX0KCQlpZiAoc2VsLmlzQ29sbGFwc2VkKQoJCQlmZWQgJiYgZmVkLmJsdXIoKSxkb2NTaGVsbC5kb0NvbW1hbmQoImNtZF9zZWxlY3RBbGwiKSwKCQkJcmVzID0gd2luLmdldFNlbGVjdGlvbigpLnRvU3RyaW5nKCksZG9jU2hlbGwuZG9Db21tYW5kKCJjbWRfc2VsZWN0Tm9uZSIpLAoJCQlmZWQgJiYgZmVkLmZvY3VzKCk7CgkJcmVzID0gcmVzIHx8IHNlbC50b1N0cmluZygpOwoJCS9cUy8udGVzdChyZXMpICYmIHNlbmRBc3luY01lc3NhZ2UoInNhdmVTZWxUb1R4dCIscmVzKTsKCX0KCXZhciB1cmwgPSAiZGF0YTo7Y2hhcnNldD11dGYtOCwiKyBlbmNvZGVVUklDb21wb25lbnQoYCgke3NmdW5jfSlgLnJlcGxhY2UoInNhdmVTZWxUb1R4dCIsbXNnTmFtZSkpICsnKENjWyJAbW96aWxsYS5vcmcvZm9jdXMtbWFuYWdlcjsxIl0uZ2V0U2VydmljZShDaS5uc0lGb2N1c01hbmFnZXIpKTsnOwoJKHNhdmVTZWxUb1R4dCA9ICgpID0+IGdCcm93c2VyLnNlbGVjdGVkQnJvd3Nlci5tZXNzYWdlTWFuYWdlci5sb2FkRnJhbWVTY3JpcHQodXJsLGZhbHNlKSkoKTsKfSwKb3BlbkRpYWwgPSAoYXJncyA9IFtGZi5jICsib3B0aW9ucy9wcmVmc193aW4ueGh0bWwiLCJ1c2VyX2Nocm9tZV9wcmVmczp3aW5kb3ciLCJjZW50ZXJzY3JlZW4scmVzaXphYmxlLGRpYWxvZz1ubyJdKSA9PiB3aW5kb3cub3BlbkRpYWxvZyguLi5hcmdzKSwgLy/QuNC70LggYWJvdXQ6dXNlci1jaHJvbWUtZmlsZXMKdG9vbHRpcF94ID0gKHRyZyx0ZXh0ID0gIiIsIHR0dCA9ICIiKSA9PiB7CglpZiAoIXRyZy5pZC5lbmRzV2l0aCgieCIpKSB7IC8vYm94CgkJdHR0ID0gKHRyZy5oYXNBdHRyaWJ1dGUoInRvb2x0aXB0ZXh0IikpID8gdHJnLnR0dCA9IHRyZy50b29sdGlwVGV4dCA6IHRyZy50dHQ7CgkJaWYgKHR0dCAmJiB0dHQuaW5kZXhPZih0ZXh0KSA9PSAtMSkgdHR0ICs9ICJcblxuIjsKCQl0cmcucmVtb3ZlQXR0cmlidXRlKCJ0b29sdGlwdGV4dCIpOwoJfQoJcmV0dXJuICh0dHQuaW5kZXhPZih0ZXh0KSA9PSAtMSkgPyB0dHQgKyB0ZXh0IDogdHR0Owp9LApicmlnaHQgPSAodHJnLGZvcndhcmQsc3RlcCA9IDEsdmFsKSA9PiB7IC8vd2hlZWwKCWlmICghdmFsKSB2YXIgdmFsID0gZ2V0SW50UHJlZih0YWJyKSArIChmb3J3YXJkID8gc3RlcCA6IC1zdGVwKTsKCXZhbCA9IHZhbCA+IDEwMCA/IDEwMCA6IHZhbCA8IDE1ID8gMTUgOiB2YWw7CglnbG9iLnByZWYodGFicix2YWwpOyB0cmcudG9nZ2xlQXR0cmlidXRlKCJyc3QiKTsgZ2xvYi50b1N0YXR1cyhUWzJdICsgdmFsICsiJSIsMWUzKTsKfSwKYnJfdmFsID0gKCkgPT4gZ2xvYi5wcmVmKFt0YWJyLDEwMF0pICsiJSIsCnpvb20gPSAoZm9yd2FyZCx0b2dnbGUgPSBmYWxzZSwgY2hhbmdlID0gdHJ1ZSx0ZXh0ID0gJycpID0+IHsKCXRvZ2dsZSA/IFpvb21NYW5hZ2VyLnRvZ2dsZVpvb20oKSA6IGNoYW5nZSA/IGZvcndhcmQgPyBGdWxsWm9vbS5lbmxhcmdlKCkgOiBGdWxsWm9vbS5yZWR1Y2UoKSA6IDA7CglnbG9iLnRvU3RhdHVzKCLCsSDQnNCw0YHRiNGC0LDQsSAiKyBNYXRoLnJvdW5kKFpvb21NYW5hZ2VyLnpvb20qMTAwKSArYCUke2dsb2IucHJlZigiYnJvd3Nlci56b29tLmZ1bGwiKSA/ICIiIDogIiAo0YLQvtC70YzQutC+INGC0LXQutGB0YIpIn1gICsgdGV4dCwzZTMpOwp9LApFeHBlcnQgPSAobSA9IEJvb2xlYW4oRmYuRXhwKCkpLHAgPSBGZi5wICsnZXhwZXJ0JykgPT4gewoJZ2xvYi5wcmVmKHAsIW0pOyBnbG9iLnRvU3RhdHVzKE92ZXIuYnJfZXhwKCIiKSwzZTMpOwp9LApIZWxwID0gKGhlbHAgPSBGZi5jICsiaGVscC5odG1sIikgPT4geyAvL9C/0L7QvNC+0YnRjAoJKEZpbGVFeGlzdHMoaGVscCkpID8gc3dpdGNoVGFiKGhlbHApIDogc3dpdGNoVGFiKCd2aWN0b3ItZG9icm92Lm5hcm9kLnJ1L2hlbHAtRkYuaHRtbCcpOwp9LApGaWxlRXhpc3RzID0gKGZpbGUpID0+IHt0cnkgeyAvL9GE0LDQudC7fNC/0LDQv9C60LAg0YHRg9GJ0LXRgdGC0LLRg9C10YI/CglpZiAoIWZpbGUuc3RhcnRzV2l0aCgiY2hyb21lOi8vIikpCgkJcmV0dXJuIEZpbGVVdGlscy5GaWxlKFN0cmluZy5yYXdgJHtmaWxlfWApLmV4aXN0cygpCgllbHNlIHJldHVybiBDY1siQG1vemlsbGEub3JnL2Nocm9tZS9jaHJvbWUtcmVnaXN0cnk7MSJdLmdldFNlcnZpY2UoQ2kubnNJWFVMQ2hyb21lUmVnaXN0cnkpLmNvbnZlcnRDaHJvbWVVUkwoU2VydmljZXMuaW8ubmV3VVJJKGZpbGUpKS5RdWVyeUludGVyZmFjZShDaS5uc0lGaWxlVVJMKS5maWxlLmV4aXN0cygpOwoJfSBjYXRjaCB7fTsgcmV0dXJuIGZhbHNlOwp9LAp1c2VyanMgPSAoZSxteWpzID0gRmYuYyArImN1c3RvbV9zY3JpcHRzL1VzZXIuanMiKSA9PiB7CglEZWJ1ZygpICYmIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJrZXlfYnJvd3NlckNvbnNvbGUiKS5kb0NvbW1hbmQoKTsgLy/RhNC+0LrRg9GBINC90LAg0LrQvtC90YHQvtC70YwKCUZpbGVFeGlzdHMobXlqcykgPyBldmFsKEN1LnJlYWRVVEY4VVJJKFNlcnZpY2VzLmlvLm5ld1VSSShteWpzKSkpIDogdCA9IG15anMgKyIg4oCUINGB0LrRgNC40L/RgiDQvdC1INC90LDQudC00LXQvSI7Cgljb25zb2xlLmxvZygi4pyFXHQiKyBNYXRoLnJhbmRvbSgpLCIiKTsgLy/QstCw0Ygg0YHQutGA0LjQv9GCCn0sCnRyYW5zbGF0ZSA9IChick1NID0gZ0Jyb3dzZXIuc2VsZWN0ZWRCcm93c2VyLm1lc3NhZ2VNYW5hZ2VyKSA9PiB7IC8vINC/0LXRgNC10LLQvtC0INGB0LDQudGCIHwg0LLRi9C00LXQuy4g0YLQtdC60YHRggoJYnJNTS5hZGRNZXNzYWdlTGlzdGVuZXIoJ2dldFNlbGVjdCcsbGlzdGVuZXIgPSAobXNnKSA9PnsKCQlpZiAobXNnLmRhdGEpIC8vINCf0LXRgNC10LLQvtC0INCy0YvQtNC10LvQtdC90L3QvtCz0L4KCQkJc3dpdGNoVGFiKCJodHRwczovL3RyYW5zbGF0ZS5nb29nbGUuY29tLyN2aWV3PWhvbWUmb3A9dHJhbnNsYXRlJnNsPWF1dG8mdGw9cnUmdGV4dD0iKyBtc2cuZGF0YSx0cnVlKQoJCWVsc2UgLy8g0JPRg9Cz0Lsg0LjQu9C4INCf0LXRgNC10LLQvtC0INGB0LDQudGC0LAg0LIg0K/QvdC00LXQutGBCgkJCXN3aXRjaFRhYigiaHR0cHM6Ly90cmFuc2xhdGUueWFuZGV4LmNvbS90cmFuc2xhdGU/dXJsPSIrIGdVUkxCYXIudmFsdWUgKyImZGlyPSZ1aT1ydSZsYW5nPWF1dG8tcnUiLHRydWUpOwoJYnJNTS5yZW1vdmVNZXNzYWdlTGlzdGVuZXIoJ2dldFNlbGVjdCcsbGlzdGVuZXIsdHJ1ZSk7Cgl9KTsKCWJyTU0ubG9hZEZyYW1lU2NyaXB0KCdkYXRhOixzZW5kQXN5bmNNZXNzYWdlKCJnZXRTZWxlY3QiLGNvbnRlbnQuZG9jdW1lbnQuZ2V0U2VsZWN0aW9uKCkudG9TdHJpbmcoKSknLGZhbHNlKTsKfSwKb3BlblByb3h5V2luID0gKF93aW4gPSBTZXJ2aWNlcy53bS5nZXRNb3N0UmVjZW50V2luZG93KCJhVGFCOlByb3h5V2luIikpID0+IHsKCWlmIChfd2luKSBfd2luLmZvY3VzKCkKCWVsc2UgewoJCV93aW4gPSBvcGVuRGlhbChbImNocm9tZTovL2Jyb3dzZXIvY29udGVudC9wcmVmZXJlbmNlcy9kaWFsb2dzL2Nvbm5lY3Rpb24ueGh0bWwiLCJfYmxhbmsiLCJjaHJvbWUsZGlhbG9nPW5vLGNlbnRlcnNjcmVlbixyZXNpemFibGUiXSk7CgkJX3dpbi5hZGRFdmVudExpc3RlbmVyKCJET01Db250ZW50TG9hZGVkIiwoKSA9PiB7CgkJCV93aW4uZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50LnNldEF0dHJpYnV0ZSgid2luZG93dHlwZSIsImFUYUI6UHJveHlXaW4iKTsKCQl9LHtvbmNlOiB0cnVlfSk7CgkJX3dpbi5vcGVuZXIgPSB3aW5kb3c7IF93aW4ub3BlbmVyLmdTdWJEaWFsb2cgPSB7X2RpYWxvZ3M6IFtdfTt9Cn0sCkZhdkl0ZW0gPSAoZmlyc3QgPSB0cnVlLGRlZl91cmwgPSAndWEucnUnKSA9PiB7IC8v0L/QtdGA0LLRi9C5fNC/0L7RgdC7LiB1cmwg0JzQtdC90Y4g0LfQsNC60LvQsNC00L7QugoJdmFyIHF1ZXJ5ID0ge30sIG9wdGlvbnMgPSB7fSwgZm9sZGVyID0gUGxhY2VzVXRpbHMuaGlzdG9yeS5leGVjdXRlUXVlcnkocXVlcnkudmFsdWUsIG9wdGlvbnMudmFsdWUpLnJvb3Q7CglQbGFjZXNVdGlscy5oaXN0b3J5LnF1ZXJ5U3RyaW5nVG9RdWVyeShgcGxhY2U6cGFyZW50PSR7UGxhY2VzVXRpbHMuYm9va21hcmtzLm1lbnVHdWlkfSZleGNsdWRlUXVlcmllcz0xYCxxdWVyeSxvcHRpb25zKTsKCWZvbGRlci5jb250YWluZXJPcGVuID0gdHJ1ZTsKCXZhciBtYXggPSBmb2xkZXIuY2hpbGRDb3VudCAtIDEsIHR5cGUgPSBDaS5uc0lOYXZIaXN0b3J5UmVzdWx0Tm9kZS5SRVNVTFRfVFlQRV9VUkksIHVybDsKCWlmIChmaXJzdCkgZm9yKHZhciBpbmQgPSAwOyBpbmQgPD0gbWF4OyBpbmQrKykgeyAvLyBmaXJzdAoJCXZhciBub2RlID0gZm9sZGVyLmdldENoaWxkKGluZCk7CgkJaWYgKG5vZGUudHlwZSA9PSB0eXBlKSB7dXJsID0gbm9kZS51cmk7IGJyZWFrO30KCX0gZWxzZQkJZm9yKHZhciBpbmQgPSBtYXg7IDAgPD0gaW5kOyBpbmQtLSkgeyAvLyBsYXN0CgkJdmFyIG5vZGUgPSBmb2xkZXIuZ2V0Q2hpbGQoaW5kKTsKCQlpZiAobm9kZS50eXBlID09IHR5cGUpIHt1cmwgPSBub2RlLnVyaTsgYnJlYWs7fQoJfQoJaWYgKCF1cmwpIHVybCA9IGRlZl91cmw7IHJldHVybiB1cmw7Cn0sCnRvRmF2ID0gKCkgPT4ge3dpdGggKFBsYWNlc1V0aWxzLmJvb2ttYXJrcyl7IC8v0LHQtdC3INC00LjQsNC70L7Qs9CwCgl2YXIgdXJsID0gZ0Jyb3dzZXIuc2VsZWN0ZWRCcm93c2VyLmN1cnJlbnRVUkkuc3BlYzsKCXNlYXJjaCh7dXJsfSkudGhlbihhc3luYyBhcnJheSA9PiB7CgkJaWYgKGFycmF5Lmxlbmd0aCkKCQkJdHJ5IHthd2FpdCByZW1vdmUoYXJyYXkpO30gY2F0Y2gge30KCQllbHNlCgkJCXRyeSB7YXdhaXQgaW5zZXJ0KHsKCQkJCXVybDogU2VydmljZXMuaW8ubmV3VVJJKHVybCksCgkJCQl0aXRsZTogKGdCcm93c2VyLmNvbnRlbnRUaXRsZSB8fCBnQnJvd3Nlci5zZWxlY3RlZFRhYi5sYWJlbCB8fCB1cmwpLAoJCQkJcGFyZW50R3VpZDogWygpID0+IHRvb2xiYXJHdWlkLCAoKSA9PiBtZW51R3VpZCwgKCkgPT4gdW5maWxlZEd1aWRdW1NlcnZpY2VzLnByZWZzLmdldEludFByZWYoImJvb2ttYXJrc3BhcmVudGd1aWQiLDApXSgpLAoJCQkJaW5kZXg6IERFRkFVTFRfSU5ERVgKCQkJfSk7fSBjYXRjaCB7fQoJfSk7Cn19CmlmIChGZi5vcyA9PSAibWFjb3N4IikKCU9iamVjdC5rZXlzKFRhZykuZm9yRWFjaCgoayk9PnsgLy9pINGB0YfRkdGC0YfQuNC6LCDQt9Cw0LzQtdC90LAg0LHRg9C60LIKCQlbJ+KXiScsJ8OYJ10uZm9yRWFjaCgoYyxpKT0+e1RhZ1trXSA9IFRhZ1trXS5yZXBsYWNlKG5ldyBSZWdFeHAoYywnZycpLAoJCVsn4qa/Jywn4peOJ11baV0pfSl9KQplbHNlIGlmIChnbG9iLnByZWYoW0ZmLnAgKyd3aW5idXR0b25zJyxmYWxzZV0pKQoJCWNzc19VU0VSKCcudGl0bGViYXItYnV0dG9uYm94IHtkaXNwbGF5OiBub25lICFpbXBvcnRhbnR9JykKCWVsc2UgY3NzX1VTRVIoRmYuYyArImN1c3RvbV9zdHlsZXMvd2luX2J1dHRvbnMtdml0di5jc3MiKTsKCn0pKCAvL2luaXQt0LrQvtC0CkZmID0ge3A6J2V4dGVuc2lvbnMudXNlcl9jaHJvbWVfZmlsZXMuJywgYzonY2hyb21lOi8vdXNlcl9jaHJvbWVfZmlsZXMvY29udGVudC8nLCBpOidwZXJtaXNzaW9ucy5kZWZhdWx0LmltYWdlJywKCW9zOiBBcHBDb25zdGFudHMucGxhdGZvcm0sIHZlcjogU2VydmljZXMuYXBwaW5mby52ZXJzaW9uLnJlcGxhY2UoLy0uKi8sJycpLAoJQ3RyKG0gPSAi4oyYIiwgdyA9ICJDdHJsKyIpe3JldHVybiB0aGlzLm9zID09ICJtYWNvc3giID8gbSA6IHd9LAoJRXhwKCl7cmV0dXJuIE51bWJlcihTZXJ2aWNlcy5wcmVmcy5nZXRCb29sUHJlZih0aGlzLnAgKydleHBlcnQnLGZhbHNlKSl9Cn0sCihuYW1lLG0gPSBGZi5FeHAoKSwgdCx6KSA9PiB7IC8v4oCmIHvQntCx0YnQuNC577iw0K3QutGB0L/QtdGA0YIgKG0gPSAxKVvvuLDigKZdfQoJdCA9IFRhZ1tuYW1lXTsgeiA9IHQubWF0Y2goLyhceykoW1xzXFNdKj8pKFx9KS9nbSk7CglpZiAoeikgei5mb3JFYWNoKChrLGgpID0+eyAvL9GC0LXQutGB0YIg0LfQsNCy0LjRgdC40YIg0L7RgiDRgNC10LbQuNC80LAKCQloID0gay5zcGxpdCgn77iwJyk7IGlmIChoICYmIGgubGVuZ3RoID4gbSkKCQkJdCA9IHQucmVwbGFjZShrLGhbbV0ucmVwbGFjZSgvXHt8XH0vZywnJykpO30pCglyZXR1cm4gdDt9IC8v0YDQsNC30LTQtdC70LjRgtC10LvRjCDQuCDQvdGD0LbQvdCw0Y8g0YfQsNGB0YLRjCDihpEKKTs=

для совместимости с Firefox 78-117 изменил UCF так (может, что-то можно сделать лучше…)

Выделить код

Код:

// фрагмент config.js для Firefox 78-117:
this.observe = (window, topic, data, icw) => {
	try {icw = (window instanceof Ci.nsIDOMChromeWindow);}
	catch {icw = (window .isChromeWindow)} //Ff116+
	if (!icw) return;
	var docElementInserted = e => {
		var win = e.target.defaultView;
		if (icw) user_chrome.initWindow(win);

// Services в jsm-скриптах для Firefox 78-117 подключается так:
if (typeof Services != "object")
	try{var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm")} catch{var {Services} = globalThis}

// Services в js-скриптах для Firefox 78-117:
if (typeof Services != "object")
	var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Dobrov
наверное можно так

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

icw должна быть определена заранее

Выделить код

Код:

icw = window.isChromeWindow ? window.isChromeWindow : Ci.nsIDOMChromeWindow;
Выделить код

Код:

var Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

Farby - значит, эти две строки создают одинаковый объект Services?


var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var Services = ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

Dobrov пишет

эти две строки создают одинаковый объект Services?

Да.

Кажется такая конструкция должна работать...

для config.js

Выделить код

Код:

if ((window.isChromeWindow) ? false : (window instanceof Ci.nsIDOMChromeWindow) ? false : true) return;

27-09-2023 18:02:12
Хотя проверил в [firefox] 58 конструкция window.isChromeWindow уже была...

Dumby
В 115 перестала нормально работать кнопка Восстановить.
В ней не раскрывается меню Недавно закрытые вкладки. В 91 все Ок.

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

Выделить код

Код:

try {(() => {
    var id = "ucf-undo-tab",
    label = "Восстановить",
    tooltiptext = "ЛКМ: Восстановить вкладку\nПКМ: Восстановить окно",
    tooltiptextbtnmenu = "ЛКМ: Открыть меню\nПКМ: Показать весь журнал",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16,12 C 16,12 3,27 3,26.81 L 24.2,48 H 45 C 46.7,48 48,46.7 48,45 V 17.3 L 40,9.3 Z'/><path style='fill:white;' d='M 27.68,3.93 C 26.7,3.93 25.66,3.992 24.58,4.138 19.23,5.17 13.74,8.472 10.22,12.78 3.018,5.815 7.525,10.29 3.021,5.815 L 3,26.81 H 24.18 L 17.03,19.7 C 20.44,14.7 30.87,6.752 38.32,19.08 40.69,25.69 40.58,36.52 35.69,44 40.97,38.26 45.35,30.55 44.98,21.33 44.59,14.08 39.37,3.992 27.68,3.93' /></g></svg>",
    imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><circle cy='24' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /></g></svg>";

    CustomizableUI.createWidget({
        id: id,
        type: "custom",
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onBuild: function(document) {
            var win = document.defaultView, toolbaritem = document.createXULElement("toolbaritem");
            toolbaritem.id = id;
            toolbaritem.className = "chromeclass-toolbar-additional";
            toolbaritem.setAttribute("label", label);
            toolbaritem.setAttribute("type", "custom");
            var toolbarbutton_0 = document.createXULElement("toolbarbutton");
            toolbarbutton_0.id = `${id}-button`;
            toolbarbutton_0.className = "toolbarbutton-1";
            toolbarbutton_0.setAttribute("label", label);
            toolbarbutton_0.setAttribute("tooltiptext", tooltiptext);
            toolbarbutton_0.setAttribute("context", "false");
            toolbarbutton_0.addEventListener("click", function(event) {
                if (event.button == 0)
                    win.undoCloseTab();
                else if (event.button == 2) {
                    event.preventDefault();
                    event.stopPropagation();
                    win.undoCloseWindow();
                }
            });
            toolbaritem.append(toolbarbutton_0);
            var toolbarbutton_1 = document.createXULElement("toolbarbutton");
            toolbarbutton_1.id = `${id}-button-menu`;
            toolbarbutton_1.className = "toolbarbutton-1";
            toolbarbutton_1.setAttribute("type", "menu");
            toolbarbutton_1.setAttribute("label", "");
            toolbarbutton_1.setAttribute("tooltiptext", tooltiptextbtnmenu);
            toolbarbutton_1.setAttribute("context", "false");
            toolbarbutton_1.addEventListener("click", function(event) {
                if (event.button == 2) {
                    event.preventDefault();
                    event.stopPropagation();
                    win.PlacesCommandHook.showPlacesOrganizer("History");
                }
            });
            var menupopup_0 = document.createXULElement("menupopup");
            menupopup_0.id = `${id}-popup`;
            menupopup_0.setAttribute("tooltip", "bhTooltip");
            menupopup_0.setAttribute("popupsinherittooltip", true);
            menupopup_0.addEventListener("click", function(event) {
                event.stopPropagation();
            });
            menupopup_0.addEventListener("command", function(event) {
                event.stopPropagation();
            });
            var menu_0 = document.createXULElement("menu");
            menu_0.setAttribute("disabled", true);
            menu_0.setAttribute("label", "Недавно закрытые вкладки");
            var menupopup_1 = document.createXULElement("menupopup");
            menupopup_1.addEventListener("popupshowing", function(event) {
                while (menupopup_1.hasChildNodes())
                    menupopup_1.firstChild.remove();
                if (win == Services.appShell.hiddenDOMWindow || win.SessionStore.getClosedTabCount(win) == 0) {
                    menu_0.setAttribute("disabled", true);
                    return;
                }
                var tabsFragment = win.RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(win, "menuitem");
                menupopup_1.append(tabsFragment);
                menu_0.removeAttribute("disabled");
            });
            menu_0.append(menupopup_1);
            menupopup_0.append(menu_0);
            var menu_1 = document.createXULElement("menu");
            menu_1.setAttribute("disabled", true);
            menu_1.setAttribute("label", "Недавно закрытые окна");
            var menupopup_2 = document.createXULElement("menupopup");
            menupopup_2.addEventListener("popupshowing", function(event) {
                while (menupopup_2.hasChildNodes())
                    menupopup_2.firstChild.remove();
                if (win.SessionStore.getClosedWindowCount() == 0) {
                    menu_1.setAttribute("disabled", true);
                    return;
                }
                var windowsFragment = win.RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(win, "menuitem");
                menupopup_2.append(windowsFragment);
                menu_1.removeAttribute("disabled");
            });
            menu_1.append(menupopup_2);
            menupopup_0.append(menu_1);
            menupopup_0.addEventListener("popupshowing", function(event) {
                if (win == Services.appShell.hiddenDOMWindow || win.SessionStore.getClosedTabCount(win) == 0)
                    menu_0.setAttribute("disabled", true);
                else
                    menu_0.removeAttribute("disabled");
                if (win.SessionStore.getClosedWindowCount() == 0)
                    menu_1.setAttribute("disabled", true);
                else
                    menu_1.removeAttribute("disabled");
            });
            var menuitem_0 = document.createXULElement("menuitem");
            menuitem_0.id = `${id}-all-history`;
            menuitem_0.setAttribute("label", "Показать весь журнал");
            menuitem_0.addEventListener("command", function(event) {
                event.stopPropagation();
                win.PlacesCommandHook.showPlacesOrganizer("History");
            });
            menupopup_0.append(menuitem_0);
            var menuitem_1 = document.createXULElement("menuitem");
            menuitem_1.setAttribute("label", "Удалить недавнюю историю…");
            menuitem_1.addEventListener("command", function(event) {
                event.stopPropagation();
                win.Sanitizer.showUI(win);
            });
            menupopup_0.append(menuitem_1);
            toolbarbutton_1.append(menupopup_0);
            toolbaritem.append(toolbarbutton_1);
            var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                #${id}-button {
                    list-style-image: url("${img}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                    margin-inline-end: 0 !important;
                }
                #${id}-button-menu {
                    list-style-image: url("${imgmenu}") !important;
                    -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                    margin-inline-start: 0px !important;
                }
                toolbarpaletteitem[place="palette"] #${id}-button-menu,
                #${id}-button-menu dropmarker {
                    display: none !important;
                }
                toolbarpaletteitem[place="palette"] #${id} {
                    -moz-box-orient: vertical !important;
                }
                #${id}-button-menu > .toolbarbutton-icon {
                    min-width: 0 !important;
                    max-width: none !important;
                    width: auto !important;
                    padding-left: 0 !important;
                    padding-right: 0 !important;
                }
            `);
            try {
                win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET);
            } catch (e) {}
            return toolbaritem;
        }
    });
})();} catch(e) {}

rubel
Замените в коде -> getClosedTabCount -> в двух местах на ->  getClosedTabCountForWindow

kokoss
Заменил, все прекрасно заработало. :)

rubel пишет

кнопка Восстановить – не раскрывается меню "Недавно закрытые вкладки"

У тебя старая версия. В новом коде строки Закрытых вкладок сразу в выпадающее меню кнопки добавляются.

Восстановить Закрытые вкладки/окна - модификация без dropmarker

Выделить код

Код:

try {(() => {
	var id = "ucf-undo-tab", label = "Закрытые вкладки/окна",
	tooltiptextbtnmenu = "ЛКМ: Открыть меню\nПКМ: Восстановить вкладку\nСКМ: Показать весь журнал";
	CustomizableUI.createWidget({
		id: id, label: label, type: "custom", localized: false,
		onBuild(doc) {
			var win = doc.defaultView, trim = doc.createXULElement("toolbaritem");
			trim.id = id;
			trim.className = "toolbaritem-combined-buttons ucf-toolbaritem-combined-buttons chromeclass-toolbar-additional";
			trim.setAttribute("label", label);
			trim.setAttribute("type", "custom");
			trim.style.setProperty("margin-inline","0");
			var trbn_1 = doc.createXULElement("toolbarbutton");
			trbn_1.id = `${id}-button-menu`;
			trbn_1.className = "toolbarbutton-1 ucf-toolbarbutton-combined-buttons-toolbarbutton";
			trbn_1.setAttribute("type", "menu");
			trbn_1.setAttribute("label", "");
			trbn_1.setAttribute("image", "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16,12 C 16,12 3,27 3,26.81 L 24.2,48 H 45 C 46.7,48 48,46.7 48,45 V 17.3 L 40,9.3 Z'/><path style='fill:white;' d='M 27.68,3.93 C 26.7,3.93 25.66,3.992 24.58,4.138 19.23,5.17 13.74,8.472 10.22,12.78 3.018,5.815 7.525,10.29 3.021,5.815 L 3,26.81 H 24.18 L 17.03,19.7 C 20.44,14.7 30.87,6.752 38.32,19.08 40.69,25.69 40.58,36.52 35.69,44 40.97,38.26 45.35,30.55 44.98,21.33 44.59,14.08 39.37,3.992 27.68,3.93' /></g></svg>");
			trbn_1.setAttribute("tooltiptext", tooltiptextbtnmenu);
			trbn_1.setAttribute("context", "");
			trbn_1.addEventListener("click", e => {
				if (e.button == 1)
					win.PlacesCommandHook.showPlacesOrganizer("History");
				else if (e.button == 2) {
					e.preventDefault(); e.stopPropagation();
					win.undoCloseTab();
				}
			});
			var mupp_0 = doc.createXULElement("menupopup");
			mupp_0.id = `${id}-popup`;
			mupp_0.setAttribute("tooltip", "bhTooltip");
			mupp_0.setAttribute("popupsinherittooltip", true);
			mupp_0.addEventListener("click", e => {
				e.stopPropagation();
			});
			mupp_0.addEventListener("command", e => {
				e.stopPropagation();
			});
			var muim_0 = doc.createXULElement("menuitem");
			muim_0.id = `${id}-all-history`;
			muim_0.className = "ucf-menuitem";
			muim_0.setAttribute("label", "Показать весь журнал");
			muim_0.addEventListener("command", e => {
				e.stopPropagation();
				win.PlacesCommandHook.showPlacesOrganizer("History");
			});
			mupp_0.append(muim_0);
			var muim_1 = doc.createXULElement("menuitem");
			muim_1.id = `${id}-sanitize`;
			muim_1.className = "ucf-menuitem";
			muim_1.setAttribute("label", "Удалить недавнюю историю…");
			muim_1.addEventListener("command", e => {
				e.stopPropagation();
				win.Sanitizer.showUI(win);
			});
			mupp_0.append(muim_1);
			var muim_2 = doc.createXULElement("menuitem");
			muim_2.id = `${id}-session`;
			muim_2.className = "ucf-menuitem";
			muim_2.setAttribute("hidden", "true");
			muim_2.setAttribute("label", "Восстановить последнюю сессию");
			muim_2.addEventListener("command", e => {
				e.stopPropagation();
				win.SessionStore.restoreLastSession();
			});
			mupp_0.append(muim_2);
			var menu_0 = doc.createXULElement("menu");
			menu_0.id = `${id}-menu-closed-win`;
			menu_0.setAttribute("hidden", "true");
			menu_0.setAttribute("label", "Недавно закрытые окна");
			var mupp_1 = doc.createXULElement("menupopup");
			var muim_3 = doc.createXULElement("menuitem");
			muim_3.id = `${id}-item-closed-win`;
			muim_3.className = "ucf-menuitem";
			muim_3.setAttribute("hidden", "true");
			muim_3.setAttribute("label", "Забыть закрытые окна");
			muim_3.addEventListener("command", e => {
				e.stopPropagation();
				var sessionStore = win.SessionStore;
				var count = sessionStore.getClosedWindowCount();
				while(count--)
					sessionStore.forgetClosedWindow(0);
			});
			mupp_1.append(muim_3);
			var musr_0 = doc.createXULElement("menuseparator");
			musr_0.id = `${id}-sep-closed-win`;
			musr_0.className = "ucf-menuseparator";
			musr_0.setAttribute("hidden", "true");
			mupp_1.append(musr_0);
			mupp_1.addEventListener("popupshowing", e => {
				e.stopPropagation();
				for (let item of mupp_1.querySelectorAll(":scope > :is(menuitem:not(.ucf-menuitem), menuseparator:not(.ucf-menuseparator))"))
					item.remove();
				if (win.SessionStore.getClosedWindowCount() == 0) {
					muim_3.setAttribute("hidden", "true");
					musr_0.setAttribute("hidden", "true");
					return;
				}
				if ("RecentlyClosedTabsAndWindowsMenuUtils" in win) {
					muim_3.removeAttribute("hidden");
					musr_0.removeAttribute("hidden");
					var windowsFragment = win.RecentlyClosedTabsAndWindowsMenuUtils.getWindowsFragment(win, "menuitem");
					mupp_1.append(windowsFragment);
					menu_0.removeAttribute("hidden");
				}
			});
			menu_0.append(mupp_1);
			mupp_0.append(menu_0);
			var musr_1 = doc.createXULElement("menuseparator");
			musr_1.className = "ucf-menuseparator";
			musr_1.setAttribute("hidden", "true");
			mupp_0.append(musr_1);
			var muim_4 = doc.createXULElement("menuitem");
			muim_4.id = `${id}-item-closed-tabs`;
			muim_4.className = "ucf-menuitem";
			muim_4.setAttribute("hidden", "true");
			muim_4.setAttribute("label", "Забыть закрытые вкладки");
			muim_4.addEventListener("command", e => {
				e.stopPropagation();
				var sessionStore = win.SessionStore, count;
				try{count = sessionStore.getClosedTabCountForWindow(win);} catch(e){count = sessionStore.getClosedTabCount(win)}
				while(count--)
					sessionStore.forgetClosedTab(win, 0);
			});
			mupp_0.append(muim_4);
			var musr_2 = doc.createXULElement("menuseparator");
			musr_2.id = `${id}-sep-closed-tabs`;
			musr_2.className = "ucf-menuseparator";
			musr_2.setAttribute("hidden", "true");
			mupp_0.append(musr_2);
			mupp_0.addEventListener("popupshowing", e => {
				var sessionStore = win.SessionStore;
				if (sessionStore.getClosedWindowCount() == 0)
					menu_0.setAttribute("hidden", "true");
				else
					menu_0.removeAttribute("hidden");
				if (!sessionStore.canRestoreLastSession)
					muim_2.setAttribute("hidden", "true");
				else
					muim_2.removeAttribute("hidden");
				for (let item of mupp_0.querySelectorAll(":scope > :is(menuitem:not(.ucf-menuitem), menuseparator:not(.ucf-menuseparator))"))
					item.remove();
				try{var sSgCTC = sessionStore.getClosedTabCountForWindow(win);} catch(e){var sSgCTC = sessionStore.getClosedTabCount(win)}
				if (win == Services.appShell.hiddenDOMWindow || sSgCTC == 0) {
					musr_1.setAttribute("hidden", "true");
					muim_4.setAttribute("hidden", "true");
					musr_2.setAttribute("hidden", "true");
					return;
				}
				if ("RecentlyClosedTabsAndWindowsMenuUtils" in win) {
					musr_1.removeAttribute("hidden");
					muim_4.removeAttribute("hidden");
					musr_2.removeAttribute("hidden");
					var tabsFragment = win.RecentlyClosedTabsAndWindowsMenuUtils.getTabsFragment(win, "menuitem");
					mupp_0.append(tabsFragment);
				}
			});
			trbn_1.append(mupp_0);
			trim.append(trbn_1);
			return trim;
		}
	});
})()} catch(e){}

Ошибки исправлены, копка теперь пашет на версиях Firefox от 84 (может и меньше) до Firefox 118.
Напомню, что в Шапке есть  Ссылка на обновляемые скрипты

Dobrov

Восстановить Закрытые вкладки/окна - модификация без подменю

Ваша кнопка вообще не работает, не появляется на панели и в настройках панели инструментов тоже её нет. :/

rubel пишет

кнопка вообще не работает, не появляется на панели и в настройках панели инструментов тоже её нет.

Это потому, что четвёртая строка заканчивается запятой.
Вместо запятой должна быть точка с запятой «;» (ну, или ничего).

Dumby Ошибку исправил! :beer:
кнопка теперь работает на старых и новых Firefox. И вот вопрос по коду: (как обычно) :)


кнопка создаётся через onBuild(doc) { …… Восстановить Закрытые вкладки/окна (вариант без dropmarker)
Можно её переделать на onCreated(btn) {……??? (у меня не заработало)


Возможно, это упростит код и сократит объём: не нужны будут return trbn_1 и trim = doc.createXULElement("toolbaritem")…

Подскажите пожалуйста, что поправить в UCF (см. ссылку) для fx 118, чтобы вернуть интервалы (special_widget), остался только растягивающийся интервал, а пробел и разделитель пропали.

https://www.upload.ee/files/15742086/ucf.rar.html

В 115 сломалась иконка кнопки "Загрузки" и "Переключить прокси" .
В 91
c5030804f6aa709957291711c53dc2a5.png
В115
eaadba129e73be4769db9e01792c0c05.png

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

Выделить код

Код:

try {
    (() => {
        var id = "ucf-toggle-proxy",
        label = "Переключить прокси",
        tooltiptext = "Переключить прокси",
        tooltiptextbtnmenu = "ЛКМ: Открыть меню\nПКМ: Открыть настройки прокси",
        toggleproxy = 1, // 0, 1, 2, 4 , 5  Первый режим
        toggleproxy2 = 2, // 0, 1, 2, 4 , 5  Второй режим
        doreload = true, // Перезагрузить страницу
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>",
        imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><circle cy='24' cx='24' style='fill:rgb(146, 69, 101);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /><circle cy='72' cx='24' style='fill:rgb(209, 8, 3);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,89.8 22.3,79.1 36.7,65.9 44,73.2 C 43.5,78.6 41,85.7 33,89.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,67 H 13 L 24,78 35,67' /><circle cy='120' cx='24' style='fill:rgb(243, 135, 37);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,138 22,127 36.7,114 44,121 C 43.5,127 40.9,134 32.8,138 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 34.9,115 H 13.1 L 24,126 34.9,115' /><circle cy='168' cx='24' style='fill:rgb(21, 161, 99);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.9,186 22,175 36.7,162 44,169 C 43.5,175 40.9,182 32.9,186 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,163 H 13 L 24,174 35,163' /><circle cy='216' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,234 22,223 36.7,210 44,217 C 43.5,223 40.9,230 32.8,234 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,211 H 13 L 24,222 35,211' /></g></svg>";

        var id2 = "ucf-open-downloads",
        label2 = "Загрузки",
        tooltiptext2 = "ЛКМ: Показать загрузки\nСКМ: Открыть папку загрузок\nПКМ: Открыть последнюю папку загрузок",
        img2 = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 23.4,33 30,39.6 H 18 L 8.7,30 3.9,44.1 7.8,48 H 45 C 46.7,48 48,46.7 48,45 V 22.5 L 28.8,3.3 Z'/><path style='fill:white;' d='M 20.27,3 C 19,3 19.25,3.798 19.25,3.798 V 17.94 20.23 H 13.75 C 12.13,20.23 13.61,21.58 13.61,21.58 L 23.36,32.85 V 32.85 C 23.36,32.85 24.17,33.75 25.01,32.95 26.16,31.84 34.47,21.3 34.47,21.3 34.47,21.3 35.93,19.89 33.98,19.89 H 28.91 V 17.83 3.672 C 28.91,3.672 28.93,3 28.03,3 Z M 3,29.93 V 42.22 C 3,43.75 4.303,45 5.918,45 H 42.08 C 43.71,45 45,43.75 45,42.22 V 29.93 H 39.16 V 39.43 H 8.837 V 29.93 Z' /></g></svg>";

        var tbarbtns = {
            get network_proxy_type() {
                delete this.network_proxy_type;
                try {
                    return this.network_proxy_type = Services.prefs.getIntPref("network.proxy.type");
                } catch(e) { }
                return this.network_proxy_type = null;
            },
            get btnstyle() {
                delete this.btnstyle;
                return this.btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-end: 0 !important;
                    }
                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-start: 0px !important;
                    }
                    #${id}[activated="1"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                    }
                    #${id}[activated="2"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(32px, 16px, 48px, 0px) !important;
                    }
                    #${id}[activated="4"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(48px, 16px, 64px, 0px) !important;
                    }
                    #${id}[activated="5"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(64px, 16px, 80px, 0px) !important;
                    }
                    toolbarpaletteitem[place="palette"] #${id}-button-menu,
                    #${id}-button-menu dropmarker {
                        display: none !important;
                    }
                    toolbarpaletteitem[place="palette"] #${id} {
                        -moz-box-orient: vertical !important;
                    }
                    #${id}-button-menu > .toolbarbutton-icon {
                        min-width: 0 !important;
                        max-width: none !important;
                        width: auto !important;
                        padding-left: 0 !important;
                        padding-right: 0 !important;
                    }
                `);
            },
            toggleTheProxy: function() {
                Services.prefs.setIntPref("network.proxy.type", (Services.prefs.getIntPref("network.proxy.type") == toggleproxy2) ? toggleproxy : toggleproxy2);
            },
            setProxyMenuItem: function(event) {
                var proxyState = Services.prefs.getIntPref("network.proxy.type"), popup = event.currentTarget;
                for (var menuitem in popup.childNodes) {
                    var childNode = popup.childNodes[menuitem];
                    if (+childNode.getAttribute("value") == proxyState) {
                        childNode.setAttribute("checked", "true");
                        break;
                    }
                }
            },
            setProxyValue: function(event) {
                Services.prefs.setIntPref("network.proxy.type", +event.target.getAttribute("value"));
            },
            checkBrowserReload: function(win) {
                if (doreload)
                    win.BrowserReloadSkipCache();
            },
            openProxyWin: function(win) {
                var _win = Services.wm.getMostRecentWindow("aTaB:ProxyWin");
                if (_win)
                    _win.focus();
                else {
                    _win = win.openDialog("chrome://browser/content/preferences/dialogs/connection.xhtml", "_blank", "chrome,dialog=no,centerscreen,resizable");
                    var DOMLoad = () => {
                        _win.document.documentElement.setAttribute("type", "prefwindow");
                        _win.document.documentElement.setAttribute("windowtype", "aTaB:ProxyWin");
                    };
                    _win.addEventListener("DOMContentLoaded", DOMLoad, { once: true });
                    _win.opener = win;
                    _win.opener.gSubDialog = {
                        _dialogs: []
                    };
                }
            },
        };
        CustomizableUI.createWidget({
            id: id,
            type: "custom",
            label: label,
            tooltiptext: tooltiptext,
            localized: false,
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onBuild: function(document) {
                var win = document.defaultView, toolbaritem = document.createXULElement("toolbaritem");
                toolbaritem.id = id;
                toolbaritem.className = "chromeclass-toolbar-additional";
                toolbaritem.setAttribute("label", label);
                toolbaritem.setAttribute("type", "custom");
                var toolbarbutton_0 = document.createXULElement("toolbarbutton");
                toolbarbutton_0.id = `${id}-button`;
                toolbarbutton_0.className = "toolbarbutton-1";
                toolbarbutton_0.setAttribute("label", label);
                toolbarbutton_0.setAttribute("tooltiptext", tooltiptext);
                toolbarbutton_0.setAttribute("context", "false");
                toolbaritem.append(toolbarbutton_0);
                var toolbarbutton_1 = document.createXULElement("toolbarbutton");
                toolbarbutton_1.id = `${id}-button-menu`;
                toolbarbutton_1.className = "toolbarbutton-1";
                toolbarbutton_1.setAttribute("type", "menu");
                toolbarbutton_1.setAttribute("label", "");
                toolbarbutton_1.setAttribute("tooltiptext", tooltiptextbtnmenu);
                toolbarbutton_1.setAttribute("context", "false");
                toolbarbutton_1.addEventListener("click", function(event) {
                    if (event.button == 2) {
                        event.preventDefault();
                        event.stopPropagation();
                        tbarbtns.openProxyWin(win);
                    }
                });
                var menupopup_0 = document.createXULElement("menupopup");
                menupopup_0.id = `${id}-popup`;
                menupopup_0.addEventListener("click", function(event) {
                    event.stopPropagation();
                });
                var proxy = tbarbtns.network_proxy_type;
                if (proxy !== null) {
                    toolbaritem.setAttribute("activated", tbarbtns.network_proxy_type);
                    toolbarbutton_0.addEventListener("command", function(event) {
                        tbarbtns.toggleTheProxy();
                        tbarbtns.checkBrowserReload(win);
                    });
                    menupopup_0.addEventListener("command", function(event) {
                        event.stopPropagation();
                        tbarbtns.setProxyValue(event);
                        tbarbtns.checkBrowserReload(win);
                    });
                    menupopup_0.addEventListener("popupshowing", function(event) {
                        tbarbtns.setProxyMenuItem(event);
                    });
                }
                var menuitem_0 = document.createXULElement("menuitem");
                menuitem_0.setAttribute("label", "Прямое подключение, без прокси");
                menuitem_0.setAttribute("type", "radio");
                menuitem_0.setAttribute("value", "0");
                menupopup_0.append(menuitem_0);
                var menuitem_1 = document.createXULElement("menuitem");
                menuitem_1.setAttribute("label", "Ручная настройка прокси");
                menuitem_1.setAttribute("type", "radio");
                menuitem_1.setAttribute("value", "1");
                menupopup_0.append(menuitem_1);
                var menuitem_2 = document.createXULElement("menuitem");
                menuitem_2.setAttribute("label", "Автоматическая настройка прокси");
                menuitem_2.setAttribute("type", "radio");
                menuitem_2.setAttribute("value", "2");
                menupopup_0.append(menuitem_2);
                var menuitem_3 = document.createXULElement("menuitem");
                menuitem_3.setAttribute("label", "Автоопределение настроек прокси");
                menuitem_3.setAttribute("type", "radio");
                menuitem_3.setAttribute("value", "4");
                menupopup_0.append(menuitem_3);
                var menuitem_4 = document.createXULElement("menuitem");
                menuitem_4.setAttribute("label", "Использовать системные настройки прокси");
                menuitem_4.setAttribute("type", "radio");
                menuitem_4.setAttribute("value", "5");
                menupopup_0.append(menuitem_4);
                var menuseparator_0 = document.createXULElement("menuseparator");
                menupopup_0.append(menuseparator_0);
                var menuitem_5 = document.createXULElement("menuitem");
                menuitem_5.setAttribute("label", "Открыть настройки прокси");
                menuitem_5.addEventListener("command", function(event) {
                    event.stopPropagation();
                    tbarbtns.openProxyWin(win);
                });
                menupopup_0.append(menuitem_5);
                toolbarbutton_1.append(menupopup_0);
                toolbaritem.append(toolbarbutton_1);
                try {
                    win.windowUtils.loadSheetUsingURIString(tbarbtns.btnstyle, win.windowUtils.USER_SHEET);
                } catch (e) {}
                return toolbaritem;
            }
        });
        Services.prefs.addObserver("network.proxy.type", {
            observe: function(aSubject, aTopic, aData) {
                if (aData == "network.proxy.type") {
                    let network_proxy_type = tbarbtns.network_proxy_type = Services.prefs.getIntPref(aData),
                    getW = CustomizableUI.getWidget(id);
                    if (getW.instances.length)
                        for(let {node} of getW.instances)
                            try {
                                node.setAttribute("activated", network_proxy_type);
                            } catch(e) {}
                    else
                        for (let win of CustomizableUI.windows)
                            try {
                                getW.forWindow(win).node.setAttribute("activated", network_proxy_type);
                            } catch(e) {}
                }
            }
        });
        CustomizableUI.createWidget({
            id: id2,
            type: "custom",
            label: label2,
            tooltiptext: tooltiptext2,
            localized: false,
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onBuild: function(document) {
                var win = document.defaultView, toolbarbutton = document.createXULElement("toolbarbutton");
                toolbarbutton.id = id2;
                toolbarbutton.className = "toolbarbutton-1 chromeclass-toolbar-additional";
                toolbarbutton.setAttribute("label", label2);
                toolbarbutton.setAttribute("context", "false");
                toolbarbutton.setAttribute("tooltiptext", tooltiptext2);
                toolbarbutton.addEventListener("click", function(event) {
                    if (event.button == 0)
                        win.DownloadsPanel.showDownloadsHistory();
                    else if (event.button == 1) {
                        try {
                            Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile).launch();
                        } catch(e) {
                            Services.dirsvc.get("DfltDwnld", Ci.nsIFile).launch();
                        }
                    } else if (event.button == 2) {
                        event.preventDefault();
                        event.stopPropagation();
                        try {
                            Services.prefs.getComplexValue("browser.download.lastDir", Ci.nsIFile).launch();
                        } catch(e) {
                            Services.dirsvc.get("DfltDwnld", Ci.nsIFile).launch();
                        }
                    }
                });
                toolbarbutton.style.setProperty("list-style-image", `url("${img2}")`, "important");
                return toolbarbutton;
            }
        });
    })();
} catch(e) {}


Как бы это поправить, уважаемые гуру.

Northtech пишет

что поправить в UCF

Это вопрос к автору мода Dobrov. В родном всё работает.

Northtech пишет

что поправить в UCF (см. ссылку) для fx 118, чтобы вернуть интервалы (special_widget)

Если что-то не пашет, просто проверьте по ссылке в шапке, может это уже исправлено!
Проще говоря, скачайте новую версию с гитхаба. У вас старый скрипт CustomStylesScripts.jsm от 12 сентября.

rubel
В ATB вроде так -> https://forum.mozilla-russia.org/viewto … 68#p804468

kokoss
В ATB  у меня все нормально. Именно в кнопке они сломались.
И что мне делать с тем архивом от Dumby ?
В кнопке иконки прописаны здесь:

Выделить код

Код:

img = "data:image
imgmenu = "data:image
Northtech пишет

Подскажите пожалуйста, что поправить в UCF (см. ссылку) для fx 118, чтобы вернуть интервалы (special_widget), остался только растягивающийся интервал, а пробел и разделитель пропали.

Встречалось такое на этом форуме

В файле user_chrome_files\custom_scripts\CustomStylesScripts.jsm нужно раскомментировать две строки:
---------------------
// { path: "special_widget.css", type: "USER_SHEET", sheet(f) { preloadSheet(this, f); }, }, // <-- Special Widgets
---------------------
// { path: "scripts3/special_widgets.js", ucfobj: true, }, // <-- Special Widgets
---------------------
В настройках UCF включить опцию Для докум. всех окон [ChromeOnly] и перезапустить с очисткой startupCache.

Еще существует отдельный скрипт, не из состава ucf, создает как раз два пропавших элемента https://forum.ru-board.com/topic.cgi?fo … tart=80#14 , по сути это скрипт от Dumby, в который Farby встроил css-код. Активировать его можно как в ucf, так и без.

Dobrov пишет

Можно её переделать на onCreated(btn) {……???

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

Выделить код

Код:

/*
		id: id, label: label, type: "custom", localized: false,
		onBuild(doc) {
			var win = doc.defaultView, trim = doc.createXULElement("toolbaritem");
			trim.id = id;
			trim.className = "toolbaritem-combined-buttons ucf-toolbaritem-combined-buttons chromeclass-toolbar-additional";
			trim.setAttribute("label", label);
			trim.setAttribute("type", "custom");
			trim.style.setProperty("margin-inline","0");
			var trbn_1 = doc.createXULElement("toolbarbutton");
			trbn_1.id = `${id}-button-menu`;
			trbn_1.className = "toolbarbutton-1 ucf-toolbarbutton-combined-buttons-toolbarbutton";
			trbn_1.setAttribute("type", "menu");
			trbn_1.setAttribute("label", "");
*/
		id, label, localized: false,
		onCreated(trbn_1) {
			var win = trbn_1.ownerGlobal, doc = win.document;
			trbn_1.setAttribute("type", "menu");

.......

/*
			trim.append(trbn_1);
			return trim;
*/

rubel пишет

сломалась иконка

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

Выделить код

Код:

/*
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>",
        imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><circle cy='24' cx='24' style='fill:rgb(146, 69, 101);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /><circle cy='72' cx='24' style='fill:rgb(209, 8, 3);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,89.8 22.3,79.1 36.7,65.9 44,73.2 C 43.5,78.6 41,85.7 33,89.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,67 H 13 L 24,78 35,67' /><circle cy='120' cx='24' style='fill:rgb(243, 135, 37);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,138 22,127 36.7,114 44,121 C 43.5,127 40.9,134 32.8,138 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 34.9,115 H 13.1 L 24,126 34.9,115' /><circle cy='168' cx='24' style='fill:rgb(21, 161, 99);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.9,186 22,175 36.7,162 44,169 C 43.5,175 40.9,182 32.9,186 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,163 H 13 L 24,174 35,163' /><circle cy='216' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,234 22,223 36.7,210 44,217 C 43.5,223 40.9,230 32.8,234 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,211 H 13 L 24,222 35,211' /></g></svg>";
*/
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 48'><view id='1' viewBox='0 48 48 48'/><view id='2' viewBox='0 96 48 48'/><view id='4' viewBox='0 144 48 48'/><view id='5' viewBox='0 192 48 48'/><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>",
        imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 48'><view id='1' viewBox='0 48 48 48'/><view id='2' viewBox='0 96 48 48'/><view id='4' viewBox='0 144 48 48'/><view id='5' viewBox='0 192 48 48'/><g><circle cy='24' cx='24' style='fill:rgb(146, 69, 101);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /><circle cy='72' cx='24' style='fill:rgb(209, 8, 3);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,89.8 22.3,79.1 36.7,65.9 44,73.2 C 43.5,78.6 41,85.7 33,89.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,67 H 13 L 24,78 35,67' /><circle cy='120' cx='24' style='fill:rgb(243, 135, 37);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,138 22,127 36.7,114 44,121 C 43.5,127 40.9,134 32.8,138 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 34.9,115 H 13.1 L 24,126 34.9,115' /><circle cy='168' cx='24' style='fill:rgb(21, 161, 99);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.9,186 22,175 36.7,162 44,169 C 43.5,175 40.9,182 32.9,186 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,163 H 13 L 24,174 35,163' /><circle cy='216' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,234 22,223 36.7,210 44,217 C 43.5,223 40.9,230 32.8,234 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,211 H 13 L 24,222 35,211' /></g></svg>";

.......

/*
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-end: 0 !important;
                    }
                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-start: 0px !important;
                    }
                    #${id}[activated="1"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                    }
                    #${id}[activated="2"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(32px, 16px, 48px, 0px) !important;
                    }
                    #${id}[activated="4"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(48px, 16px, 64px, 0px) !important;
                    }
                    #${id}[activated="5"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(64px, 16px, 80px, 0px) !important;
                    }
*/
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        margin-inline-end: 0 !important;
                    }
                    #${id}[activated="1"] > #${id}-button {
                        list-style-image: url("${img}#1") !important;
                    }
                    #${id}[activated="2"] > #${id}-button {
                        list-style-image: url("${img}#2") !important;
                    }
                    #${id}[activated="4"] > #${id}-button {
                        list-style-image: url("${img}#4") !important;
                    }
                    #${id}[activated="5"] > #${id}-button {
                        list-style-image: url("${img}#5") !important;
                    }

                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        margin-inline-start: 0px !important;
                    }
                    #${id}[activated="1"] > #${id}-button-menu {
                        list-style-image: url("${imgmenu}#1") !important;
                    }
                    #${id}[activated="2"] > #${id}-button-menu {
                        list-style-image: url("${imgmenu}#2") !important;
                    }
                    #${id}[activated="4"] > #${id}-button-menu {
                        list-style-image: url("${imgmenu}#4") !important;
                    }
                    #${id}[activated="5"] > #${id}-button-menu {
                        list-style-image: url("${imgmenu}#5") !important;
                    }

.......

/*
                        width: auto !important;
*/

Dumby
А куда вставлять код стилей после img = и  imgmenu =
Распишите, пожалуйста, подробнее для таких как я любителей.

rubel пишет

А куда вставлять код стилей после img = и  imgmenu =

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


С кодом стилей — то же самое.
Находишь в коде закомментированый кусок, и меняешь на новый.


Строка width: auto !important; (там в конце) — это третья часть.
Эту строку предлагается просто убрать, без какой-либо замены.

Dumby
Как тут такое же сделать? Как здесь. Иконка прокси, чтобы "Active", "Not Active", было на первые две в иконке.

Выделить код

Код:

data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 48'><view id='1' viewBox='0 48 48 48'/><view id='2' viewBox='0 96 48 48'/><view id='4' viewBox='0 144 48 48'/><view id='5' viewBox='0 192 48 48'/><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>

Можно еще поменять полный путь, чтобы он брал путь с папки chrome? Полный путь не совсем удобно. А так, кнопка рабочая. Можно и по другому запускать прокси. Просто так одним кликом) И на видном месте. Как бы не обязательно.

Upd: Класс. Спасибо.
У меня в голове кроме соломы ничего не откладывается) Похоже я дровосек. А так, примеры какие-то есть, а как работает код не совсем понимаю. Хоть тут и должно бы уже придти какое никакое понимание.

Dumby
Спасибо за науку. Все получилось. !
41fa8495090ca6c411eb0197df04f8b4.png

b0ttle пишет

Как тут такое же сделать? Как здесь. Иконка прокси, чтобы "Active", "Not Active", было на первые две в иконке.

Не нужно тут такое же делать,
«здесь» под стиль, а «тут» ничего подобного.


Если просто эта svg'шка нравится, тогда берём первый кусок,
и используем в двух экземплярах, только с разным fill
Вот, например, с красным
(в коде заключать в "двойные" или `такие` кавычки, не в 'одинарные')

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

Выделить код

Код:

data:image/svg+xml,<svg style='fill: red;' xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /></g></svg>

Можно еще поменять полный путь, чтобы он брал путь с папки chrome?

Ну сколько можно одно и то же, неужели не откладывается.

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

Выделить код

Код:

/*
	var path = ".......\\opera-proxy.windows-386.exe";
*/
	var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
	file.append("opera-proxy.windows-386.exe");
	var {path} = file;


rubel
Вот ещё вариант правки
скрытый текст

Выделить код

Код:

/*
        doreload = true, // Перезагрузить страницу
        img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(146, 69, 101);'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /><rect x='0' y='48' width='48' height='48' rx='3' ry='3' style='fill:rgb(209, 8, 3);'/><path style='opacity:0.25;fill:black;' d='M 16.8,65.6 23.1,71.9 8,74 6.4,80.2 11.4,85.2 3.7,92.8 6.9,96 45,96 C 46.7,96 48,94.7 48,93 V 68 L 31.4,51.4'/><path style='fill:white;' d='M 17.4,51 C 16.7,51 16.2,51.8 16.2,52.9 V 63.9 C 16.2,65 16.7,65.8 17.4,65.8 H 30.6 C 31.3,65.8 31.8,65 31.8,63.9 V 52.9 C 31.8,51.8 31.3,51 30.6,51 H 17.4 M 22.4,68.5 V 71.7 H 6.41 V 80.2 H 9.35 V 76.2 H 22.4 V 80.4 H 25.5 V 76.2 H 38.5 V 80.4 H 41.5 V 71.7 H 25.5 V 68.5 H 22.4 M 4.23,83.1 C 3.55,83.1 3,83.9 3,85.1 V 91 C 3,92.1 3.55,93 4.23,93 H 12.1 C 12.8,93 13.3,92.1 13.3,91 V 85.1 C 13.3,83.9 12.8,83.1 12.1,83.1 H 4.23 M 19.9,83.1 C 19.2,83.1 18.8,83.9 18.8,85.1 V 91 C 18.8,92.1 19.2,93 19.9,93 H 27.8 C 28.5,93 29,92.1 29,91 V 85.1 C 29,83.9 28.5,83.1 27.8,83.1 H 19.9 M 35.9,83.1 C 35.2,83.1 34.7,83.9 34.7,85.1 V 91 C 34.7,92.1 35.2,93 35.9,93 H 43.7 C 44.4,93 45,92.1 45,91 V 85.1 C 45,83.9 44.4,83.1 43.7,83.1 H 35.9' /><rect x='0' y='96' width='48' height='48' rx='3' ry='3' style='fill:rgb(243, 135, 37);'/><path style='opacity:0.25;fill:black;' d='M 16.8,114 23.1,120 8,122 6.4,128 11.4,133 3.7,141 6.9,144 H 45 C 46.7,144 48,142.7 48,141 V 116 L 31.4,99.4'/><path style='fill:white;' d='M 17.4,99 C 16.7,99 16.2,99.8 16.2,101 V 112 C 16.2,113 16.7,114 17.4,114 H 30.6 C 31.3,114 31.8,113 31.8,112 V 101 C 31.8,99.8 31.3,99 30.6,99 H 17.4 M 22.4,117 V 120 H 6.41 V 128 H 9.35 V 124 H 22.4 V 128 H 25.5 V 124 H 38.5 V 128 H 41.5 V 120 H 25.5 V 117 H 22.4 M 4.23,131 C 3.55,131 3,132 3,133 V 139 C 3,140 3.55,141 4.23,141 H 12.1 C 12.8,141 13.3,140 13.3,139 V 133 C 13.3,132 12.8,131 12.1,131 H 4.23 M 19.9,131 C 19.2,131 18.8,132 18.8,133 V 139 C 18.8,140 19.2,141 19.9,141 H 27.8 C 28.5,141 29,140 29,139 V 133 C 29,132 28.5,131 27.8,131 H 19.9 M 35.9,131 C 35.2,131 34.7,132 34.7,133 V 139 C 34.7,140 35.2,141 35.9,141 H 43.7 C 44.4,141 45,140 45,139 V 133 C 45,132 44.4,131 43.7,131 H 35.9' /><rect x='0' y='144' width='48' height='48' rx='3' ry='3' style='fill:rgb(21, 161, 99);'/><path style='opacity:0.25;fill:black;' d='M 16.8,162 23.1,168 8,170 6.4,176 11.4,181 3.7,189 6.9,192 H 45 C 46.7,192 48,190.7 48,189 V 164 L 31.4,147'/><path style='fill:white;' d='M 17.4,147 C 16.7,147 16.2,148 16.2,149 V 160 C 16.2,161 16.7,162 17.4,162 H 30.6 C 31.3,162 31.8,161 31.8,160 V 149 C 31.8,148 31.3,147 30.6,147 H 17.4 M 22.4,165 V 168 H 6.41 V 176 H 9.35 V 172 H 22.4 V 176 H 25.5 V 172 H 38.5 V 176 H 41.5 V 168 H 25.5 V 165 H 22.4 M 4.23,179 C 3.55,179 3,180 3,181 V 187 C 3,188 3.55,189 4.23,189 H 12.1 C 12.8,189 13.3,188 13.3,187 V 181 C 13.3,180 12.8,179 12.1,179 H 4.23 M 19.9,179 C 19.2,179 18.8,180 18.8,181 V 187 C 18.8,188 19.2,189 19.9,189 H 27.8 C 28.5,189 29,188 29,187 V 181 C 29,180 28.5,179 27.8,179 H 19.9 M 35.9,179 C 35.2,179 34.7,180 34.7,181 V 187 C 34.7,188 35.2,189 35.9,189 H 43.7 C 44.4,189 45,188 45,187 V 181 C 45,180 44.4,179 43.7,179 H 35.9' /><rect x='0' y='192' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 16.8,210 23.1,216 8,218 6.4,224 11.4,229 3.7,237 6.9,240 H 45 C 46.7,240 48,238.7 48,237 L 48,212 31.4,195'/><path style='fill:white;' d='M 17.4,195 C 16.7,195 16.2,196 16.2,197 V 208 C 16.2,209 16.7,210 17.4,210 H 30.6 C 31.3,210 31.8,209 31.8,208 V 197 C 31.8,196 31.3,195 30.6,195 H 17.4 M 22.4,213 V 216 H 6.41 V 224 H 9.4 V 220 H 22.4 V 224 H 25.5 V 220 H 38.5 V 224 H 41.5 V 216 H 25.5 V 213 H 22.4 M 4.23,227 C 3.55,227 3,228 3,229 V 235 C 3,236 3.55,237 4.23,237 H 12.1 C 12.8,237 13.3,236 13.3,235 V 229 C 13.3,228 12.8,227 12.1,227 H 4.23 M 19.9,227 C 19.2,227 18.7,228 18.7,229 V 235 C 18.7,236 19.2,237 19.9,237 H 27.8 C 28.5,237 29,236 29,235 V 229 C 29,228 28.5,227 27.8,227 H 19.9 M 35.9,227 C 35.2,227 34.7,228 34.7,229 V 235 C 34.7,236 35.2,237 35.9,237 H 43.7 C 44.4,237 45,236 45,235 V 229 C 45,228 44.4,227 43.7,227 H 35.9' /></g></svg>",
        imgmenu = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='80' width='16' viewBox='0 0 48 240'><g><circle cy='24' cx='24' style='fill:rgb(146, 69, 101);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /><circle cy='72' cx='24' style='fill:rgb(209, 8, 3);' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,89.8 22.3,79.1 36.7,65.9 44,73.2 C 43.5,78.6 41,85.7 33,89.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,67 H 13 L 24,78 35,67' /><circle cy='120' cx='24' style='fill:rgb(243, 135, 37);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,138 22,127 36.7,114 44,121 C 43.5,127 40.9,134 32.8,138 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 34.9,115 H 13.1 L 24,126 34.9,115' /><circle cy='168' cx='24' style='fill:rgb(21, 161, 99);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.9,186 22,175 36.7,162 44,169 C 43.5,175 40.9,182 32.9,186 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,163 H 13 L 24,174 35,163' /><circle cy='216' cx='24' style='fill:rgb(0, 120, 173);' r='20'/><path style='opacity:0.25;fill:black;' d='M 32.8,234 22,223 36.7,210 44,217 C 43.5,223 40.9,230 32.8,234 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,211 H 13 L 24,222 35,211' /></g></svg>";
*/
        doreload = true; // Перезагрузить страницу

        var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
        var r = (name, svg) => {
            var subst = id + "-" + name;
            rph.setSubstitution(subst, Services.io.newURI("data:image/svg+xml;charset=utf-8," + svg));
            return `resource://${subst}/`;
        }
        var img = r("img", "<svg style='fill: context-fill;' xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3'/><path style='opacity:0.25;fill:black;' d='M 16.8,17.6 23.1,23.9 8,26 6.4,32.2 11.4,37.2 3.7,44.8 6.9,48 45,48 C 46.7,48 48,46.7 48,45 V 20 L 31.4,3.4'/><path style='fill:white;' d='M 17.4,3 C 16.7,3 16.2,3.82 16.2,4.91 V 15.9 C 16.2,17 16.7,17.8 17.4,17.8 H 30.6 C 31.3,17.8 31.8,17 31.8,15.9 V 4.91 C 31.8,3.82 31.3,3 30.6,3 H 17.4 M 22.4,20.5 V 23.7 H 6.41 V 32.2 H 9.35 V 28.2 H 22.4 V 32.4 H 25.5 V 28.2 H 38.5 V 32.4 H 41.5 V 23.7 H 25.5 V 20.5 H 22.4 M 4.23,35.1 C 3.55,35.1 3,35.9 3,37.1 V 43 C 3,44.1 3.55,45 4.23,45 H 12.1 C 12.8,45 13.3,44.1 13.3,43 V 37.1 C 13.3,35.9 12.8,35.1 12.1,35.1 H 4.23 M 19.9,35.1 C 19.2,35.1 18.7,35.9 18.7,37.1 V 43 C 18.7,44.1 19.2,45 19.9,45 H 27.8 C 28.5,45 29,44.1 29,43 V 37.1 C 29,35.9 28.5,35.1 27.8,35.1 H 19.9 M 35.9,35.1 C 35.2,35.1 34.7,35.9 34.7,37.1 V 43 C 34.7,44.1 35.2,45 35.9,45 H 43.7 C 44.4,45 45,44.1 45,43 V 37.1 C 45,35.9 44.4,35.1 43.7,35.1 H 35.9' /></g></svg>");
        var imgmenu = r("imgmenu", "<svg style='fill: context-fill;' xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><circle cy='24' cx='24' r='20'/><path style='opacity:0.25;fill:black;' d='M 33,41.8 22.3,31.1 36.7,17.9 44,25.2 C 43.5,30.6 41,37.7 33,41.8 Z'/><path style='fill:white;stroke:white;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;' d='M 35,19 H 13 L 24,30 35,19' /></g></svg>");

.......

/*
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-end: 0 !important;
                    }
                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        -moz-image-region: rect(0px, 16px, 16px, 0px) !important;
                        margin-inline-start: 0px !important;
                    }
                    #${id}[activated="1"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(16px, 16px, 32px, 0px) !important;
                    }
                    #${id}[activated="2"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(32px, 16px, 48px, 0px) !important;
                    }
                    #${id}[activated="4"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(48px, 16px, 64px, 0px) !important;
                    }
                    #${id}[activated="5"] :-moz-any(#${id}-button,#${id}-button-menu) {
                        -moz-image-region: rect(64px, 16px, 80px, 0px) !important;
                    }
*/
                    #${id}-button {
                        list-style-image: url("${img}") !important;
                        margin-inline-end: 0 !important;
                    }
                    #${id}-button-menu {
                        list-style-image: url("${imgmenu}") !important;
                        margin-inline-start: 0px !important;
                    }
                    #${id}[activated="0"] :is(#${id}-button,#${id}-button-menu) {
                        fill: rgb(146,69,101) !important;
                    }
                    #${id}[activated="1"] :is(#${id}-button,#${id}-button-menu) {
                        fill: rgb(209,8,3) !important;
                    }
                    #${id}[activated="2"] :is(#${id}-button,#${id}-button-menu) {
                        fill: rgb(243,135,37) !important;
                    }
                    #${id}[activated="4"] :is(#${id}-button,#${id}-button-menu) {
                        fill: rgb(21,161,99) !important;
                    }
                    #${id}[activated="5"] :is(#${id}-button,#${id}-button-menu) {
                        fill: rgb(0,120,173) !important;
                    }

Dumby
А как прописать такой путь "\\user_chrome_files\\_\\opera-proxy.windows-386.exe", и желательно все прописать где "var run = ...", а "var path = ..." убрать, он путает. Нашел такое решение в одном из ваших примеров.

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

Выделить код

Код:

var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
	["user_chrome_files","_","opera-proxy.windows-386.exe"].forEach(file.append);
	var {path} = file;

rubel пишет

В ATB  у меня все нормально. Именно в кнопке они сломались.
И что мне делать с тем архивом от Dumby ?

Ну если с ATB всё нормально, то ничего.

Dumby

rubel
Вот ещё вариант правки

Этот вариант лучше. Значок Меню стал ближе к значку Переключить прокси. Спасибо. :)

b0ttle пишет

желательно все прописать где "var run = ...", а "var path = ..." убрать

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

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

Выделить код

Код:

/*
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
*/
				var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
				["user_chrome_files", "_", "opera-proxy.windows-386.exe"].forEach(file.append);

Dumby
Во то что надо. Спасибо.
А что так тихо в теме?)

Как переделать загрузчик AttributesInspector.js, чтобы вместо левой кнопки мыши код выполнялся при клике правой кнопкой ? Или колёсиком ?

Выделить код

Код:

(async (id) => CustomizableUI.createWidget({
	label:id, id:id, localized: false,
	onCreated(btn) {
		btn.setAttribute("image","data:image/webp;base64,UklGRjwAAABXRUJQVlA4TC8AAAAvD8ADAAoGbSM5Ov6k774XCPFP/0/03/8JGPxzroIzuOW06Ih60Genn1S/gHe+BgA=");
		btn.onmouseenter = btn.onmouseleave = this.onmouse;
		btn.setAttribute("oncommand","handleCommand(this)"); btn.handleCommand = this.handleCommand;
	},
	onmouse: e => e.target.focusedWindow = e.type.endsWith("r") && Services.wm.getMostRecentWindow(null),
	get handleCommand() {
		delete this.handleCommand;
		return this.handleCommand = btn => {(btn.handleCommand = new btn.ownerGlobal.Function(this.code).bind(btn))();}
	},
	get code() {
		delete this.code; var s = Ff.c +"custom_scripts/AttributesInspector.js";
		try { id = 'this.focusedWindow && this.focusedWindow.focus();\n' +
			Cu.readUTF8URI(Services.io.newURI(s))
		} catch {}
		return this.code = id;
	}
}))("AttributesInspector");
Dobrov пишет

Как переделать загрузчик AttributesInspector.js, чтобы вместо левой кнопки мыши код выполнялся при клике правой кнопкой ?

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

Выделить код

Код:

/*
		btn.setAttribute("oncommand","handleCommand(this)"); btn.handleCommand = this.handleCommand;
*/
		btn.setAttribute("oncontextmenu", "return event.ctrlKey || (handleCommand(this), false)"); btn.handleCommand = this.handleCommand;

Или колёсиком ?

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

Выделить код

Код:

/*
		btn.setAttribute("oncommand","handleCommand(this)"); btn.handleCommand = this.handleCommand;
*/
		btn.setAttribute("onauxclick", "if (event.button == 1) handleCommand(this)"); btn.handleCommand = this.handleCommand;

.......

/*
		return this.code = id;
*/
		return this.code = id.replace("canInspect: function(e) {\n", "$&\t\t\tif(e.button == 1 && e.target == context.button) return false;\n");

Выложу здесь свой код google-translate.js. Ссылка на источник, ничего не удалял. Ненужное закомментировано, изменения прокомментированы и ссылки присутствуют.

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

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=780231#p780231
(this.googletranslate = {
            init(that) {
                var lc = navigator.lastClick = {}, w = null, xhtmlns = 'http://www.w3.org/1999/xhtml';
                var mouseUp = (e) => {
                    if (e.button) return;
                    lc.X = e.screenX - mozInnerScreenX;
                    lc.Y = e.screenY - mozInnerScreenY;
                };
                gBrowser.tabpanels.addEventListener('mouseup', mouseUp, false);
                this.destructor = () => {
                    gBrowser.tabpanels.removeEventListener('mouseup', mouseUp, false);
                    if (w)
                        w.closeWin();
                };
                that.unloadlisteners.push("googletranslate");
                var createWindow = function(text, status, title, id, pos, size) {
                    var win = window, doc = win.document, wId = 'ujs_window'+(id || '');
                    w = doc.getElementById(wId);
                    var keyDown = function(e) {if (!e.shiftKey && !e.ctrlKey && !e.altKey && e.keyCode == 27)doc.getElementById(wId).closeWin();};
                    var mouseDown = function() {doc.getElementById(wId).closeWin();};

                    if (w)
                        w.closeWin();
                    w = doc.createElementNS(xhtmlns, 'div');
                    w.setAttribute('style', 'position:fixed;display:block;visibility:hidden;left:0;top:0;width:auto;height:auto;border:1px solid gray;padding:2px;margin:0;z-index:99999;overflow:hidden;cursor:move;'+(typeof w.style.borderRadius === 'string' ? 'background-color:#eaeaea;padding-top:0px;border-radius:4px;box-shadow:0 0 15px rgba(0,0,0,.4);' : 'background:-o-skin("Window Skin");'));
                    w.id = wId;
                    w.closeWin = function() {
                        doc.removeEventListener('keydown', keyDown, false);
                        gBrowser.tabpanels.removeEventListener('mousedown', mouseDown, false);
                        this.parentNode.removeChild(this);
                        w = null;
                    };
                    w.addEle = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'div');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
/* https://forum.mozilla-russia.org/viewtopic.php?pid=807299#p807299
                    w.addEle1 = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'textarea');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
*/
                    var img = doc.createElementNS(xhtmlns, 'div');
                    img.setAttribute('style', 'display:block;float:right;width:16px;height:16px;padding:0;margin-top:2px;margin-right:1px;border:none;cursor:pointer;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACQUlEQVR4Xm2SPUhbURTHfzfNJ4nU0Axx7CJCN2kQtIgVCtq6OdQuBUftWIQqFNsKDo6iHTqIqN3a0kklUKTS2AZrcOjioFtRjFWUfH+803cS9GHwB4dzcu75/+8L9xgaSEDbLXhaASzAyXAMn5/DHxwwjeJgKLRxd3Q0atxuLhGgWqmQmp9PJ7LZJ5Ow3WhQFweDG20LC1FfRwc3kUsm+T48fPIzl3s8VTdxxLt+/2FhdlZkc1MkmdR8Pba2ajlrz3z1+9OvIOaIfb7DwsSEyNKSyM6OSLksEo/rbw2ttadntV7Gnv3k86VfqskvmDzv7hZ580Zkelrk4kJqFIsii4satVrRs+rYmBQHBuRvS4u8g/fuCmDOz+HgAIWpKRgfh3AYhoZQ8Hqxjo4o9PdT3d1FcQEl8KoBnJ7C/j6KZkZGYG4OIhEUFed7erD29q69jGrd1qVBqcQVKgwEuMSEQhjtXRk4JvW/kM1i2WGMga4uzMoKBIOIfbNiolEC6+uU+/qQRALFA1QBlwVYxmBcLmhuxqytQSgEKu7t1dBavwKvbeINh/Has2IMqnXpem4bky6qWLdvZgaOj2FwEHNyoqG19vRMZ8jZs1+NOfsNcZS3EFvzeNL51laRe/dEYrFadsLpZeyZDx7P6QN4Btwx1OE1xO4HAquP2tsjAZ+Pm8gWi3xMpc6W8/kXPyAO/DM4oOsZa2pafdjZGXF7PAgOpXKZL4nE2XImcyUGMDSg63kbhgvgtYCq8+akIL4J3y7Fyn+DokZOnLlMyQAAAABJRU5ErkJggg==");background:-o-skin("Caption Close Button Skin");');
                    img.title = (win.navigator.language.indexOf('ru') == 0) ? '\u0417\u0430\u043A\u0440\u044B\u0442\u044C' : 'Close';
                    img.addEventListener('click', function() {this.parentNode.closeWin();}, false);
                    w.appendChild(img);
                    var title = w.addEle(title, 'display:table;color:#000;font:17px Times New Roman;width:auto;height:auto;padding:0;margin:0 2px;cursor:text;');
                    title.onclick = e => {
                        e.preventDefault();
                        var url = e.target.href;
                        // Здесь открываем url как хотим.
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                        doc.getElementById(wId).closeWin();
                    };
/* https://forum.mozilla-russia.org/viewtopic.php?pid=807299#p807299
                    var cnt = w.addEle1(text, 'display:block;border:1px solid #aaa;padding-bottom:3px;padding-left:3px;background-color:#fafcfe;color:#000;font:16px Times New Roman;width:310px;height:160px;overflow:auto;cursor:text;-moz-user-focus:normal;-moz-user-select:text;');
                    cnt.contentEditable="true";
                    cnt.context="contentAreaContextMenu";
*/
                    var cnt = doc.createElement("textarea");
                    cnt.style.cssText = `
                        color: #000;
                        width: 310px;
                        height: 160px;
                        outline: none;
                        padding-left: 3px;
                        padding-bottom: 3px;
                        border: 1px solid #aaa;
                        background-color: #fafcfe;
                        font: 17px Times New Roman;
                    `;
// if (text) cnt.value = text; --> if (text) cnt.value = text.trim();
                    if (text) cnt.value = text.trim();
                    w.append(cnt);
                    w.addEle(status, 'display:table;font:12px Times New Roman;font-weight:bold;color:blue;width:auto;height:auto;padding-top:2px;margin:0 3px;cursor:pointer;');
                    w.addEventListener('mousedown', function(e) {
                        if (e.target == w) {
                            e.preventDefault();
/* https://forum.mozilla-russia.org/viewtopic.php?pid=807257#p807257
                            var grabX = e.clientX, grabY = e.clientY, origX = parseInt(w.style.left), origY = parseInt(w.style.top);
                            var mouseMove = function(ev) {
                                w.style.left = origX+ev.clientX-grabX+'px';
                                w.style.top = origY+ev.clientY-grabY+'px';
                            };
*/
                            var st = w.style;
                            var mouseMove = e => {
                                st.top = parseInt(st.top) + e.movementY + "px";
                                st.left = parseInt(st.left) + e.movementX + "px";
                            }
                            doc.addEventListener('mousemove', mouseMove, false);
                            doc.addEventListener('mouseup', function() {doc.removeEventListener('mousemove', mouseMove, false);}, false);
                        }
                    }, false);
                    doc.documentElement.appendChild(w);

                    if (size) {
                        cnt.style.height = size.height;
                        cnt.style.width = size.width;
                    } else {
                        for (var i = 3; i < 10; i++) {
                            if (cnt.scrollHeight > cnt.offsetHeight || cnt.scrollWidth > cnt.offsetWidth) {
                                cnt.style.height = 80*i+'px';
                                cnt.style.width = 160*i+'px';
                            } else
                                break;
                        }
                    }

                    var docEle = (doc.compatMode == 'CSS1Compat' && win.postMessage) ? doc.documentElement : doc.body;
                    var mX = docEle.clientWidth-w.offsetWidth, mY = docEle.clientHeight-w.offsetHeight;
                    if (mX < 0) {cnt.style.width = parseInt(cnt.style.width)+mX+'px'; mX = 0;}
                    if (mY < 0) {cnt.style.height = parseInt(cnt.style.height)+mY+'px'; mY =0;}
                    var hW = parseInt(w.offsetWidth/2);
                    w.style.left = (pos && pos.X < mX+hW ? (pos.X > hW ? pos.X-hW : 0) : mX)+'px';
                    w.style.top = (pos && pos.Y+10 < mY ? pos.Y+10 : mY)+'px';
                    w.style.visibility = 'visible';
                    doc.addEventListener('keydown', keyDown, false);
                    gBrowser.tabpanels.addEventListener('mousedown', mouseDown, false);
// https://forum.mozilla-russia.org/viewtopic.php?pid=807326#p807326
                    if (text) {
                        var st = cnt.style;
                        var div = cnt.editor.rootElement;

                        var range = new Range();
                        range.selectNode(div.firstChild);
                        var rect = range.getBoundingClientRect();

                        let w = Math.ceil(rect.width);
                        if (cnt.scrollTopMax) {
                            if (!matchMedia("(-moz-overlay-scrollbars)").matches) // ???
                                w += InspectorUtils.getChildrenForNode(div, true, false).at(-1).clientWidth;
                        }
                        else st.height = Math.max(50, Math.ceil(rect.height) + 1) + "px";
// к строке выше; изменить, если font-size > 22 https://forum.mozilla-russia.org/viewtopic.php?pid=807332#p807332
                        st.width = Math.max(200, w) + "px";
                    }
                    return w;
                };

                var getHash = function (txt) {
                    TKK=eval('((function(){var a\x3d817046147;var b\x3d-335196159;return 410049+\x27.\x27+(a+b)})())');
                    function sM(a) {
                        var b;
                        if (null !== yr)
                            b = yr;
                        else {
                            b = wr(String.fromCharCode(84));
                            var c = wr(String.fromCharCode(75));
                            b = [b(), b()];
                            b[1] = c();
                            b = (yr = window[b.join(c())] || "") || "";
                        }
                        var d = wr(String.fromCharCode(116)), c = wr(String.fromCharCode(107)), d = [d(), d()];
                        d[1] = c();
                        c = "&" + d.join("") + "=";
                        d = b.split(".");
                        b = Number(d[0]) || 0;
                        for (var e = [], f = 0, g = 0; g < a.length; g++) {
                            var l = a.charCodeAt(g);
                            128 > l ? e[f++] = l : (2048 > l ? e[f++] = l >> 6 | 192 : (55296 == (l & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (l = 65536 + ((l & 1023) << 10) + (a.charCodeAt(++g) & 1023),
                            e[f++] = l >> 18 | 240,
                            e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224,
                            e[f++] = l >> 6 & 63 | 128),
                            e[f++] = l & 63 | 128);
                        }
                        a = b;
                        for (f = 0; f < e.length; f++)
                            a += e[f],
                        a = xr(a, "+-a^+6");
                        a = xr(a, "+-3^+b+-f");
                        a ^= Number(d[1]) || 0;
                        0 > a && (a = (a & 2147483647) + 2147483648);
                        a %= 1E6;
                        return c + (a.toString() + "." + (a ^ b));
                    }

                    var yr = null;
                    var wr = function(a) {
                        return function() {
                            return a;
                        };
                    }, xr = function(a, b) {
                        for (var c = 0; c < b.length - 2; c += 3) {
                            var d = b.charAt(c + 2), d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d), d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
                            a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d;
                        }
                        return a;
                    };
                    return sM(txt);
                };

                var ujs_google_translate = function (dir) {
                    var lng = window.navigator.language.slice(0, 2), txt = gContextMenu.selectionInfo.fullText, l = dir.split('|');
                    var encTxt = encodeURIComponent(txt);
                    var winWait = function(lng) {createWindow('', (lng == 'ru' ? 'Подождите идет перевод' : 'Wait, is going Translating')+'\u2026', 'Google Translate', '_gt', window.navigator.lastClick);};
                    if (txt) {
                        winWait(lng);
                        var xhr = new XMLHttpRequest();
                        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + l[0] + '&tl=' + l[1] + '&hl=' + lng + '&eotf=0&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t' + getHash(txt);
// для urlt изменил http --> https (14.10.2023); тестирую
                        var urlt = "https://translate.google.com/translate_t?text=" + encTxt + "&sl=' + langFrom_google_text + '&tl=' + langTo_google_text +'&hl=' + lng + '&eotf=0&ujs=gtt";
                        xhr.open('POST', url, true);
                        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
                        xhr.onreadystatechange = function() {
                            try {
                                if (xhr.readyState == 4 && xhr.status == 200) {
//                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, ""));
                                    for (var i = 0, n; n = tmp[0][i]; i++) {
                                        if (n[0])result += n[0].toString();
                                    };
                                   status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                                   createWindow(result, status, '<a href="'+urlt.replace(/&/g,'&amp;')+'" target="_blank" style="display:inline;padding:0;margin:0;text-decoration:none;border:none;color:#009;font:16px Times New Roman;">Google Translate</a>', '_gt', window.navigator.lastClick);
                                }
                            } catch(e) {};
                        };
                        xhr.send('q=' + encodeURIComponent(txt));
                    } else {
                        var urlt = gBrowser.currentURI.spec;
// Изменил строку ниже. Добавил параметр '&tl='+l[1] https://forum.mozilla-russia.org/viewtopic.php?pid=796999#p796999 & http --> https (14.10.2023)
                        var url = "https://translate.google.com/translate?u=" + encodeURIComponent(urlt) + '&tl=' + l[1] + "&hl=" + lng + "&langpair=" + dir + "&tbb=1";
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                    };
                };
                var contextMenu = document.getElementById("contentAreaContextMenu");
                var nextEleMenu = document.getElementById("context-inspect");

                var menuItem = document.createXULElement("menuitem");
                menuItem.setAttribute("id", "context-ru-google-translate");
                menuItem.setAttribute("label", "Перевести на русский");
                menuItem.setAttribute("class", "menuitem-iconic");
                menuItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABBUlEQVR4Ac2RMU4DMRREB2MqihQpAkUaqnAduuQw4Sx03CGcJYIiDYqQ0lCsZ77535ZFcgAkRuPvV7zd/dLiz3N1PFb8hmRckrfELZrF9ONQ1B6Yz0MyM7S0O6zGkVprh/3+Kw/JzkHSpRpJKZkpr9fYbOx0cjVsCbWG31oHBM9mtt0q73ZcrdLhAMlYQKkUI22aqsNo8HKZAMQhvVaKSHWj2Q2aPU3mJQFY7nuHyvYFduP83WF3AJRfcPNYnr/Lp1G1uK4m9sno1LaUbnX/htf8BNzoneUD5NhjvLCMhURQSQ93QCZwXYjFwg3I0NZKrvoknQPMIHkt/jRAQKMeG2yX89/mB4EJbKbZxIhFAAAAAElFTkSuQmCC");
                menuItem.addEventListener("command", function() {ujs_google_translate('auto|ru');}, false);
                contextMenu.insertBefore(menuItem, nextEleMenu);

                menuItem = document.createXULElement("menuitem");
                menuItem.setAttribute("id", "context-en-google-translate");
                menuItem.setAttribute("label", "Перевести на английский");
                menuItem.setAttribute("class", "menuitem-iconic");
                menuItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAAB5UlEQVR4AWOgPZg0c9+Oq2+qpx9LaQaiw4mNB27efbs2uXFNQh2Q4Zm9zSNth23MhqjGnXMC86vnHmUS4udxv7OvOd2yPE7PTlfCTk9GVVEoyJA7WI8dyPA0k3U1l+jJMV7CeThpTZeoKDdDUsvBPSff/V+6/P/Bvf///2+bdvU/EKRm/k9IBNIlfZe+LFrzvbsbyJ636pKkyQyQhoUb7tZNvPpw3/0/TfX/ISAm5n94BIiRm/n/xYsLd946ei4vajoiqDuRAejQf//+o4PQ0P9+fmhiQGVnrr1kXJNYG2zEzXDxDsPv3wy/fkFJoOTPn39+/fr/69e/37//AUX+/mXRkO458wWHDba2f8wswCyQ3N+/f//8/fP7z5+TV18w2Mdt6plzPiptx5Wr7/+/ffMzOR6kysjkt74hWMPfp06O+2dulZCeFZKwnkE4m8E8bN3i9aCQ+dxY96GrK6bkGJD9R1v3l6Y20NVW9kt+/v//ZdbcFwkxTVPPMQimM8XEqkb7a/zoqLxkH9v4zVyI5+///wyMmtKMmhJAnbLqQjHxaw5r2orOmZN5uj470Zahat6BKS7JzpmLBHXm8GhM5lCdcP7ai1ZpixZxk9NXXjKIFzOIFjAI54oY1vXuue5qmscw+AAAW0tKxtPoicEAAAAASUVORK5CYII=");
                menuItem.addEventListener("command", function() {ujs_google_translate('auto|en');}, false);
                contextMenu.insertBefore(menuItem, nextEleMenu);

                contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);
            }
        }).init(this);


UPD: Заменил http --> https (2 правки, см. по дате 14.10.2023).

xrun1 пишет

Выложу здесь свой код google-translate.js.

Спасибо.


И у меня вопросик.
Возможно ли в этом скрипте использовать не Гугл, а другой переводчик?
Мне, например, больше нравиться DeepL.

unter_officer пишет

Возможно ли в этом скрипте использовать не Гугл, а другой переводчик?

Скрипт написал, примерно, в 2015-м году Алексей Рузанов aka Lex1. Остальные авторы его только модифицировали. Подробнее. Здесь есть сниппет для перевода Яндекса (сейчас не работает, проверил).
Всё это было давно, но, теоретически, возможно. Надо попробовать. В скрипте есть POST-запрос, надо смотреть, что передаётся гуглу или другому переводчику и получается в ответ. Это я так понимаю, теоретически...

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

Выделить код

Код:

var ujs_google_translate = function (dir) {
                    var lng = window.navigator.language.slice(0, 2), txt = gContextMenu.selectionInfo.fullText, l = dir.split('|');
                    var encTxt = encodeURIComponent(txt);
                    var winWait = function(lng) {createWindow('', (lng == 'ru' ? 'Подождите идет перевод' : 'Wait, is going Translating')+'\u2026', 'Google Translate', '_gt', window.navigator.lastClick);};
                    if (txt) {
                        winWait(lng);
                        var xhr = new XMLHttpRequest();
                        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + l[0] + '&tl=' + l[1] + '&hl=' + lng + '&eotf=0&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t' + getHash(txt);
                        var urlt = "http://translate.google.com/translate_t?text=" + encTxt + "&sl=' + langFrom_google_text + '&tl=' + langTo_google_text +'&hl=' + lng + '&eotf=0&ujs=gtt";
                        xhr.open('POST', url, true);
                        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
                        xhr.onreadystatechange = function() {
                            try {
                                if (xhr.readyState == 4 && xhr.status == 200) {
//                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, ""));
                                    for (var i = 0, n; n = tmp[0][i]; i++) {
                                        if (n[0])result += n[0].toString();
                                    };
                                   status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                                   createWindow(result, status, '<a href="'+urlt.replace(/&/g,'&amp;')+'" target="_blank" style="display:inline;padding:0;margin:0;text-decoration:none;border:none;color:#009;font:16px Times New Roman;">Google Translate</a>', '_gt', window.navigator.lastClick);
                                }
                            } catch(e) {};
                        };
                        xhr.send('q=' + encodeURIComponent(txt));
                    } else {
                        var urlt = gBrowser.currentURI.spec;
// Изменил строку ниже. Добавил параметр '&tl='+l[1] https://forum.mozilla-russia.org/viewtopic.php?pid=796999#p796999
                        var url = "http://translate.google.com/translate?u=" + encodeURIComponent(urlt) + '&tl=' + l[1] + "&hl=" + lng + "&langpair=" + dir + "&tbb=1";
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                    };
                };

unter_officer пишет

Мне, например, больше нравиться DeepL.

На всякий случай просто к сведению. Расширение Simple Translate https://addons.mozilla.org/ru/firefox/a … translate/ добавляет два своих пункта в контекстном меню "перевести выделенное" и "перевести страницу" + плавающую кнопку.

xrun1

Выложу здесь свой код google-translate.js.

Что-то он не хочет работать с моим  UCF.
Покажи куда его подключаешь.

rubel

Выделить код

Код:

load: [ // По событию "load"
            { path: "special_widgets.js", ucfobj: true, }, // <-- Special Widgets
            // { path: "auto_hide_sidebar.js", ucfobj: true, }, // <-- Auto Hide Sidebar
...
            { path: "google-translate.js", ucfobj: true, },

xrun1
Заработал, когда изменил false на true.
Но у меня везде прописано false и все скрипты работают, а этот отказался. :/

скрытый текст
6b698cf1f3544aa4ce6ed19660020f12.png

rubel пишет

аработал, когда изменил false на true

Давно заметил, что скрипты, которые делал/редактировал Виталий, надо подписывать true. Все остальные false.:D

Не знаю, куда написать. Отвалилось расширение async_run_applications@vitaliy.ru.xpi. Правильнее сказать, работает, но добавить ничего не получается. Кто пользуется, проверьте, пожалуйста.

xrun1 пишет

Отвалилось расширение async_run_applications@vitaliy.ru.xpi

Не юзаю, но проверил, то же самое. Скорее всего, дело в config.js.

xrun1 пишет

Отвалилось расширение async_run_applications@vitaliy.ru.xpi.

там надо поправить в двух местах

parent.js

Выделить код

Код:

//var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
//var {CustomizableUI} = ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
//ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
var Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;
var CustomizableUI = globalThis.CustomizableUI || ChromeUtils.import("resource:///modules/CustomizableUI.jsm").CustomizableUI;
Components.utils.importGlobalProperties(['IOUtils']);
Выделить код

Код:

//        await OS.File.writeAtomic(this._storeFile.path, JSON.stringify(data), { tmpPath: this._storeFile.path + ".tmp" }).then(() => {
        await IOUtils.writeJSON(this._storeFile.path, data, { tmpPath: this._storeFile.path + ".tmp" }).then(() => {

Farby
:beer:

Dumby - Приветствую!


Вопрос по скрипту перехвата click, dblclick и долгое нажатие на кнопках панелей. (2021 года)
Убрал долгое нажатие и при одинарном клике стал срабатывать одинарный клик и следом двойной.
Просьба исправить ложное срабатывание DBLClick, которое происходит после одинарного клика.


Если пауза между кликами примерно как в скрипте 300 мс, в консоли видно срабатывание DBLClick. (и мышь не виновата!)

Выделить код

Код:

(async (id) =>{ // hookClicks TEST BUTTON

var Mouse = { //meta*64 Ctrl*32 Шифт*16 Alt*8 (Wh ? 2 : But*128) long*4 dbl
	[id]: {
		0(){	console.clear();
			log('CLICK 0');
		},
		1(){ log('DUBLE 0')},
		4(){ log('LONG 0')},
}};
CustomizableUI.createWidget({
	id: id, label: id, tooltiptext: id, localized: false,
	defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated(btn){
		btn.style.setProperty("list-style-image","url(chrome://devtools/skin/images/settings.svg)");
	}
});
function log(s){console.log('■ '+ s +' '+ Math.random())}
var Mus = {}; Object.keys(Mouse).forEach((k) =>{Mus["#"+ k] = Mus["."+ k] = Mouse[k]});

var listener = { // дополнительные клики кнопок и перехват существующих
	filter(sel) {
		return this.closest(sel);
	},
	find(sel) { //условия запуска ?
		return Mus[sel][this] || Mus[sel][this + 1];
	},
	get selectors() {
		this.exec = (trg, obj, num) => {
			this.clickTID = null;
			obj[num](trg);
		}
		// this.onLongPress = (trg, obj, num) => {
		// 	this.mousedownTID = null;
		// 	this.longPress = true;
		// 	obj[num*4](trg);
		// }
		delete this.selectors;
		return this.selectors = Object.keys(Mus);
	},
	handleEvent(e) {
		if (this.skip || e.detail > 2) return;
		if (e.type == "mouseenter") return; //update tooltips
		var trg = e.target;
		var sels = this.selectors.filter(this.filter, trg);
		var {length} = sels;
		if (!length) return;
		var dbl = e.detail == 2;
		var wheel = e.type.startsWith("w");
		var num = e.metaKey*64 +e.ctrlKey*32 +e.shiftKey*16 +e.altKey*8 +(wheel ? 2 : e.button*128 +dbl); //long*4
		var obj = Mus[
			length > 1 && sels.find(this.find, num) || sels[0]
		];
// wheel
		if (wheel) return obj[num]?.(trg, e.deltaY < 0);
// mousedown
		if (e.type.startsWith("m")) {
			obj.mousedownTarget && this.stop(e);
			if (dbl) return;
			// this.longPress = false; //+ задержка при обычном клике
			// if (++num in obj)
			// 	this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num);
			if (e.button == 2)
				this.ctx = trg.getAttribute("context"), trg.setAttribute("context","");
			return;
		}
// click
		obj.mousedownTarget || this.stop(e);
		// if (this.longPress) return this.longPress = false;
		dbl
			? this.clickTID &&= clearTimeout(this.clickTID)
			: this.mousedownTID &&= clearTimeout(this.mousedownTID);

		if (!obj[num]) {
			if (e.button == 1) return;
			if (e.button) {
				num = "context";
				for(var p in this.a) this.a[p] = e[p];
			} else
				num = "dispatch", this.mdt = obj.mousedownTarget;
			obj = this;
		}
		dbl
			? obj[num](trg)
			: this.clickTID = setTimeout(this.exec, 300, trg, obj, num);
	},
	get mdEvent() {
		delete this.mdEvent;
		return this.mdEvent = new MouseEvent("mousedown", {bubbles: true});
	},
	context(trg) {
		this.ctx
			? trg.setAttribute("context", this.ctx)
			: trg.removeAttribute("context");
		trg.dispatchEvent(new MouseEvent("contextmenu", this.a));
	},
	dispatch(trg) {
		this.skip = true;
		this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click();
		this.skip = false;
	},
	stop: e => {
		e.preventDefault(); e.stopImmediatePropagation();
	},
	a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}
};

var events = ["click", "mousedown", "wheel", "mouseenter"];
var els = document.querySelectorAll("#navigator-toolbox,#ucf-additional-vertical-bar,#appMenu-popup,#widget-overflow-mainView");
for(var el of els)
	for(var type of events)
		el.addEventListener(type,listener,true);
ucf_custom_script_win.unloadlisteners.push(id);
ucf_custom_script_win[id] = {destructor(){
	for(var el of els)
		for(var type of events)
			el.removeEventListener(type,listener,true);
}};

})('ucf_hookClicks_test'); //клики-подсказки
Dobrov пишет

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

Что-то не вижу такого.

Если пауза между кликами примерно как в скрипте 300 мс, в консоли видно срабатывание DBLClick

Да, согласен, 300 — это довольно погранично.
Второй клик, тогда, может быть расценен как двойной или как одинарный.


Если, зачем-то, есть желание лупить по клавише
как-то хитро, не быстро и не медленно, то чтобы всегда
было либо DUBLE, либо CLICK CLICK (то есть, чтобы не было CLICK DUBLE)
можно поднять таймаут до каких-нибудь 500.

Dumby пишет

Да, согласен, 300 — это довольно погранично.
Второй клик, тогда, может быть расценен как двойной или как одинарный.

Чём больше задержка, тем позже срабатывает действие. То есть, после одинарного клика сначала пауза на время двойного клика, потом действие.
При использовании системных cliclk dblclick таких задержек нет.
Ложных срабатываний не было в скрипте с обычным кликом и долгим нажатием, а двойной клик даёт ложные сработки, специально под Windows проверял:
2023-11-02-21-55-27.png


Dumby – может придумаешь способ назначить перехват кликов на всех кнопках панелей, используя системные onclick ondblclick ?
Это вообще возможно? Перехватывать onclick ondblclick клики всех кнопок указанных панелей вместо mousedown ?
так делается в скриптах с одной кнопкой, и клики определяет система, поэтому за одинарным не будет срабатывать двойной:

Выделить код

Код:

btn.linkedObject = this;
for(var type of ["command", "contextmenu"]) // тут dblclick можно добавить…
	btn.setAttribute("on" + type, `linkedObject.${type}(event)`);

Что ТУТ надо добавить/убавить чтобы окошко с переводом закрывалось только при нажатии на кнопку "закрыть ❌"?

Dumby — твоя кнопка Быстрое переключение опций about:config содержала два контекстных меню.


Я оставил одно меню, — прошу убрать лишний код поддержки нескольких меню: popups, popupshowing… (может попроще код станет)
Актуальные версии скриптов: ucf_hookClicks.js, ucf_QuickToggle.js

ucf_QuickToggle.js

Выделить код

Код:

/* Быстрое переключение опций about:config для окна [ChromeOnly] © Dumby

30 скрытых настроек. Ctrl+Click или Правый: сброс опции по-умолчанию
клик по параметру с Shift или колёсиком блокирует авто-закрытие меню
⟳ Обновить страницу, ↯ Перезапуск браузера
строки с pYellow = шрифт italic, цвет = ключ, пусто:Red
	refresh: false - reload current tab,	true - reload skip cache
	restart: false - restart browser,	true - restart with confirm */

(async (name, func) => { // mod by Dobrov, нужен ucf_hookClicks.js
	return CustomizableUI.createWidget(func()); //only UCF
})(this.constructor.name, () => {

	var {prefs} = Services, db = prefs.getDefaultBranch(""), ua = glob.ua(true), //real ЮзерАгент
	I = [AppConstants.platform == "win" ? '#124' : '#e8e8e8', //текст под курсором, без Aero '#fff'
	"https://antizapret.prostovpn.org/proxy.pac", "user.pacfile",
	glob.pref(['browser.sessionstore.interval', 15e3]),
	parseInt(Services.appinfo.version),"general.useragent.override",
	"Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
	"Android 12.0; Mobile; rv:109.0) Gecko/97.0 Firefox/97.0","Mozilla/5.0 ("],
	fonts = arr => arr.map(n => [(n == arr[arr.length-1] ? null : n), n]), //array с вложениями
	serif = fonts("Arial|Cantarell|DejaVu Sans|Roboto|PT Serif|Segoe UI|Ubuntu|Cambria|Fira Sans|Georgia|Noto Sans|Calibri|Times|системный".split('|')), sans = [["PT Sans","PT Sans"], ...serif]
	hints = new Map([ //опция отсутствует ? выполнить код и вернуть строку
		["ucf.savedirs", `glob.crop(glob.dirsvcget(""),34)`], [I[5], `glob.ua()`]]), //текущий ЮзерАгент
	secondary = [{ // menu: [apref, lab, akey, hint, [undef, str], code] radio: [val, lab, str-val, add-hint, code]
		pref: ["dom.disable_open_during_load", "Всплывающие окна"], pDefGreen: 2, pYellow: true,
		values: [[true, "Блокировать"], [false, "Разрешить"]]
	},{
		pref: ["javascript.enabled", "Выполнять скрипты Java",,"Поддержка интерактивных сайтов, рекламы\nтакже разрешает действия горячих клавиш"], pDefGreen: true, refresh: true,
		values: [[true, "Да"], [false, "Нет"]]
	},{
		pref: ["browser.safebrowsing.downloads.remote.block_dangerous", "Опасные файлы, сайты",,"browser.safebrowsing.downloads.remote.block_dangerous_host"], pDefGreen: true, pYellow: false,
		values: [[true, "Запрет",,,`glob.pref('browser.safebrowsing.downloads.remote.block_dangerous_host',true)`], [false, "Открыть",,,`glob.pref('browser.safebrowsing.downloads.remote.block_dangerous_host',false)`]]
	},{
		pref: ["ucf.savedirs", "Загрузки",,'Пути сохранения Страниц и Графики\nСинтаксис «_Html/subdir|_Pics/subdir»\nsubdir: пусто | 0 заголовок | 1 домен',
			["", "всё в общей папке"]], pDefGreen: "_Сайты||_Фото|0", pYellow: "_Web|1|_Images|0", pGray: "",
		values: [ // сохранение Html/Pics. [Загрузки]/"_Html/subdir|_Pics/subdir" subdir: пусто | 0 заголовок | 1 домен
			["", "всё в общую папку"],
			[`_Сайты||_Фото|0`, "_Сайты|_Фото/имя…"],
			[`_Web||_Photo|0`, "_Web|_Photo/имя"],
			[`_Web|1|_Pics|1`, "_Web/сайт|_Pics/…"],
			[`_Web|0|_Pics|`, "_Web/имя|_Pics"],
			[`_Web|1|_Images|0`, "_Web/сайт, _Images/имя"], //открыть опцию about:config:
			[`Сайт||Фото|`, "ввести свои пути",,"ключ в about:config",`glob.about_config("ucf.savedirs")`]]
	},null,{
		pref: ["network.proxy.autoconfig_url", "Прокси (VPN)", "п", "network.proxy.type\n\nПереключение сетевых настроек"],
		pDefGreen: "localhost", pYellow: I[1], pGray: "", refresh: true,
		values: [ //фон кнопки Меню: серый, голубой, красный, жёлтый, зелёный
			["localhost", "системный", "0",, `glob.pref('network.proxy.type', 0)`],
			["127.0.0.1", "Tor или Opera", "1", "Необходим сервис tor или opera-proxy",
				`glob.pref('network.proxy.type', 1)`],
			[I[1], "АнтиЗапрет", "2", "Надёжный доступ на заблокированные сайты\n«Режим прокси» меняется на 2",
				`glob.pref('network.proxy.type', 2)`],
			// ["https://git.io/ac-anticensority-pac", "ac-anticensority", "3"],
			[glob.pref([I[2], "file:///etc/proxy.pac"]), "user .pac файл", "4", "about:config "+ I[2]], // нужен диалог выбора pac-файла
			[null, "сброшен",""]]
	},{
		pref: ["network.proxy.type", "Режим прокси", "р"], pDefGreen: 5, pYellow: 2, pGray: 1, refresh: true,
		values: [
			[5, "системный", "5"],
			[0, "Без прокси", "0", "по-умолчанию"],
			[2, "Автонастройка", "2", "about:config "+ I[2]],
			[1, "Ручная настройка", "1", "Используется network.proxy.autoconfig_url"],
			[4, "Автоопределение", "4"] ]
	},{
		pref: ["network.trr.mode", "DNS поверх HttpS",, "Шифрование DNS-трафика для\nзащиты персональных данных"], pDefGreen: 0, pYellow: 2, pGray: 5, refresh: true,
		values: [
			[0, "по-умолчанию", "0"], [1, "автоматически", "1", "используется DNS или DoH, в зависимости от того, что быстрее"], [2, "DoH, затем DNS", "2"], [3, "только DoH", "3"], [4, "DNS и DoH", "4"], [5, "отключить DoH", "5"] ]
	},{
		pref: ["network.cookie.cookieBehavior", "Получать куки",, "Персональные настройки посещённых сайтов"], pDefGreen: 3, pYellow: 0, pGray: 4,
		values: [[0, "со всех сайтов"], [3, "посещённые сайты"], [4, "кроме трекеров"], [1, "кроме сторонних"], [2, "никогда"]]
	},null,{
		pref: ["browser.display.use_document_fonts", "Загружать шрифты страниц"], pDefGreen: 1, pGray: 0, refresh: true,
		values: [[1, "Да"], [0, "Нет"]]
	},{
		pref: ["font.name.sans-serif.x-cyrillic", "Шрифт без засечек ",,"Также влияет на всплывающие подсказки\nСистемный: загрузка шрифтов документа"], pDefGreen: "", pYellow: "Roboto", pGray: "Arial", values: sans
	},{
		pref: ["font.name.serif.x-cyrillic", "Шрифт с засечками"], pDefGreen: "", pYellow: "Arial", values: serif
	},{
		pref: ["gfx.webrender.force-disabled", "Ускорять отрисовку страниц", ,"gfx.webrender.compositor.force-enabled\ngfx.webrender.all\n\nАппаратная отрисовка страниц видеокартой.\nотключите при разных проблемах с графикой"],
		pDefGreen: false, pYellow: true, pGray: undefined, restart: true, values: [
		[true, "Нет",,,`[["gfx.webrender.compositor.force-enabled", false], ["gfx.webrender.all", false]].map((a) =>{glob.pref(...a)})`],
		[false, "Да",,,`[["gfx.webrender.compositor.force-enabled", true], ["gfx.webrender.all", true]].map((a) =>{glob.pref(...a)})`]]
	},null,{
		pref: ["media.autoplay.default", "Авто-play аудио/видео"], pDefGreen: 0, pYellow: 2, pGray: 5, refresh: true,
		values: [
			[0, "Разрешить", "0"], [2, "Спрашивать", "2"], [1, "Запретить", "1"], [5, "Блокировать", "5"]]
	},{
		pref: ["dom.storage.enabled", "Локальное хранилище",, "Сохранение персональных данных, по\nкоторым вас можно идентифицировать"],
		pDefGreen: false, pYellow: true,
		values: [[true, "Разрешить"], [false, "Запретить"]]
	},{
		pref: ["privacy.resistFingerprinting", "Изоляция Firstparty-Fingerprint", ,"privacy.firstparty.isolate\n\nЗащита данных пользователя также\nзапрещает запоминать размер окна"], pDefGreen: false,
		values: [[true, "Да", , "Защита от слежки",`glob.pref('privacy.firstparty.isolate', true)`], [false, "Нет", , "Защита от слежки",`glob.pref('privacy.firstparty.isolate', false)`]]
	},(()=>{
	if (I[4] > 114) return { //опции зависят от версии FF
		pref: ["browser.translations.enable", "Встроенный перевод сайтов"], pDefGreen: true, pGray: false, refresh: true,
		values: [[true, "Да"], [false, "Откл",,,`glob.toStatus("Перевод отключен для новых вкладок")`]]
		}; else return {
		pref: ["media.peerconnection.enabled", "WebRTC ваш реальный IP"], pDefGreen: false,
		values: [[true, "Выдать"], [false, "Скрыть"]]
		}
	})(),null,{
		pref: ["network.http.sendRefererHeader", "Referer: для чего"], pDefGreen: 2, pYellow: 1,
		values: [[0, "Ни для чего", "0"], [1, "Только ссылки", "1"], [2, "Ссылки, графика", "2"]]
	},{
		pref: ["browser.cache.disk.capacity", "Кэш браузера",,"\ncache.memory.max_entry_size:\nДиск и память: 5120\nтолько Память: -1"], pDefGreen: 1048576, pYellow: 0, pGray: 256e3,
		values: [
		[256e3, "По-умолчанию"],
		[1048576, "Диск и Память",,,`[["browser.cache.memory.enable", true], ["browser.cache.disk.enable", true], ["browser.cache.memory.max_entry_size", 5120]].map((a) =>{glob.pref(...a)})`],
		[0, "только Память",,,`[["browser.cache.memory.enable", true], ["browser.cache.disk.enable", false], ["browser.cache.memory.max_entry_size", -1]].map((a) =>{glob.pref(...a)})`],
		[2097152, "только Диск",,,`[["browser.cache.memory.enable", false], ["browser.cache.disk.enable", true]].map((a) =>{glob.pref(...a)})`]]
	},{
		pref: ["browser.sessionstore.interval", "Резервирование сессий",,"Браузер резервирует сессии на\nслучай сбоя, снижая ресурс SSD"], pDefGreen: 3e5, pYellow: I[3], pGray: 15e3,
		values: [
		[I[3], `${I[3]/60e3 + " мин"}`], [15e3, "15 сек"], [6e4, "1 мин"], [3e5, "5 мин"], [9e5, "15 мин"], [18e5, "30 мин"]]
	},{
		pref: [I[5], "User Agent",,"Тип гаджета меняет вид сайта", [ua, "встроенный"]],
		pDefGreen: ua, pYellow: I[8] + I[6], pGray: I[8] + I[7], refresh: true,
		values: [ [ua, "По-умолчанию"],
			[I[8] + I[6], "Chrome 118 Win10"], [I[8] + I[7], "Firefox 97 Android 12"],
			[I[8] + "Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)", "MSIE 6.0 Windows"],
			["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
			[I[8] + "Linux; Android 5.1.1; SM-G928X Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", "Samsung Galaxy S6"],
			[I[8] + "PlayStation 4 3.11) AppleWebKit/537.73 (KHTML, like Gecko)", "Playstation 4"],
			["Windows NT 10.0; Win64; x64; Xbox; Xbox One) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edge/44.18363.8131", "Edge 44 Xbox One"],
			[I[8] + "compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; SAMSUNG; GT-I8350)", "Windows Phone"],
			[I[8] + "Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.143 YaBrowser/22.5.0.1916 Yowser/2.5 Safari/537.36", "Yandex OSX"],
			[I[8] + "compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"]]
	}];
	return {
		id: "ToggleButton", label: "Журнал, Меню опций", localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		icon: "data:image/webp;base64,UklGRkYCAABXRUJQVlA4WAoAAAAQAAAAFwAAFwAAQUxQSJcAAAANcGJr25S8mH4SNGxAgsguPCZ0BcMOiDSNRpvVaCTa3AI2qm2e2ofz7OuwgIiYgMzb4kVjGvegnQFAwXgFVnvcOmtnMJtutAa3Vo4mhRMDpWq8AjfU+yQoYf1/oTkTIdUrknKswNQ5yAdDzqgr4CYbsJWQfEyEU5QNEl2eKFM2AAIbjmSIMjwXPGyEdj6Bdn4mj9KO63sAAFZQOCCIAQAAdAgAnQEqGAAYAD6dRppKgoCqgAE4lsAKwgisgG27uzPePSvBIu/Pr0HJqW+AfoAIHl2DrAnRo/G3JBpTx8yE7L6LFQyD+yUNvuRYAAD+7mwmpaoBcsJ1hVKsMI2ucqid8qndm+WEvH4l4il6lA8FPscgnrRHrnSjjyNcfUV21+TkfqOWKou2UvVsZSl1z+jKs760Vij5XCWF9Uo6TZAhKfrJpeILyQYwq2Ee/g1uyEH/dJMI/91DsVpI6i2vV/Jqpd4/KniJtTm1woLvaotA2ikt3eeBaqlHf8WPe++lSWS7fETjgvzzbflp0Rj+v23kbb9e/VjUcPaD83shRuwzEo6CAO/AGxE+Zwbvv9NDsQT6T+S4CCDOFTuMRVv9/0E4P+uK+Vc3bMfQQD05gY/fes+ZX6ZHkvFdMn7zX8LMVvI59p7F806HPD2lBjs4lWWhQ5ckJDNflZL49370shr3/Q9uMJN9i/NVCu4OT7K3+4+/RkAMnjuY09u+3i4y4CldQG789iIAAAA=",
		defGreen: "data:image/webp;base64,UklGRgoBAABXRUJQVlA4TP0AAAAvD8ADEI9hpG2kwpf60h8+nBQkbSSp/tW9hIMPFw5E0kaSKuP9q3kJDx8ePDjQSCQp2z8RkhhIHBVAEdD9J4JYEkEUJSiDD0EIEcFgBSIspCAcbdlyRzfeYpwnDotGYFHIfXe6pdToDwUE2bZNRzds24qTx9b8h+chHET0fwLiX3vNO6jyh5aWbwMaqX3Ttu/j0VDG+5ZB9nQ2nYwBUGepB8vqYH9v+qrpnsESzDcnh9i3DZ4oB3cfPD0+kmSTuyCucHH+8dXwLphnt5+6vgQlz3KwrB90f3drUFqXYE/zJz9KsufsQbatX741WnxtabN4hjds8X2veQdVxr8CAA==",
		Gray: "data:image/webp;base64,UklGRsgAAABXRUJQVlA4TLsAAAAvD8ADEFW4rbVtaVj67ViCCWhDSugiHSSVle7uMoJnhO973wgztW2IIZo4RvFEMdqu2rZtGJfNqWfYX0SisXgaGvkBFK5k4EEDTtS7Q31XN3ei94VeQuI61k6unuRaB86CMExsYWnFt+imhRgmCMMWJmYWliTRJgrYIAzfJfUMSCK7hDdji3VQkkTjE9hMrsCmNheZy9gzEqlhndwlMoJ5NSqr+KCIfPuDUm+Uoz7+FEmkmIjg/Pcf/6EBAA==",
		Red: "data:image/webp;base64,UklGRhwBAABXRUJQVlA4TBABAAAvD8ADEL+hNJKkaOTJkycJn7CQJ5GjeXIgE4ZY/5Qq+HyMRJJyEvkR6K+IQwQk8qSRSFJWfgwkMZD0b0IC1KZtwDhl9h1Afx+SIEmAIEmChO+PwVWRpOsj2EgllBRDKy1EiJmhs5ztdaTnOAkEFBjQIGAdDiPJNq37bdu2nX9uDOGevQcR/SeQtNn+Aovh/vL2eF1FYR/cz9tNJKgHvf+4jseiYn59JAC38yqV8N57SDibw3OXzaQlyWKE3dPhtSzkshkiAttSHa4VLRXzqkqhogJIvVEjLbNWScIVeC3araaGoOqxeDk89z3rdphZzMITuF3mA/SJCFA2B9xPs9FkbKTsP8JcDtMhB7nQ747/CA==",
		Yellow: "data:image/webp;base64,UklGRv4AAABXRUJQVlA4TPEAAAAvD8ADEFeBppGkTMdJQAKSkI0MSsoTgAhDkW1sG3VEF0EER06Mtm1jWBi4/68cNBJJykpiIHFUQNK/CQlIOO+Lr4fmLsUtio7rgkBiUAjszusVxAQkHiEB8xAnXoQwz5sOM4QYHEaSbVrnWf8/21b+GX6GsBvRfwJBCHHADPlLlGQhM0Qfif18v7HTj9/3mI7z4onp8Wrkj+t2ANg4+pEgdYZ5WTdwmQcnFWRF2w8zAPZtkQnCik3bAWhZMRRQAVDVrEoFCAWZoamkApKakQlSV4f2BnBTQRSYxgcGkQDx0zLfWM+PYwLPNml7iH//+A8AAA==",
		onCreated(btn) {
			btn.setAttribute("image", this.icon);
			var doc = btn.ownerDocument;
			btn.domParent = null;
			btn.popups = new btn.ownerGlobal.Array();
			this.createPopup(doc, btn, "secondary", secondary);
			btn.linkedObject = this;
			for(var type of ["contextmenu", "command"]) // "mousedown" "auxclick" события
				btn.setAttribute("on" + type, `linkedObject.${type}(event)`);
			this.addSheet(btn);
		},
		addSheet(btn) { //текст под курсором
			var cb = Array.isArray(btn._destructors);
			var id = cb ? btn.id : "ToggleButton";
			var css = `#${id} menu[_moz-menuactive] {
				color: ${I[0]} !important;
			}`;
			var args = [
				"data:text/css;charset=utf-8," + encodeURIComponent(css),
				Ci.nsIDOMWindowUtils.USER_SHEET
			];
			if (cb) var destructor = function() {
				this.removeSheetUsingURIString(...args);
			}
			var add = b => b.ownerGlobal.windowUtils.loadSheetUsingURIString(...args);
			(this.addSheet = !cb ? add : btn => {
				add(btn);
				btn._destructors.push({destructor, context: btn.ownerGlobal.windowUtils});
			})(btn);
		},
		createPopup(doc, btn, name, data) {
			var popup = doc.createXULElement("menupopup");
			var prop = name + "Popup";
			btn.popups.push(btn[prop] = popup);
			popup.id = this.id + "-" + prop;
			for (var type of ["popupshowing", "click"])
				popup.setAttribute("on" + type, `parentNode.linkedObject.${type}(event)`);
			for(var obj of data) popup.append(this.createElement(doc, obj));
			btn.append(popup);
		},
		map: {b: "Bool", n: "Int", s: "String"},
		createElement(doc, obj) { // pref
			if (!obj) return doc.createXULElement("menuseparator");
			var pref = doc.ownerGlobal.Object.create(null), node, img, bool;
			for(var [key, val] of Object.entries(obj)) {
				if (key == "pref") {
					var [apref, lab, akey, hint, undef, code] = val; // строка меню
					pref.pref = apref; pref.lab = lab || apref;
					if (hint) {
						if (RegExp(/\p{L}/,'u').test(hint[0]) && (hint[0] === hint[0].toUpperCase()))
							hint = '\n'+ hint;
						pref.hint = hint;
					}
					if (undef) pref.undef = undef; //если не массив: undef || undef == ""
					if (code) pref.code = code;
				}
				else if (key == "image") img = val, pref.img = true;
				else if (key != "values") pref[key] = val;
				else pref.hasVals = true;
			}
			var type = prefs.getPrefType(pref.pref);
			var str = this.map[type == prefs.PREF_INVALID
				? obj.values ? (typeof obj.values[0][0])[0] : "b"
				: type == prefs.PREF_BOOL ? "b" : type == prefs.PREF_INT ? "n" : "s"
			];
			pref.get = prefs[`get${str}Pref`];
			var map, set = prefs[`set${str}Pref`];
			if (pref.hasVals) {
				for(var [val, , , , code] of obj.values)
					code && (map || (map = new Map())).set(val, code);
				if (map) pref.set = (key, val) => {
					set(key, val);
					map.has(val) && eval(map.get(val)); // выполнить код
				}
			}
			if (!map) pref.set = set;
			node = doc.createXULElement("menu");
			node.className = "menu-iconic";
			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.createXULElement("menupopup"))
			);
			if ("pDefGreen" in obj) pref.noAlt = !("pYellow" in obj);
			return node;
		},
		regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
		upd(node) {
			var {pref} = node, def = false, user = false, val; // если опция не найдена

			if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
				try {
					val = pref.defVal = db[pref.get.name](pref.pref); def = true; // опция по-умолчанию получена
				} catch {def = false}
				user = prefs.prefHasUserValue(pref.pref);
				if (user) try {val = pref.get(pref.pref, undefined);} catch {}
			}
			if (val == pref.val && def == pref.def && user == pref.user) return;
			pref.val = val; pref.def = def; pref.user = user;
			var exists = def || user;
			if (!exists && pref.undef) // опция не найдена ? вернуть default-значение
				val = pref.undef[0];
			var hint = eval(hints.get(pref.pref));
			if (!hint) hint = val != undefined ? val : "Эта опция не указана";
			if (hint === "") hint = "[ пустая строка ]";
			hint += "\n" + pref.pref;
			if (pref.hint) hint += "\n" + pref.hint;
			node.tooltipText = hint; //+ текст
			var img, alt = "pYellow" in pref && val == pref.pYellow, pro = "pGray" in pref && val == pref.pGray;
			if (alt) img = this.Yellow;
			if (pro) img = this.Gray;
			if ("pDefGreen" in pref)
				if (val == pref.pDefGreen)
					node.style.removeProperty("color"), img = this.defGreen;
				else {
					node.style.setProperty("color", "#702020", "important");
					if (!alt && !pro) img = this.Red;
				}
			node.setAttribute("image", img || this.Gray); // серый значок, если нет pDefGreen
			user
				? node.style.setProperty("font-style", "italic", "important")
				: node.style.removeProperty("font-style");

			var {lab} = pref;
			if (exists && pref.hasVals) {
				if (val in pref.vals) var sfx = pref.vals[val] || val;
				else var sfx = user ? "другое" : "стандарт";
				lab += ` ${"restart" in pref ? "↯-" : "refresh" in pref ? "-⟳" : "—"} ${sfx}`;
			}
			lab = exists ? lab : '['+ lab + `${"restart" in pref ? " ↯" : "refresh" in pref ? " ⟳" : ""}` +']'+ `${pref.undef ? " - "+ pref.undef[1] : ""}`;
			node.setAttribute("label", lab); // имя = [имя] если преф не существует
		},
		createRadios(doc, vals, popup) {
			for(var arr of vals) {
				var [val, lab, key, hint] = arr;
				var menuitem = doc.createXULElement("menuitem");
				with (menuitem)
					setAttribute("type","radio"), setAttribute("closemenu","none"), setAttribute("label", popup.parentNode.pref.vals[val] = lab), key && setAttribute("accesskey", key);
				var tip = menuitem.val = val === "" ? "[ пустая строка ]" : val;
				if (hint) tip += "\n" + hint;
				menuitem.tooltipText = `${tip != undefined ? tip + "\n\n" : ""}клик с Shift блокирует авто-закрытие`;
				popup.append(menuitem);
			}
		},
		openPopup(popup) {
			var btn = popup.parentNode;
			if (btn.domParent != btn.parentNode) {
				btn.domParent = btn.parentNode;
				if (btn.matches(".widget-overflow-list > :scope"))
					var pos = "after_start";
				else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
					btn.closest("toolbar").getBoundingClientRect(), pos = width > height
						? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
						: `${win.innerWidth - right > left ? "end" : "start"}_before`;
				for(var p of btn.popups) p.setAttribute("position", pos);
			}
			popup.openPopup(btn);
		},
		maybeRestart(node, conf) {
			if (conf && !Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) return;
			var cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
			Services.obs.notifyObservers(cancel, "quit-application-requested", "restart");
			return cancel.data ? Services.prompt.alert(null, this.label, "Запрос на выход отменён.") : this.restart();
		},
		async restart() {
			var meth = Services.appinfo.inSafeMode ? "restartInSafeMode" : "quit";
			Services.startup[meth](Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
		},
		maybeRe(node, fe) {
			var {pref} = node;
			if ("restart" in pref) {
				if (this.maybeRestart(node, pref.restart)) return;
			}
			else this.popupshowing(fe, node.parentNode);
			if ("refresh" in pref) {
				var win = node.ownerGlobal;
				if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
					? win.BrowserReloadSkipCache() : win.BrowserReload();}
		},
		maybeClosePopup(e, trg) {
			(e.shiftKey || e.button == 1) || trg.parentNode.hidePopup();
		},
		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 (!pref.val && pref.val != "" && pref.undef)
					pref.val = pref.undef[0]; // опции нет ? вернуть по-умолчанию
				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;}
			}
		},
		click(e, trg = e.target) { //строки меню
			if (e.button) return;
			var {pref} = trg;
			if (!pref) return;
		},
		command(e, trg = e.target) { // LMB на кнопке
			var newVal = trg.val, menu = trg.closest("menu");
			if (!menu) return;
			this.maybeClosePopup(e, menu);
			if (newVal != menu.pref.val)
				menu.pref.set(menu.pref.pref, newVal), this.maybeRe(menu, true);
			menu.pref.code && eval(menu.pref.code); //run
		},
		contextmenu(e, trg = e.target) {
			if ("pref" in trg) {
				this.maybeClosePopup(e, trg);
				if (trg.pref.user)
					prefs.clearUserPref(trg.pref.pref), this.maybeRe(trg);
				var code = trg.pref.code; code && eval(code); //run
			}
			e.preventDefault();
		}
	};
});

В скрипт ucf_hookClicks.js добавил меню команд пользователя, которое открывается по клику колёсиком на кнопке QuickToggle about:config или на любой другой кнопке, например:
trg = document.getElementById("unified-extensions-button");
document.getElementById("QuickToggle").menupopup.openPopup(trg, "before_start");


Теперь в одном скрипте есть меню пользователя, перехват нажатий клавиш, событий мыши, подсказки кнопок, меню настроек.
Все опции в начале скрипта можно править «под себя», например добавить команды аналогично кнопке Save+
Нужно убрать из CustomStylesScripts.jsm скрипт ucf_QuickToggle.js, так как эти коды сведены в один ucf_hookClicks_new.js

Dumby посмотрите пожалуйста код кнопки в ней тусклая иконка если svg.context-properties.content.enabled стоит в false

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

Выделить код

Код:

//Очистить историю
try {
    ((img, preventClearThumbs) => {
        CustomizableUI.createWidget({
            id: "bt-clear-history",
            label: "Очистить историю",
            tooltiptext: "Очистить историю",
            defaultArea: CustomizableUI.AREA_NAVBAR,
            onCreated: function(bt) {
                bt.image = img;
            },
            onCommand: function(event) {
                var win = event.target.ownerDocument.defaultView;
                var itemsToClear = [
                    "history",
					"sessions",
                    "formdata",					
                    "cache",
		            "downloads",
                    "offlineApps",
                    "pluginData"			
                ];
                var range = win.Sanitizer.getClearRange(0);
                win.Sanitizer.sanitize(itemsToClear, {
                    ignoreTimespan: !range,
                    range,
                }).then(() => {
                    var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
                    alertsService.showAlertNotification(img, "История Очищена!", "", false);
                    win.setTimeout(()=> alertsService.closeAlert(), 2000);
                });
            }
        });
    })("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M5.5.7c1.1 0 1.1 1.6 0 1.6S4.4.7 5.5.7zm8.3.5L9.5 5.5s-1.24.1-2.68.12C5.18 7.15 4.12 7.66.822 8.01v1.2C3.35 13.3 4.97 15.2 9.21 15.2h1.09c1.4-1.7 2-2.9 2.5-4.9V9.21c-.5-1.19-1.6-2.41-1.6-2.41l4.1-4.1zm-10.3 2c1.7 0 1.7 2.6 0 2.6s-1.7-2.6 0-2.6z'/></svg>", null);
} catch(e) {}

[firefox] 120.0

Помню был скрипт для смены User-Agent для определенного сайта. Подскажите рабочий для 115 код или ссылку на код.

_zt пишет

Помню был скрипт для смены User-Agent для определенного сайта. Подскажите рабочий для 115 код или ссылку на код.

https://forum.mozilla-russia.org/viewto … 00#p797300

egorsemenov06
А как он у вас подключен?

А как он у вас подключен?

Лично у меня он расположен в config.js.


P.S. Там чуть ниже ещё маленькая правочка от Dumby есть.

_zt пишет

А как он у вас подключен?

У меня подключён в custom_script_win после строки -> // Здесь может быть ваш код который сработает по событию "DOMContentLoaded"

_zt пишет

egorsemenov06
А как он у вас подключен?

в config.js.

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

Подскажите как в 120 [firefox] убрать иконки в Панели меню? У меня установлен последний Lepton и раньше я делал всё как в этом сообщении, но сейчас этот способ не проходит... :rolleyes:

Финальный вариант скрипта перехвата кликов-нажатий ucf_hookClicks.js — оптимизировал код и быстрые настройки.
Напоминаю, что в скрипте небольшие демо-шаблоны команд, менюшек, сочетаний клавиш — эти данные для удобства расположены в начале и подробно прокомментированы. Добавьте свои команды сами, это делается проще, чем в других скриптах (например можно сделать аналог кнопки Save+).


ucf_QuickToggle.js удалён и код перенесён в ucf_hookClicks.js (всё равно без него не работало), айдишник кнопки меню быстрых настроек изменён, менюшка команд вызывается кликом колёсика (все сочетания кликов мыши и горячие клавиши можно изменить).


Cкрипт сохранения страниц SingleHTML.jsm немного улучшен.


Black_Monk - вы ошиблись темой, в стилях надо спрашивать…

Dobrov
В 120, попап перестал работать, меню не появляется и на ПКМ тоже никакой реакции. upd:спасибо.

b0ttle - исправил.

all
В общем, в config.js заработал. Всем спасибо.

Нельзя тут как-то добавить PanelUI-menu-button, чтобы он тоже мог переноситься? Пробовал, что-то не выходит. И что за кнопка alltabs-button, все время забываю?

config.js

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?pid=806232#p806232 806233#p806233
// установить для кнопок #alltabs-button и #unified-extensions-button атрибут "removable" как "true"
(async topic => {
	var obs = doc => {
		doc.getElementById("alltabs-button")?.setAttribute("removable", true);
		doc.getElementById("unified-extensions-button")?.setAttribute("removable", true);
		doc.getElementById("PanelUI-menu-button")?.setAttribute("removable", true);
	}
	Services.obs.addObserver(obs, topic);
	Services.obs.addObserver(function quit(s, t) {
		Services.obs.removeObserver(quit, t);
		Services.obs.removeObserver(obs, topic);
	}, "quit-application-granted");
})("chrome-document-interactive");


upd: fuchsfan, a точно;) Спасибо.

b0ttle пишет

И что за кнопка alltabs-button, все время забываю?

Видимо, это кропка справа сверху в виде стрелки вниз ˅, при клике на ней покажет список открытых табов.

Скрипт перехвата кликов-нажатий ucf_hookClicks.js — небольшие добработки и устранение ошибок:


1) Вернул сочетания кнопок мыши на вкладках и кнопке «Новая вкладка» — описание во всплывающих подсказках.
2) разблокировка перемещения #unified-extensions-button
3) добавлена команда alt в Меню пользователя. На строке «Закладка первая/последняя» клик правой кнопкой откроет последнюю ссылку.
В блоке «Menu = [{…» cmd имеет приоритет и сработает на клик колёсика или левой кнопки.
для alt возможны любые клики + управляющие клавиши (cmd при этом лучше убрать), пример есть в Mouse = {…B[6]…256 … btn.alt(btn)
4) Меню пользователя открывается колёсиком по кнопке «Быстрые опции» и правым кликом мыши на кнопке «Расширения» unified-extensions-button


В ucf_hookClicks треть объёма занимают блоки данных, которые можно менять «под себя»
Keys: нажатия клавиш, Menu: команды пользователя, Mouse: клики мыши, Setup быстрые опции.

Dobrov. Интересно сделано "настройки UCF", в плане тултипа.
upd: Dumby, круто. Спасибо.

Прошу прощения, я тут отсутствовал какое-то время.


egorsemenov06 пишет

Dumby посмотрите пожалуйста код кнопки в ней тусклая иконка если svg.context-properties.content.enabled стоит в false

Конкретный цвет можно прямо в SVG'шке указать, там, где stroke


А если надо чтобы context-properties подхватывались,
то можно сменить протокол

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

Выделить код

Код:

/*
                bt.image = img;
*/
                var subst = this.id + "-svg-image";
                Services.io.getProtocolHandler("resource")
                    .QueryInterface(Ci.nsIResProtocolHandler)
                    .setSubstitution(subst, Services.io.newURI(img));
                var url = `resource://${subst}/`;
                (this.onCreated = btn => btn.image = url)(bt);

b0ttle пишет

Нельзя тут как-то добавить PanelUI-menu-button, чтобы он тоже мог переноситься?

Эта кнопка не сама по себе, а в составе родительского <toolbaritem>'а,
наверно лучше их не разлучать.
А сам toolbaritem находится за пределами кастомизационных зон,
поэтому только просто устанавливать ему атрибут "removable" бесполезно.


Можно попробовать регистрировать его как "custom" виджет,
а вместо создания подсовывать существующий.
Типа в custom_script.js

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

Выделить код

Код:

(async id => CustomizableUI.createWidget({
	id,
	type: "custom",
	localized: false,
	defaultArea: CustomizableUI.AREA_NAVBAR,
	onBuild(doc) {
		var item = doc.getElementById(id);
		item.setAttribute("removable", true);
		return item;
	}
}))("PanelUI-button");

Dumby пишет

Прошу прощения, я тут отсутствовал какое-то время.


egorsemenov06 пишет

Dumby посмотрите пожалуйста код кнопки в ней тусклая иконка если svg.context-properties.content.enabled стоит в false

Конкретный цвет можно прямо в SVG'шке указать, там, где stroke


А если надо чтобы context-properties подхватывались,
то можно сменить протокол

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

Выделить код

Код:

/*
                bt.image = img;
*/
                var subst = this.id + "-svg-image";
                Services.io.getProtocolHandler("resource")
                    .QueryInterface(Ci.nsIResProtocolHandler)
                    .setSubstitution(subst, Services.io.newURI(img));
                var url = `resource://${subst}/`;
                (this.onCreated = btn => btn.image = url)(bt);

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

Скрипт ucf_hookClicks.js — изменил меню пользователя, чтобы проще подключать команды, устранил пару ошибок.
Справка по жестам мыши создаётся автоматически в ucf_mousedrag.js, и добавленные вами жесты тоже видны:
Меню пользователя > Правый клик по строке "Краткая справка | Жесты мыши"


обновлены скрипты: ucf_hookClicks.js ucf_mousedrag.js SingleHTML.jsm ClickPicSave.jsm и файлы профиля: prefs.js custom_styles_all_user.css userChrome.css userChrome_macosx.css
Firefox-Menu-Expert.png
Dumby - с возвращением!

Dobrov
Доброго времени суток.
Возможно ли убрать лишнюю разграничительную линию (на скриншоте), которая появляется при работе ucf_contextsearch.js?

Extra-line-when-running-ucf-contextsearch-js.png

Dobrov
И еще один вопрос.
Подскажите, от чего зависит стиль начертания шрифта (обычный, наклонный) и его цвет в быстрых настройках ucf_hookClicks.js?
Например, я добавил свой пункт "Unblock addons.mozilla.org"

Выделить код

Код:

pref: ["privacy.resistFingerprinting.block_mozAddonManager","Unblock addons.mozilla.org",,"Enables scripts & addons to run on the addons.mozilla.org page"], Def3el: true, refresh: true,
keys: [[true, "Unblock"], [false, "Block (default)"]]

он один в один повторяет "Выполнять скрипты Java"

Выделить код

Код:

pref: ["javascript.enabled", "Выполнять скрипты Java",,"Поддержка интерактивных сайтов, рекламы\nтакже разрешает действия горячих клавиш"], Def3el: true, refresh: true,
keys: [[true, "Да"], [false, "Нет"]]

но у моего пункта, почему-то, стиль шрифта наклонный, в отличие пункта с обычным шрифтом у "Выполнять скрипты Java".

Differences-in-menu-items-fonts-in-ucf-hook-Clicks-js.png

iG0R

custom_styles_all_user.css

Выделить код

Код:

#frame-sep{display:none!important;} //#context-sep-selectall


Выберите что-то одно. Подсмотреть id можно через Attributes-Inspector, судя по тому, что у вас ucf_hookClicks.js. Там идет отдельным файлом Attributes-Inspector.js, а код кнопка уже встроена в ucf_hookClicks.js. Есть способ без ничего, Ctrl+Alt+Shift+I > pick. Тема "настройка внешнего вида". Насчет другого, не в курсе. Может потом посмотрю.

b0ttle
Спасибо.
Я это уже сделал через userChrome.css

Выделить код

Код:

#context-sep-selectall {
	display: none !important;
}

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

iG0R. Так не бывает) Как это, без лишних движений?
У меня обычный шрифт, может не так вписали?

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

Выделить код

Код:

Setup = [
{
	pref: ["privacy.resistFingerprinting.block_mozAddonManager","Unblock addons.mozilla.org",,"Enables scripts & addons to run on the addons.mozilla.org page"], Def3el: true, refresh: true,
	keys: [[true, "Unblock"], [false, "Block (default)"]]
},null,
{ // быстрые настройки. есть Def3el: несовпадения выделяются
	pref: ["dom.disable_open_during_load", "Всплывающие окна"], Def3el: true, Yellow: false,
	keys: [[true, "Блокировать"], [false, "Разрешить"]],

iG0R пишет

Подскажите, от чего зависит стиль начертания шрифта (обычный, наклонный) и его цвет в быстрых настройках ucf_hookClicks.js?

Иконка зелёная или серая и шрифт обычный - значит опция в значении по-умолчанию, для вашей настройки это false.
Цвет других иконок задаётся для каждой строки подменю, например Blue ставлю, если опция изменена, но не критично. Пример всех цветов в строке: "Режим прокси"
если в pref:…… прописано значение для "Def3el", то текст будет красным, если опция не равна Def3el и не по-умолчанию. В новой версии скрипта вместо красного шрифта будет красный ореол текста.


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

Dumby, здравствуйте.
Может есть у Вас мысли, которые можно записать в виде скрипта. Но с начала немного о прошлом, есть три расширения в которых есть такой функционал это DownThemAll, Download Manager (S3) и совсем старинное Preserve Download Modification Timestamp. Все они умеют сохранять дату создания (брать дату с сервера) у скачиваемых файлов и присваивать конечному (скаченному). Даже существует по этому поводу Bug 733954, ему уже 12 лет. Так вот есть ли такая возможность реализовать для UCF?
-
Real UA: Mozilla/5.0 (Windows NT 10.0; Win64; x64;  rv:109.0) Gecko/20100101 Firefox/115.0

Farby пишет

умеют сохранять дату создания (брать дату с сервера) у скачиваемых файлов и присваивать конечному (скаченному)

В смысле присваивать как дату изменения?
Вроде как лиса не умееет присваивать файлам дату (именно) создания.


Я посмотрел как это сделано в S3, и, если правильно понял,
то там что-то типа такого (весьма упрощённый набросок),
но тестировать это мне особо негде.


В custom_script.js

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

Выделить код

Код:

(async (url, ...cc) => {
	var re = /^.*\//;
	var f = Components.Constructor(...cc);
	var setLastModified = (file, date) => {
		if (file.exists()) file.lastModifiedTime = date;
	}
	var {Downloads} = ChromeUtils.importESModule(url);
	(await Downloads.getList(Downloads.ALL)).addView({
		onDownloadChanged(download) {
			if (download.succeeded) try {
				var date = new Date(download.saver.entityID.replace(re, ""));
				+date && setTimeout(setLastModified, 2e3, f(download.target.path), date);
			}
			//catch(ex) {console.error(ex);}
			catch {}
		}
	});
})(
	"resource://gre/modules/Downloads.sys.mjs",
	"@mozilla.org/file/local;1", "nsIFile", "initWithPath"
);

Dumby
Большое спасибо, то что нужно :beer:. Теперь можно выключить S3 :D

Сделал отдельный скрипт на основе кнопки от Dumby «Быстрые опции about:config»: (меньше оригинала + некоторые улучшения)


Dumby, раз ты на связи, вот мои Хотелки и Проблемы к скрипту Быстрые опции:
1) сделать ввод значения текущей опции вручную через диалог ввода. Например в UserAgent > при клике по строке: "ваши данные…" нужно открыть диалог, где вводим нужные данные аналогично уже вшитым в другие строки этого подменю. После ввода или перезапуска браузера при открытии этого подменю строка "ваши данные…" должна быть выбрана флажком. Такой ввод данных пользователя нужен не для одной опции, а для многих: например в строке «Загрузки» нужна строка своих настроек…


2) надо, чтобы radio-строка подменю была выбрана (с флажком), если опция сброшена и отсутствует.
сделал костыль: pref.undef = [val,str] для pref: pref,lab,key,hint,[val,str],code
костыль делает строку меню такой: "Заголовок - str", но radio-строка подменю остаётся не выбранной.


3) при клике по родительской строке меню открывать эту опцию в about:config – функция about_config прилагается.
Это работает в ucf_hookClicks.js, но не пашет в этом коде, т.к. e.target определяется как кнопка, а не как текущая строка меню.


4) оригинальная кнопка "Быстрое переключение опций about:config" содержала два контекстных меню.
Прошу убрать код для нескольких меню: popups, popupshowing… (может попроще код станет).
Второе UserMenu другого формата и должно быть независимым, чтобы вызываться над разными кнопками.
0) странноcти кода – добавлял функцию switchTab через запятую в разной последовательности, но не
работает: ua = …}, switchTab = … Без запятой пашет: строка UserMenu не выдаст ошибку: no switchTab…

«Быстрые опции about:config» (не совместим с Custom Buttons)

Выделить код

Код:

/* Быстрые настройки меню опций about:config. Колёсико, Лев+Shift не закрывать
	Опция изменена: курсив; Цвет = ключ, по-умолчанию серый, иначе Red
	есть Def3el: несовпадения выделяются красной обводкой текста
	refresh=true ⟳ перечитать без кэша, restart=false ↯ без запроса 
pref: pref,lab,key,hint,[val,str],code | keys:val,lab,dat,+hint,code | icon:значок

	предыдущее значение: menu.pref.val
	Новое: newVal и trg.val данные
	trg.label метка keys:
*/

(async id =>{ var {prefs} = Services, db = prefs.getDefaultBranch("");
Pref = (key, set)=>{ //или key = [key,default]
	if (!Array.isArray(key)) key = [key];
	var t = prefs.getPrefType(key[0]), m = {b:"Bool",n:"Int",s:"String"};
	t = m[t == 128 ? "b" : t == 64 ? "n" : t == 32 ? "s" : ""];
	if (set == "get")
		return t; //тип опции
	if (!t)
		t = m[set != undefined ? (typeof set)[0] : (typeof key[1])[0]];
	if (t)
		if (set != undefined)
			prefs[`set${t}Pref`](key[0],set)
		else
			set = prefs[`get${t}Pref`](...key);
	return set;
}
Icon = (c = '0c0')=>"data:image/svg+xml;charset=utf-8,<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'><defs><linearGradient id='a' x1='16' x2='16' y1='32' gradientUnits='userSpaceOnUse'><stop stop-color='%23"+ c +"'/><stop stop-color='%23fff' offset='.8'/></linearGradient><linearGradient id='b' x2='32' y1='16' gradientTransform='matrix(1 0 0 1 2 2)'><stop stop-opacity='.5'/></linearGradient></defs><circle cx='16' cy='16' r='15' fill='url(%23a)' stroke='url(%23b)' stroke-width='2'/></svg>";
about_config = (filter) => { //на опцию
	if (gURLBar.value.startsWith("about:config")) switchTab(gURLBar.value);
	var setFilter = (e,input = (e?.target || window.content.document).getElementById("about-config-search")) => {	try {
		if (e || input.value != filter) input.setUserInput(filter);} catch{}
	},
	found = window.switchToTabHavingURI("about:config",true, {relatedToCurrent: true,
		triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
	if (found) setFilter(null,window);
	else gBrowser.selectedBrowser.addEventListener("pageshow",setFilter, {once: true});
};
ua = (real = false, ua_my = "general.useragent.override")=>{ //текущий или вшитый ЮзерАгент
	ttt = Pref(ua_my); prefs.clearUserPref(ua_my);
	ua = Cc["@mozilla.org/network/protocol;1?name=http"].getService(Ci.nsIHttpProtocolHandler).userAgent; //костыль
	ttt && Pref(ua_my, ttt);
	ttt ||= ua;
	if (real) ttt = ua;
	return ttt;
}
switchTab = (url = 'about:config', go)=>{ //открыть вкладку | закрыть её | выбрать
	for(var tab of gBrowser.visibleTabs)
		if (tab.linkedBrowser.currentURI.spec == url)
			{go ? gBrowser.selectedTab = tab : gBrowser.removeTab(tab); return;}
	gBrowser.addTrustedTab(url);
	gBrowser.selectedTab = gBrowser.visibleTabs[gBrowser.visibleTabs.length -1];
}
var uar = ua(true), //real ЮзерАгент
fonts = arr => arr.map(n => [(n == arr[arr.length-1] ? null : n), n]), //array с вложениями
serif = fonts("Arial|Roboto|Cantarell|Segoe UI|Cambria|Calibri".split('|')),
sans = [["Times","Times"], ...serif],
hints = new Map([ //опция отсутствует ? вернуть строку
	["general.useragent.override", "UserAgent"]]);

UserMenu = { //массив команд пользователя, alt() клик правой кнопкой
	"Настройки профайлера": {
		cmd(){switchTab('about:profiling')}
	},
	DwDir: {lab: `папка Загрузки`,
		cmd(){
			Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(),Cu.reportError)},
		img: "chrome://devtools/skin/images/folder.svg"
	},
}
AboutCfg = [{
	pref: ["dom.disable_open_during_load", "Всплывающие окна"], Def3el: true, Yellow: false,
	keys: [[true, "Блокировать"], [false, "Разрешить"]],
},null,{
	pref: ["extensions.user_chrome_files.savedirs", "Загрузки",,'Пути сохранения Страниц и Графики\nСинтаксис «_Html/subdir|_Pics/subdir»\nsubdir: пусто | 0 заголовок | 1 домен',
		["", "всё в общей папке"]],
	Blue: "_Web|1|_Images|0", Gray: "",
	keys: [
		["_Сайты||_Фото|0", "_Сайты|_Фото/имя…"],
		["_Web|1|_Images|0", "_Web/сайт|_Images/имя"],
		["Сайт||Фото|", "ваши данные…",,"ключ в about:config", `console.log("введите ваши данные…")`]]
},{
	pref: ["general.useragent.override", "User Agent",,"Изменяет вид сайта", [uar,"встроенный"]],
		refresh: true, Def3el: uar,
	keys: [
	["Windows", "ваши данные…",,,`console.log("введите ваши данные…")`],
	["Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36", "Chrome 118 Win10"],
	["Opera/9.80 (Windows NT 6.2; Win64; x64) Presto/2.12 Version/12.16", "Opera12 W8"],
	["Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", "GoogleBot"],
	[uar, "по-умолчанию"]] //родной ЮзерАгент
}];

CustomizableUI.createWidget({ defaultArea: CustomizableUI.AREA_NAVBAR,
	label: "Журнал, Меню опций", localized: false, id: id,
	onCreated(btn) {
		btn.setAttribute("image", "chrome://devtools/skin/images/settings.svg");
		var doc = btn.ownerDocument, m = nn => doc.createXULElement(nn);
		btn.domParent = null;
		btn.popups = new btn.ownerGlobal.Array();
		this.createPopup(doc, btn, "config", AboutCfg);
		btn.linkedObject = this;
		for(var type of ["contextmenu", "command"])
			btn.setAttribute("on"+ type, `linkedObject.${type}(event)`);
		var popup = m("menupopup"), menu = m("menuitem");
		menu.m = m;
		menu.fill = this.fill;
		menu.render = this.render;
		popup.append(menu);
		btn.prepend(popup);
	},
	render(){
		var popup = this.parentNode;
		this.remove();
		this.fill(UserMenu, popup);
	},
	fill(o, popup){if (typeof o == "object") for (key in o){
		var {lab, sep, sub, cmd, alt, inf, img} = o[key];
		var name = sub ? "menu" : "menuitem";
		sep && popup.append(this.m("menuseparator"));
		var item = this.m(name);
		item.setAttribute("label", lab || key);
		if (img)
			item.className = name +"-iconic", item.setAttribute("image",img);
		if (inf) item.tooltipText = inf;
		item.alt = alt; //cmd2
		sub || cmd && item.setAttribute("oncommand", cmd.toString().replace(/cmd\(.*?\)/,''));
		/^(sub|sep|inf|lab|img)$/.test(key) || popup.append(item);
		sub && this.fill(o[key], item.appendChild(this.m("menupopup")));
	}},
	createPopup(doc, btn, prop, data) {
		var popup = doc.createXULElement("menupopup");
		btn.popups.push(btn[prop] = popup);
		popup.id = this.id +"-"+ prop;
		for (var type of ["popupshowing"])
			popup.setAttribute("on"+ type, `parentNode.linkedObject.${type}(event)`);
		for(var obj of data) popup.append(this.createElement(doc, obj));
		btn.append(popup);
	},
	createElement(doc, obj) { //pref
		if (!obj) return doc.createXULElement("menuseparator");
		var pref = doc.ownerGlobal.Object.create(null), node, bool, img;
		for(var [key, val] of Object.entries(obj)) {
			if (key == "pref") {
				var [apref, lab, akey, hint, undef, code] = val; //строка меню
				pref.pref = apref; pref.lab = lab || apref;
				if (hint) {
					if (RegExp(/\p{L}/,'u').test(hint[0]) && (hint[0] === hint[0].toUpperCase()))
						hint = '\n'+ hint;
					pref.hint = hint;
				}
				if (undef) pref.undef = undef; //если не массив: undef || undef == ""
				if (code) pref.code = code;
			}
			else if (key == "icon") img = val, pref.img = true;
			else if (key != "keys") pref[key] = val;
			else pref.hasVals = true;
		}
		var t = prefs.getPrefType(pref.pref), m = {b: "Bool", n: "Int", s: "String"};
		var str = m[t == prefs.PREF_INVALID ? obj.keys ? (typeof obj.keys[0][0])[0] : "b" : t == prefs.PREF_BOOL ? "b" : t == prefs.PREF_INT ? "n" : "s"]; //String по-умолчанию
		pref.get = prefs[`get${str}Pref`];
		var map, set = prefs[`set${str}Pref`];
		if (pref.hasVals) {
			for(var [val,,,,code] of obj.keys)
				code && (map || (map = new Map())).set(val, code);
			if (map) pref.set = (key, val) => {
				set(key, val);
				map.has(val) && eval(map.get(val)); //код2 если pref изменён
			}
		}
		if (!map) pref.set = set;
		node = doc.createXULElement("menu");
		node.className = "menu-iconic";
		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.keys,
			node.appendChild(doc.createXULElement("menupopup"))
		);
		if ("Def3el" in obj) pref.noAlt = !("Yellow" in obj);
		return node;
	},
	createRadios(doc, vals, popup) {
		for(var arr of vals) {
			var [val, lab, key, hint] = arr;
			var menuitem = doc.createXULElement("menuitem");
			with (menuitem)
				setAttribute("type","radio"), setAttribute("closemenu","none"), setAttribute("label", popup.parentNode.pref.vals[val] = lab), key && setAttribute("accesskey", key);
			var tip = menuitem.val = val === "" ? "[ пустая строка ]" : val;
			if (hint) tip += "\n" + hint;
			menuitem.tooltipText = `${tip != undefined ? tip + "\n\n" : ""}клик с Shift блокирует авто-закрытие`;
			popup.append(menuitem);
		}
	},
	regexpRefresh: /^(?:view-source:)?(?:https?|ftp)/,
	upd(node) {
		var {pref} = node, def = false, user = false, val; //если опция не найдена
		if (prefs.getPrefType(pref.pref) != prefs.PREF_INVALID) {
			try {
				val = pref.defVal = db[pref.get.name](pref.pref); def = true; //опция по-умолчанию получена
			} catch {def = false}
			user = prefs.prefHasUserValue(pref.pref);
			if (user) try {val = pref.get(pref.pref, undefined);} catch {}
		}
		if (val == pref.val && def == pref.def && user == pref.user) return;
		pref.val = val; pref.def = def; pref.user = user;
		var exists = def || user;
		if (!exists && pref.undef) //опции нет ? вернуть default
			val = pref.undef[0];
		var hint = hints.get(pref.pref);
		hint ||= val != undefined ? val : "Эта опция не указана";
		if (hint === "") hint = "[ пустая строка ]";
		hint += "\n" + pref.pref;
		if (pref.hint) hint += "\n"+ pref.hint;
		node.tooltipText = hint; //+ текст
		var img = Icon("999"), alt = "Yellow" in pref && val == pref.Yellow, clr = "Gray" in pref && val == pref.Gray, blu = "Blue" in pref && val == pref.Blue;
		if (blu) img = Icon("a0f");
		if (alt) img = Icon("f80");
		if ("Def3el" in pref)
			if (val == pref.Def3el)
				img = Icon(), node.style.removeProperty('filter');
			else if (val != pref.defVal) {
				if (!alt && !clr && !blu)
					img = Icon("f26"); // Red
				node.style.filter = "drop-shadow(1px 1px 1px #B8F)";
			}
		pref.img || node.setAttribute("image", img); //нет Def3el ? серый
		user
			? node.style.setProperty("font-style", "italic", "important")
			: node.style.removeProperty("font-style");
		var {lab} = pref;
		if (exists && pref.hasVals) {
			if (val in pref.vals) var sfx = pref.vals[val] || val;
			else var sfx = user ? "другое" : "по-умолчанию";
			lab += ` ${"restart" in pref ? "↯-" : "refresh" in pref ? "-⟳" : "—"} ${sfx}`;
		}
		lab = exists ? lab : '['+ lab + `${"restart" in pref ? " ↯" : "refresh" in pref ? " ⟳" : ""}` +']'+ `${pref.undef ? " - "+ pref.undef[1] : ""}`;
		node.setAttribute("label", lab); //имя = [имя] если преф не существует
	},
	openPopup(popup) {
		var btn = popup.parentNode;
		if (btn.domParent != btn.parentNode) {
			btn.domParent = btn.parentNode;
			if (btn.matches(".widget-overflow-list > :scope"))
				var pos = "after_start";
			else var win = btn.ownerGlobal, {width, height, top, bottom, left, right} =
				btn.closest("toolbar").getBoundingClientRect(), pos = width > height
					? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
					: `${win.innerWidth - right > left ? "end" : "start"}_before`;
			for(var p of btn.popups) p.setAttribute("position", pos);
		}
		popup.openPopup(btn);
	},
	maybeRestart(node, conf) {
		if (conf && !Services.prompt.confirm(null, this.label, "Перезапустить браузер?")) return;
		var cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
		Services.obs.notifyObservers(cancel, "quit-application-requested", "restart");
		return cancel.data ? Services.prompt.alert(null, this.label, "Запрос на выход отменён.") : this.restart();
	},
	async restart() {
		var meth = Services.appinfo.inSafeMode ? "restartInSafeMode" : "quit";
		Services.startup[meth](Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
	},
	maybeRe(node, fe) {
		var {pref} = node;
		if ("restart" in pref) {
			if (this.maybeRestart(node, pref.restart)) return;
		}
		else this.popupshowing(fe, node.parentNode);
		if ("refresh" in pref) {
			var win = node.ownerGlobal;
			if (this.regexpRefresh.test(win.gBrowser.currentURI.spec)) pref.refresh
				? win.BrowserReloadSkipCache() : win.BrowserReload();}
	},
	maybeClosePopup(e, trg) {
		(e.shiftKey || e.button == 1) || trg.parentNode.hidePopup();
	},
	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 (!pref.val && pref.val != "" && pref.undef)
					pref.val = pref.undef[0]; //опции нет ? вернуть default
				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;
				}
			}
	},
	command(e, trg = e.target) { //LMB
		if (trg.id == id) {
			if (trg.config.state != "open")
				trg.menupopup.openPopup(trg, "after_start")
			else
				console.log("Здесь нужен переход на опцию about:config");
			return;
		}
		var menu = trg.closest("menu"), newVal = trg.val;
		if (!menu || !menu.pref) return;
		this.maybeClosePopup(e, menu);
		menu.pref.code && eval(menu.pref.code); //run1
		if (newVal != menu.pref.val)
			menu.pref.set(menu.pref.pref, newVal), this.maybeRe(menu, true);
	},
	contextmenu(e, trg = e.target) { //RMB
		if ((trg.id == id) && (!e.ctrlKey && !e.altKey && !e.shiftKey))
			if (trg.config.state != "open")
				this.openPopup(trg.config);
			else
			  trg.config.hidePopup();
		else if ("pref" in trg) {
			this.maybeClosePopup(e, trg);
			if (trg.pref.user)
				prefs.clearUserPref(trg.pref.pref), this.maybeRe(trg);
			trg.pref.code && eval(trg.pref.code); //run
		}
		e.preventDefault();
	}
});
})("ucf_AboutConfig");

Dobrov пишет

1) сделать ввод значения текущей опции вручную через диалог ввода. Например в UserAgent > при клике по строке: "ваши данные…" нужно открыть диалог, где вводим нужные данные аналогично уже вшитым в другие строки этого подменю. После ввода или перезапуска браузера при открытии этого подменю строка "ваши данные…" должна быть выбрана флажком.

Попробовал это записать, но как-бы слегка снаружи.
Задержка, и, если ничего не выбрано и преф имеет пользовательское значение,
тогда выбираем строку "ваши данные…".


Врезка в createRadios() перед строкой popup.append(menuitem);
добавляем lab == "ваши данные…" && this.asInput(menuitem, val);


дополнительный стафф в объект

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

Выделить код

Код:

//
	asInput(menuitem, val) {
		menuitem.ttsfx = (menuitem.ittt = menuitem.tooltipText)
			.slice(String(menuitem.ival = val).length);
		menuitem.linkedObject = this;
		menuitem.setAttribute("oncommand", "linkedObject.input(event)");
		menuitem.render = this.renderInputOnce;
	},
	input(e) {
		e.stopImmediatePropagation();
		var trg = e.target, {pref} = trg.parentNode.parentNode;
		var res = {value: trg.tt ? pref.val : trg.ival};

		if (Services.prompt.prompt(
			null, `Введите значение (${pref.isInt ? "целое" : "строка"})`, pref.pref, res, null, {}
		)) {
			if (pref.isInt) {
				// from old about:config
				var val = res.value | 0;
				if (val != res.value - 0) return Services.prompt.alert(
					null, "Недействительное значение", 
					"Введённый вами текст не является числом."
				);
			}
			else val = res.value;
			pref.set(pref.pref, val);
		}
	},
	renderInputOnce() {
		delete this.render;
		this.render();
		var {pref} = this.parentNode.parentNode;
		pref.isInt = pref.get.name[3] == "I";
		if (!("val" in pref)) try {pref.val = pref.get(pref.pref);} catch {}

		this.linkedObject.hookUncheck(this); // for Shif+Click outside
		(this.render = this.linkedObject.renderInput).call(this);
	},
	hookUncheck(menuitem) {
		var rtt = function() {
			this.tt = false;
			this.tooltipText = this.ittt;
		}
		var desc = {configurable: true, enumerable: true, value(attr) {
			this.ownerGlobal.Element.prototype.removeAttribute.call(this, attr);
			attr.startsWith("c") && this.popup.state.startsWith("o") && this.parentNode.rtt();
		}};
		(this.hookUncheck = menuitem => {
			menuitem.rtt = rtt;
			var box = menuitem.firstChild;
			box.popup = menuitem.parentNode;
			menuitem.ownerGlobal.Object.defineProperty(box, "removeAttribute", desc);
		})(menuitem);
	},
	get renderInput() {
		delete this.renderInput;
		return this.renderInput = Cu.getGlobalForObject(Cu).eval(`(${async function() {
			await new Promise(this.ownerGlobal.requestAnimationFrame);

			var popup = this.parentNode, menu = popup.parentNode;
			menu.hasAttribute("_moz-menuactive") || this.linkedObject.popupshowing(null, popup, false); // force

			var checked = popup.querySelector("[checked]");
			var {pref} = menu, checkedThis = checked == this;

			if (checked && !checkedThis || !pref.user) return this.tt && this.rtt();

			this.tt = true;
			this.tooltipText = pref.val + this.ttsfx;
			if (checkedThis) return;

			menu.label = menu.label.replace(/другое$/, this.label);
			this.setAttribute("checked", true);
		}})`);
	},


и чуть модифицировать popupshowing()
чтобы была возможность пропустить проверку state
скрытый текст

Выделить код

Код:

/*
	popupshowing(e, trg = e.target) {
		if (trg.state == "closed") return;
*/
	popupshowing(e, trg = e.target, checkstate = true) {
		if (checkstate && trg.state == "closed") return;

2) надо, чтобы radio-строка подменю была выбрана (с флажком), если опция сброшена и отсутствует.
сделал костыль: pref.undef = [val,str] для pref: pref,lab,key,hint,[val,str],code
костыль делает строку меню такой: "Заголовок - str", но radio-строка подменю остаётся не выбранной.

Не понял. Какая radio-строка остаётся не выбранной?


Вот для extensions.user_chrome_files.savedirs есть pref.undef
и есть три radio-строки, но ни одна из них
не подходит быть выбранной если преф отсутствует.


И, даже если бы подходящая radio-строка была,
то что должно было бы происходить при её активации кликом?

3) при клике по родительской строке меню открывать эту опцию в about:config – функция about_config прилагается.
Это работает в ucf_hookClicks.js, но не пашет в этом коде, т.к. e.target определяется как кнопка, а не как текущая строка меню.

У себя вижу, что на кликнутое ссылается e.explicitOriginalTarget

4) оригинальная кнопка "Быстрое переключение опций about:config" содержала два контекстных меню.
Прошу убрать код для нескольких меню: popups, popupshowing… (может попроще код станет).

Да нет, особо попроще код не станет. В popupshowing здесь менять нечего,
а popups — да, не используется, можно почистить

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

Выделить код

Код:

....
		//btn.popups = new btn.ownerGlobal.Array();
....
		//btn.popups.push(btn[prop] = popup);
		btn[prop] = popup;
....
			//for(var p of btn.popups) p.setAttribute("position", pos);
			btn.config.setAttribute("position", pos);

0) странноcти кода – добавлял функцию switchTab через запятую в разной последовательности, но не
работает: ua = …}, switchTab = … Без запятой пашет: строка UserMenu не выдаст ошибку: no switchTab…

Я добавил запятую как написано, и у меня всё работает.
Запятая, в данном случае, это оператор множественного вычисления,
она что есть, что нет.


А вот если перед ua добавить var
то запятая станет частью var-синтаксиса
и switchTab перестанет быть глобальной переменной.
То есть, из атрибута "oncommand" его видно не будет.

Можно в ucf_aom-button.js как-то добавить функцию, как у unified_extensions_button, чтобы при клике вылазило окно, не настройки? Можно эти дополнения с меню, отдельно выделять используя тот шарик с цветом. Во, или на ПКМ посадить unified_extensions_button, а саму кнопку скрыть.

b0ttle пишет

Можно эти дополнения с меню, отдельно выделять используя тот шарик с цветом.

Не понял, нужен скриншот или подробные разьяснения.
Сделал, но пока не публиковал (ucf_hookClicks.js в процессе отладки):
Дополнения к меню опций от Dumby (работает, но переделываю по-своему, ещё изменю формат Setup = [{…)
Меню пользователя и прочие клики для unified_extensions_button работают и на старой кнопке Расширений "add-ons-button"


b0ttle пишет

Можно в ucf_aom-button.js как-то добавить функцию, как у unified_extensions_button, чтобы при клике вылазило окно, не настройки?

Вообще-то удобнее иконку скрипта ucf_aom-button.js убрать, а на правый клик unified_extensions_button назначить меню управления расширениями от ucf_aom-button (но не разбирался, как в этом скрипте привязать клики по меню, открытому из другой кнопки).

 

 

b0ttle пишет

попап таких расширений как ublock, tampermonkey, про это меню имелось ввиду, чтобы вместо двух кнопок была одна.

Нет, менюшки расширений не рассматриваются, и я не проверял их открытие над другой кнопкой, если кнопка расширения не закреплена на панели инструментов.
Правым кликом на unified-extensions-button открывается меню пользовательских команд (отлажу скрипт и для "add-ons-button" это меню добавлю)

Обновил скрипта перехвата кликов-нажатий ucf_hookClicks.js — исправил ошибки несрабатывания некоторых команд пользовательского меню по клику из кнопок.


Cкрипт сохранения страниц SingleHTML.jsm исправлена связка Путь сохранения <-> установка пути из Опций быстрых настроек.


Всех с наступающим праздником!

Dobrov
в новой версии ucf_hookClicks.js пропадает блок кнопок закрытия окна (#minimize-button, #restore-button, #close-button).

Northtech
Обычно скрываю эти кнопки, пользуюсь через PanelUI. Всех с новым годом.

b0ttle
Спасибо, помогло :)
я у себя их просто уменьшил, чтобы не такие огромные были.

#titlebar-buttonbox > .titlebar-button, .titlebar-buttonbox > :-moz-any(.titlebar-min,.titlebar-max,.titlebar-close,.titlebar-restore), #minimize-button, #restore-button, #close-button {
margin: 0 !important;
padding: 4px 8px !important;
}

Всех с праздником, наступившим и наступающим! :beer:

Northtech пишет

в новой версии ucf_hookClicks.js пропадает блок кнопок закрытия окна (#minimize-button, #restore-button, #close-button).

Попробуй включить "Кнопки управления окна" в диалоге "Настройки UserChromeFiles"

Dumby посмотрите пожалуйста эти две кнопки  первая не работает

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

Выделить код

Код:

//Восстановить фавиконки закладок
(async () => {
    var id = "ucf-loads-favicons",
    label = "Восстановить фавиконки",
    tooltiptext = "Восстановить фавиконки закладок",
	    img = (rph => {
        var subst = "ucf-loads-favicons-btn-img";
        rph.setSubstitution(subst, Services.io.newURI(
           "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M3.6.6v14.8L8 11l4.4 4.4V.6z'/></svg>"    
		   ));
        return `resource://${subst}/`;
    })(Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler));
    maxrequests = 50, // Максимальное количество параллельных запросов
    maxtimeout = 30, // Длительность до прерывания запроса в секундах
    alertnotification = true; // Уведомление о завершении поиска фавиконок для закладок

    var favicons = {
        _favrunning: false,
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        showAlert(title, val) {
            try {
                this.alertsService.showAlertNotification(img, title, val, false);
            } catch(e) {}
        },
        favSearchStart() {
            if (this._favrunning) return;
            this._favrunning = true;
            this.callWithEachWindow(id, {fill: "color-mix(in srgb, currentColor 20%, #e31b5d)"});
            PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid).then(root => {
                var urlsList = [];
                var convert = (node, url) => {
                    if (node.children)
                        node.children.map(convert);
                    else if ((url = node.uri) && /^(?:https?|ftp|file):/.test(url))
                        urlsList.push(url);
                };
                convert(root);
                var favForPage = siteURI => {
                    return new Promise(resolve => {
                        try {
                            siteURI = Services.io.newURI(siteURI);
                        } catch(e) {
                            resolve(null);
                        }
                        PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => {
                            if (uri === null)
                                resolve(siteURI);
                            else
                                resolve(null);
                        });
                    });
                };
                Promise.all(urlsList.map(favForPage)).then(results => this.favSearchResults(results.filter(url => url !== null)));
            });
        },
        favComplete(favsuccesslength, favmaxlength) {
            this._favrunning = false;
            this.callWithEachWindow(id, {fill: ""});
            if (alertnotification)
                this.showAlert("Поиск фавиконок", `Успешно обработано - ${favsuccesslength}, не удалось обработать - ${favmaxlength - favsuccesslength}`);
        },
        favSearchResults(results) {
            var favmaxlength = results.length;
            var favsuccesslength = 0;
            if (!favmaxlength) {
                this.favComplete(0, 0);
                return;
            }
            var favmaxtimeout = maxtimeout * 1000;
            var _favmaxlength = favmaxlength;
            var splice = results.splice(0, maxrequests);
            var favSearchPage = siteURI => {
                (new Promise(resolve => {
                    try {
                        let req = new XMLHttpRequest();
                        req.mozBackgroundRequest = true;
                        req.open("GET", siteURI.spec, true);
                        req.responseType = "document";
                        req.overrideMimeType("text/html");
                        req.timeout = favmaxtimeout;
                        req.onload = () => {console.log(req)
                            try {
                                let doc = req.responseXML, favURI;
                                if (doc) {
                                    let links = doc.querySelectorAll("head link[href][rel~='icon']"), lastlink, is16, is32, isany;
                                    for (let link of links) {
                                        if (link.sizes.length === 1) {
                                            let size = link.sizes[0];
                                            if (/any/i.test(size))
                                                isany = link;
                                            else if (/32x32/i.test(size))
                                                is32 = link;
                                            else if (/16x16/i.test(size))
                                                is16 = link;
                                        }
                                        lastlink = link;
                                    }
                                    links = isany || is32 || is16 || lastlink;
                                    if (links)
                                        favURI = links.href;
                                }
                                if (!favURI)
                                    favURI = `${req.responseURL ? Services.io.newURI(req.responseURL).prePath : siteURI.prePath}/favicon.ico`;
                                let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
                                let request = PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, Services.io.newURI(favURI), false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
                                    onComplete() {
                                        ++favsuccesslength;
                                        resolve();
                                        timer.cancel();
                                        timer = null;
                                        request = null;
                                    },
                                }, Services.scriptSecurityManager.getSystemPrincipal());
                                if (!request) {
                                    resolve();
                                    timer = null;
                                    return;
                                }
                                timer.initWithCallback(() => {
                                    resolve();
                                    try {
                                        request.cancel();
                                    } catch(e) {}
                                    timer = null;
                                    request = null;
                                }, favmaxtimeout, timer.TYPE_ONE_SHOT);
                            } catch(e) {
                                resolve();
                            }
                        };
                        req.onabort = () => {
                            resolve();
                        };
                        req.onerror = req.ontimeout = () => {
                            resolve();
                            req.abort();
                        };
                        req.send(null);
                    } catch(e) {
                        resolve();
                    }
                })).then(() => {
                    if (!(--_favmaxlength)) {
                        this.favComplete(favsuccesslength, favmaxlength);
                        return;
                    }
                    if (!results.length) return;
                    favSearchPage(results.shift());
                });
            };
            splice.map(favSearchPage);
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
            if (favicons._favrunning)
                btn.style.setProperty("fill", "color-mix(in srgb, currentColor 20%, #e31b5d)");
        },
        onCommand(e) {
            favicons.favSearchStart();
        },
    });
})();

и в этой не оторажаються иконки поисковых систем
скрытый текст

Выделить код

Код:

//Искать в...............
(this.contextsearch = {
            topic: "browser-search-engine-modified",
            hide: "browser.search.hiddenOneOffs",
            defaultImg: "chrome://browser/skin/search-engine-placeholder.png",
            searchSelect: null,
            popup: null,
            init(that) {
                var searchSelect = this.searchSelect = document.querySelector("#context-searchselect");
                if (!searchSelect)
                    return;
                var popup = this.popup = searchSelect.closest("menupopup");
                popup.addEventListener("popupshowing", this);
                that.unloadlisteners.push("contextsearch");
            },
            destructor() {
                this.popup.removeEventListener("popupshowing", this);
                if (this.popupshowing == this.handler) {
                    this.popup.removeEventListener("popuphidden", this);
                    Services.obs.removeObserver(this, this.topic);
                    Services.prefs.removeObserver(this.hide, this);
                }
            },
            handleEvent(e) {
                this[e.type](e);
            },
            popupshowing(e) {
                var popup = this.popup;
                var searchSelect = this.searchSelect;
                if (e.target != popup || searchSelect.hidden) return;

                var menu = document.createXULElement("menu");
                menu.className = "menu-iconic";
                var menupopup = document.createXULElement("menupopup");
                menu.append(menupopup);
                menu.ePopup = menupopup;
                searchSelect.collapsed = true;
                searchSelect.before(menu);
                menu.onclick = this.search.bind(this);
                this.handler = ev => {
                    if (ev.target != popup) return;
                    menu.hidden = searchSelect.hidden;
                };
                this.handlerRebuild = () => this.handler(e) || this.rebuild(menu);
                this.popuphidden = ev => {
                    if (ev.target != popup) return;
                    menu.hidden = true;
                };
                this.popup.addEventListener("popuphidden", this);
                this.rebuild(menu);
            },
    getEngines() {
        var args = "hideOneOffButton" in Services.search.defaultEngine
            ? [e => !e.hideOneOffButton]
            : Object.defineProperty(
                [function(e) {return !this.includes(e.name);}], "1", {
                    get: () => Services.prefs.getStringPref(this.hide)?.split(",") || []
                }
            );
        return (this.getEngines = async () =>
            (await Services.search.getVisibleEngines()).filter(...args)
        )();
    },
    async rebuild(menu) {
        var de = Services.search.defaultEngine;
        de = de.wrappedJSObject || de;
        this.setAttrs(menu, de, `Искать в ${de.name} или в ...`);
        menu.ePopup.textContent = "";
        for(let engine of await this.getEngines()) {
                    if (engine == de) continue;
                    var menuitem = document.createXULElement("menuitem");
                    menuitem.className = "menuitem-iconic";
                    this.setAttrs(menuitem, engine);
                    menu.ePopup.append(menuitem);
                }
                this.popupshowing = this.handler;
                Services.obs.addObserver(this, this.topic, false);
                Services.prefs.addObserver(this.hide, this);
            },
            setAttrs(node, engine, label = engine.name) {
                node.engine = engine;
                node.setAttribute("label", label);
                node.setAttribute("image", engine.iconURI ? engine.iconURI.spec : this.defaultImg);
            },
            observe() {
                this.popupshowing = this.handlerRebuild;
                Services.obs.removeObserver(this, this.topic);
                Services.prefs.removeObserver(this.hide, this);
            },
            search(e) {
                var {engine} = e.target;
                if (!engine) return;
                var searchSelect = this.searchSelect;
                var submission = engine.getSubmission(
                    searchSelect.searchTerms, null, "contextmenu"
                );
                if (submission) {
                    let tab = gBrowser.addTab(submission.uri.spec, {
                        postData: submission.postData,
                        index: (gBrowser.selectedTab._tPos + 1),
                        triggeringPrincipal: searchSelect.principal
                    });
                    if (e.button == 0)
                        gBrowser.selectedTab = tab;
                }
                var popup = this.popup;
                e.button != 1 && popup.state == "open" && popup.hidePopup();
            }
        }).init(this);

  [firefox] 123.0

egorsemenov06
У меня кнопка
//Искать в...............
от Vitaliy V. отвалилась давно. Перешёл на вариант Dumby, работает прекрасно ссылка. Хотя, как там на 123-й не знаю, не обновлялся.

egorsemenov06
Пробуйте..

Browser search engine

Выделить код

Код:

// ==UserScript==
// @name      Browser search engine
// @author    Vitaliy V.
// @include   main
// @shutdown  window.contextsearch.destructor();
// @note      https://forum.mozilla-russia.org/viewtopic.php?pid=780283#p780283
// ==/UserScript==

(this.contextsearch = {
    topic: "browser-search-engine-modified",
    hide: "browser.search.hiddenOneOffs",
    defaultImg: "chrome://browser/skin/search-engine-placeholder.png",
    searchSelect: null,
    popup: null,
    init(that) {
        var searchSelect = this.searchSelect = document.querySelector("#context-searchselect");
        if (!searchSelect)
            return;
        var popup = this.popup = searchSelect.closest("menupopup");
        popup.addEventListener("popupshowing", this);
        that.unloadlisteners?.push("contextsearch");
    },
    destructor() {
        this.popup.removeEventListener("popupshowing", this);
        if (this.popupshowing == this.handler) {
            this.popup.removeEventListener("popuphidden", this);
            Services.obs.removeObserver(this, this.topic);
            Services.prefs.removeObserver(this.hide, this);
        }
    },
    handleEvent(e) {
        this[e.type](e);
    },
    popupshowing(e) {
        var popup = this.popup;
        var searchSelect = this.searchSelect;
        if (e.target != popup || searchSelect.hidden) return;

        var menu = document.createXULElement("menu");
        menu.className = "menu-iconic";
        var menupopup = document.createXULElement("menupopup");
        menu.append(menupopup);
        menu.ePopup = menupopup;
        searchSelect.style.setProperty("display", "none", "important");
        searchSelect.before(menu);
        menu.onclick = this.search.bind(this);
        this.handler = e => e.target != popup || (menu.hidden = searchSelect.hidden);
        this.handlerRebuild = e => this.handler(e) || this.rebuild(menu);
        this.popuphidden = ev => {
            if (ev.target != popup) return;
            menu.hidden = true;
        };
        this.popup.addEventListener("popuphidden", this);
        this.rebuild(menu);
    },
    getEngines() {
        var args = "hideOneOffButton" in Services.search.defaultEngine
            ? [e => !e.hideOneOffButton]
            : Object.defineProperty(
                [function(e) {return !this.includes(e.name);}], "1", {
                    get: () => Services.prefs.getStringPref(this.hide)?.split(",") || []
                }
            );
        return (this.getEngines = async () =>
            (await Services.search.getVisibleEngines()).filter(...args)
        )();
    },
    async rebuild(menu) {
        var de = Services.search.defaultEngine;
        de = de.wrappedJSObject || de;
        this.setAttrs(menu, de, `Искать в ${de.name} или в ...`);
        menu.ePopup.textContent = "";
        for(let engine of await this.getEngines()) {
            if (engine == de) continue;
            var menuitem = document.createXULElement("menuitem");
            menuitem.className = "menuitem-iconic";
            this.setAttrs(menuitem, engine);
            menu.ePopup.append(menuitem);
        }
        this.popupshowing = this.handler;
        Services.obs.addObserver(this, this.topic, false);
        Services.prefs.addObserver(this.hide, this);
    },
    setAttrs(node, engine, label = engine.name) {
        node.engine = engine;
        node.setAttribute("label", label);
        node.setAttribute("image", engine._iconURI ? engine._iconURI.spec : engine.iconURI ? engine.iconURI.spec : this.defaultImg);
    },
    observe() {
        this.popupshowing = this.handlerRebuild;
        Services.obs.removeObserver(this, this.topic);
        Services.prefs.removeObserver(this.hide, this);
    },
    search(e) {
        var {engine} = e.target;
        if (!engine) return;
        var searchSelect = this.searchSelect;
        var submission = engine.getSubmission(
            searchSelect.searchTerms, null, "contextmenu"
        );
        if (submission) {
            let tab = gBrowser.addTab(submission.uri.spec, {
                postData: submission.postData,
                index: (gBrowser.selectedTab._tPos + 1),
                triggeringPrincipal: searchSelect.principal
            });
            if (e.button == 0)
                gBrowser.selectedTab = tab;
        }
        var popup = this.popup;
        e.button != 1 && popup.state == "open" && popup.hidePopup();
    }
}).init(this);

Farby пишет

egorsemenov06
Пробуйте..

Browser search engine

Выделить код

Код:

// ==UserScript==
// @name      Browser search engine
// @author    Vitaliy V.
// @include   main
// @shutdown  window.contextsearch.destructor();
// @note      https://forum.mozilla-russia.org/viewtopic.php?pid=780283#p780283
// ==/UserScript==

(this.contextsearch = {
    topic: "browser-search-engine-modified",
    hide: "browser.search.hiddenOneOffs",
    defaultImg: "chrome://browser/skin/search-engine-placeholder.png",
    searchSelect: null,
    popup: null,
    init(that) {
        var searchSelect = this.searchSelect = document.querySelector("#context-searchselect");
        if (!searchSelect)
            return;
        var popup = this.popup = searchSelect.closest("menupopup");
        popup.addEventListener("popupshowing", this);
        that.unloadlisteners?.push("contextsearch");
    },
    destructor() {
        this.popup.removeEventListener("popupshowing", this);
        if (this.popupshowing == this.handler) {
            this.popup.removeEventListener("popuphidden", this);
            Services.obs.removeObserver(this, this.topic);
            Services.prefs.removeObserver(this.hide, this);
        }
    },
    handleEvent(e) {
        this[e.type](e);
    },
    popupshowing(e) {
        var popup = this.popup;
        var searchSelect = this.searchSelect;
        if (e.target != popup || searchSelect.hidden) return;

        var menu = document.createXULElement("menu");
        menu.className = "menu-iconic";
        var menupopup = document.createXULElement("menupopup");
        menu.append(menupopup);
        menu.ePopup = menupopup;
        searchSelect.style.setProperty("display", "none", "important");
        searchSelect.before(menu);
        menu.onclick = this.search.bind(this);
        this.handler = e => e.target != popup || (menu.hidden = searchSelect.hidden);
        this.handlerRebuild = e => this.handler(e) || this.rebuild(menu);
        this.popuphidden = ev => {
            if (ev.target != popup) return;
            menu.hidden = true;
        };
        this.popup.addEventListener("popuphidden", this);
        this.rebuild(menu);
    },
    getEngines() {
        var args = "hideOneOffButton" in Services.search.defaultEngine
            ? [e => !e.hideOneOffButton]
            : Object.defineProperty(
                [function(e) {return !this.includes(e.name);}], "1", {
                    get: () => Services.prefs.getStringPref(this.hide)?.split(",") || []
                }
            );
        return (this.getEngines = async () =>
            (await Services.search.getVisibleEngines()).filter(...args)
        )();
    },
    async rebuild(menu) {
        var de = Services.search.defaultEngine;
        de = de.wrappedJSObject || de;
        this.setAttrs(menu, de, `Искать в ${de.name} или в ...`);
        menu.ePopup.textContent = "";
        for(let engine of await this.getEngines()) {
            if (engine == de) continue;
            var menuitem = document.createXULElement("menuitem");
            menuitem.className = "menuitem-iconic";
            this.setAttrs(menuitem, engine);
            menu.ePopup.append(menuitem);
        }
        this.popupshowing = this.handler;
        Services.obs.addObserver(this, this.topic, false);
        Services.prefs.addObserver(this.hide, this);
    },
    setAttrs(node, engine, label = engine.name) {
        node.engine = engine;
        node.setAttribute("label", label);
        node.setAttribute("image", engine._iconURI ? engine._iconURI.spec : engine.iconURI ? engine.iconURI.spec : this.defaultImg);
    },
    observe() {
        this.popupshowing = this.handlerRebuild;
        Services.obs.removeObserver(this, this.topic);
        Services.prefs.removeObserver(this.hide, this);
    },
    search(e) {
        var {engine} = e.target;
        if (!engine) return;
        var searchSelect = this.searchSelect;
        var submission = engine.getSubmission(
            searchSelect.searchTerms, null, "contextmenu"
        );
        if (submission) {
            let tab = gBrowser.addTab(submission.uri.spec, {
                postData: submission.postData,
                index: (gBrowser.selectedTab._tPos + 1),
                triggeringPrincipal: searchSelect.principal
            });
            if (e.button == 0)
                gBrowser.selectedTab = tab;
        }
        var popup = this.popup;
        e.button != 1 && popup.state == "open" && popup.hidePopup();
    }
}).init(this);

Большое Спасибо эта рабоьает как надо !!!!!А с первой не поможите?

egorsemenov06 пишет

А с первой не поможите?

Это баг 1872673 - Remove 'console' export from Console.sys.mjs
То есть, дело не в коде кнопки, а в само́м UCF.


Но держать отладочный консольский стафф в кнопке постоянно
не требуется, можешь просто удалить console.log(req)


Однако, вернуть в укфский сандбокс консоль не помешает.
Я тут в user_chrome.js так переставлял

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

Выделить код

Код:

/*
                if ("defineLazyModuleGetters" in XPCOMUtils)
                    XPCOMUtils.defineLazyModuleGetters(scope, {
                        console: "resource://gre/modules/Console.jsm",
                        AddonManager: "resource://gre/modules/AddonManager.jsm",
                        AppConstants: "resource://gre/modules/AppConstants.jsm",
                        E10SUtils: "resource://gre/modules/E10SUtils.jsm",
                        FileUtils: "resource://gre/modules/FileUtils.jsm",
                        OS: "resource://gre/modules/osfile.jsm",
                        PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
                        setTimeout: "resource://gre/modules/Timer.jsm",
                        setTimeoutWithTarget: "resource://gre/modules/Timer.jsm",
                        clearTimeout: "resource://gre/modules/Timer.jsm",
                        setInterval: "resource://gre/modules/Timer.jsm",
                        setIntervalWithTarget: "resource://gre/modules/Timer.jsm",
                        clearInterval: "resource://gre/modules/Timer.jsm",
                    });
*/
                var data = {
                    AddonManager: null, AppConstants: null, E10SUtils: null, FileUtils: null, PlacesUtils: null,
                    Timer: ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval"]
                };
                var sfx, def, modules = {};
                var vers = parseInt(Services.appinfo.platformVersion);
                if (vers <= 114) data.osfile = "OS";
                if (vers <= 122) def = XPCOMUtils.defineLazyModuleGetters, sfx = "jsm", data.Console = "console";
                else def = ChromeUtils.defineESModuleGetters, sfx = "sys.mjs",
                    ChromeUtils.defineLazyGetter(scope, "console", () => Cu.getGlobalForObject(Cu).console.createInstance());
                var set = (key, val) => modules[key] = `resource://gre/modules/${val}.${sfx}`;
                for(var key in data) {
                    var val = data[key] || key;
                    if (Array.isArray(val)) for(var str of val) set(str, key);
                    else set(val, key);
                }
                def(scope, modules);

Dumby пишет
egorsemenov06 пишет

А с первой не поможите?

Это баг 1872673 - Remove 'console' export from Console.sys.mjs
То есть, дело не в коде кнопки, а в само́м UCF.


Но держать отладочный консольский стафф в кнопке постоянно
не требуется, можешь просто удалить console.log(req)


Однако, вернуть в укфский сандбокс консоль не помешает.
Я тут в user_chrome.js так переставлял

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

Выделить код

Код:

/*
                if ("defineLazyModuleGetters" in XPCOMUtils)
                    XPCOMUtils.defineLazyModuleGetters(scope, {
                        console: "resource://gre/modules/Console.jsm",
                        AddonManager: "resource://gre/modules/AddonManager.jsm",
                        AppConstants: "resource://gre/modules/AppConstants.jsm",
                        E10SUtils: "resource://gre/modules/E10SUtils.jsm",
                        FileUtils: "resource://gre/modules/FileUtils.jsm",
                        OS: "resource://gre/modules/osfile.jsm",
                        PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
                        setTimeout: "resource://gre/modules/Timer.jsm",
                        setTimeoutWithTarget: "resource://gre/modules/Timer.jsm",
                        clearTimeout: "resource://gre/modules/Timer.jsm",
                        setInterval: "resource://gre/modules/Timer.jsm",
                        setIntervalWithTarget: "resource://gre/modules/Timer.jsm",
                        clearInterval: "resource://gre/modules/Timer.jsm",
                    });
*/
                var data = {
                    AddonManager: null, AppConstants: null, E10SUtils: null, FileUtils: null, PlacesUtils: null,
                    Timer: ["setTimeout", "setTimeoutWithTarget", "clearTimeout", "setInterval", "setIntervalWithTarget", "clearInterval"]
                };
                var sfx, def, modules = {};
                var vers = parseInt(Services.appinfo.platformVersion);
                if (vers <= 114) data.osfile = "OS";
                if (vers <= 122) def = XPCOMUtils.defineLazyModuleGetters, sfx = "jsm", data.Console = "console";
                else def = ChromeUtils.defineESModuleGetters, sfx = "sys.mjs",
                    ChromeUtils.defineLazyGetter(scope, "console", () => Cu.getGlobalForObject(Cu).console.createInstance());
                var set = (key, val) => modules[key] = `resource://gre/modules/${val}.${sfx}`;
                for(var key in data) {
                    var val = data[key] || key;
                    if (Array.isArray(val)) for(var str of val) set(str, key);
                    else set(val, key);
                }
                def(scope, modules);

Большое Спасибо!!!!теперь отлично

В шапке бы поправить ссылку на рабочую - "Восстановить фавиконки закладок".
Похож чем-то, с тем что выше. Тоже рабочий, пользуюсь. ucf_contextsearch.js
Да они идентичны, с мелкими различиями в коде)

b0ttle пишет

В шапке бы поправить ссылку

На этом форуме нет шапок, есть только первый пост, посему это могут сделать либо создатель либо модератор...

b0ttle пишет

Да они идентичны, с мелкими различиями в коде)

Если у вас все работает,

b0ttle пишет

Тоже рабочий, пользуюсь. ucf_contextsearch.js

пользуйтесь... Речь шла про 1945 год, ой про 123 найди различия...

Farby
Сорри, этот момент упустил. Вы его сами подправили, вроде разбирайтесь в коде?
Надеюсь, что обновят некоторые ссылки с первого поста, а так скорее затеряется.

b0ttle пишет

В шапке бы поправить ссылку на рабочую - "Восстановить фавиконки закладок".

Раз это не в скрипте проблема, шапку пока не менял.


Dumby пишет

Это баг 1872673 - Remove 'console' export from Console.sys.mjs
То есть, дело не в коде кнопки, а в само́м UCF.

Dumby - Благодарю! :beer:
Обновил на гитхабе chrome/user_chrome_files/user_chrome.js
в Demo-профиле - сборке полезных скриптов немного поправил:
user_chrome.js, UcfPrefs.jsm, ucf_UrlTooltip, ucf_contextmenuopenwith, ucf_contextsearch, ucf_hookClicks

Dobrov. Ура, ждал обновы)

Dobrov пишет

Раз это не в скрипте проблема

Кстати, скрипт под багом ходит (1552815).
Движуха там затихла, но в любой момент это дело могут пнуть,
и баг вывалится как "FIXED". Прикинь тогда разгребать.

.jsm

А вот и баг о временах и сроках.


Может завести какую-нибудь папку с любым условным названием типа «129».
Запилить на неё readme, мол здесь вам (пока) не что-то готовое,
но всего лишь WIP-полигон миграции JSM —> ESM.


Это я в том смысле, что время пока есть,
и «подстелить соломку», а не чтобы как «снег на голову».
Совместимость, разумеется, может и должна быть сброшена.

Dumby пишет

.jsm
А вот и баг о временах и сроках.

когда JSM уберут, то UCF практически перестанет работать!
Я не осилю добавить ESM в UCF-user_chrome.js и в CustomStylesScripts.jsm загрузчик скриптов/стилей…

Dobrov пишет

Я не осилю добавить ESM

там нечего сложного глаза бояться, руки делают, здесь Dumby писал как...

Dobrov
есть возможность сделать в ucf_contextsearch.js открытие вкладки поиска фоном? Или зависящим от этой настройки: user_pref("browser.search.context.loadInBackground", true);

Dumby если удалят jsm то и эти кнопки перестанут работать?

скрытый текст
text-to-link.js

Выделить код

Код:

//Ссылки кликабельны
try {(() => {
    var id = "ucf-text-to-link",
    label = "Текст URL в кликабельные ссылки",
    tooltiptext = "Превратить текст URL в кликабельные ссылки",
    img = (rph => {
        var subst = "ucf-text-to-link-btn-img";
        rph.setSubstitution(subst, Services.io.newURI(
           "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16'><path style='fill:none;stroke:context-fill;stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='m5.6 10.4 4.8-4.8m-8.9 4.9c-2.6 2.6 1.4 6.6 4 4l1-1c2.6-2.6-1.4-6.6-4-4zm9-9c2.6-2.6 6.6 1.4 4 4l-1 1c-2.6 2.6-6.6-1.4-4-4z'/></svg>"        ));
        return `resource://${subst}/`;
    })(Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler));
	
    var texttolink = {
        _registerActor() {
            if (this.registerActor) return;
            ChromeUtils.registerWindowActor("UcfTextToLinkActor", {
                child: {
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.jsm",
                },
                allFrames: true,
                messageManagerGroups: ["browsers"],
            });
            this.registerActor = true;
        },
        sendAsyncMessages(win, message, data) {
            this._registerActor();
            this.sendAsyncMessages = this._sendAsyncMessages;
            this.sendAsyncMessages(win, message, data);
        },
        _sendAsyncMessages(win, message, data) {
            var {browsingContext} = win.gBrowser.selectedBrowser;
            ({
                "UcfTextToLinkActor:TextToLink"() {
                    for (let actor of this)
                        actor.sendAsyncMessage(message);
                },
                *[Symbol.iterator]() {
                    var contextsToVisit = [browsingContext];
                    while (contextsToVisit.length) {
                        let currentContext = contextsToVisit.pop();
                        let global = currentContext?.currentWindowGlobal;
                        if (!global) continue;
                        yield global.getActor("UcfTextToLinkActor");
                        contextsToVisit.push(...currentContext.children);
                    }
                },
            })[message]?.();
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
        },
        onCommand(e) {
            texttolink.sendAsyncMessages(e.view, "UcfTextToLinkActor:TextToLink");
        },
    });
})();} catch(e) {}

UcfTextToLinkActorChild.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfTextToLinkActorChild"];
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["NodeFilter", "Node"]);
XPCOMUtils.defineLazyGetter(this, "excludedTags", () => {
    return new Set(["a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img","select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"]);
});

class UcfTextToLinkActorChild extends JSWindowActorChild {
    receiveMessage(msg) {
        return ({
            "UcfTextToLinkActor:TextToLink": () => {
                this.textToLink();
            },
        })[msg.name]?.();
    }
    textToLink() {
        if (this.running || !this.document?.body)
            return;
        this.running = true;
        var url_regexp = /(^|[\s(,;'"`“\[\]=_])((?:(?:https?|ftp):\/\/[-\wа-яё.!~*'();,/?:@&=+$#%_\u2300-\u23FF\u2600-\u27BF]|www\d{0,3}[.][a-zа-яё0-9.-]{2,249}|[a-zа-яё0-9.-]{2,250}[.][a-zа-яё]{2,4}\/)[-\wа-яё.!~*'();,/?:@&=+$#%_\u2300-\u23FF\u2600-\u27BF]*)/gim,
        email_regexp = /(^|mailto:|[\s(,;'"`“\[\]=])([\w!#$%&'*+/=?^`{|}~.-]{2,}@[\[\]a-z0-9.-]+)/gim,
        setEmail = (node, text) => {
            var repl = text.replace(email_regexp, '$1<a href="mailto:$2" class="add__TextToEmail">$2</a>');
            if (text.length == repl.length)
                return;
            var span = node.ownerDocument.createElement("span");
            span["innerHTML"] = repl;
            node.replaceWith(span);
        },
        setLink = (node, text) => {
            if (!(text = node.textContent)) return;
            text = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            var repl = text.replace(url_regexp, '$1<a href="$2" target="_blank" class="add__TextToLink">$2</a>');
            if (text.length == repl.length) {
                setEmail(node, text);
                return;
            }
            var span = node.ownerDocument.createElement("span");
            span["innerHTML"] = repl;
            for (let el of span.querySelectorAll("a.add__TextToLink[href]:not([href^='http']):not([href^='ftp'])"))
                el.setAttribute("href", `http://${el.getAttribute("href")}`);
            node.replaceWith(span);
            var txtnode = Node.TEXT_NODE;
            for (let child of span.childNodes) {
                let txt;
                if (child.nodeType === txtnode && (txt = child.textContent))
                    setEmail(child, txt);
            }
        },
        elList = [],
        getWalker = elem => {
            var doc = elem.ownerDocument, reject = NodeFilter.FILTER_REJECT, skip = NodeFilter.FILTER_SKIP, accept = NodeFilter.FILTER_ACCEPT, txtnode = Node.TEXT_NODE;
            var walker = doc.createTreeWalker(elem, NodeFilter.SHOW_ALL, {
                acceptNode(node) {
                    if (excludedTags.has(node.localName))
                        return reject;
                    if (node.nodeType !== txtnode && !node.shadowRoot)
                        return skip;
                    return accept;
                }
            }, false);
            while (walker.nextNode()) {
                let currnode = walker.currentNode;
                if (!currnode.shadowRoot)
                    elList.push(currnode);
                else
                    getWalker(currnode.shadowRoot);
            }
        };
        getWalker(this.document.body);
        for (let el of elList)
            setLink(el);
        elList = [];
        this.running = false;
    }
}

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

Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}

скрытый текст
special_widgets.js

Выделить код

Код:

(this.specialwidgets = {
    _timer: null,
    get Customizable() {
        delete this.Customizable;
        if ("createSpecialWidget" in CustomizableUI)
            return this.Customizable = CustomizableUI;
        var scope = null;
        try {
            scope = Cu.import("resource:///modules/CustomizableUI.jsm", {}).CustomizableUIInternal;
        } catch (e) { }
        return this.Customizable = scope;
    },
    init(that) {
        if (!("CustomizableUI" in window) || !("gCustomizeMode" in window))
            return;
        that.unloadlisteners.push("specialwidgets");
        window.addEventListener("customizationready", this);
    },
    destructor() {
        window.removeEventListener("customizationready", this);
    },
    handleEvent(e) {
        this[e.type](e);
    },
    customizationchange() {
        clearTimeout(this._timer);
        this._timer = setTimeout(() => {
            this.createSpecialWidgets();
        }, 1000);
    },
    customizationready() {
        if (!this.Customizable)
            return;
        this.createSpecialWidgets();
        window.addEventListener("customizationchange", this);
        window.addEventListener("customizationending", this);
    },
    customizationending() {
        window.removeEventListener("customizationchange", this);
        window.removeEventListener("customizationending", this);
    },
    createSpecialWidgets() {
        try {
            let fragment = document.createDocumentFragment();
            if (this.findSpecialWidgets("spring")) {
                let spring = this.Customizable.createSpecialWidget("spring", document);
                spring.setAttribute("label", "Растягивающийся интервал");
                fragment.append(gCustomizeMode.wrapToolbarItem(spring, "palette"));
            }
            if (this.findSpecialWidgets("spacer")) {
                let spacer = this.Customizable.createSpecialWidget("spacer", document);
                spacer.setAttribute("label", "Интервал");
                fragment.append(gCustomizeMode.wrapToolbarItem(spacer, "palette"));
            }
            if (this.findSpecialWidgets("separator")) {
                let separator = this.Customizable.createSpecialWidget("separator", document);
                separator.setAttribute("label", "Разделитель");
                fragment.append(gCustomizeMode.wrapToolbarItem(separator, "palette"));
            }
            gCustomizeMode.visiblePalette.append(fragment);
        } catch (e) {}
    },
    findSpecialWidgets(string) {
        try {
            if (!gCustomizeMode.visiblePalette.querySelector(`toolbar${string}[id^="customizableui-special-${string}"]`))
                return true;
        } catch (e) {}
        return false;
    }
}).init(this);

если это так ,то просьба переделать их пожалуйста.

28-02-2024 22:00:00
и еще одна

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

Выделить код

Код:

// 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: 0,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

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

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

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

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

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

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

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

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

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

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

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

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
		if(!isContextOpened())
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& 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;
				return true;
			}
			return false;
		}
	);
}
function isContextOpened() {
	return inBtn(document.popupNode);
}
function inBtn(node) {
	for(; node; node = node.parentNode)
		if(node == self)
			return true;
	return false;
}
Выделить код

Код:

//Дополнения
try {CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js"
		));
	},
	onCreated(btn) {
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC");
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
});} catch(ex) {Cu.reportError(ex);}

Northtech пишет

есть возможность сделать в ucf_contextsearch.js открытие вкладки поиска фоном? Или зависящим от этой настройки: user_pref("browser.search.context.loadInBackground", true);

Поиск и так открывается в фоновой вкладке по клику правой кнопкой или колёсиком на любых строках меню и подменю.

egorsemenov06
Я так понял. Когда накроется jsm, отвалится весь UCF. Куда Вы будете эти кнопки писать, если CustomStylesScripts.jsm не будет работать? Или пересаживаться на старую версию версию UCF?

Dumby возникла хотелка для менюшки кнопки, которую я немного доработал.


это меню создаётся однократно и перед кликом на кнопке его не существует, т.е. однократно выполняется menu.render = this.render;
Нужно, чтоб менялись строки меню перед его открытием в зависимости от опций about:config и прочих проверок, например "Графика сайтов включена" вместо "Графика сайтов вкл/выкл"


Прошу доработать код, чтоб ключи «lab» могли быть с backticks-кавычками и текст мог обновляться перед каждым открытием меню:
lab: `${проверка условия ? "√ Да, всё OK" : "Х Нет"}`

Автономное меню для тех, кому не нужен ucf_hookClicks.js

Выделить код

Код:

(async id => { // by Dumby forum.mozilla-russia.org/viewtopic.php?pid=807581#p807581

MyMenu = { //массив команд пользователя, alt() клик правой кнопкой
	Pics: {
		lab: `Графика сайтов вкл/выкл | Right Click`,
		inf: 'текст обновляется перед открытием меню',
		cmd(){
			var n = uc.pref(E.v) == 2; uc.pref(E.v, n ? 1 : 2);
			BrowserReload();
		},
		alt(){ /* для этих действий нужен ucf_hookClicks.js */},
	},
	"Прочие команды": { sep: 1, //сперва разделитель
		cmd(){console.log("OK");}
	},
}

CustomizableUI.createWidget({
		id: id, label: id, tooltiptext: id, localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onCreated(btn) {
			btn.style.setProperty("list-style-image", "url(chrome://branding/content/icon32.png)");
			btn.type = "menu";
			var doc = btn.ownerDocument, m = nn => doc.createXULElement(nn);
			var popup = m("menupopup"), menu = m("menuitem");
			menu.m = m;
			menu.fill = this.fill;
			menu.render = this.render;
			popup.append(menu);
			btn.prepend(popup);
		},
		render(){
			var popup = this.parentNode;
			this.remove();
			this.fill(MyMenu, popup);
		},
		fill(o, popup){
			if (typeof o == "object") for (key in o){
			var {lab, sep, sub, cmd, alt, inf, img} = o[key];
			var name = sub ? "menu" : "menuitem";
			sep && popup.append(this.m("menuseparator"));
			var item = this.m(name);
			item.setAttribute("label", lab || key); //update при изменении настроек
			if (img)
				item.className = name +"-iconic", item.setAttribute("image",img);
			if (inf) item.tooltipText = inf;
			item.alt = alt; //cmd2
			sub || cmd && item.setAttribute("oncommand", cmd.toString().replace(/cmd\(.*?\){/,'{var trg = event.target || event;'));
			/^(sub|sep|inf|lab|img)$/.test(key) || popup.append(item);
			sub && this.fill(o[key], item.appendChild(this.m("menupopup")));
			}
		},
});

E = {
	v: "permissions.default.image"
}
window.uc = { //all ChromeOnly-scripts
	pref(key,set){ //или key = [key,default]
		if (!Array.isArray(key)) key = [key];
		var t = prefs.getPrefType(key[0]), m = {b:"Bool",n:"Int",s:"String"};
		t = m[t == 128 ? "b" : t == 64 ? "n" : t == 32 ? "s" : ""];
		if (set == "get") return t; //тип опции
		if (!t) t = m[set != undefined ? (typeof set)[0] : (typeof key[1])[0]];
		if (t) if (set != undefined)
			prefs[`set${t}Pref`](key[0],set)
		else
			set = prefs[`get${t}Pref`](...key);
		return set;
	}
}

})("ucf_test_menu");


xrun1 я просто останусь на старой версии браузера, как это делает Dumby (у него вроде FF78)

egorsemenov06 пишет

Ссылки кликабельны

Здесь можно переделать. Переименовать UcfTextToLinkActorChild.{ jsm —> mjs } и правки

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

В text-to-link.js

Выделить код

Код:

/*
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.jsm",
*/
                    esModuleURI: "chrome://user_chrome_files/content/custom_scripts/UcfTextToLinkActorChild.mjs"

В UcfTextToLinkActorChild.mjs

Выделить код

Код:

/*
var EXPORTED_SYMBOLS = ["UcfTextToLinkActorChild"];
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["NodeFilter", "Node"]);
XPCOMUtils.defineLazyGetter(this, "excludedTags", () => {
    return new Set(["a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img","select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"]);
});

class UcfTextToLinkActorChild extends JSWindowActorChild {
*/
var excludedTags = Object.defineProperty({}, "has", {
    value: tag => (excludedTags = new Set([
        "a","svg","canvas","applet","input","button","area","embed","noembed","frame","frameset","head","iframe","img",
        "select","option","datalist","map","meta","noscript","video","audio","object","param","script","style","textarea","code"
    ])).has(tag)
});

export class UcfTextToLinkActorChild extends JSWindowActorChild {

переключение раскладки клавиатуры по F8

Для этого кода нужен дебаггер.
В UCF 3 вообще необходимо встраивать дебаггер,
не городить же в каждом скрипте, где он нужен, свой собственный.


Но, наверно, можно пробросить implementation из кода само́й кнопки,
однако, этот код не предоставлен.

special_widgets.js

Этот скрипт — часть UCF.
На первый взгляд, в данном контексте ему ничего не угрожает.
Наоборот, его скорее покрамсать хочется.

и еще одна

Здесь можно попробовать влепить что-нибудь такое,
тогда, надеюсь, не будет импорта никаких jsm'ок.

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

Выделить код

Код:

/*
	try {
*/
	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 {


Dobrov пишет

немного доработал

Ничего себе «немного». Как там задумана обработка
структуры MyMenu я сходу понять не могу, субменю вообще в примере нет.


Ну хорошо хоть вопрос не об этом.
Добавил туда var {prefs} = Services; и заработало.


Можно ввести ещё один ключ — upd
который будет представлять собой функцию,
которая перекроет метод render() для item'а.


Ключевое слово this в этой функции будет ссылаться на сам item.
Например, вместо lab и inf пишем:


upd() {
        //var {uc, E} = this.ownerGlobal;
        var val = uc.pref(E.v);
        this.label = `${E.v} is ${val == 1 || val == 2 ? val : "not 1 and not 2"}`;
        this.tooltipText = val;
},


Далее, учитываем новый ключ в var {lab, sep, sub, cmd, alt, inf, img, upd} = o[key];
И дописываем подмену после добавления item'а в popup:
/^(sub|sep|inf|lab|img)$/.test(key) || popup.append(item) || upd && (item.render = upd).call(item);


Однако, хоть lab и убран, код тогда всё равно проставляет вместо него label — key,
а upd() уже затем ставит свой. Это слегка нехорошо, но тут уж думай сам, или забей.

Dumby пишет

Ничего себе «немного». Как там задумана обработка структуры MyMenu я сходу понять не могу, субменю вообще в примере нет.

Спасибо за помошь! :beer:
P.S. Массив вместе с субменю есть в ucf_hookClicks.js, вложенность любая.

Dumby пишет
egorsemenov06 пишет

Но, наверно, можно пробросить implementation из кода само́й кнопки,
однако, этот код не предоставлен

Если можно сделайте пожалуйста из этих 2-х кнопок одну что бы переключение раскладки клавиатуры было по F8

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

Выделить код

Код:

// Switch Keyboard Layout
try {(keybUtils => CustomizableUI.createWidget({
    type: "custom",
    id: "SwitchKeyboardLayout",
    onBuild(doc) {
        var btn = doc.createXULElement("toolbarbutton");
        btn.id = this.id;
        btn.label = btn.tooltipText = "Switch Keyboard Layout";
        btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";

        btn.setAttribute("oncommand", "linkedObj.switch(document);");
        btn.className = "toolbarbutton-1 chromeclass-toolbar-additional";
        btn.linkedObj = this;
        return btn;
    },
    switch(doc) {
        var br = doc.activeElement;
        br && br.localName == "browser" && br.isRemoteBrowser
            ? br.messageManager.loadFrameScript(this.url, false)
            : this.keybUtils.switchSelKeybLayout();
    },
    get url() {
        delete this.url;
        return this.url = `data:;charset=utf-8,(${
            encodeURIComponent(keybUtils)
        }).switchSelKeybLayout()`;
    },
    get keybUtils() {
        delete this.keybUtils;
        var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
        var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
        Services.scriptloader.loadSubScript(url, this);
        var {id} = this;
        this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
            var window = Services.focus.activeWindow, {document} = window;
            var button = document.getElementById(id);
            if(
                !_focusFixed
                && "closeMenus" in window
                && document.commandDispatcher.focusedElement == button
            ) {
                window.closeMenus(button);
                window.setTimeout(function(_this) {
                    _this.switchSelKeybLayout(_subCall, true);
                }, 0, this);
                return;
            }
            return document.commandDispatcher.focusedElement;
        }
        return this.keybUtils;
    }
}))(`{
    //== Options
    noSelBehavior:
        "BeginLine",
        //"EndLine",
        //"All",
        //"Top",
        //"Bottom",
        //"PageUp",
        //"PageDown",
        // etc

    // falsy - do nothing
    // Or use string like following to call cmd_select{string}

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            var primary = c in this.convTableForward;
            if(primary ^ c in this.convTableBackward)
                return primary;
        }
        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(Element.isInstance(node)) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
//        var e = this.noSelBehavior;
//        if(!e || typeof e != "object")
//            return;
//        var evt = node.ownerDocument.createEvent("KeyboardEvent");
//        evt.initKeyEvent(
//            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//            e.keyCode, e.charCode
//        );
//        node.dispatchEvent(evt);

       var beh = this.noSelBehavior;
       beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
    },
    beh2cmd: { // Ctrl_Shift_VK
        false_true_36: "cmd_selectLinePrevious", // Shift+Home
    },
    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`)} catch(ex) {Cu.reportError(ex);}
Выделить код

Код:

//переключение раскладки клавиатуры по F8
try {(id => {
    var listener = {
        get obj() {
            var obj = document.getElementById(id);
            if (obj) obj = obj.linkedObj;
            else {
                obj = Cu.import("resource:///modules/CustomizableUI.jsm", {})
                    .gPalette.get(id);
                if (obj) obj = obj.implementation;
                else {
                    Services.console.logStringMessage(id + " not found");
                    return this.destroy() || {switch() {}};
                }
            }
            delete this.obj; return this.obj = obj;
        },
        handleEvent(e) {
            if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat)
                return;
            //e.preventDefault();
            //e.stopPropagation();
            this.obj.switch(document);
        },
        destroy: function destroy() {
            removeEventListener("keydown", this, true);
            removeEventListener("unload", destroy);
        }
    };
    addEventListener("keydown", listener, true);
    addEventListener("unload", listener.destroy);
})("SwitchKeyboardLayout");} catch(ex) {Cu.reportError(ex);}


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

Dobrov пишет

Массив вместе с субменю есть в ucf_hookClicks.js, вложенность любая.

Да, теперь чуть понятнее, наверно.
А то я думал, к чему это /^(sub|sep|inf|lab|img)$/.test(key)


Оказывается, что если вписан sub, код прогоняет через for in
объект в который вписан sub.
То есть, и для ключей, упомянутых в этом регулярном выражении
тоже создаётся (ненужный) элемент (как минимум <menuitem>)
и ему присваивается одноимённый атрибут "label".


Если значение ключа окажется строкой, то тогда создаётся не <menuitem>,
а <menu>, и создаётся <menupopup>, который добавляется к этому menu.
Всё потому, что у строки есть sub.
Вот проверь с консоли: var {sub} = "bla-bla"; alert(sub);
Это будет — String.prototype.sub().


Эти ненужные элементы можно посмотреть в консоли,
если добавить в конец метода fill() строку
if (!item.parentNode) console.log(item.outerHTML);


Вот чтобы всё это низачем-созданное добро
не попало в итоговую DOM-структуру и нужен этот regexp.
Может этого как-то избежать, типа

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

Выделить код

Код:

//
	fill(o, popup) {
		for (key in o) {
			var val = o[key];
			if (typeof val != "object") continue;

			var {lab, sep, sub, cmd, alt, inf, img, upd} = val;

			sep && popup.append(this.m("menuseparator"));
			var name = sub ? "menu" : "menuitem";
			var item = this.m(name);

			item.setAttribute("label", lab || key);
			if (img)
				item.className = name + "-iconic",
				item.setAttribute("image", img);

			item.alt = alt; //cmd2
			if (inf) item.tooltipText = inf;
			sub || cmd && item.setAttribute("oncommand", cmd.toString().replace(
				/cmd\(.*?\){/, "{var trg = event.target || event;"
			));
			popup.append(item);
			upd && (item.render = upd).call(item);
			sub && this.fill(val, item.appendChild(this.m("menupopup")));
		}
	},

egorsemenov06 пишет

сделайте пожалуйста из этих 2-х

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

Выделить код

Код:

// Switch Keyboard Layout
(async keybUtils => CustomizableUI.createWidget(({
	localized: false,
	id: "SwitchKeyboardLayout",
	init() {
		this.label = this.tooltiptext = "Switch Keyboard Layout";
		var args = ["keydown", e => {
			if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat) return;
			//e.preventDefault();
			//e.stopPropagation();
			this.switch(e.view.document);
		}, true];
		var unload = e => e.target.ownerGlobal.removeEventListener(...args);
		var obs = win => {
			win.addEventListener(...args);
			win.addEventListener("unload", unload, {once: true});
		}
		var topic = "browser-delayed-startup-finished";
		Services.obs.addObserver(obs, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(obs, topic);
		}, "quit-application-granted");
		return this;
	},
	onCreated(btn) {
		btn.linkedObj = this;
		btn._handleClick = this.click;
		btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";
	},
	click() {
		this.linkedObj.switch(this.ownerDocument);
	},
	switch(doc) {
		var br = doc.activeElement;
		br && br.localName == "browser" && br.isRemoteBrowser
			? br.messageManager.loadFrameScript(this.url, false)
			: this.keybUtils.switchSelKeybLayout();
	},
	get url() {
		delete this.url;
		return this.url = `data:;charset=utf-8,(${
			encodeURIComponent(keybUtils)
		}).switchSelKeybLayout()`;
	},
	get keybUtils() {
		delete this.keybUtils;
		var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
		var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
		Services.scriptloader.loadSubScript(url, this);
		var {id} = this;
		this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
			var window = Services.focus.activeWindow, {document} = window;
			var button = document.getElementById(id);
			if(
				!_focusFixed
				&& "closeMenus" in window
				&& document.commandDispatcher.focusedElement == button
			) {
				window.closeMenus(button);
				window.setTimeout(function(_this) {
					_this.switchSelKeybLayout(_subCall, true);
				}, 0, this);
				return;
			}
			return document.commandDispatcher.focusedElement;
		}
		return this.keybUtils;
	}
}).init()))(`{
	//== Options
	noSelBehavior:
		"BeginLine",
		//"EndLine",
		//"All",
		//"Top",
		//"Bottom",
		//"PageUp",
		//"PageDown",
		// etc

	// falsy - do nothing
	// Or use string like following to call cmd_select{string}

	convTableForward: { // ru -> en
		"\\"": "@",
		":": "^",
		";": "$",
		"?": "&",
		",": "?",
		"/": "|",
		".": "/",
		"э": "'",
		"б": ",",
		"ю": ".",
		"Ж": ":",
		"ж": ";",
		"Б": "<",
		"Ю": ">",
		"Э": "\\"",
		"х": "[",
		"ъ": "]",
		"ё": "\`",
		"Х": "{",
		"Ъ": "}",
		"Ё": "~",
		"№": "#",
		"Ф": "A",
		"ф": "a",
		"И": "B",
		"и": "b",
		"С": "C",
		"с": "c",
		"В": "D",
		"в": "d",
		"У": "E",
		"у": "e",
		"А": "F",
		"а": "f",
		"П": "G",
		"п": "g",
		"Р": "H",
		"р": "h",
		"Ш": "I",
		"ш": "i",
		"О": "J",
		"о": "j",
		"Л": "K",
		"л": "k",
		"Д": "L",
		"д": "l",
		"Ь": "M",
		"ь": "m",
		"Т": "N",
		"т": "n",
		"Щ": "O",
		"щ": "o",
		"З": "P",
		"з": "p",
		"Й": "Q",
		"й": "q",
		"К": "R",
		"к": "r",
		"Ы": "S",
		"ы": "s",
		"Е": "T",
		"е": "t",
		"Г": "U",
		"г": "u",
		"М": "V",
		"м": "v",
		"Ц": "W",
		"ц": "w",
		"Ч": "X",
		"ч": "x",
		"Н": "Y",
		"н": "y",
		"Я": "Z",
		"я": "z",
		__proto__: null
	},
	//== End of options

	get convTableBackward() {
		var ctb = { __proto__: null };
		var ctf = this.convTableForward;
		for(var c in ctf)
			ctb[ctf[c]] = c;
		delete this.convTableBackward;
		return this.convTableBackward = ctb;
	},
	inPrimaryLayout: function(s) {
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			var primary = c in this.convTableForward;
			if(primary ^ c in this.convTableBackward)
				return primary;
		}
		return false;
	},
	switchKeybLayout: function(s, convTable) {
		var res = "";
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			res += c in convTable ? convTable[c] : c;
		}
		return res;
	},
	getFocusedElement: function() {
		return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
			.getFocusedElementForWindow(content, true, {});
	},
	switchSelKeybLayout: function(_subCall, _focusFixed) {
		var fe = this.getFocusedElement(_subCall, _focusFixed);
		if(!fe)
			return;
		if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
			var ta = fe;
			try {
				var val = ta.value;
				var sel = val.substring(ta.selectionStart, ta.selectionEnd);
			}
			catch(e) { // Non-text HTMLInputElement
				return;
			}
			if(!sel && val && this.noSelBehavior && !_subCall) {
				if(this.noSelBehavior == 1) {
					ta.selectionStart = 0;
					ta.selectionEnd = val.length;
					sel = val;
				}
				else {
					this.handleNoSel(ta);
					return;
				}
			}
			if(!sel)
				return;
			var res = this.switchKeybLayout(
				sel,
				this.inPrimaryLayout(sel)
					? this.convTableForward
					: this.convTableBackward
			);
			if(res != sel)
				this.insertText(ta, res);
		}
		else if(fe.contentEditable == "true") {
			var doc = fe.ownerDocument;

			var docURI = doc.documentURI;
			if(
				docURI.substr(0, 5) == "data:"
				&& docURI.indexOf("chrome://browser/skin/devtools/") != -1
			) {
				//~ todo: seems like we only can use paste from clipboard here...
				return;
			}

			var sel = doc.defaultView.getSelection();
			var rng = sel.rangeCount && sel.getRangeAt(0);
			var tmpNode;
			if(!rng || rng.collapsed) {
				if(!this.noSelBehavior || _subCall)
					return;
				if(this.noSelBehavior == 1) {
					var r = doc.createRange();
					r.selectNodeContents(fe);
					sel.removeAllRanges();
					sel.addRange(r);
					tmpNode = fe.cloneNode(true);
				}
				else {
					this.handleNoSel(fe);
					return;
				}
			}
			else {
				tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
				tmpNode.appendChild(rng.cloneContents());
			}

			var orig = tmpNode.innerHTML;
			var convTable = this.inPrimaryLayout(tmpNode.textContent)
				? this.convTableForward
				: this.convTableBackward;

			var _this = this;
			var parseChildNodes = function(node) {
				if(Element.isInstance(node)) {
					var childNodes = node.childNodes;
					for(var i = childNodes.length - 1; i >= 0; --i)
						parseChildNodes(childNodes[i]);
				}
				else if(node.nodeType == node.TEXT_NODE) {
					var text = node.nodeValue;
					var newText = _this.switchKeybLayout(node.nodeValue, convTable);
					if(newText != text)
						node.parentNode.replaceChild(doc.createTextNode(newText), node);
				}
			}
			parseChildNodes(tmpNode);

			var res = tmpNode.innerHTML;
			if(res != orig)
				doc.execCommand("insertHTML", false, res);
		}
	},
	handleNoSel: function(node) {
		this.select(node);
		this.switchSelKeybLayout(true);
	},
	select: function(node) {
//		var e = this.noSelBehavior;
//		if(!e || typeof e != "object")
//			return;
//		var evt = node.ownerDocument.createEvent("KeyboardEvent");
//		evt.initKeyEvent(
//			"keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//			e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//			e.keyCode, e.charCode
//		);
//		node.dispatchEvent(evt);

	   var beh = this.noSelBehavior;
	   beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
	},
	beh2cmd: { // Ctrl_Shift_VK
		false_true_36: "cmd_selectLinePrevious", // Shift+Home
	},
	insertText: function(ta, text) {
		//var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
		var editor = ta.editor
			.QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
		if(editor.flags & editor.eEditorReadonlyMask)
			return;

		var sTop = ta.scrollTop;
		var sHeight = ta.scrollHeight;
		var sLeft = ta.scrollLeft;
		// var sWidth = ta.scrollWidth;

		if(text)
			editor.insertText(text);
		else
			editor.deleteSelection(0, 0);

		ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
		ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
	}
}`);

Dumby пишет
egorsemenov06 пишет

сделайте пожалуйста из этих 2-х

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

Выделить код

Код:

// Switch Keyboard Layout
(async keybUtils => CustomizableUI.createWidget(({
	localized: false,
	id: "SwitchKeyboardLayout",
	init() {
		this.label = this.tooltiptext = "Switch Keyboard Layout";
		var args = ["keydown", e => {
			if (e.key != "F8" || e.ctrlKey || e.shiftKey || e.altKey || e.repeat) return;
			//e.preventDefault();
			//e.stopPropagation();
			this.switch(e.view.document);
		}, true];
		var unload = e => e.target.ownerGlobal.removeEventListener(...args);
		var obs = win => {
			win.addEventListener(...args);
			win.addEventListener("unload", unload, {once: true});
		}
		var topic = "browser-delayed-startup-finished";
		Services.obs.addObserver(obs, topic);
		Services.obs.addObserver(function quit(s, t) {
			Services.obs.removeObserver(quit, t);
			Services.obs.removeObserver(obs, topic);
		}, "quit-application-granted");
		return this;
	},
	onCreated(btn) {
		btn.linkedObj = this;
		btn._handleClick = this.click;
		btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAk1BMVEX///8/Pz8BAQF8fHwJCQkCAgIJCQl8fHx9fX0AAABJSUkBAQENDQ0wMDBVVVUAAABra2t0dHR7e3thYWEBAQEAAAAAAAABAQEAAAAgICABAQEBAQH8/Pzw8PDp6en39/fj4+Pe3t41V9I/YeWKioqDg4N9fX0jRa8wUrVoaGhkhuxWeNx1dXVCZMhvb28kRqsqTL4KidXxAAAAHHRSTlMAAABndwAAAAA0blVZcm1naWhNa6hrcJ8AAKRU4jk/3gAAAHVJREFUeF6FyMUCwjAUBdEbaQupUMOjqMv/fx1dQN6yZ3aDcWnyw6ezoRWNYc15kqKQZCFlgTKQQwgllL/dn5f3+bPb+6P3CpWNw56srdCZOMzDmA61i8O9nKvRaHLVukEryFKIFtnkj7ENYxmNdd5v+5xj1BcJ/w9Kj6K7ZAAAAABJRU5ErkJggg==";
	},
	click() {
		this.linkedObj.switch(this.ownerDocument);
	},
	switch(doc) {
		var br = doc.activeElement;
		br && br.localName == "browser" && br.isRemoteBrowser
			? br.messageManager.loadFrameScript(this.url, false)
			: this.keybUtils.switchSelKeybLayout();
	},
	get url() {
		delete this.url;
		return this.url = `data:;charset=utf-8,(${
			encodeURIComponent(keybUtils)
		}).switchSelKeybLayout()`;
	},
	get keybUtils() {
		delete this.keybUtils;
		var def = "let{KeyEvent,HTMLInputElement,HTMLTextAreaElement}=Cu.getGlobalForObject(Services);";
		var url = `data:;charset=utf-8,${def}%0Athis.keybUtils=${encodeURIComponent(keybUtils)}`;
		Services.scriptloader.loadSubScript(url, this);
		var {id} = this;
		this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
			var window = Services.focus.activeWindow, {document} = window;
			var button = document.getElementById(id);
			if(
				!_focusFixed
				&& "closeMenus" in window
				&& document.commandDispatcher.focusedElement == button
			) {
				window.closeMenus(button);
				window.setTimeout(function(_this) {
					_this.switchSelKeybLayout(_subCall, true);
				}, 0, this);
				return;
			}
			return document.commandDispatcher.focusedElement;
		}
		return this.keybUtils;
	}
}).init()))(`{
	//== Options
	noSelBehavior:
		"BeginLine",
		//"EndLine",
		//"All",
		//"Top",
		//"Bottom",
		//"PageUp",
		//"PageDown",
		// etc

	// falsy - do nothing
	// Or use string like following to call cmd_select{string}

	convTableForward: { // ru -> en
		"\\"": "@",
		":": "^",
		";": "$",
		"?": "&",
		",": "?",
		"/": "|",
		".": "/",
		"э": "'",
		"б": ",",
		"ю": ".",
		"Ж": ":",
		"ж": ";",
		"Б": "<",
		"Ю": ">",
		"Э": "\\"",
		"х": "[",
		"ъ": "]",
		"ё": "\`",
		"Х": "{",
		"Ъ": "}",
		"Ё": "~",
		"№": "#",
		"Ф": "A",
		"ф": "a",
		"И": "B",
		"и": "b",
		"С": "C",
		"с": "c",
		"В": "D",
		"в": "d",
		"У": "E",
		"у": "e",
		"А": "F",
		"а": "f",
		"П": "G",
		"п": "g",
		"Р": "H",
		"р": "h",
		"Ш": "I",
		"ш": "i",
		"О": "J",
		"о": "j",
		"Л": "K",
		"л": "k",
		"Д": "L",
		"д": "l",
		"Ь": "M",
		"ь": "m",
		"Т": "N",
		"т": "n",
		"Щ": "O",
		"щ": "o",
		"З": "P",
		"з": "p",
		"Й": "Q",
		"й": "q",
		"К": "R",
		"к": "r",
		"Ы": "S",
		"ы": "s",
		"Е": "T",
		"е": "t",
		"Г": "U",
		"г": "u",
		"М": "V",
		"м": "v",
		"Ц": "W",
		"ц": "w",
		"Ч": "X",
		"ч": "x",
		"Н": "Y",
		"н": "y",
		"Я": "Z",
		"я": "z",
		__proto__: null
	},
	//== End of options

	get convTableBackward() {
		var ctb = { __proto__: null };
		var ctf = this.convTableForward;
		for(var c in ctf)
			ctb[ctf[c]] = c;
		delete this.convTableBackward;
		return this.convTableBackward = ctb;
	},
	inPrimaryLayout: function(s) {
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			var primary = c in this.convTableForward;
			if(primary ^ c in this.convTableBackward)
				return primary;
		}
		return false;
	},
	switchKeybLayout: function(s, convTable) {
		var res = "";
		for(var i = 0, l = s.length; i < l; ++i) {
			var c = s.charAt(i);
			res += c in convTable ? convTable[c] : c;
		}
		return res;
	},
	getFocusedElement: function() {
		return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
			.getFocusedElementForWindow(content, true, {});
	},
	switchSelKeybLayout: function(_subCall, _focusFixed) {
		var fe = this.getFocusedElement(_subCall, _focusFixed);
		if(!fe)
			return;
		if(HTMLInputElement.isInstance(fe) || HTMLTextAreaElement.isInstance(fe)) {
			var ta = fe;
			try {
				var val = ta.value;
				var sel = val.substring(ta.selectionStart, ta.selectionEnd);
			}
			catch(e) { // Non-text HTMLInputElement
				return;
			}
			if(!sel && val && this.noSelBehavior && !_subCall) {
				if(this.noSelBehavior == 1) {
					ta.selectionStart = 0;
					ta.selectionEnd = val.length;
					sel = val;
				}
				else {
					this.handleNoSel(ta);
					return;
				}
			}
			if(!sel)
				return;
			var res = this.switchKeybLayout(
				sel,
				this.inPrimaryLayout(sel)
					? this.convTableForward
					: this.convTableBackward
			);
			if(res != sel)
				this.insertText(ta, res);
		}
		else if(fe.contentEditable == "true") {
			var doc = fe.ownerDocument;

			var docURI = doc.documentURI;
			if(
				docURI.substr(0, 5) == "data:"
				&& docURI.indexOf("chrome://browser/skin/devtools/") != -1
			) {
				//~ todo: seems like we only can use paste from clipboard here...
				return;
			}

			var sel = doc.defaultView.getSelection();
			var rng = sel.rangeCount && sel.getRangeAt(0);
			var tmpNode;
			if(!rng || rng.collapsed) {
				if(!this.noSelBehavior || _subCall)
					return;
				if(this.noSelBehavior == 1) {
					var r = doc.createRange();
					r.selectNodeContents(fe);
					sel.removeAllRanges();
					sel.addRange(r);
					tmpNode = fe.cloneNode(true);
				}
				else {
					this.handleNoSel(fe);
					return;
				}
			}
			else {
				tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
				tmpNode.appendChild(rng.cloneContents());
			}

			var orig = tmpNode.innerHTML;
			var convTable = this.inPrimaryLayout(tmpNode.textContent)
				? this.convTableForward
				: this.convTableBackward;

			var _this = this;
			var parseChildNodes = function(node) {
				if(Element.isInstance(node)) {
					var childNodes = node.childNodes;
					for(var i = childNodes.length - 1; i >= 0; --i)
						parseChildNodes(childNodes[i]);
				}
				else if(node.nodeType == node.TEXT_NODE) {
					var text = node.nodeValue;
					var newText = _this.switchKeybLayout(node.nodeValue, convTable);
					if(newText != text)
						node.parentNode.replaceChild(doc.createTextNode(newText), node);
				}
			}
			parseChildNodes(tmpNode);

			var res = tmpNode.innerHTML;
			if(res != orig)
				doc.execCommand("insertHTML", false, res);
		}
	},
	handleNoSel: function(node) {
		this.select(node);
		this.switchSelKeybLayout(true);
	},
	select: function(node) {
//		var e = this.noSelBehavior;
//		if(!e || typeof e != "object")
//			return;
//		var evt = node.ownerDocument.createEvent("KeyboardEvent");
//		evt.initKeyEvent(
//			"keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
//			e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
//			e.keyCode, e.charCode
//		);
//		node.dispatchEvent(evt);

	   var beh = this.noSelBehavior;
	   beh && node.ownerGlobal.docShell.doCommand("cmd_select" + beh);
	},
	beh2cmd: { // Ctrl_Shift_VK
		false_true_36: "cmd_selectLinePrevious", // Shift+Home
	},
	insertText: function(ta, text) {
		//var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
		var editor = ta.editor
			.QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);
		if(editor.flags & editor.eEditorReadonlyMask)
			return;

		var sTop = ta.scrollTop;
		var sHeight = ta.scrollHeight;
		var sLeft = ta.scrollLeft;
		// var sWidth = ta.scrollWidth;

		if(text)
			editor.insertText(text);
		else
			editor.deleteSelection(0, 0);

		ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
		ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
	}
}`);

Огромнейшее СПАСИБО!!!!!!!

Dumby пишет

Оказывается, что если вписан sub, код прогоняет через [for in] объект в который вписан sub.
То есть, и для ключей, упомянутых в этом регулярном выражении тоже создаётся (ненужный) элемент (как минимум <menuitem>)

Спасибо! Я делал так же: (обработать только объект), но результат разный:
for (key in o) { if (typeof o[key] != "object") continue; // Dumby
if (typeof o == "object") for (key in o){ // Dobrov


Dumby пишет

Всё потому, что у строки есть sub. Вот проверь с консоли: var {sub} = "bla-bla"; alert(sub);
Это будет — String.prototype.sub().

Исправил свой код менюшки и убрал имя переменной sub.
Но тогда js-код для var {sub} работает нелогично, ведь var {lab} = "bla-bla"; alert(lab); (и другие имена) возвращают «undefined» !

UserChromeFiles обнова https://github.com/VitaliyVstyle/Vitali … hromeFiles
На старых версиях [firefox] работать не будет т. к.
ESM, CSS3, Fluent (для локализации, пока добавленно три языка en-US, uk, ru)

Vitaliy V.
Спасибо! А одним архивом можно сделать? Или я не нашёл...:(

xrun1 пишет

А одним архивом можно сделать? Или я не нашёл...:(

А он есть ;)
https://github.com/VitaliyVstyle/VitaliyVstyle.github.io/blob/main/UserChromeFiles/UserChromeFiles.zip

Vitaliy V.. ;) Рады вас видеть. Спасибо за новую сборку.

Vitaliy V. пишет

На старых версиях  работать не будет

Vitaliy V.
Очень рады вас видеть! Огромное спасибо за новую версию.
Вопрос. Какая минимальная версия [firefox] будет работать с новой сборкой?

Dumby вот эта кнопка отвалиться когда выпилят jsm?если да то поправте ее пожалуйста

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

Выделить код

Код:

(async url => {

	var path = "D:\\Portateble Program\\Mozilla Firefox\\Profiles\\opera-proxy.windows-386.lnk";

	var icons = [
		'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsSAAALEgHS3X78AAAA/klEQVR42u2b0Q2DMAxE03bG7tN5ukRHcwUqvwjR2D7jd1IkvsD3EinEscdACKXI7DY+w9axPF9Wm8mzo6XpsjC8jMuDiDIuByLLuAQIFfMpENTMR0F4vu0ha/431hjbzXzISqhi3gVCNfNTIVQ1PwVCdfN/Q2gNIDMQCQgKSzAtBrWtKDwexZ+RsJiU/8lDYlM/kEgDeNndHcDyDTcAri8XWQW7k1QpK+MSKwAAUCs7CwAAAAAAAGAbBAAApsXa/jDU/jhMQoSUGElR0uJcjHA1xuUoACiQoESGIqnSELxEoaT6SiiTkKBinL4BOkfoHaKD7Mqmj6hN4yRC0voCm25elE1UjV4AAAAASUVORK5CYII=',
		'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsSAAALEgHS3X78AAABAElEQVR42u2bQQ4CMQhFq57RW/QQnsdLol3M1hgt8BkeSZNZzfBfSaZQGAPDsBQzu8w5ba31fFqdh8hfV0vRZWF4CZcHESVcDkSWcAkQKuJTIKiJD4Nwf9pNVfyxlo/tdj4kEqqId4FQTfxWCFXFb4FQXfzfEFoDyHREAoJCCKb5oPYrCvdH8TAS5pPymTzEN/WERBrAeNjVPSN7f8MPgOvLNaLg4yZVqsq4+AoAANSqzgIAAAAAAAD4DQIAAPt87Z4MkQ5TEKEkRlGUsjgXI1yNcTkKABokaJGhSaoyBLdzePtGSfVIKFOQoGOcuQEmR5gdYoLszKK/si6DkximbS8IilLcn+DA8wAAAABJRU5ErkJggg=='
	];
	var labels = [
		"Active",
		"Not Active"
	];
	var tooltips = [
		"Active",
		"Not Active"
	];

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

	exp.PageActions.addAction(new exp.PageActions.Action({
		title: labels[1],
		iconURL: icons[1],
		pinnedToUrlbar: true,
		id: "ucf-opera-proxy",
		onPlacedInUrlbar(node) {
			var pref = Services.prefs.getIntPref(type) == 1, proc, active;
			var upd = () => {
				var state = pref && Boolean(proc);
				if (state == active) return;

				var ind = +!(active = state);
				this.setIconURL(icons[ind]);
				this.setTitle(labels[ind]);
				this.setTooltip(tooltips[ind]);
			}
			this["nsPref:changed"] = () => upd(
				pref = Services.prefs.getIntPref(type) == 1
			);
			this["process-finished"] = this["process-failed"] = () => upd(proc = null);

			this["quit-application-granted"] = t => {
				Services.obs.removeObserver(this, t);
				Services.prefs.removeObserver(type, this);
				active && Services.prefs.setIntPref(type, 0);
				proc?.kill();
			}
			this.observe = (s, topic) => this[topic](topic);
			Services.prefs.addObserver(type, this);
			Services.obs.addObserver(this, "quit-application-granted");

			var {id} = node;
			var style = `#${id} {display: flex !important;}\n` +
				`@media (max-width: 680px) {#${id} {visibility: collapse !important;}}`;

			(this.onPlacedInUrlbar = this._onPlacedInUrlbar = node => {
				var sheet = new node.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				node.ownerDocument.adoptedStyleSheets.push(sheet);
			})(node);

			var run = () => {
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				(run = () => {
					proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
					//proc.startHidden = true;
					try {proc.init(file); proc.runwAsync([], 0, this);}
					catch {proc = null;}
					upd();
				})();
			}

			var timeout = 3000;

			var tid, oldState, reload = (gb, tab, state) => {
				tid = null;
				active != state && gb.reloadTab(tab);
			}
			this._onCommand = e => {
				tid ? clearTimeout(tid) : oldState = active;
				var gb = e.view.gBrowser;
				tid = setTimeout(reload, timeout, gb, gb.selectedTab, oldState);
				if (active) Services.prefs.setIntPref(type, 0),  proc.kill();
				else pref || Services.prefs.setIntPref(type, 1), proc || run();
			}
		}
	}));
})("resource:///modules/PageActions.");

unter_officer пишет

Вопрос. Какая минимальная версия [firefox] будет работать с новой сборкой?

Полноценно с [firefox] 117, но можно в [firefox] 115 запустить, не будут работать некоторые стили (в том числе настройках)
нет поддержки вложения селекторов https://developer.mozilla.org/en-US/doc … SS_nesting
В общем не сложно добавить поддержку со 115 версии только зачем

Vitaliy V. пишет

Полноценно с [firefox] 117, но можно в [firefox] 115 запустить, не будут работать некоторые стили (в том числе настройках)

Спасибо за ответ. Я уже успел интереса ради немного потестировать на 115 ESR.
В целом работает нормально, но проблемки со стилями, как вы и сказали, присутствуют.
Глубже копать не стал. Откатился на старую версию UCF.

unter_officer пишет

115 ESR

Хорошо т. к. 128 ESR выйдет в июле судя по графику, сделал патч для 115 ESR,
заменить файлы css из этого архива https://github.com/VitaliyVstyle/Vitali … 15_ESR.zip

Vitaliy V. пишет

Хорошо т. к. 128 ESR выйдет в июле судя по графику, сделал патч для 115 ESR,
заменить файлы css из этого архива https://github.com/VitaliyVstyle/Vitali … 15_ESR.zip

Vitaliy V., большое спасибо за патч. Теперь на 115 ESR всё нормально.
Что касается 128 ESR, то для этого надо переходить на Win10+, а я пока этого не планирую.

egorsemenov06 пишет

вот эта кнопка отвалиться когда выпилят jsm?

В этом смысле — вроде не должна.


Vitaliy V.
Насчёт UcfPrefs.mjs. Сперва по Fluent'у.
Лис 124 ru. Поставил langpack'и uk и de.


STR: Включаю de, рестарт. Открываю «Настройки UserChromeFiles»,
окно локализировано как ru, это нормально, сборка ведь ru, а de отсутствует.
Теперь переключаюсь на uk.


ER: Локализация в окне меняется на uk, ведь uk есть.
AR: Ничего не происходит, локализация остаётся ru.


Да, я понимаю, что STR довольно экзотичен.
Но, может быть, заменить appLocalesAsBCP47 на availableLocales


И по коду.
Первая строка var global = Cu.getGlobalForObject({}); Разве это не globalThis?
И, UcfPrefs.global да, полезно пробросить, но почему геттер, а не просто значение?


UcfPrefs.defineGlobalGetters есть, но в само́м UCF нигде не используется.


UcfPrefs.defineLazyGlobalGetters есть, но нужно ли это?
Вот, добавил в config.js, в самое начало, ещё до антиподписячего (мне можно),
чтобы не было никаких сомнений, что там что-то появится только когда-то позже.

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

Выделить код

Код:

//
(globalProperties => {
	var result = [];
	var global = Cu.getGlobalForObject(Cu);

	var max = Math.max(...globalProperties.map(prop => prop.length)) + 2;
	
	for(var name of globalProperties) {
		var desc = global.Object.getOwnPropertyDescriptor(global, name);
		if (desc) {
			for(var key in desc) {
				var type = typeof desc[key];
				if (type != "boolean") desc[key] = type;
			}
		}
		result.push(name.padEnd(max) + JSON.stringify(desc));
	}

	Services.obs.addObserver(async function test(win, topic) {
		Services.obs.removeObserver(test, topic);
		await win.gBrowserInit.idleTasksFinishedPromise;

		var text = encodeURIComponent(result.join("\n"));
		var url = "data:text/plain;charset=utf-8," + text;

		var gb = win.gBrowser;
		gb.selectedTab = gb.addTrustedTab(url);

	}, "browser-delayed-startup-finished");
})([
	"atob",
	"btoa",
	"Blob",
	"CSS",
	"CSSRule",
	"DOMParser",
	"Event",
	"File",
	"FileReader",
	"InspectorUtils",
	"URL",
	"XMLHttpRequest",
	"fetch",
	"L10nFileSource",
	"L10nRegistry",
	"Localization",

	// test non-existens
	"absent",
	"alert",

	// candidates
	"TextEncoder",
	"TextDecoder",
]);


Код собирает дескрипторы свойств и визуализирует во вкладке
первого окна браузера (не сразу, нужно чуть подождать).


Насколько я вижу, все свойства уже есть, и у всех сразу value (не get).
Таким образом, lazy-фикация представляется излишней,
можно просто сразу пробросить ссылки, и все дела.


Кстати, почему только эти.
Мне вот за Text{En, De}coder особенно обидно было ещё в прошлом UCF.

Dumby пишет
egorsemenov06 пишет

вот эта кнопка отвалиться когда выпилят jsm?

В этом смысле — вроде не должна.

Спасибо за ответ!!!

Vitaliy V. пишет

UserChromeFiles обнова https://github.com/VitaliyVstyle/Vitali … hromeFiles

Сделал всё по инструкции ...никаких изменений. Видимо чего-то не понимаю. 125.0a1 (2024-03-03) (64-разрядный)

grom17 пишет

Сделал всё по инструкции ...никаких изменений.

В v124.0beta6 работает. Кэш автозапуска очищал? Сразу должны быть видны три кнопки доп. тулбаров и доступны Промежуток и Разделитель. Зайди в настройки UCF и в нижней части проставь все птички, по умолчанию они все сняты. Не все скрипты, работающие в Aris-t2, работают в UCF.

04-03-2024 14:17:46
А вот кнопку  "Open settings" button я так и не смог отыскать, она, видимо, открывает настройки UCF в сепаратном окне.

Dumby пишет

AR: Ничего не происходит, локализация остаётся ru.

Да, я понимаю, что STR довольно экзотичен.
Но, может быть, заменить appLocalesAsBCP47 на availableLocales

Т. е. не происходит без перезагрузки ? Так и не было цели чтобы локали изменялись без перезагрузки, на панелях и кнопках UCF вообще ничего не происходит там просто при старте браузера один раз выбирается наиболее подходящая локаль и все, DOMLocalization не следит за ними.
А appLocalesAsBCP47 вполне достаточно для этого

скрытый текст
  /**
   * Returns a list of locales that the application should be localized to.
   *
   * The result is a ordered list of valid locale IDs and it should be
   * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
   *
   * This API always returns at least one locale.
   *
   * When retrieving the locales for language negotiation and matching
   * to language resources, use the language tag form.
   * When retrieving the locales for Intl API or ICU locale settings,
   * use the BCP47 form.
   *
   * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
   */
  readonly attribute Array<ACString> appLocalesAsLangTags;
  readonly attribute Array<ACString> appLocalesAsBCP47;

А availableLocales возвращает неупорядочный список (хоть это и не важно в данном случае) всех доступных локалей,
ну и как бы он не нужен весь список если локаль только один раз при старте инициализируется
скрытый текст
  /**
   * Returns a list of locales that the app can be localized to.
   *
   * The result is an unordered list of locale IDs which should be
   * used as a availableLocales input list for language negotiation.
   *
   * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
   */
  attribute Array<ACString> availableLocales;

Dumby пишет

Первая строка var global = Cu.getGlobalForObject({}); Разве это не globalThis?
И, UcfPrefs.global да, полезно пробросить, но почему геттер, а не просто значение?

В XPCOMUtils.sys.mjs так и записано
и да Cu.getGlobalForObject({}) == globalThis
В чем проблемма геттера?

Dumby пишет

UcfPrefs.defineGlobalGetters есть, но в само́м UCF нигде не используется.

Это я хотел использовать но потом не потребовалось, но оставил может пригодится для использования в пользовательском коде

Dumby пишет

Таким образом, lazy-фикация представляется излишней,
можно просто сразу пробросить ссылки, и все дела.

Возможно излишняя но мне нравится lazy, не используешь не загружается лишнее

Dumby пишет

Кстати, почему только эти.
Мне вот за Text{En, De}coder особенно обидно было ещё в прошлом UCF.

Хм что-то не вижу где в прошлом UCF это было? Но ок добавлю

04-03-2024 19:28:26

fuchsfan пишет

А вот кнопку  "Open settings" button я так и не смог отыскать

Значек шестеренка, в зависимости от локали может отличаться, например для en-US так
label кнопки:
Settings UserChromeFiles
tooltip:
Left-click: Open UserChromeFiles settings in window
Midle-click: Open about:config
Right-click: Open UserChromeFiles settings in the tab

И вообще она по умолчанию находится на дополнительной панели

Vitaliy V.
Мне кажется лишним код с разными переводами. Может оставить просто английский, кому надо, сами переведут в коде, вручную. Пытался выпилить не нужное, как обычно:
                "ucf-open-about-config-button", - пользуюсь через ucf_hookClicks.js
                "ucf-additional-vertical-spring", - special_widgets(из-за space, separator)
                "ucf-additional-top-spring",
                "ucf-additional-bottom-spring",
                "ucf-restart-app", - через ucf_hookClicks.js + hotkey
                "ucf-view-history-sidebar-button", - через ucf_hookClicks.js + hotkey
                "ucf-view-bookmarks-sidebar-button", - тоже самое
                "ucf-open-directories-button", - через ucf_hookClicks.js + hotkey
И их в переводе не выпилишь(плохо разбираюсь как), мне по сути нужно только вертикальная, изредка верхняя. А тут какие-то переводы еще добавились) Итак путаюсь в коде, наверняка выпиливая костыльно и не все. Многие пользуются, тем же Aris-t2, на соседнем форуме. Так он без переводов, как и многие другие альтернативы. Лишние переводы, как по мне, лишнее. На костыль похоже)


Это лишь мой каприз, может другим нужны переводы. Мне и одного вполне хватает, если код от этого будет меньше и выпиливать будет легче. Да, можно просто под коврик спрятать все эти потроха, но они и там в глаза бросаются. Вместо одного spring, висят аж 4) Можно не прятать, оставляя на местах. Но все равно, не то. Зачем их именовать по-разному, чтобы не путаться? В коде, их тоже так и хочется выпиливать, возможно это лишь мой причуды и не критично.


(В общем, пытался их убрать в "async formatMessages(){}", файл UcfPrefs.mjs, и у меня пропали пункты выбора, раскрыть-спрятать вертикальную панель и другие. Пришлось их вернуть, а так, вроде везде выпилил).

Vitaliy V. пишет

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

OK, принято.

В XPCOMUtils.sys.mjs так и записано

Точно. Запись довольно старая, старше чем globalThis.

В чем проблемма геттера?

Проблем нет никаких.
Геттер это функция. Обратился к свойству — считай вызвал функцию.
А функция всегда возвращает одно и то же.

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

OK, принято.

Возможно излишняя но мне нравится lazy, не используешь не загружается лишнее

Наоборот.
В цикле всё равно идёт перебор свойств, где для каждого из них
низачем создаётся своя отдельная функция и назначается как геттер.


Когда случится обращение к этому свойству,
геттер-функция низачем проверит, есть ли такое свойство в SystemGlobal,
ведь оно есть и было, перезапишет как свойство сандбокса, и вернёт значение.


Создание множества этих функций, которые проверяют уже заранее известное,
и возвращающих нечто заведомо существующее — это и есть «лишнее».

Хм что-то не вижу где в прошлом UCF это было?

Да-да, не было. Именно поэтому мне и было странно чем они впали в немилость.


Вообще-то да, это проблема, как выбрать то, что следует пробросить.
В смысле как определить наиболее популярное.


Гобальные свойства проброшены не все, но их хотя бы немного,
а если брать и что-то вроде L10nRegistry, то это и вовсе не гобальное свойство,
но нечто DOM-idl'ское, а такого добра в SystemGlobal целый вагон.


Например, IOUtils и PathUtils кажутся вполне достойными кандидатами.

b0ttle пишет

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

Предлагаете переводить юзерам в коде потому что вам сложно разобраться с кодом а им типа легко.
Не не вариант с локалью проще добавить новый язык просто создать в папке locales папку с названием идентификатора нужного языка и в ней файлы .ftl с переводом.

b0ttle пишет

Мне и одного вполне хватает, если код от этого будет меньше и выпиливать будет легче. Да, можно просто под коврик спрятать все эти потроха, но они и там в глаза бросаются. Вместо одного spring, висят аж 4

Первое пробовали отключить в настройках ненужные панели? Вместе с панелями отключаются и соответствующие кнопки, интервалы
И в коде лучше не выпиливать если не ориентируетесь, можно добавить данный код в custom_script.js или в другой подключенный в scriptsbackground
и эти кнопоки, интервалы будут удалятся до того как попасть на панели или хранилище

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

Выделить код

Код:

(ids => ({
    init() {
        CustomizableUI.addListener(this);
        Services.obs.addObserver(this, "browser-delayed-startup-finished");
    },
    observe() {
        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
        CustomizableUI.removeListener(this);
    },
    onWidgetCreated(aWidgetId) {
        if (ids.has(aWidgetId))
            CustomizableUI.destroyWidget(aWidgetId);
    },
}).init())(new Set([
    "ucf-open-about-config-button",
    "ucf-additional-vertical-spring",
    "ucf-additional-top-spring",
    "ucf-additional-bottom-spring",
    "ucf-restart-app",
    "ucf-view-history-sidebar-button",
    "ucf-view-bookmarks-sidebar-button",
    "ucf-open-directories-button",
]));

Dumby пишет

Наоборот.
В цикле всё равно идёт перебор свойств, где для каждого из них
низачем создаётся своя отдельная функция и назначается как геттер.

Когда случится обращение к этому свойству,
геттер-функция низачем проверит, есть ли такое свойство в SystemGlobal,
ведь оно есть и было, перезапишет как свойство сандбокса, и вернёт значение.

Да это понятно но следует добавить что это происходит только один раз при первом вызове

Dumby пишет

Создание множества этих функций, которые проверяют уже заранее известное,
и возвращающих нечто заведомо существующее — это и есть «лишнее».

Так а где гарантии что это сейчас заведомо существующее не станет потом просто существующим или вообще не существующим.
А лишнее для меня то что не требуется для работы основных функций UserChromeFiles

Vitaliy V. Спасибо. До меня потом дошло, что можно просто оставить все как есть. Если не пытаться выпилить сразу, то потом может и привыкнуть можно. Про настройки, что-то не подумал. Код попробую. Чисто мой закидоны)

Vitaliy V. пишет

добавить что это происходит только один раз при первом вызове

Да, разумеется. Но могло бы не происходить вовсе.

а где гарантии что это сейчас заведомо существующее не станет потом просто существующим или вообще не существующим

Ну так можно вообще обо всём сказать.
Хорошо, пусть сочтётся как осторожность.


Кстати, Console.sys.mjs же собираются выпилить,
неплохо бы что-то предусмотреть на такой случай.


Ещё, при переключении тулбаров посредством контекстного меню,
BrowserUsageTelemetry мусорит в консоль записью "widgetId is undefined".


Когда был проект встроить в UCF дебаггер,
то я просто добавлял id'шники в BROWSER_UI_CONTAINER_IDS


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

Dumby пишет

Кстати, Console.sys.mjs же собираются выпилить

Да уж 6 лет выпиливали Console.jsm не выпилили, выпилили jsm теперь Console.sys.mjs выпиливают

Dumby пишет

Когда был проект встроить в UCF дебаггер

Что за проект? Нашел только настройки
extensions.user_chrome_files.debug
extensions.user_chrome_files.expert

05-03-2024 18:32:38

Dumby пишет

BrowserUsageTelemetry мусорит

Её вообще выпиливать нужно а не id'шники в BROWSER_UI_CONTAINER_IDS добавлять

Vitaliy V. пишет

Что за проект?

Поскольку никто не мог знать, что нарисуется UCF 2024 от самого́ Автора,
а желающих обезопасить педыдущий UCF от последствий завершения JSM-геноцида
как-то не наблюдалось, то чувствовалось, что отдуваться придётся мне.


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


Собственно, ничего особо сделать не успел,
пока там подформатировал всякий xml-like стафф в более человеко-читаемый вид,
пока заменил все четырёх-пробелья на благородную tab'уляцию,
случилось то, что случилось, и проект, соответственно, был мгновенно остановлен.


И это, несомненно, к лучшему.


Так вот, в config.js предполагалось оставить только самый минимум,
определение nsIFile'а с путём %профиль%/chrome/user_chrome_files/init.js
проверка, что он существует, и является файлом,
и загрузка этого init.js в объект {file: этот_файл};


А сырой набросок init.js был такой, скину какой был.
Не годен ни на что, но лишь составить представление о чём речь.


Дебаггер, собственно, это некая цитата из ранее добавленного в CB,
ну и в антиподписячем подобный есть, но без наворотов.

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

Выделить код

Код:

(url => {
	(file = file.parent).append("user_chrome.manifest");
	if (!file.exists() || !file.isFile()) return;

	Components.manager.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(file);

	var sysp = Cu.getObjectPrincipal(this), SB = opts => Cu.Sandbox(sysp, opts);
	var sb = SB({sandboxName: "UserChromeFiles", wantGlobalProperties: ["ChromeUtils"]});
	Services.scriptloader.loadSubScript(url, sb);

	var unload = ["unload", e => e.target.ownerGlobal.windowRoot.removeEventListener(...ins), {once: true}];

	var ins = ["DOMDocElementInserted", e => {
		var win = e.target.ownerGlobal;
		if (win.windowRoot.ownerGlobal == win) {
			if (win.location == "about:blank") return;
			win.addEventListener(...unload);
		}
		win.isChromeWindow && sb.user_chrome.initWindow(win);
	}];

	Services.obs.addObserver(win => win.windowRoot.addEventListener(...ins), "domwindowopened");


	var re = Cu.reportError;
	var sandbox = SB({freshCompartment: true});
	Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
	var dbg = sb.dbg = new sandbox.Debugger();

	var g = Cu.getGlobalForObject(Cu);
	var gref = dbg.gref = dbg.makeGlobalObjectReference(g);

	var envRef = function(name) {
		var val = this.find(name).getVariable(name);
		return val.unsafeDereference?.() || val;
	}
	dbg.ref = (arg, func, glob) => {
		var go = glob === undefined ? g : glob || Cu.getGlobalForObject(func);
		var has = dbg.hasDebuggee(go);
		has || dbg.addDebuggee(go);
		try {
			var ref = go == g ? gref : dbg.makeGlobalObjectReference(go);
			var env = ref.makeDebuggeeValue(func).environment;

			var cn = arg.constructor.name;
			if (cn == "Object") for(var name in arg) try {
				env.find(name).setVariable(name, ref.makeDebuggeeValue(arg[name]));
			} catch(err) {re(err);}

			else return cn == "Array" ? arg.map(envRef, env) : envRef.call(env, arg);
		}
		catch(ex) {re(ex);} finally {has || dbg.removeDebuggee(go);}
	}

	// Meds. Prevent "widgetId is undefined" console spam
	// from BrowserUsageTelemetry.sys.mjs when toggle ucf-toolbars.
	if (sb.UcfPrefs.vertical_top_bottom_bar_enable) try {
		var exp = ChromeUtils.importESModule("resource:///modules/BrowserUsageTelemetry.sys.mjs");
		var ids = dbg.ref("BROWSER_UI_CONTAINER_IDS", exp.BrowserUsageTelemetry.init);
		ids && ["top", "vertical", "bottom"].forEach(pos => ids[`ucf-additional-${pos}-bar`] = "");
	} catch(ex) {re(ex);}

})("chrome://user_chrome_files/content/user_chrome.js");


И да, я согласен, что BrowserUsageTelemetry следует душить как угодно.
Но, если абстрагироваться от нашей к нему неприязни,
и рассмотреть просто как задачу: «Как получить ссылу на BROWSER_UI_CONTAINER_IDS?».


Пока нынешний Ку.import() ещё с нами можно использовать его.
Вообще, метод, конечно, волшебный. Возвращает некий прокси,
куда проброшен весь стафф — var, let, const, function, class, всё вообще.
Это arai подогнал какую-то .cpp-магию.


Но метод сгинет вместе с JSM.
И как тогда получить ссылку? Вроде никак. Только дебаггером.

grom17 пишет

Сделал всё по инструкции ...никаких изменений. Видимо чего-то не понимаю. 125.0a1 (2024-03-03) (64-разрядный)

В [firefox] 123 тоже не работает ?

Add, если не работает, тогда опишите что сделали, желательно по пунктам!


Add, ладно.... user_chrome_files. С остальным надеюсь сами разберётесь.

Dumby пишет

Я уже упоминал, что, увы, для некоторых задач, необходим дебаггер

Ну именно для BrowserUsageTelemetry я бы не стал добавлять его, но если нужно для некоего кода в custom_script.js  что если добавить этот модуль
он делает часть работы ?
ChromeUtils.defineESModuleGetters(scope, {
    ...
    addDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
    addSandboxedDebuggerToGlobal: "resource://gre/modules/jsdebugger.sys.mjs",
});

Vitaliy V. пишет

именно для BrowserUsageTelemetry я бы не стал добавлять его, но если нужно для некоего кода

Не-не, конечно же, в первую очередь, для пользовательского кода.


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


Мягонькое такое воздействие, никакого насилия,
добавляем id'шники как пустые строки, и это дело умолкает.


Если будет принято решение заткнуть BrowserUsageTelemetry
как-то иначе, или не делать с этим ничего, тогда дебаггер мог бы быть lazy getter'ом.


Наверно лучше как свойство UcfPrefs. Он определён как в сандбоксе, так и в окнах браузера.
В других окнах UcfPrefs можно просто импортировать, если вдруг понадобится.


Вот, поискал, нашёл, например, такой код.
Здесь TransactionsHistory не вытащишь никак, когда исчезнет Cu.import()
А с дебаггером просто пишем UcfPrefs.dbg.ref("lazy", PlacesTransactions.undo).TransactionsHistory

что если добавить этот модуль
он делает часть работы ?

Да, указанные методы этого модуля делают часть работы.
Но только совсем небольшую, только самое начало.
Плюс, они делают нечто совсем, в данном случае ненужное, вызывают initPromiseDebugging();
Таким образом, добавление этого модуля не представляется полезным.

Dumby
https://github.com/VitaliyVstyle/Vitali … fs.mjs#L46
Проверил сработало на BROWSER_UI_CONTAINER_IDS и код по ссылке

Но чтобы не мусорила BrowserUsageTelemetry просто переопределил
функцию onViewToolbarCommand https://github.com/VitaliyVstyle/Vitali … ar.js#L143

Также убрал Lazy, побавил TextEncoder TextDecoder https://github.com/VitaliyVstyle/Vitali … me.js#L179

О, спасибо!
Тогда код «Восстановить удалённое», будет, наверно, таким

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

Выделить код

Код:

(async sep => {
	if (!sep) return;

	var key = "hasRemoveTransaction";
	var raws = UcfPrefs.dbg.ref("lazy", PlacesTransactions.undo).TransactionsHistory.proxifiedToRaw;
	raws[key] ??= entry => {
		for(var transaction of entry) {
			if (raws.get(transaction) instanceof PlacesTransactions.Remove)
				return true;
		}
	}
	var menuitem = document.createXULElement("menuitem");
	for(var args of Object.entries({
		closemenu: "single",
		class: "menuitem-iconic",
		id: "placesCmd_undoRemove",
		label: "Восстановить удалённое",
		oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAEd0lEQVRIx53Ve2yW5RkG8N/7ft8Hxa/9CgjSSDnMA4xVTUMQWzadhzhnjSCC0y0jSjom+JdGp844ZhSjHLJsowaNZoCCHOWglXGoQbo2fBW3obDNUBfXKpsUVhxn6OHdH8+31GXGmL3/3M913ffzPNed93qeBxvmI8umCNjyPFEDSRJw40qKa9jyHSbdzY77Ar+9FTvZ+nHAb9T7P76YcwlOktlBdJia2SSthfRvaR/Mync5tp85Q2mbEHJ/X0bqRTpOBJyehog3C3j1HV9RQP91xG9T8z2SofyphMELWH4zW5s40s7kQ/T0cnYfXfPD1OgM0TlGzSN1DZkzxG1EdURjaa9HCfddEupfuSkIfHXJ5wVENA8Iw9QcRnbzXj31++g8w+WVZHMhXzaKhu2MGMtd36f5CTrn8WAJyf001KCYpA6dDBtLlOfoYuI6kgp6XuLsbhQRfw39ovDPx7/JgYS1b3NkCxcvoPgTevIULyFVTm4jyinp4uRJiirYt5BMLc15kvE0lnCsgZaE2wdx6LVCo9/ghQn4LhdFxEOonELv8ogVV9Iyjd9fx8T1FE2nq5XSQWQuJbsmrJH9JclMsldhFsW3EU0iuxsb6f9zTA3dHj1J1yPsKaPrIOtnkn6KDY2cvYF/vkfmINGuiPdnUD6Pd/7Mur/x0RCqXqTfj+m5glyO1B8ovR1pijeil6KvBw8V1QaB/QYWzNgSYu+TmMm5GVhKT47OBWR28kgnvxvKS/OjvuMGuTSr72FtBR/WctNe4l+H3GUt7DxB2TKe+CmnF5IZRzSZzFmS4wy9n2QGY2eR3Mx193DqAao+4vh04lZS2/EkQyqo7cWux4Kjwf4gKF3N5hEs+QsLlwVuVRPLP2FeTSh95hZSpSx6OOAkwWCWLSEqYWVz4JtLqd3GiuX0/oS6XKjd/BQbF6SRJynij4eJKpl7WSg8cCdzxjHjH3QuIsaorQx7NCx8QQdxCYM2kzxL423BbGN6SP+I6tmcO86hahqu4MA2To3mX+eTxbA2er6VJjcA/Yl2kOzlrutJxrDraaI1XH09j3fQVk7pcG5sCgIOttLVzejpmMPrnxJvYnIZXelwxGHTCOaX0PorsmUMOC/wpeNofg4fjg9EW3+k6CzcYJ99M7h6fSfxFFJrMZzFm0I+fzXxlTQ1EHf3eSlJyI3mlYGs/yEPx4F7/NkQ11zEymFhnDoY9RnwcHmIZ34Q1CcTkBD9FSM58gu8SmUHbmVuJbYRt2AiE/cwZT/tG3h+Gh29lH3A+VPZ+zMq1rHwcq6tY1U31ReQXPgFt3M+/9+xJRvM9f5S3NnX6btLqfqYt1p44xQrFnH0aX6zmBXIj+Hae0N95qG+ea/PJjrB7tV47csE7CzEJqKXyTcW8CquaibfwTOX0FjPLSfYk+G8XWyb+rlTUcndVUQjWXOK+FiBn/QfQen/FVBVVdhoc4FIkczqM5WBvPMWp4fQMpzFHeEN2NCFCcyNSH2b1AiSl3nhVnrnkjpNkqP9jrBvdzX8G2rIlxvMK3a7AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==",
	}))
		menuitem.setAttribute(...args);

	var desc = Object.getOwnPropertyDescriptor(XULElement.prototype, "hidden");
	var {set} = desc;
	desc.set = () => {
		var entry = PlacesTransactions.topUndoEntry;
		set.call(menuitem, !entry || !raws[key](entry));
	}
	Object.defineProperty(menuitem, "disabled", {});
	Object.defineProperty(menuitem, "hidden", desc);
	sep.before(menuitem);
})(document.getElementById("placesContext_deleteSeparator"));


Так же, вижу улучшение для UcfPrefs.global
Всё ещё функция, но лишь до первого обращения,
затем просто значение, как и могло бы быть сразу, но всяко лучше, чем было.


Кстати, и в геттере UcfPrefs.dbg
строка var g = Cu.getGlobalForObject(Cu); — это тоже globalThis


Это глобальный объект всех модулей.
Такой единственный, такой один на весь процесс. Он.

Но чтобы не мусорила BrowserUsageTelemetry просто переопределил
функцию onViewToolbarCommand https://github.com/VitaliyVstyle/Vitali … ar.js#L143

Да, вижу, теперь в консоли тишина.

onViewToolbarCommand, было - стало

Выделить код

Код:

data:image/png;base64,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

побавил TextEncoder TextDecoder https://github.com/VitaliyVstyle/Vitali … me.js#L179

Благодарю, надеюсь достойны.
И, я смотрю, console изменилась. Это тоже хорошо.

Также убрал Lazy

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

Dumby пишет

сразу скопировать
их с глобального объекта модулей

Это типа оператором присваивания ?
scope.TextEncoder = UcfPrefs.global.TextEncoder
или же Object.assign() использовать
Можно ещё в виде прототипа все унаследовать
sandboxPrototype: UcfPrefs.global,
но тогда нужно будет опять загружать CustomizableUI и UcfPrefs

Vitaliy V. пишет

Это типа оператором присваивания ?

Да-да, именно им.
Они всё равно перебираются в цикле, вот прямо там и присвоить.

Можно ещё в виде прототипа все унаследовать
sandboxPrototype: UcfPrefs.global

Ууу, мысль, несомненно, интересная.
Это был бы эпический сдвиг само́й парадигмы.
Но тогда, да, действительно, мы получим вообще всё и сразу.

тогда нужно будет опять загружать CustomizableUI и UcfPrefs

Не загружать, а сослаться. Они уже загружены.

Dumby пишет

Но тогда, да, действительно, мы получим вообще всё и сразу

Что ж пусть так и будет. Модули только нужно подгружать по необходимости
https://github.com/VitaliyVstyle/Vitali … me.js#L177

Vitaliy V. пишет

Что ж пусть так и будет.

Сунул в custom_script.js
некий рандомно-тестовый код, и всё работает так, как ожидалось.

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

Выделить код

Код:

var bla = "bla";

var test = {
	"globalThis.bla": globalThis.bla, // user box, expected string
	"SystemGlobal.bla": Cu.getGlobalForObject(Cu).bla, // SystemGlobal, expected undefined

	// expected all exsists

	// dom idl
	PathUtils: PathUtils,
	AbortController: AbortController,
	HTMLAnchorElement: HTMLAnchorElement,

	// global properties
	FileReader: FileReader,
	XMLSerializer: XMLSerializer,
	URLSearchParams: URLSearchParams,

	// modules
	PlacesUtils: PlacesUtils,
	AppConstants: AppConstants,
	CustomizableUI: CustomizableUI,

	// internal stuff
	UcfPrefs: UcfPrefs,
	user_chrome: user_chrome,
	user_chrome_files_sandbox: user_chrome_files_sandbox,
};
for(let key in test) test[key] = typeof test[key];
console.log(JSON.stringify(test, null, "\t"));


Парадигма сдвинута.

Есть вот такой скрипт: "Переключить текущий поисковик" (первый спойлер)


Кто-нибудь на FF123 пользуется этим скриптом?


Дело в том, что на Windows 7, по понятным причинам, крайнюю версию оригинального FF я поставить не могу.
Но нашлись "умельцы", которые делают сборки крайних FF, которые работают на Win7.
Так вот, на такой сборке сам скрипт работает, поисковики переключает, но вот иконки поисковиков почему-то не подгружает.
Untitled-2.png
Пытаюсь разобраться, это проблема скрипта в FF123 или "умельцы" что-то накрутил в своей сборке.


Кому не сложно, проверьте пожалуйста этот скрипт в оригинальном FF123.

unter_officer пишет

Есть вот такой скрипт: "Переключить текущий поисковик" (первый спойлер)


Кто-нибудь на FF123 пользуется этим скриптом?


Дело в том, что на Windows 7, по понятным причинам, крайнюю версию оригинального FF я поставить не могу.
Но нашлись "умельцы", которые делают сборки крайних FF, которые работают на Win7.
Так вот, на такой сборке сам скрипт работает, поисковики переключает, но вот иконки поисковиков почему-то не подгружает.
https://i.postimg.cc/439VzWC9/Untitled-2.png
Пытаюсь разобраться, это проблема скрипта в FF123 или "умельцы" что-то накрутил в своей сборке.


Кому не сложно, проверьте пожалуйста этот скрипт в оригинальном FF123.

[firefox] 123.0.1 не работает 
https://forum.mozilla-russia.org/viewto … 58#p808658 Farby  поправлял для 123  [firefox] что то похожее

unter_officer пишет

Пытаюсь разобраться

Это Bug 1870644 - Provide a single function for obtaining icon URLs from search engines


Farby предложил такую конструкцию:
engine._iconURI ? engine._iconURI.spec : engine.iconURI ? engine.iconURI.spec : this.defaultImg
что слегка замудро. Надеюсь, чуть лучше так:
(engine._iconURI || engine.iconURI)?.spec || this.defaultImg


Затем getIconURL() сделали асинхронной,
но пока ещё работает, то есть
img: e => (e._iconURI || e.iconURI)?.spec || "chrome://browser/skin/search-engine-placeholder.png",


А когда приземлится это, надо будет смотреть,
не слетят ли иконки встроенных лисьих поисковиков.


Ну, и на самой кнопке иконка не подхватывается, нужно прицепить wrappedJSObject.

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

Выделить код

Код:

/*
			var engine = popup.getDefaultEngine();
*/
			var engine = popup.getDefaultEngine().wrappedJSObject;

Dumby
egorsemenov06
Большое вам спасибо за помощь!

Dumby пишет

img: e => (e._iconURI || e.iconURI)?.spec || "chrome://browser/skin/search-engine-placeholder.png",

Спасибо, забрал. :beer:
Там ещё не работает скрытие неиспользуемых поисковиков, при excludeHiddenOneOffs: true, но полазил по форуму и нашел решение от Vitaliy V.

excludeHiddenOneOffs

Выделить код

Код:

//	excludeHiddenOneOffs: false,
	excludeHiddenOneOffs: true, // скрыть не нужные поисковики
..
//		if (this.excludeHiddenOneOffs)
//			var ex = Services.prefs.getStringPref(this.pref, "").split(",");
		var visibleEngines = await Services.search.getVisibleEngines();
		if (this.excludeHiddenOneOffs) {
			var args = "hideOneOffButton" in Services.search.defaultEngine
					? [e => !e.hideOneOffButton]
					: Object.defineProperty(
						[function(e) {return !this.includes(e.name);}], "1", {
							get: () => Services.prefs.getStringPref("browser.search.hiddenOneOffs")?.split(",") || []
						});
			visibleEngines = visibleEngines.filter(...args);
		}
..
//		for(var engine of await Services.search.getVisibleEngines()) {
		for(var engine of visibleEngines) {
..
//			if (this.excludeHiddenOneOffs && ex.includes(engine.name)) continue;

Vitaliy V.
Вы делали кнопку

Тултипы с URL

Выделить код

Код:

// Ссылка у курсора в тултипе, кнопка https://forum.mozilla-russia.org/viewtopic.php?pid=783755#p783755
(async () => {
    var id = "ucf-toggle-tooltip-url",
    label = "Тултипы с URL",
    tooltiptext = "Переключить тултипы",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' style='fill:context-fill rgb(142, 142, 152);'><path d='M9.618 6.721a2.483 2.483 0 0 0-.39-.317l-.735.734A1.486 1.486 0 0 1 8.91 9.55l-2.12 2.122a1.486 1.486 0 0 1-2.122 0 1.486 1.486 0 0 1 0-2.121l.605-.605a3.53 3.53 0 0 1-.206-1.209L3.961 8.843a2.506 2.506 0 0 0 0 3.535 2.506 2.506 0 0 0 3.535 0l2.122-2.121a2.506 2.506 0 0 0 0-3.536z'/><path d='M6.79 9.55c.12.121.25.226.389.317l.734-.734a1.486 1.486 0 0 1-.417-2.411L9.618 4.6a1.486 1.486 0 0 1 2.121 0 1.486 1.486 0 0 1 0 2.121l-.605.605c.137.391.211.798.206 1.209l1.106-1.107a2.506 2.506 0 0 0 0-3.535 2.506 2.506 0 0 0-3.535 0L6.789 6.014a2.506 2.506 0 0 0 0 3.536z'/><circle style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round' cx='8' cy='8' r='7.4'/></svg>",
    imgcolordisable = "color-mix(in srgb, currentColor 20%, #e31b5d)";

    var branch = "extensions.ucf.", pref = "tooltip_url_enable";
    var tpurl = {
        initialised: false,
        tooltip_url_enable: true,
        get ext_branch() {
            delete this.ext_branch;
            return this.ext_branch = Services.prefs.getBranch(branch);
        },
        init() {
            if (this.initialised) return;
            Services.prefs.getDefaultBranch(branch).setBoolPref(pref, true);
            Services.prefs.addObserver(`${branch}${pref}`, this);
            if (this.tooltip_url_enable = this.ext_branch.getBoolPref(pref))
                this.registerActor();
            this.initialised = true;
        },
        observe(subject, topic, data) {
            var fill = "";
            if ((this.tooltip_url_enable = this.ext_branch.getBoolPref(pref)) === true)
                this.registerActor();
            else {
                fill = imgcolordisable;
                this.unregisterActor();
            }
            this.callWithEachWindow(id, {fill: fill});
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
        },
        registerActor() {
            ChromeUtils.registerWindowActor("UcfTooltipUrl", {
                child: {
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/cs/UcfTooltipUrlChild.jsm",
                    events: {
                        mouseover: { capture: true },
                    },
                },
                allFrames: true,
                matches: ["<all_urls>"],
                messageManagerGroups: ["browsers"],
            });
        },
        unregisterActor() {
            ChromeUtils.unregisterWindowActor("UcfTooltipUrl");
        },
    };
    CustomizableUI.createWidget({
        id: id,
        label: label,
        tooltiptext: tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            tpurl.init();
            btn.style.setProperty("list-style-image", `url("${img}")`, "important");
            if (!tpurl.tooltip_url_enable)
                btn.style.setProperty("fill", imgcolordisable);
        },
        onCommand(e) {
            tpurl.ext_branch.setBoolPref(pref, !tpurl.ext_branch.getBoolPref(pref));
        },
    });
})();


UcfTooltipUrlChild.jsm

Выделить код

Код:

var EXPORTED_SYMBOLS = ["UcfTooltipUrlChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
class UcfTooltipUrlChild extends JSWindowActorChild {
    handleEvent(e) {
        timer.cancel();
        timer.initWithCallback(() => {
            var elm = e.isTrusted && e.composedTarget, el, titl;
            if (!elm) return;
            do {
                if (!elm.matches) continue;
                if (elm.matches(":any-link")) {
                    if (elm.matches("[href='#'], [href^='javascript']"))
                        return;
                    el = elm;
                    if (elm.matches("[title]"))
                        titl = elm;
                    else
                        while (elm = elm.flattenedTreeParentNode) {
                            if (!elm.matches) continue;
                            if (elm.matches("[title]")) {
                                titl = elm;
                                break;
                            }
                        }
                    break;
                }
                if (elm.matches("[title]")) {
                    titl = elm;
                    while (elm = elm.flattenedTreeParentNode) {
                        if (!elm.matches) continue;
                        if (elm.matches(":any-link")) {
                            if (elm.matches("[href='#'], [href^='javascript']"))
                                return;
                            el = elm;
                            break;
                        }
                    }
                    break;
                }
            } while (elm = elm.flattenedTreeParentNode);
            if (!el) return;
            var href = el.href;
            if (titl) el = titl;
            titl = (el.title || "");
            var title = titl.trim(), pre = "", path = "";
            try {
                href = Services.io.newURI(href);
                pre = href.displayPrePath;
                path = `\n${href.pathQueryRef}`;
                if (path === "\n/") path = "";
                href = `${pre}${path}`;
            } catch (e) {}
            try {
                href = decodeURIComponent(href);
            } catch (e) {}
            el.title = title = `${href}${title === "" ? "" : `\nTitle: ${title}`}`;
            this.contentWindow.addEventListener("mouseout", () => {
                try {
                    if (!el || title !== el.title) return;
                    if (titl !== "")
                        el.title = titl;
                    else
                        el.removeAttribute("title");
                } catch (e) {}
            }, { once: true });
        }, 100, Ci.nsITimer.TYPE_ONE_SHOT); /* было 400 */
    }
    didDestroy() {
        timer.cancel();
    }
}


Пока работает, но можно переделать на mjs?
P.S. Может, уже переделывали в теме, не уследил...

Farby пишет

нашел решение от Vitaliy V

Не, Виталий такую шляпу не пишет, у него всё как-то более академично.
Это моё, наверно. Кстати, ошибку свою заметил.


Настройка "browser.search.hiddenOneOffs" дефолтно существовала (до 116),
а значит, если там ничего не было, то значением возвращалась пустая строка.
А выражение ""?.split(",") возвращает массив с пустой строкой, и это совсем не то, что нужно.


Dumby пишет

А когда приземлится это, надо будет смотреть,
не слетят ли иконки встроенных лисьих поисковиков.

Ха, оказывается они уже слетели (причём, во всём браузере),
если руками включить настройку browser.search.newSearchConfig.enabled


Впрочем, теперь не важно, баг приземлился, настройку включили,
иконки встроенных лисьих поисковиков в брузере в порядке,
а в кнопке пропали (ну, или не появились, смотря от какого состояния настройки считать).


Но ничего, теперь хотя бы понятно, что engine._iconURI для них больше не подходит.
Вобщем вот, небольшая модификация обсуждавшегося кода

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

Выделить код

Код:

try {CustomizableUI.createWidget({
	label: "Переключить текущий поисковик",
	id: "ucf-cbbtn-ToggleCurrentSearchEngine",
	image: "data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA",
	excludeHiddenOneOffs: true,
	gn: () => Services.search.defaultEngine,
	gp: () => Services.search.defaultPrivateEngine,
	sn: val => Services.search.defaultEngine = val,
	sp: val => Services.search.defaultPrivateEngine = val,
	onCreated(btn) {
		btn.type = "menu";
		btn.owner = this;
		btn.setAttribute("image", this.image);

		var win = btn.ownerGlobal;
		var popup = btn.appendChild(win.document.createXULElement("menupopup"));
		var pr = win.PrivateBrowsingUtils.isWindowPrivate(win);
		popup.getDefaultEngine = pr ? this.gp : this.gn;
		popup.setDefaultEngine = pr ? this.sp : this.sn;
		popup.setAttribute("oncommand", "setDefaultEngine(event.target.engine)");
		popup.setAttribute("onpopupshowing", "this.shouldRebuild && owner.rebuild(this, document)");

		this.autoOpenCloseFeature(win, btn);
		this.updButton(btn, win);
	},
	getEngines() {
		var ve = Services.search.getVisibleEngines;
		if (!this.excludeHiddenOneOffs) return (this.getEngines = ve)();

		var arr = [];
		var args = this.fx116
			? [e => !e.hideOneOffButton]
			: Object.defineProperty(
				[function(e) {return !this.includes(e.name);}], "1", {get: () => {
					var str = Services.prefs.getStringPref(this.pref);
					return str ? str.split(",") : arr;
				}}
			);
		return (this.getEngines = async () => (await ve()).filter(...args))();
	},
	async rebuild(popup, doc) {
		popup.textContent = "";
		var df = doc.createDocumentFragment();
		var de = popup.getDefaultEngine().wrappedJSObject, jsde = this.json(de);
		var check = true;
		for(var engine of await this.getEngines()) {
			if (check && engine.name == de.name && this.json(engine) == jsde) {
				check = false; continue;
			}
			var menuitem = df.appendChild(doc.createXULElement("menuitem"));
			menuitem.engine = engine;
			menuitem.label = engine.name;
			menuitem.className = "menuitem-iconic";
			menuitem.image = await this.img(engine);
		}
		popup.append(df);
		delete popup.shouldRebuild;
	},
	async updButton(btn, win) {
		this.updButton = () => {};
		Services.search.isInitialized || await Services.search.init();
		this.fx116 = "hideOneOffButton" in Services.search.defaultEngine;

		var topics = ["browser-search-engine-modified", "quit-application-granted"];
		for(var topic of topics) Services.obs.addObserver(this, topic, false);
		this.observe = (s, topic) => this[topic[0]]();

		var remove = () => topics.forEach(
			topic => Services.obs.removeObserver(this, topic)
		);
		var {id} = this;
		var wins = callback => {
			for(var win of CustomizableUI.windows) {
				var btn = win.document.getElementById(id);
				btn && callback(btn, win);
			}
		}
		if (this.excludeHiddenOneOffs && !this.fx116) {
			var setRebuild = btn => btn.firstChild.shouldRebuild = true;
			var {pref} = this, obs = () => wins(setRebuild);
			Services.prefs.addObserver(pref, obs);
			this.q = () => remove(Services.prefs.removeObserver(pref, obs));
		}
		else this.q = remove;

		var updButton = (btn, win) => {
			var popup = btn.firstChild;
			var engine = popup.getDefaultEngine();
			/*btn.label =*/ btn.tooltipText = engine.name;
			popup.shouldRebuild = true;
			win.requestAnimationFrame(async () => btn.icon.src = await this.img(engine));
		}
		(this.b = () => wins(updButton))();
		this.updButton = updButton;
		btn.tooltipText || updButton(btn, win);
	},
	pref: "browser.search.hiddenOneOffs",
	json: e => JSON.stringify(e.toJSON()),
	img: async e => await e.getIconURL?.() || e.iconURI?.spec || "chrome://browser/skin/search-engine-placeholder.png",

	// https://github.com/Infocatcher/Custom_Buttons/blob/master/code_snippets/autoOpenCloseMenu.js
	// Automatically open menu on mouse over (and hide it on mouse out)
	autoOpenCloseFeature(win, btn, openDelay = 200, closeDelay = 350) {
		var _openTimer = 0;
		var _closeTimer = 0;
		btn.onmouseover = function(e) {
			win.clearTimeout(_closeTimer);
			if(e.target == btn && closeOtherMenus()) {
				btn.open = true;
				return;
			}
			_openTimer = win.setTimeout(function() {
				btn.open = true;
			}, openDelay);
		};
		btn.onmouseout = function(e) {
			win.clearTimeout(_openTimer);
			_closeTimer = win.setTimeout(function() {
					btn.open = false;
			}, closeDelay);
		};
		function closeOtherMenus() {
			return win.Array.prototype.some.call(
				btn.parentNode.getElementsByTagName("*"),
				function(node) {
					if(
						node != btn
						&& win.XULElement.isInstance(node)
						// 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;
						return true;
					}
					return false;
				}
			);
		}
	}
});} catch(ex) {Cu.reportError(ex);}

Dumby пишет

Настройка "browser.search.hiddenOneOffs" дефолтно существовала (до 116),
а значит, если там ничего не было, то значением возвращалась пустая строка.
А выражение ""?.split(",") возвращает массив с пустой строкой, и это совсем не то, что нужно.

Ха, оказывается они уже слетели (причём, во всём браузере),
если руками включить настройку browser.search.newSearchConfig.enabled


Впрочем, теперь не важно, баг приземлился, настройку включили,
иконки встроенных лисьих поисковиков в брузере в порядке,
а в кнопке пропали (ну, или не появились, смотря от какого состояния настройки считать).

Получаеться что и эта кнпка не будет работать https://forum.mozilla-russia.org/viewto … 58#p808658 .Поправте и ее пожалуйста

Dumby пишет

Вобщем вот, небольшая модификация обсуждавшегося кода

Dumby, в консоль пишет: CustomizableUI: Could not localize property 'ucf-cbbtn-ToggleCurrentSearchEngine.tooltiptext'.


Если в код добавить что-то типа:
tooltiptext: "Переключить текущий поисковик",
то тогда в консоли чисто.

egorsemenov06 пишет

Получаеться что и эта кнпка не будет работать

Ну, что значит не будет работать?
В данном случае речь о том, что перестанут подхватываться иконки
встроенных поисковиков (не поисковиков, которые поставил пользователь),
когда включат search-config-v2 (пока что, только в Nightly).


А правка там простая

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

Выделить код

Код:

/*
        node.setAttribute("image", engine._iconURI ? engine._iconURI.spec : engine.iconURI ? engine.iconURI.spec : this.defaultImg);
*/
        node.setAttribute("image", await engine.getIconURL?.() || engine.iconURI?.spec || this.defaultImg);

unter_officer пишет

в консоль пишет: CustomizableUI: Could not localize property 'ucf-cbbtn-ToggleCurrentSearchEngine.tooltiptext'.

Да, есть такое, за всем не уследишь.
Можно добавить, например, второй строкой
localized: false,


xrun1 пишет

переделать на mjs?

Что-то я не вижу там ничего такого,
что могло бы помешать сделать это самому.


Никаких выкрутасов, никаких __URI__ (которого в ESM нет),
никакого использования top-level this (который в ESM undefined),
разве что для Services, но он всё равно уже давно везде определён.


В скрипте меняем

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

Выделить код

Код:

/*
                    moduleURI: "chrome://user_chrome_files/content/custom_scripts/cs/UcfTooltipUrlChild.jsm",
*/
                    esModuleURI: "chrome://user_chrome_files/content/custom_scripts/cs/UcfTooltipUrlChild.mjs",


а в модуле
скрытый текст

Выделить код

Код:

/*
var EXPORTED_SYMBOLS = ["UcfTooltipUrlChild"];
ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
class UcfTooltipUrlChild extends JSWindowActorChild {
*/
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
export class UcfTooltipUrlChild extends JSWindowActorChild {


Вот и всё.

Dumby пишет

Да, есть такое, за всем не уследишь.
Можно добавить, например, второй строкой
localized: false,

Спасибо, теперь всё нормально.

Dumby пишет
egorsemenov06 пишет

Получаеться что и эта кнпка не будет работать

Ну, что значит не будет работать?
В данном случае речь о том, что перестанут подхватываться иконки
встроенных поисковиков (не поисковиков, которые поставил пользователь),
когда включат search-config-v2 (пока что, только в Nightly).


А правка там простая

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

Выделить код

Код:

/*
        node.setAttribute("image", engine._iconURI ? engine._iconURI.spec : engine.iconURI ? engine.iconURI.spec : this.defaultImg);
*/
        node.setAttribute("image", await engine.getIconURL?.() || engine.iconURI?.spec || this.defaultImg);

Спасибо!!! Чё-то я впереди паровоза побежал

Dumby пишет

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

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

Dumby пишет

Вобщем вот, небольшая модификация обсуждавшегося кода

ой а без директивы localized: false вы сразу отшибаете таких пользователей как например я..

Dumby
Спасибо за урок, есть много нюансов которые не знал где почерпнуть (async await)

Dumby
Вы когда-то помогли мне переделать кнопку CB в UCF:

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

Выделить код

Код:

(async initCode => CustomizableUI.createWidget({
    id: "ucf_UpDownCenterPage",
    label: "Up/Down/Center Page",
    // defaultArea: CustomizableUI.AREA_NAVBAR,
    localized: false,
    onCreated(btn) {
        btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAACXBIWXMAAAAAAAAAAAHqZRakAAACvElEQVQ4y3VTX0jTURT+rrsqmm3gwm02dZOZOk3RFCTMp4ggEQIT9GUD7VV8mDNF2NaD4qY2X8PBBpGkDwX64EP6YILGilzl3B/dnI5UMHSUWHO/3+1h/pva93S595zvfN8551IcgyYcZMYbHhpV17MU4xShABBV17MU9xR5xjSMAsCIe34ZAKb7F86S00aJ77CN3aJNUpVbnpYhX2pNvj7b5gQVJaceEEJWZ4mzEgBolHGrHPicwg5FxGtdF51ydCjKI9Z1lwgXQAGgr7CWJQkEGKh+yLqc00Tdlb+TdCTA92G/hAJAj3eenGQUmPKYsDsNAKBKzY3r1sgLJ+wlDxr1vg+TFsMS8RsulDiPfE3OWKg0fLvgh3Lf8yJQeykg4NhsBgAPAmcMnP0t48w2pLiniFIjn7hrL238rPdOeizBBjpYXMcslhHwACw1j9ioyO1y6da2wRMFAFDdyhxheffZoUqB9JlRgsUr+kBC7wlC8YuaaAkDgHZBJ1oE2vi8RiH1hcXXeOPPtaLFlGXymrOzFoGWnDK0VtUVoKQCRkc3AODk8co+XAWlRj6htMsaZRAjBxK8089NeizBhksEHYryyJDRKBzoM6PHt3BahYDEFmyf/NE9RLMOxFQUE2YmKDDllbFekwmc2QbObIOOiqFT1zPD7xD6N76RgGOzGY54whZ2sYXdxC4ZQl+JQfv4vxZUT3Od6S9JVfpRBm5yUrh7g19WhgKVCRbaFWVLaYTKjijhwZDE87Eda8BVVj2oZnyUAzVT8AwI89uQZIkrsi1ZbKZzkZwSRGJ//SPSonKIJYA0G/q5Nx8BwKlzk9wmmTv7VWYxOZ7f807TL89wUJigwBH2PnGEvVda2BjfUm+Mb6GwQxFpsWpFlzYlvvNFY/fC+3f28Cd6IBbTfVHGrjXgqj0f7LWuX/py/wDHfA2DzTMorQAAAABJRU5ErkJggg==");
        new btn.ownerGlobal.Function(initCode).call(btn);
    }
}))(`(u => {
    var id, lfs = url => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false);
    var max = () => {
        var url = u([
            "var args = [scroller.scrollHeight, 0];",
            "scroller.scrollTop != 0 || args.reverse();",
            "content.scrollTo(...args);"
        ].join("\\n\\t"));
        (max = () => lfs(url))();
    }
    var mid = () => {
        var url = u("content.scrollTo(0, (scroller.scrollHeight - scroller.clientHeight) / 2);");
        (mid = () => id = lfs(url))();
    }
    var obj = {
        mousedown: () => id = setTimeout(mid, 500),
        mouseup: () => id && max(id = clearTimeout(id))
    };
    this.onmousedown = this.onmouseup = e => e.button || obj[e.type]();
    this.tooltipText = "ЛКМ:   Вверх/Вниз по странице \\nдЛКМ: Центрирование страницы";
})
(code => "data:," + encodeURIComponent(\`(doc => {
    var root = doc.documentElement;
    var body = doc.body || root;
    var scroller = body.scrollHeight > root.scrollHeight ? body : root;
    \${code}
})(content.document)\`));`);

Я этой кнопкой пользуюсь нечасто, поэтому как-то сразу не заметил один нюанс.
Если в исходном коде страницы сайта отсутствует элемент <!DOCTYPE>, то кнопка начинает работать неправильно. Проверить можно, например, на forum.ru-board.com
Это возможно как-то поправить?

Dumby - поправь код менюшки! Не работает upd(){… для подменю!
Одинаковый код обновления не пашет в подменю, но работает в первой и последней строках меню.
screen-2024-03-15-10-11-19.png

Выделить код

Код:

(async id => { // forum.mozilla-russia.org/viewtopic.php?pid=808738#p808738

MyMenu = { //массив команд пользователя, alt() клик правой кнопкой
	Pics: { // Графика сайтов Вкл/Выкл permissions.default.image

		upd() { // обновлять сроку перед показом меню
			// var {G} = this.ownerGlobal;
			var val = G.pref(G.v), s = val == 1, i = G.pdi; s ? i = i.replace("-blocked","") : 0;
			this.label = `Графика сайтов ${s ? "загружается" : val == 3 ? "кроме сторонних" : "отключена"}`;
			this.image = i || G.chk;
			this.tooltipText = G.v +" "+ val +"\nRClick – кроме сторонних";
		},
		cmd(){
			G.pref(G.v, G.pref(G.v) == 2 ? 1 : 2);
			BrowserReload();
		},
		// alt(){ //для RClick нужен ucf_hookClicks.js
		// 	G.pref(G.v, 3); BrowserReload();
		// },
	},
	"SubMenu": { men: 1, //подменю
		"SubItem": {
			lab: "SubItem Mod",
			// img: G.opt,
			upd() { // обновлять сроку перед показом меню
				var val = G.pref(G.v), s = val == 1, i = G.pdi; s ? i = i.replace("-blocked","") : 0;
				this.label = `SubItem: Графика ${s ? "загружается" : val == 3 ? "кроме сторонних" : "отключена"}`;
				this.image = i || G.chk;
				this.tooltipText = G.v +" "+ val +"\nRClick – кроме сторонних";
			},
			cmd(){console.log(this.label)},
		},
	},
	"End Menu": { sep: 1, //сперва разделитель
		upd() { //обновить иконку
			var val = G.pref(G.v), s = val == 1, i = G.pdi; s ? i = i.replace("-blocked","") : 0;
			this.image = i;
		},
	},
}

CustomizableUI.createWidget({
		id: id, label: id, tooltiptext: id, localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onCreated(btn) {
			btn.style.setProperty("list-style-image", "url(chrome://branding/content/icon32.png)");
			btn.type = "menu";
			var doc = btn.ownerDocument, m = nn => doc.createXULElement(nn);
			var popup = m("menupopup"), menu = m("menuitem");
			menu.m = m;
			menu.fill = this.fill;
			menu.render = this.render;
			popup.append(menu);
			btn.prepend(popup);
		},
		render(){
			var popup = this.parentNode;
			this.remove();
			this.fill(MyMenu, popup);
		},

	fill(o, popup) {
		for (key in o) {
			var val = o[key];
			if (typeof val != "object") continue;

			var {lab, inf, img, cmd, alt, sep, men, upd} = val;

			sep && popup.append(this.m("menuseparator"));
			var name = men ? "menu" : "menuitem";
			var item = this.m(name);

			item.setAttribute("label", lab || key);
			// item.alt = alt; //RClick в ucf_hookClicks.js {Mouse…
			if (inf)
				item.tooltipText = inf;
			if (img || /this\.image.*=/.test(upd))
				item.className = name + "-iconic", item.setAttribute("image", img || G.nul);
			men || cmd && item.setAttribute("oncommand", cmd.toString().replace(
				/cmd\(.*?\){/, "{var trg = event.target || event;"
			));
			popup.append(item);
			upd && (item.render = upd).call(item);
			men && this.fill(val, item.appendChild(this.m("menupopup")));
		}
	},
});

var {prefs} = Services;
G = {
	pref(key,set){ //или key = [key,default]
		if (!Array.isArray(key)) key = [key];
		var t = prefs.getPrefType(key[0]), m = {b:"Bool",n:"Int",s:"String"};
		t = m[t == 128 ? "b" : t == 64 ? "n" : t == 32 ? "s" : ""];
		if (set == "get") return t; //тип опции
		if (!t) t = m[set != undefined ? (typeof set)[0] : (typeof key[1])[0]];
		if (t) if (set != undefined)
			prefs[`set${t}Pref`](key[0],set)
		else
			set = prefs[`get${t}Pref`](...key);
		return set;
	},
	v: "permissions.default.image",
	pdi: "chrome://browser/skin/canvas-blocked.svg",
	chk: "chrome://devtools/skin/images/check.svg",
	nul: "chrome://devtools/skin/images/blocked.svg",
}

})("ucf_test_menu");
unter_officer пишет

Это возможно как-то поправить?

Даже не знаю, может так попробовать

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

Выделить код

Код:

/*
    var root = doc.documentElement;
    var body = doc.body || root;
    var scroller = body.scrollHeight > root.scrollHeight ? body : root;
*/
    var scroller = doc.scrollingElement;

Dobrov пишет

код обновления не пашет в подменю

Разве? Код-то как раз пашет, но пункт не render'ится.


Когда добавляешь пункт в основное меню он render'ится сразу,
но когда в субменю, тогда нет. А когда будет следующий вызов render()
метод уже переопределён и ничего не render'ит.


Можно сразу принудительно отрендерить, но лучше, наверно, отложить.
Плюс, upd() вызывается для пунктов субменю даже просто при открытии основного меню,
а это слегка нехорошо. Возможно, стоит проверять, что субменю открыто.


Вобщем, так поменял фрагмент кода создания виджета

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

Выделить код

Код:

CustomizableUI.getWidget(id)?.label || (self => CustomizableUI.createWidget(self = {
		id, label: id, tooltiptext: id, localized: false,
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onCreated(btn) {
			btn.style.setProperty("list-style-image", "url(chrome://branding/content/icon32.png)");
			btn.type = "menu";
			var doc = btn.ownerDocument, m = nn => doc.createXULElement(nn);
			var popup = m("menupopup"), menu = m("menuitem");
			menu.m = m;
			menu.fill = this.fill;
			menu.render = this.render;
			popup.append(menu);
			btn.prepend(popup);
		},
		render(){
			var popup = this.parentNode;
			this.remove();
			this.fill(MyMenu, popup);
		},
		fill(o, popup) {
			for (key in o) {
				var val = o[key];
				if (typeof val != "object") continue;

				var {lab, inf, img, cmd, alt, sep, men, upd} = val;

				sep && popup.append(this.m("menuseparator"));
				var name = men ? "menu" : "menuitem";
				var item = this.m(name);

				item.setAttribute("label", lab || key);
				// item.alt = alt; //RClick в ucf_hookClicks.js {Mouse…
				if (inf)
					item.tooltipText = inf;
				if (img || /this\.image.*=/.test(upd))
					item.className = name + "-iconic", item.setAttribute("image", img || G.nul);
				men || cmd && item.setAttribute("oncommand", cmd.toString().replace(
					/cmd\(.*?\){/, "{var trg = event.target || event;"
				));
				popup.append(item);

				if (upd)
					if (item.renderedOnce) (item.render = upd).call(item);
					else item.upd = upd, item.render = self.renderSub;

				men && this.fill(val, item.appendChild(this.m("menupopup")));
			}
		},
		renderSub() {
			delete this.render;
			this.render();
			this.render = self.updSub;
			this.upd();
		},
		updSub() {
			this.parentNode.state.startsWith("c") || this.upd();
		}
}))();

Dumby пишет

Даже не знаю, может так попробовать

Dumby, большое спасибо. Как всегда всё супер!

Dumby - спасибо, добавил примеры в пользовательское меню, где меняются иконки, текст и подсказки в зависимости от настроек.
Изменяемые по клику строки меню отличаются выделенным шрифтом.


Обновил меню в ucf_hookClicks.js и исправил совместимость с UCF 2024.
Firefox/config.js может грузить старую и новую версию UCF.

Dobrov пишет

исправил совместимость с UCF 2024.
Firefox/config.js может грузить старую и новую версию UCF

Немного запоздало я как раз недавно обновил несколько файлов config.js user_chrome.js vertical_top_bottom_bar.css
https://github.com/VitaliyVstyle/Vitali … /config.js

Vitaliy V. - спасибо!
забываю проверять обновления UCF…
и теперь новый упрощённый config.js не загружает user_chrome.js от старого UCF

Dobrov
Спасибо за обновы, особенно за ucf_hookClicks.js) Удобная штука. Все что хотел раньше от скриптов, все в одном флаконе, под любые хотелки. Кстати, а что за сборка 116? esr?

Vitaliy V.
Спасибо за обновления. Рад вас видеть.
   
Dumby
Приветствую.
Лоадер применительно к mjs не изменился?
Предполагаю что нет, так как один конвертированный скрипт в нем уже работает.
   
И, пожалуйста, переделайте в mjs jsm-ки:
1. TST Reload Tab Interval
2. TST TabPreview
   
TST

Dumby :beer: такой глупый вопрос: JS скрипты будут дóльше поддерживаться в Firefox, чем JSM-ки?


Если это так, то возможно переделать в JS скрипт сохранения страниц SingleHTML.JSM (в custom_scripts UCF 2024 тоже JS-скрипты)
Его функции используют два скрипта (а могут и другие): ClickPicSave.jsm и ucf_hookClicks.js


b0ttle пишет

ucf_hookClicks.js Удобная штука. Все что хотел раньше от скриптов, все в одном флаконе
а что за сборка 116? esr?

в ucf_hookClicks.js код слишком компактный, чтоб легко подстроить «под себя» строки меню/нажатия клавиш/клики мыши в первых блоках. Позже отформатирую по правилам, разграничив блоки разных назначений.
А браузер обычный LibreWolf 116 arm64 на Apple Silicon Mac M3, не вижу смысла постоянно обновлять (поставил в игнор для менеджера пакетов brew)

Vitaliy V. - пожелание:
добавить подключение стилей по имени в зависимости от OS – иногда нужно использовать один профиль на разных системах, для которых CSS не совпадают.
вот так работает в CustomStylesScripts.mjs (на MacOS подключается custom_styles_all_user_macosx.css):

Выделить код

Код:

const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
var os = name => `${name.replace(/\.[^.$]+$/,'')}_${AppConstants.platform}${name.lastIndexOf('.') > 0 ? "."+ name.split('.').pop() : ""}`;
…………
	stylesall: [ // Для всех документов
		{ path: "custom_styles_all_user.css", type: "USER_SHEET", sheet() { registerSheet(this); }, },
	// стиль для вашей операционной системы: *_macosx.css, *_linux.css, *_win.css
		{ path: os("custom_styles_all_user.css"), type: "USER_SHEET", sheet() { registerSheet(this); }, },
	],
Dobrov пишет

добавить подключение стилей по имени в зависимости от OS

Да, но для стилей есть медиа запросы, например:
@media (-moz-platform: macos) {
   код для macos
}
@media (-moz-platform: linux) {
   код для linux
}
@media (-moz-platform: windows) {
   код для windows
}
Скрипты же тем более могут определить OS, я не против добавить но в каких случаях это необходимо?

Vitaliy V. пишет

Скрипты же тем более могут определить OS, я не против добавить но в каких случаях это необходимо?

Для портабельного или личного профиля, запускаемого и используемого на всяких OS.
В идеале лучше так: грузим стили из custom_styles, затем дополнительные CSS из папки, соответствующий имени операционки.
если браузер на MacOS находит папку custom_styles/macosx, догружаем custom_styles_all_agent.css и прочие CSS из неё.


Примеры стилей обычно только для винды, я пробовал @media (-moz-platform:, но неудобство в том, что придётся править стили при каждом их обновлении.
В стилях от aris-t2 есть совместимость для разных OS, но для Linux и MacOS всё же требуются дополнительные правки.
Проще разные CSS держать, т.к. CSS для MacOS и Windows слишком различаются, да и разные стили для AGENT_SHEET и USER_SHEET увеличивают число файлов.
из-за несовместимости версий Firefox я держу разные папки, например aris-t2-115+ и aris-t2-97…, но это уже другая история

_zt пишет

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

Не понял. Если сделал как написано в первой строке
конвертированного скрипта — значит изменился.


Неизменившийся у меня не работает, хотя мне казалось, что будет.
Но зачем искать себе приключений?
Для сконвертированных модулей следует использовать метод,
который сейчас в браузере для этого предназначен — ChromeUtils.importESModule("……….mjs");

И, пожалуйста, переделайте в mjs jsm-ки:

Хорошо, попробую.

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

Выделить код

Код:

var clickInterval = 5*60;
var intervals = [
	10, 15, 30, 60, 3*60,/* 5*60,*/ 15*60, 30*60, 60*60,
];
var name = "TreeStyleTabAutoReloader";
var addonId = "treestyletab@piro.sakura.ne.jp";
var sfx = "ucf-tst-tab-autoreload", id = `extension:${addonId}:${sfx}`;
var sheets = {
	def(name, css) {
		Object.defineProperty(this, name, {configurable: true, get() {
			delete this[name]; return this[name] = this.pre(name, css);
		}});
	},
	pre(name, css) {
		var ios = Services.io;
		var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		var type = "USER_SHEET";
		return (this.pre = (name, css) => {
			var subst = "tst-autoreload-stylesheet-" + name;
			rph.setSubstitution(subst, ios.newURI("data:text/css," + encodeURIComponent(css)));
			return [sss.preloadSheet(ios.newURI(`resource://${subst}/`), sss[type]), Ci.nsIDOMWindowUtils[type]];
		})(name, css);
	}
};
if (!ChromeUtils.domProcessChild.childID) {

	var ep = "resource://gre/modules/ExtensionParent.sys.mjs";
	var manager = ChromeUtils.importESModule(ep).ExtensionParent.apiManager;
	var tt = manager.global.tabTracker;
	var ss = "resource:///modules/sessionstore/SessionStore.sys.mjs";
	ss = ChromeUtils.importESModule(ss).SessionStore;
	var gsec = tab => ss.getCustomTabValue(tab, id);

	var webExt, addonUUID;
	
	var waitAddon = (e, isAppShutdown) => isAppShutdown || (
		webExt = null, manager.on("ready", onReady)
	);
	var onReady = (e, addon) => {
		if (addon.id != addonId) return;
		manager.off("ready", onReady);
		addon.once("shutdown", waitAddon);
		onAddon(addon);				
	}
	var onAddon = addon => {
		webExt = addon;
		if (addonUUID == addon.uuid) return;

		addonUUID && ChromeUtils.unregisterWindowActor(name);

		var esModuleURI = Components.stack.filename;
		ChromeUtils.registerWindowActor(name, {
			parent: {esModuleURI},
			remoteTypes: ["extension"],
			messageManagerGroups: ["webext-browsers"],
			child: {esModuleURI, events: {pageshow: {}}},
			matches: [`moz-extension://${addonUUID = addon.uuid}/sidebar/sidebar.html?*`]
		});
	}
	var format = sec => {
		var map = new Map();
		// resource://gre/modules/PluralForm.jsm
		var f = n => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
		var hh = ["", "а", "ов"], ms = ["а", "ы", ""];
		return (format = sec => {
			var res = map.get(sec = +sec);
			if (!res) {
				var num, arr = [];
				if ((num = Math.floor(sec / 3600)) > 0)
					sec -= num * 3600,
					arr.push(`${num} час${hh[f(num)]}`);
				if ((num = Math.floor(sec / 60)) > 0)
					sec -= num * 60,
					arr.push(`${num} минут${ms[f(num)]}`);
				sec > 0 && arr.push(`${sec} секунд${
					sec > Math.floor(sec) ? "ы" : ms[f(sec)]
				}`);
				map.set(sec, res = arr.join(" "));
			}
			return res;
		})(sec);
	}
	var hasDef = intervals.includes(clickInterval);
	hasDef || intervals.push(clickInterval);
	sheets.def("sb", `
		#context_autoreloadTab:not([checked]) > .menu-iconic-left,${
			hasDef ? "" : "\n\t\t#context_autoreloadTab[checked][def=true] > menupopup > :nth-child(2),"
		}
		#context_autoreloadTab:not([checked]) > menupopup > :first-child {
			fill: gray !important;
			-moz-context-properties: fill !important;
			list-style-image: url("chrome://global/skin/icons/reload.svg") !important;
		}
		#context_autoreloadTab[checked] > .menu-iconic-left > image {
			fill: currentColor !important;
			-moz-context-properties: fill !important;
			list-style-image: url("chrome://global/skin/icons/check.svg") !important;
		}
	`);
	clickInterval = String(clickInterval);

	var sym = Symbol(name);
	var TreeStyleTabAutoReloaderParent = class extends JSWindowActorParent {
		actorCreated() {
			var win = this.browsingContext.embedderElement.ownerGlobal;
			var mo = win[sym];
			if (!mo) {
				win.windowUtils.addSheet(...sheets.sb);
				mo = win[sym] = new win.MutationObserver(this.handleMutations);
				var popup = win.document.getElementById("contentAreaContextMenu");
				mo.obs = mo.observe.bind(mo, popup, {childList: true});
				mo.win = win;
			}
			((mo.actor = this).mo = mo).obs();
		}
		didDestroy() {
			this.mo.disconnect();
		}
		get menu() {
			var value = this.mo.menu;
			if (!value) {
				value = this.mo.win.MozXULElement.parseXULToFragment(
					`<menu id="context_autoreloadTab"
						class="menu-iconic"
						onclick="if (event.target == this) linkedObject.click(this);"
					>
						<menupopup oncommand="parentNode.linkedObject.cmd(event);"/>
					</menu>`
				);
				(value = this.mo.menu = value.firstChild).remove();
				value.linkedObject = this;
				(value.popup = value.firstChild).initShadowDOM = this.initShadowDOM;
			}
			return Object.defineProperty(this, "menu", {value}).menu;
		}
		initShadowDOM() {
			delete this.initShadowDOM;
			this.initShadowDOM();

			var df = this.ownerGlobal.MozXULElement.parseXULToFragment(
				`<menuitem closemenu="single" label="Не перезагружать"
					oncommand="event.stopPropagation(); parentNode.parentNode.click();"/>
				<menuitem label="Другой…"
					oncommand="event.stopPropagation(); parentNode.parentNode.linkedObject.prompt();"/>
				<menuseparator/>`
			);
			var doc = this.ownerDocument;

			for(var sec of intervals) {
				var menuitem = doc.createXULElement("menuitem");
				menuitem.setAttribute("type", "radio");
				menuitem.setAttribute("closemenu", "single");
				menuitem.setAttribute("value", sec);
				menuitem.setAttribute("label", format(sec));
				df.append(menuitem);
			}
			hasDef || df.firstChild.after(df.lastChild);
			this.append(df);
			this.parentNode.linkedObject.updMenupopup(this);
			this.setAttribute("onpopupshowing", "parentNode.linkedObject.updMenupopup(this);");
		}
		handleMutations(muts) {
			var cm = this.win.gContextMenu;
			if (cm) for(var mut of muts) for(var node of mut.addedNodes)
				if (node.id == "treestyletab_piro_sakura_ne_jp-menuitem-_context_duplicateTab") {
					var {tabId} = cm.contentData.webExtContextData;
					var tab = tt.getTab(tabId);
					//if (tab?.linkedBrowser.currentURI.scheme.startsWith("http")) {
					if (tab) {
						var {menu} = this.actor;
						menu.tabId = tabId;
						node.after(menu);
						this.actor.maybeSetLabel(tab);
						webExt.apiManager.global.gMenuBuilder.itemsToCleanUp.add(menu);
					}
					break;
				}
		}
		maybeSetLabel(tab) {
			var sec = gsec(tab);
			var has = this.menu.hasAttribute("checked");
			if (Boolean(sec) ^ has)
				has = !has, this.menu.toggleAttribute("checked");

			var curr = has && sec;
			curr !== this.menu.sec && this.setLabel(curr);
		}
		setLabel(sec) {
			this.menu.setAttribute("label", (this.menu.sec = sec)
				? `Интервал перезагрузки:   ${format(sec)}`
				: "Задать интервал перезагрузки"
			);
			hasDef || this.menu.setAttribute("def", sec == clickInterval);
		}
		click(menu) {
			var {tabId} = menu;
			var has = menu.toggleAttribute("checked");
			has
				? this.initTab(tabId, clickInterval)
				: this.destroyTab(tabId);

			var w = menu.clientWidth;
			this.setLabel(has && clickInterval);

			if (menu.popup.state == "open")
				this.updMenupopup(menu.popup),
				menu.clientWidth != w && menu.ownerGlobal.setTimeout(this.move, 50, menu);
		}
		cmd(e) {
			var {value} = e.target;
			if (value == this.menu.sec) return;

			var {tabId} = this.menu;
			this.setLabel(value);

			if (this.menu.hasAttribute("checked"))
				this.changeInterval(tt.getTab(tabId), value);
			else
				this.menu.toggleAttribute("checked"),
				this.initTab(tabId, value);
		}
		changeInterval(tab, sec) {
			var win = tab.ownerGlobal;
			win.clearInterval(tab.getAttribute(sfx));
			ss.setCustomTabValue(tab, id, sec);
			tab.setAttribute(sfx, win.setInterval(bro.reload, sec * 1e3, tab));
		}
		async prompt(val) {
			var {menu} = this, {sec} = menu, {prompt} = Services;
			var res = await prompt.asyncPrompt(
				null, prompt.MODAL_TYPE_WINDOW,
				val ? "ЕЩЁ РАЗ:" : "Задать интервал обновления",
				"Введите число секунд авто-обновления",
				val || sec || clickInterval, null, null
			);
			if (!res.get("ok")) return;

			var val = res.get("value");
			if (!val) return;
			if (!isFinite(val)) return this.prompt(val);

			var {tabId} = menu, val = String(Math.round(val) || 1);
			sec ? this.changeInterval(tt.getTab(tabId), val) : this.initTab(tabId, val);
		}
		move(menu) {
			menu.popup.moveToAnchor(menu, "end_before");
		}
		updMenupopup(popup) {
			var old = popup.querySelector("[checked=true]");
			var {sec} = this.menu;
			var cur = sec && popup.querySelector(`[value="${sec}"]`);
			if (old != cur)
				old?.removeAttribute("checked"),
				cur && cur.setAttribute("checked", true);
		}
		initTab(tabId, sec, skipSet) {
			bro.initTab(tt.getTab(tabId), sec);
			this.sendAsyncMessage(tabId, true);
		}
		destroyTab(tabId) {
			bro.destroyTab(tt.getTab(tabId));
			this.sendAsyncMessage(tabId);
		}
		receiveMessage(msg) {
			msg.name && bro.destroyTab(tt.getTab(+msg.name));
		}
	}
	var bro = {
		async observe(win) {
			var tc = win.document.getElementById("tabbrowser-tabs");
			var tp = win.document.getElementById("tabbrowser-tabpanels")
			var types = ["EndSwapDocShells", "TabClose", "SSTabRestored"];

			var destructor = (meth = "removeEventListener") => types.forEach(
				(type, ind) => (ind ? tc : tp)[meth](type, this, ind == 0)
			);
			destructor("addEventListener");

			win.ucf_custom_script_win[id] = {destructor};
			win.ucf_custom_script_win.unloadlisteners.push(id);

			await ss.promiseAllWindowsRestored;
			for(var tab of win.gBrowser.tabs)
				tab.linkedPanel || this.maybeInitTab(tab);
		},
		maybeInitTab(tab) {
			var sec = gsec(tab);
			sec && this.initTab(tab, sec, true);
		},
		handleEvent(e) {
			this[e.type](e);
		},
		reload(tab) {
			tab.ownerGlobal.gBrowser.reloadTab(tab);
		},
		initTab(tab, sec, skipSet) {
			skipSet || ss.setCustomTabValue(tab, id, sec);
			tab.setAttribute(sfx, tab.ownerGlobal.setInterval(this.reload, sec * 1e3, tab));
		},
		destroyTab(tab) {
			tab.ownerGlobal.clearInterval(tab.getAttribute(sfx));
			ss.deleteCustomTabValue(tab, id);
			tab.removeAttribute(sfx);
		},
		TabClose(e) {
			var intervalId = e.target.getAttribute(sfx);
			if (!intervalId) return;
			e.target.ownerGlobal.clearInterval(intervalId);

			var tab = e.detail.adoptedBy;
			tab && this.initTab(tab, gsec(e.target));
		},
		SSTabRestored(e) {
			var tab = e.target;
			tab.hasAttribute(sfx) || this.maybeInitTab(tab);
		},
		async EndSwapDocShells(e) {
			var br = e.detail, trg = e.target, win = br.ownerGlobal;
			await new Promise(win.requestAnimationFrame);

			if (!win.closed) return;
			var tab = win.gBrowser.getTabForBrowser(br);
			if (!tab) return;

			var sec = gsec(tab);
			if (sec)
				tab = trg.ownerGlobal.gBrowser.getTabForBrowser(trg),
				tab.hasAttribute(sfx) || this.initTab(tab, sec);
		}
		
	};
	var topic = "browser-delayed-startup-finished";
	var {obs} = Services;

	obs.addObserver(bro, topic);
	obs.addObserver(function quit(s, t) {
		obs.removeObserver(quit, t);
		obs.removeObserver(bro, topic);
	}, "quit-application-granted");

	var policy = WebExtensionPolicy.getByID(addonId);
	if (policy)
		onAddon(policy.extension),
		policy.extension.once("shutdown", waitAddon);
	else
		waitAddon();

} else {
	sheets.def("tst", `
		:root {
			--ar-ind-width: 22px;
		}
		.autoreload-indicator {
			height: 14px !important;
			position: relative !important;
			z-index: var(--tab-ui-z-index) !important;

			opacity: .6 !important;
			fill: currentColor !important;
			-moz-context-properties: fill !important;
			min-width: var(--ar-ind-width) !important;
			background: no-repeat center/60% url("chrome://global/skin/icons/reload.svg") !important;
		}
		.autoreload-indicator:hover {
			opacity: 1 !important;
		}
		tab-item-substance[autoreload] > .extra-items-container.front {
			right: calc(var(--tab-label-end-offset) + var(--ar-ind-width)) !important;
		}
		tab-item.faviconized .autoreload-indicator {
			min-width: 12px !important;
			background-size: 100% !important;
		}
		tab-item.faviconized > tab-item-substance[autoreload] {
			padding-inline-start: 0 !important;
		}
	`);
	var opts = {childList: true};

	var TreeStyleTabAutoReloaderChild = class extends JSWindowActorChild {
		handleEvent(e) {
			this.sendAsyncMessage("");
			this.stopReload = this.stopReload.bind(this);

			var doc = e.target, win = doc.ownerGlobal;
			win.windowUtils.addSheet(...sheets.tst);
			var mos = this.mos = new Set();

			for(var div of doc.querySelectorAll(
				"#pinned-tabs-container, #normal-tabs-container > .virtual-scroll-container"
			)) {
				var mo = new win.MutationObserver(this.catchUL);
				mos.add(mo);
				mo.actor = this;
				mo.observe(mo.div = div, opts);
			}
		}
		catchUL() {
			var ul = this.div.querySelector(":scope > ul");
			if (!ul) return;
			this.disconnect();
			this.actor.mos.delete(this);

			var mo = new this.constructor(this.actor.catchTabItem);
			(mo.actor = this.actor).mos.add(mo);
			mo.observe(ul, opts);
			for(var node of ul.children) mo.actor.check(node);
		}
		catchTabItem(muts) {
			for(var mut of muts) for(var node of mut.addedNodes) this.actor.check(node);
		}
		check(node) {
			node.nodeName == "TAB-ITEM" && this.onTab(node);
		}
		async onTab(tab, tabId) {
			var subs = tab.querySelector("tab-item-substance");
			var win = tab.ownerDocument.defaultView.wrappedJSObject;
			if (!tabId) {
				if (subs.hasAttribute("autoreload"))
					subs.removeAttribute("autoreload"),
					subs.querySelector(".autoreload-indicator")?.remove();

				var {tabId} = subs.dataset;
				var sec = await win.browser.sessions.getTabValue(+tabId, sfx);
				if (!sec) return;
			}
			subs.toggleAttribute("autoreload", true);
			var ind = win.document.createElement("span");
			ind.className = "autoreload-indicator";
			ind.title = "Остановить перезагузку";
			ind.tabId = tabId;
			ind.onmousedown = this.stopReload;
			subs.querySelector("tab-closebox").before(ind);
		}
		stopReload(e) {
			if (e.button) return;
			e.stopImmediatePropagation();
			var trg = e.target.wrappedJSObject;
			this.sendAsyncMessage(trg.tabId);
			this.removeIndicator(trg);
		}
		removeIndicator(ind) {
			ind.closest("tab-item-substance").removeAttribute("autoreload");
			ind.remove();
		}
		receiveMessage(msg) {
			var tab = this.contentWindow.document.getElementById("tab-" + msg.name);
			if (tab) msg.data
				? this.onTab(tab, msg.name)
				: this.removeIndicator(tab.querySelector(".autoreload-indicator"));
		}
		didDestroy() {
			var {mos} = this;
			if (!mos) return;
			for(var mo of mos) mo.disconnect();
			mos.clear();
		}
	}
}
export {TreeStyleTabAutoReloaderParent, TreeStyleTabAutoReloaderChild};

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

Выделить код

Код:

var timeout = 500;

if (!ChromeUtils.domProcessChild.childID) {

	var popupWidth = 300;

	var label = "Some Label";
	var tooltiptext = "Some Tooltip Text";
	var imgEnabled = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="16"><rect fill="limegreen" width="16" height="16"/></svg>';
	var imgDisabled = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" height="16"><rect fill="orangered" width="16" height="16"/></svg>';


	var btnImage, popupPosition, enabled, addonUUID, registeredUUID;
	var mo = (p, r = "gre") => ChromeUtils.importESModule(`resource://${r}/modules/${p}.sys.mjs`)[p];

	//-------[ Addon ]------------------------------------------------------

	var addonId = "treestyletab@piro.sakura.ne.jp";
	var manager = mo("ExtensionParent").apiManager;
	var tt = manager.global.tabTracker;

	var waitAddon = (e, isAppShutdown) => isAppShutdown || (
		addonUUID = null, manager.on("ready", onReady)
	);
	var onReady = (e, addon) => {
		if (addon.id != addonId) return;
		manager.off("ready", onReady);
		addon.once("shutdown", waitAddon);
		addonUUID = addon.uuid;
		checkRegistration();
	}
	waitAddon();

	//-------[ Actor registration ]------------------------------------------------------

	var name = "TreeStyleTabPreviewPopup";

	var esModuleURI = Components.stack.filename;
	var reg = () => ChromeUtils.registerWindowActor(name, {
		parent: {esModuleURI},
		remoteTypes: ["extension"],
		messageManagerGroups: ["webext-browsers"],
		child: {esModuleURI, events: {mouseover: {}}},
		matches: [`moz-extension://${registeredUUID = addonUUID}/sidebar/sidebar.html*`]
	});
	var unreg = () => {
		registeredUUID = null;
		ChromeUtils.unregisterWindowActor(name);
	}
	var checkRegistration = () => {
		if (enabled) {
			if (registeredUUID) {
				if (registeredUUID == addonUUID) return;
				addonUUID && unreg();
			}
			addonUUID && reg();
		}
		else if (registeredUUID && addonUUID) unreg();
	}

	//------[ Observer ]------------------------------------------------------

	var {prefs, obs} = Services;
	var pref = "ucf_tst_preview_popup";
	var branch = prefs.getBranch("sidebar.");

	var prefObs = {
		observe(b, t, data) {
			this[data]?.(branch.getBoolPref(data, true));
		},
		position_start: val => popupPosition = val ? "end_before" : "start_before",
		[pref](val) {
			btnImage = (enabled = val) ? imgEnabled : imgDisabled;
			this.setBtnsImg();
			checkRegistration();
		},
		setBtnsImg: () => prefObs.setBtnsImg = () => {
			var widget = cui.getWidget(btnId);
			for(var win of cui.windows)
				widget.forWindow(win).node?.setAttribute("image", btnImage);
		}
	};
	for (let p of [pref, "position_start"]) prefObs.observe(null, null, p);

	branch.addObserver("", prefObs);
	obs.addObserver(function quit(s, topic) {
		obs.removeObserver(quit, topic);
		branch.removeObserver("", prefObs);
	}, "quit-application-granted");

	//-------[ Widget ]------------------------------------------------------

	var popupId = "ucf-tst-preview-popup";
	var btnId = popupId + "-button";
	var cui = mo("CustomizableUI", "");
	var toggle = () => branch.setBoolPref(pref, !enabled);

	cui.createWidget({
		id: btnId, label, tooltiptext, localized: false,
		onCreated(btn) {
			btn._handleClick = toggle;
			btn.setAttribute("image", btnImage);
		}
	});

	//-------[ Actor ]------------------------------------------------------

	var TreeStyleTabPreviewPopupParent = class extends JSWindowActorParent {
		actorCreated() {
			var doc = this.browsingContext.topChromeWindow.document;
			var popup = doc.getElementById(popupId);
			if (!popup) {
				popup = doc.createXULElement("menupopup");
				popup.id = popupId;
				popup.setAttribute("ignorekeys", true);
				popup.setAttribute("rolluponmousewheel", true);
				popup.setAttribute("consumeoutsideclicks", "never");
				popup.shadowRoot.querySelector("style").append(`
					:host {
						padding: 0 !important;
						-moz-appearance: none !important;
					}
					arrowscrollbox::part(scrollbutton-up),
					arrowscrollbox::part(scrollbutton-down) {
						display: none !important;
					}
				`);
				(popup.canvas = popup.appendChild(doc.createElement("canvas")))
					.width = popupWidth;
				popup.context = popup.canvas.getContext("2d", {alpha: false});
				doc.getElementById("mainPopupSet").append(popup);
			}
			this.popup = popup;
		}
		receiveMessage(msg) {
			var id = msg.data;
			if (!id) return this.popup.hidePopup();

			var tab = tt.getTab(+id.slice(4));
			if (tab/* && !tab.selected*/) {
				var cwg = tab.linkedBrowser.browsingContext?.currentWindowGlobal;
				cwg && this.drawSnapshot(tab.ownerGlobal, cwg, id);
			}
		}
		async drawSnapshot(win, cwg, id) {
			var {width, height} = await cwg.getActor("Thumbnails")
				.sendQuery("Browser:Thumbnail:ContentInfo");
			if (width < 200) return;

			var k = popupWidth / width;
			try {var bitmap = await cwg.drawSnapshot(
				new DOMRect(0, 0, width, height), k, "white"
			);} catch {}

			if (!bitmap) return;
			var data = await this.sendQuery(id, win.devicePixelRatio);
			if (!data) return;

			this.popup.canvas.height = k * height;
			this.popup.context.drawImage(bitmap, 0, 0);
			bitmap.close();
			this.popup.openPopupAtScreenRect(popupPosition, ...data);

		}
		didDestroy() {
			this.popup.hidePopup();
			this.popup = null;
		}
	}
}
export {TreeStyleTabPreviewPopupParent};

export class TreeStyleTabPreviewPopupChild extends JSWindowActorChild {
	actorCreated() {
		this.args = ["mouseleave", () => {
			this.tab = null;
			this.tid || this.sendAsyncMessage("");
			this.tid = this.clearTimeout();
		}, {once: true}];
	}
	mult(val) {
		return this * val;
	}
	receiveMessage(msg) {
		var tab = this.document.getElementById(msg.name);
		var res = tab?.matches(":hover");
		if (res) {
			var {x, y, width, height} = tab.getBoundingClientRect();
			var win = tab.ownerGlobal;
			res = [
				x + win.mozInnerScreenX,
				y + win.mozInnerScreenY,
				width, height
			];
			var z = win.devicePixelRatio;
			if (z != 1 || msg.data != 1)
				res = res.map(this.mult, z / msg.data);
		}
		return res;
	}
	handleEvent(e) {
		var tab = e.target.closest("tab-item");
		if (!tab || tab == this.tab) return;
		this.clearTimeout();
		this.tid = this.contentWindow
			.setTimeout(this.onTab, timeout, this.tab = tab, this);
		tab.addEventListener(...this.args);
	}
	clearTimeout() {
		this.tid && this.contentWindow.clearTimeout(this.tid);
	}
	onTab(tab, self) {
		self.tid = null;
		tab.wrappedJSObject.apiTab.discarded
			|| self.sendAsyncMessage("", tab.id);
	}
	didDestroy() {
		this.tab = null;
	}
}

Dobrov пишет

такой глупый вопрос: JS скрипты будут дóльше поддерживаться в Firefox, чем JSM-ки?

Ну почему, не такой уж глупый, если имеются в виду
ChromeMessageBroadcaster.loadFrameScript()
и ParentProcessMessageManager.loadProcessScript()


Увы, в обозримом прошлом и настоящем,
мне как-то не попадалось ничего об их судьбе, так что сказать нечего.

возможно переделать в JS скрипт сохранения страниц SingleHTML.JSM

А вот это уже глупее.
Вместо простой конвертации в ESM, нахлобучиться затеять такую затею.


Кстати, а чего там win до сих пор торчит? fp.init(win, "", fp.modeSave);
Разве это не видел?
Для Fifefox 125+ вместо win уже нужен win.browsingContext


Vitaliy V.
Просто информационное сообщение, раз уж на глаза попалось.
Bug 1884792 - Consider making -moz-lwtheme a media query.


Удаление псевдокласса :-moz-lwtheme уже пошло в autoland,
а он используется в vertical_top_bottom_bar.css в двух местах.

Dumby пишет

Для Fifefox 125+ вместо win уже нужен win.browsingContext (Bug 1878401)

А как сделать, чтоб скрипт SingleHTML.mjs был совместим с FF115+ …… FF125+ ?
не понимаю, как этот win.browsingContext прописывать! SingleHTML править или из других скриптов вызывать иначе ?
1) fp.init(win || win.browsingContext, "", fp.modeSave);
2) Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](to, window.browsingContext)


посмотрел custom_buttons-0.0.7.0.0.33-fx. У тебя в SelfHelper.jsm:
picker(doc) { ……… var win = doc.ownerGlobal;
в SingleHTML (почти весь код твой): async SingleHTML(to, win = this.ownerGlobal)

SingleHTML.mjs (или JSM, если child: {moduleURI: __URI__})

Выделить код

Код:

/* SingleHtml © Лекс, правка Dumby, mod Dobrov
вызов: Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](to, window)
scriptsbackground [System Principal], «to» пуст: выбор пути */

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

export class SingleHTMLChild extends JSWindowActorChild { //класс = name + Child
	receiveMessage() { return htmlAndName(this.contentWindow);}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {esModuleURI: Components.stack.filename},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
		globalThis[Symbol.for(name)] = this.SingleHTML; //общие функции
		globalThis[Symbol.for('TitlePath')] = this.TitlePath;
	},
	observe(win) {
		win.document.getElementById("appMenu-popup").addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.setAttribute("label", "Всё или выбранное в единый HTML");
		var before = "appMenu-save-file-button2", subviewbutton = "subviewbutton";
		btn.className = subviewbutton;
		btn.setAttribute("oncommand", "SingleHTML();");
		btn.SingleHTML = this.SingleHTML;
		popup.querySelector('toolbarbutton[id^="'+ before +'"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup").removeEventListener("popupshowing", this);
	},
	TitlePath(win, to, f, u, n = 0, h = 99) { //global
		if(parseInt(to) > 0) [n,to] = [to,n]; if(parseInt(to) < 0) h = Math.abs(to);
		if (typeof(to) != 'string' || !/.*\|/.test(to)) to = prefs.getStringPref("extensions.user_chrome_files.savedirs","|||0");
		to = to.split('|').slice(0 + n, 2 + n); //Dir/Sub|[empty|0 title|1 url]
		f ||= win.gBrowser.selectedTab.label;
		f = f.replace(/\s+/g,' ').replace(/[\\\/?*\"'`]+/g,'').replace(/[|<>]+/g,'_').replace(/:/g,'։').slice(0,h).trim();
		u ||= decodeURIComponent(win.gURLBar.value); n = f, h = u;
		u = /^file:\/\//.test(u) ? 'file' : u.replace(/^.*u=|https?:\/\/|www\.|\/.*/g,'').replace(/^ru\.|^m\./,'').replace(/\/.*/,'');
		to[1] = (to[1] == "0") ? f : (to[1] == "1") ? u : "";
		f += "_"+ new Date().toLocaleDateString('ru', {day: 'numeric',month: 'numeric',year: '2-digit'}) +'-'+ new Date().toLocaleTimeString('en-GB').replace(/:/g,"։"); //дата-часы
		try {var dir = dirsvc.get("DfltDwnld",Ci.nsIFile);} catch {dir = prefs.getComplexValue("browser.download.dir",Ci.nsIFile)}
		var map = l => win.DownloadPaths.sanitize(l); //FIX имён
		to.map(map).forEach(dir.append);
		to = dir.clone(); to.append(f +'.html');
		return [dir, to.path, n, f, h, u]; //… имя, +дата, URL, домен
	},
	async Succes(win, dir, dw = true, bg) {
		var {setTimeout} = ChromeUtils.import("resource://gre/modules/Timer.jsm");
		var d = await win.Downloads.createDownload({source: "about:blank",target: win.FileUtils.File(dir)});
		(await win.Downloads.getList(win.Downloads.ALL)).add(d);
		if (dw) await d.refresh(d.succeeded = true); //flash DWButton
		d = win.document.getElementById('urlbar-input-container');
		d.style.background = dw ? 'rgba(0,200,0,0.3)' : 'rgba(250,0,0,0.2)';
		setTimeout(() => {d.style.removeProperty('background-color')}, 350);
	},
	async SingleHTML(to, win = this.ownerGlobal) {
		var br = win.gBrowser.selectedBrowser, bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;
		var actor = bc?.currentWindowGlobal?.getActor(name);
		actor && self.save(win, ...await actor.sendQuery(""), to); //htmlAndName
		},
	async save(win, data, fname, host, to) {
		var path = this.TitlePath(win, to, fname, host); //путь в зависимости от опций
		var dir = path[0], path = path[1];
		dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777);
		if (!to) { // диалог выбора папки
			var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
			fp.init(win, "", fp.modeSave);
			fp.defaultString = path.split(/.*[\/|\\]/)[1];
			fp.appendFilters(fp.filterHTML); fp.appendFilters(fp.filterAll);
			var res = await new Promise(fp.open);
			if (res == fp.returnOK || res == fp.returnReplace)
				path = fp.file.path
			else return;
		}
		this.write(path, data); //нужна проверка на ошибки записи
		await this.Succes(win, path);
	},
	write(path, html) { //без Ff 79-84 в save() IOUtils.writeUTF8 вместо this.write
		if (typeof IOUtils == "object")
			var write = IOUtils.writeUTF8 || IOUtils.writeAtomicUTF8; // Fx 85+ || 82-84
		if (!write) { // Fx 79-81
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			write = (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt));
		}
		(this.write = write)(path, html);
	}
}).init("browser-delayed-startup-finished");

var htmlAndName = async mainWin => { //не сохраняет SVG

	var resolveURL = function (url, base) {
		try { return io.newURI(url, null, io.newURI(base)).spec;} catch {}
	},
	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) {}
		}
	},
	encodeImg = function (src, obj) {
		var canvas, img, ret = src;
		if (/^https?:\/\//.test(src)) {
			canvas = doc.createElement('canvas');
			if (!obj || obj.nodeName.toLowerCase() != 'img') {
				img = doc.createElement('img');
				img.src = src;
			} else
				img = obj;
			if (img.complete) try {
				canvas.width = img.width;
				canvas.height = img.height;
				canvas.getContext('2d').drawImage(img, 0, 0);
				ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
			} catch (e) {};
			if (img != obj) img.src = 'about:blank';
		};
		return ret;
	},
	toSrc = function (obj) {
		var strToSrc = function (str) {
			var chr, ret = '', i = 0, meta = {'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','\x22':'\\\x22','\\':'\\\\'};
			while (chr = str.charAt(i++)) {
				ret += meta[chr] || chr;
			};
			return '\x22' + ret + '\x22';
		},
		arrToSrc = function (arr) {
			var ret = [];
			for (var i = 0; i < arr.length; i++) {
				ret[i] = toSrc(arr[i]) || 'null';
			};
			return '[' + ret.join(',') + ']';
		},
		objToSrc = function (obj) {
			var val, ret = [];
			for (var prop in obj) {
				if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
			};
			return '{' + ret.join(',') + '}';
		};
		switch (Object.prototype.toString.call(obj).slice(8, -1)) {
			case 'Array': return arrToSrc(obj);
			case 'Boolean':
			case 'Function':
			case 'RegExp': return obj.toString();
			case 'Date': return 'new Date(' + obj.getTime() + ')';
			case 'Math': return 'Math';
			case 'Number': return isFinite(obj) ? String(obj) : 'null';
			case 'Object': return objToSrc(obj);
			case 'String': return strToSrc(obj);
			default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
		}
	},
	selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location,
	ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g;

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

	for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
		el = all[i];
		if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
			if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
			return prev + encodeImg(url) + next;
		});
		switch (el.nodeName.toLowerCase()) {
			case 'link':
			case 'style':
			case 'script': el.parentNode.removeChild(el); break;
			case 'a':
			case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
			case 'img':
			case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
			case 'audio':
			case 'video':
			case 'embed':
			case 'frame':
			case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
			case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
			case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
		}
	};
	var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets;
	meta.httpEquiv = 'content-type';
	meta.content = 'text/html; charset=utf-8';
	head.appendChild(meta);
	var title = doc.getElementsByTagName('title')[0];
	if (title) head.appendChild(title.cloneNode(true));

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var onlyName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	return [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', onlyName, loc.hostname];
}