папку для сохранения иконок и ярлыков сайтов привязать к папке загрузок браузера, иначе - рабочий стол
Набросок: три фрагмента (если оседание фавиконов в загрузках не ценность).
Первый и второй удалить, третий добавить.
if (typeof window.saveImageURL != "function") var saveImageURL = internalSave.length == 15 ? (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) => internalSave(url, null, name, a9, type, a4, a3, null, a6, null, a7, a5, null, priv, prin) : (url, name, a3, a4, a5, a6, a7, type, a9, priv, prin) => internalSave(url, null, name, a9, type, a4, a3, null, a6, a7, a5, null, priv, prin); // Сохранить иконку текущего сайта с диалогом сохранения ............. function saveFavicon() { var uri = gBrowser.currentURI; function getSiteName() { try { var domain = uri.host.split('.') } catch(e) { return "" }; domain = (domain.length == 2) ? domain[0] : domain[1] return domain.charAt(0).toUpperCase() + domain.slice(1).split('.')[0] + " "; }; var url = gBrowser.selectedTab.image; url && saveImageURL( url, getSiteName(), null, false, false, null, null, /^data:(image\/[^;,]+)/i.test(url) ? RegExp.$1.toLowerCase() : Cc["@mozilla.org/mime;1"] .getService(Ci.nsIMIMEService).getTypeFromURI(Services.io.newURI(url)), null, PrivateBrowsingUtils.isContentWindowPrivate(content || window), document.nodePrincipal ); };
function saveShortcuts() { var file = Components.classes["@mozilla.org/file/local;1"]. createInstance(Components.interfaces.nsIFile); file.initWithPath(folderpath); if( !file.exists() || !file.isDirectory() ) { file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0x1B6);} var savetodir=folderpath+"\\"; var urllink=gBrowser.currentURI.spec; var out=getTabLabel(); var filename=savetodir+out+'.url'; var data="[InternetShortcut]\r\nURL="+urllink+"\r\n"; saveToFile(data, filename); // стиль для изображения во всплывающей подсказке .... var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); var uri = makeURI('data:text/css,'+ encodeURIComponent('#alertImage { height: 25px !important; width: 25px !important; }')); sss.loadAndRegisterSheet(uri, 0); // подсказка var notific = 'Сохранил в: ' + folderpath; var image = gBrowser.selectedBrowser.mIconURL; Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService).showAlertNotification(image, filename, notific); };
async function pick(fileName) { try { var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile); await IOUtils.makeDirectory(file.path); } catch { file = Services.dirsvc.get("Desk", Ci.nsIFile); } var fp = makeFilePicker(); fp.init(window, "", fp.modeSave); fp.displayDirectory = file; fp.defaultString = fileName; return await new Promise(fp.open) != fp.returnCancel && fp.file; } function saveFavicon() { var dn = "favicon"; var re = /^data:(image\/[^;,]+)/i; var ms = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); (saveFavicon = async () => { var url = gBrowser.selectedTab.image; if (!url) return; if (re.test(url)) { try {var name = gBrowser.currentURI.host || dn;} catch {name = dn;} name += "." + ms.getPrimaryExtension(RegExp.$1, "ico"); } else var name = Services.io.newURI(url).QueryInterface(Ci.nsIURL).fileName; var file = await pick(name); file && IOUtils.write(file.path, new Uint8Array(await (await fetch(url)).arrayBuffer())); })(); } function saveShortcuts() { var img = self.getAttribute("image"); var as = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); (saveShortcuts = async () => { var file = await pick(getTabLabel() + ".url"); if (file) await IOUtils.writeUTF8(file.path, `[InternetShortcut]\r\nURL=${gBrowser.currentURI.spec}\r\n`), as.showAlertNotification( gBrowser.selectedTab.image || img, file.leafName, "Сохранил в: " + file.parent.path ); })(); }
Если возможно
Говорят что нет.
Отредактировано Dumby (21-05-2022 13:24:24)
Отсутствует
Dumby
Фавиконки не ценность, даже отключены в about:config.
Спасибо, все работает как надо.
А для открытия списков URL и сохранения списка URL - адресов выбранных или всех текущих вкладок, есть скрипт? Из буфера обмена / файла, в буфер обмена / файл. А то приходится сохранять папку закладок, потом копировать ее и вставлять в файл, для восстановления делать все в обратном порядке. И хотелось бы еще заголовок видеть перед каждым адресом.
Отредактировано _zt (21-05-2022 21:22:43)
Отсутствует
А сохранение скриптом в .pdf без разбивки на страницы тоже никак?
У меня не получилось какой-нибудь browser.js или about:license.
Поднимаешь paperHeight — сначала качество съезжает, а ещё больше — вообще пустота.
Кстати, imgIEncoder способен собрать PNG большего размера чем canvas,
настолько, что даже сам браузер не сможет его отобразить, только подходящий вьювер.
Так что, для любителей экстремальных вещей, можно его попробовать.
Вот, например, автономный код для запуска в окне браузера, типа с консоли, или вкладка Код в CB.
Следует понимать, что на больши́х страницах может случиться ошибка (или даже краш) out of memory.
(async cid => { var fp = makeFilePicker(); fp.init(window, "Сохранить как…", fp.modeSave); fp.appendFilter("", "*.png"); var lab = DownloadPaths.sanitize(gBrowser.selectedTab.label); fp.defaultString = lab.slice(0, 50).trimRight() + ".png"; var res = await new Promise(fp.open); if (res == fp.returnCancel || !fp.file) return; var {file} = fp; var br = gBrowser.selectedBrowser; var name = "extrem-encoder:de-xy-dpr-info"; var url = "data:,(" + encodeURIComponent((de, name) => sendAsyncMessage( name, [de.scrollWidth, de.scrollHeight, content.devicePixelRatio] )) + `)(content.document.documentElement, "${name}")`; var mm = br.messageManager; mm.loadFrameScript(url, false); var res = await new Promise(r => { var lst = msg => mm.removeMessageListener(name, lst, r(msg.data)); mm.addMessageListener(name, lst); }); var [width, height, k] = res; var rectWidth = width; width = Math.floor(width * k); height = Math.floor(height * k); var step = 2000; var rectHeight = step / k; var canvas = document.createElement("canvas"); canvas.width = width; canvas.height = step; var ctx = canvas.getContext("2d"); var stride = width * 4; var encoder = Cc[cid].createInstance(Ci.imgIEncoder); var RGBA = encoder.INPUT_FORMAT_RGBA; encoder.startImageEncode(width, height, RGBA, ""); var cwg = br.browsingContext.currentWindowGlobal; for(var y = 0; y < height; y += step) { var rect = new DOMRect(0, y / k, rectWidth, rectHeight); ctx.drawImage(await cwg.drawSnapshot(rect, k, "white"), 0, 0); var {data} = ctx.getImageData(0, 0, width, step); encoder.addImageFrame(data, data.length, width, step, stride, RGBA, ""); } encoder.endImageEncode(); var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); stream.setInputStream(encoder.QueryInterface(Ci.nsIInputStream)); var bytes = stream.readByteArray(stream.available()); await IOUtils.write(file.path, new Uint8Array(bytes)); file.reveal(); })("@mozilla.org/image/encoder;2?type=image/png");
есть скрипт?
Увы, я не знаток существования скриптов.
открытия списков URL и сохранения списка URL
Если формат списка внутренний (только для этого скрипта),
то можно тяп-ляп что-нибудь набросать, например виджет
(async () => CustomizableUI.createWidget({ id: "799775", label: "799775", tooltiptext: "799775", localized: false, marker: "[ucf_tabs_linkset]\n\n", onCreated(btn) { btn.setAttribute("image", "resource://usercontext-content/pet.svg"); btn.setAttribute("type", "menu"); btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment( `<menupopup oncommand="creator.cmd(event);"> <menuitem label="Сохранить адреса вкладок" value="1"/> <menuitem label="Сохранить адреса всех вкладок" value="2"/> <menuseparator/> <menuitem label="Копировать адреса вкладок" value="3"/> <menuitem label="Копировать адреса всех вкладок" value="4"/> <menuseparator/> <menuitem label="Открыть из файла" value="5"/> <menuitem label="Открыть из буфера" value="6"/> </menupopup>` )); btn.firstChild.creator = this; }, async cmd(e) { var win = e.view, num = e.target.value; if (num < 5) { var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs; if (num > 2) return this.copy(this.text(tabs)); var file = await this.pick( win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt` ); return file && win.IOUtils.writeUTF8(file.path, this.text(tabs)); } if (num == 6) var text = win.readFromClipboard(); else { var file = await this.pick(win); if (!file) return; var text = await win.IOUtils.readUTF8(file.path); } if (!text?.startsWith(this.marker)) return; var gb = win.gBrowser; var arg = { index: gb.selectedTab._tPos + 1, triggeringPrincipal: win.document.nodePrincipal }; var arr = text.split("\n"); for(var ind = arr.length - 1; ind > 2; ind -= 3) var tab = gb.addTab(arr[ind], arg); if (!e.button && !e.shiftKey) gb.selectedTab = tab; }, text(tabs) { var res = []; for(var tab of tabs) { var br = tab.linkedBrowser; var url = br.currentURI.spec, beg = url.slice(0, 40); var title = br.contentTitle || "untitled"; if (title.startsWith(beg)) title = beg; res.push(title + "\n" + url); } return this.marker + res.join("\n\n"); }, copy(text) { var cb = Services.clipboard.kGlobalClipboard; var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); (this.copy = text => ch.copyStringToClipboard(text, cb))(text); }, async pick(win, fileName) { try { var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile); await IOUtils.makeDirectory(file.path); } catch { file = Services.dirsvc.get("Desk", Ci.nsIFile); } var fp = win.makeFilePicker(); fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen); fp.displayDirectory = file; fp.appendFilter("", "*.txt"); fp.appendFilters(fp.filterAll); if (fileName) fp.defaultString = fileName; return await new Promise(fp.open) != fp.returnCancel && fp.file; } }))();
Отсутствует
Dumby
Ну может уже делал.
Почти то что нужно.
Только вот список URL для открытия может быть создан не скриптом или скопирован откуда угодно и может быть в любом формате, и с любым текстовым мусором помимо адресов.
А вот формат сохранения идеален.
И еще, вот с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере.
Отсутствует
откуда угодно … в любом … с любым
Я же совершенно не разбираюсь в урлах и их списках.
Конкретного бы чего-нибудь дал, лекцию прочёл.
Ладно, написал какую-то лажу, просто чтобы не ничего.
/* if (!text?.startsWith(this.marker)) return; var gb = win.gBrowser; var arg = { index: gb.selectedTab._tPos + 1, triggeringPrincipal: win.document.nodePrincipal }; var arr = text.split("\n"); for(var ind = arr.length - 1; ind > 2; ind -= 3) var tab = gb.addTab(arr[ind], arg); if (!e.button && !e.shiftKey) gb.selectedTab = tab; }, */ if (!text) return; var own = text.startsWith(this.marker); if (own) var arr = text.split("\n"); else { var urls = this.parse(text); if (!urls?.length) return; } var gb = win.gBrowser; var arg = { index: gb.selectedTab._tPos + 1, triggeringPrincipal: win.document.nodePrincipal }; if (own) for(var ind = arr.length - 1; ind > 2; ind -= 3) var tab = gb.addTab(arr[ind], arg); else for(var url of urls) var tab = gb.addTab(url, arg); if (!e.button && !e.shiftKey) gb.selectedTab = tab; }, parse(text) { var result = new Set(); var candidates = new Set( text.split(this.space).filter(this.colon) ); for(var str of candidates) { var url = this.url(str); if (url) result.add(str); else { for(var splitter of this.splitters) { var arr = str.split(splitter).filter(Boolean); for(var sub of arr) url = this.url(sub), url && result.add(url); } } } var {size} = result; if (size) { result = Array.from(result); if (size > 1) result.reverse(); return result; } }, space: /\s+/, colon: str => str.includes(":"), splitters: [",", ";", '"', "'", /[\[\]]/, /()/], url(str) { try { var scheme = Services.io.extractScheme(str); if (scheme.length + 1 == str.length || scheme == "default") return; var ph = Services.io.getProtocolHandler(scheme); if (ph.scheme == scheme) return Services.io.newURI(str).spec; } catch {} },
с этой строкой - `await IOUtils.makeDirectory(file.path);` скрипт упорно лезет в папку загрузок пользователя, без этой строки, как и положено, в папку загрузок назначенную в браузере
Да, мой косяк. Конечно же await win.IOUtils.makeDirectory(file.path);
Это на тот случай, что в настройке путь до несуществующей папки,
что маловероятно, может и правильно, что удалил эту строку.
Отсутствует
Dumby
Да вроде нормально. Только вот что заметил:
1. место открытия списков - надо бы что бы всегда открывались после последней вкладки
2. ссылку с запятой в конце адреса открывает вместе с запятой
3. markdown не открывает
4. BB-code с заголовком не открывает
5. очень желательно вывести в меню опцию "заменять одиночные пробелы процентами" - "%20", но если после пробела идет любой спецсимвол, то обрезать ссылку до этого пробела.
Так как, многие поисковики отдают ссылки на страницы с поисковыми запросами с пробелами, да и вообще пробелы частенько встречаются, в ссылках на файлы, на самодельных сайтах и т.п.
Это не часто нужно и как правило будет мешать получению правильных ссылок, поэтому лучше сделать это как опцию, если такое возможно.
Создание гиперссылок - Изучение веб-разработки | MDN https://developer.mozilla.org/ru/docs/Learn/HTML/Introduction_to_HTML/Creating_hyperlinks // afgaga Управление дополнениями about:addons Ресурс resource://usercontext-content/pet.svg HTML <a href="https://en.wikipedia.org/wiki/Percent-encoding">Percent-encoding - Wikipedia</a> BB-code без заголовка [url]https://forum.mozilla-russia.org/index.php[/url] /* Проблемные ссылки */ Ссылка с запятой в конце адреса (открывает вместе с запятой) https://github.com/Aris-t2/CustomCSSforFx/blob/master/classic/css/tabs/tabs_multiple_lines.css, BB-code с заголовком (не открывает) [url=https://forum.mozilla-russia.org/viewtopic.php?id=76642&p=14]UCF-скрипты на этом форуме | Форум Mozilla Россия[/url] markdown 1 (не открывает) [Ссылка на корень сайта](https://planshet-info.ru/kompjutery/ssylka-na-koren-sajta) markdown 2 (не открывает) ![Image and Preview Themes on the toolbar](https://markdownmonster.west-wind.com/docs/images/EditorPreviewThemeUi.png) Ссылка с пробелами (обрезает по первому пробелу) https://yandex.ru/yandsearch?text=команда на копирование файлов и папок в bat&lr=213
Отредактировано _zt (26-05-2022 18:53:23)
Отсутствует
_zt
Хорошо, попробую учесть.
если после пробела идет любой спецсимвол
А что за спецсимволы? Написал так: singleSpace: / (?![\s"])/g,
то есть только «\s» и «"», не знаю что ещё.
//....... onCreated(btn) { btn.setAttribute("image", "resource://usercontext-content/pet.svg"); btn.setAttribute("type", "menu"); btn.prepend(btn.ownerGlobal.MozXULElement.parseXULToFragment( `<menupopup oncommand="creator.cmd(event);" onpopupshowing="creator.check(this.lastChild);"> <menuitem label="Сохранить адреса вкладок" value="1"/> <menuitem label="Сохранить адреса всех вкладок" value="2"/> <menuseparator/> <menuitem label="Копировать адреса вкладок" value="3"/> <menuitem label="Копировать адреса всех вкладок" value="4"/> <menuseparator/> <menuitem label="Открыть из файла" value="5"/> <menuitem label="Открыть из буфера" value="6"/> <menuitem label="Заменять одиночные пробелы процентами" closemenu="none" type="checkbox" value="7"/> </menupopup>` )); btn.firstChild.creator = this; }, check(item) { var pref = "ucf.linkset-widget.replspace"; this.checked = Services.prefs.getBoolPref(pref, false); this.setPref = Services.prefs.setBoolPref.bind(null, pref); (this.check = item => { this.checked ? item.setAttribute("checked", true) : item.removeAttribute("checked"); })(item); }, singleSpace: / (?![\s"])/g, async cmd(e) { var num = e.target.value; if (num == 7) return this.setPref(this.checked = e.target.hasAttribute("checked")); var win = e.view; if (num < 5) { var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs; if (num > 2) return this.copy(this.text(tabs)); var file = await this.pick( win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt` ); return file && win.IOUtils.writeUTF8(file.path, this.text(tabs)); } if (num == 6) var text = win.readFromClipboard(); else { var file = await this.pick(win); if (!file) return; var text = await win.IOUtils.readUTF8(file.path); } if (!text) return; // if (!text || text.length > 500_000) return; // limit? var own = text.startsWith(this.marker); if (this.checked && !own) text = text.replace(this.singleSpace, "%20"); var gb = win.gBrowser, tl = gb.visibleTabs.length; if (own) for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3) gb.addTrustedTab(arr[ind]); else { var urls = this.parse(text); if (urls?.size) for(var url of urls) gb.addTrustedTab(url); } if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl]; }, parse(text) { var result = new Set(); var candidates = new Set( text.split(this.space).filter(this.colon) ); for(var str of candidates) { var url = this.url(str); if (url) result.add(str); else { var bb = true; for(var splitter of this.splitters) { var arr = str.split(splitter).filter(this.colon); for(var sub of arr) { if (bb) { // [] var ind = sub.indexOf("="); if (ind != -1 && ind < sub.indexOf(":")) sub = sub.slice(ind + 1); } url = this.url(sub); url && result.add(url); } bb = false; } } } // console log instead of open //if (true) return Services.console.logStringMessage(Array.from(result).join("\n")); //var {size} = result; //if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit? return result; }, space: /\s+/, colon: str => str.includes(":"), splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"], unwanedEnds: /,$/, url(str) { try { var scheme = Services.io.extractScheme(str); if (scheme.length + 1 == str.length || scheme == "default") return; var ph = Services.io.getProtocolHandler(scheme); if (ph.scheme == scheme) return Services.io.newURI(str.replace(this.unwanedEnds, "")).spec; } catch {} }, text(tabs) { var res = []; for(var tab of tabs) { var br = tab.linkedBrowser; var url = br.currentURI.spec, beg = url.slice(0, 40); var title = br.contentTitle || "untitled"; if (title.startsWith(beg)) title = beg; res.push(title + "\n" + url); } return this.marker + res.join("\n\n"); }, copy(text) { var cb = Services.clipboard.kGlobalClipboard; var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); (this.copy = text => ch.copyStringToClipboard(text, cb))(text); }, async pick(win, fileName) { try { var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile); await win.IOUtils.makeDirectory(file.path); } catch { file = Services.dirsvc.get("Desk", Ci.nsIFile); } var fp = win.makeFilePicker(); fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen); fp.displayDirectory = file; fp.appendFilter("", "*.txt"); fp.appendFilters(fp.filterAll); if (fileName) fp.defaultString = fileName; return await new Promise(fp.open) != fp.returnCancel && fp.file; } }))();
Отсутствует
Dumby
Да почти любой спецсимвол, кроме, пожалуй, скобок (и точка под вопросом). Так как есть нормально, потом всегда можно будет изменить.
На мой взгляд осталось два момента...
1. Не открывает ANSI файлы, а это стандартная кодировка Windows.
2. Неверно открывает URL с запятой в конце. При копировании со страниц адресов оформленных тегами (адрес-адрес) они копируются с прилипшей запятой:
<a href="https://en.wikipedia.org/">https://en.wikipedia.org/</a>, [https://en.wikipedia.org/](https://en.wikipedia.org/), [url=https://en.wikipedia.org/]https://en.wikipedia.org/[/url],
Всегда получается "https://en.wikipedia.org/," при копировании.
Кстати, там может быть и точка с запятой. Может по аналогии с singleSpace сделать, строку с перечислением отсекающих символов?
Даже не символов, а группы символов, например: ,\s ;\s
А в singleSpace: / (?![\s"... экранировать символы нужно и какие, или вообще можно все экранировать?
Отредактировано _zt (27-05-2022 15:07:51)
Отсутствует
Неверно открывает URL с запятой в конце.
Да, есть опечатки в коде.
Про копирование не понял. Поскольку код не занимается
копированием со страниц, то, видимо, это объяснение откуда что берётся.
Точку с запятой добавил в unwantedEnds: /[,;]$/,
Не открывает ANSI файлы, а это стандартная кодировка Windows.
Насколько мне известно, в Firefox нет API для определения кодировки.
Самое простое так: пытаемся прочитать как UTF-8, если ошибка,
тогда читаем бинарную строку и конвертируем в Windows-1251.
//....... async cmd(e) { var num = e.target.value; if (num == 7) return this.setPref(this.checked = e.target.hasAttribute("checked")); var win = e.view; if (num < 5) { var tabs = num % 2 ? win.gBrowser.selectedTabs : win.gBrowser.visibleTabs; if (num > 2) return this.copy(this.text(tabs)); var file = await this.pick( win, `Linkset ${new Date().toLocaleString("mn").replace(/:/g, "\ua789")} [${tabs.length}] .txt` ); return file && win.IOUtils.writeUTF8(file.path, this.text(tabs)); } if (num == 6) var text = win.readFromClipboard(); else { var file = await this.pick(win); if (!file) return; var text = await this.read(file); } if (!text) return; // if (!text || text.length > 500_000) return; // limit? var own = text.startsWith(this.marker); if (this.checked && !own) text = text.replace(this.singleSpace, "%20"); var gb = win.gBrowser, tl = gb.visibleTabs.length; if (own) for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3) gb.addTrustedTab(arr[ind]); else { var urls = this.parse(text); if (urls?.size) for(var url of urls) gb.addTrustedTab(url); } if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl]; }, read(file) { var read1251 = async file => { var suc = Cc["@mozilla.org/intl/scriptableunicodeconverter"] .createInstance(Ci.nsIScriptableUnicodeConverter); suc.charset = "windows-1251"; var reader = new FileReader(); return (read1251 = async file => { reader.readAsBinaryString(file); await new Promise(resolve => reader.onloadend = resolve); return suc.ConvertToUnicode(reader.result); })(file); } var {IOUtils} = Cu.getGlobalForObject(Cu); return (this.read = async file => { try {return await IOUtils.readUTF8(file.path);} catch {return read1251(file);} })(file); }, parse(text) { var result = new Set(); var candidates = new Set( text.split(this.space).filter(this.colon) ); for(var str of candidates) { var url = this.url(str); if (url) result.add(url); else { var bb = true; for(var splitter of this.splitters) { var arr = str.split(splitter).filter(this.colon); for(var sub of arr) { if (bb) { // [] var ind = sub.indexOf("="); if (ind != -1 && ind < sub.indexOf(":")) sub = sub.slice(ind + 1); } url = this.url(sub); url && result.add(url); } bb = false; } } } // console log instead of open //if (true) return Services.console.logStringMessage(Array.from(result).join("\n")); //var {size} = result; //if (size > 100 && !Services.prompt.confirm(null, null, `Количество вкладок: ${size}! Открыть?`)) return; // limit? return result; }, space: /\s+/, colon: str => str.includes(":"), splitters: [/[\[\]]/, /[()]/, ",", ";", '"', "'"], unwantedEnds: /[,;]$/, url(str) { try { var scheme = Services.io.extractScheme(str); if (scheme.length + 1 == str.length || scheme == "default") return; var ph = Services.io.getProtocolHandler(scheme); if (ph.scheme == scheme) return Services.io.newURI(str.replace(this.unwantedEnds, "")).spec; } catch {} }, text(tabs) { var res = []; for(var tab of tabs) { var br = tab.linkedBrowser; var url = br.currentURI.spec, beg = url.slice(0, 40); var title = br.contentTitle || "untitled"; if (title.startsWith(beg)) title = beg; res.push(title + "\n" + url); } return this.marker + res.join("\n\n"); }, copy(text) { var cb = Services.clipboard.kGlobalClipboard; var ch = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); (this.copy = text => ch.copyStringToClipboard(text, cb))(text); }, async pick(win, fileName) { try { var file = Services.prefs.getComplexValue("browser.download.dir", Ci.nsIFile); await win.IOUtils.makeDirectory(file.path); } catch { file = Services.dirsvc.get("Desk", Ci.nsIFile); } var fp = win.makeFilePicker(); fp.init(win, "", fileName ? fp.modeSave : fp.modeOpen); fp.displayDirectory = file; fp.appendFilter("", "*.txt"); fp.appendFilters(fp.filterAll); if (fileName) fp.defaultString = fileName; if (await new Promise(fp.open) == fp.returnCancel) return; if (fileName) var file = fp.file; else { var file = fp.domFileOrDirectory; file.path = fp.file.path; } return file; } }))();
экранировать символы нужно и какие, или вообще можно все экранировать?
Ой, даже затрудняюсь ответить.
Нужно экранировать те, которые требуется включить в класс символов буквально,
чтобы соотетствовали сами себе, но без экранирования будут иметь другой смысл.
А можно экранировать, наоборот, те, которые при экранировании не образуют другой смысл.
Например s соответствует букве «эс», а \s соответствует любому юникодному символу-разделителю,
но z соответствует букве «зэт», и \z тоже соответствует букве «зэт».
Отсутствует
Про копирование не понял.
Это не про скрипт, это про список, а он может быть откуда угодно, в том числе скопирован со страницы.
Есть ещё такой жуткий вариант
Как то сложно все. Не надо наверное, я потестирую то что есть.
Не нравится мне как это работает - "if (url) result.add(url);" , окно группировки одновременно открытых вкладок TST растягивается во все окно браузера по ширине. Причем, все равно что открываешь, ansi или utf8. В предыдущем варианте текст url не шифровался и окно не растягивало. Речь про адреса с utf8 символами, например, приведенный выше яндекс. Можно это поправить? Может обратную дешифровку сделать при "выводе" из скрипта?
Отредактировано _zt (30-05-2022 03:22:34)
Отсутствует
Может обратную дешифровку сделать при "выводе" из скрипта?
Если именно при открытии вкладок, то можно так попробовать
(это если и для своего, и для сторонних форматов).
/* if (own) for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3) gb.addTrustedTab(arr[ind]); else { var urls = this.parse(text); if (urls?.size) for(var url of urls) gb.addTrustedTab(url); } if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl]; }, */ if (own) for(var ind = 3, arr = text.split("\n"), len = arr.length; ind < len; ind += 3) gb.addTrustedTab(this.decode(arr[ind])); else { var urls = this.parse(text); if (urls?.size) for(var url of urls) gb.addTrustedTab(this.decode(url)); } if (!e.button && !e.shiftKey) gb.selectedTab = gb.visibleTabs[tl]; }, decode: url => url.startsWith("data:") ? url : decodeURI(url),
Отсутствует
Dumby
Спасибо, теперь нормально.
смирительный стиль
Ну так как то.
@-moz-document url-prefix(moz-extension://uuid) { /* Не позволять растягивать дополнительные окна больше заданного размера */ .rich-confirm-dialog { max-width: 800px !important; } }
Отредактировано _zt (30-05-2022 23:26:37)
Отсутствует
Dumby
101 в расширение add_toolbar_buttons.2021.9.5.xpi от Vitaliy V. перестала полноценно работать кнопка Дополнения, не открывается список. Можешь поправить? Или хотя бы скрипт починить из этого расширения №9229?
Отредактировано voqabuhe (31-05-2022 05:32:01)
Отсутствует
перестала полноценно работать кнопка Дополнения, не открывается список
ChromeUtils.import(…, null) сто лет как считался "не по понятиям".
Может замени в parent.js
/* XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => { const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", null); return GlobalManager; }); */ XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm").ExtensionParent.GlobalManager );
Отсутствует
Dumby
На 102 превьюшки TST улетели в край экрана, можете поправить.
Отсутствует
можете поправить
Следуем за ними.
/* var z = win.windowUtils.screenPixelsPerCSSPixel; */ var z = win.devicePixelRatio;
Отсутствует
Dumby
Спасибо. Трудно самому такое найти, особенно когда не знаешь чего искать.
Этот скрипт не позволяет запустить 102, если в scriptschrome: >> load:, а если в scriptsallchrome: >> load: - крашит при вызове соответствующего окна / панели (places\/bookmarksSidebar\.xhtml или places\/places\.xhtml).
Отсутствует
крашит
Ну да, я упоминал. Если грузить скрипт снаружи в объект
глобального системного объекта, то, на ранних бетах и найтли,
разрабы валят пользователю браузер.
Это не касается доживающих последние времена NSVO.
Но Cu.import() больше таковые не возвращает.
Пока сам Cu.import() ещё с нами, можно так попробовать
/* if (!g[key]) { Services.scriptloader.loadSubScript( `data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g ); var raws = g[key]; */ var raws = g.TransactionsHistory?.proxifiedToRaw; if (raws) g = raws; if (!g[key]) { if (!raws) { Services.scriptloader.loadSubScript( `data:,this.${key}=TransactionsHistory.proxifiedToRaw;`, g ); raws = g[key]; }
Отсутствует
В 101 (может и раньше) отвалилась кнопка "Дополнения" из расширения Add Toolbar Buttons.
Была такая отдельно от 18.09.2020
try {(() => { var id = "ucf-aom-button", label = "Дополнения", tooltiptext = "ЛКМ: Меню дополнений\nShift+ЛКМ: Меню дополнений + открыть менеджер\nСКМ: Открыть менеджер дополнений", img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16' viewBox='0 0 48 48'><g><rect x='0' y='0' width='48' height='48' rx='3' ry='3' style='fill:rgb(0, 120, 173);'/><path style='opacity:0.25;fill:black;' d='M 24,4.5 18,12 3,23.7 12,32.7 3.9,44.1 7.8,48 H 45 C 46.7,48 48,46.7 48,45 V 26.1 L 34.8,12.9 31.8,12.3 Z'/><path style='fill:white;' d='M 19.88,3 C 16.93,3 14.55,4.662 14.55,6.701 14.63,7.474 15.11,8.438 15.37,8.762 16.59,10.41 16.59,11.44 16.29,12.06 H 6.299 C 4.476,12.06 3,13.53 3,15.35 V 23.68 C 3.625,24 4.65,24 6.299,22.77 6.625,22.52 7.587,22.02 8.363,21.94 10.4,21.94 12.06,24.35 12.06,27.29 12.06,30.24 10.4,32.65 8.363,32.65 7.725,32.63 6.774,32.07 6.299,31.82 4.65,30.59 3.625,30.59 3,30.91 V 41.71 C 3,43.53 4.476,45 6.299,45 H 19.58 C 19.88,44.38 19.88,43.35 18.65,41.71 18.4,41.38 17.91,40.42 17.82,39.65 17.82,37.6 20.23,35.94 23.18,35.94 26.14,35.94 28.55,37.6 28.55,39.65 28.53,40.28 27.97,41.23 27.71,41.71 26.47,43.35 26.47,44.38 26.79,45 H 32.65 C 34.47,45 35.96,43.53 35.96,41.71 V 32.55 C 36.56,32.23 37.59,32.23 39.23,33.47 39.72,33.73 40.68,34.29 41.29,34.29 43.35,34.29 45,31.91 45,28.94 45,25.99 43.35,23.59 41.29,23.59 40.54,23.67 39.58,24.17 39.23,24.41 37.59,25.65 36.56,25.65 35.96,25.33 V 15.35 C 35.96,13.53 34.47,12.06 32.65,12.06 H 23.49 C 23.19,11.44 23.19,10.41 24.41,8.762 24.66,8.287 25.22,7.337 25.23,6.713 25.23,4.662 22.85,3 19.88,3' /></g></svg>", checked = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' height='16' width='16'><path d='M 4,5 7.5,8.5 12,4 V 8 L 8,12 H 7 L 4,9 Z' style='fill:white'/></svg>", show_version = true, show_description = true, user_permissions = true, show_hidden = true, show_disabled = true, enabled_first = true, exceptions_listset = new Set([ ]); exceptions_type_listset = new Set([ ]); if (!("AddonManager" in this)) ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm"); if (!("GlobalManager" in this)) XPCOMUtils.defineLazyGetter(this, "GlobalManager", () => { const { GlobalManager } = ChromeUtils.import("resource://gre/modules/Extension.jsm", null); return GlobalManager; }); var extensionOptionsMenu = { get alertsService() { delete this.alertsService; return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService); }, get clipboardHelp() { delete this.clipboardHelp; return this.clipboardHelp = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); }, get exceptions_type_listarr() { delete this.exceptions_type_listarr; var arr = ["extension", "theme", "locale", "dictionary"]; if (!exceptions_type_listset.size) return this.exceptions_type_listarr = arr; return this.exceptions_type_listarr = arr.filter(type => !exceptions_type_listset.has(type)); }, populateMenu: async function(e) { var popup = e.target, doc = e.view.document; var addons = await AddonManager.getAddonsByTypes(this.exceptions_type_listarr); var addonsMap = new WeakMap(), setAttributesMenu = (mi, addon, extension) => { var permissions, uuid, props = { label: `${addon.name}${show_version ? ` ${addon.version}` : ""}`, class: "menuitem-iconic", tooltiptext: `${(show_description && addon.description) ? `${addon.description}\n` : ""}ID: ${addon.id}${addon.isActive && (uuid = extension?.uuid) ? `\nUUID: ${uuid}` : ""}${(user_permissions && (permissions = addon.userPermissions?.permissions)?.length) ? `\nРазрешения: ${permissions.join(", ")}` : ""}\n${addon.optionsURL ? "\nЛКМ: Настройки" : ""}\nCtrl+ЛКМ: Копировать ID${uuid ? "\nShift+ЛКМ: Копировать UUID" : ""}${addon.creator?.url ? "\nCtrl+Shift+ЛКМ: Автор" : ""}${addon.homepageURL ? "\nСКМ: Домашняя страница" : ""}${!addon.isBuiltin ? "\nCtrl+СКМ: Просмотр источника" : ""}\nShift+СКМ: Просмотр источника во вкладке\nПКМ: Включить/Отключить${(!addon.isSystem && !addon.isBuiltin) ? "\nCtrl+ПКМ: Удалить" : ""}`, }; for (let p in props) mi.setAttribute(p, props[p]); if (addon.iconURL) mi.setAttribute("image", addon.iconURL); var cls = mi.classList; addon.isActive ? cls.remove("ucf-disabled") : cls.add("ucf-disabled"); addon.optionsURL ? cls.remove("ucf-notoptions") : cls.add("ucf-notoptions"); addon.isSystem ? cls.add("ucf-system") : cls.remove("ucf-system"); cls.add(`ucf-type-${addon.type}`); }; addons.filter(a => !(a.iconURL || "").startsWith("resource://search-extensions/")).sort((a, b) => { var ka = `${(enabled_first ? a.isActive ? "0" : "1" : "")}${a.type || ""}${a.name.toLowerCase()}`; var kb = `${(enabled_first ? b.isActive ? "0" : "1" : "")}${b.type || ""}${b.name.toLowerCase()}`; return (ka < kb) ? -1 : 1; }).forEach(addon => { if (!exceptions_listset.has(addon.id) && (!addon.hidden || show_hidden) && (!addon.userDisabled || show_disabled)) { let extension = GlobalManager.extensionMap.get(addon.id), mi = doc.createXULElement("menuitem"); setAttributesMenu(mi, addon, extension); mi._Addon = addon; mi._Extension = extension; popup.append(mi); addonsMap.set(addon, mi); } }); var click = (e) => { this.handleClick(e); }; popup.addEventListener("click", click); var listener = { onEnabled: addon => { var mi = addonsMap.get(addon); if (mi) setAttributesMenu(mi, addon, mi._Extension); }, onDisabled: addon => { listener.onEnabled(addon); }, onInstalled: addon => { var extension = GlobalManager.extensionMap.get(addon.id), mi = doc.createXULElement("menuitem"); setAttributesMenu(mi, addon, extension); mi._Addon = addon; mi._Extension = extension; popup.prepend(mi); addonsMap.set(addon, mi); }, onUninstalled: addon => { var mi = addonsMap.get(addon); if (mi) { mi.remove(); addonsMap.delete(addon); } }, }; AddonManager.addAddonListener(listener); popup.addEventListener("popuphiding", (e) => { AddonManager.removeAddonListener(listener); popup.removeEventListener("click", click); addonsMap = null; while (popup.hasChildNodes()) popup.firstChild.remove(); }, { once: true }); }, handleClick: function(e) { var win = e.view, mi = e.target; if (!("_Addon" in mi) || !("_Extension" in mi)) return; var addon = mi._Addon, extension = mi._Extension; switch (e.button) { case 0: if (e.ctrlKey && e.shiftKey) { if (addon.creator?.url) win.gBrowser.selectedTab = this.addTab(win, addon.creator.url); } else if (e.ctrlKey) { this.clipboardHelp.copyString(addon.id); try { this.alertsService.showAlertNotification(`${img}`, "ID в буфере обмена!", addon.id, false); } catch(e) {} } else if (e.shiftKey) { if (extension?.uuid) { this.clipboardHelp.copyString(extension.uuid); try { this.alertsService.showAlertNotification(`${img}`, "UUID в буфере обмена!", extension.uuid, false); } catch(e) {} } } else if (addon.isActive && addon.optionsURL) this.openAddonOptions(addon, win); win.closeMenus(mi); break; case 1: if (e.ctrlKey) { if (!addon.isBuiltin) this.browseDir(addon); } else if (e.shiftKey) this.browseDir(addon, win); else if (addon.homepageURL) win.gBrowser.selectedTab = this.addTab(win, addon.homepageURL); win.closeMenus(mi); break; case 2: if (!e.ctrlKey) { let endis = addon.userDisabled ? "enable" : "disable"; if (addon.id == "screenshots@mozilla.org") Services.prefs.setBoolPref("extensions.screenshots.disabled", !addon.userDisabled); else if (addon.id == "webcompat-reporter@mozilla.org") Services.prefs.setBoolPref("extensions.webcompat-reporter.enabled", addon.userDisabled); addon[endis]({ allowSystemAddons: true }); } else if (!addon.isSystem && !addon.isBuiltin && Services.prompt.confirm(win, null, `Удалить ${addon.name}?`)) addon.uninstall(); break; } }, openAddonOptions: function(addon, win) { switch (addon.optionsType) { case 5: win.BrowserOpenAddonsMgr(`addons://detail/${encodeURIComponent(addon.id)}/preferences`); break; case 3: win.switchToTabHavingURI(addon.optionsURL, true); break; } }, browseDir: function(addon, win) { try { if (!win) { let file = Services.io.getProtocolHandler("file") .QueryInterface(Ci.nsIFileProtocolHandler) .getFileFromURLSpec(addon.getResourceURI().QueryInterface(Ci.nsIJARURI).JARFile.spec); if (file.exists()) file.launch(); } else win.gBrowser.selectedTab = this.addTab(win, addon.getResourceURI().spec); } catch (e) {} }, addTab: function(win, url, params = {}) { params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); params.relatedToCurrent = true; return win.gBrowser.addTab(url, params); }, }; CustomizableUI.createWidget({ id: id, type: "custom", label: label, tooltiptext: tooltiptext, localized: false, defaultArea: CustomizableUI.AREA_NAVBAR, onBuild: function(doc) { var btn = doc.createXULElement("toolbarbutton"), win = doc.defaultView, props = { id: id, label: label, tooltiptext: tooltiptext, type: "menu", class: "toolbarbutton-1 chromeclass-toolbar-additional", }; for (let p in props) btn.setAttribute(p, props[p]); btn.addEventListener("click", (e) => { if (e.button == 0) { if (e.shiftKey) win.BrowserOpenAddonsMgr(); } else if (e.button == 1) win.BrowserOpenAddonsMgr(); }); var mp = doc.createXULElement("menupopup"); mp.id = `${id}-popup`; mp.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); }); mp.addEventListener("contextmenu", (e) => { e.preventDefault(); e.stopPropagation(); }); mp.addEventListener("popupshowing", (e) => { extensionOptionsMenu.populateMenu(e); }); btn.append(mp); var btnstyle = "data:text/css;charset=utf-8," + encodeURIComponent(` #${id}, #${id}-popup menuitem { list-style-image: url("${img}") !important; } #${id}-popup menuitem::after { display: -moz-box !important; content: "" !important; height: 16px !important; width: 16px !important; padding: 0 !important; border: 1px solid rgb(0, 116, 232) !important; border-radius: 0 !important; background-repeat: no-repeat !important; background-position: center !important; background-size: 16px !important; background-color: rgb(0, 116, 232) !important; background-image: url("${checked}") !important; opacity: 1 !important; } #${id}-popup menuitem.ucf-disabled::after { border-color: currentColor !important; background-color: transparent !important; background-image: none !important; opacity: .6 !important; } #${id}-popup menuitem.ucf-disabled > label, #${id}-popup menuitem.ucf-notoptions > label { opacity: .6 !important; } #${id}-popup menuitem.ucf-system > label { text-decoration: underline !important; text-decoration-style: dotted !important; } #${id}-popup menuitem > label { margin-inline-end: 0 !important; } #${id}-popup menuitem > .menu-accel-container { display: -moz-box !important; padding: 4px !important; margin: 0 !important; opacity: 1 !important; } #${id}-popup menuitem > .menu-accel-container .menu-iconic-accel { display: -moz-box !important; margin: 0 !important; height: 8px !important; width: 8px !important; border-radius: 4px !important; background-color: transparent !important; opacity: 1 !important; font-size: 0 !important; } #${id}-popup menuitem.ucf-type-dictionary > .menu-accel-container .menu-iconic-accel { background-color: rgb(227, 27, 93) !important; } #${id}-popup menuitem.ucf-type-locale > .menu-accel-container .menu-iconic-accel { background-color: rgb(48, 172, 55) !important; } #${id}-popup menuitem.ucf-type-theme > .menu-accel-container .menu-iconic-accel { background-color: rgb(219, 106, 0) !important; } `); try { win.windowUtils.loadSheetUsingURIString(btnstyle, win.windowUtils.USER_SHEET); } catch (e) {} return btn; }, }); })();} catch (e) {}
Отсутствует
Отсутствует