+
+
+
Options
diff --git a/src/js/exports.js b/src/js/exports.js
index c44f1f1..72e9fb7 100644
--- a/src/js/exports.js
+++ b/src/js/exports.js
@@ -3,9 +3,10 @@
/**
* Inject extract.js to Tab and Open links.html with params
* @function processLinks
- * @param {String} filter Regex Filter
- * @param {Boolean} domains Only Domains
- * @param {Boolean} selection Only Selection
+ * @param {String} [filter] Regex Filter
+ * @param {Boolean} [domains] Only Domains
+ * @param {Boolean} [selection] Only Selection
+ * @param {chrome.tabs[]} tabs Tabs for Extraction
*/
export async function injectTab({
filter = null,
@@ -13,16 +14,30 @@ export async function injectTab({
selection = false,
} = {}) {
console.log('injectTab:', filter, domains, selection)
+ const tabIds = []
- // Get Current Tab
- const [tab] = await chrome.tabs.query({ currentWindow: true, active: true })
- console.debug(`tab: ${tab.id}`, tab)
+ // Extract tabIds from tabs
+ const tabs = await chrome.tabs.query({ highlighted: true })
+ if (!tabs.length) {
+ const [tab] = await chrome.tabs.query({
+ currentWindow: true,
+ active: true,
+ })
+ console.debug(`tab: ${tab.id}`, tab)
+ tabIds.push(tab.id)
+ } else {
+ for (const tab of tabs) {
+ console.debug(`tab: ${tab.id}`, tab)
+ tabIds.push(tab.id)
+ }
+ }
+ console.log('tabIds:', tabIds)
// Create URL to links.html
const url = new URL(chrome.runtime.getURL('../html/links.html'))
// Set URL searchParams
- url.searchParams.set('tab', tab.id.toString())
+ url.searchParams.set('tabs', tabIds.join(','))
if (filter) {
url.searchParams.set('filter', filter)
}
@@ -34,11 +49,13 @@ export async function injectTab({
}
// Inject extract.js which listens for messages
- await chrome.scripting.executeScript({
- target: { tabId: tab.id },
- files: ['/js/extract.js'],
- })
-
+ for (const tab of tabIds) {
+ console.debug(`injecting tab.id: ${tab}`)
+ await chrome.scripting.executeScript({
+ target: { tabId: tab },
+ files: ['/js/extract.js'],
+ })
+ }
// Open Tab to links.html with desired params
console.debug(`url: ${url.toString()}`)
await chrome.tabs.create({ active: true, url: url.toString() })
@@ -113,10 +130,9 @@ export async function exportClick(event) {
event.preventDefault()
const name = event.target.dataset.importName
console.debug('name:', name)
- const display = event.target.dataset.importDisplay
- console.debug('display:', display)
+ const display = event.target.dataset.importDisplay || name
const data = await chrome.storage.sync.get()
- console.debug('data:', data[name])
+ // console.debug('data:', data[name])
if (!data[name].length) {
return showToast(`No ${display} Found!`, 'warning')
}
@@ -146,8 +162,8 @@ export async function importChange(event) {
event.preventDefault()
const name = event.target.dataset.importName
console.debug('name:', name)
- const display = event.target.dataset.importDisplay
- console.debug('display:', display)
+ const display = event.target.dataset.importDisplay || name
+ // console.debug('display:', display)
const importInput = document.getElementById('import-input')
if (!importInput.files?.length) {
return console.debug('No importInput.files', importInput)
@@ -198,3 +214,80 @@ export function textFileDownload(filename, text) {
element.click()
document.body.removeChild(element)
}
+
+/**
+ * Request Host Permissions
+ * @function requestPerms
+ * @return {chrome.permissions.request}
+ */
+export async function requestPerms() {
+ return await chrome.permissions.request({
+ origins: ['*://*/*'],
+ })
+}
+
+/**
+ * Check Host Permissions
+ * @function checkPerms
+ * @return {Boolean}
+ */
+export async function checkPerms() {
+ const hasPerms = await chrome.permissions.contains({
+ origins: ['*://*/*'],
+ })
+ console.debug('checkPerms:', hasPerms)
+ // Firefox still uses DOM Based Background Scripts
+ if (typeof document === 'undefined') {
+ return hasPerms
+ }
+ const hasPermsEl = document.querySelectorAll('.has-perms')
+ const grantPermsEl = document.querySelectorAll('.grant-perms')
+ if (hasPerms) {
+ hasPermsEl.forEach((el) => el.classList.remove('d-none'))
+ grantPermsEl.forEach((el) => el.classList.add('d-none'))
+ } else {
+ grantPermsEl.forEach((el) => el.classList.remove('d-none'))
+ hasPermsEl.forEach((el) => el.classList.add('d-none'))
+ }
+ return hasPerms
+}
+
+/**
+ * Revoke Permissions Click Callback
+ * NOTE: For many reasons Chrome will determine host_perms are required and
+ * will ask for them at install time and not allow them to be revoked
+ * @function revokePerms
+ * @param {Event} event
+ */
+export async function revokePerms(event) {
+ console.debug('revokePerms:', event)
+ const permissions = await chrome.permissions.getAll()
+ console.debug('permissions:', permissions)
+ try {
+ await chrome.permissions.remove({
+ origins: permissions.origins,
+ })
+ await checkPerms()
+ } catch (e) {
+ console.log(e)
+ showToast(e.toString(), 'danger')
+ }
+}
+
+/**
+ * Permissions On Added Callback
+ * @param permissions
+ */
+export async function onAdded(permissions) {
+ console.debug('onAdded', permissions)
+ await checkPerms()
+}
+
+/**
+ * Permissions On Added Callback
+ * @param permissions
+ */
+export async function onRemoved(permissions) {
+ console.debug('onRemoved', permissions)
+ await checkPerms()
+}
diff --git a/src/js/links.js b/src/js/links.js
index 4874930..990c754 100644
--- a/src/js/links.js
+++ b/src/js/links.js
@@ -39,18 +39,34 @@ const dtOptions = {
async function initLinks() {
console.log('initLinks: urlParams:', urlParams)
try {
- const tabId = parseInt(urlParams.get('tab'))
+ const tabIds = urlParams.get('tabs')
+ const tabs = tabIds.split(',')
+ console.log('tabs:', tabs)
const selection = urlParams.has('selection')
- console.debug(`tabId: ${tabId}, selection: ${selection}`)
+ // console.debug(`tabId: ${tabId}, selection: ${selection}`)
- if (tabId) {
- const action = selection ? 'selection' : 'all'
- const links = await chrome.tabs.sendMessage(tabId, action)
- await processLinks(links)
+ // TODO: Populate Links to links then processLinks
+ const allLinks = []
+ if (tabs.length) {
+ console.log('processing tabs:', tabs)
+ // const tabId = parseInt(tabs[0])
+ for (const tabId of tabs) {
+ console.log('tabId:', tabId)
+ const action = selection ? 'selection' : 'all'
+ console.log('action:', action)
+ const links = await chrome.tabs.sendMessage(
+ parseInt(tabId),
+ action
+ )
+ allLinks.push(...links)
+ // await processLinks(links)
+ }
} else {
const { links } = await chrome.storage.local.get(['links'])
- await processLinks(links)
+ allLinks.push(...links)
+ // await processLinks(links)
}
+ await processLinks(allLinks)
} catch (e) {
console.warn('error:', e)
alert('Error Processing Results. See Console for More Details...')
diff --git a/src/js/main.js b/src/js/main.js
index 05a3885..42d09e8 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -69,7 +69,7 @@ function showToast(message, type = 'success') {
* @param {Function} fn
* @param {Number} timeout
*/
-function debounce(fn, timeout = 300) {
+function debounce(fn, timeout = 250) {
let timeoutID
return (...args) => {
clearTimeout(timeoutID)
diff --git a/src/js/options.js b/src/js/options.js
index 5e82576..c67f58a 100644
--- a/src/js/options.js
+++ b/src/js/options.js
@@ -1,29 +1,39 @@
// JS for options.html
import {
+ checkPerms,
exportClick,
importChange,
importClick,
+ onAdded,
+ onRemoved,
+ requestPerms,
+ revokePerms,
saveOptions,
updateOptions,
} from './exports.js'
chrome.storage.onChanged.addListener(onChanged)
+chrome.permissions.onAdded.addListener(onAdded)
+chrome.permissions.onRemoved.addListener(onRemoved)
+
document.addEventListener('DOMContentLoaded', initOptions)
document.addEventListener('blur', filterClick)
document.addEventListener('click', filterClick)
document.getElementById('update-filter').addEventListener('submit', filterClick)
document.getElementById('filters-form').addEventListener('submit', addFilter)
document.getElementById('reset-default').addEventListener('click', resetForm)
-document
- .querySelectorAll('[data-bs-toggle="tooltip"]')
- .forEach((el) => new bootstrap.Tooltip(el))
+document.getElementById('grant-perms').addEventListener('click', grantPerms)
+document.getElementById('revoke-perms').addEventListener('click', revokePerms)
const optionsForm = document.getElementById('options-form')
optionsForm.addEventListener('submit', (e) => e.preventDefault())
optionsForm
.querySelectorAll('input, select')
.forEach((el) => el.addEventListener('change', saveOptions))
+document
+ .querySelectorAll('[data-bs-toggle="tooltip"]')
+ .forEach((el) => new bootstrap.Tooltip(el))
// Data Import/Export
document.getElementById('export-data').addEventListener('click', exportClick)
@@ -53,6 +63,7 @@ async function initOptions() {
document.getElementById('extractKey').textContent =
commands.find((x) => x.name === 'extract').shortcut || 'Not Set'
+ await checkPerms()
document.getElementById('add-filter').focus()
}
@@ -306,3 +317,13 @@ async function resetForm(event) {
input.focus()
await saveOptions(event)
}
+
+/**
+ * Grant Permissions Click Callback
+ * @function grantPerms
+ * @param {MouseEvent} event
+ */
+export async function grantPerms(event) {
+ console.debug('grantPerms:', event)
+ await requestPerms()
+}
diff --git a/src/js/popup.js b/src/js/popup.js
index c75705c..8c0f30c 100644
--- a/src/js/popup.js
+++ b/src/js/popup.js
@@ -1,11 +1,18 @@
// JS for popup.html
-import { injectTab, saveOptions, updateOptions } from './exports.js'
+import {
+ checkPerms,
+ injectTab,
+ requestPerms,
+ saveOptions,
+ updateOptions,
+} from './exports.js'
document.addEventListener('DOMContentLoaded', initPopup)
document.getElementById('filter-form').addEventListener('submit', filterForm)
document.getElementById('links-form').addEventListener('submit', linksForm)
document.getElementById('links-text').addEventListener('input', updateLinks)
+document.getElementById('grant-perms').addEventListener('click', grantPerms)
document
.querySelectorAll('a[href]')
.forEach((el) => el.addEventListener('click', popupLinks))
@@ -19,6 +26,16 @@ document
.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach((el) => new bootstrap.Tooltip(el))
+// document.getElementById('allTabs').addEventListener('click', allTabs)
+//
+// async function allTabs(event) {
+// console.debug('allTabs:', event)
+// event.preventDefault()
+// const tabs = await chrome.tabs.query({ highlighted: true })
+// console.debug('tabs:', tabs)
+// await injectTab({ tabs: tabs })
+// }
+
/**
* Initialize Popup
* @function initOptions
@@ -45,6 +62,14 @@ async function initPopup() {
document.getElementById('homepage_url').href = manifest.homepage_url
document.getElementById('filter-input').focus()
+
+ const tabs = await chrome.tabs.query({ highlighted: true })
+ console.debug('tabs:', tabs)
+ if (tabs.length > 1) {
+ console.info('Multiple Tabs Selected')
+ }
+
+ await checkPerms()
}
/**
@@ -206,3 +231,15 @@ function extractURLs(text) {
}
return urls
}
+
+/**
+ * Grant Permissions Click Callback
+ * Promise from requestPerms is ignored so we can close the popup immediately
+ * @function grantPerms
+ * @param {MouseEvent} event
+ */
+export async function grantPerms(event) {
+ console.debug('grantPerms:', event)
+ requestPerms()
+ window.close()
+}