NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name VueMastery Lesson Description Copier // @namespace https://github.com/ArjixWasTaken/my-userscripts // @version 0.5 // @description Adds a button to copy the lesson description in markdown. (Yes, it converts the html description to markdown) // @author Arjix // @license MIT // @match https://www.vuemastery.com/courses/* // @icon https://www.google.com/s2/favicons?domain=vuemastery.com // @grant none // @require https://unpkg.com/turndown/dist/turndown.js // @require https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.13/beautify.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.13/beautify-css.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.13.13/beautify-html.min.js // ==/UserScript== const onUriChange = (callback, onHashChange = false) => { var currentPage = ""; setInterval(() => { if (currentPage != location.href) { if (onHashChange) { if (currentPage.split("#")[0] != location.href.split("#")[0]) { currentPage = location.href; callback(); } } else { currentPage = location.href; callback(); } } }, 600); }; const createElement = (element, extra) => { const x = document.createElement(element); Object.entries(extra).forEach(([key, value]) => { x.setAttribute(key, value); }); return x; }; const waitForEl = (selector, callback) => { // https://gist.github.com/chrisjhoughton/7890303 if (document.querySelector(selector)) { callback(); } else { setTimeout(function () { waitForEl(selector, callback); }, 100); } }; const waitForElemToDisappear = (selector, callback) => { if (!document.querySelector(selector)) { callback(); } else { setTimeout(function () { waitForElemToDisappear(selector, callback); }, 100); } }; const copyToClipboard = (text) => { // https://stackoverflow.com/a/46215202/13077523 const elem = document.createElement("textarea"); elem.value = text; document.body.appendChild(elem); elem.select(); document.execCommand("copy"); document.body.removeChild(elem); }; const formatCodeBlocks = (text) => { var formattedString = ""; const regex = "```(html|css|js)\n((?:\n|.)*?)```"; const matches = [...text.matchAll(regex)]; if (matches.length == 0) { return text; } var lastIndex = null; matches.forEach((match, index) => { if (lastIndex != null) { formattedString += text.slice(lastIndex, match.index - 1); lastIndex = match.index + match[0].length; } else { lastIndex = match.index + match[0].length; formattedString += text.slice(0, match.index - 1); } switch (match[1]) { case "html": // alert(match[2]); formattedString += "\n```html\n" + html_beautify(match[2]) + "\n```"; break; case "css": // alert(match[2]); formattedString += "\n```css\n" + css_beautify(match[2]) + "\n```"; break; case "js": // alert(match[2]); formattedString += "\n```js\n" + js_beautify(match[2]) + "\n```"; break; } if (index == matches.length - 1) { formattedString += text.slice(lastIndex, text.length - 1); } }); return formattedString; }; const f = () => { const lessonText = document.querySelector("#lessonContent").innerHTML; const turndownService = new TurndownService({ codeBlockStyle: "fenced" }); const markdown = formatCodeBlocks(turndownService.turndown(lessonText)); copyToClipboard(markdown); alert("copied to clipboard"); }; const main = () => { waitForElemToDisappear("#dmpDesc", () => { const btn = createElement("a", { download: "", id: "dmpDesc", class: "button secondary -full", "data-v-33c1ff86": "", "data-v-1f426ff6": "", }); const svg = createElement("svg", { name: "download-cloud", type: "feather", width: "24", height: "24", class: "icon", "data-v-7a19a3d2": "", "data-v-1f426ff6": "", }); const use = createElement("use", { "xlink:href": "/images/spr-feather.svg#download-cloud", "data-v-7a19a3d2": "", }); svg.appendChild(use); btn.appendChild(svg); btn.innerHTML += "Copy lesson MarkDown"; const panel = document.querySelector( "#__layout > div > div > div > div.page > div > aside > div > div:nth-child(1) > div.card-body" ); panel.insertBefore(btn, panel.firstElementChild.nextSibling); document.getElementById("dmpDesc").onclick = f; }); }; (function () { "use strict"; onUriChange(() => { waitForEl("div.card-body", main); }); })();