Mercurial > addons > firefox-addons > context-web-search
diff background.js @ 0:2050741e9711
Initial revision
author | Guido Berhoerster <guido+cws@berhoerster.name> |
---|---|
date | Tue, 18 Sep 2018 22:57:18 +0200 |
parents | |
children | f77dab12bb52 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/background.js Tue Sep 18 22:57:18 2018 +0200 @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 Guido Berhoerster <guido+cws@berhoerster.name> + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +'use strict'; + +function encodeXML(str) { + return str.replace(/[<>&'"]/g, c => { + switch (c) { + case '<': return '<'; + case '>': return '>'; + case '&': return '&'; + case '\'': return '''; + case '"': return '"'; + } + }); +} + +function createFavIcon(name) { + let firstLetter = name.trim().charAt(0); + if (firstLetter === '') { + firstLetter = '?'; + } + let svg = `<svg viewBox="0 0 16 16" ` + + `xmlns="http://www.w3.org/2000/svg">` + + `<defs>` + + `<filter id="ds">` + + `<feDropShadow dx="0" dy="0" stdDeviation=".5"/>` + + `</filter>` + + `</defs>` + + `<rect width="16" height="16" rx="3" ry="3" fill="#ed00b5"/>` + + `<text x="8" y="12" alignment-baseline="middle" fill="#fff" ` + + `filter="url(#ds)" font-family="sans-serif" font-size="11px" ` + + `font-weight="bold" text-anchor="middle">` + + `${encodeXML(firstLetter)}</text>` + + `</svg>`; + return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}` +} + +async function createMenu(isSelection) { + let searchEngines = await browser.search.get(); + let menuItems = new Set(); + for (let engine of searchEngines) { + let favIcon = typeof engine.favIconUrl !== 'undefined' ? + engine.favIconUrl : createFavIcon(engine.name); + let menuItemId = browser.menus.create({ + id: engine.name, + title: browser.i18n.getMessage(isSelection ? + 'searchSelectionWithEngine' : 'searchLinkWithEngine', + engine.name.trim()), + icons: {16: favIcon}, + contexts: ['link', 'selection'] + }, async () => { + if (browser.runtime.lastError) { + console.log(`Failed to create menu item:` + + `${browser.runtime.lastError}`); + } + + // browser.menus.create does not return a promise that can be + // awaited, thus collect the returned menu item IDs in a set and + // remove them from this callback which is called when the + // asynchronous creation function has finished; when the set is + // empty all creation functions have finished and the menu can be + // refreshed + menuItems.delete(menuItemId); + if (menuItems.size === 0) { + try { + await browser.menus.refresh(); + } catch (e) { + console.log(`Failed to refresh menu: ${e.message}`); + } + } + }); + menuItems.add(menuItemId); + } +} + +browser.menus.onShown.addListener(async (info, tab) => { + try { + await createMenu(info.contexts.includes('selection')); + } catch (e) { + console.log(e); + } +}); + +browser.menus.onHidden.addListener(async () => { + try { + await browser.menus.removeAll(); + } catch (e) { + console.log(`Failed to remove menu items: ${e.message}`); + } + try { + await browser.menus.refresh(); + } catch (e) { + console.log(`Failed to refresh menu: ${e.message}`); + } +}); + +browser.menus.onClicked.addListener(async (info, tab) => { + let query = typeof info.selectionText !== 'undefined' ? + info.selectionText.trim() : info.linkText.trim(); + try { + await browser.search.search({ + query: query, + engine: info.menuItemId + }); + } catch (e) { + console.log(`Failed to search for "${query}": ${e.message}`); + } +});