Skip to content

Commit

Permalink
feat: katex in markdown preview + xss fix for svg
Browse files Browse the repository at this point in the history
  • Loading branch information
NGPixel committed Mar 6, 2020
1 parent 4398573 commit 830f516
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 2 deletions.
140 changes: 140 additions & 0 deletions client/components/editor/common/katex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Test if potential opening or closing delimieter
// Assumes that there is a "$" at state.src[pos]
function isValidDelim (state, pos) {
let prevChar
let nextChar
let max = state.posMax
let canOpen = true
let canClose = true

prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1
nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1

// Check non-whitespace conditions for opening and closing, and
// check that closing delimeter isn't followed by a number
if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \t */ ||
(nextChar >= 0x30/* "0" */ && nextChar <= 0x39/* "9" */)) {
canClose = false
}
if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \t */) {
canOpen = false
}

return {
canOpen: canOpen,
canClose: canClose
}
}

export default {
katexInline (state, silent) {
let start, match, token, res, pos

if (state.src[state.pos] !== '$') { return false }

res = isValidDelim(state, state.pos)
if (!res.canOpen) {
if (!silent) { state.pending += '$' }
state.pos += 1
return true
}

// First check for and bypass all properly escaped delimieters
// This loop will assume that the first leading backtick can not
// be the first character in state.src, which is known since
// we have found an opening delimieter already.
start = state.pos + 1
match = start
while ((match = state.src.indexOf('$', match)) !== -1) {
// Found potential $, look for escapes, pos will point to
// first non escape when complete
pos = match - 1
while (state.src[pos] === '\\') { pos -= 1 }

// Even number of escapes, potential closing delimiter found
if (((match - pos) % 2) === 1) { break }
match += 1
}

// No closing delimter found. Consume $ and continue.
if (match === -1) {
if (!silent) { state.pending += '$' }
state.pos = start
return true
}

// Check if we have empty content, ie: $$. Do not parse.
if (match - start === 0) {
if (!silent) { state.pending += '$$' }
state.pos = start + 1
return true
}

// Check for valid closing delimiter
res = isValidDelim(state, match)
if (!res.canClose) {
if (!silent) { state.pending += '$' }
state.pos = start
return true
}

if (!silent) {
token = state.push('katex_inline', 'math', 0)
token.markup = '$'
token.content = state.src.slice(start, match)
}

state.pos = match + 1
return true
},

katexBlock (state, start, end, silent) {
let firstLine; let lastLine; let next; let lastPos; let found = false; let token
let pos = state.bMarks[start] + state.tShift[start]
let max = state.eMarks[start]

if (pos + 2 > max) { return false }
if (state.src.slice(pos, pos + 2) !== '$$') { return false }

pos += 2
firstLine = state.src.slice(pos, max)

if (silent) { return true }
if (firstLine.trim().slice(-2) === '$$') {
// Single line expression
firstLine = firstLine.trim().slice(0, -2)
found = true
}

for (next = start; !found;) {
next++

if (next >= end) { break }

pos = state.bMarks[next] + state.tShift[next]
max = state.eMarks[next]

if (pos < max && state.tShift[next] < state.blkIndent) {
// non-empty line with negative indent should stop the list:
break
}

if (state.src.slice(pos, max).trim().slice(-2) === '$$') {
lastPos = state.src.slice(0, max).lastIndexOf('$$')
lastLine = state.src.slice(pos, lastPos)
found = true
}
}

state.line = next + 1

token = state.push('katex_block', 'math', 0)
token.block = true
token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '') +
state.getLines(start + 1, next, state.tShift[start], true) +
(lastLine && lastLine.trim() ? lastLine : '')
token.map = [ start, state.line ]
token.markup = '$$'
return true
}
}
33 changes: 33 additions & 0 deletions client/components/editor/editor-markdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,14 @@ import mdSup from 'markdown-it-sup'
import mdSub from 'markdown-it-sub'
import mdMark from 'markdown-it-mark'
import mdImsize from 'markdown-it-imsize'
import katex from 'katex'
// Prism (Syntax Highlighting)
import Prism from 'prismjs'
// Helpers
import katexHelper from './common/katex'
// ========================================
// INIT
// ========================================
Expand Down Expand Up @@ -278,6 +282,35 @@ md.renderer.rules.paragraph_open = injectLineNumbers
md.renderer.rules.heading_open = injectLineNumbers
md.renderer.rules.blockquote_open = injectLineNumbers
// ========================================
// KATEX
// ========================================
md.inline.ruler.after('escape', 'katex_inline', katexHelper.katexInline)
md.renderer.rules.katex_inline = (tokens, idx) => {
try {
return katex.renderToString(tokens[idx].content, {
displayMode: false
})
} catch (err) {
console.warn(err)
return tokens[idx].content
}
}
md.block.ruler.after('blockquote', 'katex_block', katexHelper.katexBlock, {
alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
})
md.renderer.rules.katex_block = (tokens, idx) => {
try {
return `<p>` + katex.renderToString(tokens[idx].content, {
displayMode: true
}) + `</p>`
} catch (err) {
console.warn(err)
return tokens[idx].content
}
}
// ========================================
// Vue Component
// ========================================
Expand Down
4 changes: 2 additions & 2 deletions server/modules/rendering/html-security/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ module.exports = {
path: ['d', 'style'],
pre: ['class', 'style'],
section: ['class', 'style'],
span: ['class', 'style'],
span: ['class', 'style', 'aria-hidden'],
strong: ['class', 'style'],
summary: ['class', 'style'],
svg: ['width', 'height', 'viewBox', 'preserveAspectRatio', 'style'],
svg: ['width', 'height', 'viewbox', 'preserveaspectratio', 'style'],
table: ['border', 'class', 'id', 'style', 'width'],
tbody: ['class', 'style'],
td: ['align', 'class', 'colspan', 'rowspan', 'style', 'valign'],
Expand Down

1 comment on commit 830f516

@justinacolmena
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the latest commit, I'm not entirely sure, but the square root symbol is definitely working as one would expect it to, and also the KaTeX plugin works in preview mode, which it didn't before; as to
issue #1476.

I'm not entirely sure how I'm "supposed" to install the development version from git. From the wiki directory, various commands, dependencies, there's a Makefile. I am unfamiliar with this stuff, some futzing around, it works, comes up... Some precise build instructions might be helpful, edit config.yml etc.

$ git clone https://github.com/Requarks/wiki.git
$ cd wiki/
$ npm install
$ sudo npm install --global webpack webpack-cli
$ make build
$ node server/
Loading configuration from /home/justina/wiki/config.yml... OK
2020-03-08T00:23:49.284Z [MASTER] info: =======================================
2020-03-08T00:23:49.288Z [MASTER] info: = Wiki.js 2.0.0 =======================
2020-03-08T00:23:49.288Z [MASTER] info: =======================================
2020-03-08T00:23:49.289Z [MASTER] info: Initializing...
2020-03-08T00:23:54.844Z [MASTER] info: Using database driver pg for postgres [ OK ]
2020-03-08T00:23:54.854Z [MASTER] info: Connecting to database...
2020-03-08T00:23:54.903Z [MASTER] info: Database Connection Successful [ OK ]
2020-03-08T00:23:55.013Z [MASTER] warn: DB Configuration is empty or incomplete. Switching to Setup mode...
2020-03-08T00:23:55.014Z [MASTER] info: Starting setup wizard...
2020-03-08T00:23:56.515Z [MASTER] info: Starting HTTP server on port 3000...
2020-03-08T00:23:56.515Z [MASTER] info: HTTP Server on port: [ 3000 ]
2020-03-08T00:23:56.525Z [MASTER] info: HTTP Server: [ RUNNING ]
2020-03-08T00:23:56.525Z [MASTER] info: 🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻🔻
2020-03-08T00:23:56.525Z [MASTER] info: 
2020-03-08T00:23:56.525Z [MASTER] info: Browse to http://localhost:3000/ to complete setup!
2020-03-08T00:23:56.525Z [MASTER] info: 
2020-03-08T00:23:56.525Z [MASTER] info: 🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺🔺

ss-sqrt-wiki-js-2020-03-07

Please sign in to comment.