Greasy Fork is available in English.
在任意页面将选中的链接复制为 Markdown 格式:[文本](链接)。
// ==UserScript==
// @name Copy Selected Links as Markdown
// @name:zh-CN 复制选中链接为 Markdown
// @namespace https://github.com/utags
// @homepageURL https://github.com/utags/userscripts#readme
// @supportURL https://github.com/utags/userscripts/issues
// @version 0.1.2
// @description Copy selected link(s) on any page as Markdown: [text](url).
// @description:zh-CN 在任意页面将选中的链接复制为 Markdown 格式:[文本](链接)。
// @icon data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%2064%2064%22%20fill%3D%22none%22%3E%3Crect%20x%3D%228%22%20y%3D%228%22%20width%3D%2248%22%20height%3D%2248%22%20rx%3D%2210%22%20stroke%3D%22%231f2937%22%20stroke-width%3D%224%22/%3E%3Cpath%20d%3D%22M18%2046V18l14%2022L46%2018v28%22%20stroke%3D%22%231f2937%22%20stroke-width%3D%226%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22/%3E%3C/svg%3E
// @author Pipecraft
// @license MIT
// @match *://*/*
// @run-at document-idle
// @grant GM_registerMenuCommand
// ==/UserScript==
//
;(() => {
'use strict'
function escapeMD(s) {
s = String(s || '')
return s
.replaceAll('|', '\\|')
.replaceAll('[', '\\[')
.replaceAll(']', '\\]')
}
function getSelectionAnchors() {
const sel = globalThis.getSelection()
if (!sel || sel.rangeCount === 0) return []
const set = /* @__PURE__ */ new Set()
for (let i = 0; i < sel.rangeCount; i++) {
const range = sel.getRangeAt(i)
const common = range.commonAncestorContainer
const rootEl =
common.nodeType === Node.TEXT_NODE ? common.parentElement : common
if (rootEl) {
const as = rootEl.querySelectorAll('a[href]')
for (const a of Array.from(as)) {
if (a instanceof HTMLAnchorElement) {
try {
if (range.intersectsNode(a)) set.add(a)
} catch (e) {}
}
}
}
let startNode = range.startContainer
if (startNode && startNode.nodeType === Node.TEXT_NODE)
startNode = startNode.parentElement
let cur = startNode instanceof HTMLElement ? startNode : void 0
while (cur) {
if (cur instanceof HTMLAnchorElement && cur.getAttribute('href')) {
set.add(cur)
break
}
cur = cur.parentElement
}
}
return Array.from(set)
}
function buildMarkdown() {
const sel = globalThis.getSelection()
const textSel = sel ? sel.toString().trim() : ''
const anchors = getSelectionAnchors()
const origin = location.origin
if (anchors.length === 1) {
const a = anchors[0]
const name = textSel || (a.textContent || '').trim() || a.href
const url = new URL(a.getAttribute('href') || a.href, origin).href
return '['.concat(escapeMD(name), '](').concat(escapeMD(url), ')')
}
if (anchors.length > 1) {
return anchors
.map((a) => {
const name = (a.textContent || '').trim() || a.href
const url = new URL(a.getAttribute('href') || a.href, origin).href
return '- ['.concat(escapeMD(name), '](').concat(escapeMD(url), ')')
})
.join('\n')
}
if (textSel) {
const m = /https?:\/\/[^\s)]+/.exec(textSel)
if (m) {
const url = m[0]
const name = textSel.length > url.length ? textSel : url
return '['.concat(escapeMD(name), '](').concat(escapeMD(url), ')')
}
}
return '['
.concat(escapeMD(document.title), '](')
.concat(escapeMD(location.href), ')')
}
async function copyText(s) {
try {
await navigator.clipboard.writeText(s)
return
} catch (e) {}
try {
const ta = document.createElement('textarea')
ta.style.position = 'fixed'
ta.style.opacity = '0'
ta.value = s
document.body.append(ta)
ta.focus()
ta.select()
document.execCommand('copy')
ta.remove()
} catch (e) {}
}
function run() {
const md = buildMarkdown()
void copyText(md)
}
try {
const gmRegisterMenuCommand = globalThis.GM_registerMenuCommand
if (typeof gmRegisterMenuCommand === 'function') {
gmRegisterMenuCommand(
'\u590D\u5236\u9009\u4E2D\u94FE\u63A5\u4E3A Markdown',
run
)
}
} catch (e) {}
globalThis.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.code === 'KeyM') {
e.preventDefault()
run()
}
})
})()