|
javascript: (async () => { |
|
const style = { |
|
display: 'block', |
|
margin: 0, |
|
padding: 0, |
|
boxSizing: 'content-box', |
|
lineHeight: '28px', |
|
fontSize: '16px', |
|
fontFamily: 'sans-serif', |
|
fontWeight: 'bold', |
|
textAlign: 'center', |
|
}; |
|
const showPopup = text => { |
|
const elem = document.createElement('div'); |
|
Object.assign(elem.style, { |
|
...style, |
|
background: '#333', |
|
border: '2px solid #fff', |
|
borderTop: 'none', |
|
borderRadius: '0 0 5px 5px', |
|
zIndex: 2 ** 31 - 1, |
|
position: 'fixed', |
|
top: 0, |
|
left: '50%', |
|
right: '50%', |
|
height: '28px', |
|
width: '100px', |
|
fontSize: '18px', |
|
color: '#fff', |
|
}); |
|
elem.textContent = text; |
|
document.body.appendChild(elem); |
|
setTimeout(() => elem.remove(), 500); |
|
}; |
|
const shouldUseClipboard = async () => { |
|
if (!navigator.permissions) { |
|
return false; |
|
} |
|
return await [() => navigator.permissions.query({ |
|
name: 'clipboard-write' |
|
}), () => navigator.permissions.query({ |
|
name: 'clipboard' |
|
})].reduce((promise, fallback) => promise.catch(fallback), Promise.reject()).then(permissionStatus => permissionStatus.state === 'granted').catch(e => { |
|
if (!(e instanceof TypeError)) throw e; |
|
return false; |
|
}); |
|
}; |
|
const obtainTitle = () => { |
|
const documentTitle = document.title; |
|
if (documentTitle.length > 0) return documentTitle; |
|
const h1 = document.querySelector('h1'); |
|
if (h1 && h1.textContent.length > 0) return h1.textContent; |
|
const h2 = document.querySelector('h2'); |
|
if (h2 && h2.textContent.length > 0) return h2.textContent; |
|
return ""; |
|
}; |
|
const sanitizeTitle = (title) => title.replace(/\[/g, '%EF%BC%BB').replace(/\]/g, '%EF%BC%BD'); |
|
const badgeRemover = (title) => document.title.replace(/^\([0-9]+\) /, ''); |
|
const googleSearch = ({ |
|
title, |
|
url: uriString |
|
}) => { |
|
const url = new URL(uriString); |
|
const [q, tbm] = url.searchParams.getAll('q', 'tbm'); |
|
[...url.searchParams.keys()].forEach(k => url.searchParams.delete(k)); |
|
url.searchParams.set('q', q); |
|
if (tbm) url.searchParams.set('tbm', tbm); |
|
return `[${title} ${url.toString()}]`; |
|
}; |
|
const twitter = ({ |
|
title, |
|
url: urlString, |
|
}) => { |
|
const url = new URL(urlString); |
|
const screenName = url.pathname.match(/^\/([^/]+)\//)?.[1] || 'Twitter'; |
|
const ogp = new Map([...document.head.querySelectorAll('meta[property^="og:"]')].map(e => [e.getAttribute('property'), e.getAttribute('content')])); |
|
if (ogp.has('og:description')) { |
|
const tweetContent = ogp.get('og:description').replace(/\n/g, ' ').replace(/^“/, '').replace(/”$/, ''); |
|
return `> [${screenName} ${url}] ${tweetContent}`; |
|
} else { |
|
const tweetContent = badgeRemover(title).replace(/\/ Twitter$/, ''); |
|
return `> [${screenName} ${url}] ${tweetContent}`; |
|
} |
|
}; |
|
const amazon = ({ |
|
title, |
|
url |
|
}) => { |
|
const urlShortener = (urlString) => { |
|
const url = new URL(urlString); |
|
const [, p, aid] = url.pathname.match(/\/(dp|gp\/product)\/([a-zA-Z0-9]+)/) || []; |
|
if (aid != undefined) { |
|
return `${url.origin}/${p}/${aid}`; |
|
} else { |
|
return urlString; |
|
} |
|
}; |
|
return `[${title} ${urlShortener(url)}]`; |
|
}; |
|
const clipboardCopy = async (text) => { |
|
if (await shouldUseClipboard()) { |
|
try { |
|
await navigator.clipboard.writeText(text); |
|
return true; |
|
} catch (e) { |
|
console.error("Failed to copy: ", e); |
|
} |
|
} |
|
}; |
|
const execCommandCopy = (text) => { |
|
if (!document.execCommand) { |
|
return false; |
|
} |
|
const copyButton = document.createElement('button'); |
|
copyButton.type = 'button'; |
|
Object.assign(copyButton.style, { |
|
...style, |
|
background: '#fff', |
|
border: '2px solid #222', |
|
borderRadius: '5px', |
|
position: 'fixed', |
|
zIndex: 2 ** 31 - 1, |
|
top: 0, |
|
left: '50vw', |
|
height: '28px', |
|
width: '150px', |
|
fontSize: '18px', |
|
color: '#222', |
|
outline: 'none', |
|
animation: 'none', |
|
transition: 'none', |
|
}); |
|
copyButton.textContent = 'Click to Copy'; |
|
const handleMouseMove = (ev) => { |
|
copyButton.style.top = `${ev.clientY - 14}px`; |
|
copyButton.style.left = `${ev.clientX - 75}px`; |
|
}; |
|
document.addEventListener('mousemove', handleMouseMove); |
|
const saveRanges = () => { |
|
if (!(window.getSelection && Selection)) return []; |
|
const s = window.getSelection(); |
|
const ranges = []; |
|
for (let i = 0; i < s.rangeCount; i++) { |
|
ranges.push(s.getRangeAt(i)); |
|
} |
|
return ranges; |
|
}; |
|
const restoreRange = (savedRanges) => { |
|
if (!(window.getSelection && Selection)) return; |
|
const s = window.getSelection(); |
|
s.removeAllRanges(); |
|
savedRanges.forEach((r) => s.addRange(r)); |
|
}; |
|
const cleanup = () => { |
|
document.removeEventListener('mousemove', handleMouseMove); |
|
copyButton.remove(); |
|
}; |
|
const execCopy = () => { |
|
const ranges = saveRanges(); |
|
const input = document.createElement('input'); |
|
input.value = text; |
|
document.body.appendChild(input); |
|
input.select(); |
|
try { |
|
document.execCommand('copy'); |
|
return true; |
|
} catch (e) { |
|
console.error(e); |
|
return false; |
|
} finally { |
|
cleanup(); |
|
input.remove(); |
|
restoreRange(ranges); |
|
} |
|
}; |
|
return new Promise((resolve) => { |
|
copyButton.addEventListener('keydown', (ev) => { |
|
ev.stopPropagation(); |
|
ev.preventDefault(); |
|
if (ev.key === 'Enter' || ev.key === 'Space') { |
|
resolve(execCopy()); |
|
} |
|
}); |
|
copyButton.addEventListener('click', (ev) => { |
|
ev.stopPropagation(); |
|
ev.preventDefault(); |
|
resolve(execCopy()); |
|
}, false); |
|
copyButton.addEventListener('contextmenu', (ev) => { |
|
ev.preventDefault(); |
|
ev.stopPropagation(); |
|
cleanup(); |
|
resolve(true); |
|
}, false); |
|
document.body.appendChild(copyButton); |
|
copyButton.focus(); |
|
}); |
|
}; |
|
const windowPromptCopy = (text) => { |
|
if (!window.prompt) { |
|
return false; |
|
} |
|
window.prompt('copy it!', text); |
|
return true; |
|
}; |
|
const title = sanitizeTitle(obtainTitle()); |
|
const url = new URL(location.href); |
|
let text; |
|
if (url.origin === 'https://twitter.com' && url.pathname.split('/')[2] === 'status') { |
|
text = twitter({ |
|
title, |
|
url |
|
}); |
|
} else if (/^https?:\/\/www\.amazon\.(co\.jp|com)$/.test(url.origin)) { |
|
text = amazon({ |
|
title, |
|
url |
|
}); |
|
} else if (url.origin === 'https://www.youtube.com') { |
|
text = `[${badgeRemover(title)} ${url}]`; |
|
} else if (/^https?:\/\/www\.google\.(co.jp|com)$/.test(url.origin) && url.pathname === '/search') { |
|
text = googleSearch({ |
|
title, |
|
url |
|
}); |
|
} else { |
|
text = `[${title} ${url}]`; |
|
} |
|
const copySucceed = await [clipboardCopy, execCommandCopy, windowPromptCopy].reduce((prev, curr) => prev.then(p => p || curr(text)), Promise.resolve()); |
|
let result = copySucceed ? 'Copied!' : 'Failed to copy'; |
|
showPopup(result); |
|
undefined; |
|
})(); |