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>2020-08-10 18:09:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-10 18:09:49 +0300
commit70732753863e569f95ed954ca3c41421292f912b (patch)
treef642d44c83ab951fd4581e3c46491784376b9c18 /app/assets/javascripts/static_site_editor
parentbf593ae68b7135bf633484aa3442b7592126b1d2 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/static_site_editor')
-rw-r--r--app/assets/javascripts/static_site_editor/services/templater.js107
1 files changed, 67 insertions, 40 deletions
diff --git a/app/assets/javascripts/static_site_editor/services/templater.js b/app/assets/javascripts/static_site_editor/services/templater.js
index 081db60b601..a1c1bb6b8d6 100644
--- a/app/assets/javascripts/static_site_editor/services/templater.js
+++ b/app/assets/javascripts/static_site_editor/services/templater.js
@@ -1,62 +1,89 @@
+/**
+ * The purpose of this file is to modify Markdown source such that templated code (embedded ruby currently) can be temporarily wrapped and unwrapped in codeblocks:
+ * 1. `wrap()`: temporarily wrap in codeblocks (useful for a WYSIWYG editing experience)
+ * 2. `unwrap()`: undo the temporarily wrapped codeblocks (useful for Markdown editing experience and saving edits)
+ *
+ * Without this `templater`, the templated code is otherwise interpreted as Markdown content resulting in loss of spacing, indentation, escape characters, etc.
+ *
+ */
+
const ticks = '```';
const marker = 'sse';
-const prefix = `${ticks} ${marker}\n`; // Space intentional due to https://github.com/nhn/tui.editor/blob/6bcec75c69028570d93d973aa7533090257eaae0/libs/to-mark/src/renderer.gfm.js#L26
-const postfix = `\n${ticks}`;
-const flagPrefix = `${marker}-${Date.now()}`;
-const template = `.| |\\t|\\n(?!(\\n|${flagPrefix}))`;
-const templatedRegex = new RegExp(`(^${prefix}(${template})+?${postfix}$)`, 'gm');
-
-const nonErbMarkupRegex = new RegExp(`^((<(?!%).+>){1}(${template})+(</.+>){1})$`, 'gm');
-const embeddedRubyBlockRegex = new RegExp(`(^<%(${template})+%>$)`, 'gm');
-const embeddedRubyInlineRegex = new RegExp(`(^.*[<|&lt;]%(${template})+$)`, 'gm');
-
-// Order is intentional (general to specific) where HTML markup is flagged first, then ERB blocks, then inline ERB
-// Order in combo with the `flag()` algorithm is used to mitigate potential duplicate pattern matches (ERB nested in HTML for example)
-const orderedPatterns = [nonErbMarkupRegex, embeddedRubyBlockRegex, embeddedRubyInlineRegex];
-
-const unwrap = source => {
- let text = source;
- const matches = text.match(templatedRegex);
+const wrapPrefix = `${ticks} ${marker}\n`; // Space intentional due to https://github.com/nhn/tui.editor/blob/6bcec75c69028570d93d973aa7533090257eaae0/libs/to-mark/src/renderer.gfm.js#L26
+const wrapPostfix = `\n${ticks}`;
+const markPrefix = `${marker}-${Date.now()}`;
- if (matches) {
- matches.forEach(match => {
- const initial = match.replace(`${prefix}`, '').replace(`${postfix}`, '');
- text = text.replace(match, initial);
- });
- }
+const reHelpers = {
+ template: `.| |\\t|\\n(?!(\\n|${markPrefix}))`,
+ openTag: '<[a-zA-Z]+.*?>',
+ closeTag: '</.+>',
+};
+const reTemplated = new RegExp(`(^${wrapPrefix}(${reHelpers.template})+?${wrapPostfix}$)`, 'gm');
+const rePreexistingCodeBlocks = new RegExp(`(^${ticks}.*\\n(.|\\s)+?${ticks}$)`, 'gm');
+const reHtmlMarkup = new RegExp(
+ `^((${reHelpers.openTag}){1}(${reHelpers.template})*(${reHelpers.closeTag}){1})$`,
+ 'gm',
+);
+const reEmbeddedRubyBlock = new RegExp(`(^<%(${reHelpers.template})+%>$)`, 'gm');
+const reEmbeddedRubyInline = new RegExp(`(^.*[<|&lt;]%(${reHelpers.template})+$)`, 'gm');
- return text;
+const patternGroups = {
+ ignore: [rePreexistingCodeBlocks],
+ // Order is intentional (general to specific) where HTML markup is marked first, then ERB blocks, then inline ERB
+ // Order in combo with the `mark()` algorithm is used to mitigate potential duplicate pattern matches (ERB nested in HTML for example)
+ allow: [reHtmlMarkup, reEmbeddedRubyBlock, reEmbeddedRubyInline],
};
-const flag = (source, patterns) => {
+const mark = (source, groups) => {
let text = source;
let id = 0;
const hash = {};
- patterns.forEach(pattern => {
- const matches = text.match(pattern);
- if (matches) {
- matches.forEach(match => {
- const key = `${flagPrefix}${id}`;
- text = text.replace(match, key);
- hash[key] = match;
- id += 1;
- });
- }
+ Object.entries(groups).forEach(([groupKey, group]) => {
+ group.forEach(pattern => {
+ const matches = text.match(pattern);
+ if (matches) {
+ matches.forEach(match => {
+ const key = `${markPrefix}-${groupKey}-${id}`;
+ text = text.replace(match, key);
+ hash[key] = match;
+ id += 1;
+ });
+ }
+ });
});
return { text, hash };
};
-const wrap = source => {
- const { text, hash } = flag(unwrap(source), orderedPatterns);
+const unmark = (text, hash) => {
+ let source = text;
- let wrappedSource = text;
Object.entries(hash).forEach(([key, value]) => {
- wrappedSource = wrappedSource.replace(key, `${prefix}${value}${postfix}`);
+ const newVal = key.includes('ignore') ? value : `${wrapPrefix}${value}${wrapPostfix}`;
+ source = source.replace(key, newVal);
});
- return wrappedSource;
+ return source;
+};
+
+const unwrap = source => {
+ let text = source;
+ const matches = text.match(reTemplated);
+
+ if (matches) {
+ matches.forEach(match => {
+ const initial = match.replace(`${wrapPrefix}`, '').replace(`${wrapPostfix}`, '');
+ text = text.replace(match, initial);
+ });
+ }
+
+ return text;
+};
+
+const wrap = source => {
+ const { text, hash } = mark(unwrap(source), patternGroups);
+ return unmark(text, hash);
};
export default { wrap, unwrap };