mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
fixed eslint error & integrated with prettier as well as formatted the whole codebase (#3450)
This commit is contained in:
@@ -10,18 +10,20 @@ import ConfigManager from 'browser/main/lib/ConfigManager'
|
||||
import katex from 'katex'
|
||||
import { lastFindInArray } from './utils'
|
||||
|
||||
function createGutter (str, firstLineNumber) {
|
||||
function createGutter(str, firstLineNumber) {
|
||||
if (Number.isNaN(firstLineNumber)) firstLineNumber = 1
|
||||
const lastLineNumber = (str.match(/\n/g) || []).length + firstLineNumber - 1
|
||||
const lines = []
|
||||
for (let i = firstLineNumber; i <= lastLineNumber; i++) {
|
||||
lines.push('<span class="CodeMirror-linenumber">' + i + '</span>')
|
||||
}
|
||||
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
|
||||
return (
|
||||
'<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
|
||||
)
|
||||
}
|
||||
|
||||
class Markdown {
|
||||
constructor (options = {}) {
|
||||
constructor(options = {}) {
|
||||
const config = ConfigManager.get()
|
||||
const defaultOptions = {
|
||||
typographer: config.preview.smartQuotes,
|
||||
@@ -37,29 +39,129 @@ class Markdown {
|
||||
this.md.linkify.set({ fuzzyLink: false })
|
||||
|
||||
if (updatedOptions.sanitize !== 'NONE') {
|
||||
const allowedTags = ['iframe', 'input', 'b',
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'h7', 'h8', 'br', 'b', 'i', 'strong', 'em', 'a', 'pre', 'code', 'img', 'tt',
|
||||
'div', 'ins', 'del', 'sup', 'sub', 'p', 'ol', 'ul', 'table', 'thead', 'tbody', 'tfoot', 'blockquote',
|
||||
'dl', 'dt', 'dd', 'kbd', 'q', 'samp', 'var', 'hr', 'ruby', 'rt', 'rp', 'li', 'tr', 'td', 'th', 's', 'strike', 'summary', 'details'
|
||||
const allowedTags = [
|
||||
'iframe',
|
||||
'input',
|
||||
'b',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'h7',
|
||||
'h8',
|
||||
'br',
|
||||
'b',
|
||||
'i',
|
||||
'strong',
|
||||
'em',
|
||||
'a',
|
||||
'pre',
|
||||
'code',
|
||||
'img',
|
||||
'tt',
|
||||
'div',
|
||||
'ins',
|
||||
'del',
|
||||
'sup',
|
||||
'sub',
|
||||
'p',
|
||||
'ol',
|
||||
'ul',
|
||||
'table',
|
||||
'thead',
|
||||
'tbody',
|
||||
'tfoot',
|
||||
'blockquote',
|
||||
'dl',
|
||||
'dt',
|
||||
'dd',
|
||||
'kbd',
|
||||
'q',
|
||||
'samp',
|
||||
'var',
|
||||
'hr',
|
||||
'ruby',
|
||||
'rt',
|
||||
'rp',
|
||||
'li',
|
||||
'tr',
|
||||
'td',
|
||||
'th',
|
||||
's',
|
||||
'strike',
|
||||
'summary',
|
||||
'details'
|
||||
]
|
||||
const allowedAttributes = [
|
||||
'abbr', 'accept', 'accept-charset',
|
||||
'accesskey', 'action', 'align', 'alt', 'axis',
|
||||
'border', 'cellpadding', 'cellspacing', 'char',
|
||||
'charoff', 'charset', 'checked',
|
||||
'clear', 'cols', 'colspan', 'color',
|
||||
'compact', 'coords', 'datetime', 'dir',
|
||||
'disabled', 'enctype', 'for', 'frame',
|
||||
'headers', 'height', 'hreflang',
|
||||
'hspace', 'ismap', 'label', 'lang',
|
||||
'maxlength', 'media', 'method',
|
||||
'multiple', 'name', 'nohref', 'noshade',
|
||||
'nowrap', 'open', 'prompt', 'readonly', 'rel', 'rev',
|
||||
'rows', 'rowspan', 'rules', 'scope',
|
||||
'selected', 'shape', 'size', 'span',
|
||||
'start', 'summary', 'tabindex', 'target',
|
||||
'title', 'type', 'usemap', 'valign', 'value',
|
||||
'vspace', 'width', 'itemprop'
|
||||
'abbr',
|
||||
'accept',
|
||||
'accept-charset',
|
||||
'accesskey',
|
||||
'action',
|
||||
'align',
|
||||
'alt',
|
||||
'axis',
|
||||
'border',
|
||||
'cellpadding',
|
||||
'cellspacing',
|
||||
'char',
|
||||
'charoff',
|
||||
'charset',
|
||||
'checked',
|
||||
'clear',
|
||||
'cols',
|
||||
'colspan',
|
||||
'color',
|
||||
'compact',
|
||||
'coords',
|
||||
'datetime',
|
||||
'dir',
|
||||
'disabled',
|
||||
'enctype',
|
||||
'for',
|
||||
'frame',
|
||||
'headers',
|
||||
'height',
|
||||
'hreflang',
|
||||
'hspace',
|
||||
'ismap',
|
||||
'label',
|
||||
'lang',
|
||||
'maxlength',
|
||||
'media',
|
||||
'method',
|
||||
'multiple',
|
||||
'name',
|
||||
'nohref',
|
||||
'noshade',
|
||||
'nowrap',
|
||||
'open',
|
||||
'prompt',
|
||||
'readonly',
|
||||
'rel',
|
||||
'rev',
|
||||
'rows',
|
||||
'rowspan',
|
||||
'rules',
|
||||
'scope',
|
||||
'selected',
|
||||
'shape',
|
||||
'size',
|
||||
'span',
|
||||
'start',
|
||||
'summary',
|
||||
'tabindex',
|
||||
'target',
|
||||
'title',
|
||||
'type',
|
||||
'usemap',
|
||||
'valign',
|
||||
'value',
|
||||
'vspace',
|
||||
'width',
|
||||
'itemprop'
|
||||
]
|
||||
|
||||
if (updatedOptions.sanitize === 'ALLOW_STYLES') {
|
||||
@@ -72,20 +174,20 @@ class Markdown {
|
||||
allowedTags,
|
||||
allowedAttributes: {
|
||||
'*': allowedAttributes,
|
||||
'a': ['href'],
|
||||
'div': ['itemscope', 'itemtype'],
|
||||
'blockquote': ['cite'],
|
||||
'del': ['cite'],
|
||||
'ins': ['cite'],
|
||||
'q': ['cite'],
|
||||
'img': ['src', 'width', 'height'],
|
||||
'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
|
||||
'input': ['type', 'id', 'checked']
|
||||
a: ['href'],
|
||||
div: ['itemscope', 'itemtype'],
|
||||
blockquote: ['cite'],
|
||||
del: ['cite'],
|
||||
ins: ['cite'],
|
||||
q: ['cite'],
|
||||
img: ['src', 'width', 'height'],
|
||||
iframe: ['src', 'width', 'height', 'frameborder', 'allowfullscreen'],
|
||||
input: ['type', 'id', 'checked']
|
||||
},
|
||||
allowedIframeHostnames: ['www.youtube.com'],
|
||||
selfClosing: [ 'img', 'br', 'hr', 'input' ],
|
||||
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ],
|
||||
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ],
|
||||
selfClosing: ['img', 'br', 'hr', 'input'],
|
||||
allowedSchemes: ['http', 'https', 'ftp', 'mailto'],
|
||||
allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
|
||||
allowProtocolRelative: true
|
||||
})
|
||||
}
|
||||
@@ -98,7 +200,7 @@ class Markdown {
|
||||
inlineClose: config.preview.latexInlineClose,
|
||||
blockOpen: config.preview.latexBlockOpen,
|
||||
blockClose: config.preview.latexBlockClose,
|
||||
inlineRenderer: function (str) {
|
||||
inlineRenderer: function(str) {
|
||||
let output = ''
|
||||
try {
|
||||
output = katex.renderToString(str.trim())
|
||||
@@ -107,7 +209,7 @@ class Markdown {
|
||||
}
|
||||
return output
|
||||
},
|
||||
blockRenderer: function (str) {
|
||||
blockRenderer: function(str) {
|
||||
let output = ''
|
||||
try {
|
||||
output = katex.renderToString(str.trim(), { displayMode: true })
|
||||
@@ -124,7 +226,19 @@ class Markdown {
|
||||
slugify: require('./slugify')
|
||||
})
|
||||
this.md.use(require('markdown-it-kbd'))
|
||||
this.md.use(require('markdown-it-admonition'), {types: ['note', 'hint', 'attention', 'caution', 'danger', 'error', 'quote', 'abstract', 'question']})
|
||||
this.md.use(require('markdown-it-admonition'), {
|
||||
types: [
|
||||
'note',
|
||||
'hint',
|
||||
'attention',
|
||||
'caution',
|
||||
'danger',
|
||||
'error',
|
||||
'quote',
|
||||
'abstract',
|
||||
'question'
|
||||
]
|
||||
})
|
||||
this.md.use(require('markdown-it-abbr'))
|
||||
this.md.use(require('markdown-it-sub'))
|
||||
this.md.use(require('markdown-it-sup'))
|
||||
@@ -144,63 +258,86 @@ class Markdown {
|
||||
this.md.use(require('./markdown-it-deflist'))
|
||||
this.md.use(require('./markdown-it-frontmatter'))
|
||||
|
||||
this.md.use(require('./markdown-it-fence'), {
|
||||
chart: token => {
|
||||
if (token.parameters.hasOwnProperty('yaml')) {
|
||||
token.parameters.format = 'yaml'
|
||||
}
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="chart" data-height="${token.parameters.height}" data-format="${token.parameters.format || 'json'}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
flowchart: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="flowchart" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
gallery: token => {
|
||||
const content = token.content.split('\n').slice(0, -1).map(line => {
|
||||
const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line)
|
||||
if (match) {
|
||||
return mdurl.encode(match[1])
|
||||
} else {
|
||||
return mdurl.encode(line)
|
||||
this.md.use(
|
||||
require('./markdown-it-fence'),
|
||||
{
|
||||
chart: token => {
|
||||
if (token.parameters.hasOwnProperty('yaml')) {
|
||||
token.parameters.format = 'yaml'
|
||||
}
|
||||
}).join('\n')
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="gallery" data-autoplay="${token.parameters.autoplay}" data-height="${token.parameters.height}">${content}</div>
|
||||
<div class="chart" data-height="${
|
||||
token.parameters.height
|
||||
}" data-format="${token.parameters.format || 'json'}">${
|
||||
token.content
|
||||
}</div>
|
||||
</pre>`
|
||||
},
|
||||
flowchart: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="flowchart" data-height="${token.parameters.height}">${
|
||||
token.content
|
||||
}</div>
|
||||
</pre>`
|
||||
},
|
||||
gallery: token => {
|
||||
const content = token.content
|
||||
.split('\n')
|
||||
.slice(0, -1)
|
||||
.map(line => {
|
||||
const match = /!\[[^\]]*]\(([^\)]*)\)/.exec(line)
|
||||
if (match) {
|
||||
return mdurl.encode(match[1])
|
||||
} else {
|
||||
return mdurl.encode(line)
|
||||
}
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="gallery" data-autoplay="${
|
||||
token.parameters.autoplay
|
||||
}" data-height="${token.parameters.height}">${content}</div>
|
||||
</pre>`
|
||||
},
|
||||
mermaid: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="mermaid" data-height="${token.parameters.height}">${
|
||||
token.content
|
||||
}</div>
|
||||
</pre>`
|
||||
},
|
||||
sequence: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="sequence" data-height="${token.parameters.height}">${
|
||||
token.content
|
||||
}</div>
|
||||
</pre>`
|
||||
}
|
||||
},
|
||||
mermaid: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="mermaid" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
},
|
||||
sequence: token => {
|
||||
return `<pre class="fence" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
<div class="sequence" data-height="${token.parameters.height}">${token.content}</div>
|
||||
</pre>`
|
||||
}
|
||||
}, token => {
|
||||
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
|
||||
token => {
|
||||
return `<pre class="code CodeMirror" data-line="${token.map[0]}">
|
||||
<span class="filename">${token.fileName}</span>
|
||||
${createGutter(token.content, token.firstLineNumber)}
|
||||
<code class="${token.langType}">${token.content}</code>
|
||||
</pre>`
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const deflate = require('markdown-it-plantuml/lib/deflate')
|
||||
const plantuml = require('markdown-it-plantuml')
|
||||
const plantUmlStripTrailingSlash = (url) => url.endsWith('/') ? url.slice(0, -1) : url
|
||||
const plantUmlServerAddress = plantUmlStripTrailingSlash(config.preview.plantUMLServerAddress)
|
||||
const parsePlantUml = function (umlCode, openMarker, closeMarker, type) {
|
||||
const plantUmlStripTrailingSlash = url =>
|
||||
url.endsWith('/') ? url.slice(0, -1) : url
|
||||
const plantUmlServerAddress = plantUmlStripTrailingSlash(
|
||||
config.preview.plantUMLServerAddress
|
||||
)
|
||||
const parsePlantUml = function(umlCode, openMarker, closeMarker, type) {
|
||||
const s = unescape(encodeURIComponent(umlCode))
|
||||
const zippedCode = deflate.encode64(
|
||||
deflate.zip_deflate(`${openMarker}\n${s}\n${closeMarker}`, 9)
|
||||
@@ -209,39 +346,47 @@ class Markdown {
|
||||
}
|
||||
|
||||
this.md.use(plantuml, {
|
||||
generateSource: (umlCode) => parsePlantUml(umlCode, '@startuml', '@enduml', 'svg')
|
||||
generateSource: umlCode =>
|
||||
parsePlantUml(umlCode, '@startuml', '@enduml', 'svg')
|
||||
})
|
||||
|
||||
// Ditaa support. PlantUML server doesn't support Ditaa in SVG, so we set the format as PNG at the moment.
|
||||
this.md.use(plantuml, {
|
||||
openMarker: '@startditaa',
|
||||
closeMarker: '@endditaa',
|
||||
generateSource: (umlCode) => parsePlantUml(umlCode, '@startditaa', '@endditaa', 'png')
|
||||
generateSource: umlCode =>
|
||||
parsePlantUml(umlCode, '@startditaa', '@endditaa', 'png')
|
||||
})
|
||||
|
||||
// Mindmap support
|
||||
this.md.use(plantuml, {
|
||||
openMarker: '@startmindmap',
|
||||
closeMarker: '@endmindmap',
|
||||
generateSource: (umlCode) => parsePlantUml(umlCode, '@startmindmap', '@endmindmap', 'svg')
|
||||
generateSource: umlCode =>
|
||||
parsePlantUml(umlCode, '@startmindmap', '@endmindmap', 'svg')
|
||||
})
|
||||
|
||||
// WBS support
|
||||
this.md.use(plantuml, {
|
||||
openMarker: '@startwbs',
|
||||
closeMarker: '@endwbs',
|
||||
generateSource: (umlCode) => parsePlantUml(umlCode, '@startwbs', '@endwbs', 'svg')
|
||||
generateSource: umlCode =>
|
||||
parsePlantUml(umlCode, '@startwbs', '@endwbs', 'svg')
|
||||
})
|
||||
|
||||
// Gantt support
|
||||
this.md.use(plantuml, {
|
||||
openMarker: '@startgantt',
|
||||
closeMarker: '@endgantt',
|
||||
generateSource: (umlCode) => parsePlantUml(umlCode, '@startgantt', '@endgantt', 'svg')
|
||||
generateSource: umlCode =>
|
||||
parsePlantUml(umlCode, '@startgantt', '@endgantt', 'svg')
|
||||
})
|
||||
|
||||
// Override task item
|
||||
this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
|
||||
this.md.block.ruler.at('paragraph', function(
|
||||
state,
|
||||
startLine /*, endLine */
|
||||
) {
|
||||
let content, terminate, i, l, token
|
||||
let nextLine = startLine + 1
|
||||
const terminatorRules = state.md.block.ruler.getRules('paragraph')
|
||||
@@ -251,10 +396,14 @@ class Markdown {
|
||||
for (; nextLine < endLine && !state.isEmpty(nextLine); nextLine++) {
|
||||
// this would be a code block normally, but after paragraph
|
||||
// it's considered a lazy continuation regardless of what's there
|
||||
if (state.sCount[nextLine] - state.blkIndent > 3) { continue }
|
||||
if (state.sCount[nextLine] - state.blkIndent > 3) {
|
||||
continue
|
||||
}
|
||||
|
||||
// quirk for blockquotes, this line should already be checked by that rule
|
||||
if (state.sCount[nextLine] < 0) { continue }
|
||||
if (state.sCount[nextLine] < 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Some tags can terminate paragraph without empty line.
|
||||
terminate = false
|
||||
@@ -264,10 +413,14 @@ class Markdown {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (terminate) { break }
|
||||
if (terminate) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
content = state.getLines(startLine, nextLine, state.blkIndent, false).trim()
|
||||
content = state
|
||||
.getLines(startLine, nextLine, state.blkIndent, false)
|
||||
.trim()
|
||||
|
||||
state.line = nextLine
|
||||
|
||||
@@ -277,18 +430,31 @@ class Markdown {
|
||||
if (state.parentType === 'list') {
|
||||
const match = content.match(/^\[( |x)\] ?(.+)/i)
|
||||
if (match) {
|
||||
const liToken = lastFindInArray(state.tokens, token => token.type === 'list_item_open')
|
||||
const liToken = lastFindInArray(
|
||||
state.tokens,
|
||||
token => token.type === 'list_item_open'
|
||||
)
|
||||
if (liToken) {
|
||||
if (!liToken.attrs) {
|
||||
liToken.attrs = []
|
||||
}
|
||||
if (config.preview.lineThroughCheckbox) {
|
||||
liToken.attrs.push(['class', `taskListItem${match[1] !== ' ' ? ' checked' : ''}`])
|
||||
liToken.attrs.push([
|
||||
'class',
|
||||
`taskListItem${match[1] !== ' ' ? ' checked' : ''}`
|
||||
])
|
||||
} else {
|
||||
liToken.attrs.push(['class', 'taskListItem'])
|
||||
}
|
||||
}
|
||||
content = `<label class='taskListItem${match[1] !== ' ' ? ' checked' : ''}' for='checkbox-${startLine + 1}'><input type='checkbox'${match[1] !== ' ' ? ' checked' : ''} id='checkbox-${startLine + 1}'/> ${content.substring(4, content.length)}</label>`
|
||||
content = `<label class='taskListItem${
|
||||
match[1] !== ' ' ? ' checked' : ''
|
||||
}' for='checkbox-${startLine + 1}'><input type='checkbox'${
|
||||
match[1] !== ' ' ? ' checked' : ''
|
||||
} id='checkbox-${startLine + 1}'/> ${content.substring(
|
||||
4,
|
||||
content.length
|
||||
)}</label>`
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +475,7 @@ class Markdown {
|
||||
// Add line number attribute for scrolling
|
||||
const originalRender = this.md.renderer.render
|
||||
this.md.renderer.render = (tokens, options, env) => {
|
||||
tokens.forEach((token) => {
|
||||
tokens.forEach(token => {
|
||||
switch (token.type) {
|
||||
case 'blockquote_open':
|
||||
case 'dd_open':
|
||||
@@ -330,7 +496,7 @@ class Markdown {
|
||||
window.md = this.md
|
||||
}
|
||||
|
||||
render (content) {
|
||||
render(content) {
|
||||
if (!_.isString(content)) content = ''
|
||||
return this.md.render(content)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user