Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-06-14 09:09:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-06-14 09:09:22 +0300
commit067b3d04573d1473dbc6c81ef775d70c6636ff3f (patch)
treee05bfa986e49c1527fa93f03b2592f0c1a3da735 /app/assets/javascripts/content_editor
parentef9eff8e7e1b38f48a354f90ecaeeb67da35b08c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/content_editor')
-rw-r--r--app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js2
-rw-r--r--app/assets/javascripts/content_editor/services/serialization_helpers.js97
2 files changed, 76 insertions, 23 deletions
diff --git a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
index f38e4514393..2c462cdde91 100644
--- a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
+++ b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js
@@ -63,7 +63,7 @@ function maybeMerge(a, b) {
function createSourceMapAttributes(hastNode, source) {
const { position } = hastNode;
- return position.end
+ return position && position.end
? {
sourceMapKey: `${position.start.offset}:${position.end.offset}`,
sourceMarkdown: source.substring(position.start.offset, position.end.offset),
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index 055a32420b2..88f5192af77 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -12,22 +12,6 @@ const ignoreAttrs = {
const tableMap = new WeakMap();
-// Source taken from
-// prosemirror-markdown/src/to_markdown.js
-export function isPlainURL(link, parent, index, side) {
- if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) return false;
- const content = parent.child(index + (side < 0 ? -1 : 0));
- if (
- !content.isText ||
- content.text !== link.attrs.href ||
- content.marks[content.marks.length - 1] !== link
- )
- return false;
- if (index === (side < 0 ? 1 : parent.childCount - 1)) return true;
- const next = parent.child(index + (side < 0 ? -2 : 1));
- return !link.isInSet(next.marks);
-}
-
function containsOnlyText(node) {
if (node.childCount === 1) {
const child = node.child(0);
@@ -498,10 +482,79 @@ const linkType = (sourceMarkdown) => {
return LINK_HTML;
};
+const removeUrlProtocol = (url) => url.replace(/^\w+:\/?\/?/, '');
+
+const normalizeUrl = (url) => decodeURIComponent(removeUrlProtocol(url));
+
+/**
+ * Validates that the provided URL is well-formed
+ *
+ * @param {String} url
+ * @returns Returns true when the browser’s URL constructor
+ * can successfully parse the URL string
+ */
+const isValidUrl = (url) => {
+ try {
+ return new URL(url) && true;
+ } catch {
+ return false;
+ }
+};
+
+const findChildWithMark = (mark, parent) => {
+ let child;
+ let offset;
+ let index;
+
+ parent.forEach((_child, _offset, _index) => {
+ if (mark.isInSet(_child.marks)) {
+ child = _child;
+ offset = _offset;
+ index = _index;
+ }
+ });
+
+ return child ? { child, offset, index } : null;
+};
+
+/**
+ * This function detects whether a link should be serialized
+ * as an autolink.
+ *
+ * See https://github.github.com/gfm/#autolinks-extension-
+ * to understand the parsing rules of autolinks.
+ * */
+const isAutoLink = (linkMark, parent) => {
+ const { title, href } = linkMark.attrs;
+
+ if (title || !/^\w+:/.test(href)) {
+ return false;
+ }
+
+ const { child } = findChildWithMark(linkMark, parent);
+
+ if (
+ !child ||
+ !child.isText ||
+ !isValidUrl(href) ||
+ normalizeUrl(child.text) !== normalizeUrl(href)
+ ) {
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Returns true if the user used brackets to the define
+ * the autolink in the original markdown source
+ */
+const isBracketAutoLink = (sourceMarkdown) => /^<.+?>$/.test(sourceMarkdown);
+
export const link = {
- open(state, mark, parent, index) {
- if (isPlainURL(mark, parent, index, 1)) {
- return '<';
+ open(state, mark, parent) {
+ if (isAutoLink(mark, parent)) {
+ return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '<' : '';
}
const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs;
@@ -518,9 +571,9 @@ export const link = {
return openTag('a', attrs);
},
- close(state, mark, parent, index) {
- if (isPlainURL(mark, parent, index, -1)) {
- return '>';
+ close(state, mark, parent) {
+ if (isAutoLink(mark, parent)) {
+ return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '>' : '';
}
const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs;