-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmarkdown.js
156 lines (124 loc) · 4.38 KB
/
markdown.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { Remarkable, utils } from "remarkable"
const { has, unescapeMd, replaceEntities, escapeHtml } = utils
import { linkify } from "remarkable/linkify"
import hljs from "highlight.js"
import remarkableKatex from "remarkable-katex"
import "katex/dist/katex.css"
/**
* @param {string} link
* @returns {string}
*/
const getDomain = (link) => {
try {
return new URL(link)?.hostname
} catch {
return ''
}
}
/**
* @param {string} link
* @returns {boolean}
*/
const isWhiteListed = (link) => {
return imgHostWhitelist.includes(getDomain(link))
}
verifyLink = (link) => {
let linkHref = escapeHtml(replaceEntities(link.href))
if (linkHref !== link.innerHTML) {
return confirm(`Warning, please verify this is where you want to go: ${linkHref}`)
}
return true
}
/**
* @param {Partial<Remarkable.Options>} options
*/
const createRenderer = (options) => {
const markdownOptions = {
html: false,
xhtmlOut: false,
breaks: true,
langPrefix: 'hljs language-',
linkTarget: '_blank" rel="noreferrer',
typographer: true,
quotes: `""''`,
doHighlight: true,
/**
* @param {string} code
* @param {string} lang
*/
highlight(code, lang) {
if (!this.doHighlight) {
return ''
}
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(code, { language: lang }).value
} catch (_) { }
}
try {
return hljs.highlightAuto(code).value
} catch (_) { }
return ''
},
...options
}
const md = new Remarkable('full', markdownOptions)
md.renderer.rules.fence = function (tokens, idx, options, env, instance) {
var token = tokens[idx]
var langClass = ' class="hljs"'
var langPrefix = options.langPrefix
var langName = '', fences, fenceName
var highlighted
if (token.params) {
fences = token.params.split(/\s+/g)
fenceName = fences.join(' ')
if (has(instance.rules.fence_custom, fences[0])) {
return instance.rules.fence_custom[fences[0]](tokens, idx, options, env, instance)
}
langName = escapeHtml(replaceEntities(unescapeMd(fenceName)))
langClass = ' class="' + langPrefix + langName + '"'
}
if (options.highlight) {
highlighted = options.highlight.apply(options, [token.content].concat(fences))
|| escapeHtml(token.content)
} else {
highlighted = escapeHtml(token.content)
}
return '<pre><code' + langClass + '>'
+ highlighted
+ '</code></pre>'
+ md.renderer.getBreak(tokens, idx)
}
md.renderer.rules.image = (tokens, idx, options) => {
const src = escapeHtml(tokens[idx].src)
if (isWhiteListed(src) && allowImages) {
const imgSrc = ' src="' + escapeHtml(tokens[idx].src) + '"'
const title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : ''
const alt = ' alt="' + (tokens[idx].alt ? escapeHtml(replaceEntities(unescapeMd(tokens[idx].alt))) : '') + '"'
const suffix = options.xhtmlOut ? ' /' : ''
const scrollOnload = isAtBottom() ? ' onload="window.scrollTo(0, document.body.scrollHeight)"' : ''
return '<a href="' + src + '" target="_blank" rel="noreferrer"><img' + scrollOnload + imgSrc + alt + title + suffix + '></a>'
}
return '<a href="' + src + '" target="_blank" rel="noreferrer">' + escapeHtml(replaceEntities(src)) + '</a>'
}
md.renderer.rules.link_open = (tokens, idx, options) => {
const title = tokens[idx].title ? (' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"') : ''
const target = options.linkTarget ? (' target="' + options.linkTarget + '"') : ''
return '<a rel="noreferrer" onclick="return verifyLink(this)" href="' + escapeHtml(tokens[idx].href) + '"' + title + target + '>'
}
md.renderer.rules.text = (tokens, idx) => {
tokens[idx].content = escapeHtml(tokens[idx].content)
if (tokens[idx].content.indexOf('?') !== -1) {
tokens[idx].content = tokens[idx].content.replace(/(^|\s)(\?)\S+?(?=[,.!?:)]?\s|$)/gm, (match) => {
const channelLink = escapeHtml(replaceEntities(match.trim()))
const whiteSpace = (match[0] === '?') ? '' : match[0]
return whiteSpace + '<a href="' + channelLink + '" target="_blank">' + channelLink + '</a>'
})
}
return tokens[idx].content
}
md.use(remarkableKatex)
md.use(linkify)
return md
}
export { createRenderer }