From 04097ecfcd525c84fd31ffaa38d6bd3dbc6b956c Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sun, 19 Mar 2017 14:10:33 -0700 Subject: [PATCH 01/15] Fix incorrect tag completion in code block --- browser/components/MarkdownPreview.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 460920d8..9210e675 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -24,6 +24,21 @@ function decodeHTMLEntities (text) { return text } +function encodeHTMLEntities (text) { + var entities = [ + ['\'', 'apos'], + ['&', 'amp'], + ['<', 'lt'], + ['>', 'gt'] + ] + + for (var i = 0, max = entities.length; i < max; ++i) { + text = text.replace(entities[i][0], '&' + entities[i][1] + ';') + } + + return text +} + const { remote } = require('electron') const { app } = remote const path = require('path') @@ -241,7 +256,7 @@ export default class MarkdownPreview extends React.Component { let { value, theme, indentSize, codeBlockTheme } = this.props this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) - this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) + this.refs.root.contentWindow.document.body.innerHTML = markdown.render(encodeHTMLEntities(value)) _.forEach(this.refs.root.contentWindow.document.querySelectorAll('.taskListItem'), (el) => { el.parentNode.parentNode.style.listStyleType = 'none' From f7e0cb655f7c2bc6b107fddbc30e459f7dc93f72 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sun, 19 Mar 2017 14:18:59 -0700 Subject: [PATCH 02/15] Add question mark --- browser/components/MarkdownPreview.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 9210e675..4c862a05 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -14,7 +14,8 @@ function decodeHTMLEntities (text) { ['apos', '\''], ['amp', '&'], ['lt', '<'], - ['gt', '>'] + ['gt', '>'], + ['#63', '?'] ] for (var i = 0, max = entities.length; i < max; ++i) { @@ -29,7 +30,8 @@ function encodeHTMLEntities (text) { ['\'', 'apos'], ['&', 'amp'], ['<', 'lt'], - ['>', 'gt'] + ['>', 'gt'], + ['?', '#63'] ] for (var i = 0, max = entities.length; i < max; ++i) { From e60a5430b432a711a8c74bb733ad9ccbcd9d311f Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sun, 19 Mar 2017 15:26:43 -0700 Subject: [PATCH 03/15] Fix to replace only CodeBlock --- browser/components/MarkdownPreview.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 4c862a05..70d86819 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -258,7 +258,15 @@ export default class MarkdownPreview extends React.Component { let { value, theme, indentSize, codeBlockTheme } = this.props this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) - this.refs.root.contentWindow.document.body.innerHTML = markdown.render(encodeHTMLEntities(value)) + + const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g) + if (codeBlocks.length > 0) { + codeBlocks.forEach((codeBlock) => { + const encodedCodeBlock = encodeHTMLEntities(codeBlock) + value = value.replace(codeBlock, encodedCodeBlock) + }) + } + this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) _.forEach(this.refs.root.contentWindow.document.querySelectorAll('.taskListItem'), (el) => { el.parentNode.parentNode.style.listStyleType = 'none' From 10043cc755ad18fa332258040540c703b05c3e13 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sun, 19 Mar 2017 21:43:54 -0700 Subject: [PATCH 04/15] Fix to handle when codeBlocks is null --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 70d86819..863a3054 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -260,7 +260,7 @@ export default class MarkdownPreview extends React.Component { this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g) - if (codeBlocks.length > 0) { + if (codeBlocks !== null && codeBlocks.length > 0) { codeBlocks.forEach((codeBlock) => { const encodedCodeBlock = encodeHTMLEntities(codeBlock) value = value.replace(codeBlock, encodedCodeBlock) From 6229ca7ac994b6d5a6887da99f9eb437c8f80651 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Tue, 21 Mar 2017 23:27:32 -0700 Subject: [PATCH 05/15] Refactor to ES6 --- browser/components/MarkdownPreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 863a3054..069195bc 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -26,7 +26,7 @@ function decodeHTMLEntities (text) { } function encodeHTMLEntities (text) { - var entities = [ + const entities = [ ['\'', 'apos'], ['&', 'amp'], ['<', 'lt'], @@ -34,7 +34,7 @@ function encodeHTMLEntities (text) { ['?', '#63'] ] - for (var i = 0, max = entities.length; i < max; ++i) { + for (let i = 0; i < entities.length; ++i) { text = text.replace(entities[i][0], '&' + entities[i][1] + ';') } From cd2dc471c78c8eb2699204a43fd2d2615c936cd6 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Tue, 21 Mar 2017 23:33:08 -0700 Subject: [PATCH 06/15] Fix to string literal --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 069195bc..f191e018 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -35,7 +35,7 @@ function encodeHTMLEntities (text) { ] for (let i = 0; i < entities.length; ++i) { - text = text.replace(entities[i][0], '&' + entities[i][1] + ';') + text = text.replace(entities[i][0], `&${entities[i][1]};`) } return text From 1daf07edebfa30fd925d9880c579f775a373e1d5 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Tue, 21 Mar 2017 23:37:00 -0700 Subject: [PATCH 07/15] Refactor to more readable --- browser/components/MarkdownPreview.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index f191e018..d8bfcfe6 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -34,10 +34,9 @@ function encodeHTMLEntities (text) { ['?', '#63'] ] - for (let i = 0; i < entities.length; ++i) { - text = text.replace(entities[i][0], `&${entities[i][1]};`) - } - + entities.forEach((entity) => { + text = text.replace(entity[0], `&${entity[1]};`) + }) return text } From 39274ce483e2d5bfe4958f00aef1b69bb326f2e8 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Tue, 21 Mar 2017 23:43:37 -0700 Subject: [PATCH 08/15] Fix to handle with multiple entities in one line --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index d8bfcfe6..ee7eab94 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -35,7 +35,7 @@ function encodeHTMLEntities (text) { ] entities.forEach((entity) => { - text = text.replace(entity[0], `&${entity[1]};`) + text = text.replace(new RegExp(entity[0], 'g'), `&${entity[1]};`) }) return text } From adf81175f3817f5ffd445421b0894babe556048c Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Mon, 27 Mar 2017 02:59:04 -0700 Subject: [PATCH 09/15] Reduce the code --- browser/components/MarkdownPreview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index ee7eab94..8ac02648 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -261,8 +261,7 @@ export default class MarkdownPreview extends React.Component { const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g) if (codeBlocks !== null && codeBlocks.length > 0) { codeBlocks.forEach((codeBlock) => { - const encodedCodeBlock = encodeHTMLEntities(codeBlock) - value = value.replace(codeBlock, encodedCodeBlock) + value = value.replace(codeBlock, encodeHTMLEntities(codeBlock)) }) } this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) From 003d8a1b2127526d61585c810eb38155a5254617 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Mon, 27 Mar 2017 03:11:14 -0700 Subject: [PATCH 10/15] Fix a bug on regexp of a question mark --- browser/components/MarkdownPreview.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 8ac02648..218c805e 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -15,7 +15,7 @@ function decodeHTMLEntities (text) { ['amp', '&'], ['lt', '<'], ['gt', '>'], - ['#63', '?'] + ['#63', '\\?'] ] for (var i = 0, max = entities.length; i < max; ++i) { @@ -31,7 +31,7 @@ function encodeHTMLEntities (text) { ['&', 'amp'], ['<', 'lt'], ['>', 'gt'], - ['?', '#63'] + ['\\?', '#63'] ] entities.forEach((entity) => { From 71464112ce94a1e7066d64af05277ff9fbc4560a Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Mon, 27 Mar 2017 03:15:05 -0700 Subject: [PATCH 11/15] Remove a disused condition --- browser/components/MarkdownPreview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 218c805e..f3f5afdf 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -259,7 +259,7 @@ export default class MarkdownPreview extends React.Component { this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme) const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g) - if (codeBlocks !== null && codeBlocks.length > 0) { + if (codeBlocks !== null) { codeBlocks.forEach((codeBlock) => { value = value.replace(codeBlock, encodeHTMLEntities(codeBlock)) }) From 1722e103fc90f9b39669a70774567e508fbb5479 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sat, 22 Apr 2017 16:10:41 -0700 Subject: [PATCH 12/15] Change methods to helper methods --- browser/components/MarkdownPreview.js | 40 +++--------------------- browser/lib/htmlTextHelper.js | 44 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 35 deletions(-) create mode 100644 browser/lib/htmlTextHelper.js diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index f3f5afdf..d744a2e7 100644 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -8,37 +8,7 @@ import flowchart from 'flowchart' import SequenceDiagram from 'js-sequence-diagrams' import eventEmitter from 'browser/main/lib/eventEmitter' import fs from 'fs' - -function decodeHTMLEntities (text) { - var entities = [ - ['apos', '\''], - ['amp', '&'], - ['lt', '<'], - ['gt', '>'], - ['#63', '\\?'] - ] - - for (var i = 0, max = entities.length; i < max; ++i) { - text = text.replace(new RegExp('&' + entities[i][0] + ';', 'g'), entities[i][1]) - } - - return text -} - -function encodeHTMLEntities (text) { - const entities = [ - ['\'', 'apos'], - ['&', 'amp'], - ['<', 'lt'], - ['>', 'gt'], - ['\\?', '#63'] - ] - - entities.forEach((entity) => { - text = text.replace(new RegExp(entity[0], 'g'), `&${entity[1]};`) - }) - return text -} +import htmlTextHelper from 'browser/lib/htmlTextHelper' const { remote } = require('electron') const { app } = remote @@ -261,7 +231,7 @@ export default class MarkdownPreview extends React.Component { const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g) if (codeBlocks !== null) { codeBlocks.forEach((codeBlock) => { - value = value.replace(codeBlock, encodeHTMLEntities(codeBlock)) + value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock)) }) } this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) @@ -286,7 +256,7 @@ export default class MarkdownPreview extends React.Component { let syntax = CodeMirror.findModeByName(el.className) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') CodeMirror.requireMode(syntax.mode, () => { - let content = decodeHTMLEntities(el.innerHTML) + let content = htmlTextHelper.decodeEntities(el.innerHTML) el.innerHTML = '' el.parentNode.className += ` cm-s-${codeBlockTheme} CodeMirror` CodeMirror.runMode(content, syntax.mime, el, { @@ -304,7 +274,7 @@ export default class MarkdownPreview extends React.Component { _.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => { Raphael.setWindow(this.getWindow()) try { - let diagram = flowchart.parse(decodeHTMLEntities(el.innerHTML)) + let diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML)) el.innerHTML = '' diagram.drawSVG(el, opts) _.forEach(el.querySelectorAll('a'), (el) => { @@ -320,7 +290,7 @@ export default class MarkdownPreview extends React.Component { _.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => { Raphael.setWindow(this.getWindow()) try { - let diagram = SequenceDiagram.parse(decodeHTMLEntities(el.innerHTML)) + let diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML)) el.innerHTML = '' diagram.drawSVG(el, {theme: 'simple'}) _.forEach(el.querySelectorAll('a'), (el) => { diff --git a/browser/lib/htmlTextHelper.js b/browser/lib/htmlTextHelper.js new file mode 100644 index 00000000..7cb9e5fe --- /dev/null +++ b/browser/lib/htmlTextHelper.js @@ -0,0 +1,44 @@ +/** + * @fileoverview Text trimmer for html. + */ + +/** + * @param {string} text + * @return {string} + */ + +export function decodeEntities (text) { + var entities = [ + ['apos', '\''], + ['amp', '&'], + ['lt', '<'], + ['gt', '>'], + ['#63', '\\?'] + ] + + for (var i = 0, max = entities.length; i < max; ++i) { + text = text.replace(new RegExp(`&${entities[i][0]};`, 'g'), entities[i][1]) + } + + return text +} + +export function encodeEntities (text) { + const entities = [ + ['\'', 'apos'], + ['&', 'amp'], + ['<', 'lt'], + ['>', 'gt'], + ['\\?', '#63'] + ] + + entities.forEach((entity) => { + text = text.replace(new RegExp(entity[0], 'g'), `&${entity[1]};`) + }) + return text +} + +export default { + decodeEntities, + encodeEntities, +} From 8e2fd300f6cbd884ce8a964d0d933b5e3d827936 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sat, 22 Apr 2017 16:13:55 -0700 Subject: [PATCH 13/15] Delete an encode table because when & is encoded, it affect other encodes due to they use & --- browser/lib/htmlTextHelper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/lib/htmlTextHelper.js b/browser/lib/htmlTextHelper.js index 7cb9e5fe..884e8744 100644 --- a/browser/lib/htmlTextHelper.js +++ b/browser/lib/htmlTextHelper.js @@ -26,7 +26,6 @@ export function decodeEntities (text) { export function encodeEntities (text) { const entities = [ ['\'', 'apos'], - ['&', 'amp'], ['<', 'lt'], ['>', 'gt'], ['\\?', '#63'] From b695d27817d8e0b5453a4a471a3fafdd3c592f09 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sat, 22 Apr 2017 17:19:05 -0700 Subject: [PATCH 14/15] Add test for htmlTextHelper --- tests/lib/html-text-helper-test.js | 52 ++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/lib/html-text-helper-test.js diff --git a/tests/lib/html-text-helper-test.js b/tests/lib/html-text-helper-test.js new file mode 100644 index 00000000..c61b3d16 --- /dev/null +++ b/tests/lib/html-text-helper-test.js @@ -0,0 +1,52 @@ +/** + * @fileoverview Unit test for browser/lib/htmlTextHelper + */ +const test = require('ava') +const htmlTextHelper = require('browser/lib/htmlTextHelper') + +// Unit test +test('htmlTextHelper#decodeEntities should return encoded text (string)', t => { + // [input, expected] + const testCases = [ + ['<a href=', 'Boostnote"], + ["<\\?php\n var = 'hoge';", "<\\?php\n var = 'hoge';"], + ["&", "&"], + ] + + testCases.forEach(testCase => { + const [input, expected] = testCase; + t.is(htmlTextHelper.decodeEntities(input), expected, `Test for decodeEntities() input: ${input} expected: ${expected}`); + }) +}) + +test('htmlTextHelper#decodeEntities() should return decoded text (string)', t => { + // [input, expected] + const testCases = [ + ['Boostnote", '<a href='https://boostnote.io'>Boostnote'], + [" { + const [input, expected] = testCase; + t.is(htmlTextHelper.encodeEntities(input), expected, `Test for encodeEntities() input: ${input} expected: ${expected}`); + }) +}) + +// Integration test +test(t => { + const testCases = [ + "var test = 'test'", + "Boostnote", + "", + ] + + testCases.forEach(testCase => { + const encodedText = htmlTextHelper.encodeEntities(testCase) + const decodedText = htmlTextHelper.decodeEntities(encodedText) + t.is(decodedText, testCase, `Integration test for encodedText() and decodedText()`); + }) +}) From 2df295dc1d393cd718015fbc44e37077ac0185f1 Mon Sep 17 00:00:00 2001 From: asmsuechan Date: Sat, 22 Apr 2017 17:30:55 -0700 Subject: [PATCH 15/15] Fix styles by lint --- browser/lib/htmlTextHelper.js | 2 +- tests/lib/html-text-helper-test.js | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/browser/lib/htmlTextHelper.js b/browser/lib/htmlTextHelper.js index 884e8744..49952fbd 100644 --- a/browser/lib/htmlTextHelper.js +++ b/browser/lib/htmlTextHelper.js @@ -39,5 +39,5 @@ export function encodeEntities (text) { export default { decodeEntities, - encodeEntities, + encodeEntities } diff --git a/tests/lib/html-text-helper-test.js b/tests/lib/html-text-helper-test.js index c61b3d16..a476c0dd 100644 --- a/tests/lib/html-text-helper-test.js +++ b/tests/lib/html-text-helper-test.js @@ -9,15 +9,15 @@ test('htmlTextHelper#decodeEntities should return encoded text (string)', t => { // [input, expected] const testCases = [ ['<a href=', 'Boostnote"], - ["<\\?php\n var = 'hoge';", "<\\?php\n var = 'hoge';"], - ["&", "&"], + ['var test = 'test'', 'var test = \'test\''], + ['<a href='https://boostnote.io'>Boostnote', 'Boostnote'], + ['<\\\\?php\n var = 'hoge';', '<\\\\?php\n var = \'hoge\';'], + ['&', '&'] ] testCases.forEach(testCase => { - const [input, expected] = testCase; - t.is(htmlTextHelper.decodeEntities(input), expected, `Test for decodeEntities() input: ${input} expected: ${expected}`); + const [input, expected] = testCase + t.is(htmlTextHelper.decodeEntities(input), expected, `Test for decodeEntities() input: ${input} expected: ${expected}`) }) }) @@ -25,28 +25,28 @@ test('htmlTextHelper#decodeEntities() should return decoded text (string)', t => // [input, expected] const testCases = [ ['Boostnote", '<a href='https://boostnote.io'>Boostnote'], - ["Boostnote', '<a href='https://boostnote.io'>Boostnote'], + [' { - const [input, expected] = testCase; - t.is(htmlTextHelper.encodeEntities(input), expected, `Test for encodeEntities() input: ${input} expected: ${expected}`); + const [input, expected] = testCase + t.is(htmlTextHelper.encodeEntities(input), expected, `Test for encodeEntities() input: ${input} expected: ${expected}`) }) }) // Integration test test(t => { const testCases = [ - "var test = 'test'", - "Boostnote", - "", + 'var test = \'test\'', + 'Boostnote', + '' ] testCases.forEach(testCase => { const encodedText = htmlTextHelper.encodeEntities(testCase) const decodedText = htmlTextHelper.decodeEntities(encodedText) - t.is(decodedText, testCase, `Integration test for encodedText() and decodedText()`); + t.is(decodedText, testCase, 'Integration test through encodedText() and decodedText()') }) })