Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,19 @@
import '../mathquill/mathquill.js';
import 'codemirror/lib/codemirror.css';
import '@toast-ui/editor/dist/toastui-editor.css';
import * as Showdown from 'showdown';

import Editor from '@toast-ui/editor';
import { stripHtml } from 'string-strip-html';

import imageUpload, { paramsToImageFieldHTML } from '../plugins/image-upload';
import formulas from '../plugins/formulas';
import minimize from '../plugins/minimize';
import formulaHtmlToMd from '../plugins/formulas/formula-html-to-md';
import formulaMdToHtml from '../plugins/formulas/formula-md-to-html';
import imagesHtmlToMd from '../plugins/image-upload/image-html-to-md';
import imagesMdToHtml from '../plugins/image-upload/image-md-to-html';

import { CLASS_MATH_FIELD_ACTIVE } from '../constants';
import { registerMarkdownFormulaField } from '../plugins/formulas/MarkdownFormulaField';
import { registerMarkdownImageField } from '../plugins/image-upload/MarkdownImageField';
import { clearNodeFormat, getExtensionMenuPosition } from './utils';
import { clearNodeFormat, generateCustomConverter, getExtensionMenuPosition } from './utils';
import FormulasMenu from './FormulasMenu/FormulasMenu';
import ImagesMenu from './ImagesMenu/ImagesMenu';
import ClickOutside from 'shared/directives/click-outside';
Expand Down Expand Up @@ -167,38 +163,7 @@
mounted() {
this.mathQuill = MathQuill.getInterface(2);

// This is currently the only way of inheriting and adjusting
// default TUI's convertor methods
// see https://github.com/nhn/tui.editor/issues/615
const tmpEditor = new Editor({
el: this.$refs.editor,
});
const showdown = new Showdown.Converter();
const Convertor = tmpEditor.convertor.constructor;
class CustomConvertor extends Convertor {
toMarkdown(content) {
content = imagesHtmlToMd(content);
content = formulaHtmlToMd(content);
content = showdown.makeMarkdown(content);
// TUI.editor sprinkles in extra `<br>` tags that Kolibri renders literally
// When showdown has already added linebreaks to render these in markdown
// so we just remove these here.
content = content.replaceAll('<br>', '');

// any copy pasted rich text that renders as HTML but does not get converted
// will linger here, so remove it as Kolibri will render it literally also.
content = stripHtml(content).result;
return content;
}
toHTML(content) {
// Kolibri and showdown assume double newlines for a single line break,
// wheras TUI.editor prefers single newline characters.
content = content.replaceAll('\n\n', '\n');
content = super.toHTML(content);
return content;
}
}
tmpEditor.remove();
const CustomConvertor = generateCustomConverter(this.$refs.editor);

const createBoldButton = () => {
{
Expand Down Expand Up @@ -268,8 +233,8 @@
// https://github.com/nhn/tui.editor/blob/master/apps/editor/docs/custom-html-renderer.md
customHTMLRenderer: {
text(node) {
let content = formulaMdToHtml(node.literal);
content = imagesMdToHtml(content);
let content = formulaMdToHtml(node.literal, true);
content = imagesMdToHtml(content, true);
return {
type: 'html',
content,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import * as Showdown from 'showdown';
import Editor from '@toast-ui/editor';
import { stripHtml } from 'string-strip-html';

import imagesHtmlToMd from '../plugins/image-upload/image-html-to-md';
import formulaHtmlToMd from '../plugins/formulas/formula-html-to-md';

/**
* Clear DOM node by keeping only its text content.
*
Expand Down Expand Up @@ -74,3 +81,37 @@ export const getExtensionMenuPosition = ({ editorEl, targetX, targetY }) => {
right: menuRight,
};
};

export const generateCustomConverter = el => {
// This is currently the only way of inheriting and adjusting
// default TUI's convertor methods
// see https://github.com/nhn/tui.editor/issues/615
const tmpEditor = new Editor({ el });
const showdown = new Showdown.Converter();
const Convertor = tmpEditor.convertor.constructor;
class CustomConvertor extends Convertor {
toMarkdown(content) {
content = showdown.makeMarkdown(content);
content = imagesHtmlToMd(content);
content = formulaHtmlToMd(content);
// TUI.editor sprinkles in extra `<br>` tags that Kolibri renders literally
// When showdown has already added linebreaks to render these in markdown
// so we just remove these here.
content = content.replaceAll('<br>', '');

// any copy pasted rich text that renders as HTML but does not get converted
// will linger here, so remove it as Kolibri will render it literally also.
content = stripHtml(content).result;
return content;
}
toHTML(content) {
// Kolibri and showdown assume double newlines for a single line break,
// wheras TUI.editor prefers single newline characters.
content = content.replaceAll('\n\n', '\n');
content = super.toHTML(content);
return content;
}
}
tmpEditor.remove();
return CustomConvertor;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { clearNodeFormat } from '../MarkdownEditor/utils';
/**
* @jest-environment jest-environment-jsdom-sixteen
*/
// Jsdom@^16 is required to test toast UI, as it relies on the Range API.

import { clearNodeFormat, generateCustomConverter } from '../MarkdownEditor/utils';

const htmlStringToFragment = htmlString => {
const template = document.createElement('template');
Expand Down Expand Up @@ -56,3 +61,17 @@ describe('clearNodeFormat', () => {
);
});
});

describe('markdown conversion', () => {
it('converts image tags to markdown without escaping them', () => {
const el = document.createElement('div');
const CustomConvertor = generateCustomConverter(el);
const converter = new CustomConvertor();
const html =
'<span is="markdown-image-field" vce-ready="" contenteditable="false" class="markdown-field-753aa86a-8159-403b-8b1c-d2b8f9504408 markdown-field-34843d46-79b8-40b4-866c-b83dc8916a47" editing="true" markdown="![](${☣ CONTENTSTORAGE}/bc1c5a86e1e46f20a6b4ee2c1bb6d6ff.png =485.453125x394)">![](${☣ CONTENTSTORAGE}/bc1c5a86e1e46f20a6b4ee2c1bb6d6ff.png =485.453125x394)</span>';

expect(converter.toMarkdown(html)).toBe(
'![](${☣ CONTENTSTORAGE}/bc1c5a86e1e46f20a6b4ee2c1bb6d6ff.png =485.453125x394)'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
*
*/

export default markdown => {
return markdown.replace(/\$\$(.*?)\$\$/g, '<span is="markdown-formula-field">$1</span>');
export default (markdown, editing) => {
const editAttr = editing ? ' editing="true"' : '';
return markdown.replace(
/\$\$(.*?)\$\$/g,
`<span is="markdown-formula-field"${editAttr}>$1</span>`
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ import { IMAGE_REGEX, imageMdToImageFieldHTML } from './index';

// convert markdown images to image editor field custom elements

export default markdown => {
return markdown.replace(IMAGE_REGEX, imageMd => imageMdToImageFieldHTML(imageMd));
export default (markdown, editing) => {
return markdown.replace(IMAGE_REGEX, imageMd => imageMdToImageFieldHTML(imageMd, editing));
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ export const paramsToImageMd = ({ src, alt, width, height }) => {
}
};

export const imageMdToImageFieldHTML = imageMd =>
`<span is='markdown-image-field'>${imageMd}</span>`;
export const imageMdToImageFieldHTML = (imageMd, editing) => {
const editAttr = editing ? ' editing="true"' : '';
return `<span is='markdown-image-field'${editAttr}>${imageMd}</span>`;
};
export const paramsToImageFieldHTML = params => imageMdToImageFieldHTML(paramsToImageMd(params));

export default imageUploadExtension;
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ export default VueComponent => {
''
);
}
this.parentNode.removeChild(this);
if (this.parentNode) {
this.parentNode.removeChild(this);
}
});

this.editing = true;

if (!hasLeftwardSpace(this)) {
this.insertAdjacentText('beforebegin', '\xa0');
}
Expand Down