/** * FeHelper 右键菜单管理 * @type {{manage}} * @author zhaoxianlie */ import CrxDownloader from './crx-download.js'; import Awesome from './awesome.js'; import toolMap from './tools.js'; import Settings from '../options/settings.js'; export default (function () { let FeJson = { contextMenuId:"fhm_main", menuClickHandlers: {} }; // 邮件菜单配置项 let defaultMenuOptions = { 'download-crx': { icon: '♥', text: '插件下载分享', onClick: function (info, tab) { CrxDownloader.downloadCrx(tab); } }, 'fehelper-setting': { icon: '❂', text: 'FeHelper设置', onClick: function (info, tab) { chrome.runtime.openOptionsPage(); } } }; // 初始化菜单配置 let _initMenuOptions = (() => { Object.keys(toolMap).forEach(tool => { // context-menu switch (tool) { case 'json-format': toolMap[tool].menuConfig[0].onClick = function (info, tab) { chrome.scripting.executeScript({ target: {tabId:tab.id,allFrames:false}, args: [info.selectionText || ''], func: (text) => text }, resp => { if (chrome.runtime.lastError || !resp || !resp[0]) return; globalThis.FeHelperBg.DynamicToolRunner({ tool, withContent: resp[0].result }); }); }; break; case 'code-beautify': case 'en-decode': toolMap[tool].menuConfig[0].onClick = function (info, tab) { chrome.scripting.executeScript({ target: {tabId:tab.id,allFrames:false}, args: [info.linkUrl || info.srcUrl || info.selectionText || info.pageUrl || ''], func: (text) => text }, resp => { if (chrome.runtime.lastError || !resp || !resp[0]) return; globalThis.FeHelperBg.DynamicToolRunner({ tool, withContent: resp[0].result }); }); }; break; case 'qr-code': toolMap[tool].menuConfig[0].onClick = function (info, tab) { chrome.scripting.executeScript({ target: {tabId:tab.id,allFrames:false}, args: [info.linkUrl || info.srcUrl || info.selectionText || info.pageUrl || tab.url || ''], func: (text) => text }, resp => { if (chrome.runtime.lastError || !resp || !resp[0]) return; globalThis.FeHelperBg.DynamicToolRunner({ tool, withContent: resp[0].result }); }); }; toolMap[tool].menuConfig[1].onClick = function (info, tab) { chrome.scripting.executeScript({ target: {tabId:tab.id,allFrames:false}, args: [info.srcUrl || ''], func: (text) => { try { if (typeof window.qrcodeContentScript === 'function') { let qrcode = window.qrcodeContentScript(); if (typeof qrcode.decode === 'function') { qrcode.decode(text); return 1; } } } catch (e) { return 0; } } }); }; break; default: toolMap[tool].menuConfig[0].onClick = function (info, tab) { globalThis.FeHelperBg.DynamicToolRunner({ tool, withContent: tool === 'image-base64' ? info.srcUrl : '' }) }; break; } }); })(); // MV3: 单一全局监听器,通过 handler map 分发点击事件(避免 SW 重启后监听器丢失) chrome.contextMenus.onClicked.addListener((info, tab) => { let handler = FeJson.menuClickHandlers[info.menuItemId]; if (handler) { handler(info, tab); } }); /** * 创建一个menu 菜单 * @param toolName * @param menuList * @returns {boolean} * @private */ let _createItem = (toolName, menuList) => { menuList && menuList.forEach && menuList.forEach(menu => { let menuItemId = 'fhm_c' + escape(menu.text).replace(/\W/g,'') + Date.now() + Math.floor(Math.random() * 1000); chrome.contextMenus.create({ id: menuItemId, title: menu.icon + ' ' + menu.text, contexts: menu.contexts || ['all'], parentId: FeJson.contextMenuId }, () => { if (chrome.runtime.lastError) return; FeJson.menuClickHandlers[menuItemId] = menu.onClick || (() => { globalThis.FeHelperBg.DynamicToolRunner({ tool: toolName }); }); }); }); }; /** * 绘制一条分割线 * @private */ let _createSeparator = function () { chrome.contextMenus.create({ id: 'fhm_s' + Math.ceil(Math.random()*10e9), type: 'separator', parentId: FeJson.contextMenuId }, () => { chrome.runtime.lastError; }); }; /** * 创建扩展专属的右键菜单(debounce 防并发) */ let _initMenusTimer = null; let _initMenus = function () { clearTimeout(_initMenusTimer); _initMenusTimer = setTimeout(_doInitMenus, 80); }; let _doInitMenus = function () { FeJson.menuClickHandlers = {}; _removeContextMenu(() => { chrome.contextMenus.create({ id: FeJson.contextMenuId, title: "FeHelper", contexts: ['page', 'selection', 'editable', 'link', 'image'], documentUrlPatterns: ['http://*/*', 'https://*/*', 'file://*/*'] }, () => { if (chrome.runtime.lastError) return; _buildChildMenus(); }); }); }; let _buildChildMenus = function () { Awesome.getInstalledTools().then(tools => { let allMenus = Object.keys(tools).filter(tool => tools[tool].installed && tools[tool].menu); let onlineTools = allMenus.filter(tool => tool !== 'devtools' && !tools[tool].hasOwnProperty('_devTool')); let devTools = allMenus.filter(tool => tool === 'devtools' || tools[tool].hasOwnProperty('_devTool')); onlineTools.forEach(tool => _createItem(tool, tools[tool].menuConfig)); devTools.length && _createSeparator(); devTools.forEach(tool => { if(!tools[tool].menuConfig) { tools[tool].menuConfig = [{ icon: tools[tool].icon, text: tools[tool].name, onClick: (info, tab) => { globalThis.FeHelperBg.DynamicToolRunner({ page: 'dynamic', noPage: !!tools[tool].noPage, query: `tool=${tool}` }); if (tools[tool].noPage && typeof self !== 'undefined' && typeof self.close === 'function') { setTimeout(() => self.close(), 200); } } }]; } _createItem(tool, tools[tool].menuConfig); }); }); let sysMenu = ['download-crx', 'fehelper-setting']; let arrPromises = sysMenu.map(menu => Awesome.menuMgr(menu, 'get')); Promise.all(arrPromises).then(values => { _createSeparator(); String(values[0]) === '1' && _createItem(sysMenu[0], [defaultMenuOptions[sysMenu[0]]]); String(values[1]) !== '0' && _createItem(sysMenu[1], [defaultMenuOptions[sysMenu[1]]]); }); }; /** * 移除扩展专属的右键菜单 */ let _removeContextMenu = function (callback) { chrome.contextMenus.removeAll(callback); }; /** * 创建或移除扩展专属的右键菜单 */ let _createOrRemoveContextMenu = function () { Settings.getOptions((opts) => { if (String(opts['OPT_ITEM_CONTEXTMENUS']) !== 'false') { _initMenus(); } else { _removeContextMenu(); } }); }; return { rebuild: _createOrRemoveContextMenu }; })();