Dumby
Здравия! Сделайте пожалуйста что бы этот скрипт: https://forum.mozilla-russia.org/viewto … 03#p776503 работал в актуальных версиях [firefox] !


P,S, и только для открытия истории из журнала и боковой панели в новой вкладке.

kokoss
У меня нет userChrome.js, нужно железное обоснование (и ссылка),
почему именно userChrome.js, ведь у тебя есть UCF.


Недавно же sandro79 обращал наше внимание на то,
что в 96 отвалился ucf_wheretoopenlink.js. А отвалился он потому,
что Bug 1742801 «move whereToOpenLink and getRootEvent implementations into BrowserUtils».


Я тогда попытался набросать новый вариант, и вот теперь уже,
переделать под только Журнал (боковушка ведь тоже озаглавлена как Журнал).
Так что, если хочешь, попробуй (это в custom_script.js).

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

Выделить код

Код:

(async sel => {
	var trees = ["places", "historySidebar"];
	var url = "resource://gre/modules/BrowserUtils.jsm";
	var bu = ChromeUtils.import(url).BrowserUtils, {whereToOpenLink} = bu;

	bu.whereToOpenLink = function(e) {
		var res = whereToOpenLink.apply(bu, arguments);
		if (res != "current" || !Event.isInstance(e)) return res;
		try {
			var skip = true, trg = e.composedTarget, win = trg.ownerGlobal;
			var name = win.document.documentURIObject
				.QueryInterface(Ci.nsIURL).fileName.slice(0, -6);

			if (name == "browser")
				skip = win.gBrowser.selectedTab.isEmpty || !trg.closest(sel);

			else if (trees.includes(name))
				skip = (win.opener || win.windowRoot.ownerGlobal).gBrowser.selectedTab.isEmpty
					|| trg.closest("tree").selectedNode.itemId != -1;

			return skip ? res : "tab";
		}
		catch {return res;}
	}
})("#historyMenuPopup,#PanelUI-history");

Dumby
Класс, при чём работает и в старой версии UCF!!! Благодарю :beer:


Add, всё же придётся перейти на актуальную версию UCF, в ней этот скрипт работает правильно!

Dumby пишет

Так что, если хочешь, попробуй (это в custom_script.js)

Благодарю за замену для ucf_wheretoopenlink.js для 96+
Работает по журналу отлично: с окна библиотеки, с боковой панели, с меню бутерброда, с вкладки chrome://browser/content/places/places.xhtml, в общем везде где есть журнал.
Работает как с user_chrome_files, так и с этим комплектом - одинаково, что меня очень обрадовало. Надо будет ещё с этим комплектом потестить.
Dumby, ну сделайте пожалуйста подобный компактный (873 байт) скрипт для версий [firefox] < 96. По идее должен будет наверно и в 78 работать.
И огромноая благодарность Вам за кнопку «вывести на кнопку индикацию muted-состояния активной вкладки» Тоже забрал на замену, работает отлично!

sandro79 пишет

Работает как с user_chrome_files, так и с этим комплектом - одинаково, что меня очень обрадовало.

А меня это огорчает. Сколько раз было сказано,
что коды для для custom_script.js не следует совать в окна.
Но, видимо, это слишком сложная мысль.
Я даже не понимаю зачем это может понадобиться,
какой-то поиск приключений браузеру на ...

подобный компактный (873 байт) скрипт для версий [firefox] < 96

Кстати, в скрипте есть баг: в Библиотеке, в закладках,
в новой вкладке также открывается добро исторических кверей,
типа «Двадцать часто посещаемых», а это не есть Журнал.


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


Код для custom_script_win.js (вот его можно во всяких там комплектах проверять).
Набирал из-под 77, в 98 вроде тоже работает.

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

Выделить код

Код:

(async sel => {
	var wtol = whereToOpenLink;
	whereToOpenLink = function whereToOpenLink(e) {
		var res = wtol.apply(window, arguments);
		return res == "current" && !gBrowser.selectedTab.isEmpty &&
			Event.isInstance(e) && e.composedTarget.closest(sel) ? "tab" : res;
	}
	var puu = PlacesUIUtils, key = "openNodeWithEvent";
	if (puu[key].name.startsWith("b")) return;

	var bt = Object.create(puu), bwt = BrowserWindowTracker;
	bt._openNodeIn = (node, where, win) => {
		if (where == "current" && node.itemId == -1 &&
			!(win.opener || bwt.getTopWindow()).gBrowser.selectedTab.isEmpty
		)
			where = "tab";
		puu._openNodeIn(node, where, win);
	}
	puu[key] = puu[key].bind(bt);
})("#goPopup,#historyMenuPopup,#PanelUI-history");

Dumby пишет

А меня это огорчает. Сколько раз было сказано, что коды для для custom_script.js не следует совать в окна

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

типа «Двадцать часто посещаемых», а это не есть Журнал

Да это не страшно, мелочи, пусть как фича будет.

Код для custom_script_win.js (вот его можно во всяких там комплектах проверять)

Скрипт подключал в custom_script_win.js в 69 и 78, в 91 и 98 в CustomStylesScripts.jsm в scriptschrome: { // Для докум. окна браузера [ChromeOnly]
Везде всё проверил, вроде всё как надо, работает везде по журналу, ну по мне - получилось отлично.
В основном использую журнал из автоскрываемой боковой панели, с журнала панели меню и значка в виде часов, размещённого на панели меню.
Огромное Спасибо! :beer:

как сделать чтоб было как в Центе нажать правой кнопкой на ссылку и было чтоб Скопировать текст ссылки
5.1692165433.png

казявка
Уж не знаю что за Цент,
а для лисы как-то так, наверно

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

Выделить код

Код:

(async (id, url) => {
	if (location != url) return;
	var menuitem = document.createXULElement("menuitem");
	document.getElementById(id).after(menuitem);
	var hidden = () => !nsContextMenu.contentData.context.linkTextStr;	
	menuitem.hidden = true;
	menuitem.render = () => {
		if (hidden()) return;
		menuitem.hidden = false;
		menuitem.id = id + "text";
		menuitem.label = "Скопировать текст ссылки";
		menuitem.setAttribute("oncommand", "navigator.clipboard.writeText(gContextMenu.linkTextStr);");
		delete menuitem.render;
		menuitem.render();
		menuitem.render = () => menuitem.hidden = hidden();
	}
})("context-copylink", "chrome://browser/content/browser.xhtml");

Dumby
ВЫ волшебник! я неделю наверно  искал в гугле в яндексе как так сделать и не нашел и пришлось ставить расширение Copy Link Text - а ВЫ на коленке сделали мою мечту - кладу ВАМ в карман огромное БЛШ СПС!

Dumby пишет

а для лисы как-то так, наверно

Если этот скрипт прописать в userChrome.js от Aris-t2 или Endor8, то при каждом открытии главной консоли (Ctrl + Shift + J) такая ошибка:
Uncaught (in promise) TypeError: document.getElementById(...) is null
хотя это не мешает работе, т.е. текст ссылки копируется

6e73epo пишет

Uncaught (in promise) TypeError: document.getElementById(...) is null

Да, точно! userChrome же.
Следует проверять, что это именно документ окна браузера.
Я избалован UCF'ским custom_script_win.js, где такая проверка не требуется,
вот и вылетело из головы. Подправил.

@Dumby
Здравствуйте. У вас есть скрипт под ucf, добавляющий функцию открытия встроенного родного прогресса загрузки файла при старте загрузки, а не по ее окончании
https://forum.mozilla-russia.org/viewto … 32#p801732
В Firefox v115 он уже не работает, но я даже не об этом. Просьба адаптировать его, чтобы он работал в среде Aris-t2. Благодарю. Речь о встроенном окне загрузки в виде поп-апа, а не о классическом самостоятельном окне загрузки.

fuchsfan пишет

Здравствуйте. У вас есть скрипт под ucf, добавляющий функцию открытия встроенного родного прогресса загрузки файла при старте загрузки, а не по ее окончании
https://forum.mozilla-russia.org/viewto … 32#p801732
В Firefox v115 он уже не работает

Ну почему не работает, у меня работает!
:offtopic:

fuchsfan пишет

Просьба адаптировать его, чтобы он работал в среде Aris-t2.

Почему у тебя в sandbox'е не работает я без понятия.
А в окно можно попробовать, например, так

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

Выделить код

Код:

(async (flag, url) => {
	if (location != url) return;
	await delayedStartupPromise;
	if (Downloads[flag]) return;
	Downloads[flag] = true;

	var bwt = BrowserWindowTracker;
	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 Downloads.getList(Downloads[type])).addView(view);
	});
})("806555", "chrome://browser/content/browser.xhtml");

Dumby пишет

А в окно можно попробовать, например, так

Благодарю, работает как надо. И с Aris-t2, и с UCF...

leshiy_odessa пишет

Решил вернуть себе нижнюю панель по типа AddonBar.

Эта панель работает в v117, v118b7, v119a1. На пару строчек ниже есть эта же панель с кнопкой скрыть\показать панель. Здесь https://forum.ru-board.com/topic.cgi?fo … tart=220#4 еще одна работающая нижняя панель с очень маленьким кодом в 20 строк. Там же в шапке описание активации скриптов методом Aris-t2, при внимательном выполнении совершенно не вызывает трудностей, я бы удалил все и выполнил повторно.

fuchsfan пишет

Эта панель работает в v117, v118b7, v119a1. На пару строчек ниже есть эта же панель с кнопкой скрыть\показать панель.

А вы можете показать скриншот этой кнопки или написать где её искать?

fuchsfan пишет

я бы удалил все и выполнил повторно.

Дя я уже несколько раз всё перепроверял и переустанавливал. Есть подозрение что где-то конфликт.

leshiy_odessa пишет

А вы можете показать скриншот этой кнопки или написать где её искать?

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

fuchsfan пишет

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

Да спасибо, у меня только что заработало с кнопки Restart, а потом начал добавлять остальное. Где я ошибся так и не понял.
Спасибо за помощь.

update И снова всё пропало. :(

 
update2 Како-то ломаю синтаксис — userChrome.js

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");

Открыть папку Chrome, Profiles хоткеем

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

Выделить код

Код:

// OpenChromeFolder(Alt+C).uc.js

(function(win){
    function openChromeDirectory() {
      // Get the chrome directory.
      let currUChrm = Services.dirsvc.get("UChrm", Ci.nsIFile);
      let chromeDir = currUChrm.path;
      // Show the chrome directory.
      let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1","nsIFile", "initWithPath");
      new nsLocalFile(chromeDir).reveal();
    }
    if (typeof win.openChromeDirectory == 'undefined') {
        win.openChromeDirectory = openChromeDirectory;
        win.addEventListener('keydown', function(e) {
            if (e.altKey == true && e.keyCode == 67) {
                e.preventDefault();
                openChromeDirectory();
            }
        }, false);
    }
})(window);

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

Выделить код

Код:

// OpenProfileFolder(Alt+P).uc.js

(function(win){
    function openProfileDirectory() {
        Components.classes["@mozilla.org/file/directory_service;1"]
              .getService(Components.interfaces.nsIProperties)
              .get("ProfD", Components.interfaces.nsIFile)
              .launch();
    }
    if(typeof win.openProfileDirectory == 'undefined') {
        win.openProfileDirectory = openProfileDirectory;
        win.addEventListener('keydown', function(e) {
            if (e.altKey == true && e.keyCode == 80) {
                e.preventDefault();
                openProfileDirectory();
            }
        }, false);
    }
})(window);

rubel пишет

Dumby
Вроде на форуме нет темы по скриптам, подключаемых по методам
метод Aris-t2   метод xiaoxiaoflood   метод Endor8
Может нужно создать тему по этим методам.

А что по названию темы непонятно... ?!

rubel пишет

Применяю метод Endor8 для загрузки скриптов.

То есть, насколько могу судить, скрипты грузятся
в top-level окна документов с адресом с протоколом chrome://
и по событию "load".


В первом приближении, перерисовал так

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && (async id => {
	var g = Cu.getGlobalForObject(Cu);
	g[id] || Services.scriptloader.loadSubScript("data:charset=utf-8," + encodeURIComponent(`(${id => this[id] = {

		delay: 2e3,

		val: "",
		init(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()"
				}"><label id="${id += "-label"}"/></hbox>`
			);
			this.cui = win.CustomizableUI;
			var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
			this.next = timer.initWithCallback.bind(null, this, this.delay, timer.TYPE_ONE_SHOT);

			(this.init = win => {
				timer.cancel();
				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.next();

			var prev = this.val;
			if ((this.val = this.mgb(bytes)) != prev)
				for(var win of this.cui.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"

	}})("${id}");`), g);

	g[id].init(window);
})("urlbar-memory-indicator");

Dumby
Огромное спасибо, работает превосходно. :)

Dumby, существует ли возможность на кнопке в onCommand: function(event) {...)  прописать команды, чтобы кнопка открывала определенный раздел реестра через запуск regedit? Просто ответьте да или нет.

Вит

Вит пишет

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

Эта боковая панель входит в состав UCF, выдирается ли она оттуда как самостоятельный скрипт, не знаю. Если у вас UCF не установлен, то вот вам боковая панель от Aris-t2 https://github.com/Aris-t2/CustomJSforF … ical.uc.js

Вит пишет

Не появляется, видимо из-за конфликта

У вас v115, видимо, вам нужна версия скрипта постарее https://github.com/Aris-t2/CustomJSforF … ical.uc.js. Та, что в посту выше, исправлена автором для v117 и выше. Оба скрипта на своих версиях активируются совершенно легко и без бубна.

6e73epo пишет

Dumby, существует ли возможность на кнопке в onCommand: function(event) {...)  прописать команды, чтобы кнопка открывала определенный раздел реестра через запуск regedit? Просто ответьте да или нет.

Ну, как сказать, вот состряпал тестовую кнопку, и мне открывает.


Если использовать launch(); то открывает
только с нуля, то есть когда regedit не запущен,
иначе просто активируется окно, в котором открыто то, что открыто.


А если прицепить ключ «/m», то открывает всегда, но всегда в новом окне.


Вобщем, ответ, скорее «да».

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

Выделить код

Код:

(async (regedit, file) => CustomizableUI.createWidget({
	id: "regedit-opener",
	label: "RegEdit",
	localized: false,
	onCreated(btn) {
		file = Services.dirsvc.get("WinD", Ci.nsIFile);
		file.append("regedit.exe");
		var img = "moz-icon:file://" + file.path;
		(this.onCreated = btn => btn.image = img)(btn);
	},
	onCommand() {

		var path = "Компьютер\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Mozilla";

		var wrk = Cc["@mozilla.org/windows-registry-key;1"].createInstance(Ci.nsIWindowsRegKey);
		wrk.open(wrk.ROOT_KEY_CURRENT_USER, regedit, wrk.ACCESS_WRITE);
		wrk.writeStringValue("LastKey", path);

		//file.launch();

		var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
		process.init(file);
		process.runwAsync(["/m"], 1);
	}
}))("Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit");

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

Совсем забыл про "WinD" и делал типа такого: Services.env.get("windir") + "\\regedit.exe". А вместо wrk.open и process.runwAsync прописывал соответственно излишнее wrk.create и process.run

Теперь по вашему коду:
Без проверки на окно пишет, что CustomizableUI не определен. А почему ошибок нет через try {} catch(e) {}? Получается, что при запуске браузера идет как минимум 2 обращения и какое-то из них с ошибкой, но мы ее не видим? Или еще как-то писали про возвращение promise и при try это происходит позже на этапе, когда уже CustomizableUI инициализирован?
Компьютер\\ можно убрать, это автоматом дописывается.
wrk.close() стоит добавить или нет необходимости?
Если runwAsync без аргументов, то правильней ([], 0) или (null, null)?

6e73epo пишет

Без проверки на окно пишет, что CustomizableUI не определен.

Ну, я-то, разумеется, в custom_script.js добавил.
Вызывать CustomizableUI.createWidget() следует только один раз,
поэтому в окнах такой код слегка неуместен.


Если нет выбора, то хотя бы следует проверять, что виджет уже создавался.
Типа флаг какой-нибудь куда-нибудь поставить, или, может проверять, что
CustomizableUI.getWidget(id).provider равно (или не равно) "api"


К тому же, если коде самого виджета есть что-то связанное с promise, какой-то async, то если
закрыть окно браузера в котором виджет создавался, то этот функционал перестанет работать.

А почему ошибок нет через try {} catch(e) {}? Получается, что при запуске браузера идет как минимум 2 обращения и какое-то из них с ошибкой, но мы ее не видим?

Видим мы ошибку, или не видим, зависит от того, что находится в блоке catch.
Если там что-то типа Cu.reportError(e) или console.error(e), то дожно быть видно.
А если блок catch пустой, то тогда неудивительно, на то он и try.


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

Или еще как-то писали про возвращение promise и при try это происходит позже на этапе, когда уже CustomizableUI инициализирован?

Нет, try никак не должен влиять. Да это и не важно.
Первый же инлайн скрипт в browser.xhtml подгружает скрипт browser.js
который устанавливает в окно кучу геттеров модулей, среди которых и CustomizableUI.


То есть, это ещё до события "DOMContentLoaded".
Не думаю, что существует какой-либо uc-загрузчик, который подгружает скрипты так рано.
Ноборот, дожидаются этого сбытия, или даже события "load".

Компьютер\\ можно убрать, это автоматом дописывается.

Хорошая находка.
Я-то просто написал по-аналогии с тем, что там увидел.

wrk.close() стоит добавить или нет необходимости?

О, кстати да, лучше добавить.

Если runwAsync без аргументов, то правильней ([], 0) или (null, null)?

Как правильней мне, конечно, не известно, но если метод принимает null
без негативных последствий, то и хорошо, не создаётся ненужный пустой массив.
(null, 0) выглядит логичней.
С другой стороны, если без аргументов, то почему бы не просто file.launch();
возможно, есть приемущество в асинхронном вызове, типа UI браузера не подвиснет,
пока он за файлом сходит, не знаю.

Dumby, благодарю за подробные ответы. Добавил проверку на окно, теперь ошибок нет. Думаю, что дополнительных проверок не требуется и виджет повторно не запустится, иначе будет ошибка нормализации. Дополнительных окон никогда не открываю.
Состряпал кнопку для открытия папки Roaming. Не знаю, правильно или нет, но работает и без ошибок. Иконки не нужны, т.к. вывожу кнопки на доп. панель в текстовом виде

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

Выделить код

Код:

// Import from userChrome.js
location.href.endsWith("://browser/content/browser.xhtml") && (async (file) => CustomizableUI.createWidget({
	id: "openfolder-roaming",
	label: "roaming",
	tooltiptext: "Open Users/./Roaming",
	localized: false,
	onCreated() {
		file = Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent.parent;
	},
	onCommand() {
		if (file.exists()) file.launch();
	}
}))();

6e73epo пишет

Состряпал кнопку для открытия папки Roaming. Не знаю, правильно или нет, но работает и без ошибок.

Возможно, стоит добавить, что вроде как есть свойство "AppData"
по которому Services.dirsvc.get() отдаст папку Roaming сразу,
и обращение к parent'ам не потребуется.


И добавить, что это всё, конечно, должно подходить для установленной версии,
но вот, например, у меня портабельные лисицы на libportable,
так вот там, ни "XREUSysExt", ни "AppData", да вообще почти ничего,
никакой папки Roaming в пути не содержат.


Насколько я вижу, остались только "Progs" (четыре раза parent) и "CookD" (три раза parent).
Ещё можно от "Home" прибавить AppData и Roaming.
Ну, это я в том смысле, что если стоит задача получить с такой лисицы именно «Users/./Roaming».

Dumby, в линуксе и маке вроде должны работать "XREUserNativeManifests" и "XRESysNativeManifests"

6e73epo пишет

в линуксе и маке

Об этих ничего не знаю.
Но попробовал сейчас на Debian, и показывает так:


XRESysNativeManifests —> /usr/lib/mozilla
XREUserNativeManifests —> /home/dumby/.mozilla


Мне это ни о чём не говорит :).

Dumby
А возможно ли приспособить скрипт SingleHTML.jsm в загрузчик метода Endor8 ?
Очень нужный скрипт.

paths — путь до папки со скриптами (.js, .jsm, mjs), в данном случае %Профиль%\chrome\widget\
content — хром-регистрация на эту папку, в данном случае chrome://widget/content/

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

Выделить код

Код:

// UCF In background [System Principal] by Dumby, tested FF 115+
(async topic => {
	let ucf = async doc => {
		try { (async (xpc, cui) => {
			let paths = ["chrome", "widget"], content = "widget";

			let {ChromeUtils, Services} = Cu.getGlobalForObject(Cu),
				imp = ChromeUtils.import, impESM = ChromeUtils.importESModule,
				{XPCOMUtils} = impESM(xpc), {CustomizableUI} = impESM(cui);
			let sb = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(),
				{wantComponents: true, sandboxName: "ucfBox", sandboxPrototype: globalThis,});
			Object.assign(sb, {Services, XPCOMUtils, CustomizableUI, ChromeUtils});
		
			XPCOMUtils.defineLazyGlobalGetters(sb, [
				"atob", "btoa", "crypto", "fetch", "Blob", "CSS", "CSSRule", "Document",
				"DOMException", "DOMParser", "Element", "Event", "File", "FileReader", "FormData",
				"Headers", "InspectorUtils", "Node", "NodeFilter", "PathUtils", "Range", "Selection",
				"TextDecoder", "TextEncoder", "URL", "URLSearchParams", "XMLHttpRequest", "XMLSerializer"
			]);
			let m = {console: "Console"};
			m.AddonManager = m.AppConstants = m.E10SUtils = m.FileUtils = m.PlacesUtils = false;
			for (let [key, val] of Object.entries(m)) m[key] = `resource://gre/modules/${val || key}.sys.mjs`;
			m.setTimeout = m.setTimeoutWithTarget = m.clearTimeout = m.setInterval
				= m.setIntervalWithTarget = m.clearInterval = "resource://gre/modules/Timer.sys.mjs";
			ChromeUtils.defineESModuleGetters(sb, m);

			let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
			paths.forEach(dir.append);
			let ams = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
			sb[Symbol()] = ams.registerChrome(Services.io.newFileURI(dir), [["content", content, paths.pop() + "/"]]);
		
			let re = /\.(m)?js(m)?$/, lss = Services.scriptloader.loadSubScript, prfx = `chrome://${content}/content/`;
			for(let {leafName} of dir.directoryEntries) if (re.test(leafName))
				try {RegExp.$1 ? impESM(prfx + leafName) : RegExp.$2 ? imp(prfx + leafName) : lss(prfx + leafName, sb);} catch(ex) {Cu.reportError(ex);}
		})("resource://gre/modules/XPCOMUtils.sys.mjs", "resource:///modules/CustomizableUI.sys.mjs");
		} catch(ex) {Cu.reportError(ex);}
	}
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;
	let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
	obs.addObserver(ucf, topic, false);
	obs.addObserver(function quitucf(aDoc, qtopic) {
		obs.removeObserver(quitucf, qtopic);
		obs.removeObserver(ucf, topic);
	}, "quit-application-granted");
})("profile-after-change");

rubel, загрузчик Endor8 не рекомендую, а именно когда папка userChromeJS в core находится.

Farby пишет

Проще говоря закинул в папку и работает, но не забываем после изменения чистить startupCache

У меня из установленных 30 активировались 10, допускаю, что мог допустить ошибку.

6e73epo пишет

загрузчик Endor8 не рекомендую, а именно когда папка userChromeJS в core находится.

Чтобы "тупо" бекапить не только папку профиля, как все делают всю жизнь, но и папку core?

fuchsfan пишет

У меня из установленных 30 активировались 10, допускаю, что мог допустить ошибку.

Ну конечно у многих скриптов есть вызовы типа

Выделить код

Код:

chrome://user_chrome_files/content/custom_scripts/...

и в этом есть подвох! Надо менять на

Выделить код

Код:

chrome://widget/content/...

Farby
Ваш код я применил для файла System Principal.uc.js. Поместил его в папку со скриптами
D:\\Firefox 115.2.0esr\\Profiles\\chrome\\Scripts"
В папке chrome создал папку widget в неё поместил файл SingleHTML.jsm
Что еще нужно, растолкуйте, пожалуйста.
И что делать с кодом от Dumby ?

fuchsfan пишет

Чтобы "тупо" бекапить не только папку профиля, как все делают всю жизнь, но и папку core?

С загрузчиком Endor8 , хоть скрипты и работают, идут ошибки в главной консоли, не помогает даже проверка на окно. Их не видно, потому что  catch(e) пустой.

rubel
Не в папку со скриптами, а добавить в config.js который расположен рядом с firefox.exe

Farby

В папке chrome создал папку widget в неё поместил файл SingleHTML.jsm
Вот такой сейчас config.js

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

Выделить код

Код:

// config.js

try {
  Cu.importGlobalProperties(['PathUtils']);

  if (!Services.appinfo.inSafeMode) {
    let path = PathUtils.parent(PathUtils.xulLibraryPath);
    if (Services.appinfo.OS == 'Darwin') { // macOS
      path = PathUtils.join(PathUtils.parent(path), 'Resources');
    }
    var ucjsDirPath = PathUtils.join(path, 'userChromeJS');
    path = PathUtils.join(ucjsDirPath, 'main.js');
    const mainFileURI = PathUtils.toFileURI(path);
    Services.scriptloader.loadSubScript(mainFileURI, this, 'UTF-8');
  }
}
catch(e) {
  Cu.reportError(e);
}

// UCF In background [System Principal] by Dumby
(async topic => {
	let ucf = async doc => {
		try { (async (xpc, cui) => {
			let paths = ["chrome", "widget"], content = "widget";

			let {ChromeUtils, Services} = Cu.getGlobalForObject(Cu),
				imp = ChromeUtils.import, impESM = ChromeUtils.importESModule,
				{XPCOMUtils} = impESM(xpc), {CustomizableUI} = impESM(cui);
			let sb = Cu.Sandbox(Cu.getObjectPrincipal(this), {wantComponents: true, sandboxName: "ucfBox"});
			Object.assign(sb, {Services, XPCOMUtils, CustomizableUI, ChromeUtils});
		
			XPCOMUtils.defineLazyGlobalGetters(sb, [
				"atob", "btoa", "crypto", "fetch", "Blob", "CSS", "CSSRule", "Document",
				"DOMException", "DOMParser", "Element", "Event", "File", "FileReader",
				"FormData", "Headers", "InspectorUtils", "Node", "NodeFilter", "Range", "Selection",
				"TextDecoder", "TextEncoder", "URL", "URLSearchParams", "XMLHttpRequest", "XMLSerializer"
			]);
			let m = {console: "Console"};
			m.AddonManager = m.AppConstants = m.E10SUtils = m.FileUtils = m.PlacesUtils = false;
			for (let [key, val] of Object.entries(m)) m[key] = `resource://gre/modules/${val || key}.sys.jsm`;
			m.setTimeout = m.setTimeoutWithTarget = m.clearTimeout = m.setInterval
				= m.setIntervalWithTarget = m.clearInterval = "resource://gre/modules/Timer.sys.mjs";
			ChromeUtils.defineESModuleGetters(sb, m);

			let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
			paths.forEach(dir.append);
			let ams = Cc["@mozilla.org/addons/addon-manager-startup;1"].getService(Ci.amIAddonManagerStartup);
			sb[Symbol()] = ams.registerChrome(Services.io.newFileURI(dir), [["content", content, paths.pop() + "/"]]);
		
			let re = /\.(m)?js(m)?$/, lss = Services.scriptloader.loadSubScript, prfx = `chrome://${content}/content/`;
			for(let {leafName} of dir.directoryEntries) if (re.test(leafName))
				try {RegExp.$1 ? impESM(prfx + leafName) : RegExp.$2 ? imp(prfx + leafName) : lss(prfx + leafName, sb);} catch(ex) {Cu.reportError(ex);}
		})("resource://gre/modules/XPCOMUtils.sys.mjs", "resource:///modules/CustomizableUI.sys.mjs");
		} catch(ex) {Cu.reportError(ex);}
	}
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;
	let obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
	obs.addObserver(ucf, topic, false);
	obs.addObserver(function quit(s, t) {
		obs.removeObserver(quit, t);
		obs.removeObserver(ucf, topic);
	}, "quit-application-granted");
})("profile-after-change");


Почистил startupCache, меню не появляется. Где ещё посмотреть ?

rubel пишет

Где ещё посмотреть ?

файл точно называется SingleHTML.jsm, а не как нибудь так SingleHTML.jsm.txt?
url: chrome://widget/content/SingleHTML.jsm открывается?

Farby

url: chrome://widget/content/SingleHTML.jsm открывается?

Сейчас открывается, но все равно меню не появляется.

rubel
В меню гамбургера точно нет?

Farby
Точно нет.

rubel
Тогда последняя догадка, код скрипта должен быть в кодировке utf-8
chrome://widget/content/SingleHTML.jsm должны читаться русские буквы.

Farby
Русские буквы читаются. Я же скачал этот файл у Доброва.

02-10-2023 15:17:31
Farby
Все заработало! Оказывается сейчас с  https://github.com/VicDobrov/UserChromeFiles/blob/main/profile_ucf_dobrov/chrome/user_chrome_files/custom_scripts/SingleHTML.jsm скачивается файл размером 159 кб. А раньше я качал этот файл размером 13 кб.
Вот он и заработал при замене. Спасибо Вам за помощь. Будут вопросы тогда задам.

Farby
Нет, этот скрипт нормально не работает.
Открываю  Firefox, открываю вкладку, на ней скрипт работает. Можно сохранить. Открываю вторую вкладку, на ней скрипт не работает.
Это поправимо ?

rubel пишет

скачивается файл размером 159 кб. А раньше я качал этот файл размером 13 кб.

SingleHTML.0.png

rubel пишет

Это поправимо ?

Да конечно, этот файл весит всего 11,9 KB, и его можно забрать здесь
Рекомендую, перезагрузить ваше устройство с полной переустановкой системы, на макос это делается так...

rubel пишет

Да, глючит этот скрипт с загрузкой метод Aris-t2  и метод Endor8, хотя в UCF работает прекрасно.

хотя рекомендую, хотя в UCF работает прекрасно

Видимо, UCF скрипты в неизменном виде нормально работать не будут.
Кроме того, один SingleHTML.jsm мало полезен! Из меню запускать скрипт неудобно.
Нужен ucf_hookClicks.js, чтобы сохранять страницы по клику на кнопке Загрузки или по сочетанию клавиш.


Farby пишет

Рекомендую, перезагрузить ваше устройство с полной переустановкой системы, на макос это делается так...

Это шутка? Только винда требует перестановки при различных глюках системы. МакOS, также как Линукс, не имеет таких недостатков.

Farby

Да конечно, этот файл весит всего 11,9 KB, и его можно забрать здесь...

Его и использую сейчас.
Dobrov

Ясно. Может быть уважаемый Dumby поможет в этом вопросе. Подождем.

rubel - под [System Principal] есть много полезных .
Переделка UCF и CustomButtons скриптов под userChrome.js не гарантирует их правильную работу. Может лучше установить UCF ???

Dobrov
Дело в том, что UCF уже давно не обновляется автором. А искать его исправленную версию разными умельцами под новые выпуски Firefox тот ещё гемморой. Как то так.
Хотя я его использую на 91esr и на тестовой 115esr.

rubel пишет

Открываю  Firefox, открываю вкладку, на ней скрипт работает. Можно сохранить. Открываю вторую вкладку, на ней скрипт не работает.

Хмм :/, я вижу это. Слетает хром-регистрация.
Она и должна слетать, а чтобы не слетала нужно где-то сохранить на неё ссылку.
И так и сделано, ссылка добавлена в ucfBox, но это, почему-то, не помогает.


Перенёс ссылку из ucfBox'а в конфигский сандбокс, и теперь, вроде, не слетает.
Вобщем, попробуй заменить sb[Symbol()] на globalThis[Symbol()]

rubel пишет

Дело в том, что UCF уже давно не обновляется автором. А искать его исправленную версию разными умельцами под новые выпуски Firefox тот ещё гемморой.

UCF (по ссылке в родной теме) работает на версиях Firefox 78-118.
посмотрел код поддержки методов Aris-t2 и Endor8. По моему, их качество проигрывают по сравнению с UCF,
да и обновлялись они в 2018 году, а UCF на 2 года новее.

Dumby

Вобщем, попробуй заменить sb[Symbol()] на globalThis[Symbol()]

Заменил, теперь скрипт прекрасно заработал на всех открытых вкладках, как и в UCF !
Спасибо Вам за ваше внимание. :)
Dobrov
Вам тоже спасибо.

Dobrov пишет

посмотрел код поддержки методов Aris-t2 и Endor8. По моему, их качество проигрывают по сравнению с UCF,

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

Dobrov пишет

да и обновлялись они в 2018 году, а UCF на 2 года новее.

А нужны ли обновления ради обновлений? Aris-t2 держит руку на пульсе, свои скрипты регулярно обновляет, проблемы с ними и с активацией практически отсутствуют, в поддержании работоспособности при смене версий браузера практически не нуждается, вроде больше ничего и не надо. Скрипты этих двух товарищей очень популярны среди иноязычных пользователей, и они совсем ничего не знают о ucf, такое сложилось мнение.
В конечном счете у юзера есть альтернативный выбор, каждый возьмет, что его устроит.

Dumby
Вот здесь Вы мне помогли со скриптом google-translate.js.
Возможно ли его сделать для загрузчика метода Endor8.?
Сейчас в контекстном меню появляется пункт Перевод из буфера, да и он не работает.
977e376bb9c8c3671ee43b986758cdf6.png 
Код скрипта google-translate.js там .

Скрипт google-translate-loader.js у меня сейчас такой:

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

Выделить код

Код:

({
	async init() {
		await delayedStartupPromise;
		var code = Cu.readUTF8URI(Services.io.newURI(
			"chrome://scripts/google-translate.js"
		));
		var addEventListener = (...args) => {
			var trg = args[3];
			if (!trg) trg = args[3] = window;
			trg.addEventListener(...args);
			this.handlers.push(args);
		}
		new Function(
			"_id,xhtmlns,addDestructor,addEventListener,gClipboard", code
		).call(
			this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
			() => {}, addEventListener, {read: () => readFromClipboard()}
		);
		addEventListener("unload", this, {once: true});
	},
	handlers: [],
	handleEvent() {
		for(var args of this.handlers)
			args.pop().removeEventListener(...args);
		delete this.handlers;
	}
}).init();

Dumby, на счет urlbar-memory-indicator заметил, что в 117 звездочка была левее индикатора, как в 118.0 - не знаю, а в 118.0.1 уже правее, причем без разницы - after или before
win.document.getElementById("star-button-box").after(clone);
Пока поправил стилем

rubel пишет

Возможно ли его сделать для загрузчика метода Endor8.?
Сейчас в контекстном меню появляется пункт Перевод из буфера, да и он не работает.
Скрипт google-translate-loader.js у меня сейчас такой

Это что ещё такое: «chrome://scripts/google-translate.js»?
Даже если бы метод Endor8 содержал какую-то свою хром-регистрацию,
а он не содержит, то такой адрес просто невозможен сам по себе.
Да и зачем это вообще, можно ведь прямо из файла читать.


И, нужно говорить где у тебя эти файлы лежат и что прописано в userChrome.js
а то непонятно. Такой google-translate-loader.js работать не может,
но какой-то пункт появляется, значит google-translate.js в авто-папке лежит,
а это неправильно, нужно либо сменить расширение, либо положить в другую папку.


Ладно, вот совмещённый вариант. Не слишком оптимально, зато гадать не надо.

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && ({
	async init(func) {
		await delayedStartupPromise;
		var code = func.toString();
		code = code.slice(code.indexOf("{") + 1, -1).trim();

		var addEventListener = (...args) => {
			var trg = args[3];
			if (!trg) trg = args[3] = window;
			trg.addEventListener(...args);
			this.handlers.push(args);
		}
		new Function(
			"_id,xhtmlns,addDestructor,addEventListener,gClipboard,LOG", code
		).call(
			this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
			() => {}, addEventListener, {read: readFromClipboard}, Cu.reportError
		);
		window.addEventListener("unload", this, {once: true});
	},
	handlers: [],
	handleEvent() {
		for(var args of this.handlers)
			args.pop().removeEventListener(...args);
		delete this.handlers;
	}
}).init(() => {

	// Здесь код google-translate.js

});

Код скрипта google-translate.js там .

Там loadURI(url, торчит, надо заменить, например, на fixupAndLoadURIString(url,


Ещё вот этот кусок совсем какой-то левый, возможно так подойдёт

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

Выделить код

Код:

/*
((id, g) => addDestructor(reason => id in g && g[id].destroy(reason)) || id in g || ({
    actions: [{
        title: "Перевод из буфера",
        tooltip: "Перевод из буфера",
        iconURL: gticon,
     
        id: "TranslateBufer1",
        _insertBeforeActionID: "copyURL",
        
     // onCommand: (e, btn) => e.view.BrowserReloadOrDuplicate(e)
        onCommand: (e, btn) => ujs_google_translat('auto|ru')
    }],
    init() {
        g[id] = this;
        this.actions = this.actions.map(action => {
            action.extensionID = "custombuttons@xsms.org";
            return g.PageActions.addAction(new g.PageActions.Action(action));
        });
    },
    destroy(reason) {
        if (reason[5] != "e") return;
        delete g[id];
        for(var action of this.actions) action.remove();
    }
}).init())(
    "CBPageActionsMaker", Cu.import("resource:///modules/PageActions.jsm", {})
);
*/
(id => {
    var action = PageActions.actionForID(id);
    if (!action) {
        var obj = {id, iconURL: gticon, onCommand(e) {this[e.view.browsingContext.id]("auto|ru");}};
        obj.title = obj.tooltip = "Перевод из буфера";
        action = PageActions.addAction(new PageActions.Action(obj));
    }
    action[id = browsingContext.id] = ujs_google_translat;
    addEventListener("unload", () => delete action[id]);
})("TranslateBufer1");


Может ещё что-то вылезет, надо смотреть.
Кстати, перетаскивание окошка «Google Translate» показалось мне
слегка глюковатым, сделал такую замену
скрытый текст

Выделить код

Код:

/*
            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";
            }

6e73epo пишет

заметил, что в 117 звездочка была левее индикатора, как в 118.0 - не знаю, а в 118.0.1 уже правее

Странно, я попробовал на 117 добавить код в custom_script_win.js по событию "load",
и у меня звёздочка справа от индикатора.
Добавляется то всё правильно, а затем инициализируется модуль PageActions
и переставляет звёздочку в конец.


То есть, получается не по-задумке.
Можно попробовать подождать, пусть она сначала передобавится.

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

Выделить код

Код:

/*
			(this.init = win => {
*/
			(this.init = async win => {
				await win.delayedStartupPromise;

Dumby

Ладно, вот совмещённый вариант.

Все сделал как Вы сказали, сделал все правки, работает прекрасно.
В userChrome.js прописано так:

Выделить код

Код:

userChrome.import("scripts", "UChrm");

Поместил его в папку с остальными скриптами.
Огромное спасибо Вам за помощь !
Вот такой у меня сейчас google_translate.js

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && ({
	async init(func) {
		await delayedStartupPromise;
		var code = func.toString();
		code = code.slice(code.indexOf("{") + 1, -1).trim();

		var addEventListener = (...args) => {
			var trg = args[3];
			if (!trg) trg = args[3] = window;
			trg.addEventListener(...args);
			this.handlers.push(args);
		}
		new Function(
			"_id,xhtmlns,addDestructor,addEventListener,gClipboard,LOG", code
		).call(
			this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
			() => {}, addEventListener, {read: readFromClipboard}, Cu.reportError
		);
		window.addEventListener("unload", this, {once: true});
	},
	handlers: [],
	handleEvent() {
		for(var args of this.handlers)
			args.pop().removeEventListener(...args);
		delete this.handlers;
	}
}).init(() => {

	// Здесь код google-translate.js

//Google,
var langFrom_google_text = "auto";//авто
var langTo_google_text = "ru"; 

      
//Назначаем иконки
var mainicon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQtJREFUKM9VkSFOhFEQg799/CjEihWAwKDgIFwAyWHgLDjuAGfZgMCQDQkG8abtQ/z8u9A0k4rOTCez2u0GB0gCsLELbCuxLduJy0zAZgOQBBqQALEbtOQoyRgjSZLt9nOaJyf5K2zPjr01SWst8ermZtzd5euLhCQ2Y9iOHXssIvZYr1f39x/T87Ourtr7O3ZUyK6KlN5HVRaOqlxcNGACJKRItigdHFXpFdXoPVWRGmSac0suRYWUWhz7zt5/BXh65Pi6Hr7rI/IopZyqpSqldLsq9onPX3iabuHYr6o3pCVHP2Sa11nIzZdnMAmOSpyejiocVEhtjrwc15L5l4HVDgReCGTR+V9n/AAByma0J+vVwwAAAABJRU5ErkJggg==";
var gticon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADKElEQVR42p2TX2hTdxTHPzfJTW6T3qQmapvWabUbog+udp3kYeLQ4f6wiZQNtjlXHyZaBYe6TekYVGRQYexFscOtuD7UdWxzMtAXEbZStKIbDsaUCbVj9o8mTW+aNrlJbu5vJ8Ex2OMO/Lj87jnne873nO9PSyTV3tkAfYi5NhQ1MB1Ijc3ijN+D7CQh28YK+GHDMyxpjlJTkmAdQi5dWvOsUrYkGB5QC5Kcg/aFBxxqqycmcY38a/1yTqYgMw8ByfFKjtZ4V6myCeW8IApyfOw2155fw1DG4dOJJK1r45x5BPDUpZuMr2uXSuAvgBMQgOitjLJNn7iDSJP0rIJ35dt19TeGoi0Es1MMP72KFvm3+88phuYM3HCd3ObQizpa/cW7Kh020E0Tz1yOUxvjdIr7o6t/cLpkEJqZp7s5zN62ZewfnWLQ9VIIBqodBdI5tIbBG8oKhSAaJZDN8tpSP5+3L8eVgLbeAdKal7NH3mKL3FcOXCfV1ITjc7FLFrWZIlpt/4+qEA7hLRrCwk/RusOXu7ax8xHvGTkVgi/89As/T8ia9EUyghKOWSaYyqNx8oIiuEgcshetsooceibJinvTfNH1DpseD3LHSZE4eprs6k24NQaevI1fF8q5FJqv9xvlmFLdkKZlhb77E8TdIlFD54OOl3mzdXm1k+sP0jzbdwF7cRhsj8ysAWYnpYPeQfXPnn3JJG8/sZT+PW8gkuDbkWEiRh3b29dV/edujNE5dB4nHJSCUrQsTdP9mUKrCKHA5piHK+918quVp/VoD9W9uhGejNRx6+N9VZAmoTJZmhbK4vR7BGDPCQEQ/qU8R15aT2/Hi/R8PcyxyyMyVBmaXwIfPiTVf5yYHiF28ARpOyuqk3nJ4DV2HFc4SamUZ3vrGr7vPlitFNraQc4SzZZt6ptWMv3DAOdGrrGj7yvR+2KYr5A0BeCVAwqvBQuyMG+MTzp3cvj15/ivfXfzNq++/yFEIlD7GFjZ6uPRaE4oGuNQI68jIINJu0QbTFoadIJlL7lSgb8sxbSVEb8geUUVtkh55r50MY5Wec6jibV9/A9LjP7e9TdFQScjW9P1XAAAAABJRU5ErkJggg==";


function GetXmlHttpObject(){
         if (window.XMLHttpRequest){ return new XMLHttpRequest();}
         if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP");}
        return null;
        };

var lc = navigator.lastClick = {};
addEventListener("mouseup", e => {
    if (e.button) return;
    lc.X = e.screenX - mozInnerScreenX;
    lc.Y = e.screenY - mozInnerScreenY;
}, false, gBrowser.tabpanels || 1);

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()};
    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);
        this.parentNode.removeChild(this);
    };
    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:17px 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 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);
    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);
};

//----------Перевести  текст  из буфера в окне Google------------
var ujs_google_translat = function (dir){
   var lng = 'ru';
   var txt = gClipboard.read(); 
   var 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=' + l[0] + '&tl=' + l[1] + '&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();
                    };
                  //  result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
    };
};



//----------Перевести выделенный текст в окне Google------------
function ujs_google_translate (){
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    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=t&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 url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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();
                    };
                   // result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    //status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                     status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + langTo_google_text.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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
     };
};


//----------Заменить текст переводом Google------------
function ujs_google_TexReplace() {
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    if (txt) {
        var xhr = new XMLHttpRequest();
        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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);
       
           function gettransdata(){
           xmlhttp=GetXmlHttpObject();
           xmlhttp.onreadystatechange=stateChanged;
           xmlhttp.open('POST', url, true);
           xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
           xmlhttp.send('q=' + encodeURIComponent(txt));
        }
        function stateChanged() {
            
           if (xmlhttp.readyState == 4 ) {
           var result = '';
           var data = JSON.parse(xmlhttp.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
           for(var i = 0, n; n = data[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
        var msgName = _id + ":ReplaceSelectionRangeAt0";
        var url = "data:," + encodeURIComponent(
       `addMessageListener("${msgName}", function listener(msg) {
        removeMessageListener("${msgName}", listener);
        var win = {};
        Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, win);

        var sel = win.value.document.getSelection();
        if (sel.isCollapsed) return;
        var range = sel.getRangeAt(0);
        range.deleteContents();
        range.insertNode(range.createContextualFragment(msg.data));
    });`
);
function replace(tagString) {
    var mm = gBrowser.selectedBrowser.messageManager;
    mm.loadFrameScript(url, false);
    mm.sendAsyncMessage(msgName, tagString);
}
replace('<span>'+result+'</span>');
                }
        }  
        gettransdata();
    } 
};



//--------Перевести страницу с Google--------------
function ujs_googlePage_translate() {
   var urlt = gBrowser.currentURI.spec;  
   var url = "http://translate.google.com/translate?hl=ru&sl=auto&tl=ru&u="+ encodeURIComponent(urlt) + "&sandbox=1";
   gBrowser. fixupAndLoadURIString(url, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
};


//Контекстное меню для перевода из буфера-------------------------------------------  

(function () {
 if ( document.getElementById("TranslateBufer") ) return; 
 var contextMenu = document.getElementById("contentAreaContextMenu");  
 var Item = document.createXULElement("menuitem");
       Item.setAttribute("Id", "TranslateBufer");
       Item.setAttribute("label", "Перевод из буфера");
       Item.setAttribute("class", "menuitem-iconic");
       Item.setAttribute("image", mainicon);
       Item.addEventListener("command", function(){ujs_google_translat('auto|ru')}, false);

    contextMenu.insertBefore(Item, document.getElementById("context-viewpartialsource-selection") ); 
    addDestructor(function() { contextMenu.removeChild( Item ) });
 })();

  (id => {
    var action = PageActions.actionForID(id);
    if (!action) {
        var obj = {id, iconURL: gticon, onCommand(e) {this[e.view.browsingContext.id]("auto|ru");}};
        obj.title = obj.tooltip = "Перевод из буфера";
        action = PageActions.addAction(new PageActions.Action(obj));
    }
    action[id = browsingContext.id] = ujs_google_translat;
    addEventListener("unload", () => delete action[id]);
})("TranslateBufer1");

 //Контекстное меню для перевода страниц-------------------------------------------  

(function () {
 if ( document.getElementById("TranslatePage") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
  
    menu.id = "TranslatePage";
    menu.setAttribute("label", "Перевести страницу");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);
   
  contextMenu.insertBefore(menu, document.getElementById("context-viewsource") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) }); 

    var array = [
        {label:"Google", func: ujs_googlePage_translate, image:gticon},
        
        
        ];
        
   array.forEach(function( m ) {  
       if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );

       });
   
     addEventListener("popupshowing", function() {
     menu.hidden = gContextMenu.isTextSelected || gContextMenu.onImage || gContextMenu.onTextInput ; 
  }, true, contextMenu );
})();



 //Контекстное меню для перевода текста-------------------------------------------  
(function () {
 if ( document.getElementById("TranslateSelected") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
      
    menu.id = "TranslateSelected";
    menu.setAttribute("label", "Перевести выделенный текст");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);

  contextMenu.insertBefore(menu, document.getElementById("context-viewpartialsource-selection") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) });
     
    
  var array = [
        {label:"В окне Google", func: ujs_google_translate, image:gticon},
       
        { separator: ''},
        {label:"Заменить текст переводом Google", func: ujs_google_TexReplace, image:gticon},
        
        
        
              ];
  array.forEach(function( m ) {  
        if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );
       });
 
     addEventListener("popupshowing", function() {
     menu.hidden = !gContextMenu.isTextSelected; 
  }, false, contextMenu ); 
 })();


});

Dumby
А можно ли адаптировать кнопку Attributes_Inspector для загрузчика метода Endor8.?
Сейчас, если его поместить в папку со всеми скриптами и запустить браузер, то он сразу запускается и можно смотреть атрибуты. Кнопка не создается.

rubel пишет

Dumby
А можно ли адаптировать кнопку Attributes_Inspector для загрузчика метода Endor8.?
Сейчас, если его поместить в папку со всеми скриптами и запустить браузер, то он сразу запускается и можно смотреть атрибуты. Кнопка не создается.

Ну, например, переименовываешь его в AttributesInspector.txt (txt, не js),
и кладёшь рядом скрипт, который создаст кнопку.

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

Выделить код

Код:

(async (cui, id, self) => cui?.getWidget(id)?.provider != "api" && cui.createWidget(self = {
	id, label: "Attributes Inspector", localized: false,
	onCreated(btn) {
		btn._handleClick = this.click;
		btn.setAttribute("tooltiptext", this.label);
		btn.onmouseenter = btn.onmouseleave = this.onmouse;
		btn.image = "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf///////////9EAKf/RACn/0QAp////////////0QAp///////////////////////RACn/0QAp/9EAKf/RACn////////////RACn/0QAp/9EAKf///////////9EAKf//////////////////////0QAp/9EAKf/RACn/0QAp////////////0QAp/9EAKf/RACn////////////RACn/0QAp////////////0QAp/9EAKf/RACn/0QAp/9EAKf///////////9EAKf/RACn/0QAp////////////0QAp/9EAKf///////////9EAKf/RACn/0QAp/9EAKf/RACn//////////////////////////////////////9EAKf/RACn////////////RACn/0QAp/9EAKf/RACn/0QAp///////////////////////////////////////RACn/0QAp////////////0QAp/9EAKf/RACn/0QAp/9EAKf///////////9EAKf/RACn/0QAp////////////0QAp/9EAKf///////////9EAKf/RACn/0QAp/9EAKf/RACn////////////RACn/0QAp/9EAKf///////////9EAKf/RACn////////////RACn/0QAp/9EAKf/RACn/0QAp/////////////////9EAKf/RACn////////////RACn/0QAp////////////0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/////////////////////////////////0QAp/9EAKf///////////9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf//////////////////////0QAp/9EAKf//////////////////////0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp////////////0QAp/9EAKf/RACn//////////////////////9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/RACn/0QAp/9EAKf/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ==";
	},
	click() {
		(this._handleClick = new this.ownerGlobal.Function(self.code).bind(this))();
	},
	get code() {
		delete this.code;
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		["scripts", "AttributesInspector.txt"].forEach(file.append);
		return this.code = "this.focusedWindow && this.focusedWindow.focus();\n" + Cu.readUTF8File(file);
	},
	onmouse: e => e.target.focusedWindow = e.type.endsWith("r") && Services.wm.getMostRecentWindow(null)
}))(window.CustomizableUI, "AttributesInspector");


Кстати, тут есть чуть подправленный, надеюсь.

Dumby
Спасибо Вам за помощь. Все работает прекрасно с вашим Мод-вриантом AttributesInspector.
Ctrl+Shift+C - copy tooltip's contents тоже работает, а в старом не работало.

Dumby, теперь все норм при ожидании, звездочка относительно индикатора появляется там где нужно в зависимости от after или before.
Несколькими постами ранее обсуждался скрипт google_translate.js (userChrome загрузка) . Я переделал меню для перевода страниц в пункт меню и пару моментов не смог понять:
1. Строка с addDestructor. Комментировал ее и никаких негативных изменений или последствий визуально не увидел. При каких событиях они наступят?
2. Два addEventListener. true или false? Читал, но вникнуть не смог, наверное потому, что, меняя значени(е)(я), опять же не увидел никакой разницы

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

Выделить код

Код:

/** ------------------------- Пункт контекстного меню для перевода страниц ------------------------- **/
(function () {
	if (document.getElementById("TranslatePage")) return;
	var contextMenu = document.getElementById("contentAreaContextMenu");
	var Item = document.createXULElement("menuitem");
	Item.setAttribute("id", "TranslatePage");
	Item.setAttribute("label", "Translate Page");
	Item.setAttribute("class", "menuitem-iconic");
	Item.setAttribute("image", mainicon);
	Item.addEventListener("command", function() {ujs_googlePage_translate()}, false);

	contextMenu.insertBefore(Item, document.getElementById("context-viewsource"));
	addDestructor(function() {contextMenu.removeChild(Item)});

	contextMenu.addEventListener("popupshowing", function() {
		Item.hidden = document.getElementById("context-savepage").hidden
	}, false);
})();

Dumby
Еще касательно google_translate.js
В окне перевода в конец текста добавляются 3, 5, 6 пустых строчек. Такая же проблема была с оригинальным кодом для Custom Buttons, но одна из его модификаций НЕ добавляла пустые строчки https://forum.mozilla-russia.org/viewto … 96#p777296
Спасибо, если исправите это в нашем случае.
Up
Хм, обнаружился баг. Наличие в переводимом тексте символа стрелки влево < приводит к падению окна перевода. Проверить можно здесь https://malwaretips.com/threads/testing … st-1052718

Перенес в пост выше.

6e73epo пишет

Несколькими постами ранее обсуждался скрипт google_translate.js (userChrome загрузка) . Я переделал меню для перевода страниц в пункт меню и пару моментов не смог понять:
1. Строка с addDestructor. Комментировал ее и никаких негативных изменений или последствий визуально не увидел. При каких событиях они наступят?

Нет, никакой «скрипт» google_translate.js не обсуждался.
Это просто код для Custom Buttons кнопки.


Поскольку никакой перевод для меня не возможен,
было предложено чисто формальное решение — пишем оболочку,
некий симулятор CB-кнопки, которая добавляет недостающее,
то, что специфично этому расширению и на что расчитывает код.


Функция addDestructor() — одна из таких вещей.
Предназначена для зачистки от результатов предыдущего исполнения кода
при переинициализации кнопки, например, удалить добавленные пункты меню,
ведь будут созданы новые, а то иначе будет эффект размножения.


Скрипты для большинства загрузчиков не restartless, исполняются в окне только раз,
поэтому, в данном случае, на addDestructor() просто поставлена заглушка — пустая функция.

скрытый текст
new Function(
        "_id,xhtmlns,addDestructor,addEventListener,gClipboard,LOG", code
).call(
        this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
        () => {}, addEventListener, {read: readFromClipboard}, Cu.reportError
);

Так что ответ — ни при каких.
Если есть желание сделать небольшой шаг на пути переделки в настоящий скрипт,
то можно удалить все вызовы addDestructor()
и убрать его из оболочки, то есть то, что выделено болдом (вместе с последующей запятой, конечно).

2. Два addEventListener. true или false? Читал, но вникнуть не смог

Да я тоже не особо это понимаю.
Событие как-то спускается сверху вниз до цели, а затем поднимается обратно.
Если true, можно перехватить пораньше, и, например, вызвать e.stopPropagation().
Может ещё что-то, не знаю.
В приведённом отрывке true, вроде как, совсем никчему.


fuchsfan пишет

В окне перевода в конец текста

Ещё раз повторю, негде мне взять никакой текст.

но одна из его модификаций НЕ добавляла пустые строчки https://forum.mozilla-russia.org/viewto … 96#p777296

Насколько я вижу, для сплошного текста в окне перевода,
Андрей предложил заменить .replace(/\\n/g, "<br />")); на .replace(/\\n/g, "").replace(/\\r/g, ""));
Так что можешь попробовать.

Проверить можно

Как теперь, надеюсь, понятно, проверить можно, но не мне.
Если бы дал xhr.responseText, то, может быть, попробовал бы посмотреть.


Ну, типа воткнуть после if (xhr.readyState == 4 && xhr.status == 200) {
строку console.log(xhr.responseText); и забрать responseText с консоли.

Dumby пишет

строку console.log(xhr.responseText); и забрать responseText с консоли.

console.log плюс ошибки

Выделить код

Код:

[[["Когда переключатели \u003cswh\u003e и \u003cwdac\u003e","When \u003c SWH \u003e and \u003c WDAC \u003e switches are ON",null,null,3,null,null,[[null,"offline"]],[[["edbff5b2398eeca464de2caaf36a7a7e","efficient_models_2022q2.md"]]]],[null,null,"Kogda pereklyuchateli \u003cswh\u003e i \u003cwdac\u003e"]],null,"en",null,null,[["When \u003c SWH \u003e and \u003c WDAC \u003e switches are ON",null,[["Когда переключатели \u003cswh\u003e и \u003cwdac\u003e",0,true,false,[3],null,[[3]]],["Когда переключатели \u003cswh\u003e и \u003cwdac\u003e включены",0,true,false,[8]]],[[0,41]],"When \u003c SWH \u003e and \u003c WDAC \u003e switches are ON",0,0]],1,[],[["en"],null,[1],["en"]]] google-translate.uc.js:257:29
Выделить код

Код:

XML Parsing Error: mismatched tag. Expected: </wdac>.
Location: 
Line Number 1, Column 93:
chrome://browser/content/browser.xhtml : Unable to run script because scripts are blocked internally. 2
SyntaxError: An invalid or illegal string was specified google-translate.uc.js:70:13
    addEle1 chrome://chrome/content/jsx/google-translate.uc.js?1696864250457 line 31 > Function:70
    createWindow chrome://chrome/content/jsx/google-translate.uc.js?1696864250457 line 31 > Function:98
    onreadystatechange chrome://chrome/content/jsx/google-translate.uc.js?1696864250457 line 31 > Function:266

Dumby, благодарю, подчистил  от деструктора.


fuchsfan пишет

Хм, обнаружился баг. Наличие в переводимом тексте символа стрелки влево < приводит к падению окна перевода

Логично. Добавь перед созданием окна, что-то типа такого, а может Dumby подскажет куда правильнее вставить замену

result=result.replace(/</g, '&lt;');

fuchsfan пишет

Хм, обнаружился баг. Наличие в переводимом тексте символа стрелки влево < приводит к падению окна перевода.

Никогда не замечал раньше (просто не попадалось). Здесь так же.

6e73epo пишет

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

Да я вообще не понимаю почему в коде на <textarea> устанавливается innerHTML
Гугл когда-нибудь отдаёт на запрос перевода простого текста что-нибудь HTML'ское?
Если нет, то почему бы не просто value, типа так

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

Выделить код

Код:

/*
     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 cnt = w.addEle1(text, 'display:block;border:1px solid #aaa;padding-bottom:3px;padding-left:3px;background-color:#fafcfe;color:#000;font:17px 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;
    w.append(cnt);


Но json responseText'а, который дал Farby, какой-то странный.
С него, в правленном окошке, отображается только
«Когда переключатели <swh> и <wdac>».
Причём, полный вариант в json есть, но не там, где код ожидает его собрать.

Dumby пишет

Если нет, то почему бы не просто value, типа так

После этих правок падения со стрелками < прекратились. Продублирую линк на тестовый пост, чтобы народ не искал долго https://malwaretips.com/threads/testing … st-1052718
Эх, остался один бич - пустые строки.

fuchsfan
Не пользуюсь userChrome.js, у меня UCF. Но внёс в свой скрипт последние правки. Если выделяю несколько абзацев, вылезает <br />
Вроде, раньше у меня такого не было или уже забыл... Скажите, у Вас также?

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

Если нет, можете выложить свой полный скрипт, чтобы я проверил у себя?

xrun1 пишет

Скажите, у Вас также?

Точно так, <br /> вылазит.

fuchsfan
Не знаю, правильно ли, но так этого нет.

Выделить код

Код:

//                                    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, ""));

UPD: Если у Вас этот код, там в 3-х местах поправить надо.

xrun1 пишет

Не знаю, правильно ли, но так этого нет.

Помогло, спасибо. До финального релиза скрипта совсем чуть-чуть :)

fuchsfan пишет

До финального релиза скрипта совсем чуть-чуть

Т.к. избавиться от пустых строк не получается, давайте считать текущее состояние скрипта финальным и прекратить дебаты :)

fuchsfan пишет

избавиться от пустых строк не получается

Так хоть пробовал?

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

Выделить код

Код:

/*
    if (text) cnt.value = text;
*/
    if (text) cnt.value = text.trimRight();

Dumby пишет

Так хоть пробовал?

Пробовал, эффекта не дает.

fuchsfan
Что же там за responseText такой?

Dumby, начальный размер окна 310 или 320 на 160. Пусть текст вписался идеально в 8 строк без пустых строк. Если на слово длиннее, то будет переполенние и размер окна станет 480х240. Сразу получаем 3-4 пустых строки + еще несколько, т.к. для текста требуется уже меньше 8 строк. Пробовал не так резко менять размер окна (с шагом 20, сохраняя пропорции), но все равно 1-3 пустых строки иногда присутствуют.

Dumby пишет

Что же там за responseText такой?

В таких вопросах я ничего не понимаю.

6e73epo пишет

(с шагом 20, сохраняя пропорции), но все равно 1-3 пустых строки иногда присутствуют.

Попробую это, если 1-3, да только иногда.

11-10-2023 14:15:48
Попробовал, ответ отрицательный, в основном 5-6 пустых строчек, редко-редко 1. Но ведь был же код для Custom Buttons, который не давал пустых строчек.

Dumby пишет

Что же там за responseText такой?

Это в варианте без ваших правок? Так окно падает даже если попытаться перевести только один символ '<' А вообще, не смотрел еще, по какой причине переводимая строка "When < SWH > and < WDAC > switches are ON" оказывается не на своем месте в массиве

6e73epo пишет

размер окна

Ааа! Речь не о каких не «пустых строках», а о размере <textarea>.
Ни за что бы не догадался.


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


Но, наверно, можно попробовать сделать окончательную
подгонку уже под конец.
Тогда, на всякий случай, всё таки if (text) cnt.value = text.trim();
и, перед return w;

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

Выделить код

Код:

//
    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";

        st.width = Math.max(200, w) + "px";
    }

Dumby пишет

Тут бы хорошо вообще весь разресайз переписать,

Я тоже пробовал, не смог. :) Отлично всё работает, спасибо.:beer:

скрытый текст
Image001_2023-10-12_13-29.png

Dumby пишет

Но, наверно, можно попробовать сделать окончательную
подгонку уже под конец.

Безусловно, лучше, чем с теми "пустыми строками", хотя у меня гарантированно появляется вертикальный скролл, в 1 строку, если текст короткий, или в несколько пикселей, если текст побольше.

12-10-2023 13:57:16
Причину выяснил, такое происходит при установке в css-стиле скрипта размера шрифта 24px и выше, при размере 22px скролл отсутствует. Может, это можно исправить?

fuchsfan пишет

происходит при установке в css-стиле скрипта размера шрифта 24px и выше

Поставил 24px, и действительно, вижу,
пояляется ненужный вертикальный скролл.


Как ни странно, у меня помогает в строке
else st.height = Math.max(50, Math.ceil(rect.height) + 1) + "px";


заменить 1 на 2 (ну или сколько не жалко). Попробуй.

Dumby пишет

else st.height = Math.max(50, Math.ceil(rect.height) + 1) + "px";

заменить 1 на 2 (ну или сколько не жалко).

Супер, помогло. Благодарю.
Ну все, похоже, что приплыли, типа Final :)

xrun1
У вас вот на этом скрине googletranslate переводит с русского на английский.
В моем google_translate.js вообще нет такой строки ну и нет перевода на английский.

Выделить код

Код:

var url = "http://translate.google.com/translate?u="+encodeURIComponent(urlt)+"&hl="+lng+"&langpair="+dir+"&tbb=1";

Все остальные правки с этой темы я сделал.Что нужно еще поправить, чтоб был перевод и на английский ?
google_translate.js для Aris-t2

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && ({
	async init(func) {
		await delayedStartupPromise;
		var code = func.toString();
		code = code.slice(code.indexOf("{") + 1, -1).trim();

		var addEventListener = (...args) => {
			var trg = args[3];
			if (!trg) trg = args[3] = window;
			trg.addEventListener(...args);
			this.handlers.push(args);
		}
		new Function(
			"_id,xhtmlns,addDestructor,addEventListener,gClipboard,LOG", code
		).call(
			this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
			() => {}, addEventListener, {read: readFromClipboard}, Cu.reportError
		);
		window.addEventListener("unload", this, {once: true});
	},
	handlers: [],
	handleEvent() {
		for(var args of this.handlers)
			args.pop().removeEventListener(...args);
		delete this.handlers;
	}
}).init(() => {

	// Здесь код google-translate.js

//Google,
var langFrom_google_text = "auto";//авто
var langTo_google_text = "ru"; 

      
//Назначаем иконки
var mainicon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQtJREFUKM9VkSFOhFEQg799/CjEihWAwKDgIFwAyWHgLDjuAGfZgMCQDQkG8abtQ/z8u9A0k4rOTCez2u0GB0gCsLELbCuxLduJy0zAZgOQBBqQALEbtOQoyRgjSZLt9nOaJyf5K2zPjr01SWst8ermZtzd5euLhCQ2Y9iOHXssIvZYr1f39x/T87Ourtr7O3ZUyK6KlN5HVRaOqlxcNGACJKRItigdHFXpFdXoPVWRGmSac0suRYWUWhz7zt5/BXh65Pi6Hr7rI/IopZyqpSqldLsq9onPX3iabuHYr6o3pCVHP2Sa11nIzZdnMAmOSpyejiocVEhtjrwc15L5l4HVDgReCGTR+V9n/AAByma0J+vVwwAAAABJRU5ErkJggg==";
var gticon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADKElEQVR42p2TX2hTdxTHPzfJTW6T3qQmapvWabUbog+udp3kYeLQ4f6wiZQNtjlXHyZaBYe6TekYVGRQYexFscOtuD7UdWxzMtAXEbZStKIbDsaUCbVj9o8mTW+aNrlJbu5vJ8Ex2OMO/Lj87jnne873nO9PSyTV3tkAfYi5NhQ1MB1Ijc3ijN+D7CQh28YK+GHDMyxpjlJTkmAdQi5dWvOsUrYkGB5QC5Kcg/aFBxxqqycmcY38a/1yTqYgMw8ByfFKjtZ4V6myCeW8IApyfOw2155fw1DG4dOJJK1r45x5BPDUpZuMr2uXSuAvgBMQgOitjLJNn7iDSJP0rIJ35dt19TeGoi0Es1MMP72KFvm3+88phuYM3HCd3ObQizpa/cW7Kh020E0Tz1yOUxvjdIr7o6t/cLpkEJqZp7s5zN62ZewfnWLQ9VIIBqodBdI5tIbBG8oKhSAaJZDN8tpSP5+3L8eVgLbeAdKal7NH3mKL3FcOXCfV1ITjc7FLFrWZIlpt/4+qEA7hLRrCwk/RusOXu7ax8xHvGTkVgi/89As/T8ia9EUyghKOWSaYyqNx8oIiuEgcshetsooceibJinvTfNH1DpseD3LHSZE4eprs6k24NQaevI1fF8q5FJqv9xvlmFLdkKZlhb77E8TdIlFD54OOl3mzdXm1k+sP0jzbdwF7cRhsj8ysAWYnpYPeQfXPnn3JJG8/sZT+PW8gkuDbkWEiRh3b29dV/edujNE5dB4nHJSCUrQsTdP9mUKrCKHA5piHK+918quVp/VoD9W9uhGejNRx6+N9VZAmoTJZmhbK4vR7BGDPCQEQ/qU8R15aT2/Hi/R8PcyxyyMyVBmaXwIfPiTVf5yYHiF28ARpOyuqk3nJ4DV2HFc4SamUZ3vrGr7vPlitFNraQc4SzZZt6ptWMv3DAOdGrrGj7yvR+2KYr5A0BeCVAwqvBQuyMG+MTzp3cvj15/ivfXfzNq++/yFEIlD7GFjZ6uPRaE4oGuNQI68jIINJu0QbTFoadIJlL7lSgb8sxbSVEb8geUUVtkh55r50MY5Wec6jibV9/A9LjP7e9TdFQScjW9P1XAAAAABJRU5ErkJggg==";


function GetXmlHttpObject(){
         if (window.XMLHttpRequest){ return new XMLHttpRequest();}
         if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP");}
        return null;
        };

var lc = navigator.lastClick = {};
addEventListener("mouseup", e => {
    if (e.button) return;
    lc.X = e.screenX - mozInnerScreenX;
    lc.Y = e.screenY - mozInnerScreenY;
}, false, gBrowser.tabpanels || 1);

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()};
    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:#33343F;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);
        this.parentNode.removeChild(this);
    };
    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);
    };

    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 = 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.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();
            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);
	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";

        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);
};

//----------Перевести  текст  из буфера в окне Google------------
var ujs_google_translat = function (dir){
   var lng = 'ru';
   var txt = gClipboard.read(); 
   var 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=' + l[0] + '&tl=' + l[1] + '&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, ""));
                    for(var i = 0, n; n = tmp[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
                  //  result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
    };
};



//----------Перевести выделенный текст в окне Google------------
function ujs_google_translate (){
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    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 url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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, ""));
                    for(var i = 0, n; n = tmp[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
                   // result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    //status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                     status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + langTo_google_text.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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
     };
};


//----------Заменить текст переводом Google------------
function ujs_google_TexReplace() {
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    if (txt) {
        var xhr = new XMLHttpRequest();
        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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);
       
           function gettransdata(){
           xmlhttp=GetXmlHttpObject();
           xmlhttp.onreadystatechange=stateChanged;
           xmlhttp.open('POST', url, true);
           xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
           xmlhttp.send('q=' + encodeURIComponent(txt));
        }
        function stateChanged() {
            
           if (xmlhttp.readyState == 4 ) {
           var result = '';
           var data = JSON.parse(xmlhttp.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
           for(var i = 0, n; n = data[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
        var msgName = _id + ":ReplaceSelectionRangeAt0";
        var url = "data:," + encodeURIComponent(
       `addMessageListener("${msgName}", function listener(msg) {
        removeMessageListener("${msgName}", listener);
        var win = {};
        Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, win);

        var sel = win.value.document.getSelection();
        if (sel.isCollapsed) return;
        var range = sel.getRangeAt(0);
        range.deleteContents();
        range.insertNode(range.createContextualFragment(msg.data));
    });`
);
function replace(tagString) {
    var mm = gBrowser.selectedBrowser.messageManager;
    mm.loadFrameScript(url, false);
    mm.sendAsyncMessage(msgName, tagString);
}
replace('<span>'+result+'</span>');
                }
        }  
        gettransdata();
    } 
};



//--------Перевести страницу с Google--------------
function ujs_googlePage_translate() {
   var urlt = gBrowser.currentURI.spec;  
   var url = "http://translate.google.com/translate?hl=ru&sl=auto&tl=ru&u="+ encodeURIComponent(urlt) + "&sandbox=1";
   gBrowser. fixupAndLoadURIString(url, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
};


//Контекстное меню для перевода из буфера-------------------------------------------  

(function () {
 if ( document.getElementById("TranslateBufer") ) return; 
 var contextMenu = document.getElementById("contentAreaContextMenu");  
 var Item = document.createXULElement("menuitem");
       Item.setAttribute("label", "Перевод из буфера");
       Item.setAttribute("class", "menuitem-iconic");
       Item.setAttribute("image", mainicon);
       Item.addEventListener("command", function(){ujs_google_translat('auto|ru')}, false);

    contextMenu.insertBefore(Item, document.getElementById("context-viewpartialsource-selection") ); 
    addDestructor(function() { contextMenu.removeChild( Item ) });
 })();

  

 //Контекстное меню для перевода страниц-------------------------------------------  

(function () {
 if ( document.getElementById("TranslatePage") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
  
    menu.id = "TranslatePage";
    menu.setAttribute("label", "Перевести страницу");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);
   
  contextMenu.insertBefore(menu, document.getElementById("context-viewsource") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) }); 

    var array = [
        {label:"Google", func: ujs_googlePage_translate, image:gticon},
        
        
        ];
        
   array.forEach(function( m ) {  
       if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );

       });
   
     addEventListener("popupshowing", function() {
     menu.hidden = gContextMenu.isTextSelected || gContextMenu.onImage || gContextMenu.onTextInput ; 
  }, true, contextMenu );
})();



 //Контекстное меню для перевода текста-------------------------------------------  
(function () {
 if ( document.getElementById("TranslateSelected") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
      
    menu.id = "TranslateSelected";
    menu.setAttribute("label", "Перевести выделенный текст");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);

  contextMenu.insertBefore(menu, document.getElementById("context-viewpartialsource-selection") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) });
     
    
  var array = [
        {label:"В окне Google", func: ujs_google_translate, image:gticon},
       
        { separator: ''},
        {label:"Заменить текст переводом Google", func: ujs_google_TexReplace, image:gticon},
        
        
        
              ];
  array.forEach(function( m ) {  
        if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );
       });
 
     addEventListener("popupshowing", function() {
     menu.hidden = !gContextMenu.isTextSelected; 
  }, false, contextMenu ); 
 })();


});

В проекте Floorp появился скрипт для создания QR Code на текущую загруженную страницу. Сам скрипт базируется на функционале QR Code Styling. Работу проверял в [firefox] 115+ с загрузчиком от Xiao

qr-code.uc.js

Выделить код

Код:

// ==UserScript==
// @name	QR Code Generate
// @author	Project Floorp
// @include	main
// @homepageURL	https://github.com/Floorp-Projects/Floorp-core/blob/main/browser/base/content/browser-pageActions.js
// @description Need QR Code Styling https://unpkg.com/qr-code-styling@1.5.0/lib/qr-code-styling.js \n Place "qr-code-styling.js" in same folder with this script
// ==/UserScript==
/* eslint-disable no-undef */
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

if (location.href.startsWith("chrome://browser/content/browser.x")) {

  let strlngs = {
    'en-US': {
      'qrcode-generate-page-action-title': 'Scan QR Code with your Phone',
      'qrcode-generate-page-action': 'Share this page with your phone',
      'qrcode-generate-page-action-dl': 'L+Click: Download QR Code',
    },
    'ru': {
      'qrcode-generate-page-action-title': 'Сканировать QR-код с помощью телефона.',
      'qrcode-generate-page-action': 'Отправить страницу на телефон.',
      'qrcode-generate-page-action-dl': 'ЛКМ: Скачать QR Code',
        },
  };
  function QRCodeGenerateL10n(strlng) {
    let LOCALE = strlngs[Services.locale.requestedLocales[0]]
      ? Services.locale.requestedLocales[0]
      : 'en-US';
    return (!strlng)
      ? ""
      : strlngs[LOCALE][strlng]
        ? strlngs[LOCALE][strlng]
        : "";
  }

  let QRCodeGeneratePageActionButton = window.MozXULElement.parseXULToFragment(`
  <hbox id="QRCodeGeneratePageAction" label="${QRCodeGenerateL10n('qrcode-generate-page-action')}"
    class="urlbar-page-action" tooltiptext="${QRCodeGenerateL10n('qrcode-generate-page-action')}"
    role="button" popup="qrcode-panel">
    <image id="QRCodeGeneratePageAction-image" class="urlbar-icon"
    style="list-style-image: url(data:image/svg+xml;base64,PCEtLSBUaGlzIFNvdXJjZSBDb2RlIEZvcm0gaXMgc3ViamVjdCB0byB0aGUgdGVybXMgb2YgdGhlIE1vemlsbGEgUHVibGljCiAgIC0gTGljZW5zZSwgdi4gMi4wLiBJZiBhIGNvcHkgb2YgdGhlIE1QTCB3YXMgbm90IGRpc3RyaWJ1dGVkIHdpdGggdGhpcwogICAtIGZpbGUsIFlvdSBjYW4gb2J0YWluIG9uZSBhdCBodHRwOi8vbW96aWxsYS5vcmcvTVBMLzIuMC8uIC0tPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDE2IDE2IiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9ImNvbnRleHQtZmlsbCIgZmlsbC1vcGFjaXR5PSJjb250ZXh0LWZpbGwtb3BhY2l0eSI+CiAgPHBhdGggZD0iTTguMjUgMTAuMzc1YS42MjUuNjI1IDAgMCAxLTEuMjUgMGwwLTguMzIzLTMuMzA4IDMuMzA5YS42MjYuNjI2IDAgMCAxLS44ODUtLjg4NUw3LjI4NSAwbC42ODEgMCA0LjQ3NyA0LjQ3NmEuNjI2LjYyNiAwIDAgMS0uODg1Ljg4NUw4LjI1IDIuMDUybDAgOC4zMjN6Ii8+CiAgPHBhdGggZD0iTTEyLjI1IDE1YTIgMiAwIDAgMCAyLTJsMC0xLjM3NWEuNjI1LjYyNSAwIDAgMC0xLjI1IDBsMCAxLjUyNS0uNi42LTkuNTUgMC0uNi0uNiAwLTEuNTI1YS42MjUuNjI1IDAgMCAwLTEuMjUgMEwxIDEzYTIgMiAwIDAgMCAyIDJsOS4yNSAweiIvPgo8L3N2Zz4K);"/>
    <panel id="qrcode-panel" type="arrow" position="bottomright topright" tooltiptext="${QRCodeGenerateL10n('qrcode-generate-page-action-dl')}" onpopupshowing="generateCurrentTabQRCode()" onclick="if (event.button == 0) generateCurrentTabQRCode(true)">
     <vbox id="qrcode-box">
      <vbox class="panel-header">
       <html:h1>
        <html:span>${QRCodeGenerateL10n('qrcode-generate-page-action-title')}</html:span>
       </html:h1>
      </vbox>
      <toolbarseparator/>
      <a href="">
        <vbox id="qrcode-img-vbox"/>
      </a>
     </vbox>
    </panel>
  </hbox>
  `);

  function generateCurrentTabQRCode(DL) {
    let QRcodeStyling = Services.io.newURI(Components.stack.filename).resolve("qr-code-styling.js");
    // Need QR Code Styling from https://unpkg.com/qr-code-styling@1.5.0/lib/qr-code-styling.js
    // Place "qr-code-styling.js" in same folder with this script
    Services.scriptloader.loadSubScript(QRcodeStyling, window);

    let currentTab = gBrowser.selectedTab;
    let currentTabURL = currentTab.linkedBrowser.currentURI.spec;

    const qrCode = new QRCodeStyling({
      width: 250,
      height: 250,
      type: "svg",
      data: currentTabURL,
      image: "chrome://branding/content/about-logo.svg",
      dotsOptions: {
          color: "#4267b2",
      },
      cornersSquareOptions: {
        type: "extra-rounded",
      },
      backgroundOptions: {
          color: "#e9ebee",
      },
      imageOptions: {
          crossOrigin: "anonymous",
          margin: 10
      }
    });

  //remove old qrcode
    let QRCodeBox = document.getElementById("qrcode-img-vbox");

    while (QRCodeBox.firstChild) {
      QRCodeBox.firstChild.remove();
    }

    qrCode.append(QRCodeBox);
    if (DL)
      qrCode.download({ name: currentTabURL.replace(/[:\\\/<>?*|&#="]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, ''), extension: "svg" });
  }

  SessionStore.promiseInitialized.then(() => {
    document.getElementById("star-button-box").before(QRCodeGeneratePageActionButton);
  });
}


и
qr-code-styling.js

Выделить код

Код:

data:application/x-zip-compressed;base64,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

или qr-code-styling.js


Оба скрипта должны лежать в одной папке.

rubel
У нас скрипты разные. Вы изначально начали пилить скрипт с контекстным меню. У меня такой вариант:

скрытый текст
Image001_2023-10-13_12-56.png

Если текст выделен на странице - переводится в окне, если выделения нет - переводится вся страница. Исходный язык определяется автоматически, перевод на русский или английский соответственно.
Попробуйте в своём скрипте заменить 2 строки: 257 и 258

Выделить код

Код:

xhr.send('q=' + encodeURIComponent(txt));
    };

таким кодом

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

Выделить код

Код:

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);
                    };


Проверить не могу, не пользуюсь методом Aris-t2, а ставить муторно.

xrun1 пишет

внёс в свой скрипт последние правки. Если выделяю несколько абзацев, вылезает <br />

С абзацами: удалить замену, т.к. она уже не требуется .replace(/\\n/g, "<br />")
Для сплошного текста: заменять на пробел, а не на пусто .replace(/\\n/g, ' ')

xrun1
Заменил вашим кодом, не переводит.
3f4595a26d152086aca05e13638e8126.png 
Может еще что-то нужно сделать.?

6e73epo
Уже сделал.
rubel
У Вас там второй кусок подобный есть, не заметил. Попробуйте поиском в скрипте xhr.send('q=' + encodeURIComponent(txt));.
В исходном вашем это строки 292 и 293. Попробуйте там тоже заменить.

13-10-2023 15:21:46
Свой код для UCF выложил в соответствующей теме.

xrun1
Нет и это не помогло.

rubel
Тогда не знаю. Скрипты разные, а я разобраться не смогу. :(

Dumby
Если возможно, то помогите, пожалуйста с моим вопросом.
xrun1
Вам спасибо за внимание и правки скрипта. :)

rubel пишет

Dumby
Если возможно, то помогите, пожалуйста с моим вопросом.

Ну сколько раз повторять, что проверить мне такое негде.
Хочешь чтобы я что-то из пальца высосал? Хорошо.


Допустим, что речь о переводе выделенного текста в окошке «Google Translate».
Тогда, находишь строку function ujs_google_translate (){
и меняешь на function ujs_google_translate(langTo_google_text) {


Ну, и определяем пункты субменю

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

Выделить код

Код:

/*
        {label:"В окне Google", func: ujs_google_translate, image:gticon},
*/
        {label: "В окне Google на русский", func: () => ujs_google_translate("ru"), image: gticon},
        {label: "В окне Google на английский", func: () => ujs_google_translate("en"), image: gticon},

Dumby
Огромное Вам спасибо. Все заработало прекрасно.
Появился выбор языка перевода.
f2aa3ddd0cfddf216b6bd4fc14a12713.png 
Вот окончательный вариант скрипта google_translate.js для Aris-t2

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && ({
	async init(func) {
		await delayedStartupPromise;
		var code = func.toString();
		code = code.slice(code.indexOf("{") + 1, -1).trim();

		var addEventListener = (...args) => {
			var trg = args[3];
			if (!trg) trg = args[3] = window;
			trg.addEventListener(...args);
			this.handlers.push(args);
		}
		new Function(
			"_id,xhtmlns,addDestructor,addEventListener,gClipboard,LOG", code
		).call(
			this, "ucf-cbinit-google-translate", "http://www.w3.org/1999/xhtml",
			() => {}, addEventListener, {read: readFromClipboard}, Cu.reportError
		);
		window.addEventListener("unload", this, {once: true});
	},
	handlers: [],
	handleEvent() {
		for(var args of this.handlers)
			args.pop().removeEventListener(...args);
		delete this.handlers;
	}
}).init(() => {

	// Здесь код google-translate.js

//Google,
var langFrom_google_text = "auto";//авто
var langTo_google_text = "ru"; 

      
//Назначаем иконки
var mainicon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAALCAIAAAD5gJpuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQtJREFUKM9VkSFOhFEQg799/CjEihWAwKDgIFwAyWHgLDjuAGfZgMCQDQkG8abtQ/z8u9A0k4rOTCez2u0GB0gCsLELbCuxLduJy0zAZgOQBBqQALEbtOQoyRgjSZLt9nOaJyf5K2zPjr01SWst8ermZtzd5euLhCQ2Y9iOHXssIvZYr1f39x/T87Ourtr7O3ZUyK6KlN5HVRaOqlxcNGACJKRItigdHFXpFdXoPVWRGmSac0suRYWUWhz7zt5/BXh65Pi6Hr7rI/IopZyqpSqldLsq9onPX3iabuHYr6o3pCVHP2Sa11nIzZdnMAmOSpyejiocVEhtjrwc15L5l4HVDgReCGTR+V9n/AAByma0J+vVwwAAAABJRU5ErkJggg==";
var gticon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADKElEQVR42p2TX2hTdxTHPzfJTW6T3qQmapvWabUbog+udp3kYeLQ4f6wiZQNtjlXHyZaBYe6TekYVGRQYexFscOtuD7UdWxzMtAXEbZStKIbDsaUCbVj9o8mTW+aNrlJbu5vJ8Ex2OMO/Lj87jnne873nO9PSyTV3tkAfYi5NhQ1MB1Ijc3ijN+D7CQh28YK+GHDMyxpjlJTkmAdQi5dWvOsUrYkGB5QC5Kcg/aFBxxqqycmcY38a/1yTqYgMw8ByfFKjtZ4V6myCeW8IApyfOw2155fw1DG4dOJJK1r45x5BPDUpZuMr2uXSuAvgBMQgOitjLJNn7iDSJP0rIJ35dt19TeGoi0Es1MMP72KFvm3+88phuYM3HCd3ObQizpa/cW7Kh020E0Tz1yOUxvjdIr7o6t/cLpkEJqZp7s5zN62ZewfnWLQ9VIIBqodBdI5tIbBG8oKhSAaJZDN8tpSP5+3L8eVgLbeAdKal7NH3mKL3FcOXCfV1ITjc7FLFrWZIlpt/4+qEA7hLRrCwk/RusOXu7ax8xHvGTkVgi/89As/T8ia9EUyghKOWSaYyqNx8oIiuEgcshetsooceibJinvTfNH1DpseD3LHSZE4eprs6k24NQaevI1fF8q5FJqv9xvlmFLdkKZlhb77E8TdIlFD54OOl3mzdXm1k+sP0jzbdwF7cRhsj8ysAWYnpYPeQfXPnn3JJG8/sZT+PW8gkuDbkWEiRh3b29dV/edujNE5dB4nHJSCUrQsTdP9mUKrCKHA5piHK+918quVp/VoD9W9uhGejNRx6+N9VZAmoTJZmhbK4vR7BGDPCQEQ/qU8R15aT2/Hi/R8PcyxyyMyVBmaXwIfPiTVf5yYHiF28ARpOyuqk3nJ4DV2HFc4SamUZ3vrGr7vPlitFNraQc4SzZZt6ptWMv3DAOdGrrGj7yvR+2KYr5A0BeCVAwqvBQuyMG+MTzp3cvj15/ivfXfzNq++/yFEIlD7GFjZ6uPRaE4oGuNQI68jIINJu0QbTFoadIJlL7lSgb8sxbSVEb8geUUVtkh55r50MY5Wec6jibV9/A9LjP7e9TdFQScjW9P1XAAAAABJRU5ErkJggg==";


function GetXmlHttpObject(){
         if (window.XMLHttpRequest){ return new XMLHttpRequest();}
         if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP");}
        return null;
        };

var lc = navigator.lastClick = {};
addEventListener("mouseup", e => {
    if (e.button) return;
    lc.X = e.screenX - mozInnerScreenX;
    lc.Y = e.screenY - mozInnerScreenY;
}, false, gBrowser.tabpanels || 1);

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()};
    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:#33343F;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);
        this.parentNode.removeChild(this);
    };
    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);
    };

    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 = 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.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();
            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);
	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";

        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);
};

//----------Перевести  текст  из буфера в окне Google------------
var ujs_google_translat = function (dir){
   var lng = 'ru';
   var txt = gClipboard.read(); 
   var 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=' + l[0] + '&tl=' + l[1] + '&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, ""));
                    for(var i = 0, n; n = tmp[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
                  //  result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
    };
};



//----------Перевести выделенный текст в окне Google------------
function ujs_google_translate(langTo_google_text) {
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    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 url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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, ""));
                    for(var i = 0, n; n = tmp[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
                   // result = '<span style="background-color:inherit;color:inherit;font-size:inherit;font-family:Times,serif;">' + result + '</span>';
                    //status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                     status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + langTo_google_text.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 (x){LOG(x)};
        };
        xhr.send('q=' + encodeURIComponent(txt));
     };
};


//----------Заменить текст переводом Google------------
function ujs_google_TexReplace() {
    var lng = 'ru';
    var txt = gContextMenu.selectionInfo.fullText;
    if (txt) {
        var xhr = new XMLHttpRequest();
        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + langFrom_google_text + '&tl=' + langTo_google_text + '&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);
       
           function gettransdata(){
           xmlhttp=GetXmlHttpObject();
           xmlhttp.onreadystatechange=stateChanged;
           xmlhttp.open('POST', url, true);
           xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
           xmlhttp.send('q=' + encodeURIComponent(txt));
        }
        function stateChanged() {
            
           if (xmlhttp.readyState == 4 ) {
           var result = '';
           var data = JSON.parse(xmlhttp.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
           for(var i = 0, n; n = data[0][i]; i++){
                        if(n[0])result += n[0].toString();
                    };
        var msgName = _id + ":ReplaceSelectionRangeAt0";
        var url = "data:," + encodeURIComponent(
       `addMessageListener("${msgName}", function listener(msg) {
        removeMessageListener("${msgName}", listener);
        var win = {};
        Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, win);

        var sel = win.value.document.getSelection();
        if (sel.isCollapsed) return;
        var range = sel.getRangeAt(0);
        range.deleteContents();
        range.insertNode(range.createContextualFragment(msg.data));
    });`
);
function replace(tagString) {
    var mm = gBrowser.selectedBrowser.messageManager;
    mm.loadFrameScript(url, false);
    mm.sendAsyncMessage(msgName, tagString);
}
replace('<span>'+result+'</span>');
                }
        }  
        gettransdata();
    } 
};



//--------Перевести страницу с Google--------------
function ujs_googlePage_translate() {
   var urlt = gBrowser.currentURI.spec;  
   var url = "http://translate.google.com/translate?hl=ru&sl=auto&tl=ru&u="+ encodeURIComponent(urlt) + "&sandbox=1";
   gBrowser. fixupAndLoadURIString(url, {
   triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()
});
};


//Контекстное меню для перевода из буфера-------------------------------------------  

(function () {
 if ( document.getElementById("TranslateBufer") ) return; 
 var contextMenu = document.getElementById("contentAreaContextMenu");  
 var Item = document.createXULElement("menuitem");
       Item.setAttribute("label", "Перевод из буфера");
       Item.setAttribute("class", "menuitem-iconic");
       Item.setAttribute("image", mainicon);
       Item.addEventListener("command", function(){ujs_google_translat('auto|ru')}, false);

    contextMenu.insertBefore(Item, document.getElementById("context-viewpartialsource-selection") ); 
    addDestructor(function() { contextMenu.removeChild( Item ) });
 })();

  

 //Контекстное меню для перевода страниц-------------------------------------------  

(function () {
 if ( document.getElementById("TranslatePage") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
  
    menu.id = "TranslatePage";
    menu.setAttribute("label", "Перевести страницу");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);
   
  contextMenu.insertBefore(menu, document.getElementById("context-viewsource") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) }); 

    var array = [
        {label:"Google", func: ujs_googlePage_translate, image:gticon},
        
        
        ];
        
   array.forEach(function( m ) {  
       if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );

       });
   
     addEventListener("popupshowing", function() {
     menu.hidden = gContextMenu.isTextSelected || gContextMenu.onImage || gContextMenu.onTextInput ; 
  }, true, contextMenu );
})();



 //Контекстное меню для перевода текста-------------------------------------------  
(function () {
 if ( document.getElementById("TranslateSelected") ) return; 
  
  var menu = document.createXULElement("menu");  
  var menuPopup = document.createXULElement("menupopup");
  var contextMenu = document.getElementById("contentAreaContextMenu");  
      
    menu.id = "TranslateSelected";
    menu.setAttribute("label", "Перевести выделенный текст");
    menu.setAttribute("class", "menu-iconic");
    menu.setAttribute("image", mainicon);

  contextMenu.insertBefore(menu, document.getElementById("context-viewpartialsource-selection") ); 
  menu.appendChild( menuPopup );
  addDestructor(function() { contextMenu.removeChild( menu ) });
     
    
  var array = [
        {label: "В окне Google на русский", func: () => ujs_google_translate("ru"), image: gticon},
        {label: "В окне Google на английский", func: () => ujs_google_translate("en"), image:gticon},
       
        { separator: ''},
        {label:"Заменить текст переводом Google", func: ujs_google_TexReplace, image:gticon},
        
        
        
              ];
  array.forEach(function( m ) {  
        if ( "separator" in m ) { menuPopup.appendChild( document.createXULElement("menuseparator") ); return };
       var mItem = document.createXULElement("menuitem");
       mItem.setAttribute("label", m.label);
       mItem.setAttribute("class", "menuitem-iconic");
       mItem.setAttribute("image", m.image);
       mItem.addEventListener("command", m.func, false);
       menuPopup.appendChild( mItem );
       });
 
     addEventListener("popupshowing", function() {
     menu.hidden = !gContextMenu.isTextSelected; 
  }, false, contextMenu ); 
 })();


});

Dumby, правильно ли я добавил сепаратор?

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

Выделить код

Код:

/** ------------------------- Пункт контекстного меню для перевода страниц ------------------------- **/
(function () {
	if (document.getElementById("TranslatePage")) return;
	var contextMenu = document.getElementById("contentAreaContextMenu");
	var Item = document.createXULElement("menuitem");
	var sep = document.createXULElement("menuseparator");

	Item.setAttribute("id", "TranslatePage");
	Item.setAttribute("label", "Translate Page");
	Item.setAttribute("class", "menuitem-iconic");
	Item.setAttribute("image", rusFlag);
	Item.addEventListener("command", function() {ujs_googlePage_translate()}, false);

	var cvs = document.getElementById("context-viewsource");

	contextMenu.insertBefore(Item, cvs);
	contextMenu.insertBefore(sep, cvs);
	contextMenu.addEventListener("popupshowing", function() {
		Item.hidden = sep.hidden = cvs.hidden;
	}, false);
})();

6e73epo
Правильно. Можно так: contextMenu.insertBefore(Item, cvs).after(sep);


Да и как вообще можно добавить сепаратор неправильно?
Если он не добавился, или добавился не туда, куда ожидалось,
то это сразу будет видно что неправильно.

Dumby, да просто без sep.hidden сепаратор двоился при первом вызове контекстного меню, в котором пункты скрыты
На скрине, который давал, меню выше затемнено, если буфер обмена пуст и наоборот. Ничего умнее, чем применение readFromClipboard()  - не нашел

Если кому-то как и мне требуется переводить выделенный текст или текст из буфера на английский только с русского, то можно автоматом определять исходный язык. Вызываю функцию с параметром auto|det, а в коде функции добавляю

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

Выделить код

Код:

if (l[1] === "det") {
	l[1] = "ru";
	var fCodeRus = String.fromCodePoint(1040);
	var lCodeRus = String.fromCodePoint(1103);
	for (var j = 0; j < txt.length && j < 20; j++) {
		if (txt[j] >= fCodeRus && txt[j] <= lCodeRus) {
			l[1] = "en";
			break;
		}
	}
};


Не вижу смысла учитывать ёЁ. Также пробовал сначала задействовать встроенный языковый детектор браузера, но он косячит. Если строку "я хожу в школу" определяет как русскую, то например, строку "мама" определяет как английскую

Подскажите что подправить в google_translate.js для Aris-t2.
В контекстном меню страницы строки переводчика немного сместились вправо от иконки по сравнению с другими пунктами меню
Как бы их выровнять с другими пунктами меню. ?
989297a1f69fe2d0c5ade51a6a444ed9.png

Выровнял с другими пунктами меню этот код:

Выделить код

Код:

menupopup .menu-iconic-left {
    -moz-appearance: none !important;
    appearance: none !important;
    margin-inline-start: 0 !important;
    margin-inline-end: 3px !important;
    visibility: visible !important;
  
}

скрытый текст
1c30d38370a4374bad44f525eedd95a8.png 

Dumby пишет

Ааа! Речь не о каких не «пустых строках», а о размере <textarea>.
Тут бы хорошо вообще весь разресайз переписать

Вроде бы ничего сложного нет. Можно по детски. Начальный размер окна (320х160) оставляем без изменений, т.к. не очень приятно лицезреть, например, двухстрочную высоту. При переполнении плавно, с сохранением пропорций, в цикле увеличиваем ширину на 40 px, а высоту на 20 px до пропадания скролла, затем в цикле уменьшаем высоту до появления скролла, затем обратно увеличиваем высоту на 20 px и готово.
С учетом того, что скролл может остаться из-за нарушения форматирования текста, то можно увеличить высоту не на 20, а на 40 px, а затем уменьшить на 20px.

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

Выделить код

Код:

if(size) {
	cnt.style.height = size.height;
	cnt.style.width = size.width;
}
else {
	for (var i = 9; i < 33; i++) {
		if (cnt.scrollTopMax) {
			cnt.style.width = 40*i+'px';
			cnt.style.height = 20*i+'px';
		}
		else break;
	}
	if (i > 9) {
		var k = 0;
		while (!cnt.scrollTopMax) {
			cnt.style.height = parseInt(cnt.style.height)-20+'px';
			k++;
		}
		if (k) {
			cnt.style.height = parseInt(cnt.style.height)+40+'px';
		}
	}
};

// ну и ближе к концу, например после строки w.style.top ...

if (k) {
	cnt.style.height = parseInt(cnt.style.height)-20+'px';
}

6e73epo пишет

Код:

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


И, вобщем-то, весьма неплохо,
в большинстве случаев, подгонка выглядит хорошо.


Впрочем, попалось и такое
Здесь ширина чуть великовата, как мне кажется
(это на странице about:license, где много всякого текста).

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

Выделить код

Код:

data:image/webp;base64,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


И, даже сподобился словить не ожидавшийся скролл.
Это надеюсь показать в следующем посту,
а то в один пост два таких скиншота не поместятся.

Dumby пишет

надеюсь показать в следующем посту

Ага, размечтался, — «Unable to merge post.»

21-10-2023 20:29:58
Во, с третьей попытки получилось

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

Выделить код

Код:

data:image/webp;base64,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

Dumby пишет

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

Не понял, имеется ввиду без перевода?


Dumby пишет

ширина чуть великовата

Но пропорции сохранились. Лицезреть на окно, где высота иногда может быть больше ширины, как то не очень приятно


Dumby пишет

И, даже сподобился словить не ожидавшийся скролл

Повторил, текст выглядит один в один как на скрине, вот только скролла не было и последняя строка видна


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

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

Выделить код

Код:

if (size) {
	cnt.style.height = size.height;
	cnt.style.width = size.width;
}
else {
	for (var i = 9; i < 33; i++) {
		if (cnt.scrollTopMax) {
			cnt.style.width = 40*i+'px';
			cnt.style.height = 20*i+'px';
		}
		else break;
	}
	if (i > 9) {
		cnt.style.height = cnt.editor.rootElement.childNodes[1].offsetTop-4+'px';
	}
};

6e73epo пишет

Не понял, имеется ввиду без перевода?

Да-да, выделенный текст, без перевода.

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

А, это <br> который идёт после текстовой ноды.
Довольно остроумно. И работает.


Но, вычитать четыре пикселя сразу было понятно,
что у меня не прокатит, скролл стопроцентно возникает.
Убрал этот вычет, но что-то не помогло.
А вот когда, наоборот, прибавил четыре пикселя — стало нормально.
Правда это у меня font так ещё и остался 24px от прошлых экспериментов.


И, вот ещё какой момент, кейс когда текст большой.
У меня написано так, что если, после всех увеличений в цикле, скролл так и не пропал,
то высота не подвергается изменению, остаётся как получилось.
Иначе говоря, если менять, то только в меньшую сторону, для убирания «пустых строк».


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


Вобщем, даже не знаю, как здесь лучше, как должно быть.
Просто отмечу, что небольшое отличие в таком случае может присутствовать.

Dumby, благодарю, уже сам заметил, что весь цикл может отработать, а скролл остаться, поэтому добавил условие i > 9 && !cnt.scrollTopMax и теперь все ок.


Этот _moz_dirty идеально работает при размере шрифта 17, который как раз меня устраивает, а для большинства размеров offsetTop с плавающим значением. Конечно, можно под определенный размер все настроить, но мне не требуется.


Теперь единственное, что меня не устраивает, это когда при еще не наступившем событии "mouseup" для navigator.lastClick, окошко перевода появляется в правом нижнем углу окна документа браузера, а хочется, чтобы ближе к центру.

6e73epo пишет

Теперь единственное, что меня не устраивает, это когда при еще не наступившем событии "mouseup" для navigator.lastClick, окошко перевода появляется в правом нижнем углу окна документа браузера, а хочется, чтобы ближе к центру.

Функция createWindow() в коде всегда вызывается с аргументом pos
как navigator.lastClick (и, кстати, никогда с аргументом size),
значит pos это гарантированно объект, и это проверять не требуется.


Поэтому, набросал такой вариант. Надеюсь ничего не испортил.

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

Выделить код

Код:

/*
    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';
*/
    var {X, Y} = pos, U = X == undefined;
    w.style.left = (U ? mX/2 : X < mX + hW ? X > hW ? X - hW : 0 : mX) + "px";
    w.style.top  = (U ? mY/2 : Y + 10 < mY ? Y + 10 : mY) + "px";

6e73epo пишет

работает при размере шрифта 17…… Конечно, можно под определенный размер все настроить…

API браузера - почти операционная система, неужели нет способа подгонки окна под произвольный текст (про HTML с графикой уж молчу) ???
Печально, что баги google-translate.js так долго (c 04-10-2023) победить не получается!


Dumby пишет

Поэтому, набросал такой вариант. Надеюсь ничего не испортил.

А если у кого-нибудь на планшете с нестандартным DPI или просто на Линуксе/MacOS размер окна не совпадёт?
Придётся логарифмы ещё добавлять или систему уравнений ! :)

Dumby пишет

Надеюсь ничего не испортил

Да вроде все ок. Спасибо.


Dobrov пишет

API браузера - почти операционная система, неужели нет способа подгонки окна под произвольный текст (про HTML с графикой уж молчу) ???

Когда текст еще не вписан? Вряд ли это возможно или будет крайне сложно. Даже при уже вписанном тексте при разных способах подгонки, получаются разные результаты, т.е. при одних и тех же размерах окна (например 400х200) в одном случае будет скролл, в другом - нет, а в третьем тоже нет, но с пустой строкой.


Так что в итоге, на случай если буду использовать размер шрифта отличный от 17, переделал все под способ Dumby с небольшой модификацией

Dumby, поисковик яндекса с результатами поиска. При наведении мыши на ссылки мы видим реальные адреса, но стоит только вызвать контекстное меню, как адрес меняется на редирект, либо добавляется хвост.
Что бы вы посоветовали (без реализации) для избавления от такого изменения, с учетом, что реализация будет выполняться из консоли страницы?

6e73epo

Remove_Fake_Links, но это user.js скрипт

Dumby
Помогите, пожалуйста, сделать скрипт cookiesPermissions.js для загрузчика метода Aris-t2.
В userChrome.js прописано так:

Выделить код

Код:

userChrome.import("scripts", "UChrm");

Farby, как и ожидалось, Remove_Fake_Links не работает, если включены скрипты статистики яндекса

6e73epo пишет

Что бы вы посоветовали

К сожалению, ничего.
У меня странный FF78 с запрещёнными скриптами, куками, и всё такое прочее.
Там даже сам «поисковик яндекса с результатами поиска» просто не возможен.


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


rubel пишет

Помогите, пожалуйста, сделать скрипт cookiesPermissions.js для загрузчика метода Aris-t2.

Что значит «сделать скрипт cookiesPermissions.js»?
Скрипт вполне себе (как-то) сделан, но под CB,
а значит расчитан на (уже) существующую кнопку.


Если нужен скрипт, который такую кнопку создаст,
то клади в scripts рядом с ним что-то типа такого,
а у самого cookiesPermissions.js смени расширение на .txt
чтобы был не для исполнения загрузчиком, а для чтения скриптом.

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

Выделить код

Код:

window.__SSi == "window0" && CustomizableUI.createWidget({
	id: "CookiesPermissions",
	label: "Cookies Permissions",
	localized: false,
	onCreated(btn) {
		var u = Services.io.newURI;
		var code = Cu.readUTF8URI(u(
			u(Components.stack.filename).resolve("cookiesPermissions.txt")
		)).replace(
			'Components.utils.import("resource://gre/modules/Services.jsm", {})',
			"Cu.getGlobalForObject(Cu)"
		);
		var del = function() {
			this.previousSibling.remove();
			this.remove();
		};
		(this.onCreated = btn => {
			btn.defaultContextId = "toolbar-context-menu";
			var win = btn.ownerGlobal;
			var wdp = new win.DOMParser();
			var parser = class {
				parseFromString(...args) {
					var doc = wdp.parseFromSafeString(...args);
					doc.documentElement.lastChild.appendChild = del;
					return doc;
				}
			}
			win.setTimeout(() => new win.Function("DOMParser", code).call(btn, parser), 50);
		})(btn);
	}
});

Dumby

Если нужен скрипт, который такую кнопку создаст...

ОК. Спасибо. Прекрасно работает. :)

Dumby, можете попробовать во втором приближении перерисовать так, чтобы очистка памяти по левому клику мыши работала без наличия кнопки memoryMinimizationButton, без notifications, статус инфо и прочего мусора, требующегося для сборщиков мусора и без обсервера? Все это у меня отключено в параметрах. Достаточно так, но как на клик прописать?

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

Выделить код

Код:

function doMemMinimize() {
	var gMgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
	gMgr.minimizeMemoryUsage(() => {});
}


Вопрос решен. Одной строкой прописал и заработало

6e73epo пишет

Вопрос решен. Одной строкой прописал и заработало

Куда именно добавить, после какой строки?

fuchsfan, "memoryMinimizationButton.doMinimize()" заменил на
"Cc['@mozilla.org/memory-reporter-manager;1'].getService(Ci.nsIMemoryReporterManager).minimizeMemoryUsage(() => {})"
и не знаю, есть ли негативные последствия сего действия

Dumby

Уж не знаю что за Цент,а для лисы как-то так

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

Выделить код

Код:

(async (id, url) => {
	if (location != url) return;
	var menuitem = document.createXULElement("menuitem");
	document.getElementById(id).after(menuitem);
	var hidden = () => !nsContextMenu.contentData.context.linkTextStr;	
	menuitem.hidden = true;
	menuitem.render = () => {
		if (hidden()) return;
		menuitem.hidden = false;
		menuitem.id = id + "text";
		menuitem.label = "Скопировать текст ссылки";
		menuitem.setAttribute("oncommand", "navigator.clipboard.writeText(gContextMenu.linkTextStr);");
		delete menuitem.render;
		menuitem.render();
		menuitem.render = () => menuitem.hidden = hidden();
	}
})("context-copylink", "chrome://browser/content/browser.xhtml");

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

Злой Буратино пишет

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

Ух ты, как там всего навёрнуто!
Но, ничего, создал папку utils, а в ней «three files» :D руками.
И, рядом, папку JS, в которую положил test.uc.js с приведённым кодом.


И пункт контекстного меню страниц «Скопировать текст ссылки»
на ссылках появился, и копирует.
Таким образом, на первый взгляд, никакая переделка не требуется.

Dumby пишет

Ух ты, как там всего навёрнуто!

В описании так: начиная с версии «0.8» fx-autoconfig несовместим с Firefox ESR 102
Непонятно, с какой минимальной версии Firefox этот загрузчик скриптов работает ?

Странно, но не работает.
Либо неправильно понял... У меня в папке utils лежат файлы chrome.manifest, boot.sys.mjs, fs.sys.mjs, utils.sys.mjs. В папке JS (не подпапка, а папка этого же уровня, что и utils) лежат скрипты *.uc.js и, собственно, ваш скрипт. Все работают, ваш не хочет :D
Fx 115.0.4 ESR
ЧЯДНТ?

Dobrov пишет

В описании так: начиная с версии «0.8» fx-autoconfig несовместим с Firefox ESR 102

Ну правильно, там вызов метода, который импортирует ES-модуль,
а это специально выкинули из 102, чтоб не повадно было.

Непонятно, с какой минимальной версии Firefox этот загрузчик скриптов работает ?

Мне-то откуда ведомо, я его сегодня первый раз попробовал развернуть.
Рыться в кодах в поисках самого слабого звена — желания нет.
Заниматься бинарным поиском по-факту от 103 до 121 — тоже неохота.


Злой Буратино пишет

ЧЯДНТ?

Не знаю, и не могу знать.
Можно высказать странное предположение, что скрипт не в кодировке UTF-8.
Есть ли в консоли что-то типа «Caused by: SyntaxError: bad trailing UTF-8 byte 0xCA doesn't match the pattern 0b10xxxxxx»?


Нет, ну не обязательно чтобы только именно в UTF-8,
можно и в ANSI, но тогда и вместо "Скопировать текст ссылки"
придётся писать что-нибудь такое:
"\u0421\u043A\u043E\u043F\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0442\u0435\u043A\u0441\u0442 \u0441\u0441\u044B\u043B\u043A\u0438"

Dumby пишет

Можно высказать странное предположение, что скрипт не в кодировке UTF-8.

Бинго! :)
Спасибо, всё работает.

Dumby, попробовал сохранить страницу с поисковыми результатами яндекса, но в оффлайн режиме скрипты статистики не работают. Да и не беда. Я все равно такие скрипты не разрешаю, а без них победить прослушку можной одной строкой из консоли или букмарклета.
Однако, решил автоматизировать, но через скрипт, загружаемый из UserChrome.js, получить доступ к странице яндекса, наверное, не реально, да и ставить одно из обезьяних расширений из-за одного скрипта тоже не хотелось.
Добавил к скрипту манифест, упаковал в xpi, установил. Доступ к функциям страницы был получен малой кровью (wrappedJSObject). И все работает как надо

Dumby пишет

Ух ты, как там всего навёрнуто!

:beer: давно смотрю как загрузчик от ксяо раскорячило, ой продвинул MrOtherGuy

Farby
Посмотрел Ваш измененный пост и попробовал заменить в своем config.js на ваш новый код.
SingleHTML.jsm перестал работать как надо.

rubel пишет

Посмотрел Ваш

Так Вам

Dumby пишет

Перенёс ссылку из ucfBox'а в конфигский сандбокс, и теперь, вроде, не слетает.
Вобщем, попробуй заменить sb[Symbol()] на globalThis[Symbol()]

Уже давно расписал что надо сделать если у Вас не работает...

Здравствуйте.
На некоторых сайтах при закрытии страницы всплывает сообщение "Уйти со страницы" которое не позволяет закрыть вкладку не нажав соответствующую кнопку в предупреждении.

Например здесь: https://www.zbrushcentral.com/t/pendant/434810

Как убрать это предупреждение чтобы не появлялось вновь?

leex
В about:config - dom.disable_beforeunload = true
userChrome.js тут не причём .

А можно закрыть вкладку повторным нажатием, но не окно.

AlAvis пишет

leex
В about:config - dom.disable_beforeunload = true
userChrome.js тут не причём .


Благодарю!
Сработало.

Народ привет.
Не совсем по Firefox, как перенести этот js скрипт, чтобы работал на Vivaldi?

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

Выделить код

Код:

/* Firefox userChrome script
 * Right-clicking on tab button to close tab
 * Shift + right-clicking to popup menu
 * Tested on Firefox 91
 * Author: garywill (https://garywill.github.io)
 */

// ==UserScript==
// @include         main
// ==/UserScript==

console.log("right_click_close_tab.js");

(() => {

    gBrowser.tabContainer.addEventListener("TabOpen", eventTabAdded, false);
    function eventTabAdded(event) {
        var tab = event.target;
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    }
    
    
    function onTabEvent(event) {
        //console.log(event.type);
        if (event.button != 2 || event.shiftKey  ) 
            return;
            
        event.preventDefault();
        event.stopPropagation();
        
        if (event.type == 'click')
            gBrowser.removeTab(this, {animate: true});
    }
    
    gBrowser.tabContainer.querySelectorAll('tab').forEach( function(tab, index) {
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    });
    
})();
Nich пишет

Не совсем по Firefox, как перенести этот js скрипт, чтобы работал на Vivaldi?

К сожалению для вас спешу огорчить, это низкоуровневый скрипт, который исполнятся на уровне ядра [firefox]

gBrowser

Выделить код

Код:

// ==UserScript==
// @include         main
// ==/UserScript==


директива означает что, этот скрипт загружаться раньше чем вы увидели что [firefox] загрузился, что касается chromium подобных сущностей, рекомендую обратить внимание на приставку Version.dll, попробуйте уговорить умельцев создать загрузчик на уровне ядра Chromium

О кстати

https://github.com/Bush2021/chrome_plus
Features

    Double-click to close tab.
    Right-click to close tab. (Hold Shift to show the original menu.)
    Preserve the last tab (prevents the browser from closing when the last tab is closed; clicking the close button will not work.)
    Use the mouse wheel to switch tabs when hovering over the tab bar.
    Use the mouse wheel to switch tabs when holding the right mouse button.
    Create new tab to opens the contents entered in address bar. (Can be configured to open in foreground or background.)
    Create new tab to opens bookmarks. (Can be configured to open in foreground or background.)
    Disable the above two features when the current tab is a new tab.
    Customize hotkeys to quickly hide the browser window (boss key).
    Customize hotkeys to translate web page.
    Portable design: the program is placed in the App directory, and data is stored in the Data directory (incompatible with the original data; you can reinstall the system or change computers without losing data).
    Allow configuration of features using ini files.
    Allow custom Chromium startup parameters.


Вообще-то там это заявлено из коробки

Nich пишет

Vivaldi, как и Firefox, тоже поддерживает js скрипты.

когда увидите фразу

Firefox userChrome script

сразу проходите мимо, это не про Chromium Clones...

Nich пишет

Народ привет.
Не совсем по Firefox, как перенести этот js скрипт, чтобы работал на Vivaldi?

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

Выделить код

Код:

/* Firefox userChrome script
 * Right-clicking on tab button to close tab
 * Shift + right-clicking to popup menu
 * Tested on Firefox 91
 * Author: garywill (https://garywill.github.io)
 */

// ==UserScript==
// @include         main
// ==/UserScript==

console.log("right_click_close_tab.js");

(() => {

    gBrowser.tabContainer.addEventListener("TabOpen", eventTabAdded, false);
    function eventTabAdded(event) {
        var tab = event.target;
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    }
    
    
    function onTabEvent(event) {
        //console.log(event.type);
        if (event.button != 2 || event.shiftKey  ) 
            return;
            
        event.preventDefault();
        event.stopPropagation();
        
        if (event.type == 'click')
            gBrowser.removeTab(this, {animate: true});
    }
    
    gBrowser.tabContainer.querySelectorAll('tab').forEach( function(tab, index) {
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    });
    
})();

https://github.com/benzBrake/VivaldiMods/blob/main/chrome/userChromeJS/rightClickTabToClose.ac.js

brake пишет
Nich пишет

Народ привет.
Не совсем по Firefox, как перенести этот js скрипт, чтобы работал на Vivaldi?

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

Выделить код

Код:

/* Firefox userChrome script
 * Right-clicking on tab button to close tab
 * Shift + right-clicking to popup menu
 * Tested on Firefox 91
 * Author: garywill (https://garywill.github.io)
 */

// ==UserScript==
// @include         main
// ==/UserScript==

console.log("right_click_close_tab.js");

(() => {

    gBrowser.tabContainer.addEventListener("TabOpen", eventTabAdded, false);
    function eventTabAdded(event) {
        var tab = event.target;
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    }
    
    
    function onTabEvent(event) {
        //console.log(event.type);
        if (event.button != 2 || event.shiftKey  ) 
            return;
            
        event.preventDefault();
        event.stopPropagation();
        
        if (event.type == 'click')
            gBrowser.removeTab(this, {animate: true});
    }
    
    gBrowser.tabContainer.querySelectorAll('tab').forEach( function(tab, index) {
        tab.addEventListener('click', onTabEvent);
        tab.addEventListener('contextmenu', onTabEvent);
    });
    
})();

https://github.com/benzBrake/VivaldiMods/blob/main/chrome/userChromeJS/rightClickTabToClose.ac.js


Очень благодарю!

Ещё хотел спросить, вот у меня в Firefox, js скрипт активирует вкладку при наведении на нее, но не работает тогда выбор выделение вкладок, сбивается фокус, може есть какое нибудь решение?

Этот скрипт работал из config.js пока AboutNewTab.jsm не превратился в AboutNewTab.sys.mjs. Может кому-то не слабо переделать?

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

Выделить код

Код:

try {
  Cu.import("resource:///modules/AboutNewTab.jsm");

  if (AboutNewTab.newTabURL === "about:newtab") AboutNewTab.newTabURL = "about:blank";
} catch(e) {Cu.reportError(e)};

6e73epo
Эээ...


Cu.import("resource:///modules/AboutNewTab.jsm");
let {AboutNewTab} = ChromeUtils.importESModule("resource:///modules/AboutNewTab.sys.mjs");

Dumby
Спасибо. Делал также, но вместо ChromeUtils указывал Cu и получал: Cu.importESModule is not a function. Ну и зачем-то  полез искать решение в Cu.getGlobalForObject(Cu)

6e73epo пишет

вместо ChromeUtils указывал Cu

Cu.import("….jsm") и сейчас работает,
надо было только забрать AboutNewTab с того, что он возвращает.


Код, видимо, был сделан ещё во времена, когда этот метод
устанавливал экстортное добро прямо в глобальный объект.
Но он давно уже этого не делает.


Однако, ChromeUtils.importESModule() — это мейнстрим,
а Cu.import() подлежит удалению, надо полагать в 129.

зачем-то  полез искать решение в Cu.getGlobalForObject(Cu)

ChromeUtils был добавлен в конфигский сандбокс с Firefox 101+
А если бы не был добавлен, то правильно полез,
туда за ним слазить прямо напрашивается.

6e73epo

Dumby пишет

Но он давно уже этого не делает.

Похоже, здесь я совравши.
Подорвался проверить, в 125.0.1 запускаю с консоли


console.log(window.ProcessType);
Cu.import("resource://gre/modules/ProcessType.jsm");
console.log(window.ProcessType);


Первый лог — undefined
Второй лог — уже Object {...}


Тогда вернул Cu.import("resource:///modules/AboutNewTab.jsm");
Рестарт, и по Ctrl+T открывается about:blank


Я был уверен, что фичу выпилили, блин, всё надо проверять.
Осталось только непонятно, почему у тебя перестало работать.

Dumby
Вернул по старому и ошибок нет, а раньше была AboutNewTab is not defined. Теперь уже не знаю почему. Может Cu.import проходил с задержкой. Долго экспериментировал в стилях для установки фонового локального изображения для blanktab.html и about:blank. В итоге оставил только для blanktab.html.

@Dumby
Большая просьба восстановить работоспособность скрипта. Скрипт от aborix создает кнопку для обновления закладки, перестал работать в v129 (в v115.14.0esr работает). В чем его удобство и преимущество, так в том, что с ним, в отличие от скрипта в контекстное меню, невозможно ошибочно обновить (заменить), например, закладку на эту тему на закладку погодного сайта, вкладка которого сейчас активна.
По умолчанию скрипт имеет большую цветную демо-кнопку, но это не проблема, я могу заменить ее на свою.

Выделить код

Код:

(function() {

  if (window.__SSi != 'window0')
    return;

  CustomizableUI.createWidget({
    id: 'bookmark-update-button',
    label: 'Update Bookmark',
    tooltiptext: 'Update this Bookmark',
    defaultArea: CustomizableUI.AREA_NAVBAR,

    onCreated: button => {
  button.style.backgroundColor = 'hotpink';				// icon like a pink square
    },

    onCommand: async (event) => {
      let window = event.target.ownerGlobal;
      let document = window.document;
      window.FillHistoryMenu(document.getElementById('backForwardMenu'));
      let bookmarkUrl = document.querySelector('#backForwardMenu > menuitem[historyindex="-1"]')
                        ?.getAttribute('uri');
      let bookmark = await window.PlacesUtils.bookmarks.fetch({url: bookmarkUrl});
      if (bookmark) {
        window.PlacesUtils.bookmarks.update({
          guid: bookmark.guid,
          url: window.gBrowser.currentURI
        });
      }
    }
  });

})();
fuchsfan пишет

перестал работать в v129

FillHistoryMenu() теперь хочет event

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

Выделить код

Код:

/*
      window.FillHistoryMenu(document.getElementById('backForwardMenu'));
*/
      window.FillHistoryMenu({
        preventDefault() {},
        target: document.getElementById('backForwardMenu')
      });

Dumby пишет

FillHistoryMenu() теперь хочет event

Большое спасибо, чудесно!

Как-то копаясь по сампу, я там тестирую [firefox] переводчик. Нарвался на пост Mira-Belle. Оказывается они пытаются создать что-то на подобии рыжего [firefox] меню, правда я про меню совсем не помню, ибо [seamonkey]. Да ещё тут на форуме полетел шумок про main-menubar, Dumby его конечно же поправил. При всем уважении кому-то должно было стать стыдно, зачем нужно использовать три скрипта, да ещё перегруженных png,base64. В общем покопался и пересобрал рыжее меню для рыжего лиса, взял Endor8, прибавил Aris-t2 и конечно коды Dumby не прошел стороной.

Appmenu.uc.js

Выделить код

Код:

// ==UserScript==
// @name           Appmenu.uc.js
// @namespace      Appmenu@gmail.com
// @description    Basiert auf dem Script externalFuncButtonM.uc.js, Wiederherstellung der Orangenen FF-Menü Schaltfläche
// @include        main
// @version        update für Firefox 129+ by bege
// @author         defpt
// @charset        UTF-8
// @version        2019.08.04
// @version        2020.05.27
// @version        2020.07.13 Weitere Menüs und Funktionen ergänzt by bege
// @version        2024.08.10 alle Einstellungen im Abschnitt Konfiguration vornehmen
// ==/UserScript==

var Appmenu = {
    // Beginn der Konfiguration ------------------
    // Editor mit angegebenem Pfad verwenden
    // editor: 'C:\\Program Files\\Notepad++\\notepad++.exe',
    // oder
    // in 'view_source.editor.path' eingetragenen Editor verwenden
    editor: Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch).getCharPref('view_source.editor.path'),
    // Dateimanager mit angegebenem Pfad verwenden oder leer ('') wenn System-Dateimanager verwenden
    fileManager: '',
    // fileManager: 'C:\\Program files\\FreeCommander XE\\FreeCommander.exe',
    // Parameter für Dateimanager oder leer ('')
    FMParameter: '/T',
    // Submenüs ohne Inhalt im Hauptmenü automatisch ausblenden
    autohideEmptySubDirs: true,
    // Submenüs im Hauptmenü nach unten verschieben
    moveSubDirstoBottom: false,
    // Ort und Aussehen des Menü-Buttons einstellen
    isUrlbar: 0,  // 0: TabsToolbar, 1: navbar, 2: toolbar-menubar;
    isButton: 1,  // 0: Hamburger, klein; 1: Firefox, groß
    isEditMenu: 1, // Move Main-Menubar
    // Hotkey zum Öffnen des Appmenüs oder leer ('')
    hotkey: 'x',
    hotkeyModifier: 'alt',
    // Ende der Konfiguration --------------------

    isDElang: Cc["@mozilla.org/intl/localeservice;1"].getService(Ci.mozILocaleService).requestedLocale.includes("de"), // German Language
    style: `
/* appbutton */
  #main-window[tabsintitlebar] #AppMenuButton {
    padding-inline-start: 1.6em !important;
    padding-inline-end: 2em !important;
    border-radius: 0 0 4px 4px !important;
    border-top: none !important;
    border-right: 1px solid !important;
    border-left: 1px solid !important;
    border-bottom: 1px solid !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton .toolbarbutton-icon {
    display: none !important;
  }
  /* 'Firefox' title */
  #main-window[tabsintitlebar] #AppMenuButton[label="Firefox"]::after,
  #main-window[tabsintitlebar] #AppMenuButton:not([label="Nightly"],[label="Firefox Nightly"],[label="Firefox Developer Edition"],[label="Firefox"],[label="Tor Browser"],[label="Tor-Browser"])::after {
    content: "Firefox" !important;
  }
  /* 'DevFox' title */
  #main-window[tabsintitlebar] #AppMenuButton[label="Firefox Developer Edition"]::after {
    content: "DevFox" !important;
  }
  /* 'Nightly' title */
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Nightly"],[label="Firefox Nightly"])::after {
    content: "Nightly" !important;
  }
  /* 'Tor-Browser' title */
  #main-window[tabsintitlebar] #AppMenuButton:-moz-any([label="Tor Browser"],[label="Tor-Browser"])::after {
    content: "TorFox" !important;
  }
  /* orange (default) */
  #main-window[tabsintitlebar] #AppMenuButton {
    background-image: linear-gradient(rgb(247,182,82), rgb(215,98,10) 95%) !important;
    border-right-color:hsla(214,89%,21%,.5) !important;
    border-left-color: hsla(214,89%,21%,.5) !important;
    border-bottom-color: hsla(214,89%,21%,.5) !important;
    box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
  			  0 0 2px 1px hsla(0,0%,100%,.25) inset,
  			  0 1px 0 0px rgba(255,255,255,.6),
  			  0 -1px 0 0px rgba(255,255,255,.6),
  			  1px 0 0 0px rgba(255,255,255,.6),
  			  -1px 0 0 0px rgba(255,255,255,.6) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:hover:not(:active):not([open]) {
    background-image: radial-gradient(farthest-side at center bottom, rgba(252,240,89,.5) 10%, rgba(252,240,89,0) 70%),
  					radial-gradient(farthest-side at center bottom, rgb(236,133,0), rgba(255,229,172,0)),
  					linear-gradient(rgb(246,170,69), rgb(209,74,0) 95%) !important;
    border-color: rgba(83,42,6,.9) !important;
    box-shadow: 0 1px 0 hsla(0,0%,100%,.15) inset,
  			  0 0 2px 1px hsla(0,0%,100%,.5) inset,
  			  0 -1px 0 hsla(0,0%,100%,.2),
  			  0 1px 0 0px rgba(255,255,255,.6),
  			  0 -1px 0 0px rgba(255,255,255,.6),
  			  1px 0 0 0px rgba(255,255,255,.6),
  			  -1px 0 0 0px rgba(255,255,255,.6) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:is(:hover:active,[open]) {
    background-image: linear-gradient(rgb(246,170,69), rgb(209,74,0) 95%) !important;
    box-shadow: 0 2px 3px rgba(0,0,0,.4) inset,
      0 1px 1px rgba(0,0,0,.2) inset,
      0 1px 0 0px rgba(255,255,255,.6),
      0 -1px 0 0px rgba(255,255,255,.6),
      1px 0 0 0px rgba(255,255,255,.6),
      -1px 0 0 0px rgba(255,255,255,.6) !important;
  }
  /* Aurora */
  #main-window[tabsintitlebar] #AppMenuButton[label="Firefox Developer Edition"] {
    background-image: linear-gradient(hsl(208,99%,37%), hsl(214,90%,23%) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton[label="Firefox Developer Edition"]:hover:not(:active):not([open]){
    background-image: radial-gradient(farthest-side at center bottom, hsla(202,100%,85%,.5) 10%, hsla(202,100%,85%,0) 70%),
    radial-gradient(farthest-side at center bottom, hsla(205,100%,72%,.7), hsla(205,100%,72%,0)),
    linear-gradient(hsl(208,98%,34%), hsl(213,87%,20%) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton[label="Firefox Developer Edition"]:is(:hover:active,[open]) {
    background-image: linear-gradient(hsl(208,95%,30%), hsl(214,85%,17%) 95%) !important;
  }
  /* Nightly */
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Nightly"],[label="Firefox Nightly"]) {
    background-image: linear-gradient(hsl(211,33%,32%), hsl(209,53%,10%) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Nightly"],[label="Firefox Nightly"]):hover:not(:active):not([open]){
    background-image: radial-gradient(farthest-side at center bottom, hsla(210,48%,90%,.5) 10%, hsla(210,48%,90%,0) 70%),
    radial-gradient(farthest-side at center bottom, hsla(211,70%,83%,.5), hsla(211,70%,83%,0)),
    linear-gradient(hsl(211,33%,32%), hsl(209,53%,10%) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Nightly"],[label="Firefox Nightly"]):is(:hover:active,[open]) {
    background-image: linear-gradient(hsl(211,33%,26%), hsl(209,53%,6%) 95%) !important;
  }
  /* Tor-Browser */
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Tor Browser"],[label="Tor-Browser"]) {
    background-image: linear-gradient(rgb(153,38,211), rgb(105,19,163) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Tor Browser"],[label="Tor-Browser"]):hover:not(:active):not([open]){
    background-image: radial-gradient(farthest-side at center bottom, rgba(240,193,255,.5) 10%, rgba(240,193,255,0) 70%),
    radial-gradient(farthest-side at center bottom, rgb(192,81,247), rgba(236,172,255,0)),
    linear-gradient(rgb(144,20,207), rgb(95,0,158) 95%) !important;
  }
  #main-window[tabsintitlebar] #AppMenuButton:is([label="Tor Browser"],[label="Tor-Browser"]):is(:hover:active,[open]) {
    background-image: linear-gradient(rgb(144,20,207), rgb(95,0,158) 95%) !important;
  }
  /*private browsing - purple */
  #main-window[privatebrowsingmode=temporary][tabsintitlebar] #navigator-toolbox #AppMenuButton {
    background-image: linear-gradient(rgb(153,38,211), rgb(105,19,163) 95%) !important;
  }
  #main-window[privatebrowsingmode=temporary][tabsintitlebar] #navigator-toolbox #AppMenuButton:hover:not(:active):not([open]),
  #main-window[privatebrowsingmode=temporary][tabsintitlebar] #navigator-toolbox #PanelUI-button #PanelUI-menu-button:hover:not(:active):not([open]){
    background-image: radial-gradient(farthest-side at center bottom, rgba(240,193,255,.5) 10%, rgba(240,193,255,0) 70%),
    radial-gradient(farthest-side at center bottom, rgb(192,81,247), rgba(236,172,255,0)),
    linear-gradient(rgb(144,20,207), rgb(95,0,158) 95%) !important;
  }
  #main-window[privatebrowsingmode=temporary][tabsintitlebar] #navigator-toolbox #AppMenuButton:is(:hover:active,[open]) {
    background-image: linear-gradient(rgb(144,20,207), rgb(95,0,158) 95%) !important;
  }
}
    `,
    sss: Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService),
    subdirPopupHash: [],
    subdirMenuHash: [],
    iconsMenu: {
      "file-menu": `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path fill-rule="evenodd" d="M1.25 10.255V14c0 .414.336.75.75.75h12a.75.75 0 0 0 .75-.75v-3.745h-3.534c-.46 0-.868.292-1.016.727-.716 2.1-3.684 2.1-4.4 0a1.074 1.074 0 0 0-1.016-.727H1.25Zm13.5-1.25h-3.534c-.995 0-1.879.633-2.2 1.574-.33.97-1.702.97-2.032 0a2.324 2.324 0 0 0-2.2-1.574H1.25V2A.75.75 0 0 1 2 1.25h12a.75.75 0 0 1 .75.75v7.005ZM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" clip-rule="evenodd"/><path d="M3 3.625C3 3.28 3.28 3 3.625 3h8.75a.625.625 0 1 1 0 1.25h-8.75A.625.625 0 0 1 3 3.625ZM3 6.625C3 6.28 3.28 6 3.625 6h8.75a.625.625 0 1 1 0 1.25h-8.75A.625.625 0 0 1 3 6.625Z"/></svg>')`,
      "edit-menu": 'url("chrome://global/skin/icons/edit.svg")',
      "view-menu": `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path fill-rule="evenodd" d="M14 1.25H2a.75.75 0 0 0-.75.75v12c0 .414.336.75.75.75h12a.75.75 0 0 0 .75-.75V2a.75.75 0 0 0-.75-.75ZM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2Z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M4.25 4.25v3.5h7.5v-3.5h-7.5ZM4 3a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H4ZM10.25 11.25v.5h1.5v-.5h-1.5ZM10 10a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1h-2Z" clip-rule="evenodd"/><path d="M3 10.375c0-.345.28-.625.625-.625h3.75a.625.625 0 1 1 0 1.25h-3.75A.625.625 0 0 1 3 10.375ZM3 12.375c0-.345.28-.625.625-.625h3.75a.625.625 0 1 1 0 1.25h-3.75A.625.625 0 0 1 3 12.375Z"/></svg>')`,
      "history-menu": 'url("chrome://browser/skin/history.svg")',
      "bookmarksMenu": 'url("chrome://browser/skin/bookmark.svg")',
      "tools-menu": `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path fill-rule="evenodd" d="M1 6a2 2 0 0 1 2-2h1V3a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v1h1a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V6Zm3-.75H3a.75.75 0 0 0-.75.75v2H4v-.375a.625.625 0 1 1 1.25 0V8h5.5v-.375a.625.625 0 1 1 1.25 0V8h1.75V6a.75.75 0 0 0-.75-.75H4ZM2.25 13c0 .414.336.75.75.75h10a.75.75 0 0 0 .75-.75V9.25H12v1.125a.625.625 0 1 1-1.25 0V9.25h-5.5v1.125a.625.625 0 1 1-1.25 0V9.25H2.25V13ZM10 2.25H6a.75.75 0 0 0-.75.75v1h5.5V3a.75.75 0 0 0-.75-.75Z" clip-rule="evenodd"/></svg>')`,
      "helpMenu": 'url("chrome://global/skin/icons/help.svg")',
    },
    toolbar: {
        // Submenüs des Hauptmenüs definieren; Separator einfügen mit {name: 'separator'}
        subdirs: [{
            name: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
            id: 'AMfolders',
      image: "chrome://browser/skin/save.svg"
        },
        {
            name: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
            id: 'AMprofiles',
            image: "chrome://devtools/skin/images/folder.svg"
        },
        {
            name: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
            id: 'AMfeatures',
      image: "chrome://branding/content/about-logo.svg"
        },
        {
            name: 'about:',
            id: 'AMabout',
            image: "chrome://global/skin/icons/developer.svg"
        }],
        
        apps: [{
        // Untermenü Firefox Profil-Dateien
            name: 'userChrome.css',
            root: 'ProfD',
            path: '\\chrome\\userChrome.css',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image: 'data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M725.333333 85.333333l213.333334 213.333334v170.666666h-85.333334v-135.253333L689.92 170.666667H170.666667v298.666666H85.333333V85.333333zM256 768v42.666667a42.666667 42.666667 0 0 1-85.333333 0v-170.666667a42.666667 42.666667 0 0 1 85.333333 0v42.666667h85.333333v-42.666667a128 128 0 0 0-256 0v170.666667a128 128 0 0 0 256 0v-42.666667z m256-85.333333a42.666667 42.666667 0 1 1 42.666667-42.666667h85.333333a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z m298.666667 0a42.666667 42.666667 0 1 1 42.666666-42.666667h85.333334a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z" p-id="7385"/></svg>'
        },
        {
            name: 'userContent.css',
            root: 'ProfD',
            path: '\\chrome\\userContent.css',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image: 'data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M725.333333 85.333333l213.333334 213.333334v170.666666h-85.333334v-135.253333L689.92 170.666667H170.666667v298.666666H85.333333V85.333333zM256 768v42.666667a42.666667 42.666667 0 0 1-85.333333 0v-170.666667a42.666667 42.666667 0 0 1 85.333333 0v42.666667h85.333333v-42.666667a128 128 0 0 0-256 0v170.666667a128 128 0 0 0 256 0v-42.666667z m256-85.333333a42.666667 42.666667 0 1 1 42.666667-42.666667h85.333333a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z m298.666667 0a42.666667 42.666667 0 1 1 42.666666-42.666667h85.333334a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z" p-id="7385"/></svg>'
        },
        {
            name: 'userChrome.js',
            root: 'ProfD',
            path: '\\chrome\\userChrome.js',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 3.55h1.98v5.66c0 2.51-1.2 3.39-3.12 3.39-.47 0-1.07-.08-1.46-.21l.22-1.61c.28.1.64.16 1.02.16.84 0 1.36-.38 1.36-1.74V3.55zm3.7 6.87c.54.28 1.36.54 2.22.54.9 0 1.4-.37 1.4-.95 0-.54-.43-.87-1.49-1.24C9.37 8.24 8.4 7.44 8.4 6.15c0-1.5 1.27-2.65 3.33-2.65 1 0 1.73.21 2.26.46l-.45 1.58a4.13 4.13 0 00-1.83-.42c-.87 0-1.28.42-1.28.86 0 .56.49.81 1.63 1.26 1.56.57 2.28 1.37 2.28 2.63 0 1.48-1.13 2.73-3.55 2.73-1 0-1.99-.28-2.49-.55l.4-1.63z" /></svg>'
        },
        {
            name: 'prefs.js',
            root: 'ProfD',
            path: '\\prefs.js',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 3.55h1.98v5.66c0 2.51-1.2 3.39-3.12 3.39-.47 0-1.07-.08-1.46-.21l.22-1.61c.28.1.64.16 1.02.16.84 0 1.36-.38 1.36-1.74V3.55zm3.7 6.87c.54.28 1.36.54 2.22.54.9 0 1.4-.37 1.4-.95 0-.54-.43-.87-1.49-1.24C9.37 8.24 8.4 7.44 8.4 6.15c0-1.5 1.27-2.65 3.33-2.65 1 0 1.73.21 2.26.46l-.45 1.58a4.13 4.13 0 00-1.83-.42c-.87 0-1.28.42-1.28.86 0 .56.49.81 1.63 1.26 1.56.57 2.28 1.37 2.28 2.63 0 1.48-1.13 2.73-3.55 2.73-1 0-1.99-.28-2.49-.55l.4-1.63z" /></svg>'
        },
        {
            name: 'user.js',
            root: 'ProfD',
            path: '\\user.js',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 3.55h1.98v5.66c0 2.51-1.2 3.39-3.12 3.39-.47 0-1.07-.08-1.46-.21l.22-1.61c.28.1.64.16 1.02.16.84 0 1.36-.38 1.36-1.74V3.55zm3.7 6.87c.54.28 1.36.54 2.22.54.9 0 1.4-.37 1.4-.95 0-.54-.43-.87-1.49-1.24C9.37 8.24 8.4 7.44 8.4 6.15c0-1.5 1.27-2.65 3.33-2.65 1 0 1.73.21 2.26.46l-.45 1.58a4.13 4.13 0 00-1.83-.42c-.87 0-1.28.42-1.28.86 0 .56.49.81 1.63 1.26 1.56.57 2.28 1.37 2.28 2.63 0 1.48-1.13 2.73-3.55 2.73-1 0-1.99-.28-2.49-.55l.4-1.63z" /></svg>'
        },
        // Untermenü Firefox Verzeichnisse
        {
            name: this.isDElang ? 'Profil' : 'Profile',
            root: 'ProfD',
            path: '\\',
            subdir: this.isDElang ? 'Firefox Profil-Dateien' : 'Firefox Profile',
      image: "chrome://devtools/skin/images/folder.svg"
        },
        {
            name: 'chrome',
            root: 'ProfD',
            path: '\\chrome',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image: 'chrome://devtools/skin/images/browsers/chrome.svg'
        },
        {
            name: 'CSS',
            root: 'ProfD',
            path: '\\chrome\\CSS',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image: 'data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M725.333333 85.333333l213.333334 213.333334v170.666666h-85.333334v-135.253333L689.92 170.666667H170.666667v298.666666H85.333333V85.333333zM256 768v42.666667a42.666667 42.666667 0 0 1-85.333333 0v-170.666667a42.666667 42.666667 0 0 1 85.333333 0v42.666667h85.333333v-42.666667a128 128 0 0 0-256 0v170.666667a128 128 0 0 0 256 0v-42.666667z m256-85.333333a42.666667 42.666667 0 1 1 42.666667-42.666667h85.333333a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z m298.666667 0a42.666667 42.666667 0 1 1 42.666666-42.666667h85.333334a128 128 0 1 0-128 128 42.666667 42.666667 0 1 1-42.666667 42.666667h-85.333333a128 128 0 1 0 128-128z" p-id="7385"/></svg>'
        },
        {
            name: 'JS',
            root: 'ProfD',
            path: '\\chrome\\JS',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 3.55h1.98v5.66c0 2.51-1.2 3.39-3.12 3.39-.47 0-1.07-.08-1.46-.21l.22-1.61c.28.1.64.16 1.02.16.84 0 1.36-.38 1.36-1.74V3.55zm3.7 6.87c.54.28 1.36.54 2.22.54.9 0 1.4-.37 1.4-.95 0-.54-.43-.87-1.49-1.24C9.37 8.24 8.4 7.44 8.4 6.15c0-1.5 1.27-2.65 3.33-2.65 1 0 1.73.21 2.26.46l-.45 1.58a4.13 4.13 0 00-1.83-.42c-.87 0-1.28.42-1.28.86 0 .56.49.81 1.63 1.26 1.56.57 2.28 1.37 2.28 2.63 0 1.48-1.13 2.73-3.55 2.73-1 0-1.99-.28-2.49-.55l.4-1.63z" /></svg>'
        },
        {
            name: 'Addons',
            root: 'ProfD',
            path: '\\extensions',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image: 'chrome://mozapps/skin/extensions/category-extensions.svg'
         },
         {
            name: this.isDElang ? 'Programm' : "core/browser",
            root: 'CurProcD',
            path: '\\',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image: 'chrome://branding/content/about-logo.svg'
         },
         {
            name: 'Startup Cache',
            root: 'ProfLD',
            path: '\\startupCache',
            subdir: this.isDElang ? 'Firefox Verzeichnisse' : 'Firefox Folders',
      image: 'chrome://mozapps/skin/extensions/category-discover.svg'
         }
         ],
        
        configs: [
        // Untermenü Firefox Funktionen
        {
            name: this.isDElang ? 'Symbolleiste anpassen…' : 'Customize toolbar…',
            command: "gCustomizeMode.enter()",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://browser/skin/customize.svg'
        },

        {
            name: this.isDElang ? 'Neustart im abgesicherten Modus' : 'Reatart in Troubleshooting Mode',
            command: "safeModeRestart();",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://devtools/skin/images/debugging-workers.svg'
        },
        {
            name: this.isDElang ? 'Browser-Konsole' : 'Browser Console',
            command: "var { require } = ChromeUtils.importESModule('resource://devtools/shared/loader/Loader.sys.mjs', {});\
                      var { BrowserConsoleManager } = require('resource://devtools/client/webconsole/browser-console-manager');\
                      BrowserConsoleManager.openBrowserConsoleOrFocus();",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://devtools/skin/images/tool-webconsole.svg'
        },
        {
            name: this.isDElang ? 'Entwickler-Werkzeuge' : 'Web Developer Tools',
            command: "var { require } = ChromeUtils.importESModule('resource://devtools/shared/loader/Loader.sys.mjs', {});\
                var { gDevToolsBrowser } = require('devtools/client/framework/devtools-browser');\
                gDevToolsBrowser.toggleToolboxCommand(window.gBrowser, Cu.now());",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://global/skin/icons/performance.svg',
        },	
        {
            name: this.isDElang ? 'Browser-Werkzeuge' : 'Browser Toolbox',
            command: "var { require } = ChromeUtils.importESModule('resource://devtools/shared/loader/Loader.sys.mjs', {});\
                      var { BrowserToolboxLauncher } = require('resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs');\
                      BrowserToolboxLauncher.init();",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://devtools/skin/images/command-frames.svg',
        },
        {
            name: this.isDElang ? 'Firefox synchronisieren' : 'Firefox synchronise',
            command: "gSync.openPrefs('menubar');",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://browser/skin/sync.svg'
        },
        {
            name: this.isDElang ? 'Zugangsdaten und Passwörter' : 'Logins & Passwords',
            command: "LoginHelper.openPasswordManager(window, { entryPoint: 'mainmenu' })",
            tooltiptext: 'about:logins',
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'chrome://browser/skin/login.svg'
        },
        {
            name: this.isDElang ? 'Task Manager' : 'Task Manager',
            command: "switchToTabHavingURI('about:processes', true)",
            tooltiptext: 'about:processes',
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 5a3 3 0 0 1 6 0v7a3 3 0 1 1-6 0V5Z"/><path fill-rule="evenodd" d="M6.369 0c.345 0 .625.28.625.625v1.371a1.006 1.006 0 0 0 2.012 0V.626a.625.625 0 1 1 1.25 0v1.37a2.256 2.256 0 1 1-4.512 0V.626c0-.346.28-.626.625-.626ZM2.627 1c.345 0 .625.28.625.626v1.871c0 .76.616 1.376 1.376 1.376h6.745c.76 0 1.376-.616 1.376-1.376V1.626a.625.625 0 0 1 1.25 0v1.871a2.627 2.627 0 0 1-2.626 2.627H4.628A2.627 2.627 0 0 1 2 3.497V1.626c0-.345.28-.625.626-.625ZM0 8.63c0-.345.28-.625.625-.625h14.75a.625.625 0 1 1 0 1.25H.625A.625.625 0 0 1 0 8.63Zm4.628 3.498c-.76 0-1.376.616-1.376 1.375v1.872a.625.625 0 1 1-1.25 0v-1.872a2.627 2.627 0 0 1 2.626-2.626h6.745a2.627 2.627 0 0 1 2.626 2.626v1.872a.625.625 0 1 1-1.25 0v-1.872c0-.76-.616-1.375-1.376-1.375H4.628Z" clip-rule="evenodd"/></svg>'
        },
        {
            name: this.isDElang ? 'Offline arbeiten' : 'Work Offline',
            command: "BrowserOffline.toggleOfflineStatus();",
            subdir: this.isDElang ? 'Firefox Funktionen' : 'Firefox Features',
      image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path fill-rule="evenodd" d="m12.499 9.154 1.326-1.326a4 4 0 0 0-5.657-5.656L6.842 3.497a.625.625 0 0 0 0 .884l4.773 4.773c.244.244.64.244.884 0ZM9.052 3.055a2.75 2.75 0 0 1 3.889 3.89l-.878.878-3.89-3.89.879-.878ZM3.497 6.842 2.172 8.168a4 4 0 0 0 5.656 5.657l1.326-1.326a.625.625 0 0 0 0-.884L4.381 6.842a.625.625 0 0 0-.884 0Zm3.448 6.099a2.75 2.75 0 0 1-3.89-3.89l.876-.875 3.889 3.89-.875.875Z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M15.812.188a.625.625 0 0 1 0 .884l-2 2a.625.625 0 1 1-.884-.884l2-2a.625.625 0 0 1 .884 0Zm-8.37 6.37a.625.625 0 0 1 0 .884l-1.5 1.5a.625.625 0 0 1-.884-.884l1.5-1.5a.625.625 0 0 1 .884 0Zm2 2a.625.625 0 0 1 0 .884l-1.5 1.5a.625.625 0 1 1-.884-.884l1.5-1.5a.625.625 0 0 1 .884 0Zm-6.5 4.5a.625.625 0 0 1 0 .884l-1.87 1.87a.625.625 0 0 1-.884-.884l1.87-1.87a.625.625 0 0 1 .884 0Z" clip-rule="evenodd"/></svg>'
        },
        // Untermenü about:
        
        /* {
            name: 'separator'
        }, */
        {
            name: 'about:about',
            command: "openTrustedLinkIn('about:about', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://branding/content/about-logo.svg'
        },
        {
            name: 'about:cache',
            command: "openTrustedLinkIn('about:cache', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/developer.svg'
        },
        {
            name: 'about:certificate',
            command: "openTrustedLinkIn('about:certificate', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="16" height="16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M25 2C12.296875 2 2 12.296875 2 25C2 37.703125 12.296875 48 25 48C37.703125 48 48 37.703125 48 25C48 12.296875 37.703125 2 25 2 Z M 25 4C36.578125 4 46 13.421875 46 25C46 36.578125 36.578125 46 25 46C13.421875 46 4 36.578125 4 25C4 13.421875 13.421875 4 25 4 Z M 25 8C20.035156 8 16 12.035156 16 17L16 21L22 21L22 17C22 15.347656 23.347656 14 25 14C26.652344 14 28 15.347656 28 17L28 21L34 21L34 17C34 12.035156 29.964844 8 25 8 Z M 25 10C28.867188 10 32 13.132813 32 17L32 19L30 19L30 17C30 14.238281 27.761719 12 25 12C22.238281 12 20 14.238281 20 17L20 19L18 19L18 17C18 13.132813 21.132813 10 25 10 Z M 16 22C13.792969 22 12 23.792969 12 26L12 36C12 38.207031 13.792969 40 16 40L34 40C36.207031 40 38 38.207031 38 36L38 26C38 23.792969 36.207031 22 34 22 Z M 16 24L34 24C35.105469 24 36 24.894531 36 26L36 36C36 37.105469 35.105469 38 34 38L16 38C14.894531 38 14 37.105469 14 36L14 26C14 24.894531 14.894531 24 16 24 Z M 17 26C16.449219 26 16 26.449219 16 27L16 35C16 35.550781 16.449219 36 17 36C17.550781 36 18 35.550781 18 35L18 27C18 26.449219 17.550781 26 17 26 Z M 25 26C23.894531 26 23 26.894531 23 28C23 28.714844 23.382813 29.375 24 29.730469L24 35L26 35L26 29.730469C26.617188 29.371094 27 28.714844 27 28C27 26.894531 26.105469 26 25 26Z" /></svg>'

            
        },
        {
            name: 'about:checkerboard',
            command: "openTrustedLinkIn('about:checkerboard', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/clipboard.svg'
        },
        {
            name: 'about:compat',
            command: "openTrustedLinkIn('about:compat', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'resource://devtools-shared-images/alert-small.svg'
        },
        {
            name: 'about:config',
            command: "openTrustedLinkIn('about:config', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/settings.svg'
        },
        {
            name: 'about:crashes',
            command: "openTrustedLinkIn('about:crashes', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/loading.svg'
        },
        {
            name: 'about:debugging',
            command: "openTrustedLinkIn('about:debugging', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5 5a3 3 0 0 1 6 0v7a3 3 0 1 1-6 0V5Z"/><path fill-rule="evenodd" d="M6.369 0c.345 0 .625.28.625.625v1.371a1.006 1.006 0 0 0 2.012 0V.626a.625.625 0 1 1 1.25 0v1.37a2.256 2.256 0 1 1-4.512 0V.626c0-.346.28-.626.625-.626ZM2.627 1c.345 0 .625.28.625.626v1.871c0 .76.616 1.376 1.376 1.376h6.745c.76 0 1.376-.616 1.376-1.376V1.626a.625.625 0 0 1 1.25 0v1.871a2.627 2.627 0 0 1-2.626 2.627H4.628A2.627 2.627 0 0 1 2 3.497V1.626c0-.345.28-.625.626-.625ZM0 8.63c0-.345.28-.625.625-.625h14.75a.625.625 0 1 1 0 1.25H.625A.625.625 0 0 1 0 8.63Zm4.628 3.498c-.76 0-1.376.616-1.376 1.375v1.872a.625.625 0 1 1-1.25 0v-1.872a2.627 2.627 0 0 1 2.626-2.626h6.745a2.627 2.627 0 0 1 2.626 2.626v1.872a.625.625 0 1 1-1.25 0v-1.872c0-.76-.616-1.375-1.376-1.375H4.628Z" clip-rule="evenodd"/></svg>'
        },
        {
            name: 'about:downloads',
            command: "openTrustedLinkIn('about:downloads', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/downloads/downloads.svg'
        },
        {
            name: 'about:logging',
            command: "openTrustedLinkIn('about:logging', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://devtools/skin/images/tool-webconsole.svg'
        },
        {
            name: 'about:logins',
            command: "openTrustedLinkIn('about:logins', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/login.svg'
        },
        {
            name: 'about:memory',
            command: "openTrustedLinkIn('about:memory', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://devtools/skin/images/tool-memory.svg'
        },
        {
            name: 'about:networking',
            command: "openTrustedLinkIn('about:networking', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path fill-rule="evenodd" d="m12.499 9.154 1.326-1.326a4 4 0 0 0-5.657-5.656L6.842 3.497a.625.625 0 0 0 0 .884l4.773 4.773c.244.244.64.244.884 0ZM9.052 3.055a2.75 2.75 0 0 1 3.889 3.89l-.878.878-3.89-3.89.879-.878ZM3.497 6.842 2.172 8.168a4 4 0 0 0 5.656 5.657l1.326-1.326a.625.625 0 0 0 0-.884L4.381 6.842a.625.625 0 0 0-.884 0Zm3.448 6.099a2.75 2.75 0 0 1-3.89-3.89l.876-.875 3.889 3.89-.875.875Z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M15.812.188a.625.625 0 0 1 0 .884l-2 2a.625.625 0 1 1-.884-.884l2-2a.625.625 0 0 1 .884 0Zm-8.37 6.37a.625.625 0 0 1 0 .884l-1.5 1.5a.625.625 0 0 1-.884-.884l1.5-1.5a.625.625 0 0 1 .884 0Zm2 2a.625.625 0 0 1 0 .884l-1.5 1.5a.625.625 0 1 1-.884-.884l1.5-1.5a.625.625 0 0 1 .884 0Zm-6.5 4.5a.625.625 0 0 1 0 .884l-1.87 1.87a.625.625 0 0 1-.884-.884l1.87-1.87a.625.625 0 0 1 .884 0Z" clip-rule="evenodd"/></svg>'
        },
        {
            name: 'about:processes',
            command: "openTrustedLinkIn('about:processes', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            tooltiptext: 'Task Manager',
            subdir: 'about:',
            image:'chrome://global/skin/icons/performance.svg'
        },
        {
            name: 'about:policies',
            command: "openTrustedLinkIn('about:policies', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/content/policies/policies-active.svg'
        },
        {
            name: 'about:profiles',
            command: "openTrustedLinkIn('about:profiles', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/info.svg'
        },
        {
            name: 'about:profiling',
            command: "openTrustedLinkIn('about:profiling', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://devtools/skin/images/profiler-stopwatch.svg'
        },
        {
            name: 'about:protections',
            command: "openTrustedLinkIn('about:protections', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/tracking-protection.svg'
        },
        {
            name: 'about:rights',
            command: "openTrustedLinkIn('about:rights', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/illustrations/about-rights.svg'
        },
    {
            name: 'about:serviceworkers',
            command: "openTrustedLinkIn('about:serviceworkers', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/developer.svg'
        },
        {
            name: 'about:studies',
            command: "openTrustedLinkIn('about:studies', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/ion.svg'
        },
        {
            name: 'about:support',
            command: "openTrustedLinkIn('about:support', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://devtools/skin/images/browsers/firefox.svg'
        },
        {
            name: 'about:sync-log',
            command: "openTrustedLinkIn('about:sync-log', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/sync.svg'
        },
        {
            name: 'about:telemetry',
            command: "openTrustedLinkIn('about:telemetry', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/arrow-down.svg'
        },
        {
            name: 'about:third-party',
            command: "openTrustedLinkIn('about:third-party', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/library.svg'
        },
        {
            name: 'about:unloads',
            command: "openTrustedLinkIn('about:unloads', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            tooltiptext: 'Tabs entladen',
            subdir: 'about:',
            image:'chrome://mozapps/skin/extensions/category-available.svg'
        },
        {
            name: 'about:url-classifier',
            command: "openTrustedLinkIn('about:url-classifier', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://global/skin/icons/link.svg'
        },
        {
            name: 'about:webrtc',
            command: "openTrustedLinkIn('about:webrtc', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/notification-icons/screen.svg'
        },
        {
            name: 'about:windows-messages',
            command: "openTrustedLinkIn('about:windows-messages', gBrowser.selectedTab.isEmpty ? 'current' : 'tab')",
            subdir: 'about:',
            image:'chrome://browser/skin/window.svg'
        },

        // Hauptmenü  Einträge
        {
            name: this.isDElang ? 'Neues privates Fenster' : 'New Private Window',
            command: "OpenBrowserWindow({private: true});",
            id: 'AMprivate',
            image: "chrome://browser/skin/privateBrowsing.svg"
        },
        {
            name: 'separator'
        },
        {
            name: this.isDElang ? 'Einstellungen' : 'Firefox Settings',
            command: "openPreferences();",
            id: 'AMsettings',
      image: "chrome://devtools/skin/images/settings.svg",
        },
        {
            name: 'Add-ons',
            command: "BrowserAddonUI.openAddonsMgr();",
            id: 'AMaddons',
      image: "chrome://mozapps/skin/extensions/category-extensions.svg",
        },
        {
            name: 'separator'
        },

        {
            name: this.isDElang ? 'Lesezeichen-Verwaltung' : 'Manage Bookmarks',
            command: "PlacesCommandHook.showPlacesOrganizer('AllBookmarks');",
            id: 'AMbookmarks',
            image: "chrome://browser/skin/bookmark-star-on-tray.svg",
        },
/*         {
            name: 'separator'
        },
*/
        {
            name: this.isDElang ? 'Chronik' : 'History',
            command: "PlacesCommandHook.showPlacesOrganizer('History');",
            id: 'AMhistory',
            image: "chrome://browser/skin/history.svg",
        },
        {
            name: this.isDElang ? 'Downloads' : 'Downloads',
            command: "BrowserCommands.downloadsUI();",
            id: 'AMdownloads',
            image: "chrome://browser/skin/downloads/download-summary.svg",
        },
        {
            name: this.isDElang ? 'Seite speichern unter…' : 'Save Page as…',
            command: "saveBrowser(gBrowser.selectedBrowser)",
            id: 'AMsave',
            image: "chrome://browser/skin/save.svg",
        },
        {
            name: this.isDElang ? 'Chronik löschen' : 'Clear browsing data and cookies',
            command: "Sanitizer.showUI(window);",
            id: 'AMsanitize',
            image: "chrome://devtools/skin/images/clear.svg",
        },
        {
            name: 'separator',
        },
        {
            name: this.isDElang ? 'Neustart' : 'Restart',
            tooltiptext: this.isDElang ? 'userChrome.js-Cache wird geleert' : 'Restart and recreate the quick start cache.',
            // command: "Services.appinfo.invalidateCachesOnRestart(); BrowserUtils.restartApplication();",
        command: 'Services.appinfo.invalidateCachesOnRestart(); \
                      Services.startup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);',
            id: 'AMreboot',
            image: "chrome://devtools/skin/images/reload.svg",
        },
        {
            name: this.isDElang ? 'Beenden' : "Exit",
            command: "goQuitApplication(event);",
            id: 'AMquit',
      image: 'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill" fill-opacity="context-fill-opacity"><path d="M5.561 3.112c-.132-.32-.5-.474-.807-.314a7 7 0 1 0 6.492 0c-.306-.16-.675-.006-.807.314s.021.683.325.85a5.747 5.747 0 1 1-5.528 0c.303-.167.457-.53.325-.85Z"/><path fill-rule="evenodd" d="M8 1.375c.345 0 .625.28.625.625v6a.625.625 0 1 1-1.25 0V2c0-.345.28-.625.625-.625Z" clip-rule="evenodd"/></svg>'
        },

/*         {
            name: 'separator',
        },
 */        ]
    },
    _externalAppPopup: null,
    _isready: false,
    init: function() {
        this.handleRelativePath(this.toolbar.apps);
        const XULNS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';

        var ExternalAppBtn = document.createElementNS(XULNS, 'toolbarbutton');
        ExternalAppBtn.id = "AppMenuButton";
        ExternalAppBtn.className = "toolbarbutton-1";
        ExternalAppBtn.setAttribute("label", AppConstants.MOZ_APP_DISPLAYNAME_DO_NOT_USE);
        ExternalAppBtn.setAttribute("onclick", "event.preventDefault();event.stopPropagation();");
        ExternalAppBtn.setAttribute("tooltiptext", AppConstants.MOZ_APP_DISPLAYNAME_DO_NOT_USE + " " + (this.isDElang ? "Menü" : "Menu"));
        ExternalAppBtn.setAttribute("type", "menu");
        ExternalAppBtn.setAttribute("removable", "true");

      if (Appmenu.isButton) {
        const newURIParam = {
          aURL: 'data:text/css,' + encodeURIComponent(this.style),
          aOriginCharset: null,
          aBaseURI: null
        };
        const cssUri = Services.io.newURI(newURIParam.aURL, newURIParam.aOriginCharset, newURIParam.aBaseURI);
        if (!this.sss.sheetRegistered(cssUri, this.sss.USER_SHEET))
          this.sss.loadAndRegisterSheet(cssUri, this.sss.USER_SHEET);

      } else {
        ExternalAppBtn.style.listStyleImage = 'url("chrome://browser/skin/menu.svg")';
        ExternalAppBtn.style.MozContextProperties = "fill";
        ExternalAppBtn.style.setProperty("fill", "currentColor");
      }
        if (Appmenu.isUrlbar === 1) {
            var navBar = document.getElementById("nav-bar-customization-target");
            navBar.insertBefore(ExternalAppBtn, navBar.firstChild);
        } else if (Appmenu.isUrlbar === 2) {
            var menubar = document.getElementById("toolbar-menubar");
            menubar.insertBefore(ExternalAppBtn, menubar.firstChild);
        } else {
            var TabsToolbar = document.getElementById("TabsToolbar");
            TabsToolbar.insertBefore(ExternalAppBtn, TabsToolbar.firstChild);
        }

        var ExternalAppPopup = document.createElementNS(XULNS, 'menupopup');
        //ExternalAppPopup.setAttribute('onpopupshowing', 'event.stopPropagation(); Appmenu.onpopupshowing();');
        ExternalAppPopup.setAttribute('id', 'AMpopup');
        this._externalAppPopup = ExternalAppPopup;
        ExternalAppBtn.appendChild(ExternalAppPopup);
        Appmenu.onpopupshowing();
        
      // Menü mit Tastaturkürzel öffnen
        if (Appmenu.hotkey) {
        let key = document.createXULElement('key');
        key.id = 'key_AppMenuPopup';
        key.setAttribute('key', Appmenu.hotkey);
            if (Appmenu.hotkeyModifier)
        key.setAttribute('modifiers', Appmenu.hotkeyModifier);
        key.setAttribute('oncommand', 'document.getElementById("AMpopup").openPopup();');
        document.getElementById('mainKeyset').appendChild(key);
        }
    },

    onpopupshowing: function() {
        if (this._isready)
            return;
        if (this._externalAppPopup === null)
            return;
        var ExternalAppPopup = this._externalAppPopup;
        for (let subdir of this.toolbar.subdirs) {
            if (subdir.name == 'separator') {
                ExternalAppPopup.appendChild(document.createXULElement('menuseparator'));
            } else {
                var subdirItem = ExternalAppPopup.appendChild(document.createXULElement('menu'));
                var subdirItemPopup = subdirItem.appendChild(document.createXULElement('menupopup'));
                if (subdir.id) subdirItem.setAttribute('id', subdir.id);
                subdirItem.setAttribute('class', 'menu-iconic');
                subdirItem.setAttribute('label', subdir.name);
                subdirItem.setAttribute('image', subdir.image);
                Appmenu.subdirPopupHash[subdir.name] = subdirItemPopup;
                Appmenu.subdirMenuHash[subdir.name] = subdirItem;
            }
        }

        for (let app of this.toolbar.apps) {
            var appItem;
            if (app.name == 'separator') {
                appItem = document.createXULElement('menuseparator');
            } else {
                appItem = document.createXULElement('menuitem');
                appItem.setAttribute('class', 'menuitem-iconic');
                appItem.setAttribute('label', app.name);
                appItem.setAttribute('image', app.image);
                appItem.setAttribute('oncommand', "Appmenu.exec(this.path, this.args);");
                appItem.setAttribute('tooltiptext', app.name);
                appItem.path = app.path;
                appItem.args = app.args;
            }
            if (app.subdir && Appmenu.subdirPopupHash[app.subdir])
                Appmenu.subdirPopupHash[app.subdir].appendChild(appItem);
            else ExternalAppPopup.appendChild(appItem);
        }

        for (let config of this.toolbar.configs) {
            var configItem;
            if (config.name == 'separator') {
                configItem = document.createXULElement('menuseparator');
            } else {
                configItem = ExternalAppPopup.appendChild(document.createXULElement('menuitem'));
                configItem.setAttribute('class', 'menuitem-iconic');
                configItem.setAttribute('label', config.name);
                configItem.setAttribute('image', config.image);
                configItem.setAttribute('oncommand', config.command);
                if (config.tooltiptext) {
                configItem.setAttribute('tooltiptext', config.tooltiptext);
                } else {
                   configItem.setAttribute('tooltiptext', config.name);
                }
                configItem.setAttribute('id', config.id);
            }
            if (config.subdir && Appmenu.subdirPopupHash[config.subdir]) {
                Appmenu.subdirPopupHash[config.subdir].appendChild(configItem);
            } else {
                ExternalAppPopup.appendChild(configItem);
            }
        }

        if (this.autohideEmptySubDirs) {
            for (let i = 0; i < Appmenu.subdirPopupHash.length; i++) {
                if (Appmenu.subdirPopupHash[i].hasChildNodes()) {
                    continue;
                } else {
                    Appmenu.subdirMenuHash[i].setAttribute("hidden", "true");
                }
            }
        }

        if (this.moveSubDirstoBottom) {
            let i = ExternalAppPopup.childNodes.length;
            while (ExternalAppPopup.firstChild.getAttribute('class') != 'menuitem-iconic' && i-- != 0) {
                ExternalAppPopup.appendChild(ExternalAppPopup.firstChild);
            }
        }
        this._isready = true;
    },

    handleRelativePath: function(apps) {
        for (let app of apps) {
            if (app.path) {
                app.path = app.path.replace(/\//g, '\\');
                var ffdir = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get(app.root, Ci.nsIFile).path;
                if (/^(\\)/.test(app.path)) {
                    app.path = ffdir + app.path;
                }
            }
        }
    },

    exec: function(path, args) {
        args = args || [];
        var args_t = args.slice(0);
        for (let arg of args_t) {
            arg = arg.replace(/%u/g, gBrowser.currentURI.spec);
        }
        var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
        file.initWithPath(path);
        if (!file.exists()) {
            //Cu.reportError('Datei nicht gefunden: ' + path);
            alert('Datei nicht gefunden: ' + path);
            return;
        }
        if (file.isExecutable() && !path.endsWith('.js')) {
            var process = Cc['@mozilla.org/process/util;1'].createInstance(Ci.nsIProcess);
            process.init(file);
            process.run(false, args_t, args_t.length);
        } else if (file.isFile()) {
            if (this.editor) {
                let UI = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
                UI.charset = window.navigator.platform.toLowerCase().includes('win') ? 'Shift_JIS' : 'UTF-8';
                let path = UI.ConvertFromUnicode(file.path);
                let app = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
                app.initWithPath(this.editor);
                let process = Cc['@mozilla.org/process/util;1'].createInstance(Ci.nsIProcess);
                process.init(app);
                process.run(false, [path], 1);
            } else {
                file.launch();
            }
        } else if (file.isDirectory()) {
            if (this.fileManager) {
                let args=[this.FMParameter,path];
                let app = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
                app.initWithPath(this.fileManager);
                let process = Cc['@mozilla.org/process/util;1'].createInstance(Ci.nsIProcess);
                process.init(app);
                process.run(false, args, args.length);
            } else {
                file.launch();
            }
        }
    },

    editmenu: async () => {  // ZUGEFÜGT!
      if (document.getElementById("main-menubar").querySelectorAll(":scope > script").length >= 1) {
        const _AMjs = {};
        _AMjs.uri = "data:application/x-javascript;charset=UTF-8,";
        _AMjs.res = await fetch(document.getElementById("main-menubar").querySelectorAll(":scope > script")[0].src);
        _AMjs.text = (await _AMjs.res.text()).replace(/main-menubar/, "AMpopup");
        const scriptloader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
        scriptloader.loadSubScript(_AMjs.uri + encodeURIComponent(_AMjs.text), this);
      };
      setTimeout(function() {
        var pane1 = document.getElementById('AMpopup');
        var item2 = document.getElementById('AMfolders');

        // Menüs einfügen
        for (var menu of Array.from(document.getElementById("main-menubar").querySelectorAll(":scope > menu"))) {
          var popup = menu.menupopup;
          popup.remove();
          menu.textContent = menu.renderedOnce = "";
          var img = Appmenu.iconsMenu[menu.id];
          if (img) {
            menu.className = "menu-iconic";
            menu.style.listStyleImage = img;
          }
          menu.render;
          menu.append(popup);
          menu.disablrd = false;
          pane1.insertBefore(menu, item2);
        }
        var separator = document.createXULElement('menuseparator');
        separator.setAttribute('flex', '1');
        pane1.insertBefore(separator, item2);   

        // Script-Menüs einfügen
        var ids = ['usercssloader-menu', 'ExtraConfigMenu', 'no-eom-button'];
        for (var id of ids) {
          var _id =  document.getElementById(id);
          if (_id)
            pane1.insertBefore(_id, item2);
        }
      }, 2e3);
    }  // ZUGEFÜGT!
};

if (window.gBrowser) {
    Appmenu.init();
    if (Appmenu.isEditMenu)
      Appmenu.editmenu();
}


ЗЫ: кому по настольгировать, но там ещё есть режим иконки гамбергера, как по мне компактно и удобно.

Farby пишет

В общем покопался и пересобрал рыжее меню для рыжего лиса,

Мощное меню, и все работает!

Dumby посмотрите оконную виджет кнопочку-индикатор. Прослушки глобальные, действуют в любом месте окна, т.е. жестко с кнопкой не связаны. Цель: вместо этих прослушек добавить такую, чтобы кнопка делала opacity = 0.5 или disabled = true, если буфер обмена пуст и opacity = 1 или disabled = false? если буфер обмена не пуст.
Или с событием 'copy' такое не прокатит и нужно ждать поддержки такой фишки?

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && (async () => CustomizableUI.createWidget({
	id: "test_button",
	localized: false,
	get icon() {
		let icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABY0lEQVR4nGNgwANeL2EofrWUoYiBXPBoHsMfECbbgLszGf6DMNkGXJvM8B+EyTYgODXuPwiTpit0FTNv0ip1noTltsJhU/6DsED0Ylve6MXqIDm8ennjl8fyxi39yh+35D9/7KL/ArEL/gvEzPsvGD33v2DU7P+CkTO/CodPi8VpAH/sglUCMfPhGoQiZ/0XipjxXzh8GtglIqGT/ouGTFiF04CP29T2f9qu/h8f/rhNbR9OA77sMXz5ZY/hf3z4826D51g1fzpkI/r9oPV/XPjjLhMEe7epMIYBv486+/w66vQfhL8fsgfTyPjtVn04+/dhJ09MA054tvw56fUfhD/uMgfTyPjNJk04+/cJryYMA/6e9j/+74z/fxB+s0kDTCPj1+tk//897Qtm/z3jdxTDgP/nQrL+nwua+/9c0KG368S//j8b9O7/uaBfYHw26N3HzfIffx9zPg1RE5KFMyZIBQA3pi1KgcfhAAAAAABJRU5ErkJggg";
		let subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		delete this.icon;
		return this.icon = "resource://" + subst;
	},
	onCreated(btn) {
		btn.ownerGlobal.addEventListener('mousedown', () => btn.style.opacity = 0.5);
		btn.ownerGlobal.addEventListener('mouseup', () => btn.style.opacity = 1);
		btn.setAttribute("image", this.icon);
	},
}))();

dinn пишет

Или с событием 'copy' такое не прокатит

Какое «такое»? Событие "copy" наступит когда случится копирование
в этом окне, а значит в этом процессе, не в контентских процессах.
А даже если слушать и в них, то это всё равно всего лишь copy.

нужно ждать поддержки такой фишки?

Да, такая фишка была бы здесь полезна, только это настораживает:
«
    If the clipboard contents are changed outside the user agent,
    then the clipboardchange event MUST fire when the user agent regains focus.
»
Хотел попробовать записать какой-то аналог на ctypes,
но далеко не продвинулся, то ли в ctypes что-то сломано,
то ли руки кривые, но при попытке использовать функцию
(процедуру, как в осуществлении SetWindowsHookExW() или SetWindowLongA())
браузер валится с MozCrashReason: *** Compartment mismatch 0 vs. xxxxxxxx at argument 0


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

Dumby пишет

можно было бы и интервал подрядить на сколько не жалко.

Это интересно только в плане технической реализации

Dumby пишет

Но как узнать что буфер обмена пуст? В смысле, какое состояние буфера считать пустым?

Все упрощается тем, что я никогда не очищаю буфер вне браузера, т.е. это будет делать кнопка. А в плане реализации мне не подходит оконный readFromClipboard(). Это становится понятным, если в буфере несколько типов данных, например, мы сначала скопировали текст, а потом картинку. Подходит await navigator.clipboard.read().
В итоге сделал такую кнопку. Просьба посмотреть и более грамотно расписать с точки зрения гуру, ну и заменить readFromClipboard()

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

Выделить код

Код:

location.href.endsWith("://browser/content/browser.xhtml") && (async (alertsService) => CustomizableUI.createWidget({
	id: "my-cleanClipbrd",
	label: "Clean clipbrd",
	tooltiptext: "Clean data from Clipboard",
	localized: false,
	get icon() {
		let icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABY0lEQVR4nGNgwANeL2EofrWUoYiBXPBoHsMfECbbgLszGf6DMNkGXJvM8B+EyTYgODXuPwiTpit0FTNv0ip1noTltsJhU/6DsED0Ylve6MXqIDm8ennjl8fyxi39yh+35D9/7KL/ArEL/gvEzPsvGD33v2DU7P+CkTO/CodPi8VpAH/sglUCMfPhGoQiZ/0XipjxXzh8GtglIqGT/ouGTFiF04CP29T2f9qu/h8f/rhNbR9OA77sMXz5ZY/hf3z4826D51g1fzpkI/r9oPV/XPjjLhMEe7epMIYBv486+/w66vQfhL8fsgfTyPjtVn04+/dhJ09MA054tvw56fUfhD/uMgfTyPjNJk04+/cJryYMA/6e9j/+74z/fxB+s0kDTCPj1+tk//897Qtm/z3jdxTDgP/nQrL+nwua+/9c0KG368S//j8b9O7/uaBfYHw26N3HzfIffx9zPg1RE5KFMyZIBQA3pi1KgcfhAAAAAABJRU5ErkJggg";
		let subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		delete this.icon;
		return this.icon = "resource://" + subst;
	},
	onCreated(btn) {
		btn.setAttribute("image", this.icon);
		args11 = ["popuphiding", () => {
			console.log('popup close');
			if (!readFromClipboard()) return;
			btn.style.visibility = 'visible';
			PlacesToolbarItems.removeEventListener(...args11);
			console.log('remove listener');
		}];
		if (!readFromClipboard()) {
			btn.style.visibility = 'hidden';
			PlacesToolbarItems.addEventListener(...args11);
		}
	},
	onCommand(e) {
		Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
		e.target.style.visibility = 'hidden';
		PlacesToolbarItems.addEventListener(...args11);
		alertsService.showAlertNotification("chrome://browser/skin/customizableui/whimsy.png", "Clipboard", "Буфер обмена растоптан!");
		setTimeout(() => alertsService.closeAlert(), 2000);
    },
}))(Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService));

dinn пишет

заменить readFromClipboard()

На await navigator.clipboard.read() что ли?
А в чём тут затруднение? Выражение вернёт массив,
либо пустой, либо не пустой, просто проверяем length


Другое дело, что navigator.clipboard берёт лишь немногое.
Например, в копируем файл в проводнике,
и в буфере торчит больше десятка форматов,
а await navigator.clipboard.read() отдаёт пустой массив.

Просьба посмотреть

Здесь виджет создаётся кодом, исполнившемся в окне,
и многое некоторое привязано к этому окну.
Например, PlacesToolbarItems вычисляется
как #PlacesToolbarItems того окна, в котором создан виджет.


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


Также args11, мало того, что это ненужная глобальная переменная,
типа window.args11, так при открытии нового окна, код перепишет этот args11,
и если листенер из предыдущего args11 был добавлен и не удалён, то его уже не удалить.


setTimeout() это тоже setTimeout() окна происхождения,
если это окно закрыть, то setTimeout() работать перестанет.
И если это ещё можно поправить, то если в код добавить async-await,
то от отвала асинхронной машинерии при закрытии окна происхождения
уже не спасёт ничто.


Лучше создавать виджет в другом месте, вот, например, через SystemGlobal.eval()

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

Выделить код

Код:

(async p => await p || window.__SSi == "window0" && Cu.getGlobalForObject(Cu)
	.eval(`(${(m, self, widget) => widget = m.CustomizableUI.createWidget(self = {

	id: "my-cleanClipbrd",
	label: "Clean clipbrd",
	tooltiptext: "Clean data from Clipboard",
	localized: false,
	get icon() {
		let icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABY0lEQVR4nGNgwANeL2EofrWUoYiBXPBoHsMfECbbgLszGf6DMNkGXJvM8B+EyTYgODXuPwiTpit0FTNv0ip1noTltsJhU/6DsED0Ylve6MXqIDm8ennjl8fyxi39yh+35D9/7KL/ArEL/gvEzPsvGD33v2DU7P+CkTO/CodPi8VpAH/sglUCMfPhGoQiZ/0XipjxXzh8GtglIqGT/ouGTFiF04CP29T2f9qu/h8f/rhNbR9OA77sMXz5ZY/hf3z4826D51g1fzpkI/r9oPV/XPjjLhMEe7epMIYBv486+/w66vQfhL8fsgfTyPjtVn04+/dhJ09MA054tvw56fUfhD/uMgfTyPjNJk04+/cJryYMA/6e9j/+74z/fxB+s0kDTCPj1+tk//897Qtm/z3jdxTDgP/nQrL+nwua+/9c0KG368S//j8b9O7/uaBfYHw26N3HzfIffx9zPg1RE5KFMyZIBQA3pi1KgcfhAAAAAABJRU5ErkJggg";
		let subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		delete this.icon;
		return this.icon = "resource://" + subst;
	},
	show: async win => (await win.navigator.clipboard.read()).length,
	args11: ["popuphiding", async e => {
		console.log("popup closing");
		await self.show(e.view) && self.setState(
			"visible", "removeEventListener",
			console.log("remove listener(s)")
		);
	}],
	setState(visibility, method) {
		for(var {node} of widget.instances)
			node.style.visibility = visibility,
			node.trg[method](...this.args11);
	},
	async onCreated(btn) {
		btn.setAttribute("image", this.icon);
		btn.trg = btn.ownerDocument.getElementById("PlacesToolbarItems");

		if (await this.show(btn.ownerGlobal)) return;
		btn.style.visibility = "hidden";
		btn.trg.addEventListener(...this.args11);
		console.log("add listener");
	},
	onCommand(e) {
		Services.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
		self.setState("hidden", "addEventListener");
		console.log("add listener(s)");
		self.notify(e.view);
	},
	notify(win) {
		var name = "whimsy_clipboard";
		var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
		var close = as.closeAlert.bind(null, name);
		(this.notify = win => {
			as.showAlertNotification(
				"chrome://browser/skin/customizableui/whimsy.png",
				"Clipboard", "Буфер обмена растоптан!", false, "", null, name
			);
			win.setTimeout(close, 2e3);
		})(win);
	},

})})(ChromeUtils.importESModule("resource:///modules/CustomizableUI.sys.mjs"));`))(window.delayedStartupPromise);

Dumby пишет

копируем файл в проводнике,
и в буфере торчит больше десятка форматов,
а await navigator.clipboard.read() отдаёт пустой массив

Да, так и есть, для .read() и readFromClipboard() - это неизвестный формат данных. Сможем ли где-то в браузере вставить такое? Вряд ли. Но и веб стандарт по буферу еще не до конца реализован в браузерах

виджет создаётся кодом, исполнившемся в окне,
и многое некоторое привязано к этому окну

Редко когда открываю window1, разве что случайно нажав Ctrl+N, и сразу закрываю. Отношусь к одноокошечникам.

args11, мало того, что это ненужная глобальная переменная

С моими познаниями даже не знаю как, например, в onCommand() получить значение переменной, созданной через let (var) в onCreated()

Лучше создавать виджет в другом месте, вот, например, через SystemGlobal.eval()

А если работать только в одном окне, то лучше тоже через SystemGlobal.eval()?

Спасибо за виджет и информацию, все работает как надо


Но главный вопрос, который меня интересует: возможно ли скриптом внедрить свой css на страницу about:preferences для ее стилизации?

dinn пишет

С моими познаниями даже не знаю как, например, в onCommand() получить значение переменной, созданной через let (var) в onCreated()

Неудивительно.
Такое не возможно ни с какими познаниями, просто потому что — никак.
Ну, вариант вытащить ссылку дебаггером мы здесь рассматривать не будем,
поскольку это лютая жесть.

А если работать только в одном окне, то лучше тоже через SystemGlobal.eval()?

Если так ставить вопрос, то получается, что не особо лучше.


Но, вот послушай.
Теретически, открыть окно браузера может и вэб-контент.
Допустим, например, в about:preferences#privacy снята галка
«Блокировать всплывающие окна».
Открываем адрес data:text/plain;charset=utf-8,test
и с веб-консоли (Ctrl+Shift+K) запускаем
open("about:logo", "_blank", "width=300,height=280");


Открывается окошко, которое не выглядит как окно браузера,
но, тем не менее, это именно оно.


Или, вот, например, ставим этот (замечательный, кстати) аддон.
Убеждаемся, что в настройках аддона стоит галка [✔] Открывать в окне.
Теперь, ПКМ на какой-нибудь ссылке —> «Свойства ссылки».


Открывается окно, которое выглядит ещё менее похожим на окно браузера,
тем не менее, это именно оно. И аддон здесь нипричём, просто таково
осуществление в браузере симуляции гуглячьего API.


Всё это, скорее, важно не само по себе,
а при наличии в коде чего-то деструктивного, вроде переопределения args11.


Короче, это я всё к тому, что делай как хочешь, но делай осознанно.
Типа да, я знаю, что args11 будет торчать в окне, но это так проще и удобнее.
И, имя args11 выглядит достаточно уникальным, чтобы не образовывать конфликта имён.
Ещё раз — сознательно, а не то, чтобы просто по запарке.

Но главный вопрос, который меня интересует: возможно ли скриптом внедрить свой css на страницу about:preferences для ее стилизации?

Ооо, здесь нужно железобетонное обоснование
чем «для ее стилизации» не угодил userContent.css


Почему именно «скриптом», почему именно «внедрить на страницу»,
и всякие иные прочие подробности.
И, если есть сам скрипт, пусть даже неудачный, то его тоже.
В любом случае, разумеется, ответ подразумевает начинаться с "Да, это возможно."

Dumby пишет

здесь нужно железобетонное обоснование

Его нет, но есть интерес альтернативного подхода. Если очень кратко, то какова вероятность того, что при переносе на четвертый уровень, а затем полного удаления документа из web стандарта, это не распространится на юзер стили agent, chrome и content? В поисках альтернативы был успешно опробован метод, а вот что делать с внутренними страницами about:*, chrome:* пока не знаю. Или слишком забегаю вперед?

dinn пишет

был успешно опробован метод

Это что ещё за monkey-offtopic?

что делать

Вот, развлекайся

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

Выделить код

Код:

location == "chrome://browser/content/browser.xhtml"
&& addEventListener("DOMDocElementInserted", e => {
	var doc = e.target;
	if (!doc.documentURI.startsWith("about:preferences")) return;

	var css1 = "richlistitem {background-color: pink !important}";
	doc.documentElement.appendChild(doc.createElement("style")).append(css1);

	var win = doc.ownerGlobal;

	var css2 = "h1, h2 {background-color: limegreen !important;}";
	var sheet = new win.CSSStyleSheet();
	sheet.replaceSync(css2);
	doc.adoptedStyleSheets.push(sheet);

	var wu = win.windowUtils;

	var css3 = "label, description {background-color: yellow !important; color: red !important;}";
	wu.loadSheetUsingURIString("data:text/css," + encodeURIComponent(css3), wu.USER_SHEET);
});

Dumby пишет

Вот, развлекайся

ооо, спасибо, красота. Целых 3 способа. С конструктором больше понравилось + есть гордая надпись в правилах: constructed.

Это что ещё за monkey-offtopic?

Не совсем, если userChrome.js скрипт не умеет выборочно стилизовать http(s) страницы

Dumby, может перед push впаять wrappedJSObject? Не дает мне стилизовать меню и настройки одного из расширений

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

Выделить код

Код:

location == "chrome://browser/content/browser.xhtml" && addEventListener("DOMDocElementInserted", e => {
	let doc = e.target;
	if (!doc.documentURI.startsWith("moz-extension://f6665cb1")) return;
	let win = doc.ownerGlobal;
	
	let file = Services.dirsvc.get("UChrm", Ci.nsIFile);
	file.initWithPath(file.path+"\\css\\test\\yt_dark.css");
	let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
	let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
	fis.init(file, -1, 0, 0);
	sis.init(fis, "UTF-8");
	let data = sis.read(sis.available());

	let sheet = new win.CSSStyleSheet();
	sheet.replaceSync(data);
	doc.adoptedStyleSheets.push(sheet);
});


e.target выдает, что readyState находится на этапе loading, а значит title всегда будет пустым. Неужели нет способа как-то проверить, чтобы стилизовать именно это расширение, а не другое? приходится часто менять буквы цифры в moz-extension://

dinn пишет

расширений

Расширения запрещены со времён Firefox 57.

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

Ну по id'шнику можно, только они тоже бывают uuid'ом.

e.target выдает, что readyState находится на этапе loading, а значит title всегда будет пустым

Это логично, однако, только что-то я тебя не пойму.
Звучит так, словно WebExtensions у тебя в родительском процессе.


Но разве такое сейчас возможно?
Когда-то давно было возможно, но уже тогда они,
в таком случае, работали криво, а теперь, вроде, вообще не работают.


WebExtensions живут в своём отдельном процессе.
А значит, бесполезно что-то слушать в окне браузера,
которое в родительском процессе, в котором
аддонского добра просто быть не может, никакого.


Чтобы действовать в аддонском процессе,
лучше всего подходит зарегистрировать JSWindowActor
то есть .mjs скрипт-модуль.
Но для этого нужен файл на диске, и адрес на него
по протоколу chrome: или resource:


Поскольку о своём uc-скриптоприёмнике ты не рассказал ничего
(ведь в некоторых может есть своя встроенная хром/ресурс регистрация),
то вот, например, регистрируем ресурс на папку css
и регистрируем actor созданный в этой папке css
под именем WebExtensionsStylerChild.mjs

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

Выделить код

Код:

(async p => {
	if (await p || window.__SSi != "window0") return;

	var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
	file.append("css");
	var subst = "uc-css-folder";
	Services.io.getProtocolHandler("resource")
		.QueryInterface(Ci.nsIResProtocolHandler)
		.setSubstitution(subst, Services.io.newFileURI(file));

	ChromeUtils.registerWindowActor("WebExtensionsStyler", {
		child: {
			events: {DOMDocElementInserted: {}},
			esModuleURI: `resource://${subst}/WebExtensionsStylerChild.mjs`
		},
		allFrames: true,
		remoteTypes: ["extension"]
	});
})(window.delayedStartupPromise);


Теперь про mjs'ку.
Аддонский процесс это процесс контентский, и оперирование файлами
в нём ограничено. Stream'ы воротят от файла нос. Services.dirsvc тоже.
PathUtils.profileDir — выдаёт ошибку. Cu.readUTF8URI() — тоже.


Cu.readUTF8File() — работает, и с ним попроще,
но в консоли есть предупреждение: «Use of nsIFile in content process is deprecated.»


А раз тебе «С конструктором больше понравилось», то надо читать с диска,
хотя, казалось бы, лучше использовать windowUtils
не только потому, что он сам будет читать, но и потому, что можно
заказать author, user или agent origin стиля, а для adopted — нельзя.


Ладно, вот, например, чтение через fetch()
и синхрон-костыль, потому что не хочется чего-то лишнего
в других частях кода, а читаться каждый стиль будет только раз.

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

Выделить код

Код:

var data = {

	"uBlock0@raymondhill.net": {
		"popup-fenix.html": "ublock/popup",
		"dashboard.html": "ublock/prefs-root",
		"1p-filters.html": "ublock/prefs-my-filters",
		"whitelist.html": "ublock/prefs-test-non-existent-style-throws-once",
	},
	"linkPropertiesPlus@infocatcher": {
		"properties.html": "lpp/link-props"
	},
	"treestyletab@piro.sakura.ne.jp": {
		"sidebar/sidebar.html?style=photon&reloadMaskImage=true": "tst/two-sidebars",
		"sidebar/sidebar.html?style=highcontrast&reloadMaskImage=true": "tst/two-sidebars",
	},
};

var read = obj => {
	var folder = Services.io.newURI(import.meta.url).resolve("..");

	var text, thread = Services.tm.currentThread;
	var tick, ex, stop = () => tick = false, err = e => ex = e;
	var ftch = async url => text = await (await fetch(url)).text();

	return (read = obj => {
		var url = folder + obj.css + ".css";
		ftch(url).catch(err).finally(tick = stop);
		while(tick) thread.processNextEvent(true);
		if (ex)
			return delete data[obj.id][obj.path],
			console.error(`Fetch-Sheet-Error for ${obj.id}\n${url}\n${ex.message}`),
			ex = null;

		var res = text;
		text = null;
		return data[obj.id][obj.path] = res;
	})(obj);
}

var readSheet = function() {
	return read(this);
}

for(var [id, obj] of Object.entries(data)) {
	var dupes = {};
	for(var path in obj) {

		var css = obj[path];
		delete obj[path];
		path = "/" + path;

		if (css in dupes) {
			if (typeof dupes[css] == "string") {
				const parent = obj, link = dupes[css];
				dupes[css] = () => parent[link];
			}
			Object.defineProperty(obj, path, {
				get: dupes[css], configurable: true, enumerable: true
			});
		}
		else
			dupes[css] = path,
			(obj[path] = {css, path, id})[Symbol.toPrimitive] = readSheet;
	}
}

export class WebExtensionsStylerChild extends JSWindowActorChild {
	handleEvent(e) {
		var doc = e.target;
		var obj = data[doc.nodePrincipal.addonId];
		if (obj) {
			var u = doc.documentURIObject;
			var style =
				obj[u.pathQueryRef] ||
				u.hasRef && obj[u.specIgnoringRef.slice(52)] ||
				obj[u.filePath];

			if (style) {
				var sheet = new doc.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				doc.adoptedStyleSheets.wrappedJSObject.push(sheet);
			}
		}
	}
}


Кстати, можно выкинуть всё связанное со стилями,
и воткнуть проставление на <html> атрибутов "data-a-url" и "data-a-domain"
Тогда будет типа унификация с методом, который «был успешно опробован».

может перед push впаять wrappedJSObject?

Я сначала впаял перед doc, но да, перед push тоже работает.

Dumby пишет

WebExtensions живут в своём отдельном процессе.
А значит, бесполезно что-то слушать в окне браузера,
которое в родительском процессе, в котором
аддонского добра просто быть не может, никакого.

Ну почему бесполезно слушать? Пока еще не удалили эту настройку, а если она в false, то можно послушать


Поскольку о своём uc-скриптоприёмнике ты не рассказал ничего

Этот. Нет в нем никаких регистраций


Теперь про mjs'ку

В вашем первом коде все понятно, норм зарегилось, а во втором коде ничего не ясно. Это содержимое WebExtensionsStylerChild.mjs? Как этот mjs запускать? Что в нем за export .. extends? Не получилось ничего стилизовать таким способом, хотя бы на примере uMatrix


А раз тебе «С конструктором больше понравилось

Чтение из css файла я только с ним попробовал


Кстати, можно выкинуть всё связанное со стилями,
и воткнуть проставление на <html> атрибутов "data-a-url" и "data-a-domain"
Тогда будет типа унификация с методом, который «был успешно опробован»

Было бы круто, но раз не смог разобраться с тем, что понимает первоклассник, то  ...

dinn пишет

Ну почему бесполезно слушать? Пока еще не удалили эту настройку, а если она в false, то можно послушать

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


Но тогда непонятно в чём затруднение.
Событие приходит и с попапа, и со страницы.

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

Выделить код

Код:

location == "chrome://browser/content/browser.xhtml" && addEventListener("DOMDocElementInserted", e => {
	let doc = e.target;

	if (doc.nodePrincipal.addonId == "uMatrix@raymondhill.net") {

		var uri = doc.documentURIObject;

		console.log("\u{1f7e9}".repeat(7) + " uMatrix DOCUMENT DETECTED\n" + uri.spec);

		var color;
		switch (uri.filePath) {
			case "/popup.html":     color = "deeppink"; break;
			case "/dashboard.html": color = "limegreen"; break;
			case "/settings.html":  color = "royalblue"; break;
			default: return;
		}

		doc.documentElement.style.cssText = `
			contain: paint !important;
			overflow: hidden !important;
			transform: translateX(64px) !important;
			box-shadow: -4px 0 yellow, -64px 0 ${color} !important;
		`;
	}
});

Этот. Нет в нем никаких регистраций

Кстати, там по событию "load".
Вроде как на этот момент window.__SSi уже есть,
поэтому delayedStartupPromise для него можно не ждать.

Это содержимое WebExtensionsStylerChild.mjs?

Да, это содержимое WebExtensionsStylerChild.mjs

Как этот mjs запускать?

Запускать ничего не надо, он зарегистрирован
в первом (оконном) коде через ChromeUtils.registerWindowActor()
там где esModuleURI

Что в нем за export .. extends?

Модуль должен экспортировать класс, с именем, заявленным при регистрации,
то есть, имя actor'а плюс "Child" — для контентской стороны,
и плюс "Parent" — для родительского процесса (если указан).


А extends, ну он extend'ит существующий класс JSWindowActorChild
чтобы пользоваться его свойствами и методами, да и вообще, похоже,
что он просто обязан это делать, а то будет молча проигнорирован.

Не получилось ничего стилизовать таким способом

Ещё бы, в регистрации написано: remoteTypes: ["extension"]
а у тебя такого процесса нет.
Можно заменить на matches: ["moz-extension://*"]
тогда будет работать.


Так-то, о подобных вещах поперёк мейнстрима,
типа WE в родительском процессе, лучше говорить сразу,
чтобы не приходилось гадать.

Было бы круто, но раз не смог разобраться

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

Dumby пишет

Но тогда непонятно в чём затруднение

в том, но я не смог найти в e.target.... uMatrix@raymondhill.net


Так-то, о подобных вещах поперёк мейнстрима,
типа WE в родительском процессе, лучше говорить сразу,
чтобы не приходилось гадать.

Это со стороны выглядит, что я знал, а на самом деле узнал только после тестов перед написанием предыдущего сообщения, т.к. у меня в user.js extensions.webextensions.remote=false еще с FF91


Ещё бы, в регистрации написано: remoteTypes: ["extension"]
а у тебя такого процесса нет.
Можно заменить на matches: ["moz-extension://*"]
тогда будет работать.

Сократим mjs до

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

Выделить код

Код:

export class WebExtensionsStylerChild extends JSWindowActorChild {
	handleEvent(e) {
		var doc = e.target;
		console.log(doc);
	}
}

Если extensions.webextensions.remote=true, то
remoteTypes: ["extension"]       // не пашет
matches: ["moz-extension://*"]  // не пашет


или в консоли уже не увидеть?


Если extensions.webextensions.remote=false, то
remoteTypes: ["extension"]       // не пашет
matches: ["moz-extension://*"]  // пашет

dinn пишет

Если extensions.webextensions.remote=false, то
remoteTypes: ["extension"]       // не пашет

Да, я уже говорил, что не должен пахать.
remoteTypes — это массив префиксов типов процессов, где должно работать
(если отсутствует — тогда во всех DOM'ских процессах).
А раз процесса с remoteType "extension" нет, то ...

matches: ["moz-extension://*"]  // пашет

Это хорошо, значит хотя бы всё размещено и подключено верно.

Если extensions.webextensions.remote=true, то
remoteTypes: ["extension"]       // не пашет
matches: ["moz-extension://*"]  // не пашет


или в консоли уже не увидеть?

А вот это странно.
Если аддонский процесс есть (можно глянуть в about:processes),
то я даже не знаю что и предположить.


Ну, разве что, действительно,
в консоли браузера просто не включён многопроцессный режим
(devtools.browsertoolbox.scope = everything).

Dumby Большое спасибо за практикум.

в консоли браузера просто не включён многопроцессный режим

Да, так и есть. Ни разу в жизни не включал. Теперь вижу при
extensions.webextensions.remote=true
remoteTypes: ["extension"]


Для стилизации uMatrix у меня один css файл для попапа и настроек. Если получится, то сделаю чтение из css, а если нет, то придется вбивать в mjs 300 строк.
Нет, чушь несу. Зачем читать css, если все уже прочитано и закешировано при старте брауза. Вообщем, содержимое mjs

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

Выделить код

Код:

export class WebExtensionsStylerChild extends JSWindowActorChild {
	handleEvent(e) {
		var doc = e.target;
		if (doc.nodePrincipal.addonId == "uMatrix@raymondhill.net") {
			doc.documentElement.setAttribute('data-a-url', "uMatrix");
		}
	}
}

Пашет, расшира стилизовалась, но атрибут прописался еще в двух местах, где он быть не должен?
1) В каком-то внутреннем расширении брауза на странице _generated_background_page.html
2) в расширении uMatrix на странице background.html

Dumby отказался от стилизации в обертке data-a-url из-за невозможности стилизации ShadowRoot, хотя это легко решается добавлением стилей извне css.
Ваша версия с синхрон-косылем не осталась без внимания. В итоге перешел на нее. Порадовало то, что не требует обертки в css и нет зависимости от "toolkit.legacyUserProfileCustomizations.stylesheets"

Dumby возможно ли добавить кнопку на страницу about:config, чтобы при клике она выполняла код, который спокойно выполняется из консоли текущей страницы? Например такой:
var test = [...gExistingPrefs.values()].sort((a, b) => a.name > b.name);

Само собой, все отключено, чтобы сабж не вякал варнингом и allow pasting

Хотя сомневаюсь, что Content-Security-Policy такое позволит. Попробую поработать с html и js старого конфига, который через манифест стартует

dinn пишет

возможно ли добавить кнопку на страницу about:config, чтобы при клике она выполняла код, который спокойно выполняется из консоли текущей страницы?

Да, возможно.
Можно дебаггером, но у тебя его нет.


Можно подрядить разрезольвившийся precompiledScript,
с отдаваемого ChromeUtils.compileScript()
вызвав его метод executeInGlobal();


Или, например вот, тупо scriptloader'ом
ну, просто чтобы ты не сомневался, что возможно

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

Выделить код

Код:

location == "chrome://browser/content/browser.xhtml"
	&& (func => addEventListener("DOMContentLoaded", e =>
		e.target.documentURI == "about:config" && Services.scriptloader.loadSubScript(
			"data:," + encodeURIComponent(`(${func})();`), e.target.ownerGlobal
		)
))(() => {
	var btn = document.createElement("button");
	btn.append("Button");
	document.getElementById("toolbar").prepend(btn);

	btn.addEventListener("click", () => {
		var test = [...gExistingPrefs.values()].sort((a, b) => a.name > b.name);
		alert(test.map(obj => obj.name + " -> " + obj.value).join("\n"));
	});
});

Dumby пишет

Можно подрядить

Подрядил. Все работает как надо. Спасибо!

Dumby никак не могу заальтернативить код из вашего поста №160, чтобы слушало не PlacesToolbarItems, а contentAreaContextMenu - то, в котором присутствует групповуха context-navigation. Оно же в gBrowser?

dinn пишет

никак не могу заальтернативить код из вашего поста №160, чтобы слушало не PlacesToolbarItems, а contentAreaContextMenu

Странно, у меня работает простая замена одного на другое.


Заменил, рестарт, жму кнопку, чтобы очистить буфер,
открываю #contentAreaContextMenu, затем закрываю,
и в консоли — прописанный лог «popup closing».
При закрытии субменю, кстати, тоже.


А если скопировать в буфер текст, то,
при закрытии меню, кнопка вновь обретает visibility

то, в котором присутствует групповуха context-navigation

Да, там есть <menugroup> с таким id
Но это не характеризует его совершенно однозначно.


Например, если посмотреть по адресу
view-source:chrome://browser/content/webext-panels.xhtml
то там тоже есть #contentAreaContextMenu с #context-navigation
Надеюсь, имеется в виду то, которое в документе окна браузера.

Оно же в gBrowser?

Не смог понять вопрос.
Оно не «в», и не «вовне», это просто разные вещи.


gBrowser — это js-объект создаваемый скриптом
chrome://browser/content/tabbrowser/tabbrowser.js
А #contentAreaContextMenu — это XULPopupElement <menupopup>

Dumby пишет

Странно, у меня работает простая замена одного на другое

Да я лопухнулся, заменил еще событие на contextmenu. Исправил, теперь пашет



Давеча смотрел скрипт и не мог понять,  куда нужно зайти, чтобы прослушка сработала, т.е. gContextMenu.shouldDisplay = false
Переделал с render как смог

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

Выделить код

Код:

(async (id, url) => {
	if (location != url) return;
	var menuitem = document.createXULElement("menuitem");
	document.getElementById(id).append(menuitem);
	var hidden = () => {
		var hs = nsContextMenu.contentData;
		return hs.context.link || hs.context.onImage || hs.context.onTextInput || hs.selectionInfo.text;
	}		
	menuitem.hidden = true;
	menuitem.render = () => {
		if (hidden()) return;
		menuitem.hidden = false;
		menuitem.id = "context-closetab";
		menuitem.label = "Close Tab";
		menuitem.setAttribute('oncommand', 'BrowserCommands.closeTabOrWindow();');
		delete menuitem.render;
		menuitem.render();
		menuitem.render = () => menuitem.hidden = hidden();
	}
})("contentAreaContextMenu", "chrome://browser/content/browser.xhtml");

А потом решил сделать версию с запиханием в context-navigation, чтобы не мудрить с hidden. Короче не придумаешь и вроде все норм пашет или есть подводное течение?

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

Выделить код

Код:

(async (id, url) => {
	if (location != url) return;
	let menuitem = document.createXULElement('menuitem');
	menuitem.setAttribute('id', 'context-closetab');
	menuitem.className = 'menuitem-iconic';
	menuitem.setAttribute('image', 'chrome://global/skin/icons/close.svg');
	menuitem.setAttribute('tooltiptext', 'Close tab');
	menuitem.setAttribute('oncommand', 'BrowserCommands.closeTabOrWindow();');
	document.getElementById(id).prepend(menuitem);
})("context-navigation", "chrome://browser/content/browser.xhtml");

dinn пишет

куда нужно зайти, чтобы прослушка сработала, т.е. gContextMenu.shouldDisplay = false

Нее, загадку, что бы это могло значить, мне не разгадать.

Давеча смотрел скрипт
Переделал

Под label "Close Tab" чуть больше подходит gBrowser.removeCurrentTab()
ведь BrowserCommands.closeTabOrWindow() закрывает и выбранные вкладки, если есть.
Посмотри в консоли, набрав BrowserCommands.closeTabOrWindow + "";
там код несложный и хорошо прокомментирован.

решил сделать версию с запиханием в context-navigation, чтобы не мудрить с hidden

Этот menugroup скрывается чуть в больших случаях, чем заказано в коде «с render»,
например, ещё onCanvas, onVideo, onAudio. Если это подходит, то решение хорошее.

есть подводное течение?

Ну, разве что есть мета, где выбрасывают все «on…» атрибуты.
Среди причин: «adding a restrictive CSP to browser windows».
Если сделают — атрибут 'oncommand' работать, наверно, перестанет.

Dumby пишет

Посмотри в консоли, набрав BrowserCommands.closeTabOrWindow + "";

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


Если сделают — атрибут 'oncommand' работать, наверно, перестанет

Сделал без on, попутно еще к крестику добавил плюсик, но BrowserCommands.openTab() не впечатлило, а вот так вроде самое оно
func: () => gBrowser.addAdjacentNewTab(gBrowser.selectedTab) + gURLBar.focus(),

Есть Скрипт Firefox userChrome.js для добавления второй боковой панели с веб-панелями, как в Vivaldi/Floorp/Zen.
Сам скрипт заточен под загрузчик fx-autoconfig, но желающие могут протестировать и на других. Можно создать рядом с second_sidebar.uc.mjs файл с названием например second_sidebar.uc.js

second_sidebar.uc.js

Выделить код

Код:

// ==UserScript==
// @name            Firefox Second Sidebar
// @author          aminought
// @include         main
// @homepageURL     https://github.com/aminought/firefox-second-sidebar/tree/master
// @description     A Firefox userChrome.js script for adding a second sidebar with web panels like in Vivaldi/Floorp/Zen.
// ==/UserScript==

if (location.href.startsWith("chrome://browser/content/browser.x")) {
	(async url => {
		(await ChromeUtils.compileScript(`data:,"use strict";import("${url}").catch(console.error)`)).executeInGlobal(window);
	})(Services.io.newURI(Components.stack.filename).resolve("second_sidebar.uc.mjs"));
}

Dumby как мониторить вкладки на добавление (наличие) атрибута hidden = true? При этом выводить алерт или удалять атрибут. Прослушка TabHide не катит, т.к. редко кто скрывает вкладку через браузный hideTab()

dinn пишет

как мониторить вкладки на добавление (наличие) атрибута hidden = true? При этом выводить алерт или удалять атрибут. Прослушка TabHide не катит

Звучит как возможность попробовать MutationObserver.
Как-то так, наверно (но, поосторожнее там с alert'ом)

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

Выделить код

Код:

(url => {
	if (location != url) return;

	var labelify = record => record.target.label;
	var tabAndHidden = record => record.target.matches("tab.tabbrowser-tab[hidden=true]");

	(new MutationObserver(mutations => {

		var hiddenTabsRecords = mutations.filter(tabAndHidden);
		hiddenTabsRecords.length && alert(
			'"true" hidden attribute on tab(s) detected.\n\n'
			+ hiddenTabsRecords.map(labelify).join("\n")
		);
	}))
		.observe(
			gBrowser.selectedTab.parentNode,
			{subtree: true, attributes: true, attributeFilter: ["hidden"]}
		);

})("chrome://browser/content/browser.xhtml");

Dumby спасибо, то что нужно. Добавил вывод url вкладки и стало вообще зашибись

Dumby потестировал ваш код на паре случаев, когда сам брауз скрывает вкладки:
1 открытие исходного кода страницы в новом окне
2 кнопка Firefox View
Напрашивается не исключение для about:blank, а задержка с правильно подобранным значением для alert, чтобы видеть актуальное, а не переходное состояние label и url для hidden tabs.
Правильно ли я все сделал?

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

Выделить код

Код:

(url => {
	if (location != url) return;

	var adr = record => record.target.linkedBrowser.currentURI.spec;
	var labelify = record => record.target.label;
	var tabAndHidden = record => record.target.matches("tab.tabbrowser-tab[hidden=true]");

	(new MutationObserver(mutations => {

		var hiddenTabsRecords = mutations.filter(tabAndHidden);
		hiddenTabsRecords.length && setTimeout(() => {
			alert(
				'"true" hidden attribute on tab(s) detected.\n\n'
				+ hiddenTabsRecords.map(labelify).join("\n")
				+ '\n\n' + hiddenTabsRecords.map(adr)
			);
		}, 155);
	}))
		.observe(
			gBrowser.selectedTab.parentNode,
			{subtree: true, attributes: true, attributeFilter: ["hidden"]}
		);

})("chrome://browser/content/browser.xhtml");

dinn пишет

1 открытие исходного кода страницы в новом окне

А, тут надо настройку view_source.tab поперёк дефолта переключить.
Мог бы напомнить, а то пока дошло...

2 кнопка Firefox View

Да, это был целевой кейс при рассмотрении.

когда сам брауз скрывает вкладки

Ещё он должен так делать по заказу WebExtensions (типа такого).

Правильно ли я все сделал?

Как не знал, что есть «правильно», так и не буду знать никогда.
Однако, ни SyntaxError'а, ни какой-то вопиющей неправильности я не вижу.


Мне лишь только кажется, что функцию для setTimeout()
лучше вынести наружу, чтобы она не создавалась каждый раз новая.
Впрочем, это довольно субъективно.

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

Выделить код

Код:

(url => {
	if (location != url) return;

	var tabAndHidden = record => record.target.matches("tab.tabbrowser-tab[hidden=true]");
	var info = record => record.target.label + "\n" + record.target.linkedBrowser.currentURI.spec;
	var notify = records => alert('"true" hidden attribute on tab(s) detected.\n\n' + records.map(info).join("\n\n"));

	(new MutationObserver(mutations => {

		var hiddenTabsRecords = mutations.filter(tabAndHidden);
		hiddenTabsRecords.length && setTimeout(notify, 155, hiddenTabsRecords);
	}))
		.observe(
			gBrowser.selectedTab.parentNode,
			{subtree: true, attributes: true, attributeFilter: ["hidden"]}
		);

})("chrome://browser/content/browser.xhtml");

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

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

Выделить код

Код:

gBrowser.hideTab(FirefoxViewHandler.tab = gBrowser.addTrustedTab("about:blank"));

Dumby
В FF132 скрипт для запуска внешних программ мешает запоминать масштаб страниц. Выставляю масштаб на этом сайте 110%, а после перезагрузки опять 100%. Если его удалить, то всё ОК.
загрузчик метода Aris-t2.
В userChrome.js прописано так:

Выделить код

Код:

userChrome.import("scripts", "UChrm");

Вот сам скрипт:

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

var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
try {
    CustomizableUI.createWidget({
        id: "add-Notepad-app",
        label: "Notepad",
        tooltiptext: "Запуск редактора Notepad",
        onCreated: btn => btn.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAABuElEQVQ4T42T3UoCURSFe5BexHeI6KIewqAg7F+HySgqiiIjLwpSCBKUgkwEbYxJm4nM/JvoR0vJiMlEBC0kvFjNOUzZmA4tWMy52Ovbex/OdLUTwzAGYqvVOtB6/v6yLNutlreI6TJsRO1YEJaxdmHDvLAE9mwO8+dLGDoZBSPMYpgfg3F7EASmppqa4sxiqniDSzmFyEsUx88RHD1x8OR92H3cx3Z2D4spG5whJ7xeL9SYKqV7OC9AKt7pAsa4aRQKBXAcpwVYQqyYLeV0AStpO5ycE6VyqQWgdOdyPA2ni7cdAabAJNJSGu+fH1qAKTguktC3o3LyD2Bd2sLh1SHK5TKImgCl+37miAZ+O/xyoQFYTmeQTCbxWipqJxgMDIuksJ2Dz2EKsF874Iq4KICEmwClu+PGRYs62ft0jImgBZIk4T6b0QJ6VvtEMp6eN6934Jf8kGUZlUqF7k9EAeR1kdvVs1ndPZFIQH571U7Qe9DvNoZG0MkjJ5PwC34ajsfjP+EfQDv5fD4a+G0SzuYe/geIxWKo1WoaV6tVNBoNdXugXq+3+RdUeTyeAZ7n3XpWwm6GYQxfSZKfb4uO/HAAAAAASUVORK5CYII=",
        onCommand: function(event) {
            var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
            file.initWithPath("E:\\Software\\Просмотр\\Notepad++ 7.7 Final Portable\\notepad++.exe");
            if (file.exists()) file.launch();
        }
    });
} catch(e) {}

rubel пишет

Если его удалить, то всё ОК.

Достаточно удалить только то, что до строки try {
Там нечто, не только совершенно ненужное, но даже вредное.

Dumby
Прекрасно заработал ! Спасибо.

Dumby
А можно поправить скрипт для загрузчика метода Aris-t2. Закрытие вкладки = переход на предыдущую посещенную
Под вторым спойлером.

rubel
Ну, эпопея с выкидыванием «on…» атрибутов показывает,
что удаление листенеров (именно) по причине выгрузки окна
не рассматривается как необходимость.


А очистка set'а табов, даже не знаю, оставил, на всякий случай.

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

Выделить код

Код:

location == "chrome://browser/content/browser.xhtml" && (async () => {
	await delayedStartupPromise;
	var set = new Set([gBrowser.selectedTab]);
	var bt = gBrowser._blurTab;
	gBrowser._blurTab = tab => {
		if (!tab.selected) return;
		set.delete(tab);
		var res;
		for(var t of set) t.hidden || (res = t);
		res ? gBrowser.selectedTab = res : bt.call(gBrowser, tab);
	}
	for(var args of [
		["TabClose", e => set.delete(e.target)],
		["TabSelect", e => set.add(e.target, set.delete(e.target))]
	])
		gBrowser.tabContainer.addEventListener(...args);
	window.addEventListener("unload", () => set.clear(), {once: true});
})();

Dumby
Работает отлично ! Спасибо Вам, ГУРУ. :)

Dumby
А можно кнопку Перезагрузка браузера от UCF сделать для загрузчика метода Aris-t2.

rubel пишет

А можно кнопку Перезагрузка браузера от UCF сделать для загрузчика метода Aris-t2.

Она работает, как минимум в v128.4.0. Она в гамбургере, может, ты ее не нашел? И еще, кнопок перезапуска "на рынке" как грязи поздней осенью.

fuchsfan

Она в гамбургере, может, ты ее не нашел?

Именно так, в гамбургере нашёл. :) :beer:

fuchsfan пишет

Она работает, как минимум в v128.4.0

Поделитесь пожалуйста, а лодырь у вас какой?

Farby пишет

Поделитесь пожалуйста, а лодырь у вас какой?

Так это та, что по вашей ссылке https://forum.mozilla-russia.org/viewto … 44#p811444 Загрузчик от Aris-t2.