Skip to content

Instantly share code, notes, and snippets.

@halostatue
Forked from kepano/obsidian-web-clipper.js
Last active January 11, 2022 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save halostatue/d616ffe5620a68b95ddea0a4fc3d2589 to your computer and use it in GitHub Desktop.
Save halostatue/d616ffe5620a68b95ddea0a4fc3d2589 to your computer and use it in GitHub Desktop.
Obsidian Web Clipper Bookmarklet to save articles and pages from the web (for Safari, Chrome, Firefox, and mobile browsers)

By @kepano

🎉 Support my work at buymeacoffee.com/kepano

Demo

You can find a demo of this bookmarklet on YouTube

Installation

Create a new bookmark in your browser, then copy/paste the minified code below into the URL field.

You can customize the output using the optional variables at the top, and the template at the bottom. The default template is designed for use with the Dataview plugin. If you make changes I recommend using Bookmarklet Maker to minify and URI encode the bookmarklet.

Usage

By default, clicking the bookmarklet creates a new Obsidian file from the main body of the article (similar to Readability view). Alternatively you can choose to create a file from a selection, by either selecting all (CMD+A), or just a portion of the page.

Troubleshooting

This bookmarklet may not work on all websites. If you run into issues, you can also try the MarkDownload browser extension which provides similar functionality. You can troubleshoot issues by opening the Developer Console in your browser and checking if any errors appear when you click the bookmarklet. The most common error is that a website or the browser itself is blocking third party code execution. Unfortunately there is no good solve for that yet.

javascript: Promise.all([
import("https://unpkg.com/turndown@7.0?module"),
import("https://unpkg.com/@tehshrike/readability@0.2"),
]).then(([{ default: Turndown }, { default: Readability }]) => {
const vault = ""; /* Optional vault name */
const folder = prompt(
"Folder: ",
"Clippings/",
); /* Optional folder name such as "Clippings/" */
const tags = `#clippings ${prompt("Tags", "#to_read")}`; /* Optional tags */
const getSelectionHtml = () => {
let html = "";
if (typeof window.getSelection != "undefined") {
const sel = window.getSelection();
if (sel.rangeCount) {
const container = document.createElement("div");
for (let i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
};
const getFileName = (fileName) => {
const platform = window.navigator.platform;
const windowsPlatforms = ["Win32", "Win64", "Windows", "WinCE"];
if (windowsPlatforms.indexOf(platform) !== -1) {
fileName = fileName.replace(":", "").replace(/[/\\?%*|"<>]/g, "-");
} else {
fileName = fileName.replace(":", "").replace(/\//g, "-").replace(
/\\/g,
"-",
);
}
return fileName;
};
const selection = getSelectionHtml();
const {
title,
byline,
content,
} = new Readability(document.cloneNode(true)).parse();
const fileName = getFileName(title);
const markdownify = selection ?? content;
const vaultName = vault ? `&vault=${encodeURIComponent(`${vault}`)}` : "";
const markdownBody = new Turndown({
headingStyle: "atx",
hr: "---",
bulletListMarker: "-",
codeBlockStyle: "fenced",
emDelimiter: "*",
}).turndown(markdownify);
const today = new Date().toISOString().split("T")[0];
const fileContent = `---\n
author: ${byline}\n
date: ${today}\n
tags: [${tags}]\n
source: ${document.URL}\n
published:\n
--\n\n
${markdownBody}\n\n
author:: ${byline}\n
source:: [${title}](${document.URL})\n
clipped:: [[${today}]]\n
published:: \n\n
${tags}\n`;
document.location.href = [
"obsidian://new?",
`file=${encodeURIComponent(folder + fileName)}`,
`&content=${encodeURIComponent(fileContent)}`,
vaultName,
].join("");
});
javascript:(function()%7Bjavascript%3A%20Promise.all(%5B%0A%20%20import(%22https%3A%2F%2Funpkg.com%2Fturndown%407.0%3Fmodule%22)%2C%0A%20%20import(%22https%3A%2F%2Funpkg.com%2F%40tehshrike%2Freadability%400.2%22)%2C%0A%5D).then((%5B%7B%20default%3A%20Turndown%20%7D%2C%20%7B%20default%3A%20Readability%20%7D%5D)%20%3D%3E%20%7B%0A%20%20const%20vault%20%3D%20%22%22%3B%20%2F*%20Optional%20vault%20name%20*%2F%0A%20%20const%20folder%20%3D%20prompt(%0A%20%20%20%20%22Folder%3A%20%22%2C%0A%20%20%20%20%22Clippings%2F%22%2C%0A%20%20)%3B%20%2F*%20Optional%20folder%20name%20such%20as%20%22Clippings%2F%22%20*%2F%0A%20%20const%20tags%20%3D%20%60%23clippings%20%24%7Bprompt(%22Tags%22%2C%20%22%23to_read%22)%7D%60%3B%20%2F*%20Optional%20tags%20*%2F%0A%0A%20%20const%20getSelectionHtml%20%3D%20()%20%3D%3E%20%7B%0A%20%20%20%20let%20html%20%3D%20%22%22%3B%0A%20%20%20%20if%20(typeof%20window.getSelection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20const%20sel%20%3D%20window.getSelection()%3B%0A%0A%20%20%20%20%20%20if%20(sel.rangeCount)%20%7B%0A%20%20%20%20%20%20%20%20const%20container%20%3D%20document.createElement(%22div%22)%3B%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%200%2C%20len%20%3D%20sel.rangeCount%3B%20i%20%3C%20len%3B%20%2B%2Bi)%20%7B%0A%20%20%20%20%20%20%20%20%20%20container.appendChild(sel.getRangeAt(i).cloneContents())%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20html%20%3D%20container.innerHTML%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20if%20(typeof%20document.selection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20if%20(document.selection.type%20%3D%3D%20%22Text%22)%20%7B%0A%20%20%20%20%20%20%20%20html%20%3D%20document.selection.createRange().htmlText%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20html%3B%0A%20%20%7D%3B%0A%0A%20%20const%20getFileName%20%3D%20(fileName)%20%3D%3E%20%7B%0A%20%20%20%20const%20platform%20%3D%20window.navigator.platform%3B%0A%20%20%20%20const%20windowsPlatforms%20%3D%20%5B%22Win32%22%2C%20%22Win64%22%2C%20%22Windows%22%2C%20%22WinCE%22%5D%3B%0A%0A%20%20%20%20if%20(windowsPlatforms.indexOf(platform)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20fileName%20%3D%20fileName.replace(%22%3A%22%2C%20%22%22).replace(%2F%5B%2F%5C%5C%3F%25*%7C%22%3C%3E%5D%2Fg%2C%20%22-%22)%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20fileName%20%3D%20fileName.replace(%22%3A%22%2C%20%22%22).replace(%2F%5C%2F%2Fg%2C%20%22-%22).replace(%0A%20%20%20%20%20%20%20%20%2F%5C%5C%2Fg%2C%0A%20%20%20%20%20%20%20%20%22-%22%2C%0A%20%20%20%20%20%20)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20fileName%3B%0A%20%20%7D%3B%0A%0A%20%20const%20selection%20%3D%20getSelectionHtml()%3B%0A%0A%20%20const%20%7B%0A%20%20%20%20title%2C%0A%20%20%20%20byline%2C%0A%20%20%20%20content%2C%0A%20%20%7D%20%3D%20new%20Readability(document.cloneNode(true)).parse()%3B%0A%0A%20%20const%20fileName%20%3D%20getFileName(title)%3B%0A%20%20const%20markdownify%20%3D%20selection%20%3F%3F%20content%3B%0A%20%20const%20vaultName%20%3D%20vault%20%3F%20%60%26vault%3D%24%7BencodeURIComponent(%60%24%7Bvault%7D%60)%7D%60%20%3A%20%22%22%3B%0A%0A%20%20const%20markdownBody%20%3D%20new%20Turndown(%7B%0A%20%20%20%20headingStyle%3A%20%22atx%22%2C%0A%20%20%20%20hr%3A%20%22---%22%2C%0A%20%20%20%20bulletListMarker%3A%20%22-%22%2C%0A%20%20%20%20codeBlockStyle%3A%20%22fenced%22%2C%0A%20%20%20%20emDelimiter%3A%20%22*%22%2C%0A%20%20%7D).turndown(markdownify)%3B%0A%0A%20%20const%20today%20%3D%20new%20Date().toISOString().split(%22T%22)%5B0%5D%3B%0A%0A%20%20const%20fileContent%20%3D%20%60---%5Cn%0A%20%20author%3A%20%24%7Bbyline%7D%5Cn%0A%20%20date%3A%20%24%7Btoday%7D%5Cn%0A%20%20tags%3A%20%5B%24%7Btags%7D%5D%5Cn%0A%20%20source%3A%20%24%7Bdocument.URL%7D%5Cn%0A%20%20published%3A%5Cn%0A%20%20--%5Cn%5Cn%0A%20%20%24%7BmarkdownBody%7D%5Cn%5Cn%0A%20%20author%3A%3A%20%24%7Bbyline%7D%5Cn%0A%20%20source%3A%3A%20%5B%24%7Btitle%7D%5D(%24%7Bdocumnet.URL%7D)%5Cn%0A%20%20clipped%3A%3A%20%5B%5B%24%7Btoday%7D%5D%5D%5Cn%0A%20%20published%3A%3A%20%5Cn%5Cn%0A%20%20%24%7Btags%7D%5Cn%60%3B%0A%0A%20%20document.location.href%20%3D%20%5B%0A%20%20%20%20%22obsidian%3A%2F%2Fnew%3F%22%2C%0A%20%20%20%20%60file%3D%24%7BencodeURIComponent(folder%20%2B%20fileName)%7D%60%2C%0A%20%20%20%20%60%26content%3D%24%7BencodeURIComponent(fileContent)%7D%60%2C%0A%20%20%20%20vaultName%2C%0A%20%20%5D.join(%22%22)%3B%0A%7D)%3B%7D)()%3B
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment