From b29d2c0a311b1ebca10b71e55e61fede6388dbdd Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Fri, 1 May 2020 12:14:59 +0800 Subject: [PATCH 1/4] Implement cspreview renderer Signed-off-by: Yukai Huang --- package-lock.json | 6 ++++ package.json | 1 + public/js/extra.js | 7 +++++ public/js/index.js | 4 ++- public/js/lib/renderer/csvpreview.js | 43 ++++++++++++++++++++++++++++ public/js/lib/syncscroll.js | 14 +++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 public/js/lib/renderer/csvpreview.js diff --git a/package-lock.json b/package-lock.json index 2b8efafe..59bce698 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12024,6 +12024,12 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "papaparse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz", + "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==", + "dev": true + }, "parallel-transform": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", diff --git a/package.json b/package.json index cbdcdd50..0e38cb59 100644 --- a/package.json +++ b/package.json @@ -169,6 +169,7 @@ "mock-require": "~3.0.3", "nyc": "~14.0.0", "optimize-css-assets-webpack-plugin": "~5.0.0", + "papaparse": "^5.2.0", "pdfobject": "~2.1.1", "plantuml-encoder": "^1.2.5", "power-assert": "~1.6.1", diff --git a/public/js/extra.js b/public/js/extra.js index b3ea2409..10e163d2 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -25,6 +25,7 @@ import { } from './lib/markdown/utils' import { renderFretBoard } from './lib/renderer/fretboard/fretboard' import './lib/renderer/lightbox' +import { renderCSVPreview } from './lib/renderer/csvpreview' import markdownit from 'markdown-it' import markdownitContainer from 'markdown-it-container' @@ -1211,6 +1212,12 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { if (info) { langName = info.split(/\s+/g)[0] + + if (langName === 'csvpreview') { + const params = parseFenceCodeParams(info) + return renderCSVPreview(token.content, params) + } + if (/!$/.test(info)) token.attrJoin('class', 'wrap') token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, '')) token.attrJoin('class', 'hljs') diff --git a/public/js/index.js b/public/js/index.js index f5010960..47203a55 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2786,7 +2786,9 @@ function updateViewInner () { var lastMeta = md.meta md.meta = {} delete md.metaError - var rendered = md.render(value) + const mdEnv = {} + window.mdTokens = md.parse(value, mdEnv) + let rendered = md.renderer.render(window.mdTokens, md.options, mdEnv) if (md.meta.type && md.meta.type === 'slide') { var slideOptions = { separator: '^(\r\n?|\n)---(\r\n?|\n)$', diff --git a/public/js/lib/renderer/csvpreview.js b/public/js/lib/renderer/csvpreview.js new file mode 100644 index 00000000..da12d720 --- /dev/null +++ b/public/js/lib/renderer/csvpreview.js @@ -0,0 +1,43 @@ +import Papa from 'papaparse' + +const safeParse = d => { + try { + return JSON.parse(d) + } catch (err) { + return d + } +} + +export function renderCSVPreview (csv, options = {}, attr = '') { + const opt = Object.keys(options).reduce((acc, key) => { + return Object.assign(acc, { + [key]: safeParse(options[key]) + }) + }, {}) + + const results = Papa.parse(csv.trim(), opt) + + if (opt.header) { + const fields = results.meta.fields + return ` + + + ${fields.map(f => ``).join('')} + + + + ${results.data.map(d => ` + ${fields.map(f => ``).join('')} + `).join('')} + +
${f}
${d[f]}
` + } else { + return ` + + ${results.data.map(d => ` + ${d.map(f => ``).join('')} + `).join('')} + +
${f}
` + } +} diff --git a/public/js/lib/syncscroll.js b/public/js/lib/syncscroll.js index df771c8e..41f0af1f 100644 --- a/public/js/lib/syncscroll.js +++ b/public/js/lib/syncscroll.js @@ -7,6 +7,8 @@ import markdownitContainer from 'markdown-it-container' import { md } from '../extra' import modeType from './modeType' import appState from './appState' +import { renderCSVPreview } from './renderer/csvpreview' +import { parseFenceCodeParams } from './markdown/utils' function addPart (tokens, idx) { if (tokens[idx].map && tokens[idx].level === 0) { @@ -71,6 +73,18 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { if (info) { langName = info.split(/\s+/g)[0] + + if (langName === 'csvpreview') { + const params = parseFenceCodeParams(info) + let attr = '' + if (tokens[idx].map && tokens[idx].level === 0) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + attr = `class="part" data-startline="${startline}" data-endline="${endline}"` + } + return renderCSVPreview(token.content, params, attr) + } + if (/!$/.test(info)) token.attrJoin('class', 'wrap') token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!/, '')) token.attrJoin('class', 'hljs') From 1adf1221c665e43283e312de804e195d54ec6058 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Thu, 9 Jul 2020 14:42:48 +0800 Subject: [PATCH 2/4] Escape html for table cell Signed-off-by: Yukai Huang --- public/js/lib/renderer/csvpreview.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/js/lib/renderer/csvpreview.js b/public/js/lib/renderer/csvpreview.js index da12d720..a8a0724c 100644 --- a/public/js/lib/renderer/csvpreview.js +++ b/public/js/lib/renderer/csvpreview.js @@ -1,4 +1,5 @@ import Papa from 'papaparse' +import escapeHTML from 'lodash/escape' const safeParse = d => { try { @@ -22,12 +23,12 @@ export function renderCSVPreview (csv, options = {}, attr = '') { return ` - ${fields.map(f => ``).join('')} + ${fields.map(f => ``).join('')} ${results.data.map(d => ` - ${fields.map(f => ``).join('')} + ${fields.map(f => ``).join('')} `).join('')}
${f}${escapeHTML(f)}
${d[f]}${escapeHTML(d[f])}
` @@ -35,7 +36,7 @@ export function renderCSVPreview (csv, options = {}, attr = '') { return ` ${results.data.map(d => ` - ${d.map(f => ``).join('')} + ${d.map(f => ``).join('')} `).join('')}
${f}${escapeHTML(f)}
` From cec3c45c2ec9a7ff667a35aea8455618c7d9001d Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Thu, 9 Jul 2020 16:53:50 +0800 Subject: [PATCH 3/4] Update features.md Signed-off-by: Yukai Huang --- public/docs/features.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/public/docs/features.md b/public/docs/features.md index c3f5fac9..d806dd83 100644 --- a/public/docs/features.md +++ b/public/docs/features.md @@ -208,6 +208,30 @@ When you’re a carpenter making a beautiful chest of drawers, you’re not goin > > Even support the nest blockquotes! > > [name=ChengHan Wu] [time=Sun, Jun 28, 2015 10:00 PM] [color=red] +### Render CSV as table + +You can use write csv in the codeblock: + +~~~md +```csvpreview {header="true"} +firstName,lastName,email,phoneNumber +John,Doe,john@doe.com,0123456789 +Jane,Doe,jane@doe.com,9876543210 +James,Bond,james.bond@mi6.co.uk,0612345678 +``` +~~~ + +which rendered to: + +```csvpreview {header="true"} +firstName,lastName,email,phoneNumber +John,Doe,john@doe.com,0123456789 +Jane,Doe,jane@doe.com,9876543210 +James,Bond,james.bond@mi6.co.uk,0612345678 +``` + +We use [Papa Parse](https://www.papaparse.com/) for parsing csv. The parsing option is given in braces: `{}`, and multiple options are seperated by a space. e.g. `{header="true" delimiter="."}`. Please read [their documentation](https://www.papaparse.com/docs#config) as reference. + ## Externals ### YouTube From 731cec6966280350f9c5ab8e6e7f4f267e819740 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Thu, 9 Jul 2020 17:25:07 +0800 Subject: [PATCH 4/4] Revert mdTokens changes Signed-off-by: Yukai Huang --- public/js/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index 47203a55..f5010960 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -2786,9 +2786,7 @@ function updateViewInner () { var lastMeta = md.meta md.meta = {} delete md.metaError - const mdEnv = {} - window.mdTokens = md.parse(value, mdEnv) - let rendered = md.renderer.render(window.mdTokens, md.options, mdEnv) + var rendered = md.render(value) if (md.meta.type && md.meta.type === 'slide') { var slideOptions = { separator: '^(\r\n?|\n)---(\r\n?|\n)$',