Skip to content

Commit

Permalink
Merge pull request #1935 from ZeroX-DG/allow-no-html-escape
Browse files Browse the repository at this point in the history
Allow customizing html escape when export note
  • Loading branch information
Rokt33r committed Jul 17, 2018
2 parents ef3649b + 563fdcb commit b18a09e
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 43 deletions.
2 changes: 1 addition & 1 deletion browser/components/MarkdownPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export default class MarkdownPreview extends React.Component {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()

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 attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(noteContent, this.props.storagePath)
Expand Down
1 change: 0 additions & 1 deletion browser/lib/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,3 @@ class Markdown {
}

export default Markdown

94 changes: 53 additions & 41 deletions browser/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,64 @@ export function lastFindInArray (array, callback) {
}
}

export function escapeHtmlCharacters (text) {
const matchHtmlRegExp = /["'&<>]/
const str = '' + text
const match = matchHtmlRegExp.exec(str)
export function escapeHtmlCharacters (html, opt = { detectCodeBlock: false }) {
const matchHtmlRegExp = /["'&<>]/g
const escapes = ['&quot;', '&amp;', '&#39;', '&lt;', '&gt;']
let match = null
const replaceAt = (str, index, replace) =>
str.substr(0, index) +
replace +
str.substr(index + replace.length - (replace.length - 1))

if (!match) {
return str
}

let escape
let html = ''
let index = 0
let lastIndex = 0

for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = '&quot;'
break
case 38: // &
escape = '&amp;'
break
case 39: // '
escape = '&#39;'
break
case 60: // <
escape = '&lt;'
break
case 62: // >
escape = '&gt;'
break
default:
// detecting code block
while ((match = matchHtmlRegExp.exec(html)) != null) {
const current = { char: match[0], index: match.index }
if (opt.detectCodeBlock) {
// position of the nearest line start
let previousLineEnd = current.index - 1
while (html[previousLineEnd] !== '\n' && previousLineEnd !== -1) {
previousLineEnd--
}
// 4 spaces means this character is in a code block
if (
html[previousLineEnd + 1] === ' ' &&
html[previousLineEnd + 2] === ' ' &&
html[previousLineEnd + 3] === ' ' &&
html[previousLineEnd + 4] === ' '
) {
// so skip it
continue
}
}

if (lastIndex !== index) {
html += str.substring(lastIndex, index)
// otherwise, escape it !!!
if (current.char === '&') {
let nextStr = ''
let nextIndex = current.index
let escapedStr = false
// maximum length of an escape string is 5. For example ('&quot;')
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, '&amp;')
}
} else if (current.char === '"') {
html = replaceAt(html, current.index, '&quot;')
} else if (current.char === "'") {
html = replaceAt(html, current.index, '&#39;')
} else if (current.char === '<') {
html = replaceAt(html, current.index, '&lt;')
} else if (current.char === '>') {
html = replaceAt(html, current.index, '&gt;')
}

lastIndex = index + 1
html += escape
}

return lastIndex !== index
? html + str.substring(lastIndex, index)
: html
return html
}

export function isObjectEqual (a, b) {
Expand Down
48 changes: 48 additions & 0 deletions tests/lib/escapeHtmlCharacters-test.js
Original file line number Diff line number Diff line change
@@ -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>
&lt;escapeMe&gt;`
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 &amp;'
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 = ` &lt;no escape&gt;
&lt;escapeMe&gt;`
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 &amp; or &quot; but do escape &'
const expected = 'Do not escape &amp; or &quot; but do escape &amp;'
const actual = escapeHtmlCharacters(input)
t.is(actual, expected)
})

test('escapeHtmlCharacters should return the correct result', t => {
const input = '& < > " \''
const expected = '&amp; &lt; &gt; &quot; &#39;'
const actual = escapeHtmlCharacters(input)
t.is(actual, expected)
})

0 comments on commit b18a09e

Please sign in to comment.