Ki_rrrilll пишетКак открыть папку с закладкой по имени этой закладки или по имени папки?
Задача не столь проста, как кажется. Советую найти старое расширение "Go Parent Folder" и посмотреть, как оно работает.
Главная проблема в том, что скрипту доступны только те элементы дерева закладок, которые видны в данный момент. Если нужные закладка или папка находятся внутри свёрнутой папки, до них так просто не добраться.
Ну я все равно не разберусь как работает то расширение. И UCF я не использую.
Сейчас я открываю папку с закладкой кодом
PlacesUtils.bookmarks.fetch(guid, null, {includePath: true}) .then(res => PlacesCommandHook.showPlacesOrganizer(["AllBookmarks", ...res.path.map(b => b.guid), guid]))
А guid беру из резервной копии закладок в формате json. Ни о каких "живых" результатах конечно и речи нет, ну хоть как то.
Так вот я думал, может можно из этого json получать guid, по ключевому слову закладки? И желательно имя родительской папки.
Потому что я не очень умею обращаться с этими json и сейчас выуживаю нужный guid путем многочисленных split-ов, отсекая ненужное.
Отредактировано Ki_rrrilll (Сегодня 00:02:27)
Отсутствует
А guid беру из резервной копии закладок в формате json.
А что является критерием для поиска этого GUID? Имя закладки, URL, метка, ключевое слово или что?
Так вот я думал, может можно из этого json получать guid, по ключевому слову закладки? И желательно имя родительской папки.
Потому что я не очень умею обращаться с этими json и сейчас выуживаю нужный guid путем многочисленных split-ов, отсекая ненужное.
Если преобразовать JSON в объект (а это просто один вызов функции JSON.parse(str)), то можно прогуляться по получившемуся дереву и найти в нём нужный элемент. А заодно в процессе этой прогулки и путь к закладке построится.
Только вот алгоритм прохода по дереву абсолютно аналогичен алгоритму, который используется скрипте SidebarBookmarkSearchOpenFolder. Зато скрипт не нуждается в JSON-е, он напрямую с данными окна работает.
И ещё мне кажется, что если в поиске информации о нужной закладке используется человек, то, наверное, имеет смысл взять ранее упомянутое расширение "Go Parent Folder" и засунуть его в кнопку. Потому что работает расширение так: ткнулись мышкой в нужную закладку, выбрали из её контекстного меню пункт "Go Parent Folder" - и происходит переход в папку, в которой где эта закладка лежит. А слева, соответственно, дерево до этой папки разворачивается и папка выбирается (становится текущей).
А SidebarBookmarkSearchOpenFolder делает то же самое, только не в окне, а в боковой панели.
И UCF я не использую.
В кнопку данный скрипт засовывается с минимальными усилиями.
Отредактировано yup (Сегодня 02:44:26)
Отсутствует
Всем привет!
Если вы используете это расширение, отличные новости — с помощью ИИ удалось устранить баги, возникшие при работе в Firefox 138.
Теперь код для расширения снова работает корректно.
Оригинальный код был создан Dumby и отлично себя показал в более ранних версиях браузера.
Новая версия кода базируется на том же исходнике, но адаптирована под последние изменения Firefox.
Финальная версия доступна здесь:
// ==UserScript== // @name Иконки поисковых систем из расширения ContextSearch-web-ext в контекстном меню // @namespace cswem // @version 2.1 // @description Редактирует контекстное меню Firefox // @match *://*/* // @grant none // @icon https://www.mozilla.org/favicon.ico // ==/UserScript== (function() { 'use strict'; // Конфигурация const SETTINGS = { initializedFlag: 'cswemInitialized', // Флаг инициализации menuId: 'cswem-menugroup', // ID группы меню styleId: 'cswem-styles', // ID стилей hiddenItems: [ // Элементы для скрытия '_5dd73bb9-e728-4d1e-990b-c77d8e03670f_-menuitem-_root_menu', 'context-searchselect', 'context-keywordfield' ] }; // Проверяем, не запущен ли скрипт ранее if (window[SETTINGS.initializedFlag]) return; window[SETTINGS.initializedFlag] = true; try { // ██████████████████████████████████████████████████████████████ // █ 1. ДОБАВЛЯЕМ СТИЛИ ДЛЯ СКРЫТИЯ ЛИШНИХ ЭЛЕМЕНТОВ И НАСТРОЙКИ МЕНЮ // ██████████████████████████████████████████████████████████████ const style = document.createElement('style'); style.id = SETTINGS.styleId; style.textContent = ` #${SETTINGS.menuId} { padding-left: 30px; display: grid; grid-template-columns: repeat(auto-fill, 32px); grid-auto-rows: 26px; gap: 2px; } #${SETTINGS.menuId} > menuitem { -moz-box-pack: center; list-style-image: var(--image) !important; } ${SETTINGS.hiddenItems.map(id => `#${id}`).join(', ')}, #${SETTINGS.menuId}:empty { display: none !important; } `; document.head.appendChild(style); // ██████████████████████████████████████████████████████████████ // █ 2. СОЗДАЕМ КОНТЕЙНЕР ДЛЯ ИКОНОК // ██████████████████████████████████████████████████████████████ const menugroup = document.createXULElement('menugroup'); menugroup.id = SETTINGS.menuId; // Размещаем после поисковой строки или в начало меню const searchSelect = document.getElementById('context-searchselect'); if (searchSelect) { searchSelect.after(menugroup); } else { document.getElementById('contentAreaContextMenu').prepend(menugroup); } // ██████████████████████████████████████████████████████████████ // █ 3. ФУНКЦИЯ ФИЛЬТРАЦИИ: ОПРЕДЕЛЯЕМ, КАКИЕ ЭЛЕМЕНТЫ ОСТАВИТЬ // ██████████████████████████████████████████████████████████████ const shouldKeepItem = (item) => { const forbiddenClasses = ['menuitem-iconic-webext', 'extension-menu-item']; const forbiddenIds = ['ublock', 'adguard', 'grammarly', 'ext-', 'extension-', ...SETTINGS.hiddenItems]; return ( item.tagName === 'menuitem' && !item.hidden && item.getAttribute('image') && item.getAttribute('label') && !item.id?.includes('_separator') && !forbiddenClasses.some(c => item.classList.contains(c)) && !forbiddenIds.some(id => item.id?.includes(id)) ); }; // ██████████████████████████████████████████████████████████████ // █ 4. ОБНОВЛЕНИЕ МЕНЮ: УДАЛЯЕМ СТАРЫЕ ЭЛЕМЕНТЫ И ДОБАВЛЯЕМ НОВЫЕ // ██████████████████████████████████████████████████████████████ const refreshMenu = () => { // Полностью очищаем меню перед обновлением while (menugroup.firstChild) { menugroup.removeChild(menugroup.firstChild); } // Находим все элементы меню и фильтруем их const allMenuItems = Array.from(document.querySelectorAll('#contentAreaContextMenu menuitem')); const validItems = allMenuItems.filter(shouldKeepItem); // Клонируем и добавляем только нужные элементы validItems.forEach(item => { const clone = item.cloneNode(true); clone.id = ''; // Удаляем ID, чтобы избежать конфликтов // Назначаем обработчик клика clone.addEventListener('command', () => { try { item.click(); } catch (e) { console.error('Ошибка при клике:', e); } }); menugroup.appendChild(clone); }); // Скрываем группу, если нет элементов menugroup.hidden = menugroup.children.length === 0; }; // ██████████████████████████████████████████████████████████████ // █ 5. НАСТРОЙКА СОБЫТИЙ: ОБНОВЛЯЕМ МЕНЮ ПРИ КАЖДОМ ОТКРЫТИИ // ██████████████████████████████████████████████████████████████ const contextMenu = document.getElementById('contentAreaContextMenu'); contextMenu.addEventListener('popupshowing', refreshMenu); // ██████████████████████████████████████████████████████████████ // █ 6. ОЧИСТКА: УДАЛЯЕМ ЭЛЕМЕНТЫ ПРИ ЗАКРЫТИИ СТРАНИЦЫ // ██████████████████████████████████████████████████████████████ window.addEventListener('unload', () => { contextMenu.removeEventListener('popupshowing', refreshMenu); document.getElementById(SETTINGS.menuId)?.remove(); document.getElementById(SETTINGS.styleId)?.remove(); delete window[SETTINGS.initializedFlag]; }); } catch (error) { console.error('Ошибка в скрипте контекстного меню:', error); } })();
Кинуть код в поле «Инициализация».
Иконки поисковых систем автоматически подгружаются в контекстное меню из установленного дополнения ContextSearch-web-ext.
Отредактировано leex (Сегодня 12:23:42)
Отсутствует