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

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

№30114-05-2022 13:52:59

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

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

Dumby пишет

Почему jsm?

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

Dumby пишет

pdf там через какой-то сторонний сайт, так что это без меня.

Сохраняет то не сайт, а скрипт или браузер, но если бы был браузер, то он сохранял бы стандартно.
   

Dumby пишет

А html — я не вижу такого.

Да, я забыл настроить папку на одном из профилей.
   

Dumby пишет

Да, у saveURL() аргументов, определённо, больше.

C этим все в порядке. Спасибо.
   

Dumby пишет

try {var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {file = Services.dirsvc.get("Desk", Ci.nsIFile);}

1. Так сохраняет в профиль. [firefox] 91, 100
2. И не сохраняет текст из textarea, можно это изменить?

Отредактировано _zt (14-05-2022 13:56:12)

Отсутствует

 

№30215-05-2022 17:21:44

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

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

Dumby
Просьба, сделать запускатор для кнопок ATB.


Win7

Отсутствует

 

№30316-05-2022 09:19:07

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

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

_zt пишет

Сохраняет то не сайт, а скрипт

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


Мне вот стало любопытно, а сохранялка в pdf,
которая у лисы на борту, сохраняет хуже чем веб-сервис?
Если нет, то можно было бы обсудить перевод на лисий код.
Сравнить можно сопоставлением с результатами сохранения,
например, этим WebExtensions (поднастроить его ещё).
Ну, если есть желание, разумеется.

1. Так сохраняет в профиль.

Путь к папке сохранения должен браться из настройки browser.download.dir
То есть в «папку загрузок назначенную в браузере» (если не получилось взять, тогда на рабочий стол).
Если там путь, ведущий в профиль, то всё правильно.

2. И не сохраняет текст из textarea

Даже и не предусмотрено. Как и работа в (i)frame'ах.

можно это изменить?

Вроде не должно быть сложностей, это же контекстное меню,
туда выделенный текст уже проброшен самим браузером.
Функция saveSelectionToFile() на замену

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

Выделить код

Код:

function saveSelectionToFile() {
	var line = ".".repeat(62) + "\n";
	var hint = "Кликни чтобы открыть файл";
	var prfx = "Сохранил выделенный текст в файл ";

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

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

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

kokoss пишет

Просьба, сделать запускатор для кнопок ATB.

В смысле чтобы кнопки в окне появлялись раньше?
Можно так попробовать (код для custom_script.js).

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

Выделить код

Код:

(async url => {
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var onReady = (e, addon) => {
		if (addon.id == "add_toolbar_buttons@vitaliy.ru")
			onAddon(addon), manager.off("ready", onReady);
	}
	var onAddon = async addon => {
		var mgr = addon.experimentAPIManager;
		var loaded = mgr.getModule("addToolbarButtons").asyncLoaded;
		loaded ? await loaded : mgr.getAPI("addToolbarButtons", addon);

		mgr.global.baseUri = (mgr.global.contExt = addon).baseURL;
		var atb = mgr.global.add_toolbar_buttons;
		style(atb, addon);
		atb.init();
	}
	var style = (atb, addon) => {
		var noop = () => {};
		var sss = atb.styleSS;
		var subst = "v-add-toolbar-buttons-style";
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var shutdown = (e, isAppShutdown) => {
			if (isAppShutdown) return;
			sss.unregisterSheet(...args),
			rph.setSubstitution(subst, null);
			manager.on("ready", onReady);
		}
		(style = async (atb, addon) => {
			atb.loadButtonStyle = atb.removeButtonStyle = noop;
			var css = `@-moz-document url(chrome://browser/content/browser.xhtml) {\n${
				await (await fetch(addon.baseURI.resolve("button.css"))).text()
			}\n}`;
			rph.setSubstitution(subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(css)));
			sss.loadAndRegisterSheet(...args);
			addon.once("shutdown", shutdown);
		})(atb, addon);
	}
	manager.on("ready", onReady);
})("resource://gre/modules/ExtensionParent.jsm");

Отсутствует

 

№30416-05-2022 12:53:48

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

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

Dumby пишет

Путь к папке сохранения должен браться из настройки browser.download.dir

Ага, только вы написали менять var file = Services.dirsvc.get("Desk", Ci.nsIFile);, ну я его и заменил, а это совсем не там, менять нужно было var file = Services.dirsvc.get('ProfD', Ci.nsIFile); :)
   

Dumby пишет

Функция saveSelectionToFile() на замену

В конце ";" не нужен?
   

Dumby пишет

Мне вот стало любопытно, а сохранялка в pdf,
которая у лисы на борту, сохраняет хуже чем веб-сервис?

Лучше, так как там хоть немного можно настроить вывод. У меня и без расширений пункт Печать позволяет сохранить PDF. Конечно надо заменить этот атавизм (сервис) на встроенную функцию. Вот только как, с выводом окна настроек или с чтением уже имеющихся настроек и моментальным сохранением? Я за второй вариант, если он возможен (настройки печати браузер сохраняет).

Отсутствует

 

№30516-05-2022 15:09:23

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

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

Dumby пишет

Можно так попробовать (код для custom_script.js).
скрытый текст

Благодарю, в актуальной версии UCF работает, а можно сделать что бы работало и в этой версии UCF ?

Отредактировано kokoss (17-05-2022 00:09:04)


Win7

Отсутствует

 

№30616-05-2022 16:00:22

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

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

_zt пишет

менять нужно было var file = Services.dirsvc.get('ProfD', Ci.nsIFile);

Скриншот с форума.

В конце ";" не нужен?

Вообще, после функций не принято, и, за редким исключением, не нужно.
Но если есть сомнение, то можно поставить, хуже не будет.

чтением уже имеющихся настроек и моментальным сохранением

Я так предполагал: ты настроишь тот WebExtensions так, как нужно,
затем выложишь настройки, а я попробую вписать их в код.

вариант публикации

Идём в отладочную консоль аддона, например, открываем вкладку с адресом
about:devtools-toolbox?id=%7B9ab38051-cd73-4e46-b7bd-dc147f6f6b29%7D&type=extension&tool=webconsole


Там запускаем код. Должна открыться вкладка с настройками.
Копируем настройки в пост под спойлер.

Выделить код

Код:

(async () => {
	var data = JSON.stringify(
		await browser.storage.local.get(), null, "\t"
	);
	var url = URL.createObjectURL(
		new Blob([data], {type: "text/plain;charset=utf-8"})
	);
	browser.tabs.create({url})
		.finally(() => URL.revokeObjectURL(url));
})();


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


kokoss пишет

а можно сделать что бы работало и в этой версии UCF ?

Даже не знаю, может так попробуй

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

Выделить код

Код:

…
	//manager.on("ready", onReady);
	var policy = Cu.getGlobalForObject(Cu)
		.WebExtensionPolicy.getByID("add_toolbar_buttons@vitaliy.ru");
	policy ? onAddon(policy.extension) : manager.on("ready", onReady);

Отсутствует

 

№30716-05-2022 17:04:26

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

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

Dumby пишет

может так попробуй
скрытый текст

К сожалению у меня не работает. А если для файла config.js ?

заработало

Выделить код

Код:

(async url => {
	var manager = ChromeUtils.import(url).ExtensionParent.apiManager;
	var onReady = (e, addon) => {
		if (addon.id == "add_toolbar_buttons@vitaliy.ru")
			onAddon(addon), manager.off("ready", onReady);
	}
	var onAddon = async addon => {
		var mgr = addon.experimentAPIManager;
		var loaded = mgr.getModule("addToolbarButtons").asyncLoaded;
		loaded ? await loaded : mgr.getAPI("addToolbarButtons", addon);

		mgr.global.baseUri = (mgr.global.contExt = addon).baseURL;
		var atb = mgr.global.add_toolbar_buttons;
		style(atb, addon);
		atb.init();
	}
	var style = (atb, addon) => {
		var noop = () => {};
		var sss = atb.styleSS;
		var subst = "v-add-toolbar-buttons-style";
		var args = [Services.io.newURI(`resource://${subst}/`), sss.USER_SHEET];
		var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
		var shutdown = (e, isAppShutdown) => {
			if (isAppShutdown) return;
			sss.unregisterSheet(...args),
			rph.setSubstitution(subst, null);
			manager.on("ready", onReady);
		}
		(style = async (atb, addon) => {
			atb.loadButtonStyle = atb.removeButtonStyle = noop;
			var css = `@-moz-document url(chrome://browser/content/browser.xhtml) {\n${
				await (await fetch(addon.baseURI.resolve("button.css"))).text()
			}\n}`;
			rph.setSubstitution(subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(css)));
			sss.loadAndRegisterSheet(...args);
			addon.once("shutdown", shutdown);
		})(atb, addon);
	}
    manager.on("ready", onReady);
	var policy = Cu.getGlobalForObject(Cu)
		.WebExtensionPolicy.getByID("add_toolbar_buttons@vitaliy.ru");
	policy ? onAddon(policy.extension) : manager.on("ready", onReady);
})("resource://gre/modules/ExtensionParent.jsm");


thank-you.gif

Отредактировано kokoss (16-05-2022 23:17:30)


Win7

Отсутствует

 

№30816-05-2022 18:26:01

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

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

Dumby пишет

Скриншот с форума.

Ну да, и оно сохраняло в профиль, пока не изменил var file = Services.dirsvc.get('ProfD', Ci.nsIFile);, да еще и папку левую там создавало, с названием как папка загрузок браузера.
Теперь все работает и ладно.
   
Ну пусть так будет, вывел все опции. Если оно в скрипте будет человекочитаемым то всегда изменить можно.

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

Выделить код

Код:

{
	"footerCenter": "",
	"footerLeft": "&PT",
	"footerRight": "&D",
	"headerCenter": "",
	"headerLeft": "&T",
	"headerRight": "&U",
	"marginBottom": 0.2,
	"marginLeft": 0.2,
	"marginRight": 0.2,
	"marginTop": 0.2,
	"orientation": 0,
	"paperHeight": 11,
	"paperSizeUnit": 1,
	"paperWidth": 8.5,
	"scaling": 1,
	"showBackgroundColors": true,
	"showBackgroundImages": false,
	"shrinkToFit": true,
}

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

Отсутствует

 

№30917-05-2022 09:48:44

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

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

_zt пишет

    "paperHeight": 11,
    "paperSizeUnit": 1,
    "paperWidth": 8.5,

Странно, paperSizeUnit единица.
1 — это nsIPrintSettings.kPaperSizeMillimeters (миллиметры).


Наверно имелся в виду ноль.
0 — это nsIPrintSettings.kPaperSizeInches (дюймы).
А то получается размер с ноготь мизинца.


Вобщем, пока такой набросок замены функции savePageToPDF()
И, на всякий случай, idl'ки: release и esr91,
вдруг захочется с оставшимися настройками повозиться.

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

Выделить код

Код:

function savePageToPDF() {
	var ps = Ci.nsIPrintSettings, cfg = {

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

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

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

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

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

		printBGColors: true,
		printBGImages: false,

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

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

как добавить в кнопку пункт "Печать" браузера (шоб имя свое задать можно было)

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

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

Выделить код

Код:

{ label: "Союзпечать", func: document.getElementById(document.getElementById("appMenu-viewCache").content.querySelector("[key=printKb]").getAttribute("command")).getAttribute("oncommand"), image: "chrome://global/skin/icons/print.svg"},

Отсутствует

 

№31017-05-2022 20:21:00

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

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

Dumby пишет

paperSizeUnit

Забыл вернуть, я же пытался все настройки вывести.
В общем, шикарно получилось. Спасибо. И главное без стороннего сервиса и с возможностью настройки.
   
А для открытия списков URL и сохранения списка адресов, выбранных или всех текущих вкладок, есть скрипт? Из буфера обмена / файла, в буфер обмена / файл / закладки.

Отсутствует

 

№31117-05-2022 20:38:05

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 100.0

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

_zt пишет

В общем, шикарно получилось.

И что получилось, можно итоговый вариант?

Отсутствует

 

№31218-05-2022 14:21:41

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

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

voqabuhe

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

Выделить код

Код:

self.label = "Save";
self._handleClick =()=> menuPopup.openPopup(this, "after_start");
self.image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABGklEQVQ4EaWSvQ4BQRSFd/3EUouGksIDiNBLvMAqtVoaiShUHkItIRGd1iN4A0ql2l8wvruxw67FJuR87p05d85OFsP482PKeaVUjWpDDKpwgyAd2GyZprmiPkXAWFbUIvSld2EdgQ5UYApzKLl+xG2+VAuvDjZcoAldQspUI0yAzMXlS+D6e6qEtKlvAVc2o/CqI4sJTMF5Z4Qc6OU2hrw0eq01XZLrjah+ldhYgEeeAJIVbg9CyxMgp3j6kFqAIK15SP/V8ARwOI95ZqhBfRP+QCmVx9+4pv9XiGNc4JPEkxnt+wO0EbbRAVwtwyELfsl6zDpzOoBVFlLwSzIjs86c88cgscbKhgSkYQtByrG5gxPMeJlL6n+6A9I+WoE5cj6LAAAAAElFTkSuQmCC";


var folderpath="E:\Download";         // папка для сохранения иконок для ярлыков и ярлыков сайтов

// Создать меню для кнопки .............
var array = [
   { label: "Сохранить favicon сайта", func: "saveFavicon()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAACPVBMVEX09ff////C3L+Uq8+Vq8+Uqs+Zr9CZrtCZr9Gfu+ear8+mt9JRf8ORxl3t8vfF06+Twojs8/d9otl8o9s+aquZrs/X9KLp8Pft8fZhisf//+DBzN2hveihv+pii8hti9pgicl6oNlojs1zncNsi836/P2duebx8/eYyWqBp+Gn0IKBvlKHsm9qmaVuk8zt7/FEbauEv1Tp7/JdhL9oi9Pl8e2LwlmdsdD7/P76+/3H7ofo8+peh8eHwFaSteZ0pkp2gl7q8/Ohy5OApt2by2eZuOqbuOWaezWuvtd7nN2HvWxul9Ty9feQxV5ljcqBp+JEcLCVtOOo0nR7odx5n9suX6Z1mtBzmtSXyGPv9PewzfOzx+O6zu/s8fd9o95Xfrthi8lYhMN5oNnw9ffw9Pjw9Pf8/f6ewO/m8O9zmdE6aapsjdyUwouPxWPDzd6XteOSs9B5nNVpnpqHt7h/s6F6n9d7ntSTttGHwVh4qp+Ev1HH7ox6qk5wj+Hm8e3t9fOm0IKAtqOBpNrx+P9ljcyhs9FpkM2hv+/u8/fF0eOLu4N+vFKgzX3p9OSFqN13qExekIl4n9j7/P3x9PhxmNDm8e9Vg8Zfkozr8veq0YTX9qL//92AtamOwnHFz96Fot1diMh+pd13ntmatu+YyW/3+/+Tqs5UgcShzJNbhsdTf8GHs7bo8PaXtuqMr+Ty8/SZt+SUqs7r7Ox3ndb9/f7t8feZyXGYyWWCpNbz9PRuiteNtNDn7/V4ntjx8fGo3JqNAAAAv3RSTlP/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AEVuhDkAAAD+SURBVBhXY5jHzcUMAqxAICq9bx8D96adDFAgaGQOFOBaH7h7zoqZDTlFyptncAAFWBjyi52CXCI0unRLxcECPhsatbbzmlXMnS60hg0kkOxW0uNrq93tNaFpD1ggUm21QK532ZQdSm1hmXKdDCwdnOWVOi1RjNGMQCCrwMDMJ8NZ4LAynVGPkXFp8zpJBubYmn579wXtqhZb0iwn9a1iWLaViYmJ3891obOwYtLEvcYMGyWAAkwJdv6accEhi8LjGVr11SenpC5f61g3NcO0vjCAIc+DjZ2dnWexddWSbYa9nlkM+8BgWsxsK7FZ1VLzRaACNokmtdnyu1QMQgF7Rlh4zWWTAwAAAABJRU5ErkJggg=="},
   { label: "Копировать favicon в base64", func: "copyFaviconData()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAAAAAAAAAAAAAI2bv/9RVpf/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAACIkvD/Jia6/ywpq/8AAAAAAAAAAAAAAAAAAAD/AAAA/wbwAf90qpv/Ymic/1RWqP9OUKr/W2Ch/2dumf9YYKT/Ly/B/xQP3/8MB9P/JCGb/wAAAAAAAAAAAAAAAAAAAP8G8AH/U5ea/ycr8f8VIP3/HiP4/ywo8v8sIvb/LCL2/ywi9v8KBOj/BQDe/wQAtv8tK4P/AAAAAAAAAAAAAAD/BvAB/3Sqm/9iaJz/Tim3/0UuuP9GPrT/R0ex/zk8uf8gIMz/FRDe/xEMzv8jIJz/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8AAAD/SqOR/yImvP8sLKj/AAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/BvAB/3Sqm/9KW5r/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAABvAB/wbwAf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/BvAB/wbwAf8G8AH/AAAAAAAAAAAAAAAAAAAAAAAAAAAG8AH/AAAAAAAAAP8G8AH/AAAAAAAAAAAAAAAAAAAA/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAP8G8AH/BvAB/wbwAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/BvAB/wbwAf8AAAAAAAAA/wAAAP8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/AAAAAAAAAAAG8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wbwAf8G8AH/BvAB/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOesQQBjrEGAAaxBwACsQcABrEHDg6xBwAesQcAPrEHAD6xBw8+sQcPprEHD8axBwAGsQQABrEGAAaxB//+sQQ=="},  
   { separator: ''},
   { label: "Сохранить ярлык страницы как…", func: "saveShortcuts()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/BP8k/wT/JP8E/yT/BP8k/5XLDv/zqgD/86oA//I1///yNf//86oA//OqAP/zqgD/86oA//OqAP+Vyw7/lcsO/wT/JP8E/yT/BP8k/5XLDv+Vyw7/86oA//OqAP/yNf//8jX///OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA//02AP/9NgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP/9NgD//TYA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/wT/JP+Vyw7/86oA//OqAP/zqgD/86oA/wA31v8AN9b/86oA//9If///SH//86oA//OqAP/zqgD/86oA/5XLDv8E/yT/lcsO//OqAP/zqgD/86oA//OqAP8AN9b/ADfW//OqAP//SH///0h///OqAP/zqgD/86oA//OqAP+Vyw7/BP8k/5XLDv/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/lcsO/5XLDv+Vyw7/86oA//OqAP/zqgD/86oA/0CA//9AgP//86oA/07+9f9O/vX/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP9AgP//QID///OqAP9O/vX/Tv71//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/86oA//OqAP/zqgD/AACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQAArEEAAKxBAACsQQ=="},
   { separator: ''},  
   { label: "Копировать изображение / текст в base64", func: "copyFaviconbase()", image: "data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AIAQ/wCAEf8AgA//AIAR/wCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AgBX/AIAVAAAAAAAAAAD/AIAo/wCA//8AgP//AID//wCA//8AgP//AIAoAAAAAAAAAAAAAAAAAAAAAP8AgBL/AID//wCA//8AgA3/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgBAAAAAAAAAAAAAAAAD/AIAR/wCA//8AgP//AIAK/wCACv8AgP//AID//wCAIf8AgAX/AIAh/wCA//8AgP//AIAQAAAAAAAAAAAAAAAA/wCACv8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAUAAAAA/wCABf8AgP//AID//wCACgAAAAD/AIAQ/wCADP8AgCH/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCABf8AgCH/AID//wCA//8AgAv/AIAh/wCA//8AgP//AID//wCA//8AgP//AIAH/wCAB/8AgP//AID//wCA//8AgP//AID//wCA//8AgP//AIAg/wCA//8AgP//AID//wCA//8AgP//AID//wCAB/8AgAf/AID//wCA//8AgP//AID//wCA//8AgP//AIAh/wCAC/8AgP//AID//wCAHP8AgBz/AID//wCA//8AgAf/AIAH/wCA//8AgP//AIAh/wCACf8AgA7/AIAMAAAAAP8AgAj/AID//wCA//8AgAP/AIAD/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCABQAAAAAAAAAA/wCADf8AgAr/AIAL/wCA//8AgP//AIAH/wCAB/8AgP//AID//wCAB/8AgAr/AID//wCA//8AgCH/AIAH/wCAJf8AgP//AID//wCAI/8AgP//AID//wCAB/8AgAf/AID//wCA//8AgAf/AIAL/wCA//8AgP//AID//wCA//8AgP//AID//wCA//8AgCT/AID//wCA//8AgAr/AIAK/wCA//8AgP//AIAKAAAAAP8AgCj/AID//wCA//8AgP//AID//wCA//8AgCP/AIAM/wCA//8AgP//AIAN/wCADf8AgP//AID//wCADQAAAAAAAAAA/wCAEP8AgBH/AIAP/wCAEf8AgBAAAAAAAAAAAP8AgBT/AIAVAAAAAAAAAAD/AIAV/wCAFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//+sQcH5rEGA8KxBAHCsQQBwrEEIQKxBAACsQQAArEEAAKxBAQCsQQwArEEAAKxBAACsQYAArEHBmaxB//+sQQ=="},
   { separator: ''},
   { label: "Сохранить страницу как PDF", func: "savePageToPDF()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUw4pJt3V/+Rb1D8lnFP/55zTf+VcVH/lXJS/5VyUv+VclL/lXJS/5VyUv+VclL/k3BR/J56Wv9cRzSkAAAAAJNvUKTto2L/4ppe/uehZf/Pmmr/noZv/9Scav/Wl17/1plh/9eZYf/XmWH/15lh/9eZYf/WmGD/2Jlg/suRXP9mRiuk8rWA/397dP1akKn/rqqi/3LF3f8Mntj/4dLJ///+9f/48u3/9e7n//bt5v/47+f/+O/n//jv5//rvZP/1ZJX/a9/VP9+lrD8AIvz/xOt+f8Douv/ALb6/wC28/9tmar/z8jJ//Lq5/////7//v////n9///6/P3//////+Ta0P/PjVP/pnpT/IWds/+aiXj/5efl/8Px//951/3/LMz//wCx8/8GltL/NIu3/4ycqf/l29L////+//n6+//8/v//3tXM/8+OVP+oe1P/6K57/86QWP/r6Ob////////++v/r9/z/oOb9/zDL//8Arf//AI/r/ydysv+hpqr//PTr///////d1Mz/0I5U/6h7U//osH//wo5g/+fm5P/7/f//9vf5//z7+////vv/9/r8/5Dc/P8Oqv7/AJf//wF02v9ffZ7/8Ojh/+Ha1P/NjFL/qHtT/+ewf//Ej1//7O3r///////+/v7//v7+//f5+v/6+vv////8/8Tq/f8hp/7/AJH//wB18/8/bqj/1MGu/9mXXv+leVH/569+/8iSYv+/tKn/wLew/8O5sf/P0M////////7+/v/3+fv////7/9Hv/v8hoP3/AIn//wB4/v84ZqL/w4NH/619VP/nsYD/x45c/9W5of/bv6j/0Jxt/6J/YP+spqD/2N3i//7////6+/3///76/8vr/v8Slv7/AIb+/wBz//83VH7/nW1B/+ewfv/Fjl7/7Ozr///////9+/r/9d7K/9Ghdv+jd1D/pJ6Y/+jt8f/9///////7/6fb/v8Ahv7/AYD//wRp6f95YlT/57B//8SOXv/n5uT//v7///r7/P/8/v////////XRsv/DhEv/loBu/9DX3P/9/v/////7/2O6/f8Afv//FnTU/5JtTf/nsH//xY9e/+jn5f/+/f//+fj5//n4+P/5+Pn/+/////Xbxf/Wj1D/nX1h/+Ll6P////7/3+/3/w+V//8tcbP/qHJB/+WuffzCjV7/5efn///////+/v7//////////////v7///////XRsP/TnW3/8fT3//////////z/ddD//0Nxl/+zdkH88LmH/9CRWP2+qJT/0M7N/8/Jxv/Pysf/z8rH/8/Kxv/Py8n/zsO6/7+pmP/PzMv/zsnG/9bNyP+nsK7/h4R3/b2BT/+Sb1Ck7qxw/9GSW/69hlb/wYhX/8CIV//AiFf/wIhX/8CIVv/BiVj/xI1d/8CIVv/BiFf/vYZW/9STW/7ppmv/bE80pAAAAACUd16k+sui/+7AmPzwwpr/8MKa//DCmv/wwpr/8MKa//DCmf/vwZj/8MKa//DCmv/uwJj8+cui/5N3XaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { label: "Печать / печать в PDF", func: document.getElementById(document.getElementById("appMenu-viewCache").content.querySelector("[key=printKb]").getAttribute("command")).getAttribute("oncommand"), image: "chrome://global/skin/icons/print.svg"},
   { label: "Сохранить страницу / выбор как HTML", func: "savePageToHTML()", image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACzElEQVQ4jV2STW8bZRDHf/Psrr11nZhgVKcKaSmUiArRKBRVIMgH4BKJ7xAp5MSFiA+Rc28ckBAoipC4koJQLyAaVFChIlQllDRWmjRev8Qvu14/+wwHx6ZlpNFcZn7zn79GVJW1tbXc6urqijHGqKoAPF3TgcVaq8owXJbphx99fOOnWzcHoqqsr68/t7y83BARVBXnHHraXo3a3HoUU8gFAHRTS2Bg/7C2Zx7vXPIBwjA0p3BU9Zns9i3vvVrhr6MWzlpee+EsRqAbHc3+/Pee7wPkcrkx4P+g0IMvb/5Iv5/iXEaWZQS+By578MaVy9YH8H3fE5FnANZajqMmgcAnH7yN73mICMYMd21sbLy7srKiPkA+n3/qBLhzv8p+EuOfTUjShPbODvOVWa5fnRsvKBaLAcAIMFbw+bc/EBVn8At7SPcQ60LSiVm++P0fGu0e7y++yalvHoABCILAiAj7hzU+vfcVT6zPVGmR3x4/xJoLSH6BVu4cN777nnQwQETI5XL/AYwxRkS4ff8BEk6Q2pBfq3dxUuHMmWt0kgFGStTDY/7YrTLybQwYPU2cpgzsCd1OgmSzWHeOR7VjWicxaT9lxn+HfjoYmWnGHgAiIsydf5HKnUXiThehQDG4Tr3e5KR7hN8XilnGKxfOoyrDgZECwIgIb125xIxxTGVdgjRG4pR2+zbG/sIkD7l2cYJyaYLMOZxzjBV4nueLCI1GnaWFab65t4OXt4gUuFqYJ8gGuPYBCxdLHNciys9PkaapjAHGGM8YQ7lcZv71OaYmC/xZPaJHTN4cUgpDXrr8MtOVaUqTRXzfY3t7u7O0tDR82a2trfkkSTSOY+31etrpdLTZbGoURRpFkdbrTY3qTW00mhrHse7u7t4tl8uhqg4VxHHcPDg4+ExVHeBU1YmIU1WnisucZtamLszn01ar9WRzc/PrWq2WAPwLJ7l2ULfXOAMAAAAASUVORK5CYII="},
   { label: "Сохранить URL вкладки / выбор как TXT", func: "saveSelectionToTxt()", image: "data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAQE6AAAAZAAAAGgAAAJmAAACZgAAAGYAAABmAAAAZgAAAGYAAABmAAAAZgAAAGYAAABlAAAAaQABAmYAAQEjAAAAADlVkOdVcKHxVXKi8kdklPJLZpfyU3Cg8lJvn/JRbZ7yUW+g8lFun/JRbp/yUG6e8lVyofFVdKjzLkV45wAAAFABAQEAaIzF/3y34v9wsuL/cJe0/0lpgv9bjLP/dLLh/2+v3v9sq9v/cK3d/3Gv3v9sqtv/b7Df/4G77P9VcqT2AAAAVAMDAQBnir/+ZqfU/pDB4/7a3uD+j46N/kxYXv6To6/+1er4/tXp+P7J3/D+1ej4/svg8v6Gut/9aqzd/lhxnvAAAABSAwIBAGaIvv9pptX/ocfj//f4/P/P0tX/g4CA/1xZWf+Woqr/2uz8/9Hl+f/W5fT/2Of3/5fC4v5tq93/Vm+e8QAAAFIDAgEAaYm+/3mw2/+iyeX/9Pn8/+z0+//IzNL/d3h5/0tMTv+Mkpn/xdvs/9Hp/f/O4PL/msXk/nmz4/9XcJ/xAAAAUgMDAQBti7//k7/h/6fL5v/v9fn/4u73/9Dk9P+8wMT/YGpx/zJJWv94iJf/ztzo/9Pp/P+ZwuD+gLfk/1dwnvEAAABSAwMBAHOPwf+myub/sNHp//j7/P/6/P3/8fr///r39P+sxdP/IXWq/xJJcv+NjZD/0+Lu/6TN7f6Ft+L/WXGf8QAAAFIDAwEAd5LD/7PR6/+ZxOP/0ePx/97r9f/Y5/L/3e32/8Tc7f9gseT/CHK3/zBYdv+HiY7/lL/f/pLE7/9bcJ3xAAAAUgMDAQB4k8P/xNvx/5/F5f+kyOX/qMrn/6TH5f+kyOX/sM/q/5e51P9Mm83/GHm3/xxIav9fdYf+pMvs/1x1pfIAAABTAwMBAHmSxf/O4/T/y9/y/8Hb9P/C3PX/wNv0/7vW7/+93ff/utDm/42fsf9Gjbn/EXS2/ytSbv6Fj5r/WnGc8gAAAFICAwEBepPE/tHk9f/S5PX/vMjV/7fCzP+3w8//uMPP/7XBzP+6ytf/qa+4/3h/hv9Ghaz/JoO+/jNXdP82PVnyAAAAVgACAgCBmcb/2+r3/dHh8fyPkpX/kI6N/5yam/+dnJ3/paWk/6enpf+sr6//mpSR/3Bubf9SjbD8H4C+/QsrSvcCAAB/AAAAA3yVx//j8///3u/7/52gpf6pqKf+uLm5/rq7u/7Ly8v+ycnI/paWlf6LjY/+np2f/Xh+g/5gnL7/MX+y/xcgJ80BAgNNN1OUs6W84fDA1O73mJyj/ainpv+2trb/t7e4/8fHyP/Fxsb/kZGQ/4eGhP+vucT/j6G+/W53k/NlkbnwNoOv/AgiNb8CCBsQDRo/YwsaO3B0d33arKyp9q2trfWvr6/2u7u79ru7vPawr7H1sbCt9nJ2gOQIGD6bFCFEYB0qPE1Bf6SpEz9cggAAAAMCAQEBAQAAADIyMmlDQ0ONQUFBhUFBQYVCQkGFQkJBhUhISIRNTU2OKysrZAEAAAUBAQAAAgEAAAkEAAEDAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},
   { separator: ''},
   { label: "(Меню ПКМ) Сохр./добавить текст в файл", value: "Save.SelectionToFile" },
   { label: "(Меню ПКМ) Открыть текст в редакторе", value: "Save.TextToEditor"},
];

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


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

function WebScreenShotonImage(image) {
      var canvas = document.createElementNS(xhtmlns, 'canvas');
      canvas.width = image.naturalWidth;
      canvas.height = image.naturalHeight;
      var ctx = canvas.getContext('2d');
      ctx.drawImage(image, 0, 0);
      var base64 = canvas.toDataURL();
      gClipboard.write(base64);
   
      // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Запомнил изображение как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(base64, self.label, "Изображение копировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
};


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

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


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


function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};

// Кодировать изображение или текстовой файл в base64 .............
function copyFaviconbase(){
var fp = window.makeFilePicker();
fp.init(window, "Открыть файл", fp.modeOpen);
fp.appendFilter("Text and images", "*.txt; *.text; *.css; *.js; *.ini; *.rdf; *.xml; *.html; *.htm; *.shtml; *.xhtml; *.jpe; *.jpg; *.jpeg;\
                                    *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw");
  fp.open(re=> { 
  if ( re != fp.returnOK ) return;
   var file = fp.file;
   var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
   inputStream.init(file, 0x01, 0600, 0);
   var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
   stream.setInputStream(inputStream);
   var encoded = btoa(stream.readBytes(stream.available()));
   var contentType = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService).getTypeFromFile(file);
   var dataURI = "data:" + contentType + ";charset=utf-8;base64," + encoded;
   gClipboard.write(dataURI);
   //Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Текст скопирован как  base64");
    // стиль для изображение в сплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
      
     // alertsService.showAlertNotification(base64, self.label, "Изображение скопировано как base64", false, "", (s, t)=> { 
     Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(dataURI, self.label, "Изображение скопировано как base64", false, "", (s, t)=> {
         if (t == 'alertfinished')
             sss.unregisterSheet(uri, 0); // удалить стиль когда подсказка закрывается
      }, "");
});
};

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

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

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

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

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

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

        printBGColors: true,
        printBGImages: false,

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

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

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
    ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
    : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

// Сохранить иконку текущего сайта с диалогом сохранения .............
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};


// Копировать иконку текущего сайта в base64 .............
function copyFaviconData() {
   var img = new Image();
   img.src = gBrowser.selectedTab.image;
   WebScreenShotonImage(img);
};


// Сохранить выделенный текст или весь текст на странице как txt файл .............
function saveSelectionToTxt() {

let browserMM = gBrowser.selectedBrowser.messageManager;
        browserMM.addMessageListener('getSelection', function listener(message) {
        var sel = message.data;
       !sel && document.getElementById("cmd_selectAll").doCommand(); 
     
   // создать название файла из заголовка страницы и текущего времени и сохранить текст ....
   var fileTitle = getTabLabel() + '  ' + aDate().replace(/:/g, ".");
    saveURL(
        "data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\r\n\r\n" + sel),
        fileTitle + ".txt",
        null, false, false, null, null, null,
        gBrowser.selectedBrowser.browsingContext.originAttributes.privateBrowsingId > 0,
        document.nodePrincipal
    );
   !sel && goDoCommand("cmd_selectNone"); 
 browserMM.removeMessageListener('getSelection', listener, true);
});
        browserMM.loadFrameScript('data:,sendAsyncMessage("getSelection", content.document.getSelection().toString())', false);
};


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

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

   var editorItem = contextMenu.insertBefore(document.createXULElement("menuitem"), el);
   editorItem.id = "content-editorItem";
   editorItem.setAttribute("label", "Открыть выбранный текст в редакторе");
   editorItem.setAttribute("class", "menuitem-iconic");
   editorItem.setAttribute("image", "data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAEAAAAAAAAAAAAAAAEAAAAAAAAAAAAA////AIiafwC85K4AS1ZGANLsyABKU0UAy+m+AL/lsQC14aQAn9WMAJ/QjQCsrKwAqNyXAOPz3ADe8dcA2e/QANPtyQDM68EAxei4AL7lrwC14aUArt6dAJfOhACdz4sAgICAALThpQDg8doA3vHVANjuzgDR7MYAyum+AMLmtQC7460AsuCjAK3dmgCUy4AArdedANDsxQDL6sAAz+PIAMviwwDH4L4Awd62ALvbrwC02KcArdafAKbTlgCEu3EAtNWoANPqywDa8NIA1u7NANHtxwDK6cAAw+e3ALzkrwC14aYAr9+dAKXbkQCJwnMA1+3QAKncmADd8dQAyuLBAMbgvAC6264AttmoAK/XoACn1JYAotKPAJbNgwB9tWgA4+zfANju0ADT7coAz+vFAMPntgC95K8Art6eAKndlQCa1IYAi8R3AP3+/QDD37kAvt2yALjarACz2KYArdaeAKHRjwCXzoUAj8V6AI6/ewDu8e4Awea2AMrqwQDG6LoAweazALnjqwCz4KQAr9+cAKbbkwCd2IkAlc1/AKbTlQC73K8At9qqALDXowCp1ZsApdOVAKDRjQCYzoUAksx7AIi+cwCu0aEAp9yUAMXougDC5rYAveSuALfipwCw4J8AqdyXAKLajwCd2IcAlNR9AI3FdwDU6ssAqdWZAKPSkgCe0YsAmc6GAJDLfQB/tmoA3enZAJfWhACc2IkApNuRAKTbkACj2pAAodqOAKHZjgCl25IAotmOAIzHdgAPAAAA2JIKAAAA9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcrkUA9CUMAAAAAAAAAAAAAAAAAAAAAAAAAA8AAQAAAAEAAAAAAAAAVCIMAAAAAAABAAAAuwP/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAACZDSMAAQAAAAAAAAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAABAEAABHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGRkZGRkZGRkZAAAAAAAZAQEBAQEBAQEBARkAAIeHiImKi4yNjY5+AQEZAD4mJlZXRH+AgYKDhIVdGQBzJnR1dnd4eXp7fH1+ARkAGiYnaWprbG1ub3Bxcl0ZACdeJl9gYWJjZGVmZ2gBGQAAAyZUVVZXWEVZWltcXRkAAA1KS0wfTU45T1BRUlMZAAA+P0BBK0JDREVGR0hJGQAAAzIzNDU2Nzg5Ojs8PRkAACYnKCkqKywtLi8YMDEZAAAAGhscHR4fICEiIyQlGQAAAA0ODxAREhMUFRYXGBkAAAADBAUGBwYIBgkGChkAAAAAAAACAAIAAgACAAIAAPgBAADwAAAAwAAAAIAAAACAAAAAgAAAAIAAAADAAAAAwAAAAMAAAADAAAAAwAAAAOAAAADgAAAA4AEAAPqrAAA="); 
   editorItem.onclick =()=> textToEditor();


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

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


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

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

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

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

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

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


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

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

    var df = MozXULElement.parseXULToFragment(`
        <menugroup orient="vertical">
            <menuseparator/>
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить всю страницу как PNG"
                value="all"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAzAAAAiAcFBa4KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwoJCa8KCQmvCgkJrwsJCaECAQE/BQMDAAAAAJUgICD4V1ZW/2FhYf5hYWH/YmFh/2BgYP9fX1//X19f/19fX/9gYGD/YmFh/2FhYf9gYGD+ZmVl/1RSUuIVFBQtCgkJy1paWv+Li4v9h4eH/oiIiP6FhYX+i4uL/pKSkv6Sk5P+kpKS/ouLi/6FhYX+iIiI/oiIiP6Hh4f7lZaW/25tbYQNDQ3OcHBw/5KSkv6Li4v/i4uL/5mZmf+EhIT/ZGRk/1tbWv9kZGT/hISE/5mZmf+Li4v/jY2N/4yMjPyWl5f/iomJjQ4NDc13d3f/m5ub/pWVlf+goKD/XFxc/ygoKP8fHyD/GBsb/yAhIv8pKSn/W1tb/6CgoP+Wlpb/lpaW/J6env+Jh4eMDg4OzX1+fv+ioqL+qqqq/1hYWP8ZGRn/Ghwb/x4dHP8mIh//FhQR/xUWF/8aGhr/WFhY/6urq/+cnJz8pKSk/4qJiYwPDg7Ng4OD/7W1tf6MjIz/Ghoa/xYYGP8uKCb/ZEAo/5xyOP++saL/RD45/xISE/8bGxv/jY2N/6+vr/ypqan/ioiIjA8PD82IiIj/xMTE/l1cXP8LDAz/JiId/1o3LP9ADgD/mGog//Dt6P/VysX/Ih4Z/wsMDf9eXl7/v7+//K6urv+KiYmMEA8PzY+Pj//Kysr+SEhH/wEDBv9MPi7/hlES/3dCAP+VZAn/tJVO/7eVXf9OQTL/AAIE/0pJSf/FxcX8tLS0/4qJiYwQEBDNm5ub/9/e3/5SUlL/AAAA/0M7Mf/aya7/ybiO/5RmEf9aIAD/cjkX/z80KP8AAAD/U1JS/9nZ2fzAwMD/i4qKjBEREc2oqKn/8O/w/oeGhv8AAAD/DAsK/6qkof/17uj/nW8l/14eCf9hPTr/ExUU/wAAAP+Hh4f/6urq/MzMzf+Mi4uMERERzbCwsv/r6uz+3Nzd/yoqKv8AAAD/ExEP/2heU/9yWjv/UD0u/xcXFv8AAAD/Kioq/93d3v/l5eb81NTV/4yLi4wSERHNuLm5/+/v8P7z8/P/xsbG/yAgIP8AAAD/AQEB/wAAAP8BAQH/AAAA/yAgIP/Gxsb/9PT0/+np6vzb29v/jIuLjBIREc2+vb7/+Pj5/uvr7P/7+/v/4ODg/3Nyc/8uLi7/Hh4d/y0sLP9zc3P/4ODg//v7+//s7O3/8/P0/OHg4f+Mi4uLFBMTyMPDw//////7+Pj4/ff29v38/Pz9/////fr6+v3u7u79+vr6/f////38/Pz99/b2/fn5+f37+/v53t7e/5KSko4GBgZ7m5yc//j4+P/w8fH/8fLy//Dw8P/u7u7/8vLy//X19f/y8vL/7u7u//Dw8P/x8vL/8vLy/uXl5f/BwcH+k5GRUAAAAAQeHR1yb25uxn59fcZ9fHzGfXx8xn18fMZ9e3vGfHt7xn17e8Z9fHzGfXx8xn18fMZ9fHzGgH9/xYmIiGVaV1cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить видимую часть как PNG"
                value="page"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAIBkAAAEAIAAMDQAAFgAAACgAAAAgAAAAMgAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD29fT/2tra/8jIyP/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8XFxf/FxcX/xcXF/8jIyP/a2tr/9vX0/+zs7P/ak0b/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/9qTRv/s7Oz/7Ozs/+J9Dv/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+//7+/v/+/v7//v7+/6SdmP/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/+vn4//z7+v/6+fj/4n0O/+zs7P/s7Oz/4n0O//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r//Pv6//z7+v/8+/r/aFtT//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//j39v/ifQ7/7Ozs/+zs7P/ifQ7/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P9oW1P/7+zq/+/s6v/v7Or/8O3r//Dt6//w7ev/8O3r//Dt6//w7ev/8O3r/+/s6v/w7ev/9fTy/+J9Dv/s7Oz/7Ozs/+J9Dv/49/b/+Pf2//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4/2hbU//q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/y8O//4n0O/+zs7P/s7Oz/4n0O//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/+Pf2//j39v/49/b/aFtT/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe//Dt6//ifQ7/7Ozs/+zs7P/ifQ7/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P/29fT/9vX0//b19P9oW1P/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/7uro/+J9Dv/s7Oz/7Ozs/+J9Dv/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx//Ty8f/08vH/9PLx/2hbU//x7+3/8vDv//Hv7f/x7+3/8e/t//Lw7//x7+3/8e/t//Lw7//x7+3/8vDv//Hv7f/29fT/4n0O/+zs7P/s7Oz/4n0O//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//8vDv//Lw7//y8O//aFtT/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/6igmP+ooJj/qKCY/8vGwf/ifQ7/7Ozs/+zs7P/ifQ7/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f/x7+3/8e/t//Hv7f9nWlL/aFtT/2hbU/9nWlL/Z1pS/2hbU/9oW1P/Z1pS/2daUv9oW1P/aFtT/2hbU/9nWlL/pJyX/+J9Dv/s7Oz/7Ozs/+J9Dv/w7ev/8O3r//Dt6//w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Hv7f/w7ev/8O3r//Dt6//x7+3/8O3r//Dt6//w7ev/8e/t//Dt6//w7ev/8O3r//Dt6//w7ev/4n0O/+zs7P/s7Oz/4n0O/+/s6v/v7Or/7uro/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/v7Or/7+zq/+/s6v/u6uj/7+zq/+/s6v/ifQ7/7Ozs/+zs7P/ifQ7/7uro/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7uro/+zo5v/u6uj/7uro/+7q6P/s6Ob/7uro/+7q6P/u6uj/7Ojm/+7q6P/u6uj/7Ojm/+J9Dv/s7Oz/7Ozs/+J9Dv/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/7Ojm/+zo5v/s6Ob/4n0O/+zs7P/s7Oz/4n0O/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/q5uT/6ubk/+rm5P/ifQ7/7Ozs/+zs7P/ifQ7/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+nl4v/p5eL/6eXi/+J9Dv/s7Oz/7Ozs/+J9Dv/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/5uHe/+bh3v/m4d7/6eXi/+bh3v/m4d7/5uHe/+nl4v/m4d7/5uHe/+bh3v/p5eL/4n0O/+zs7P/s7Oz/4n0O/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/m4d7/5uHe/+bh3v/ifQ7/7Ozs/+zs7P/ifQ7/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+bh3v/k39z/5uHe/+Tf3P/m4d7/5N/c/+J9Dv/s7Oz/7Ozs/+J9Dv/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/i3Nn/4tzZ/+Lc2f/k39z/4n0O/+zs7P/s7Oz/4n0O/+Lc2f/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/h29j/4dvY/+Hb2P/ifQ7/7Ozs/+zs7P/ifQ7/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+DZ1v/g2db/4NnW/+J9Dv/s7Oz/9fTy/+J9Dv/8+/r/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/4n0O//X08v/8+/r/6KFU/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ifQ7/4n0O/+J9Dv/ooVT//Pv6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранный элемент как PNG"
                value="click"/>
    
            <menuitem class="menuitem-iconic"
                image="data:image/x-icon;base64,AAABAAEAEREAAAEAIADwBAAAFgAAACgAAAARAAAAIgAAAAEAIAAAAAAAyAQAAAAAAAAAAAAAAAAAAAAAAADDn2Hfz5pE/8eVQP7IlkH/yJZB/8iWQf/IlUH/yJVA/8iVQP/IlED/yJQ//8iUP//IlD//yJM+/8iTPv/Hkj3/nI1w//bDbP//8OH//+zW///s1///69b//+rV///p1P//59L//+XQ///izf//38n//9vG///ZxP//1b///dG////Prf/KlkX/88N4/v37///99Pj//fT6//vy9v/68fT/+u/y//rt8P/66u3/+ufq//rl6P/64eT/+93h//3a3//71d7//9LK/86XR//0xHb//vv////18///9fT///f5///4////9f7///P8///v+f//7Pb//+nz///m8f//4eb//9vZ//3Y2v//1Mb/zphH//TGd//+/////Pj1///7///Q58r/m9aV/6TZnv+i15r/otWY/6LTlv+j0pb/mc6M/9DXuf//3+P//Nnc///Wyf/OmUf/9MZ3//7////8+/j//////53WnP+Y5pn/rvGv/6PvpP+e7p//me6b/5nvm/95533/mM+L///j7f/629z//9jL/86ZR//0xnf//v////z9+v//////qtup/8Xzxf/a/tn/z/vO/8n7yf/D+sL/xPvD/6Hzo/+j05b//+Tu//re3///2cz/zplI//XGeP/+/////P36//////+n26f/uvC6/9T71P/K+Mr/xvjG/8D3wP+/+L//nfCf/6LTlf//5u//+t/g///cz//OmUj/9MZ3//7////8/fr//////6rcqv/G9MX/3//f/9n92f/V/NX/0PzQ/9H+0P+s9a7/pdSY///o8f/64OL//9zP/86aSP/0xnf//v////z9+f//////ndid/5TjlP+v7q//qeyp/6jsqf+k7KX/p+6n/4Tlh/+Z0Y7//+r0//rh4v//3tH/zppI//TGd//+/////v77///////Y8Nj/p9+n/6/jr/+t4a3/rd2p/67bpv+u2ab/p9Wc/9jgx///6Oz//OPl///e0f/Omkj/9MV1//7//////fr///78///+/f///////////////////P////j////0+///8fn//+vu///m4v/94+P//97P/86ZSP/zx3v//v/////+/f///////f////v////7////+/////v+///7+///+/f///vz/P/98Pr//+33//3p9///5OL/zppL//a1Sv/0xoL/9cR7//XEfP/1xHz/9cR8//XEfP/1xH3/9cR8//XCev/1wXr/9b94//W9d//1u3X/87l0//y6bP/Llj7/+pMA/vWBAP/1gwD/9YMA//WDAP/1gwD/9YMA//WDAP/1gwD/9YQA//WEAP/1hAD/9YQA//WEAP/zhAH//okA/8qLIv3xpzP/4ptV/+OdU//jnVP/451T/+OdU//jnVP/451T/+OdU//jnVL/451S/+OdUv/jnVL/451S/+GdVf/qnUf/2aRJ/9q0c9/8yn7/98V5/vjGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6//jGev/4xnr/+MZ6/vrIe/+jj2y4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
                label="Сохранить выбранную область как PNG"
                value="clipping"/>
        </menugroup>
    `);
    var menugroup = df.firstChild;
    menugroup.setAttribute("context", "");
    menugroup.setAttribute("oncommand", "handleCommand(event);");
    menugroup.handleCommand = e => {
        var name = _id + ":DataURLReady";
        main = main.replace("%MESSAGE_NAME%", name);

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

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

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

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

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

Отредактировано _zt (18-05-2022 14:32:52)

Отсутствует

 

№31319-05-2022 00:41:41

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 100.0

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

_zt
Спасибо.

Отсутствует

 

№31421-05-2022 11:11:09

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 100.0

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

Dumby
Кнопка Save не сохраняет полностью длинные страницы на этом форуме, если выбрать "Сохранить всю страницу как PNG", низ обрезается. Если возможно, поправь плиз.

Отсутствует

 

№31521-05-2022 13:20:13

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

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

_zt пишет

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

Набросок: три фрагмента (если оседание фавиконов в загрузках не ценность).
Первый и второй удалить, третий добавить.

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

Выделить код

Код:

if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15
    ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin)
    : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) =>
        internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin);

// Сохранить иконку текущего сайта с диалогом сохранения .............
function saveFavicon() {
       var uri = gBrowser.currentURI;
       function getSiteName() {
                  try { var domain = uri.host.split('.') } catch(e) { return "" };
                   domain = (domain.length == 2) ? domain[0] : domain[1]
                   return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " ";  
            };
    var url = gBrowser.selectedTab.image;
    url && saveImageURL(
        url, getSiteName(), null, false, false, null, null,
        /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"]
            .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)),
        null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal
    );
};
Выделить код

Код:

function saveShortcuts() {
var file = Components.classes["@mozilla.org/file/local;1"].
           createInstance(Components.interfaces.nsIFile);
file.initWithPath(folderpath);

if( !file.exists() || !file.isDirectory() ) {   file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);}

var savetodir=folderpath+"\\"; 
var urllink=gBrowser.currentURI.spec;
var out=getTabLabel();
var filename=savetodir+out+'.url';
var data="[InternetShortcut]\r\nURL="+urllink+"\r\n";

saveToFile(data, filename);
 // стиль для изображения во всплывающей подсказке ....
      var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
      var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }'));
      sss.loadAndRegisterSheet(uri, 0);
   // подсказка
   var notific = 'Сохранил в: ' + folderpath;
   var image = gBrowser.selectedBrowser.mIconURL;
   Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific);
};
Выделить код

Код:

async function pick(fileName) {
	try {
		var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
		await IOUtils.makeDirectory(file.path);
	} catch {
		file = Services.dirsvc.get("Desk", Ci.nsIFile);
	}
	var fp = makeFilePicker();
	fp.init(window, "", fp.modeSave);
	fp.displayDirectory = file;
	fp.defaultString = fileName;
	return await new Promise(fp.open) != fp.returnCancel && fp.file;
}
function saveFavicon() {
	var dn = "favicon";
	var re = /^data:(image\/[^;,]+)/i;
	var ms = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
	(saveFavicon = async () => {
		var url = gBrowser.selectedTab.image;
		if (!url) return;
		if (re.test(url)) {
			try {var name = gBrowser.currentURI.host || dn;} catch {name = dn;}
			name += "." + ms.getPrimaryExtension(RegExp.$1, "ico");
		} else
			var name = Services.io.newURI(url).QueryInterface(Ci.nsIURL).fileName;

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

voqabuhe пишет

Если возможно

Говорят что нет.

Отредактировано Dumby (21-05-2022 13:24:24)

Отсутствует

 

№31621-05-2022 13:39:57

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3231
UA: Firefox 100.0

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

Dumby
А сохранение скриптом в .pdf без разбивки на страницы тоже никак?

Отсутствует

 

№31721-05-2022 21:22:00

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

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

Dumby
Фавиконки не ценность, даже отключены в about:config.
Спасибо, все работает как надо.
   
А для открытия списков URL и сохранения списка URL - адресов выбранных или всех текущих вкладок, есть скрипт? Из буфера обмена / файла, в буфер обмена / файл. А то приходится сохранять папку закладок, потом копировать ее и вставлять в файл, для восстановления делать все в обратном порядке. И хотелось бы еще заголовок видеть перед каждым адресом.

Отредактировано _zt (21-05-2022 21:22:43)

Отсутствует

 

№31825-05-2022 19:34:31

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

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

voqabuhe пишет

А сохранение скриптом в .pdf без разбивки на страницы тоже никак?

У меня не получилось какой-нибудь browser.js или about:license.
Поднимаешь paperHeight — сначала качество съезжает, а ещё больше — вообще пустота.


Кстати, imgIEncoder способен собрать PNG большего размера чем canvas,
настолько, что даже сам браузер не сможет его отобразить, только подходящий вьювер.
Так что, для любителей экстремальных вещей, можно его попробовать.
Вот, например, автономный код для запуска в окне браузера, типа с консоли, или вкладка Код в CB.
Следует понимать, что на больши́х страницах может случиться ошибка (или даже краш) out of memory.

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

Выделить код

Код:

(async cid => {
	var fp = makeFilePicker();
	fp.init(window, "Сохранить как…", fp.modeSave);
	fp.appendFilter("", "*.png");
	var lab = DownloadPaths.sanitize(gBrowser.selectedTab.label);
	fp.defaultString = lab.slice(0, 50).trimRight() + ".png";
	var res = await new Promise(fp.open);
	if (res == fp.returnCancel || !fp.file) return;
	var {file} = fp;

	var br = gBrowser.selectedBrowser;
	var name = "extrem-encoder:de-xy-dpr-info";
	var url = "data:,(" + encodeURIComponent((de, name) => sendAsyncMessage(
		name, [de.scrollWidth, de.scrollHeight, content.devicePixelRatio]
	)) + `)(content.document.documentElement, "${name}")`;

	var mm = br.messageManager;
	mm.loadFrameScript(url, false);
	var res = await new Promise(r => {
		var lst = msg => mm.removeMessageListener(name, lst, r(msg.data));
		mm.addMessageListener(name, lst);
	});
	var [width, height, k] = res;
	var rectWidth = width;

	width = Math.floor(width * k);
	height = Math.floor(height * k);

	var step = 2000;
	var rectHeight = step / k;

	var canvas = document.createElement("canvas");
	canvas.width = width;
	canvas.height = step;
	var ctx = canvas.getContext("2d");

	var stride = width * 4;
	var encoder = Cc[cid].createInstance(Ci.imgIEncoder);
	var RGBA = encoder.INPUT_FORMAT_RGBA;
	encoder.startImageEncode(width, height, RGBA, "");

	var cwg = br.browsingContext.currentWindowGlobal;
	for(var y = 0; y < height; y += step) {

		var rect = new DOMRect(0, y / k, rectWidth, rectHeight);
		ctx.drawImage(await cwg.drawSnapshot(rect, k, "white"), 0, 0);

		var {data} = ctx.getImageData(0, 0, width, step);
		encoder.addImageFrame(data, data.length, width, step, stride, RGBA, "");
	}
	encoder.endImageEncode();

 	var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
 	stream.setInputStream(encoder.QueryInterface(Ci.nsIInputStream));
 	var bytes = stream.readByteArray(stream.available());

	await IOUtils.write(file.path, new Uint8Array(bytes));
 	file.reveal();

})("@mozilla.org/image/encoder;2?type=image/png");

_zt пишет

есть скрипт?

Увы, я не знаток существования скриптов.

открытия списков URL и сохранения списка URL

Если формат списка внутренний (только для этого скрипта),
то можно тяп-ляп что-нибудь набросать, например виджет

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	id: "799775",
	label: "799775",
	tooltiptext: "799775",

	localized: false,
	marker: "[ucf_tabs_linkset]\n\n",
	onCreated(btn) {
		btn.setAttribute("image", "resource://usercontext-content/pet.svg");
		btn.setAttribute("type", "menu");
		btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment(
			`<menupopup oncommand="creator.cmd(event);">
				<menuitem label="Сохранить адреса вкладок" value="1"/>
				<menuitem label="Сохранить адреса всех вкладок" value="2"/>
				<menuseparator/>
				<menuitem label="Копировать адреса вкладок" value="3"/>
				<menuitem label="Копировать адреса всех вкладок" value="4"/>
				<menuseparator/>
				<menuitem label="Открыть из файла" value="5"/>
				<menuitem label="Открыть из буфера" value="6"/>
			</menupopup>`
		));
		btn.firstChild.creator = this;
	},
	async cmd(e) {
		var win = e.view, num = e.target.value;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await win.IOUtils.readUTF8(file.path);
		}
		if (!text?.startsWith(this.marker)) return;

		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		var arr = text.split("\n");
		for(var ind = arr.length - 1; ind > 2; ind -= 3)
			var tab = gb.addTab(arr[ind], arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, fileName) {
		try {
			var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
			await IOUtils.makeDirectory(file.path);
		} catch {
			file = Services.dirsvc.get("Desk", Ci.nsIFile);
		}
		var fp = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		return await new Promise(fp.open) != fp.returnCancel && fp.file;
	}
}))();

Отсутствует

 

№31925-05-2022 21:00:00

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

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

Dumby
Ну может уже делал.
   
Почти то что нужно.
Только вот список URL для открытия может быть создан не скриптом или скопирован откуда угодно и может быть в любом формате, и с любым текстовым мусором помимо адресов.
А вот формат сохранения идеален.
И еще, вот с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере.

Отсутствует

 

№32026-05-2022 09:28:53

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

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

_zt пишет

откуда угодно … в любом …  с любым

Я же совершенно не разбираюсь в урлах и их списках.
Конкретного бы чего-нибудь дал, лекцию прочёл.
Ладно, написал какую-то лажу, просто чтобы не ничего.

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

Выделить код

Код:

/*
		if (!text?.startsWith(this.marker)) return;

		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		var arr = text.split("\n");
		for(var ind = arr.length - 1; ind > 2; ind -= 3)
			var tab = gb.addTab(arr[ind], arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
*/
		if (!text) return;
		var own = text.startsWith(this.marker);
		if (own) var arr = text.split("\n");
		else {
			var urls = this.parse(text);
			if (!urls?.length) return;
		}
		var gb = win.gBrowser;
		var arg = {
			index: gb.selectedTab._tPos + 1,
			triggeringPrincipal: win.document.nodePrincipal
		};
		if (own)
			for(var ind = arr.length - 1; ind > 2; ind -= 3)
				var tab = gb.addTab(arr[ind], arg);
		else 
			for(var url of urls)
				var tab = gb.addTab(url, arg);
		if (!e.button && !e.shiftKey) gb.selectedTab = tab;
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(str);
			else {
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(Boolean);
					for(var sub of arr)
						url = this.url(sub),
						url && result.add(url);
				}
			}
		}
		var {size} = result;
		if (size) {
			result = Array.from(result);
			if (size > 1) result.reverse();
			return result;
		}
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [",", ";", '"', "'", /[\[\]]/, /()/],
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str).spec;
		} catch {}
	},

с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере

Да, мой косяк. Конечно же await win.IOUtils.makeDirectory(file.path);
Это на тот случай, что в настройке путь до несуществующей папки,
что маловероятно, может и правильно, что удалил эту строку.

Отсутствует

 

№32126-05-2022 18:30:43

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

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

Dumby
Да вроде нормально. Только вот что заметил:
1. место открытия списков - надо бы что бы всегда открывались после последней вкладки
2. ссылку с запятой в конце адреса открывает вместе с запятой
3. markdown не открывает
4. BB-code с заголовком не открывает
   
5. очень желательно вывести в меню опцию "заменять одиночные пробелы процентами" - "%20", но если после пробела идет любой спецсимвол, то обрезать ссылку до этого пробела.
Так как, многие поисковики отдают ссылки на страницы с поисковыми запросами с пробелами, да и вообще пробелы частенько встречаются, в ссылках на файлы, на самодельных сайтах и т.п.
Это не часто нужно и как правило будет мешать получению правильных ссылок, поэтому лучше сделать это как опцию, если такое возможно.
   

Пример списка с перечисленными проблемами

Выделить код

Код:

Создание гиперссылок - Изучение веб-разработки | MDN
https://developer.mozilla.org/ru/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks // afgaga

Управление дополнениями
about:addons

Ресурс
resource://usercontext-content/pet.svg


HTML
<a href="https://en.wikipedia.org/wiki/Percent-encoding">Percent-encoding - Wikipedia</a>

BB-code без заголовка
[url]https://forum.mozilla-russia.org/index.php[/url]


/* Проблемные ссылки */

Ссылка с запятой в конце адреса (открывает вместе с запятой)
https://github.com/Aris-t2/CustomCSSforFx/blob/master/classic/css/tabs/tabs_multiple_lines.css,

BB-code с заголовком (не открывает)
[url=https://forum.mozilla-russia.org/viewtopic.php?id=76642&p=14]UCF-скрипты на этом форуме | Форум Mozilla Россия[/url]

markdown 1 (не открывает)
[Ссылка на корень сайта](https://planshet-info.ru/kompjutery/ssylka-na-koren-sajta)

markdown 2 (не открывает)
![Image and Preview Themes on the toolbar](https://markdownmonster.west-wind.com/docs/images/EditorPreviewThemeUi.png) 

Ссылка с пробелами (обрезает по первому пробелу)
https://yandex.ru/yandsearch?text=команда на копирование файлов и папок в bat&lr=213


ps^ Исправил пост, сам пытался парсер править и сломал BB-code без заголовка.

Отредактировано _zt (26-05-2022 18:53:23)

Отсутствует

 

№32227-05-2022 09:02:28

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

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

_zt
Хорошо, попробую учесть.

если после пробела идет любой спецсимвол

А что за спецсимволы? Написал так: singleSpace: / (?![\s"])/g,
то есть только «\s» и «"», не знаю что ещё.

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

Выделить код

Код:

//.......
	onCreated(btn) {
		btn.setAttribute("image", "resource://usercontext-content/pet.svg");
		btn.setAttribute("type", "menu");
		btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment(
			`<menupopup oncommand="creator.cmd(event);" onpopupshowing="creator.check(this.lastChild);">
				<menuitem label="Сохранить адреса вкладок" value="1"/>
				<menuitem label="Сохранить адреса всех вкладок" value="2"/>
				<menuseparator/>
				<menuitem label="Копировать адреса вкладок" value="3"/>
				<menuitem label="Копировать адреса всех вкладок" value="4"/>
				<menuseparator/>
				<menuitem label="Открыть из файла" value="5"/>
				<menuitem label="Открыть из буфера" value="6"/>
				<menuitem label="Заменять одиночные пробелы процентами" closemenu="none" type="checkbox" value="7"/>
			</menupopup>`
		));
		btn.firstChild.creator = this;
	},
	check(item) {
		var pref = "ucf.linkset-widget.replspace";
		this.checked = Services.prefs.getBoolPref(pref, false);
		this.setPref = Services.prefs.setBoolPref.bind(null, pref);
		(this.check = item => {
			this.checked ? item.setAttribute("checked", true) : item.removeAttribute("checked");
		})(item);
	},
	singleSpace: / (?![\s"])/g,
	async cmd(e) {
		var num = e.target.value;
		if (num == 7)
			return this.setPref(this.checked = e.target.hasAttribute("checked"));
		var win = e.view;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await win.IOUtils.readUTF8(file.path);
		}
		if (!text) return;
		// if (!text || text.length > 500_000) return; // limit?

		var own = text.startsWith(this.marker);
		if (this.checked && !own) text = text.replace(this.singleSpace, "%20");

		var gb = win.gBrowser, tl = gb.visibleTabs.length;
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(arr[ind]);
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(url);
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(str);
			else {
				var bb = true;
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(this.colon);
					for(var sub of arr) {
						if (bb) { // []
							var ind = sub.indexOf("=");
							if (ind != -1 && ind < sub.indexOf(":"))
								sub = sub.slice(ind + 1);
						}
						url = this.url(sub);
						url && result.add(url);
					}
					bb = false;
				}
			}
		}
		// console log instead of open
		//if (true) return Services.console.logStringMessage(Array.from(result).join("\n"));

		//var {size} = result;
		//if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit?

		return result;
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"],

	unwanedEnds: /,$/,
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str.replace(this.unwanedEnds, "")).spec;
		} catch {}
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, fileName) {
		try {
			var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
			await win.IOUtils.makeDirectory(file.path);
		} catch {
			file = Services.dirsvc.get("Desk", Ci.nsIFile);
		}
		var fp = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		return await new Promise(fp.open) != fp.returnCancel && fp.file;
	}
}))();

Отсутствует

 

№32327-05-2022 14:32:14

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

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

Dumby
Да почти любой спецсимвол, кроме, пожалуй, скобок (и точка под вопросом). Так как есть нормально, потом всегда можно будет изменить.
   
На мой взгляд осталось два момента...
1. Не открывает ANSI файлы, а это стандартная кодировка Windows.
2. Неверно открывает URL с запятой в конце. При копировании со страниц адресов оформленных тегами (адрес-адрес) они копируются с прилипшей запятой:

Выделить код

Код:

<a href="https://en.wikipedia.org/">https://en.wikipedia.org/</a>,
[https://en.wikipedia.org/](https://en.wikipedia.org/),
[url=https://en.wikipedia.org/]https://en.wikipedia.org/[/url],

Всегда получается "https://en.wikipedia.org/," при копировании.
   
Кстати, там может быть и точка с запятой. Может по аналогии с singleSpace сделать, строку с перечислением отсекающих символов?
Даже не символов, а группы символов, например: ,\s ;\s
   
А в singleSpace: / (?![\s"... экранировать символы нужно и какие, или вообще можно все экранировать?

Отредактировано _zt (27-05-2022 15:07:51)

Отсутствует

 

№32429-05-2022 12:41:57

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

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

_zt пишет

Неверно открывает URL с запятой в конце.

Да, есть опечатки в коде.
Про копирование не понял. Поскольку код не занимается
копированием со страниц, то, видимо, это объяснение откуда что берётся.
Точку с запятой добавил в unwantedEnds: /[,;]$/,

Не открывает ANSI файлы, а это стандартная кодировка Windows.

Насколько мне известно, в Firefox нет API для определения кодировки.
Самое простое так: пытаемся прочитать как UTF-8, если ошибка,
тогда читаем бинарную строку и конвертируем в Windows-1251.

или

Есть ещё такой жуткий вариант: загружаем добро в невидимое окно как документ,
где Firefox сам определит кодировку по содержимому, и забираем результат.
В многопроцессном режиме, естественно, грузится не захочет, поэтому ещё
и свой протокол надо регистрировать. Ну и в консоли будет спам, что мол
кодировка не указана, что ожидаемо. Вобщем, только на крайний случай,
если первый вариант не подойдёт.

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

Выделить код

Код:

//.......
	async cmd(e) {
		var num = e.target.value;
		if (num == 7)
			return this.setPref(this.checked = e.target.hasAttribute("checked"));
		var win = e.view;
		if (num < 5) {
			var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs;
			if (num > 2) return this.copy(this.text(tabs));
			var file = await this.pick(
				win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt`
			);
			return file && win.IOUtils.writeUTF8(file.path, this.text(tabs));
		}
		if (num == 6) var text = win.readFromClipboard();
		else {
			var file = await this.pick(win);
			if (!file) return;
			var text = await this.read(file);
		}
		if (!text) return;
		// if (!text || text.length > 500_000) return; // limit?

		var own = text.startsWith(this.marker);
		if (this.checked && !own) text = text.replace(this.singleSpace, "%20");

		var gb = win.gBrowser, tl = gb.visibleTabs.length;
		if (own)
			for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3)
				gb.addTrustedTab(arr[ind]);
		else {
			var urls = this.parse(text);
			if (urls?.size) for(var url of urls) gb.addTrustedTab(url);
		}
		if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl];
	},
	read(file) {
		var read1251 = async file => {
			var suc = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
				.createInstance(Ci.nsIScriptableUnicodeConverter);
			suc.charset = "windows-1251";
			var reader = new FileReader();

			return (read1251 = async file => {
				reader.readAsBinaryString(file);
				await new Promise(resolve => reader.onloadend = resolve);
				return suc.ConvertToUnicode(reader.result);
			})(file);
		}
		var {IOUtils} = Cu.getGlobalForObject(Cu);
		return (this.read = async file => {
			try {return await IOUtils.readUTF8(file.path);}
			catch {return read1251(file);}
		})(file);
	},
	parse(text) {
		var result = new Set();

		var candidates = new Set(
			text.split(this.space).filter(this.colon)
		);
		for(var str of candidates) {
			var url = this.url(str);
			if (url) result.add(url);
			else {
				var bb = true;
				for(var splitter of this.splitters) {
					var arr = str.split(splitter).filter(this.colon);
					for(var sub of arr) {
						if (bb) { // []
							var ind = sub.indexOf("=");
							if (ind != -1 && ind < sub.indexOf(":"))
								sub = sub.slice(ind + 1);
						}
						url = this.url(sub);
						url && result.add(url);
					}
					bb = false;
				}
			}
		}
		// console log instead of open
		//if (true) return Services.console.logStringMessage(Array.from(result).join("\n"));

		//var {size} = result;
		//if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit?

		return result;
	},
	space: /\s+/,
	colon: str => str.includes(":"),
	splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"],

	unwantedEnds: /[,;]$/,
	url(str) {
		try {
			var scheme = Services.io.extractScheme(str);
			if (scheme.length + 1 == str.length || scheme == "default") return;
			var ph = Services.io.getProtocolHandler(scheme);
			if (ph.scheme == scheme)
				return Services.io.newURI(str.replace(this.unwantedEnds, "")).spec;
		} catch {}
	},
	text(tabs) {
		var res = [];
		for(var tab of tabs) {
			var br = tab.linkedBrowser;
			var url = br.currentURI.spec, beg = url.slice(0, 40);
			var title = br.contentTitle || "untitled";
			if (title.startsWith(beg)) title = beg;
			res.push(title + "\n" + url);
		}
		return this.marker + res.join("\n\n");
	},
	copy(text) {
		var cb = Services.clipboard.kGlobalClipboard;
		var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
		(this.copy = text => ch.copyStringToClipboard(text, cb))(text);
	},
	async pick(win, fileName) {
		try {
			var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);
			await win.IOUtils.makeDirectory(file.path);
		} catch {
			file = Services.dirsvc.get("Desk", Ci.nsIFile);
		}
		var fp = win.makeFilePicker();
		fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen);
		fp.displayDirectory = file;
		fp.appendFilter("", "*.txt");
		fp.appendFilters(fp.filterAll);
		if (fileName) fp.defaultString = fileName;
		if (await new Promise(fp.open) == fp.returnCancel) return;
		if (fileName) var file = fp.file;
		else {
			var file = fp.domFileOrDirectory;
			file.path = fp.file.path;
		}
		return file;
	}
}))();

экранировать символы нужно и какие, или вообще можно все экранировать?

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


А можно экранировать, наоборот, те, которые при экранировании не образуют другой смысл.
Например s соответствует букве «эс», а \s соответствует любому юникодному символу-разделителю,
но z соответствует букве «зэт», и \z тоже соответствует букве «зэт».

Отсутствует

 

№32530-05-2022 02:45:58

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

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

Dumby пишет

Про копирование не понял.

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

Dumby пишет

Есть ещё такой жуткий вариант

Как то сложно все. Не надо наверное, я потестирую то что есть.
   
Не нравится мне как это работает - "if (url) result.add(url);" , окно группировки одновременно открытых вкладок TST растягивается во все окно браузера по ширине. Причем, все равно что открываешь, ansi или utf8. В предыдущем варианте текст url не шифровался и окно не растягивало. Речь про адреса с utf8 символами, например, приведенный выше яндекс. Можно это поправить? Может обратную дешифровку сделать при "выводе" из скрипта?

Отредактировано _zt (30-05-2022 03:22:34)

Отсутствует

 

Board footer

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