|
/* Link-selection bookmarklet – unminified |
|
Click the bookmark, drag a rectangle, links touched are |
|
1) outlined for visual feedback |
|
2) copied (newline-separated) to the clipboard */ |
|
|
|
(function () { |
|
// Prevent multiple instances |
|
if (window.__linkSelectActive) return; |
|
window.__linkSelectActive = true; |
|
|
|
/* -------------------------------------------------- |
|
Overlay that captures mouse events and shows cursor |
|
-------------------------------------------------- */ |
|
const overlay = document.createElement('div'); |
|
Object.assign(overlay.style, { |
|
position: 'fixed', |
|
inset: 0, |
|
zIndex: 2147483647, // top of everything |
|
cursor: 'crosshair' |
|
}); |
|
document.body.appendChild(overlay); |
|
|
|
/* -------------------------------------------------- |
|
Rubber-band rectangle the user drags out |
|
-------------------------------------------------- */ |
|
const box = document.createElement('div'); |
|
Object.assign(box.style, { |
|
position: 'absolute', |
|
border: '2px dashed #09f', |
|
background: 'rgba(0,153,255,0.12)', |
|
pointerEvents: 'none' |
|
}); |
|
overlay.appendChild(box); |
|
|
|
/* -------------------------------------------------- |
|
State for drag operation |
|
-------------------------------------------------- */ |
|
let startX, startY; // drag origin |
|
let links = []; // anchors inside selection |
|
|
|
overlay.addEventListener('mousedown', onDown); |
|
|
|
/* -------------------------------------------------- |
|
Mouse down – start selection |
|
-------------------------------------------------- */ |
|
function onDown(e) { |
|
startX = e.clientX; |
|
startY = e.clientY; |
|
|
|
// reset & position box |
|
Object.assign(box.style, { |
|
left: startX + 'px', |
|
top: startY + 'px', |
|
width: 0, |
|
height: 0 |
|
}); |
|
|
|
overlay.addEventListener('mousemove', onMove); |
|
overlay.addEventListener('mouseup', onUp); |
|
e.preventDefault(); // stop page selects etc. |
|
} |
|
|
|
/* -------------------------------------------------- |
|
Mouse move – resize selection rectangle |
|
-------------------------------------------------- */ |
|
function onMove(e) { |
|
const x = Math.min(e.clientX, startX); |
|
const y = Math.min(e.clientY, startY); |
|
const w = Math.abs(e.clientX - startX); |
|
const h = Math.abs(e.clientY - startY); |
|
|
|
Object.assign(box.style, { |
|
left: x + 'px', |
|
top: y + 'px', |
|
width: w + 'px', |
|
height: h + 'px' |
|
}); |
|
} |
|
|
|
/* -------------------------------------------------- |
|
Mouse up – finish, collect links, copy to clipboard |
|
-------------------------------------------------- */ |
|
function onUp() { |
|
overlay.removeEventListener('mousemove', onMove); |
|
overlay.removeEventListener('mouseup', onUp); |
|
|
|
const sel = box.getBoundingClientRect(); |
|
|
|
// Anchors with an href that intersect the rectangle |
|
links = [...document.querySelectorAll('a[href]')].filter(a => { |
|
const r = a.getBoundingClientRect(); |
|
return !( |
|
r.right < sel.left || |
|
r.left > sel.right || |
|
r.bottom < sel.top || |
|
r.top > sel.bottom |
|
); |
|
}); |
|
|
|
// Visual highlight |
|
links.forEach(a => (a.style.outline = '2px solid #ff5252')); |
|
|
|
const textToCopy = links.map(a => a.href).join('\n'); |
|
|
|
/* ---------- Copy helpers ---------- */ |
|
|
|
// Remove overlay & outlines, clear flag |
|
function cleanup() { |
|
links.forEach(a => (a.style.outline = '')); |
|
try { document.body.removeChild(overlay); } catch (_) { /* ignore */ } |
|
delete window.__linkSelectActive; |
|
} |
|
|
|
// Successful copy → notify + leave highlights briefly |
|
function copySuccess() { |
|
alert(`Copied ${links.length} link${links.length !== 1 ? 's' : ''} to clipboard.`); |
|
setTimeout(cleanup, 800); // keep outlines for 0.8 s |
|
} |
|
|
|
// Fallback using textarea + execCommand, or prompt |
|
function legacyCopy() { |
|
const ta = document.createElement('textarea'); |
|
ta.value = textToCopy; |
|
document.body.appendChild(ta); |
|
ta.select(); |
|
try { |
|
document.execCommand('copy'); |
|
copySuccess(); |
|
} catch (err) { |
|
// Last resort – let the user copy manually |
|
prompt('Copy links:', textToCopy); |
|
cleanup(); |
|
} |
|
document.body.removeChild(ta); |
|
} |
|
|
|
/* ---------- Copy execution ---------- */ |
|
if (links.length) { |
|
// Modern async API first |
|
if (navigator.clipboard && navigator.clipboard.writeText) { |
|
navigator.clipboard.writeText(textToCopy) |
|
.then(copySuccess) |
|
.catch(legacyCopy); |
|
} else { |
|
legacyCopy(); |
|
} |
|
} else { |
|
alert('No links selected.'); |
|
cleanup(); |
|
} |
|
} |
|
})(); |