Dumby - спасибо, исправил и доработал код.
Вопрос - как сделать перехват кликов на кнопках расширений, которые динамически появляются?
То есть, при старте браузера кнопки Reader View в строке адреса нет, она появляется, когда открывается сайт.
Будет удобнее, если у кнопки режима чтения появится ещё действие - например альтернативный режим просмотра.
То есть, нужно на кнопке Reader View по клику колёсиком включать/выключать режим "Адаптивный дизайн".
win.document.getElementById("pageAction-panel-_2495d258-41e7-4cd5-bc7d-ac15981f064e_").click() trg.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // Адаптивный дизайн id="pageAction-panel-_2495d258-41e7-4cd5-bc7d-ac15981f064e_" Reader View в меню Действия на странице id="pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_" в строке адреса actionid="_2495d258-41e7-4cd5-bc7d-ac15981f064e_"
Желательно добавить перехват кликов в тот же UCF-скрипт, который расширяет возможности кнопок Меню и Загрузки:
(async (id, func) => { // дополнительные клики на downloads-button, PanelUI-menu для custom_script_win.js await window.delayedStartupPromise; var btn = document.getElementById("downloads-button"), pui = document.getElementById("PanelUI-menu-button"); if (!btn) return; btn_help =` Двойной клик: открыть [Загрузки] …на картинке: ⧉ найти Похожие\n Правый клик (Alt+S): Сохранить в единый html всё / выделенное …дважды Картинки вкл/выкл\n Ролик: Сохранить как файл .txt Колёсико на рисунке: ➜ Сохранить`, PanelUI_help = `Клик мыши: меню Firefox ${Services.appinfo.platformVersion} …+ Shift ⚑ Краткая справка …+ Alt Персонализация Клик дважды ⊠ закрыть браузер \n Правый клик ⇲ Свернуть …+ дважды ⤾ Вернуть вкладку …+ Alt Диспетчер задач …+ Shift Адаптивный дизайн\n Колёсико: Развернуть | окно …+ Alt Полный экран …+ дважды Обновить без кэша`, singlesave_help = `\nAlt⇧S ⌨ нажатие SingleSave`; var addDestructor = nextDestructor => { var {destructor} = ucf[id]; ucf[id].destructor = () => { try {destructor();} catch(ex) {Cu.reportError(ex);} nextDestructor(); } }, showInStatusPanel = (info, time = 5000) => { var win = Services.wm.getMostRecentWindow("navigator:browser"); StatusPanel = win.StatusPanel; if (StatusPanel.update.tid) clearTimeout(StatusPanel.update.tid) else { var {update} = StatusPanel; StatusPanel.update = () => {}; StatusPanel.update.ret = () => { StatusPanel.update = update; StatusPanel.update(); } } StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time); StatusPanel._label = info; }, saveSelectionToTxt = async () => { // сохранить страницу или выделенный текст как файл .txt var splice = saveURL.length == 10; var msgName = id + ":Save:GetSelection"; var receiver = msg => { var title = document.title || gBrowser.selectedTab.label; var args = [ "data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data), title.replace(/[:\\\/<>?*|"]+/g,'_').replace(/\s+/g,' ').slice(0, 100).trim() + '_' + new Date().toLocaleString('ru').replace(', ','-').replace(/:/g, '։') + '.txt', null, false, true, null, window.document ]; splice && args.splice(5, 0, null); saveURL(...args) && showInStatusPanel("√ текст сохранён: " + title.slice(0, 60)); } messageManager.addMessageListener(msgName, receiver); addDestructor(() => messageManager.removeMessageListener(msgName, receiver)); var func = fm => { var res, fed, win = {}, fe = fm.getFocusedElementForWindow(content, true, win); var sel = (win = win.value).getSelection(); if (sel.isCollapsed) { var ed = fe && fe.editor; if (ed && ed instanceof Ci.nsIEditor) sel = ed.selection, fed = fe; } if (sel.isCollapsed) fed && fed.blur(), docShell.doCommand("cmd_selectAll"), res = win.getSelection().toString(), docShell.doCommand("cmd_selectNone"), fed && fed.focus(); res = res || sel.toString(); /\S/.test(res) && sendAsyncMessage("saveSelectionToTxt", res); } var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));'; (saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, // end save = async () => { // автор: Лекс, правка: Dumby, Dobrov (пути сохранения HTML) var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML"; if (typeof IOUtils != "object") { // Firefox 78 ESR var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); var PathUtils = {join: (...args) => OS.Path.join(...args)}; var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))}; } var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8"; var Title = (type) => { // получить заголовок (без обрезки, если type не указан) или домен (type <0) var title = (document.title || gBrowser.selectedTab.label); if ( !type ) return title; // заголовок if ( type > 0 ) return title.slice(0, type).replace(/ \| Форум Mozilla Россия$| — Mozilla Firefox|[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); // ограничить длину имени var host = (/^file:\/\//.test(gURLBar.value)) ? '' : gURLBar.value.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,''); return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns'); } var msgListener = async msg => { var [fileContent, fileName] = msg.data, dir; try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);} var arr = prefs.getStringPref("ucf_save.dirs", "_Web||_Images|0").split('|').slice(0, 2); // Dir/subdir: пусто|0 title|1 домен arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен arr.forEach(dir.append); // для ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/имя вкладки dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать папку, если не существует… var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(dir.path); dir.append(fileName); await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60)); var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download (await Downloads.getList(Downloads.ALL)).add(d); d.refresh(d.succeeded = true); // кнопка Загрузки мигает } messageManager.addMessageListener(msgName, msgListener); addDestructor(() => messageManager.removeMessageListener(msgName, msgListener)); var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services'; var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName)); (save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, // end save tid, allowMousedown, listener = { handleEvent(e) { if (e.detail > 2) return; var btn = e.target; var dbl = e.detail == 2; var num = (btn == pui && 100) + 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl; if (!this[num]) { if (e.button == 1) return; if (e.button) { num = "context"; for(var p in this.a) this.a[p] = e[p]; } else num = "mousedown"; } if (dbl) tid &&= clearTimeout(tid), this[num](btn); else tid = setTimeout(this.exec, 300, num, btn, this); }, exec(num, btn, self) { tid = null; self[num](btn); }, mousedown(btn) { allowMousedown = true; btn.dispatchEvent(new MouseEvent("mousedown", {})); allowMousedown = false; }, context(btn) { btn.removeAttribute("context"); btn.dispatchEvent(new MouseEvent("contextmenu", this.a)); btn.toggleAttribute("context"); }, switchToTab(but, url) { // открыть вкладку | закрыть, если открыта for(var tab of but.ownerGlobal.gBrowser.tabs) if ( tab.linkedBrowser.currentURI.spec == url ) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // вкладка найдена, закрыть but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()}); }, a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}, /*** ======= Downloads Clicks ======= ***/ 16: saveSelectionToTxt, // СКМ Click (сохранить .txt) 1() { // Double Left Click - Обзор папки «Загрузки» Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError) // Обзор папки «Загрузки» }, 32: save, // ПКМ Click (Single HTML) 33(btn) { // Double Right Click var pref = "permissions.default.image"; var one = prefs.getIntPref(pref) == 1; prefs.setIntPref(pref, one ? 2 : 1); btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : ""; BrowserReload(); }, 2() { // Alt + ЛКМ }, /*** ======= PanelUI-menu Clicks ======= ***/ 102() { gCustomizeMode.enter(); // ЛКМ + Alt Персонализация }, 104() { // Shift + ЛКМ var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762']; var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:/// (newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? this.switchToTab(pui, help_ucf[0]) : this.switchToTab(pui, help_ucf[1]); }, 118: BrowserFullScreen, // Alt + СКМ 116() { // СКМ windowState != STATE_MAXIMIZED ? maximize() : restore(); }, 117: BrowserReloadSkipCache, // СКМ Double 132() { // ПКМ minimize(); }, 134(pui) { // Alt + ПКМ this.switchToTab(pui, 'about:performance'); }, 136(pui) { // Shift + ПКМ pui.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey }, 138(pui) { // Shift + Alt + ПКМ var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging"); (this[136] = target => obj.oncommand({target}))(pui); // запуск пункта меню, у которого нет HotKey }, 101: BrowserTryToCloseWindow, // Double Left Click 133(pui) { // ПКМ Double Right Click pui.ownerGlobal.undoCloseTab(); }, }, // end Clicks keydown_win = e => { // нажатие клавиш if (e.keyCode == 83 && e.altKey) e.shiftKey // Alt+S [+Shift] ? singlesave ? singlesave.click() : save() : save(); // имитировать клик по кнопке, используя её ID }, {prefs, dirsvc} = Services, linux = /macos|linux/.test(AppConstants.platform), singlesave; prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки var mouseenter = function(e) { this.parentNode.addEventListener("mousedown", stop, true); this.addEventListener("mouseleave", mouseleave, {once: true}); if ((e.target == pui) && (!/справка/.test(pui.tooltipText))) pui.tooltipText = PanelUI_help; // обновить подсказки кнопок if (e.target == btn) { var singlesave = document.getElementById("_531906d3-e22f-4a6c-a102-8057b88a1a63_-browser-action"), dw; try {dw = prefs.getComplexValue("browser.download.dir",Ci.nsIFile)} catch {dw = dirsvc.get("DfltDwnld", Ci.nsIFile)}; // if (!/Двойной/.test(btn.tooltipText)) btn.tooltipText = GetDynamicShortcutTooltipText(btn.id) + btn_help; // обновлять подсказку при наведении мыши if (singlesave){ if (!/SingleSave/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + singlesave_help; } else btn.tooltipText = btn.tooltipText.replace(singlesave_help,''); if (!/выбранная/.test(btn.tooltipText)) btn.tooltipText = btn.tooltipText + `${dw ? "\n\n[Загрузки] — выбранная папка:\n"+ dw.path.substring(0,33) + `${dw.path.length > 32 ? `…\n…${dw.path.substring(dw.path.length -31, dw.path.length)}`: ""}` : ""}`; // сократить/разбить длинную строку } } var mouseleave = function(e) { this.parentNode.removeEventListener("mousedown", stop, true); } var stop = e => { e.button || allowMousedown || e.stopImmediatePropagation(); } var btns = [btn, pui]; for(var b of btns) { b.toggleAttribute("context"), b.addEventListener("click", listener, true), b.addEventListener("mouseenter", mouseenter); } window.addEventListener("keydown", keydown_win); var ucf = window.ucf_custom_script_win || window.ucf_custom_script_all_win; ucf[id] = {destructor() { for(var b of btns) { b.removeEventListener("click", listener, true); b.removeEventListener("mouseenter", mouseenter); if (b.matches(":hover")) b.removeEventListener("mouseleave", mouseleave), b.parentNode.removeEventListener("mousedown", stop, true); } window.removeEventListener("keydown", keydown_win); }}; ucf.unloadlisteners.push(id); })("downloads-button-click-listener", ({io, focus}) => { // SingleHTML не сохраняет svg графику var resolveURL = function (url, base) { try { return io.newURI(url, null, io.newURI(base)).spec; } catch {} }, getSelWin = function (w) { if (w.getSelection().toString()) return w; for (var i = 0, f, r; f = w.frames[i]; i++) { try { if (r = getSelWin(f)) return r; } catch(e) {} } }, encodeImg = function (src, obj) { var canvas, img, ret = src; if (/^https?:\/\//.test(src)) { canvas = doc.createElement('canvas'); if (!obj || obj.nodeName.toLowerCase() != 'img') { img = doc.createElement('img'); img.src = src; } else img = obj; if (img.complete) try{ canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png')); } catch (e) {}; if (img != obj) img.src = 'about:blank'; }; return ret; }, toSrc = function (obj) { var strToSrc = function (str) { var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'}; while (chr = str.charAt(i++)) { ret += meta[chr] || chr; }; return '\x22' + ret + '\x22'; }, arrToSrc = function (arr) { var ret = []; for (var i = 0; i < arr.length; i++) { ret[i] = toSrc(arr[i]) || 'null'; }; return '[' + ret.join(',') + ']'; }, objToSrc = function (obj) { var val, ret = []; for (var prop in obj) { if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val); }; return '{' + ret.join(',') + '}'; }; switch (Object.prototype.toString.call(obj).slice(8, -1)) { case 'Array': return arrToSrc(obj); case 'Boolean': case 'Function': case 'RegExp': return obj.toString(); case 'Date': return 'new Date(' + obj.getTime() + ')'; case 'Math': return 'Math'; case 'Number': return isFinite(obj) ? String(obj) : 'null'; case 'Object': return objToSrc(obj); case 'String': return strToSrc(obj); default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null'; } }, mainWin = {}; focus.getFocusedElementForWindow(content, true, mainWin); mainWin = mainWin.value; var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location; var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g; if (selWin) { var rng = win.getSelection().getRangeAt(0); pEle = rng.commonAncestorContainer; ele = rng.cloneContents(); } else { pEle = doc.documentElement; ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true); }; while (pEle) { if (pEle.nodeType == 1) { clone = pEle.cloneNode(false); clone.appendChild(ele); ele = clone; }; pEle = pEle.parentNode }; var sel = doc.createElement('div'); sel.appendChild(ele); for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) { el = all[i]; if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href); return prev + encodeImg(url) + next; }); switch (el.nodeName.toLowerCase()) { case 'link': case 'style': case 'script': el.parentNode.removeChild(el); break; case 'a': case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break; case 'img': case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break; case 'audio': case 'video': case 'embed': case 'frame': case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break; case 'object': if (el.hasAttribute('data')) el.data = el.data; break; case 'form': if (el.hasAttribute('action')) el.action = el.action; break; } }; var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0]; meta.httpEquiv = 'content-type'; meta.content = 'text/html; charset=utf-8'; head.appendChild(meta); if (title) head.appendChild(title.cloneNode(true)); head.copyScript = function (unsafeWin) { if ('$' in unsafeWin) return; var f = doc.createElement('iframe'); f.src = 'about:blank'; f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;'); doc.documentElement.appendChild(f); var str, script = doc.createElement('script'); script.type = 'text/javascript'; for (var name in unsafeWin) { if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue; try { str = toSrc(unsafeWin[name]); if (!/\{\s*\[native code\]\s*\}/.test(str)) { script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n')); } } catch (e) {}; }; f.parentNode.removeChild(f); if (script.childNodes.length) this.nextSibling.appendChild(script); }; head.copyScript(win.wrappedJSObject || win); head.copyStyle = function (s) { if (!s) return; var style = doc.createElement('style'); style.type = 'text/css'; if (s.media && s.media.mediaText) style.media = s.media.mediaText; try { for (var i = 0, rule; rule = s.cssRules[i]; i++) { if (rule.type != 3) { if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) { var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href); if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url); return prev + url + next; }); style.appendChild(doc.createTextNode(css + '\n')); } } else { this.copyStyle(rule.styleSheet); } } } catch(e) { if (s.ownerNode) style = s.ownerNode.cloneNode(false); }; this.appendChild(style); }; for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]); head.appendChild(doc.createTextNode('\n')); var doctype = '', dt = doc.doctype; if (dt && dt.name) { doctype += '<!DOCTYPE ' + dt.name; if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22'; if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22'; doctype += '>\n'; }; var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop()); fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).trim(); fileName += "_" + new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։"); if (!/\.html?$/.test(fileName)) fileName += '.html'; sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', fileName]); }); // END hookClicks
Отсутствует
Dumby
Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?
«The Truth Is Out There»
Отсутствует
Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?
(async (bar, exp, tid, self) => CustomizableUI.createWidget(self = { id: "test-button", localized: false, label: "Test Button", tooltiptext: `ЛКМ: Design View CKM: folder UCF`, onCreated(btn) { btn.onclick = this.click; btn.toggleAttribute("context"); btn.setAttribute("image", "chrome://browser/skin/preferences/application.png"); }, exec(num, win) { tid = null; self[num](win); }, context(win) { var btn = win.document.getElementById(this.id); btn.removeAttribute("context"); btn.dispatchEvent(new win.MouseEvent("contextmenu", this.a)); btn.toggleAttribute("context"); }, a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}, click(e) { if (e.detail > 2) return; var n2 = e.button != 2; var dbl = e.detail == 2; var num = 16 * e.button + 8 * e.ctrlKey + 4 * e.shiftKey + 2 * e.altKey + dbl; if (!self[num]) { if (n2) return; num = "context"; for(var p in self.a) self.a[p] = e[p]; } var win = e.view; if (dbl) tid &&= win.clearTimeout(tid), self[num](win); else tid = win.setTimeout(self.exec, 300, num, win); }, 0(win) { // ЛКМ var btn = win.document.getElementById(this.id); btn.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey }, 1(win) { // Double Left Click win.alert("DBL Click"); }, 16: () => exp("UChrm", "user_chrome_files"), // СКМ }))( (win, bar) => win.SidebarUI.toggle(bar), (dir, sub) => { dir = Services.dirsvc.get(dir, Ci.nsIFile); sub && dir.append(sub); dir.exists() && dir.launch(); } );
Отсутствует
Dumby - ещё просьба вдобавок к первой:
подскажи код, чтобы выполнять скрипт из файла. То есть, кликом на кнопке выполняем внешний …/user_chrome_files/custom_scripts/user.js, в который должны быть переданы все текущие переменные, функции и т.п. той кнопки, из которой вызван внешний JS.
Может, это пригодиться для отладки скриптов без перезапуска браузера. А может есть более простой способ отладки части кода кнопки?
Наверное, код внешнего JS не должен попадать в startupCache, это верно?
Отсутствует
Dobrov
Как пример вызова функции из другого скрипта можно посмотреть здесь, выше и ниже. Скрипт с функцией тоже должен быть загружен. Не знаю, это ли было нужно.
https://forum.mozilla-russia.org/viewtopic.php?pid=796057#p796057
Отсутствует
при старте браузера кнопки Reader View в строке адреса нет, она появляется, когда открывается сайт
Значит слушать клики на родительском контейнере и проверять id кликнутого.
кликом на кнопке выполняем внешний …/user_chrome_files/custom_scripts/user.js, в который должны быть переданы все текущие переменные, функции и т.п. той кнопки, из которой вызван внешний JS
Наверное, код внешнего JS не должен попадать в startupCache, это верно?
Если scriptloader'ом по протоколу chrome:, то будет кэшироваться.
Можно использовать loadSubScriptWithOptions(),
но «все текущие переменные, функции» идут лесом,
разве что только в объект пробросить.
Services.scriptloader.loadSubScriptWithOptions( "chrome://user_chrome_files/content/custom_scripts/user.js", {ignoreCache: true, target: {myVariable1, myVariable2, myFunction}} );
То есть, нужно на кнопке Reader View по клику колёсиком включать/выключать режим "Адаптивный дизайн".
Желательно добавить перехват кликов в тот же UCF-скрипт, который расширяет возможности кнопок Меню и Загрузки
Вот, сводный пример. Добавляем после ucf.unloadlisteners.push(id);
..... var box = document.getElementById("page-action-buttons"); var key = document.getElementById("key_responsiveDesignMode"); var uri = Services.io.newURI("chrome://user_chrome_files/content/custom_scripts/user.js"); var boxLst = e => eval(Cu.readUTF8URI(uri)); box.addEventListener("auxclick", boxLst, true); addDestructor(() => box.removeEventListener("auxclick", boxLst, true));
if ( e.button == 1 && e.target.id == "pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_" ) e.stopImmediatePropagation(), key.doCommand();
this[136]
this[138], наверно.
Возможно ли сделать отдельную кнопочку для Вкл/Выкл "Адаптивного дизайна"?
А в чём конкретно затруднение?
Там же просто получаем <key> и вызываем doCommand();
(async id => CustomizableUI.createWidget({ get label() { var l10n = new (ChromeUtils.import("resource://devtools/shared/Loader.jsm") .require("devtools/shared/l10n")).MultiLocalizationHelper( "devtools/client/locales/startup.properties", "devtools/client/locales/menus.properties" ); this.tooltiptext = l10n.getFormatStr( "toolbox.buttons.responsive", Services.appinfo.OS == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M" ); delete this.label; return this.label = l10n.getStr("responsiveDesignMode.label"); }, id: "ucf-responsiveDesignMode-btn", localized: false, onCreated(btn) { btn._handleClick = this.click; btn.style.cssText = ` fill-opacity: 0 !important; -moz-context-properties: fill, fill-opacity !important; list-style-image: url(chrome://devtools/skin/images/command-responsivemode.svg) !important; `; }, click() { this.ownerDocument.getElementById(id).doCommand(); } }))("key_responsiveDesignMode");
Отсутствует
А в чём конкретно затруднение?
Там же просто получаем <key> и вызываем doCommand();скрытый текстВыделить кодКод:
(async id => CustomizableUI.createWidget({ get label() { var l10n = new (ChromeUtils.import("resource://devtools/shared/Loader.jsm") .require("devtools/shared/l10n")).MultiLocalizationHelper( "devtools/client/locales/startup.properties", "devtools/client/locales/menus.properties" ); this.tooltiptext = l10n.getFormatStr( "toolbox.buttons.responsive", Services.appinfo.OS == "Darwin" ? "Cmd+Opt+M" : "Ctrl+Shift+M" ); delete this.label; return this.label = l10n.getStr("responsiveDesignMode.label"); }, id: "ucf-responsiveDesignMode-btn", localized: false, onCreated(btn) { btn._handleClick = this.click; btn.style.cssText = ` fill-opacity: 0 !important; -moz-context-properties: fill, fill-opacity !important; list-style-image: url(chrome://devtools/skin/images/command-responsivemode.svg) !important; `; }, click() { this.ownerDocument.getElementById(id).doCommand(); } }))("key_responsiveDesignMode");
Для меня это не так просто.
Dumby, большое спасибо.
«The Truth Is Out There»
Отсутствует
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 пригодится многим, он позволит прописывать обработку кликов в одном скрипте и позволит «разгрузить» другие кнопки, не добавлять в них код обработки кликов.
Сделал финальный демо-скрипт, расширяющий возможности нескольких кнопок. Dumby, проверь, может я где-то накосячил или что-то можно сделать проще!
(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 "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; var hint = tooltips[trg.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("nav-bar"); var events = ["click", "mousedown", "wheel"]; root.addEventListener("mouseenter", onMouseenter, true); for(var type of events) root.addEventListener(type, listener, true); window.addEventListener("keydown", keydown_win); ucf_custom_script_win.unloadlisteners.push(id); ucf_custom_script_win[id] = {destructor() { root.removeEventListener("mouseenter", onMouseenter, true); for(var type of events) root.removeEventListener(type, listener, true); window.removeEventListener("keydown", keydown_win); }}; addDestructor = nextDestructor => { var {destructor} = ucf_custom_script_win[id]; ucf_custom_script_win[id].destructor = () => { try {destructor();} catch(ex) {Cu.reportError(ex);} nextDestructor(); } }; // end Hooks var {prefs, dirsvc} = Services, getIntPref = (p) => prefs.getIntPref(p, 100), c = msg => Services.console.logStringMessage("[HC] "+ msg), // отладка sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService), my_br = "ucf.tabbrowser-tabpanels.opacity", // яркость страниц css = `@-moz-document url(chrome://browser/content/browser.xhtml) { :is(${id})[rst] {filter: grayscale(1%) !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabbox {background-color: black !important;} :root:not([chromehidden*=toolbar]) #tabbrowser-tabpanels {opacity:${getIntPref(my_br)/100} !important;}}`, subst = "ucf-tabbrowser-tabpanels-opacity-style", url = `resource://${subst}/`; Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler) .setSubstitution(subst, Services.io.newURI("data:text/css," + encodeURIComponent(css))); sss.loadAndRegisterSheet(Services.io.newURI(url), sss.USER_SHEET); var st = InspectorUtils.getAllStyleSheets(document).find(s => s.href == url).cssRules[0].cssRules[2].style; var observer = () => st.setProperty("opacity", getIntPref(my_br)/100, "important"); prefs.addObserver(my_br, observer); this.removePrefObs = () => prefs.removeObserver(my_br, observer); // end яркость if (typeof IOUtils != "object") { // Firefox 78 ESR var {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm"); var PathUtils = {join: (...args) => OS.Path.join(...args)}; var IOUtils = {writeUTF8: (path, txt) => OS.File.writeAtomic(path, new TextEncoder().encode(txt))}; }; prefs.setBoolPref("browser.download.autohideButton", false); // не скрывать кнопку Загрузки str_cut = (s, cut = 33) => { // сократить/разбить строку return s.substring(0,cut) + `${s.length > cut - 1 ? `…\n…${s.substring(s.length -cut + 2, s.length)}`: ''}`; }, url_color = (color = "rgba(240,176,0,0.5)", ms = 300) => { // строка адреса мигает var u_alert = document.getElementById("urlbar-input-container"); u_alert.style.background = color; setTimeout(() => u_alert.style.background = "", ms); }, gClipboard = { get ch() { delete this.ch; return this.ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); }, write(str) { this.ch.copyStringToClipboard(str, Services.clipboard.kGlobalClipboard);} }, switchToTab = (url, but = window) => { // открыть вкладку | закрыть, если открыта for(var tab of but.ownerGlobal.gBrowser.tabs) if (tab.linkedBrowser.currentURI.spec == url) {but.ownerGlobal.gBrowser.removeTab(tab); return;}; // закрыть but.ownerGlobal.switchToTabHavingURI(url, true, {relatedToCurrent: true, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()}); }, showInStatusPanel = (info, time = 5000) => { StatusPanel = window.StatusPanel; if (StatusPanel.update.tid) clearTimeout(StatusPanel.update.tid) else { var {update} = StatusPanel; StatusPanel.update = () => {}; StatusPanel.update.ret = () => { StatusPanel.update = update; StatusPanel.update(); } } StatusPanel.update.tid = setTimeout(StatusPanel.update.ret, time); StatusPanel._label = info; }, Title = (max, title) => { // получить заголовок. без обрезки: max не указан, домен: max <0, + дата: max=0 if (!title) var title = document.title || gBrowser.selectedTab.label; if (max == undefined) return title; // заголовок как есть или ограничить длину, убрать служебные символы title = title.replace(/[\\\/?*\"'`]+/g,'').replace(/\s+/g,' ').replace(/[|<>]+/g,'_').replace(/:/g,'։').trim(); if ( max > 0 ) return title.slice(0, max); if ( max == 0) return title.slice(0, 100) +"_"+ new Date().toLocaleDateString('ru', {day: 'numeric', month: 'numeric', year: '2-digit'}) +'-'+ new Date().toLocaleTimeString().replace(/:/g, "։"); var host = decodeURIComponent(gURLBar.value); // max < 0 if (!/^file:\/\//.test(host)) host = host.replace(/^.*url=|https?:\/\/|www\.|\/.*/g,''); return host.replace(/^ru\.|^m\.|forum\./,'').replace(/^club\.dns/,'dns'); }, saveSelectionToTxt = async () => { // сохранить выделенный/весь текст страницы как .txt var msgName = id + ":Save:GetSelection", splice = saveURL.length == 10; var receiver = msg => { var args = ["data:text/plain," + encodeURIComponent(gBrowser.currentURI.spec + "\n\n" + msg.data), Title(0) + '.txt', null, false, true, null, window.document]; splice && args.splice(5, 0, null); saveURL(...args); showInStatusPanel("√ текст сохранён: "+ Title(0).slice(0, 60)); } messageManager.addMessageListener(msgName, receiver); addDestructor(() => messageManager.removeMessageListener(msgName, receiver)); var func = fm => { var res, fed, win = {}, fe = fm.getFocusedElementForWindow(content, true, win); var sel = (win = win.value).getSelection(); if (sel.isCollapsed) { var ed = fe && fe.editor; if (ed && ed instanceof Ci.nsIEditor) sel = ed.selection, fed = fe; } if (sel.isCollapsed) fed && fed.blur(), docShell.doCommand("cmd_selectAll"), res = win.getSelection().toString(), docShell.doCommand("cmd_selectNone"), fed && fed.focus(); res = res || sel.toString(); /\S/.test(res) && sendAsyncMessage("saveSelectionToTxt", res); } var url = "data:;charset=utf-8," + encodeURIComponent(`(${func})`.replace("saveSelectionToTxt", msgName)) + '(Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager));'; (saveSelectionToTxt = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, save = async () => { // SingleHtml by Лекс, правка: Dumby, Dobrov var msgName = id + "ucfDwnldsBtnSaveSnapshotToHTML"; var write = IOUtils.writeUTF8 ? "writeUTF8" : "writeAtomicUTF8"; var msgListener = async msg => { var [fileContent, fileName] = msg.data, dir; // fileName: выделенный текст или null try {dir = prefs.getComplexValue("browser.download.dir", Ci.nsIFile);} catch {dir = dirsvc.get("DfltDwnld", Ci.nsIFile);} var arr = prefs.getStringPref("ucf_save.dirs","_Web||_Images|0").split('|').slice(0,2); //subdir: title|host arr[1] = (arr[1] == "0") ? Title(100) : (arr[1] == "1") ? Title(-1) : ""; // имя вкладки или домен arr.forEach(dir.append); // ucf_save.dirs = "_Web||_Pics|1" HTML сохранится в папку [Загрузки]/_Web/label dir.exists() && dir.isDirectory() || dir.create(dir.DIRECTORY_TYPE, 0o777); // создать, если не существует… var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); file.initWithPath(dir.path); if (!fileName) fileName = Title(100); // убрать служебные символы dir.append(Title(0, fileName) +'.html'); await IOUtils[write](dir.path, fileContent) && showInStatusPanel("√ страница записана: " + fileName.slice(0, 60)); var d = await Downloads.createDownload({ source: "about:blank", target: FileUtils.File(dir.path)}); // Fake download (await Downloads.getList(Downloads.ALL)).add(d); d.refresh(d.succeeded = true); // кнопка Загрузки мигает } messageManager.addMessageListener(msgName, msgListener); addDestructor(() => messageManager.removeMessageListener(msgName, msgListener)); var svc = 'globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services'; var url = "data:;charset=utf8," + encodeURIComponent(`(${func})(${svc});`.replace("%MSG_NAME%", msgName)); (save = () => gBrowser.selectedBrowser.messageManager.loadFrameScript(url, false))(); }, bright = (trg, forward, val) => { // wheel if (!val) var val = getIntPref(my_br) + (forward ? 5 : -5); val = val > 100 ? 100 : val < 20 ? 20 : val; prefs.setIntPref(my_br, val), trg.toggleAttribute("rst"), showInStatusPanel("☀ Яркость страниц: "+ val +"%"); }, help = (btn) => { // встроенная справка var help_ucf = ['chrome://user_chrome_files/content/help.html', 'http://forum.puppyrus.org/index.php?topic=22762']; var newURI = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry).convertChromeURL(Services.io.newURI(help_ucf[0])); // .spec = file:/// (newURI.QueryInterface(Ci.nsIFileURL).file.exists()) ? switchToTab(help_ucf[0]) : switchToTab(help_ucf[1]); }, GetSelection = (mM = gBrowser.selectedBrowser.messageManager) => { mM.addMessageListener('getSelect', function sel_listener(msg) { window.seltxt = msg.data; mM.removeMessageListener('getSelect', sel_listener, true); }); mM.loadFrameScript('data:,sendAsyncMessage("getSelect",content.document.getSelection().toString())',false); }, data = { "#downloads-button": { mousedownTarget: true, 4() { // Double Left Click - Обзор папки «Загрузки» c("DW Double Left Click"); Downloads.getSystemDownloadsDirectory().then(path => FileUtils.File(path).launch(), Cu.reportError); }, 128() { saveSelectionToTxt();}, // СКМ Click (сохранить .txt) 256() { save();}, // ПКМ Click (Single HTML) 260(btn) { // Double ПКМ Click var pref = "permissions.default.image"; var one = prefs.getIntPref(pref) == 1; prefs.setIntPref(pref, one ? 2 : 1); btn.style.filter = one ? "hue-rotate(180deg) brightness(95%)" : ""; BrowserReload(); }, }, "#PanelUI-menu-button": { mousedownTarget: true, 1(btn) { help(btn);}, // Long Press 8() { gCustomizeMode.enter();}, //ЛКМ + Alt Персонализация 16(btn) { help(btn);}, // Shift + ЛКМ 4() { goQuitApplication();}, // Double Left Click 128() { windowState != STATE_MAXIMIZED ? maximize() : restore();}, // СКМ 136(btn) { BrowserFullScreen();}, // Alt + СКМ 132() { BrowserReloadSkipCache();}, // СКМ Double 256() { minimize();}, // ПКМ 260(btn) { btn.ownerGlobal.undoCloseTab();}, // ПКМ Double Right Click 264(btn) { switchToTab('about:performance');}, // Alt + ПКМ 280(btn) { // Shift + Alt + ПКМ var obj = ChromeUtils.import("resource://devtools/shared/Loader.jsm").require("devtools/client/menus").menuitems.find(menuitem => menuitem.id == "menu_devtools_remotedebugging"); (this[280] = target => obj.oncommand({target}))(btn); // запуск пункта меню, у которого нет HotKey }, }, "#pageAction-urlbar-_2495d258-41e7-4cd5-bc7d-ac15981f064e_": { // Reader View Button 128(btn) { // СКМ btn.ownerDocument.getElementById("key_responsiveDesignMode").doCommand(); // запуск пункта меню с HotKey if (gBrowser.selectedBrowser.browsingContext.inRDMPane) BrowserReload(); }, 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 264(btn) { // Alt + ПКМ translate(gBrowser.selectedBrowser.messageManager, 1); }, 1(btn) { // Shift + ПКМ translate(gBrowser.selectedBrowser.messageManager); }, }, "#star-button-box": { 1() { // Left Long Press c("Star Left Long Press"); }, 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± // 128(btn) { // СКМ // switchToTab('about:config'); // }, 256() { // ПКМ window.undoCloseTab(); }, }, "#identity-box": { // Замок 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 128(trg, forward) { bright(trg, forward, 100);}, // СКМ 256(btn) { // ПКМ gClipboard.write(gURLBar.value); url_color(), showInStatusPanel("в буфере: "+ gURLBar.value.slice(0, 80)); }, }, "#tracking-protection-icon-container": { // Защита 2(trg, forward) { bright(trg, forward);}, // яркость по wheel ± 128(trg, forward) { bright(trg, forward, 100);}, // СКМ }, "#identity-permission-box": { 2(trg, forward) { // wheel c("Identity Permisson Box Wheel " + (forward ? "forward" : "backward")); } }, "#identity-icon-box": { 16() { // Shift + Left Click c("Identity Icon Box Shift + Left Click"); }, }, }; // end Clicks, HotKeys ================================================== })("hookClicks-and-tooltips", ({io, focus}) => { // SingleHTML не сохраняет svg графику var resolveURL = function (url, base) { try { return io.newURI(url, null, io.newURI(base)).spec;} catch {} }, getSelWin = function (w) { if (w.getSelection().toString()) return w; for (var i = 0, f, r; f = w.frames[i]; i++) { try { if (r = getSelWin(f)) return r;} catch(e) {} } }, encodeImg = function (src, obj) { var canvas, img, ret = src; if (/^https?:\/\//.test(src)) { canvas = doc.createElement('canvas'); if (!obj || obj.nodeName.toLowerCase() != 'img') { img = doc.createElement('img'); img.src = src; } else img = obj; if (img.complete) try{ canvas.width = img.width; canvas.height = img.height; canvas.getContext('2d').drawImage(img, 0, 0); ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png')); } catch (e) {}; if (img != obj) img.src = 'about:blank'; }; return ret; }, toSrc = function (obj) { var strToSrc = function (str) { var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'}; while (chr = str.charAt(i++)) { ret += meta[chr] || chr; }; return '\x22' + ret + '\x22'; }, arrToSrc = function (arr) { var ret = []; for (var i = 0; i < arr.length; i++) { ret[i] = toSrc(arr[i]) || 'null'; }; return '[' + ret.join(',') + ']'; }, objToSrc = function (obj) { var val, ret = []; for (var prop in obj) { if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val); }; return '{' + ret.join(',') + '}'; }; switch (Object.prototype.toString.call(obj).slice(8, -1)) { case 'Array': return arrToSrc(obj); case 'Boolean': case 'Function': case 'RegExp': return obj.toString(); case 'Date': return 'new Date(' + obj.getTime() + ')'; case 'Math': return 'Math'; case 'Number': return isFinite(obj) ? String(obj) : 'null'; case 'Object': return objToSrc(obj); case 'String': return strToSrc(obj); default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null'; } }, mainWin = {}; focus.getFocusedElementForWindow(content, true, mainWin); mainWin = mainWin.value; var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location; var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g; if (selWin) { var rng = win.getSelection().getRangeAt(0); pEle = rng.commonAncestorContainer; ele = rng.cloneContents(); } else { pEle = doc.documentElement; ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true); }; while (pEle) { if (pEle.nodeType == 1) { clone = pEle.cloneNode(false); clone.appendChild(ele); ele = clone; }; pEle = pEle.parentNode }; var sel = doc.createElement('div'); sel.appendChild(ele); for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) { el = all[i]; if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href); return prev + encodeImg(url) + next; }); switch (el.nodeName.toLowerCase()) { case 'link': case 'style': case 'script': el.parentNode.removeChild(el); break; case 'a': case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break; case 'img': case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break; case 'audio': case 'video': case 'embed': case 'frame': case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break; case 'object': if (el.hasAttribute('data')) el.data = el.data; break; case 'form': if (el.hasAttribute('action')) el.action = el.action; break; } }; var head = ele.insertBefore(doc.createElement('head'), ele.firstChild), meta = doc.createElement('meta'), sheets = doc.styleSheets, title = doc.getElementsByTagName('title')[0]; meta.httpEquiv = 'content-type'; meta.content = 'text/html; charset=utf-8'; head.appendChild(meta); if (title) head.appendChild(title.cloneNode(true)); head.copyScript = function (unsafeWin) { if ('$' in unsafeWin) return; var f = doc.createElement('iframe'); f.src = 'about:blank'; f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;'); doc.documentElement.appendChild(f); var str, script = doc.createElement('script'); script.type = 'text/javascript'; for (var name in unsafeWin) { if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue; try { str = toSrc(unsafeWin[name]); if (!/\{\s*\[native code\]\s*\}/.test(str)) { script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n')); } } catch (e) {}; }; f.parentNode.removeChild(f); if (script.childNodes.length) this.nextSibling.appendChild(script); }; head.copyScript(win.wrappedJSObject || win); head.copyStyle = function (s) { if (!s) return; var style = doc.createElement('style'); style.type = 'text/css'; if (s.media && s.media.mediaText) style.media = s.media.mediaText; try { for (var i = 0, rule; rule = s.cssRules[i]; i++) { if (rule.type != 3) { if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) { var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) { if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href); if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url); return prev + url + next; }); style.appendChild(doc.createTextNode(css + '\n')); } } else { this.copyStyle(rule.styleSheet); } } } catch(e) { if (s.ownerNode) style = s.ownerNode.cloneNode(false); }; this.appendChild(style); }; for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]); head.appendChild(doc.createTextNode('\n')); var doctype = '', dt = doc.doctype; if (dt && dt.name) { doctype += '<!DOCTYPE ' + dt.name; if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22'; if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22'; doctype += '>\n'; }; sendAsyncMessage("%MSG_NAME%", [doctype + sel.innerHTML +'\n<a href='+ (loc.protocol != 'data:' ? loc.href : 'data:uri') +'><small><blockquote>источник: '+ new Date().toLocaleString("ru") +'</blockquote></small></a>', selWin ? win.getSelection().toString().slice(0, 200) : undefined]); // выделенный текст }); // END hookClicks
Отсутствует
Dumby посмотрите пожалуйста кнопку toggleRestartlessAddons в ней при ПКМ не появляется сообщение в правом нижнем углу а в консоле появляеться ошибка
Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data
forgetClosedTab chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:794
removeTab chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:805
waitTimer chrome://user_chrome_files/content/custom_scripts/custom_script.js line 237 > Function:809
// http://infocatcher.ucoz.net/js/cb/toggleRestartlessAddons.js // https://forum.mozilla-russia.org/viewtopic.php?id=57948 // https://github.com/Infocatcher/Custom_Buttons/tree/master/Toggle_Restartless_Add-ons // Toggle Restartless Add-ons button for Custom Buttons // (code for "initialization" section) // Also the code can be used from main window context (as Mouse Gestures code, for example) // Also you can check for add-ons updates using right-click: // copy all code from // https://github.com/Infocatcher/Custom_Buttons/blob/master/Check_for_Addons_Updates/checkForAddonsUpdates.js // after "//== Check for Addons Updates begin" // See "var style = " to modify styles for specific add-ons // (c) Infocatcher 2013-2019 // version 0.1.3pre4 - 2020-01-01 var options = { addonTypes: ["extension", "plugin"], // Possible values: "extension", "plugin" // From extensions: "userstyle" (Stylish), "greasemonkey-user-script" (Greasemonkey), "userscript" (Scriptish) // (swap to reorder in the menu) showVersions: 0, // 0 - don't show versions // 1 - show after name: "Addon Name 1.2" // 2 - show as "acceltext" (in place for hotkey text) showHidden: 1, // 0 - don't show hidden add-ons // -1 - show only enabled hidden add-ons (e.g. to track new items) // 1 - show all hidden add-ons sort: { enabled: 0, clickToPlay: 0, disabled: 0 // Sort order: // 0, 0, 0 - sort add-ons of each type alphabetically // 0, 0, 1 - show enabled add-ons (of each type) first // 0, 1, 2 - enabled add-ons, then click-to-play and then disabled }, closeMenu: false, // Close menu after left-click closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins // Use Shift+click to invert closeMenu* behavior }; var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; var mp = document.createElementNS(xulns, "menupopup"); mp.setAttribute("onpopupshowing", "this.updateMenu();"); mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+ mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);"); mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);"); mp.setAttribute("oncontextmenu", "return false;"); mp.setAttribute("onpopuphidden", "this.destroyMenu();"); var tb = this.parentNode; if(tb && tb.getAttribute("orient") == "vertical") { // https://addons.mozilla.org/firefox/addon/vertical-toolbar/ var isRight = tb.parentNode.getAttribute("placement") == "right"; mp.setAttribute("position", isRight ? "start_before" : "end_before"); } var cleanupTimer = 0; mp.updateMenu = function() { clearTimeout(cleanupTimer); addStyle(); getRestartlessAddons(options.addonTypes, function(addons) { var df = document.createDocumentFragment(); var prevType; function sortPosition(addon) { if("STATE_ASK_TO_ACTIVATE" in AddonManager && addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) return options.sort.clickToPlay; if(addon.isActive) return options.sort.enabled; return options.sort.disabled; } function key(addon) { return options.addonTypes.indexOf(addon.type) + "\n" + sortPosition(addon) + "\n" + addon.name.toLowerCase(); } addons.sort(function(a, b) { var ka = key(a); var kb = key(b); return ka == kb ? 0 : ka < kb ? -1 : 1; }).forEach(function(addon) { var type = addon.type; if(prevType && type != prevType) df.appendChild(document.createElementNS(xulns, "menuseparator")); prevType = type; var icon = addon.iconURL || addon.icon64URL; var mi = document.createElementNS(xulns, "menuitem"); mi.className = "menuitem-iconic"; var label = addon.name; if(options.showVersions == 1) label += " " + addon.version; else if(options.showVersions == 2) mi.setAttribute("acceltext", addon.version); mi.setAttribute("label", label); mi.setAttribute("image", icon || mp.icons[type] || ""); if(!icon && mp.icons.useSVG) mi.style.fill = "#15c"; var tip = addon.description || ""; var delay = "delayedStartupAddons" in Services && Services.delayedStartupAddons[addon.id] || null; var isDelayed = delay !== null; mi.classList.toggle("toggleRestartlessAddons-isDelayed", isDelayed); if(isDelayed) tip = "[Delayed Startup: " + delay.toLocaleString() + "]" + (tip ? "\n" + tip : ""); tip && mi.setAttribute("tooltiptext", tip); mi.classList.toggle("toggleRestartlessAddons-isHidden", addon.hidden || false); setDisabled(mi, addon.userDisabled); mi._cbAddon = addon; df.appendChild(mi); }); mp.textContent = ""; mp.appendChild(df); }); }; mp.handleEvent = function(e) { var mi = e.target; if(!("_cbAddon" in mi)) return; var addon = mi._cbAddon; if(e.type == "mousedown") { var closeMenu = isAskToActivateAddon(addon) ? options.closeMenuClickToPlay : options.closeMenu; if(e.shiftKey) closeMenu = !closeMenu; mi.setAttribute("closemenu", closeMenu ? "auto" : "none"); return; } var hasMdf = hasModifier(e); if(e.type == "command" && (!hasMdf || e.shiftKey)) { let newDis = setNewDisabled(addon); setDisabled(mi, newDis); } else if(e.type == "command" && hasMdf || e.type == "click" && e.button == 1) { openAddonPage(addon); closeMenus(mi); } else if(e.type == "click" && e.button == 2) { if(openAddonOptions(addon)) closeMenus(mi); } }; mp.destroyMenu = function() { removeStyle(); clearTimeout(cleanupTimer); cleanupTimer = setTimeout(function() { mp.textContent = ""; }, 5000); }; mp.icons = { get platformVersion() { delete this.platformVersion; return this.platformVersion = parseFloat(Services.appinfo.platformVersion); }, get useSVG() { delete this.useSVG; return this.useSVG = Services.appinfo.name == "Firefox" && this.platformVersion >= 57; }, get plugin() { delete this.plugin; return this.plugin = this.useSVG ? this.platformVersion >= 65 ? "chrome://global/skin/plugins/pluginGeneric.svg" : "chrome://mozapps/skin/plugins/pluginGeneric.svg" : "chrome://mozapps/skin/plugins/pluginGeneric-16.png"; }, get extension() { delete this.extension; return this.extension = this.useSVG ? this.platformVersion >= 76 ? "chrome://mozapps/skin/extensions/extensionGeneric.svg" // Or chrome://mozapps/skin/extensions/extension.svg : "chrome://mozapps/skin/extensions/extensionGeneric-16.svg" : "chrome://mozapps/skin/extensions/extensionGeneric-16.png"; } }; function isAskToActivateAddon(addon) { return addon.type == "plugin" && "STATE_ASK_TO_ACTIVATE" in AddonManager && Services.prefs.getBoolPref("plugins.click_to_play", true); } function setNewDisabled(addon) { var newDis = getNewDisabled(addon); var oldDis = addon.userDisabled; try { addon.userDisabled = newDis; } catch(e) { // Error: Cannot disable hidden add-on firefox@getpocket.com _log("Can't set addon.userDisabled to " + newDis + ", error:\n" + e); if(addon.hidden) setNewDisabledRaw(addon, newDis); } var realDis = addon.userDisabled; if(realDis != newDis && addon.type == "extension") { // Firefox 62+? Weird things happens setNewDisabledRaw(addon, newDis); realDis = addon.userDisabled; } if(realDis != newDis) { // We can't enable vulnerable plugins let err = "Can't set addon.userDisabled to " + newDis + ", real value: " + realDis; if(newDis) { _log(err + "\nSTATE_ASK_TO_ACTIVATE not supported?"); newDis = false; } else { _log(err + "\nVulnerable plugin?"); if(oldDis == AddonManager.STATE_ASK_TO_ACTIVATE) newDis = true; else newDis = AddonManager.STATE_ASK_TO_ACTIVATE; } addon.userDisabled = newDis; } ensureSpecialDisabled(addon, newDis); return addon.userDisabled; } function getNewDisabled(addon) { // disabled -> STATE_ASK_TO_ACTIVATE -> enabled -> ... var curDis = addon.userDisabled; var newDis; if("STATE_ASK_TO_ACTIVATE" in AddonManager && curDis == AddonManager.STATE_ASK_TO_ACTIVATE) newDis = false; else if(!curDis) newDis = true; else { if(isAskToActivateAddon(addon)) newDis = AddonManager.STATE_ASK_TO_ACTIVATE; else newDis = false; } return newDis; } function setNewDisabledRaw(addon, newDis) { _log("Let's try set addon.userDisabled using raw hack"); let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {}); if("XPIDatabase" in g && "updateAddonDisabledState" in g.XPIDatabase) { // Firefox 61+ let rawAddon = g.XPIDatabase.getAddons().find(function(rawAddon) { return rawAddon.id == addon.id; }); g.XPIDatabase.updateAddonDisabledState( rawAddon, g.XPIDatabase.updateAddonDisabledState.length == 1 // Firefox 74+ ? { userDisabled: newDis } : newDis ); } else if("eval" in g) { // See "set userDisabled(val)" let addonFor = g.eval("addonFor"); let rawAddon = addonFor(addon); //rawAddon.userDisabled = newDis; g.XPIProvider.updateAddonDisabledState(rawAddon, newDis); } else { // Firefox 57+? See https://forum.mozilla-russia.org/viewtopic.php?pid=745272#p745272 updateAddonDisabledState(addon, newDis); } } function updateAddonDisabledState(addon, newDis) { var nsvo = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {}); var key = "_cbToggleRestartlessAddonsData"; var url = URL.createObjectURL(new Blob([ "XPIProvider.updateAddonDisabledState(addonFor(this." + key + "[0]), this." + key + "[1]); delete this." + key + ";" ])); addDestructor(function() { URL.revokeObjectURL(url); }); (updateAddonDisabledState = function(addon, newDis) { nsvo[key] = [addon, newDis]; Services.scriptloader.loadSubScript(url, nsvo); })(addon, newDis); } function setDisabled(mi, disabled) { var askToActivate = "STATE_ASK_TO_ACTIVATE" in AddonManager && disabled == AddonManager.STATE_ASK_TO_ACTIVATE; var cl = mi.classList; cl.toggle("toggleRestartlessAddons-askToActivate", askToActivate); cl.toggle("toggleRestartlessAddons-disabled", disabled && !askToActivate); } function ensureSpecialDisabled(addon, newDis) { if(addon.id == "screenshots@mozilla.org") Services.prefs.setBoolPref("extensions.screenshots.disabled", newDis); } if( this instanceof XULElement // Custom Buttons && typeof event == "object" && !("type" in event) && typeof _phase == "string" && _phase == "init" // Initialization ) { this.type = "menu"; this.orient = "horizontal"; this.appendChild(mp); this.onmouseover = function(e) { if(e.target != this) return; Array.prototype.some.call( this.parentNode.getElementsByTagName("*"), function(node) { if( node != this && node.namespaceURI == xulns // See https://github.com/Infocatcher/Custom_Buttons/issues/28 //&& node.boxObject //&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject && "open" in node && node.open && node.getElementsByTagName("menupopup").length ) { node.open = false; this.open = true; return true; } return false; }, this ); }; this.onmousedown = function(e) { if(e.target == this && e.button == 0 && hasModifier(e)) e.preventDefault(); }; this.oncontextmenu = function(e) { if(e.target == this && !hasModifier(e) && hasUpdater()) e.preventDefault(); }; this.onclick = function(e) { if(e.target != this) return; if(e.button == 0 && hasModifier(e) || e.button == 1) openAddonsManager(); else if(e.button == 2 && !hasModifier(e) && hasUpdater()) checkForAddonsUpdates.call(this); }; } else { // Mouse gestures or something other... let e; if(typeof event == "object" && event instanceof Event && "screenX" in event) // FireGestures e = event; else if( this instanceof Components.interfaces.nsIDOMChromeWindow && "mgGestureState" in window && "endEvent" in mgGestureState // Mouse Gestures Redox ) e = mgGestureState.endEvent; else { let anchor = this instanceof XULElement && this || window.gBrowser && gBrowser.selectedBrowser || document.documentElement; if("boxObject" in anchor) { let bo = anchor.boxObject; e = { screenX: bo.screenX, screenY: bo.screenY }; if(this instanceof XULElement) e.screenY += bo.height; } } if(!e || !("screenX" in e)) throw new Error("[Toggle Restartless Add-ons]: Can't get event object"); document.documentElement.appendChild(mp); mp.addEventListener("popuphidden", function destroy(e) { mp.removeEventListener(e.type, destroy, false); setTimeout(function() { mp.destroyMenu(); mp.parentNode.removeChild(mp); }, 0); }, false); mp.openPopupAtScreen(e.screenX, e.screenY); } function getRestartlessAddons(addonTypes, callback, context) { if(!("AddonManager" in window)) Components.utils.import("resource://gre/modules/AddonManager.jsm"); if(!("Services" in window)) Components.utils.import("resource://gre/modules/Services.jsm"); var then, promise = AddonManager.getAddonsByTypes(addonTypes, then = function(addons) { callback.call(context, addons.filter(function(addon) { var ops = addon.operationsRequiringRestart; return !addon.appDisabled && !(ops & AddonManager.OP_NEEDS_RESTART_ENABLE || ops & AddonManager.OP_NEEDS_RESTART_DISABLE) && ( !addon.hidden || options.showHidden > 0 || options.showHidden == -1 && !addon.userDisabled ) && (addon.iconURL || "").substr(0, 29) != "resource://search-extensions/"; })); }); promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+ } function openAddonOptions(addon) { // Based on code from chrome://mozapps/content/extensions/extensions.js // Firefox 21.0a1 (2013-01-27) var optionsURL = addon.optionsURL; if(!addon.isActive || !optionsURL) return false; if(addon.type == "plugin") // No options for now! return false; if( addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE || NaN) || addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_INFO || NaN) || addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_BROWSER || NaN) ) openAddonPage(addon, true); else if(addon.optionsType == AddonManager.OPTIONS_TYPE_TAB && "switchToTabHavingURI" in window) switchToTabHavingURI(optionsURL, true); else { let windows = Services.wm.getEnumerator(null); while(windows.hasMoreElements()) { let win = windows.getNext(); if(win.document.documentURI == optionsURL) { win.focus(); return true; } } // Note: original code checks browser.preferences.instantApply and may open modal windows window.openDialog(optionsURL, "", "chrome,titlebar,toolbar,centerscreen,dialog=no"); } return true; } function openAddonsManager(view) { var openAddonsMgr = window.BrowserOpenAddonsMgr // Firefox || window.openAddonsMgr // Thunderbird || window.toEM; // SeaMonkey openAddonsMgr(view); } function openAddonPage(addon, scrollToPreferences) { var platformVersion = parseFloat( Services.appinfo.name == "Pale Moon" ? Services.appinfo.version : Services.appinfo.platformVersion ); scrollToPreferences = scrollToPreferences && platformVersion >= 12 ? "/preferences" : ""; openAddonsManager("addons://detail/" + encodeURIComponent(addon.id) + scrollToPreferences); } function hasModifier(e) { return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey; } function addStyle() { if(addStyle.hasOwnProperty("_style")) return; var style = '\ .toggleRestartlessAddons-isDelayed > .menu-iconic-text {\n\ opacity: 0.75;\n\ color: #070;\n\ }\n\ .toggleRestartlessAddons-isHidden > .menu-iconic-text {\n\ color: #609;\n\ }\n\ .toggleRestartlessAddons-disabled > .menu-iconic-left {\n\ opacity: 0.4;\n\ }\n\ .toggleRestartlessAddons-disabled > .menu-iconic-text,\n\ .toggleRestartlessAddons-disabled > .menu-accel-container {\n\ opacity: 0.5;\n\ }\n\ .toggleRestartlessAddons-askToActivate {\n\ color: -moz-nativehyperlinktext;\n\ }'; addStyle._style = document.insertBefore( document.createProcessingInstruction( "xml-stylesheet", 'href="' + "data:text/css," + encodeURIComponent(style) + '" type="text/css"' ), document.documentElement ); } function removeStyle() { if(!addStyle.hasOwnProperty("_style")) return; var s = addStyle._style; s.parentNode.removeChild(s); delete addStyle._style; } function closeMenus(node) { // Based on function closeMenus from chrome://browser/content/utilityOverlay.js for(; node && "tagName" in node; node = node.parentNode) { if( node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" && (node.localName == "menupopup" || node.localName == "popup") ) node.hidePopup(); } } function _log(s) { if(typeof LOG == "function") // Custom Buttons LOG(s); else // Or something else Services.console.logStringMessage("Toggle Restartless Add-ons: " + s); } function hasUpdater() { var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1; hasUpdater = function() { return has; }; return has; } function checkForAddonsUpdates() { //== Check for Addons Updates begin // http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js // https://forum.mozilla-russia.org/viewtopic.php?id=57958 // https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates // Check for Addons Updates button for Custom Buttons // (code for "code" section) // (c) Infocatcher 2012-2021 // version 0.1.6pre4 - 2021-03-28 // Button just open hidden tab with about:addons and trigger built-in "Check for Updates" function. // And show tab, if found updates. (function() { var btn = this instanceof XULElement ? this : { // Launched not from custom button image: "", // Base64-encoded icon (if empty, will be used "imgLoading") label: "Check for Addons Updates", tooltipText: "" }; if("_cb_disabled" in btn) return; btn._cb_disabled = true; if(!("Services" in window)) Components.utils.import("resource://gre/modules/Services.jsm"); var app = Services.appinfo.name; var pv = parseFloat(Services.appinfo.platformVersion); var ADDONS_URL = "about:addons"; var progressIcon = new ProgressIcon(btn); var image = btn.image || progressIcon.imgLoading; var tip = btn.tooltipText; btn.tooltipText = "Open " + ADDONS_URL + "…"; var tab, browser, gBrowser; var tbTabInfo, tbTab; var trgWindow = Services.wm.getMostRecentWindow("navigator:browser") || app == "Thunderbird" && Services.wm.getMostRecentWindow("mail:3pane") || window; var trgDocument = trgWindow.document; var tabmail = trgDocument.getElementById("tabmail"); if(tabmail && app == "Thunderbird") { // Note: SeaMonkey doesn't support content tabs in mail window let addonsWin; let receivePong = function(subject, topic, data) { addonsWin = subject; }; Services.obs.addObserver(receivePong, "EM-pong", false); Services.obs.notifyObservers(null, "EM-ping", ""); Services.obs.removeObserver(receivePong, "EM-pong"); if(addonsWin) { let rootWindow = addonsWin .QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) .QueryInterface(Components.interfaces.nsIDocShellTreeItem) .rootTreeItem .QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIDOMWindow); tabmail = rootWindow.document.getElementById("tabmail"); tbTabInfo = tabmail.getBrowserForDocument(addonsWin); tbTab = tab = tbTabInfo.tabNode; processAddonsTab(addonsWin); } else { Services.obs.addObserver(function observer(subject, topic, data) { Services.obs.removeObserver(observer, topic); if(subject.document.readyState == "complete") processAddonsTab(subject); else { subject.addEventListener("load", function onLoad(e) { subject.removeEventListener(e.type, onLoad, false); processAddonsTab(subject); }, false); } }, "EM-loaded", false); // See openAddonsMgr() -> openContentTab() tbTabInfo = tabmail.openTab("contentTab", { contentPage: ADDONS_URL, clickHandler: "specialTabs.siteClickHandler(event, /addons\.mozilla\.org/);", background: true }); tbTab = tab = tbTabInfo.tabNode; tbTab.collapsed = true; // Note: dontSelectHiddenTab() not implemented } } else if("gBrowser" in trgWindow && trgWindow.gBrowser.tabs) { let isPending = false; let ws = Services.wm.getEnumerator("navigator:browser"); windowsLoop: while(ws.hasMoreElements()) { let w = ws.getNext(); let tabs = w.gBrowser.tabs; for(let i = 0, l = tabs.length; i < l; ++i) { let t = tabs[i]; if( !t.closing && t.linkedBrowser && t.linkedBrowser.currentURI.spec == ADDONS_URL ) { tab = t; break windowsLoop; } } } gBrowser = trgWindow.gBrowser; if(!tab) { tab = gBrowser.addTab(ADDONS_URL, { triggeringPrincipal: "Services" in window // Firefox 63+ && Services.scriptSecurityManager && Services.scriptSecurityManager.getSystemPrincipal() }); tab.collapsed = true; tab.closing = true; // See "visibleTabs" getter in chrome://browser/content/tabbrowser.xml trgWindow.addEventListener("TabSelect", dontSelectHiddenTab, false); } else if( tab.getAttribute("pending") == "true" // Gecko >= 9.0 || tab.linkedBrowser.contentDocument.readyState == "uninitialized" // || tab.linkedBrowser.__SS_restoreState == 1 ) isPending = true; browser = tab.linkedBrowser; if( isPending || browser.webProgress.isLoadingDocument || browser.currentURI.spec == "about:blank" // Firefox 79+ ) { browser.addEventListener("load", processAddonsTab, true); if(isPending) { if(pv >= 41) { // Workaround to correctly restore pending tab // See https://github.com/Infocatcher/Custom_Buttons/issues/39 let selTab = gBrowser.selectedTab; gBrowser.selectedTab = tab; gBrowser.selectedTab = selTab; } else { browser.reload(); } } } else { processAddonsTab(); } } else { progressIcon.restore(); btn.tooltipText = tip; delete btn._cb_disabled; Services.prompt.alert(window, btn.label, "Error: Can't find supported window!"); return; } function processAddonsTab(e, again) { var doc; if(e && e instanceof Components.interfaces.nsIDOMWindow) { doc = e.document; } else if(e) { doc = e.target; if(doc.location != ADDONS_URL) return; browser.removeEventListener(e.type, processAddonsTab, true); } else { doc = browser.contentDocument; } btn.tooltipText = "Process " + ADDONS_URL + "…"; progressIcon.loading(); var origAttr = "_cb_checkForAddonsUpdates_origImage"; if(!tab.hasAttribute(origAttr)) { var link = doc.querySelector('link[rel="shortcut icon"]'); // Not loaded yet? tab.setAttribute(origAttr, link && link.href || tab.image); } tab.image = image; var fu = $("cmd_findAllUpdates"); if(!fu) { // Firefox 72+ var win = doc.defaultView; var vb = doc.getElementById("html-view-browser"); if(!vb) { if(!HTMLHtmlElement.isInstance(doc.documentElement)) { // Firefox 87+ win.setTimeout(processAddonsTab, 20, win); return; } vb = browser; } if(!again) { // Strange errors happens // chrome://mozapps/content/extensions/aboutaddons.js // getTelemetryViewName() -> el.closest(...) is null win.setTimeout(processAddonsTab, 20, win, true); return; } var vbDoc = vb.contentDocument; fu = vbDoc.querySelector('[action="check-for-updates"]'); var um = vbDoc.getElementById("updates-message"); } var notFound = $("updates-noneFound") || { get hidden() { return um.getAttribute("state") != "none-found"; } }; var updated = $("updates-installed") || { get hidden() { return um.getAttribute("state") != "installed"; } }; // Avoid getting false results from the past update check (may not be required for "noneFound") if(um) { // Firefox 72+ um.hidden = true; um.removeAttribute("state"); } else { notFound.hidden = updated.hidden = true; } //fu.doCommand(); fu.click(); function localize(node, key, callback) { if(um) { // Firefox 72+ doc.l10n.formatValue(key).then(function(s) { callback(s || key); }, Components.utils.reportError); return; } callback(node.getAttribute("value") || key); } var inProgress = $("updates-progress") || { get hidden() { return um.getAttribute("state") != "updating"; } }; localize(inProgress, "addon-updates-updating", function(s) { btn.tooltipText = s; }); var waitTimer = setInterval(function() { if(!doc.defaultView || doc.defaultView.closed) { stopWait(); notify("Tab with add-ons manager was closed!"); return; } if(!inProgress.hidden) return; var autoUpdate = $("utils-autoUpdateDefault") || vbDoc.querySelector('[action="set-update-automatically"]'); var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true" || autoUpdate.checked; var found = $("updates-manualUpdatesFound-btn") || { get hidden() { return um.getAttribute("state") != "manual-updates-found"; } }; if( autoUpdateChecked ? notFound.hidden && updated.hidden : notFound.hidden && found.hidden ) // Too early? return; stopWait(); if(!tbTab) tab.closing = false; function removeTab() { if(!tab.collapsed) return; if(tbTab) { tabmail.closeTab(tbTabInfo, true /*aNoUndo*/); return; } gBrowser.removeTab(tab); (function forgetClosedTab(isSecondTry) { var ss = "nsISessionStore" in Components.interfaces ? ( Components.classes["@mozilla.org/browser/sessionstore;1"] || Components.classes["@mozilla.org/suite/sessionstore;1"] ).getService(Components.interfaces.nsISessionStore) : trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559 if(!("forgetClosedTab" in ss)) return; var closedTabs = JSON.parse(ss.getClosedTabData(window)); for(let i = 0, l = closedTabs.length; i < l; ++i) { let closedTab = closedTabs[i]; let state = closedTab.state; if(state.entries[state.index - 1].url == ADDONS_URL) { ss.forgetClosedTab(window, i); return; } } if(!isSecondTry) // May be needed in SeaMonkey setTimeout(forgetClosedTab, 0, true); })(); } if(!notFound.hidden) { removeTab(); localize(notFound, "addon-updates-none-found", function(s) { notify(s); }); return; } if(autoUpdateChecked) { removeTab(); localize(updated, "addon-updates-installed", function(s) { notify(s); }); return; } tab.collapsed = false; var cats = $("categories"); var upds = $("category-availableUpdates"); if(cats && upds) { if(vb && cats.selectedItem == upds) // Only for Firefox 72+ cats.selectedItem = $("category-extension"); // Trick to force update cats.selectedItem = upds; } else { // Firefox 76+ ? vbDoc.querySelector('.category[name="available-updates"]').click(); } var tabWin = tab.ownerDocument.defaultView; if(tbTab) tabmail.switchToTab(tbTabInfo); else tabWin.gBrowser.selectedTab = tab; setTimeout(function() { tabWin.focus(); doc.defaultView.focus(); var al = $("addon-list") || vb; al.focus(); }, 0); }, 50); function $(id) { return doc.getElementById(id); } function stopWait() { clearInterval(waitTimer); progressIcon.restore(); btn.tooltipText = tip; if(tab.image == image) tab.image = tab.getAttribute(origAttr); tab.removeAttribute(origAttr); trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false); setTimeout(function() { delete btn._cb_disabled; }, 500); } function notify(msg) { Components.classes["@mozilla.org/alerts-service;1"] .getService(Components.interfaces.nsIAlertsService) .showAlertNotification( app == "Firefox" && pv >= 57 ? "chrome://mozapps/skin/extensions/extensionGeneric.svg" : "chrome://mozapps/skin/extensions/extensionGeneric.png", btn.label, msg, false, "", null ); } } function dontSelectHiddenTab(e) { // <tab /><tab collapsed="true" /> // Close first tab: collapsed tab becomes selected var trgTab = e.originalTarget || e.target; if(trgTab != tab) return; if(/\n(?:BrowserOpenAddonsMgr|toEM)@chrome:\/\//.test(new Error().stack)) { // User open Add-ons Manager, show tab trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false); setTimeout(function() { // Hidden tab can't be selected, so select it manually... tab.collapsed = tab.closing = false; gBrowser.selectedTab = tab; }, 0); } function done(t) { if(!t.hidden && !t.closing) { e.preventDefault(); e.stopPropagation(); return gBrowser.selectedTab = t; } return false; } for(var t = tab.nextSibling; t; t = t.nextSibling) if(done(t)) return; for(var t = tab.previousSibling; t; t = t.previousSibling) if(done(t)) return; } function ProgressIcon(btn) { var app = Services.appinfo.name; var pv = parseFloat(Services.appinfo.platformVersion); if(app == "SeaMonkey") this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif"; else if(app == "Thunderbird") { this.imgConnecting = "chrome://messenger/skin/icons/connecting.png"; this.imgLoading = "chrome://messenger/skin/icons/loading.png"; } else { this.imgConnecting = app == "Firefox" && pv >= 58 ? "chrome://browser/skin/tabbrowser/tab-connecting.png" : "chrome://browser/skin/tabbrowser/connecting.png"; this.imgLoading = app == "Firefox" && pv >= 48 ? "chrome://global/skin/icons/loading.png" : "chrome://browser/skin/tabbrowser/loading.png"; } if(!(btn instanceof XULElement)) { this.loading = this.restore = function() {}; return; } var useAnimation = app == "Firefox" && pv >= 32 && pv < 48; var btnIcon = btn.icon || btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon"); var origIcon = btnIcon.src; btnIcon.src = this.imgConnecting; if(useAnimation) { let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null); let s = btnIcon.style; s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" "); s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" "); s.width = cs.width; s.height = cs.height; s.boxShadow = "none"; s.borderColor = s.background = "transparent"; btnIcon.setAttribute("fadein", "true"); btnIcon.setAttribute("busy", "true"); btnIcon.classList.add("tab-throbber"); btnIcon._restore = function() { delete btnIcon._restore; btnIcon.removeAttribute("busy"); btnIcon.removeAttribute("progress"); setTimeout(function() { btnIcon.classList.remove("tab-throbber"); btnIcon.removeAttribute("style"); btnIcon.removeAttribute("fadein"); }, 0); }; } this.loading = function() { btnIcon.src = this.imgLoading; if(useAnimation) btnIcon.setAttribute("progress", "true"); }; this.restore = function() { btnIcon.src = origIcon; if(useAnimation) btnIcon._restore(); }; } }).call(this); //== Check for Addons Updates end } this.tooltipText = "Переключатель джетпаков" + "\n\nУправление:\nЛКМ – открыть меню" + "\nПКМ – проверить обновления" + "\nСКМ – открыть страницу дополнений" + "\nShift+ПКМ – меню кнопки" + "\n\nВ меню: \nЛКМ – включить/выключить дополнение без закрытия меню" + "\nShift+ЛКМ – включить/выключить дополнение" + "\nСКМ – открыть страницу дополнения в управлении дополнениями" + "\nПКМ – открыть настройки дополнения (если есть)"; // Autoopen/close feature var openDelay = 200; var closeDelay = 350; var _openTimer = 0; var _closeTimer = 0; this.onmouseover = function(e) { clearTimeout(_closeTimer); if(e.target == this && closeOtherMenus()) { this.open = true; return; } _openTimer = setTimeout(function() { self.open = true; }, openDelay); }; this.onmouseout = function(e) { clearTimeout(_openTimer); _closeTimer = setTimeout(function() { if(!isContextOpened()) self.open = false; }, closeDelay); }; function closeOtherMenus() { return Array.prototype.some.call( self.parentNode.getElementsByTagName("*"), function(node) { if( node != self && node.namespaceURI == xulns // See https://github.com/Infocatcher/Custom_Buttons/issues/28 //&& node.boxObject //&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject && "open" in node && node.open && node.getElementsByTagName("menupopup").length ) { node.open = false; return true; } return false; } ); } function isContextOpened() { return inBtn(document.popupNode); } function inBtn(node) { for(; node; node = node.parentNode) if(node == self) return true; return false; }
try {CustomizableUI.createWidget({ label: "Дополнения", id: "ucf-cbbtn-ToggleRestartlessAddons", localized: false, get initCode() { this.event = Object.create(null); delete this.initCode; return this.initCode = Cu.readUTF8URI(Services.io.newURI( "chrome://user_chrome_files/content/custom_scripts/custom_script/toggleRestartlessAddons.js" )); }, onCreated(btn) { btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAt0lEQVR42mP4//8/AzK269D+zxDPAMYgNro8OsYUsGf4L5IrAMYgNskGPHrzkAHmAhCbZAPAglFAA6II2w5WC/ZzGEQDDMO9gCQGUmNYr4dhKANPBgdcAyEMMgSrAWA/R0H8jdcALN5iQHYWLABJMgBbABJjACiKweFHigHwgIW6Eux1UryA1QD0QISzsQQqVi+gKEBLfeguwhqIBEM5ihQD4rEoQHaBLwEDQOGBHKggNiyl4sreAEpLK4gRQN5TAAAAAElFTkSuQmCC"); new btn.ownerGlobal.Function("self,event,_phase", this.initCode) .call(btn, btn, this.event, "init"); } });} catch(ex) {Cu.reportError(ex);}
Отредактировано egorsemenov06 (06-12-2021 17:36:51)
Отсутствует
egorsemenov06 пишетJSON.parse
/*JSON.parse*/
Огромное Спасибо!!!!!
Отсутствует
воможно ли с помощью скрипта https://forum.mozilla-russia.org/viewto … 54#p782454 открывать ссылки и страницы в tor browser? после последнего обновления тор открывается через скрипт, но соединения нет
Отсутствует
Dumby
Хочу на базе этого скрипта сделать ещё один, но чтоб он закрывал текущую вкладку, заменить им хочу расширение.
Подскажите пожалуйста, может где подправить можно по мелочи?
Сейчас мой код закрытия "других вкладок" стал немного другим, иконку ещё более подходящую нашёл
CustomizableUI.createWidget({ id: "Close-Tabs-button", label: "Закрыть другие вкладки", tooltiptext: "Закрыть другие вкладки", defaultArea: CustomizableUI.AREA_NAVBAR, localized: false, onCreated(btn) { btn.render = this.render; btn._handleClick = this.close; btn.setAttribute("image", "chrome://devtools/skin/images/close.svg"); }, render() { delete this.render; this.render(); this.icon.style.setProperty("padding", "3px", "important"); }, close() { var gb = this.ownerGlobal.gBrowser; gb.removeAllTabsBut(gb.selectedTab); } });
Отсутствует
По-моему, строку gb.removeAllTabsBut(gb.selectedTab); нужно изменить, но вот как...
Верно, эту строку. Как? Ну, обычно,
следует просто посмотреть как это делает сам браузер, и срисовать себе.
Если как пункт контекстного меню вкладок «Закрыть (N) вклад(ку|ки|ок)»,
то получится что-то типа
... //gb.removeAllTabsBut(gb.selectedTab); var tab = gb.selectedTab; tab.multiselected ? gb.removeMultiSelectedTabs() : gb.removeTab(tab, {animate: true});
Отсутствует