-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from hannahilea/hr/local
Add md, logo assets locally
- Loading branch information
Showing
8 changed files
with
318 additions
and
10 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,4 +98,3 @@ end | |
if abspath(PROGRAM_FILE) == @__FILE__ | ||
run_wizard(isempty(ARGS) ? missing : first(ARGS)) | ||
end | ||
|
This file contains 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
This file contains 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
This file contains 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
// https://md-block.verou.me/md-block.js | ||
/** | ||
* <md-block> 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/[email protected]/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(/</g, "<"); | ||
} | ||
|
||
return `<code>${code}</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 = `<a href="#${id}" class="anchor">`; | ||
|
||
if (hlinks === "") { | ||
// Heading content is the link | ||
content += text + "</a>"; | ||
} | ||
else { | ||
// Headings are prepended with a linked symbol | ||
content += hlinks + "</a>" + text; | ||
} | ||
} | ||
|
||
return ` | ||
<h${level} id="${id}"> | ||
${content} | ||
</h${level}>`; | ||
}, | ||
|
||
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(/</g, "<"); | ||
} | ||
|
||
return `<pre class="language-${language}"><code>${code}</code></pre>`; | ||
} | ||
}, 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); |