debian-mirror-gitlab/app/assets/javascripts/notebook/cells/markdown.vue

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

196 lines
5 KiB
Vue
Raw Normal View History

2017-08-17 22:00:37 +05:30
<script>
2020-01-01 13:55:28 +05:30
import katex from 'katex';
2022-08-27 11:52:29 +05:30
import { marked } from 'marked';
2021-11-18 22:05:49 +05:30
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
2021-01-03 14:25:43 +05:30
import { sanitize } from '~/lib/dompurify';
2021-09-30 23:02:18 +05:30
import { hasContent, markdownConfig } from '~/lib/utils/text_utility';
2018-12-13 13:39:08 +05:30
import Prompt from './prompt.vue';
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
const renderer = new marked.Renderer();
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
/*
2017-08-17 22:00:37 +05:30
Regex to match KaTex blocks.
Supports the following:
\begin{equation}<math>\end{equation}
$$<math>$$
inline $<math>$
The matched text then goes through the KaTex renderer & then outputs the HTML
*/
2018-12-13 13:39:08 +05:30
const katexRegexString = `(
2017-08-17 22:00:37 +05:30
^\\\\begin{[a-zA-Z]+}\\s
|
^\\$\\$
|
\\s\\$(?!\\$)
)
2017-09-10 17:25:29 +05:30
((.|\\n)+?)
2017-08-17 22:00:37 +05:30
(
\\s\\\\end{[a-zA-Z]+}$
|
\\$\\$$
|
\\$
)
2018-12-13 13:39:08 +05:30
`
.replace(/\s/g, '')
.trim();
2017-08-17 22:00:37 +05:30
2021-04-29 21:17:54 +05:30
function deHTMLify(t) {
// get some specific characters back, that are allowed for KaTex rendering
const text = t.replace(/&#39;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>');
return text;
}
2020-05-24 23:13:21 +05:30
function renderKatex(t) {
2018-12-13 13:39:08 +05:30
let text = t;
2020-05-24 23:13:21 +05:30
let numInline = 0; // number of successfull converted math formulas
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
if (typeof katex !== 'undefined') {
const katexString = text
.replace(/&amp;/g, '&')
2020-04-22 19:07:51 +05:30
.replace(/&=&/g, '\\space=\\space') // eslint-disable-line @gitlab/require-i18n-strings
2018-12-13 13:39:08 +05:30
.replace(/<(\/?)em>/g, '_');
const regex = new RegExp(katexRegexString, 'gi');
const matchLocation = katexString.search(regex);
const numberOfMatches = katexString.match(regex);
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
if (numberOfMatches && numberOfMatches.length !== 0) {
2020-05-24 23:13:21 +05:30
let matches = regex.exec(katexString);
2018-12-13 13:39:08 +05:30
if (matchLocation > 0) {
2020-05-24 23:13:21 +05:30
numInline += 1;
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
while (matches !== null) {
2020-05-24 23:13:21 +05:30
try {
2021-04-29 21:17:54 +05:30
const renderedKatex = katex.renderToString(deHTMLify(matches[0].replace(/\$/g, '')));
2020-05-24 23:13:21 +05:30
text = `${text.replace(matches[0], ` ${renderedKatex}`)}`;
} catch {
numInline -= 1;
}
2018-12-13 13:39:08 +05:30
matches = regex.exec(katexString);
2017-08-17 22:00:37 +05:30
}
2018-12-13 13:39:08 +05:30
} else {
2020-05-24 23:13:21 +05:30
try {
2021-04-29 21:17:54 +05:30
text = katex.renderToString(deHTMLify(matches[2]));
2020-05-24 23:13:21 +05:30
} catch (error) {
numInline -= 1;
}
2017-08-17 22:00:37 +05:30
}
}
2018-12-13 13:39:08 +05:30
}
2020-05-24 23:13:21 +05:30
return [text, numInline > 0];
}
2021-03-08 18:12:59 +05:30
renderer.paragraph = (t) => {
2020-05-24 23:13:21 +05:30
const [text, inline] = renderKatex(t);
2018-12-13 13:39:08 +05:30
return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
};
2021-03-08 18:12:59 +05:30
renderer.listitem = (t) => {
2020-05-24 23:13:21 +05:30
const [text, inline] = renderKatex(t);
return `<li class="${inline ? 'inline-katex' : ''}">${text}</li>`;
};
2021-06-08 01:23:25 +05:30
renderer.originalImage = renderer.image;
renderer.image = function image(href, title, text) {
const attachmentHeader = `attachment:`; // eslint-disable-line @gitlab/require-i18n-strings
if (!this.attachments || !href.startsWith(attachmentHeader)) {
2021-11-11 11:23:49 +05:30
let relativeHref = href;
// eslint-disable-next-line @gitlab/require-i18n-strings
if (!(href.startsWith('http') || href.startsWith('data:'))) {
// These are images within the repo. This will only work if the image
// is relative to the path where the file is located
relativeHref = this.relativeRawPath + href;
}
return this.originalImage(relativeHref, title, text);
2021-06-08 01:23:25 +05:30
}
let img = ``;
const filename = href.substring(attachmentHeader.length);
if (hasContent(filename)) {
const attachment = this.attachments[filename];
if (attachment) {
const imageType = Object.keys(attachment)[0];
if (hasContent(imageType)) {
const data = attachment[imageType];
const inlined = `data:${imageType};base64,${data}"`; // eslint-disable-line @gitlab/require-i18n-strings
img = this.originalImage(inlined, title, text);
}
}
}
if (!hasContent(img)) {
return this.originalImage(href, title, text);
}
return sanitize(img);
};
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
marked.setOptions({
renderer,
});
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
export default {
components: {
2022-10-11 01:57:18 +05:30
Prompt,
2018-12-13 13:39:08 +05:30
},
2021-11-18 22:05:49 +05:30
directives: {
SafeHtml,
},
2021-11-11 11:23:49 +05:30
inject: ['relativeRawPath'],
2018-12-13 13:39:08 +05:30
props: {
cell: {
type: Object,
required: true,
2017-08-17 22:00:37 +05:30
},
2023-01-13 00:05:48 +05:30
hidePrompt: {
type: Boolean,
required: false,
default: false,
},
2018-12-13 13:39:08 +05:30
},
computed: {
markdown() {
2021-06-08 01:23:25 +05:30
renderer.attachments = this.cell.attachments;
2021-11-11 11:23:49 +05:30
renderer.relativeRawPath = this.relativeRawPath;
2021-06-08 01:23:25 +05:30
2021-11-18 22:05:49 +05:30
return marked(this.cell.source.join('').replace(/\\/g, '\\\\'));
2017-08-17 22:00:37 +05:30
},
2018-12-13 13:39:08 +05:30
},
2021-11-18 22:05:49 +05:30
markdownConfig,
2018-12-13 13:39:08 +05:30
};
2017-08-17 22:00:37 +05:30
</script>
2018-03-17 18:26:18 +05:30
<template>
<div class="cell text-cell">
2023-01-13 00:05:48 +05:30
<prompt v-if="!hidePrompt" />
2021-11-18 22:05:49 +05:30
<div v-safe-html:[$options.markdownConfig]="markdown" class="markdown"></div>
2018-03-17 18:26:18 +05:30
</div>
</template>
2017-08-17 22:00:37 +05:30
<style>
2020-01-01 13:55:28 +05:30
/*
Importing the necessary katex stylesheet from the node_module folder rather
than copying the stylesheet into `app/assets/stylesheets/vendors` for
automatic importing via `app/assets/stylesheets/application.scss`. The reason
is that the katex stylesheet depends on many fonts that are in node_module
subfolders - moving all these fonts would make updating katex difficult.
*/
@import '~katex/dist/katex.min.css';
2018-12-13 13:39:08 +05:30
.markdown .katex {
display: block;
text-align: center;
}
2017-08-17 22:00:37 +05:30
2018-12-13 13:39:08 +05:30
.markdown .inline-katex .katex {
display: inline;
text-align: initial;
}
2017-08-17 22:00:37 +05:30
</style>