diff options
Diffstat (limited to 'apps/settings/src/components/Markdown.vue')
-rw-r--r-- | apps/settings/src/components/Markdown.vue | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/apps/settings/src/components/Markdown.vue b/apps/settings/src/components/Markdown.vue new file mode 100644 index 00000000000..36535e46763 --- /dev/null +++ b/apps/settings/src/components/Markdown.vue @@ -0,0 +1,155 @@ +<!-- + - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> + +<template> + <!-- eslint-disable-next-line vue/no-v-html This is rendered markdown so should be "safe" --> + <div class="settings-markdown" v-html="renderMarkdown" /> +</template> + +<script> +import { marked } from 'marked' +import dompurify from 'dompurify' + +export default { + name: 'Markdown', + props: { + text: { + type: String, + default: '', + }, + minHeading: { + type: Number, + default: 1, + }, + }, + computed: { + renderMarkdown() { + const renderer = new marked.Renderer() + renderer.link = function({ href, title, text }) { + let prot + try { + prot = decodeURIComponent(unescape(href)) + .replace(/[^\w:]/g, '') + .toLowerCase() + } catch (e) { + return '' + } + + if (prot.indexOf('http:') !== 0 && prot.indexOf('https:') !== 0) { + return '' + } + + let out = '<a href="' + href + '" rel="noreferrer noopener"' + if (title) { + out += ' title="' + title + '"' + } + out += '>' + text + '</a>' + return out + } + renderer.heading = ({ text, depth }) => { + depth = Math.min(6, depth + (this.minHeading - 1)) + return `<h${depth}>${text}</h${depth}>` + } + renderer.image = ({ title, text }) => { + if (text) { + return text + } + return title + } + renderer.blockquote = ({ text }) => { + return `<blockquote>${text}</blockquote>` + } + return dompurify.sanitize( + marked(this.text.trim(), { + renderer, + gfm: false, + highlight: false, + tables: false, + breaks: false, + pedantic: false, + sanitize: true, + smartLists: true, + smartypants: false, + }), + { + SAFE_FOR_JQUERY: true, + ALLOWED_TAGS: [ + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'strong', + 'p', + 'a', + 'ul', + 'ol', + 'li', + 'em', + 'del', + 'blockquote', + ], + }, + ) + }, + }, +} +</script> + +<style scoped lang="scss"> +.settings-markdown :deep { + a { + text-decoration: underline; + &::after { + content: '↗'; + padding-inline: calc(var(--default-grid-baseline) / 2); + } + } + + pre { + white-space: pre; + overflow-x: auto; + background-color: var(--color-background-dark); + border-radius: var(--border-radius); + padding: 1em 1.3em; + margin-bottom: 1em; + } + + p code { + background-color: var(--color-background-dark); + border-radius: var(--border-radius); + padding: .1em .3em; + } + + li { + position: relative; + } + + ul, ol { + padding-inline-start: 10px; + margin-inline-start: 10px; + } + + ul li { + list-style-type: disc; + } + + ul > li > ul > li { + list-style-type: circle; + } + + ul > li > ul > li ul li { + list-style-type: square; + } + + blockquote { + padding-inline-start: 1em; + border-inline-start: 4px solid var(--color-primary-element); + color: var(--color-text-maxcontrast); + margin-inline: 0; + } +} +</style> |