Skip to content

Commit

Permalink
Minor Updates and Fixes (#15)
Browse files Browse the repository at this point in the history
* Added Visited Link Color and Flair

* Update manifest.json and README.md

* Fix Keybind Alternate Keys and Update Text Parser

* Enable Tool-tips on Popup for Link Buttons
  • Loading branch information
smashedr authored Oct 30, 2023
1 parent 162e145 commit 22c898e
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 73 deletions.
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,54 @@
[![Build](https://github.com/cssnr/link-extractor/actions/workflows/build.yaml/badge.svg)](https://github.com/cssnr/link-extractor/actions/workflows/build.yaml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cssnr_link-extractor&metric=alert_status)](https://sonarcloud.io/summary/overall?id=cssnr_link-extractor)
[![GitHub Release](https://img.shields.io/github/v/release/cssnr/link-extractor)](https://github.com/cssnr/link-extractor/releases/latest)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=cssnr_link-extractor&metric=alert_status&label=quality)](https://sonarcloud.io/summary/overall?id=cssnr_link-extractor)
[![Manifest Version](https://img.shields.io/github/manifest-json/v/cssnr/link-extractor?filename=src%2Fmanifest.json&logo=json&label=manifest)](https://github.com/cssnr/link-extractor/blob/master/src/manifest.json)
[![GitHub Release Version](https://img.shields.io/github/v/release/cssnr/link-extractor?logo=github)](https://github.com/cssnr/link-extractor/releases/latest)
[![Chrome Web Store Version](https://img.shields.io/chrome-web-store/v/ifefifghpkllfibejafbakmflidjcjfp?label=chrome&logo=googlechrome)](https://chrome.google.com/webstore/detail/link-extractor/ifefifghpkllfibejafbakmflidjcjfp)
[![Mozilla Add-on Version](https://img.shields.io/amo/v/link-extractor?label=firefox&logo=firefox)](https://addons.mozilla.org/addon/link-extractor)
[![Chrome Web Store Users](https://img.shields.io/chrome-web-store/users/ifefifghpkllfibejafbakmflidjcjfp?logo=google&logoColor=white&label=google%20users)](https://chrome.google.com/webstore/detail/link-extractor/ifefifghpkllfibejafbakmflidjcjfp)
[![Mozilla Add-on Users](https://img.shields.io/amo/users/link-extractor?logo=mozilla&label=mozilla%20users)](https://addons.mozilla.org/addon/link-extractor)
# Link Extractor

Modern Chrome and Firefox Addon to easily extract, parse, and open all links/domains from a web page with optional filters.
Modern Chrome and Firefox Addon to easily extract, parse, or open all links/domains from a site or text with optional filters.
Feature packed with automatic dark/light mode, copy to clipboard, keyboard shortcuts, custom options, and much more...

* [Install](#install)
* [Features](#features)
* [Configuration](#configuration)
* [Development](#development)
- [Chrome](#chrome)
- [Firefox](#firefox)
- [Chrome Setup](#chrome-setup)
- [Firefox Setup](#firefox-setup)

# Install

* Google Chrome: https://chrome.google.com/webstore/detail/link-extractor/ifefifghpkllfibejafbakmflidjcjfp
* Mozilla Firefox: https://addons.mozilla.org/addon/link-extractor

You may also download the latest [Release](https://github.com/cssnr/link-extractor/releases) from GitHub for
[Chrome](https://github.com/cssnr/link-extractor/releases/latest/download/link_extractor-chrome.crx) or
[Firefox](https://github.com/cssnr/link-extractor/releases/latest/download/link_extractor-firefox.xpi).

# Features

Please submit a [Feature Request](https://github.com/cssnr/link-extractor/discussions/new?category=feature-requests)
for any new features you would like to see implemented.
for any new features you would like to see implemented and if you find any problems, please
[Open an Issue](https://github.com/cssnr/link-extractor/issues/new).


* Extract all Links and/or Domains
* Parse or open links from text or clipboard
* Copy all URLs or Domains to the clipboard
* Quick Filter URLs with a regular expression
* Quick Filter links by a saved regular expressions
* Automatic Dark/Light mode based on browser setting
* Keyboard Shortcuts for Copying Links or Domains
* Keyboard Shortcuts for Copying Links and/or Domains

[![Screenshot of Links and Popup](https://repository-images.githubusercontent.com/707614074/7807bbb5-ec14-4fae-85d8-e3252a460cff)](https://github.com/cssnr/link-extractor)

# Configuration

You can pin the Addon by clicking the `Puzzle Piece`, find the Link Extractor icon, then;
**Firefox**, click the `Settings Wheel` and `Pin to Toolbar`.
**Chrome**, click the `Pin` icon.
**Chrome,** click the `Pin` icon.
**Firefox,** click the `Settings Wheel` and `Pin to Toolbar`.

To open the options, click on the icon (from above) then click `Open Options`.
Here you can set flags and add as many saved regular expressions as you would like for easy use later.
Expand All @@ -58,27 +65,23 @@ Files are copied automatically after `npm install`. See [gulpfile.js](gulpfile.j
The extension is automatically built on new releases then automatically uploaded to that release.
See [build.yaml](.github/workflows/build.yaml) for more information.

## Chrome
## Chrome Setup

To install for normal use, visit: https://chrome.google.com/webstore/detail/link-extractor/ifefifghpkllfibejafbakmflidjcjfp

1. Download a [Release](https://github.com/cssnr/link-extractor/releases).
1. Unzip the archive, place the folder where it must remain and note its location for later.
1. Open Chrome, click the `3 dots` in the top right, click `Extensions`, click `Manage Extensions`.
1. In the top right, click `Developer Mode` then on the top left click `Load unpacked`.
1. Navigate to the folder you extracted in step #3 then click `Select Folder`.

## Firefox
## Firefox Setup

For development, you can and should load unpacked in Firefox as a temporary addon.
This will **not** remain after restarting Firefox. It is also useful to keep data after removing an extension.
To install for normal use, visit: https://addons.mozilla.org/addon/link-extractor

Firefox Temporary addon's will **not** remain after restarting Firefox, therefore;
it is very useful to keep addon storage after uninstall/restart with `keepStorageOnUninstall`.

1. Download a [Release](https://github.com/cssnr/link-extractor/releases).
1. Load temporary from: `about:debugging#/runtime/this-firefox`
1. Open `about:config` search for `extensions.webextensions.keepStorageOnUninstall` and set to `true`.

> **Note**
>
> This method **does not** work on Release Firefox and is NOT recommended for development.
> You must use [ESR](https://www.mozilla.org/en-CA/firefox/all/#product-desktop-esr), Development, or Nightly.
1. Open `about:config` search for `xpinstall.signatures.required` and set to `false`.
1. Open `about:addons` and drag the zip file to the page or choose Install from File from the Settings wheel.
3 changes: 3 additions & 0 deletions src/css/links.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a:visited {
color: #bf40bf;
}
2 changes: 1 addition & 1 deletion src/css/popup.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
body {
width: 300px;
width: 320px;
}

input::placeholder,
Expand Down
14 changes: 9 additions & 5 deletions src/html/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@
<label for="links-text" class="form-label visually-hidden">Open Links</label>
<textarea id="links-text" class="form-control form-control-sm" rows="2" placeholder="Paste Links to Parse/Open"></textarea>
</form>
<div class="btn-group btn-group-sm w-100" role="group" aria-label="Open Links">
<button type="submit" form="links-form" class="btn btn-outline-warning disabled" id="parse-links" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Parse All Links" data-text="Parse">Parse</button>
<button type="submit" form="links-form" class="btn btn-outline-warning disabled" id="open-links" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Open All Links" data-text="Open">Open</button>

<div class="btn-group btn-group-sm w-100" role="group" aria-label="Parse/Open Links">
<button type="submit" form="links-form" class="btn btn-outline-warning disabled parse-links" id="parse-links" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Parse Links" data-text="Parse">Parse</button>
<button type="submit" form="links-form" class="btn btn-outline-warning disabled parse-links" id="open-parsed" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Open Parsed Links" data-text="Open">Open</button>
<button type="submit" form="links-form" class="btn btn-outline-warning disabled parse-lines" id="open-lines" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Open Each Line" data-text="Open">Open</button>
</div>

<button class="btn btn-outline-info btn-sm popup-click" type="button" data-href="html/options.html">Options</button>
<p class="mb-0 text-center small">
<a id="btn-about" class="link-offset-2 link-underline link-underline-opacity-0 link-underline-opacity-75-hover popup-click" type="button" rel="noopener"
Expand Down
84 changes: 57 additions & 27 deletions src/js/links.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
// JS for links.html

window.addEventListener('keydown', checkKey)
let keysPressed = {}
window.onblur = function () {
keysPressed = {}
}
window.addEventListener('keydown', handleKeybinds)
document.addEventListener('keyup', (event) => {
delete keysPressed[event.key]
})

document.addEventListener('DOMContentLoaded', initLinks)

const urlParams = new URLSearchParams(window.location.search)
Expand Down Expand Up @@ -96,6 +104,24 @@ async function processLinks(links) {
document.getElementById('message').style.display = 'none'
}

/**
* Get base URL of link
* @function getBaseURL
* @param {string} link
* @return string
*/
function getBaseURL(link) {
const reBaseURL = /(^\w+:\/\/[^/]+)|(^[A-Za-z0-9.-]+)\/|(^[A-Za-z0-9.-]+$)/
const result = RegExp(reBaseURL).exec(link)
if (!result) {
return null
} else if (result[1]) {
return `${result[1]}/`
} else {
return `http://${result[2] || result[3]}/`
}
}

/**
* Update Table with URLs
* @function addNodes
Expand All @@ -116,41 +142,45 @@ function updateTable(data, elementId) {
}

/**
* Get base URL of link
* @function getBaseURL
* @param {string} link
* @return string
*/
function getBaseURL(link) {
const reBaseURL = /(^\w+:\/\/[^/]+)|(^[A-Za-z0-9.-]+)\/|(^[A-Za-z0-9.-]+$)/
const result = RegExp(reBaseURL).exec(link)
if (!result) {
return null
} else if (result[1]) {
return `${result[1]}/`
} else {
return `http://${result[2] || result[3]}/`
}
}

/**
* Keyboard Callback
* @function checkKey
* Keyboard Event Callback
* @function handleKeybinds
* @param {KeyboardEvent} event
*/
function checkKey(event) {
function handleKeybinds(event) {
// console.log('handleKeybinds:', event)
const formElements = ['INPUT', 'TEXTAREA', 'SELECT', 'OPTION']
if (!formElements.includes(event.target.tagName)) {
// console.log(event.code)
if (event.code === 'KeyC' || event.code === 'KeyL') {
keysPressed[event.key] = true
if (checkKey(event, ['KeyC', 'KeyL'])) {
document.getElementById('links-clip').click()
} else if (event.code === 'KeyD' || event.code === 'KeyM') {
} else if (checkKey(event, ['KeyD', 'KeyM'])) {
document.getElementById('domains-clip').click()
} else if (event.code === 'KeyT' || event.code === 'KeyO') {
} else if (checkKey(event, ['KeyT', 'KeyO'])) {
const url = chrome.runtime.getURL('../html/options.html')
chrome.tabs.create({ active: true, url: url }).then()
} else if (event.code === 'KeyZ' || event.code === 'KeyK') {
} else if (checkKey(event, ['KeyZ', 'KeyK'])) {
$('#keybinds-modal').modal('toggle')
}
}
}

/**
* Check Key Down Combination
* @function checkKey
* @param {KeyboardEvent} event
* @param {array} keys
* @return {boolean}
*/
function checkKey(event, keys) {
const ctrlKeys = ['Control', 'Alt', 'Shift', 'Meta']
let hasCtrlKey = false
ctrlKeys.forEach(function (key) {
if (keysPressed[key]) {
hasCtrlKey = true
}
})
if (hasCtrlKey) {
return false
}
return !!keys.includes(event.code)
}
55 changes: 38 additions & 17 deletions src/js/popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ document.getElementById('filter-form').addEventListener('submit', popupClick)
document.getElementById('links-form').addEventListener('submit', linksForm)
document.getElementById('links-text').addEventListener('input', updateLinks)

// const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
// const tooltipList = [...tooltipTriggerList].map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl))
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]'
)
const tooltipList = [...tooltipTriggerList].map(
(tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl)
)

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log(`popup.js: request.msg: ${request.msg}`)
Expand Down Expand Up @@ -125,18 +129,20 @@ async function linksForm(event) {
event.preventDefault()
console.log('linksForm:', event)
const urls = extractURLs(event.target[0].value)
if (!urls.length) {
return console.error('No Links Parsed.')
}
if (event.submitter.id === 'parse-links') {
// let urls = event.target[0].value.split(/\r\n?|\n/g)
// urls = urls.map((string) => string.trim())
const url = new URL(chrome.runtime.getURL('../html/links.html'))
url.searchParams.set('popup', 'yes')
await chrome.tabs.create({ active: true, url: url.toString() })
} else if (event.submitter.id === 'open-links') {
} else if (event.submitter.id === 'open-parsed') {
openLinksInTabs(urls)
window.close()
} else if (event.submitter.id === 'open-lines') {
let text = event.target[0].value.split(/\r\n?|\n/g)
text = text.filter((str) => str !== '')
openLinksInTabs(text)
window.close()
} else {
console.error('Unknown event.submitter:', event.submitter)
}
Expand All @@ -149,19 +155,34 @@ async function linksForm(event) {
*/
function updateLinks(event) {
// console.log('updateLinks:', event)
let text = event.target.value.split(/\r\n?|\n/g)
text = text.filter((str) => str !== '')
const urls = extractURLs(event.target.value)
console.log(`urls.length: ${urls.length}`)
const parse = document.getElementById('parse-links')
const open = document.getElementById('open-links')
parse.textContent = `${parse.dataset.text} (${urls.length})`
open.textContent = `${open.dataset.text} (${urls.length})`
if (urls?.length > 0) {
parse.classList.remove('disabled')
open.classList.remove('disabled')
// const re = new RegExp('btn-[a-z]+-?[a-z]+', 'gi')
const parseLines = document.getElementsByClassName('parse-lines')
Array.from(parseLines).forEach(function (el) {
updateElements(el, text)
})
const parseLinks = document.getElementsByClassName('parse-links')
Array.from(parseLinks).forEach(function (el) {
updateElements(el, urls)
})
}

/**
* Update Elements based on Array lines
* @function extractURLs
* @param {HTMLElement} el
* @param {array} lines
*/
function updateElements(el, lines) {
// console.log('el:', el)
// console.log('lines:', lines)
if (lines?.length > 0) {
el.classList.remove('disabled')
el.textContent = `${el.dataset.text} (${lines.length})`
} else {
parse.classList.add('disabled')
open.classList.add('disabled')
el.classList.add('disabled')
el.textContent = `${el.dataset.text}`
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "Link Extractor",
"description": "Modern Chrome and Firefox Addon to extract all links from a web page with optional filters.",
"description": "Easily extract, parse, or open all links/domains from a site or text with optional filters.",
"homepage_url": "https://github.com/cssnr/link-extractor",
"version": "0.0.8",
"version": "0.0.9",
"manifest_version": 3,
"permissions": [
"activeTab",
Expand All @@ -15,6 +15,7 @@
},
"action": {
"default_popup": "html/popup.html",
"default_title": "Link Extractor",
"default_icon": {
"16": "images/logo16.png",
"32": "images/logo32.png",
Expand Down

0 comments on commit 22c898e

Please sign in to comment.