diff --git a/src/background.js b/src/background.js index 05f0bf2..93205e1 100644 --- a/src/background.js +++ b/src/background.js @@ -1,3 +1,92 @@ +import utils from './utils'; + global.browser = require('webextension-polyfill'); +const menu = { + hosts: {}, + actions: {}, + scripts: {}, + categories: {}, + separator: '//', + + categoriesByHost(host) { + return Object.entries({ ...(this.actions || {}), ...(this.scripts || {}) }).reduce((obj, [actionName, action]) => { + if (action.hosts.indexOf(host) < 0) { + return obj; + } + + const appendAction = category => { + if (!(category in obj)) { + obj[category] = {}; + } + + obj[category][actionName] = action; + }; + + if (!(action.categories && action.categories.length)) { + appendAction(''); + } else { + action.categories.forEach(category => appendAction(category)); + } + + return obj; + }, {}); + }, + + async refresh() { + this.hosts = await utils.methods.getHosts(); + this.actions = await utils.methods.getActions(); + this.scripts = await utils.methods.getScripts(); + browser.contextMenus.removeAll(); + + for (const [host] of Object.entries(this.hosts)) { + const hostId = this.separator + host; + browser.contextMenus.create({ + id: hostId, + title: host, + }); + + const categories = this.categoriesByHost(host); + for (const [categoryName, category] of Object.entries(categories)) { + const categoryId = hostId + this.separator + (categoryName.length ? categoryName : '[NONE]'); + browser.contextMenus.create({ + id: categoryId, + parentId: hostId, + title: categoryName.length ? categoryName : '[No Category]', + }); + + for (const [action] of Object.entries(category)) { + const actionId = categoryId + this.separator + action; + browser.contextMenus.create({ + id: actionId, + parentId: categoryId, + title: action, + }); + } + } + } + + browser.contextMenus.onClicked.addListener(async (info, tab) => { + const [host, , action] = info.menuItemId.split(this.separator).slice(1); + const target = await utils.methods.getTargetElement(); + + if (action in this.actions) { + utils.methods.run(this.actions[action], this.hosts[host]); + } else { + utils.methods.runScript(this.scripts[action].script, this.hosts[host], tab, target); + } + }); + }, + + async create() { + await this.refresh(); + }, +}; + +const onCreate = () => { + menu.create(); +}; + +onCreate(); + // vim:sw=2:ts=2:et: diff --git a/src/content.js b/src/content.js index da96953..bec2a28 100644 --- a/src/content.js +++ b/src/content.js @@ -1,5 +1,9 @@ global.browser = require('webextension-polyfill'); +const context = { + targetElement: null, +}; + browser.runtime.onMessage.addListener((message, sender, sendResponse) => { switch (message.type) { case 'getURL': @@ -13,7 +17,15 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => { case 'setDOM': document.getElementsByTagName('html')[0].innerHTML = message.html; break; + + case 'getTargetElement': + sendResponse(context.targetElement ? context.targetElement.outerHTML : null); + break; } }); +document.addEventListener('contextmenu', event => { + context.targetElement = event.target; +}); + // vim:sw=2:ts=2:et: diff --git a/src/manifest.json b/src/manifest.json index 6eccac3..ca70da9 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -9,7 +9,7 @@ "48": "icons/icon-48.png", "64": "icons/icon-64.png" }, - "permissions": ["activeTab", "storage", "notifications", "clipboardRead", "clipboardWrite", ""], + "permissions": ["activeTab", "storage", "notifications", "clipboardRead", "clipboardWrite", "contextMenus", ""], "browser_action": { "default_title": "platypush", diff --git a/src/options/Config.vue b/src/options/Config.vue index 06efc55..60855d1 100644 --- a/src/options/Config.vue +++ b/src/options/Config.vue @@ -1,10 +1,9 @@ @@ -131,27 +130,56 @@ export default {