Skip to content
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

Initial Find/Replace Implementation #103

Merged
merged 12 commits into from
Sep 16, 2024
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
66 changes: 66 additions & 0 deletions src/html/links.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,72 @@ <h2 id="links">Links <span class="badge bg-success-subtle"><span id="links-count
</div>
</div>
</div> <!-- links-buttons -->

<div class="my-2">
<a class="link-body-emphasis" data-bs-toggle="collapse" href="#findCollapse" aria-expanded="false" aria-controls="findCollapse">
<i class="fa-solid fa-magnifying-glass pe-2"></i>Find and Replace</a>
<i class="fa-solid fa-flask text-warning-emphasis ms-2" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="Experimental Feature"></i>
<a href="https://link-extractor.cssnr.com/docs/#find-replace" class="link-body-emphasis text-decoration-none" target="_blank" rel="noopener">
<i class="fa-regular fa-circle-question ms-2" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="View Documentation for Feature"></i></a>
<div id="findCollapse" class="collapse">
<form id="findReplace" name="findReplace" class="my-2">
<div class="mb-1">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="reType" id="reNormal" value="normal" checked>
<label class="form-check-label me-1" for="reNormal">Normal</label>
<i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-placement="bottom"
data-bs-title="Normal Text Find and Replace"></i>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="reType" id="reRegex" value="regex">
<label class="form-check-label me-1" for="reRegex">Normal w/ Regex</label>
<i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-placement="bottom"
data-bs-title="Normal Regex Find and Replace"></i>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="reType" id="reGroups" value="groups">
<label class="form-check-label me-1" for="reGroups">Regex w/ Match Groups</label>
<i class="fa-solid fa-circle-info" data-bs-toggle="tooltip" data-bs-placement="bottom"
data-bs-title="Regex Find and Replace w/ Match Groups: $1, $2, etc."></i>
</div>
</div>

<div class="d-flex flex-column flex-sm-row gap-1">
<input id="reFind" name="reFind" type="text" class="form-control" placeholder="Find" aria-label="Find">
<!-- <div class="input-group">-->
<!-- <input id="reFind" name="reFind" type="text" class="form-control" placeholder="Find" aria-label="Find">-->
<!-- <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"></button>-->
<!-- <ul class="dropdown-menu dropdown-menu-end">-->
<!-- <li><a class="dropdown-item small" role="button">Item 1</a></li>-->
<!-- <li><a class="dropdown-item small" role="button">Item 2</a></li>-->
<!-- <li><hr class="dropdown-divider my-1"></li>-->
<!-- <li><a class="dropdown-item small" href="#">Manage Saved Values</a></li>-->
<!-- </ul>-->
<!-- </div>-->

<input id="reReplace" name="reReplace" type="text" class="form-control" placeholder="Replace" aria-label="Replace">
<!-- <div class="input-group">-->
<!-- <input id="reReplace" name="reReplace" type="text" class="form-control" placeholder="Replace" aria-label="Replace">-->
<!-- <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false"></button>-->
<!-- <ul class="dropdown-menu dropdown-menu-end">-->
<!-- <li><a class="dropdown-item small" role="button">Item 1</a></li>-->
<!-- <li><a class="dropdown-item small" role="button">Item 2</a></li>-->
<!-- <li><hr class="dropdown-divider my-1"></li>-->
<!-- <li><a class="dropdown-item small" href="#">Manage Saved Values</a></li>-->
<!-- </ul>-->
<!-- </div>-->

<button class="btn btn-outline-success" type="submit">Execute</button>
<button id="reReset" class="btn btn-outline-danger disabled" type="button">Reset</button>
</div>
</form>
<div class="small">
Note: Updates links do not yet work with Datatables (Copy Table, CSV Export, Filter);
however, do work with Copy Links, Download and Open buttons at the top.
</div>
</div>
</div>

<div class="table-wrapper">
<table id="links-table" class="table table-sm table-striped table-hover small w-100" data-counter="links-count">
<thead class="">
Expand Down
127 changes: 126 additions & 1 deletion src/js/links.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { openURL, textFileDownload } from './exports.js'

window.addEventListener('keydown', handleKeyboard)
document.addEventListener('DOMContentLoaded', initLinks)

document.getElementById('findReplace').addEventListener('submit', findReplace)
document.getElementById('reReset').addEventListener('click', reResetClick)
document
.getElementsByName('reType')
.forEach((el) => el.addEventListener('change', reTypeChange))
document
.querySelectorAll('.copy-links')
.forEach((el) => el.addEventListener('click', copyLinksClick))
Expand All @@ -14,6 +18,19 @@ document
document
.querySelectorAll('.open-in-tabs')
.forEach((el) => el.addEventListener('click', openLinksClick))
document
.querySelectorAll('[data-bs-toggle="tooltip"]')
.forEach((el) => new bootstrap.Tooltip(el))

const findCollapse = document.getElementById('findCollapse')
findCollapse.addEventListener('show.bs.collapse', () => {
console.debug('Show Collapse')
localStorage.setItem('findCollapse', 'shown')
})
findCollapse.addEventListener('hide.bs.collapse', () => {
console.debug('Hide Collapse')
localStorage.setItem('findCollapse', 'hidden')
})

const urlParams = new URLSearchParams(window.location.search)

Expand Down Expand Up @@ -109,6 +126,7 @@ function genUrl(url) {
link.text = url
link.href = url
link.title = url
link.dataset.original = url
link.target = '_blank'
link.rel = 'noopener'
return link
Expand Down Expand Up @@ -155,6 +173,21 @@ async function initLinks() {
window.close()
}

const collapse = localStorage.getItem('findCollapse')
console.debug('collapse:', collapse)
if (collapse === 'shown') {
// const bsCollapse = new bootstrap.Collapse(findCollapse, {
// toggle: false,
// })
// bsCollapse.show()
findCollapse.classList.add('show')
}
const type = localStorage.getItem('reType')
console.debug('type:', type)
if (type) {
document.getElementById(type).checked = true
}

const { patterns } = await chrome.storage.sync.get(['patterns'])
if (patterns.length) {
const datalist = document.createElement('datalist')
Expand Down Expand Up @@ -307,6 +340,98 @@ function dtVisibility(e, settings, column, state) {
linksTable.rows().invalidate().draw()
}

/**
* Find and Replace Submit Callback
* @function findReplace
* @param {SubmitEvent} event
*/
async function findReplace(event) {
console.debug('findReplace:', event)
event.preventDefault()
const find = event.target.elements.reFind.value
const replace = event.target.elements.reReplace.value
console.debug('find:', find)
console.debug('replace:', replace)
if (!find) {
showToast('You must enter a find value.', 'danger')
return
}
const re = new RegExp(find, 'gm')
console.debug('re:', re)
// const type = document.querySelector('input[name="reType"]:checked').value
const type = event.target.elements.reType.value
console.debug('type:', type)
const links = document.getElementById('links-body').querySelectorAll('a')
let count = 0
for (const link of links) {
const before = link.href
console.debug('before:', before)
if (type === 'normal') {
const result = link.href.replace(find, replace)
console.debug('result:', result)
link.href = result
link.textContent = result
} else if (type === 'regex') {
const result = link.href.replace(re, replace)
console.debug('result:', result)
link.href = result
link.textContent = result
} else if (type === 'groups') {
const matches = link.href.match(re)
console.debug('matches:', matches)
if (matches) {
matches.forEach((match, i) => {
console.debug(`match ${i}:`, match)
const result = replace.replace(`$${i + 1}`, match)
console.debug('result:', result)
link.href = result
link.textContent = result
})
}
}
const after = link.getAttribute('href')
console.debug('after:', after)
if (after !== before) {
count++
}
}
const status = count ? 'success' : 'warning'
showToast(`Updated ${count} Links.`, status)
if (count) {
document.getElementById('reReset').classList.remove('disabled')
}
}

/**
* Reset Regex Click Callback
* @function reResetClick
* @param {MouseEvent} event
*/
async function reResetClick(event) {
console.debug('reResetClick:', event)
event.currentTarget.classList.add('disabled')
document
.getElementById('links-body')
.querySelectorAll('a')
.forEach((el) => {
console.debug('el.dataset.original:', el.dataset.original)
el.href = el.dataset.original
el.textContent = el.dataset.original
})
showToast('Links reset to original values.')
}

/**
* Regex Type Change Callback
* @function reTypeChange
* @param {InputEvent} event
*/
async function reTypeChange(event) {
// console.debug('reTypeChange:', event)
console.debug('reTypeChange id:', event.target.id)
localStorage.setItem('reType', event.target.id)
}

/**
* Copy links Button Click Callback
* @function copyLinksClick
Expand Down