-
-
Save II-H/7f321d5fe5d1ad19a510982ca01d642e to your computer and use it in GitHub Desktop.
Notenik Quote-From Bookmarklet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| javascript:(function(){ | |
| function escapeMarkdown(text) { | |
| return text.replace(/^/gm, '> ').replace(/`/g, '\\`'); | |
| } | |
| function extractAuthor() { | |
| // Try various meta tags for author | |
| const authorMeta = document.querySelector('meta[name="author"], meta[property="article:author"], meta[name="twitter:creator"], meta[property="og:article:author"]'); | |
| if (authorMeta && authorMeta.content) { | |
| return authorMeta.content.trim(); | |
| } | |
| // Try to find author in common class/id patterns | |
| const authorSelectors = [ | |
| '.author-name', '.by-author', '.author', '.byline', | |
| '[rel="author"]', '[itemprop="author"]', '.post-author', | |
| '.entry-author', '.article-author', '.content-author' | |
| ]; | |
| for (const selector of authorSelectors) { | |
| const element = document.querySelector(selector); | |
| if (element) { | |
| const text = element.textContent.trim(); | |
| // Clean up common patterns like "By John Doe" or "Written by John Doe" | |
| return text.replace(/^(by|written by|author:)\s*/i, '').trim(); | |
| } | |
| } | |
| return ''; | |
| } | |
| function extractYear() { | |
| // Try meta tags for publication date | |
| const dateMeta = document.querySelector( | |
| 'meta[property="article:published_time"], ' + | |
| 'meta[name="publish_date"], ' + | |
| 'meta[name="publication_date"], ' + | |
| 'meta[property="og:article:published_time"], ' + | |
| 'time[datetime], ' + | |
| '[itemprop="datePublished"]' | |
| ); | |
| if (dateMeta) { | |
| const dateContent = dateMeta.content || dateMeta.getAttribute('datetime') || dateMeta.textContent; | |
| if (dateContent) { | |
| const yearMatch = dateContent.match(/(\d{4})/); | |
| if (yearMatch) { | |
| return yearMatch[1]; | |
| } | |
| } | |
| } | |
| // Try to find year in URL | |
| const urlYearMatch = location.href.match(/\/(\d{4})\//); | |
| if (urlYearMatch) { | |
| const year = parseInt(urlYearMatch[1]); | |
| // Sanity check for reasonable year range | |
| if (year >= 1990 && year <= new Date().getFullYear()) { | |
| return urlYearMatch[1]; | |
| } | |
| } | |
| // Look for year in common date containers | |
| const dateSelectors = ['.post-date', '.entry-date', '.publish-date', '.article-date', '.date']; | |
| for (const selector of dateSelectors) { | |
| const element = document.querySelector(selector); | |
| if (element) { | |
| const yearMatch = element.textContent.match(/(\d{4})/); | |
| if (yearMatch) { | |
| const year = parseInt(yearMatch[1]); | |
| if (year >= 1990 && year <= new Date().getFullYear()) { | |
| return yearMatch[1]; | |
| } | |
| } | |
| } | |
| } | |
| return ''; | |
| } | |
| function getWebsiteName() { | |
| // Try Open Graph site name first | |
| const ogSiteName = document.querySelector('meta[property="og:site_name"]'); | |
| if (ogSiteName && ogSiteName.content) { | |
| return ogSiteName.content.trim(); | |
| } | |
| // Try application name | |
| const appName = document.querySelector('meta[name="application-name"]'); | |
| if (appName && appName.content) { | |
| return appName.content.trim(); | |
| } | |
| // Fall back to hostname without www | |
| return location.hostname.replace(/^www\./, ''); | |
| } | |
| function getRootUrl() { | |
| return location.protocol + '//' + location.hostname; | |
| } | |
| function cleanTitle(title) { | |
| // Remove common suffixes and separators | |
| return title | |
| .replace(/\s*[\|–—-]\s*.*$/, '') // Remove everything after separators | |
| .trim(); | |
| } | |
| const selection = window.getSelection().toString().trim(); | |
| if (!selection) { | |
| alert('Please select some text first.'); | |
| return; | |
| } | |
| // Extract metadata | |
| const author = extractAuthor(); | |
| const year = extractYear(); | |
| const websiteName = author || getWebsiteName(); // Use website name if no author found | |
| const pageTitle = cleanTitle(document.title); | |
| const rootUrl = getRootUrl(); | |
| const pageUrl = location.href; | |
| // Format the citation | |
| const citation = `{:quote-from:${websiteName}|${year}|article|${pageTitle}|${rootUrl}|${pageUrl}}`; | |
| // Create the formatted markdown | |
| const md = `${escapeMarkdown(selection)}\n\n${citation}`; | |
| // Use modern clipboard API if available, otherwise fall back | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(md).then( | |
| () => { | |
| // Visual feedback | |
| const toast = document.createElement('div'); | |
| toast.textContent = 'Markdown quote copied!'; | |
| toast.style.cssText = ` | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| background: #4CAF50; | |
| color: white; | |
| padding: 12px 24px; | |
| border-radius: 4px; | |
| z-index: 999999; | |
| font-family: system-ui, -apple-system, sans-serif; | |
| font-size: 14px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.2); | |
| `; | |
| document.body.appendChild(toast); | |
| setTimeout(() => document.body.removeChild(toast), 2000); | |
| }, | |
| (err) => { | |
| console.error('Copy failed', err); | |
| prompt('Copy the Markdown quote manually:', md); | |
| } | |
| ); | |
| } else { | |
| // Fallback for older browsers | |
| const textarea = document.createElement('textarea'); | |
| textarea.value = md; | |
| textarea.style.cssText = 'position:absolute;left:-9999px'; | |
| document.body.appendChild(textarea); | |
| textarea.select(); | |
| try { | |
| const successful = document.execCommand('copy'); | |
| if (successful) { | |
| alert('Markdown quote copied to clipboard!'); | |
| } else { | |
| throw new Error('Copy command failed'); | |
| } | |
| } catch (err) { | |
| console.error('Copy failed', err); | |
| prompt('Copy the Markdown quote manually:', md); | |
| } | |
| document.body.removeChild(textarea); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment