Skip to content

Commit

Permalink
refactor to markdown-it/linkify-it
Browse files Browse the repository at this point in the history
  • Loading branch information
kennethbruskiewicz committed Nov 6, 2023
1 parent 568a944 commit 0da7f16
Show file tree
Hide file tree
Showing 5 changed files with 12,458 additions and 764 deletions.
13 changes: 7 additions & 6 deletions lib/DataHarmonizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import '@selectize/selectize';
import Handsontable from 'handsontable';
import $ from 'jquery';
import { utils as XlsxUtils, read as xlsxRead } from 'xlsx/xlsx.mjs';
import { renderContent, urlToClickableAnchor } from './utils/content';

import { wait, isValidHeaderRow } from './utils/general';
import { readFileAsync, updateSheetRange } from './utils/files';
Expand Down Expand Up @@ -127,7 +128,7 @@ class DataHarmonizer {
const field = this.getFields().filter(
(field) => field.title === innerText
)[0];
$('#field-description-text').html(this.getComment(field));
$('#field-description-text').html(urlToClickableAnchor(this.getComment(field)));
$('#field-description-modal').modal('show');
});
}
Expand Down Expand Up @@ -1192,7 +1193,7 @@ class DataHarmonizer {
}

$('#field-description-text').html(
`${fields[col].title}<select multiple class="multiselect" rows="15">${content}</select>`
`${fields[col].title}<select multiple class="multiselect" rows="15">${renderContent(content)}</select>`
);
$('#field-description-modal').modal('show');
$('#field-description-text .multiselect')
Expand All @@ -1205,14 +1206,14 @@ class DataHarmonizer {
let indentation = 12 + label.search(/\S/) * 8; // pixels
return `<div style="padding-left:${indentation}px" class="option ${
value === '' ? 'selectize-dropdown-emptyoptionlabel' : ''
}">${escape(label)}</div>`;
}">${renderContent(escape(label))}</div>`;
},
},
}) // must be rendered when html is visible
.on('change', function () {
let newValCsv = formatMultivaluedValue(
let newValCsv = renderContent(formatMultivaluedValue(
$('#field-description-text .multiselect').val()
);
));
self.setDataAtCell(row, col, newValCsv, 'thisChange');
});
// Saves users a click:
Expand Down Expand Up @@ -1350,7 +1351,7 @@ class DataHarmonizer {
ret += `<p><strong>Menus</strong>: </p>${slot_dict.sources}`;
}

return ret;
return renderContent(ret);
}

getCommentDict(field) {
Expand Down
66 changes: 41 additions & 25 deletions lib/utils/content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { marked } from 'marked'; // TODO: use nano-markdown instead?
import DOMPurify from 'isomorphic-dompurify'; // TODO: other sanitizers smaller, quicker?
import MarkdownIt from 'markdown-it';
import linkifyIt from 'linkify-it';

const linkify = linkifyIt();

const md = new MarkdownIt({
html: true,
linkify: true,
typographer: false,
breaks: true
});


// These parsers are used on modal content to render markdown and sanitize inputs.
// Support for custom HTML markup based on contents is also performed here using render rules.
Expand All @@ -10,14 +20,6 @@ import DOMPurify from 'isomorphic-dompurify'; // TODO: other sanitizers smaller,
// const URL_MATCH = new RegExp("(?<url>@^(https?|ftp)://[^\s/$.?#].[^\s]*$@iS)");
const URL_MATCH = /(?<!\]\()(?<url>@^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$@iS)/g;

// Rendering helpers
// Pipe implementation to chain steps we might add to the content renderer
// https://www.freecodecamp.org/news/pipe-and-compose-in-javascript-5b04004ac937/
const pipe =
(...fns) =>
(x) =>
fns.reduce((v, f) => f(v), x);

/* Operations */

// Convert matched raw URLs to a markdown link, to guarantee the conversion of links to anchored HTML hrefs
Expand All @@ -29,26 +31,40 @@ export const urlToMarkdownLink = (content) =>
// Replace operation using the named capture group "url"
// const str = "Visit [my site](https://example.com) or directly go to https://example.org";
// urlToClickableAnchor(str) == `Visit [my site](https://example.com) or directly go to <a href="https://example.org">https://example.org</a>`
export const urlToClickableAnchor = (content) =>
content.replace(URL_MATCH, (_, url) => `<a href="${url}">${url}</a>`);
export const urlToClickableAnchor = (content) => {
const url = linkify.match(content)[0].url
return content.replace(linkify.match(content)[0].url, `<a href="${url}">${url}</a>`);
};

/* Renderer */

// DEPRECATED

// Rendering helpers
// Pipe implementation to chain steps we might add to the content renderer
// https://www.freecodecamp.org/news/pipe-and-compose-in-javascript-5b04004ac937/
const pipe =
(...fns) =>
(x) =>
fns.reduce((v, f) => f(v), x);

// Put the renderers and the sanitizers on the pre- or post-process pipes
const preprocess = (content) => pipe(urlToMarkdownLink)(content);
const postprocess = (content) =>
pipe(
// urlToClickableAnchor,
DOMPurify.sanitize // NOTE: make keep this as the last command in the pipe for safety!
)(content);
// const preprocess = (content) => pipe(urlToMarkdownLink)(content);
// const postprocess = (content) =>
// pipe(
// // urlToClickableAnchor,
// DOMPurify.sanitize // NOTE: make keep this as the last command in the pipe for safety!
// )(content);

// Initialize the markdown renderer with pre- and post-processing pipes
const MARKDOWN_RENDER = marked.use({
hooks: {
preprocess,
postprocess,
},
});
// const MARKDOWN_RENDER = (content) => marked.use({
// hooks: {
// preprocess,
// postprocess,
// },
// }).parse(content);

const MARKDOWN_RENDER = (content) => md.render(content);

// export the function using our customized markdown parser only
export const renderContent = (content) => MARKDOWN_RENDER.parse(content);
export const renderContent = (content) => MARKDOWN_RENDER(content);
Loading

0 comments on commit 0da7f16

Please sign in to comment.