From ad6c35b45132cb780294600d29c6ed0f51bd8067 Mon Sep 17 00:00:00 2001 From: hannahilea Date: Thu, 9 May 2024 12:12:25 -0400 Subject: [PATCH] add md, logo assets locally --- add_stuff.jl | 1 - blog/__template/index.html | 2 +- blog/hello-world/index.html | 2 +- blog/index.html | 2 +- img/github-logo-styled.svg | 1 + img/linkedin-logo-styled.svg | 1 + index.html | 14 +- scripts/md-block.js | 305 +++++++++++++++++++++++++++++++++++ 8 files changed, 318 insertions(+), 10 deletions(-) create mode 100644 img/github-logo-styled.svg create mode 100644 img/linkedin-logo-styled.svg create mode 100644 scripts/md-block.js diff --git a/add_stuff.jl b/add_stuff.jl index c069394..dc7b846 100644 --- a/add_stuff.jl +++ b/add_stuff.jl @@ -98,4 +98,3 @@ end if abspath(PROGRAM_FILE) == @__FILE__ run_wizard(isempty(ARGS) ? missing : first(ARGS)) end - diff --git a/blog/__template/index.html b/blog/__template/index.html index 695d618..c6dabfa 100644 --- a/blog/__template/index.html +++ b/blog/__template/index.html @@ -4,7 +4,7 @@ Blog: {{ BLOG_TITLE }} - + diff --git a/blog/hello-world/index.html b/blog/hello-world/index.html index b567d29..021ffff 100644 --- a/blog/hello-world/index.html +++ b/blog/hello-world/index.html @@ -4,7 +4,7 @@ Blog: Hello world - + diff --git a/blog/index.html b/blog/index.html index bf23461..b021798 100644 --- a/blog/index.html +++ b/blog/index.html @@ -10,7 +10,7 @@ - + @hannahilea: blog diff --git a/img/github-logo-styled.svg b/img/github-logo-styled.svg new file mode 100644 index 0000000..60636b1 --- /dev/null +++ b/img/github-logo-styled.svg @@ -0,0 +1 @@ +GITHUBGITHUB \ No newline at end of file diff --git a/img/linkedin-logo-styled.svg b/img/linkedin-logo-styled.svg new file mode 100644 index 0000000..6efa444 --- /dev/null +++ b/img/linkedin-logo-styled.svg @@ -0,0 +1 @@ +LINKEDINLINKEDIN \ No newline at end of file diff --git a/index.html b/index.html index db0ad48..5bbce3c 100755 --- a/index.html +++ b/index.html @@ -49,13 +49,15 @@
DSP ENGINEER | MAKER

This website is currently under active development!

-

View Hannah Robertson's profile on Github +

+ + View Hannah Robertson's profile on Github
- View Hannah Robertson's profile on LinkedIn + + + View Hannah Robertson's profile on LinkedIn

diff --git a/scripts/md-block.js b/scripts/md-block.js new file mode 100644 index 0000000..a2f47d1 --- /dev/null +++ b/scripts/md-block.js @@ -0,0 +1,305 @@ +// https://md-block.verou.me/md-block.js +/** + * custom element + * @author Lea Verou + */ + +let marked = window.marked; +let DOMPurify = window.DOMPurify; +let Prism = window.Prism; + +export const URLs = { + marked: "https://cdn.jsdelivr.net/npm/marked/src/marked.min.js", + DOMPurify: "https://cdn.jsdelivr.net/npm/dompurify@2.3.3/dist/purify.es.min.js" +} + +// Fix indentation +function deIndent(text) { + let indent = text.match(/^[\r\n]*([\t ]+)/); + + if (indent) { + indent = indent[1]; + + text = text.replace(RegExp("^" + indent, "gm"), ""); + } + + return text; +} + +export class MarkdownElement extends HTMLElement { + constructor() { + super(); + + this.renderer = Object.assign({}, this.constructor.renderer); + + for (let property in this.renderer) { + this.renderer[property] = this.renderer[property].bind(this); + } + } + + get rendered() { + return this.getAttribute("rendered"); + } + + get mdContent () { + return this._mdContent; + } + + set mdContent (html) { + this._mdContent = html; + this._contentFromHTML = false; + + this.render(); + } + + connectedCallback() { + Object.defineProperty(this, "untrusted", { + value: this.hasAttribute("untrusted"), + enumerable: true, + configurable: false, + writable: false + }); + + if (this._mdContent === undefined) { + this._contentFromHTML = true; + this._mdContent = deIndent(this.innerHTML); + // https://github.com/markedjs/marked/issues/874#issuecomment-339995375 + // marked expects markdown quotes (>) to be un-escaped, otherwise they won't render correctly + this._mdContent = this._mdContent.replace(/>/g, '>'); + } + + this.render(); + } + + async render () { + if (!this.isConnected || this._mdContent === undefined) { + return; + } + + if (!marked) { + marked = import(URLs.marked).then(m => m.marked); + } + + marked = await marked; + + marked.setOptions({ + gfm: true, + smartypants: true, + langPrefix: "language-", + }); + + marked.use({renderer: this.renderer}); + + let html = this._parse(); + + if (this.untrusted) { + let mdContent = this._mdContent; + html = await MarkdownElement.sanitize(html); + if (this._mdContent !== mdContent) { + // While we were running this async call, the content changed + // We don’t want to overwrite with old data. Abort mission! + return; + } + } + + this.innerHTML = html; + + if (!Prism && URLs.Prism && this.querySelector("code")) { + Prism = import(URLs.Prism); + + if (URLs.PrismCSS) { + let link = document.createElement("link"); + link.rel = "stylesheet"; + link.href = URLs.PrismCSS; + document.head.appendChild(link); + } + } + + if (Prism) { + await Prism; // in case it's still loading + Prism.highlightAllUnder(this); + } + + if (this.src) { + this.setAttribute("rendered", this._contentFromHTML? "fallback" : "remote"); + } + else { + this.setAttribute("rendered", this._contentFromHTML? "content" : "property"); + } + + // Fire event + let event = new CustomEvent("md-render", {bubbles: true, composed: true}); + this.dispatchEvent(event); + } + + static async sanitize(html) { + if (!DOMPurify) { + DOMPurify = import(URLs.DOMPurify).then(m => m.default); + } + + DOMPurify = await DOMPurify; // in case it's still loading + + return DOMPurify.sanitize(html); + } +}; + +export class MarkdownSpan extends MarkdownElement { + constructor() { + super(); + } + + _parse () { + return marked.parseInline(this._mdContent); + } + + static renderer = { + codespan (code) { + if (this._contentFromHTML) { + // Inline HTML code needs to be escaped to not be parsed as HTML by the browser + // This results in marked double-escaping it, so we need to unescape it + code = code.replace(/&(?=[lg]t;)/g, "&"); + } + else { + // Remote code may include characters that need to be escaped to be visible in HTML + code = code.replace(/${code}`; + } + } +} + +export class MarkdownBlock extends MarkdownElement { + constructor() { + super(); + } + + get src() { + return this._src; + } + + set src(value) { + this.setAttribute("src", value); + } + + get hmin() { + return this._hmin || 1; + } + + set hmin(value) { + this.setAttribute("hmin", value); + } + + get hlinks() { + return this._hlinks ?? null; + } + + set hlinks(value) { + this.setAttribute("hlinks", value); + } + + _parse () { + return marked.parse(this._mdContent); + } + + static renderer = Object.assign({ + heading (text, level, _raw, slugger) { + level = Math.min(6, level + (this.hmin - 1)); + const id = slugger.slug(text); + const hlinks = this.hlinks; + + let content; + + if (hlinks === null) { + // No heading links + content = text; + } + else { + content = ``; + + if (hlinks === "") { + // Heading content is the link + content += text + ""; + } + else { + // Headings are prepended with a linked symbol + content += hlinks + "" + text; + } + } + + return ` + + ${content} + `; + }, + + code (code, language, escaped) { + if (this._contentFromHTML) { + // Inline HTML code needs to be escaped to not be parsed as HTML by the browser + // This results in marked double-escaping it, so we need to unescape it + code = code.replace(/&(?=[lg]t;)/g, "&"); + } + else { + // Remote code may include characters that need to be escaped to be visible in HTML + code = code.replace(/${code}`; + } + }, MarkdownSpan.renderer); + + static get observedAttributes() { + return ["src", "hmin", "hlinks"]; + } + + attributeChangedCallback(name, oldValue, newValue) { + if (oldValue === newValue) { + return; + } + + switch (name) { + case "src": + let url; + try { + url = new URL(newValue, location); + } + catch (e) { + return; + } + + let prevSrc = this.src; + this._src = url; + + if (this.src !== prevSrc) { + fetch(this.src) + .then(response => { + if (!response.ok) { + throw new Error(`Failed to fetch ${this.src}: ${response.status} ${response.statusText}`); + } + + return response.text(); + }) + .then(text => { + this.mdContent = text; + }) + .catch(e => {}); + } + + break; + case "hmin": + if (newValue > 0) { + this._hmin = +newValue; + + this.render(); + } + break; + case "hlinks": + this._hlinks = newValue; + this.render(); + } + } +} + + +customElements.define("md-block", MarkdownBlock); +customElements.define("md-span", MarkdownSpan);