Skip to content

Updates #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "Easily extract, parse, or open all links/domains from a site or text with optional filters.",
"homepage_url": "https://link-extractor.cssnr.com/",
"author": "Shane",
"version": "0.1.5",
"version": "0.1.6",
"manifest_version": 3,
"commands": {
"_execute_action": {
Expand Down
6 changes: 6 additions & 0 deletions src/css/links.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
a:visited {
color: #bf40bf;
}

#top-container {
margin-top: 10px;
margin-right: 10px;
width: 160px;
}
65 changes: 51 additions & 14 deletions src/html/links.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,66 @@
</head>
<body>

<div id="top-container" class="position-fixed top-0 end-0 user-select-none visually-hidden links domains">
<div class="row g-1">
<div class="col">
<a href="#links"><span class="badge text-bg-success visually-hidden w-100 links">Links</span></a>
</div>
<div class="col">
<a role="button" class="clip" data-clipboard-target="#links-clip">
<span class="badge text-bg-success visually-hidden w-100 links">Copy</span></a>
</div>
</div>
<div class="row g-1">
<div class="col">
<a href="#domains"><span class="badge text-bg-primary visually-hidden w-100 domains">Domains</span></a>
</div>
<div class="col">
<a role="button" class="clip" data-clipboard-target="#domains-clip">
<span class="badge text-bg-primary visually-hidden w-100 domains">Copy</span></a>
</div>
</div>
</div>

<div class="container-fluid p-3">
<h2 id="message">Loading...</h2>
<div class="links" style="display: none">
<h2 id="loading-message" class="user-select-none">Loading...</h2>
<div class="visually-hidden links">
<input id="links-clip" class="visually-hidden" type="hidden" value="">
<div class="user-select-none">
<h2>Links</h2>
<a id="links-clip" class="clip btn btn-sm btn-outline-success mb-2" role="button" data-clipboard-text="">
Copy Links</a> Press <kbd>C</kbd> or <kbd>L</kbd> to Copy Links.
<h2 id="links">Links <span id="links-count" class="badge bg-success-subtle"></span></h2>
<a id="copy-links" class="btn btn-sm btn-success me-1 clip" role="button" data-clipboard-target="#links-clip">
Copy Links</a>
<!-- <a id="open-links" class="btn btn-sm btn-outline-warning open-in-tabs" role="button" data-target="#links-clip">-->
<!-- Open ALL</a>-->
<button type="button" id="open-links" class="btn btn-sm btn-outline-warning open-in-tabs position-relative me-3" data-target="#links-clip">
Open Links
<span id="open-links-count" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger visually-hidden"></span>
</button>
Press <kbd>C</kbd> or <kbd>L</kbd> to Copy Links.
</div>
<table id="links" class="table table-sm table-striped table-hover">
<caption class="visually-hidden">Links</caption>
<table id="links-table" class="table table-sm table-striped table-hover">
<caption class="visually-hidden user-select-none">Links</caption>
<thead><tr><th></th></tr></thead>
<tbody></tbody>
</table>
</div>

<div class="domains" style="display: none">
<div class="visually-hidden domains">
<input id="domains-clip" class="visually-hidden" type="hidden" value="">
<div class="user-select-none">
<h2>Domains</h2>
<a id="domains-clip" class="clip btn btn-sm btn-outline-primary mb-2" role="button" data-clipboard-text="">
Copy Domains</a> Press <kbd>D</kbd> or <kbd>M</kbd> to Copy Domains.
<h2 id="domains">Domains <span id="domains-count" class="badge bg-primary-subtle"></span></h2>
<a id="copy-domains" class="btn btn-sm btn-primary me-1 clip" role="button" data-clipboard-target="#domains-clip">
Copy Domains</a>
<!-- <a id="open-domains" class="btn btn-sm btn-outline-warning open-in-tabs" role="button" data-target="#domains-clip">-->
<!-- Open Domains</a>-->
<button type="button" id="open-domains" class="btn btn-sm btn-outline-warning open-in-tabs position-relative me-3" data-target="#domains-clip">
Open Domains
<span id="open-domains-count" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger visually-hidden"></span>
</button>
Press <kbd>D</kbd> or <kbd>M</kbd> to Copy Domains.
</div>
<table id="domains" class="table table-sm table-striped table-hover">
<caption class="visually-hidden">Domains</caption>
<table id="domains-table" class="table table-sm table-striped table-hover">
<caption class="visually-hidden user-select-none">Domains</caption>
<thead><tr><th></th></tr></thead>
<tbody></tbody>
</table>
Expand Down Expand Up @@ -68,7 +105,7 @@ <h5 class="modal-title">Keyboard Shortcuts</h5>
<script type="text/javascript" src="../dist/bootstrap/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="../dist/clipboard/clipboard.min.js"></script>
<script type="text/javascript" src="../js/main.js"></script>
<script type="text/javascript" src="../js/links.js"></script>
<script type="module" src="../js/links.js"></script>

</body>
</html>
14 changes: 12 additions & 2 deletions src/html/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,21 @@ <h1 class="align-middle">Link Extractor</h1>
<input class="form-check-input" type="checkbox" role="switch" id="contextMenu">
<label class="form-check-label" for="contextMenu" aria-describedby="contextMenuHelp">Enable Right Click Menu</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="defaultFilter">
<label class="form-check-label" for="defaultFilter" aria-describedby="defaultFilterHelp">
Use Default Link Filtering
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Filter Out Links Without ://">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</svg>
</label>
</div>
<hr>
<span id="add-input" class="badge text-bg-primary" role="button">Add Filter</span>
<div id="filters-inputs" class="my-2"></div>
<div class="d-md-none">
<button type="submit" class="btn btn-outline-success w-100">Save Options</button>
<button type="submit" class="btn btn-success w-100">Save Options</button>
</div>
</form>
</div>
Expand All @@ -77,7 +87,7 @@ <h1 class="align-middle">Link Extractor</h1>
<div id="bottom-container" class="position-fixed bottom-0 end-0">
<div id="toast-container" class=""></div>
<div class="d-flex justify-content-end mt-4 d-none d-md-flex">
<button type="submit" form="filters-form" class="btn btn-outline-success text-end">Save Options</button>
<button type="submit" form="filters-form" class="btn btn-success text-end">Save Options</button>
</div>
</div>

Expand Down
15 changes: 14 additions & 1 deletion src/html/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<body>

<div class="container-fluid p-3">
<div class="d-grid g-2 gap-3">
<div class="d-grid g-2 gap-2">
<div class="btn-group btn-group-sm" role="group" aria-label="Button group with nested dropdown">
<button id="btn-all" class="btn btn-success btn-sm popup-click" type="button">
All Links</button>
Expand Down Expand Up @@ -46,6 +46,19 @@
data-bs-placement="bottom" data-bs-title="Open Each Line" data-text="Open">Open</button>
</div>

<div class="form-check form-switch form-check-inline d-flex justify-content-center align-items-center">
<input class="form-check-input me-2" type="checkbox" role="switch" id="defaultFilter">
<!-- <label class="form-check-label" for="defaultFilter" aria-describedby="defaultFilterHelp">No Internal Filtering for-->
<!-- <span class="badge bg-secondary ">://</span></label>-->
<label class="form-check-label" for="defaultFilter" aria-describedby="defaultFilterHelp">
Use Default Link Filtering
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16" data-bs-toggle="tooltip"
data-bs-placement="bottom" data-bs-title="Filter Out Links Without ://">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</svg>
</label>
</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
13 changes: 13 additions & 0 deletions src/js/exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,16 @@ export async function injectTab(filter, domains, selection) {
console.log(`url: ${url.toString()}`)
await chrome.tabs.create({ active: true, url: url.toString() })
}

/**
* Open Links in Tabs
* @function openLinksInTabs
* @param {Array} links
* @param {Boolean} active
*/
export function openLinksInTabs(links, active = true) {
console.log('openLinksInTabs:', links)
links.forEach(function (url) {
chrome.tabs.create({ active, url }).then()
})
}
4 changes: 3 additions & 1 deletion src/js/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ function extractLinks() {
console.log('extractLinks')
const links = []
for (const element of document.links) {
links.push(decodeURI(element.href))
if (element.href) {
links.push(decodeURI(element.href))
}
}
console.log(links)
return links
Expand Down
79 changes: 57 additions & 22 deletions src/js/links.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// JS for links.html

import { openLinksInTabs } from './exports.js'

document.addEventListener('DOMContentLoaded', initLinks)

const urlParams = new URLSearchParams(window.location.search)
Expand All @@ -14,6 +16,9 @@ document.addEventListener('keyup', (event) => {
delete keysPressed[event.key]
})

const openLinksBtns = document.querySelectorAll('.open-in-tabs')
openLinksBtns.forEach((el) => el.addEventListener('click', openLinksClick))

/**
* Links Init
* TODO: Review this function
Expand Down Expand Up @@ -52,25 +57,27 @@ async function processLinks(links) {
const onlyDomains = urlParams.has('domains')
console.log(`urlFilter: ${urlFilter}`)
console.log(`onlyDomains: ${onlyDomains}`)
const openWarnCount = 30
const { options } = await chrome.storage.sync.get(['options'])
console.log('options:', options)

if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError)
window.close()
return
}

// Filter bad links like: javascript:void(0)
const filteredLinks = links.filter(
(link) => link.lastIndexOf('://', 10) > 0
)
// Filter links by :// if not disabled by user
if (options.defaultFilter) {
links = links.filter((link) => link.lastIndexOf('://', 10) > 0)
}

// Remove duplicate and sort links
let items = [...new Set(filteredLinks)].sort()
let items = [...new Set(links)].sort()

// Filter links based on pattern
if (urlFilter) {
const { options } = await chrome.storage.sync.get(['options'])
const flags = options !== undefined ? options.flags : 'ig'
const flags = options?.flags !== undefined ? options.flags : 'ig'
const re = new RegExp(urlFilter, flags)
console.log(`Filtering Links with re: ${re}`)
items = items.filter((item) => item.match(re))
Expand All @@ -85,27 +92,40 @@ async function processLinks(links) {

// Update links if onlyDomains is not set
if (!onlyDomains) {
document
.getElementById('links-clip')
.setAttribute('data-clipboard-text', items.join('\n'))
document.getElementById('links-count').textContent =
items.length.toString()
if (items.length >= openWarnCount) {
const openCount = document.getElementById('open-links-count')
openCount.classList.remove('visually-hidden')
openCount.textContent = items.length.toString()
}
document.getElementById('links-clip').value = items.join('\n')
const linksElements = document.querySelectorAll('.links')
linksElements.forEach((el) => (el.style.display = 'block'))
updateTable(items, 'links')
linksElements.forEach((el) => el.classList.remove('visually-hidden'))
updateTable(items, 'links-table')
}

// Extract domains from items and sort
const domains = [...new Set(items.map((link) => getBaseURL(link)))].sort()
document
.getElementById('domains-clip')
.setAttribute('data-clipboard-text', domains.join('\n'))
// Extract domains from items, sort, and remove null
let domains = [...new Set(items.map((link) => getBaseURL(link)))].sort()
domains = domains.filter(function (el) {
return el != null
})
document.getElementById('domains-count').textContent =
domains.length.toString()
if (domains.length >= openWarnCount) {
const openCount = document.getElementById('open-domains-count')
openCount.classList.remove('visually-hidden')
openCount.textContent = domains.length.toString()
}
document.getElementById('domains-clip').value = domains.join('\n')
if (domains.length) {
const domainsElements = document.querySelectorAll('.domains')
domainsElements.forEach((el) => (el.style.display = 'block'))
updateTable(domains, 'domains')
domainsElements.forEach((el) => el.classList.remove('visually-hidden'))
updateTable(domains, 'domains-table')
}

// Hide Loading message
document.getElementById('message').style.display = 'none'
document.getElementById('loading-message').classList.add('visually-hidden')
}

/**
Expand Down Expand Up @@ -156,9 +176,9 @@ function handleKeybinds(event) {
if (!formElements.includes(event.target.tagName)) {
keysPressed[event.key] = true
if (checkKey(event, ['KeyC', 'KeyL'])) {
document.getElementById('links-clip').click()
document.getElementById('copy-links').click()
} else if (checkKey(event, ['KeyD', 'KeyM'])) {
document.getElementById('domains-clip').click()
document.getElementById('copy-domains').click()
} else if (checkKey(event, ['KeyT', 'KeyO'])) {
const url = chrome.runtime.getURL('../html/options.html')
chrome.tabs.create({ active: true, url: url }).then()
Expand Down Expand Up @@ -188,3 +208,18 @@ function checkKey(event, keys) {
}
return !!keys.includes(event.code)
}

/**
* Open links Button Click Callback
* @function openLinksClick
* @param {KeyboardEvent} event
*/
function openLinksClick(event) {
console.log('openLinksBtn:', event)
console.log(`openLinksBtn: ${event.target.dataset.target}`)
const input = document.querySelector(event.target.dataset.target)
console.log('input:', input)
const links = input.value.toString().split('\n')
console.log('links:', links)
openLinksInTabs(links)
}
10 changes: 10 additions & 0 deletions src/js/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ document.getElementById('reset-default').addEventListener('click', resetForm)
})
})

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

/**
* Options Page Init
* @function initOptions
Expand All @@ -25,9 +32,11 @@ async function initOptions() {
'options',
'patterns',
])
console.log('options:', options)
document.getElementById('reFlags').value =
options !== undefined ? options.flags : 'ig'
document.getElementById('contextMenu').checked = options.contextMenu
document.getElementById('defaultFilter').checked = options.defaultFilter
if (patterns?.length) {
console.log(patterns)
patterns.forEach(function (value, i) {
Expand Down Expand Up @@ -139,6 +148,7 @@ async function saveOptions(event) {
chrome.contextMenus.removeAll()
}
console.log(options)
options.defaultFilter = document.getElementById('defaultFilter').checked

await chrome.storage.sync.set({ options, patterns })
showToast('Options Saved')
Expand Down
Loading