Dumby - Спасибо! Ещё проблема: при возвращении из режима "Адаптивный дизайн" в обычный просмотр многие страницы остаются обрезанными, нужно F5 жать.
Как при возвращении в обычный просмотр ещё и обновить страницу? (но не обновлять её для Адаптивного дизайна)
Dumby - а как обновить подсказку для "Reader View" в панели адреса?
У меня только грубый вариант: зарегистрировать mouseenter и постоянно делать: ReaderView.tooltipText = подсказка……
var box = document.getElementById("page-action-buttons"); // кнопки панели адреса var boxLst = e => { if (e.button == 1 && e.target.id == "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_") // Reader View e.stopImmediatePropagation(), document.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн } box.addEventListener("auxclick", boxLst, true); addDestructor(() => box.removeEventListener("auxclick", boxLst, true));
Dumby - Вопрос: в custom_script.js функция loadscript не грузит одну .JSM-ку, для которой нужна отдельная строка запуска :
ChromeUtils.import(`${scripts}/UCFTitleChangedChild.jsm`, {}).registerUCFTitleChanged(); // исправление заголовка вкладки
var EXPORTED_SYMBOLS = ["registerUCFTitleChanged", "UCFTitleChangedChild"]; function registerUCFTitleChanged() { // исправление заголовка вкладки …………
Попробуй исправить loadscript, чтобы он был более универсальный, а второй параметр мог быть именем выполняемой функции:
loadscript("UCFTitleChangedChild.jsm", registerUCFTitleChanged()); // так не работает!
const scripts = 'chrome://user_chrome_files/content/custom_scripts'; (async () => { // загрузка внешних js или jsm-скриптов var loadscript = (name, function_register) => { try { name.split('.').pop().split("?")[0].split("#")[0].toLowerCase() == "jsm" ? ChromeUtils.import(`${scripts}/${name}`, {}).function_register : Services.scriptloader.loadSubScript(`${scripts}/${name}`,globalThis,"UTF-8"); return true; } catch(e) {} }; loadscript("ucf_eom-button.js"); // нижеследующая строка не работает: // loadscript("UCFTitleChangedChild.jsm", registerUCFTitleChanged()); })();
Отредактировано Dobrov (14-11-2021 04:37:50)
Отсутствует
но не обновлять её для Адаптивного дизайна
Можно проверять gBrowser.selectedBrowser.browsingContext.inRDMPane
как обновить подсказку для "Reader View" в панели адреса?
Если в смысле установить свою как для аддона, то вот вариант (в custom_script.js).
(async url => { var tooltip = "Test Tooltip"; var m = "2495d258-41e7-4cd5-bc7d-ac15981f064e", id = `{${m}}`, aid = `_${m}_`; var manager = ChromeUtils.import(url).ExtensionParent.apiManager; var wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady); var onReady = (e, addon) => { if (addon.id != id) return; manager.off("ready", onReady); addon.once("shutdown", wait); manager.global.PageActions.actionForID(aid).setTooltip(tooltip); } manager.on("ready", onReady); })("resource://gre/modules/ExtensionParent.jsm");
исправить loadscript
Что-то мне не слишком понятны код и задача, может так сойдёт?
(async scripts => { var re = /\.jsm$/i; var loadscript = name => { try { var {href, pathname} = new URL(scripts + name); if (re.test(pathname)) return ChromeUtils.import(href); Services.scriptloader.loadSubScript(href); return true; } catch(ex) {Cu.reportError(ex);} } loadscript("ucf_eom-button.js"); loadscript("UCFTitleChangedChild.jsm")?.registerUCFTitleChanged?.(); })("chrome://user_chrome_files/content/custom_scripts/");
(async scripts => { var re = /\.jsm$/i; var loadscript = (name, funcName) => { try { var {href, pathname} = new URL(scripts + name); if (re.test(pathname)) { var obj = ChromeUtils.import(href); funcName && obj[funcName](); } else Services.scriptloader.loadSubScript(href); return true; } catch(ex) {Cu.reportError(ex);} } loadscript("ucf_eom-button.js"); loadscript("UCFTitleChangedChild.jsm", "registerUCFTitleChanged"); })("chrome://user_chrome_files/content/custom_scripts/");
Отредактировано Dumby (14-11-2021 21:07:11)
Отсутствует
Dumby - Спасибо! обновление подсказки кнопки также работает из custom_script_win.js.
Dumby - проверь мой новый загрузчик: (сократил, чтобы не повторять строки с loadscript)
Переделал через список массива js-jsm скриптов. Путь к скриптам используется ещё для подключения [CB]-кодов, поэтому константа.
const scripts = 'chrome://user_chrome_files/content/custom_scripts/'; (async () => { // ваши скрипты [['ucf_QuickToggle.js'], ['UCFTitleChangedChild.jsm', 'registerUCFTitleChanged'], ['Test.jsm']] .forEach(function(name) { try { if (/\.jsm$/i.test(name[0])) { // [скрипт js или jsm, инициализация] var obj = ChromeUtils.import(scripts + name[0]); name[1] && obj[name[1]](); } else Services.scriptloader.loadSubScript(scripts + name[0]); } catch(ex) {Cu.reportError(ex);} }); })();
Отредактировано Dobrov (16-11-2021 02:10:34)
Отсутствует
проверь мой новый загрузчик: (сократил, чтобы не повторять строки с loadscript)
Ну, выглядит нормально.
Но, замысел целиком мне же неизвестен.
Вот зачем тогда функция что-то возвращает, раз это не используется.
Или, в исходнике, name.split('.').pop().split("?")[0].split("#")[0].toLowerCase()
наводило на мысль, что будут присутствовать имена типа "SomeModule.JsM?q=lol#bla",
но ничего подобного пока не видно, хотя, может потом добавятся, а если нет, то зря new URL() создаётся.
Короче — ничего серьёзного.
Отсутствует
Dumby - вопрос по коду обновления ToolTip кнопки расширения.
Почему-то подсказка для Video DownloadHelper не обновляется! И как переделать код для замены Tooltip на нескольких кнопках расширений?
var view_id = "2495d258-41e7-4cd5-bc7d-ac15981f064e"; // Reader View var vdh_id = "b9db16a4-6edc-47ec-a1f4-b86292ed211d"; // Video DownloadHelper var manager = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm").ExtensionParent.apiManager, wait = (e, isAppShutdown) => isAppShutdown || manager.on("ready", onReady), onReady = (e, addon) => { // if (addon.id != `{${view_id}}`) return; if (addon.id == `{${view_id}}`) { manager.off("ready", onReady), addon.once("shutdown", wait); manager.global.PageActions.actionForID(`_${view_id}_`).setTooltip(`Reader View ${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n\nКлик мыши Режим для чтения\nКолёсико Адаптивный дизайн`); // изменить подсказку } if (addon.id == `{${vdh_id}}`) { manager.off("ready", onReady), addon.once("shutdown", wait); manager.global.PageActions.actionForID(`_${vdh_id}_`).setTooltip(`Video DownloadHelper\nСкачивание проигрываемого видео`); } }; manager.on("ready", onReady);
Отсутствует
Video DownloadHelper
Совсем разные вещи. У RV pageAction, а у VDH browserAction.
(async url => { // Reader View var rv = "2495d258-41e7-4cd5-bc7d-ac15981f064e"; var rv_id = `{${rv}}`, rv_aid = `_${rv}_`; var rv_ttt = `Reader View ${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n\nКлик мыши Режим для чтения\nКолёсико Адаптивный дизайн`; // Video DownloadHelper var vdh_id = "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}"; var vdh_ttt = "Video DownloadHelper\nСкачивание проигрываемого видео"; var count = 0; var manager = ChromeUtils.import(url).ExtensionParent.apiManager; var wait = (e, isAppShutdown) => isAppShutdown || !--count || manager.on("ready", onReady); var onReady = (e, addon) => { if (addon.id == rv_id) manager.global.PageActions.actionForID(rv_aid).setTooltip(rv_ttt); else if (addon.id == vdh_id) setVDHTooltip(addon); else return; ++count == 2 && manager.off("ready", onReady); addon.once("shutdown", wait); } manager.on("ready", onReady); var setVDHTooltip = addon => { var vdh_wid = `_${vdh_id.slice(1, -1)}_-browser-action`; var {gPalette} = Cu.import("resource:///modules/CustomizableUI.jsm", {}); var upd = manager.global.browserAction.prototype.updateButton; var asgn = eval(`({${upd}})`.replace(/\n^.+"tooltiptext".+$/m, "")); (setVDHTooltip = addon => { var widget = gPalette.get(vdh_wid); widget.tooltiptext = vdh_ttt; var {action} = manager.global.browserActionFor(addon); Object.assign(action.buttonDelegate, asgn); for(var [, node] of widget.instances) node.setAttribute("tooltiptext", vdh_ttt); })(addon); } })("resource://gre/modules/ExtensionParent.jsm");
Отсутствует
Dumby - ты делал перехват кликов для кнопок в панели адреса в скрипте ucf_hookClicks.js.
Получилась обработка кликов двумя дублирующими способами: первый до ucf.unloadlisteners, затем для кнопок на панели адреса.
Второй способ перехватывает клики всех кнопок page-action-buttons, а при обработке проверяется id кнопки.
Возможно ли доработать код, чтобы сразу перехватывать клики кнопок "nav-bar-customization-target" основной панели и "page-action-buttons" панели адреса ?
Выгода этого способа в том, что проще в одном скрипте прописать дополнительные клики нужных кнопок, а не делать кучу скриптов, где каждая кнопка обрабатывается персонально. Также прошу по возможности добавить действие на долгое нажатие кнопки, так как у меня перестал обрабатываться долгий клик в скрипте ToggleAboutConfig, когда я добавил addEventListener("mouseenter" для nav-bar-customization-target в скрипт ucf_hookClicks.
Ещё хотелка - добавить перехват "wheel". Ожидаемый итог работы кода: перехват событий кнопок для двух панелей, разбор такой же, как в твоём коде перехвата кликов: 512: saveSelectionToTxt, // СКМ Click (сохранить .txt) цифра содержит сумму событий: id кнопки, клавиш мыши, мета-клавиш, тип кликов, скролл над кнопками тулбара…
Удобнее сделать изменение яркости скролом над панелью безопасности "identity-box", чем над Звёздочкой. А скролл над кнопками основной панели определять отдельно для каждой, то есть добавить флаги e.scroll+ и e.scroll- так же, как сделано для dbl (дубль-клик).
(async (id, func) => { // для custom_script_win.js: дополнительные клики и подсказки кнопок await window.delayedStartupPromise; var box = document.getElementById("page-action-buttons"), // кнопки панели адреса nav = document.getElementById("nav-bar-customization-target"), // кнопки панели btn = document.getElementById("downloads-button"), // 0 Загрузки pui = document.getElementById("PanelUI-menu-button"), // 1 меню fav = document.getElementById("star-button"), // 2 prn = document.getElementById("print-button"), // 3 rv = "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_", // 4 Reader View sgs = "_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action", // SingleSave button vdh = "_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action"; /*Video DownloadHelper*/ if (!btn) return; btn_help =` Двойной клик: ⬇︎ открыть [Загрузки] …на картинке: ⧉ найти Похожие\n Правый клик (Alt+S): Сохранить в единый html всё / выделенное …дважды Картинки вкл/выкл\n Ролик: Сохранить как файл .txt Колёсико на рисунке: ➜ Сохранить`, PanelUI_help = `Клик мыши: меню Firefox ${Services.appinfo.platformVersion} …+ Shift ⚑ Краткая справка …+ Alt Персонализация Клик дважды ⊠ закрыть браузер \n Правый клик ⇲ Свернуть …+ дважды ⤾ Вернуть вкладку …+ Alt Диспетчер задач …+ Shift Адаптивный дизайн\n Колёсико: Развернуть | окно …+ Alt Полный экран …+ дважды Обновить без кэша`, sgs_help = `\nAlt⇧S ⌨ нажатие SingleSave`, rv_help = `Reader View ${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n Клик мыши Режим для чтения Колёсико Адаптивный дизайн`; // vdh_help =`Video DownloadHelper\nСкачивание проигрываемого видео`; var addDestructor = nextDestructor => { var {destructor} = ucf[id]; ucf[id].destructor = () => { try {destructor();} catch(ex) {Cu.reportError(ex);} nextDestructor(); } }, showInStatusPanel = (info, time = 5000) => { var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel; if (StatusPanel.update.tid) clearTimeout(StatusPanel.update.tid) else { var {update} = StatusPanel; StatusPanel.update = () => {}; StatusPanel.update.ret = () => { StatusPanel.update = update; StatusPanel.update(); } } StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time); StatusPanel._label = info; }, Title = (max, title) => { // получить заголовок. без обрезки: max не указан, домен: max <0, + дата: max=0 if (!title) var title = document.title || gBrowser.selectedTab.label; if (max == undefined) return title; // заголовок как есть или ограничить длину, убрать служебные символы title = title.replace(/[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); if ( max > 0 ) return title.slice(0, max); if ( max == 0) return title.slice(0, 100) +"_"+ new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։"); var host = decodeURIComponent(gURLBar.value); // max < 0 if (!/^file:\/\//.test(host)) host = host.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,''); return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns'); }, saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt var splice = saveURL.length == 10; var msgName = id + ":Save:GetSelection"; var receiver = msg => { var title = Title(0); var args = [ "data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data), title + '.txt', null, false, true, null, window.document]; splice && args.splice(5, 0, null); saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60)); } messageManager.addMessageListener(msgName, receiver); addDestructor(() => messageManager.removeMessageListener(msgName, receiver)); var func = fm => { var res, fed, win = {}, fe = fm.getFocusedElementForWindow(content, true, win); var sel = (win = win.value).getSelection(); if (sel.isCollapsed) { var ed = fe && fe.editor; if (ed && ed instanceof Ci.nsIEditor) sel = ed.selection, fed = fe; } if (sel.isCollapsed) fed && fed.blur(), docShell.doCommand("cmd_selectAll"), res = win.getSelection().toString(), docShell.doCommand("cmd_selectNone"), fed && fed.focus(); res = res || sel.toString(); /\S/.test(res) && sendAsyncMessage("saveSelectionToTxt", res); } var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));'; (saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, save = async () => { // SingleHtml by Лекс, правка: Dumby, Dobrov var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML"; if (typeof IOUtils != "object") { // Firefox 78 ESR var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); var PathUtils = {join: (...args) => OS.Path.join(...args)}; var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))}; } var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8"; var msgListener = async msg => { var [fileContent, fileName] = msg.data, dir; // fileName: выделенный текст или null try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);} var arr = prefs.getStringPref("ucf_save.dirs","_Web||_Images|0").split('|').slice(0,2); //subdir: title|host arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен arr.forEach(dir.append); // ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/label dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать, если не существует… var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(dir.path); if (!fileName) fileName = Title(100); // убрать служебные символы dir.append(Title(0, fileName) +'.html'); await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60)); var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download (await Downloads.getList(Downloads.ALL)).add(d); d.refresh(d.succeeded = true); // кнопка Загрузки мигает } messageManager.addMessageListener(msgName, msgListener); addDestructor(() => messageManager.removeMessageListener(msgName, msgListener)); var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services'; var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName)); (save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, tid, allowMousedown, listener = { // доп.события для 15 кнопок handleEvent(e) { if (e.detail > 2) return; var btn = e.target; var dbl = e.detail == 2; var num = e.button *512 + e.metaKey *256 + e.ctrlKey *128 + e.shiftKey *64 + e.altKey *32 + dbl *16 + (btn == document.getElementById(rv) && 4) + (btn == prn && 3) + (btn == fav && 2) + (btn == pui && 1); if (!this[num]) { if (e.button == 1) return; if (e.button) { num = "context"; for(var p in this.a) this.a[p] = e[p]; } else num = "mousedown"; } if (dbl) tid &&= clearTimeout(tid), this[num](btn); else tid = setTimeout(this.exec, 300, num, btn, this); }, exec(num, btn, self) { tid = null; self[num](btn); }, mousedown(btn) { allowMousedown = true; btn.dispatchEvent(new MouseEvent("mousedown", {})); allowMousedown = false; }, context(btn) { btn.removeAttribute("context"); btn.dispatchEvent(new MouseEvent("contextmenu", this.a)); btn.toggleAttribute("context"); }, switchToTab(but, url) { // открыть вкладку | закрыть, если открыта for(var tab of but.ownerGlobal.gBrowser.tabs) if ( tab.linkedBrowser.currentURI.spec == url ) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()}); }, a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}, /*** ======= Downloads Clicks ======= ***/ 512: saveSelectionToTxt, // СКМ Click (сохранить .txt) 16() { // Double Left Click - Обзор папки «Загрузки» Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки» }, 1024: save, // ПКМ Click (Single HTML) 1040(btn) { // Double Right Click var pref = "permissions.default.image"; var one = prefs.getIntPref(pref) == 1; prefs.setIntPref(pref, one ? 2 : 1); btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : ""; BrowserReload(); }, /*** ======= PanelUI-menu Clicks ======= 100*pui 32*e.button 8*e.ctrlKey 4*e.shiftKey 2*e.altKey dbl btn ***/ 33() { gCustomizeMode.enter(); // ЛКМ + Alt Персонализация }, 65() { // Shift + ЛКМ var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762']; var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:/// (newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(pui, help_ucf[0]) : this.switchToTab(pui, help_ucf[1]); }, 545: BrowserFullScreen, // Alt + СКМ 513() { // СКМ windowState != STATE_MAXIMIZED ? maximize() : restore(); }, 529: BrowserReloadSkipCache, // СКМ Double 1025() { // ПКМ minimize(); }, 1057(pui) { // Alt + ПКМ this.switchToTab(pui, 'about:performance'); }, 1089(pui) { // Shift + ПКМ pui.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload(); }, 1153(pui) { // Shift + Alt + ПКМ var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging"); (this[138] = target => obj.oncommand({target}))(pui); // запуск пункта меню, у которого нет HotKey }, 17: BrowserTryToCloseWindow, // Double Left Click 1041(pui) { // ПКМ Double Right Click pui.ownerGlobal.undoCloseTab(); }, }, // end Clicks keydown_win = e => { // нажатие клавиш if (e.keyCode == 83 && e.altKey) e.shiftKey // Alt+S [+Shift] ? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID if (e.keyCode == 68 && e.altKey){ // Alt+D отладка - запуск внешнего JS // e.target.ownerDocument.getElementById("key_browserConsole").doCommand(); eval(Cu.readUTF8URI(Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/User.js"))); console.log("END User.js " + Math.random()); } }, {prefs, dirsvc} = Services, linux = /macos|linux/.test(AppConstants.platform), singlesave; prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки var hint_upd = function(btn, text, find) { // обновить подсказку return; } var mouseenter = function(e) { this.parentNode.addEventListener("mousedown", stop, true); this.addEventListener("mouseleave", mouseleave, {once: true}); var sgs_btn = document.getElementById(sgs), dw; try {dw = prefs.getComplexValue("browser.download.dir",Ci.nsIFile)} catch {dw = dirsvc.get("DfltDwnld", Ci.nsIFile)}; var rv_btn = document.getElementById(rv); // Reader View if (e.target == rv_btn) { rv_btn.tooltipText = rv_help; } // var vdh_btn = document.getElementById(vdh); // Video DownloadHelper // if (e.target == vdh_btn) { // vdh_btn.tooltipText = vdh_help; // } if ((e.target == pui) && (!/справка/.test(pui.tooltipText))) pui.tooltipText = PanelUI_help; // обновить подсказки кнопок if (e.target == btn) { // if (!/Двойной/.test(btn.tooltipText)) btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) + btn_help; // обновлять подсказку при наведении мыши if (sgs_btn){ if (!/SingleSave/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + sgs_help; } else btn.tooltipText = btn.tooltipText.replace(sgs_help,''); if (!/выбранная/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + `${dw ? "\n\n[Загрузки] — выбранная папка:\n"+ dw.path.substring(0,33) + `${dw.path.length > 32 ? `…\n…${dw.path.substring(dw.path.length -31, dw.path.length)}`: ""}` : ""}`; // сократить/разбить длинную строку } } var mouseleave = function(e) { this.parentNode.removeEventListener("mousedown", stop, true); } var stop = e => { e.button || allowMousedown || e.stopImmediatePropagation(); } var btns = [btn, pui]; for(var b of btns) { b.toggleAttribute("context"), b.addEventListener("click", listener, true), b.addEventListener("mouseenter", mouseenter); } var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win; ucf[id] = {destructor() { for(var b of btns) { b.removeEventListener("click", listener, true); b.removeEventListener("mouseenter", mouseenter); if (b.matches(":hover")) b.removeEventListener("mouseleave", mouseleave), b.parentNode.removeEventListener("mousedown", stop, true); } }}; ucf.unloadlisteners.push(id); var boxLst = e => { console.log('@: '+ e.button); if (e.button == 1 && e.target.id == `pageAction-urlbar-_${rv}_`) { // Reader View Button e.stopImmediatePropagation(), document.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload(); } } box.addEventListener("auxclick", boxLst, true); box.addEventListener("mouseenter", mouseenter, true); window.addEventListener("keydown", keydown_win); addDestructor(() => { box.removeEventListener("auxclick", boxLst, true); box.removeEventListener("mouseenter", mouseenter, true); window.removeEventListener("keydown", keydown_win); }); })("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику var resolveURL = function (url, base) { try { return io.newURI(url, null, io.newURI(base)).spec;} catch {} }, getSelWin = function (w) { if (w.getSelection().toString()) return w; for (var i = 0, f, r; f = w.frames[i]; i++) { try { if (r = getSelWin(f)) return r;} catch(e) {} } }, encodeImg = function (src, obj) { var canvas, img, ret = src; if (/^https?:\/\//.test(src)) { canvas = doc.createElement('canvas'); if (!obj || obj.nodeName.toLowerCase() != 'img') { img = doc.createElement('img'); img.src = src; } else img = obj; if (img.complete) try{ canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png')); } catch (e) {}; if (img != obj) img.src = 'about:blank'; }; return ret; }, toSrc = function (obj) { var strToSrc = function (str) { var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'}; while (chr = str.charAt(i++)) { ret += meta[chr] || chr; }; return '\x22' + ret + '\x22'; }, arrToSrc = function (arr) { var ret = []; for (var i = 0; i < arr.length; i++) { ret[i] = toSrc(arr[i]) || 'null'; }; return '[' + ret.join(',') + ']'; }, objToSrc = function (obj) { var val, ret = []; for (var prop in obj) { if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val); }; return '{' + ret.join(',') + '}'; }; switch (Object.prototype.toString.call(obj).slice(8, -1)) { case 'Array': return arrToSrc(obj); case 'Boolean': case 'Function': case 'RegExp': return obj.toString(); case 'Date': return 'new Date(' + obj.getTime() + ')'; case 'Math': return 'Math'; case 'Number': return isFinite(obj) ? String(obj) : 'null'; case 'Object': return objToSrc(obj); case 'String': return strToSrc(obj); default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null'; } }, mainWin = {}; focus.getFocusedElementForWindow(content, true, mainWin); mainWin = mainWin.value; var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location; var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g; if (selWin) { var rng = win.getSelection().getRangeAt(0); pEle = rng.commonAncestorContainer; ele = rng.cloneContents(); } else { pEle = doc.documentElement; ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true); }; while (pEle) { if (pEle.nodeType == 1) { clone = pEle.cloneNode(false); clone.appendChild(ele); ele = clone; }; pEle = pEle.parentNode }; var sel = doc.createElement('div'); sel.appendChild(ele); for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) { el = all[i]; if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href); return prev + encodeImg(url) + next; }); switch (el.nodeName.toLowerCase()) { case 'link': case 'style': case 'script': el.parentNode.removeChild(el); break; case 'a': case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break; case 'img': case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break; case 'audio': case 'video': case 'embed': case 'frame': case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break; case 'object': if (el.hasAttribute('data')) el.data = el.data; break; case 'form': if (el.hasAttribute('action')) el.action = el.action; break; } }; var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0]; meta.httpEquiv = 'content-type'; meta.content = 'text/html; charset=utf-8'; head.appendChild(meta); if (title) head.appendChild(title.cloneNode(true)); head.copyScript = function (unsafeWin) { if ('$' in unsafeWin) return; var f = doc.createElement('iframe'); f.src = 'about:blank'; f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;'); doc.documentElement.appendChild(f); var str, script = doc.createElement('script'); script.type = 'text/javascript'; for (var name in unsafeWin) { if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue; try { str = toSrc(unsafeWin[name]); if (!/\{\s*\[native code\]\s*\}/.test(str)) { script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n')); } } catch (e) {}; }; f.parentNode.removeChild(f); if (script.childNodes.length) this.nextSibling.appendChild(script); }; head.copyScript(win.wrappedJSObject || win); head.copyStyle = function (s) { if (!s) return; var style = doc.createElement('style'); style.type = 'text/css'; if (s.media && s.media.mediaText) style.media = s.media.mediaText; try { for (var i = 0, rule; rule = s.cssRules[i]; i++) { if (rule.type != 3) { if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) { var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href); if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url); return prev + url + next; }); style.appendChild(doc.createTextNode(css + '\n')); } } else { this.copyStyle(rule.styleSheet); } } } catch(e) { if (s.ownerNode) style = s.ownerNode.cloneNode(false); }; this.appendChild(style); }; for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]); head.appendChild(doc.createTextNode('\n')); var doctype = '', dt = doc.doctype; if (dt && dt.name) { doctype += '<!DOCTYPE ' + dt.name; if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22'; if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22'; doctype += '>\n'; }; var selText = selWin ? win.getSelection().toString().slice(0, 200) : undefined; sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', selText]); // выделенный текст }); // END hookClicks
(async (id, sel) => { // Клики на Звёздочке, ToolTip: расположение закладки в Избранном, Недавняя папка var g = Cu.getGlobalForObject(Cu), stt = g[id]; // https://forum.mozilla-russia.org/viewtopic.php?pid=790890#p790890 if (!stt) { var {obs, prefs} = Services, {bookmarks: bm, observers: pobs} = PlacesUtils; stt = g[id] = { bm, help_star: ` Правый клик: ⤾ Вернуть вкладку …+ Alt Перевод выдел.текст | Сайт …+ Shift Гугл Перевод или поиск\n Колесико ± Яркость страниц …+ клик Полная яркость`, pref: `ucf.${id}Guid`, events: ["bookmark-added"], async init() { this.handleEvent = e => this[e.type](e); if ((this.pbm = typeof PlacesBookmarkMoved == "function")) this.events.push("bookmark-moved"); else this.QueryInterface = g.ChromeUtils.generateQI([Ci.nsINavBookmarkObserver]), bm.addObserver(this); pobs.addListener(this.events, this.added = events => { for(var e of events) e.isTagging || this[e.constructor.name](e); }); obs.addObserver(this, "quit-application-granted"); this.args = [b => this.bguids.add(b.parentGuid), {concurrent: true}]; var guid = prefs.getStringPref(this.pref, ""); if (!guid) try {var [guid] = await PlacesUtils.metadata.get( PlacesUIUtils.LAST_USED_FOLDERS_META_KEY, [] )} catch {} this.guids.push(guid || await PlacesUIUtils.defaultParentGuid || bm.unfiledGuid); var pref = "ucf.tabbrowser-tabpanels.opacity"; // яркость страницы var getPref = () => Services.prefs.getIntPref(pref, 100); var css = `@-moz-document url(chrome://browser/content/browser.xhtml) { :is(${sel})[rst] {filter: grayscale(1%) !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabbox {background-color: black !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabpanels {opacity:${getPref()/100} !important;}}`; var subst = "ucf-tabbrowser-tabpanels-opacity-style", url = `resource://${subst}/`; Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler) .setSubstitution(subst, Services.io.newURI("data:text/css," + encodeURIComponent(css))); var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET); var st = InspectorUtils.getAllStyleSheets(document).find(s => s.href == url).cssRules[0].cssRules[2].style; this.setPref = (e, val = 100) => { Services.prefs.setIntPref(pref, val); e.target.toggleAttribute("rst"); } this.wheel = e => { var val = getPref() + (e.deltaY < 0 ? 5 : -5); // шаг val < 25 || val > 100 || this.setPref(e, val); } var observer = () => st.setProperty("opacity", getPref() / 100, "important"); Services.prefs.addObserver(pref, observer); this.removePrefObs = () => Services.prefs.removeObserver(pref, observer); }, observe() { this.pbm || bm.removeObserver(this); pobs.removeListener(this.events, this.added); obs.removeObserver(this, "quit-application-granted"); prefs.setStringPref(this.pref, this.guids[0]); this.removePrefObs(); }, bguids: new g.Set(), guids: new g.Array(), skipTags: true, tt(win) { var list = win.InspectorUtils .getChildrenForNode(win.document.documentElement, true); return list.item(list.length - 1); }, PlacesBookmarkAddition(e) { if (e.itemType == bm.TYPE_BOOKMARK && e.source == bm.SOURCES.DEFAULT) this.guids[0] = e.parentGuid; }, PlacesBookmarkMoved(e) { e.parentGuid != e.oldParentGuid && this.PlacesBookmarkAddition(e); }, onItemMoved(a, b, c, d, e, itemType, f, oldParentGuid, parentGuid, source) { this.PlacesBookmarkMoved({itemType, source, oldParentGuid, parentGuid}); }, fetch(win) { this.bguids.clear(); return bm.fetch({url: win.gBrowser.currentURI.spec}, ...this.args); }, addTab: function(win, url, add, params = {relatedToCurrent: true}) { // открыть адрес [add: в новой вкладке] params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); return (add) ? win.gBrowser.addTab(url, params) : win.gBrowser.loadURI(url, params); }, translate(browserMM, win, e, go) { // Google-перевод сайта | выделенного текста (go) поиск выдел. текста в Яндекс browserMM.addMessageListener('getSelect', function listener(msg) { var url = (msg.data) ? (go) ? "https://yandex.ru/search/?text="+ msg.data +"&src=suggest_Pers&lang=ru" // поиск текста в Яндекс : "https://translate.google.com/#view=home&op=translate&sl=auto&tl=ru&text="+ msg.data // Гугл перевод : "http://translate.google.com/translate?u="+ gURLBar.value +"&hl=ru&ie=UTF-8&sl=auto&tl=ru"; // Перевод сайта if (go && !msg.data) // Перевод сайти в Яндекс. ничего не выделено + go не пуст gBrowser.selectedTab = e.addTab(win, "https://translate.yandex.com/translate?url=" + gURLBar.value + "&dir=&ui=ru&lang=auto-ru", 1) else gBrowser.selectedTab = e.addTab(win, url, 1); browserMM.removeMessageListener('getSelect', listener, true); }); browserMM.loadFrameScript('data:,sendAsyncMessage("getSelect", content.document.getSelection().toString())', false); }, auxclick(e) { if (e.button == 2) { var win = e.view; if (e.altKey) this.translate(gBrowser.selectedBrowser.messageManager, win, this, 1); else if (e.shiftKey) this.translate(gBrowser.selectedBrowser.messageManager, win, this); else win.undoCloseTab(); } else this.setPref(e); }, find: obj => obj.name == "tooltiptext" }; var ps = ["onBeginUpdateBatch", "onEndUpdateBatch", "onItemChanged", "onItemVisited"]; var noop = () => {}; for(var p of ps) stt[p] = noop; stt.init(); var func = id => this[id].mouseenter = async function(e) { var win = e.view, star = e.target, result = [], starred = star.hasAttribute("starred"); starred && await this.fetch(win); this.help_star = this.help_star.replace(/Яркость страниц.*/, `Яркость страниц ${win.Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity", 100)}%`); for(var guid of (starred ? this.bguids : this.guids)) { var arr = [], num = 50; while(--num) { if (!star.matches(":hover")) return; var res = await this.bm.fetch(guid); if (!res) break; if ((guid = res.parentGuid) == this.bm.rootGuid) { arr.unshift(this.bm.getLocalizedTitle(res)); break; } arr.unshift(res.title || "[Безымянная папка]"); } arr.length && result.push(arr.join("\\")); } if (!star.matches(":hover")) return; var text = (await win.document.l10n.formatMessages([{ // стандартная подсказка id: star.getAttribute("data-l10n-id"), args: JSON.parse(star.getAttribute("data-l10n-args")) }]))[0].attributes.find(this.find).value, txt; if (result.length) { txt = result.join("\n"); txt = starred ? `\n\n★ ${result.length > 1 ? "Данные закладки добавлены" : "Данная закладка добавлена"} в:\n${txt}` : "\n\n★ Недавно добавленная папка:\n" + txt; } win.document.tooltipNode == star ? this.tt(win).label = text + this.help_star + txt : star.tooltipText = text + this.help_star + txt; } var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})("${id}")`); g.ChromeUtils.compileScript(url).then(ps => ps.executeInGlobal(g)); } await delayedStartupPromise; var types = ["auxclick", "mouseenter", "wheel"]; var stars = Array.from(document.querySelectorAll(sel)); for(var star of stars) for(var type of types) star.addEventListener(type, stt); star.setAttribute("context", "event.stopPropagation()"); var destructor = () => { for(var star of stars) for(var type of types) star.removeEventListener(type, stt); } var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win; if (ucf) ucf[id] = {destructor}, ucf.unloadlisteners.push(id); else window.addEventListener("unload", destructor, {once: true}); })("ucfBookmarksStarFTooltipHelper", "#star-button, #context-bookmarkpage");
ucf_QuickToggle.js - здесь код LongPress
Отредактировано Dobrov (20-11-2021 01:29:28)
Отсутствует
доработать код, чтобы сразу перехватывать клики кнопок "nav-bar-customization-target" основной панели и "page-action-buttons"
Так PanelUI-menu-button же торчит в коде,
nav-bar-customization-target ей не родитель. Общим будет nav-bar.
identity-box
Раз предполагаются элементы с видимыми для мыши
дочерними элементами, придётся использовать перебор и closest().
Кстати, с FF90+ таковы pageAction's (в hbox иконки завернули).
добавить действие на долгое нажатие кнопки
добавить перехват "wheel"
И ты думаешь я смогу это всё нормально записать?
Весьма сомнительно. Попробую, так, отдельно, в консоль.
(() => { var c = msg => Services.console.logStringMessage("[HC] " + msg); var data = { "#downloads-button": { mousedownTarget: true, 128() { // СКМ Click c("Downloads Button Middle Click"); }, 4() { // Double Left Click c("Downloads Button Double Left Click"); }, 256() { // ПКМ Click c("Downloads Button Right Click"); }, 260(btn) { // Double Right Click c("Downloads Button Double Right Click"); }, 1() { // Left Long Press c("Downloads Button Left Long Press"); }, }, "#PanelUI-menu-button": { mousedownTarget: true, 8() { // ЛКМ + Alt c("PUI Button Alt + Left Click"); }, 16(btn) { // Shift + ЛКМ c("PUI Button Shift + Left Click"); }, 136(btn) { // Alt + СКМ c("PUI Button Alt + Middle Click"); }, 128() { // СКМ c("PUI Button Middle Click"); }, 132() { // СКМ Double c("PUI Button Double Middle Click"); }, 256() { // ПКМ c("PUI Button Right Click"); }, 264(btn) { // Alt + ПКМ c("PUI Button Alt + Right Click"); }, 272() { // Shift + ПКМ c("PUI Button Shift + Right Click"); }, 280(btn) { // Shift + Alt + ПКМ c("PUI Button Shift + Alt + Right Click"); }, 4() { // Double Left Click c("PUI Button Double Left Click"); }, 260() { // ПКМ Double Right Click c("PUI Button Double Right Click"); }, 145() { // Shift + Middle Long Press c("PUI Button Shift + Middle Long Press"); }, }, "#pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": { // Reader View Button 128() { // Middle Click c("Reader View Middle Click"); }, 289() { // Ctrl + Right Long Press c("Reader View Ctrl + Right Long Press"); }, 2(trg, forward) { // wheel c("Reader View Wheel " + (forward ? "forward" : "backward")); }, }, "#star-button-box": { 1() { // Left Long Press c("Star Left Long Press"); }, 4() { // Double Left Click c("Star Double Left Click"); }, 129() { // Middle Long Press c("Star Middle Long Press"); }, }, "#identity-permission-box": { 2(trg, forward) { // wheel c("Identity Permisson Box Wheel " + (forward ? "forward" : "backward")); } }, "#identity-box": { 2(trg, forward) { // wheel c("Identity Box Wheel " + (forward ? "forward" : "backward")); }, 34(trg, forward) { // Ctrl + wheel c("Identity Box Ctrl + Wheel " + (forward ? "forward" : "backward")); }, }, "#identity-icon-box": { 16() { // Shift + Left Click c("Identity Icon Box Shift + Left Click"); }, }, }; var listener = { filter(sel) { return this.closest(sel); }, find(sel) { return data[sel][this] || data[sel][this + 1]; }, handleEvent(e) { if (this.skip || e.detail > 2) return; var trg = e.target; var sels = this.selectors.filter(this.filter, trg); var {length} = sels; if (!length) return; var dbl = e.detail == 2; var wh = e.type.startsWith("w"); var num = e.metaKey *64 + e.ctrlKey *32 + e.shiftKey *16 + e.altKey *8 + (wh ? 2 : e.button *128 + dbl *4); var obj = data[ length > 1 && sels.find(this.find, num) || sels[0] ]; // wheel if (wh) return obj[num]?.(trg, e.deltaY < 0); // mousedown if (e.type.startsWith("m")) { obj.mousedownTarget && this.stop(e); if (dbl) return; this.longPress = false; if (++num in obj) this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num); if (e.button == 2) this.ctx = trg.getAttribute("context"), trg.setAttribute("context", ""); return; } // click obj.mousedownTarget || this.stop(e); if (this.longPress) return this.longPress = false; dbl ? this.clickTID &&= clearTimeout(this.clickTID) : this.mousedownTID &&= clearTimeout(this.mousedownTID); if (!obj[num]) { if (e.button == 1) return; if (e.button) { num = "context"; for(var p in this.a) this.a[p] = e[p]; } else num = "dispatch", this.mdt = obj.mousedownTarget; obj = this; } dbl ? obj[num](trg) : this.clickTID = setTimeout(this.exec, 300, trg, obj, num); }, get selectors() { this.exec = (trg, obj, num) => { this.clickTID = null; obj[num](trg); } this.onLongPress = (trg, obj, num) => { this.mousedownTID = null; this.longPress = true; obj[num](trg); } delete this.selectors; return this.selectors = Object.keys(data); }, get mdEvent() { delete this.mdEvent; return this.mdEvent = new MouseEvent("mousedown", {bubbles: true}); }, context(trg) { this.ctx ? trg.setAttribute("context", this.ctx) : trg.removeAttribute("context"); trg.dispatchEvent(new MouseEvent("contextmenu", this.a)); }, dispatch(trg) { this.skip = true; this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click(); this.skip = false; }, stop: e => { e.preventDefault(); e.stopImmediatePropagation(); }, a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0} }; var root = document.getElementById("nav-bar"); var events = ["click", "mousedown", "wheel"]; for(var type of events) root.addEventListener(type, listener, true); var id = "test-hookClicks"; ucf_custom_script_win.unloadlisteners.push(id); ucf_custom_script_win[id] = {destructor() { for(var type of events) root.removeEventListener(type, listener, true); }}; })();
Отсутствует
Dumby спасибо за отличный код дополнительных кликов.
Подключил оба кода в custom_script_win.js, попробую вернуть функции Save HTML и прочие…
Только не понял, как в код обновления tooltips добавить removeEventListener и нужен ли он.
Ещё не обновляются подсказки для 1) tracking-protection-icon-container "На этой странице не обнаружено ни одного известного Firefox трекера" и 2) identity-icon-box "Подтверждено: Let's Encrypt".
Я подключил на них яркость страниц, код работает. А как к этим кнопкам с динамической подсказкой добавить свой текст?
`Колесико ± Яркость страниц\n…+ клик Полная яркость
Яркость страниц ${win.Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity", 100)}%` ?
Отредактировано Dobrov (23-11-2021 10:45:15)
Отсутствует
как в код обновления tooltips добавить removeEventListener и нужен ли он
Да так же как и в коде для кликов.
А вот нужен ли он — это я и сам хотел бы знать.
Необходимость вызывает сомнение, но так принято (было),
поэтому, по возможности, оно так и продолжается.
Допустим, тултипский код рядом, сразу после var listener = {.....};
и добавление обработчиков и деструктора в самом конце.
....... var str_cut = s => s; var dsym = Symbol(); var j = (...args) => args.join("\n"); var tooltips = { get "PanelUI-menu-button"() { delete this["PanelUI-menu-button"]; return j( `Клик мыши: меню Firefox ${Services.appinfo.platformVersion}`, "…+ Shift ⚑ Краткая справка", "…+ Alt Персонализация", "Клик дважды ⊠ закрыть браузер\n", "Правый клик ⇲ Свернуть", "…+ дважды ⤾ Вернуть вкладку", "…+ Alt Диспетчер задач", "…+ Shift Адаптивный дизайн\n", "Колёсико: Развернуть | окно", "…+ Alt Полный экран", "…+ дважды Обновить без кэша" ); }, "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": j( `Reader View ${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n`, "Клик мыши Режим для чтения", "Колёсико Адаптивный дизайн" ), "_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action": j( "Video DownloadHelper", "Скачивание проигрываемого видео" ), [dsym]: j( GetDynamicShortcutTooltipText("downloads-button"), "\nДвойной клик: ⬇︎ открыть [Загрузки]", "…на картинке: ⧉ найти Похожие\n", "Правый клик (Alt+S): Сохранить", " в единый html всё / выделенное", "…дважды Картинки вкл/выкл\n", "Ролик: Сохранить как файл .txt", "Колёсико на рисунке: ➜ Сохранить" ), get "downloads-button"() { var hint = this[dsym]; if (document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action")) hint += "\nAlt⇧S ⌨ нажатие SingleSave"; try {var dw = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dw = Services.dirsvc.get("DfltDwnld", Ci.nsIFile);} if (dw) hint += "\n\n[Загрузки] — выбранная папка:\n" + str_cut(dw.path, 33); return hint; }, get "identity-icon-box"() { var ttt = ""; var trg = window.event.target; if (!trg.id.endsWith("x")) { if (trg.hasAttribute("tooltiptext")) ttt = trg.ttt = trg.tooltipText; else ttt = trg.ttt; if (ttt) ttt += "\n\n"; trg.removeAttribute("tooltiptext"); } return ttt + "Свой текст"; }, get "tracking-protection-icon-container"() { var trg = window.event?.target; return trg.id.endsWith("r") && trg.textContent + "\n\nСвой текст"; } }; document.getElementById("tracking-protection-icon-container") .removeAttribute("tooltip"); var onMouseenter = e => { var trg = e.target; var hint = tooltips[trg.id] || tooltips[(trg = trg.parentNode).id]; if (hint) trg.tooltipText = hint; } var root = document.getElementById("nav-bar"); var events = ["click", "mousedown", "wheel"]; root.addEventListener("mouseenter", onMouseenter, true); for(var type of events) root.addEventListener(type, listener, true); var id = "hookClicks-and-tooltips"; ucf_custom_script_win.unloadlisteners.push(id); ucf_custom_script_win[id] = {destructor() { root.removeEventListener("mouseenter", onMouseenter, true); for(var type of events) root.removeEventListener(type, listener, true); }}; })();
Отсутствует
Dumby - спасибо! Скрипт hookClicks пригодится многим, он позволит прописывать обработку кликов в одном скрипте и позволит «разгрузить» другие кнопки, не добавлять в них код обработки кликов.
Этот код, расширяющий возможности нескольких кнопок, значительно устарел. Рекомендуется ucf_hookClicks.js
(async (id, func) => { // для custom_script_win.js: дополнительные клики и подсказки кнопок © Dumby, mod Dobrov var dsym = Symbol(), j = (...args) => args.join("\n"), br_val = () => { return ` ${Services.prefs.getIntPref("ucf.tabbrowser-tabpanels.opacity",100)}%`;}, br_txt = `Клик ролика сброс яркости\nКрутить ± Яркость страниц`, tooltips = { get "PanelUI-menu-button"() { /* delete this["PanelUI-menu-button"]; */ return j( `Клик мыши: меню Firefox ${Services.appinfo.platformVersion}`, `… держать ⚑ Краткая справка`, `…+ Alt Персонализация`, `Клик дважды ⊠ закрыть браузер\n`, `Правый клик ⇲ Свернуть`, `…+ дважды ⤾ Вернуть вкладку`, `…+ Alt Диспетчер задач\n`, `Колёсико: Развернуть | окно`, `…+ Alt Полный экран`, `…+ дважды Обновить без кэша` );}, [dsym]: j(GetDynamicShortcutTooltipText("downloads-button"), `\nДвойной клик: ⬇︎ открыть [Загрузки]`, `…на картинке: ⧉ найти Похожие\n`, `Правый клик (Alt+S): Сохранить`, ` в единый html всё / выделенное`, `…дважды Картинки вкл/выкл\n`, `Ролик: Сохранить как файл .txt`, `Колёсико на рисунке: ➜ Сохранить` ), get "titlebar-button titlebar-close"() { return j( `Закрыть Firefox ${AppConstants.MOZ_APP_VERSION.replace(/-.*/,'')}\n`, `◉ колёсико вернуть вкладку`, `◧ держать краткая Справка`, `◨ пр. клик ⇲ Свернуть`); }, get "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_"() { return j( `Reader View ${Services.appinfo.OS == "Darwin" ? "⌥⌘M" : "Ctrl+⇧+M"}\n`, `Клик мыши Режим для чтения`, `Колёсико Адаптивный дизайн\nКолесико ± Яркость сайта` + br_val()); }, "_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action": `Сохранить страницу с помощью SingleFile (Alt+S)` , "_b9db16a4-6edc-47ec-a1f4-b86292ed211d_-browser-action": `Video DownloadHelper\nСкачивание проигрываемого видео` , get "downloads-button"() { var hint = this[dsym]; if (document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action")) hint += "\nAlt⇧S ⌨ нажатие SingleSave"; //убрать/добавить try {var dw = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dw = Services.dirsvc.get("DfltDwnld", Ci.nsIFile);} //отличается от ⇧ if (dw) hint += "\n\n[Загрузки] — выбранная папка:\n" + str_cut(dw.path, 33); return hint; }, get "identity-icon-box"() { var trg = window.event.target, ttt = ""; if (!trg.id.endsWith("x")) { if (trg.hasAttribute("tooltiptext")) ttt = trg.ttt = trg.tooltipText; else ttt = trg.ttt; if (ttt) ttt += "\n\n"; trg.removeAttribute("tooltiptext"); } return ttt +`Правый клик Копировать адрес в буфер\n`+ br_txt + br_val(); }, get "tracking-protection-icon-container"() { var trg = window.event?.target; return trg.id.endsWith("r") && trg.textContent + "\n\n" + br_txt + br_val(); } }; /* end tooltips */ document.getElementById("tracking-protection-icon-container").removeAttribute("tooltip"); var listener = { // дополнительные клики кнопок и перехват существующих filter(sel) { return this.closest(sel); }, find(sel) { return data[sel][this] || data[sel][this + 1]; }, handleEvent(e) { if (this.skip || e.detail > 2) return; var trg = e.target; var sels = this.selectors.filter(this.filter, trg); var {length} = sels; if (!length) return; var dbl = e.detail == 2; var wh = e.type.startsWith("w"); var num = e.metaKey *64 + e.ctrlKey *32 + e.shiftKey *16 + e.altKey *8 + (wh ? 2 : e.button *128 + dbl *4); var obj = data[ length > 1 && sels.find(this.find, num) || sels[0] ]; // wheel if (wh) return obj[num]?.(trg, e.deltaY < 0); // mousedown if (e.type.startsWith("m")) { obj.mousedownTarget && this.stop(e); if (dbl) return; this.longPress = false; if (++num in obj) this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num); if (e.button == 2) this.ctx = trg.getAttribute("context"), trg.setAttribute("context", ""); return; } // click obj.mousedownTarget || this.stop(e); if (this.longPress) return this.longPress = false; dbl ? this.clickTID &&= clearTimeout(this.clickTID) : this.mousedownTID &&= clearTimeout(this.mousedownTID); if (!obj[num]) { if (e.button == 1) return; if (e.button) { num = "context"; for(var p in this.a) this.a[p] = e[p]; } else num = "dispatch", this.mdt = obj.mousedownTarget; obj = this; } dbl ? obj[num](trg) : this.clickTID = setTimeout(this.exec, 300, trg, obj, num); }, get selectors() { this.exec = (trg, obj, num) => { this.clickTID = null; obj[num](trg); } this.onLongPress = (trg, obj, num) => { this.mousedownTID = null; this.longPress = true; obj[num](trg); } delete this.selectors; return this.selectors = Object.keys(data); }, get mdEvent() { delete this.mdEvent; return this.mdEvent = new MouseEvent("mousedown", {bubbles: true}); }, context(trg) { this.ctx ? trg.setAttribute("context", this.ctx) : trg.removeAttribute("context"); trg.dispatchEvent(new MouseEvent("contextmenu", this.a)); }, dispatch(trg) { this.skip = true; this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click(); this.skip = false; }, stop: e => { e.preventDefault(); e.stopImmediatePropagation(); }, a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0} }; var onMouseenter = e => { var trg = e.target, id = trg.id || trg.className; console.log('id= «'+ id + '» '+ Math.random()); var hint = tooltips[id] || tooltips[(trg = trg.parentNode).id]; if (hint) trg.tooltipText = hint; } var keydown_win = e => { // нажатие клавиш if (e.keyCode == 83 && e.altKey) { // Alt+S [+Shift] var singlesave = document.getElementById('_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action'); e.shiftKey ? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID } if (e.keyCode == 88 && e.altKey){ // Alt+X отладка внешнего JS-кода // e.target.ownerDocument.getElementById("key_browserConsole").doCommand(); eval(Cu.readUTF8URI(Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/User.js"))); console.log("[END] User.js " + Math.random()); } } var root = document.getElementById("navigator-toolbox"); var events = ["click", "mousedown", "wheel"]; root.addEventListener("mouseenter", onMouseenter, true); for(var type of events) root.addEventListener(type, listener, true); window.addEventListener("keydown", keydown_win); ucf_custom_script_win.unloadlisteners.push(id); ucf_custom_script_win[id] = {destructor() { root.removeEventListener("mouseenter", onMouseenter, true); for(var type of events) root.removeEventListener(type, listener, true); window.removeEventListener("keydown", keydown_win); }}; addDestructor = nextDestructor => { var {destructor} = ucf_custom_script_win[id]; ucf_custom_script_win[id].destructor = () => { try {destructor();} catch(ex) {Cu.reportError(ex);} nextDestructor(); } }; // end Hooks var {prefs, dirsvc} = Services, getIntPref = (p) => prefs.getIntPref(p, 100), c = msg => Services.console.logStringMessage("[HC] "+ msg), // отладка sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService), my_br = "ucf.tabbrowser-tabpanels.opacity", // яркость страниц css = `@-moz-document url(chrome://browser/content/browser.xhtml) { :is(${id})[rst] {filter: grayscale(1%) !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabbox {background-color: black !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabpanels {opacity:${getIntPref(my_br)/100} !important;}}`, subst = "ucf-tabbrowser-tabpanels-opacity-style", url = `resource://${subst}/`; Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler) .setSubstitution(subst, Services.io.newURI("data:text/css," + encodeURIComponent(css))); sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET); var st = InspectorUtils.getAllStyleSheets(document).find(s => s.href == url).cssRules[0].cssRules[2].style; var observer = () => st.setProperty("opacity", getIntPref(my_br)/100, "important"); prefs.addObserver(my_br, observer); this.removePrefObs = () => prefs.removeObserver(my_br, observer); // end яркость if (typeof IOUtils != "object") { // Firefox 78 ESR var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); var PathUtils = {join: (...args) => OS.Path.join(...args)}; var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))}; }; prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки str_cut = (s, cut = 33) => { // сократить/разбить строку return s.substring(0,cut) + `${s.length > cut - 1 ? `…\n…${s.substring(s.length -cut + 2, s.length)}`: ''}`; }, url_color = (color = "rgba(240,176,0,0.5)", ms = 300) => { // строка адреса мигает var u_alert = document.getElementById("urlbar-input-container"); u_alert.style.background = color; setTimeout(() => u_alert.style.background = "", ms); }, gClipboard = { get ch() { delete this.ch; return this.ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); }, write(str) { this.ch.copyStringToClipboard(str, Services.clipboard.kGlobalClipboard);} }, switchToTab = (url, but = window) => { // открыть вкладку | закрыть, если открыта for(var tab of but.ownerGlobal.gBrowser.tabs) if (tab.linkedBrowser.currentURI.spec == url) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // закрыть but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()}); }, showInStatusPanel = (info, time = 5000) => { StatusPanel = window.StatusPanel; if (StatusPanel.update.tid) clearTimeout(StatusPanel.update.tid) else { var {update} = StatusPanel; StatusPanel.update = () => {}; StatusPanel.update.ret = () => { StatusPanel.update = update; StatusPanel.update(); } } StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time); StatusPanel._label = info; }, Title = (max, title) => { // получить заголовок. без обрезки: max не указан, домен: max <0, + дата: max=0 if (!title) var title = document.title || gBrowser.selectedTab.label; if (max == undefined) return title; // заголовок как есть или ограничить длину, убрать служебные символы title = title.replace(/[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); if ( max > 0 ) return title.slice(0, max); if ( max == 0) return title.slice(0, 100) +"_"+ new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։"); var host = decodeURIComponent(gURLBar.value); // max < 0 if (!/^file:\/\//.test(host)) host = host.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,''); return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns'); }, saveSelectionToTxt = async () => { // сохранить выделенный/весь текст страницы как .txt var msgName = id + ":Save:GetSelection", splice = saveURL.length == 10; var receiver = msg => { var args = ["data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data), Title(0) + '.txt', null, false, true, null, window.document]; splice && args.splice(5, 0, null); saveURL(...args); showInStatusPanel("√ текст сохранён: "+ Title(0).slice(0, 60)); } messageManager.addMessageListener(msgName, receiver); addDestructor(() => messageManager.removeMessageListener(msgName, receiver)); var func = fm => { var res, fed, win = {}, fe = fm.getFocusedElementForWindow(content, true, win); var sel = (win = win.value).getSelection(); if (sel.isCollapsed) { var ed = fe && fe.editor; if (ed && ed instanceof Ci.nsIEditor) sel = ed.selection, fed = fe; } if (sel.isCollapsed) fed && fed.blur(), docShell.doCommand("cmd_selectAll"), res = win.getSelection().toString(), docShell.doCommand("cmd_selectNone"), fed && fed.focus(); res = res || sel.toString(); /\S/.test(res) && sendAsyncMessage("saveSelectionToTxt", res); } var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));'; (saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, save = async () => { // SingleHtml by Лекс, правка: Dumby, Dobrov var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML"; var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8"; var msgListener = async msg => { var [fileContent, fileName] = msg.data, dir; // fileName: выделенный текст или null try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);} var arr = prefs.getStringPref("ucf_save.dirs","_Web||_Images|0").split('|').slice(0,2); //subdir: title|host arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен arr.forEach(dir.append); // ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/label dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать, если не существует… var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(dir.path); if (!fileName) fileName = Title(100); // убрать служебные символы dir.append(Title(0, fileName) +'.html'); await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60)); var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download (await Downloads.getList(Downloads.ALL)).add(d); d.refresh(d.succeeded = true); // кнопка Загрузки мигает } messageManager.addMessageListener(msgName, msgListener); addDestructor(() => messageManager.removeMessageListener(msgName, msgListener)); var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services'; var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName)); (save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, bright = (trg, forward, val) => { // wheel if (!val) var val = getIntPref(my_br) + (forward ? 5 : -5); val = val > 100 ? 100 : val < 20 ? 20 : val; prefs.setIntPref(my_br, val), trg.toggleAttribute("rst"), showInStatusPanel("☀ Яркость страниц: "+ val +"%"); }, help = (btn) => { // встроенная справка var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762']; var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:/// (newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? switchToTab(help_ucf[0]) : switchToTab(help_ucf[1]); }, GetSelection = (mM = gBrowser.selectedBrowser.messageManager) => { mM.addMessageListener('getSelect', function sel_listener(msg) { window.seltxt = msg.data; mM.removeMessageListener('getSelect', sel_listener, true); }); mM.loadFrameScript('data:,sendAsyncMessage("getSelect",content.document.getSelection().toString())',false); }, data = { "#downloads-button": { mousedownTarget: true, 4() { // Double Left Click - Обзор папки «Загрузки» c("DW Double Left Click"); Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError); }, 128() { saveSelectionToTxt();}, // СКМ Click (сохранить .txt) 256() { save();}, // ПКМ Click (Single HTML) 260(btn) { // Double ПКМ Click var pref = "permissions.default.image"; var one = prefs.getIntPref(pref) == 1; prefs.setIntPref(pref, one ? 2 : 1); btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : ""; BrowserReload(); }, }, "#PanelUI-menu-button": { mousedownTarget: true, 1(btn) { help(btn);}, // Long Press 8() { gCustomizeMode.enter();}, //ЛКМ + Alt Персонализация 16(btn) { help(btn);}, // Shift + ЛКМ 4() { goQuitApplication();}, // Double Left Click 128() { windowState != STATE_MAXIMIZED ? maximize() : restore();}, // СКМ 136(btn) { BrowserFullScreen();}, // Alt + СКМ 132() { BrowserReloadSkipCache();}, // СКМ Double 256() { minimize();}, // ПКМ 260(btn) { btn.ownerGlobal.undoCloseTab();}, // ПКМ Double Right Click 264(btn) { switchToTab('about:performance');}, // Alt + ПКМ 280(btn) { // Shift + Alt + ПКМ var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging"); (this[280] = target => obj.oncommand({target}))(btn); // запуск пункта меню, у которого нет HotKey }, }, "#pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": { // Reader View Button 128(btn) { // СКМ btn.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload(); }, 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 264(btn) { // Alt + ПКМ translate(gBrowser.selectedBrowser.messageManager, 1); }, 1(btn) { // Shift + ПКМ translate(gBrowser.selectedBrowser.messageManager); }, }, "#star-button-box": { 1() { // Left Long Press c("Star Left Long Press"); }, 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± // 128(btn) { // СКМ // switchToTab('about:config'); // }, 256() { // ПКМ window.undoCloseTab(); }, }, "#identity-box": { // Замок 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 128(trg, forward) { bright(trg, forward, 100);}, // СКМ 256(btn) { // ПКМ gClipboard.write(gURLBar.value); url_color(), showInStatusPanel("в буфере: "+ gURLBar.value.slice(0, 80)); }, }, "#tracking-protection-icon-container": { // Защита 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 128(trg, forward) { bright(trg, forward, 100);}, // СКМ }, "#identity-permission-box": { 2(trg, forward) { // wheel c("Identity Permisson Box Wheel " + (forward ? "forward" : "backward")); } }, "#identity-icon-box": { 16() { // Shift + Left Click c("Identity Icon Box Shift + Left Click"); }, }, }; // end Clicks, HotKeys ================================================== })("hookClicks-and-tooltips", ({io, focus}) => { // SingleHTML не сохраняет svg графику var resolveURL = function (url, base) { try { return io.newURI(url, null, io.newURI(base)).spec;} catch {} }, getSelWin = function (w) { if (w.getSelection().toString()) return w; for (var i = 0, f, r; f = w.frames[i]; i++) { try { if (r = getSelWin(f)) return r;} catch(e) {} } }, encodeImg = function (src, obj) { var canvas, img, ret = src; if (/^https?:\/\//.test(src)) { canvas = doc.createElement('canvas'); if (!obj || obj.nodeName.toLowerCase() != 'img') { img = doc.createElement('img'); img.src = src; } else img = obj; if (img.complete) try{ canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png')); } catch (e) {}; if (img != obj) img.src = 'about:blank'; }; return ret; }, toSrc = function (obj) { var strToSrc = function (str) { var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'}; while (chr = str.charAt(i++)) { ret += meta[chr] || chr; }; return '\x22' + ret + '\x22'; }, arrToSrc = function (arr) { var ret = []; for (var i = 0; i < arr.length; i++) { ret[i] = toSrc(arr[i]) || 'null'; }; return '[' + ret.join(',') + ']'; }, objToSrc = function (obj) { var val, ret = []; for (var prop in obj) { if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val); }; return '{' + ret.join(',') + '}'; }; switch (Object.prototype.toString.call(obj).slice(8, -1)) { case 'Array': return arrToSrc(obj); case 'Boolean': case 'Function': case 'RegExp': return obj.toString(); case 'Date': return 'new Date(' + obj.getTime() + ')'; case 'Math': return 'Math'; case 'Number': return isFinite(obj) ? String(obj) : 'null'; case 'Object': return objToSrc(obj); case 'String': return strToSrc(obj); default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null'; } }, mainWin = {}; focus.getFocusedElementForWindow(content, true, mainWin); mainWin = mainWin.value; var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location; var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g; if (selWin) { var rng = win.getSelection().getRangeAt(0); pEle = rng.commonAncestorContainer; ele = rng.cloneContents(); } else { pEle = doc.documentElement; ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true); }; while (pEle) { if (pEle.nodeType == 1) { clone = pEle.cloneNode(false); clone.appendChild(ele); ele = clone; }; pEle = pEle.parentNode }; var sel = doc.createElement('div'); sel.appendChild(ele); for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) { el = all[i]; if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href); return prev + encodeImg(url) + next; }); switch (el.nodeName.toLowerCase()) { case 'link': case 'style': case 'script': el.parentNode.removeChild(el); break; case 'a': case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break; case 'img': case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break; case 'audio': case 'video': case 'embed': case 'frame': case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break; case 'object': if (el.hasAttribute('data')) el.data = el.data; break; case 'form': if (el.hasAttribute('action')) el.action = el.action; break; } }; var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0]; meta.httpEquiv = 'content-type'; meta.content = 'text/html; charset=utf-8'; head.appendChild(meta); if (title) head.appendChild(title.cloneNode(true)); head.copyScript = function (unsafeWin) { if ('$' in unsafeWin) return; var f = doc.createElement('iframe'); f.src = 'about:blank'; f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;'); doc.documentElement.appendChild(f); var str, script = doc.createElement('script'); script.type = 'text/javascript'; for (var name in unsafeWin) { if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue; try { str = toSrc(unsafeWin[name]); if (!/\{\s*\[native code\]\s*\}/.test(str)) { script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n')); } } catch (e) {}; }; f.parentNode.removeChild(f); if (script.childNodes.length) this.nextSibling.appendChild(script); }; head.copyScript(win.wrappedJSObject || win); head.copyStyle = function (s) { if (!s) return; var style = doc.createElement('style'); style.type = 'text/css'; if (s.media && s.media.mediaText) style.media = s.media.mediaText; try { for (var i = 0, rule; rule = s.cssRules[i]; i++) { if (rule.type != 3) { if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) { var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href); if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url); return prev + url + next; }); style.appendChild(doc.createTextNode(css + '\n')); } } else { this.copyStyle(rule.styleSheet); } } } catch(e) { if (s.ownerNode) style = s.ownerNode.cloneNode(false); }; this.appendChild(style); }; for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]); head.appendChild(doc.createTextNode('\n')); var doctype = '', dt = doc.doctype; if (dt && dt.name) { doctype += '<!DOCTYPE ' + dt.name; if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22'; if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22'; doctype += '>\n'; }; sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', selWin ? win.getSelection().toString().slice(0, 200) : undefined]); // выделенный текст }); // END hookClicks
Отредактировано Dobrov (15-06-2024 07:27:54)
Отсутствует
воможно ли с помощью скрипта https://forum.mozilla-russia.org/viewto … 54#p782454 открывать ссылки и страницы в tor browser? после последнего обновления тор открывается через скрипт, но соединения нет
Отсутствует
Dumby
Хочу на базе этого скрипта сделать ещё один, но чтоб он закрывал текущую вкладку, заменить им хочу расширение.
Подскажите пожалуйста, может где подправить можно по мелочи?
Сейчас мой код закрытия "других вкладок" стал немного другим, иконку ещё более подходящую нашёл
CustomizableUI.createWidget({ id: "Close-Tabs-button", label: "Закрыть другие вкладки", tooltiptext: "Закрыть другие вкладки", defaultArea: CustomizableUI.AREA_NAVBAR, localized: false, onCreated(btn) { btn.render = this.render; btn._handleClick = this.close; btn.setAttribute("image", "chrome://devtools/skin/images/close.svg"); }, render() { delete this.render; this.render(); this.icon.style.setProperty("padding", "3px", "important"); }, close() { var gb = this.ownerGlobal.gBrowser; gb.removeAllTabsBut(gb.selectedTab); } });
Отсутствует
По-моему, строку gb.removeAllTabsBut(gb.selectedTab); нужно изменить, но вот как...
Верно, эту строку. Как? Ну, обычно,
следует просто посмотреть как это делает сам браузер, и срисовать себе.
Если как пункт контекстного меню вкладок «Закрыть (N) вклад(ку|ки|ок)»,
то получится что-то типа
... //gb.removeAllTabsBut(gb.selectedTab); var tab = gb.selectedTab; tab.multiselected ? gb.removeMultiSelectedTabs() : gb.removeTab(tab, {animate: true});
Отсутствует
следует просто посмотреть как это делает сам браузер, и срисовать себе
С этим я пока не разобрался, хотя не помешало бы такую мелочь самому вычислить.
строго буквально «чтоб он закрывал текущую вкладку» и никак иначе
Да, как раз так и хотел, пункт "Выбрать все вкладки" не использую, и он у меня скрыт стилем. Скрипт собрал, всё работает
CustomizableUI.createWidget({ id: "Close-Tab", label: "Закрыть вкладку", tooltiptext: "Закрыть вкладку", defaultArea: CustomizableUI.AREA_NAVBAR, localized: false, onCreated(btn) { btn.render = this.render; btn._handleClick = this.close; btn.setAttribute("image", "chrome://devtools/skin/images/close.svg"); }, render() { delete this.render; this.render(); this.icon.style.setProperty("padding", "3px", "important"); }, close() { var gb = this.ownerGlobal.gBrowser; gb.removeTab(gb.selectedTab, {animate: true}); } });
Отсутствует
У меня кнопка "Закрыть вкладки..." такая, может кому пригодится. Кто-то помогал сделать, закреплённые вкладки, когда удаление слева или другие не удаляет. Я бы сам так не осилил. Работает, ucf у меня старый.
// Этот скрипт можно использовать для создания кнопок с помощью CustomizableUI.createWidget (() => { var loadscript = name => { try { Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/custom_scripts/${name}`, globalThis, "UTF-8"); } catch(e) {} }; loadscript("my_buttons.js"); })();
var {classes: Cc, interfaces: Ci, utils: Cu} = Components; var {console} = Cu.import("resource://gre/modules/Console.jsm", {}); try { CustomizableUI.createWidget({ id: "add-select-close-tabs-app", type: "custom", tooltiptext: [ "ЛКМ: Закрыть все вкладки", "Shift+ЛКМ: Закрыть другие вкладки", "Ctrl+ЛКМ: Закрыть слева", "Alt+ЛКМ: Закрыть справа", ].join("\n"), onBuild: function(document) { var toolbarbutton_0 = document.createXULElement("toolbarbutton"); toolbarbutton_0.id = this.id; toolbarbutton_0.tooltipText = this.tooltiptext; toolbarbutton_0.label = "Закрыть все вкладки"; toolbarbutton_0.setAttribute("context", false); toolbarbutton_0.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPYxg8wD5wy1y7gK1/7AO3/ncI2nq7uOnk3cq20//BuOP07b1Hnt09eurl/6OnXvwB0rOh2hAAphmGU0uOQDRD8ZrN94CaQQZADIFqQwBkzSAcnLTnVkLewWMgnFZy6Ep9z9kr3dMuHoNhqDYEQDeAEIZqQ4Dd937/JwVDtSEANkX4MFQbAmBThA9DtSEANkXIeM/t7/8vz1v9/1590//rE2f8/+pkIQ3VCgHYNMEwSPOLtOT/X1ws/n9xNvv/xRVIO5l9+OxsoQvVjt+AS/PXgDX/vXnt/4/q4v9/b1z9/y0tGmiY6S6odvwGgJwNshmk+f+fP/9/790JcQnQFVDt+A0A+RnsAqDNIM3/Prz//y0lEmTAZah2/OCrvZkM0LnvvqXFgG3+lgzUDAoHZ9N4qBLC4LOriQ7Qxt0gZwMNuQLRzMAAAAEG9BCOlZEaAAAAAElFTkSuQmCC"); toolbarbutton_0.addEventListener("click", function(event) { var win = event.target.ownerDocument.defaultView; if (event.button == 0) { if (event.shiftKey) { win.gBrowser.removeAllTabsBut(win.gBrowser.selectedTab); } else if (event.ctrlKey) { var ctab = win.gBrowser.selectedTab, tabs; if (ctab.multiselected) tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned); else tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned); var index = tabs.indexOf(ctab); tabs = tabs.slice(0, (index != -1) ? index : tabs.length); tabs.forEach((tab) => { win.gBrowser.removeTab(tab); }); } else if (event.altKey) { var ctab = win.gBrowser.selectedTab, tabs; if (ctab.multiselected) tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned); else tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned); var index = tabs.indexOf(ctab); tabs = tabs.slice((index != -1) ? (index + 1) : 0, tabs.length); tabs.forEach((tab) => { win.gBrowser.removeTab(tab); }); } else { win.gBrowser.selectAllTabs(); win.gBrowser.removeMultiSelectedTabs(); } } }, false); toolbarbutton_0.classList.add("toolbarbutton-1"); toolbarbutton_0.classList.add("chromeclass-toolbar-additional"); return toolbarbutton_0; } }); } catch(e) {}
Отредактировано xrun1 (15-12-2021 19:49:44)
Отсутствует
куда лучше подключить
Что значит лучше?
Либо добавить в custom_script.js или подобный, либо отдельным файлом.
Придумываешь название, создаёшь, и прописываешь в CustomStylesScripts.jsm
(это если использовать встроенный загрузчик)
var UcfStylesScripts = { /** ************************▼ Настройки ▼************************ */ ....... scriptsbackground: [ // В фоне [System Principal] ....... { path: "closeOtherTabs.js" }, ], /** ************************▲ Настройки ▲************************ */ };
никак не хочет работать
Что значит не хочет работать?
Открывает вкладки вместо того, чтобы закрывать?
id'шник проверь, чтоб уникальный был.
Иконки, кстати, такой как там у тебя может не быть, пропиши свою.
Кэш ещё может залипнуть, закрой браузер и удали папку startupCache руками.
Отсутствует
my_buttons.jsВыделить кодКод:
var {classes: Cc, interfaces: Ci, utils: Cu} = Components; var {console} = Cu.import("resource://gre/modules/Console.jsm", {}); try { CustomizableUI.createWidget({ id: "add-select-close-tabs-app", type: "custom", tooltiptext: [ "ЛКМ: Закрыть все вкладки", "Shift+ЛКМ: Закрыть другие вкладки", "Ctrl+ЛКМ: Закрыть слева", "Alt+ЛКМ: Закрыть справа", ].join("\n"), onBuild: function(document) { var toolbarbutton_0 = document.createXULElement("toolbarbutton"); toolbarbutton_0.id = this.id; toolbarbutton_0.tooltipText = this.tooltiptext; toolbarbutton_0.label = "Закрыть все вкладки"; toolbarbutton_0.setAttribute("context", false); toolbarbutton_0.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFLSURBVDhPYxg8wD5wy1y7gK1/7AO3/ncI2nq7uOnk3cq20//BuOP07b1Hnt09eurl/6OnXvwB0rOh2hAAphmGU0uOQDRD8ZrN94CaQQZADIFqQwBkzSAcnLTnVkLewWMgnFZy6Ep9z9kr3dMuHoNhqDYEQDeAEIZqQ4Dd937/JwVDtSEANkX4MFQbAmBThA9DtSEANkXIeM/t7/8vz1v9/1590//rE2f8/+pkIQ3VCgHYNMEwSPOLtOT/X1ws/n9xNvv/xRVIO5l9+OxsoQvVjt+AS/PXgDX/vXnt/4/q4v9/b1z9/y0tGmiY6S6odvwGgJwNshmk+f+fP/9/790JcQnQFVDt+A0A+RnsAqDNIM3/Prz//y0lEmTAZah2/OCrvZkM0LnvvqXFgG3+lgzUDAoHZ9N4qBLC4LOriQ7Qxt0gZwMNuQLRzMAAAAEG9BCOlZEaAAAAAElFTkSuQmCC"); toolbarbutton_0.addEventListener("click", function(event) { var win = event.target.ownerDocument.defaultView; if (event.button == 0) { if (event.shiftKey) { win.gBrowser.removeAllTabsBut(win.gBrowser.selectedTab); } else if (event.ctrlKey) { var ctab = win.gBrowser.selectedTab, tabs; if (ctab.multiselected) tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned); else tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned); var index = tabs.indexOf(ctab); tabs = tabs.slice(0, (index != -1) ? index : tabs.length); tabs.forEach((tab) => { win.gBrowser.removeTab(tab); }); } else if (event.altKey) { var ctab = win.gBrowser.selectedTab, tabs; if (ctab.multiselected) tabs = win.gBrowser.visibleTabs.filter(tab => !tab.multiselected && !tab.pinned); else tabs = win.gBrowser.visibleTabs.filter(tab => !tab.pinned); var index = tabs.indexOf(ctab); tabs = tabs.slice((index != -1) ? (index + 1) : 0, tabs.length); tabs.forEach((tab) => { win.gBrowser.removeTab(tab); }); } else { win.gBrowser.selectAllTabs(); win.gBrowser.removeMultiSelectedTabs(); } } }, false); toolbarbutton_0.classList.add("toolbarbutton-1"); toolbarbutton_0.classList.add("chromeclass-toolbar-additional"); return toolbarbutton_0; } }); } catch(e) {}
Интересная кнопочка.
Только немешало бы в неё добавить "защиту от дурака" на все действия - подтверждение на закрытие вкладок.
Что-то типа такого:
Отредактировано unter_officer (15-12-2021 21:20:15)
«The Truth Is Out There»
Отсутствует
xrun1
Спасибо за кнопку"показа/скрытия панели закладок и доп.панели".
Win7
Отсутствует
Dumby
Посмотрите пожалуйста, я правильно отредактировал?
try { CustomizableUI.createWidget({ id: "additional-toolbars-button", type: "custom", label: "Доп. панели", tooltiptext: [ "ЛКМ: Переключить верт. панель", "ПКМ: Переключить доп. панель" ].join("\n"), localized: false, onBuild(doc) { var trbn = doc.createXULElement("toolbarbutton"); trbn.id = this.id; trbn.tooltipText = this.tooltiptext; trbn.label = this.label; trbn.className = "toolbarbutton-1 chromeclass-toolbar-additional"; trbn.setAttribute("context", false); trbn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/layer-visible-off.svg"); trbn.addEventListener("click", function(e) { var pref = "browser.add.toolbars.visibility"; if (e.button == 0) { e.preventDefault(); e.stopPropagation(); CustomizableUI.setToolbarVisibility("ucf-additional-vertical-bar", doc.querySelector("#ucf-additional-vertical-bar").collapsed); } else if (e.button == 2) { e.preventDefault(); e.stopPropagation(); CustomizableUI.setToolbarVisibility("ucf-additional-top-bar", doc.querySelector("#ucf-additional-top-bar").collapsed); } }, false); return trbn; }, }); } catch(e) {}
Отсутствует
unter_officer
Для меня такое сделать нереально. Не представляю, как в кнопку прикрутить confirm()... Да и не нужно. Кнопка "Восстановить" из расширения ATB Vitaliy V. восстанавливает все закрытые вкладки моей кнопкой. Если сразу озаботиться, конечно.
kokoss
Я ещё для боковой панели менял кнопку "Закладки" для экономии места, вдруг пригодится.
try { CustomizableUI.createWidget({ id: "add-view-bookmarks-sidebar-button", type: "custom", label: "Закладки Библиотека История", tooltiptext: "ЛКМ: Показать / Скрыть Закладки\nСКМ: Открыть Библиотеки в табе\nПКМ: Показать / Скрыть Историю", localized: false, onBuild: function(doc) { var win = doc.defaultView; var trbn_0 = doc.createElementNS(ns_xul, "toolbarbutton"); trbn_0.id = "add-view-bookmarks-sidebar-button"; trbn_0.className = "toolbarbutton-1 chromeclass-toolbar-additional"; trbn_0.setAttribute("label", "Закладки Библиотека История"); trbn_0.setAttribute("context", "false"); trbn_0.setAttribute("tooltiptext", "ЛКМ: Показать / Скрыть Закладки\nСКМ: Открыть Библиотеки в табе\nПКМ: Показать / Скрыть Историю"); trbn_0.addEventListener("click", function(e) { if (e.button == 0) { if ("SidebarUI" in win) win.SidebarUI.toggle("viewBookmarksSidebar"); else if ("toggleSidebar" in win) win.toggleSidebar("viewBookmarksSidebar"); } else if (e.button == 1) { var url="chrome://browser/content/places/places.xhtml"; win.SidebarUI.hide(); /* Для CB, открывает "История" в окне "Библиотека" PlacesCommandHook.showPlacesOrganizer('History'); */ win.gBrowser.selectedTab = win.gBrowser.addTrustedTab(url); } else if (e.button == 2) { if ("SidebarUI" in win) win.SidebarUI.toggle("viewHistorySidebar"); else if ("toggleSidebar" in win) win.toggleSidebar("viewHistorySidebar"); } }); return trbn_0; } }); } catch(e) {}
Отсутствует
Посмотрите пожалуйста, я правильно отредактировал?
Не люблю я вопросы про «правильно», откуда мне знать что есть правильно.
Если работает, и страшных косяков нет (а их нет) — значит правильно.
Вот, например, в коде определяется переменная pref, но нигде не используется.
Или зачем e.preventDefault(); e.stopPropagation(); я же не знаю.
Превент может использоваться для предотвращения появления контекстного
меню при ПКМ (Windows), но там уже решено, что этого контекстного меню
не будет совсем никогда, поскольку установлен атрибут "context".
Или tooltiptext массивом. Когда там целая батарея подсказочных строк, то имеет смысл.
А парочку вполне можно и одной строкой записать. Это не вопрос правильности,
а вопрос предпочтения. Вот там widget создаётся как type: "custom", не знаю,
может это как-то внутренне оптимальнее, но чего не сделаешь, чтоб записать попроще, типа
(async () => CustomizableUI.createWidget({ label: "Доп. панели", tooltiptext: "ЛКМ: Переключить верт. панель\nПКМ: Переключить доп. панель", id: "additional-toolbars-button", localized: false, onCreated(btn) { btn.toggleAttribute("context"); btn.setAttribute("image", "chrome://user_chrome_files/content/custom_styles/svg/layer-visible-off.svg"); }, 0: "ucf-additional-vertical-bar", 2: "ucf-additional-top-bar", onClick(e) { var id = this[e.button]; id && CustomizableUI.setToolbarVisibility(id, e.view.document.getElementById(id).collapsed); } }))();
Отсутствует
Dumby
По мне судить не надо, судите по Vitaliy V., это его код.
Я в скриптах разбираюсь на уровне - что нибудь добавить/удалить по доступному примеру. (Сколько раз это повторить надо?) Поэтому и спросил. Спасибо за готовый код, это то что в итоге мне нужно было.
Отсутствует