@ckeditor/ckeditor5-markdown-gfm 47.6.1 → 48.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ckeditor5-metadata.json +1 -1
- package/{src → dist}/gfmdataprocessor.d.ts +1 -1
- package/dist/index.css +3 -0
- package/dist/index.css.map +1 -0
- package/{src → dist}/index.d.ts +1 -1
- package/dist/index.js +44 -14
- package/dist/index.js.map +1 -1
- package/{src → dist}/markdown.d.ts +1 -1
- package/dist/markdown2html/markdown2html.d.ts +34 -0
- package/{src → dist}/pastefrommarkdownexperimental.d.ts +2 -2
- package/package.json +22 -45
- package/build/markdown-gfm.js +0 -4
- package/src/augmentation.js +0 -5
- package/src/gfmdataprocessor.js +0 -86
- package/src/html2markdown/html2markdown.js +0 -97
- package/src/index.js +0 -13
- package/src/markdown.js +0 -35
- package/src/markdown2html/markdown2html.d.ts +0 -12
- package/src/markdown2html/markdown2html.js +0 -98
- package/src/pastefrommarkdownexperimental.js +0 -157
- /package/{src → dist}/augmentation.d.ts +0 -0
- /package/{src → dist}/html2markdown/html2markdown.d.ts +0 -0
package/src/gfmdataprocessor.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm/gfmdataprocessor
|
|
7
|
-
*/
|
|
8
|
-
import { HtmlDataProcessor } from 'ckeditor5/src/engine.js';
|
|
9
|
-
import { MarkdownGfmMdToHtml } from './markdown2html/markdown2html.js';
|
|
10
|
-
import { MarkdownGfmHtmlToMd } from './html2markdown/html2markdown.js';
|
|
11
|
-
/**
|
|
12
|
-
* This data processor implementation uses GitHub Flavored Markdown as input/output data.
|
|
13
|
-
*
|
|
14
|
-
* See the {@glink features/markdown Markdown output} guide to learn more on how to enable it.
|
|
15
|
-
*/
|
|
16
|
-
export class MarkdownGfmDataProcessor {
|
|
17
|
-
/**
|
|
18
|
-
* HTML data processor used to process HTML produced by the Markdown-to-HTML converter and the other way.
|
|
19
|
-
*/
|
|
20
|
-
_htmlDP;
|
|
21
|
-
/**
|
|
22
|
-
* Helper for converting Markdown to HTML.
|
|
23
|
-
*/
|
|
24
|
-
_markdown2html;
|
|
25
|
-
/**
|
|
26
|
-
* Helper for converting HTML to Markdown.
|
|
27
|
-
*/
|
|
28
|
-
_html2markdown;
|
|
29
|
-
/**
|
|
30
|
-
* Creates a new instance of the Markdown data processor class.
|
|
31
|
-
*/
|
|
32
|
-
constructor(document) {
|
|
33
|
-
this._htmlDP = new HtmlDataProcessor(document);
|
|
34
|
-
this._markdown2html = new MarkdownGfmMdToHtml();
|
|
35
|
-
this._html2markdown = new MarkdownGfmHtmlToMd();
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Keeps the specified element in the output as HTML. This is useful if the editor contains
|
|
39
|
-
* features producing HTML that is not a part of the Markdown standard.
|
|
40
|
-
*
|
|
41
|
-
* By default, all HTML tags are removed.
|
|
42
|
-
*
|
|
43
|
-
* @param element The element name to be kept.
|
|
44
|
-
*/
|
|
45
|
-
keepHtml(element) {
|
|
46
|
-
this._html2markdown.keep(element);
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Converts the provided Markdown string to a view tree.
|
|
50
|
-
*
|
|
51
|
-
* @param data A Markdown string.
|
|
52
|
-
* @returns The converted view element.
|
|
53
|
-
*/
|
|
54
|
-
toView(data) {
|
|
55
|
-
const html = this._markdown2html.parse(data);
|
|
56
|
-
return this._htmlDP.toView(html);
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Converts the provided {@link module:engine/view/documentfragment~ViewDocumentFragment} to data format – in this
|
|
60
|
-
* case to a Markdown string.
|
|
61
|
-
*
|
|
62
|
-
* @returns Markdown string.
|
|
63
|
-
*/
|
|
64
|
-
toData(viewFragment) {
|
|
65
|
-
const html = this._htmlDP.toData(viewFragment);
|
|
66
|
-
return this._html2markdown.parse(html);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Registers a {@link module:engine/view/matcher~MatcherPattern} for view elements whose content should be treated as raw data
|
|
70
|
-
* and not processed during the conversion from Markdown to view elements.
|
|
71
|
-
*
|
|
72
|
-
* The raw data can be later accessed by a
|
|
73
|
-
* {@link module:engine/view/element~ViewElement#getCustomProperty custom property of a view element} called `"$rawContent"`.
|
|
74
|
-
*
|
|
75
|
-
* @param pattern The pattern matching all view elements whose content should
|
|
76
|
-
* be treated as raw data.
|
|
77
|
-
*/
|
|
78
|
-
registerRawContentMatcher(pattern) {
|
|
79
|
-
this._htmlDP.registerRawContentMatcher(pattern);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* This method does not have any effect on the data processor result. It exists for compatibility with the
|
|
83
|
-
* {@link module:engine/dataprocessor/dataprocessor~DataProcessor `DataProcessor` interface}.
|
|
84
|
-
*/
|
|
85
|
-
useFillerType() { }
|
|
86
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm/html2markdown/html2markdown
|
|
7
|
-
*/
|
|
8
|
-
import { unified } from 'unified';
|
|
9
|
-
import rehypeParse from 'rehype-dom-parse';
|
|
10
|
-
import rehypeRemark from 'rehype-remark';
|
|
11
|
-
import remarkBreaks from 'remark-breaks';
|
|
12
|
-
import remarkGfm from 'remark-gfm';
|
|
13
|
-
import remarkStringify from 'remark-stringify';
|
|
14
|
-
import { visit } from 'unist-util-visit';
|
|
15
|
-
import { h } from 'hastscript';
|
|
16
|
-
import { toHtml } from 'hast-util-to-html';
|
|
17
|
-
export class MarkdownGfmHtmlToMd {
|
|
18
|
-
_processor;
|
|
19
|
-
_keepRawTags = [];
|
|
20
|
-
constructor() {
|
|
21
|
-
this._buildProcessor();
|
|
22
|
-
}
|
|
23
|
-
keep(tagName) {
|
|
24
|
-
this._keepRawTags.push(tagName.toLowerCase());
|
|
25
|
-
this._buildProcessor();
|
|
26
|
-
}
|
|
27
|
-
parse(html) {
|
|
28
|
-
return this._processor
|
|
29
|
-
.processSync(html)
|
|
30
|
-
.toString()
|
|
31
|
-
.trim();
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Returns handlers for raw HTML tags that should be kept in the Markdown output.
|
|
35
|
-
*/
|
|
36
|
-
_getRawTagsHandlers() {
|
|
37
|
-
return this._keepRawTags.reduce((handlers, tagName) => {
|
|
38
|
-
handlers[tagName] = (state, node) => {
|
|
39
|
-
const tag = toHtml(h(node.tagName, node.properties), {
|
|
40
|
-
allowDangerousHtml: true,
|
|
41
|
-
closeSelfClosing: true
|
|
42
|
-
});
|
|
43
|
-
const endOfOpeningTagIndex = tag.indexOf('>');
|
|
44
|
-
const openingTag = tag.slice(0, endOfOpeningTagIndex + 1);
|
|
45
|
-
const closingTag = tag.slice(endOfOpeningTagIndex + 1);
|
|
46
|
-
return [
|
|
47
|
-
{ type: 'html', value: openingTag },
|
|
48
|
-
...state.all(node),
|
|
49
|
-
{ type: 'html', value: closingTag }
|
|
50
|
-
];
|
|
51
|
-
};
|
|
52
|
-
return handlers;
|
|
53
|
-
}, {});
|
|
54
|
-
}
|
|
55
|
-
_buildProcessor() {
|
|
56
|
-
this._processor = unified()
|
|
57
|
-
// Parse HTML to an abstract syntax tree (AST).
|
|
58
|
-
.use(rehypeParse)
|
|
59
|
-
// Removes `<label>` element from TODO lists.
|
|
60
|
-
.use(removeLabelFromCheckboxes)
|
|
61
|
-
// Turns HTML syntax tree into Markdown syntax tree.
|
|
62
|
-
.use(rehypeRemark, {
|
|
63
|
-
// Keeps allowed HTML tags.
|
|
64
|
-
handlers: this._getRawTagsHandlers()
|
|
65
|
-
})
|
|
66
|
-
// Adds support for GitHub Flavored Markdown (GFM).
|
|
67
|
-
.use(remarkGfm, {
|
|
68
|
-
singleTilde: true
|
|
69
|
-
})
|
|
70
|
-
// Replaces line breaks with `<br>` tags.
|
|
71
|
-
.use(remarkBreaks)
|
|
72
|
-
// Serializes Markdown syntax tree to Markdown string.
|
|
73
|
-
.use(remarkStringify, {
|
|
74
|
-
resourceLink: true,
|
|
75
|
-
emphasis: '_',
|
|
76
|
-
rule: '-',
|
|
77
|
-
handlers: {
|
|
78
|
-
break: () => '\n'
|
|
79
|
-
},
|
|
80
|
-
unsafe: [
|
|
81
|
-
{ character: '<' }
|
|
82
|
-
]
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Removes `<label>` element from TODO lists, so that `<input>` and `text` are direct children of `<li>`.
|
|
88
|
-
*/
|
|
89
|
-
function removeLabelFromCheckboxes() {
|
|
90
|
-
return function (tree) {
|
|
91
|
-
visit(tree, 'element', (node, index, parent) => {
|
|
92
|
-
if (index !== null && node.tagName === 'label' && parent.type === 'element' && parent.tagName === 'li') {
|
|
93
|
-
parent.children.splice(index, 1, ...node.children);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
};
|
|
97
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm
|
|
7
|
-
*/
|
|
8
|
-
export { Markdown } from './markdown.js';
|
|
9
|
-
export { PasteFromMarkdownExperimental } from './pastefrommarkdownexperimental.js';
|
|
10
|
-
export { MarkdownGfmDataProcessor } from './gfmdataprocessor.js';
|
|
11
|
-
export { MarkdownGfmMdToHtml } from './markdown2html/markdown2html.js';
|
|
12
|
-
export { MarkdownGfmHtmlToMd } from './html2markdown/html2markdown.js';
|
|
13
|
-
import './augmentation.js';
|
package/src/markdown.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm/markdown
|
|
7
|
-
*/
|
|
8
|
-
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
-
import { MarkdownGfmDataProcessor } from './gfmdataprocessor.js';
|
|
10
|
-
/**
|
|
11
|
-
* The GitHub Flavored Markdown (GFM) plugin.
|
|
12
|
-
*
|
|
13
|
-
* For a detailed overview, check the {@glink features/markdown Markdown feature} guide.
|
|
14
|
-
*/
|
|
15
|
-
export class Markdown extends Plugin {
|
|
16
|
-
/**
|
|
17
|
-
* @inheritDoc
|
|
18
|
-
*/
|
|
19
|
-
constructor(editor) {
|
|
20
|
-
super(editor);
|
|
21
|
-
editor.data.processor = new MarkdownGfmDataProcessor(editor.data.viewDocument);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* @inheritDoc
|
|
25
|
-
*/
|
|
26
|
-
static get pluginName() {
|
|
27
|
-
return 'Markdown';
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* @inheritDoc
|
|
31
|
-
*/
|
|
32
|
-
static get isOfficialPlugin() {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* This is a helper class used by the {@link module:markdown-gfm/markdown Markdown feature} to convert Markdown to HTML.
|
|
7
|
-
*/
|
|
8
|
-
export declare class MarkdownGfmMdToHtml {
|
|
9
|
-
private _processor;
|
|
10
|
-
constructor();
|
|
11
|
-
parse(markdown: string): string;
|
|
12
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm/markdown2html/markdown2html
|
|
7
|
-
*/
|
|
8
|
-
import { unified } from 'unified';
|
|
9
|
-
import remarkGfm from 'remark-gfm';
|
|
10
|
-
import remarkParse from 'remark-parse';
|
|
11
|
-
import remarkRehype from 'remark-rehype';
|
|
12
|
-
import remarkBreaks from 'remark-breaks';
|
|
13
|
-
import rehypeStringify from 'rehype-dom-stringify';
|
|
14
|
-
import { visit } from 'unist-util-visit';
|
|
15
|
-
import { toHtml } from 'hast-util-to-html';
|
|
16
|
-
import { fromDom } from 'hast-util-from-dom';
|
|
17
|
-
/**
|
|
18
|
-
* This is a helper class used by the {@link module:markdown-gfm/markdown Markdown feature} to convert Markdown to HTML.
|
|
19
|
-
*/
|
|
20
|
-
export class MarkdownGfmMdToHtml {
|
|
21
|
-
_processor;
|
|
22
|
-
constructor() {
|
|
23
|
-
this._processor = unified()
|
|
24
|
-
// Parses Markdown to an abstract syntax tree (AST).
|
|
25
|
-
.use(remarkParse)
|
|
26
|
-
// Adds support for GitHub Flavored Markdown (GFM).
|
|
27
|
-
.use(remarkGfm, { singleTilde: true })
|
|
28
|
-
// Replaces line breaks with `<br>` tags.
|
|
29
|
-
.use(remarkBreaks)
|
|
30
|
-
// Turns markdown syntax tree to HTML syntax tree, ignoring embedded HTML.
|
|
31
|
-
.use(remarkRehype, { allowDangerousHtml: true })
|
|
32
|
-
// Handles HTML embedded in Markdown.
|
|
33
|
-
.use(rehypeDomRaw)
|
|
34
|
-
// Removes classes from list elements.
|
|
35
|
-
.use(deleteClassesFromToDoLists)
|
|
36
|
-
// Serializes HTML syntax tree to HTML string.
|
|
37
|
-
.use(rehypeStringify);
|
|
38
|
-
}
|
|
39
|
-
parse(markdown) {
|
|
40
|
-
return this._processor
|
|
41
|
-
.processSync(markdown)
|
|
42
|
-
.toString()
|
|
43
|
-
.replaceAll('\n</code>', '</code>');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Rehype plugin that improves handling of the To-do lists by removing:
|
|
48
|
-
* * default classes added to `<ul>`, `<ol>`, and `<li>` elements.
|
|
49
|
-
* * bogus space after <input type="checkbox"> because it would be preserved by ViewDomConverter as it's next to an inline object.
|
|
50
|
-
*/
|
|
51
|
-
function deleteClassesFromToDoLists() {
|
|
52
|
-
return (tree) => {
|
|
53
|
-
visit(tree, 'element', (node) => {
|
|
54
|
-
if (node.tagName === 'ul' || node.tagName === 'ol' || node.tagName === 'li') {
|
|
55
|
-
node.children = node.children.filter(child => child.type !== 'text' || !!child.value.trim());
|
|
56
|
-
delete node.properties.className;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Rehype plugin to parse raw HTML nodes inside Markdown. This plugin is used instead of `rehype-raw` or `rehype-stringify`,
|
|
63
|
-
* because those plugins rely on `parse5` DOM parser which is heavy and redundant in the browser environment where we can
|
|
64
|
-
* use the native DOM APIs.
|
|
65
|
-
*
|
|
66
|
-
* This plugins finds any node (root or element) whose children include `raw` nodes and reparses them like so:
|
|
67
|
-
* 1. Serializes its children to an HTML string.
|
|
68
|
-
* 2. Reparses the HTML string using a `<template>` element.
|
|
69
|
-
* 3. Converts each parsed DOM node back into HAST nodes.
|
|
70
|
-
* 4. Replaces the original children with the newly created HAST nodes.
|
|
71
|
-
*/
|
|
72
|
-
function rehypeDomRaw() {
|
|
73
|
-
return (tree) => {
|
|
74
|
-
visit(tree, ['root', 'element'], (node) => {
|
|
75
|
-
/* istanbul ignore next -- @preserve */
|
|
76
|
-
if (!isNodeRootOrElement(node)) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// Only act on nodes with at least one raw child.
|
|
80
|
-
if (!node.children.some(child => child.type === 'raw')) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const template = document.createElement('template');
|
|
84
|
-
// Serialize all children to an HTML fragment.
|
|
85
|
-
template.innerHTML = toHtml({ type: 'root', children: node.children }, { allowDangerousHtml: true });
|
|
86
|
-
// Convert each parsed DOM node back into HAST and replace the original children.
|
|
87
|
-
node.children = Array
|
|
88
|
-
.from(template.content.childNodes)
|
|
89
|
-
.map(domNode => fromDom(domNode));
|
|
90
|
-
});
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Only needed for the type guard.
|
|
95
|
-
*/
|
|
96
|
-
function isNodeRootOrElement(node) {
|
|
97
|
-
return (node.type === 'root' || node.type === 'element') && node.children;
|
|
98
|
-
}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
-
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @module markdown-gfm/pastefrommarkdownexperimental
|
|
7
|
-
*/
|
|
8
|
-
import { Plugin } from 'ckeditor5/src/core.js';
|
|
9
|
-
import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
|
|
10
|
-
import { MarkdownGfmDataProcessor } from './gfmdataprocessor.js';
|
|
11
|
-
const ALLOWED_MARKDOWN_FIRST_LEVEL_TAGS = ['SPAN', 'BR', 'PRE', 'CODE'];
|
|
12
|
-
/**
|
|
13
|
-
* The GitHub Flavored Markdown (GFM) paste plugin.
|
|
14
|
-
*
|
|
15
|
-
* For a detailed overview, check the {@glink features/pasting/paste-markdown Paste Markdown feature} guide.
|
|
16
|
-
*/
|
|
17
|
-
export class PasteFromMarkdownExperimental extends Plugin {
|
|
18
|
-
/**
|
|
19
|
-
* @internal
|
|
20
|
-
*/
|
|
21
|
-
_gfmDataProcessor;
|
|
22
|
-
/**
|
|
23
|
-
* @inheritDoc
|
|
24
|
-
*/
|
|
25
|
-
constructor(editor) {
|
|
26
|
-
super(editor);
|
|
27
|
-
this._gfmDataProcessor = new MarkdownGfmDataProcessor(editor.data.viewDocument);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* @inheritDoc
|
|
31
|
-
*/
|
|
32
|
-
static get pluginName() {
|
|
33
|
-
return 'PasteFromMarkdownExperimental';
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* @inheritDoc
|
|
37
|
-
*/
|
|
38
|
-
static get isOfficialPlugin() {
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* @inheritDoc
|
|
43
|
-
*/
|
|
44
|
-
static get requires() {
|
|
45
|
-
return [ClipboardPipeline];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* @inheritDoc
|
|
49
|
-
*/
|
|
50
|
-
init() {
|
|
51
|
-
const editor = this.editor;
|
|
52
|
-
const view = editor.editing.view;
|
|
53
|
-
const viewDocument = view.document;
|
|
54
|
-
const clipboardPipeline = editor.plugins.get('ClipboardPipeline');
|
|
55
|
-
let shiftPressed = false;
|
|
56
|
-
this.listenTo(viewDocument, 'keydown', (evt, data) => {
|
|
57
|
-
shiftPressed = data.shiftKey;
|
|
58
|
-
});
|
|
59
|
-
this.listenTo(clipboardPipeline, 'inputTransformation', (evt, data) => {
|
|
60
|
-
if (shiftPressed) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const dataAsTextHtml = data.dataTransfer.getData('text/html');
|
|
64
|
-
if (!dataAsTextHtml) {
|
|
65
|
-
const dataAsTextPlain = data.dataTransfer.getData('text/plain');
|
|
66
|
-
data.content = this._gfmDataProcessor.toView(dataAsTextPlain);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const markdownFromHtml = this._parseMarkdownFromHtml(dataAsTextHtml);
|
|
70
|
-
if (markdownFromHtml) {
|
|
71
|
-
data.content = this._gfmDataProcessor.toView(markdownFromHtml);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Determines if the code copied from a website in the `text/html` type can be parsed as Markdown.
|
|
77
|
-
* It removes any OS-specific HTML tags, for example, <meta> on macOS and <!--StartFragment--> on Windows.
|
|
78
|
-
* Then removes a single wrapper HTML tag or wrappers for sibling tags, and if there are no more tags left,
|
|
79
|
-
* returns the remaining text. Returns null if there are any remaining HTML tags detected.
|
|
80
|
-
*
|
|
81
|
-
* @param htmlString Clipboard content in the `text/html` type format.
|
|
82
|
-
*/
|
|
83
|
-
_parseMarkdownFromHtml(htmlString) {
|
|
84
|
-
const withoutOsSpecificTags = this._removeOsSpecificTags(htmlString);
|
|
85
|
-
if (!this._containsOnlyAllowedFirstLevelTags(withoutOsSpecificTags)) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
const withoutWrapperTag = this._removeFirstLevelWrapperTagsAndBrs(withoutOsSpecificTags);
|
|
89
|
-
if (this._containsAnyRemainingHtmlTags(withoutWrapperTag)) {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
return this._replaceHtmlReservedEntitiesWithCharacters(withoutWrapperTag);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Removes OS-specific tags.
|
|
96
|
-
*
|
|
97
|
-
* @param htmlString Clipboard content in the `text/html` type format.
|
|
98
|
-
*/
|
|
99
|
-
_removeOsSpecificTags(htmlString) {
|
|
100
|
-
// Removing the <meta> tag present on Mac.
|
|
101
|
-
const withoutMetaTag = htmlString.replace(/^<meta\b[^>]*>/, '').trim();
|
|
102
|
-
// Removing the <html> tag present on Windows.
|
|
103
|
-
const withoutHtmlTag = withoutMetaTag.replace(/^<html>/, '').replace(/<\/html>$/, '').trim();
|
|
104
|
-
// Removing the <body> tag present on Windows.
|
|
105
|
-
const withoutBodyTag = withoutHtmlTag.replace(/^<body>/, '').replace(/<\/body>$/, '').trim();
|
|
106
|
-
// Removing the <!--StartFragment--> tag present on Windows.
|
|
107
|
-
return withoutBodyTag.replace(/^<!--StartFragment-->/, '').replace(/<!--EndFragment-->$/, '').trim();
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* If the input HTML string contains any first-level formatting tags
|
|
111
|
-
* like <b>, <strong>, or <i>, we should not treat it as Markdown.
|
|
112
|
-
*
|
|
113
|
-
* @param htmlString Clipboard content.
|
|
114
|
-
*/
|
|
115
|
-
_containsOnlyAllowedFirstLevelTags(htmlString) {
|
|
116
|
-
const parser = new DOMParser();
|
|
117
|
-
const { body: tempElement } = parser.parseFromString(htmlString, 'text/html');
|
|
118
|
-
const tagNames = Array.from(tempElement.children).map(el => el.tagName);
|
|
119
|
-
return tagNames.every(el => ALLOWED_MARKDOWN_FIRST_LEVEL_TAGS.includes(el));
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Removes multiple HTML wrapper tags from a list of sibling HTML tags.
|
|
123
|
-
*
|
|
124
|
-
* @param htmlString Clipboard content without any OS-specific tags.
|
|
125
|
-
*/
|
|
126
|
-
_removeFirstLevelWrapperTagsAndBrs(htmlString) {
|
|
127
|
-
const parser = new DOMParser();
|
|
128
|
-
const { body: tempElement } = parser.parseFromString(htmlString, 'text/html');
|
|
129
|
-
const brElements = tempElement.querySelectorAll('br');
|
|
130
|
-
for (const br of brElements) {
|
|
131
|
-
br.replaceWith('\n');
|
|
132
|
-
}
|
|
133
|
-
const outerElements = tempElement.querySelectorAll(':scope > *');
|
|
134
|
-
for (const element of outerElements) {
|
|
135
|
-
const elementClone = element.cloneNode(true);
|
|
136
|
-
element.replaceWith(...elementClone.childNodes);
|
|
137
|
-
}
|
|
138
|
-
return tempElement.innerHTML;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Determines if a string contains any HTML tags.
|
|
142
|
-
*/
|
|
143
|
-
_containsAnyRemainingHtmlTags(str) {
|
|
144
|
-
return str.includes('<');
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Replaces the reserved HTML entities with the actual characters.
|
|
148
|
-
*
|
|
149
|
-
* @param htmlString Clipboard content without any tags.
|
|
150
|
-
*/
|
|
151
|
-
_replaceHtmlReservedEntitiesWithCharacters(htmlString) {
|
|
152
|
-
return htmlString
|
|
153
|
-
.replace(/>/g, '>')
|
|
154
|
-
.replace(/</g, '<')
|
|
155
|
-
.replace(/ /g, ' ');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
File without changes
|
|
File without changes
|