mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
Merge pull request #1935 from ZeroX-DG/allow-no-html-escape
Allow customizing html escape when export note
This commit is contained in:
@@ -240,7 +240,7 @@ export default class MarkdownPreview extends React.Component {
|
|||||||
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
|
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
|
||||||
|
|
||||||
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
|
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
|
||||||
let body = this.markdown.render(escapeHtmlCharacters(noteContent))
|
let body = this.markdown.render(escapeHtmlCharacters(noteContent, { detectCodeBlock: true }))
|
||||||
|
|
||||||
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
|
||||||
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(noteContent, this.props.storagePath)
|
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(noteContent, this.props.storagePath)
|
||||||
|
|||||||
@@ -251,4 +251,3 @@ class Markdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Markdown
|
export default Markdown
|
||||||
|
|
||||||
|
|||||||
@@ -6,52 +6,64 @@ export function lastFindInArray (array, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function escapeHtmlCharacters (text) {
|
export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
|
||||||
const matchHtmlRegExp = /["'&<>]/
|
const matchHtmlRegExp = /["'&<>]/g
|
||||||
const str = '' + text
|
const escapes = ['"', '&', ''', '<', '>']
|
||||||
const match = matchHtmlRegExp.exec(str)
|
let match = null
|
||||||
|
const replaceAt = (str, index, replace) =>
|
||||||
|
str.substr(0, index) +
|
||||||
|
replace +
|
||||||
|
str.substr(index + replace.length - (replace.length - 1))
|
||||||
|
|
||||||
if (!match) {
|
// detecting code block
|
||||||
return str
|
while ((match = matchHtmlRegExp.exec(html)) != null) {
|
||||||
}
|
const current = { char: match[0], index: match.index }
|
||||||
|
if (opt.detectCodeBlock) {
|
||||||
let escape
|
// position of the nearest line start
|
||||||
let html = ''
|
let previousLineEnd = current.index - 1
|
||||||
let index = 0
|
while (html[previousLineEnd] !== '\n' && previousLineEnd !== -1) {
|
||||||
let lastIndex = 0
|
previousLineEnd--
|
||||||
|
}
|
||||||
for (index = match.index; index < str.length; index++) {
|
// 4 spaces means this character is in a code block
|
||||||
switch (str.charCodeAt(index)) {
|
if (
|
||||||
case 34: // "
|
html[previousLineEnd + 1] === ' ' &&
|
||||||
escape = '"'
|
html[previousLineEnd + 2] === ' ' &&
|
||||||
break
|
html[previousLineEnd + 3] === ' ' &&
|
||||||
case 38: // &
|
html[previousLineEnd + 4] === ' '
|
||||||
escape = '&'
|
) {
|
||||||
break
|
// so skip it
|
||||||
case 39: // '
|
|
||||||
escape = '''
|
|
||||||
break
|
|
||||||
case 60: // <
|
|
||||||
escape = '<'
|
|
||||||
break
|
|
||||||
case 62: // >
|
|
||||||
escape = '>'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// otherwise, escape it !!!
|
||||||
if (lastIndex !== index) {
|
if (current.char === '&') {
|
||||||
html += str.substring(lastIndex, index)
|
let nextStr = ''
|
||||||
|
let nextIndex = current.index
|
||||||
|
let escapedStr = false
|
||||||
|
// maximum length of an escape string is 5. For example ('"')
|
||||||
|
while (nextStr.length <= 5) {
|
||||||
|
nextStr += html[nextIndex]
|
||||||
|
nextIndex++
|
||||||
|
if (escapes.indexOf(nextStr) !== -1) {
|
||||||
|
escapedStr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!escapedStr) {
|
||||||
|
// this & char is not a part of an escaped string
|
||||||
|
html = replaceAt(html, current.index, '&')
|
||||||
|
}
|
||||||
|
} else if (current.char === '"') {
|
||||||
|
html = replaceAt(html, current.index, '"')
|
||||||
|
} else if (current.char === "'") {
|
||||||
|
html = replaceAt(html, current.index, ''')
|
||||||
|
} else if (current.char === '<') {
|
||||||
|
html = replaceAt(html, current.index, '<')
|
||||||
|
} else if (current.char === '>') {
|
||||||
|
html = replaceAt(html, current.index, '>')
|
||||||
}
|
}
|
||||||
|
|
||||||
lastIndex = index + 1
|
|
||||||
html += escape
|
|
||||||
}
|
}
|
||||||
|
return html
|
||||||
return lastIndex !== index
|
|
||||||
? html + str.substring(lastIndex, index)
|
|
||||||
: html
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isObjectEqual (a, b) {
|
export function isObjectEqual (a, b) {
|
||||||
|
|||||||
48
tests/lib/escapeHtmlCharacters-test.js
Normal file
48
tests/lib/escapeHtmlCharacters-test.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const { escapeHtmlCharacters } = require('browser/lib/utils')
|
||||||
|
const test = require('ava')
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should return the original string if nothing needed to escape', t => {
|
||||||
|
const input = 'Nothing to be escaped'
|
||||||
|
const expected = 'Nothing to be escaped'
|
||||||
|
const actual = escapeHtmlCharacters(input)
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should skip code block if that option is enabled', t => {
|
||||||
|
const input = ` <no escape>
|
||||||
|
<escapeMe>`
|
||||||
|
const expected = ` <no escape>
|
||||||
|
<escapeMe>`
|
||||||
|
const actual = escapeHtmlCharacters(input, { detectCodeBlock: true })
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should NOT skip character not in code block but start with 4 spaces', t => {
|
||||||
|
const input = '4 spaces &'
|
||||||
|
const expected = '4 spaces &'
|
||||||
|
const actual = escapeHtmlCharacters(input, { detectCodeBlock: true })
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should NOT skip code block if that option is NOT enabled', t => {
|
||||||
|
const input = ` <no escape>
|
||||||
|
<escapeMe>`
|
||||||
|
const expected = ` <no escape>
|
||||||
|
<escapeMe>`
|
||||||
|
const actual = escapeHtmlCharacters(input)
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should NOT escape & character if it\'s a part of an escaped character', t => {
|
||||||
|
const input = 'Do not escape & or " but do escape &'
|
||||||
|
const expected = 'Do not escape & or " but do escape &'
|
||||||
|
const actual = escapeHtmlCharacters(input)
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('escapeHtmlCharacters should return the correct result', t => {
|
||||||
|
const input = '& < > " \''
|
||||||
|
const expected = '& < > " ''
|
||||||
|
const actual = escapeHtmlCharacters(input)
|
||||||
|
t.is(actual, expected)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user