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

Заказывай стафф с атрибутикой Mozilla и... пусть все вокруг завидуют тебе! Быть уникальным - быть с Mozilla!

№95106-07-2023 21:01:59

vassemm
Участник
 
Группа: Members
Зарегистрирован: 11-02-2019
Сообщений: 23
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»

Благодарю.
На первый беглый взгляд все заработало. WIN11  FF116 FF117
:beer:

Отсутствует

 

№95206-07-2023 21:11:44

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Можно заменить все (два) « instanceof Ci.nsIDOMChromeWindow» на «.isChromeWindow»

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



Добавлено 06-07-2023 21:16:32

Viatcheslav пишет

скриптом для UCF размещения фавиконки сайта в адресной строке

Если ничего не путаю, то вот: https://forum.mozilla-russia.org/viewto … 52#p793152

Отредактировано unter_officer (06-07-2023 21:16:47)


«The Truth Is Out There»

Отсутствует

 

№95306-07-2023 23:55:05

Viatcheslav
Участник
 
Группа: Members
Откуда: г. Бобруйск, Беларусь
Зарегистрирован: 23-11-2016
Сообщений: 312
UA: Firefox 88.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

unter_officer пишет

Если ничего не путаю, то вот: https://forum.mozilla-russia.org/viewto … 52#p793152

Спасибо большое, но... Всё время показывает дефолтную иконку.
А вот этот работает - https://github.com/Aris-t2/CustomJSforFx/blob/master/scripts/favicon_in_urlbar.uc.js

Отсутствует

 

№95407-07-2023 00:43:38

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Viatcheslav
Вот это рабочий, если нет, то проблема у вас.
Иконку дефолтную свою вписать или положить по прописанному пути.

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

Выделить код

Код:

// FavIcon in URL-bar - Иконка сайта в url-баре
// https://forum.mozilla-russia.org/viewtopic.php?pid=789469#p789469
(this.faviconinurlbar = {
            init(that) {
                var identity = document.querySelector("#identity-icon");
                if (!identity)
                    return;
                var iconDefault = "chrome://user_chrome_files/content/custom_styles/png/globe-16.png"; // или свою иконку
                var style = "data:text/css;charset=utf-8," + encodeURIComponent(`
                    #identity-faviconinurlbar {
                        --v-faviconinurlbar-default: url("${iconDefault}");

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

Отсутствует

 

№95507-07-2023 09:59:38

Viatcheslav
Участник
 
Группа: Members
Откуда: г. Бобруйск, Беларусь
Зарегистрирован: 23-11-2016
Сообщений: 312
UA: Firefox 88.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

_zt пишет

Вот это рабочий, если нет, то проблема у вас

Наверное... :(
А каким способом подключали?
У меня вот этот - https://github.com/Aris-t2/CustomJSforFx/blob/master/scripts/favicon_in_urlbar.uc.js
подключен к UCF в custom_script_win.js по событию "DOMContentLoaded".
Только не зря его Виталий назвал "ужасным... :usch:

Отсутствует

 

№95607-07-2023 19:51:45

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Viatcheslav Они разные, может и подключать по разному надо, у меня в CustomStylesScripts.jsm

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

Это аналог custom_script_win.js после секции ...

Выделить код

Код:

load() {
        if (this.initialized)
            return;
        this.initialized = true;
        /* ************************************************ */
тут коды или импорт

Импорт раньше у меня такой был ...

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

Выделить код

Код:

// Для скриптов отдельными файлами (см. примеры)
        // С уточнением в каком "документе" работать
        // https://forum.mozilla-russia.org/viewtopic.php?pid=788301#p788301
        (async () => {
            var loadscript = (relpath, obj) => {
                try {
                    Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${relpath}`, obj, "UTF-8");
                    return true;
                } catch(e) { }
                return false;
            },
            load_scripts_by_url = {
                browser: win => {
                    //>>>>>>>>>>| Блок требуется для боковой панели и т.п. |>>>>>>>>>>
                    var box = document.querySelector("#browser") || window;
                    var listener = e => {
                        var doc = e.target || ({});
                        load_scripts_by_url[doc.documentURI]?.(doc.defaultView);
                    };
                    box.addEventListener("pageshow", listener);
                    this.loadscriptswinandsidebar = {
                        destructor() {
                            box.removeEventListener("pageshow", listener);
                        }
                    };
                    this.unloadlisteners.push("loadscriptswinandsidebar");
                    /* <<<<<<<<<<<<<<<<<<<< */
                    
                    //>>>>>>>>>>| Загрузка скриптов для browser.xhtml |>>>>>>>>>>
                        // Здесь скрипты для основного окна
                        loadscript("custom_js_win/ucf_Bookmarks_Star_Tooltip_Helper.uc.js", win);
                        loadscript("custom_js_win/ucf_Context_Menu_Open_With.us.js", this) && this.unloadlisteners.push("contextmenuopenwith");
                        loadscript("custom_js_win/ucf_FavIcon_In_URL-bar.uc.js", this);
                        loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.browser();
                        loadscript("custom_js_win/ucf_SidebarTabs.us.js", this) && this.unloadlisteners.push("sidebar_tabs");
                        // и т.д.

                    //<<<<<<<<<<<<<<<<<<<<
                },
                //>>>>>>>>>>| Загрузка скриптов для др. документов |>>>>>>>>>>
                "chrome://browser/content/places/bookmarksSidebar.xhtml": win => {
                    // Здесь скрипты для боковой панели закладок
                    loadscript("custom_js_win/s_AutoCloseBookMarkFolder_Fx37.uc.js", win);
                    loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.bookmarksSidebar();
                    // и т.д.

                },
                "chrome://browser/content/places/historySidebar.xhtml": win => {
                    // Здесь скрипты для боковой панели истории
                    loadscript("custom_js_win/s_AutoCloseHistoryFolder_Fx37.uc.js", win);
                    loadscript("custom_js_win/ucf_Open_Hisory_Bookmark_In_NewTab.uc.js", win) && win.ucf_where_to_open_link.historySidebar();
                    // и т.д.

                },
                //<<<<<<<<<<<<<<<<<<<<
            };
            load_scripts_by_url.browser(window);
        })();


Понятия не имею бкдет ли оно сейчас работать.

Отредактировано _zt (07-07-2023 20:03:41)

Отсутствует

 

№95707-07-2023 20:19:08

Viatcheslav
Участник
 
Группа: Members
Откуда: г. Бобруйск, Беларусь
Зарегистрирован: 23-11-2016
Сообщений: 312
UA: Firefox 88.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

_zt пишет

Импорт раньше у меня такой был ...

Это для старой версии UCF
Спасибо за помощь :beer:

Отсутствует

 

№95810-07-2023 13:38:30

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby
Есть ваш скриптик "Восстановление удалённых закладок".

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

Выделить код

Код:

//
// Dumby: https://forum.mozilla-russia.org/viewtopic.php?pid=801497#p801497
//
(async sep => {
	if (!sep) return;

	var key = "hasRemoveTransaction";
	var g = Cu.import("resource://gre/modules/PlacesTransactions.jsm", {});

	var raws = (g.lazy || g).TransactionsHistory?.proxifiedToRaw;
	if (raws) g = raws;

	if (!g[key]) {
		if (!raws) {
			Services.scriptloader.loadSubScript(
				`data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g
			);
			raws = g[key];
		}
		g[key] = entry => {
			for(var tr of entry)
				if (raws.get(tr) instanceof PlacesTransactions.Remove)
					return true;
		}
	}
	var menuitem = document.createXULElement("menuitem");
	for(var args of Object.entries({
		closemenu: "single",
		class: "menuitem-iconic",
		id: "placesCmd_undoRemove",
		label: "Восстановить удалённое",
		oncommand: "PlacesTransactions.undo().catch(Cu.reportError);",
		image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQEAYAAABPYyMiAAAABmJLR0T///////8JWPfcAAAACXBIWXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAQAAAAEABcxq3DAAAEd0lEQVRIx53Ve2yW5RkG8N/7ft8Hxa/9CgjSSDnMA4xVTUMQWzadhzhnjSCC0y0jSjom+JdGp844ZhSjHLJsowaNZoCCHOWglXGoQbo2fBW3obDNUBfXKpsUVhxn6OHdH8+31GXGmL3/3M913ffzPNed93qeBxvmI8umCNjyPFEDSRJw40qKa9jyHSbdzY77Ar+9FTvZ+nHAb9T7P76YcwlOktlBdJia2SSthfRvaR/Mync5tp85Q2mbEHJ/X0bqRTpOBJyehog3C3j1HV9RQP91xG9T8z2SofyphMELWH4zW5s40s7kQ/T0cnYfXfPD1OgM0TlGzSN1DZkzxG1EdURjaa9HCfddEupfuSkIfHXJ5wVENA8Iw9QcRnbzXj31++g8w+WVZHMhXzaKhu2MGMtd36f5CTrn8WAJyf001KCYpA6dDBtLlOfoYuI6kgp6XuLsbhQRfw39ovDPx7/JgYS1b3NkCxcvoPgTevIULyFVTm4jyinp4uRJiirYt5BMLc15kvE0lnCsgZaE2wdx6LVCo9/ghQn4LhdFxEOonELv8ogVV9Iyjd9fx8T1FE2nq5XSQWQuJbsmrJH9JclMsldhFsW3EU0iuxsb6f9zTA3dHj1J1yPsKaPrIOtnkn6KDY2cvYF/vkfmINGuiPdnUD6Pd/7Mur/x0RCqXqTfj+m5glyO1B8ovR1pijeil6KvBw8V1QaB/QYWzNgSYu+TmMm5GVhKT47OBWR28kgnvxvKS/OjvuMGuTSr72FtBR/WctNe4l+H3GUt7DxB2TKe+CmnF5IZRzSZzFmS4wy9n2QGY2eR3Mx193DqAao+4vh04lZS2/EkQyqo7cWux4Kjwf4gKF3N5hEs+QsLlwVuVRPLP2FeTSh95hZSpSx6OOAkwWCWLSEqYWVz4JtLqd3GiuX0/oS6XKjd/BQbF6SRJynij4eJKpl7WSg8cCdzxjHjH3QuIsaorQx7NCx8QQdxCYM2kzxL423BbGN6SP+I6tmcO86hahqu4MA2To3mX+eTxbA2er6VJjcA/Yl2kOzlrutJxrDraaI1XH09j3fQVk7pcG5sCgIOttLVzejpmMPrnxJvYnIZXelwxGHTCOaX0PorsmUMOC/wpeNofg4fjg9EW3+k6CzcYJ99M7h6fSfxFFJrMZzFm0I+fzXxlTQ1EHf3eSlJyI3mlYGs/yEPx4F7/NkQ11zEymFhnDoY9RnwcHmIZ34Q1CcTkBD9FSM58gu8SmUHbmVuJbYRt2AiE/cwZT/tG3h+Gh29lH3A+VPZ+zMq1rHwcq6tY1U31ReQXPgFt3M+/9+xJRvM9f5S3NnX6btLqfqYt1p44xQrFnH0aX6zmBXIj+Hae0N95qG+ea/PJjrB7tV47csE7CzEJqKXyTcW8CquaibfwTOX0FjPLSfYk+G8XWyb+rlTUcndVUQjWXOK+FiBn/QfQen/FVBVVdhoc4FIkczqM5WBvPMWp4fQMpzFHeEN2NCFCcyNSH2b1AiSl3nhVnrnkjpNkqP9jrBvdzX8G2rIlxvMK3a7AAAAInpUWHRTb2Z0d2FyZQAAeNorLy/Xy8zLLk5OLEjVyy9KBwA22AZYEFPKXAAAAABJRU5ErkJggg==",
	}))
		menuitem.setAttribute(...args);

	var desc = Object.getOwnPropertyDescriptor(XULElement.prototype, "hidden");
	var {set} = desc;
	desc.set = () => {
		var entry = PlacesTransactions.topUndoEntry;
		var vis = entry && g[key](entry);
		vis && menuitem.removeAttribute("disabled");
		set.call(menuitem, !vis);
	}
	Object.defineProperty(menuitem, "hidden", desc);
	sep.after(menuitem);
})(document.getElementById("placesContext_deleteSeparator"));

Заметил в FF115 такой момент. Если вызвать контекстное меню на свободном месте панели закладок, то пункт "Восстановить удалённое" остаётся затенённым.
Untitled-2.png
В предыдущих версиях [firefox] всё работает нормально.
Возможно поправить?


«The Truth Is Out There»

Отсутствует

 

№95910-07-2023 14:09:58

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

ucf_hookClicks.js некорректно работает в 115.0.1, где-то нет тултипов, отвалилась подсветка и вся система алертов при копирований адресной строки через identity-box, и еще что-то. Все остальное работает, пока не разобрался что еще отвалилось.
Кстати, как в консоли высматривать ошибки? Тот который Ctrl+Shift+J.

Отредактировано b0ttle (10-07-2023 14:13:43)

Отсутствует

 

№96011-07-2023 08:24:44

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

unter_officer пишет

В предыдущих версиях [firefox] всё работает нормально.

Да, вижу. Слегка поменяли код.
Раньше disabled ставился перед hidden,
а теперь получилось наоборот, сначала hidden.


Можно попробовать вообще просто поставить на disabled заглушку

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

Выделить код

Код:

/*
		var vis = entry && g[key](entry);
		vis && menuitem.removeAttribute("disabled");
		set.call(menuitem, !vis);
	}
*/
		set.call(menuitem, !entry || !g[key](entry));
	}
	Object.defineProperty(menuitem, "disabled", {});


Bug 1780695 - «Remove Services.jsm» (Firefox 117+)
See also: Bug 1667455 - «Expose a "Services" property on all privileged JS scopes (like Cu/Cc/Ci)» (Firefox 104+)

Отсутствует

 

№96111-07-2023 14:12:13

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Можно попробовать вообще просто поставить на disabled заглушку

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


«The Truth Is Out There»

Отсутствует

 

№96211-07-2023 15:32:36

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Тут тултипы работают в hookClicks, но при запуске firefox почему-то пишет, что файл не запущен, хотя по виду все работает. Странно.
Проверил. В коде по ссылке, там тултипы работают, а функций нет, при кликах ничего не происходит.
Хотел свою версию сюда(код то не мой, не умею кодить. просто чуть-чуть под себя подогнал), но там какие-то символы мешают, сайт не принимает, а в base64txt, не умею.
Смог сделать в base64txt, но теперь из-за размера не пропустил, что-то про (64 КБ).
В общем, https://pastebin.com/fn2WwQVS Судя по длине кода, наверно никто не захочет в ней возиться.

Отредактировано b0ttle (11-07-2023 17:25:12)

Отсутствует

 

№96311-07-2023 16:56:04

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 405
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby Возможно ли запускать перевод выделеного текста горячими клавишами (двойнм Ctrl) в этом скрипте?

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

Выделить код

Код:

// Google Translate в контекстном меню.......
(this.googletranslate = {
            init(that) {
                var lc = navigator.lastClick = {}, w = null, xhtmlns = 'http://www.w3.org/1999/xhtml';
                var mouseUp = (e) => {
                    if (e.button) return;
                    lc.X = e.screenX - mozInnerScreenX;
                    lc.Y = e.screenY - mozInnerScreenY;
                };
                gBrowser.tabpanels.addEventListener('mouseup', mouseUp, false);
                this.destructor = () => {
                    gBrowser.tabpanels.removeEventListener('mouseup', mouseUp, false);
                    if (w)
                        w.closeWin();
                };
                that.unloadlisteners.push("googletranslate");
                var createWindow = function(text, status, title, id, pos, size) {
                    var win = window, doc = win.document, wId = 'ujs_window'+(id || '');
                    w = doc.getElementById(wId);
                    var keyDown = function(e) {if (!e.shiftKey && !e.ctrlKey && !e.altKey && e.keyCode == 27)doc.getElementById(wId).closeWin();};
                    var mouseDown = function() {doc.getElementById(wId).closeWin();};

                    if (w)
                        w.closeWin();
                    w = doc.createElementNS(xhtmlns, 'div');
                    w.setAttribute('style', 'position:fixed;display:block;visibility:hidden;left:0;top:0;width:auto;height:auto;border:1px solid gray;padding:2px;margin:0;z-index:99999;overflow:hidden;cursor:move;'+(typeof w.style.borderRadius === 'string' ? 'background-color:#eaeaea;padding-top:0px;border-radius:4px;box-shadow:0 0 15px rgba(0,0,0,.4);' : 'background:-o-skin("Window Skin");'));
                    w.id = wId;
                    w.closeWin = function() {
                        doc.removeEventListener('keydown', keyDown, false);
                        gBrowser.tabpanels.removeEventListener('mousedown', mouseDown, false);
                        this.parentNode.removeChild(this);
                        w = null;
                    };
                    w.addEle = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'div');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
                    w.addEle1 = function(str, style) {
                        var ele = doc.createElementNS(xhtmlns, 'textarea');
                        ele.setAttribute('style', style);
                        if (str) {
                            ele.innerHTML = str;
                            for (var el, all = ele.getElementsByTagName('*'), i = all.length; i--;) {
                                el = all[i];
                                if (/^(script|frame|iframe|applet|embed|object)$/i.test(el.nodeName)) {
                                    el.parentNode.removeChild(el);
                                } else {
                                    for (var att = el.attributes, j = att.length; j--;) {
                                        if (/^on[a-z]+$/i.test(att[j].name))att[j].value = '';
                                    }
                                }
                            }
                        }
                        return this.appendChild(ele);
                    };
                    var img = doc.createElementNS(xhtmlns, 'div');
                    img.setAttribute('style', 'display:block;float:right;width:16px;height:16px;padding:0;margin-top:2px;margin-right:1px;border:none;cursor:pointer;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACQUlEQVR4Xm2SPUhbURTHfzfNJ4nU0Axx7CJCN2kQtIgVCtq6OdQuBUftWIQqFNsKDo6iHTqIqN3a0kklUKTS2AZrcOjioFtRjFWUfH+803cS9GHwB4dzcu75/+8L9xgaSEDbLXhaASzAyXAMn5/DHxwwjeJgKLRxd3Q0atxuLhGgWqmQmp9PJ7LZJ5Ow3WhQFweDG20LC1FfRwc3kUsm+T48fPIzl3s8VTdxxLt+/2FhdlZkc1MkmdR8Pba2ajlrz3z1+9OvIOaIfb7DwsSEyNKSyM6OSLksEo/rbw2ttadntV7Gnv3k86VfqskvmDzv7hZ580Zkelrk4kJqFIsii4satVrRs+rYmBQHBuRvS4u8g/fuCmDOz+HgAIWpKRgfh3AYhoZQ8Hqxjo4o9PdT3d1FcQEl8KoBnJ7C/j6KZkZGYG4OIhEUFed7erD29q69jGrd1qVBqcQVKgwEuMSEQhjtXRk4JvW/kM1i2WGMga4uzMoKBIOIfbNiolEC6+uU+/qQRALFA1QBlwVYxmBcLmhuxqytQSgEKu7t1dBavwKvbeINh/Has2IMqnXpem4bky6qWLdvZgaOj2FwEHNyoqG19vRMZ8jZs1+NOfsNcZS3EFvzeNL51laRe/dEYrFadsLpZeyZDx7P6QN4Btwx1OE1xO4HAquP2tsjAZ+Pm8gWi3xMpc6W8/kXPyAO/DM4oOsZa2pafdjZGXF7PAgOpXKZL4nE2XImcyUGMDSg63kbhgvgtYCq8+akIL4J3y7Fyn+DokZOnLlMyQAAAABJRU5ErkJggg==");background:-o-skin("Caption Close Button Skin");');
                    img.title = (win.navigator.language.indexOf('ru') == 0) ? '\u0417\u0430\u043A\u0440\u044B\u0442\u044C' : 'Close';
                    img.addEventListener('click', function() {this.parentNode.closeWin();}, false);
                    w.appendChild(img);
                    var title = w.addEle(title, 'display:table;color:#000;font:17px Times New Roman;width:auto;height:auto;padding:0;margin:0 2px;cursor:text;');
                    title.onclick = e => {
                        e.preventDefault();
                        var url = e.target.href;
                        // Здесь открываем url как хотим.
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                        doc.getElementById(wId).closeWin();
                    };
                    var cnt = w.addEle1(text, 'display:block;border:1px solid #aaa;padding-bottom:3px;padding-left:3px;background-color:#fafcfe;color:#000;font:16px Times New Roman;width:310px;height:160px;overflow:auto;cursor:text;-moz-user-focus:normal;-moz-user-select:text;');
                    cnt.contentEditable="true";
                    cnt.context="contentAreaContextMenu";
                    w.addEle(status, 'display:table;font:12px Times New Roman;font-weight:bold;color:blue;width:auto;height:auto;padding-top:2px;margin:0 3px;cursor:pointer;');
                    w.addEventListener('mousedown', function(e) {
                        if (e.target == w) {
                            e.preventDefault();
                            var grabX = e.clientX, grabY = e.clientY, origX = parseInt(w.style.left), origY = parseInt(w.style.top);
                            var mouseMove = function(ev) {
                                w.style.left = origX+ev.clientX-grabX+'px';
                                w.style.top = origY+ev.clientY-grabY+'px';
                            };
                            doc.addEventListener('mousemove', mouseMove, false);
                            doc.addEventListener('mouseup', function() {doc.removeEventListener('mousemove', mouseMove, false);}, false);
                        }
                    }, false);
                    doc.documentElement.appendChild(w);

                    if (size) {
                        cnt.style.height = size.height;
                        cnt.style.width = size.width;
                    } else {
                        for (var i = 3; i < 10; i++) {
                            if (cnt.scrollHeight > cnt.offsetHeight || cnt.scrollWidth > cnt.offsetWidth) {
                                cnt.style.height = 80*i+'px';
                                cnt.style.width = 160*i+'px';
                            } else
                                break;
                        }
                    }

                    var docEle = (doc.compatMode == 'CSS1Compat' && win.postMessage) ? doc.documentElement : doc.body;
                    var mX = docEle.clientWidth-w.offsetWidth, mY = docEle.clientHeight-w.offsetHeight;
                    if (mX < 0) {cnt.style.width = parseInt(cnt.style.width)+mX+'px'; mX = 0;}
                    if (mY < 0) {cnt.style.height = parseInt(cnt.style.height)+mY+'px'; mY =0;}
                    var hW = parseInt(w.offsetWidth/2);
                    w.style.left = (pos && pos.X < mX+hW ? (pos.X > hW ? pos.X-hW : 0) : mX)+'px';
                    w.style.top = (pos && pos.Y+10 < mY ? pos.Y+10 : mY)+'px';
                    w.style.visibility = 'visible';
                    doc.addEventListener('keydown', keyDown, false);
                    gBrowser.tabpanels.addEventListener('mousedown', mouseDown, false);
                    return w;
                };

                var getHash = function (txt) {
                    TKK=eval('((function(){var a\x3d817046147;var b\x3d-335196159;return 410049+\x27.\x27+(a+b)})())');
                    function sM(a) {
                        var b;
                        if (null !== yr)
                            b = yr;
                        else {
                            b = wr(String.fromCharCode(84));
                            var c = wr(String.fromCharCode(75));
                            b = [b(), b()];
                            b[1] = c();
                            b = (yr = window[b.join(c())] || "") || "";
                        }
                        var d = wr(String.fromCharCode(116)), c = wr(String.fromCharCode(107)), d = [d(), d()];
                        d[1] = c();
                        c = "&" + d.join("") + "=";
                        d = b.split(".");
                        b = Number(d[0]) || 0;
                        for (var e = [], f = 0, g = 0; g < a.length; g++) {
                            var l = a.charCodeAt(g);
                            128 > l ? e[f++] = l : (2048 > l ? e[f++] = l >> 6 | 192 : (55296 == (l & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (l = 65536 + ((l & 1023) << 10) + (a.charCodeAt(++g) & 1023),
                            e[f++] = l >> 18 | 240,
                            e[f++] = l >> 12 & 63 | 128) : e[f++] = l >> 12 | 224,
                            e[f++] = l >> 6 & 63 | 128),
                            e[f++] = l & 63 | 128);
                        }
                        a = b;
                        for (f = 0; f < e.length; f++)
                            a += e[f],
                        a = xr(a, "+-a^+6");
                        a = xr(a, "+-3^+b+-f");
                        a ^= Number(d[1]) || 0;
                        0 > a && (a = (a & 2147483647) + 2147483648);
                        a %= 1E6;
                        return c + (a.toString() + "." + (a ^ b));
                    }

                    var yr = null;
                    var wr = function(a) {
                        return function() {
                            return a;
                        };
                    }, xr = function(a, b) {
                        for (var c = 0; c < b.length - 2; c += 3) {
                            var d = b.charAt(c + 2), d = "a" <= d ? d.charCodeAt(0) - 87 : Number(d), d = "+" == b.charAt(c + 1) ? a >>> d : a << d;
                            a = "+" == b.charAt(c) ? a + d & 4294967295 : a ^ d;
                        }
                        return a;
                    };
                    return sM(txt);
                };

                var ujs_google_translate = function (dir) {
                    var lng = window.navigator.language.slice(0, 2), txt = gContextMenu.selectionInfo.fullText, l = dir.split('|');
                    var encTxt = encodeURIComponent(txt);
                    var winWait = function(lng) {createWindow('', (lng == 'ru' ? 'Подождите идет перевод' : 'Wait, is going Translating')+'\u2026', 'Google Translate', '_gt', window.navigator.lastClick);};
                    if (txt) {
                        winWait(lng);
                        var xhr = new XMLHttpRequest();
                        var url = 'https://translate.google.com/translate_a/single?client=gtx&sl=' + l[0] + '&tl=' + l[1] + '&hl=' + lng + '&eotf=0&dt=at&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t' + getHash(txt);
                        var urlt = "http://translate.google.com/translate_t?text="+encTxt+"&sl='  + langFrom_google_text + '&tl=' + langTo_google_text +'&hl=' + lng + '&eotf=0&ujs=gtt";
                        xhr.open('POST', url, true);
                        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
                        xhr.onreadystatechange = function() {
                            try {
                                if (xhr.readyState == 4 && xhr.status == 200) {
                                    var result = '', status = '', tmp = JSON.parse(xhr.responseText.replace(/\[(?=,)/g, '[0').replace(/,(?=,|\])/g, ',0').replace(/\\n/g, "<br />"));
                                    for (var i = 0, n; n = tmp[0][i]; i++) {
                                        if (n[0])result += n[0].toString();
                                    };
                                   status = tmp[8][0][0].toUpperCase() + ' -\u203A ' + l[1].toUpperCase();
                                   createWindow(result, status, '<a href="'+urlt.replace(/&/g,'&amp;')+'" target="_blank" style="display:inline;padding:0;margin:0;text-decoration:none;border:none;color:#009;font:16px Times New Roman;">Google Translate</a>', '_gt', window.navigator.lastClick);
                                }
                            } catch(e) {};
                        };
                        xhr.send('q=' + encodeURIComponent(txt));
                    } else {
                        var urlt = gBrowser.currentURI.spec;
                        var url = "http://translate.google.com/translate?u="+encodeURIComponent(urlt)+'&tl='+l[1]+"&hl="+lng+"&langpair="+dir+"&tbb=1";
                        var ctabpos = gBrowser.selectedTab._tPos +1;
                        gBrowser.moveTabTo(gBrowser.selectedTab = gBrowser.addWebTab(url), ctabpos);
                    };
                };
                var contextMenu = document.getElementById("contentAreaContextMenu");
                var nextEleMenu = document.getElementById("context-inspect");

                var menuItem = document.createXULElement("menuitem");
                menuItem.setAttribute("id", "context-ru-google-translate");
                menuItem.setAttribute("label", "Перевести на русский");
                menuItem.setAttribute("class", "menuitem-iconic");
                menuItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABBUlEQVR4Ac2RMU4DMRREB2MqihQpAkUaqnAduuQw4Sx03CGcJYIiDYqQ0lCsZ77535ZFcgAkRuPvV7zd/dLiz3N1PFb8hmRckrfELZrF9ONQ1B6Yz0MyM7S0O6zGkVprh/3+Kw/JzkHSpRpJKZkpr9fYbOx0cjVsCbWG31oHBM9mtt0q73ZcrdLhAMlYQKkUI22aqsNo8HKZAMQhvVaKSHWj2Q2aPU3mJQFY7nuHyvYFduP83WF3AJRfcPNYnr/Lp1G1uK4m9sno1LaUbnX/htf8BNzoneUD5NhjvLCMhURQSQ93QCZwXYjFwg3I0NZKrvoknQPMIHkt/jRAQKMeG2yX89/mB4EJbKbZxIhFAAAAAElFTkSuQmCC");
                menuItem.addEventListener("command", function() {ujs_google_translate('auto|ru');}, false);
                contextMenu.insertBefore(menuItem, nextEleMenu);
				
                contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);
            }
        }).init(this);

Отредактировано egorsemenov06 (12-07-2023 12:06:43)

Отсутствует

 

№96412-07-2023 02:31:44

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

b0ttle пишет

Тут тултипы работают в hookClicks, но при запуске firefox почему-то пишет, что файл не запущен, хотя по виду все работает.

на FF114 всё работало. На 115 перехват кликов не работает, базовый hookClicks выдаёт много ошибок в консоли.


Dumby - проверьте на Firefox 115 ваш базовый hookClicks, на его основе я делал все доработки для профиля в шапке.

Отсутствует

 

№96512-07-2023 03:18:40

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

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

AppMenuTbbSaveHTMLChild.jsm

Выделить код

Код:

/* для UCF CustomStylesScripts.jsm
	scriptsbackground: [ // В фоне [System Principal]
		{ func: jsmImport("AppMenuTbbSaveHTMLChild.jsm"), }, */

var self, name = "AppMenuTbbSaveHTML", EXPORTED_SYMBOLS = [name + "Child"];
var {io, focus, obs} = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

class AppMenuTbbSaveHTMLChild extends JSWindowActorChild {
	receiveMessage() {
		return htmlAndName(this.contentWindow);
	}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {moduleURI: __URI__},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
	},
	observe(win) {
		win.document.getElementById("appMenu-popup")
			.addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.setAttribute("label", "Страница | выбранное в единый HTML");
		var before = "appMenu-save-file-button2", subviewbutton = "subviewbutton";
		if ( parseInt(popup.ownerGlobal.Services.appinfo.version) < 89 ) {
			subviewbutton = "subviewbutton subviewbutton-iconic", before = "appMenu-print-button";
			btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAAAAAClZ7nPAAAAAXRSTlMAQObYZgAAACNJREFUCNdjYH/AgBX9/89Q/4/B/g+D/A8G/g8gEeYDDIwNAIB7EDCcKCcMAAAAAElFTkSuQmCC");
		}
		btn.className = subviewbutton;
		btn.setAttribute("oncommand", "saveHTML();");
		btn.saveHTML = this.saveHTML;
		popup.querySelector('toolbarbutton[id^="'+ before +'"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup").removeEventListener("popupshowing", this);
	},
	async saveHTML() {
		var win = this.ownerGlobal;
		var br = win.gBrowser.selectedBrowser;
		var bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;

		var actor = bc?.currentWindowGlobal?.getActor(name);
		actor && self.save(win, ...await actor.sendQuery(""));
	},
	async save(win, fileContent, fileName) {
		var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
		fp.init(win, "", fp.modeSave);
		fp.defaultString = fileName;
		fp.appendFilters(fp.filterHTML);
		fp.appendFilters(fp.filterAll);
		var res = await new Promise(fp.open);
		if (res == fp.returnOK || res == fp.returnReplace)
			this.write(fp.file.path, fileContent);
	},
	write(path, html) {
		if (typeof IOUtils == "object")
			var write = IOUtils.writeUTF8 || IOUtils.writeAtomicUTF8; // Fx 85+ || 82-84
		if (!write) { // Fx 79-81
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			write = (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt));
		}
		(this.write = write)(path, html);
	}
}).init("browser-delayed-startup-finished");

var htmlAndName = async mainWin => {

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

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

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

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

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, '');
	fileName += (function () {
		var d = new Date(), z = function(n){return '_' + (n < 10 ? '0' : '') + n};
		return z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds());
	})();
	if(!/\.html?$/.test(fileName))fileName += '.html';

	return [doctype + sel.innerHTML + '\n<!-- This document saved from ' + (loc.protocol != 'data:' ? loc.href : 'data:uri') + ' -->', fileName];
}

Этот скрипт спрашивает, куда сохранять страницу в единый HTML. Второй подобный код ты делал для скрипта ucf_hookClicks.js – сохранять страницу без запроса (я его доработал для сохранения в папку с имёнем сайта и прочими условиями).


Просьба такая - сделать единый скрипт, который работает в зависимости от переданных опций, то есть либо сохраняет в единый HTML с запросом "Куда?", либо сохраняет сразу без запроса в "Загрузки".
То есть, чтобы скрипт можно было вызывать из разных кнопок (например, по перехвату кликов из ucf_hookClicks) с разными опциями – например "Спросить", куда сохранять или "Не спрашивать".

Отсутствует

 

№96612-07-2023 11:11:18

Northtech
Участник
 
Группа: Members
Зарегистрирован: 16-04-2011
Сообщений: 261
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Не знаю в какую тему написать... В FX115 отвалилась кнопка восстановления вкладок после закрытия (не показывает список закрытых вкладок), вроде она относится к расширению add_toolbar_buttons@vitaliy.ru. Можно как-то ее починить?

https://dropmefiles.com/XaeKh - расширение


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

Отсутствует

 

№96712-07-2023 11:33:30

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Northtech
https://forum.mozilla-russia.org/viewtopic.php?pid=804061#p804061


Add, надеюсь оно, я просто эту кнопку не использую.

Отредактировано kokoss (12-07-2023 11:38:06)


Win7

Отсутствует

 

№96812-07-2023 11:51:37

Northtech
Участник
 
Группа: Members
Зарегистрирован: 16-04-2011
Сообщений: 261
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

kokoss
нет, к сожалению не то. Эти изменения я вносил уже...

Отсутствует

 

№96912-07-2023 12:51:54

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

egorsemenov06 пишет

Возможно ли запускать перевод горячими клавишами (двойнм Ctrl) в этом скрипте?

Это как-то неудобно. Скрипт завязан на контекстное меню,
и берёт выделенный текст из его машинерии. А если клавишей, то взять негде.


Разве что через буфер обмена. Ну попробуй дописать после строки
contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);

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

Выделить код

Код:

//
                var translate = async () => {
                    var br = gBrowser.selectedBrowser;
                    var fw = Services.focus.focusedWindow;
                    if (fw == window) {
                        if (document.activeElement != br) return;
                    }
                    else if (fw.browsingContext.top != br.browsingContext) return;

                    var cb = navigator.clipboard;
                    var was = await cb.readText();
                    if (was) await cb.writeText("");

                    docShell.doCommand("cmd_copy");
                    await new Promise(r => setTimeout(r, 100));
                    var txt = await cb.readText();

                    if (txt || was) cb.writeText(was);
                    if (!txt && !br.currentURI.scheme.startsWith("http")) return;
                    window.gContextMenu = {selectionInfo: {
                        get fullText() {
                            window.gContextMenu = null;
                            return txt;
                        }
                    }};
                    ujs_google_translate("auto|ru");
                }
                var ts = 0, destr = this.destructor, args = ["keyup", e =>
                    e.key == "Control" && ts - (ts = Cu.now()) > -300
                    && !e.shiftKey && !e.altKey && translate(ts = 0)
                ];
                addEventListener(...args);
                this.destructor = () => destr(removeEventListener(...args));

Dobrov пишет

ваш базовый hookClicks

Нет у меня никакого hookClicks, не сочиняй.
Мы там что-то когда-то обсуждали, и ты его родил.

проверьте на Firefox 115

Ну я убрал кусок кода if (typeof IOUtils != "object") {…};
и, на первый взгляд, вроде нормализовалось.
Так, потыкал немного, там же невозможно всё проверить,
нужно что-то поконкретнее.


В этом куске всё тот же косяк с непониманием работы var.
Вчера вот Services.jsm удалили, так у меня всё рухнуло в основном из-за этого.
Вобщем, тоже удали, или перепиши кусок правильно.


Да, кстати, потерялся var перед str_cut = …
Без него, в окно добавятся ненужные там
str_cut, url_color, switchToTab, showInStatusPanel, Title,
saveSelectionToTxt, save, bright, help, GetSelection, data,


и gClipboard конечно, который может перезаписать
тот, что от Custom Buttons, а это уже совсем нехорошо.

чтобы скрипт можно было вызывать

Скрипт нельзя «вызвать», только загрузить. Вызвать можно функцию.


Это надо что-то в SystemGlobal добавить, чтобы с других мест вызывать.
Попробую расписать, чисто для примера. Следи за мыслью

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

Сначала в init() дописываем
globalThis[Symbol.for(name)] = this.saveHTML;


Затем, из saveHTML() убираем var win = this.ownerGlobal;
а в саму функцию добавляем наш аргумент, и вторым аргументом окно
async saveHTML(arg, win = this.ownerGlobal) {


и, в последней строке функции, наш аргумент пробрасываем
actor && self.save(win, ...await actor.sendQuery(""), arg);


Далее вписываем его и в save()
async save(win, fileContent, fileName, arg) {


Таким образом, внутри save() теперь можно его использовать
if (arg) {
    win.alert(arg); // test
    // Здесь делаем что-то одно, типа вычисляем путь для файла
} else {
    // Здесь делаем что-то другое, типа открываем файл-пикер и забираем путь с него
}
// Здесь записываем файл


Всё. Теперь можно вызывать снаружи, например, с консоли браузера:

(() => {
    var save = Cu.getGlobalForObject(Cu)[Symbol.for("AppMenuTbbSaveHTML")];
    save("TesT", window);
})();

Northtech пишет

dropmefiles.com

javascript:void(0); — нет уж, разрешите отказаться.


Но наверняка дело в getClosedTabCount
Можно заменить в parent.js все два на getClosedTabCountForWindow


Если нужна обратная совместимость, то два
sessionStore.getClosedTabCount
заменить на
(sessionStore.getClosedTabCountForWindow || sessionStore.getClosedTabCount)

Отсутствует

 

№97012-07-2023 13:05:53

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 12-06-2018
Сообщений: 405
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

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

Возможно ли запускать перевод горячими клавишами (двойнм Ctrl) в этом скрипте?

Это как-то неудобно. Скрипт завязан на контекстное меню,
и берёт выделенный текст из его машинерии. А если клавишей, то взять негде.


Разве что через буфер обмена. Ну попробуй дописать после строки
contextMenu.insertBefore(document.createXULElement("menuseparator"), nextEleMenu);

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

Выделить код

Код:

//
                var translate = async () => {
                    var br = gBrowser.selectedBrowser;
                    var fw = Services.focus.focusedWindow;
                    if (fw == window) {
                        if (document.activeElement != br) return;
                    }
                    else if (fw.browsingContext.top != br.browsingContext) return;

                    var cb = navigator.clipboard;
                    var was = await cb.readText();
                    if (was) await cb.writeText("");

                    docShell.doCommand("cmd_copy");
                    await new Promise(r => setTimeout(r, 100));
                    var txt = await cb.readText();

                    if (txt || was) cb.writeText(was);
                    if (!txt && !br.currentURI.scheme.startsWith("http")) return;
                    window.gContextMenu = {selectionInfo: {
                        get fullText() {
                            window.gContextMenu = null;
                            return txt;
                        }
                    }};
                    ujs_google_translate("auto|ru");
                }
                var ts = 0, destr = this.destructor, args = ["keyup", e =>
                    e.key == "Control" && ts - (ts = Cu.now()) > -300
                    && !e.shiftKey && !e.altKey && translate(ts = 0)
                ];
                addEventListener(...args);
                this.destructor = () => destr(removeEventListener(...args));

Большое Спасибо!!!!то что доктор прописал!

Отсутствует

 

№97112-07-2023 13:18:45

Northtech
Участник
 
Группа: Members
Зарегистрирован: 16-04-2011
Сообщений: 261
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby

javascript:void(0); — нет уж, разрешите отказаться.

Да я этого и не видел никогда, у меня ublock. :)

getClosedTabCountForWindow

сработало, спасибо.

Отсутствует

 

№97212-07-2023 14:06:35

6e73epo
Участник
 
Группа: Members
Зарегистрирован: 06-05-2022
Сообщений: 193
UA: Firefox 116.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

В Firefox 117 не открывается старый about:config. Достаточно ли закомментировать строку с импортом Services.jsm?

Отсутствует

 

№97315-07-2023 11:25:05

odd74RUS
Участник
 
Группа: Members
Зарегистрирован: 15-08-2021
Сообщений: 26
UA: Firefox 115.0

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

После какого-то из недавних обновлений перестал работать скрипт (contextsearch.uc.js), вот он: https://forum.mozilla-russia.org/viewto … 83#p780283 , переключающий поисковые системы в контекстном меню выделенного текста.
В данный момент стоит [firefox] 115.02 (Windows 7).
В скриптах я, к сожалению, совсем не разбираюсь. Подскажите пожалуйста, как теперь его снова заставить работать?

Отсутствует

 

№97415-07-2023 12:07:57

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby пишет

Попробую расписать, чисто для примера. Следи за мыслью…

Спасибо за подсказки! Доработал скрипт AppMenuTbbSaveHTMLChild, проверь, может я накосячил где-то…
1) теперь запускается так: Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](true, window);
2) можно сохранять страницу автоматически или с запросом "Куда"
3) может сохранять в разные папки в зависимости от опции ucf.savedirs: Загрузки[домен]имя вкладки
или указать любой путь сохранения, например: Загрузки/Web/домен/имя …for("SingleHTML")]("Web|1", window)

SingleHTMLChild.jsm

Выделить код

Код:

/* SingleHtml by Лекс, правка: Dumby, mod Dobrov
для UCF CustomStylesScripts.jsm
	scriptsbackground: [ // В фоне [System Principal]
		{ func: jsmImport("SingleHTMLChild.jsm"), },
вызов: Cu.getGlobalForObject(Cu)[Symbol.for("SingleHTML")](arg, window)
	если arg False, то диалог выбора пути сохранения */

var self, name = "SingleHTML", EXPORTED_SYMBOLS = [name + "Child"];
var {io, focus, obs, prefs, dirsvc} = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

class SingleHTMLChild extends JSWindowActorChild { //класс = name + Child
	receiveMessage() { return htmlAndName(this.contentWindow);}
}
ChromeUtils.domProcessChild.childID || ({
	init(topic) {
		ChromeUtils.registerWindowActor(name, {
			allFrames: true,
			child: {moduleURI: __URI__},
			messageManagerGroups: ["browsers"]
		});
		obs.addObserver(self = this, topic);
		obs.addObserver(function quit(s, t) {
			obs.removeObserver(quit, t);
			obs.removeObserver(self, topic);
		}, "quit-application-granted");
		this.handleEvent = e => this[e.type](e);
		globalThis[Symbol.for(name)] = this.saveHTML;
	},
	observe(win) {
		win.document.getElementById("appMenu-popup")
			.addEventListener("popupshowing", this);
		win.addEventListener("unload", this);
	},
	popupshowing(e) {
		this.unload(e);
		var popup = e.target;
		var btn = popup.ownerDocument.createXULElement("toolbarbutton");
		btn.id = "appMenu-ucf-save-html-button";
		btn.setAttribute("label", "Страница | выбранное в единый HTML");
		var before = "appMenu-save-file-button2", subviewbutton = "subviewbutton";
		btn.className = subviewbutton;
		btn.setAttribute("oncommand", "saveHTML();");
		btn.saveHTML = this.saveHTML;
		popup.querySelector('toolbarbutton[id^="'+ before +'"]').before(btn);
	},
	unload(e) {
		var win = e.target.ownerGlobal;
		win.removeEventListener("unload", this);
		win.document.getElementById("appMenu-popup").removeEventListener("popupshowing", this);
	},
	async saveHTML(arg, win = this.ownerGlobal) {
		var br = win.gBrowser.selectedBrowser;
		var bc = focus.focusedContentBrowsingContext;
		if (bc?.top.embedderElement != br) bc = br.browsingContext;
		var actor = bc?.currentWindowGlobal?.getActor(name);
		actor && self.save(win, ...await actor.sendQuery(""), arg); // htmlAndName
	},
	async save(win, data, fname, host, arg, d = prefs.getStringPref("ucf.savedirs","_Web||_Pic|0")) {
		if (/.*\|/.test(arg)) d = arg; //Dir/Subdir|[пусто|0 title|1 host]
		if (!/.*\|/.test(d)) d += '|'; d = d.split('|').slice(0,2); //Загрузки[домен]имя
		fname = fname.replace(/\s+/g,' ').replace(/[\\\/?*\"'`]+/g,'').replace(/[|<>]+/g,'_').replace(/:/g,'։').slice(0,100).trim();
		d[1] = (d[1] == "0") ? fname.slice(0,48).trim() : (d[1] == "1") ? host.replace(/\/.*/,'') : "";
		try {var dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {var dir = dirsvc.get("DfltDwnld", Ci.nsIFile);}
		d.forEach(dir.append); dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777);
		fname += (function() {var d = new Date(), z = function(n){return '։' + (n < 10 ? '0' : '') + n}; return '_'+ d.getHours() + z(d.getMinutes()) + z(d.getSeconds());})();
		dir.append(fname +'.html'); var path = dir.path; //назначить путь сохранения

		if (!arg) { // диалог выбора папки
			var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
			fp.init(win, "", fp.modeSave);
			fp.defaultString = path.split(/.*[\/|\\]/)[1];
			fp.appendFilters(fp.filterHTML); fp.appendFilters(fp.filterAll);
			var res = await new Promise(fp.open);
			if (res == fp.returnOK || res == fp.returnReplace)
				path = fp.file.path
			else return;
		}
		this.write(path, data);
		d = await win.Downloads.createDownload({source: "about:blank",target: win.FileUtils.File(path)}); (await win.Downloads.getList(win.Downloads.ALL)).add(d); await d.refresh(d.succeeded = true); //flash DWButton
	},
	write(path, html) {
		if (typeof IOUtils == "object")
			var write = IOUtils.writeUTF8 || IOUtils.writeAtomicUTF8; // Fx 85+ || 82-84
		if (!write) { // Fx 79-81
			var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
			write = (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt));
		}
		(this.write = write)(path, html);
	}
}).init("browser-delayed-startup-finished");

var htmlAndName = async mainWin => {

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

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

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

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

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

	var doctype = '', dt = doc.doctype;
	if (dt && dt.name) {
		doctype += '<!DOCTYPE ' + dt.name;
		if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
		if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
		doctype += '>\n';
	};
	var onlyName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
	return [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', onlyName, loc.hostname];
}

Отредактировано Dobrov (15-07-2023 18:36:11)

Отсутствует

 

№97516-07-2023 05:43:08

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

Re: UCF - ваши кнопки, темы, дополнения, скрипты…

Dumby - ещё нужна помощь!
хочу добавить в SystemGlobal вторую функцию, в ней нужны адрес и имя текущей вкладки.
gURLBar и gBrowser не работают, а win.location.hostname и doc.getElementsByTagName('title')[0] работают только в var htmlAndName = async mainWin => {
не знаю, как получить адрес и имя текущей вкладки без receiveMessage()


как в JSM-ке SingleHTMLChild получить адрес и имя текущей вкладки ?

Отсутствует

 

Board footer

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